chore: sync local project changes

This commit is contained in:
Shen Wei
2026-04-28 16:39:21 +08:00
parent 365caa800a
commit e4cf7f8485
27 changed files with 13691 additions and 1317 deletions

View File

@@ -465,7 +465,127 @@
---
## 6. 上线计划
## 6. 技术考量
### 6.1 依赖关系
| 系统/模块 | 依赖原因 | 优先级风险 |
|-----------|---------|-----------|
| 登录管理模块 | 员工账号的创建、冻结、密码重置由登录模块提供接口 | 高 |
| 权限管理模块 | 员工的角色/权限分配在人事模块发起,权限模块执行 | 高 |
| 系统配置模块 | 职务类别、部门级别、奖惩类型等枚举值从系统配置读取 | 中 |
### 6.2 核心技术设计
#### 6.2.1 组织树存储方案
采用 **Closure Table闭包表** 存储多层级部门树,最大支持 8 层嵌套:
| 方案 | 优点 | 缺点 | 选用理由 |
|------|------|------|---------|
| Adjacency List | 写入简单 | 递归查询慢,需多次 SQL | ❌ 不适合 8 层嵌套 |
| Nested Set | 读取极快 | 写入时需重排序,高并发写冲突大 | ❌ 人事模块写入频繁 |
| **Closure Table** | 读写均衡,查询任意层级 O(1) | 存储量略多 | ✅ **选用** |
**数据结构**
```python
class OrgUnit(TenantModel): # 部门节点
id = UUIDField(primary_key=True)
name = CharField(max_length=100)
level = CharField(choices=['事业部','大区','区域','片区','门店','店组','职能'])
parent = ForeignKey('self', null=True, on_delete=PROTECT) # 直接父节点
is_direct = BooleanField(default=True) # True=直营, False=加盟
deleted_at = DateTimeField(null=True)
class OrgUnitClosure(TenantModel): # 闭包表
ancestor = ForeignKey(OrgUnit, related_name='descendants')
descendant = ForeignKey(OrgUnit, related_name='ancestors')
depth = IntegerField() # 0=自身, 1=子, 2=孙...
```
**查询示例**
- 查某节点所有子孙:`WHERE ancestor_id = X AND depth > 0`
- 查某节点完整路径:`WHERE descendant_id = X ORDER BY depth DESC`
- 移动节点:删除旧闭包记录 + 插入新闭包记录(事务内执行)
#### 6.2.2 员工状态机
员工在系统内的状态须通过后端状态机严格控制:
```
[录入] ──► 试用期 ──► 正式 ──► 离职
│ │
└──────────────┘
复职
试用期 / 正式(恢复上次状态)
```
| 状态 | 允许流转目标 | 触发方式 |
|------|------------|---------|
| 试用期 | 正式、离职 | 管理员手动操作 |
| 正式 | 离职 | 管理员手动操作 |
| 离职 | 复职 | 管理员手动操作 |
| 冻结 | 启用(需先恢复员工状态) | 账号冻结操作 |
**实现要求**
- 每次状态变更写入 `StaffChangeLog`(异动记录表),字段包含:变更人、变更类型、变更前后值、变更时间、备注
- 员工离职时,系统自动:① 冻结关联登录账号 ② 解除权限角色 ③ 归属客源触发掉公检查
- 员工状态变更失败返回明确错误码,前端展示友好提示
#### 6.2.3 异动记录审计策略
异动记录为**只追加append-only**设计,不支持修改或删除:
| 异动类型枚举 | 触发场景 |
|------------|---------|
| 入职 | 员工首次录入系统 |
| 转正 | 试用期转为正式员工 |
| 调岗 | 员工所属部门变更 |
| 上级变动 | 直接上级变更 |
| 离职 | 办理离职手续 |
| 复职 | 离职员工重新入职 |
| 账号冻结 | 账号被冻结 |
| 账号启用 | 账号从冻结恢复 |
```python
class StaffChangeLog(TenantModel):
staff = ForeignKey(Staff)
change_type = CharField(choices=[...]) # 见枚举表
before_value = JSONField(null=True) # 变更前状态快照
after_value = JSONField(null=True) # 变更后状态快照
operator = ForeignKey(User)
remark = TextField(blank=True)
created_at = DateTimeField(auto_now_add=True)
# 无 updated_at / deleted_at — append-only
```
#### 6.2.4 敏感数据脱敏
| 数据 | 存储方式 | 展示方式 | 查看权限 |
|------|---------|---------|---------|
| 手机号 | AES-256-GCM 加密 + SHA-256 哈希索引 | `159****9696` | 需「查看员工手机号」权限,留操作日志 |
| 证件号码 | AES-256-GCM 加密 | `410***********3037` | 需「查看员工证件」权限,留操作日志 |
| 通讯录号码 | AES-256-GCM 加密 | 脱敏显示 | 点击「查看号码」后临时展示,留痕 |
### 6.3 已知风险
| 风险 | 可能性 | 影响 | 缓解措施 |
|------|--------|------|---------|
| 大型机构部门树深度超过 8 层 | 低 | 中 | 前端录入时限制最大深度,超出提示联系运营调整 |
| 员工大批量导入时账号创建并发冲突 | 中 | 中 | 批量导入走 Celery 异步任务,序列化账号创建 |
| 组织调整时历史客源/房源归属关系错乱 | 中 | 高 | 员工调岗不改变现有资源归属,仅更新组织关系;业务重新分配由管理员手动操作 |
### 6.4 待确认问题
- [ ] 员工工号规则:系统自动生成还是管理员手动填写?格式约定是什么? — 待业务确认 — 开发前必须明确
- [ ] 「复职」时是否复用原有账号?还是重新创建新账号? — 待产品/法务确认
- [ ] 部门移动操作(将某节点及其子树挂到新父节点)是否在 MVP 范围内? — 待产品确认
---
## 7. 上线计划
| 阶段 | 时间 | 受众 | 验收门槛 |
|------|------|------|----------|