From 1c0304091d5a7dd05859167f5e417cf932c67028 Mon Sep 17 00:00:00 2001
From: 刘光辉 <347230014@qq.com>
Date: 星期四, 17 九月 2026 11:19:17 +0800
Subject: [PATCH] 字段信息基础班
---
docs/tms/gen_tms_schema.py | 786 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 786 insertions(+), 0 deletions(-)
diff --git a/docs/tms/gen_tms_schema.py b/docs/tms/gen_tms_schema.py
new file mode 100644
index 0000000..53900b8
--- /dev/null
+++ b/docs/tms/gen_tms_schema.py
@@ -0,0 +1,786 @@
+# -*- coding: utf-8 -*-
+from pathlib import Path
+
+SYS = [
+ ("f_id", "varchar(50)", "NOT NULL", "涓婚敭"),
+ ("f_tenant_id", "varchar(50)", None, "绉熸埛"),
+]
+SYS_AUDIT = [
+ ("f_creator_user_id", "varchar(50)", None, "鍒涘缓浜�"),
+ ("f_creator_time", "timestamp", None, "鍒涘缓鏃堕棿"),
+ ("f_last_modify_user_id", "varchar(50)", None, "淇敼浜�"),
+ ("f_last_modify_time", "timestamp", None, "淇敼鏃堕棿"),
+ ("f_delete_mark", "int", "NOT NULL DEFAULT 0", "鍒犻櫎鏍囪 0姝e父 1鍒犻櫎"),
+ ("f_delete_user_id", "varchar(50)", None, "鍒犻櫎浜�"),
+ ("f_delete_time", "timestamp", None, "鍒犻櫎鏃堕棿"),
+]
+SYS_FLOW = [
+ ("f_flow_id", "varchar(50)", None, "娴佺▼妯℃澘ID"),
+ ("f_flow_task_id", "varchar(50)", None, "娴佺▼浠诲姟ID"),
+ ("f_flow_state", "int", None, "娴佺▼鐘舵��"),
+]
+CHILD_FK = ("f_foreign_id", "varchar(50)", "NOT NULL", "涓昏〃f_id")
+
+def col(name, typ, extra=None, cmt=""):
+ return (name, typ, extra, cmt)
+
+tables = []
+
+def add(name, comment, cols, indexes=None, child=False, flow=False, delete_user=True):
+ fields = list(SYS)
+ if child:
+ fields.append(CHILD_FK)
+ fields.extend(cols)
+ if flow:
+ fields.extend(SYS_FLOW)
+ audit = list(SYS_AUDIT)
+ if not delete_user:
+ audit = [x for x in audit if x[0] not in ("f_delete_user_id", "f_delete_time")]
+ fields.extend(audit)
+ tables.append({
+ "name": name,
+ "comment": comment,
+ "cols": fields,
+ "indexes": indexes or [],
+ "child": child,
+ })
+
+# ===== 1 鍩虹璁剧疆 =====
+add("tms_place", "鍩硅鍦扮偣銆傚湪绾垮涔犱换鍔″湴鐐瑰彲绌猴紱鍗犵敤鎸夊皬鏃朵笌鏈彇娑堜换鍔℃椂闂翠氦鍙夋牎楠屻��", [
+ col("place_no", "varchar(50)", "NOT NULL", "鍦扮偣缂栧彿锛屽崟鎹鍒� A+YYYYMM+4锛屽 A2026090001"),
+ col("place_name", "varchar(200)", "NOT NULL", "鍦扮偣鍚嶇О"),
+ col("place_group", "varchar(50)", None, "缁勫埆/鍒嗗尯锛屽瓧鍏革細鍔炲叕鍖�/鐢熶骇鍖�/A鍖�/B鍖�"),
+ col("enabled", "varchar(20)", "NOT NULL DEFAULT '1'", "1鍚敤 0鍋滅敤"),
+ col("remark", "varchar(500)", None, "澶囨敞"),
+], indexes=[
+ ("uk", "uk_tms_place_no", ["place_no"], True),
+ ("idx", "idx_tms_place_group", ["place_group", "enabled"], False),
+])
+
+add("tms_trainer", "鍩硅甯堟。妗堬紝鍏宠仈绯荤粺鐢ㄦ埛锛涢噸澶嶅綍鍏ユ寜user_id璇嗗埆锛涜处鍙锋敞閿�鍚庤嚜鍔ㄥ簾寮冦��", [
+ col("archive_no", "varchar(50)", None, "妗f缂栧彿 AR+YYYY+4锛屽彲涓庝釜浜哄煿璁。妗堝叡鐢�"),
+ col("user_id", "varchar(50)", "NOT NULL", "绯荤粺鐢ㄦ埛ID"),
+ col("trainer_name", "varchar(100)", None, "濮撳悕锛堝啑浣欙紝渚夸簬鍒楄〃锛�"),
+ col("dept_id", "varchar(50)", None, "閮ㄩ棬"),
+ col("post_id", "varchar(50)", None, "宀椾綅"),
+ col("school", "varchar(200)", None, "姣曚笟闄㈡牎"),
+ col("education", "varchar(50)", None, "瀛﹀巻"),
+ col("major", "varchar(100)", None, "涓撲笟"),
+ col("tech_title", "varchar(100)", None, "鎶�鏈亴绉�"),
+ col("hire_date", "date", None, "鍏ヨ亴鏃堕棿"),
+ col("license", "varchar(200)", None, "鎵т笟璇佷功"),
+ col("work_years", "varchar(50)", None, "宸ヤ綔骞撮檺"),
+ col("trainer_level", "varchar(50)", None, "鍩硅甯堢骇鍒�"),
+ col("biz_status", "varchar(20)", "NOT NULL DEFAULT 'valid'", "valid鏈夋晥鏈熷唴 / revoked宸叉挙閿�"),
+ col("remark", "varchar(500)", None, "澶囨敞"),
+], indexes=[
+ ("uk", "uk_tms_trainer_user", ["user_id"], True),
+ ("uk", "uk_tms_trainer_no", ["archive_no"], True),
+ ("idx", "idx_tms_trainer_status", ["biz_status"], False),
+])
+
+add("tms_trainer_work", "鍩硅甯堜釜浜哄伐浣滅粡鍘嗗瓙琛�", [
+ col("sort_no", "int", None, "搴忓彿"),
+ col("hire_date", "date", None, "鍏ヨ亴鏃堕棿"),
+ col("leave_date", "date", None, "绂昏亴鏃堕棿"),
+ col("company_name", "varchar(200)", None, "鍏徃鍚嶇О"),
+ col("post_name", "varchar(100)", None, "浠昏亴宀椾綅"),
+ col("leave_reason", "varchar(200)", None, "绂昏亴鍘熷洜"),
+], child=True, delete_user=False, indexes=[("idx", "idx_tms_trainer_work_fid", ["f_foreign_id"], False)])
+
+add("tms_trainer_exp", "鍩硅甯堜釜浜哄煿璁粡鍘嗗瓙琛�", [
+ col("sort_no", "int", None, "搴忓彿"),
+ col("train_date", "date", None, "鍩硅鏃堕棿"),
+ col("org_name", "varchar(200)", None, "鍩硅鏈烘瀯"),
+ col("cert_name", "varchar(200)", None, "鍩硅璇佷功"),
+ col("cert_file", "text", None, "璇佷功闄勪欢"),
+], child=True, delete_user=False, indexes=[("idx", "idx_tms_trainer_exp_fid", ["f_foreign_id"], False)])
+
+add("tms_admin_auth", "鍩硅绠$悊鍛樿法閮ㄩ棬鎺堟潈銆傚埌鏈熷墠涓�涓湀鎻愰啋绠$悊鍛�/鍩硅甯堛��", [
+ col("admin_user_id", "varchar(50)", "NOT NULL", "鍩硅绠$悊鍛�"),
+ col("scope_dept_id", "text", "NOT NULL", "绠$悊鑼冨洿锛岀粍缁囧閫�"),
+ col("include_child", "varchar(10)", "NOT NULL DEFAULT '1'", "鏄惁鍖呭惈涓嬬骇閮ㄩ棬 1鏄� 0鍚�"),
+ col("registrar_id", "varchar(50)", None, "鐧昏浜�"),
+ col("register_date", "date", None, "鐧昏鏃ユ湡"),
+ col("expire_date", "date", None, "鎺堟潈鍒版湡鏃ワ紝鍙┖琛ㄧず闀挎湡"),
+ col("biz_status", "varchar(20)", "NOT NULL DEFAULT 'valid'", "valid鏈夋晥 / revoked搴熷純"),
+ col("remark", "varchar(500)", None, "澶囨敞"),
+], indexes=[("idx", "idx_tms_admin_auth_user", ["admin_user_id", "biz_status"], False)])
+
+add("tms_train_mode", "鍩硅鏂瑰紡閰嶇疆锛堝惈绛惧埌瑙勫垯锛屼笉浠呮槸瀛楀吀锛�", [
+ col("mode_code", "varchar(50)", "NOT NULL", "缂栫爜锛歰nsite/practice/online"),
+ col("mode_name", "varchar(100)", "NOT NULL", "鍚嶇О锛氶泦涓巿璇�/鎿嶄綔鎺堣/鍦ㄧ嚎瀛︿範"),
+ col("sign_rule", "varchar(20)", "NOT NULL DEFAULT 'both'", "scan浠呮壂鐮� / both鎵爜+鍦ㄧ嚎"),
+ col("allow_guest_sign", "varchar(10)", "NOT NULL DEFAULT '0'", "鍏佽鍚嶅崟澶栫鍒帮紝闆嗕腑鎺堣=1"),
+ col("same_day_required", "varchar(10)", "NOT NULL DEFAULT '0'", "寮�濮嬬粨鏉熷繀椤诲悓涓�澶╋紝闆嗕腑/鎿嶄綔=1"),
+ col("place_required", "varchar(10)", "NOT NULL DEFAULT '1'", "鍦扮偣蹇呭~锛屽湪绾垮涔�=0"),
+ col("sort_no", "int", None, "鎺掑簭"),
+ col("enabled", "varchar(10)", "NOT NULL DEFAULT '1'", "鍚敤"),
+ col("remark", "varchar(500)", None, "澶囨敞"),
+], indexes=[("uk", "uk_tms_train_mode_code", ["mode_code"], True)])
+
+add("tms_sign_mode", "绛惧埌鏂瑰紡锛欰PP鎵爜 / 寰俊鎵爜 / 鍦ㄧ嚎绛惧埌", [
+ col("mode_code", "varchar(50)", "NOT NULL", "缂栫爜锛歛pp_scan/wx_scan/online"),
+ col("mode_name", "varchar(100)", "NOT NULL", "鍚嶇О"),
+ col("tip_seconds", "int", "NOT NULL DEFAULT 30", "鏈夊煿璁鐐规椂寮哄埗闃呰绉掓暟锛孖T鍙皟"),
+ col("match_roster", "varchar(10)", "NOT NULL DEFAULT '1'", "鏄惁蹇呴』鍖归厤鍩硅瀵硅薄鍚嶅崟"),
+ col("enabled", "varchar(10)", "NOT NULL DEFAULT '1'", "鍚敤"),
+ col("remark", "varchar(500)", None, "澶囨敞"),
+], indexes=[("uk", "uk_tms_sign_mode_code", ["mode_code"], True)])
+
+add("tms_eval_mode", "鍩硅鏁堟灉璇勪及鏂瑰紡锛氭彁闂� / 鐜板満鎿嶄綔 / 鍦ㄧ嚎鑰冭瘯 / 鏃犻渶鑰冩牳", [
+ col("mode_code", "varchar(50)", "NOT NULL", "缂栫爜锛歲uiz/practice/exam/none"),
+ col("mode_name", "varchar(100)", "NOT NULL", "鍚嶇О"),
+ col("need_paper", "varchar(10)", "NOT NULL DEFAULT '0'", "鏄惁闇�瑕佽瘯鍗�"),
+ col("need_quiz", "varchar(10)", "NOT NULL DEFAULT '0'", "鏄惁闇�瑕佹彁闂璁�"),
+ col("need_practice", "varchar(10)", "NOT NULL DEFAULT '0'", "鏄惁闇�瑕佸疄鎿嶈瘎鍒�"),
+ col("enabled", "varchar(10)", "NOT NULL DEFAULT '1'", "鍚敤"),
+ col("remark", "varchar(500)", None, "澶囨敞"),
+], indexes=[("uk", "uk_tms_eval_mode_code", ["mode_code"], True)])
+
+add("tms_course", "鍩硅璇剧▼涓绘暟鎹紝璁″垝/浠诲姟鍙紩鐢�", [
+ col("course_no", "varchar(50)", None, "璇剧▼缂栧彿"),
+ col("course_name", "varchar(200)", "NOT NULL", "璇剧▼鍚嶇О"),
+ col("category", "varchar(50)", None, "璇剧▼鍒嗙被"),
+ col("train_mode", "varchar(50)", None, "鍩硅鏂瑰紡缂栫爜"),
+ col("eval_mode", "varchar(50)", None, "鑰冩牳鏂瑰紡缂栫爜"),
+ col("paper_id", "varchar(50)", None, "鍏宠仈璇曞嵎"),
+ col("hours", "numeric(8,1)", None, "瀛︽椂"),
+ col("enabled", "varchar(10)", "NOT NULL DEFAULT '1'", "鍚敤"),
+ col("remark", "varchar(500)", None, "澶囨敞"),
+], indexes=[("uk", "uk_tms_course_no", ["course_no"], True)])
+
+add("tms_material", "鍩硅鏁欐潗/浼佷笟鐭ヨ瘑搴撱�傚彲涓婁紶鏈湴鎴栧紩鐢―MS锛堥渶閴存潈锛夈�傛挙閿�鍚庝笉鍙啀寮曠敤銆�", [
+ col("material_no", "varchar(50)", "NOT NULL", "鏁欐潗缂栧彿 TM+YYYYMM+4"),
+ col("material_name", "varchar(500)", "NOT NULL", "鏁欐潗鍚嶇О"),
+ col("file_type", "varchar(50)", None, "Word/PPT/Excel/PDF/瑙嗛/闊抽"),
+ col("apply_time", "date", None, "鐢宠鏃堕棿"),
+ col("applicant_id", "varchar(50)", None, "涓婁紶鐢宠浜�"),
+ col("apply_dept_id", "varchar(50)", None, "鐢宠浜洪儴闂�"),
+ col("file_json", "text", None, "鏁欐潗闄勪欢锛圝NPF闄勪欢JSON锛�"),
+ col("dms_file_id", "varchar(50)", None, "寮曠敤鐨凞MS鏂囦欢ID"),
+ col("quote_count", "int", "NOT NULL DEFAULT 0", "琚换鍔″紩鐢ㄦ鏁�"),
+ col("biz_status", "varchar(20)", "NOT NULL DEFAULT 'valid'", "valid鏈夋晥 / revoked宸叉挙閿�"),
+ col("remark", "varchar(500)", None, "澶囨敞"),
+], indexes=[
+ ("uk", "uk_tms_material_no", ["material_no"], True),
+ ("idx", "idx_tms_material_type", ["file_type", "biz_status"], False),
+])
+
+# ===== 2 鑰冭瘯寮曟搸 =====
+add("tms_question_bank", "棰樺簱", [
+ col("bank_name", "varchar(200)", "NOT NULL", "棰樺簱鍚嶇О"),
+ col("question_count", "int", "NOT NULL DEFAULT 0", "璇曢鏁伴噺锛堢淮鎶ゆ椂鍥炲啓锛�"),
+ col("biz_status", "varchar(20)", "NOT NULL DEFAULT 'normal'", "normal姝e父 / invalid澶辨晥"),
+ col("remark", "text", None, "澶囨敞"),
+], indexes=[("idx", "idx_tms_qbank_status", ["biz_status"], False)])
+
+add("tms_question", "璇曢銆備慨鏀逛笉褰卞搷宸插畬鎴愯�冭瘯蹇収銆�", [
+ col("question_no", "varchar(50)", None, "璇曢缂栧彿锛屽彲娴佹按"),
+ col("bank_id", "varchar(50)", "NOT NULL", "鎵�灞為搴� tms_question_bank.f_id"),
+ col("question_type", "varchar(20)", "NOT NULL", "single鍗曢�� / multi澶氶�� / judge鍒ゆ柇 / blank濉┖ / essay闂瓟"),
+ col("difficulty", "varchar(20)", None, "easy/normal/hard锛岀晫闈㈤粯璁や竴鑸�"),
+ col("source_type", "varchar(50)", None, "璇曢鏉ユ簮锛岄粯璁よ嚜鍛介"),
+ col("stem", "text", "NOT NULL", "棰樺共锛屽瘜鏂囨湰"),
+ col("analysis", "text", None, "绛旀瑙f瀽/澶囨敞锛岄敊棰樺彲鏄剧ず"),
+ col("admin_user_id", "varchar(50)", None, "绠$悊鍛�"),
+ col("biz_status", "varchar(20)", "NOT NULL DEFAULT 'open'", "open寮�鏀� / closed涓嶅紑鏀� / invalid搴熷純"),
+], indexes=[
+ ("idx", "idx_tms_question_bank", ["bank_id", "question_type", "biz_status"], False),
+])
+
+add("tms_question_option", "璇曢閫夐」銆傚崟閫夊崟閫変腑锛屽閫夊彲澶氶�変腑锛屽垽鏂彲鍋氭垚A瀵笲閿欍��", [
+ col("sort_no", "int", None, "搴忓彿"),
+ col("option_label", "varchar(10)", None, "A/B/C/D..."),
+ col("option_content", "text", None, "閫夐」鍐呭"),
+ col("is_correct", "varchar(10)", "NOT NULL DEFAULT '0'", "鏄惁姝g‘绛旀 1鏄� 0鍚�"),
+], child=True, delete_user=False, indexes=[("idx", "idx_tms_qopt_fid", ["f_foreign_id"], False)])
+
+add("tms_paper", "璇曞嵎銆傚け鏁堣瘯鍗蜂笉寰楀啀鐢ㄤ簬鏂颁换鍔★紱鏀瑰嵎涓嶅奖鍝嶅凡鑰冭褰曘��", [
+ col("paper_name", "varchar(300)", "NOT NULL", "璇曞嵎鍚嶇О"),
+ col("biz_status", "varchar(20)", "NOT NULL DEFAULT 'open'", "open寮�鏀� / closed涓嶅紑鏀� / invalid澶辨晥"),
+ col("duration_min", "int", "NOT NULL DEFAULT 60", "鑰冭瘯鏃堕暱锛堝垎閽燂級"),
+ col("exam_start", "timestamp", None, "璇曞嵎绾у紑鑰冩椂闂达紝鍙┖锛屼换鍔′笂鍐嶈"),
+ col("exam_end", "timestamp", None, "璇曞嵎绾х粨鑰冩椂闂达紝鍙┖"),
+ col("score_publish_time", "timestamp", None, "鎴愮哗鍏竷鏃堕棿锛岀┖=绔嬪嵆鍏竷"),
+ col("pass_score", "numeric(8,1)", None, "鍚堟牸鍒嗘暟"),
+ col("total_score", "numeric(8,1)", None, "璇曞嵎鎬诲垎"),
+ col("sort_mode", "varchar(20)", "NOT NULL DEFAULT 'paper'", "paper璇曞嵎鑷韩椤哄簭 / random闅忔満"),
+ col("has_subjective", "varchar(10)", "NOT NULL DEFAULT '0'", "鏄惁鍚富瑙傞锛岀粍鍗锋椂鍥炲啓"),
+ col("remark", "text", None, "澶囨敞"),
+], indexes=[("idx", "idx_tms_paper_status", ["biz_status"], False)])
+
+add("tms_paper_section", "璇曞嵎绔犺妭锛屽彲鎸夐鍨嬪綊绫�", [
+ col("section_name", "varchar(100)", None, "绔犺妭鍚嶏紝濡� 涓�銆佸崟閫夐"),
+ col("question_type", "varchar(20)", None, "榛樿棰樺瀷"),
+ col("sort_no", "int", None, "鎺掑簭"),
+], child=True, delete_user=False, indexes=[("idx", "idx_tms_psec_fid", ["f_foreign_id"], False)])
+
+add("tms_paper_question", "璇曞嵎缁勬垚璇曢锛堝綋鍓嶇増鏈級銆傚凡鑰冭瘯鍗疯蛋 tms_exam_item 蹇収銆�", [
+ col("section_id", "varchar(50)", None, "绔犺妭ID"),
+ col("question_id", "varchar(50)", "NOT NULL", "璇曢ID"),
+ col("score", "numeric(8,1)", None, "鏈鍒嗗��"),
+ col("sort_no", "int", None, "鑷畾涔夋帓搴�"),
+], child=True, delete_user=False, indexes=[
+ ("idx", "idx_tms_pq_fid", ["f_foreign_id"], False),
+ ("idx", "idx_tms_pq_qid", ["question_id"], False),
+])
+
+# ===== 3 宀椾綅/骞村害璁″垝 =====
+add("tms_post_catalog", "宀椾綅鍩硅鐩綍銆傛敮鎸佸崌鐗堛�佸簾寮冦�傚鎵归�氳繃鍚庡彲鏁村崟澶嶅埗銆�", [
+ col("catalog_no", "varchar(50)", None, "鐩綍缂栧彿"),
+ col("post_id", "varchar(50)", "NOT NULL", "宀椾綅"),
+ col("post_name", "varchar(100)", None, "宀椾綅鍚嶇О鍐椾綑"),
+ col("dept_id", "varchar(50)", None, "鎵�灞為儴闂�"),
+ col("version_no", "int", "NOT NULL DEFAULT 1", "鐗堟湰鍙凤紝鍗囩増+1"),
+ col("biz_status", "varchar(20)", "NOT NULL DEFAULT 'valid'", "valid鏈夋晥 / revoked搴熷純"),
+ col("remark", "varchar(500)", None, "澶囨敞"),
+], flow=True, indexes=[
+ ("idx", "idx_tms_pcat_post", ["post_id", "biz_status"], False),
+])
+
+add("tms_post_catalog_item", "宀椾綅鍩硅鐩綍鍐呭琛岋紝鏀寔鍥炶溅澶氳銆佹墜鍔ㄦ帓搴�", [
+ col("sort_no", "int", None, "椤哄簭"),
+ col("subject", "varchar(500)", None, "鍩硅鍐呭"),
+ col("train_mode", "varchar(50)", None, "鍩硅鏂瑰紡"),
+ col("hours", "numeric(8,1)", None, "璇炬椂"),
+ col("eval_mode", "varchar(50)", None, "鑰冩牳鏂瑰紡"),
+ col("material_id", "varchar(50)", None, "鏁欐潗"),
+ col("course_id", "varchar(50)", None, "璇剧▼"),
+], child=True, delete_user=False, indexes=[("idx", "idx_tms_pcati_fid", ["f_foreign_id"], False)])
+
+add("tms_post_plan", "宀椾綅鍩硅璁″垝銆傜敱鐩綍鎺ㄩ�侊紝鍙敓鎴愪釜浜哄矖浣嶅煿璁拰鍩硅浠诲姟銆�", [
+ col("plan_no", "varchar(50)", "NOT NULL", "璁″垝缂栧彿 JP+YYYYMM+4锛屽 JP2026090012"),
+ col("post_id", "varchar(50)", None, "宀椾綅缂栫爜/ID"),
+ col("post_name", "varchar(100)", None, "宀椾綅鍚嶇О"),
+ col("dept_id", "varchar(50)", None, "鎵�灞為儴闂�"),
+ col("plan_end_time", "date", None, "璁″垝缁撴潫鏃堕棿锛屽甫鍏ヤ釜浜哄矖浣嶅煿璁�"),
+ col("post_desc", "text", None, "宀椾綅鎻忚堪"),
+ col("trainees", "text", None, "鍩硅瀵硅薄锛岀敤鎴峰閫�"),
+ col("catalog_id", "varchar(50)", None, "鏉ユ簮鐩綍"),
+ col("publish_status", "varchar(20)", "NOT NULL DEFAULT 'draft'", "draft鏈彂甯� / published宸插彂甯�"),
+ col("remark", "varchar(500)", None, "澶囨敞"),
+], flow=True, indexes=[
+ ("uk", "uk_tms_pplan_no", ["plan_no"], True),
+ ("idx", "idx_tms_pplan_post", ["post_id", "publish_status"], False),
+])
+
+add("tms_post_plan_item", "宀椾綅鍩硅璁″垝鏄庣粏", [
+ col("plan_time", "date", None, "鍩硅璁″垝鏃堕棿"),
+ col("teacher_id", "varchar(50)", None, "鍩硅甯�"),
+ col("subject", "varchar(500)", None, "鍩硅鍐呭"),
+ col("train_mode", "varchar(50)", None, "鍩硅鏂瑰紡"),
+ col("hours", "numeric(8,1)", None, "鍩硅璇炬椂"),
+ col("eval_mode", "varchar(50)", None, "鑰冩牳鏂瑰紡"),
+ col("material_id", "varchar(50)", None, "鏁欐潗"),
+ col("task_id", "varchar(50)", None, "鐢熸垚鐨勫煿璁换鍔D"),
+ col("line_status", "varchar(20)", None, "pending寰呭彂甯� / published宸插彂甯� / done宸插畬鎴�"),
+], child=True, delete_user=False, indexes=[("idx", "idx_tms_pplani_fid", ["f_foreign_id"], False)])
+
+add("tms_person_post", "涓汉宀椾綅鍩硅娓呭崟銆傝浆宀楁椂瀛愯〃鏍囪宸插煿璁繃鍒欎笉鐢熸垚浠诲姟銆�", [
+ col("plan_id", "varchar(50)", None, "宀椾綅鍩硅璁″垝ID"),
+ col("catalog_id", "varchar(50)", None, "鐩綍ID"),
+ col("user_id", "varchar(50)", "NOT NULL", "鍩硅瀵硅薄"),
+ col("post_id", "varchar(50)", None, "宀椾綅"),
+ col("plan_end_time", "date", None, "璁″垝缁撴潫鏃堕棿锛堢敱璁″垝甯﹀嚭锛�"),
+ col("biz_status", "varchar(20)", "NOT NULL DEFAULT 'doing'", "doing鎵ц涓� / done瀹屾垚 / returned閫�鍥�"),
+ col("person_remark", "varchar(500)", None, "涓汉澶囨敞"),
+ col("audit_remark", "varchar(500)", None, "瀹℃牳澶囨敞"),
+ col("approve_remark", "varchar(500)", None, "瀹℃壒澶囨敞"),
+], flow=True, indexes=[
+ ("idx", "idx_tms_ppost_user", ["user_id", "biz_status"], False),
+ ("idx", "idx_tms_ppost_plan", ["plan_id"], False),
+])
+
+add("tms_person_post_item", "涓汉宀椾綅鍩硅鏄庣粏", [
+ col("subject", "varchar(500)", None, "鍩硅鍐呭"),
+ col("train_mode", "varchar(50)", None, "鍩硅鏂瑰紡"),
+ col("hours", "numeric(8,1)", None, "璇炬椂"),
+ col("eval_mode", "varchar(50)", None, "鑰冩牳鏂瑰紡"),
+ col("already_trained", "varchar(10)", "NOT NULL DEFAULT '0'", "杞矖锛氭槸鍚﹀凡鍩硅杩囷紝鏄垯涓嶇敓鎴愪换鍔�"),
+ col("task_id", "varchar(50)", None, "鐢熸垚鐨勫煿璁换鍔�"),
+ col("line_status", "varchar(20)", None, "鐘舵��"),
+], child=True, delete_user=False, indexes=[("idx", "idx_tms_pposti_fid", ["f_foreign_id"], False)])
+
+add("tms_annual_plan", "骞村害鍩硅璁″垝锛堝叕鍙�/閮ㄩ棬锛夈�傚姙缁撳悗鐢熸垚鍩硅浠诲姟銆傛祦绋嬶細閮ㄩ棬璐熻矗浜衡啋QA璐熻矗浜恒��", [
+ col("plan_year", "int", "NOT NULL", "骞翠唤锛屽埗瀹氭椂榛樿褰撳墠骞�+1"),
+ col("plan_no", "varchar(50)", "NOT NULL", "璁″垝缂栧彿锛屽 20260001"),
+ col("plan_type", "varchar(20)", None, "company鍏徃绾� / dept閮ㄩ棬绾�"),
+ col("author_id", "varchar(50)", None, "鍒跺畾浜�"),
+ col("author_dept_id", "varchar(50)", None, "鍒跺畾閮ㄩ棬"),
+ col("author_date", "date", None, "鍒跺畾鏃ユ湡"),
+ col("version_no", "int", "NOT NULL DEFAULT 1", "鐗堟湰"),
+ col("dept_leader_id", "varchar(50)", None, "閮ㄩ棬璐熻矗浜猴紙绛炬牳灞曠ず锛�"),
+ col("qa_leader_id", "varchar(50)", None, "QA閮ㄨ礋璐d汉"),
+ col("biz_status", "varchar(20)", "NOT NULL DEFAULT 'draft'", "draft鑽夌 / approving瀹℃壒涓� / approved宸叉壒鍑�"),
+ col("remark", "text", None, "澶囨敞"),
+], flow=True, indexes=[
+ ("uk", "uk_tms_aplan_no", ["plan_no"], True),
+ ("idx", "idx_tms_aplan_year", ["plan_year", "plan_type", "author_dept_id"], False),
+])
+
+add("tms_annual_plan_item", "骞村害鍩硅璁″垝鏄庣粏", [
+ col("train_month", "varchar(6)", None, "鍩硅鏈堜唤 YYYYMM锛屽 202609"),
+ col("subject", "varchar(500)", None, "鍩硅鍐呭"),
+ col("train_mode", "varchar(50)", None, "鍩硅鏂瑰紡"),
+ col("trainees", "text", None, "鍩硅瀵硅薄"),
+ col("hours", "numeric(8,1)", None, "鍩硅璇炬椂"),
+ col("teacher_id", "varchar(50)", None, "鍩硅甯�"),
+ col("eval_mode", "varchar(50)", None, "鑰冩牳鏂瑰紡"),
+ col("course_id", "varchar(50)", None, "鍩硅璇剧▼"),
+ col("line_status", "varchar(20)", "NOT NULL DEFAULT 'pending'", "pending寰呮墽琛� / tasked宸茬敓鎴愪换鍔� / done宸插畬鎴� / delayed宸插欢鏈�"),
+ col("task_id", "varchar(50)", None, "鐢熸垚鐨勫煿璁换鍔�"),
+], child=True, delete_user=False, indexes=[
+ ("idx", "idx_tms_aplani_fid", ["f_foreign_id"], False),
+ ("idx", "idx_tms_aplani_month", ["train_month"], False),
+])
+
+add("tms_annual_delay", "骞村害鍩硅璁″垝寤舵湡鐢宠", [
+ col("plan_id", "varchar(50)", "NOT NULL", "骞村害璁″垝ID"),
+ col("item_id", "varchar(50)", None, "璁″垝鏄庣粏ID"),
+ col("old_month", "varchar(6)", None, "鍘熸湀浠�"),
+ col("new_month", "varchar(6)", None, "寤舵湡鑷虫湀浠�"),
+ col("reason", "text", None, "寤舵湡鍘熷洜"),
+ col("biz_status", "varchar(20)", None, "瀹℃壒鐘舵��"),
+], flow=True, indexes=[("idx", "idx_tms_adelay_plan", ["plan_id"], False)])
+
+# ===== 4 鍩硅浠诲姟 / 璁板綍 =====
+add("tms_task", "鍩硅浠诲姟锛堟牳蹇冿級銆傛潵婧愶細宀椾綅璁″垝/骞村害璁″垝/鏂囦欢鐢熸晥/涓存椂/澶栧嚭/鍐嶇淮鎶ゃ��", [
+ col("task_no", "varchar(50)", "NOT NULL", "浠诲姟缂栧彿 TT+YYYYMM+4锛屽 TT2026090618"),
+ col("category", "varchar(50)", None, "鍩硅鍒嗙被锛氫复鏃跺煿璁�/宀椾綅鍩硅璁″垝/骞村害鍩硅璁″垝/鏂囦欢鐢熸晥鍩硅/澶栧嚭鍩硅/鍐嶇淮鎶�"),
+ col("train_type", "varchar(50)", None, "鍩硅绫诲瀷锛堝垪琛ㄥ瓧娈碉紝鍙笌鍒嗙被骞跺瓨锛�"),
+ col("source_type", "varchar(50)", None, "鏉ユ簮绫诲瀷缂栫爜"),
+ col("source_plan_no", "varchar(50)", None, "鏉ユ簮璁″垝缂栧彿"),
+ col("source_id", "varchar(50)", None, "鏉ユ簮涓昏〃ID"),
+ col("source_item_id", "varchar(50)", None, "鏉ユ簮鏄庣粏ID锛屽綊妗e洖鍐�"),
+ col("subject", "text", None, "鍩硅涓婚"),
+ col("key_points", "text", None, "鍩硅瑕佺偣锛岀鍒板墠寮哄埗闃呰"),
+ col("material_id", "varchar(50)", None, "鍩硅鏁欐潗"),
+ col("material_no", "varchar(50)", None, "鏁欐潗缂栧彿鍐椾綑"),
+ col("place_id", "varchar(50)", None, "鍩硅鍦扮偣"),
+ col("place_no", "varchar(50)", None, "鍦扮偣缂栧彿鍐椾綑"),
+ col("place_name", "varchar(200)", None, "鍦扮偣鍚嶇О鍐椾綑"),
+ col("start_time", "timestamp", None, "鍩硅寮�濮嬫椂闂�"),
+ col("end_time", "timestamp", None, "鍩硅缁撴潫鏃堕棿"),
+ col("close_time", "timestamp", None, "浠诲姟鍏抽棴鏃堕棿锛屽埌杈惧悗璁板綍杩涘緟褰掓。"),
+ col("paper_id", "varchar(50)", None, "璇曞嵎"),
+ col("paper_name", "varchar(300)", None, "璇曞嵎鍚嶇О鍐椾綑"),
+ col("exam_start", "timestamp", None, "鑰冭瘯寮�濮嬫椂闂�"),
+ col("exam_end", "timestamp", None, "鑰冭瘯缁撴潫鏃堕棿"),
+ col("teacher_id", "text", None, "鍩硅甯堬紝鍙閫�"),
+ col("train_mode", "varchar(50)", None, "鍩硅鏂瑰紡"),
+ col("eval_mode", "varchar(50)", None, "鍩硅鏁堟灉璇勪及鏂瑰紡/鑰冩牳鏂瑰紡"),
+ col("trainees", "text", None, "鍩硅瀵硅薄锛岀敤鎴峰閫夛紱鍙戝竷鏃舵媶涓汉浠诲姟"),
+ col("task_kind", "varchar(20)", None, "浠诲姟绫诲瀷锛歯ew鏂板 / continue缁х画(鍐嶇淮鎶�)"),
+ col("prev_task_no", "varchar(50)", None, "涓婁竴娆″煿璁换鍔$紪鍙�"),
+ col("publish_status", "varchar(20)", "NOT NULL DEFAULT 'draft'", "draft鏈彂甯� / published宸插彂甯� / cancelled宸插彇娑� / done宸插畬鎴�"),
+ col("publish_time", "timestamp", None, "鍙戝竷鏃堕棿"),
+ col("retake_limit", "int", "NOT NULL DEFAULT 1", "涓嶅悎鏍煎彲閲嶈�冩鏁�"),
+ col("pass_score", "numeric(8,1)", None, "閫氳繃鍒嗘暟锛岀┖鍒欑敤璇曞嵎"),
+ col("quiz_ratio", "int", None, "鎻愰棶姣斾緥0-100锛岃嚜鍔ㄧ畻搴旀彁闂汉鏁�"),
+ col("sign_rate", "int", None, "绛惧埌鐜�%锛屽彇鏁磋垗灏忔暟"),
+ col("pass_rate", "int", None, "鍚堟牸鐜�%锛屽彇鏁磋垗灏忔暟"),
+ col("unfinished_rate", "int", None, "鏈畬鎴愮巼%"),
+ col("overdue_remind", "varchar(10)", "NOT NULL DEFAULT '0'", "鏄惁閫炬湡鎻愰啋閮ㄩ棬棰嗗"),
+ col("invite_flag", "varchar(10)", "NOT NULL DEFAULT '0'", "鏄惁鍙戦�佸煿璁個璇�"),
+ col("dms_file_id", "varchar(50)", None, "鏂囦欢鐢熸晥鑷姩寮曞叆鐨勯檮浠�"),
+ col("remark", "varchar(500)", None, "澶囨敞"),
+], flow=True, indexes=[
+ ("uk", "uk_tms_task_no", ["task_no"], True),
+ ("idx", "idx_tms_task_status", ["publish_status", "start_time"], False),
+ ("idx", "idx_tms_task_place", ["place_id", "start_time", "end_time"], False),
+ ("idx", "idx_tms_task_source", ["source_type", "source_id"], False),
+ ("idx", "idx_tms_task_teacher", ["teacher_id"], False),
+])
+
+add("tms_task_file", "鍩硅浠诲姟鏁欐潗/闄勪欢娓呭崟锛屽彂甯冨悗鍚屾鍒板煿璁褰�", [
+ col("file_no", "varchar(50)", None, "缂栧彿"),
+ col("file_name", "varchar(300)", None, "鏂囦欢鍚嶇О"),
+ col("material_id", "varchar(50)", None, "鏉ユ簮鏁欐潗"),
+ col("file_json", "text", None, "闄勪欢"),
+ col("can_download", "varchar(10)", "NOT NULL DEFAULT '0'", "寮曠敤鏁欐潗榛樿涓嶅彲涓嬭浇鎵撳嵃"),
+], child=True, delete_user=False, indexes=[("idx", "idx_tms_tfile_fid", ["f_foreign_id"], False)])
+
+add("tms_task_quiz", "鍩硅浠诲姟鎻愰棶棰勮闂鍜岀瓟妗堬紝绛惧埌鍚庡脊鍑�", [
+ col("sort_no", "int", None, "搴忓彿"),
+ col("question", "text", None, "闂"),
+ col("answer", "text", None, "鍙傝�冪瓟妗�"),
+], child=True, delete_user=False, indexes=[("idx", "idx_tms_tquiz_fid", ["f_foreign_id"], False)])
+
+add("tms_person_task", "涓汉鍩硅浠诲姟銆傚彂甯冩椂鎸変汉鎷嗚锛涘鍛樼鑷/绛惧埌/鑰冭瘯鍏ュ彛銆�", [
+ col("task_id", "varchar(50)", "NOT NULL", "鍩硅浠诲姟ID"),
+ col("task_no", "varchar(50)", None, "浠诲姟缂栧彿鍐椾綑"),
+ col("user_id", "varchar(50)", "NOT NULL", "鍩硅瀵硅薄"),
+ col("dept_id", "varchar(50)", None, "褰撴椂閮ㄩ棬蹇収"),
+ col("hours", "numeric(8,1)", None, "瀛︽椂"),
+ col("biz_status", "varchar(20)", "NOT NULL DEFAULT 'todo'", "todo鏈畬鎴� / signed宸茬鍒� / done宸插畬鎴� / expired宸茶繃鏈� / cancelled宸插彇娑�"),
+ col("sign_time", "timestamp", None, "绛惧埌鏃堕棿"),
+ col("sign_mode", "varchar(20)", None, "绛惧埌鏂瑰紡"),
+ col("learn_seconds", "int", None, "鍦ㄧ嚎瀛︿範鏃堕暱绉�"),
+ col("exam_score", "numeric(8,1)", None, "鑰冭瘯鎴愮哗"),
+ col("pass_flag", "varchar(20)", None, "pass鍚堟牸 / fail涓嶅悎鏍� / na鏃犻渶鑰冩牳"),
+ col("guest_flag", "varchar(10)", "NOT NULL DEFAULT '0'", "鏄惁鍚嶅崟澶栫鍒�"),
+], indexes=[
+ ("idx", "idx_tms_ptask_user", ["user_id", "biz_status"], False),
+ ("idx", "idx_tms_ptask_task", ["task_id"], False),
+ ("uk", "uk_tms_ptask_task_user", ["task_id", "user_id"], True),
+])
+
+add("tms_record", "鍩硅璁板綍銆備换鍔″彂甯冨悗鐢熸垚锛涘叧闂椂闂村埌杈捐繘寰呭綊妗c��", [
+ col("record_no", "varchar(50)", None, "璁板綍缂栧彿锛岄粯璁ゅ悓浠诲姟缂栧彿"),
+ col("task_id", "varchar(50)", "NOT NULL", "鍩硅浠诲姟ID"),
+ col("task_no", "varchar(50)", None, "浠诲姟缂栧彿"),
+ col("category", "varchar(50)", None, "鍩硅鍒嗙被"),
+ col("subject", "text", None, "鍩硅涓婚"),
+ col("teacher_id", "text", None, "鍩硅甯�"),
+ col("train_type", "varchar(50)", None, "鍩硅绫诲瀷"),
+ col("train_mode", "varchar(50)", None, "鍩硅鏂瑰紡"),
+ col("eval_mode", "varchar(50)", None, "鑰冩牳鏂瑰紡"),
+ col("start_time", "timestamp", None, "鍩硅寮�濮嬫椂闂达紝濂楁墦妯℃澘鎸夋绱㈠紩鐗堟湰"),
+ col("end_time", "timestamp", None, "鍩硅缁撴潫鏃堕棿"),
+ col("close_time", "timestamp", None, "鍏抽棴鏃堕棿"),
+ col("archive_status", "varchar(20)", "NOT NULL DEFAULT 'pending'", "pending寰呭綊妗� / ready鍙綊妗� / archived宸插綊妗� / invalid鏃犳晥"),
+ col("archive_time", "timestamp", None, "褰掓。鏃堕棿"),
+ col("archiver_id", "varchar(50)", None, "褰掓。浜�"),
+ col("sign_rate", "int", None, "鍙備笌/绛惧埌鐜�% 鍙栨暣鑸嶅皬鏁�"),
+ col("pass_rate", "int", None, "鍚堟牸鐜�%"),
+ col("lecture_file", "text", None, "鍩硅璁蹭箟闄勪欢"),
+ col("paper_sign_file", "text", None, "绾歌川绛惧埌琛ㄩ檮浠�"),
+ col("print_tpl_id", "varchar(50)", None, "鎵撳嵃妯℃澘鐗堟湰"),
+ col("remark", "varchar(500)", None, "澶囨敞"),
+], indexes=[
+ ("uk", "uk_tms_record_task", ["task_id"], True),
+ ("idx", "idx_tms_record_arch", ["archive_status", "start_time"], False),
+])
+
+add("tms_record_sign", "鍩硅璁板綍绛惧埌/缁撴灉鏄庣粏锛堜竴浜轰竴琛岋級", [
+ col("user_id", "varchar(50)", "NOT NULL", "鍩硅瀵硅薄"),
+ col("user_name", "varchar(100)", None, "濮撳悕蹇収"),
+ col("dept_id", "varchar(50)", None, "閮ㄩ棬蹇収"),
+ col("sign_time", "timestamp", None, "绛惧埌鏃堕棿"),
+ col("sign_mode", "varchar(20)", None, "绛惧埌鏂瑰紡"),
+ col("guest_flag", "varchar(10)", "NOT NULL DEFAULT '0'", "鍚嶅崟澶栫鍒�"),
+ col("result_flag", "varchar(20)", None, "pass鍚堟牸 / fail涓嶅悎鏍� / absent鏈弬鍔� / na鏃犻渶鑰冩牳"),
+ col("exam_score", "numeric(8,1)", None, "鑰冭瘯鎴愮哗"),
+ col("practice_score", "numeric(8,1)", None, "瀹炴搷鎴愮哗"),
+ col("quiz_score", "numeric(8,1)", None, "鎻愰棶鎴愮哗"),
+ col("exam_id", "varchar(50)", None, "绛斿嵎ID"),
+ col("judge_user_id", "varchar(50)", None, "缁撴灉鍒ゅ畾浜猴紙鍩硅甯堬級"),
+ col("judge_time", "timestamp", None, "鍒ゅ畾鏃堕棿"),
+], child=True, delete_user=False, indexes=[
+ ("idx", "idx_tms_rsign_fid", ["f_foreign_id"], False),
+ ("idx", "idx_tms_rsign_user", ["user_id"], False),
+])
+
+add("tms_record_change", "鍩硅璁板綍鍙樻洿锛氭敼缁撴潫鏃堕棿銆佸鍔犲煿璁汉鍛樸�傚煿璁笀鍙戣捣鈫掗儴闂ㄩ瀵硷紙棰嗗鍗冲煿璁笀鍒欒烦杩囷級銆�", [
+ col("record_id", "varchar(50)", "NOT NULL", "鍩硅璁板綍ID"),
+ col("task_id", "varchar(50)", None, "浠诲姟ID"),
+ col("change_type", "varchar(20)", None, "end_time鏀圭粨鏉熸椂闂� / add_user澧炲姞浜哄憳"),
+ col("old_end_time", "timestamp", None, "鍘熺粨鏉熸椂闂�"),
+ col("new_end_time", "timestamp", None, "鏂扮粨鏉熸椂闂�"),
+ col("add_users", "text", None, "鏂板浜哄憳"),
+ col("reason", "text", None, "鍙樻洿鍘熷洜"),
+ col("biz_status", "varchar(20)", None, "瀹℃壒鐘舵��"),
+], flow=True, indexes=[("idx", "idx_tms_rchg_rec", ["record_id"], False)])
+
+add("tms_archive_file", "鍩硅璧勬枡褰掓。锛堢數瀛�/绾歌川/瀹炴搷璁板綍鍙岀粨鍚堬級", [
+ col("record_id", "varchar(50)", "NOT NULL", "鍩硅璁板綍ID"),
+ col("task_id", "varchar(50)", None, "浠诲姟ID"),
+ col("archive_type", "varchar(20)", None, "electronic鐢靛瓙 / paper绾歌川 / practice瀹炴搷妗f"),
+ col("file_json", "text", None, "褰掓。璧勬枡"),
+ col("archive_time", "timestamp", None, "褰掓。鏃堕棿"),
+ col("archiver_id", "varchar(50)", None, "褰掓。浜�"),
+ col("remark", "varchar(500)", None, "澶囨敞"),
+], indexes=[("idx", "idx_tms_afile_rec", ["record_id"], False)])
+
+# ===== 5 鑰冭瘯鎵ц =====
+add("tms_exam", "鍦ㄧ嚎鑰冭瘯绛斿嵎銆備腑閫旈��鍑哄彲缁�冿紱鎻愪氦鍚庡瑙傞鑷姩璇勫垎锛屾湁涓昏棰樿繘闃呭嵎銆�", [
+ col("task_id", "varchar(50)", "NOT NULL", "鍩硅浠诲姟"),
+ col("person_task_id", "varchar(50)", None, "涓汉浠诲姟"),
+ col("record_id", "varchar(50)", None, "鍩硅璁板綍"),
+ col("paper_id", "varchar(50)", None, "褰撴椂璇曞嵎ID"),
+ col("paper_name", "varchar(300)", None, "璇曞嵎鍚嶇О蹇収"),
+ col("user_id", "varchar(50)", "NOT NULL", "鑰冪敓"),
+ col("attempt_no", "int", "NOT NULL DEFAULT 1", "绗嚑娆¤�冭瘯"),
+ col("start_time", "timestamp", None, "寮�濮嬩綔绛�"),
+ col("submit_time", "timestamp", None, "浜ゅ嵎鏃堕棿"),
+ col("duration_min", "int", None, "褰撴椂鏃堕暱蹇収"),
+ col("objective_score", "numeric(8,1)", None, "瀹㈣棰樺緱鍒�"),
+ col("subjective_score", "numeric(8,1)", None, "涓昏棰樺緱鍒�"),
+ col("total_score", "numeric(8,1)", None, "鎬诲垎"),
+ col("pass_score", "numeric(8,1)", None, "褰撴椂鍚堟牸鍒嗗揩鐓�"),
+ col("pass_flag", "varchar(20)", None, "pass/fail"),
+ col("grade_status", "varchar(20)", "NOT NULL DEFAULT 'auto'", "auto鏃犻渶闃呭嵎 / pending寰呮壒鏀� / graded宸叉壒鏀�"),
+ col("grader_id", "varchar(50)", None, "闃呭嵎鑰佸笀"),
+ col("grade_time", "timestamp", None, "闃呭嵎鏃堕棿"),
+ col("exam_status", "varchar(20)", "NOT NULL DEFAULT 'doing'", "doing鑰冭瘯涓� / submitted宸蹭氦鍗� / cancelled宸插彇娑�"),
+], indexes=[
+ ("idx", "idx_tms_exam_task_user", ["task_id", "user_id", "attempt_no"], False),
+ ("idx", "idx_tms_exam_grade", ["grade_status"], False),
+ ("idx", "idx_tms_exam_user", ["user_id"], False),
+])
+
+add("tms_exam_item", "绛斿嵎璇曢蹇収锛氶鐩�/閫夐」/绛旀/鍒嗘暟淇濈暀鑰冭瘯褰撴椂鐘舵�侊紝鍚庣画鏀归鏀瑰嵎涓嶅奖鍝嶃��", [
+ col("question_id", "varchar(50)", None, "鍘熻瘯棰業D"),
+ col("question_type", "varchar(20)", None, "棰樺瀷蹇収"),
+ col("stem", "text", None, "棰樺共蹇収"),
+ col("options_json", "text", None, "閫夐」蹇収 JSON"),
+ col("correct_answer", "text", None, "褰撴椂姝g‘绛旀"),
+ col("user_answer", "text", None, "鑰冪敓浣滅瓟"),
+ col("score", "numeric(8,1)", None, "鏈婊″垎"),
+ col("got_score", "numeric(8,1)", None, "寰楀垎"),
+ col("is_subjective", "varchar(10)", "NOT NULL DEFAULT '0'", "鏄惁涓昏棰�"),
+ col("analysis", "text", None, "瑙f瀽蹇収"),
+ col("sort_no", "int", None, "璇曞嵎鍐呴『搴�"),
+], child=True, delete_user=False, indexes=[("idx", "idx_tms_eitem_fid", ["f_foreign_id"], False)])
+
+add("tms_practice", "瀹炴搷鑰冩牳涓昏〃锛屼繚瀛樺悗鍥炲啓鍩硅璁板綍", [
+ col("task_id", "varchar(50)", "NOT NULL", "鍩硅浠诲姟"),
+ col("record_id", "varchar(50)", None, "鍩硅璁板綍"),
+ col("teacher_id", "varchar(50)", None, "鑰冩牳浜�"),
+ col("practice_time", "timestamp", None, "鑰冩牳鏃堕棿"),
+ col("remark", "varchar(500)", None, "澶囨敞"),
+], indexes=[("idx", "idx_tms_prac_task", ["task_id"], False)])
+
+add("tms_practice_item", "瀹炴搷鑰冩牳鏄庣粏", [
+ col("user_id", "varchar(50)", "NOT NULL", "鍩硅瀵硅薄"),
+ col("content", "text", None, "鑰冩牳鍐呭"),
+ col("score", "numeric(8,1)", None, "鍒嗘暟"),
+ col("result_flag", "varchar(20)", None, "鍚堟牸/涓嶅悎鏍�"),
+], child=True, delete_user=False, indexes=[("idx", "idx_tms_praci_fid", ["f_foreign_id"], False)])
+
+add("tms_quiz_score", "鐜板満鎻愰棶鑰冩牳鎵撳垎锛屼繚瀛樺悗鍥炲啓鍩硅璁板綍", [
+ col("task_id", "varchar(50)", "NOT NULL", "鍩硅浠诲姟"),
+ col("record_id", "varchar(50)", None, "鍩硅璁板綍"),
+ col("user_id", "varchar(50)", "NOT NULL", "鍩硅瀵硅薄"),
+ col("question", "text", None, "鎻愰棶鍐呭"),
+ col("answer", "text", None, "鍥炵瓟"),
+ col("score", "numeric(8,1)", None, "鍒嗘暟"),
+ col("teacher_id", "varchar(50)", None, "鎻愰棶浜�"),
+ col("quiz_time", "timestamp", None, "鏃堕棿"),
+], indexes=[
+ ("idx", "idx_tms_qscore_task", ["task_id"], False),
+ ("idx", "idx_tms_qscore_user", ["user_id"], False),
+])
+
+# ===== 6 妗f =====
+add("tms_person_file", "涓汉鍩硅妗f銆傚叆鑱岃嚜鍔ㄥ缓妗o紱涓�浜轰竴妗c��", [
+ col("archive_no", "varchar(50)", "NOT NULL", "妗f缂栧彿 AR+YYYY+4"),
+ col("archive_date", "date", None, "寤烘。鏃堕棿"),
+ col("user_id", "varchar(50)", "NOT NULL", "鍛樺伐"),
+ col("user_name", "varchar(100)", None, "濮撳悕鍐椾綑"),
+ col("dept_id", "varchar(50)", None, "鎵�灞為儴闂�"),
+ col("hire_date", "date", None, "鍏ヨ亴鏃堕棿"),
+ col("leave_date", "date", None, "绂昏亴鏃堕棿"),
+ col("main_post_id", "varchar(50)", None, "涓昏宀椾綅"),
+ col("part_post_id", "varchar(50)", None, "鍏艰亴宀椾綅"),
+ col("biz_status", "varchar(20)", "NOT NULL DEFAULT 'valid'", "valid鍦ㄨ亴 / left绂昏亴"),
+], indexes=[
+ ("uk", "uk_tms_pfile_user", ["user_id"], True),
+ ("uk", "uk_tms_pfile_no", ["archive_no"], True),
+])
+
+add("tms_person_work", "涓汉鍩硅妗f-宸ヤ綔灞ュ巻", [
+ col("company_name", "varchar(200)", None, "鍏徃鍚嶇О"),
+ col("post_name", "varchar(100)", None, "浠昏亴宀椾綅"),
+ col("hire_date", "date", None, "鍏ヨ亴鏃堕棿"),
+ col("leave_date", "date", None, "绂昏亴鏃堕棿"),
+], child=True, delete_user=False, indexes=[("idx", "idx_tms_pwork_fid", ["f_foreign_id"], False)])
+
+add("tms_person_cert", "璧勮川鏂囦欢/鑾峰緱璇佷功銆傝祫璐ㄦ枃浠剁鐞嗕笌妗f瀛愯〃鍏辩敤姝よ〃銆�", [
+ col("archive_id", "varchar(50)", None, "涓汉妗fID锛屾棤澶栭敭瀛楁鏃剁敱涓讳粠鍐欏叆f_foreign_id"),
+ col("user_id", "varchar(50)", None, "鍛樺伐锛屽垪琛ㄦā鍧楃敤"),
+ col("cert_no", "varchar(100)", None, "璇佷功缂栧彿"),
+ col("cert_name", "varchar(200)", None, "璇佷功鍚嶇О"),
+ col("gain_date", "date", None, "鑾峰緱鏃堕棿"),
+ col("expire_date", "date", None, "鏈夋晥鏈熻嚦"),
+ col("cert_file", "text", None, "璇佷功闄勪欢"),
+ col("cert_status", "varchar(20)", None, "valid鏈夋晥 / near杩戞晥鏈� / invalid鏃犳晥"),
+], child=True, indexes=[
+ ("idx", "idx_tms_pcert_fid", ["f_foreign_id"], False),
+ ("idx", "idx_tms_pcert_user", ["user_id"], False),
+])
+
+add("tms_person_train", "涓汉鍩硅鐩綍锛堝綊妗e揩鐓э級銆傝褰曞綊妗e悗鍐欏叆锛屾弧瓒冲璁°��", [
+ col("archive_id", "varchar(50)", "NOT NULL", "涓汉妗fID"),
+ col("user_id", "varchar(50)", "NOT NULL", "鍛樺伐"),
+ col("record_id", "varchar(50)", None, "鍩硅璁板綍ID"),
+ col("record_no", "varchar(50)", None, "鍩硅璁板綍缂栧彿"),
+ col("category", "varchar(50)", None, "鍩硅绫诲瀷/鍒嗙被"),
+ col("subject", "text", None, "鍩硅鍐呭"),
+ col("train_mode", "varchar(50)", None, "鍩硅鏂瑰紡"),
+ col("teacher_id", "text", None, "鍩硅甯�"),
+ col("eval_mode", "varchar(50)", None, "鑰冩牳鏂瑰紡"),
+ col("pass_flag", "varchar(20)", None, "鏄惁鍚堟牸"),
+ col("start_time", "timestamp", None, "鍩硅寮�濮嬫椂闂�"),
+ col("end_time", "timestamp", None, "鍩硅缁撴潫鏃堕棿"),
+ col("hours", "numeric(8,1)", None, "瀛︽椂"),
+], indexes=[
+ ("idx", "idx_tms_ptrain_user", ["user_id"], False),
+ ("idx", "idx_tms_ptrain_arch", ["archive_id"], False),
+ ("idx", "idx_tms_ptrain_rec", ["record_id"], False),
+])
+
+# ===== 7 鍏朵粬娴佺▼ =====
+add("tms_out_train", "澶栧嚭鍩硅", [
+ col("apply_no", "varchar(50)", None, "鐢宠鍗曞彿"),
+ col("applicant_id", "varchar(50)", None, "鐢宠浜�"),
+ col("apply_dept_id", "varchar(50)", None, "鐢宠閮ㄩ棬"),
+ col("org_name", "varchar(200)", None, "澶栭儴鏈烘瀯"),
+ col("subject", "varchar(500)", None, "鍩硅涓婚"),
+ col("start_time", "timestamp", None, "寮�濮�"),
+ col("end_time", "timestamp", None, "缁撴潫"),
+ col("hours", "numeric(8,1)", None, "瀛︽椂"),
+ col("file_json", "text", None, "璧勬枡闄勪欢"),
+ col("biz_status", "varchar(20)", None, "瀹℃壒鐘舵��"),
+], flow=True, indexes=[("uk", "uk_tms_out_no", ["apply_no"], True)])
+
+add("tms_transfer_eval", "杞矖浜哄憳鑰冩牳閴村畾", [
+ col("user_id", "varchar(50)", "NOT NULL", "杞矖浜哄憳"),
+ col("from_post_id", "varchar(50)", None, "鍘熷矖浣�"),
+ col("to_post_id", "varchar(50)", None, "鏂板矖浣�"),
+ col("eval_result", "varchar(50)", None, "閴村畾缁撹"),
+ col("remark", "text", None, "璇存槑"),
+ col("biz_status", "varchar(20)", None, "瀹℃壒鐘舵��"),
+], flow=True, indexes=[("idx", "idx_tms_teval_user", ["user_id"], False)])
+
+add("tms_newcomer_eval", "鏂板憳宸ュ煿璁晥鏋滆�冭瘎", [
+ col("user_id", "varchar(50)", "NOT NULL", "鏂板憳宸�"),
+ col("eval_result", "varchar(50)", None, "鑰冭瘎缁撹"),
+ col("remark", "text", None, "璇存槑"),
+ col("biz_status", "varchar(20)", None, "瀹℃壒鐘舵��"),
+], flow=True, indexes=[("idx", "idx_tms_neval_user", ["user_id"], False)])
+
+add("tms_return_eval", "澶嶅矖鍛樺伐鍩硅鏁堟灉鑰冭瘎", [
+ col("user_id", "varchar(50)", "NOT NULL", "澶嶅矖鍛樺伐"),
+ col("eval_result", "varchar(50)", None, "鑰冭瘎缁撹"),
+ col("remark", "text", None, "璇存槑"),
+ col("biz_status", "varchar(20)", None, "瀹℃壒鐘舵��"),
+], flow=True, indexes=[("idx", "idx_tms_reval_user", ["user_id"], False)])
+
+add("tms_print_template", "鍩硅璁板綍濂楁墦妯℃澘鐗堟湰銆傛寜鍩硅寮�濮嬫椂闂村尮閰嶇敓鏁堢増鏈��", [
+ col("tpl_code", "varchar(50)", "NOT NULL", "妯℃澘缂栫爜锛屽 TRAIN_RECORD"),
+ col("version_no", "int", "NOT NULL", "鐗堟湰鍙�"),
+ col("effective_from", "date", "NOT NULL", "鐢熸晥璧峰锛堟寜鍩硅寮�濮嬫椂闂寸储寮曪級"),
+ col("effective_to", "date", None, "鐢熸晥鎴锛岀┖=褰撳墠"),
+ col("file_json", "text", None, "妯℃澘鏂囦欢"),
+ col("enabled", "varchar(10)", "NOT NULL DEFAULT '1'", "鍚敤"),
+], indexes=[("idx", "idx_tms_ptpl_code", ["tpl_code", "effective_from"], False)])
+
+add("tms_remind_rule", "鎻愰啋瑙勫垯锛氳鍒掑埌鏈熷墠涓�涓湀銆佹巿鏉冨埌鏈熴�佷釜浜哄矖浣嶅煿璁椂闄愮瓑", [
+ col("rule_code", "varchar(50)", "NOT NULL", "瑙勫垯缂栫爜"),
+ col("rule_name", "varchar(100)", None, "鍚嶇О"),
+ col("biz_type", "varchar(50)", None, "annual_plan/admin_auth/person_post/task_overdue"),
+ col("days_before", "int", "NOT NULL DEFAULT 30", "鎻愬墠澶╂暟"),
+ col("notify_roles", "varchar(200)", None, "閫氱煡瑙掕壊锛氶儴闂ㄥ煿璁鐞嗗憳,鍩硅甯�,閮ㄩ棬棰嗗"),
+ col("enabled", "varchar(10)", "NOT NULL DEFAULT '1'", "鍚敤"),
+], indexes=[("uk", "uk_tms_remind_code", ["rule_code"], True)])
+
+
+HEADER = '''-- =============================================================================
+-- 鐜绘�濋煬 TMS 鍩硅绠$悊绯荤粺 路 PostgreSQL 18 寤鸿〃鑴氭湰
+-- 閫傜敤锛欽NPF 6.x锛堝瓧娈甸鏍煎榻愮幇鏈� lims_/dms_ 琛級
+-- 鏉ユ簮锛氱幓鎬濋煬TMS鍔熻兘璁捐涔� V1 + 鑰侀浼佺晫闈㈠瓧娈�
+--
+-- 浣跨敤
+-- 1) 鎺ㄨ崘锛欽NPF銆屾暟鎹缓妯°�嶆寜琛ㄦ竻鍗曞缓琛紝鍐嶇粦琛ㄥ崟锛堢郴缁熷瓧娈典細鑷姩甯︼級
+-- 2) 鎴栧厛鎵ц鏈剼鏈紝鍐嶅埌鏁版嵁寤烘ā銆屽悓姝ユ暟鎹〃銆�
+--
+-- 绾﹀畾
+-- f_id 涓婚敭 varchar(50)锛涘瓙琛� f_foreign_id = 涓昏〃 f_id
+-- 娴佺▼琛ㄥ崟鍚� f_flow_id / f_flow_task_id / f_flow_state
+-- 鐢ㄦ埛/缁勭粐/宀椾綅瀛� JNPF ID锛涘閫� text锛涢檮浠� text(JSON)
+-- 涓氬姟鐘舵�佸瓨缂栫爜锛岄�夐」鐢� JNPF 瀛楀吀
+-- =============================================================================
+
+BEGIN;
+'''
+
+FOOTER = '''
+-- =============================================================================
+-- 棰勭疆鏁版嵁锛堝煿璁柟寮� / 绛惧埌鏂瑰紡 / 璇勪及鏂瑰紡 / 鎻愰啋瑙勫垯锛�
+-- =============================================================================
+INSERT INTO tms_train_mode (f_id, mode_code, mode_name, sign_rule, allow_guest_sign, same_day_required, place_required, sort_no, enabled, f_delete_mark)
+VALUES
+ ('tms_mode_onsite', 'onsite', '闆嗕腑鎺堣', 'scan', '1', '1', '1', 1, '1', 0),
+ ('tms_mode_practice', 'practice', '鎿嶄綔鎺堣', 'scan', '0', '1', '1', 2, '1', 0),
+ ('tms_mode_online', 'online', '鍦ㄧ嚎瀛︿範', 'both', '0', '0', '0', 3, '1', 0)
+ON CONFLICT (f_id) DO NOTHING;
+
+INSERT INTO tms_sign_mode (f_id, mode_code, mode_name, tip_seconds, match_roster, enabled, f_delete_mark)
+VALUES
+ ('tms_sign_app', 'app_scan', 'APP鎵爜', 30, '1', '1', 0),
+ ('tms_sign_wx', 'wx_scan', '寰俊鎵爜', 30, '1', '1', 0),
+ ('tms_sign_online', 'online', '鍦ㄧ嚎绛惧埌', 30, '1', '1', 0)
+ON CONFLICT (f_id) DO NOTHING;
+
+INSERT INTO tms_eval_mode (f_id, mode_code, mode_name, need_paper, need_quiz, need_practice, enabled, f_delete_mark)
+VALUES
+ ('tms_eval_quiz', 'quiz', '鎻愰棶', '0', '1', '0', '1', 0),
+ ('tms_eval_practice', 'practice', '鐜板満鎿嶄綔', '0', '0', '1', '1', 0),
+ ('tms_eval_exam', 'exam', '鍦ㄧ嚎鑰冭瘯', '1', '0', '0', '1', 0),
+ ('tms_eval_none', 'none', '鏃犻渶鑰冩牳', '0', '0', '0', '1', 0)
+ON CONFLICT (f_id) DO NOTHING;
+
+INSERT INTO tms_remind_rule (f_id, rule_code, rule_name, biz_type, days_before, notify_roles, enabled, f_delete_mark)
+VALUES
+ ('tms_rm_annual', 'annual_due', '骞村害璁″垝鍒版湡鍓嶄竴涓湀', 'annual_plan', 30, '閮ㄩ棬鍩硅绠$悊鍛�,鍩硅甯�', '1', 0),
+ ('tms_rm_auth', 'auth_due', '绠$悊鍛樻巿鏉冨埌鏈熷墠涓�涓湀', 'admin_auth', 30, '鍩硅绠$悊鍛�,鍩硅甯�', '1', 0),
+ ('tms_rm_overdue','task_overdue','鍩硅閫炬湡鎻愰啋閮ㄩ棬棰嗗', 'task_overdue', 0, '閮ㄩ棬棰嗗', '1', 0)
+ON CONFLICT (f_id) DO NOTHING;
+
+COMMIT;
+'''
+
+def sql_type_line(c):
+ name, typ, extra, _ = c
+ extra_s = f" {extra}" if extra else ""
+ return f" {name:<24} {typ}{extra_s}"
+
+def emit_table(t):
+ lines = []
+ lines.append(f"-- {t['comment']}")
+ lines.append(f"CREATE TABLE IF NOT EXISTS {t['name']} (")
+ col_lines = []
+ for c in t["cols"]:
+ col_lines.append(sql_type_line(c))
+ col_lines.append(f" CONSTRAINT pk_{t['name']} PRIMARY KEY (f_id)")
+ lines.append(",\n".join(col_lines))
+ lines.append(");")
+ lines.append(f"COMMENT ON TABLE {t['name']} IS '{t['comment'].replace(chr(39), chr(39)*2)}';")
+ for c in t["cols"]:
+ if c[3]:
+ cmt = c[3].replace("'", "''")
+ lines.append(f"COMMENT ON COLUMN {t['name']}.{c[0]} IS '{cmt}';")
+ for kind, iname, cols, uniqueish in t["indexes"]:
+ colsql = ", ".join(cols)
+ if kind == "uk":
+ # unique among not-deleted; nullable columns allowed multiple NULL in PG unique, extra WHERE keeps it simple
+ lines.append(f"CREATE UNIQUE INDEX IF NOT EXISTS {iname} ON {t['name']} ({colsql}) WHERE f_delete_mark = 0;")
+ else:
+ lines.append(f"CREATE INDEX IF NOT EXISTS {iname} ON {t['name']} ({colsql});")
+ lines.append("")
+ return "\n".join(lines)
+
+out = [HEADER]
+groups = [
+ ("1. 鍩虹璁剧疆", ["tms_place","tms_trainer","tms_trainer_work","tms_trainer_exp","tms_admin_auth","tms_train_mode","tms_sign_mode","tms_eval_mode","tms_course","tms_material"]),
+ ("2. 棰樺簱璇曞嵎", ["tms_question_bank","tms_question","tms_question_option","tms_paper","tms_paper_section","tms_paper_question"]),
+ ("3. 宀椾綅/骞村害璁″垝", ["tms_post_catalog","tms_post_catalog_item","tms_post_plan","tms_post_plan_item","tms_person_post","tms_person_post_item","tms_annual_plan","tms_annual_plan_item","tms_annual_delay"]),
+ ("4. 鍩硅浠诲姟涓庤褰�", ["tms_task","tms_task_file","tms_task_quiz","tms_person_task","tms_record","tms_record_sign","tms_record_change","tms_archive_file"]),
+ ("5. 鑰冭瘯涓庤�冩牳", ["tms_exam","tms_exam_item","tms_practice","tms_practice_item","tms_quiz_score"]),
+ ("6. 涓汉妗f", ["tms_person_file","tms_person_work","tms_person_cert","tms_person_train"]),
+ ("7. 鍏朵粬娴佺▼涓庨厤缃�", ["tms_out_train","tms_transfer_eval","tms_newcomer_eval","tms_return_eval","tms_print_template","tms_remind_rule"]),
+]
+by_name = {t["name"]: t for t in tables}
+for title, names in groups:
+ out.append(f"-- ---------------------------------------------------------------------------")
+ out.append(f"-- {title}")
+ out.append(f"-- ---------------------------------------------------------------------------")
+ for n in names:
+ out.append(emit_table(by_name[n]))
+out.append(FOOTER)
+
+sql_path = Path(r"D:/qiansheng/demo/flowApi/docs/tms/tms_schema_pg.sql")
+sql_path.write_text("\n".join(out), encoding="utf-8")
+print("tables", len(tables), "bytes", sql_path.stat().st_size)
--
Gitblit v1.8.0