fix: create schema before postgres import
Some checks failed
Deploy to Production / Build and Test (push) Successful in 10m8s
Deploy to Production / Deploy to Server (push) Failing after 6m17s

This commit is contained in:
你的用户名
2025-11-06 23:16:58 +08:00
parent ce5cb92cb6
commit 812313c37f
2 changed files with 212 additions and 51 deletions

View File

@@ -190,56 +190,52 @@ jobs:
- name: Health Check - name: Health Check
if: success() if: success()
run: | uses: appleboy/ssh-action@v1.0.0
echo "🔍 执行健康检查..." with:
host: ${{ secrets.SERVER_HOST || '172.16.74.149' }}
username: ${{ secrets.SERVER_USER || 'atai' }}
password: ${{ secrets.SERVER_PASSWORD || 'wengewudi666808' }}
port: ${{ secrets.SERVER_PORT || '22' }}
command_timeout: 10m
script: |
set -e
echo "🔍 执行健康检查..."
sleep 20
# 等待服务完全启动(延长等待时间) for i in {1..10}; do
sleep 20
# 健康检查(增加重试次数和诊断信息)
for i in {1..10}; do
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "尝试 $i/10: 检查服务 ${{ env.HEALTH_CHECK_URL }}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# 详细的curl诊断
HTTP_CODE=$(curl -v -s -o /dev/null -w "%{http_code}" --connect-timeout 5 --max-time 10 ${{ env.HEALTH_CHECK_URL }} 2>&1)
echo "响应: $HTTP_CODE"
if echo "$HTTP_CODE" | grep -q "200\|301\|302"; then
echo "✅ 服务健康检查通过HTTP状态码正常"
echo "" echo ""
echo "🎉 部署成功!服务已正常运行" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
exit 0 echo "尝试 ${i}/10: 检查服务 ${{ env.HEALTH_CHECK_URL }}"
fi echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# 如果失败,显示更多诊断信息 HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 --max-time 10 ${{ env.HEALTH_CHECK_URL }} || true)
if [ $i -eq 5 ]; then echo "响应: ${HTTP_CODE}"
echo ""
echo "⚠️ 第5次尝试失败执行深度诊断..." if printf "%s" "$HTTP_CODE" | grep -qE "200|301|302"; then
echo "" echo "✅ 服务健康检查通过HTTP状态码正常"
echo "🔍 检查容器运行状态:" echo ""
ssh -o StrictHostKeyChecking=no ${{ secrets.SERVER_USER || 'atai' }}@${{ secrets.SERVER_HOST || '172.16.74.149' }} "cd /home/atai/kt-financial-system && sudo docker-compose ps" || true echo "🎉 部署成功!服务已正常运行"
echo "" exit 0
echo "📝 最新容器日志:" fi
ssh -o StrictHostKeyChecking=no ${{ secrets.SERVER_USER || 'atai' }}@${{ secrets.SERVER_HOST || '172.16.74.149' }} "cd /home/atai/kt-financial-system && sudo docker-compose logs --tail=50" || true
fi if [ "$i" -eq 5 ]; then
echo ""
echo "⚠️ 第5次尝试失败执行深度诊断..."
echo ""
echo "🔍 检查容器运行状态:"
cd /home/atai/kt-financial-system
sudo docker-compose ps || true
echo ""
echo "📝 最新容器日志:"
sudo docker-compose logs --tail=50 || true
fi
if [ $i -lt 10 ]; then
echo "⏳ 等待6秒后重试..."
sleep 6 sleep 6
fi done
done
echo "" echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "❌ 服务健康检查失败:无法在多次重试后获得 200/301/302 响应"
echo "❌ 健康检查失败10次尝试均未成功" exit 1
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "🔍 最终诊断信息:"
ssh -o StrictHostKeyChecking=no ${{ secrets.SERVER_USER || 'atai' }}@${{ secrets.SERVER_HOST || '172.16.74.149' }} "cd /home/atai/kt-financial-system && sudo docker-compose ps && echo '---' && sudo docker-compose logs --tail=100" || true
exit 1
- name: Send notification on success - name: Send notification on success
if: success() if: success()

View File

