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 +++++++++++++
 docs/tms/gen_xlsx.py             |  222 +++
 docs/tms/玻思韬TMS-JNPF数据表设计.md     |  269 ++++
 docs/tms/玻思韬TMS-JNPF数据表字段清单.xlsx |    0 
 docs/tms/tms_schema_pg.sql       | 2171 +++++++++++++++++++++++++++++++++++++
 5 files changed, 3,448 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, "涓汉妗fID锛屾棤澶栭敭瀛楁鏃剁敱涓讳粠鍐欏叆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", "涓汉妗fID"),
+    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)
diff --git a/docs/tms/gen_xlsx.py b/docs/tms/gen_xlsx.py
new file mode 100644
index 0000000..f5c7565
--- /dev/null
+++ b/docs/tms/gen_xlsx.py
@@ -0,0 +1,222 @@
+# -*- coding: utf-8 -*-
+from pathlib import Path
+import re
+from openpyxl import Workbook
+from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
+from openpyxl.utils import get_column_letter
+
+sql = Path(r"D:/qiansheng/demo/flowApi/docs/tms/tms_schema_pg.sql").read_text(encoding="utf-8")
+tables = []
+current = None
+block = None
+create_map = {}
+for line in sql.splitlines():
+    m = re.match(r"CREATE TABLE IF NOT EXISTS (\w+)", line)
+    if m:
+        current = {"name": m.group(1), "comment": "", "cols": []}
+        tables.append(current)
+        block = [line]
+        continue
+    if block is not None:
+        block.append(line)
+        if line.startswith(");"):
+            cols = []
+            for ln in block[1:]:
+                if "CONSTRAINT" in ln or ln.strip().startswith(");") or not ln.strip():
+                    continue
+                ln2 = ln.strip().rstrip(",")
+                parts = ln2.split()
+                if len(parts) >= 2:
+                    cols.append((parts[0], " ".join(parts[1:])))
+            create_map[current["name"]] = cols
+            block = None
+        continue
+    if current is None:
+        continue
+    m = re.match(r"COMMENT ON TABLE (\w+) IS '(.+)';", line)
+    if m and current["name"] == m.group(1):
+        current["comment"] = m.group(2)
+        continue
+    m = re.match(r"COMMENT ON COLUMN (\w+)\.(\w+) IS '(.+)';", line)
+    if m and current["name"] == m.group(1):
+        current["cols"].append((m.group(2), m.group(3)))
+
+FORMS = {
+    "tms_place": ("鍩硅鍦扮偣", "A+YYYYMM+4"),
+    "tms_trainer": ("鍩硅甯堟。妗�", "AR+YYYY+4"),
+    "tms_trainer_work": ("鍩硅甯�-宸ヤ綔缁忓巻", ""),
+    "tms_trainer_exp": ("鍩硅甯�-鍩硅缁忓巻", ""),
+    "tms_admin_auth": ("鍩硅绠$悊鍛樻巿鏉�", ""),
+    "tms_train_mode": ("鍩硅鏂瑰紡", ""),
+    "tms_sign_mode": ("绛惧埌鏂瑰紡", ""),
+    "tms_eval_mode": ("鍩硅鏁堟灉璇勪及鏂瑰紡", ""),
+    "tms_course": ("鍩硅璇剧▼", "KC+YYYYMM+4"),
+    "tms_material": ("鍩硅鏁欐潗", "TM+YYYYMM+4"),
+    "tms_question_bank": ("棰樺簱", ""),
+    "tms_question": ("璇曢", ""),
+    "tms_question_option": ("璇曢閫夐」", ""),
+    "tms_paper": ("璇曞嵎", ""),
+    "tms_paper_section": ("璇曞嵎绔犺妭", ""),
+    "tms_paper_question": ("璇曞嵎璇曢", ""),
+    "tms_post_catalog": ("宀椾綅鍩硅鐩綍", ""),
+    "tms_post_catalog_item": ("宀椾綅鐩綍鏄庣粏", ""),
+    "tms_post_plan": ("宀椾綅鍩硅璁″垝", "JP+YYYYMM+4"),
+    "tms_post_plan_item": ("宀椾綅璁″垝鏄庣粏", ""),
+    "tms_person_post": ("涓汉宀椾綅鍩硅", ""),
+    "tms_person_post_item": ("涓汉宀椾綅鍩硅鏄庣粏", ""),
+    "tms_annual_plan": ("骞村害鍩硅璁″垝", "YYYY+4浣嶆祦姘�"),
+    "tms_annual_plan_item": ("骞村害璁″垝鏄庣粏", ""),
+    "tms_annual_delay": ("骞村害璁″垝寤舵湡", ""),
+    "tms_task": ("鍩硅浠诲姟", "TT+YYYYMM+4"),
+    "tms_task_file": ("浠诲姟鏁欐潗闄勪欢", ""),
+    "tms_task_quiz": ("浠诲姟鎻愰棶棰勮", ""),
+    "tms_person_task": ("涓汉鍩硅浠诲姟", ""),
+    "tms_record": ("鍩硅璁板綍", "榛樿鍚屼换鍔$紪鍙�"),
+    "tms_record_sign": ("绛惧埌/缁撴灉鏄庣粏", ""),
+    "tms_record_change": ("鍩硅璁板綍鍙樻洿", ""),
+    "tms_archive_file": ("鍩硅璧勬枡褰掓。", ""),
+    "tms_exam": ("鍦ㄧ嚎鑰冭瘯绛斿嵎", ""),
+    "tms_exam_item": ("绛斿嵎璇曢蹇収", ""),
+    "tms_practice": ("瀹炴搷鑰冩牳", ""),
+    "tms_practice_item": ("瀹炴搷鑰冩牳鏄庣粏", ""),
+    "tms_quiz_score": ("鐜板満鎻愰棶鑰冩牳", ""),
+    "tms_person_file": ("涓汉鍩硅妗f", "AR+YYYY+4"),
+    "tms_person_work": ("妗f-宸ヤ綔灞ュ巻", ""),
+    "tms_person_cert": ("璧勮川鏂囦欢/璇佷功", ""),
+    "tms_person_train": ("妗f-鍩硅鐩綍蹇収", ""),
+    "tms_out_train": ("澶栧嚭鍩硅", "WC+YYYYMM+4"),
+    "tms_transfer_eval": ("杞矖鑰冩牳閴村畾", ""),
+    "tms_newcomer_eval": ("鏂板憳宸ュ煿璁晥鏋滆�冭瘎", ""),
+    "tms_return_eval": ("澶嶅矖鍩硅鏁堟灉鑰冭瘎", ""),
+    "tms_print_template": ("濂楁墦妯℃澘鐗堟湰", ""),
+    "tms_remind_rule": ("鎻愰啋瑙勫垯", ""),
+}
+
+CTRL = {
+    "user_id": "鐢ㄦ埛",
+    "teacher_id": "鐢ㄦ埛(鍙閫�)",
+    "trainees": "鐢ㄦ埛澶氶��",
+    "admin_user_id": "鐢ㄦ埛",
+    "applicant_id": "鐢ㄦ埛",
+    "author_id": "鐢ㄦ埛",
+    "registrar_id": "鐢ㄦ埛",
+    "grader_id": "鐢ㄦ埛",
+    "archiver_id": "鐢ㄦ埛",
+    "dept_id": "缁勭粐",
+    "apply_dept_id": "缁勭粐",
+    "author_dept_id": "缁勭粐",
+    "scope_dept_id": "缁勭粐澶氶��",
+    "post_id": "宀椾綅",
+    "main_post_id": "宀椾綅",
+    "part_post_id": "宀椾綅",
+    "file_json": "闄勪欢",
+    "cert_file": "闄勪欢",
+    "lecture_file": "闄勪欢",
+    "paper_sign_file": "闄勪欢",
+    "stem": "瀵屾枃鏈�",
+    "key_points": "澶氳",
+    "subject": "澶氳",
+    "start_time": "鏃ユ湡鏃堕棿",
+    "end_time": "鏃ユ湡鏃堕棿",
+    "close_time": "鏃ユ湡鏃堕棿",
+    "exam_start": "鏃ユ湡鏃堕棿",
+    "exam_end": "鏃ユ湡鏃堕棿",
+    "hire_date": "鏃ユ湡",
+    "enabled": "涓嬫媺/寮�鍏�",
+    "biz_status": "涓嬫媺-瀛楀吀",
+    "publish_status": "涓嬫媺-瀛楀吀",
+    "train_mode": "涓嬫媺/鍏宠仈鍩硅鏂瑰紡",
+    "eval_mode": "涓嬫媺/鍏宠仈璇勪及鏂瑰紡",
+    "place_id": "鍏宠仈琛ㄥ崟-鍩硅鍦扮偣",
+    "material_id": "鍏宠仈琛ㄥ崟-鍩硅鏁欐潗",
+    "paper_id": "鍏宠仈琛ㄥ崟-璇曞嵎",
+    "bank_id": "鍏宠仈琛ㄥ崟-棰樺簱",
+    "course_id": "鍏宠仈琛ㄥ崟-璇剧▼",
+    "task_id": "鍏宠仈琛ㄥ崟-鍩硅浠诲姟",
+    "record_id": "鍏宠仈琛ㄥ崟-鍩硅璁板綍",
+}
+
+
+def suggest_ctrl(name, typ):
+    if name in CTRL:
+        return CTRL[name]
+    if name.startswith("f_"):
+        return "绯荤粺瀛楁"
+    if "timestamp" in typ:
+        return "鏃ユ湡鏃堕棿"
+    if typ.startswith("date"):
+        return "鏃ユ湡"
+    if "numeric" in typ or typ.startswith("int"):
+        return "鏁板瓧"
+    if name.endswith("_id"):
+        return "鍏宠仈/鐢ㄦ埛/缁勭粐"
+    if "text" in typ:
+        return "澶氳/闄勪欢/JSON"
+    return "鍗曡/涓嬫媺"
+
+
+wb = Workbook()
+ws = wb.active
+ws.title = "琛ㄦ竻鍗�"
+ws.append(["搴忓彿", "琛ㄥ悕", "涓枃鍚�", "琛ㄨ鏄�", "涓讳粠", "娴佺▼", "鍗曟嵁瑙勫垯"])
+for i, t in enumerate(tables, 1):
+    child = any(c[0] == "f_foreign_id" for c in t["cols"])
+    flow = any(c[0] == "f_flow_id" for c in t["cols"])
+    form_name, rule = FORMS.get(t["name"], (t["comment"][:20], ""))
+    ws.append([i, t["name"], form_name, t["comment"], "瀛愯〃" if child else "涓昏〃", "鏄�" if flow else "鍚�", rule])
+
+ws2 = wb.create_sheet("瀛楁鏄庣粏")
+ws2.append(["琛ㄥ悕", "涓枃鍚�", "瀛楁鍚�", "绫诲瀷", "璇存槑", "JNPF寤鸿鎺т欢"])
+for t in tables:
+    comments = {n: c for n, c in t["cols"]}
+    form_name = FORMS.get(t["name"], (t["comment"][:20], ""))[0]
+    for n, typ in create_map.get(t["name"], []):
+        ws2.append([t["name"], form_name, n, typ, comments.get(n, ""), suggest_ctrl(n, typ)])
+
+ws3 = wb.create_sheet("瀛楀吀涓庡崟鎹鍒�")
+ws3.append(["绫诲瀷", "鍚嶇О", "閫夐」鎴栬鍒�", "鐢ㄩ��"])
+for row in [
+    ("瀛楀吀", "浠诲姟鍙戝竷鐘舵��", "draft鏈彂甯� / published宸插彂甯� / cancelled宸插彇娑� / done宸插畬鎴�", "tms_task.publish_status"),
+    ("瀛楀吀", "鍩硅鍒嗙被", "涓存椂鍩硅 / 宀椾綅鍩硅璁″垝 / 骞村害鍩硅璁″垝 / 鏂囦欢鐢熸晥鍩硅 / 澶栧嚭鍩硅 / 鍐嶇淮鎶�", "tms_task.category"),
+    ("瀛楀吀", "浠诲姟绫诲瀷", "new鏂板 / continue缁х画", "tms_task.task_kind"),
+    ("瀛楀吀", "褰掓。鐘舵��", "pending寰呭綊妗� / ready鍙綊妗� / archived宸插綊妗� / invalid鏃犳晥", "tms_record.archive_status"),
+    ("瀛楀吀", "涓汉浠诲姟鐘舵��", "todo鏈畬鎴� / signed宸茬鍒� / done宸插畬鎴� / expired宸茶繃鏈� / cancelled宸插彇娑�", "tms_person_task.biz_status"),
+    ("瀛楀吀", "鏄惁鍚堟牸", "pass鍚堟牸 / fail涓嶅悎鏍� / absent鏈弬鍔� / na鏃犻渶鑰冩牳", "绛惧埌缁撴灉/鑰冭瘯"),
+    ("瀛楀吀", "棰樺瀷", "single鍗曢�� / multi澶氶�� / judge鍒ゆ柇 / blank濉┖ / essay闂瓟", "璇曢"),
+    ("瀛楀吀", "璇曢璇曞嵎鐘舵��", "open寮�鏀� / closed涓嶅紑鏀� / invalid搴熷純", "棰樺簱璇曞嵎"),
+    ("瀛楀吀", "闅惧害", "easy绠�鍗� / normal涓�鑸� / hard鍥伴毦", "璇曢"),
+    ("瀛楀吀", "闃呭嵎鐘舵��", "auto鏃犻渶闃呭嵎 / pending寰呮壒鏀� / graded宸叉壒鏀�", "tms_exam.grade_status"),
+    ("瀛楀吀", "鏈夋晥鎾ら攢", "valid鏈夋晥鏈熷唴 / revoked宸叉挙閿�", "鍩硅甯�/鎺堟潈/鏁欐潗"),
+    ("瀛楀吀", "璁″垝琛岀姸鎬�", "pending寰呮墽琛� / tasked宸茬敓鎴愪换鍔� / done宸插畬鎴� / delayed宸插欢鏈�", "骞村害璁″垝鏄庣粏"),
+    ("瀛楀吀", "鏁欐潗绫诲瀷", "Word / PPT / Excel / PDF / 瑙嗛 / 闊抽", "鏁欐潗"),
+    ("鍗曟嵁瑙勫垯", "鍦扮偣缂栧彿", "A + YYYYMM + 4浣嶆祦姘�", "tms_place.place_no 渚� A2026090001"),
+    ("鍗曟嵁瑙勫垯", "妗f缂栧彿", "AR + YYYY + 4浣嶆祦姘�", "鍩硅甯�/涓汉妗f 渚� AR20260028"),
+    ("鍗曟嵁瑙勫垯", "鏁欐潗缂栧彿", "TM + YYYYMM + 4浣嶆祦姘�", "tms_material 渚� TM2026090001"),
+    ("鍗曟嵁瑙勫垯", "宀椾綅璁″垝缂栧彿", "JP + YYYYMM + 4浣嶆祦姘�", "tms_post_plan 渚� JP2026090012"),
+    ("鍗曟嵁瑙勫垯", "骞村害璁″垝缂栧彿", "骞翠唤4浣� + 4浣嶆祦姘�", "tms_annual_plan 渚� 20260001"),
+    ("鍗曟嵁瑙勫垯", "鍩硅浠诲姟缂栧彿", "TT + YYYYMM + 4浣嶆祦姘�", "tms_task 渚� TT2026090618"),
+]:
+    ws3.append(list(row))
+
+header_fill = PatternFill("solid", fgColor="1F4E79")
+header_font = Font(color="FFFFFF", bold=True)
+thin = Border(*(Side(style="thin", color="D0D0D0"),) * 4)
+for sheet in wb.worksheets:
+    for cell in sheet[1]:
+        cell.fill = header_fill
+        cell.font = header_font
+        cell.alignment = Alignment(horizontal="center")
+    sheet.freeze_panes = "A2"
+    sheet.auto_filter.ref = sheet.dimensions
+    widths = {}
+    for row in sheet.iter_rows(min_row=1, max_row=min(sheet.max_row, 80)):
+        for c in row:
+            c.border = thin
+            c.alignment = Alignment(wrap_text=True, vertical="center")
+            widths[c.column] = max(widths.get(c.column, 10), min(len(str(c.value or "")) + 4, 42))
+    for col, w in widths.items():
+        sheet.column_dimensions[get_column_letter(col)].width = w
+
+xlsx = Path(r"D:/qiansheng/demo/flowApi/docs/tms/鐜绘�濋煬TMS-JNPF鏁版嵁琛ㄥ瓧娈垫竻鍗�.xlsx")
+wb.save(xlsx)
+print("tables", len(tables), "xlsx", xlsx.stat().st_size)
diff --git a/docs/tms/tms_schema_pg.sql b/docs/tms/tms_schema_pg.sql
new file mode 100644
index 0000000..d122c8a
--- /dev/null
+++ b/docs/tms/tms_schema_pg.sql
@@ -0,0 +1,2171 @@
+-- =============================================================================
+-- 鐜绘�濋煬 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;
+
+-- ---------------------------------------------------------------------------
+-- 1. 鍩虹璁剧疆
+-- ---------------------------------------------------------------------------
+-- 鍩硅鍦扮偣銆傚湪绾垮涔犱换鍔″湴鐐瑰彲绌猴紱鍗犵敤鎸夊皬鏃朵笌鏈彇娑堜换鍔℃椂闂翠氦鍙夋牎楠屻��
+CREATE TABLE IF NOT EXISTS tms_place (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    place_no                 varchar(50) NOT NULL,
+    place_name               varchar(200) NOT NULL,
+    place_group              varchar(50),
+    enabled                  varchar(20) NOT NULL DEFAULT '1',
+    remark                   varchar(500),
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_place PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_place IS '鍩硅鍦扮偣銆傚湪绾垮涔犱换鍔″湴鐐瑰彲绌猴紱鍗犵敤鎸夊皬鏃朵笌鏈彇娑堜换鍔℃椂闂翠氦鍙夋牎楠屻��';
+COMMENT ON COLUMN tms_place.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_place.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_place.place_no IS '鍦扮偣缂栧彿锛屽崟鎹鍒� A+YYYYMM+4锛屽 A2026090001';
+COMMENT ON COLUMN tms_place.place_name IS '鍦扮偣鍚嶇О';
+COMMENT ON COLUMN tms_place.place_group IS '缁勫埆/鍒嗗尯锛屽瓧鍏革細鍔炲叕鍖�/鐢熶骇鍖�/A鍖�/B鍖�';
+COMMENT ON COLUMN tms_place.enabled IS '1鍚敤 0鍋滅敤';
+COMMENT ON COLUMN tms_place.remark IS '澶囨敞';
+COMMENT ON COLUMN tms_place.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_place.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_place.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_place.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_place.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_place.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_place.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE UNIQUE INDEX IF NOT EXISTS uk_tms_place_no ON tms_place (place_no) WHERE f_delete_mark = 0;
+CREATE INDEX IF NOT EXISTS idx_tms_place_group ON tms_place (place_group, enabled);
+
+-- 鍩硅甯堟。妗堬紝鍏宠仈绯荤粺鐢ㄦ埛锛涢噸澶嶅綍鍏ユ寜user_id璇嗗埆锛涜处鍙锋敞閿�鍚庤嚜鍔ㄥ簾寮冦��
+CREATE TABLE IF NOT EXISTS tms_trainer (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    archive_no               varchar(50),
+    user_id                  varchar(50) NOT NULL,
+    trainer_name             varchar(100),
+    dept_id                  varchar(50),
+    post_id                  varchar(50),
+    school                   varchar(200),
+    education                varchar(50),
+    major                    varchar(100),
+    tech_title               varchar(100),
+    hire_date                date,
+    license                  varchar(200),
+    work_years               varchar(50),
+    trainer_level            varchar(50),
+    biz_status               varchar(20) NOT NULL DEFAULT 'valid',
+    remark                   varchar(500),
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_trainer PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_trainer IS '鍩硅甯堟。妗堬紝鍏宠仈绯荤粺鐢ㄦ埛锛涢噸澶嶅綍鍏ユ寜user_id璇嗗埆锛涜处鍙锋敞閿�鍚庤嚜鍔ㄥ簾寮冦��';
+COMMENT ON COLUMN tms_trainer.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_trainer.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_trainer.archive_no IS '妗f缂栧彿 AR+YYYY+4锛屽彲涓庝釜浜哄煿璁。妗堝叡鐢�';
+COMMENT ON COLUMN tms_trainer.user_id IS '绯荤粺鐢ㄦ埛ID';
+COMMENT ON COLUMN tms_trainer.trainer_name IS '濮撳悕锛堝啑浣欙紝渚夸簬鍒楄〃锛�';
+COMMENT ON COLUMN tms_trainer.dept_id IS '閮ㄩ棬';
+COMMENT ON COLUMN tms_trainer.post_id IS '宀椾綅';
+COMMENT ON COLUMN tms_trainer.school IS '姣曚笟闄㈡牎';
+COMMENT ON COLUMN tms_trainer.education IS '瀛﹀巻';
+COMMENT ON COLUMN tms_trainer.major IS '涓撲笟';
+COMMENT ON COLUMN tms_trainer.tech_title IS '鎶�鏈亴绉�';
+COMMENT ON COLUMN tms_trainer.hire_date IS '鍏ヨ亴鏃堕棿';
+COMMENT ON COLUMN tms_trainer.license IS '鎵т笟璇佷功';
+COMMENT ON COLUMN tms_trainer.work_years IS '宸ヤ綔骞撮檺';
+COMMENT ON COLUMN tms_trainer.trainer_level IS '鍩硅甯堢骇鍒�';
+COMMENT ON COLUMN tms_trainer.biz_status IS 'valid鏈夋晥鏈熷唴 / revoked宸叉挙閿�';
+COMMENT ON COLUMN tms_trainer.remark IS '澶囨敞';
+COMMENT ON COLUMN tms_trainer.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_trainer.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_trainer.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_trainer.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_trainer.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_trainer.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_trainer.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE UNIQUE INDEX IF NOT EXISTS uk_tms_trainer_user ON tms_trainer (user_id) WHERE f_delete_mark = 0;
+CREATE UNIQUE INDEX IF NOT EXISTS uk_tms_trainer_no ON tms_trainer (archive_no) WHERE f_delete_mark = 0;
+CREATE INDEX IF NOT EXISTS idx_tms_trainer_status ON tms_trainer (biz_status);
+
+-- 鍩硅甯堜釜浜哄伐浣滅粡鍘嗗瓙琛�
+CREATE TABLE IF NOT EXISTS tms_trainer_work (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    f_foreign_id             varchar(50) NOT NULL,
+    sort_no                  int,
+    hire_date                date,
+    leave_date               date,
+    company_name             varchar(200),
+    post_name                varchar(100),
+    leave_reason             varchar(200),
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    CONSTRAINT pk_tms_trainer_work PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_trainer_work IS '鍩硅甯堜釜浜哄伐浣滅粡鍘嗗瓙琛�';
+COMMENT ON COLUMN tms_trainer_work.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_trainer_work.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_trainer_work.f_foreign_id IS '涓昏〃f_id';
+COMMENT ON COLUMN tms_trainer_work.sort_no IS '搴忓彿';
+COMMENT ON COLUMN tms_trainer_work.hire_date IS '鍏ヨ亴鏃堕棿';
+COMMENT ON COLUMN tms_trainer_work.leave_date IS '绂昏亴鏃堕棿';
+COMMENT ON COLUMN tms_trainer_work.company_name IS '鍏徃鍚嶇О';
+COMMENT ON COLUMN tms_trainer_work.post_name IS '浠昏亴宀椾綅';
+COMMENT ON COLUMN tms_trainer_work.leave_reason IS '绂昏亴鍘熷洜';
+COMMENT ON COLUMN tms_trainer_work.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_trainer_work.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_trainer_work.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_trainer_work.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_trainer_work.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+CREATE INDEX IF NOT EXISTS idx_tms_trainer_work_fid ON tms_trainer_work (f_foreign_id);
+
+-- 鍩硅甯堜釜浜哄煿璁粡鍘嗗瓙琛�
+CREATE TABLE IF NOT EXISTS tms_trainer_exp (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    f_foreign_id             varchar(50) NOT NULL,
+    sort_no                  int,
+    train_date               date,
+    org_name                 varchar(200),
+    cert_name                varchar(200),
+    cert_file                text,
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    CONSTRAINT pk_tms_trainer_exp PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_trainer_exp IS '鍩硅甯堜釜浜哄煿璁粡鍘嗗瓙琛�';
+COMMENT ON COLUMN tms_trainer_exp.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_trainer_exp.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_trainer_exp.f_foreign_id IS '涓昏〃f_id';
+COMMENT ON COLUMN tms_trainer_exp.sort_no IS '搴忓彿';
+COMMENT ON COLUMN tms_trainer_exp.train_date IS '鍩硅鏃堕棿';
+COMMENT ON COLUMN tms_trainer_exp.org_name IS '鍩硅鏈烘瀯';
+COMMENT ON COLUMN tms_trainer_exp.cert_name IS '鍩硅璇佷功';
+COMMENT ON COLUMN tms_trainer_exp.cert_file IS '璇佷功闄勪欢';
+COMMENT ON COLUMN tms_trainer_exp.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_trainer_exp.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_trainer_exp.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_trainer_exp.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_trainer_exp.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+CREATE INDEX IF NOT EXISTS idx_tms_trainer_exp_fid ON tms_trainer_exp (f_foreign_id);
+
+-- 鍩硅绠$悊鍛樿法閮ㄩ棬鎺堟潈銆傚埌鏈熷墠涓�涓湀鎻愰啋绠$悊鍛�/鍩硅甯堛��
+CREATE TABLE IF NOT EXISTS tms_admin_auth (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    admin_user_id            varchar(50) NOT NULL,
+    scope_dept_id            text NOT NULL,
+    include_child            varchar(10) NOT NULL DEFAULT '1',
+    registrar_id             varchar(50),
+    register_date            date,
+    expire_date              date,
+    biz_status               varchar(20) NOT NULL DEFAULT 'valid',
+    remark                   varchar(500),
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_admin_auth PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_admin_auth IS '鍩硅绠$悊鍛樿法閮ㄩ棬鎺堟潈銆傚埌鏈熷墠涓�涓湀鎻愰啋绠$悊鍛�/鍩硅甯堛��';
+COMMENT ON COLUMN tms_admin_auth.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_admin_auth.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_admin_auth.admin_user_id IS '鍩硅绠$悊鍛�';
+COMMENT ON COLUMN tms_admin_auth.scope_dept_id IS '绠$悊鑼冨洿锛岀粍缁囧閫�';
+COMMENT ON COLUMN tms_admin_auth.include_child IS '鏄惁鍖呭惈涓嬬骇閮ㄩ棬 1鏄� 0鍚�';
+COMMENT ON COLUMN tms_admin_auth.registrar_id IS '鐧昏浜�';
+COMMENT ON COLUMN tms_admin_auth.register_date IS '鐧昏鏃ユ湡';
+COMMENT ON COLUMN tms_admin_auth.expire_date IS '鎺堟潈鍒版湡鏃ワ紝鍙┖琛ㄧず闀挎湡';
+COMMENT ON COLUMN tms_admin_auth.biz_status IS 'valid鏈夋晥 / revoked搴熷純';
+COMMENT ON COLUMN tms_admin_auth.remark IS '澶囨敞';
+COMMENT ON COLUMN tms_admin_auth.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_admin_auth.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_admin_auth.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_admin_auth.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_admin_auth.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_admin_auth.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_admin_auth.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE INDEX IF NOT EXISTS idx_tms_admin_auth_user ON tms_admin_auth (admin_user_id, biz_status);
+
+-- 鍩硅鏂瑰紡閰嶇疆锛堝惈绛惧埌瑙勫垯锛屼笉浠呮槸瀛楀吀锛�
+CREATE TABLE IF NOT EXISTS tms_train_mode (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    mode_code                varchar(50) NOT NULL,
+    mode_name                varchar(100) NOT NULL,
+    sign_rule                varchar(20) NOT NULL DEFAULT 'both',
+    allow_guest_sign         varchar(10) NOT NULL DEFAULT '0',
+    same_day_required        varchar(10) NOT NULL DEFAULT '0',
+    place_required           varchar(10) NOT NULL DEFAULT '1',
+    sort_no                  int,
+    enabled                  varchar(10) NOT NULL DEFAULT '1',
+    remark                   varchar(500),
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_train_mode PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_train_mode IS '鍩硅鏂瑰紡閰嶇疆锛堝惈绛惧埌瑙勫垯锛屼笉浠呮槸瀛楀吀锛�';
+COMMENT ON COLUMN tms_train_mode.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_train_mode.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_train_mode.mode_code IS '缂栫爜锛歰nsite/practice/online';
+COMMENT ON COLUMN tms_train_mode.mode_name IS '鍚嶇О锛氶泦涓巿璇�/鎿嶄綔鎺堣/鍦ㄧ嚎瀛︿範';
+COMMENT ON COLUMN tms_train_mode.sign_rule IS 'scan浠呮壂鐮� / both鎵爜+鍦ㄧ嚎';
+COMMENT ON COLUMN tms_train_mode.allow_guest_sign IS '鍏佽鍚嶅崟澶栫鍒帮紝闆嗕腑鎺堣=1';
+COMMENT ON COLUMN tms_train_mode.same_day_required IS '寮�濮嬬粨鏉熷繀椤诲悓涓�澶╋紝闆嗕腑/鎿嶄綔=1';
+COMMENT ON COLUMN tms_train_mode.place_required IS '鍦扮偣蹇呭~锛屽湪绾垮涔�=0';
+COMMENT ON COLUMN tms_train_mode.sort_no IS '鎺掑簭';
+COMMENT ON COLUMN tms_train_mode.enabled IS '鍚敤';
+COMMENT ON COLUMN tms_train_mode.remark IS '澶囨敞';
+COMMENT ON COLUMN tms_train_mode.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_train_mode.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_train_mode.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_train_mode.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_train_mode.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_train_mode.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_train_mode.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE UNIQUE INDEX IF NOT EXISTS uk_tms_train_mode_code ON tms_train_mode (mode_code) WHERE f_delete_mark = 0;
+
+-- 绛惧埌鏂瑰紡锛欰PP鎵爜 / 寰俊鎵爜 / 鍦ㄧ嚎绛惧埌
+CREATE TABLE IF NOT EXISTS tms_sign_mode (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    mode_code                varchar(50) NOT NULL,
+    mode_name                varchar(100) NOT NULL,
+    tip_seconds              int NOT NULL DEFAULT 30,
+    match_roster             varchar(10) NOT NULL DEFAULT '1',
+    enabled                  varchar(10) NOT NULL DEFAULT '1',
+    remark                   varchar(500),
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_sign_mode PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_sign_mode IS '绛惧埌鏂瑰紡锛欰PP鎵爜 / 寰俊鎵爜 / 鍦ㄧ嚎绛惧埌';
+COMMENT ON COLUMN tms_sign_mode.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_sign_mode.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_sign_mode.mode_code IS '缂栫爜锛歛pp_scan/wx_scan/online';
+COMMENT ON COLUMN tms_sign_mode.mode_name IS '鍚嶇О';
+COMMENT ON COLUMN tms_sign_mode.tip_seconds IS '鏈夊煿璁鐐规椂寮哄埗闃呰绉掓暟锛孖T鍙皟';
+COMMENT ON COLUMN tms_sign_mode.match_roster IS '鏄惁蹇呴』鍖归厤鍩硅瀵硅薄鍚嶅崟';
+COMMENT ON COLUMN tms_sign_mode.enabled IS '鍚敤';
+COMMENT ON COLUMN tms_sign_mode.remark IS '澶囨敞';
+COMMENT ON COLUMN tms_sign_mode.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_sign_mode.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_sign_mode.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_sign_mode.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_sign_mode.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_sign_mode.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_sign_mode.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE UNIQUE INDEX IF NOT EXISTS uk_tms_sign_mode_code ON tms_sign_mode (mode_code) WHERE f_delete_mark = 0;
+
+-- 鍩硅鏁堟灉璇勪及鏂瑰紡锛氭彁闂� / 鐜板満鎿嶄綔 / 鍦ㄧ嚎鑰冭瘯 / 鏃犻渶鑰冩牳
+CREATE TABLE IF NOT EXISTS tms_eval_mode (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    mode_code                varchar(50) NOT NULL,
+    mode_name                varchar(100) NOT NULL,
+    need_paper               varchar(10) NOT NULL DEFAULT '0',
+    need_quiz                varchar(10) NOT NULL DEFAULT '0',
+    need_practice            varchar(10) NOT NULL DEFAULT '0',
+    enabled                  varchar(10) NOT NULL DEFAULT '1',
+    remark                   varchar(500),
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_eval_mode PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_eval_mode IS '鍩硅鏁堟灉璇勪及鏂瑰紡锛氭彁闂� / 鐜板満鎿嶄綔 / 鍦ㄧ嚎鑰冭瘯 / 鏃犻渶鑰冩牳';
+COMMENT ON COLUMN tms_eval_mode.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_eval_mode.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_eval_mode.mode_code IS '缂栫爜锛歲uiz/practice/exam/none';
+COMMENT ON COLUMN tms_eval_mode.mode_name IS '鍚嶇О';
+COMMENT ON COLUMN tms_eval_mode.need_paper IS '鏄惁闇�瑕佽瘯鍗�';
+COMMENT ON COLUMN tms_eval_mode.need_quiz IS '鏄惁闇�瑕佹彁闂璁�';
+COMMENT ON COLUMN tms_eval_mode.need_practice IS '鏄惁闇�瑕佸疄鎿嶈瘎鍒�';
+COMMENT ON COLUMN tms_eval_mode.enabled IS '鍚敤';
+COMMENT ON COLUMN tms_eval_mode.remark IS '澶囨敞';
+COMMENT ON COLUMN tms_eval_mode.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_eval_mode.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_eval_mode.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_eval_mode.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_eval_mode.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_eval_mode.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_eval_mode.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE UNIQUE INDEX IF NOT EXISTS uk_tms_eval_mode_code ON tms_eval_mode (mode_code) WHERE f_delete_mark = 0;
+
+-- 鍩硅璇剧▼涓绘暟鎹紝璁″垝/浠诲姟鍙紩鐢�
+CREATE TABLE IF NOT EXISTS tms_course (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    course_no                varchar(50),
+    course_name              varchar(200) NOT NULL,
+    category                 varchar(50),
+    train_mode               varchar(50),
+    eval_mode                varchar(50),
+    paper_id                 varchar(50),
+    hours                    numeric(8,1),
+    enabled                  varchar(10) NOT NULL DEFAULT '1',
+    remark                   varchar(500),
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_course PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_course IS '鍩硅璇剧▼涓绘暟鎹紝璁″垝/浠诲姟鍙紩鐢�';
+COMMENT ON COLUMN tms_course.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_course.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_course.course_no IS '璇剧▼缂栧彿';
+COMMENT ON COLUMN tms_course.course_name IS '璇剧▼鍚嶇О';
+COMMENT ON COLUMN tms_course.category IS '璇剧▼鍒嗙被';
+COMMENT ON COLUMN tms_course.train_mode IS '鍩硅鏂瑰紡缂栫爜';
+COMMENT ON COLUMN tms_course.eval_mode IS '鑰冩牳鏂瑰紡缂栫爜';
+COMMENT ON COLUMN tms_course.paper_id IS '鍏宠仈璇曞嵎';
+COMMENT ON COLUMN tms_course.hours IS '瀛︽椂';
+COMMENT ON COLUMN tms_course.enabled IS '鍚敤';
+COMMENT ON COLUMN tms_course.remark IS '澶囨敞';
+COMMENT ON COLUMN tms_course.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_course.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_course.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_course.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_course.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_course.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_course.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE UNIQUE INDEX IF NOT EXISTS uk_tms_course_no ON tms_course (course_no) WHERE f_delete_mark = 0;
+
+-- 鍩硅鏁欐潗/浼佷笟鐭ヨ瘑搴撱�傚彲涓婁紶鏈湴鎴栧紩鐢―MS锛堥渶閴存潈锛夈�傛挙閿�鍚庝笉鍙啀寮曠敤銆�
+CREATE TABLE IF NOT EXISTS tms_material (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    material_no              varchar(50) NOT NULL,
+    material_name            varchar(500) NOT NULL,
+    file_type                varchar(50),
+    apply_time               date,
+    applicant_id             varchar(50),
+    apply_dept_id            varchar(50),
+    file_json                text,
+    dms_file_id              varchar(50),
+    quote_count              int NOT NULL DEFAULT 0,
+    biz_status               varchar(20) NOT NULL DEFAULT 'valid',
+    remark                   varchar(500),
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_material PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_material IS '鍩硅鏁欐潗/浼佷笟鐭ヨ瘑搴撱�傚彲涓婁紶鏈湴鎴栧紩鐢―MS锛堥渶閴存潈锛夈�傛挙閿�鍚庝笉鍙啀寮曠敤銆�';
+COMMENT ON COLUMN tms_material.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_material.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_material.material_no IS '鏁欐潗缂栧彿 TM+YYYYMM+4';
+COMMENT ON COLUMN tms_material.material_name IS '鏁欐潗鍚嶇О';
+COMMENT ON COLUMN tms_material.file_type IS 'Word/PPT/Excel/PDF/瑙嗛/闊抽';
+COMMENT ON COLUMN tms_material.apply_time IS '鐢宠鏃堕棿';
+COMMENT ON COLUMN tms_material.applicant_id IS '涓婁紶鐢宠浜�';
+COMMENT ON COLUMN tms_material.apply_dept_id IS '鐢宠浜洪儴闂�';
+COMMENT ON COLUMN tms_material.file_json IS '鏁欐潗闄勪欢锛圝NPF闄勪欢JSON锛�';
+COMMENT ON COLUMN tms_material.dms_file_id IS '寮曠敤鐨凞MS鏂囦欢ID';
+COMMENT ON COLUMN tms_material.quote_count IS '琚换鍔″紩鐢ㄦ鏁�';
+COMMENT ON COLUMN tms_material.biz_status IS 'valid鏈夋晥 / revoked宸叉挙閿�';
+COMMENT ON COLUMN tms_material.remark IS '澶囨敞';
+COMMENT ON COLUMN tms_material.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_material.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_material.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_material.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_material.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_material.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_material.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE UNIQUE INDEX IF NOT EXISTS uk_tms_material_no ON tms_material (material_no) WHERE f_delete_mark = 0;
+CREATE INDEX IF NOT EXISTS idx_tms_material_type ON tms_material (file_type, biz_status);
+
+-- ---------------------------------------------------------------------------
+-- 2. 棰樺簱璇曞嵎
+-- ---------------------------------------------------------------------------
+-- 棰樺簱
+CREATE TABLE IF NOT EXISTS tms_question_bank (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    bank_name                varchar(200) NOT NULL,
+    question_count           int NOT NULL DEFAULT 0,
+    biz_status               varchar(20) NOT NULL DEFAULT 'normal',
+    remark                   text,
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_question_bank PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_question_bank IS '棰樺簱';
+COMMENT ON COLUMN tms_question_bank.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_question_bank.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_question_bank.bank_name IS '棰樺簱鍚嶇О';
+COMMENT ON COLUMN tms_question_bank.question_count IS '璇曢鏁伴噺锛堢淮鎶ゆ椂鍥炲啓锛�';
+COMMENT ON COLUMN tms_question_bank.biz_status IS 'normal姝e父 / invalid澶辨晥';
+COMMENT ON COLUMN tms_question_bank.remark IS '澶囨敞';
+COMMENT ON COLUMN tms_question_bank.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_question_bank.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_question_bank.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_question_bank.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_question_bank.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_question_bank.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_question_bank.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE INDEX IF NOT EXISTS idx_tms_qbank_status ON tms_question_bank (biz_status);
+
+-- 璇曢銆備慨鏀逛笉褰卞搷宸插畬鎴愯�冭瘯蹇収銆�
+CREATE TABLE IF NOT EXISTS tms_question (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    question_no              varchar(50),
+    bank_id                  varchar(50) NOT NULL,
+    question_type            varchar(20) NOT NULL,
+    difficulty               varchar(20),
+    source_type              varchar(50),
+    stem                     text NOT NULL,
+    analysis                 text,
+    admin_user_id            varchar(50),
+    biz_status               varchar(20) NOT NULL DEFAULT 'open',
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_question PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_question IS '璇曢銆備慨鏀逛笉褰卞搷宸插畬鎴愯�冭瘯蹇収銆�';
+COMMENT ON COLUMN tms_question.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_question.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_question.question_no IS '璇曢缂栧彿锛屽彲娴佹按';
+COMMENT ON COLUMN tms_question.bank_id IS '鎵�灞為搴� tms_question_bank.f_id';
+COMMENT ON COLUMN tms_question.question_type IS 'single鍗曢�� / multi澶氶�� / judge鍒ゆ柇 / blank濉┖ / essay闂瓟';
+COMMENT ON COLUMN tms_question.difficulty IS 'easy/normal/hard锛岀晫闈㈤粯璁や竴鑸�';
+COMMENT ON COLUMN tms_question.source_type IS '璇曢鏉ユ簮锛岄粯璁よ嚜鍛介';
+COMMENT ON COLUMN tms_question.stem IS '棰樺共锛屽瘜鏂囨湰';
+COMMENT ON COLUMN tms_question.analysis IS '绛旀瑙f瀽/澶囨敞锛岄敊棰樺彲鏄剧ず';
+COMMENT ON COLUMN tms_question.admin_user_id IS '绠$悊鍛�';
+COMMENT ON COLUMN tms_question.biz_status IS 'open寮�鏀� / closed涓嶅紑鏀� / invalid搴熷純';
+COMMENT ON COLUMN tms_question.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_question.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_question.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_question.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_question.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_question.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_question.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE INDEX IF NOT EXISTS idx_tms_question_bank ON tms_question (bank_id, question_type, biz_status);
+
+-- 璇曢閫夐」銆傚崟閫夊崟閫変腑锛屽閫夊彲澶氶�変腑锛屽垽鏂彲鍋氭垚A瀵笲閿欍��
+CREATE TABLE IF NOT EXISTS tms_question_option (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    f_foreign_id             varchar(50) NOT NULL,
+    sort_no                  int,
+    option_label             varchar(10),
+    option_content           text,
+    is_correct               varchar(10) NOT NULL DEFAULT '0',
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    CONSTRAINT pk_tms_question_option PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_question_option IS '璇曢閫夐」銆傚崟閫夊崟閫変腑锛屽閫夊彲澶氶�変腑锛屽垽鏂彲鍋氭垚A瀵笲閿欍��';
+COMMENT ON COLUMN tms_question_option.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_question_option.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_question_option.f_foreign_id IS '涓昏〃f_id';
+COMMENT ON COLUMN tms_question_option.sort_no IS '搴忓彿';
+COMMENT ON COLUMN tms_question_option.option_label IS 'A/B/C/D...';
+COMMENT ON COLUMN tms_question_option.option_content IS '閫夐」鍐呭';
+COMMENT ON COLUMN tms_question_option.is_correct IS '鏄惁姝g‘绛旀 1鏄� 0鍚�';
+COMMENT ON COLUMN tms_question_option.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_question_option.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_question_option.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_question_option.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_question_option.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+CREATE INDEX IF NOT EXISTS idx_tms_qopt_fid ON tms_question_option (f_foreign_id);
+
+-- 璇曞嵎銆傚け鏁堣瘯鍗蜂笉寰楀啀鐢ㄤ簬鏂颁换鍔★紱鏀瑰嵎涓嶅奖鍝嶅凡鑰冭褰曘��
+CREATE TABLE IF NOT EXISTS tms_paper (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    paper_name               varchar(300) NOT NULL,
+    biz_status               varchar(20) NOT NULL DEFAULT 'open',
+    duration_min             int NOT NULL DEFAULT 60,
+    exam_start               timestamp,
+    exam_end                 timestamp,
+    score_publish_time       timestamp,
+    pass_score               numeric(8,1),
+    total_score              numeric(8,1),
+    sort_mode                varchar(20) NOT NULL DEFAULT 'paper',
+    has_subjective           varchar(10) NOT NULL DEFAULT '0',
+    remark                   text,
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_paper PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_paper IS '璇曞嵎銆傚け鏁堣瘯鍗蜂笉寰楀啀鐢ㄤ簬鏂颁换鍔★紱鏀瑰嵎涓嶅奖鍝嶅凡鑰冭褰曘��';
+COMMENT ON COLUMN tms_paper.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_paper.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_paper.paper_name IS '璇曞嵎鍚嶇О';
+COMMENT ON COLUMN tms_paper.biz_status IS 'open寮�鏀� / closed涓嶅紑鏀� / invalid澶辨晥';
+COMMENT ON COLUMN tms_paper.duration_min IS '鑰冭瘯鏃堕暱锛堝垎閽燂級';
+COMMENT ON COLUMN tms_paper.exam_start IS '璇曞嵎绾у紑鑰冩椂闂达紝鍙┖锛屼换鍔′笂鍐嶈';
+COMMENT ON COLUMN tms_paper.exam_end IS '璇曞嵎绾х粨鑰冩椂闂达紝鍙┖';
+COMMENT ON COLUMN tms_paper.score_publish_time IS '鎴愮哗鍏竷鏃堕棿锛岀┖=绔嬪嵆鍏竷';
+COMMENT ON COLUMN tms_paper.pass_score IS '鍚堟牸鍒嗘暟';
+COMMENT ON COLUMN tms_paper.total_score IS '璇曞嵎鎬诲垎';
+COMMENT ON COLUMN tms_paper.sort_mode IS 'paper璇曞嵎鑷韩椤哄簭 / random闅忔満';
+COMMENT ON COLUMN tms_paper.has_subjective IS '鏄惁鍚富瑙傞锛岀粍鍗锋椂鍥炲啓';
+COMMENT ON COLUMN tms_paper.remark IS '澶囨敞';
+COMMENT ON COLUMN tms_paper.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_paper.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_paper.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_paper.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_paper.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_paper.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_paper.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE INDEX IF NOT EXISTS idx_tms_paper_status ON tms_paper (biz_status);
+
+-- 璇曞嵎绔犺妭锛屽彲鎸夐鍨嬪綊绫�
+CREATE TABLE IF NOT EXISTS tms_paper_section (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    f_foreign_id             varchar(50) NOT NULL,
+    section_name             varchar(100),
+    question_type            varchar(20),
+    sort_no                  int,
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    CONSTRAINT pk_tms_paper_section PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_paper_section IS '璇曞嵎绔犺妭锛屽彲鎸夐鍨嬪綊绫�';
+COMMENT ON COLUMN tms_paper_section.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_paper_section.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_paper_section.f_foreign_id IS '涓昏〃f_id';
+COMMENT ON COLUMN tms_paper_section.section_name IS '绔犺妭鍚嶏紝濡� 涓�銆佸崟閫夐';
+COMMENT ON COLUMN tms_paper_section.question_type IS '榛樿棰樺瀷';
+COMMENT ON COLUMN tms_paper_section.sort_no IS '鎺掑簭';
+COMMENT ON COLUMN tms_paper_section.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_paper_section.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_paper_section.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_paper_section.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_paper_section.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+CREATE INDEX IF NOT EXISTS idx_tms_psec_fid ON tms_paper_section (f_foreign_id);
+
+-- 璇曞嵎缁勬垚璇曢锛堝綋鍓嶇増鏈級銆傚凡鑰冭瘯鍗疯蛋 tms_exam_item 蹇収銆�
+CREATE TABLE IF NOT EXISTS tms_paper_question (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    f_foreign_id             varchar(50) NOT NULL,
+    section_id               varchar(50),
+    question_id              varchar(50) NOT NULL,
+    score                    numeric(8,1),
+    sort_no                  int,
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    CONSTRAINT pk_tms_paper_question PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_paper_question IS '璇曞嵎缁勬垚璇曢锛堝綋鍓嶇増鏈級銆傚凡鑰冭瘯鍗疯蛋 tms_exam_item 蹇収銆�';
+COMMENT ON COLUMN tms_paper_question.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_paper_question.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_paper_question.f_foreign_id IS '涓昏〃f_id';
+COMMENT ON COLUMN tms_paper_question.section_id IS '绔犺妭ID';
+COMMENT ON COLUMN tms_paper_question.question_id IS '璇曢ID';
+COMMENT ON COLUMN tms_paper_question.score IS '鏈鍒嗗��';
+COMMENT ON COLUMN tms_paper_question.sort_no IS '鑷畾涔夋帓搴�';
+COMMENT ON COLUMN tms_paper_question.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_paper_question.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_paper_question.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_paper_question.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_paper_question.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+CREATE INDEX IF NOT EXISTS idx_tms_pq_fid ON tms_paper_question (f_foreign_id);
+CREATE INDEX IF NOT EXISTS idx_tms_pq_qid ON tms_paper_question (question_id);
+
+-- ---------------------------------------------------------------------------
+-- 3. 宀椾綅/骞村害璁″垝
+-- ---------------------------------------------------------------------------
+-- 宀椾綅鍩硅鐩綍銆傛敮鎸佸崌鐗堛�佸簾寮冦�傚鎵归�氳繃鍚庡彲鏁村崟澶嶅埗銆�
+CREATE TABLE IF NOT EXISTS tms_post_catalog (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    catalog_no               varchar(50),
+    post_id                  varchar(50) NOT NULL,
+    post_name                varchar(100),
+    dept_id                  varchar(50),
+    version_no               int NOT NULL DEFAULT 1,
+    biz_status               varchar(20) NOT NULL DEFAULT 'valid',
+    remark                   varchar(500),
+    f_flow_id                varchar(50),
+    f_flow_task_id           varchar(50),
+    f_flow_state             int,
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_post_catalog PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_post_catalog IS '宀椾綅鍩硅鐩綍銆傛敮鎸佸崌鐗堛�佸簾寮冦�傚鎵归�氳繃鍚庡彲鏁村崟澶嶅埗銆�';
+COMMENT ON COLUMN tms_post_catalog.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_post_catalog.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_post_catalog.catalog_no IS '鐩綍缂栧彿';
+COMMENT ON COLUMN tms_post_catalog.post_id IS '宀椾綅';
+COMMENT ON COLUMN tms_post_catalog.post_name IS '宀椾綅鍚嶇О鍐椾綑';
+COMMENT ON COLUMN tms_post_catalog.dept_id IS '鎵�灞為儴闂�';
+COMMENT ON COLUMN tms_post_catalog.version_no IS '鐗堟湰鍙凤紝鍗囩増+1';
+COMMENT ON COLUMN tms_post_catalog.biz_status IS 'valid鏈夋晥 / revoked搴熷純';
+COMMENT ON COLUMN tms_post_catalog.remark IS '澶囨敞';
+COMMENT ON COLUMN tms_post_catalog.f_flow_id IS '娴佺▼妯℃澘ID';
+COMMENT ON COLUMN tms_post_catalog.f_flow_task_id IS '娴佺▼浠诲姟ID';
+COMMENT ON COLUMN tms_post_catalog.f_flow_state IS '娴佺▼鐘舵��';
+COMMENT ON COLUMN tms_post_catalog.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_post_catalog.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_post_catalog.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_post_catalog.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_post_catalog.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_post_catalog.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_post_catalog.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE INDEX IF NOT EXISTS idx_tms_pcat_post ON tms_post_catalog (post_id, biz_status);
+
+-- 宀椾綅鍩硅鐩綍鍐呭琛岋紝鏀寔鍥炶溅澶氳銆佹墜鍔ㄦ帓搴�
+CREATE TABLE IF NOT EXISTS tms_post_catalog_item (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    f_foreign_id             varchar(50) NOT NULL,
+    sort_no                  int,
+    subject                  varchar(500),
+    train_mode               varchar(50),
+    hours                    numeric(8,1),
+    eval_mode                varchar(50),
+    material_id              varchar(50),
+    course_id                varchar(50),
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    CONSTRAINT pk_tms_post_catalog_item PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_post_catalog_item IS '宀椾綅鍩硅鐩綍鍐呭琛岋紝鏀寔鍥炶溅澶氳銆佹墜鍔ㄦ帓搴�';
+COMMENT ON COLUMN tms_post_catalog_item.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_post_catalog_item.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_post_catalog_item.f_foreign_id IS '涓昏〃f_id';
+COMMENT ON COLUMN tms_post_catalog_item.sort_no IS '椤哄簭';
+COMMENT ON COLUMN tms_post_catalog_item.subject IS '鍩硅鍐呭';
+COMMENT ON COLUMN tms_post_catalog_item.train_mode IS '鍩硅鏂瑰紡';
+COMMENT ON COLUMN tms_post_catalog_item.hours IS '璇炬椂';
+COMMENT ON COLUMN tms_post_catalog_item.eval_mode IS '鑰冩牳鏂瑰紡';
+COMMENT ON COLUMN tms_post_catalog_item.material_id IS '鏁欐潗';
+COMMENT ON COLUMN tms_post_catalog_item.course_id IS '璇剧▼';
+COMMENT ON COLUMN tms_post_catalog_item.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_post_catalog_item.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_post_catalog_item.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_post_catalog_item.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_post_catalog_item.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+CREATE INDEX IF NOT EXISTS idx_tms_pcati_fid ON tms_post_catalog_item (f_foreign_id);
+
+-- 宀椾綅鍩硅璁″垝銆傜敱鐩綍鎺ㄩ�侊紝鍙敓鎴愪釜浜哄矖浣嶅煿璁拰鍩硅浠诲姟銆�
+CREATE TABLE IF NOT EXISTS tms_post_plan (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    plan_no                  varchar(50) NOT NULL,
+    post_id                  varchar(50),
+    post_name                varchar(100),
+    dept_id                  varchar(50),
+    plan_end_time            date,
+    post_desc                text,
+    trainees                 text,
+    catalog_id               varchar(50),
+    publish_status           varchar(20) NOT NULL DEFAULT 'draft',
+    remark                   varchar(500),
+    f_flow_id                varchar(50),
+    f_flow_task_id           varchar(50),
+    f_flow_state             int,
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_post_plan PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_post_plan IS '宀椾綅鍩硅璁″垝銆傜敱鐩綍鎺ㄩ�侊紝鍙敓鎴愪釜浜哄矖浣嶅煿璁拰鍩硅浠诲姟銆�';
+COMMENT ON COLUMN tms_post_plan.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_post_plan.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_post_plan.plan_no IS '璁″垝缂栧彿 JP+YYYYMM+4锛屽 JP2026090012';
+COMMENT ON COLUMN tms_post_plan.post_id IS '宀椾綅缂栫爜/ID';
+COMMENT ON COLUMN tms_post_plan.post_name IS '宀椾綅鍚嶇О';
+COMMENT ON COLUMN tms_post_plan.dept_id IS '鎵�灞為儴闂�';
+COMMENT ON COLUMN tms_post_plan.plan_end_time IS '璁″垝缁撴潫鏃堕棿锛屽甫鍏ヤ釜浜哄矖浣嶅煿璁�';
+COMMENT ON COLUMN tms_post_plan.post_desc IS '宀椾綅鎻忚堪';
+COMMENT ON COLUMN tms_post_plan.trainees IS '鍩硅瀵硅薄锛岀敤鎴峰閫�';
+COMMENT ON COLUMN tms_post_plan.catalog_id IS '鏉ユ簮鐩綍';
+COMMENT ON COLUMN tms_post_plan.publish_status IS 'draft鏈彂甯� / published宸插彂甯�';
+COMMENT ON COLUMN tms_post_plan.remark IS '澶囨敞';
+COMMENT ON COLUMN tms_post_plan.f_flow_id IS '娴佺▼妯℃澘ID';
+COMMENT ON COLUMN tms_post_plan.f_flow_task_id IS '娴佺▼浠诲姟ID';
+COMMENT ON COLUMN tms_post_plan.f_flow_state IS '娴佺▼鐘舵��';
+COMMENT ON COLUMN tms_post_plan.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_post_plan.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_post_plan.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_post_plan.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_post_plan.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_post_plan.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_post_plan.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE UNIQUE INDEX IF NOT EXISTS uk_tms_pplan_no ON tms_post_plan (plan_no) WHERE f_delete_mark = 0;
+CREATE INDEX IF NOT EXISTS idx_tms_pplan_post ON tms_post_plan (post_id, publish_status);
+
+-- 宀椾綅鍩硅璁″垝鏄庣粏
+CREATE TABLE IF NOT EXISTS tms_post_plan_item (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    f_foreign_id             varchar(50) NOT NULL,
+    plan_time                date,
+    teacher_id               varchar(50),
+    subject                  varchar(500),
+    train_mode               varchar(50),
+    hours                    numeric(8,1),
+    eval_mode                varchar(50),
+    material_id              varchar(50),
+    task_id                  varchar(50),
+    line_status              varchar(20),
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    CONSTRAINT pk_tms_post_plan_item PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_post_plan_item IS '宀椾綅鍩硅璁″垝鏄庣粏';
+COMMENT ON COLUMN tms_post_plan_item.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_post_plan_item.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_post_plan_item.f_foreign_id IS '涓昏〃f_id';
+COMMENT ON COLUMN tms_post_plan_item.plan_time IS '鍩硅璁″垝鏃堕棿';
+COMMENT ON COLUMN tms_post_plan_item.teacher_id IS '鍩硅甯�';
+COMMENT ON COLUMN tms_post_plan_item.subject IS '鍩硅鍐呭';
+COMMENT ON COLUMN tms_post_plan_item.train_mode IS '鍩硅鏂瑰紡';
+COMMENT ON COLUMN tms_post_plan_item.hours IS '鍩硅璇炬椂';
+COMMENT ON COLUMN tms_post_plan_item.eval_mode IS '鑰冩牳鏂瑰紡';
+COMMENT ON COLUMN tms_post_plan_item.material_id IS '鏁欐潗';
+COMMENT ON COLUMN tms_post_plan_item.task_id IS '鐢熸垚鐨勫煿璁换鍔D';
+COMMENT ON COLUMN tms_post_plan_item.line_status IS 'pending寰呭彂甯� / published宸插彂甯� / done宸插畬鎴�';
+COMMENT ON COLUMN tms_post_plan_item.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_post_plan_item.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_post_plan_item.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_post_plan_item.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_post_plan_item.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+CREATE INDEX IF NOT EXISTS idx_tms_pplani_fid ON tms_post_plan_item (f_foreign_id);
+
+-- 涓汉宀椾綅鍩硅娓呭崟銆傝浆宀楁椂瀛愯〃鏍囪宸插煿璁繃鍒欎笉鐢熸垚浠诲姟銆�
+CREATE TABLE IF NOT EXISTS tms_person_post (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    plan_id                  varchar(50),
+    catalog_id               varchar(50),
+    user_id                  varchar(50) NOT NULL,
+    post_id                  varchar(50),
+    plan_end_time            date,
+    biz_status               varchar(20) NOT NULL DEFAULT 'doing',
+    person_remark            varchar(500),
+    audit_remark             varchar(500),
+    approve_remark           varchar(500),
+    f_flow_id                varchar(50),
+    f_flow_task_id           varchar(50),
+    f_flow_state             int,
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_person_post PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_person_post IS '涓汉宀椾綅鍩硅娓呭崟銆傝浆宀楁椂瀛愯〃鏍囪宸插煿璁繃鍒欎笉鐢熸垚浠诲姟銆�';
+COMMENT ON COLUMN tms_person_post.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_person_post.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_person_post.plan_id IS '宀椾綅鍩硅璁″垝ID';
+COMMENT ON COLUMN tms_person_post.catalog_id IS '鐩綍ID';
+COMMENT ON COLUMN tms_person_post.user_id IS '鍩硅瀵硅薄';
+COMMENT ON COLUMN tms_person_post.post_id IS '宀椾綅';
+COMMENT ON COLUMN tms_person_post.plan_end_time IS '璁″垝缁撴潫鏃堕棿锛堢敱璁″垝甯﹀嚭锛�';
+COMMENT ON COLUMN tms_person_post.biz_status IS 'doing鎵ц涓� / done瀹屾垚 / returned閫�鍥�';
+COMMENT ON COLUMN tms_person_post.person_remark IS '涓汉澶囨敞';
+COMMENT ON COLUMN tms_person_post.audit_remark IS '瀹℃牳澶囨敞';
+COMMENT ON COLUMN tms_person_post.approve_remark IS '瀹℃壒澶囨敞';
+COMMENT ON COLUMN tms_person_post.f_flow_id IS '娴佺▼妯℃澘ID';
+COMMENT ON COLUMN tms_person_post.f_flow_task_id IS '娴佺▼浠诲姟ID';
+COMMENT ON COLUMN tms_person_post.f_flow_state IS '娴佺▼鐘舵��';
+COMMENT ON COLUMN tms_person_post.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_person_post.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_person_post.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_person_post.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_person_post.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_person_post.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_person_post.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE INDEX IF NOT EXISTS idx_tms_ppost_user ON tms_person_post (user_id, biz_status);
+CREATE INDEX IF NOT EXISTS idx_tms_ppost_plan ON tms_person_post (plan_id);
+
+-- 涓汉宀椾綅鍩硅鏄庣粏
+CREATE TABLE IF NOT EXISTS tms_person_post_item (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    f_foreign_id             varchar(50) NOT NULL,
+    subject                  varchar(500),
+    train_mode               varchar(50),
+    hours                    numeric(8,1),
+    eval_mode                varchar(50),
+    already_trained          varchar(10) NOT NULL DEFAULT '0',
+    task_id                  varchar(50),
+    line_status              varchar(20),
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    CONSTRAINT pk_tms_person_post_item PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_person_post_item IS '涓汉宀椾綅鍩硅鏄庣粏';
+COMMENT ON COLUMN tms_person_post_item.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_person_post_item.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_person_post_item.f_foreign_id IS '涓昏〃f_id';
+COMMENT ON COLUMN tms_person_post_item.subject IS '鍩硅鍐呭';
+COMMENT ON COLUMN tms_person_post_item.train_mode IS '鍩硅鏂瑰紡';
+COMMENT ON COLUMN tms_person_post_item.hours IS '璇炬椂';
+COMMENT ON COLUMN tms_person_post_item.eval_mode IS '鑰冩牳鏂瑰紡';
+COMMENT ON COLUMN tms_person_post_item.already_trained IS '杞矖锛氭槸鍚﹀凡鍩硅杩囷紝鏄垯涓嶇敓鎴愪换鍔�';
+COMMENT ON COLUMN tms_person_post_item.task_id IS '鐢熸垚鐨勫煿璁换鍔�';
+COMMENT ON COLUMN tms_person_post_item.line_status IS '鐘舵��';
+COMMENT ON COLUMN tms_person_post_item.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_person_post_item.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_person_post_item.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_person_post_item.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_person_post_item.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+CREATE INDEX IF NOT EXISTS idx_tms_pposti_fid ON tms_person_post_item (f_foreign_id);
+
+-- 骞村害鍩硅璁″垝锛堝叕鍙�/閮ㄩ棬锛夈�傚姙缁撳悗鐢熸垚鍩硅浠诲姟銆傛祦绋嬶細閮ㄩ棬璐熻矗浜衡啋QA璐熻矗浜恒��
+CREATE TABLE IF NOT EXISTS tms_annual_plan (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    plan_year                int NOT NULL,
+    plan_no                  varchar(50) NOT NULL,
+    plan_type                varchar(20),
+    author_id                varchar(50),
+    author_dept_id           varchar(50),
+    author_date              date,
+    version_no               int NOT NULL DEFAULT 1,
+    dept_leader_id           varchar(50),
+    qa_leader_id             varchar(50),
+    biz_status               varchar(20) NOT NULL DEFAULT 'draft',
+    remark                   text,
+    f_flow_id                varchar(50),
+    f_flow_task_id           varchar(50),
+    f_flow_state             int,
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_annual_plan PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_annual_plan IS '骞村害鍩硅璁″垝锛堝叕鍙�/閮ㄩ棬锛夈�傚姙缁撳悗鐢熸垚鍩硅浠诲姟銆傛祦绋嬶細閮ㄩ棬璐熻矗浜衡啋QA璐熻矗浜恒��';
+COMMENT ON COLUMN tms_annual_plan.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_annual_plan.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_annual_plan.plan_year IS '骞翠唤锛屽埗瀹氭椂榛樿褰撳墠骞�+1';
+COMMENT ON COLUMN tms_annual_plan.plan_no IS '璁″垝缂栧彿锛屽 20260001';
+COMMENT ON COLUMN tms_annual_plan.plan_type IS 'company鍏徃绾� / dept閮ㄩ棬绾�';
+COMMENT ON COLUMN tms_annual_plan.author_id IS '鍒跺畾浜�';
+COMMENT ON COLUMN tms_annual_plan.author_dept_id IS '鍒跺畾閮ㄩ棬';
+COMMENT ON COLUMN tms_annual_plan.author_date IS '鍒跺畾鏃ユ湡';
+COMMENT ON COLUMN tms_annual_plan.version_no IS '鐗堟湰';
+COMMENT ON COLUMN tms_annual_plan.dept_leader_id IS '閮ㄩ棬璐熻矗浜猴紙绛炬牳灞曠ず锛�';
+COMMENT ON COLUMN tms_annual_plan.qa_leader_id IS 'QA閮ㄨ礋璐d汉';
+COMMENT ON COLUMN tms_annual_plan.biz_status IS 'draft鑽夌 / approving瀹℃壒涓� / approved宸叉壒鍑�';
+COMMENT ON COLUMN tms_annual_plan.remark IS '澶囨敞';
+COMMENT ON COLUMN tms_annual_plan.f_flow_id IS '娴佺▼妯℃澘ID';
+COMMENT ON COLUMN tms_annual_plan.f_flow_task_id IS '娴佺▼浠诲姟ID';
+COMMENT ON COLUMN tms_annual_plan.f_flow_state IS '娴佺▼鐘舵��';
+COMMENT ON COLUMN tms_annual_plan.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_annual_plan.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_annual_plan.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_annual_plan.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_annual_plan.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_annual_plan.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_annual_plan.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE UNIQUE INDEX IF NOT EXISTS uk_tms_aplan_no ON tms_annual_plan (plan_no) WHERE f_delete_mark = 0;
+CREATE INDEX IF NOT EXISTS idx_tms_aplan_year ON tms_annual_plan (plan_year, plan_type, author_dept_id);
+
+-- 骞村害鍩硅璁″垝鏄庣粏
+CREATE TABLE IF NOT EXISTS tms_annual_plan_item (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    f_foreign_id             varchar(50) NOT NULL,
+    train_month              varchar(6),
+    subject                  varchar(500),
+    train_mode               varchar(50),
+    trainees                 text,
+    hours                    numeric(8,1),
+    teacher_id               varchar(50),
+    eval_mode                varchar(50),
+    course_id                varchar(50),
+    line_status              varchar(20) NOT NULL DEFAULT 'pending',
+    task_id                  varchar(50),
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    CONSTRAINT pk_tms_annual_plan_item PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_annual_plan_item IS '骞村害鍩硅璁″垝鏄庣粏';
+COMMENT ON COLUMN tms_annual_plan_item.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_annual_plan_item.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_annual_plan_item.f_foreign_id IS '涓昏〃f_id';
+COMMENT ON COLUMN tms_annual_plan_item.train_month IS '鍩硅鏈堜唤 YYYYMM锛屽 202609';
+COMMENT ON COLUMN tms_annual_plan_item.subject IS '鍩硅鍐呭';
+COMMENT ON COLUMN tms_annual_plan_item.train_mode IS '鍩硅鏂瑰紡';
+COMMENT ON COLUMN tms_annual_plan_item.trainees IS '鍩硅瀵硅薄';
+COMMENT ON COLUMN tms_annual_plan_item.hours IS '鍩硅璇炬椂';
+COMMENT ON COLUMN tms_annual_plan_item.teacher_id IS '鍩硅甯�';
+COMMENT ON COLUMN tms_annual_plan_item.eval_mode IS '鑰冩牳鏂瑰紡';
+COMMENT ON COLUMN tms_annual_plan_item.course_id IS '鍩硅璇剧▼';
+COMMENT ON COLUMN tms_annual_plan_item.line_status IS 'pending寰呮墽琛� / tasked宸茬敓鎴愪换鍔� / done宸插畬鎴� / delayed宸插欢鏈�';
+COMMENT ON COLUMN tms_annual_plan_item.task_id IS '鐢熸垚鐨勫煿璁换鍔�';
+COMMENT ON COLUMN tms_annual_plan_item.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_annual_plan_item.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_annual_plan_item.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_annual_plan_item.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_annual_plan_item.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+CREATE INDEX IF NOT EXISTS idx_tms_aplani_fid ON tms_annual_plan_item (f_foreign_id);
+CREATE INDEX IF NOT EXISTS idx_tms_aplani_month ON tms_annual_plan_item (train_month);
+
+-- 骞村害鍩硅璁″垝寤舵湡鐢宠
+CREATE TABLE IF NOT EXISTS tms_annual_delay (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    plan_id                  varchar(50) NOT NULL,
+    item_id                  varchar(50),
+    old_month                varchar(6),
+    new_month                varchar(6),
+    reason                   text,
+    biz_status               varchar(20),
+    f_flow_id                varchar(50),
+    f_flow_task_id           varchar(50),
+    f_flow_state             int,
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_annual_delay PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_annual_delay IS '骞村害鍩硅璁″垝寤舵湡鐢宠';
+COMMENT ON COLUMN tms_annual_delay.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_annual_delay.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_annual_delay.plan_id IS '骞村害璁″垝ID';
+COMMENT ON COLUMN tms_annual_delay.item_id IS '璁″垝鏄庣粏ID';
+COMMENT ON COLUMN tms_annual_delay.old_month IS '鍘熸湀浠�';
+COMMENT ON COLUMN tms_annual_delay.new_month IS '寤舵湡鑷虫湀浠�';
+COMMENT ON COLUMN tms_annual_delay.reason IS '寤舵湡鍘熷洜';
+COMMENT ON COLUMN tms_annual_delay.biz_status IS '瀹℃壒鐘舵��';
+COMMENT ON COLUMN tms_annual_delay.f_flow_id IS '娴佺▼妯℃澘ID';
+COMMENT ON COLUMN tms_annual_delay.f_flow_task_id IS '娴佺▼浠诲姟ID';
+COMMENT ON COLUMN tms_annual_delay.f_flow_state IS '娴佺▼鐘舵��';
+COMMENT ON COLUMN tms_annual_delay.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_annual_delay.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_annual_delay.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_annual_delay.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_annual_delay.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_annual_delay.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_annual_delay.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE INDEX IF NOT EXISTS idx_tms_adelay_plan ON tms_annual_delay (plan_id);
+
+-- ---------------------------------------------------------------------------
+-- 4. 鍩硅浠诲姟涓庤褰�
+-- ---------------------------------------------------------------------------
+-- 鍩硅浠诲姟锛堟牳蹇冿級銆傛潵婧愶細宀椾綅璁″垝/骞村害璁″垝/鏂囦欢鐢熸晥/涓存椂/澶栧嚭/鍐嶇淮鎶ゃ��
+CREATE TABLE IF NOT EXISTS tms_task (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    task_no                  varchar(50) NOT NULL,
+    category                 varchar(50),
+    train_type               varchar(50),
+    source_type              varchar(50),
+    source_plan_no           varchar(50),
+    source_id                varchar(50),
+    source_item_id           varchar(50),
+    subject                  text,
+    key_points               text,
+    material_id              varchar(50),
+    material_no              varchar(50),
+    place_id                 varchar(50),
+    place_no                 varchar(50),
+    place_name               varchar(200),
+    start_time               timestamp,
+    end_time                 timestamp,
+    close_time               timestamp,
+    paper_id                 varchar(50),
+    paper_name               varchar(300),
+    exam_start               timestamp,
+    exam_end                 timestamp,
+    teacher_id               text,
+    train_mode               varchar(50),
+    eval_mode                varchar(50),
+    trainees                 text,
+    task_kind                varchar(20),
+    prev_task_no             varchar(50),
+    publish_status           varchar(20) NOT NULL DEFAULT 'draft',
+    publish_time             timestamp,
+    retake_limit             int NOT NULL DEFAULT 1,
+    pass_score               numeric(8,1),
+    quiz_ratio               int,
+    sign_rate                int,
+    pass_rate                int,
+    unfinished_rate          int,
+    overdue_remind           varchar(10) NOT NULL DEFAULT '0',
+    invite_flag              varchar(10) NOT NULL DEFAULT '0',
+    dms_file_id              varchar(50),
+    remark                   varchar(500),
+    f_flow_id                varchar(50),
+    f_flow_task_id           varchar(50),
+    f_flow_state             int,
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_task PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_task IS '鍩硅浠诲姟锛堟牳蹇冿級銆傛潵婧愶細宀椾綅璁″垝/骞村害璁″垝/鏂囦欢鐢熸晥/涓存椂/澶栧嚭/鍐嶇淮鎶ゃ��';
+COMMENT ON COLUMN tms_task.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_task.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_task.task_no IS '浠诲姟缂栧彿 TT+YYYYMM+4锛屽 TT2026090618';
+COMMENT ON COLUMN tms_task.category IS '鍩硅鍒嗙被锛氫复鏃跺煿璁�/宀椾綅鍩硅璁″垝/骞村害鍩硅璁″垝/鏂囦欢鐢熸晥鍩硅/澶栧嚭鍩硅/鍐嶇淮鎶�';
+COMMENT ON COLUMN tms_task.train_type IS '鍩硅绫诲瀷锛堝垪琛ㄥ瓧娈碉紝鍙笌鍒嗙被骞跺瓨锛�';
+COMMENT ON COLUMN tms_task.source_type IS '鏉ユ簮绫诲瀷缂栫爜';
+COMMENT ON COLUMN tms_task.source_plan_no IS '鏉ユ簮璁″垝缂栧彿';
+COMMENT ON COLUMN tms_task.source_id IS '鏉ユ簮涓昏〃ID';
+COMMENT ON COLUMN tms_task.source_item_id IS '鏉ユ簮鏄庣粏ID锛屽綊妗e洖鍐�';
+COMMENT ON COLUMN tms_task.subject IS '鍩硅涓婚';
+COMMENT ON COLUMN tms_task.key_points IS '鍩硅瑕佺偣锛岀鍒板墠寮哄埗闃呰';
+COMMENT ON COLUMN tms_task.material_id IS '鍩硅鏁欐潗';
+COMMENT ON COLUMN tms_task.material_no IS '鏁欐潗缂栧彿鍐椾綑';
+COMMENT ON COLUMN tms_task.place_id IS '鍩硅鍦扮偣';
+COMMENT ON COLUMN tms_task.place_no IS '鍦扮偣缂栧彿鍐椾綑';
+COMMENT ON COLUMN tms_task.place_name IS '鍦扮偣鍚嶇О鍐椾綑';
+COMMENT ON COLUMN tms_task.start_time IS '鍩硅寮�濮嬫椂闂�';
+COMMENT ON COLUMN tms_task.end_time IS '鍩硅缁撴潫鏃堕棿';
+COMMENT ON COLUMN tms_task.close_time IS '浠诲姟鍏抽棴鏃堕棿锛屽埌杈惧悗璁板綍杩涘緟褰掓。';
+COMMENT ON COLUMN tms_task.paper_id IS '璇曞嵎';
+COMMENT ON COLUMN tms_task.paper_name IS '璇曞嵎鍚嶇О鍐椾綑';
+COMMENT ON COLUMN tms_task.exam_start IS '鑰冭瘯寮�濮嬫椂闂�';
+COMMENT ON COLUMN tms_task.exam_end IS '鑰冭瘯缁撴潫鏃堕棿';
+COMMENT ON COLUMN tms_task.teacher_id IS '鍩硅甯堬紝鍙閫�';
+COMMENT ON COLUMN tms_task.train_mode IS '鍩硅鏂瑰紡';
+COMMENT ON COLUMN tms_task.eval_mode IS '鍩硅鏁堟灉璇勪及鏂瑰紡/鑰冩牳鏂瑰紡';
+COMMENT ON COLUMN tms_task.trainees IS '鍩硅瀵硅薄锛岀敤鎴峰閫夛紱鍙戝竷鏃舵媶涓汉浠诲姟';
+COMMENT ON COLUMN tms_task.task_kind IS '浠诲姟绫诲瀷锛歯ew鏂板 / continue缁х画(鍐嶇淮鎶�)';
+COMMENT ON COLUMN tms_task.prev_task_no IS '涓婁竴娆″煿璁换鍔$紪鍙�';
+COMMENT ON COLUMN tms_task.publish_status IS 'draft鏈彂甯� / published宸插彂甯� / cancelled宸插彇娑� / done宸插畬鎴�';
+COMMENT ON COLUMN tms_task.publish_time IS '鍙戝竷鏃堕棿';
+COMMENT ON COLUMN tms_task.retake_limit IS '涓嶅悎鏍煎彲閲嶈�冩鏁�';
+COMMENT ON COLUMN tms_task.pass_score IS '閫氳繃鍒嗘暟锛岀┖鍒欑敤璇曞嵎';
+COMMENT ON COLUMN tms_task.quiz_ratio IS '鎻愰棶姣斾緥0-100锛岃嚜鍔ㄧ畻搴旀彁闂汉鏁�';
+COMMENT ON COLUMN tms_task.sign_rate IS '绛惧埌鐜�%锛屽彇鏁磋垗灏忔暟';
+COMMENT ON COLUMN tms_task.pass_rate IS '鍚堟牸鐜�%锛屽彇鏁磋垗灏忔暟';
+COMMENT ON COLUMN tms_task.unfinished_rate IS '鏈畬鎴愮巼%';
+COMMENT ON COLUMN tms_task.overdue_remind IS '鏄惁閫炬湡鎻愰啋閮ㄩ棬棰嗗';
+COMMENT ON COLUMN tms_task.invite_flag IS '鏄惁鍙戦�佸煿璁個璇�';
+COMMENT ON COLUMN tms_task.dms_file_id IS '鏂囦欢鐢熸晥鑷姩寮曞叆鐨勯檮浠�';
+COMMENT ON COLUMN tms_task.remark IS '澶囨敞';
+COMMENT ON COLUMN tms_task.f_flow_id IS '娴佺▼妯℃澘ID';
+COMMENT ON COLUMN tms_task.f_flow_task_id IS '娴佺▼浠诲姟ID';
+COMMENT ON COLUMN tms_task.f_flow_state IS '娴佺▼鐘舵��';
+COMMENT ON COLUMN tms_task.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_task.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_task.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_task.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_task.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_task.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_task.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE UNIQUE INDEX IF NOT EXISTS uk_tms_task_no ON tms_task (task_no) WHERE f_delete_mark = 0;
+CREATE INDEX IF NOT EXISTS idx_tms_task_status ON tms_task (publish_status, start_time);
+CREATE INDEX IF NOT EXISTS idx_tms_task_place ON tms_task (place_id, start_time, end_time);
+CREATE INDEX IF NOT EXISTS idx_tms_task_source ON tms_task (source_type, source_id);
+CREATE INDEX IF NOT EXISTS idx_tms_task_teacher ON tms_task (teacher_id);
+
+-- 鍩硅浠诲姟鏁欐潗/闄勪欢娓呭崟锛屽彂甯冨悗鍚屾鍒板煿璁褰�
+CREATE TABLE IF NOT EXISTS tms_task_file (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    f_foreign_id             varchar(50) NOT NULL,
+    file_no                  varchar(50),
+    file_name                varchar(300),
+    material_id              varchar(50),
+    file_json                text,
+    can_download             varchar(10) NOT NULL DEFAULT '0',
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    CONSTRAINT pk_tms_task_file PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_task_file IS '鍩硅浠诲姟鏁欐潗/闄勪欢娓呭崟锛屽彂甯冨悗鍚屾鍒板煿璁褰�';
+COMMENT ON COLUMN tms_task_file.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_task_file.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_task_file.f_foreign_id IS '涓昏〃f_id';
+COMMENT ON COLUMN tms_task_file.file_no IS '缂栧彿';
+COMMENT ON COLUMN tms_task_file.file_name IS '鏂囦欢鍚嶇О';
+COMMENT ON COLUMN tms_task_file.material_id IS '鏉ユ簮鏁欐潗';
+COMMENT ON COLUMN tms_task_file.file_json IS '闄勪欢';
+COMMENT ON COLUMN tms_task_file.can_download IS '寮曠敤鏁欐潗榛樿涓嶅彲涓嬭浇鎵撳嵃';
+COMMENT ON COLUMN tms_task_file.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_task_file.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_task_file.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_task_file.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_task_file.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+CREATE INDEX IF NOT EXISTS idx_tms_tfile_fid ON tms_task_file (f_foreign_id);
+
+-- 鍩硅浠诲姟鎻愰棶棰勮闂鍜岀瓟妗堬紝绛惧埌鍚庡脊鍑�
+CREATE TABLE IF NOT EXISTS tms_task_quiz (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    f_foreign_id             varchar(50) NOT NULL,
+    sort_no                  int,
+    question                 text,
+    answer                   text,
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    CONSTRAINT pk_tms_task_quiz PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_task_quiz IS '鍩硅浠诲姟鎻愰棶棰勮闂鍜岀瓟妗堬紝绛惧埌鍚庡脊鍑�';
+COMMENT ON COLUMN tms_task_quiz.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_task_quiz.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_task_quiz.f_foreign_id IS '涓昏〃f_id';
+COMMENT ON COLUMN tms_task_quiz.sort_no IS '搴忓彿';
+COMMENT ON COLUMN tms_task_quiz.question IS '闂';
+COMMENT ON COLUMN tms_task_quiz.answer IS '鍙傝�冪瓟妗�';
+COMMENT ON COLUMN tms_task_quiz.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_task_quiz.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_task_quiz.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_task_quiz.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_task_quiz.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+CREATE INDEX IF NOT EXISTS idx_tms_tquiz_fid ON tms_task_quiz (f_foreign_id);
+
+-- 涓汉鍩硅浠诲姟銆傚彂甯冩椂鎸変汉鎷嗚锛涘鍛樼鑷/绛惧埌/鑰冭瘯鍏ュ彛銆�
+CREATE TABLE IF NOT EXISTS tms_person_task (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    task_id                  varchar(50) NOT NULL,
+    task_no                  varchar(50),
+    user_id                  varchar(50) NOT NULL,
+    dept_id                  varchar(50),
+    hours                    numeric(8,1),
+    biz_status               varchar(20) NOT NULL DEFAULT 'todo',
+    sign_time                timestamp,
+    sign_mode                varchar(20),
+    learn_seconds            int,
+    exam_score               numeric(8,1),
+    pass_flag                varchar(20),
+    guest_flag               varchar(10) NOT NULL DEFAULT '0',
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_person_task PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_person_task IS '涓汉鍩硅浠诲姟銆傚彂甯冩椂鎸変汉鎷嗚锛涘鍛樼鑷/绛惧埌/鑰冭瘯鍏ュ彛銆�';
+COMMENT ON COLUMN tms_person_task.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_person_task.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_person_task.task_id IS '鍩硅浠诲姟ID';
+COMMENT ON COLUMN tms_person_task.task_no IS '浠诲姟缂栧彿鍐椾綑';
+COMMENT ON COLUMN tms_person_task.user_id IS '鍩硅瀵硅薄';
+COMMENT ON COLUMN tms_person_task.dept_id IS '褰撴椂閮ㄩ棬蹇収';
+COMMENT ON COLUMN tms_person_task.hours IS '瀛︽椂';
+COMMENT ON COLUMN tms_person_task.biz_status IS 'todo鏈畬鎴� / signed宸茬鍒� / done宸插畬鎴� / expired宸茶繃鏈� / cancelled宸插彇娑�';
+COMMENT ON COLUMN tms_person_task.sign_time IS '绛惧埌鏃堕棿';
+COMMENT ON COLUMN tms_person_task.sign_mode IS '绛惧埌鏂瑰紡';
+COMMENT ON COLUMN tms_person_task.learn_seconds IS '鍦ㄧ嚎瀛︿範鏃堕暱绉�';
+COMMENT ON COLUMN tms_person_task.exam_score IS '鑰冭瘯鎴愮哗';
+COMMENT ON COLUMN tms_person_task.pass_flag IS 'pass鍚堟牸 / fail涓嶅悎鏍� / na鏃犻渶鑰冩牳';
+COMMENT ON COLUMN tms_person_task.guest_flag IS '鏄惁鍚嶅崟澶栫鍒�';
+COMMENT ON COLUMN tms_person_task.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_person_task.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_person_task.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_person_task.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_person_task.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_person_task.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_person_task.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE INDEX IF NOT EXISTS idx_tms_ptask_user ON tms_person_task (user_id, biz_status);
+CREATE INDEX IF NOT EXISTS idx_tms_ptask_task ON tms_person_task (task_id);
+CREATE UNIQUE INDEX IF NOT EXISTS uk_tms_ptask_task_user ON tms_person_task (task_id, user_id) WHERE f_delete_mark = 0;
+
+-- 鍩硅璁板綍銆備换鍔″彂甯冨悗鐢熸垚锛涘叧闂椂闂村埌杈捐繘寰呭綊妗c��
+CREATE TABLE IF NOT EXISTS tms_record (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    record_no                varchar(50),
+    task_id                  varchar(50) NOT NULL,
+    task_no                  varchar(50),
+    category                 varchar(50),
+    subject                  text,
+    teacher_id               text,
+    train_type               varchar(50),
+    train_mode               varchar(50),
+    eval_mode                varchar(50),
+    start_time               timestamp,
+    end_time                 timestamp,
+    close_time               timestamp,
+    archive_status           varchar(20) NOT NULL DEFAULT 'pending',
+    archive_time             timestamp,
+    archiver_id              varchar(50),
+    sign_rate                int,
+    pass_rate                int,
+    lecture_file             text,
+    paper_sign_file          text,
+    print_tpl_id             varchar(50),
+    remark                   varchar(500),
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_record PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_record IS '鍩硅璁板綍銆備换鍔″彂甯冨悗鐢熸垚锛涘叧闂椂闂村埌杈捐繘寰呭綊妗c��';
+COMMENT ON COLUMN tms_record.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_record.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_record.record_no IS '璁板綍缂栧彿锛岄粯璁ゅ悓浠诲姟缂栧彿';
+COMMENT ON COLUMN tms_record.task_id IS '鍩硅浠诲姟ID';
+COMMENT ON COLUMN tms_record.task_no IS '浠诲姟缂栧彿';
+COMMENT ON COLUMN tms_record.category IS '鍩硅鍒嗙被';
+COMMENT ON COLUMN tms_record.subject IS '鍩硅涓婚';
+COMMENT ON COLUMN tms_record.teacher_id IS '鍩硅甯�';
+COMMENT ON COLUMN tms_record.train_type IS '鍩硅绫诲瀷';
+COMMENT ON COLUMN tms_record.train_mode IS '鍩硅鏂瑰紡';
+COMMENT ON COLUMN tms_record.eval_mode IS '鑰冩牳鏂瑰紡';
+COMMENT ON COLUMN tms_record.start_time IS '鍩硅寮�濮嬫椂闂达紝濂楁墦妯℃澘鎸夋绱㈠紩鐗堟湰';
+COMMENT ON COLUMN tms_record.end_time IS '鍩硅缁撴潫鏃堕棿';
+COMMENT ON COLUMN tms_record.close_time IS '鍏抽棴鏃堕棿';
+COMMENT ON COLUMN tms_record.archive_status IS 'pending寰呭綊妗� / ready鍙綊妗� / archived宸插綊妗� / invalid鏃犳晥';
+COMMENT ON COLUMN tms_record.archive_time IS '褰掓。鏃堕棿';
+COMMENT ON COLUMN tms_record.archiver_id IS '褰掓。浜�';
+COMMENT ON COLUMN tms_record.sign_rate IS '鍙備笌/绛惧埌鐜�% 鍙栨暣鑸嶅皬鏁�';
+COMMENT ON COLUMN tms_record.pass_rate IS '鍚堟牸鐜�%';
+COMMENT ON COLUMN tms_record.lecture_file IS '鍩硅璁蹭箟闄勪欢';
+COMMENT ON COLUMN tms_record.paper_sign_file IS '绾歌川绛惧埌琛ㄩ檮浠�';
+COMMENT ON COLUMN tms_record.print_tpl_id IS '鎵撳嵃妯℃澘鐗堟湰';
+COMMENT ON COLUMN tms_record.remark IS '澶囨敞';
+COMMENT ON COLUMN tms_record.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_record.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_record.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_record.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_record.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_record.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_record.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE UNIQUE INDEX IF NOT EXISTS uk_tms_record_task ON tms_record (task_id) WHERE f_delete_mark = 0;
+CREATE INDEX IF NOT EXISTS idx_tms_record_arch ON tms_record (archive_status, start_time);
+
+-- 鍩硅璁板綍绛惧埌/缁撴灉鏄庣粏锛堜竴浜轰竴琛岋級
+CREATE TABLE IF NOT EXISTS tms_record_sign (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    f_foreign_id             varchar(50) NOT NULL,
+    user_id                  varchar(50) NOT NULL,
+    user_name                varchar(100),
+    dept_id                  varchar(50),
+    sign_time                timestamp,
+    sign_mode                varchar(20),
+    guest_flag               varchar(10) NOT NULL DEFAULT '0',
+    result_flag              varchar(20),
+    exam_score               numeric(8,1),
+    practice_score           numeric(8,1),
+    quiz_score               numeric(8,1),
+    exam_id                  varchar(50),
+    judge_user_id            varchar(50),
+    judge_time               timestamp,
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    CONSTRAINT pk_tms_record_sign PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_record_sign IS '鍩硅璁板綍绛惧埌/缁撴灉鏄庣粏锛堜竴浜轰竴琛岋級';
+COMMENT ON COLUMN tms_record_sign.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_record_sign.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_record_sign.f_foreign_id IS '涓昏〃f_id';
+COMMENT ON COLUMN tms_record_sign.user_id IS '鍩硅瀵硅薄';
+COMMENT ON COLUMN tms_record_sign.user_name IS '濮撳悕蹇収';
+COMMENT ON COLUMN tms_record_sign.dept_id IS '閮ㄩ棬蹇収';
+COMMENT ON COLUMN tms_record_sign.sign_time IS '绛惧埌鏃堕棿';
+COMMENT ON COLUMN tms_record_sign.sign_mode IS '绛惧埌鏂瑰紡';
+COMMENT ON COLUMN tms_record_sign.guest_flag IS '鍚嶅崟澶栫鍒�';
+COMMENT ON COLUMN tms_record_sign.result_flag IS 'pass鍚堟牸 / fail涓嶅悎鏍� / absent鏈弬鍔� / na鏃犻渶鑰冩牳';
+COMMENT ON COLUMN tms_record_sign.exam_score IS '鑰冭瘯鎴愮哗';
+COMMENT ON COLUMN tms_record_sign.practice_score IS '瀹炴搷鎴愮哗';
+COMMENT ON COLUMN tms_record_sign.quiz_score IS '鎻愰棶鎴愮哗';
+COMMENT ON COLUMN tms_record_sign.exam_id IS '绛斿嵎ID';
+COMMENT ON COLUMN tms_record_sign.judge_user_id IS '缁撴灉鍒ゅ畾浜猴紙鍩硅甯堬級';
+COMMENT ON COLUMN tms_record_sign.judge_time IS '鍒ゅ畾鏃堕棿';
+COMMENT ON COLUMN tms_record_sign.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_record_sign.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_record_sign.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_record_sign.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_record_sign.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+CREATE INDEX IF NOT EXISTS idx_tms_rsign_fid ON tms_record_sign (f_foreign_id);
+CREATE INDEX IF NOT EXISTS idx_tms_rsign_user ON tms_record_sign (user_id);
+
+-- 鍩硅璁板綍鍙樻洿锛氭敼缁撴潫鏃堕棿銆佸鍔犲煿璁汉鍛樸�傚煿璁笀鍙戣捣鈫掗儴闂ㄩ瀵硷紙棰嗗鍗冲煿璁笀鍒欒烦杩囷級銆�
+CREATE TABLE IF NOT EXISTS tms_record_change (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    record_id                varchar(50) NOT NULL,
+    task_id                  varchar(50),
+    change_type              varchar(20),
+    old_end_time             timestamp,
+    new_end_time             timestamp,
+    add_users                text,
+    reason                   text,
+    biz_status               varchar(20),
+    f_flow_id                varchar(50),
+    f_flow_task_id           varchar(50),
+    f_flow_state             int,
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_record_change PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_record_change IS '鍩硅璁板綍鍙樻洿锛氭敼缁撴潫鏃堕棿銆佸鍔犲煿璁汉鍛樸�傚煿璁笀鍙戣捣鈫掗儴闂ㄩ瀵硷紙棰嗗鍗冲煿璁笀鍒欒烦杩囷級銆�';
+COMMENT ON COLUMN tms_record_change.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_record_change.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_record_change.record_id IS '鍩硅璁板綍ID';
+COMMENT ON COLUMN tms_record_change.task_id IS '浠诲姟ID';
+COMMENT ON COLUMN tms_record_change.change_type IS 'end_time鏀圭粨鏉熸椂闂� / add_user澧炲姞浜哄憳';
+COMMENT ON COLUMN tms_record_change.old_end_time IS '鍘熺粨鏉熸椂闂�';
+COMMENT ON COLUMN tms_record_change.new_end_time IS '鏂扮粨鏉熸椂闂�';
+COMMENT ON COLUMN tms_record_change.add_users IS '鏂板浜哄憳';
+COMMENT ON COLUMN tms_record_change.reason IS '鍙樻洿鍘熷洜';
+COMMENT ON COLUMN tms_record_change.biz_status IS '瀹℃壒鐘舵��';
+COMMENT ON COLUMN tms_record_change.f_flow_id IS '娴佺▼妯℃澘ID';
+COMMENT ON COLUMN tms_record_change.f_flow_task_id IS '娴佺▼浠诲姟ID';
+COMMENT ON COLUMN tms_record_change.f_flow_state IS '娴佺▼鐘舵��';
+COMMENT ON COLUMN tms_record_change.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_record_change.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_record_change.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_record_change.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_record_change.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_record_change.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_record_change.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE INDEX IF NOT EXISTS idx_tms_rchg_rec ON tms_record_change (record_id);
+
+-- 鍩硅璧勬枡褰掓。锛堢數瀛�/绾歌川/瀹炴搷璁板綍鍙岀粨鍚堬級
+CREATE TABLE IF NOT EXISTS tms_archive_file (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    record_id                varchar(50) NOT NULL,
+    task_id                  varchar(50),
+    archive_type             varchar(20),
+    file_json                text,
+    archive_time             timestamp,
+    archiver_id              varchar(50),
+    remark                   varchar(500),
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_archive_file PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_archive_file IS '鍩硅璧勬枡褰掓。锛堢數瀛�/绾歌川/瀹炴搷璁板綍鍙岀粨鍚堬級';
+COMMENT ON COLUMN tms_archive_file.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_archive_file.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_archive_file.record_id IS '鍩硅璁板綍ID';
+COMMENT ON COLUMN tms_archive_file.task_id IS '浠诲姟ID';
+COMMENT ON COLUMN tms_archive_file.archive_type IS 'electronic鐢靛瓙 / paper绾歌川 / practice瀹炴搷妗f';
+COMMENT ON COLUMN tms_archive_file.file_json IS '褰掓。璧勬枡';
+COMMENT ON COLUMN tms_archive_file.archive_time IS '褰掓。鏃堕棿';
+COMMENT ON COLUMN tms_archive_file.archiver_id IS '褰掓。浜�';
+COMMENT ON COLUMN tms_archive_file.remark IS '澶囨敞';
+COMMENT ON COLUMN tms_archive_file.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_archive_file.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_archive_file.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_archive_file.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_archive_file.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_archive_file.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_archive_file.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE INDEX IF NOT EXISTS idx_tms_afile_rec ON tms_archive_file (record_id);
+
+-- ---------------------------------------------------------------------------
+-- 5. 鑰冭瘯涓庤�冩牳
+-- ---------------------------------------------------------------------------
+-- 鍦ㄧ嚎鑰冭瘯绛斿嵎銆備腑閫旈��鍑哄彲缁�冿紱鎻愪氦鍚庡瑙傞鑷姩璇勫垎锛屾湁涓昏棰樿繘闃呭嵎銆�
+CREATE TABLE IF NOT EXISTS tms_exam (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    task_id                  varchar(50) NOT NULL,
+    person_task_id           varchar(50),
+    record_id                varchar(50),
+    paper_id                 varchar(50),
+    paper_name               varchar(300),
+    user_id                  varchar(50) NOT NULL,
+    attempt_no               int NOT NULL DEFAULT 1,
+    start_time               timestamp,
+    submit_time              timestamp,
+    duration_min             int,
+    objective_score          numeric(8,1),
+    subjective_score         numeric(8,1),
+    total_score              numeric(8,1),
+    pass_score               numeric(8,1),
+    pass_flag                varchar(20),
+    grade_status             varchar(20) NOT NULL DEFAULT 'auto',
+    grader_id                varchar(50),
+    grade_time               timestamp,
+    exam_status              varchar(20) NOT NULL DEFAULT 'doing',
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_exam PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_exam IS '鍦ㄧ嚎鑰冭瘯绛斿嵎銆備腑閫旈��鍑哄彲缁�冿紱鎻愪氦鍚庡瑙傞鑷姩璇勫垎锛屾湁涓昏棰樿繘闃呭嵎銆�';
+COMMENT ON COLUMN tms_exam.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_exam.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_exam.task_id IS '鍩硅浠诲姟';
+COMMENT ON COLUMN tms_exam.person_task_id IS '涓汉浠诲姟';
+COMMENT ON COLUMN tms_exam.record_id IS '鍩硅璁板綍';
+COMMENT ON COLUMN tms_exam.paper_id IS '褰撴椂璇曞嵎ID';
+COMMENT ON COLUMN tms_exam.paper_name IS '璇曞嵎鍚嶇О蹇収';
+COMMENT ON COLUMN tms_exam.user_id IS '鑰冪敓';
+COMMENT ON COLUMN tms_exam.attempt_no IS '绗嚑娆¤�冭瘯';
+COMMENT ON COLUMN tms_exam.start_time IS '寮�濮嬩綔绛�';
+COMMENT ON COLUMN tms_exam.submit_time IS '浜ゅ嵎鏃堕棿';
+COMMENT ON COLUMN tms_exam.duration_min IS '褰撴椂鏃堕暱蹇収';
+COMMENT ON COLUMN tms_exam.objective_score IS '瀹㈣棰樺緱鍒�';
+COMMENT ON COLUMN tms_exam.subjective_score IS '涓昏棰樺緱鍒�';
+COMMENT ON COLUMN tms_exam.total_score IS '鎬诲垎';
+COMMENT ON COLUMN tms_exam.pass_score IS '褰撴椂鍚堟牸鍒嗗揩鐓�';
+COMMENT ON COLUMN tms_exam.pass_flag IS 'pass/fail';
+COMMENT ON COLUMN tms_exam.grade_status IS 'auto鏃犻渶闃呭嵎 / pending寰呮壒鏀� / graded宸叉壒鏀�';
+COMMENT ON COLUMN tms_exam.grader_id IS '闃呭嵎鑰佸笀';
+COMMENT ON COLUMN tms_exam.grade_time IS '闃呭嵎鏃堕棿';
+COMMENT ON COLUMN tms_exam.exam_status IS 'doing鑰冭瘯涓� / submitted宸蹭氦鍗� / cancelled宸插彇娑�';
+COMMENT ON COLUMN tms_exam.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_exam.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_exam.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_exam.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_exam.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_exam.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_exam.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE INDEX IF NOT EXISTS idx_tms_exam_task_user ON tms_exam (task_id, user_id, attempt_no);
+CREATE INDEX IF NOT EXISTS idx_tms_exam_grade ON tms_exam (grade_status);
+CREATE INDEX IF NOT EXISTS idx_tms_exam_user ON tms_exam (user_id);
+
+-- 绛斿嵎璇曢蹇収锛氶鐩�/閫夐」/绛旀/鍒嗘暟淇濈暀鑰冭瘯褰撴椂鐘舵�侊紝鍚庣画鏀归鏀瑰嵎涓嶅奖鍝嶃��
+CREATE TABLE IF NOT EXISTS tms_exam_item (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    f_foreign_id             varchar(50) NOT NULL,
+    question_id              varchar(50),
+    question_type            varchar(20),
+    stem                     text,
+    options_json             text,
+    correct_answer           text,
+    user_answer              text,
+    score                    numeric(8,1),
+    got_score                numeric(8,1),
+    is_subjective            varchar(10) NOT NULL DEFAULT '0',
+    analysis                 text,
+    sort_no                  int,
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    CONSTRAINT pk_tms_exam_item PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_exam_item IS '绛斿嵎璇曢蹇収锛氶鐩�/閫夐」/绛旀/鍒嗘暟淇濈暀鑰冭瘯褰撴椂鐘舵�侊紝鍚庣画鏀归鏀瑰嵎涓嶅奖鍝嶃��';
+COMMENT ON COLUMN tms_exam_item.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_exam_item.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_exam_item.f_foreign_id IS '涓昏〃f_id';
+COMMENT ON COLUMN tms_exam_item.question_id IS '鍘熻瘯棰業D';
+COMMENT ON COLUMN tms_exam_item.question_type IS '棰樺瀷蹇収';
+COMMENT ON COLUMN tms_exam_item.stem IS '棰樺共蹇収';
+COMMENT ON COLUMN tms_exam_item.options_json IS '閫夐」蹇収 JSON';
+COMMENT ON COLUMN tms_exam_item.correct_answer IS '褰撴椂姝g‘绛旀';
+COMMENT ON COLUMN tms_exam_item.user_answer IS '鑰冪敓浣滅瓟';
+COMMENT ON COLUMN tms_exam_item.score IS '鏈婊″垎';
+COMMENT ON COLUMN tms_exam_item.got_score IS '寰楀垎';
+COMMENT ON COLUMN tms_exam_item.is_subjective IS '鏄惁涓昏棰�';
+COMMENT ON COLUMN tms_exam_item.analysis IS '瑙f瀽蹇収';
+COMMENT ON COLUMN tms_exam_item.sort_no IS '璇曞嵎鍐呴『搴�';
+COMMENT ON COLUMN tms_exam_item.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_exam_item.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_exam_item.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_exam_item.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_exam_item.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+CREATE INDEX IF NOT EXISTS idx_tms_eitem_fid ON tms_exam_item (f_foreign_id);
+
+-- 瀹炴搷鑰冩牳涓昏〃锛屼繚瀛樺悗鍥炲啓鍩硅璁板綍
+CREATE TABLE IF NOT EXISTS tms_practice (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    task_id                  varchar(50) NOT NULL,
+    record_id                varchar(50),
+    teacher_id               varchar(50),
+    practice_time            timestamp,
+    remark                   varchar(500),
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_practice PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_practice IS '瀹炴搷鑰冩牳涓昏〃锛屼繚瀛樺悗鍥炲啓鍩硅璁板綍';
+COMMENT ON COLUMN tms_practice.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_practice.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_practice.task_id IS '鍩硅浠诲姟';
+COMMENT ON COLUMN tms_practice.record_id IS '鍩硅璁板綍';
+COMMENT ON COLUMN tms_practice.teacher_id IS '鑰冩牳浜�';
+COMMENT ON COLUMN tms_practice.practice_time IS '鑰冩牳鏃堕棿';
+COMMENT ON COLUMN tms_practice.remark IS '澶囨敞';
+COMMENT ON COLUMN tms_practice.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_practice.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_practice.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_practice.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_practice.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_practice.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_practice.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE INDEX IF NOT EXISTS idx_tms_prac_task ON tms_practice (task_id);
+
+-- 瀹炴搷鑰冩牳鏄庣粏
+CREATE TABLE IF NOT EXISTS tms_practice_item (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    f_foreign_id             varchar(50) NOT NULL,
+    user_id                  varchar(50) NOT NULL,
+    content                  text,
+    score                    numeric(8,1),
+    result_flag              varchar(20),
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    CONSTRAINT pk_tms_practice_item PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_practice_item IS '瀹炴搷鑰冩牳鏄庣粏';
+COMMENT ON COLUMN tms_practice_item.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_practice_item.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_practice_item.f_foreign_id IS '涓昏〃f_id';
+COMMENT ON COLUMN tms_practice_item.user_id IS '鍩硅瀵硅薄';
+COMMENT ON COLUMN tms_practice_item.content IS '鑰冩牳鍐呭';
+COMMENT ON COLUMN tms_practice_item.score IS '鍒嗘暟';
+COMMENT ON COLUMN tms_practice_item.result_flag IS '鍚堟牸/涓嶅悎鏍�';
+COMMENT ON COLUMN tms_practice_item.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_practice_item.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_practice_item.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_practice_item.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_practice_item.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+CREATE INDEX IF NOT EXISTS idx_tms_praci_fid ON tms_practice_item (f_foreign_id);
+
+-- 鐜板満鎻愰棶鑰冩牳鎵撳垎锛屼繚瀛樺悗鍥炲啓鍩硅璁板綍
+CREATE TABLE IF NOT EXISTS tms_quiz_score (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    task_id                  varchar(50) NOT NULL,
+    record_id                varchar(50),
+    user_id                  varchar(50) NOT NULL,
+    question                 text,
+    answer                   text,
+    score                    numeric(8,1),
+    teacher_id               varchar(50),
+    quiz_time                timestamp,
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_quiz_score PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_quiz_score IS '鐜板満鎻愰棶鑰冩牳鎵撳垎锛屼繚瀛樺悗鍥炲啓鍩硅璁板綍';
+COMMENT ON COLUMN tms_quiz_score.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_quiz_score.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_quiz_score.task_id IS '鍩硅浠诲姟';
+COMMENT ON COLUMN tms_quiz_score.record_id IS '鍩硅璁板綍';
+COMMENT ON COLUMN tms_quiz_score.user_id IS '鍩硅瀵硅薄';
+COMMENT ON COLUMN tms_quiz_score.question IS '鎻愰棶鍐呭';
+COMMENT ON COLUMN tms_quiz_score.answer IS '鍥炵瓟';
+COMMENT ON COLUMN tms_quiz_score.score IS '鍒嗘暟';
+COMMENT ON COLUMN tms_quiz_score.teacher_id IS '鎻愰棶浜�';
+COMMENT ON COLUMN tms_quiz_score.quiz_time IS '鏃堕棿';
+COMMENT ON COLUMN tms_quiz_score.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_quiz_score.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_quiz_score.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_quiz_score.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_quiz_score.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_quiz_score.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_quiz_score.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE INDEX IF NOT EXISTS idx_tms_qscore_task ON tms_quiz_score (task_id);
+CREATE INDEX IF NOT EXISTS idx_tms_qscore_user ON tms_quiz_score (user_id);
+
+-- ---------------------------------------------------------------------------
+-- 6. 涓汉妗f
+-- ---------------------------------------------------------------------------
+-- 涓汉鍩硅妗f銆傚叆鑱岃嚜鍔ㄥ缓妗o紱涓�浜轰竴妗c��
+CREATE TABLE IF NOT EXISTS tms_person_file (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    archive_no               varchar(50) NOT NULL,
+    archive_date             date,
+    user_id                  varchar(50) NOT NULL,
+    user_name                varchar(100),
+    dept_id                  varchar(50),
+    hire_date                date,
+    leave_date               date,
+    main_post_id             varchar(50),
+    part_post_id             varchar(50),
+    biz_status               varchar(20) NOT NULL DEFAULT 'valid',
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_person_file PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_person_file IS '涓汉鍩硅妗f銆傚叆鑱岃嚜鍔ㄥ缓妗o紱涓�浜轰竴妗c��';
+COMMENT ON COLUMN tms_person_file.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_person_file.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_person_file.archive_no IS '妗f缂栧彿 AR+YYYY+4';
+COMMENT ON COLUMN tms_person_file.archive_date IS '寤烘。鏃堕棿';
+COMMENT ON COLUMN tms_person_file.user_id IS '鍛樺伐';
+COMMENT ON COLUMN tms_person_file.user_name IS '濮撳悕鍐椾綑';
+COMMENT ON COLUMN tms_person_file.dept_id IS '鎵�灞為儴闂�';
+COMMENT ON COLUMN tms_person_file.hire_date IS '鍏ヨ亴鏃堕棿';
+COMMENT ON COLUMN tms_person_file.leave_date IS '绂昏亴鏃堕棿';
+COMMENT ON COLUMN tms_person_file.main_post_id IS '涓昏宀椾綅';
+COMMENT ON COLUMN tms_person_file.part_post_id IS '鍏艰亴宀椾綅';
+COMMENT ON COLUMN tms_person_file.biz_status IS 'valid鍦ㄨ亴 / left绂昏亴';
+COMMENT ON COLUMN tms_person_file.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_person_file.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_person_file.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_person_file.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_person_file.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_person_file.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_person_file.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE UNIQUE INDEX IF NOT EXISTS uk_tms_pfile_user ON tms_person_file (user_id) WHERE f_delete_mark = 0;
+CREATE UNIQUE INDEX IF NOT EXISTS uk_tms_pfile_no ON tms_person_file (archive_no) WHERE f_delete_mark = 0;
+
+-- 涓汉鍩硅妗f-宸ヤ綔灞ュ巻
+CREATE TABLE IF NOT EXISTS tms_person_work (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    f_foreign_id             varchar(50) NOT NULL,
+    company_name             varchar(200),
+    post_name                varchar(100),
+    hire_date                date,
+    leave_date               date,
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    CONSTRAINT pk_tms_person_work PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_person_work IS '涓汉鍩硅妗f-宸ヤ綔灞ュ巻';
+COMMENT ON COLUMN tms_person_work.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_person_work.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_person_work.f_foreign_id IS '涓昏〃f_id';
+COMMENT ON COLUMN tms_person_work.company_name IS '鍏徃鍚嶇О';
+COMMENT ON COLUMN tms_person_work.post_name IS '浠昏亴宀椾綅';
+COMMENT ON COLUMN tms_person_work.hire_date IS '鍏ヨ亴鏃堕棿';
+COMMENT ON COLUMN tms_person_work.leave_date IS '绂昏亴鏃堕棿';
+COMMENT ON COLUMN tms_person_work.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_person_work.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_person_work.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_person_work.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_person_work.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+CREATE INDEX IF NOT EXISTS idx_tms_pwork_fid ON tms_person_work (f_foreign_id);
+
+-- 璧勮川鏂囦欢/鑾峰緱璇佷功銆傝祫璐ㄦ枃浠剁鐞嗕笌妗f瀛愯〃鍏辩敤姝よ〃銆�
+CREATE TABLE IF NOT EXISTS tms_person_cert (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    f_foreign_id             varchar(50) NOT NULL,
+    archive_id               varchar(50),
+    user_id                  varchar(50),
+    cert_no                  varchar(100),
+    cert_name                varchar(200),
+    gain_date                date,
+    expire_date              date,
+    cert_file                text,
+    cert_status              varchar(20),
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_person_cert PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_person_cert IS '璧勮川鏂囦欢/鑾峰緱璇佷功銆傝祫璐ㄦ枃浠剁鐞嗕笌妗f瀛愯〃鍏辩敤姝よ〃銆�';
+COMMENT ON COLUMN tms_person_cert.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_person_cert.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_person_cert.f_foreign_id IS '涓昏〃f_id';
+COMMENT ON COLUMN tms_person_cert.archive_id IS '涓汉妗fID锛屾棤澶栭敭瀛楁鏃剁敱涓讳粠鍐欏叆f_foreign_id';
+COMMENT ON COLUMN tms_person_cert.user_id IS '鍛樺伐锛屽垪琛ㄦā鍧楃敤';
+COMMENT ON COLUMN tms_person_cert.cert_no IS '璇佷功缂栧彿';
+COMMENT ON COLUMN tms_person_cert.cert_name IS '璇佷功鍚嶇О';
+COMMENT ON COLUMN tms_person_cert.gain_date IS '鑾峰緱鏃堕棿';
+COMMENT ON COLUMN tms_person_cert.expire_date IS '鏈夋晥鏈熻嚦';
+COMMENT ON COLUMN tms_person_cert.cert_file IS '璇佷功闄勪欢';
+COMMENT ON COLUMN tms_person_cert.cert_status IS 'valid鏈夋晥 / near杩戞晥鏈� / invalid鏃犳晥';
+COMMENT ON COLUMN tms_person_cert.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_person_cert.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_person_cert.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_person_cert.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_person_cert.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_person_cert.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_person_cert.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE INDEX IF NOT EXISTS idx_tms_pcert_fid ON tms_person_cert (f_foreign_id);
+CREATE INDEX IF NOT EXISTS idx_tms_pcert_user ON tms_person_cert (user_id);
+
+-- 涓汉鍩硅鐩綍锛堝綊妗e揩鐓э級銆傝褰曞綊妗e悗鍐欏叆锛屾弧瓒冲璁°��
+CREATE TABLE IF NOT EXISTS tms_person_train (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    archive_id               varchar(50) NOT NULL,
+    user_id                  varchar(50) NOT NULL,
+    record_id                varchar(50),
+    record_no                varchar(50),
+    category                 varchar(50),
+    subject                  text,
+    train_mode               varchar(50),
+    teacher_id               text,
+    eval_mode                varchar(50),
+    pass_flag                varchar(20),
+    start_time               timestamp,
+    end_time                 timestamp,
+    hours                    numeric(8,1),
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_person_train PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_person_train IS '涓汉鍩硅鐩綍锛堝綊妗e揩鐓э級銆傝褰曞綊妗e悗鍐欏叆锛屾弧瓒冲璁°��';
+COMMENT ON COLUMN tms_person_train.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_person_train.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_person_train.archive_id IS '涓汉妗fID';
+COMMENT ON COLUMN tms_person_train.user_id IS '鍛樺伐';
+COMMENT ON COLUMN tms_person_train.record_id IS '鍩硅璁板綍ID';
+COMMENT ON COLUMN tms_person_train.record_no IS '鍩硅璁板綍缂栧彿';
+COMMENT ON COLUMN tms_person_train.category IS '鍩硅绫诲瀷/鍒嗙被';
+COMMENT ON COLUMN tms_person_train.subject IS '鍩硅鍐呭';
+COMMENT ON COLUMN tms_person_train.train_mode IS '鍩硅鏂瑰紡';
+COMMENT ON COLUMN tms_person_train.teacher_id IS '鍩硅甯�';
+COMMENT ON COLUMN tms_person_train.eval_mode IS '鑰冩牳鏂瑰紡';
+COMMENT ON COLUMN tms_person_train.pass_flag IS '鏄惁鍚堟牸';
+COMMENT ON COLUMN tms_person_train.start_time IS '鍩硅寮�濮嬫椂闂�';
+COMMENT ON COLUMN tms_person_train.end_time IS '鍩硅缁撴潫鏃堕棿';
+COMMENT ON COLUMN tms_person_train.hours IS '瀛︽椂';
+COMMENT ON COLUMN tms_person_train.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_person_train.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_person_train.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_person_train.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_person_train.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_person_train.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_person_train.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE INDEX IF NOT EXISTS idx_tms_ptrain_user ON tms_person_train (user_id);
+CREATE INDEX IF NOT EXISTS idx_tms_ptrain_arch ON tms_person_train (archive_id);
+CREATE INDEX IF NOT EXISTS idx_tms_ptrain_rec ON tms_person_train (record_id);
+
+-- ---------------------------------------------------------------------------
+-- 7. 鍏朵粬娴佺▼涓庨厤缃�
+-- ---------------------------------------------------------------------------
+-- 澶栧嚭鍩硅
+CREATE TABLE IF NOT EXISTS tms_out_train (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    apply_no                 varchar(50),
+    applicant_id             varchar(50),
+    apply_dept_id            varchar(50),
+    org_name                 varchar(200),
+    subject                  varchar(500),
+    start_time               timestamp,
+    end_time                 timestamp,
+    hours                    numeric(8,1),
+    file_json                text,
+    biz_status               varchar(20),
+    f_flow_id                varchar(50),
+    f_flow_task_id           varchar(50),
+    f_flow_state             int,
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_out_train PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_out_train IS '澶栧嚭鍩硅';
+COMMENT ON COLUMN tms_out_train.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_out_train.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_out_train.apply_no IS '鐢宠鍗曞彿';
+COMMENT ON COLUMN tms_out_train.applicant_id IS '鐢宠浜�';
+COMMENT ON COLUMN tms_out_train.apply_dept_id IS '鐢宠閮ㄩ棬';
+COMMENT ON COLUMN tms_out_train.org_name IS '澶栭儴鏈烘瀯';
+COMMENT ON COLUMN tms_out_train.subject IS '鍩硅涓婚';
+COMMENT ON COLUMN tms_out_train.start_time IS '寮�濮�';
+COMMENT ON COLUMN tms_out_train.end_time IS '缁撴潫';
+COMMENT ON COLUMN tms_out_train.hours IS '瀛︽椂';
+COMMENT ON COLUMN tms_out_train.file_json IS '璧勬枡闄勪欢';
+COMMENT ON COLUMN tms_out_train.biz_status IS '瀹℃壒鐘舵��';
+COMMENT ON COLUMN tms_out_train.f_flow_id IS '娴佺▼妯℃澘ID';
+COMMENT ON COLUMN tms_out_train.f_flow_task_id IS '娴佺▼浠诲姟ID';
+COMMENT ON COLUMN tms_out_train.f_flow_state IS '娴佺▼鐘舵��';
+COMMENT ON COLUMN tms_out_train.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_out_train.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_out_train.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_out_train.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_out_train.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_out_train.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_out_train.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE UNIQUE INDEX IF NOT EXISTS uk_tms_out_no ON tms_out_train (apply_no) WHERE f_delete_mark = 0;
+
+-- 杞矖浜哄憳鑰冩牳閴村畾
+CREATE TABLE IF NOT EXISTS tms_transfer_eval (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    user_id                  varchar(50) NOT NULL,
+    from_post_id             varchar(50),
+    to_post_id               varchar(50),
+    eval_result              varchar(50),
+    remark                   text,
+    biz_status               varchar(20),
+    f_flow_id                varchar(50),
+    f_flow_task_id           varchar(50),
+    f_flow_state             int,
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_transfer_eval PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_transfer_eval IS '杞矖浜哄憳鑰冩牳閴村畾';
+COMMENT ON COLUMN tms_transfer_eval.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_transfer_eval.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_transfer_eval.user_id IS '杞矖浜哄憳';
+COMMENT ON COLUMN tms_transfer_eval.from_post_id IS '鍘熷矖浣�';
+COMMENT ON COLUMN tms_transfer_eval.to_post_id IS '鏂板矖浣�';
+COMMENT ON COLUMN tms_transfer_eval.eval_result IS '閴村畾缁撹';
+COMMENT ON COLUMN tms_transfer_eval.remark IS '璇存槑';
+COMMENT ON COLUMN tms_transfer_eval.biz_status IS '瀹℃壒鐘舵��';
+COMMENT ON COLUMN tms_transfer_eval.f_flow_id IS '娴佺▼妯℃澘ID';
+COMMENT ON COLUMN tms_transfer_eval.f_flow_task_id IS '娴佺▼浠诲姟ID';
+COMMENT ON COLUMN tms_transfer_eval.f_flow_state IS '娴佺▼鐘舵��';
+COMMENT ON COLUMN tms_transfer_eval.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_transfer_eval.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_transfer_eval.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_transfer_eval.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_transfer_eval.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_transfer_eval.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_transfer_eval.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE INDEX IF NOT EXISTS idx_tms_teval_user ON tms_transfer_eval (user_id);
+
+-- 鏂板憳宸ュ煿璁晥鏋滆�冭瘎
+CREATE TABLE IF NOT EXISTS tms_newcomer_eval (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    user_id                  varchar(50) NOT NULL,
+    eval_result              varchar(50),
+    remark                   text,
+    biz_status               varchar(20),
+    f_flow_id                varchar(50),
+    f_flow_task_id           varchar(50),
+    f_flow_state             int,
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_newcomer_eval PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_newcomer_eval IS '鏂板憳宸ュ煿璁晥鏋滆�冭瘎';
+COMMENT ON COLUMN tms_newcomer_eval.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_newcomer_eval.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_newcomer_eval.user_id IS '鏂板憳宸�';
+COMMENT ON COLUMN tms_newcomer_eval.eval_result IS '鑰冭瘎缁撹';
+COMMENT ON COLUMN tms_newcomer_eval.remark IS '璇存槑';
+COMMENT ON COLUMN tms_newcomer_eval.biz_status IS '瀹℃壒鐘舵��';
+COMMENT ON COLUMN tms_newcomer_eval.f_flow_id IS '娴佺▼妯℃澘ID';
+COMMENT ON COLUMN tms_newcomer_eval.f_flow_task_id IS '娴佺▼浠诲姟ID';
+COMMENT ON COLUMN tms_newcomer_eval.f_flow_state IS '娴佺▼鐘舵��';
+COMMENT ON COLUMN tms_newcomer_eval.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_newcomer_eval.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_newcomer_eval.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_newcomer_eval.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_newcomer_eval.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_newcomer_eval.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_newcomer_eval.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE INDEX IF NOT EXISTS idx_tms_neval_user ON tms_newcomer_eval (user_id);
+
+-- 澶嶅矖鍛樺伐鍩硅鏁堟灉鑰冭瘎
+CREATE TABLE IF NOT EXISTS tms_return_eval (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    user_id                  varchar(50) NOT NULL,
+    eval_result              varchar(50),
+    remark                   text,
+    biz_status               varchar(20),
+    f_flow_id                varchar(50),
+    f_flow_task_id           varchar(50),
+    f_flow_state             int,
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_return_eval PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_return_eval IS '澶嶅矖鍛樺伐鍩硅鏁堟灉鑰冭瘎';
+COMMENT ON COLUMN tms_return_eval.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_return_eval.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_return_eval.user_id IS '澶嶅矖鍛樺伐';
+COMMENT ON COLUMN tms_return_eval.eval_result IS '鑰冭瘎缁撹';
+COMMENT ON COLUMN tms_return_eval.remark IS '璇存槑';
+COMMENT ON COLUMN tms_return_eval.biz_status IS '瀹℃壒鐘舵��';
+COMMENT ON COLUMN tms_return_eval.f_flow_id IS '娴佺▼妯℃澘ID';
+COMMENT ON COLUMN tms_return_eval.f_flow_task_id IS '娴佺▼浠诲姟ID';
+COMMENT ON COLUMN tms_return_eval.f_flow_state IS '娴佺▼鐘舵��';
+COMMENT ON COLUMN tms_return_eval.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_return_eval.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_return_eval.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_return_eval.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_return_eval.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_return_eval.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_return_eval.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE INDEX IF NOT EXISTS idx_tms_reval_user ON tms_return_eval (user_id);
+
+-- 鍩硅璁板綍濂楁墦妯℃澘鐗堟湰銆傛寜鍩硅寮�濮嬫椂闂村尮閰嶇敓鏁堢増鏈��
+CREATE TABLE IF NOT EXISTS tms_print_template (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    tpl_code                 varchar(50) NOT NULL,
+    version_no               int NOT NULL,
+    effective_from           date NOT NULL,
+    effective_to             date,
+    file_json                text,
+    enabled                  varchar(10) NOT NULL DEFAULT '1',
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_print_template PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_print_template IS '鍩硅璁板綍濂楁墦妯℃澘鐗堟湰銆傛寜鍩硅寮�濮嬫椂闂村尮閰嶇敓鏁堢増鏈��';
+COMMENT ON COLUMN tms_print_template.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_print_template.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_print_template.tpl_code IS '妯℃澘缂栫爜锛屽 TRAIN_RECORD';
+COMMENT ON COLUMN tms_print_template.version_no IS '鐗堟湰鍙�';
+COMMENT ON COLUMN tms_print_template.effective_from IS '鐢熸晥璧峰锛堟寜鍩硅寮�濮嬫椂闂寸储寮曪級';
+COMMENT ON COLUMN tms_print_template.effective_to IS '鐢熸晥鎴锛岀┖=褰撳墠';
+COMMENT ON COLUMN tms_print_template.file_json IS '妯℃澘鏂囦欢';
+COMMENT ON COLUMN tms_print_template.enabled IS '鍚敤';
+COMMENT ON COLUMN tms_print_template.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_print_template.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_print_template.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_print_template.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_print_template.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_print_template.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_print_template.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE INDEX IF NOT EXISTS idx_tms_ptpl_code ON tms_print_template (tpl_code, effective_from);
+
+-- 鎻愰啋瑙勫垯锛氳鍒掑埌鏈熷墠涓�涓湀銆佹巿鏉冨埌鏈熴�佷釜浜哄矖浣嶅煿璁椂闄愮瓑
+CREATE TABLE IF NOT EXISTS tms_remind_rule (
+    f_id                     varchar(50) NOT NULL,
+    f_tenant_id              varchar(50),
+    rule_code                varchar(50) NOT NULL,
+    rule_name                varchar(100),
+    biz_type                 varchar(50),
+    days_before              int NOT NULL DEFAULT 30,
+    notify_roles             varchar(200),
+    enabled                  varchar(10) NOT NULL DEFAULT '1',
+    f_creator_user_id        varchar(50),
+    f_creator_time           timestamp,
+    f_last_modify_user_id    varchar(50),
+    f_last_modify_time       timestamp,
+    f_delete_mark            int NOT NULL DEFAULT 0,
+    f_delete_user_id         varchar(50),
+    f_delete_time            timestamp,
+    CONSTRAINT pk_tms_remind_rule PRIMARY KEY (f_id)
+);
+COMMENT ON TABLE tms_remind_rule IS '鎻愰啋瑙勫垯锛氳鍒掑埌鏈熷墠涓�涓湀銆佹巿鏉冨埌鏈熴�佷釜浜哄矖浣嶅煿璁椂闄愮瓑';
+COMMENT ON COLUMN tms_remind_rule.f_id IS '涓婚敭';
+COMMENT ON COLUMN tms_remind_rule.f_tenant_id IS '绉熸埛';
+COMMENT ON COLUMN tms_remind_rule.rule_code IS '瑙勫垯缂栫爜';
+COMMENT ON COLUMN tms_remind_rule.rule_name IS '鍚嶇О';
+COMMENT ON COLUMN tms_remind_rule.biz_type IS 'annual_plan/admin_auth/person_post/task_overdue';
+COMMENT ON COLUMN tms_remind_rule.days_before IS '鎻愬墠澶╂暟';
+COMMENT ON COLUMN tms_remind_rule.notify_roles IS '閫氱煡瑙掕壊锛氶儴闂ㄥ煿璁鐞嗗憳,鍩硅甯�,閮ㄩ棬棰嗗';
+COMMENT ON COLUMN tms_remind_rule.enabled IS '鍚敤';
+COMMENT ON COLUMN tms_remind_rule.f_creator_user_id IS '鍒涘缓浜�';
+COMMENT ON COLUMN tms_remind_rule.f_creator_time IS '鍒涘缓鏃堕棿';
+COMMENT ON COLUMN tms_remind_rule.f_last_modify_user_id IS '淇敼浜�';
+COMMENT ON COLUMN tms_remind_rule.f_last_modify_time IS '淇敼鏃堕棿';
+COMMENT ON COLUMN tms_remind_rule.f_delete_mark IS '鍒犻櫎鏍囪 0姝e父 1鍒犻櫎';
+COMMENT ON COLUMN tms_remind_rule.f_delete_user_id IS '鍒犻櫎浜�';
+COMMENT ON COLUMN tms_remind_rule.f_delete_time IS '鍒犻櫎鏃堕棿';
+CREATE UNIQUE INDEX IF NOT EXISTS uk_tms_remind_code ON tms_remind_rule (rule_code) WHERE f_delete_mark = 0;
+
+
+-- =============================================================================
+-- 棰勭疆鏁版嵁锛堝煿璁柟寮� / 绛惧埌鏂瑰紡 / 璇勪及鏂瑰紡 / 鎻愰啋瑙勫垯锛�
+-- =============================================================================
+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;
diff --git "a/docs/tms/\347\216\273\346\200\235\351\237\254TMS-JNPF\346\225\260\346\215\256\350\241\250\345\255\227\346\256\265\346\270\205\345\215\225.xlsx" "b/docs/tms/\347\216\273\346\200\235\351\237\254TMS-JNPF\346\225\260\346\215\256\350\241\250\345\255\227\346\256\265\346\270\205\345\215\225.xlsx"
new file mode 100644
index 0000000..50f2c2b
--- /dev/null
+++ "b/docs/tms/\347\216\273\346\200\235\351\237\254TMS-JNPF\346\225\260\346\215\256\350\241\250\345\255\227\346\256\265\346\270\205\345\215\225.xlsx"
Binary files differ
diff --git "a/docs/tms/\347\216\273\346\200\235\351\237\254TMS-JNPF\346\225\260\346\215\256\350\241\250\350\256\276\350\256\241.md" "b/docs/tms/\347\216\273\346\200\235\351\237\254TMS-JNPF\346\225\260\346\215\256\350\241\250\350\256\276\350\256\241.md"
new file mode 100644
index 0000000..7e093e4
--- /dev/null
+++ "b/docs/tms/\347\216\273\346\200\235\351\237\254TMS-JNPF\346\225\260\346\215\256\350\241\250\350\256\276\350\256\241.md"
@@ -0,0 +1,269 @@
+# 鐜绘�濋煬 TMS 路 JNPF / PostgreSQL 鏁版嵁琛ㄨ璁�
+
+渚濇嵁銆婄幓鎬濋煬TMS鍔熻兘璁捐涔� V1銆嬪拰鑰侀浼佺晫闈㈠瓧娈佃璁★紝缁� JNPF 6.x + PostgreSQL 18 鐢ㄣ��  
+椋庢牸瀵归綈鐜版湁 `lims_*` / `dms_*`锛氫富閿� `f_id`锛屽瓙琛� `f_foreign_id`锛屾祦绋� `f_flow_id`锛屼笟鍔″瓧娈佃嫳鏂囪泧褰€�佷笉鍔� `f_`銆�
+
+浜や粯鐗╋細
+
+- 寤鸿〃鑴氭湰锛歚docs/tms/tms_schema_pg.sql`锛�48 寮犺〃 + 棰勭疆鏁版嵁锛�
+- 瀛楁娓呭崟锛歚docs/tms/鐜绘�濋煬TMS-JNPF鏁版嵁琛ㄥ瓧娈垫竻鍗�.xlsx`
+
+---
+
+## 1. 鎬庝箞鍦� JNPF 寤�
+
+鎺ㄨ崘椤哄簭锛�
+
+1. 鍏堥厤瀛楀吀鍜屽崟鎹鍒欙紙绗� 3銆�4 鑺傦級
+2. 鏁版嵁寤烘ā鎸夌 2 鑺傞『搴忓缓琛紙鎴栧厛璺� SQL锛屽啀銆屽悓姝ユ暟鎹〃銆嶏級
+3. 鍩虹妗f琛ㄥ崟 鈫� 棰樺簱璇曞嵎 鈫� 璁″垝 鈫� 浠诲姟/璁板綍 鈫� 妗f
+4. 鐢ㄦ埛銆佺粍缁囥�佸矖浣嶃�侀檮浠朵竴寰嬬敤骞冲彴鎺т欢锛屼笉瑕佽嚜寤虹敤鎴疯〃
+5. 鍩硅瀵硅薄銆佸煿璁笀澶氶�夊瓨鐢ㄦ埛 ID锛涘彂甯冩椂鍐嶆寜浜烘媶 `tms_person_task`
+
+绯荤粺瀛楁姣忓紶琛ㄩ兘鏈夛紝璁捐鍣ㄤ細甯﹀嚭锛屼笉蹇呭湪涓氬姟瀛楁閲岄噸澶嶏細
+
+| 瀛楁 | 鐢ㄩ�� |
+|---|---|
+| `f_id` | 涓婚敭 |
+| `f_tenant_id` | 绉熸埛 |
+| `f_foreign_id` | 瀛愯〃鎸囧悜涓昏〃 |
+| `f_flow_id` / `f_flow_task_id` / `f_flow_state` | 娴佺▼琛ㄥ崟 |
+| `f_creator_user_id` / `f_creator_time` | 鍒涘缓 |
+| `f_last_modify_user_id` / `f_last_modify_time` | 淇敼 |
+| `f_delete_mark` | 閫昏緫鍒犻櫎 |
+
+---
+
+## 2. 琛ㄥ叧绯伙紙涓婚摼璺級
+
+```mermaid
+flowchart LR
+  subgraph base [鍩虹]
+    P[tms_place 鍦扮偣]
+    T[tms_trainer 鍩硅甯圿
+    M[tms_material 鏁欐潗]
+    C[tms_course 璇剧▼]
+    A[tms_admin_auth 鎺堟潈]
+  end
+  subgraph exameng [鑰冭瘯寮曟搸]
+    QB[tms_question_bank 棰樺簱]
+    Q[tms_question 璇曢]
+    PAP[tms_paper 璇曞嵎]
+  end
+  subgraph plan [璁″垝]
+    PC[tms_post_catalog 宀椾綅鐩綍]
+    PP[tms_post_plan 宀椾綅璁″垝]
+    PPP[tms_person_post 涓汉宀椾綅鍩硅]
+    AP[tms_annual_plan 骞村害璁″垝]
+  end
+  subgraph exec [鎵ц]
+    TK[tms_task 鍩硅浠诲姟]
+    PT[tms_person_task 涓汉浠诲姟]
+    RC[tms_record 鍩硅璁板綍]
+    EX[tms_exam 绛斿嵎]
+  end
+  subgraph file [妗f]
+    PF[tms_person_file 涓汉妗f]
+    CERT[tms_person_cert 璧勮川璇佷功]
+    DIR[tms_person_train 鍩硅鐩綍]
+  end
+  QB --> Q --> PAP
+  PC --> PP --> PPP
+  PP --> TK
+  AP --> TK
+  M --> TK
+  P --> TK
+  T --> TK
+  PAP --> TK
+  C --> AP
+  TK --> PT
+  TK --> RC
+  TK --> EX
+  PT --> EX
+  RC --> DIR
+  PF --> CERT
+  PF --> DIR
+```
+
+涓婚敭鎬庝箞涓诧細
+
+| 浠� | 鍒� | 瀛楁 |
+|---|---|---|
+| 骞磋鍒掓槑缁� / 宀楄鍒掓槑缁� | 浠诲姟 | `source_id` + `source_item_id` |
+| 浠诲姟 | 涓汉浠诲姟 | `task_id` |
+| 浠诲姟 | 鍩硅璁板綍 | `task_id`锛堜竴瀵逛竴锛� |
+| 浠诲姟 | 绛斿嵎 | `task_id` + `user_id` + `attempt_no` |
+| 璁板綍绛惧埌瀛愯〃 | 璁板綍 | `f_foreign_id` |
+| 璁板綍褰掓。 | 涓汉鍩硅鐩綍 | `record_id` + `user_id` |
+
+鑰佺晫闈㈤噷銆屽煿璁换鍔°�嶅拰銆屼釜浜轰换鍔°�嶅瓧娈电爜娣风敤锛孞NPF 蹇呴』鎷嗘垚涓ゅ紶琛ㄣ��
+
+---
+
+## 3. 鍏堥厤瀛楀吀
+
+| 瀛楀吀 | 閫夐」 |
+|---|---|
+| 浠诲姟鍙戝竷鐘舵�� | draft 鏈彂甯� / published 宸插彂甯� / cancelled 宸插彇娑� / done 宸插畬鎴� |
+| 鍩硅鍒嗙被 | 涓存椂鍩硅 / 宀椾綅鍩硅璁″垝 / 骞村害鍩硅璁″垝 / 鏂囦欢鐢熸晥鍩硅 / 澶栧嚭鍩硅 / 鍐嶇淮鎶� |
+| 浠诲姟绫诲瀷 | new 鏂板 / continue 缁х画锛堝啀缁存姢锛� |
+| 褰掓。鐘舵�� | pending 寰呭綊妗� / ready 鍙綊妗� / archived 宸插綊妗� / invalid 鏃犳晥 |
+| 涓汉浠诲姟鐘舵�� | todo 鏈畬鎴� / signed 宸茬鍒� / done 宸插畬鎴� / expired 宸茶繃鏈� / cancelled 宸插彇娑� |
+| 鏄惁鍚堟牸 | pass 鍚堟牸 / fail 涓嶅悎鏍� / absent 鏈弬鍔� / na 鏃犻渶鑰冩牳 |
+| 棰樺瀷 | single 鍗曢�� / multi 澶氶�� / judge 鍒ゆ柇 / blank 濉┖ / essay 闂瓟 |
+| 璇曢璇曞嵎鐘舵�� | open 寮�鏀� / closed 涓嶅紑鏀� / invalid 搴熷純 |
+| 闅惧害 | easy 绠�鍗� / normal 涓�鑸� / hard 鍥伴毦 |
+| 闃呭嵎鐘舵�� | auto 鏃犻渶闃呭嵎 / pending 寰呮壒鏀� / graded 宸叉壒鏀� |
+| 鏈夋晥鎾ら攢 | valid 鏈夋晥鏈熷唴 / revoked 宸叉挙閿� |
+| 璁″垝琛岀姸鎬� | pending 寰呮墽琛� / tasked 宸茬敓鎴愪换鍔� / done 宸插畬鎴� / delayed 宸插欢鏈� |
+| 鏁欐潗绫诲瀷 | Word / PPT / Excel / PDF / 瑙嗛 / 闊抽 |
+
+鍩硅鏂瑰紡銆佺鍒版柟寮忋�佽瘎浼版柟寮忓仛鎴�**涓氬姟琛�**锛堝甫瑙勫垯锛夛紝涓嶈鍙仛瀛楀吀锛�
+
+- 闆嗕腑鎺堣锛氫粎鎵爜銆佸厑璁稿悕鍗曞绛惧埌銆佸紑濮嬬粨鏉熷悓涓�澶┿�佸湴鐐瑰繀濉�
+- 鎿嶄綔鎺堣锛氫粎鎵爜銆佷笉鍏佽鍚嶅崟澶栥�佸悓涓�澶┿�佸湴鐐瑰繀濉�
+- 鍦ㄧ嚎瀛︿範锛氭壂鐮�+鍦ㄧ嚎銆佸湴鐐逛笉蹇呭~銆佹壂鐮佸繀椤诲尮閰嶅悕鍗�
+
+---
+
+## 4. 鍗曟嵁瑙勫垯锛堝榻愯�佺紪鍙凤級
+
+| 瀵硅薄 | 瑙勫垯 | 鑰佺晫闈㈢ず渚� |
+|---|---|---|
+| 鍦扮偣缂栧彿 `place_no` | `A` + YYYYMM + 4 浣� | A2026090001 |
+| 妗f缂栧彿 `archive_no` | `AR` + YYYY + 4 浣� | AR20260028 |
+| 鏁欐潗缂栧彿 `material_no` | `TM` + YYYYMM + 4 浣� | TM2026090001 |
+| 宀椾綅璁″垝 `plan_no` | `JP` + YYYYMM + 4 浣� | JP2026090012 |
+| 骞村害璁″垝 `plan_no` | 骞翠唤 4 浣� + 4 浣嶆祦姘� | 20260001 |
+| 鍩硅浠诲姟 `task_no` | `TT` + YYYYMM + 4 浣� | TT2026090618 |
+
+鍩硅璁板綍缂栧彿榛樿绛変簬浠诲姟缂栧彿锛堣�佹。妗堝煿璁洰褰曢噷灏辨槸 TT 鍙凤級銆�
+
+---
+
+## 5. 琛ㄥ崟涓庤〃锛堟惌寤洪『搴忥級
+
+### 5.1 鍩虹璁剧疆
+
+| 椤哄簭 | 琛ㄥ崟 | 琛� | 涓讳粠 | 娴佺▼ |
+|---|---|---|---|---|
+| 1 | 鍩硅鍦扮偣 | `tms_place` | 鍚� | 鍚� |
+| 2 | 鍩硅甯堟。妗� | `tms_trainer` | 宸ヤ綔缁忓巻 / 鍩硅缁忓巻 | 鍚� |
+| 3 | 鍩硅绠$悊鍛樻巿鏉� | `tms_admin_auth` | 鍚� | 鍚� |
+| 4 | 鍩硅鏂瑰紡 | `tms_train_mode` | 鍚� | 鍚� |
+| 5 | 绛惧埌鏂瑰紡 | `tms_sign_mode` | 鍚� | 鍚� |
+| 6 | 璇勪及鏂瑰紡 | `tms_eval_mode` | 鍚� | 鍚� |
+| 7 | 鍩硅璇剧▼ | `tms_course` | 鍚� | 鍚� |
+| 8 | 鍩硅鏁欐潗 | `tms_material` | 鍚� | 鍚� |
+
+鍦扮偣琛ㄦ瘮鑰佺晫闈㈠浜� `place_group`锛團DS 瑕佹眰鎸夌粍鍒垝鍒嗭級銆傚崰鐢ㄤ笉鍗曠嫭瀛樿〃锛屽彂甯�/淇濆瓨浠诲姟鏃剁敤鏃堕棿浜ゅ弶鏍¢獙銆�
+
+鍩硅甯堟寜 `user_id` 鍞竴锛岄噸澶嶅綍鍏ユ彁绀轰笉鎶ラ敊锛涘垪琛ㄧ敤 `biz_status` 绛涖�屾湁鏁堟湡鍐� / 宸叉挙閿�銆嶃��
+
+### 5.2 棰樺簱璇曞嵎
+
+| 椤哄簭 | 琛ㄥ崟 | 琛� | 涓讳粠 |
+|---|---|---|---|
+| 9 | 棰樺簱 | `tms_question_bank` | 鍚� |
+| 10 | 璇曢 | `tms_question` | 閫夐」 `tms_question_option` |
+| 11 | 璇曞嵎 | `tms_paper` | 绔犺妭 + 璇曞嵎璇曢 |
+
+鑰佺晫闈㈠瓧娈靛凡钀藉叆锛氶搴撳悕绉�/鐘舵��/澶囨敞锛涜瘯棰樼被鍨嬨�佹墍灞為搴撱�侀毦搴︺�佺姸鎬侊紙榛樿寮�鏀撅級銆佹潵婧愩�侀骞层�侀�夐」 A-D 鍙鍒狅紱璇曞嵎鍚嶇О銆佺姸鎬併�佽�冭瘯鏃堕暱銆佹垚缁╁叕甯冩椂闂淬�佸悎鏍煎垎鏁般�佽瘯棰樻帓搴忥紙鑷韩椤哄簭/闅忔満锛夈��
+
+**鍏抽敭绾︽潫锛圙MP锛�**锛氭敼璇曢銆佹敼璇曞嵎銆佹敼绛旀锛岄兘涓嶈兘鍔ㄥ凡鑰冭褰曘�傚凡鑰冩暟鎹彧瀛樺湪 `tms_exam` + `tms_exam_item` 蹇収閲屻��
+
+### 5.3 宀椾綅 / 骞村害璁″垝
+
+| 椤哄簭 | 琛ㄥ崟 | 琛� | 娴佺▼ |
+|---|---|---|---|
+| 12 | 宀椾綅鍩硅鐩綍 | `tms_post_catalog` + item | 鍙� |
+| 13 | 宀椾綅鍩硅璁″垝 | `tms_post_plan` + item | 鍙� |
+| 14 | 涓汉宀椾綅鍩硅 | `tms_person_post` + item | 鍙��鍥� |
+| 15 | 骞村害鍩硅璁″垝 | `tms_annual_plan` + item | 閮ㄩ棬璐熻矗浜� 鈫� QA |
+| 16 | 骞村害璁″垝寤舵湡 | `tms_annual_delay` | 鏄� |
+
+宀椾綅璁″垝涓昏〃瀵归綈鑰佺晫闈細宀椾綅缂栫爜/鍚嶇О銆佹墍灞為儴闂ㄣ�佽鍒掔粨鏉熸椂闂淬�佸矖浣嶆弿杩般�佸煿璁璞★紱瀛愯〃锛氬煿璁鍒掓椂闂淬�佸煿璁笀銆佸煿璁唴瀹广�佸煿璁柟寮忋�佽鏃躲�佽�冩牳鏂瑰紡銆�
+
+骞村害璁″垝瀵归綈鑰佺晫闈細骞翠唤銆佽鍒掔紪鍙枫�佽鍒掔被鍨嬨�佸埗瀹氭棩鏈�/浜�/閮ㄩ棬銆佺増鏈紱瀛愯〃锛氬煿璁湀浠姐�佸唴瀹广�佹柟寮忋�佸璞°�佽鏃躲�佸煿璁笀銆佽�冩牳鏂瑰紡銆佺姸鎬侊紱琛ㄥ熬澶囨敞銆侀儴闂ㄨ礋璐d汉銆丵A 閮ㄨ礋璐d汉銆�
+
+杞矖锛歚tms_person_post_item.already_trained=1` 鏃朵笉鐢熸垚瀵瑰簲鍩硅浠诲姟銆�
+
+### 5.4 浠诲姟 / 璁板綍 / 鑰冭瘯
+
+| 椤哄簭 | 琛ㄥ崟 | 琛� | 璇存槑 |
+|---|---|---|---|
+| 17 | 鍩硅浠诲姟 | `tms_task` + 鏁欐潗 + 鎻愰棶棰勮 | 鏍稿績锛涜法鏈�/涓存椂/鍦ㄥ矖璧版祦绋� |
+| 18 | 涓汉鍩硅浠诲姟 | `tms_person_task` | 鍙戝竷鏃舵寜浜虹敓鎴愶紝瀛﹀憳鍏ュ彛 |
+| 19 | 鍩硅璁板綍 | `tms_record` + 绛惧埌鏄庣粏 | 鍙戝竷鏃剁敓鎴愶紱鍏抽棴鏃堕棿鍒� 鈫� 寰呭綊妗� |
+| 20 | 鍩硅璁板綍鍙樻洿 | `tms_record_change` | 鏀圭粨鏉熸椂闂� / 鍔犱汉 |
+| 21 | 鍩硅璧勬枡褰掓。 | `tms_archive_file` | 鐢靛瓙/绾歌川/瀹炴搷 |
+| 22 | 鍦ㄧ嚎鑰冭瘯 | `tms_exam` + 蹇収 | 鍒楄〃/闃呭嵎/涓汉鑰冭瘯璁板綍鍏辩敤 |
+| 23 | 瀹炴搷鑰冩牳 | `tms_practice` + item | 鍥炲啓璁板綍 |
+| 24 | 鐜板満鎻愰棶鑰冩牳 | `tms_quiz_score` | 鍥炲啓璁板綍 |
+
+鍩硅浠诲姟涓昏〃瀵归綈鑰佺晫闈細
+
+缂栧彿銆佸煿璁垎绫汇�佹潵婧愯鍒掔紪鍙枫�佸煿璁富棰樸�佸煿璁鐐广�佹暀鏉愮紪鍙�/闄勪欢銆佸湴鐐圭紪鍙�/鍚嶇О銆佸紑濮�/缁撴潫銆佽瘯鍗风紪鍙�/鍚嶇О銆佸紑鑰�/缁撹�冦�佸煿璁笀銆佷换鍔″叧闂椂闂淬�佸煿璁柟寮忋�佽瘎浼版柟寮忋�佸煿璁璞°��
+
+鍒楄〃鍙︽湁锛氭槸鍚﹀凡鍙戝竷銆佷换鍔$被鍨嬶紙鏂板/缁х画锛夈�佷笂涓�娆′换鍔$紪鍙枫�傝繖浜涢兘鍦� `tms_task`銆�
+
+璁板綍鍙拌处涓嶅繀鍐嶅缓妯★紝鐢� `tms_record.archive_status` 绛涘緟褰掓。 / 鍙綊妗� / 宸插綊妗c��
+
+### 5.5 妗f涓庡叾瀹冩祦绋�
+
+| 椤哄簭 | 琛ㄥ崟 | 琛� |
+|---|---|---|
+| 25 | 涓汉鍩硅妗f | `tms_person_file` + 灞ュ巻 + 璇佷功 + 鍩硅鐩綍 |
+| 26 | 璧勮川鏂囦欢绠$悊 | 澶嶇敤 `tms_person_file` 鍒楄〃锛岃瘉涔﹀瓙琛� `tms_person_cert` |
+| 27 | 澶栧嚭鍩硅 | `tms_out_train` |
+| 28 | 杞矖 / 鏂板憳宸� / 澶嶅矖鑰冭瘎 | `tms_transfer_eval` / `tms_newcomer_eval` / `tms_return_eval` |
+| 29 | 濂楁墦妯℃澘鐗堟湰 | `tms_print_template`锛堟寜鍩硅寮�濮嬫椂闂村尮閰嶇増鏈級 |
+| 30 | 鎻愰啋瑙勫垯 | `tms_remind_rule` |
+
+涓汉妗f瀵归綈鑰佺晫闈細妗f缂栧彿銆佸缓妗f椂闂淬�佸憳宸ュ鍚嶃�佹墍灞為儴闂ㄣ�佸叆鑱屾椂闂淬�佷富瑕佸矖浣嶃�佸吋鑱屽矖浣嶏紱瀛愯〃宸ヤ綔灞ュ巻锛涙煡鐪嬮〉杩樻湁澶栭儴鍩硅/璇佷功銆佸煿璁洰褰曪紙璁板綍缂栧彿銆佺被鍨嬨�佸唴瀹广�佹柟寮忋�佸煿璁笀銆佽�冩牳鏂瑰紡銆佹槸鍚﹀悎鏍笺�佸紑濮�/缁撴潫锛夈��
+
+鍏ヨ亴鑷姩寤烘。锛氫汉浜嬬敤鎴锋柊澧炴椂鎻� `tms_person_file`銆備竴浜轰竴妗o紝`user_id` 鍞竴銆�
+
+---
+
+## 6. 蹇呴』鍐欏湪浜嬩欢/浠g爜閲岀殑瑙勫垯
+
+杩欎簺涓嶈闈犺〃缁撴瀯纭害鏉燂紝鏀惧彂甯�/淇濆瓨/褰掓。鎸夐挳锛�
+
+1. **鏃堕棿**锛歚start_time <= end_time <= close_time`锛涜�冭瘯鏃堕棿涓嶅緱鏃╀簬鍩硅寮�濮嬶紱闆嗕腑/鎿嶄綔鎺堣寮�濮嬬粨鏉熷繀椤诲悓涓�澶┿��
+2. **鍦扮偣鍗犵敤**锛氬悓涓� `place_id`锛屾湭鍙栨秷浠诲姟鐨� `[start,end)` 鎸夊皬鏃朵笉寰椾氦鍙夈��
+3. **鍩硅甯堝啿绐�**锛氶泦涓�/鎿嶄綔鎺堣涓嬶紝鍚屼竴鍩硅甯堝悓涓�鏃舵涓嶈兘鍦ㄤ袱涓换鍔°��
+4. **鏈変汉绛惧埌涓嶈兘鍙栨秷**锛涜鍙栨秷鍙仮澶嶏紙`cancelled 鈫� draft/published`锛夈��
+5. **鍙戝竷**锛氭媶涓汉浠诲姟锛涚敓鎴愬煿璁褰曪紱鑰冩牳鏂瑰紡=鍦ㄧ嚎鑰冭瘯鍒欑敓鎴� `tms_exam` 寰呰�冭锛涘悓姝ユ暀鏉愬埌璁板綍銆�
+6. **鍏抽棴鏃堕棿鍒拌揪** 鈫� 璁板綍 `archive_status=pending`銆�
+7. **褰掓。**锛氳绠楃鍒扮巼/鍚堟牸鐜囷紙鍙栨暣鑸嶅皬鏁帮級锛涙湭瀹屾垚浜哄憳鐢熸垚鍐嶇淮鎶や换鍔★紙`task_kind=continue`锛宍prev_task_no` 鎸囧悜鍘熶换鍔★級锛涘啓鍏� `tms_person_train`銆�
+8. **鏃犳晥浠诲姟**锛氭棤浜哄畬鎴愭椂鍙墦 `invalid`锛屽苟鑷姩鐢熸垚鏂颁换鍔°��
+9. **鎻愰棶浜烘暟**锛歚ceil(绛惧埌浜烘暟 * quiz_ratio / 100)`锛屽彇鏁淬��
+10. **鎺堟潈/璁″垝鍒版湡鍓� 30 澶�** 鎸� `tms_remind_rule` 鎺ㄩ�佺粰閮ㄩ棬鍩硅绠$悊鍛樺拰鍩硅甯堛��
+
+---
+
+## 7. 瑙掕壊鍙厤鏉冮檺锛屼笉鏂板缓琛�
+
+| 瑙掕壊 | 鏁版嵁鑼冨洿 |
+|---|---|
+| 绯荤粺绠$悊鍛� | 鍩虹璁剧疆 |
+| 鍏徃鍩硅绠$悊鍛� | 鍏ㄥ叕鍙歌鍒�/浠诲姟/妗f |
+| 閮ㄩ棬鍩硅绠$悊鍛� | 鏈儴闂� + `tms_admin_auth` 鎺堟潈閮ㄩ棬锛堝彲鍚笅绾э級 |
+| 鍩硅甯� | 鑷繁鐨勪换鍔℃巿璇俱�佹墦鍒嗐�佸疄鎿嶃�侀槄鍗� |
+| 鏅�氬憳宸� | 浠呬釜浜轰换鍔°�佷釜浜鸿�冭瘯璁板綍銆佷釜浜烘。妗� |
+
+銆屼釜浜哄煿璁。妗堛�嶅彧鑳界湅鑷繁锛涖�屼釜浜哄煿璁。妗堢鐞嗐�嶆寜鎺堟潈鐪嬮儴闂�/鍏徃銆�
+
+---
+
+## 8. 寤鸿涓�鏈熻寖鍥�
+
+鍏堝缓骞惰兘璺戦�氫富閾捐矾鐨勮〃锛�
+
+鍦扮偣銆佸煿璁笀銆佹暀鏉愩�侀搴撱�佽瘯棰樸�佽瘯鍗枫�佸勾搴﹁鍒掋�佸矖浣嶈鍒掋�佸煿璁换鍔°�佷釜浜轰换鍔°�佸煿璁褰曘�佺鍒版槑缁嗐�佺瓟鍗峰揩鐓с�佷釜浜烘。妗堛��
+
+浜屾湡鍐嶉摵锛氬矖浣嶇洰褰曞崌鐗堛�佹彁闂璁俱�佸疄鎿�/鐜板満鎻愰棶銆佽褰曞彉鏇淬�佽祫鏂欏綊妗c�佸鍑�/杞矖/鏂板憳宸�/澶嶅矖娴佺▼銆佸鎵撶増鏈�佹彁閱掕鍒欍��
+
+棰樺簱璇曞嵎寮曟搸涓�鏈熷氨瑕佸揩鐓ц〃锛屽惁鍒欎互鍚庢敼棰樹細姹℃煋宸插綊妗h褰曘��
+

--
Gitblit v1.8.0