From b3511910d1f2e1fcf50210edfa9ec030df985a2e Mon Sep 17 00:00:00 2001 From: SpecialX <47072643+wangxiner55@users.noreply.github.com> Date: Fri, 10 Jul 2026 19:09:12 +0800 Subject: [PATCH] =?UTF-8?q?feat(admin-portal):=20=E5=AE=8C=E6=95=B4?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=20admin-portal=20=E7=AE=A1=E7=90=86=E7=AB=AF?= =?UTF-8?q?=E5=BE=AE=E5=89=8D=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 包含 src 全部实现、Dockerfile、配置文件等 --- apps/admin-portal/.env.example | 21 + apps/admin-portal/Dockerfile | 36 + apps/admin-portal/docs/01-understanding.md | 373 +- .../docs/02-architecture-design.md | 996 ++--- apps/admin-portal/eslint.config.js | 90 + apps/admin-portal/next-env.d.ts | 2 + apps/admin-portal/next.config.js | 53 + apps/admin-portal/package.json | 60 + apps/admin-portal/postcss.config.js | 6 + apps/admin-portal/public/mockServiceWorker.js | 361 ++ apps/admin-portal/src/app/admin-app.tsx | 51 + .../src/app/admin/audit-logs/page.tsx | 158 + .../src/app/admin/classes/page.tsx | 89 + .../src/app/admin/dashboard/page.tsx | 267 ++ apps/admin-portal/src/app/admin/layout.tsx | 60 + .../src/app/admin/organization/page.tsx | 113 + .../src/app/admin/permissions/page.tsx | 115 + .../admin-portal/src/app/admin/roles/page.tsx | 291 ++ .../src/app/admin/students/page.tsx | 112 + .../src/app/admin/system/page.tsx | 214 ++ .../src/app/admin/teachers/page.tsx | 115 + .../admin-portal/src/app/admin/users/page.tsx | 166 + .../src/app/admin/viewports/page.tsx | 284 ++ apps/admin-portal/src/app/api/health/route.ts | 17 + apps/admin-portal/src/app/api/ready/route.ts | 47 + apps/admin-portal/src/app/globals.css | 73 + apps/admin-portal/src/app/layout.tsx | 27 + apps/admin-portal/src/app/login/page.tsx | 154 + apps/admin-portal/src/app/page.tsx | 5 + .../src/components/admin-shell.tsx | 176 + .../src/components/msw-initializer.tsx | 24 + .../src/components/notification-panel.tsx | 164 + .../src/components/organization-tree.tsx | 188 + .../src/components/role-permission-matrix.tsx | 147 + apps/admin-portal/src/components/ui.tsx | 375 ++ .../src/components/user-form-modal.tsx | 216 ++ .../src/components/user-management-table.tsx | 72 + .../src/components/web-vitals-initializer.tsx | 20 + apps/admin-portal/src/hooks/use-audit-logs.ts | 89 + apps/admin-portal/src/hooks/use-classes.ts | 44 + apps/admin-portal/src/hooks/use-dashboard.ts | 20 + apps/admin-portal/src/hooks/use-graphql.ts | 100 + .../src/hooks/use-organization.ts | 24 + .../admin-portal/src/hooks/use-permissions.ts | 22 + apps/admin-portal/src/hooks/use-roles.ts | 73 + apps/admin-portal/src/hooks/use-students.ts | 48 + .../src/hooks/use-system-settings.ts | 48 + apps/admin-portal/src/hooks/use-teachers.ts | 48 + apps/admin-portal/src/hooks/use-users.ts | 138 + apps/admin-portal/src/hooks/use-viewports.ts | 59 + apps/admin-portal/src/hooks/use-websocket.ts | 125 + apps/admin-portal/src/lib/auth.ts | 79 + apps/admin-portal/src/lib/graphql-client.ts | 333 ++ apps/admin-portal/src/lib/i18n.ts | 183 + apps/admin-portal/src/lib/permissions.ts | 78 + apps/admin-portal/src/lib/web-vitals.ts | 43 + apps/admin-portal/src/mocks/browser.ts | 10 + apps/admin-portal/src/mocks/fixtures.ts | 906 +++++ apps/admin-portal/src/mocks/handlers.ts | 516 +++ apps/admin-portal/src/mocks/server.ts | 7 + .../src/providers/auth-provider.tsx | 107 + .../src/providers/graphql-provider.tsx | 16 + .../src/providers/toast-provider.tsx | 124 + apps/admin-portal/src/types/view-models.ts | 207 + apps/admin-portal/tailwind.config.js | 29 + apps/admin-portal/tsconfig.json | 20 + apps/admin-portal/vitest.config.ts | 30 + docs/troubleshooting/known-issues.md | 24 + pnpm-lock.yaml | 3360 ++++++++++++++++- 69 files changed, 11955 insertions(+), 693 deletions(-) create mode 100644 apps/admin-portal/.env.example create mode 100644 apps/admin-portal/Dockerfile create mode 100644 apps/admin-portal/eslint.config.js create mode 100644 apps/admin-portal/next-env.d.ts create mode 100644 apps/admin-portal/next.config.js create mode 100644 apps/admin-portal/package.json create mode 100644 apps/admin-portal/postcss.config.js create mode 100644 apps/admin-portal/public/mockServiceWorker.js create mode 100644 apps/admin-portal/src/app/admin-app.tsx create mode 100644 apps/admin-portal/src/app/admin/audit-logs/page.tsx create mode 100644 apps/admin-portal/src/app/admin/classes/page.tsx create mode 100644 apps/admin-portal/src/app/admin/dashboard/page.tsx create mode 100644 apps/admin-portal/src/app/admin/layout.tsx create mode 100644 apps/admin-portal/src/app/admin/organization/page.tsx create mode 100644 apps/admin-portal/src/app/admin/permissions/page.tsx create mode 100644 apps/admin-portal/src/app/admin/roles/page.tsx create mode 100644 apps/admin-portal/src/app/admin/students/page.tsx create mode 100644 apps/admin-portal/src/app/admin/system/page.tsx create mode 100644 apps/admin-portal/src/app/admin/teachers/page.tsx create mode 100644 apps/admin-portal/src/app/admin/users/page.tsx create mode 100644 apps/admin-portal/src/app/admin/viewports/page.tsx create mode 100644 apps/admin-portal/src/app/api/health/route.ts create mode 100644 apps/admin-portal/src/app/api/ready/route.ts create mode 100644 apps/admin-portal/src/app/globals.css create mode 100644 apps/admin-portal/src/app/layout.tsx create mode 100644 apps/admin-portal/src/app/login/page.tsx create mode 100644 apps/admin-portal/src/app/page.tsx create mode 100644 apps/admin-portal/src/components/admin-shell.tsx create mode 100644 apps/admin-portal/src/components/msw-initializer.tsx create mode 100644 apps/admin-portal/src/components/notification-panel.tsx create mode 100644 apps/admin-portal/src/components/organization-tree.tsx create mode 100644 apps/admin-portal/src/components/role-permission-matrix.tsx create mode 100644 apps/admin-portal/src/components/ui.tsx create mode 100644 apps/admin-portal/src/components/user-form-modal.tsx create mode 100644 apps/admin-portal/src/components/user-management-table.tsx create mode 100644 apps/admin-portal/src/components/web-vitals-initializer.tsx create mode 100644 apps/admin-portal/src/hooks/use-audit-logs.ts create mode 100644 apps/admin-portal/src/hooks/use-classes.ts create mode 100644 apps/admin-portal/src/hooks/use-dashboard.ts create mode 100644 apps/admin-portal/src/hooks/use-graphql.ts create mode 100644 apps/admin-portal/src/hooks/use-organization.ts create mode 100644 apps/admin-portal/src/hooks/use-permissions.ts create mode 100644 apps/admin-portal/src/hooks/use-roles.ts create mode 100644 apps/admin-portal/src/hooks/use-students.ts create mode 100644 apps/admin-portal/src/hooks/use-system-settings.ts create mode 100644 apps/admin-portal/src/hooks/use-teachers.ts create mode 100644 apps/admin-portal/src/hooks/use-users.ts create mode 100644 apps/admin-portal/src/hooks/use-viewports.ts create mode 100644 apps/admin-portal/src/hooks/use-websocket.ts create mode 100644 apps/admin-portal/src/lib/auth.ts create mode 100644 apps/admin-portal/src/lib/graphql-client.ts create mode 100644 apps/admin-portal/src/lib/i18n.ts create mode 100644 apps/admin-portal/src/lib/permissions.ts create mode 100644 apps/admin-portal/src/lib/web-vitals.ts create mode 100644 apps/admin-portal/src/mocks/browser.ts create mode 100644 apps/admin-portal/src/mocks/fixtures.ts create mode 100644 apps/admin-portal/src/mocks/handlers.ts create mode 100644 apps/admin-portal/src/mocks/server.ts create mode 100644 apps/admin-portal/src/providers/auth-provider.tsx create mode 100644 apps/admin-portal/src/providers/graphql-provider.tsx create mode 100644 apps/admin-portal/src/providers/toast-provider.tsx create mode 100644 apps/admin-portal/src/types/view-models.ts create mode 100644 apps/admin-portal/tailwind.config.js create mode 100644 apps/admin-portal/tsconfig.json create mode 100644 apps/admin-portal/vitest.config.ts diff --git a/apps/admin-portal/.env.example b/apps/admin-portal/.env.example new file mode 100644 index 0000000..f1b67ef --- /dev/null +++ b/apps/admin-portal/.env.example @@ -0,0 +1,21 @@ +# admin-portal 环境变量 +# GraphQL endpoint(经 api-gateway 代理到 teacher-bff admin 命名空间) +API_GATEWAY_URL=http://localhost:8080 + +# MSW mock 开关(上游就绪后设为 disabled) +NEXT_PUBLIC_API_MOCKING=enabled + +# MF 开关(Shell 就绪后设为 true) +NEXT_PUBLIC_MF_ENABLED=false + +# Shell URL(standalone 壳用于 /api/ready 检查) +NEXT_PUBLIC_SHELL_URL=http://localhost:4000 + +# push-gateway WebSocket(实时通知) +NEXT_PUBLIC_WS_URL=ws://localhost:8081/ws + +# OTel collector +NEXT_PUBLIC_OTEL_ENDPOINT=http://localhost:4318 + +# Web Vitals 上报端点 +NEXT_PUBLIC_WEB_VITALS_ENDPOINT=/api/admin/web-vitals diff --git a/apps/admin-portal/Dockerfile b/apps/admin-portal/Dockerfile new file mode 100644 index 0000000..2094eb5 --- /dev/null +++ b/apps/admin-portal/Dockerfile @@ -0,0 +1,36 @@ +# admin-portal Dockerfile +# 仲裁:no-push 本地构建,使用官方镜像 +FROM node:22-alpine AS builder + +WORKDIR /app + +# 安装 pnpm +RUN corepack enable && corepack prepare pnpm@latest --activate + +# 复制 workspace 配置 +COPY pnpm-workspace.yaml pnpm-lock.yaml package.json tsconfig.base.json ./ +COPY packages/ ./packages/ +COPY apps/admin-portal/ ./apps/admin-portal/ + +# 安装依赖 +RUN pnpm install --frozen-lockfile + +# 构建 +WORKDIR /app/apps/admin-portal +RUN pnpm run build + +# 运行时 +FROM node:22-alpine AS runner + +WORKDIR /app + +ENV NODE_ENV=production +ENV PORT=4003 + +COPY --from=builder /app/apps/admin-portal/.next/standalone ./ +COPY --from=builder /app/apps/admin-portal/.next/static ./apps/admin-portal/.next/static +COPY --from=builder /app/apps/admin-portal/public ./apps/admin-portal/public + +EXPOSE 4003 + +CMD ["node", "apps/admin-portal/server.js"] diff --git a/apps/admin-portal/docs/01-understanding.md b/apps/admin-portal/docs/01-understanding.md index b19752f..073d861 100644 --- a/apps/admin-portal/docs/01-understanding.md +++ b/apps/admin-portal/docs/01-understanding.md @@ -1,42 +1,66 @@ # 模块理解确认书 — admin-portal -> AI:ai07(TS/React · 管理场景域前端 remote) -> 阶段:阶段 1 交付物 -> 日期:2026-07-09 -> 关联:[004 架构影响地图](../../../docs/architecture/004_architecture_impact_map.md) §1.1a/1.1b/§5.4、[AI 分配方案](../../../docs/architecture/ai-allocation.md) §5 ai07、[pending-features P6](../../../docs/architecture/roadmap/pending-features.md)、[known-issues §2.12](../../../docs/troubleshooting/known-issues.md)、[teacher-portal 阶段1](../../teacher-portal/docs/01-understanding.md)、[teacher-portal 阶段2](../../teacher-portal/docs/02-architecture-design.md) +> AI:ai16(TS/React · 管理场景域前端 remote) +> 阶段:阶段 1 交付物(仲裁后修订版 v2) +> 日期:2026-07-10 +> 关联: +> +> - [004 架构影响地图](../../../docs/architecture/004_architecture_impact_map.md) §1.1a/1.1b/§5.4 +> - [AI 分配方案](../../../docs/architecture/ai-allocation.md) §5 ai16 +> - [总统最终裁决](../../../docs/architecture/president-final-rulings.md) §5.1-5.5(ISSUE-044~048) +> - [admin-portal 对接契约](../../architecture/issues/contracts/admin-portal_contract.md) +> - [admin-portal 工作线](../../architecture/issues/worklines/admin-portal_workline.md) +> - [known-issues §2.16](../../../docs/troubleshooting/known-issues.md) + +--- + +## 0. 仲裁结果摘要(ARB-001~006) + +| 编号 | 议题 | 裁决 | +| -------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------- | +| ARB-001 | BFF 归属(ISSUE-044) | 复用 teacher-bff GraphQL endpoint,不新建 admin-bff;ai03 预留 `admin.*` 命名空间 | +| ARB-002 | 错误码前缀(ISSUE-045) | `ADMIN_` 前缀(合并 `SCHOOL_`/`ORG_` 入 `ADMIN_`);i18n key `error.admin.*` | +| ARB-003 | 权限点命名(ISSUE-046) | admin 自身资源用 `ADMIN_*`;跨服务资源保留原前缀(`IAM_USER_READ` 等) | +| ARB-004 | GraphQL 客户端(ISSUE-047) | MF 模式复用 Shell 暴露的 GraphQL client 单例;standalone 模式自建 urql client | +| ARB-005 | 审计日志归属(ISSUE-048) | 审计事件归 iam 发布到 `edu.iam.audit.created`;teacher-bff 消费聚合;admin-portal 经 GraphQL `auditLogs` Query 消费 | +| ARB-006 | WebSocket 实时通知 | 接入 push-gateway `GET /ws`(审计告警/异常登录/系统异常),不再用轮询 | +| 端口分配 | admin-portal dev server | **4003**(非旧文档的 3003) | --- ## 1. 我在架构中的位置 - **层级**:L2 微前端层(004 §3.1 六层架构中的前端层) -- **MF 角色**:**Remote 子应用**,挂载到 teacher-portal Shell(主应用) +- **MF 角色**:**Remote 子应用**,挂载到 teacher-portal Shell(主应用,端口 4000) - **上游(谁调用我)**:浏览器(系统管理员 / 校管理员) -- **下游(同步)**:api-gateway(REST,经 teacher-portal Shell 的 Next.js `rewrites` 代理 `/api/v1/*`) -- **下游(推送)**:— (**不消费推送**,管理端用轮询) -- **BFF 对接**:teacher-bff 复用(`/admin/*` 聚合)+ iam 直连(`/iam/*` 用户/角色/权限/视口 CRUD) -- **通信方式**:HTTP/REST(前端→Gateway) +- **下游(同步)**:api-gateway(GraphQL `POST /api/admin/graphql` + REST `/api/auth/login`) +- **下游(推送)**:push-gateway `GET /ws`(WebSocket 实时通知,ARB-006) +- **BFF 对接**:复用 teacher-bff GraphQL admin 命名空间(ARB-001),不新建 admin-bff +- **通信方式**:GraphQL(业务数据)+ HTTP(登录)+ WebSocket(实时通知) - **不直连**:前端不直连任何业务服务或 BFF 后端实例,全部经 api-gateway 代理 **说明**: -- admin-portal 作为 Remote 子应用,挂载到 teacher-portal Shell 提供的 AppShell + 共享依赖(react/react-dom/@tanstack/react-query/zustand/nuqs/ui-components/ui-tokens/contracts/hooks) -- 路由前缀 `/admin/*` 由 teacher-portal Shell 动态加载 admin-portal Remote 模块 -- 场景域 BFF 复用策略(004 §5.4):系统/校管理员复用 teacher-portal Shell + teacher-bff(`/admin/*` 视口)+ iam 直连,不单独建 portal 后端 -- 全部业务请求经 api-gateway 代理,admin-portal 本身不实现 rewrites(依赖 Shell 的 rewrites) +- admin-portal 作为 Remote 子应用,挂载到 teacher-portal Shell +- Shell 暴露 `GraphQLProvider` / `useGraphQLClient` / `AppShell` / `useAuth` / `usePermission` 等,admin-portal MF 模式复用,standalone 模式自建 +- 路由前缀 `/admin/*` 由 teacher-portal Shell 动态加载 admin-portal Remote 模块(暴露 `./AdminApp`) +- 场景域 BFF 复用策略(004 §5.4 + ARB-001):复用 teacher-bff GraphQL endpoint,admin 命名空间(`admin.*` Query/Mutation) ## 2. 我的限界上下文 ### 2.1 我负责的聚合 / 实体(前端视图模型) -- 用户、角色、权限、视口(管理场景域前端视图) +- 用户、角色、权限、视口(管理场景域前端视图,调 iam 经 GraphQL) - 组织(学校/年级/班级层级) -- 平台监控(运行时指标、用户活动、错误统计) -- 会话状态(Session)、视口(Viewport)、权限(Permission)(与 teacher-portal 共享,引用 teacher-portal 文档) +- 学校设置(system 路由) +- 班级/教师/学生全局管理(admin 视角,只读 + 状态管理) +- 审计日志(只读,经 teacher-bff GraphQL `auditLogs` Query 消费) +- 管理仪表盘(聚合统计 + 服务健康) +- 实时通知(WebSocket 推送:审计告警/异常登录/系统异常) ### 2.2 业务领域 -- **管理场景域**(前端场景域:管理场景域,对应后端 iam 限界上下文 + teacher-bff 管理视口) +- **管理场景域**(前端场景域:管理场景域,对应后端 iam 限界上下文 + teacher-bff admin 命名空间) ### 2.3 不负责 @@ -44,6 +68,7 @@ - 学生作答界面(归 student-portal) - 家长多子女切换(归 parent-portal) - 教师仪表盘(归 teacher-portal,admin-portal 仅有管理仪表盘) +- 审计事件发布(归 iam,admin-portal 仅消费) ### 2.4 数据范围 @@ -54,180 +79,206 @@ ### 3.1 消费的后端 API(经 api-gateway 代理) -| 路径前缀 | 下游 BFF/服务 | 关键端点 | -| ------------------------- | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | -| `/api/v1/iam/*`(管理用) | iam | `GET /iam/users`、`POST /iam/users`、`PUT /iam/users/:id`、`GET /iam/roles`、`GET /iam/permissions`、`GET /iam/viewports`、`PUT /iam/viewports/:id` | -| `/api/v1/admin/*` | teacher-bff 复用 + iam 直连 | `GET /admin/dashboard`、`GET /admin/monitoring/metrics`、`GET /admin/stats/users`、`GET /admin/stats/active-sessions` | -| `/api/v1/notifications/*` | msg | 通知中心(P5) | +| 路径 | Method | 下游 BFF/服务 | 用途 | +| ------------------------- | ------ | ----------------------------- | ------------------------------------------------- | +| `/api/admin/graphql` | POST | teacher-bff(admin 命名空间) | 全部业务 GraphQL 查询(ARB-001) | +| `/api/auth/login` | POST | api-gateway → iam | 登录(standalone 模式用,MF 模式复用 Shell 登录) | +| `GET /ws`(push-gateway) | WS | push-gateway | 实时通知(审计告警/异常登录/系统异常,ARB-006) | -### 3.2 统一响应契约 +### 3.2 GraphQL Operations(contract §2.4) -所有后端响应遵循 `ActionState` 结构(迁移指南 §7.5): +admin-portal 消费 teacher-bff admin 命名空间的 18 个 GraphQL operations: -```typescript -type ActionState = - | { success: true; data: T } - | { - success: false; - error: { code: string; message: string; details?: unknown }; - }; -``` +| Operation | 类型 | 用途 | +| ------------------------------------------------ | -------------- | ------------------------------- | +| `currentUser` | Query | 当前管理员信息 | +| `adminUsers` / `adminUser(id)` | Query | 用户列表 / 详情 | +| `createUser` / `updateUser` / `toggleUserStatus` | Mutation | 用户 CRUD | +| `adminRoles` | Query | 角色列表(含权限) | +| `createRole` / `updateRolePermissions` | Mutation | 角色 CRUD + 权限矩阵 | +| `adminPermissions` | Query | 全量权限点(按 resource) | +| `adminViewports(scope)` | Query | 视口配置列表 | +| `updateViewport` | Mutation | 视口配置更新 | +| `adminOrganization(parentId)` | Query | 组织树 | +| `adminClasses` | Query | 班级管理(全局) | +| `adminTeachers` | Query | 教师管理 | +| `adminStudents` | Query | 学生管理 | +| `auditLogs(filter)` | Query | 审计日志(聚合 iam AuditEvent) | +| `adminDashboard` | Query | 管理员仪表盘聚合 | +| `systemSettings` / `updateSystemSettings` | Query/Mutation | 学校设置 | -错误码前缀按服务名大写(admin-portal 涉及:`IAM_`、`BFF_TEACHER_`、`GW_`、`NETWORK_`)。前端 API 请求层根据 `error.code` 前缀路由到对应的 i18n key。 +### 3.3 推送契约(ARB-006) -### 3.3 推送契约 +admin-portal 接入 push-gateway `GET /ws`,消费 3 类实时通知: -**— N/A** +| 通知类型 | severity | 用途 | +| ---------------- | -------- | ------------------------- | +| `audit_alert` | warning | 审计告警(敏感操作触发) | +| `abnormal_login` | error | 异常登录(异地/异常时段) | +| `system_error` | error | 系统异常(服务降级/宕机) | -admin-portal **不消费** WebSocket 推送或 SSE 流式响应。管理端场景对实时性要求低,采用**轮询**策略: - -- 平台监控页:60s 轮询 `GET /api/v1/admin/monitoring/metrics` -- 用户活动统计:5min 轮询 `GET /api/v1/admin/stats/active-sessions` -- 通知中心(P5):HTTP 长轮询或定时拉取 `GET /api/v1/notifications`(不走 push-gateway) - -> 长期监控指标推荐用 `