刘光辉
4 小时以前 06f314545ac3514d7ce28939920866ae8188d31c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package jnpf.dmsService.center;
 
import jnpf.dmsIntegration.audit.DmsBusinessChangeAudit;
import static jnpf.dmsIntegration.audit.DmsBusinessChangeAudit.values;
 
import jnpf.dmsEntity.permission.entity.DmsFileVersionEntity;
 
import jnpf.dmsEntity.center.DmsCenterActivation;
import jnpf.dmsEntity.permission.entity.DmsDocumentEntity;
import jnpf.dmsMapper.*;
import jnpf.dmsService.search.DmsSearchIndexJobService;
import jnpf.exception.DataException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
 
/** Atomic center state transitions; no native form, workflow API or business queue dependency. */
@Service
@RequiredArgsConstructor
public class DmsCenterActivationService {
    private final DmsBusinessChangeAudit businessAudit;
    private final DmsDocumentMapper documents;
    private final DmsFileVersionMapper versions;
    private final DmsVersionStateMapper states;
    private final DmsSearchIndexJobService search;
 
    @Transactional(rollbackFor = Exception.class)
    public void activate(String tenantId, String actorId, List<DmsCenterActivation> targets) {
        if (tenantId == null || tenantId.isBlank() || actorId == null || actorId.isBlank()
                || targets == null || targets.isEmpty()) throw new DataException("中心生效请求不完整");
        Set<UUID> seen = new HashSet<>();
        for (DmsCenterActivation target : targets) {
            if (target == null || target.documentId() == null || target.fileVersionId() == null
                    || !Set.of("CREATE", "REVISE", "REUSE", "RETIRE").contains(target.operation())
                    || !seen.add(target.documentId())) throw new DataException("中心生效目标无效或重复");
        }
        List<DmsCenterActivation> ordered = targets.stream().sorted(Comparator.comparing(t -> t.documentId().toString())).toList();
        Map<UUID, DmsDocumentEntity> locked = new HashMap<>();
        for (DmsCenterActivation target : ordered) {
            DmsDocumentEntity document = documents.selectByTenantAndIdForUpdate(tenantId, target.documentId());
            if (document == null)
                throw new DataException("接收文件不存在");
            locked.put(target.documentId(), document);
        }
        for (DmsCenterActivation target : ordered) {
            if (!"RETIRE".equals(target.operation())) continue;
            UUID mainId = locked.get(target.documentId()).getMainDocumentId();
            if (mainId == null || !locked.containsKey(mainId)
                    || locked.get(mainId).getMainDocumentId() != null
                    || targets.stream().noneMatch(t -> mainId.equals(t.documentId())
                        && Set.of("REUSE", "REVISE").contains(t.operation())))
                throw new DataException("只能将本次修订主文件目录中的记录转入历史");
        }
        for (DmsCenterActivation target : ordered) {
            DmsFileVersionEntity current = versions.selectCurrentEffective(tenantId, target.documentId());
            if ("RETIRE".equals(target.operation())) {
                if (current == null || !target.fileVersionId().equals(current.getId())
                        || !target.fileVersionId().equals(target.baseFileVersionId()))
                    throw new DataException("移除记录已不是原批准的现行版本");
                var before = states.selectAuditState(tenantId, target.fileVersionId());
                if (states.retireCurrent(tenantId, target.documentId(), target.fileVersionId(), actorId) != 1)
                    throw new DataException("移除记录转入历史失败");
                recordState(tenantId, actorId, target.documentId(), target.fileVersionId(), before);
                continue;
            }
            if ("REUSE".equals(target.operation())) {
                if (current == null || !target.fileVersionId().equals(current.getId()))
                    throw new DataException("沿用版本已不是现行版本");
                continue;
            }
            DmsFileVersionEntity version = versions.selectByTenantAndId(tenantId, target.fileVersionId());
            if (version == null || !target.documentId().equals(version.getDocumentId()))
                throw new DataException("生效版本不属于目标文档");
            if ("REVISE".equals(target.operation())) {
                if (current == null || !Objects.equals(target.baseFileVersionId(), current.getId()))
                    throw new DataException("修订来源版本已不是现行版本");
                var before = states.selectAuditState(tenantId, current.getId());
                if (states.supersedeCurrent(tenantId, target.documentId(), current.getId(),
                        version.getVersionLabel(), actorId) != 1) throw new DataException("现行版本切换失败");
                recordState(tenantId, actorId, target.documentId(), current.getId(), before);
            } else if (current != null) throw new DataException("新文件已存在有效版本");
            var before = states.selectAuditState(tenantId, target.fileVersionId());
            if (states.activateApproved(tenantId, target.fileVersionId(), actorId) != 1)
                throw new DataException("批准文件尚未到生效日期或已失效");
            recordState(tenantId, actorId, target.documentId(), target.fileVersionId(), before);
            search.enqueueContentUpsert(tenantId, target.documentId(), target.fileVersionId(), target.checksumSha256());
        }
    }
 
    private void recordState(String tenantId, String approvalActorId, UUID documentId, UUID versionId,
                             jnpf.dmsIntegration.audit.DmsVersionStateAuditRow before) {
        var after = states.selectAuditState(tenantId, versionId);
        if (before == null || after == null) throw new DataException("文件状态审计快照缺失");
        businessAudit.record(tenantId, "DMS_VERSION_STATE_CHANGED", "文件版本效力变更",
                "dms_version_states", versionId.toString(), state(before), state(after), after.getStatusReason(),
                values("document_id", documentId, "file_version_id", versionId, "approval_actor_id", approvalActorId));
    }
 
    private Map<String, Object> state(jnpf.dmsIntegration.audit.DmsVersionStateAuditRow value) {
        return values("lifecycle_status", value.getLifecycleStatus(), "effective_from", value.getEffectiveFrom(),
                "effective_to", value.getEffectiveTo(), "status_reason", value.getStatusReason());
    }
}