刘光辉
5 小时以前 06f314545ac3514d7ce28939920866ae8188d31c
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
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
package jnpf.dmsService.impl;
 
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import jnpf.audit.sdk.annotation.AuditLog;
import jnpf.base.ActionResult;
import jnpf.base.ActionResultCode;
import jnpf.base.UserInfo;
import jnpf.dmsEntity.DmsWenjianFushenJihuaFenpeiZixiangEntity;
import jnpf.dmsEntity.DmsWenjianFushenZhixingEntity;
import jnpf.dmsEntity.DmsWenjianFushenZhixingZixiangEntity;
import jnpf.dmsEntity.DmsWenjianZiyuanEntity;
import jnpf.dmsEntity.storage.form.DmsFileExtendValidityForm;
import jnpf.dmsEntity.storage.form.DmsFileExtendValidityForm.FileRequest;
import jnpf.dmsEntity.form.DmsWenjianFushenZhixingBatchReturnForm;
import jnpf.dmsMapper.DmsWenjianFushenJihuaFenpeiZixiangMapper;
import jnpf.dmsMapper.DmsWenjianFushenZhixingMapper;
import jnpf.dmsMapper.DmsWenjianFushenZhixingZixiangMapper;
import jnpf.dmsMapper.DmsWenjianZiyuanMapper;
import jnpf.dmsPermission.DmsCurrentIdentity;
import jnpf.dmsService.DmsFileValidityService;
import jnpf.dmsService.DmsWenjianFushenZhixingService;
import jnpf.exception.DataException;
import jnpf.flowable.TaskApi;
import jnpf.flowable.WorkFlowApi;
import jnpf.flowable.entity.TemplateJsonEntity;
import jnpf.flowable.enums.TemplateJsonStatueEnum;
import jnpf.flowable.model.task.FlowModel;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
import java.text.SimpleDateFormat;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
 
@Service
@RequiredArgsConstructor
public class DmsWenjianFushenZhixingServiceImpl implements DmsWenjianFushenZhixingService {
 
    private static final String KEYANYONG = "keyanyong";
    private static final String XUXIUDING = "xuxiuding";
    private static final String XUCHEXIAO = "xuchexiao";
    private static final String REVISION_TEMPLATE_ID = "861598967111148613";
    private static final String REVOCATION_TEMPLATE_ID = "859064410424006213";
    private static final ZoneId SHANGHAI_ZONE = ZoneId.of("Asia/Shanghai");
    private static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
 
    private final DmsWenjianFushenJihuaFenpeiZixiangMapper zixiangMapper;
    private final DmsWenjianFushenZhixingMapper zhixingMapper;
    private final DmsWenjianFushenZhixingZixiangMapper zhixingZixiangMapper;
    private final DmsWenjianZiyuanMapper ziyuanMapper;
    private final DmsFileValidityService fileValidityService;
    private final DmsCurrentIdentity identity;
    private final TaskApi taskApi;
    private final WorkFlowApi workFlowApi;
 
    @Override
    @Transactional(rollbackFor = Exception.class)
    @AuditLog(
            action = "FILE_REVIEW_BATCH_RETURN",
            label = "文件复审执行批量退回",
            bizModule = "文件复审执行",
            bizType = "DMS_FILE_REVIEW",
            bizCodeExpr = "",
            bizCodeRequired = false,
            targetTable = "wenjian_fushen_jihua_fenpei_zixiang",
            targetIdExpr = "#p0[0].fenpeiZixiangId",
            targetIdsExpr = "#p0.![fenpeiZixiangId]",
            diff = true,
            entityClass = DmsWenjianFushenJihuaFenpeiZixiangEntity.class
    )
    public void batchReturn(List<DmsWenjianFushenZhixingBatchReturnForm> forms) throws DataException {
        Set<String> zixiangIds = resolveZixiangIds(forms);
 
        LambdaUpdateWrapper<DmsWenjianFushenJihuaFenpeiZixiangEntity> update = new LambdaUpdateWrapper<>();
        update.in(DmsWenjianFushenJihuaFenpeiZixiangEntity::getId, zixiangIds)
                .isNull(DmsWenjianFushenJihuaFenpeiZixiangEntity::getDeleteMark)
                .set(DmsWenjianFushenJihuaFenpeiZixiangEntity::getHuiguRen, null)
                .set(DmsWenjianFushenJihuaFenpeiZixiangEntity::getFenpeiBumenId, null)
                .set(DmsWenjianFushenJihuaFenpeiZixiangEntity::getFenpeiBumenFuzeren, null)
                .set(DmsWenjianFushenJihuaFenpeiZixiangEntity::getFenpeiRiqi, null)
                .set(DmsWenjianFushenJihuaFenpeiZixiangEntity::getBizStatus, "daifenpei");
        if (zixiangMapper.update(null, update) != zixiangIds.size()) {
            throw new DataException("存在未找到或已退回的文件复审计划分配子项,无法批量退回");
        }
    }
 
