optimize and add cdkey
This commit is contained in:
parent
a2897f288e
commit
bc368e96cf
@ -27,7 +27,7 @@ func initCommonResourcesRepo(db *gorm.DB) {
|
||||
r(consts.ResourcesName_MailRole, "个人邮件", repo.NewCommonResourceRepo(db, &model.RoleMail{}), ShowMethod_Get|ShowMethod_Post) // 个人邮件发放就没法撤回?
|
||||
r(consts.ResourcesName_MailGlobal, "全服邮件", repo.NewCommonResourceRepo(db, &model.GlobalMail{}), ShowMethod_Get|ShowMethod_Post) // 直接删除,别修改了,玩家收到的更乱
|
||||
r(consts.ResourcesName_Notice, "公告", repo.NewCommonResourceRepo(db, &model.Notice{}), ShowMethod_Get|ShowMethod_Post|ShowMethod_Put|ShowMethod_Delete)
|
||||
r(consts.ResourcesName_RewardCode, "奖励码", repo.NewCommonResourceRepo(db, &model.RewardCode{}), ShowMethod_Get)
|
||||
r(consts.ResourcesName_RewardCode, "奖励码", repo.NewCommonResourceRepo(db, &model.CDKey{}), ShowMethod_Get)
|
||||
r(consts.ResourcesName_DevicePush, "设备推送", repo.NewCommonResourceRepo(db, &model.DevicePush{}), ShowMethod_Get)
|
||||
}
|
||||
|
||||
|
22
admin/apps/game/domain/entity/cdkey.go
Normal file
22
admin/apps/game/domain/entity/cdkey.go
Normal file
@ -0,0 +1,22 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"admin/apps/game/model"
|
||||
"admin/lib/cdkey"
|
||||
)
|
||||
|
||||
var MaxKeyNum = 100000 // 每个批次直接搞10w个,不然运营想补加码,算法又要一开始定好数量
|
||||
|
||||
type CDKey struct {
|
||||
Po *model.CDKey
|
||||
}
|
||||
|
||||
func NewCDKey(po *model.CDKey) *CDKey {
|
||||
return &CDKey{
|
||||
Po: po,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CDKey) GenerateKeys() []string {
|
||||
return cdkey.GenerateAll(c.Po.ID, MaxKeyNum)[:c.Po.CodeNum]
|
||||
}
|
@ -16,6 +16,7 @@ var projectsResourceHookMgr = map[string]map[string]any{
|
||||
consts.ResourcesName_Ban: &smdl.BanHook{}, // 所有角色走神魔大陆api直接获取
|
||||
consts.ResourcesName_MailGlobal: &smdl.MailGlobalHook{}, // 所有角色走神魔大陆api直接获取
|
||||
consts.ResourcesName_MailRole: &smdl.MailRoleHook{}, // 所有角色走神魔大陆api直接获取
|
||||
consts.ResourcesName_WhiteList: &smdl.WhitelistHook{}, // 所有角色走神魔大陆api直接获取
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -11,12 +11,12 @@ type Items struct {
|
||||
}
|
||||
|
||||
func (items *Items) GetItems(projectInfo *entity.Project) ([]*dto.CommonDtoFieldChoice, error) {
|
||||
return []*dto.CommonDtoFieldChoice{
|
||||
{Desc: "黄金战甲", Value: 123, Type: 1},
|
||||
{Desc: "黄金头盔", Value: 234, Type: 1},
|
||||
{Desc: "黄金大刀", Value: 346, Type: 1},
|
||||
{Desc: "白银大刀", Value: 346, Type: 1},
|
||||
}, nil
|
||||
//return []*dto.CommonDtoFieldChoice{
|
||||
// {Desc: "黄金战甲", Value: 123, Type: 1},
|
||||
// {Desc: "黄金头盔", Value: 234, Type: 1},
|
||||
// {Desc: "黄金大刀", Value: 346, Type: 1},
|
||||
// {Desc: "白银大刀", Value: 346, Type: 1},
|
||||
//}, nil
|
||||
alisrvAddr := projectInfo.GetApiAddr()
|
||||
if alisrvAddr == "" {
|
||||
return nil, errcode.New(errcode.ServerError, "项目%v没有配置api服务器地址", projectInfo.Po.Name)
|
||||
|
63
admin/apps/game/domain/projects/smdl/whitelist.go
Normal file
63
admin/apps/game/domain/projects/smdl/whitelist.go
Normal file
@ -0,0 +1,63 @@
|
||||
package smdl
|
||||
|
||||
import (
|
||||
"admin/apps/game/domain/entity"
|
||||
"admin/apps/game/model"
|
||||
"admin/apps/game/model/dto"
|
||||
"admin/internal/errcode"
|
||||
"admin/lib/httpclient"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type WhitelistHook struct {
|
||||
}
|
||||
|
||||
func (hook *WhitelistHook) 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.RoleMail{}).FromDto(dtoObj)
|
||||
whiteInfo := et.ToPo().(*model.WhiteList)
|
||||
|
||||
params := &url.Values{}
|
||||
params.Add("cmd_data", "OpWhitelist")
|
||||
params.Add("type", whiteInfo.WType)
|
||||
params.Add("value", whiteInfo.Value)
|
||||
params.Add("op", "add")
|
||||
params.Add("server", whiteInfo.ServerConfID)
|
||||
|
||||
rsp := make(map[string]any)
|
||||
err := httpclient.Request(alisrvAddr+"/gm", "get", params, &rsp)
|
||||
if err != nil {
|
||||
return errcode.New(errcode.ServerError, "发送新增白名单请求:%+v错误:%v", whiteInfo, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hook *WhitelistHook) 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.RoleMail{}).FromDto(dtoObj)
|
||||
whiteInfo := et.ToPo().(*model.WhiteList)
|
||||
|
||||
params := &url.Values{}
|
||||
params.Add("cmd_data", "OpWhitelist")
|
||||
params.Add("type", whiteInfo.WType)
|
||||
params.Add("value", whiteInfo.Value)
|
||||
params.Add("op", "remove")
|
||||
params.Add("server", whiteInfo.ServerConfID)
|
||||
|
||||
rsp := make(map[string]any)
|
||||
err := httpclient.Request(alisrvAddr+"/gm", "get", params, &rsp)
|
||||
if err != nil {
|
||||
return errcode.New(errcode.ServerError, "发送删除白名单请求:%+v错误:%v", whiteInfo, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -11,7 +11,7 @@ func init() {
|
||||
|
||||
// Account 空表,就是用来兼容资源增删改查公共操作的,实际列举账号等都是走各个项目api拉取
|
||||
type Account struct {
|
||||
ProjectId int
|
||||
ProjectId int `gorm:"index:idx_project_id"`
|
||||
Account string `name:"账号" json:"account" where:"eq"`
|
||||
ServerConfId string `name:"区服id" json:"serveId" where:"eq"`
|
||||
RoleIds []string `gorm:"type:json;serializer:json" name:"角色id列表" json:"roleIds"`
|
||||
|
41
admin/apps/game/model/cdkey.go
Normal file
41
admin/apps/game/model/cdkey.go
Normal file
@ -0,0 +1,41 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"admin/apps/game/model/dto"
|
||||
"admin/internal/db"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
db.RegisterTableModels(CDKey{})
|
||||
}
|
||||
|
||||
type CDKey struct {
|
||||
ID int `gorm:"primarykey" readonly:"true"`
|
||||
ProjectId int `gorm:"index:idx_project_id"`
|
||||
Desc string `name:"礼包说明" required:"true"`
|
||||
ServerIDs []string `gorm:"type:json;serializer:json" name:"区服" type:"[]string" choices:"GetChoiceServers" multi_choice:"true"`
|
||||
CodeType int `name:"礼包类型" required:"true" choices:"GetCodeTypeChoices"`
|
||||
Code string `name:"礼包码" desc:"一码通用才配置"`
|
||||
CodeNum int `name:"礼包数量" desc:"一码一用才配置"`
|
||||
ValidStartTime time.Time `name:"生效起始时间"`
|
||||
ValidEndTime time.Time `name:"生效结束时间"`
|
||||
RewardList []*MailAttachItem `gorm:"type:json;serializer:json" name:"邮件附件" type:"items" desc:"搜索道具并点击添加"`
|
||||
|
||||
CreatedAt time.Time `readonly:"true"`
|
||||
}
|
||||
|
||||
func (lm *CDKey) TableName() string {
|
||||
return "cdkey"
|
||||
}
|
||||
|
||||
func (m *CDKey) GetId() int {
|
||||
return m.ID
|
||||
}
|
||||
|
||||
func (m *CDKey) GetCodeTypeChoices() []*dto.CommonDtoFieldChoice {
|
||||
return []*dto.CommonDtoFieldChoice{
|
||||
{Desc: "一码通用", Value: 0},
|
||||
{Desc: "一码一用", Value: 1},
|
||||
}
|
||||
}
|
29
admin/apps/game/model/cdkey_used.go
Normal file
29
admin/apps/game/model/cdkey_used.go
Normal file
@ -0,0 +1,29 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"admin/internal/db"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
db.RegisterTableModels(CDKeyUsed{})
|
||||
}
|
||||
|
||||
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"`
|
||||
RoleName string `gorm:"type:varchar(255)"`
|
||||
Key string `gorm:"type:varchar(255);index:idx_key"`
|
||||
CreatedAt time.Time `readonly:"true"`
|
||||
}
|
||||
|
||||
func (lm *CDKeyUsed) TableName() string {
|
||||
return "cdkey_used"
|
||||
}
|
||||
|
||||
func (m *CDKeyUsed) GetId() int {
|
||||
return m.ID
|
||||
}
|
@ -11,8 +11,8 @@ func init() {
|
||||
}
|
||||
|
||||
type GlobalMail struct {
|
||||
ID int `gorm:"primarykey" readonly:"true"`
|
||||
ProjectId int
|
||||
ID int `gorm:"primarykey" readonly:"true"`
|
||||
ProjectId int `gorm:"index:idx_project_id"`
|
||||
ServerIDs []string `gorm:"type:json;serializer:json" name:"区服" type:"[]string" choices:"GetChoiceServers" multi_choice:"true"`
|
||||
Title string `name:"邮件标题" required:"true"`
|
||||
Content string `name:"邮件内容" required:"true"`
|
||||
|
@ -11,8 +11,8 @@ func init() {
|
||||
}
|
||||
|
||||
type Notice struct {
|
||||
ID int `gorm:"primarykey" readonly:"true"`
|
||||
ProjectId int
|
||||
ID int `gorm:"primarykey" readonly:"true"`
|
||||
ProjectId int `gorm:"index:idx_project_id"`
|
||||
ServerIDs []int `gorm:"type:json;serializer:json" name:"公告生效服务器" desc:"为空表示所有服" choices:"GetChoiceServers"`
|
||||
Content string `name:"公告内容" required:"true"`
|
||||
StartAt time.Time `name:"开始时间" required:"true"`
|
||||
|
@ -1,27 +0,0 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"admin/internal/db"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
db.RegisterTableModels(RewardCode{})
|
||||
}
|
||||
|
||||
type RewardCode struct {
|
||||
ID int `gorm:"primarykey" readonly:"true"`
|
||||
ProjectId int
|
||||
Group int `name:"奖励码组"`
|
||||
Code int `name:"奖励码"`
|
||||
|
||||
CreatedAt time.Time `readonly:"true"`
|
||||
}
|
||||
|
||||
func (lm *RewardCode) TableName() string {
|
||||
return "reward_code"
|
||||
}
|
||||
|
||||
func (m *RewardCode) GetId() int {
|
||||
return m.ID
|
||||
}
|
@ -17,8 +17,8 @@ type MailAttachItem struct {
|
||||
}
|
||||
|
||||
type RoleMail struct {
|
||||
ID int `gorm:"primarykey" readonly:"true"`
|
||||
ProjectId int
|
||||
ID int `gorm:"primarykey" readonly:"true"`
|
||||
ProjectId int `gorm:"index:idx_project_id"`
|
||||
RoleIDs []string `gorm:"type:json;serializer:json" name:"生效的角色id" desc:"生效的角色id,逗号分隔多个" required:"true"`
|
||||
ServerID string `name:"所属区服" choices:"GetChoiceServers" required:"true" where:"eq"`
|
||||
Title string `name:"邮件标题" required:"true"`
|
||||
|
@ -13,9 +13,9 @@ func init() {
|
||||
type WhiteList struct {
|
||||
ID int `gorm:"primarykey" readonly:"true"`
|
||||
ProjectId int `gorm:"uniqueIndex:idx_whitelist"`
|
||||
ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_server" name:"区服id" required:"true" choices:"GetChoiceServers" where:"eq"`
|
||||
Account string `gorm:"type:varchar(128);uniqueIndex:idx_whitelist" name:"账户" required:"true"`
|
||||
Desc string `name:"描述"`
|
||||
ServerConfID string `gorm:"type:varchar(200);uniqueIndex:idx_whitelist;index:idx_server" name:"区服id" required:"true" choices:"GetChoiceServers" where:"eq"`
|
||||
WType string `name:"白名单类型" desc:"账号或者ip" required:"true" choices:"GetWhitelistTypeChoices"`
|
||||
Value string `gorm:"type:varchar(128);uniqueIndex:idx_whitelist" name:"账户" required:"true"`
|
||||
|
||||
CreatedAt time.Time `readonly:"true" where:"range"`
|
||||
}
|
||||
@ -31,3 +31,10 @@ func (m *WhiteList) GetId() int {
|
||||
func (m *WhiteList) GetChoiceServers(project *Project) []*dto.CommonDtoFieldChoice {
|
||||
return getChoiceServers(project)
|
||||
}
|
||||
|
||||
func (m *WhiteList) GetWhitelistTypeChoices(project *Project) []*dto.CommonDtoFieldChoice {
|
||||
return []*dto.CommonDtoFieldChoice{
|
||||
{Desc: "IP", Value: "ip"},
|
||||
{Desc: "账号", Value: "account"},
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,11 @@ import (
|
||||
|
||||
func (ctl *controller) CommonList(ctx *context.WebContext, params *dto.CommonListReq, rsp *dto.CommonListRsp) error {
|
||||
projectId, resource := getCtxURIProjectIdAndResource(ctx)
|
||||
list, err := ctl.svc.CommonList(ctx, projectId, resource, params)
|
||||
|
||||
ctx1 := stdContext.WithValue(ctx.Context, "user_id", ctx.Header.UserId)
|
||||
ctx1 = stdContext.WithValue(ctx1, "user_name", ctx.Header.UserName)
|
||||
|
||||
list, err := ctl.svc.CommonList(ctx1, projectId, resource, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -20,7 +24,7 @@ func (ctl *controller) CommonPost(ctx *context.WebContext, params *dto.CommonPos
|
||||
projectId, resource := getCtxURIProjectIdAndResource(ctx)
|
||||
|
||||
ctx1 := stdContext.WithValue(ctx.Context, "user_id", ctx.Header.UserId)
|
||||
ctx1 = stdContext.WithValue(ctx1, "user_name", ctx.Header.UserId)
|
||||
ctx1 = stdContext.WithValue(ctx1, "user_name", ctx.Header.UserName)
|
||||
|
||||
newObj, err := ctl.svc.CommonPost(ctx1, projectId, resource, *params.Dto)
|
||||
if err != nil {
|
||||
@ -32,7 +36,11 @@ func (ctl *controller) CommonPost(ctx *context.WebContext, params *dto.CommonPos
|
||||
|
||||
func (ctl *controller) CommonPut(ctx *context.WebContext, params *dto.CommonPutReq, rsp *dto.CommonPutRsp) error {
|
||||
projectId, resource := getCtxURIProjectIdAndResource(ctx)
|
||||
err := ctl.svc.CommonPut(ctx, projectId, resource, *params.Dto)
|
||||
|
||||
ctx1 := stdContext.WithValue(ctx.Context, "user_id", ctx.Header.UserId)
|
||||
ctx1 = stdContext.WithValue(ctx1, "user_name", ctx.Header.UserName)
|
||||
|
||||
err := ctl.svc.CommonPut(ctx1, projectId, resource, *params.Dto)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -42,7 +50,11 @@ func (ctl *controller) CommonPut(ctx *context.WebContext, params *dto.CommonPutR
|
||||
|
||||
func (ctl *controller) CommonDelete(ctx *context.WebContext, params *dto.CommonDeleteReq, rsp *dto.CommonDeleteRsp) error {
|
||||
projectId, resource := getCtxURIProjectIdAndResource(ctx)
|
||||
err := ctl.svc.CommonDelete(ctx, projectId, resource, params.Id)
|
||||
|
||||
ctx1 := stdContext.WithValue(ctx.Context, "user_id", ctx.Header.UserId)
|
||||
ctx1 = stdContext.WithValue(ctx1, "user_name", ctx.Header.UserName)
|
||||
|
||||
err := ctl.svc.CommonDelete(ctx1, projectId, resource, params.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -12,14 +12,14 @@ func init() {
|
||||
|
||||
// History 用户执行历史
|
||||
type History struct {
|
||||
ID int `gorm:"primarykey" readonly:"true"`
|
||||
UserId int
|
||||
UserName string
|
||||
ProjectId int
|
||||
Resource string
|
||||
Method string
|
||||
Data string `gorm:"type:longtext"`
|
||||
CreateTime time.Time
|
||||
ID int `gorm:"primarykey" readonly:"true"`
|
||||
UserId int
|
||||
UserName string
|
||||
ProjectId int
|
||||
Resource string
|
||||
Method string
|
||||
Data string `gorm:"type:longtext"`
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
func (m *History) TableName() string {
|
||||
|
@ -14,10 +14,12 @@ type Server struct {
|
||||
}
|
||||
|
||||
func New(svc *service.Service) *Server {
|
||||
return &Server{
|
||||
srv := &Server{
|
||||
svc: svc,
|
||||
ctl: newController(svc),
|
||||
}
|
||||
srv.jobsSubscribe()
|
||||
return srv
|
||||
}
|
||||
|
||||
func (srv *Server) jobsSubscribe() {
|
||||
@ -30,6 +32,7 @@ func (srv *Server) subscriberHandlerUserExecute(msg *event.Msg) {
|
||||
err := json.Unmarshal(msg.Payload, msgHistory)
|
||||
if err != nil {
|
||||
xlog.Errorf("unmarshal msg(%+v) err:%v", string(msg.Payload), err)
|
||||
return
|
||||
}
|
||||
|
||||
po.UserId = msgHistory.UserId
|
||||
|
@ -23,7 +23,7 @@ type WebContext struct {
|
||||
}
|
||||
|
||||
func NewWebContext(rawCtx *gin.Context) web.IContext {
|
||||
return &WebContext{rawCtx: rawCtx}
|
||||
return &WebContext{Context: context.Background(), rawCtx: rawCtx}
|
||||
}
|
||||
|
||||
func (ctx *WebContext) ExtractHeader() error {
|
||||
|
57
admin/lib/cdkey/bitmap.go
Normal file
57
admin/lib/cdkey/bitmap.go
Normal file
@ -0,0 +1,57 @@
|
||||
package cdkey
|
||||
|
||||
import "fmt"
|
||||
|
||||
type bitmap struct {
|
||||
size int
|
||||
set int64
|
||||
}
|
||||
|
||||
func newBitmap(size int, set int64) *bitmap {
|
||||
if size < 0 || size > 63 {
|
||||
panic("size must be between 0 and 63")
|
||||
}
|
||||
if size == 0 {
|
||||
size = 32
|
||||
}
|
||||
m := &bitmap{size: size, set: set}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *bitmap) Get(index int) bool {
|
||||
return m.GetInt(index) != 0
|
||||
}
|
||||
|
||||
func (m *bitmap) GetInt(index int) int64 {
|
||||
if index < 0 || index >= m.size {
|
||||
panic(fmt.Errorf("index out of range:%v", index))
|
||||
}
|
||||
|
||||
if m.set&(1<<uint(index)) != 0 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *bitmap) Set(index int, value bool) {
|
||||
if index < 0 || index >= m.size {
|
||||
panic(fmt.Errorf("index out of range:%v", index))
|
||||
}
|
||||
if value {
|
||||
m.set |= 1 << uint(index)
|
||||
} else {
|
||||
m.set &^= 1 << uint(index)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *bitmap) Clear() {
|
||||
m.set = 0
|
||||
}
|
||||
|
||||
func (m *bitmap) toInt64() int64 {
|
||||
return m.set
|
||||
}
|
||||
|
||||
func (m *bitmap) GetSize() int {
|
||||
return m.size
|
||||
}
|
124
admin/lib/cdkey/cdkey.go
Normal file
124
admin/lib/cdkey/cdkey.go
Normal file
@ -0,0 +1,124 @@
|
||||
package cdkey
|
||||
|
||||
/*
|
||||
* 奖励码模块,从神魔大陆java代码移植过来的
|
||||
*/
|
||||
|
||||
func GenerateAll(batch int, totalCount int) []string {
|
||||
keyList := make([]string, 0, totalCount)
|
||||
for i := 0; i < totalCount; i++ {
|
||||
codeKey := GenerateOne(batch, totalCount, i)
|
||||
keyList = append(keyList, codeKey)
|
||||
}
|
||||
return keyList
|
||||
}
|
||||
|
||||
func GenerateOne(batch int, totalCount int, curNo int) string {
|
||||
base := MAXNUM / totalCount
|
||||
numId := curNo
|
||||
num := base + numId*base
|
||||
mainKeyBitSet := newBitmap(MAIN_KEY_LENGTH, 0)
|
||||
numKeyIndex := int((num + batch) % NUM_KEY_LENGTH_BINARY)
|
||||
temp := newEncode(int64(num), numKeyIndex)
|
||||
for i := 0; i < MAIN_KEY_NUM_LENGTH; i++ {
|
||||
mainKeyBitSet.Set(i, (temp&1) == 1)
|
||||
temp >>= 1
|
||||
}
|
||||
for i := 0; i < NUM_KEY_LENGTH; i++ {
|
||||
mainKeyBitSet.Set(i+MAIN_KEY_NUM_LENGTH, toNumBitSet[numKeyIndex].Get(i))
|
||||
}
|
||||
|
||||
var beCheckNum int64
|
||||
if mainKeyBitSet.toInt64() > 0 {
|
||||
beCheckNum = mainKeyBitSet.toInt64()
|
||||
} else {
|
||||
beCheckNum = -mainKeyBitSet.toInt64()
|
||||
}
|
||||
checkNum := int32(beCheckNum % 32)
|
||||
checkNumBitSet := toBitSet32[checkNum]
|
||||
for i := 0; i < CHECK_LENGTH; i++ {
|
||||
mainKeyBitSet.Set(i+MAIN_KEY_NUM_LENGTH+NUM_KEY_LENGTH, checkNumBitSet.Get(i))
|
||||
}
|
||||
batchBitSet := newBitmap(BATCH_LENGTH, 0)
|
||||
temp = int64(batch)
|
||||
for i := 0; i < BATCH_LENGTH; i++ {
|
||||
batchBitSet.Set(i, (temp&1) == 1)
|
||||
temp >>= 1
|
||||
}
|
||||
batchKeyIndex := (num + batch) % BATCH_KEY_LENGTH_BINARY
|
||||
batchStr := base32Encode(fakeRC4Encode(batchBitSet, batchKey[batchKeyIndex]))
|
||||
temp1 := fakeRC4Encode(mainKeyBitSet, getMainKey(batchBitSet))
|
||||
mainKeyStr := base32Encode(temp1)
|
||||
|
||||
cdKeyType := 1
|
||||
typeIndex := (num + cdKeyType) % TYPE_LENGTH_BINARY
|
||||
|
||||
codeKey := mainKeyStr + batchStr + string(toBase32[batchKeyIndex]) + string(toBase32[typeIndex])
|
||||
return shuffleCDKey(codeKey, batchKeyIndex)
|
||||
}
|
||||
|
||||
type CDKeyInfo struct {
|
||||
Batch int
|
||||
Num int64
|
||||
}
|
||||
|
||||
func DecodeCDKey(cdKey string) (*CDKeyInfo, bool) {
|
||||
if len(cdKey) != CDKEY_LENGTH {
|
||||
return nil, false
|
||||
}
|
||||
index := toBase32CToI[rune(cdKey[SHUFFLE_INDEX])]
|
||||
cdKey1 := recoverCDKey(cdKey[0:SHUFFLE_INDEX], index)
|
||||
if cdKey1 == "" {
|
||||
return nil, false
|
||||
}
|
||||
typeCheck, find := toBase32CToI[rune(cdKey1[TYPE_INDEX])]
|
||||
if !find {
|
||||
return nil, false
|
||||
}
|
||||
batchKeyIndex, find := toBase32CToI[rune(cdKey1[BATCH_NUM_BEGIN])]
|
||||
if !find {
|
||||
return nil, false
|
||||
}
|
||||
if !(batchKeyIndex == index) {
|
||||
return nil, false
|
||||
}
|
||||
batch := base32Decode(cdKey1[BATCH_BEGIN:BATCH_END])
|
||||
if batch == nil {
|
||||
return nil, false
|
||||
}
|
||||
batchBitSet := fakeRC4Encode(batch, batchKey[batchKeyIndex])
|
||||
if batchBitSet == nil {
|
||||
return nil, false
|
||||
}
|
||||
mainKey := base32Decode(cdKey1[MAIN_KEY_BEGIN:MAIN_KEY_END])
|
||||
if mainKey == nil {
|
||||
return nil, false
|
||||
}
|
||||
mainKey2 := getMainKey(batchBitSet)
|
||||
if mainKey2 == nil {
|
||||
return nil, false
|
||||
}
|
||||
mainKeyBitSet := fakeRC4Encode(mainKey, mainKey2)
|
||||
if mainKeyBitSet == nil {
|
||||
return nil, false
|
||||
}
|
||||
if !(check(mainKeyBitSet)) {
|
||||
return nil, false
|
||||
}
|
||||
numKeyIndex := getNumKeyIndex(mainKeyBitSet)
|
||||
numInt := numEncode(mainKeyBitSet.toInt64()&intFormat, numKeyIndex)
|
||||
batchInt := int(batchBitSet.toInt64() & intFormat)
|
||||
if !(checkIndex(numInt, batchInt, batchKeyIndex, numKeyIndex)) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
cdkeyInfo := &CDKeyInfo{
|
||||
Batch: batchInt,
|
||||
Num: numInt,
|
||||
// Type: typeCheck,
|
||||
}
|
||||
if typeCheck > 0 {
|
||||
|
||||
}
|
||||
return cdkeyInfo, true
|
||||
}
|
19
admin/lib/cdkey/cdkey_test.go
Normal file
19
admin/lib/cdkey/cdkey_test.go
Normal file
@ -0,0 +1,19 @@
|
||||
package cdkey
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCDKey(t *testing.T) {
|
||||
for i, key := range GenerateAll(2, 100000) {
|
||||
if i > 10 {
|
||||
continue
|
||||
}
|
||||
info, ok := DecodeCDKey(key)
|
||||
if !ok {
|
||||
panic(key)
|
||||
}
|
||||
fmt.Printf("%s, info:%+v\n", key, info)
|
||||
}
|
||||
}
|
311
admin/lib/cdkey/utils.go
Normal file
311
admin/lib/cdkey/utils.go
Normal file
@ -0,0 +1,311 @@
|
||||
package cdkey
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
const (
|
||||
MAXNUM = math.MaxInt32 - 20000000
|
||||
CDKEY_LENGTH = 15
|
||||
MAIN_KEY_LENGTH = 40
|
||||
NUM_KEY_LENGTH_BINARY = 8
|
||||
MAIN_KEY_NUM_LENGTH = 32
|
||||
NUM_KEY_LENGTH = 3
|
||||
CHECK_LENGTH = 5
|
||||
BATCH_LENGTH = 20
|
||||
BATCH_KEY_LENGTH_BINARY = 32
|
||||
TYPE_LENGTH_BINARY = 32
|
||||
shuffleLength = 32
|
||||
exceptShuffleSeedTotalLength = 70
|
||||
SHUFFLE_INDEX = 14
|
||||
TYPE_INDEX = 13
|
||||
BATCH_NUM_BEGIN = 12
|
||||
BATCH_BEGIN = 8
|
||||
BATCH_END = 12
|
||||
MAIN_KEY_BEGIN = 0
|
||||
MAIN_KEY_END = 8
|
||||
mainKeyFormat int64 = 34359738367
|
||||
checkNumFormat int64 = 1065151889408
|
||||
CHECK_LENGTH_BINARY = 32
|
||||
)
|
||||
|
||||
var toBase32 = []rune{'2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}
|
||||
var toBase32CToI map[rune]int
|
||||
|
||||
var mainKeyBatchKey = newBitmap(20, 1918215104086)
|
||||
|
||||
var intFormat int64 = 4294967295
|
||||
var key = newBitmap(20, 1209617303503)
|
||||
var shuffleMap = make(map[int][]int)
|
||||
|
||||
var numKey = []int64{
|
||||
3000669867,
|
||||
3671239387,
|
||||
2526368469,
|
||||
2493864681,
|
||||
3604110165,
|
||||
3567998377,
|
||||
2763216037,
|
||||
2863360677,
|
||||
}
|
||||
|
||||
var batchKey = []*bitmap{
|
||||
newBitmap(20, 31511741981182095),
|
||||
newBitmap(20, 3664122353341979125),
|
||||
newBitmap(20, 3024789513220408272),
|
||||
newBitmap(20, 7614911559402769357),
|
||||
newBitmap(20, 2108459002177461007),
|
||||
newBitmap(20, -3779584974404014377),
|
||||
newBitmap(20, -4708816669881919040),
|
||||
newBitmap(20, -6362362468306307184),
|
||||
newBitmap(20, 5237470582631498811),
|
||||
newBitmap(20, 5507680150663927504),
|
||||
newBitmap(20, -533592023685287698),
|
||||
newBitmap(20, 5175063634818869382),
|
||||
newBitmap(20, -8744795741519816883),
|
||||
newBitmap(20, -4080090257317771786),
|
||||
newBitmap(20, -8036132546007449313),
|
||||
newBitmap(20, 202953352259602475),
|
||||
newBitmap(20, 1095317292689077154),
|
||||
newBitmap(20, 2991873574004670411),
|
||||
newBitmap(20, -6707239314758981218),
|
||||
newBitmap(20, 7712241472074747715),
|
||||
newBitmap(20, -8133590642362246474),
|
||||
newBitmap(20, -4968343163714505661),
|
||||
newBitmap(20, -6905628074578549760),
|
||||
newBitmap(20, 5195681389062354029),
|
||||
newBitmap(20, -5910830154906229288),
|
||||
newBitmap(20, -1676518468791541432),
|
||||
newBitmap(20, -7994149653806568719),
|
||||
newBitmap(20, -7378005837432330374),
|
||||
newBitmap(20, 1068884024847281324),
|
||||
newBitmap(20, 1945505497196485942),
|
||||
newBitmap(20, -3492268017556027220),
|
||||
newBitmap(20, 7364928014934311923),
|
||||
}
|
||||
|
||||
var toNumBitSet [NUM_KEY_LENGTH_BINARY]*bitmap
|
||||
var toBitSet32 [32]*bitmap
|
||||
|
||||
func init() {
|
||||
for i := 0; i < 32; i++ {
|
||||
temp := i
|
||||
toBitSet32[i] = newBitmap(5, 0)
|
||||
for j := 0; j < 5; j++ {
|
||||
toBitSet32[i].Set(j, (temp&1) == 1)
|
||||
temp >>= 1
|
||||
}
|
||||
}
|
||||
for i := 0; i < NUM_KEY_LENGTH_BINARY; i++ {
|
||||
temp := i
|
||||
toNumBitSet[i] = newBitmap(NUM_KEY_LENGTH, 0)
|
||||
for j := 0; j < NUM_KEY_LENGTH; j++ {
|
||||
toNumBitSet[i].Set(j, (temp&1) == 1)
|
||||
temp >>= 1
|
||||
}
|
||||
}
|
||||
|
||||
toBase32CToI = make(map[rune]int)
|
||||
for i := 0; i < len(toBitSet32); i++ {
|
||||
toBase32CToI[toBase32[i]] = i
|
||||
}
|
||||
for seed := 0; seed < shuffleLength; seed++ {
|
||||
array := make([]int, exceptShuffleSeedTotalLength)
|
||||
for i := 0; i < exceptShuffleSeedTotalLength; i++ {
|
||||
array[i] = i
|
||||
}
|
||||
shuffle(array, rand.New(rand.NewSource(int64(seed))))
|
||||
shuffleMap[seed] = array
|
||||
}
|
||||
}
|
||||
|
||||
func checkIndex(num int64, batch int, batchKeyIndex int, numKeyIndex int) bool {
|
||||
return (num+int64(batch))%BATCH_KEY_LENGTH_BINARY == int64(batchKeyIndex) && (num+int64(batch))%NUM_KEY_LENGTH_BINARY == int64(numKeyIndex)
|
||||
}
|
||||
|
||||
func numEncode(num int64, index int) int64 {
|
||||
return numKey[index] ^ num
|
||||
}
|
||||
|
||||
func getNumKeyIndex(mainKeyBitSet *bitmap) int {
|
||||
bitset := newBitmap(NUM_KEY_LENGTH, 0)
|
||||
for i := 0; i < bitset.GetSize(); i++ {
|
||||
bitset.Set(i, mainKeyBitSet.Get(MAIN_KEY_NUM_LENGTH+i))
|
||||
}
|
||||
return int(bitset.toInt64())
|
||||
}
|
||||
|
||||
func check(mainkeyBitSet *bitmap) bool {
|
||||
mainkey := mainkeyBitSet.toInt64()
|
||||
if mainkey <= 0 {
|
||||
mainkey = -mainkey
|
||||
}
|
||||
beCheckNum := mainkey & mainKeyFormat
|
||||
checkNum := mainkey & checkNumFormat
|
||||
checkNum >>= (MAIN_KEY_NUM_LENGTH + NUM_KEY_LENGTH)
|
||||
return beCheckNum%CHECK_LENGTH_BINARY == checkNum
|
||||
}
|
||||
|
||||
func base32Decode(data string) *bitmap {
|
||||
if len(data) > 63/5 {
|
||||
return nil
|
||||
}
|
||||
bitset := newBitmap(len(data)*5, 0)
|
||||
i := 0
|
||||
for _, c := range data {
|
||||
if _, find := toBase32CToI[c]; !find {
|
||||
return nil
|
||||
}
|
||||
index := toBase32CToI[c]
|
||||
letterBitSet := toBitSet32[index]
|
||||
for j := 0; j < 5; j++ {
|
||||
bitset.Set(i, letterBitSet.Get(j))
|
||||
i++
|
||||
}
|
||||
}
|
||||
return bitset
|
||||
}
|
||||
|
||||
func recoverCDKey(cdKey string, index int) string {
|
||||
array := shuffleMap[index]
|
||||
if array == nil {
|
||||
return ""
|
||||
}
|
||||
bytes := base32DecodeReturnByteArray(cdKey)
|
||||
if bytes == nil {
|
||||
return ""
|
||||
}
|
||||
if len(array) != len(bytes) {
|
||||
return ""
|
||||
}
|
||||
resultByte := make([]byte, len(bytes))
|
||||
for i := 0; i < len(array); i++ {
|
||||
resultByte[array[i]] = bytes[i]
|
||||
}
|
||||
return base32Encode1(resultByte)
|
||||
}
|
||||
|
||||
func shuffleCDKey(key string, index int) string {
|
||||
array := shuffleMap[index]
|
||||
bytes := base32DecodeReturnByteArray(key)
|
||||
resultByte := make([]byte, len(bytes))
|
||||
for i := 0; i < len(array); i++ {
|
||||
resultByte[i] = bytes[array[i]]
|
||||
}
|
||||
return base32Encode1(resultByte) + string(toBase32[index])
|
||||
}
|
||||
|
||||
func shuffle(array []int, rander *rand.Rand) {
|
||||
for i := len(array); i > 1; i-- {
|
||||
swap(array, i-1, rander.Intn(i))
|
||||
}
|
||||
}
|
||||
|
||||
func swap(array []int, i, j int) {
|
||||
array[i], array[j] = array[j], array[i]
|
||||
}
|
||||
|
||||
func newEncode(num int64, index int) int64 {
|
||||
return numKey[index] ^ num
|
||||
}
|
||||
|
||||
func base32Encode(m *bitmap) string {
|
||||
if m.GetSize()%5 != 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
stringBuilder := ""
|
||||
|
||||
for i := 0; i < m.GetSize(); i += 5 {
|
||||
bitset1 := newBitmap(5, 0)
|
||||
for j := 0; j < 5; j++ {
|
||||
bitset1.Set(j, m.Get(i+j))
|
||||
}
|
||||
if bitset1.toInt64() >= int64(len(toBase32)) {
|
||||
return ""
|
||||
}
|
||||
letter := toBase32[int(bitset1.toInt64())]
|
||||
stringBuilder += string(letter)
|
||||
}
|
||||
|
||||
return stringBuilder
|
||||
}
|
||||
|
||||
func base32Encode1(bytes []byte) string {
|
||||
if len(bytes)%5 != 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
stringBuilder := ""
|
||||
|
||||
for i := 0; i < len(bytes); i += 5 {
|
||||
bitset1 := newBitmap(5, 0)
|
||||
for j := 0; j < 5; j++ {
|
||||
bitset1.Set(j, bytes[i+j] > 0)
|
||||
}
|
||||
if bitset1.toInt64() >= int64(len(toBase32)) {
|
||||
return ""
|
||||
}
|
||||
letter := toBase32[int(bitset1.toInt64())]
|
||||
stringBuilder += string(letter)
|
||||
}
|
||||
|
||||
return stringBuilder
|
||||
}
|
||||
|
||||
func fakeRC4Encode(data, key *bitmap) *bitmap {
|
||||
if data.GetSize() != key.GetSize() {
|
||||
return nil
|
||||
}
|
||||
bitSet := newBitmap(data.GetSize(), 0)
|
||||
for i := 0; i < data.GetSize(); i++ {
|
||||
bitSet.Set(i, data.GetInt(i)^key.GetInt(i) == 1)
|
||||
}
|
||||
return bitSet
|
||||
}
|
||||
|
||||
func getMainKey(batch *bitmap) *bitmap {
|
||||
bitSet := newBitmap(MAIN_KEY_LENGTH, 0)
|
||||
batchKey := fakeRC4Encode(batch, mainKeyBatchKey)
|
||||
if batchKey == nil {
|
||||
return nil
|
||||
}
|
||||
index := (int)(batchKey.toInt64()&intFormat) % key.GetSize()
|
||||
count := 0
|
||||
for i := 0; i < index; i++ {
|
||||
bitSet.Set(count, key.Get(i))
|
||||
count++
|
||||
}
|
||||
for i := 0; i < batchKey.GetSize(); i++ {
|
||||
bitSet.Set(count, batchKey.Get(i))
|
||||
count++
|
||||
}
|
||||
for i := index; i < key.GetSize(); i++ {
|
||||
bitSet.Set(count, key.Get(i))
|
||||
count++
|
||||
}
|
||||
return bitSet
|
||||
}
|
||||
|
||||
func base32DecodeReturnByteArray(data string) []byte {
|
||||
bytes := make([]byte, len(data)*5)
|
||||
i := 0
|
||||
for _, c := range data {
|
||||
if _, find := toBase32CToI[c]; !find {
|
||||
return nil
|
||||
}
|
||||
index := toBase32CToI[c]
|
||||
letterBitSet := toBitSet32[index]
|
||||
for j := 0; j < 5; j++ {
|
||||
bitValue := letterBitSet.Get(j)
|
||||
if bitValue {
|
||||
bytes[i] = 1
|
||||
} else {
|
||||
bytes[i] = 0
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
return bytes
|
||||
}
|
@ -1,2 +1,2 @@
|
||||
VITE_APP_BASE_API = '/api'
|
||||
VITE_APP_BASE_URL = 'http://localhost:8080/api'
|
||||
VITE_APP_BASE_URL = 'http://192.168.78.128:8080/api'
|
@ -98,7 +98,7 @@ const listData = async () => {
|
||||
|
||||
calcElColSpan.value = 0
|
||||
// 计算el-col占用24格子的span数量
|
||||
let calcElColSpanTmp = 1
|
||||
let calcElColSpanTmp = 2
|
||||
whereFieldsDescInfo.value.forEach((field) => {
|
||||
if (field.where === "range") {
|
||||
calcElColSpanTmp += 2
|
||||
@ -106,9 +106,9 @@ const listData = async () => {
|
||||
calcElColSpanTmp += 1
|
||||
}
|
||||
})
|
||||
calcElColSpan.value = 24/calcElColSpanTmp
|
||||
calcElColSpan.value = 24 / calcElColSpanTmp
|
||||
|
||||
console.log("where fields:", whereFieldsDescInfo.value)
|
||||
// console.log("where fields:", whereFieldsDescInfo.value)
|
||||
// console.log('await list rsp:', listRsp.value, fieldsDescInfo.value, toRaw(rows.value), toRaw(rules.value))
|
||||
|
||||
listDataOK.value = true
|
||||
@ -270,7 +270,7 @@ const handleQueryItem = (itemQueryStr) => {
|
||||
itemQueryStr = itemQueryStr.replace(/[\s\u3000]/g, "")
|
||||
resourceGetAllItems(projectId).then((res) => {
|
||||
console.log("获取所有道具返回:", res.data)
|
||||
console.log("查询字符串:[" + itemQueryStr + "]")
|
||||
console.log("查询字符串:[" + itemQueryStr + "]")
|
||||
itemChoices.value = res.data.items.filter((item) => {
|
||||
return item.desc.includes(itemQueryStr)
|
||||
})
|
||||
@ -280,6 +280,14 @@ const handleQueryItem = (itemQueryStr) => {
|
||||
})
|
||||
}
|
||||
|
||||
const resetConditionSearch = () => {
|
||||
for (let i = 0; i < whereFieldsDescInfo.value.length; i++) {
|
||||
let field = whereFieldsDescInfo.value[i]
|
||||
field.value1 = null
|
||||
field.value2 = null
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -299,13 +307,14 @@ const handleQueryItem = (itemQueryStr) => {
|
||||
</el-col>
|
||||
<el-col :span="calcElColSpan">
|
||||
<el-date-picker v-model="fieldDescInfo.value2" type="datetime"
|
||||
:placeholder="(fieldDescInfo.name + '结束')" format="YYYY/MM/DD HH:mm:ss"
|
||||
value-format="YYYY/MM/DD HH:mm:ss"></el-date-picker>
|
||||
:placeholder="(fieldDescInfo.name + '结束')" format="YYYY/MM/DD HH:mm:ss"
|
||||
value-format="YYYY/MM/DD HH:mm:ss"></el-date-picker>
|
||||
</el-col>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-col :span="calcElColSpan">
|
||||
<el-select v-model="fieldDescInfo.value1" :placeholder="(fieldDescInfo.multi_choice === true ? '--多选--' : '--单选--')"
|
||||
<el-select v-model="fieldDescInfo.value1"
|
||||
:placeholder="(fieldDescInfo.multi_choice === true ? '--' + fieldDescInfo.name + '--' : '--' + fieldDescInfo.name + '--')"
|
||||
style="width: 150px"
|
||||
filterable v-if="(fieldDescInfo.choices.length > 0)">
|
||||
<el-option v-for="choice in fieldDescInfo.choices" :key="choice.value" :label="choice.desc"
|
||||
@ -320,6 +329,9 @@ const handleQueryItem = (itemQueryStr) => {
|
||||
<el-col :span="calcElColSpan">
|
||||
<el-button @click="listData" type="primary">条件搜索</el-button>
|
||||
</el-col>
|
||||
<el-col :span="calcElColSpan">
|
||||
<el-button @click="resetConditionSearch">清空条件</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row style="margin-top: 10px">
|
||||
|
@ -49,7 +49,7 @@ router.beforeEach((to, from, next) => {
|
||||
LocalCache.deleteCache("projectsRoute")
|
||||
})
|
||||
} else {
|
||||
// console.log("访问页面" + to.path + "已经请求过用户数据,跳过获取")
|
||||
console.log("访问页面" + to.path + "已经请求过用户数据,跳过获取")
|
||||
// console.log("op tree routes length valid:", projectOpTreeRoutes.value.length)
|
||||
next()
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ const activeMenu = computed(() => route.path)
|
||||
|
||||
const hasClickedMenu = ref(false)
|
||||
|
||||
router.push("/welcome")
|
||||
// router.push("/welcome")
|
||||
|
||||
const handleEnterIndex = () => {
|
||||
router.push("/welcome")
|
||||
|
10
ui/todo.md
10
ui/todo.md
@ -1,7 +1,11 @@
|
||||
# todo列表
|
||||
|
||||
- [x] 道具列表选择支持远程搜索(避免道具列表太长卡顿)
|
||||
- [ ] 表格各种项目支持分页(难点是账户、角色、订单如何分)
|
||||
- [ ] 表格各种项目支持分页(难点是账户、角色、订单这种来自各个游戏区服数据如何分)
|
||||
- [ ] 表格字段排序
|
||||
- [ ] 表格字段搜索
|
||||
- [x] 表格字段搜索
|
||||
- [ ] 表格定制化按钮,比如服务器列表支持单个服务器维护、一键维护(难点是带页面跳转的比如角色列表快速封禁)
|
||||
- [ ] 执行历史记录
|
||||
- [x] 执行历史记录
|
||||
- [ ] 执行历史查看和条件检索
|
||||
- [ ] 公告发送游戏
|
||||
- [ ] 奖励码接入
|
Loading…
x
Reference in New Issue
Block a user