文档更新
This commit is contained in:
@@ -2,17 +2,27 @@
|
||||
|
||||
**状态**: Draft
|
||||
**作者**: 产品经理
|
||||
**最后更新**: 2026-04-30(v2.0 根据 review 后的 §4 用户故事全面同步 §5 功能详细说明:删除找回用户名流程及邮件模板;找回密码改为纯短信流程;新增 §5.5 手机验证码登录详细说明;§6 技术注意事项更新短信依赖/风险/开放问题;§8.2 接口清单同步正式功能状态)
|
||||
**版本**: 2.0
|
||||
**最后更新**: 2026-05-02(v3.0 按 `ADR-20260502-003` 重写:剥离全部实现细节——具体 API 路径、HTTP 方法、JSON Schema、Redis Key、字段类型、Electron API 名、Cookie 属性、限流接口实现等——交由 Tech / DATA_MODEL 承接;本文件只保留业务能力、页面与导航、业务规则、状态机、验收标准)
|
||||
**版本**: 3.0
|
||||
**所属系统**: Fonrey 房产经纪管理系统
|
||||
**关联模块**: 组织人事管理、权限管理、系统管理
|
||||
**关联模块**: 组织人事管理、权限管理、平台管理后台
|
||||
|
||||
## 关联文档(实现口径)
|
||||
|
||||
- 实现细节(端点路径、HTTP 方法、JSON Schema、错误码、滑块/OTP 算法、限流策略、Electron 客户端约定):`TECH_STACK/登录管理技术方案.md`(v4.1)
|
||||
- 数据结构(账号表、登录审计表、短信 OTP 表、历史密码表、Redis Key 命名、字段类型与索引):`DATA_MODEL/DATA_MODEL_LOGIN.md`
|
||||
- 全局 API 契约(统一错误响应、分页、限流外显规范):`TECH_STACK/API_CONTRACT.md`
|
||||
- 测试用例:`TEST_CASES/TEST_CASES_LOGIN_MODULE.md`
|
||||
|
||||
> 本 PRD 涉及的"具体路径 / 字段名 / Token TTL 数值 / Cookie 属性 / Redis Key / 框架 API 名"等实现口径,**均不在本文件出现**,请按上表查阅对应权威文档。
|
||||
|
||||
## 变更历史
|
||||
|
||||
| 版本 | 日期 | 作者 | 变更说明 |
|
||||
|------|------|------|---------|
|
||||
| v1.x | — | 产品经理 | 初稿至 v1.9(详见 git 历史) |
|
||||
| 版本 | 日期 | 作者 | 变更说明 |
|
||||
| ---- | ---------- | ---- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| v1.x | — | 产品经理 | 初稿至 v1.9(详见 git 历史) |
|
||||
| v2.0 | 2026-04-30 | 产品经理 | 根据 review 后的 §4 用户故事全面同步 §5 功能详细说明:删除找回用户名流程及邮件模板;找回密码改为纯短信流程;新增 §5.5 手机验证码登录详细说明;§6 技术注意事项更新短信依赖/风险/开放问题;§8.2 接口清单同步正式功能状态;全文「Tenant ID」对外概念统一替换为「Tenant Code」 |
|
||||
| v3.0 | 2026-05-02 | Sisyphus | 按 `ADR-20260502-003` 重写:剥离全部实现细节交由 Tech / DATA_MODEL 承接;删除 §5.1.4 接口规范代码块、§5.2.2 Redis Key 与传输层细节、§5.3.3 字段类型表、§5.6 数据模型字段清单、§5.7 Electron 客户端约定、§6.1 依赖技术选型表、§6.2 多租户实现细节、§8.2 接口清单汇总;保留页面/导航/业务规则/状态机/验收标准;具体口径全部以 `TECH_STACK/登录管理技术方案.md` 为准 |
|
||||
|
||||
|
||||
---
|
||||
@@ -21,14 +31,14 @@
|
||||
|
||||
### 1.1 背景
|
||||
|
||||
Fonrey 是一套面向房产经纪公司的 B2B SaaS 平台,采用多租户架构(`django-tenants` + PostgreSQL Schema 隔离)。终端用户通过 **Windows 桌面客户端(Electron)** 使用系统,无需手动输入网址即可打开 Web 应用。
|
||||
Fonrey 是一套面向房产经纪公司的 B2B SaaS 平台,采用多租户架构,每家经纪公司是一个独立租户。终端用户通过 **Windows 桌面客户端(Electron 壳应用)** 使用系统,无需手动输入网址即可打开 Web 应用。
|
||||
|
||||
在多租户环境下,用户的身份验证流程比单租户系统更复杂:
|
||||
|
||||
- 用户安装客户端后,系统必须先识别当前设备归属哪个租户,才能加载对应租户的登录界面和数据隔离环境
|
||||
- 每家经纪公司作为独立租户,其员工账号、组织结构、数据均完全隔离
|
||||
- 经纪人账号须与实名员工档案绑定,确保每一条操作记录可追溯至具体自然人
|
||||
- 现阶段登录方式以账号密码为主;手机验证码登录、微信扫码登录需预留接口,待移动端(小程序)上线后实现
|
||||
- 现阶段登录方式以账号密码与手机验证码为主;微信扫码登录需预留入口,待移动端(小程序)上线后实现
|
||||
|
||||
### 1.2 核心痛点
|
||||
|
||||
@@ -36,7 +46,7 @@ Fonrey 是一套面向房产经纪公司的 B2B SaaS 平台,采用多租户架
|
||||
|------|--------|---------|
|
||||
| 多租户环境下,客户端不知道应该连接哪个租户的服务端 | 新用户首次安装后无法正常使用 | 系统无法启动,用户体验极差 |
|
||||
| 账号密码裸露登录,缺乏验证码保护 | 所有用户 | 存在暴力破解、自动化恶意登录风险 |
|
||||
| 用户忘记账号或密码无自助找回通道 | Agent(经纪人) | 依赖管理员手动重置,效率低 |
|
||||
| 用户忘记密码无自助找回通道 | Agent(经纪人) | 依赖管理员手动重置,效率低 |
|
||||
| 账号未与实名经纪人档案绑定 | Tenant Admin(租户管理员)、合规审计 | 操作行为无法追溯至自然人 |
|
||||
| 无多因素认证,安全系数低 | 管理层、数据合规 | 存在账号冒用、数据泄露风险 |
|
||||
|
||||
@@ -56,45 +66,97 @@ Fonrey 是一套面向房产经纪公司的 B2B SaaS 平台,采用多租户架
|
||||
| 目标 | 指标 | 当前基准 | 目标值 | 衡量周期 |
|
||||
|------|------|----------|--------|----------|
|
||||
| 降低登录失败率 | 账号密码正确情况下的登录成功率 | 待统计 | ≥ 99% | 上线后 30 天 |
|
||||
| 防止恶意登录 | 每日验证码拦截异常登录请求数 | 0(无保护) | 建立基线,同IP异常次数 > 5次/分钟触发封锁 | 上线后持续监控 |
|
||||
| 提升找回账号效率 | 用户自助找回密码耗时 | 依赖管理员(约1工作日) | < 3 分钟(短信验证码自助) | 上线后 30 天 |
|
||||
| 防止恶意登录 | 每日验证码拦截异常登录请求数 | 0(无保护) | 建立基线,对同 IP 异常突增触发封锁 | 上线后持续监控 |
|
||||
| 提升找回账号效率 | 用户自助找回密码耗时 | 依赖管理员(约 1 工作日) | < 3 分钟(短信验证码自助) | 上线后 30 天 |
|
||||
| 确保账号实名绑定率 | 拥有系统账号且未与员工档案绑定的账号比例 | 待统计 | 0%(强制绑定) | 上线即达标 |
|
||||
| Tenant 识别成功率 | 首次安装后成功完成 Tenant 识别的用户比例 | 待统计 | ≥ 98% | 上线后 30 天 |
|
||||
|
||||
> 具体的同 IP 限流阈值与 Tenant Code 校验频控数值,详见 `TECH_STACK/登录管理技术方案.md`。
|
||||
|
||||
---
|
||||
|
||||
## 3. 非目标(本期不做)
|
||||
|
||||
- **手机验证码登录**:移动端小程序上线后实现,本期**接口预留**,UI 入口以「即将开放」禁用态展示
|
||||
> **注意**:本条已更新。手机验证码登录已升级为 **MVP 正式功能**(见 Story 5),与密码登录并列提供。此非目标条目保留仅作版本记录,已失效。
|
||||
- **微信扫码登录**:移动端小程序上线后实现,本期**接口预留**,UI 入口以「即将开放」禁用态展示
|
||||
- **微信扫码登录**:移动端小程序上线后实现,本期**仅在登录页保留禁用入口**,不开放实际功能
|
||||
- **单点登录(SSO)/ 企业微信集成**:后续版本规划
|
||||
- **多设备并发登录的强制踢出策略**:本期允许同账号多端登录,后续安全策略模块规划
|
||||
- **登录时段限制 / IP 白名单**:安全策略模块另行规划
|
||||
- **管理后台(Platform Admin)登录**:Tenant Admin(租户管理员)登录管理后台的流程属于系统管理模块,本 PRD 专注租户内用户登录
|
||||
- **平台管理后台(Platform Admin)登录**:属于平台管理后台模块,本 PRD 专注租户内用户登录
|
||||
- **找回用户名流程**:普通员工用户名固定为手机号,无需找回;Tenant Admin 如忘记用户名请联系平台运营线下处理(原 Story 4 已废弃)
|
||||
|
||||
---
|
||||
|
||||
## 4. 用户故事与验收标准
|
||||
## 4. 页面清单与导航
|
||||
|
||||
> 本节描述用户视角的页面与跳转关系;具体的前端路由实现与 URL 形态由 `TECH_STACK/登录管理技术方案.md` 定义。
|
||||
|
||||
### 4.1 页面清单
|
||||
|
||||
| 页面 | 访问权限 | 用途 |
|
||||
|---|---|---|
|
||||
| Tenant 识别页 | 公开(无需登录) | 客户端首次启动或切换公司时输入租户识别码 |
|
||||
| 登录页 | 公开(需先完成 Tenant 识别) | 提供「密码登录」/「验证码登录」两种方式,含微信扫码禁用入口 |
|
||||
| 找回密码页(三步) | 公开 | 输入手机号 → 输入短信验证码 → 设置新密码 |
|
||||
| 首次登录强制改密页 | 已登录且账号处于初始密码状态 | 强制用户修改初始密码后方可使用系统 |
|
||||
| 系统首页 | 已登录且非初始密码状态 | 业务入口;不在本 PRD 范围 |
|
||||
|
||||
### 4.2 导航流程
|
||||
|
||||
```
|
||||
[客户端启动]
|
||||
│
|
||||
├─ 本地无 Tenant 缓存 ──→ [Tenant 识别页]
|
||||
│ │ 验证成功
|
||||
│ ↓
|
||||
│ 缓存 Tenant ──→ [登录页]
|
||||
│
|
||||
└─ 本地有 Tenant 缓存 ──→ [登录页]
|
||||
|
||||
[登录页]
|
||||
│
|
||||
├─ 密码 / 验证码登录成功
|
||||
│ │
|
||||
│ ├─ 账号为"初始密码"状态 ──→ [首次登录强制改密页] ──改密成功──→ [系统首页]
|
||||
│ └─ 账号为"非初始密码"状态 ──→ [系统首页]
|
||||
│
|
||||
├─ 点击"忘记密码" ──→ [找回密码页(步骤一)] → [步骤二] → [步骤三] ──→ 回 [登录页]
|
||||
│
|
||||
└─ 点击"切换公司"(二次确认)──→ 清缓存 ──→ [Tenant 识别页]
|
||||
|
||||
[系统内]
|
||||
│
|
||||
├─ 主动登出 ──→ [登录页]
|
||||
├─ Session 过期 ──→ [登录页](提示"登录已过期,请重新登录")
|
||||
└─ 客户端版本过低 ──→ [更新提示页] → 阻断后续登录流程
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 用户故事与验收标准
|
||||
|
||||
### Story 1:新用户首次启动客户端——Tenant 识别
|
||||
|
||||
**As** 新安装 Fonrey 客户端的经纪人,**I want** 在首次启动时输入所属公司的 12位 Tenant Code 完成租户识别,**So that** 客户端能连接到正确的服务端,后续显示对应公司的登录界面和数据。
|
||||
**As** 新安装 Fonrey 客户端的经纪人,**I want** 在首次启动时输入所属公司的 12 位 Tenant Code 完成租户识别,**So that** 客户端能连接到正确的服务端,后续显示对应公司的登录界面和数据。
|
||||
|
||||
**验收标准**:
|
||||
|
||||
- [ ] 客户端首次启动时(本地无 Tenant Code 缓存),自动呈现「Tenant 识别」界面,而非直接显示登录界面
|
||||
- [ ] 界面包含:产品 Logo、产品名称「Fonrey 房睿」、说明文案「请输入您公司的专属识别码」、Tenant Code 输入框、「确认」按钮
|
||||
- [ ] Tenant Code 输入框支持粘贴操作,自动去除前后空格
|
||||
- [ ] 点击「确认」后,客户端向服务端发起 Tenant 验证请求(`POST /api/auth/tenant/verify/`),展示加载状态(spinner)
|
||||
- [ ] **验证成功**:服务端返回租户名称及品牌信息(如公司名称、Logo URL);客户端将 Tenant Code 写入本地持久化存储,自动跳转至该租户的登录界面;界面顶部展示「正在登录:XX 房产」
|
||||
- [ ] **验证失败(Tenant Code 无效)**:输入框下方显示红色错误提示「识别码无效,请联系您的Tenant Admin(租户管理员)获取正确的识别码」;Tenant Code 不写入本地缓存;用户可重新输入
|
||||
- [ ] 客户端首次启动时(本地无 Tenant Code 缓存),自动呈现「Tenant 识别」页面,而非直接显示登录页
|
||||
- [ ] 页面包含:产品 Logo、产品名称「Fonrey 房睿」、说明文案「请输入您公司的专属识别码」、Tenant Code 输入框、「确认」按钮、帮助文案「不知道识别码?请联系您公司的Tenant Admin(租户管理员)」
|
||||
- [ ] Tenant Code 输入框支持粘贴操作,自动去除前后空格,仅接受数字字符(非数字自动过滤),固定 12 位
|
||||
- [ ] 点击「确认」后展示加载状态(spinner),由客户端向服务端发起 Tenant 验证
|
||||
- [ ] **验证成功**:服务端返回租户名称及品牌信息(公司名称、Logo 等);客户端将 Tenant Code 持久化到本地(必须加密存储),自动跳转至该租户的登录页;登录页顶部展示「正在登录:XX 房产」
|
||||
- [ ] **验证失败**(识别码无效):输入框下方显示红色错误提示「识别码无效,请联系您的 Tenant Admin(租户管理员)获取正确的识别码」;Tenant Code 不写入本地缓存;用户可重新输入
|
||||
- [ ] **网络异常**:显示「网络连接失败,请检查网络后重试」,提供「重试」按钮
|
||||
- [ ] 非首次启动(本地已有合法 Tenant Code 缓存):直接跳过识别界面,进入登录界面
|
||||
- [ ] 登录界面提供「切换公司」入口(链接文字,非主要 CTA),点击后清除本地 Tenant Code 缓存并重新显示 Tenant 识别界面;确认前弹出二次确认「切换公司将退出当前账号,是否继续?」
|
||||
- [ ] Tenant 验证接口属于公开接口,无需鉴权;但需对单 IP 请求频率限制(每分钟 ≤ 10 次)以防止枚举攻击
|
||||
- [ ] 非首次启动(本地已有合法 Tenant Code 缓存):直接跳过识别页,进入登录页
|
||||
- [ ] 登录页提供「切换公司」入口(链接文字,非主要 CTA),点击后弹出二次确认「切换公司将退出当前账号,是否继续?」,确认后清除本地 Tenant Code 缓存并重新显示 Tenant 识别页
|
||||
- [ ] Tenant 验证为公开操作,无需登录态,但平台需对该接口做防枚举的频控保护(具体阈值见 Tech 文档)
|
||||
|
||||
**Tenant Code 业务规则**:
|
||||
|
||||
- 格式:12 位纯数字
|
||||
- 生成:由平台运营在平台管理后台开通租户时自动生成,全局唯一,不允许手动指定
|
||||
- 唯一性:全局唯一,同一 Tenant Code 不得分配给多个租户
|
||||
- 客户端存储:必须加密落盘,禁止明文(实现细节见 Tech 文档 §十三 Electron 客户端约定)
|
||||
|
||||
---
|
||||
|
||||
@@ -102,35 +164,38 @@ Fonrey 是一套面向房产经纪公司的 B2B SaaS 平台,采用多租户架
|
||||
|
||||
**As** 已识别租户的经纪人,**I want** 通过手机号和密码完成登录,**So that** 进入系统开始工作。
|
||||
|
||||
> **说明**:普通员工的登录账号即为其手机号(由Tenant Admin(租户管理员)在新增员工时自动创建),无需记忆额外用户名。Tenant Admin 账号的登录名为平台运营自定义字符串,不受此约束。
|
||||
> **说明**:普通员工的登录账号即为其手机号(由 Tenant Admin 在新增员工时自动创建),无需记忆额外用户名。Tenant Admin 账号的登录名为该租户联系人手机号,规则一致。
|
||||
|
||||
**验收标准**:
|
||||
|
||||
- [ ] 登录界面展示:租户品牌标识(公司 Logo + 公司名称)、手机号输入框、密码输入框、滑块拼图验证区域、「登录」按钮
|
||||
- [ ] 登录页展示:租户品牌标识(公司 Logo + 公司名称)、登录方式 Tab(「密码登录」/「验证码登录」,默认选中「密码登录」)、手机号输入框、密码输入框、滑块拼图验证区域、「登录」按钮、「忘记密码」链接、「切换公司」入口、微信扫码禁用入口
|
||||
- [ ] 手机号输入框 Placeholder:「请输入您的手机号」;仅接受数字字符(非数字自动过滤),固定 11 位
|
||||
- [ ] 密码输入框默认密文显示,右侧提供「显示/隐藏」图标切换明密文
|
||||
- [ ] **行为验证码(滑块拼图)**:展示一张带缺口的背景图和一块可拖动的拼图碎片,用户通过拖动滑块将碎片移动至缺口位置完成验证;无需输入任何字符,操作直观快速
|
||||
- [ ] 验证逻辑:前端记录滑动轨迹(坐标序列 + 耗时),与背景图缺口位置一同发送至服务端;服务端综合校验**位置偏差**(允许 ±5px 容差)和**轨迹特征**(是否存在人类滑动的加速/减速规律)以区分机器行为
|
||||
- [ ] 验证失败(位置不准或轨迹异常):拼图区域抖动动画提示失败,自动刷新新的背景图,用户重新拖动;**不计入账号密码错误次数**
|
||||
- [ ] **行为验证码(滑块拼图)**:展示一张带缺口的背景图和一块可拖动的拼图碎片,用户通过拖动滑块将碎片移动至缺口位置完成验证;无需输入任何字符
|
||||
- [ ] 验证失败(位置不准或轨迹异常):拼图区域抖动提示,自动刷新新背景图,用户重新拖动;**不计入账号密码错误次数**
|
||||
- [ ] 验证成功后,拼图区域显示绿色对勾 + 「验证通过」文案,状态持续至本次登录提交完成
|
||||
- [ ] 提供「刷新」图标按钮,允许用户主动刷新背景图(针对图片模糊或缺口不清晰的情况)
|
||||
- [ ] 背景图从预置图库中随机抽取,缺口位置每次随机生成,防止固定模式被预测
|
||||
- [ ] 三项(手机号、密码、验证码)均有填写后,「登录」按钮才可点击(否则置灰)
|
||||
- [ ] 提供「刷新」图标按钮,允许用户主动刷新背景图
|
||||
- [ ] 三项(手机号、密码、滑块)均完成后,「登录」按钮才可点击(否则置灰)
|
||||
- [ ] 点击「登录」触发前端格式校验:
|
||||
- 手机号为空 → 输入框下方红色提示「请输入手机号」
|
||||
- 手机号为空 → 提示「请输入手机号」
|
||||
- 手机号不满 11 位 → 提示「请输入完整的 11 位手机号」
|
||||
- 密码为空 → 提示「请输入密码」
|
||||
- 验证码为空 → 提示「请完成滑块验证」
|
||||
- [ ] 格式校验通过后,向服务端发起登录请求,按钮进入 loading 状态防止重复提交
|
||||
- [ ] **登录成功(常规)**:服务端返回 Session Token 及 `is_initial_password` 标记;客户端存储 Token;
|
||||
- 若 `is_initial_password = False`:直接跳转系统首页,顶部显示欢迎信息「欢迎回来,{姓名}」
|
||||
- 若 `is_initial_password = True`:**立即跳转「修改初始密码」强制页面**,不可关闭、不可跳过、不可访问任何其他功能页面(详见 §5.3.4)
|
||||
- [ ] **登录失败(手机号或密码错误)**:显示「手机号或密码错误,请重新输入」(不区分具体原因,防止枚举攻击);验证码自动刷新;密码输入框清空;手机号保留
|
||||
- [ ] **登录失败(验证码错误)**:显示「验证码有误,请重新输入」;验证码自动刷新;验证码输入框清空
|
||||
- [ ] **账号被锁定**(同一账号密码连续错误 ≥ 5 次):显示「账号已被临时锁定,请 30 分钟后重试,或联系管理员解锁」;锁定状态下「登录」按钮置灰
|
||||
- 滑块未完成 → 提示「请完成滑块验证」
|
||||
- [ ] 格式校验通过后提交登录,按钮进入 loading 状态防止重复提交
|
||||
- [ ] **登录成功**:
|
||||
- 若账号处于"非初始密码"状态:直接进入系统首页,顶部显示欢迎信息「欢迎回来,{姓名}」
|
||||
- 若账号处于"初始密码"状态:**立即跳转至「首次登录强制改密页」**,不可关闭、不可跳过、不可访问其他功能页面(详见 §6.4)
|
||||
- [ ] **登录失败(手机号或密码错误)**:显示「手机号或密码错误,请重新输入」(不区分具体原因,防止枚举攻击);滑块自动刷新;密码输入框清空;手机号保留
|
||||
- [ ] **登录失败(滑块异常)**:显示「验证码有误,请重新输入」;滑块自动刷新
|
||||
- [ ] **账号被锁定**(同一账号密码连续错误达到上限):显示「账号已被临时锁定,请稍后再试,或联系管理员解锁」;锁定状态下「登录」按钮置灰
|
||||
- [ ] **账号已停用**:显示「账号已停用,请联系您的管理员」
|
||||
- [ ] **Session 过期**:用户在系统内操作时 Session 过期,自动跳转至登录界面,并提示「登录已过期,请重新登录」
|
||||
- [ ] 登录界面底部提供:「忘记密码」链接(详见 Story 3);移除「忘记用户名」入口(普通员工用户名即手机号,无需找回;Tenant Admin 如忘记用户名请联系平台运营)
|
||||
- [ ] **Session 过期**:用户在系统内操作时 Session 过期,自动跳转至登录页,并提示「登录已过期,请重新登录」
|
||||
|
||||
**业务规则**(具体数值口径见 Tech 文档):
|
||||
|
||||
- 同一账号密码连续错误达到上限后,账号进入临时锁定状态,到期自动解锁;管理员可手动解锁
|
||||
- 滑块验证失败不计入密码错误次数
|
||||
- 所有登录请求强制 HTTPS
|
||||
|
||||
---
|
||||
|
||||
@@ -138,52 +203,50 @@ Fonrey 是一套面向房产经纪公司的 B2B SaaS 平台,采用多租户架
|
||||
|
||||
**As** 忘记密码的经纪人,**I want** 通过手机号 + 短信验证码完成身份核验,重新设定密码,**So that** 无需邮箱、无需联系管理员,独立完成密码重置。
|
||||
|
||||
> **说明**:考虑到大多数Agent(经纪人)没有常用邮箱,本期找回密码统一通过短信验证码实现,废弃邮箱找回方式。账号中 `email` 字段在本系统无任何必须业务用途,完全可选。
|
||||
> **说明**:考虑到大多数经纪人没有常用邮箱,本期找回密码统一通过短信验证码实现,废弃邮箱找回方式。账号中邮箱字段在本系统无任何必须业务用途,完全可选。
|
||||
|
||||
**验收标准**:
|
||||
|
||||
- [ ] 点击登录界面「忘记密码」链接,跳转至「找回密码」流程(Stepper 分步页面,共三步)
|
||||
- [ ] 点击登录页「忘记密码」链接,跳转至「找回密码」流程(分三步)
|
||||
|
||||
**步骤一:输入手机号**
|
||||
|
||||
- [ ] 页面显示:手机号输入框(11 位数字,自动过滤非数字)、「获取验证码」按钮、「返回登录」链接
|
||||
- [ ] 手机号为空或不足 11 位 → 点击「获取验证码」时在输入框下方提示「请输入完整的 11 位手机号」
|
||||
- [ ] 手机号为空或不足 11 位 → 点击「获取验证码」时输入框下方提示「请输入完整的 11 位手机号」
|
||||
- [ ] 手机号格式合法后,点击「获取验证码」,按钮进入 60 秒倒计时冷却态(「重新获取(59s)」),倒计时结束后按钮恢复可点击
|
||||
- [ ] 服务端收到请求后:
|
||||
- 若该手机号**存在**且账号状态为 `active`:向该号码发送 6 位数字短信验证码,有效期 **10 分钟**
|
||||
- 若手机号**不存在**或账号已停用:页面统一提示「如该手机号已注册,验证码将在 1 分钟内发送」(**不泄露账号是否存在**)
|
||||
- [ ] 同一手机号 1 小时内最多发送 **5 次**短信验证码,超限后提示「发送次数过多,请 1 小时后再试」
|
||||
- [ ] 不论该手机号是否注册,前端文案统一显示「如该手机号已注册,验证码将在 1 分钟内发送」(**不泄露账号是否存在**)
|
||||
- [ ] 后台仅在手机号存在且账号处于可用状态时实际发送短信
|
||||
- [ ] 同一手机号在一段时间窗口内发送次数有上限,超限后提示「发送次数过多,请稍后再试」
|
||||
- [ ] 短信内容模板:「【Fonrey 房睿】您的密码重置验证码为 {code},10 分钟内有效,请勿泄露。」
|
||||
|
||||
**步骤二:输入短信验证码**
|
||||
|
||||
- [ ] 页面显示:6 位验证码输入框(支持分格输入)、「重新发送」倒计时链接、「下一步」按钮
|
||||
- [ ] 「下一步」按钮:6 位验证码全部输入后方可点击
|
||||
- [ ] 服务端校验验证码:
|
||||
- 正确且未过期 → 进入步骤三,颁发一次性 `sms_reset_token`(有效期 15 分钟,一次性,服务端存储)
|
||||
- 错误 → 提示「验证码有误,请重新输入」,错误次数 ≥ 5 次则本次验证码作废,需重新获取
|
||||
- [ ] 校验结果:
|
||||
- 正确且未过期 → 进入步骤三
|
||||
- 错误 → 提示「验证码有误,请重新输入」;同一验证码连续错误达到上限后作废,需重新获取
|
||||
- 已过期 → 提示「验证码已过期,请重新获取」
|
||||
|
||||
**步骤三:重置密码**
|
||||
|
||||
- [ ] 步骤三依赖步骤二颁发的 `sms_reset_token`(通过 URL 参数或会话状态传递),Token 无效或过期 → 显示「操作已超时,请重新发起找回密码」,跳回步骤一
|
||||
- [ ] **本页面复用「设置新密码」公共组件**(与首次登录强制修改密码页面为同一组件,详见 §5.3.4),保持 UI 与交互逻辑完全一致;入口上下文不同时,仅页面标题和提示文案有所差异:
|
||||
- [ ] 步骤三依赖步骤二颁发的一次性凭证;凭证无效或过期 → 显示「操作已超时,请重新发起找回密码」,跳回步骤一
|
||||
- [ ] **本页面复用「设置新密码」公共组件**(与首次登录强制改密页为同一组件,详见 §6.4),保持 UI 与交互逻辑完全一致;入口上下文不同时,仅页面标题与提示文案差异:
|
||||
|
||||
| 元素 | 首次登录强制修改(§5.3.4) | 找回密码步骤三(本 Story) |
|
||||
| 元素 | 首次登录强制改密(§6.4) | 找回密码步骤三(本 Story) |
|
||||
|------|--------------------------|--------------------------|
|
||||
| 页面标题 | 「欢迎使用 Fonrey,请先设置您的登录密码」 | 「重置您的登录密码」 |
|
||||
| 提示文案 | 「您当前使用的是初始密码,为保障账号安全,请立即设置新密码后开始使用」 | 「请输入您的新密码,设置完成后请使用新密码重新登录」 |
|
||||
| 提交按钮文案 | 「确认并进入系统」 | 「确认重置密码」 |
|
||||
| 提交后跳转 | `is_initial_password = False`,Session 保持,直接进入首页 | 所有 Session 立即失效,跳转登录界面并提示「密码已重置,请使用新密码登录」 |
|
||||
| 提交后跳转 | Session 保持,直接进入首页 | 该账号所有 Session 立即失效,跳转登录页并提示「密码已重置,请使用新密码登录」 |
|
||||
|
||||
- [ ] 提交成功后:`is_initial_password` 置为 **`False`**(找回密码属于用户主动操作,已完成身份核验,无需再触发强制修改流程)
|
||||
> **注意**:与首次登录流程不同,找回密码时用户已通过短信验证码完成了身份核验,本次密码设置即视为"用户本人主动设置",不应再触发 `is_initial_password = True` 的二次强制修改。
|
||||
- [ ] 提交成功后:账号"初始密码"标记置为否(找回密码属于用户主动操作,已完成身份核验,**不应再触发首次登录强制改密流程**)
|
||||
|
||||
---
|
||||
|
||||
### Story 4:经纪人找回用户名(已废弃)
|
||||
|
||||
> **状态**:已废弃。普通员工用户名固定为手机号,无需找回;Tenant Admin 如忘记用户名,请联系平台运营线下处理。本 Story 保留占位以维持版本记录,实现时跳过此 Story。
|
||||
> **状态**:已废弃。普通员工用户名固定为手机号,无需找回;Tenant Admin 如忘记用户名请联系平台运营线下处理。本 Story 保留占位以维持版本记录,实现时跳过。
|
||||
|
||||
---
|
||||
|
||||
@@ -191,150 +254,103 @@ Fonrey 是一套面向房产经纪公司的 B2B SaaS 平台,采用多租户架
|
||||
|
||||
**As** 已有账号的经纪人,**I want** 通过手机号 + 短信验证码直接登录,**So that** 在忘记密码或不想输入密码时,仍能快速进入系统。
|
||||
|
||||
> **说明**:短信基础设施(`sms_otp_records` 表、OTP 发送/校验逻辑)已在 Story 3 找回密码中建设完成,本 Story 直接复用,实现成本极低。登录界面提供「密码登录」和「验证码登录」两个并列入口,用户自由切换,两种方式均为 MVP 正式功能。
|
||||
> **说明**:短信能力在 Story 3 找回密码中已建设完成,本 Story 复用。登录页提供「密码登录」和「验证码登录」两个并列 Tab,用户自由切换,两种方式均为 MVP 正式功能。
|
||||
|
||||
**验收标准**:
|
||||
|
||||
- [ ] 登录界面提供两种登录方式的切换 Tab:**「密码登录」**(默认选中)和 **「验证码登录」**
|
||||
- [ ] 登录页提供两种登录方式的切换 Tab:**「密码登录」**(默认选中)和 **「验证码登录」**
|
||||
- [ ] 切换 Tab 时,输入区域平滑切换,已填内容清空,滑块验证状态重置
|
||||
|
||||
**「验证码登录」界面元素**:
|
||||
- [ ] 手机号输入框(规格同 Story 2,11 位数字,自动过滤非数字)
|
||||
|
||||
- [ ] 手机号输入框(规格同 Story 2,11 位数字)
|
||||
- [ ] 验证码输入框(6 位数字分格输入)+ 「获取验证码」按钮(60 秒倒计时冷却态)
|
||||
- [ ] 滑块拼图验证区域(规格同 Story 2,**先通过滑块验证,再允许点击「获取验证码」**)
|
||||
- [ ] 「登录」按钮(手机号 + 验证码均填写后方可点击)
|
||||
|
||||
**获取验证码逻辑**:
|
||||
|
||||
- [ ] 用户须先完成滑块验证,「获取验证码」按钮方可点击;未完成滑块时点击 → 提示「请先完成滑块验证」
|
||||
- [ ] 点击「获取验证码」后,服务端:
|
||||
- 手机号格式不合法 → 前端拦截,提示「请输入完整的 11 位手机号」
|
||||
- 手机号存在且状态 `active` → 发送 6 位 OTP,有效期 **5 分钟**,存入 `sms_otp_records`(`scene = 'login'`)
|
||||
- 手机号不存在或已停用 → 统一响应「如该手机号已注册,验证码将在 1 分钟内发送」(防止枚举攻击)
|
||||
- [ ] 同一手机号 1 小时内最多发送 **10 次**登录验证码(找回密码为独立计数,两者不共享限额);超限后提示「发送次数过多,请 1 小时后再试」
|
||||
- [ ] 手机号格式不合法 → 前端拦截,提示「请输入完整的 11 位手机号」
|
||||
- [ ] 不论手机号是否注册,前端文案统一显示「如该手机号已注册,验证码将在 1 分钟内发送」(防止枚举攻击)
|
||||
- [ ] 后台仅在手机号存在且账号处于可用状态时实际发送短信
|
||||
- [ ] 同一手机号在一段时间窗口内的发送次数有上限(与找回密码独立计数,互不共享);超限后提示「发送次数过多,请稍后再试」
|
||||
- [ ] 短信内容模板:「【Fonrey 房睿】您的登录验证码为 {code},5 分钟内有效,请勿泄露。」
|
||||
|
||||
**登录校验逻辑**:
|
||||
- [ ] 点击「登录」,服务端校验 OTP:
|
||||
- 正确且未过期 → 登录成功,后续行为与 Story 2 密码登录完全一致(含 `is_initial_password` 判断)
|
||||
- 错误 → 提示「验证码有误,请重新输入」;连续错误 ≥ 5 次 → 本次 OTP 作废,提示「验证码已失效,请重新获取」
|
||||
|
||||
- [ ] 校验结果:
|
||||
- 正确且未过期 → 登录成功,后续行为与 Story 2 密码登录完全一致(含初始密码状态判断)
|
||||
- 错误 → 提示「验证码有误,请重新输入」;连续错误达到上限 → 本次验证码作废,提示「验证码已失效,请重新获取」
|
||||
- 已过期 → 提示「验证码已过期,请重新获取」
|
||||
- [ ] **账号被锁定**(密码登录失败次数触发):验证码登录仍受账号锁定限制,锁定期间无法通过任何方式登录,提示「账号已被临时锁定,请 30 分钟后重试,或联系管理员解锁」
|
||||
- [ ] **账号被锁定**(密码登录失败次数触发):验证码登录仍受账号锁定限制,锁定期间无法通过任何方式登录,提示与 Story 2 一致
|
||||
> **设计说明**:账号锁定是账号维度的安全策略,不区分登录方式;否则锁定形同虚设。
|
||||
- [ ] **账号已停用**:提示「账号已停用,请联系您的管理员」
|
||||
|
||||
**接口规范**:
|
||||
---
|
||||
|
||||
```
|
||||
POST /api/auth/login/phone/
|
||||
Request: { phone: string, sms_code: string }
|
||||
Response: { token: string, is_initial_password: bool, user: {...} } | { error_code: string, message: string }
|
||||
```
|
||||
### Story 6:预留——微信扫码登录(v2 实现)
|
||||
|
||||
**As** 绑定了微信账号的经纪人,**I want** 在登录页扫描微信二维码完成登录,**So that** 免去输入账号密码的步骤,提升登录体验。
|
||||
|
||||
**当前状态**:
|
||||
|
||||
- 本期登录页 UI 入口以「即将开放」禁用态展示,**不提供任何可点击的实际功能**
|
||||
- 后端服务端在 MVP 中**不开放微信相关接口**,相关端点仅占位规划,详见 Tech 文档 §5.4
|
||||
- v2 实现时的业务前置要求:
|
||||
- 微信账号必须先在「个人设置」中与系统账号完成绑定
|
||||
- 二维码有效期 3 分钟,过期后前端自动刷新二维码
|
||||
- 同一微信账号在同一租户内只能绑定一个系统账号
|
||||
|
||||
---
|
||||
|
||||
### Story 6:预留——微信扫码登录(接口预留,v2 实现)
|
||||
## 6. 业务规则与功能详细说明
|
||||
|
||||
**As** 绑定了微信账号的经纪人,**I want** 在登录界面扫描微信二维码完成登录,**So that** 免去输入账号密码的步骤,提升登录体验。
|
||||
|
||||
**当前状态**:本期 UI 入口以「即将开放」禁用态展示于登录界面,接口定义预留,不开放实际功能。
|
||||
|
||||
**预留接口设计**(供后端提前规划):
|
||||
|
||||
```
|
||||
GET /api/auth/wechat/qrcode/ # 获取微信扫码二维码(含 state + 有效期)
|
||||
POST /api/auth/wechat/callback/ # 微信扫码确认后回调,换取系统 Token
|
||||
```
|
||||
|
||||
**绑定条件**(v2 实现时的前置要求):
|
||||
- 微信账号必须先在「个人设置」中与用户名账号完成绑定
|
||||
- 二维码有效期 3 分钟,过期后前端自动刷新二维码
|
||||
- 微信账号只能绑定一个用户名账号(同一租户内)
|
||||
|
||||
---
|
||||
|
||||
## 5. 功能详细说明
|
||||
|
||||
### 5.1 客户端 Tenant 识别流程
|
||||
|
||||
#### 5.1.1 流程概述
|
||||
### 6.1 客户端 Tenant 识别业务流程
|
||||
|
||||
```
|
||||
客户端启动
|
||||
│
|
||||
├─ 本地有 Tenant Code 缓存?
|
||||
│ │
|
||||
│ YES ──→ 校验缓存 Tenant Code 是否仍有效(服务端 validate)
|
||||
│ YES ──→ 复核缓存 Tenant Code 是否仍有效
|
||||
│ │
|
||||
│ 有效 ──→ 直接进入登录界面
|
||||
│ 有效 ──→ 直接进入登录页
|
||||
│ │
|
||||
│ 无效 ──→ 清除缓存,进入 Tenant 识别界面
|
||||
│ 无效 ──→ 清除缓存,进入 Tenant 识别页
|
||||
│
|
||||
└─ NO ──→ 显示 Tenant 识别界面
|
||||
└─ NO ──→ 显示 Tenant 识别页
|
||||
│
|
||||
用户输入 Tenant Code → 发起验证
|
||||
│
|
||||
验证成功 ──→ 缓存 Tenant Code → 进入登录界面
|
||||
验证成功 ──→ 缓存 Tenant Code → 进入登录页
|
||||
│
|
||||
验证失败 ──→ 显示错误信息,保持识别界面
|
||||
验证失败 ──→ 显示错误信息,保持识别页
|
||||
```
|
||||
|
||||
#### 5.1.2 Tenant 识别界面规范
|
||||
#### Tenant 识别页规范
|
||||
|
||||
| 元素 | 规格 |
|
||||
| ------------- | --------------------------------------- |
|
||||
| 页面背景 | 品牌色渐变(与登录界面保持一致的视觉风格) |
|
||||
| Logo | Fonrey 产品 Logo,居中显示 |
|
||||
| 标题 | 「欢迎使用 Fonrey 房睿」 |
|
||||
| 副标题 | 「请输入您公司的专属识别码以继续」 |
|
||||
| 元素 | 规格 |
|
||||
| ----------- | --------------------------------------- |
|
||||
| 页面背景 | 品牌色渐变(与登录页保持一致的视觉风格) |
|
||||
| Logo | Fonrey 产品 Logo,居中显示 |
|
||||
| 标题 | 「欢迎使用 Fonrey 房睿」 |
|
||||
| 副标题 | 「请输入您公司的专属识别码以继续」 |
|
||||
| Tenant Code 输入框 | 单行数字输入,固定 12 位,支持粘贴;非数字字符自动过滤,超出 12 位截断 |
|
||||
| 输入框 Label | 「公司识别码(Tenant Code)」 |
|
||||
| 确认按钮 | 主色调按钮,文字「确认」 |
|
||||
| 错误提示 | 输入框下方红色文字,固定区域占位(不影响布局抖动) |
|
||||
| 帮助文案 | 「不知道识别码?请联系您公司的Tenant Admin(租户管理员)」 |
|
||||
| 输入框 Label | 「公司识别码(Tenant Code)」 |
|
||||
| 确认按钮 | 主色调按钮,文字「确认」 |
|
||||
| 错误提示 | 输入框下方红色文字,固定区域占位(不影响布局抖动) |
|
||||
| 帮助文案 | 「不知道识别码?请联系您公司的Tenant Admin(租户管理员)」 |
|
||||
|
||||
#### 5.1.3 Tenant Code 格式规范
|
||||
|
||||
- **格式**:固定 **12 位纯数字**,如 `202500010001`
|
||||
- **生成规则**(建议):由平台运营在系统管理后台开通租户时自动生成,不允许手动指定,确保全局唯一性;可采用时间戳前缀 + 随机后缀的方式生成(如 `YYYYMM` + 6 位随机数)
|
||||
- **前端校验**:输入框仅接受数字字符(非数字自动过滤),输入满 12 位后自动触发格式完成状态;少于 12 位时点击「确认」弹出提示「识别码须为 12 位数字」
|
||||
- **唯一性**:全局唯一(公共 Schema 层面),同一 Tenant Code 不可分配给多个租户
|
||||
- **客户端存储**:Electron `app.getPath('userData')` 目录下的配置文件(加密存储,防止明文读取)
|
||||
|
||||
#### 5.1.4 服务端 Tenant 验证接口规范
|
||||
|
||||
```
|
||||
POST /api/auth/tenant/verify/
|
||||
|
||||
Request Body:
|
||||
{
|
||||
"tenant_code": "202500010001"
|
||||
}
|
||||
|
||||
Response 200 (成功):
|
||||
{
|
||||
"valid": true,
|
||||
"tenant_name": "XX房产经纪有限公司",
|
||||
"tenant_logo_url": "https://cdn.fonrey.com/tenants/xxx/logo.png",
|
||||
"login_url": "https://xxx.fonrey.com/auth/login/"
|
||||
}
|
||||
|
||||
Response 200 (失败):
|
||||
{
|
||||
"valid": false,
|
||||
"error_code": "TENANT_NOT_FOUND",
|
||||
"message": "识别码无效"
|
||||
}
|
||||
```
|
||||
|
||||
> **注意**:该接口属于 `shared_apps` 范围,路由在公共 Schema 下,不需要租户鉴权,但需要限流保护(每 IP 每分钟 ≤ 10 次请求)。
|
||||
> Tenant 验证接口的具体路径、请求/响应 Schema、限流阈值与 Schema 隔离实现:见 `TECH_STACK/登录管理技术方案.md` §5.3.1 与 §三。
|
||||
|
||||
---
|
||||
|
||||
### 5.2 登录界面设计规范
|
||||
### 6.2 登录页设计规范
|
||||
|
||||
#### 5.2.1 界面布局
|
||||
#### 6.2.1 页面布局
|
||||
|
||||
登录界面顶部以 **Tab 切换**区分两种登录方式(「密码登录」默认选中),Tab 下方的表单区随当前选中 Tab 动态切换,微信扫码作为独立的「其他登录」保持禁用。
|
||||
登录页顶部以 **Tab 切换** 区分两种登录方式(「密码登录」默认选中),Tab 下方的表单区随当前选中 Tab 动态切换;微信扫码作为独立的「其他登录」保持禁用。
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
@@ -382,80 +398,83 @@ Response 200 (失败):
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### 5.2.2 安全机制
|
||||
#### 6.2.2 安全机制(业务视角)
|
||||
|
||||
| 机制 | 规格 |
|
||||
| 机制 | 业务规则 |
|
||||
|------|------|
|
||||
| 验证码类型 | **滑块拼图行为验证码**:展示带缺口的背景图 + 可拖动的拼图碎片,用户滑动碎片至缺口完成验证,无需输入字符 |
|
||||
| 验证逻辑 | 服务端综合校验**位置偏差**(缺口中心 ±5px 容差)+ **滑动轨迹特征**(加速/减速曲线、总耗时),双重判断是否为人类行为 |
|
||||
| 背景图来源 | 预置图库随机抽取,缺口位置每次服务端随机生成,防止固定模式被预测 |
|
||||
| 验证码有效期 | 单次验证会话有效,提交登录后服务端 Token 立即失效;超过 3 分钟未操作需重新加载 |
|
||||
| 验证失败处理 | 拼图区域抖动动画提示,自动刷新新背景图;**不计入账号密码错误次数**(行为验证失败属独立事件) |
|
||||
| 密码错误锁定 | 同一账号连续密码错误 ≥ 5 次,锁定 30 分钟;解锁方式:等待超时自动解锁 或 管理员手动解锁 |
|
||||
| 密码错误计数 | 计数存于 Redis,Key 格式:`login_fail:tenant_id:phone`(phone 即用户名/手机号),TTL 30 分钟 |
|
||||
| 验证码刷新 | 登录失败(用户名/密码错误)后自动刷新拼图;用户亦可主动点击「刷新」图标重新加载背景图 |
|
||||
| HTTPS | 所有登录相关请求强制 HTTPS,不允许 HTTP 降级 |
|
||||
| 密码传输 | 前端不做密码加密,HTTPS 层保证传输安全;后端存储使用 `django.contrib.auth` 默认的 `PBKDF2+SHA256` 哈希 |
|
||||
| Session 有效期 | 默认 8 小时(工作日单日使用场景);可由租户管理员在「系统设置」中调整 |
|
||||
| 验证码类型 | 滑块拼图行为验证码:用户拖动碎片至缺口完成验证,无需输入字符 |
|
||||
| 验证判定 | 综合位置精度与滑动轨迹特征判断是否为人类行为;具体容差与算法见 Tech 文档 |
|
||||
| 背景图来源 | 预置图库随机抽取,缺口位置每次随机生成,防止固定模式被预测 |
|
||||
| 验证失败处理 | 拼图区域抖动提示,自动刷新新背景图;**不计入账号密码错误次数** |
|
||||
| 密码错误锁定 | 同一账号连续密码错误达到阈值后自动锁定,到期自动解锁;管理员可手动解锁 |
|
||||
| 验证码刷新 | 登录失败后自动刷新拼图;用户亦可主动点击「刷新」图标 |
|
||||
| 传输安全 | 所有登录相关请求强制 HTTPS,不允许 HTTP 降级 |
|
||||
| 会话有效期 | 默认按工作日单日使用场景设定;可由 Tenant Admin 在「系统设置」中调整 |
|
||||
|
||||
> 滑块容差像素值、密码错误次数阈值、锁定时长、Session 默认时长、密码哈希算法、登录失败计数存储位置等实现口径,统一由 `TECH_STACK/登录管理技术方案.md` §六、§七、§八 定义;本 PRD 不重复。
|
||||
|
||||
---
|
||||
|
||||
### 5.3 账号与员工实名绑定规范
|
||||
### 6.3 账号与员工实名绑定规范
|
||||
|
||||
#### 5.3.1 绑定原则
|
||||
#### 6.3.1 绑定原则
|
||||
|
||||
- 每个系统登录账号必须与「组织人事管理」模块中的一条**员工档案(Staff)**绑定
|
||||
- 账号与员工是 **1:1 关系**,一个员工对应一个账号,一个账号只能绑定一个员工
|
||||
- **不支持用户自行注册**,所有账号均由有权限的管理角色创建
|
||||
|
||||
#### 5.3.2 账号创建权限分层
|
||||
#### 6.3.2 账号创建权限分层
|
||||
|
||||
系统内共有两类账号创建场景,权限和规则各不相同:
|
||||
|
||||
**② Tenant Admin 账号(每个租户的超级管理账号)**
|
||||
**① Tenant Admin 账号(每个租户的超级管理账号)**
|
||||
|
||||
| 项目 | 规格 |
|
||||
| 项目 | 业务规则 |
|
||||
| ---- | ------------------------------------------------------------ |
|
||||
| 创建时机 | 平台运营在系统管理后台开通租户时,系统**自动**以该租户联系人手机号创建 Tenant Admin 账号,无需手动设置 |
|
||||
| 用户名 | **固定为该租户联系人的手机号**(11 位数字),全局唯一,创建后不可更改 |
|
||||
| 初始密码 | **系统统一固定初始密码**(与普通员工相同,由平台在部署配置中设定,如 `Fonrey@2025`) |
|
||||
| 首次登录 | 强制修改初始密码,不可跳过 |
|
||||
| 权限范围 | 拥有该租户内最高权限,可管理员工账号、角色、系统设置等 |
|
||||
| 数量限制 | 每个租户仅限 1 个 Tenant Admin 账号(后续可扩展为多管理员,v2 规划) |
|
||||
| 数据来源 | 联系人手机号来自 `public.tenants.contact_phone` 字段,开通租户时由平台运营录入,必填 |
|
||||
| 创建时机 | 平台运营在平台管理后台开通租户时,系统**自动**以该租户联系人手机号创建 Tenant Admin 账号,无需手动设置 |
|
||||
| 用户名 | **固定为该租户联系人的手机号**(11 位数字),全局唯一,创建后不可更改 |
|
||||
| 初始密码 | 系统统一固定初始密码(由平台运营在系统配置中设定),与普通员工相同 |
|
||||
| 首次登录 | 强制修改初始密码,不可跳过 |
|
||||
| 权限范围 | 拥有该租户内最高权限,可管理员工账号、角色、系统设置等 |
|
||||
| 数量限制 | 每个租户仅限 1 个 Tenant Admin 账号(v2 可扩展为多管理员) |
|
||||
| 数据来源 | 联系人手机号在开通租户时由平台运营录入,必填 |
|
||||
|
||||
**① 普通员工账号(经纪人、店长、行政等)**
|
||||
**② 普通员工账号(经纪人、店长、行政等)**
|
||||
|
||||
| 项目 | 规格 |
|
||||
| 项目 | 业务规则 |
|
||||
|------|------|
|
||||
| 创建时机 | Tenant Admin 在「组织人事管理 → 新增员工」时,系统自动为该员工创建登录账号 |
|
||||
| 用户名 | **固定为该员工的手机号**(11 位数字),同租户内唯一,创建后不可更改 |
|
||||
| 初始密码 | **系统统一固定初始密码**(由平台在部署配置中设定,如 `Fonrey@2025`),所有新员工账号均使用同一初始密码 |
|
||||
| 首次登录 | 强制修改初始密码,**不可跳过**(详见 5.3.4) |
|
||||
| 密码重置 | Tenant Admin 可在员工管理界面对任意员工账号执行「重置密码」,重置后恢复为固定初始密码,触发首次登录强制修改流程 |
|
||||
| 初始密码 | 系统统一固定初始密码(与 Tenant Admin 共用同一配置) |
|
||||
| 首次登录 | 强制修改初始密码,**不可跳过**(详见 §6.4) |
|
||||
| 密码重置 | Tenant Admin 可在员工管理界面对任意员工账号执行「重置密码」,重置后恢复为固定初始密码,触发首次登录强制改密流程 |
|
||||
| 账号禁用 | 员工离职或被停用时,对应账号自动禁用;禁用账号无法登录,历史操作记录保留 |
|
||||
|
||||
#### 5.3.3 账号字段规范
|
||||
#### 6.3.3 账号字段语义(业务视角)
|
||||
|
||||
| 字段 | 类型 | Tenant Admin | 普通员工账号 | 说明 |
|
||||
|------|------|-------------|-------------|------|
|
||||
| 用户名(username) | CharField(30) | **固定为联系人手机号**(11 位数字) | **固定为员工手机号**(11 位数字) | 登录 ID,创建后不可更改;两类账号规则统一 |
|
||||
| 密码(password) | CharField | **系统统一固定初始密码** | **系统统一固定初始密码** | PBKDF2+SHA256 哈希存储;首次登录强制修改 |
|
||||
| 手机号(phone) | CharField(11) | **必填,同时作为用户名**,来源于 `public.tenants.contact_phone` | **必填,同时作为用户名**,加密存储,同租户内唯一 | 两类账号均用手机号登录,v2 启用手机验证码后复用此字段 |
|
||||
| 邮箱(email) | EmailField | 选填,同租户唯一 | 选填,同租户唯一 | 在本系统无必须业务用途,完全可选;普通员工忘记密码通过手机短信验证码自助找回,**与邮箱无关** |
|
||||
| 员工档案关联(staff_id) | OneToOneField → `org.Staff` | 可选关联(平台运营账号) | 必须关联 | 实名绑定 |
|
||||
| 账号状态(status) | CharField | `active` / `disabled` / `locked` | `active` / `disabled` / `locked` | locked 为密码错误锁定,30 分钟自动恢复 |
|
||||
| 初始密码标记(is_initial_password) | BooleanField | True(首次登录前) | True(首次登录前) | True 时登录成功后强制跳转修改密码页 |
|
||||
| 创建人(created_by) | ForeignKey → self | 平台运营(系统管理后台) | Tenant Admin | 审计追溯 |
|
||||
| 字段 | Tenant Admin | 普通员工 | 业务说明 |
|
||||
|------|------|------|------|
|
||||
| 用户名 | 联系人手机号 | 员工手机号 | 登录 ID,创建后不可更改 |
|
||||
| 密码 | 系统统一初始密码 | 系统统一初始密码 | 必须安全哈希存储;首次登录强制修改 |
|
||||
| 手机号 | 必填,同时作为用户名 | 必填,同时作为用户名 | 用于密码登录与短信验证码登录;存储须加密 |
|
||||
| 邮箱 | 选填 | 选填 | 在本系统无必须业务用途,完全可选 |
|
||||
| 员工档案关联 | 可选关联 | 必须关联 | 实名绑定 |
|
||||
| 账号状态 | active / disabled / locked | active / disabled / locked | locked 为密码错误锁定,到期自动恢复 |
|
||||
| 初始密码标记 | 是 / 否 | 是 / 否 | 为"是"时登录成功后强制跳转改密页 |
|
||||
| 创建人 | 平台运营 | Tenant Admin | 审计追溯 |
|
||||
|
||||
#### 5.3.4 首次登录强制修改密码
|
||||
> 字段类型、字段名、表名、索引、跨 App 依赖、Migration 顺序等实现口径,全部以 `DATA_MODEL/DATA_MODEL_LOGIN.md` 为唯一权威。
|
||||
|
||||
- 新员工账号创建后,`is_initial_password = True`,账号处于「初始密码」状态
|
||||
- 员工使用手机号(用户名)+ 固定初始密码登录成功后,系统**立即跳转**至「修改初始密码」强制页面,**不可关闭、不可跳过**,任何其他系统功能页面均不可访问
|
||||
- Tenant Admin 对员工账号执行「重置密码」后,`is_initial_password` 重置为 True,该员工下次登录时再次触发强制修改流程
|
||||
- 修改成功后,`is_initial_password` 更新为 False,原 Session 保持有效,直接进入系统首页
|
||||
---
|
||||
|
||||
**强制修改密码页面规范**:
|
||||
### 6.4 首次登录强制修改密码
|
||||
|
||||
- 新员工账号创建后,账号处于「初始密码」状态
|
||||
- 员工使用手机号 + 固定初始密码登录成功后,系统**立即跳转**至「首次登录强制改密页」,**不可关闭、不可跳过**,任何其他系统功能页面均不可访问
|
||||
- Tenant Admin 对员工账号执行「重置密码」后,账号重新进入「初始密码」状态,该员工下次登录时再次触发强制改密流程
|
||||
- 修改成功后,账号脱离「初始密码」状态,原 Session 保持有效,直接进入系统首页
|
||||
|
||||
**首次登录强制改密页规范**:
|
||||
|
||||
| 元素 | 规格 |
|
||||
|------|------|
|
||||
@@ -467,13 +486,13 @@ Response 200 (失败):
|
||||
| 提交按钮 | 「确认并进入系统」 |
|
||||
| 不可操作项 | 无「跳过」按钮;顶部导航栏、侧边菜单、关闭按钮均禁用 |
|
||||
|
||||
> 历史密码校验范围、密码强度算法等实现细节见 Tech 文档与 `DATA_MODEL_LOGIN.md` 中历史密码记录表设计。
|
||||
|
||||
---
|
||||
|
||||
### 5.4 找回密码详细说明
|
||||
### 6.5 找回密码业务流程
|
||||
|
||||
> **说明**:Story 4「找回用户名」已废弃。普通员工用户名固定为手机号,无需找回;Tenant Admin 如忘记用户名请联系平台运营线下处理。
|
||||
|
||||
#### 5.4.1 找回密码流程
|
||||
> 实现细节(一次性凭证 TTL、OTP 有效期、错误次数阈值、限流计数策略)见 `TECH_STACK/登录管理技术方案.md` §6.3 与 §七。
|
||||
|
||||
```
|
||||
用户点击「忘记密码」
|
||||
@@ -482,143 +501,127 @@ Response 200 (失败):
|
||||
│
|
||||
├─ 输入 11 位手机号,点击「获取验证码」
|
||||
│ │
|
||||
│ 服务端校验手机号是否存在且状态为 active
|
||||
│ 前端文案统一为「如该手机号已注册,验证码将在 1 分钟内发送」(防止枚举)
|
||||
│ │
|
||||
│ 统一响应「如该手机号已注册,验证码将在 1 分钟内发送」(防止枚举)
|
||||
│ │
|
||||
│ 后台:存在且 active → 生成 6 位 OTP,有效期 10 分钟,存入 sms_otp_records → 发送短信
|
||||
│ 后台:手机号存在且账号可用 → 生成 OTP → 发送短信
|
||||
│ 不存在或已停用 → 静默处理
|
||||
│
|
||||
步骤2:输入短信验证码
|
||||
│
|
||||
├─ 输入 6 位验证码,点击「下一步」
|
||||
│ │
|
||||
│ 服务端校验 OTP:
|
||||
│ │
|
||||
│ 正确且未过期 → 颁发一次性 sms_reset_token(有效期 15 分钟)→ 进入步骤3
|
||||
│ │
|
||||
│ 错误(累计 < 5 次)→ 提示「验证码有误,请重新输入」
|
||||
│ 错误(累计 ≥ 5 次)→ 提示「验证已失败,请重新获取验证码」,本次 OTP 作废
|
||||
│ 正确且未过期 → 颁发一次性凭证 → 进入步骤3
|
||||
│ 错误(次数未到上限)→ 提示「验证码有误,请重新输入」
|
||||
│ 错误(达到上限)→ 提示「验证已失败,请重新获取验证码」,本次 OTP 作废
|
||||
│ 已过期 → 提示「验证码已过期,请重新获取」
|
||||
│
|
||||
步骤3:重置密码
|
||||
│
|
||||
├─ 页面携带 sms_reset_token,服务端校验有效性
|
||||
├─ 服务端校验一次性凭证有效性
|
||||
│ │
|
||||
│ 无效/过期 → 提示「操作已超时,请重新发起找回密码」,跳回步骤1
|
||||
│ │
|
||||
│
|
||||
├─ 用户输入新密码 + 确认新密码,实时逐条校验复杂度规则(✓/✗)
|
||||
│
|
||||
└─ 提交成功
|
||||
→ 更新密码,is_initial_password = False
|
||||
→ 更新密码,账号脱离「初始密码」状态
|
||||
→ 清除该账号所有有效 Session(强制重新登录)
|
||||
→ 跳转登录界面,提示「密码已重置,请使用新密码登录」
|
||||
→ 跳转登录页,提示「密码已重置,请使用新密码登录」
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5.5 手机验证码登录详细说明
|
||||
### 6.6 手机验证码登录(业务规则补充)
|
||||
|
||||
> 本节为 Story 5 的实现规范补充。短信基础设施(`sms_otp_records` 表、OTP 发送/校验逻辑)在 Story 3 找回密码中已建设完成,本节描述在**登录场景**下复用该基础设施时的关键差异点。
|
||||
> 本节为 Story 5 的业务规则补充。短信能力在 Story 3 找回密码中已建设完成,本节描述在**登录场景**下复用时的关键业务差异点。
|
||||
|
||||
**与找回密码短信逻辑的差异对比**:
|
||||
**与找回密码短信逻辑的业务差异对比**:
|
||||
|
||||
| 维度 | 找回密码(Story 3) | 验证码登录(Story 5) |
|
||||
|------|-------------------|---------------------|
|
||||
| `scene` 字段 | `password_reset` | `login` |
|
||||
| OTP 有效期 | 10 分钟 | 5 分钟 |
|
||||
| 每小时发送上限 | 5 次 | 10 次 |
|
||||
| 验证成功后动作 | 颁发 `sms_reset_token` → 步骤三重置密码 | 直接颁发 Session Token,登录成功 |
|
||||
| 业务场景 | 密码重置 | 登录认证 |
|
||||
| OTP 有效期 | 较长 | 较短 |
|
||||
| 每小时发送上限 | 较低 | 较高 |
|
||||
| 验证成功后动作 | 颁发一次性凭证 → 步骤三重置密码 | 直接登录成功 |
|
||||
| 短信文案 | 「密码重置验证码」 | 「登录验证码」 |
|
||||
| 账号锁定影响 | 不受密码错误锁定限制(非密码登录路径) | **受账号锁定限制**(账号维度安全策略,不区分方式)|
|
||||
|
||||
> 各项数值(OTP 时长、每小时上限)口径见 `TECH_STACK/登录管理技术方案.md` §6.3、§6.4、§七。
|
||||
|
||||
**滑块验证前置规则**(验证码登录特有):
|
||||
|
||||
- 用户须先完成滑块拼图验证,「获取验证码」按钮方可点击
|
||||
- 滑块验证通过后,拼图区域保持「验证通过」状态,不需要在点击「登录」前再次验证
|
||||
- 切换 Tab 时,滑块验证状态重置(须重新完成验证后方可获取验证码)
|
||||
|
||||
**`sms_otp_records` 表复用说明**:
|
||||
**短信记录复用说明**:
|
||||
|
||||
- 不新建表,复用 `DATA_MODEL_LOGIN.md` 中定义的 `sms_otp_records`
|
||||
- `scene` 字段区分场景:`login` / `password_reset`,各自独立限流计数
|
||||
- 同一手机号同一 scene 同一时间只有一条有效 OTP;新发送时将旧记录标记为 `used`
|
||||
- 不新建表,复用 `DATA_MODEL_LOGIN.md` 中定义的短信验证码记录表
|
||||
- 通过场景字段区分"登录"与"密码重置",各自独立限流计数
|
||||
- 同一手机号同一场景同一时间只有一条有效 OTP;新发送时将旧记录置为失效
|
||||
|
||||
---
|
||||
|
||||
### 5.6 后端数据模型设计
|
||||
### 6.7 后端数据模型
|
||||
|
||||
> **数据模型已迁移至独立文档**,请参阅:
|
||||
> **`Project/fonrey/DATA_MODEL/DATA_MODEL_LOGIN.md`**
|
||||
> 数据模型已迁移至独立文档,请参阅:**`DATA_MODEL/DATA_MODEL_LOGIN.md`**
|
||||
|
||||
该文档包含:
|
||||
- `user_accounts` 账号主表(完整字段定义、约束、索引、Django Model 代码)
|
||||
- `login_attempts` 登录审计表
|
||||
- `sms_otp_records` 短信验证码记录表(找回密码 + 验证码登录共用)
|
||||
- `password_histories` 历史密码记录表
|
||||
|
||||
- 账号主表(完整字段定义、约束、索引)
|
||||
- 登录审计表
|
||||
- 短信验证码记录表(找回密码 + 验证码登录共用)
|
||||
- 历史密码记录表
|
||||
- Redis 缓存结构说明
|
||||
- 账号状态机与创建流程
|
||||
- 与 `org.Staff` 的关联规则及跨 App 依赖设计
|
||||
- Django Migrations 迁移顺序说明
|
||||
- Migrations 迁移顺序说明
|
||||
- 架构决策说明(ADR)
|
||||
|
||||
---
|
||||
|
||||
### 5.7 Electron 客户端登录相关约定
|
||||
### 6.8 Electron 客户端约定
|
||||
|
||||
| 约定项 | 规格 |
|
||||
|--------|------|
|
||||
| Tenant Code 存储 | `electron-store` 或 `app.getPath('userData')` + AES 加密,不存储明文 |
|
||||
| Session Token 存储 | 内存(`global` 变量)+ `session` Cookie(Chromium 管理),不写入磁盘明文文件 |
|
||||
| 登录页加载 | 客户端主进程根据 Tenant Code 构建目标 URL(`https://{tenant_slug}.fonrey.com/auth/login/`),通过 `BrowserWindow.loadURL()` 加载 |
|
||||
| 多标签页处理 | 同一 `BrowserWindow` 内,所有页面共享同一 Session Cookie |
|
||||
| 客户端登出 | 调用服务端 `POST /api/auth/logout/` 使服务端 Session 失效 + 清除 Chromium Session Cookie |
|
||||
| 窗口关闭时 | Session 保留(不自动登出),下次打开客户端时若 Session 未过期,直接进入系统 |
|
||||
| 强制更新场景 | 若客户端版本低于服务端 `min_required_version`,则在登录界面前先展示「请更新客户端」提示,阻断登录流程(参见发布管理模块 PRD)|
|
||||
> Electron 客户端在登录链路上的实现约定(Tenant Code 加密存储、Session Cookie 策略、登录页加载、多标签页、登出、窗口关闭、强制更新、安全约束)已迁移至 `TECH_STACK/登录管理技术方案.md` §十三 Electron 客户端约定。
|
||||
|
||||
本 PRD 仅约束业务行为:
|
||||
|
||||
- 客户端必须在登录前完成 Tenant 识别,且 Tenant Code 须**加密**持久化
|
||||
- 客户端登出后,本地不得保留可直接复用的登录凭证
|
||||
- 客户端版本低于服务端要求的最低版本时,**必须阻断登录流程**并展示更新提示(联动平台管理后台 PRD 的客户端发布章节)
|
||||
- 同一客户端窗口内的多标签页应共享同一登录态
|
||||
|
||||
---
|
||||
|
||||
## 6. 技术注意事项
|
||||
## 7. 技术注意事项(产品视角)
|
||||
|
||||
### 6.1 依赖与技术选型
|
||||
### 7.1 业务依赖
|
||||
|
||||
| 依赖项 | 用途 | 说明 |
|
||||
|--------|------|------|
|
||||
| `django.contrib.auth` | 用户认证基础框架 | 扩展 `AbstractBaseUser` 而非直接使用 `User` 模型,以支持 `username` 唯一性约束在租户维度而非全局 |
|
||||
| `django-tenants` | 多租户隔离 | `UserAccount` 属于租户级 Schema,Tenant 验证接口属于 `shared_apps` |
|
||||
| `Redis` | 滑块验证 Token 存储、登录失败计数、短信 OTP 限流计数 | 验证 Key:`captcha_token:{uuid}`(TTL 3min);登录失败 Key:`login_fail:{tenant_id}:{username}`;OTP 限流 Key:`sms_limit:{scene}:{phone}`(TTL 1h)|
|
||||
| 短信服务(待选型) | 发送登录验证码 / 找回密码验证码 | 国内需选用具备短信资质的服务商(如阿里云短信、腾讯云短信);需申请短信签名和模板审核 |
|
||||
| `Celery` | 异步任务处理 | 短信发送异步处理,防止接口响应超时;原邮件发送需求已废弃,短信为主要通知方式 |
|
||||
| `django-ratelimit` 或自定义中间件 | 接口限流 | Tenant 验证接口、登录接口、找回密码接口均需限流 |
|
||||
| `Pillow` | 滑块拼图图片处理 | 生成拼图背景图(抠出缺口区域)及对应的拼图碎片图片,输出为 Base64,分别通过两个字段返回给前端 |
|
||||
- 短信发送依赖具备短信资质的服务商;签名与模板审核须在功能上线前完成
|
||||
- 短信发送须异步处理,避免阻塞登录链路;密码登录可作为短信故障时的保底入口
|
||||
- 多租户隔离与跨 Schema 调用细节由 `TECH_STACK/登录管理技术方案.md` 与 `DATA_MODEL/DATA_MODEL_LOGIN.md` 定义
|
||||
|
||||
### 6.2 多租户下的 `UserAccount` 隔离
|
||||
|
||||
- `UserAccount` 表位于**租户 Schema 内**(`django-tenants` 租户隔离范围),因此 username 唯一性约束在租户维度生效,不同租户的经纪人可以有相同用户名
|
||||
- Tenant 验证接口(`/api/auth/tenant/verify/`)位于**公共 Schema**(`shared_apps`),使用 `TenantModel` 查询
|
||||
- 登录、找回密码等接口通过请求域名(`{tenant_slug}.fonrey.com`)切换到对应租户 Schema(`django-tenants` 中间件自动处理)
|
||||
|
||||
### 6.3 已知风险
|
||||
### 7.2 已知风险
|
||||
|
||||
| 风险 | 可能性 | 影响 | 缓解措施 |
|
||||
|------|--------|------|---------|
|
||||
| 滑块验证被机器模拟轨迹绕过 | 低 | 高 | 服务端同时校验位置偏差 + 轨迹曲线特征(非线性运动特征),拒绝匀速/程序化轨迹;后续可引入设备指纹加固 |
|
||||
| Tenant Code 枚举攻击(暴力试探) | 低 | 中 | Tenant 验证接口限流(每IP每分钟≤10次),返回结果不区分「未找到」与「已禁用」|
|
||||
| 密码重置 Token 泄露 | 低 | 高 | `sms_reset_token` 单次有效、15 分钟过期、HTTPS 传输 |
|
||||
| 短信服务故障导致用户无法找回密码或验证码登录 | 中 | 高 | 短信发送失败写入告警日志;密码登录作为保底方式(非单一入口);建议配置备用短信服务商通道 |
|
||||
| 多端同时登录同一账号 | 高(日常场景) | 低 | 本期允许,后续如需踢出,可在 Token 机制中引入版本号 |
|
||||
| 滑块验证被机器模拟轨迹绕过 | 低 | 高 | 服务端综合位置 + 轨迹特征校验;后续可引入设备指纹加固 |
|
||||
| Tenant Code 枚举攻击(暴力试探) | 低 | 中 | 接口限流 + 统一外显文案,不区分「未找到」与「已禁用」 |
|
||||
| 密码重置凭证泄露 | 低 | 高 | 一次性、短时效、HTTPS 传输 |
|
||||
| 短信服务故障 | 中 | 高 | 失败告警 + 密码登录保底 + 建议配置备用短信通道 |
|
||||
| 多端同时登录同一账号 | 高(日常场景) | 低 | 本期允许,后续如需踢出再做版本号机制 |
|
||||
|
||||
### 6.4 开放问题(开发前需确认)
|
||||
### 7.3 开放问题(开发前需确认)
|
||||
|
||||
- [ ] **短信服务商选型**:使用阿里云短信 / 腾讯云短信 / 其他服务商?需运维确认并提前申请短信签名和模板审核(国内审核周期 1–3 个工作日)— 负责人:后端负责人 — 截止:开发启动前
|
||||
- [ ] **Session 有效期默认值**:8 小时是否满足各租户需求?是否允许租户管理员自行配置?— 负责人:产品经理 — 截止:开发启动前
|
||||
- [ ] **滑块拼图实现方案**:自研(Pillow 生成图片 + 前端拖拽组件)还是集成第三方行为验证服务(如极验 GeeTest / 网易易盾)?自研可控但需维护图库;第三方开箱即用但引入外部依赖,需评估数据合规要求 — 负责人:后端负责人 + 安全 — 截止:开发启动前
|
||||
- [ ] **账号锁定通知**:账号被锁定后,是否自动发短信通知用户和/或通知管理员(站内消息)?— 负责人:产品经理 — 截止:开发启动前
|
||||
- [ ] **短信服务商选型**:阿里云 / 腾讯云 / 其他?短信签名与模板审核需要 1–3 个工作日 — 负责人:后端负责人 — 截止:开发启动前
|
||||
- [ ] **Session 默认有效期**:8 小时是否满足各租户需求?是否允许 Tenant Admin 自行配置?— 负责人:产品经理 — 截止:开发启动前
|
||||
- [ ] **滑块拼图实现方案**:自研还是集成第三方行为验证服务?需评估数据合规要求 — 负责人:后端负责人 + 安全 — 截止:开发启动前
|
||||
- [ ] **账号锁定通知**:账号被锁定后,是否自动发短信通知用户和/或通知管理员?— 负责人:产品经理 — 截止:开发启动前
|
||||
- [ ] **历史密码校验范围**:最近 3 次是否足够?是否需要额外规则(如不能与用户名相同)?— 负责人:产品经理 — 截止:开发启动前
|
||||
|
||||
---
|
||||
|
||||
## 7. 发布计划
|
||||
## 8. 发布计划
|
||||
|
||||
| 阶段 | 时间 | 受众 | 准入门槛 |
|
||||
|------|------|------|---------|
|
||||
@@ -630,49 +633,50 @@ Response 200 (失败):
|
||||
|
||||
---
|
||||
|
||||
## 8. 附录
|
||||
## 9. 附录
|
||||
|
||||
### 8.1 登录状态流转图
|
||||
### 9.1 登录状态流转图
|
||||
|
||||
```
|
||||
[未识别 Tenant]
|
||||
│ 输入有效 Tenant Code
|
||||
↓
|
||||
[未登录]
|
||||
│ 账密登录成功
|
||||
│ 账密 / 验证码登录成功
|
||||
↓
|
||||
[初始密码状态](如账号为初始密码)
|
||||
│ 强制修改密码成功
|
||||
│ 强制改密成功
|
||||
↓
|
||||
[已登录 - Active Session]
|
||||
│ Session 过期 / 主动登出 / 管理员强制登出
|
||||
↓
|
||||
[未登录](跳转登录界面)
|
||||
[未登录](跳转登录页)
|
||||
|
||||
[账号锁定状态](5次错误后)
|
||||
│ 30 分钟后自动解锁 或 管理员手动解锁
|
||||
[账号锁定状态](密码连续错误达到阈值后)
|
||||
│ 锁定时长结束自动解锁 或 管理员手动解锁
|
||||
↓
|
||||
[未登录](可重新登录)
|
||||
```
|
||||
|
||||
### 8.2 接口清单汇总
|
||||
### 9.2 业务能力清单(替代原"接口清单")
|
||||
|
||||
| 接口 | 方法 | Schema 位置 | 是否需要鉴权 | 说明 |
|
||||
|------|------|------------|------------|------|
|
||||
| `/api/auth/tenant/verify/` | POST | Public(shared) | 否 | Tenant Code 验证 |
|
||||
| `/api/auth/captcha/` | GET | Tenant | 否 | 获取滑块拼图验证码(返回背景图 Base64 + 碎片图 Base64 + 验证 Token) |
|
||||
| `/api/auth/captcha/verify/` | POST | Tenant | 否 | 提交滑动轨迹 + 位置,服务端校验并返回一次性通过凭证(供登录接口使用) |
|
||||
| `/api/auth/login/` | POST | Tenant | 否 | 手机号 + 密码登录 |
|
||||
| `/api/auth/login/phone/` | POST | Tenant | 否 | 手机号 + 短信验证码登录(MVP 正式功能) |
|
||||
| `/api/auth/logout/` | POST | Tenant | 是 | 登出,使 Session 失效 |
|
||||
| `/api/auth/recover/password/request/` | POST | Tenant | 否 | 发起找回密码(发送短信验证码) |
|
||||
| `/api/auth/recover/password/verify/` | POST | Tenant | 否 | 校验短信验证码,颁发一次性 `sms_reset_token` |
|
||||
| `/api/auth/recover/password/reset/` | POST | Tenant | 否(Token 鉴权) | 提交新密码 |
|
||||
| `/api/auth/wechat/qrcode/` | GET | Tenant | 否 | **预留 v2**,获取微信二维码 |
|
||||
| `/api/auth/wechat/callback/` | POST | Tenant | 否 | **预留 v2**,微信扫码回调 |
|
||||
> 本文件不再列出具体接口路径与 HTTP 方法。完整 API 端点清单(含路径、方法、Schema 位置、是否鉴权、预留端点)请查阅 `TECH_STACK/登录管理技术方案.md` §五。
|
||||
|
||||
### 8.3 相关文档参考
|
||||
| 业务能力 | 提供给谁 | 说明 |
|
||||
|---|---|---|
|
||||
| Tenant 识别 | 客户端 | 输入 12 位 Tenant Code,返回租户品牌与登录入口 |
|
||||
| 获取/校验滑块验证码 | 登录页 | 行为验证码生成与校验,输出一次性通过凭证 |
|
||||
| 密码登录 | 已识别租户的用户 | 手机号 + 密码 + 滑块凭证完成认证 |
|
||||
| 验证码登录 | 已识别租户的用户 | 手机号 + 短信验证码完成认证(需先通过滑块再获取验证码) |
|
||||
| 登出 | 已登录用户 | 销毁服务端会话与本地登录凭证 |
|
||||
| 找回密码(三步) | 忘记密码的用户 | 发送 OTP → 校验 OTP → 设置新密码 |
|
||||
| 首次登录强制改密 | 处于初始密码状态的用户 | 强制流程,提交后脱离初始密码状态 |
|
||||
| 微信扫码登录(v2 预留) | 全量用户 | MVP 仅展示禁用入口,v2 实现 |
|
||||
|
||||
- 技术栈文档:`Project/fonrey/TECH_STACK/TECH_STACK.md`
|
||||
- **登录管理数据模型**:`Project/fonrey/DATA_MODEL/DATA_MODEL_LOGIN.md`
|
||||
- **登录管理技术方案**:`Project/fonrey/TECH_STACK/登录管理技术方案.md`
|
||||
### 9.3 相关文档参考
|
||||
|
||||
- 技术栈总纲:`TECH_STACK/TECH_STACK.md`
|
||||
- **登录管理技术方案(实现口径权威)**:`TECH_STACK/登录管理技术方案.md`
|
||||
- **登录管理数据模型(数据口径权威)**:`DATA_MODEL/DATA_MODEL_LOGIN.md`
|
||||
- 全局 API 契约:`TECH_STACK/API_CONTRACT.md`
|
||||
- ADR:`ADR.md`(特别是 `ADR-20260502-003` PRD/Tech 职责边界、`ADR-20260430-004` 登录接口路径同步规则)
|
||||
|
||||
Reference in New Issue
Block a user