package jnpf.onlinedev.service;
|
|
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
|
import jnpf.base.entity.VisualdevEntity;
|
import jnpf.base.mapper.UploadEditDraftMapper;
|
import jnpf.base.model.flow.DataModel;
|
import jnpf.base.model.flow.FlowFormDataModel;
|
import jnpf.base.model.uploadedit.UploadEditDraft;
|
import jnpf.base.service.UploadEditDraftService;
|
import jnpf.base.service.UploadFilePermissionQuery;
|
import jnpf.base.service.VisualdevService;
|
import jnpf.base.util.FlowFormDataUtil;
|
import jnpf.base.util.UploadEditStateMachine;
|
import jnpf.base.util.UploadEditWorkingCopy;
|
import jnpf.exception.DataException;
|
import jnpf.exception.WorkFlowException;
|
import jnpf.flowable.model.filepermission.FilePermissionView;
|
import jnpf.flowable.model.filepermission.WorkflowFileCheckpoint;
|
import jnpf.onlinedev.model.OnlineInfoModel;
|
import jnpf.onlinedev.util.FlowFormCustomUtils;
|
import jnpf.util.JsonUtil;
|
import jnpf.util.UserProvider;
|
import lombok.RequiredArgsConstructor;
|
import org.springframework.stereotype.Service;
|
|
import java.util.*;
|
import java.util.function.Consumer;
|
|
/** 聚合部署的流程附件检查点。只调用原生保存/历史,不直接写业务表。 */
|
@Service
|
@RequiredArgsConstructor
|
public class WorkflowFileCheckpointService implements WorkflowFileCheckpoint {
|
private final VisualdevService models;
|
private final FlowFormDataUtil nativeData;
|
private final FlowFormCustomUtils nativeForms;
|
private final UploadEditDraftMapper drafts;
|
private final UploadFilePermissionQuery permissions;
|
|
@Override
|
@DSTransactional(rollbackFor = Exception.class)
|
@jnpf.audit.sdk.annotation.AuditLog(action = "WORKFLOW_FILE_CHECKPOINT", label = "保存流程共享附件", bizType = "UPLOAD_EDIT", bizCodeExpr = "'UPLOAD_EDIT'", bizCodeRequired = false)
|
public void save(String taskId, String formId, Map<String, Object> formData, Consumer<Map<String, Object>> continuation) {
|
save(taskId, formId, null, formData, continuation);
|
}
|
|
@Override
|
@DSTransactional(rollbackFor = Exception.class)
|
@jnpf.audit.sdk.annotation.AuditLog(action = "WORKFLOW_FILE_CHECKPOINT", label = "保存流程共享附件", bizType = "UPLOAD_EDIT", bizCodeExpr = "'UPLOAD_EDIT'", bizCodeRequired = false)
|
public void save(String taskId, String formId, String operatorId, Map<String, Object> formData,
|
Consumer<Map<String, Object>> continuation) {
|
FilePermissionView view = permissions.current(taskId, formId);
|
if (view == null || !view.isEnabled() || !"ready".equals(view.getStatus())
|
|| !Objects.equals(taskId, view.getTaskId()) || !Objects.equals(formId, view.getFormId()))
|
throw new DataException("流程文件权限暂不可用,未执行节点操作");
|
VisualdevEntity model = models.getReleaseInfo(formId);
|
if (model == null) throw new DataException("流程表单不存在");
|
Map<String, Object> persisted = nativeData.getFilePermissionDataInfo(model, taskId, OnlineInfoModel.builder().build());
|
String recordId = Objects.toString(persisted.get("id"), "");
|
if (recordId.isEmpty() || !Objects.equals(taskId, Objects.toString(persisted.get("flowTaskId"), "")))
|
throw new DataException("流程与原生表单记录不匹配");
|
if (nativeData.lockDmsAttachmentRecord(formId, recordId))
|
persisted = nativeData.getFilePermissionDataInfo(model, recordId, OnlineInfoModel.builder().build());
|
List<UploadEditDraft> shared = new ArrayList<>();
|
for (UploadEditDraft draft : drafts.lockRecord(UploadEditDraftService.tenant(UserProvider.getUser()), formId, recordId)) {
|
if (!UploadEditWorkingCopy.shared(draft) || !taskId.equals(draft.getTaskId())) continue;
|
if (UploadEditWorkingCopy.active(draft)) throw new DataException("流程附件正在编辑,请关闭编辑器并等待保存后再办理");
|
if (!UploadEditStateMachine.sameFiles(draft.getBaselineJson(), draft.getCurrentJson())) shared.add(draft);
|
}
|
Map<String, Object> result = formData == null ? new LinkedHashMap<>() : new LinkedHashMap<>(formData);
|
// 以完整真实记录作为基底,不能把审批弹窗的局部数据变成主子表增删。
|
Map<String, Object> checkpoint = new LinkedHashMap<>(persisted);
|
Map<String, Object> form = JsonUtil.getJsonToBean(model.getFormData(), Map.class);
|
boolean submittedAttachments = mergeSubmittedAttachments(checkpoint, result, form == null ? null : form.get("fields"));
|
if (shared.isEmpty() && !submittedAttachments) { continuation.accept(result); return; }
|
if (result.containsKey(UploadEditDraftService.SUBMIT_STATE))
|
checkpoint.put(UploadEditDraftService.SUBMIT_STATE, result.get(UploadEditDraftService.SUBMIT_STATE));
|
DataModel saved;
|
try {
|
saved = nativeForms.checkpointWorkflowFiles(model, FlowFormDataModel.builder().id(recordId).formId(formId)
|
.map(checkpoint).workflowOperatorId(operatorId).skipRequiredValidation(true).build());
|
} catch (WorkFlowException error) {
|
throw new DataException(error.getMessage());
|
}
|
// 节点暂存的其他字段仍保持用户输入,仅同步真正采用的附件和原生版本号。
|
for (UploadEditDraft draft : shared) {
|
String parent = draft.getParentField();
|
String field = parent == null || parent.isEmpty() ? draft.getField() : parent + "-" + draft.getField();
|
if (!saved.getWorkflowWorkingFields().contains(field)) continue;
|
if (parent == null || parent.isEmpty()) result.put(draft.getField(), checkpoint.get(draft.getField()));
|
else overlayChild(result, checkpoint, draft);
|
}
|
result.remove(UploadEditDraftService.SUBMIT_STATE);
|
String version = jnpf.util.TableFeildsEnum.VERSION.getField();
|
if (checkpoint.containsKey(version)) result.put(version, checkpoint.get(version));
|
continuation.accept(result);
|
}
|
|
private static boolean mergeSubmittedAttachments(Map<String, Object> target, Map<String, Object> submitted, Object fields) {
|
Map<String, Set<String>> uploads = new LinkedHashMap<>();
|
collectUploadFields(fields, null, uploads);
|
boolean changed = false;
|
for (String field : uploads.getOrDefault("", Collections.emptySet())) {
|
if (!submitted.containsKey(field) || UploadEditStateMachine.sameFiles(target.get(field), submitted.get(field))) continue;
|
target.put(field, submitted.get(field));
|
changed = true;
|
}
|
for (Map.Entry<String, Set<String>> table : uploads.entrySet()) {
|
if (table.getKey().isEmpty() || !(submitted.get(table.getKey()) instanceof List<?>)
|
|| !(target.get(table.getKey()) instanceof List<?>)) continue;
|
List<Map<String, Object>> rows = copyRows(target.get(table.getKey()));
|
Map<String, Map<?, ?>> submittedRows = new LinkedHashMap<>();
|
for (Object value : (List<?>) submitted.get(table.getKey())) {
|
if (!(value instanceof Map<?, ?>)) continue;
|
Map<?, ?> row = (Map<?, ?>) value;
|
String rowId = jnpf.base.service.UploadEditAccess.rowId(row);
|
if (!rowId.isEmpty() && submittedRows.put(rowId, row) != null) throw new DataException("附件子表行标识重复");
|
}
|
boolean tableChanged = false;
|
for (Map<String, Object> row : rows) {
|
Map<?, ?> supplied = submittedRows.get(jnpf.base.service.UploadEditAccess.rowId(row));
|
if (supplied == null) continue;
|
for (String field : table.getValue()) {
|
if (!supplied.containsKey(field) || UploadEditStateMachine.sameFiles(row.get(field), supplied.get(field))) continue;
|
row.put(field, supplied.get(field));
|
tableChanged = true;
|
}
|
}
|
if (tableChanged) {
|
target.put(table.getKey(), rows);
|
changed = true;
|
}
|
}
|
return changed;
|
}
|
|
private static void collectUploadFields(Object value, String parent, Map<String, Set<String>> result) {
|
if (!(value instanceof List<?>)) return;
|
for (Object item : (List<?>) value) {
|
if (!(item instanceof Map<?, ?>)) continue;
|
Map<?, ?> field = (Map<?, ?>) item;
|
if (!(field.get("__config__") instanceof Map<?, ?>)) continue;
|
Map<?, ?> config = (Map<?, ?>) field.get("__config__");
|
String key = Objects.toString(field.get("__vModel__"), "");
|
String type = Objects.toString(config.get("jnpfKey"), "");
|
if ("uploadFile".equals(type) && !key.isEmpty()) {
|
result.computeIfAbsent(Objects.toString(parent, ""), ignored -> new LinkedHashSet<>()).add(key);
|
}
|
collectUploadFields(config.get("children"), "table".equals(type) ? key : parent, result);
|
}
|
}
|
|
@SuppressWarnings("unchecked")
|
private static List<Map<String, Object>> copyRows(Object rows) {
|
return (List) JsonUtil.getJsonToList(JsonUtil.getObjectToString(rows), Map.class);
|
}
|
|
@SuppressWarnings("unchecked")
|
private static void overlayChild(Map<String, Object> target, Map<String, Object> source, UploadEditDraft draft) {
|
Object rows = target.get(draft.getParentField());
|
if (!(rows instanceof List<?>)) return; // 不替用户恢复缺失/已删除的行。
|
List<Map<String, Object>> copy = copyRows(rows);
|
Object files = jnpf.base.service.UploadEditAccess.fieldValue(source, UploadEditDraftService.context(draft));
|
for (Map<String, Object> row : copy) {
|
if (draft.getRowId().equals(jnpf.base.service.UploadEditAccess.rowId(row))) row.put(draft.getField(), files);
|
}
|
target.put(draft.getParentField(), copy);
|
}
|
}
|