package jnpf.dmsService; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import jnpf.audit.sdk.annotation.AuditLog; import jnpf.base.ActionResult; import jnpf.base.ActionResultCode; import jnpf.dms.DmsResourceApi; import jnpf.dmsEntity.DmsWenjianBanbenEntity; import jnpf.dmsEntity.fenfa.DmsWenjianDayinRenwuEntity; import jnpf.dmsEntity.permission.form.DmsPersonalFolderFileForm; import jnpf.dmsEntity.print.DmsWenjianDayinShenqingDetailEntity; import jnpf.dmsEntity.print.DmsWenjianDayinShenqingEntity; import jnpf.dmsEntity.print.form.DmsWorkflowPrintFilesGrantForm; import jnpf.dmsEntity.print.vo.DmsWorkflowPrintFilesGrantResultVO; import jnpf.dmsMapper.DmsWenjianBanbenMapper; import jnpf.dmsMapper.DmsWenjianDayinShenqingDetailMapper; import jnpf.dmsMapper.DmsWenjianDayinShenqingMapper; import jnpf.dmsMapper.DmsWenjianFenfaMapper; import jnpf.exception.DataException; import jnpf.util.RandomUtil; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.sql.Timestamp; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.Date; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.function.Function; import java.util.stream.Collectors; /** 文件辅助管理中的打印授权业务。 */ @Service @RequiredArgsConstructor public class DmsWenjianFuzhuGuanliDayinService { private static final String FLOWABLE_SERVICE_ID = "jnpf-flowable"; private static final String PRINT_REQUEST_SOURCE = "wenjian_dayin"; private static final String DOCUMENT_CATEGORY = "[\"wenjian\"]"; private static final String PENDING_PRINT = "daidayin"; private static final ZoneId BUSINESS_ZONE = ZoneId.of("Asia/Shanghai"); private final DmsWenjianDayinShenqingMapper shenqingMapper; private final DmsWenjianDayinShenqingDetailMapper zixiangMapper; private final DmsWenjianBanbenMapper banbenMapper; private final DmsWenjianFenfaMapper fenfaMapper; private final DmsWenjianDayinHaomaService numbers; private final DmsResourceApi dmsResourceApi; /** 根据流程申请为申请人永久授予获批文件版本的打印权限。 */ @Transactional(rollbackFor = Exception.class) @AuditLog(action = "CREATE_PRINT_TASKS_FROM_PRINT_REQUEST", recordSuccess = false, label = "文件打印申请生成打印任务", bizModule = "DMS", bizType = "DMS_PRINT_TASK", bizCodeExpr = "#flowTaskId", targetTable = "dms_wenjian_dayin_renwu", bizCodeRequired = false) public DmsWorkflowPrintFilesGrantResultVO dayinWenjianShouquan( String flowId, String flowTaskId) { String normalizedFlowId = requireValue(flowId, "流程ID(flow_id)"); String normalizedFlowTaskId = requireValue(flowTaskId, "流程任务ID(flow_task_id)"); DmsWenjianDayinShenqingEntity shenqing = findShenqing( normalizedFlowId, normalizedFlowTaskId); List zixiangs = findZixiangs(shenqing.getId()); Set banbenIds = extractBanbenIds(zixiangs); List banbens = findBanbens(banbenIds); DmsWorkflowPrintFilesGrantForm grantForm = buildGrantForm(shenqing, banbens); // 全部明细先校验、构造完成,再调用跨服务授权,避免非法数量留下授权副作用。 List tasks = buildPrintTasks(shenqing, zixiangs, banbens); ActionResult response; try { response = dmsResourceApi.grantWorkflowPrintFiles( FLOWABLE_SERVICE_ID, grantForm); } catch (RuntimeException exception) { throw new DataException("调用DMS文件打印授权接口失败"); } if (response == null || !ActionResultCode.SUCCESS.getCode().equals(response.getCode()) || response.getData() == null) { String message = response == null ? null : response.getMsg(); throw new DataException(isBlank(message) ? "打印文件授权失败或响应数据不完整" : message); } tasks.sort(java.util.Comparator.comparing(DmsWenjianDayinRenwuEntity::getWenjianBianhao).thenComparing(DmsWenjianDayinRenwuEntity::getBanbenhao).thenComparing(DmsWenjianDayinRenwuEntity::getId)); tasks.forEach(task -> { if (fenfaMapper.insertPrintTaskIfAbsent(task) == 1) numbers.ensureTaskNumbers(task.getTenantId(), task, 0); }); return response.getData(); } /** 根据申请明细及获批版本快照构造任务,在任何授权或写入前校验整批数据。 */ private List buildPrintTasks( DmsWenjianDayinShenqingEntity shenqing, List zixiangs, List banbens) { Map versions = banbens.stream() .collect(Collectors.toMap(DmsWenjianBanbenEntity::getId, Function.identity())); List tasks = new ArrayList<>(zixiangs.size()); Date now = new Date(); String tenantId = shenqing.getTenantId(); String requestId = shenqing.getId(); String applicantUserId = requireValue( shenqing.getApplicantUserId(), "文件打印申请人(shenqing_ren)"); for (DmsWenjianDayinShenqingDetailEntity zixiang : zixiangs) { DmsWenjianDayinRenwuEntity task = new DmsWenjianDayinRenwuEntity(); task.setId(RandomUtil.uuId()); task.setTenantId(tenantId); task.setLaiyuanLeixing(PRINT_REQUEST_SOURCE); task.setLaiyuanDanjuId(requestId); task.setLaiyuanMingxiId(zixiang.getId()); task.setWenjianId(zixiang.getFileReferenceId()); task.setWenjianBianhao(zixiang.getFileNumber()); task.setWenjianMingcheng(zixiang.getFileName()); task.setBanbenhao(zixiang.getVersionLabel()); task.setShenheWanchengRiqi(shenqing.getFileAdministratorDate()); task.setShengxiaoRiqi(parseEffectiveDate(zixiang.getEffectiveDate())); task.setWenjianLeibie(DOCUMENT_CATEGORY); task.setDayinFenshu(parsePrintCopies(zixiang.getPrintCopies(), zixiang.getId())); task.setBeizhuShuoming(zixiang.getPurpose()); task.setBizStatus(PENDING_PRINT); task.setCreatorUserId(applicantUserId); task.setCreatorTime(now); task.setJieshouBumenId(shenqing.getShenqingrenBumen()); String controlled = requireValue(versions.get(zixiang.getBanbenId()).getShifouShoukong(), "申请文件版本是否受控"); if (!"yes".equals(controlled) && !"no".equals(controlled)) { throw new DataException("申请文件版本是否受控必须为yes或no:" + zixiang.getBanbenId()); } task.setShifouShoukong(controlled); tasks.add(task); } return tasks; } /** 将在线表单保存的日期文本转换为打印任务时间。 */ private Date parseEffectiveDate(String value) { if (value == null || value.isEmpty()) { return null; } String normalized = requireValue(value, "文件生效日期(wenjian_shengxiao_riqi)"); try { if (normalized.matches("\\d{13}")) { return new Date(Long.parseLong(normalized)); } if (normalized.length() == 10) { return Date.from(LocalDate.parse(normalized).atStartOfDay(BUSINESS_ZONE).toInstant()); } if (normalized.endsWith("Z")) { return Date.from(Instant.parse(normalized)); } return Timestamp.valueOf(LocalDateTime.parse( normalized.replace(' ', 'T'))); } catch (DateTimeParseException | IllegalArgumentException exception) { throw new DataException("文件生效日期格式不正确:" + normalized); } } /** 解析并校验打印份数。 */ private int parsePrintCopies(String value, String detailId) { String normalized = requireValue(value, "打印数量(dayin_shuliang)"); try { if (!normalized.matches("[1-9][0-9]*")) { throw new NumberFormatException(); } int copies = Integer.parseInt(normalized); if (copies <= 0) { throw new NumberFormatException(); } return copies; } catch (NumberFormatException exception) { throw new DataException("打印数量必须是正整数:明细ID=" + detailId); } } /** 查询流程唯一对应的未删除打印申请。 */ private DmsWenjianDayinShenqingEntity findShenqing(String flowId, String flowTaskId) { LambdaQueryWrapper query = new LambdaQueryWrapper<>(); query.eq(DmsWenjianDayinShenqingEntity::getFlowId, flowId) .eq(DmsWenjianDayinShenqingEntity::getFlowTaskId, flowTaskId) .isNull(DmsWenjianDayinShenqingEntity::getDeleteMark); List rows = shenqingMapper.selectList(query); if (rows.isEmpty()) { throw new DataException("未找到文件打印申请:flow_id=" + flowId + ", flow_task_id=" + flowTaskId); } if (rows.size() > 1) { throw new DataException("flow_id与flow_task_id关联了多条文件打印申请,无法授权"); } return rows.get(0); } /** 根据申请主键查询未删除的打印申请明细。 */ private List findZixiangs(String shenqingId) { LambdaQueryWrapper query = new LambdaQueryWrapper<>(); query.eq(DmsWenjianDayinShenqingDetailEntity::getRequestId, shenqingId) .isNull(DmsWenjianDayinShenqingDetailEntity::getDeleteMark); List rows = zixiangMapper.selectList(query); if (rows.isEmpty()) { throw new DataException("文件打印申请未关联打印明细,无法授权"); } return rows; } /** 从打印明细提取业务版本 ID,并保持原顺序去重。 */ private Set extractBanbenIds(List zixiangs) { Set ids = new LinkedHashSet<>(); for (DmsWenjianDayinShenqingDetailEntity zixiang : zixiangs) { ids.add(requireValue(zixiang.getBanbenId(), "文件打印明细版本ID(banben_id)")); } return ids; } /** 按明细版本 ID 查询未删除的业务文件版本。 */ private List findBanbens(Set banbenIds) { LambdaQueryWrapper query = new LambdaQueryWrapper<>(); query.in(DmsWenjianBanbenEntity::getId, banbenIds) .isNull(DmsWenjianBanbenEntity::getDeleteMark); List rows = banbenMapper.selectList(query); if (rows.size() != banbenIds.size()) { throw new DataException("部分打印明细未找到对应的有效文件版本"); } return rows; } /** 组装永久有效的文件打印授权请求。 */ private DmsWorkflowPrintFilesGrantForm buildGrantForm( DmsWenjianDayinShenqingEntity shenqing, List banbens) { List files = new ArrayList<>(banbens.size()); for (DmsWenjianBanbenEntity banben : banbens) { DmsPersonalFolderFileForm.FileRelation relation = new DmsPersonalFolderFileForm.FileRelation(); try { relation.setDocumentId(UUID.fromString(banben.getWenjianZhongxinWenjianId())); relation.setFileVersionId(UUID.fromString(banben.getWenjianZhongxinBanbenId())); } catch (IllegalArgumentException exception) { throw new DataException("文件中心文件ID或版本ID格式不正确:banben_id=" + banben.getId()); } relation.setValidTo(null); files.add(relation); } DmsWorkflowPrintFilesGrantForm form = new DmsWorkflowPrintFilesGrantForm(); form.setUserId(requireValue( shenqing.getApplicantUserId(), "文件打印申请人(shenqing_ren)")); form.setFiles(files); return form; } private String requireValue(String value, String fieldName) { if (isBlank(value)) { throw new DataException(fieldName + "不能为空"); } return value.trim(); } private boolean isBlank(String value) { return value == null || value.trim().isEmpty(); } }