| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- # -*- coding: utf-8 -*-
- """
- 从《智能穴位识别数据模型》xlsx 生成种子 SQL,并写回 V1__init.sql 中
- 「-- >>> GENERATED_SEED_START」与「-- <<< GENERATED_SEED_END」之间的内容。
- 读取工作表:
- - 第 1 张:穴位数据模型(筛选「艾灸椅产品穴位」为「是」,且身体部位∈{背部,颈部},
- 部位分区∈{背部,骶区,颈后部,腰部};列名支持「身体部位」或「身体补位」)
- - 第 2 张:症状模型 -> symptom
- - 第 3 张:部位定位模型 -> body_region
- 并追加:admin_role、admin_user(默认 admin/admin 的 BCrypt)、admin_user_role、device_model。
- 需在环境中安装:pip install openpyxl
- """
- import glob
- import os
- try:
- import openpyxl
- except ImportError:
- raise SystemExit("pip install openpyxl")
- BASE = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
- os.chdir(BASE)
- V1_SQL = os.path.join(BASE, "src", "main", "resources", "db", "migration", "V1__init.sql")
- START_TAG = "-- >>> GENERATED_SEED_START"
- END_TAG = "-- <<< GENERATED_SEED_END"
- def esc(s):
- if s is None:
- return "NULL"
- s = str(s).replace("\\", "\\\\").replace("'", "''").replace("\r", " ").replace("\n", " ")
- return "'" + s + "'"
- def main():
- paths = glob.glob(os.path.join(BASE, "*数据模型*.xlsx"))
- if not paths:
- paths = glob.glob(os.path.join(BASE, "*.xlsx"))
- paths = [p for p in paths if "数据模型" in os.path.basename(p) or "模型" in os.path.basename(p)]
- if not paths:
- raise SystemExit("No data model xlsx found")
- path = paths[0]
- wb = openpyxl.load_workbook(path, read_only=True, data_only=True)
- lines = []
- lines.append("SET FOREIGN_KEY_CHECKS = 0;")
- # Symptoms sheet 2
- ws_sym = wb[wb.sheetnames[1]]
- rows_sym = list(ws_sym.iter_rows(values_only=True))
- lines.append("")
- lines.append("-- 症状字典(Excel「症状模型」)")
- lines.append("INSERT IGNORE INTO symptom (code, name) VALUES ")
- vals = []
- for row in rows_sym[1:]:
- if not row or row[0] is None:
- continue
- code = esc(str(row[0]).strip())
- name = esc(str(row[1]).strip() if len(row) > 1 and row[1] is not None else "")
- vals.append("({0}, {1})".format(code, name))
- lines.append(",\n".join(vals) + ";")
- # Body regions sheet 3
- ws_br = wb[wb.sheetnames[2]]
- rows_br = list(ws_br.iter_rows(values_only=True))
- lines.append("")
- lines.append("-- 身体部位与分区(Excel「部位定位模型」)")
- lines.append("INSERT IGNORE INTO body_region (body_part, region_partition) VALUES ")
- vals = []
- for row in rows_br[1:]:
- if row is None:
- continue
- bp = row[0]
- rp = row[1]
- if bp is None and rp is None:
- continue
- vals.append("({0}, {1})".format(esc(bp), esc(rp)))
- lines.append(",\n".join(vals) + ";")
- # Acupoints sheet 1
- ws = wb[wb.sheetnames[0]]
- header = next(ws.iter_rows(min_row=1, max_row=1, values_only=True))
- h = list(header)
- def col(*names):
- for n in names:
- if n in h:
- return h.index(n)
- raise SystemExit("Excel 表头缺少列: " + " / ".join(names))
- idx_chair = col("艾灸椅产品穴位")
- idx_body = col("身体部位", "身体补位")
- cols = {
- "serial_no": h.index("序号"),
- "code": h.index("穴位编号"),
- "name": h.index("穴位名称"),
- "side_type": h.index("单/双穴"),
- "meridian": h.index("所属经络"),
- "body_region": idx_body,
- "region_partition": h.index("部位分区"),
- "location_text": h.index("穴位定位"),
- "relative_position_text": h.index("相对位置"),
- "indications": h.index("主管疾病"),
- "symptom_coverage": h.index("产品症状覆盖"),
- "acupoint_method": h.index("取穴"),
- "remark": len(h) - 1,
- }
- allowed_body = {"背部", "颈部"}
- allowed_partition = {"背部", "骶区", "颈后部", "腰部"}
- lines.append("")
- lines.append(
- "-- 标准穴位:艾灸椅=是 ∧ 身体部位∈(背部,颈部) ∧ 部位分区∈(背部,骶区,颈后部,腰部)"
- )
- lines.append(
- "INSERT IGNORE INTO acupoint (serial_no, code, name, side_type, meridian, body_region, region_partition, "
- "location_text, relative_position_text, indications, symptom_coverage, acupoint_method, chair_product, remark) VALUES "
- )
- vals = []
- for row in ws.iter_rows(min_row=2, values_only=True):
- if not row or row[idx_chair] != "是":
- continue
- r = list(row) + [None] * 20
- def g(key):
- i = cols[key]
- v = r[i] if i < len(r) else None
- return v
- br = g("body_region")
- rp = g("region_partition")
- if br is None or rp is None:
- continue
- brs = str(br).strip()
- rps = str(rp).strip()
- if brs not in allowed_body or rps not in allowed_partition:
- continue
- remark = r[cols["remark"]] if cols["remark"] < len(r) else None
- vals.append(
- "({0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, 1, {12})".format(
- esc(g("serial_no")),
- esc(g("code")),
- esc(g("name")),
- esc(g("side_type")),
- esc(g("meridian")),
- esc(g("body_region")),
- esc(g("region_partition")),
- esc(g("location_text")),
- esc(g("relative_position_text")),
- esc(g("indications")),
- esc(g("symptom_coverage")),
- esc(g("acupoint_method")),
- esc(remark),
- )
- )
- lines.append(",\n".join(vals) + ";")
- bcrypt_password = "$2b$10$yW9CwM2u/vqz4CGZsQXPyeiKYtsu4PbAj.vKstKoY4hbFT6I0LiNm"
- lines.append("")
- lines.append("-- 后台角色、默认管理员、默认设备(联调)")
- lines.append(
- "INSERT IGNORE INTO admin_role (code, name) VALUES "
- "('SUPER_ADMIN', '超级管理员'), ('ADMIN', '管理员'), ('OPERATOR', '运营');"
- )
- lines.append(
- "INSERT IGNORE INTO admin_user (username, password_hash, display_name, status) VALUES "
- "('admin', '{0}', '系统管理员', 'ACTIVE');".format(bcrypt_password)
- )
- lines.append("INSERT IGNORE INTO admin_user_role (admin_user_id, admin_role_id) VALUES (1, 1);")
- lines.append(
- "INSERT IGNORE INTO device_model (code, name, stroke_range_x, stroke_range_y, stroke_range_z, "
- "origin_offset_x, origin_offset_y, origin_offset_z, safety_distance) VALUES "
- "('CHAIR-DEFAULT', '默认艾灸椅', 800, 600, 400, 0, 0, 0, 10);"
- )
- lines.append("-- INSERT IGNORE 不更新已存在的 admin 行:将管理员 admin 密码统一为明文 admin(BCrypt)")
- lines.append(
- "UPDATE admin_user SET password_hash = '{0}' WHERE username = 'admin' AND deleted = 0;".format(
- bcrypt_password
- )
- )
- lines.append("")
- lines.append("SET FOREIGN_KEY_CHECKS = 1;")
- with open(V1_SQL, encoding="utf-8") as f:
- content = f.read()
- clines = content.splitlines()
- si = ei = None
- for i, L in enumerate(clines):
- if L.strip() == START_TAG:
- si = i
- elif si is not None and L.strip() == END_TAG:
- ei = i
- break
- if si is None or ei is None or ei <= si:
- raise SystemExit("{0} 中缺少 {1} / {2} 标记".format(V1_SQL, START_TAG, END_TAG))
- new_clines = clines[: si + 1] + lines + clines[ei:]
- with open(V1_SQL, "w", encoding="utf-8", newline="\n") as f:
- f.write("\n".join(new_clines) + "\n")
- print("Updated seed in", V1_SQL, "acupoints:", len(vals))
- wb.close()
- if __name__ == "__main__":
- main()
|