「权限 × 状态 × 审计」联合约束机制
This commit is contained in:
@@ -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: "中心",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user