build: 数据库迁移脚本同步

This commit is contained in:
xiaojunnuo
2026-06-04 01:14:21 +08:00
parent 205a7d134e
commit 9792c616b5
8 changed files with 241 additions and 14 deletions
@@ -0,0 +1,31 @@
ALTER TABLE cd_access ADD COLUMN subtype varchar(100);
CREATE INDEX `index_access_subtype` ON `cd_access` (`subtype`);
CREATE TABLE `cd_dns_persist_record`
(
`id` bigint PRIMARY KEY AUTO_INCREMENT NOT NULL,
`user_id` bigint NOT NULL,
`project_id` bigint,
`domain` varchar(255) NOT NULL,
`main_domain` varchar(255) NOT NULL,
`ca_type` varchar(50) NOT NULL,
`acme_account_access_id` bigint NOT NULL,
`account_uri` varchar(512) NOT NULL,
`host_record` varchar(255) NOT NULL,
`record_value` longtext NOT NULL,
`policy` varchar(50),
`persist_until` bigint,
`status` varchar(50) NOT NULL DEFAULT 'pending',
`dns_provider_type` varchar(50),
`dns_provider_access` bigint,
`record_res` longtext,
`disabled` bigint NOT NULL DEFAULT 0,
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE = InnoDB ROW_FORMAT = DYNAMIC;
CREATE INDEX `index_dns_persist_user_id` ON `cd_dns_persist_record` (`user_id`);
CREATE INDEX `index_dns_persist_project_id` ON `cd_dns_persist_record` (`project_id`);
CREATE INDEX `index_dns_persist_domain` ON `cd_dns_persist_record` (`domain`(191));
CREATE INDEX `index_dns_persist_main_domain` ON `cd_dns_persist_record` (`main_domain`(191));
CREATE INDEX `index_dns_persist_account` ON `cd_dns_persist_record` (`acme_account_access_id`);
@@ -0,0 +1,29 @@
-- 激活码表
CREATE TABLE `cd_product_activation_code`
(
`id` bigint PRIMARY KEY AUTO_INCREMENT NOT NULL,
`code` varchar(50) NOT NULL,
`product_id` bigint NOT NULL,
`duration` bigint NOT NULL,
`batch_no` varchar(50) NOT NULL DEFAULT '',
`status` varchar(20) NOT NULL DEFAULT 'unused',
`used_user_id` bigint,
`used_time` bigint,
`expire_time` bigint,
`disabled_time` bigint,
`exported` bigint NOT NULL DEFAULT 0,
`export_time` bigint,
`remark` varchar(500) NOT NULL DEFAULT '',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE = InnoDB ROW_FORMAT = DYNAMIC;
CREATE UNIQUE INDEX `index_activation_code_code` ON `cd_product_activation_code` (`code`);
CREATE INDEX `index_activation_code_batch_no` ON `cd_product_activation_code` (`batch_no`);
CREATE INDEX `index_activation_code_status` ON `cd_product_activation_code` (`status`);
CREATE INDEX `index_activation_code_expire_time` ON `cd_product_activation_code` (`expire_time`);
CREATE INDEX `index_activation_code_exported` ON `cd_product_activation_code` (`exported`);
-- cd_user_suite 增加激活码来源追溯
ALTER TABLE `cd_user_suite`
ADD COLUMN `activation_code_id` bigint;
@@ -0,0 +1,16 @@
CREATE TABLE `cd_cert_apply_template`
(
`id` bigint PRIMARY KEY AUTO_INCREMENT NOT NULL,
`user_id` bigint NOT NULL,
`project_id` bigint NULL,
`name` varchar(100) NOT NULL,
`content` longtext NOT NULL,
`is_default` boolean NOT NULL DEFAULT false,
`disabled` boolean NOT NULL DEFAULT false,
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE = InnoDB ROW_FORMAT = DYNAMIC;
CREATE INDEX `index_cert_apply_template_user_id` ON `cd_cert_apply_template` (`user_id`);
CREATE INDEX `index_cert_apply_template_project_id` ON `cd_cert_apply_template` (`project_id`);
CREATE INDEX `index_cert_apply_template_default` ON `cd_cert_apply_template` (`user_id`, `project_id`, `is_default`);
@@ -0,0 +1,31 @@
ALTER TABLE cd_access ADD COLUMN subtype varchar(100);
CREATE INDEX "index_access_subtype" ON "cd_access" ("subtype");
CREATE TABLE "cd_dns_persist_record"
(
"id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL,
"user_id" bigint NOT NULL,
"project_id" bigint,
"domain" varchar(255) NOT NULL,
"main_domain" varchar(255) NOT NULL,
"ca_type" varchar(50) NOT NULL,
"acme_account_access_id" bigint NOT NULL,
"account_uri" varchar(512) NOT NULL,
"host_record" varchar(255) NOT NULL,
"record_value" text NOT NULL,
"policy" varchar(50),
"persist_until" bigint,
"status" varchar(50) NOT NULL DEFAULT 'pending',
"dns_provider_type" varchar(50),
"dns_provider_access" bigint,
"record_res" text,
"disabled" bigint NOT NULL DEFAULT 0,
"create_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP),
"update_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP)
);
CREATE INDEX "index_dns_persist_user_id" ON "cd_dns_persist_record" ("user_id");
CREATE INDEX "index_dns_persist_project_id" ON "cd_dns_persist_record" ("project_id");
CREATE INDEX "index_dns_persist_domain" ON "cd_dns_persist_record" ("domain");
CREATE INDEX "index_dns_persist_main_domain" ON "cd_dns_persist_record" ("main_domain");
CREATE INDEX "index_dns_persist_account" ON "cd_dns_persist_record" ("acme_account_access_id");
@@ -0,0 +1,29 @@
-- 激活码表
CREATE TABLE "cd_product_activation_code"
(
"id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL,
"code" varchar(50) NOT NULL,
"product_id" bigint NOT NULL,
"duration" bigint NOT NULL,
"batch_no" varchar(50) NOT NULL DEFAULT '',
"status" varchar(20) NOT NULL DEFAULT 'unused',
"used_user_id" bigint,
"used_time" bigint,
"expire_time" bigint,
"disabled_time" bigint,
"exported" bigint NOT NULL DEFAULT 0,
"export_time" bigint,
"remark" varchar(500) NOT NULL DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP),
"update_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP)
);
CREATE UNIQUE INDEX "index_activation_code_code" ON "cd_product_activation_code" ("code");
CREATE INDEX "index_activation_code_batch_no" ON "cd_product_activation_code" ("batch_no");
CREATE INDEX "index_activation_code_status" ON "cd_product_activation_code" ("status");
CREATE INDEX "index_activation_code_expire_time" ON "cd_product_activation_code" ("expire_time");
CREATE INDEX "index_activation_code_exported" ON "cd_product_activation_code" ("exported");
-- cd_user_suite 增加激活码来源追溯
ALTER TABLE "cd_user_suite"
ADD COLUMN "activation_code_id" bigint;
@@ -0,0 +1,16 @@
CREATE TABLE "cd_cert_apply_template"
(
"id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL,
"user_id" bigint NOT NULL,
"project_id" bigint NULL,
"name" varchar(100) NOT NULL,
"content" text NOT NULL,
"is_default" boolean NOT NULL DEFAULT (false),
"disabled" boolean NOT NULL DEFAULT (false),
"create_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP),
"update_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP)
);
CREATE INDEX "index_cert_apply_template_user_id" ON "cd_cert_apply_template" ("user_id");
CREATE INDEX "index_cert_apply_template_project_id" ON "cd_cert_apply_template" ("project_id");
CREATE INDEX "index_cert_apply_template_default" ON "cd_cert_apply_template" ("user_id", "project_id", "is_default");
+87 -14
View File
@@ -1,4 +1,7 @@
import fs from "fs";
const MYSQL_INDEX_PREFIX_LENGTH = 191;
const MYSQL_TABLE_OPTIONS = "ENGINE = InnoDB ROW_FORMAT = DYNAMIC";
/**
* ## sqlite与postgres不同点
* 1.
@@ -55,6 +58,88 @@ function transformPG() {
}
}
function buildMysqlTableColumnMap(sql) {
const tableColumnMap = new Map();
const createTableReg = /CREATE TABLE `([^`]*)`[\s\S]*?;/gi;
for (const match of sql.matchAll(createTableReg)) {
const [statement, tableName] = match;
const tableBody = getCreateTableBody(statement);
const columnMap = new Map();
const columnReg = /`([^`]*)`\s+varchar\((\d+)\)/gi;
for (const columnMatch of tableBody.matchAll(columnReg)) {
const [, columnName, length] = columnMatch;
columnMap.set(columnName, Number(length));
}
tableColumnMap.set(tableName, columnMap);
}
const alterAddColumnReg = /ALTER TABLE\s+`?([^`\s]+)`?\s+ADD COLUMN\s+`?([^`\s]+)`?\s+varchar\((\d+)\)/gi;
for (const match of sql.matchAll(alterAddColumnReg)) {
const [, tableName, columnName, length] = match;
const columnMap = tableColumnMap.get(tableName) || new Map();
columnMap.set(columnName, Number(length));
tableColumnMap.set(tableName, columnMap);
}
return tableColumnMap;
}
function getCreateTableBody(statement) {
const start = statement.indexOf("(");
const end = statement.lastIndexOf(")");
if (start === -1 || end === -1 || end <= start) {
return "";
}
return statement.substring(start + 1, end);
}
function appendMysqlTableOptions(sql) {
const createTableReg = /CREATE TABLE `([^`]*)`[\s\S]*?;/gi;
return sql.replace(createTableReg, statement => {
const sqlWithoutSemicolon = statement.replace(/;\s*$/, "");
const sqlWithoutOptions = sqlWithoutSemicolon.replace(/\s+ENGINE\s*=\s*\w+(?:\s+ROW_FORMAT\s*=\s*\w+)?\s*$/i, "");
return `${sqlWithoutOptions} ${MYSQL_TABLE_OPTIONS};`;
});
}
function addMysqlIndexPrefix(sql) {
const tableColumnMap = buildMysqlTableColumnMap(sql);
const createIndexReg = /CREATE\s+(UNIQUE\s+)?INDEX\s+`([^`]*)`\s+ON\s+`([^`]*)`\s*\(([^;]*)\);/gi;
return sql.replace(createIndexReg, (statement, uniqueKeyword, indexName, tableName, columns) => {
const columnMap = tableColumnMap.get(tableName);
if (!columnMap) {
return statement;
}
const parsedColumns = columns.split(",").map(item => {
const columnMatch = item.trim().match(/^`([^`]*)`(?:\((\d+)\))?$/);
if (!columnMatch) {
return item.trim();
}
const [, columnName, prefixLength] = columnMatch;
const columnLength = columnMap.get(columnName);
if (!columnLength || columnLength <= MYSQL_INDEX_PREFIX_LENGTH) {
return item.trim();
}
if (prefixLength && Number(prefixLength) <= MYSQL_INDEX_PREFIX_LENGTH) {
return item.trim();
}
// MySQL 5.7 老配置下 utf8mb4 varchar(255) 完整索引可能超过 767/1000 字节限制。
if (uniqueKeyword) {
throw new Error(`唯一索引 ${indexName} 的字段 ${tableName}.${columnName} 长度超过 ${MYSQL_INDEX_PREFIX_LENGTH},不能自动改成前缀索引,请缩短字段长度或增加 hash 字段`);
}
return `\`${columnName}\`(${MYSQL_INDEX_PREFIX_LENGTH})`;
});
const unique = uniqueKeyword || "";
return `CREATE ${unique}INDEX \`${indexName}\` ON \`${tableName}\` (${parsedColumns.join(", ")});`;
});
}
function transformMysql() {
// 读取文件列表
const sqliteFiles = fs.readdirSync("./migration/");
@@ -76,20 +161,8 @@ function transformMysql() {
//双引号 替换成反引号
pgSql = pgSql.replaceAll(/"/g, "`");
//提取所有的 create table 的表格name
const tableNames = pgSql.match(/CREATE TABLE `([^`]*)`/g);
if (tableNames && tableNames.length > 0) {
for (const item of tableNames) {
/**
* CREATE TABLE `cd_project`
CREATE TABLE `cd_project_member`
CREATE TABLE `cd_audit_log`
*/
//提取表名
const tableName = item.match(/`([^`]*)`/)[1];
pgSql += `\nALTER TABLE \`${tableName}\` ENGINE = InnoDB;`;
}
}
pgSql = appendMysqlTableOptions(pgSql);
pgSql = addMysqlIndexPrefix(pgSql);
fs.writeFileSync(`./migration-mysql/${notFile}`, pgSql);
}
@@ -134,5 +134,7 @@ export class MainConfiguration {
});
logger.info("当前环境:", this.app.getEnv()); // prod
}
}