灰度脚本

This commit is contained in:
2026-05-07 11:28:42 +08:00
parent 29d3d48dbe
commit 573e887ca5
8 changed files with 367 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
-- 生产数据库手工创建模板
-- 用途:首次上线时手工创建数据库和应用账号
-- 注意:执行前请先替换所有占位符
--
-- 占位符:
-- __DB_NAME__ 生产数据库名
-- __DB_USER__ 生产数据库用户名
-- __DB_PASSWORD__ 生产数据库密码
-- 1. 创建应用数据库账号
CREATE ROLE junhong_cmp
LOGIN
PASSWORD 'CtCom1zBzPQbpVNf3rCNxH';
-- 2. 创建生产数据库
CREATE DATABASE junhong_cmp_prod
OWNER junhong_cmp
ENCODING 'UTF8'
TEMPLATE template0;
-- 3. 授权
GRANT ALL PRIVILEGES ON DATABASE junhong_cmp_prod TO junhong_cmp;
-- 4. 设置默认时区(与应用一致)
ALTER DATABASE junhong_cmp_prod SET timezone TO 'Asia/Shanghai';

View File

@@ -0,0 +1,76 @@
#!/bin/bash
# 生成生产首发 schema 基线(只读,不修改源库)
#
# 用法:
# SOURCE_DSN="postgres://user:pass@host:5432/db?sslmode=disable" \
# OUTPUT_SQL="/tmp/00_schema_baseline.sql" \
# bash scripts/manual_db_release/02_generate_schema_baseline.sh
#
# 可选:
# EXTRA_EXCLUDE_TABLES="table_a,table_b"
#
# 默认排除:
# - schema_migrations
# - tb_data_usage_record
# - tb_card_replacement_record_legacy
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
OUTPUT_SQL="${OUTPUT_SQL:-${SCRIPT_DIR}/generated/00_schema_baseline.sql}"
if ! command -v pg_dump >/dev/null 2>&1; then
echo "错误:未找到 pg_dump请先安装 PostgreSQL 客户端工具。"
exit 1
fi
if [ -z "${SOURCE_DSN:-}" ]; then
echo "错误:必须设置 SOURCE_DSN。"
exit 1
fi
mkdir -p "$(dirname "${OUTPUT_SQL}")"
exclude_tables=(
"schema_migrations"
"tb_data_usage_record"
"tb_card_replacement_record_legacy"
)
if [ -n "${EXTRA_EXCLUDE_TABLES:-}" ]; then
IFS=',' read -r -a extra_tables <<< "${EXTRA_EXCLUDE_TABLES}"
for table_name in "${extra_tables[@]}"; do
table_name="$(echo "${table_name}" | xargs)"
if [ -n "${table_name}" ]; then
exclude_tables+=("${table_name}")
fi
done
fi
pg_dump_args=(
"--schema-only"
"--no-owner"
"--no-acl"
"--quote-all-identifiers"
)
for table_name in "${exclude_tables[@]}"; do
pg_dump_args+=("--exclude-table=${table_name}")
done
echo "开始生成 schema 基线:${OUTPUT_SQL}"
pg_dump "${pg_dump_args[@]}" "${SOURCE_DSN}" > "${OUTPUT_SQL}"
echo ""
echo "schema 基线生成完成。"
echo "默认已排除以下对象:"
for table_name in "${exclude_tables[@]}"; do
echo " - ${table_name}"
done
echo ""
echo "建议你人工复核以下兼容保留表是否需要继续保留:"
echo " - tb_card_replacement_request"
echo " - tb_number_card"
echo " - tb_payment_merchant_setting"
echo " - tb_dev_capability_config"

View File

