118 lines
3.7 KiB
TypeScript
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))
|
|
);
|
|
}
|