    @Override
    @Transactional(rollbackFor = Exception.class)
    @AuditLog(
            action = "FILE_REVIEW_EXECUTION_WRITE_BACK",
            label = "文件复审执行结果回写",
            bizModule = "文件复审执行",
            bizType = "DMS_FILE_REVIEW",
            bizCodeExpr = "",
            bizCodeRequired = false,
            targetTable = "wenjian_fushen_zhixing",
            targetIdExpr = "#p0"
    )
    public void writeBack(String fId) throws DataException {
        String zhixingId = requireValue(fId, "文件复审执行ID(f_id)");
        DmsWenjianFushenZhixingEntity zhixing = findZhixing(zhixingId);
        List<DmsWenjianFushenZhixingZixiangEntity> zhixingZixiangs = findZhixingZixiangs(zhixingId);
        Set<String> fenpeiZixiangIds = new LinkedHashSet<>();
 
        for (DmsWenjianFushenZhixingZixiangEntity zhixingZixiang : zhixingZixiangs) {
            String fenpeiZixiangId = requireValue(zhixingZixiang.getFenpeiZixiangId(),
                    "文件复审执行子项fenpei_zixiang_id");
            if (!fenpeiZixiangIds.add(fenpeiZixiangId)) {
                throw new DataException("文件复审执行子项不能关联重复的fenpei_zixiang_id");
            }
        }
        for (DmsWenjianFushenZhixingZixiangEntity zhixingZixiang : zhixingZixiangs) {
            String fenpeiZixiangId = zhixingZixiang.getFenpeiZixiangId().trim();
            writeBackZixiang(zhixing, zhixingZixiang, fenpeiZixiangId);
        }
        handleConclusionGroups(zhixing, zhixingZixiangs);
    }
 
    private void handleConclusionGroups(DmsWenjianFushenZhixingEntity zhixing,
            List<DmsWenjianFushenZhixingZixiangEntity> zixiangs) throws DataException {
        Map<String, List<DmsWenjianFushenZhixingZixiangEntity>> groups = new LinkedHashMap<>();
        for (DmsWenjianFushenZhixingZixiangEntity zixiang : zixiangs) {
            String conclusion = trimToNull(zixiang.getZuizhongJielun());
            if (conclusion != null) {
                groups.computeIfAbsent(conclusion, key -> new ArrayList<>()).add(zixiang);
            }
        }
        for (Map.Entry<String, List<DmsWenjianFushenZhixingZixiangEntity>> entry : groups.entrySet()) {
            if (KEYANYONG.equals(entry.getKey())) {
                extendUsableFiles(zhixing.getId(), entry.getValue());
            } else if (XUXIUDING.equals(entry.getKey())) {
                createRevisionDraft(entry.getValue());
            } else if (XUCHEXIAO.equals(entry.getKey())) {
                createRevocationDraft(entry.getValue());
            }
        }
    }
 
