package mysql import ( "context" "database/sql" "strings" "time" _ "github.com/go-sql-driver/mysql" "hyapp/pkg/xerr" ) // Repository 是 lucky-gift-service 的 MySQL 持久化入口。 // 幸运礼物的规则版本、RTP 窗口、单池水位、抽奖记录和返奖 outbox 必须落在同一个 owner 库, // 调用方只能通过 service/grpc 进入,不能绕过事务边界直接修改这些表。 type Repository struct { db *sql.DB } // Open 创建幸运礼物服务专用连接池并做启动探活。 // DSN 必须指向 hyapp_lucky_gift;复用 activity 或 wallet 库会破坏 owner 边界,也会让 outbox 补偿读错事实。 func Open(ctx context.Context, dsn string) (*Repository, error) { if strings.TrimSpace(dsn) == "" { return nil, xerr.New(xerr.InvalidArgument, "mysql_dsn is required") } db, err := sql.Open("mysql", dsn) if err != nil { return nil, err } // 标准库默认连接数不设限;幸运礼物抽奖会持有多张表行锁,必须限制并发连接避免压垮 MySQL。 db.SetMaxOpenConns(20) db.SetMaxIdleConns(10) db.SetConnMaxLifetime(30 * time.Minute) if err := db.PingContext(ctx); err != nil { _ = db.Close() return nil, err } return &Repository{db: db}, nil } // Migrate 只做本地/测试环境的轻量幂等兜底。 // 生产表结构仍以 deploy/mysql/initdb 和显式 migration 为准,避免服务启动时悄悄改动线上结构。 func (r *Repository) Migrate(ctx context.Context) error { if r == nil || r.db == nil { return xerr.New(xerr.Unavailable, "mysql repository is not configured") } // 当前拆分版本优先依赖完整初始化 SQL;这里保留探活,让 Docker 本地启动能在 DSN 错误时快速失败。 return r.db.PingContext(ctx) } // Close 释放连接池;服务关闭时先停止 worker 再关库,避免补偿协程拿到已关闭连接。 func (r *Repository) Close() error { if r == nil || r.db == nil { return nil } return r.db.Close() } // Ping 给健康检查使用,只确认 owner 库可访问,不代表钱包或 IM 下游可用。 func (r *Repository) Ping(ctx context.Context) error { if r == nil || r.db == nil { return xerr.New(xerr.Unavailable, "mysql repository is not configured") } return r.db.PingContext(ctx) }