Преглед на файлове

Merge branch 'master' of git.mooctest.net:mooctest-server/main-site

maoyd преди 8 години
родител
ревизия
edcc96dae9
променени са 23 файла, в които са добавени 1850 реда и са изтрити 8 реда
  1. 8 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/common/constant/AppConstants.java
  2. 10 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/common/constant/PrivilegeConstants.java
  3. 29 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/common/constant/SubsiteConstants.java
  4. 1 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/common/constant/UrlConstants.java
  5. 8 5
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/configure/FilterConfiguration.java
  6. 4 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/AssignedTaskDao.java
  7. 3 3
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/CaseDao.java
  8. 5 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/Group2WorkerDao.java
  9. 2 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/AppService.java
  10. 49 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/application/ApiService.java
  11. 719 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/application/impl/ApiServiceImpl.java
  12. 5 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/AppServiceImpl.java
  13. 107 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/ctrl/ApiController.java
  14. 79 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/internal/AddCaseKivulVO.java
  15. 70 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/internal/AddCaseVO.java
  16. 34 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/internal/JoinGroupVO.java
  17. 52 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/internal/RecordScoreVO.java
  18. 34 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/internal/TakeTaskVO.java
  19. 43 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/internal/UploadAppVO.java
  20. 25 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/internal/VerifyAppVO.java
  21. 40 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/filter/ApiHeaderFilter.java
  22. 46 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/ApiLogic.java
  23. 477 0
      mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/impl/ApiLogicImpl.java

+ 8 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/common/constant/AppConstants.java

@@ -0,0 +1,8 @@
+package cn.iselab.mooctest.site.common.constant;
+
+/**
+ * Created by Liu on 2017/3/21.
+ */
+public class AppConstants {
+    public static final int APP_VERIFIED = 1;
+}

+ 10 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/common/constant/PrivilegeConstants.java

@@ -0,0 +1,10 @@
+package cn.iselab.mooctest.site.common.constant;
+
+/**
+ * Created by Liu on 2017/3/21.
+ */
+public class PrivilegeConstants {
+    public static final int SUPER_GROUP_ID = 0;
+
+    public static final int SUPER_MANAGER_ID = 13;
+}

+ 29 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/common/constant/SubsiteConstants.java

@@ -1,5 +1,16 @@
 package cn.iselab.mooctest.site.common.constant;
 
+import cn.iselab.mooctest.site.dao.SubsiteDao;
+import cn.iselab.mooctest.site.models.Subsite;
+import cn.iselab.mooctest.site.service.common.SubsiteService;
+import cn.iselab.mooctest.site.service.common.impl.SubsiteServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
 /**
  * Created by Liu on 2017/1/3.
  */
@@ -16,4 +27,22 @@ public class SubsiteConstants {
      * Subsites
      */
     public static final String KIKBUG_NAME = "Kikbug App Testing";
+
+    /**
+     * Secrets
+     */
+    public static final HashSet<String> SECRETS = new HashSet<String>(){
+        {
+            // TODO: Currently use constants pool for testing
+            // Add singleton to initialize the value-obtaing after @Autowired
+            add("kikbug.mooctest.net");
+            add("dev.mooctest.net");
+            add("kivul.mooctest.net");
+            add("kijam.mooctest.net");
+            add("dev-d.mooctest.net");
+            add("py.mooctest.net:8080/KiVul");
+        }
+    };
+    public static final int KIKBUG_SUBSITE_ID = 6;
+    public static final int KIJAM_SUBSITE_ID = 12;
 }

+ 1 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/common/constant/UrlConstants.java