    private void extendUsableFiles(String zhixingId,
            List<DmsWenjianFushenZhixingZixiangEntity> zixiangs) throws DataException {
        UserInfo user = identity.requireUser();
        String tenantId = identity.tenantId(user);
        String fushenRiqi = formatDate(new Date());
        Set<String> wenjianIds = new LinkedHashSet<>();
        Set<UUID> fileVersionIds = new LinkedHashSet<>();
        List<FileRequest> files = new ArrayList<>(zixiangs.size());
        List<ReviewExtension> extensions = new ArrayList<>(zixiangs.size());
 
        for (DmsWenjianFushenZhixingZixiangEntity zixiang : zixiangs) {
            String wenjianId = requireValue(zixiang.getWenjianId(), "文件复审执行子项wenjian_id");
            if (!wenjianIds.add(wenjianId)) {
                throw new DataException("最终结论为keyanyong的文件复审执行子项不能关联重复wenjian_id");
            }
            // Read the expected version without a resource lock. The validity service locks documents first.
            DmsWenjianZiyuanEntity ziyuan = ziyuanMapper.selectByTenantAndId(tenantId, wenjianId);
            if (ziyuan == null) {
                throw new DataException("未找到可延期的文件资源:wenjian_id=" + wenjianId);
            }
            UUID fileVersionId = parseFileVersionId(ziyuan.getWenjianZhongxinBanbenId(), wenjianId);
            if (!fileVersionIds.add(fileVersionId)) {
                throw new DataException("最终结论为keyanyong的文件复审执行子项不能关联重复文件版本");
            }
            Date effectiveTo = extendThreeYearsToEndOfDay(ziyuan.getYouxiaoQiZhi(), wenjianId);
            FileRequest file = new FileRequest();
            file.setFileVersionId(fileVersionId);
            file.setEffectiveTo(effectiveTo);
            files.add(file);
            extensions.add(new ReviewExtension(wenjianId, effectiveTo));
        }
 
        DmsFileExtendValidityForm form = new DmsFileExtendValidityForm();
        form.setSourceId("");
        form.setTaskId("");
        form.setFiles(files);
        fileValidityService.extendValidity(form);
        for (ReviewExtension extension : extensions) {
            if (ziyuanMapper.updateReviewExecutionExtension(tenantId, extension.wenjianId,
                    extension.effectiveTo, zhixingId, fushenRiqi) != 1) {
                throw new DataException("文件复审执行延期回写失败:wenjian_id=" + extension.wenjianId);
            }
        }
    }
 
    private void createRevisionDraft(List<DmsWenjianFushenZhixingZixiangEntity> zixiangs)
            throws DataException {
        UserInfo user = identity.requireUser();
        String tenantId = identity.tenantId(user);
        List<Map<String, Object>> details = new ArrayList<>(zixiangs.size());
        for (DmsWenjianFushenZhixingZixiangEntity zixiang : zixiangs) {
            String wenjianId = requireValue(zixiang.getWenjianId(), "文件复审执行子项wenjian_id");
            DmsWenjianZiyuanEntity ziyuan = requireResource(tenantId, wenjianId);
            Map<String, Object> detail = new HashMap<>();
            detail.put("wenjian_id", wenjianId);
            detail.put("wenjian_bianhao", valueOrEmpty(zixiang.getWenjianBianhao(), ziyuan.getWenjianBianhao()));
            detail.put("wenjian_mingcheng", valueOrEmpty(zixiang.getWenjianMingcheng(), ziyuan.getWenjianMingcheng()));
            detail.put("yuan_wenjian_banben_id", ziyuan.getDangqianBanbenId());
            detail.put("banbenhao", valueOrEmpty(zixiang.getBanbenhao(), null));
            detail.put("biangeng_neirong", Collections.singletonList("wenjian"));
            detail.put("biz_status", "available");
            details.add(detail);
        }
        Map<String, Object> formData = draftHeader(user, "文件复审结论为需修订,待申请人补充修订内容。");
        formData.put("tiyi_yuanyin_mudi", "wenjianFushen");
        formData.put("xiuding_shenqing_zixiang", details);
        saveDraft(REVISION_TEMPLATE_ID, user, formData, "文件修订申请");
    }
 
