排行榜
This commit is contained in:
parent
cf601f9466
commit
7f7a881d46
37
docker-compose-dev.yaml
Normal file
37
docker-compose-dev.yaml
Normal file
@ -0,0 +1,37 @@
|
||||
version: "3"
|
||||
services:
|
||||
mysql:
|
||||
image: mysql
|
||||
container_name: mysql
|
||||
restart: always
|
||||
command: [
|
||||
'--character-set-server=utf8mb4',
|
||||
'--collation-server=utf8mb4_general_ci',
|
||||
'--explicit_defaults_for_timestamp=true',
|
||||
'--lower_case_table_names=1'
|
||||
]
|
||||
ports:
|
||||
- "3306:3306"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: youtu!0113
|
||||
MYSQL_INITDB_SKIP_TZINFO: "Asia/Shanghai"
|
||||
MYSQL_DATABASE: ecpm
|
||||
volumes:
|
||||
##初始化的脚本,初始化我们存放的init.sql文件
|
||||
- ./sql:/docker-entrypoint-initdb.d/
|
||||
healthcheck:
|
||||
test: [ "CMD", "mysqladmin", "ping", "-h", "localhost", "-uyoutu", "-pyoutu!0113" ]
|
||||
interval: 6s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
#network_mode: host
|
||||
redis:
|
||||
image: redis
|
||||
restart: always
|
||||
hostname: redis
|
||||
container_name: redis
|
||||
privileged: true
|
||||
ports:
|
||||
- "6379:6379"
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
@ -20,6 +20,7 @@ type Config struct {
|
||||
|
||||
DWCache struct {
|
||||
Host string // 缓存服务器地址
|
||||
Password string // 缓存服务器密码
|
||||
IdleTimeout int
|
||||
} // 抖音微信缓存配置
|
||||
|
||||
|
@ -2,6 +2,7 @@ package game
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gitea.youtukeji.com.cn/xiabin/youtu_server/game_open_api/internal/logic/rankings"
|
||||
"gitea.youtukeji.com.cn/xiabin/youtu_server/game_open_api/internal/svc"
|
||||
"gitea.youtukeji.com.cn/xiabin/youtu_server/game_open_api/internal/types"
|
||||
|
||||
@ -32,28 +33,40 @@ func (l *RankingListLogic) RankingList(req *types.RankingListRequest) (resp *typ
|
||||
l.Logger.Debugf("at: %+v", at)
|
||||
l.Logger.Debugf("req: %+v", req)
|
||||
|
||||
resp.RankingData, err = l.svcCtx.GameScore.GetRankList(l.ctx, at.AppId, req.Type, req.PageBase)
|
||||
cacheData, err := l.svcCtx.RedisRanking.GetList(l.ctx, rankings.GetRankingsCacheKey(at.AppId, req.Type), 0, 99)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
flag bool
|
||||
userRank types.RankingData
|
||||
)
|
||||
for i := range resp.RankingData {
|
||||
if resp.RankingData[i].UserId == at.UserId {
|
||||
resp.RankingData[i].Self = true
|
||||
userRank = resp.RankingData[i]
|
||||
l.Logger.Debugf("userRank: %+v", userRank)
|
||||
resp.RankingData = append(resp.RankingData, userRank)
|
||||
flag = true
|
||||
break
|
||||
var flag bool
|
||||
|
||||
resp.RankingData = make([]types.RankingData, len(cacheData))
|
||||
var userRank types.RankingData
|
||||
|
||||
for i, datum := range cacheData {
|
||||
if id, ok := datum.Member.(uint64); ok {
|
||||
//查询用户数据,FindOne带缓存
|
||||
user, err := l.svcCtx.AppUser.FindOne(l.ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := types.RankingData{
|
||||
Nickname: user.Nickname,
|
||||
Avatar: user.Avatar,
|
||||
Score: uint32(datum.Score),
|
||||
Rank: uint32(i), //todo
|
||||
Self: user.Id == at.UserId,
|
||||
}
|
||||
if user.Id == at.UserId {
|
||||
flag = true
|
||||
userRank = data
|
||||
}
|
||||
resp.RankingData = append(resp.RankingData, data)
|
||||
}
|
||||
}
|
||||
|
||||
if !flag {
|
||||
userRank, err := l.svcCtx.GameScore.GetUserRank(l.ctx, at.AppId, at.UserId, req.Type)
|
||||
userRank, err = l.svcCtx.GameScore.GetUserRank(l.ctx, at.AppId, at.UserId, req.Type)
|
||||
if err != nil {
|
||||
|
||||
return nil, err
|
||||
|
@ -3,7 +3,9 @@ package game
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gitea.youtukeji.com.cn/xiabin/youtu_server/game_open_api/internal/logic/rankings"
|
||||
"gitea.youtukeji.com.cn/xiabin/youtu_server/game_open_api/model"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlc"
|
||||
|
||||
"gitea.youtukeji.com.cn/xiabin/youtu_server/game_open_api/internal/svc"
|
||||
@ -55,6 +57,15 @@ func (l *RankingSetScoreLogic) RankingSetScore(req *types.SetUserGameScoreReques
|
||||
err = l.svcCtx.GameScore.CreateScore(l.ctx, oldScore)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 更新排行榜
|
||||
l.svcCtx.RedisRanking.SetList(l.ctx, rankings.GetRankingsCacheKey(at.AppId, req.Type), redis.Z{
|
||||
Member: at.UserId,
|
||||
Score: float64(req.Score),
|
||||
})
|
||||
l.Logger.Debugf("GameScore: %+v", oldScore)
|
||||
|
||||
return
|
||||
|
45
game_open_api/internal/logic/rankings/ranking.go
Normal file
45
game_open_api/internal/logic/rankings/ranking.go
Normal file
@ -0,0 +1,45 @@
|
||||
package rankings
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
// Ranking 排行榜结构体
|
||||
type Ranking struct {
|
||||
c *redis.Client
|
||||
}
|
||||
|
||||
const EcpmRankingsListPrefix = "ecpm:rankings:"
|
||||
|
||||
// NewRanking 创建一个新的排行榜实例
|
||||
func NewRanking(c *redis.Client) *Ranking {
|
||||
return &Ranking{
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
|
||||
func GetRankingsCacheKey(appId, t uint64) string {
|
||||
return fmt.Sprintf("%sappId:%d:type:%d", EcpmRankingsListPrefix, appId, t)
|
||||
}
|
||||
|
||||
// SetList 向排行榜中添加成员及其分数
|
||||
func (r *Ranking) SetList(ctx context.Context, key string, data ...redis.Z) {
|
||||
r.c.ZAdd(ctx, EcpmRankingsListPrefix+key, data...)
|
||||
}
|
||||
|
||||
// GetList 获取排行榜,按照分数从高到低排序
|
||||
func (r *Ranking) GetList(ctx context.Context, key string, start, stop int64) (data []redis.Z, err error) {
|
||||
return r.c.ZRevRangeWithScores(ctx, EcpmRankingsListPrefix+key, start, stop).Result()
|
||||
}
|
||||
|
||||
// GetRank 获取指定成员在排行榜中的排名(排名从 0 开始,分数越高排名越靠前)
|
||||
func (r *Ranking) GetRank(ctx context.Context, key, member string) (rank int64, err error) {
|
||||
return r.c.ZRevRank(ctx, EcpmRankingsListPrefix+key, member).Result()
|
||||
}
|
||||
|
||||
// GetScore 获取指定成员在排行榜中的分数
|
||||
func (r *Ranking) GetScore(ctx context.Context, key, member string) (score float64, err error) {
|
||||
return r.c.ZScore(ctx, EcpmRankingsListPrefix+key, member).Result()
|
||||
}
|
@ -5,9 +5,11 @@ import (
|
||||
"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/game_open_api/model"
|
||||
helper "gitea.youtukeji.com.cn/youtu/openapi-helper"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/redis/go-redis/v9"
|
||||
redisCache "github.com/silenceper/wechat/v2/cache"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
"go.uber.org/zap"
|
||||
@ -18,13 +20,14 @@ import (
|
||||
)
|
||||
|
||||
type ServiceContext struct {
|
||||
Config config.Config
|
||||
AppUser model.AppUserModel
|
||||
GameScore model.GameScoreModel
|
||||
AppAccount model.AppAccountModel
|
||||
DouyinCli *helper.DouYinOpenApiClient
|
||||
WechatCli *helper.WechatApi
|
||||
ZapLogger *zap.Logger
|
||||
Config config.Config
|
||||
AppUser model.AppUserModel
|
||||
GameScore model.GameScoreModel
|
||||
AppAccount model.AppAccountModel
|
||||
DouyinCli *helper.DouYinOpenApiClient
|
||||
WechatCli *helper.WechatApi
|
||||
ZapLogger *zap.Logger
|
||||
RedisRanking *rankings.Ranking
|
||||
}
|
||||
|
||||
func NewServiceContext(c config.Config) *ServiceContext {
|
||||
@ -35,15 +38,26 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||
AppAccount: model.NewAppAccountModel(sqlx.NewMysql(c.DB.DataSource), c.Cache),
|
||||
}
|
||||
|
||||
//初始化redis client
|
||||
redisClient := redis.NewClient(&redis.Options{
|
||||
Addr: c.DWCache.Host,
|
||||
Password: c.DWCache.Password, // 没有密码,默认值
|
||||
})
|
||||
|
||||
//初始化排行榜对象
|
||||
svc.RedisRanking = rankings.NewRanking(redisClient)
|
||||
|
||||
dwCache := redisCache.NewRedis(context.Background(), &redisCache.RedisOpts{Host: c.DWCache.Host, IdleTimeout: c.DWCache.IdleTimeout})
|
||||
|
||||
svc.DouyinCli = helper.NewDouYinOpenApiClient()
|
||||
svc.WechatCli = helper.NewWechatOpenApiClient()
|
||||
|
||||
//查询小程序配置(抖音&微信)
|
||||
result, err := svc.AppAccount.FindAll(context.Background())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
//小程序配置
|
||||
for _, v := range *result {
|
||||
if v.Type == 0 {
|
||||
svc.DouyinCli.NewAndStoreDouYinOpenApi(v.AppID, v.Secret, v.EcpmValue.V, v.EcpmView.V, dwCache)
|
||||
@ -52,6 +66,24 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||
}
|
||||
}
|
||||
|
||||
//获取所有排行榜
|
||||
rankList, err := svc.GameScore.FindAllRankList(context.Background())
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
//添加排行榜数据
|
||||
for s, scores := range rankList {
|
||||
data := make([]redis.Z, len(scores))
|
||||
for _, score := range scores {
|
||||
data = append(data, redis.Z{
|
||||
Score: float64(score.Score),
|
||||
Member: score.AppUserId,
|
||||
})
|
||||
}
|
||||
svc.RedisRanking.SetList(context.Background(), s, data...)
|
||||
}
|
||||
|
||||
//初始化一个zap日志对象用于写入ecpm日志
|
||||
svc.ZapLogger = zap.New(zapcore.NewCore(
|
||||
zapcore.NewJSONEncoder(zapcore.EncoderConfig{
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"gitea.youtukeji.com.cn/xiabin/youtu_server/game_open_api/internal/logic/rankings"
|
||||
"sync"
|
||||
|
||||
"gitea.youtukeji.com.cn/xiabin/youtu_server/game_open_api/internal/types"
|
||||
@ -29,6 +30,8 @@ type (
|
||||
GetUserRank(ctx context.Context, appId uint64, userId uint64, t uint64) (types.RankingData, error)
|
||||
FindUserScore(ctx context.Context, appId, userId, t uint64) (*GameScore, error)
|
||||
|
||||
FindAllRankList(ctx context.Context) (resp map[string][]GameScore, err error)
|
||||
|
||||
UpdateScore(ctx context.Context, data *GameScore) error
|
||||
CreateScore(ctx context.Context, data *GameScore) error
|
||||
}
|
||||
@ -66,6 +69,32 @@ func (m *customGameScoreModel) userRankCacheKey(userId, appId, t uint64) string
|
||||
return fmt.Sprintf("%srank:userId:%d:appId:%v:t:%d", cacheEcpmGameScorePrefix, userId, appId, t)
|
||||
}
|
||||
|
||||
// FindAllRankList 查询所有排行榜
|
||||
func (m *customGameScoreModel) FindAllRankList(ctx context.Context) (resp map[string][]GameScore, err error) {
|
||||
list := make([]GameScore, 0)
|
||||
resp = make(map[string][]GameScore)
|
||||
err = m.QueryRowNoCacheCtx(ctx, &list, "select DISTINCT app_account,t from game_score")
|
||||
if err != nil && !errors.Is(err, sqlx.ErrNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, rank := range list {
|
||||
data, err := m.FindAllScore(ctx, rank.AppAccount, rank.T)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(data) != 0 {
|
||||
resp[rankings.GetRankingsCacheKey(rank.AppAccount, rank.T)] = data
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (m *customGameScoreModel) FindAllScore(ctx context.Context, appId uint64, t uint64) (resp []GameScore, err error) {
|
||||
err = m.QueryRowNoCacheCtx(ctx, &resp, "select * from game_score where app_account = ? and t = ?", appId, t)
|
||||
return
|
||||
}
|
||||
|
||||
// GetRankList 获取排行榜列表
|
||||
func (m *customGameScoreModel) GetRankList(ctx context.Context, appId uint64, t uint64, page types.PageBase) (resp []types.RankingData, err error) {
|
||||
cacheKey := m.rankListCacheKey(appId, t)
|
||||
|
1
go.mod
1
go.mod
@ -4,6 +4,7 @@ go 1.23.5
|
||||
|
||||
require (
|
||||
gitea.youtukeji.com.cn/youtu/openapi-helper v0.0.3-2
|
||||
github.com/go-redis/redis v6.15.9+incompatible
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1
|
||||
github.com/silenceper/wechat/v2 v2.1.7
|
||||
github.com/zeromicro/go-zero v1.7.6
|
||||
|
2
go.sum
2
go.sum
@ -47,6 +47,8 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
|
||||
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
|
149
sql/ecpm_backup.sql
Normal file
149
sql/ecpm_backup.sql
Normal file
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user