package com.ruoyi.system.service.impl; import java.util.ArrayList; import java.util.List; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.system.domain.CjFillTask; import com.ruoyi.system.domain.CjFormConfig; import com.ruoyi.system.mapper.CjFillTaskMapper; import com.ruoyi.system.mapper.CjFormConfigMapper; import com.ruoyi.system.mapper.CjStudentMapper; import com.ruoyi.system.service.ICjFillTaskService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * 填表任务Service业务层处理 * * @author ruoyi * @date 2026-09-09 */ @Service public class CjFillTaskServiceImpl implements ICjFillTaskService { private static final Logger log = LoggerFactory.getLogger(CjFillTaskServiceImpl.class); /** 默认“是否外省/境外=否”排除集(本省+直辖市,可按任务调整) */ private static final String DEFAULT_OUTSIDE_EXCLUDE = "北京市,天津市,上海市,重庆市,江苏省"; @Autowired private CjFillTaskMapper cjFillTaskMapper; @Autowired private CjFormConfigMapper cjFormConfigMapper; @Autowired private CjStudentMapper cjStudentMapper; private final ObjectMapper objectMapper = new ObjectMapper(); /** * 查询填表任务 * * @param taskId 填表任务主键 * @return 填表任务 */ @Override public CjFillTask selectCjFillTaskByTaskId(Long taskId) { return cjFillTaskMapper.selectCjFillTaskByTaskId(taskId); } /** * 查询填表任务列表 * * @param cjFillTask 填表任务 * @return 填表任务集合 */ @Override public List selectCjFillTaskList(CjFillTask cjFillTask) { return cjFillTaskMapper.selectCjFillTaskList(cjFillTask); } /** * 新增填表任务 * * @param cjFillTask 填表任务 * @return 结果 */ @Override public int insertCjFillTask(CjFillTask cjFillTask) { if (cjFillTask.getTaskName() == null || cjFillTask.getTaskName().trim().isEmpty()) { throw new ServiceException("请填写任务名称"); } if (existSameName(cjFillTask.getTaskName(), null)) { throw new ServiceException("任务名称已存在,请换一个"); } // 默认参数 if (cjFillTask.getNeedApprove() == null || cjFillTask.getNeedApprove().isEmpty()) { cjFillTask.setNeedApprove("1"); } if (cjFillTask.getOutsideExclude() == null || cjFillTask.getOutsideExclude().isEmpty()) { cjFillTask.setOutsideExclude(DEFAULT_OUTSIDE_EXCLUDE); } if (cjFillTask.getTwhkAsOutside() == null || cjFillTask.getTwhkAsOutside().isEmpty()) { cjFillTask.setTwhkAsOutside("0"); } if (cjFillTask.getEnableEnterpriseLib() == null || cjFillTask.getEnableEnterpriseLib().isEmpty()) { cjFillTask.setEnableEnterpriseLib("0"); } if (cjFillTask.getStatus() == null || cjFillTask.getStatus().isEmpty()) { cjFillTask.setStatus("0"); } // 表单快照(从引用的填表配置生成) refreshSnapshot(cjFillTask); // 填报范围人数 refreshScopeCount(cjFillTask); cjFillTask.setCreateBy(username()); cjFillTask.setCreateTime(DateUtils.getNowDate()); return cjFillTaskMapper.insertCjFillTask(cjFillTask); } /** * 修改填表任务(仅草稿状态可改) * * @param cjFillTask 填表任务 * @return 结果 */ @Override public int updateCjFillTask(CjFillTask cjFillTask) { CjFillTask old = cjFillTaskMapper.selectCjFillTaskByTaskId(cjFillTask.getTaskId()); if (old == null) { throw new ServiceException("填表任务不存在"); } if (!"0".equals(old.getStatus())) { throw new ServiceException("任务已发布/截止,不可修改,请先取消发布或另建任务"); } if (cjFillTask.getTaskName() != null && !cjFillTask.getTaskName().trim().isEmpty() && existSameName(cjFillTask.getTaskName(), cjFillTask.getTaskId())) { throw new ServiceException("任务名称已存在,请换一个"); } // 合并:保留旧值,用本次提交覆盖非空字段 CjFillTask merged = new CjFillTask(); copy(merged, old); copy(merged, cjFillTask); refreshSnapshot(merged); refreshScopeCount(merged); merged.setUpdateBy(username()); merged.setUpdateTime(DateUtils.getNowDate()); return cjFillTaskMapper.updateCjFillTask(merged); } /** * 批量删除填表任务(仅草稿) * * @param taskIds 需要删除的填表任务主键 * @return 结果 */ @Override public int deleteCjFillTaskByTaskIds(Long[] taskIds) { if (taskIds == null || taskIds.length == 0) { return 0; } for (Long taskId : taskIds) { CjFillTask old = cjFillTaskMapper.selectCjFillTaskByTaskId(taskId); if (old == null) { continue; } if (!"0".equals(old.getStatus())) { throw new ServiceException("仅“草稿”状态的任务可删除,请先取消发布/归档处理"); } } return cjFillTaskMapper.deleteCjFillTaskByTaskIds(taskIds); } /** * 发布填表任务(草稿→填报中) * * @param taskId 任务主键 * @return 结果 */ @Override public int publish(Long taskId) { CjFillTask task = require(taskId); if (!"0".equals(task.getStatus())) { throw new ServiceException("仅“草稿”状态的任务可发布"); } if (task.getFillStartDate() == null || task.getFillEndDate() == null) { throw new ServiceException("发布前请先设置填报起止日期"); } if (task.getFillStartDate().after(task.getFillEndDate())) { throw new ServiceException("填报开始日期不能晚于截止日期"); } // 统一班级码注册制:范围以班级为准,学生随后可扫码注册建档填报,注册人数允许为 0 if (task.getScopeJson() == null || task.getScopeJson().trim().isEmpty()) { throw new ServiceException("发布前请先选择填报范围(至少一个班级)"); } if (task.getSchemaJson() == null || task.getSchemaJson().trim().isEmpty()) { throw new ServiceException("发布前请先关联填表配置(生成表单快照)"); } task.setStatus("1"); task.setPublishBy(username()); task.setPublishTime(DateUtils.getNowDate()); return save(task); } /** * 取消发布(填报中→草稿) * * @param taskId 任务主键 * @return 结果 */ @Override public int unpublish(Long taskId) { CjFillTask task = require(taskId); if (!"1".equals(task.getStatus())) { throw new ServiceException("仅“填报中”状态的任务可取消发布"); } task.setStatus("0"); return save(task); } /** * 截止(填报中/草稿→已截止) * * @param taskId 任务主键 * @return 结果 */ @Override public int close(Long taskId) { CjFillTask task = require(taskId); if (!"1".equals(task.getStatus()) && !"0".equals(task.getStatus())) { throw new ServiceException("仅“草稿/填报中”状态的任务可截止"); } task.setStatus("2"); return save(task); } /** * 归档(已截止→已归档) * * @param taskId 任务主键 * @return 结果 */ @Override public int archive(Long taskId) { CjFillTask task = require(taskId); if (!"2".equals(task.getStatus())) { throw new ServiceException("仅“已截止”状态的任务可归档"); } task.setStatus("3"); return save(task); } // ========== 私有工具 ========== private CjFillTask require(Long taskId) { CjFillTask task = cjFillTaskMapper.selectCjFillTaskByTaskId(taskId); if (task == null) { throw new ServiceException("填表任务不存在"); } return task; } private int save(CjFillTask task) { task.setUpdateBy(username()); task.setUpdateTime(DateUtils.getNowDate()); return cjFillTaskMapper.updateCjFillTask(task); } private void copy(CjFillTask dest, CjFillTask src) { if (src == null) { return; } if (src.getTaskId() != null) { dest.setTaskId(src.getTaskId()); } if (src.getTaskName() != null) { dest.setTaskName(src.getTaskName()); } if (src.getSchoolYear() != null) { dest.setSchoolYear(src.getSchoolYear()); } if (src.getSemester() != null) { dest.setSemester(src.getSemester()); } if (src.getConfigId() != null) { dest.setConfigId(src.getConfigId()); } if (src.getSchemaJson() != null) { dest.setSchemaJson(src.getSchemaJson()); } if (src.getScopeJson() != null) { dest.setScopeJson(src.getScopeJson()); } if (src.getScopeCount() != null) { dest.setScopeCount(src.getScopeCount()); } if (src.getFillStartDate() != null) { dest.setFillStartDate(src.getFillStartDate()); } if (src.getFillEndDate() != null) { dest.setFillEndDate(src.getFillEndDate()); } if (src.getNeedApprove() != null) { dest.setNeedApprove(src.getNeedApprove()); } if (src.getOutsideExclude() != null) { dest.setOutsideExclude(src.getOutsideExclude()); } if (src.getTwhkAsOutside() != null) { dest.setTwhkAsOutside(src.getTwhkAsOutside()); } if (src.getRangeMode() != null) { dest.setRangeMode(src.getRangeMode()); } if (src.getEnableEnterpriseLib() != null) { dest.setEnableEnterpriseLib(src.getEnableEnterpriseLib()); } if (src.getStatus() != null) { dest.setStatus(src.getStatus()); } if (src.getPublishBy() != null) { dest.setPublishBy(src.getPublishBy()); } if (src.getPublishTime() != null) { dest.setPublishTime(src.getPublishTime()); } if (src.getDelFlag() != null) { dest.setDelFlag(src.getDelFlag()); } if (src.getRemark() != null) { dest.setRemark(src.getRemark()); } } /** 由 configId 关联的填表配置生成表单快照 {configId, groups, attachments} */ private void refreshSnapshot(CjFillTask task) { Long configId = task.getConfigId(); if (configId == null) { if (task.getSchemaJson() == null || task.getSchemaJson().trim().isEmpty()) { throw new ServiceException("请关联一张填表配置(作为本任务要收集的表单)"); } return; } CjFormConfig config = cjFormConfigMapper.selectCjFormConfigByConfigId(configId); if (config == null) { throw new ServiceException("引用的填表配置不存在"); } try { ObjectNode snap = objectMapper.createObjectNode(); snap.put("configId", config.getConfigId()); snap.set("configName", objectMapper.getNodeFactory().textNode(config.getConfigName())); snap.set("groups", objectMapper.readTree(config.getGroupsJson())); String att = config.getAttachmentsJson(); snap.set("attachments", (att == null || att.trim().isEmpty()) ? objectMapper.createArrayNode() : objectMapper.readTree(att)); task.setSchemaJson(objectMapper.writeValueAsString(snap)); } catch (Exception e) { throw new ServiceException("生成表单快照失败: " + e.getMessage()); } } /** 从 scopeJson.classIds 统计学生人数并回写 scopeCount */ private void refreshScopeCount(CjFillTask task) { String scope = task.getScopeJson(); if (scope == null || scope.trim().isEmpty()) { if ("0".equals(task.getStatus())) { throw new ServiceException("请选择填报范围(至少一个班级)"); } return; } Long[] ids; try { JsonNode root = objectMapper.readTree(scope); JsonNode arr = root.path("classIds"); if (!arr.isArray() || arr.size() == 0) { if ("0".equals(task.getStatus())) { throw new ServiceException("请选择填报范围(至少一个班级)"); } return; } List list = new ArrayList<>(); for (JsonNode node : arr) { list.add(node.asLong()); } ids = list.toArray(new Long[0]); } catch (Exception e) { throw new ServiceException("填报范围数据格式错误: " + e.getMessage()); } task.setScopeCount((long) cjStudentMapper.countByClassIds(ids)); } /** 名称唯一性检查(创建/编辑) */ private boolean existSameName(String taskName, Long excludeTaskId) { CjFillTask q = new CjFillTask(); q.setTaskName(taskName); List list = cjFillTaskMapper.selectCjFillTaskList(q); if (list != null) { for (CjFillTask t : list) { if (excludeTaskId == null || !excludeTaskId.equals(t.getTaskId())) { return true; } } } return false; } private String username() { try { return SecurityUtils.getUsername(); } catch (Exception e) { return null; } } }