"feat(socket): 添加跨域请求处理并集成enum依赖,优化TCP连接数据注入"
This commit is contained in:
8
common/cool/config.go
Normal file
8
common/cool/config.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package cool
|
||||
|
||||
import "blazing/cool/coolconfig"
|
||||
|
||||
var (
|
||||
Config = coolconfig.Config // 配置中的cool节相关配置
|
||||
GetCfgWithDefault = coolconfig.GetCfgWithDefault // GetCfgWithDefault 获取配置,如果配置不存在,则使用默认值
|
||||
)
|
||||
8
common/cool/const.go
Normal file
8
common/cool/const.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package cool
|
||||
|
||||
// Join 类型
|
||||
const (
|
||||
LeftJoin JoinType = "LeftJoin"
|
||||
RightJoin JoinType = "RightJoin"
|
||||
InnerJoin JoinType = "InnerJoin"
|
||||
)
|
||||
27
common/cool/controller-simple.go
Normal file
27
common/cool/controller-simple.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package cool
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type IControllerSimple interface {
|
||||
}
|
||||
type ControllerSimple struct {
|
||||
Prefix string
|
||||
}
|
||||
|
||||
// 注册不带crud的路由
|
||||
func RegisterControllerSimple(c IControllerSimple) {
|
||||
var sController = &ControllerSimple{}
|
||||
// var sService = &Service{}
|
||||
gconv.Struct(c, &sController)
|
||||
g.Server().Group(
|
||||
sController.Prefix, func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(MiddlewareHandlerResponse)
|
||||
group.Bind(
|
||||
c,
|
||||
)
|
||||
})
|
||||
}
|
||||
217
common/cool/controller.go
Normal file
217
common/cool/controller.go
Normal file
@@ -0,0 +1,217 @@
|
||||
package cool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"blazing/cool/coolconfig"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type IController interface {
|
||||
Add(ctx context.Context, req *AddReq) (res *BaseRes, err error)
|
||||
Delete(ctx context.Context, req *DeleteReq) (res *BaseRes, err error)
|
||||
Update(ctx context.Context, req *UpdateReq) (res *BaseRes, err error)
|
||||
Info(ctx context.Context, req *InfoReq) (res *BaseRes, err error)
|
||||
List(ctx context.Context, req *ListReq) (res *BaseRes, err error)
|
||||
Page(ctx context.Context, req *PageReq) (res *BaseRes, err error)
|
||||
}
|
||||
type Controller struct {
|
||||
Prefix string `json:"prefix"`
|
||||
Api g.ArrayStr `json:"api"`
|
||||
Service IService `json:"service"`
|
||||
}
|
||||
|
||||
type AddReq struct {
|
||||
g.Meta `path:"/add" method:"POST"`
|
||||
}
|
||||
|
||||
type DeleteReq struct {
|
||||
g.Meta `path:"/delete" method:"POST"`
|
||||
Ids []int `json:"ids" v:"required#请选择要删除的数据"`
|
||||
}
|
||||
|
||||
type UpdateReq struct {
|
||||
g.Meta `path:"/update" method:"POST"`
|
||||
}
|
||||
|
||||
type InfoReq struct {
|
||||
g.Meta `path:"/info" method:"GET"`
|
||||
Id int `json:"id" v:"integer|required#请选择要查询的数据"`
|
||||
}
|
||||
|
||||
// type InfoRes struct {
|
||||
// *BaseRes
|
||||
// Data interface{} `json:"data"`
|
||||
// }
|
||||
|
||||
type ListReq struct {
|
||||
g.Meta `path:"/list" method:"POST"`
|
||||
Order string `json:"order"`
|
||||
Sort string `json:"sort"`
|
||||
}
|
||||
|
||||
type PageReq struct {
|
||||
g.Meta `path:"/page" method:"POST"`
|
||||
Page int `d:"1" json:"page"` // 页码
|
||||
Size int `d:"15" json:"size"` //每页条数
|
||||
Order string `json:"order"` // 排序字段
|
||||
Sort string `json:"sort"` // 排序方式 asc desc
|
||||
IsExport bool `json:"isExport"` // 是否导出
|
||||
MaxExportLimit int `json:"maxExportLimit"` // 最大导出条数,不传或者小于等于0则不限制
|
||||
}
|
||||
|
||||
func (c *Controller) Add(ctx context.Context, req *AddReq) (res *BaseRes, err error) {
|
||||
if garray.NewStrArrayFrom(c.Api).Contains("Add") {
|
||||
err := c.Service.ModifyBefore(ctx, "Add", g.RequestFromCtx(ctx).GetMap())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := c.Service.ServiceAdd(ctx, req)
|
||||
if err != nil {
|
||||
return Fail(err.Error()), err
|
||||
}
|
||||
err = c.Service.ModifyAfter(ctx, "Add", g.RequestFromCtx(ctx).GetMap())
|
||||
if err != nil {
|
||||
return Fail(err.Error()), err
|
||||
}
|
||||
return Ok(data), err
|
||||
}
|
||||
g.RequestFromCtx(ctx).Response.Status = 404
|
||||
return nil, nil
|
||||
}
|
||||
func (c *Controller) Delete(ctx context.Context, req *DeleteReq) (res *BaseRes, err error) {
|
||||
if garray.NewStrArrayFrom(c.Api).Contains("Delete") {
|
||||
err := c.Service.ModifyBefore(ctx, "Delete", g.RequestFromCtx(ctx).GetMap())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := c.Service.ServiceDelete(ctx, req)
|
||||
if err != nil {
|
||||
return Fail(err.Error()), err
|
||||
}
|
||||
c.Service.ModifyAfter(ctx, "Delete", g.RequestFromCtx(ctx).GetMap())
|
||||
return Ok(data), err
|
||||
}
|
||||
g.RequestFromCtx(ctx).Response.Status = 404
|
||||
return nil, nil
|
||||
}
|
||||
func (c *Controller) Update(ctx context.Context, req *UpdateReq) (res *BaseRes, err error) {
|
||||
if garray.NewStrArrayFrom(c.Api).Contains("Update") {
|
||||
err := c.Service.ModifyBefore(ctx, "Update", g.RequestFromCtx(ctx).GetMap())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := c.Service.ServiceUpdate(ctx, req)
|
||||
if err != nil {
|
||||
return Fail(err.Error()), err
|
||||
}
|
||||
c.Service.ModifyAfter(ctx, "Update", g.RequestFromCtx(ctx).GetMap())
|
||||
return Ok(data), err
|
||||
}
|
||||
g.RequestFromCtx(ctx).Response.Status = 404
|
||||
return nil, nil
|
||||
}
|
||||
func (c *Controller) Info(ctx context.Context, req *InfoReq) (res *BaseRes, err error) {
|
||||
if garray.NewStrArrayFrom(c.Api).Contains("Info") {
|
||||
data, err := c.Service.ServiceInfo(ctx, req)
|
||||
return Ok(data), err
|
||||
}
|
||||
g.RequestFromCtx(ctx).Response.Status = 404
|
||||
return nil, nil
|
||||
}
|
||||
func (c *Controller) List(ctx context.Context, req *ListReq) (res *BaseRes, err error) {
|
||||
if garray.NewStrArrayFrom(c.Api).Contains("List") {
|
||||
data, err := c.Service.ServiceList(ctx, req)
|
||||
return Ok(data), err
|
||||
}
|
||||
g.RequestFromCtx(ctx).Response.Status = 404
|
||||
return nil, nil
|
||||
}
|
||||
func (c *Controller) Page(ctx context.Context, req *PageReq) (res *BaseRes, err error) {
|
||||
if garray.NewStrArrayFrom(c.Api).Contains("Page") {
|
||||
data, err := c.Service.ServicePage(ctx, req)
|
||||
return Ok(data), err
|
||||
}
|
||||
g.RequestFromCtx(ctx).Response.Status = 404
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 注册控制器到路由
|
||||
func RegisterController(c IController) {
|
||||
var ctx = context.Background()
|
||||
var sController = &Controller{}
|
||||
gconv.Struct(c, &sController)
|
||||
if coolconfig.Config.Eps {
|
||||
model := sController.Service.GetModel()
|
||||
columns := getModelInfo(ctx, sController.Prefix, model)
|
||||
ModelInfo[sController.Prefix] = columns
|
||||
}
|
||||
g.Server().Group(
|
||||
sController.Prefix, func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(MiddlewareHandlerResponse)
|
||||
group.Bind(
|
||||
c,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// ColumnInfo 表字段信息
|
||||
type ColumnInfo struct {
|
||||
Comment string `json:"comment"`
|
||||
Length string `json:"length"`
|
||||
Nullable bool `json:"nullable"`
|
||||
PropertyName string `json:"propertyName"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// ModelInfo 路由prefix 对应的model信息
|
||||
var ModelInfo = make(map[string][]*ColumnInfo)
|
||||
|
||||
// getModelInfo 获取模型信息
|
||||
func getModelInfo(ctx g.Ctx, prefix string, model IModel) (columns []*ColumnInfo) {
|
||||
fields, err := g.DB(model.GroupName()).TableFields(ctx, model.TableName())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// g.Log().Info(ctx, "fields", fields)
|
||||
sortedFields := garray.NewArraySize(len(fields), len(fields))
|
||||
for _, field := range fields {
|
||||
sortedFields.Set(field.Index, field)
|
||||
}
|
||||
for _, field := range sortedFields.Slice() {
|
||||
if field.(*gdb.TableField).Name == "deleted_at" {
|
||||
continue
|
||||
}
|
||||
var comment string
|
||||
if field.(*gdb.TableField).Comment != "" {
|
||||
comment = field.(*gdb.TableField).Comment
|
||||
} else {
|
||||
comment = field.(*gdb.TableField).Name
|
||||
}
|
||||
// 去除 type中的长度
|
||||
var length string
|
||||
if strings.Contains(field.(*gdb.TableField).Type, "(") {
|
||||
length = field.(*gdb.TableField).Type[strings.Index(field.(*gdb.TableField).Type, "(")+1 : strings.Index(field.(*gdb.TableField).Type, ")")]
|
||||
}
|
||||
columnType := gstr.Replace(field.(*gdb.TableField).Type, "("+length+")", "")
|
||||
column := &ColumnInfo{
|
||||
Comment: comment,
|
||||
Length: "",
|
||||
Nullable: field.(*gdb.TableField).Null,
|
||||
PropertyName: field.(*gdb.TableField).Name,
|
||||
Type: columnType,
|
||||
}
|
||||
columns = append(columns, column)
|
||||
}
|
||||
return
|
||||
}
|
||||
91
common/cool/cool.go
Normal file
91
common/cool/cool.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package cool
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gredis"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/i18n/gi18n"
|
||||
"github.com/gogf/gf/v2/os/gbuild"
|
||||
"github.com/gogf/gf/v2/os/gcache"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/util/guid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var (
|
||||
GormDBS = make(map[string]*gorm.DB) // 定义全局gorm.DB对象集合 仅供内部使用
|
||||
CacheEPS = gcache.New() // 定义全局缓存对象 供EPS使用
|
||||
CacheManager = gcache.New() // 定义全局缓存对象 供其他业务使用
|
||||
ProcessFlag = guid.S() // 定义全局进程标识
|
||||
RunMode = "dev" // 定义全局运行模式
|
||||
IsRedisMode = false // 定义全局是否为redis模式
|
||||
I18n = gi18n.New() // 定义全局国际化对象
|
||||
)
|
||||
|
||||
func init() {
|
||||
var (
|
||||
ctx = gctx.GetInitCtx()
|
||||
redisConfig = &gredis.Config{}
|
||||
)
|
||||
g.Log().Debug(ctx, "module cool init start ...")
|
||||
buildData := gbuild.Data()
|
||||
if _, ok := buildData["mode"]; ok {
|
||||
RunMode = buildData["mode"].(string)
|
||||
}
|
||||
if RunMode == "cool-tools" {
|
||||
return
|
||||
}
|
||||
redisVar, err := g.Cfg().Get(ctx, "redis.cool")
|
||||
if err != nil {
|
||||
g.Log().Error(ctx, "初始化缓存失败,请检查配置文件")
|
||||
panic(err)
|
||||
}
|
||||
if !redisVar.IsEmpty() {
|
||||
redisVar.Struct(redisConfig)
|
||||
redis, err := gredis.New(redisConfig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
CacheManager.SetAdapter(gcache.NewAdapterRedis(redis))
|
||||
IsRedisMode = true
|
||||
}
|
||||
g.Log().Debug(ctx, "当前运行模式", RunMode)
|
||||
g.Log().Debug(ctx, "当前实例ID:", ProcessFlag)
|
||||
g.Log().Debug(ctx, "是否缓存模式:", IsRedisMode)
|
||||
g.Log().Debug(ctx, "module cool init finished ...")
|
||||
|
||||
}
|
||||
|
||||
// cool.OK 正常返回
|
||||
type BaseRes struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// 返回正常结果
|
||||
func Ok(data interface{}) *BaseRes {
|
||||
|
||||
return &BaseRes{
|
||||
Code: 1000,
|
||||
Message: I18n.Translate(context.TODO(), "BaseResMessage"),
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// 失败返回结果
|
||||
func Fail(message string) *BaseRes {
|
||||
return &BaseRes{
|
||||
Code: 1001,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
// 分布式函数
|
||||
// func DistributedFunc(ctx g.Ctx, f func(ctx g.Ctx) (interface{}, error)) (interface{}, error) {
|
||||
// if ProcessFlag == ctx.Request.Header.Get("processFlag") {
|
||||
// return f(ctx)
|
||||
// }
|
||||
// return nil, nil
|
||||
// }
|
||||
64
common/cool/coolconfig/config.go
Normal file
64
common/cool/coolconfig/config.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package coolconfig
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
// cool config
|
||||
type sConfig struct {
|
||||
AutoMigrate bool `json:"auto_migrate,omitempty"` // 是否自动创建表
|
||||
Eps bool `json:"eps,omitempty"` // 是否开启eps
|
||||
File *file `json:"file,omitempty"` // 文件上传配置
|
||||
}
|
||||
|
||||
// OSS相关配置
|
||||
type oss struct {
|
||||
Endpoint string `json:"endpoint"`
|
||||
AccessKeyID string `json:"accessKeyID"`
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
UseSSL bool `json:"useSSL"`
|
||||
BucketName string `json:"bucketName"`
|
||||
Location string `json:"location"`
|
||||
}
|
||||
|
||||
// 文件上传配置
|
||||
type file struct {
|
||||
Mode string `json:"mode"` // 模式 local oss
|
||||
Domain string `json:"domain"` // 域名 http://
|
||||
Oss *oss `json:"oss,omitempty"`
|
||||
}
|
||||
|
||||
// NewConfig new config
|
||||
func newConfig() *sConfig {
|
||||
var ctx g.Ctx
|
||||
config := &sConfig{
|
||||
AutoMigrate: GetCfgWithDefault(ctx, "cool.autoMigrate", g.NewVar(false)).Bool(),
|
||||
Eps: GetCfgWithDefault(ctx, "cool.eps", g.NewVar(false)).Bool(),
|
||||
File: &file{
|
||||
Mode: GetCfgWithDefault(ctx, "cool.file.mode", g.NewVar("none")).String(),
|
||||
Domain: GetCfgWithDefault(ctx, "cool.file.domain", g.NewVar("http://127.0.0.1:8300")).String(),
|
||||
Oss: &oss{
|
||||
Endpoint: GetCfgWithDefault(ctx, "cool.file.oss.endpoint", g.NewVar("127.0.0.1:9000")).String(),
|
||||
AccessKeyID: GetCfgWithDefault(ctx, "cool.file.oss.accessKeyID", g.NewVar("")).String(),
|
||||
SecretAccessKey: GetCfgWithDefault(ctx, "cool.file.oss.secretAccessKey", g.NewVar("")).String(),
|
||||
UseSSL: GetCfgWithDefault(ctx, "cool.file.oss.useSSL", g.NewVar(false)).Bool(),
|
||||
BucketName: GetCfgWithDefault(ctx, "cool.file.oss.bucketName", g.NewVar("cool-admin-go")).String(),
|
||||
Location: GetCfgWithDefault(ctx, "cool.file.oss.location", g.NewVar("us-east-1")).String(),
|
||||
},
|
||||
},
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
// Config config
|
||||
var Config = newConfig()
|
||||
|
||||
// GetCfgWithDefault get config with default value
|
||||
func GetCfgWithDefault(ctx g.Ctx, key string, defaultValue *g.Var) *g.Var {
|
||||
value, err := g.Cfg().GetWithEnv(ctx, key)
|
||||
if err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
if value.IsEmpty() || value.IsNil() {
|
||||
return defaultValue
|
||||
}
|
||||
return value
|
||||
}
|
||||
40
common/cool/cooldb/cooldb.go
Normal file
40
common/cool/cooldb/cooldb.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package cooldb
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Driver 数据库驱动通用接口
|
||||
type Driver interface {
|
||||
GetConn(node *gdb.ConfigNode) (db *gorm.DB, err error) // 获取数据库连接
|
||||
}
|
||||
|
||||
var (
|
||||
driverMap = map[string]Driver{} // driverMap is the map for registered database drivers.
|
||||
)
|
||||
|
||||
// GetConn returns the connection object of specified database driver.
|
||||
func GetConn(node *gdb.ConfigNode) (db *gorm.DB, err error) {
|
||||
if driver, ok := driverMap[node.Type]; ok {
|
||||
return driver.GetConn(node)
|
||||
}
|
||||
errorMsg := "\n"
|
||||
errorMsg += `cannot find database driver for specified database type "%s"`
|
||||
errorMsg += `, did you misspell type name "%s" or forget importing the database driver? `
|
||||
errorMsg += `possible reference: https://blazing/tree/master/contrib/drivers`
|
||||
// 换行
|
||||
errorMsg += "\n"
|
||||
errorMsg += `无法找到指定数据库类型的数据库驱动 "%s"`
|
||||
errorMsg += `,您是否拼写错误了类型名称 "%s" 或者忘记导入数据库驱动?`
|
||||
errorMsg += `参考:https://blazing/contrib/drivers`
|
||||
err = gerror.Newf(errorMsg, node.Type, node.Type, node.Type, node.Type)
|
||||
return
|
||||
}
|
||||
|
||||
// Register registers custom database driver to gdb.
|
||||
func Register(name string, driver Driver) error {
|
||||
driverMap[name] = driver
|
||||
return nil
|
||||
}
|
||||
49
common/cool/coolfile/coolfile.go
Normal file
49
common/cool/coolfile/coolfile.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package coolfile
|
||||
|
||||
import (
|
||||
"blazing/cool/coolconfig"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
type Driver interface {
|
||||
New() Driver
|
||||
GetMode() (data interface{}, err error)
|
||||
Upload(ctx g.Ctx) (string, error)
|
||||
}
|
||||
|
||||
var (
|
||||
// FileMap is the map for registered file drivers.
|
||||
FileMap = map[string]Driver{}
|
||||
)
|
||||
|
||||
func NewFile() (d Driver) {
|
||||
if driver, ok := FileMap[coolconfig.Config.File.Mode]; ok {
|
||||
return driver.New()
|
||||
}
|
||||
errorMsg := "\n"
|
||||
errorMsg += `无法找到指定文件上传类型 "%s"`
|
||||
errorMsg += `,您是否拼写错误了类型名称 "%s" 或者忘记导入上传支持包?`
|
||||
errorMsg += `参考:https://blazing/tree/master/contrib/files`
|
||||
err := gerror.Newf(errorMsg, coolconfig.Config.File.Mode, coolconfig.Config.File.Mode)
|
||||
|
||||
panic(err)
|
||||
|
||||
}
|
||||
|
||||
// Register registers custom file driver to cool.
|
||||
func Register(name string, driver Driver) error {
|
||||
FileMap[name] = driver
|
||||
return nil
|
||||
}
|
||||
|
||||
// func init() {
|
||||
// // Register("local", &Local{})
|
||||
// // Register("oss", &Oss{})
|
||||
// file, err := NewFile()
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// File = file
|
||||
// }
|
||||
37
common/cool/ctx.go
Normal file
37
common/cool/ctx.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package cool
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
)
|
||||
|
||||
type Claims struct {
|
||||
IsRefresh bool `json:"isRefresh"`
|
||||
RoleIds []string `json:"roleIds"`
|
||||
Username string `json:"username"`
|
||||
UserId uint `json:"userId"`
|
||||
PasswordVersion *int32 `json:"passwordVersion"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
type Admin struct {
|
||||
IsRefresh bool `json:"isRefresh"`
|
||||
RoleIds []string `json:"roleIds"`
|
||||
Username string `json:"username"`
|
||||
UserId uint `json:"userId"`
|
||||
PasswordVersion *int32 `json:"passwordVersion"`
|
||||
}
|
||||
|
||||
// 获取传入ctx 中的 admin 对象
|
||||
func GetAdmin(ctx context.Context) *Admin {
|
||||
r := g.RequestFromCtx(ctx)
|
||||
admin := &Admin{}
|
||||
err := gjson.New(r.GetCtxVar("admin").String()).Scan(admin)
|
||||
if err != nil {
|
||||
g.Log().Error(ctx, err)
|
||||
}
|
||||
return admin
|
||||
}
|
||||
16
common/cool/db.go
Normal file
16
common/cool/db.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package cool
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// Deprecated 请使用 cool.DBM 替代
|
||||
func GDBM(m IModel) *gdb.Model {
|
||||
return g.DB(m.GroupName()).Model(m.TableName())
|
||||
}
|
||||
|
||||
// DBM 根据model获取 *gdb.Model
|
||||
func DBM(m IModel) *gdb.Model {
|
||||
return g.DB(m.GroupName()).Model(m.TableName())
|
||||
}
|
||||
7
common/cool/file.go
Normal file
7
common/cool/file.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package cool
|
||||
|
||||
import "blazing/cool/coolfile"
|
||||
|
||||
var (
|
||||
File = coolfile.NewFile // File 文件上传操作
|
||||
)
|
||||
103
common/cool/func.go
Normal file
103
common/cool/func.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package cool
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
type CoolFunc interface {
|
||||
// Func handler
|
||||
Func(ctx g.Ctx, param string) (err error)
|
||||
// IsSingleton 是否单例,当为true时,只能有一个任务在执行,在注意函数为计划任务时使用
|
||||
IsSingleton() bool
|
||||
// IsAllWorker 是否所有worker都执行
|
||||
IsAllWorker() bool
|
||||
}
|
||||
|
||||
// FuncMap 函数列表
|
||||
var FuncMap = make(map[string]CoolFunc)
|
||||
|
||||
// RegisterFunc 注册函数
|
||||
func RegisterFunc(name string, f CoolFunc) {
|
||||
FuncMap[name] = f
|
||||
}
|
||||
|
||||
// GetFunc 获取函数
|
||||
func GetFunc(name string) CoolFunc {
|
||||
return FuncMap[name]
|
||||
}
|
||||
|
||||
// RunFunc 运行函数
|
||||
func RunFunc(ctx g.Ctx, funcstring string) (err error) {
|
||||
funcName := gstr.SubStr(funcstring, 0, gstr.Pos(funcstring, "("))
|
||||
funcParam := gstr.SubStr(funcstring, gstr.Pos(funcstring, "(")+1, gstr.Pos(funcstring, ")")-gstr.Pos(funcstring, "(")-1)
|
||||
if _, ok := FuncMap[funcName]; !ok {
|
||||
err = gerror.New("函数不存在:" + funcName)
|
||||
return
|
||||
}
|
||||
if !FuncMap[funcName].IsAllWorker() {
|
||||
// 检查当前是否为主进程, 如果不是主进程, 则不执行
|
||||
if ProcessFlag != CacheManager.MustGetOrSet(ctx, "cool:masterflag", ProcessFlag, 60*time.Second).String() {
|
||||
g.Log().Debug(ctx, "当前进程不是主进程, 不执行单例函数", funcName)
|
||||
return
|
||||
}
|
||||
}
|
||||
err = FuncMap[funcName].Func(ctx, funcParam)
|
||||
return
|
||||
}
|
||||
|
||||
// ClusterRunFunc 集群运行函数,如果是单机模式, 则直接运行函数
|
||||
func ClusterRunFunc(ctx g.Ctx, funcstring string) (err error) {
|
||||
if IsRedisMode {
|
||||
conn, err := g.Redis("cool").Conn(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close(ctx)
|
||||
_, err = conn.Do(ctx, "publish", "cool:func", funcstring)
|
||||
return err
|
||||
} else {
|
||||
return RunFunc(ctx, funcstring)
|
||||
}
|
||||
}
|
||||
|
||||
// ListenFunc 监听函数
|
||||
func ListenFunc(ctx g.Ctx) {
|
||||
if IsRedisMode {
|
||||
conn, err := g.Redis("cool").Conn(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer conn.Close(ctx)
|
||||
_, err = conn.Do(ctx, "subscribe", "cool:func")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for {
|
||||
data, err := conn.Receive(ctx)
|
||||
if err != nil {
|
||||
g.Log().Error(ctx, err)
|
||||
time.Sleep(10 * time.Second)
|
||||
continue
|
||||
}
|
||||
if data != nil {
|
||||
dataMap := data.MapStrStr()
|
||||
if dataMap["Kind"] == "subscribe" {
|
||||
continue
|
||||
}
|
||||
if dataMap["Channel"] == "cool:func" {
|
||||
g.Log().Debug(ctx, "执行函数", dataMap["Payload"])
|
||||
err := RunFunc(ctx, dataMap["Payload"])
|
||||
if err != nil {
|
||||
g.Log().Error(ctx, "执行函数失败", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic(gerror.New("集群模式下, 请使用Redis作为缓存"))
|
||||
}
|
||||
}
|
||||
39
common/cool/go.mod
Normal file
39
common/cool/go.mod
Normal file
@@ -0,0 +1,39 @@
|
||||
module blazing/cool
|
||||
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/v2 v2.6.3
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
gorm.io/gorm v1.25.7
|
||||
)
|
||||
|
||||
require go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||
github.com/clbanning/mxj/v2 v2.7.0 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/gorilla/websocket v1.5.1 // indirect
|
||||
github.com/grokify/html-strip-tags-go v0.1.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||
golang.org/x/net v0.21.0 // indirect
|
||||
golang.org/x/sys v0.17.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
80
common/cool/go.sum
Normal file
80
common/cool/go.sum
Normal file
@@ -0,0 +1,80 @@
|
||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
|
||||
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/gogf/gf/v2 v2.6.3 h1:DoqeuwU98wotpFoDSQEx8RZbmJdK8KdGiJtzJeqpyIo=
|
||||
github.com/gogf/gf/v2 v2.6.3/go.mod h1:x2XONYcI4hRQ/4gMNbWHmZrNzSEIg20s2NULbzom5k0=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4=
|
||||
github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
||||
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
||||
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
|
||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A=
|
||||
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
93
common/cool/initdb.go
Normal file
93
common/cool/initdb.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package cool
|
||||
|
||||
import (
|
||||
"blazing/cool/cooldb"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gres"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// 初始化数据库连接供gorm使用
|
||||
func InitDB(group string) (*gorm.DB, error) {
|
||||
// var ctx context.Context
|
||||
var db *gorm.DB
|
||||
// 如果group为空,则使用默认的group,否则使用group参数
|
||||
if group == "" {
|
||||
group = "default"
|
||||
}
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
panic("failed to connect database")
|
||||
}
|
||||
}()
|
||||
config := g.DB(group).GetConfig()
|
||||
db, err := cooldb.GetConn(config)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
GormDBS[group] = db
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// 根据entity结构体获取 *gorm.DB
|
||||
func getDBbyModel(model IModel) *gorm.DB {
|
||||
|
||||
group := model.GroupName()
|
||||
// 判断是否存在 GormDBS[group] 字段,如果存在,则使用该字段的值作为DB,否则初始化DB
|
||||
if _, ok := GormDBS[group]; ok {
|
||||
return GormDBS[group]
|
||||
} else {
|
||||
|
||||
db, err := InitDB(group)
|
||||
if err != nil {
|
||||
panic("failed to connect database")
|
||||
}
|
||||
// 把重新初始化的GormDBS存入全局变量中
|
||||
GormDBS[group] = db
|
||||
return db
|
||||
}
|
||||
}
|
||||
|
||||
// 根据entity结构体创建表
|
||||
func CreateTable(model IModel) error {
|
||||
if Config.AutoMigrate {
|
||||
db := getDBbyModel(model)
|
||||
return db.AutoMigrate(model)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FillInitData 数据库填充初始数据
|
||||
func FillInitData(ctx g.Ctx, moduleName string, model IModel) error {
|
||||
mInit := g.DB("default").Model("base_sys_init")
|
||||
n, err := mInit.Clone().Where("group", model.GroupName()).Where("table", model.TableName()).Count()
|
||||
if err != nil {
|
||||
g.Log().Error(ctx, "读取表 base_sys_init 失败 ", err.Error())
|
||||
return err
|
||||
}
|
||||
if n > 0 {
|
||||
g.Log().Debug(ctx, "分组", model.GroupName(), "中的表", model.TableName(), "已经初始化过,跳过本次初始化.")
|
||||
return nil
|
||||
}
|
||||
m := g.DB(model.GroupName()).Model(model.TableName())
|
||||
jsonData, _ := gjson.LoadContent(gres.GetContent("modules/" + moduleName + "/resource/initjson/" + model.TableName() + ".json"))
|
||||
if jsonData.Var().Clone().IsEmpty() {
|
||||
g.Log().Debug(ctx, "分组", model.GroupName(), "中的表", model.TableName(), "无可用的初始化数据,跳过本次初始化.")
|
||||
return nil
|
||||
}
|
||||
_, err = m.Data(jsonData).Insert()
|
||||
if err != nil {
|
||||
g.Log().Error(ctx, err.Error())
|
||||
return err
|
||||
}
|
||||
_, err = mInit.Insert(g.Map{"group": model.GroupName(), "table": model.TableName()})
|
||||
if err != nil {
|
||||
g.Log().Error(ctx, err.Error())
|
||||
return err
|
||||
}
|
||||
g.Log().Info(ctx, "分组", model.GroupName(), "中的表", model.TableName(), "初始化完成.")
|
||||
return nil
|
||||
}
|
||||
1
common/cool/middleware.go
Normal file
1
common/cool/middleware.go
Normal file
@@ -0,0 +1 @@
|
||||
package cool
|
||||
79
common/cool/middleware_handler_response.go
Normal file
79
common/cool/middleware_handler_response.go
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package cool
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
)
|
||||
|
||||
// DefaultHandlerResponse is the default implementation of HandlerResponse.
|
||||
type DefaultHandlerResponse struct {
|
||||
Code int `json:"code" dc:"Error code"`
|
||||
Message string `json:"message" dc:"Error message"`
|
||||
Data interface{} `json:"data,omitempty" dc:"Result data for certain request according API definition"`
|
||||
}
|
||||
|
||||
// MiddlewareHandlerResponse is the default middleware handling handler response object and its error.
|
||||
func MiddlewareHandlerResponse(r *ghttp.Request) {
|
||||
r.Middleware.Next()
|
||||
|
||||
// There's custom buffer content, it then exits current handler.
|
||||
if r.Response.BufferLength() > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
// ctx g.Ctx
|
||||
msg string
|
||||
err = r.GetError()
|
||||
res = r.GetHandlerResponse()
|
||||
code = gerror.Code(err).Code()
|
||||
)
|
||||
|
||||
// g.Log().Debug(ctx, code, msg, res)
|
||||
msg = "success"
|
||||
|
||||
if err != nil {
|
||||
if code == -1 {
|
||||
code = 1001
|
||||
}
|
||||
msg = err.Error()
|
||||
} else if r.Response.Status > 0 && r.Response.Status != http.StatusOK {
|
||||
msg = http.StatusText(r.Response.Status)
|
||||
switch r.Response.Status {
|
||||
case http.StatusNotFound:
|
||||
code = 404
|
||||
case http.StatusForbidden:
|
||||
code = 403
|
||||
default:
|
||||
code = 500
|
||||
}
|
||||
} else {
|
||||
// code = gcode.CodeOK
|
||||
code = 1000
|
||||
}
|
||||
// 做一些code转换适配cooladmin的错误码
|
||||
switch code {
|
||||
case 51: // 参数错误
|
||||
code = 51
|
||||
case 50: // 内部错误
|
||||
code = 1003
|
||||
default:
|
||||
}
|
||||
// g.Log().Debug(ctx, code, msg, res)
|
||||
// 如果是正常返回,直接返回res
|
||||
if code == 1000 && r.Response.Status == 200 {
|
||||
r.Response.WriteJsonExit(res)
|
||||
}
|
||||
r.Response.WriteJson(DefaultHandlerResponse{
|
||||
Code: code,
|
||||
Message: msg,
|
||||
})
|
||||
}
|
||||
35
common/cool/model.go
Normal file
35
common/cool/model.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package cool
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type IModel interface {
|
||||
TableName() string
|
||||
GroupName() string
|
||||
}
|
||||
type Model struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
CreateTime time.Time `gorm:"column:createTime;not null;index,priority:1;autoCreateTime:nano;comment:创建时间" json:"createTime"` // 创建时间
|
||||
UpdateTime time.Time `gorm:"column:updateTime;not null;index,priority:1;autoUpdateTime:nano;comment:更新时间" json:"updateTime"` // 更新时间
|
||||
DeletedAt time.Time `gorm:"index" json:"deletedAt"`
|
||||
}
|
||||
|
||||
// 返回表名
|
||||
func (m *Model) TableName() string {
|
||||
return "this_table_should_not_exist"
|
||||
}
|
||||
|
||||
// 返回分组名
|
||||
func (m *Model) GroupName() string {
|
||||
return "default"
|
||||
}
|
||||
|
||||
func NewModel() *Model {
|
||||
return &Model{
|
||||
ID: 0,
|
||||
CreateTime: time.Time{},
|
||||
UpdateTime: time.Time{},
|
||||
DeletedAt: time.Time{},
|
||||
}
|
||||
}
|
||||
429
common/cool/service.go
Normal file
429
common/cool/service.go
Normal file
@@ -0,0 +1,429 @@
|
||||
package cool
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type IService interface {
|
||||
ServiceAdd(ctx context.Context, req *AddReq) (data interface{}, err error) // 新增
|
||||
ServiceDelete(ctx context.Context, req *DeleteReq) (data interface{}, err error) // 删除
|
||||
ServiceUpdate(ctx context.Context, req *UpdateReq) (data interface{}, err error) // 修改
|
||||
ServiceInfo(ctx context.Context, req *InfoReq) (data interface{}, err error) // 详情
|
||||
ServiceList(ctx context.Context, req *ListReq) (data interface{}, err error) // 列表
|
||||
ServicePage(ctx context.Context, req *PageReq) (data interface{}, err error) // 分页
|
||||
ModifyBefore(ctx context.Context, method string, param g.MapStrAny) (err error) // 新增|删除|修改前的操作
|
||||
ModifyAfter(ctx context.Context, method string, param g.MapStrAny) (err error) // 新增|删除|修改后的操作
|
||||
GetModel() IModel // 获取model
|
||||
}
|
||||
type Service struct {
|
||||
Model IModel
|
||||
ListQueryOp *QueryOp
|
||||
PageQueryOp *QueryOp
|
||||
InsertParam func(ctx context.Context) g.MapStrAny // Add时插入参数
|
||||
Before func(ctx context.Context) (err error) // CRUD前的操作
|
||||
InfoIgnoreProperty string // Info时忽略的字段,多个字段用逗号隔开
|
||||
UniqueKey g.MapStrStr // 唯一键 key:字段名 value:错误信息
|
||||
NotNullKey g.MapStrStr // 非空键 key:字段名 value:错误信息
|
||||
}
|
||||
|
||||
// List/Add接口条件配置
|
||||
type QueryOp struct {
|
||||
FieldEQ []string // 字段等于
|
||||
KeyWordField []string // 模糊搜索匹配的数据库字段
|
||||
AddOrderby g.MapStrStr // 添加排序
|
||||
Where func(ctx context.Context) []g.Array // 自定义条件
|
||||
Select string // 查询字段,多个字段用逗号隔开 如: id,name 或 a.id,a.name,b.name AS bname
|
||||
Join []*JoinOp // 关联查询
|
||||
Extend func(ctx g.Ctx, m *gdb.Model) *gdb.Model // 追加其他条件
|
||||
ModifyResult func(ctx g.Ctx, data interface{}) interface{} // 修改结果
|
||||
}
|
||||
|
||||
// JoinOp 关联查询
|
||||
type JoinOp struct {
|
||||
Model IModel // 关联的model
|
||||
Alias string // 别名
|
||||
Condition string // 关联条件
|
||||
Type JoinType // 关联类型 LeftJoin RightJoin InnerJoin
|
||||
}
|
||||
|
||||
// JoinType 关联类型
|
||||
type JoinType string
|
||||
|
||||
// ServiceAdd 新增
|
||||
func (s *Service) ServiceAdd(ctx context.Context, req *AddReq) (data interface{}, err error) {
|
||||
r := g.RequestFromCtx(ctx)
|
||||
|
||||
rmap := r.GetMap()
|
||||
// 非空键
|
||||
if s.NotNullKey != nil {
|
||||
for k, v := range s.NotNullKey {
|
||||
if rmap[k] == nil {
|
||||
return nil, gerror.New(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 唯一键
|
||||
if s.UniqueKey != nil {
|
||||
for k, v := range s.UniqueKey {
|
||||
if rmap[k] != nil {
|
||||
m := DBM(s.Model)
|
||||
count, err := m.Where(k, rmap[k]).Count()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count > 0 {
|
||||
err = gerror.New(v)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if s.InsertParam != nil {
|
||||
insertParams := s.InsertParam(ctx)
|
||||
if len(insertParams) > 0 {
|
||||
for k, v := range insertParams {
|
||||
rmap[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
m := DBM(s.Model)
|
||||
lastInsertId, err := m.Data(rmap).InsertAndGetId()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
data = g.Map{"id": lastInsertId}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ServiceDelete 删除
|
||||
func (s *Service) ServiceDelete(ctx context.Context, req *DeleteReq) (data interface{}, err error) {
|
||||
ids := g.RequestFromCtx(ctx).Get("ids").Slice()
|
||||
m := g.DB(s.Model.GroupName()).Model(s.Model.TableName())
|
||||
data, err = m.WhereIn("id", ids).Delete()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ServiceUpdate 修改
|
||||
func (s *Service) ServiceUpdate(ctx context.Context, req *UpdateReq) (data interface{}, err error) {
|
||||
r := g.RequestFromCtx(ctx)
|
||||
rmap := r.GetMap()
|
||||
if rmap["id"] == nil {
|
||||
err = gerror.New("id不能为空")
|
||||
return
|
||||
}
|
||||
if s.UniqueKey != nil {
|
||||
for k, v := range s.UniqueKey {
|
||||
if rmap[k] != nil {
|
||||
count, err := DBM(s.Model).Where(k, rmap[k]).WhereNot("id", rmap["id"]).Count()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count > 0 {
|
||||
err = gerror.New(v)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m := DBM(s.Model)
|
||||
_, err = m.Data(rmap).Where("id", rmap["id"]).Update()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Service) ServiceInfo(ctx context.Context, req *InfoReq) (data interface{}, err error) {
|
||||
if s.Before != nil {
|
||||
err = s.Before(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
m := g.DB(s.Model.GroupName()).Model(s.Model.TableName())
|
||||
// 如果InfoIgnoreProperty不为空 则忽略相关字段
|
||||
if len(s.InfoIgnoreProperty) > 0 {
|
||||
m = m.FieldsEx(s.InfoIgnoreProperty)
|
||||
}
|
||||
data, err = m.Clone().Where("id", req.Id).One()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Service) ServiceList(ctx context.Context, req *ListReq) (data interface{}, err error) {
|
||||
if s.Before != nil {
|
||||
err = s.Before(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
r := g.RequestFromCtx(ctx)
|
||||
m := g.DB(s.Model.GroupName()).Model(s.Model.TableName())
|
||||
|
||||
// 如果 req.Order 和 req.Sort 均不为空 则添加排序
|
||||
if !r.Get("order").IsEmpty() && !r.Get("sort").IsEmpty() {
|
||||
m.Order(r.Get("order").String() + " " + r.Get("sort").String())
|
||||
// m.OrderDesc("orderNum")
|
||||
}
|
||||
// 如果 ListQueryOp 不为空 则使用 ListQueryOp 进行查询
|
||||
if s.ListQueryOp != nil {
|
||||
if Select := s.ListQueryOp.Select; Select != "" {
|
||||
m.Fields(Select)
|
||||
}
|
||||
// 如果Join不为空 则添加Join
|
||||
if len(s.ListQueryOp.Join) > 0 {
|
||||
for _, join := range s.ListQueryOp.Join {
|
||||
switch join.Type {
|
||||
case LeftJoin:
|
||||
m.LeftJoin(join.Model.TableName(), join.Condition).As(join.Alias)
|
||||
case RightJoin:
|
||||
m.RightJoin(join.Model.TableName(), join.Condition).As(join.Alias)
|
||||
case InnerJoin:
|
||||
m.InnerJoin(join.Model.TableName(), join.Condition).As(join.Alias)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果fileldEQ不为空 则添加查询条件
|
||||
if len(s.ListQueryOp.FieldEQ) > 0 {
|
||||
for _, field := range s.ListQueryOp.FieldEQ {
|
||||
if !r.Get(field).IsEmpty() {
|
||||
m.Where(field, r.Get(field))
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果KeyWordField不为空 则添加查询条件
|
||||
if !r.Get("keyWord").IsEmpty() {
|
||||
if len(s.ListQueryOp.KeyWordField) > 0 {
|
||||
builder := m.Builder()
|
||||
for _, field := range s.ListQueryOp.KeyWordField {
|
||||
// g.DumpWithType(field)
|
||||
// builder.WhereLike(field, "%"+r.Get("keyWord").String()+"%")
|
||||
builder = builder.WhereOrLike(field, "%"+r.Get("keyWord").String()+"%")
|
||||
}
|
||||
m.Where(builder)
|
||||
}
|
||||
}
|
||||
if s.ListQueryOp.Where != nil {
|
||||
where := s.ListQueryOp.Where(ctx)
|
||||
if len(where) > 0 {
|
||||
for _, v := range where {
|
||||
if len(v) == 3 {
|
||||
if gconv.Bool(v[2]) {
|
||||
m.Where(v[0], v[1])
|
||||
}
|
||||
}
|
||||
if len(v) == 2 {
|
||||
m.Where(v[0], v[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果ListQueryOp的Extend不为空 则执行Extend
|
||||
if s.ListQueryOp.Extend != nil {
|
||||
m = s.ListQueryOp.Extend(ctx, m)
|
||||
}
|
||||
// 如果 addOrderby 不为空 则添加排序
|
||||
if len(s.ListQueryOp.AddOrderby) > 0 && r.Get("order").IsEmpty() && r.Get("sort").IsEmpty() {
|
||||
for field, order := range s.ListQueryOp.AddOrderby {
|
||||
m.Order(field, order)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 增加默认数据限制,防止查询所有数据
|
||||
m.Limit(10000)
|
||||
|
||||
result, err := m.All()
|
||||
if err != nil {
|
||||
g.Log().Error(ctx, "ServiceList error:", err)
|
||||
}
|
||||
if result == nil {
|
||||
data = garray.New()
|
||||
} else {
|
||||
data = result
|
||||
}
|
||||
if s.ListQueryOp != nil {
|
||||
if s.ListQueryOp.ModifyResult != nil {
|
||||
data = s.ListQueryOp.ModifyResult(ctx, data)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Service) ServicePage(ctx context.Context, req *PageReq) (data interface{}, err error) {
|
||||
var (
|
||||
r = g.RequestFromCtx(ctx)
|
||||
total = 0
|
||||
)
|
||||
|
||||
type pagination struct {
|
||||
Page int `json:"page"`
|
||||
Size int `json:"size"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
if req.Size <= 0 {
|
||||
req.Size = 10
|
||||
}
|
||||
if req.Page <= 0 {
|
||||
req.Page = 1
|
||||
}
|
||||
m := g.DB(s.Model.GroupName()).Model(s.Model.TableName())
|
||||
|
||||
// 如果pageQueryOp不为空 则使用pageQueryOp进行查询
|
||||
if s.PageQueryOp != nil {
|
||||
|
||||
// 如果Join不为空 则添加Join
|
||||
if len(s.PageQueryOp.Join) > 0 {
|
||||
for _, join := range s.PageQueryOp.Join {
|
||||
switch join.Type {
|
||||
case LeftJoin:
|
||||
m.LeftJoin(join.Model.TableName(), join.Condition).As(join.Alias)
|
||||
case RightJoin:
|
||||
m.RightJoin(join.Model.TableName(), join.Condition).As(join.Alias)
|
||||
case InnerJoin:
|
||||
m.InnerJoin(join.Model.TableName(), join.Condition).As(join.Alias)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果fileldEQ不为空 则添加查询条件
|
||||
if len(s.PageQueryOp.FieldEQ) > 0 {
|
||||
for _, field := range s.PageQueryOp.FieldEQ {
|
||||
if !r.Get(field).IsEmpty() {
|
||||
m.Where(field, r.Get(field))
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果KeyWordField不为空 则添加查询条件
|
||||
if !r.Get("keyWord").IsEmpty() {
|
||||
if len(s.PageQueryOp.KeyWordField) > 0 {
|
||||
builder := m.Builder()
|
||||
for _, field := range s.PageQueryOp.KeyWordField {
|
||||
// g.DumpWithType(field)
|
||||
// builder.WhereLike(field, "%"+r.Get("keyWord").String()+"%")
|
||||
builder = builder.WhereOrLike(field, "%"+r.Get("keyWord").String()+"%")
|
||||
}
|
||||
m.Where(builder)
|
||||
}
|
||||
}
|
||||
// 加入where条件
|
||||
if s.PageQueryOp.Where != nil {
|
||||
where := s.PageQueryOp.Where(ctx)
|
||||
if len(where) > 0 {
|
||||
for _, v := range where {
|
||||
if len(v) == 3 {
|
||||
if gconv.Bool(v[2]) {
|
||||
m.Where(v[0], v[1])
|
||||
}
|
||||
}
|
||||
if len(v) == 2 {
|
||||
m.Where(v[0], v[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果PageQueryOp的Extend不为空 则执行Extend
|
||||
if s.PageQueryOp.Extend != nil {
|
||||
m = s.PageQueryOp.Extend(ctx, m)
|
||||
}
|
||||
|
||||
// 如果 addOrderby 不为空 则添加排序
|
||||
if len(s.PageQueryOp.AddOrderby) > 0 && r.Get("order").IsEmpty() && r.Get("sort").IsEmpty() {
|
||||
for field, order := range s.PageQueryOp.AddOrderby {
|
||||
m.Order(field, order)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 统计总数
|
||||
total, err = m.Clone().Count()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if s.PageQueryOp != nil {
|
||||
if Select := s.PageQueryOp.Select; Select != "" {
|
||||
m.Fields(Select)
|
||||
}
|
||||
}
|
||||
// 如果 req.Order 和 req.Sort 均不为空 则添加排序
|
||||
if !r.Get("order").IsEmpty() && !r.Get("sort").IsEmpty() {
|
||||
m.Order(r.Get("order").String() + " " + r.Get("sort").String())
|
||||
}
|
||||
|
||||
// 如果req.IsExport为true 则导出数据
|
||||
if req.IsExport {
|
||||
// 如果req.MaxExportSize大于0 则限制导出数据的最大条数
|
||||
if req.MaxExportLimit > 0 {
|
||||
m.Limit(req.MaxExportLimit)
|
||||
}
|
||||
result, err := m.All()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = g.Map{
|
||||
"list": result,
|
||||
"total": total,
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
result, err := m.Offset((req.Page - 1) * req.Size).Limit(req.Size).All()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result != nil {
|
||||
data = g.Map{
|
||||
"list": result,
|
||||
"pagination": pagination{
|
||||
Page: req.Page,
|
||||
Size: req.Size,
|
||||
Total: total,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
data = g.Map{
|
||||
"list": garray.New(),
|
||||
"pagination": pagination{
|
||||
Page: req.Page,
|
||||
Size: req.Size,
|
||||
Total: total,
|
||||
},
|
||||
}
|
||||
}
|
||||
if s.PageQueryOp != nil {
|
||||
if s.PageQueryOp.ModifyResult != nil {
|
||||
data = s.PageQueryOp.ModifyResult(ctx, data)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ModifyBefore 新增|删除|修改前的操作
|
||||
func (s *Service) ModifyBefore(ctx context.Context, method string, param g.MapStrAny) (err error) {
|
||||
// g.Log().Debugf(ctx, "ModifyBefore: %s", method)
|
||||
return
|
||||
}
|
||||
|
||||
// ModifyAfter 新增|删除|修改后的操作
|
||||
func (s *Service) ModifyAfter(ctx context.Context, method string, param g.MapStrAny) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// GetModel 获取model
|
||||
func (s *Service) GetModel() IModel {
|
||||
return s.Model
|
||||
}
|
||||
|
||||
// NewService 新建一个service
|
||||
func NewService(model IModel) *Service {
|
||||
return &Service{
|
||||
Model: model,
|
||||
}
|
||||
}
|
||||
217
common/core/info/LoginUserInfo.go
Normal file
217
common/core/info/LoginUserInfo.go
Normal file
@@ -0,0 +1,217 @@
|
||||
package info
|
||||
|
||||
// Point 表示坐标结构
|
||||
type Point struct {
|
||||
X uint32
|
||||
Y uint32
|
||||
}
|
||||
|
||||
// TeamInfo 战队信息
|
||||
type TeamInfo struct {
|
||||
// 此处应包含TeamInfo的具体字段,原Java代码中未给出详细定义
|
||||
// 请根据实际需求补充
|
||||
}
|
||||
|
||||
// TeamPKInfo 战队PK信息
|
||||
type TeamPKInfo struct {
|
||||
// 此处应包含TeamPKInfo的具体字段,原Java代码中未给出详细定义
|
||||
// 请根据实际需求补充
|
||||
}
|
||||
|
||||
// PeopleItemInfo 人物物品信息
|
||||
type PeopleItemInfo struct {
|
||||
// 此处应包含PeopleItemInfo的具体字段,原Java代码中未给出详细定义
|
||||
// 请根据实际需求补充
|
||||
}
|
||||
|
||||
// PetInfo 精灵信息
|
||||
type PetInfo struct {
|
||||
// 此处应包含PetInfo的具体字段,原Java代码中未给出详细定义
|
||||
// 请根据实际需求补充
|
||||
}
|
||||
|
||||
// LoginUserInfo 登录用户信息结构体
|
||||
type LoginUserInfo struct {
|
||||
// 米米号 通过sid拿到
|
||||
UserId uint64
|
||||
// 注册时间(按秒的时间戳)
|
||||
RegisterTime uint64
|
||||
// 16字节昵称
|
||||
Nick [16]byte `array_serialize:"fixed_length,16"`
|
||||
// 暂时不明建议先给固定值0
|
||||
Vip uint16 `ushort:"true"`
|
||||
// 暂时不明建议先给固定值15
|
||||
Viped uint16 `ushort:"true"`
|
||||
// 暂时不明建议先给固定值0
|
||||
DsFlag uint64
|
||||
// 机器人人物颜色 00 rgb
|
||||
Color uint64
|
||||
// 暂时不明建议先给固定值0
|
||||
Texture uint64
|
||||
// 暂时不明建议先给固定值3000
|
||||
Energy uint64 `default:"3000"`
|
||||
// 赛尔豆
|
||||
Coins uint64
|
||||
// 暂时不明建议先给固定值0
|
||||
FightBadge uint64
|
||||
// 上线的地图id
|
||||
MapID uint64
|
||||
// 上线的坐标 2个uint
|
||||
Pos Point `array_serialize:"fixed_length,8"`
|
||||
// 已经消耗掉的时间(秒为单位)
|
||||
TimeToday uint64
|
||||
// 总电池限制(秒为单位)
|
||||
TimeLimit uint64
|
||||
// 暂时不明感觉是某种活动建议先给固定值0(只能0或1)
|
||||
IsClothHalfDay byte
|
||||
// 暂时不明感觉是某种活动建议先给固定值0(只能0或1)
|
||||
IsRoomHalfDay byte
|
||||
// 暂时不明感觉是某种活动建议先给固定值0(只能0或1)
|
||||
IFortressHalfDay byte
|
||||
// 暂时不明感觉是某种活动建议先给固定值0(只能0或1)
|
||||
IsHQHalfDay byte
|
||||
// 暂时不明建议先给固定值0
|
||||
LoginCount uint64
|
||||
// 邀请活动建议先给固定值0
|
||||
Inviter uint64
|
||||
// 邀请活动建议先给固定值0
|
||||
NewInviteeCount uint64
|
||||
// 超no等级建议固定8
|
||||
VipLevel uint64 `default:"8"`
|
||||
// 超no的vip值建议固定80000
|
||||
VipValue uint64 `default:"80000"`
|
||||
// 超no的外形等级建议固定1(暂定)
|
||||
VipStage uint64 `default:"1"`
|
||||
// nono是否自动充电 建议固定1
|
||||
AutoCharge uint64 `default:"1"`
|
||||
// 超no的结束时间建议尽可能大
|
||||
VipEndTime uint64 `default:"4294967295"`
|
||||
// 邀请活动建议先给固定值0
|
||||
FreshManBonus uint64
|
||||
// 超no芯片列表*(80字节)
|
||||
NonoChipList [80]byte `array_serialize:"fixed_length,80"`
|
||||
// 50字节,默认值为3
|
||||
DailyResArr [50]byte `array_serialize:"fixed_length,50"`
|
||||
// 教官id
|
||||
TeacherID uint64
|
||||
// 学员id
|
||||
StudentID uint64
|
||||
// 毕业人数
|
||||
GraduationCount uint64
|
||||
// 默认值为0
|
||||
MaxPuniLv uint64 `default:"0"`
|
||||
// 精灵的最高等级
|
||||
PetMaxLevel uint64
|
||||
// 所有的精灵的数量
|
||||
AllPetNumber uint64
|
||||
// 精灵王之战胜场
|
||||
MonKingWin uint64
|
||||
// 勇者之塔当前到达的层数
|
||||
CurrentStage uint64
|
||||
// 试炼之塔最大胜利的层数
|
||||
MaxStage uint64
|
||||
// 试炼之塔当前到达的层数
|
||||
CurrentFreshStage uint64
|
||||
// 试炼之塔最大胜利的层数
|
||||
MaxFreshStage uint64
|
||||
// 星际擂台连胜
|
||||
MaxArenaWins uint64
|
||||
// 未知默认0
|
||||
TwoTimes uint64 `default:"0"`
|
||||
// 未知默认0
|
||||
ThreeTimes uint64 `default:"0"`
|
||||
// 是否自动战斗(未知默认值0)
|
||||
AutoFight uint64 `default:"0"`
|
||||
// 自动战斗剩余的场次(未知默认值0)
|
||||
AutoFightTime uint64 `default:"0"`
|
||||
// 能量吸收仪剩余次数(未知待定默认值0)
|
||||
EnergyTime uint64 `default:"0"`
|
||||
// 学习力吸收仪剩余次数(未知待定默认值0)
|
||||
LearnTimes uint64 `default:"0"`
|
||||
// 未知默认0
|
||||
MonBattleMedal uint64 `default:"0"`
|
||||
// 未知默认0
|
||||
RecordCount uint64 `default:"0"`
|
||||
// 未知默认0
|
||||
ObtainTm uint64 `default:"0"`
|
||||
// 当前在孵化的元神珠id
|
||||
SoulBeadItemID uint64
|
||||
// 未知默认0
|
||||
ExpireTm uint64 `default:"0"`
|
||||
// 未知默认0
|
||||
FuseTimes uint64 `default:"0"`
|
||||
// 玩家有没有nono
|
||||
HasNono uint64 `default:"1"`
|
||||
// 玩家有没有超能nono
|
||||
SuperNono uint64 `default:"1"`
|
||||
// 默认值-1
|
||||
NonoState uint64 `default:"4294967295"`
|
||||
// nono的颜色
|
||||
NonoColor uint64
|
||||
// nono的名字 必须要补齐到16位
|
||||
NonoNick [16]byte `array_serialize:"fixed_length,16"`
|
||||
// 猜测为战队信息24字节
|
||||
TeamInfo TeamInfo `array_serialize:"fixed_length,24"`
|
||||
// 8字节
|
||||
TeamPkInfo TeamPKInfo `array_serialize:"fixed_length,8"`
|
||||
// 1字节 无内容
|
||||
Reserved byte
|
||||
// 默认值为0
|
||||
Badge uint64 `default:"0"`
|
||||
// 未知(27字节,默认值为3)
|
||||
Reserved1 [27]byte `array_serialize:"fixed_length,27"`
|
||||
// 任务状态数组(500字节,3为已经完成,建议默认值为3)
|
||||
TaskList [500]byte `array_serialize:"fixed_length,500"`
|
||||
// 精灵背包内的信息由于特性精灵的存在精灵背包不定长 如果有特性占199字节 如果没特性 一个精灵占175字节
|
||||
PetList []PetInfo
|
||||
// 穿戴装备 8字节
|
||||
Clothes []PeopleItemInfo
|
||||
}
|
||||
|
||||
// NewLoginUserInfo 创建新的登录用户信息实例,设置默认值
|
||||
func NewLoginUserInfo() *LoginUserInfo {
|
||||
info := &LoginUserInfo{
|
||||
Vip: 0,
|
||||
Viped: 15,
|
||||
Energy: 3000,
|
||||
VipLevel: 8,
|
||||
VipValue: 80000,
|
||||
VipStage: 1,
|
||||
AutoCharge: 1,
|
||||
VipEndTime: uint64(^uint32(0)),
|
||||
TwoTimes: 0,
|
||||
ThreeTimes: 0,
|
||||
AutoFight: 0,
|
||||
AutoFightTime: 0,
|
||||
EnergyTime: 0,
|
||||
LearnTimes: 0,
|
||||
MonBattleMedal: 0,
|
||||
RecordCount: 0,
|
||||
ObtainTm: 0,
|
||||
ExpireTm: 0,
|
||||
FuseTimes: 0,
|
||||
HasNono: 1,
|
||||
SuperNono: 1,
|
||||
NonoState: uint64(^uint32(0)),
|
||||
Badge: 0,
|
||||
}
|
||||
|
||||
// 初始化固定长度数组
|
||||
for i := range info.NonoChipList {
|
||||
info.NonoChipList[i] = 0
|
||||
}
|
||||
for i := range info.DailyResArr {
|
||||
info.DailyResArr[i] = 3
|
||||
}
|
||||
for i := range info.Reserved1 {
|
||||
info.Reserved1[i] = 3
|
||||
}
|
||||
for i := range info.TaskList {
|
||||
info.TaskList[i] = 3
|
||||
}
|
||||
|
||||
// 初始化nono昵称
|
||||
copy(info.NonoNick[:], "nono")
|
||||
|
||||
return info
|
||||
}
|
||||
41
common/core/info/ServerInfo.go
Normal file
41
common/core/info/ServerInfo.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package info
|
||||
|
||||
// ServerInfo 服务器信息结构体
|
||||
type ServerInfo struct {
|
||||
// 连接ID, 即服务器序号
|
||||
OnlineID uint32
|
||||
// 当前服务器玩家在线数量, 供SWF显示
|
||||
UserCnt uint32
|
||||
// 服务器IP, 16字节UTF-8, 不足16补齐到16
|
||||
IP []byte `v:"FIXED_LENGTH|length:16"`
|
||||
// 端口
|
||||
Port uint16
|
||||
// 好友在线的个数
|
||||
Friends uint32
|
||||
}
|
||||
|
||||
// NewServerInfo 创建新的服务器信息实例
|
||||
func NewServerInfo() *ServerInfo {
|
||||
return &ServerInfo{
|
||||
OnlineID: 0,
|
||||
UserCnt: 0,
|
||||
IP: []byte{},
|
||||
Port: 0,
|
||||
Friends: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// // SetIP 设置IP地址并自动填充到16字节
|
||||
// func (s *ServerInfo) SetIP(ip string) {
|
||||
// copy(s.IP[:], ip)
|
||||
// if len(ip) < 16 {
|
||||
// for i := len(ip); i < 16; i++ {
|
||||
// s.IP[i] = 0 // 用0填充剩余字节
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // GetIP 获取IP地址(去除填充的0)
|
||||
// func (s *ServerInfo) GetIP() string {
|
||||
// return strings.TrimRight(string(s.IP[:]), "\x00")
|
||||
// }
|
||||
@@ -7,6 +7,7 @@ require github.com/panjf2000/gnet v1.6.7
|
||||
require (
|
||||
github.com/panjf2000/ants/v2 v2.11.3 // indirect
|
||||
github.com/panjf2000/gnet/v2 v2.5.0 // indirect
|
||||
github.com/tnnmigga/enum v1.0.2 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
|
||||
@@ -23,6 +23,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tnnmigga/enum v1.0.2 h1:Yvchx0Esc01X5HiphW78sKzH/RXKttdFsfPO1ARiOa4=
|
||||
github.com/tnnmigga/enum v1.0.2/go.mod h1:QaBFBwGJi/2GAM34b2pz6UL2NRtl2TRZ8lXp4vGwqhA=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
|
||||
@@ -1,360 +0,0 @@
|
||||
package socket
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
)
|
||||
|
||||
// Server 游戏服务器核心类,管理玩家、星球和游戏逻辑
|
||||
type Server struct {
|
||||
players map[int64]*entity.Player
|
||||
planets map[int64]*planet.Planet
|
||||
mutex sync.RWMutex
|
||||
gameRepo repo.GameResourceRepo
|
||||
serverRepo repo.ServerRepo
|
||||
accountRepo repo.AccountRepo
|
||||
playerInfoRepo repo.PlayerInfoRepo
|
||||
playerItemRepo repo.PlayerItemInfoRepo
|
||||
petRepo repo.PetRepo
|
||||
}
|
||||
|
||||
// NewServer 创建新的游戏服务器实例
|
||||
func NewServer(
|
||||
gameRepo repo.GameResourceRepo,
|
||||
serverRepo repo.ServerRepo,
|
||||
accountRepo repo.AccountRepo,
|
||||
playerInfoRepo repo.PlayerInfoRepo,
|
||||
playerItemRepo repo.PlayerItemInfoRepo,
|
||||
petRepo repo.PetRepo,
|
||||
) *Server {
|
||||
s := &Server{
|
||||
players: make(map[int64]*entity.Player),
|
||||
planets: make(map[int64]*planet.Planet),
|
||||
gameRepo: gameRepo,
|
||||
serverRepo: serverRepo,
|
||||
accountRepo: accountRepo,
|
||||
playerInfoRepo: playerInfoRepo,
|
||||
playerItemRepo: playerItemRepo,
|
||||
petRepo: petRepo,
|
||||
}
|
||||
s.initializePlanets()
|
||||
return s
|
||||
}
|
||||
|
||||
// initializePlanets 初始化所有星球
|
||||
func (s *Server) initializePlanets() {
|
||||
maps := s.gameRepo.GetAllMaps()
|
||||
for _, config := range maps {
|
||||
planet := s.generatePlanet(config)
|
||||
s.planets[planet.GetId()] = planet
|
||||
}
|
||||
}
|
||||
|
||||
// generatePlanet 根据地图配置生成星球
|
||||
func (s *Server) generatePlanet(config *mapInfo.MapXmlModel) *planet.Planet {
|
||||
entries := config.GetEntries()
|
||||
positions := make(map[int64]structs.Point)
|
||||
|
||||
if len(entries) == 0 {
|
||||
positions = make(map[int64]structs.Point)
|
||||
} else {
|
||||
for _, entry := range entries {
|
||||
positions[entry.GetFromMap()] = s.generatePoint(entry)
|
||||
}
|
||||
}
|
||||
|
||||
return planet.NewPlanet(
|
||||
s,
|
||||
int64(config.GetId()),
|
||||
config.GetName(),
|
||||
structs.Point{X: config.GetX(), Y: config.GetY()},
|
||||
positions,
|
||||
s.gameRepo.CanMapRefresh(config.GetId()),
|
||||
)
|
||||
}
|
||||
|
||||
// generatePoint 从入口配置生成点坐标
|
||||
func (s *Server) generatePoint(xml *mapInfo.EntryXmlModel) structs.Point {
|
||||
return structs.Point{X: xml.GetPosX(), Y: xml.GetPosY()}
|
||||
}
|
||||
|
||||
// GetPlayer 获取玩家信息
|
||||
func (s *Server) GetPlayer(accountID int64) (*entity.Player, error) {
|
||||
s.mutex.RLock()
|
||||
defer s.mutex.RUnlock()
|
||||
|
||||
player, exists := s.players[accountID]
|
||||
if !exists {
|
||||
return nil, errors.New("玩家不存在")
|
||||
}
|
||||
return player, nil
|
||||
}
|
||||
|
||||
// GetPlanet 获取星球信息
|
||||
func (s *Server) GetPlanet(planetID int64) (*planet.Planet, error) {
|
||||
s.mutex.RLock()
|
||||
defer s.mutex.RUnlock()
|
||||
|
||||
planet, exists := s.planets[planetID]
|
||||
if !exists {
|
||||
return nil, errors.New("星球不存在")
|
||||
}
|
||||
return planet, nil
|
||||
}
|
||||
|
||||
// GetDefaultPlanet 获取默认星球
|
||||
func (s *Server) GetDefaultPlanet() (*planet.Planet, error) {
|
||||
s.mutex.RLock()
|
||||
defer s.mutex.RUnlock()
|
||||
|
||||
planet, exists := s.planets[1] // 假设1是默认星球ID
|
||||
if !exists {
|
||||
return nil, errors.New("未找到默认星球")
|
||||
}
|
||||
return planet, nil
|
||||
}
|
||||
|
||||
// PlayerEnter 玩家加入服务器
|
||||
func (s *Server) PlayerEnter(player *entity.Player) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
s.players[player.GetAccountID()] = player
|
||||
}
|
||||
|
||||
// PlayerOffline 玩家离线处理
|
||||
func (s *Server) PlayerOffline(player *entity.Player) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
// 清理缓存会话数据
|
||||
if err := s.accountRepo.RemoveSessionID(player.GetSessionID()); err != nil {
|
||||
log.Printf("清除会话ID失败: %v", err)
|
||||
}
|
||||
|
||||
// 清理登录绑定服务器
|
||||
if err := s.serverRepo.CancelRecordAccount(player.GetAccountID()); err != nil {
|
||||
log.Printf("取消账号绑定失败: %v", err)
|
||||
}
|
||||
|
||||
// 从玩家列表中移除
|
||||
delete(s.players, player.GetAccountID())
|
||||
}
|
||||
|
||||
// BroadcastMessage 广播消息给所有在线玩家
|
||||
func (s *Server) BroadcastMessage(message *net.OutboundMessage) {
|
||||
s.mutex.RLock()
|
||||
defer s.mutex.RUnlock()
|
||||
|
||||
for _, player := range s.players {
|
||||
if player.IsOnline() {
|
||||
player.SendMessage(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BroadcastMessageWithFilter 按条件广播消息给在线玩家
|
||||
func (s *Server) BroadcastMessageWithFilter(message *net.OutboundMessage, filter func(*entity.Player) bool) {
|
||||
s.mutex.RLock()
|
||||
defer s.mutex.RUnlock()
|
||||
|
||||
for _, player := range s.players {
|
||||
if player.IsOnline() && filter(player) {
|
||||
player.SendMessage(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GeneratePetEntity 生成宠物实体
|
||||
func (s *Server) GeneratePetEntity(
|
||||
playerID int64,
|
||||
petTypeID int,
|
||||
individualValue int16,
|
||||
nature int,
|
||||
abilityTypeEnum int,
|
||||
isShiny bool,
|
||||
level int,
|
||||
) (*pet.PetEntity, error) {
|
||||
if level < 1 || level > 100 {
|
||||
return nil, fmt.Errorf("精灵等级必须在1到100之间, level: %d", level)
|
||||
}
|
||||
|
||||
petInfo, err := s.gameRepo.GetMonsterByID(petTypeID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("无效的精灵ID, pet_id: %d, 错误: %v", petTypeID, err)
|
||||
}
|
||||
|
||||
firstSkillInfo, err := s.gameRepo.GetPetFirstSkillID(petTypeID, level)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("精灵没有初始技能, pet_id: %d, 错误: %v", petTypeID, err)
|
||||
}
|
||||
|
||||
zero := int16(0)
|
||||
|
||||
pet := pet.NewPetEntityBuilder()
|
||||
.WithAsset(petInfo)
|
||||
.WithLevelToExp(s.gameRepo.GetLevelToExp())
|
||||
.WithPlayerID(playerID)
|
||||
.WithCapturePlayerID(playerID)
|
||||
.WithCaptureTime(time.Now().Unix())
|
||||
.WithCaptureMap(0) // 假设0是默认地图ID
|
||||
.WithCaptureRect(0) // 假设0是默认区域
|
||||
.WithCaptureLevel(level)
|
||||
.WithIndividualValue(individualValue)
|
||||
.WithNature(nature)
|
||||
.WithAbilityTypeEnum(abilityTypeEnum)
|
||||
.WithIsShiny(isShiny)
|
||||
.WithLevel(level)
|
||||
.WithCurrentExp(0)
|
||||
.WithEvHp(zero)
|
||||
.WithEvAttack(zero)
|
||||
.WithEvDefense(zero)
|
||||
.WithEvSpecialAttack(zero)
|
||||
.WithEvSpecialDefense(zero)
|
||||
.WithEvSpeed(zero)
|
||||
.WithSkill1ID(firstSkillInfo[0].GetId())
|
||||
.WithSkill1Pp(firstSkillInfo[0].GetMaxPp())
|
||||
.WithSkill2ID(firstSkillInfo[1].GetId())
|
||||
.WithSkill2Pp(firstSkillInfo[1].GetMaxPp())
|
||||
.WithSkill3ID(firstSkillInfo[2].GetId())
|
||||
.WithSkill3Pp(firstSkillInfo[2].GetMaxPp())
|
||||
.WithSkill4ID(firstSkillInfo[3].GetId())
|
||||
.WithSkill4Pp(firstSkillInfo[3].GetMaxPp())
|
||||
.WithIndividualGuarantee(0)
|
||||
.WithNatureGuarantee(0)
|
||||
.Build()
|
||||
|
||||
if err := s.CalculatePetPanel(pet); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pet, nil
|
||||
}
|
||||
|
||||
// CalculatePetPanel 计算宠物面板属性
|
||||
func (s *Server) CalculatePetPanel(petEntity *pet.PetEntity) error {
|
||||
natureInfo, err := s.gameRepo.GetNatureInfoByID(petEntity.GetNature())
|
||||
if err != nil {
|
||||
return fmt.Errorf("无效的性格ID, nature: %d, 错误: %v", petEntity.GetNature(), err)
|
||||
}
|
||||
|
||||
hp := util.CalculatePetHPPanelSize(
|
||||
petEntity.GetAsset().GetHp(),
|
||||
petEntity.GetIndividualValue(),
|
||||
petEntity.GetLevel(),
|
||||
petEntity.GetEvHp(),
|
||||
)
|
||||
|
||||
attack := util.CalculatePetPanelSize(
|
||||
petEntity.GetAsset().GetAtk(),
|
||||
petEntity.GetIndividualValue(),
|
||||
petEntity.GetLevel(),
|
||||
petEntity.GetEvHp(),
|
||||
natureInfo.GetAttackCorrect(),
|
||||
)
|
||||
|
||||
defense := util.CalculatePetPanelSize(
|
||||
petEntity.GetAsset().GetDef(),
|
||||
petEntity.GetIndividualValue(),
|
||||
petEntity.GetLevel(),
|
||||
petEntity.GetEvHp(),
|
||||
natureInfo.GetDefenseCorrect(),
|
||||
)
|
||||
|
||||
specialAttack := util.CalculatePetPanelSize(
|
||||
petEntity.GetAsset().GetSpAtk(),
|
||||
petEntity.GetIndividualValue(),
|
||||
petEntity.GetLevel(),
|
||||
petEntity.GetEvHp(),
|
||||
natureInfo.GetSaCorrect(),
|
||||
)
|
||||
|
||||
specialDefense := util.CalculatePetPanelSize(
|
||||
petEntity.GetAsset().GetSpDef(),
|
||||
petEntity.GetIndividualValue(),
|
||||
petEntity.GetLevel(),
|
||||
petEntity.GetEvHp(),
|
||||
natureInfo.GetSdCorrect(),
|
||||
)
|
||||
|
||||
speed := util.CalculatePetPanelSize(
|
||||
petEntity.GetAsset().GetSpd(),
|
||||
petEntity.GetIndividualValue(),
|
||||
petEntity.GetLevel(),
|
||||
petEntity.GetEvHp(),
|
||||
natureInfo.GetSpeedCorrect(),
|
||||
)
|
||||
|
||||
petEntity.SetMaxHp(hp)
|
||||
petEntity.SetCurrentHp(hp)
|
||||
petEntity.SetAttack(attack)
|
||||
petEntity.SetDefense(defense)
|
||||
petEntity.SetSpecialAttack(specialAttack)
|
||||
petEntity.SetSpecialDefense(specialDefense)
|
||||
petEntity.SetSpeed(speed)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SavePlayer 保存玩家信息到数据库
|
||||
func (s *Server) SavePlayer(player *entity.Player) (bool, error) {
|
||||
ctx := context.Background()
|
||||
tx, err := s.playerInfoRepo.BeginTransaction(ctx)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("开始事务失败: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
tx.Rollback()
|
||||
log.Printf("保存玩家时发生panic: %v", r)
|
||||
} else if err != nil {
|
||||
tx.Rollback()
|
||||
} else {
|
||||
tx.Commit()
|
||||
}
|
||||
}()
|
||||
|
||||
playerID := player.GetGameID()
|
||||
pets := player.GetPetBag().GetUsedPets()
|
||||
items := player.GetItemBag().GetItems()
|
||||
|
||||
// 保存玩家信息
|
||||
if err := s.playerInfoRepo.Save(ctx, tx, player); err != nil {
|
||||
return false, fmt.Errorf("保存玩家信息失败: %v", err)
|
||||
}
|
||||
|
||||
// 保存精灵信息
|
||||
if err := s.petRepo.SaveAll(ctx, tx, playerID, pets); err != nil {
|
||||
return false, fmt.Errorf("保存精灵信息失败: %v", err)
|
||||
}
|
||||
|
||||
// 保存玩家背包信息
|
||||
if err := s.playerItemRepo.SaveAll(ctx, tx, playerID, items); err != nil {
|
||||
return false, fmt.Errorf("保存背包信息失败: %v", err)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Destroy 销毁服务器,清理资源
|
||||
func (s *Server) Destroy() error {
|
||||
// 保存所有玩家数据
|
||||
s.mutex.RLock()
|
||||
for _, player := range s.players {
|
||||
go func(p *entity.Player) {
|
||||
if _, err := s.SavePlayer(p); err != nil {
|
||||
log.Printf("保存玩家 %d 数据失败: %v", p.GetAccountID(), err)
|
||||
}
|
||||
}(player)
|
||||
}
|
||||
s.mutex.RUnlock()
|
||||
|
||||
log.Println("Destroying server ...")
|
||||
return nil
|
||||
}
|
||||
10
common/serialize/ArraySerialize.go
Normal file
10
common/serialize/ArraySerialize.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package serialize
|
||||
|
||||
type ArraySerialize struct {
|
||||
OrderId int64 `v:"order-exist"`
|
||||
ProductName string
|
||||
Amount int64
|
||||
// ...
|
||||
}
|
||||
var paddedContent byte=0//长度不足时填充的内容, 默认填充0
|
||||
|
||||
20
common/serialize/cheak.go
Normal file
20
common/serialize/cheak.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package serialize
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/util/gvalid"
|
||||
)
|
||||
|
||||
type Request struct {
|
||||
OrderId int64 `v:"order-exist"`
|
||||
ProductName string `v:"order-exist"`
|
||||
Amount int64
|
||||
// ...
|
||||
}
|
||||
|
||||
func RuleOrderExist(ctx context.Context, in gvalid.RuleFuncInput) error {
|
||||
fmt.Println(in)
|
||||
return nil
|
||||
}
|
||||
22
common/serialize/cheak_test.go
Normal file
22
common/serialize/cheak_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package serialize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
)
|
||||
|
||||
func Test_main(t *testing.T) {
|
||||
var (
|
||||
ctx = gctx.New()
|
||||
req = &Request{
|
||||
OrderId: 65535,
|
||||
ProductName: "HikingShoe",
|
||||
Amount: 10000,
|
||||
}
|
||||
)
|
||||
err := g.Validator().RuleFunc("order-exist", RuleOrderExist).Data(req).Run(ctx)
|
||||
fmt.Println(err)
|
||||
}
|
||||
236
common/serialize/ser.go
Normal file
236
common/serialize/ser.go
Normal file
@@ -0,0 +1,236 @@
|
||||
package serialize
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/gvalid"
|
||||
"github.com/tnnmigga/enum"
|
||||
)
|
||||
|
||||
var lengthtype = enum.New[struct {
|
||||
LENGTH_FIRST int
|
||||
FIXED_LENGTH int
|
||||
}]()
|
||||
|
||||
// PacketSerializer 定义序列化函数类型,将数据转换为字节切片
|
||||
type PacketSerializer[T any] func(data T) ([]byte, error)
|
||||
|
||||
// PacketDeserializer 定义反序列化函数类型,将字节切片转换为数据
|
||||
type PacketDeserializer[T any] func(data []byte) (T, error)
|
||||
|
||||
// PacketHandler 封装序列化和反序列化处理函数
|
||||
type PacketHandler[T any] struct {
|
||||
Serialize PacketSerializer[T] // 序列化函数
|
||||
Deserialize PacketDeserializer[T] // 反序列化函数
|
||||
}
|
||||
|
||||
|
||||
|
||||
func serializebase[T any](field reflect.StructField, buf *bytes.Buffer, writedata any) error {
|
||||
|
||||
|
||||
if field.Type.Kind() == reflect.Slice { //|| field.Type.Kind() == reflect.Array}
|
||||
|
||||
datatype := make(chan int, 1)
|
||||
FIXED_LENGTH := func(ctx context.Context, in gvalid.RuleFuncInput) error {
|
||||
if in.Field == field.Name { //判断相同
|
||||
datatype <- lengthtype.FIXED_LENGTH
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
LENGTH_FIRST := func(ctx context.Context, in gvalid.RuleFuncInput) error {
|
||||
datatype <- lengthtype.FIXED_LENGTH
|
||||
// fmt.Println(in)
|
||||
return nil
|
||||
}
|
||||
length := func(ctx context.Context, in gvalid.RuleFuncInput) error {
|
||||
tem := <-datatype
|
||||
close(datatype)
|
||||
writelen := 0
|
||||
if parts := strings.Split(in.Rule, ":"); len(parts) > 1 {
|
||||
writelen = gconv.Int(strings.TrimSpace(parts[1]))
|
||||
|
||||
}
|
||||
switch tem {
|
||||
case lengthtype.FIXED_LENGTH: //in.value
|
||||
tempslice := gconv.SliceAny(writedata)
|
||||
temp := make([]byte, writelen-len(tempslice))
|
||||
|
||||
for i := 0; i < len(tempslice); i++ {
|
||||
tempdata := tempslice[i]
|
||||
fmt.Println(i)
|
||||
serializebase[T]( field, buf, tempdata) //todo递归序列化
|
||||
// copy(temp, date1)
|
||||
// if err := binary.Write(buf, binary.BigEndian, temp); err != nil {
|
||||
// return nil
|
||||
// }
|
||||
}
|
||||
|
||||
if err := binary.Write(buf, binary.BigEndian, temp); err != nil {
|
||||
return nil
|
||||
}
|
||||
case lengthtype.LENGTH_FIRST:
|
||||
// temp := make([]byte, writelen)
|
||||
// date1 := []byte(writedata.(string))
|
||||
// copy(temp, date1)
|
||||
// if err := binary.Write(buf, binary.BigEndian, temp); err != nil {
|
||||
// return nil
|
||||
// }
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
rules := make(map[string]gvalid.RuleFunc, 3)
|
||||
rules["LENGTH_FIRST"] = LENGTH_FIRST
|
||||
rules["FIXED_LENGTH"] = FIXED_LENGTH
|
||||
rules["length"] = length
|
||||
g.Validator().RuleFuncMap(rules).Data(writedata).Run(gctx.New())
|
||||
// serializeslice[T](field, buf, writedata)
|
||||
} else {
|
||||
if err := binary.Write(buf, binary.BigEndian, writedata); err != nil {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DefaultPacketSerializer 默认序列化实现,使用大端序写入数据
|
||||
func DefaultPacketSerializer[T any]() PacketSerializer[T] {
|
||||
return func(data T) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
// 使用大端序写入数据
|
||||
// 1. 使用reflect获取结构体类型
|
||||
typ := reflect.TypeOf(data)
|
||||
fmt.Println("结构体类型名称:", typ.Name())
|
||||
fmt.Println("字段数量:", typ.NumField())
|
||||
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
fmt.Printf("字段名: %s, 类型: %s",
|
||||
field.Name, field.Type)
|
||||
fmt.Println("字段值:", reflect.ValueOf(data).Field(i).Interface())
|
||||
|
||||
writedata := reflect.ValueOf(data).Field(i).Interface()
|
||||
fmt.Println(field.Type.Kind())
|
||||
|
||||
serializebase[T]( field, &buf, writedata)
|
||||
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultPacketDeserializer 默认反序列化实现,使用大端序读取数据
|
||||
func DefaultPacketDeserializer[T any]() PacketDeserializer[T] {
|
||||
return func(data []byte) (T, error) {
|
||||
var result T
|
||||
reader := bytes.NewReader(data)
|
||||
// 使用大端序读取数据
|
||||
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
|
||||
var zero T
|
||||
return zero, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewDefaultPacketHandler 创建默认的数据包处理句柄
|
||||
func NewDefaultPacketHandler[T any]() *PacketHandler[T] {
|
||||
return &PacketHandler[T]{
|
||||
Serialize: DefaultPacketSerializer[T](),
|
||||
Deserialize: DefaultPacketDeserializer[T](),
|
||||
}
|
||||
}
|
||||
|
||||
// 示例:使用自定义类型演示序列化与反序列化
|
||||
type ExampleData struct {
|
||||
ID int32
|
||||
Name string
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// 自定义序列化函数(处理结构体类型)
|
||||
func CustomSerializer() PacketSerializer[ExampleData] {
|
||||
return func(data ExampleData) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
// 先序列化基本类型字段
|
||||
if err := binary.Write(&buf, binary.BigEndian, data.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 序列化字符串长度和内容
|
||||
lenName := int32(len(data.Name))
|
||||
if err := binary.Write(&buf, binary.BigEndian, lenName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := buf.Write([]byte(data.Name)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 序列化字节数组长度和内容
|
||||
lenData := int32(len(data.Data))
|
||||
if err := binary.Write(&buf, binary.BigEndian, lenData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := buf.Write(data.Data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
}
|
||||
|
||||
// 自定义反序列化函数(处理结构体类型)
|
||||
func CustomDeserializer() PacketDeserializer[ExampleData] {
|
||||
return func(data []byte) (ExampleData, error) {
|
||||
var result ExampleData
|
||||
reader := bytes.NewReader(data)
|
||||
|
||||
// 读取基本类型字段
|
||||
if err := binary.Read(reader, binary.BigEndian, &result.ID); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
// 读取字符串长度和内容
|
||||
var lenName int32
|
||||
if err := binary.Read(reader, binary.BigEndian, &lenName); err != nil {
|
||||
return result, err
|
||||
}
|
||||
if lenName > int32(reader.Len()) {
|
||||
return result, errors.New("invalid name length")
|
||||
}
|
||||
nameBuf := make([]byte, lenName)
|
||||
if _, err := reader.Read(nameBuf); err != nil {
|
||||
return result, err
|
||||
}
|
||||
result.Name = string(nameBuf)
|
||||
|
||||
// 读取字节数组长度和内容
|
||||
var lenData int32
|
||||
if err := binary.Read(reader, binary.BigEndian, &lenData); err != nil {
|
||||
return result, err
|
||||
}
|
||||
if lenData > int32(reader.Len()) {
|
||||
return result, errors.New("invalid data length")
|
||||
}
|
||||
result.Data = make([]byte, lenData)
|
||||
if _, err := reader.Read(result.Data); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/panjf2000/gnet/v2"
|
||||
"github.com/panjf2000/gnet/v2/pkg/logging"
|
||||
"blazing/common/data/entity"
|
||||
)
|
||||
|
||||
func (s *Server) Boot() error {
|
||||
@@ -35,19 +36,18 @@ func (s *Server) OnBoot(eng gnet.Engine) gnet.Action {
|
||||
|
||||
func (s *Server) OnTraffic(conn gnet.Conn) (action gnet.Action) {
|
||||
|
||||
|
||||
conn.SetContext(entity.NewClientData())//注入data
|
||||
if s.network == "tcp" {
|
||||
return s.handleTcp(conn)
|
||||
}
|
||||
|
||||
|
||||
|
||||
return gnet.None
|
||||
}
|
||||
|
||||
|
||||
func (s *Server) handleTcp(conn gnet.Conn) (action gnet.Action) {
|
||||
|
||||
for {
|
||||
|
||||
data, err := s.codec.Decode(conn)
|
||||
if err != nil {
|
||||
break
|
||||
@@ -73,11 +73,11 @@ func (s *Server) handleTcp(conn gnet.Conn) (action gnet.Action) {
|
||||
}
|
||||
|
||||
func (s *Server) parser(c gnet.Conn, line []byte) {
|
||||
|
||||
//todo 这里待实现注入player实体
|
||||
s.handler.Handle(line)
|
||||
}
|
||||
func (s *Server) Start() {
|
||||
|
||||
|
||||
err := gnet.Run(s, s.network+"://"+s.addr, gnet.WithMulticore(s.multicore))
|
||||
logging.Infof("server exits with error: %v", err)
|
||||
}
|
||||
|
||||
98
common/socket/cmd/cmd.go
Normal file
98
common/socket/cmd/cmd.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/tnnmigga/enum"
|
||||
)
|
||||
|
||||
// Enum 辅助类型定义
|
||||
type EnumValue struct {
|
||||
Value int
|
||||
Name string
|
||||
}
|
||||
|
||||
// MessageCommandIDRegistry 消息命令ID注册表
|
||||
var MessageCommandIDRegistry = enum.New[struct {
|
||||
// 在线相关命令
|
||||
Commend_OnLine int `enum:"105"` // 在线命令
|
||||
Login_In int `enum:"1001"` // 玩家登录
|
||||
System_Time int `enum:"1002"` // 返回当前时间戳
|
||||
Map_Hot int `enum:"1004"` // 显示地图热度(此地图内玩家数量)
|
||||
Gold_Online_Check_Remain int `enum:"1106"` // 返回玩家金豆数量
|
||||
|
||||
// 地图相关命令
|
||||
Enter_Map int `enum:"2001"` // 告知后端玩家进入新地图
|
||||
Leave_Map int `enum:"2002"` // 玩家离开地图
|
||||
List_Map_Player int `enum:"2003"` // 返回当前地图玩家列表
|
||||
Map_Ogre_List int `enum:"2004"` // 精灵刷新
|
||||
|
||||
// 用户信息相关命令
|
||||
Get_Sim_UserInfo int `enum:"2051"` // 返回邮人物简单信息
|
||||
Get_More_UserInfo int `enum:"2052"` // 返回人物详细信息
|
||||
Change_Nick_Name int `enum:"2061"` // 修改玩家名字
|
||||
|
||||
// 动作相关命令
|
||||
People_Walk int `enum:"2101"` // 玩家走路包
|
||||
Chat int `enum:"2102"` // 公屏聊天
|
||||
Aimat int `enum:"2104"` // 射击
|
||||
|
||||
// 精灵相关命令
|
||||
Get_Pet_Info int `enum:"2301"` // 获取精灵详细信息
|
||||
Get_Pet_List int `enum:"2303"` // 获取所有的精灵列表
|
||||
Pet_Release int `enum:"2304"` // 精灵加入背包或放回仓库
|
||||
Pet_Show int `enum:"2305"` // 展示精灵
|
||||
Pet_Cure int `enum:"2306"` // 恢复精灵状态,NONO恢复所有精灵
|
||||
Pet_Study_Skill int `enum:"2307"` // 升级学习替换技能
|
||||
Pet_Default int `enum:"2308"` // 设置精灵首发
|
||||
Pet_One_Cure int `enum:"2310"` // 恢复精灵状态,精灵恢复单只精灵
|
||||
Pet_Skill_Switch int `enum:"2312"` // 切换精灵技能
|
||||
Pet_Set_Exp int `enum:"2318"` // 分配精灵经验
|
||||
Pet_Get_Exp int `enum:"2319"` // 获取积累经验
|
||||
Pet_Room int `enum:"2325"` // 跟随精灵获取信息
|
||||
Get_Soul_Bead_List int `enum:"2354"` // 返回元神珠信息
|
||||
|
||||
// 战斗相关命令
|
||||
Ready_To_Fight int `enum:"2404"` // 客户端通知服务端可以开始战斗, 此包表示客户端可以开始战斗, 无需服务端回复此包内容
|
||||
Use_Skill int `enum:"2405"` // 使用技能
|
||||
Change_Pet int `enum:"2407"` // 切换精灵
|
||||
Fight_NPC_Monster int `enum:"2408"` // 与野怪对战的申请进入战斗
|
||||
Catch_Monster int `enum:"2409"` // 捕捉精灵
|
||||
Challenge_Boss int `enum:"2411"` // 与当前地图的Boss类型野怪进入战斗
|
||||
Note_ReadyTo_Fight int `enum:"2503"` // 通知客户端已经可以开始战斗
|
||||
Note_Start_Fight int `enum:"2504"` // 在客户端告知服务端可以开始战斗后, 服务端回给客户端战斗开始
|
||||
Note_Use_Skill int `enum:"2505"` // 通知使用技能
|
||||
Fight_Over int `enum:"2506"` // 战斗结束
|
||||
Note_Update_Skill int `enum:"2507"` // 升级获得学习技能
|
||||
Note_Update_Prop int `enum:"2508"` // 返回升级后的信息
|
||||
|
||||
// 装备物品相关命令
|
||||
Change_Cloth int `enum:"2604"` // 修改玩家装备
|
||||
Item_List int `enum:"2605"` // 返回玩家物品列表
|
||||
|
||||
// 奖励相关命令
|
||||
Talk_Count int `enum:"2701"` // 玩家领取奖励的次数(挖矿,礼包等)
|
||||
Talk_Cate int `enum:"2702"` // 领取奖品的内容
|
||||
Mail_Get_Unread int `enum:"2757"` // 返回邮件数量
|
||||
|
||||
// 系统相关命令
|
||||
System_Message int `enum:"8002"` // 后端主动发送面板消息
|
||||
Get_Boss_Monster int `enum:"8004"` // 返回战斗结束后的奖励包或主动发放奖励
|
||||
|
||||
// NONO相关命令
|
||||
Nono_Info int `enum:"9003"` // 通过米米号获取nono信息
|
||||
Nono_Follow_Or_Home int `enum:"9019"` // nono跟随或回家
|
||||
|
||||
// 特殊命令
|
||||
Get_Quadruple_Exe_Time int `enum:"50007"` // 返回已使用四倍剩余时间
|
||||
|
||||
// 暂未处理的包
|
||||
Item_Buy int `enum:"2601"` // 物品购买
|
||||
Item_Sale int `enum:"2602"` // 物品出售
|
||||
Friend_Add int `enum:"2151"` // 添加好友
|
||||
Friend_Remove int `enum:"2153"` // 移除好友
|
||||
Invite_To_Fight int `enum:"2401"` // 邀请战斗
|
||||
Escape_Fight int `enum:"2410"` // 逃离战斗
|
||||
Join_Game int `enum:"5001"` // 加入游戏
|
||||
Game_Over int `enum:"5002"` // 游戏结束
|
||||
Leave_Game int `enum:"5003"` // 离开游戏
|
||||
}]()
|
||||
|
||||
14
common/socket/cmd/cmd_test.go
Normal file
14
common/socket/cmd/cmd_test.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkCmd(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
//写入
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.Log(MessageCommandIDRegistry.Change_Cloth)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package codec
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/panjf2000/gnet/v2"
|
||||
)
|
||||
|
||||
// CROSS_DOMAIN 定义跨域策略文件内容
|
||||
const CROSS_DOMAIN = "<?xml version=\"1.0\"?><!DOCTYPE cross-domain-policy><cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>\x00"
|
||||
|
||||
// TEXT 定义跨域请求的文本格式
|
||||
const TEXT = "<policy-file-request/>\x00"
|
||||
|
||||
// Handle 处理网络连接
|
||||
func Handle(conn gnet.Conn) error {
|
||||
// 读取数据并检查是否为跨域请求
|
||||
data, err := conn.Peek(len(TEXT))
|
||||
if err != nil {
|
||||
log.Printf("Error reading cross-domain request: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if string(data) == TEXT { //判断是否是跨域请求
|
||||
log.Printf("Received cross-domain request from %s", conn.RemoteAddr())
|
||||
// 处理跨域请求
|
||||
conn.Write([]byte(CROSS_DOMAIN))
|
||||
conn.Discard(len(TEXT))
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -10,4 +10,5 @@ type SocketCodec interface {
|
||||
Encode([]byte) ([]byte, error)
|
||||
Decode(gnet.Conn) ([]byte, error)
|
||||
|
||||
|
||||
}
|
||||
@@ -1,13 +1,21 @@
|
||||
package codec
|
||||
|
||||
import (
|
||||
"blazing/common/data/entity"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
|
||||
"github.com/panjf2000/gnet/v2"
|
||||
)
|
||||
|
||||
// CROSS_DOMAIN 定义跨域策略文件内容
|
||||
const CROSS_DOMAIN = "<?xml version=\"1.0\"?><!DOCTYPE cross-domain-policy><cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>\x00"
|
||||
|
||||
// TEXT 定义跨域请求的文本格式
|
||||
const TEXT = "<policy-file-request/>\x00"
|
||||
|
||||
var ErrIncompletePacket = errors.New("incomplete packet")
|
||||
|
||||
// TomeeSocketCodec 协议格式:
|
||||
@@ -23,26 +31,54 @@ var ErrIncompletePacket = errors.New("incomplete packet")
|
||||
// * | ... ... |
|
||||
// * +-----------+
|
||||
type TomeeSocketCodec struct{}
|
||||
var _ SocketCodec = (*TomeeSocketCodec)(nil)
|
||||
|
||||
var _ SocketCodec = (*TomeeSocketCodec)(nil)
|
||||
|
||||
func NewTomeeSocketCodec() *TomeeSocketCodec {
|
||||
return &TomeeSocketCodec{}
|
||||
|
||||
}
|
||||
|
||||
func handle(c gnet.Conn) {
|
||||
clientdata:=c.Context().(*entity.ClientData)
|
||||
if(clientdata.IsCrossDomain){
|
||||
return
|
||||
|
||||
}
|
||||
// 读取数据并检查是否为跨域请求
|
||||
data, err := c.Peek(len(TEXT))
|
||||
if err != nil {
|
||||
log.Printf("Error reading cross-domain request: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if string(data) == TEXT { //判断是否是跨域请求
|
||||
log.Printf("Received cross-domain request from %s", c.RemoteAddr())
|
||||
// 处理跨域请求
|
||||
c.Write([]byte(CROSS_DOMAIN))
|
||||
c.Discard(len(TEXT))
|
||||
|
||||
clientdata.IsCrossDomain=true
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
func (codec TomeeSocketCodec) Encode(buf []byte) ([]byte, error) {
|
||||
bodyLen := len(buf)
|
||||
data := make([]byte, 4+bodyLen)
|
||||
|
||||
|
||||
// 写入4字节的包长度
|
||||
binary.BigEndian.PutUint32(data[:4], uint32(bodyLen))
|
||||
// 写入包体
|
||||
copy(data[4:], buf)
|
||||
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (codec TomeeSocketCodec) Decode(c gnet.Conn) ([]byte, error) {
|
||||
|
||||
handle(c)
|
||||
// 先读取4字节的包长度
|
||||
lenBuf, err := c.Peek(4)
|
||||
if err != nil {
|
||||
@@ -51,10 +87,10 @@ func (codec TomeeSocketCodec) Decode(c gnet.Conn) ([]byte, error) {
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
bodyLen := binary.BigEndian.Uint32(lenBuf)
|
||||
totalLen := 4 + int(bodyLen)
|
||||
|
||||
|
||||
// 检查整个包是否完整
|
||||
buf, err := c.Peek(totalLen)
|
||||
if err != nil {
|
||||
@@ -63,13 +99,13 @@ func (codec TomeeSocketCodec) Decode(c gnet.Conn) ([]byte, error) {
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
// 提取包体
|
||||
body := make([]byte, bodyLen)
|
||||
copy(body, buf[4:totalLen])
|
||||
|
||||
|
||||
// 从缓冲区中丢弃已读取的数据
|
||||
_, _ = c.Discard(totalLen)
|
||||
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user