    private void createRevocationDraft(List<DmsWenjianFushenZhixingZixiangEntity> zixiangs)
            throws DataException {
        UserInfo user = identity.requireUser();
        String tenantId = identity.tenantId(user);
        List<Map<String, Object>> details = new ArrayList<>(zixiangs.size());
        for (DmsWenjianFushenZhixingZixiangEntity zixiang : zixiangs) {
            String wenjianId = requireValue(zixiang.getWenjianId(), "文件复审执行子项wenjian_id");
            DmsWenjianZiyuanEntity ziyuan = requireResource(tenantId, wenjianId);
            Map<String, Object> detail = new HashMap<>();
            detail.put("wenjian_id", wenjianId);
            detail.put("wenjian_banben_id", requireValue(ziyuan.getDangqianBanbenId(), "待撤销文件当前版本"));
            detail.put("wenjian_bianhao", valueOrEmpty(zixiang.getWenjianBianhao(), ziyuan.getWenjianBianhao()));
            detail.put("wenjian_mingcheng", valueOrEmpty(zixiang.getWenjianMingcheng(), ziyuan.getWenjianMingcheng()));
            detail.put("banben_hao", requireValue(ziyuan.getDangqianBanbenhao(), "待撤销文件当前版本号"));
            detail.put("tiyi_yuanyin_mudi", "文件复审结论为需撤销,待申请人补充撤销原因。");
            details.add(detail);
        }
        Map<String, Object> formData = draftHeader(user, null);
        formData.put("biz_status", "draft");
        formData.put("chexiao_wenjian_mingxi", details);
        saveDraft(REVOCATION_TEMPLATE_ID, user, formData, "文件撤销申请");
    }
 
    private Map<String, Object> draftHeader(UserInfo user, String summary) {
        Map<String, Object> formData = new HashMap<>();
        formData.put("tiyi_bumen_id", trimToNull(user.getOrganizeId()));
        formData.put("tiyi_ren_id", user.getUserId());
        formData.put("tiyi_riqi", new Date());
        if (summary != null) {
            formData.put("gaiban_neirong_gaishu", summary);
        }
        return formData;
    }
 
    private void saveDraft(String templateId, UserInfo user, Map<String, Object> formData, String flowName)
            throws DataException {
        FlowModel model = new FlowModel();
        model.setFlowId(resolveActiveFlowVersion(templateId));
        model.setStatus(0);
        model.setIsFlow(1);
        model.setUserInfo(user);
        model.setFormData(formData);
        ActionResult<?> result = taskApi.saveDraft(model);
        if (result == null || !ActionResultCode.SUCCESS.getCode().equals(result.getCode())) {
            throw new DataException("创建" + flowName + "暂存失败:" + (result == null ? "无返回" : result.getMsg()));
        }
    }
 
    private String resolveActiveFlowVersion(String templateId) throws DataException {
        List<TemplateJsonEntity> versions = workFlowApi.getFlowIdsByTemplate(templateId);
        if (versions != null) {
            for (TemplateJsonEntity version : versions) {
                if (version != null && TemplateJsonStatueEnum.START.getCode().equals(version.getState())
                        && trimToNull(version.getId()) != null) {
                    return version.getId().trim();
                }
            }
        }
        throw new DataException("未找到已启用的流程版本:template_id=" + templateId);
    }
 
    private DmsWenjianZiyuanEntity requireResource(String tenantId, String wenjianId) throws DataException {
        DmsWenjianZiyuanEntity ziyuan = ziyuanMapper.selectByTenantAndId(tenantId, wenjianId);
        if (ziyuan == null) {
            throw new DataException("未找到文件资源:wenjian_id=" + wenjianId);
        }
        return ziyuan;
    }
 
    private UUID parseFileVersionId(String value, String wenjianId) throws DataException {
        String fileVersionId = requireValue(value, "文件中心版本ID:wenjian_id=" + wenjianId);
        try {
            return UUID.fromString(fileVersionId);
        } catch (IllegalArgumentException exception) {
            throw new DataException("文件中心版本ID格式不正确:wenjian_id=" + wenjianId);
        }
    }
 
    private Date extendThreeYearsToEndOfDay(Date value, String wenjianId) throws DataException {
        if (value == null) {
            throw new DataException("文件资源youxiao_qi_zhi不能为空:wenjian_id=" + wenjianId);
        }
        return Date.from(value.toInstant().atZone(SHANGHAI_ZONE).plusYears(3).toInstant());
    }
 
    private String formatDate(Date value) {
        return new SimpleDateFormat(DATE_TIME_PATTERN).format(value);
    }
 
    private String valueOrEmpty(String preferred, String fallback) {
        String value = trimToNull(preferred);
        return value == null ? (fallback == null ? "" : fallback) : value;
    }
 
