liu
2026-09-22 0837c3ff473499d8a267924125c776b34cc8b517
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
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<CjFillTask> 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<Long> 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<CjFillTask> 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; }
    }
}