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<DmsXiudingKanwuRow> loadAndValidate(String tenantId, String auditId, String fileId, String baseVersionId) {
|
List<DmsXiudingKanwuRow> handled = new ArrayList<>(mapper.selectHandlingForUpdate(tenantId, auditId));
|
List<DmsXiudingKanwuRow> approved = mapper.selectApprovedForUpdate(tenantId, fileId, baseVersionId);
|
Map<String, DmsXiudingKanwuRow> 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<String> 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<DmsXiudingKanwuRow> 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<String, Object> before = processingState(tenantId, row.getKanwuZixiangId());
|
if (mapper.markProcessed(tenantId, row) != 1) {
|
throw new DataException("勘误台账回写失败,修订生效业务回写已回滚");
|
}
|
Map<String, Object> 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<String, Object> processingState(String tenantId, String id) {
|
Map<String, Object> 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<String> 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(); }
|
}
|