#!/usr/bin/env python3 # -*- coding: utf-8 -*- """json2ddl 等价实现(ocai database-table-definition-spec): 读取 models/{module}/*.json(summary/fields/indexes/codes),生成幂等 MySQL/MariaDB DDL。 幂等性:CREATE TABLE IF NOT EXISTS;索引先查 information_schema 再按需补建(可重跑)。 用法:python3 scripts/json2ddl.py [输出sql文件] """ import json, os, sys, glob TYPE_MAP = { "varchar": lambda f: f"VARCHAR({f.get('length', 64)})", "text": lambda f: "TEXT", "int": lambda f: "INT", "date": lambda f: "DATE", "timestamp": lambda f: "TIMESTAMP NULL DEFAULT NULL", "decimal": lambda f: f"DECIMAL({f.get('precision', 18)},{f.get('scale', 4)})", } def render_field(f): t = f["type"] if t not in TYPE_MAP: raise ValueError(f"未知字段类型 {t} ({f.get('name')})") col = f"`{f['name']}` {TYPE_MAP[t](f)}" if f.get("notnull"): col += " NOT NULL" if "default" in f and t != "timestamp": dv = f["default"] col += f" DEFAULT '{dv}'" if isinstance(dv, str) else f" DEFAULT {dv}" col += f" COMMENT '{f['title']}'" return col def render_index_sql(table, idx): kind = "UNIQUE KEY" if idx.get("unique") else "KEY" cols = ",".join(f"`{c}`" for c in idx["fields"]) return f" {kind} `{idx['name']}` ({cols})" def gen_table_ddl(path): doc = json.load(open(path, encoding="utf-8")) s = doc["summary"][0] table = s["name"] lines = [render_field(f) for f in doc["fields"]] pk = ",".join(f"`{c}`" for c in s.get("pk", ["id"])) lines.append(f" PRIMARY KEY ({pk})") for idx in doc.get("indexes", []): lines.append(render_index_sql(table, idx)) charset = s.get("charset", "utf8mb4") collate = s.get("collate", "utf8mb4_unicode_ci") title = s.get("title", table) return table, (f"CREATE TABLE IF NOT EXISTS `{table}` (\n" + ",\n".join(lines) + f"\n) ENGINE=InnoDB DEFAULT CHARSET={charset} COLLATE={collate} COMMENT='{title}';") def gen_index_guard_sql(table, idx): """运行时补索引(建表时已含;此处供重跑补漏,apply_ddl.py 使用)""" kind = "UNIQUE INDEX" if idx.get("unique") else "INDEX" cols = ",".join(f"`{c}`" for c in idx["fields"]) return f"ALTER TABLE `{table}` ADD {kind} `{idx['name']}` ({cols});" def main(): src = sys.argv[1] if len(sys.argv) > 1 else "models" out = sys.argv[2] if len(sys.argv) > 2 else None parts = ["-- 人事系统 hr-web 批次1 DDL(json2ddl 生成,幂等可重跑)", "-- 来源:models/*.json(database-design.md v3.0,49 表);目标库:hrs(MariaDB)", "-- 执行:mysql -utest -ptest123 hrs < mysql.ddl.sql 或 python3 scripts/apply_ddl.py", "SET NAMES utf8mb4;", ""] n = 0 for moddir in sorted(glob.glob(os.path.join(src, "*"))): if not os.path.isdir(moddir): continue files = sorted(glob.glob(os.path.join(moddir, "*.json"))) if not files: continue parts.append(f"-- ===== {os.path.basename(moddir)}({len(files)} 张)=====") for p in files: table, ddl = gen_table_ddl(p) parts.append(f"-- {table}") parts.append(ddl) parts.append("") n += 1 parts.append(f"-- 共 {n} 张表") sql = "\n".join(parts) + "\n" if out: with open(out, "w", encoding="utf-8") as f: f.write(sql) print(f"生成 {out}:{n} 张表") else: sys.stdout.write(sql) if __name__ == "__main__": main()