im 管理后台
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
// Package sys
|
||||
// @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 sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"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"
|
||||
)
|
||||
|
||||
type sSysAddons struct{}
|
||||
|
||||
func NewSysAddons() *sSysAddons {
|
||||
return &sSysAddons{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysAddons(NewSysAddons())
|
||||
}
|
||||
|
||||
// List 获取列表
|
||||
func (s *sSysAddons) List(ctx context.Context, in *sysin.AddonsListInp) (list []*sysin.AddonsListModel, totalCount int, err error) {
|
||||
sks := addons.GetSkeletons()
|
||||
if len(sks) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
i int
|
||||
_, perPage, offset = form.CalPage(in.Page, in.PerPage)
|
||||
)
|
||||
|
||||
for k, skeleton := range sks {
|
||||
ok := k >= offset && i <= perPage
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
row := new(sysin.AddonsListModel)
|
||||
row.Skeleton = *skeleton
|
||||
|
||||
if in.Group > 0 {
|
||||
if row.Skeleton.Group != in.Group {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if in.Name != "" {
|
||||
if row.Skeleton.Label != in.Name && row.Skeleton.Name != in.Name {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
install, err := addons.ScanInstall(row.Skeleton.GetModule())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if install == nil {
|
||||
row.InstallStatus = consts.AddonsInstallStatusNo
|
||||
row.InstallVersion = "v0.0.0"
|
||||
} else {
|
||||
row.InstallStatus = install.Status
|
||||
row.InstallVersion = install.Version
|
||||
row.CanSave = gstr.CompareVersion(row.Skeleton.Version, install.Version) > 0
|
||||
}
|
||||
|
||||
if in.Status > 0 {
|
||||
if row.InstallStatus != in.Status {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if row.Skeleton.Logo == "" {
|
||||
row.Skeleton.Logo = consts.AddonsGroupIconMap[row.Skeleton.Group]
|
||||
}
|
||||
|
||||
row.GroupName = dict.GetOptionLabel(consts.AddonsGroupOptions, row.Skeleton.Group)
|
||||
list = append(list, row)
|
||||
i++
|
||||
}
|
||||
|
||||
totalCount = len(sks)
|
||||
return
|
||||
}
|
||||
|
||||
// Build 提交生成
|
||||
func (s *sSysAddons) Build(ctx context.Context, in *sysin.AddonsBuildInp) (err error) {
|
||||
config, err := service.SysConfig().GetLoadGenerate(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if config == nil || config.Addon == nil {
|
||||
err = gerror.New("没有找到有效的生成或插件配置,请检查配置文件是否正常")
|
||||
return
|
||||
}
|
||||
|
||||
option := new(addons.BuildOption)
|
||||
option.Config = config.Addon
|
||||
option.Skeleton = in.Skeleton
|
||||
option.Extend = in.Extend
|
||||
return addons.Build(ctx, option)
|
||||
}
|
||||
|
||||
// Install 安装模块
|
||||
func (s *sSysAddons) Install(ctx context.Context, in *sysin.AddonsInstallInp) (err error) {
|
||||
return addons.Install(in.GetModule())
|
||||
}
|
||||
|
||||
// Upgrade 更新模块
|
||||
func (s *sSysAddons) Upgrade(ctx context.Context, in *sysin.AddonsUpgradeInp) (err error) {
|
||||
return addons.Upgrade(in.GetModule())
|
||||
}
|
||||
|
||||
// UnInstall 卸载模块
|
||||
func (s *sSysAddons) UnInstall(ctx context.Context, in *sysin.AddonsUnInstallInp) (err error) {
|
||||
return addons.UnInstall(in.GetModule())
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
// Package sys
|
||||
// @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 sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
"hotgo/utility/simple"
|
||||
|
||||
"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/gtime"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
var AddonsMaskDemoField []string
|
||||
|
||||
type sSysAddonsConfig struct{}
|
||||
|
||||
func NewSysAddonsConfig() *sSysAddonsConfig {
|
||||
return &sSysAddonsConfig{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysAddonsConfig(NewSysAddonsConfig())
|
||||
}
|
||||
|
||||
// GetConfigByGroup 获取指定分组的配置
|
||||
func (s *sSysAddonsConfig) GetConfigByGroup(ctx context.Context, in *sysin.GetAddonsConfigInp) (res *sysin.GetAddonsConfigModel, err error) {
|
||||
if in.AddonName == "" {
|
||||
err = gerror.New("插件名称不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
if in.Group == "" {
|
||||
err = gerror.New("分组不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
mod = dao.SysAddonsConfig.Ctx(ctx)
|
||||
models []*entity.SysAddonsConfig
|
||||
cols = dao.SysAddonsConfig.Columns()
|
||||
)
|
||||
|
||||
if err = mod.Fields(cols.Key, cols.Value, cols.Type).
|
||||
Where(cols.AddonName, in.AddonName).
|
||||
Where(cols.Group, in.Group).
|
||||
Scan(&models); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(models) > 0 {
|
||||
res = new(sysin.GetAddonsConfigModel)
|
||||
res.List = make(g.Map, len(models))
|
||||
for _, v := range models {
|
||||
val, err := s.ConversionType(ctx, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.List[v.Key] = val
|
||||
if simple.IsDemo(ctx) && gstr.InArray(AddonsMaskDemoField, v.Key) {
|
||||
res.List[v.Key] = consts.DemoTips
|
||||
res.List[v.Key] = consts.DemoTips
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ConversionType 转换类型
|
||||
func (s *sSysAddonsConfig) ConversionType(ctx context.Context, models *entity.SysAddonsConfig) (value interface{}, err error) {
|
||||
if models == nil {
|
||||
err = gerror.New("数据不存在")
|
||||
return
|
||||
}
|
||||
return consts.ConvType(models.Value, models.Type), nil
|
||||
}
|
||||
|
||||
// UpdateConfigByGroup 更新指定分组的配置
|
||||
func (s *sSysAddonsConfig) UpdateConfigByGroup(ctx context.Context, in *sysin.UpdateAddonsConfigInp) (err error) {
|
||||
if in.AddonName == "" {
|
||||
err = gerror.New("插件名称不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
if in.Group == "" {
|
||||
err = gerror.New("分组不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
mod = dao.SysAddonsConfig.Ctx(ctx)
|
||||
models []*entity.SysAddonsConfig
|
||||
)
|
||||
|
||||
if err = mod.
|
||||
Where("addon_name", in.AddonName).
|
||||
Where("group", in.Group).
|
||||
Scan(&models); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = dao.SysAddonsConfig.Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
for k, v := range in.List {
|
||||
row := s.getConfigByKey(k, models)
|
||||
// 新增
|
||||
if row == nil {
|
||||
return gerror.Newf("暂不支持从前台添加变量,请先在数据库表[%v]中配置变量:%v", dao.SysAddonsConfig.Table(), k)
|
||||
}
|
||||
|
||||
// 更新
|
||||
if _, err = dao.SysAddonsConfig.Ctx(ctx).Where("id", row.Id).Data(g.Map{"value": v, "updated_at": gtime.Now()}).Update(); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysAddonsConfig) getConfigByKey(key string, models []*entity.SysAddonsConfig) *entity.SysAddonsConfig {
|
||||
if len(models) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, v := range models {
|
||||
if key == v.Key {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
// Package sys
|
||||
// @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 sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/contexts"
|
||||
"hotgo/internal/library/dict"
|
||||
"hotgo/internal/library/hgorm/handler"
|
||||
"hotgo/internal/library/storager"
|
||||
"hotgo/internal/model"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
"hotgo/utility/format"
|
||||
)
|
||||
|
||||
type sSysAttachment struct{}
|
||||
|
||||
func NewSysAttachment() *sSysAttachment {
|
||||
return &sSysAttachment{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysAttachment(NewSysAttachment())
|
||||
dict.RegisterFunc("AttachmentKindOption", "上传类型选项", service.SysAttachment().AttachmentKindOption)
|
||||
}
|
||||
|
||||
// Model ORM模型
|
||||
func (s *sSysAttachment) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
||||
return handler.Model(dao.SysAttachment.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// Delete 删除附件
|
||||
func (s *sSysAttachment) Delete(ctx context.Context, in *sysin.AttachmentDeleteInp) (err error) {
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除附件失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取附件信息
|
||||
func (s *sSysAttachment) View(ctx context.Context, in *sysin.AttachmentViewInp) (res *sysin.AttachmentViewModel, err error) {
|
||||
if err = s.Model(ctx).WherePri(in.Id).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取附件信息失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// List 获取附件列表
|
||||
func (s *sSysAttachment) List(ctx context.Context, in *sysin.AttachmentListInp) (list []*sysin.AttachmentListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx)
|
||||
memberId := contexts.GetUserId(ctx)
|
||||
|
||||
// 超管允许查看指定用户的附件
|
||||
if service.AdminMember().VerifySuperId(ctx, memberId) && in.MemberId > 0 {
|
||||
mod = mod.Where(dao.SysAttachment.Columns().MemberId, in.MemberId)
|
||||
} else {
|
||||
mod = mod.Where(dao.SysAttachment.Columns().MemberId, memberId)
|
||||
}
|
||||
|
||||
if in.Drive != "" {
|
||||
mod = mod.Where(dao.SysAttachment.Columns().Drive, in.Drive)
|
||||
}
|
||||
|
||||
if in.Name != "" {
|
||||
mod = mod.WhereLike(dao.SysAttachment.Columns().Name, "%"+in.Name+"%")
|
||||
}
|
||||
|
||||
if in.Status > 0 {
|
||||
mod = mod.Where(dao.SysAttachment.Columns().Status, in.Status)
|
||||
}
|
||||
|
||||
if len(in.UpdatedAt) == 2 {
|
||||
mod = mod.WhereBetween(dao.SysAttachment.Columns().UpdatedAt, in.UpdatedAt[0], in.UpdatedAt[1])
|
||||
}
|
||||
|
||||
if in.Kind != "" {
|
||||
mod = mod.Where(dao.SysAttachment.Columns().Kind, in.Kind)
|
||||
}
|
||||
|
||||
totalCount, err = mod.Count()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, "获取附件数据行失败!")
|
||||
return
|
||||
}
|
||||
|
||||
if totalCount == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if err = mod.Page(in.Page, in.PerPage).OrderDesc(dao.SysAttachment.Columns().UpdatedAt).Scan(&list); err != nil {
|
||||
err = gerror.Wrap(err, "获取附件列表失败!")
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range list {
|
||||
v.SizeFormat = format.FileSize(v.Size)
|
||||
v.FileUrl = storager.LastUrl(ctx, v.FileUrl, v.Drive)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ClearKind 清空上传类型
|
||||
func (s *sSysAttachment) ClearKind(ctx context.Context, in *sysin.AttachmentClearKindInp) (err error) {
|
||||
memberId := contexts.GetUserId(ctx)
|
||||
if _, err = s.Model(ctx).Where(dao.SysAttachment.Columns().MemberId, memberId).Where(dao.SysAttachment.Columns().Kind, in.Kind).Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除附件上传类型失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AttachmentKindOption 上传类型选项
|
||||
func (s *sSysAttachment) AttachmentKindOption(ctx context.Context) (opts []*model.Option, err error) {
|
||||
opts = append(opts, dict.GenDefaultOption("", "全部"))
|
||||
opts = append(opts, dict.GenSuccessOption(storager.KindImg, "图片", "PictureOutlined"))
|
||||
opts = append(opts, dict.GenPrimaryOption(storager.KindDoc, "文档", "FileWordOutlined"))
|
||||
opts = append(opts, dict.GenSuccessOption(storager.KindAudio, "音频", "CustomerServiceOutlined"))
|
||||
opts = append(opts, dict.GenSuccessOption(storager.KindVideo, "视频", "PlaySquareOutlined"))
|
||||
opts = append(opts, dict.GenSuccessOption(storager.KindZip, "压缩包", "FileZipOutlined"))
|
||||
opts = append(opts, dict.GenSuccessOption(storager.KindOther, "其他", "PlusOutlined"))
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// Package sys
|
||||
// @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 sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/database/gredis"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"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/gtime"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/global"
|
||||
"hotgo/internal/library/location"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
"hotgo/utility/convert"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type sSysBlacklist struct {
|
||||
sync.RWMutex
|
||||
list map[string]struct{}
|
||||
}
|
||||
|
||||
func NewSysBlacklist() *sSysBlacklist {
|
||||
return &sSysBlacklist{
|
||||
list: make(map[string]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysBlacklist(NewSysBlacklist())
|
||||
}
|
||||
|
||||
// Delete 删除
|
||||
func (s *sSysBlacklist) Delete(ctx context.Context, in *sysin.BlacklistDeleteInp) (err error) {
|
||||
defer s.VariableLoad(ctx, err)
|
||||
_, err = dao.SysBlacklist.Ctx(ctx).Where("id", in.Id).Delete()
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增
|
||||
func (s *sSysBlacklist) Edit(ctx context.Context, in *sysin.BlacklistEditInp) (err error) {
|
||||
defer s.VariableLoad(ctx, err)
|
||||
if in.Ip == "" {
|
||||
err = gerror.New("ip不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
// 修改
|
||||
if in.Id > 0 {
|
||||
_, err = dao.SysBlacklist.Ctx(ctx).Where("id", in.Id).Data(in).Update()
|
||||
return
|
||||
}
|
||||
|
||||
// 新增
|
||||
_, err = dao.SysBlacklist.Ctx(ctx).Data(in).OmitEmptyData().Insert()
|
||||
return
|
||||
}
|
||||
|
||||
// Status 更新状态
|
||||
func (s *sSysBlacklist) Status(ctx context.Context, in *sysin.BlacklistStatusInp) (err error) {
|
||||
defer s.VariableLoad(ctx, err)
|
||||
// 修改
|
||||
_, err = dao.SysBlacklist.Ctx(ctx).Where("id", in.Id).Data("status", in.Status).Update()
|
||||
return
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// List 获取列表
|
||||
func (s *sSysBlacklist) List(ctx context.Context, in *sysin.BlacklistListInp) (list []*sysin.BlacklistListModel, totalCount int, err error) {
|
||||
mod := dao.SysBlacklist.Ctx(ctx)
|
||||
cols := dao.SysBlacklist.Columns()
|
||||
|
||||
if in.Ip != "" {
|
||||
mod = mod.WhereLike(cols.Ip, "%"+in.Ip+"%")
|
||||
}
|
||||
|
||||
if in.Remark != "" {
|
||||
mod = mod.WhereLike(cols.Remark, "%"+in.Remark+"%")
|
||||
}
|
||||
|
||||
if in.Status > 0 {
|
||||
mod = mod.Where(cols.Status, in.Status)
|
||||
}
|
||||
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween(cols.CreatedAt, gtime.New(in.CreatedAt[0]), gtime.New(in.CreatedAt[1]))
|
||||
}
|
||||
|
||||
totalCount, err = mod.Count()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if totalCount == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if err = mod.Page(in.Page, in.PerPage).Order("id desc").Scan(&list); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// VariableLoad 变化加载
|
||||
func (s *sSysBlacklist) VariableLoad(ctx context.Context, err error) {
|
||||
if err == nil {
|
||||
s.Load(ctx)
|
||||
global.PublishClusterSync(ctx, consts.ClusterSyncSysBlacklist, nil)
|
||||
}
|
||||
}
|
||||
|
||||
// Load 加载黑名单
|
||||
func (s *sSysBlacklist) Load(ctx context.Context) {
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
|
||||
s.list = make(map[string]struct{})
|
||||
|
||||
array, err := dao.SysBlacklist.Ctx(ctx).
|
||||
Fields(dao.SysBlacklist.Columns().Ip).
|
||||
Where(dao.SysBlacklist.Columns().Status, consts.StatusEnabled).
|
||||
Array()
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "load blacklist fail:%+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range array {
|
||||
list := convert.IpFilterStrategy(v.String())
|
||||
if len(list) > 0 {
|
||||
for k := range list {
|
||||
s.list[k] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// VerifyRequest 验证请求的访问IP是否在黑名单,如果存在则返回错误
|
||||
func (s *sSysBlacklist) VerifyRequest(r *ghttp.Request) (err error) {
|
||||
if len(s.list) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := s.list[location.GetClientIp(r)]; ok {
|
||||
err = gerror.NewCode(gcode.New(gcode.CodeServerBusy.Code(), "请求异常,已被封禁,如有疑问请联系管理员!", nil))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ClusterSync 集群同步
|
||||
func (s *sSysBlacklist) ClusterSync(ctx context.Context, message *gredis.Message) {
|
||||
s.Load(ctx)
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
// Package sys
|
||||
// @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 sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/global"
|
||||
"hotgo/internal/library/payment"
|
||||
"hotgo/internal/library/sms"
|
||||
"hotgo/internal/library/storager"
|
||||
"hotgo/internal/library/token"
|
||||
"hotgo/internal/library/wechat"
|
||||
"hotgo/internal/model"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
"hotgo/utility/simple"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/database/gredis"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type sSysConfig struct{}
|
||||
|
||||
func NewSysConfig() *sSysConfig {
|
||||
return &sSysConfig{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysConfig(NewSysConfig())
|
||||
}
|
||||
|
||||
// InitConfig 初始化系统配置
|
||||
func (s *sSysConfig) InitConfig(ctx context.Context) {
|
||||
if err := s.LoadConfig(ctx); err != nil {
|
||||
g.Log().Fatalf(ctx, "InitConfig fail:%+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadConfig 加载系统配置
|
||||
func (s *sSysConfig) LoadConfig(ctx context.Context) (err error) {
|
||||
wx, err := s.GetWechat(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
wechat.SetConfig(wx)
|
||||
|
||||
pay, err := s.GetPay(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
payment.SetConfig(pay)
|
||||
|
||||
upload, err := s.GetUpload(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
storager.SetConfig(upload)
|
||||
|
||||
sm, err := s.GetSms(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sms.SetConfig(sm)
|
||||
|
||||
tk, err := s.GetLoadToken(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
token.SetConfig(tk)
|
||||
|
||||
// 更多
|
||||
// ...
|
||||
return
|
||||
}
|
||||
|
||||
// GetLogin 获取登录配置
|
||||
func (s *sSysConfig) GetLogin(ctx context.Context) (conf *model.LoginConfig, err error) {
|
||||
models, err := s.GetConfigByGroup(ctx, &sysin.GetConfigInp{Group: "login"})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = gconv.Scan(models.List, &conf)
|
||||
return
|
||||
}
|
||||
|
||||
// GetWechat 获取微信配置
|
||||
func (s *sSysConfig) GetWechat(ctx context.Context) (conf *model.WechatConfig, err error) {
|
||||
models, err := s.GetConfigByGroup(ctx, &sysin.GetConfigInp{Group: "wechat"})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = gconv.Scan(models.List, &conf)
|
||||
return
|
||||
}
|
||||
|
||||
// GetPay 获取支付配置
|
||||
func (s *sSysConfig) GetPay(ctx context.Context) (conf *model.PayConfig, err error) {
|
||||
models, err := s.GetConfigByGroup(ctx, &sysin.GetConfigInp{Group: "pay"})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = gconv.Scan(models.List, &conf)
|
||||
return
|
||||
}
|
||||
|
||||
// GetSms 获取短信配置
|
||||
func (s *sSysConfig) GetSms(ctx context.Context) (conf *model.SmsConfig, err error) {
|
||||
models, err := s.GetConfigByGroup(ctx, &sysin.GetConfigInp{Group: "sms"})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = gconv.Scan(models.List, &conf)
|
||||
return
|
||||
}
|
||||
|
||||
// GetGeo 获取地理配置
|
||||
func (s *sSysConfig) GetGeo(ctx context.Context) (conf *model.GeoConfig, err error) {
|
||||
models, err := s.GetConfigByGroup(ctx, &sysin.GetConfigInp{Group: "geo"})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = gconv.Scan(models.List, &conf)
|
||||
return
|
||||
}
|
||||
|
||||
// GetUpload 获取上传配置
|
||||
func (s *sSysConfig) GetUpload(ctx context.Context) (conf *model.UploadConfig, err error) {
|
||||
models, err := s.GetConfigByGroup(ctx, &sysin.GetConfigInp{Group: "upload"})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = gconv.Scan(models.List, &conf)
|
||||
return
|
||||
}
|
||||
|
||||
// GetSmtp 获取邮件配置
|
||||
func (s *sSysConfig) GetSmtp(ctx context.Context) (conf *model.EmailConfig, err error) {
|
||||
models, err := s.GetConfigByGroup(ctx, &sysin.GetConfigInp{Group: "smtp"})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = gconv.Scan(models.List, &conf); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
conf.Addr = fmt.Sprintf("%s:%d", conf.Host, conf.Port)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetBasic 获取基础配置
|
||||
func (s *sSysConfig) GetBasic(ctx context.Context) (conf *model.BasicConfig, err error) {
|
||||
models, err := s.GetConfigByGroup(ctx, &sysin.GetConfigInp{Group: "basic"})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = gconv.Scan(models.List, &conf)
|
||||
return
|
||||
}
|
||||
|
||||
// GetLoadTCP 获取本地tcp配置
|
||||
func (s *sSysConfig) GetLoadTCP(ctx context.Context) (conf *model.TCPConfig, err error) {
|
||||
err = g.Cfg().MustGet(ctx, "tcp").Scan(&conf)
|
||||
return
|
||||
}
|
||||
|
||||
// GetLoadGenerate 获取本地生成配置
|
||||
func (s *sSysConfig) GetLoadGenerate(ctx context.Context) (conf *model.GenerateConfig, err error) {
|
||||
err = g.Cfg().MustGet(ctx, "hggen").Scan(&conf)
|
||||
return
|
||||
}
|
||||
|
||||
// GetLoadToken 获取本地token配置
|
||||
func (s *sSysConfig) GetLoadToken(ctx context.Context) (conf *model.TokenConfig, err error) {
|
||||
err = g.Cfg().MustGet(ctx, "token").Scan(&conf)
|
||||
return
|
||||
}
|
||||
|
||||
// GetLoadLog 获取本地日志配置
|
||||
func (s *sSysConfig) GetLoadLog(ctx context.Context) (conf *model.LogConfig, err error) {
|
||||
err = g.Cfg().MustGet(ctx, "system.log").Scan(&conf)
|
||||
return
|
||||
}
|
||||
|
||||
// GetLoadServeLog 获取本地服务日志配置
|
||||
func (s *sSysConfig) GetLoadServeLog(ctx context.Context) (conf *model.ServeLogConfig, err error) {
|
||||
err = g.Cfg().MustGet(ctx, "system.serveLog").Scan(&conf)
|
||||
return
|
||||
}
|
||||
|
||||
// GetConfigByGroup 获取指定分组的配置
|
||||
func (s *sSysConfig) GetConfigByGroup(ctx context.Context, in *sysin.GetConfigInp) (res *sysin.GetConfigModel, err error) {
|
||||
if in.Group == "" {
|
||||
err = gerror.New("分组不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
var models []*entity.SysConfig
|
||||
cols := dao.SysConfig.Columns()
|
||||
if err = dao.SysConfig.Ctx(ctx).Fields(cols.Key, cols.Value, cols.Type).Where(cols.Group, in.Group).Scan(&models); err != nil {
|
||||
err = gerror.Wrapf(err, "获取配置分组[ %v ]失败,请稍后重试!", in.Group)
|
||||
return
|
||||
}
|
||||
|
||||
res = new(sysin.GetConfigModel)
|
||||
if len(models) > 0 {
|
||||
res.List = make(g.Map, len(models))
|
||||
for _, v := range models {
|
||||
val, err := s.ConversionType(ctx, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.List[v.Key] = val
|
||||
}
|
||||
}
|
||||
|
||||
res.List = simple.FilterMaskDemo(ctx, res.List)
|
||||
return
|
||||
}
|
||||
|
||||
// ConversionType 转换类型
|
||||
func (s *sSysConfig) ConversionType(ctx context.Context, models *entity.SysConfig) (value interface{}, err error) {
|
||||
if models == nil {
|
||||
err = gerror.New("数据不存在")
|
||||
return
|
||||
}
|
||||
return consts.ConvType(models.Value, models.Type), nil
|
||||
}
|
||||
|
||||
// UpdateConfigByGroup 更新指定分组的配置
|
||||
func (s *sSysConfig) UpdateConfigByGroup(ctx context.Context, in *sysin.UpdateConfigInp) (err error) {
|
||||
if in.Group == "" {
|
||||
err = gerror.New("分组不能为空")
|
||||
return
|
||||
}
|
||||
var (
|
||||
mod = dao.SysConfig.Ctx(ctx)
|
||||
models []*entity.SysConfig
|
||||
)
|
||||
|
||||
if err = mod.Where("group", in.Group).Scan(&models); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = dao.SysConfig.Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
for k, v := range in.List {
|
||||
row := s.getConfigByKey(k, models)
|
||||
// 新增
|
||||
if row == nil {
|
||||
err = gerror.Newf("暂不支持从前台添加变量,请先在数据库表[%v]中配置变量:%v", dao.SysConfig.Table(), k)
|
||||
return
|
||||
}
|
||||
|
||||
// 更新
|
||||
_, err = dao.SysConfig.Ctx(ctx).Where("id", row.Id).Data(g.Map{"value": v, "updated_at": gtime.Now()}).Update()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return s.syncUpdate(ctx, in)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
global.PublishClusterSync(ctx, consts.ClusterSyncSysconfig, nil)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysConfig) getConfigByKey(key string, models []*entity.SysConfig) *entity.SysConfig {
|
||||
if len(models) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, v := range models {
|
||||
if key == v.Key {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// syncUpdate 同步更新一些加载配置
|
||||
func (s *sSysConfig) syncUpdate(ctx context.Context, in *sysin.UpdateConfigInp) (err error) {
|
||||
var cfg any
|
||||
switch in.Group {
|
||||
case "wechat":
|
||||
cfg, err = s.GetWechat(ctx)
|
||||
if err == nil {
|
||||
wechat.SetConfig(cfg.(*model.WechatConfig))
|
||||
}
|
||||
case "pay":
|
||||
cfg, err = s.GetPay(ctx)
|
||||
if err == nil {
|
||||
payment.SetConfig(cfg.(*model.PayConfig))
|
||||
}
|
||||
case "upload":
|
||||
cfg, err = s.GetUpload(ctx)
|
||||
if err == nil {
|
||||
storager.SetConfig(cfg.(*model.UploadConfig))
|
||||
}
|
||||
case "sms":
|
||||
cfg, err = s.GetSms(ctx)
|
||||
if err == nil {
|
||||
sms.SetConfig(cfg.(*model.SmsConfig))
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err = gerror.Newf("syncUpdate %v conifg fail:%+v", in.Group, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ClusterSync 集群同步
|
||||
func (s *sSysConfig) ClusterSync(ctx context.Context, message *gredis.Message) {
|
||||
if err := s.LoadConfig(ctx); err != nil {
|
||||
g.Log().Errorf(ctx, "ClusterSync fail:%+v", err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
// Package sys
|
||||
// @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 sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/cron"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/form"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
"hotgo/utility/simple"
|
||||
"hotgo/utility/validate"
|
||||
|
||||
"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/gctx"
|
||||
)
|
||||
|
||||
type sSysCron struct{}
|
||||
|
||||
func NewSysCron() *sSysCron {
|
||||
return &sSysCron{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysCron(NewSysCron())
|
||||
}
|
||||
|
||||
func (s *sSysCron) StartCron(ctx context.Context) {
|
||||
var list []*entity.SysCron
|
||||
if err := dao.SysCron.Ctx(ctx).
|
||||
Where("status", consts.StatusEnabled).
|
||||
Order("sort asc,id desc").
|
||||
Scan(&list); err != nil {
|
||||
cron.Logger().Fatalf(ctx, "定时任务获取失败, err . %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := cron.StartALL(list); err != nil {
|
||||
cron.Logger().Fatalf(ctx, "定时任务启动失败, err . %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete 删除
|
||||
func (s *sSysCron) Delete(ctx context.Context, in *sysin.CronDeleteInp) (err error) {
|
||||
var models *entity.SysCron
|
||||
if err = dao.SysCron.Ctx(ctx).Where("id", in.Id).Scan(&models); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return
|
||||
}
|
||||
|
||||
if models == nil {
|
||||
err = gerror.New("定时任务不存在或已被删除")
|
||||
return
|
||||
}
|
||||
|
||||
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
if _, err = dao.SysCron.Ctx(ctx).Where("id", in.Id).Delete(); err != nil {
|
||||
return
|
||||
}
|
||||
return cron.Delete(models)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增
|
||||
func (s *sSysCron) Edit(ctx context.Context, in *sysin.CronEditInp) (err error) {
|
||||
if in.Name == "" {
|
||||
err = gerror.New("标题不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
// 修改
|
||||
if in.Id > 0 {
|
||||
if _, err = dao.SysCron.Ctx(ctx).Where("id", in.Id).Data(in).Update(); err != nil {
|
||||
return
|
||||
}
|
||||
simple.SafeGo(ctx, func(ctx context.Context) {
|
||||
_ = cron.RefreshStatus(&in.SysCron)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 新增
|
||||
in.SysCron.Id, err = dao.SysCron.Ctx(ctx).Data(in).OmitEmptyData().InsertAndGetId()
|
||||
if err != nil || in.SysCron.Id < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
if in.SysCron.Status == consts.StatusEnabled {
|
||||
simple.SafeGo(ctx, func(ctx context.Context) {
|
||||
_ = cron.Start(&in.SysCron)
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Status 更新状态
|
||||
func (s *sSysCron) Status(ctx context.Context, in *sysin.CronStatusInp) (err error) {
|
||||
if in.Id <= 0 {
|
||||
err = gerror.New("ID不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
if in.Status <= 0 {
|
||||
err = gerror.New("状态不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
if !validate.InSlice(consts.StatusSlice, in.Status) {
|
||||
err = gerror.New("状态不正确")
|
||||
return
|
||||
}
|
||||
|
||||
var models *entity.SysCron
|
||||
if err = dao.SysCron.Ctx(ctx).Where("id", in.Id).Scan(&models); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if models == nil {
|
||||
err = gerror.New("定时任务不存在")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = dao.SysCron.Ctx(ctx).Where("id", in.Id).Data("status", in.Status).Update()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
models.Status = in.Status
|
||||
simple.SafeGo(ctx, func(ctx context.Context) {
|
||||
_ = cron.RefreshStatus(models)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// MaxSort 最大排序
|
||||
func (s *sSysCron) MaxSort(ctx context.Context, in *sysin.CronMaxSortInp) (res *sysin.CronMaxSortModel, err error) {
|
||||
if err = dao.SysCron.Ctx(ctx).Where("id", in.Id).Order("sort desc").Scan(&res); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if res == nil {
|
||||
res = new(sysin.CronMaxSortModel)
|
||||
}
|
||||
|
||||
res.Sort = form.DefaultMaxSort(res.Sort)
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取指定信息
|
||||
func (s *sSysCron) View(ctx context.Context, in *sysin.CronViewInp) (res *sysin.CronViewModel, err error) {
|
||||
if err = dao.SysCron.Ctx(ctx).Where("id", in.Id).Scan(&res); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// List 获取列表
|
||||
func (s *sSysCron) List(ctx context.Context, in *sysin.CronListInp) (list []*sysin.CronListModel, totalCount int, err error) {
|
||||
mod := dao.SysCron.Ctx(ctx)
|
||||
|
||||
if in.Name != "" {
|
||||
mod = mod.WhereLike("name", "%"+in.Name+"%")
|
||||
}
|
||||
|
||||
if in.GroupId > 0 {
|
||||
mod = mod.Where("group_id", in.GroupId)
|
||||
}
|
||||
|
||||
if in.Status > 0 {
|
||||
mod = mod.Where("status", in.Status)
|
||||
}
|
||||
|
||||
totalCount, err = mod.Count()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return
|
||||
}
|
||||
|
||||
if totalCount == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if err = mod.Page(in.Page, in.PerPage).Order("id desc").Scan(&list); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range list {
|
||||
v.GroupName, _ = s.GetName(ctx, v.GroupId)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetName 获取分组名称
|
||||
func (s *sSysCron) GetName(ctx context.Context, id int64) (name string, err error) {
|
||||
m := dao.SysCronGroup.Ctx(ctx).Fields(dao.SysCronGroup.Columns().Name).Where(dao.SysCronGroup.Columns().Id, id)
|
||||
list, err := m.Value()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return name, err
|
||||
}
|
||||
return list.String(), nil
|
||||
}
|
||||
|
||||
// OnlineExec 在线执行
|
||||
func (s *sSysCron) OnlineExec(ctx context.Context, in *sysin.OnlineExecInp) (err error) {
|
||||
var data *entity.SysCron
|
||||
if err = dao.SysCron.Ctx(ctx).Where(dao.SysCron.Columns().Id, in.Id).Scan(&data); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if data == nil {
|
||||
err = gerror.New("定时任务不存在")
|
||||
return
|
||||
}
|
||||
return cron.Once(gctx.New(), data)
|
||||
}
|
||||
|
||||
// DispatchLog 查看指定任务的调度日志
|
||||
func (s *sSysCron) DispatchLog(ctx context.Context, in *sysin.DispatchLogInp) (res *sysin.DispatchLogModel, err error) {
|
||||
var data *entity.SysCron
|
||||
if err = dao.SysCron.Ctx(ctx).Where(dao.SysCron.Columns().Id, in.Id).Scan(&data); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if data == nil {
|
||||
err = gerror.New("定时任务不存在")
|
||||
return
|
||||
}
|
||||
|
||||
res = new(sysin.DispatchLogModel)
|
||||
res.Log, err = cron.DispatchLog(data)
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
// Package sys
|
||||
// @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 sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/form"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
)
|
||||
|
||||
type sSysCronGroup struct{}
|
||||
|
||||
func NewSysCronGroup() *sSysCronGroup {
|
||||
return &sSysCronGroup{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysCronGroup(NewSysCronGroup())
|
||||
}
|
||||
|
||||
// Delete 删除
|
||||
func (s *sSysCronGroup) Delete(ctx context.Context, in *sysin.CronGroupDeleteInp) (err error) {
|
||||
_, err = dao.SysCronGroup.Ctx(ctx).Where("id", in.Id).Delete()
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增
|
||||
func (s *sSysCronGroup) Edit(ctx context.Context, in *sysin.CronGroupEditInp) (err error) {
|
||||
// 修改
|
||||
if in.Id > 0 {
|
||||
if _, err = dao.SysCronGroup.Ctx(ctx).Fields(sysin.CronGroupUpdateFields{}).Where("id", in.Id).Data(in).Update(); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 新增
|
||||
if _, err = dao.SysCronGroup.Ctx(ctx).Fields(sysin.CronGroupInsertFields{}).Data(in).OmitEmptyData().Insert(); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Status 更新状态
|
||||
func (s *sSysCronGroup) Status(ctx context.Context, in *sysin.CronGroupStatusInp) (err error) {
|
||||
if _, err = dao.SysCronGroup.Ctx(ctx).Where("id", in.Id).Data("status", in.Status).Update(); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MaxSort 最大排序
|
||||
func (s *sSysCronGroup) MaxSort(ctx context.Context, in *sysin.CronGroupMaxSortInp) (res *sysin.CronGroupMaxSortModel, err error) {
|
||||
if in.Id > 0 {
|
||||
if err = dao.SysCronGroup.Ctx(ctx).Where("id", in.Id).Order("sort desc").Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if res == nil {
|
||||
res = new(sysin.CronGroupMaxSortModel)
|
||||
}
|
||||
|
||||
res.Sort = form.DefaultMaxSort(res.Sort)
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取指定信息
|
||||
func (s *sSysCronGroup) View(ctx context.Context, in *sysin.CronGroupViewInp) (res *sysin.CronGroupViewModel, err error) {
|
||||
if err = dao.SysCronGroup.Ctx(ctx).Where("id", in.Id).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// List 获取列表
|
||||
func (s *sSysCronGroup) List(ctx context.Context, in *sysin.CronGroupListInp) (list []*sysin.CronGroupListModel, totalCount int, err error) {
|
||||
mod := dao.SysCronGroup.Ctx(ctx)
|
||||
|
||||
if in.Name != "" {
|
||||
mod = mod.WhereLike("name", "%"+in.Name+"%")
|
||||
}
|
||||
|
||||
if in.Status > 0 {
|
||||
mod = mod.Where("status", in.Status)
|
||||
}
|
||||
|
||||
totalCount, err = mod.Count()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return
|
||||
}
|
||||
|
||||
if totalCount == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if err = mod.Page(in.Page, in.PerPage).OrderDesc(dao.SysCronGroup.Columns().Id).Scan(&list); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
}
|
||||
|
||||
for _, v := range list {
|
||||
if v.Pid < 1 {
|
||||
continue
|
||||
}
|
||||
name, err := dao.SysCronGroup.Ctx(ctx).Fields(dao.SysCronGroup.Columns().Name).WherePri(v.Pid).Value()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
v.SupName = name.String()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Select 选项
|
||||
func (s *sSysCronGroup) Select(ctx context.Context, in *sysin.CronGroupSelectInp) (res *sysin.CronGroupSelectModel, err error) {
|
||||
var (
|
||||
mod = dao.SysCronGroup.Ctx(ctx)
|
||||
models []*entity.SysCronGroup
|
||||
)
|
||||
|
||||
if err = mod.Order("pid asc,sort asc").Scan(&models); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return
|
||||
}
|
||||
|
||||
res = new(sysin.CronGroupSelectModel)
|
||||
res.List = s.treeList(0, models)
|
||||
return
|
||||
}
|
||||
|
||||
// treeList 树状列表
|
||||
func (s *sSysCronGroup) treeList(pid int64, nodes []*entity.SysCronGroup) (list []*sysin.CronGroupTree) {
|
||||
list = make([]*sysin.CronGroupTree, 0)
|
||||
for _, v := range nodes {
|
||||
if v.Pid == pid {
|
||||
item := new(sysin.CronGroupTree)
|
||||
item.SysCronGroup = *v
|
||||
item.Label = v.Name
|
||||
item.Value = v.Id
|
||||
item.Key = v.Id
|
||||
|
||||
child := s.treeList(v.Id, nodes)
|
||||
if len(child) > 0 {
|
||||
item.Children = child
|
||||
}
|
||||
list = append(list, item)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
// Package sys
|
||||
// @Link https://github.com/bufanyun/hotgo
|
||||
// @Copyright Copyright (c) 2025 HotGo CLI
|
||||
// @Author Ms <133814250@qq.com>
|
||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||
// @AutoGenerate Version 2.18.6
|
||||
package sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/contexts"
|
||||
"hotgo/internal/library/hgorm"
|
||||
"hotgo/internal/library/hgorm/handler"
|
||||
"hotgo/internal/library/hgorm/hook"
|
||||
"hotgo/internal/model/input/form"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
"hotgo/utility/convert"
|
||||
"hotgo/utility/excel"
|
||||
"hotgo/utility/validate"
|
||||
|
||||
"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/gctx"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type sSysCurdDemo struct{}
|
||||
|
||||
func NewSysCurdDemo() *sSysCurdDemo {
|
||||
return &sSysCurdDemo{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysCurdDemo(NewSysCurdDemo())
|
||||
}
|
||||
|
||||
// Model CURD列表ORM模型
|
||||
func (s *sSysCurdDemo) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
||||
return handler.Model(dao.SysGenCurdDemo.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 获取CURD列表列表
|
||||
func (s *sSysCurdDemo) List(ctx context.Context, in *sysin.CurdDemoListInp) (list []*sysin.CurdDemoListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx)
|
||||
|
||||
// 字段过滤
|
||||
mod = mod.FieldsPrefix(dao.SysGenCurdDemo.Table(), sysin.CurdDemoListModel{})
|
||||
mod = mod.Fields(hgorm.JoinFields(ctx, sysin.CurdDemoListModel{}, &dao.TestCategory, "testCategory"))
|
||||
|
||||
// 关联表字段
|
||||
mod = mod.LeftJoinOnFields(dao.TestCategory.Table(), dao.SysGenCurdDemo.Columns().CategoryId, "=", dao.TestCategory.Columns().Id)
|
||||
|
||||
// 查询ID
|
||||
if in.Id > 0 {
|
||||
mod = mod.Where(dao.SysGenCurdDemo.Columns().Id, in.Id)
|
||||
}
|
||||
|
||||
// 查询标题
|
||||
if in.Title != "" {
|
||||
mod = mod.WhereLike(dao.SysGenCurdDemo.Columns().Title, "%"+in.Title+"%")
|
||||
}
|
||||
|
||||
// 查询描述
|
||||
if in.Description != "" {
|
||||
mod = mod.WhereLike(dao.SysGenCurdDemo.Columns().Description, "%"+in.Description+"%")
|
||||
}
|
||||
|
||||
// 查询创建者
|
||||
if in.CreatedBy != "" {
|
||||
ids, err := service.AdminMember().GetIdsByKeyword(ctx, in.CreatedBy)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
mod = mod.WhereIn(dao.SysGenCurdDemo.Columns().CreatedBy, ids)
|
||||
}
|
||||
|
||||
// 查询删除者
|
||||
if in.DeletedBy != "" {
|
||||
ids, err := service.AdminMember().GetIdsByKeyword(ctx, in.DeletedBy)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
mod = mod.WhereIn(dao.SysGenCurdDemo.Columns().DeletedBy, ids)
|
||||
}
|
||||
|
||||
// 查询创建时间
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween(dao.SysGenCurdDemo.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
// 查询关联分类
|
||||
if in.TestCategoryName != "" {
|
||||
mod = mod.WherePrefix(dao.TestCategory.Table(), dao.TestCategory.Columns().Name, in.TestCategoryName)
|
||||
}
|
||||
|
||||
// 分页
|
||||
mod = mod.Page(in.Page, in.PerPage)
|
||||
|
||||
// 排序
|
||||
mod = mod.OrderAsc(dao.SysGenCurdDemo.Table() + "." + dao.SysGenCurdDemo.Columns().Sort).OrderDesc(dao.SysGenCurdDemo.Table() + "." + dao.SysGenCurdDemo.Columns().Id)
|
||||
|
||||
// 操作人摘要信息
|
||||
mod = mod.Hook(hook.MemberSummary)
|
||||
|
||||
// 查询数据
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取CURD列表列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Export 导出CURD列表
|
||||
func (s *sSysCurdDemo) Export(ctx context.Context, in *sysin.CurdDemoListInp) (err error) {
|
||||
list, totalCount, err := s.List(ctx, in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
|
||||
tags, err := convert.GetEntityDescTags(sysin.CurdDemoExportModel{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
fileName = "导出CURD列表-" + gctx.CtxId(ctx)
|
||||
sheetName = fmt.Sprintf("索引条件共%v行,共%v页,当前导出是第%v页,本页共%v行", totalCount, form.CalPageCount(totalCount, in.PerPage), in.Page, len(list))
|
||||
exports []sysin.CurdDemoExportModel
|
||||
)
|
||||
|
||||
if err = gconv.Scan(list, &exports); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增CURD列表
|
||||
func (s *sSysCurdDemo) Edit(ctx context.Context, in *sysin.CurdDemoEditInp) (err error) {
|
||||
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
|
||||
// 修改
|
||||
if in.Id > 0 {
|
||||
in.UpdatedBy = contexts.GetUserId(ctx)
|
||||
if _, err = s.Model(ctx).
|
||||
Fields(sysin.CurdDemoUpdateFields{}).
|
||||
WherePri(in.Id).Data(in).Update(); err != nil {
|
||||
err = gerror.Wrap(err, "修改CURD列表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 新增
|
||||
in.CreatedBy = contexts.GetUserId(ctx)
|
||||
if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).
|
||||
Fields(sysin.CurdDemoInsertFields{}).
|
||||
Data(in).OmitEmptyData().Insert(); err != nil {
|
||||
err = gerror.Wrap(err, "新增CURD列表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除CURD列表
|
||||
func (s *sSysCurdDemo) Delete(ctx context.Context, in *sysin.CurdDemoDeleteInp) (err error) {
|
||||
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Data(g.Map{
|
||||
dao.SysGenCurdDemo.Columns().DeletedBy: contexts.GetUserId(ctx),
|
||||
dao.SysGenCurdDemo.Columns().DeletedAt: gtime.Now(),
|
||||
}).Unscoped().Update(); err != nil {
|
||||
err = gerror.Wrap(err, "删除CURD列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MaxSort 获取CURD列表最大排序
|
||||
func (s *sSysCurdDemo) MaxSort(ctx context.Context, in *sysin.CurdDemoMaxSortInp) (res *sysin.CurdDemoMaxSortModel, err error) {
|
||||
if err = dao.SysGenCurdDemo.Ctx(ctx).Fields(dao.SysGenCurdDemo.Columns().Sort).OrderDesc(dao.SysGenCurdDemo.Columns().Sort).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取CURD列表最大排序,请稍后重试!")
|
||||
return
|
||||
}
|
||||
|
||||
if res == nil {
|
||||
res = new(sysin.CurdDemoMaxSortModel)
|
||||
}
|
||||
|
||||
res.Sort = form.DefaultMaxSort(res.Sort)
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取CURD列表指定信息
|
||||
func (s *sSysCurdDemo) View(ctx context.Context, in *sysin.CurdDemoViewInp) (res *sysin.CurdDemoViewModel, err error) {
|
||||
if err = s.Model(ctx).WherePri(in.Id).Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取CURD列表信息,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Status 更新CURD列表状态
|
||||
func (s *sSysCurdDemo) Status(ctx context.Context, in *sysin.CurdDemoStatusInp) (err error) {
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Data(g.Map{
|
||||
dao.SysGenCurdDemo.Columns().Status: in.Status,
|
||||
dao.SysGenCurdDemo.Columns().UpdatedBy: contexts.GetUserId(ctx),
|
||||
}).Update(); err != nil {
|
||||
err = gerror.Wrap(err, "更新CURD列表状态失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Switch 更新CURD列表开关
|
||||
func (s *sSysCurdDemo) Switch(ctx context.Context, in *sysin.CurdDemoSwitchInp) (err error) {
|
||||
var fields = []string{
|
||||
dao.SysGenCurdDemo.Columns().Switch,
|
||||
// ...
|
||||
}
|
||||
|
||||
if !validate.InSlice(fields, in.Key) {
|
||||
err = gerror.New("开关键名不在白名单")
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Data(g.Map{
|
||||
in.Key: in.Value,
|
||||
dao.SysGenCurdDemo.Columns().UpdatedBy: contexts.GetUserId(ctx),
|
||||
}).Update(); err != nil {
|
||||
err = gerror.Wrap(err, "更新CURD列表开关失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
// Package sys
|
||||
// @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 sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/dict"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
type sSysDictData struct{}
|
||||
|
||||
func NewSysDictData() *sSysDictData {
|
||||
return &sSysDictData{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysDictData(NewSysDictData())
|
||||
}
|
||||
|
||||
// Delete 删除
|
||||
func (s *sSysDictData) Delete(ctx context.Context, in *sysin.DictDataDeleteInp) error {
|
||||
_, err := dao.SysDictData.Ctx(ctx).Where("id", in.Id).Delete()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Edit 修改/新增
|
||||
func (s *sSysDictData) Edit(ctx context.Context, in *sysin.DictDataEditInp) (err error) {
|
||||
// 修改
|
||||
if in.Id > 0 {
|
||||
_, err = dao.SysDictData.Ctx(ctx).Fields(sysin.DictDataUpdateFields{}).WherePri(in.Id).Data(in).Update()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 新增
|
||||
in.Type, err = s.GetType(ctx, in.TypeID)
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
}
|
||||
|
||||
if in.Type == "" {
|
||||
return gerror.Wrap(err, "类型选择无效,请检查")
|
||||
}
|
||||
|
||||
_, err = dao.SysDictData.Ctx(ctx).Fields(sysin.DictDataInsertFields{}).Data(in).OmitEmptyData().Insert()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// List 获取列表
|
||||
func (s *sSysDictData) List(ctx context.Context, in *sysin.DictDataListInp) (list []*sysin.DictDataListModel, totalCount int, err error) {
|
||||
mod := dao.SysDictData.Ctx(ctx)
|
||||
// 类型ID
|
||||
if in.TypeID > 0 {
|
||||
types, err := s.GetTypes(ctx, in.TypeID)
|
||||
if err != nil {
|
||||
return list, totalCount, err
|
||||
}
|
||||
mod = mod.WhereIn("type", types)
|
||||
}
|
||||
|
||||
if in.Type != "" {
|
||||
mod = mod.Where("type", in.Type)
|
||||
}
|
||||
|
||||
if in.Value != "" {
|
||||
mod = mod.Where("value", in.Value)
|
||||
}
|
||||
|
||||
if in.Label != "" {
|
||||
mod = mod.WhereLike("label", "%"+in.Label+"%")
|
||||
}
|
||||
|
||||
if in.Status > 0 {
|
||||
mod = mod.Where("status", in.Status)
|
||||
}
|
||||
|
||||
totalCount, err = mod.Count()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return list, totalCount, err
|
||||
}
|
||||
|
||||
if totalCount == 0 {
|
||||
return list, totalCount, nil
|
||||
}
|
||||
|
||||
if err = mod.Page(in.Page, in.PerPage).Order("sort asc,id desc").Scan(&list); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return list, totalCount, err
|
||||
}
|
||||
|
||||
for _, v := range list {
|
||||
v.TypeID, _ = s.GetId(ctx, v.Type)
|
||||
}
|
||||
return list, totalCount, err
|
||||
}
|
||||
|
||||
// GetId 获取指定类型的ID
|
||||
func (s *sSysDictData) GetId(ctx context.Context, t string) (id int64, err error) {
|
||||
m := dao.SysDictType.Ctx(ctx).Fields(dao.SysDictType.Columns().Id).Where(dao.SysDictType.Columns().Type, t).Where(dao.SysDictType.Columns().Status, consts.StatusEnabled)
|
||||
val, err := m.Value()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return 0, err
|
||||
}
|
||||
return val.Int64(), nil
|
||||
}
|
||||
|
||||
// GetType 获取指定ID的类型标识
|
||||
func (s *sSysDictData) GetType(ctx context.Context, id int64) (types string, err error) {
|
||||
m := dao.SysDictType.Ctx(ctx).Fields(dao.SysDictType.Columns().Type).Where(dao.SysDictType.Columns().Id, id).Where(dao.SysDictType.Columns().Status, consts.StatusEnabled)
|
||||
val, err := m.Value()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return types, err
|
||||
}
|
||||
return val.String(), nil
|
||||
}
|
||||
|
||||
// GetTypes 获取指定ID的所有类型标识,包含下级
|
||||
func (s *sSysDictData) GetTypes(ctx context.Context, id int64) (types []string, err error) {
|
||||
columns, err := dao.SysDictType.Ctx(ctx).Fields(dao.SysDictType.Columns().Type).
|
||||
Where(dao.SysDictType.Columns().Id, id).WhereOr(dao.SysDictType.Columns().Pid, id).Where(dao.SysDictType.Columns().Status, consts.StatusEnabled).
|
||||
Array()
|
||||
types = g.NewVar(columns).Strings()
|
||||
return
|
||||
}
|
||||
|
||||
// Select 获取列表
|
||||
func (s *sSysDictData) Select(ctx context.Context, in *sysin.DataSelectInp) (list sysin.DataSelectModel, err error) {
|
||||
// 优先从内置字典中获取
|
||||
options, err := dict.GetOptions(ctx, in.Type)
|
||||
if err == nil {
|
||||
return options, nil
|
||||
}
|
||||
if !errors.Is(err, dict.NotExistKeyError) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mod := dao.SysDictData.Ctx(ctx).Where("type", in.Type)
|
||||
if err = mod.Order("sort asc,id desc").Scan(&list); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range list {
|
||||
list[k].Value = consts.ConvType(v.Value, v.ValueType)
|
||||
list[k].Key = list[k].Value
|
||||
}
|
||||
|
||||
if len(list) == 0 {
|
||||
list = make(sysin.DataSelectModel, 0)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
// Package sys
|
||||
// @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 sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/dict"
|
||||
"hotgo/internal/library/hgorm"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
)
|
||||
|
||||
type sSysDictType struct{}
|
||||
|
||||
func NewSysDictType() *sSysDictType {
|
||||
return &sSysDictType{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysDictType(NewSysDictType())
|
||||
}
|
||||
|
||||
// Tree 树
|
||||
func (s *sSysDictType) Tree(ctx context.Context) (list []*sysin.DictTypeTree, err error) {
|
||||
var (
|
||||
mod = dao.SysDictType.Ctx(ctx)
|
||||
models []*entity.SysDictType
|
||||
)
|
||||
|
||||
if err = mod.Order("sort asc,id asc").Scan(&models); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return
|
||||
}
|
||||
|
||||
list = s.treeList(0, models)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete 删除
|
||||
func (s *sSysDictType) Delete(ctx context.Context, in *sysin.DictTypeDeleteInp) (err error) {
|
||||
var models *entity.SysDictType
|
||||
if err = dao.SysDictType.Ctx(ctx).Where("id", in.Id).Scan(&models); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if models == nil {
|
||||
err = gerror.New("数据不存在或已删除!")
|
||||
return
|
||||
}
|
||||
|
||||
exist, err := dao.SysDictData.Ctx(ctx).Where("type", models.Type).One()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
}
|
||||
if !exist.IsEmpty() {
|
||||
err = gerror.New("请先删除该字典类型下得所有字典数据!")
|
||||
return
|
||||
}
|
||||
|
||||
pidExist, err := dao.SysDictType.Ctx(ctx).Where("pid", models.Id).One()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
}
|
||||
|
||||
if !pidExist.IsEmpty() {
|
||||
err = gerror.New("请先删除该字典类型下得所有子级类型!")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = dao.SysDictType.Ctx(ctx).Where("id", in.Id).Delete()
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增
|
||||
func (s *sSysDictType) Edit(ctx context.Context, in *sysin.DictTypeEditInp) (err error) {
|
||||
if err = hgorm.IsUnique(ctx, &dao.SysDictType, g.Map{dao.SysDictType.Columns().Name: in.Name}, "名称已存在", in.Id); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 修改
|
||||
if in.Id > 0 {
|
||||
if _, err = dao.SysDictType.Ctx(ctx).Fields(sysin.DictTypeUpdateFields{}).WherePri(in.Id).Data(in).Update(); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 新增
|
||||
if _, err = dao.SysDictType.Ctx(ctx).Fields(sysin.DictTypeInsertFields{}).Data(in).OmitEmptyData().Insert(); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TreeSelect 获取类型关系树选项
|
||||
func (s *sSysDictType) TreeSelect(ctx context.Context, in *sysin.DictTreeSelectInp) (list []*sysin.DictTypeTree, err error) {
|
||||
var (
|
||||
mod = dao.SysDictType.Ctx(ctx)
|
||||
models []*entity.SysDictType
|
||||
)
|
||||
|
||||
if err = mod.Order("pid asc,sort asc").Scan(&models); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return
|
||||
}
|
||||
|
||||
list = s.treeList(0, models)
|
||||
list = append(list, s.BuiltinSelect()...)
|
||||
return
|
||||
}
|
||||
|
||||
// BuiltinSelect 内置字典选项
|
||||
func (s *sSysDictType) BuiltinSelect() (list []*sysin.DictTypeTree) {
|
||||
top := &sysin.DictTypeTree{
|
||||
SysDictType: entity.SysDictType{
|
||||
Id: dict.BuiltinId,
|
||||
Pid: 0,
|
||||
},
|
||||
Label: "内置字典",
|
||||
Value: dict.BuiltinId,
|
||||
Key: dict.BuiltinId,
|
||||
}
|
||||
|
||||
enums := &sysin.DictTypeTree{
|
||||
SysDictType: entity.SysDictType{
|
||||
Id: dict.EnumsId,
|
||||
Pid: dict.BuiltinId,
|
||||
},
|
||||
Label: "枚举字典",
|
||||
Value: dict.EnumsId,
|
||||
Key: dict.EnumsId,
|
||||
}
|
||||
|
||||
for _, v := range dict.GetAllEnums() {
|
||||
children := &sysin.DictTypeTree{
|
||||
SysDictType: entity.SysDictType{
|
||||
Id: v.Id,
|
||||
Pid: dict.EnumsId,
|
||||
},
|
||||
Label: v.Label,
|
||||
Value: v.Id,
|
||||
Key: v.Id,
|
||||
}
|
||||
enums.Children = append(enums.Children, children)
|
||||
}
|
||||
|
||||
fun := &sysin.DictTypeTree{
|
||||
SysDictType: entity.SysDictType{
|
||||
Id: dict.FuncId,
|
||||
Pid: dict.BuiltinId,
|
||||
},
|
||||
Label: "方法字典",
|
||||
Value: dict.FuncId,
|
||||
Key: dict.FuncId,
|
||||
}
|
||||
|
||||
for _, v := range dict.GetAllFunc() {
|
||||
children := &sysin.DictTypeTree{
|
||||
SysDictType: entity.SysDictType{
|
||||
Id: v.Id,
|
||||
Pid: dict.FuncId,
|
||||
},
|
||||
Label: v.Label,
|
||||
Value: v.Id,
|
||||
Key: v.Id,
|
||||
}
|
||||
fun.Children = append(fun.Children, children)
|
||||
}
|
||||
|
||||
top.Children = append(top.Children, enums, fun)
|
||||
list = append(list, top)
|
||||
return
|
||||
}
|
||||
|
||||
// treeList 树状列表
|
||||
func (s *sSysDictType) treeList(pid int64, nodes []*entity.SysDictType) (list []*sysin.DictTypeTree) {
|
||||
list = make([]*sysin.DictTypeTree, 0)
|
||||
for _, v := range nodes {
|
||||
if v.Pid == pid {
|
||||
item := new(sysin.DictTypeTree)
|
||||
item.SysDictType = *v
|
||||
item.Label = v.Name
|
||||
item.Value = v.Id
|
||||
item.Key = v.Id
|
||||
|
||||
child := s.treeList(v.Id, nodes)
|
||||
if len(child) > 0 {
|
||||
item.Children = child
|
||||
}
|
||||
list = append(list, item)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,430 @@
|
||||
// Package sys
|
||||
// @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 sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/contexts"
|
||||
"hotgo/internal/library/ems"
|
||||
"hotgo/internal/library/location"
|
||||
"hotgo/internal/model"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
"hotgo/utility/charset"
|
||||
"hotgo/utility/simple"
|
||||
"hotgo/utility/useragent"
|
||||
"hotgo/utility/validate"
|
||||
"time"
|
||||
|
||||
"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/gtime"
|
||||
"github.com/gogf/gf/v2/os/gview"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
)
|
||||
|
||||
type sSysEmsLog struct{}
|
||||
|
||||
func NewSysEmsLog() *sSysEmsLog {
|
||||
return &sSysEmsLog{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysEmsLog(NewSysEmsLog())
|
||||
}
|
||||
|
||||
// Delete 删除
|
||||
func (s *sSysEmsLog) Delete(ctx context.Context, in *sysin.EmsLogDeleteInp) (err error) {
|
||||
_, err = dao.SysEmsLog.Ctx(ctx).Where("id", in.Id).Delete()
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增
|
||||
func (s *sSysEmsLog) Edit(ctx context.Context, in *sysin.EmsLogEditInp) (err error) {
|
||||
if in.Ip == "" {
|
||||
err = gerror.New("ip不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
// 修改
|
||||
if in.Id > 0 {
|
||||
_, err = dao.SysEmsLog.Ctx(ctx).Where("id", in.Id).Data(in).Update()
|
||||
return
|
||||
}
|
||||
|
||||
// 新增
|
||||
_, err = dao.SysEmsLog.Ctx(ctx).Data(in).OmitEmptyData().Insert()
|
||||
return
|
||||
}
|
||||
|
||||
// Status 更新部门状态
|
||||
func (s *sSysEmsLog) Status(ctx context.Context, in *sysin.EmsLogStatusInp) (err error) {
|
||||
if in.Id <= 0 {
|
||||
err = gerror.New("ID不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
if in.Status <= 0 {
|
||||
err = gerror.New("状态不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
if !validate.InSlice(consts.StatusSlice, in.Status) {
|
||||
err = gerror.New("状态不正确")
|
||||
return
|
||||
}
|
||||
|
||||
// 修改
|
||||
_, err = dao.SysEmsLog.Ctx(ctx).Where("id", in.Id).Data("status", in.Status).Update()
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取指定字典类型信息
|
||||
func (s *sSysEmsLog) View(ctx context.Context, in *sysin.EmsLogViewInp) (res *sysin.EmsLogViewModel, err error) {
|
||||
err = dao.SysEmsLog.Ctx(ctx).Where("id", in.Id).Scan(&res)
|
||||
return
|
||||
}
|
||||
|
||||
// List 获取列表
|
||||
func (s *sSysEmsLog) List(ctx context.Context, in *sysin.EmsLogListInp) (list []*sysin.EmsLogListModel, totalCount int, err error) {
|
||||
mod := dao.SysEmsLog.Ctx(ctx)
|
||||
|
||||
if in.Status > 0 {
|
||||
mod = mod.Where("status", in.Status)
|
||||
}
|
||||
|
||||
totalCount, err = mod.Count()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return
|
||||
}
|
||||
|
||||
if totalCount == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
err = mod.Page(in.Page, in.PerPage).Order("id desc").Scan(&list)
|
||||
return
|
||||
}
|
||||
|
||||
// Send 发送邮件
|
||||
func (s *sSysEmsLog) Send(ctx context.Context, in *sysin.SendEmsInp) (err error) {
|
||||
var models *entity.SysEmsLog
|
||||
if err = dao.SysEmsLog.Ctx(ctx).
|
||||
Where("event", in.Event).
|
||||
Where("email", in.Email).
|
||||
OrderDesc(dao.SysEmsLog.Columns().Id).
|
||||
Scan(&models); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return
|
||||
}
|
||||
|
||||
config, err := service.SysConfig().GetSmtp(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
in.Template, err = s.GetTemplate(ctx, in.Event, config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.AllowSend(ctx, models, config); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if consts.IsCodeEmsTemplate(in.Event) && in.Code == "" {
|
||||
in.Code = grand.Digits(4)
|
||||
}
|
||||
|
||||
view, err := s.newView(ctx, in, config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if in.TplData == nil {
|
||||
in.TplData = make(g.Map)
|
||||
}
|
||||
|
||||
switch in.Event {
|
||||
// 富文本
|
||||
case consts.EmsTemplateText:
|
||||
if in.Content == "" {
|
||||
err = gerror.New("富文本类型邮件内容不能为空")
|
||||
return
|
||||
}
|
||||
in.TplData["content"] = in.Content
|
||||
in.Content, err = view.Parse(ctx, in.Template, in.TplData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 验证码、重置密码类
|
||||
default:
|
||||
in.Content, err = view.Parse(ctx, in.Template, in.TplData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
subject, ok := consts.EmsSubjectMap[in.Event]
|
||||
if !ok {
|
||||
subject = simple.AppName(ctx)
|
||||
}
|
||||
|
||||
err = ems.Send(config, in.Email, subject, in.Content)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var data = new(entity.SysEmsLog)
|
||||
data.Event = in.Event
|
||||
data.Email = in.Email
|
||||
data.Content = in.Content
|
||||
data.Code = in.Code
|
||||
data.Ip = location.GetClientIp(ghttp.RequestFromCtx(ctx))
|
||||
data.Status = consts.CodeStatusNotUsed
|
||||
data.CreatedAt = gtime.Now()
|
||||
data.UpdatedAt = gtime.Now()
|
||||
|
||||
_, err = dao.SysEmsLog.Ctx(ctx).Data(data).OmitEmptyData().Insert()
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sSysEmsLog) newView(ctx context.Context, in *sysin.SendEmsInp, config *model.EmailConfig) (view *gview.View, err error) {
|
||||
view = gview.New()
|
||||
err = view.SetConfig(gview.Config{
|
||||
Delimiters: g.Cfg().MustGet(ctx, "viewer.delimiters").Strings(),
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 富文本为自定义内容,可能不需要变量
|
||||
if in.Event == consts.EmsTemplateText {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
username string
|
||||
user = contexts.GetUser(ctx)
|
||||
request = ghttp.RequestFromCtx(ctx)
|
||||
ip = location.GetClientIp(request)
|
||||
)
|
||||
|
||||
loc, err := location.GetLocation(ctx, ip)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if loc == nil {
|
||||
loc = new(location.IpLocationData)
|
||||
}
|
||||
|
||||
cityLabel, err := location.ParseRegion(ctx, loc.ProvinceCode, loc.CityCode, 0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
basic, err := service.SysConfig().GetBasic(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if basic == nil {
|
||||
basic = new(model.BasicConfig)
|
||||
basic.Name = simple.AppName(ctx)
|
||||
basic.Domain = "https://hotgo.facms.cn"
|
||||
basic.Logo = "https://gmycos.facms.cn/haoka/attachment/images/2023-02-04/cq9kf7s66jt7hkpvbh.png"
|
||||
basic.SystemOpen = true
|
||||
}
|
||||
|
||||
if user != nil {
|
||||
username = user.Username
|
||||
}
|
||||
|
||||
// 公共变量
|
||||
view.Assigns(gview.Params{
|
||||
"code": in.Code, // 验证码
|
||||
"expires": config.CodeExpire / 60, // 验证码有效期(分钟)
|
||||
"username": username, // 发送者用户名
|
||||
"name": basic.Name, // 网站名称
|
||||
"logo": basic.Logo, // 网站logo
|
||||
"domain": basic.Domain, // 网站域名
|
||||
"github": "https://github.com/bufanyun/hotgo", // github
|
||||
"os": useragent.GetOs(request.Header.Get("User-Agent")), // 发送者操作系统
|
||||
"ip": gstr.HideStr(ip, 30, `*`), // 发送者IP
|
||||
"cityLabel": cityLabel, // IP归属地
|
||||
})
|
||||
|
||||
// 重置密码
|
||||
if in.Event == consts.EmsTemplateResetPwd {
|
||||
var (
|
||||
passwordResetLink string
|
||||
resetToken = charset.RandomCreateBytes(32)
|
||||
)
|
||||
if user != nil {
|
||||
switch user.App {
|
||||
// 后台用户
|
||||
case consts.AppAdmin:
|
||||
_, err = dao.AdminMember.Ctx(ctx).Where(dao.AdminMember.Columns().Id, user.Id).Data(g.Map{dao.AdminMember.Columns().PasswordResetToken: resetToken}).Update()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
passwordResetLink = fmt.Sprintf("%s/admin/passwordReset?token=%s", basic.Domain, resetToken)
|
||||
// 前台用户
|
||||
case consts.AppApi:
|
||||
// ...
|
||||
}
|
||||
}
|
||||
view.Assign("passwordResetLink", passwordResetLink)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetTemplate 获取指定邮件模板
|
||||
func (s *sSysEmsLog) GetTemplate(ctx context.Context, template string, config *model.EmailConfig) (val string, err error) {
|
||||
if template == "" {
|
||||
err = gerror.New("模板不能为空")
|
||||
return
|
||||
}
|
||||
if config == nil {
|
||||
config, err = service.SysConfig().GetSmtp(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(config.Template) == 0 {
|
||||
err = gerror.New("管理员还没有配置任何模板!")
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range config.Template {
|
||||
if v.Key == template {
|
||||
return v.Value, nil
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AllowSend 是否允许发送
|
||||
func (s *sSysEmsLog) AllowSend(ctx context.Context, models *entity.SysEmsLog, config *model.EmailConfig) (err error) {
|
||||
if models == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 富文本事件不限制
|
||||
if models.Event == consts.EmsTemplateText {
|
||||
return
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
config, err = service.SysConfig().GetSmtp(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if gtime.Now().Before(models.CreatedAt.Add(time.Second * time.Duration(config.MinInterval))) {
|
||||
err = gerror.New("发送频繁,请稍后再试!")
|
||||
return
|
||||
}
|
||||
|
||||
if config.MaxIpLimit > 0 {
|
||||
count, err := s.NowDayIpSendCount(ctx, models.Event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if count >= config.MaxIpLimit {
|
||||
err = gerror.New("今天发送短信过多,请次日后再试!")
|
||||
return err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NowDayIpSendCount 当天IP累计发送次数
|
||||
func (s *sSysEmsLog) NowDayIpSendCount(ctx context.Context, event string) (count int, err error) {
|
||||
return dao.SysEmsLog.Ctx(ctx).
|
||||
Where("ip", location.GetClientIp(ghttp.RequestFromCtx(ctx))).
|
||||
Where("event", event).
|
||||
WhereGTE("created_at", gtime.Now().Format("Y-m-d")).
|
||||
Count()
|
||||
}
|
||||
|
||||
// VerifyCode 效验验证码
|
||||
func (s *sSysEmsLog) VerifyCode(ctx context.Context, in *sysin.VerifyEmsCodeInp) (err error) {
|
||||
if in.Event == "" {
|
||||
err = gerror.New("事件不能为空")
|
||||
return
|
||||
}
|
||||
if in.Email == "" {
|
||||
err = gerror.New("邮箱不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
if in.Event == consts.EmsTemplateResetPwd || in.Event == consts.EmsTemplateText {
|
||||
err = gerror.Newf("事件类型无需验证:%v", in.Event)
|
||||
return
|
||||
}
|
||||
|
||||
config, err := service.SysConfig().GetSmtp(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var models *entity.SysEmsLog
|
||||
if err = dao.SysEmsLog.Ctx(ctx).
|
||||
Where(dao.SysEmsLog.Columns().Event, in.Event).
|
||||
Where(dao.SysEmsLog.Columns().Email, in.Email).
|
||||
OrderDesc(dao.SysEmsLog.Columns().Id).
|
||||
Scan(&models); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
}
|
||||
|
||||
if models == nil {
|
||||
err = gerror.New("验证码错误")
|
||||
return
|
||||
}
|
||||
|
||||
if models.Times >= 10 {
|
||||
err = gerror.New("验证码错误次数过多,请重新发送!")
|
||||
return
|
||||
}
|
||||
|
||||
if in.Event != consts.EmsTemplateCode {
|
||||
if models.Status == consts.CodeStatusUsed {
|
||||
err = gerror.New("验证码已使用,请重新发送!")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if gtime.Now().After(models.CreatedAt.Add(time.Second * time.Duration(config.CodeExpire))) {
|
||||
err = gerror.New("验证码已过期,请重新发送")
|
||||
return
|
||||
}
|
||||
|
||||
if models.Code != in.Code {
|
||||
_, _ = dao.SysEmsLog.Ctx(ctx).Where(dao.SysEmsLog.Columns().Id, models.Id).Increment(dao.SysEmsLog.Columns().Times, 1)
|
||||
err = gerror.New("验证码错误!")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = dao.SysEmsLog.Ctx(ctx).Where(dao.SysEmsLog.Columns().Id, models.Id).Data(g.Map{
|
||||
dao.SysEmsLog.Columns().Times: models.Times + 1,
|
||||
dao.SysEmsLog.Columns().Status: consts.CodeStatusUsed,
|
||||
dao.SysEmsLog.Columns().UpdatedAt: gtime.Now(),
|
||||
}).Update()
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
// Package sys
|
||||
// @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 sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/hggen"
|
||||
"hotgo/internal/model"
|
||||
"hotgo/internal/model/input/form"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
"hotgo/utility/validate"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
type sSysGenCodes struct{}
|
||||
|
||||
func NewSysGenCodes() *sSysGenCodes {
|
||||
return &sSysGenCodes{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysGenCodes(NewSysGenCodes())
|
||||
}
|
||||
|
||||
// Delete 删除
|
||||
func (s *sSysGenCodes) Delete(ctx context.Context, in *sysin.GenCodesDeleteInp) (err error) {
|
||||
_, err = dao.SysGenCodes.Ctx(ctx).Where("id", in.Id).Delete()
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增
|
||||
func (s *sSysGenCodes) Edit(ctx context.Context, in *sysin.GenCodesEditInp) (res *sysin.GenCodesEditModel, err error) {
|
||||
if in.GenType == 0 {
|
||||
err = gerror.New("生成类型不能为空")
|
||||
return
|
||||
}
|
||||
if in.VarName == "" {
|
||||
err = gerror.New("实体名称不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
if in.GenType == consts.GenCodesTypeCurd {
|
||||
var temp *model.GenerateAppCrudTemplate
|
||||
cfg := fmt.Sprintf("hggen.application.crud.templates.%v", in.GenTemplate)
|
||||
if err = g.Cfg().MustGet(ctx, cfg).Scan(&temp); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if temp == nil {
|
||||
err = gerror.Newf("选择的模板不存在:%v", cfg)
|
||||
return
|
||||
}
|
||||
|
||||
if temp.IsAddon && in.AddonName == "" {
|
||||
err = gerror.New("插件模板必须选择一个有效的插件")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 修改
|
||||
in.UpdatedAt = gtime.Now()
|
||||
if in.Id > 0 {
|
||||
_, err = dao.SysGenCodes.Ctx(ctx).Where("id", in.Id).Data(in).Update()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return
|
||||
}
|
||||
return &sysin.GenCodesEditModel{SysGenCodes: in.SysGenCodes}, nil
|
||||
}
|
||||
|
||||
// 新增
|
||||
if in.Options.IsNil() {
|
||||
in.Options = gjson.New(consts.NilJsonToString)
|
||||
}
|
||||
|
||||
in.MasterColumns = gjson.New("[]")
|
||||
in.Status = consts.GenCodesStatusWait
|
||||
in.CreatedAt = gtime.Now()
|
||||
id, err := dao.SysGenCodes.Ctx(ctx).Data(in).OmitEmptyData().InsertAndGetId()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return
|
||||
}
|
||||
|
||||
in.Id = id
|
||||
return &sysin.GenCodesEditModel{SysGenCodes: in.SysGenCodes}, nil
|
||||
}
|
||||
|
||||
// Status 更新部门状态
|
||||
func (s *sSysGenCodes) Status(ctx context.Context, in *sysin.GenCodesStatusInp) (err error) {
|
||||
if in.Id <= 0 {
|
||||
err = gerror.New("ID不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
if in.Status <= 0 {
|
||||
err = gerror.New("状态不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
if !validate.InSlice(consts.StatusSlice, in.Status) {
|
||||
err = gerror.New("状态不正确")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = dao.SysGenCodes.Ctx(ctx).Where("id", in.Id).Data("status", in.Status).Update()
|
||||
return
|
||||
}
|
||||
|
||||
// MaxSort 最大排序
|
||||
func (s *sSysGenCodes) MaxSort(ctx context.Context, in *sysin.GenCodesMaxSortInp) (res *sysin.GenCodesMaxSortModel, err error) {
|
||||
if in.Id > 0 {
|
||||
if err = dao.SysGenCodes.Ctx(ctx).Where("id", in.Id).Order("sort desc").Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if res == nil {
|
||||
res = new(sysin.GenCodesMaxSortModel)
|
||||
}
|
||||
|
||||
res.Sort = form.DefaultMaxSort(res.Sort)
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取指定字典类型信息
|
||||
func (s *sSysGenCodes) View(ctx context.Context, in *sysin.GenCodesViewInp) (res *sysin.GenCodesViewModel, err error) {
|
||||
err = dao.SysGenCodes.Ctx(ctx).Where("id", in.Id).Scan(&res)
|
||||
return
|
||||
}
|
||||
|
||||
// List 获取列表
|
||||
func (s *sSysGenCodes) List(ctx context.Context, in *sysin.GenCodesListInp) (list []*sysin.GenCodesListModel, totalCount int, err error) {
|
||||
mod := dao.SysGenCodes.Ctx(ctx)
|
||||
|
||||
if in.GenType > 0 {
|
||||
mod = mod.Where("gen_type", in.GenType)
|
||||
}
|
||||
|
||||
if in.VarName != "" {
|
||||
mod = mod.Where("var_name", in.VarName)
|
||||
}
|
||||
|
||||
if in.Status > 0 {
|
||||
mod = mod.Where("status", in.Status)
|
||||
}
|
||||
|
||||
totalCount, err = mod.Count()
|
||||
if err != nil || totalCount == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if err = mod.Page(in.Page, in.PerPage).Order("id desc").Scan(&list); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return
|
||||
}
|
||||
|
||||
typeSelect, err := hggen.GenTypeSelect(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
getTpGroup := func(row *sysin.GenCodesListModel) string {
|
||||
if row == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
genType := int(row.GenType)
|
||||
if genType == consts.GenCodesTypeTree {
|
||||
genType = consts.GenCodesTypeCurd
|
||||
}
|
||||
|
||||
for _, v := range typeSelect {
|
||||
if v.Value == genType {
|
||||
for index, template := range v.Templates {
|
||||
if index == row.GenTemplate {
|
||||
return template.Label
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
if len(list) > 0 {
|
||||
for _, v := range list {
|
||||
v.GenTemplateGroup = getTpGroup(v)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Selects 选项
|
||||
func (s *sSysGenCodes) Selects(ctx context.Context, in *sysin.GenCodesSelectsInp) (res *sysin.GenCodesSelectsModel, err error) {
|
||||
return hggen.TableSelects(ctx, in)
|
||||
}
|
||||
|
||||
// TableSelect 表选项
|
||||
func (s *sSysGenCodes) TableSelect(ctx context.Context, in *sysin.GenCodesTableSelectInp) (res []*sysin.GenCodesTableSelectModel, err error) {
|
||||
var (
|
||||
sql string
|
||||
config = g.DB(in.Name).GetConfig()
|
||||
disableTables = g.Cfg().MustGet(ctx, "hggen.disableTables").Strings()
|
||||
lists []*sysin.GenCodesTableSelectModel
|
||||
)
|
||||
|
||||
// 根据数据库类型使用不同的SQL
|
||||
if config.Type == consts.DBPgsql {
|
||||
// PostgreSQL: 使用pg_catalog查询表和注释
|
||||
sql = `
|
||||
SELECT
|
||||
c.relname as value,
|
||||
COALESCE(obj_description(c.oid), '') as label
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
WHERE n.nspname = 'public'
|
||||
AND c.relkind = 'r'
|
||||
ORDER BY c.relname`
|
||||
} else {
|
||||
// MySQL: 使用information_schema.TABLES
|
||||
sql = fmt.Sprintf("SELECT TABLE_NAME as value, TABLE_COMMENT as label FROM information_schema.TABLES WHERE TABLE_SCHEMA = '%s'", config.Name)
|
||||
}
|
||||
|
||||
if err = g.DB(in.Name).Ctx(ctx).Raw(sql).Scan(&lists); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
patternStr := `addon_(\w+)_`
|
||||
repStr := ``
|
||||
|
||||
for _, v := range lists {
|
||||
if gstr.InArray(disableTables, v.Value) {
|
||||
continue
|
||||
}
|
||||
|
||||
newValue := v.Value
|
||||
if config.Prefix != "" {
|
||||
newValue = gstr.SubStrFromEx(v.Value, config.Prefix)
|
||||
}
|
||||
if newValue == "" {
|
||||
err = gerror.Newf("表名[%v]前缀必须和配置中的前缀设置[%v] 保持一致", v.Value, config.Prefix)
|
||||
return
|
||||
}
|
||||
|
||||
// 如果是插件模块,则移除掉插件表前缀
|
||||
bt, err := gregex.Replace(patternStr, []byte(repStr), []byte(newValue))
|
||||
if err != nil {
|
||||
err = gerror.Newf("表名[%v] gregex.Replace err:%v", v.Value, err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
row := v
|
||||
row.DefTableComment = v.Label
|
||||
row.DaoName = gstr.CaseCamel(newValue)
|
||||
row.DefVarName = gstr.CaseCamel(string(bt))
|
||||
row.DefAlias = gstr.CaseCamelLower(newValue)
|
||||
row.Name = fmt.Sprintf("%s (%s)", v.Value, v.Label)
|
||||
row.Label = row.Name
|
||||
res = append(res, row)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ColumnSelect 表字段选项
|
||||
func (s *sSysGenCodes) ColumnSelect(ctx context.Context, in *sysin.GenCodesColumnSelectInp) (res []*sysin.GenCodesColumnSelectModel, err error) {
|
||||
var (
|
||||
sql string
|
||||
config = g.DB(in.Name).GetConfig()
|
||||
)
|
||||
|
||||
// 根据数据库类型使用不同的SQL
|
||||
if config.Type == consts.DBPgsql {
|
||||
// PostgreSQL: 使用pg_catalog查询列注释
|
||||
sql = `
|
||||
SELECT
|
||||
a.attname as value,
|
||||
COALESCE(col_description(a.attrelid, a.attnum), '') as label
|
||||
FROM pg_attribute a
|
||||
JOIN pg_class c ON a.attrelid = c.oid
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
WHERE n.nspname = 'public'
|
||||
AND c.relname = '%s'
|
||||
AND a.attnum > 0
|
||||
AND NOT a.attisdropped
|
||||
ORDER BY a.attnum`
|
||||
sql = fmt.Sprintf(sql, in.Table)
|
||||
} else {
|
||||
// MySQL: 使用information_schema.COLUMNS
|
||||
sql = fmt.Sprintf("SELECT COLUMN_NAME as value, COLUMN_COMMENT as label FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s'", config.Name, in.Table)
|
||||
}
|
||||
|
||||
if err = g.DB(in.Name).Ctx(ctx).Raw(sql).Scan(&res); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
err = gerror.Newf("table does not exist:%v", in.Table)
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range res {
|
||||
res[k].Name = fmt.Sprintf("%s (%s)", v.Value, v.Label)
|
||||
res[k].Label = res[k].Name
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ColumnList 表字段列表
|
||||
func (s *sSysGenCodes) ColumnList(ctx context.Context, in *sysin.GenCodesColumnListInp) (res []*sysin.GenCodesColumnListModel, err error) {
|
||||
return hggen.TableColumns(ctx, in)
|
||||
}
|
||||
|
||||
// Preview 生成预览
|
||||
func (s *sSysGenCodes) Preview(ctx context.Context, in *sysin.GenCodesPreviewInp) (res *sysin.GenCodesPreviewModel, err error) {
|
||||
return hggen.Preview(ctx, in)
|
||||
}
|
||||
|
||||
// Build 提交生成
|
||||
func (s *sSysGenCodes) Build(ctx context.Context, in *sysin.GenCodesBuildInp) (err error) {
|
||||
// 先保存配置
|
||||
ein := in.SysGenCodes
|
||||
if _, err = s.Edit(ctx, &sysin.GenCodesEditInp{SysGenCodes: ein}); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = s.Status(ctx, &sysin.GenCodesStatusInp{Id: in.Id, Status: consts.GenCodesStatusOk}); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = hggen.Build(ctx, in); err != nil {
|
||||
_ = s.Status(ctx, &sysin.GenCodesStatusInp{Id: in.Id, Status: consts.GenCodesStatusFail})
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,452 @@
|
||||
// Package sys
|
||||
// @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 sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/global"
|
||||
"hotgo/internal/library/contexts"
|
||||
"hotgo/internal/library/dict"
|
||||
"hotgo/internal/library/hgorm"
|
||||
"hotgo/internal/library/hgorm/handler"
|
||||
"hotgo/internal/library/hgorm/hook"
|
||||
"hotgo/internal/library/location"
|
||||
"hotgo/internal/library/queue"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
"hotgo/utility/excel"
|
||||
"hotgo/utility/simple"
|
||||
"hotgo/utility/validate"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"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/gctx"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/gmode"
|
||||
)
|
||||
|
||||
type sSysLog struct{}
|
||||
|
||||
func NewSysLog() *sSysLog {
|
||||
return &sSysLog{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysLog(NewSysLog())
|
||||
}
|
||||
|
||||
// Model 请求日志Orm模型
|
||||
func (s *sSysLog) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
||||
return handler.Model(dao.SysLog.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// Export 导出
|
||||
func (s *sSysLog) Export(ctx context.Context, in *sysin.LogListInp) (err error) {
|
||||
// 导出格式
|
||||
type exportImage struct {
|
||||
Id int64 `json:"id" description:""`
|
||||
AppId string `json:"app_id" description:"应用id"`
|
||||
Method string `json:"method" description:"提交类型"`
|
||||
Module string `json:"module" description:"模块"`
|
||||
Url string `json:"url" description:"提交url"`
|
||||
Ip string `json:"ip" description:"ip地址"`
|
||||
ErrorCode int `json:"error_code" description:"报错code"`
|
||||
ErrorMsg string `json:"error_msg" description:"报错信息"`
|
||||
ReqId string `json:"req_id" description:"对外id"`
|
||||
TakeUpTime int64 `json:"take_up_time" description:"请求耗时"`
|
||||
CreatedAt *gtime.Time `json:"created_at" description:"创建时间"`
|
||||
MemberName string `json:"memberName"`
|
||||
Region string `json:"region"`
|
||||
}
|
||||
|
||||
var (
|
||||
titleList = []string{"ID", "应用", "提交类型", "模块", "提交url", "ip地址", "报错code", "报错信息", "对外id", "请求耗时", "创建时间", "用户", "访问地"}
|
||||
fileName = "访问日志导出-" + gctx.CtxId(ctx)
|
||||
sheetName = simple.AppName(ctx)
|
||||
exportList []exportImage
|
||||
row exportImage
|
||||
)
|
||||
|
||||
list, _, err := s.List(ctx, in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 格式化格式
|
||||
for i := 0; i < len(list); i++ {
|
||||
row.Id = list[i].Id
|
||||
row.AppId = list[i].AppId
|
||||
row.Module = list[i].Module
|
||||
row.Method = list[i].Method
|
||||
row.Url = list[i].Url
|
||||
row.Ip = list[i].Ip
|
||||
row.ReqId = list[i].ReqId
|
||||
row.ErrorCode = list[i].ErrorCode
|
||||
row.ErrorMsg = list[i].ErrorMsg
|
||||
row.TakeUpTime = list[i].TakeUpTime
|
||||
row.CreatedAt = list[i].CreatedAt
|
||||
row.MemberName = list[i].MemberName
|
||||
row.Region = list[i].Region
|
||||
exportList = append(exportList, row)
|
||||
}
|
||||
|
||||
err = excel.ExportByStructs(ctx, titleList, exportList, fileName, sheetName)
|
||||
return
|
||||
}
|
||||
|
||||
// RealWrite 真实写入
|
||||
func (s *sSysLog) RealWrite(ctx context.Context, log entity.SysLog) (err error) {
|
||||
_, err = dao.SysLog.Ctx(ctx).FieldsEx(dao.SysLog.Columns().Id).Data(log).Unscoped().OmitEmptyData().Insert()
|
||||
return
|
||||
}
|
||||
|
||||
// AutoLog 根据配置自动记录请求日志
|
||||
func (s *sSysLog) AutoLog(ctx context.Context) error {
|
||||
return g.Try(ctx, func(ctx context.Context) {
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
g.Log().Error(ctx, "autoLog err:%+v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
config, err := service.SysConfig().GetLoadLog(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if config == nil || !config.Switch {
|
||||
return
|
||||
}
|
||||
|
||||
data := s.AnalysisLog(ctx)
|
||||
if ok := validate.InSliceExistStr(config.Module, data.Module); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if ok := validate.InSliceExistStr(config.SkipCode, gconv.String(data.ErrorCode)); ok {
|
||||
return
|
||||
}
|
||||
|
||||
if config.Queue {
|
||||
err = queue.Push(consts.QueueLogTopic, data)
|
||||
return
|
||||
}
|
||||
|
||||
err = s.RealWrite(ctx, data)
|
||||
})
|
||||
}
|
||||
|
||||
// AnalysisLog 解析日志数据
|
||||
func (s *sSysLog) AnalysisLog(ctx context.Context) entity.SysLog {
|
||||
var (
|
||||
mctx = contexts.Get(ctx)
|
||||
response = mctx.Response
|
||||
user = mctx.User
|
||||
request = ghttp.RequestFromCtx(ctx)
|
||||
module = mctx.Module
|
||||
clientIp = location.GetClientIp(request)
|
||||
postData = gjson.New(consts.NilJsonToString)
|
||||
getData = gjson.New(request.URL.Query())
|
||||
headerData = gjson.New(consts.NilJsonToString)
|
||||
errorData = gjson.New(consts.NilJsonToString)
|
||||
data entity.SysLog
|
||||
memberId int64
|
||||
errorCode int
|
||||
errorMsg string
|
||||
traceID string
|
||||
timestamp int64
|
||||
appId string
|
||||
takeUpTime int64
|
||||
)
|
||||
|
||||
// 响应数据
|
||||
if response != nil {
|
||||
errorCode = response.Code
|
||||
errorMsg = response.Message
|
||||
traceID = response.TraceID
|
||||
timestamp = response.Timestamp
|
||||
if len(gconv.String(response.Error)) > 0 {
|
||||
errorData = gjson.New(response.Error)
|
||||
}
|
||||
}
|
||||
|
||||
if timestamp == 0 {
|
||||
timestamp = gtime.Timestamp()
|
||||
}
|
||||
|
||||
// 请求头
|
||||
if reqHeadersBytes, _ := gjson.New(request.Header).MarshalJSON(); len(reqHeadersBytes) > 0 {
|
||||
headerData = gjson.New(reqHeadersBytes)
|
||||
}
|
||||
|
||||
// post参数
|
||||
if body, ok := mctx.Data["request.body"].(*gjson.Json); ok {
|
||||
postData = body
|
||||
}
|
||||
|
||||
// post表单
|
||||
postForm := gjson.New(gconv.String(request.PostForm)).Map()
|
||||
if len(postForm) > 0 {
|
||||
for k, v := range postForm {
|
||||
postData.MustSet(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
if postData.IsNil() || len(postData.Map()) == 0 {
|
||||
postData = gjson.New(consts.NilJsonToString)
|
||||
} else {
|
||||
// 隐藏明文显示的密码
|
||||
for k := range postData.Map() {
|
||||
if gstr.ContainsI(k, "password") {
|
||||
postData.MustSet(k, "******")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 当前登录用户
|
||||
if user != nil {
|
||||
memberId = user.Id
|
||||
appId = user.App
|
||||
}
|
||||
|
||||
ipData, err := location.GetLocation(ctx, clientIp)
|
||||
if err != nil {
|
||||
g.Log().Debugf(ctx, "location.GetLocation clientIp:%v, err:%+v", clientIp, err)
|
||||
}
|
||||
|
||||
if ipData == nil {
|
||||
ipData = new(location.IpLocationData)
|
||||
}
|
||||
|
||||
// 请求耗时
|
||||
if tt, ok := mctx.Data["request.takeUpTime"].(int64); ok {
|
||||
takeUpTime = tt
|
||||
}
|
||||
|
||||
headerData.MustAppend("Enter-Time", request.EnterTime.String())
|
||||
|
||||
data = entity.SysLog{
|
||||
AppId: appId,
|
||||
MerchantId: 0,
|
||||
MemberId: memberId,
|
||||
Method: request.Method,
|
||||
Module: module,
|
||||
Url: request.URL.Path,
|
||||
GetData: getData,
|
||||
PostData: postData,
|
||||
HeaderData: s.SimplifyHeaderParams(headerData),
|
||||
Ip: clientIp,
|
||||
ProvinceId: ipData.ProvinceCode,
|
||||
CityId: ipData.CityCode,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
ErrorData: errorData,
|
||||
ReqId: traceID,
|
||||
Timestamp: timestamp,
|
||||
UserAgent: request.Header.Get("User-Agent"),
|
||||
Status: consts.StatusEnabled,
|
||||
TakeUpTime: takeUpTime,
|
||||
UpdatedAt: gtime.Now(),
|
||||
CreatedAt: request.EnterTime,
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// SimplifyHeaderParams 过滤掉请求头中的大参数
|
||||
func (s *sSysLog) SimplifyHeaderParams(data *gjson.Json) *gjson.Json {
|
||||
if data == nil || data.IsNil() {
|
||||
return data
|
||||
}
|
||||
var filters = []string{"Accept", "Authorization", "Cookie"}
|
||||
for _, filter := range filters {
|
||||
v := data.Get(filter)
|
||||
if len(v.String()) > 128 {
|
||||
data.MustRemove(filter)
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// View 获取指定请求日志信息
|
||||
func (s *sSysLog) View(ctx context.Context, in *sysin.LogViewInp) (res *sysin.LogViewModel, err error) {
|
||||
mod := s.Model(ctx)
|
||||
|
||||
count, err := service.SysLoginLog().Model(ctx).
|
||||
LeftJoinOnFields(dao.SysLog.Table(), dao.SysLoginLog.Columns().ReqId, "=", dao.SysLog.Columns().ReqId).
|
||||
WherePrefix(dao.SysLog.Table(), dao.SysLog.Columns().Id, in.Id).Count()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if count > 0 {
|
||||
mod = dao.SysLog.Ctx(ctx)
|
||||
}
|
||||
|
||||
if err = mod.Hook(hook.CityLabel).WherePri(in.Id).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return
|
||||
}
|
||||
|
||||
if res == nil {
|
||||
return
|
||||
}
|
||||
|
||||
routes := global.LoadHTTPRoutes(ghttp.RequestFromCtx(ctx))
|
||||
key := global.GenRouteKey(res.Method, res.Url)
|
||||
route, ok := routes[key]
|
||||
if ok {
|
||||
res.Tags = route.Tags
|
||||
res.Summary = route.Summary
|
||||
res.Description = route.Description
|
||||
}
|
||||
|
||||
if simple.IsDemo(ctx) {
|
||||
res.HeaderData = gjson.New(`{
|
||||
"none": [
|
||||
"` + consts.DemoTips + `"
|
||||
]
|
||||
}`)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete 删除请求日志
|
||||
func (s *sSysLog) Delete(ctx context.Context, in *sysin.LogDeleteInp) (err error) {
|
||||
_, err = s.Model(ctx).WherePri(in.Id).Delete()
|
||||
return
|
||||
}
|
||||
|
||||
// List 请求日志列表
|
||||
func (s *sSysLog) List(ctx context.Context, in *sysin.LogListInp) (list []*sysin.LogListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx).FieldsEx("get_data", "header_data", "post_data")
|
||||
|
||||
// 访问路径
|
||||
if in.Url != "" {
|
||||
mod = mod.WhereLike("url", "%"+in.Url+"%")
|
||||
}
|
||||
|
||||
// 模块
|
||||
if in.Module != "" {
|
||||
mod = mod.Where("module", in.Module)
|
||||
}
|
||||
|
||||
// 链路ID
|
||||
if in.ReqId != "" {
|
||||
mod = mod.Where("req_id", in.ReqId)
|
||||
}
|
||||
|
||||
// 请求方式
|
||||
if in.Method != "" {
|
||||
mod = mod.Where("method", in.Method)
|
||||
}
|
||||
|
||||
// 用户
|
||||
if in.MemberId > 0 {
|
||||
mod = mod.Where("member_id", in.MemberId)
|
||||
}
|
||||
|
||||
// 操作人筛选
|
||||
if len(in.ComplexMemberId) == 2 && len(in.ComplexMemberId[0]) > 0 {
|
||||
memberIds, err := service.AdminMember().GetComplexMemberIds(ctx, in.ComplexMemberId[0], in.ComplexMemberId[1])
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if len(memberIds) == 0 {
|
||||
return nil, 0, nil
|
||||
}
|
||||
mod = mod.WhereIn("member_id", memberIds)
|
||||
}
|
||||
|
||||
// 访问IP
|
||||
if in.Ip != "" {
|
||||
mod = mod.Where("ip", in.Ip)
|
||||
}
|
||||
|
||||
// 日期范围
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween("created_at", gtime.New(in.CreatedAt[0]), gtime.New(in.CreatedAt[1]))
|
||||
}
|
||||
|
||||
// 状态码
|
||||
if in.ErrorCode != "" {
|
||||
mod = mod.Where("error_code", in.ErrorCode)
|
||||
}
|
||||
|
||||
// 请求耗时
|
||||
if dict.HasOptionKey(consts.HTTPHandlerTimeOptions, in.TakeUpTime) {
|
||||
mod = mod.Where(gdb.Raw(fmt.Sprintf("(`take_up_time` %v)", in.TakeUpTime)))
|
||||
}
|
||||
|
||||
// 非生产环境,允许关键词查询日志
|
||||
// 生成环境使用需谨慎,日志量大易产生慢日志
|
||||
if !gmode.IsProduct() && in.Keyword != "" {
|
||||
filterColumns := map[string]string{
|
||||
"get_data": "LIKE",
|
||||
"post_data": "LIKE",
|
||||
"header_data": "LIKE",
|
||||
"error_data": "LIKE",
|
||||
"error_msg": "LIKE",
|
||||
}
|
||||
mod = hgorm.FilterKeywordsWithOr(mod, filterColumns, in.Keyword)
|
||||
}
|
||||
|
||||
totalCount, err = mod.Count()
|
||||
if err != nil || totalCount == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if err = mod.Page(in.Page, in.PerPage).Hook(hook.CityLabel).Order("id desc").Scan(&list); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
routes := global.LoadHTTPRoutes(ghttp.RequestFromCtx(ctx))
|
||||
for _, v := range list {
|
||||
if v.AppId == consts.AppAdmin {
|
||||
memberName, err := dao.AdminMember.Ctx(ctx).Fields(dao.AdminMember.Columns().RealName).WherePri(v.MemberId).Value()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return list, totalCount, err
|
||||
}
|
||||
v.MemberName = memberName.String()
|
||||
}
|
||||
|
||||
if v.MemberName == "" {
|
||||
v.MemberName = "游客"
|
||||
}
|
||||
|
||||
// 截取请求url路径
|
||||
if gstr.Contains(v.Url, "?") {
|
||||
v.Url = gstr.StrTillEx(v.Url, "?")
|
||||
}
|
||||
|
||||
key := global.GenRouteKey(v.Method, v.Url)
|
||||
route, ok := routes[key]
|
||||
if ok {
|
||||
v.Tags = route.Tags
|
||||
v.Summary = route.Summary
|
||||
v.Description = route.Description
|
||||
}
|
||||
|
||||
if simple.IsDemo(ctx) {
|
||||
v.HeaderData = gjson.New(`{
|
||||
"none": [
|
||||
"` + consts.DemoTips + `"
|
||||
]
|
||||
}`)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
// Package sys
|
||||
// @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 sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"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/gctx"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/hgorm/handler"
|
||||
"hotgo/internal/library/hgorm/hook"
|
||||
"hotgo/internal/library/location"
|
||||
"hotgo/internal/library/queue"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/adminin"
|
||||
"hotgo/internal/model/input/form"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
"hotgo/utility/convert"
|
||||
"hotgo/utility/excel"
|
||||
"hotgo/utility/useragent"
|
||||
)
|
||||
|
||||
type sSysLoginLog struct{}
|
||||
|
||||
func NewSysLoginLog() *sSysLoginLog {
|
||||
return &sSysLoginLog{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysLoginLog(NewSysLoginLog())
|
||||
}
|
||||
|
||||
// Model 登录日志Orm模型
|
||||
func (s *sSysLoginLog) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
||||
return handler.Model(dao.SysLoginLog.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 获取登录日志列表
|
||||
func (s *sSysLoginLog) List(ctx context.Context, in *sysin.LoginLogListInp) (list []*sysin.LoginLogListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx)
|
||||
|
||||
// 查询状态
|
||||
if in.Status > 0 {
|
||||
mod = mod.Where(dao.SysLoginLog.Columns().Status, in.Status)
|
||||
}
|
||||
|
||||
// 查询登录时间
|
||||
if len(in.LoginAt) == 2 {
|
||||
mod = mod.WhereBetween(dao.SysLoginLog.Columns().LoginAt, in.LoginAt[0], in.LoginAt[1])
|
||||
}
|
||||
|
||||
// 查询IP地址
|
||||
if in.LoginIp != "" {
|
||||
mod = mod.Where(dao.SysLoginLog.Columns().LoginIp, in.LoginIp)
|
||||
}
|
||||
|
||||
// 用户名
|
||||
if in.Username != "" {
|
||||
mod = mod.Where(dao.SysLoginLog.Columns().Username, in.Username)
|
||||
}
|
||||
|
||||
totalCount, err = mod.Clone().Count()
|
||||
if err != nil || totalCount == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if err = mod.Fields(sysin.LoginLogListModel{}).Hook(hook.CityLabel).Page(in.Page, in.PerPage).OrderDesc(dao.SysLoginLog.Columns().Id).Scan(&list); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return list, totalCount, err
|
||||
}
|
||||
|
||||
for _, v := range list {
|
||||
if v.Response.Contains("token") {
|
||||
v.Response.Set("token", "******")
|
||||
}
|
||||
v.Os = useragent.GetOs(v.UserAgent)
|
||||
v.Browser = useragent.GetBrowser(v.UserAgent)
|
||||
v.SysLogId, err = dao.SysLog.Ctx(ctx).Fields(dao.SysLog.Columns().Id).Where(dao.SysLog.Columns().ReqId, v.ReqId).Value()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Export 导出登录日志
|
||||
func (s *sSysLoginLog) Export(ctx context.Context, in *sysin.LoginLogListInp) (err error) {
|
||||
list, totalCount, err := s.List(ctx, in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
|
||||
tags, err := convert.GetEntityDescTags(sysin.LoginLogExportModel{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
fileName = "导出登录日志-" + gctx.CtxId(ctx)
|
||||
sheetName = fmt.Sprintf("索引条件共%v行,共%v页,当前导出是第%v页,本页共%v行", totalCount, form.CalPageCount(totalCount, in.PerPage), in.Page, len(list))
|
||||
exports []sysin.LoginLogExportModel
|
||||
)
|
||||
|
||||
if err = gconv.Scan(list, &exports); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete 删除登录日志
|
||||
func (s *sSysLoginLog) Delete(ctx context.Context, in *sysin.LoginLogDeleteInp) (err error) {
|
||||
_, err = s.Model(ctx).WherePri(in.Id).Delete()
|
||||
return
|
||||
}
|
||||
|
||||
// Push 推送登录日志
|
||||
func (s *sSysLoginLog) Push(ctx context.Context, in *sysin.LoginLogPushInp) {
|
||||
if in.Response == nil {
|
||||
in.Response = new(adminin.LoginModel)
|
||||
}
|
||||
|
||||
r := ghttp.RequestFromCtx(ctx)
|
||||
if r == nil {
|
||||
g.Log().Warningf(ctx, "ctx not http request")
|
||||
return
|
||||
}
|
||||
|
||||
clientIp := location.GetClientIp(r)
|
||||
ipData, err := location.GetLocation(ctx, clientIp)
|
||||
if err != nil {
|
||||
g.Log().Debugf(ctx, "location.GetLocation clientIp:%v, err:%+v", clientIp, err)
|
||||
}
|
||||
|
||||
if ipData == nil {
|
||||
ipData = new(location.IpLocationData)
|
||||
}
|
||||
|
||||
var models entity.SysLoginLog
|
||||
models.ReqId = gctx.CtxId(ctx)
|
||||
models.MemberId = in.Response.Id
|
||||
models.Username = in.Response.Username
|
||||
models.LoginAt = gtime.Now()
|
||||
models.LoginIp = clientIp
|
||||
models.UserAgent = r.UserAgent()
|
||||
models.ProvinceId = ipData.ProvinceCode
|
||||
models.CityId = ipData.CityCode
|
||||
models.Status = consts.StatusEnabled
|
||||
|
||||
if in.Err != nil {
|
||||
models.Status = consts.StatusDisable
|
||||
models.ErrMsg = in.Err.Error()
|
||||
}
|
||||
|
||||
models.Response = gjson.New(consts.NilJsonToString)
|
||||
if in.Response != nil {
|
||||
models.Response = gjson.New(in.Response)
|
||||
}
|
||||
|
||||
if err = queue.Push(consts.QueueLoginLogTopic, models); err != nil {
|
||||
g.Log().Warningf(ctx, "push LoginLog err:%+v, models:%v", err, gjson.New(models).String())
|
||||
}
|
||||
}
|
||||
|
||||
// RealWrite 真实写入
|
||||
func (s *sSysLoginLog) RealWrite(ctx context.Context, models entity.SysLoginLog) (err error) {
|
||||
_, err = dao.SysLoginLog.Ctx(ctx).Data(models).OmitEmptyData().Insert()
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
// Package sys
|
||||
// @Link https://github.com/bufanyun/hotgo
|
||||
// @Copyright Copyright (c) 2024 HotGo CLI
|
||||
// @Author Ms <133814250@qq.com>
|
||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||
// @AutoGenerate Version 2.15.7
|
||||
package sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/contexts"
|
||||
"hotgo/internal/library/hgorm"
|
||||
"hotgo/internal/library/hgorm/handler"
|
||||
"hotgo/internal/library/hgorm/hook"
|
||||
"hotgo/internal/model/input/form"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
"hotgo/utility/tree"
|
||||
)
|
||||
|
||||
type sSysNormalTreeDemo struct{}
|
||||
|
||||
func NewSysNormalTreeDemo() *sSysNormalTreeDemo {
|
||||
return &sSysNormalTreeDemo{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysNormalTreeDemo(NewSysNormalTreeDemo())
|
||||
}
|
||||
|
||||
// Model 普通树表ORM模型
|
||||
func (s *sSysNormalTreeDemo) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
||||
return handler.Model(dao.SysGenTreeDemo.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 获取普通树表列表
|
||||
func (s *sSysNormalTreeDemo) List(ctx context.Context, in *sysin.NormalTreeDemoListInp) (list []*sysin.NormalTreeDemoListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx)
|
||||
|
||||
// 字段过滤
|
||||
mod = mod.Fields(sysin.NormalTreeDemoListModel{})
|
||||
|
||||
// 查询标题
|
||||
if in.Title != "" {
|
||||
mod = mod.WhereLike(dao.SysGenTreeDemo.Columns().Title, "%"+in.Title+"%")
|
||||
}
|
||||
|
||||
// 查询上级
|
||||
if in.Pid > 0 {
|
||||
mod = mod.Where(dao.SysGenTreeDemo.Columns().Pid, in.Pid)
|
||||
}
|
||||
|
||||
// 查询测试分类
|
||||
if in.CategoryId > 0 {
|
||||
mod = mod.Where(dao.SysGenTreeDemo.Columns().CategoryId, in.CategoryId)
|
||||
}
|
||||
|
||||
// 查询状态
|
||||
if in.Status > 0 {
|
||||
mod = mod.Where(dao.SysGenTreeDemo.Columns().Status, in.Status)
|
||||
}
|
||||
|
||||
// 查询创建时间
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween(dao.SysGenTreeDemo.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
// 树形列表判断是否需要分页
|
||||
if in.Pagination {
|
||||
mod = mod.Page(in.Page, in.PerPage)
|
||||
}
|
||||
|
||||
// 排序
|
||||
mod = mod.OrderAsc(dao.SysGenTreeDemo.Columns().Sort).OrderDesc(dao.SysGenTreeDemo.Columns().Id)
|
||||
|
||||
// 操作人摘要信息
|
||||
mod = mod.Hook(hook.MemberSummary)
|
||||
|
||||
// 查询数据
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取普通树表列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增普通树表
|
||||
func (s *sSysNormalTreeDemo) Edit(ctx context.Context, in *sysin.NormalTreeDemoEditInp) (err error) {
|
||||
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
in.Pid, in.Level, in.Tree, err = hgorm.AutoUpdateTree(ctx, &dao.SysGenTreeDemo, in.Id, in.Pid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 修改
|
||||
if in.Id > 0 {
|
||||
in.UpdatedBy = contexts.GetUserId(ctx)
|
||||
if _, err = s.Model(ctx).
|
||||
Fields(sysin.NormalTreeDemoUpdateFields{}).
|
||||
WherePri(in.Id).Data(in).Update(); err != nil {
|
||||
err = gerror.Wrap(err, "修改普通树表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 新增
|
||||
in.CreatedBy = contexts.GetUserId(ctx)
|
||||
if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).
|
||||
Fields(sysin.NormalTreeDemoInsertFields{}).
|
||||
Data(in).OmitEmptyData().Insert(); err != nil {
|
||||
err = gerror.Wrap(err, "新增普通树表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除普通树表
|
||||
func (s *sSysNormalTreeDemo) Delete(ctx context.Context, in *sysin.NormalTreeDemoDeleteInp) (err error) {
|
||||
count, err := dao.SysGenTreeDemo.Ctx(ctx).Where(dao.SysGenTreeDemo.Columns().Pid, in.Id).Count()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return gerror.New("请先删除该普通树表下的所有下级!")
|
||||
}
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除普通树表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MaxSort 获取普通树表最大排序
|
||||
func (s *sSysNormalTreeDemo) MaxSort(ctx context.Context, in *sysin.NormalTreeDemoMaxSortInp) (res *sysin.NormalTreeDemoMaxSortModel, err error) {
|
||||
if err = dao.SysGenTreeDemo.Ctx(ctx).Fields(dao.SysGenTreeDemo.Columns().Sort).OrderDesc(dao.SysGenTreeDemo.Columns().Sort).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取普通树表最大排序,请稍后重试!")
|
||||
return
|
||||
}
|
||||
|
||||
if res == nil {
|
||||
res = new(sysin.NormalTreeDemoMaxSortModel)
|
||||
}
|
||||
|
||||
res.Sort = form.DefaultMaxSort(res.Sort)
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取普通树表指定信息
|
||||
func (s *sSysNormalTreeDemo) View(ctx context.Context, in *sysin.NormalTreeDemoViewInp) (res *sysin.NormalTreeDemoViewModel, err error) {
|
||||
if err = s.Model(ctx).WherePri(in.Id).Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取普通树表信息,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TreeOption 获取普通树表关系树选项
|
||||
func (s *sSysNormalTreeDemo) TreeOption(ctx context.Context) (nodes []tree.Node, err error) {
|
||||
var models []*sysin.NormalTreeDemoTreeOption
|
||||
if err = s.Model(ctx).Fields(sysin.NormalTreeDemoTreeOption{}).OrderAsc(dao.SysGenTreeDemo.Columns().Pid).OrderAsc(dao.SysGenTreeDemo.Columns().Sort).OrderDesc(dao.SysGenTreeDemo.Columns().Id).Scan(&models); err != nil {
|
||||
err = gerror.Wrap(err, "获取普通树表关系树选项失败!")
|
||||
return
|
||||
}
|
||||
nodes = make([]tree.Node, len(models))
|
||||
for i, v := range models {
|
||||
nodes[i] = v
|
||||
}
|
||||
return tree.ListToTree(0, nodes)
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
// Package sys
|
||||
// @Link https://github.com/bufanyun/hotgo
|
||||
// @Copyright Copyright (c) 2025 HotGo CLI
|
||||
// @Author Ms <133814250@qq.com>
|
||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||
// @AutoGenerate Version 2.17.8
|
||||
package sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/contexts"
|
||||
"hotgo/internal/library/hgorm"
|
||||
"hotgo/internal/library/hgorm/handler"
|
||||
"hotgo/internal/library/hgorm/hook"
|
||||
"hotgo/internal/model/input/form"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
"hotgo/utility/tree"
|
||||
)
|
||||
|
||||
type sSysOptionTreeDemo struct{}
|
||||
|
||||
func NewSysOptionTreeDemo() *sSysOptionTreeDemo {
|
||||
return &sSysOptionTreeDemo{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysOptionTreeDemo(NewSysOptionTreeDemo())
|
||||
}
|
||||
|
||||
// Model 选项树表ORM模型
|
||||
func (s *sSysOptionTreeDemo) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
||||
return handler.Model(dao.SysGenTreeDemo.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 获取选项树表列表
|
||||
func (s *sSysOptionTreeDemo) List(ctx context.Context, in *sysin.OptionTreeDemoListInp) (list []*sysin.OptionTreeDemoListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx)
|
||||
|
||||
// 字段过滤
|
||||
mod = mod.Fields(sysin.OptionTreeDemoListModel{})
|
||||
|
||||
// 查询标题
|
||||
if in.Title != "" {
|
||||
mod = mod.WhereLike(dao.SysGenTreeDemo.Columns().Title, "%"+in.Title+"%")
|
||||
}
|
||||
|
||||
// 查询上级
|
||||
if in.Pid > 0 {
|
||||
mod = mod.Where(dao.SysGenTreeDemo.Columns().Pid, in.Pid)
|
||||
}
|
||||
|
||||
// 查询测试分类
|
||||
if in.CategoryId > 0 {
|
||||
mod = mod.Where(dao.SysGenTreeDemo.Columns().CategoryId, in.CategoryId)
|
||||
}
|
||||
|
||||
// 查询状态
|
||||
if in.Status > 0 {
|
||||
mod = mod.Where(dao.SysGenTreeDemo.Columns().Status, in.Status)
|
||||
}
|
||||
|
||||
// 查询创建时间
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween(dao.SysGenTreeDemo.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
// 树形列表判断是否需要分页
|
||||
if in.Pagination {
|
||||
mod = mod.Page(in.Page, in.PerPage)
|
||||
}
|
||||
|
||||
// 排序
|
||||
mod = mod.OrderAsc(dao.SysGenTreeDemo.Columns().Sort).OrderDesc(dao.SysGenTreeDemo.Columns().Id)
|
||||
|
||||
// 操作人摘要信息
|
||||
mod = mod.Hook(hook.MemberSummary)
|
||||
|
||||
// 查询数据
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取选项树表列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增选项树表
|
||||
func (s *sSysOptionTreeDemo) Edit(ctx context.Context, in *sysin.OptionTreeDemoEditInp) (err error) {
|
||||
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
in.Pid, in.Level, in.Tree, err = hgorm.AutoUpdateTree(ctx, &dao.SysGenTreeDemo, in.Id, in.Pid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 修改
|
||||
if in.Id > 0 {
|
||||
in.UpdatedBy = contexts.GetUserId(ctx)
|
||||
if _, err = s.Model(ctx).
|
||||
Fields(sysin.OptionTreeDemoUpdateFields{}).
|
||||
WherePri(in.Id).Data(in).Update(); err != nil {
|
||||
err = gerror.Wrap(err, "修改选项树表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 新增
|
||||
in.CreatedBy = contexts.GetUserId(ctx)
|
||||
if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).
|
||||
Fields(sysin.OptionTreeDemoInsertFields{}).
|
||||
Data(in).OmitEmptyData().Insert(); err != nil {
|
||||
err = gerror.Wrap(err, "新增选项树表失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除选项树表
|
||||
func (s *sSysOptionTreeDemo) Delete(ctx context.Context, in *sysin.OptionTreeDemoDeleteInp) (err error) {
|
||||
count, err := dao.SysGenTreeDemo.Ctx(ctx).Where(dao.SysGenTreeDemo.Columns().Pid, in.Id).Count()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return gerror.New("请先删除该选项树表下的所有下级!")
|
||||
}
|
||||
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Unscoped().Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除选项树表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MaxSort 获取选项树表最大排序
|
||||
func (s *sSysOptionTreeDemo) MaxSort(ctx context.Context, in *sysin.OptionTreeDemoMaxSortInp) (res *sysin.OptionTreeDemoMaxSortModel, err error) {
|
||||
if err = dao.SysGenTreeDemo.Ctx(ctx).Fields(dao.SysGenTreeDemo.Columns().Sort).OrderDesc(dao.SysGenTreeDemo.Columns().Sort).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取选项树表最大排序,请稍后重试!")
|
||||
return
|
||||
}
|
||||
|
||||
if res == nil {
|
||||
res = new(sysin.OptionTreeDemoMaxSortModel)
|
||||
}
|
||||
|
||||
res.Sort = form.DefaultMaxSort(res.Sort)
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取选项树表指定信息
|
||||
func (s *sSysOptionTreeDemo) View(ctx context.Context, in *sysin.OptionTreeDemoViewInp) (res *sysin.OptionTreeDemoViewModel, err error) {
|
||||
if err = s.Model(ctx).WherePri(in.Id).Hook(hook.MemberSummary).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取选项树表信息,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TreeOption 获取选项树表关系树选项
|
||||
func (s *sSysOptionTreeDemo) TreeOption(ctx context.Context) (nodes []tree.Node, err error) {
|
||||
var models []*sysin.OptionTreeDemoTreeOption
|
||||
if err = s.Model(ctx).Fields(sysin.OptionTreeDemoTreeOption{}).OrderAsc(dao.SysGenTreeDemo.Columns().Pid).OrderAsc(dao.SysGenTreeDemo.Columns().Sort).OrderDesc(dao.SysGenTreeDemo.Columns().Id).Scan(&models); err != nil {
|
||||
err = gerror.Wrap(err, "获取选项树表关系树选项失败!")
|
||||
return
|
||||
}
|
||||
nodes = make([]tree.Node, len(models))
|
||||
for i, v := range models {
|
||||
nodes[i] = v
|
||||
}
|
||||
return tree.ListToTree(0, nodes)
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
// Package sys
|
||||
// @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 sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/hgorm"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/form"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
type sSysProvinces struct{}
|
||||
|
||||
func NewSysProvinces() *sSysProvinces {
|
||||
return &sSysProvinces{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysProvinces(NewSysProvinces())
|
||||
}
|
||||
|
||||
// Tree 关系树选项列表
|
||||
func (s *sSysProvinces) Tree(ctx context.Context) (list []*sysin.ProvincesTree, err error) {
|
||||
var models []*entity.SysProvinces
|
||||
if err = dao.SysProvinces.Ctx(ctx).Order("pid asc,id asc,sort asc").Scan(&models); err != nil {
|
||||
err = gerror.Wrap(err, "获取省市区关系树选项列表失败!")
|
||||
return
|
||||
}
|
||||
|
||||
list = s.treeList(0, models)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete 删除省市区数据
|
||||
func (s *sSysProvinces) Delete(ctx context.Context, in *sysin.ProvincesDeleteInp) (err error) {
|
||||
var models *entity.SysProvinces
|
||||
if err = dao.SysProvinces.Ctx(ctx).Where("id", in.Id).Scan(&models); err != nil {
|
||||
err = gerror.Wrap(err, "获取省市区数据失败!")
|
||||
return
|
||||
}
|
||||
|
||||
if models == nil {
|
||||
err = gerror.New("数据不存在或已删除!")
|
||||
return
|
||||
}
|
||||
|
||||
has, err := dao.SysProvinces.Ctx(ctx).Where("pid", models.Id).One()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, "删除省市区数据时获取上级数据失败!")
|
||||
return
|
||||
}
|
||||
|
||||
if !has.IsEmpty() {
|
||||
err = gerror.New("请先删除该地区下得所有子级!")
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = dao.SysProvinces.Ctx(ctx).Where("id", in.Id).Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除省市区数据失败!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增省市区数据
|
||||
func (s *sSysProvinces) Edit(ctx context.Context, in *sysin.ProvincesEditInp) (err error) {
|
||||
// 关系树
|
||||
in.Pid, in.Level, in.Tree, err = hgorm.GenSubTree(ctx, &dao.SysProvinces, in.Pid)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
models, err := s.View(ctx, &sysin.ProvincesViewInp{Id: in.Id})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 修改
|
||||
if models != nil {
|
||||
if _, err = dao.SysProvinces.Ctx(ctx).Fields(sysin.ProvincesUpdateFields{}).WherePri(in.Id).Data(in).Update(); err != nil {
|
||||
err = gerror.Wrap(err, "修改省市区数据失败!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 新增
|
||||
if _, err = dao.SysProvinces.Ctx(ctx).Fields(sysin.ProvincesInsertFields{}).Data(in).OmitEmptyData().Insert(); err != nil {
|
||||
err = gerror.Wrap(err, "新增省市区数据失败!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Status 更新省市区状态
|
||||
func (s *sSysProvinces) Status(ctx context.Context, in *sysin.ProvincesStatusInp) (err error) {
|
||||
if _, err = dao.SysProvinces.Ctx(ctx).Where("id", in.Id).Data("status", in.Status).Update(); err != nil {
|
||||
err = gerror.Wrap(err, "更新省市区状态失败!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MaxSort 最大排序
|
||||
func (s *sSysProvinces) MaxSort(ctx context.Context, in *sysin.ProvincesMaxSortInp) (res *sysin.ProvincesMaxSortModel, err error) {
|
||||
if err = dao.SysProvinces.Ctx(ctx).Fields(dao.SysProvinces.Columns().Sort).OrderDesc(dao.SysProvinces.Columns().Sort).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取省市区最大排序失败!")
|
||||
return
|
||||
}
|
||||
|
||||
if res == nil {
|
||||
res = new(sysin.ProvincesMaxSortModel)
|
||||
}
|
||||
res.Sort = form.DefaultMaxSort(res.Sort)
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取省市区信息
|
||||
func (s *sSysProvinces) View(ctx context.Context, in *sysin.ProvincesViewInp) (res *sysin.ProvincesViewModel, err error) {
|
||||
if err = dao.SysProvinces.Ctx(ctx).Where("id", in.Id).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取省市区信息失败!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// List 获取列表
|
||||
func (s *sSysProvinces) List(ctx context.Context, in *sysin.ProvincesListInp) (list []*sysin.ProvincesListModel, totalCount int, err error) {
|
||||
mod := dao.SysProvinces.Ctx(ctx)
|
||||
|
||||
if in.Title != "" {
|
||||
mod = mod.WhereLike("title", "%"+in.Title+"%")
|
||||
}
|
||||
|
||||
if in.Status > 0 {
|
||||
mod = mod.Where("status", in.Status)
|
||||
}
|
||||
|
||||
totalCount, err = mod.Count()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, "获取省市区数据行失败!")
|
||||
return
|
||||
}
|
||||
|
||||
if totalCount == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if err = mod.Page(in.Page, in.PerPage).Order("id desc").Scan(&list); err != nil {
|
||||
err = gerror.Wrap(err, "获取省市区列表失败!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ChildrenList 获取省市区下级列表
|
||||
func (s *sSysProvinces) ChildrenList(ctx context.Context, in *sysin.ProvincesChildrenListInp) (list []*sysin.ProvincesChildrenListModel, totalCount int, err error) {
|
||||
mod := dao.SysProvinces.Ctx(ctx)
|
||||
|
||||
if in.Title != "" {
|
||||
mod = mod.WhereLike("title", "%"+in.Title+"%")
|
||||
}
|
||||
|
||||
if in.Pid > 0 {
|
||||
mod = mod.Where("pid", in.Pid)
|
||||
}
|
||||
|
||||
if in.Id > 0 {
|
||||
mod = mod.Where("id", in.Id)
|
||||
}
|
||||
|
||||
totalCount, err = mod.Count()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, "获取省市区下级数据行失败!")
|
||||
return
|
||||
}
|
||||
|
||||
if totalCount == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if err = mod.Page(in.Page, in.PerPage).Order("sort asc,id desc").Scan(&list); err != nil {
|
||||
err = gerror.Wrap(err, "获取省市区下级列表失败!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UniqueId 获取省市区下级列表
|
||||
func (s *sSysProvinces) UniqueId(ctx context.Context, in *sysin.ProvincesUniqueIdInp) (res *sysin.ProvincesUniqueIdModel, err error) {
|
||||
res = new(sysin.ProvincesUniqueIdModel)
|
||||
res.IsUnique = true
|
||||
if in.NewId == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if err = hgorm.IsUnique(ctx, &dao.SysProvinces, g.Map{dao.SysProvinces.Columns().Id: in.NewId}, "", in.OldId); err != nil {
|
||||
res.IsUnique = false
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Select 省市区选项
|
||||
func (s *sSysProvinces) Select(ctx context.Context, in *sysin.ProvincesSelectInp) (res *sysin.ProvincesSelectModel, err error) {
|
||||
res = new(sysin.ProvincesSelectModel)
|
||||
cols := dao.SysProvinces.Columns()
|
||||
mod := dao.SysProvinces.Ctx(ctx).Fields(fmt.Sprintf("%s as value, %s as label, %s ", cols.Id, cols.Title, cols.Level)).Where(cols.Pid, in.Value)
|
||||
|
||||
if err = mod.OrderAsc(cols.Sort).OrderAsc(cols.Id).Scan(&res.List); err != nil {
|
||||
err = gerror.Wrap(err, "获取省市区选项失败!")
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range res.List {
|
||||
if in.DataType == "p" {
|
||||
v.IsLeaf = true
|
||||
continue
|
||||
}
|
||||
|
||||
if in.DataType == "pc" && v.Level >= 2 {
|
||||
v.IsLeaf = true
|
||||
continue
|
||||
}
|
||||
|
||||
if in.DataType == "pca" && v.Level >= 3 {
|
||||
v.IsLeaf = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// treeList 树状列表
|
||||
func (s *sSysProvinces) treeList(pid int64, nodes []*entity.SysProvinces) (list []*sysin.ProvincesTree) {
|
||||
list = make([]*sysin.ProvincesTree, 0)
|
||||
for _, v := range nodes {
|
||||
if v.Pid == pid {
|
||||
item := new(sysin.ProvincesTree)
|
||||
item.SysProvinces = *v
|
||||
item.Label = v.Title
|
||||
item.Value = v.Id
|
||||
item.Key = v.Id
|
||||
|
||||
child := s.treeList(v.Id, nodes)
|
||||
if len(child) > 0 {
|
||||
item.Children = child
|
||||
}
|
||||
list = append(list, item)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
// Package sys
|
||||
// @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
|
||||
// @AutoGenerate Version 2.7.6
|
||||
package sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"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/gctx"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/hgorm"
|
||||
"hotgo/internal/library/hgorm/handler"
|
||||
"hotgo/internal/model/input/form"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
"hotgo/utility/convert"
|
||||
"hotgo/utility/excel"
|
||||
)
|
||||
|
||||
type sSysServeLicense struct{}
|
||||
|
||||
func NewSysServeLicense() *sSysServeLicense {
|
||||
return &sSysServeLicense{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysServeLicense(NewSysServeLicense())
|
||||
}
|
||||
|
||||
// Model 服务许可证ORM模型
|
||||
func (s *sSysServeLicense) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
||||
return handler.Model(dao.SysServeLicense.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 获取服务许可证列表
|
||||
func (s *sSysServeLicense) List(ctx context.Context, in *sysin.ServeLicenseListInp) (list []*sysin.ServeLicenseListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx)
|
||||
|
||||
// 查询许可ID
|
||||
if in.Id > 0 {
|
||||
mod = mod.Where(dao.SysServeLicense.Columns().Id, in.Id)
|
||||
}
|
||||
|
||||
// 查询分组
|
||||
if in.Group != "" {
|
||||
mod = mod.Where(dao.SysServeLicense.Columns().Group, in.Group)
|
||||
}
|
||||
|
||||
// 查询许可名称
|
||||
if in.Name != "" {
|
||||
mod = mod.WhereLike(dao.SysServeLicense.Columns().Name, "%"+in.Name+"%")
|
||||
}
|
||||
|
||||
// 查询应用ID
|
||||
if in.Appid != "" {
|
||||
mod = mod.Where(dao.SysServeLicense.Columns().Appid, in.Appid)
|
||||
}
|
||||
|
||||
// 查询授权结束时间
|
||||
if len(in.EndAt) == 2 {
|
||||
mod = mod.WhereBetween(dao.SysServeLicense.Columns().EndAt, in.EndAt[0], in.EndAt[1])
|
||||
}
|
||||
|
||||
// 查询状态
|
||||
if in.Status > 0 {
|
||||
mod = mod.Where(dao.SysServeLicense.Columns().Status, in.Status)
|
||||
}
|
||||
|
||||
// 查询创建时间
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween(dao.SysServeLicense.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
totalCount, err = mod.Clone().Count()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, "获取服务许可证数据行失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
|
||||
if totalCount == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if err = mod.Fields(sysin.ServeLicenseListModel{}).Page(in.Page, in.PerPage).OrderDesc(dao.SysServeLicense.Columns().Id).Scan(&list); err != nil {
|
||||
err = gerror.Wrap(err, "获取服务许可证列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
|
||||
serv := service.TCPServer().Instance()
|
||||
for _, v := range list {
|
||||
v.Online = serv.GetAppIdOnline(v.Appid)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Export 导出服务许可证
|
||||
func (s *sSysServeLicense) Export(ctx context.Context, in *sysin.ServeLicenseListInp) (err error) {
|
||||
list, totalCount, err := s.List(ctx, in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
|
||||
tags, err := convert.GetEntityDescTags(sysin.ServeLicenseExportModel{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
fileName = "导出服务许可证-" + gctx.CtxId(ctx)
|
||||
sheetName = fmt.Sprintf("索引条件共%v行,共%v页,当前导出是第%v页,本页共%v行", totalCount, form.CalPageCount(totalCount, in.PerPage), in.Page, len(list))
|
||||
exports []sysin.ServeLicenseExportModel
|
||||
)
|
||||
|
||||
if err = gconv.Scan(list, &exports); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增服务许可证
|
||||
func (s *sSysServeLicense) Edit(ctx context.Context, in *sysin.ServeLicenseEditInp) (err error) {
|
||||
// 验证'Appid'唯一
|
||||
if err = hgorm.IsUnique(ctx, &dao.SysServeLicense, g.Map{dao.SysServeLicense.Columns().Appid: in.Appid}, "应用ID已存在", in.Id); err != nil {
|
||||
return
|
||||
}
|
||||
// 修改
|
||||
if in.Id > 0 {
|
||||
if _, err = s.Model(ctx).Fields(sysin.ServeLicenseUpdateFields{}).WherePri(in.Id).Data(in).Update(); err != nil {
|
||||
err = gerror.Wrap(err, "修改服务许可证失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 新增
|
||||
if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).Fields(sysin.ServeLicenseInsertFields{}).Data(in).OmitEmptyData().Insert(); err != nil {
|
||||
err = gerror.Wrap(err, "新增服务许可证失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete 删除服务许可证
|
||||
func (s *sSysServeLicense) Delete(ctx context.Context, in *sysin.ServeLicenseDeleteInp) (err error) {
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除服务许可证失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取服务许可证指定信息
|
||||
func (s *sSysServeLicense) View(ctx context.Context, in *sysin.ServeLicenseViewInp) (res *sysin.ServeLicenseViewModel, err error) {
|
||||
if err = s.Model(ctx).WherePri(in.Id).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取服务许可证信息,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Status 更新服务许可证状态
|
||||
func (s *sSysServeLicense) Status(ctx context.Context, in *sysin.ServeLicenseStatusInp) (err error) {
|
||||
update := g.Map{
|
||||
dao.SysServeLicense.Columns().Status: in.Status,
|
||||
}
|
||||
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Data(update).Update(); err != nil {
|
||||
err = gerror.Wrap(err, "更新服务许可证状态失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AssignRouter 分配服务许可证路由
|
||||
func (s *sSysServeLicense) AssignRouter(ctx context.Context, in *sysin.ServeLicenseAssignRouterInp) (err error) {
|
||||
update := g.Map{
|
||||
dao.SysServeLicense.Columns().Routes: in.Routes,
|
||||
}
|
||||
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Data(update).Update(); err != nil {
|
||||
err = gerror.Wrap(err, "分配服务许可证路由失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
// Package sys
|
||||
// @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 sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/gmode"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/hgorm"
|
||||
"hotgo/internal/library/hgorm/handler"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/form"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
"hotgo/utility/convert"
|
||||
"hotgo/utility/excel"
|
||||
)
|
||||
|
||||
type sSysServeLog struct{}
|
||||
|
||||
func NewSysServeLog() *sSysServeLog {
|
||||
return &sSysServeLog{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysServeLog(NewSysServeLog())
|
||||
}
|
||||
|
||||
// Model 服务日志Orm模型
|
||||
func (s *sSysServeLog) Model(ctx context.Context) *gdb.Model {
|
||||
return dao.SysServeLog.Ctx(ctx)
|
||||
}
|
||||
|
||||
// List 获取服务日志列表
|
||||
func (s *sSysServeLog) List(ctx context.Context, in *sysin.ServeLogListInp) (list []*sysin.ServeLogListModel, totalCount int, err error) {
|
||||
mod := dao.SysServeLog.Ctx(ctx)
|
||||
|
||||
// 查询链路ID
|
||||
if in.TraceId != "" {
|
||||
mod = mod.Where(dao.SysServeLog.Columns().TraceId, in.TraceId)
|
||||
}
|
||||
|
||||
// 非生产环境,允许关键词查询日志
|
||||
// 生成环境使用需谨慎,日志量大易产生慢日志
|
||||
if !gmode.IsProduct() && in.Content != "" {
|
||||
mod = mod.WhereLike(dao.SysServeLog.Columns().Content, "%"+in.Content+"%")
|
||||
}
|
||||
|
||||
// 查询日志级别
|
||||
if in.LevelFormat != "" {
|
||||
mod = mod.WhereLike(dao.SysServeLog.Columns().LevelFormat, in.LevelFormat)
|
||||
}
|
||||
|
||||
// 查询触发时间(ns)
|
||||
if len(in.TriggerNs) == 2 {
|
||||
mod = mod.WhereBetween(dao.SysServeLog.Columns().TriggerNs, in.TriggerNs[0], in.TriggerNs[1])
|
||||
}
|
||||
|
||||
// 查询创建时间
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween(dao.SysServeLog.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
// 关联表sysLog
|
||||
mod = mod.LeftJoin(hgorm.GenJoinOnRelation(
|
||||
dao.SysServeLog.Table(), dao.SysServeLog.Columns().TraceId, // 主表表名,关联条件
|
||||
dao.SysLog.Table(), "sysLog", dao.SysLog.Columns().ReqId, // 关联表表名,别名,关联条件
|
||||
)...)
|
||||
|
||||
totalCount, err = mod.Clone().Count()
|
||||
if err != nil || totalCount == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// 关联表select
|
||||
fields, err := hgorm.GenJoinSelect(ctx, sysin.ServeLogListModel{}, &dao.SysServeLog, []*hgorm.Join{
|
||||
{Dao: &dao.SysLog, Alias: "sysLog"},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = mod.Fields(fields).Handler(handler.FilterAuth).Page(in.Page, in.PerPage).OrderDesc(dao.SysServeLog.Columns().Id).Scan(&list)
|
||||
return
|
||||
}
|
||||
|
||||
// Export 导出服务日志
|
||||
func (s *sSysServeLog) Export(ctx context.Context, in *sysin.ServeLogListInp) (err error) {
|
||||
list, totalCount, err := s.List(ctx, in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
|
||||
tags, err := convert.GetEntityDescTags(sysin.ServeLogExportModel{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
fileName = "导出服务日志-" + gctx.CtxId(ctx)
|
||||
sheetName = fmt.Sprintf("索引条件共%v行,共%v页,当前导出是第%v页,本页共%v行", totalCount, form.CalPageCount(totalCount, in.PerPage), in.Page, len(list))
|
||||
exports []sysin.ServeLogExportModel
|
||||
)
|
||||
|
||||
if err = gconv.Scan(list, &exports); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete 删除服务日志
|
||||
func (s *sSysServeLog) Delete(ctx context.Context, in *sysin.ServeLogDeleteInp) (err error) {
|
||||
_, err = dao.SysServeLog.Ctx(ctx).Where(dao.SysServeLog.Columns().Id, in.Id).Delete()
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取服务日志指定信息
|
||||
func (s *sSysServeLog) View(ctx context.Context, in *sysin.ServeLogViewInp) (res *sysin.ServeLogViewModel, err error) {
|
||||
err = dao.SysServeLog.Ctx(ctx).Where(dao.SysServeLog.Columns().Id, in.Id).Scan(&res)
|
||||
return
|
||||
}
|
||||
|
||||
// RealWrite 真实写入
|
||||
func (s *sSysServeLog) RealWrite(ctx context.Context, models entity.SysServeLog) (err error) {
|
||||
_, err = dao.SysServeLog.Ctx(ctx).FieldsEx(dao.SysLog.Columns().Id).Data(models).OmitEmptyData().Insert()
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
// Package sys
|
||||
// @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 sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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/gtime"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/location"
|
||||
"hotgo/internal/library/sms"
|
||||
"hotgo/internal/model"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
"time"
|
||||
)
|
||||
|
||||
type sSysSmsLog struct{}
|
||||
|
||||
func NewSysSmsLog() *sSysSmsLog {
|
||||
return &sSysSmsLog{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysSmsLog(NewSysSmsLog())
|
||||
}
|
||||
|
||||
// Delete 删除
|
||||
func (s *sSysSmsLog) Delete(ctx context.Context, in *sysin.SmsLogDeleteInp) (err error) {
|
||||
_, err = dao.SysSmsLog.Ctx(ctx).WherePri(in.Id).Delete()
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取指定字典类型信息
|
||||
func (s *sSysSmsLog) View(ctx context.Context, in *sysin.SmsLogViewInp) (res *sysin.SmsLogViewModel, err error) {
|
||||
if err = dao.SysSmsLog.Ctx(ctx).WherePri(in.Id).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// List 获取列表
|
||||
func (s *sSysSmsLog) List(ctx context.Context, in *sysin.SmsLogListInp) (list []*sysin.SmsLogListModel, totalCount int, err error) {
|
||||
mod := dao.SysSmsLog.Ctx(ctx)
|
||||
cols := dao.SysSmsLog.Columns()
|
||||
|
||||
if in.Mobile != "" {
|
||||
mod = mod.WhereLike(cols.Mobile, "%"+in.Mobile+"%")
|
||||
}
|
||||
|
||||
if in.Ip != "" {
|
||||
mod = mod.Where(cols.Ip, in.Ip)
|
||||
}
|
||||
|
||||
if in.Event != "" {
|
||||
mod = mod.Where(cols.Event, in.Event)
|
||||
}
|
||||
|
||||
if in.Status > 0 {
|
||||
mod = mod.Where(cols.Status, in.Status)
|
||||
}
|
||||
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween(cols.CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
totalCount, err = mod.Count()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return
|
||||
}
|
||||
|
||||
if totalCount == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if err = mod.Page(in.Page, in.PerPage).OrderDesc(cols.Id).Scan(&list); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SendCode 发送验证码
|
||||
func (s *sSysSmsLog) SendCode(ctx context.Context, in *sysin.SendCodeInp) (err error) {
|
||||
if in.Event == "" {
|
||||
err = gerror.New("事件不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
if in.Mobile == "" {
|
||||
err = gerror.New("手机号不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
var models *entity.SysSmsLog
|
||||
if err = dao.SysSmsLog.Ctx(ctx).
|
||||
Where(dao.SysSmsLog.Columns().Event, in.Event).
|
||||
Where(dao.SysSmsLog.Columns().Mobile, in.Mobile).
|
||||
OrderDesc(dao.SysSmsLog.Columns().Id).
|
||||
Scan(&models); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return
|
||||
}
|
||||
|
||||
config, err := service.SysConfig().GetSms(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if in.Template, err = s.GetTemplate(ctx, in.Event, config); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.AllowSend(ctx, models, config); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if in.Code == "" {
|
||||
in.Code = grand.Digits(4)
|
||||
}
|
||||
|
||||
if err = sms.New(config.SmsDrive).SendCode(ctx, in); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var data = new(entity.SysSmsLog)
|
||||
data.Event = in.Event
|
||||
data.Mobile = in.Mobile
|
||||
data.Code = in.Code
|
||||
data.Ip = location.GetClientIp(ghttp.RequestFromCtx(ctx))
|
||||
data.Status = consts.CodeStatusNotUsed
|
||||
data.CreatedAt = gtime.Now()
|
||||
data.UpdatedAt = gtime.Now()
|
||||
|
||||
_, err = dao.SysSmsLog.Ctx(ctx).Data(data).OmitEmptyData().Insert()
|
||||
return
|
||||
}
|
||||
|
||||
// GetTemplate 获取指定短信模板
|
||||
func (s *sSysSmsLog) GetTemplate(ctx context.Context, template string, config *model.SmsConfig) (val string, err error) {
|
||||
if template == "" {
|
||||
err = gerror.New("模板不能为空")
|
||||
return
|
||||
}
|
||||
if config == nil {
|
||||
config, err = service.SysConfig().GetSms(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
switch config.SmsDrive {
|
||||
case consts.SmsDriveAliYun:
|
||||
if len(config.AliYunTemplate) == 0 {
|
||||
err = gerror.New("管理员还没有配置任何阿里云短信模板!")
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range config.AliYunTemplate {
|
||||
if v.Key == template {
|
||||
return v.Value, nil
|
||||
}
|
||||
}
|
||||
|
||||
case consts.SmsDriveTencent:
|
||||
if len(config.TencentTemplate) == 0 {
|
||||
err = gerror.New("管理员还没有配置任何腾讯云短信模板!")
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range config.TencentTemplate {
|
||||
if v.Key == template {
|
||||
return v.Value, nil
|
||||
}
|
||||
}
|
||||
default:
|
||||
err = gerror.Newf("暂不支持短信驱动:%v", config.SmsDrive)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AllowSend 是否允许发送
|
||||
func (s *sSysSmsLog) AllowSend(ctx context.Context, models *entity.SysSmsLog, config *model.SmsConfig) (err error) {
|
||||
if models == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
if config, err = service.SysConfig().GetSms(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if gtime.Now().Before(models.CreatedAt.Add(time.Second * time.Duration(config.SmsMinInterval))) {
|
||||
err = gerror.New("发送频繁,请稍后再试!")
|
||||
return
|
||||
}
|
||||
|
||||
if config.SmsMaxIpLimit > 0 {
|
||||
count, err := s.NowDayIpSendCount(ctx, models.Event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if count >= config.SmsMaxIpLimit {
|
||||
err = gerror.New("今天发送短信过多,请次日后再试!")
|
||||
return err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NowDayIpSendCount 当天IP累计发送次数
|
||||
func (s *sSysSmsLog) NowDayIpSendCount(ctx context.Context, event string) (count int, err error) {
|
||||
return dao.SysSmsLog.Ctx(ctx).
|
||||
Where("ip", location.GetClientIp(ghttp.RequestFromCtx(ctx))).
|
||||
Where("event", event).
|
||||
WhereGTE("created_at", gtime.Now().Format("Y-m-d")).
|
||||
Count()
|
||||
}
|
||||
|
||||
// VerifyCode 效验验证码
|
||||
func (s *sSysSmsLog) VerifyCode(ctx context.Context, in *sysin.VerifyCodeInp) (err error) {
|
||||
if in.Event == "" {
|
||||
err = gerror.New("事件不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
if in.Mobile == "" {
|
||||
err = gerror.New("手机号不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
config, err := service.SysConfig().GetSms(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var models *entity.SysSmsLog
|
||||
cols := dao.SysSmsLog.Columns()
|
||||
if err = dao.SysSmsLog.Ctx(ctx).Where(cols.Event, in.Event).Where(cols.Mobile, in.Mobile).OrderDesc(cols.Id).Scan(&models); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return
|
||||
}
|
||||
|
||||
if models == nil {
|
||||
err = gerror.New("验证码错误")
|
||||
return
|
||||
}
|
||||
|
||||
if models.Times >= 10 {
|
||||
err = gerror.New("验证码错误次数过多,请重新发送!")
|
||||
return
|
||||
}
|
||||
|
||||
if in.Event != consts.SmsTemplateCode {
|
||||
if models.Status == consts.CodeStatusUsed {
|
||||
err = gerror.New("验证码已使用,请重新发送!")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if gtime.Now().After(models.CreatedAt.Add(time.Second * time.Duration(config.SmsCodeExpire))) {
|
||||
err = gerror.New("验证码已过期,请重新发送")
|
||||
return
|
||||
}
|
||||
|
||||
if models.Code != in.Code {
|
||||
_, _ = dao.SysSmsLog.Ctx(ctx).WherePri(models.Id).Increment(cols.Times, 1)
|
||||
err = gerror.New("验证码错误!")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = dao.SysSmsLog.Ctx(ctx).WherePri(models.Id).Data(g.Map{
|
||||
cols.Times: models.Times + 1,
|
||||
cols.Status: consts.CodeStatusUsed,
|
||||
cols.UpdatedAt: gtime.Now(),
|
||||
}).Update()
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// Package sys
|
||||
// @Link https://github.com/bufanyun/hotgo
|
||||
// @Copyright Copyright (c) 2024 HotGo CLI
|
||||
// @Author Ms <133814250@qq.com>
|
||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||
// @AutoGenerate Version 2.15.7
|
||||
package sys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/hgorm/handler"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/form"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
|
||||
"hotgo/internal/library/dict"
|
||||
"hotgo/internal/model"
|
||||
)
|
||||
|
||||
type sSysTestCategory struct{}
|
||||
|
||||
func NewSysTestCategory() *sSysTestCategory {
|
||||
return &sSysTestCategory{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterSysTestCategory(NewSysTestCategory())
|
||||
dict.RegisterFunc("testCategoryOption", "测试分类选项", service.SysTestCategory().Option)
|
||||
}
|
||||
|
||||
// Model 测试分类ORM模型
|
||||
func (s *sSysTestCategory) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
||||
return handler.Model(dao.TestCategory.Ctx(ctx), option...)
|
||||
}
|
||||
|
||||
// List 获取测试分类列表
|
||||
func (s *sSysTestCategory) List(ctx context.Context, in *sysin.TestCategoryListInp) (list []*sysin.TestCategoryListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx)
|
||||
|
||||
// 字段过滤
|
||||
mod = mod.Fields(sysin.TestCategoryListModel{})
|
||||
|
||||
// 查询分类ID
|
||||
if in.Id > 0 {
|
||||
mod = mod.Where(dao.TestCategory.Columns().Id, in.Id)
|
||||
}
|
||||
|
||||
// 查询分类名称
|
||||
if in.Name != "" {
|
||||
mod = mod.WhereLike(dao.TestCategory.Columns().Name, "%"+in.Name+"%")
|
||||
}
|
||||
|
||||
// 查询状态
|
||||
if in.Status > 0 {
|
||||
mod = mod.Where(dao.TestCategory.Columns().Status, in.Status)
|
||||
}
|
||||
|
||||
// 查询创建时间
|
||||
if len(in.CreatedAt) == 2 {
|
||||
mod = mod.WhereBetween(dao.TestCategory.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||
}
|
||||
|
||||
// 分页
|
||||
mod = mod.Page(in.Page, in.PerPage)
|
||||
|
||||
// 排序
|
||||
mod = mod.OrderAsc(dao.TestCategory.Columns().Sort).OrderDesc(dao.TestCategory.Columns().Id)
|
||||
|
||||
// 查询数据
|
||||
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
|
||||
err = gerror.Wrap(err, "获取测试分类列表失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增测试分类
|
||||
func (s *sSysTestCategory) Edit(ctx context.Context, in *sysin.TestCategoryEditInp) (err error) {
|
||||
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||
|
||||
// 修改
|
||||
if in.Id > 0 {
|
||||
if _, err = s.Model(ctx).
|
||||
Fields(sysin.TestCategoryUpdateFields{}).
|
||||
WherePri(in.Id).Data(in).Update(); err != nil {
|
||||
err = gerror.Wrap(err, "修改测试分类失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 新增
|
||||
if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).
|
||||
Fields(sysin.TestCategoryInsertFields{}).
|
||||
Data(in).OmitEmptyData().Insert(); err != nil {
|
||||
err = gerror.Wrap(err, "新增测试分类失败,请稍后重试!")
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// Delete 删除测试分类
|
||||
func (s *sSysTestCategory) Delete(ctx context.Context, in *sysin.TestCategoryDeleteInp) (err error) {
|
||||
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Unscoped().Delete(); err != nil {
|
||||
err = gerror.Wrap(err, "删除测试分类失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MaxSort 获取测试分类最大排序
|
||||
func (s *sSysTestCategory) MaxSort(ctx context.Context, in *sysin.TestCategoryMaxSortInp) (res *sysin.TestCategoryMaxSortModel, err error) {
|
||||
if err = dao.TestCategory.Ctx(ctx).Fields(dao.TestCategory.Columns().Sort).OrderDesc(dao.TestCategory.Columns().Sort).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取测试分类最大排序,请稍后重试!")
|
||||
return
|
||||
}
|
||||
|
||||
if res == nil {
|
||||
res = new(sysin.TestCategoryMaxSortModel)
|
||||
}
|
||||
|
||||
res.Sort = form.DefaultMaxSort(res.Sort)
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取测试分类指定信息
|
||||
func (s *sSysTestCategory) View(ctx context.Context, in *sysin.TestCategoryViewInp) (res *sysin.TestCategoryViewModel, err error) {
|
||||
if err = s.Model(ctx).WherePri(in.Id).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "获取测试分类信息,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Status 更新测试分类状态
|
||||
func (s *sSysTestCategory) Status(ctx context.Context, in *sysin.TestCategoryStatusInp) (err error) {
|
||||
if _, err = s.Model(ctx).WherePri(in.Id).Data(g.Map{
|
||||
dao.TestCategory.Columns().Status: in.Status,
|
||||
}).Update(); err != nil {
|
||||
err = gerror.Wrap(err, "更新测试分类状态失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Option 获取测试分类选项
|
||||
func (s *sSysTestCategory) Option(ctx context.Context) (opts []*model.Option, err error) {
|
||||
var models []*entity.TestCategory
|
||||
if err = s.Model(ctx).Fields(dao.TestCategory.Columns().Id, dao.TestCategory.Columns().Name).
|
||||
OrderAsc(dao.TestCategory.Columns().Sort).OrderDesc(dao.TestCategory.Columns().Id).Scan(&models); err != nil {
|
||||
err = gerror.Wrap(err, "获取测试分类选项失败!")
|
||||
return
|
||||
}
|
||||
|
||||
opts = make([]*model.Option, len(models))
|
||||
for k, v := range models {
|
||||
opts[k] = dict.GenHashOption(v.Id, gconv.String(v.Name))
|
||||
}
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user