137 lines
4.5 KiB
TypeScript
137 lines
4.5 KiB
TypeScript
|
|
/**
|
|||
|
|
* 功能:定义工作人员关联字段的显式过滤、角色展示与资质来源校验策略。
|
|||
|
|
* 版本:v1.0.0
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
export const staffRoleCodes = ['installer', 'delivery', 'operations'] as const;
|
|||
|
|
|
|||
|
|
export type StaffRoleCode = (typeof staffRoleCodes)[number];
|
|||
|
|
|
|||
|
|
export type StaffRelationPolicy = {
|
|||
|
|
roles: readonly StaffRoleCode[] | 'context';
|
|||
|
|
enabledOnly?: boolean;
|
|||
|
|
workStatus?: 'on_duty' | 'off_duty';
|
|||
|
|
lockPrefilled?: boolean;
|
|||
|
|
showIdentityCopy?: boolean;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export type StaffRelationContext = {
|
|||
|
|
staffType?: string;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
type StaffRelationField = {
|
|||
|
|
relation?: string;
|
|||
|
|
staffRelation?: StaffRelationPolicy;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
type StaffOwner = {
|
|||
|
|
identity?: unknown;
|
|||
|
|
role_code?: unknown;
|
|||
|
|
status?: unknown;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const roleLabels: Record<StaffRoleCode, string> = {
|
|||
|
|
installer: '安装人员',
|
|||
|
|
delivery: '配送人员',
|
|||
|
|
operations: '运维人员',
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/** 将外部字符串收敛为工作人员闭集角色。 */
|
|||
|
|
export function normalizeStaffRole(value: unknown): StaffRoleCode | '' {
|
|||
|
|
const role = String(value ?? '').trim();
|
|||
|
|
return staffRoleCodes.includes(role as StaffRoleCode)
|
|||
|
|
? (role as StaffRoleCode)
|
|||
|
|
: '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 返回工作人员角色的中文名称。 */
|
|||
|
|
export function staffRoleLabel(value: unknown) {
|
|||
|
|
const role = normalizeStaffRole(value);
|
|||
|
|
return role ? roleLabels[role] : '未知角色';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 为工作人员关联字段构造服务端过滤条件;缺少显式策略时立即失败,
|
|||
|
|
* 防止业务字段再次继承隐含的配送人员默认值。
|
|||
|
|
*/
|
|||
|
|
export function staffRelationFilters(
|
|||
|
|
field: StaffRelationField,
|
|||
|
|
context: StaffRelationContext = {},
|
|||
|
|
) {
|
|||
|
|
if (field.relation !== '/staff_account') return {};
|
|||
|
|
if (!field.staffRelation) {
|
|||
|
|
throw new Error('工作人员关联字段缺少显式过滤策略');
|
|||
|
|
}
|
|||
|
|
const roles =
|
|||
|
|
field.staffRelation.roles === 'context'
|
|||
|
|
? [normalizeStaffRole(context.staffType)].filter(
|
|||
|
|
(role): role is StaffRoleCode => Boolean(role),
|
|||
|
|
)
|
|||
|
|
: [...field.staffRelation.roles];
|
|||
|
|
if (!roles.length) throw new Error('工作人员关联字段缺少有效角色上下文');
|
|||
|
|
const filters: Record<string, string> =
|
|||
|
|
roles.length === 1
|
|||
|
|
? { role_code: roles[0] }
|
|||
|
|
: { role_codes: roles.join(',') };
|
|||
|
|
if (field.staffRelation.enabledOnly) filters.status = '1';
|
|||
|
|
if (field.staffRelation.workStatus) {
|
|||
|
|
filters.work_status = field.staffRelation.workStatus;
|
|||
|
|
}
|
|||
|
|
return filters;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 从站内返回地址中推断工作人员菜单角色,仅用作显式参数缺失时的回退。 */
|
|||
|
|
export function staffRoleFromReturnPath(returnTo: string) {
|
|||
|
|
let current = returnTo;
|
|||
|
|
for (let depth = 0; depth < 3 && current; depth += 1) {
|
|||
|
|
const url = new URL(current, 'http://codex.local');
|
|||
|
|
const queryRole = normalizeStaffRole(url.searchParams.get('staff_type'));
|
|||
|
|
if (queryRole) return queryRole;
|
|||
|
|
const pathRole = [
|
|||
|
|
['/staff/installers', 'installer'],
|
|||
|
|
['/staff/delivery', 'delivery'],
|
|||
|
|
['/staff/operations', 'operations'],
|
|||
|
|
].find(([path]) => url.pathname.startsWith(path))?.[1];
|
|||
|
|
if (pathRole) return pathRole as StaffRoleCode;
|
|||
|
|
current = url.searchParams.get('return_to') ?? '';
|
|||
|
|
}
|
|||
|
|
return '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 解析资质页面角色来源,并拒绝显式参数与返回路径互相冲突。 */
|
|||
|
|
export function resolveCredentialStaffRole(
|
|||
|
|
explicitRole: unknown,
|
|||
|
|
returnTo: string,
|
|||
|
|
) {
|
|||
|
|
const explicit = normalizeStaffRole(explicitRole);
|
|||
|
|
const inferred = staffRoleFromReturnPath(returnTo);
|
|||
|
|
if (explicit && inferred && explicit !== inferred) {
|
|||
|
|
return { role: '' as const, error: '工作人员角色与来源菜单不一致' };
|
|||
|
|
}
|
|||
|
|
const role = explicit || inferred;
|
|||
|
|
return role
|
|||
|
|
? { role, error: '' }
|
|||
|
|
: {
|
|||
|
|
role: '' as const,
|
|||
|
|
error: '缺少工作人员角色来源,请从工作人员页面进入',
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 校验资质所有者与经服务端查询得到的真实人员记录是否一致。 */
|
|||
|
|
export function credentialOwnerValidationMessage(
|
|||
|
|
ownerIdentity: string,
|
|||
|
|
expectedRole: StaffRoleCode,
|
|||
|
|
owner: StaffOwner | undefined,
|
|||
|
|
) {
|
|||
|
|
if (!ownerIdentity) return '缺少工作人员唯一标识,请从工作人员页面进入';
|
|||
|
|
if (!owner) return '工作人员不存在、已归档或无权访问';
|
|||
|
|
if (String(owner.identity ?? '') !== ownerIdentity) {
|
|||
|
|
return '工作人员唯一标识与查询结果不一致';
|
|||
|
|
}
|
|||
|
|
if (normalizeStaffRole(owner.role_code) !== expectedRole) {
|
|||
|
|
return '工作人员角色已变化,请从当前角色菜单重新进入';
|
|||
|
|
}
|
|||
|
|
if (Number(owner.status) === 3) return '已归档工作人员不能新增资质';
|
|||
|
|
return '';
|
|||
|
|
}
|