bindbox-game/main.go
2025-12-26 12:22:32 +08:00

186 lines
5.9 KiB
Go

package main
import (
"context"
"fmt"
"net/http"
"bindbox-game/configs"
"bindbox-game/internal/pkg/env"
"bindbox-game/internal/pkg/logger"
"bindbox-game/internal/pkg/redis"
"bindbox-game/internal/pkg/shutdown"
"bindbox-game/internal/pkg/timeutil"
"bindbox-game/internal/repository/mysql"
"bindbox-game/internal/repository/mysql/dao"
"bindbox-game/internal/router"
activitysvc "bindbox-game/internal/service/activity"
usersvc "bindbox-game/internal/service/user"
"flag"
"go.uber.org/zap"
)
// @title mini-chat 接口文档
// @version v0.0.1
// @securityDefinitions.apikey LoginVerifyToken
// @in header
// @name Authorization
// @BasePath /
func main() {
flag.Parse()
// 初始化 MySQL
dbRepo, err := mysql.New()
if err != nil {
panic(err)
}
// 修复缺失的列以避免创建活动时报错
{
db := dbRepo.GetDbW()
var cnt int64
_ = db.Raw(
"SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = ? AND table_name = 'activities' AND column_name = 'image'",
configs.Get().MySQL.Write.Name,
).Scan(&cnt).Error
if cnt == 0 {
_ = db.Exec("ALTER TABLE `activities` ADD COLUMN `image` VARCHAR(512) NULL COMMENT '活动主图URL' AFTER `banner`").Error
}
cnt = 0
_ = db.Raw(
"SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = ? AND table_name = 'activities' AND column_name = 'gameplay_intro'",
configs.Get().MySQL.Write.Name,
).Scan(&cnt).Error
if cnt == 0 {
_ = db.Exec("ALTER TABLE `activities` ADD COLUMN `gameplay_intro` TEXT NULL COMMENT '玩法介绍' AFTER `image`").Error
}
cnt = 0
_ = db.Raw(
"SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = ? AND table_name = 'activities' AND column_name = 'allow_item_cards'",
configs.Get().MySQL.Write.Name,
).Scan(&cnt).Error
if cnt == 0 {
_ = db.Exec("ALTER TABLE `activities` ADD COLUMN `allow_item_cards` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许使用道具卡' AFTER `is_boss`").Error
}
cnt = 0
_ = db.Raw(
"SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = ? AND table_name = 'activities' AND column_name = 'allow_coupons'",
configs.Get().MySQL.Write.Name,
).Scan(&cnt).Error
if cnt == 0 {
_ = db.Exec("ALTER TABLE `activities` ADD COLUMN `allow_coupons` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许使用优惠券' AFTER `allow_item_cards`").Error
}
// 检查并创建 channels 表
if !db.Migrator().HasTable("channels") {
_ = db.Exec(`CREATE TABLE IF NOT EXISTS channels (
id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
name varchar(255) NOT NULL COMMENT '渠道名称',
code varchar(255) NOT NULL COMMENT '渠道唯一标识',
type varchar(50) NOT NULL DEFAULT 'other' COMMENT '渠道类型',
remarks varchar(512) DEFAULT NULL COMMENT '备注',
created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
updated_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '更新时间',
deleted_at datetime(3) DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (id),
UNIQUE KEY uk_code (code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='推广渠道表';`).Error
}
// users 表新增 channel_id 字段
cnt = 0
_ = db.Raw(
"SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = ? AND table_name = 'users' AND column_name = 'channel_id'",
configs.Get().MySQL.Write.Name,
).Scan(&cnt).Error
if cnt == 0 {
_ = db.Exec("ALTER TABLE users ADD COLUMN channel_id bigint(20) DEFAULT 0 COMMENT '渠道ID' AFTER douyin_id").Error
}
// shipping_records 表新增 batch_no 字段(批量发货分组用)
cnt = 0
_ = db.Raw(
"SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = ? AND table_name = 'shipping_records' AND column_name = 'batch_no'",
configs.Get().MySQL.Write.Name,
).Scan(&cnt).Error
if cnt == 0 {
_ = db.Exec("ALTER TABLE shipping_records ADD COLUMN batch_no VARCHAR(64) NULL COMMENT '批次号(批量发货时用于聚合)' AFTER express_no").Error
_ = db.Exec("CREATE INDEX idx_shipping_records_batch_no ON shipping_records(batch_no)").Error
}
}
// 初始化 自定义 Logger
customLogger, err := logger.NewCustomLogger(dao.Use(dbRepo.GetDbW()),
logger.WithDebugLevel(), // 启用调试级别日志
logger.WithOutputInConsole(), // 启用控制台输出
logger.WithField("domain", fmt.Sprintf("%s[%s]", configs.ProjectName, env.Active().Value())),
logger.WithTimeLayout(timeutil.CSTLayout),
logger.WithFileRotationP(configs.ProjectAccessLogFile),
)
if err != nil {
panic(err)
}
defer func() {
_ = customLogger.Sync()
}()
// 初始化 Redis
if err := redis.Init(context.Background(), customLogger); err != nil {
customLogger.Warn("Redis init failed, some features may be disabled", zap.Error(err))
}
// 初始化 HTTP 服务
mux, cleanup, err := router.NewHTTPMux(customLogger, dbRepo)
if err != nil {
panic(err)
}
server := &http.Server{
Addr: configs.ProjectPort,
Handler: mux,
}
go func() {
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
customLogger.Fatal("http server startup err", zap.Error(err))
}
}()
activitysvc.StartScheduledSettlement(customLogger, dbRepo)
usersvc.StartExpirationCheck(customLogger, dbRepo)
// 优雅关闭
shutdown.Close(
func() {
// 清理资源 (Worker)
if cleanup != nil {
cleanup()
}
// 关闭 http server
if err := server.Shutdown(context.TODO()); err != nil {
customLogger.Error("server shutdown err", zap.Error(err))
}
// 关闭 db master (支持读写)
if err := dbRepo.DbWClose(); err != nil {
customLogger.Error("dbw close err", zap.Error(err))
}
// 关闭 db slave (仅支持读)
if err := dbRepo.DbRClose(); err != nil {
customLogger.Error("dbr close err", zap.Error(err))
}
},
)
}