package jnpf.dmsService; import jnpf.dmsEntity.storage.*; import jnpf.dmsMapper.DmsRevisionEditMapper; import jnpf.exception.DataException; import jnpf.util.JsonUtil; import jnpf.util.TicketUtil; import org.springframework.beans.factory.annotation.Autowired; import java.util.function.Function; import java.util.function.BiFunction; import org.springframework.stereotype.Service; import java.util.*; /** 核验本人有效修订来源,供原生保存与副本编辑使用,不修改原版本或业务单据。 */ @Service public class DmsRevisionEditService { private static final com.fasterxml.jackson.databind.ObjectMapper SOURCE_JSON = new com.fasterxml.jackson.databind.ObjectMapper(); private final DmsRevisionEditMapper mapper; private final Function parseTicket; private final BiFunction issueTicket; @Autowired public DmsRevisionEditService(DmsRevisionEditMapper mapper) { this(mapper, TicketUtil::parseTicket, TicketUtil::createTicket); } DmsRevisionEditService(DmsRevisionEditMapper mapper, Function parseTicket, BiFunction issueTicket) { this.mapper = mapper; this.parseTicket = parseTicket; this.issueTicket = issueTicket; } public List> prepare(DmsRevisionEditRequest request, String tenant, String owner) { List sources=mapper.sources(tenant, owner, request.getSourceItemId(), request.getModelId()); if (sources.isEmpty()) throw denied(); List> result=new ArrayList<>(); for (DmsRevisionEditSource source:sources) { List> files=new ArrayList<>(); for (Map original:files(source.getFiles())) { Map file=canonical(original); if ("wenjian".equals(source.getField()) && Objects.toString(source.getParentField(), "").isEmpty() && original.get("laiyuan_muban_id") instanceof String && !((String) original.get("laiyuan_muban_id")).isBlank()) file.put("laiyuan_muban_id", original.get("laiyuan_muban_id")); // 来源凭据也用于原生暂存,不能只覆盖可在线编辑的 DOC/DOCX。 if (hasFileId(file)) { Map proof=new LinkedHashMap<>(); proof.put("purpose","dms-revision-edit"); proof.put("tenantId",tenant); proof.put("ownerId",owner); proof.put("modelId",request.getModelId()); proof.put("sourceItemId",request.getSourceItemId()); proof.put("parentField",source.getParentField()); proof.put("field",source.getField()); proof.put("versionId",source.getVersionId()); proof.put("originFileId",file.get("fileId")); proof.put("expiresAt",System.currentTimeMillis()+14400000L); proof.put("file",new LinkedHashMap<>(file)); file.put("revisionEditReceipt",issueTicket.apply(JsonUtil.getObjectToString(proof),14400L)); } files.add(file); } Map row=new LinkedHashMap<>(); row.put("versionId",source.getVersionId()); row.put("parentField",source.getParentField()); row.put("field",source.getField()); row.put("files",files); result.add(row); } return result; } public Map verify(DmsRevisionEditRequest request) { String raw=request.getReceipt()==null?null:parseTicket.apply(request.getReceipt()); Map proof=raw==null?Collections.emptyMap():JsonUtil.getJsonToBean(raw,Map.class); if (!"dms-revision-edit".equals(proof.get("purpose")) || !Objects.equals(request.getTenantId(),proof.get("tenantId")) || !Objects.equals(request.getOwnerId(),proof.get("ownerId")) || !Objects.equals(request.getModelId(),proof.get("modelId")) || !Objects.equals(request.getField(),proof.get("field")) || !Objects.toString(request.getParentField(),"").equals(Objects.toString(proof.get("parentField"),"")) || !(proof.get("expiresAt") instanceof Number) || ((Number)proof.get("expiresAt")).longValue()<=System.currentTimeMillis() || !(proof.get("file") instanceof Map) || !hasFileId((Map)proof.get("file"))) throw denied(); Map original=mapper.sources(request.getTenantId(),request.getOwnerId(),Objects.toString(proof.get("sourceItemId"),""),request.getModelId()) .stream().filter(source -> Objects.equals(source.getVersionId(),proof.get("versionId")) && Objects.equals(source.getField(),proof.get("field")) && Objects.equals(source.getParentField(),proof.get("parentField")) ).flatMap(source -> files(source.getFiles()).stream()) .filter(file -> Objects.equals(file.get("fileId"),proof.get("originFileId"))).findFirst().orElse(null); if (original == null) throw denied(); // 历史来源凭据不代表模板。未编辑附件保留数据库原值,不能用五字段副本覆盖沿用数据。 // 使用 JSON 文本跨服务携带,避免空属性(如 fileVersionId:null)被响应序列化省略。 proof.remove("sourceFileJson"); if (Objects.equals(((Map)proof.get("file")).get("fileId"), proof.get("originFileId"))) { try { proof.put("sourceFileJson", SOURCE_JSON.writeValueAsString(original)); } catch (com.fasterxml.jackson.core.JsonProcessingException e) { throw denied(); } } return proof; } private static boolean hasFileId(Map file) { return file.get("fileId") instanceof String && !((String)file.get("fileId")).isBlank(); } private static Map canonical(Map original) { Map result=new LinkedHashMap<>(); for(String key:List.of("fileId","name","fileSize","fileExtension","url")) result.put(key,original.get(key)); return result; } private static List> files(String json) { if (json==null || json.isBlank()) return Collections.emptyList(); return (List)JsonUtil.getJsonToList(json,Map.class); } private static DataException denied() { return new DataException("修订来源已失效或不属于本人,请重新选择有效的修订申请"); } }