审批流程管理(系统管理员)+ 系统管理员去首页

- 审批规则引擎 evaluateApproval(纯函数+8单测):有序条件规则决定风控通过后是否需管理层终审
- approval_config 表 + 持久化 + 默认规则种子(红线/不可接受/高风险/低毛利/大合同→需管理层;低风险达标→风控终批)
- 风控审核接入规则:低风险达标可风控终批,否则转管理层;审计记录命中规则
- GET/PUT /api/approval-config(PUT 限系统管理员);overdue SLA 改为读配置
- 审批流程配置页 WorkflowManagement(全局SLA/默认/驳回去向 + 规则与条件编辑器)
- 系统管理员去掉首页,登录落地用户管理;导航=用户管理+审批流程
This commit is contained in:
freedakgmail
2026-06-13 17:55:28 +08:00
parent 6562208b13
commit 757b9c4a69
11 changed files with 632 additions and 13 deletions
+42
View File
@@ -844,6 +844,48 @@ export async function deleteUserApi(id: string): Promise<void> {
await fetch(`${API_BASE}/api/users/${encodeURIComponent(id)}`, { method: 'DELETE', headers: authHeader() });
}
/* ----------------------------- 审批流程配置 ----------------------------- */
export type ApprovalField = 'riskGrade' | 'netMarginPct' | 'acceptability' | 'redlineHit' | 'contractAmount' | 'businessType';
export type ApprovalOp = '>=' | '<=' | '>' | '<' | '==' | '!=';
export interface ApprovalCondition {
field: ApprovalField;
op: ApprovalOp;
value: string | number | boolean;
}
export interface ApprovalRule {
id: string;
name: string;
enabled: boolean;
requireManagement: boolean;
conditions: ApprovalCondition[];
}
export interface ApprovalConfig {
defaultRequireManagement: boolean;
slaRiskHours: number;
slaMgmtHours: number;
rejectTo: 'origin' | 'risk';
rules: ApprovalRule[];
}
/** 读取审批流程配置。 */
export async function fetchApprovalConfig(): Promise<ApprovalConfig> {
return request<ApprovalConfig>('GET', '/api/approval-config');
}
/** 保存审批流程配置(系统管理员)。 */
export async function saveApprovalConfig(config: ApprovalConfig): Promise<ApprovalConfig> {
const res = await fetch(`${API_BASE}/api/approval-config`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json', ...authHeader() },
body: JSON.stringify(config),
});
const data = await res.json().catch(() => ({}));
if (!res.ok) throw new ApiError(res.status, typeof data.error === 'string' ? data.error : `HTTP ${res.status}`);
return data as ApprovalConfig;
}
/** 方案对比。 */
export interface ScenarioItem {
id: string;