@@ -231,6 +231,7 @@ function normalizeDate(rawValue, monthTracker, baseYear) {
} }
async function resetFinanceTables(client) { async function resetFinanceTables(client) {
await ensureSchema(client);
await client.query(` await client.query(`
TRUNCATE TABLE TRUNCATE TABLE
finance_transactions, finance_transactions,
@@ -258,6 +259,165 @@ async function ensureCurrency(client, cache, code, name = code, symbol = code) {
cache.currencies.add(code); cache.currencies.add(code);
} }
async function ensureSchema(client) {
await client.query(`
CREATE TABLE IF NOT EXISTS finance_currencies (
code TEXT PRIMARY KEY,
name TEXT NOT NULL,
symbol TEXT NOT NULL,
is_base BOOLEAN NOT NULL DEFAULT FALSE,
is_active BOOLEAN NOT NULL DEFAULT TRUE
);
`);
await client.query(`
CREATE TABLE IF NOT EXISTS finance_exchange_rates (
id SERIAL PRIMARY KEY,
from_currency TEXT NOT NULL REFERENCES finance_currencies(code),
to_currency TEXT NOT NULL REFERENCES finance_currencies(code),
rate NUMERIC NOT NULL,
date DATE NOT NULL,
source TEXT DEFAULT 'manual'
);
`);
await client.query(`
CREATE TABLE IF NOT EXISTS finance_accounts (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
currency TEXT NOT NULL REFERENCES finance_currencies(code),
type TEXT DEFAULT 'cash',
icon TEXT,
color TEXT,
user_id INTEGER DEFAULT 1,
is_active BOOLEAN DEFAULT TRUE
);
`);
await client.query(`
CREATE TABLE IF NOT EXISTS finance_categories (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
type TEXT NOT NULL,
icon TEXT,
color TEXT,
user_id INTEGER,
is_active BOOLEAN DEFAULT TRUE
);
`);
await client.query(`
CREATE TABLE IF NOT EXISTS finance_transactions (
id SERIAL PRIMARY KEY,
type TEXT NOT NULL,
amount NUMERIC NOT NULL,
currency TEXT NOT NULL REFERENCES finance_currencies(code),
exchange_rate_to_base NUMERIC NOT NULL,
amount_in_base NUMERIC NOT NULL,
category_id INTEGER REFERENCES finance_categories(id),
account_id INTEGER REFERENCES finance_accounts(id),
transaction_date DATE NOT NULL,
description TEXT,
project TEXT,
memo TEXT,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
status TEXT NOT NULL DEFAULT 'approved',
status_updated_at TIMESTAMP WITH TIME ZONE,
reimbursement_batch TEXT,
review_notes TEXT,
submitted_by TEXT,
approved_by TEXT,
approved_at TIMESTAMP WITH TIME ZONE,
is_deleted BOOLEAN NOT NULL DEFAULT FALSE,
deleted_at TIMESTAMP WITH TIME ZONE
);
`);
await client.query(`
CREATE TABLE IF NOT EXISTS finance_media_messages (
id SERIAL PRIMARY KEY,
chat_id BIGINT NOT NULL,
message_id BIGINT NOT NULL,
user_id INTEGER NOT NULL,
username TEXT,
display_name TEXT,
file_type TEXT NOT NULL,
file_id TEXT NOT NULL,
file_unique_id TEXT,
caption TEXT,
file_name TEXT,
file_path TEXT NOT NULL,
file_size INTEGER,
mime_type TEXT,
duration INTEGER,
width INTEGER,
height INTEGER,
forwarded_to INTEGER,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
UNIQUE(chat_id, message_id)
);
`);
await client.query(`
CREATE TABLE IF NOT EXISTS telegram_notification_configs (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
bot_token TEXT NOT NULL,
chat_id TEXT NOT NULL,
notification_types TEXT NOT NULL,
is_enabled BOOLEAN NOT NULL DEFAULT TRUE,
priority TEXT DEFAULT 'normal',
rate_limit_seconds INTEGER DEFAULT 0,
batch_enabled BOOLEAN DEFAULT FALSE,
batch_interval_minutes INTEGER DEFAULT 60,
retry_enabled BOOLEAN DEFAULT TRUE,
retry_max_attempts INTEGER DEFAULT 3,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
`);
await client.query(`
CREATE TABLE IF NOT EXISTS telegram_notification_history (
id SERIAL PRIMARY KEY,
config_id INTEGER NOT NULL REFERENCES telegram_notification_configs(id),
notification_type TEXT NOT NULL,
content_hash TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
retry_count INTEGER DEFAULT 0,
sent_at TIMESTAMP WITH TIME ZONE,
error_message TEXT,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
`);
await client.query(`
CREATE INDEX IF NOT EXISTS idx_finance_media_messages_created_at
ON finance_media_messages (created_at DESC);
`);
await client.query(`
CREATE INDEX IF NOT EXISTS idx_finance_media_messages_user_id
ON finance_media_messages (user_id);
`);
await client.query(`
CREATE INDEX IF NOT EXISTS idx_telegram_notification_configs_enabled
ON telegram_notification_configs (is_enabled);
`);
await client.query(`
CREATE INDEX IF NOT EXISTS idx_telegram_notification_history_config
ON telegram_notification_history (config_id, created_at DESC);
`);
await client.query(`
CREATE INDEX IF NOT EXISTS idx_telegram_notification_history_hash
ON telegram_notification_history (content_hash, created_at DESC);
`);
await client.query(`
CREATE INDEX IF NOT EXISTS idx_telegram_notification_history_status
ON telegram_notification_history (status, retry_count);
`);
}
async function ensureExchangeRate( async function ensureExchangeRate(
client, client,
cache, cache,
@@ -463,6 +623,17 @@ async function importNewFormat(client, header, rows, cache) {
throw new Error('CSV 表头缺少必需字段,无法导入新版格式数据'); throw new Error('CSV 表头缺少必需字段,无法导入新版格式数据');
} }
await ensureCurrency(client, cache, 'CNY', '人民币', '¥');
await ensureExchangeRate(
client,
cache,
'CNY',
'CNY',
1,
'1970-01-01',
'system',
);
const transactions = []; const transactions = [];
for (const columns of rows) { for (const columns of rows) {
@@ -487,13 +658,7 @@ async function importNewFormat(client, header, rows, cache) {
const accountIcon = resolveCurrencyIcon(currency); const accountIcon = resolveCurrencyIcon(currency);
const accountColor = resolveCurrencyColor(currency); const accountColor = resolveCurrencyColor(currency);
await ensureCurrency( await ensureCurrency(client, cache, currency, currencyName, currencySymbol);
client,
cache,
currency,
currencyName,
currencySymbol,
);
await ensureExchangeRate(client, cache, currency, 'CNY', 1, date); await ensureExchangeRate(client, cache, currency, 'CNY', 1, date);
const accountName = columns[indexMap.account]?.trim() || '默认账户'; const accountName = columns[indexMap.account]?.trim() || '默认账户';