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 forms) throws DataException { Set zixiangIds = resolveZixiangIds(forms); LambdaUpdateWrapper 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 zhixingZixiangs = findZhixingZixiangs(zhixingId); Set 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 zixiangs) throws DataException { Map> 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> 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 zixiangs) throws DataException { UserInfo user = identity.requireUser(); String tenantId = identity.tenantId(user); String fushenRiqi = formatDate(new Date()); Set wenjianIds = new LinkedHashSet<>(); Set fileVersionIds = new LinkedHashSet<>(); List files = new ArrayList<>(zixiangs.size()); List 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 zixiangs) throws DataException { UserInfo user = identity.requireUser(); String tenantId = identity.tenantId(user); List> details = new ArrayList<>(zixiangs.size()); for (DmsWenjianFushenZhixingZixiangEntity zixiang : zixiangs) { String wenjianId = requireValue(zixiang.getWenjianId(), "文件复审执行子项wenjian_id"); DmsWenjianZiyuanEntity ziyuan = requireResource(tenantId, wenjianId); Map 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 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 zixiangs) throws DataException { UserInfo user = identity.requireUser(); String tenantId = identity.tenantId(user); List> details = new ArrayList<>(zixiangs.size()); for (DmsWenjianFushenZhixingZixiangEntity zixiang : zixiangs) { String wenjianId = requireValue(zixiang.getWenjianId(), "文件复审执行子项wenjian_id"); DmsWenjianZiyuanEntity ziyuan = requireResource(tenantId, wenjianId); Map 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 formData = draftHeader(user, null); formData.put("biz_status", "draft"); formData.put("chexiao_wenjian_mingxi", details); saveDraft(REVOCATION_TEMPLATE_ID, user, formData, "文件撤销申请"); } private Map draftHeader(UserInfo user, String summary) { Map 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 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 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 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 findZhixingZixiangs(String zhixingId) throws DataException { LambdaQueryWrapper query = new LambdaQueryWrapper<>(); query.eq(DmsWenjianFushenZhixingZixiangEntity::getForeignId, zhixingId) .isNull(DmsWenjianFushenZhixingZixiangEntity::getDeleteMark); List 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 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 resolveZixiangIds(List forms) throws DataException { if (forms == null || forms.isEmpty()) { throw new DataException("文件复审执行批量退回参数不能为空"); } Set 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; } } }