feat(settings): 设置与个人信息模块审计重构 — i18n + 服务注入解耦 + Error Boundary + 流式渲染

- 新增 SettingsService 接口 + Context 注入,组件层不再直接 import users/messaging actions

- 新增 resolveRoleSettingsConfig 配置驱动角色路由,删除 parent/student/teacher-settings-view 冗余文件

- 新增 SettingsSectionErrorBoundary,每个 TabsContent + profile 角色概览区块均包裹

- 新增 ProfileStudentOverview/ProfileTeacherOverview 异步 Server Component + 骨架屏,支持流式渲染

- 抽取 buildStudentOverviewData 等纯函数到 lib/student-overview-data.ts,便于单元测试

- 新增 settings.json 翻译文件(zh-CN + en),所有组件改用 useTranslations/getTranslations

- 重构 profile/page.tsx:i18n 适配 + Suspense 分区加载 + 业务逻辑抽离

- 同步更新架构图 004/005
This commit is contained in:
SpecialX
2026-06-22 16:15:36 +08:00
parent 21c7e65fee
commit 5d42495480
29 changed files with 2445 additions and 1094 deletions

View File

@@ -1,12 +1,16 @@
import { redirect } from "next/navigation"
import { getTranslations } from "next-intl/server"
import { requireAuth } from "@/shared/lib/auth-guard"
import { SettingsView } from "@/modules/settings/components/settings-view"
import { StudentSettingsView } from "@/modules/settings/components/student-settings-view"
import { TeacherSettingsView } from "@/modules/settings/components/teacher-settings-view"
import { ParentSettingsView } from "@/modules/settings/components/parent-settings-view"
import { SettingsServiceProvider } from "@/modules/settings/components/settings-service-context"
import { resolveRoleSettingsConfig } from "@/modules/settings/config/role-settings-config"
import type { SettingsService } from "@/modules/settings/types"
import { getUserProfile } from "@/modules/users/data-access"
import { updateUserProfile } from "@/modules/users/actions"
import { getNotificationPreferences } from "@/modules/notifications/preferences"
import { updateNotificationPreferencesAction } from "@/modules/messaging/actions"
import type { UpdateNotificationPreferencesInput } from "@/modules/notifications/types"
export const dynamic = "force-dynamic"
@@ -14,6 +18,32 @@ export const metadata = {
title: "Settings",
}
/**
* 将通知偏好输入对象转换为 FormData适配 updateNotificationPreferencesAction 的签名。
* Action 内部通过 formData.get(key) === "on" 解析布尔值。
*/
function buildNotificationFormData(input: UpdateNotificationPreferencesInput): FormData {
const formData = new FormData()
const booleanFields: Array<keyof UpdateNotificationPreferencesInput> = [
"emailEnabled",
"smsEnabled",
"pushEnabled",
"homeworkNotifications",
"gradeNotifications",
"announcementNotifications",
"messageNotifications",
"attendanceNotifications",
"quietHoursEnabled",
]
for (const field of booleanFields) {
const value = input[field]
if (value === true) formData.set(field, "on")
}
if (input.quietHoursStart) formData.set("quietHoursStart", input.quietHoursStart)
if (input.quietHoursEnd) formData.set("quietHoursEnd", input.quietHoursEnd)
return formData
}
export default async function SettingsPage() {
const ctx = await requireAuth()
@@ -24,22 +54,36 @@ export default async function SettingsPage() {
const roles = ctx.roles
const notificationPrefs = await getNotificationPreferences(userId)
const t = await getTranslations("settings")
if (roles.includes("admin")) {
return (
const config = resolveRoleSettingsConfig(roles)
const description = t(config?.descriptionKey ?? "title")
const backHref = config?.backHref ?? "/dashboard"
const generalExtra = config?.generalExtra
// 构建 SettingsService 实现,注入到 SettingsServiceProvider
// 组件层通过 useSettingsService() 消费,不直接 import users/messaging actions
const service: SettingsService = {
profile: {
getProfile: async () => getUserProfile(userId),
updateProfile: async (input) => updateUserProfile(input),
},
notifications: {
getPreferences: async () => getNotificationPreferences(userId),
updatePreferences: async (input) =>
updateNotificationPreferencesAction(null, buildNotificationFormData(input)),
},
}
return (
<SettingsServiceProvider service={service}>
<SettingsView
description="Manage your admin preferences and account access."
backHref="/admin/dashboard"
description={description}
backHref={backHref}
user={userProfile}
notificationPreferences={notificationPrefs}
generalExtra={generalExtra}
/>
)
}
if (roles.includes("student")) {
return <StudentSettingsView user={userProfile} notificationPreferences={notificationPrefs} />
}
if (roles.includes("parent")) {
return <ParentSettingsView user={userProfile} notificationPreferences={notificationPrefs} />
}
return <TeacherSettingsView user={userProfile} notificationPreferences={notificationPrefs} />
</SettingsServiceProvider>
)
}