This commit is contained in:
孟帅
2024-03-07 20:08:56 +08:00
parent 6dd8cbadad
commit 0fbc1ad47c
246 changed files with 9441 additions and 2293 deletions

View File

@@ -11,11 +11,9 @@ import (
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/util/gmode"
"hotgo/internal/consts"
"hotgo/internal/library/addons"
"hotgo/internal/library/casbin"
"hotgo/internal/library/hggen"
"hotgo/internal/library/payment"
"hotgo/internal/router"
"hotgo/internal/service"
"hotgo/internal/websocket"
@@ -30,33 +28,23 @@ var (
// 初始化http服务
s := g.Server()
// 错误状态码接管
s.BindStatusHandler(404, func(r *ghttp.Request) {
r.Response.Writeln("404 - 你似乎来到了没有知识存在的荒原…")
})
s.BindStatusHandler(403, func(r *ghttp.Request) {
r.Response.Writeln("403 - 网站拒绝显示此网页")
})
// 初始化请求前回调
s.BindHookHandler("/*any", ghttp.HookBeforeServe, service.Hook().BeforeServe)
// 请求响应结束后回调
s.BindHookHandler("/*any", ghttp.HookAfterOutput, service.Hook().AfterOutput)
// 注册全局中间件
s.BindMiddleware("/*any", []ghttp.HandlerFunc{
service.Middleware().Ctx, // 初始化请求上下文,一般需要第一个进行加载,后续中间件存在依赖关系
service.Middleware().CORS, // 跨域中间件,自动处理跨域问题
service.Middleware().Blacklist, // IP黑名单中间件如果请求IP被后台拉黑所有请求将被拒绝
service.Middleware().DemoLimit, // 演示系統操作限制当开启演示模式时所有POST请求将被拒绝
service.Middleware().PreFilter, // 请求输入预处理api使用gf规范路由并且XxxReq结构体实现了validate.Filter接口即可隐式预处理
service.Middleware().ResponseHandler, // HTTP响应预处理在业务处理完成后对响应结果进行格式化和错误过滤将处理后的数据发送给请求方
}...)
s.Group("/", func(group *ghttp.RouterGroup) {
// 注册全局中间件
group.Middleware(
service.Middleware().Ctx, // 初始化请求上下文,一般需要第一个进行加载,后续中间件存在依赖关系
service.Middleware().CORS, // 跨域中间件,自动处理跨域问题
service.Middleware().Blacklist, // IP黑名单中间件如果请求IP被后台拉黑所有请求将被拒绝
service.Middleware().DemoLimit, // 演示系統操作限制当开启演示模式时所有POST请求将被拒绝
service.Middleware().PreFilter, // 请求输入预处理api使用gf规范路由并且XxxReq结构体实现了validate.Filter接口即可隐式预处理
service.Middleware().ResponseHandler, // HTTP响应预处理在业务处理完成后对响应结果进行格式化和错误过滤将处理后的数据发送给请求方
)
// 注册后台路由
router.Admin(ctx, group)
@@ -68,13 +56,12 @@ var (
// 注册前台页面路由
router.Home(ctx, group)
// 注册插件路由
addons.RegisterModulesRouter(ctx, group)
})
// 设置插件静态目录映射
addons.AddStaticPath(ctx, s)
// 启动插件
if err = addons.StartModules(ctx, &addons.Option{Server: s}); err != nil {
return err
}
// 初始化casbin权限
casbin.InitEnforcer(ctx)
@@ -94,9 +81,7 @@ var (
service.SysBlacklist().Load(ctx)
// 注册支付成功回调方法
payment.RegisterNotifyCallMap(map[string]payment.NotifyCallFunc{
consts.OrderGroupAdminOrder: service.AdminOrder().PayNotify, // 后台充值订单
})
service.Pay().RegisterNotifyCall()
serverWg.Add(1)
@@ -107,6 +92,7 @@ var (
<-serverCloseSignal
websocket.Stop() // 关闭websocket
service.TCPServer().Stop(ctx) // 关闭tcp服务器
addons.StopModules(ctx) // 停止插件
_ = s.Shutdown() // 关闭http服务主服务建议放在最后一个关闭
g.Log().Debug(ctx, "http successfully closed ..")
serverWg.Done()

View File

@@ -5,6 +5,17 @@
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package consts
import (
"hotgo/internal/library/dict"
"hotgo/internal/model"
)
func init() {
dict.RegisterEnums("addonsGroupOptions", "插件分组", AddonsGroupOptions)
dict.RegisterEnums("addonsInstallStatus", "插件安装状态", AddonsInstallStatusOptions)
dict.RegisterEnums("addonsExtend", "插件扩展", AddonsExtendOptions)
}
const (
AddonsTag = "addons_" // 插件标签前缀
AddonsDir = "addons" // 插件路径
@@ -21,15 +32,16 @@ const (
AddonsGroupBiz = 8 // 行业解决方案
)
var AddonsGroupNameMap = map[int]string{
AddonsGroupPlug: "功能扩展",
AddonsGroupBusiness: "主要业务",
AddonsGroupThirdParty: "第三方插件",
AddonsGroupMiniApp: "小程序",
AddonsGroupCustomer: "客户关系",
AddonsGroupActivity: "营销及活动",
AddonsGroupServices: "常用服务及工具",
AddonsGroupBiz: "行业解决方案",
// AddonsGroupOptions 插件分组选项
var AddonsGroupOptions = []*model.Option{
dict.GenInfoOption(AddonsGroupPlug, "功能扩展"),
dict.GenInfoOption(AddonsGroupBusiness, "主要业务"),
dict.GenInfoOption(AddonsGroupThirdParty, "第三方插件"),
dict.GenInfoOption(AddonsGroupMiniApp, "小程序"),
dict.GenInfoOption(AddonsGroupCustomer, "客户关系"),
dict.GenInfoOption(AddonsGroupActivity, "营销及活动"),
dict.GenInfoOption(AddonsGroupServices, "常用服务及工具"),
dict.GenInfoOption(AddonsGroupBiz, "行业解决方案"),
}
var AddonsGroupIconMap = map[int]string{
@@ -49,8 +61,20 @@ const (
AddonsInstallStatusUn = 3 // 已卸载
)
var AddonsInstallStatusNameMap = map[int]string{
AddonsInstallStatusOk: "已安装",
AddonsInstallStatusNo: "安装",
AddonsInstallStatusUn: "已卸载",
// AddonsInstallStatusOptions 插件安装状态
var AddonsInstallStatusOptions = []*model.Option{
dict.GenInfoOption(AddonsInstallStatusOk, "安装"),
dict.GenInfoOption(AddonsInstallStatusNo, "未安装"),
dict.GenInfoOption(AddonsInstallStatusUn, "已卸载"),
}
const (
AddonsExtendResourcePublic = "resourcePublic"
AddonsExtendResourceTemplate = "resourceTemplate"
)
// AddonsExtendOptions 插件扩展选项
var AddonsExtendOptions = []*model.Option{
dict.GenInfoOption(AddonsExtendResourcePublic, "创建静态目录"),
dict.GenInfoOption(AddonsExtendResourceTemplate, "创建模板目录"),
}

View File

@@ -7,6 +7,7 @@ package consts
// cache
const (
CacheToken = "token" // 登录token
CacheTokenBind = "token_bind" // 登录用户身份绑定
CacheToken = "token" // 登录token
CacheTokenBind = "token_bind" // 登录用户身份绑定
CacheMultipartUpload = "multipart_upload" // 分片上传
)

View File

@@ -5,7 +5,15 @@
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package consts
import "github.com/gogf/gf/v2/frame/g"
import (
"hotgo/internal/library/dict"
"hotgo/internal/model"
)
func init() {
dict.RegisterEnums("creditType", "资金变动类型", CreditTypeOptions)
dict.RegisterEnums("creditGroup", "资金变动分组", CreditGroupOptions)
}
const (
CreditTypeBalance = "balance" // 余额
@@ -23,63 +31,18 @@ const (
)
// CreditTypeOptions 变动类型
var CreditTypeOptions = []g.Map{
{
"key": CreditTypeBalance,
"value": CreditTypeBalance,
"label": "余额",
"listClass": "success",
},
{
"key": CreditTypeIntegral,
"value": CreditTypeIntegral,
"label": "积分",
"listClass": "info",
},
var CreditTypeOptions = []*model.Option{
dict.GenSuccessOption(CreditTypeBalance, "余额"),
dict.GenInfoOption(CreditTypeIntegral, "积分"),
}
// CreditGroupOptions 变动分组
var CreditGroupOptions = []g.Map{
{
"key": CreditGroupDecr,
"value": CreditGroupDecr,
"label": "扣款",
"listClass": "warning",
},
{
"key": CreditGroupIncr,
"value": CreditGroupIncr,
"label": "加款",
"listClass": "success",
},
{
"key": CreditGroupOpDecr,
"value": CreditGroupOpDecr,
"label": "操作扣款",
"listClass": "warning",
},
{
"key": CreditGroupOpIncr,
"value": CreditGroupOpIncr,
"label": "操作加款",
"listClass": "success",
},
{
"key": CreditGroupBalanceRefund,
"value": CreditGroupBalanceRefund,
"label": "余额退款",
"listClass": "warning",
},
{
"key": CreditGroupBalanceRecharge,
"value": CreditGroupBalanceRecharge,
"label": "余额充值",
"listClass": "success",
},
{
"key": CreditGroupApplyCash,
"value": CreditGroupApplyCash,
"label": "申请提现",
"listClass": "info",
},
var CreditGroupOptions = []*model.Option{
dict.GenWarningOption(CreditGroupDecr, "扣款"),
dict.GenSuccessOption(CreditGroupIncr, "加款"),
dict.GenWarningOption(CreditGroupOpDecr, "操作扣款"),
dict.GenSuccessOption(CreditGroupOpIncr, "操作加款"),
dict.GenWarningOption(CreditGroupBalanceRefund, "余额退款"),
dict.GenSuccessOption(CreditGroupBalanceRecharge, "余额充值"),
dict.GenInfoOption(CreditGroupApplyCash, "申请提现"),
}

View File

@@ -5,7 +5,15 @@
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package consts
import "github.com/gogf/gf/v2/frame/g"
import (
"hotgo/internal/library/dict"
"hotgo/internal/model"
)
func init() {
dict.RegisterEnums("orderStatus", "订单状态", OrderStatusOptions)
dict.RegisterEnums("acceptRefundStatus", "订单退款受理状态", OrderAcceptRefundOptions)
}
// 订单分组
// 为不同的业务订单设置不同的分组,分组可以设置不同的业务回调方法
@@ -36,94 +44,23 @@ const (
OrderStatusReturnReject = 9 // 拒绝退款
)
var OrderStatusSlice = []int64{
OrderStatusALL,
OrderStatusNotPay, OrderStatusPay, OrderStatusShipments, OrderStatusDone, OrderStatusClose,
OrderStatusReturnRequest, OrderStatusReturning, OrderStatusReturned, OrderStatusReturnReject,
}
// OrderStatusOptions 订单状态选项
var OrderStatusOptions = []g.Map{
{
"key": OrderStatusALL,
"value": OrderStatusALL,
"label": "全部",
"listClass": "info",
},
{
"key": OrderStatusNotPay,
"value": OrderStatusNotPay,
"label": "待付款",
"listClass": "info",
},
{
"key": OrderStatusPay,
"value": OrderStatusPay,
"label": "已付款",
"listClass": "info",
},
{
"key": OrderStatusShipments,
"value": OrderStatusShipments,
"label": "已发货",
"listClass": "info",
},
{
"key": OrderStatusDone,
"value": OrderStatusDone,
"label": "已完成",
"listClass": "success",
},
{
"key": OrderStatusClose,
"value": OrderStatusClose,
"label": "已关闭",
"listClass": "default",
},
{
"key": OrderStatusReturnRequest,
"value": OrderStatusReturnRequest,
"label": "申请退款",
"listClass": "warning",
},
{
"key": OrderStatusReturning,
"value": OrderStatusReturning,
"label": "退款中",
"listClass": "default",
},
{
"key": OrderStatusReturned,
"value": OrderStatusReturned,
"label": "已退款",
"listClass": "success",
},
{
"key": OrderStatusReturnReject,
"value": OrderStatusReturnReject,
"label": "拒绝退款",
"listClass": "error",
},
var OrderStatusOptions = []*model.Option{
dict.GenInfoOption(OrderStatusALL, "全部"),
dict.GenInfoOption(OrderStatusNotPay, "待付款"),
dict.GenInfoOption(OrderStatusPay, "已付款"),
dict.GenInfoOption(OrderStatusShipments, "已发货"),
dict.GenSuccessOption(OrderStatusDone, "已完成"),
dict.GenDefaultOption(OrderStatusClose, "已关闭"),
dict.GenWarningOption(OrderStatusReturnRequest, "申请退款"),
dict.GenDefaultOption(OrderStatusReturning, "退款中"),
dict.GenErrorOption(OrderStatusReturned, "已退款"),
dict.GenWarningOption(OrderStatusReturnReject, "拒绝退款"),
}
// OrderAcceptRefundOptions 订单退款受理状态
var OrderAcceptRefundOptions = []g.Map{
{
"key": OrderStatusReturnRequest,
"value": OrderStatusReturnRequest,
"label": "申请退款",
"listClass": "warning",
},
{
"key": OrderStatusReturned,
"value": OrderStatusReturned,
"label": "已退款",
"listClass": "success",
},
{
"key": OrderStatusReturnReject,
"value": OrderStatusReturnReject,
"label": "拒绝退款",
"listClass": "error",
},
var OrderAcceptRefundOptions = []*model.Option{
dict.GenWarningOption(OrderStatusReturnRequest, "申请退款"),
dict.GenSuccessOption(OrderStatusReturned, "已退款"),
dict.GenErrorOption(OrderStatusReturnReject, "拒绝退款"),
}

View File

@@ -5,10 +5,17 @@
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package consts
import "github.com/gogf/gf/v2/frame/g"
import (
"hotgo/internal/library/dict"
"hotgo/internal/model"
)
// 支付方式
func init() {
dict.RegisterEnums("payType", "支付方式", PayTypeOptions)
}
const (
PayTypeALL = "" // 全部
PayTypeWxPay = "wxpay" // 微信支付
@@ -75,23 +82,8 @@ const (
)
// PayTypeOptions 支付方式选项
var PayTypeOptions = []g.Map{
{
"key": PayTypeWxPay,
"value": PayTypeWxPay,
"label": "微信支付",
"listClass": "success",
},
{
"key": PayTypeAliPay,
"value": PayTypeAliPay,
"label": "支付宝",
"listClass": "info",
},
{
"key": PayTypeQQPay,
"value": PayTypeQQPay,
"label": "QQ支付",
"listClass": "default",
},
var PayTypeOptions = []*model.Option{
dict.GenSuccessOption(PayTypeWxPay, "微信支付"),
dict.GenInfoOption(PayTypeAliPay, "支付宝"),
dict.GenDefaultOption(PayTypeQQPay, "QQ支付"),
}

View File

@@ -7,5 +7,5 @@ package consts
// VersionApp HotGo版本
const (
VersionApp = "2.12.1"
VersionApp = "2.13.1"
)

View File

@@ -10,7 +10,6 @@ package admin
import (
"context"
"hotgo/api/admin/creditslog"
"hotgo/internal/consts"
"hotgo/internal/service"
)
@@ -38,12 +37,3 @@ func (c *cCreditsLog) Export(ctx context.Context, req *creditslog.ExportReq) (re
err = service.AdminCreditsLog().Export(ctx, &req.CreditsLogListInp)
return
}
// Option 获取变动状态选项
func (c *cCreditsLog) Option(_ context.Context, _ *creditslog.OptionReq) (res *creditslog.OptionRes, err error) {
res = &creditslog.OptionRes{
CreditType: consts.CreditTypeOptions,
CreditGroup: consts.CreditGroupOptions,
}
return
}

View File

@@ -8,7 +8,6 @@ package admin
import (
"context"
"hotgo/api/admin/order"
"hotgo/internal/consts"
"hotgo/internal/service"
)
@@ -30,16 +29,6 @@ func (c *cOrder) ApplyRefund(ctx context.Context, req *order.ApplyRefundReq) (re
return
}
// Option 获取订单状态选项
func (c *cOrder) Option(ctx context.Context, req *order.OptionReq) (res *order.OptionRes, err error) {
res = &order.OptionRes{
Status: consts.OrderStatusOptions,
AcceptRefundStatus: consts.OrderAcceptRefundOptions,
PayType: consts.PayTypeOptions,
}
return
}
// Create 创建充值订单
func (c *cOrder) Create(ctx context.Context, req *order.CreateReq) (res *order.CreateRes, err error) {
data, err := service.AdminOrder().Create(ctx, &req.OrderCreateInp)

View File

@@ -35,3 +35,25 @@ func (c *cUpload) UploadFile(ctx context.Context, _ *common.UploadFileReq) (res
}
return service.CommonUpload().UploadFile(ctx, uploadType, file)
}
// CheckMultipart 检查文件分片
func (c *cUpload) CheckMultipart(ctx context.Context, req *common.CheckMultipartReq) (res *common.CheckMultipartRes, err error) {
data, err := service.CommonUpload().CheckMultipart(ctx, &req.CheckMultipartInp)
if err != nil {
return nil, err
}
res = new(common.CheckMultipartRes)
res.CheckMultipartModel = data
return
}
// UploadPart 上传分片
func (c *cUpload) UploadPart(ctx context.Context, req *common.UploadPartReq) (res *common.UploadPartRes, err error) {
data, err := service.CommonUpload().UploadPart(ctx, &req.UploadPartInp)
if err != nil {
return nil, err
}
res = new(common.UploadPartRes)
res.UploadPartModel = data
return
}

View File

@@ -30,18 +30,6 @@ func (c *cAddons) List(ctx context.Context, req *addons.ListReq) (res *addons.Li
return
}
// Selects 获取指定信息
func (c *cAddons) Selects(ctx context.Context, req *addons.SelectsReq) (res *addons.SelectsRes, err error) {
data, err := service.SysAddons().Selects(ctx, &req.AddonsSelectsInp)
if err != nil {
return
}
res = new(addons.SelectsRes)
res.AddonsSelectsModel = data
return
}
// Build 生成预览
func (c *cAddons) Build(ctx context.Context, req *addons.BuildReq) (res *addons.BuildRes, err error) {
err = service.SysAddons().Build(ctx, &req.AddonsBuildInp)

View File

@@ -1,14 +1,15 @@
// Package sys
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
// @AutoGenerate Version 2.11.5
// @AutoGenerate Version 2.12.10
package sys
import (
"context"
"hotgo/api/admin/curddemo"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
@@ -25,6 +26,10 @@ func (c *cCurdDemo) List(ctx context.Context, req *curddemo.ListReq) (res *curdd
return
}
if list == nil {
list = []*sysin.CurdDemoListModel{}
}
res = new(curddemo.ListRes)
res.List = list
res.PageRes.Pack(req, totalCount)

View File

@@ -11,26 +11,47 @@ import (
"hotgo/internal/consts"
)
var cacheResourcePath string
// GetResourcePath 获取插件资源路径
func GetResourcePath(ctx context.Context) string {
if len(cacheResourcePath) > 0 {
return cacheResourcePath
}
basePath := g.Cfg().MustGet(ctx, "hotgo.addonsResourcePath").String()
if basePath == "" {
g.Log().Warning(ctx, "addons GetResourcePath not config found:'hotgo.addonsResourcePath', use default values:'resource'")
basePath = "resource"
}
cacheResourcePath = basePath
return basePath
}
// GetModulePath 获取指定模块相对路径
func GetModulePath(name string) string {
return "./" + consts.AddonsDir + "/" + name
}
// ViewPath 默认的插件模板路径
func ViewPath(name string) string {
return consts.AddonsDir + "/" + name + "/" + "resource/template"
// 模板路径resource/addons/插件模块名称/template
// 例如resource/addons/hgexample/template
// 如果你不喜欢现在的风格,可以自行调整
func ViewPath(name, basePath string) string {
return basePath + "/" + consts.AddonsDir + "/" + name + "/template"
}
// StaticPath 默认的插件静态路映射关系
// 最终效果:对外访问地址:/addons/插件模块名称;静态资源路径:/addons/插件模块名称/设置的子路径。
// 如果你不喜欢现在的路由风格,可以自行调整
func StaticPath(name, path string) (string, string) {
return "/" + consts.AddonsDir + "/" + name, consts.AddonsDir + "/" + name + "/" + path
// 静态资源路径:resource/public/addons/插件模块名称/public
// 例如访问http://127.0.0.1:8000/addons/hgexample/default 则指向文件-> resource/addons/hgexample/public/default
// 如果你不喜欢现在的风格,可以自行调整
func StaticPath(name, basePath string) (string, string) {
return "/" + consts.AddonsDir + "/" + name, basePath + "/" + consts.AddonsDir + "/" + name + "/public"
}
// RouterPrefix 路由前缀
// 最终效果:/应用名称/插件模块名称/xxx/xxx。
// 如果你不喜欢现在的路由风格,可以自行调整
// 如果你不喜欢现在的风格,可以自行调整
func RouterPrefix(ctx context.Context, app, name string) string {
var prefix = "/"
if app != "" {

View File

@@ -7,40 +7,54 @@ package addons
import (
"context"
"fmt"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gstr"
"hotgo/internal/consts"
"hotgo/internal/model"
"hotgo/utility/validate"
"strconv"
"strings"
)
type BuildOption struct {
Skeleton Skeleton
Config *model.BuildAddonConfig
Extend []string `json:"extend" dc:"扩展功能"`
}
// Build 构建新插件
func Build(ctx context.Context, sk Skeleton, conf *model.BuildAddonConfig) (err error) {
func Build(ctx context.Context, option *BuildOption) (err error) {
var (
buildPath = "./" + consts.AddonsDir + "/" + sk.Name
modulesPath = "./" + consts.AddonsDir + "/modules/" + sk.Name + ".go"
webApiPath = gstr.Replace(conf.WebApiPath, "{$name}", sk.Name)
webViewsPath = gstr.Replace(conf.WebViewsPath, "{$name}", sk.Name)
resourcePath = GetResourcePath(ctx)
buildPath = "./" + consts.AddonsDir + "/" + option.Skeleton.Name
modulesPath = "./" + consts.AddonsDir + "/modules/" + option.Skeleton.Name + ".go"
webApiPath = gstr.Replace(option.Config.WebApiPath, "{$name}", option.Skeleton.Name)
webViewsPath = gstr.Replace(option.Config.WebViewsPath, "{$name}", option.Skeleton.Name)
replaces = map[string]string{
"@{.label}": sk.Label,
"@{.name}": sk.Name,
"@{.group}": strconv.Itoa(sk.Group),
"@{.brief}": sk.Brief,
"@{.description}": sk.Description,
"@{.author}": sk.Author,
"@{.version}": sk.Version,
"@{.label}": option.Skeleton.Label,
"@{.name}": option.Skeleton.Name,
"@{.group}": strconv.Itoa(option.Skeleton.Group),
"@{.brief}": option.Skeleton.Brief,
"@{.description}": option.Skeleton.Description,
"@{.author}": option.Skeleton.Author,
"@{.version}": option.Skeleton.Version,
"@{.hgVersion}": consts.VersionApp, // HG 版本
}
)
if resourcePath == "" {
err = gerror.New("请先设置一个有效的插件资源路径,配置名称:'hotgo.addonsResourcePath'")
return
}
if err = checkBuildDir(buildPath, modulesPath, webApiPath, webViewsPath); err != nil {
return
}
// scans directory recursively
list, err := gfile.ScanDirFunc(conf.SrcPath, "*", true, func(path string) string {
list, err := gfile.ScanDirFunc(option.Config.SrcPath, "*", true, func(path string) string {
return path
})
@@ -59,8 +73,8 @@ func Build(ctx context.Context, sk Skeleton, conf *model.BuildAddonConfig) (err
}
flowFile := gstr.ReplaceByMap(path, map[string]string{
gfile.RealPath(conf.SrcPath): "",
".template": "",
gfile.RealPath(option.Config.SrcPath): "",
".template": "",
})
flowFile = buildPath + "/" + flowFile
@@ -90,6 +104,23 @@ func Build(ctx context.Context, sk Skeleton, conf *model.BuildAddonConfig) (err
if err = gfile.PutContents(webViewsPath+"/config/system.vue", gstr.ReplaceByMap(webConfigSystem, replaces)); err != nil {
return
}
// 创建静态目录
if validate.InSlice(option.Extend, consts.AddonsExtendResourcePublic) {
_, staticPath := StaticPath(option.Skeleton.Name, resourcePath)
content := fmt.Sprintf(resourcePublicDefaultFile, option.Skeleton.Label)
if err = gfile.PutContents(staticPath+"/default", content); err != nil {
return
}
}
// 创建模板目录
if validate.InSlice(option.Extend, consts.AddonsExtendResourceTemplate) {
viewPath := ViewPath(option.Skeleton.Name, resourcePath)
if err = gfile.PutContents(viewPath+"/home/index.html", resourceTemplateHomeFile); err != nil {
return
}
}
return
}

View File

@@ -194,4 +194,39 @@ export function updateConfig(params) {
}
</style>
`
resourcePublicDefaultFile = "Hello这是创建插件 [%v] 时默认生成的一个静态目录文件,用于测试,当你看到这个提示时,说明已经联调成功啦!"
resourceTemplateHomeFile = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">
<meta name="keywords" content="@{.Keywords}"/>
<meta name="description" content="@{.Description}"/>
<title>@{.Title}</title>
<script type="text/javascript" src="/resource/home/js/jquery-3.6.0.min.js"></script>
<style>
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
background-color: #f6f6f6;
}
</style>
</head>
<body>
<div style="padding-top: 100px;text-align:center;">
<h1><p>Hello@{.Data.name}!!</p></h1>
<h2><p>@{.Data.module}</p></h2>
<h2><p>服务器时间:@{.Data.time}</p></h2>
</div>
</body>
<script>
</script>
</html>`
)

View File

@@ -15,8 +15,16 @@ import (
"hotgo/internal/model/input/form"
"sort"
"sync"
"time"
)
// Option 模块启动选项
type Option struct {
Server *ghttp.Server // http服务器
// 更多选项参数
// ..
}
// Skeleton 模块骨架
type Skeleton struct {
Label string `json:"label"` // 标识
@@ -37,32 +45,42 @@ func (s *Skeleton) GetModule() Module {
// Module 插件模块
type Module interface {
Init(ctx context.Context) // 初始化
InitRouter(ctx context.Context, group *ghttp.RouterGroup) // 初始化并注册路由
Ctx() context.Context // 上下文
GetSkeleton() *Skeleton // 架子
Install(ctx context.Context) error // 安装模块
Upgrade(ctx context.Context) error // 更新模块
UnInstall(ctx context.Context) error // 卸载模块
Start(option *Option) (err error) // 启动模块
Stop() (err error) // 停止模块
Ctx() context.Context // 上下文
GetSkeleton() *Skeleton // 获取模块
Install(ctx context.Context) (err error) // 安装模块
Upgrade(ctx context.Context) (err error) // 更新模块
UnInstall(ctx context.Context) (err error) // 卸载模块
}
var (
modules = make(map[string]Module, 0)
modules = make(map[string]Module)
mLock sync.Mutex
)
// InitModules 初始化所有已注册模块
func InitModules(ctx context.Context) {
for _, module := range modules {
module.Init(ctx)
// StartModules 启动所有已安装模块
func StartModules(ctx context.Context, option *Option) (err error) {
for _, module := range filterInstalled() {
if err = module.Start(option); err != nil {
return
}
}
// 为所有已安装模块设置静态资源路径
AddStaticPath(ctx, option.Server)
return
}
// RegisterModulesRouter 注册所有已安装模块路由
func RegisterModulesRouter(ctx context.Context, group *ghttp.RouterGroup) {
// StopModules 停止所有已安装模块
func StopModules(ctx context.Context) {
for _, module := range filterInstalled() {
module.InitRouter(ctx, group)
if err := module.Stop(); err != nil {
g.Log().Warningf(ctx, "StopModules err:%v, module:%v", err.Error(), module.GetSkeleton().Name)
time.Sleep(time.Second)
}
}
return
}
// RegisterModule 注册模块
@@ -123,9 +141,20 @@ func GetModuleRealPath(name string) string {
// NewView 初始化一个插件的模板引擎
func NewView(ctx context.Context, name string) *gview.View {
view := gview.New()
basePath := GetResourcePath(ctx)
if basePath == "" {
return nil
}
if err := view.SetPath(ViewPath(name)); err != nil {
view := gview.New()
path := ViewPath(name, basePath)
if !gfile.IsDir(gfile.RealPath(path)) {
g.Log().Warningf(ctx, "NewView template path does not exist:%v,default use of main module template.", path)
return nil
}
if err := view.SetPath(path); err != nil {
g.Log().Warningf(ctx, "NewView SetPath err:%+v", err)
return nil
}
@@ -145,12 +174,8 @@ func NewView(ctx context.Context, name string) *gview.View {
}
// AddStaticPath 设置插件静态目录映射
func AddStaticPath(ctx context.Context, server *ghttp.Server, p ...string) {
basePath := g.Cfg().MustGet(ctx, "server.serverRoot").String()
if len(p) > 0 {
basePath = p[0]
}
func AddStaticPath(ctx context.Context, server *ghttp.Server) {
basePath := GetResourcePath(ctx)
if basePath == "" {
return
}
@@ -160,7 +185,7 @@ func AddStaticPath(ctx context.Context, server *ghttp.Server, p ...string) {
prefix, path := StaticPath(name, basePath)
if !gres.Contains(path) {
if _, err := gfile.Search(path); err != nil {
g.Log().Warningf(ctx, `AddStaticPath failed: %v`, err)
g.Log().Warningf(ctx, `addons AddStaticPath failed: %v`, err)
continue
}
}

View File

@@ -11,8 +11,6 @@ import (
"github.com/gogf/gf/v2/os/gcache"
"github.com/gogf/gf/v2/os/gfile"
"hotgo/internal/library/cache/file"
"hotgo/internal/model"
"hotgo/internal/service"
)
// cache 缓存驱动
@@ -29,33 +27,24 @@ func Instance() *gcache.Cache {
// SetAdapter 设置缓存适配器
func SetAdapter(ctx context.Context) {
var adapter gcache.Adapter
conf, err := service.SysConfig().GetLoadCache(ctx)
if err != nil {
g.Log().Fatalf(ctx, "cache init err:%+v", err)
return
}
if conf == nil {
conf = new(model.CacheConfig)
g.Log().Info(ctx, "no cache driver is configured. default memory cache is used.")
}
switch conf.Adapter {
switch g.Cfg().MustGet(ctx, "cache.adapter").String() {
case "redis":
adapter = gcache.NewAdapterRedis(g.Redis())
case "file":
if conf.FileDir == "" {
fileDir := g.Cfg().MustGet(ctx, "cache.fileDir").String()
if fileDir == "" {
g.Log().Fatal(ctx, "file path must be configured for file caching.")
return
}
if !gfile.Exists(conf.FileDir) {
if err := gfile.Mkdir(conf.FileDir); err != nil {
if !gfile.Exists(fileDir) {
if err := gfile.Mkdir(fileDir); err != nil {
g.Log().Fatalf(ctx, "failed to create the cache directory. procedure, err:%+v", err)
return
}
}
adapter = file.NewAdapterFile(conf.FileDir)
adapter = file.NewAdapterFile(fileDir)
default:
adapter = gcache.NewAdapterMemory()
}

View File

@@ -0,0 +1,61 @@
// Package dict
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package dict
import (
"context"
"errors"
"fmt"
"hash/fnv"
"hotgo/internal/model"
"strconv"
)
const (
BuiltinId int64 = -1 // 内置字典ID
EnumsId int64 = -2 // 枚举字典ID
FuncId int64 = -3 // 方法字典ID
)
var NotExistKeyError = errors.New("not exist key")
// GetOptions 获取内置选项
func GetOptions(ctx context.Context, key string) (opts []*model.Option, err error) {
opts = GetEnumsOptions(key)
if opts != nil {
return
}
return GetFuncOptions(ctx, key)
}
// GetOptionsById 通过类型ID获取内置选项
func GetOptionsById(ctx context.Context, id int64) (opts []*model.Option, err error) {
for _, v := range GetAllEnums() {
if v.Id == id {
return v.Opts, nil
}
}
for _, v := range GetAllFunc() {
if v.Id == id {
return LoadFuncOptions(ctx, v)
}
}
err = NotExistKeyError
return
}
// GenIdHash 生成字典id
func GenIdHash(str string, t int64) int64 {
prefix := 10000 * t
h := fnv.New32a()
h.Write([]byte("dict" + str))
idStr := fmt.Sprintf("%d%d", prefix, int64(h.Sum32()))
id, _ := strconv.ParseInt(idStr, 10, 64)
return id
}

View File

@@ -0,0 +1,106 @@
// Package dict
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package dict
import (
"github.com/gogf/gf/v2/util/gconv"
"hash/fnv"
"hotgo/internal/model"
)
// GenDefaultOption 生成默认表格回显样式
func GenDefaultOption(key interface{}, label string) *model.Option {
return &model.Option{
Key: key,
Label: label,
Value: key,
ListClass: "default",
}
}
func GenSuccessOption(key interface{}, label string) *model.Option {
return &model.Option{
Key: key,
Label: label,
Value: key,
ListClass: "success",
}
}
func GenWarningOption(key interface{}, label string) *model.Option {
return &model.Option{
Key: key,
Label: label,
Value: key,
ListClass: "warning",
}
}
func GenErrorOption(key interface{}, label string) *model.Option {
return &model.Option{
Key: key,
Label: label,
Value: key,
ListClass: "error",
}
}
func GenInfoOption(key interface{}, label string) *model.Option {
return &model.Option{
Key: key,
Label: label,
Value: key,
ListClass: "info",
}
}
// GenCustomOption 生成自定义表格回显样式
func GenCustomOption(key interface{}, label string, custom string) *model.Option {
return &model.Option{
Key: key,
Label: label,
Value: key,
ListClass: custom,
}
}
// GenHashOption 根据不同label以hash算法生成表格回显样式
func GenHashOption(key interface{}, label string) *model.Option {
strings := []string{"default", "primary", "info", "success", "warning", "error"}
hash := fnv.New32()
tag := "default"
if _, err := hash.Write(gconv.Bytes(label)); err == nil {
index := int(hash.Sum32()) % len(strings)
tag = strings[index]
}
return &model.Option{
Key: key,
Label: label,
Value: key,
ListClass: tag,
}
}
// GetOptionLabel 通过key找到label
func GetOptionLabel(ses []*model.Option, key interface{}) string {
for _, v := range ses {
if gconv.String(v.Key) == gconv.String(key) {
return v.Label
}
}
return `Unknown`
}
// HasOptionKey 是否存在指定key
func HasOptionKey(ses []*model.Option, key interface{}) bool {
for _, v := range ses {
if gconv.String(v.Key) == gconv.String(key) {
return true
}
}
return false
}

View File

@@ -0,0 +1,72 @@
// Package dict
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package dict
import (
"fmt"
"hotgo/internal/model"
"sync"
)
type EnumsOption struct {
Id int64 // 字典ID由系统自动生成
Key string // 字典选项key
Label string // 字典选项标签名称
Opts []*model.Option // 数据选项
}
var (
enumsOptions = make(map[string]*EnumsOption)
eLock sync.Mutex
)
// GetAllEnums 获取所有枚举字典
func GetAllEnums() map[string]*EnumsOption {
return enumsOptions
}
// RegisterEnums 注册枚举字典选项
func RegisterEnums(key, label string, opts []*model.Option) {
eLock.Lock()
defer eLock.Unlock()
if len(key) == 0 {
panic("字典key不能为空")
}
if _, ok := enumsOptions[key]; ok {
panic(fmt.Sprintf("重复注册枚举字典选项:%v", key))
}
for _, v := range opts {
v.Type = key
}
enumsOptions[key] = &EnumsOption{
Id: GenIdHash(key, EnumsId),
Key: key,
Label: label,
Opts: opts,
}
}
// SaveEnums 更新枚举字典选项
func SaveEnums(key, label string, opts []*model.Option) {
eLock.Lock()
defer eLock.Unlock()
if _, ok := enumsOptions[key]; ok {
delete(enumsOptions, key)
}
RegisterEnums(key, label, opts)
}
// GetEnumsOptions 获取指定枚举字典的数据选项
func GetEnumsOptions(key string) []*model.Option {
enums, ok := enumsOptions[key]
if !ok {
return nil
}
return enums.Opts
}

View File

@@ -0,0 +1,131 @@
// Package dict
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package dict
import (
"context"
"fmt"
"hotgo/internal/model"
"sync"
)
// FuncDict 方法字典,实现本接口即可使用内置方法字典
type FuncDict func(ctx context.Context) (res []*model.Option, err error)
type FuncOption struct {
Id int64 // 字典ID由系统自动生成
Key string // 字典选项key
Label string // 字典选项标签名称
Fun FuncDict // 字典方法
Cache bool // 是否缓存数据选项
Opts []*model.Option // 缓存的数据选项
sync.Mutex
}
var (
funcOptions = make(map[string]*FuncOption)
fLock sync.Mutex
)
// GetAllFunc 获取所有方法字典
func GetAllFunc() map[string]*FuncOption {
return funcOptions
}
// RegisterFunc 注册方法字典选项
func RegisterFunc(key, label string, fun FuncDict, cache ...bool) {
fLock.Lock()
defer fLock.Unlock()
if len(key) == 0 {
panic("字典key不能为空")
}
if _, ok := funcOptions[key]; ok {
panic(fmt.Sprintf("重复注册方法选项:%v", key))
}
isCache := false
if len(cache) > 0 {
isCache = cache[0]
}
funcOptions[key] = &FuncOption{
Id: GenIdHash(key, FuncId),
Key: key,
Label: label,
Fun: fun,
Cache: isCache,
Opts: nil,
}
}
// SaveFunc 更新方法字典选项
func SaveFunc(key, label string, fun FuncDict, cache ...bool) {
fLock.Lock()
defer fLock.Unlock()
if _, ok := funcOptions[key]; ok {
delete(funcOptions, key)
}
RegisterFunc(key, label, fun, cache...)
}
// ClearFuncCache 清理指定方法缓存选项
func ClearFuncCache(key string) (err error) {
fun, ok := funcOptions[key]
if !ok {
err = NotExistKeyError
return
}
fun.Lock()
defer fun.Unlock()
if fun.Opts != nil {
fun.Opts = nil
}
return
}
// GetFuncOptions 获取指定方法字典的数据选项
func GetFuncOptions(ctx context.Context, key string) (res []*model.Option, err error) {
fun, ok := funcOptions[key]
if !ok {
err = NotExistKeyError
return
}
return LoadFuncOptions(ctx, fun)
}
// LoadFuncOptions 加载指定方法字典的数据选项
func LoadFuncOptions(ctx context.Context, fun *FuncOption) (res []*model.Option, err error) {
if fun.Cache && fun.Opts != nil {
res = fun.Opts
return
}
fun.Lock()
defer fun.Unlock()
if fun.Cache && fun.Opts != nil {
res = fun.Opts
return
}
res, err = fun.Fun(ctx)
if err != nil {
return nil, err
}
for k := range res {
res[k].Type = fun.Key
}
if fun.Cache {
fun.Opts = res
}
return
}

View File

@@ -44,8 +44,9 @@ type cBuild struct {
}
const (
cBuildBrief = `cross-building go project for lots of platforms`
cBuildEg = `
cBuildDefaultFile = "main.go"
cBuildBrief = `cross-building go project for lots of platforms`
cBuildEg = `
gf build main.go
gf build main.go --ps public,template
gf build main.go --cgo
@@ -123,7 +124,7 @@ type cBuildInput struct {
Arch string `short:"a" name:"arch" brief:"output binary architecture, multiple arch separated with ','"`
System string `short:"s" name:"system" brief:"output binary system, multiple os separated with ','"`
Output string `short:"o" name:"output" brief:"output binary path, used when building single binary file"`
Path string `short:"p" name:"path" brief:"output binary directory path, default is './temp'" d:"./temp"`
Path string `short:"p" name:"path" brief:"output binary directory path, default is '.'" d:"."`
Extra string `short:"e" name:"extra" brief:"extra custom \"go build\" options"`
Mod string `short:"m" name:"mod" brief:"like \"-mod\" option of \"go build\", use \"-m none\" to disable go module"`
Cgo bool `short:"c" name:"cgo" brief:"enable or disable cgo feature, it's disabled in default" orphan:"true"`
@@ -152,12 +153,13 @@ func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, e
var (
parser = gcmd.ParserFromCtx(ctx)
file = parser.GetArg(2).String()
file = in.File
)
if len(file) < 1 {
if file == "" {
file = parser.GetArg(2).String()
// Check and use the main.go file.
if gfile.Exists("main.go") {
file = "main.go"
if gfile.Exists(cBuildDefaultFile) {
file = cBuildDefaultFile
} else {
mlog.Fatal("build file path is empty or main.go not found in current working directory")
}
@@ -239,13 +241,7 @@ func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, e
} else {
genv.MustSet("CGO_ENABLED", "0")
}
var (
cmd = ""
ext = ""
)
for system, item := range platformMap {
cmd = ""
ext = ""
if len(customSystems) > 0 && customSystems[0] != "all" && !gstr.InArray(customSystems, system) {
continue
}
@@ -258,58 +254,22 @@ func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, e
// For example:
// `gf build`
// `gf build -o main.exe`
if runtime.GOOS == "windows" {
ext = ".exe"
}
var outputPath string
if len(in.Output) > 0 {
outputPath = "-o " + in.Output
} else {
outputPath = "-o " + in.Name + ext
}
cmd = fmt.Sprintf(
`go build %s -ldflags "%s" %s %s`,
outputPath, ldFlags, in.Extra, file,
c.doBinaryBuild(
ctx, file,
in.Output, in.Path,
runtime.GOOS, runtime.GOARCH, in.Name, ldFlags, in.Extra,
in.ExitWhenError,
true,
)
} else {
// Cross-building, output the compiled binary to specified path.
if system == "windows" {
ext = ".exe"
}
genv.MustSet("GOOS", system)
genv.MustSet("GOARCH", arch)
var outputPath string
if len(in.Output) > 0 {
outputPath = "-o " + in.Output
} else {
outputPath = fmt.Sprintf(
"-o %s/%s/%s%s",
in.Path, system+"_"+arch, in.Name, ext,
)
}
cmd = fmt.Sprintf(
`go build %s -ldflags "%s" %s%s`,
outputPath, ldFlags, in.Extra, file,
c.doBinaryBuild(
ctx, file,
in.Output, in.Path,
system, arch, in.Name, ldFlags, in.Extra,
in.ExitWhenError,
false,
)
}
mlog.Debug(fmt.Sprintf("build for GOOS=%s GOARCH=%s", system, arch))
mlog.Debug(cmd)
// It's not necessary printing the complete command string.
cmdShow, _ := gregex.ReplaceString(`\s+(-ldflags ".+?")\s+`, " ", cmd)
mlog.Print(cmdShow)
if result, err := gproc.ShellExec(ctx, cmd); err != nil {
mlog.Printf(
"failed to build, os:%s, arch:%s, error:\n%s\n\n%s\n",
system, arch, gstr.Trim(result),
`you may use command option "--debug" to enable debug info and check the details`,
)
if in.ExitWhenError {
os.Exit(1)
}
} else {
mlog.Debug(gstr.Trim(result))
}
// single binary building.
if len(customSystems) == 0 && len(customArches) == 0 {
goto buildDone
@@ -322,6 +282,68 @@ buildDone:
return
}
func (c cBuild) doBinaryBuild(
ctx context.Context,
filePath string,
outputPath, dirPath string,
system, arch, name, ldFlags, extra string,
exitWhenError bool,
singleBuild bool,
) {
var (
cmd string
ext string
)
// Cross-building, output the compiled binary to specified path.
if system == "windows" {
ext = ".exe"
}
genv.MustSet("GOOS", system)
genv.MustSet("GOARCH", arch)
if outputPath != "" {
outputPath = "-o " + outputPath
} else {
if dirPath == "" {
dirPath = "."
} else {
dirPath = gstr.TrimRight(dirPath, "/")
}
if singleBuild {
outputPath = fmt.Sprintf(
"-o %s/%s%s",
dirPath, name, ext,
)
} else {
outputPath = fmt.Sprintf(
"-o %s/%s/%s%s",
dirPath, system+"_"+arch, name, ext,
)
}
}
cmd = fmt.Sprintf(
`go build %s -ldflags "%s" %s%s`,
outputPath, ldFlags, extra, filePath,
)
mlog.Debug(fmt.Sprintf("build for GOOS=%s GOARCH=%s", system, arch))
mlog.Debug(cmd)
// It's not necessary printing the complete command string, filtering ldFlags.
cmdShow, _ := gregex.ReplaceString(`\s+(-ldflags ".+?")\s+`, " ", cmd)
mlog.Print(cmdShow)
if result, err := gproc.ShellExec(ctx, cmd); err != nil {
mlog.Printf(
"failed to build, os:%s, arch:%s, error:\n%s\n\n%s\n",
system, arch, gstr.Trim(result),
`you may use command option "--debug" to enable debug info and check the details`,
)
if exitWhenError {
os.Exit(1)
}
} else {
mlog.Debug(gstr.Trim(result))
}
}
// getBuildInVarStr retrieves and returns the custom build-in variables in configuration
// file as json.
func (c cBuild) getBuildInVarStr(ctx context.Context, in cBuildInput) string {

View File

@@ -99,6 +99,7 @@ func (c cRun) Index(ctx context.Context, in cRunInput) (out *cRunOutput, err err
if len(in.WatchPaths) == 1 {
in.WatchPaths = strings.Split(in.WatchPaths[0], ",")
mlog.Printf("watchPaths: %v", in.WatchPaths)
}
app := &cRunApp{
@@ -109,8 +110,9 @@ func (c cRun) Index(ctx context.Context, in cRunInput) (out *cRunOutput, err err
WatchPaths: in.WatchPaths,
}
dirty := gtype.NewBool()
_, err = gfsnotify.Add(gfile.RealPath("."), func(event *gfsnotify.Event) {
if gfile.ExtName(event.Path) != "go" && !matchWatchPaths(app.WatchPaths, event.Path) {
callbackFunc := func(event *gfsnotify.Event) {
if gfile.ExtName(event.Path) != "go" {
return
}
@@ -125,10 +127,22 @@ func (c cRun) Index(ctx context.Context, in cRunInput) (out *cRunOutput, err err
mlog.Printf(`watched file changes: %s`, event.String())
app.Run(ctx)
})
})
if err != nil {
mlog.Fatal(err)
}
if len(app.WatchPaths) > 0 {
for _, path := range app.WatchPaths {
_, err = gfsnotify.Add(gfile.RealPath(path), callbackFunc)
if err != nil {
mlog.Fatal(err)
}
}
} else {
_, err = gfsnotify.Add(gfile.RealPath("."), callbackFunc)
if err != nil {
mlog.Fatal(err)
}
}
go app.Run(ctx)
select {}
}

View File

@@ -0,0 +1,151 @@
// Copyright GoFrame gf 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 cmd
import (
"testing"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gproc"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/text/gstr"
)
func Test_Build_Single(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
buildPath = gtest.DataPath(`build`, `single`)
pwd = gfile.Pwd()
binaryName = `t.test`
binaryPath = gtest.DataPath(`build`, `single`, binaryName)
f = cBuild{}
)
defer gfile.Chdir(pwd)
defer gfile.Remove(binaryPath)
err := gfile.Chdir(buildPath)
t.AssertNil(err)
t.Assert(gfile.Exists(binaryPath), false)
_, err = f.Index(ctx, cBuildInput{
File: cBuildDefaultFile,
Name: binaryName,
})
t.AssertNil(err)
t.Assert(gfile.Exists(binaryPath), true)
})
}
func Test_Build_Single_Output(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
buildPath = gtest.DataPath(`build`, `single`)
pwd = gfile.Pwd()
binaryName = `tt`
binaryDirPath = gtest.DataPath(`build`, `single`, `tt`)
binaryPath = gtest.DataPath(`build`, `single`, `tt`, binaryName)
f = cBuild{}
)
defer gfile.Chdir(pwd)
defer gfile.Remove(binaryDirPath)
err := gfile.Chdir(buildPath)
t.AssertNil(err)
t.Assert(gfile.Exists(binaryPath), false)
_, err = f.Index(ctx, cBuildInput{
Output: "./tt/tt",
})
t.AssertNil(err)
t.Assert(gfile.Exists(binaryPath), true)
})
}
func Test_Build_Single_Path(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
buildPath = gtest.DataPath(`build`, `single`)
pwd = gfile.Pwd()
dirName = "ttt"
binaryName = `main`
binaryDirPath = gtest.DataPath(`build`, `single`, dirName)
binaryPath = gtest.DataPath(`build`, `single`, dirName, binaryName)
f = cBuild{}
)
defer gfile.Chdir(pwd)
defer gfile.Remove(binaryDirPath)
err := gfile.Chdir(buildPath)
t.AssertNil(err)
t.Assert(gfile.Exists(binaryPath), false)
_, err = f.Index(ctx, cBuildInput{
Path: "ttt",
})
t.AssertNil(err)
t.Assert(gfile.Exists(binaryPath), true)
})
}
func Test_Build_Single_VarMap(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
buildPath = gtest.DataPath(`build`, `varmap`)
pwd = gfile.Pwd()
binaryName = `main`
binaryPath = gtest.DataPath(`build`, `varmap`, binaryName)
f = cBuild{}
)
defer gfile.Chdir(pwd)
defer gfile.Remove(binaryPath)
err := gfile.Chdir(buildPath)
t.AssertNil(err)
t.Assert(gfile.Exists(binaryPath), false)
_, err = f.Index(ctx, cBuildInput{
VarMap: map[string]interface{}{
"a": "1",
"b": "2",
},
})
t.AssertNil(err)
t.Assert(gfile.Exists(binaryPath), true)
result, err := gproc.ShellExec(ctx, binaryPath)
t.AssertNil(err)
t.Assert(gstr.Contains(result, `a: 1`), true)
t.Assert(gstr.Contains(result, `b: 2`), true)
})
}
func Test_Build_Multiple(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
buildPath = gtest.DataPath(`build`, `multiple`)
pwd = gfile.Pwd()
binaryDirPath = gtest.DataPath(`build`, `multiple`, `temp`)
binaryPathLinux = gtest.DataPath(`build`, `multiple`, `temp`, `v1.1`, `linux_amd64`, `ttt`)
binaryPathWindows = gtest.DataPath(`build`, `multiple`, `temp`, `v1.1`, `windows_amd64`, `ttt.exe`)
f = cBuild{}
)
defer gfile.Chdir(pwd)
defer gfile.Remove(binaryDirPath)
err := gfile.Chdir(buildPath)
t.AssertNil(err)
t.Assert(gfile.Exists(binaryPathLinux), false)
t.Assert(gfile.Exists(binaryPathWindows), false)
_, err = f.Index(ctx, cBuildInput{
File: "multiple.go",
Name: "ttt",
Version: "v1.1",
Arch: "amd64",
System: "linux, windows",
Path: "temp",
})
t.AssertNil(err)
t.Assert(gfile.Exists(binaryPathLinux), true)
t.Assert(gfile.Exists(binaryPathWindows), true)
})
}

View File

@@ -7,13 +7,14 @@
package cmd
import (
"path/filepath"
"testing"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/util/guid"
"github.com/gogf/gf/v2/util/gutil"
"hotgo/internal/library/hggen/internal/cmd/genctrl"
"path/filepath"
"testing"
)
func Test_Gen_Ctrl_Default(t *testing.T) {

View File

@@ -11,6 +11,7 @@ import (
"path/filepath"
"testing"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/text/gstr"
@@ -209,3 +210,261 @@ func Test_Gen_Dao_TypeMapping(t *testing.T) {
}
})
}
func execSqlFile(db gdb.DB, filePath string, args ...any) error {
sqlContent := fmt.Sprintf(
gfile.GetContents(filePath),
args...,
)
array := gstr.SplitAndTrim(sqlContent, ";")
for _, v := range array {
if _, err := db.Exec(ctx, v); err != nil {
return err
}
}
return nil
}
func Test_Gen_Dao_Issue2572(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
err error
db = testDB
table1 = "user1"
table2 = "user2"
issueDirPath = gtest.DataPath(`issue`, `2572`)
)
t.AssertNil(execSqlFile(db, gtest.DataPath(`issue`, `2572`, `sql1.sql`)))
t.AssertNil(execSqlFile(db, gtest.DataPath(`issue`, `2572`, `sql2.sql`)))
defer dropTableWithDb(db, table1)
defer dropTableWithDb(db, table2)
var (
path = gfile.Temp(guid.S())
group = "test"
in = gendao.CGenDaoInput{
Path: path,
Link: link,
Tables: "",
TablesEx: "",
Group: group,
Prefix: "",
RemovePrefix: "",
JsonCase: "SnakeScreaming",
ImportPrefix: "",
DaoPath: "",
DoPath: "",
EntityPath: "",
TplDaoIndexPath: "",
TplDaoInternalPath: "",
TplDaoDoPath: "",
TplDaoEntityPath: "",
StdTime: false,
WithTime: false,
GJsonSupport: false,
OverwriteDao: false,
DescriptionTag: false,
NoJsonTag: false,
NoModelComment: false,
Clear: false,
TypeMapping: nil,
}
)
err = gutil.FillStructWithDefault(&in)
t.AssertNil(err)
err = gfile.Copy(issueDirPath, path)
t.AssertNil(err)
defer gfile.Remove(path)
pwd := gfile.Pwd()
err = gfile.Chdir(path)
t.AssertNil(err)
defer gfile.Chdir(pwd)
_, err = gendao.CGenDao{}.Dao(ctx, in)
t.AssertNil(err)
generatedFiles, err := gfile.ScanDir(path, "*.go", true)
t.AssertNil(err)
t.Assert(len(generatedFiles), 8)
for i, generatedFile := range generatedFiles {
generatedFiles[i] = gstr.TrimLeftStr(generatedFile, path)
}
t.Assert(gstr.InArray(generatedFiles, "/dao/internal/user_1.go"), true)
t.Assert(gstr.InArray(generatedFiles, "/dao/internal/user_2.go"), true)
t.Assert(gstr.InArray(generatedFiles, "/dao/user_1.go"), true)
t.Assert(gstr.InArray(generatedFiles, "/dao/user_2.go"), true)
t.Assert(gstr.InArray(generatedFiles, "/model/do/user_1.go"), true)
t.Assert(gstr.InArray(generatedFiles, "/model/do/user_2.go"), true)
t.Assert(gstr.InArray(generatedFiles, "/model/entity/user_1.go"), true)
t.Assert(gstr.InArray(generatedFiles, "/model/entity/user_2.go"), true)
})
}
func Test_Gen_Dao_Issue2616(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
err error
db = testDB
table1 = "user1"
table2 = "user2"
issueDirPath = gtest.DataPath(`issue`, `2616`)
)
t.AssertNil(execSqlFile(db, gtest.DataPath(`issue`, `2616`, `sql1.sql`)))
t.AssertNil(execSqlFile(db, gtest.DataPath(`issue`, `2616`, `sql2.sql`)))
defer dropTableWithDb(db, table1)
defer dropTableWithDb(db, table2)
var (
path = gfile.Temp(guid.S())
group = "test"
in = gendao.CGenDaoInput{
Path: path,
Link: link,
Tables: "",
TablesEx: "",
Group: group,
Prefix: "",
RemovePrefix: "",
JsonCase: "SnakeScreaming",
ImportPrefix: "",
DaoPath: "",
DoPath: "",
EntityPath: "",
TplDaoIndexPath: "",
TplDaoInternalPath: "",
TplDaoDoPath: "",
TplDaoEntityPath: "",
StdTime: false,
WithTime: false,
GJsonSupport: false,
OverwriteDao: false,
DescriptionTag: false,
NoJsonTag: false,
NoModelComment: false,
Clear: false,
TypeMapping: nil,
}
)
err = gutil.FillStructWithDefault(&in)
t.AssertNil(err)
err = gfile.Copy(issueDirPath, path)
t.AssertNil(err)
defer gfile.Remove(path)
pwd := gfile.Pwd()
err = gfile.Chdir(path)
t.AssertNil(err)
defer gfile.Chdir(pwd)
_, err = gendao.CGenDao{}.Dao(ctx, in)
t.AssertNil(err)
generatedFiles, err := gfile.ScanDir(path, "*.go", true)
t.AssertNil(err)
t.Assert(len(generatedFiles), 8)
for i, generatedFile := range generatedFiles {
generatedFiles[i] = gstr.TrimLeftStr(generatedFile, path)
}
t.Assert(gstr.InArray(generatedFiles, "/dao/internal/user_1.go"), true)
t.Assert(gstr.InArray(generatedFiles, "/dao/internal/user_2.go"), true)
t.Assert(gstr.InArray(generatedFiles, "/dao/user_1.go"), true)
t.Assert(gstr.InArray(generatedFiles, "/dao/user_2.go"), true)
t.Assert(gstr.InArray(generatedFiles, "/model/do/user_1.go"), true)
t.Assert(gstr.InArray(generatedFiles, "/model/do/user_2.go"), true)
t.Assert(gstr.InArray(generatedFiles, "/model/entity/user_1.go"), true)
t.Assert(gstr.InArray(generatedFiles, "/model/entity/user_2.go"), true)
// Key string to check if overwrite the dao files.
// dao user1 is not be overwritten as configured in config.yaml.
// dao user2 is to be overwritten as configured in config.yaml.
var (
keyStr = `// I am not overwritten.`
daoUser1Content = gfile.GetContents(path + "/dao/user_1.go")
daoUser2Content = gfile.GetContents(path + "/dao/user_2.go")
)
t.Assert(gstr.Contains(daoUser1Content, keyStr), true)
t.Assert(gstr.Contains(daoUser2Content, keyStr), false)
})
}
// https://github.com/gogf/gf/issues/2746
func Test_Gen_Dao_Issue2746(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
err error
mdb gdb.DB
link2746 = "mariadb:root:12345678@tcp(127.0.0.1:3307)/test?loc=Local&parseTime=true"
table = "issue2746"
sqlContent = fmt.Sprintf(
gtest.DataContent(`issue`, `2746`, `sql.sql`),
table,
)
)
mdb, err = gdb.New(gdb.ConfigNode{
Link: link2746,
})
t.AssertNil(err)
array := gstr.SplitAndTrim(sqlContent, ";")
for _, v := range array {
if _, err = mdb.Exec(ctx, v); err != nil {
t.AssertNil(err)
}
}
defer dropTableWithDb(mdb, table)
var (
path = gfile.Temp(guid.S())
group = "test"
in = gendao.CGenDaoInput{
Path: path,
Link: link2746,
Tables: "",
TablesEx: "",
Group: group,
Prefix: "",
RemovePrefix: "",
JsonCase: "SnakeScreaming",
ImportPrefix: "",
DaoPath: "",
DoPath: "",
EntityPath: "",
TplDaoIndexPath: "",
TplDaoInternalPath: "",
TplDaoDoPath: "",
TplDaoEntityPath: "",
StdTime: false,
WithTime: false,
GJsonSupport: true,
OverwriteDao: false,
DescriptionTag: false,
NoJsonTag: false,
NoModelComment: false,
Clear: false,
TypeMapping: nil,
}
)
err = gutil.FillStructWithDefault(&in)
t.AssertNil(err)
err = gfile.Mkdir(path)
t.AssertNil(err)
_, err = gendao.CGenDao{}.Dao(ctx, in)
t.AssertNil(err)
defer gfile.Remove(path)
var (
file = filepath.FromSlash(path + "/model/entity/issue_2746.go")
expectContent = gtest.DataContent(`issue`, `2746`, `issue_2746.go`)
)
t.Assert(expectContent, gfile.GetContents(file))
})
}

View File

@@ -7,13 +7,14 @@
package cmd
import (
"path/filepath"
"testing"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/util/guid"
"github.com/gogf/gf/v2/util/gutil"
"hotgo/internal/library/hggen/internal/cmd/genservice"
"path/filepath"
"testing"
)
func Test_Gen_Service_Default(t *testing.T) {

View File

@@ -202,6 +202,7 @@ type (
Clear bool `name:"clear" short:"a" brief:"{CGenDaoBriefClear}" orphan:"true"`
TypeMapping map[DBFieldTypeName]CustomAttributeType `name:"typeMapping" short:"y" brief:"{CGenDaoBriefTypeMapping}" orphan:"true"`
genItems *CGenDaoInternalGenItems
}
CGenDaoOutput struct{}
@@ -220,6 +221,7 @@ type (
)
func (c CGenDao) Dao(ctx context.Context, in CGenDaoInput) (out *CGenDaoOutput, err error) {
in.genItems = newCGenDaoInternalGenItems()
if g.Cfg().Available(ctx) {
v := g.Cfg().MustGet(ctx, CGenDaoConfig)
if v.IsSlice() {
@@ -232,12 +234,16 @@ func (c CGenDao) Dao(ctx context.Context, in CGenDaoInput) (out *CGenDaoOutput,
} else {
doGenDaoForArray(ctx, -1, in)
}
doClear(in.genItems)
mlog.Print("done!")
return
}
// doGenDaoForArray implements the "gen dao" command for configuration array.
func doGenDaoForArray(ctx context.Context, index int, in CGenDaoInput) {
if in.genItems == nil {
in.genItems = newCGenDaoInternalGenItems()
}
var (
err error
db gdb.DB
@@ -312,6 +318,8 @@ func doGenDaoForArray(ctx context.Context, index int, in CGenDaoInput) {
newTableNames[i] = newTableName
}
in.genItems.Scale()
// Dao: index and internal.
generateDao(ctx, CGenDaoInternalInput{
CGenDaoInput: in,
@@ -333,6 +341,8 @@ func doGenDaoForArray(ctx context.Context, index int, in CGenDaoInput) {
TableNames: tableNames,
NewTableNames: newTableNames,
})
in.genItems.SetClear(in.Clear)
}
func getImportPartContent(ctx context.Context, source string, isDo bool, appendImports []string) string {

View File

@@ -7,22 +7,40 @@
package gendao
import (
"context"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gstr"
"hotgo/internal/library/hggen/internal/utility/mlog"
"hotgo/internal/library/hggen/internal/utility/utils"
)
func doClear(ctx context.Context, dirPath string, force bool) {
files, err := gfile.ScanDirFile(dirPath, "*.go", true)
if err != nil {
mlog.Fatal(err)
func doClear(items *CGenDaoInternalGenItems) {
var allGeneratedFilePaths = make([]string, 0)
for _, item := range items.Items {
allGeneratedFilePaths = append(allGeneratedFilePaths, item.GeneratedFilePaths...)
}
for _, file := range files {
if force || utils.IsFileDoNotEdit(file) {
if err = gfile.Remove(file); err != nil {
for i, v := range allGeneratedFilePaths {
allGeneratedFilePaths[i] = gfile.RealPath(v)
}
for _, item := range items.Items {
if !item.Clear {
continue
}
doClearItem(item, allGeneratedFilePaths)
}
}
func doClearItem(item CGenDaoInternalGenItem, allGeneratedFilePaths []string) {
var generatedFilePaths = make([]string, 0)
for _, dirPath := range item.StorageDirPaths {
filePaths, err := gfile.ScanDirFile(dirPath, "*.go", true)
if err != nil {
mlog.Fatal(err)
}
generatedFilePaths = append(generatedFilePaths, filePaths...)
}
for _, filePath := range generatedFilePaths {
if !gstr.InArray(allGeneratedFilePaths, filePath) {
if err := gfile.Remove(filePath); err != nil {
mlog.Print(err)
}
}

View File

@@ -30,9 +30,7 @@ func generateDao(ctx context.Context, in CGenDaoInternalInput) {
dirPathDao = gfile.Join(in.Path, in.DaoPath)
dirPathDaoInternal = gfile.Join(dirPathDao, "internal")
)
if in.Clear {
doClear(ctx, dirPathDao, true)
}
in.genItems.AppendDirPath(dirPathDao)
for i := 0; i < len(in.TableNames); i++ {
generateDaoSingle(ctx, generateDaoSingleInput{
CGenDaoInternalInput: in,
@@ -108,6 +106,8 @@ type generateDaoIndexInput struct {
func generateDaoIndex(in generateDaoIndexInput) {
path := filepath.FromSlash(gfile.Join(in.DirPathDao, in.FileName+".go"))
// It should add path to result slice whenever it would generate the path file or not.
in.genItems.AppendGeneratedFilePath(path)
if in.OverwriteDao || !gfile.Exists(path) {
indexContent := gstr.ReplaceByMap(
getTemplateFromPathOrDefault(in.TplDaoIndexPath, consts.TemplateGenDaoIndexContent),
@@ -151,6 +151,7 @@ func generateDaoInternal(in generateDaoInternalInput) {
tplVarColumnNames: gstr.Trim(generateColumnNamesForDao(in.FieldMap, removeFieldPrefixArray)),
})
modelContent = replaceDefaultVar(in.CGenDaoInternalInput, modelContent)
in.genItems.AppendGeneratedFilePath(path)
if err := gfile.PutContents(path, strings.TrimSpace(modelContent)); err != nil {
mlog.Fatalf("writing content to '%s' failed: %v", path, err)
} else {

View File

@@ -24,9 +24,7 @@ import (
func generateDo(ctx context.Context, in CGenDaoInternalInput) {
var dirPathDo = filepath.FromSlash(gfile.Join(in.Path, in.DoPath))
if in.Clear {
doClear(ctx, dirPathDo, false)
}
in.genItems.AppendDirPath(dirPathDo)
in.NoJsonTag = true
in.DescriptionTag = false
in.NoModelComment = false
@@ -66,6 +64,7 @@ func generateDo(ctx context.Context, in CGenDaoInternalInput) {
gstr.CaseCamel(newTableName),
structDefinition,
)
in.genItems.AppendGeneratedFilePath(doFilePath)
err = gfile.PutContents(doFilePath, strings.TrimSpace(modelContent))
if err != nil {
mlog.Fatalf(`writing content to "%s" failed: %v`, doFilePath, err)

View File

@@ -22,9 +22,7 @@ import (
func generateEntity(ctx context.Context, in CGenDaoInternalInput) {
var dirPathEntity = gfile.Join(in.Path, in.EntityPath)
if in.Clear {
doClear(ctx, dirPathEntity, false)
}
in.genItems.AppendDirPath(dirPathEntity)
// Model content.
for i, tableName := range in.TableNames {
fieldMap, err := in.DB.TableFields(ctx, tableName)
@@ -51,7 +49,7 @@ func generateEntity(ctx context.Context, in CGenDaoInternalInput) {
appendImports,
)
)
in.genItems.AppendGeneratedFilePath(entityFilePath)
err = gfile.PutContents(entityFilePath, strings.TrimSpace(entityContent))
if err != nil {
mlog.Fatalf("writing content to '%s' failed: %v", entityFilePath, err)

View File

@@ -0,0 +1,53 @@
// Copyright GoFrame gf 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 gendao
type (
CGenDaoInternalGenItems struct {
index int
Items []CGenDaoInternalGenItem
}
CGenDaoInternalGenItem struct {
Clear bool
StorageDirPaths []string
GeneratedFilePaths []string
}
)
func newCGenDaoInternalGenItems() *CGenDaoInternalGenItems {
return &CGenDaoInternalGenItems{
index: -1,
Items: make([]CGenDaoInternalGenItem, 0),
}
}
func (i *CGenDaoInternalGenItems) Scale() {
i.Items = append(i.Items, CGenDaoInternalGenItem{
StorageDirPaths: make([]string, 0),
GeneratedFilePaths: make([]string, 0),
Clear: false,
})
i.index++
}
func (i *CGenDaoInternalGenItems) SetClear(clear bool) {
i.Items[i.index].Clear = clear
}
func (i CGenDaoInternalGenItems) AppendDirPath(storageDirPath string) {
i.Items[i.index].StorageDirPaths = append(
i.Items[i.index].StorageDirPaths,
storageDirPath,
)
}
func (i CGenDaoInternalGenItems) AppendGeneratedFilePath(generatedFilePath string) {
i.Items[i.index].GeneratedFilePaths = append(
i.Items[i.index].GeneratedFilePaths,
generatedFilePath,
)
}

View File

@@ -174,7 +174,7 @@ func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGe
// Parse single logic package folder.
var (
// StructName => FunctionDefinitions
srcPkgInterfaceMap = make(map[string]*garray.StrArray)
srcPkgInterfaceMap = gmap.NewListMap()
srcImportedPackages = garray.NewSortedStrArray().SetUnique(true)
importAliasToPathMap = gmap.NewStrStrMap() // for conflict imports check. alias => import path(with `"`)
importPathToAliasMap = gmap.NewStrStrMap() // for conflict imports check. import path(with `"`) => alias

View File

@@ -12,6 +12,7 @@ import (
"go/token"
"github.com/gogf/gf/v2/container/garray"
"github.com/gogf/gf/v2/container/gmap"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
)
@@ -99,10 +100,9 @@ func (c CGenService) calculateCodeCommented(in CGenServiceInput, fileContent str
}
func (c CGenService) calculateInterfaceFunctions(
in CGenServiceInput, fileContent string, srcPkgInterfaceMap map[string]*garray.StrArray,
in CGenServiceInput, fileContent string, srcPkgInterfaceMap *gmap.ListMap,
) (err error) {
var (
ok bool
matches [][]string
srcPkgInterfaceFuncArray *garray.StrArray
)
@@ -142,9 +142,11 @@ func (c CGenService) calculateInterfaceFunctions(
continue
}
structName = gstr.CaseCamel(structMatch[1])
if srcPkgInterfaceFuncArray, ok = srcPkgInterfaceMap[structName]; !ok {
srcPkgInterfaceMap[structName] = garray.NewStrArray()
srcPkgInterfaceFuncArray = srcPkgInterfaceMap[structName]
if !srcPkgInterfaceMap.Contains(structName) {
srcPkgInterfaceFuncArray = garray.NewStrArray()
srcPkgInterfaceMap.Set(structName, srcPkgInterfaceFuncArray)
} else {
srcPkgInterfaceFuncArray = srcPkgInterfaceMap.Get(structName).(*garray.StrArray)
}
srcPkgInterfaceFuncArray.Append(functionHead)
}
@@ -165,8 +167,8 @@ func (c CGenService) calculateInterfaceFunctions(
continue
}
structName = gstr.CaseCamel(structMatch[1])
if srcPkgInterfaceFuncArray, ok = srcPkgInterfaceMap[structName]; !ok {
srcPkgInterfaceMap[structName] = garray.NewStrArray()
if !srcPkgInterfaceMap.Contains(structName) {
srcPkgInterfaceMap.Set(structName, garray.NewStrArray())
}
}
return nil

View File

@@ -10,6 +10,7 @@ import (
"fmt"
"github.com/gogf/gf/v2/container/garray"
"github.com/gogf/gf/v2/container/gmap"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gregex"
@@ -23,7 +24,7 @@ import (
type generateServiceFilesInput struct {
CGenServiceInput
DstFilePath string // Absolute file path for generated service go file.
SrcStructFunctions map[string]*garray.StrArray
SrcStructFunctions *gmap.ListMap
SrcImportedPackages []string
SrcPackageName string
DstPackageName string
@@ -46,7 +47,8 @@ func (c CGenService) generateServiceFile(in generateServiceFilesInput) (ok bool,
// Type definitions.
generatedContent += "type("
generatedContent += "\n"
for structName, funcArray := range in.SrcStructFunctions {
in.SrcStructFunctions.Iterator(func(key, value interface{}) bool {
structName, funcArray := key.(string), value.(*garray.StrArray)
allFuncArray.Append(funcArray.Slice()...)
// Add comments to a method.
for index, funcName := range funcArray.Slice() {
@@ -60,7 +62,8 @@ func (c CGenService) generateServiceFile(in generateServiceFilesInput) (ok bool,
"{FuncDefinition}": funcArray.Join("\n\t"),
}))
generatedContent += "\n"
}
return true
})
generatedContent += ")"
generatedContent += "\n"
@@ -70,17 +73,19 @@ func (c CGenService) generateServiceFile(in generateServiceFilesInput) (ok bool,
generatingInterfaceCheck string
)
// Variable definitions.
for structName := range in.SrcStructFunctions {
in.SrcStructFunctions.Iterator(func(key, value interface{}) bool {
structName := key.(string)
generatingInterfaceCheck = fmt.Sprintf(`[^\w\d]+%s.I%s[^\w\d]`, in.DstPackageName, structName)
if gregex.IsMatchString(generatingInterfaceCheck, generatedContent) {
continue
return true
}
variableContent += gstr.Trim(gstr.ReplaceByMap(consts.TemplateGenServiceContentVariable, g.MapStrStr{
"{StructName}": structName,
"{InterfaceName}": "I" + structName,
}))
variableContent += "\n"
}
return true
})
if variableContent != "" {
generatedContent += "var("
generatedContent += "\n"
@@ -89,17 +94,19 @@ func (c CGenService) generateServiceFile(in generateServiceFilesInput) (ok bool,
generatedContent += "\n"
}
// Variable register function definitions.
for structName := range in.SrcStructFunctions {
in.SrcStructFunctions.Iterator(func(key, value interface{}) bool {
structName := key.(string)
generatingInterfaceCheck = fmt.Sprintf(`[^\w\d]+%s.I%s[^\w\d]`, in.DstPackageName, structName)
if gregex.IsMatchString(generatingInterfaceCheck, generatedContent) {
continue
return true
}
generatedContent += gstr.Trim(gstr.ReplaceByMap(consts.TemplateGenServiceContentRegister, g.MapStrStr{
"{StructName}": structName,
"{InterfaceName}": "I" + structName,
}))
generatedContent += "\n\n"
}
return true
})
// Replace empty braces that have new line.
generatedContent, _ = gregex.ReplaceString(`{[\s\t]+}`, `{}`, generatedContent)

View File

@@ -0,0 +1,5 @@
package main
func main() {
}

View File

@@ -0,0 +1,5 @@
package main
func main() {
}

View File

@@ -0,0 +1,12 @@
module github.com/gogf/gf/cmd/gf/cmd/gf/testdata/vardump/v2
go 1.18
require github.com/gogf/gf/v2 v2.6.1
require (
go.opentelemetry.io/otel v1.14.0 // indirect
go.opentelemetry.io/otel/trace v1.14.0 // indirect
)
replace github.com/gogf/gf/v2 => ../../../../../../../

View File

@@ -0,0 +1,27 @@
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM=
go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU=
go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY=
go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M=
go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@@ -0,0 +1,13 @@
package main
import (
"fmt"
"github.com/gogf/gf/v2/os/gbuild"
)
func main() {
for k, v := range gbuild.Data() {
fmt.Printf("%s: %v\n", k, v)
}
}

View File

@@ -8,6 +8,7 @@ package article
import (
"context"
"hotgo/internal/library/hggen/internal/cmd/testdata/genservice/service"
)

View File

@@ -0,0 +1,20 @@
gfcli:
gen:
dao:
- link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
tables: "user1"
descriptionTag: true
noModelComment: true
group: "sys"
clear: true
overwriteDao: true
- link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
tables: "user2"
descriptionTag: true
noModelComment: true
group: "book"
clear: true
overwriteDao: true

View File

@@ -0,0 +1,85 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// User3Dao is the data access object for table user3.
type User3Dao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of current DAO.
columns User3Columns // columns contains all the column names of Table for convenient usage.
}
// User3Columns defines and stores column names for table user3.
type User3Columns struct {
Id string // User ID
Passport string // User Passport
Password string // User Password
Nickname string // User Nickname
Score string // Total score amount.
CreateAt string // Created Time
UpdateAt string // Updated Time
}
// user3Columns holds the columns for table user3.
var user3Columns = User3Columns{
Id: "id",
Passport: "passport",
Password: "password",
Nickname: "nickname",
Score: "score",
CreateAt: "create_at",
UpdateAt: "update_at",
}
// NewUser3Dao creates and returns a new DAO object for table data access.
func NewUser3Dao() *User3Dao {
return &User3Dao{
group: "sys",
table: "user3",
columns: user3Columns,
}
}
// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *User3Dao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of current dao.
func (dao *User3Dao) Table() string {
return dao.table
}
// Columns returns all column names of current dao.
func (dao *User3Dao) Columns() User3Columns {
return dao.columns
}
// Group returns the configuration group name of database of current dao.
func (dao *User3Dao) Group() string {
return dao.group
}
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *User3Dao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
// It rollbacks the transaction and returns the error from function f if it returns non-nil error.
// It commits the transaction and returns nil if function f returns nil.
//
// Note that, you should not Commit or Rollback the transaction in function f
// as it is automatically handled by this function.
func (dao *User3Dao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}

View File

@@ -0,0 +1,85 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// User4Dao is the data access object for table user4.
type User4Dao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of current DAO.
columns User4Columns // columns contains all the column names of Table for convenient usage.
}
// User4Columns defines and stores column names for table user4.
type User4Columns struct {
Id string // User ID
Passport string // User Passport
Password string // User Password
Nickname string // User Nickname
Score string // Total score amount.
CreateAt string // Created Time
UpdateAt string // Updated Time
}
// user4Columns holds the columns for table user4.
var user4Columns = User4Columns{
Id: "id",
Passport: "passport",
Password: "password",
Nickname: "nickname",
Score: "score",
CreateAt: "create_at",
UpdateAt: "update_at",
}
// NewUser4Dao creates and returns a new DAO object for table data access.
func NewUser4Dao() *User4Dao {
return &User4Dao{
group: "book",
table: "user4",
columns: user4Columns,
}
}
// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *User4Dao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of current dao.
func (dao *User4Dao) Table() string {
return dao.table
}
// Columns returns all column names of current dao.
func (dao *User4Dao) Columns() User4Columns {
return dao.columns
}
// Group returns the configuration group name of database of current dao.
func (dao *User4Dao) Group() string {
return dao.group
}
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *User4Dao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
// It rollbacks the transaction and returns the error from function f if it returns non-nil error.
// It commits the transaction and returns nil if function f returns nil.
//
// Note that, you should not Commit or Rollback the transaction in function f
// as it is automatically handled by this function.
func (dao *User4Dao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}

View File

@@ -0,0 +1,27 @@
// =================================================================================
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
// =================================================================================
package dao
import (
"/internal"
)
// internalUser3Dao is internal type for wrapping internal DAO implements.
type internalUser3Dao = *internal.User3Dao
// user3Dao is the data access object for table user3.
// You can define custom methods on it to extend its functionality as you wish.
type user3Dao struct {
internalUser3Dao
}
var (
// User3 is globally public accessible object for table user3 operations.
User3 = user3Dao{
internal.NewUser3Dao(),
}
)
// Fill with you ideas below.

View File

@@ -0,0 +1,27 @@
// =================================================================================
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
// =================================================================================
package dao
import (
"/internal"
)
// internalUser4Dao is internal type for wrapping internal DAO implements.
type internalUser4Dao = *internal.User4Dao
// user4Dao is the data access object for table user4.
// You can define custom methods on it to extend its functionality as you wish.
type user4Dao struct {
internalUser4Dao
}
var (
// User4 is globally public accessible object for table user4 operations.
User4 = user4Dao{
internal.NewUser4Dao(),
}
)
// Fill with you ideas below.

View File

@@ -0,0 +1,22 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package do
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// User1 is the golang structure of table user1 for DAO operations like Where/Data.
type User1 struct {
g.Meta `orm:"table:user1, do:true"`
Id interface{} // User ID
Passport interface{} // User Passport
Password interface{} // User Password
Nickname interface{} // User Nickname
Score interface{} // Total score amount.
CreateAt *gtime.Time // Created Time
UpdateAt *gtime.Time // Updated Time
}

View File

@@ -0,0 +1,22 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package do
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// User2 is the golang structure of table user2 for DAO operations like Where/Data.
type User2 struct {
g.Meta `orm:"table:user2, do:true"`
Id interface{} // User ID
Passport interface{} // User Passport
Password interface{} // User Password
Nickname interface{} // User Nickname
Score interface{} // Total score amount.
CreateAt *gtime.Time // Created Time
UpdateAt *gtime.Time // Updated Time
}

View File

@@ -0,0 +1,20 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// User1 is the golang structure for table user1.
type User1 struct {
Id uint `json:"ID" description:"User ID"`
Passport string `json:"PASSPORT" description:"User Passport"`
Password string `json:"PASSWORD" description:"User Password"`
Nickname string `json:"NICKNAME" description:"User Nickname"`
Score float64 `json:"SCORE" description:"Total score amount."`
CreateAt *gtime.Time `json:"CREATE_AT" description:"Created Time"`
UpdateAt *gtime.Time `json:"UPDATE_AT" description:"Updated Time"`
}

View File

@@ -0,0 +1,20 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// User2 is the golang structure for table user2.
type User2 struct {
Id uint `json:"ID" description:"User ID"`
Passport string `json:"PASSPORT" description:"User Passport"`
Password string `json:"PASSWORD" description:"User Password"`
Nickname string `json:"NICKNAME" description:"User Nickname"`
Score float64 `json:"SCORE" description:"Total score amount."`
CreateAt *gtime.Time `json:"CREATE_AT" description:"Created Time"`
UpdateAt *gtime.Time `json:"UPDATE_AT" description:"Updated Time"`
}

View File

@@ -0,0 +1,10 @@
CREATE TABLE `user1` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'User ID',
`passport` varchar(45) NOT NULL COMMENT 'User Passport',
`password` varchar(45) NOT NULL COMMENT 'User Password',
`nickname` varchar(45) NOT NULL COMMENT 'User Nickname',
`score` decimal(10,2) unsigned DEFAULT NULL COMMENT 'Total score amount.',
`create_at` datetime DEFAULT NULL COMMENT 'Created Time',
`update_at` datetime DEFAULT NULL COMMENT 'Updated Time',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

View File

@@ -0,0 +1,10 @@
CREATE TABLE `user2` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'User ID',
`passport` varchar(45) NOT NULL COMMENT 'User Passport',
`password` varchar(45) NOT NULL COMMENT 'User Password',
`nickname` varchar(45) NOT NULL COMMENT 'User Nickname',
`score` decimal(10,2) unsigned DEFAULT NULL COMMENT 'Total score amount.',
`create_at` datetime DEFAULT NULL COMMENT 'Created Time',
`update_at` datetime DEFAULT NULL COMMENT 'Updated Time',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

View File

@@ -0,0 +1,20 @@
gfcli:
gen:
dao:
- link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
tables: "user1"
descriptionTag: true
noModelComment: true
group: "sys"
clear: true
overwriteDao: false
- link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
tables: "user2"
descriptionTag: true
noModelComment: true
group: "book"
clear: true
overwriteDao: true

View File

@@ -0,0 +1,85 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// User1Dao is the data access object for table user1.
type User1Dao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of current DAO.
columns User1Columns // columns contains all the column names of Table for convenient usage.
}
// User1Columns defines and stores column names for table user1.
type User1Columns struct {
Id string // User ID
Passport string // User Passport
Password string // User Password
Nickname string // User Nickname
Score string // Total score amount.
CreateAt string // Created Time
UpdateAt string // Updated Time
}
// user1Columns holds the columns for table user1.
var user1Columns = User1Columns{
Id: "id",
Passport: "passport",
Password: "password",
Nickname: "nickname",
Score: "score",
CreateAt: "create_at",
UpdateAt: "update_at",
}
// NewUser1Dao creates and returns a new DAO object for table data access.
func NewUser1Dao() *User1Dao {
return &User1Dao{
group: "sys",
table: "user1",
columns: user1Columns,
}
}
// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *User1Dao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of current dao.
func (dao *User1Dao) Table() string {
return dao.table
}
// Columns returns all column names of current dao.
func (dao *User1Dao) Columns() User1Columns {
return dao.columns
}
// Group returns the configuration group name of database of current dao.
func (dao *User1Dao) Group() string {
return dao.group
}
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *User1Dao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
// It rollbacks the transaction and returns the error from function f if it returns non-nil error.
// It commits the transaction and returns nil if function f returns nil.
//
// Note that, you should not Commit or Rollback the transaction in function f
// as it is automatically handled by this function.
func (dao *User1Dao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}

View File

@@ -0,0 +1,85 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// User2Dao is the data access object for table user2.
type User2Dao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of current DAO.
columns User2Columns // columns contains all the column names of Table for convenient usage.
}
// User2Columns defines and stores column names for table user2.
type User2Columns struct {
Id string // User ID
Passport string // User Passport
Password string // User Password
Nickname string // User Nickname
Score string // Total score amount.
CreateAt string // Created Time
UpdateAt string // Updated Time
}
// user2Columns holds the columns for table user2.
var user2Columns = User2Columns{
Id: "id",
Passport: "passport",
Password: "password",
Nickname: "nickname",
Score: "score",
CreateAt: "create_at",
UpdateAt: "update_at",
}
// NewUser2Dao creates and returns a new DAO object for table data access.
func NewUser2Dao() *User2Dao {
return &User2Dao{
group: "sys",
table: "user2",
columns: user2Columns,
}
}
// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *User2Dao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of current dao.
func (dao *User2Dao) Table() string {
return dao.table
}
// Columns returns all column names of current dao.
func (dao *User2Dao) Columns() User2Columns {
return dao.columns
}
// Group returns the configuration group name of database of current dao.
func (dao *User2Dao) Group() string {
return dao.group
}
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *User2Dao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
// It rollbacks the transaction and returns the error from function f if it returns non-nil error.
// It commits the transaction and returns nil if function f returns nil.
//
// Note that, you should not Commit or Rollback the transaction in function f
// as it is automatically handled by this function.
func (dao *User2Dao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}

View File

@@ -0,0 +1,85 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// User3Dao is the data access object for table user3.
type User3Dao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of current DAO.
columns User3Columns // columns contains all the column names of Table for convenient usage.
}
// User3Columns defines and stores column names for table user3.
type User3Columns struct {
Id string // User ID
Passport string // User Passport
Password string // User Password
Nickname string // User Nickname
Score string // Total score amount.
CreateAt string // Created Time
UpdateAt string // Updated Time
}
// user3Columns holds the columns for table user3.
var user3Columns = User3Columns{
Id: "id",
Passport: "passport",
Password: "password",
Nickname: "nickname",
Score: "score",
CreateAt: "create_at",
UpdateAt: "update_at",
}
// NewUser3Dao creates and returns a new DAO object for table data access.
func NewUser3Dao() *User3Dao {
return &User3Dao{
group: "sys",
table: "user3",
columns: user3Columns,
}
}
// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *User3Dao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of current dao.
func (dao *User3Dao) Table() string {
return dao.table
}
// Columns returns all column names of current dao.
func (dao *User3Dao) Columns() User3Columns {
return dao.columns
}
// Group returns the configuration group name of database of current dao.
func (dao *User3Dao) Group() string {
return dao.group
}
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *User3Dao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
// It rollbacks the transaction and returns the error from function f if it returns non-nil error.
// It commits the transaction and returns nil if function f returns nil.
//
// Note that, you should not Commit or Rollback the transaction in function f
// as it is automatically handled by this function.
func (dao *User3Dao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}

View File

@@ -0,0 +1,85 @@
// ==========================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// ==========================================================================
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// User4Dao is the data access object for table user4.
type User4Dao struct {
table string // table is the underlying table name of the DAO.
group string // group is the database configuration group name of current DAO.
columns User4Columns // columns contains all the column names of Table for convenient usage.
}
// User4Columns defines and stores column names for table user4.
type User4Columns struct {
Id string // User ID
Passport string // User Passport
Password string // User Password
Nickname string // User Nickname
Score string // Total score amount.
CreateAt string // Created Time
UpdateAt string // Updated Time
}
// user4Columns holds the columns for table user4.
var user4Columns = User4Columns{
Id: "id",
Passport: "passport",
Password: "password",
Nickname: "nickname",
Score: "score",
CreateAt: "create_at",
UpdateAt: "update_at",
}
// NewUser4Dao creates and returns a new DAO object for table data access.
func NewUser4Dao() *User4Dao {
return &User4Dao{
group: "book",
table: "user4",
columns: user4Columns,
}
}
// DB retrieves and returns the underlying raw database management object of current DAO.
func (dao *User4Dao) DB() gdb.DB {
return g.DB(dao.group)
}
// Table returns the table name of current dao.
func (dao *User4Dao) Table() string {
return dao.table
}
// Columns returns all column names of current dao.
func (dao *User4Dao) Columns() User4Columns {
return dao.columns
}
// Group returns the configuration group name of database of current dao.
func (dao *User4Dao) Group() string {
return dao.group
}
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
func (dao *User4Dao) Ctx(ctx context.Context) *gdb.Model {
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
}
// Transaction wraps the transaction logic using function f.
// It rollbacks the transaction and returns the error from function f if it returns non-nil error.
// It commits the transaction and returns nil if function f returns nil.
//
// Note that, you should not Commit or Rollback the transaction in function f
// as it is automatically handled by this function.
func (dao *User4Dao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
return dao.Ctx(ctx).Transaction(ctx, f)
}

View File

@@ -0,0 +1,29 @@
// =================================================================================
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
// =================================================================================
// I am not overwritten.
package dao
import (
"/internal"
)
// internalUser1Dao is internal type for wrapping internal DAO implements.
type internalUser1Dao = *internal.User1Dao
// user1Dao is the data access object for table user1.
// You can define custom methods on it to extend its functionality as you wish.
type user1Dao struct {
internalUser1Dao
}
var (
// User1 is globally public accessible object for table user1 operations.
User1 = user1Dao{
internal.NewUser1Dao(),
}
)
// Fill with you ideas below.

View File

@@ -0,0 +1,29 @@
// =================================================================================
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
// =================================================================================
// I am not overwritten.
package dao
import (
"/internal"
)
// internalUser2Dao is internal type for wrapping internal DAO implements.
type internalUser2Dao = *internal.User2Dao
// user2Dao is the data access object for table user2.
// You can define custom methods on it to extend its functionality as you wish.
type user2Dao struct {
internalUser2Dao
}
var (
// User2 is globally public accessible object for table user2 operations.
User2 = user2Dao{
internal.NewUser2Dao(),
}
)
// Fill with you ideas below.

View File

@@ -0,0 +1,27 @@
// =================================================================================
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
// =================================================================================
package dao
import (
"/internal"
)
// internalUser3Dao is internal type for wrapping internal DAO implements.
type internalUser3Dao = *internal.User3Dao
// user3Dao is the data access object for table user3.
// You can define custom methods on it to extend its functionality as you wish.
type user3Dao struct {
internalUser3Dao
}
var (
// User3 is globally public accessible object for table user3 operations.
User3 = user3Dao{
internal.NewUser3Dao(),
}
)
// Fill with you ideas below.

View File

@@ -0,0 +1,27 @@
// =================================================================================
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
// =================================================================================
package dao
import (
"/internal"
)
// internalUser4Dao is internal type for wrapping internal DAO implements.
type internalUser4Dao = *internal.User4Dao
// user4Dao is the data access object for table user4.
// You can define custom methods on it to extend its functionality as you wish.
type user4Dao struct {
internalUser4Dao
}
var (
// User4 is globally public accessible object for table user4 operations.
User4 = user4Dao{
internal.NewUser4Dao(),
}
)
// Fill with you ideas below.

View File

@@ -0,0 +1,22 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package do
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// User1 is the golang structure of table user1 for DAO operations like Where/Data.
type User1 struct {
g.Meta `orm:"table:user1, do:true"`
Id interface{} // User ID
Passport interface{} // User Passport
Password interface{} // User Password
Nickname interface{} // User Nickname
Score interface{} // Total score amount.
CreateAt *gtime.Time // Created Time
UpdateAt *gtime.Time // Updated Time
}

View File

@@ -0,0 +1,22 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package do
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
// User2 is the golang structure of table user2 for DAO operations like Where/Data.
type User2 struct {
g.Meta `orm:"table:user2, do:true"`
Id interface{} // User ID
Passport interface{} // User Passport
Password interface{} // User Password
Nickname interface{} // User Nickname
Score interface{} // Total score amount.
CreateAt *gtime.Time // Created Time
UpdateAt *gtime.Time // Updated Time
}

View File

@@ -0,0 +1,20 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// User1 is the golang structure for table user1.
type User1 struct {
Id uint `json:"ID" description:"User ID"`
Passport string `json:"PASSPORT" description:"User Passport"`
Password string `json:"PASSWORD" description:"User Password"`
Nickname string `json:"NICKNAME" description:"User Nickname"`
Score float64 `json:"SCORE" description:"Total score amount."`
CreateAt *gtime.Time `json:"CREATE_AT" description:"Created Time"`
UpdateAt *gtime.Time `json:"UPDATE_AT" description:"Updated Time"`
}

View File

@@ -0,0 +1,20 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/os/gtime"
)
// User2 is the golang structure for table user2.
type User2 struct {
Id uint `json:"ID" description:"User ID"`
Passport string `json:"PASSPORT" description:"User Passport"`
Password string `json:"PASSWORD" description:"User Password"`
Nickname string `json:"NICKNAME" description:"User Nickname"`
Score float64 `json:"SCORE" description:"Total score amount."`
CreateAt *gtime.Time `json:"CREATE_AT" description:"Created Time"`
UpdateAt *gtime.Time `json:"UPDATE_AT" description:"Updated Time"`
}

View File

@@ -0,0 +1,10 @@
CREATE TABLE `user1` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'User ID',
`passport` varchar(45) NOT NULL COMMENT 'User Passport',
`password` varchar(45) NOT NULL COMMENT 'User Password',
`nickname` varchar(45) NOT NULL COMMENT 'User Nickname',
`score` decimal(10,2) unsigned DEFAULT NULL COMMENT 'Total score amount.',
`create_at` datetime DEFAULT NULL COMMENT 'Created Time',
`update_at` datetime DEFAULT NULL COMMENT 'Updated Time',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

View File

@@ -0,0 +1,10 @@
CREATE TABLE `user2` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'User ID',
`passport` varchar(45) NOT NULL COMMENT 'User Passport',
`password` varchar(45) NOT NULL COMMENT 'User Password',
`nickname` varchar(45) NOT NULL COMMENT 'User Nickname',
`score` decimal(10,2) unsigned DEFAULT NULL COMMENT 'Total score amount.',
`create_at` datetime DEFAULT NULL COMMENT 'Created Time',
`update_at` datetime DEFAULT NULL COMMENT 'Updated Time',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

View File

@@ -0,0 +1,18 @@
// =================================================================================
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
// =================================================================================
package entity
import (
"github.com/gogf/gf/v2/encoding/gjson"
)
// Issue2746 is the golang structure for table issue2746.
type Issue2746 struct {
Id uint `json:"ID" ` // User ID
Nickname string `json:"NICKNAME" ` // User Nickname
Tag *gjson.Json `json:"TAG" ` //
Info string `json:"INFO" ` //
Tag2 *gjson.Json `json:"TAG_2" ` // Tag2
}

View File

@@ -0,0 +1,9 @@
CREATE TABLE %s (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'User ID',
`nickname` varchar(45) NOT NULL COMMENT 'User Nickname',
`tag` json NOT NULL,
`info` longtext DEFAULT NULL,
`tag2` json COMMENT 'Tag2',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

View File

@@ -9,6 +9,7 @@ package utils
import (
"context"
"fmt"
"golang.org/x/tools/imports"
"github.com/gogf/gf/v2/os/gfile"

View File

@@ -7,6 +7,7 @@ package views
import (
"context"
"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/os/gfile"
@@ -235,6 +236,18 @@ func (l *gCurd) DoBuild(ctx context.Context, in *CurdBuildInput) (err error) {
return
}
db, err := g.DB().Open(ParseDBConfigNodeLink(&gdb.ConfigNode{Link: in.PreviewIn.DaoConfig.Link}))
if err != nil {
err = gerror.Newf("连接数据库失败,请检查配置文件[server/hack/config.yaml]数据库配置是否正确err:%v", err.Error())
return err
}
defer db.Close()
if err = db.Ping(); err != nil {
err = gerror.Newf("数据库访问异常,请检查配置文件[server/hack/config.yaml]数据库配置是否正确err:%v", err.Error())
return
}
// 前置操作
if len(in.BeforeEvent) > 0 {
for name, f := range in.BeforeEvent {

View File

@@ -8,9 +8,11 @@ package views
import (
"bytes"
"context"
"errors"
"fmt"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"hotgo/internal/library/dict"
"hotgo/internal/model/input/sysin"
"hotgo/utility/convert"
)
@@ -64,36 +66,66 @@ func (l *gCurd) generateWebModelDictOptions(ctx context.Context, in *CurdPreview
}
var (
options = make(g.Map)
dictTypeIds []int64
dictTypeList []*DictType
options = make(g.Map)
dictTypeIds []int64
dictTypeList []*DictType
builtinDictTypeIds []int64
builtinDictTypeList []*DictType
)
for _, field := range in.masterFields {
if field.DictType > 0 {
dictTypeIds = append(dictTypeIds, field.DictType)
}
if field.DictType < 0 {
builtinDictTypeIds = append(builtinDictTypeIds, field.DictType)
}
}
dictTypeIds = convert.UniqueSlice(dictTypeIds)
if len(dictTypeIds) == 0 {
builtinDictTypeIds = convert.UniqueSlice(builtinDictTypeIds)
if len(dictTypeIds) == 0 && len(builtinDictTypeIds) == 0 {
options["has"] = false
return options, nil
}
err := g.Model("sys_dict_type").Ctx(ctx).
Fields("id", "type").
WhereIn("id", dictTypeIds).
Scan(&dictTypeList)
if err != nil {
return nil, err
if len(dictTypeIds) > 0 {
err := g.Model("sys_dict_type").Ctx(ctx).
Fields("id", "type").
WhereIn("id", dictTypeIds).
Scan(&dictTypeList)
if err != nil {
return nil, err
}
}
if len(dictTypeList) == 0 {
if len(builtinDictTypeIds) > 0 {
for _, id := range builtinDictTypeIds {
opts, err := dict.GetOptionsById(ctx, id)
if err != nil && !errors.Is(err, dict.NotExistKeyError) {
return nil, err
}
if len(opts) > 0 {
row := new(DictType)
row.Id = id
row.Type = opts[0].Type
builtinDictTypeList = append(builtinDictTypeList, row)
}
}
}
if len(dictTypeList) == 0 && len(builtinDictTypeList) == 0 {
options["has"] = false
return options, nil
}
if len(builtinDictTypeList) > 0 {
dictTypeList = append(dictTypeList, builtinDictTypeList...)
}
options["has"] = true
var (
@@ -109,7 +141,7 @@ func (l *gCurd) generateWebModelDictOptions(ctx context.Context, in *CurdPreview
for _, v := range dictTypeList {
// 字段映射字典
for _, field := range in.masterFields {
if field.DictType > 0 && v.Id == field.DictType {
if field.DictType != 0 && v.Id == field.DictType {
in.options.dictMap[field.TsName] = v.Type
switchLoadOptions = fmt.Sprintf("%s case '%s':\n item.componentProps.options = options.value.%s;\n break;\n", switchLoadOptions, field.TsName, v.Type)
}
@@ -129,7 +161,6 @@ func (l *gCurd) generateWebModelDictOptions(ctx context.Context, in *CurdPreview
options["interface"] = interfaceOptionsBuffer.String()
options["const"] = constOptionsBuffer.String()
options["load"] = loadOptionsBuffer.String()
return options, nil
}

View File

@@ -7,11 +7,13 @@ package views
import (
"context"
"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/os/gfile"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"hotgo/internal/consts"
"hotgo/internal/model"
"hotgo/internal/model/input/sysin"
@@ -175,3 +177,46 @@ func IsIndexPK(index string) bool {
func IsIndexUNI(index string) bool {
return gstr.ToUpper(index) == gstr.ToUpper(consts.GenCodesIndexUNI)
}
// ParseDBConfigNodeLink 解析数据库连接配置
func ParseDBConfigNodeLink(node *gdb.ConfigNode) *gdb.ConfigNode {
const linkPattern = `(\w+):([\w\-\$]*):(.*?)@(\w+?)\((.+?)\)/{0,1}([^\?]*)\?{0,1}(.*)`
const defaultCharset = `utf8`
const defaultProtocol = `tcp`
var match []string
if node.Link != "" {
match, _ = gregex.MatchString(linkPattern, node.Link)
if len(match) > 5 {
node.Type = match[1]
node.User = match[2]
node.Pass = match[3]
node.Protocol = match[4]
array := gstr.Split(match[5], ":")
if len(array) == 2 && node.Protocol != "file" {
node.Host = array[0]
node.Port = array[1]
node.Name = match[6]
} else {
node.Name = match[5]
}
if len(match) > 6 && match[7] != "" {
node.Extra = match[7]
}
node.Link = ""
}
}
if node.Extra != "" {
if m, _ := gstr.Parse(node.Extra); len(m) > 0 {
_ = gconv.Struct(m, &node)
}
}
// Default value checks.
if node.Charset == "" {
node.Charset = defaultCharset
}
if node.Protocol == "" {
node.Protocol = defaultProtocol
}
return node
}

View File

@@ -25,7 +25,7 @@ import (
const (
whoisApi = "https://whois.pconline.com.cn/ipJson.jsp?json=true&ip="
dyndns = "http://members.3322.org/dyndns/getip"
dyndns = "http://members.3322.org/dyndns/getip" // 备用:"https://ifconfig.co/ip"
)
type IpLocationData struct {

View File

@@ -1,3 +1,8 @@
// Package tcp_test
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package tcp_test
import (

View File

@@ -5,13 +5,65 @@
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package storager
import (
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gtime"
"hotgo/internal/model/entity"
)
// FileMeta 文件元数据
type FileMeta struct {
Filename string // 文件名称
Size int64 // 文件大小
Kind string // 文件上传类型
MimeType string // 文件扩展类型
NaiveType string // NaiveUI类型
Ext string // 文件扩展名
Md5 string // 文件hash
Filename string `json:"filename"` // 文件名称
Size int64 `json:"size"` // 文件大小
Kind string `json:"kind"` // 文件上传类型
MimeType string `json:"mimeType"` // 文件扩展类型
NaiveType string `json:"naiveType"` // NaiveUI类型
Ext string `json:"ext"` // 文件扩展名
Md5 string `json:"md5"` // 文件hash
}
// MultipartProgress 分片进度
type MultipartProgress struct {
UploadId string `json:"uploadId"` // 上传事件ID
ThirdUploadId string `json:"thirdUploadId"` // 第三方上传事件ID
Meta *FileMeta `json:"meta"` // 文件元数据
ShardCount int `json:"shardCount"` // 分片数量
UploadedIndex []int `json:"uploadedIndex"` // 已上传的分片索引
CreatedAt *gtime.Time `json:"createdAt"` // 创建时间
}
// CheckMultipartParams 检查文件分片
type CheckMultipartParams struct {
UploadType string `json:"uploadType" dc:"文件类型"`
FileName string `json:"fileName" dc:"文件名称"`
Size int64 `json:"size" dc:"文件大小"`
Md5 string `json:"md5" dc:"文件md5值"`
ShardCount int `json:"shardCount" dc:"分片数量"`
meta *FileMeta
}
type CheckMultipartModel struct {
UploadId string `json:"uploadId" dc:"上传事件ID"`
Attachment *entity.SysAttachment `json:"attachment" dc:"附件"`
WaitUploadIndex []int `json:"waitUploadIndex" dc:"等待上传的分片索引"`
Progress float64 `json:"progress" dc:"上传进度"`
SizeFormat string `json:"sizeFormat" dc:"文件大小"`
}
// UploadPartParams 分片上传
type UploadPartParams struct {
UploadId string `json:"uploadId" dc:"上传事件ID"`
UploadType string `json:"uploadType" dc:"文件类型"`
FileName string `json:"fileName" dc:"文件名称"`
Size int64 `json:"size" dc:"文件大小"`
Md5 string `json:"md5" dc:"文件md5值"`
Index int `json:"index" dc:"分片索引"`
File *ghttp.UploadFile `json:"file" type:"file" dc:"分片文件"`
mp *MultipartProgress
}
type UploadPartModel struct {
Attachment *entity.SysAttachment `json:"attachment" dc:"附件"`
Progress float64 `json:"progress" dc:"上传进度"`
Finish bool `json:"finish" dc:"是否完成"`
}

View File

@@ -14,18 +14,26 @@ import (
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/grand"
"hotgo/internal/consts"
"hotgo/internal/library/cache"
"hotgo/internal/library/contexts"
"hotgo/internal/model/entity"
"hotgo/utility/convert"
"hotgo/utility/format"
"hotgo/utility/url"
"hotgo/utility/validate"
"strconv"
"strings"
"time"
)
// UploadDrive 存储驱动
type UploadDrive interface {
// Upload 上传
Upload(ctx context.Context, file *ghttp.UploadFile) (fullPath string, err error)
// CreateMultipart 创建分片事件
CreateMultipart(ctx context.Context, in *CheckMultipartParams) (res *MultipartProgress, err error)
// UploadPart 上传分片
UploadPart(ctx context.Context, in *UploadPartParams) (res *UploadPartModel, err error)
}
// New 初始化存储驱动
@@ -70,6 +78,31 @@ func DoUpload(ctx context.Context, typ string, file *ghttp.UploadFile) (result *
return
}
if err = ValidateFileMeta(typ, meta); err != nil {
return
}
result, err = HasFile(ctx, meta.Md5)
if err != nil {
return
}
// 相同存储相同身份才复用
if result != nil && result.Drive == config.Drive && result.MemberId == contexts.GetUserId(ctx) && result.AppId == contexts.GetModule(ctx) {
return
}
// 上传到驱动
fullPath, err := New(config.Drive).Upload(ctx, file)
if err != nil {
return
}
// 写入附件记录
return write(ctx, meta, fullPath)
}
// ValidateFileMeta 验证文件元数据
func ValidateFileMeta(typ string, meta *FileMeta) (err error) {
if _, err = GetFileMimeType(meta.Ext); err != nil {
return
}
@@ -123,24 +156,7 @@ func DoUpload(ctx context.Context, typ string, file *ghttp.UploadFile) (result *
return
}
}
result, err = hasFile(ctx, meta.Md5)
if err != nil {
return
}
// 相同存储相同身份才复用
if result != nil && result.Drive == config.Drive && result.MemberId == contexts.GetUserId(ctx) && result.AppId == contexts.GetModule(ctx) {
return
}
// 上传到驱动
fullPath, err := New(config.Drive).Upload(ctx, file)
if err != nil {
return
}
// 写入附件记录
return write(ctx, meta, fullPath)
return
}
// LastUrl 根据驱动获取最终文件访问地址
@@ -225,8 +241,8 @@ func write(ctx context.Context, meta *FileMeta, fullPath string) (models *entity
return
}
// hasFile 检查附件是否存在
func hasFile(ctx context.Context, md5 string) (res *entity.SysAttachment, err error) {
// HasFile 检查附件是否存在
func HasFile(ctx context.Context, md5 string) (res *entity.SysAttachment, err error) {
if err = GetModel(ctx).Where("md5", md5).Scan(&res); err != nil {
err = gerror.Wrap(err, "检查文件hash时出现错误")
return
@@ -238,10 +254,147 @@ func hasFile(ctx context.Context, md5 string) (res *entity.SysAttachment, err er
// 只有在上传时才会检查md5值如果附件存在则更新最后上传时间保证上传列表更新显示在最前面
if res.Id > 0 {
_, _ = GetModel(ctx).WherePri(res.Id).Data(g.Map{
update := g.Map{
"status": consts.StatusEnabled,
"updated_at": gtime.Now(),
}).Update()
}
_, _ = GetModel(ctx).WherePri(res.Id).Data(update).Update()
}
return
}
// CheckMultipart 检查文件分片
func CheckMultipart(ctx context.Context, in *CheckMultipartParams) (res *CheckMultipartModel, err error) {
res = new(CheckMultipartModel)
meta := new(FileMeta)
meta.Filename = in.FileName
meta.Size = in.Size
meta.Ext = Ext(in.FileName)
meta.Kind = GetFileKind(meta.Ext)
meta.MimeType, err = GetFileMimeType(meta.Ext)
if err != nil {
return
}
// 兼容naiveUI
naiveType := "text/plain"
if IsImgType(Ext(in.FileName)) {
naiveType = ""
}
meta.NaiveType = naiveType
meta.Md5 = in.Md5
if err = ValidateFileMeta(in.UploadType, meta); err != nil {
return
}
result, err := HasFile(ctx, in.Md5)
if err != nil {
return nil, err
}
// 文件已存在,直接返回。相同存储相同身份才复用
if result != nil && result.Drive == config.Drive && result.MemberId == contexts.GetUserId(ctx) && result.AppId == contexts.GetModule(ctx) {
res.Attachment = result
return
}
for i := 0; i < in.ShardCount; i++ {
res.WaitUploadIndex = append(res.WaitUploadIndex, i+1)
}
in.meta = meta
progress, err := GetOrCreateMultipartProgress(ctx, in)
if err != nil {
return nil, err
}
if len(progress.UploadedIndex) > 0 {
res.WaitUploadIndex = convert.DifferenceSlice(progress.UploadedIndex, res.WaitUploadIndex)
}
if len(res.WaitUploadIndex) == 0 {
res.WaitUploadIndex = make([]int, 0)
}
res.UploadId = progress.UploadId
res.Progress = CalcUploadProgress(progress.UploadedIndex, progress.ShardCount)
res.SizeFormat = format.FileSize(progress.Meta.Size)
return
}
// CalcUploadProgress 计算上传进度
func CalcUploadProgress(uploadedIndex []int, shardCount int) float64 {
return format.Round2Float64(float64(len(uploadedIndex)) / float64(shardCount) * 100)
}
// GenUploadId 生成上传ID
func GenUploadId(ctx context.Context, md5 string) string {
return fmt.Sprintf("%v:%v:%v@%v", md5, contexts.GetUserId(ctx), contexts.GetModule(ctx), config.Drive)
}
// GetOrCreateMultipartProgress 获取或创建分片上传事件进度
func GetOrCreateMultipartProgress(ctx context.Context, in *CheckMultipartParams) (res *MultipartProgress, err error) {
uploadId := GenUploadId(ctx, in.Md5)
res, err = GetMultipartProgress(ctx, uploadId)
if err != nil {
return nil, err
}
if res != nil {
return res, nil
}
return New(config.Drive).CreateMultipart(ctx, in)
}
// GetMultipartProgress 获取分片上传事件进度
func GetMultipartProgress(ctx context.Context, uploadId string) (res *MultipartProgress, err error) {
key := fmt.Sprintf("%v:%v", consts.CacheMultipartUpload, uploadId)
get, err := cache.Instance().Get(ctx, key)
if err != nil {
return nil, err
}
err = get.Scan(&res)
return
}
// CreateMultipartProgress 创建分片上传事件进度
func CreateMultipartProgress(ctx context.Context, in *MultipartProgress) (err error) {
key := fmt.Sprintf("%v:%v", consts.CacheMultipartUpload, in.UploadId)
return cache.Instance().Set(ctx, key, in, time.Hour*24*7)
}
// UpdateMultipartProgress 更新分片上传事件进度
func UpdateMultipartProgress(ctx context.Context, in *MultipartProgress) (err error) {
key := fmt.Sprintf("%v:%v", consts.CacheMultipartUpload, in.UploadId)
return cache.Instance().Set(ctx, key, in, time.Hour*24*7)
}
// DelMultipartProgress 删除分片上传事件进度
func DelMultipartProgress(ctx context.Context, in *MultipartProgress) (err error) {
key := fmt.Sprintf("%v:%v", consts.CacheMultipartUpload, in.UploadId)
_, err = cache.Instance().Remove(ctx, key)
return
}
// UploadPart 上传分片
func UploadPart(ctx context.Context, in *UploadPartParams) (res *UploadPartModel, err error) {
in.mp, err = GetMultipartProgress(ctx, in.UploadId)
if err != nil {
return nil, err
}
if in.mp == nil {
err = gerror.New("分片事件不存在,请重新上传!")
return
}
if validate.InSlice(in.mp.UploadedIndex, in.Index) {
err = gerror.New("该分片已上传过了")
return
}
res, err = New(config.Drive).UploadPart(ctx, in)
if err != nil {
return nil, err
}
return
}

View File

@@ -45,3 +45,15 @@ func (d *CosDrive) Upload(ctx context.Context, file *ghttp.UploadFile) (fullPath
_, err = client.Object.Put(ctx, fullPath, f2, nil)
return
}
// CreateMultipart 创建分片事件
func (d *CosDrive) CreateMultipart(ctx context.Context, in *CheckMultipartParams) (res *MultipartProgress, err error) {
err = gerror.New("当前驱动暂不支持分片上传!")
return
}
// UploadPart 上传分片
func (d *CosDrive) UploadPart(ctx context.Context, in *UploadPartParams) (res *UploadPartModel, err error) {
err = gerror.New("当前驱动暂不支持分片上传!")
return
}

View File

@@ -10,7 +10,11 @@ import (
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
"os"
"path/filepath"
"strings"
)
@@ -45,3 +49,102 @@ func (d *LocalDrive) Upload(ctx context.Context, file *ghttp.UploadFile) (fullPa
fullPath = config.LocalPath + nowDate + "/" + fileName
return
}
// CreateMultipart 创建分片事件
func (d *LocalDrive) CreateMultipart(ctx context.Context, in *CheckMultipartParams) (mp *MultipartProgress, err error) {
mp = new(MultipartProgress)
mp.UploadId = GenUploadId(ctx, in.Md5)
mp.Meta = in.meta
mp.ShardCount = in.ShardCount
mp.UploadedIndex = make([]int, 0)
mp.CreatedAt = gtime.Now()
if err = CreateMultipartProgress(ctx, mp); err != nil {
return nil, err
}
return
}
// UploadPart 上传分片
func (d *LocalDrive) UploadPart(ctx context.Context, in *UploadPartParams) (res *UploadPartModel, err error) {
sp := g.Cfg().MustGet(ctx, "server.serverRoot")
if sp.IsEmpty() {
err = gerror.New("本地上传驱动必须配置静态路径!")
return
}
spStr := strings.Trim(sp.String(), "/") + "/"
if config.LocalPath == "" {
err = gerror.New("本地上传驱动必须配置本地存储路径!")
return
}
// 分片文件存放路径
partFilePath := spStr + config.LocalPath + "tmp/" + in.Md5
// 写入文件
in.File.Filename = gconv.String(in.Index)
if _, err = in.File.Save(partFilePath, false); err != nil {
return
}
// 更新上传进度
in.mp.UploadedIndex = append(in.mp.UploadedIndex, in.Index)
if err = UpdateMultipartProgress(ctx, in.mp); err != nil {
return nil, err
}
res = new(UploadPartModel)
// 已全部上传完毕
if len(in.mp.UploadedIndex) == in.mp.ShardCount {
// 删除进度统计
if err = DelMultipartProgress(ctx, in.mp); err != nil {
return nil, err
}
// 合并文件
finalDirPath := GenFullPath(config.LocalPath, gfile.Ext(in.mp.Meta.Filename))
if err = MergePartFile(partFilePath, spStr+finalDirPath); err != nil {
err = gerror.Newf("合并分片文件出错:%v", err.Error())
return nil, err
}
// 删除临时分片
if err = os.RemoveAll(partFilePath); err != nil {
err = gerror.Newf("删除临时分片文件出错:%v", err.Error())
return nil, err
}
// 写入附件记录
attachment, err := write(ctx, in.mp.Meta, finalDirPath)
if err != nil {
return nil, err
}
res.Finish = true
res.Progress = 100
res.Attachment = attachment
return res, nil
}
// 计算上传进度
res.Progress = CalcUploadProgress(in.mp.UploadedIndex, in.mp.ShardCount)
return
}
// MergePartFile 合并分片文件
func MergePartFile(srcPath, dstPath string) (err error) {
dir, err := os.ReadDir(srcPath)
if err != nil {
return err
}
for _, file := range dir {
filePath := filepath.Join(srcPath, file.Name())
if err = gfile.PutBytesAppend(dstPath, gfile.GetBytes(filePath)); err != nil {
return err
}
}
return
}

View File

@@ -62,3 +62,15 @@ func (d *MinioDrive) Upload(ctx context.Context, file *ghttp.UploadFile) (fullPa
_, err = client.PutObject(ctx, config.MinioBucket, fullPath, reader, file.Size, opts)
return
}
// CreateMultipart 创建分片事件
func (d *MinioDrive) CreateMultipart(ctx context.Context, in *CheckMultipartParams) (res *MultipartProgress, err error) {
err = gerror.New("当前驱动暂不支持分片上传!")
return
}
// UploadPart 上传分片
func (d *MinioDrive) UploadPart(ctx context.Context, in *UploadPartParams) (res *UploadPartModel, err error) {
err = gerror.New("当前驱动暂不支持分片上传!")
return
}

View File

@@ -45,3 +45,15 @@ func (d *OssDrive) Upload(ctx context.Context, file *ghttp.UploadFile) (fullPath
err = bucket.PutObject(fullPath, f2)
return
}
// CreateMultipart 创建分片事件
func (d *OssDrive) CreateMultipart(ctx context.Context, in *CheckMultipartParams) (res *MultipartProgress, err error) {
err = gerror.New("当前驱动暂不支持分片上传!")
return
}
// UploadPart 上传分片
func (d *OssDrive) UploadPart(ctx context.Context, in *UploadPartParams) (res *UploadPartModel, err error) {
err = gerror.New("当前驱动暂不支持分片上传!")
return
}

View File

@@ -55,3 +55,15 @@ func (d *QiNiuDrive) Upload(ctx context.Context, file *ghttp.UploadFile) (fullPa
err = storage.NewFormUploader(&cfg).Put(ctx, &storage.PutRet{}, token, fullPath, f2, file.Size, &storage.PutExtra{})
return
}
// CreateMultipart 创建分片事件
func (d *QiNiuDrive) CreateMultipart(ctx context.Context, in *CheckMultipartParams) (res *MultipartProgress, err error) {
err = gerror.New("当前驱动暂不支持分片上传!")
return
}
// UploadPart 上传分片
func (d *QiNiuDrive) UploadPart(ctx context.Context, in *UploadPartParams) (res *UploadPartModel, err error) {
err = gerror.New("当前驱动暂不支持分片上传!")
return
}

View File

@@ -48,3 +48,15 @@ func (d *UCloudDrive) Upload(ctx context.Context, file *ghttp.UploadFile) (fullP
err = client.IOPut(f2, fullPath, "")
return
}
// CreateMultipart 创建分片事件
func (d *UCloudDrive) CreateMultipart(ctx context.Context, in *CheckMultipartParams) (res *MultipartProgress, err error) {
err = gerror.New("当前驱动暂不支持分片上传!")
return
}
// UploadPart 上传分片
func (d *UCloudDrive) UploadPart(ctx context.Context, in *UploadPartParams) (res *UploadPartModel, err error) {
err = gerror.New("当前驱动暂不支持分片上传!")
return
}

View File

@@ -1,3 +1,8 @@
// Package admin
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package admin
import (

View File

@@ -11,7 +11,10 @@ import (
"github.com/gogf/gf/v2/frame/g"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/library/dict"
"hotgo/internal/library/hgorm"
"hotgo/internal/model"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/adminin"
"hotgo/internal/model/input/form"
"hotgo/internal/service"
@@ -25,6 +28,7 @@ func NewAdminPost() *sAdminPost {
func init() {
service.RegisterAdminPost(NewAdminPost())
dict.RegisterFunc("adminPostOption", "岗位选项", service.AdminPost().Option)
}
// Delete 删除
@@ -156,6 +160,24 @@ func (s *sAdminPost) List(ctx context.Context, in *adminin.PostListInp) (list []
return
}
// Option 岗位选项
func (s *sAdminPost) Option(ctx context.Context) (opts []*model.Option, err error) {
var list []*entity.AdminPost
if err = dao.AdminPost.Ctx(ctx).OrderAsc(dao.AdminPost.Columns().Sort).Scan(&list); err != nil {
return nil, err
}
if len(list) == 0 {
opts = make([]*model.Option, 0)
return
}
for _, v := range list {
opts = append(opts, dict.GenHashOption(v.Id, v.Name))
}
return
}
// GetMemberByStartName 获取指定用户的第一岗位
func (s *sAdminPost) GetMemberByStartName(ctx context.Context, memberId int64) (name string, err error) {
// 默认取第一岗位

View File

@@ -1,3 +1,8 @@
// Package admin
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package admin
import (
@@ -237,7 +242,7 @@ func (s *sAdminSite) handleLogin(ctx context.Context, mb *entity.AdminMember) (r
}
var dept *entity.AdminDept
if err = g.Model("admin_dept").Ctx(ctx).Fields("id,status").Where("id", mb.DeptId).Scan(&dept); err != nil || dept == nil {
if err = dao.AdminDept.Ctx(ctx).Fields("id,status").Where("id", mb.DeptId).Scan(&dept); err != nil || dept == nil {
err = gerror.Wrap(err, "获取部门信息失败,请稍后重试!")
return
}
@@ -279,7 +284,7 @@ func (s *sAdminSite) handleLogin(ctx context.Context, mb *entity.AdminMember) (r
// BindUserContext 绑定用户上下文
func (s *sAdminSite) BindUserContext(ctx context.Context, claims *model.Identity) (err error) {
var mb *entity.AdminMember
if err = g.Model("admin_member").Ctx(ctx).Where("id", claims.Id).Scan(&mb); err != nil {
if err = dao.AdminMember.Ctx(ctx).Where("id", claims.Id).Scan(&mb); err != nil {
err = gerror.Wrap(err, "获取用户信息失败,请稍后重试!")
return
}
@@ -295,7 +300,7 @@ func (s *sAdminSite) BindUserContext(ctx context.Context, claims *model.Identity
}
var role *entity.AdminRole
if err = g.Model("admin_role").Ctx(ctx).Fields("id,key,status").Where("id", mb.RoleId).Scan(&role); err != nil || role == nil {
if err = dao.AdminRole.Ctx(ctx).Fields("id,key,status").Where("id", mb.RoleId).Scan(&role); err != nil || role == nil {
err = gerror.Wrap(err, "获取角色信息失败,请稍后重试!")
return
}
@@ -306,7 +311,7 @@ func (s *sAdminSite) BindUserContext(ctx context.Context, claims *model.Identity
}
var dept *entity.AdminDept
if err = g.Model("admin_dept").Ctx(ctx).Fields("id,status").Where("id", mb.DeptId).Scan(&dept); err != nil || dept == nil {
if err = dao.AdminDept.Ctx(ctx).Fields("id,status").Where("id", mb.DeptId).Scan(&dept); err != nil || dept == nil {
err = gerror.Wrap(err, "获取部门信息失败,请稍后重试!")
return
}

View File

@@ -38,3 +38,25 @@ func (s *sCommonUpload) UploadFile(ctx context.Context, uploadType string, file
}
return
}
// CheckMultipart 检查文件分片
func (s *sCommonUpload) CheckMultipart(ctx context.Context, in *sysin.CheckMultipartInp) (res *sysin.CheckMultipartModel, err error) {
data, err := storager.CheckMultipart(ctx, in.CheckMultipartParams)
if err != nil {
return nil, err
}
res = new(sysin.CheckMultipartModel)
res.CheckMultipartModel = data
return
}
// UploadPart 上传分片
func (s *sCommonUpload) UploadPart(ctx context.Context, in *sysin.UploadPartInp) (res *sysin.UploadPartModel, err error) {
data, err := storager.UploadPart(ctx, in.UploadPartParams)
if err != nil {
return nil, err
}
res = new(sysin.UploadPartModel)
res.UploadPartModel = data
return
}

View File

@@ -21,7 +21,7 @@ func (s *sHook) accessLog(r *ghttp.Request) {
return
}
var ctx = contexts.Detach(r.Context())
var ctx = r.Context()
if contexts.Get(ctx) == nil {
return
}

View File

@@ -1,3 +1,8 @@
// Package hook
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package hook
import (
@@ -65,7 +70,7 @@ func (s *sHook) lastAdminActive(r *ghttp.Request) {
}
var (
ctx = contexts.Detach(r.Context())
ctx = r.Context()
member = contexts.GetUser(ctx)
)

View File

@@ -1,3 +1,8 @@
// Package middleware
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package middleware
import (

View File

@@ -30,10 +30,11 @@ import (
)
type sMiddleware struct {
LoginUrl string // 登录路由地址
DemoWhiteList g.Map // 演示模式放行的路由白名单
FilterRoutes map[string]ghttp.RouterItem // 支持预处理的web路由
routeMutex sync.Mutex
LoginUrl string // 登录路由地址
DemoWhiteList g.Map // 演示模式放行的路由白名单
NotRecordRequest g.Map // 不记录请求数据的路由(当前请求数据过大时会影响响应效率,可以将路径放到该选项中改善)
FilterRoutes map[string]ghttp.RouterItem // 支持预处理的web路由
routeMutex sync.Mutex
}
func init() {
@@ -48,6 +49,10 @@ func NewMiddleware() *sMiddleware {
"/admin/site/mobileLogin": struct{}{}, // 手机号登录
"/admin/genCodes/preview": struct{}{}, // 预览代码
},
NotRecordRequest: g.Map{
"/admin/upload/file": struct{}{}, // 上传文件
"/admin/upload/uploadPart": struct{}{}, // 上传分片
},
}
}
@@ -63,8 +68,11 @@ func (s *sMiddleware) Ctx(r *ghttp.Request) {
r.SetCtx(ctx)
}
data := g.Map{
"request.body": gjson.New(r.GetBodyString()),
data := make(g.Map)
if _, ok := s.NotRecordRequest[r.URL.Path]; ok {
data["request.body"] = gjson.New(nil)
} else {
data["request.body"] = gjson.New(r.GetBodyString())
}
contexts.Init(r, &model.Context{
@@ -75,6 +83,8 @@ func (s *sMiddleware) Ctx(r *ghttp.Request) {
if len(r.Cookie.GetSessionId()) == 0 {
r.Cookie.SetSessionId(gctx.CtxId(r.Context()))
}
r.SetCtx(r.GetNeverDoneCtx())
r.Middleware.Next()
}
@@ -98,7 +108,7 @@ func (s *sMiddleware) CORS(r *ghttp.Request) {
r.Middleware.Next()
}
// DemoLimit 演示系操作限制
// DemoLimit 演示系操作限制
func (s *sMiddleware) DemoLimit(r *ghttp.Request) {
isDemo := g.Cfg().MustGet(r.Context(), "hotgo.isDemo", false)
if !isDemo.Bool() {

View File

@@ -1,3 +1,8 @@
// Package middleware
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package middleware
import (

View File

@@ -1,3 +1,8 @@
// Package middleware
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package middleware
import (

View File

@@ -1,3 +1,8 @@
// Package middleware
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package middleware
import (
@@ -36,9 +41,19 @@ func (s *sMiddleware) GetFilterRoutes(r *ghttp.Request) map[string]ghttp.RouterI
return s.FilterRoutes
}
// GenFilterRouteKey 生成路由唯一key
func (s *sMiddleware) GenFilterRouteKey(router *ghttp.Router) string {
return router.Method + " " + router.Uri
// GenFilterRequestKey 根据请求生成唯一key
func (s *sMiddleware) GenFilterRequestKey(r *ghttp.Request) string {
return s.GenRouteKey(r.Method, r.Request.URL.Path)
}
// GenFilterRouteKey 根据路由生成唯一key
func (s *sMiddleware) GenFilterRouteKey(r *ghttp.Router) string {
return s.GenRouteKey(r.Method, r.Uri)
}
// GenRouteKey 生成唯一key
func (s *sMiddleware) GenRouteKey(method, path string) string {
return method + " " + path
}
// PreFilter 请求输入预处理

View File

@@ -22,6 +22,16 @@ import (
func (s *sMiddleware) ResponseHandler(r *ghttp.Request) {
r.Middleware.Next()
// 错误状态码接管
switch r.Response.Status {
case 403:
r.Response.Writeln("403 - 网站拒绝显示此网页")
return
case 404:
r.Response.Writeln("404 - 你似乎来到了没有知识存在的荒原…")
return
}
contentType := getContentType(r)
// 已存在响应
if contentType != consts.HTTPContentTypeStream && r.Response.BufferLength() > 0 && contexts.Get(r.Context()).Response != nil {

View File

@@ -1,3 +1,8 @@
// Package pay
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package pay
// 订单创建

View File

@@ -1,3 +1,8 @@
// Package pay
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
package pay
// 异步通知
@@ -15,8 +20,16 @@ import (
"hotgo/internal/library/payment"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/payin"
"hotgo/internal/service"
)
// RegisterNotifyCall 注册支付成功回调方法
func (s *sPay) RegisterNotifyCall() {
payment.RegisterNotifyCallMap(map[string]payment.NotifyCallFunc{
consts.OrderGroupAdminOrder: service.AdminOrder().PayNotify, // 后台充值订单
})
}
// Notify 异步通知
func (s *sPay) Notify(ctx context.Context, in *payin.PayNotifyInp) (res *payin.PayNotifyModel, err error) {
data, err := payment.New(in.PayType).Notify(ctx, payin.NotifyInp{})

View File

@@ -11,10 +11,10 @@ import (
"github.com/gogf/gf/v2/text/gstr"
"hotgo/internal/consts"
"hotgo/internal/library/addons"
"hotgo/internal/library/dict"
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"sort"
)
type sSysAddons struct{}
@@ -83,8 +83,7 @@ func (s *sSysAddons) List(ctx context.Context, in *sysin.AddonsListInp) (list []
row.Skeleton.Logo = consts.AddonsGroupIconMap[row.Skeleton.Group]
}
row.GroupName = consts.AddonsGroupNameMap[row.Skeleton.Group]
row.GroupName = dict.GetOptionLabel(consts.AddonsGroupOptions, row.Skeleton.Group)
list = append(list, row)
i++
}
@@ -93,42 +92,23 @@ func (s *sSysAddons) List(ctx context.Context, in *sysin.AddonsListInp) (list []
return
}
// Selects 选项
func (s *sSysAddons) Selects(ctx context.Context, in *sysin.AddonsSelectsInp) (res *sysin.AddonsSelectsModel, err error) {
res = new(sysin.AddonsSelectsModel)
for k, v := range consts.AddonsGroupNameMap {
res.GroupType = append(res.GroupType, &form.Select{
Value: k,
Name: v,
Label: v,
})
}
sort.Sort(res.GroupType)
for k, v := range consts.AddonsInstallStatusNameMap {
res.Status = append(res.Status, &form.Select{
Value: k,
Name: v,
Label: v,
})
}
sort.Sort(res.Status)
return
}
// Build 提交生成
func (s *sSysAddons) Build(ctx context.Context, in *sysin.AddonsBuildInp) (err error) {
genConfig, err := service.SysConfig().GetLoadGenerate(ctx)
config, err := service.SysConfig().GetLoadGenerate(ctx)
if err != nil {
return
}
if genConfig == nil || genConfig.Addon == nil {
if config == nil || config.Addon == nil {
err = gerror.New("没有找到有效的生成或插件配置,请检查配置文件是否正常")
return
}
return addons.Build(ctx, in.Skeleton, genConfig.Addon)
option := new(addons.BuildOption)
option.Config = config.Addon
option.Skeleton = in.Skeleton
option.Extend = in.Extend
return addons.Build(ctx, option)
}
// Install 安装模块

View File

@@ -64,7 +64,7 @@ func (s *sSysBlacklist) Edit(ctx context.Context, in *sysin.BlacklistEditInp) (e
return
}
// Status 更新部门状态
// Status 更新状态
func (s *sSysBlacklist) Status(ctx context.Context, in *sysin.BlacklistStatusInp) (err error) {
defer s.VariableLoad(ctx, err)
// 修改
@@ -72,7 +72,7 @@ func (s *sSysBlacklist) Status(ctx context.Context, in *sysin.BlacklistStatusInp
return
}
// View 获取指定字典类型信息
// View 获取指定信息
func (s *sSysBlacklist) View(ctx context.Context, in *sysin.BlacklistViewInp) (res *sysin.BlacklistViewModel, err error) {
err = dao.SysBlacklist.Ctx(ctx).Where("id", in.Id).Scan(&res)
return

Some files were not shown because too many files have changed in this diff Show More