youtu_server/game_open_api/internal/svc/service_context.go
2025-01-25 18:47:49 +08:00

216 lines
6.9 KiB
Go

package svc
import (
"context"
"encoding/json"
"errors"
"gitea.youtukeji.com.cn/xiabin/youtu_server/game_open_api/internal/config"
"gitea.youtukeji.com.cn/xiabin/youtu_server/game_open_api/internal/logic/rankings"
"gitea.youtukeji.com.cn/xiabin/youtu_server/gorm-gen/dao/query"
redisCacher "gitea.youtukeji.com.cn/xiabin/youtu_server/gorm-gen/redis"
helper "gitea.youtukeji.com.cn/youtu/openapi-helper"
"github.com/go-gorm/caches/v4"
"github.com/golang-jwt/jwt/v4"
"github.com/redis/go-redis/v9"
redisCache "github.com/silenceper/wechat/v2/cache"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"math/rand"
"time"
)
type ServiceContext struct {
Config config.Config
DouyinCli *helper.DouYinOpenApiClient
WechatCli *helper.WechatApi
ZapLogger *zap.Logger
RedisRanking *rankings.Ranking
Query *query.Query
}
func NewServiceContext(c config.Config) *ServiceContext {
//初始化redis client
redisClient := redis.NewClient(&redis.Options{
Addr: c.DWCache.Host,
Password: c.DWCache.Password, // 没有密码,默认值
})
//初始化数据库
db, err := gorm.Open(mysql.Open(c.DB.DataSource), &gorm.Config{})
if err != nil {
panic(err)
}
err = db.Use(&caches.Caches{Conf: &caches.Config{
Easer: true,
Cacher: redisCacher.New(redisClient),
}})
if err != nil {
panic(err)
}
svc := &ServiceContext{
Config: c,
Query: query.Use(db),
}
//初始化排行榜对象
svc.InitRankings(redisClient)
//初始化小程序配置
svc.InitDWClient(c)
//初始化一个zap日志对象用于写入ecpm日志
svc.InitEcpmLog(c)
return svc
}
func (svc *ServiceContext) InitRankings(redisClient *redis.Client) {
//初始化排行榜对象
svc.RedisRanking = rankings.NewRanking(redisClient)
//获取所有不同的排行榜
gs := svc.Query.GameScore
r, err := gs.FindDistinctRanking()
if err != nil {
panic(err)
}
//获取所有排行榜
for _, ranking := range r {
scores, err := gs.Where(gs.AppAccount.Eq(ranking.AppAccount), gs.T.Eq(ranking.T)).Find()
if err != nil {
panic(err)
}
data := make([]redis.Z, 0, len(scores))
for _, score := range scores {
data = append(data, redis.Z{
Score: float64(uint64(score.Score)<<32 + uint64(score.UpdatedAt.Unix())),
Member: score.AppUserID,
})
}
svc.RedisRanking.SetList(context.Background(), rankings.GetRankingsCacheKey(ranking.AppAccount, ranking.T), data...)
}
}
func (svc *ServiceContext) InitDWClient(c config.Config) {
dwCache := redisCache.NewRedis(context.Background(), &redisCache.RedisOpts{Host: c.DWCache.Host, IdleTimeout: c.DWCache.IdleTimeout})
svc.DouyinCli = helper.NewDouYinOpenApiClient()
svc.WechatCli = helper.NewWechatOpenApiClient()
//select `app_id`,`secret`,`ecpm_value`,`ecpm_view`,`type` from `app_account` left join douyin_ecpm_config on app_account.id = douyin_ecpm_config.app_account_id
var result []struct {
ID int32 `gorm:"column:id;type:int unsigned;primaryKey;autoIncrement:true" json:"id"`
Type int32 `gorm:"column:type;type:tinyint unsigned;not null;comment:类型(0:抖音,1:微信)" json:"type"` // 类型(0:抖音,1:微信)
AppID string `gorm:"column:app_id;type:char(20);not null;uniqueIndex:app_id,priority:1" json:"app_id"`
Secret string `gorm:"column:secret;type:char(40);not null" json:"secret"`
EcpmValue uint32 `gorm:"column:ecpm_value;type:int unsigned;not null;comment:值" json:"ecpm_value"` // 值
EcpmView uint32 `gorm:"column:ecpm_view;type:int unsigned;not null;comment:浏览次数" json:"ecpm_view"` // 浏览次数
}
//查询小程序配置(抖音&微信)
err := svc.Query.AppAccount.LeftJoin(svc.Query.DouyinEcpmConfig, svc.Query.AppAccount.ID.EqCol(svc.Query.DouyinEcpmConfig.AppAccountID)).Scan(&result)
if err != nil {
panic(err)
}
//小程序配置
for _, v := range result {
if v.Type == 0 {
svc.DouyinCli.NewAndStoreDouYinOpenApi(v.AppID, v.Secret, v.EcpmValue, v.EcpmView, dwCache)
} else {
svc.WechatCli.NewAndStoreWechatOpenApi(v.AppID, v.Secret, dwCache)
}
}
}
func (svc *ServiceContext) InitEcpmLog(c config.Config) {
svc.ZapLogger = zap.New(zapcore.NewCore(
zapcore.NewJSONEncoder(zapcore.EncoderConfig{
TimeKey: "ts",
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
FunctionKey: zapcore.OmitKey,
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: zapcore.EpochTimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
}),
zapcore.NewMultiWriteSyncer(zapcore.AddSync(&lumberjack.Logger{
Filename: c.EcpmLogPath, // Log file path 日志文件的路径
MaxSize: 1024, // Maximum size unit for each log file: M 每个日志最大大小
MaxBackups: 30, // The maximum number of backups that can be saved for log files 可以保存的日志文件数量
MaxAge: 7, // Maximum number of days the file can be saved 日志文件保存的最大天数
Compress: true, // Compression or not 是否对日志文件进行压缩
})),
zapcore.InfoLevel,
))
}
type AccessToken struct {
AppId uint32 `json:"appId"`
UserId uint64 `json:"userId"`
AppIdStr string `json:"appIdStr"`
OpenId string `json:"openId"`
}
func UnmarshalAccessToken(d any) (ac AccessToken, err error) {
m, ok := d.(map[string]interface{})
if !ok {
err = errors.New("invalid access token")
return
}
appId, _ := m["appId"].(json.Number).Int64()
ac.AppId = uint32(appId)
userId, _ := m["userId"].(json.Number).Int64()
ac.UserId = uint64(userId)
ac.AppIdStr = m["appIdStr"].(string)
ac.OpenId = m["openId"].(string)
return
}
func GetCtxToken(ctx context.Context) (ac AccessToken, err error) {
return UnmarshalAccessToken(ctx.Value("payload"))
}
// GenerateAccessToken 生成 JWT 认证的 token
// 会从ServiceContext中获取配置信息
func (svc *ServiceContext) GenerateAccessToken(at *AccessToken) (token string, err error) {
claims := make(jwt.MapClaims)
claims["exp"] = time.Now().Unix() + svc.Config.Auth.AccessExpire
claims["iat"] = svc.Config.Auth.AccessExpire
claims["payload"] = at
t := jwt.New(jwt.SigningMethodHS256)
t.Claims = claims
token, err = t.SignedString([]byte(svc.Config.Auth.AccessSecret))
return
}
var DefaultUsername = []string{
"甜蜜糖果",
"糖果爱好者",
"软糖粉丝",
"巧克力糖果控",
"棒棒糖迷",
"小熊软糖达人",
"硬糖狂人",
"焦糖糖果控",
"水果糖行家",
"棉花糖达人",
}
// GetRandomUsername 随机获取一个糖果相关的用户名
func GetRandomUsername() string {
// 初始化随机数种子
r := rand.New(rand.NewSource(time.Now().UnixNano()))
// 生成一个 0 到列表长度减 1 之间的随机索引
randomIndex := r.Intn(len(DefaultUsername))
// 根据随机索引返回对应的用户名
return DefaultUsername[randomIndex]
}