Changes
This commit is contained in:
+16
-10
@@ -59,18 +59,12 @@ public class QsvActivityController {
|
||||
//保存问卷基础信息
|
||||
@At
|
||||
@SaCheckPermission("qsv.activity")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@ApiOperation("保存问卷基础信息")
|
||||
@SLog(type = "qsv.activity", tag = "保存问卷基础信息", msg = "保存问卷基础信息")
|
||||
public Result save(QsvActivity qsvActivity) {
|
||||
if (qsvActivity.getCategory().equals("QUIZ")) {
|
||||
if (qsvActivity.getMode().equals("SCHEDULED")) {
|
||||
qsvActivity.setRepeatMode("DAILY");
|
||||
} else if (qsvActivity.getMode().equals("REGULAR")) {
|
||||
qsvActivity.setRepeatMode("TOTAL");
|
||||
}
|
||||
}
|
||||
dao.insertOrUpdate(qsvActivity);
|
||||
return Result.success();
|
||||
QsvActivity activity = qsvActivityService.saveActivity(qsvActivity);
|
||||
return Result.success(activity);
|
||||
}
|
||||
|
||||
@At
|
||||
@@ -83,13 +77,25 @@ public class QsvActivityController {
|
||||
// 删除问卷
|
||||
@At
|
||||
@SaCheckPermission("qsv.activity")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@ApiOperation("删除问卷")
|
||||
@SLog(type = "qsv.activity", tag = "删除问卷", msg = "删除问卷")
|
||||
public Result delete(@Valid String id) {
|
||||
dao.delete(QsvActivity.class, id);
|
||||
qsvActivityService.deleteActivity(id);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
// 更新活动开启状态
|
||||
@At
|
||||
@SaCheckPermission("qsv.activity")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@ApiOperation("更新活动开启状态")
|
||||
@SLog(type = "qsv.activity", tag = "更新活动开启状态", msg = "更新活动开启状态")
|
||||
public Result updateEnabled(@Valid String id, Boolean enabled) {
|
||||
qsvActivityService.updateEnabled(id, enabled);
|
||||
return Result.success(enabled ? "开启成功" : "关闭成功");
|
||||
}
|
||||
|
||||
|
||||
// 保存问卷题目
|
||||
@At
|
||||
|
||||
+287
-5
@@ -1,16 +1,27 @@
|
||||
package com.budwk.app.zhgh.dayofficework.qsv.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.budwk.app.base.annotation.SLog;
|
||||
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.base.utils.CommonDownloadUtil;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.models.QsvActivity;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.models.QsvUserAnswerRecord;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.service.QsvActivityService;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.service.QsvSurveyService;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||
import org.apache.pdfbox.pdmodel.font.PDType0Font;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
@@ -21,6 +32,10 @@ import org.nutz.mvc.annotation.Ok;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.Valid;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
|
||||
@IocBean
|
||||
@@ -43,12 +58,19 @@ public class QsvSurveyController {
|
||||
|
||||
}
|
||||
|
||||
@At("/reportPage")
|
||||
@Ok("beetl:/platform/zhgh/dayofficework/qsv/survey/reportPage.html")
|
||||
@SaCheckPermission("qsv.survey")
|
||||
public void reportPage() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@At
|
||||
@SaCheckPermission("qsv.survey")
|
||||
@ApiOperation("分页查询")
|
||||
public Result pageData(@Valid PageForm pageForm, Integer year) {
|
||||
Cnd cnd = Cnd.where("category", "=", "SURVEY");
|
||||
Cnd cnd = Cnd.where("category", "in", new String[]{"SURVEY", "VOTE"});
|
||||
cnd.andEX("YEAR(startTime)", "=", year);
|
||||
cnd.desc("category");
|
||||
Pagination pagination = qsvActivityService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), cnd);
|
||||
@@ -63,6 +85,43 @@ public class QsvSurveyController {
|
||||
return Result.success(report);
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("qsv.survey")
|
||||
@ApiOperation("查询调查信息")
|
||||
public Result activityInfo(@Valid String activityId) {
|
||||
QsvActivity activity = dao.fetch(QsvActivity.class, activityId);
|
||||
NutMap data = NutMap.NEW();
|
||||
if (activity != null) {
|
||||
data.setv("id", activity.getId());
|
||||
data.setv("title", activity.getTitle());
|
||||
}
|
||||
return Result.success(data);
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("qsv.survey")
|
||||
@ApiOperation("生成分类报告")
|
||||
public Result categoryReport(@Valid String activityId, String conditions) {
|
||||
List<NutMap> report = qsvSurveyService.categoryReport(activityId, conditions);
|
||||
return Result.success(report);
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("qsv.survey")
|
||||
@ApiOperation("生成交叉分析报告")
|
||||
public Result crossReport(@Valid String activityId, @Valid String xSubjectIds, @Valid String ySubjectIds, String conditions) {
|
||||
NutMap report = qsvSurveyService.crossReport(activityId, xSubjectIds, ySubjectIds, conditions);
|
||||
return Result.success(report);
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("qsv.survey")
|
||||
@ApiOperation("生成对比分析报告")
|
||||
public Result compareReport(@Valid String activityId, @Valid String subjectId, @Valid String optionId) {
|
||||
NutMap report = qsvSurveyService.compareReport(activityId, subjectId, optionId);
|
||||
return Result.success(report);
|
||||
}
|
||||
|
||||
|
||||
@At
|
||||
@Ok("void")
|
||||
@@ -72,13 +131,236 @@ public class QsvSurveyController {
|
||||
qsvSurveyService.exportReportXlsx(activityId, response);
|
||||
}
|
||||
|
||||
@At
|
||||
@Ok("void")
|
||||
@SaCheckPermission("qsv.survey")
|
||||
@ApiOperation("下载PDF报告")
|
||||
public void exportReportPdf(String reportData, HttpServletResponse response) {
|
||||
if (StrUtil.isBlank(reportData)) {
|
||||
throw new IllegalArgumentException("报告数据不能为空");
|
||||
}
|
||||
JSONObject report = JSONUtil.parseObj(reportData);
|
||||
String title = StrUtil.blankToDefault(report.getStr("title"), "分析报告");
|
||||
String tabLabel = StrUtil.blankToDefault(report.getStr("tabLabel"), "分析报告");
|
||||
try {
|
||||
CommonDownloadUtil.download(safeFileName(title + "-" + tabLabel) + ".pdf", buildReportPdf(report), response);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("生成PDF报告失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] buildReportPdf(JSONObject report) throws IOException {
|
||||
try (PDDocument document = new PDDocument(); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
PdfReportWriter writer = new PdfReportWriter(document);
|
||||
writer.title(StrUtil.blankToDefault(report.getStr("title"), "分析报告"));
|
||||
writer.text("报告类型:" + StrUtil.blankToDefault(report.getStr("tabLabel"), "分析报告"), 10);
|
||||
writer.text("生成时间:" + DateUtil.now(), 10);
|
||||
writer.gap(8);
|
||||
|
||||
JSONArray sections = report.getJSONArray("sections");
|
||||
if (sections == null || sections.isEmpty()) {
|
||||
writer.text("暂无可下载内容", 12);
|
||||
} else {
|
||||
for (int i = 0; i < sections.size(); i++) {
|
||||
JSONObject section = JSONUtil.parseObj(sections.get(i));
|
||||
writer.section(StrUtil.blankToDefault(section.getStr("title"), "统计项"));
|
||||
String description = section.getStr("description");
|
||||
if (StrUtil.isNotBlank(description)) {
|
||||
writer.text(description, 10);
|
||||
}
|
||||
String image = section.getStr("image");
|
||||
if (StrUtil.isNotBlank(image)) {
|
||||
writer.image(image);
|
||||
}
|
||||
writer.table(section.getJSONArray("rows"));
|
||||
writer.gap(8);
|
||||
}
|
||||
}
|
||||
writer.close();
|
||||
document.save(out);
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
private String safeFileName(String fileName) {
|
||||
return StrUtil.blankToDefault(fileName, "分析报告").replaceAll("[\\\\/:*?\"<>|\\r\\n]", "_");
|
||||
}
|
||||
|
||||
private PDType0Font loadChineseFont(PDDocument document, boolean bold) throws IOException {
|
||||
String[] paths = bold ? new String[]{
|
||||
"C:/Windows/Fonts/simhei.ttf",
|
||||
"C:/Windows/Fonts/simsunb.ttf",
|
||||
"C:/Windows/Fonts/msyhbd.ttf"
|
||||
} : new String[]{
|
||||
"C:/Windows/Fonts/simfang.ttf",
|
||||
"C:/Windows/Fonts/simsun.ttf",
|
||||
"C:/Windows/Fonts/msyh.ttf"
|
||||
};
|
||||
for (String path : paths) {
|
||||
File file = new File(path);
|
||||
if (file.exists()) {
|
||||
return PDType0Font.load(document, file);
|
||||
}
|
||||
}
|
||||
throw new IOException("未找到可用的中文字体");
|
||||
}
|
||||
|
||||
private class PdfReportWriter {
|
||||
private final PDDocument document;
|
||||
private final PDType0Font regularFont;
|
||||
private final PDType0Font boldFont;
|
||||
private final float margin = 42F;
|
||||
private final float pageWidth = PDRectangle.A4.getWidth();
|
||||
private final float pageHeight = PDRectangle.A4.getHeight();
|
||||
private final float contentWidth = pageWidth - margin * 2;
|
||||
private PDPageContentStream content;
|
||||
private float y;
|
||||
|
||||
PdfReportWriter(PDDocument document) throws IOException {
|
||||
this.document = document;
|
||||
this.regularFont = loadChineseFont(document, false);
|
||||
this.boldFont = loadChineseFont(document, true);
|
||||
newPage();
|
||||
}
|
||||
|
||||
void newPage() throws IOException {
|
||||
if (content != null) {
|
||||
content.close();
|
||||
}
|
||||
PDPage page = new PDPage(PDRectangle.A4);
|
||||
document.addPage(page);
|
||||
content = new PDPageContentStream(document, page);
|
||||
y = pageHeight - margin;
|
||||
}
|
||||
|
||||
void close() throws IOException {
|
||||
if (content != null) {
|
||||
content.close();
|
||||
content = null;
|
||||
}
|
||||
}
|
||||
|
||||
void ensure(float height) throws IOException {
|
||||
if (y - height < margin) {
|
||||
newPage();
|
||||
}
|
||||
}
|
||||
|
||||
void gap(float height) throws IOException {
|
||||
ensure(height);
|
||||
y -= height;
|
||||
}
|
||||
|
||||
void title(String text) throws IOException {
|
||||
ensure(36);
|
||||
drawText(text, margin, y, 18, boldFont);
|
||||
y -= 32;
|
||||
}
|
||||
|
||||
void section(String text) throws IOException {
|
||||
ensure(30);
|
||||
drawText(text, margin, y, 13, boldFont);
|
||||
y -= 24;
|
||||
}
|
||||
|
||||
void text(String text, float fontSize) throws IOException {
|
||||
List<String> lines = wrap(StrUtil.blankToDefault(text, ""), fontSize, contentWidth, regularFont);
|
||||
for (String line : lines) {
|
||||
ensure(fontSize + 8);
|
||||
drawText(line, margin, y, fontSize, regularFont);
|
||||
y -= fontSize + 7;
|
||||
}
|
||||
}
|
||||
|
||||
void image(String dataUrl) throws IOException {
|
||||
int commaIndex = dataUrl.indexOf(",");
|
||||
if (commaIndex < 0) {
|
||||
return;
|
||||
}
|
||||
byte[] bytes = Base64.getDecoder().decode(dataUrl.substring(commaIndex + 1));
|
||||
PDImageXObject image = PDImageXObject.createFromByteArray(document, bytes, "chart");
|
||||
float width = contentWidth;
|
||||
float height = width * image.getHeight() / image.getWidth();
|
||||
if (height > 260) {
|
||||
height = 260;
|
||||
width = height * image.getWidth() / image.getHeight();
|
||||
}
|
||||
ensure(height + 12);
|
||||
content.drawImage(image, margin, y - height, width, height);
|
||||
y -= height + 12;
|
||||
}
|
||||
|
||||
void table(JSONArray rows) throws IOException {
|
||||
if (rows == null || rows.isEmpty()) {
|
||||
text("暂无统计数据", 10);
|
||||
return;
|
||||
}
|
||||
float[] widths = new float[]{contentWidth * 0.46F, contentWidth * 0.15F, contentWidth * 0.17F, contentWidth * 0.22F};
|
||||
drawTableRow(new String[]{"选项/内容", "数量", "占比", "备注"}, widths, true);
|
||||
for (int i = 0; i < rows.size(); i++) {
|
||||
JSONObject row = JSONUtil.parseObj(rows.get(i));
|
||||
drawTableRow(new String[]{
|
||||
StrUtil.blankToDefault(row.getStr("name"), ""),
|
||||
StrUtil.blankToDefault(row.getStr("count"), ""),
|
||||
StrUtil.blankToDefault(row.getStr("percent"), ""),
|
||||
StrUtil.blankToDefault(row.getStr("remark"), "")
|
||||
}, widths, false);
|
||||
}
|
||||
}
|
||||
|
||||
void drawTableRow(String[] values, float[] widths, boolean header) throws IOException {
|
||||
float rowHeight = 24F;
|
||||
ensure(rowHeight);
|
||||
float x = margin;
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
content.addRect(x, y - rowHeight, widths[i], rowHeight);
|
||||
content.stroke();
|
||||
drawText(clip(values[i], header ? 16 : 28), x + 5, y - 16, 9, header ? boldFont : regularFont);
|
||||
x += widths[i];
|
||||
}
|
||||
y -= rowHeight;
|
||||
}
|
||||
|
||||
void drawText(String text, float x, float y, float fontSize, PDType0Font font) throws IOException {
|
||||
content.beginText();
|
||||
content.setFont(font, fontSize);
|
||||
content.newLineAtOffset(x, y);
|
||||
content.showText(StrUtil.blankToDefault(text, "").replaceAll("[\\r\\n\\t]", " "));
|
||||
content.endText();
|
||||
}
|
||||
|
||||
List<String> wrap(String text, float fontSize, float maxWidth, PDType0Font font) throws IOException {
|
||||
List<String> lines = new java.util.ArrayList<>();
|
||||
StringBuilder line = new StringBuilder();
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
char ch = text.charAt(i);
|
||||
String next = line.toString() + ch;
|
||||
if (font.getStringWidth(next) / 1000 * fontSize > maxWidth && line.length() > 0) {
|
||||
lines.add(line.toString());
|
||||
line.setLength(0);
|
||||
}
|
||||
line.append(ch);
|
||||
}
|
||||
if (line.length() > 0 || lines.isEmpty()) {
|
||||
lines.add(line.toString());
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
String clip(String text, int maxLength) {
|
||||
if (text == null || text.length() <= maxLength) {
|
||||
return StrUtil.blankToDefault(text, "");
|
||||
}
|
||||
return text.substring(0, maxLength) + "...";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@At
|
||||
@SaCheckPermission("qsv.survey")
|
||||
@ApiOperation("选项选择详情")
|
||||
public Result selectOptionUsers(@Valid String activityId, @Valid String subjectId, @Valid String optionId) {
|
||||
List<QsvUserAnswerRecord> answerRecords = dao.query(QsvUserAnswerRecord.class, Cnd.where("activityId", "=", activityId));
|
||||
List<QsvUserAnswerRecord> selectOptionUsers = answerRecords.stream().filter(ext -> ObjectUtil.isNotNull(ext.getExtJson().get(subjectId, JSONObject.class)) && ext.getExtJson().get(subjectId, JSONObject.class).getJSONArray("optionIds").contains(optionId)).toList();
|
||||
public Result selectOptionUsers(@Valid String activityId, @Valid String subjectId, @Valid String optionId, String conditions) {
|
||||
List<NutMap> selectOptionUsers = qsvSurveyService.selectOptionUsers(activityId, subjectId, optionId, conditions);
|
||||
return Result.success(selectOptionUsers);
|
||||
}
|
||||
|
||||
|
||||
+70
-2
@@ -1,19 +1,29 @@
|
||||
package com.budwk.app.zhgh.dayofficework.qsv.h5controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
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.dayofficework.qsv.models.QsvUserAnswerRecord;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.service.QsvActivityService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
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.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@IocBean
|
||||
@At("/platform/h5/qsv")
|
||||
@@ -23,6 +33,8 @@ public class H5QsvController {
|
||||
|
||||
@Inject
|
||||
private QsvActivityService qsvActivityService;
|
||||
@Inject
|
||||
private Dao dao;
|
||||
|
||||
@At("")
|
||||
@Ok("beetl:/platform/zhghh5/dayofficework/qsv/index.html")
|
||||
@@ -33,13 +45,69 @@ public class H5QsvController {
|
||||
|
||||
@At
|
||||
@SaCheckPermission("h5.qsv")
|
||||
public Result pageData(@Valid PageForm pageForm, @Valid String category) {
|
||||
public Result pageData(@Valid PageForm pageForm, @Valid String category, @Param("title") String title) {
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.andEX("category", "=", category);
|
||||
cnd.and(Cnd.likeEX("title", title));
|
||||
cnd.and(Cnd.exps("enabled", "=", true).or("enabled", "is", null));
|
||||
LocalDate currentYearFirstDay = LocalDate.now().withDayOfYear(1);
|
||||
Date currentYearStart = Date.from(currentYearFirstDay.atStartOfDay(ZoneId.systemDefault()).toInstant());
|
||||
Date nextYearStart = Date.from(currentYearFirstDay.plusYears(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
|
||||
cnd.and("startTime", ">=", currentYearStart);
|
||||
cnd.and("startTime", "<", nextYearStart);
|
||||
cnd.desc("startTime");
|
||||
Pagination pagination = qsvActivityService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), cnd);
|
||||
fillAnsweredStatus(pagination);
|
||||
return Result.success(pagination);
|
||||
}
|
||||
|
||||
private void fillAnsweredStatus(Pagination pagination) {
|
||||
List<NutMap> rows = pagination.getList();
|
||||
if (rows == null || rows.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<String> activityIds = rows.stream()
|
||||
.map(this::getRowActivityId)
|
||||
.filter(id -> id != null && !id.isBlank())
|
||||
.collect(Collectors.toList());
|
||||
if (activityIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Set<String> answeredActivityIds = dao.query(QsvUserAnswerRecord.class, Cnd.where("userId", "=", SecurityUtil.getUserId())
|
||||
.and("activityId", "in", activityIds)
|
||||
.and("isFinish", "=", true))
|
||||
.stream()
|
||||
.map(QsvUserAnswerRecord::getActivityId)
|
||||
.collect(Collectors.toSet());
|
||||
rows.forEach(row -> {
|
||||
boolean isAnswered = answeredActivityIds.contains(getRowActivityId(row));
|
||||
row.put("isAnswered", isAnswered);
|
||||
row.put("answeredText", isAnswered ? getAnsweredText(row.getString("category")) : "");
|
||||
});
|
||||
}
|
||||
|
||||
private String getRowActivityId(NutMap row) {
|
||||
String id = row.getString("id");
|
||||
if (id == null || id.isBlank()) {
|
||||
id = row.getString("ID");
|
||||
}
|
||||
if (id == null || id.isBlank()) {
|
||||
id = row.getString("activityId");
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
private String getAnsweredText(String category) {
|
||||
if ("QUIZ".equals(category)) {
|
||||
return "已答题";
|
||||
}
|
||||
if ("SURVEY".equals(category)) {
|
||||
return "已填写";
|
||||
}
|
||||
if ("VOTE".equals(category)) {
|
||||
return "已投票";
|
||||
}
|
||||
return "已完成";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+32
-28
@@ -1,7 +1,6 @@
|
||||
package com.budwk.app.zhgh.dayofficework.qsv.h5controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
@@ -9,7 +8,7 @@ import cn.hutool.json.JSONObject;
|
||||
import com.budwk.app.base.annotation.SLog;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.activity.basic.service.ActivityBasicScopeService;
|
||||
import com.budwk.app.zhgh.activity.basic.models.ActivityUserScope;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.dto.QsvCheckAnswerResult;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.models.QsvActivity;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.models.QsvSubject;
|
||||
@@ -44,8 +43,6 @@ public class H5QsvQuizController {
|
||||
@Inject
|
||||
private Dao dao;
|
||||
@Inject
|
||||
private ActivityBasicScopeService activityBasicScopeService;
|
||||
@Inject
|
||||
private QsvUserAnswerRecordService qsvUserAnswerRecordService;
|
||||
@Inject
|
||||
private QsvQuizService qsvQuizService;
|
||||
@@ -71,7 +68,9 @@ public class H5QsvQuizController {
|
||||
QsvActivity activity = dao.fetch(QsvActivity.class, activityId);
|
||||
|
||||
if (activity.getGroupId() != null) {
|
||||
if (!activityBasicScopeService.isUserInGroup(activity.getGroupId(), SecurityUtil.getUserId())) {
|
||||
int count = dao.count(ActivityUserScope.class, Cnd.where("groupId", "=", activity.getGroupId())
|
||||
.and("userId", "=", SecurityUtil.getUserId()));
|
||||
if (count != 1) {
|
||||
return Result.error("您无需参加此次答题,感谢您的关注!");
|
||||
}
|
||||
}
|
||||
@@ -103,7 +102,7 @@ public class H5QsvQuizController {
|
||||
List<String> subjectIds = lastRecordOptional.get().getSubjectIds();
|
||||
Cnd cnd = Cnd.where("id", "in", subjectIds);
|
||||
String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(","));
|
||||
cnd.orderBy("sortNum","asc");
|
||||
cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")"));
|
||||
resultSubjects = dao.query(QsvSubject.class, cnd);
|
||||
answerRecordId = lastRecordOptional.get().getId();
|
||||
} else {
|
||||
@@ -115,7 +114,7 @@ public class H5QsvQuizController {
|
||||
List<String> subjectIds = answerRecord.getSubjectIds();
|
||||
Cnd cnd = Cnd.where("id", "in", subjectIds);
|
||||
String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(","));
|
||||
cnd.orderBy("sortNum","asc");
|
||||
cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")"));
|
||||
resultSubjects = dao.query(QsvSubject.class, cnd);
|
||||
}
|
||||
} else {
|
||||
@@ -125,7 +124,9 @@ public class H5QsvQuizController {
|
||||
//首次进来生成答题记录
|
||||
if ("ALL".equals(displayMode)) {
|
||||
resultSubjects = dao.query(QsvSubject.class, Cnd.where("activityId", "=", activityId).asc("sortNum"));
|
||||
answerRecordId = qsvUserAnswerRecordService.insertRecord(activityId, resultSubjects.stream().map(QsvSubject::getId).collect(Collectors.toList())).getId();
|
||||
QsvUserAnswerRecord answerRecord = qsvUserAnswerRecordService.insertRecord(activityId, resultSubjects.stream().map(QsvSubject::getId).collect(Collectors.toList()));
|
||||
answerRecordId = answerRecord.getId();
|
||||
resultSubjects = querySubjectsByRecordOrder(answerRecord.getSubjectIds());
|
||||
} else if ("RANDOM".equals(displayMode)) {
|
||||
//单次随机抽取题目数量
|
||||
Integer randomCount = activity.getRandomCount();
|
||||
@@ -144,7 +145,7 @@ public class H5QsvQuizController {
|
||||
List<String> subjectIds = answerRecord.getSubjectIds();
|
||||
Cnd cnd = Cnd.where("id", "in", subjectIds);
|
||||
String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(","));
|
||||
cnd.orderBy("sortNum","asc");
|
||||
cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")"));
|
||||
resultSubjects = dao.query(QsvSubject.class, cnd);
|
||||
}
|
||||
} else {
|
||||
@@ -170,7 +171,7 @@ public class H5QsvQuizController {
|
||||
}
|
||||
Cnd cnd = Cnd.where("id", "in", subjectIds);
|
||||
String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(","));
|
||||
cnd.orderBy("sortNum","asc");
|
||||
cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")"));
|
||||
resultSubjects = dao.query(QsvSubject.class, cnd);
|
||||
repeatTips = true;
|
||||
} else {
|
||||
@@ -180,7 +181,7 @@ public class H5QsvQuizController {
|
||||
List<String> subjectIds = maxAnswerRecordOptional.get().getSubjectIds();
|
||||
Cnd cnd = Cnd.where("id", "in", subjectIds);
|
||||
String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(","));
|
||||
cnd.orderBy("sortNum","asc");
|
||||
cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")"));
|
||||
resultSubjects = dao.query(QsvSubject.class, cnd);
|
||||
answerRecordId = maxAnswerRecordOptional.get().getId();
|
||||
} else {
|
||||
@@ -193,7 +194,7 @@ public class H5QsvQuizController {
|
||||
List<String> subjectIds = maxAnswerRecordOptional.get().getSubjectIds();
|
||||
Cnd cnd = Cnd.where("id", "in", subjectIds);
|
||||
String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(","));
|
||||
cnd.orderBy("sortNum","asc");
|
||||
cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")"));
|
||||
resultSubjects = dao.query(QsvSubject.class, cnd);
|
||||
answerRecordId = maxAnswerRecordOptional.get().getId();
|
||||
} else {
|
||||
@@ -207,7 +208,7 @@ public class H5QsvQuizController {
|
||||
List<String> subjectIds = notFinishRecordOptional.get().getSubjectIds();
|
||||
Cnd cnd = Cnd.where("id", "in", subjectIds);
|
||||
String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(","));
|
||||
cnd.orderBy("sortNum","asc");
|
||||
cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")"));
|
||||
resultSubjects = dao.query(QsvSubject.class, cnd);
|
||||
answerRecordId = notFinishRecordOptional.get().getId();
|
||||
} else {
|
||||
@@ -245,7 +246,7 @@ public class H5QsvQuizController {
|
||||
List<String> subjectIds = maxAnswerRecordOptional.get().getSubjectIds();
|
||||
Cnd cnd = Cnd.where("id", "in", subjectIds);
|
||||
String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(","));
|
||||
cnd.orderBy("sortNum","asc");
|
||||
cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")"));
|
||||
resultSubjects = dao.query(QsvSubject.class, cnd);
|
||||
answerRecordId = maxAnswerRecordOptional.get().getId();
|
||||
} else {
|
||||
@@ -281,7 +282,7 @@ public class H5QsvQuizController {
|
||||
List<String> subjectIds = answerRecord.getSubjectIds();
|
||||
Cnd cnd = Cnd.where("id", "in", subjectIds);
|
||||
String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(","));
|
||||
cnd.orderBy("sortNum","asc");
|
||||
cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")"));
|
||||
resultSubjects = dao.query(QsvSubject.class, cnd);
|
||||
} else {
|
||||
//今天最大那次的记录
|
||||
@@ -292,7 +293,7 @@ public class H5QsvQuizController {
|
||||
List<String> subjectIds = maxTodayRecord.get().getSubjectIds();
|
||||
Cnd cnd = Cnd.where("id", "in", subjectIds);
|
||||
String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(","));
|
||||
cnd.orderBy("sortNum","asc");
|
||||
cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")"));
|
||||
resultSubjects = dao.query(QsvSubject.class, cnd);
|
||||
} else {
|
||||
throw new RuntimeException("业务异常");
|
||||
@@ -306,12 +307,9 @@ public class H5QsvQuizController {
|
||||
answerRecordId = answerRecord.getId();
|
||||
|
||||
List<String> subjectIds = answerRecord.getSubjectIds();
|
||||
if(CollectionUtil.isEmpty(subjectIds)){
|
||||
throw new RuntimeException("题目列表为空!请检查答题显示日期");
|
||||
}
|
||||
Cnd cnd = Cnd.where("id", "in", subjectIds);
|
||||
String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(","));
|
||||
cnd.orderBy("sortNum","asc");
|
||||
cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")"));
|
||||
resultSubjects = dao.query(QsvSubject.class, cnd);
|
||||
}
|
||||
}
|
||||
@@ -344,13 +342,6 @@ public class H5QsvQuizController {
|
||||
float totalScore = 0;
|
||||
|
||||
for (QsvAnswerParam.Subject subject : qsvAnswerParam.getSubjects()) {
|
||||
QsvSubject dbSubject = dao.fetch(QsvSubject.class, subject.getId());
|
||||
if (ObjectUtil.isNotEmpty(dbSubject) && "checkbox".equals(dbSubject.getType())
|
||||
&& ObjectUtil.isNotEmpty(dbSubject.getMaxMulti()) && dbSubject.getMaxMulti() > 0
|
||||
&& CollectionUtil.size(subject.getUserSelectOptionIds()) > dbSubject.getMaxMulti()) {
|
||||
// 后端兜底校验最大可选数,避免绕过前端直接提交超限答案。
|
||||
return Result.error("题目【" + dbSubject.getTitle() + "】最多只能选择" + dbSubject.getMaxMulti() + "项");
|
||||
}
|
||||
JSONObject entries = extJson.get(subject.getId(), JSONObject.class);
|
||||
if (ObjectUtil.isNotEmpty(entries)) {
|
||||
entries.set("optionIds", subject.getUserSelectOptionIds());
|
||||
@@ -395,7 +386,7 @@ public class H5QsvQuizController {
|
||||
|
||||
Cnd cnd = Cnd.where("id", "in", subjectIds);
|
||||
String dynamic = subjectIds.stream().map(id -> "'" + id + "'").collect(Collectors.joining(","));
|
||||
cnd.orderBy("sortNum","asc");
|
||||
cnd.and(new Static("1=1 ORDER BY FIELD (id," + dynamic + ")"));
|
||||
|
||||
List<QsvSubject> subjects = dao.query(QsvSubject.class, cnd);
|
||||
dao.fetchLinks(subjects, "options", Cnd.NEW().asc("sortNum"));
|
||||
@@ -407,4 +398,17 @@ public class H5QsvQuizController {
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
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, java.util.function.Function.identity(), (oldValue, newValue) -> oldValue));
|
||||
return subjectIds.stream()
|
||||
.map(subjectMap::get)
|
||||
.filter(ObjectUtil::isNotEmpty)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+116
-7
@@ -4,7 +4,7 @@ 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.service.ActivityBasicScopeService;
|
||||
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.QsvSubject;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.models.QsvUserAnswerRecord;
|
||||
@@ -12,6 +12,8 @@ import com.budwk.app.zhgh.dayofficework.qsv.param.QsvAnswerParam;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.service.QsvUserAnswerRecordService;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
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;
|
||||
@@ -22,7 +24,11 @@ import org.nutz.mvc.annotation.Param;
|
||||
import javax.validation.Valid;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
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/survey")
|
||||
@@ -32,8 +38,6 @@ public class H5QsvSurveyController {
|
||||
@Inject
|
||||
private Dao dao;
|
||||
@Inject
|
||||
private ActivityBasicScopeService activityBasicScopeService;
|
||||
@Inject
|
||||
private QsvUserAnswerRecordService qsvUserAnswerRecordService;
|
||||
|
||||
@At("")
|
||||
@@ -42,29 +46,37 @@ public class H5QsvSurveyController {
|
||||
}
|
||||
|
||||
@At
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result subjects(String activityId) {
|
||||
QsvActivity activity = dao.fetch(QsvActivity.class, activityId);
|
||||
Result activityStatusResult = checkActivityStatus(activity);
|
||||
if (activityStatusResult != null) {
|
||||
return activityStatusResult;
|
||||
}
|
||||
|
||||
if (!activityBasicScopeService.isUserInGroup(activity.getGroupId(), SecurityUtil.getUserId())) {
|
||||
int count = dao.count(ActivityUserScope.class, Cnd.where("groupId", "=", activity.getGroupId())
|
||||
.and("userId", "=", SecurityUtil.getUserId()));
|
||||
if (count != 1) {
|
||||
return Result.error("您无需参加此次投票,感谢您的关注!");
|
||||
}
|
||||
|
||||
String answerRecordId = null;
|
||||
|
||||
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<QsvSubject> subjects = dao.query(QsvSubject.class, Cnd.where("activityId", "=", activityId).asc("sortNum"));
|
||||
qsvUserAnswerRecordService.insertRecord(activityId, subjects.stream().map(QsvSubject::getId).toList());
|
||||
qsvUserAnswerRecordService.insertRecord(activityId, activitySubjects.stream().map(QsvSubject::getId).toList());
|
||||
}
|
||||
|
||||
QsvUserAnswerRecord answerRecord2 = dao.fetch(QsvUserAnswerRecord.class,
|
||||
Cnd.where("activityId", "=", activityId)
|
||||
.and("userId", "=", SecurityUtil.getUserId()));
|
||||
syncAnswerRecordSubjects(answerRecord2, activitySubjects);
|
||||
|
||||
answerRecordId = answerRecord2.getId();
|
||||
|
||||
List<QsvSubject> subjects = dao.query(QsvSubject.class, Cnd.where("id", "in", answerRecord2.getSubjectIds()).asc("sortNum"));
|
||||
List<QsvSubject> subjects = querySubjectsByRecordOrder(answerRecord2.getSubjectIds());
|
||||
dao.fetchLinks(subjects, "options", Cnd.NEW().asc("sortNum"));
|
||||
|
||||
for (QsvSubject subject : subjects) {
|
||||
@@ -77,6 +89,95 @@ public class H5QsvSurveyController {
|
||||
return Result.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验调查活动是否处于可答题状态。activity 为空表示活动不存在;
|
||||
* enabled 为 false 表示活动已关闭,当前时间早于 startTime 表示未开始,当前时间晚于 endTime 表示已结束。
|
||||
*
|
||||
* @param activity 当前调查活动,包含开启状态、开始时间和结束时间
|
||||
* @return 状态或时间不允许答题时返回错误结果,允许答题时返回 null
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步问卷当前题目到未完成答题记录。后台新增题目后,已进入过问卷的用户记录里没有新题ID,
|
||||
* 这里补齐 subjectIds 和 extJson,保证手机端能拿到最新题目并正常提交答案。
|
||||
*
|
||||
* @param answerRecord 用户当前问卷答题记录
|
||||
* @param activitySubjects 当前活动下按排序查询出的最新题目列表
|
||||
*/
|
||||
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());
|
||||
}
|
||||
|
||||
@At
|
||||
public Result answerRecord(@Valid String answerRecordId) {
|
||||
QsvUserAnswerRecord record = dao.fetch(QsvUserAnswerRecord.class, answerRecordId);
|
||||
@@ -84,7 +185,14 @@ public class H5QsvSurveyController {
|
||||
}
|
||||
|
||||
@At
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
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;
|
||||
}
|
||||
|
||||
QsvUserAnswerRecord answerRecord = dao.fetch(QsvUserAnswerRecord.class, qsvAnswerParam.getAnswerRecordId());
|
||||
JSONObject extJson = answerRecord.getExtJson();
|
||||
|
||||
@@ -93,6 +201,7 @@ public class H5QsvSurveyController {
|
||||
if (ObjectUtil.isNotEmpty(entries)) {
|
||||
entries.set("optionIds", subject.getUserSelectOptionIds());
|
||||
entries.set("text", subject.getUserFillContent());
|
||||
entries.set("optionFillContents", subject.getOptionFillContents());
|
||||
}
|
||||
extJson.set(subject.getId(), entries);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.budwk.app.zhgh.dayofficework.qsv.models;
|
||||
|
||||
import com.budwk.app.base.model.BaseModel;
|
||||
import com.budwk.app.sys.models.Sys_home_activity;
|
||||
import com.budwk.app.sys.services.SysHomeConvert;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.nutz.dao.entity.annotation.*;
|
||||
@@ -14,7 +16,7 @@ import java.util.Date;
|
||||
@Table("qsv_activity")
|
||||
@TableMeta("{'mysql-charset':'utf8mb4'}")
|
||||
@Comment("问卷调查投票活动表")
|
||||
public class QsvActivity extends BaseModel implements Serializable {
|
||||
public class QsvActivity extends BaseModel implements Serializable, SysHomeConvert {
|
||||
@Name
|
||||
@Comment("ID")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
@@ -46,6 +48,12 @@ public class QsvActivity extends BaseModel implements Serializable {
|
||||
@ColDefine(type = ColType.DATETIME)
|
||||
private Date endTime;
|
||||
|
||||
@Column
|
||||
@Comment("是否开启")
|
||||
@ColDefine(type = ColType.BOOLEAN)
|
||||
@Default("1")
|
||||
private Boolean enabled;
|
||||
|
||||
@Column
|
||||
@Comment("活动分组ID")
|
||||
@ColDefine(type = ColType.INT)
|
||||
@@ -107,4 +115,35 @@ public class QsvActivity extends BaseModel implements Serializable {
|
||||
@Comment("封面图片")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 255)
|
||||
private String cover;
|
||||
|
||||
@Override
|
||||
public Sys_home_activity covertToSysHomeActivity() {
|
||||
Sys_home_activity sysHomeActivity = new Sys_home_activity();
|
||||
sysHomeActivity.setId(this.getId());
|
||||
sysHomeActivity.setName(this.getTitle());
|
||||
sysHomeActivity.setCover(this.getCover());
|
||||
sysHomeActivity.setContent(this.getDescription());
|
||||
sysHomeActivity.setUrl("/platform/qsv/activity");
|
||||
sysHomeActivity.setH5Url(getH5Url());
|
||||
sysHomeActivity.setStartDate(this.getStartTime());
|
||||
sysHomeActivity.setEndDate(this.getEndTime());
|
||||
sysHomeActivity.setAllowUserGroupId(this.getGroupId());
|
||||
sysHomeActivity.setEnable(!Boolean.FALSE.equals(this.getEnabled()));
|
||||
sysHomeActivity.setClassPath(this.getClass().getPackageName() + this.getClass().getName());
|
||||
return sysHomeActivity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据活动类型生成移动端首页跳转地址。
|
||||
* QUIZ 跳转答题页,SURVEY 跳转调查页,VOTE 预留投票页地址,返回值为移动端路由字符串。
|
||||
*/
|
||||
private String getH5Url() {
|
||||
if ("SURVEY".equals(this.getCategory())) {
|
||||
return "/platform/h5/qsv/survey?id=" + this.getId();
|
||||
}
|
||||
if ("VOTE".equals(this.getCategory())) {
|
||||
return "/platform/h5/qsv/vote?id=" + this.getId();
|
||||
}
|
||||
return "/platform/h5/qsv/quiz?id=" + this.getId();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,16 @@ public class QsvOption extends BaseModel implements Serializable {
|
||||
@ColDefine(customType = "longtext")
|
||||
private String description;
|
||||
|
||||
@Column
|
||||
@Comment("选中后是否需要填写补充内容")
|
||||
@ColDefine(type = ColType.BOOLEAN)
|
||||
private Boolean fillRequired;
|
||||
|
||||
@Column
|
||||
@Comment("补充内容输入提示")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 255)
|
||||
private String fillPlaceholder;
|
||||
|
||||
@Column
|
||||
@Comment("排序")
|
||||
@ColDefine(type = ColType.INT)
|
||||
|
||||
@@ -58,11 +58,41 @@ public class QsvSubject extends BaseModel implements Serializable {
|
||||
@ColDefine(type = ColType.INT, width = 1)
|
||||
private Integer maxMulti;
|
||||
|
||||
@Column
|
||||
@Comment("投票选项排列方式")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 20)
|
||||
private String optionLayout;
|
||||
|
||||
@Column
|
||||
@Comment("投票选项横向列数")
|
||||
@ColDefine(type = ColType.INT, width = 1)
|
||||
private Integer optionColumns;
|
||||
|
||||
@Column
|
||||
@Comment("排序")
|
||||
@ColDefine(type = ColType.INT, width = 1)
|
||||
private Integer sortNum;
|
||||
|
||||
@Column
|
||||
@Comment("是否启用题目隐显逻辑")
|
||||
@ColDefine(type = ColType.BOOLEAN)
|
||||
private Boolean visibleRuleEnabled;
|
||||
|
||||
@Column
|
||||
@Comment("隐显逻辑处理方式(show显示,hide隐藏)")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 20)
|
||||
private String visibleRuleAction;
|
||||
|
||||
@Column
|
||||
@Comment("隐显逻辑条件关系(AND并且,OR或者)")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 20)
|
||||
private String visibleRuleLogic;
|
||||
|
||||
@Column
|
||||
@Comment("隐显逻辑条件列表")
|
||||
@ColDefine(type = ColType.MYSQL_JSON)
|
||||
private List<VisibleRuleCondition> visibleRuleConditions;
|
||||
|
||||
/**
|
||||
* 选项
|
||||
*/
|
||||
@@ -79,4 +109,52 @@ public class QsvSubject extends BaseModel implements Serializable {
|
||||
*/
|
||||
private String userFillContent;
|
||||
|
||||
/**
|
||||
* 题目隐显逻辑条件,一条条件表示选择指定题目的指定选项后参与显示或隐藏判断。
|
||||
*/
|
||||
@Data
|
||||
public static class VisibleRuleCondition implements Serializable {
|
||||
|
||||
/**
|
||||
* 作为触发条件的题目ID。
|
||||
*/
|
||||
private String subjectId;
|
||||
|
||||
/**
|
||||
* 作为触发条件的选项ID。
|
||||
*/
|
||||
private String optionId;
|
||||
|
||||
/**
|
||||
* 条件节点类型:group 分组,item 条件项。
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 分组内条件关系:and 并且,or 或者。
|
||||
*/
|
||||
private String logic;
|
||||
|
||||
/**
|
||||
* 条件项引用的题目ID。
|
||||
*/
|
||||
private String field;
|
||||
|
||||
/**
|
||||
* 比较符:==、!=、contains、>、<、>=、<=。
|
||||
*/
|
||||
private String operator;
|
||||
|
||||
/**
|
||||
* 比较值,选择题为选项ID,填空题可为文本或数字。
|
||||
*/
|
||||
private Object value;
|
||||
|
||||
/**
|
||||
* 子条件节点,支持条件分组嵌套。
|
||||
*/
|
||||
private List<VisibleRuleCondition> children;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.budwk.app.zhgh.dayofficework.qsv.param;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
//答题参数
|
||||
@@ -21,6 +22,7 @@ public class QsvAnswerParam {
|
||||
private String id;
|
||||
private List<String> userSelectOptionIds;
|
||||
private String userFillContent;
|
||||
private Map<String, String> optionFillContents;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,4 +5,10 @@ import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.zhgh.dayofficework.qsv.models.QsvActivity;
|
||||
|
||||
public interface QsvActivityService extends BaseService<QsvActivity> {
|
||||
|
||||
QsvActivity saveActivity(QsvActivity qsvActivity);
|
||||
|
||||
void deleteActivity(String id);
|
||||
|
||||
void updateEnabled(String id, Boolean enabled);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,14 @@ public interface QsvSurveyService extends BaseService<QsvUserAnswerRecord> {
|
||||
|
||||
List<NutMap> report(String activityId);
|
||||
|
||||
List<NutMap> categoryReport(String activityId, String conditions);
|
||||
|
||||
NutMap crossReport(String activityId, String xSubjectIds, String ySubjectIds, String conditions);
|
||||
|
||||
NutMap compareReport(String activityId, String subjectId, String optionId);
|
||||
|
||||
List<NutMap> selectOptionUsers(String activityId, String subjectId, String optionId, String conditions);
|
||||
|
||||
void exportReportXlsx(String activityId, HttpServletResponse response);
|
||||
|
||||
void exportUserAnswerXlsx(String activityId, HttpServletResponse response);
|
||||
|
||||
+67
@@ -1,15 +1,82 @@
|
||||
package com.budwk.app.zhgh.dayofficework.qsv.service.impl;
|
||||
|
||||
import com.budwk.app.base.service.impl.BaseServiceImpl;
|
||||
import com.budwk.app.sys.models.Sys_home_activity;
|
||||
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.service.QsvActivityService;
|
||||
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.IocBean;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class QsvActivityServiceImpl extends BaseServiceImpl<QsvActivity> implements QsvActivityService {
|
||||
|
||||
public QsvActivityServiceImpl(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public QsvActivity saveActivity(QsvActivity qsvActivity) {
|
||||
fillQuizRepeatMode(qsvActivity);
|
||||
dao().insertOrUpdate(qsvActivity);
|
||||
syncHomeActivity(qsvActivity);
|
||||
return qsvActivity;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public void deleteActivity(String id) {
|
||||
List<QsvSubject> subjects = dao().query(QsvSubject.class, Cnd.where("activityId", "=", id));
|
||||
List<String> subjectIds = subjects.stream().map(QsvSubject::getId).collect(Collectors.toList());
|
||||
if (!subjectIds.isEmpty()) {
|
||||
dao().clear(QsvOption.class, Cnd.where("subjectId", "in", subjectIds));
|
||||
dao().clear(QsvSubject.class, Cnd.where("id", "in", subjectIds));
|
||||
}
|
||||
dao().clear(QsvUserAnswerRecord.class, Cnd.where("activityId", "=", id));
|
||||
dao().delete(QsvActivity.class, id);
|
||||
dao().delete(Sys_home_activity.class, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public void updateEnabled(String id, Boolean enabled) {
|
||||
QsvActivity activity = dao().fetch(QsvActivity.class, id);
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
activity.setEnabled(enabled);
|
||||
dao().update(activity);
|
||||
syncHomeActivity(activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 答题活动的重复模式由出题模式决定。定时定题按天统计,常规模式按活动总次数统计。
|
||||
*/
|
||||
private void fillQuizRepeatMode(QsvActivity qsvActivity) {
|
||||
if (!"QUIZ".equals(qsvActivity.getCategory())) {
|
||||
return;
|
||||
}
|
||||
if ("SCHEDULED".equals(qsvActivity.getMode())) {
|
||||
qsvActivity.setRepeatMode("DAILY");
|
||||
} else if ("REGULAR".equals(qsvActivity.getMode())) {
|
||||
qsvActivity.setRepeatMode("TOTAL");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步移动端首页活动。问卷活动保存或启停后,都以活动自身 enabled、时间、人员范围生成首页记录。
|
||||
*/
|
||||
private void syncHomeActivity(QsvActivity activity) {
|
||||
Sys_home_activity sysHomeActivity = activity.covertToSysHomeActivity();
|
||||
dao().insertOrUpdate(sysHomeActivity);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-11
@@ -119,17 +119,11 @@ public class QsvQuizRankServiceImpl extends BaseServiceImpl<QsvUserAnswerRecord>
|
||||
cnd.andEX("t1.unionId","=",pageForm.getUnionId());
|
||||
cnd.andEX("t1.unitId","=",pageForm.getUnitId());
|
||||
|
||||
if ("SCHEDULED".equals(mode)) {
|
||||
if ("HIGH".equals(scoreStatisticsMode)) {
|
||||
cnd.and("t1.isHighestScore","=",1);
|
||||
} else if ("LAST".equals(scoreStatisticsMode)) {
|
||||
cnd.and("t1.isLatestScore","=",1);
|
||||
}
|
||||
} else if ("REGULAR".equals(mode)) {
|
||||
if ("HIGH".equals(scoreStatisticsMode)) {
|
||||
cnd.and("t1.isHighestScore","=",1);
|
||||
} else if ("LAST".equals(scoreStatisticsMode)) {
|
||||
cnd.and("t1.isLatestScore","=",1);
|
||||
if ("SCHEDULED".equals(mode) || "REGULAR".equals(mode)) {
|
||||
if ("HIGH".equals(scoreStatisticsMode) || "HIGHEST".equals(scoreStatisticsMode)) {
|
||||
cnd.and("t1.isHighestScore", "=", 1);
|
||||
} else {
|
||||
cnd.and("t1.isLatestScore", "=", 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+493
-39
@@ -8,6 +8,7 @@ import cn.afterturn.easypoi.excel.export.ExcelExportService;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.param.PageForm;
|
||||
import com.budwk.app.base.service.impl.BaseServiceImpl;
|
||||
@@ -68,47 +69,47 @@ public class QsvSurveyServiceImpl extends BaseServiceImpl<QsvUserAnswerRecord> i
|
||||
// 处理每个题目
|
||||
for (NutMap subject : subjects) {
|
||||
// 获取当前题目的选项列表
|
||||
List<NutMap> subjectOptions = Lang.collection2list(optionsGroup.get(subject.getString("id")), NutMap.class);
|
||||
List<QsvOption> currentOptions = optionsGroup.get(subject.getString("id"));
|
||||
List<NutMap> subjectOptions = ObjectUtil.isEmpty(currentOptions) ? new ArrayList<>() : Lang.collection2list(currentOptions, NutMap.class);
|
||||
|
||||
// 获取题目类型
|
||||
String subjectType = subject.getString("type");
|
||||
|
||||
// 处理文本类型题目
|
||||
if ("text".equals(subjectType)) {
|
||||
List<String> texts = answerExtList.stream()
|
||||
.filter(ext -> ObjectUtil.isNull(ext.get(subject.getString("id"), JSONObject.class)))
|
||||
.map(ext -> ext.get(subject.getString("id"), JSONObject.class).getStr("text"))
|
||||
List<NutMap> textAnswers = buildTextAnswerDetails(answerRecords, subject.getString("id"));
|
||||
List<String> texts = textAnswers.stream()
|
||||
.map(textAnswer -> textAnswer.getString("text"))
|
||||
.toList();
|
||||
subject.put("texts", texts);
|
||||
subject.put("textAnswers", textAnswers);
|
||||
}
|
||||
// 处理单选类型题目 处理多选类型题目
|
||||
else if ("radio".equals(subjectType) || "checkbox".equals(subjectType)) {
|
||||
long selectTotal = answerExtList.stream()
|
||||
.filter(ext -> ext.containsKey(subject.getString("id")))
|
||||
.filter(ext -> ObjectUtil.isNotEmpty(ext) && ext.containsKey(subject.getString("id")))
|
||||
.count();
|
||||
|
||||
subjectOptions.forEach(subjectOption -> {
|
||||
long selectCount = answerExtList.stream()
|
||||
.filter(ext ->
|
||||
{
|
||||
if (ObjectUtil.isEmpty(ext)) {
|
||||
return false;
|
||||
}
|
||||
JSONObject jsonObject = ext.get(subject.getString("id"), JSONObject.class);
|
||||
return jsonObject != null
|
||||
&& jsonObject.getJSONArray("optionIds") != null
|
||||
&& jsonObject.getJSONArray("optionIds").contains(subjectOption.getString("id"));
|
||||
})
|
||||
.count();
|
||||
long round = Math.round((double) selectCount / selectTotal * 100);
|
||||
long round = selectTotal == 0 ? 0 : Math.round((double) selectCount / selectTotal * 100);
|
||||
subjectOption.put("selectPercent", round + "%");
|
||||
subjectOption.put("selectCount", selectCount);
|
||||
});
|
||||
|
||||
subjectOptions.sort((o1, o2) -> {
|
||||
int count1 = o1.getInt("selectCount");
|
||||
int count2 = o2.getInt("selectCount");
|
||||
if (count1 == count2) {
|
||||
return Integer.compare(o1.getInt("sortNum"), o2.getInt("sortNum"));
|
||||
}
|
||||
return Integer.compare(count2, count1);
|
||||
return Integer.compare(o1.getInt("sortNum"), o2.getInt("sortNum"));
|
||||
});
|
||||
|
||||
subject.put("selectTotal", selectTotal);
|
||||
@@ -125,6 +126,228 @@ public class QsvSurveyServiceImpl extends BaseServiceImpl<QsvUserAnswerRecord> i
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<NutMap> categoryReport(String activityId, String conditions) {
|
||||
if (activityId == null || activityId.isEmpty()) {
|
||||
throw new IllegalArgumentException("activityId不能为空");
|
||||
}
|
||||
try {
|
||||
List<NutMap> conditionList = parseCategoryConditions(conditions);
|
||||
List<QsvUserAnswerRecord> answerRecords = dao().query(QsvUserAnswerRecord.class, Cnd.where("activityId", "=", activityId)
|
||||
.and("isFinish", "=", true));
|
||||
return buildReport(activityId, filterAnswerRecords(answerRecords, conditionList));
|
||||
} catch (Exception e) {
|
||||
log.error("分类报告生成失败", e);
|
||||
throw new RuntimeException("分类报告生成失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NutMap crossReport(String activityId, String xSubjectIds, String ySubjectIds, String conditions) {
|
||||
if (activityId == null || activityId.isEmpty()) {
|
||||
throw new IllegalArgumentException("activityId不能为空");
|
||||
}
|
||||
List<String> xSubjectIdList = parseSubjectIds(xSubjectIds);
|
||||
List<String> ySubjectIdList = parseSubjectIds(ySubjectIds);
|
||||
if (ObjectUtil.isEmpty(xSubjectIdList) || ObjectUtil.isEmpty(ySubjectIdList)) {
|
||||
throw new IllegalArgumentException("请选择自变量X和因变量Y");
|
||||
}
|
||||
if (xSubjectIdList.stream().anyMatch(ySubjectIdList::contains)) {
|
||||
throw new IllegalArgumentException("自变量X和因变量Y不能选择相同变量");
|
||||
}
|
||||
|
||||
try {
|
||||
List<NutMap> subjects = querySubjects(activityId);
|
||||
Map<String, NutMap> subjectMap = subjects.stream()
|
||||
.collect(Collectors.toMap(subject -> subject.getString("id"), subject -> subject));
|
||||
if (!subjectMap.keySet().containsAll(xSubjectIdList) || !subjectMap.keySet().containsAll(ySubjectIdList)) {
|
||||
throw new IllegalArgumentException("题目不存在");
|
||||
}
|
||||
|
||||
List<String> allSubjectIds = new ArrayList<>();
|
||||
allSubjectIds.addAll(xSubjectIdList);
|
||||
allSubjectIds.addAll(ySubjectIdList);
|
||||
List<QsvOption> options = dao().query(QsvOption.class, Cnd.where("subjectId", "in", allSubjectIds).asc("sortNum"));
|
||||
Map<String, List<QsvOption>> optionGroup = options.stream().collect(Collectors.groupingBy(QsvOption::getSubjectId));
|
||||
List<NutMap> xOptionMaps = buildVariableOptionCombinations(xSubjectIdList, subjectMap, optionGroup);
|
||||
List<NutMap> conditionList = parseCategoryConditions(conditions);
|
||||
List<QsvUserAnswerRecord> answerRecords = dao().query(QsvUserAnswerRecord.class, Cnd.where("activityId", "=", activityId)
|
||||
.and("isFinish", "=", true));
|
||||
List<QsvUserAnswerRecord> filteredRecords = filterAnswerRecords(answerRecords, conditionList);
|
||||
List<NutMap> reports = new ArrayList<>();
|
||||
|
||||
for (String ySubjectId : ySubjectIdList) {
|
||||
NutMap ySubject = subjectMap.get(ySubjectId);
|
||||
List<QsvOption> yOptions = optionGroup.get(ySubjectId);
|
||||
if (ObjectUtil.isEmpty(yOptions)) {
|
||||
yOptions = new ArrayList<>();
|
||||
}
|
||||
List<NutMap> yOptionMaps = ObjectUtil.isEmpty(yOptions) ? new ArrayList<>() : Lang.collection2list(yOptions, NutMap.class);
|
||||
List<NutMap> rows = new ArrayList<>();
|
||||
|
||||
for (NutMap xOption : xOptionMaps) {
|
||||
List<NutMap> xConditions = xOption.getList("conditions", NutMap.class);
|
||||
List<QsvUserAnswerRecord> xMatchedRecords = filteredRecords.stream()
|
||||
.filter(record -> isRecordMatched(record, xConditions))
|
||||
.toList();
|
||||
long rowTotal = xMatchedRecords.size();
|
||||
List<NutMap> cells = new ArrayList<>();
|
||||
for (QsvOption yOption : yOptions) {
|
||||
List<NutMap> yCondition = List.of(NutMap.NEW()
|
||||
.addv("subjectId", ySubjectId)
|
||||
.addv("optionId", yOption.getId()));
|
||||
long count = xMatchedRecords.stream()
|
||||
.filter(record -> isRecordMatched(record, yCondition))
|
||||
.count();
|
||||
double percent = rowTotal == 0 ? 0 : Math.round((double) count / rowTotal * 10000) / 100.0;
|
||||
cells.add(NutMap.NEW()
|
||||
.addv("yOptionId", yOption.getId())
|
||||
.addv("yOptionText", yOption.getText())
|
||||
.addv("count", count)
|
||||
.addv("percent", percent));
|
||||
}
|
||||
rows.add(NutMap.NEW()
|
||||
.addv("xOptionId", xOption.getString("id"))
|
||||
.addv("xOptionText", xOption.getString("text"))
|
||||
.addv("cells", cells)
|
||||
.addv("total", rowTotal));
|
||||
}
|
||||
|
||||
reports.add(NutMap.NEW()
|
||||
.addv("ySubject", ySubject)
|
||||
.addv("yOptions", yOptionMaps)
|
||||
.addv("rows", rows));
|
||||
}
|
||||
|
||||
return NutMap.NEW()
|
||||
.addv("xSubject", buildVariableSubject(xSubjectIdList, subjectMap))
|
||||
.addv("xOptions", xOptionMaps)
|
||||
.addv("reports", reports)
|
||||
.addv("total", filteredRecords.size());
|
||||
} catch (Exception e) {
|
||||
log.error("交叉分析报告生成失败", e);
|
||||
throw new RuntimeException("交叉分析报告生成失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NutMap compareReport(String activityId, String subjectId, String optionId) {
|
||||
if (activityId == null || activityId.isEmpty()) {
|
||||
throw new IllegalArgumentException("activityId不能为空");
|
||||
}
|
||||
if (ObjectUtil.isEmpty(subjectId) || ObjectUtil.isEmpty(optionId)) {
|
||||
throw new IllegalArgumentException("请选择对比变量和选项");
|
||||
}
|
||||
|
||||
try {
|
||||
List<NutMap> subjects = querySubjects(activityId);
|
||||
Map<String, NutMap> subjectMap = subjects.stream()
|
||||
.collect(Collectors.toMap(subject -> subject.getString("id"), subject -> subject));
|
||||
NutMap compareSubject = subjectMap.get(subjectId);
|
||||
if (compareSubject == null) {
|
||||
throw new IllegalArgumentException("对比变量不存在");
|
||||
}
|
||||
|
||||
List<String> subjectIds = subjects.stream().map(subject -> subject.getString("id")).toList();
|
||||
List<QsvOption> options = dao().query(QsvOption.class, Cnd.where("subjectId", "in", subjectIds).asc("sortNum"));
|
||||
Map<String, List<QsvOption>> optionGroup = options.stream().collect(Collectors.groupingBy(QsvOption::getSubjectId));
|
||||
QsvOption compareOption = optionGroup.getOrDefault(subjectId, new ArrayList<>()).stream()
|
||||
.filter(option -> optionId.equals(option.getId()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (compareOption == null) {
|
||||
throw new IllegalArgumentException("对比选项不存在");
|
||||
}
|
||||
|
||||
List<NutMap> compareCondition = List.of(NutMap.NEW()
|
||||
.addv("subjectId", subjectId)
|
||||
.addv("optionId", optionId));
|
||||
List<QsvUserAnswerRecord> answerRecords = dao().query(QsvUserAnswerRecord.class, Cnd.where("activityId", "=", activityId)
|
||||
.and("isFinish", "=", true));
|
||||
List<QsvUserAnswerRecord> filteredRecords = filterAnswerRecords(answerRecords, compareCondition);
|
||||
List<NutMap> reports = new ArrayList<>();
|
||||
|
||||
for (NutMap subject : subjects) {
|
||||
String currentSubjectId = subject.getString("id");
|
||||
String subjectType = subject.getString("type");
|
||||
if (subjectId.equals(currentSubjectId) || (!"radio".equals(subjectType) && !"checkbox".equals(subjectType))) {
|
||||
continue;
|
||||
}
|
||||
List<QsvOption> currentOptions = optionGroup.get(currentSubjectId);
|
||||
if (ObjectUtil.isEmpty(currentOptions)) {
|
||||
continue;
|
||||
}
|
||||
List<NutMap> cells = new ArrayList<>();
|
||||
long rowTotal = filteredRecords.size();
|
||||
for (QsvOption option : currentOptions) {
|
||||
List<NutMap> optionCondition = List.of(NutMap.NEW()
|
||||
.addv("subjectId", currentSubjectId)
|
||||
.addv("optionId", option.getId()));
|
||||
long count = filteredRecords.stream()
|
||||
.filter(record -> isRecordMatched(record, optionCondition))
|
||||
.count();
|
||||
double percent = rowTotal == 0 ? 0 : Math.round((double) count / rowTotal * 10000) / 100.0;
|
||||
cells.add(NutMap.NEW()
|
||||
.addv("yOptionId", option.getId())
|
||||
.addv("yOptionText", option.getText())
|
||||
.addv("count", count)
|
||||
.addv("percent", percent));
|
||||
}
|
||||
reports.add(NutMap.NEW()
|
||||
.addv("ySubject", subject)
|
||||
.addv("yOptions", Lang.collection2list(currentOptions, NutMap.class))
|
||||
.addv("rows", List.of(NutMap.NEW()
|
||||
.addv("xOptionId", optionId)
|
||||
.addv("xOptionText", compareOption.getText())
|
||||
.addv("cells", cells)
|
||||
.addv("total", rowTotal))));
|
||||
}
|
||||
|
||||
return NutMap.NEW()
|
||||
.addv("compareSubject", compareSubject)
|
||||
.addv("compareOption", Lang.obj2nutmap(compareOption))
|
||||
.addv("reports", reports)
|
||||
.addv("total", filteredRecords.size());
|
||||
} catch (Exception e) {
|
||||
log.error("对比分析报告生成失败", e);
|
||||
throw new RuntimeException("对比分析报告生成失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NutMap> selectOptionUsers(String activityId, String subjectId, String optionId, String conditions) {
|
||||
List<NutMap> conditionList = parseCategoryConditions(conditions);
|
||||
List<QsvUserAnswerRecord> answerRecords = dao().query(QsvUserAnswerRecord.class, Cnd.where("activityId", "=", activityId)
|
||||
.and("isFinish", "=", true));
|
||||
return filterAnswerRecords(answerRecords, conditionList).stream().map(record -> {
|
||||
JSONObject extJson = record.getExtJson();
|
||||
if (ObjectUtil.isEmpty(extJson)) {
|
||||
return null;
|
||||
}
|
||||
JSONObject subjectAnswer = extJson.get(subjectId, JSONObject.class);
|
||||
if (ObjectUtil.isEmpty(subjectAnswer)) {
|
||||
return null;
|
||||
}
|
||||
JSONArray optionIds = subjectAnswer.getJSONArray("optionIds");
|
||||
if (ObjectUtil.isEmpty(optionIds) || !optionIds.contains(optionId)) {
|
||||
return null;
|
||||
}
|
||||
JSONObject optionFillContents = subjectAnswer.getJSONObject("optionFillContents");
|
||||
String fillContent = ObjectUtil.isEmpty(optionFillContents) ? "" : optionFillContents.getStr(optionId);
|
||||
return NutMap.NEW()
|
||||
.addv("userName", record.getUserName())
|
||||
.addv("loginName", record.getLoginName())
|
||||
.addv("unionName", record.getUnionName())
|
||||
.addv("unitName", record.getUnitName())
|
||||
.addv("attemptDate", record.getAttemptDate())
|
||||
.addv("submitTime", record.getSubmitTime())
|
||||
.addv("fillContent", fillContent);
|
||||
})
|
||||
.filter(ObjectUtil::isNotNull)
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void exportReportXlsx(String activityId, HttpServletResponse response) {
|
||||
List<NutMap> report = report(activityId);
|
||||
@@ -134,10 +357,8 @@ public class QsvSurveyServiceImpl extends BaseServiceImpl<QsvUserAnswerRecord> i
|
||||
exportEntities.add(new ExcelExportEntity("选择人数", "selectCount", 20));
|
||||
exportEntities.add(new ExcelExportEntity("选择比例", "selectPercent", 20));
|
||||
|
||||
Map<String, List<NutMap>> listMap = report.stream().collect(Collectors.groupingBy(v -> v.getString("id")));
|
||||
Workbook workbook = new XSSFWorkbook();
|
||||
listMap.forEach((k, v) -> {
|
||||
NutMap nutMap = v.get(0);
|
||||
report.forEach(nutMap -> {
|
||||
ExcelExportService service = new ExcelExportService();
|
||||
ExportParams exportParams = new ExportParams();
|
||||
exportParams.setTitle(nutMap.getString("title"));
|
||||
@@ -171,7 +392,7 @@ public class QsvSurveyServiceImpl extends BaseServiceImpl<QsvUserAnswerRecord> i
|
||||
List<JSONObject> answerExtList = answerRecords.stream().map(QsvUserAnswerRecord::getExtJson).toList();
|
||||
|
||||
// 查询活动题目列表
|
||||
List<QsvSubject> subjects = dao().query(QsvSubject.class, Cnd.where("activityId", "=", activityId));
|
||||
List<QsvSubject> subjects = dao().query(QsvSubject.class, Cnd.where("activityId", "=", activityId).asc("sortNum"));
|
||||
// 将题目列表转换为Map
|
||||
Map<String, QsvSubject> subjectMap = subjects.stream().collect(Collectors.toMap(QsvSubject::getId, v -> v));
|
||||
// 获取题目ID列表
|
||||
@@ -185,7 +406,7 @@ public class QsvSurveyServiceImpl extends BaseServiceImpl<QsvUserAnswerRecord> i
|
||||
// 构建Excel导出实体
|
||||
List<ExcelExportEntity> excelExportEntities = buildExcelExportEntities(subjects);
|
||||
// 构建答题记录列表
|
||||
List<NutMap> list = buildAnswerRecords(answerRecords, subjectMap, options);
|
||||
List<NutMap> list = buildAnswerRecords(answerRecords, subjectMap, optionMap);
|
||||
|
||||
// 设置导出参数
|
||||
ExportParams exportParams = new ExportParams();
|
||||
@@ -223,7 +444,7 @@ public class QsvSurveyServiceImpl extends BaseServiceImpl<QsvUserAnswerRecord> i
|
||||
// List<JSONObject> answerExtList = answerRecords.stream().map(QsvUserAnswerRecord::getExtJson).toList();
|
||||
|
||||
// 查询活动题目列表
|
||||
List<QsvSubject> subjects = dao().query(QsvSubject.class, Cnd.where("activityId", "=", activityId));
|
||||
List<QsvSubject> subjects = dao().query(QsvSubject.class, Cnd.where("activityId", "=", activityId).asc("sortNum"));
|
||||
// 将题目列表转换为Map
|
||||
Map<String, QsvSubject> subjectMap = subjects.stream().collect(Collectors.toMap(QsvSubject::getId, v -> v));
|
||||
// 获取题目ID列表
|
||||
@@ -245,18 +466,15 @@ public class QsvSurveyServiceImpl extends BaseServiceImpl<QsvUserAnswerRecord> i
|
||||
.addv("unitName", record.getString("unitName"))
|
||||
.addv("unionName", record.getString("unionName"));
|
||||
JSONObject extJson = record.getAs("extJson", JSONObject.class);
|
||||
if (ObjectUtil.isEmpty(extJson)) {
|
||||
return map;
|
||||
}
|
||||
extJson.forEach((k, v) -> {
|
||||
JSONObject jsonVal = (JSONObject) v;
|
||||
|
||||
String type = subjectMap.get(k).getType();
|
||||
if (type.equals("text")) {
|
||||
map.addv(k, jsonVal.getStr("text"));
|
||||
} else if (type.equals("radio") || type.equals("checkbox")) {
|
||||
JSONArray optionIds = jsonVal.getJSONArray("optionIds");
|
||||
String selectOptionTexts = options.stream().filter(option -> optionIds.contains(option.getId()))
|
||||
.map(QsvOption::getText).collect(Collectors.joining(";"));
|
||||
map.addv(k, selectOptionTexts);
|
||||
QsvSubject subject = subjectMap.get(k);
|
||||
if (subject == null || !(v instanceof JSONObject jsonVal)) {
|
||||
return;
|
||||
}
|
||||
map.addv(k, buildAnswerText(jsonVal, subject, optionMap));
|
||||
});
|
||||
return map;
|
||||
}).toList();
|
||||
@@ -278,6 +496,171 @@ public class QsvSurveyServiceImpl extends BaseServiceImpl<QsvUserAnswerRecord> i
|
||||
}
|
||||
}
|
||||
|
||||
private List<NutMap> buildReport(String activityId, List<QsvUserAnswerRecord> answerRecords) {
|
||||
List<JSONObject> answerExtList = answerRecords.stream().map(QsvUserAnswerRecord::getExtJson).toList();
|
||||
List<NutMap> subjects = querySubjects(activityId);
|
||||
List<String> subjectIds = subjects.stream().map(v -> v.getString("id")).toList();
|
||||
List<QsvOption> options = dao().query(QsvOption.class, Cnd.where("subjectId", "in", subjectIds).asc("sortNum"));
|
||||
Map<String, List<QsvOption>> optionsGroup = options.stream().collect(Collectors.groupingBy(QsvOption::getSubjectId));
|
||||
|
||||
for (NutMap subject : subjects) {
|
||||
List<QsvOption> currentOptions = optionsGroup.get(subject.getString("id"));
|
||||
List<NutMap> subjectOptions = ObjectUtil.isEmpty(currentOptions) ? new ArrayList<>() : Lang.collection2list(currentOptions, NutMap.class);
|
||||
String subjectType = subject.getString("type");
|
||||
if ("text".equals(subjectType)) {
|
||||
List<NutMap> textAnswers = buildTextAnswerDetails(answerRecords, subject.getString("id"));
|
||||
List<String> texts = textAnswers.stream()
|
||||
.map(textAnswer -> textAnswer.getString("text"))
|
||||
.toList();
|
||||
subject.put("texts", texts);
|
||||
subject.put("textAnswers", textAnswers);
|
||||
} else if ("radio".equals(subjectType) || "checkbox".equals(subjectType)) {
|
||||
long selectTotal = answerExtList.stream()
|
||||
.filter(ext -> ObjectUtil.isNotEmpty(ext) && ext.containsKey(subject.getString("id")))
|
||||
.count();
|
||||
subjectOptions.forEach(subjectOption -> {
|
||||
long selectCount = answerExtList.stream()
|
||||
.filter(ext -> {
|
||||
if (ObjectUtil.isEmpty(ext)) {
|
||||
return false;
|
||||
}
|
||||
JSONObject jsonObject = ext.get(subject.getString("id"), JSONObject.class);
|
||||
return jsonObject != null
|
||||
&& jsonObject.getJSONArray("optionIds") != null
|
||||
&& jsonObject.getJSONArray("optionIds").contains(subjectOption.getString("id"));
|
||||
})
|
||||
.count();
|
||||
long round = selectTotal == 0 ? 0 : Math.round((double) selectCount / selectTotal * 100);
|
||||
subjectOption.put("selectPercent", round + "%");
|
||||
subjectOption.put("selectCount", selectCount);
|
||||
});
|
||||
subjectOptions.sort((o1, o2) -> Integer.compare(o1.getInt("sortNum"), o2.getInt("sortNum")));
|
||||
subject.put("selectTotal", selectTotal);
|
||||
}
|
||||
subject.addv("options", subjectOptions);
|
||||
}
|
||||
return subjects;
|
||||
}
|
||||
|
||||
private List<NutMap> parseCategoryConditions(String conditions) {
|
||||
if (ObjectUtil.isEmpty(conditions)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return JSONUtil.parseArray(conditions).toList(NutMap.class).stream()
|
||||
.filter(condition -> ObjectUtil.isNotEmpty(condition.getString("subjectId"))
|
||||
&& ObjectUtil.isNotEmpty(condition.getString("optionId")))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private List<String> parseSubjectIds(String subjectIds) {
|
||||
if (ObjectUtil.isEmpty(subjectIds)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
String value = subjectIds.trim();
|
||||
List<String> result = new ArrayList<>();
|
||||
if (value.startsWith("[")) {
|
||||
for (Object subjectId : JSONUtil.parseArray(value)) {
|
||||
if (ObjectUtil.isNotEmpty(subjectId)) {
|
||||
result.add(String.valueOf(subjectId));
|
||||
}
|
||||
}
|
||||
return result.stream().distinct().toList();
|
||||
}
|
||||
for (String subjectId : value.split(",")) {
|
||||
if (ObjectUtil.isNotEmpty(subjectId)) {
|
||||
result.add(subjectId.trim());
|
||||
}
|
||||
}
|
||||
return result.stream().distinct().toList();
|
||||
}
|
||||
|
||||
private NutMap buildVariableSubject(List<String> subjectIds, Map<String, NutMap> subjectMap) {
|
||||
String title = subjectIds.stream()
|
||||
.map(subjectId -> subjectMap.get(subjectId).getString("title"))
|
||||
.collect(Collectors.joining(" / "));
|
||||
return NutMap.NEW()
|
||||
.addv("id", String.join("|", subjectIds))
|
||||
.addv("title", title);
|
||||
}
|
||||
|
||||
private List<NutMap> buildVariableOptionCombinations(List<String> subjectIds, Map<String, NutMap> subjectMap, Map<String, List<QsvOption>> optionGroup) {
|
||||
List<List<NutMap>> subjectOptionGroups = new ArrayList<>();
|
||||
for (String subjectId : subjectIds) {
|
||||
NutMap subject = subjectMap.get(subjectId);
|
||||
List<QsvOption> options = optionGroup.get(subjectId);
|
||||
if (ObjectUtil.isEmpty(options)) {
|
||||
continue;
|
||||
}
|
||||
List<NutMap> optionMaps = options.stream()
|
||||
.map(option -> NutMap.NEW()
|
||||
.addv("subjectId", subjectId)
|
||||
.addv("subjectTitle", subject.getString("title"))
|
||||
.addv("optionId", option.getId())
|
||||
.addv("optionText", option.getText()))
|
||||
.toList();
|
||||
subjectOptionGroups.add(optionMaps);
|
||||
}
|
||||
if (subjectOptionGroups.size() != subjectIds.size()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<NutMap> result = new ArrayList<>();
|
||||
buildVariableOptionCombinations(subjectOptionGroups, 0, new ArrayList<>(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void buildVariableOptionCombinations(List<List<NutMap>> subjectOptionGroups, int index, List<NutMap> current, List<NutMap> result) {
|
||||
if (index >= subjectOptionGroups.size()) {
|
||||
String id = current.stream()
|
||||
.map(option -> option.getString("subjectId") + ":" + option.getString("optionId"))
|
||||
.collect(Collectors.joining("|"));
|
||||
String text = current.stream()
|
||||
.map(option -> option.getString("optionText"))
|
||||
.collect(Collectors.joining(" / "));
|
||||
List<NutMap> conditions = current.stream()
|
||||
.map(option -> NutMap.NEW()
|
||||
.addv("subjectId", option.getString("subjectId"))
|
||||
.addv("optionId", option.getString("optionId")))
|
||||
.toList();
|
||||
result.add(NutMap.NEW()
|
||||
.addv("id", id)
|
||||
.addv("text", text)
|
||||
.addv("conditions", conditions));
|
||||
return;
|
||||
}
|
||||
for (NutMap option : subjectOptionGroups.get(index)) {
|
||||
current.add(option);
|
||||
buildVariableOptionCombinations(subjectOptionGroups, index + 1, current, result);
|
||||
current.remove(current.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
private List<QsvUserAnswerRecord> filterAnswerRecords(List<QsvUserAnswerRecord> answerRecords, List<NutMap> conditions) {
|
||||
if (ObjectUtil.isEmpty(conditions)) {
|
||||
return answerRecords;
|
||||
}
|
||||
return answerRecords.stream()
|
||||
.filter(record -> isRecordMatched(record, conditions))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private boolean isRecordMatched(QsvUserAnswerRecord record, List<NutMap> conditions) {
|
||||
JSONObject extJson = record.getExtJson();
|
||||
if (ObjectUtil.isEmpty(extJson)) {
|
||||
return false;
|
||||
}
|
||||
for (NutMap condition : conditions) {
|
||||
JSONObject subjectAnswer = extJson.get(condition.getString("subjectId"), JSONObject.class);
|
||||
if (ObjectUtil.isEmpty(subjectAnswer)) {
|
||||
return false;
|
||||
}
|
||||
JSONArray optionIds = subjectAnswer.getJSONArray("optionIds");
|
||||
if (ObjectUtil.isEmpty(optionIds) || !optionIds.contains(condition.getString("optionId"))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private List<NutMap> querySubjects(String activityId) {
|
||||
Sql subjectSql = Sqls.create("""
|
||||
SELECT
|
||||
@@ -309,7 +692,37 @@ public class QsvSurveyServiceImpl extends BaseServiceImpl<QsvUserAnswerRecord> i
|
||||
return excelExportEntities;
|
||||
}
|
||||
|
||||
private List<NutMap> buildAnswerRecords(List<QsvUserAnswerRecord> answerRecords, Map<String, QsvSubject> subjectMap, List<QsvOption> options) {
|
||||
/**
|
||||
* 构建填空题填写详情。分析弹窗需要同时展示填写内容和答题人信息,
|
||||
* 因此这里从已完成答题记录中提取当前题目的文本答案,并携带姓名、工号、所属工会和单位。
|
||||
*
|
||||
* @param answerRecords 当前活动已完成的答题记录
|
||||
* @param subjectId 当前填空题ID,用于从 extJson 中取出该题填写内容
|
||||
* @return 填空题填写详情列表,每条包含 loginName、userName、unionName、unitName、submitTime、text
|
||||
*/
|
||||
private List<NutMap> buildTextAnswerDetails(List<QsvUserAnswerRecord> answerRecords, String subjectId) {
|
||||
List<NutMap> textAnswers = new ArrayList<>();
|
||||
for (QsvUserAnswerRecord answerRecord : answerRecords) {
|
||||
JSONObject extJson = answerRecord.getExtJson();
|
||||
if (ObjectUtil.isEmpty(extJson)) {
|
||||
continue;
|
||||
}
|
||||
JSONObject subjectAnswer = extJson.get(subjectId, JSONObject.class);
|
||||
if (ObjectUtil.isEmpty(subjectAnswer) || ObjectUtil.isEmpty(subjectAnswer.getStr("text"))) {
|
||||
continue;
|
||||
}
|
||||
textAnswers.add(NutMap.NEW()
|
||||
.addv("loginName", answerRecord.getLoginName())
|
||||
.addv("userName", answerRecord.getUserName())
|
||||
.addv("unionName", answerRecord.getUnionName())
|
||||
.addv("unitName", answerRecord.getUnitName())
|
||||
.addv("submitTime", answerRecord.getSubmitTime())
|
||||
.addv("text", subjectAnswer.getStr("text")));
|
||||
}
|
||||
return textAnswers;
|
||||
}
|
||||
|
||||
private List<NutMap> buildAnswerRecords(List<QsvUserAnswerRecord> answerRecords, Map<String, QsvSubject> subjectMap, Map<String, QsvOption> optionMap) {
|
||||
return answerRecords.stream().map(record -> {
|
||||
NutMap map = NutMap.NEW()
|
||||
.addv("id", record.getId())
|
||||
@@ -318,20 +731,61 @@ public class QsvSurveyServiceImpl extends BaseServiceImpl<QsvUserAnswerRecord> i
|
||||
.addv("unitName", record.getUnitName())
|
||||
.addv("unionName", record.getUnionName());
|
||||
JSONObject extJson = record.getExtJson();
|
||||
if (ObjectUtil.isEmpty(extJson)) {
|
||||
return map;
|
||||
}
|
||||
extJson.forEach((k, v) -> {
|
||||
JSONObject jsonVal = (JSONObject) v;
|
||||
|
||||
String type = subjectMap.get(k).getType();
|
||||
if (type.equals("text")) {
|
||||
map.addv(k, jsonVal.getStr("text"));
|
||||
} else if (type.equals("radio") || type.equals("checkbox")) {
|
||||
JSONArray optionIds = jsonVal.getJSONArray("optionIds");
|
||||
String selectOptionTexts = options.stream().filter(option -> optionIds.contains(option.getId()))
|
||||
.map(QsvOption::getText).collect(Collectors.joining(";"));
|
||||
map.addv(k, selectOptionTexts);
|
||||
QsvSubject subject = subjectMap.get(k);
|
||||
if (subject == null || !(v instanceof JSONObject jsonVal)) {
|
||||
return;
|
||||
}
|
||||
map.addv(k, buildAnswerText(jsonVal, subject, optionMap));
|
||||
});
|
||||
return map;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装单题答案展示内容。填空题返回填写文本;选择题返回选项文本,若选项配置了补充填写,
|
||||
* 则追加对应补充内容,保证后台查看答卷和导出能看到用户填写的说明。
|
||||
*
|
||||
* @param jsonVal 答题记录 extJson 中当前题目的答案对象,包含 optionIds、text、optionFillContents
|
||||
* @param subject 当前题目,用于判断题目类型
|
||||
* @param optionMap 当前活动所有选项,key 为选项ID,value 为选项实体
|
||||
* @return 当前题目的答案展示文本
|
||||
*/
|
||||
private String buildAnswerText(JSONObject jsonVal, QsvSubject subject, Map<String, QsvOption> optionMap) {
|
||||
String type = subject.getType();
|
||||
if ("text".equals(type)) {
|
||||
return jsonVal.getStr("text");
|
||||
}
|
||||
|
||||
if (!"radio".equals(type) && !"checkbox".equals(type)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
JSONArray optionIds = jsonVal.getJSONArray("optionIds");
|
||||
if (ObjectUtil.isEmpty(optionIds)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
JSONObject optionFillContents = jsonVal.getJSONObject("optionFillContents");
|
||||
List<String> answerTexts = new ArrayList<>();
|
||||
for (Object optionIdObj : optionIds) {
|
||||
String optionId = String.valueOf(optionIdObj);
|
||||
QsvOption option = optionMap.get(optionId);
|
||||
if (option == null) {
|
||||
continue;
|
||||
}
|
||||
String answerText = option.getText();
|
||||
if (ObjectUtil.isNotEmpty(optionFillContents)) {
|
||||
String fillContent = optionFillContents.getStr(optionId);
|
||||
if (ObjectUtil.isNotEmpty(fillContent)) {
|
||||
answerText = answerText + ":" + fillContent;
|
||||
}
|
||||
}
|
||||
answerTexts.add(answerText);
|
||||
}
|
||||
return String.join(";", answerTexts);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -121,11 +121,12 @@ public class QsvUserAnswerRecordServiceImpl extends BaseServiceImpl<QsvUserAnswe
|
||||
|
||||
String mode = activity.getMode();
|
||||
String scoreMode = activity.getScoreMode();
|
||||
boolean highestScoreMode = "HIGH".equals(scoreMode) || "HIGHEST".equals(scoreMode);
|
||||
|
||||
if (mode.equals("SCHEDULED")) {
|
||||
processScoreRecords(activityId, SecurityUtil.getUserId(), true, scoreMode.equals("HIGH"));
|
||||
processScoreRecords(activityId, SecurityUtil.getUserId(), true, highestScoreMode);
|
||||
} else if (mode.equals("REGULAR")) {
|
||||
processScoreRecords(activityId, SecurityUtil.getUserId(), false, scoreMode.equals("HIGH"));
|
||||
processScoreRecords(activityId, SecurityUtil.getUserId(), false, highestScoreMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user