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" | "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 boolean> = { overview: isStaff, todos: isAdvisor, customers: isAdvisor, drafts: isAdvisor, visits: isAdvisor, risk: isRiskOfficer, workorders: isRiskOfficer, funds: 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; }