package jnpf.dmsIntegration.audit;
|
|
import com.alibaba.fastjson.JSON;
|
import jakarta.servlet.http.HttpServletRequest;
|
import jnpf.audit.AuditConsts;
|
import jnpf.audit.diff.AuditFieldDiff;
|
import jnpf.audit.model.AuditEventDTO;
|
import jnpf.audit.model.AuditRelatedSignDTO;
|
import jnpf.audit.sdk.AuditClient;
|
import jnpf.base.ModuleApi;
|
import jnpf.base.entity.ModuleEntity;
|
import jnpf.dmsEntity.fenfa.DmsWenjianDayinJiluEntity;
|
import jnpf.dmsEntity.fenfa.DmsWenjianDayinRenwuEntity;
|
import jnpf.dmsEntity.fenfa.DmsWenjianFenfaRenwuEntity;
|
import jnpf.dmsEntity.fenfa.DmsWenjianHuishouMingxiEntity;
|
import jnpf.dmsEntity.permission.entity.DmsAccessExclusionEntity;
|
import jnpf.dmsEntity.permission.entity.DmsFilePermissionEntity;
|
import jnpf.util.JsonUtil;
|
import jnpf.util.ServletUtil;
|
import org.springframework.beans.factory.ObjectProvider;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Component;
|
|
import java.util.ArrayList;
|
import java.util.Collections;
|
import java.util.HashMap;
|
import java.util.LinkedHashMap;
|
import java.util.LinkedHashSet;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.UUID;
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
@Slf4j
|
@Component
|
public class DmsAuditAdapter {
|
private static final String FOLDER_TABLE = "dms_folders";
|
private static final String FOLDER_BIZ_MODULE = "DMS";
|
private static final String FOLDER_BIZ_TYPE = "DMS_FOLDER";
|
private static final String FILE_TABLE = "dms_documents";
|
private static final String FILE_VERSION_TABLE = "dms_file_versions";
|
private static final String VERSION_STATE_TABLE = "dms_version_states";
|
private static final String FILE_BIZ_TYPE = "DMS_FILE";
|
private static final String FILE_ENTRY_TYPE = "MENU";
|
private static final String FILE_ENTRY_ID = "dms-file-center";
|
private static final String FILE_ENTRY_NAME = "DMS 文件中心";
|
private static final String PERMISSION_BIZ_TYPE = "DMS_PERMISSION";
|
private static final String PERMISSION_ENTRY_ID = "dms-permission-management";
|
private static final String PERMISSION_ENTRY_NAME = "DMS 权限管理";
|
private static final String RECYCLE_MAIN_TABLE = "dms_wenjian_huishou";
|
private static final String RECYCLE_DETAIL_TABLE = "dms_wenjian_huishou_zixiang";
|
private static final String PRINT_RECORD_TABLE = "dms_wenjian_dayin_jilu";
|
private static final String RECYCLE_BIZ_TYPE = "DMS_FILE_RECYCLE";
|
private static final String RECYCLE_ENTRY_ID = "POST:/api/dms/biz/wenjian/fenfa/huishou";
|
private static final String RECYCLE_ENTRY_NAME = "文件扫码回收";
|
private static final String PRINT_TASK_TABLE = "dms_wenjian_dayin_renwu";
|
private static final String DISTRIBUTION_TASK_TABLE = "dms_wenjian_fenfa_renwu";
|
private static final String PRINT_BIZ_TYPE = "DMS_FILE_PRINT";
|
private static final String PRINT_SOURCE = "LIST_PRINT";
|
private static final long MENU_CACHE_MILLIS = 5 * 60 * 1000L;
|
private static final Map<String, AuditFieldDiff.FieldMeta> FOLDER_FIELD_META;
|
private static final Map<String, AuditFieldDiff.FieldMeta> FILE_FIELD_META;
|
private static final Map<String, AuditFieldDiff.FieldMeta> PERMISSION_FIELD_META;
|
private static final Map<String, AuditFieldDiff.FieldMeta> EXCLUSION_FIELD_META;
|
private static final Map<String, AuditFieldDiff.FieldMeta> RECYCLE_MAIN_FIELD_META;
|
private static final Map<String, AuditFieldDiff.FieldMeta> RECYCLE_DETAIL_FIELD_META;
|
private static final Map<String, AuditFieldDiff.FieldMeta> RECYCLE_PRINT_FIELD_META;
|
private static final Map<String, AuditFieldDiff.FieldMeta> PRINT_TASK_FIELD_META;
|
private static final Map<String, AuditFieldDiff.FieldMeta> PRINT_RECORD_FIELD_META;
|
private static final Map<String, AuditFieldDiff.FieldMeta> DISTRIBUTION_TASK_FIELD_META;
|
|
static {
|
Map<String, AuditFieldDiff.FieldMeta> meta = new LinkedHashMap<>();
|
meta.put("name", new AuditFieldDiff.FieldMeta("文件夹名称", null, null));
|
meta.put("parent_id", new AuditFieldDiff.FieldMeta("父文件夹", null, null));
|
FOLDER_FIELD_META = Collections.unmodifiableMap(meta);
|
|
Map<String, AuditFieldDiff.FieldMeta> fileMeta = new LinkedHashMap<>();
|
fileMeta.put("document_no", meta("文件编号"));
|
fileMeta.put("name", meta("文件名称"));
|
fileMeta.put("document_type", meta("文件类型"));
|
fileMeta.put("responsible_dept_id", meta("归属部门"));
|
fileMeta.put("folder_id", meta("所属文件夹"));
|
fileMeta.put("version_no", meta("版本序号"));
|
fileMeta.put("version_label", meta("版本号"));
|
fileMeta.put("file_name", meta("文件名"));
|
fileMeta.put("mime_type", meta("MIME 类型"));
|
fileMeta.put("size_bytes", meta("文件大小"));
|
fileMeta.put("checksum_sha256", meta("SHA-256"));
|
fileMeta.put("prepared_by", meta("制定人"));
|
fileMeta.put("prepared_on", meta("制定日期"));
|
fileMeta.put("lifecycle_status", lifecycleStatusMeta());
|
fileMeta.put("effective_from", meta("生效时间"));
|
fileMeta.put("effective_to", meta("失效时间"));
|
fileMeta.put("status_reason", meta("状态变更原因"));
|
FILE_FIELD_META = Collections.unmodifiableMap(fileMeta);
|
|
Map<String, AuditFieldDiff.FieldMeta> permissionMeta = new LinkedHashMap<>();
|
permissionMeta.put("resource_type", options("资源类型", "FOLDER", "文件夹",
|
"DOCUMENT", "文件", "FILE_VERSION", "文件版本"));
|
permissionMeta.put("resource_id", meta("授权资源"));
|
permissionMeta.put("resource_scope", options("授权范围", "SELF", "当前资源",
|
"SUBTREE", "当前文件夹及子树"));
|
permissionMeta.put("principal_type", options("主体类型", "USER", "用户", "ORG", "组织",
|
"POSITION", "岗位", "ROLE", "角色", "GROUP", "权限组"));
|
permissionMeta.put("principal_id", meta("授权主体"));
|
permissionMeta.put("include_child_orgs", booleanMeta("包含子组织"));
|
permissionMeta.put("approved_org_scope", options("审核查阅部门范围", "SELF", "本部门",
|
"CHILDREN", "本部门及直接子部门", "DESCENDANTS", "本部门及全部下级"));
|
permissionMeta.put("action_code", actionMeta());
|
permissionMeta.put("source_type", sourceTypeMeta());
|
permissionMeta.put("source_id", meta("来源标识"));
|
permissionMeta.put("valid_from", meta("生效时间"));
|
permissionMeta.put("valid_to", meta("失效时间"));
|
permissionMeta.put("max_use_count", meta("最大使用次数"));
|
permissionMeta.put("used_count", meta("已使用次数"));
|
permissionMeta.put("grant_reason", meta("授权原因"));
|
permissionMeta.put("granted_by_type", options("授权人类型", "USER", "用户", "SYSTEM", "系统"));
|
permissionMeta.put("granted_by_id", meta("授权人"));
|
permissionMeta.put("revoked_at", meta("撤销时间"));
|
permissionMeta.put("revoked_by_type", options("撤销人类型", "USER", "用户", "SYSTEM", "系统"));
|
permissionMeta.put("revoked_by_id", meta("撤销人"));
|
permissionMeta.put("revoke_reason", meta("撤销原因"));
|
permissionMeta.put("revision", meta("修订号"));
|
PERMISSION_FIELD_META = Collections.unmodifiableMap(permissionMeta);
|
|
Map<String, AuditFieldDiff.FieldMeta> exclusionMeta = new LinkedHashMap<>();
|
exclusionMeta.put("resource_type", permissionMeta.get("resource_type"));
|
exclusionMeta.put("resource_id", meta("排除资源"));
|
exclusionMeta.put("user_id", meta("排除用户"));
|
exclusionMeta.put("action_code", actionMeta());
|
exclusionMeta.put("source_type", sourceTypeMeta());
|
exclusionMeta.put("source_id", meta("来源标识"));
|
exclusionMeta.put("valid_from", meta("生效时间"));
|
exclusionMeta.put("valid_to", meta("失效时间"));
|
exclusionMeta.put("exclude_reason", meta("排除原因"));
|
exclusionMeta.put("excluded_by_type", options("排除操作人类型", "USER", "用户", "SYSTEM", "系统"));
|
exclusionMeta.put("excluded_by_id", meta("排除操作人"));
|
exclusionMeta.put("revoked_at", meta("撤销时间"));
|
exclusionMeta.put("revoked_by_type", permissionMeta.get("revoked_by_type"));
|
exclusionMeta.put("revoked_by_id", permissionMeta.get("revoked_by_id"));
|
exclusionMeta.put("revoke_reason", permissionMeta.get("revoke_reason"));
|
exclusionMeta.put("revision", permissionMeta.get("revision"));
|
EXCLUSION_FIELD_META = Collections.unmodifiableMap(exclusionMeta);
|
|
Map<String, AuditFieldDiff.FieldMeta> recycleMainMeta = new LinkedHashMap<>();
|
recycleMainMeta.put("beizhu_shuoming", meta("回收备注"));
|
RECYCLE_MAIN_FIELD_META = Collections.unmodifiableMap(recycleMainMeta);
|
|
Map<String, AuditFieldDiff.FieldMeta> recycleDetailMeta = new LinkedHashMap<>();
|
recycleDetailMeta.put("f_foreign_id", meta("回收单"));
|
recycleDetailMeta.put("dayin_jilu_id", meta("打印记录"));
|
recycleDetailMeta.put("dayin_liushuihao", meta("打印流水号"));
|
recycleDetailMeta.put("wenjian_bianhao", meta("文件编号"));
|
recycleDetailMeta.put("wenjian_mingcheng", meta("文件名称"));
|
recycleDetailMeta.put("banbenhao", meta("版本号"));
|
recycleDetailMeta.put("qianshou_bumen", meta("签收部门"));
|
recycleDetailMeta.put("qianshou_ren", meta("签收人"));
|
recycleDetailMeta.put("qianshou_shijian", meta("签收时间"));
|
recycleDetailMeta.put("huishou_ren_id", meta("回收人"));
|
recycleDetailMeta.put("huishou_shijian", meta("回收时间"));
|
RECYCLE_DETAIL_FIELD_META = Collections.unmodifiableMap(recycleDetailMeta);
|
|
Map<String, AuditFieldDiff.FieldMeta> recyclePrintMeta = new LinkedHashMap<>();
|
recyclePrintMeta.put("biz_status", options(
|
"文件副本状态", "daifafang", "待发放", "daiqianshou", "待签收",
|
"daihuishou", "待回收", "daixiaohui", "待销毁"));
|
RECYCLE_PRINT_FIELD_META = Collections.unmodifiableMap(recyclePrintMeta);
|
|
Map<String, AuditFieldDiff.FieldMeta> printTaskMeta = new LinkedHashMap<>();
|
printTaskMeta.put("biz_status", options(
|
"打印状态", "daidayin", "待打印", "bufendayin", "部分打印", "yidayin", "已打印"));
|
PRINT_TASK_FIELD_META = Collections.unmodifiableMap(printTaskMeta);
|
|
Map<String, AuditFieldDiff.FieldMeta> printRecordMeta = new LinkedHashMap<>();
|
printRecordMeta.put("dayin_renwu_id", meta("打印任务"));
|
printRecordMeta.put("dayin_xuhao", meta("打印序号"));
|
printRecordMeta.put("dayin_liushuihao", meta("打印流水号"));
|
printRecordMeta.put("dayin_jishu_liushuihao", meta("计数流水号"));
|
printRecordMeta.put("dayin_ren_id", meta("打印人"));
|
printRecordMeta.put("dayin_bumen_id", meta("打印部门"));
|
printRecordMeta.put("dayin_shijian", meta("打印时间"));
|
printRecordMeta.put("biz_status", options(
|
"分发状态", "daifafang", "待发放", "yiguidang", "已归档"));
|
PRINT_RECORD_FIELD_META = Collections.unmodifiableMap(printRecordMeta);
|
|
Map<String, AuditFieldDiff.FieldMeta> distributionTaskMeta = new LinkedHashMap<>();
|
distributionTaskMeta.put("dayin_renwu_id", meta("打印任务"));
|
distributionTaskMeta.put("fenfa_bianhao", meta("分发编号"));
|
distributionTaskMeta.put("fafang_leixing", options(
|
"发放类型", "zidongfenfa", "自动分发"));
|
distributionTaskMeta.put("biz_status", options(
|
"分发状态", "daifafang", "待发放"));
|
DISTRIBUTION_TASK_FIELD_META = Collections.unmodifiableMap(distributionTaskMeta);
|
}
|
|
private final ObjectProvider<AuditClient> auditClient;
|
private final ObjectProvider<DmsPermissionAuditDisplayResolver> permissionDisplayResolver;
|
private final ObjectProvider<ModuleApi> moduleApi;
|
private final Map<String, MenuCacheEntry> menuCache = new ConcurrentHashMap<>();
|
|
@Autowired
|
public DmsAuditAdapter(ObjectProvider<AuditClient> auditClient,
|
ObjectProvider<DmsPermissionAuditDisplayResolver> permissionDisplayResolver,
|
ObjectProvider<ModuleApi> moduleApi) {
|
this.auditClient = auditClient;
|
this.permissionDisplayResolver = permissionDisplayResolver;
|
this.moduleApi = moduleApi;
|
}
|
|
/** Test and compatibility constructor for callers without menu resolution. */
|
public DmsAuditAdapter(ObjectProvider<AuditClient> auditClient,
|
ObjectProvider<DmsPermissionAuditDisplayResolver> permissionDisplayResolver) {
|
this.auditClient = auditClient;
|
this.permissionDisplayResolver = permissionDisplayResolver;
|
this.moduleApi = null;
|
}
|
|
/** Test and compatibility constructor for callers that do not need permission display resolution. */
|
public DmsAuditAdapter(ObjectProvider<AuditClient> auditClient) {
|
this.auditClient = auditClient;
|
this.permissionDisplayResolver = null;
|
this.moduleApi = null;
|
}
|
|
public void dataChange(String actionCode, String actionLabel, String table, String id,
|
String reason, String extra) {
|
record(AuditConsts.TYPE_DATA_CHANGE, actionCode, actionLabel, table, id, reason, extra);
|
}
|
|
public void permissionChange(String actionCode, String actionLabel,
|
List<DmsFilePermissionEntity> before,
|
List<DmsFilePermissionEntity> after,
|
List<AuditRelatedSignDTO> relatedSigns) {
|
try {
|
List<DmsFilePermissionEntity> retained = retainedPermissions(before, after);
|
if (retained.isEmpty()) return;
|
DmsPermissionAuditDisplayResolver.DisplayValues displays = permissionDisplays(retained);
|
List<Map<String, Object>> targets = new ArrayList<>();
|
Map<UUID, DmsFilePermissionEntity> beforeById = permissionsById(before);
|
for (DmsFilePermissionEntity item : retained) {
|
List<Map<String, Object>> changes = permissionDiffs(beforeById.get(item.getId()), item,
|
displays, false);
|
targets.add(permissionTarget(item, changes, displays));
|
}
|
DmsFilePermissionEntity first = retained.get(0);
|
PermissionActionSummary actions = permissionActions(retained);
|
Map<String, Object> extra = batchExtra(targets);
|
extra.put("permissionId", value(first.getId()));
|
extra.put("actionCodes", actions.codes);
|
extra.put("actionNames", actions.names);
|
extra.put("sourceType", first.getSourceType());
|
extra.put("sourceId", first.getSourceId());
|
List<Map<String, Object>> firstDiffs = permissionDiffs(
|
beforeById.get(first.getId()), first, displays, true);
|
combineActionDiff(firstDiffs, actions);
|
Map<String, Object> firstSnapshot = permissionSnapshot(first, displays);
|
combineActionSnapshot(firstSnapshot, actions);
|
recordPermissionEvent(actionCode, actionLabel, first,
|
firstDiffs, firstSnapshot, extra,
|
first.getRevokeReason() == null ? first.getGrantReason() : first.getRevokeReason(),
|
permissionTitle(first, displays, actions.display), relatedSigns);
|
} catch (RuntimeException ex) {
|
log.warn("DMS permission audit event construction failed actionCode={}", actionCode, ex);
|
}
|
}
|
|
public void accessExclusionChange(String actionCode, String actionLabel,
|
List<DmsAccessExclusionEntity> before,
|
List<DmsAccessExclusionEntity> after,
|
List<AuditRelatedSignDTO> relatedSigns) {
|
try {
|
List<DmsAccessExclusionEntity> retained = retainedExclusions(before, after);
|
if (retained.isEmpty()) return;
|
DmsPermissionAuditDisplayResolver.DisplayValues displays = exclusionDisplays(retained);
|
List<Map<String, Object>> targets = new ArrayList<>();
|
Map<UUID, DmsAccessExclusionEntity> beforeById = exclusionsById(before);
|
for (DmsAccessExclusionEntity item : retained) {
|
List<Map<String, Object>> changes = exclusionDiffs(beforeById.get(item.getId()), item,
|
displays, false);
|
targets.add(exclusionTarget(item, changes, displays));
|
}
|
DmsAccessExclusionEntity first = retained.get(0);
|
Map<String, Object> extra = batchExtra(targets);
|
extra.put("exclusionId", value(first.getId()));
|
extra.put("sourceType", first.getSourceType());
|
extra.put("sourceId", first.getSourceId());
|
recordExclusionEvent(actionCode, actionLabel, first,
|
exclusionDiffs(beforeById.get(first.getId()), first, displays, true),
|
exclusionSnapshot(first, displays), extra,
|
first.getRevokeReason() == null ? first.getExcludeReason() : first.getRevokeReason(),
|
exclusionTitle(first, displays), relatedSigns);
|
} catch (RuntimeException ex) {
|
log.warn("DMS access exclusion audit event construction failed actionCode={}", actionCode, ex);
|
}
|
}
|
|
public void folderChange(DmsFolderAuditEvent event) {
|
if (event == null || event.getFolderId() == null) {
|
throw new IllegalArgumentException("文件夹审计事件和 folderId 不能为空");
|
}
|
DmsFolderAuditSnapshot before = event.getBefore();
|
DmsFolderAuditSnapshot after = event.getAfter();
|
Map<String, Object> extra = new LinkedHashMap<>();
|
extra.put("schemaVersion", 1);
|
extra.put("oldParentId", value(before == null ? null : before.getParentId()));
|
extra.put("newParentId", value(after == null ? null : after.getParentId()));
|
extra.put("oldParentName", before == null ? null : before.getParentName());
|
extra.put("newParentName", after == null ? null : after.getParentName());
|
extra.put("oldPath", before == null ? null : before.getPath());
|
extra.put("newPath", after == null ? null : after.getPath());
|
extra.put("requestRevision", event.getRequestRevision());
|
extra.put("resultRevision", after == null ? null : after.getRevision());
|
extra.put("signatureRequired", event.isSignatureRequired());
|
|
DmsFolderAuditSnapshot retained = after == null ? before : after;
|
auditClient.ifAvailable(client -> client.record(AuditEventDTO.builder()
|
.eventType(AuditConsts.TYPE_DATA_CHANGE)
|
.actionCode(event.getActionCode())
|
.actionLabel(event.getActionLabel())
|
.bizModule(FOLDER_BIZ_MODULE)
|
.bizType(FOLDER_BIZ_TYPE)
|
.bizCode(event.getFolderId().toString())
|
.targetTable(FOLDER_TABLE)
|
.targetId(event.getFolderId().toString())
|
.fieldDiffs(AuditFieldDiff.diff(diffData(before), diffData(after),
|
Collections.emptySet(), FOLDER_FIELD_META))
|
.dataSnapshot(JsonUtil.getObjectToString(snapshotData(retained)))
|
.extra(JsonUtil.getObjectToString(extra))
|
.recordTitle(title(before, after, event.getFolderId().toString()))
|
.entryType(event.getEntryType())
|
.entryId(event.getEntryId())
|
.entryName(event.getEntryName())
|
.relatedSigns(event.getRelatedSigns())
|
.sourceLayer(3)
|
.build()));
|
}
|
|
public void fileChange(DmsFileAuditEvent event) {
|
if (event == null || event.getDocumentId() == null) {
|
throw new IllegalArgumentException("文件审计事件和 documentId 不能为空");
|
}
|
DmsFileAuditSnapshot before = event.getBefore();
|
DmsFileAuditSnapshot after = event.getAfter();
|
DmsFileAuditSnapshot retained = after == null ? before : after;
|
List<Map<String, Object>> diffs = new ArrayList<>();
|
addScopedDiffs(diffs, FILE_TABLE, event.getDocumentId().toString(),
|
documentDiffData(before), documentDiffData(after));
|
|
DmsFileVersionAuditSnapshot beforeVersion = before == null ? null : before.getCurrentVersion();
|
DmsFileVersionAuditSnapshot afterVersion = after == null ? null : after.getCurrentVersion();
|
if (afterVersion != null && (beforeVersion == null
|
|| !afterVersion.getFileVersionId().equals(beforeVersion.getFileVersionId()))) {
|
addScopedDiffs(diffs, FILE_VERSION_TABLE, value(afterVersion.getFileVersionId()),
|
null, versionDiffData(afterVersion));
|
addScopedDiffs(diffs, VERSION_STATE_TABLE, value(afterVersion.getFileVersionId()),
|
null, stateDiffData(afterVersion));
|
}
|
if (beforeVersion != null && event.getPreviousVersionAfter() != null) {
|
addScopedDiffs(diffs, VERSION_STATE_TABLE, value(beforeVersion.getFileVersionId()),
|
stateDiffData(beforeVersion), stateDiffData(event.getPreviousVersionAfter()));
|
}
|
|
Map<String, Object> extra = new LinkedHashMap<>();
|
extra.put("schemaVersion", 1);
|
extra.put("sourceServiceId", event.getSourceServiceId());
|
extra.put("sourceId", event.getSourceId());
|
extra.put("baseFileVersionId", beforeVersion == null ? null : value(beforeVersion.getFileVersionId()));
|
extra.put("newFileVersionId", afterVersion == null ? null : value(afterVersion.getFileVersionId()));
|
extra.put("oldVersionLabel", beforeVersion == null ? null : beforeVersion.getVersionLabel());
|
extra.put("newVersionLabel", afterVersion == null ? null : afterVersion.getVersionLabel());
|
extra.put("oldChecksumSha256", beforeVersion == null ? null : beforeVersion.getChecksumSha256());
|
extra.put("newChecksumSha256", afterVersion == null ? null : afterVersion.getChecksumSha256());
|
|
auditClient.ifAvailable(client -> client.record(AuditEventDTO.builder()
|
.eventType(AuditConsts.TYPE_DATA_CHANGE)
|
.actionCode(event.getActionCode())
|
.actionLabel(event.getActionLabel())
|
.bizModule(FOLDER_BIZ_MODULE)
|
.bizType(FILE_BIZ_TYPE)
|
.bizCode(retained == null || retained.getDocumentNo() == null
|
? event.getDocumentId().toString() : retained.getDocumentNo())
|
.targetTable(FILE_TABLE)
|
.targetId(event.getDocumentId().toString())
|
.fieldDiffs(JsonUtil.getObjectToString(diffs))
|
.dataSnapshot(JsonUtil.getObjectToString(fileSnapshotData(retained)))
|
.reason(event.getReason())
|
.extra(JsonUtil.getObjectToString(extra))
|
.recordTitle(fileTitle(before, after, event.getDocumentId().toString()))
|
.entryType(FILE_ENTRY_TYPE)
|
.entryId(FILE_ENTRY_ID)
|
.entryName(FILE_ENTRY_NAME)
|
.relatedSigns(event.getRelatedSigns())
|
.sourceLayer(3)
|
.build()));
|
}
|
|
/** 文件归档两阶段状态变更;不借用回收单据承载归档审计。 */
|
public void archiveStatus(List<String> recordIds, String before, String after,
|
Map<String, ?> signParameters) {
|
if (recordIds == null || recordIds.isEmpty()) return;
|
List<Map<String, Object>> diffs = new ArrayList<>();
|
Map<String, AuditFieldDiff.FieldMeta> statusMeta = Collections.singletonMap("biz_status",
|
options("文件副本状态", "daihuishou", "待回收", "daiguidang", "待归档", "yiguidang", "已归档"));
|
for (String id : recordIds) {
|
addScopedDiffs(diffs, PRINT_RECORD_TABLE, id,
|
Collections.<String, Object>singletonMap("biz_status", before),
|
Collections.<String, Object>singletonMap("biz_status", after), statusMeta);
|
}
|
Map<String, Object> snapshot = new LinkedHashMap<>();
|
snapshot.put("dayin_jilu_ids", recordIds);
|
snapshot.put("before_status", before);
|
snapshot.put("after_status", after);
|
boolean completed = "yiguidang".equals(after);
|
auditClient.ifAvailable(client -> client.record(AuditEventDTO.builder()
|
.eventType(AuditConsts.TYPE_DATA_CHANGE)
|
.actionCode(completed ? "FILE_ARCHIVE_COMPLETE" : "FILE_ARCHIVE_CONFIRM")
|
.actionLabel(completed ? "文件归档完成" : "文件确认归档")
|
.bizModule(FOLDER_BIZ_MODULE).bizType("DMS_FILE_ARCHIVE")
|
.bizCode(recordIds.get(0)).targetTable(PRINT_RECORD_TABLE).targetId(recordIds.get(0))
|
.fieldDiffs(JsonUtil.getObjectToString(diffs))
|
.dataSnapshot(JsonUtil.getObjectToString(snapshot))
|
.relatedSigns(DmsAuditRequestSignResolver.resolve(signParameters))
|
.sourceLayer(3).build()));
|
}
|
|
public void recycle(DmsWenjianHuishouAuditEvent event) {
|
if (event == null || blank(event.getHuishouDanjuId())) {
|
throw new IllegalArgumentException("文件回收审计事件和回收单ID不能为空");
|
}
|
if (event.getDetails() == null || event.getDetails().isEmpty()) {
|
throw new IllegalArgumentException("文件回收审计明细不能为空");
|
}
|
try {
|
List<Map<String, Object>> diffs = new ArrayList<>();
|
Map<String, Object> mainAfter = new LinkedHashMap<>();
|
mainAfter.put("beizhu_shuoming", event.getBeizhuShuoming());
|
addScopedDiffs(diffs, RECYCLE_MAIN_TABLE, event.getHuishouDanjuId(),
|
null, mainAfter, RECYCLE_MAIN_FIELD_META);
|
|
List<Map<String, Object>> targets = new ArrayList<>();
|
List<String> subtitles = new ArrayList<>();
|
for (DmsWenjianHuishouMingxiEntity detail : event.getDetails()) {
|
addScopedDiffs(diffs, RECYCLE_DETAIL_TABLE, detail.getId(), null,
|
recycleDetailDiffData(detail), RECYCLE_DETAIL_FIELD_META);
|
String beforeStatus = event.getBeforeStatuses() == null ? null
|
: event.getBeforeStatuses().get(detail.getDayinJiluId());
|
Map<String, Object> oldStatus = Collections.<String, Object>singletonMap(
|
"biz_status", beforeStatus);
|
Map<String, Object> newStatus = Collections.<String, Object>singletonMap(
|
"biz_status", "daixiaohui");
|
addScopedDiffs(diffs, PRINT_RECORD_TABLE, detail.getDayinJiluId(),
|
oldStatus, newStatus, RECYCLE_PRINT_FIELD_META);
|
Map<String, Object> target = recycleTarget(detail, beforeStatus);
|
targets.add(target);
|
subtitles.add(String.valueOf(target.get("recordTitle")));
|
}
|
|
Map<String, Object> extra = new LinkedHashMap<>();
|
extra.put("schemaVersion", 1);
|
extra.put("batch", targets.size() > 1);
|
extra.put("batchSize", targets.size());
|
extra.put("affected", targets.size());
|
extra.put("affectedTargets", targets);
|
extra.put("recordSubTitles", subtitles);
|
|
auditClient.ifAvailable(client -> client.record(AuditEventDTO.builder()
|
.eventType(AuditConsts.TYPE_DATA_CHANGE)
|
.actionCode("FILE_RECYCLE")
|
.actionLabel("文件回收")
|
.bizModule(FOLDER_BIZ_MODULE)
|
.bizType(RECYCLE_BIZ_TYPE)
|
.bizCode(event.getHuishouDanjuId())
|
.targetTable(RECYCLE_MAIN_TABLE)
|
.targetId(event.getHuishouDanjuId())
|
.fieldDiffs(JsonUtil.getObjectToString(diffs))
|
.dataSnapshot(JsonUtil.getObjectToString(recycleSnapshot(event, targets)))
|
.reason(event.getBeizhuShuoming())
|
.extra(JsonUtil.getObjectToString(extra))
|
.recordTitle(recycleTitle(event.getDetails()))
|
.entryType("API")
|
.entryId(RECYCLE_ENTRY_ID)
|
.entryName(RECYCLE_ENTRY_NAME)
|
.relatedSigns(event.getRelatedSigns())
|
.sourceLayer(3)
|
.build()));
|
} catch (RuntimeException ex) {
|
log.warn("DMS recycle audit event construction failed recycleId={}",
|
event.getHuishouDanjuId(), ex);
|
}
|
}
|
|
public void print(DmsWenjianDayinAuditEvent event) {
|
try {
|
List<DmsWenjianDayinRenwuEntity> tasks = event == null
|
? Collections.emptyList() : orEmpty(event.getTasks());
|
List<DmsWenjianDayinJiluEntity> records = event == null
|
? Collections.emptyList() : orEmpty(event.getRecords());
|
if (tasks.isEmpty() || records.isEmpty()) {
|
throw new IllegalArgumentException("文件打印审计任务和打印记录不能为空");
|
}
|
|
Map<String, DmsWenjianDayinRenwuEntity> taskById = new LinkedHashMap<>();
|
Map<String, List<DmsWenjianDayinJiluEntity>> recordsByTask = new LinkedHashMap<>();
|
for (DmsWenjianDayinRenwuEntity task : tasks) {
|
if (task == null || blank(task.getId()) || taskById.put(task.getId(), task) != null) {
|
throw new IllegalArgumentException("文件打印审计任务无效或重复");
|
}
|
recordsByTask.put(task.getId(), new ArrayList<>());
|
}
|
for (DmsWenjianDayinJiluEntity record : records) {
|
List<DmsWenjianDayinJiluEntity> taskRecords = record == null
|
? null : recordsByTask.get(record.getDayinRenwuId());
|
if (record == null || blank(record.getId()) || taskRecords == null) {
|
throw new IllegalArgumentException("打印记录未关联本次打印任务");
|
}
|
taskRecords.add(record);
|
}
|
|
List<Map<String, Object>> diffs = new ArrayList<>();
|
List<Map<String, Object>> targets = new ArrayList<>(tasks.size());
|
List<String> subtitles = new ArrayList<>(tasks.size());
|
for (DmsWenjianDayinRenwuEntity task : tasks) {
|
List<DmsWenjianDayinJiluEntity> taskRecords = recordsByTask.get(task.getId());
|
if (taskRecords.isEmpty()) {
|
throw new IllegalArgumentException("打印任务没有生成打印记录");
|
}
|
if (event.getAfterStatuses() == null || blank(event.getAfterStatuses().get(task.getId()))) {
|
throw new IllegalArgumentException("打印审计缺少实际更新状态");
|
}
|
addScopedDiffs(diffs, PRINT_TASK_TABLE, task.getId(),
|
Collections.<String, Object>singletonMap("biz_status", task.getBizStatus()),
|
Collections.<String, Object>singletonMap("biz_status", event.getAfterStatuses().get(task.getId())),
|
PRINT_TASK_FIELD_META);
|
for (DmsWenjianDayinJiluEntity record : taskRecords) {
|
addScopedDiffs(diffs, PRINT_RECORD_TABLE, record.getId(), null,
|
printRecordDiffData(record), PRINT_RECORD_FIELD_META);
|
}
|
Map<String, Object> target = printTarget(task, taskRecords);
|
targets.add(target);
|
subtitles.add(String.valueOf(target.get("title")));
|
}
|
for (DmsWenjianFenfaRenwuEntity distributionTask : orEmpty(
|
event.getDistributionTasks())) {
|
if (distributionTask != null && !blank(distributionTask.getId())) {
|
addScopedDiffs(diffs, DISTRIBUTION_TASK_TABLE, distributionTask.getId(), null,
|
distributionTaskDiffData(distributionTask),
|
DISTRIBUTION_TASK_FIELD_META);
|
}
|
}
|
|
boolean batch = tasks.size() > 1;
|
int totalCopies = records.size();
|
PrintMenuContext menu = resolvePrintMenu();
|
Map<String, Object> extra = new LinkedHashMap<>();
|
extra.put("schemaVersion", 1);
|
extra.put("printName", "受控文件打印");
|
extra.put("targetTable", PRINT_TASK_TABLE);
|
extra.put("bizType", PRINT_BIZ_TYPE);
|
extra.put("source", PRINT_SOURCE);
|
if (menu.menuId != null) extra.put("menuId", menu.menuId);
|
if (menu.menuName != null) extra.put("menuName", menu.menuName);
|
extra.put("menuResolution", menu.resolution);
|
extra.put("printMode", batch ? "BATCH" : "SINGLE");
|
extra.put("printCount", tasks.size());
|
extra.put("totalCopies", totalCopies);
|
extra.put("printTargets", targets);
|
extra.put("recordSubTitles", subtitles);
|
|
DmsWenjianDayinRenwuEntity single = batch ? null : tasks.get(0);
|
String eventId = "PRINT-" + UUID.randomUUID().toString().replace("-", "");
|
auditClient.ifAvailable(client -> client.record(AuditEventDTO.builder()
|
.clientEventId(eventId)
|
.operationId(eventId)
|
.eventType(AuditConsts.TYPE_PRINT)
|
.actionCode("PRINT")
|
.actionLabel("发起打印")
|
.bizModule(menu.menuName == null ? "待打印台账" : menu.menuName)
|
.bizType(PRINT_BIZ_TYPE)
|
.bizCode(single == null ? null : single.getWenjianBianhao())
|
.targetTable(PRINT_TASK_TABLE)
|
.targetId(single == null ? null : single.getId())
|
.fieldDiffs(JsonUtil.getObjectToString(diffs))
|
.dataSnapshot(JsonUtil.getObjectToString(printSnapshot(targets, totalCopies)))
|
.extra(JsonUtil.getObjectToString(extra))
|
.recordTitle(printTitle(tasks, totalCopies))
|
.entryType(menu.menuId == null ? null : "MENU")
|
.entryId(menu.menuId)
|
.entryName(menu.menuName)
|
.sourceLayer(3)
|
.build()));
|
} catch (RuntimeException ex) {
|
log.warn("DMS print audit event construction failed", ex);
|
}
|
}
|
|
private void recordPermissionEvent(String actionCode, String actionLabel,
|
DmsFilePermissionEntity first,
|
List<Map<String, Object>> diffs,
|
Map<String, Object> snapshot,
|
Map<String, Object> extra,
|
String reason,
|
String recordTitle,
|
List<AuditRelatedSignDTO> relatedSigns) {
|
auditClient.ifAvailable(client -> client.record(AuditEventDTO.builder()
|
.eventType(AuditConsts.TYPE_DATA_CHANGE)
|
.actionCode(actionCode)
|
.actionLabel(actionLabel)
|
.bizModule(FOLDER_BIZ_MODULE)
|
.bizType(PERMISSION_BIZ_TYPE)
|
.bizCode(value(first.getResourceId()))
|
.targetTable(resourceTable(first.getResourceType()))
|
.targetId(value(first.getResourceId()))
|
.fieldDiffs(JsonUtil.getObjectToString(diffs))
|
.dataSnapshot(JsonUtil.getObjectToString(snapshot))
|
.reason(reason)
|
.extra(JsonUtil.getObjectToString(extra))
|
.recordTitle(batchTitle(recordTitle, extra))
|
.entryType(FILE_ENTRY_TYPE)
|
.entryId(PERMISSION_ENTRY_ID)
|
.entryName(PERMISSION_ENTRY_NAME)
|
.relatedSigns(relatedSigns)
|
.sourceLayer(3)
|
.build()));
|
}
|
|
private void recordExclusionEvent(String actionCode, String actionLabel,
|
DmsAccessExclusionEntity first,
|
List<Map<String, Object>> diffs,
|
Map<String, Object> snapshot,
|
Map<String, Object> extra,
|
String reason,
|
String recordTitle,
|
List<AuditRelatedSignDTO> relatedSigns) {
|
auditClient.ifAvailable(client -> client.record(AuditEventDTO.builder()
|
.eventType(AuditConsts.TYPE_DATA_CHANGE)
|
.actionCode(actionCode)
|
.actionLabel(actionLabel)
|
.bizModule(FOLDER_BIZ_MODULE)
|
.bizType(PERMISSION_BIZ_TYPE)
|
.bizCode(value(first.getResourceId()))
|
.targetTable(resourceTable(first.getResourceType()))
|
.targetId(value(first.getResourceId()))
|
.fieldDiffs(JsonUtil.getObjectToString(diffs))
|
.dataSnapshot(JsonUtil.getObjectToString(snapshot))
|
.reason(reason)
|
.extra(JsonUtil.getObjectToString(extra))
|
.recordTitle(batchTitle(recordTitle, extra))
|
.entryType(FILE_ENTRY_TYPE)
|
.entryId(PERMISSION_ENTRY_ID)
|
.entryName(PERMISSION_ENTRY_NAME)
|
.relatedSigns(relatedSigns)
|
.sourceLayer(3)
|
.build()));
|
}
|
|
private static Map<String, Object> batchExtra(List<Map<String, Object>> targets) {
|
Map<String, Object> extra = new LinkedHashMap<>();
|
extra.put("schemaVersion", 1);
|
extra.put("batch", targets.size() > 1);
|
extra.put("batchSize", targets.size());
|
extra.put("affected", targets.size());
|
extra.put("affectedTargets", targets);
|
List<String> titles = new ArrayList<>();
|
for (Map<String, Object> target : targets) {
|
Object title = target.get("recordTitle");
|
if (title != null) titles.add(String.valueOf(title));
|
}
|
extra.put("recordSubTitles", titles);
|
return extra;
|
}
|
|
private static String batchTitle(String title, Map<String, Object> extra) {
|
int size = Integer.parseInt(String.valueOf(extra.get("batchSize")));
|
return size > 1 ? title + "(共 " + size + " 条)" : title;
|
}
|
|
private static List<Map<String, Object>> permissionDiffs(DmsFilePermissionEntity before,
|
DmsFilePermissionEntity after,
|
DmsPermissionAuditDisplayResolver.DisplayValues displays,
|
boolean scoped) {
|
return auditDiffs(permissionData(before), permissionData(after), PERMISSION_FIELD_META,
|
"dms_file_permissions", after == null ? value(before.getId()) : value(after.getId()),
|
(field, raw, oldValue) -> permissionDisplay(field, raw, oldValue ? before : after, displays),
|
scoped);
|
}
|
|
private static List<Map<String, Object>> exclusionDiffs(DmsAccessExclusionEntity before,
|
DmsAccessExclusionEntity after,
|
DmsPermissionAuditDisplayResolver.DisplayValues displays,
|
boolean scoped) {
|
return auditDiffs(exclusionData(before), exclusionData(after), EXCLUSION_FIELD_META,
|
"dms_access_exclusions", after == null ? value(before.getId()) : value(after.getId()),
|
(field, raw, oldValue) -> exclusionDisplay(field, raw, oldValue ? before : after, displays),
|
scoped);
|
}
|
|
@SuppressWarnings("unchecked")
|
private static List<Map<String, Object>> auditDiffs(Map<String, Object> before, Map<String, Object> after,
|
Map<String, AuditFieldDiff.FieldMeta> meta,
|
String table, String id,
|
DisplayLookup displayLookup, boolean scoped) {
|
List<Map> parsed = JSON.parseArray(AuditFieldDiff.diff(before, after,
|
Collections.emptySet(), meta), Map.class);
|
List<Map<String, Object>> result = new ArrayList<>();
|
for (Map item : parsed) {
|
Map<String, Object> diff = new LinkedHashMap<>();
|
if (scoped) {
|
diff.put("targetTable", table);
|
diff.put("targetId", id);
|
}
|
diff.putAll(item);
|
String field = String.valueOf(item.get("field"));
|
String oldRaw = String.valueOf(item.get("oldData"));
|
String newRaw = String.valueOf(item.get("newData"));
|
diff.put("oldDisplay", displayLookup.display(field, oldRaw, true));
|
diff.put("newDisplay", displayLookup.display(field, newRaw, false));
|
result.add(diff);
|
}
|
return result;
|
}
|
|
private static String permissionDisplay(String field, String raw, DmsFilePermissionEntity row,
|
DmsPermissionAuditDisplayResolver.DisplayValues displays) {
|
if (raw == null || raw.isEmpty() || "null".equals(raw)) return "";
|
if (row != null && "resource_id".equals(field)) return displays.resource(row.getResourceType(), row.getResourceId());
|
if (row != null && "principal_id".equals(field)) return displays.principal(row.getPrincipalType(), row.getPrincipalId());
|
if (row != null && ("granted_by_id".equals(field) || "revoked_by_id".equals(field))) return displays.user(raw);
|
return optionDisplay(PERMISSION_FIELD_META.get(field), raw);
|
}
|
|
private static String exclusionDisplay(String field, String raw, DmsAccessExclusionEntity row,
|
DmsPermissionAuditDisplayResolver.DisplayValues displays) {
|
if (raw == null || raw.isEmpty() || "null".equals(raw)) return "";
|
if (row != null && "resource_id".equals(field)) return displays.resource(row.getResourceType(), row.getResourceId());
|
if ("user_id".equals(field) || "excluded_by_id".equals(field) || "revoked_by_id".equals(field)) {
|
return displays.user(raw);
|
}
|
return optionDisplay(EXCLUSION_FIELD_META.get(field), raw);
|
}
|
|
private static String optionDisplay(AuditFieldDiff.FieldMeta meta, String raw) {
|
String display = meta == null ? null : meta.optionDisplay(raw);
|
return display == null ? raw : display;
|
}
|
|
private static Map<String, Object> permissionTarget(DmsFilePermissionEntity value,
|
List<Map<String, Object>> changes,
|
DmsPermissionAuditDisplayResolver.DisplayValues displays) {
|
Map<String, Object> target = permissionSnapshot(value, displays);
|
target.put("changes", changes);
|
target.put("recordTitle", permissionTitle(value, displays));
|
return target;
|
}
|
|
private static Map<String, Object> exclusionTarget(DmsAccessExclusionEntity value,
|
List<Map<String, Object>> changes,
|
DmsPermissionAuditDisplayResolver.DisplayValues displays) {
|
Map<String, Object> target = exclusionSnapshot(value, displays);
|
target.put("changes", changes);
|
target.put("recordTitle", exclusionTitle(value, displays));
|
return target;
|
}
|
|
private static Map<String, Object> permissionSnapshot(DmsFilePermissionEntity value,
|
DmsPermissionAuditDisplayResolver.DisplayValues displays) {
|
Map<String, Object> data = new LinkedHashMap<>();
|
data.put("permissionId", value(value.getId()));
|
data.put("resourceType", value.getResourceType());
|
data.put("resourceId", value(value.getResourceId()));
|
data.put("resourceName", displays.resource(value.getResourceType(), value.getResourceId()));
|
data.put("resourceScope", value.getResourceScope());
|
data.put("principalType", value.getPrincipalType());
|
data.put("principalId", value.getPrincipalId());
|
data.put("principalName", displays.principal(value.getPrincipalType(), value.getPrincipalId()));
|
data.put("includeChildOrgs", value.getIncludeChildOrgs());
|
if (value.getApprovedOrgScope() != null) data.put("approvedOrgScope", value.getApprovedOrgScope());
|
data.put("actionCode", value.getActionCode());
|
data.put("actionName", optionDisplay(PERMISSION_FIELD_META.get("action_code"), value.getActionCode()));
|
data.put("sourceType", value.getSourceType());
|
data.put("sourceId", value.getSourceId());
|
data.put("validFrom", value.getValidFrom());
|
data.put("validTo", value.getValidTo());
|
data.put("maxUseCount", value.getMaxUseCount());
|
data.put("usedCount", value.getUsedCount());
|
data.put("grantReason", value.getGrantReason());
|
data.put("grantedByType", value.getGrantedByType());
|
data.put("grantedById", value.getGrantedById());
|
data.put("grantedByName", displays.user(value.getGrantedById()));
|
data.put("revokedAt", value.getRevokedAt());
|
data.put("revokedByType", value.getRevokedByType());
|
data.put("revokedById", value.getRevokedById());
|
data.put("revokedByName", displays.user(value.getRevokedById()));
|
data.put("revokeReason", value.getRevokeReason());
|
data.put("revision", value.getRevision());
|
return data;
|
}
|
|
private static Map<String, Object> exclusionSnapshot(DmsAccessExclusionEntity value,
|
DmsPermissionAuditDisplayResolver.DisplayValues displays) {
|
Map<String, Object> data = new LinkedHashMap<>();
|
data.put("exclusionId", value(value.getId()));
|
data.put("resourceType", value.getResourceType());
|
data.put("resourceId", value(value.getResourceId()));
|
data.put("resourceName", displays.resource(value.getResourceType(), value.getResourceId()));
|
data.put("userId", value.getUserId());
|
data.put("userName", displays.user(value.getUserId()));
|
data.put("actionCode", value.getActionCode());
|
data.put("actionName", optionDisplay(EXCLUSION_FIELD_META.get("action_code"), value.getActionCode()));
|
data.put("sourceType", value.getSourceType());
|
data.put("sourceId", value.getSourceId());
|
data.put("validFrom", value.getValidFrom());
|
data.put("validTo", value.getValidTo());
|
data.put("excludeReason", value.getExcludeReason());
|
data.put("excludedByType", value.getExcludedByType());
|
data.put("excludedById", value.getExcludedById());
|
data.put("excludedByName", displays.user(value.getExcludedById()));
|
data.put("revokedAt", value.getRevokedAt());
|
data.put("revokedByType", value.getRevokedByType());
|
data.put("revokedById", value.getRevokedById());
|
data.put("revokedByName", displays.user(value.getRevokedById()));
|
data.put("revokeReason", value.getRevokeReason());
|
data.put("revision", value.getRevision());
|
return data;
|
}
|
|
private static Map<String, Object> permissionData(DmsFilePermissionEntity value) {
|
if (value == null) return null;
|
Map<String, Object> data = new LinkedHashMap<>();
|
data.put("resource_type", value.getResourceType());
|
data.put("resource_id", value.getResourceId());
|
data.put("resource_scope", value.getResourceScope());
|
data.put("principal_type", value.getPrincipalType());
|
data.put("principal_id", value.getPrincipalId());
|
data.put("include_child_orgs", value.getIncludeChildOrgs());
|
if (value.getApprovedOrgScope() != null) data.put("approved_org_scope", value.getApprovedOrgScope());
|
data.put("action_code", value.getActionCode());
|
data.put("source_type", value.getSourceType());
|
data.put("source_id", value.getSourceId());
|
data.put("valid_from", value.getValidFrom());
|
data.put("valid_to", value.getValidTo());
|
data.put("max_use_count", value.getMaxUseCount());
|
data.put("used_count", value.getUsedCount());
|
data.put("grant_reason", value.getGrantReason());
|
data.put("granted_by_type", value.getGrantedByType());
|
data.put("granted_by_id", value.getGrantedById());
|
data.put("revoked_at", value.getRevokedAt());
|
data.put("revoked_by_type", value.getRevokedByType());
|
data.put("revoked_by_id", value.getRevokedById());
|
data.put("revoke_reason", value.getRevokeReason());
|
data.put("revision", value.getRevision());
|
return data;
|
}
|
|
private static Map<String, Object> exclusionData(DmsAccessExclusionEntity value) {
|
if (value == null) return null;
|
Map<String, Object> data = new LinkedHashMap<>();
|
data.put("resource_type", value.getResourceType());
|
data.put("resource_id", value.getResourceId());
|
data.put("user_id", value.getUserId());
|
data.put("action_code", value.getActionCode());
|
data.put("source_type", value.getSourceType());
|
data.put("source_id", value.getSourceId());
|
data.put("valid_from", value.getValidFrom());
|
data.put("valid_to", value.getValidTo());
|
data.put("exclude_reason", value.getExcludeReason());
|
data.put("excluded_by_type", value.getExcludedByType());
|
data.put("excluded_by_id", value.getExcludedById());
|
data.put("revoked_at", value.getRevokedAt());
|
data.put("revoked_by_type", value.getRevokedByType());
|
data.put("revoked_by_id", value.getRevokedById());
|
data.put("revoke_reason", value.getRevokeReason());
|
data.put("revision", value.getRevision());
|
return data;
|
}
|
|
private static String permissionTitle(DmsFilePermissionEntity value,
|
DmsPermissionAuditDisplayResolver.DisplayValues displays) {
|
return permissionTitle(value, displays,
|
optionDisplay(PERMISSION_FIELD_META.get("action_code"), value.getActionCode()));
|
}
|
|
private static String permissionTitle(DmsFilePermissionEntity value,
|
DmsPermissionAuditDisplayResolver.DisplayValues displays,
|
String actionDisplay) {
|
return displays.resource(value.getResourceType(), value.getResourceId()) + " - "
|
+ displays.principal(value.getPrincipalType(), value.getPrincipalId()) + " - "
|
+ actionDisplay;
|
}
|
|
private static PermissionActionSummary permissionActions(List<DmsFilePermissionEntity> values) {
|
LinkedHashSet<String> remaining = new LinkedHashSet<>();
|
for (DmsFilePermissionEntity item : values) {
|
if (item != null && item.getActionCode() != null && !item.getActionCode().trim().isEmpty()) {
|
remaining.add(item.getActionCode().trim());
|
}
|
}
|
List<String> codes = new ArrayList<>();
|
for (String known : new String[]{"CREATE", "READ", "PRINT", "DOWNLOAD", "UPDATE", "DELETE"}) {
|
if (remaining.remove(known)) codes.add(known);
|
}
|
codes.addAll(remaining);
|
List<String> names = new ArrayList<>();
|
for (String code : codes) names.add(optionDisplay(PERMISSION_FIELD_META.get("action_code"), code));
|
return new PermissionActionSummary(codes, names);
|
}
|
|
private static void combineActionDiff(List<Map<String, Object>> diffs, PermissionActionSummary actions) {
|
for (Map<String, Object> diff : diffs) {
|
if (!"action_code".equals(diff.get("field"))) continue;
|
diff.put("newData", actions.raw);
|
diff.put("newDisplay", actions.display);
|
return;
|
}
|
}
|
|
private static void combineActionSnapshot(Map<String, Object> snapshot, PermissionActionSummary actions) {
|
snapshot.put("actionCode", actions.raw);
|
snapshot.put("actionName", actions.display);
|
snapshot.put("actionCodes", actions.codes);
|
snapshot.put("actionNames", actions.names);
|
}
|
|
private static String exclusionTitle(DmsAccessExclusionEntity value,
|
DmsPermissionAuditDisplayResolver.DisplayValues displays) {
|
return displays.resource(value.getResourceType(), value.getResourceId()) + " - "
|
+ displays.user(value.getUserId()) + " - 排除查看";
|
}
|
|
private DmsPermissionAuditDisplayResolver.DisplayValues permissionDisplays(
|
List<DmsFilePermissionEntity> values) {
|
DmsPermissionAuditDisplayResolver resolver = permissionDisplayResolver == null
|
? null : permissionDisplayResolver.getIfAvailable();
|
return resolver == null ? new DmsPermissionAuditDisplayResolver.DisplayValues()
|
: resolver.resolvePermissions(values.get(0).getTenantId(), values);
|
}
|
|
private DmsPermissionAuditDisplayResolver.DisplayValues exclusionDisplays(
|
List<DmsAccessExclusionEntity> values) {
|
DmsPermissionAuditDisplayResolver resolver = permissionDisplayResolver == null
|
? null : permissionDisplayResolver.getIfAvailable();
|
return resolver == null ? new DmsPermissionAuditDisplayResolver.DisplayValues()
|
: resolver.resolveExclusions(values.get(0).getTenantId(), values);
|
}
|
|
private static List<DmsFilePermissionEntity> retainedPermissions(List<DmsFilePermissionEntity> before,
|
List<DmsFilePermissionEntity> after) {
|
return after == null || after.isEmpty() ? orEmpty(before) : after;
|
}
|
|
private static List<DmsAccessExclusionEntity> retainedExclusions(List<DmsAccessExclusionEntity> before,
|
List<DmsAccessExclusionEntity> after) {
|
return after == null || after.isEmpty() ? orEmpty(before) : after;
|
}
|
|
private static Map<UUID, DmsFilePermissionEntity> permissionsById(List<DmsFilePermissionEntity> values) {
|
Map<UUID, DmsFilePermissionEntity> result = new HashMap<>();
|
for (DmsFilePermissionEntity value : orEmpty(values)) result.put(value.getId(), value);
|
return result;
|
}
|
|
private static Map<UUID, DmsAccessExclusionEntity> exclusionsById(List<DmsAccessExclusionEntity> values) {
|
Map<UUID, DmsAccessExclusionEntity> result = new HashMap<>();
|
for (DmsAccessExclusionEntity value : orEmpty(values)) result.put(value.getId(), value);
|
return result;
|
}
|
|
private static <T> List<T> orEmpty(List<T> values) {
|
return values == null ? Collections.emptyList() : values;
|
}
|
|
private static String resourceTable(String type) {
|
if ("FOLDER".equals(type)) return FOLDER_TABLE;
|
if ("DOCUMENT".equals(type)) return FILE_TABLE;
|
if ("FILE_VERSION".equals(type)) return FILE_VERSION_TABLE;
|
return "dms_permission_resources";
|
}
|
|
private interface DisplayLookup {
|
String display(String field, String raw, boolean oldValue);
|
}
|
|
private static final class PermissionActionSummary {
|
private final List<String> codes;
|
private final List<String> names;
|
private final String raw;
|
private final String display;
|
|
private PermissionActionSummary(List<String> codes, List<String> names) {
|
this.codes = codes;
|
this.names = names;
|
this.raw = String.join(",", codes);
|
this.display = String.join("、", names);
|
}
|
}
|
|
private static Map<String, Object> recycleDetailDiffData(
|
DmsWenjianHuishouMingxiEntity detail) {
|
Map<String, Object> data = new LinkedHashMap<>();
|
data.put("f_foreign_id", detail.getForeignId());
|
data.put("dayin_jilu_id", detail.getDayinJiluId());
|
data.put("dayin_liushuihao", detail.getDayinLiushuihao());
|
data.put("wenjian_bianhao", detail.getWenjianBianhao());
|
data.put("wenjian_mingcheng", detail.getWenjianMingcheng());
|
data.put("banbenhao", detail.getBanbenhao());
|
data.put("qianshou_bumen", detail.getQianshouBumen());
|
data.put("qianshou_ren", detail.getQianshouRen());
|
data.put("qianshou_shijian", detail.getQianshouShijian());
|
data.put("huishou_ren_id", detail.getHuishouRenId());
|
data.put("huishou_shijian", detail.getHuishouShijian());
|
return data;
|
}
|
|
private static Map<String, Object> recycleTarget(DmsWenjianHuishouMingxiEntity detail, String beforeStatus) {
|
Map<String, Object> data = new LinkedHashMap<>();
|
data.put("detailId", detail.getId());
|
data.put("dayinJiluId", detail.getDayinJiluId());
|
data.put("dayinLiushuihao", detail.getDayinLiushuihao());
|
data.put("wenjianBianhao", detail.getWenjianBianhao());
|
data.put("wenjianMingcheng", detail.getWenjianMingcheng());
|
data.put("banbenhao", detail.getBanbenhao());
|
data.put("qianshouBumen", detail.getQianshouBumen());
|
data.put("qianshouRen", detail.getQianshouRen());
|
data.put("qianshouShijian", detail.getQianshouShijian());
|
data.put("oldStatus", beforeStatus);
|
data.put("newStatus", "daixiaohui");
|
data.put("recordTitle", recycleDetailTitle(detail));
|
return data;
|
}
|
|
private static Map<String, Object> recycleSnapshot(DmsWenjianHuishouAuditEvent event,
|
List<Map<String, Object>> targets) {
|
Map<String, Object> data = new LinkedHashMap<>();
|
data.put("huishouDanjuId", event.getHuishouDanjuId());
|
data.put("beizhuShuoming", event.getBeizhuShuoming());
|
data.put("huishouRenId", event.getHuishouRenId());
|
data.put("huishouShijian", event.getHuishouShijian());
|
data.put("huishouFenshu", targets.size());
|
data.put("huishouMingxi", targets);
|
return data;
|
}
|
|
private static String recycleTitle(List<DmsWenjianHuishouMingxiEntity> details) {
|
String first = recycleDetailTitle(details.get(0));
|
return details.size() > 1 ? first + "(共 " + details.size() + " 份)" : first;
|
}
|
|
private static String recycleDetailTitle(DmsWenjianHuishouMingxiEntity detail) {
|
String document = "[" + detail.getWenjianBianhao() + "] " + detail.getWenjianMingcheng();
|
return document + " - " + detail.getBanbenhao() + " / " + detail.getDayinLiushuihao();
|
}
|
|
private static Map<String, Object> printRecordDiffData(DmsWenjianDayinJiluEntity record) {
|
Map<String, Object> data = new LinkedHashMap<>();
|
data.put("dayin_renwu_id", record.getDayinRenwuId());
|
data.put("dayin_xuhao", record.getDayinXuhao());
|
data.put("dayin_liushuihao", record.getDayinLiushuihao());
|
data.put("dayin_jishu_liushuihao", record.getDayinJishuLiushuihao());
|
data.put("dayin_ren_id", record.getDayinRenId());
|
data.put("dayin_bumen_id", record.getDayinBumenId());
|
data.put("dayin_shijian", record.getDayinShijian());
|
data.put("biz_status", record.getBizStatus());
|
return data;
|
}
|
|
private static Map<String, Object> distributionTaskDiffData(
|
DmsWenjianFenfaRenwuEntity task) {
|
Map<String, Object> data = new LinkedHashMap<>();
|
data.put("dayin_renwu_id", task.getDayinRenwuId());
|
data.put("fenfa_bianhao", task.getFenfaBianhao());
|
data.put("fafang_leixing", task.getFafangLeixing());
|
data.put("biz_status", task.getBizStatus());
|
return data;
|
}
|
|
private static Map<String, Object> printTarget(DmsWenjianDayinRenwuEntity task,
|
List<DmsWenjianDayinJiluEntity> records) {
|
Map<String, Object> data = new LinkedHashMap<>();
|
List<String> recordIds = new ArrayList<>(records.size());
|
List<String> serialNumbers = new ArrayList<>(records.size());
|
for (DmsWenjianDayinJiluEntity record : records) {
|
recordIds.add(record.getId());
|
serialNumbers.add(record.getDayinLiushuihao());
|
}
|
data.put("id", task.getId());
|
data.put("bizCode", task.getWenjianBianhao());
|
data.put("title", printTaskTitle(task));
|
data.put("wenjianId", task.getWenjianId());
|
data.put("version", task.getBanbenhao());
|
data.put("copies", records.size());
|
data.put("printRecordIds", recordIds);
|
data.put("serialNumbers", serialNumbers);
|
return data;
|
}
|
|
private static Map<String, Object> printSnapshot(List<Map<String, Object>> targets,
|
int totalCopies) {
|
Map<String, Object> data = new LinkedHashMap<>();
|
data.put("printTaskCount", targets.size());
|
data.put("totalCopies", totalCopies);
|
data.put("printTargets", targets);
|
return data;
|
}
|
|
private static String printTitle(List<DmsWenjianDayinRenwuEntity> tasks, int totalCopies) {
|
String first = printTaskTitle(tasks.get(0));
|
if (tasks.size() > 1) {
|
return first + "(批量打印 " + tasks.size() + " 个任务,共 " + totalCopies + " 份)";
|
}
|
return first + "(打印 " + totalCopies + " 份)";
|
}
|
|
private static String printTaskTitle(DmsWenjianDayinRenwuEntity task) {
|
return "[" + task.getWenjianBianhao() + "] " + task.getWenjianMingcheng()
|
+ " - " + task.getBanbenhao();
|
}
|
|
private PrintMenuContext resolvePrintMenu() {
|
String menuId;
|
try {
|
HttpServletRequest request = ServletUtil.getRequest();
|
if (request == null) return PrintMenuContext.missing();
|
menuId = textOrNull(request.getHeader("Jnpf-Menu-Id"));
|
if (menuId != null && menuId.length() > 100) {
|
return PrintMenuContext.invalid();
|
}
|
} catch (Throwable ex) {
|
log.warn("DMS print audit cannot read menu header", ex);
|
return PrintMenuContext.invalid();
|
}
|
if (menuId == null) return PrintMenuContext.missing();
|
|
long now = System.currentTimeMillis();
|
MenuCacheEntry cached = menuCache.get(menuId);
|
if (cached != null && cached.expiresAt > now) {
|
return PrintMenuContext.resolved(menuId, cached.menuName);
|
}
|
if (cached != null) menuCache.remove(menuId, cached);
|
|
ModuleApi resolver = moduleApi == null ? null : moduleApi.getIfAvailable();
|
if (resolver == null) return PrintMenuContext.failed(menuId);
|
try {
|
ModuleEntity menu = resolver.getModuleByList(menuId);
|
String menuName = menu == null || Integer.valueOf(1).equals(menu.getDeleteMark())
|
? null : textOrNull(menu.getFullName());
|
if (menuName == null) return PrintMenuContext.unresolved(menuId);
|
menuCache.put(menuId, new MenuCacheEntry(menuName, now + MENU_CACHE_MILLIS));
|
return PrintMenuContext.resolved(menuId, menuName);
|
} catch (Throwable ex) {
|
log.warn("DMS print audit cannot resolve menu menuId={}", menuId, ex);
|
return PrintMenuContext.failed(menuId);
|
}
|
}
|
|
private static String textOrNull(String value) {
|
if (value == null) return null;
|
String trimmed = value.trim();
|
return trimmed.isEmpty() ? null : trimmed;
|
}
|
|
private static boolean blank(String value) {
|
return value == null || value.trim().isEmpty();
|
}
|
|
private static final class PrintMenuContext {
|
private final String menuId;
|
private final String menuName;
|
private final String resolution;
|
|
private PrintMenuContext(String menuId, String menuName, String resolution) {
|
this.menuId = menuId;
|
this.menuName = menuName;
|
this.resolution = resolution;
|
}
|
|
private static PrintMenuContext resolved(String menuId, String menuName) {
|
return new PrintMenuContext(menuId, menuName, "RESOLVED");
|
}
|
|
private static PrintMenuContext unresolved(String menuId) {
|
return new PrintMenuContext(menuId, null, "UNRESOLVED");
|
}
|
|
private static PrintMenuContext failed(String menuId) {
|
return new PrintMenuContext(menuId, null, "FAILED");
|
}
|
|
private static PrintMenuContext missing() {
|
return new PrintMenuContext(null, null, "MISSING");
|
}
|
|
private static PrintMenuContext invalid() {
|
return new PrintMenuContext(null, null, "INVALID");
|
}
|
}
|
|
private static final class MenuCacheEntry {
|
private final String menuName;
|
private final long expiresAt;
|
|
private MenuCacheEntry(String menuName, long expiresAt) {
|
this.menuName = menuName;
|
this.expiresAt = expiresAt;
|
}
|
}
|
|
private void record(String eventType, String actionCode, String actionLabel, String table, String id,
|
String reason, String extra) {
|
auditClient.ifAvailable(client -> client.record(AuditEventDTO.builder()
|
.eventType(eventType)
|
.actionCode(actionCode)
|
.actionLabel(actionLabel)
|
.bizModule("DMS_PERMISSION")
|
.bizType("DMS_PERMISSION")
|
.targetTable(table)
|
.targetId(id)
|
.reason(reason)
|
.extra(extra)
|
.sourceLayer(3)
|
.build()));
|
}
|
|
private static Map<String, Object> diffData(DmsFolderAuditSnapshot snapshot) {
|
if (snapshot == null) {
|
return null;
|
}
|
Map<String, Object> data = new LinkedHashMap<>();
|
data.put("name", snapshot.getName());
|
data.put("parent_id", value(snapshot.getParentId()));
|
return data;
|
}
|
|
@SuppressWarnings("unchecked")
|
private static void addScopedDiffs(List<Map<String, Object>> combined, String table, String id,
|
Map<String, Object> before, Map<String, Object> after) {
|
addScopedDiffs(combined, table, id, before, after, FILE_FIELD_META);
|
}
|
|
@SuppressWarnings("unchecked")
|
private static void addScopedDiffs(List<Map<String, Object>> combined, String table, String id,
|
Map<String, Object> before, Map<String, Object> after,
|
Map<String, AuditFieldDiff.FieldMeta> fieldMeta) {
|
List<Map> items = JSON.parseArray(AuditFieldDiff.diff(before, after,
|
Collections.emptySet(), fieldMeta), Map.class);
|
for (Map item : items) {
|
Map<String, Object> scoped = new LinkedHashMap<>();
|
scoped.put("targetTable", table);
|
scoped.put("targetId", id);
|
scoped.putAll(item);
|
combined.add(scoped);
|
}
|
}
|
|
private static Map<String, Object> documentDiffData(DmsFileAuditSnapshot snapshot) {
|
if (snapshot == null) return null;
|
Map<String, Object> data = new LinkedHashMap<>();
|
data.put("document_no", snapshot.getDocumentNo());
|
data.put("name", snapshot.getDocumentName());
|
data.put("document_type", snapshot.getDocumentType());
|
data.put("responsible_dept_id", snapshot.getResponsibleDeptId());
|
data.put("folder_id", value(snapshot.getFolderId()));
|
return data;
|
}
|
|
private static Map<String, Object> versionDiffData(DmsFileVersionAuditSnapshot snapshot) {
|
if (snapshot == null) return null;
|
Map<String, Object> data = new LinkedHashMap<>();
|
data.put("version_no", snapshot.getVersionNo());
|
data.put("version_label", snapshot.getVersionLabel());
|
data.put("file_name", snapshot.getFileName());
|
data.put("mime_type", snapshot.getMimeType());
|
data.put("size_bytes", snapshot.getSizeBytes());
|
data.put("checksum_sha256", snapshot.getChecksumSha256());
|
data.put("prepared_by", snapshot.getPreparedBy());
|
data.put("prepared_on", snapshot.getPreparedOn());
|
return data;
|
}
|
|
private static Map<String, Object> stateDiffData(DmsFileVersionAuditSnapshot snapshot) {
|
if (snapshot == null) return null;
|
Map<String, Object> data = new LinkedHashMap<>();
|
data.put("lifecycle_status", snapshot.getLifecycleStatus());
|
data.put("effective_from", snapshot.getEffectiveFrom());
|
data.put("effective_to", snapshot.getEffectiveTo());
|
data.put("status_reason", snapshot.getStatusReason());
|
return data;
|
}
|
|
private static Map<String, Object> fileSnapshotData(DmsFileAuditSnapshot snapshot) {
|
if (snapshot == null) return Collections.emptyMap();
|
Map<String, Object> data = new LinkedHashMap<>();
|
data.put("documentId", value(snapshot.getDocumentId()));
|
data.put("documentNo", snapshot.getDocumentNo());
|
data.put("documentName", snapshot.getDocumentName());
|
data.put("documentType", snapshot.getDocumentType());
|
data.put("folderId", value(snapshot.getFolderId()));
|
data.put("folderPath", snapshot.getFolderPath());
|
data.put("responsibleDeptId", snapshot.getResponsibleDeptId());
|
DmsFileVersionAuditSnapshot version = snapshot.getCurrentVersion();
|
if (version != null) {
|
Map<String, Object> current = new LinkedHashMap<>();
|
current.put("fileVersionId", value(version.getFileVersionId()));
|
current.put("versionNo", version.getVersionNo());
|
current.put("versionLabel", version.getVersionLabel());
|
current.put("fileName", version.getFileName());
|
current.put("mimeType", version.getMimeType());
|
current.put("sizeBytes", version.getSizeBytes());
|
current.put("checksumSha256", version.getChecksumSha256());
|
current.put("preparedBy", version.getPreparedBy());
|
current.put("preparedOn", version.getPreparedOn());
|
current.put("lifecycleStatus", version.getLifecycleStatus());
|
current.put("effectiveFrom", version.getEffectiveFrom());
|
current.put("effectiveTo", version.getEffectiveTo());
|
current.put("statusReason", version.getStatusReason());
|
data.put("currentVersion", current);
|
}
|
return data;
|
}
|
|
private static String fileTitle(DmsFileAuditSnapshot before, DmsFileAuditSnapshot after,
|
String fallback) {
|
DmsFileAuditSnapshot retained = after == null ? before : after;
|
if (retained == null) return fallback;
|
String base = "[" + retained.getDocumentNo() + "] " + retained.getDocumentName();
|
DmsFileVersionAuditSnapshot oldVersion = before == null ? null : before.getCurrentVersion();
|
DmsFileVersionAuditSnapshot newVersion = after == null ? null : after.getCurrentVersion();
|
if (oldVersion != null && newVersion != null
|
&& !oldVersion.getFileVersionId().equals(newVersion.getFileVersionId())) {
|
return base + ":" + oldVersion.getVersionLabel() + " -> " + newVersion.getVersionLabel();
|
}
|
return newVersion == null ? base : base + " - " + newVersion.getVersionLabel();
|
}
|
|
private static AuditFieldDiff.FieldMeta meta(String label) {
|
return new AuditFieldDiff.FieldMeta(label, null, null);
|
}
|
|
private static AuditFieldDiff.FieldMeta booleanMeta(String label) {
|
return options(label, "true", "是", "false", "否");
|
}
|
|
private static AuditFieldDiff.FieldMeta actionMeta() {
|
return options("权限动作", "CREATE", "新建", "READ", "查看", "PRINT", "打印",
|
"DOWNLOAD", "下载", "UPDATE", "修改", "DELETE", "删除");
|
}
|
|
private static AuditFieldDiff.FieldMeta sourceTypeMeta() {
|
return options("授权来源", "MANUAL", "手工", "WORKFLOW", "工作流", "SYSTEM", "系统",
|
"MIGRATION", "数据迁移");
|
}
|
|
private static AuditFieldDiff.FieldMeta options(String label, String... values) {
|
Map<String, String> labels = new LinkedHashMap<>();
|
for (int i = 0; i + 1 < values.length; i += 2) labels.put(values[i], values[i + 1]);
|
return new AuditFieldDiff.FieldMeta(label, null, null, "string", "select", null, labels);
|
}
|
|
private static AuditFieldDiff.FieldMeta lifecycleStatusMeta() {
|
Map<String, String> labels = new LinkedHashMap<>();
|
labels.put("PENDING_EFFECTIVE", "待生效");
|
labels.put("EFFECTIVE", "生效");
|
labels.put("SUPERSEDED", "已替代");
|
labels.put("EXPIRED", "已过期");
|
labels.put("REVOKED", "已撤销");
|
labels.put("INVALIDATED", "已失效");
|
labels.put("OBSOLETE", "已作废");
|
return new AuditFieldDiff.FieldMeta("生命周期状态", null, null,
|
"string", null, null, labels);
|
}
|
|
private static Map<String, Object> snapshotData(DmsFolderAuditSnapshot snapshot) {
|
if (snapshot == null) {
|
return Collections.emptyMap();
|
}
|
Map<String, Object> data = new LinkedHashMap<>();
|
data.put("id", value(snapshot.getId()));
|
data.put("name", snapshot.getName());
|
data.put("parentId", value(snapshot.getParentId()));
|
data.put("parentName", snapshot.getParentName());
|
data.put("folderPath", snapshot.getPath());
|
data.put("revision", snapshot.getRevision());
|
return data;
|
}
|
|
private static String title(DmsFolderAuditSnapshot before, DmsFolderAuditSnapshot after,
|
String fallback) {
|
DmsFolderAuditSnapshot snapshot = after == null ? before : after;
|
if (snapshot == null) {
|
return fallback;
|
}
|
if (snapshot.getPath() != null && !snapshot.getPath().trim().isEmpty()) {
|
return snapshot.getPath();
|
}
|
return snapshot.getName() == null ? fallback : snapshot.getName();
|
}
|
|
private static String value(Object value) {
|
return value == null ? null : String.valueOf(value);
|
}
|
}
|