feat: 后台打包测试部署
All checks were successful
Backend Deploy (Go + Docker) / deploy (push) Successful in 1m29s
All checks were successful
Backend Deploy (Go + Docker) / deploy (push) Successful in 1m29s
This commit is contained in:
@@ -33,7 +33,6 @@ jobs:
|
|||||||
cp server/Dockerfile deploy/
|
cp server/Dockerfile deploy/
|
||||||
cp server/docker-compose.yml deploy/
|
cp server/docker-compose.yml deploy/
|
||||||
cp server/.env.example deploy/
|
cp server/.env.example deploy/
|
||||||
cp -r server/migrations deploy/
|
|
||||||
|
|
||||||
- name: 部署文件到服务器
|
- name: 部署文件到服务器
|
||||||
uses: up9cloud/action-rsync@master
|
uses: up9cloud/action-rsync@master
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ InsightReply 采用现代化解耦的三端架构:
|
|||||||
## 🛠️ 本地开发指南 (Local Development)
|
## 🛠️ 本地开发指南 (Local Development)
|
||||||
|
|
||||||
### 1. 启动 PostgreSQL 数据库
|
### 1. 启动 PostgreSQL 数据库
|
||||||
|
> **注意**:本项目已禁用 Go 服务自动迁移。请使用本项目支持的 MCP 服务 (`InsightReply_PostgreSQL`) 或手动执行 `docs/schema.sql` 来同步数据库结构。
|
||||||
|
|
||||||
### 2. 配置与启动后端 (Go Server)
|
### 2. 配置与启动后端 (Go Server)
|
||||||
```bash
|
```bash
|
||||||
@@ -82,9 +83,10 @@ VITE_API_BASE_URL=https://insight.buildapp.eu.org/api/v1
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /var/admin/InsightReply/server
|
cd /var/admin/InsightReply/server
|
||||||
docker-compose up -d --build
|
# 生产环境使用 docker compose (V2)
|
||||||
|
docker compose up -d --build --remove-orphans
|
||||||
```
|
```
|
||||||
> 此时应用的全局运行日志将自动映射并写入由于宿主机的 `/root/logs/InsightReply.log` 内以供探查。
|
> 此时应用将以 `network_mode: host` 模式运行,全局运行日志将自动映射并写入宿主机的 `/app/logs/InsightReply.log` 内以供探查。
|
||||||
|
|
||||||
### 3. Caddyfile 反向代理与 SSL 自动签发
|
### 3. Caddyfile 反向代理与 SSL 自动签发
|
||||||
在宿主机中编辑 `/etc/caddy/Caddyfile`,配置以下动静分离策略:
|
在宿主机中编辑 `/etc/caddy/Caddyfile`,配置以下动静分离策略:
|
||||||
|
|||||||
@@ -55,9 +55,9 @@ CREATE TABLE IF NOT EXISTS tweets (
|
|||||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_tweets_x_tweet_id ON tweets(x_tweet_id);
|
CREATE INDEX IF NOT EXISTS idx_tweets_x_tweet_id ON tweets(x_tweet_id);
|
||||||
CREATE INDEX idx_tweets_heat_score ON tweets(heat_score DESC);
|
CREATE INDEX IF NOT EXISTS idx_tweets_heat_score ON tweets(heat_score DESC);
|
||||||
CREATE INDEX idx_tweets_crawl_queue ON tweets(crawl_queue, last_crawled_at);
|
CREATE INDEX IF NOT EXISTS idx_tweets_crawl_queue ON tweets(crawl_queue, last_crawled_at);
|
||||||
|
|
||||||
-- generated_replies 表:生成的 AI 评论记录
|
-- generated_replies 表:生成的 AI 评论记录
|
||||||
CREATE TABLE IF NOT EXISTS generated_replies (
|
CREATE TABLE IF NOT EXISTS generated_replies (
|
||||||
@@ -71,8 +71,8 @@ CREATE TABLE IF NOT EXISTS generated_replies (
|
|||||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_generated_replies_user_id ON generated_replies(user_id);
|
CREATE INDEX IF NOT EXISTS idx_generated_replies_user_id ON generated_replies(user_id);
|
||||||
CREATE INDEX idx_generated_replies_tweet_id ON generated_replies(tweet_id);
|
CREATE INDEX IF NOT EXISTS idx_generated_replies_tweet_id ON generated_replies(tweet_id);
|
||||||
|
|
||||||
-- reply_performance 表:针对已发布评论的效果数据回拨
|
-- reply_performance 表:针对已发布评论的效果数据回拨
|
||||||
CREATE TABLE IF NOT EXISTS reply_performance (
|
CREATE TABLE IF NOT EXISTS reply_performance (
|
||||||
@@ -85,8 +85,8 @@ CREATE TABLE IF NOT EXISTS reply_performance (
|
|||||||
check_time TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
check_time TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_reply_performance_reply_id ON reply_performance(reply_id);
|
CREATE INDEX IF NOT EXISTS idx_reply_performance_reply_id ON reply_performance(reply_id);
|
||||||
CREATE INDEX idx_reply_performance_user_id ON reply_performance(user_id);
|
CREATE INDEX IF NOT EXISTS idx_reply_performance_user_id ON reply_performance(user_id);
|
||||||
|
|
||||||
-- ====================================================
|
-- ====================================================
|
||||||
-- 新增表 (v1.1)
|
-- 新增表 (v1.1)
|
||||||
@@ -106,8 +106,8 @@ CREATE TABLE IF NOT EXISTS api_usage_logs (
|
|||||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_api_usage_logs_user_id ON api_usage_logs(user_id);
|
CREATE INDEX IF NOT EXISTS idx_api_usage_logs_user_id ON api_usage_logs(user_id);
|
||||||
CREATE INDEX idx_api_usage_logs_created_at ON api_usage_logs(created_at DESC);
|
CREATE INDEX IF NOT EXISTS idx_api_usage_logs_created_at ON api_usage_logs(created_at DESC);
|
||||||
|
|
||||||
-- subscriptions 表:用户订阅记录(支付历史)
|
-- subscriptions 表:用户订阅记录(支付历史)
|
||||||
CREATE TABLE IF NOT EXISTS subscriptions (
|
CREATE TABLE IF NOT EXISTS subscriptions (
|
||||||
@@ -122,8 +122,8 @@ CREATE TABLE IF NOT EXISTS subscriptions (
|
|||||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_subscriptions_user_id ON subscriptions(user_id);
|
CREATE INDEX IF NOT EXISTS idx_subscriptions_user_id ON subscriptions(user_id);
|
||||||
CREATE INDEX idx_subscriptions_status ON subscriptions(status);
|
CREATE INDEX IF NOT EXISTS idx_subscriptions_status ON subscriptions(status);
|
||||||
|
|
||||||
-- user_style_profiles 表:用户风格画像(用于个性化 Prompt)
|
-- user_style_profiles 表:用户风格画像(用于个性化 Prompt)
|
||||||
CREATE TABLE IF NOT EXISTS user_style_profiles (
|
CREATE TABLE IF NOT EXISTS user_style_profiles (
|
||||||
@@ -147,7 +147,7 @@ CREATE TABLE IF NOT EXISTS crawl_snapshots (
|
|||||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_crawl_snapshots_created_at ON crawl_snapshots(created_at DESC);
|
CREATE INDEX IF NOT EXISTS idx_crawl_snapshots_created_at ON crawl_snapshots(created_at DESC);
|
||||||
|
|
||||||
-- ====================================================
|
-- ====================================================
|
||||||
-- 新增表 (v1.2) — 用户可配置系统
|
-- 新增表 (v1.2) — 用户可配置系统
|
||||||
@@ -191,7 +191,7 @@ CREATE TABLE IF NOT EXISTS user_custom_strategies (
|
|||||||
UNIQUE (user_id, strategy_key)
|
UNIQUE (user_id, strategy_key)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_user_custom_strategies_user_id ON user_custom_strategies(user_id);
|
CREATE INDEX IF NOT EXISTS idx_user_custom_strategies_user_id ON user_custom_strategies(user_id);
|
||||||
|
|
||||||
-- competitor_monitors 表:竞品品牌监控(复用后端雷达,按品牌词自动抓取)
|
-- competitor_monitors 表:竞品品牌监控(复用后端雷达,按品牌词自动抓取)
|
||||||
CREATE TABLE IF NOT EXISTS competitor_monitors (
|
CREATE TABLE IF NOT EXISTS competitor_monitors (
|
||||||
@@ -204,7 +204,7 @@ CREATE TABLE IF NOT EXISTS competitor_monitors (
|
|||||||
UNIQUE (user_id, brand_name)
|
UNIQUE (user_id, brand_name)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_competitor_monitors_user_id ON competitor_monitors(user_id);
|
CREATE INDEX IF NOT EXISTS idx_competitor_monitors_user_id ON competitor_monitors(user_id);
|
||||||
|
|
||||||
-- ====================================================
|
-- ====================================================
|
||||||
-- 触发器:自动更新 updated_at
|
-- 触发器:自动更新 updated_at
|
||||||
@@ -219,12 +219,20 @@ END;
|
|||||||
$$ language 'plpgsql';
|
$$ language 'plpgsql';
|
||||||
|
|
||||||
-- 为所有需要追踪更新时间的表添加触发器
|
-- 为所有需要追踪更新时间的表添加触发器
|
||||||
CREATE TRIGGER update_users_modtime
|
DO $$ BEGIN
|
||||||
BEFORE UPDATE ON users
|
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'update_users_modtime') THEN
|
||||||
FOR EACH ROW
|
CREATE TRIGGER update_users_modtime
|
||||||
EXECUTE FUNCTION update_modified_column();
|
BEFORE UPDATE ON users
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION update_modified_column();
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
CREATE TRIGGER update_user_style_profiles_modtime
|
DO $$ BEGIN
|
||||||
BEFORE UPDATE ON user_style_profiles
|
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'update_user_style_profiles_modtime') THEN
|
||||||
FOR EACH ROW
|
CREATE TRIGGER update_user_style_profiles_modtime
|
||||||
EXECUTE FUNCTION update_modified_column();
|
BEFORE UPDATE ON user_style_profiles
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION update_modified_column();
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|||||||
@@ -57,3 +57,7 @@ globs: *.go, *.sql
|
|||||||
* 对于本项目的初步开发,推荐使用如 **`gorm`** 或 **`sqlx`** 进行快速的数据交互操作。
|
* 对于本项目的初步开发,推荐使用如 **`gorm`** 或 **`sqlx`** 进行快速的数据交互操作。
|
||||||
* 所有表名、字段名在 Go 结构体 (`struct`) 的 tag 中必须显式定义为下划线 (snake_case)。
|
* 所有表名、字段名在 Go 结构体 (`struct`) 的 tag 中必须显式定义为下划线 (snake_case)。
|
||||||
* UUID 作为主键,禁止前端或外部服务自行生成传入,一律由 PostgreSQL `gen_random_uuid()` 或者服务端生成。
|
* UUID 作为主键,禁止前端或外部服务自行生成传入,一律由 PostgreSQL `gen_random_uuid()` 或者服务端生成。
|
||||||
|
* **数据库迁移 (Migration)**:
|
||||||
|
* **禁用自动迁移**: 后端程序不再自动执行 `Up()` 迁移,所有变更需手动通过 MCP 或 DBA 工具执行。
|
||||||
|
* **幂等性**: 所有 SQL 脚本(如 `CREATE INDEX`, `CREATE TABLE`)必须包含 `IF NOT EXISTS` 保护。
|
||||||
|
* **触发器**: 创建触发器时必须先检查是否存在,避免重复定义导致部署中断。
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ WORKDIR /app
|
|||||||
COPY server_bin .
|
COPY server_bin .
|
||||||
RUN chmod +x server_bin
|
RUN chmod +x server_bin
|
||||||
|
|
||||||
# 拷贝数据库迁移文件 (服务启动时自动执行)
|
# 数据库迁移现已通过 MCP 手动管理,不再打包进镜像
|
||||||
COPY migrations ./migrations
|
|
||||||
|
|
||||||
EXPOSE 8009
|
EXPOSE 8009
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,6 @@ import (
|
|||||||
"github.com/zs/InsightReply/internal/service"
|
"github.com/zs/InsightReply/internal/service"
|
||||||
"github.com/zs/InsightReply/internal/worker"
|
"github.com/zs/InsightReply/internal/worker"
|
||||||
|
|
||||||
"github.com/golang-migrate/migrate/v4"
|
|
||||||
_ "github.com/golang-migrate/migrate/v4/database/postgres"
|
|
||||||
_ "github.com/golang-migrate/migrate/v4/source/file"
|
|
||||||
"gorm.io/driver/postgres"
|
"gorm.io/driver/postgres"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@@ -58,19 +55,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
fmt.Println("Database connection established")
|
fmt.Println("Database connection established")
|
||||||
|
|
||||||
// 2.1 Run Database Migrations
|
|
||||||
log.Println("Running database migrations...")
|
|
||||||
m, err := migrate.New("file://migrations", dsn)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to initialize migrate, skipping: %v", err)
|
|
||||||
} else {
|
|
||||||
if err := m.Up(); err != nil && err != migrate.ErrNoChange {
|
|
||||||
log.Printf("Failed to run migrate (maybe tables already exist), continuing: %v", err)
|
|
||||||
} else {
|
|
||||||
log.Println("Database migrations applied successfully")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Initialize Layers
|
// 3. Initialize Layers
|
||||||
userRepo := repository.NewUserRepository(db)
|
userRepo := repository.NewUserRepository(db)
|
||||||
userSvc := service.NewUserService(userRepo)
|
userSvc := service.NewUserService(userRepo)
|
||||||
|
|||||||
Reference in New Issue
Block a user