refactor(announcements,messaging,notifications): V1+V2 审计重构 — i18n 命名空间独立 + 通知标题 i18n 化 + 服务端过滤 + 编排下沉 + 表单错误展示 + 架构图同步

V1 改进(已完成):
- P0-4/P1-4/P1-5: 通知组件和 CRUD Action 从 messaging 迁移至 notifications 模块
- P1-5: 新增 getMessagesPageData / getAdminAnnouncementsPageData 编排函数
- P1-6: announcements schema 添加 superRefine 条件校验
- P1-7: 新增 useMessageSearch hook(防抖 + 请求竞态取消)+ 客户端分页 UI
- P1-9: deleteMessage 事务化
- P2-11: 全模块 trackEvent 埋点
- 全模块 i18n 接入 + Error Boundary + a11y 改进

V2 改进(本次完成):
- V2-P0-1: 通知 i18n 命名空间独立(notifications.json),useTranslations 从 "messages" 切换到 "notifications"
- V2-P0-2: 公告/消息通知标题 i18n 化,Server Action 中使用 getTranslations 生成通知标题
- V2-P1-1: AnnouncementList 纯服务端过滤,移除客户端 useState/useMemo
- V2-P1-2: MessageList 客户端过滤仅在初始数据时执行,搜索结果由服务端按 tab 过滤
- V2-P1-3: 消息详情页编排下沉,新增 getMessageDetailPageData 编排函数
- V2-P1-4: 表单服务端校验错误展示(fieldErrors + aria-invalid)
- V2-P2-1: 轮询间隔常量化(POLL_INTERVAL_MS)
- V2-P2-2: 架构图同步(004 + 005)
This commit is contained in:
SpecialX
2026-06-22 18:43:12 +08:00
parent 6d7838a210
commit 1fe30984b6
29 changed files with 1252 additions and 351 deletions

View File

