#!/usr/bin/env python3 # -*- coding: utf-8 -*- """T01 数据契约生成器:依据 docs/01-design/database-design.md v3.0 §3/§4/§5/§7 生成 49 张表的 models/{module}/{table}.json(database-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-org(23 张,前缀 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-roster(7 张,前缀 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-flow(7 张,前缀 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-contract(4 张,前缀 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-system(8 张,前缀 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()