Changes
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
package com.budwk.app.zhgh.dayofficework.qsv.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
|
||||
@IocBean
|
||||
@At("/platform/qsv")
|
||||
@Ok("json:full")
|
||||
@ApiOperation("问卷调查新建")
|
||||
public class QsvNewController {
|
||||
|
||||
@At("/new")
|
||||
@Ok("beetl:/platform/zhgh/dayofficework/qsv/activity/new.html")
|
||||
@SaCheckPermission("qsv.activity")
|
||||
public void newPage() {
|
||||
|
||||
}
|
||||
}
|
||||
+562
@@ -0,0 +1,562 @@
|
||||
package com.budwk.app.zhgh.dayofficework.qsv.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.param.PageForm;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.activity.basic.models.ActivityUserScope;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.models.QsvActivity;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.models.QsvOption;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.models.QsvSubject;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.models.QsvUserAnswerRecord;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.param.QsvAnswerParam;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.dto.QsvCheckAnswerResult;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.service.QsvQuizService;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.service.QsvUserAnswerRecordService;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@IocBean
|
||||
@At("/platform/qsv/online")
|
||||
@Ok("json:full")
|
||||
@ApiOperation("在线答题")
|
||||
public class QsvOnlineController {
|
||||
|
||||
@Inject
|
||||
private Dao dao;
|
||||
|
||||
@Inject
|
||||
private QsvUserAnswerRecordService qsvUserAnswerRecordService;
|
||||
@Inject
|
||||
private QsvQuizService qsvQuizService;
|
||||
|
||||
@At("")
|
||||
@Ok("beetl:/platform/zhgh/dayofficework/qsv/online/index.html")
|
||||
@SaCheckPermission("qsv.online")
|
||||
public void index() {
|
||||
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("qsv.online")
|
||||
@ApiOperation("在线答题任务分页")
|
||||
public Result pageData(@Valid PageForm pageForm, Integer year, String title, Boolean isAnswered) {
|
||||
List<Integer> groupIds = dao.query(ActivityUserScope.class, Cnd.where("userId", "=", SecurityUtil.getUserId()))
|
||||
.stream().map(ActivityUserScope::getGroupId).collect(Collectors.toList());
|
||||
if (groupIds.isEmpty()) {
|
||||
return Result.success(new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), 0, new ArrayList<>()));
|
||||
}
|
||||
|
||||
Cnd cnd = Cnd.where("enabled", "=", true);
|
||||
cnd.and("groupId", "in", groupIds);
|
||||
cnd.andEX("YEAR(startTime)", "=", year);
|
||||
cnd.and(Cnd.likeEX("title", title));
|
||||
cnd.desc("startTime");
|
||||
|
||||
int totalCount = dao.count(QsvActivity.class, cnd);
|
||||
if (totalCount == 0) {
|
||||
return Result.success(new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), 0, new ArrayList<>()));
|
||||
}
|
||||
List<QsvActivity> activities = dao.query(QsvActivity.class, cnd);
|
||||
List<String> activityIds = activities.stream().map(QsvActivity::getId).collect(Collectors.toList());
|
||||
Map<String, List<QsvUserAnswerRecord>> recordGroup = dao.query(QsvUserAnswerRecord.class, Cnd.where("userId", "=", SecurityUtil.getUserId())
|
||||
.and("activityId", "in", activityIds))
|
||||
.stream().collect(Collectors.groupingBy(QsvUserAnswerRecord::getActivityId));
|
||||
List<NutMap> rows = activities.stream()
|
||||
.map((activity) -> buildOnlineRow(activity, recordGroup.getOrDefault(activity.getId(), new ArrayList<>())))
|
||||
.filter((row) -> {
|
||||
if (Boolean.TRUE.equals(isAnswered)) {
|
||||
return row.getBoolean("isAnswered");
|
||||
}
|
||||
return !row.getBoolean("isAnswered") || row.getBoolean("canAnswer");
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
int filteredTotal = rows.size();
|
||||
int fromIndex = Math.min((pageForm.getPageNumber() - 1) * pageForm.getPageSize(), filteredTotal);
|
||||
int toIndex = Math.min(fromIndex + pageForm.getPageSize(), filteredTotal);
|
||||
return Result.success(new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), filteredTotal, rows.subList(fromIndex, toIndex)));
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("qsv.online")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@ApiOperation("在线答题题目")
|
||||
public Result subjects(@Valid String activityId, Boolean viewOnly) {
|
||||
QsvActivity activity = dao.fetch(QsvActivity.class, activityId);
|
||||
if (activity == null) {
|
||||
return Result.error("调查不存在");
|
||||
}
|
||||
QsvUserAnswerRecord answerRecord = findCurrentAnswerRecord(activityId, Boolean.TRUE.equals(viewOnly));
|
||||
if (Boolean.TRUE.equals(viewOnly) && answerRecord == null) {
|
||||
return Result.error("未找到已完成的答题记录");
|
||||
}
|
||||
boolean finishedView = Boolean.TRUE.equals(viewOnly) && answerRecord != null && Boolean.TRUE.equals(answerRecord.getIsFinish());
|
||||
if (!finishedView) {
|
||||
Result activityStatusResult = checkActivityStatus(activity);
|
||||
if (activityStatusResult != null) {
|
||||
return activityStatusResult;
|
||||
}
|
||||
}
|
||||
Result scopeResult = checkUserScope(activity);
|
||||
if (scopeResult != null) {
|
||||
return scopeResult;
|
||||
}
|
||||
|
||||
answerRecord = prepareAnswerRecord(activity, answerRecord);
|
||||
List<QsvSubject> activitySubjects = queryActivitySubjectsForSync(activity);
|
||||
syncAnswerRecordSubjects(answerRecord, activity, activitySubjects);
|
||||
|
||||
List<QsvSubject> subjects = querySubjectsByRecordOrder(answerRecord.getSubjectIds());
|
||||
dao.fetchLinks(subjects, "options", Cnd.NEW().asc("sortNum"));
|
||||
subjects.forEach((subject) -> {
|
||||
if (subject.getUserSelectOptionIds() == null) {
|
||||
subject.setUserSelectOptionIds(new ArrayList<>());
|
||||
}
|
||||
});
|
||||
|
||||
return Result.success(NutMap.NEW()
|
||||
.addv("activity", activity)
|
||||
.addv("subjects", subjects)
|
||||
.addv("answerRecordId", answerRecord.getId()));
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("qsv.online")
|
||||
@ApiOperation("在线答题记录")
|
||||
public Result answerRecord(@Valid String answerRecordId) {
|
||||
QsvUserAnswerRecord record = dao.fetch(QsvUserAnswerRecord.class, answerRecordId);
|
||||
return Result.success(record);
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("qsv.online")
|
||||
@ApiOperation("在线投票统计")
|
||||
public Result voteStats(@Valid String activityId) {
|
||||
QsvActivity activity = dao.fetch(QsvActivity.class, activityId);
|
||||
if (activity == null || !"VOTE".equals(activity.getCategory())) {
|
||||
return Result.error("投票不存在");
|
||||
}
|
||||
Result scopeResult = checkUserScope(activity);
|
||||
if (scopeResult != null) {
|
||||
return scopeResult;
|
||||
}
|
||||
return Result.success(buildVoteStats(activityId));
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("qsv.online")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@ApiOperation("提交在线答题")
|
||||
public Result submitAnswer(@Valid @Param("answer") QsvAnswerParam qsvAnswerParam) {
|
||||
QsvActivity activity = dao.fetch(QsvActivity.class, qsvAnswerParam.getActivityId());
|
||||
Result activityStatusResult = checkActivityStatus(activity);
|
||||
if (activityStatusResult != null) {
|
||||
return activityStatusResult;
|
||||
}
|
||||
Result scopeResult = checkUserScope(activity);
|
||||
if (scopeResult != null) {
|
||||
return scopeResult;
|
||||
}
|
||||
|
||||
QsvUserAnswerRecord answerRecord = dao.fetch(QsvUserAnswerRecord.class, qsvAnswerParam.getAnswerRecordId());
|
||||
if (answerRecord == null || !SecurityUtil.getUserId().equals(answerRecord.getUserId())) {
|
||||
return Result.error("答题记录不存在");
|
||||
}
|
||||
if (Boolean.TRUE.equals(answerRecord.getIsFinish())) {
|
||||
return Result.error("您已完成该调查");
|
||||
}
|
||||
|
||||
JSONObject extJson = answerRecord.getExtJson();
|
||||
if (extJson == null) {
|
||||
extJson = new JSONObject();
|
||||
}
|
||||
float totalScore = 0;
|
||||
boolean isQuiz = "QUIZ".equals(activity.getCategory());
|
||||
for (QsvAnswerParam.Subject subject : qsvAnswerParam.getSubjects()) {
|
||||
JSONObject entries = extJson.get(subject.getId(), JSONObject.class);
|
||||
if (ObjectUtil.isEmpty(entries)) {
|
||||
entries = new JSONObject();
|
||||
}
|
||||
entries.set("optionIds", subject.getUserSelectOptionIds());
|
||||
entries.set("text", subject.getUserFillContent());
|
||||
entries.set("optionFillContents", subject.getOptionFillContents());
|
||||
if (isQuiz) {
|
||||
QsvCheckAnswerResult answerResult = qsvQuizService.calcScore(subject.getId(), subject.getUserSelectOptionIds());
|
||||
entries.set("score", answerResult.getScore());
|
||||
entries.set("isCorrect", answerResult.isCorrect());
|
||||
totalScore += answerResult.getScore();
|
||||
}
|
||||
extJson.set(subject.getId(), entries);
|
||||
}
|
||||
|
||||
answerRecord.setExtJson(extJson);
|
||||
if (isQuiz) {
|
||||
answerRecord.setTotalScore(totalScore);
|
||||
answerRecord.setAnswerTime(qsvAnswerParam.getAnswerTime());
|
||||
}
|
||||
answerRecord.setAttemptDate(new Date());
|
||||
answerRecord.setSubmitTime(new Date());
|
||||
answerRecord.setIsFinish(true);
|
||||
dao.update(answerRecord);
|
||||
if (isQuiz) {
|
||||
qsvUserAnswerRecordService.calcByScoreMode(qsvAnswerParam.getActivityId(), SecurityUtil.getUserId());
|
||||
}
|
||||
return Result.success("提交成功");
|
||||
}
|
||||
|
||||
private Result checkActivityStatus(QsvActivity activity) {
|
||||
if (activity == null) {
|
||||
return Result.error("调查不存在");
|
||||
}
|
||||
if (Boolean.FALSE.equals(activity.getEnabled())) {
|
||||
return Result.error("活动已关闭,暂不能参与");
|
||||
}
|
||||
Date now = new Date();
|
||||
if (activity.getStartTime() != null && activity.getStartTime().after(now)) {
|
||||
return Result.error("调查尚未开始,请在开始后再参与");
|
||||
}
|
||||
if (activity.getEndTime() != null && activity.getEndTime().before(now)) {
|
||||
return Result.error("调查已结束");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Result checkUserScope(QsvActivity activity) {
|
||||
int count = dao.count(ActivityUserScope.class, Cnd.where("groupId", "=", activity.getGroupId())
|
||||
.and("userId", "=", SecurityUtil.getUserId()));
|
||||
if (count != 1) {
|
||||
return Result.error("您无需参加此次调查,感谢您的关注!");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private NutMap buildOnlineRow(QsvActivity activity, List<QsvUserAnswerRecord> records) {
|
||||
boolean isAnswered = records.stream().anyMatch((record) -> Boolean.TRUE.equals(record.getIsFinish()));
|
||||
boolean hasUnfinished = records.stream().anyMatch((record) -> !Boolean.TRUE.equals(record.getIsFinish()));
|
||||
boolean canRepeat = "QUIZ".equals(activity.getCategory())
|
||||
&& Boolean.TRUE.equals(activity.getRepeatable())
|
||||
&& ("SCHEDULED".equals(activity.getMode()) || ("REGULAR".equals(activity.getMode()) && "ALL".equals(activity.getDisplayMode())));
|
||||
boolean randomDisplayMode = "QUIZ".equals(activity.getCategory())
|
||||
&& "REGULAR".equals(activity.getMode())
|
||||
&& "RANDOM".equals(activity.getDisplayMode());
|
||||
int maxAttempts = activity.getMaxAttempts() == null || activity.getMaxAttempts() < 1 ? 1 : activity.getMaxAttempts();
|
||||
int totalRandom = activity.getTotalRandom() == null || activity.getTotalRandom() < 1 ? 1 : activity.getTotalRandom();
|
||||
long attemptCount = records.size();
|
||||
if ("SCHEDULED".equals(activity.getMode())) {
|
||||
attemptCount = records.stream()
|
||||
.filter((record) -> record.getAttemptDate() != null && DateUtil.isSameDay(record.getAttemptDate(), DateUtil.date()))
|
||||
.count();
|
||||
}
|
||||
Date now = new Date();
|
||||
boolean notStarted = activity.getStartTime() != null && activity.getStartTime().after(now);
|
||||
boolean ended = activity.getEndTime() != null && activity.getEndTime().before(now);
|
||||
boolean activityAvailable = Boolean.TRUE.equals(activity.getEnabled()) && !notStarted && !ended;
|
||||
boolean canAnswerByAttempt = hasUnfinished || !isAnswered || (canRepeat && attemptCount < maxAttempts) || (randomDisplayMode && attemptCount < totalRandom);
|
||||
boolean canAnswer = activityAvailable && canAnswerByAttempt;
|
||||
boolean isVote = "VOTE".equals(activity.getCategory());
|
||||
String actionText;
|
||||
String actionType;
|
||||
String statusText;
|
||||
String statusType;
|
||||
if (!activityAvailable && !isAnswered) {
|
||||
if (!Boolean.TRUE.equals(activity.getEnabled())) {
|
||||
actionText = "已关闭";
|
||||
statusText = "已关闭";
|
||||
} else if (notStarted) {
|
||||
actionText = isVote ? "未开始投票" : "未开始答题";
|
||||
statusText = "未开始";
|
||||
} else {
|
||||
actionText = isVote ? "投票已结束" : "答题已结束";
|
||||
statusText = "已结束";
|
||||
}
|
||||
actionType = "unavailable";
|
||||
statusType = "info";
|
||||
} else if (hasUnfinished && canAnswer) {
|
||||
actionText = isVote ? "继续投票" : "继续答题";
|
||||
actionType = "answer";
|
||||
statusText = isVote ? "投票中" : "答题中";
|
||||
statusType = "warning";
|
||||
} else if (!isAnswered && canAnswer) {
|
||||
actionText = isVote ? "投票" : "答题";
|
||||
actionType = "answer";
|
||||
statusText = isVote ? "未投票" : "未答题";
|
||||
statusType = "warning";
|
||||
} else if (canAnswer) {
|
||||
actionText = isVote ? "再次投票" : "再次答题";
|
||||
actionType = "answer";
|
||||
statusText = isVote ? "可再次投票" : "可再次答题";
|
||||
statusType = "warning";
|
||||
} else {
|
||||
actionText = isVote ? "查看投票" : "查看答题";
|
||||
actionType = "view";
|
||||
statusText = isVote ? "已投票" : "已答题";
|
||||
statusType = "success";
|
||||
}
|
||||
Optional<QsvUserAnswerRecord> latestFinishedRecord = records.stream()
|
||||
.filter((record) -> Boolean.TRUE.equals(record.getIsFinish()))
|
||||
.max((first, second) -> {
|
||||
long firstTime = first.getSubmitTime() == null ? 0 : first.getSubmitTime().getTime();
|
||||
long secondTime = second.getSubmitTime() == null ? 0 : second.getSubmitTime().getTime();
|
||||
return Long.compare(firstTime, secondTime);
|
||||
});
|
||||
return NutMap.NEW()
|
||||
.addv("id", activity.getId())
|
||||
.addv("title", activity.getTitle())
|
||||
.addv("category", activity.getCategory())
|
||||
.addv("startTime", activity.getStartTime())
|
||||
.addv("endTime", activity.getEndTime())
|
||||
.addv("enabled", activity.getEnabled())
|
||||
.addv("isAnswered", isAnswered)
|
||||
.addv("hasUnfinished", hasUnfinished)
|
||||
.addv("canAnswer", canAnswer)
|
||||
.addv("notStarted", notStarted)
|
||||
.addv("ended", ended)
|
||||
.addv("canRepeat", canRepeat)
|
||||
.addv("answeredCount", records.stream().filter((record) -> Boolean.TRUE.equals(record.getIsFinish())).count())
|
||||
.addv("remainingAttempts", canRepeat ? Math.max(maxAttempts - attemptCount, 0) : (randomDisplayMode ? Math.max(totalRandom - attemptCount, 0) : 0))
|
||||
.addv("actionText", actionText)
|
||||
.addv("actionType", actionType)
|
||||
.addv("statusText", statusText)
|
||||
.addv("statusType", statusType)
|
||||
.addv("totalScore", latestFinishedRecord.map(QsvUserAnswerRecord::getTotalScore).orElse(null));
|
||||
}
|
||||
|
||||
private QsvUserAnswerRecord findCurrentAnswerRecord(String activityId, boolean viewOnly) {
|
||||
Cnd cnd = Cnd.where("activityId", "=", activityId)
|
||||
.and("userId", "=", SecurityUtil.getUserId());
|
||||
if (viewOnly) {
|
||||
cnd.and("isFinish", "=", true).desc("submitTime").desc("createdAt");
|
||||
} else {
|
||||
cnd.and(Cnd.exps("isFinish", "=", false).or("isFinish", "is", null)).desc("createdAt");
|
||||
}
|
||||
return dao.fetch(QsvUserAnswerRecord.class, cnd);
|
||||
}
|
||||
|
||||
private QsvUserAnswerRecord prepareAnswerRecord(QsvActivity activity, QsvUserAnswerRecord answerRecord) {
|
||||
if (answerRecord != null) {
|
||||
return answerRecord;
|
||||
}
|
||||
if ("QUIZ".equals(activity.getCategory())
|
||||
&& "REGULAR".equals(activity.getMode())
|
||||
&& "RANDOM".equals(activity.getDisplayMode())) {
|
||||
return createRandomAnswerRecord(activity);
|
||||
}
|
||||
List<QsvSubject> activitySubjects = queryActivitySubjectsForSync(activity);
|
||||
return qsvUserAnswerRecordService.insertRecord(activity.getId(), activitySubjects.stream().map(QsvSubject::getId).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
private QsvUserAnswerRecord createRandomAnswerRecord(QsvActivity activity) {
|
||||
Integer randomCount = activity.getRandomCount();
|
||||
Integer totalRandom = activity.getTotalRandom();
|
||||
if (randomCount == null || randomCount < 1) {
|
||||
return qsvUserAnswerRecordService.insertRecord(activity.getId(), new ArrayList<>());
|
||||
}
|
||||
if (totalRandom == null || totalRandom < 1) {
|
||||
totalRandom = 1;
|
||||
}
|
||||
List<QsvUserAnswerRecord> answerRecords = dao.query(QsvUserAnswerRecord.class, Cnd.where("activityId", "=", activity.getId())
|
||||
.and("userId", "=", SecurityUtil.getUserId()));
|
||||
if (answerRecords.size() >= totalRandom) {
|
||||
return answerRecords.stream()
|
||||
.max((first, second) -> Integer.compare(first.getRandomNumber() == null ? 0 : first.getRandomNumber(), second.getRandomNumber() == null ? 0 : second.getRandomNumber()))
|
||||
.orElse(null);
|
||||
}
|
||||
List<String> usedSubjectIds = answerRecords.stream()
|
||||
.filter((record) -> record.getSubjectIds() != null)
|
||||
.flatMap((record) -> record.getSubjectIds().stream())
|
||||
.collect(Collectors.toList());
|
||||
Cnd subjectCnd = Cnd.where("activityId", "=", activity.getId());
|
||||
if (!usedSubjectIds.isEmpty()) {
|
||||
subjectCnd.and("id", "not in", usedSubjectIds);
|
||||
}
|
||||
List<QsvSubject> subjects = dao.query(QsvSubject.class, subjectCnd.asc("sortNum"));
|
||||
if (subjects.size() < randomCount) {
|
||||
throw new RuntimeException("题目数不够,无法生成题目");
|
||||
}
|
||||
Collections.shuffle(subjects);
|
||||
List<String> randomSubjectIds = subjects.subList(0, randomCount).stream().map(QsvSubject::getId).collect(Collectors.toList());
|
||||
return qsvUserAnswerRecordService.insertRecord(activity.getId(), randomSubjectIds);
|
||||
}
|
||||
|
||||
private List<QsvSubject> queryActivitySubjectsForSync(QsvActivity activity) {
|
||||
Cnd cnd = Cnd.where("activityId", "=", activity.getId());
|
||||
if ("QUIZ".equals(activity.getCategory())
|
||||
&& "SCHEDULED".equals(activity.getMode())) {
|
||||
cnd.and("displayDate", "=", DateUtil.today());
|
||||
}
|
||||
return dao.query(QsvSubject.class, cnd.asc("sortNum"));
|
||||
}
|
||||
|
||||
private void syncAnswerRecordSubjects(QsvUserAnswerRecord answerRecord, QsvActivity activity, List<QsvSubject> activitySubjects) {
|
||||
if (answerRecord == null || Boolean.TRUE.equals(answerRecord.getIsFinish())) {
|
||||
return;
|
||||
}
|
||||
List<String> currentSubjectIds = activitySubjects.stream().map(QsvSubject::getId).collect(Collectors.toList());
|
||||
List<String> recordSubjectIds = answerRecord.getSubjectIds();
|
||||
if (recordSubjectIds == null) {
|
||||
recordSubjectIds = new ArrayList<>();
|
||||
}
|
||||
JSONObject extJson = answerRecord.getExtJson();
|
||||
if (extJson == null) {
|
||||
extJson = new JSONObject();
|
||||
}
|
||||
|
||||
boolean randomDisplayMode = "QUIZ".equals(activity.getCategory())
|
||||
&& "REGULAR".equals(activity.getMode())
|
||||
&& "RANDOM".equals(activity.getDisplayMode());
|
||||
LinkedHashSet<String> currentSubjectIdSet = new LinkedHashSet<>(currentSubjectIds);
|
||||
List<String> normalizedSubjectIds = recordSubjectIds.stream()
|
||||
.filter(currentSubjectIdSet::contains)
|
||||
.collect(Collectors.toList());
|
||||
if (randomDisplayMode) {
|
||||
currentSubjectIds = normalizedSubjectIds;
|
||||
} else {
|
||||
for (String subjectId : currentSubjectIds) {
|
||||
if (!normalizedSubjectIds.contains(subjectId)) {
|
||||
normalizedSubjectIds.add(subjectId);
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean changed = !recordSubjectIds.equals(normalizedSubjectIds);
|
||||
boolean subjectOrderLocked = Boolean.TRUE.equals(extJson.get("_subjectOrderLocked"));
|
||||
if ("QUIZ".equals(activity.getCategory())
|
||||
&& Boolean.TRUE.equals(activity.getShuffleSubject())
|
||||
&& !subjectOrderLocked
|
||||
&& isAnswerRecordEmpty(extJson, currentSubjectIds)) {
|
||||
Collections.shuffle(normalizedSubjectIds);
|
||||
extJson.set("_subjectOrderLocked", true);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
for (String subjectId : currentSubjectIds) {
|
||||
if (ObjectUtil.isEmpty(extJson.get(subjectId, JSONObject.class))) {
|
||||
extJson.set(subjectId, Dict.create()
|
||||
.set("optionIds", new ArrayList<>())
|
||||
.set("text", null)
|
||||
.set("optionFillContents", new JSONObject()));
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
answerRecord.setSubjectIds(normalizedSubjectIds);
|
||||
answerRecord.setExtJson(extJson);
|
||||
dao.update(answerRecord);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAnswerRecordEmpty(JSONObject extJson, List<String> subjectIds) {
|
||||
for (String subjectId : subjectIds) {
|
||||
JSONObject answer = extJson.get(subjectId, JSONObject.class);
|
||||
if (ObjectUtil.isEmpty(answer)) {
|
||||
continue;
|
||||
}
|
||||
if (ObjectUtil.isNotEmpty(answer.get("optionIds"))
|
||||
|| ObjectUtil.isNotEmpty(answer.get("text"))
|
||||
|| ObjectUtil.isNotEmpty(answer.get("optionFillContents"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private List<QsvSubject> querySubjectsByRecordOrder(List<String> subjectIds) {
|
||||
if (subjectIds == null || subjectIds.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<QsvSubject> subjects = dao.query(QsvSubject.class, Cnd.where("id", "in", subjectIds));
|
||||
Map<String, QsvSubject> subjectMap = subjects.stream()
|
||||
.collect(Collectors.toMap(QsvSubject::getId, Function.identity(), (oldValue, newValue) -> oldValue));
|
||||
return subjectIds.stream()
|
||||
.map(subjectMap::get)
|
||||
.filter(ObjectUtil::isNotEmpty)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private NutMap buildVoteStats(String activityId) {
|
||||
List<QsvSubject> subjects = dao.query(QsvSubject.class, Cnd.where("activityId", "=", activityId).asc("sortNum"));
|
||||
dao.fetchLinks(subjects, "options", Cnd.NEW().asc("sortNum"));
|
||||
|
||||
Map<String, NutMap> optionStats = new LinkedHashMap<>();
|
||||
for (QsvSubject subject : subjects) {
|
||||
if (subject.getOptions() == null) {
|
||||
continue;
|
||||
}
|
||||
for (QsvOption option : subject.getOptions()) {
|
||||
optionStats.put(option.getId(), NutMap.NEW()
|
||||
.addv("optionId", option.getId())
|
||||
.addv("subjectId", subject.getId())
|
||||
.addv("text", option.getText())
|
||||
.addv("imgUrl", option.getImgUrl())
|
||||
.addv("votes", 0)
|
||||
.addv("percent", 0D));
|
||||
}
|
||||
}
|
||||
|
||||
List<QsvUserAnswerRecord> records = dao.query(QsvUserAnswerRecord.class, Cnd.where("activityId", "=", activityId)
|
||||
.and("isFinish", "=", true));
|
||||
for (QsvUserAnswerRecord record : records) {
|
||||
JSONObject extJson = record.getExtJson();
|
||||
if (extJson == null) {
|
||||
continue;
|
||||
}
|
||||
for (QsvSubject subject : subjects) {
|
||||
JSONObject entry = extJson.get(subject.getId(), JSONObject.class);
|
||||
if (entry == null) {
|
||||
continue;
|
||||
}
|
||||
List<String> optionIds = entry.getBeanList("optionIds", String.class);
|
||||
if (optionIds == null) {
|
||||
continue;
|
||||
}
|
||||
for (String optionId : optionIds) {
|
||||
NutMap stat = optionStats.get(optionId);
|
||||
if (stat != null) {
|
||||
stat.put("votes", stat.getInt("votes", 0) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int totalParticipants = records.size();
|
||||
List<NutMap> rankList = new ArrayList<>(optionStats.values());
|
||||
for (NutMap stat : rankList) {
|
||||
int votes = stat.getInt("votes", 0);
|
||||
double percent = totalParticipants == 0 ? 0D : votes * 100D / totalParticipants;
|
||||
stat.put("percent", percent);
|
||||
}
|
||||
rankList.sort((left, right) -> Integer.compare(right.getInt("votes", 0), left.getInt("votes", 0)));
|
||||
|
||||
return NutMap.NEW()
|
||||
.addv("totalParticipants", totalParticipants)
|
||||
.addv("optionCount", optionStats.size())
|
||||
.addv("optionStats", optionStats)
|
||||
.addv("rankList", rankList);
|
||||
}
|
||||
}
|
||||
+301
@@ -0,0 +1,301 @@
|
||||
package com.budwk.app.zhgh.dayofficework.qsv.h5controller;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.activity.basic.models.ActivityUserScope;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.models.QsvActivity;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.models.QsvOption;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.models.QsvSubject;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.models.QsvUserAnswerRecord;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.param.QsvAnswerParam;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.service.QsvUserAnswerRecordService;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@IocBean
|
||||
@At("/platform/h5/qsv/vote")
|
||||
@Ok("json:full")
|
||||
public class H5QsvVoteController {
|
||||
|
||||
@Inject
|
||||
private Dao dao;
|
||||
@Inject
|
||||
private QsvUserAnswerRecordService qsvUserAnswerRecordService;
|
||||
|
||||
@At("")
|
||||
@Ok("beetl:/platform/zhghh5/dayofficework/qsv/vote/index.html")
|
||||
public void index() {
|
||||
}
|
||||
|
||||
@At
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result subjects(String activityId) {
|
||||
QsvActivity activity = dao.fetch(QsvActivity.class, activityId);
|
||||
Result activityStatusResult = checkActivityReadable(activity);
|
||||
if (activityStatusResult != null) {
|
||||
return activityStatusResult;
|
||||
}
|
||||
|
||||
Result scopeResult = checkScope(activity);
|
||||
if (scopeResult != null) {
|
||||
return scopeResult;
|
||||
}
|
||||
|
||||
List<QsvSubject> activitySubjects = dao.query(QsvSubject.class, Cnd.where("activityId", "=", activityId).asc("sortNum"));
|
||||
QsvUserAnswerRecord answerRecord = dao.fetch(QsvUserAnswerRecord.class, Cnd.where("activityId", "=", activityId)
|
||||
.and("userId", "=", SecurityUtil.getUserId()));
|
||||
if (answerRecord == null) {
|
||||
List<String> subjectIds = activitySubjects.stream()
|
||||
.map(QsvSubject::getId)
|
||||
.collect(Collectors.toList());
|
||||
answerRecord = qsvUserAnswerRecordService.insertRecord(activityId, subjectIds);
|
||||
}
|
||||
syncAnswerRecordSubjects(answerRecord, activitySubjects);
|
||||
|
||||
List<QsvSubject> subjects = querySubjectsByRecordOrder(answerRecord.getSubjectIds());
|
||||
dao.fetchLinks(subjects, "options", Cnd.NEW().asc("sortNum"));
|
||||
for (QsvSubject subject : subjects) {
|
||||
if (subject.getUserSelectOptionIds() == null) {
|
||||
subject.setUserSelectOptionIds(new ArrayList<>());
|
||||
}
|
||||
}
|
||||
|
||||
NutMap result = NutMap.NEW()
|
||||
.addv("subjects", subjects)
|
||||
.addv("answerRecordId", answerRecord.getId())
|
||||
.addv("answerRecord", answerRecord)
|
||||
.addv("activity", activity)
|
||||
.addv("voteStats", buildVoteStats(activityId));
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
@At
|
||||
public Result answerRecord(@Valid String answerRecordId) {
|
||||
QsvUserAnswerRecord record = dao.fetch(QsvUserAnswerRecord.class, answerRecordId);
|
||||
return Result.success(record);
|
||||
}
|
||||
|
||||
@At
|
||||
public Result rank(@Valid String activityId) {
|
||||
return Result.success(buildVoteStats(activityId));
|
||||
}
|
||||
|
||||
@At
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result submitAnswer(@Valid @Param("answer") QsvAnswerParam qsvAnswerParam) {
|
||||
QsvActivity activity = dao.fetch(QsvActivity.class, qsvAnswerParam.getActivityId());
|
||||
Result activityStatusResult = checkActivityWritable(activity);
|
||||
if (activityStatusResult != null) {
|
||||
return activityStatusResult;
|
||||
}
|
||||
|
||||
QsvUserAnswerRecord answerRecord = dao.fetch(QsvUserAnswerRecord.class, qsvAnswerParam.getAnswerRecordId());
|
||||
if (answerRecord == null || !SecurityUtil.getUserId().equals(answerRecord.getUserId())) {
|
||||
return Result.error("\u6295\u7968\u8bb0\u5f55\u4e0d\u5b58\u5728");
|
||||
}
|
||||
if (Boolean.TRUE.equals(answerRecord.getIsFinish())) {
|
||||
return Result.error("\u60a8\u5df2\u5b8c\u6210\u6295\u7968");
|
||||
}
|
||||
|
||||
JSONObject extJson = answerRecord.getExtJson();
|
||||
if (extJson == null) {
|
||||
extJson = new JSONObject();
|
||||
}
|
||||
|
||||
for (QsvAnswerParam.Subject subject : qsvAnswerParam.getSubjects()) {
|
||||
JSONObject entries = extJson.get(subject.getId(), JSONObject.class);
|
||||
if (entries == null) {
|
||||
entries = new JSONObject();
|
||||
}
|
||||
entries.set("optionIds", subject.getUserSelectOptionIds());
|
||||
entries.set("text", subject.getUserFillContent());
|
||||
entries.set("optionFillContents", subject.getOptionFillContents());
|
||||
extJson.set(subject.getId(), entries);
|
||||
}
|
||||
|
||||
answerRecord.setExtJson(extJson);
|
||||
answerRecord.setAttemptDate(new Date());
|
||||
answerRecord.setSubmitTime(new Date());
|
||||
answerRecord.setIsFinish(true);
|
||||
dao.update(answerRecord);
|
||||
|
||||
return Result.success("\u6295\u7968\u6210\u529f");
|
||||
}
|
||||
|
||||
private Result checkActivityReadable(QsvActivity activity) {
|
||||
if (activity == null) {
|
||||
return Result.error("\u6295\u7968\u4e0d\u5b58\u5728");
|
||||
}
|
||||
if (Boolean.FALSE.equals(activity.getEnabled())) {
|
||||
return Result.error("\u6d3b\u52a8\u5df2\u5173\u95ed\uff0c\u6682\u4e0d\u80fd\u53c2\u4e0e");
|
||||
}
|
||||
Date now = new Date();
|
||||
if (activity.getStartTime() != null && activity.getStartTime().after(now)) {
|
||||
return Result.error("\u6295\u7968\u5c1a\u672a\u5f00\u59cb\uff0c\u8bf7\u5728\u5f00\u59cb\u540e\u518d\u53c2\u4e0e");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Result checkActivityWritable(QsvActivity activity) {
|
||||
Result readableResult = checkActivityReadable(activity);
|
||||
if (readableResult != null) {
|
||||
return readableResult;
|
||||
}
|
||||
Date now = new Date();
|
||||
if (activity.getEndTime() != null && activity.getEndTime().before(now)) {
|
||||
return Result.error("\u6295\u7968\u5df2\u7ed3\u675f");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Result checkScope(QsvActivity activity) {
|
||||
if (activity.getGroupId() == null) {
|
||||
return null;
|
||||
}
|
||||
int count = dao.count(ActivityUserScope.class, Cnd.where("groupId", "=", activity.getGroupId())
|
||||
.and("userId", "=", SecurityUtil.getUserId()));
|
||||
if (count != 1) {
|
||||
return Result.error("\u60a8\u65e0\u9700\u53c2\u52a0\u6b64\u6b21\u6295\u7968\uff0c\u611f\u8c22\u60a8\u7684\u5173\u6ce8\uff01");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void syncAnswerRecordSubjects(QsvUserAnswerRecord answerRecord, List<QsvSubject> activitySubjects) {
|
||||
if (answerRecord == null || Boolean.TRUE.equals(answerRecord.getIsFinish())) {
|
||||
return;
|
||||
}
|
||||
List<String> currentSubjectIds = activitySubjects.stream()
|
||||
.map(QsvSubject::getId)
|
||||
.collect(Collectors.toList());
|
||||
List<String> recordSubjectIds = answerRecord.getSubjectIds();
|
||||
if (recordSubjectIds == null) {
|
||||
recordSubjectIds = new ArrayList<>();
|
||||
}
|
||||
JSONObject extJson = answerRecord.getExtJson();
|
||||
if (extJson == null) {
|
||||
extJson = new JSONObject();
|
||||
}
|
||||
|
||||
LinkedHashSet<String> currentSubjectIdSet = new LinkedHashSet<>(currentSubjectIds);
|
||||
List<String> normalizedSubjectIds = recordSubjectIds.stream()
|
||||
.filter(currentSubjectIdSet::contains)
|
||||
.collect(Collectors.toList());
|
||||
for (String subjectId : currentSubjectIds) {
|
||||
if (!normalizedSubjectIds.contains(subjectId)) {
|
||||
normalizedSubjectIds.add(subjectId);
|
||||
}
|
||||
}
|
||||
|
||||
boolean changed = !recordSubjectIds.equals(normalizedSubjectIds);
|
||||
for (String subjectId : currentSubjectIds) {
|
||||
if (ObjectUtil.isEmpty(extJson.get(subjectId, JSONObject.class))) {
|
||||
JSONObject entry = new JSONObject();
|
||||
entry.set("optionIds", new ArrayList<>());
|
||||
entry.set("text", null);
|
||||
entry.set("optionFillContents", new JSONObject());
|
||||
extJson.set(subjectId, entry);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
answerRecord.setSubjectIds(normalizedSubjectIds);
|
||||
answerRecord.setExtJson(extJson);
|
||||
dao.update(answerRecord);
|
||||
}
|
||||
}
|
||||
|
||||
private List<QsvSubject> querySubjectsByRecordOrder(List<String> subjectIds) {
|
||||
if (subjectIds == null || subjectIds.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<QsvSubject> subjects = dao.query(QsvSubject.class, Cnd.where("id", "in", subjectIds));
|
||||
Map<String, QsvSubject> subjectMap = subjects.stream()
|
||||
.collect(Collectors.toMap(QsvSubject::getId, Function.identity(), (oldValue, newValue) -> oldValue));
|
||||
return subjectIds.stream()
|
||||
.map(subjectMap::get)
|
||||
.filter(ObjectUtil::isNotEmpty)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private NutMap buildVoteStats(String activityId) {
|
||||
List<QsvSubject> subjects = dao.query(QsvSubject.class, Cnd.where("activityId", "=", activityId).asc("sortNum"));
|
||||
dao.fetchLinks(subjects, "options", Cnd.NEW().asc("sortNum"));
|
||||
|
||||
Map<String, NutMap> optionStats = new LinkedHashMap<>();
|
||||
for (QsvSubject subject : subjects) {
|
||||
if (subject.getOptions() == null) {
|
||||
continue;
|
||||
}
|
||||
for (QsvOption option : subject.getOptions()) {
|
||||
optionStats.put(option.getId(), NutMap.NEW()
|
||||
.addv("optionId", option.getId())
|
||||
.addv("subjectId", subject.getId())
|
||||
.addv("text", option.getText())
|
||||
.addv("imgUrl", option.getImgUrl())
|
||||
.addv("votes", 0)
|
||||
.addv("percent", 0D));
|
||||
}
|
||||
}
|
||||
|
||||
List<QsvUserAnswerRecord> records = dao.query(QsvUserAnswerRecord.class, Cnd.where("activityId", "=", activityId)
|
||||
.and("isFinish", "=", true));
|
||||
for (QsvUserAnswerRecord record : records) {
|
||||
JSONObject extJson = record.getExtJson();
|
||||
if (extJson == null) {
|
||||
continue;
|
||||
}
|
||||
for (QsvSubject subject : subjects) {
|
||||
JSONObject entry = extJson.get(subject.getId(), JSONObject.class);
|
||||
if (entry == null) {
|
||||
continue;
|
||||
}
|
||||
List<String> optionIds = entry.getBeanList("optionIds", String.class);
|
||||
if (optionIds == null) {
|
||||
continue;
|
||||
}
|
||||
for (String optionId : optionIds) {
|
||||
NutMap stat = optionStats.get(optionId);
|
||||
if (stat != null) {
|
||||
stat.put("votes", stat.getInt("votes", 0) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int totalParticipants = records.size();
|
||||
List<NutMap> rankList = new ArrayList<>(optionStats.values());
|
||||
for (NutMap stat : rankList) {
|
||||
int votes = stat.getInt("votes", 0);
|
||||
double percent = totalParticipants == 0 ? 0D : votes * 100D / totalParticipants;
|
||||
stat.put("percent", percent);
|
||||
}
|
||||
rankList.sort((left, right) -> Integer.compare(right.getInt("votes", 0), left.getInt("votes", 0)));
|
||||
|
||||
return NutMap.NEW()
|
||||
.addv("totalParticipants", totalParticipants)
|
||||
.addv("optionCount", optionStats.size())
|
||||
.addv("optionStats", optionStats)
|
||||
.addv("rankList", rankList);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user