「权限 × 状态 × 审计」联合约束机制

This commit is contained in:
Cheng Zhou
2025-12-17 21:48:29 +08:00
parent 13efb0a3a9
commit 4202ed7922
36 changed files with 714 additions and 85 deletions
+25
View File
@@ -32,4 +32,29 @@ export const auditDict: Record<
actionText: "更新了风险/问题状态",
targetLabel: "风险/问题",
},
UNAUTHORIZED_ACTION_ATTEMPT: {
label: "越权操作尝试",
actionText: "尝试执行无权限操作",
targetLabel: "系统资源",
},
INVALID_STATE_TRANSITION_ATTEMPT: {
label: "非法状态流转尝试",
actionText: "尝试执行非法状态流转",
targetLabel: "业务对象",
},
PROJECT_MEMBER_UPDATED: {
label: "项目成员变更",
actionText: "调整了项目成员",
targetLabel: "项目成员",
},
SITE_CRA_BOUND: {
label: "中心 CRA 绑定",
actionText: "调整了中心与 CRA 的绑定",
targetLabel: "中心",
},
SITE_STATUS_CHANGED: {
label: "中心状态变更",
actionText: "更新了中心信息",
targetLabel: "中心",
},
};
+46 -2
View File
@@ -1,5 +1,7 @@
import { auditDict } from "./auditDict";
import { roleDict, getDictLabel, statusDict } from "../dictionaries";
import { useAuthStore } from "../store/auth";
import { useStudyStore } from "../store/study";
export interface AuditEvent {
eventType: string;
@@ -77,5 +79,47 @@ export const normalizeAuditEvent = (raw: any, userMap: Record<string, string>):
export { auditDict };
// 前端占位的 logAudit:不修改后端逻辑,失败不影响主流程
export const logAudit = async (_eventType: string, _payload: Record<string, any>) => Promise.resolve();
const LOCAL_KEY_PREFIX = "audit_local_";
const getLocalKey = (studyId?: string | null) => `${LOCAL_KEY_PREFIX}${studyId || "global"}`;
const appendLocalAudit = (studyId: string | null, log: any) => {
try {
const key = getLocalKey(studyId);
const existingRaw = localStorage.getItem(key);
const existing = existingRaw ? JSON.parse(existingRaw) : [];
existing.unshift(log);
localStorage.setItem(key, JSON.stringify(existing.slice(0, 200)));
} catch {
/* ignore */
}
};
// 前端占位的 logAudit:不修改后端接口,落地到本地存储,失败不影响主流程
export const logAudit = async (eventType: string, payload: Record<string, any>) => {
try {
const auth = useAuthStore();
const study = useStudyStore();
const user = auth.user;
const studyId = study.currentStudy?.id || null;
const log = {
action: eventType,
operator_id: user?.id || user?.username || "current_user",
operator_role: user?.role || "",
entity_type: payload.targetType || "client",
entity_id: payload.targetId || "",
created_at: new Date().toISOString(),
detail: JSON.stringify({
targetName: payload.targetName,
before: payload.before,
after: payload.after,
reason: payload.reason,
severity: payload.severity,
result: payload.severity === "normal" ? "SUCCESS" : "FAIL",
}),
};
appendLocalAudit(studyId, log);
} catch {
/* ignore */
}
return Promise.resolve();
};