@@ -0,0 +1,80 @@
#!/bin/bash
# 从已确认的源库导出基础数据(只读,不修改源库)
#
# 用法:
# SOURCE_DSN="postgres://user:pass@host:5432/db?sslmode=disable" \
# bash scripts/manual_db_release/03_export_base_data_from_source.sh
#
# 如需导出敏感表:
# SOURCE_DSN="..." INCLUDE_SENSITIVE=1 bash scripts/manual_db_release/03_export_base_data_from_source.sh
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
GENERATED_DIR="${SCRIPT_DIR}/generated"
NON_SENSITIVE_DIR="${GENERATED_DIR}/non_sensitive"
SENSITIVE_DIR="${GENERATED_DIR}/sensitive"
if ! command -v pg_dump >/dev/null 2>&1; then
echo "错误:未找到 pg_dump请先安装 PostgreSQL 客户端工具。"
exit 1
fi
if [ -z "${SOURCE_DSN:-}" ]; then
echo "错误:必须设置 SOURCE_DSN。"
exit 1
fi
mkdir -p "${NON_SENSITIVE_DIR}" "${SENSITIVE_DIR}"
non_sensitive_tables=(
"tb_permission"
"tb_role"
"tb_role_permission"
"tb_carrier"
"tb_commission_withdrawal_setting"
"tb_polling_config"
"tb_polling_concurrency_config"
"tb_polling_alert_rule"
"tb_data_cleanup_config"
)
sensitive_tables=(
"tb_wechat_config"
)
dump_table() {
local table_name="$1"
local output_file="$2"
pg_dump \
--data-only \
--column-inserts \
--no-owner \
--no-acl \
--quote-all-identifiers \
--table="${table_name}" \
"${SOURCE_DSN}" > "${output_file}"
}
echo "开始导出非敏感基础数据..."
for table_name in "${non_sensitive_tables[@]}"; do
output_file="${NON_SENSITIVE_DIR}/${table_name}.sql"
dump_table "${table_name}" "${output_file}"
echo " 已导出: ${table_name}"
done
if [ "${INCLUDE_SENSITIVE:-0}" = "1" ]; then
echo "开始导出敏感基础数据..."
for table_name in "${sensitive_tables[@]}"; do
output_file="${SENSITIVE_DIR}/${table_name}.sql"
dump_table "${table_name}" "${output_file}"
echo " 已导出: ${table_name}"
done
else
echo "未导出敏感表。若需导出 tb_wechat_config请设置 INCLUDE_SENSITIVE=1。"
fi
echo ""
echo "导出完成,输出目录:${GENERATED_DIR}"
echo "注意generated 目录下的 SQL 可能包含敏感信息,请勿直接提交到 Git。"

View File

@@ -0,0 +1,71 @@
#!/bin/bash
# 将已导出的基础数据 SQL 导入目标库
#
# 用法:
# TARGET_DSN="postgres://user:pass@host:5432/db?sslmode=disable" \
# bash scripts/manual_db_release/04_import_base_data_into_target.sh
#
# 如需导入敏感表:
# TARGET_DSN="..." INCLUDE_SENSITIVE=1 bash scripts/manual_db_release/04_import_base_data_into_target.sh
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
GENERATED_DIR="${SCRIPT_DIR}/generated"
NON_SENSITIVE_DIR="${GENERATED_DIR}/non_sensitive"
SENSITIVE_DIR="${GENERATED_DIR}/sensitive"
if ! command -v psql >/dev/null 2>&1; then
echo "错误:未找到 psql请先安装 PostgreSQL 客户端工具。"
exit 1
fi
if [ -z "${TARGET_DSN:-}" ]; then
echo "错误:必须设置 TARGET_DSN。"
exit 1
fi
import_order=(
"tb_permission"
"tb_role"
"tb_role_permission"
"tb_carrier"
"tb_commission_withdrawal_setting"
"tb_polling_config"
"tb_polling_concurrency_config"
"tb_polling_alert_rule"
"tb_data_cleanup_config"
)
echo "开始导入非敏感基础数据..."
for table_name in "${import_order[@]}"; do
sql_file="${NON_SENSITIVE_DIR}/${table_name}.sql"
if [ ! -f "${sql_file}" ]; then
echo "错误:缺少导出文件 ${sql_file}"
echo "请先执行 03_export_base_data_from_source.sh"
exit 1
fi
echo " 导入: ${table_name}"
psql "${TARGET_DSN}" -v ON_ERROR_STOP=1 -f "${sql_file}"
done
if [ "${INCLUDE_SENSITIVE:-0}" = "1" ]; then
sensitive_file="${SENSITIVE_DIR}/tb_wechat_config.sql"
if [ ! -f "${sensitive_file}" ]; then
echo "错误:缺少敏感导出文件 ${sensitive_file}"
echo "请先使用 INCLUDE_SENSITIVE=1 执行 03_export_base_data_from_source.sh"
exit 1
fi
echo " 导入: tb_wechat_config"
psql "${TARGET_DSN}" -v ON_ERROR_STOP=1 -f "${sensitive_file}"
else
echo "未导入敏感表。若需导入 tb_wechat_config请设置 INCLUDE_SENSITIVE=1。"
fi
echo ""
echo "基础数据导入完成。"
echo "下一步请执行:"
echo " 1. scripts/manual_db_release/05_repair_base_data_sequences.sql"
echo " 2. scripts/manual_db_release/06_mark_schema_migrations.sql"

View File

