hr-system/docs/01-design/database-design.md

479 lines
39 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 一期批次1总体设计 —— 数据库设计hr-webocai 口径)
- 版本v3.0批次1设计定稿v3.0 变更:对齐 SRS v3.1 —— 库名 hrs/MariaDB、新增编制(F16)/项目式(F17) 5 张表与 sys_openapp 预留表43→**49** 张、roster_employee 证件字段扩展D1 手工录入、org_entry 登记链接/二维码字段扫码入职降级、org_leave_handover 钉钉交接占位、字典扩充)
- 状态:**设计定稿批次1评审修订版**
- 需求基线:`docs/00-requirement/requirement-spec.md`SRS v3.1+ `iteration1-function-detail.md`F01~F15+ approved-features.mdFEAT-B1-01~12
- 架构基线:`docs/01-design/architecture.md`
- 规范依据ocai 技能集 database-table-definition-specmodels/*.json + json2ddl、crud-definition-spec、sqlor-database-module
## 1. 设计总则
1. **表定义声明式管理**:所有表以 `models/{table}.json`summary/fields/indexes/codes 四段定义build.sh 中 `json2ddl mysql . > mysql.ddl.sql` 生成 DDL 导入 **hrs** 库。本文 DDL 为生成物等价示例,开发以 models JSON 为准。
2. **统一约定**InnoDButf8mb4 / utf8mb4_unicode_ci数据库 MariaDB库名 `hrs`;主键 `id VARCHAR(32)`getID() 雪花串sqlor 惯例);公共审计列 `created_by/updated_by/created_at/updated_at``sor.C` 不自动填时间戳,写入时显式 `created_at=curDateString()`
3. **逻辑删除策略**:员工与组织**不做物理删除**,统一走"停用(status=inactive)/离职流程";物理删除仅 admin 可执行且必须留审计SRS §5.2-2
4. **自定义字段EAV**花名册字段F03与组织字段F01采用"字段定义表 + 字段值表"EAV 方案;固定高频查询字段(组织、职位、状态、入离职日期)冗余在员工主表保证列表性能。
5. **敏感字段**:身份证号密文列 + 哈希列(确定性哈希,供唯一性与黑名单匹配),手机号明文存储、按角色脱敏展示;`roster_field_def.sensitive` 驱动字段级脱敏与可见性SRS §2.2-4
6. **库划分**:单库 `hrs`,表前缀区分模块:`org_`(hr-org)、`roster_`(hr-roster)、`flow_`(hr-flow)、`contract_`(hr-contract)、`sys_`(hr-system)appbaseusers/orgs/appcodes与 rbac 表沿用基础包自带结构,不在本文重复。
7. **预留字段约定**:二期对接预留字段一律带明确默认值与字典枚举位(如 esign_status='not_enabled'、id_source='manual'、send_channel='site'),不参与一期业务分支。
## 2. ER 图(实体关系描述)
```
┌──────────────┐
│ appbase │
│ users/orgs/ │◄── 登录用户、字典 appcodes
│ appcodes │
└──────┬───────┘
│ user_id 引用
┌─────────────┐ parent_id │ ┌──────────────────┐
│ org_unit │◄─自引用──────┼──────│ sys_data_scope │ 组织维度数据范围
│ 组织 │ │ │ sys_audit_log │ 操作日志(F12)
└──┬───┬──────┘ │ │ sys_message │ 站内消息(F15)
│ │org_field_value │ │ sys_remind_rule │ 提醒规则
│ ▼ │ │ sys_openapp │ API开放预留
│ org_field_def │ └────────▲─────────┘
│ │ │ 推送
│ ┌─────────────┐ ┌─────▼───────────────┴───┐
│ │org_job/ │ │ roster_employee │ 员工唯一事实源(F03)
│ │org_position◄─┼──│ org_id/position_id/ │
│ │org_sequence/ │ │ employee_status/证件字段组│
│ │org_grade_* │ └──┬──────┬──────┬─────────┘
│ └─────────────┘ │ │ │
│ │ │ ▼
│ roster_field_def◄────┘ roster_field_value(EAV)
│ roster_field_group roster_timeline(时间轴)
│ │
│ 异动业务(hr-org) │ 合同(hr-contract)
│ org_entry ──────────┤ contract_info ── employee_id
│ org_regularization ─┼──► contract_type / contract_template
│ org_transfer(+detail)│ contract_remind_rule
│ org_leave(+handover) │ │审批
│ org_concurrent_post │ ▼
│ org_headcount_scheme │ ┌──────────────┐
│ └ headcount_item │ │ flow_def │ 流程定义
│ └ headcount_snapshot│ │ flow_form_def│ 表单定义(引用花名册字段)
│ org_project(F17) │ │ flow_node_def│ 节点(审批人/字段权限)
│ └ org_project_post │ │ flow_role │ 审批角色
│ │审批流转 │ │ flow_instance│ 实例 ──► flow_task(待办)
└────────┴──────────────┴────►│ │ └► flow_op_log
biz_type+biz_id └──────────────┘
hr-report 无自有表:只读聚合 org_/roster_/flow_ 表,叠加 sys_data_scope 范围过滤。
```
核心关系说明:
- `roster_employee` 为全系统员工数据唯一事实源;`org_entry/regularization/transfer/leave/concurrent_post` 通过 `employee_id`(入职审批阶段可为空)关联,审批通过后经 hr-roster `roster_writeback` 更新主档。
- 所有审批类记录通过 `flow_instance_id` 关联流程实例;`flow_instance.biz_type + biz_id` 反向定位业务单据,流程完成后回调业务模块回写。
- **编制F16**`org_headcount_scheme` 1─N `headcount_item`(细分额度);`headcount_snapshot` 按日快照历史;员工异动回写后同步触发占编重算。占编范围条件以 JSON 表达(员工类型/状态/职位/职级/自定义字段),与 roster_employee + roster_field_value 联查计数。
- **项目式组织F17**`org_project` 为横向组织(自引用多层级),员工经 `org_project_post` 同时挂纵向部门roster_employee.org_id与横向项目横向职务体系复用 org_job 表org_job.job_category=horizontal 区分,被花名册/薪酬引用)。
- 所有表与 appbase.users 之间为逻辑外键operator_id/leader_id/approver_id不建物理外键sqlor 惯例,跨模块解耦)。
## 3. models JSON 示例规范格式json2ddl 输入)
`hr-roster/models/roster_employee.json`(含 D1 证件字段组扩展):
```json
{
"summary": [
{"name": "roster_employee", "title": "员工主档", "pk": ["id"], "charset": "utf8mb4", "collate": "utf8mb4_unicode_ci"}
],
"fields": [
{"name": "id", "title": "主键", "type": "varchar", "length": 32},
{"name": "employee_no", "title": "工号", "type": "varchar", "length": 32},
{"name": "name", "title": "姓名", "type": "varchar", "length": 64},
{"name": "gender", "title": "性别", "type": "varchar", "length": 16},
{"name": "id_type", "title": "证件类型", "type": "varchar", "length": 16, "default": "id_card"},
{"name": "id_number_cipher", "title": "证件号密文", "type": "varchar", "length": 256},
{"name": "id_number_hash", "title": "证件号哈希", "type": "varchar", "length": 64},
{"name": "id_valid_from", "title": "证件有效期起", "type": "date"},
{"name": "id_valid_to", "title": "证件有效期止(长期=9999-12-31)", "type": "date"},
{"name": "id_authority", "title": "证件签发机关", "type": "varchar", "length": 128},
{"name": "id_source", "title": "证件信息来源", "type": "varchar", "length": 16, "default": "manual"},
{"name": "birthday", "title": "出生日期", "type": "date"},
{"name": "phone", "title": "手机号", "type": "varchar", "length": 20},
{"name": "email", "title": "邮箱", "type": "varchar", "length": 128},
{"name": "org_id", "title": "组织", "type": "varchar", "length": 32},
{"name": "position_id", "title": "职位", "type": "varchar", "length": 32},
{"name": "job_id", "title": "职务", "type": "varchar", "length": 32},
{"name": "grade_level_id", "title": "职级", "type": "varchar", "length": 32},
{"name": "grade_rank_id", "title": "职等", "type": "varchar", "length": 32},
{"name": "sequence_id", "title": "序列", "type": "varchar", "length": 32},
{"name": "employee_type", "title": "员工类型", "type": "varchar", "length": 16},
{"name": "employee_status", "title": "员工状态", "type": "varchar", "length": 16},
{"name": "hire_date", "title": "入职日期", "type": "date"},
{"name": "probation_end_date", "title": "试用期止(预计转正日)", "type": "date"},
{"name": "regular_date", "title": "转正日期", "type": "date"},
{"name": "leave_date", "title": "离职日期", "type": "date"},
{"name": "company_id", "title": "合同公司", "type": "varchar", "length": 32},
{"name": "work_location", "title": "办公地点", "type": "varchar", "length": 128},
{"name": "direct_leader_id", "title": "直属主管", "type": "varchar", "length": 32},
{"name": "prev_employee_id", "title": "复职关联原档案ID", "type": "varchar", "length": 32},
{"name": "remark", "title": "备注", "type": "text"},
{"name": "created_by", "title": "创建人", "type": "varchar", "length": 32},
{"name": "updated_by", "title": "更新人", "type": "varchar", "length": 32},
{"name": "created_at", "title": "创建时间", "type": "timestamp"},
{"name": "updated_at", "title": "更新时间", "type": "timestamp"}
],
"indexes": [
{"name": "uk_employee_no", "unique": true, "fields": ["employee_no"]},
{"name": "uk_id_number_hash", "unique": true, "fields": ["id_number_hash"]},
{"name": "idx_org_id", "fields": ["org_id"]},
{"name": "idx_status", "fields": ["employee_status"]},
{"name": "idx_hire_date", "fields": ["hire_date"]},
{"name": "idx_probation_end", "fields": ["probation_end_date"]},
{"name": "idx_direct_leader", "fields": ["direct_leader_id"]},
{"name": "idx_prev_employee", "fields": ["prev_employee_id"]}
],
"codes": [
{"name": "gender", "source": "appcodes", "code": "GENDER"},
{"name": "employee_type", "source": "appcodes", "code": "EMP_TYPE"},
{"name": "employee_status", "source": "appcodes", "code": "EMP_STATUS"},
{"name": "id_source", "source": "appcodes", "code": "ID_SOURCE"}
]
}
```
CRUD 侧配套 `hr-roster/json/roster_employee_list.json`crud-definition-spec`tblname=roster_employee``params` 顶层配置 `new_data_url/update_data_url/delete_data_url` 指向 `wwwroot/api/roster_employee_{create,update,delete}.dspy``data_filter` 声明姓名/工号/组织/状态筛选项,`browserfields.alters` 引用 GENDER/EMP_TYPE/EMP_STATUS 字典。
## 4. 表结构设计49 张业务表)
> 公共列约定:凡标【审计】的表均含 `created_by/updated_by/created_at/updated_at`;日志/快照类表仅含 `created_at`。DDL 示例见 §5。
> v3.0 变化hr-org 18→23+编制 3 表+项目式 2 表、hr-system 7→8+sys_openapp 预留、roster_employee/org_entry/org_leave_handover 字段扩展;合计 **49 张**。
### 4.1 hr-org 组织人事23 张,前缀 org_
| # | 表 | 用途 | 关键字段 | 关键索引 |
|---|---|---|---|---|
| 1 | org_unit | 组织F01 | id, org_code, org_name, parent_id(自引用), org_type(字典ORG_TYPE), leader_id, effective_date, expire_date(空=长期), status(active/inactive), sort_no【审计】 | uk_org_code; idx_parent_id; idx_status |
| 2 | org_unit_change | 组织变更时间轴F01 | id, org_id, change_type(add/edit/disable/move/split/merge/delete), before_json, after_json, effective_date, operator_id, flow_instance_id, created_at | idx_org_id; idx_created_at |
| 3 | org_field_def | 组织自定义字段F01 | id, field_code, field_name, field_type(text/number/date/select/file), options_json, required, sort_no, status【审计】 | uk_field_code |
| 4 | org_field_value | 组织字段值 EAV | id, org_id, field_id, value_text, value_date, value_file, updated_at | uk(org_id,field_id) |
| 5 | org_job | 职务F02/F17 | id, job_code, job_name, job_category(vertical/horizontalF17 横向职务), status, sort_no, remark【审计】 | uk_job_code; idx_job_category |
| 6 | org_position | 职位F02 | id, position_code, position_name, job_id, sequence_id, category(字典POS_CATEGORY), dept_ids(多值,逗号分隔), description, status, sort_no【审计】 | uk_position_code; idx_job_id |
| 7 | org_sequence | 序列F02 | id, seq_category, seq_name, grade_level_ids(关联职级类别), status【审计】 | — |
| 8 | org_grade_level | 职级F02 | id, level_code, level_name, level_category, start_rank_id, end_rank_id, sort_no【审计】 | uk_level_code |
| 9 | org_grade_rank | 职等F02 | id, rank_code, rank_name, rank_level(int), sort_no【审计】 | uk_rank_code |
| 10 | org_blacklist | 黑名单F04/F07 | id, name, id_number_hash, id_number_cipher, reason, source(leave/manual), source_employee_id, created_by, created_at | idx_id_number_hash |
| 11 | org_contract_company | 合同公司F01/F09 | id, company_code, company_name, credit_code(统一社会信用代码), legal_person, contact_info, status【审计】 | uk_company_code |
| 12 | org_entry | 入职记录F04 | id, employee_id(可空,待入职), name, id_type, id_number_cipher/hash, id_valid_from/id_valid_to/id_authority/id_source(手工录入 D1), employee_type, org_id, position_id, company_id, work_location, hire_date, entry_status(pending_approval/pending_entry/entered/cancelled/blocked), is_rehire, register_status(none/invited/filled), **register_token, register_url, qrcode_file, register_expire**(登记链接/二维码,扫码入职降级), flow_instance_id, remark【审计】 | idx_entry_status; idx_hire_date; idx_flow_instance_id; uk_register_token |
| 13 | org_regularization | 转正记录F05 | id, employee_id, probation_end_date, regular_date, apply_type(self/proxy/manual), status(pending/approved/rejected/cancelled), flow_instance_id, remark【审计】 | idx_employee_id; idx_regular_date |
| 14 | org_transfer | 调动单F06支持批量 | id, transfer_type(promotion/demotion/position_change/org_adjust), status(pending/confirmed/cancelled), effective_date, batch_no, flow_instance_id, remark【审计】 | idx_status; idx_effective_date |
| 15 | org_transfer_detail | 调动明细(逐人) | id, transfer_id, employee_id, from_org_id, to_org_id, from_position_id, to_position_id, from_grade_level_id, to_grade_level_id, status | idx_transfer_id; idx_employee_id |
| 16 | org_leave | 离职记录F07 | id, employee_id, leave_type(辞职/劝退/合同到期/退休), leave_reason(字典LEAVE_REASON), leave_date, apply_type(self/proxy/manual), status(pending_leave/left/cancelled), handover_status, certificate_file, blacklist_flag, flow_instance_id, remark【审计】 | idx_employee_id; idx_status; idx_leave_date |
| 17 | org_leave_handover | 离职交接项 | id, leave_id, item_type(approval/file/subordinate/permission/**dingtalk_resource(钉钉资源交接占位,D3)**/other), item_content, handover_to_id, status(pending/done), remark | idx_leave_id |
| 18 | org_concurrent_post | 兼岗F08 | id, employee_id, org_id, position_id, start_date, end_date, status(awaiting/active/expired), flow_instance_id【审计】 | idx_employee_id; idx_status |
| 19 | org_headcount_scheme | 编制方案F16 | id, scheme_name, cycle_type(year/quarter/month字典HEADCOUNT_CYCLE), cycle_start, cycle_end, status(active/inactive), remark【审计】 | idx_cycle(cycle_start,cycle_end); idx_status |
| 20 | headcount_item | 编制细分项F16 | id, scheme_id, scope_json(占编范围:员工类型/状态/职位/职级/自定义字段条件), seg_json(细分维度,可空=整体), head_limit(int), remark【审计】 | idx_scheme_id |
| 21 | headcount_snapshot | 编制快照F16 历史回溯/实时计数) | id, scheme_id, item_id, snap_date, used_count(int), over_count(int超编数), created_at | uk(scheme_id,item_id,snap_date); idx_snap_date |
| 22 | org_project | 项目式组织F17 | id, project_code, project_name, parent_id(自引用多层级), owner_id(负责人), start_date, end_date, project_status(active/expired/inactive), attrs_json(项目属性), sort_no【审计】 | uk_project_code; idx_parent_id; idx_project_status |
| 23 | org_project_post | 项目任职F17 员工挂横向组织) | id, project_id, employee_id, job_id(横向职务,引用 org_job job_category=horizontal), start_date, end_date, status(active/expired)【审计】 | uk(project_id,employee_id,job_id,start_date); idx_employee_id |
### 4.2 hr-roster 花名册7 张,前缀 roster_
| # | 表 | 用途 | 关键字段 | 关键索引 |
|---|---|---|---|---|
| 24 | roster_field_group | 字段分组F03 | id, group_code, group_name(工作信息/个人信息/绩效结果/培训记录), sort_no, status【审计】 | uk_group_code |
| 25 | roster_field_def | 字段定义F03 核心) | id, group_id, field_code, field_name, field_type(text/number/date/select/multiselect/file), options_json(字典code或自定义项), required, sensitive(0/1), editable_self(0/1), need_audit(0/1自助修改审核), apply_types(JSON适用员工类型), sort_no, status【审计】 | uk_field_code; idx_group_id |
| 26 | roster_employee | 员工主档F03 | 见 §3 models 示例(含证件字段组/probation_end_date/prev_employee_id | 见 §3 |
| 27 | roster_field_value | 字段值 EAV | id, employee_id, field_id, value_text, value_number(decimal(18,4)), value_date, value_file, updated_by, updated_at | uk(employee_id,field_id); idx_field_id |
| 28 | roster_empno_rule | 工号规则F03 | id, rule_name, prefix, match_field(如company_id), match_value, seq_length, current_seq(int), status【审计】 | — |
| 29 | roster_timeline | 员工时间轴F03/F12 联动) | id, employee_id, event_type(entry/regular/transfer/leave/concurrent/contract/field_change/headcount/project/migrate), event_date, title, content_json, source_type, source_id, created_at | idx_emp_date(employee_id,event_date) |
| 30 | roster_type_rule | 员工类型字段规则F03 分类管理) | id, employee_type, field_rules_json(按类型必填/隐藏字段), status【审计】 | uk(employee_type) |
### 4.3 hr-flow 流程审批7 张,前缀 flow_
| # | 表 | 用途 | 关键字段 | 关键索引 |
|---|---|---|---|---|
| 31 | flow_form_def | 表单定义F10 | id, form_code, form_name, biz_type(entry/regular/transfer/leave/handover/concurrent/contract_sign/contract_renew/contract_change/contract_stop/roster_self_edit/**attendance_\*桩位**), fields_json(引用roster_field_def或自定义项), status【审计】 | uk_form_code |
| 32 | flow_def | 流程定义F10 | id, flow_code, flow_name, form_id, biz_type, match_cond_json(员工类型/部门匹配条件), version, status【审计】 | uk_flow_code; idx_biz_type |
| 33 | flow_node_def | 流程节点 | id, flow_id, node_seq, node_name, approver_type(user/role/leader/admin/flow_role), approver_value, pass_rule(any/all), field_perm_json(节点字段可读写), cc_to(抄送) | idx_flow_id |
| 34 | flow_role | 审批角色F10 | id, role_name, flow_scope_json(可用流程), op_perm_json(操作权限), data_scope_json(数据查看范围), status【审计】 | — |
| 35 | flow_instance | 流程实例 | id, inst_no(审批编号), flow_id, form_id, biz_type, biz_id, title, initiator_id, inst_status(running/approved/rejected/cancelled), submit_data_json, start_time, finish_time | uk_inst_no; idx_biz(biz_type,biz_id); idx_initiator; idx_status |
| 36 | flow_task | 审批任务(待办/已办) | id, instance_id, node_id, node_name, approver_id, task_status(pending/approved/rejected/forwarded/cancelled), comment, handle_time, created_at | idx_approver_status(approver_id,task_status); idx_instance |
| 37 | flow_op_log | 流转日志 | id, instance_id, op_type(submit/approve/reject/forward/cancel/withdraw), operator_id, comment, created_at | idx_instance |
### 4.4 hr-contract 合同4 张,前缀 contract_
| # | 表 | 用途 | 关键字段 | 关键索引 |
|---|---|---|---|---|
| 38 | contract_type | 合同类型F09 自定义) | id, type_code, type_name(劳动合同/保密协议/竞业协议…), status【审计】 | uk_type_code |
| 39 | contract_template | 合同模板F09 | id, template_name, type_id, file_path, field_marks_json(占位符映射), status【审计】 | — |
| 40 | contract_info | 合同台账F09 | id, contract_no, employee_id, type_id, company_id, template_id, start_date, end_date, sign_date, contract_status(active/expired/stopped/renewing), file_path, text_file_path(模板生成合同文本,D4), flow_instance_id, remind_rule_id, esign_status(not_enabled/待二期流转,D4), remark【审计】 | idx_employee; idx_end_date; idx_status |
| 41 | contract_remind_rule | 到期提醒规则F09 | id, rule_name, days_before(int), target_role(self/leader/hr), content_template, status【审计】 | — |
### 4.5 hr-system 权限日志与通用服务8 张,前缀 sys_
| # | 表 | 用途 | 关键字段 | 关键索引 |
|---|---|---|---|---|
| 42 | sys_data_scope | 数据范围F11 | id, admin_user_id(管理员), scope_type(org/field), org_ids_json(组织维度,含子树), field_ids_json(花名册字段维度), remark【审计】 | idx_admin_user |
| 43 | sys_audit_log | 操作日志F12只增不改 | id, module, target_type, target_id, operation(create/update/delete/import/export/approve/login/migrate), operator_id, operator_name, before_json, after_json, request_ip, created_at | idx_target(target_type,target_id); idx_operator; idx_created_at; idx_module_op(module,operation) |
| 44 | sys_message | 站内消息F15 | id, receiver_id, msg_type(todo/approval/remind/care/notice), title, content, biz_type, biz_id, send_channel(site; sms/email预留 D9), is_read, created_at | idx_receiver_read(receiver_id,is_read); idx_created_at |
| 45 | sys_remind_rule | 通用提醒规则F15 多场景) | id, scene_code(entry/regular/leave/retire/contract_expire/health_cert/social_insurance/custom), rule_name, days_before, content_template, target_role(admin/employee/leader), status【审计】 | uk(scene_code,rule_name) |
| 46 | sys_announcement | 企业公告F15 | id, title, content, publish_scope_json, publish_time, status(draft/published/offline), created_by【审计】 | idx_status |
| 47 | sys_policy | 企业政策F15 | id, title, file_path, view_scope_json, downloadable(0/1), publish_time, status, created_by【审计】 | idx_status |
| 48 | sys_care_config | 关怀配置F15 生日/周年) | id, care_type(birthday/anniversary), template, enable(0/1), push_target(employee/leader), updated_by, updated_at | uk_care_type |
| 49 | sys_openapp | API 开放应用一期预留FEAT-B1-01⑦ | id, app_key, app_secret_cipher, app_name, scope_json(授权资源), rate_limit(int), status(active/inactive)【审计】 | uk_app_key |
## 5. 核心表 DDL 示例json2ddl 生成等价物)
```sql
-- roster_employee完整字段见 §3 models JSON此处为等价 DDL 含证件字段组)
CREATE TABLE roster_employee (
id VARCHAR(32) NOT NULL COMMENT '主键',
employee_no VARCHAR(32) NOT NULL COMMENT '工号',
name VARCHAR(64) NOT NULL COMMENT '姓名',
gender VARCHAR(16) DEFAULT NULL COMMENT '性别(字典GENDER)',
id_type VARCHAR(16) DEFAULT 'id_card' COMMENT '证件类型',
id_number_cipher VARCHAR(256) DEFAULT NULL COMMENT '证件号密文',
id_number_hash VARCHAR(64) DEFAULT NULL COMMENT '证件号哈希(唯一)',
id_valid_from DATE DEFAULT NULL COMMENT '证件有效期起',
id_valid_to DATE DEFAULT NULL COMMENT '证件有效期止(长期=9999-12-31)',
id_authority VARCHAR(128) DEFAULT NULL COMMENT '证件签发机关',
id_source VARCHAR(16) DEFAULT 'manual' COMMENT '证件来源(字典ID_SOURCE: manual/card_reader预留)',
birthday DATE DEFAULT NULL,
phone VARCHAR(20) DEFAULT NULL,
email VARCHAR(128) DEFAULT NULL,
org_id VARCHAR(32) DEFAULT NULL COMMENT '组织',
position_id VARCHAR(32) DEFAULT NULL COMMENT '职位',
job_id VARCHAR(32) DEFAULT NULL COMMENT '职务',
grade_level_id VARCHAR(32) DEFAULT NULL COMMENT '职级',
grade_rank_id VARCHAR(32) DEFAULT NULL COMMENT '职等',
sequence_id VARCHAR(32) DEFAULT NULL COMMENT '序列',
employee_type VARCHAR(16) DEFAULT NULL COMMENT '员工类型(字典EMP_TYPE)',
employee_status VARCHAR(16) DEFAULT NULL COMMENT '状态(字典EMP_STATUS)',
hire_date DATE DEFAULT NULL,
probation_end_date DATE DEFAULT NULL COMMENT '试用期止(预计转正日,转正提醒依据)',
regular_date DATE DEFAULT NULL,
leave_date DATE DEFAULT NULL,
company_id VARCHAR(32) DEFAULT NULL COMMENT '合同公司',
work_location VARCHAR(128) DEFAULT NULL,
direct_leader_id VARCHAR(32) DEFAULT NULL COMMENT '直属主管',
prev_employee_id VARCHAR(32) DEFAULT NULL COMMENT '复职关联原档案ID(F04-5)',
remark TEXT DEFAULT NULL,
created_by VARCHAR(32) DEFAULT NULL, updated_by VARCHAR(32) DEFAULT NULL,
created_at TIMESTAMP NULL DEFAULT NULL, updated_at TIMESTAMP NULL DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_employee_no (employee_no),
UNIQUE KEY uk_id_number_hash (id_number_hash),
KEY idx_org_id (org_id),
KEY idx_status (employee_status),
KEY idx_hire_date (hire_date),
KEY idx_probation_end (probation_end_date),
KEY idx_direct_leader (direct_leader_id),
KEY idx_prev_employee (prev_employee_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='员工主档';
CREATE TABLE org_unit (
id VARCHAR(32) NOT NULL,
org_code VARCHAR(32) NOT NULL COMMENT '组织编码',
org_name VARCHAR(128) NOT NULL COMMENT '组织名称',
parent_id VARCHAR(32) DEFAULT NULL COMMENT '上级组织',
org_type VARCHAR(16) DEFAULT NULL COMMENT '组织类型(字典ORG_TYPE)',
leader_id VARCHAR(32) DEFAULT NULL COMMENT '负责人',
effective_date DATE DEFAULT NULL COMMENT '生效日期',
expire_date DATE DEFAULT NULL COMMENT '失效日期(空=长期)',
status VARCHAR(16) NOT NULL DEFAULT 'active' COMMENT 'active/inactive',
sort_no INT DEFAULT 0,
remark TEXT DEFAULT NULL,
created_by VARCHAR(32) DEFAULT NULL, updated_by VARCHAR(32) DEFAULT NULL,
created_at TIMESTAMP NULL DEFAULT NULL, updated_at TIMESTAMP NULL DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_org_code (org_code),
KEY idx_parent_id (parent_id),
KEY idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='组织';
CREATE TABLE flow_instance (
id VARCHAR(32) NOT NULL,
inst_no VARCHAR(32) NOT NULL COMMENT '审批编号',
flow_id VARCHAR(32) NOT NULL, form_id VARCHAR(32) NOT NULL,
biz_type VARCHAR(32) NOT NULL COMMENT '业务类型(字典FLOW_BIZ_TYPE)',
biz_id VARCHAR(32) DEFAULT NULL COMMENT '业务单据ID',
title VARCHAR(256) DEFAULT NULL,
initiator_id VARCHAR(32) NOT NULL,
inst_status VARCHAR(16) NOT NULL DEFAULT 'running' COMMENT 'running/approved/rejected/cancelled',
submit_data_json TEXT DEFAULT NULL COMMENT '提交表单数据快照',
start_time TIMESTAMP NULL DEFAULT NULL, finish_time TIMESTAMP NULL DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_inst_no (inst_no),
KEY idx_biz (biz_type, biz_id),
KEY idx_initiator (initiator_id),
KEY idx_status (inst_status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='流程实例';
CREATE TABLE sys_audit_log (
id VARCHAR(32) NOT NULL,
module VARCHAR(32) NOT NULL COMMENT 'hr-org/hr-roster/...',
target_type VARCHAR(32) NOT NULL COMMENT 'roster_employee/org_unit/...',
target_id VARCHAR(32) NOT NULL,
operation VARCHAR(16) NOT NULL COMMENT 'create/update/delete/import/export/approve/login/migrate',
operator_id VARCHAR(32) NOT NULL, operator_name VARCHAR(64) DEFAULT '',
before_json TEXT DEFAULT NULL COMMENT '变更前快照',
after_json TEXT DEFAULT NULL COMMENT '变更后快照',
request_ip VARCHAR(64) DEFAULT NULL,
created_at TIMESTAMP NULL DEFAULT NULL,
PRIMARY KEY (id),
KEY idx_target (target_type, target_id),
KEY idx_operator (operator_id),
KEY idx_created_at (created_at),
KEY idx_module_op (module, operation)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='操作日志(只增)';
-- F16 编制管理(新增)
CREATE TABLE org_headcount_scheme (
id VARCHAR(32) NOT NULL,
scheme_name VARCHAR(128) NOT NULL COMMENT '方案名称',
cycle_type VARCHAR(16) NOT NULL COMMENT '周期(字典HEADCOUNT_CYCLE: year/quarter/month)',
cycle_start DATE NOT NULL, cycle_end DATE NOT NULL,
status VARCHAR(16) NOT NULL DEFAULT 'active',
remark TEXT DEFAULT NULL,
created_by VARCHAR(32) DEFAULT NULL, updated_by VARCHAR(32) DEFAULT NULL,
created_at TIMESTAMP NULL DEFAULT NULL, updated_at TIMESTAMP NULL DEFAULT NULL,
PRIMARY KEY (id),
KEY idx_cycle (cycle_start, cycle_end),
KEY idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='编制方案';
CREATE TABLE headcount_item (
id VARCHAR(32) NOT NULL,
scheme_id VARCHAR(32) NOT NULL COMMENT '编制方案',
scope_json TEXT NOT NULL COMMENT '占编范围条件(员工类型/状态/职位/职级/自定义字段)',
seg_json TEXT DEFAULT NULL COMMENT '细分维度(空=整体)',
head_limit INT NOT NULL DEFAULT 0 COMMENT '编制数量',
remark TEXT DEFAULT NULL,
created_by VARCHAR(32) DEFAULT NULL, updated_by VARCHAR(32) DEFAULT NULL,
created_at TIMESTAMP NULL DEFAULT NULL, updated_at TIMESTAMP NULL DEFAULT NULL,
PRIMARY KEY (id),
KEY idx_scheme_id (scheme_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='编制细分项';
CREATE TABLE headcount_snapshot (
id VARCHAR(32) NOT NULL,
scheme_id VARCHAR(32) NOT NULL,
item_id VARCHAR(32) NOT NULL COMMENT '细分项',
snap_date DATE NOT NULL COMMENT '快照日期',
used_count INT NOT NULL DEFAULT 0 COMMENT '占编人数',
over_count INT NOT NULL DEFAULT 0 COMMENT '超编数(0=未超编)',
created_at TIMESTAMP NULL DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_scheme_item_date (scheme_id, item_id, snap_date),
KEY idx_snap_date (snap_date)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='编制快照(历史回溯)';
-- F17 项目式组织(新增)
CREATE TABLE org_project (
id VARCHAR(32) NOT NULL,
project_code VARCHAR(32) NOT NULL COMMENT '项目编码',
project_name VARCHAR(128) NOT NULL COMMENT '项目名称',
parent_id VARCHAR(32) DEFAULT NULL COMMENT '上级项目(多层级)',
owner_id VARCHAR(32) DEFAULT NULL COMMENT '负责人',
start_date DATE DEFAULT NULL, end_date DATE DEFAULT NULL COMMENT '时间范围',
project_status VARCHAR(16) NOT NULL DEFAULT 'active' COMMENT 'active/expired/inactive(到期自动标识)',
attrs_json TEXT DEFAULT NULL COMMENT '项目属性',
sort_no INT DEFAULT 0,
created_by VARCHAR(32) DEFAULT NULL, updated_by VARCHAR(32) DEFAULT NULL,
created_at TIMESTAMP NULL DEFAULT NULL, updated_at TIMESTAMP NULL DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_project_code (project_code),
KEY idx_parent_id (parent_id),
KEY idx_project_status (project_status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='项目式组织';
CREATE TABLE org_project_post (
id VARCHAR(32) NOT NULL,
project_id VARCHAR(32) NOT NULL COMMENT '项目组织',
employee_id VARCHAR(32) NOT NULL COMMENT '员工',
job_id VARCHAR(32) DEFAULT NULL COMMENT '横向职务(org_job.job_category=horizontal)',
start_date DATE DEFAULT NULL, end_date DATE DEFAULT NULL,
status VARCHAR(16) NOT NULL DEFAULT 'active' COMMENT 'active/expired',
created_by VARCHAR(32) DEFAULT NULL, updated_by VARCHAR(32) DEFAULT NULL,
created_at TIMESTAMP NULL DEFAULT NULL, updated_at TIMESTAMP NULL DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_proj_emp_job (project_id, employee_id, job_id, start_date),
KEY idx_employee_id (employee_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='项目任职(横向)';
```
## 6. 索引与性能策略
1. 100 人规模全表扫描亦可满足 ≤500ms但仍按查询模式建索引列表筛选列status/org_id/hire_date/end_date、EAV 值表 uk(employee_id,field_id)、待办 idx(approver_id,task_status)、转正提醒 idx(probation_end_date)。
2. 组织树、职位职级主数据一次性全量加载≤200/≤500 行),前端缓存,无需分页。
3. EAV 查询模式:花名册列表先按主表条件分页取员工,再批量 `IN` 查值表拼装(每页 ≤50 人 × 显示字段数),避免 EAV 行转列大 JOIN。
4. 报表hr-report直接 SQL 聚合sor.sqlExe 只读100 人规模不建物化视图,满足"延迟 ≤5 分钟"实为实时sys_audit_log 按月保留,冷数据归档不删除。
5. 编制计数headcount_snapshot 以"异动事件触发 + 每日兜底扫描"双机制维护;实时查询取最新 snap_date历史回溯按 snap_date 精确取数F16 验收③)。
6. 唯一性:工号、组织编码、项目编码、职位/职务/职级/职等编码、审批编号、登记表 token 均唯一索引;证件号经哈希列唯一(密文为非确定性加密,哈希列承担唯一性与黑名单匹配)。
## 7. 数据字典appbase appcodesinit/data.json 初始化)
| code | 值 | 说明 |
|---|---|---|
| GENDER | male/female/unknown | 性别 |
| ORG_TYPE | group/subsidiary/dept/team | 组织类型 |
| EMP_TYPE | formal/temp/dispatch/intern | 员工类型(正式/临时/派遣/实习) |
| EMP_STATUS | pending_entry/probation/regular/pending_leave/left | 员工状态(待入职/试用/正式/待离职/离职) |
| ID_TYPE | id_card/passport/other | 证件类型 |
| ID_SOURCE | manual/card_reader(预留,二期) | 证件信息来源D1 |
| POS_CATEGORY | management/professional/skill | 职位类别 |
| JOB_CATEGORY | vertical/horizontal | 职务类别F17 横向职务) |
| TRANSFER_TYPE | promotion/demotion/position_change/org_adjust | 调动类型 |
| LEAVE_REASON | personal/company/contract_expire/retire/other | 离职原因 |
| HANDOVER_TYPE | approval/file/subordinate/permission/dingtalk_resource(占位)/other | 离职交接项类型D3 |
| CONTRACT_STATUS | active/expired/stopped/renewing | 合同状态 |
| ESIGN_STATUS | not_enabled/signing/signed(后两者二期启用) | 电子签状态D4 预留) |
| FLOW_BIZ_TYPE | entry/regular/transfer/leave/handover/concurrent/contract_sign/contract_renew/contract_change/contract_stop/roster_self_edit/attendance_*(桩位) | 审批业务类型 |
| INST_STATUS | running/approved/rejected/cancelled | 实例状态 |
| REMIND_SCENE | entry/regular/leave/retire/contract_expire/health_cert/social_insurance/custom | 提醒场景≥6 类F15 验收④) |
| HEADCOUNT_CYCLE | year/quarter/month | 编制周期F16 |
| PROJECT_STATUS | active/expired/inactive | 项目状态F17到期自动 expired |
| MSG_CHANNEL | site/sms(预留)/email(预留) | 消息渠道D9 |
| AUDIT_OP | create/update/delete/import/export/approve/login/migrate | 操作类型 |
| ENTRY_STATUS | pending_approval/pending_entry/entered/cancelled/blocked | 入职状态 |
| REGISTER_STATUS | none/invited/filled | 登记表状态 |
枚举一律不硬编码于 dspyCRUD 下拉经 models `codes` 段引用。
## 8. staff-mgr 数据迁移策略v1 → 一期NFR-8
迁移工具:`repos/hr-system` 仓内规划 `scripts/migrate_staff_mgr.py`Python + sqlor幂等可重跑输出核对报告总数一致 + 抽样字段一致)。
| v1 源表staff-mgr | 目标表 | 映射规则 |
|---|---|---|
| staff_department_cache | org_unit | 按树形重建 parent_idorg_code=DEPT{id}status 映射 active/inactive |
| staff_employee | roster_employee | employee_no/name/gender/phone/email 直映id_number AES 解密后按新方案重加密cipher+hashid_source='manual',证件有效期/签发机关置空待补department_id→org_idposition 文本→预置 org_position无匹配则建"未定职"status: active→regular、probation→probation、inactive→lefthire_date 直映 |
| staff_employee_extra | roster_field_value + contract_info | education/major/school/graduation_date/previous_company/work_years → 预置 roster_field_def 字段值technical_level/salary_grade → 预置敏感字段值contract_type/start/end → contract_infotype_id 映射正式/外包/实习协议) |
| staff_change_log | roster_timeline | change_type→event_type(transfer/field_change)old/new 值入 content_jsonevent_type 前缀 `migrate:` 标识迁移来源 |
| staff_audit_log | sys_audit_log | 字段直映module 标记 `staff-mgr-legacy` |
策略与约束:
1. **顺序**org_unit → org_position预置→ roster_field_def/group预置→ roster_employee → roster_field_value → contract_info → timeline/audit。
2. **幂等**:按 employee_no/org_code 去重,重跑仅补差;证件号哈希冲突时输出冲突报告人工裁决(冲突员工按离职记录保留,不入在职)。
3. **范围**:默认"在职 + 离职保留档案"全量;仅迁在职/含历史的最终范围与切换时间点待 PM 确认SRS §11-Q3脚本参数 `--scope=active|all` 支持两种口径。
4. **切换**UAT 通过后一次性迁移+双库比对 → staff-mgr 服务下线、仓库冻结只读(保留一个迭代周期)。
5. 迁移全程写 sys_audit_logoperation=migrate迁移数据在时间轴/日志中可辨识。
## 9. 与需求验收项的映射(摘选)
| 验收项 | 数据支撑 |
|---|---|
| F01-4 时间轴留痕 | org_unit_change |
| F01-5 历史架构图 | org_unit.effective_date/expire_date + org_unit_change 回放 |
| F02-1~5 五套体系 | org_job/position/sequence/grade_level/grade_rank |
| F03-1/2 自定义字段与分组 | roster_field_def/group/value |
| F03-3 多规则工号 | roster_empno_rule |
| F03-4 自助修改审核 | roster_field_def.editable_self/need_audit + flow biz_type=roster_self_edit |
| F03-6 类型差异规则 | roster_type_rule + apply_types |
| F04-4 身份证手工录入D1 | roster_employee/org_entry 证件字段组 + id_source='manual' |
| F04-5 复职 | roster_employee.prev_employee_id |
| F04-6 黑名单拦截 | org_blacklistid_number_hash 匹配) |
| F05-3 转正提醒 | roster_employee.probation_end_date + sys_remind_rule(scene=regular) |
| F07-3 钉钉交接占位D3 | org_leave_handover.item_type=dingtalk_resource |
| F07-5 离职证明 | org_leave.certificate_filefiles/ 存储) |
| F09-6 电子签预留D4 | contract_info.esign_status + contract_template.text_file_path |
| F10-2 字段权限 | flow_node_def.field_perm_json |
| F11-2 双维度数据范围 | sys_data_scope(scope_type=org/field) |
| F12-3 前后对比 | sys_audit_log.before_json/after_json |
| F13-6 报表数据范围受控 | 聚合查询叠加 sys_data_scope |
| F15-4 多场景提醒≥6 类) | sys_remind_rule(scene_code) + REMIND_SCENE 字典 |
| F16-1~6 编制 | org_headcount_scheme/headcount_item/headcount_snapshot |
| F17-1~3 项目式组织 | org_project/org_project_post + org_job(job_category=horizontal) |
| FEAT-B1-01⑦ API 开放预留 | sys_openapp一期不启用 |