From e2bff25b45f413cc3ac78ae84768c36f8fc591b2 Mon Sep 17 00:00:00 2001 From: xiabin Date: Mon, 20 Jan 2025 14:37:14 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=92=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- game_open_api/auth.api | 6 ++ game_open_api/douyin.api | 3 +- game_open_api/game.api | 23 ++++--- .../handler/game/rankingListHandler.go | 4 +- .../logic/douyin/douyinCode2UserIdLogic.go | 14 ++-- .../internal/logic/game/rankingListLogic.go | 36 ++++++++-- .../logic/wechat/wechatCode2UserIdLogic.go | 15 ++--- game_open_api/internal/types/types.go | 13 ++++ game_open_api/model/appusermodel.go | 31 +++++++++ game_open_api/model/gamescoremodel.go | 67 +++++++++++++++++++ game_open_api/wechat.api | 5 +- 11 files changed, 181 insertions(+), 36 deletions(-) create mode 100644 game_open_api/auth.api diff --git a/game_open_api/auth.api b/game_open_api/auth.api new file mode 100644 index 0000000..d39595e --- /dev/null +++ b/game_open_api/auth.api @@ -0,0 +1,6 @@ +syntax = "v1" + + +type Auth { + Token string `json:"token"` +} \ No newline at end of file diff --git a/game_open_api/douyin.api b/game_open_api/douyin.api index 48657ca..ffe1583 100644 --- a/game_open_api/douyin.api +++ b/game_open_api/douyin.api @@ -1,5 +1,6 @@ syntax = "v1" +import "auth.api" type DouyinCode2UserIdRequest { AppId string `form:"appId"` @@ -17,7 +18,7 @@ type DouyinCode2UserIdRequest { service game_open_api-api { @handler douyinCode2UserId - get /code2userId (DouyinCode2UserIdRequest) returns (string) + get /code2userId (DouyinCode2UserIdRequest) returns (Auth) } diff --git a/game_open_api/game.api b/game_open_api/game.api index 06f96c2..dcce9f5 100644 --- a/game_open_api/game.api +++ b/game_open_api/game.api @@ -1,23 +1,30 @@ syntax = "v1" - - - type SetUserGameScoreRequest { - Score uint64 `json:"score"` + Score uint64 `json:"score"` } -@server ( +type RankingData { + Nickname string `json:"nickname" db:"nickname"` // 昵称 + Avatar string `json:"avatar" db:"avatar"` // 头像 + Score uint32 `json:"score" db:"score"` // 得分 + UserId uint64 `json:"-" db:"app_user_id"` // 用户 ID + Rank uint32 `json:"rank" db:"t_rank"` // 排名 + Self bool `json:"self" db:"-"` // 是否是自己 +} + + +@server( group: game prefix: /v1/game // 对当前 Foo 语法块下的所有路由,新增 /v1 路由前缀,不需要则请删除此行 - jwt: Auth // 开启 jwt 认证 + jwt: Auth // 开启 jwt 认证 timeout: 3s // 对当前 Foo 语法块下的所有路由进行超时配置,不需要则请删除此行 maxBytes: 1048576 // 对当前 Foo 语法块下的所有路由添加请求体大小控制,单位为 byte,goctl 版本 >= 1.5.0 才支持 ) -service game_open_api-api { +service game_open_api-api { @handler rankingList - get /ranking/list + get /ranking/list returns ([]RankingData) @handler rankingSetScore post /ranking/set_score (SetUserGameScoreRequest) diff --git a/game_open_api/internal/handler/game/rankingListHandler.go b/game_open_api/internal/handler/game/rankingListHandler.go index 0ef63c2..a4a3ef1 100644 --- a/game_open_api/internal/handler/game/rankingListHandler.go +++ b/game_open_api/internal/handler/game/rankingListHandler.go @@ -11,11 +11,11 @@ import ( func RankingListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { l := game.NewRankingListLogic(r.Context(), svcCtx) - err := l.RankingList() + resp, err := l.RankingList() if err != nil { httpx.ErrorCtx(r.Context(), w, err) } else { - httpx.Ok(w) + httpx.OkJsonCtx(r.Context(), w, resp) } } } diff --git a/game_open_api/internal/logic/douyin/douyinCode2UserIdLogic.go b/game_open_api/internal/logic/douyin/douyinCode2UserIdLogic.go index bdd897c..b9d3b93 100644 --- a/game_open_api/internal/logic/douyin/douyinCode2UserIdLogic.go +++ b/game_open_api/internal/logic/douyin/douyinCode2UserIdLogic.go @@ -29,7 +29,9 @@ func NewDouyinCode2UserIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) } } -func (l *DouyinCode2UserIdLogic) DouyinCode2UserId(req *types.DouyinCode2UserIdRequest) (resp string, err error) { +func (l *DouyinCode2UserIdLogic) DouyinCode2UserId(req *types.DouyinCode2UserIdRequest) (resp *types.Auth, err error) { + resp = new(types.Auth) + douyinCli, err := app_api_helper.DouyinCli.GetDouYinOpenApi(req.AppId) if err != nil { return @@ -56,17 +58,11 @@ func (l *DouyinCode2UserIdLogic) DouyinCode2UserId(req *types.DouyinCode2UserIdR Unionid: res.Unionid, AnonymousOpenid: res.AnonymousOpenid, } - result, err := l.svcCtx.AppUser.Insert(l.ctx, aui) + err = l.svcCtx.AppUser.FindOrCreate(l.ctx, aui) if err != nil { return } - id, err := result.LastInsertId() - if err != nil { - return - } - aui.Id = uint64(id) - claims := make(jwt.MapClaims) claims["exp"] = time.Now().Unix() + l.svcCtx.Config.Auth.AccessExpire claims["iat"] = l.svcCtx.Config.Auth.AccessExpire @@ -79,6 +75,6 @@ func (l *DouyinCode2UserIdLogic) DouyinCode2UserId(req *types.DouyinCode2UserIdR } token := jwt.New(jwt.SigningMethodHS256) token.Claims = claims - resp, err = token.SignedString([]byte(l.svcCtx.Config.Auth.AccessSecret)) + resp.Token, err = token.SignedString([]byte(l.svcCtx.Config.Auth.AccessSecret)) return } diff --git a/game_open_api/internal/logic/game/rankingListLogic.go b/game_open_api/internal/logic/game/rankingListLogic.go index 24321b4..1c1aa8d 100644 --- a/game_open_api/internal/logic/game/rankingListLogic.go +++ b/game_open_api/internal/logic/game/rankingListLogic.go @@ -2,9 +2,10 @@ package game import ( "context" + "youtu_server/game_open_api/internal/svc" + "youtu_server/game_open_api/internal/types" "github.com/zeromicro/go-zero/core/logx" - "youtu_server/game_open_api/internal/svc" ) type RankingListLogic struct { @@ -21,8 +22,35 @@ func NewRankingListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Ranki } } -func (l *RankingListLogic) RankingList() error { - // todo: add your logic here and delete this line +func (l *RankingListLogic) RankingList() (resp []types.RankingData, err error) { + at, err := svc.GetCtxToken(l.ctx) + if err != nil { + return nil, err + } - return nil + resp, err = l.svcCtx.GameScore.GetRankList(l.ctx, at.AppId, at.UserId) + if err != nil { + return nil, err + } + + var flag bool + for i := range resp { + if resp[i].UserId == at.UserId { + resp[i].Self = true + flag = true + break + } + } + + if !flag { + userRank, err := l.svcCtx.GameScore.GetUserRank(l.ctx, at.AppId, at.UserId) + //_, _ = userRank, err + if err != nil { + return nil, err + } + userRank.Self = true + resp = append(resp, *userRank) + } + + return } diff --git a/game_open_api/internal/logic/wechat/wechatCode2UserIdLogic.go b/game_open_api/internal/logic/wechat/wechatCode2UserIdLogic.go index bb0366b..7a826ca 100644 --- a/game_open_api/internal/logic/wechat/wechatCode2UserIdLogic.go +++ b/game_open_api/internal/logic/wechat/wechatCode2UserIdLogic.go @@ -29,7 +29,8 @@ func NewWechatCode2UserIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) } } -func (l *WechatCode2UserIdLogic) WechatCode2UserId(req *types.WechatCode2UserIdRequest) (resp string, err error) { +func (l *WechatCode2UserIdLogic) WechatCode2UserId(req *types.WechatCode2UserIdRequest) (resp *types.Auth, err error) { + resp = new(types.Auth) wechatCli, err := app_api_helper.WechatCli.GetWechatOpenApi(req.AppId) if err != nil { return @@ -56,19 +57,11 @@ func (l *WechatCode2UserIdLogic) WechatCode2UserId(req *types.WechatCode2UserIdR Unionid: res.UnionID, } - //todo 校验唯一 - - result, err := l.svcCtx.AppUser.Insert(l.ctx, aui) + err = l.svcCtx.AppUser.FindOrCreate(l.ctx, aui) if err != nil { return } - id, err := result.LastInsertId() - if err != nil { - return - } - aui.Id = uint64(id) - claims := make(jwt.MapClaims) claims["exp"] = time.Now().Unix() + l.svcCtx.Config.Auth.AccessExpire claims["iat"] = l.svcCtx.Config.Auth.AccessExpire @@ -81,7 +74,7 @@ func (l *WechatCode2UserIdLogic) WechatCode2UserId(req *types.WechatCode2UserIdR } token := jwt.New(jwt.SigningMethodHS256) token.Claims = claims - resp, err = token.SignedString([]byte(l.svcCtx.Config.Auth.AccessSecret)) + resp.Token, err = token.SignedString([]byte(l.svcCtx.Config.Auth.AccessSecret)) return } diff --git a/game_open_api/internal/types/types.go b/game_open_api/internal/types/types.go index f63af4e..338677f 100644 --- a/game_open_api/internal/types/types.go +++ b/game_open_api/internal/types/types.go @@ -3,12 +3,25 @@ package types +type Auth struct { + Token string `json:"token"` +} + type DouyinCode2UserIdRequest struct { AppId string `form:"appId"` Code string `form:"code,optional"` AnonymousCode string `form:"anonymousCode,optional"` } +type RankingData struct { + Nickname string `json:"nickname" db:"nickname"` // 昵称 + Avatar string `json:"avatar" db:"avatar"` // 头像 + Score uint32 `json:"score" db:"score"` // 得分 + UserId uint64 `json:"-" db:"app_user_id"` // 用户 ID + Rank uint32 `json:"rank" db:"t_rank"` // 排名 + Self bool `json:"self" db:"-"` // 是否是自己 +} + type SetAppUserRequest struct { Nickname string `json:"nickname"` Avatar string `json:"avatar"` diff --git a/game_open_api/model/appusermodel.go b/game_open_api/model/appusermodel.go index f8ac301..95c71e7 100644 --- a/game_open_api/model/appusermodel.go +++ b/game_open_api/model/appusermodel.go @@ -1,7 +1,10 @@ package model import ( + "context" + "fmt" "github.com/zeromicro/go-zero/core/stores/cache" + "github.com/zeromicro/go-zero/core/stores/sqlc" "github.com/zeromicro/go-zero/core/stores/sqlx" ) @@ -12,6 +15,7 @@ type ( // and implement the added methods in customAppUserModel. AppUserModel interface { appUserModel + FindOrCreate(ctx context.Context, data *AppUser) (err error) } customAppUserModel struct { @@ -25,3 +29,30 @@ func NewAppUserModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) defaultAppUserModel: newAppUserModel(conn, c, opts...), } } + +func (m *customAppUserModel) FindOrCreate(ctx context.Context, data *AppUser) (err error) { + ecpmAppUserIdKey := fmt.Sprintf("%s%v", cacheEcpmAppUserIdPrefix, data.Id) + var resp AppUser + err = m.QueryRowCtx(ctx, &resp, ecpmAppUserIdKey, func(ctx context.Context, conn sqlx.SqlConn, v any) error { + query := fmt.Sprintf("select %s from %s where `openid` = ? limit 1", appUserRows, m.table) + return conn.QueryRowCtx(ctx, v, query, data.Openid) + }) + switch err { + case nil: + *data = resp + return nil + case sqlc.ErrNotFound: + res, err := m.Insert(ctx, data) + if err != nil { + return err + } + id, err := res.LastInsertId() + if err != nil { + return err + } + data.Id = uint64(id) + return nil + default: + return err + } +} diff --git a/game_open_api/model/gamescoremodel.go b/game_open_api/model/gamescoremodel.go index c41e425..c878a4e 100644 --- a/game_open_api/model/gamescoremodel.go +++ b/game_open_api/model/gamescoremodel.go @@ -1,17 +1,29 @@ package model import ( + "context" + "errors" + "fmt" "github.com/zeromicro/go-zero/core/stores/cache" + "github.com/zeromicro/go-zero/core/stores/sqlc" "github.com/zeromicro/go-zero/core/stores/sqlx" + "youtu_server/game_open_api/internal/types" ) var _ GameScoreModel = (*customGameScoreModel)(nil) +const ( + cacheEcpmRankListPrefix = "cache:ecpm:game:rankList:appId:" + cacheEcpmUserRankPrefix = "cache:ecpm:game:userRank:userId:" +) + type ( // GameScoreModel is an interface to be customized, add more methods here, // and implement the added methods in customGameScoreModel. GameScoreModel interface { gameScoreModel + GetRankList(ctx context.Context, appId uint64, userId uint64) ([]types.RankingData, error) + GetUserRank(ctx context.Context, appId uint64, userId uint64) (*types.RankingData, error) } customGameScoreModel struct { @@ -25,3 +37,58 @@ func NewGameScoreModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Optio defaultGameScoreModel: newGameScoreModel(conn, c, opts...), } } + +func (m *defaultGameScoreModel) GetRankList(ctx context.Context, appId uint64, userId uint64) (resp []types.RankingData, err error) { + ecpmGameScoreAppUserIdKey := fmt.Sprintf("%s%v", cacheEcpmRankListPrefix, appId) + err = m.QueryRowCtx(ctx, &resp, ecpmGameScoreAppUserIdKey, func(ctx context.Context, conn sqlx.SqlConn, v any) error { + query := "SELECT game_score.score,app_user.nickname,app_user.avatar,game_score.app_user_id,rank() over(ORDER BY game_score.score desc) t_rank FROM `game_score` JOIN app_user ON app_user.id=game_score.app_user_id WHERE game_score.app_account=? ORDER BY game_score.score DESC LIMIT 20" + return conn.QueryRowsPartialCtx(ctx, v, query, appId) + }) + switch { + case err == nil: + return resp, nil + case errors.Is(err, sqlc.ErrNotFound): + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *defaultGameScoreModel) GetUserRank(ctx context.Context, appId uint64, userId uint64) (resp *types.RankingData, err error) { + ecpmGameScoreAppUserIdKey := fmt.Sprintf("%s%v", cacheEcpmUserRankPrefix, userId) + var res []types.RankingData + err = m.QueryRowCtx(ctx, &res, ecpmGameScoreAppUserIdKey, func(ctx context.Context, conn sqlx.SqlConn, v any) error { + query := `SELECT + app_user.nickname, + app_user.avatar, + gs.score, + gs.app_user_id, + gs.t_rank +FROM + ( + SELECT + game_score.score, + game_score.app_user_id, + game_score.app_account, + rank() over (ORDER BY game_score.score DESC) t_rank + FROM + game_score) AS gs + LEFT JOIN app_user ON app_user.id = gs.app_user_id + AND gs.app_account = ? +WHERE + gs.app_user_id = ? + LIMIT 1 ` + return conn.QueryRowsPartialCtx(ctx, v, query, appId, userId) + }) + switch { + case err == nil: + if len(res) == 1 { + resp = &res[0] + } + return resp, nil + case errors.Is(err, sqlc.ErrNotFound): + return nil, ErrNotFound + default: + return nil, err + } +} diff --git a/game_open_api/wechat.api b/game_open_api/wechat.api index 313b576..1a033d2 100644 --- a/game_open_api/wechat.api +++ b/game_open_api/wechat.api @@ -1,5 +1,8 @@ syntax = "v1" +import "auth.api" + + type WechatCode2UserIdRequest { AppId string `form:"appId"` Code string `form:"code"` @@ -15,6 +18,6 @@ type WechatCode2UserIdRequest { service game_open_api-api { @handler wechatCode2UserId - get /code2userId (WechatCode2UserIdRequest) returns (string ) + get /code2userId (WechatCode2UserIdRequest) returns (Auth ) }