mirror of
https://github.com/bufanyun/hotgo.git
synced 2025-11-10 19:23:44 +08:00
发布v2.11.5版本,更新内容请查看:https://github.com/bufanyun/hotgo/blob/v2.0/docs/guide-zh-CN/start-update-log.md
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
"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/gres"
|
||||
"github.com/gogf/gf/v2/os/gview"
|
||||
"hotgo/internal/model/input/form"
|
||||
"sort"
|
||||
@@ -157,6 +158,12 @@ func AddStaticPath(ctx context.Context, server *ghttp.Server, p ...string) {
|
||||
for _, module := range filterInstalled() {
|
||||
name := module.GetSkeleton().Name
|
||||
prefix, path := StaticPath(name, basePath)
|
||||
if !gres.Contains(path) {
|
||||
if _, err := gfile.Search(path); err != nil {
|
||||
g.Log().Warningf(ctx, `AddStaticPath failed: %v`, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
server.AddStaticPath(prefix, path)
|
||||
}
|
||||
}
|
||||
|
||||
18
server/internal/library/cache/file/file.go
vendored
18
server/internal/library/cache/file/file.go
vendored
@@ -36,6 +36,10 @@ type (
|
||||
|
||||
const perm = 0o666
|
||||
|
||||
var (
|
||||
CacheExpiredErr = errors.New("cache expired")
|
||||
)
|
||||
|
||||
// NewAdapterFile creates and returns a new memory cache object.
|
||||
func NewAdapterFile(dir string) gcache.Adapter {
|
||||
return &AdapterFile{
|
||||
@@ -77,7 +81,7 @@ func (c *AdapterFile) Get(ctx context.Context, key interface{}) (*gvar.Var, erro
|
||||
|
||||
func (c *AdapterFile) GetOrSet(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (result *gvar.Var, err error) {
|
||||
result, err = c.Get(ctx, key)
|
||||
if err != nil {
|
||||
if err != nil && !errors.Is(err, CacheExpiredErr) {
|
||||
return nil, err
|
||||
}
|
||||
if result.IsNil() {
|
||||
@@ -88,7 +92,7 @@ func (c *AdapterFile) GetOrSet(ctx context.Context, key interface{}, value inter
|
||||
|
||||
func (c *AdapterFile) GetOrSetFunc(ctx context.Context, key interface{}, f gcache.Func, duration time.Duration) (result *gvar.Var, err error) {
|
||||
v, err := c.Get(ctx, key)
|
||||
if err != nil {
|
||||
if err != nil && !errors.Is(err, CacheExpiredErr) {
|
||||
return nil, err
|
||||
}
|
||||
if v.IsNil() {
|
||||
@@ -160,7 +164,6 @@ func (c *AdapterFile) UpdateExpire(ctx context.Context, key interface{}, duratio
|
||||
return
|
||||
}
|
||||
err = c.Set(ctx, fileKey, v.Val(), duration)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -173,7 +176,6 @@ func (c *AdapterFile) GetExpire(ctx context.Context, key interface{}) (time.Dura
|
||||
if content.Duration <= time.Now().Unix() {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
return time.Duration(time.Now().Unix()-content.Duration) * time.Second, nil
|
||||
}
|
||||
|
||||
@@ -202,7 +204,6 @@ func (c *AdapterFile) createName(key string) string {
|
||||
h := sha256.New()
|
||||
_, _ = h.Write([]byte(key))
|
||||
hash := hex.EncodeToString(h.Sum(nil))
|
||||
|
||||
return filepath.Join(c.dir, fmt.Sprintf("%s.cache", hash))
|
||||
}
|
||||
|
||||
@@ -228,9 +229,8 @@ func (c *AdapterFile) read(key string) (*fileContent, error) {
|
||||
|
||||
if content.Duration <= time.Now().Unix() {
|
||||
_ = c.Delete(key)
|
||||
return nil, errors.New("cache expired")
|
||||
return nil, CacheExpiredErr
|
||||
}
|
||||
|
||||
return content, nil
|
||||
}
|
||||
|
||||
@@ -246,7 +246,6 @@ func (c *AdapterFile) Delete(key string) error {
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return os.Remove(c.createName(key))
|
||||
}
|
||||
|
||||
@@ -270,7 +269,6 @@ func (c *AdapterFile) Fetch(key string) (interface{}, error) {
|
||||
if content == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return content.Data, nil
|
||||
}
|
||||
|
||||
@@ -282,7 +280,6 @@ func (c *AdapterFile) FetchMulti(keys []string) map[string]interface{} {
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -302,7 +299,6 @@ func (c *AdapterFile) Flush() error {
|
||||
for _, name := range names {
|
||||
_ = os.Remove(filepath.Join(c.dir, name))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -6,36 +6,55 @@
|
||||
package cron
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcron"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/utility/simple"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var crons = &cronManager{
|
||||
tasks: make(map[string]*TaskItem),
|
||||
tasks: make(map[string]*TaskItem),
|
||||
loggers: make(map[string]*glog.Logger),
|
||||
}
|
||||
|
||||
// Cron 定时任务接口
|
||||
type Cron interface {
|
||||
// GetName 获取任务名称
|
||||
GetName() string
|
||||
// Execute 执行一次任务
|
||||
Execute(ctx context.Context)
|
||||
// Execute 执行任务
|
||||
Execute(ctx context.Context, parser *Parser) (err error)
|
||||
}
|
||||
|
||||
// Parser 任务执行参数
|
||||
type Parser struct {
|
||||
Args []string // 任务参数
|
||||
Logger *glog.Logger // 日志管理实例
|
||||
}
|
||||
|
||||
// Log 任务调度日志
|
||||
type Log struct {
|
||||
FileName string `json:"fileName" dc:"文件名称"`
|
||||
SizeFormat string `json:"sizeFormat" dc:"文件大小"`
|
||||
Contents string `json:"contents" dc:"文件内容"`
|
||||
}
|
||||
|
||||
// consumerManager 任务管理者
|
||||
type cronManager struct {
|
||||
tasks map[string]*TaskItem
|
||||
tasks map[string]*TaskItem
|
||||
loggers map[string]*glog.Logger
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
@@ -62,7 +81,8 @@ func Register(c Cron) {
|
||||
Logger().Debugf(gctx.GetInitCtx(), "cron.Register name:%v duplicate registration.", name)
|
||||
return
|
||||
}
|
||||
crons.tasks[name] = &TaskItem{Name: c.GetName(), Fun: c.Execute}
|
||||
|
||||
crons.tasks[name] = &TaskItem{Name: c.GetName(), Fun: GenExecuteFun(c.Execute)}
|
||||
}
|
||||
|
||||
// StopALL 停止所有任务
|
||||
@@ -88,27 +108,29 @@ func StartALL(sysCron []*entity.SysCron) (err error) {
|
||||
return gerror.Newf("该任务没有加入任务列表:%v", cron.Name)
|
||||
}
|
||||
|
||||
sn := GenCronSn(cron)
|
||||
|
||||
// 没有则添加
|
||||
if gcron.Search(cron.Name) == nil {
|
||||
if gcron.Search(sn) == nil {
|
||||
var (
|
||||
t *gcron.Entry
|
||||
ctx = context.WithValue(gctx.New(), consts.ContextKeyCronArgs, strings.Split(cron.Params, consts.CronSplitStr))
|
||||
ctx = GenCronCtx(cron)
|
||||
)
|
||||
switch cron.Policy {
|
||||
case consts.CronPolicySame:
|
||||
t, err = gcron.Add(ctx, cron.Pattern, f.Fun, cron.Name)
|
||||
t, err = gcron.Add(ctx, cron.Pattern, f.Fun, sn)
|
||||
|
||||
case consts.CronPolicySingle:
|
||||
t, err = gcron.AddSingleton(ctx, cron.Pattern, f.Fun, cron.Name)
|
||||
t, err = gcron.AddSingleton(ctx, cron.Pattern, f.Fun, sn)
|
||||
|
||||
case consts.CronPolicyOnce:
|
||||
t, err = gcron.AddOnce(ctx, cron.Pattern, f.Fun, cron.Name)
|
||||
t, err = gcron.AddOnce(ctx, cron.Pattern, f.Fun, sn)
|
||||
|
||||
case consts.CronPolicyTimes:
|
||||
if f.Count <= 0 {
|
||||
f.Count = 1
|
||||
}
|
||||
t, err = gcron.AddTimes(ctx, cron.Pattern, int(cron.Count), f.Fun, cron.Name)
|
||||
t, err = gcron.AddTimes(ctx, cron.Pattern, int(cron.Count), f.Fun, sn)
|
||||
|
||||
default:
|
||||
return gerror.Newf("使用无效的策略, cron.Policy=%v", cron.Policy)
|
||||
@@ -122,7 +144,7 @@ func StartALL(sysCron []*entity.SysCron) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
gcron.Start(cron.Name)
|
||||
gcron.Start(sn)
|
||||
|
||||
// 执行完毕,单次和多次执行的任务更新状态
|
||||
if cron.Policy == consts.CronPolicyOnce || cron.Policy == consts.CronPolicyTimes {
|
||||
@@ -144,14 +166,14 @@ func RefreshStatus(sysCron *entity.SysCron) (err error) {
|
||||
}
|
||||
|
||||
if sysCron.Status == consts.StatusEnabled {
|
||||
return Start(sysCron)
|
||||
return ResetStart(sysCron)
|
||||
}
|
||||
return Stop(sysCron)
|
||||
}
|
||||
|
||||
// Stop 停止单个任务
|
||||
func Stop(sysCron *entity.SysCron) (err error) {
|
||||
cr := gcron.Search(sysCron.Name)
|
||||
cr := gcron.Search(GenCronSn(sysCron))
|
||||
if cr == nil {
|
||||
return
|
||||
}
|
||||
@@ -159,6 +181,17 @@ func Stop(sysCron *entity.SysCron) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// ResetStart 重置任务
|
||||
func ResetStart(sysCron *entity.SysCron) (err error) {
|
||||
if err = Stop(sysCron); err != nil {
|
||||
return
|
||||
}
|
||||
if err = Delete(sysCron); err != nil {
|
||||
return
|
||||
}
|
||||
return Start(sysCron)
|
||||
}
|
||||
|
||||
// Once 立即执行一次某个任务
|
||||
func Once(ctx context.Context, sysCron *entity.SysCron) error {
|
||||
crons.RLock()
|
||||
@@ -167,7 +200,7 @@ func Once(ctx context.Context, sysCron *entity.SysCron) error {
|
||||
for _, v := range crons.tasks {
|
||||
if v.Name == sysCron.Name {
|
||||
simple.SafeGo(ctx, func(ctx context.Context) {
|
||||
v.Fun(ctx)
|
||||
v.Fun(GenCronCtx(sysCron))
|
||||
})
|
||||
return nil
|
||||
}
|
||||
@@ -182,7 +215,7 @@ func Delete(sysCron *entity.SysCron) (err error) {
|
||||
}
|
||||
|
||||
for _, v := range gcron.Entries() {
|
||||
if v.Name == sysCron.Name {
|
||||
if v.Name == GenCronSn(sysCron) {
|
||||
gcron.Remove(v.Name)
|
||||
}
|
||||
}
|
||||
@@ -195,10 +228,86 @@ func Start(sysCron *entity.SysCron) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
c := gcron.Search(sysCron.Name)
|
||||
c := gcron.Search(GenCronSn(sysCron))
|
||||
if c != nil {
|
||||
c.Start()
|
||||
return
|
||||
}
|
||||
return StartALL([]*entity.SysCron{sysCron})
|
||||
}
|
||||
|
||||
// DispatchLog 查看指定任务的调度日志
|
||||
func DispatchLog(sysCron *entity.SysCron) (log *Log, err error) {
|
||||
path := fmt.Sprintf("%v/%v", Logger().GetConfig().Path, GenCronSn(sysCron))
|
||||
file, err := FindLastModifiedFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(file) == 0 || !gfile.IsFile(file) {
|
||||
err = gerror.New("未找到日志!")
|
||||
return
|
||||
}
|
||||
|
||||
log = new(Log)
|
||||
log.FileName = file
|
||||
log.SizeFormat = gfile.SizeFormat(file)
|
||||
|
||||
if gfile.Size(file) > 1024*50 {
|
||||
log.Contents, err = ReadLastLines(file, 100)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
log.Contents = gfile.GetContents(file)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ReadLastLines(filename string, lineCount int) (string, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
lines := make([]string, 0, lineCount)
|
||||
for scanner.Scan() {
|
||||
lines = append(lines, scanner.Text())
|
||||
if len(lines) > lineCount {
|
||||
lines = lines[1:]
|
||||
}
|
||||
}
|
||||
|
||||
if err = scanner.Err(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
result := strings.Join(lines, "\n")
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func FindLastModifiedFile(dirPath string) (string, error) {
|
||||
if !gfile.Exists(dirPath) {
|
||||
return "", gerror.New("该任务暂未产生日志!")
|
||||
}
|
||||
|
||||
files, err := gfile.ScanDir(dirPath, "*.log", true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var lastModifiedFile string
|
||||
var lastModifiedTime int64 = 0
|
||||
|
||||
for _, file := range files {
|
||||
modTime := gfile.MTimestamp(file)
|
||||
if modTime > lastModifiedTime {
|
||||
lastModifiedTime = modTime
|
||||
lastModifiedFile = file
|
||||
}
|
||||
}
|
||||
return lastModifiedFile, nil
|
||||
}
|
||||
|
||||
86
server/internal/library/cron/cron_gen.go
Normal file
86
server/internal/library/cron/cron_gen.go
Normal file
@@ -0,0 +1,86 @@
|
||||
// Package cron
|
||||
// @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 cron
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/model/entity"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GenCronSn 生成任务序列号
|
||||
func GenCronSn(sysCron *entity.SysCron) string {
|
||||
return fmt.Sprintf("%s@%d", sysCron.Name, sysCron.Id)
|
||||
}
|
||||
|
||||
// GenCronCtx 生成任务上下文
|
||||
func GenCronCtx(sysCron *entity.SysCron) (ctx context.Context) {
|
||||
ctx = context.WithValue(gctx.New(), consts.ContextKeyCronArgs, strings.Split(sysCron.Params, consts.CronSplitStr))
|
||||
ctx = context.WithValue(ctx, consts.ContextKeyCronSn, GenCronSn(sysCron))
|
||||
return ctx
|
||||
}
|
||||
|
||||
func GenLoggerByCtx(ctx context.Context) *glog.Logger {
|
||||
sn, ok := ctx.Value(consts.ContextKeyCronSn).(string)
|
||||
if !ok {
|
||||
Logger().Panic(ctx, "获取定时任务序列号失败!")
|
||||
}
|
||||
|
||||
logger, ok := crons.loggers[sn]
|
||||
if ok {
|
||||
return logger
|
||||
}
|
||||
|
||||
logger = glog.New()
|
||||
if err := logger.SetConfig(Logger().GetConfig()); err != nil {
|
||||
Logger().Panic(ctx, err)
|
||||
}
|
||||
|
||||
logger.SetFlags(glog.F_TIME_STD | glog.F_FILE_SHORT)
|
||||
// 设置子路径
|
||||
if err := logger.SetPath(fmt.Sprintf("%v/%v", logger.GetPath(), sn)); err != nil {
|
||||
Logger().Panic(ctx, err)
|
||||
}
|
||||
|
||||
crons.Lock()
|
||||
defer crons.Unlock()
|
||||
crons.loggers[sn] = logger
|
||||
return logger
|
||||
}
|
||||
|
||||
// GenExecuteFun 生成执行过程
|
||||
func GenExecuteFun(fun func(ctx context.Context, parser *Parser) (err error)) func(ctx context.Context) {
|
||||
return func(ctx context.Context) {
|
||||
args, ok := ctx.Value(consts.ContextKeyCronArgs).([]string)
|
||||
if !ok {
|
||||
Logger().Panic(ctx, "执行定时任务时,参数解析失败!")
|
||||
return
|
||||
}
|
||||
parser := new(Parser)
|
||||
parser.Args = args
|
||||
parser.Logger = GenLoggerByCtx(ctx)
|
||||
|
||||
st := gtime.Now()
|
||||
err := g.Try(ctx, func(ctx context.Context) {
|
||||
if err := fun(ctx, parser); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
})
|
||||
|
||||
milliseconds := gtime.Now().Sub(st).Milliseconds() // 执行耗时
|
||||
if err != nil {
|
||||
parser.Logger.Errorf(ctx, "execute failed, took %vms, err:%+v", milliseconds, err)
|
||||
return
|
||||
}
|
||||
parser.Logger.Infof(ctx, "execute success, took %vms.", milliseconds)
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,7 @@ func (c cGF) Index(ctx context.Context, in cGFInput) (out *cGFOutput, err error)
|
||||
_, err = Version.Index(ctx, cVersionInput{})
|
||||
return
|
||||
}
|
||||
|
||||
answer := "n"
|
||||
// No argument or option, do installation checks.
|
||||
if data, isInstalled := service.Install.IsInstalled(); !isInstalled {
|
||||
@@ -71,6 +72,7 @@ func (c cGF) Index(ctx context.Context, in cGFInput) (out *cGFOutput, err error)
|
||||
gcmd.Scan("press `Enter` to exit...")
|
||||
return
|
||||
}
|
||||
|
||||
// Print help content.
|
||||
gcmd.CommandFromCtx(ctx).Print()
|
||||
return
|
||||
|
||||
@@ -159,7 +159,7 @@ func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, e
|
||||
if gfile.Exists("main.go") {
|
||||
file = "main.go"
|
||||
} else {
|
||||
mlog.Fatal("build file path cannot be empty")
|
||||
mlog.Fatal("build file path is empty or main.go not found in current working directory")
|
||||
}
|
||||
}
|
||||
if in.Name == "" {
|
||||
@@ -254,17 +254,23 @@ func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, e
|
||||
continue
|
||||
}
|
||||
if len(customSystems) == 0 && len(customArches) == 0 {
|
||||
// Single binary building, output the binary to current working folder.
|
||||
// For example:
|
||||
// `gf build`
|
||||
// `gf build -o main.exe`
|
||||
if runtime.GOOS == "windows" {
|
||||
ext = ".exe"
|
||||
}
|
||||
// Single binary building, output the binary to current working folder.
|
||||
output := ""
|
||||
var outputPath string
|
||||
if len(in.Output) > 0 {
|
||||
output = "-o " + in.Output + ext
|
||||
outputPath = "-o " + in.Output
|
||||
} else {
|
||||
output = "-o " + in.Name + ext
|
||||
outputPath = "-o " + in.Name + ext
|
||||
}
|
||||
cmd = fmt.Sprintf(`go build %s -ldflags "%s" %s %s`, output, ldFlags, in.Extra, file)
|
||||
cmd = fmt.Sprintf(
|
||||
`go build %s -ldflags "%s" %s %s`,
|
||||
outputPath, ldFlags, in.Extra, file,
|
||||
)
|
||||
} else {
|
||||
// Cross-building, output the compiled binary to specified path.
|
||||
if system == "windows" {
|
||||
@@ -272,11 +278,22 @@ func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, e
|
||||
}
|
||||
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 -o %s/%s/%s%s -ldflags "%s" %s%s`,
|
||||
in.Path, system+"_"+arch, in.Name, ext, ldFlags, in.Extra, file,
|
||||
`go build %s -ldflags "%s" %s%s`,
|
||||
outputPath, ldFlags, in.Extra, file,
|
||||
)
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -7,14 +7,19 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gbuild"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
|
||||
@@ -25,6 +30,10 @@ var (
|
||||
Version = cVersion{}
|
||||
)
|
||||
|
||||
const (
|
||||
defaultIndent = "{{indent}}"
|
||||
)
|
||||
|
||||
type cVersion struct {
|
||||
g.Meta `name:"version" brief:"show version information of current binary"`
|
||||
}
|
||||
@@ -36,34 +45,90 @@ type cVersionInput struct {
|
||||
type cVersionOutput struct{}
|
||||
|
||||
func (c cVersion) Index(ctx context.Context, in cVersionInput) (*cVersionOutput, error) {
|
||||
info := gbuild.Info()
|
||||
if info.Git == "" {
|
||||
info.Git = "none"
|
||||
}
|
||||
mlog.Printf(`GoFrame CLI Tool %s, https://goframe.org`, gf.VERSION)
|
||||
gfVersion, err := c.getGFVersionOfCurrentProject()
|
||||
if err != nil {
|
||||
gfVersion = err.Error()
|
||||
detailBuffer := &detailBuffer{}
|
||||
detailBuffer.WriteString(fmt.Sprintf("%s", gf.VERSION))
|
||||
|
||||
detailBuffer.appendLine(0, "Welcome to GoFrame!")
|
||||
|
||||
detailBuffer.appendLine(0, "Env Detail:")
|
||||
goVersion, ok := getGoVersion()
|
||||
if ok {
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("Go Version: %s", goVersion))
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("GF Version(go.mod): %s", getGoFrameVersion(2)))
|
||||
} else {
|
||||
gfVersion = gfVersion + " in current go.mod"
|
||||
}
|
||||
mlog.Printf(`GoFrame Version: %s`, gfVersion)
|
||||
mlog.Printf(`CLI Installed At: %s`, gfile.SelfPath())
|
||||
if info.GoFrame == "" {
|
||||
mlog.Print(`Current is a custom installed version, no installation information.`)
|
||||
return nil, nil
|
||||
v, err := c.getGFVersionOfCurrentProject()
|
||||
if err == nil {
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("GF Version(go.mod): %s", v))
|
||||
} else {
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("GF Version(go.mod): %s", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
mlog.Print(gstr.Trim(fmt.Sprintf(`
|
||||
CLI Built Detail:
|
||||
Go Version: %s
|
||||
GF Version: %s
|
||||
Git Commit: %s
|
||||
Build Time: %s
|
||||
`, info.Golang, info.GoFrame, info.Git, info.Time)))
|
||||
detailBuffer.appendLine(0, "CLI Detail:")
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("Installed At: %s", gfile.SelfPath()))
|
||||
info := gbuild.Info()
|
||||
if info.GoFrame == "" {
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("Built Go Version: %s", runtime.Version()))
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("Built GF Version: %s", gf.VERSION))
|
||||
} else {
|
||||
if info.Git == "" {
|
||||
info.Git = "none"
|
||||
}
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("Built Go Version: %s", info.Golang))
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("Built GF Version: %s", info.GoFrame))
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("Git Commit: %s", info.Git))
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("Built Time: %s", info.Time))
|
||||
}
|
||||
|
||||
detailBuffer.appendLine(0, "Others Detail:")
|
||||
detailBuffer.appendLine(1, "Docs: https://goframe.org")
|
||||
detailBuffer.appendLine(1, fmt.Sprintf("Now : %s", time.Now().Format(time.RFC3339)))
|
||||
|
||||
mlog.Print(detailBuffer.replaceAllIndent(" "))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// detailBuffer is a buffer for detail information.
|
||||
type detailBuffer struct {
|
||||
bytes.Buffer
|
||||
}
|
||||
|
||||
// appendLine appends a line to the buffer with given indent level.
|
||||
func (d *detailBuffer) appendLine(indentLevel int, line string) {
|
||||
d.WriteString(fmt.Sprintf("\n%s%s", strings.Repeat(defaultIndent, indentLevel), line))
|
||||
}
|
||||
|
||||
// replaceAllIndent replaces the tab with given indent string and prints the buffer content.
|
||||
func (d *detailBuffer) replaceAllIndent(indentStr string) string {
|
||||
return strings.ReplaceAll(d.String(), defaultIndent, indentStr)
|
||||
}
|
||||
|
||||
// getGoFrameVersion returns the goframe version of current project using.
|
||||
func getGoFrameVersion(indentLevel int) (gfVersion string) {
|
||||
pkgInfo, err := gproc.ShellExec(context.Background(), `go list -f "{{if (not .Main)}}{{.Path}}@{{.Version}}{{end}}" -m all`)
|
||||
if err != nil {
|
||||
return "cannot find go.mod"
|
||||
}
|
||||
pkgList := gstr.Split(pkgInfo, "\n")
|
||||
for _, v := range pkgList {
|
||||
if strings.HasPrefix(v, "github.com/gogf/gf") {
|
||||
gfVersion += fmt.Sprintf("\n%s%s", strings.Repeat(defaultIndent, indentLevel), v)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// getGoVersion returns the go version
|
||||
func getGoVersion() (goVersion string, ok bool) {
|
||||
goVersion, err := gproc.ShellExec(context.Background(), "go version")
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
goVersion = gstr.TrimLeftStr(goVersion, "go version ")
|
||||
goVersion = gstr.TrimRightStr(goVersion, "\n")
|
||||
return goVersion, true
|
||||
}
|
||||
|
||||
// getGFVersionOfCurrentProject checks and returns the GoFrame version current project using.
|
||||
func (c cVersion) getGFVersionOfCurrentProject() (string, error) {
|
||||
goModPath := gfile.Join(gfile.Pwd(), "go.mod")
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// 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 "context"
|
||||
|
||||
var ctx = context.Background()
|
||||
@@ -0,0 +1,85 @@
|
||||
// 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 (
|
||||
"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) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
path = gfile.Temp(guid.S())
|
||||
apiFolder = gtest.DataPath("genctrl", "api")
|
||||
in = genctrl.CGenCtrlInput{
|
||||
SrcFolder: apiFolder,
|
||||
DstFolder: path,
|
||||
WatchFile: "",
|
||||
SdkPath: "",
|
||||
SdkStdVersion: false,
|
||||
SdkNoV1: false,
|
||||
Clear: false,
|
||||
Merge: false,
|
||||
}
|
||||
)
|
||||
err := gutil.FillStructWithDefault(&in)
|
||||
t.AssertNil(err)
|
||||
|
||||
err = gfile.Mkdir(path)
|
||||
t.AssertNil(err)
|
||||
defer gfile.Remove(path)
|
||||
|
||||
_, err = genctrl.CGenCtrl{}.Ctrl(ctx, in)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// apiInterface file
|
||||
var (
|
||||
genApi = apiFolder + filepath.FromSlash("/article/article.go")
|
||||
genApiExpect = apiFolder + filepath.FromSlash("/article/article_expect.go")
|
||||
)
|
||||
defer gfile.Remove(genApi)
|
||||
t.Assert(gfile.GetContents(genApi), gfile.GetContents(genApiExpect))
|
||||
|
||||
// files
|
||||
files, err := gfile.ScanDir(path, "*.go", true)
|
||||
t.AssertNil(err)
|
||||
t.Assert(files, []string{
|
||||
path + filepath.FromSlash("/article/article.go"),
|
||||
path + filepath.FromSlash("/article/article_new.go"),
|
||||
path + filepath.FromSlash("/article/article_v1_create.go"),
|
||||
path + filepath.FromSlash("/article/article_v1_get_list.go"),
|
||||
path + filepath.FromSlash("/article/article_v1_get_one.go"),
|
||||
path + filepath.FromSlash("/article/article_v1_update.go"),
|
||||
path + filepath.FromSlash("/article/article_v2_create.go"),
|
||||
path + filepath.FromSlash("/article/article_v2_update.go"),
|
||||
})
|
||||
|
||||
// content
|
||||
testPath := gtest.DataPath("genctrl", "controller")
|
||||
expectFiles := []string{
|
||||
testPath + filepath.FromSlash("/article/article.go"),
|
||||
testPath + filepath.FromSlash("/article/article_new.go"),
|
||||
testPath + filepath.FromSlash("/article/article_v1_create.go"),
|
||||
testPath + filepath.FromSlash("/article/article_v1_get_list.go"),
|
||||
testPath + filepath.FromSlash("/article/article_v1_get_one.go"),
|
||||
testPath + filepath.FromSlash("/article/article_v1_update.go"),
|
||||
testPath + filepath.FromSlash("/article/article_v2_create.go"),
|
||||
testPath + filepath.FromSlash("/article/article_v2_update.go"),
|
||||
}
|
||||
for i := range files {
|
||||
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -7,8 +7,8 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
@@ -20,8 +20,6 @@ import (
|
||||
"hotgo/internal/library/hggen/internal/cmd/gendao"
|
||||
)
|
||||
|
||||
var ctx = context.Background()
|
||||
|
||||
func dropTableWithDb(db gdb.DB, table string) {
|
||||
dropTableStmt := fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table)
|
||||
if _, err := db.Exec(ctx, dropTableStmt); err != nil {
|
||||
@@ -105,18 +103,18 @@ func Test_Gen_Dao_Default(t *testing.T) {
|
||||
files, err := gfile.ScanDir(path, "*.go", true)
|
||||
t.AssertNil(err)
|
||||
t.Assert(files, []string{
|
||||
path + "/dao/internal/table_user.go",
|
||||
path + "/dao/table_user.go",
|
||||
path + "/model/do/table_user.go",
|
||||
path + "/model/entity/table_user.go",
|
||||
filepath.FromSlash(path + "/dao/internal/table_user.go"),
|
||||
filepath.FromSlash(path + "/dao/table_user.go"),
|
||||
filepath.FromSlash(path + "/model/do/table_user.go"),
|
||||
filepath.FromSlash(path + "/model/entity/table_user.go"),
|
||||
})
|
||||
// content
|
||||
testPath := gtest.DataPath("gendao", "generated_user")
|
||||
expectFiles := []string{
|
||||
testPath + "/dao/internal/table_user.go",
|
||||
testPath + "/dao/table_user.go",
|
||||
testPath + "/model/do/table_user.go",
|
||||
testPath + "/model/entity/table_user.go",
|
||||
filepath.FromSlash(testPath + "/dao/internal/table_user.go"),
|
||||
filepath.FromSlash(testPath + "/dao/table_user.go"),
|
||||
filepath.FromSlash(testPath + "/model/do/table_user.go"),
|
||||
filepath.FromSlash(testPath + "/model/entity/table_user.go"),
|
||||
}
|
||||
for i, _ := range files {
|
||||
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
|
||||
@@ -209,18 +207,18 @@ func Test_Gen_Dao_TypeMapping(t *testing.T) {
|
||||
files, err := gfile.ScanDir(path, "*.go", true)
|
||||
t.AssertNil(err)
|
||||
t.Assert(files, []string{
|
||||
path + "/dao/internal/table_user.go",
|
||||
path + "/dao/table_user.go",
|
||||
path + "/model/do/table_user.go",
|
||||
path + "/model/entity/table_user.go",
|
||||
filepath.FromSlash(path + "/dao/internal/table_user.go"),
|
||||
filepath.FromSlash(path + "/dao/table_user.go"),
|
||||
filepath.FromSlash(path + "/model/do/table_user.go"),
|
||||
filepath.FromSlash(path + "/model/entity/table_user.go"),
|
||||
})
|
||||
// content
|
||||
testPath := gtest.DataPath("gendao", "generated_user_type_mapping")
|
||||
expectFiles := []string{
|
||||
testPath + "/dao/internal/table_user.go",
|
||||
testPath + "/dao/table_user.go",
|
||||
testPath + "/model/do/table_user.go",
|
||||
testPath + "/model/entity/table_user.go",
|
||||
filepath.FromSlash(testPath + "/dao/internal/table_user.go"),
|
||||
filepath.FromSlash(testPath + "/dao/table_user.go"),
|
||||
filepath.FromSlash(testPath + "/model/do/table_user.go"),
|
||||
filepath.FromSlash(testPath + "/model/entity/table_user.go"),
|
||||
}
|
||||
for i, _ := range files {
|
||||
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
|
||||
@@ -0,0 +1,72 @@
|
||||
// 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 (
|
||||
"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) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
path = gfile.Temp(guid.S())
|
||||
dstFolder = path + filepath.FromSlash("/service")
|
||||
apiFolder = gtest.DataPath("genservice", "logic")
|
||||
in = genservice.CGenServiceInput{
|
||||
SrcFolder: apiFolder,
|
||||
DstFolder: dstFolder,
|
||||
DstFileNameCase: "Snake",
|
||||
WatchFile: "",
|
||||
StPattern: "",
|
||||
Packages: nil,
|
||||
ImportPrefix: "",
|
||||
Clear: false,
|
||||
}
|
||||
)
|
||||
err := gutil.FillStructWithDefault(&in)
|
||||
t.AssertNil(err)
|
||||
|
||||
err = gfile.Mkdir(path)
|
||||
t.AssertNil(err)
|
||||
defer gfile.Remove(path)
|
||||
|
||||
_, err = genservice.CGenService{}.Service(ctx, in)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// logic file
|
||||
var (
|
||||
genApi = apiFolder + filepath.FromSlash("/logic.go")
|
||||
genApiExpect = apiFolder + filepath.FromSlash("/logic_expect.go")
|
||||
)
|
||||
defer gfile.Remove(genApi)
|
||||
t.Assert(gfile.GetContents(genApi), gfile.GetContents(genApiExpect))
|
||||
|
||||
// files
|
||||
files, err := gfile.ScanDir(dstFolder, "*.go", true)
|
||||
t.AssertNil(err)
|
||||
t.Assert(files, []string{
|
||||
dstFolder + filepath.FromSlash("/article.go"),
|
||||
})
|
||||
|
||||
// contents
|
||||
testPath := gtest.DataPath("genservice", "service")
|
||||
expectFiles := []string{
|
||||
testPath + filepath.FromSlash("/article.go"),
|
||||
}
|
||||
for i := range files {
|
||||
t.Assert(gfile.GetContents(files[i]), gfile.GetContents(expectFiles[i]))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -38,7 +38,7 @@ gf gen ctrl
|
||||
)
|
||||
|
||||
const (
|
||||
PatternApiDefinition = `type\s+(\w+)Req\s+struct\s+{([\s\S]+?)}`
|
||||
PatternApiDefinition = `type[\s\(]+(\w+)Req\s+struct\s+{([\s\S]+?)}`
|
||||
PatternCtrlDefinition = `func\s+\(.+?\)\s+\w+\(.+?\*(\w+)\.(\w+)Req\)\s+\(.+?\*(\w+)\.(\w+)Res,\s+\w+\s+error\)\s+{`
|
||||
)
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ package genctrl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
@@ -64,8 +65,8 @@ func (c *controllerGenerator) doGenerateCtrlNewByModuleAndVersion(
|
||||
dstModuleFolderPath, module, version, importPath string,
|
||||
) (err error) {
|
||||
var (
|
||||
moduleFilePath = gfile.Join(dstModuleFolderPath, module+".go")
|
||||
moduleFilePathNew = gfile.Join(dstModuleFolderPath, module+"_new.go")
|
||||
moduleFilePath = filepath.FromSlash(gfile.Join(dstModuleFolderPath, module+".go"))
|
||||
moduleFilePathNew = filepath.FromSlash(gfile.Join(dstModuleFolderPath, module+"_new.go"))
|
||||
ctrlName = fmt.Sprintf(`Controller%s`, gstr.UcFirst(version))
|
||||
interfaceName = fmt.Sprintf(`%s.I%s%s`, module, gstr.CaseCamel(module), gstr.UcFirst(version))
|
||||
newFuncName = fmt.Sprintf(`New%s`, gstr.UcFirst(version))
|
||||
@@ -107,7 +108,7 @@ func (c *controllerGenerator) doGenerateCtrlNewByModuleAndVersion(
|
||||
"{NewFuncName}": newFuncName,
|
||||
"{InterfaceName}": interfaceName,
|
||||
})
|
||||
err = gfile.PutContentsAppend(moduleFilePathNew, gstr.TrimLeft(content))
|
||||
err = gfile.PutContentsAppend(moduleFilePathNew, content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -119,9 +120,9 @@ func (c *controllerGenerator) doGenerateCtrlItem(dstModuleFolderPath string, ite
|
||||
var (
|
||||
methodNameSnake = gstr.CaseSnake(item.MethodName)
|
||||
ctrlName = fmt.Sprintf(`Controller%s`, gstr.UcFirst(item.Version))
|
||||
methodFilePath = gfile.Join(dstModuleFolderPath, fmt.Sprintf(
|
||||
methodFilePath = filepath.FromSlash(gfile.Join(dstModuleFolderPath, fmt.Sprintf(
|
||||
`%s_%s_%s.go`, item.Module, item.Version, methodNameSnake,
|
||||
))
|
||||
)))
|
||||
)
|
||||
var content string
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ package genctrl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
@@ -39,18 +40,17 @@ func (c *apiInterfaceGenerator) Generate(apiModuleFolderPath string, apiModuleAp
|
||||
|
||||
func (c *apiInterfaceGenerator) doGenerate(apiModuleFolderPath string, module string, items []apiItem) (err error) {
|
||||
var (
|
||||
moduleFilePath = gfile.Join(apiModuleFolderPath, fmt.Sprintf(`%s.go`, module))
|
||||
moduleFilePath = filepath.FromSlash(gfile.Join(apiModuleFolderPath, fmt.Sprintf(`%s.go`, module)))
|
||||
importPathMap = gmap.NewListMap()
|
||||
importPaths []string
|
||||
)
|
||||
// if there's already exist file that with the same but not auto generated go file,
|
||||
// it uses another file name.
|
||||
if !utils.IsFileDoNotEdit(moduleFilePath) {
|
||||
moduleFilePath = gfile.Join(apiModuleFolderPath, fmt.Sprintf(`%s.if.go`, module))
|
||||
moduleFilePath = filepath.FromSlash(gfile.Join(apiModuleFolderPath, fmt.Sprintf(`%s.if.go`, module)))
|
||||
}
|
||||
// all import paths.
|
||||
importPathMap.Set("\t"+`"context"`, 1)
|
||||
importPathMap.Set("\t"+``, 1)
|
||||
importPathMap.Set("\t"+`"context"`+"\n", 1)
|
||||
for _, item := range items {
|
||||
importPathMap.Set(fmt.Sprintf("\t"+`"%s"`, item.Import), 1)
|
||||
}
|
||||
@@ -91,7 +91,7 @@ func (c *apiInterfaceGenerator) doGenerate(apiModuleFolderPath string, module st
|
||||
interfaceDefinition += "\n\n"
|
||||
}
|
||||
interfaceContent = gstr.TrimLeft(gstr.ReplaceByMap(interfaceContent, g.MapStrStr{
|
||||
"{Interfaces}": interfaceDefinition,
|
||||
"{Interfaces}": gstr.TrimRightStr(interfaceDefinition, "\n", 2),
|
||||
}))
|
||||
err = gfile.PutContents(moduleFilePath, interfaceContent)
|
||||
mlog.Printf(`generated: %s`, moduleFilePath)
|
||||
|
||||
@@ -8,6 +8,7 @@ package genctrl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
@@ -54,7 +55,7 @@ func (c *apiSdkGenerator) Generate(apiModuleApiItems []apiItem, sdkFolderPath st
|
||||
func (c *apiSdkGenerator) doGenerateSdkPkgFile(sdkFolderPath string) (err error) {
|
||||
var (
|
||||
pkgName = gfile.Basename(sdkFolderPath)
|
||||
pkgFilePath = gfile.Join(sdkFolderPath, fmt.Sprintf(`%s.go`, pkgName))
|
||||
pkgFilePath = filepath.FromSlash(gfile.Join(sdkFolderPath, fmt.Sprintf(`%s.go`, pkgName)))
|
||||
fileContent string
|
||||
)
|
||||
if gfile.Exists(pkgFilePath) {
|
||||
@@ -79,7 +80,7 @@ func (c *apiSdkGenerator) doGenerateSdkIClient(
|
||||
funcName = gstr.CaseCamel(module) + gstr.UcFirst(version)
|
||||
interfaceName = fmt.Sprintf(`I%s`, funcName)
|
||||
moduleImportPath = gstr.Replace(fmt.Sprintf(`"%s"`, gfile.Dir(versionImportPath)), "\\", "/", -1)
|
||||
iClientFilePath = gfile.Join(sdkFolderPath, fmt.Sprintf(`%s.iclient.go`, pkgName))
|
||||
iClientFilePath = filepath.FromSlash(gfile.Join(sdkFolderPath, fmt.Sprintf(`%s.iclient.go`, pkgName)))
|
||||
interfaceFuncDefinition = fmt.Sprintf(
|
||||
`%s() %s.%s`,
|
||||
gstr.CaseCamel(module)+gstr.UcFirst(version), module, interfaceName,
|
||||
@@ -145,9 +146,9 @@ func (c *apiSdkGenerator) doGenerateSdkImplementer(
|
||||
moduleImportPath = gstr.Replace(gfile.Dir(versionImportPath), "\\", "/", -1)
|
||||
versionPrefix = ""
|
||||
implementerName = moduleNameCamel + gstr.UcFirst(version)
|
||||
implementerFilePath = gfile.Join(sdkFolderPath, fmt.Sprintf(
|
||||
implementerFilePath = filepath.FromSlash(gfile.Join(sdkFolderPath, fmt.Sprintf(
|
||||
`%s_%s_%s.go`, pkgName, moduleNameSnake, version,
|
||||
))
|
||||
)))
|
||||
)
|
||||
if sdkNoV1 && version == "v1" {
|
||||
implementerName = moduleNameCamel
|
||||
|
||||
@@ -9,9 +9,10 @@ package gendao
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"golang.org/x/mod/modfile"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
@@ -106,7 +107,7 @@ type generateDaoIndexInput struct {
|
||||
}
|
||||
|
||||
func generateDaoIndex(in generateDaoIndexInput) {
|
||||
path := gfile.Join(in.DirPathDao, in.FileName+".go")
|
||||
path := filepath.FromSlash(gfile.Join(in.DirPathDao, in.FileName+".go"))
|
||||
if in.OverwriteDao || !gfile.Exists(path) {
|
||||
indexContent := gstr.ReplaceByMap(
|
||||
getTemplateFromPathOrDefault(in.TplDaoIndexPath, consts.TemplateGenDaoIndexContent),
|
||||
@@ -136,7 +137,7 @@ type generateDaoInternalInput struct {
|
||||
}
|
||||
|
||||
func generateDaoInternal(in generateDaoInternalInput) {
|
||||
path := gfile.Join(in.DirPathDaoInternal, in.FileName+".go")
|
||||
path := filepath.FromSlash(gfile.Join(in.DirPathDaoInternal, in.FileName+".go"))
|
||||
modelContent := gstr.ReplaceByMap(
|
||||
getTemplateFromPathOrDefault(in.TplDaoInternalPath, consts.TemplateGenDaoInternalContent),
|
||||
g.MapStrStr{
|
||||
|
||||
@@ -9,6 +9,7 @@ package gendao
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
@@ -22,7 +23,7 @@ import (
|
||||
)
|
||||
|
||||
func generateDo(ctx context.Context, in CGenDaoInternalInput) {
|
||||
var dirPathDo = gfile.Join(in.Path, in.DoPath)
|
||||
var dirPathDo = filepath.FromSlash(gfile.Join(in.Path, in.DoPath))
|
||||
if in.Clear {
|
||||
doClear(ctx, dirPathDo, false)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ package gendao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
@@ -33,7 +34,7 @@ func generateEntity(ctx context.Context, in CGenDaoInternalInput) {
|
||||
|
||||
var (
|
||||
newTableName = in.NewTableNames[i]
|
||||
entityFilePath = gfile.Join(dirPathEntity, gstr.CaseSnake(newTableName)+".go")
|
||||
entityFilePath = filepath.FromSlash(gfile.Join(dirPathEntity, gstr.CaseSnake(newTableName)+".go"))
|
||||
structDefinition, appendImports = generateStructDefinition(ctx, generateStructDefinitionInput{
|
||||
CGenDaoInternalInput: in,
|
||||
TableName: tableName,
|
||||
|
||||
@@ -10,9 +10,10 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"strings"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
@@ -70,7 +71,7 @@ func generateStructFieldDefinition(
|
||||
err error
|
||||
localTypeName gdb.LocalType
|
||||
localTypeNameStr string
|
||||
jsonTag = getJsonTagFromCase(field.Name, in.JsonCase)
|
||||
jsonTag = gstr.CaseConvert(field.Name, gstr.CaseTypeMatch(in.JsonCase))
|
||||
)
|
||||
|
||||
if in.TypeMapping != nil && len(in.TypeMapping) > 0 {
|
||||
@@ -158,30 +159,3 @@ func formatComment(comment string) string {
|
||||
comment = gstr.Trim(comment)
|
||||
return comment
|
||||
}
|
||||
|
||||
// getJsonTagFromCase call gstr.Case* function to convert the s to specified case.
|
||||
func getJsonTagFromCase(str, caseStr string) string {
|
||||
switch gstr.ToLower(caseStr) {
|
||||
case gstr.ToLower("Camel"):
|
||||
return gstr.CaseCamel(str)
|
||||
|
||||
case gstr.ToLower("CamelLower"):
|
||||
return gstr.CaseCamelLower(str)
|
||||
|
||||
case gstr.ToLower("Kebab"):
|
||||
return gstr.CaseKebab(str)
|
||||
|
||||
case gstr.ToLower("KebabScreaming"):
|
||||
return gstr.CaseKebabScreaming(str)
|
||||
|
||||
case gstr.ToLower("Snake"):
|
||||
return gstr.CaseSnake(str)
|
||||
|
||||
case gstr.ToLower("SnakeFirstUpper"):
|
||||
return gstr.CaseSnakeFirstUpper(str)
|
||||
|
||||
case gstr.ToLower("SnakeScreaming"):
|
||||
return gstr.CaseSnakeScreaming(str)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
@@ -19,9 +19,10 @@ import (
|
||||
const pkgLoadMode = 0xffffff
|
||||
|
||||
type EnumsParser struct {
|
||||
enums []EnumItem
|
||||
parsedPkg map[string]struct{}
|
||||
prefixes []string
|
||||
enums []EnumItem
|
||||
parsedPkg map[string]struct{}
|
||||
prefixes []string
|
||||
standardPackages map[string]struct{}
|
||||
}
|
||||
|
||||
type EnumItem struct {
|
||||
@@ -31,23 +32,12 @@ type EnumItem struct {
|
||||
Type string // Pkg.ID + TypeName
|
||||
}
|
||||
|
||||
var standardPackages = make(map[string]struct{})
|
||||
|
||||
//func init() {
|
||||
// stdPackages, err := packages.Load(nil, "std")
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// for _, p := range stdPackages {
|
||||
// standardPackages[p.ID] = struct{}{}
|
||||
// }
|
||||
//}
|
||||
|
||||
func NewEnumsParser(prefixes []string) *EnumsParser {
|
||||
return &EnumsParser{
|
||||
enums: make([]EnumItem, 0),
|
||||
parsedPkg: make(map[string]struct{}),
|
||||
prefixes: prefixes,
|
||||
enums: make([]EnumItem, 0),
|
||||
parsedPkg: make(map[string]struct{}),
|
||||
prefixes: prefixes,
|
||||
standardPackages: getStandardPackages(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +49,7 @@ func (p *EnumsParser) ParsePackages(pkgs []*packages.Package) {
|
||||
|
||||
func (p *EnumsParser) ParsePackage(pkg *packages.Package) {
|
||||
// Ignore std packages.
|
||||
if _, ok := standardPackages[pkg.ID]; ok {
|
||||
if _, ok := p.standardPackages[pkg.ID]; ok {
|
||||
return
|
||||
}
|
||||
// Ignore pared packages.
|
||||
@@ -144,3 +134,15 @@ func (p *EnumsParser) Export() string {
|
||||
}
|
||||
return gjson.MustEncodeString(typeEnumMap)
|
||||
}
|
||||
|
||||
func getStandardPackages() map[string]struct{} {
|
||||
standardPackages := make(map[string]struct{})
|
||||
stdPackages, err := packages.Load(nil, "std")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, p := range stdPackages {
|
||||
standardPackages[p.ID] = struct{}{}
|
||||
}
|
||||
return standardPackages
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
@@ -197,7 +198,7 @@ func doGenPbEntityForArray(ctx context.Context, index int, in CGenPbEntityInput)
|
||||
if len(match) == 3 {
|
||||
gdb.AddConfigNode(tempGroup, gdb.ConfigNode{
|
||||
Type: gstr.Trim(match[1]),
|
||||
Link: gstr.Trim(match[2]),
|
||||
Link: in.Link,
|
||||
})
|
||||
db, _ = gdb.Instance(tempGroup)
|
||||
}
|
||||
@@ -246,7 +247,7 @@ func generatePbEntityContentFile(ctx context.Context, in CGenPbEntityInternalInp
|
||||
tableNameSnakeCase = gstr.CaseSnake(newTableName)
|
||||
entityMessageDefine = generateEntityMessageDefinition(tableNameCamelCase, fieldMap, in)
|
||||
fileName = gstr.Trim(tableNameSnakeCase, "-_.")
|
||||
path = gfile.Join(in.Path, fileName+".proto")
|
||||
path = filepath.FromSlash(gfile.Join(in.Path, fileName+".proto"))
|
||||
)
|
||||
if gstr.Contains(entityMessageDefine, "google.protobuf.Timestamp") {
|
||||
imports = `import "google/protobuf/timestamp.proto";`
|
||||
|
||||
@@ -9,6 +9,7 @@ package genservice
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
@@ -93,10 +94,10 @@ const (
|
||||
)
|
||||
|
||||
func (c CGenService) Service(ctx context.Context, in CGenServiceInput) (out *CGenServiceOutput, err error) {
|
||||
in.SrcFolder = gstr.TrimRight(in.SrcFolder, `\/`)
|
||||
in.SrcFolder = gstr.Replace(in.SrcFolder, "\\", "/")
|
||||
in.WatchFile = gstr.TrimRight(in.WatchFile, `\/`)
|
||||
in.WatchFile = gstr.Replace(in.WatchFile, "\\", "/")
|
||||
in.SrcFolder = filepath.ToSlash(in.SrcFolder)
|
||||
in.SrcFolder = gstr.TrimRight(in.SrcFolder, `/`)
|
||||
in.WatchFile = filepath.ToSlash(in.WatchFile)
|
||||
in.WatchFile = gstr.TrimRight(in.WatchFile, `/`)
|
||||
|
||||
// Watch file handling.
|
||||
if in.WatchFile != "" {
|
||||
|
||||
@@ -194,28 +194,9 @@ func (c CGenService) generateInitializationFile(in CGenServiceInput, importSrcPa
|
||||
}
|
||||
|
||||
// getDstFileNameCase call gstr.Case* function to convert the s to specified case.
|
||||
func (c CGenService) getDstFileNameCase(str, caseStr string) string {
|
||||
switch gstr.ToLower(caseStr) {
|
||||
case gstr.ToLower("Lower"):
|
||||
return gstr.ToLower(str)
|
||||
|
||||
case gstr.ToLower("Camel"):
|
||||
return gstr.CaseCamel(str)
|
||||
|
||||
case gstr.ToLower("CamelLower"):
|
||||
return gstr.CaseCamelLower(str)
|
||||
|
||||
case gstr.ToLower("Kebab"):
|
||||
return gstr.CaseKebab(str)
|
||||
|
||||
case gstr.ToLower("KebabScreaming"):
|
||||
return gstr.CaseKebabScreaming(str)
|
||||
|
||||
case gstr.ToLower("SnakeFirstUpper"):
|
||||
return gstr.CaseSnakeFirstUpper(str)
|
||||
|
||||
case gstr.ToLower("SnakeScreaming"):
|
||||
return gstr.CaseSnakeScreaming(str)
|
||||
func (c CGenService) getDstFileNameCase(str, caseStr string) (newStr string) {
|
||||
if newStr := gstr.CaseConvert(str, gstr.CaseTypeMatch(caseStr)); newStr != str {
|
||||
return newStr
|
||||
}
|
||||
return gstr.CaseSnake(str)
|
||||
}
|
||||
|
||||
24
server/internal/library/hggen/internal/cmd/testdata/genctrl/api/article/article_expect.go
vendored
Normal file
24
server/internal/library/hggen/internal/cmd/testdata/genctrl/api/article/article_expect.go
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package article
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"hotgo/internal/library/hggen/internal/cmd/testdata/genctrl/api/article/v1"
|
||||
"hotgo/internal/library/hggen/internal/cmd/testdata/genctrl/api/article/v2"
|
||||
)
|
||||
|
||||
type IArticleV1 interface {
|
||||
Create(ctx context.Context, req *v1.CreateReq) (res *v1.CreateRes, err error)
|
||||
Update(ctx context.Context, req *v1.UpdateReq) (res *v1.UpdateRes, err error)
|
||||
GetList(ctx context.Context, req *v1.GetListReq) (res *v1.GetListRes, err error)
|
||||
GetOne(ctx context.Context, req *v1.GetOneReq) (res *v1.GetOneRes, err error)
|
||||
}
|
||||
|
||||
type IArticleV2 interface {
|
||||
Create(ctx context.Context, req *v2.CreateReq) (res *v2.CreateRes, err error)
|
||||
Update(ctx context.Context, req *v2.UpdateReq) (res *v2.UpdateRes, err error)
|
||||
}
|
||||
27
server/internal/library/hggen/internal/cmd/testdata/genctrl/api/article/v1/edit.go
vendored
Normal file
27
server/internal/library/hggen/internal/cmd/testdata/genctrl/api/article/v1/edit.go
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package v1
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
type (
|
||||
CreateReq struct {
|
||||
g.Meta `path:"/article/create" method:"post" tags:"ArticleService"`
|
||||
Title string `v:"required"`
|
||||
}
|
||||
|
||||
CreateRes struct{}
|
||||
)
|
||||
|
||||
type (
|
||||
UpdateReq struct {
|
||||
g.Meta `path:"/article/update" method:"post" tags:"ArticleService"`
|
||||
Title string `v:"required"`
|
||||
}
|
||||
|
||||
UpdateRes struct{}
|
||||
)
|
||||
25
server/internal/library/hggen/internal/cmd/testdata/genctrl/api/article/v1/get.go
vendored
Normal file
25
server/internal/library/hggen/internal/cmd/testdata/genctrl/api/article/v1/get.go
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package v1
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
type GetListReq struct {
|
||||
g.Meta `path:"/article/list" method:"get" tags:"ArticleService"`
|
||||
}
|
||||
|
||||
type GetListRes struct {
|
||||
list []struct{}
|
||||
}
|
||||
|
||||
type GetOneReq struct {
|
||||
g.Meta `path:"/article/one" method:"get" tags:"ArticleService"`
|
||||
}
|
||||
|
||||
type GetOneRes struct {
|
||||
one struct{}
|
||||
}
|
||||
23
server/internal/library/hggen/internal/cmd/testdata/genctrl/api/article/v2/edit.go
vendored
Normal file
23
server/internal/library/hggen/internal/cmd/testdata/genctrl/api/article/v2/edit.go
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package v2
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
type CreateReq struct {
|
||||
g.Meta `path:"/article/create" method:"post" tags:"ArticleService"`
|
||||
Title string `v:"required"`
|
||||
}
|
||||
|
||||
type CreateRes struct{}
|
||||
|
||||
type UpdateReq struct {
|
||||
g.Meta `path:"/article/update" method:"post" tags:"ArticleService"`
|
||||
Title string `v:"required"`
|
||||
}
|
||||
|
||||
type UpdateRes struct{}
|
||||
5
server/internal/library/hggen/internal/cmd/testdata/genctrl/controller/article/article.go
vendored
Normal file
5
server/internal/library/hggen/internal/cmd/testdata/genctrl/controller/article/article.go
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// =================================================================================
|
||||
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
|
||||
// =================================================================================
|
||||
|
||||
package article
|
||||
21
server/internal/library/hggen/internal/cmd/testdata/genctrl/controller/article/article_new.go
vendored
Normal file
21
server/internal/library/hggen/internal/cmd/testdata/genctrl/controller/article/article_new.go
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
// =================================================================================
|
||||
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
|
||||
// =================================================================================
|
||||
|
||||
package article
|
||||
|
||||
import (
|
||||
"hotgo/internal/library/hggen/internal/cmd/testdata/genctrl/api/article"
|
||||
)
|
||||
|
||||
type ControllerV1 struct{}
|
||||
|
||||
func NewV1() article.IArticleV1 {
|
||||
return &ControllerV1{}
|
||||
}
|
||||
|
||||
type ControllerV2 struct{}
|
||||
|
||||
func NewV2() article.IArticleV2 {
|
||||
return &ControllerV2{}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package article
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"hotgo/internal/library/hggen/internal/cmd/testdata/genctrl/api/article/v1"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) Create(ctx context.Context, req *v1.CreateReq) (res *v1.CreateRes, err error) {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package article
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"hotgo/internal/library/hggen/internal/cmd/testdata/genctrl/api/article/v1"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) GetList(ctx context.Context, req *v1.GetListReq) (res *v1.GetListRes, err error) {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package article
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"hotgo/internal/library/hggen/internal/cmd/testdata/genctrl/api/article/v1"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) GetOne(ctx context.Context, req *v1.GetOneReq) (res *v1.GetOneRes, err error) {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package article
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"hotgo/internal/library/hggen/internal/cmd/testdata/genctrl/api/article/v1"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) Update(ctx context.Context, req *v1.UpdateReq) (res *v1.UpdateRes, err error) {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package article
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"hotgo/internal/library/hggen/internal/cmd/testdata/genctrl/api/article/v2"
|
||||
)
|
||||
|
||||
func (c *ControllerV2) Create(ctx context.Context, req *v2.CreateReq) (res *v2.CreateRes, err error) {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package article
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"hotgo/internal/library/hggen/internal/cmd/testdata/genctrl/api/article/v2"
|
||||
)
|
||||
|
||||
func (c *ControllerV2) Update(ctx context.Context, req *v2.UpdateReq) (res *v2.UpdateRes, err error) {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
}
|
||||
33
server/internal/library/hggen/internal/cmd/testdata/genservice/logic/article/article.go
vendored
Normal file
33
server/internal/library/hggen/internal/cmd/testdata/genservice/logic/article/article.go
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package article
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/library/hggen/internal/cmd/testdata/genservice/service"
|
||||
)
|
||||
|
||||
type sArticle struct {
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterArticle(&sArticle{})
|
||||
}
|
||||
|
||||
// Get article details
|
||||
func (s *sArticle) Get(ctx context.Context, id uint) (info struct{}, err error) {
|
||||
return struct{}{}, err
|
||||
}
|
||||
|
||||
// Create
|
||||
/**
|
||||
* create an article.
|
||||
* @author oldme
|
||||
*/
|
||||
func (s *sArticle) Create(ctx context.Context, info struct{}) (id uint, err error) {
|
||||
return id, err
|
||||
}
|
||||
9
server/internal/library/hggen/internal/cmd/testdata/genservice/logic/logic_expect.go
vendored
Normal file
9
server/internal/library/hggen/internal/cmd/testdata/genservice/logic/logic_expect.go
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// ==========================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// ==========================================================================
|
||||
|
||||
package logic
|
||||
|
||||
import (
|
||||
_ "hotgo/internal/library/hggen/internal/cmd/testdata/genservice/logic/article"
|
||||
)
|
||||
38
server/internal/library/hggen/internal/cmd/testdata/genservice/service/article.go
vendored
Normal file
38
server/internal/library/hggen/internal/cmd/testdata/genservice/service/article.go
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
// ================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// You can delete these comments if you wish manually maintain this interface file.
|
||||
// ================================================================================
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type (
|
||||
IArticle interface {
|
||||
// Get article details
|
||||
Get(ctx context.Context, id uint) (info struct{}, err error)
|
||||
// Create
|
||||
/**
|
||||
* create an article.
|
||||
* @author oldme
|
||||
*/
|
||||
Create(ctx context.Context, info struct{}) (id uint, err error)
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
localArticle IArticle
|
||||
)
|
||||
|
||||
func Article() IArticle {
|
||||
if localArticle == nil {
|
||||
panic("implement not found for interface IArticle, forgot register?")
|
||||
}
|
||||
return localArticle
|
||||
}
|
||||
|
||||
func RegisterArticle(i IArticle) {
|
||||
localArticle = i
|
||||
}
|
||||
@@ -12,12 +12,11 @@ const TemplateGenCtrlControllerEmpty = `
|
||||
// =================================================================================
|
||||
|
||||
package {Module}
|
||||
|
||||
`
|
||||
|
||||
const TemplateGenCtrlControllerNewEmpty = `
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
|
||||
// =================================================================================
|
||||
|
||||
package {Module}
|
||||
@@ -25,7 +24,6 @@ package {Module}
|
||||
import (
|
||||
{ImportPath}
|
||||
)
|
||||
|
||||
`
|
||||
|
||||
const TemplateGenCtrlControllerNewFunc = `
|
||||
@@ -34,7 +32,6 @@ type {CtrlName} struct{}
|
||||
func {NewFuncName}() {InterfaceName} {
|
||||
return &{CtrlName}{}
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
const TemplateGenCtrlControllerMethodFunc = `
|
||||
@@ -63,7 +60,7 @@ func (c *{CtrlName}) {MethodName}(ctx context.Context, req *{Version}.{MethodNam
|
||||
|
||||
const TemplateGenCtrlApiInterface = `
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package {Module}
|
||||
|
||||
@@ -118,7 +118,7 @@ func GetImportPath(filePath string) string {
|
||||
func GetModPath() string {
|
||||
var (
|
||||
oldDir = gfile.Pwd()
|
||||
newDir = gfile.Dir(oldDir)
|
||||
newDir = oldDir
|
||||
goModName = "go.mod"
|
||||
goModPath string
|
||||
)
|
||||
@@ -127,11 +127,11 @@ func GetModPath() string {
|
||||
if gfile.Exists(goModPath) {
|
||||
return goModPath
|
||||
}
|
||||
oldDir = newDir
|
||||
newDir = gfile.Dir(oldDir)
|
||||
if newDir == oldDir {
|
||||
break
|
||||
}
|
||||
oldDir = newDir
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 默认表单组件映射 Ts -> 表单组件
|
||||
@@ -119,7 +118,7 @@ func setDefault(field *sysin.GenCodesColumnListModel) {
|
||||
field.Required = true
|
||||
}
|
||||
|
||||
if strings.Contains(field.Index, consts.GenCodesIndexUNI) {
|
||||
if IsIndexUNI(field.Index) {
|
||||
field.Unique = true
|
||||
}
|
||||
|
||||
@@ -136,7 +135,7 @@ func setDefault(field *sysin.GenCodesColumnListModel) {
|
||||
func setDefaultEdit(field *sysin.GenCodesColumnListModel) {
|
||||
field.IsEdit = true
|
||||
|
||||
if field.Index == consts.GenCodesIndexPK {
|
||||
if IsIndexPK(field.Index) {
|
||||
field.IsEdit = false
|
||||
return
|
||||
}
|
||||
@@ -258,7 +257,7 @@ func setDefaultExport(field *sysin.GenCodesColumnListModel) {
|
||||
func setDefaultQuery(field *sysin.GenCodesColumnListModel) {
|
||||
field.IsQuery = false
|
||||
|
||||
if field.Index == consts.GenCodesIndexPK {
|
||||
if IsIndexPK(field.Index) {
|
||||
field.IsQuery = true
|
||||
return
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"hotgo/internal/consts"
|
||||
)
|
||||
|
||||
func (l *gCurd) webEditTplData(ctx context.Context, in *CurdPreviewInput) (data g.Map, err error) {
|
||||
@@ -28,7 +27,7 @@ func (l *gCurd) generateWebEditFormItem(ctx context.Context, in *CurdPreviewInpu
|
||||
continue
|
||||
}
|
||||
|
||||
if field.Index == consts.GenCodesIndexPK {
|
||||
if IsIndexPK(field.Index) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ func (l *gCurd) getPkField(in *CurdPreviewInput) *sysin.GenCodesColumnListModel
|
||||
panic("getPkField masterFields uninitialized.")
|
||||
}
|
||||
for _, field := range in.masterFields {
|
||||
if field.Index == consts.GenCodesIndexPK {
|
||||
if IsIndexPK(field.Index) {
|
||||
return field
|
||||
}
|
||||
}
|
||||
@@ -165,3 +165,13 @@ func GetModName(ctx context.Context) (modName string, err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// IsIndexPK 是否是主键
|
||||
func IsIndexPK(index string) bool {
|
||||
return gstr.ToUpper(index) == gstr.ToUpper(consts.GenCodesIndexPK)
|
||||
}
|
||||
|
||||
// IsIndexUNI 是否是唯一索引
|
||||
func IsIndexUNI(index string) bool {
|
||||
return gstr.ToUpper(index) == gstr.ToUpper(consts.GenCodesIndexUNI)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"hotgo/utility/convert"
|
||||
"hotgo/utility/tree"
|
||||
)
|
||||
|
||||
type daoInstance interface {
|
||||
@@ -117,6 +116,23 @@ func GetPkField(ctx context.Context, dao daoInstance) (string, error) {
|
||||
return "", gerror.New("no primary key")
|
||||
}
|
||||
|
||||
// GetFieldsToSlice 获取dao实例中的所有字段
|
||||
func GetFieldsToSlice(ctx context.Context, dao daoInstance) ([]string, error) {
|
||||
fields, err := dao.Ctx(ctx).TableFields(dao.Table())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(fields) == 0 {
|
||||
return nil, gerror.New("field not found")
|
||||
}
|
||||
|
||||
var keys []string
|
||||
for _, field := range fields {
|
||||
keys = append(keys, field.Name)
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
// IsUnique 是否唯一
|
||||
func IsUnique(ctx context.Context, dao daoInstance, where g.Map, message string, pkId ...interface{}) error {
|
||||
if len(where) == 0 {
|
||||
@@ -148,40 +164,3 @@ func IsUnique(ctx context.Context, dao daoInstance, where g.Map, message string,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenSubTree 生成下级关系树
|
||||
func GenSubTree(ctx context.Context, dao daoInstance, oldPid int64) (newPid int64, newLevel int, subTree string, err error) {
|
||||
// 顶级树
|
||||
if oldPid <= 0 {
|
||||
return 0, 1, "", nil
|
||||
}
|
||||
|
||||
field, err := GetPkField(ctx, dao)
|
||||
if err != nil {
|
||||
return 0, 0, "", err
|
||||
}
|
||||
|
||||
models, err := dao.Ctx(ctx).Where(field, oldPid).One()
|
||||
if err != nil {
|
||||
return 0, 0, "", err
|
||||
}
|
||||
|
||||
if models.IsEmpty() {
|
||||
return 0, 0, "", gerror.New("上级信息不存在")
|
||||
}
|
||||
|
||||
level, ok := models["level"]
|
||||
if !ok {
|
||||
return 0, 0, "", gerror.New("表中必须包含`level`字段")
|
||||
}
|
||||
|
||||
supTree, ok := models["tree"]
|
||||
if !ok {
|
||||
return 0, 0, "", gerror.New("表中必须包含`tree`字段")
|
||||
}
|
||||
|
||||
newPid = oldPid
|
||||
newLevel = level.Int() + 1
|
||||
subTree = tree.GenLabel(supTree.String(), oldPid)
|
||||
return
|
||||
}
|
||||
|
||||
146
server/internal/library/hgorm/dao_tree.go
Normal file
146
server/internal/library/hgorm/dao_tree.go
Normal file
@@ -0,0 +1,146 @@
|
||||
// Package hgorm
|
||||
// @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 hgorm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"hotgo/internal/model"
|
||||
"hotgo/utility/tree"
|
||||
"hotgo/utility/validate"
|
||||
)
|
||||
|
||||
// GenSubTree 生成下级关系树
|
||||
func GenSubTree(ctx context.Context, dao daoInstance, oldPid int64) (newPid int64, newLevel int, subTree string, err error) {
|
||||
if err = CheckTreeTable(ctx, dao); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if oldPid <= 0 {
|
||||
return 0, 1, "", nil
|
||||
}
|
||||
|
||||
var models *model.DefaultTree
|
||||
if err = dao.Ctx(ctx).WherePri(oldPid).Scan(&models); err != nil {
|
||||
return 0, 0, "", err
|
||||
}
|
||||
|
||||
if models == nil {
|
||||
return 0, 0, "", gerror.New("上级信息不存在")
|
||||
}
|
||||
|
||||
newPid = oldPid
|
||||
newLevel = models.Level + 1
|
||||
subTree = tree.GenLabel(models.Tree, oldPid)
|
||||
return
|
||||
}
|
||||
|
||||
// CheckTreeTable 检查树表
|
||||
func CheckTreeTable(ctx context.Context, dao daoInstance) (err error) {
|
||||
fields, err := GetFieldsToSlice(ctx, dao)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !validate.InSlice(fields, "pid") {
|
||||
return gerror.New("树表必须包含`pid`字段")
|
||||
}
|
||||
|
||||
if !validate.InSlice(fields, "level") {
|
||||
return gerror.New("树表必须包含`level`字段")
|
||||
}
|
||||
|
||||
if !validate.InSlice(fields, "tree") {
|
||||
return gerror.New("树表必须包含`tree`字段")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AutoUpdateTree 自动更新关系树
|
||||
func AutoUpdateTree(ctx context.Context, dao daoInstance, id, pid int64) (newPid int64, newLevel int, newTree string, err error) {
|
||||
if err = CheckTreeTable(ctx, dao); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if pid <= 0 {
|
||||
newPid = 0
|
||||
newLevel = 1
|
||||
newTree = ""
|
||||
} else {
|
||||
var pd *model.DefaultTree
|
||||
if err = dao.Ctx(ctx).WherePri(pid).Scan(&pd); err != nil {
|
||||
return 0, 0, "", err
|
||||
}
|
||||
|
||||
if pd == nil {
|
||||
return 0, 0, "", gerror.New("未查询到树表上级信息,请检查!")
|
||||
}
|
||||
|
||||
if id > 0 && validate.InSlice(tree.GetIds(pd.Tree), id) {
|
||||
return 0, 0, "", gerror.New("上级不能设为自己当前的子级!")
|
||||
}
|
||||
|
||||
newPid = pid
|
||||
newLevel = pd.Level + 1
|
||||
newTree = tree.GenLabel(pd.Tree, pid)
|
||||
}
|
||||
|
||||
if id > 0 {
|
||||
if pid == id {
|
||||
return 0, 0, "", gerror.New("上级不能是自己!")
|
||||
}
|
||||
|
||||
var models *model.DefaultTree
|
||||
if err = dao.Ctx(ctx).WherePri(id).Scan(&models); err != nil {
|
||||
return 0, 0, "", err
|
||||
}
|
||||
|
||||
if models == nil {
|
||||
return 0, 0, "", gerror.New("树表信息不存在,请检查!")
|
||||
}
|
||||
|
||||
// 上级发生变化时,遍历修改其所有的下级关系树
|
||||
if models.Pid != pid {
|
||||
if err = updateChildrenTree(ctx, dao, models.Id, newLevel, newTree); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// updateChildrenTree 更新下级关系树
|
||||
func updateChildrenTree(ctx context.Context, dao daoInstance, pid int64, pLevel int, pTree string) (err error) {
|
||||
var list []*model.DefaultTree
|
||||
if err = dao.Ctx(ctx).Where("pid", pid).Scan(&list); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(list) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
newLevel := pLevel + 1
|
||||
newTree := tree.GenLabel(pTree, pid)
|
||||
|
||||
var updateIds []int64
|
||||
for _, v := range list {
|
||||
updateIds = append(updateIds, v.Id)
|
||||
if err = updateChildrenTree(ctx, dao, v.Id, newLevel, newTree); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(updateIds) > 0 {
|
||||
update := g.Map{
|
||||
"level": newLevel,
|
||||
"tree": newTree,
|
||||
}
|
||||
_, err = dao.Ctx(ctx).WhereIn("id", updateIds).Data(update).Update()
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -104,7 +104,7 @@ func Sorter(in ISorter) func(m *gdb.Model) *gdb.Model {
|
||||
// 不存在排序条件,默认使用主表主键做降序排序
|
||||
var pk string
|
||||
for name, field := range fields {
|
||||
if gstr.ContainsI(field.Key, consts.GenCodesIndexPK) {
|
||||
if gstr.ContainsI(gstr.ToUpper(field.Key), gstr.ToUpper(consts.GenCodesIndexPK)) {
|
||||
pk = name
|
||||
break
|
||||
}
|
||||
|
||||
@@ -48,6 +48,10 @@ type Conn struct {
|
||||
|
||||
var idCounter int64
|
||||
|
||||
var pkgOption = gtcp.PkgOption{
|
||||
MaxDataSize: 0x7FFFFFFF,
|
||||
}
|
||||
|
||||
func NewConn(conn *gtcp.Conn, logger *glog.Logger, msgParser *MsgParser) *Conn {
|
||||
tcpConn := new(Conn)
|
||||
tcpConn.CID = atomic.AddInt64(&idCounter, 1)
|
||||
@@ -66,6 +70,7 @@ func NewConn(conn *gtcp.Conn, logger *glog.Logger, msgParser *MsgParser) *Conn {
|
||||
break
|
||||
}
|
||||
if err := conn.SendPkg(b); err != nil {
|
||||
logger.Errorf(gctx.New(), "SendPkg err:%+v", err)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@ func New(name ...string) UploadDrive {
|
||||
drive = &OssDrive{}
|
||||
case consts.UploadDriveQiNiu:
|
||||
drive = &QiNiuDrive{}
|
||||
case consts.UploadDriveMinio:
|
||||
drive = &MinioDrive{}
|
||||
default:
|
||||
panic(fmt.Sprintf("暂不支持的存储驱动:%v", driveType))
|
||||
}
|
||||
@@ -82,6 +84,11 @@ func DoUpload(ctx context.Context, typ string, file *ghttp.UploadFile) (result *
|
||||
err = gerror.Newf("图片大小不能超过%vMB", config.ImageSize)
|
||||
return
|
||||
}
|
||||
|
||||
if len(config.ImageType) > 0 && !validate.InSlice(strings.Split(config.ImageType, `,`), meta.Ext) {
|
||||
err = gerror.New("上传图片类型未经允许")
|
||||
return
|
||||
}
|
||||
case KindDoc:
|
||||
if !IsDocType(meta.Ext) {
|
||||
err = gerror.New("上传的文件不是文档")
|
||||
@@ -110,6 +117,11 @@ func DoUpload(ctx context.Context, typ string, file *ghttp.UploadFile) (result *
|
||||
err = gerror.Newf("文件大小不能超过%vMB", config.FileSize)
|
||||
return
|
||||
}
|
||||
|
||||
if len(config.FileType) > 0 && !validate.InSlice(strings.Split(config.FileType, `,`), meta.Ext) {
|
||||
err = gerror.New("上传文件类型未经允许")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
result, err = hasFile(ctx, meta.Md5)
|
||||
@@ -148,6 +160,8 @@ func LastUrl(ctx context.Context, fullPath, drive string) string {
|
||||
return config.OssBucketURL + "/" + fullPath
|
||||
case consts.UploadDriveQiNiu:
|
||||
return config.QiNiuDomain + "/" + fullPath
|
||||
case consts.UploadDriveMinio:
|
||||
return fmt.Sprintf("%s/%s/%s", config.MinioDomain, config.MinioBucket, fullPath)
|
||||
default:
|
||||
return fullPath
|
||||
}
|
||||
|
||||
64
server/internal/library/storager/upload_minio.go
Normal file
64
server/internal/library/storager/upload_minio.go
Normal file
@@ -0,0 +1,64 @@
|
||||
// Package storager
|
||||
// @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 storager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
"github.com/minio/minio-go/v7/pkg/s3utils"
|
||||
"mime"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// MinioDrive minio对象存储驱动
|
||||
type MinioDrive struct {
|
||||
}
|
||||
|
||||
// Upload 上传到minio对象存储
|
||||
func (d *MinioDrive) Upload(ctx context.Context, file *ghttp.UploadFile) (fullPath string, err error) {
|
||||
if config.MinioPath == "" {
|
||||
err = gerror.New("minio存储驱动必须配置存储路径!")
|
||||
return
|
||||
}
|
||||
|
||||
client, err := minio.New(config.MinioEndpoint, &minio.Options{
|
||||
Creds: credentials.NewStaticV4(config.MinioAccessKey, config.MinioSecretKey, ""),
|
||||
Secure: config.MinioUseSSL == 1,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err = s3utils.CheckValidBucketName(config.MinioBucket); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fullPath = GenFullPath(config.MinioPath, gfile.Ext(file.Filename))
|
||||
if err = s3utils.CheckValidObjectName(fullPath); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
reader, err := file.Open()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
opts := minio.PutObjectOptions{
|
||||
ContentType: mime.TypeByExtension(filepath.Ext(file.Filename)),
|
||||
}
|
||||
if opts.ContentType == "" {
|
||||
opts.ContentType = "application/octet-stream"
|
||||
}
|
||||
|
||||
_, err = client.PutObject(ctx, config.MinioBucket, fullPath, reader, file.Size, opts)
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user