@@ -12,6 +12,7 @@ public class UrlConstants {
     public static final String API_MANAGER = "/api/manager/";
     public static final String API_WORKER = "/api/worker/";
     public static final String API_ADMIN = "/api/admin/";
+    public static final String API_INTERNAL = "/api/internal/";
 
 
     public static final String HOSTNAME = "http://www.mooctest.net/";

+ 8 - 5
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/configure/FilterConfiguration.java

@@ -2,11 +2,7 @@ package cn.iselab.mooctest.site.configure;
 
 import cn.iselab.mooctest.site.common.constant.UrlConstants;
 import cn.iselab.mooctest.site.web.constants.FilterConsts;
-import cn.iselab.mooctest.site.web.filter.AdminAuthFilter;
-import cn.iselab.mooctest.site.web.filter.AuthFilter;
-import cn.iselab.mooctest.site.web.filter.ManagerAuthFilter;
-import cn.iselab.mooctest.site.web.filter.WorkerAuthFilter;
-import org.springframework.beans.factory.annotation.Qualifier;
+import cn.iselab.mooctest.site.web.filter.*;
 import org.springframework.boot.context.embedded.FilterRegistrationBean;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.context.annotation.Bean;
@@ -70,6 +66,13 @@ public class FilterConfiguration {
         return bean;
     }
 
+    @Bean
+    public FilterRegistrationBean registerApiHeaderFilter(ApiHeaderFilter apiHeaderFilter){
+        FilterRegistrationBean bean = new FilterRegistrationBean(apiHeaderFilter);
+        bean.addUrlPatterns(UrlConstants.API_INTERNAL + "*");
+        return bean;
+    }
+
     private class FilterConfig {
 
         private List<String> escapeUrls;

+ 4 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/AssignedTaskDao.java

@@ -1,7 +1,9 @@
 package cn.iselab.mooctest.site.dao;
 
 import cn.iselab.mooctest.site.models.AssignedTask;
+import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.query.Param;
 
 import javax.transaction.Transactional;
 import java.util.List;
@@ -18,4 +20,6 @@ public interface AssignedTaskDao extends CrudRepository<AssignedTask, Long> {
 
     List<AssignedTask> findByWorkerId(long workerId);
 
+    @Query("SELECT a FROM AssignedTask a WHERE a.taskId = :taskId AND a.workerId = :workerId")
+    AssignedTask findByTaskIdAndWorkerIdIgnoringDeletion(@Param("taskId") long taskId, @Param("workerId") long workerId);
 }

+ 3 - 3
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/CaseDao.java

@@ -27,9 +27,9 @@ public interface CaseDao extends PagingAndSortingRepository<Case, Long> {
     @Query("SELECT c FROM Case c WHERE c.subjectId = :subjectId AND c.visible = :visible")
     List<Case> findBySubjectIdAndPublic(@Param("subjectId") long subjectId, @Param("visible") boolean visible);
 
-    @Query("SELECT c FROM Case c, Subject s " +
-            "WHERE s.subsiteId = c.subjectId " +
-            "And s.id = :subsiteId " +
+    @Query("SELECT c FROM Case c , Subject s " +
+            "WHERE s.id = c.subjectId " +
+            "And s.subsiteId = :subsiteId " +
             "AND c.caseId = :subsiteCaseId")
     Case findBySubsiteIdAndSubsiteCaseId(@Param("subsiteId") long subsiteId, @Param("subsiteCaseId") String subsiteCaseId);
 

+ 5 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/dao/Group2WorkerDao.java

@@ -4,6 +4,8 @@ import cn.iselab.mooctest.site.models.Group2Worker;
 import org.springframework.data.repository.CrudRepository;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.util.List;
+
 /**
  * @author sean
  * @date 2017-03-08.
@@ -15,4 +17,7 @@ public interface Group2WorkerDao extends CrudRepository<Group2Worker, Long> {
 
     Group2Worker findByWorkerIdAndGroupId(long workerId, long groupId);
 
+    List<Group2Worker> findByWorkerId(Long workerId);
+
+    List<Group2Worker> findByGroupId(long id);
 }

+ 2 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/AppService.java

@@ -16,4 +16,6 @@ public interface AppService {
     List<App> getManagerApps(long managerId);
 
     App uploadCase(App app);
+
+    App getAppById(long appId);
 }

+ 49 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/application/ApiService.java

@@ -0,0 +1,49 @@
+package cn.iselab.mooctest.site.service.application;
+
+import cn.iselab.mooctest.site.models.Worker;
+
+/**
+ * Created by Liu on 2017/3/21.
+ */
+public interface ApiService {
+    String getTasks(String host, Long workerId, Long groupId);
+
+    String getTaskWithCases(Long taskId, Long workerId);
+
+    String getTaskTime(Long taskId);
+
+    String getCases(Long workerId);
+
+    String checkCaseOwnership(Long workerId, Long taskId, String caseId);
+
+    Worker workerExists(Long workerId);
+
+    String formInfoJson(Worker worker);
+
+    String getGroupJSON(Long groupId);
+
+    String getAllGroupsJSON(Long workerId);
+
+    boolean verifyApp(Long appId, String url);
+
+    boolean joinGroup(Long workerId, Long groupId, String managerName);
+
+    boolean addScore(Long workerId, Long taskId, Double score, String resultUrl, String picked, String host);
+
+    String checkCaseAssignment(Long workerId, String subsiteCaseId, Long taskId, String host);
+
+    String checkCaseExists(Long workerId, String subsiteCaseId, Long taskId, String host);
+
+    String uploadApp(String host, String name, String caseUrl, Boolean isPublic, Long uploaderId);
+
+    String addCase(String host, String caseId, String typeName, Long managerId, String name, String url, String description, Integer domain, boolean isPublic);
+
+    String addCase(String host, String caseId, String typeName,
+                          String name, String url, String description, int domain, Long appId);
+
+    String takeTask(Long workerId, Long taskId, String subsiteCaseId, String host);
+
+    String checkCaseAvailability(Long taskId, String subsiteCaseId, String host);
+
+    String booleanToJSON(boolean b);
+}

+ 719 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/application/impl/ApiServiceImpl.java

@@ -0,0 +1,719 @@
+package cn.iselab.mooctest.site.service.application.impl;
+
+import cn.iselab.mooctest.site.common.constant.AppConstants;
+import cn.iselab.mooctest.site.common.constant.PrivilegeConstants;
+import cn.iselab.mooctest.site.common.constant.SubsiteConstants;
+import cn.iselab.mooctest.site.dao.*;
+import cn.iselab.mooctest.site.models.*;
+import cn.iselab.mooctest.site.service.application.ApiService;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.sql.Timestamp;
+import java.util.*;
+
+/**
+ * Created by Liu on 2017/3/21.
+ */
+@Service
+public class ApiServiceImpl implements ApiService {
+    @Autowired
+    private SubsiteDao subsiteDao;
+    @Autowired
+    private AssignedTaskDao assignedTaskDao;
+    @Autowired
+    private Task2GroupDao task2GroupDao;
+    @Autowired
+    private TaskDao taskDao;
+    @Autowired
+    private ManagerDao managerDao;
+    @Autowired
+    private GroupDao groupDao;
+    @Autowired
+    private Task2CaseDao task2CaseDao;
+    @Autowired
+    private CaseDao caseDao;
+    @Autowired
+    private WorkerDao workerDao;
+    @Autowired
+    private Group2WorkerDao group2WorkerDao;
+    @Autowired
+    private AppDao appDao;
+    @Autowired
+    private SubjectDao subjectDao;
+
+    /**
+     *
+     * GET methods
+     */
+    @Override
+    public String getTasks(String host, Long workerId, Long groupId) {
+        Subsite subsite = subsiteDao.findByBaseUrl(host);
+
+        List<AssignedTask> assignedTasks = new ArrayList<AssignedTask>();
+        if (groupId == null){
+            assignedTasks = assignedTaskDao.findByWorkerId(workerId);
+        }
+        else{
+            List<Task2Group> task2Groups = task2GroupDao.findByGroupId(groupId);
+            for (Task2Group task2Group : task2Groups){
+                Task taskInGroup = taskDao.findOne(task2Group.getTaskId());
+                assignedTasks.add(assignedTaskDao.findByTaskIdAndWorkerId(taskInGroup.getId(), workerId));
+            }
+        }
+
+        // generate output JSON
+        JSONArray resultArr = new JSONArray();
+        for (AssignedTask assignedTask : assignedTasks){
+            JSONObject resultObj = new JSONObject();
+            Task taskAssigned = taskDao.findOne(assignedTask.getTaskId());
+            Subsite subsiteAssigned = subsiteDao.findOne(taskAssigned.getSubsiteId());
+            Manager managerOfTask = managerDao.findOne(taskAssigned.getManagerId());
+
+            if (subsiteAssigned.getId() != subsite.getId()){
+                continue;
+            }
+            resultObj.put("name", taskAssigned.getName());
+            resultObj.put("id", taskAssigned.getId());
+            resultObj.put("beginTime", taskAssigned.getBeginTime());
+            resultObj.put("endTime", taskAssigned.getEndTime());
+            resultObj.put("managerName", managerOfTask.getName());
+            resultObj.put("information", taskAssigned.getInformation());
+
+            JSONArray groupArr = new JSONArray();
+            List<Task2Group> task2Groups = task2GroupDao.findByTaskId(taskAssigned.getId());
+            for (Task2Group task2Group : task2Groups){
+                Group groupInTask = groupDao.findOne(task2Group.getGroupId());
+                groupArr.put(groupInTask.getName());
+            }
+            resultObj.put("groups", groupArr);
+            resultArr.put(resultObj);
+        }
+
+        return resultArr.toString();
+    }
+
+    @Override
+    public String getTaskWithCases(Long taskId, Long workerId) {
+        JSONObject result = new JSONObject();
+
+        Task task = taskDao.findOne(taskId);
+        Worker worker = workerDao.findOne(workerId);
+
+        if (task == null || worker == null){
+            return "";
+        }
+        Manager managerOfTask = managerDao.findOne(task.getManagerId());
+        Subsite subsiteOfTask = subsiteDao.findOne(task.getSubsiteId());
+
+        result.put("name", task.getName());
+        result.put("beginTime", task.getBeginTime());
+        result.put("endTime", task.getEndTime());
+        result.put("managerName", managerOfTask.getName());
+        result.put("information", task.getInformation());
+        result.put("subsite", subsiteOfTask.getType());
+        result.put("managerId", managerOfTask.getId());
+        JSONArray groupArr = new JSONArray();
+        List<Task2Group> task2Groups = task2GroupDao.findByTaskId(taskId);
+        if (task2Groups != null){
+            for (Task2Group task2Group : task2Groups){
+                Group groupInTask = groupDao.findOne(task2Group.getGroupId());
+                groupArr.put(groupInTask.getName());
+            }
+        }
+        result.put("groups", groupArr);
+
+        AssignedTask assignedTask = assignedTaskDao.findByTaskIdAndWorkerId(taskId, workerId);
+        if (assignedTask == null){
+            return "";
+        }
+        JSONArray caseListArr = new JSONArray();
+
+        // Set here is used to prevent duplicates
+        Set<String> subsiteCaseIds = new HashSet<String>();
+
+        // put taken cases at first
+        JSONObject contentObj = new JSONObject(assignedTask.getContent());
+        JSONObject casesObj = contentObj.getJSONObject("cases");
+        Set<String> keySet = casesObj.keySet();
+        for (String key : keySet){
+            subsiteCaseIds.add(key);
+            JSONObject caseObj = new JSONObject();
+            caseObj.put("id", key);
+            caseObj.put("taken", true);
+            caseListArr.put(caseObj);
+        }
+
+        // check if there are any cases which are not assigned(need to be picked by workers themselves)
+        Task taskAssigned = taskDao.findOne(assignedTask.getTaskId());
+        List<Task2Case> task2Cases = task2CaseDao.findByTaskId(taskAssigned.getId());
+        for (Task2Case task2Case : task2Cases){
+            Case cazeInTask = caseDao.findOne(task2Case.getId());
+            if ((!task2Case.isAutoSelect()) && (!subsiteCaseIds.contains(cazeInTask.getCaseId()))){
+
+                Case caze = caseDao.findOne(task2Case.getCaseId());
+                subsiteCaseIds.add(caze.getCaseId());
+
+                JSONObject caseObj = new JSONObject();
+                caseObj.put("id", caze.getCaseId());
+                caseObj.put("taken", false);
+                caseListArr.put(caseObj);
+            }
+        }
+        result.put("caseList", caseListArr);
+
+        return result.toString();
+    }
+
+    @Override
+    public String getTaskTime(Long taskId) {
+        JSONObject result = new JSONObject();
+
+        Task task = taskDao.findOne(taskId);
+        if (task == null){
+            return "";
+        }
+        result.put("id", task.getId());
+        result.put("beginTimeMillis", task.getBeginTime().getTime());
+        result.put("endTimeMillis", task.getEndTime().getTime());
+
+        return result.toString();
+    }
+
+
+    @Override
+    public String getCases(Long workerId) {
+        JSONArray result = new JSONArray();
+
+        List<AssignedTask> assignedTasks = assignedTaskDao.findByWorkerId(workerId);
+        Set<String> outputCaseIds = new HashSet<String>();
+        for (AssignedTask assignedTask : assignedTasks){
+            Task taskAssigned = taskDao.findOne(assignedTask.getTaskId());
+            Subsite subsiteAssigned = subsiteDao.findOne(taskAssigned.getSubsiteId());
+            if (subsiteAssigned.getId() != SubsiteConstants.KIKBUG_SUBSITE_ID || subsiteAssigned.getId() != SubsiteConstants.KIJAM_SUBSITE_ID){
+                continue;
+            }
+            JSONObject contentObj = new JSONObject(assignedTask.getContent());
+            JSONObject casesObj = contentObj.getJSONObject("cases");
+            Set<String> keySet = casesObj.keySet();
+            for (String key : keySet){
+                outputCaseIds.add(key);
+                JSONObject caseObj = new JSONObject();
+                caseObj.put("caseId", key);
+                caseObj.put("taken", true);
+                caseObj.put("taskId", assignedTask.getTaskId());
+                result.put(caseObj);
+            }
+
+            List<Task2Case> task2Cases = task2CaseDao.findByTaskId(assignedTask.getTaskId());
+            for (Task2Case task2Case : task2Cases){
+                taskAssigned = taskDao.findOne(assignedTask.getTaskId());
+                subsiteAssigned = subsiteDao.findOne(taskAssigned.getSubsiteId());
+                if (subsiteAssigned.getId() != SubsiteConstants.KIKBUG_SUBSITE_ID || subsiteAssigned.getId() != SubsiteConstants.KIJAM_SUBSITE_ID){
+                    continue;
+                }
+                Case cazeInTask = caseDao.findOne(task2Case.getCaseId());
+                if (!task2Case.isAutoSelect() && (!outputCaseIds.contains(cazeInTask.getCaseId()))){
+                    outputCaseIds.add(cazeInTask.getCaseId());
+                    JSONObject caseObj = new JSONObject();
+                    caseObj.put("caseId", cazeInTask.getCaseId());
+                    caseObj.put("taken", false);
+                    caseObj.put("taskId", assignedTask.getTaskId());
+                    result.put(caseObj);
+                }
+
+            }
+        }
+
+        return result.toString();
+    }
+
+
+    /**
+     *
+     * POST methods
+     *
+     */
+    @Override
+    public boolean verifyApp(Long appId, String url) {
+        App app = appDao.findOne(appId);
+        app.setCaseUrl(url);
+        if (url != null) {
+            app.setStatus(AppConstants.APP_VERIFIED);
+        }
+
+        App s = appDao.save(app);
+        return (s != null);
+    }
+
+    @Override
+    public boolean joinGroup(Long workerId, Long groupId, String managerName) {
+        Group2Worker group2Worker = new Group2Worker();
+
+        Group group = groupDao.findOne(groupId);
+        // check privilege
+        if (groupId != PrivilegeConstants.SUPER_GROUP_ID){
+            if (privilegeExpired(managerDao.findOne(group.getManagerId())) || groupMemberExceeded(group)){
+                return false;
+            }
+        }
+        if (group != null) {
+            Worker worker = workerDao.findOne(workerId);
+            Manager manager = managerDao.findOne(group.getManagerId());
+            if (!manager.getName().equals(managerName)){
+                return false;
+            }
+            if (group2WorkerDao.findByWorkerIdAndGroupId(workerId, groupId) != null){
+                return false;
+            }
+            group2Worker.setWorkerId(worker.getId());
+            group2Worker.setGroupId(group.getId());
+
+            group2WorkerDao.save(group2Worker);
+            // Assign tasks for new members
+            assignTaskForNewMember(workerId, groupId);
+
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean addScore(Long workerId, Long taskId, Double score, String resultUrl, String picked, String host) {
+        JSONObject caseContentObj = new JSONObject(picked);
+        AssignedTask assignedTask = assignedTaskDao.findByTaskIdAndWorkerId(taskId, workerId);
+
+        JSONObject results = new JSONObject(assignedTask.getResult());
+        JSONObject resultObj = results.getJSONObject("results");
+
+        String subsiteCaseId = caseContentObj.getString("caseId");
+
+        double weight = caseContentObj.getDouble("weight");
+        JSONObject caseResultObj = new JSONObject();
+        JSONArray caseScoreArr = new JSONArray(), caseResultUrlArr = new JSONArray();
+        double maxScore = score;
+        if (resultObj.has(String.valueOf(subsiteCaseId))){
+
+            caseResultObj = resultObj.getJSONObject(String.valueOf(subsiteCaseId));
+            caseScoreArr = caseResultObj.getJSONArray("scores");
+            caseResultUrlArr = caseResultObj.getJSONArray("resultUrls");
+            maxScore = Math.max(caseResultObj.getDouble("maxScore"), score);
+        }
+        else{
+            caseResultObj.put("id", caseContentObj.getLong("id"));
+            caseResultObj.put("name", caseContentObj.getString("name"));
+            caseResultObj.put("weight", caseContentObj.getDouble("weight"));
+        }
+        caseScoreArr.put(score);
+        caseResultObj.put("scores", caseScoreArr);
+        caseResultUrlArr.put(resultUrl);
+        caseResultObj.put("resultUrls", caseResultUrlArr);
+        caseResultObj.put("maxScore", maxScore);
+
+        // update resultObj
+        resultObj.put(String.valueOf(subsiteCaseId), caseResultObj);
+        results.put("results", resultObj);
+
+        // update assignedTask score
+        assignedTask.setScore(score);
+        assignedTask.setResult(results.toString());
+        assignedTaskDao.save(assignedTask);
+
+        return true;
+    }
+
+    @Override
+    public String uploadApp(String host, String name, String caseUrl, Boolean isPublic, Long uploaderId) {
+        String message = "";
+        Subsite subsite = subsiteDao.findByBaseUrl(host);
+        if (subsite == null){
+            return "No subsite match from host";
+        }
+
+        Manager uploader = managerDao.findById(uploaderId);
+        if (uploader == null){
+            return "No such manager";
+        }
+
+        App app = new App();
+        app.setCaseUrl(caseUrl);
+        app.setVisibility(isPublic);
+        app.setName(name);
+        app.setSubsiteId(subsite.getId());
+        app.setManagerId(uploader.getId());
+        app.setStatus(0);
+
+        appDao.save(app);
+
+        return message;
+    }
+
+    @Override
+    public String addCase(String host, String caseId, String typeName,
+                          String name, String url, String description, int domain, Long appId) {
+        String message = "";
+        App app = appDao.findOne(appId);
+
+        Subsite subsite = subsiteDao.findByBaseUrl(host);
+        if (subsite == null){
+            return "No subsite match from host";
+        }
+        description = (description == null) ? "" : description;
+
+        Subject subject = subjectDao.findBySubsiteIdAndTypeName(subsite.getId(), typeName);
+        if (subject == null){
+            subject = new Subject();
+            subject.setName(typeName);
+            subject.setTargetUrl("");
+            subject.setTypeName(typeName);
+            subject.setSubsiteId(subsite.getId());
+            subjectDao.save(subject);
+        }
+
+        Case oldCase = caseDao.findBySubsiteIdAndSubsiteCaseId(subsite.getId(), caseId);
+        if (oldCase != null){
+            return "case already exists";
+        }
+
+        Case caze = new Case();
+        caze.setDomain(domain);
+        caze.setVisible((app == null) ? true : app.isVisibility());
+        caze.setName(name);
+        caze.setUrl(url);
+        caze.setSubjectId(subject.getId());
+        caze.setManagerId((app == null) ? PrivilegeConstants.SUPER_MANAGER_ID : app.getManagerId());
+        caze.setDescription(description);
+        caze.setCaseId(caseId);
+        caze.setDifficulty(1);
+
+        caseDao.save(caze);
+
+        return message;
+    }
+
+    @Override
+    public String addCase(String host, String caseId, String typeName, Long managerId, String name, String url, String description, Integer domain, boolean isPublic) {
+        String message = "";
+
+        Subsite subsite = subsiteDao.findByBaseUrl(host);
+        if (subsite == null){
+            return "No subsite match from host";
+        }
+        Manager manager = (managerId == null) ?  null : managerDao.findById(managerId);
+        description = (description == null) ? "" : description;
+
+        Subject subject = subjectDao.findBySubsiteIdAndTypeName(subsite.getId(), typeName);
+        if (subject == null){
+            subject = new Subject();
+            subject.setName(typeName);
+            subject.setTargetUrl("");
+            subject.setTypeName(typeName);
+            subject.setSubsiteId(subsite.getId());
+            subjectDao.save(subject);
+        }
+
+        Case oldCase = caseDao.findBySubsiteIdAndSubsiteCaseId(subsite.getId(), caseId);
+        if (oldCase != null){
+            return "case already exists";
+        }
+
+        Case caze = new Case();
+        caze.setDomain(domain);
+        caze.setVisible(isPublic);
+        caze.setName(name);
+        caze.setUrl(url);
+        caze.setSubjectId(subject.getId());
+        caze.setManagerId(manager.getId());
+        caze.setDescription(description);
+        caze.setCaseId(caseId);
+        caze.setDifficulty(1);
+
+        caseDao.save(caze);
+
+        return message;
+    }
+
+
+    @Override
+    public String takeTask(Long workerId, Long taskId, String subsiteCaseId, String host) {
+        // get all entities
+        Subsite subsite = subsiteDao.findByBaseUrl(host);
+        Case caze = caseDao.findBySubsiteIdAndSubsiteCaseId(subsite.getId(), subsiteCaseId);
+        AssignedTask assignedTask = assignedTaskDao.findByTaskIdAndWorkerId(taskId, workerId);
+
+        Task2Case task2Case = task2CaseDao.findByTaskIdAndCaseId(taskId, caze.getId());
+
+        // parse content
+        String content = assignedTask.getContent();
+        JSONObject assignedContent = new JSONObject();
+        if (content.length() > 0){
+            assignedContent = new JSONObject(content);
+        }
+        JSONObject assignedObjs = assignedContent.has("cases") ? assignedContent.getJSONObject("cases") : new JSONObject();
+        boolean picked = false;
+        if (assignedObjs.has(String.valueOf(caze.getCaseId()))){
+            picked = true;
+        }
+        JSONObject assignedCase = new JSONObject();
+
+        Subject subject = subjectDao.findOne(caze.getSubjectId());
+        assignedCase.put("id", caze.getId());
+        assignedCase.put("name", caze.getName());
+        assignedCase.put("weight", task2Case.getWeight());
+        assignedCase.put("hint", caze.getHint());
+        assignedCase.put("caseUrl", caze.getUrl());
+        assignedCase.put("caseType", subject.getTypeName());
+        assignedCase.put("subsiteUrl", subsite.getBaseUrl());
+        assignedCase.put("caseId", caze.getCaseId());
+        assignedObjs.put(String.valueOf(caze.getCaseId()), assignedCase);
+        assignedContent.put("cases", assignedObjs);
+        // update assignedTask if not chosen
+        if (!picked){
+            // if not picked before
+            assignedTask.setContent(assignedContent.toString());
+            assignedTaskDao.save(assignedTask);
+
+            // Update count of Task2Case
+            task2Case.setCount(task2Case.getCount() + 1);
+            task2CaseDao.save(task2Case);
+        }
+
+        return "{\"success\":true}";
+    }
+
+    /**
+     *
+     * UTILs methods
+     *
+     */
+    @Override
+    public String checkCaseAssignment(Long workerId, String subsiteCaseId, Long taskId, String host) {
+        AssignedTask assignedTask = assignedTaskDao.findByTaskIdAndWorkerId(taskId, workerId);
+        Subsite subsite = subsiteDao.findByBaseUrl(host);
+        Case caze = caseDao.findBySubsiteIdAndSubsiteCaseId(subsite.getId(), subsiteCaseId);
+        if (assignedTask == null || subsite == null || caze == null){
+            return null;
+        }
+
+        JSONObject contentObj = new JSONObject(assignedTask.getContent());
+        JSONObject casesObj = contentObj.getJSONObject("cases");
+        if (casesObj.has(String.valueOf(caze.getCaseId()))){
+            return casesObj.get(String.valueOf(caze.getCaseId())).toString();
+        }
+        else{
+            return null;
+        }
+    }
+
+    @Override
+    public String checkCaseExists(Long workerId, String subsiteCaseId, Long taskId, String host) {
+        AssignedTask assignedTask = assignedTaskDao.findByTaskIdAndWorkerId(taskId, workerId);
+        Subsite subsite = subsiteDao.findByBaseUrl(host);
+        if (subsite == null){
+            return "No subsite match from host";
+        }
+
+        Case caze = caseDao.findBySubsiteIdAndSubsiteCaseId(subsite.getId(), subsiteCaseId);
+
+        if (assignedTask == null){
+            return "AssignedTask not found by task and worker";
+        }
+        if (caze == null){
+            return "Case not found";
+        }
+
+        return "";
+    }
+
+    @Override
+    public String checkCaseAvailability(Long taskId, String subsiteCaseId, String host) {
+        Subsite subsite = subsiteDao.findByBaseUrl(host);
+        Case caze = caseDao.findBySubsiteIdAndSubsiteCaseId(subsite.getId(), subsiteCaseId);
+
+        Task2Case task2Case = task2CaseDao.findByTaskIdAndCaseId(taskId, caze.getId());
+        if (task2Case == null || task2Case.isAutoSelect() == true) {
+            return "Not allowed to pick assigned cases";
+        }
+        return "";
+    }
+
+    @Override
+    public String booleanToJSON(boolean success) {
+        String result;
+        result = success ? "{\"success\":true}" : "{\"success\":false}";
+        return result;
+    }
+
+    private boolean groupMemberExceeded(Group group){
+        return (group2WorkerDao.findByGroupId(group.getId()).size() >= managerDao.findOne(group.getManagerId()).getGroupSize());
+    }
+
+    private boolean privilegeExpired(Manager manager){
+        Timestamp current = new Timestamp(System.currentTimeMillis());
+        if (manager.getExpireTime() == null) {
+            return true;
+        }
+        return manager.getExpireTime().before(current);
+    }
+    public boolean assignTaskForNewMember(String workerEmail, String workerMobile, long groupId) {
+        Worker worker = workerDao.findByAccount(workerEmail, workerMobile);
+        return assignTaskForNewMember(worker, groupId);
+    }
+
+    public boolean assignTaskForNewMember(long workerId, long groupId) {
+        return assignTaskForNewMember(workerDao.findOne(workerId), groupId);
+    }
+
+    private boolean assignTaskForNewMember(Worker worker, long groupId){
+        if (worker == null){
+            return false;
+        }
+
+        List<Task2Group> taskGroupList = task2GroupDao.findByGroupId(groupId);
+        for (Task2Group task2Group : taskGroupList){
+            Task task = taskDao.findOne(task2Group.getTaskId());
+            // Check if there are already assigned tasks
+            AssignedTask existingAssignedTask = assignedTaskDao.findByTaskIdAndWorkerIdIgnoringDeletion(task.getId(), worker.getId());
+            if (existingAssignedTask != null){
+                existingAssignedTask.setDeleted(false);
+                assignedTaskDao.save(existingAssignedTask);
+                continue;
+            }
+            else{
+                existingAssignedTask = new AssignedTask();
+            }
+            List<Task2Case> task2CaseList = task2CaseDao.findByTaskId(task.getId());
+            HashMap<Long, List<Long>> caseGroupMap = new HashMap<Long, List<Long>>();
+            HashMap<Long, Double> weightGroupMap = new HashMap<Long, Double>();
+            for (Task2Case task2Case : task2CaseList){
+                long caseGroupId = task2Case.getCaseIndex();
+                List<Long> caseList = (caseGroupMap.containsKey(caseGroupId)) ? caseGroupMap.get(caseGroupId) : new ArrayList<Long>();
+                caseList.add(task2Case.getCaseId());
+                caseGroupMap.put(caseGroupId, caseList);
+
+                if (!weightGroupMap.containsKey(caseGroupId)){
+                    weightGroupMap.put(caseGroupId, task2Case.getWeight());
+                }
+            }
+
+            // prepare cases content for AssignedTask
+            JSONObject assignedTaskObj = new JSONObject(), assignedCasesObj = new JSONObject();
+            for (Long caseGroupId : caseGroupMap.keySet()){
+                // Add new AssignedTask
+                List<Long> caseList = caseGroupMap.get(caseGroupId);
+                long chosenCaseId = generateCase(caseList);
+                JSONObject assignedCaseObj = new JSONObject();
+                Case caze = caseDao.findOne(chosenCaseId);
+                Subject subject = subjectDao.findOne(caze.getSubjectId());
+
+                assignedCaseObj.put("id", caze.getId());
+                assignedCaseObj.put("name", caze.getName());
+                assignedCaseObj.put("weight", weightGroupMap.get(caseGroupId));
+                assignedCaseObj.put("caseUrl", caze.getUrl());
+                assignedCaseObj.put("caseType", subject.getTypeName());
+                assignedCaseObj.put("hint", caze.getHint());
+                assignedCaseObj.put("subsiteUrl", subsiteDao.findOne(subject.getSubsiteId()).getBaseUrl());
+                assignedCaseObj.put("caseId", caze.getCaseId());
+                assignedCasesObj.put(String.valueOf(caze.getCaseId()), assignedCaseObj);
+
+                // update Task2Case count
+                Task2Case task2Case = task2CaseDao.findByTaskIdAndCaseId(task.getId(), caze.getId());
+                task2Case.setCount(task2Case.getCount() + 1);
+                task2CaseDao.save(task2Case);
+            }
+            assignedTaskObj.put("cases", assignedCasesObj);
+
+            // prepare results for AssignedTask
+            JSONObject resultObj = new JSONObject();
+            resultObj.put("results", new JSONObject());
+
+            // assign values for AssignedTask
+            existingAssignedTask.setName(task.getName());
+            existingAssignedTask.setContent(assignedTaskObj.toString());
+            existingAssignedTask.setResult(resultObj.toString());
+            existingAssignedTask.setManagerId(task.getManagerId());
+            existingAssignedTask.setTaskId(task.getId());
+            existingAssignedTask.setScore(0.0);
+            existingAssignedTask.setWorkerId(worker.getId());
+            existingAssignedTask.setDeleted(false);
+
+            // Create AssignedTask entity
+            assignedTaskDao.save(existingAssignedTask);
+        }
+        return true;
+    }
+
+    private static long generateCase(List<Long> caseIdsList) {
+        int serial = new Double(Math.floor(Math.random() * caseIdsList.size()))
+                .intValue();
+        return caseIdsList.get(serial);
+    }
+
+
+    @Override
+    public String getGroupJSON(Long groupId) {
+        JSONObject result = new JSONObject();
+        Group group = groupDao.findOne(groupId);
+        if (group  == null){
+            return null;
+        }
+
+        Manager manager = managerDao.findOne(group.getId());
+        result.put("id", group.getId());
+        result.put("name", group.getName());
+        result.put("managerName", manager.getName());
+
+        return result.toString();
+    }
+
+    @Override
+    public String getAllGroupsJSON(Long workerId) {
+        Worker worker = workerDao.findOne(workerId);
+        JSONArray groupArr = new JSONArray();
+
+        List<Group> groups = new ArrayList<Group>();
+        List<Group2Worker> group2Workers = group2WorkerDao.findByWorkerId(workerId);
+        for (Group2Worker group2Worker : group2Workers){
+            Group group = groupDao.findOne(group2Worker.getGroupId());
+
+            Manager groupManager = managerDao.findOne(group.getManagerId());
+            JSONObject groupObj = new JSONObject();
+            groupObj.put("id", group.getId());
+            groupObj.put("name", group.getName());
+            groupObj.put("managerName", groupManager.getName());
+            groupArr.put(groupObj);
+        }
+        return groupArr.toString();
+    }
+
+    @Override
+    public String checkCaseOwnership(Long workerId, Long taskId, String caseId) {
+        AssignedTask assignedTask = assignedTaskDao.findByTaskIdAndWorkerId(taskId, workerId);
+        JSONObject taskContent = new JSONObject(assignedTask.getContent());
+        JSONObject casesContent = taskContent.getJSONObject("cases");
+        JSONObject result = new JSONObject();
+
+        result.put("taken", casesContent.has(caseId));
+
+        return result.toString();
+    }
+
+    @Override
+    public Worker workerExists(Long workerId) {
+        return (workerDao.findOne(workerId));
+    }
+
+    @Override
+    public String formInfoJson(Worker worker) {
+        JSONObject userInfoObj = new JSONObject();
+        userInfoObj.put("email", worker.getEmail());
+        userInfoObj.put("mobile", worker.getMobile());
+        userInfoObj.put("name", worker.getName());
+        userInfoObj.put("avatar", worker.getAvatar());
+        userInfoObj.put("level", worker.getLevel());
+
+        return userInfoObj.toString();
+    }
+}

+ 5 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/service/impl/AppServiceImpl.java

@@ -40,4 +40,9 @@ public class AppServiceImpl implements AppService {
         app.setCreateTime(new Timestamp(System.currentTimeMillis()));
         return appDao.save(app);
     }
+
+    @Override
+    public App getAppById(long appId) {
+        return appDao.findOne(appId);
+    }
 }

+ 107 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/ctrl/ApiController.java

@@ -0,0 +1,107 @@
+package cn.iselab.mooctest.site.web.ctrl;
+
+import cn.iselab.mooctest.site.common.constant.UrlConstants;
+import cn.iselab.mooctest.site.web.data.internal.*;
+import cn.iselab.mooctest.site.web.logic.ApiLogic;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * @author sean
+ * @date 2017-03-07.
+ * REMINDER:!!
+ * This is a class to be deprecated when RPC is done
+ * Now unlike other classes, it doesn't return VO values
+ */
+
+@RestController
+public class ApiController extends BaseController {
+
+    @Autowired
+    private ApiLogic apiLogic;
+
+    @RequestMapping(value = UrlConstants.API_INTERNAL + "checkIdentity", method = RequestMethod.GET)
+    public String checkIdentity(HttpServletRequest request) {
+        return apiLogic.checkIdentity(request);
+    }
+
+    @RequestMapping(value = UrlConstants.API_INTERNAL + "getTasks", method = RequestMethod.GET)
+    public String getTasks(HttpServletRequest request) {
+        return apiLogic.getTasks(request);
+    }
+
+    @RequestMapping(value = UrlConstants.API_INTERNAL + "getTask", method = RequestMethod.GET)
+    public String getTask(HttpServletRequest request) {
+        return apiLogic.getTask(request);
+    }
+
+    @RequestMapping(value = UrlConstants.API_INTERNAL + "getTaskTime", method = RequestMethod.GET)
+    public String getTaskTime(HttpServletRequest request) {
+        return apiLogic.getTaskTime(request);
+    }
+
+    @RequestMapping(value = UrlConstants.API_INTERNAL + "getCases", method = RequestMethod.GET)
+    public String getCases(HttpServletRequest request) {
+        return apiLogic.getCases(request);
+    }
+
+    @RequestMapping(value = UrlConstants.API_INTERNAL + "checkOwnership", method = RequestMethod.GET)
+    public String checkOwnership(HttpServletRequest request) {
+        return apiLogic.checkOwnership(request);
+    }
+
+    @RequestMapping(value = UrlConstants.API_INTERNAL + "getUserInfo", method = RequestMethod.GET)
+    public String getUserInfo(HttpServletRequest request) {
+        return apiLogic.getUserInfo(request);
+    }
+
+    @RequestMapping(value = UrlConstants.API_INTERNAL + "getGroup", method = RequestMethod.GET)
+    public String getGroup(HttpServletRequest request) {
+        return apiLogic.getGroup(request);
+    }
+
+    @RequestMapping(value = UrlConstants.API_INTERNAL + "getGroups", method = RequestMethod.GET)
+    public String getGroups(HttpServletRequest request) {
+        return apiLogic.getGroups(request);
+    }
+
+    @RequestMapping(value = UrlConstants.API_INTERNAL + "takeTask", method = RequestMethod.POST)
+    public String takeTask(HttpServletRequest request, @RequestBody TakeTaskVO body) {
+        return apiLogic.takeTask(request, body);
+    }
+
+    @RequestMapping(value = UrlConstants.API_INTERNAL + "addCase", method = RequestMethod.POST)
+    public String addCase(HttpServletRequest request, @RequestBody AddCaseVO body) {
+        return apiLogic.addCase(request, body);
+    }
+
+    @RequestMapping(value = UrlConstants.API_INTERNAL + "addCaseKivul", method = RequestMethod.POST)
+    public String addCaseKivul(HttpServletRequest request, @RequestBody AddCaseKivulVO body) {
+        return apiLogic.addCaseKivul(request, body);
+    }
+
+    @RequestMapping(value = UrlConstants.API_INTERNAL + "uploadApp", method = RequestMethod.POST)
+    public String uploadApp(HttpServletRequest request, @RequestBody UploadAppVO body) {
+        return apiLogic.uploadApp(request, body);
+    }
+
+    @RequestMapping(value = UrlConstants.API_INTERNAL + "recordScore", method = RequestMethod.POST)
+    public String recordScore(HttpServletRequest request, @RequestBody RecordScoreVO body) {
+        return apiLogic.recordScore(request, body);
+    }
+
+    @RequestMapping(value = UrlConstants.API_INTERNAL + "joinGroup", method = RequestMethod.POST)
+    public String joinGroup(HttpServletRequest request, @RequestBody JoinGroupVO body) {
+        return apiLogic.joinGroup(request, body);
+    }
+    @RequestMapping(value = UrlConstants.API_INTERNAL + "verifyApp", method = RequestMethod.POST)
+    public String verifyApp(HttpServletRequest request, @RequestBody VerifyAppVO body) {
+        return apiLogic.verifyApp(request, body);
+    }
+
+}

+ 79 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/internal/AddCaseKivulVO.java

@@ -0,0 +1,79 @@
+package cn.iselab.mooctest.site.web.data.internal;
+
+/**
+ * Created by Liu on 2017/3/21.
+ */
+public class AddCaseKivulVO {
+    private Long managerId;
+    private String id;
+    private String name;
+    private String url;
+    private String description;
+    private String typeName;
+    private Integer domain;
+    private Boolean isPublic;
+
+    public Long getManagerId() {
+        return managerId;
+    }
+
+    public void setManagerId(Long managerId) {
+        this.managerId = managerId;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getTypeName() {
+        return typeName;
+    }
+
+    public void setTypeName(String typeName) {
+        this.typeName = typeName;
+    }
+
+    public Integer getDomain() {
+        return domain;
+    }
+
+    public void setDomain(Integer domain) {
+        this.domain = domain;
+    }
+
+    public Boolean isPublic() {
+        return isPublic;
+    }
+
+    public void setPublic(boolean aPublic) {
+        isPublic = aPublic;
+    }
+}

+ 70 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/internal/AddCaseVO.java

@@ -0,0 +1,70 @@
+package cn.iselab.mooctest.site.web.data.internal;
+
+/**
+ * Created by Liu on 2017/3/21.
+ */
+public class AddCaseVO {
+    private Long appId;
+    private String id;
+    private String name;
+    private String url;
+    private String description;
+    private String typeName;
+    private Integer domain;
+
+    public Long getAppId() {
+        return appId;
+    }
+
+    public void setAppId(Long appId) {
+        this.appId = appId;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getTypeName() {
+        return typeName;
+    }
+
+    public void setTypeName(String typeName) {
+        this.typeName = typeName;
+    }
+
+    public Integer getDomain() {
+        return domain;
+    }
+
+    public void setDomain(Integer domain) {
+        this.domain = domain;
+    }
+}

+ 34 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/internal/JoinGroupVO.java

@@ -0,0 +1,34 @@
+package cn.iselab.mooctest.site.web.data.internal;
+
+/**
+ * Created by Liu on 2017/3/21.
+ */
+public class JoinGroupVO {
+    private Long workerId;
+    private String managerName;
+    private Long groupId;
+
+    public Long getWorkerId() {
+        return workerId;
+    }
+
+    public void setWorkerId(Long workerId) {
+        this.workerId = workerId;
+    }
+
+    public String getManagerName() {
+        return managerName;
+    }
+
+    public void setManagerName(String managerName) {
+        this.managerName = managerName;
+    }
+
+    public Long getGroupId() {
+        return groupId;
+    }
+
+    public void setGroupId(Long groupId) {
+        this.groupId = groupId;
+    }
+}

+ 52 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/internal/RecordScoreVO.java

@@ -0,0 +1,52 @@
+package cn.iselab.mooctest.site.web.data.internal;
+
+/**
+ * Created by Liu on 2017/3/21.
+ */
+public class RecordScoreVO {
+    private Long workerId;
+    private String caseId;
+    private Long taskId;
+    private Double score;
+    private String resultUrl;
+
+    public Long getWorkerId() {
+        return workerId;
+    }
+
+    public void setWorkerId(Long workerId) {
+        this.workerId = workerId;
+    }
+
+    public String getCaseId() {
+        return caseId;
+    }
+
+    public void setCaseId(String caseId) {
+        this.caseId = caseId;
+    }
+
+    public Long getTaskId() {
+        return taskId;
+    }
+
+    public void setTaskId(Long taskId) {
+        this.taskId = taskId;
+    }
+
+    public Double getScore() {
+        return score;
+    }
+
+    public void setScore(Double score) {
+        this.score = score;
+    }
+
+    public String getResultUrl() {
+        return resultUrl;
+    }
+
+    public void setResultUrl(String resultUrl) {
+        this.resultUrl = resultUrl;
+    }
+}

+ 34 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/internal/TakeTaskVO.java

@@ -0,0 +1,34 @@
+package cn.iselab.mooctest.site.web.data.internal;
+
+/**
+ * Created by Liu on 2017/3/21.
+ */
+public class TakeTaskVO {
+    private Long workerId;
+    private String caseId;
+    private Long taskId;
+
+    public Long getWorkerId() {
+        return workerId;
+    }
+
+    public void setWorkerId(Long workerId) {
+        this.workerId = workerId;
+    }
+
+    public String getCaseId() {
+        return caseId;
+    }
+
+    public void setCaseId(String caseId) {
+        this.caseId = caseId;
+    }
+
+    public Long getTaskId() {
+        return taskId;
+    }
+
+    public void setTaskId(Long taskId) {
+        this.taskId = taskId;
+    }
+}

+ 43 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/internal/UploadAppVO.java

@@ -0,0 +1,43 @@
+package cn.iselab.mooctest.site.web.data.internal;
+
+/**
+ * Created by Liu on 2017/3/21.
+ */
+public class UploadAppVO {
+    private Boolean isPublic;
+    private String name;
+    private String caseUrl;
+    private Long uploaderId;
+
+    public Boolean getPublic() {
+        return isPublic;
+    }
+
+    public void setPublic(Boolean aPublic) {
+        isPublic = aPublic;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getCaseUrl() {
+        return caseUrl;
+    }
+
+    public void setCaseUrl(String caseUrl) {
+        this.caseUrl = caseUrl;
+    }
+
+    public Long getUploaderId() {
+        return uploaderId;
+    }
+
+    public void setUploaderId(Long uploaderId) {
+        this.uploaderId = uploaderId;
+    }
+}

+ 25 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/data/internal/VerifyAppVO.java

@@ -0,0 +1,25 @@
+package cn.iselab.mooctest.site.web.data.internal;
+
+/**
+ * Created by Liu on 2017/3/21.
+ */
+public class VerifyAppVO {
+    private Long appId;
+    private String url;
+
+    public Long getAppId() {
+        return appId;
+    }
+
+    public void setAppId(Long appId) {
+        this.appId = appId;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+}

+ 40 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/filter/ApiHeaderFilter.java

@@ -0,0 +1,40 @@
+package cn.iselab.mooctest.site.web.filter;
+
+import cn.iselab.mooctest.site.common.constant.SubsiteConstants;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * Created by Liu on 2017/3/20.
+ */
+@Component
+public class ApiHeaderFilter implements Filter {
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+
+    }
+
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+        HttpServletRequest request = (HttpServletRequest) servletRequest;
+        HttpServletResponse response = (HttpServletResponse) servletResponse;
+
+        String secret = request.getHeader("secret");
+        if (secret == null || secret.length() == 0 || (!SubsiteConstants.SECRETS.contains(secret))){
+            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Secret mismatch");
+            filterChain.doFilter(request, response);
+            return;
+        }
+
+        filterChain.doFilter(request, response);
+    }
+
+    @Override
+    public void destroy() {
+
+    }
+}

+ 46 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/ApiLogic.java

@@ -0,0 +1,46 @@
+package cn.iselab.mooctest.site.web.logic;
+
+import cn.iselab.mooctest.site.web.data.WorkerVO;
+import cn.iselab.mooctest.site.web.data.internal.*;
+import javafx.scene.shape.VertexFormat;
+import org.springframework.data.domain.Page;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Created by Liu on 2017/3/20.
+ */
+public interface ApiLogic {
+
+    String checkIdentity(HttpServletRequest request);
+
+    String getTasks(HttpServletRequest request);
+
+    String getTask(HttpServletRequest request);
+
+    String getTaskTime(HttpServletRequest request);
+
+    String getCases(HttpServletRequest request);
+
+    String checkOwnership(HttpServletRequest request);
+
+    String getUserInfo(HttpServletRequest request);
+
+    String getGroup(HttpServletRequest request);
+
+    String getGroups(HttpServletRequest request);
+
+    String takeTask(HttpServletRequest request, TakeTaskVO input);
+
+    String addCase(HttpServletRequest request, AddCaseVO input);
+
+    String addCaseKivul(HttpServletRequest request, AddCaseKivulVO input);
+
+    String uploadApp(HttpServletRequest request, UploadAppVO input);
+
+    String recordScore(HttpServletRequest request, RecordScoreVO input);
+
+    String joinGroup(HttpServletRequest request, JoinGroupVO input);
+
+    String verifyApp(HttpServletRequest request, VerifyAppVO input);
+}

+ 477 - 0
mooctest-site-server/src/main/java/cn/iselab/mooctest/site/web/logic/impl/ApiLogicImpl.java

@@ -0,0 +1,477 @@
+package cn.iselab.mooctest.site.web.logic.impl;
+
+import cn.iselab.mooctest.site.service.AdminService;
+import cn.iselab.mooctest.site.service.AppService;
+import cn.iselab.mooctest.site.service.ManagerService;
+import cn.iselab.mooctest.site.service.WorkerService;
+import cn.iselab.mooctest.site.service.application.ApiService;
+import cn.iselab.mooctest.site.util.data.EncryptionUtil;
+import cn.iselab.mooctest.site.web.data.internal.*;
+import cn.iselab.mooctest.site.web.logic.ApiLogic;
+import cn.iselab.mooctest.site.models.*;
+
+import org.json.JSONObject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Created by Liu on 2017/3/20.
+ */
+@Service
+public class ApiLogicImpl implements ApiLogic {
+    @Autowired
+    private AppService appService;
+    @Autowired
+    private WorkerService workerService;
+    @Autowired
+    private AdminService adminService;
+    @Autowired
+    private ManagerService managerService;
+    @Autowired
+    private ApiService apiService;
+
+    private static final int HTTP_OK = 200;
+    private static final int HTTP_INTERNAL_ERROR = 500;
+    private static final String  missingParameterResponse = generateResponse(HTTP_OK, "Missing parameter", "{}");
+
+    private String getSecretHeader(HttpServletRequest request){
+        String host = request.getHeader("secret");
+
+        return host;
+    }
+
+    private boolean checkWorkerToken(HttpServletRequest request, Long workerId) {
+        try {
+            Worker worker = workerService.getWorkerById(workerId);
+            String account = (worker.getEmail() == null || worker.getEmail().length() == 0) ? worker.getMobile() : worker.getEmail();
+            String encryptedToken = EncryptionUtil.encryptDES(account);
+
+            String workerToken = request.getHeader("worker-token");
+            if (workerToken != null && workerToken.length() > 0 && workerToken.equals(encryptedToken)){
+                return true;
+            }
+            else{
+                return false;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return false;
+    }
+
+    private boolean checkAppToken(Long appId) {
+        App app = appService.getAppById(appId);
+        return (app != null);
+    }
+
+    private static String generateResponse(int status, String message, String data){
+        JSONObject response = new JSONObject();
+        response.put("status" , status);
+        response.put("message", message);
+        if (data == null || data.length() == 0){
+            data = "{}";
+        }
+        response.put("data", data);
+
+        return response.toString();
+    }
+
+    @Override
+    public String checkIdentity(HttpServletRequest request) {
+        int statusCode = HTTP_OK;
+        String host = getSecretHeader(request);
+        if (host != null) {
+            String account = request.getParameter("account");
+            if (account == null) {
+                return missingParameterResponse;
+            }
+
+            Admin admin = adminService.getAdminByUsername(account);
+            Manager manager = managerService.getManagerByUsername(account);
+            Worker worker = workerService.getWorkerByUserName(account);
+
+            JSONObject userObj = null;
+            if (admin != null) {
+                userObj = new JSONObject();
+                userObj.put("id", admin.getId());
+                userObj.put("identity", "admin");
+                userObj.put("avatar", "");
+                userObj.put("name", admin.getName());
+            } else if (manager != null) {
+                userObj = new JSONObject();
+                userObj.put("id", manager.getId());
+                userObj.put("identity", "manager");
+                userObj.put("avatar", manager.getAvatar());
+                userObj.put("name", manager.getName());
+            } else if (worker != null) {
+                userObj = new JSONObject();
+                userObj.put("id", worker.getId());
+                userObj.put("identity", "worker");
+                userObj.put("avatar", worker.getAvatar());
+                userObj.put("name", worker.getName());
+            }
+            if (userObj != null){
+                return generateResponse(statusCode, "", userObj.toString());
+            }
+            else{
+                return generateResponse(statusCode, "No such user", "{}");
+            }
+        }
+        return generateResponse(statusCode, "Subsite mismatch", "{}");
+    }
+
+    @Override
+    public String getTasks(HttpServletRequest request) {
+        int statusCode = HTTP_OK;
+        String host = getSecretHeader(request);
+        if (host != null){
+            if (request.getParameter("workerId") == null){
+                return missingParameterResponse;
+            }
+
+            Long workerId = Long.parseLong(request.getParameter("workerId"));
+            Long groupId = request.getParameter("groupId") == null ? null : Long.parseLong(request.getParameter("groupId"));
+
+            return generateResponse(statusCode, "", apiService.getTasks(host, workerId, groupId));
+        }
+        return generateResponse(statusCode, "Subsite mismatch", "{}");
+    }
+
+    @Override
+    public String getTask(HttpServletRequest request) {
+        int statusCode = HTTP_OK;
+        String host = getSecretHeader(request);
+        if (host != null){
+            if (request.getParameter("taskId") == null || request.getParameter("workerId") == null){
+                return missingParameterResponse;
+            }
+            Long taskId = Long.parseLong(request.getParameter("taskId"));
+            Long workerId = Long.parseLong(request.getParameter("workerId"));
+
+            String result = apiService.getTaskWithCases(taskId, workerId);
+            String message = "";
+            if (result == null || result.length() == 0){
+                message = "task, worker of task not found";
+            }
+            return generateResponse(statusCode, message, result);
+        }
+        return generateResponse(statusCode, "Subsite mismatch", "{}");
+    }
+
+    @Override
+    public String getTaskTime(HttpServletRequest request) {
+
+        int statusCode = HTTP_OK;
+        String host = getSecretHeader(request);
+        if (host != null){
+            if (request.getParameter("taskId") == null){
+                return missingParameterResponse;
+            }
+            Long taskId = Long.parseLong(request.getParameter("taskId"));
+
+            String result = apiService.getTaskTime(taskId);
+            String message = "";
+            if (result == null || result.length() == 0){
+                message = "task not found";
+            }
+            return generateResponse(statusCode, message, result);
+        }
+        return generateResponse(statusCode, "Subsite mismatch", "{}");
+    }
+
+    @Override
+    public String getCases(HttpServletRequest request) {
+        int statusCode = HTTP_OK;
+        String host = getSecretHeader(request);
+        if (host != null){
+            if (request.getParameter("workerId") == null){
+                return missingParameterResponse;
+            }
+            Long workerId = Long.parseLong(request.getParameter("workerId"));
+
+            String message = "";
+            String result = apiService.getCases(workerId);
+            if (result == null || result.length() == 0){
+                message = "worker invalid";
+            }
+            return generateResponse(statusCode, message, result);
+        }
+        return generateResponse(statusCode, "Subsite mismatch", "{}");
+    }
+
+    @Override
+    public String checkOwnership(HttpServletRequest request) {
+        int statusCode = HTTP_OK;
+
+        if (request.getParameter("workerId") == null || request.getParameter("taskId") == null || request.getParameter("caseId") == null){
+            return missingParameterResponse;
+        }
+
+        Long workerId = Long.parseLong(request.getParameter("workerId"));
+        Long taskId = Long.parseLong(request.getParameter("taskId"));
+        String caseId = request.getParameter("caseId");
+
+
+        String result = apiService.checkCaseOwnership(workerId, taskId, caseId);
+
+        String message = (result == null || result.length() == 0) ? "Worker/Task/Case not found." : "";
+
+        return generateResponse(statusCode, message , result);
+
+    }
+
+    @Override
+    public String getUserInfo(HttpServletRequest request) {
+        int statusCode = HTTP_OK;
+        String host = getSecretHeader(request);
+        if (host != null){
+            if (request.getParameter("workerId") == null){
+                return missingParameterResponse;
+            }
+            Long workerId = Long.parseLong(request.getParameter("workerId"));
+            Worker worker = apiService.workerExists(workerId);
+            String message = "", result = "";
+            if (worker == null){
+                result = "no such user";
+            }
+            else{
+                result = apiService.formInfoJson(worker);
+            }
+            return generateResponse(statusCode, message, result);
+        }
+        return generateResponse(statusCode, "Subsite mismatch", "{}");
+    }
+
+    @Override
+    public String getGroup(HttpServletRequest request) {
+        int statusCode = HTTP_OK;
+        String host = getSecretHeader(request);
+        if (host != null){
+            if (request.getParameter("groupId") == null || request.getParameter("workerId") == null){
+                return missingParameterResponse;
+            }
+            Long groupId = Long.parseLong(request.getParameter("groupId"));
+            Long workerId = Long.parseLong(request.getParameter("workerId"));
+            if (!checkWorkerToken(request, workerId)){
+                return generateResponse(statusCode, "worker token mismatch", "{}");
+            }
+            String result = apiService.getGroupJSON(groupId);
+            String message = "";
+            if (result == null){
+                message = "no such group";
+            }
+            return generateResponse(statusCode, message, result);
+        }
+        return generateResponse(statusCode, "Subsite mismatch", "{}");
+    }
+
+    @Override
+    public String getGroups(HttpServletRequest request) {
+        int statusCode = HTTP_OK;
+        String host = getSecretHeader(request);
+        if (host != null){
+            if (request.getParameter("workerId") == null){
+                return missingParameterResponse;
+            }
+            Long workerId = Long.parseLong(request.getParameter("workerId"));
+            if (!checkWorkerToken(request, workerId)){
+                return generateResponse(statusCode, "worker token mismatch", "[]");
+            }
+            String message = "";
+            String result = apiService.getAllGroupsJSON(workerId);
+            return generateResponse(statusCode, message, result);
+        }
+        return generateResponse(statusCode, "Subsite mismatch", "{}");
+    }
+
+    @Override
+    public String takeTask(HttpServletRequest request, TakeTaskVO input) {
+        int statusCode = HTTP_OK;
+
+        String host = getSecretHeader(request);
+        if (host != null){
+            if (input == null){
+                return generateResponse(HTTP_INTERNAL_ERROR, "Exception occured" , "{}");
+            }
+            Long workerId = input.getWorkerId();
+            String subsiteCaseId = input.getCaseId();
+            Long taskId = input.getTaskId();
+
+            if (workerId == null || subsiteCaseId == null || taskId == null){
+                return missingParameterResponse;
+            }
+
+            String error = apiService.checkCaseExists(workerId, subsiteCaseId, taskId, host);
+            if (error != null && error.length() != 0){
+                return generateResponse(statusCode, error, "{}");
+            }
+            error = apiService.checkCaseAvailability(taskId, subsiteCaseId, host);
+            if (error != null && error.length() != 0){
+                return generateResponse(statusCode, error, "{}");
+            }
+
+            String response = apiService.takeTask(workerId, taskId, subsiteCaseId, host);
+            return generateResponse(statusCode, "", response);
+        }
+        return generateResponse(statusCode, "Subsite mismatch", "{}");
+    }
+
+    @Override
+    public String addCase(HttpServletRequest request, AddCaseVO input) {
+        int statusCode = HTTP_OK;
+        String host = getSecretHeader(request);
+        if (host != null){
+            if (input == null){
+                return generateResponse(HTTP_INTERNAL_ERROR, "Exception occured" , "{}");
+            }
+            String caseId = input.getId();
+            String name = input.getName();
+            String url = input.getUrl();
+            String description = input.getDescription();
+            String typeName =input.getTypeName();
+            Integer domain = (input.getDomain() == null) ? 0 : input.getDomain();
+            Long appId = input.getAppId();
+
+            if (caseId == null || name == null || url == null || typeName == null || appId == null) {
+                return missingParameterResponse;
+            }
+
+            String message = apiService.addCase(host, caseId, typeName, name, url, description, domain, appId);
+
+            return generateResponse(statusCode, message, apiService.booleanToJSON(message == null || message.length() == 0));
+        }
+        return generateResponse(statusCode, "Subsite mismatch", "{}");
+    }
+
+    @Override
+    public String addCaseKivul(HttpServletRequest request, AddCaseKivulVO input) {
+        int statusCode = HTTP_OK;
+        String host = getSecretHeader(request);
+        if (host != null){
+            if (input == null){
+                return generateResponse(HTTP_INTERNAL_ERROR, "Exception occured" , "{}");
+            }
+            Long managerId = input.getManagerId();
+            String caseId = input.getId();
+            String name = input.getName();
+            String url = input.getUrl();
+            String description = input.getDescription();
+            String typeName =input.getTypeName();
+            Integer domain = (input.getDomain() == null) ? 0 : input.getDomain();
+            boolean isPublic = (input.isPublic() == null) ? true : input.isPublic();
+
+            if (managerId == null || caseId == null || name == null || url == null || typeName == null) {
+                return missingParameterResponse;
+            }
+
+            String message = apiService.addCase(host, caseId, typeName, managerId, name, url, description, domain, isPublic);
+
+            return generateResponse(statusCode, message, apiService.booleanToJSON(message == null || message.length() == 0));
+        }
+        return generateResponse(statusCode, "Subsite mismatch", "{}");
+    }
+
+    @Override
+    public String uploadApp(HttpServletRequest request, UploadAppVO input) {
+        int statusCode = HTTP_OK;
+        String host = getSecretHeader(request);
+        if (host != null){
+            if (input == null){
+                return generateResponse(HTTP_INTERNAL_ERROR, "Exception occured" , "{}");
+            }
+            Boolean isPublic = input.getPublic();
+            String name = input.getName();
+            String caseUrl = input.getCaseUrl();
+            Long uploaderId = input.getUploaderId();
+
+            if (isPublic == null || name == null || caseUrl == null || uploaderId == null) {
+                return missingParameterResponse;
+            }
+
+            String message = apiService.uploadApp(host, name, caseUrl, isPublic, uploaderId);
+
+            return generateResponse(statusCode, message, apiService.booleanToJSON(message == null || message.length() == 0));
+        }
+        return generateResponse(statusCode, "Subsite mismatch", "{}");
+    }
+
+    @Override
+    public String recordScore(HttpServletRequest request, RecordScoreVO input) {
+        int statusCode = HTTP_OK;
+        String host = getSecretHeader(request);
+        if (host != null){
+            if (input == null){
+                return generateResponse(HTTP_INTERNAL_ERROR, "Exception occured" , "{}");
+            }
+            Long workerId = input.getWorkerId();
+            String subsiteCaseId = input.getCaseId();
+            Long taskId = input.getTaskId();
+            Double score = input.getScore();
+            String resultUrl = input.getResultUrl();
+
+            if (workerId == null || subsiteCaseId == null || taskId == null || score == null || resultUrl == null){
+                return missingParameterResponse;
+            }
+
+            String error = apiService.checkCaseExists(workerId, subsiteCaseId, taskId, host);
+            if (error != null && error.length() != 0){
+                return generateResponse(statusCode, error, "{}");
+            }
+
+            // Check if the worker has picked the case
+            String picked = apiService.checkCaseAssignment(workerId, subsiteCaseId, taskId, host);
+
+            if (picked == null){
+                return generateResponse(statusCode, "the case has not picked by this worker", "{}");
+            }
+            else{
+                // Add score to result JSON and update total score
+                boolean success = apiService.addScore(workerId, taskId, score, resultUrl, picked, host);
+                return generateResponse(statusCode, success ? "" : "less score", apiService.booleanToJSON(success));
+            }
+        }
+        return generateResponse(statusCode, "Subsite mismatch", "{}");
+    }
+
+    @Override
+    public String joinGroup(HttpServletRequest request, JoinGroupVO input) {
+        int statusCode = HTTP_OK;
+        String host = getSecretHeader(request);
+        if (host != null){
+            if (input == null){
+                return generateResponse(HTTP_INTERNAL_ERROR, "Exception occured" , "{}");
+            }
+            Long workerId = input.getWorkerId();
+            if (!checkWorkerToken(request, workerId)){
+                return generateResponse(statusCode, "worker token mismatch", "{}");
+            }
+            Long groupId = input.getGroupId();
+            String managerName = input.getManagerName();
+            boolean success = apiService.joinGroup(workerId, groupId, managerName);
+            return generateResponse(statusCode, "", "{\"success\":" + success + "}");
+        }
+        return generateResponse(statusCode, "Subsite mismatch", "{}");
+    }
+
+    @Override
+    public String verifyApp(HttpServletRequest request, VerifyAppVO input) {
+        int statusCode = HTTP_OK;
+        String host = getSecretHeader(request);
+        if (host != null){
+            if (input == null){
+                return generateResponse(HTTP_INTERNAL_ERROR, "Exception occured" , "{}");
+            }
+            Long appId = input.getAppId();
+            if (!checkAppToken(appId)){
+                return generateResponse(statusCode, "application not exists", "{}");
+            }
+            String url = input.getUrl();
+            boolean success = apiService.verifyApp(appId, url);
+            return generateResponse(statusCode, "", "{\"success\":" + success + "}");
+        }
+        return generateResponse(statusCode, "Subsite mismatch", "{}");
+    }
+}
+