package main import ( "context" "fmt" "net/http" "bindbox-game/configs" "bindbox-game/internal/pkg/env" "bindbox-game/internal/pkg/logger" "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" "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() }() // 初始化 HTTP 服务 mux, 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) // 优雅关闭 shutdown.Close( func() { // 关闭 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)) } }, ) }