feat(admin-portal): docker 本地测试通过 + 下游工作清单

docker Desktop 本地验证:
- 镜像构建成功(edu/admin-portal:test)
- 容器运行正常(端口 4003)
- /api/health、/api/ready、/login、/admin/dashboard 全部 200

修复项:
- next.config.js 添加 output: standalone
- package.json 添加 autoprefixer 依赖
- login/layout.tsx 提供 AuthProvider/ToastProvider

部署配置:
- docker-compose.deploy.yml 添加 admin-portal 服务
- deploy.env.example 添加 ADMIN_PORTAL_PORT

下游工作清单(nextstep.md):
- api-gateway: 缺 /api/admin/graphql 路由
- teacher-bff: 缺 16 Query + 11 Mutation
- iam: 缺 BatchGetUsers 等 RPC
This commit is contained in:
SpecialX
2026-07-13 16:19:35 +08:00
parent 18d94c0cc2
commit 64b2016fa8
7 changed files with 882 additions and 0 deletions

View File

@@ -0,0 +1,156 @@
# admin-portal 下游工作清单Next Steps
> 负责人ai16
> 更新日期2026-07-13
> 关联:[admin-portal_contract.md](../../docs/architecture/issues/contracts/admin-portal_contract.md)、[admin-portal_workline.md](../../docs/architecture/issues/worklines/admin-portal_workline.md)
---
## 1. 概述
admin-portal 前端已全部完成P2-P6 + §7 差距分析 4 批次 + Vitest 测试Docker 镜像构建成功并在本地 Docker Desktop 验证通过。但真实后端集成需要下游服务补齐以下工作。
**本地 Docker 测试结果2026-07-13**
- ✅ 镜像构建成功(`edu/admin-portal:test`
- ✅ 容器启动正常(端口 4003
-`/api/health` 返回 200
-`/api/ready` 返回 200gateway 可达)
-`/login` 页面返回 200
-`/admin/dashboard` 页面返回 200
-`/api/admin/graphql` 返回 500api-gateway 返回 404路由未配置
---
## 2. 下游阻塞项
### 2.1 api-gatewayai01 负责)
**阻塞级别P0必须完成才能端到端联调**
| # | 工作项 | 详情 |
| --- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| 1 | 新增 `/api/admin/graphql` 路由 | 当前 api-gateway 仅有 `/api/v1/*` 路由组,无 `/api/admin/*` 路由。需新增 POST `/api/admin/graphql` 代理到 teacher-bff :3003/graphql |
| 2 | admin 角色强制校验中间件 | 在 `/api/admin/*` 路由组上添加 admin 角色门禁,拒绝非 admin 角色访问 |
| 3 | 路径对齐契约 §2.3 | 契约要求 `POST /api/admin/graphql` 为唯一入口,当前 api-gateway 无此路由 |
**相关文件:**
- `services/api-gateway/main.go`L69-104路由注册
- `services/api-gateway/internal/middleware/auth.go`(鉴权中间件)
- `services/api-gateway/internal/proxy/proxy.go`(反向代理)
- `services/api-gateway/internal/config/config.go`(配置)
### 2.2 teacher-bffai03 负责)
**阻塞级别P0必须完成才能端到端联调**
#### 2.2.1 缺失的 Query16 个)
| # | 契约 Query | 当前状态 | 备注 |
| --- | ----------------------- | --------- | ----------------------------------- |
| 1 | `adminPermissions` | ❌ 未实现 | 权限点列表 |
| 2 | `adminViewports(scope)` | ❌ 未实现 | 视口配置,需 scope 参数 |
| 3 | `adminClasses` | ❌ 未实现 | 班级全局管理 |
| 4 | `adminTeachers` | ❌ 未实现 | 教师全局管理 |
| 5 | `adminStudents` | ❌ 未实现 | 学生全局管理 |
| 6 | `adminDashboard` | ❌ 未实现 | 管理仪表盘聚合数据 |
| 7 | `adminSchools` | ❌ 未实现 | 多学校列表(当前仅有单数 `school` |
| 8 | `adminGrades` | ❌ 未实现 | 年级管理 |
| 9 | `adminDepartments` | ❌ 未实现 | 部门/教研组管理 |
| 10 | `adminAcademicYears` | ❌ 未实现 | 学年学期管理 |
| 11 | `adminAnnouncements` | ❌ 未实现 | 公告列表 |
| 12 | `adminInvitationCodes` | ❌ 未实现 | 邀请码列表 |
| 13 | `adminFiles` | ❌ 未实现 | 文件列表 |
| 14 | `adminFileStats` | ❌ 未实现 | 文件统计 |
| 15 | `aiProviders` | ❌ 未实现 | AI Provider 列表 |
| 16 | `aiUsage` | ❌ 未实现 | AI 用量统计 |
#### 2.2.2 缺失的 Mutation11 个)
| # | 契约 Mutation | 当前状态 | 备注 |
| --- | ------------------------- | --------- | --------------------- |
| 1 | `toggleUserStatus` | ❌ 未实现 | 用户启用/禁用/锁定 |
| 2 | `updateRolePermissions` | ❌ 未实现 | 角色权限矩阵更新 |
| 3 | `updateViewport` | ❌ 未实现 | 视口配置更新 |
| 4 | `createAnnouncement` | ❌ 未实现 | 创建公告 |
| 5 | `publishAnnouncement` | ❌ 未实现 | 发布公告 |
| 6 | `archiveAnnouncement` | ❌ 未实现 | 归档公告 |
| 7 | `toggleAnnouncementPin` | ❌ 未实现 | 公告置顶切换 |
| 8 | `generateInvitationCodes` | ❌ 未实现 | 批量生成邀请码 |
| 9 | `deleteInvitationCodes` | ❌ 未实现 | 删除邀请码 |
| 10 | `deleteFiles` | ❌ 未实现 | 批量删除文件 |
| 11 | `updateAiProvider` | ❌ 未实现 | 更新 AI Provider 配置 |
#### 2.2.3 已实现但需对齐的项
| # | 工作项 | 详情 |
| --- | ------------------------------------ | -------------------------------------------------------------------------------------------------------- |
| 1 | 命名规范对齐 | 契约用扁平命名 `adminUsers`/`adminRoles`,实际实现用命名空间 `admin { users }`。需 ai03 与 ai16 协调统一 |
| 2 | `auditLogs` 补 filter 参数 | 当前仅 `page/pageSize`,需补 `action/userId/dateRange` 等 filter |
| 3 | `adminOrganization` 补 parentId 参数 | 当前无参数,需支持 `parentId` 筛选子节点 |
| 4 | 用户/角色 CRUD 接入真实 iam gRPC | 当前返回 mock 数据 |
**相关文件:**
- `services/teacher-bff/src/admin/admin.resolver.ts`
- `services/teacher-bff/src/admin/admin.service.ts`
- `services/teacher-bff/src/admin/admin.module.ts`
- `services/teacher-bff/src/graphql/graphql.controller.ts`
- `services/teacher-bff/src/graphql/graphql.module.ts`
- `packages/shared-ts/contracts/graphql/teacher-bff.schema.graphql`
### 2.3 iam 服务ai06 负责,间接影响)
**阻塞级别P1影响 admin 用户管理真实性)**
| # | 工作项 | 详情 |
| --- | --------------------------------------------- | ---------------------------------- |
| 1 | 补 `BatchGetUsers` RPC | admin 用户列表需要批量获取用户信息 |
| 2 | 补 `CreateUser`/`UpdateUser`/`DeleteUser` RPC | admin 用户 CRUD 需要 iam 支持 |
---
## 3. admin-portal 前端待办(非阻塞)
以下工作不阻塞当前部署,但需在下游就绪后完成:
| # | 工作项 | 触发条件 |
| --- | ------------------------------------------------ | -------------------------------- |
| 1 | GraphQL query 字符串对齐命名规范 | ai03 与 ai16 协调命名后 |
| 2 | 移除 MSW mock 依赖(生产构建已移除) | api-gateway + teacher-bff 就绪后 |
| 3 | MF 模式集成测试(`NEXT_PUBLIC_MF_ENABLED=true` | teacher-portal Shell 就绪后 |
| 4 | OTel trace 上报联调 | otel-collector 就绪后 |
| 5 | WebSocket 实时通知联调 | push-gateway 就绪后 |
---
## 4. 已完成项汇总
| 工作项 | 状态 | 验证方式 |
| ------------------------------------------ | ---- | ------------------------------------------------------------- |
| P2 MF Remote 骨架 | ✅ | arch:scan + typecheck |
| P3 用户/角色权限管理 | ✅ | typecheck + lint + vitest |
| P4 组织/学校/全局实体 | ✅ | typecheck + lint + vitest |
| P5 审计日志/实时通知 | ✅ | typecheck + lint + vitest |
| P6 硬化A11y/Web Vitals/测试/Dockerfile | ✅ | vitest 69/69 + Docker 构建成功 |
| §7 差距分析 4 批次补齐 | ✅ | typecheck + lint + vitest |
| Docker 镜像构建 | ✅ | `edu/admin-portal:test` 构建成功 |
| Docker 容器运行 | ✅ | /api/health + /api/ready + /login + /admin/dashboard 全部 200 |
| docker-compose.deploy.yml 服务定义 | ✅ | admin-portal 服务已添加 |
| next.config.js output: standalone | ✅ | Docker 构建产物正确 |
| .env.local 关闭 mock | ✅ | NEXT_PUBLIC_API_MOCKING=disabled |
---
## 5. 联调顺序建议
1. **ai01** 完成 api-gateway `/api/admin/graphql` 路由 + admin 角色校验
2. **ai03** 完成 teacher-bff admin Query/Mutation 补齐 + 命名规范对齐
3. **ai06** 完成 iam gRPC 补齐BatchGetUsers 等)
4. **ai16** 前端 GraphQL query 字符串对齐命名规范
5. 端到端联调admin-portal → api-gateway → teacher-bff → iam/classes/core-edu
---
**本文件由 ai16 维护,下游工作项请各负责 AI 完成后通知 ai16 更新状态。**

View File

@@ -3,6 +3,7 @@ const NextFederationPlugin = require("@module-federation/nextjs-mf");
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = { const nextConfig = {
reactStrictMode: true, reactStrictMode: true,
output: "standalone",
transpilePackages: [ transpilePackages: [
"@edu/ui-tokens", "@edu/ui-tokens",
"@edu/ui-components", "@edu/ui-components",

View File

@@ -41,6 +41,7 @@
"@types/react": "^18.3.0", "@types/react": "^18.3.0",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.0", "@vitejs/plugin-react": "^4.3.0",
"autoprefixer": "^10.4.20",
"eslint": "^9.0.0", "eslint": "^9.0.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-jsx-a11y": "^6.10.0",

View File

@@ -0,0 +1,28 @@
"use client";
import { type ReactNode } from "react";
import { GraphQLProvider } from "@/providers/graphql-provider";
import { AuthProvider } from "@/providers/auth-provider";
import { ToastProvider } from "@/providers/toast-provider";
import { MswInitializer } from "@/components/msw-initializer";
import { useState } from "react";
/**
* Login layout - 提供 login 页面所需的 providers
*
* login 页面不在 /admin 下,需要独立的 provider 链
*/
export default function LoginLayout({ children }: { children: ReactNode }) {
const [mswReady, setMswReady] = useState(false);
return (
<GraphQLProvider>
<MswInitializer onReady={() => setMswReady(true)} />
{mswReady && (
<AuthProvider>
<ToastProvider>{children}</ToastProvider>
</AuthProvider>
)}
</GraphQLProvider>
);
}

View File

@@ -26,6 +26,7 @@ JWT_AUDIENCE=next-edu-cloud
# ============ 服务端口 ============ # ============ 服务端口 ============
API_GATEWAY_PORT=8080 API_GATEWAY_PORT=8080
TEACHER_PORTAL_PORT=3000 TEACHER_PORTAL_PORT=3000
ADMIN_PORTAL_PORT=4003
# ============ KafkaP3+ 启用,留空则 Outbox publisher 持续重试)============ # ============ KafkaP3+ 启用,留空则 Outbox publisher 持续重试)============
KAFKA_BROKERS= KAFKA_BROKERS=

View File

@@ -315,6 +315,40 @@ services:
- edu-net - edu-net
- edu-shared - edu-shared
admin-portal:
build:
context: ./repo
dockerfile: apps/admin-portal/Dockerfile
container_name: edu-admin-portal
restart: unless-stopped
environment:
NODE_ENV: production
PORT: 4003
API_GATEWAY_URL: http://api-gateway:8080
# 生产环境强制关闭 mock使用真实后端
NEXT_PUBLIC_API_MOCKING: "disabled"
NEXT_PUBLIC_MF_ENABLED: "false"
NEXT_PUBLIC_SHELL_URL: http://admin-portal:4003
NEXT_PUBLIC_WS_URL: ws://push-gateway:8081/ws
NEXT_PUBLIC_OTEL_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-http://otel-collector:4318}
NEXT_PUBLIC_WEB_VITALS_ENDPOINT: /api/admin/web-vitals
ports:
- "${ADMIN_PORTAL_PORT:-4003}:4003"
depends_on:
api-gateway:
condition: service_healthy
teacher-bff:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--quiet", "--spider", "http://localhost:4003/api/ready"]
interval: 30s
timeout: 5s
start_period: 20s
retries: 3
networks:
- edu-net
- edu-shared
networks: networks:
# 应用服务内部网络 # 应用服务内部网络
edu-net: edu-net:

661
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff