package jnpf.dmsService; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import jnpf.dmsIntegration.audit.DmsBusinessChangeAudit; import jnpf.dmsEntity.kanwu.DmsXiudingKanwuRow; import jnpf.dmsMapper.DmsXiudingKanwuMapper; import jnpf.exception.DataException; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.util.*; /** Validates native handling rows and applies only the effective-revision side effect. */ @Service @RequiredArgsConstructor public class DmsXiudingKanwuService { private static final ObjectMapper JSON = new ObjectMapper(); private final DmsXiudingKanwuMapper mapper; private final DmsBusinessChangeAudit audit; @Transactional(propagation = Propagation.MANDATORY, rollbackFor = Exception.class) public List loadAndValidate(String tenantId, String auditId, String fileId, String baseVersionId) { List handled = new ArrayList<>(mapper.selectHandlingForUpdate(tenantId, auditId)); List approved = mapper.selectApprovedForUpdate(tenantId, fileId, baseVersionId); Map sources = new HashMap<>(); for (DmsXiudingKanwuRow row : approved) { if (sources.put(row.getKanwuZixiangId(), row) != null) throw new DataException("勘误来源重复"); } if (handled.size() != sources.size()) throw new DataException("已批准勘误已变化,请退回起草人逐条处理后重新批准"); Set seen = new HashSet<>(); for (DmsXiudingKanwuRow row : handled) { DmsXiudingKanwuRow source = sources.get(row.getKanwuZixiangId()); if (source == null || !seen.add(row.getKanwuZixiangId()) || !snapshot(row).equals(snapshot(source)) || !files(row.getFujian()).equals(files(source.getFujian()))) { throw new DataException("勘误来源或内容已变化,请重新核对并批准"); } if (!Set.of("yixiugai_queren", "bucaina").contains(normalize(row.getChuliJielun())) || blank(row.getChuliYuanyin()) || blank(row.getChuliRenId()) || row.getChuliShijian() == null) { throw new DataException("每条勘误必须填写处理结论、原因、处理人和处理时间"); } row.setAlreadyProcessed("yes".equals(source.getShifouYichuli())); } handled.sort(Comparator.comparing(DmsXiudingKanwuRow::getKanwuZixiangId)); return handled; } /** Must share the existing applyApproved transaction: a failure rolls back settlement too. */ @Transactional(propagation = Propagation.MANDATORY, rollbackFor = Exception.class) public void complete(String tenantId, String auditId, List verifiedRows) { for (DmsXiudingKanwuRow row : verifiedRows) { // Another effective revision may have processed the same record erratum already. // Preserve its first operator/time; the new decision stays in its native audit row. if (row.isAlreadyProcessed()) continue; Map before = processingState(tenantId, row.getKanwuZixiangId()); if (mapper.markProcessed(tenantId, row) != 1) { throw new DataException("勘误台账回写失败,修订生效业务回写已回滚"); } Map after = processingState(tenantId, row.getKanwuZixiangId()); audit.record(tenantId, "DMS_REVISION_ERRATA_PROCESSED", "修订生效后回写勘误处理状态", "dms_wenjian_kanwu_shenqing_zixiang", row.getKanwuZixiangId(), before, after, row.getChuliYuanyin(), DmsBusinessChangeAudit.values( "xiuding_shenhe_id", auditId, "xiuding_kanwu_id", row.getId(), "kanwu_shenqing_id", row.getKanwuShenqingId(), "wenjian_id", row.getWenjianId(), "banben_id", row.getBanbenId(), "wenjian_bianhao", row.getWenjianBianhao(), "wenjian_mingcheng", row.getWenjianMingcheng(), "banbenhao", row.getBanbenhao(), "chuli_jielun", row.getChuliJielun(), "chuli_ren_id", row.getChuliRenId(), "chuli_shijian", row.getChuliShijian())); } } private Map processingState(String tenantId, String id) { Map state = mapper.selectProcessingState(tenantId, id); if (state == null) throw new DataException("勘误台账状态读取失败,修订生效业务回写已回滚"); // 保留真实空值;处理决定时间与数据库实际回写时间分别记录。 return DmsBusinessChangeAudit.values("shifou_yichuli", state.get("shifou_yichuli"), "chuliren", state.get("chuliren"), "chuli_shijian", state.get("chuli_shijian")); } private static List snapshot(DmsXiudingKanwuRow row) { return Arrays.asList(normalize(row.getKanwuShenqingId()), normalize(row.getWenjianId()), normalize(row.getBanbenId()), normalize(row.getWenjianBianhao()), normalize(row.getWenjianMingcheng()), normalize(row.getBanbenhao()), normalize(row.getKanwuWeizhi()), normalize(row.getKanwuNeirong()), normalize(row.getXiugaiJianyi()), normalize(row.getKanwuYuanyin())); } private static JsonNode files(String value) { try { JsonNode node = JSON.readTree(blank(value) ? "[]" : value); if (!node.isArray()) throw new DataException("勘误附件格式无效"); return node; } catch (DataException e) { throw e; } catch (Exception e) { throw new DataException("勘误附件格式无效"); } } private static String normalize(String value) { return value == null ? "" : value; } private static boolean blank(String value) { return value == null || value.isBlank(); } }