Sync: add design ux architecture notes
This commit is contained in:
@@ -2,8 +2,8 @@
|
||||
|
||||
**状态**: Draft
|
||||
**作者**: 产品经理
|
||||
**最后更新**: 2026-04-24(v1.1 验证码方式由图形字符验证码更新为滑块拼图行为验证码)
|
||||
**版本**: 1.1
|
||||
**最后更新**: 2026-04-24(v1.3 明确账号创建权限层级:Tenant Admin 可自定义用户名/密码;普通员工账号由 Tenant Admin 在新增员工时创建,用户名为手机号,初始密码固定,首次登录强制修改)
|
||||
**版本**: 1.3
|
||||
**所属系统**: Fonrey 房产经纪管理系统
|
||||
**关联模块**: 组织人事管理、权限管理、系统管理
|
||||
|
||||
@@ -246,7 +246,7 @@ POST /api/auth/wechat/callback/ # 微信扫码确认后回调,换取系
|
||||
| Logo | Fonrey 产品 Logo,居中显示 |
|
||||
| 标题 | 「欢迎使用 Fonrey 房睿」 |
|
||||
| 副标题 | 「请输入您公司的专属识别码以继续」 |
|
||||
| Tenant ID 输入框 | 单行文本,最大长度 64 字符,支持粘贴 |
|
||||
| Tenant ID 输入框 | 单行数字输入,固定 12 位,支持粘贴;非数字字符自动过滤,超出 12 位截断 |
|
||||
| 输入框 Label | 「公司识别码(Tenant ID)」 |
|
||||
| 确认按钮 | 主色调按钮,文字「确认」 |
|
||||
| 错误提示 | 输入框下方红色文字,固定区域占位(不影响布局抖动) |
|
||||
@@ -254,10 +254,11 @@ POST /api/auth/wechat/callback/ # 微信扫码确认后回调,换取系
|
||||
|
||||
#### 5.1.3 Tenant ID 格式规范
|
||||
|
||||
- Tenant ID 由系统管理模块生成(每个租户在开通时由平台运营分配)
|
||||
- 格式建议:`tenant_<slug>`,如 `tenant_fonrey_demo`,仅包含字母、数字、下划线,不区分大小写
|
||||
- 长度:6 ~ 64 字符
|
||||
- 客户端存储位置:Electron `app.getPath('userData')` 目录下的配置文件(加密存储,防止明文读取)
|
||||
- **格式**:固定 **12 位纯数字**,如 `202500010001`
|
||||
- **生成规则**(建议):由平台运营在系统管理后台开通租户时自动生成,不允许手动指定,确保全局唯一性;可采用时间戳前缀 + 随机后缀的方式生成(如 `YYYYMM` + 6 位随机数)
|
||||
- **前端校验**:输入框仅接受数字字符(非数字自动过滤),输入满 12 位后自动触发格式完成状态;少于 12 位时点击「确认」弹出提示「识别码须为 12 位数字」
|
||||
- **唯一性**:全局唯一(公共 Schema 层面),同一 Tenant ID 不可分配给多个租户
|
||||
- **客户端存储**:Electron `app.getPath('userData')` 目录下的配置文件(加密存储,防止明文读取)
|
||||
|
||||
#### 5.1.4 服务端 Tenant 验证接口规范
|
||||
|
||||
@@ -266,7 +267,7 @@ POST /api/auth/tenant/verify/
|
||||
|
||||
Request Body:
|
||||
{
|
||||
"tenant_id": "tenant_fonrey_demo"
|
||||
"tenant_id": "202500010001"
|
||||
}
|
||||
|
||||
Response 200 (成功):
|
||||
@@ -346,29 +347,67 @@ Response 200 (失败):
|
||||
|
||||
#### 5.3.1 绑定原则
|
||||
|
||||
- 每个系统登录账号(username)必须与「组织人事管理」模块中的一条**员工档案(Staff)**绑定
|
||||
- 员工档案包含:姓名、工号、手机号(可选,未来用于手机登录)、邮箱(用于找回密码)、所属门店/组别
|
||||
- 每个系统登录账号必须与「组织人事管理」模块中的一条**员工档案(Staff)**绑定
|
||||
- 账号与员工是 **1:1 关系**,一个员工对应一个账号,一个账号只能绑定一个员工
|
||||
- 账号创建流程:**由系统管理员在「组织人事管理 → 员工列表」中为员工开通账号**,不支持用户自行注册
|
||||
- 账号禁用:员工离职或被停用时,对应账号自动禁用;禁用账号无法登录,但历史操作记录保留
|
||||
- **不支持用户自行注册**,所有账号均由有权限的管理角色创建
|
||||
|
||||
#### 5.3.2 账号字段规范
|
||||
#### 5.3.2 账号创建权限分层
|
||||
|
||||
| 字段 | 规格 | 说明 |
|
||||
|------|------|------|
|
||||
| 用户名(username) | 英文字母开头,仅包含字母/数字/下划线,6~30 字符,同租户内唯一 | 登录 ID,不可更改 |
|
||||
| 密码(password) | 8~32 位,含字母+数字,建议含特殊字符 | 使用 `PBKDF2+SHA256` 哈希存储 |
|
||||
| 邮箱(email) | 标准邮箱格式,同租户内唯一(可选,但若空则无法自助找回密码) | 用于找回账号/密码 |
|
||||
| 手机号(phone) | 中国大陆 11 位手机号,加密存储,同租户内唯一(可选) | 预留,v2 用于手机登录 |
|
||||
| 员工档案关联(staff_id) | 外键关联 `org.Staff` 模型 | 实名绑定 |
|
||||
| 账号状态(status) | `active` / `disabled` / `locked` | locked 为密码错误锁定,30 分钟自动恢复 |
|
||||
| 初始密码 | 由管理员创建账号时设置或系统随机生成 | 建议首次登录强制修改密码 |
|
||||
系统内共有两类账号创建场景,权限和规则各不相同:
|
||||
|
||||
#### 5.3.3 首次登录强制修改密码
|
||||
**① Tenant Admin 账号(每个租户唯一的超级管理账号)**
|
||||
|
||||
- 管理员新建账号或重置密码后,账号处于「初始密码」状态
|
||||
- 持有「初始密码」的账号登录成功后,系统自动弹出「请修改初始密码」强制页面,**不可跳过**
|
||||
- 修改成功后,账号状态更新为正常,进入系统首页
|
||||
| 项目 | 规格 |
|
||||
|------|------|
|
||||
| 创建时机 | 平台运营在系统管理后台开通租户时,同步创建第一个 Tenant Admin 账号 |
|
||||
| 用户名 | **由平台运营自定义设置**,格式:英文字母开头,仅含字母/数字/下划线,6~30 字符,同租户内唯一 |
|
||||
| 初始密码 | **由平台运营自定义设置**,须符合密码复杂度规则(8~32 位,含字母+数字) |
|
||||
| 首次登录 | 强制修改初始密码,不可跳过 |
|
||||
| 权限范围 | 拥有该租户内最高权限,可管理员工账号、角色、系统设置等 |
|
||||
| 数量限制 | 每个租户仅限 1 个 Tenant Admin 账号(后续可扩展为多管理员,v2 规划) |
|
||||
|
||||
**② 普通员工账号(经纪人、店长、行政等)**
|
||||
|
||||
| 项目 | 规格 |
|
||||
|------|------|
|
||||
| 创建时机 | Tenant Admin 在「组织人事管理 → 新增员工」时,系统自动为该员工创建登录账号 |
|
||||
| 用户名 | **固定为该员工的手机号**(11 位数字),同租户内唯一,创建后不可更改 |
|
||||
| 初始密码 | **系统统一固定初始密码**(由平台在部署配置中设定,如 `Fonrey@2025`),所有新员工账号均使用同一初始密码 |
|
||||
| 首次登录 | 强制修改初始密码,**不可跳过**(详见 5.3.4) |
|
||||
| 密码重置 | Tenant Admin 可在员工管理界面对任意员工账号执行「重置密码」,重置后恢复为固定初始密码,触发首次登录强制修改流程 |
|
||||
| 账号禁用 | 员工离职或被停用时,对应账号自动禁用;禁用账号无法登录,历史操作记录保留 |
|
||||
|
||||
#### 5.3.3 账号字段规范
|
||||
|
||||
| 字段 | 类型 | Tenant Admin | 普通员工账号 | 说明 |
|
||||
|------|------|-------------|-------------|------|
|
||||
| 用户名(username) | CharField(30) | 平台运营自定义,字母开头,含字母/数字/下划线,6~30 字符 | **固定为员工手机号**(11 位数字) | 登录 ID,创建后不可更改 |
|
||||
| 密码(password) | CharField | 平台运营自定义初始密码 | **系统统一固定初始密码** | PBKDF2+SHA256 哈希存储 |
|
||||
| 手机号(phone) | CharField(11) | 选填,加密存储 | **必填,同时作为用户名**,加密存储,同租户内唯一 | 当前阶段为登录 ID;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 | 审计追溯 |
|
||||
|
||||
#### 5.3.4 首次登录强制修改密码
|
||||
|
||||
- 新员工账号创建后,`is_initial_password = True`,账号处于「初始密码」状态
|
||||
- 员工使用手机号(用户名)+ 固定初始密码登录成功后,系统**立即跳转**至「修改初始密码」强制页面,**不可关闭、不可跳过**,任何其他系统功能页面均不可访问
|
||||
- Tenant Admin 对员工账号执行「重置密码」后,`is_initial_password` 重置为 True,该员工下次登录时再次触发强制修改流程
|
||||
- 修改成功后,`is_initial_password` 更新为 False,原 Session 保持有效,直接进入系统首页
|
||||
|
||||
**强制修改密码页面规范**:
|
||||
|
||||
| 元素 | 规格 |
|
||||
|------|------|
|
||||
| 页面标题 | 「欢迎使用 Fonrey,请先设置您的登录密码」 |
|
||||
| 提示文案 | 「您当前使用的是初始密码,为保障账号安全,请立即设置新密码后开始使用」 |
|
||||
| 新密码输入框 | 密文显示,右侧「显示/隐藏」切换 |
|
||||
| 确认新密码输入框 | 密文显示,与新密码一致性实时校验 |
|
||||
| 密码强度提示 | 逐条实时显示规则达标状态(✓/✗):长度 ≥ 8 位 / 包含字母 / 包含数字 |
|
||||
| 提交按钮 | 「确认并进入系统」 |
|
||||
| 不可操作项 | 无「跳过」按钮;顶部导航栏、侧边菜单、关闭按钮均禁用 |
|
||||
|
||||
---
|
||||
|
||||
@@ -376,21 +415,30 @@ Response 200 (失败):
|
||||
|
||||
#### 5.4.1 找回用户名流程
|
||||
|
||||
> **说明**:由于普通员工的用户名即为其**手机号**,通常无需「找回用户名」功能。登录界面的「忘记用户名」入口保留,但仅对 Tenant Admin 账号有意义(其用户名为自定义字符串)。
|
||||
|
||||
```
|
||||
用户点击「忘记用户名」
|
||||
│
|
||||
├─ 输入邮箱地址
|
||||
│ │
|
||||
│ 服务端查询该邮箱是否绑定账号(不向前端返回查询结果,防止枚举)
|
||||
│ │
|
||||
│ 统一响应「如该邮箱已绑定账号,您将收到邮件」
|
||||
│ │
|
||||
│ 后台:若邮箱存在 → 发送邮件(包含用户名)
|
||||
│ 若邮箱不存在 → 静默处理,不发送
|
||||
├─ 普通员工:提示「您的登录账号为您的手机号,请直接使用手机号登录」
|
||||
│ 提供「返回登录」按钮
|
||||
│
|
||||
└─ 用户查收邮件,获取用户名 → 返回登录
|
||||
└─ Tenant Admin(用户名非手机号格式):
|
||||
│
|
||||
├─ 输入绑定邮箱
|
||||
│ │
|
||||
│ 服务端查询(不向前端返回查询结果,防止枚举)
|
||||
│ │
|
||||
│ 统一响应「如该邮箱已绑定账号,您将收到邮件」
|
||||
│ │
|
||||
│ 后台:邮箱存在 → 发送邮件(包含用户名)
|
||||
│ 邮箱不存在 → 静默处理
|
||||
│
|
||||
└─ 用户查收邮件,获取用户名 → 返回登录
|
||||
```
|
||||
|
||||
> **前端识别逻辑**:用户在「忘记用户名」页面输入邮箱提交后,服务端根据是否匹配到 Tenant Admin 账号决定处理路径,前端无需区分,统一展示「如该邮箱已绑定账号,您将收到邮件」。
|
||||
|
||||
**邮件模板(找回用户名)**:
|
||||
|
||||
```
|
||||
@@ -413,13 +461,16 @@ Response 200 (失败):
|
||||
```
|
||||
用户点击「忘记密码」
|
||||
│
|
||||
步骤1:输入用户名 + 邮箱
|
||||
步骤1:身份验证
|
||||
│
|
||||
├─ 服务端校验(用户名 + 邮箱匹配)
|
||||
├─ 输入手机号(即用户名)+ 绑定邮箱
|
||||
│ (Tenant Admin 则输入自定义用户名 + 绑定邮箱)
|
||||
│ │
|
||||
│ 统一响应「如信息匹配,重置链接将发送至您的邮箱」
|
||||
│ 服务端校验用户名与邮箱是否匹配
|
||||
│ │
|
||||
│ 后台:匹配成功 → 生成加密 Token(含过期时间 30min)→ 发送邮件
|
||||
│ 统一响应「如信息匹配,重置链接将发送至您的邮箱」(防止枚举)
|
||||
│ │
|
||||
│ 后台:匹配成功 → 生成加密 Token(有效期 30min)→ 异步发送邮件
|
||||
│ 不匹配 → 静默处理
|
||||
│
|
||||
步骤2:用户点击邮件中的重置链接
|
||||
@@ -428,16 +479,20 @@ Response 200 (失败):
|
||||
│ │
|
||||
│ 有效 → 展示「重置密码」表单
|
||||
│ │
|
||||
│ 无效/过期 → 提示「链接已过期,请重新申请」
|
||||
│ 无效/过期 → 提示「链接已过期,请重新申请」,提供「重新申请」按钮
|
||||
│
|
||||
步骤3:用户输入并提交新密码
|
||||
│
|
||||
├─ 密码复杂度校验
|
||||
├─ 与历史密码对比校验(最近 3 次)
|
||||
├─ 密码复杂度校验(≥ 8 位,含字母+数字)
|
||||
├─ 与历史密码对比校验(最近 3 次,含固定初始密码)
|
||||
│
|
||||
└─ 校验通过 → 更新密码 → 清除所有 Session → 跳转登录界面
|
||||
└─ 校验通过 → 更新密码,is_initial_password = False
|
||||
→ 清除该账号所有有效 Session(强制重新登录)
|
||||
→ 跳转登录界面,提示「密码已重置,请重新登录」
|
||||
```
|
||||
|
||||
> **注意**:找回密码流程依赖员工账号绑定了邮箱。若员工未绑定邮箱,无法自助找回,需联系 Tenant Admin 在管理界面执行「重置密码」操作,将密码恢复为固定初始密码。
|
||||
|
||||
**邮件模板(重置密码)**:
|
||||
|
||||
```
|
||||
@@ -482,16 +537,17 @@ apps/
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | BigAutoField | 主键 |
|
||||
| `username` | CharField(30) | 登录名,同租户唯一,不可变更 |
|
||||
| `username` | CharField(30) | 登录名,同租户唯一,不可变更;普通员工账号固定为手机号(11位数字),Tenant Admin 为自定义字符串 |
|
||||
| `password` | CharField | PBKDF2+SHA256 哈希,使用 Django `make_password` |
|
||||
| `email` | EmailField | 绑定邮箱,同租户唯一,选填 |
|
||||
| `phone` | CharField(11) | 绑定手机号,加密存储(`core.encryption`),选填 |
|
||||
| `staff` | OneToOneField → `org.Staff` | 员工档案绑定,必须 |
|
||||
| `email` | EmailField | 绑定邮箱,同租户唯一,选填;为空时无法自助找回密码 |
|
||||
| `phone` | CharField(11) | 绑定手机号,加密存储(`core.encryption`);普通员工必填(同时作为 username 来源),Tenant Admin 选填 |
|
||||
| `staff` | OneToOneField → `org.Staff` | 员工档案绑定;普通员工必须,Tenant Admin 可为空 |
|
||||
| `is_tenant_admin` | BooleanField | 标记是否为该租户的 Tenant Admin 账号,默认 False |
|
||||
| `status` | CharField | `active` / `disabled` / `locked` |
|
||||
| `is_initial_password` | BooleanField | True 时登录后强制修改密码 |
|
||||
| `is_initial_password` | BooleanField | True 时登录成功后强制跳转修改密码页,不可跳过 |
|
||||
| `last_login` | DateTimeField | 最后登录时间 |
|
||||
| `created_at` | DateTimeField | 账号创建时间 |
|
||||
| `created_by` | ForeignKey → self | 创建人(管理员) |
|
||||
| `created_by` | ForeignKey → self | 创建人(普通员工由 Tenant Admin 创建;Tenant Admin 由平台运营创建) |
|
||||
|
||||
#### 5.5.3 `LoginAttempt` 登录尝试记录
|
||||
|
||||
|
||||
Reference in New Issue
Block a user