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 1ac481a..0000000
Binary files a/admin/uniugm and /dev/null differ
diff --git a/ui/README.md b/ui/README.md
index 1a66c21..f28622e 100644
--- a/ui/README.md
+++ b/ui/README.md
@@ -1,5 +1,5 @@
# ui
-npm install --registry=https://registry.npmmirror.com
-npm run dev
-npm run build
+npm install --registry=https://registry.npmmirror.com
+npm run dev
+npm run build
diff --git a/ui/src/api/cdkey.js b/ui/src/api/cdkey.js
index 8c8edd4..ae161e1 100644
--- a/ui/src/api/cdkey.js
+++ b/ui/src/api/cdkey.js
@@ -8,4 +8,12 @@ export function cdkeyExport(baseUrl, params) {
params: params,
responseType: 'blob',
})
+}
+
+export function cdkeyUsed(baseUrl, params) {
+ return request({
+ url: baseUrl + '/special/used',
+ method: 'get',
+ params: params,
+ })
}
\ No newline at end of file
diff --git a/ui/src/components/restful/tableCDKey.vue b/ui/src/components/restful/tableCDKey.vue
index 3ae935f..167da86 100644
--- a/ui/src/components/restful/tableCDKey.vue
+++ b/ui/src/components/restful/tableCDKey.vue
@@ -6,7 +6,7 @@ import {useRoute} from 'vue-router';
import LocalCache from "@/stores/localCache.js";
import empty from '@/components/restful/empty.vue';
import {getWhereConditionDesc} from "@/utils/string.js";
-import {cdkeyExport} from "@/api/cdkey.js";
+import {cdkeyExport, cdkeyUsed} from "@/api/cdkey.js";
const cachedResource = LocalCache.getCache("resource");
@@ -64,14 +64,14 @@ const listData = async () => {
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) => {
+
+ })
+}
+
@@ -400,9 +417,13 @@ const resetConditionSearch = () => {
v-if="(resource_raw_node.meta.methods.put === true)">
修改
+
+ 礼包使用
+
- 调试用
+ 删除
@@ -422,7 +443,7 @@ const resetConditionSearch = () => {
-
@@ -450,7 +471,7 @@ const resetConditionSearch = () => {
-
+
@@ -469,7 +490,7 @@ const resetConditionSearch = () => {
@@ -481,7 +502,7 @@ const resetConditionSearch = () => {
-
@@ -490,7 +511,7 @@ const resetConditionSearch = () => {
-
@@ -507,14 +528,14 @@ const resetConditionSearch = () => {
-
-
+
@@ -529,7 +550,7 @@ const resetConditionSearch = () => {
@@ -541,9 +562,9 @@ const resetConditionSearch = () => {
-
@@ -551,9 +572,8 @@ const resetConditionSearch = () => {
-
+
@@ -571,7 +591,7 @@ const resetConditionSearch = () => {
-
@@ -581,10 +601,15 @@ const resetConditionSearch = () => {
label-width="130px">
-
+
+ :value="info">
@@ -596,7 +621,7 @@ const resetConditionSearch = () => {
-
+
@@ -616,7 +641,7 @@ const resetConditionSearch = () => {
@@ -628,7 +653,7 @@ const resetConditionSearch = () => {
-
@@ -637,7 +662,7 @@ const resetConditionSearch = () => {
-
@@ -645,7 +670,7 @@ const resetConditionSearch = () => {
-
@@ -663,6 +688,33 @@ const resetConditionSearch = () => {
+
+
+
+ {{ dialogObjectForm.Name }}-{{ dialogObjectForm.Code }}:使用{{ cdkeyUsedHistoryList.length }}个
+
+
+ {{ dialogObjectForm.Name }}-{{ dialogObjectForm.CodeNum }}个:使用{{
+ cdkeyUsedHistoryList.length
+ }}个
+
+
+
+
+
+
+
+
+
+
+
+
+
+