corpId + userId 计数。flowchart TD
A["启动客户端或打开登录页"] --> B{"Fmode Token 有效"}
B -- "否" --> B1["配置或刷新 Token"] --> B
B -- "是" --> C["查询订阅状态"]
C --> D{"订阅有效"}
D -- "否" --> D1["开通或续费"] --> C
D -- "是" --> E["查询已绑定账号"]
E --> F{"选择登录方式"}
F -- "已有账号在线" --> S["绑定当前电脑并共享会话"]
F -- "已有账号离线" --> G["复用账号 guid 生成二维码"]
F -- "登录其他账号" --> H["创建临时 uid 和上游实例"]
G --> Q["扫码与验证码状态轮询"]
H --> Q
Q --> I{"登录成功并获得 corpId + userId"}
I -- "身份已绑定" --> R["复用席位并更新 canonical guid"] --> S
I -- "新身份" --> J{"客户席位有空余"}
J -- "是" --> K["正式绑定新账号"] --> S
J -- "否" --> L{"购买、替换或取消"}
L -- "新增购买席位" --> M["实时询价并幂等扣费"]
M --> N["重新查询席位并提交登录"] --> K
L -- "替换已有账号" --> O["确认目标账号并停止旧 guid"] --> K
L -- "取消" --> P["停止临时实例并清理"]
S --> T["登录完成并启动监听或 Relay"]
| 接口 | 用途 |
|---|---|
GET /login/accounts |
返回当前租户已绑定账号、在线状态和席位摘要 |
POST /login/select |
将当前电脑 uid 绑定到已有账号 |
POST /login/start |
创建或复用上游登录会话;newAccount=true 时创建临时 uid |
POST /login/check |
轮询二维码;成功后按账号身份去重并返回席位决策 |
POST /login/resolve |
执行购买后提交、替换或取消 |
GET /subscribe/status?seats=N&months=1 |
获取增购实时补差报价 |
POST /subscribe |
使用报价金额和幂等键完成增购 |
扫码身份为新账号且席位已满时,/login/check 返回:
{
"status": 2,
"decisionRequired": true,
"loginState": "decision_required",
"actions": ["purchase", "replace", "cancel"]
}
purchase:按当前席位数加一询价并购买;购买成功后再次加锁校验,随后自动绑定当前扫码账号。replace:客户选择一个已绑定账号,二次确认后停止旧会话并绑定新账号。cancel:停止临时会话并清理登录尝试。支付和账号绑定使用相同租户锁串行化。支付成功但绑定请求中断时,客户端复用原幂等订单并再次执行 purchase 提交,避免重复扣费。
QiweiDevice 记录存在 guid + lastLoginAt 时,懒迁移为一个 legacy 账号席位。