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:
@@ -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 = XXX;replaceState 清除 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')
|
||||
|
||||
Reference in New Issue
Block a user