feat: 问卷任务管理模块增强
- 新增犯人完成进度查询接口 - 新增批量分配 Agent 填写功能 - 优化仪表盘统计逻辑 - 新增 Agent 填写权限 SQL
This commit is contained in:
parent
f0caa49133
commit
ff09efa216
@ -69,6 +69,21 @@ public class PrisonerDashboardStatsRespVO {
|
||||
@Schema(description = "风险等级:1-低风险 2-中风险 3-高风险 4-极高风险", example = "1")
|
||||
private Integer riskLevel;
|
||||
|
||||
@Schema(description = "剩余刑期天数", example = "1000")
|
||||
private Integer remainingDays;
|
||||
|
||||
@Schema(description = "累计违规次数", example = "5")
|
||||
private Integer violationCount;
|
||||
|
||||
@Schema(description = "累计表扬天数", example = "-")
|
||||
private String praiseDays;
|
||||
|
||||
@Schema(description = "累计扣分次数", example = "3")
|
||||
private Integer penaltyCount;
|
||||
|
||||
@Schema(description = "累计加分次数", example = "8")
|
||||
private Integer rewardCount;
|
||||
|
||||
@Schema(description = "中心左侧数据")
|
||||
private CenterLeftData centerLeftData;
|
||||
|
||||
|
||||
@ -149,6 +149,15 @@ public class PrisonQuestionnaireTaskController {
|
||||
return success(questionnaireTaskService.getPendingPrisoners(id, pageReqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/prisoner-progress")
|
||||
@Operation(summary = "获取任务的人员填写进度列表")
|
||||
@Parameter(name = "id", description = "任务ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-task:query')")
|
||||
public CommonResult<List<PrisonerProgressRespVO>> getPrisonerProgress(
|
||||
@NotNull(message = "任务ID不能为空") @RequestParam("id") Long id) {
|
||||
return success(questionnaireTaskService.getPrisonerProgress(id));
|
||||
}
|
||||
|
||||
@PostMapping("/remind")
|
||||
@Operation(summary = "提醒未完成人员")
|
||||
@Parameter(name = "id", description = "任务ID", required = true)
|
||||
@ -157,6 +166,24 @@ public class PrisonQuestionnaireTaskController {
|
||||
return success(questionnaireTaskService.remindPendingPrisoners(id));
|
||||
}
|
||||
|
||||
@PostMapping("/notify-prisoner")
|
||||
@Operation(summary = "通知单个人员")
|
||||
@Parameter(name = "recordId", description = "记录ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-task:notify')")
|
||||
public CommonResult<Boolean> notifyPrisoner(@NotNull(message = "记录ID不能为空") @RequestParam("recordId") Long recordId) {
|
||||
questionnaireTaskService.notifyPrisoner(recordId);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/reset-record")
|
||||
@Operation(summary = "重置人员答题记录")
|
||||
@Parameter(name = "recordId", description = "记录ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-record:reset')")
|
||||
public CommonResult<Boolean> resetPrisonerRecord(@NotNull(message = "记录ID不能为空") @RequestParam("recordId") Long recordId) {
|
||||
questionnaireTaskService.resetPrisonerRecord(recordId);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
// ==================== 统计相关 ====================
|
||||
|
||||
@GetMapping("/area-statistics")
|
||||
|
||||
@ -0,0 +1,55 @@
|
||||
package cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 问卷任务人员填写进度 Response VO")
|
||||
@Data
|
||||
public class PrisonerProgressRespVO {
|
||||
|
||||
@Schema(description = "记录ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "罪犯ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
private Long prisonerId;
|
||||
|
||||
@Schema(description = "罪犯编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "P001")
|
||||
private String prisonerNo;
|
||||
|
||||
@Schema(description = "罪犯姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
|
||||
private String prisonerName;
|
||||
|
||||
@Schema(description = "监区ID", example = "100")
|
||||
private Long areaId;
|
||||
|
||||
@Schema(description = "监区名称", example = "一监区")
|
||||
private String areaName;
|
||||
|
||||
@Schema(description = "填写状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "客观分", example = "80")
|
||||
private Integer objectiveScore;
|
||||
|
||||
@Schema(description = "主观分", example = "85")
|
||||
private Integer subjectiveScore;
|
||||
|
||||
@Schema(description = "总分", example = "165")
|
||||
private Integer totalScore;
|
||||
|
||||
@Schema(description = "风险等级", example = "2")
|
||||
private Integer riskLevel;
|
||||
|
||||
@Schema(description = "答题用时(秒)", example = "300")
|
||||
private Integer duration;
|
||||
|
||||
@Schema(description = "开始时间")
|
||||
private LocalDateTime startTime;
|
||||
|
||||
@Schema(description = "完成时间")
|
||||
private LocalDateTime finishTime;
|
||||
|
||||
}
|
||||
@ -129,6 +129,14 @@ public class PrisonQuestionnaireRecordController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/submit-by-agent")
|
||||
@Operation(summary = "代为提交答卷(民警代填)")
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-record:agent-fill')")
|
||||
public CommonResult<Boolean> submitAnswerByAgent(@Valid @RequestBody AssessmentAnswerSubmitReqVO reqVO) {
|
||||
questionnaireRecordService.submitAnswerByAgent(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/finish")
|
||||
@Operation(summary = "结束测评")
|
||||
@PreAuthorize("@ss.hasPermission('prison:questionnaire-record:finish')")
|
||||
|
||||
@ -67,5 +67,21 @@ public class AnswerDO extends TenantBaseDO {
|
||||
* 答题时间(秒)
|
||||
*/
|
||||
private Integer duration;
|
||||
/**
|
||||
* 是否代填:true-民警代填 false-本人填写 null-未知
|
||||
*/
|
||||
private Boolean isAgentFill;
|
||||
/**
|
||||
* 代填操作人ID(民警ID)
|
||||
*/
|
||||
private Long agentOperatorId;
|
||||
/**
|
||||
* 代填操作人姓名
|
||||
*/
|
||||
private String agentOperatorName;
|
||||
/**
|
||||
* 代填时间
|
||||
*/
|
||||
private LocalDateTime agentFillTime;
|
||||
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo.QuestionnaireTaskPageReqVO;
|
||||
import cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
@ -56,4 +57,34 @@ public interface QuestionnaireTaskMapper extends BaseMapperX<QuestionnaireTaskDO
|
||||
"WHERE deleted = 0 AND status IN (2, 3)")
|
||||
Map<String, Object> selectTaskStatisticsSummary();
|
||||
|
||||
@Select("SELECT qt.id as task_id, qt.task_name, " +
|
||||
"SUM(qr.total_count) as total_count, " +
|
||||
"SUM(qr.completed_count) as completed_count " +
|
||||
"FROM prison_questionnaire_task qt " +
|
||||
"INNER JOIN prison_questionnaire_record qr ON qt.id = qr.task_id " +
|
||||
"WHERE qt.deleted = 0 " +
|
||||
"AND qt.questionnaire_id = #{questionnaireId} " +
|
||||
"AND qr.area_id IN " +
|
||||
"<foreach item='id' collection='areaIds' open='(' separator=',' close=')'>" +
|
||||
"#{id}" +
|
||||
"</foreach> " +
|
||||
"GROUP BY qt.id, qt.task_name")
|
||||
List<Map<String, Object>> selectAreaComparisonByQuestionnaire(
|
||||
@Param("questionnaireId") Long questionnaireId,
|
||||
@Param("areaIds") List<Long> areaIds);
|
||||
|
||||
@Select("SELECT qt.id as task_id, qt.task_name, " +
|
||||
"SUM(qr.total_count) as total_count, " +
|
||||
"SUM(qr.completed_count) as completed_count " +
|
||||
"FROM prison_questionnaire_task qt " +
|
||||
"INNER JOIN prison_questionnaire_record qr ON qt.id = qr.task_id " +
|
||||
"WHERE qt.deleted = 0 " +
|
||||
"AND qr.area_id IN " +
|
||||
"<foreach item='id' collection='areaIds' open='(' separator=',' close=')'>" +
|
||||
"#{id}" +
|
||||
"</foreach> " +
|
||||
"GROUP BY qt.id, qt.task_name")
|
||||
List<Map<String, Object>> selectAreaComparisonByAreas(
|
||||
@Param("areaIds") List<Long> areaIds);
|
||||
|
||||
}
|
||||
|
||||
@ -76,8 +76,13 @@ public interface AnswerService {
|
||||
* @param questionnaireId 问卷ID
|
||||
* @param prisonerId 罪犯ID
|
||||
* @param answers 答题详情列表
|
||||
* @param isAgentFill 是否代填
|
||||
* @param agentOperatorId 代填操作人ID
|
||||
* @param agentOperatorName 代填操作人姓名
|
||||
*/
|
||||
void saveAnswers(Long assessmentRecordId, Long questionnaireId, Long prisonerId, List<AssessmentAnswerSubmitReqVO.AnswerItem> answers);
|
||||
void saveAnswers(Long assessmentRecordId, Long questionnaireId, Long prisonerId,
|
||||
List<AssessmentAnswerSubmitReqVO.AnswerItem> answers,
|
||||
Boolean isAgentFill, Long agentOperatorId, String agentOperatorName);
|
||||
|
||||
/**
|
||||
* 计算客观题得分并更新
|
||||
|
||||
@ -13,6 +13,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import java.util.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDateTime;
|
||||
import cn.iocoder.yudao.module.prison.controller.admin.answer.vo.*;
|
||||
import cn.iocoder.yudao.module.prison.controller.admin.questionnairerecord.vo.AssessmentAnswerSubmitReqVO;
|
||||
import cn.iocoder.yudao.module.prison.dal.dataobject.answer.AnswerDO;
|
||||
@ -113,10 +114,13 @@ public class AnswerServiceImpl implements AnswerService {
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveAnswers(Long assessmentRecordId, Long questionnaireId, Long prisonerId,
|
||||
List<AssessmentAnswerSubmitReqVO.AnswerItem> answers) {
|
||||
List<AssessmentAnswerSubmitReqVO.AnswerItem> answers,
|
||||
Boolean isAgentFill, Long agentOperatorId, String agentOperatorName) {
|
||||
if (CollUtil.isEmpty(answers)) {
|
||||
return;
|
||||
}
|
||||
// 删除该测评已有的旧答题记录
|
||||
deleteByAssessmentRecordId(assessmentRecordId);
|
||||
|
||||
for (AssessmentAnswerSubmitReqVO.AnswerItem answerItem : answers) {
|
||||
// 获取问题信息
|
||||
@ -151,6 +155,16 @@ public class AnswerServiceImpl implements AnswerService {
|
||||
answer.setIsCorrect(isCorrect);
|
||||
}
|
||||
|
||||
// 设置代填信息
|
||||
if (Boolean.TRUE.equals(isAgentFill)) {
|
||||
answer.setIsAgentFill(true);
|
||||
answer.setAgentOperatorId(agentOperatorId);
|
||||
answer.setAgentOperatorName(agentOperatorName);
|
||||
answer.setAgentFillTime(LocalDateTime.now());
|
||||
} else {
|
||||
answer.setIsAgentFill(false);
|
||||
}
|
||||
|
||||
answerMapper.insert(answer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,6 +208,11 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
}
|
||||
if (prisoner.getReleaseDate() != null) {
|
||||
vo.setReleaseDate(prisoner.getReleaseDate().toString());
|
||||
// 计算剩余刑期天数
|
||||
long remainingDays = ChronoUnit.DAYS.between(LocalDate.now(), prisoner.getReleaseDate());
|
||||
vo.setRemainingDays(remainingDays > 0 ? (int) remainingDays : 0);
|
||||
} else {
|
||||
vo.setRemainingDays(null);
|
||||
}
|
||||
|
||||
// 监区信息(简单拼接)
|
||||
@ -298,6 +303,30 @@ public class PrisonDashboardServiceImpl implements PrisonDashboardService {
|
||||
.orderByDesc(SituationDO::getOccurTime)
|
||||
.last("LIMIT 10"));
|
||||
|
||||
// 8. 查询累计违规次数(狱情收集-监管安全类型)
|
||||
Long violationCount = situationMapper.selectCount(new LambdaQueryWrapper<SituationDO>()
|
||||
.eq(SituationDO::getPrisonerId, prisonerId)
|
||||
.eq(SituationDO::getStatus, 3) // 已处理
|
||||
.eq(SituationDO::getCategory, 1)); // 监管安全类型
|
||||
vo.setViolationCount(violationCount != null ? violationCount.intValue() : 0);
|
||||
|
||||
// 9. 查询累计计分考核记录(统计加分和扣分次数)
|
||||
List<ScoreDO> allScores = scoreMapper.selectList(new LambdaQueryWrapper<ScoreDO>()
|
||||
.eq(ScoreDO::getPrisonerId, prisonerId)
|
||||
.eq(ScoreDO::getStatus, 1)); // 已通过
|
||||
|
||||
long rewardCount = allScores.stream()
|
||||
.filter(s -> s.getRewardScore() != null && s.getRewardScore().doubleValue() > 0)
|
||||
.count();
|
||||
long penaltyCount = allScores.stream()
|
||||
.filter(s -> s.getPenaltyScore() != null && s.getPenaltyScore().doubleValue() > 0)
|
||||
.count();
|
||||
vo.setRewardCount((int) rewardCount);
|
||||
vo.setPenaltyCount((int) penaltyCount);
|
||||
|
||||
// 10. 设置累计表扬天数(固定返回"-")
|
||||
vo.setPraiseDays("-");
|
||||
|
||||
// ==================== 构建中心数据 ====================
|
||||
|
||||
// 左侧数据:本月消费、奖励、惩罚、余额
|
||||
|
||||
@ -112,6 +112,14 @@ public interface QuestionnaireTaskService {
|
||||
*/
|
||||
PageResult<QuestionnaireRecordDO> getPendingPrisoners(Long id, PageParam pageReqVO);
|
||||
|
||||
/**
|
||||
* 获取任务的人员填写进度列表
|
||||
*
|
||||
* @param id 任务ID
|
||||
* @return 人员填写进度列表
|
||||
*/
|
||||
List<PrisonerProgressRespVO> getPrisonerProgress(Long id);
|
||||
|
||||
/**
|
||||
* 提醒未完成人员
|
||||
*
|
||||
@ -120,7 +128,19 @@ public interface QuestionnaireTaskService {
|
||||
*/
|
||||
Integer remindPendingPrisoners(Long id);
|
||||
|
||||
// ==================== 统计相关 ====================
|
||||
/**
|
||||
* 通知单个人员
|
||||
*
|
||||
* @param recordId 记录ID
|
||||
*/
|
||||
void notifyPrisoner(Long recordId);
|
||||
|
||||
/**
|
||||
* 重置人员答题记录
|
||||
*
|
||||
* @param recordId 记录ID
|
||||
*/
|
||||
void resetPrisonerRecord(Long recordId);
|
||||
|
||||
/**
|
||||
* 按监区统计任务完成情况
|
||||
|
||||
@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.prison.controller.admin.questionnaire_task.vo.*;
|
||||
import cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire_task.QuestionnaireTaskDO;
|
||||
import cn.iocoder.yudao.module.prison.dal.dataobject.questionnaire.QuestionnaireDO;
|
||||
@ -168,16 +169,12 @@ public class QuestionnaireTaskServiceImpl implements QuestionnaireTaskService {
|
||||
|
||||
@Override
|
||||
public PageResult<QuestionnaireTaskDO> getQuestionnaireTaskPage(QuestionnaireTaskPageReqVO pageReqVO) {
|
||||
Page<QuestionnaireTaskDO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
|
||||
LambdaQueryWrapper<QuestionnaireTaskDO> wrapper = new LambdaQueryWrapper<QuestionnaireTaskDO>()
|
||||
return questionnaireTaskMapper.selectPage(pageReqVO, new LambdaQueryWrapperX<QuestionnaireTaskDO>()
|
||||
.likeIfPresent(QuestionnaireTaskDO::getTaskName, pageReqVO.getTaskName())
|
||||
.eqIfPresent(QuestionnaireTaskDO::getStatus, pageReqVO.getStatus())
|
||||
.eqIfPresent(QuestionnaireTaskDO::getQuestionnaireId, pageReqVO.getQuestionnaireId())
|
||||
.betweenIfPresent(QuestionnaireTaskDO::getCreateTime, pageReqVO.getCreateTime())
|
||||
.orderByDesc(QuestionnaireTaskDO::getCreateTime);
|
||||
|
||||
Page<QuestionnaireTaskDO> result = questionnaireTaskMapper.selectPage(page, wrapper);
|
||||
return new PageResult<>(result.getRecords(), result.getTotal());
|
||||
.orderByDesc(QuestionnaireTaskDO::getCreateTime));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -329,11 +326,74 @@ public class QuestionnaireTaskServiceImpl implements QuestionnaireTaskService {
|
||||
return pendingRecords.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PrisonerProgressRespVO> getPrisonerProgress(Long id) {
|
||||
List<cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO> records = questionnaireRecordMapper.selectList(
|
||||
new LambdaQueryWrapper<cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO>()
|
||||
.eq(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getTaskId, id)
|
||||
.orderByAsc(cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO::getCreateTime)
|
||||
);
|
||||
|
||||
if (CollUtil.isEmpty(records)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return records.stream().map(record -> {
|
||||
PrisonerProgressRespVO vo = new PrisonerProgressRespVO();
|
||||
vo.setId(record.getId());
|
||||
vo.setPrisonerId(record.getPrisonerId());
|
||||
vo.setPrisonerNo(record.getPrisonerNo());
|
||||
vo.setPrisonerName(record.getPrisonerName());
|
||||
vo.setAreaId(record.getPrisonerAreaId());
|
||||
vo.setAreaName(record.getPrisonerAreaName());
|
||||
vo.setStatus(record.getStatus());
|
||||
vo.setObjectiveScore(record.getObjectiveScore() != null ? record.getObjectiveScore().intValue() : null);
|
||||
vo.setSubjectiveScore(record.getSubjectiveScore() != null ? record.getSubjectiveScore().intValue() : null);
|
||||
vo.setTotalScore(record.getTotalScore() != null ? record.getTotalScore().intValue() : null);
|
||||
vo.setRiskLevel(record.getRiskLevel());
|
||||
vo.setStartTime(record.getStartTime());
|
||||
vo.setFinishTime(record.getEndTime());
|
||||
vo.setDuration(record.getDuration());
|
||||
return vo;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyPrisoner(Long recordId) {
|
||||
cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO record = questionnaireRecordMapper.selectById(recordId);
|
||||
if (record == null) {
|
||||
throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_RECORD_NOT_EXISTS);
|
||||
}
|
||||
// TODO: 发送站内信通知,此处为占位实现
|
||||
log.info("通知任务 {} 中的罪犯 {} 完成问卷", record.getTaskId(), record.getPrisonerName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetPrisonerRecord(Long recordId) {
|
||||
cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO record = questionnaireRecordMapper.selectById(recordId);
|
||||
if (record == null) {
|
||||
throw new ServiceException(ErrorCodeConstants.QUESTIONNAIRE_RECORD_NOT_EXISTS);
|
||||
}
|
||||
// 重置记录状态为待测评
|
||||
cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO updateRecord = new cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO();
|
||||
updateRecord.setId(recordId);
|
||||
updateRecord.setStatus(QuestionnaireRecordStatusEnum.PENDING.getCode());
|
||||
updateRecord.setObjectiveScore(null);
|
||||
updateRecord.setSubjectiveScore(null);
|
||||
updateRecord.setTotalScore(null);
|
||||
updateRecord.setRiskLevel(null);
|
||||
updateRecord.setStartTime(null);
|
||||
updateRecord.setEndTime(null);
|
||||
updateRecord.setDuration(null);
|
||||
questionnaireRecordMapper.updateById(updateRecord);
|
||||
log.info("重置任务 {} 中的罪犯 {} 答题记录", record.getTaskId(), record.getPrisonerName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TaskAreaStatisticsRespVO> getTaskAreaStatistics(Long id) {
|
||||
List<Map<String, Object>> stats = questionnaireRecordMapper.selectMaps(
|
||||
new QueryWrapper<cn.iocoder.yudao.module.prison.dal.dataobject.questionnairerecord.QuestionnaireRecordDO>()
|
||||
.select("area_id", "area_name",
|
||||
.select("prisoner_area_id", "prisoner_area_name",
|
||||
"COUNT(*) as total_count",
|
||||
"SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) as completed_count",
|
||||
"AVG(CASE WHEN status = 3 THEN total_score ELSE NULL END) as avg_score",
|
||||
@ -342,7 +402,7 @@ public class QuestionnaireTaskServiceImpl implements QuestionnaireTaskService {
|
||||
"SUM(CASE WHEN risk_level = 2 THEN 1 ELSE 0 END) as medium_risk_count",
|
||||
"SUM(CASE WHEN risk_level = 3 THEN 1 ELSE 0 END) as low_risk_count")
|
||||
.eq("task_id", id)
|
||||
.groupBy("area_id", "area_name")
|
||||
.groupBy("prisoner_area_id", "prisoner_area_name")
|
||||
.orderByDesc("completed_count")
|
||||
);
|
||||
|
||||
@ -355,8 +415,8 @@ public class QuestionnaireTaskServiceImpl implements QuestionnaireTaskService {
|
||||
new BigDecimal(stat.get("pass_rate").toString()) : BigDecimal.ZERO;
|
||||
|
||||
return TaskAreaStatisticsRespVO.builder()
|
||||
.areaId(stat.get("area_id") != null ? ((Number) stat.get("area_id")).longValue() : null)
|
||||
.areaName((String) stat.get("area_name"))
|
||||
.areaId(stat.get("prisoner_area_id") != null ? ((Number) stat.get("prisoner_area_id")).longValue() : null)
|
||||
.areaName((String) stat.get("prisoner_area_name"))
|
||||
.totalCount(total)
|
||||
.completedCount(completed)
|
||||
.completionRate(total > 0 ? BigDecimal.valueOf(completed * 100.0 / total).setScale(2, RoundingMode.HALF_UP) : BigDecimal.ZERO)
|
||||
@ -405,30 +465,9 @@ public class QuestionnaireTaskServiceImpl implements QuestionnaireTaskService {
|
||||
|
||||
List<Map<String, Object>> stats;
|
||||
if (questionnaireId != null) {
|
||||
stats = questionnaireTaskMapper.selectMaps(
|
||||
new QueryWrapper<QuestionnaireTaskDO>()
|
||||
.select("qt.id as task_id", "qt.task_name",
|
||||
"SUM(qr.total_count) as total_count",
|
||||
"SUM(qr.completed_count) as completed_count")
|
||||
.from("prison_questionnaire_task qt")
|
||||
.innerJoin("prison_questionnaire_record qr ON qt.id = qr.task_id")
|
||||
.eq("qt.deleted", 0)
|
||||
.eq("qt.questionnaire_id", questionnaireId)
|
||||
.in("qr.area_id", areaIds)
|
||||
.groupBy("qt.id", "qt.task_name")
|
||||
);
|
||||
stats = questionnaireTaskMapper.selectAreaComparisonByQuestionnaire(questionnaireId, areaIds);
|
||||
} else {
|
||||
stats = questionnaireTaskMapper.selectMaps(
|
||||
new QueryWrapper<QuestionnaireTaskDO>()
|
||||
.select("qt.id as task_id", "qt.task_name",
|
||||
"SUM(qr.total_count) as total_count",
|
||||
"SUM(qr.completed_count) as completed_count")
|
||||
.from("prison_questionnaire_task qt")
|
||||
.innerJoin("prison_questionnaire_record qr ON qt.id = qr.task_id")
|
||||
.eq("qt.deleted", 0)
|
||||
.in("qr.area_id", areaIds)
|
||||
.groupBy("qt.id", "qt.task_name")
|
||||
);
|
||||
stats = questionnaireTaskMapper.selectAreaComparisonByAreas(areaIds);
|
||||
}
|
||||
|
||||
return stats.stream().map(stat -> AreaComparisonRespVO.builder()
|
||||
|
||||
@ -86,6 +86,13 @@ public interface QuestionnaireRecordService {
|
||||
*/
|
||||
void submitAnswer(@Valid AssessmentAnswerSubmitReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 代为提交答卷(民警代填)
|
||||
*
|
||||
* @param reqVO 提交信息,包含代填操作人信息
|
||||
*/
|
||||
void submitAnswerByAgent(@Valid AssessmentAnswerSubmitReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 结束测评
|
||||
*
|
||||
|
||||
@ -161,6 +161,25 @@ public class QuestionnaireRecordServiceImpl implements QuestionnaireRecordServic
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void submitAnswer(AssessmentAnswerSubmitReqVO reqVO) {
|
||||
submitAnswerInternal(reqVO, false, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void submitAnswerByAgent(AssessmentAnswerSubmitReqVO reqVO) {
|
||||
// 获取当前登录的民警信息
|
||||
Long currentUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
String currentUserName = SecurityFrameworkUtils.getLoginUser().getInfo() != null
|
||||
? SecurityFrameworkUtils.getLoginUser().getInfo().get("nickname")
|
||||
: "未知";
|
||||
submitAnswerInternal(reqVO, true, currentUserId, currentUserName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部提交答卷方法
|
||||
*/
|
||||
private void submitAnswerInternal(AssessmentAnswerSubmitReqVO reqVO, Boolean isAgentFill,
|
||||
Long agentOperatorId, String agentOperatorName) {
|
||||
QuestionnaireRecordDO record = validateQuestionnaireRecordExists(reqVO.getRecordId());
|
||||
if (!QuestionnaireRecordStatusEnum.IN_PROGRESS.getStatus().equals(record.getStatus())) {
|
||||
throw exception(QUESTIONNAIRE_RECORD_STATUS_ERROR);
|
||||
@ -171,7 +190,8 @@ public class QuestionnaireRecordServiceImpl implements QuestionnaireRecordServic
|
||||
record.setDuration((int) java.time.Duration.between(record.getStartTime(), LocalDateTime.now()).getSeconds());
|
||||
}
|
||||
// 保存答题记录并计算客观题得分
|
||||
answerService.saveAnswers(reqVO.getRecordId(), record.getQuestionnaireId(), reqVO.getPrisonerId(), reqVO.getAnswers());
|
||||
answerService.saveAnswers(reqVO.getRecordId(), record.getQuestionnaireId(), reqVO.getPrisonerId(),
|
||||
reqVO.getAnswers(), isAgentFill, agentOperatorId, agentOperatorName);
|
||||
// 计算客观题得分
|
||||
BigDecimal objectiveScore = answerService.calculateObjectiveScore(reqVO.getRecordId());
|
||||
record.setObjectiveScore(objectiveScore);
|
||||
|
||||
@ -0,0 +1,110 @@
|
||||
-- =====================================================
|
||||
-- XL监狱综合管理平台 - 问卷代填权限SQL
|
||||
-- 生成时间: 2026-01-26
|
||||
-- 功能: 添加问卷代填按钮权限
|
||||
-- =====================================================
|
||||
|
||||
-- =====================================================
|
||||
-- 方法1: 自动查找并插入(推荐)
|
||||
-- =====================================================
|
||||
|
||||
-- 1. 查找问卷记录管理(assessment-record)的父菜单ID
|
||||
SELECT @parentId := parent_id
|
||||
FROM system_menu
|
||||
WHERE name = '测评记录管理' AND type = 2 AND deleted = 0
|
||||
LIMIT 1;
|
||||
|
||||
-- 如果找不到,尝试查找 questionnaire-record
|
||||
SELECT @parentId := id
|
||||
FROM system_menu
|
||||
WHERE path = 'assessment-record' AND type = 2 AND deleted = 0
|
||||
LIMIT 1;
|
||||
|
||||
-- 2. 插入代填权限
|
||||
INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status)
|
||||
SELECT '代为填写', 'prison:questionnaire-record:agent-fill', 3, 8, @parentId, '', '', '', 0
|
||||
WHERE @parentId IS NOT NULL;
|
||||
|
||||
-- 3. 验证插入结果
|
||||
SELECT * FROM system_menu
|
||||
WHERE permission = 'prison:questionnaire-record:agent-fill';
|
||||
|
||||
-- =====================================================
|
||||
-- 方法2: 手动指定父菜单ID插入
|
||||
-- =====================================================
|
||||
-- 请将 5047 替换为实际的测评记录管理父菜单ID
|
||||
|
||||
-- INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status)
|
||||
-- VALUES ('代为填写', 'prison:questionnaire-record:agent-fill', 3, 8, 5047, '', '', '', 0);
|
||||
|
||||
-- =====================================================
|
||||
-- 方法3: 完整的查找和插入脚本(包含错误处理)
|
||||
-- =====================================================
|
||||
|
||||
-- 设置临时变量
|
||||
SET @parentId := NULL;
|
||||
|
||||
-- 查找问卷记录管理菜单的父ID
|
||||
SELECT parent_id INTO @parentId
|
||||
FROM system_menu
|
||||
WHERE name = '测评记录管理'
|
||||
AND type = 2
|
||||
AND deleted = 0
|
||||
ORDER BY id DESC
|
||||
LIMIT 1;
|
||||
|
||||
-- 如果没找到,尝试通过path查找
|
||||
IF @parentId IS NULL THEN
|
||||
SELECT parent_id INTO @parentId
|
||||
FROM system_menu
|
||||
WHERE path = 'assessment-record'
|
||||
AND type = 2
|
||||
AND deleted = 0
|
||||
ORDER BY id DESC
|
||||
LIMIT 1;
|
||||
END IF;
|
||||
|
||||
-- 如果还是没找到,输出提示
|
||||
SELECT
|
||||
CASE
|
||||
WHEN @parentId IS NULL THEN '未找到测评记录管理菜单,请手动检查!'
|
||||
ELSE CONCAT('找到父菜单ID: ', @parentId, ',即将插入代填权限...')
|
||||
END AS result;
|
||||
|
||||
-- 插入代填权限(使用 INSERT IGNORE 避免重复插入)
|
||||
INSERT IGNORE INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted)
|
||||
VALUES (
|
||||
'代为填写',
|
||||
'prison:questionnaire-record:agent-fill',
|
||||
3, -- type=3 表示按钮
|
||||
8, -- sort=8 排在最后(根据现有排序调整)
|
||||
@parentId, -- 父菜单ID
|
||||
'', -- path
|
||||
'', -- icon
|
||||
'', -- component
|
||||
0, -- status
|
||||
b'1', -- visible
|
||||
b'0', -- keep_alive
|
||||
b'1', -- always_show
|
||||
'system',
|
||||
NOW(),
|
||||
'',
|
||||
NOW(),
|
||||
b'0'
|
||||
);
|
||||
|
||||
-- =====================================================
|
||||
-- 验证结果
|
||||
-- =====================================================
|
||||
|
||||
-- 查看所有 questionnaire-record 相关的权限
|
||||
SELECT id, name, permission, type, sort, parent_id
|
||||
FROM system_menu
|
||||
WHERE (permission LIKE 'prison:questionnaire-record:%'
|
||||
OR permission LIKE 'prison:assessment-record:%')
|
||||
AND deleted = 0
|
||||
ORDER BY parent_id, sort;
|
||||
|
||||
-- 查看插入的代填权限
|
||||
SELECT * FROM system_menu
|
||||
WHERE permission = 'prison:questionnaire-record:agent-fill';
|
||||
@ -0,0 +1,35 @@
|
||||
-- ================================================
|
||||
-- 代填功能数据库迁移脚本
|
||||
-- 添加 prison_answer 表的代填相关字段
|
||||
-- ================================================
|
||||
|
||||
-- 检查字段是否已存在,如果不存在则添加
|
||||
-- 注意:MySQL 语法
|
||||
|
||||
-- 1. 添加是否代填字段
|
||||
ALTER TABLE `prison_answer`
|
||||
ADD COLUMN `is_agent_fill` TINYINT(1) NULL COMMENT '是否代填:1-是,0-否,null-未知' AFTER `duration`;
|
||||
|
||||
-- 2. 添加代填操作人ID字段
|
||||
ALTER TABLE `prison_answer`
|
||||
ADD COLUMN `agent_operator_id` BIGINT NULL COMMENT '代填操作人ID(民警ID)' AFTER `is_agent_fill`;
|
||||
|
||||
-- 3. 添加代填操作人姓名字段
|
||||
ALTER TABLE `prison_answer`
|
||||
ADD COLUMN `agent_operator_name` VARCHAR(100) NULL COMMENT '代填操作人姓名' AFTER `agent_operator_id`;
|
||||
|
||||
-- 4. 添加代填时间字段
|
||||
ALTER TABLE `prison_answer`
|
||||
ADD COLUMN `agent_fill_time` DATETIME NULL COMMENT '代填时间' AFTER `agent_operator_name`;
|
||||
|
||||
-- ================================================
|
||||
-- 回滚脚本(如需回滚)
|
||||
-- ================================================
|
||||
-- ALTER TABLE `prison_answer`
|
||||
-- DROP COLUMN IF EXISTS `is_agent_fill`;
|
||||
-- ALTER TABLE `prison_answer`
|
||||
-- DROP COLUMN IF EXISTS `agent_operator_id`;
|
||||
-- ALTER TABLE `prison_answer`
|
||||
-- DROP COLUMN IF EXISTS `agent_operator_name`;
|
||||
-- ALTER TABLE `prison_answer`
|
||||
-- DROP COLUMN IF EXISTS `agent_fill_time`;
|
||||
Loading…
x
Reference in New Issue
Block a user