feat(migrate): add staging and monitor module database setup scripts

新增了两套数据库一键建表脚本:
1. PostgreSQL缓冲表建表脚本,用于APS数据清洗缓冲表初始化
2. SQLite监控模块建表脚本,用于API监控数据记录表初始化
同时附带了对应的SQL建表语句文件
This commit is contained in:
2026-05-28 16:17:34 +08:00
parent 100218abe4
commit c1075bc9ab
4 changed files with 633 additions and 1 deletions
+192
View File
@@ -0,0 +1,192 @@
-- =====================================================
-- 监控模块数据库表建表脚本 (SQLite版本)
-- 版本: V001
-- 生成时间: 自动生成
-- 说明: 可重入脚本,支持重复执行和增量更新
-- =====================================================
-- =====================================================
-- 1. API 请求记录表
-- =====================================================
CREATE TABLE IF NOT EXISTS api_requests (
id INTEGER PRIMARY KEY AUTOINCREMENT,
request_id VARCHAR(36),
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
method VARCHAR(10) NOT NULL,
path VARCHAR(512) NOT NULL,
query_params TEXT,
status_code INTEGER NOT NULL,
response_time REAL NOT NULL,
client_ip VARCHAR(64),
user_agent TEXT,
payload_size INTEGER,
response_size INTEGER,
request_body TEXT,
response_body TEXT,
is_slow INTEGER DEFAULT 0,
slow_threshold REAL,
is_error INTEGER DEFAULT 0,
error_message TEXT,
is_internal INTEGER DEFAULT 0
);
-- 索引(幂等创建)
CREATE INDEX IF NOT EXISTS idx_api_requests_timestamp ON api_requests(timestamp);
CREATE INDEX IF NOT EXISTS idx_api_requests_path ON api_requests(path);
CREATE INDEX IF NOT EXISTS idx_api_requests_status_code ON api_requests(status_code);
CREATE INDEX IF NOT EXISTS idx_api_requests_response_time ON api_requests(response_time);
CREATE INDEX IF NOT EXISTS idx_api_requests_is_slow ON api_requests(is_slow);
CREATE INDEX IF NOT EXISTS idx_api_requests_is_error ON api_requests(is_error);
CREATE INDEX IF NOT EXISTS idx_api_requests_is_internal ON api_requests(is_internal);
CREATE INDEX IF NOT EXISTS idx_api_requests_request_id ON api_requests(request_id);
CREATE INDEX IF NOT EXISTS idx_api_requests_client_ip ON api_requests(client_ip);
CREATE INDEX IF NOT EXISTS idx_api_requests_method ON api_requests(method);
CREATE INDEX IF NOT EXISTS idx_api_requests_timestamp_status ON api_requests(timestamp, status_code);
-- =====================================================
-- 2. 对外 HTTP 请求记录表
-- =====================================================
CREATE TABLE IF NOT EXISTS outbound_api_requests (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
method VARCHAR(10) NOT NULL,
url TEXT NOT NULL,
status_code INTEGER NOT NULL,
duration REAL NOT NULL,
request_headers TEXT,
request_body TEXT,
response_headers TEXT,
response_body TEXT,
error_message TEXT,
module VARCHAR(255),
is_error INTEGER DEFAULT 0,
is_slow INTEGER DEFAULT 0,
is_internal INTEGER DEFAULT 0
);
-- 索引(幂等创建)
CREATE INDEX IF NOT EXISTS idx_outbound_timestamp ON outbound_api_requests(timestamp);
CREATE INDEX IF NOT EXISTS idx_outbound_module ON outbound_api_requests(module);
CREATE INDEX IF NOT EXISTS idx_outbound_status_code ON outbound_api_requests(status_code);
CREATE INDEX IF NOT EXISTS idx_outbound_is_error ON outbound_api_requests(is_error);
CREATE INDEX IF NOT EXISTS idx_outbound_is_slow ON outbound_api_requests(is_slow);
CREATE INDEX IF NOT EXISTS idx_outbound_is_internal ON outbound_api_requests(is_internal);
CREATE INDEX IF NOT EXISTS idx_outbound_method ON outbound_api_requests(method);
CREATE INDEX IF NOT EXISTS idx_outbound_timestamp_status ON outbound_api_requests(timestamp, status_code);
-- =====================================================
-- 3. 系统日志表
-- =====================================================
CREATE TABLE IF NOT EXISTS system_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
level VARCHAR(10) NOT NULL,
module VARCHAR(255) NOT NULL,
function VARCHAR(255) NOT NULL,
line_number INTEGER NOT NULL,
message TEXT NOT NULL,
details TEXT,
stack_trace TEXT,
process_id INTEGER,
thread_id INTEGER,
thread_name VARCHAR(255)
);
-- 索引(幂等创建)
CREATE INDEX IF NOT EXISTS idx_logs_timestamp ON system_logs(timestamp);
CREATE INDEX IF NOT EXISTS idx_logs_level ON system_logs(level);
CREATE INDEX IF NOT EXISTS idx_logs_module ON system_logs(module);
CREATE INDEX IF NOT EXISTS idx_logs_function ON system_logs(function);
CREATE INDEX IF NOT EXISTS idx_logs_timestamp_level ON system_logs(timestamp, level);
-- =====================================================
-- 4. Binlog 位置记录表
-- =====================================================
CREATE TABLE IF NOT EXISTS binlog_positions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
server_id VARCHAR(255) NOT NULL,
log_file VARCHAR(255) NOT NULL,
log_pos INTEGER NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 索引(幂等创建)
CREATE INDEX IF NOT EXISTS idx_binlog_server_id ON binlog_positions(server_id);
-- =====================================================
-- 5. 已处理事件记录表(用于去重)
-- =====================================================
CREATE TABLE IF NOT EXISTS processed_events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
event_id VARCHAR(512) UNIQUE NOT NULL,
log_file VARCHAR(255) NOT NULL,
log_pos INTEGER NOT NULL,
event_type VARCHAR(100) NOT NULL,
table_name VARCHAR(255) NOT NULL,
database_name VARCHAR(255) NOT NULL,
processed_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 索引(幂等创建)
CREATE INDEX IF NOT EXISTS idx_events_event_id ON processed_events(event_id);
CREATE INDEX IF NOT EXISTS idx_events_log_file_pos ON processed_events(log_file, log_pos);
CREATE INDEX IF NOT EXISTS idx_events_processed_at ON processed_events(processed_at);
-- =====================================================
-- 6. 失败操作记录表
-- =====================================================
CREATE TABLE IF NOT EXISTS failed_operations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
operation_id VARCHAR(64) UNIQUE NOT NULL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
db_name VARCHAR(100) NOT NULL,
function_name VARCHAR(255) NOT NULL,
args_json TEXT NOT NULL,
kwargs_json TEXT NOT NULL,
error_message TEXT NOT NULL,
error_type VARCHAR(255) NOT NULL,
status VARCHAR(50) DEFAULT 'pending',
retry_count INTEGER DEFAULT 0,
max_retries INTEGER DEFAULT 10,
last_retry_time DATETIME,
next_retry_time DATETIME,
event_type VARCHAR(100),
event_data TEXT,
metadata TEXT
);
-- 索引(幂等创建)
CREATE INDEX IF NOT EXISTS idx_failed_timestamp ON failed_operations(timestamp);
CREATE INDEX IF NOT EXISTS idx_failed_db_name_status ON failed_operations(db_name, status);
CREATE INDEX IF NOT EXISTS idx_failed_next_retry_status ON failed_operations(next_retry_time, status);
CREATE INDEX IF NOT EXISTS idx_failed_event_type ON failed_operations(event_type);
-- =====================================================
-- 7. 版本管理表
-- =====================================================
CREATE TABLE IF NOT EXISTS schema_version (
id INTEGER PRIMARY KEY AUTOINCREMENT,
version VARCHAR(16) UNIQUE NOT NULL,
applied_at DATETIME DEFAULT CURRENT_TIMESTAMP,
description TEXT,
sql_scripts TEXT,
status VARCHAR(16) DEFAULT 'applied'
);
-- =====================================================
-- 记录版本(幂等)
-- =====================================================
INSERT OR IGNORE INTO schema_version (version, description, sql_scripts)
VALUES (
'V001',
'初始化监控模块表结构:api_requests, outbound_api_requests, system_logs, binlog_positions, processed_events, failed_operations',
'monitor_tables.sql V001'
);
-- =====================================================
-- 完成提示
-- =====================================================
-- 执行完成后,可通过以下命令验证:
-- SELECT name FROM sqlite_master WHERE type='table';
-- SELECT * FROM schema_version ORDER BY applied_at DESC;
+239
View File
@@ -0,0 +1,239 @@
#!/bin/bash
# ==============================================
# 监控模块数据库一键建表脚本
# 使用方法: ./setup_monitor_tables.sh [选项]
# 功能: 向MyAPS API容器的SQLite数据库执行建表脚本
# ==============================================
set -e
# 配置
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
SQL_FILE="${SCRIPT_DIR}/monitor_tables.sql"
ENV_FILE="${SCRIPT_DIR}/../../../.env"
CONTAINER_NAME="myaps_api"
STORAGE_DIR="/app/storage"
# 默认数据库名
SQLITE_FILE="local_data"
# 从.env文件读取配置
read_env_config() {
if [ -f "$ENV_FILE" ]; then
while IFS='=' read -r key value; do
case "$key" in
SQLITE_FILE)
if [ -n "$value" ]; then
SQLITE_FILE="$value"
fi
;;
esac
done < "$ENV_FILE"
fi
# 移除.sqlite3后缀
SQLITE_FILE="${SQLITE_FILE%.sqlite3}"
}
# 显示帮助
show_help() {
echo "监控模块数据库一键建表脚本"
echo ""
echo "用法: $0 [选项]"
echo ""
echo "选项:"
echo " --help, -h 显示此帮助信息"
echo " --db, -d 指定SQLite数据库文件名(不含.sqlite3后缀)"
echo " --container, -c 指定MyAPS API容器名称 (默认: $CONTAINER_NAME)"
echo " --dry-run, -n 仅显示将要执行的操作,不实际执行"
echo " --local, -l 在本地直接执行(不通过容器)"
echo ""
echo "示例:"
echo " # 使用默认配置"
echo " ./setup_monitor_tables.sh"
echo ""
echo " # 指定数据库文件名"
echo " ./setup_monitor_tables.sh -d my_data"
echo ""
echo " # 指定容器名称"
echo " ./setup_monitor_tables.sh -c my_api_container"
echo ""
echo " # 本地执行(开发环境)"
echo " ./setup_monitor_tables.sh -l"
}
# 解析参数
DRY_RUN=false
LOCAL_MODE=false
while [[ $# -gt 0 ]]; do
case "$1" in
--help|-h)
show_help
exit 0
;;
--db|-d)
SQLITE_FILE="$2"
shift
;;
--container|-c)
CONTAINER_NAME="$2"
shift
;;
--dry-run|-n)
DRY_RUN=true
;;
--local|-l)
LOCAL_MODE=true
;;
*)
echo "未知选项: $1"
show_help
exit 1
;;
esac
shift
done
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# 读取环境变量
read_env_config
echo -e "${BLUE}==============================================${NC}"
echo -e "${BLUE} 监控模块数据库一键建表脚本${NC}"
echo -e "${BLUE}==============================================${NC}"
# 1. 检查SQL文件是否存在
echo -e "\n${YELLOW}🔍 检查SQL文件...${NC}"
if [ ! -f "$SQL_FILE" ]; then
echo -e "${RED}❌ 错误: SQL文件不存在 - $SQL_FILE${NC}"
exit 1
fi
echo -e "${GREEN}✅ SQL文件存在: $SQL_FILE${NC}"
if [ "$LOCAL_MODE" = true ]; then
# 本地模式执行
echo -e "\n${YELLOW}⚙️ 本地模式执行...${NC}"
DB_PATH="${SCRIPT_DIR}/../../../storage/${SQLITE_FILE}.sqlite3"
echo -e "${BLUE} 数据库文件: ${DB_PATH}${NC}"
if $DRY_RUN; then
echo -e "${YELLOW} [模拟] sqlite3 ${DB_PATH} < ${SQL_FILE}${NC}"
else
# 确保storage目录存在
mkdir -p "$(dirname "$DB_PATH")"
# 执行SQL脚本
echo -e "${YELLOW} 执行SQL脚本...${NC}"
sqlite3 "$DB_PATH" < "$SQL_FILE"
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
echo -e "${GREEN}✅ 建表成功${NC}"
else
echo -e "${RED}❌ 建表失败 (退出码: $EXIT_CODE)${NC}"
exit 1
fi
fi
else
# 容器模式执行
# 2. 检查容器是否存在
echo -e "\n${YELLOW}🔍 检查MyAPS API容器状态...${NC}"
if ! docker inspect "$CONTAINER_NAME" &>/dev/null; then
echo -e "${RED}❌ 错误: 容器 $CONTAINER_NAME 不存在${NC}"
echo -e "${RED} 请先启动MyAPS API容器${NC}"
exit 1
fi
# 检查容器是否运行
CONTAINER_STATUS=$(docker inspect -f '{{.State.Status}}' "$CONTAINER_NAME")
if [ "$CONTAINER_STATUS" != "running" ]; then
if $DRY_RUN; then
echo -e "${YELLOW}⚠️ [模拟] 容器未运行,将启动...${NC}"
else
echo -e "${YELLOW}⚠️ 容器未运行,正在启动...${NC}"
docker start "$CONTAINER_NAME"
sleep 5
fi
fi
echo -e "${GREEN}✅ 容器 $CONTAINER_NAME 运行正常${NC}"
# 3. 执行建表
echo -e "\n${YELLOW}⚙️ 执行建表脚本...${NC}"
echo -e "${BLUE} 数据库文件: ${STORAGE_DIR}/${SQLITE_FILE}.sqlite3${NC}"
if $DRY_RUN; then
echo -e "${YELLOW} [模拟] docker cp ${SQL_FILE} ${CONTAINER_NAME}:/tmp/monitor_tables.sql${NC}"
echo -e "${YELLOW} [模拟] docker exec ${CONTAINER_NAME} sqlite3 ${STORAGE_DIR}/${SQLITE_FILE}.sqlite3 < /tmp/monitor_tables.sql${NC}"
else
# 复制SQL文件到容器
echo -e "${YELLOW} 复制SQL文件到容器...${NC}"
docker cp "$SQL_FILE" "$CONTAINER_NAME":/tmp/monitor_tables.sql
# 执行建表脚本
echo -e "${YELLOW} 执行SQL脚本...${NC}"
docker exec "$CONTAINER_NAME" sqlite3 "${STORAGE_DIR}/${SQLITE_FILE}.sqlite3" < /tmp/monitor_tables.sql
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
echo -e "${GREEN}✅ 建表成功${NC}"
else
echo -e "${RED}❌ 建表失败 (退出码: $EXIT_CODE)${NC}"
exit 1
fi
# 清理容器内的临时文件
docker exec "$CONTAINER_NAME" rm -f /tmp/monitor_tables.sql
fi
fi
# 4. 验证结果
echo -e "\n${YELLOW}📊 验证建表结果...${NC}"
if $DRY_RUN; then
echo -e "${YELLOW} [模拟] 验证表是否创建成功${NC}"
else
if [ "$LOCAL_MODE" = true ]; then
TABLES=$(sqlite3 "$DB_PATH" "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;")
else
TABLES=$(docker exec "$CONTAINER_NAME" sqlite3 "${STORAGE_DIR}/${SQLITE_FILE}.sqlite3" "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;")
fi
echo -e "${GREEN}✅ 创建的表:${NC}"
echo "$TABLES" | while read -r table; do
if [ -n "$table" ]; then
echo -e " - $table"
fi
done
# 检查版本记录
if [ "$LOCAL_MODE" = true ]; then
VERSION=$(sqlite3 "$DB_PATH" "SELECT version FROM schema_version ORDER BY applied_at DESC LIMIT 1;")
else
VERSION=$(docker exec "$CONTAINER_NAME" sqlite3 "${STORAGE_DIR}/${SQLITE_FILE}.sqlite3" "SELECT version FROM schema_version ORDER BY applied_at DESC LIMIT 1;")
fi
if [ -n "$VERSION" ]; then
echo -e "\n${GREEN}✅ 版本记录: $VERSION${NC}"
fi
fi
echo -e "\n${BLUE}==============================================${NC}"
if $DRY_RUN; then
echo -e "${YELLOW}⚠️ 模拟完成,未执行实际操作${NC}"
else
echo -e "${GREEN}🎉 建表完成!${NC}"
fi
echo -e "${BLUE}==============================================${NC}"
echo -e "\n${YELLOW}💡 验证命令:${NC}"
if [ "$LOCAL_MODE" = true ]; then
echo -e "${YELLOW} sqlite3 ${DB_PATH}${NC}"
else
echo -e "${YELLOW} docker exec -it ${CONTAINER_NAME} sqlite3 ${STORAGE_DIR}/${SQLITE_FILE}.sqlite3${NC}"
fi
echo -e "${YELLOW} SELECT * FROM schema_version;${NC}"
+199
View File
@@ -0,0 +1,199 @@
#!/bin/bash
# ==============================================
# PostgreSQL 缓冲表一键建表脚本
# 使用方法: ./setup_staging_tables.sh [选项]
# 功能: 向PostgreSQL容器执行建表SQL脚本
# ==============================================
set -e
# 配置
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
SQL_FILE="${SCRIPT_DIR}/staging_tables.sql"
ENV_FILE="${SCRIPT_DIR}/../../.env"
CONTAINER_NAME="myaps_postgres"
# 默认数据库名
DB_NAME=""
# 从.env文件读取数据库配置
read_env_config() {
if [ -f "$ENV_FILE" ]; then
while IFS='=' read -r key value; do
case "$key" in
THIS_DB_HOST) DB_HOST="$value" ;;
THIS_DB_PORT) DB_PORT="$value" ;;
THIS_DB_USER) DB_USER="$value" ;;
THIS_DB_PASSWORD) DB_PASSWORD="$value" ;;
THIS_DB_NAME) DB_NAME="$value" ;;
esac
done < "$ENV_FILE"
fi
# 设置默认值
DB_HOST=${DB_HOST:-localhost}
DB_PORT=${DB_PORT:-5432}
DB_USER=${DB_USER:-postgres}
}
# 显示帮助
show_help() {
echo "PostgreSQL 缓冲表一键建表脚本"
echo ""
echo "用法: $0 [选项]"
echo ""
echo "选项:"
echo " --help, -h 显示此帮助信息"
echo " --db, -d 指定数据库名称(必填)"
echo " --container, -c 指定PostgreSQL容器名称 (默认: $CONTAINER_NAME)"
echo " --dry-run, -n 仅显示将要执行的操作,不实际执行"
echo ""
echo "示例:"
echo " # 使用默认配置"
echo " ./setup_staging_tables.sh -d myaps_db"
echo ""
echo " # 指定容器名称"
echo " ./setup_staging_tables.sh -d myaps_db -c my_postgres"
echo ""
echo " # 模拟执行"
echo " ./setup_staging_tables.sh -d myaps_db -n"
}
# 解析参数
DRY_RUN=false
while [[ $# -gt 0 ]]; do
case "$1" in
--help|-h)
show_help
exit 0
;;
--db|-d)
DB_NAME="$2"
shift
;;
--container|-c)
CONTAINER_NAME="$2"
shift
;;
--dry-run|-n)
DRY_RUN=true
;;
*)
echo "未知选项: $1"
show_help
exit 1
;;
esac
shift
done
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# 检查数据库名称是否指定
if [ -z "$DB_NAME" ]; then
echo -e "${RED}❌ 错误: 必须使用 -d 指定数据库名称${NC}"
show_help
exit 1
fi
# 读取环境变量
read_env_config
echo -e "${BLUE}==============================================${NC}"
echo -e "${BLUE} PostgreSQL 缓冲表一键建表脚本${NC}"
echo -e "${BLUE}==============================================${NC}"
# 1. 检查SQL文件是否存在
echo -e "\n${YELLOW}🔍 检查SQL文件...${NC}"
if [ ! -f "$SQL_FILE" ]; then
echo -e "${RED}❌ 错误: SQL文件不存在 - $SQL_FILE${NC}"
exit 1
fi
echo -e "${GREEN}✅ SQL文件存在: $SQL_FILE${NC}"
# 2. 检查容器是否存在
echo -e "\n${YELLOW}🔍 检查PostgreSQL容器状态...${NC}"
if ! docker inspect "$CONTAINER_NAME" &>/dev/null; then
echo -e "${RED}❌ 错误: 容器 $CONTAINER_NAME 不存在${NC}"
echo -e "${RED} 请先启动PostgreSQL容器${NC}"
exit 1
fi
# 检查容器是否运行
CONTAINER_STATUS=$(docker inspect -f '{{.State.Status}}' "$CONTAINER_NAME")
if [ "$CONTAINER_STATUS" != "running" ]; then
if $DRY_RUN; then
echo -e "${YELLOW}⚠️ [模拟] 容器未运行,将启动...${NC}"
else
echo -e "${YELLOW}⚠️ 容器未运行,正在启动...${NC}"
docker start "$CONTAINER_NAME"
sleep 5
fi
fi
echo -e "${GREEN}✅ 容器 $CONTAINER_NAME 运行正常${NC}"
# 3. 执行建表
echo -e "\n${YELLOW}⚙️ 执行建表脚本...${NC}"
echo -e "${BLUE} 数据库: ${DB_NAME}${NC}"
echo -e "${BLUE} 用户: ${DB_USER}${NC}"
echo -e "${BLUE} 端口: ${DB_PORT}${NC}"
if $DRY_RUN; then
echo -e "${YELLOW} [模拟] docker exec -i $CONTAINER_NAME psql -U $DB_USER -d $DB_NAME -f - < $SQL_FILE${NC}"
else
# 检查数据库是否存在,不存在则创建
echo -e "${YELLOW} 检查数据库是否存在...${NC}"
if ! docker exec "$CONTAINER_NAME" psql -U "$DB_USER" -tAc "SELECT 1 FROM pg_database WHERE datname='$DB_NAME'" | grep -q 1; then
echo -e "${YELLOW} 数据库不存在,创建数据库...${NC}"
docker exec "$CONTAINER_NAME" createdb -U "$DB_USER" "$DB_NAME"
fi
# 执行建表脚本
echo -e "${YELLOW} 执行SQL脚本...${NC}"
cat "$SQL_FILE" | docker exec -i "$CONTAINER_NAME" psql -U "$DB_USER" -d "$DB_NAME"
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
echo -e "${GREEN}✅ 建表成功${NC}"
else
echo -e "${RED}❌ 建表失败 (退出码: $EXIT_CODE)${NC}"
exit 1
fi
fi
# 4. 验证结果
echo -e "\n${YELLOW}📊 验证建表结果...${NC}"
if $DRY_RUN; then
echo -e "${YELLOW} [模拟] 验证缓冲表是否创建成功${NC}"
else
TABLES=$(docker exec "$CONTAINER_NAME" psql -U "$DB_USER" -d "$DB_NAME" -tAc "SELECT tablename FROM pg_tables WHERE tablename LIKE '%staging' OR tablename IN ('t_validation_error', 't_transform_rule', 't_schema_version') ORDER BY tablename;")
echo -e "${GREEN}✅ 创建的表:${NC}"
echo "$TABLES" | while read -r table; do
if [ -n "$table" ]; then
echo -e " - $table"
fi
done
# 检查版本记录
VERSION=$(docker exec "$CONTAINER_NAME" psql -U "$DB_USER" -d "$DB_NAME" -tAc "SELECT version FROM t_schema_version ORDER BY applied_at DESC LIMIT 1;")
if [ -n "$VERSION" ]; then
echo -e "\n${GREEN}✅ 版本记录: $VERSION${NC}"
fi
fi
echo -e "\n${BLUE}==============================================${NC}"
if $DRY_RUN; then
echo -e "${YELLOW}⚠️ 模拟完成,未执行实际操作${NC}"
else
echo -e "${GREEN}🎉 建表完成!${NC}"
fi
echo -e "${BLUE}==============================================${NC}"
echo -e "\n${YELLOW}💡 验证命令:${NC}"
echo -e "${YELLOW} docker exec -it $CONTAINER_NAME psql -U $DB_USER -d $DB_NAME${NC}"
echo -e "${YELLOW} SELECT * FROM t_schema_version;${NC}"
@@ -77,7 +77,8 @@ CREATE TABLE IF NOT EXISTS t_material_staging (
DO $$
BEGIN
-- V001 新增字段示例(演示增量模式)
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 't_material_staging' AND column_name = 'free4') THEN
-- 使用 pg_attribute 检查,支持大小写敏感的标识符
IF NOT EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = 't_material_staging'::regclass AND LOWER(attname) = LOWER('Free4') AND NOT attisdropped) THEN
ALTER TABLE t_material_staging ADD COLUMN "Free4" VARCHAR(255) NULL;
END IF;
END $$;
@@ -347,6 +348,7 @@ CREATE TABLE IF NOT EXISTS t_mat_wc_mold_staging (
"BaseSec" INT NULL,
"FixSec" INT NULL,
"Priority" INT NULL,
"Worker" FLOAT NULL,
"Memo" VARCHAR(255) NULL
);