From aea644c553ebe30dd6b7d817a433520835bdd837 Mon Sep 17 00:00:00 2001 From: likun <906102152@qq.com> Date: Thu, 8 May 2025 15:48:34 +0800 Subject: [PATCH] finish cdkey --- admin/apps/game/boot.go | 10 +- admin/apps/game/config/flags.go | 7 + admin/apps/game/domain/cdkey.go | 58 ++++++- admin/apps/game/domain/entity/cdkey.go | 49 ++++++ admin/apps/game/domain/entity/utils.go | 10 ++ .../apps/game/domain/projects/smdl/notice.go | 110 +++++++++++++ admin/apps/game/domain/repo/cdkey.go | 88 ++++++++++- admin/apps/game/model/cdkey.go | 4 +- admin/apps/game/model/cdkey_used.go | 13 +- admin/apps/game/model/dto/common.go | 14 +- admin/apps/game/model/dto/msg.go | 34 ++++ admin/apps/game/server/ctl_cdkey.go | 36 ++++- admin/apps/game/server/route.go | 56 ++++--- admin/apps/game/service/service_cdkey.go | 21 ++- admin/internal/errcode/code.go | 9 ++ admin/lib/node/app.go | 7 +- admin/todo.md | 0 admin/uniugm | Bin 94208 -> 0 bytes ui/README.md | 6 +- ui/src/api/cdkey.js | 8 + ui/src/components/restful/tableCDKey.vue | 148 ++++++++++++------ 21 files changed, 580 insertions(+), 108 deletions(-) create mode 100644 admin/apps/game/config/flags.go create mode 100644 admin/apps/game/domain/projects/smdl/notice.go create mode 100644 admin/todo.md delete mode 100644 admin/uniugm diff --git a/admin/apps/game/boot.go b/admin/apps/game/boot.go index fe90095..9526dc4 100644 --- a/admin/apps/game/boot.go +++ b/admin/apps/game/boot.go @@ -1,8 +1,10 @@ package game import ( + "admin/apps/game/config" "admin/apps/game/server" "admin/apps/game/service" + "admin/internal/context" "admin/internal/global" "admin/lib/node" ) @@ -12,13 +14,15 @@ func initFun(app *node.Application) error { if err != nil { panic(err) } - srv := server.New(svc) // 初始化http服务 - srv.Route(global.GLOB_API_ENGINE) // 初始化http服务路由 + sdkEngine := app.WithServer("内部游戏调用的sdk服务", ":"+config.BootFlags.ApiSDKPort, context.NewWebContext) + srv := server.New(svc) // 初始化http服务 + srv.Route(global.GLOB_API_ENGINE, sdkEngine) // 初始化http服务路由 return nil } func New() *node.ApplicationDescInfo { - app := node.NewApplicationDescInfo("game", initFun) + app := node.NewApplicationDescInfo("game", initFun). + WithOptions(node.WithAppBootFlag(config.BootFlags)) return app } diff --git a/admin/apps/game/config/flags.go b/admin/apps/game/config/flags.go new file mode 100644 index 0000000..64f27b3 --- /dev/null +++ b/admin/apps/game/config/flags.go @@ -0,0 +1,7 @@ +package config + +var BootFlags = &GameBootFlags{} + +type GameBootFlags struct { + ApiSDKPort string `env:"api_sdk_port" default:"8180" desc:"sdk接口,内部游戏调用地址"` +} diff --git a/admin/apps/game/domain/cdkey.go b/admin/apps/game/domain/cdkey.go index ee9fb49..51325ab 100644 --- a/admin/apps/game/domain/cdkey.go +++ b/admin/apps/game/domain/cdkey.go @@ -3,7 +3,11 @@ package domain import ( "admin/apps/game/domain/entity" "admin/apps/game/domain/repo" + "admin/apps/game/model/dto" + "admin/internal/errcode" + "admin/lib/cdkey" "gorm.io/gorm" + "time" ) type CDKeyService struct { @@ -14,14 +18,60 @@ func NewCDKeyService(db *gorm.DB) *CDKeyService { return &CDKeyService{repo: repo.NewCDKeyRepo(db)} } -func (svc *CDKeyService) AddCount(id int, delta int) (int, error) { - et, err := svc.repo.AddCount(id, delta) +func (svc *CDKeyService) AddCount(projectId, id int, delta int) (int, error) { + et, err := svc.repo.AddCount(projectId, id, delta) if err != nil { return 0, err } return et.GetCount(), nil } -func (svc *CDKeyService) Get(id int) (*entity.CDKey, bool, error) { - return svc.repo.GetByID(id) +func (svc *CDKeyService) Get(projectId, id int) (*entity.CDKey, bool, error) { + return svc.repo.GetByID(projectId, id) +} + +func (svc *CDKeyService) CDKeyUse(params *dto.CDKeyUseReq) (*entity.CDKey, error) { + var cdkeyEt *entity.CDKey + var find bool + var err error + cdkeyInfo, ok := cdkey.DecodeCDKey(params.Key) + if !ok { + // 可能是通用码,通过数据库查询通用码 + cdkeyEt, find, err = svc.repo.GetByKey(params.ProjectId, params.Key) + } else { + cdkeyEt, find, err = svc.repo.GetByID(params.ProjectId, cdkeyInfo.Batch) + } + if err != nil { + return nil, err + } + if !find { + return nil, errcode.New(errcode.ParamsInvalid, "not found project:%v cdkey info by key:%v", params.ProjectId, params.Key) + } + + // 校验区服、时间 + if err = cdkeyEt.CheckCanUse(params.ServerID); err != nil { + return nil, err + } + + // 校验是否使用过 + usedInfo, find, err := svc.repo.GetUsedHistory(cdkeyEt, params.Key, params.ServerID, params.RoleID) + if err != nil { + return nil, err + } + if find { + // 只要使用过同一批的就没法再用 + return nil, errcode.New(errcode.CDKeyAlreadyUsed, "already used %v in %v", usedInfo.Key, usedInfo.CreatedAt.Format(time.DateTime)) + } + + err = svc.repo.RecordUse(cdkeyEt, params) + if err != nil { + return nil, err + } + + return cdkeyEt, nil +} + +func (svc *CDKeyService) GetUsedHistoryList(projectId int, cdKeyID int) ([]*dto.CDKeyUsedInfo, error) { + list, err := svc.repo.GetUsedHistoryList(projectId, cdKeyID) + return list, err } diff --git a/admin/apps/game/domain/entity/cdkey.go b/admin/apps/game/domain/entity/cdkey.go index 72a435c..53b8ea5 100644 --- a/admin/apps/game/domain/entity/cdkey.go +++ b/admin/apps/game/domain/entity/cdkey.go @@ -2,8 +2,11 @@ package entity import ( "admin/apps/game/model" + "admin/apps/game/model/dto" "admin/internal/consts" + "admin/internal/errcode" "admin/lib/cdkey" + "time" ) var MaxKeyNum = 100000 // 每个批次直接搞10w个,不然运营想补加码,算法又要一开始定好数量 @@ -40,3 +43,49 @@ func (c *CDKey) AddCount(delta int) { func (c *CDKey) GetCount() int { return c.Po.CodeNum } + +func (c *CDKey) CheckCanUse(roleServerId string) error { + if ok := c.checkServerId(roleServerId); !ok { + return errcode.New(errcode.CDKeyInvalid, "server_id %v not in %v", roleServerId, c.Po.ServerIDs) + } + timeNow := time.Now() + if c.Po.ValidStartTime.Valid { + if c.Po.ValidStartTime.Time.After(timeNow) { + return errcode.New(errcode.CDKeyAlreadyExpired, "not reach start time:%v", c.Po.ValidStartTime.Time.Format(time.DateTime)) + } + } + if c.Po.ValidEndTime.Valid { + if c.Po.ValidEndTime.Time.Before(timeNow) { + return errcode.New(errcode.CDKeyAlreadyExpired, "expired at:%v", c.Po.ValidEndTime.Time.Format(time.DateTime)) + } + } + return nil +} + +func (c *CDKey) checkServerId(serverId string) bool { + if len(c.Po.ServerIDs) == 0 { + return true + } + if len(c.Po.ServerIDs) == 1 && c.Po.ServerIDs[0] == "" { + return true + } + for _, id := range c.Po.ServerIDs { + if id == serverId { + return true + } + } + return false +} + +func (c *CDKey) RewardItemsView() []*dto.ItemInfo { + items := make([]*dto.ItemInfo, 0, len(c.Po.Attach)) + for _, v := range c.Po.Attach { + items = append(items, &dto.ItemInfo{ + ItemID: int(v.ID), + ItemNum: v.Num, + ItemType: v.ItemType, + Desc: v.Desc, + }) + } + return items +} diff --git a/admin/apps/game/domain/entity/utils.go b/admin/apps/game/domain/entity/utils.go index ffece39..743a90d 100644 --- a/admin/apps/game/domain/entity/utils.go +++ b/admin/apps/game/domain/entity/utils.go @@ -169,6 +169,16 @@ func parseStr2FieldValue(field reflect.StructField, rawValue any) (realSetValue } } } + i := 0 + for { + if i >= len(list) { + break + } + if list[i] == "" { + list = append(list[:i], list[i+1:]...) + } + i++ + } parsedValue = list } else if typeName == "[]*model.MailAttachItem" { items := make([]*model.MailAttachItem, 0) diff --git a/admin/apps/game/domain/projects/smdl/notice.go b/admin/apps/game/domain/projects/smdl/notice.go new file mode 100644 index 0000000..46c81ad --- /dev/null +++ b/admin/apps/game/domain/projects/smdl/notice.go @@ -0,0 +1,110 @@ +package smdl + +import ( + "admin/apps/game/domain/entity" + "admin/apps/game/model" + "admin/apps/game/model/dto" + "admin/internal/consts" + "admin/internal/errcode" + "admin/lib/httpclient" + "admin/lib/xlog" + "net/url" + "strconv" + "time" +) + +type NoticeHook struct { +} + +func (hook *NoticeHook) Create(projectInfo *entity.Project, resource string, dtoObj dto.CommonDtoValues) error { + alisrvAddr := projectInfo.GetApiAddr() + if alisrvAddr == "" { + return errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name) + } + + et := (&entity.CommonResource{}).FromPo(&model.Ban{}).FromDto(dtoObj) + banInfo := et.ToPo().(*model.Ban) + + banApi := "" + switch banInfo.BanType { + case consts.BanType_Role: + banApi = "banrole" + case consts.BanType_RoleChat: + banApi = "banroletalk" + default: + xlog.Warnf("神魔大陆不支持此类型的封禁:%v", banInfo.BanType) + return nil + } + + roleId := banInfo.Value + + params := &url.Values{} + params.Add("server", banInfo.ServerConfID) + params.Add("roleid", roleId) + + expireAt := banInfo.ExpireAt.Unix() + + if expireAt <= 0 { + // 解封 + params.Add("forbidtime", "0") + params.Add("desc", banInfo.BanReason) + params.Add("notifytouser", banInfo.BanNotifyReason) + } else { + dura := (expireAt - time.Now().Unix()) / 60 // 神魔大陆封禁是分钟 + if dura <= 0 { + // 解封 + params.Add("forbidtime", "0") + } else { + params.Add("forbidtime", strconv.FormatInt(dura, 10)) + params.Add("desc", banInfo.BanReason) + params.Add("notifytouser", banInfo.BanNotifyReason) + } + } + + rsp := make(map[string]any) + err := httpclient.Request(alisrvAddr+"/"+banApi, "get", params, &rsp) + if err != nil { + return err + } + + return nil +} + +func (hook *NoticeHook) Delete(projectInfo *entity.Project, resource string, dtoObj dto.CommonDtoValues) error { + alisrvAddr := projectInfo.GetApiAddr() + if alisrvAddr == "" { + return errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name) + } + + et := (&entity.CommonResource{}).FromPo(&model.Ban{}).FromDto(dtoObj) + banInfo := et.ToPo().(*model.Ban) + + banApi := "" + switch banInfo.BanType { + case consts.BanType_Role: + banApi = "banrole" + case consts.BanType_RoleChat: + banApi = "banroletalkdel" + default: + xlog.Warnf("神魔大陆不支持此类型的封禁:%v", banInfo.BanType) + return nil + } + + roleId := banInfo.Value + params := &url.Values{} + params.Add("server", banInfo.ServerConfID) + params.Add("roleid", roleId) + + // 解封 + params.Add("forbidtime", "-1") + params.Add("desc", "") + params.Add("notifytouser", "") + + rsp := make(map[string]any) + err := httpclient.Request(alisrvAddr+"/"+banApi, "get", params, &rsp) + if err != nil { + return err + } + + return nil +} diff --git a/admin/apps/game/domain/repo/cdkey.go b/admin/apps/game/domain/repo/cdkey.go index 20d7404..00e6a00 100644 --- a/admin/apps/game/domain/repo/cdkey.go +++ b/admin/apps/game/domain/repo/cdkey.go @@ -3,14 +3,20 @@ package repo import ( "admin/apps/game/domain/entity" "admin/apps/game/model" + "admin/apps/game/model/dto" "admin/internal/errcode" "errors" "gorm.io/gorm" + "time" ) type ICDKeyRepo interface { - GetByID(id int) (*entity.CDKey, bool, error) - AddCount(id int, delta int) (*entity.CDKey, error) + GetByID(projectId, id int) (*entity.CDKey, bool, error) + GetByKey(projectId int, key string) (*entity.CDKey, bool, error) + AddCount(projectId, id int, delta int) (*entity.CDKey, error) + GetUsedHistory(batchInfo *entity.CDKey, key string, serverId, roleId string) (*model.CDKeyUsed, bool, error) + RecordUse(batchInfo *entity.CDKey, params *dto.CDKeyUseReq) error + GetUsedHistoryList(projectId, id int) ([]*dto.CDKeyUsedInfo, error) } func NewCDKeyRepo(db *gorm.DB) ICDKeyRepo { @@ -21,24 +27,24 @@ type cdKeyRepoImpl struct { db *gorm.DB } -func (impl *cdKeyRepoImpl) AddCount(id int, delta int) (*entity.CDKey, error) { +func (impl *cdKeyRepoImpl) AddCount(projectId, id int, delta int) (*entity.CDKey, error) { po := new(model.CDKey) - err := impl.db.Where("id = ?", id).First(po).Error + err := impl.db.Where("project_id = ? and id = ?", projectId, id).First(po).Error if err != nil { return nil, errcode.New(errcode.ParamsInvalid, "not found cdkey conf:%v", id) } et := entity.NewCDKey(po) et.AddCount(delta) - err = impl.db.Where("id = ?", id).Updates(po).Error + err = impl.db.Where("project_id = ? and id = ?", projectId, id).Updates(po).Error if err != nil { return nil, errcode.New(errcode.DBError, "update data:%+v error:%v", po, id) } return et, nil } -func (impl *cdKeyRepoImpl) GetByID(id int) (*entity.CDKey, bool, error) { +func (impl *cdKeyRepoImpl) GetByID(projectId, id int) (*entity.CDKey, bool, error) { po := new(model.CDKey) - err := impl.db.Where("id = ?", id).First(po).Error + err := impl.db.Where("project_id = ? and id = ?", projectId, id).First(po).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, false, nil @@ -47,3 +53,71 @@ func (impl *cdKeyRepoImpl) GetByID(id int) (*entity.CDKey, bool, error) { } return entity.NewCDKey(po), true, nil } + +func (impl *cdKeyRepoImpl) GetByKey(projectId int, key string) (*entity.CDKey, bool, error) { + po := new(model.CDKey) + err := impl.db.Where("project_id = ? and code = ?", projectId, key).First(po).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, false, nil + } + return nil, false, errcode.New(errcode.ParamsInvalid, "not found cdkey conf:%v", key) + } + return entity.NewCDKey(po), true, nil +} + +func (impl *cdKeyRepoImpl) GetUsedHistory(batchInfo *entity.CDKey, key string, serverId, roleId string) (*model.CDKeyUsed, bool, error) { + po := new(model.CDKeyUsed) + // 获取同一批次角色的使用情况,同一批只能使用一次 + err := impl.db.Where("project_id = ? and cd_key_id = ? and server_id = ? and role_id = ?", + batchInfo.Po.ProjectId, batchInfo.Po.ID, serverId, roleId).First(po).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, false, nil + } + return nil, false, errcode.New(errcode.ParamsInvalid, "found cdkey use:%v,%v,%v,%v error:%v", + batchInfo.Po.ProjectId, serverId, roleId, key, err) + } + return po, true, nil +} + +func (impl *cdKeyRepoImpl) RecordUse(batchInfo *entity.CDKey, params *dto.CDKeyUseReq) error { + po := &model.CDKeyUsed{ + ProjectId: batchInfo.Po.ProjectId, + CDKeyId: batchInfo.Po.ID, + ServerID: params.ServerID, + Account: params.Account, + RoleID: params.RoleID, + RoleName: params.RoleName, + Key: params.Key, + IP: params.IP, + DeviceId: params.DeviceID, + } + err := impl.db.Save(po).Error + if err != nil { + return errcode.New(errcode.CDKeyInvalid, "save db(%+v) error:%v", po, err) + } + return nil +} + +func (impl *cdKeyRepoImpl) GetUsedHistoryList(projectId, id int) ([]*dto.CDKeyUsedInfo, error) { + list := make([]*model.CDKeyUsed, 0) + err := impl.db.Where("project_id = ? and cd_key_id = ?", projectId, id).Find(&list).Error + if err != nil { + return nil, errcode.New(errcode.DBError, "GetUsedHistoryList %v %v error:%v", projectId, id, err) + } + retList := make([]*dto.CDKeyUsedInfo, 0, len(list)) + for _, v := range list { + retList = append(retList, &dto.CDKeyUsedInfo{ + ServerID: v.ServerID, + Account: v.Account, + RoleID: v.RoleID, + RoleName: v.RoleName, + Key: v.Key, + IP: v.IP, + DeviceID: v.DeviceId, + CreatedAt: v.CreatedAt.Format(time.DateTime), + }) + } + return retList, nil +} diff --git a/admin/apps/game/model/cdkey.go b/admin/apps/game/model/cdkey.go index a4a7488..190d280 100644 --- a/admin/apps/game/model/cdkey.go +++ b/admin/apps/game/model/cdkey.go @@ -15,10 +15,10 @@ func init() { type CDKey struct { ID int `gorm:"primarykey" readonly:"true"` ProjectId int `gorm:"index:idx_project_id"` - Name string `name:"礼包说明" required:"true" uneditable:"true"` + Name string `gorm:"type:varchar(100);unique" name:"礼包说明" required:"true" uneditable:"true"` ServerIDs []string `gorm:"type:json;serializer:json" name:"区服" desc:"不选就是全服通用" type:"[]string" choices:"GetChoiceServers" multi_choice:"true" uneditable:"true""` CodeType int `name:"礼包类型" required:"true" choices:"GetCodeTypeChoices" uneditable:"true"` - Code string `name:"礼包码" desc:"一码通用才配置"` + Code string `gorm:"type:VARCHAR(50);index" name:"礼包码" desc:"一码通用才配置"` CodeNum int `name:"礼包数量" desc:"一码一用才配置"` ValidStartTime sql.NullTime `name:"生效起始时间"` ValidEndTime sql.NullTime `name:"生效结束时间"` diff --git a/admin/apps/game/model/cdkey_used.go b/admin/apps/game/model/cdkey_used.go index 6434421..7835446 100644 --- a/admin/apps/game/model/cdkey_used.go +++ b/admin/apps/game/model/cdkey_used.go @@ -11,12 +11,15 @@ func init() { type CDKeyUsed struct { ID int `gorm:"primarykey" readonly:"true"` - ProjectId int `gorm:"index:idx_project_id"` - ServerID string `name:"所属区服" choices:"GetChoiceServers" required:"true" where:"eq"` - Account string `gorm:"type:varchar(255);index:idx_account"` - RoleID string `gorm:"type:varchar(255);index:idx_role_id"` + ProjectId int `gorm:"index:idx_project_id;uniqueIndex:one_time_used"` + CDKeyId int `gorm:"index:idx_cdkeyid;uniqueIndex:one_time_used"` + ServerID string `gorm:"type:varchar(30);uniqueIndex:one_time_used" name:"所属区服" choices:"GetChoiceServers" required:"true" where:"eq"` + Account string `gorm:"type:varchar(128);index:idx_account"` + RoleID string `gorm:"type:varchar(128);index:idx_role_id;uniqueIndex:one_time_used"` RoleName string `gorm:"type:varchar(255)"` - Key string `gorm:"type:varchar(255);index:idx_key"` + Key string `gorm:"type:varchar(50);index:idx_key;"` + IP string `gorm:"type:varchar(20)"` + DeviceId string `gorm:"type:varchar(50)"` CreatedAt time.Time `readonly:"true"` } diff --git a/admin/apps/game/model/dto/common.go b/admin/apps/game/model/dto/common.go index 088f177..1a01937 100644 --- a/admin/apps/game/model/dto/common.go +++ b/admin/apps/game/model/dto/common.go @@ -1,9 +1,10 @@ package dto type WebRspData struct { - Code int `json:"code"` - Msg string `json:"msg"` - Data any `json:"data"` + Code int `json:"code"` + Msg string `json:"msg"` + DetailMsg string `json:"detail_msg"` + Data any `json:"data"` } type CommonDtoFieldChoice struct { @@ -51,3 +52,10 @@ type GetWhereCondition struct { Value1 any `json:"value1"` Value2 any `json:"value2"` } + +type ItemInfo struct { + ItemID int `json:"item_id"` + ItemNum int64 `json:"item_num"` + ItemType int `json:"item_type"` + Desc string `json:"desc"` +} diff --git a/admin/apps/game/model/dto/msg.go b/admin/apps/game/model/dto/msg.go index 371b328..6103555 100644 --- a/admin/apps/game/model/dto/msg.go +++ b/admin/apps/game/model/dto/msg.go @@ -68,3 +68,37 @@ type CDKeyAddCountRsp struct { ID int `json:"id"` NewCount int `json:"new_count"` } + +type CDKeyUseReq struct { + ProjectId int `json:"project_id"` + Key string `json:"key"` + Account string `json:"account"` + RoleID string `json:"role_id"` + RoleName string `json:"role_name"` + IP string `json:"ip"` + ServerID string `json:"server_id"` + DeviceID string `json:"device_id"` +} + +type CDKeyUseRsp struct { + RewardItems []*ItemInfo `json:"reward_items"` +} + +type CDKeyUsedHistoryReq struct { + ID int `json:"id"` +} + +type CDKeyUsedInfo struct { + ServerID string `json:"server_id"` + Account string `json:"account"` + RoleID string `json:"role_id"` + RoleName string `json:"role_name"` + Key string `json:"key"` + IP string `json:"ip"` + DeviceID string `json:"device_id"` + CreatedAt string `json:"created_at"` +} + +type CDKeyUsedHistoryRsp struct { + List []*CDKeyUsedInfo `json:"list"` +} diff --git a/admin/apps/game/server/ctl_cdkey.go b/admin/apps/game/server/ctl_cdkey.go index ffe8f67..eb0f7fb 100644 --- a/admin/apps/game/server/ctl_cdkey.go +++ b/admin/apps/game/server/ctl_cdkey.go @@ -5,17 +5,27 @@ import ( "admin/internal/context" "bytes" "fmt" + "strings" ) func (ctl *controller) CDKeyExportFile(ctx *context.WebContext, params *dto.CDKeyExportFileReq, rsp *dto.NilRsp) error { - et, keys, err := ctl.svc.GetCDKeyAllKeys(params.ID) + projectId := getCtxURIProjectId(ctx) + + et, keys, err := ctl.svc.GetCDKeyAllKeys(projectId, params.ID) if err != nil { return err } content := bytes.NewBuffer(nil) content.WriteString(fmt.Sprintf("礼包码描述:%s\n", et.GetName())) - content.WriteString(fmt.Sprintf("礼包码数量:%v\n\n", et.GetCount())) + content.WriteString(fmt.Sprintf("礼包码数量:%v\n", et.GetCount())) + var itemStr = make([]string, 0, len(et.Po.Attach)) + for _, item := range et.Po.Attach { + itemStr = append(itemStr, fmt.Sprintf("%v*%v", item.Desc, item.Num)) + } + content.WriteString(fmt.Sprintf("礼包码道具:%v\n", strings.Join(itemStr, ","))) + + content.WriteString("\n") content.WriteString(fmt.Sprintf("礼包码列表:\n")) for _, key := range keys { content.WriteString(fmt.Sprintf("%s\n", key)) @@ -27,10 +37,30 @@ func (ctl *controller) CDKeyExportFile(ctx *context.WebContext, params *dto.CDKe } func (ctl *controller) CDKeyAddCount(ctx *context.WebContext, params *dto.CDKeyAddCountReq, rsp *dto.CDKeyAddCountRsp) error { - newCount, err := ctl.svc.CDKeyAddCount(params.ID, params.AddCount) + projectId := getCtxURIProjectId(ctx) + newCount, err := ctl.svc.CDKeyAddCount(projectId, params.ID, params.AddCount) if err != nil { return err } rsp.NewCount = newCount return nil } + +func (ctl *controller) CDKeyUse(ctx *context.WebContext, params *dto.CDKeyUseReq, rsp *dto.CDKeyUseRsp) error { + batchInfo, err := ctl.svc.CDKeyUse(params) + if err != nil { + return err + } + rsp.RewardItems = batchInfo.RewardItemsView() + return nil +} + +func (ctl *controller) CDKeyUsedHistory(ctx *context.WebContext, params *dto.CDKeyUsedHistoryReq, rsp *dto.CDKeyUsedHistoryRsp) error { + projectId := getCtxURIProjectId(ctx) + list, err := ctl.svc.CDKeyUsedHistoryList(projectId, params.ID) + if err != nil { + return err + } + rsp.List = list + return nil +} diff --git a/admin/apps/game/server/route.go b/admin/apps/game/server/route.go index 4c8af4f..a31ec9d 100644 --- a/admin/apps/game/server/route.go +++ b/admin/apps/game/server/route.go @@ -5,35 +5,45 @@ import ( "admin/lib/web" ) -func (srv *Server) Route(engine *web.Engine) { - engine.Use(srv.CheckToken) +func (srv *Server) Route(engine *web.Engine, sdkEngine *web.Engine) { + { // gm后台内部路由 + engine.Use(srv.CheckToken) - apiGroup := engine.Group("/api", "") - - { - // 注册项目增删改查接口 - projectGroup := apiGroup.Group("/"+consts.ResourcesName_Project, "项目") - projectGroup.Get("", "查看列表", consts.WebPathPermit_Read, srv.ctl.CommonList) - projectGroup.Post("", "新增", consts.WebPathPermit_Read, srv.ctl.CommonPost) - projectGroup.Put("", "编辑", consts.WebPathPermit_Read, srv.ctl.CommonPut) - projectGroup.Delete("", "删除", consts.WebPathPermit_Read, srv.ctl.CommonDelete) + apiGroup := engine.Group("/api", "") - // 注册项目之下其它所有资源通用增删改查接口 { - resourceUnderProjectGroup := projectGroup.Group("/:projectId/:resource", "") - resourceUnderProjectGroup.Get("", "查看列表", consts.WebPathPermit_Read, srv.ctl.CommonList) - resourceUnderProjectGroup.Post("", "新增", consts.WebPathPermit_Read, srv.ctl.CommonPost) - resourceUnderProjectGroup.Put("", "编辑", consts.WebPathPermit_Read, srv.ctl.CommonPut) - resourceUnderProjectGroup.Delete("", "删除", consts.WebPathPermit_Read, srv.ctl.CommonDelete) + // 注册项目增删改查接口 + projectGroup := apiGroup.Group("/"+consts.ResourcesName_Project, "项目") + projectGroup.Get("", "查看列表", consts.WebPathPermit_Read, srv.ctl.CommonList) + projectGroup.Post("", "新增", consts.WebPathPermit_Read, srv.ctl.CommonPost) + projectGroup.Put("", "编辑", consts.WebPathPermit_Read, srv.ctl.CommonPut) + projectGroup.Delete("", "删除", consts.WebPathPermit_Read, srv.ctl.CommonDelete) + + // 注册项目之下其它所有资源通用增删改查接口 + { + resourceUnderProjectGroup := projectGroup.Group("/:projectId/:resource", "") + resourceUnderProjectGroup.Get("", "查看列表", consts.WebPathPermit_Read, srv.ctl.CommonList) + resourceUnderProjectGroup.Post("", "新增", consts.WebPathPermit_Read, srv.ctl.CommonPost) + resourceUnderProjectGroup.Put("", "编辑", consts.WebPathPermit_Read, srv.ctl.CommonPut) + resourceUnderProjectGroup.Delete("", "删除", consts.WebPathPermit_Read, srv.ctl.CommonDelete) + } + + projectGroup.Get("/:projectId/items", "获取项目所有道具列表", consts.WebPathPermit_Read, srv.ctl.GetProjectAllItems) + + { + // 礼包码特殊接口 + cdkeyGroup := projectGroup.Group("/:projectId/cdkey/special", "") + cdkeyGroup.Get("/add_count", "礼包码数量追加", consts.WebPathPermit_Write, srv.ctl.CDKeyAddCount) + cdkeyGroup.Get("/export", "导出礼包码文件", consts.WebPathPermit_Write, srv.ctl.CDKeyExportFile) + cdkeyGroup.Get("/used", "查看礼包码使用情况", consts.WebPathPermit_Write, srv.ctl.CDKeyUsedHistory) + } } + } - projectGroup.Get("/:projectId/items", "获取项目所有道具列表", consts.WebPathPermit_Read, srv.ctl.GetProjectAllItems) - + { // gm后台作为sdk,供内部其它游戏调用的路由 + sdkGroup := sdkEngine.Group("/api", "") { - // 礼包码特殊接口 - cdkeyGroup := projectGroup.Group("/:projectId/cdkey/special", "") - cdkeyGroup.Get("/add_count", "礼包码数量追加", consts.WebPathPermit_Write, srv.ctl.CDKeyAddCount) - cdkeyGroup.Get("/export", "导出礼包码文件", consts.WebPathPermit_Write, srv.ctl.CDKeyExportFile) + sdkGroup.Get("/cdkey/use", "使用奖励码", consts.WebPathPermit_Write, srv.ctl.CDKeyUse) } } } diff --git a/admin/apps/game/service/service_cdkey.go b/admin/apps/game/service/service_cdkey.go index 87014c9..629ca4a 100644 --- a/admin/apps/game/service/service_cdkey.go +++ b/admin/apps/game/service/service_cdkey.go @@ -2,11 +2,12 @@ package service import ( "admin/apps/game/domain/entity" + "admin/apps/game/model/dto" "admin/internal/errcode" ) -func (svc *Service) GetCDKeyAllKeys(id int) (*entity.CDKey, []string, error) { - et, find, err := svc.cdkeySvc.Get(id) +func (svc *Service) GetCDKeyAllKeys(projectId int, id int) (*entity.CDKey, []string, error) { + et, find, err := svc.cdkeySvc.Get(projectId, id) if err != nil { return nil, nil, err } @@ -16,6 +17,18 @@ func (svc *Service) GetCDKeyAllKeys(id int) (*entity.CDKey, []string, error) { return et, et.GenerateKeys(), nil } -func (svc *Service) CDKeyAddCount(id int, deltaCount int) (int, error) { - return svc.cdkeySvc.AddCount(id, deltaCount) +func (svc *Service) CDKeyAddCount(projectId int, id int, deltaCount int) (int, error) { + return svc.cdkeySvc.AddCount(projectId, id, deltaCount) +} + +func (svc *Service) CDKeyUse(params *dto.CDKeyUseReq) (*entity.CDKey, error) { + batchInfo, err := svc.cdkeySvc.CDKeyUse(params) + if err != nil { + return nil, err + } + return batchInfo, nil +} + +func (svc *Service) CDKeyUsedHistoryList(projectId, id int) ([]*dto.CDKeyUsedInfo, error) { + return svc.cdkeySvc.GetUsedHistoryList(projectId, id) } diff --git a/admin/internal/errcode/code.go b/admin/internal/errcode/code.go index ad91540..c9e68f2 100644 --- a/admin/internal/errcode/code.go +++ b/admin/internal/errcode/code.go @@ -11,3 +11,12 @@ const ( NoPermission = 8 // 没有权限 ParamsInvalid = 9 ) + +// sdk系统错误码,给内部别的游戏调用api返回 +// 错误码格式是系统号(100-999)*100 + 100 + 错误码自增 +const ( + CDKey_ = 100 // cdkey系统占用100 + CDKeyInvalid = 100100 // cdkey无效 + CDKeyAlreadyUsed = 100101 // cdkey已经使用过 + CDKeyAlreadyExpired = 100102 // cdkey过期 +) diff --git a/admin/lib/node/app.go b/admin/lib/node/app.go index 33d0f54..af6b14c 100644 --- a/admin/lib/node/app.go +++ b/admin/lib/node/app.go @@ -1,6 +1,7 @@ package node import ( + "admin/lib/web" "admin/lib/xlog" "fmt" "github.com/gin-gonic/gin" @@ -51,8 +52,8 @@ func (app *Application) WithInitializeTask(desc string, task Task) *Application } // WithServer 添加web服务器 -func (app *Application) WithServer(desc string, addr string) *gin.Engine { - server := gin.Default() +func (app *Application) WithServer(desc string, addr string, newContextFun func(ctx *gin.Context) web.IContext) *web.Engine { + server := web.New(newContextFun) app.servers = append(app.servers, pair{desc, pair{addr, server}}) return server } @@ -144,7 +145,7 @@ func (app *Application) run() (err error) { for _, server := range app.servers { go func(desc string, info pair) { addr := info.key.(string) - engine := info.value.(*gin.Engine) + engine := info.value.(*web.Engine) xlog.Noticef("app %v server %v will listen on %v", app.Name, desc, addr) err := engine.Run(addr) if err != nil { diff --git a/admin/todo.md b/admin/todo.md new file mode 100644 index 0000000..e69de29 diff --git a/admin/uniugm b/admin/uniugm deleted file mode 100644 index 1ac481a1877e1ae86becb91d3354ee4663413fb0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 94208 zcmeI*O>EoN0S9o>vL(y0BD=1`1{q+s4G_!FpIRU>J58Fbo5lV!iafAsDdBPP=Rfu;X^wI^;SG*kPAF^3l|X<$U-f z5wgD`OXTAt-+RCJMOtFKd}~p&RDRv4S7eKiGb0SkGT-ERhGAYKKOFfve2kKX{=*gW z&bBNcxA+SZyKfGYcw{>;LRO|N7_`*_pzL4e%^WR!b z^Cdf|#7~yAa*5Zfmb#(V`S+GG+4-e4{$_fOpI<5DGdZ$PHk~U(oan`>Tv1E>u3T3( z<@#hSkq{!Ks;1oOy1`DMSd&e&a}RrGS+U60ZpSk%+1erdkUULF0)IXI&iu+^ffpks zrLM}BS}w{~i7%5SORK1ng?w(gur!~^6-be)TG2_>d7%ei$z^V>q)9oOVM6zvS_J z8F$pG^D@?6RZiG5)Rh~*Sx+;ho}BWxRCc7K?$xw9DMKsg(;+(v6y0PzEV?cT!kC|X zn>!AQ?khQOuQswH0d8V~J(DO;D9 zzT0%YvO9?gf;8&q(i6uOy}7sKExtm=h(UXtsDuODwF$QIy3?Mt@?Pt0XiQFH-ppN8a1P zTL`zyd*RO@-;(m-5EChphQCX?zG76@?JPA%6Hz3s^e}&3uoRmm!DZD{O1!1+S=}p2 zXhcE*?(${!Anp{0CPH5?wikp3o^L6hoZZ!7s{cJFyA@f}i*-X+y@gPFroHgf=G#t$ zuzRiEtqr>(sIH}1IvL|#YJ*G+q_w!KvSrE2=Hohy&`>TobI(1&DVUv{{WIKN96QJ^ zN!JGAX3h%1>1avZ$m{w6Y zHneKdo>N+87k4=92o@URP=I^$P4?g#Ev5RATrZO*T~_PO#S0x}Zf-ekC2rJ>omxwo z?1WoayUde9V>%e%u3TXczTsrqm3QBU+Oz70o*A=k8` zTrQK75W20~^e!|qgS1~)oNQMOOH)+(8f?!r4Lm`f+vY^CKX^Taw6xYfKw4Mb>XQDl#SV~v)+loQ_b`MIencFk!x?zyL_Zttj7MeSJa zZv*exIB5@*5wfSy_;00bZa0SG_<0uX=z1R&59u&@99;omahe}x}~{}z52{sURT0|F3$ z00bZa0SG_<0uX=z1Rwx`-Vhk_`!2Kewt&9j0pCQ>yPa>S-*+YG-Z(Mn^Kn7vw%9;sF5&KmY;|fB*y_009U<00IzzKraf+vjJv284BgiN?HHsXTN;(#n1k6|JRRx z{_)>`^We*$e(~imejF2HiLZ*}8Rx~+Tr4#wrJ~7rQc8%aDRFjA6!H0gFIFt_1OW&@ z00Izz00bZa0SG_<0uVS`0bKu|ttXBK0SG_<0uX=z1Rwwb2tWV=5a>k#`u+b0O!z@B z<_>v+00bZa0SG_<0uX=z1Rwwb2>jm*EU-Z)IKhow9?gIFMobh%>5cC?Z|mi9d7b>* z_>io--~XTHW71q)oSUABre+e!L?Yh&{(oQiS4{X%IS%BXE|Bwkke9j3YIS4=i0uX=z1Rwwb2tWV=5P$##P9reI4lo>(m&+Bc z>V5y;`TKuTlKct4sFX^^#kl+T|Lo8I+3_}onH{p&;O^G@YKsyK$Hdo5P$##AOHafKmY;|fB*y_a6*B`5*uI+CW86Ax_@Ky z_O}%+uieP3+|5WiEn{Y?O9^EmGn2Vf`(Sn9Mk-1oNlI*WKeJ_M*<});qTf~aNrdW> zuEcL?iwie&_1$?bzm<;Xt}9aZZh9|Q$fcr2ZK { for (let i = 0; i < fieldsDescInfo.value.length; i++) { var field = fieldsDescInfo.value[i] - dialogAddForm.value[field.key] = '' + dialogObjectForm.value[field.key] = '' if (field.required == true) { rules.value[field.key] = [{required: true, message: field.name + "不能为空", trigger: "blur"}] } if (field.type == "items") { - dialogAddForm.value[field.key] = [] + dialogObjectForm.value[field.key] = [] for (let j = 0; j < rows.value.length; j++) { rows.value[j].jsonValue = JSON.stringify(rows.value[j][field.key]) } @@ -128,22 +128,22 @@ onMounted(() => { const dialogAddVisible = ref(false) const dialogLookVisible = ref(false) const dialogEditVisible = ref(false) +const dialogUsedHistoryVisible = ref(false) const dialogAddFormRef = ref(null) const dialogEditFormRef = ref(null) -const dialogAddForm = ref({ +const dialogObjectForm = ref({ ServerIDs: [], Attach: [], }) -const dialogEditForm = ref({}) const submitAdd = async () => { try { await dialogAddFormRef.value.validate(valid => { if (valid) { - console.log("commit add form:", dialogAddForm.value) - resourcePost(resource_url, dialogAddForm.value).then((res) => { + console.log("commit add form:", dialogObjectForm.value) + resourcePost(resource_url, dialogObjectForm.value).then((res) => { ElNotification({ title: "添加结果通知", message: "添加成功!", @@ -157,7 +157,7 @@ const submitAdd = async () => { }, (err) => { console.log("添加报错:", err) }) - console.log("提交数据:", dialogAddForm.value) + console.log("提交数据:", dialogObjectForm.value) } }) } catch (error) { @@ -169,7 +169,11 @@ const submitEdit = async () => { try { await dialogEditFormRef.value.validate(valid => { if (valid) { - resourcePut(resource_url, dialogEditForm.value).then((res) => { + const oldIndex = dialogObjectForm.value.oldIndex + const oldData = dialogObjectForm.value.oldData + delete dialogObjectForm.value.oldIndex + delete dialogObjectForm.value.oldData + resourcePut(resource_url, dialogObjectForm.value).then((res) => { ElNotification({ title: "编辑结果通知", message: "编辑成功!", @@ -178,12 +182,12 @@ const submitEdit = async () => { "show-close": true, }) dialogEditVisible.value = false - rows.value[dialogEditForm.value.oldIndex] = res.data.dto + rows.value[oldIndex] = res.data.dto handleCloseDialog() }, (err) => { console.log("添加报错:", err) }) - console.log("提交数据:", dialogEditForm.value) + console.log("提交数据:", dialogObjectForm.value) } }) } catch (error) { @@ -220,23 +224,23 @@ const handleExport = (index, row) => { } const handleLook = (index, row) => { - dialogEditForm.value.oldData = row - dialogEditForm.value.oldIndex = index - dialogEditForm.value = row + dialogObjectForm.value = row + dialogObjectForm.value.oldData = row + dialogObjectForm.value.oldIndex = index console.log("look data:", row) dialogLookVisible.value = true } const handleEdit = (index, row) => { - dialogEditForm.value.oldData = row - dialogEditForm.value.oldIndex = index - dialogEditForm.value = row + dialogObjectForm.value = row + dialogObjectForm.value.oldData = row + dialogObjectForm.value.oldIndex = index console.log("edit data:", row) dialogEditVisible.value = true } const handleDelete = (index, row) => { - ElMessageBox.confirm("确定要删除吗?").then(() => { + ElMessageBox.confirm("请确认礼包码不会使用了,删除后发放的礼包码都会作废!!!!!确定要删除吗?").then(() => { resourceDelete(resource_url, {id: row.ID}).then((res) => { ElNotification({ title: "删除结果通知", @@ -268,16 +272,16 @@ function addItem() { console.log("add item:", d) - if (typeof dialogAddForm.value.Attach === typeof "") { - dialogAddForm.value.Attach = []; + if (typeof dialogObjectForm.value.Attach === typeof "") { + dialogObjectForm.value.Attach = []; } - dialogAddForm.value.Attach.push(d); + dialogObjectForm.value.Attach.push(d); } function deleteItem(row) { // 移除该对象 - let number = form.value.Attach.findIndex(item => item === row); - dialogAddForm.value.Attach.splice(number, 1); + let number = dialogObjectForm.value.Attach.findIndex(item => item === row); + dialogObjectForm.value.Attach.splice(number, 1); } const handleCloseDialog = () => { @@ -285,10 +289,11 @@ const handleCloseDialog = () => { dialogAddVisible.value = false dialogLookVisible.value = false dialogEditVisible.value = false - dialogAddForm.value = { + dialogUsedHistoryVisible.value = false + dialogObjectForm.value = { Attach: [], } - dialogEditForm.value = {} + item.value.desc = '' } const loadingRemoteItems = ref(false) @@ -325,6 +330,18 @@ const resetConditionSearch = () => { } } +const cdkeyUsedHistoryList = ref([]) + +const handleGetUsedHistory = (index, row) => { + cdkeyUsed(resource_url, {id: row.ID}).then((res) => { + cdkeyUsedHistoryList.value = res.data.list + dialogObjectForm.value = row + dialogUsedHistoryVisible.value = true + }, (err) => { + + }) +} + @@ -422,7 +443,7 @@ const resetConditionSearch = () => { -