package jnpf.dmsService;
|
|
import jnpf.audit.sdk.annotation.AuditLog;
|
import jnpf.dmsEntity.storage.approval.DmsDisableRequest;
|
import jnpf.dmsEntity.storage.approval.DmsApprovedBatch;
|
import jnpf.dmsMapper.DmsApprovedBatchMapper;
|
import java.nio.charset.StandardCharsets;
|
import java.security.MessageDigest;
|
import java.security.NoSuchAlgorithmException;
|
import jnpf.dmsMapper.DmsApprovedDisableMapper;
|
import jnpf.dmsMapper.DmsWenjianZiyuanMapper;
|
import jnpf.exception.DataException;
|
import jnpf.flowable.WorkFlowApi;
|
import jnpf.flowable.entity.TaskEntity;
|
import lombok.RequiredArgsConstructor;
|
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import java.util.*;
|
|
/** Short local transactions only. Native forms are read, never created or replaced here. */
|
@Service
|
@RequiredArgsConstructor
|
public class DmsApprovedDisableService {
|
private final DmsApprovedDisableMapper mapper;
|
private final DmsWenjianZiyuanMapper resources;
|
private final WorkFlowApi workflow;
|
private final DmsApprovedBatchMapper batches;
|
@Value("${dms.approved-disable.enabled:false}") private boolean enabled;
|
|
@Transactional(rollbackFor = Exception.class)
|
@AuditLog(action="DMS_QUEUE_APPROVED_DISABLE", bizCodeExpr="#taskId")
|
public DmsApprovedBatch enqueue(String tenantId, String flowId, String taskId, String operation) {
|
requireEnabled(); requireOperation(operation);
|
TaskEntity task = approved(flowId, taskId);
|
List<DmsDisableRequest> rows = new ArrayList<>(mapper.nativeRows(tenantId, flowId, taskId, operation));
|
if (rows.isEmpty()) throw new DataException("未找到已批准的原生停用申请明细");
|
Set<String> files = new HashSet<>();
|
Set<String> applications = new HashSet<>();
|
rows.sort(Comparator.comparing(DmsDisableRequest::getSourceRowId));
|
for (DmsDisableRequest row : rows) {
|
applications.add(row.getApplicationId());
|
if (!files.add(row.getFileId()) || row.getFileId()==null || row.getBusinessVersionId()==null
|
|| row.getDocumentId()==null || row.getFileVersionId()==null || row.getFolderId()==null)
|
throw new DataException("停用明细重复或缺少真实文件/版本关联,请先发布并补全表单");
|
row.setId(UUID.randomUUID()); row.setApprovedAt(task.getEndTime());
|
// getInfoSubmit only returns workflow identity/status; nativeRows resolves its persisted actor.
|
if (row.getActorId()==null || row.getActorId().isBlank()) throw new DataException("批准任务缺少操作人");
|
row.setScheduledAt(DmsApprovedFileDates.disableScheduled(row.getRequestedAt(), task.getEndTime()));
|
}
|
if (applications.size()!=1) throw new DataException("流程任务关联了多个停用申请");
|
String auditType = auditType(operation);
|
String digest = digest(rows);
|
DmsApprovedBatch existing = batches.find(tenantId, taskId, auditType);
|
if (existing != null) {
|
if (!Objects.equals(existing.getSourceDigest(), digest)
|
|| !Objects.equals(existing.getApprovedAt(), task.getEndTime())
|
|| !Objects.equals(existing.getFlowId(), flowId))
|
throw new DataException("批准后的停用来源已改变,停止自动处理");
|
return existing;
|
}
|
DmsApprovedBatch batch = new DmsApprovedBatch();
|
batch.setId(UUID.randomUUID()); batch.setTenantId(tenantId); batch.setFlowId(flowId);
|
batch.setTaskId(taskId); batch.setAuditType(auditType); batch.setAuditId(rows.get(0).getApplicationId());
|
batch.setSourceDigest(digest); batch.setActorId(rows.get(0).getActorId());
|
batch.setApprovedAt(task.getEndTime()); batch.setStatus("WAITING");
|
batch.setScheduledAt(rows.stream().map(DmsDisableRequest::getScheduledAt).min(Date::compareTo).orElseThrow());
|
batches.insertBatch(batch);
|
for (DmsDisableRequest row : rows) {
|
row.setBatchId(batch.getId());
|
resources.selectByIdForUpdate(tenantId, row.getFileId());
|
validateTarget(row);
|
mapper.enqueue(row);
|
}
|
return batch;
|
}
|
|
// Length prefixes preserve nulls and separators in native IDs; row order is canonical.
|
private static String digest(List<DmsDisableRequest> rows) {
|
StringBuilder source = new StringBuilder();
|
for (DmsDisableRequest row : rows) {
|
for (Object value : Arrays.asList(row.getSourceRowId(), row.getApplicationId(), row.getFileId(),
|
row.getBusinessVersionId(), row.getDocumentId(), row.getFileVersionId(), row.getFolderId(),
|
row.getSourceFileId(), row.getSourceVersionId(),
|
row.getRequestedAt() == null ? null : row.getRequestedAt().getTime())) {
|
String text = value == null ? null : value.toString();
|
source.append(text == null ? -1 : text.length()).append(':');
|
if (text != null) source.append(text);
|
}
|
}
|
try { return HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(source.toString().getBytes(StandardCharsets.UTF_8))); }
|
catch (NoSuchAlgorithmException impossible) { throw new IllegalStateException(impossible); }
|
}
|
|
static String auditType(String operation) {
|
requireOperation(operation);
|
return "yichexiao".equals(operation) ? "chexiao_shenqing" : "shixiao_shenqing";
|
}
|
|
@Transactional(rollbackFor = Exception.class)
|
public void refreshProgress(DmsApprovedBatch batch) {
|
requireEnabled();
|
if (mapper.refreshProgress(batch) != 1) throw new DataException("统一任务进度更新失败或租约已失效");
|
}
|
|
private void validateTarget(DmsDisableRequest row) {
|
if (mapper.validateTarget(row)!=1) throw new DataException("业务文件版本或有效状态已改变,不能登记原停用申请");
|
if ("yishixiao".equals(row.getOperation()) && mapper.candidateExists(row)<1)
|
throw new DataException("该文件不是已生效新增文件检出的同名旧文件");
|
}
|
private TaskEntity approved(String flowId,String taskId) {
|
TaskEntity task=workflow.getInfoSubmit(taskId);
|
if (task==null || !taskId.equals(task.getId()) || !Integer.valueOf(2).equals(task.getStatus())
|
|| task.getEndTime()==null || (!flowId.equals(task.getFlowId()) && !flowId.equals(task.getTemplateId())))
|
throw new DataException("文件停用必须先通过真实工作流审批");
|
return task;
|
}
|
static void requireOperation(String operation) {
|
if (!"yichexiao".equals(operation) && !"yishixiao".equals(operation)) throw new DataException("文件停用类型无效");
|
}
|
private void requireEnabled() {
|
if (!enabled) throw new DataException("停用推送尚未启用:需先完成技术迁移和原生流程接入");
|
}
|
}
|