    private String trimToNull(String value) {
        String trimmed = value == null ? "" : value.trim();
        return trimmed.isEmpty() ? null : trimmed;
    }
 
    private DmsWenjianFushenZhixingEntity findZhixing(String zhixingId) throws DataException {
        LambdaQueryWrapper<DmsWenjianFushenZhixingEntity> query = new LambdaQueryWrapper<>();
        query.eq(DmsWenjianFushenZhixingEntity::getId, zhixingId)
                .isNull(DmsWenjianFushenZhixingEntity::getDeleteMark);
        DmsWenjianFushenZhixingEntity zhixing = zhixingMapper.selectOne(query);
        if (zhixing == null) {
            throw new DataException("未找到文件复审执行记录:f_id=" + zhixingId);
        }
        return zhixing;
    }
 
    private List<DmsWenjianFushenZhixingZixiangEntity> findZhixingZixiangs(String zhixingId)
            throws DataException {
        LambdaQueryWrapper<DmsWenjianFushenZhixingZixiangEntity> query = new LambdaQueryWrapper<>();
        query.eq(DmsWenjianFushenZhixingZixiangEntity::getForeignId, zhixingId)
                .isNull(DmsWenjianFushenZhixingZixiangEntity::getDeleteMark);
        List<DmsWenjianFushenZhixingZixiangEntity> zixiangs = zhixingZixiangMapper.selectList(query);
        if (zixiangs == null || zixiangs.isEmpty()) {
            throw new DataException("未找到文件复审执行子项:f_foreign_id=" + zhixingId);
        }
        return zixiangs;
    }
 
    private void writeBackZixiang(DmsWenjianFushenZhixingEntity zhixing,
            DmsWenjianFushenZhixingZixiangEntity zhixingZixiang, String fenpeiZixiangId) throws DataException {
        LambdaUpdateWrapper<DmsWenjianFushenJihuaFenpeiZixiangEntity> update = new LambdaUpdateWrapper<>();
        update.eq(DmsWenjianFushenJihuaFenpeiZixiangEntity::getId, fenpeiZixiangId)
                .isNull(DmsWenjianFushenJihuaFenpeiZixiangEntity::getDeleteMark)
                .set(DmsWenjianFushenJihuaFenpeiZixiangEntity::getHuiguRiqi, zhixing.getHuiguRiqi())
                .set(DmsWenjianFushenJihuaFenpeiZixiangEntity::getHuiguNeirongZongjie,
                        zhixingZixiang.getHuiguNeirongZongjie())
                .set(DmsWenjianFushenJihuaFenpeiZixiangEntity::getZuizhongJielun,
                        zhixingZixiang.getZuizhongJielun())
                .set(DmsWenjianFushenJihuaFenpeiZixiangEntity::getBizStatus, "yiwancheng");
        if (zixiangMapper.update(null, update) != 1) {
            throw new DataException("未找到可回写的文件复审计划分配子项:f_id=" + fenpeiZixiangId);
        }
    }
 
    private Set<String> resolveZixiangIds(List<DmsWenjianFushenZhixingBatchReturnForm> forms)
            throws DataException {
        if (forms == null || forms.isEmpty()) {
            throw new DataException("文件复审执行批量退回参数不能为空");
        }
        Set<String> ids = new LinkedHashSet<>();
        for (DmsWenjianFushenZhixingBatchReturnForm form : forms) {
            if (form == null) {
                throw new DataException("文件复审执行批量退回参数中不能包含空记录");
            }
            String id = requireValue(form.getFenpeiZixiangId(), "fenpei_zixiang_id");
            if (!ids.add(id)) {
                throw new DataException("文件复审执行批量退回参数中不能包含重复fenpei_zixiang_id");
            }
        }
        return ids;
    }
 
    private String requireValue(String value, String fieldName) throws DataException {
        String result = trimToNull(value);
        if (result == null) {
            throw new DataException(fieldName + "不能为空");
        }
        return result;
    }
 
    private static class ReviewExtension {
        private final String wenjianId;
        private final Date effectiveTo;
 
        private ReviewExtension(String wenjianId, Date effectiveTo) {
            this.wenjianId = wenjianId;
            this.effectiveTo = effectiveTo;
        }
    }
}