@@ -4086,6 +4086,12 @@
"permission": "TEXTBOOK_UPDATE",
"signature": "(chapterId, newIndex, parentId, textbookId) => Promise<ActionState>",
"purpose": "章节排序"
},
{
"name": "getKnowledgePointsByChapterAction",
"permission": "TEXTBOOK_READ",
"signature": "(chapterId, textbookId) => Promise<ActionState<KnowledgePoint[]>>",
"purpose": "获取章节下的知识点列表(含资源归属校验)"
}
],
"dataAccess": [
@@ -4249,6 +4255,24 @@
"updateKnowledgePointAction",
"deleteKnowledgePointAction"
]
},
{
"name": "getSubjectLabelKey",
"signature": "(subject: string) => string",
"purpose": "获取学科的 i18n 标签键",
"usedBy": [
"textbooks/components/textbook-filters.tsx",
"textbooks/components/textbook-card.tsx"
]
},
{
"name": "getGradeLabelKey",
"signature": "(grade: string) => string",
"purpose": "获取年级的 i18n 标签键",
"usedBy": [
"textbooks/components/textbook-filters.tsx",
"textbooks/components/textbook-card.tsx"
]
}
],
"hooks": [
@@ -4264,7 +4288,7 @@
{
"name": "useKnowledgePointActions",
"file": "hooks/use-knowledge-point-actions.ts",
"signature": "(textbookId, selectedChapterId, selectedChapterTextbookId, highlightedKpId, setHighlightedKpId, onKpCreated?) => { editingKp, setEditingKp, editKpDialogOpen, setEditKpDialogOpen, isUpdatingKp, questionDialogOpen, setQuestionDialogOpen, targetKpForQuestion, setTargetKpForQuestion, deleteConfirmOpen, setDeleteConfirmOpen, handleCreateKnowledgePoint, requestDeleteKP, confirmDeleteKP, handleUpdateKP }",
"signature": "(textbookId, selectedChapterId, selectedChapterTextbookId, highlightedKpId, setHighlightedKpId, onKpCreated?) => { editingKp, setEditingKp, editKpDialogOpen, setEditKpDialogOpen, isUpdatingKp, questionDialogOpen, setQuestionDialogOpen, targetKpForQuestion, setTargetKpForQuestion, deleteConfirmOpen, setDeleteConfirmOpen, handleCreateKnowledgePoint, requestDeleteKnowledgePoint, confirmDeleteKnowledgePoint, handleUpdateKnowledgePoint }",
"purpose": "知识点操作Hook(6参数)",
"usedBy": [
"textbook-reader.tsx"
@@ -4361,6 +4385,105 @@
]
}
],
"constants": [
{
"name": "SUBJECTS",
"file": "constants.ts",
"purpose": "学科常量数组"
},
{
"name": "GRADES",
"file": "constants.ts",
"purpose": "年级常量数组"
},
{
"name": "SUBJECT_COLORS",
"file": "constants.ts",
"purpose": "学科颜色映射"
},
{
"name": "getSubjectColor",
"file": "constants.ts",
"signature": "(subject: string) => string",
"purpose": "获取学科对应颜色"
},
{
"name": "getSubjectLabelKey",
"file": "constants.ts",
"signature": "(subject: string) => string",
"purpose": "获取学科 i18n 标签键"
},
{
"name": "getGradeLabelKey",
"file": "constants.ts",
"signature": "(grade: string) => string",
"purpose": "获取年级 i18n 标签键"
}
],
"utils": [
{
"name": "sortChapters",
"file": "utils.ts",
"purpose": "章节排序"
},
{
"name": "buildChapterTree",
"file": "utils.ts",
"purpose": "构建章节树(返回 ChapterTreeNode[]"
},
{
"name": "buildChapterIndex",
"file": "utils.ts",
"purpose": "构建章节索引 Map"
},
{
"name": "findChapterParent",
"file": "utils.ts",
"purpose": "查找章节父节点"
},
{
"name": "filterKnowledgePointsByChapter",
"file": "utils.ts",
"purpose": "按章节过滤知识点"
},
{
"name": "normalizeOptional",
"file": "utils.ts",
"purpose": "可选字段归一化"
},
{
"name": "highlightKnowledgePoints",
"file": "utils.ts",
"purpose": "高亮知识点"
}
],
"graphLayout": [
{
"name": "computeGraphLayout",
"file": "graph-layout.ts",
"purpose": "知识图谱布局计算纯函数"
}
],
"analytics": [
{
"name": "TextbookAnalytics",
"file": "analytics.tsx",
"type": "context",
"purpose": "教材分析 Context"
},
{
"name": "TextbookAnalyticsProvider",
"file": "analytics.tsx",
"type": "component",
"purpose": "教材分析 Provider"
},
{
"name": "useTextbookAnalytics",
"file": "analytics.tsx",
"type": "hook",
"purpose": "教材分析 Hook"
}
],
"components": [
{
"name": "ChapterSidebarList",
@@ -4370,10 +4493,6 @@
"name": "CreateChapterDialog",
"purpose": "创建章节对话框"
},
{
"name": "CreateKnowledgePointDialog",
"purpose": "创建知识点对话框"
},
{
"name": "KnowledgeGraph",
"purpose": "知识图谱可视化"
@@ -4387,8 +4506,8 @@
"purpose": "知识点列表"
},
{
"name": "KnowledgePointPanel",
"purpose": "知识点面板"
"name": "SectionErrorBoundary",
"purpose": "章节内容错误边界class component + i18n + router.refresh 重试)"
},
{
"name": "TextbookCard",
@@ -4415,27 +4534,25 @@
"purpose": "教材设置对话框"
}
],
"uiDeps": [
{
"from": "textbooks/components/knowledge-point-dialogs.tsx",
"to": "questions/components/create-question-dialog",
"status": "P0 待解耦(直接 import 跨模块业务组件,应改为 props/Context 注入)",
"auditRef": "audit/textbooks-audit-report.md §2.1.1"
}
],
"uiDeps": [],
"uiDepsNote": "已通过 render prop 解耦,不再直接 import questions 模块组件",
"knownIssues": [
"P0 跨模块 UI 依赖knowledge-point-dialogs.tsx 直接 import questions 模块 CreateQuestionDialog",
"P0 前端权限硬编码canEdit={true} 按路由写死,未用 usePermission().hasPermission()",
"P0 全模块零 i18n中英文文案硬编码未接入 next-intl",
"P1 Server Action 未校验资源归属chapterId 是否属于 textbookId",
"P1 data-access 缺数据范围过滤(学生端未按年级过滤)",
"P1 缺 Error Boundary无 error.tsx",
"P1 知识点列表/弹窗重复实现knowledge-point-panel.tsx 无调用方)",
"P1 学科/年级选项硬编码三处且彼此不一致",
"P1 纯逻辑未导出,零单测",
"P2 类型断言chapter-sidebar-list.tsx 用 ! 、knowledge-graph.tsx 用 as+!",
"P2 删除确认不一致textbook-settings-dialog 用 confirm(),其余用 AlertDialog"
"i18n 覆盖率约 95%chapter-sidebar-list 已接入actions.ts 已接入section-error-boundary 默认值已改英文)",
"类型断言残留 3 处 as string",
"P2 图谱方向键导航未实现"
],
"files": {
"actions.ts": 377,
"data-access.ts": 619,
"types.ts": 45,
"schema.ts": 64,
"constants.ts": 91,
"utils.ts": 181,
"graph-layout.ts": 141,
"analytics.tsx": 43,
"hooks/use-knowledge-point-actions.ts": 121,
"hooks/use-text-selection.ts": 57
},
"auditReport": "audit/textbooks-audit-report.md"
}
},
@@ -5804,6 +5921,24 @@
{
"name": "AcademicYearClient",
"purpose": "学年管理客户端"
},
{
"name": "SchoolErrorBoundary",
"file": "components/school-error-boundary.tsx",
"purpose": "school 模块共享 Error Boundaryclass component + i18n + router.refresh 重试4 个页面均包裹",
"props": "children, fallback?"
},
{
"name": "SchoolListSkeleton",
"file": "components/school-skeleton.tsx",
"purpose": "表格加载骨架屏animate-pulse",
"props": "rows?"
},
{
"name": "SchoolCardSkeleton",
"file": "components/school-skeleton.tsx",
"purpose": "卡片加载骨架屏animate-pulse",
"props": ""
}
],
"hooks": [
@@ -12938,7 +13073,13 @@
"P1-7": "新增 4 个角色配置TEACHER/ADMIN/STUDENT/PARENT+ ROLE_CONFIGS 注册表",
"P1-8": "新增 BLOCK_REGISTRY 注册表node-edit-panel 配置驱动渲染",
"P2-1": "5 个组件添加 role=dialog/aria-modal/aria-label",
"P2-4": "预留 LessonPlanTracker 接口 + noopTracker 默认实现"
"P2-4": "预留 LessonPlanTracker 接口 + noopTracker 默认实现",
"V2-1": "Server Actions i18n + 错误码模式12 个 Action 通过 getTranslations 翻译错误消息Service/DataAccess 层抛出 PublishServiceError/LessonPlanDataError 错误码Actions 层通过 PUBLISH_ERROR_KEY_MAP 翻译",
"V2-2": "SYSTEM_TEMPLATES i18n 化name/title 改为 i18n 键createLessonPlan 接受 translateTitle 函数在服务端翻译后存储到 DB",
"V2-3": "as unknown as 类型断言清零8 处替换为显式类型映射函数mapRowToLessonPlan/mapRowToListItem/mapRowToTemplate/mapRowToVersion+ 类型守卫isLessonPlanStatus/isTemplateType/isTemplateScope",
"V2-4": "MiniMap nodeColor 复用 lib/node-summary.ts 的 getNodeColor",
"V2-5": "a11y 深度修复lesson-plan-filters/exercise-block/inline-question-editor 的 select 添加 label htmlFor 关联exercise-block 题目列表改为 ul/linode-editor 画布添加 role=application + 键盘导航配置",
"V2-6": "Tracker 埋点接入:新增 useLessonPlanTrackerSafe hook在 create/save/publish/revert/duplicate/archive 6 处调用 tracker.track"
}
}
},