# -*- coding: utf-8 -*-
|
"""重新生成 docs/sql/config_export_all_20260928.sql(全量重生成,替换 0918 版)。
|
|
阶段一:当前源库 ruoyi-vue-pro 全库 416 张表 CREATE TABLE + 6 类配置数据(mysqldump 字面量)。
|
阶段二:沿用 0918 文件的跨库身份/菜单/OAuth2 补导段(逐字复用)。
|
"""
|
import re
|
import subprocess
|
import sys
|
|
MYSQL_BIN = r"D:\MySQL\mysql-8.0.31-winx64\bin"
|
DB = "ruoyi-vue-pro"
|
USER = "root"
|
PWD = "1234"
|
|
BASE_DIR = r"D:\develop\XingRuan\MOM\mom-pro2-after\docs\sql"
|
SRC_0918 = BASE_DIR + r"\config_export_all_20260918.sql"
|
OUT = BASE_DIR + r"\config_export_all_20260928.sql"
|
|
MYSQL = MYSQL_BIN + r"\mysql.exe"
|
MYSQLDUMP = MYSQL_BIN + r"\mysqldump.exe"
|
|
COMMON = ["--default-character-set=utf8mb4", "--no-tablespaces", "--skip-comments",
|
"--skip-add-locks", "--skip-disable-keys", "--skip-triggers",
|
"--column-statistics=0", "--set-gtid-purged=OFF", "--single-transaction"]
|
|
# ---------------- 6 类配置表(表名, 过滤条件 None=全表) ----------------
|
SECTIONS = [
|
("① 协同审批流程(引擎元数据+定义,仅激活流程,剔历史/运行/身份)", [
|
("act_ge_property", None),
|
("act_id_property", None),
|
("act_re_procdef", "SUSPENSION_STATE_=1"),
|
("act_re_deployment", "ID_ IN (SELECT DEPLOYMENT_ID_ FROM act_re_procdef WHERE SUSPENSION_STATE_=1)"),
|
("act_ge_bytearray",
|
"DEPLOYMENT_ID_ IN (SELECT DEPLOYMENT_ID_ FROM act_re_procdef WHERE SUSPENSION_STATE_=1) "
|
"OR NAME_ IN ('source','source-extra')"),
|
("act_re_model", None),
|
("bpm_process_definition_info", "deleted=0"),
|
]),
|
("② 协同(业务附件配置 + 分类 + 表单 + 扩展配置)", [
|
("bpm_business_attach", "deleted=0"),
|
("bpm_category", "deleted=0"),
|
("bpm_form", "deleted=0"),
|
("bpm_user_group", "deleted=0"),
|
("bpm_process_listener", "deleted=0"),
|
("bpm_process_expression", "deleted=0"),
|
]),
|
("③ BI 决策大屏 / go-view", [
|
("bi_dashboard", "deleted=0"),
|
("bi_chart_config", "deleted=0"),
|
("bi_data_source_config", "deleted=0"),
|
("bi_warehouse_coordinate", "deleted=0"),
|
("report_go_view_project", "deleted=0"),
|
]),
|
("④ MES 编码规则(剔流水记录)", [
|
("mes_md_auto_code_rule", "deleted=0"),
|
("mes_md_auto_code_part", "deleted=0"),
|
]),
|
("⑤ 系统配置主数据(字典/租户/公司配置;剔身份/菜单/日志)", [
|
("system_dict_type", "deleted=0"),
|
("system_dict_data", "deleted=0"),
|
("system_tenant", "deleted=0"),
|
("system_tenant_package", "deleted=0"),
|
("system_company_config", "deleted=0"),
|
]),
|
("⑥ AI 配置(仅模型+key)+ 基础设施配置管理", [
|
("ai_model", "deleted=0"),
|
("ai_api_key", "deleted=0"),
|
("infra_config", "deleted=0"),
|
("infra_job", "deleted=0"),
|
("infra_file_config", "deleted=0"),
|
("infra_data_source_config", "deleted=0"),
|
("infra_codegen_table", "deleted=0"),
|
("infra_codegen_column", "deleted=0"),
|
]),
|
]
|
|
|
def run(cmd):
|
p = subprocess.run(cmd, capture_output=True)
|
if p.returncode != 0:
|
sys.stderr.write("FAILED: %s\n%s\n" % (" ".join(cmd), p.stderr.decode("utf-8", "replace")))
|
sys.exit(1)
|
return p.stdout.decode("utf-8")
|
|
|
def strip_mysqldump_noise(text):
|
out = []
|
for line in text.splitlines():
|
if line.startswith("/*!") or line.startswith("--"):
|
continue
|
out.append(line)
|
return out
|
|
|
def fetch_tables():
|
sql = ("SELECT table_name FROM information_schema.tables "
|
"WHERE table_schema='%s' AND table_type='BASE TABLE' ORDER BY table_name" % DB)
|
out = run([MYSQL, "-u" + USER, "-p" + PWD, "--default-character-set=utf8mb4", "-N", "-B", "-e", sql])
|
return [l.strip() for l in out.splitlines() if l.strip()]
|
|
|
def fetch_view_count():
|
sql = ("SELECT COUNT(*) FROM information_schema.views WHERE table_schema='%s'" % DB)
|
out = run([MYSQL, "-u" + USER, "-p" + PWD, "--default-character-set=utf8mb4", "-N", "-B", "-e", sql])
|
return int(out.strip())
|
|
|
def dump_ddl(tables):
|
"""一次导出全部表结构,切分成 {表名: ddl块}。"""
|
out = run([MYSQLDUMP, "-u" + USER, "-p" + PWD] + COMMON + ["--no-data", DB] + tables)
|
lines = strip_mysqldump_noise(out)
|
blocks = {}
|
cur = None
|
buf = []
|
for line in lines:
|
m = re.match(r"^DROP TABLE IF EXISTS `([^`]+)`;", line)
|
if m:
|
if cur:
|
blocks[cur] = "\n".join(buf).rstrip()
|
cur = m.group(1)
|
buf = [line]
|
elif cur is not None:
|
buf.append(line)
|
if cur:
|
blocks[cur] = "\n".join(buf).rstrip()
|
return blocks
|
|
|
def dump_data(tables_filters):
|
"""tables_filters: [(table, where)] 相同 where 的分组调用;返回 {表名: insert语句}。"""
|
groups = {}
|
for t, w in tables_filters:
|
if w is None:
|
groups.setdefault(None, []).append(t)
|
else:
|
groups.setdefault(w, []).append(t)
|
data = {}
|
for where, tables in groups.items():
|
cmd = [MYSQLDUMP, "-u" + USER, "-p" + PWD] + COMMON + \
|
["--no-create-info", "--complete-insert", "--hex-blob", "--order-by-primary"]
|
if where is not None:
|
cmd.append("--where=" + where)
|
cmd.append(DB)
|
cmd += tables
|
out = run(cmd)
|
lines = strip_mysqldump_noise(out)
|
stmt_lines = []
|
for line in lines:
|
if not line.strip():
|
continue
|
if line.startswith("INSERT INTO "):
|
stmt_lines = [line]
|
elif stmt_lines:
|
stmt_lines.append(line)
|
else:
|
sys.stderr.write("UNEXPECTED line: %s\n" % line[:120])
|
continue
|
stmt = "\n".join(stmt_lines)
|
if stmt.rstrip().endswith(";"):
|
m = re.match(r"^INSERT INTO `([^`]+)`", stmt)
|
data[m.group(1)] = stmt.rstrip()
|
stmt_lines = []
|
return data
|
|
|
def fetch_counts(tables_filters):
|
parts = []
|
for t, w in tables_filters:
|
parts.append("SELECT '%s' AS t, COUNT(*) AS c FROM `%s`%s"
|
% (t, t, (" WHERE " + w) if w else ""))
|
sql = " UNION ALL ".join(parts)
|
out = run([MYSQL, "-u" + USER, "-p" + PWD, "--default-character-set=utf8mb4", "-N", "-B", DB, "-e", sql])
|
counts = {}
|
for line in out.splitlines():
|
if not line.strip():
|
continue
|
k, v = line.split("\t")
|
counts[k] = int(v)
|
return counts
|
|
|
def load_stage2_block():
|
with open(SRC_0918, encoding="utf-8") as f:
|
lines = f.read().splitlines()
|
for i, line in enumerate(lines):
|
if "阶段二:补导身份" in line:
|
start = i - 1 # 上方 =============== 横线
|
return "\n".join(lines[start:]).rstrip() + "\n"
|
raise RuntimeError("0918 文件未找到阶段二段落")
|
|
|
HEADER = """-- =======================================================================
|
-- 文件:config_export_all_20260928.sql
|
-- 用途:ruoyi-vue-pro「全库表结构 + 6 类配置数据」导出,供同结构全新库初始化
|
-- 阶段一:全库 {n_tables} 张表 CREATE TABLE + 6 类配置数据(字面量,自包含,不依赖源库在线)
|
-- 阶段二:跨库补导 菜单/角色/用户/OAuth2 等身份数据(执行期从源库拷贝)
|
-- 来源库:ruoyi-vue-pro
|
-- 基线:2026-09-18 版(419 张表)→ 本次为**全量重生成**(不再基于旧文件打补丁):
|
-- 直接按当前源库最新状态重导,一次性覆盖 0918 之后全部结构与配置漂移。
|
-- 本次变更(相对 0918,2026-09-28):
|
-- ① 表结构:419 → {n_tables} 张
|
-- - 删除 6 张历史备份表(bpm_bak_20260904_bytearray/category/deployment/model/procdef/procinfo,源库已无)
|
-- + 新增 3 张表(after_sale_problem、after_sale_visit、bpm_process_auto_approve_config,仅结构,源库无数据)
|
-- + 10 张表 DDL 与 0918 不同,以当前源库为准(after_sale_ticket、mes_qc_indicator、mes_qc_indicator_result_detail、
|
-- mes_qc_ipqc、mes_qc_iqc、mes_qc_oqc、mes_qc_rqc、mes_qc_template、mes_qc_template_indicator、mes_wm_return_issue_line)
|
-- ② 配置数据以当前源库为准重导(下列为 0918 声明值 → 本次导出值):
|
-- - system_dict_type 241→{n_dicttype}、system_dict_data 1157→{n_dictdata}(含 hrm_holiday_type 节假日类型等新增字典)
|
-- - system_company_config 0→1、bi_chart_config 36→37、bi_warehouse_coordinate 1→2、ai_api_key 1→3、bpm_user_group 3→4
|
-- - bpm_business_attach 11→4、bpm_category 22→15、bpm_form 4→3、bpm_process_listener 1→0(源库已删除的行不再导出)
|
-- - 协同流程定义按源库当前**激活**集合重导({n_procdef} 个流程定义 + 对应部署/字节资源 + 设计态模型)
|
-- ③ 菜单/权限节点(节假日配置 6014/60141-60145、各模块导入按钮 1075311/1075417-1075420 等)由阶段二
|
-- 跨库全量拷贝自动携带,无需改动本文件数据段;在此登记以说明本版文件确实覆盖这些变更。
|
-- 验证:{verify}
|
-- 范围:全库 BASE TABLE 共 {n_tables} 张,全部输出 CREATE TABLE;
|
-- 仅以下配置类表带数据(INSERT),其余业务/系统/运行历史表只保留空表结构:
|
-- ① 协同审批流程(引擎元数据+定义,仅激活流程,剔历史/运行/身份):act_ge_property、act_id_property、act_re_procdef、act_re_deployment、act_ge_bytearray、act_re_model、bpm_process_definition_info
|
-- ② 协同(业务附件配置 + 分类 + 表单 + 扩展配置):bpm_business_attach、bpm_category、bpm_form、bpm_user_group、bpm_process_listener、bpm_process_expression
|
-- ③ BI 决策大屏 / go-view:bi_dashboard、bi_chart_config、bi_data_source_config、bi_warehouse_coordinate、report_go_view_project
|
-- ④ MES 编码规则(剔流水记录):mes_md_auto_code_rule、mes_md_auto_code_part
|
-- ⑤ 系统配置主数据(字典/租户/公司配置;剔身份/菜单/日志):system_dict_type、system_dict_data、system_tenant、system_tenant_package、system_company_config
|
-- ⚠ 身份/权限类表(system_users/role/menu/role_menu/user_role/dept/post/oauth2_client)此处仅建空表结构,数据由文件末尾"阶段二"跨库补导。
|
-- ⑥ AI 配置(仅模型+key)+ 基础设施配置管理:ai_model、ai_api_key、infra_config、infra_job、infra_file_config、infra_data_source_config、infra_codegen_table、infra_codegen_column
|
-- 过滤:协同流程仅保留激活(SUSPENSION_STATE_=1)定义及其部署资源(bpmn/png 字节);
|
-- 设计态模型 act_re_model 与模型源码字节(NAME_=source/source-extra)按源库全量导出(含个别已删除流程残留的模型,
|
-- 源库实际存在即保留,供流程设计器打开);运行期变量字节(var-*/hist.var-*)一律不导出;
|
-- 其余配置表仅导 deleted=0;编码规则剔除流水记录 mes_md_auto_code_record。
|
-- ⚠ 脚本含全库 DROP TABLE IF EXISTS,会把除本脚本建表外的所有表删除重建,
|
-- 业务表数据将全部清空,仅可在全新/可覆盖的空库执行!
|
-- =======================================================================
|
"""
|
|
VERIFY_PLACEHOLDER = "(待空库整份执行验证后回填;见文件末尾生成备注)"
|
|
|
def main():
|
tables = fetch_tables()
|
views = fetch_view_count()
|
if views:
|
print("WARNING: 源库存在 %d 个视图,本生成器未处理" % views)
|
|
ddl = dump_ddl(tables)
|
missing = [t for t in tables if t not in ddl]
|
extra = [t for t in ddl if t not in tables]
|
if missing or extra:
|
print("DDL 集合不一致 missing=%s extra=%s" % (missing, extra))
|
sys.exit(1)
|
print("tables=%d ddl_blocks=%d" % (len(tables), len(ddl)))
|
|
all_cfg = [tf for _, tfs in SECTIONS for tf in tfs]
|
cfg_names = [t for t, _ in all_cfg]
|
counts = fetch_counts(all_cfg)
|
data = dump_data(all_cfg)
|
for t in cfg_names:
|
if counts.get(t, 0) > 0 and t not in data:
|
print("MISSING data for %s (count=%d)" % (t, counts[t]))
|
sys.exit(1)
|
print("config tables=%d, with data=%d" % (len(cfg_names), len(data)))
|
|
rest = [t for t in tables if t not in set(cfg_names)]
|
print("rest tables=%d" % len(rest))
|
|
parts = []
|
header = HEADER.format(
|
n_tables=len(tables),
|
n_dicttype=counts["system_dict_type"],
|
n_dictdata=counts["system_dict_data"],
|
n_procdef=counts["act_re_procdef"],
|
verify=VERIFY_PLACEHOLDER,
|
)
|
parts.append(header)
|
parts.append("SET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n"
|
"SET @OLD_SQL_MODE = @@SQL_MODE, SQL_MODE = 'NO_AUTO_VALUE_ON_ZERO';\n\n")
|
|
for title, tfs in SECTIONS:
|
parts.append("\n-- ***********************************************************************\n")
|
parts.append("-- %s(DDL + 数据)\n" % title)
|
parts.append("-- ***********************************************************************\n\n")
|
for t, w in tfs:
|
n = counts.get(t, 0)
|
parts.append("-- ============ 表:%s(导出 %d 行) ============\n" % (t, n))
|
parts.append(ddl[t] + "\n")
|
if n > 0:
|
parts.append(data[t] + "\n")
|
parts.append("\n")
|
|
parts.append("\n-- ***********************************************************************\n")
|
parts.append("-- 其余 %d 张表(仅表结构,无数据)\n" % len(rest))
|
parts.append("-- ***********************************************************************\n\n")
|
for t in rest:
|
parts.append("-- ============ 表:%s(仅 DDL,无数据) ============\n" % t)
|
parts.append(ddl[t] + "\n\n")
|
|
parts.append("\nSET FOREIGN_KEY_CHECKS = 1;\nSET SQL_MODE = @OLD_SQL_MODE;\n\n\n")
|
parts.append(load_stage2_block())
|
|
text = "".join(parts)
|
with open(OUT, "w", encoding="utf-8", newline="\n") as f:
|
f.write(text)
|
print("WROTE %s bytes=%d lines=%d" % (OUT, len(text.encode("utf-8")), text.count("\n")))
|
|
|
if __name__ == "__main__":
|
main()
|