gongxiangp2/lib/departments.ts
chenpei ea9076df45 feat: 实现统一的下载错误处理系统并修复产品全案数据库Schema
- 创建统一的下载错误处理模块 (lib/download-errors.ts)
- 实现13种错误类型分类和中文错误消息
- 后端统一使用 createDownloadError 创建标准化错误响应
- 前端统一使用 parseDownloadError 解析错误并显示
- 修复正式系统数据库Schema,添加产品全案相关字段和表
- 执行数据库迁移:添加 task_no, task_status, task_fetched_at 字段
- 创建 material_assets, api_keys, api_call_logs 表
2026-01-05 17:52:42 +08:00

133 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 部门工具函数
*
* 提供部门层级结构处理、权限范围检查等功能
* 参考 flnxi_sucai 项目的实现逻辑
*/
/**
* 标准化部门名称(去除空白字符)
*/
const normalizeName = (name?: string | null): string => {
return (name || '').trim()
}
/**
* 构建部门后裔映射
* 返回一个Map,key是部门名称,value是该部门的所有后裔部门名称集合
*
* @param departments 部门列表
* @returns Map<string, Set<string>> 部门名称到其后裔部门名称集合的映射
*/
export function buildDepartmentDescendants(
departments: Array<{ id: string; name: string; parentId?: string | null }>
): Map<string, Set<string>> {
// 构建部门ID到部门的映射
const deptMap = new Map<string, { id: string; name: string; parentId?: string | null }>()
departments.forEach(dept => {
deptMap.set(dept.id, dept)
})
// 构建部门名称到部门的映射
const nameToDeptMap = new Map<string, { id: string; name: string; parentId?: string | null }>()
departments.forEach(dept => {
const normalizedName = normalizeName(dept.name)
if (normalizedName) {
nameToDeptMap.set(normalizedName, dept)
}
})
// 构建子部门映射(parentId -> children)
const childrenMap = new Map<string, string[]>()
departments.forEach(dept => {
if (dept.parentId && deptMap.has(dept.parentId)) {
const parent = deptMap.get(dept.parentId)!
const parentName = normalizeName(parent.name)
if (parentName) {
if (!childrenMap.has(parentName)) {
childrenMap.set(parentName, [])
}
childrenMap.get(parentName)!.push(normalizeName(dept.name))
}
}
})
// 构建后裔映射(递归收集所有后裔)
const descendantMap = new Map<string, Set<string>>()
const visit = (deptName: string): Set<string> => {
if (descendantMap.has(deptName)) {
return descendantMap.get(deptName)!
}
const descendants = new Set<string>([deptName])
const children = childrenMap.get(deptName) || []
children.forEach(childName => {
visit(childName).forEach(desc => descendants.add(desc))
})
descendantMap.set(deptName, descendants)
return descendants
}
// 遍历所有部门,构建后裔映射
departments.forEach(dept => {
const deptName = normalizeName(dept.name)
if (deptName) {
visit(deptName)
}
})
return descendantMap
}
/**
* 检查目标部门是否在权限范围内
*
* @param scope 权限范围部门名称(当前用户的部门或管理范围)
* @param target 目标部门名称(要检查的部门)
* @param descendantMap 部门后裔映射(可选,如果不提供则使用简单的字符串匹配)
* @returns boolean 目标部门是否在权限范围内
*/
export function isDepartmentWithinScope(
scope: string | null | undefined,
target: string | null | undefined,
descendantMap?: Map<string, Set<string>>
): boolean {
const normalizedScope = normalizeName(scope)
const normalizedTarget = normalizeName(target)
if (!normalizedScope || !normalizedTarget) {
return false
}
// 完全匹配
if (normalizedScope === normalizedTarget) {
return true
}
// 如果有后裔映射,检查目标是否是范围部门的后裔
if (descendantMap) {
const scopedDescendants = descendantMap.get(normalizedScope)
if (scopedDescendants?.has(normalizedTarget)) {
return true
}
}
// 回退到简单的层级匹配(如果使用名称层级结构,如:父部门-子部门)
// 注意:当前项目使用 parentId,这个匹配可能不准确,但作为备用方案
return normalizedTarget.startsWith(`${normalizedScope}-`)
}
/**
* 获取部门排序比较器(中文拼音排序)
*/
export function getDepartmentCollator(): Intl.Collator {
return new Intl.Collator('zh-Hans-CN-u-co-pinyin', {
numeric: true,
sensitivity: 'base',
})
}