244 lines
21 KiB
Markdown
244 lines
21 KiB
Markdown
# 迭代1总体设计 —— 系统架构与技术选型(hr-web)
|
||
|
||
- 版本:v1.1(迭代1-总体设计;v1.1 变更:database/api/ui 三份配套文档已按 ocai 口径重写为 v2.0,表数量与端点数量同步修正)
|
||
- 状态:评审通过(主agent评审,基线冻结)
|
||
- 需求基线:`docs/00-requirement/requirement-spec.md`(SRS v2)+ `docs/00-requirement/iteration1-function-detail.md`(F01~F15)
|
||
- 应用定义:`apps/hr-web.md`;模块定义:`modules/hr-org.md`、`hr-roster.md`、`hr-flow.md`、`hr-contract.md`、`hr-report.md`、`hr-system.md`
|
||
- 开发规范:ocai 技能集 —— web-application-spec、module-development-spec、database-table-definition-spec、crud-definition-spec、dspy-file-implementation-spec
|
||
|
||
## 0. 审核退回意见响应索引
|
||
|
||
PM 退回意见要求交付件至少包含 6 项内容,落点如下:
|
||
|
||
| # | 退回意见要求 | 落点文档/章节 |
|
||
|---|---|---|
|
||
| 1 | 系统总体架构与模块划分(hr-system、staff-mgr 职责边界) | 本文 §2、§4、§6 |
|
||
| 2 | 数据库设计(核心表、字段、索引、关系、迁移策略) | `database-design.md` 全文(§4 表结构、§5 索引策略、§7 迁移策略) |
|
||
| 3 | API 设计(路径、方法、请求/响应、鉴权、错误码) | `api-design.md` 全文(§1 通用约定、§2 端点清单、§3 示例) |
|
||
| 4 | UI 设计(页面结构、核心交互、组件/路由) | `ui-design.md` 全文(§2 路由、§3 组件树、§4 交互流程) |
|
||
| 5 | 与现有 staff-mgr 员工管理模块的衔接方案 | 本文 §6 + `database-design.md` §6 数据迁移 |
|
||
| 6 | 技术可行性说明与后续开发任务拆解 | `iteration1-task-breakdown.md` 全文 |
|
||
|
||
## 1. 概述
|
||
|
||
迭代1交付「Web 版人事系统(hr-web)」组织人事底座,功能范围 F01~F15:组织架构、职位职级体系、花名册、入职/转正/调动/离职/兼岗、合同台账(不含电子签)、流程审批、权限/日志、报表、工作台、员工服务与提醒。规模规格 100 人账号。
|
||
|
||
设计原则:
|
||
1. **全栈遵循 ocai 规范**:前端 bricks 组件体系(.ui 纯 JSON 声明式页面 + dspy 驱动的 CRUD),后端 ahserver(Python/aiohttp),数据层 apppublic/sqlor,基础模块 appbase(字典/用户)+ rbac(角色权限)。
|
||
2. **配置驱动**:表结构(models/*.json)、CRUD 界面(json/*.json)、字典(appcodes)全部声明式定义,减少硬编码,支撑 SRS 的"字段/流程/表单自定义"诉求。
|
||
3. **模块宿主无关**:每个模块仅依赖基础包与自己的数据表,通过 `load_{module}()` 注册 ServerEnv,可被任意宿主应用加载(module-development-spec)。
|
||
4. **单一事实来源**:花名册(roster_employee)是全系统员工数据唯一事实来源,入转调离审批通过后统一回写花名册。
|
||
|
||
## 2. 总体架构
|
||
|
||
```
|
||
┌────────────────────────────────────────────────────────────────────┐
|
||
│ 浏览器(PC Web 为主) │
|
||
│ bricks.js 渲染引擎:.ui(JSON) 页面 + DataViewer/Tree/Form/Chart │
|
||
└──────────────────────────────┬─────────────────────────────────────┘
|
||
│ HTTP(S) session cookie(Redis 会话)
|
||
┌──────────────────────────────▼─────────────────────────────────────┐
|
||
│ nginx 反向代理(prod: 443) │
|
||
└──────────────────────────────┬─────────────────────────────────────┘
|
||
┌──────────────────────────────▼─────────────────────────────────────┐
|
||
│ ahserver 应用进程(hr-web,dev:9080) │
|
||
│ 路由:wwwroot 自动路由 /{module}/{file}.ui|.dspy|.css|.js │
|
||
│ processors: .tmpl→tmpl, .ui→bui, .dspy→dspy │
|
||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||
│ │ 业务模块(load_xxx 注册 ServerEnv) │ │
|
||
│ │ hr-org | hr-roster | hr-flow | hr-contract | hr-report | │ │
|
||
│ │ hr-system │ │
|
||
│ ├─────────────────────────────────────────────────────────────┤ │
|
||
│ │ 基础模块:appbase(users/appcodes 字典)+ rbac(角色/权限) │ │
|
||
│ │ bricks_for_python(UiWindow 等 pybricks) │ │
|
||
│ └─────────────────────────────────────────────────────────────┘ │
|
||
└──────────┬───────────────────────────────┬─────────────────────────┘
|
||
│ sqlor 连接池(sor.C/U/D/R/I/sqlExe) │ Redis session
|
||
┌──────────▼──────────┐ ┌────────▼────────┐
|
||
│ MySQL 8.0(库 hr) │ │ Redis(会话) │
|
||
│ json2ddl 生成 DDL │ └─────────────────┘
|
||
└─────────────────────┘ files/ 目录:附件存储(合同文件、头像、导入导出文件)
|
||
```
|
||
|
||
请求处理链路(以花名册列表为例):
|
||
1. 浏览器访问 `/hrroster/roster_list.ui`(由 json/roster_employee_list.json 经 xls2ui 生成的 DataViewer 页);
|
||
2. DataViewer 按 CRUD JSON 中的 `dataurl`/`new_data_url` 等调用 `/hrroster/api/xxx.dspy`;
|
||
3. ahserver 将 GET query + POST body 统一解析为 `params_kw` 注入 dspy 上下文;
|
||
4. dspy 调用 `load_hrroster()` 注册的业务函数(内部经 sqlor `sor.R` 查询,叠加 rbac 数据范围过滤与敏感字段脱敏);
|
||
5. dspy 显式 `return` JSON(列表必须 `{status:'success', data:{rows:[...], total:N}}`),bricks 渲染。
|
||
|
||
## 3. 技术选型与理由
|
||
|
||
| 层 | 选型 | 选型理由 |
|
||
|---|---|---|
|
||
| 前端 | bricks 组件体系 + .ui 纯 JSON 页面 | ocai 强制规范;CRUD JSON 自动生成列表/表单/树页面,F01~F15 中 80% 的增删改查页面零 Vue 代码;DataViewer 自带筛选(data_filter)、子表(subtables)、工具栏 bind |
|
||
| 声明式页面脚本 | .dspy(受控 Python) | 后端 API 与页面逻辑统一语言栈(Python);ahserver 自动解析参数、自动 JSON 序列化;禁止 import/print,天然受控 |
|
||
| 后端框架 | ahserver(aiohttp 异步) | ocai 强制规范;内置会话/RBAC/文件上传/后台任务;wwwroot 自动路由免手写路由注册;100 人规模单实例足够 |
|
||
| 数据访问 | apppublic/sqlor | 异步连接池 + 绑定参数防注入;`sor.C/U/D/R/I/sqlExe` 六元 API 覆盖全部数据操作;与 models JSON 配套 |
|
||
| 表定义 | models/*.json + json2ddl | database-table-definition-spec 标准;数据库无关的抽象类型,DDL 自动生成,保证 collate utf8mb4_unicode_ci 一致 |
|
||
| CRUD 生成 | json/*.json + xls2ui | crud-definition-spec 标准;自动产出列表页 .ui 与 add/update/delete dspy 包装 |
|
||
| 字典 | appbase appcodes/appcodes_kv | 性别/员工类型/异动类型/离职原因等枚举统一管理,CRUD 下拉直接引用(codes 段) |
|
||
| 权限 | rbac 模块 + 自研数据范围表 | rbac 管"功能权限(路径级)",hr-system 扩展"数据范围(组织维度/花名册字段维度)",满足 SRS §3.1.14 |
|
||
| 数据库 | MySQL 8.0 | 产线既有设施(staff-mgr 亦为 MySQL),sqlor DDL 模板成熟 |
|
||
| 会话 | Redis + aiohttp_session | web-application-spec 推荐;服务重启不丢登录 |
|
||
| 附件 | ahserver files/ 文件存储 | 合同附件、离职证明、导入导出文件;100 人规模本地目录足够 |
|
||
|
||
与 v1(Java/Spring Boot/JPA/JWT)口径的差异已全部废弃归档(`docs/_archive/`),不再保留双栈:鉴权由"自研 JWT Filter"改为"ahserver session + rbac",ORM 由 JPA 改为 sqlor,前端由"无"改为 bricks/dspy。
|
||
|
||
## 4. 模块划分与职责边界
|
||
|
||
### 4.1 迭代1模块清单(6 个,均挂载于 hr-web 应用)
|
||
|
||
| 模块 | 路由前缀 | 职责 | 数据表前缀 | 迭代1功能 |
|
||
|---|---|---|---|---|
|
||
| hr-org | /hrorg | 组织架构、职位职级体系、入转调离兼岗异动业务、黑名单、合同公司 | org_ | F01/F02/F04~F08 |
|
||
| hr-roster | /hrroster | 花名册(字段定义/字段值 EAV/员工主档/时间轴/工号规则)、导入导出 | roster_ | F03 |
|
||
| hr-flow | /hrflow | 表单定义、流程定义、流程实例、审批任务(待办/已办/我发起) | flow_ | F10 |
|
||
| hr-contract | /hrcontract | 合同台账、合同模板、到期提醒规则、合同审批 | contract_ | F09 |
|
||
| hr-report | /hrreport | 只读聚合报表(花名册分析/入职/转正/调岗/离职分析),无自有表 | — | F13 |
|
||
| hr-system | /hrsystem | 角色/管理员/数据范围、操作日志、工作台聚合、提醒规则、公告政策、站内消息 | sys_ | F11/F12/F14/F15 |
|
||
|
||
### 4.2 职责边界规则
|
||
|
||
1. **员工主数据唯一入口**:所有模块读写员工信息必须经 hr-roster 的注册函数(ServerEnv 暴露),禁止跨模块直接 SQL 写 roster_ 表。入转调离审批通过后由 hr-flow 回调 hr-org 的业务函数,再调用 hr-roster 回写接口更新员工状态。
|
||
2. **审批引擎与业务解耦**:hr-flow 只管"表单+流程+实例+任务"的流转,不含业务语义;业务回写通过 `biz_type + biz_id` 关联 + 流程完成回调(ServerEnv 注册的 hook 函数)实现。
|
||
3. **权限横切**:功能权限(路径级 RBAC)由 rbac 模块统一拦截(各模块 scripts/load_path.py 注册路径与角色);数据范围由 hr-system 提供 `get_data_scope(user_id)` 公共函数,hr-org/hr-roster/hr-report 在查询时强制叠加。
|
||
4. **操作日志横切**:所有写操作(sor.C/U/D)统一经 hr-system 的 `write_audit_log(...)` 记录前后值(before/after JSON),F12 查询页只读 sys_audit_log。
|
||
5. **字典统一**:任何枚举值不得硬编码在 dspy 中,一律走 appcodes(init/data.json 初始化),CRUD 下拉用 models codes 段引用。
|
||
|
||
### 4.3 hr-system 与 staff-mgr 的职责边界(重点澄清)
|
||
|
||
| 对象 | 性质 | 职责 | 状态 |
|
||
|---|---|---|---|
|
||
| **hr-system**(模块,repos/hr-system) | 迭代1 ocai 规范模块 | 系统支撑层:F11 权限管理、F12 操作日志、F14 工作台、F15 员工服务与提醒;并承载全应用的横切能力(数据范围、审计、站内消息) | 迭代1新建(复用现有空仓 hr-system.git) |
|
||
| **staff-mgr**(repos/staff-mgr) | v1 遗留 Java 模块 | 旧员工 CRUD REST 服务(/api/v1/staff/*,Spring Boot + JPA + JWT) | **已归档,冻结不再开发**;仅作迭代1数据迁移来源与参考实现(见 §6) |
|
||
|
||
边界结论:
|
||
- 迭代1上线后 staff-mgr **不再承担任何线上职责**;其员工数据一次性迁移至 hr-roster(roster_employee + roster_field_value),部门缓存迁移至 hr-org(org_unit),日志迁移/归档至 hr-system(sys_audit_log)。
|
||
- 旧模块的"员工 CRUD"职责由 **hr-roster** 承接(不是 hr-system);hr-system 只承接其"操作日志/权限"类横切职责。详见 §6 衔接方案。
|
||
- v1 应用定义 `apps/hr-system.md`(Java 后端服务)已废弃,与模块 hr-system(ocai 支撑模块)仅重名,无继承关系。
|
||
|
||
## 5. 模块结构与依赖关系
|
||
|
||
### 5.1 标准模块目录(module-development-spec)
|
||
|
||
每个业务模块仓库结构一致:
|
||
|
||
```
|
||
{module}/
|
||
├── {module}/ # Python 包
|
||
│ ├── __init__.py # 导出全部公共函数(三处注册同步点之一)
|
||
│ ├── init.py # load_{module}():ServerEnv 注册
|
||
│ └── *.py # 业务实现(service 层)
|
||
├── wwwroot/ # index.ui、menu、业务页 .ui、api/*.dspy、css/js
|
||
├── models/ # 表定义 JSON(json2ddl → mysql.ddl.sql)
|
||
├── json/ # CRUD 定义 JSON(xls2ui → 列表页 + add/update/delete dspy)
|
||
├── init/data.json # 字典与初始化数据(appcodes Format B)
|
||
├── scripts/load_path.py # RBAC 路径注册(角色:logined/admin/hr/manager/employee)
|
||
├── skill/SKILL.md # 模块 AI 说明(数据模型/端点/坑位)
|
||
├── pyproject.toml
|
||
└── README.md
|
||
```
|
||
|
||
### 5.2 依赖关系图
|
||
|
||
```
|
||
┌─────────── rbac(功能权限) ◄──── appbase(users/orgs/appcodes)
|
||
│ ▲ ▲
|
||
│ │ 路径注册/数据范围 │ 字典/用户
|
||
┌─────────┴──┐ ┌──────┴─────┐ ┌──────┴─────┐
|
||
│ hr-system │◄─────│ hr-flow │◄──────────│ hr-contract│
|
||
│ F11/12/14/15│ 审批回调/权限 │ 合同审批 │
|
||
└─────▲──────┘ └──────▲─────┘ └──────┬─────┘
|
||
│ 审计/数据范围 │ 入转调离审批 │ 员工/公司引用
|
||
│ │ │
|
||
┌─────┴──────┐ ┌──────┴─────┐ │
|
||
│ hr-report │─────►│ hr-org │◄─────────────────┘
|
||
│ (只读聚合) │ 异动数据 │ 组织/职位/异动 │
|
||
└─────▲──────┘ └──────┬─────┘
|
||
│ 花名册分析 │ 员工主数据读写(唯一入口)
|
||
│ ┌──────▼─────┐
|
||
└─────────────│ hr-roster │
|
||
│ 员工唯一事实源│
|
||
└────────────┘
|
||
依赖方向:箭头指向被依赖方。hr-report 只读,不被任何模块依赖。
|
||
```
|
||
|
||
依赖矩阵(行依赖列):
|
||
|
||
| ↓依赖→ | hr-org | hr-roster | hr-flow | hr-contract | hr-report | hr-system |
|
||
|---|---|---|---|---|---|---|
|
||
| hr-org | — | 回写花名册/黑名单 | 异动审批流转 | — | — | 权限/日志 |
|
||
| hr-roster | 组织/职位主数据 | — | 自助修改审核 | — | — | 字段级权限/脱敏/日志 |
|
||
| hr-flow | — | 表单引用字段/完成回调 | — | — | — | 审批角色权限/日志 |
|
||
| hr-contract | 合同公司主数据 | 员工关联 | 合同审批 | — | — | 提醒通道/日志 |
|
||
| hr-report | 异动数据 | 花名册数据 | — | — | — | 数据范围 |
|
||
| hr-system | 工作台引用组织 | 工作台引用档案 | 工作台引用待办 | 工作台引用合同 | — | — |
|
||
|
||
### 5.3 应用装配与部署
|
||
|
||
- 应用入口 `app/hr_web.py`:`init()` 中依次 `ServerEnv()` → 设置 `get_module_dbname`(返回 `hr`)→ `load_appbase()` → `load_rbac()` → `load_pybricks()` → `load_hrsystem/load_hrorg/load_hrroster/load_hrflow/load_hrcontract/load_hrreport()`。
|
||
- `conf/config.json`:processors 必含 `[".tmpl","tmpl"], [".ui","bui"], [".dspy","dspy"]`;indexes 含 `index.ui`;databases 配置 hr 库;Redis session(session_max_time/session_issue_time)。
|
||
- build.sh 顺序:建 venv → pip install apppublic/sqlor/ahserver/bricks/appbase/rbac → clone 6 个业务模块 pip install → 各模块 models/ 执行 `json2ddl mysql . > mysql.ddl.sql` 并导入 → init/data.json 导入 → json/ 执行 `xls2ui` 生成 CRUD → symlink 各模块 wwwroot 与 bricks dist → systemd 服务。
|
||
- 环境:dev/test 单实例容器(nginx:8080 → ahserver:9080);prod nginx:443 反代,ahserver 不直接暴露,MySQL/Redis 仅内网;数据每日备份。100 人规模单实例即可满足"列表页 ≤500ms"指标(见 §8)。
|
||
|
||
## 6. 与现有 staff-mgr 员工管理模块的衔接方案
|
||
|
||
### 6.1 衔接定位
|
||
|
||
staff-mgr 是 v1 Java 口径下的员工 CRUD 模块(已通过端到端验证,见其 README),与 ocai 规范冲突,不改造、不并行演进,采取**一次性数据迁移 + 接口替代 + 退役**策略。
|
||
|
||
### 6.2 职责与接口替代映射
|
||
|
||
| staff-mgr 旧接口(/api/v1/staff) | 迭代1替代(hr-roster/hr-system dspy) | 说明 |
|
||
|---|---|---|
|
||
| POST /(创建员工) | /hrroster/api/roster_employee_create.dspy(或手动入职 /hrorg/api/entry_manual.dspy) | 新建一律走入职流程或花名册新增 |
|
||
| GET /(分页列表) | /hrroster/roster_list(CRUD 列表 + data_filter) | 筛选能力增强(自定义字段) |
|
||
| GET /{id}(详情) | /hrroster/employee_detail.dspy | 档案 Tabs + 时间轴 |
|
||
| PUT /{id}(更新) | /hrroster/api/roster_employee_update.dspy | 可配置自助修改 + 审核 |
|
||
| DELETE /{id}、POST /batch-delete | 离职流程 /hrorg/api/leave_manual.dspy | 迭代1不做物理/逻辑删除员工,统一走离职 |
|
||
| GET /{id}/change-logs | /hrroster/timeline.dspy | 时间轴(含迁移进来的旧变更记录) |
|
||
| GET /audit-logs | /hrsystem/audit_log_list(CRUD) | 历史日志迁入 sys_audit_log |
|
||
| GET /check/employee-no | /hrroster/api/check_employee_no.dspy | 工号查重保留 |
|
||
| GET /departments | /hrorg/api/org_tree.dspy | 部门缓存表废弃,直接查 org_unit |
|
||
|
||
脱敏规则延续并按 SRS 加强:身份证号全角色脱敏(前3后3)、手机号(前3后4)、薪资职级类字段仅 admin/hr 可见——由 hr-system 字段级权限表驱动,替代 staff-mgr 硬编码 MaskUtil。
|
||
|
||
### 6.3 数据迁移方案(详见 database-design.md §6)
|
||
|
||
- 迁移对象:staff_employee → roster_employee;staff_employee_extra → roster_field_value(学历/专业等映射到预置字段定义)+ contract_info(合同起止);staff_department_cache → org_unit;staff_change_log → roster_timeline;staff_audit_log → sys_audit_log。
|
||
- 迁移工具:hr-system 仓内 `scripts/migrate_staff_mgr.py`(Python + sqlor,幂等、按 employee_no 去重、输出核对报告)。
|
||
- 切换策略:迭代1 UAT 通过后一次性切换;切换后 staff-mgr 服务下线、仓库冻结归档(保留只读访问一个迭代周期)。
|
||
|
||
### 6.4 风险与对策
|
||
|
||
| 风险 | 对策 |
|
||
|---|---|
|
||
| 旧数据字段语义不一致(如 status 枚举) | 迁移脚本内置映射表 + 人工核对报告;UAT 期间双库比对 |
|
||
| 旧 department_id 无组织主数据 | 先迁 staff_department_cache 建 org_unit,再按 department_id 关联 |
|
||
| 身份证号加密算法不同(AesCipher) | 迁移时解密后按 hr-web 统一加密方案重新落库 |
|
||
|
||
## 7. 安全设计
|
||
|
||
1. **鉴权**:ahserver session(Redis)+ rbac 角色。迭代1角色:`admin`(系统管理员)、`hr`(人事)、`manager`(部门经理)、`employee`(员工自助);所有路径经 scripts/load_path.py 注册,未注册路径默认拒绝。
|
||
2. **数据范围**(F11):sys_role_data_scope 支持两类范围——组织维度(org_id 集合,含子树)、花名册字段维度(field_id 可见集合)。查询层强制拼接范围条件;报表同样受控。
|
||
3. **脱敏**:敏感字段(身份证/手机/薪资级)在 roster_field_def 标记 sensitive,dspy 返回前按角色脱敏。
|
||
4. **审计**(F12):全量写操作记录前后值 JSON、操作人、IP;审批、导入导出同样记录。
|
||
5. **SQL 注入**:全部经 sqlor 绑定参数;dspy 禁止拼接用户输入(sqlExe 使用 `${var}$` 占位)。
|
||
|
||
## 8. 非功能设计响应
|
||
|
||
| SRS 非功能项 | 设计响应 |
|
||
|---|---|
|
||
| 列表响应 ≤500ms | 100 人规模 + 索引设计(database-design.md §5);列表页分页默认 50;组织树一次性加载(≤200 节点) |
|
||
| 批量导入导出 100 人量级 | openpyxl 同步处理(ahserver 已内置),导入走后台任务 + 结果文件下载 |
|
||
| 字段/流程/表单自定义 | roster_field_def(EAV)、flow_form_def/flow_def JSON 化配置,无硬编码 |
|
||
| PC 为主 + 员工自助移动端兼容 | 员工自助页面(个人档案/发起审批/待办)使用 ResponsableBox 自适应布局 |
|
||
|
||
## 9. 配套设计文档
|
||
|
||
- `database-design.md`(v2.0):ER、43 张表结构、索引、字典、models JSON 示例、staff-mgr 迁移策略
|
||
- `api-design.md`(v2.0):dspy 端点清单(约 100 个)、请求/响应、鉴权角色、错误码
|
||
- `ui-design.md`(v2.0):页面结构、组件树、路由表、核心交互流程
|
||
- `iteration1-task-breakdown.md`(v1.0):可行性分析、风险、开发任务拆解(T01~T24)
|