@@ -0,0 +1,62 @@
-- 修正基础数据导入后的自增序列
-- 用途:避免显式插入 ID 后,后续新增数据仍从旧序列值开始
SELECT setval(
pg_get_serial_sequence('"tb_permission"', 'id'),
COALESCE((SELECT MAX(id) FROM tb_permission), 1),
(SELECT COUNT(*) > 0 FROM tb_permission)
);
SELECT setval(
pg_get_serial_sequence('"tb_role"', 'id'),
COALESCE((SELECT MAX(id) FROM tb_role), 1),
(SELECT COUNT(*) > 0 FROM tb_role)
);
SELECT setval(
pg_get_serial_sequence('"tb_role_permission"', 'id'),
COALESCE((SELECT MAX(id) FROM tb_role_permission), 1),
(SELECT COUNT(*) > 0 FROM tb_role_permission)
);
SELECT setval(
pg_get_serial_sequence('"tb_carrier"', 'id'),
COALESCE((SELECT MAX(id) FROM tb_carrier), 1),
(SELECT COUNT(*) > 0 FROM tb_carrier)
);
SELECT setval(
pg_get_serial_sequence('"tb_wechat_config"', 'id'),
COALESCE((SELECT MAX(id) FROM tb_wechat_config), 1),
(SELECT COUNT(*) > 0 FROM tb_wechat_config)
);
SELECT setval(
pg_get_serial_sequence('"tb_commission_withdrawal_setting"', 'id'),
COALESCE((SELECT MAX(id) FROM tb_commission_withdrawal_setting), 1),
(SELECT COUNT(*) > 0 FROM tb_commission_withdrawal_setting)
);
SELECT setval(
pg_get_serial_sequence('"tb_polling_config"', 'id'),
COALESCE((SELECT MAX(id) FROM tb_polling_config), 1),
(SELECT COUNT(*) > 0 FROM tb_polling_config)
);
SELECT setval(
pg_get_serial_sequence('"tb_polling_concurrency_config"', 'id'),
COALESCE((SELECT MAX(id) FROM tb_polling_concurrency_config), 1),
(SELECT COUNT(*) > 0 FROM tb_polling_concurrency_config)
);
SELECT setval(
pg_get_serial_sequence('"tb_polling_alert_rule"', 'id'),
COALESCE((SELECT MAX(id) FROM tb_polling_alert_rule), 1),
(SELECT COUNT(*) > 0 FROM tb_polling_alert_rule)
);
SELECT setval(
pg_get_serial_sequence('"tb_data_cleanup_config"', 'id'),
COALESCE((SELECT MAX(id) FROM tb_data_cleanup_config), 1),
(SELECT COUNT(*) > 0 FROM tb_data_cleanup_config)
);

View File

@@ -0,0 +1,16 @@
-- 手工对齐 schema_migrations
-- 场景:首次生产上线使用“手工 schema 基线”建表后,
-- 需要告诉 golang-migrate 当前数据库已经处于哪个版本。
--
-- 当前仓库基线版本140
-- 如果你上线时仓库已新增 141+ 迁移,请同步修改此文件。
CREATE TABLE IF NOT EXISTS schema_migrations (
version BIGINT NOT NULL PRIMARY KEY,
dirty BOOLEAN NOT NULL
);
TRUNCATE TABLE schema_migrations;
INSERT INTO schema_migrations (version, dirty)
VALUES (140, FALSE);

View File

@@ -0,0 +1,35 @@
-- 手工上线后的数据库校验脚本
-- 用途:确认 schema 版本、核心基础数据、敏感配置是否已正确导入
SELECT version, dirty
FROM schema_migrations;
SELECT 'tb_permission' AS table_name, COUNT(*) AS row_count FROM tb_permission
UNION ALL
SELECT 'tb_role', COUNT(*) FROM tb_role
UNION ALL
SELECT 'tb_role_permission', COUNT(*) FROM tb_role_permission
UNION ALL
SELECT 'tb_carrier', COUNT(*) FROM tb_carrier
UNION ALL
SELECT 'tb_commission_withdrawal_setting', COUNT(*) FROM tb_commission_withdrawal_setting
UNION ALL
SELECT 'tb_polling_config', COUNT(*) FROM tb_polling_config
UNION ALL
SELECT 'tb_polling_concurrency_config', COUNT(*) FROM tb_polling_concurrency_config
UNION ALL
SELECT 'tb_polling_alert_rule', COUNT(*) FROM tb_polling_alert_rule
UNION ALL
SELECT 'tb_data_cleanup_config', COUNT(*) FROM tb_data_cleanup_config
UNION ALL
SELECT 'tb_wechat_config', COUNT(*) FROM tb_wechat_config;
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name IN ('tb_data_usage_record', 'tb_card_replacement_record_legacy')
ORDER BY table_name;
SELECT COUNT(*) AS super_admin_count
FROM tb_account
WHERE user_type = 1;

View File

@@ -0,0 +1,2 @@
*
!.gitignore