Files

118 lines
3.7 KiB
TypeScript

import type { UserPayload } from "@/types/api";
/**
* 后端账号取值契约(与 common_const §13 对齐,数据库存值不改动)。
* 前端只做展示映射和菜单显隐,不修改后端权限判断 —— 真正的鉴权仍在服务端。
*/
/** sys_user.user_type:建表枚举,沿用 DDL 定义。 */
export const USER_TYPE = {
CUSTOMER: "CUSTOMER",
EMPLOYEE: "EMPLOYEE",
ADMIN: "ADMIN",
} as const;
/** sys_user.employee_role:中文,与既有数据一致。 */
export const EMPLOYEE_ROLE = {
ADMIN: "管理员",
ADVISOR: "投顾",
RISK: "风控专员",
CUSTOMER_MANAGER: "客户经理",
} as const;
/** 统一工作台上可切换的功能区。 */
export type WorkspaceSection =
| "overview"
| "todos"
| "customers"
| "drafts"
| "visits"
| "risk"
| "workorders"
| "funds"
| "nl2sql"
| "admin";
/**
* 左侧「工作分区」分组。侧边栏按分区归组展示,而不是平铺所有功能区,
* 员工能一眼看出自己手头有几类活。
*/
export interface WorkspaceZone {
id: string;
label: string;
sections: WorkspaceSection[];
}
export const WORKSPACE_ZONES: WorkspaceZone[] = [
{ id: "home", label: "概览", sections: ["overview"] },
{ id: "advisor", label: "投顾作业", sections: ["todos", "customers", "drafts", "visits"] },
{ id: "risk", label: "风控处置", sections: ["risk", "workorders"] },
{ id: "data", label: "数据工具", sections: ["funds", "nl2sql"] },
{ id: "system", label: "系统管理", sections: ["admin"] },
];
/** 管理员:账号类型为 ADMIN,或员工账号且角色为「管理员」。 */
export function isAdmin(user?: UserPayload | null): boolean {
if (!user) return false;
if (user.user_type === USER_TYPE.ADMIN) return true;
return user.user_type === USER_TYPE.EMPLOYEE && user.employee_role === EMPLOYEE_ROLE.ADMIN;
}
export function isEmployee(user?: UserPayload | null): boolean {
return user?.user_type === USER_TYPE.EMPLOYEE;
}
/** 是否能进入员工后台(管理员视同拥有全部员工权限)。 */
export function isStaff(user?: UserPayload | null): boolean {
return isAdmin(user) || isEmployee(user);
}
/** 员工角色命中即通过;管理员一律放行。 */
function hasEmployeeRole(user: UserPayload | null | undefined, role: string): boolean {
if (isAdmin(user)) return true;
return isEmployee(user) && user?.employee_role === role;
}
export function isAdvisor(user?: UserPayload | null): boolean {
return hasEmployeeRole(user, EMPLOYEE_ROLE.ADVISOR);
}
export function isRiskOfficer(user?: UserPayload | null): boolean {
return hasEmployeeRole(user, EMPLOYEE_ROLE.RISK);
}
/**
* 各功能区所需权限。
* 不满足时不整页拒绝,而是在该区域内渲染「无权限」提示,员工仍可使用其它有权限的模块。
*/
const SECTION_GUARD: Record<WorkspaceSection, (user?: UserPayload | null) => boolean> = {
overview: isStaff,
todos: isAdvisor,
customers: isAdvisor,
drafts: isAdvisor,
visits: isAdvisor,
risk: isRiskOfficer,
workorders: isRiskOfficer,
funds: isStaff,
nl2sql: isStaff,
admin: isAdmin,
};
export function canUseSection(
user: UserPayload | null | undefined,
section: WorkspaceSection
): boolean {
return SECTION_GUARD[section]?.(user) ?? false;
}
export function isWorkspaceSection(value: string | null): value is WorkspaceSection {
return !!value && value in SECTION_GUARD;
}
/** 分区下所有功能区都无权限时整组隐藏,避免侧边栏出现全是灰标的空分区。 */
export function visibleZones(user: UserPayload | null | undefined): WorkspaceZone[] {
return WORKSPACE_ZONES.filter((zone) =>
zone.sections.some((section) => canUseSection(user, section))
);
}