hr-system/scripts/gen_models.py

781 lines
34 KiB
Python
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.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""T01 数据契约生成器:依据 docs/01-design/database-design.md v3.0 §3/§4/§5/§7
生成 49 张表的 models/{module}/{table}.jsondatabase-table-definition-spec 四段式:
summary/fields/indexes/codes。幂等可重跑覆盖写入
用法python3 scripts/gen_models.py [输出根目录]
"""
import json, os, sys
def F(name, title, ftype, **kw):
d = {"name": name, "title": title, "type": ftype}
d.update(kw)
return d
def V(name, title, length, **kw):
return F(name, title, "varchar", length=length, **kw)
def AUDIT():
return [
V("created_by", "创建人", 32),
V("updated_by", "更新人", 32),
F("created_at", "创建时间", "timestamp"),
F("updated_at", "更新时间", "timestamp"),
]
def IDX(name, fields, unique=False):
d = {"name": name, "fields": fields}
if unique:
d["unique"] = True
return d
def CODE(name, code):
return {"name": name, "source": "appcodes", "code": code}
# ---------------- hr-org23 张,前缀 org_ ----------------
ORG = {
"org_unit": {
"title": "组织", "fields": [
V("id", "主键", 32, notnull=True),
V("org_code", "组织编码", 32, notnull=True),
V("org_name", "组织名称", 128, notnull=True),
V("parent_id", "上级组织(自引用)", 32),
V("org_type", "组织类型(字典ORG_TYPE)", 16),
V("leader_id", "负责人", 32),
F("effective_date", "生效日期", "date"),
F("expire_date", "失效日期(空=长期)", "date"),
V("status", "状态 active/inactive", 16, notnull=True, default="active"),
F("sort_no", "排序号", "int", default=0),
F("remark", "备注", "text"),
] + AUDIT(),
"indexes": [IDX("uk_org_code", ["org_code"], True), IDX("idx_parent_id", ["parent_id"]), IDX("idx_status", ["status"])],
"codes": [CODE("org_type", "ORG_TYPE")],
},
"org_unit_change": {
"title": "组织变更时间轴", "log": True, "fields": [
V("id", "主键", 32, notnull=True),
V("org_id", "组织", 32),
V("change_type", "变更类型(add/edit/disable/move/split/merge/delete)", 16),
F("before_json", "变更前快照", "text"),
F("after_json", "变更后快照", "text"),
F("effective_date", "生效日期", "date"),
V("operator_id", "操作人", 32),
V("flow_instance_id", "流程实例", 32),
F("created_at", "创建时间", "timestamp"),
],
"indexes": [IDX("idx_org_id", ["org_id"]), IDX("idx_created_at", ["created_at"])],
},
"org_field_def": {
"title": "组织自定义字段定义", "fields": [
V("id", "主键", 32, notnull=True),
V("field_code", "字段编码", 64, notnull=True),
V("field_name", "字段名称", 64, notnull=True),
V("field_type", "字段类型(text/number/date/select/file)", 16),
F("options_json", "选项定义JSON", "text"),
F("required", "是否必填(0/1)", "int", default=0),
F("sort_no", "排序号", "int", default=0),
V("status", "状态 active/inactive", 16, notnull=True, default="active"),
] + AUDIT(),
"indexes": [IDX("uk_field_code", ["field_code"], True)],
},
"org_field_value": {
"title": "组织字段值(EAV)", "fields": [
V("id", "主键", 32, notnull=True),
V("org_id", "组织", 32, notnull=True),
V("field_id", "字段定义", 32, notnull=True),
F("value_text", "文本值", "text"),
F("value_date", "日期值", "date"),
V("value_file", "附件值(文件ID)", 64),
F("updated_at", "更新时间", "timestamp"),
],
"indexes": [IDX("uk_org_field", ["org_id", "field_id"], True)],
},
"org_job": {
"title": "职务(含F17横向职务)", "fields": [
V("id", "主键", 32, notnull=True),
V("job_code", "职务编码", 32, notnull=True),
V("job_name", "职务名称", 64, notnull=True),
V("job_category", "职务类别(vertical/horizontal,F17)", 16, notnull=True, default="vertical"),
V("status", "状态 active/inactive", 16, notnull=True, default="active"),
F("sort_no", "排序号", "int", default=0),
F("remark", "备注", "text"),
] + AUDIT(),
"indexes": [IDX("uk_job_code", ["job_code"], True), IDX("idx_job_category", ["job_category"])],
"codes": [CODE("job_category", "JOB_CATEGORY")],
},
"org_position": {
"title": "职位", "fields": [
V("id", "主键", 32, notnull=True),
V("position_code", "职位编码", 32, notnull=True),
V("position_name", "职位名称", 64, notnull=True),
V("job_id", "职务", 32),
V("sequence_id", "序列", 32),
V("category", "职位类别(字典POS_CATEGORY)", 16),
V("dept_ids", "适用部门(多值,逗号分隔)", 512),
F("description", "职位描述", "text"),
V("status", "状态 active/inactive", 16, notnull=True, default="active"),
F("sort_no", "排序号", "int", default=0),
] + AUDIT(),
"indexes": [IDX("uk_position_code", ["position_code"], True), IDX("idx_job_id", ["job_id"])],
"codes": [CODE("category", "POS_CATEGORY")],
},
"org_sequence": {
"title": "序列", "fields": [
V("id", "主键", 32, notnull=True),
V("seq_category", "序列类别", 32),
V("seq_name", "序列名称", 64, notnull=True),
V("grade_level_ids", "关联职级类别(多值,逗号分隔)", 512),
V("status", "状态 active/inactive", 16, notnull=True, default="active"),
] + AUDIT(),
"indexes": [],
},
"org_grade_level": {
"title": "职级", "fields": [
V("id", "主键", 32, notnull=True),
V("level_code", "职级编码", 32, notnull=True),
V("level_name", "职级名称", 64, notnull=True),
V("level_category", "职级类别", 32),
V("start_rank_id", "起始职等", 32),
V("end_rank_id", "结束职等", 32),
F("sort_no", "排序号", "int", default=0),
] + AUDIT(),
"indexes": [IDX("uk_level_code", ["level_code"], True)],
},
"org_grade_rank": {
"title": "职等", "fields": [
V("id", "主键", 32, notnull=True),
V("rank_code", "职等编码", 32, notnull=True),
V("rank_name", "职等名称", 64, notnull=True),
F("rank_level", "职等层级", "int"),
F("sort_no", "排序号", "int", default=0),
] + AUDIT(),
"indexes": [IDX("uk_rank_code", ["rank_code"], True)],
},
"org_blacklist": {
"title": "黑名单", "log": True, "fields": [
V("id", "主键", 32, notnull=True),
V("name", "姓名", 64),
V("id_number_hash", "证件号哈希(确定性,匹配用)", 64, sensitive=True),
V("id_number_cipher", "证件号密文", 256, sensitive=True),
V("reason", "拉黑原因", 256),
V("source", "来源(leave/manual)", 16),
V("source_employee_id", "来源员工档案", 32),
V("created_by", "创建人", 32),
F("created_at", "创建时间", "timestamp"),
],
"indexes": [IDX("idx_id_number_hash", ["id_number_hash"])],
},
"org_contract_company": {
"title": "合同公司", "fields": [
V("id", "主键", 32, notnull=True),
V("company_code", "公司编码", 32, notnull=True),
V("company_name", "公司名称", 128, notnull=True),
V("credit_code", "统一社会信用代码", 32),
V("legal_person", "法人", 64),
V("contact_info", "联系方式", 128),
V("status", "状态 active/inactive", 16, notnull=True, default="active"),
] + AUDIT(),
"indexes": [IDX("uk_company_code", ["company_code"], True)],
},
"org_entry": {
"title": "入职记录", "fields": [
V("id", "主键", 32, notnull=True),
V("employee_id", "员工档案(可空,待入职)", 32),
V("name", "姓名", 64, notnull=True),
V("id_type", "证件类型(字典ID_TYPE)", 16, default="id_card"),
V("id_number_cipher", "证件号密文", 256, sensitive=True),
V("id_number_hash", "证件号哈希", 64, sensitive=True),
F("id_valid_from", "证件有效期起", "date"),
F("id_valid_to", "证件有效期止(长期=9999-12-31)", "date"),
V("id_authority", "证件签发机关", 128),
V("id_source", "证件信息来源(字典ID_SOURCE,D1手工录入)", 16, default="manual"),
V("employee_type", "员工类型(字典EMP_TYPE)", 16),
V("org_id", "入职组织", 32),
V("position_id", "职位", 32),
V("company_id", "合同公司", 32),
V("work_location", "办公地点", 128),
F("hire_date", "入职日期", "date"),
V("entry_status", "入职状态(字典ENTRY_STATUS)", 24, default="pending_approval"),
F("is_rehire", "是否复职(0/1)", "int", default=0),
V("register_status", "登记表状态(none/invited/filled)", 16, default="none"),
V("register_token", "登记表token(扫码入职降级)", 64),
V("register_url", "登记表链接", 256),
V("qrcode_file", "登记二维码图片(文件ID)", 64),
F("register_expire", "登记链接过期时间", "timestamp"),
V("flow_instance_id", "流程实例", 32),
F("remark", "备注", "text"),
] + AUDIT(),
"indexes": [IDX("idx_entry_status", ["entry_status"]), IDX("idx_hire_date", ["hire_date"]),
IDX("idx_flow_instance_id", ["flow_instance_id"]), IDX("uk_register_token", ["register_token"], True)],
"codes": [CODE("id_type", "ID_TYPE"), CODE("id_source", "ID_SOURCE"), CODE("employee_type", "EMP_TYPE"),
CODE("entry_status", "ENTRY_STATUS"), CODE("register_status", "REGISTER_STATUS")],
},
"org_regularization": {
"title": "转正记录", "fields": [
V("id", "主键", 32, notnull=True),
V("employee_id", "员工档案", 32),
F("probation_end_date", "试用期止(预计转正日)", "date"),
F("regular_date", "转正日期", "date"),
V("apply_type", "申请方式(self/proxy/manual)", 16),
V("status", "状态(pending/approved/rejected/cancelled)", 16, default="pending"),
V("flow_instance_id", "流程实例", 32),
F("remark", "备注", "text"),
] + AUDIT(),
"indexes": [IDX("idx_employee_id", ["employee_id"]), IDX("idx_regular_date", ["regular_date"])],
},
"org_transfer": {
"title": "调动单(支持批量)", "fields": [
V("id", "主键", 32, notnull=True),
V("transfer_type", "调动类型(字典TRANSFER_TYPE)", 24),
V("status", "状态(pending/confirmed/cancelled)", 16, default="pending"),
F("effective_date", "生效日期", "date"),
V("batch_no", "批次号", 32),
V("flow_instance_id", "流程实例", 32),
F("remark", "备注", "text"),
] + AUDIT(),
"indexes": [IDX("idx_status", ["status"]), IDX("idx_effective_date", ["effective_date"])],
"codes": [CODE("transfer_type", "TRANSFER_TYPE")],
},
"org_transfer_detail": {
"title": "调动明细(逐人)", "fields": [
V("id", "主键", 32, notnull=True),
V("transfer_id", "调动单", 32),
V("employee_id", "员工档案", 32),
V("from_org_id", "原组织", 32),
V("to_org_id", "新组织", 32),
V("from_position_id", "原职位", 32),
V("to_position_id", "新职位", 32),
V("from_grade_level_id", "原职级", 32),
V("to_grade_level_id", "新职级", 32),
V("status", "状态(pending/done/cancelled)", 16, default="pending"),
],
"indexes": [IDX("idx_transfer_id", ["transfer_id"]), IDX("idx_employee_id", ["employee_id"])],
},
"org_leave": {
"title": "离职记录", "fields": [
V("id", "主键", 32, notnull=True),
V("employee_id", "员工档案", 32),
V("leave_type", "离职类型(辞职/劝退/合同到期/退休)", 24),
V("leave_reason", "离职原因(字典LEAVE_REASON)", 32),
F("leave_date", "离职日期", "date"),
V("apply_type", "申请方式(self/proxy/manual)", 16),
V("status", "状态(pending_leave/left/cancelled)", 16, default="pending_leave"),
V("handover_status", "交接状态(pending/done)", 16, default="pending"),
V("certificate_file", "离职证明文件ID", 64),
F("blacklist_flag", "是否加入黑名单(0/1)", "int", default=0),
V("flow_instance_id", "流程实例", 32),
F("remark", "备注", "text"),
] + AUDIT(),
"indexes": [IDX("idx_employee_id", ["employee_id"]), IDX("idx_status", ["status"]), IDX("idx_leave_date", ["leave_date"])],
"codes": [CODE("leave_reason", "LEAVE_REASON")],
},
"org_leave_handover": {
"title": "离职交接项", "fields": [
V("id", "主键", 32, notnull=True),
V("leave_id", "离职记录", 32),
V("item_type", "交接项类型(字典HANDOVER_TYPE,含dingtalk_resource占位D3)", 32),
F("item_content", "交接内容", "text"),
V("handover_to_id", "交接人", 32),
V("status", "状态(pending/done)", 16, default="pending"),
F("remark", "备注", "text"),
F("created_at", "创建时间", "timestamp"),
F("updated_at", "更新时间", "timestamp"),
],
"indexes": [IDX("idx_leave_id", ["leave_id"])],
"codes": [CODE("item_type", "HANDOVER_TYPE")],
},
"org_concurrent_post": {
"title": "兼岗", "fields": [
V("id", "主键", 32, notnull=True),
V("employee_id", "员工档案", 32),
V("org_id", "兼岗组织", 32),
V("position_id", "兼岗职位", 32),
F("start_date", "开始日期", "date"),
F("end_date", "结束日期", "date"),
V("status", "状态(awaiting/active/expired)", 16, default="awaiting"),
V("flow_instance_id", "流程实例", 32),
] + AUDIT(),
"indexes": [IDX("idx_employee_id", ["employee_id"]), IDX("idx_status", ["status"])],
},
"org_headcount_scheme": {
"title": "编制方案(F16)", "fields": [
V("id", "主键", 32, notnull=True),
V("scheme_name", "方案名称", 128, notnull=True),
V("cycle_type", "周期(字典HEADCOUNT_CYCLE: year/quarter/month)", 16, notnull=True),
F("cycle_start", "周期起", "date", notnull=True),
F("cycle_end", "周期止", "date", notnull=True),
V("status", "状态 active/inactive", 16, notnull=True, default="active"),
F("remark", "备注", "text"),
] + AUDIT(),
"indexes": [IDX("idx_cycle", ["cycle_start", "cycle_end"]), IDX("idx_status", ["status"])],
"codes": [CODE("cycle_type", "HEADCOUNT_CYCLE")],
},
"headcount_item": {
"title": "编制细分项(F16)", "fields": [
V("id", "主键", 32, notnull=True),
V("scheme_id", "编制方案", 32, notnull=True),
F("scope_json", "占编范围条件(员工类型/状态/职位/职级/自定义字段)", "text", notnull=True),
F("seg_json", "细分维度(空=整体)", "text"),
F("head_limit", "编制数量", "int", notnull=True, default=0),
F("remark", "备注", "text"),
] + AUDIT(),
"indexes": [IDX("idx_scheme_id", ["scheme_id"])],
},
"headcount_snapshot": {
"title": "编制快照(F16历史回溯/实时计数)", "log": True, "fields": [
V("id", "主键", 32, notnull=True),
V("scheme_id", "编制方案", 32, notnull=True),
V("item_id", "细分项", 32, notnull=True),
F("snap_date", "快照日期", "date", notnull=True),
F("used_count", "占编人数", "int", notnull=True, default=0),
F("over_count", "超编数(0=未超编)", "int", notnull=True, default=0),
F("created_at", "创建时间", "timestamp"),
],
"indexes": [IDX("uk_scheme_item_date", ["scheme_id", "item_id", "snap_date"], True), IDX("idx_snap_date", ["snap_date"])],
},
"org_project": {
"title": "项目式组织(F17)", "fields": [
V("id", "主键", 32, notnull=True),
V("project_code", "项目编码", 32, notnull=True),
V("project_name", "项目名称", 128, notnull=True),
V("parent_id", "上级项目(自引用多层级)", 32),
V("owner_id", "负责人", 32),
F("start_date", "开始日期", "date"),
F("end_date", "结束日期", "date"),
V("project_status", "项目状态(字典PROJECT_STATUS)", 16, notnull=True, default="active"),
F("attrs_json", "项目属性", "text"),
F("sort_no", "排序号", "int", default=0),
] + AUDIT(),
"indexes": [IDX("uk_project_code", ["project_code"], True), IDX("idx_parent_id", ["parent_id"]), IDX("idx_project_status", ["project_status"])],
"codes": [CODE("project_status", "PROJECT_STATUS")],
},
"org_project_post": {
"title": "项目任职(F17横向)", "fields": [
V("id", "主键", 32, notnull=True),
V("project_id", "项目组织", 32, notnull=True),
V("employee_id", "员工", 32, notnull=True),
V("job_id", "横向职务(org_job.job_category=horizontal)", 32),
F("start_date", "开始日期", "date"),
F("end_date", "结束日期", "date"),
V("status", "状态 active/expired", 16, notnull=True, default="active"),
] + AUDIT(),
"indexes": [IDX("uk_proj_emp_job", ["project_id", "employee_id", "job_id", "start_date"], True), IDX("idx_employee_id", ["employee_id"])],
},
}
# ---------------- hr-roster7 张,前缀 roster_ ----------------
ROSTER = {
"roster_field_group": {
"title": "花名册字段分组", "fields": [
V("id", "主键", 32, notnull=True),
V("group_code", "分组编码", 32, notnull=True),
V("group_name", "分组名称(工作信息/个人信息/绩效结果/培训记录)", 64, notnull=True),
F("sort_no", "排序号", "int", default=0),
V("status", "状态 active/inactive", 16, notnull=True, default="active"),
] + AUDIT(),
"indexes": [IDX("uk_group_code", ["group_code"], True)],
},
"roster_field_def": {
"title": "花名册字段定义", "fields": [
V("id", "主键", 32, notnull=True),
V("group_id", "字段分组", 32),
V("field_code", "字段编码", 64, notnull=True),
V("field_name", "字段名称", 64, notnull=True),
V("field_type", "字段类型(text/number/date/select/multiselect/file)", 16),
F("options_json", "选项定义(字典code或自定义项)", "text"),
F("required", "是否必填(0/1)", "int", default=0),
F("sensitive", "是否敏感(0/1,驱动脱敏与可见性)", "int", default=0),
F("editable_self", "员工自助可编辑(0/1)", "int", default=0),
F("need_audit", "自助修改需审核(0/1)", "int", default=0),
F("apply_types", "适用员工类型JSON", "text"),
F("sort_no", "排序号", "int", default=0),
V("status", "状态 active/inactive", 16, notnull=True, default="active"),
] + AUDIT(),
"indexes": [IDX("uk_field_code", ["field_code"], True), IDX("idx_group_id", ["group_id"])],
},
"roster_employee": {
"title": "员工主档", "fields": [
V("id", "主键", 32, notnull=True),
V("employee_no", "工号", 32, notnull=True),
V("name", "姓名", 64, notnull=True),
V("gender", "性别(字典GENDER)", 16),
V("id_type", "证件类型", 16, default="id_card"),
V("id_number_cipher", "证件号密文(敏感)", 256, sensitive=True),
V("id_number_hash", "证件号哈希(确定性,唯一/黑名单匹配)", 64, sensitive=True),
F("id_valid_from", "证件有效期起", "date"),
F("id_valid_to", "证件有效期止(长期=9999-12-31)", "date"),
V("id_authority", "证件签发机关", 128),
V("id_source", "证件信息来源(字典ID_SOURCE)", 16, default="manual"),
F("birthday", "出生日期", "date"),
V("phone", "手机号(明文存储,按角色脱敏)", 20, sensitive=True),
V("email", "邮箱", 128),
V("org_id", "组织", 32),
V("position_id", "职位", 32),
V("job_id", "职务", 32),
V("grade_level_id", "职级", 32),
V("grade_rank_id", "职等", 32),
V("sequence_id", "序列", 32),
V("employee_type", "员工类型(字典EMP_TYPE)", 16),
V("employee_status", "员工状态(字典EMP_STATUS)", 16),
F("hire_date", "入职日期", "date"),
F("probation_end_date", "试用期止(预计转正日)", "date"),
F("regular_date", "转正日期", "date"),
F("leave_date", "离职日期", "date"),
V("company_id", "合同公司", 32),
V("work_location", "办公地点", 128),
V("direct_leader_id", "直属主管", 32),
V("prev_employee_id", "复职关联原档案ID", 32),
F("remark", "备注", "text"),
] + AUDIT(),
"indexes": [IDX("uk_employee_no", ["employee_no"], True), IDX("uk_id_number_hash", ["id_number_hash"], True),
IDX("idx_org_id", ["org_id"]), IDX("idx_status", ["employee_status"]), IDX("idx_hire_date", ["hire_date"]),
IDX("idx_probation_end", ["probation_end_date"]), IDX("idx_direct_leader", ["direct_leader_id"]),
IDX("idx_prev_employee", ["prev_employee_id"])],
"codes": [CODE("gender", "GENDER"), CODE("employee_type", "EMP_TYPE"), CODE("employee_status", "EMP_STATUS"),
CODE("id_source", "ID_SOURCE"), CODE("id_type", "ID_TYPE")],
},
"roster_field_value": {
"title": "花名册字段值(EAV)", "fields": [
V("id", "主键", 32, notnull=True),
V("employee_id", "员工档案", 32, notnull=True),
V("field_id", "字段定义", 32, notnull=True),
F("value_text", "文本值", "text"),
F("value_number", "数值", "decimal", precision=18, scale=4),
F("value_date", "日期值", "date"),
V("value_file", "附件值(文件ID)", 64),
V("updated_by", "更新人", 32),
F("updated_at", "更新时间", "timestamp"),
],
"indexes": [IDX("uk_employee_field", ["employee_id", "field_id"], True), IDX("idx_field_id", ["field_id"])],
},
"roster_empno_rule": {
"title": "工号规则", "fields": [
V("id", "主键", 32, notnull=True),
V("rule_name", "规则名称", 64, notnull=True),
V("prefix", "工号前缀", 16),
V("match_field", "匹配字段(如company_id)", 32),
V("match_value", "匹配值", 64),
F("seq_length", "流水号长度", "int", default=4),
F("current_seq", "当前流水号", "int", default=0),
V("status", "状态 active/inactive", 16, notnull=True, default="active"),
] + AUDIT(),
"indexes": [],
},
"roster_timeline": {
"title": "员工时间轴", "log": True, "fields": [
V("id", "主键", 32, notnull=True),
V("employee_id", "员工档案", 32),
V("event_type", "事件类型(entry/regular/transfer/leave/concurrent/contract/field_change/headcount/project/migrate)", 24),
F("event_date", "事件日期", "date"),
V("title", "标题", 128),
F("content_json", "内容JSON", "text"),
V("source_type", "来源类型", 32),
V("source_id", "来源ID", 32),
F("created_at", "创建时间", "timestamp"),
],
"indexes": [IDX("idx_emp_date", ["employee_id", "event_date"])],
},
"roster_type_rule": {
"title": "员工类型字段规则", "fields": [
V("id", "主键", 32, notnull=True),
V("employee_type", "员工类型(字典EMP_TYPE)", 16, notnull=True),
F("field_rules_json", "按类型必填/隐藏字段规则", "text"),
V("status", "状态 active/inactive", 16, notnull=True, default="active"),
] + AUDIT(),
"indexes": [IDX("uk_employee_type", ["employee_type"], True)],
"codes": [CODE("employee_type", "EMP_TYPE")],
},
}
# ---------------- hr-flow7 张,前缀 flow_ ----------------
FLOW = {
"flow_form_def": {
"title": "表单定义", "fields": [
V("id", "主键", 32, notnull=True),
V("form_code", "表单编码", 64, notnull=True),
V("form_name", "表单名称", 128, notnull=True),
V("biz_type", "业务类型(字典FLOW_BIZ_TYPE,含attendance_*桩位)", 32),
F("fields_json", "字段定义(引用roster_field_def或自定义项)", "text"),
V("status", "状态 active/inactive", 16, notnull=True, default="active"),
] + AUDIT(),
"indexes": [IDX("uk_form_code", ["form_code"], True)],
"codes": [CODE("biz_type", "FLOW_BIZ_TYPE")],
},
"flow_def": {
"title": "流程定义", "fields": [
V("id", "主键", 32, notnull=True),
V("flow_code", "流程编码", 64, notnull=True),
V("flow_name", "流程名称", 128, notnull=True),
V("form_id", "表单定义", 32),
V("biz_type", "业务类型(字典FLOW_BIZ_TYPE)", 32),
F("match_cond_json", "匹配条件(员工类型/部门)", "text"),
F("version", "版本", "int", default=1),
V("status", "状态 active/inactive", 16, notnull=True, default="active"),
] + AUDIT(),
"indexes": [IDX("uk_flow_code", ["flow_code"], True), IDX("idx_biz_type", ["biz_type"])],
"codes": [CODE("biz_type", "FLOW_BIZ_TYPE")],
},
"flow_node_def": {
"title": "流程节点", "fields": [
V("id", "主键", 32, notnull=True),
V("flow_id", "流程定义", 32),
F("node_seq", "节点序号", "int", default=1),
V("node_name", "节点名称", 64),
V("approver_type", "审批人类型(user/role/leader/admin/flow_role)", 16),
V("approver_value", "审批人取值", 256),
V("pass_rule", "通过规则(any/all)", 8, default="all"),
F("field_perm_json", "节点字段可读写权限", "text"),
V("cc_to", "抄送对象", 512),
],
"indexes": [IDX("idx_flow_id", ["flow_id"])],
},
"flow_role": {
"title": "审批角色", "fields": [
V("id", "主键", 32, notnull=True),
V("role_name", "角色名称", 64, notnull=True),
F("flow_scope_json", "可用流程范围", "text"),
F("op_perm_json", "操作权限", "text"),
F("data_scope_json", "数据查看范围", "text"),
V("status", "状态 active/inactive", 16, notnull=True, default="active"),
] + AUDIT(),
"indexes": [],
},
"flow_instance": {
"title": "流程实例", "fields": [
V("id", "主键", 32, notnull=True),
V("inst_no", "审批编号", 32, notnull=True),
V("flow_id", "流程定义", 32, notnull=True),
V("form_id", "表单定义", 32, notnull=True),
V("biz_type", "业务类型(字典FLOW_BIZ_TYPE)", 32, notnull=True),
V("biz_id", "业务单据ID", 32),
V("title", "标题", 256),
V("initiator_id", "发起人", 32, notnull=True),
V("inst_status", "实例状态(字典INST_STATUS)", 16, notnull=True, default="running"),
F("submit_data_json", "提交表单数据快照", "text"),
F("start_time", "发起时间", "timestamp"),
F("finish_time", "结束时间", "timestamp"),
],
"indexes": [IDX("uk_inst_no", ["inst_no"], True), IDX("idx_biz", ["biz_type", "biz_id"]),
IDX("idx_initiator", ["initiator_id"]), IDX("idx_status", ["inst_status"])],
"codes": [CODE("biz_type", "FLOW_BIZ_TYPE"), CODE("inst_status", "INST_STATUS")],
},
"flow_task": {
"title": "审批任务(待办/已办)", "log": True, "fields": [
V("id", "主键", 32, notnull=True),
V("instance_id", "流程实例", 32),
V("node_id", "节点定义", 32),
V("node_name", "节点名称", 64),
V("approver_id", "审批人", 32),
V("task_status", "任务状态(pending/approved/rejected/forwarded/cancelled)", 16, default="pending"),
F("comment", "审批意见", "text"),
F("handle_time", "处理时间", "timestamp"),
F("created_at", "创建时间", "timestamp"),
],
"indexes": [IDX("idx_approver_status", ["approver_id", "task_status"]), IDX("idx_instance", ["instance_id"])],
},
"flow_op_log": {
"title": "流转日志", "log": True, "fields": [
V("id", "主键", 32, notnull=True),
V("instance_id", "流程实例", 32),
V("op_type", "操作类型(submit/approve/reject/forward/cancel/withdraw)", 16),
V("operator_id", "操作人", 32),
F("comment", "意见", "text"),
F("created_at", "创建时间", "timestamp"),
],
"indexes": [IDX("idx_instance", ["instance_id"])],
},
}
# ---------------- hr-contract4 张,前缀 contract_ ----------------
CONTRACT = {
"contract_type": {
"title": "合同类型", "fields": [
V("id", "主键", 32, notnull=True),
V("type_code", "类型编码", 32, notnull=True),
V("type_name", "类型名称(劳动合同/保密协议/竞业协议…)", 64, notnull=True),
V("status", "状态 active/inactive", 16, notnull=True, default="active"),
] + AUDIT(),
"indexes": [IDX("uk_type_code", ["type_code"], True)],
},
"contract_template": {
"title": "合同模板", "fields": [
V("id", "主键", 32, notnull=True),
V("template_name", "模板名称", 128, notnull=True),
V("type_id", "合同类型", 32),
V("file_path", "模板文件(文件ID)", 256),
F("field_marks_json", "占位符映射", "text"),
V("status", "状态 active/inactive", 16, notnull=True, default="active"),
] + AUDIT(),
"indexes": [],
},
"contract_info": {
"title": "合同台账", "fields": [
V("id", "主键", 32, notnull=True),
V("contract_no", "合同编号", 64, notnull=True),
V("employee_id", "员工档案", 32),
V("type_id", "合同类型", 32),
V("company_id", "签约公司(org_contract_company)", 32),
V("template_id", "合同模板", 32),
F("start_date", "合同起始日", "date"),
F("end_date", "合同到期日", "date"),
F("sign_date", "签订日期", "date"),
V("contract_status", "合同状态(字典CONTRACT_STATUS)", 16, default="active"),
V("file_path", "合同文件(文件ID)", 256),
V("text_file_path", "模板生成合同文本(D4)", 256),
V("flow_instance_id", "流程实例", 32),
V("remind_rule_id", "到期提醒规则", 32),
V("esign_status", "电子签状态(字典ESIGN_STATUS,D4预留)", 16, default="not_enabled"),
F("remark", "备注", "text"),
] + AUDIT(),
"indexes": [IDX("idx_employee", ["employee_id"]), IDX("idx_end_date", ["end_date"]), IDX("idx_status", ["contract_status"])],
"codes": [CODE("contract_status", "CONTRACT_STATUS"), CODE("esign_status", "ESIGN_STATUS")],
},
"contract_remind_rule": {
"title": "合同到期提醒规则", "fields": [
V("id", "主键", 32, notnull=True),
V("rule_name", "规则名称", 64, notnull=True),
F("days_before", "提前天数", "int", default=30),
V("target_role", "提醒对象(self/leader/hr)", 16, default="hr"),
F("content_template", "提醒内容模板", "text"),
V("status", "状态 active/inactive", 16, notnull=True, default="active"),
] + AUDIT(),
"indexes": [],
},
}
# ---------------- hr-system8 张,前缀 sys_ ----------------
SYS = {
"sys_data_scope": {
"title": "数据范围(F11)", "fields": [
V("id", "主键", 32, notnull=True),
V("admin_user_id", "管理员用户", 32),
V("scope_type", "范围类型(org/field)", 16),
F("org_ids_json", "组织维度(含子树)", "text"),
F("field_ids_json", "花名册字段维度", "text"),
F("remark", "备注", "text"),
] + AUDIT(),
"indexes": [IDX("idx_admin_user", ["admin_user_id"])],
},
"sys_audit_log": {
"title": "操作日志(只增,F12)", "log": True, "fields": [
V("id", "主键", 32, notnull=True),
V("module", "模块(hr-org/hr-roster/...)", 32, notnull=True),
V("target_type", "对象类型(roster_employee/org_unit/...)", 32, notnull=True),
V("target_id", "对象ID", 32, notnull=True),
V("operation", "操作类型(字典AUDIT_OP)", 16, notnull=True),
V("operator_id", "操作人", 32, notnull=True),
V("operator_name", "操作人姓名", 64, default=""),
F("before_json", "变更前快照", "text"),
F("after_json", "变更后快照", "text"),
V("request_ip", "请求IP", 64),
F("created_at", "创建时间", "timestamp"),
],
"indexes": [IDX("idx_target", ["target_type", "target_id"]), IDX("idx_operator", ["operator_id"]),
IDX("idx_created_at", ["created_at"]), IDX("idx_module_op", ["module", "operation"])],
"codes": [CODE("operation", "AUDIT_OP")],
},
"sys_message": {
"title": "站内消息(F15)", "log": True, "fields": [
V("id", "主键", 32, notnull=True),
V("receiver_id", "接收人", 32),
V("msg_type", "消息类型(todo/approval/remind/care/notice)", 16),
V("title", "标题", 256),
F("content", "内容", "text"),
V("biz_type", "业务类型", 32),
V("biz_id", "业务ID", 32),
V("send_channel", "发送渠道(字典MSG_CHANNEL,一期仅site;D9)", 16, default="site"),
F("is_read", "是否已读(0/1)", "int", default=0),
F("created_at", "创建时间", "timestamp"),
],
"indexes": [IDX("idx_receiver_read", ["receiver_id", "is_read"]), IDX("idx_created_at", ["created_at"])],
"codes": [CODE("send_channel", "MSG_CHANNEL")],
},
"sys_remind_rule": {
"title": "通用提醒规则(F15)", "fields": [
V("id", "主键", 32, notnull=True),
V("scene_code", "提醒场景(字典REMIND_SCENE,≥6类)", 32, notnull=True),
V("rule_name", "规则名称", 64, notnull=True),
F("days_before", "提前天数", "int", default=7),
F("content_template", "提醒内容模板", "text"),
V("target_role", "提醒对象(admin/employee/leader)", 16, default="admin"),
V("status", "状态 active/inactive", 16, notnull=True, default="active"),
] + AUDIT(),
"indexes": [IDX("uk_scene_rule", ["scene_code", "rule_name"], True)],
"codes": [CODE("scene_code", "REMIND_SCENE")],
},
"sys_announcement": {
"title": "企业公告(F15)", "fields": [
V("id", "主键", 32, notnull=True),
V("title", "标题", 256, notnull=True),
F("content", "内容", "text"),
F("publish_scope_json", "发布范围", "text"),
F("publish_time", "发布时间", "timestamp"),
V("status", "状态(draft/published/offline)", 16, default="draft"),
] + AUDIT(),
"indexes": [IDX("idx_status", ["status"])],
},
"sys_policy": {
"title": "企业政策(F15)", "fields": [
V("id", "主键", 32, notnull=True),
V("title", "标题", 256, notnull=True),
V("file_path", "政策文件(文件ID)", 256),
F("view_scope_json", "查阅范围", "text"),
F("downloadable", "允许下载(0/1)", "int", default=0),
F("publish_time", "发布时间", "timestamp"),
V("status", "状态(draft/published/offline)", 16, default="draft"),
] + AUDIT(),
"indexes": [IDX("idx_status", ["status"])],
},
"sys_care_config": {
"title": "关怀配置(生日/周年,F15)", "fields": [
V("id", "主键", 32, notnull=True),
V("care_type", "关怀类型(birthday/anniversary)", 16, notnull=True),
F("template", "文案模板", "text"),
F("enable", "启用(0/1)", "int", default=1),
V("push_target", "推送对象(employee/leader)", 16, default="employee"),
V("updated_by", "更新人", 32),
F("updated_at", "更新时间", "timestamp"),
],
"indexes": [IDX("uk_care_type", ["care_type"], True)],
},
"sys_openapp": {
"title": "API开放应用(一期预留,FEAT-B1-01⑦)", "fields": [
V("id", "主键", 32, notnull=True),
V("app_key", "应用Key", 64, notnull=True),
V("app_secret_cipher", "应用密钥密文", 256, sensitive=True),
V("app_name", "应用名称", 128),
F("scope_json", "授权资源范围", "text"),
F("rate_limit", "限流(req/s,默认10)", "int", default=10),
V("status", "状态 active/inactive", 16, notnull=True, default="active"),
] + AUDIT(),
"indexes": [IDX("uk_app_key", ["app_key"], True)],
},
}
MODULES = [("hr_org", ORG), ("hr_roster", ROSTER), ("hr_flow", FLOW), ("hr_contract", CONTRACT), ("hr_system", SYS)]
def main():
root = sys.argv[1] if len(sys.argv) > 1 else os.path.join(os.path.dirname(__file__), "..", "models")
total = 0
for mod, tables in MODULES:
mdir = os.path.join(root, mod)
os.makedirs(mdir, exist_ok=True)
for tname, spec in tables.items():
doc = {
"summary": [{"name": tname, "title": spec["title"], "pk": ["id"],
"charset": "utf8mb4", "collate": "utf8mb4_unicode_ci"}],
"fields": spec["fields"],
"indexes": spec.get("indexes", []),
"codes": spec.get("codes", []),
}
path = os.path.join(mdir, tname + ".json")
with open(path, "w", encoding="utf-8") as f:
json.dump(doc, f, ensure_ascii=False, indent=2)
f.write("\n")
total += 1
print(f"{mod}: {len(tables)} 张 -> {mdir}")
print(f"共生成 models JSON: {total} 张表")
if __name__ == "__main__":
main()