360 lines
11 KiB
Markdown
360 lines
11 KiB
Markdown
> **For AI assistants**: Read this entire file before writing any code. All decisions here are final. Do not suggest alternatives unless asked.
|
||
|
||
# Fonrey 组织人事技术方案
|
||
|
||
**版本**: 1.0
|
||
**项目**: Fonrey 房产经纪管理系统
|
||
**技术栈**: Django 4.x + HTMX + Alpine.js + PostgreSQL 16 + Redis + Celery
|
||
**关联 PRD**: `PRD/组织人事管理/组织人事管理模块PRD.md`(v1.2)
|
||
**关联数据模型**: `DATA_MODEL/DATA_MODEL_ORG.md`(本方案不重复 DDL)
|
||
**关联契约规范**: `TECH_STACK/API_CONTRACT.md`(全局 API 契约权威)
|
||
**关联枚举字典**: `DATA_MODEL/ENUMS.md`
|
||
**最后更新**: 2026-04-27
|
||
|
||
---
|
||
|
||
## 变更历史
|
||
|
||
| 日期 | 变更人 | 变更内容 |
|
||
|---|---|---|
|
||
| 2026-04-30 | Atlas | 补充“变更历史”章节(文档治理) |
|
||
|
||
## 一、文档定位与边界
|
||
|
||
本文件只定义组织人事模块的:
|
||
|
||
1. 服务边界与模块协作
|
||
2. API 端点设计(重点)
|
||
3. HTMX 局刷协议
|
||
4. 权限接入、异动审计、缓存与性能约束
|
||
5. 测试与验收映射
|
||
|
||
> **不在本文件展开**:`org_units/staff/staff_transfer_logs/staff_reward_punish/staff_accounts` 结构细节。以 `DATA_MODEL_ORG.md` 为唯一权威。
|
||
|
||
---
|
||
|
||
## 二、范围定义(以 TASK.md 为准)
|
||
|
||
### 2.1 P0 必须覆盖
|
||
|
||
- US-ORG-001:管理员维护公司组织结构(部门/门店树)
|
||
- US-ORG-002:管理员查看与维护员工列表
|
||
- US-ORG-003:管理员办理员工入职并创建系统账号
|
||
|
||
### 2.2 P1/P2 预留
|
||
|
||
- US-ORG-010:员工离职与调动
|
||
- US-ORG-011:员工通讯录
|
||
- US-ORG-012:员工职务管理
|
||
- US-ORG-020:全局异动记录
|
||
- US-ORG-021:员工奖惩记录
|
||
- US-ORG-022:门店分布地图
|
||
|
||
---
|
||
|
||
## 三、模块架构边界
|
||
|
||
## 3.1 模块职责(`apps/org`)
|
||
|
||
- 管理组织树(公司→事业部→大区→区域→片区→门店→店组→职能)
|
||
- 管理员工主档(任职、联系方式、账号、状态)
|
||
- 管理入职与账号开通主流程
|
||
- 维护人事异动审计链路(入职/调动/离职/复职等)
|
||
|
||
## 3.2 外部依赖
|
||
|
||
| 依赖模块 | 依赖内容 | 依赖方式 |
|
||
|---|---|---|
|
||
| `apps/account` | 新员工账号创建、初始密码策略、登录状态联动 | Service 调用 |
|
||
| `apps/permission` | 组织/员工管理权限校验 | PermissionChecker |
|
||
| `apps/property` | 员工离职/调动时业务归属迁移(预留) | 异步任务 |
|
||
| `apps/client` | 客源归属迁移(预留) | 异步任务 |
|
||
| `core/encryption.py` | 手机号/证件号加密存储与脱敏 | 统一工具 |
|
||
| `core/cache.py` | 组织树缓存、员工选择器缓存 | Redis |
|
||
| `Celery` | 批量导出、跨模块归属迁移、异动统计重算 | 异步任务 |
|
||
|
||
## 3.3 分层约束
|
||
|
||
- `views.py` 仅做参数校验、权限门禁、响应组装
|
||
- 组织层级规则(店组必须挂门店、经纪人归属限制)必须在 `services/` 强校验
|
||
- 员工状态变更必须联动写 `staff_transfer_logs`(不可删除)
|
||
- 与账号相关操作通过 `AccountService`,禁止跨模块直写登录表
|
||
|
||
---
|
||
|
||
## 四、API 设计原则
|
||
|
||
1. 页面路由与数据 API 分离:
|
||
- 页面:`/org/...`
|
||
- 数据:`/api/org/...`
|
||
2. 组织树、员工列表、异动记录采用 HTMX 局刷,减少全页刷新。
|
||
3. 手机号/证件号默认脱敏展示,明文查看须权限与审计。
|
||
4. 账号状态(启用/冻结)与员工状态变更必须一致性联动。
|
||
5. 所有异动事件统一错误协议与日志结构,便于审计与报表。
|
||
|
||
---
|
||
|
||
## 五、端点清单(核心)
|
||
|
||
## 5.1 页面路由(SSR + HTMX 容器)
|
||
|
||
| 路径 | 方法 | 鉴权 | 说明 |
|
||
|---|---|---|---|
|
||
| `/org/structure/` | GET | 是 | 组织结构主页面(组织树 + 员工列表) |
|
||
| `/org/departments/create/` | GET | 是 | 新增部门页面 |
|
||
| `/org/departments/{org_id}/` | GET | 是 | 部门详情 |
|
||
| `/org/departments/{org_id}/edit/` | GET | 是 | 编辑部门 |
|
||
| `/org/staff/{staff_id}/` | GET | 是 | 员工详情页(多 Tab) |
|
||
| `/org/transfer-logs/` | GET | 是 | 全局异动记录页(预留) |
|
||
|
||
## 5.2 HTMX 片段端点
|
||
|
||
| 路径 | 方法 | 用途 | 返回 |
|
||
|---|---|---|---|
|
||
| `/org/fragments/org-tree/` | GET | 左侧组织树局刷 | HTML |
|
||
| `/org/fragments/staff-table/` | POST | 员工列表筛选/分页局刷 | HTML |
|
||
| `/org/staff/{id}/fragments/tab/{tab}/` | GET | 员工详情 Tab 懒加载 | HTML |
|
||
| `/org/fragments/department-staff-selector/` | GET | 选择器弹层局刷 | HTML |
|
||
|
||
> fragment 端点必须校验 `HX-Request=true`,否则 400。
|
||
|
||
## 5.3 JSON API(P0)
|
||
|
||
| 端点 | 方法 | 权限 code(建议) | 说明 |
|
||
|---|---|---|---|
|
||
| `/api/org/departments/tree/` | GET | `hr.org.view.scope` | 组织树加载(US-001) |
|
||
| `/api/org/departments/` | POST | `hr.org.edit.allow` | 新增部门(US-001) |
|
||
| `/api/org/departments/{id}/` | PATCH | `hr.org.edit.allow` | 编辑部门(US-001) |
|
||
| `/api/org/departments/{id}/close/` | POST | `hr.org.edit.allow` | 关闭部门 |
|
||
| `/api/org/staff/query/` | POST | `hr.staff.view.scope` | 员工列表查询(US-002) |
|
||
| `/api/org/staff/{id}/detail/` | GET | `hr.staff.view.scope` | 员工详情(US-002) |
|
||
| `/api/org/staff/onboard/` | POST | `hr.staff.create.allow` | 办理入职+建账号(US-003) |
|
||
| `/api/org/staff/{id}/base-info/` | PATCH | `hr.staff.edit.allow` | 编辑员工基础信息(US-002) |
|
||
| `/api/org/staff/{id}/account/freeze/` | POST | `hr.staff.account.manage.allow` | 冻结账号(预留) |
|
||
| `/api/org/staff/{id}/account/unfreeze/` | POST | `hr.staff.account.manage.allow` | 解冻账号(预留) |
|
||
| `/api/org/staff/{id}/phone/view/` | POST | `hr.staff.phone.view.allow` | 查看完整手机号(审计) |
|
||
|
||
---
|
||
|
||
## 六、关键 API 规范(请求/响应)
|
||
|
||
## 6.1 新增部门
|
||
|
||
`POST /api/org/departments/`
|
||
|
||
```json
|
||
{
|
||
"name": "都市港湾店二组",
|
||
"parent_id": "uuid",
|
||
"type": "group",
|
||
"address_city": "上海",
|
||
"address_district": "闵行区",
|
||
"address_detail": "XX路XX号",
|
||
"manager_id": "uuid"
|
||
}
|
||
```
|
||
|
||
规则:
|
||
- `type=group` 时父节点必须为 `store`
|
||
- 违反层级约束返回 400
|
||
|
||
## 6.2 员工列表查询
|
||
|
||
`POST /api/org/staff/query/`
|
||
|
||
```json
|
||
{
|
||
"keyword": "张三",
|
||
"filters": {
|
||
"org_unit_ids": ["uuid"],
|
||
"status": ["active", "probation"],
|
||
"job_category": ["置业顾问"]
|
||
},
|
||
"sort": {"field": "updated_at", "order": "desc"},
|
||
"pagination": {"mode": "keyset", "cursor": "opaque_cursor", "limit": 20}
|
||
}
|
||
```
|
||
|
||
## 6.3 办理入职并创建账号
|
||
|
||
`POST /api/org/staff/onboard/`
|
||
|
||
```json
|
||
{
|
||
"name": "李雷",
|
||
"phone": "13800000000",
|
||
"org_unit_id": "uuid",
|
||
"job_title": "高级业务员",
|
||
"job_category": "置业顾问",
|
||
"role_codes": ["agent"],
|
||
"supervisor_id": "uuid"
|
||
}
|
||
```
|
||
|
||
成功 `201`:
|
||
|
||
```json
|
||
{
|
||
"staff_id": "uuid",
|
||
"user_id": 12345,
|
||
"initial_password_sent": true,
|
||
"message": "入职办理成功"
|
||
}
|
||
```
|
||
|
||
联动要求:
|
||
- 创建 `staff`
|
||
- 创建/绑定 `auth_user`
|
||
- 写入一条 `staff_transfer_logs(transfer_type='onboard')`
|
||
|
||
## 6.4 查看员工手机号(审计)
|
||
|
||
`POST /api/org/staff/{id}/phone/view/`
|
||
|
||
```json
|
||
{
|
||
"reason": "入职资料核对"
|
||
}
|
||
```
|
||
|
||
规则:
|
||
- 必须有 `hr.staff.phone.view.allow`
|
||
- 返回明文仅本次有效
|
||
- 必须写入审计日志(操作者、时间、原因)
|
||
|
||
---
|
||
|
||
## 七、HTMX 交互约定
|
||
|
||
## 7.1 Header 约定
|
||
|
||
- 请求:`HX-Request: true`
|
||
- 成功:`HX-Trigger: {"toast":{"level":"success","message":"操作成功"}}`
|
||
- 失败:`HX-Trigger: {"toast":{"level":"error","message":"操作失败"}}`
|
||
- 跳转:`HX-Redirect: /org/staff/{id}/`
|
||
|
||
## 7.2 模板分片命名
|
||
|
||
| 场景 | 模板 |
|
||
|---|---|
|
||
| 组织树 | `templates/org/fragments/org_tree.html` |
|
||
| 员工列表 | `templates/org/fragments/staff_table.html` |
|
||
| 员工详情-异动记录 | `templates/org/fragments/staff_transfer_logs.html` |
|
||
| 部门人员选择器 | `templates/org/fragments/department_staff_selector.html` |
|
||
|
||
---
|
||
|
||
## 八、权限与数据范围
|
||
|
||
## 8.1 最小权限矩阵(P0 建议)
|
||
|
||
| 能力 | 权限 code |
|
||
|---|---|
|
||
| 查看组织结构范围 | `hr.org.view.scope` |
|
||
| 编辑组织结构 | `hr.org.edit.allow` |
|
||
| 查看员工列表范围 | `hr.staff.view.scope` |
|
||
| 新建员工 | `hr.staff.create.allow` |
|
||
| 编辑员工 | `hr.staff.edit.allow` |
|
||
| 账号冻结/恢复 | `hr.staff.account.manage.allow` |
|
||
| 查看员工手机号 | `hr.staff.phone.view.allow` |
|
||
|
||
## 8.2 数据范围叠加
|
||
|
||
最终可见数据 = `scope 过滤` ∩ `员工状态过滤` ∩ `组织层级过滤`
|
||
|
||
- 系统管理员可走 `is_system_admin` 短路
|
||
- 普通管理员按 scope 限制组织子树
|
||
|
||
---
|
||
|
||
## 九、异步任务与缓存策略
|
||
|
||
## 9.1 Celery 任务
|
||
|
||
| 任务 | 触发时机 | 说明 |
|
||
|---|---|---|
|
||
| `org_staff_export_task` | 导出员工列表 | 异步导出 Excel |
|
||
| `org_transfer_affiliation_task` | 离职/调动后(预留) | 迁移房源/客源归属 |
|
||
| `org_transfer_summary_task` | 每日定时 | 异动统计重算 |
|
||
|
||
## 9.2 Redis Key 规范
|
||
|
||
| Key | TTL | 说明 |
|
||
|---|---|---|
|
||
| `{schema}:org:tree:{staff_id}` | 300s | 组织树缓存 |
|
||
| `{schema}:org:staff:list:{hash}` | 60s | 员工筛选缓存 |
|
||
| `{schema}:org:staff:detail:{staff_id}` | 120s | 员工详情聚合缓存 |
|
||
| `{schema}:org:selector:staff:{org_unit_id}` | 300s | 选择器缓存 |
|
||
|
||
写操作成功后必须主动失效相关 key。
|
||
|
||
---
|
||
|
||
## 十、性能与可靠性约束
|
||
|
||
1. 组织树查询基于 `path` 前缀与索引,避免递归 N+1。
|
||
2. 员工列表采用 Keyset 分页,避免深分页性能退化。
|
||
3. 员工详情按 Tab 懒加载,避免一次加载全量档案。
|
||
4. 批量操作(导入/导出/调动)需限流与幂等控制。
|
||
|
||
---
|
||
|
||
## 十一、安全与合规
|
||
|
||
1. 手机号与证件号全程加密存储,前端默认脱敏。
|
||
2. 以下动作必须写 `staff_transfer_logs`:入职、调动、离职、复职、上级变更、冻结/解冻。
|
||
3. `staff.status='resigned'` 或 `staff.status='frozen'` 时,`auth_user.is_active=False`。
|
||
4. 恢复在职后才可重新启用账号。
|
||
|
||
---
|
||
|
||
## 十二、错误码建议
|
||
|
||
| code | HTTP | 场景 |
|
||
|---|---|---|
|
||
| `ORG_UNIT_NOT_FOUND` | 404 | 部门不存在或无权限 |
|
||
| `ORG_INVALID_HIERARCHY` | 400 | 组织层级违反规则(如店组不挂门店) |
|
||
| `STAFF_NOT_FOUND` | 404 | 员工不存在或无权限 |
|
||
| `STAFF_PHONE_DUPLICATED` | 409 | 手机号冲突 |
|
||
| `STAFF_ACCOUNT_CREATE_FAILED` | 500 | 账号创建失败 |
|
||
| `ORG_PERMISSION_DENIED` | 403 | 权限不足 |
|
||
|
||
---
|
||
|
||
## 十三、测试映射
|
||
|
||
### 13.1 P0 User Story 映射
|
||
|
||
| User Story | 最低覆盖 |
|
||
|---|---|
|
||
| US-ORG-001 | 组织树查询、新增/编辑部门、层级约束校验 |
|
||
| US-ORG-002 | 员工列表筛选、详情加载、权限过滤 |
|
||
| US-ORG-003 | 入职建档+账号创建、必填校验、异动日志写入 |
|
||
|
||
测试文件:`tests/integration/org/test_us_org.py`
|
||
|
||
### 13.2 强制测试约束
|
||
|
||
- 集成测试使用 `TenantClient`
|
||
- HTMX 请求携带 `HTTP_HX_REQUEST=true`
|
||
- 权限三态:200 / 403 / 302
|
||
- 外部服务(短信/邮件/Redis/Celery)全部 mock
|
||
|
||
---
|
||
|
||
## 十四、落地顺序建议
|
||
|
||
1. 先实现组织树与员工列表查询(US-ORG-001/002)
|
||
2. 再实现入职流程与账号开通(US-ORG-003)
|
||
3. 补齐审计与缓存失效机制
|
||
4. 再推进离职/调动、通讯录、奖惩等 P1/P2 能力
|
||
|
||
---
|
||
|
||
## 十五、文档同步规则
|
||
|
||
- 枚举变更:同步 `DATA_MODEL/ENUMS.md`
|
||
- 权限 code 变更:同步 `DATA_MODEL/DATA_MODEL_PERMISSION.md`
|
||
- 数据结构变更:同步 `DATA_MODEL/DATA_MODEL_ORG.md`
|
||
- API 变更:同步本文件与 `PRD/TASK.md` 对应条目
|