docs: test reports + knowledge iteration design + PRDs

提交 OTP/RBAC/Tier0/Tier1/P0+P2 测试报告、方案A E2E 验证、知识库迭代设计(PRD/mermaid/html 原型)、项目状态看板更新; 根配置 docker-compose.yml/mkdocs.yml。
This commit is contained in:
Simon
2026-07-09 11:50:19 +08:00
parent 584c975e7f
commit e4e2de47bb
21 changed files with 3311 additions and 214 deletions
+41 -95
View File
@@ -1,99 +1,45 @@
%% 4.1 员工端 OAuth 静默授权(snsapi_base → ?token= 镜像)
sequenceDiagram
participant U as 员工(企微WebView)
participant H5 as H5前端(/itdesk/)
participant R as 路由守卫
participant B as 后端(/api/h5)
participant W as 企微OAuth
U->>H5: 打开 /itdesk/
H5->>R: beforeEach 守卫
R->>R: 读 ?token=(无) / ?code=(无) / 无 token
R->>B: GET /h5/oauth/authorize (prod 校验 wxwork UA)
B-->>R: {authorize_url}
R->>W: 302 跳转企微授权页
W-->>H5: 回调 redirect_uri?code=CODE
H5->>B: GET /h5/oauth/sns-callback?code=CODE
B->>W: code 换 userid + 用户信息
B->>B: 生成 employee token 存 Redis(employee:token:)
B-->>H5: 302 /itdesk/?token=XXX
H5->>R: 守卫读 ?token=XXX
R->>R: localStorage.h5_token = XXXreplaceState 清除 URL
R->>B: 携带 Bearer 拉取用户信息
B-->>H5: 工作台数据(内层 data
participant User as 坐席
participant LoginVue as Login.vue
participant AgentStore as agentStore
participant OtpBind as OtpBindPanel.vue
participant API as apiClient
participant Backend as FastAPI Backend
participant Redis as Redis
%% 4.2 坐席/管理 扫码登录(auth_qrcode
sequenceDiagram
participant A as 坐席/管理员
participant FE as 前端登录页
participant B as 后端(/api/auth_qrcode)
participant WX as 企微App(扫码确认)
participant R as Redis
A->>FE: 点击「企微扫码登录」
FE->>B: POST /auth_qrcode/create
B->>R: 写 ticket(120s) + OAuth URL
B-->>FE: {ticket, qrcode_png_base64}
FE->>FE: 展示二维码 + 2s 轮询
loop 轮询
FE->>B: GET /auth_qrcode/poll/{ticket}
B-->>FE: {status: waiting/scanned}
end
WX->>B: GET /auth_qrcode/scan?code&state=ticket (企微OAuth回调)
B->>R: 写 scan:{ticket}
A->>WX: 在企微点「确认登录」
WX->>B: POST /auth_qrcode/confirm {ticket}
B->>B: 校验身份→签发 token(agent/admin)
B->>R: 写 confirm:{ticket}=token
FE->>B: GET /poll/{ticket} → {status:confirmed, token}
FE->>FE: localStorage.agent_token/admin_token = token
FE->>FE: 跳 /workspace 或 /
User->>LoginVue: 输入账号密码 → 点击登录
LoginVue->>AgentStore: login(userId, password, undefined)
AgentStore->>API: POST /api/agents/login {user_id, password}
API->>Backend: agent_login()
Note over Backend: agent.mfa_enabled == False
Backend-->>API: { require_otp_bind: true, user_id, name, role }
API-->>AgentStore: { require_otp_bind: true, ... }
AgentStore-->>LoginVue: return { require_otp_bind: true }
%% 4.3 坐席/管理 账号密码 + OTP
sequenceDiagram
participant A as 坐席/管理员
participant FE as 前端登录页
participant B as 后端(/api/agents/login)
participant M as MFAService/Redis
A->>FE: 输入账号+密码,点登录
FE->>B: POST /agents/login {user_id, password}
alt 已绑定 MFA 且无 otp_code
B-->>FE: {require_otp:true, user_id, name, role}(无 token
FE->>FE: 渲染 OTP 输入框(v-if requireOtp)
A->>FE: 输入 6 位 OTP
FE->>B: POST /agents/login {user_id, password, otp_code}
B->>M: verify_code(mfa_secret, otp_code)
M-->>B: True
B->>B: 签发 token
B-->>FE: {token, user_id, name, role}
else 未绑定 MFA
B-->>FE: {token, ...} 直接登录
end
FE->>FE: localStorage.agent_token/admin_token = token;跳主页
Note over LoginVue: 隐藏账密表单<br/>显示 OtpBindPanel
LoginVue->>OtpBind: requireOtpBind = true
%% 4.4 令牌过期 / 401 处理
sequenceDiagram
participant FE as 三端前端
participant I as 响应拦截器
participant B as 后端
participant R as Redis
FE->>B: 业务请求(Bearer token)
B-->>FE: 401 / {code:1002}
alt H5 员工端
I->>I: 清 h5_token
alt 生产(有 CorpId)
I->>B: 重走 OAuth 重定向(带防循环计数)
else Mock(dev)
I->>FE: 跳 /itdesk/login
end
else 坐席端
I->>B: POST /api/auth/refresh?token=(静默)
B->>R: 延长 user:token TTL
alt 刷新成功
I->>FE: 重放原请求
else 失败
I->>I: 清 agent_token(不再清 portal_token
I->>FE: 跳 /login
end
else 管理端
I->>I: 清 admin_token
I->>FE: 跳 /login
end
OtpBind->>API: POST /api/auth/otp-bind
API->>Backend: bind_otp()
Note over Backend: 生成 secret + QR
Backend-->>API: { secret, otpauth_url, qr_code_base64 }
API-->>OtpBind: { secret, otpauth_url, qr_code_base64 }
Note over OtpBind: 渲染二维码 + secret 明文
User->>User: 用 Authenticator 扫码(或手动输入 secret
User->>OtpBind: 输入 6 位验证码 → 点击"验证并完成绑定"
OtpBind->>API: POST /api/auth/otp-verify { otp_code }
API->>Backend: verify_otp()
Note over Backend: mfa_enabled=False (绑定场景)<br/>校验 TOTP → 通过<br/>设置 mfa_enabled=True<br/>设置 mfa_bound_at=now
Backend->>Redis: mark_verified(employee_id, TTL=1800)
Backend->>Backend: 签发 JWT token
Backend-->>API: { verified: true, token, user_id, name, role }
API-->>OtpBind: { verified: true, token, ... }
OtpBind-->>LoginVue: emit('bind-success', { token, user_id, name })
LoginVue->>AgentStore: 保存 token → 设置 agentInfo
LoginVue->>LoginVue: router.push('/workspace')