refactor: P1-3/4/6 解耦修复 - 拆分 auth/users 文件 + notifications 反向依赖

This commit is contained in:
SpecialX
2026-06-18 02:21:44 +08:00
parent 62be0b9404
commit 2c8e229e00
11 changed files with 514 additions and 288 deletions

View File

@@ -176,6 +176,10 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
──▶ import { ... } from "@/shared/lib/login-logger"
──▶ import { ... } from "@/shared/lib/password-policy"
──▶ import { ... } from "@/shared/lib/rate-limit"
──▶ import { ... } from "@/shared/lib/role-utils" # P1-3 拆出
──▶ import { ... } from "@/shared/lib/bcrypt-utils" # P1-3 拆出
──▶ import { ... } from "@/shared/lib/http-utils" # P1-3 拆出
──▶ import { ... } from "@/shared/lib/password-security-service" # P1-3 拆出
──▶ import { db, schema } from "@/shared/db"
⟳ 循环shared/lib/* → @/auth → shared/lib/*
@@ -365,7 +369,7 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
**已知问题**
- ❌ P0`shared/lib/*``@/auth` 循环依赖
- ⚠️ P1`schema.ts` 1111 行54 张表混合,超 1000 硬上限)
- ⚠️ P1`auth.ts` 293 行混合 5 类职责
- P1~~`auth.ts` 293 行混合 5 类职责~~ 已拆分4 个辅助函数组迁移至 `shared/lib/{role-utils,bcrypt-utils,http-utils,password-security-service}`auth.ts 仅保留 NextAuth 配置)
- ⚠️ P2`ai.ts` 218 行混合 5 类职责
- ⚠️ P2`onboarding-gate.tsx` 业务逻辑泄漏到 shared
@@ -383,6 +387,10 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
| `lib/login-logger.ts` | - | 登录日志 |
| `lib/password-policy.ts` | - | 密码策略纯函数 |
| `lib/rate-limit.ts` | - | 内存滑动窗口限流 |
| `lib/role-utils.ts` | 35 | 角色规范化纯函数normalizeRole / resolvePrimaryRole |
| `lib/bcrypt-utils.ts` | 22 | bcrypt 哈希前缀规范化纯函数 |
| `lib/http-utils.ts` | 30 | 请求头解析resolveClientIpserver-only |
| `lib/password-security-service.ts` | 100 | 密码安全 DB 操作(账户锁定/失败登录追踪server-only |
| `lib/excel.ts` | - | Excel 导入导出 |
| `lib/file-storage.ts` | - | 文件存储抽象 |
| `hooks/use-permission.ts` | - | 客户端权限 Hook |
@@ -669,22 +677,26 @@ src/auth.ts ──▶ import { ... } from "@/shared/lib/permissions"
**导出函数**
- Actions`getUserProfileAction` / `updateUserProfileAction` / `importUsersAction` / `exportUsersAction` / `downloadUserTemplateAction`
- Data-access`getUserProfile`
- Import-export`generateUserImportTemplate` / `parseUserImportData` / `batchImportUsers` / `exportUsersToExcel`
- Import-export`generateUserImportTemplate` / `parseUserImportData` / `exportUsersToExcel`+ re-export `batchImportUsers` / `UserImportResult` 保持向后兼容)
- User-service`batchImportUsers`(用户创建 + 密码哈希 + 角色分配)
- Class-registration`registerStudentByInvitationCode`(委托 classes/data-access 完成班级注册)
**依赖关系**
- 依赖:`shared/*``@/auth``classes``batchImportUsers` 直查 classes + 直写 classEnrollments
- 依赖:`shared/*``@/auth``classes`✅ P1-4 已修复:通过 `class-registration.ts` 调用 `classes/data-access.enrollStudentByInvitationCode`,不再直写 classEnrollments
- 被依赖:`dashboard`(通过 data-accessP0-4 已修复)、`grades`(❌ 直查)、`homework`(❌ 直查)
**已知问题**
- P1`import-export.ts` 四重职责混合(导入解析 + 导出 + 用户创建 + 班级注册)
- P1`batchImportUsers` 跨模块写 `classEnrollments`classes 模块的写操作)
- P1 已解决`import-export.ts` 四重职责已拆分为 `import-export.ts`(解析/生成)+ `user-service.ts`用户创建+ `class-registration.ts`班级注册)
- P1 已解决`batchImportUsers` 不再跨模块`classEnrollments`,改为调用 `classes/data-access.enrollStudentByInvitationCode`
- ❌ P1`updateUserProfile` 绕过 data-access 直接 DB 写
- ⚠️ P2`data-access.ts` 仅 71 行,写操作缺失
**文件清单**
| 文件 | 行数 | 职责 |
|------|------|------|
| `import-export.ts` | 291 | 导入解析 + 导出 + 用户创建(职责混合) |
| `import-export.ts` | 177 | 文件解析/生成(模板生成 + 解析校验 + Excel 导出+ re-export 向后兼容 |
| `user-service.ts` | 96 | 用户创建(批量导入 + 密码哈希 + 角色分配) |
| `class-registration.ts` | 30 | 班级注册(委托 classes/data-access |
| `actions.ts` | 151 | 5 个 Server Action |
| `data-access.ts` | 71 | 仅 getUserProfile |
@@ -1196,24 +1208,28 @@ shared/lib/{audit-logger, change-logger, auth-guard} → @/auth → shared/lib/*
**解耦建议**actions 层仅做"权限校验 → 解析 → 调用 data-access → revalidatePath → 返回",所有 DB 操作下沉到 data-access。
### P1-3auth.ts 混合 5 类职责
### P1-3auth.ts 混合 5 类职责 ✅ 已完成
`src/auth.ts` 293 行混合NextAuth 配置 + 密码安全 DB 操作 + 角色规范化 + IP 解析 + 回调函数。
`src/auth.ts` 293 行混合NextAuth 配置 + 密码安全 DB 操作 + 角色规范化 + IP 解析 + 回调函数。
**解耦建议**
- 密码安全 DB 操作 → `shared/lib/password-security-service.ts`
- 角色规范化 → `shared/lib/role-utils.ts`
- IP 解析`shared/lib/http-utils.ts`与三个 logger 共用
- `authorize` 回调拆分为 `checkRateLimit` / `checkAccountLockout` / `verifyPassword` / `loadUserRoles`
**已完成拆分**auth.ts 现 208 行,仅保留 NextAuth 配置)
- 密码安全 DB 操作 → `shared/lib/password-security-service.ts`getOrCreatePasswordSecurity / recordFailedLogin / resetFailedLoginserver-only
- 角色规范化 → `shared/lib/role-utils.ts`normalizeRole / resolvePrimaryRole纯函数
- ✅ bcrypt 哈希规范化`shared/lib/bcrypt-utils.ts`normalizeBcryptHash纯函数
- ✅ IP 解析 → `shared/lib/http-utils.ts`resolveClientIpserver-only
### P1-4users/import-export.ts 四重职责
**后续可选优化**(未执行,需保持 NextAuth 配置不变原则下评估):
- `authorize` 回调可进一步拆分为 `checkRateLimit` / `checkAccountLockout` / `verifyPassword` / `loadUserRoles`,使 auth.ts 降至 ≤150 行
同时处理:导入解析 + 导出 + 用户创建(含密码哈希)+ 班级注册(跨模块写 classEnrollments
### P1-4users/import-export.ts 四重职责 ✅ 已完成
**解耦建议**
- 拆分为 `import.ts`(解析+校验)+ `export.ts`(模板生成+列表导出)
- 用户创建逻辑迁移至 `data-access.ts``createUser`
- classEnrollments 写入改为调用 `classes/data-access.enrollStudentByInvitationCode`
`users/import-export.ts` 原 291 行混合:导入解析 + 导出 + 用户创建(含密码哈希)+ 班级注册(跨模块写 classEnrollments
**已完成拆分**import-export.ts 现 177 行,仅保留文件解析/生成):
- ✅ 用户创建(含密码哈希、角色分配)→ `user-service.ts``batchImportUsers`96 行server-only
- ✅ 班级注册 → `class-registration.ts``registerStudentByInvitationCode`30 行server-only
-`batchImportUsers` 不再直写 `classEnrollments`,改为调用 `classes/data-access.enrollStudentByInvitationCode`
-`import-export.ts` 通过 re-export `batchImportUsers` / `UserImportResult` 保持向后兼容(`actions.ts``app/api/export/route.ts` 无需修改)
### P1-5notifications 反向依赖 messaging
@@ -1269,8 +1285,8 @@ shared/lib/{audit-logger, change-logger, auth-guard} → @/auth → shared/lib/*
### 短期执行P1
8. actions 层移除直接 DB 操作exams/homework/questions/announcements/users/scheduling
9. 拆分 `auth.ts`
10. 拆分 `users/import-export.ts`
9. ~~拆分 `auth.ts`~~ ✅ 已完成4 个辅助函数组迁移至 shared/libauth.ts 保留 NextAuth 配置)
10. ~~拆分 `users/import-export.ts`~~ ✅ 已完成(拆为 import-export.ts 177行 + user-service.ts 96行 + class-registration.ts 30行班级注册改为调用 classes/data-access
11. 消除 notifications → messaging 反向依赖
12. 提取 `shared/lib/http-utils.ts` 统一 IP 提取
13. 各模块暴露跨模块查询接口(见 P1-1
@@ -1316,7 +1332,7 @@ shared/lib/{audit-logger, change-logger, auth-guard} → @/auth → shared/lib/*
| **school** | ✅ | ✅ | - | - | - | - | - | - | - | ⚠️可接受 | - | - | - | - |
| **grades** | ✅ | ✅ | ✅外键 | ✅外键 | - | - | ❌直查 | ❌直查 | - | ❌直查 | - | - | - | - |
| **dashboard** | ✅ | ✅ | ✅data-access | ✅data-access | ✅data-access | ✅data-access | ✅data-access | - | - | ✅data-access | - | - | - | - |
| **users** | ✅ | ✅ | - | - | - | - | ❌写enrollments | - | - | - | - | - | - | - |
| **users** | ✅ | ✅ | - | - | - | - | ✅data-access | - | - | - | - | - | - | - |
| **messaging** | ✅ | ✅ | - | - | - | - | - | - | - | - | - | - | ❌绕过 | - |
| **notifications** | ✅ | ✅ | - | - | - | - | ❌直查 | - | - | - | - | ⟳反向依赖 | - | - |
| **attendance** | ✅ | ✅ | - | - | - | - | ❌直查 | - | - | - | - | - | - | - |

View File

@@ -527,6 +527,97 @@
"auth.ts (events.signIn, events.signOut)"
]
},
{
"name": "normalizeRole",
"file": "lib/role-utils.ts",
"signature": "normalizeRole(value: unknown): NormalizedRole",
"purpose": "将角色值规范化为 admin/teacher/student/parent 之一纯函数legacy 别名 grade_head/teaching_head→teacher",
"deps": [],
"usedBy": [
"auth.ts (jwt/session callbacks)",
"lib/role-utils.resolvePrimaryRole"
]
},
{
"name": "resolvePrimaryRole",
"file": "lib/role-utils.ts",
"signature": "resolvePrimaryRole(roleNames: string[]): NormalizedRole",
"purpose": "从多角色列表解析主角色(优先级 admin>teacher>parent>student纯函数",
"deps": [
"lib/role-utils.normalizeRole"
],
"usedBy": [
"auth.ts (authorize, jwt callback)"
]
},
{
"name": "normalizeBcryptHash",
"file": "lib/bcrypt-utils.ts",
"signature": "normalizeBcryptHash(value: string): string",
"purpose": "将存储的 bcrypt 哈希规范化为 $2b$ 前缀形式(纯函数,兼容 legacy 无前缀存储)",
"deps": [],
"usedBy": [
"auth.ts (authorize)"
]
},
{
"name": "resolveClientIp",
"file": "lib/http-utils.ts",
"signature": "resolveClientIp(): Promise<string>",
"purpose": "从请求头解析客户端 IPx-forwarded-for/x-real-ipbest-effort失败返回 unknown",
"deps": [
"next/headers"
],
"usedBy": [
"auth.ts (authorize 速率限制键)"
]
},
{
"name": "getOrCreatePasswordSecurity",
"file": "lib/password-security-service.ts",
"signature": "getOrCreatePasswordSecurity(db, passwordSecurity, userId: string): Promise<PasswordSecurityRow>",
"purpose": "获取或创建用户的 password_security 行server-only",
"deps": [
"drizzle-orm.eq",
"@paralleldrive/cuid2",
"shared.db",
"shared.db.schema.passwordSecurity"
],
"usedBy": [
"auth.ts (authorize)",
"lib/password-security-service.recordFailedLogin",
"lib/password-security-service.resetFailedLogin"
]
},
{
"name": "recordFailedLogin",
"file": "lib/password-security-service.ts",
"signature": "recordFailedLogin(db, passwordSecurity, userId: string): Promise<{ locked: boolean; lockedUntil: Date | null }>",
"purpose": "递增失败登录计数达到阈值则锁定账户server-only",
"deps": [
"lib/password-security-service.getOrCreatePasswordSecurity",
"lib/password-policy.PASSWORD_RULES",
"shared.db",
"shared.db.schema.passwordSecurity"
],
"usedBy": [
"auth.ts (authorize 密码校验失败分支)"
]
},
{
"name": "resetFailedLogin",
"file": "lib/password-security-service.ts",
"signature": "resetFailedLogin(db, passwordSecurity, userId: string): Promise<void>",
"purpose": "登录成功后重置失败计数与锁定状态server-only",
"deps": [
"lib/password-security-service.getOrCreatePasswordSecurity",
"shared.db",
"shared.db.schema.passwordSecurity"
],
"usedBy": [
"auth.ts (authorize 登录成功分支)"
]
},
{
"name": "isAllowedMimeType",
"file": "lib/file-storage.ts",
@@ -1801,7 +1892,19 @@
},
"auth": {
"path": "src/auth.ts",
"description": "用户认证NextAuth配置、JWT/Session callbacks、events回调(登录日志)、middleware。集成密码安全策略(账户锁定、失败登录追踪)和登录速率限制",
"description": "用户认证NextAuth配置handlers/auth/signIn/signOut、JWT/Session callbacks、events回调(登录日志)。集成密码安全策略(账户锁定、失败登录追踪)和登录速率限制。P1-3 拆分后,辅助函数已迁移至 shared/lib/{role-utils,bcrypt-utils,http-utils,password-security-service}",
"imports": [
"shared/lib/permissions",
"shared/lib/login-logger",
"shared/lib/password-policy",
"shared/lib/rate-limit",
"shared/lib/role-utils",
"shared/lib/bcrypt-utils",
"shared/lib/http-utils",
"shared/lib/password-security-service",
"shared/db",
"shared/db/schema"
],
"exports": {
"functions": [
{
@@ -4850,25 +4953,6 @@
"actions.importUsersAction"
]
},
{
"name": "batchImportUsers",
"signature": "(records: UserImportRecord[]) => Promise<UserImportResult>",
"file": "import-export.ts",
"purpose": "批量创建用户(默认密码 123456 bcrypt 哈希,自动创建 usersToRolesstudent 通过邀请码自动加入班级)",
"deps": [
"shared.db",
"shared.db.schema.users",
"shared.db.schema.roles",
"shared.db.schema.usersToRoles",
"shared.db.schema.classes",
"shared.db.schema.classEnrollments",
"bcryptjs",
"@paralleldrive/cuid2"
],
"usedBy": [
"actions.importUsersAction"
]
},
{
"name": "exportUsersToExcel",
"signature": "(params: { scope: DataScope; role?: string }) => Promise<Buffer>",
@@ -4887,6 +4971,47 @@
]
}
],
"userService": [
{
"name": "batchImportUsers",
"signature": "(records: UserImportRecord[]) => Promise<UserImportResult>",
"file": "user-service.ts",
"purpose": "批量创建用户(默认密码 123456 bcrypt 哈希,自动创建 usersToRolesstudent 通过邀请码自动加入班级——委托 class-registration",
"deps": [
"shared.db",
"shared.db.schema.users",
"shared.db.schema.roles",
"shared.db.schema.usersToRoles",
"bcryptjs",
"@paralleldrive/cuid2",
"class-registration.registerStudentByInvitationCode"
],
"usedBy": [
"actions.importUsersAction",
"import-export.ts (re-export 向后兼容)"
]
}
],
"classRegistration": [
{
"name": "registerStudentByInvitationCode",
"signature": "(studentId: string, invitationCode: string) => Promise<ClassRegistrationResult>",
"file": "class-registration.ts",
"purpose": "通过邀请码将学生注册到班级,委托 classes/data-access.enrollStudentByInvitationCode返回结构化结果不抛异常",
"deps": [
"classes/data-access.enrollStudentByInvitationCode"
],
"usedBy": [
"user-service.batchImportUsers"
]
},
{
"name": "ClassRegistrationResult",
"type": "type",
"file": "class-registration.ts",
"definition": "{ success: boolean; error?: string }"
}
],
"types": [
{
"name": "UserImportRecord",
@@ -4910,11 +5035,12 @@
{
"name": "UserImportResult",
"type": "type",
"file": "import-export.ts",
"file": "user-service.ts",
"definition": "{ successCount, failedCount, errors: Array<{ row, email, error }> }",
"usedBy": [
"batchImportUsers",
"importUsersAction"
"importUsersAction",
"import-export.ts (re-export 向后兼容)"
]
}
],
@@ -10255,7 +10381,8 @@
"users": {
"dependsOn": [
"shared",
"auth"
"auth",
"classes"
],
"uses": {
"shared": [
@@ -10265,14 +10392,15 @@
"db.schema.users",
"db.schema.roles",
"db.schema.usersToRoles",
"db.schema.classes",
"db.schema.classEnrollments",
"types.permissions",
"types.action-state",
"lib.excel"
],
"auth": [
"auth"
],
"classes": [
"data-access.enrollStudentByInvitationCode"
]
}
},
@@ -10952,7 +11080,9 @@
"title": "auth.ts 混合 5 类职责",
"file": "src/auth.ts",
"problem": "NextAuth 配置 + 密码安全 DB 操作 + 角色规范化 + IP 解析 + 回调函数混合",
"suggestion": "拆分为 auth-config/password-security/role-normalizer/ip-utils 等多文件"
"suggestion": "拆分为 auth-config/password-security/role-normalizer/ip-utils 等多文件",
"status": "resolved",
"resolution": "已拆分密码安全DB操作→shared/lib/password-security-service.ts角色规范化→shared/lib/role-utils.tsbcrypt哈希规范化→shared/lib/bcrypt-utils.tsIP解析→shared/lib/http-utils.ts。auth.ts 现 208 行仅保留 NextAuth 配置"
},
{
"id": "P1-4",