chore: seed migration objects batch 13
This commit is contained in:
@@ -0,0 +1,202 @@
|
|||||||
|
// Package admin
|
||||||
|
// @Link https://github.com/bufanyun/hotgo
|
||||||
|
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||||
|
// @Author Ms <133814250@qq.com>
|
||||||
|
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||||
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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"
|
||||||
|
"hotgo/internal/model/entity"
|
||||||
|
"hotgo/internal/model/input/adminin"
|
||||||
|
"hotgo/internal/model/input/form"
|
||||||
|
"hotgo/internal/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sAdminPost struct{}
|
||||||
|
|
||||||
|
func NewAdminPost() *sAdminPost {
|
||||||
|
return &sAdminPost{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
service.RegisterAdminPost(NewAdminPost())
|
||||||
|
dict.RegisterFunc("adminPostOption", "岗位选项", service.AdminPost().Option)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 删除
|
||||||
|
func (s *sAdminPost) Delete(ctx context.Context, in *adminin.PostDeleteInp) (err error) {
|
||||||
|
exist, err := dao.AdminMemberPost.Ctx(ctx).Where(dao.AdminMemberPost.Columns().PostId, in.Id).One()
|
||||||
|
if err != nil {
|
||||||
|
err = gerror.Wrap(err, consts.ErrorORM)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !exist.IsEmpty() {
|
||||||
|
return gerror.New("请先解除该岗位下所有已关联用户关联关系!")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = dao.AdminPost.Ctx(ctx).WherePri(in.Id).Delete()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyUnique 验证部门唯一属性
|
||||||
|
func (s *sAdminPost) VerifyUnique(ctx context.Context, in *adminin.VerifyUniqueInp) (err error) {
|
||||||
|
if in.Where == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cols := dao.AdminPost.Columns()
|
||||||
|
msgMap := g.MapStrStr{
|
||||||
|
cols.Name: "岗位名称已存在,请换一个",
|
||||||
|
cols.Code: "岗位编码已存在,请换一个",
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range in.Where {
|
||||||
|
if v == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
message, ok := msgMap[k]
|
||||||
|
if !ok {
|
||||||
|
err = gerror.Newf("字段 [ %v ] 未配置唯一属性验证", k)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = hgorm.IsUnique(ctx, &dao.AdminPost, g.Map{k: v}, message, in.Id); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Edit 修改/新增
|
||||||
|
func (s *sAdminPost) Edit(ctx context.Context, in *adminin.PostEditInp) (err error) {
|
||||||
|
// 验证唯一性
|
||||||
|
err = s.VerifyUnique(ctx, &adminin.VerifyUniqueInp{
|
||||||
|
Id: in.Id,
|
||||||
|
Where: g.Map{
|
||||||
|
dao.AdminPost.Columns().Name: in.Name,
|
||||||
|
dao.AdminPost.Columns().Code: in.Code,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改
|
||||||
|
if in.Id > 0 {
|
||||||
|
_, err = dao.AdminPost.Ctx(ctx).WherePri(in.Id).Data(in).Update()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
_, err = dao.AdminPost.Ctx(ctx).Data(in).OmitEmptyData().Insert()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxSort 最大排序
|
||||||
|
func (s *sAdminPost) MaxSort(ctx context.Context, in *adminin.PostMaxSortInp) (res *adminin.PostMaxSortModel, err error) {
|
||||||
|
if in.Id > 0 {
|
||||||
|
if err = dao.AdminPost.Ctx(ctx).WherePri(in.Id).OrderDesc(dao.AdminPost.Columns().Sort).Scan(&res); err != nil {
|
||||||
|
err = gerror.Wrap(err, consts.ErrorORM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if res == nil {
|
||||||
|
res = new(adminin.PostMaxSortModel)
|
||||||
|
}
|
||||||
|
res.Sort = form.DefaultMaxSort(res.Sort)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// View 获取指定岗位信息
|
||||||
|
func (s *sAdminPost) View(ctx context.Context, in *adminin.PostViewInp) (res *adminin.PostViewModel, err error) {
|
||||||
|
err = dao.AdminPost.Ctx(ctx).WherePri(in.Id).Scan(&res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// List 获取列表
|
||||||
|
func (s *sAdminPost) List(ctx context.Context, in *adminin.PostListInp) (list []*adminin.PostListModel, totalCount int, err error) {
|
||||||
|
mod := dao.AdminPost.Ctx(ctx)
|
||||||
|
cols := dao.AdminPost.Columns()
|
||||||
|
|
||||||
|
// 访问路径
|
||||||
|
if in.Name != "" {
|
||||||
|
mod = mod.WhereLike(cols.Name, "%"+in.Name+"%")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模块
|
||||||
|
if in.Code != "" {
|
||||||
|
mod = mod.Where(cols.Code, in.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求方式
|
||||||
|
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, "获取岗位列表失败!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if totalCount == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mod.Page(in.Page, in.PerPage).OrderAsc(cols.Sort).Scan(&list)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option 岗位选项
|
||||||
|
func (s *sAdminPost) Option(ctx context.Context) (opts []*model.Option, err error) {
|
||||||
|
var list []*entity.AdminPost
|
||||||
|
if err = dao.AdminPost.Ctx(ctx).OrderAsc(dao.AdminPost.Columns().Sort).Scan(&list); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(list) == 0 {
|
||||||
|
opts = make([]*model.Option, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range list {
|
||||||
|
opts = append(opts, dict.GenHashOption(v.Id, v.Name))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMemberByStartName 获取指定用户的第一岗位
|
||||||
|
func (s *sAdminPost) GetMemberByStartName(ctx context.Context, memberId int64) (name string, err error) {
|
||||||
|
// 默认取第一岗位
|
||||||
|
postId, err := dao.AdminMemberPost.Ctx(ctx).Fields(dao.AdminMemberPost.Columns().PostId).Where(dao.AdminMemberPost.Columns().MemberId, memberId).Limit(1).Value()
|
||||||
|
if err != nil {
|
||||||
|
err = gerror.Wrap(err, consts.ErrorORM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err := dao.AdminPost.Ctx(ctx).Fields(dao.AdminPost.Columns().Name).WherePri(postId.Int()).OrderDesc(dao.AdminPost.Columns().Id).Value()
|
||||||
|
if err != nil {
|
||||||
|
err = gerror.Wrap(err, consts.ErrorORM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return val.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status 更新状态
|
||||||
|
func (s *sAdminPost) Status(ctx context.Context, in *adminin.PostStatusInp) (err error) {
|
||||||
|
_, err = dao.AdminPost.Ctx(ctx).WherePri(in.Id).Data(dao.AdminPost.Columns().Status, in.Status).Update()
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,356 @@
|
|||||||
|
// Package admin
|
||||||
|
// @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
|
||||||
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"hotgo/internal/consts"
|
||||||
|
"hotgo/internal/dao"
|
||||||
|
"hotgo/internal/library/casbin"
|
||||||
|
"hotgo/internal/library/contexts"
|
||||||
|
"hotgo/internal/library/hgorm"
|
||||||
|
"hotgo/internal/model/entity"
|
||||||
|
"hotgo/internal/model/input/adminin"
|
||||||
|
"hotgo/internal/model/input/form"
|
||||||
|
"hotgo/internal/service"
|
||||||
|
"hotgo/utility/convert"
|
||||||
|
"hotgo/utility/tree"
|
||||||
|
"hotgo/utility/validate"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sAdminRole struct{}
|
||||||
|
|
||||||
|
func NewAdminRole() *sAdminRole {
|
||||||
|
return &sAdminRole{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
service.RegisterAdminRole(NewAdminRole())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify 验证权限
|
||||||
|
func (s *sAdminRole) Verify(ctx context.Context, path, method string) bool {
|
||||||
|
var (
|
||||||
|
user = contexts.Get(ctx).User
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if user == nil {
|
||||||
|
g.Log().Info(ctx, "admin Verify user = nil")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if service.AdminMember().VerifySuperId(ctx, user.Id) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := casbin.Enforcer.Enforce(user.RoleKey, path, method)
|
||||||
|
if err != nil {
|
||||||
|
g.Log().Infof(ctx, "admin Verify Enforce err:%+v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// List 获取列表
|
||||||
|
func (s *sAdminRole) List(ctx context.Context, in *adminin.RoleListInp) (res *adminin.RoleListModel, totalCount int, err error) {
|
||||||
|
var (
|
||||||
|
mod = dao.AdminRole.Ctx(ctx)
|
||||||
|
models []*entity.AdminRole
|
||||||
|
pid int64 = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
// 非超管只获取下级角色
|
||||||
|
if !service.AdminMember().VerifySuperId(ctx, contexts.GetUserId(ctx)) {
|
||||||
|
pid = contexts.GetRoleId(ctx)
|
||||||
|
mod = mod.WhereLike(dao.AdminRole.Columns().Tree, "%"+tree.GetIdLabel(pid)+"%")
|
||||||
|
}
|
||||||
|
|
||||||
|
totalCount, err = mod.Count()
|
||||||
|
if err != nil {
|
||||||
|
err = gerror.Wrap(err, consts.ErrorORM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = mod.Page(in.Page, in.PerPage).Order("sort asc,id asc").Scan(&models); err != nil {
|
||||||
|
err = gerror.Wrap(err, consts.ErrorORM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res = new(adminin.RoleListModel)
|
||||||
|
res.List = s.treeList(pid, models)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName 获取指定角色的名称
|
||||||
|
func (s *sAdminRole) GetName(ctx context.Context, id int64) (name string, err error) {
|
||||||
|
r, err := dao.AdminRole.Ctx(ctx).Fields(dao.AdminRole.Columns().Name).WherePri(id).OrderDesc(dao.AdminRole.Columns().Id).Value()
|
||||||
|
if err != nil {
|
||||||
|
err = gerror.Wrap(err, consts.ErrorORM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return r.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMemberList 获取指定用户的岗位列表
|
||||||
|
func (s *sAdminRole) GetMemberList(ctx context.Context, id int64) (list []*adminin.RoleListModel, err error) {
|
||||||
|
if err = dao.AdminRole.Ctx(ctx).WherePri(id).OrderDesc(dao.AdminRole.Columns().Id).Scan(&list); err != nil {
|
||||||
|
err = gerror.Wrap(err, consts.ErrorORM)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPermissions 更改角色菜单权限
|
||||||
|
func (s *sAdminRole) GetPermissions(ctx context.Context, in *adminin.GetPermissionsInp) (res *adminin.GetPermissionsModel, err error) {
|
||||||
|
values, err := dao.AdminRoleMenu.Ctx(ctx).Fields(dao.AdminRoleMenu.Columns().MenuId).Where(dao.AdminRoleMenu.Columns().RoleId, in.RoleId).Array()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(values) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res = new(adminin.GetPermissionsModel)
|
||||||
|
for i := 0; i < len(values); i++ {
|
||||||
|
res.MenuIds = append(res.MenuIds, values[i].Int64())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatePermissions 更改角色菜单权限
|
||||||
|
func (s *sAdminRole) UpdatePermissions(ctx context.Context, in *adminin.UpdatePermissionsInp) (err error) {
|
||||||
|
err = dao.AdminRoleMenu.Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||||
|
if _, err = dao.AdminRoleMenu.Ctx(ctx).Where("role_id", in.RoleId).Delete(); err != nil {
|
||||||
|
err = gerror.Wrap(err, consts.ErrorORM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if in.MenuIds = convert.UniqueSlice(in.MenuIds); len(in.MenuIds) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
list := make(g.List, 0, len(in.MenuIds))
|
||||||
|
for _, v := range in.MenuIds {
|
||||||
|
list = append(list, g.Map{
|
||||||
|
"role_id": in.RoleId,
|
||||||
|
"menu_id": v,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = dao.AdminRoleMenu.Ctx(ctx).Data(list).OmitEmptyData().Insert(); err != nil {
|
||||||
|
err = gerror.Wrap(err, consts.ErrorORM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return casbin.Refresh(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sAdminRole) Edit(ctx context.Context, in *adminin.RoleEditInp) (err error) {
|
||||||
|
if err = hgorm.IsUnique(ctx, &dao.AdminRole, g.Map{dao.AdminRole.Columns().Name: in.Name}, "名称已存在", in.Id); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = hgorm.IsUnique(ctx, &dao.AdminRole, g.Map{dao.AdminRole.Columns().Key: in.Key}, "编码已存在", in.Id); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if in.Pid, in.Level, in.Tree, err = hgorm.GenSubTree(ctx, &dao.AdminRole, in.Pid); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改
|
||||||
|
if in.Id > 0 {
|
||||||
|
err = dao.AdminRole.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
||||||
|
// 更新数据
|
||||||
|
_, err = dao.AdminRole.Ctx(ctx).Fields(adminin.RoleUpdateFields{}).WherePri(in.Id).Data(in).Update()
|
||||||
|
if err != nil {
|
||||||
|
err = gerror.Wrap(err, consts.ErrorORM)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果当前角色有子级,更新子级tree关系树
|
||||||
|
return updateRoleChildrenTree(ctx, in.Id, in.Level, in.Tree)
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
if _, err = dao.AdminRole.Ctx(ctx).Fields(adminin.RoleInsertFields{}).Data(in).OmitEmptyData().Insert(); err != nil {
|
||||||
|
err = gerror.Wrap(err, consts.ErrorORM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateRoleChildrenTree(ctx context.Context, _id int64, _level int, _tree string) (err error) {
|
||||||
|
var list []*entity.AdminRole
|
||||||
|
if err = dao.AdminRole.Ctx(ctx).Where("pid", _id).Scan(&list); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, child := range list {
|
||||||
|
child.Level = _level + 1
|
||||||
|
child.Tree = tree.GenLabel(_tree, child.Pid)
|
||||||
|
|
||||||
|
if _, err = dao.AdminRole.Ctx(ctx).Where("id", child.Id).Data("level", child.Level, "tree", child.Tree).Update(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = updateRoleChildrenTree(ctx, child.Id, child.Level, child.Tree); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sAdminRole) Delete(ctx context.Context, in *adminin.RoleDeleteInp) (err error) {
|
||||||
|
var models *entity.AdminRole
|
||||||
|
if err = dao.AdminRole.Ctx(ctx).Where("id", in.Id).Scan(&models); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if models == nil {
|
||||||
|
return gerror.New("数据不存在或已删除!")
|
||||||
|
}
|
||||||
|
|
||||||
|
if models.Key == consts.SuperRoleKey {
|
||||||
|
return gerror.New("超管角色禁止删除!")
|
||||||
|
}
|
||||||
|
|
||||||
|
exist, err := dao.AdminRole.Ctx(ctx).Where("pid", models.Id).Exist()
|
||||||
|
if err != nil {
|
||||||
|
err = gerror.Wrap(err, consts.ErrorORM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if exist {
|
||||||
|
return gerror.New("请先删除该角色下得所有子级!")
|
||||||
|
}
|
||||||
|
|
||||||
|
exist, err = dao.AdminMember.Ctx(ctx).Where("role_id", models.Id).Exist()
|
||||||
|
if err != nil {
|
||||||
|
err = gerror.Wrap(err, consts.ErrorORM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if exist {
|
||||||
|
return gerror.New("请先删除该角色下得所有用户账号!")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = dao.AdminRole.Ctx(ctx).Where("id", in.Id).Delete(); err != nil {
|
||||||
|
err = gerror.Wrap(err, consts.ErrorORM)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sAdminRole) DataScopeSelect() (res form.Selects) {
|
||||||
|
for k, v := range consts.RoleDataNameMap {
|
||||||
|
res = append(res, &form.Select{
|
||||||
|
Value: k,
|
||||||
|
Name: v,
|
||||||
|
Label: v,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
sort.Sort(res)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sAdminRole) DataScopeEdit(ctx context.Context, in *adminin.DataScopeEditInp) (err error) {
|
||||||
|
var models *entity.AdminRole
|
||||||
|
if err = dao.AdminRole.Ctx(ctx).Where("id", in.Id).Scan(&models); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if models == nil {
|
||||||
|
return gerror.New("角色不存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
if models.Key == consts.SuperRoleKey {
|
||||||
|
return gerror.New("超管角色拥有全部权限,无需修改!")
|
||||||
|
}
|
||||||
|
|
||||||
|
if in.DataScope == consts.RoleDataDeptCustom && len(convert.UniqueSlice(in.CustomDept)) == 0 {
|
||||||
|
return gerror.New("自定义权限必须配置自定义部门!")
|
||||||
|
}
|
||||||
|
|
||||||
|
models.DataScope = in.DataScope
|
||||||
|
models.CustomDept = gjson.New(convert.UniqueSlice(in.CustomDept))
|
||||||
|
|
||||||
|
_, err = dao.AdminRole.Ctx(ctx).
|
||||||
|
Fields(dao.AdminRole.Columns().DataScope, dao.AdminRole.Columns().CustomDept).
|
||||||
|
Where("id", in.Id).
|
||||||
|
Data(models).
|
||||||
|
Update()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// treeList 角色树列表
|
||||||
|
func (s *sAdminRole) treeList(pid int64, nodes []*entity.AdminRole) (list []*adminin.RoleTree) {
|
||||||
|
list = make([]*adminin.RoleTree, 0)
|
||||||
|
for _, v := range nodes {
|
||||||
|
if v.Pid == pid {
|
||||||
|
item := new(adminin.RoleTree)
|
||||||
|
item.AdminRole = *v
|
||||||
|
item.Label = v.Name
|
||||||
|
item.Value = v.Id
|
||||||
|
|
||||||
|
child := s.treeList(v.Id, nodes)
|
||||||
|
if len(child) > 0 {
|
||||||
|
item.Children = child
|
||||||
|
}
|
||||||
|
list = append(list, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyRoleId 验证角色ID
|
||||||
|
func (s *sAdminRole) VerifyRoleId(ctx context.Context, id int64) (err error) {
|
||||||
|
mb := contexts.GetUser(ctx)
|
||||||
|
if mb == nil {
|
||||||
|
err = gerror.New("用户信息获取失败!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ids, err := s.GetSubRoleIds(ctx, mb.RoleId, service.AdminMember().VerifySuperId(ctx, mb.Id))
|
||||||
|
if err != nil {
|
||||||
|
err = gerror.New("验证角色信息失败!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !validate.InSlice(ids, id) {
|
||||||
|
err = gerror.New("角色ID是无效的")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSubRoleIds 获取所有下级角色ID
|
||||||
|
func (s *sAdminRole) GetSubRoleIds(ctx context.Context, roleId int64, isSuper bool) (ids []int64, err error) {
|
||||||
|
mod := dao.AdminRole.Ctx(ctx).Fields(dao.AdminRole.Columns().Id)
|
||||||
|
if !isSuper {
|
||||||
|
mod = mod.WhereNot(dao.AdminRole.Columns().Id, roleId).WhereLike(dao.AdminRole.Columns().Tree, "%"+tree.GetIdLabel(roleId)+"%")
|
||||||
|
}
|
||||||
|
|
||||||
|
columns, err := mod.Array()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ids = g.NewVar(columns).Int64s()
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,338 @@
|
|||||||
|
// Package admin
|
||||||
|
// @Link https://github.com/bufanyun/hotgo
|
||||||
|
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||||
|
// @Author Ms <133814250@qq.com>
|
||||||
|
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||||
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"hotgo/internal/consts"
|
||||||
|
"hotgo/internal/dao"
|
||||||
|
"hotgo/internal/library/contexts"
|
||||||
|
"hotgo/internal/library/token"
|
||||||
|
"hotgo/internal/model"
|
||||||
|
"hotgo/internal/model/entity"
|
||||||
|
"hotgo/internal/model/input/adminin"
|
||||||
|
"hotgo/internal/model/input/sysin"
|
||||||
|
"hotgo/internal/service"
|
||||||
|
"hotgo/utility/simple"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/v2/crypto/gmd5"
|
||||||
|
"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/util/grand"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sAdminSite struct{}
|
||||||
|
|
||||||
|
func NewAdminSite() *sAdminSite {
|
||||||
|
return &sAdminSite{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
service.RegisterAdminSite(NewAdminSite())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register 账号注册
|
||||||
|
func (s *sAdminSite) Register(ctx context.Context, in *adminin.RegisterInp) (err error) {
|
||||||
|
config, err := service.SysConfig().GetLogin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.ForceInvite == 1 && in.InviteCode == "" {
|
||||||
|
err = gerror.New("请填写邀请码")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var data adminin.MemberAddInp
|
||||||
|
|
||||||
|
// 默认上级
|
||||||
|
data.Pid = 1
|
||||||
|
|
||||||
|
// 存在邀请人
|
||||||
|
if in.InviteCode != "" {
|
||||||
|
pmb, err := service.AdminMember().GetIdByCode(ctx, &adminin.GetIdByCodeInp{Code: in.InviteCode})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if pmb == nil {
|
||||||
|
err = gerror.New("邀请人信息不存在")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
data.Pid = pmb.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.RegisterSwitch != 1 {
|
||||||
|
err = gerror.New("管理员未开放注册")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.RoleId < 1 {
|
||||||
|
err = gerror.New("管理员未配置默认角色")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.DeptId < 1 {
|
||||||
|
err = gerror.New("管理员未配置默认部门")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证唯一性
|
||||||
|
err = service.AdminMember().VerifyUnique(ctx, &adminin.VerifyUniqueInp{
|
||||||
|
Where: g.Map{
|
||||||
|
dao.AdminMember.Columns().Username: in.Username,
|
||||||
|
dao.AdminMember.Columns().Mobile: in.Mobile,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证短信验证码
|
||||||
|
err = service.SysSmsLog().VerifyCode(ctx, &sysin.VerifyCodeInp{
|
||||||
|
Event: consts.SmsTemplateRegister,
|
||||||
|
Mobile: in.Mobile,
|
||||||
|
Code: in.Code,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data.MemberEditInp = &adminin.MemberEditInp{
|
||||||
|
Id: 0,
|
||||||
|
RoleId: config.RoleId,
|
||||||
|
PostIds: config.PostIds,
|
||||||
|
DeptId: config.DeptId,
|
||||||
|
Username: in.Username,
|
||||||
|
Password: in.Password,
|
||||||
|
RealName: "",
|
||||||
|
Avatar: config.Avatar,
|
||||||
|
Sex: 3, // 保密
|
||||||
|
Mobile: in.Mobile,
|
||||||
|
Status: consts.StatusEnabled,
|
||||||
|
}
|
||||||
|
data.Salt = grand.S(6)
|
||||||
|
data.InviteCode = grand.S(12)
|
||||||
|
data.PasswordHash = gmd5.MustEncryptString(data.Password + data.Salt)
|
||||||
|
data.Level, data.Tree, err = service.AdminMember().GenTree(ctx, data.Pid)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交注册信息
|
||||||
|
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
|
||||||
|
id, err := dao.AdminMember.Ctx(ctx).Data(data).OmitEmptyData().InsertAndGetId()
|
||||||
|
if err != nil {
|
||||||
|
err = gerror.Wrap(err, consts.ErrorORM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新岗位
|
||||||
|
if err = service.AdminMemberPost().UpdatePostIds(ctx, id, config.PostIds); err != nil {
|
||||||
|
err = gerror.Wrap(err, consts.ErrorORM)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountLogin 账号登录
|
||||||
|
func (s *sAdminSite) AccountLogin(ctx context.Context, in *adminin.AccountLoginInp) (res *adminin.LoginModel, err error) {
|
||||||
|
defer func() {
|
||||||
|
service.SysLoginLog().Push(ctx, &sysin.LoginLogPushInp{Response: res, Err: err})
|
||||||
|
}()
|
||||||
|
|
||||||
|
var mb *entity.AdminMember
|
||||||
|
if err = dao.AdminMember.Ctx(ctx).Where("username", in.Username).Scan(&mb); err != nil {
|
||||||
|
err = gerror.Wrap(err, consts.ErrorORM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if mb == nil {
|
||||||
|
err = gerror.New("用户名或密码错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res = new(adminin.LoginModel)
|
||||||
|
res.Id = mb.Id
|
||||||
|
res.Username = mb.Username
|
||||||
|
if mb.Salt == "" {
|
||||||
|
err = gerror.New("用户信息错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = simple.CheckPassword(in.Password, mb.Salt, mb.PasswordHash); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if mb.Status != consts.StatusEnabled {
|
||||||
|
err = gerror.New("账号已被禁用")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err = s.handleLogin(ctx, mb)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// MobileLogin 手机号登录
|
||||||
|
func (s *sAdminSite) MobileLogin(ctx context.Context, in *adminin.MobileLoginInp) (res *adminin.LoginModel, err error) {
|
||||||
|
defer func() {
|
||||||
|
service.SysLoginLog().Push(ctx, &sysin.LoginLogPushInp{Response: res, Err: err})
|
||||||
|
}()
|
||||||
|
|
||||||
|
var mb *entity.AdminMember
|
||||||
|
if err = dao.AdminMember.Ctx(ctx).Where("mobile ", in.Mobile).Scan(&mb); err != nil {
|
||||||
|
err = gerror.Wrap(err, consts.ErrorORM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if mb == nil {
|
||||||
|
err = gerror.New("账号不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res = new(adminin.LoginModel)
|
||||||
|
res.Id = mb.Id
|
||||||
|
res.Username = mb.Username
|
||||||
|
|
||||||
|
err = service.SysSmsLog().VerifyCode(ctx, &sysin.VerifyCodeInp{
|
||||||
|
Event: consts.SmsTemplateLogin,
|
||||||
|
Mobile: in.Mobile,
|
||||||
|
Code: in.Code,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if mb.Status != consts.StatusEnabled {
|
||||||
|
err = gerror.New("账号已被禁用")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err = s.handleLogin(ctx, mb)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleLogin .
|
||||||
|
func (s *sAdminSite) handleLogin(ctx context.Context, mb *entity.AdminMember) (res *adminin.LoginModel, err error) {
|
||||||
|
role, dept, err := s.getLoginRoleAndDept(ctx, mb.RoleId, mb.DeptId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
user := &model.Identity{
|
||||||
|
Id: mb.Id,
|
||||||
|
Pid: mb.Pid,
|
||||||
|
DeptId: dept.Id,
|
||||||
|
DeptType: dept.Type,
|
||||||
|
RoleId: role.Id,
|
||||||
|
RoleKey: role.Key,
|
||||||
|
Username: mb.Username,
|
||||||
|
RealName: mb.RealName,
|
||||||
|
Avatar: mb.Avatar,
|
||||||
|
Email: mb.Email,
|
||||||
|
Mobile: mb.Mobile,
|
||||||
|
App: consts.AppAdmin,
|
||||||
|
LoginAt: gtime.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
lt, expires, err := token.Login(ctx, user)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res = &adminin.LoginModel{
|
||||||
|
Username: user.Username,
|
||||||
|
Id: user.Id,
|
||||||
|
Token: lt,
|
||||||
|
Expires: expires,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// getLoginRoleAndDept 获取登录的角色和部门信息
|
||||||
|
func (s *sAdminSite) getLoginRoleAndDept(ctx context.Context, roleId, deptId int64) (role *entity.AdminRole, dept *entity.AdminDept, err error) {
|
||||||
|
if err = dao.AdminRole.Ctx(ctx).Fields(dao.AdminRole.Columns().Id, dao.AdminRole.Columns().Key, dao.AdminRole.Columns().Status).WherePri(roleId).Scan(&role); err != nil {
|
||||||
|
err = gerror.Wrap(err, consts.ErrorORM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if role == nil {
|
||||||
|
err = gerror.New("角色不存在或已被删除")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if role.Status != consts.StatusEnabled {
|
||||||
|
err = gerror.New("角色已被禁用,如有疑问请联系管理员")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = dao.AdminDept.Ctx(ctx).Fields(dao.AdminDept.Columns().Id, dao.AdminDept.Columns().Type, dao.AdminDept.Columns().Status).WherePri(deptId).Scan(&dept); err != nil {
|
||||||
|
err = gerror.Wrap(err, "获取部门信息失败,请稍后重试!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if dept == nil {
|
||||||
|
err = gerror.New("部门不存在或已被删除")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if dept.Status != consts.StatusEnabled {
|
||||||
|
err = gerror.New("部门已被禁用,如有疑问请联系管理员")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindUserContext 绑定用户上下文
|
||||||
|
func (s *sAdminSite) BindUserContext(ctx context.Context, claims *model.Identity) (err error) {
|
||||||
|
//// 如果不想每次访问都重新加载用户信息,可以放开注释。但在本次登录未失效前,用户信息不会刷新
|
||||||
|
// contexts.SetUser(ctx, claims)
|
||||||
|
// return
|
||||||
|
|
||||||
|
var mb *entity.AdminMember
|
||||||
|
if err = dao.AdminMember.Ctx(ctx).WherePri(claims.Id).Scan(&mb); err != nil {
|
||||||
|
err = gerror.Wrap(err, "获取用户信息失败,请稍后重试!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if mb == nil {
|
||||||
|
err = gerror.Wrap(err, "账号不存在或已被删除!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if mb.Status != consts.StatusEnabled {
|
||||||
|
err = gerror.New("账号已被禁用,如有疑问请联系管理员")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
role, dept, err := s.getLoginRoleAndDept(ctx, mb.RoleId, mb.DeptId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
user := &model.Identity{
|
||||||
|
Id: mb.Id,
|
||||||
|
Pid: mb.Pid,
|
||||||
|
DeptId: dept.Id,
|
||||||
|
DeptType: dept.Type,
|
||||||
|
RoleId: mb.RoleId,
|
||||||
|
RoleKey: role.Key,
|
||||||
|
Username: mb.Username,
|
||||||
|
RealName: mb.RealName,
|
||||||
|
Avatar: mb.Avatar,
|
||||||
|
Email: mb.Email,
|
||||||
|
Mobile: mb.Mobile,
|
||||||
|
App: claims.App,
|
||||||
|
LoginAt: claims.LoginAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
contexts.SetUser(ctx, user)
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
// Package common
|
||||||
|
// @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
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"hotgo/internal/library/storager"
|
||||||
|
"hotgo/internal/model/input/sysin"
|
||||||
|
"hotgo/internal/service"
|
||||||
|
"hotgo/utility/file"
|
||||||
|
"hotgo/utility/format"
|
||||||
|
"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/text/gstr"
|
||||||
|
"github.com/gogf/gf/v2/util/grand"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sCommonUpload struct{}
|
||||||
|
|
||||||
|
func NewCommonUpload() *sCommonUpload {
|
||||||
|
return &sCommonUpload{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
service.RegisterCommonUpload(NewCommonUpload())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadFile 上传文件
|
||||||
|
func (s *sCommonUpload) UploadFile(ctx context.Context, uploadType string, file *ghttp.UploadFile) (res *sysin.AttachmentListModel, err error) {
|
||||||
|
attachment, err := storager.DoUpload(ctx, uploadType, file)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
attachment.FileUrl = storager.LastUrl(ctx, attachment.FileUrl, attachment.Drive)
|
||||||
|
res = &sysin.AttachmentListModel{
|
||||||
|
SysAttachment: *attachment,
|
||||||
|
SizeFormat: format.FileSize(attachment.Size),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckMultipart 检查文件分片
|
||||||
|
func (s *sCommonUpload) CheckMultipart(ctx context.Context, in *sysin.CheckMultipartInp) (res *sysin.CheckMultipartModel, err error) {
|
||||||
|
data, err := storager.CheckMultipart(ctx, in.CheckMultipartParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res = new(sysin.CheckMultipartModel)
|
||||||
|
res.CheckMultipartModel = data
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadPart 上传分片
|
||||||
|
func (s *sCommonUpload) UploadPart(ctx context.Context, in *sysin.UploadPartInp) (res *sysin.UploadPartModel, err error) {
|
||||||
|
data, err := storager.UploadPart(ctx, in.UploadPartParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res = new(sysin.UploadPartModel)
|
||||||
|
res.UploadPartModel = data
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageTransferStorage 图片链接转存
|
||||||
|
func (s *sCommonUpload) ImageTransferStorage(ctx context.Context, in *sysin.ImageTransferStorageInp) (res *sysin.ImageTransferStorageModel, err error) {
|
||||||
|
if !gstr.HasPrefix(in.Url, "http://") && !gstr.HasPrefix(in.Url, "https://") {
|
||||||
|
return nil, gerror.New("仅支持 HTTP/HTTPS 协议的图片链接")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := g.Client().SetTimeout(time.Second*30).Get(ctx, in.Url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return nil, gerror.Newf("请求图片资源失败, StatusCode:%v", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
contentType := gstr.ToLower(resp.Header.Get("Content-Type"))
|
||||||
|
if !gstr.HasPrefix(contentType, "image/") {
|
||||||
|
return nil, gerror.New("资源不是图片类型")
|
||||||
|
}
|
||||||
|
|
||||||
|
content := resp.ReadAll()
|
||||||
|
if len(content) == 0 {
|
||||||
|
return nil, gerror.New("图片内容为空")
|
||||||
|
}
|
||||||
|
|
||||||
|
res = new(sysin.ImageTransferStorageModel)
|
||||||
|
fileHeader, err := file.NewMultipartFileHeader("its-"+grand.Letters(8)+".png", content)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gerror.Newf("创建文件头失败:%v", err)
|
||||||
|
}
|
||||||
|
res.AttachmentListModel, err = s.UploadFile(ctx, storager.KindImg, &ghttp.UploadFile{FileHeader: fileHeader})
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
// Package common
|
||||||
|
// @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 common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"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/gcron"
|
||||||
|
"github.com/gogf/gf/v2/os/gctx"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
"github.com/gogf/gf/v2/util/gmeta"
|
||||||
|
"hotgo/api/admin/common"
|
||||||
|
"hotgo/internal/consts"
|
||||||
|
"hotgo/internal/library/cache"
|
||||||
|
"hotgo/internal/library/contexts"
|
||||||
|
"hotgo/internal/library/response"
|
||||||
|
"hotgo/internal/library/token"
|
||||||
|
"hotgo/internal/library/wechat"
|
||||||
|
"hotgo/internal/model/input/commonin"
|
||||||
|
"hotgo/internal/service"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sCommonWechat struct {
|
||||||
|
temp map[string]*AuthorizeCallState
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthorizeCallState 微信授权回调参数
|
||||||
|
type AuthorizeCallState struct {
|
||||||
|
State string `json:"state" dc:"state"`
|
||||||
|
MemberId int64 `json:"memberId" dc:"管理员ID"`
|
||||||
|
Type string `json:"type" dc:"授权类型"`
|
||||||
|
SyncRedirect string `json:"syncRedirect" dc:"同步跳转地址"`
|
||||||
|
Tick *gtime.Time `json:"tick" dc:"标记授权时间"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCommonWechat() *sCommonWechat {
|
||||||
|
return &sCommonWechat{
|
||||||
|
temp: make(map[string]*AuthorizeCallState),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
serv := NewCommonWechat()
|
||||||
|
service.RegisterCommonWechat(serv)
|
||||||
|
_, _ = gcron.Add(gctx.New(), "@every 300s", serv.CleanTempMap, "WechatCleanTempMap")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authorize 微信用户授权
|
||||||
|
func (s *sCommonWechat) Authorize(ctx context.Context, in *commonin.WechatAuthorizeInp) (res *commonin.WechatAuthorizeModel, err error) {
|
||||||
|
basic, err := service.SysConfig().GetBasic(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
request = g.RequestFromCtx(ctx)
|
||||||
|
prefix = g.Cfg().MustGet(ctx, "router.admin.prefix", "/admin").String()
|
||||||
|
path = gmeta.Get(common.WechatAuthorizeCallReq{}, "path").String()
|
||||||
|
redirectUri = basic.Domain + prefix + path
|
||||||
|
memberId = contexts.GetUserId(ctx)
|
||||||
|
state = s.GetCacheKey(in.Type, token.GetAuthKey(token.GetAuthorization(request)))
|
||||||
|
scope string
|
||||||
|
)
|
||||||
|
|
||||||
|
if memberId <= 0 {
|
||||||
|
err = gerror.New("获取用户信息失败!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch in.Type {
|
||||||
|
case consts.WechatAuthorizeOpenId: // 设置openid
|
||||||
|
scope = consts.WechatScopeBase
|
||||||
|
case consts.WechatAuthorizeBindLogin: // 绑定微信登录
|
||||||
|
scope = consts.WechatScopeUserinfo
|
||||||
|
default:
|
||||||
|
err = gerror.New("无效的授权方式!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
url, err := wechat.GetOauthURL(ctx, redirectUri, scope, state)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.temp[state] = &AuthorizeCallState{
|
||||||
|
State: state,
|
||||||
|
MemberId: memberId,
|
||||||
|
Type: in.Type,
|
||||||
|
SyncRedirect: in.SyncRedirect,
|
||||||
|
Tick: gtime.Now(),
|
||||||
|
}
|
||||||
|
response.Redirect(g.RequestFromCtx(ctx), url)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sCommonWechat) AuthorizeCall(ctx context.Context, in *commonin.WechatAuthorizeCallInp) (res *commonin.WechatAuthorizeCallModel, err error) {
|
||||||
|
data, ok := s.temp[in.State]
|
||||||
|
if !ok || data == nil {
|
||||||
|
err = gerror.New("授权无效或已过期,请重试")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer delete(s.temp, in.State)
|
||||||
|
|
||||||
|
tk, err := wechat.GetUserAccessToken(ctx, in.Code)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch data.Type {
|
||||||
|
case consts.WechatAuthorizeOpenId: // 设置openid
|
||||||
|
_ = cache.Instance().Set(ctx, data.State, tk.OpenID, time.Hour*24*7)
|
||||||
|
case consts.WechatAuthorizeBindLogin: // 绑定微信登录
|
||||||
|
// ...
|
||||||
|
default:
|
||||||
|
err = gerror.New("无效的授权方式!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Redirect(g.RequestFromCtx(ctx), data.SyncRedirect)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOpenId 从缓存中获取临时openid
|
||||||
|
func (s *sCommonWechat) GetOpenId(ctx context.Context) (openId string, err error) {
|
||||||
|
request := ghttp.RequestFromCtx(ctx)
|
||||||
|
key := s.GetCacheKey(consts.WechatAuthorizeOpenId, token.GetAuthKey(token.GetAuthorization(request)))
|
||||||
|
date, err := cache.Instance().Get(ctx, key)
|
||||||
|
if err != nil {
|
||||||
|
err = gerror.Newf("GetOpenId err:%+v", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
openId = date.String()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sCommonWechat) GetCacheKey(typ, ak string) string {
|
||||||
|
return fmt.Sprintf("%v:%v", typ, ak)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanTempMap 清理临时map
|
||||||
|
func (s *sCommonWechat) CleanTempMap(ctx context.Context) {
|
||||||
|
t := gtime.Now().Add(time.Second * 600)
|
||||||
|
for _, state := range s.temp {
|
||||||
|
if state.Tick.After(t) {
|
||||||
|
delete(s.temp, state.State)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
// Package hook
|
||||||
|
// @Link https://github.com/bufanyun/hotgo
|
||||||
|
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||||
|
// @Author Ms <133814250@qq.com>
|
||||||
|
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||||
|
package hook
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"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/text/gstr"
|
||||||
|
"hotgo/internal/library/contexts"
|
||||||
|
"hotgo/internal/service"
|
||||||
|
"hotgo/utility/simple"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 忽略的请求方式
|
||||||
|
var ignoredRequestMethods = []string{"HEAD", "PRI"}
|
||||||
|
|
||||||
|
// accessLog 访问日志
|
||||||
|
func (s *sHook) accessLog(r *ghttp.Request) {
|
||||||
|
if s.isIgnoredRequest(r) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var ctx = r.Context()
|
||||||
|
if contexts.Get(ctx) == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
contexts.SetDataMap(ctx, g.Map{
|
||||||
|
"request.takeUpTime": gtime.Now().Sub(gtime.New(r.EnterTime)).Milliseconds(),
|
||||||
|
// ...
|
||||||
|
})
|
||||||
|
|
||||||
|
simple.SafeGo(ctx, func(ctx context.Context) {
|
||||||
|
if err := service.SysLog().AutoLog(ctx); err != nil {
|
||||||
|
g.Log().Infof(ctx, "hook accessLog err:%+v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// isIgnoredRequest 是否忽略请求
|
||||||
|
func (s *sHook) isIgnoredRequest(r *ghttp.Request) bool {
|
||||||
|
if r.IsFileRequest() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if gstr.InArray(ignoredRequestMethods, strings.ToUpper(r.Method)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// Package hook
|
||||||
|
// @Link https://github.com/bufanyun/hotgo
|
||||||
|
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||||
|
// @Author Ms <133814250@qq.com>
|
||||||
|
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||||
|
package hook
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/net/ghttp"
|
||||||
|
"hotgo/internal/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sHook struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
service.RegisterHook(New())
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() *sHook {
|
||||||
|
return &sHook{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sHook) BeforeServe(r *ghttp.Request) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sHook) AfterOutput(r *ghttp.Request) {
|
||||||
|
s.accessLog(r)
|
||||||
|
s.lastAdminActive(r)
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
// Package hook
|
||||||
|
// @Link https://github.com/bufanyun/hotgo
|
||||||
|
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||||
|
// @Author Ms <133814250@qq.com>
|
||||||
|
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||||
|
package hook
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"hotgo/internal/consts"
|
||||||
|
"hotgo/internal/dao"
|
||||||
|
"hotgo/internal/library/contexts"
|
||||||
|
"hotgo/utility/simple"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"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/os/gtimer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type visitor struct {
|
||||||
|
lastSeen *gtime.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
visitors = make(map[int64]*visitor)
|
||||||
|
mtx sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
// Run a background goroutine to remove old entries from the visitors map.
|
||||||
|
func init() {
|
||||||
|
go cleanupVisitors()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Every minute check the map for visitors that haven't been seen for
|
||||||
|
// more than 3 minutes and delete the entries.
|
||||||
|
func cleanupVisitors() {
|
||||||
|
gtimer.AddSingleton(gctx.New(), time.Minute, func(ctx context.Context) {
|
||||||
|
mtx.Lock()
|
||||||
|
for memberId, v := range visitors {
|
||||||
|
if gtime.Now().Sub(v.lastSeen) > 3*time.Minute {
|
||||||
|
delete(visitors, memberId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mtx.Unlock()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func allow(memberId int64) bool {
|
||||||
|
mtx.Lock()
|
||||||
|
defer mtx.Unlock()
|
||||||
|
v, exists := visitors[memberId]
|
||||||
|
if !exists {
|
||||||
|
visitors[memberId] = &visitor{gtime.Now()}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if gtime.Now().Sub(v.lastSeen) > time.Second*3 {
|
||||||
|
v.lastSeen = gtime.Now()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// lastAdminActive 更新后台用户最后活跃
|
||||||
|
func (s *sHook) lastAdminActive(r *ghttp.Request) {
|
||||||
|
if r.IsFileRequest() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ctx = r.Context()
|
||||||
|
member = contexts.GetUser(ctx)
|
||||||
|
)
|
||||||
|
|
||||||
|
if member == nil || member.App != consts.AppAdmin {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if allow(member.Id) {
|
||||||
|
simple.SafeGo(ctx, func(ctx context.Context) {
|
||||||
|
_, err := dao.AdminMember.Ctx(ctx).
|
||||||
|
Where(dao.AdminMember.Columns().Id, member.Id).
|
||||||
|
Data(g.Map{dao.AdminMember.Columns().LastActiveAt: gtime.Now()}).
|
||||||
|
Update()
|
||||||
|
if err != nil {
|
||||||
|
g.Log().Warningf(ctx, "hook lastActive err:%+v, memberId:%v", err, member.Id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// ==========================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// ==========================================================================
|
||||||
|
|
||||||
|
package logic
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "hotgo/internal/logic/admin"
|
||||||
|
_ "hotgo/internal/logic/common"
|
||||||
|
_ "hotgo/internal/logic/hook"
|
||||||
|
_ "hotgo/internal/logic/middleware"
|
||||||
|
_ "hotgo/internal/logic/pay"
|
||||||
|
_ "hotgo/internal/logic/sys"
|
||||||
|
_ "hotgo/internal/logic/tcpclient"
|
||||||
|
_ "hotgo/internal/logic/tcpserver"
|
||||||
|
_ "hotgo/internal/logic/view"
|
||||||
|
)
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
// Package middleware
|
||||||
|
// @Link https://github.com/bufanyun/hotgo
|
||||||
|
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||||
|
// @Author Ms <133814250@qq.com>
|
||||||
|
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/errors/gcode"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/net/ghttp"
|
||||||
|
"github.com/gogf/gf/v2/text/gstr"
|
||||||
|
"hotgo/internal/consts"
|
||||||
|
"hotgo/internal/library/contexts"
|
||||||
|
"hotgo/internal/library/response"
|
||||||
|
"hotgo/internal/service"
|
||||||
|
"hotgo/utility/simple"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdminAuth 后台鉴权中间件
|
||||||
|
func (s *sMiddleware) AdminAuth(r *ghttp.Request) {
|
||||||
|
var (
|
||||||
|
ctx = r.Context()
|
||||||
|
path = gstr.Replace(r.URL.Path, simple.RouterPrefix(ctx, consts.AppAdmin), "", 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
// 不需要验证登录的路由地址
|
||||||
|
if s.IsExceptLogin(ctx, consts.AppAdmin, path) {
|
||||||
|
r.Middleware.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将用户信息传递到上下文中
|
||||||
|
if err := s.DeliverUserContext(r); err != nil {
|
||||||
|
response.JsonExit(r, gcode.CodeNotAuthorized.Code(), err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不需要验证权限的路由地址
|
||||||
|
if s.IsExceptAuth(ctx, consts.AppAdmin, path) {
|
||||||
|
r.Middleware.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证路由访问权限
|
||||||
|
if !service.AdminRole().Verify(ctx, path, r.Method) {
|
||||||
|
g.Log().Debugf(ctx, "AdminAuth fail path:%+v, GetRoleKey:%+v, r.Method:%+v", path, contexts.GetRoleKey(ctx), r.Method)
|
||||||
|
response.JsonExit(r, gcode.CodeSecurityReason.Code(), "你没有访问权限!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Middleware.Next()
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// Package middleware
|
||||||
|
// @Link https://github.com/bufanyun/hotgo
|
||||||
|
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||||
|
// @Author Ms <133814250@qq.com>
|
||||||
|
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/errors/gcode"
|
||||||
|
"github.com/gogf/gf/v2/net/ghttp"
|
||||||
|
"github.com/gogf/gf/v2/text/gstr"
|
||||||
|
"hotgo/internal/consts"
|
||||||
|
"hotgo/internal/library/response"
|
||||||
|
"hotgo/utility/simple"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ApiAuth API鉴权中间件
|
||||||
|
func (s *sMiddleware) ApiAuth(r *ghttp.Request) {
|
||||||
|
var (
|
||||||
|
ctx = r.Context()
|
||||||
|
path = gstr.Replace(r.URL.Path, simple.RouterPrefix(ctx, consts.AppApi), "", 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
// 不需要验证登录的路由地址
|
||||||
|
if s.IsExceptLogin(ctx, consts.AppApi, path) {
|
||||||
|
r.Middleware.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将用户信息传递到上下文中
|
||||||
|
if err := s.DeliverUserContext(r); err != nil {
|
||||||
|
response.JsonExit(r, gcode.CodeNotAuthorized.Code(), err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证路由访问权限
|
||||||
|
// ...
|
||||||
|
|
||||||
|
r.Middleware.Next()
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// Package middleware
|
||||||
|
// @Link https://github.com/bufanyun/hotgo
|
||||||
|
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||||
|
// @Author Ms <133814250@qq.com>
|
||||||
|
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/net/ghttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HomeAuth 前台页面鉴权中间件
|
||||||
|
func (s *sMiddleware) HomeAuth(r *ghttp.Request) {
|
||||||
|
// 鉴权
|
||||||
|
// ...
|
||||||
|
|
||||||
|
r.Middleware.Next()
|
||||||
|
}
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
// Package middleware
|
||||||
|
// @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
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gogf/gf/v2/encoding/gjson"
|
||||||
|
"github.com/gogf/gf/v2/errors/gcode"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/i18n/gi18n"
|
||||||
|
"github.com/gogf/gf/v2/net/ghttp"
|
||||||
|
"github.com/gogf/gf/v2/net/gtrace"
|
||||||
|
"github.com/gogf/gf/v2/os/gctx"
|
||||||
|
"github.com/gogf/gf/v2/text/gstr"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"hotgo/internal/consts"
|
||||||
|
"hotgo/internal/library/addons"
|
||||||
|
"hotgo/internal/library/contexts"
|
||||||
|
"hotgo/internal/library/response"
|
||||||
|
"hotgo/internal/library/token"
|
||||||
|
"hotgo/internal/model"
|
||||||
|
"hotgo/internal/service"
|
||||||
|
"hotgo/utility/simple"
|
||||||
|
"hotgo/utility/validate"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sMiddleware struct {
|
||||||
|
LoginUrl string // 登录路由地址
|
||||||
|
DemoWhiteList g.Map // 演示模式放行的路由白名单
|
||||||
|
NotRecordRequest g.Map // 不记录请求数据的路由(当前请求数据过大时会影响响应效率,可以将路径放到该选项中改善)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
service.RegisterMiddleware(NewMiddleware())
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMiddleware() *sMiddleware {
|
||||||
|
return &sMiddleware{
|
||||||
|
LoginUrl: "/common",
|
||||||
|
DemoWhiteList: g.Map{
|
||||||
|
"/admin/site/accountLogin": struct{}{}, // 账号登录
|
||||||
|
"/admin/site/mobileLogin": struct{}{}, // 手机号登录
|
||||||
|
"/admin/genCodes/preview": struct{}{}, // 预览代码
|
||||||
|
},
|
||||||
|
NotRecordRequest: g.Map{
|
||||||
|
"/admin/upload/file": struct{}{}, // 上传文件
|
||||||
|
"/admin/upload/uploadPart": struct{}{}, // 上传分片
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ctx 初始化请求上下文
|
||||||
|
func (s *sMiddleware) Ctx(r *ghttp.Request) {
|
||||||
|
// 国际化
|
||||||
|
r.SetCtx(gi18n.WithLanguage(r.Context(), simple.GetHeaderLocale(r.Context())))
|
||||||
|
|
||||||
|
// 链路追踪
|
||||||
|
if g.Cfg().MustGet(r.Context(), "jaeger.switch").Bool() {
|
||||||
|
ctx, span := gtrace.NewSpan(r.Context(), "middleware.ctx")
|
||||||
|
span.SetAttributes(attribute.KeyValue{
|
||||||
|
Key: "traceID",
|
||||||
|
Value: attribute.StringValue(gctx.CtxId(ctx)),
|
||||||
|
})
|
||||||
|
span.End()
|
||||||
|
r.SetCtx(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make(g.Map)
|
||||||
|
if _, ok := s.NotRecordRequest[r.URL.Path]; ok {
|
||||||
|
data["request.body"] = gjson.New(nil)
|
||||||
|
} else {
|
||||||
|
data["request.body"] = gjson.New(r.GetBodyString())
|
||||||
|
}
|
||||||
|
|
||||||
|
contexts.Init(r, &model.Context{
|
||||||
|
Data: data,
|
||||||
|
Module: getModule(r.URL.Path),
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(r.Cookie.GetSessionId()) == 0 {
|
||||||
|
r.Cookie.SetSessionId(gctx.CtxId(r.Context()))
|
||||||
|
}
|
||||||
|
|
||||||
|
r.SetCtx(r.GetNeverDoneCtx())
|
||||||
|
r.Middleware.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getModule(path string) (module string) {
|
||||||
|
slice := strings.Split(path, "/")
|
||||||
|
if len(slice) < 2 {
|
||||||
|
module = consts.AppDefault
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if slice[1] == "" {
|
||||||
|
module = consts.AppDefault
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return slice[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// CORS allows Cross-origin resource sharing.
|
||||||
|
func (s *sMiddleware) CORS(r *ghttp.Request) {
|
||||||
|
r.Response.CORSDefault()
|
||||||
|
r.Middleware.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DemoLimit 演示系统操作限制
|
||||||
|
func (s *sMiddleware) DemoLimit(r *ghttp.Request) {
|
||||||
|
if !simple.IsDemo(r.Context()) {
|
||||||
|
r.Middleware.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Method == http.MethodPost {
|
||||||
|
if _, ok := s.DemoWhiteList[r.URL.Path]; ok {
|
||||||
|
r.Middleware.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.JsonExit(r, gcode.CodeNotSupported.Code(), "演示系统禁止操作!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Middleware.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addon 插件中间件
|
||||||
|
func (s *sMiddleware) Addon(r *ghttp.Request) {
|
||||||
|
var ctx = r.Context()
|
||||||
|
|
||||||
|
if contexts.Get(ctx).Module == "" {
|
||||||
|
g.Log().Warning(ctx, "application module is not initialized.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 替换掉应用模块前缀
|
||||||
|
path := gstr.Replace(r.URL.Path, "/"+contexts.Get(ctx).Module+"/", "", 1)
|
||||||
|
ss := gstr.Explode("/", path)
|
||||||
|
if len(ss) == 0 {
|
||||||
|
g.Log().Warning(ctx, "addon was not recognized.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
module := addons.GetModule(ss[0])
|
||||||
|
if module == nil {
|
||||||
|
g.Log().Warningf(ctx, "addon module = nil, name:%v", ss[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sk := module.GetSkeleton()
|
||||||
|
if sk == nil {
|
||||||
|
g.Log().Warningf(ctx, "addon skeleton = nil, name:%v", ss[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
contexts.SetAddonName(ctx, sk.Name)
|
||||||
|
r.Middleware.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeliverUserContext 将用户信息传递到上下文中
|
||||||
|
func (s *sMiddleware) DeliverUserContext(r *ghttp.Request) (err error) {
|
||||||
|
user, err := token.ParseLoginUser(r)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch user.App {
|
||||||
|
case consts.AppAdmin:
|
||||||
|
if err = service.AdminSite().BindUserContext(r.Context(), user); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
contexts.SetUser(r.Context(), user)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExceptAuth 是否是不需要验证权限的路由地址
|
||||||
|
func (s *sMiddleware) IsExceptAuth(ctx context.Context, appName, path string) bool {
|
||||||
|
pathList := g.Cfg().MustGet(ctx, fmt.Sprintf("router.%v.exceptAuth", appName)).Strings()
|
||||||
|
|
||||||
|
for i := 0; i < len(pathList); i++ {
|
||||||
|
if validate.InSliceExistStr(pathList[i], path) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExceptLogin 是否是不需要登录的路由地址
|
||||||
|
func (s *sMiddleware) IsExceptLogin(ctx context.Context, appName, path string) bool {
|
||||||
|
pathList := g.Cfg().MustGet(ctx, fmt.Sprintf("router.%v.exceptLogin", appName)).Strings()
|
||||||
|
|
||||||
|
for i := 0; i < len(pathList); i++ {
|
||||||
|
if validate.InSliceExistStr(pathList[i], path) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// Package middleware
|
||||||
|
// @Link https://github.com/bufanyun/hotgo
|
||||||
|
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||||
|
// @Author Ms <133814250@qq.com>
|
||||||
|
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/errors/gerror"
|
||||||
|
"github.com/gogf/gf/v2/net/ghttp"
|
||||||
|
"hotgo/internal/library/response"
|
||||||
|
"hotgo/internal/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Blacklist IP黑名单限制中间件
|
||||||
|
func (s *sMiddleware) Blacklist(r *ghttp.Request) {
|
||||||
|
if err := service.SysBlacklist().VerifyRequest(r); err != nil {
|
||||||
|
response.JsonExit(r, gerror.Code(err).Code(), err.Error())
|
||||||
|
}
|
||||||
|
r.Middleware.Next()
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// Package middleware
|
||||||
|
// @Link https://github.com/bufanyun/hotgo
|
||||||
|
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||||
|
// @Author Ms <133814250@qq.com>
|
||||||
|
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/gogf/gf/v2/errors/gcode"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/net/ghttp"
|
||||||
|
"github.com/gogf/gf/v2/text/gstr"
|
||||||
|
"hotgo/internal/library/location"
|
||||||
|
"hotgo/internal/library/response"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Develop 开发工具白名单过滤
|
||||||
|
func (s *sMiddleware) Develop(r *ghttp.Request) {
|
||||||
|
ips := g.Cfg().MustGet(r.Context(), "hggen.allowedIPs").Strings()
|
||||||
|
if len(ips) == 0 {
|
||||||
|
response.JsonExit(r, gcode.CodeNotSupported.Code(), "请配置生成白名单!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !gstr.InArray(ips, "*") {
|
||||||
|
clientIp := location.GetClientIp(r)
|
||||||
|
ok := false
|
||||||
|
for _, ip := range ips {
|
||||||
|
if ip == clientIp {
|
||||||
|
ok = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
response.JsonExit(r, gcode.CodeNotSupported.Code(), fmt.Sprintf("当前IP[%s]没有配置生成白名单!", clientIp))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Middleware.Next()
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
// Package middleware
|
||||||
|
// @Link https://github.com/bufanyun/hotgo
|
||||||
|
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||||
|
// @Author Ms <133814250@qq.com>
|
||||||
|
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/errors/gerror"
|
||||||
|
"github.com/gogf/gf/v2/net/ghttp"
|
||||||
|
"github.com/gogf/gf/v2/util/gconv"
|
||||||
|
"hotgo/internal/global"
|
||||||
|
"hotgo/internal/library/response"
|
||||||
|
"hotgo/utility/validate"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PreFilter 请求输入预处理
|
||||||
|
// api使用gf规范路由并且XxxReq结构体实现了validate.Filter接口即可
|
||||||
|
func (s *sMiddleware) PreFilter(r *ghttp.Request) {
|
||||||
|
router := global.GetRequestRoute(r)
|
||||||
|
if router == nil {
|
||||||
|
r.Middleware.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
funcInfo := router.Handler.Info
|
||||||
|
|
||||||
|
// 非规范路由不处理
|
||||||
|
if funcInfo.Type.NumIn() != 2 {
|
||||||
|
r.Middleware.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
inputType := funcInfo.Type.In(1)
|
||||||
|
var inputObject reflect.Value
|
||||||
|
if inputType.Kind() == reflect.Ptr {
|
||||||
|
inputObject = reflect.New(inputType.Elem())
|
||||||
|
} else {
|
||||||
|
inputObject = reflect.New(inputType.Elem()).Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先验证基本校验规则
|
||||||
|
if err := r.Parse(inputObject.Interface()); err != nil {
|
||||||
|
resp := gerror.Code(err)
|
||||||
|
response.JsonExit(r, resp.Code(), gerror.Current(err).Error(), resp.Detail())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 没有实现预处理
|
||||||
|
if _, ok := inputObject.Interface().(validate.Filter); !ok {
|
||||||
|
r.Middleware.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行预处理
|
||||||
|
if err := validate.PreFilter(r.Context(), inputObject.Interface()); err != nil {
|
||||||
|
resp := gerror.Code(err)
|
||||||
|
response.JsonExit(r, resp.Code(), gerror.Current(err).Error(), resp.Detail())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤后的参数
|
||||||
|
r.SetParamMap(gconv.Map(inputObject.Interface()))
|
||||||
|
r.Middleware.Next()
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
// Package middleware
|
||||||
|
// @Link https://github.com/bufanyun/hotgo
|
||||||
|
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||||
|
// @Author Ms <133814250@qq.com>
|
||||||
|
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"hotgo/internal/consts"
|
||||||
|
"hotgo/internal/library/response"
|
||||||
|
"hotgo/utility/charset"
|
||||||
|
"hotgo/utility/simple"
|
||||||
|
|
||||||
|
"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/util/gmeta"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResponseHandler HTTP响应预处理
|
||||||
|
func (s *sMiddleware) ResponseHandler(r *ghttp.Request) {
|
||||||
|
r.Middleware.Next()
|
||||||
|
|
||||||
|
// 错误状态码接管
|
||||||
|
switch r.Response.Status {
|
||||||
|
case 403:
|
||||||
|
r.Response.Writeln("403 - 网站拒绝显示此网页")
|
||||||
|
return
|
||||||
|
case 404:
|
||||||
|
r.Response.Writeln("404 - 你似乎来到了没有知识存在的荒原…")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
contentType := getContentType(r)
|
||||||
|
// 已存在响应
|
||||||
|
if contentType != consts.HTTPContentTypeStream && r.Response.BufferLength() > 0 { // && contexts.Get(r.Context()).Response != nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch contentType {
|
||||||
|
case consts.HTTPContentTypeHtml:
|
||||||
|
s.responseHtml(r)
|
||||||
|
return
|
||||||
|
case consts.HTTPContentTypeXml:
|
||||||
|
s.responseXml(r)
|
||||||
|
return
|
||||||
|
case consts.HTTPContentTypeStream:
|
||||||
|
case consts.HTTPContentTypeOctetStream:
|
||||||
|
default:
|
||||||
|
responseJson(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// responseHtml html模板响应
|
||||||
|
func (s *sMiddleware) responseHtml(r *ghttp.Request) {
|
||||||
|
code, message, resp := parseResponse(r)
|
||||||
|
if code == gcode.CodeOK.Code() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Response.ClearBuffer()
|
||||||
|
_ = r.Response.WriteTplContent(simple.DefaultErrorTplContent(r.Context()), g.Map{"code": code, "message": message, "stack": resp})
|
||||||
|
}
|
||||||
|
|
||||||
|
// responseXml xml响应
|
||||||
|
func (s *sMiddleware) responseXml(r *ghttp.Request) {
|
||||||
|
code, message, data := parseResponse(r)
|
||||||
|
response.RXml(r, code, message, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// responseJson json响应
|
||||||
|
func responseJson(r *ghttp.Request) {
|
||||||
|
code, message, data := parseResponse(r)
|
||||||
|
response.RJson(r, code, message, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseResponse 解析响应数据
|
||||||
|
func parseResponse(r *ghttp.Request) (code int, message string, resp interface{}) {
|
||||||
|
ctx := r.Context()
|
||||||
|
err := r.GetError()
|
||||||
|
if err == nil {
|
||||||
|
return gcode.CodeOK.Code(), "操作成功", r.GetHandlerResponse()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否输出错误堆栈到页面
|
||||||
|
if simple.Debug(ctx) {
|
||||||
|
message = gerror.Current(err).Error()
|
||||||
|
if getContentType(r) == consts.HTTPContentTypeHtml {
|
||||||
|
resp = charset.SerializeStack(err)
|
||||||
|
} else {
|
||||||
|
resp = charset.ParseErrStack(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message = consts.ErrorMessage(gerror.Current(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
code = gerror.Code(err).Code()
|
||||||
|
|
||||||
|
// 记录异常日志
|
||||||
|
// 如果你想对错误做不同的处理,可以通过定义不同的错误码来区分
|
||||||
|
// 默认-1为安全可控错误码只记录文件日志,非-1为不可控错误,记录文件日志+服务日志并打印堆栈
|
||||||
|
if code == gcode.CodeNil.Code() {
|
||||||
|
g.Log().Stdout(false).Infof(ctx, "exception:%v", err)
|
||||||
|
} else {
|
||||||
|
g.Log().Errorf(ctx, "exception:%v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getContentType(r *ghttp.Request) (contentType string) {
|
||||||
|
contentType = r.Response.Header().Get("Content-Type")
|
||||||
|
if contentType != "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mime := gmeta.Get(r.GetHandlerResponse(), "mime").String()
|
||||||
|
if mime == "" {
|
||||||
|
contentType = consts.HTTPContentTypeJson
|
||||||
|
} else {
|
||||||
|
contentType = mime
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
// Package middleware
|
||||||
|
// @Link https://github.com/bufanyun/hotgo
|
||||||
|
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||||
|
// @Author Ms <133814250@qq.com>
|
||||||
|
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/errors/gcode"
|
||||||
|
"github.com/gogf/gf/v2/net/ghttp"
|
||||||
|
"github.com/gogf/gf/v2/text/gstr"
|
||||||
|
"hotgo/internal/consts"
|
||||||
|
"hotgo/internal/library/response"
|
||||||
|
"hotgo/utility/simple"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WebSocketAuth websocket鉴权中间件
|
||||||
|
func (s *sMiddleware) WebSocketAuth(r *ghttp.Request) {
|
||||||
|
var (
|
||||||
|
ctx = r.Context()
|
||||||
|
path = gstr.Replace(r.URL.Path, simple.RouterPrefix(ctx, consts.AppWebSocket), "", 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
// 不需要验证登录的路由地址
|
||||||
|
if s.IsExceptLogin(ctx, consts.AppWebSocket, path) {
|
||||||
|
r.Middleware.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将用户信息传递到上下文中
|
||||||
|
if err := s.DeliverUserContext(r); err != nil {
|
||||||
|
response.JsonExit(r, gcode.CodeNotAuthorized.Code(), err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Middleware.Next()
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
// Package pay
|
||||||
|
// @Link https://github.com/bufanyun/hotgo
|
||||||
|
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||||
|
// @Author Ms <133814250@qq.com>
|
||||||
|
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||||
|
package pay
|
||||||
|
|
||||||
|
// 订单创建
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
v1 "hotgo/api/api/pay/v1"
|
||||||
|
"hotgo/internal/consts"
|
||||||
|
"hotgo/internal/library/contexts"
|
||||||
|
"hotgo/internal/library/location"
|
||||||
|
"hotgo/internal/library/payment"
|
||||||
|
"hotgo/internal/model/entity"
|
||||||
|
"hotgo/internal/model/input/payin"
|
||||||
|
"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/net/ghttp"
|
||||||
|
"github.com/gogf/gf/v2/os/gctx"
|
||||||
|
"github.com/gogf/gf/v2/util/gmeta"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create 创建支付订单和日志
|
||||||
|
func (s *sPay) Create(ctx context.Context, in payin.PayCreateInp) (res *payin.PayCreateModel, err error) {
|
||||||
|
request := ghttp.RequestFromCtx(ctx)
|
||||||
|
if in.TradeType == "" {
|
||||||
|
in.TradeType = payment.AutoTradeType(in.PayType, request.UserAgent())
|
||||||
|
}
|
||||||
|
|
||||||
|
if in.Openid == "" {
|
||||||
|
if in.Openid, err = service.CommonWechat().GetOpenId(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if in.TradeType == consts.TradeTypeWxMP {
|
||||||
|
if in.Openid == "" {
|
||||||
|
err = gerror.New("微信公众号支付必须设置openid")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if in.ReturnUrl == "" {
|
||||||
|
err = gerror.New("微信公众号支付必须设置同步通知地址")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyURL, err := s.GenNotifyURL(ctx, in)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
config := payment.GetConfig()
|
||||||
|
mchId := ""
|
||||||
|
switch in.PayType {
|
||||||
|
case consts.PayTypeAliPay:
|
||||||
|
mchId = config.AliPayAppId
|
||||||
|
case consts.PayTypeWxPay:
|
||||||
|
mchId = config.WxPayMchId
|
||||||
|
case consts.PayTypeQQPay:
|
||||||
|
mchId = config.QQPayMchId
|
||||||
|
}
|
||||||
|
|
||||||
|
data := &entity.PayLog{
|
||||||
|
MemberId: contexts.GetUserId(ctx),
|
||||||
|
AppId: contexts.GetModule(ctx),
|
||||||
|
AddonsName: contexts.GetAddonName(ctx),
|
||||||
|
OrderSn: in.OrderSn,
|
||||||
|
OrderGroup: in.OrderGroup,
|
||||||
|
Openid: in.Openid,
|
||||||
|
MchId: mchId,
|
||||||
|
Subject: in.Subject,
|
||||||
|
Detail: in.Detail,
|
||||||
|
OutTradeNo: payment.GenOutTradeNo(ctx),
|
||||||
|
TransactionId: "",
|
||||||
|
PayType: in.PayType,
|
||||||
|
PayAmount: in.PayAmount,
|
||||||
|
PayStatus: consts.PayStatusWait,
|
||||||
|
TradeType: in.TradeType,
|
||||||
|
IsRefund: consts.RefundStatusNo,
|
||||||
|
CreateIp: location.GetClientIp(request),
|
||||||
|
NotifyUrl: notifyURL,
|
||||||
|
ReturnUrl: in.ReturnUrl,
|
||||||
|
TraceIds: gjson.New([]string{gctx.CtxId(ctx)}),
|
||||||
|
Status: consts.StatusEnabled,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建支付记录
|
||||||
|
if _, err = s.Model(ctx).Data(data).OmitEmptyData().Insert(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建第三方平台支付订单
|
||||||
|
order, err := payment.New(in.PayType).CreateOrder(ctx, payin.CreateOrderInp{Pay: data})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res = new(payin.PayCreateModel)
|
||||||
|
res.Order = order
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenNotifyURL 生成支付通知地址
|
||||||
|
func (s *sPay) GenNotifyURL(ctx context.Context, in payin.PayCreateInp) (notifyURL string, err error) {
|
||||||
|
basic, err := service.SysConfig().GetBasic(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if basic.Domain == "" {
|
||||||
|
err = gerror.New("请先到后台【系统设置】-【配置管理】中设置网站域名!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !validate.IsURL(basic.Domain) {
|
||||||
|
err = gerror.New("网站域名格式有误,请检查!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var object interface{}
|
||||||
|
switch in.PayType {
|
||||||
|
case consts.PayTypeAliPay:
|
||||||
|
object = v1.NotifyAliPayReq{}
|
||||||
|
case consts.PayTypeWxPay:
|
||||||
|
object = v1.NotifyWxPayReq{}
|
||||||
|
case consts.PayTypeQQPay:
|
||||||
|
object = v1.NotifyQQPayReq{}
|
||||||
|
default:
|
||||||
|
err = gerror.Newf("未被支持的支付方式:%v", in.PayType)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyURL = fmt.Sprintf("%s%s%s",
|
||||||
|
basic.Domain,
|
||||||
|
g.Cfg().MustGet(ctx, "router.api.prefix", "/api").String(),
|
||||||
|
gmeta.Get(object, "path").String(),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
// Package pay
|
||||||
|
// @Link https://github.com/bufanyun/hotgo
|
||||||
|
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||||
|
// @Author Ms <133814250@qq.com>
|
||||||
|
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||||
|
package pay
|
||||||
|
|
||||||
|
// 异步通知
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"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"
|
||||||
|
"hotgo/internal/consts"
|
||||||
|
"hotgo/internal/dao"
|
||||||
|
"hotgo/internal/library/location"
|
||||||
|
"hotgo/internal/library/payment"
|
||||||
|
"hotgo/internal/model/entity"
|
||||||
|
"hotgo/internal/model/input/payin"
|
||||||
|
"hotgo/internal/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterNotifyCall 注册支付成功回调方法
|
||||||
|
func (s *sPay) RegisterNotifyCall() {
|
||||||
|
payment.RegisterNotifyCallMap(map[string]payment.NotifyCallFunc{
|
||||||
|
consts.OrderGroupAdminOrder: service.AdminOrder().PayNotify, // 后台充值订单
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify 异步通知
|
||||||
|
func (s *sPay) Notify(ctx context.Context, in *payin.PayNotifyInp) (res *payin.PayNotifyModel, err error) {
|
||||||
|
data, err := payment.New(in.PayType).Notify(ctx, payin.NotifyInp{})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var models *entity.PayLog
|
||||||
|
if err = s.Model(ctx).Where(dao.PayLog.Columns().OutTradeNo, data.OutTradeNo).Scan(&models); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if models == nil {
|
||||||
|
err = gerror.Newf("商户订单号[%v]不存在支付记录,请检查", data.OutTradeNo)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if models.PayStatus != consts.PayStatusWait {
|
||||||
|
err = gerror.Newf("商户订单号[%v]已被处理,请勿重复操作", data.OutTradeNo)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var traceIds []string
|
||||||
|
if err = models.TraceIds.Scan(&traceIds); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
traceIds = append(traceIds, gctx.CtxId(ctx))
|
||||||
|
|
||||||
|
models.TransactionId = data.TransactionId
|
||||||
|
models.PayStatus = consts.PayStatusOk
|
||||||
|
models.PayAt = data.PayAt
|
||||||
|
models.ActualAmount = data.ActualAmount
|
||||||
|
models.PayIp = location.GetClientIp(ghttp.RequestFromCtx(ctx))
|
||||||
|
models.TraceIds = gjson.New(traceIds)
|
||||||
|
|
||||||
|
result, err := s.Model(ctx).
|
||||||
|
Fields(
|
||||||
|
dao.PayLog.Columns().TransactionId,
|
||||||
|
dao.PayLog.Columns().PayStatus,
|
||||||
|
dao.PayLog.Columns().PayAt,
|
||||||
|
dao.PayLog.Columns().PayIp,
|
||||||
|
dao.PayLog.Columns().TraceIds,
|
||||||
|
dao.PayLog.Columns().ActualAmount,
|
||||||
|
).
|
||||||
|
Where(dao.PayLog.Columns().Id, models.Id).
|
||||||
|
Where(dao.PayLog.Columns().PayStatus, consts.PayStatusWait).
|
||||||
|
OmitEmpty().
|
||||||
|
Data(models).Update()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret, err := result.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ret == 0 {
|
||||||
|
g.Log().Warningf(ctx, "没有被更新的数据行")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回调业务
|
||||||
|
payment.NotifyCall(ctx, &payin.NotifyCallFuncInp{Pay: models})
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
// Package pay
|
||||||
|
// @Link https://github.com/bufanyun/hotgo
|
||||||
|
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||||
|
// @Author Ms <133814250@qq.com>
|
||||||
|
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||||
|
package pay
|
||||||
|
|
||||||
|
// 支付日志相关
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"hotgo/internal/consts"
|
||||||
|
"hotgo/internal/dao"
|
||||||
|
"hotgo/internal/library/hgorm/handler"
|
||||||
|
"hotgo/internal/model/input/form"
|
||||||
|
"hotgo/internal/model/input/payin"
|
||||||
|
"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/util/gconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sPay struct{}
|
||||||
|
|
||||||
|
func NewPay() *sPay {
|
||||||
|
return &sPay{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
service.RegisterPay(NewPay())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Model 支付日志ORM模型
|
||||||
|
func (s *sPay) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
||||||
|
return handler.Model(dao.PayLog.Ctx(ctx), option...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List 获取支付日志列表
|
||||||
|
func (s *sPay) List(ctx context.Context, in payin.PayListInp) (list []*payin.PayListModel, totalCount int, err error) {
|
||||||
|
mod := s.Model(ctx)
|
||||||
|
|
||||||
|
// 查询ID
|
||||||
|
if in.Id > 0 {
|
||||||
|
mod = mod.Where(dao.PayLog.Columns().Id, in.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询状态
|
||||||
|
if in.Status > 0 {
|
||||||
|
mod = mod.Where(dao.PayLog.Columns().Status, in.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询创建时间
|
||||||
|
if len(in.CreatedAt) == 2 {
|
||||||
|
mod = mod.WhereBetween(dao.PayLog.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
totalCount, err = mod.Clone().Count()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if totalCount == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mod.Page(in.Page, in.PerPage).OrderDesc(dao.PayLog.Columns().Id).Scan(&list)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export 导出支付日志
|
||||||
|
func (s *sPay) Export(ctx context.Context, in payin.PayListInp) (err error) {
|
||||||
|
list, totalCount, err := s.List(ctx, in)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
|
||||||
|
tags, err := convert.GetEntityDescTags(payin.PayExportModel{})
|
||||||
|
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 []payin.PayExportModel
|
||||||
|
)
|
||||||
|
|
||||||
|
if err = gconv.Scan(list, &exports); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Edit 修改/新增支付日志
|
||||||
|
func (s *sPay) Edit(ctx context.Context, in payin.PayEditInp) (err error) {
|
||||||
|
// 修改
|
||||||
|
if in.Id > 0 {
|
||||||
|
_, err = s.Model(ctx).Where(dao.PayLog.Columns().Id, in.Id).Data(in).Update()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
_, err = s.Model(ctx, &handler.Option{FilterAuth: false}).Data(in).OmitEmptyData().Insert()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete 删除支付日志
|
||||||
|
func (s *sPay) Delete(ctx context.Context, in payin.PayDeleteInp) (err error) {
|
||||||
|
_, err = s.Model(ctx).Where(dao.PayLog.Columns().Id, in.Id).Delete()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// View 获取支付日志指定信息
|
||||||
|
func (s *sPay) View(ctx context.Context, in payin.PayViewInp) (res *payin.PayViewModel, err error) {
|
||||||
|
err = s.Model(ctx).Where(dao.PayLog.Columns().Id, in.Id).Scan(&res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status 更新支付日志状态
|
||||||
|
func (s *sPay) Status(ctx context.Context, in payin.PayStatusInp) (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 = s.Model(ctx).Where(dao.PayLog.Columns().Id, in.Id).Data(g.Map{
|
||||||
|
dao.PayLog.Columns().Status: in.Status,
|
||||||
|
}).Update()
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
// Package pay
|
||||||
|
// @Link https://github.com/bufanyun/hotgo
|
||||||
|
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||||
|
// @Author Ms <133814250@qq.com>
|
||||||
|
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||||
|
// @AutoGenerate Version 2.5.3
|
||||||
|
// @AutoGenerate Date 2023-04-15 15:59:58
|
||||||
|
package pay
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"hotgo/internal/consts"
|
||||||
|
"hotgo/internal/dao"
|
||||||
|
"hotgo/internal/library/hgorm/handler"
|
||||||
|
"hotgo/internal/library/location"
|
||||||
|
"hotgo/internal/library/payment"
|
||||||
|
"hotgo/internal/model/entity"
|
||||||
|
"hotgo/internal/model/input/form"
|
||||||
|
"hotgo/internal/model/input/payin"
|
||||||
|
"hotgo/internal/service"
|
||||||
|
"hotgo/utility/convert"
|
||||||
|
"hotgo/utility/excel"
|
||||||
|
|
||||||
|
"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/database/gdb"
|
||||||
|
"github.com/gogf/gf/v2/os/gctx"
|
||||||
|
"github.com/gogf/gf/v2/util/gconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 订单退款.
|
||||||
|
|
||||||
|
type sPayRefund struct{}
|
||||||
|
|
||||||
|
func NewPayRefund() *sPayRefund {
|
||||||
|
return &sPayRefund{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
service.RegisterPayRefund(NewPayRefund())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Model 交易退款ORM模型
|
||||||
|
func (s *sPayRefund) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
||||||
|
return handler.Model(dao.PayRefund.Ctx(ctx), option...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refund 订单退款
|
||||||
|
func (s *sPayRefund) Refund(ctx context.Context, in *payin.PayRefundInp) (res *payin.PayRefundModel, err error) {
|
||||||
|
var models *entity.PayLog
|
||||||
|
if err = service.Pay().Model(ctx).Where(dao.PayLog.Columns().OrderSn, in.OrderSn).Scan(&models); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if models == nil {
|
||||||
|
err = gerror.Newf("业务订单号[%v]不存在支付记录,请检查", in.OrderSn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if models.PayStatus != consts.PayStatusOk {
|
||||||
|
err = gerror.Newf("业务订单号[%v]未支付,无需退款", in.OrderSn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if models.IsRefund != consts.RefundStatusNo {
|
||||||
|
err = gerror.Newf("业务订单号[%v]退款已被处理,请勿重复操作", in.OrderSn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var traceIds []string
|
||||||
|
if err = models.TraceIds.Scan(&traceIds); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
traceIds = append(traceIds, gctx.CtxId(ctx))
|
||||||
|
|
||||||
|
refundSn := payment.GenRefundSn(ctx)
|
||||||
|
|
||||||
|
// 创建第三方平台退款
|
||||||
|
req := payin.RefundInp{
|
||||||
|
Pay: models,
|
||||||
|
RefundMoney: in.RefundMoney,
|
||||||
|
Reason: in.Reason,
|
||||||
|
Remark: in.Remark,
|
||||||
|
RefundSn: refundSn,
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = payment.New(models.PayType).Refund(ctx, req); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
models.RefundSn = refundSn
|
||||||
|
models.IsRefund = consts.RefundStatusAgree
|
||||||
|
models.TraceIds = gjson.New(traceIds)
|
||||||
|
|
||||||
|
result, err := s.Model(ctx).
|
||||||
|
Fields(
|
||||||
|
dao.PayLog.Columns().RefundSn,
|
||||||
|
dao.PayLog.Columns().IsRefund,
|
||||||
|
dao.PayLog.Columns().TraceIds,
|
||||||
|
).
|
||||||
|
Where(dao.PayLog.Columns().Id, models.Id).
|
||||||
|
OmitEmpty().
|
||||||
|
Data(models).Update()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret, err := result.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ret == 0 {
|
||||||
|
g.Log().Warningf(ctx, "Refund 没有被更新的数据行")
|
||||||
|
}
|
||||||
|
|
||||||
|
data := &entity.PayRefund{
|
||||||
|
Id: 0,
|
||||||
|
MemberId: models.MemberId,
|
||||||
|
AppId: models.AppId,
|
||||||
|
OrderSn: models.OrderSn,
|
||||||
|
RefundTradeNo: "",
|
||||||
|
RefundMoney: in.RefundMoney,
|
||||||
|
RefundWay: 1,
|
||||||
|
Ip: location.GetClientIp(ghttp.RequestFromCtx(ctx)),
|
||||||
|
Reason: in.Reason,
|
||||||
|
Remark: in.Remark,
|
||||||
|
Status: consts.RefundStatusAgree,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建退款记录
|
||||||
|
if _, err = s.Model(ctx).Data(data).OmitEmptyData().Insert(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// List 获取交易退款列表
|
||||||
|
func (s *sPayRefund) List(ctx context.Context, in *payin.PayRefundListInp) (list []*payin.PayRefundListModel, totalCount int, err error) {
|
||||||
|
mod := s.Model(ctx)
|
||||||
|
|
||||||
|
// 查询变动ID
|
||||||
|
if in.Id > 0 {
|
||||||
|
mod = mod.Where(dao.PayRefund.Columns().Id, in.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询管理员ID
|
||||||
|
if in.MemberId > 0 {
|
||||||
|
mod = mod.Where(dao.PayRefund.Columns().MemberId, in.MemberId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询应用id
|
||||||
|
if in.AppId != "" {
|
||||||
|
mod = mod.WhereLike(dao.PayRefund.Columns().AppId, in.AppId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询备注
|
||||||
|
if in.Remark != "" {
|
||||||
|
mod = mod.WhereLike(dao.PayRefund.Columns().Remark, in.Remark)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询操作人IP
|
||||||
|
if in.Ip != "" {
|
||||||
|
mod = mod.WhereLike(dao.PayRefund.Columns().Ip, in.Ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询状态
|
||||||
|
if in.Status > 0 {
|
||||||
|
mod = mod.Where(dao.PayRefund.Columns().Status, in.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询创建时间
|
||||||
|
if len(in.CreatedAt) == 2 {
|
||||||
|
mod = mod.WhereBetween(dao.PayRefund.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
totalCount, err = mod.Clone().Count()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if totalCount == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mod.Fields(payin.PayRefundListModel{}).Page(in.Page, in.PerPage).OrderDesc(dao.PayRefund.Columns().Id).Scan(&list)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export 导出交易退款
|
||||||
|
func (s *sPayRefund) Export(ctx context.Context, in *payin.PayRefundListInp) (err error) {
|
||||||
|
list, totalCount, err := s.List(ctx, in)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
|
||||||
|
tags, err := convert.GetEntityDescTags(payin.PayRefundExportModel{})
|
||||||
|
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 []payin.PayRefundExportModel
|
||||||
|
)
|
||||||
|
|
||||||
|
if err = gconv.Scan(list, &exports); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
// Package tcpclient
|
||||||
|
// @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 tcpclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gctx"
|
||||||
|
"hotgo/api/servmsg"
|
||||||
|
"hotgo/internal/library/network/tcp"
|
||||||
|
"hotgo/internal/model/input/servmsgin"
|
||||||
|
"hotgo/internal/service"
|
||||||
|
"hotgo/utility/simple"
|
||||||
|
)
|
||||||
|
|
||||||
|
// tcp客户端
|
||||||
|
type sAuthClient struct {
|
||||||
|
client *tcp.Client
|
||||||
|
summary *servmsgin.AuthSummaryModel
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
service.RegisterAuthClient(newAuthClient())
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAuthClient() *sAuthClient {
|
||||||
|
return &sAuthClient{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instance 获取实例
|
||||||
|
func (s *sAuthClient) Instance() *tcp.Client {
|
||||||
|
return s.client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start 启动服务
|
||||||
|
func (s *sAuthClient) Start(ctx context.Context) {
|
||||||
|
g.Log().Debug(ctx, "AuthClient start..")
|
||||||
|
|
||||||
|
config, err := service.SysConfig().GetLoadTCP(ctx)
|
||||||
|
if err != nil {
|
||||||
|
g.Log().Errorf(ctx, "AuthClient start fail:%+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Client == nil || config.Client.Auth == nil {
|
||||||
|
g.Log().Errorf(ctx, "AuthClient config is invalid")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建客户端配置
|
||||||
|
clientConfig := &tcp.ClientConfig{
|
||||||
|
Addr: config.Client.Auth.Address,
|
||||||
|
AutoReconnect: true,
|
||||||
|
Auth: &tcp.AuthMeta{
|
||||||
|
Name: config.Client.Auth.Name,
|
||||||
|
Extra: g.Map{
|
||||||
|
"test": 13,
|
||||||
|
},
|
||||||
|
Group: config.Client.Auth.Group,
|
||||||
|
AppId: config.Client.Auth.AppId,
|
||||||
|
SecretKey: config.Client.Auth.SecretKey,
|
||||||
|
},
|
||||||
|
LoginEvent: s.onLoginEvent,
|
||||||
|
CloseEvent: s.onCloseEvent,
|
||||||
|
}
|
||||||
|
|
||||||
|
simple.SafeGo(ctx, func(ctx context.Context) {
|
||||||
|
s.client = tcp.NewClient(clientConfig)
|
||||||
|
|
||||||
|
// 注册路由
|
||||||
|
s.client.RegisterRouter(
|
||||||
|
s.OnResponseAuthSummary, // 响应授权信息
|
||||||
|
s.OnResponseExampleHello, // 一个tcp请求例子
|
||||||
|
)
|
||||||
|
|
||||||
|
if err = s.client.Start(); err != nil {
|
||||||
|
g.Log().Errorf(ctx, "AuthClient Start fail:%+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop 停止服务
|
||||||
|
func (s *sAuthClient) Stop(ctx context.Context) {
|
||||||
|
if s.client != nil && !s.client.IsStop() {
|
||||||
|
s.client.Stop()
|
||||||
|
g.Log().Debug(ctx, "AuthClient stop..")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// onLoginEvent 登录认证成功事件
|
||||||
|
func (s *sAuthClient) onLoginEvent() {
|
||||||
|
ctx := gctx.New()
|
||||||
|
|
||||||
|
// 获取授权信息
|
||||||
|
_ = s.client.Send(ctx, &servmsg.AuthSummaryReq{})
|
||||||
|
|
||||||
|
// 测试例子,实际使用时可以注释掉
|
||||||
|
s.testExample(ctx)
|
||||||
|
|
||||||
|
g.Log().Debug(ctx, "AuthClient login succeed.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// onCloseEvent 连接关闭回调事件
|
||||||
|
func (s *sAuthClient) onCloseEvent() {
|
||||||
|
g.Log().Debug(gctx.New(), "AuthClient closed.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Summary 获取授权信息
|
||||||
|
func (s *sAuthClient) Summary() *servmsgin.AuthSummaryModel {
|
||||||
|
if s.summary == nil {
|
||||||
|
s.summary = new(servmsgin.AuthSummaryModel)
|
||||||
|
}
|
||||||
|
return s.summary
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
// Package tcpclient
|
||||||
|
// @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 tcpclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"hotgo/api/servmsg"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OnResponseAuthSummary 响应授权信息
|
||||||
|
func (s *sAuthClient) OnResponseAuthSummary(ctx context.Context, req *servmsg.AuthSummaryRes) {
|
||||||
|
if err := req.GetError(); err != nil {
|
||||||
|
g.Log().Warningf(ctx, "OnResponseAuthSummary GetError:%+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拿到授权的数据,可以是一些动态的功能、路由、权限控制等
|
||||||
|
s.summary = req.Data
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnResponseExampleHello 一个tcp请求例子
|
||||||
|
func (s *sAuthClient) OnResponseExampleHello(ctx context.Context, req *servmsg.ExampleHelloRes) {
|
||||||
|
if err := req.GetError(); err != nil {
|
||||||
|
g.Log().Warningf(ctx, "OnResponseExampleHello GetError:%+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
g.Log().Infof(ctx, "OnResponseExampleHello data:%+v", req.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// testExample 测试例子
|
||||||
|
func (s *sAuthClient) testExample(ctx context.Context) {
|
||||||
|
// 发起tcp请求
|
||||||
|
// 异步执行,服务端返回消息后会转到`OnResponseExampleHello`中
|
||||||
|
_ = s.client.Send(ctx, &servmsg.ExampleHelloReq{
|
||||||
|
Name: "Tom",
|
||||||
|
})
|
||||||
|
|
||||||
|
// 发起rpc请求
|
||||||
|
// 同步执行,阻塞等待服务端返回消息
|
||||||
|
var req = &servmsg.ExampleRPCHelloReq{
|
||||||
|
Name: "Tony",
|
||||||
|
}
|
||||||
|
var res *servmsg.ExampleRPCHelloRes
|
||||||
|
if err := s.client.RequestScan(ctx, req, &res); err != nil {
|
||||||
|
g.Log().Warningf(ctx, "client.Request ExampleRPCHelloReq err:%+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.Log().Infof(ctx, "ExampleRPCHelloRes data:%+v", res.Data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// Package tcpclient
|
||||||
|
// @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 tcpclient
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
// Package tcpclient
|
||||||
|
// @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 tcpclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gctx"
|
||||||
|
"hotgo/internal/library/network/tcp"
|
||||||
|
"hotgo/internal/service"
|
||||||
|
"hotgo/utility/simple"
|
||||||
|
)
|
||||||
|
|
||||||
|
// tcp客户端
|
||||||
|
type sCronClient struct {
|
||||||
|
client *tcp.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
service.RegisterCronClient(newCronClient())
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCronClient() *sCronClient {
|
||||||
|
return &sCronClient{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instance 获取实例
|
||||||
|
func (s *sCronClient) Instance() *tcp.Client {
|
||||||
|
return s.client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start 启动服务
|
||||||
|
func (s *sCronClient) Start(ctx context.Context) {
|
||||||
|
g.Log().Debug(ctx, "CronClient start..")
|
||||||
|
|
||||||
|
config, err := service.SysConfig().GetLoadTCP(ctx)
|
||||||
|
if err != nil {
|
||||||
|
g.Log().Errorf(ctx, "CronClient start fail:%+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if config == nil || config.Client == nil || config.Client.Cron == nil {
|
||||||
|
g.Log().Errorf(ctx, "CronClient config is invalid")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建客户端配置
|
||||||
|
clientConfig := &tcp.ClientConfig{
|
||||||
|
Addr: config.Client.Cron.Address,
|
||||||
|
AutoReconnect: true,
|
||||||
|
Auth: &tcp.AuthMeta{
|
||||||
|
Name: config.Client.Cron.Name,
|
||||||
|
Group: config.Client.Cron.Group,
|
||||||
|
AppId: config.Client.Cron.AppId,
|
||||||
|
SecretKey: config.Client.Cron.SecretKey,
|
||||||
|
},
|
||||||
|
LoginEvent: s.onLoginEvent,
|
||||||
|
CloseEvent: s.onCloseEvent,
|
||||||
|
}
|
||||||
|
|
||||||
|
simple.SafeGo(ctx, func(ctx context.Context) {
|
||||||
|
s.client = tcp.NewClient(clientConfig)
|
||||||
|
|
||||||
|
// 注册RPC路由
|
||||||
|
s.client.RegisterRPCRouter(
|
||||||
|
s.OnCronDelete, // 删除任务
|
||||||
|
s.OnCronEdit, // 编辑任务
|
||||||
|
s.OnCronStatus, // 修改任务状态
|
||||||
|
s.OnCronOnlineExec, // 执行一次任务
|
||||||
|
s.OnCronDispatchLog, // 查看调度日志
|
||||||
|
)
|
||||||
|
|
||||||
|
// 注册拦截器
|
||||||
|
s.client.RegisterInterceptor(s.DefaultInterceptor)
|
||||||
|
|
||||||
|
if err = s.client.Start(); err != nil {
|
||||||
|
g.Log().Errorf(ctx, "CronClient Start fail:%+v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop 停止服务
|
||||||
|
func (s *sCronClient) Stop(ctx context.Context) {
|
||||||
|
if s.client != nil && !s.client.IsStop() {
|
||||||
|
s.client.Stop()
|
||||||
|
g.Log().Debug(ctx, "CronClient stop..")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// onLoginEvent 登录认证成功事件
|
||||||
|
func (s *sCronClient) onLoginEvent() {
|
||||||
|
g.Log().Debug(gctx.New(), "CronClient login succeed.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// onCloseEvent 连接关闭回调事件
|
||||||
|
func (s *sCronClient) onCloseEvent() {
|
||||||
|
g.Log().Debug(gctx.New(), "CronClient closed.")
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// Package tcpclient
|
||||||
|
// @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 tcpclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"hotgo/api/servmsg"
|
||||||
|
"hotgo/internal/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OnCronDelete 删除任务
|
||||||
|
func (s *sCronClient) OnCronDelete(ctx context.Context, req *servmsg.CronDeleteReq) (res *servmsg.CronDeleteRes, err error) {
|
||||||
|
err = service.SysCron().Delete(ctx, req.CronDeleteInp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnCronEdit 编辑任务
|
||||||
|
func (s *sCronClient) OnCronEdit(ctx context.Context, req *servmsg.CronEditReq) (res *servmsg.CronEditRes, err error) {
|
||||||
|
err = service.SysCron().Edit(ctx, req.CronEditInp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnCronStatus 修改任务状态
|
||||||
|
func (s *sCronClient) OnCronStatus(ctx context.Context, req *servmsg.CronStatusReq) (res *servmsg.CronStatusRes, err error) {
|
||||||
|
err = service.SysCron().Status(ctx, req.CronStatusInp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnCronOnlineExec 执行一次任务
|
||||||
|
func (s *sCronClient) OnCronOnlineExec(ctx context.Context, req *servmsg.CronOnlineExecReq) (res *servmsg.CronOnlineExecRes, err error) {
|
||||||
|
err = service.SysCron().OnlineExec(ctx, req.OnlineExecInp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnCronDispatchLog 查看调度日志
|
||||||
|
func (s *sCronClient) OnCronDispatchLog(ctx context.Context, req *servmsg.CronDispatchLogReq) (res *servmsg.CronDispatchLogRes, err error) {
|
||||||
|
res = new(servmsg.CronDispatchLogRes)
|
||||||
|
res.DispatchLogModel, err = service.SysCron().DispatchLog(ctx, req.DispatchLogInp)
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// Package tcpclient
|
||||||
|
// @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 tcpclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"hotgo/internal/library/network/tcp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultInterceptor 默认拦截器
|
||||||
|
func (s *sCronClient) DefaultInterceptor(ctx context.Context, msg *tcp.Message) (err error) {
|
||||||
|
//conn := tcp.ConnFromCtx(ctx)
|
||||||
|
//g.Log().Warningf(ctx, "DefaultInterceptor msg:%+v, conn:%+v", msg, gjson.New(conn).String())
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
// Package tcpserver
|
||||||
|
// @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 tcpserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/gogf/gf/v2/errors/gerror"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
"hotgo/api/servmsg"
|
||||||
|
"hotgo/internal/consts"
|
||||||
|
"hotgo/internal/dao"
|
||||||
|
"hotgo/internal/library/network/tcp"
|
||||||
|
"hotgo/internal/model/entity"
|
||||||
|
"hotgo/internal/model/input/servmsgin"
|
||||||
|
"hotgo/internal/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OnAuthSummary 获取授权信息
|
||||||
|
func (s *sTCPServer) OnAuthSummary(ctx context.Context, req *servmsg.AuthSummaryReq) {
|
||||||
|
var (
|
||||||
|
conn = tcp.ConnFromCtx(ctx)
|
||||||
|
models *entity.SysServeLicense
|
||||||
|
res = new(servmsg.AuthSummaryRes)
|
||||||
|
)
|
||||||
|
|
||||||
|
if conn == nil {
|
||||||
|
g.Log().Warningf(ctx, "conn is nil.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if conn.Auth == nil {
|
||||||
|
res.SetError(gerror.New("登录信息获取失败,请重新登录"))
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := dao.SysServeLicense.Ctx(ctx).Where("appid = ?", conn.Auth.AppId).Scan(&models); err != nil {
|
||||||
|
res.SetError(err)
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if models == nil {
|
||||||
|
res.SetError(gerror.New("授权信息不存在"))
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if models.Status != consts.StatusEnabled {
|
||||||
|
res.SetError(gerror.New("授权已禁用,请联系管理员"))
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if models.Group != conn.Auth.Group {
|
||||||
|
res.SetError(gerror.New("你登录的授权分组未得到授权,请联系管理员"))
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if models.EndAt.Before(gtime.Now()) {
|
||||||
|
res.SetError(gerror.New("授权已过期,请联系管理员"))
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data := new(servmsgin.AuthSummaryModel)
|
||||||
|
data.EndAt = models.EndAt
|
||||||
|
data.Online = service.TCPServer().Instance().GetAppIdOnline(models.Appid)
|
||||||
|
|
||||||
|
// 请填充你的授权数据
|
||||||
|
// ...
|
||||||
|
|
||||||
|
res.Data = data
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
// Package tcpserver
|
||||||
|
// @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 tcpserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/gogf/gf/v2/errors/gerror"
|
||||||
|
"hotgo/api/servmsg"
|
||||||
|
"hotgo/internal/consts"
|
||||||
|
"hotgo/internal/library/cron"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CronDelete 删除任务
|
||||||
|
func (s *sTCPServer) CronDelete(ctx context.Context, in *servmsg.CronDeleteReq) (err error) {
|
||||||
|
clients := s.serv.GetGroupClients(consts.LicenseGroupCron)
|
||||||
|
if len(clients) == 0 {
|
||||||
|
err = gerror.New("没有在线的定时任务服务")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, client := range clients {
|
||||||
|
var res servmsg.CronDeleteRes
|
||||||
|
if err = s.serv.RequestScan(ctx, client, in, &res); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = res.GetError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CronEdit 编辑任务
|
||||||
|
func (s *sTCPServer) CronEdit(ctx context.Context, in *servmsg.CronEditReq) (err error) {
|
||||||
|
clients := s.serv.GetGroupClients(consts.LicenseGroupCron)
|
||||||
|
if len(clients) == 0 {
|
||||||
|
err = gerror.New("没有在线的定时任务服务")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, client := range clients {
|
||||||
|
var res servmsg.CronEditRes
|
||||||
|
if err = s.serv.RequestScan(ctx, client, in, &res); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = res.GetError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CronStatus 修改任务状态
|
||||||
|
func (s *sTCPServer) CronStatus(ctx context.Context, in *servmsg.CronStatusReq) (err error) {
|
||||||
|
clients := s.serv.GetGroupClients(consts.LicenseGroupCron)
|
||||||
|
if len(clients) == 0 {
|
||||||
|
err = gerror.New("没有在线的定时任务服务")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, client := range clients {
|
||||||
|
var res servmsg.CronStatusRes
|
||||||
|
if err = s.serv.RequestScan(ctx, client, in, &res); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = res.GetError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CronOnlineExec 执行一次任务
|
||||||
|
func (s *sTCPServer) CronOnlineExec(ctx context.Context, in *servmsg.CronOnlineExecReq) (err error) {
|
||||||
|
clients := s.serv.GetGroupClients(consts.LicenseGroupCron)
|
||||||
|
if len(clients) == 0 {
|
||||||
|
err = gerror.New("没有在线的定时任务服务")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, client := range clients {
|
||||||
|
var res servmsg.CronOnlineExecRes
|
||||||
|
if err = s.serv.RequestScan(ctx, client, in, &res); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = res.GetError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DispatchLog 查看调度日志
|
||||||
|
func (s *sTCPServer) DispatchLog(ctx context.Context, in *servmsg.CronDispatchLogReq) (log *cron.Log, err error) {
|
||||||
|
clients := s.serv.GetGroupClients(consts.LicenseGroupCron)
|
||||||
|
if len(clients) == 0 {
|
||||||
|
err = gerror.New("没有在线的定时任务服务")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var res servmsg.CronDispatchLogRes
|
||||||
|
if err = s.serv.RequestScan(ctx, clients[0], in, &res); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = res.GetError(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return res.Log, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
// Package tcpserver
|
||||||
|
// @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 tcpserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gogf/gf/v2/errors/gerror"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
"hotgo/api/servmsg"
|
||||||
|
"hotgo/internal/consts"
|
||||||
|
"hotgo/internal/library/network/tcp"
|
||||||
|
"hotgo/internal/model/input/servmsgin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OnExampleHello 一个tcp请求例子
|
||||||
|
func (s *sTCPServer) OnExampleHello(ctx context.Context, req *servmsg.ExampleHelloReq) {
|
||||||
|
var (
|
||||||
|
conn = tcp.ConnFromCtx(ctx)
|
||||||
|
res = new(servmsg.ExampleHelloRes)
|
||||||
|
data = new(servmsgin.ExampleHelloModel)
|
||||||
|
)
|
||||||
|
|
||||||
|
if conn == nil {
|
||||||
|
g.Log().Warningf(ctx, "conn is nil.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if conn.Auth == nil {
|
||||||
|
res.SetError(gerror.New("连接未认证,请确认已登录成功!"))
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data.Desc = fmt.Sprintf("Hello %v, 你的APPID:%v,当前HotGo版本:%v,你成功请求了`servmsg.ExampleHelloReq`接口!", req.Name, conn.Auth.AppId, consts.VersionApp)
|
||||||
|
data.Timestamp = gtime.Now()
|
||||||
|
res.Data = data
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnExampleRPCHello 一个rpc请求例子
|
||||||
|
func (s *sTCPServer) OnExampleRPCHello(ctx context.Context, req *servmsg.ExampleRPCHelloReq) (res *servmsg.ExampleRPCHelloRes, err error) {
|
||||||
|
var data = new(servmsgin.ExampleHelloModel)
|
||||||
|
data.Desc = fmt.Sprintf("Hello %v, 当前HotGo版本:%v,你成功请求了`servmsg.ExampleRPCHelloReq`接口!", req.Name, consts.VersionApp)
|
||||||
|
data.Timestamp = gtime.Now()
|
||||||
|
|
||||||
|
res = new(servmsg.ExampleRPCHelloRes)
|
||||||
|
res.Data = data
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
// Package tcpserver
|
||||||
|
// @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 tcpserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"hotgo/internal/library/network/tcp"
|
||||||
|
"hotgo/internal/service"
|
||||||
|
"hotgo/utility/simple"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sTCPServer struct {
|
||||||
|
serv *tcp.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
service.RegisterTCPServer(newTCPServer())
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTCPServer() *sTCPServer {
|
||||||
|
return &sTCPServer{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instance 获取实例
|
||||||
|
func (s *sTCPServer) Instance() *tcp.Server {
|
||||||
|
return s.serv
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start 启动服务
|
||||||
|
func (s *sTCPServer) Start(ctx context.Context) {
|
||||||
|
simple.SafeGo(ctx, func(ctx context.Context) {
|
||||||
|
g.Log().Debug(ctx, "TCPServer start..")
|
||||||
|
|
||||||
|
s.serv = tcp.NewServer(&tcp.ServerConfig{
|
||||||
|
Name: simple.AppName(ctx),
|
||||||
|
Addr: g.Cfg().MustGet(ctx, "tcp.server.address").String(),
|
||||||
|
})
|
||||||
|
|
||||||
|
// 注册路由
|
||||||
|
s.serv.RegisterRouter(
|
||||||
|
s.onServerLogin, // 服务登录
|
||||||
|
s.onServerHeartbeat, // 心跳
|
||||||
|
s.OnAuthSummary, // 获取授权信息
|
||||||
|
s.OnExampleHello, // 一个tcp请求例子
|
||||||
|
)
|
||||||
|
|
||||||
|
// 注册RPC路由
|
||||||
|
s.serv.RegisterRPCRouter(
|
||||||
|
s.OnExampleRPCHello, // 一个rpc请求例子
|
||||||
|
)
|
||||||
|
|
||||||
|
// 注册拦截器
|
||||||
|
s.serv.RegisterInterceptor(s.DefaultInterceptor, s.PreFilterInterceptor)
|
||||||
|
|
||||||
|
// 服务监听
|
||||||
|
if err := s.serv.Listen(); err != nil {
|
||||||
|
if !s.serv.IsClose() {
|
||||||
|
g.Log().Warningf(ctx, "TCPServer Listen err:%v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop 关闭服务
|
||||||
|
func (s *sTCPServer) Stop(ctx context.Context) {
|
||||||
|
if s.serv != nil && !s.serv.IsClose() {
|
||||||
|
s.serv.Close()
|
||||||
|
g.Log().Debug(ctx, "TCPServer stop..")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
// Package tcpserver
|
||||||
|
// @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 tcpserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"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"
|
||||||
|
"hotgo/internal/consts"
|
||||||
|
"hotgo/internal/dao"
|
||||||
|
"hotgo/internal/library/network/tcp"
|
||||||
|
"hotgo/internal/model/entity"
|
||||||
|
"hotgo/utility/convert"
|
||||||
|
"hotgo/utility/encrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// onServerLogin 处理客户端登录
|
||||||
|
func (s *sTCPServer) onServerLogin(ctx context.Context, req *tcp.ServerLoginReq) {
|
||||||
|
var (
|
||||||
|
conn = tcp.ConnFromCtx(ctx)
|
||||||
|
models *entity.SysServeLicense
|
||||||
|
res = new(tcp.ServerLoginRes)
|
||||||
|
cols = dao.SysServeLicense.Columns()
|
||||||
|
)
|
||||||
|
|
||||||
|
if conn == nil {
|
||||||
|
g.Log().Warningf(ctx, "conn is nil.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := dao.SysServeLicense.Ctx(ctx).Where(cols.Appid, req.AppId).Scan(&models); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if models == nil {
|
||||||
|
res.SetError(gerror.New("授权信息不存在"))
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证签名
|
||||||
|
sign := encrypt.Md5ToString(fmt.Sprintf("%v%v%v", models.Appid, req.Timestamp, models.SecretKey))
|
||||||
|
if sign != req.Sign {
|
||||||
|
res.SetError(gerror.New("签名错误,请检查!"))
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if models.Status != consts.StatusEnabled {
|
||||||
|
res.SetError(gerror.New("授权已禁用,请联系管理员"))
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if models.Group != req.Group {
|
||||||
|
res.SetError(gerror.New("你登录的授权分组未得到授权,请联系管理员"))
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if models.EndAt.Before(gtime.Now()) {
|
||||||
|
res.SetError(gerror.New("授权已过期,请联系管理员"))
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := gstr.StrTillEx(conn.RemoteAddr().String(), ":")
|
||||||
|
if !convert.MatchIpStrategy(models.AllowedIps, ip) {
|
||||||
|
res.SetError(gerror.New("IP(" + ip + ")未授权,请联系管理员"))
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var routes []string
|
||||||
|
if err := models.Routes.Scan(&routes); err != nil {
|
||||||
|
res.SetError(gerror.New("授权路由解析失败,请联系管理员"))
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拿出当前登录应用的所有客户端
|
||||||
|
clients := s.serv.GetAppIdClients(models.Appid)
|
||||||
|
|
||||||
|
// 检查多地登录,如果连接超过上限,则断开当前许可证下的所有连接
|
||||||
|
if len(clients)+1 > models.OnlineLimit {
|
||||||
|
for _, client := range clients {
|
||||||
|
client.Close()
|
||||||
|
}
|
||||||
|
res.SetError(gerror.New("授权登录端超出上限,请勿多地登录"))
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, client := range clients {
|
||||||
|
if client.Auth.Name == req.Name {
|
||||||
|
res.SetError(gerror.Newf("应用名称[%v]已存在登录用户,当前连接已被拒绝。", req.Name))
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auth := &tcp.AuthMeta{
|
||||||
|
Name: req.Name,
|
||||||
|
Extra: req.Extra,
|
||||||
|
Group: models.Group,
|
||||||
|
AppId: models.Appid,
|
||||||
|
SecretKey: models.SecretKey,
|
||||||
|
EndAt: models.EndAt,
|
||||||
|
Routes: routes,
|
||||||
|
}
|
||||||
|
s.serv.AuthClient(conn, auth)
|
||||||
|
|
||||||
|
update := g.Map{
|
||||||
|
cols.LoginTimes: models.LoginTimes + 1,
|
||||||
|
cols.LastLoginAt: gtime.Now(),
|
||||||
|
cols.LastActiveAt: gtime.Now(),
|
||||||
|
cols.RemoteAddr: conn.RemoteAddr().String(),
|
||||||
|
}
|
||||||
|
if _, err := dao.SysServeLicense.Ctx(ctx).Where(cols.Id, models.Id).Data(update).Update(); err != nil {
|
||||||
|
res.SetError(err)
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
g.Log().Debugf(ctx, "onServerLogin succeed. appid:%v, group:%v, name:%v", auth.AppId, auth.Group, auth.Name)
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// onServerHeartbeat 处理客户端心跳
|
||||||
|
func (s *sTCPServer) onServerHeartbeat(ctx context.Context, req *tcp.ServerHeartbeatReq) {
|
||||||
|
var (
|
||||||
|
conn = tcp.ConnFromCtx(ctx)
|
||||||
|
res = new(tcp.ServerHeartbeatRes)
|
||||||
|
)
|
||||||
|
|
||||||
|
if conn == nil {
|
||||||
|
g.Log().Warningf(ctx, "conn is nil.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
client := s.serv.GetClient(conn.Conn)
|
||||||
|
if client == nil {
|
||||||
|
res.SetError(gerror.New("登录异常,请重新登录"))
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新心跳
|
||||||
|
client.Heartbeat = gtime.Timestamp()
|
||||||
|
|
||||||
|
// 更新活跃时间
|
||||||
|
update := g.Map{
|
||||||
|
dao.SysServeLicense.Columns().LastActiveAt: gtime.Now(),
|
||||||
|
}
|
||||||
|
if _, err := dao.SysServeLicense.Ctx(ctx).Where(dao.SysServeLicense.Columns().Appid, client.Auth.AppId).Data(update).Update(); err != nil {
|
||||||
|
res.SetError(err)
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = conn.Send(ctx, res)
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
// Package tcpserver
|
||||||
|
// @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 tcpserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/gogf/gf/v2/errors/gcode"
|
||||||
|
"github.com/gogf/gf/v2/errors/gerror"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
"hotgo/internal/library/network/tcp"
|
||||||
|
"hotgo/utility/validate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 免登录路由
|
||||||
|
var noLoginRouter = map[string]struct{}{
|
||||||
|
"ServerLoginReq": {}, // 服务登录
|
||||||
|
}
|
||||||
|
|
||||||
|
// 免验证路由
|
||||||
|
var noVerifyRouter = map[string]struct{}{
|
||||||
|
"ServerHeartbeatReq": {}, // 心跳
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sTCPServer) isNoLoginRouter(router string) bool {
|
||||||
|
_, ok := noLoginRouter[router]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sTCPServer) isNoVerifyRouter(router string) bool {
|
||||||
|
_, ok := noVerifyRouter[router]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultInterceptor 默认拦截器
|
||||||
|
func (s *sTCPServer) DefaultInterceptor(ctx context.Context, msg *tcp.Message) (err error) {
|
||||||
|
conn := tcp.ConnFromCtx(ctx)
|
||||||
|
// g.Log().Debugf(ctx, "DefaultInterceptor msg:%+v, conn:%+v", msg, gjson.New(conn).String())
|
||||||
|
|
||||||
|
// 免登录
|
||||||
|
if s.isNoLoginRouter(msg.Router) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if conn.Auth == nil {
|
||||||
|
err = gerror.NewCode(gcode.CodeNotAuthorized, "未进行登录认证,请先登录")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查授权有效期
|
||||||
|
if conn.Auth.EndAt.Before(gtime.Now()) {
|
||||||
|
err = gerror.NewCode(gcode.CodeNotAuthorized, "授权已过期")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 免验证
|
||||||
|
if s.isNoVerifyRouter(msg.Router) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证路由权限
|
||||||
|
if len(conn.Auth.Routes) > 0 && !validate.InSlice(conn.Auth.Routes, msg.Router) {
|
||||||
|
err = gerror.NewCodef(gcode.CodeNotAuthorized, "没有授权路由访问权限:%v", msg.Router)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// PreFilterInterceptor 预处理
|
||||||
|
func (s *sTCPServer) PreFilterInterceptor(ctx context.Context, msg *tcp.Message) (err error) {
|
||||||
|
// g.Log().Debug(ctx, "PreFilterInterceptor...")
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
// Package view
|
||||||
|
// @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 view
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/gogf/gf/v2/errors/gerror"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gfile"
|
||||||
|
"github.com/gogf/gf/v2/os/gres"
|
||||||
|
"github.com/gogf/gf/v2/text/gstr"
|
||||||
|
"github.com/gogf/gf/v2/util/gconv"
|
||||||
|
"hotgo/internal/library/addons"
|
||||||
|
"hotgo/internal/library/contexts"
|
||||||
|
"hotgo/internal/model"
|
||||||
|
"hotgo/internal/service"
|
||||||
|
"hotgo/utility/charset"
|
||||||
|
"hotgo/utility/simple"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sView struct{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
service.RegisterView(New())
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() *sView {
|
||||||
|
return &sView{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBreadCrumb 前台系统-获取面包屑列表
|
||||||
|
func (s *sView) GetBreadCrumb(ctx context.Context, in *model.ViewGetBreadCrumbInput) []model.ViewBreadCrumb {
|
||||||
|
breadcrumb := []model.ViewBreadCrumb{
|
||||||
|
{Name: "首页", Url: "/"},
|
||||||
|
}
|
||||||
|
return breadcrumb
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTitle 前台系统-获取标题
|
||||||
|
func (s *sView) GetTitle(ctx context.Context, in *model.ViewGetTitleInput) string {
|
||||||
|
return "title"
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderTpl 渲染指定模板页面
|
||||||
|
func (s *sView) RenderTpl(ctx context.Context, tpl string, data ...model.View) {
|
||||||
|
var (
|
||||||
|
viewObj = model.View{}
|
||||||
|
request = g.RequestFromCtx(ctx)
|
||||||
|
)
|
||||||
|
if len(data) > 0 {
|
||||||
|
viewObj = data[0]
|
||||||
|
}
|
||||||
|
if viewObj.Title == "" {
|
||||||
|
viewObj.Title = g.Cfg().MustGet(ctx, `setting.title`).String()
|
||||||
|
} else {
|
||||||
|
viewObj.Title = viewObj.Title + ` - ` + g.Cfg().MustGet(ctx, `setting.title`).String()
|
||||||
|
}
|
||||||
|
if viewObj.Keywords == "" {
|
||||||
|
viewObj.Keywords = g.Cfg().MustGet(ctx, `setting.keywords`).String()
|
||||||
|
}
|
||||||
|
if viewObj.Description == "" {
|
||||||
|
viewObj.Description = g.Cfg().MustGet(ctx, `setting.description`).String()
|
||||||
|
}
|
||||||
|
if viewObj.IpcCode == "" {
|
||||||
|
viewObj.IpcCode = g.Cfg().MustGet(ctx, `setting.icpCode`).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
if viewObj.GET == nil {
|
||||||
|
viewObj.GET = request.GetQueryMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去掉空数据
|
||||||
|
viewData := gconv.Map(viewObj)
|
||||||
|
for k, v := range viewData {
|
||||||
|
if g.IsEmpty(v) {
|
||||||
|
delete(viewData, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 内置对象
|
||||||
|
viewData["BuildIn"] = &viewBuildIn{httpRequest: request}
|
||||||
|
|
||||||
|
// 插件模板,兼容从资源文件中读取
|
||||||
|
if addonName := contexts.GetAddonName(ctx); len(addonName) > 0 {
|
||||||
|
basePath := addons.GetResourcePath(ctx)
|
||||||
|
if len(basePath) > 0 && !gstr.Contains(tpl, basePath) {
|
||||||
|
path := addons.ViewPath(addonName, basePath)
|
||||||
|
tpl = path + "/" + tpl
|
||||||
|
}
|
||||||
|
|
||||||
|
content := ""
|
||||||
|
if !gres.IsEmpty() && gres.Contains(tpl) {
|
||||||
|
content = string(gres.GetContent(tpl))
|
||||||
|
} else {
|
||||||
|
content = gfile.GetContents(tpl)
|
||||||
|
}
|
||||||
|
_ = request.Response.WriteTplContent(content, viewData)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 渲染模板
|
||||||
|
_ = request.Response.WriteTpl(tpl, viewData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render 渲染默认模板页面
|
||||||
|
func (s *sView) Render(ctx context.Context, data ...model.View) {
|
||||||
|
s.RenderTpl(ctx, g.Cfg().MustGet(ctx, "viewer.homeLayout").String(), data...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error 自定义错误页面
|
||||||
|
func (s *sView) Error(ctx context.Context, err error) {
|
||||||
|
var (
|
||||||
|
request = g.RequestFromCtx(ctx)
|
||||||
|
code = gerror.Code(err)
|
||||||
|
stack string
|
||||||
|
)
|
||||||
|
|
||||||
|
// 是否输出错误堆栈到页面
|
||||||
|
if simple.Debug(ctx) {
|
||||||
|
stack = charset.SerializeStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
request.Response.ClearBuffer()
|
||||||
|
_ = request.Response.WriteTplContent(simple.DefaultErrorTplContent(ctx), g.Map{
|
||||||
|
"code": code.Code(),
|
||||||
|
"message": code.Message(),
|
||||||
|
"stack": stack,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
// Package view
|
||||||
|
// @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 view
|
||||||
|
|
||||||
|
import (
|
||||||
|
"hotgo/internal/consts"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/v2/net/ghttp"
|
||||||
|
"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 viewBuildIn struct {
|
||||||
|
httpRequest *ghttp.Request
|
||||||
|
}
|
||||||
|
|
||||||
|
// Page 创建分页HTML内容
|
||||||
|
func (s *viewBuildIn) Page(total, size int) string {
|
||||||
|
page := s.httpRequest.GetPage(total, size)
|
||||||
|
page.LinkStyle = "page-link"
|
||||||
|
page.SpanStyle = "page-link"
|
||||||
|
page.PrevPageTag = "«"
|
||||||
|
page.NextPageTag = "»"
|
||||||
|
content := page.PrevPage() + page.PageBar() + page.NextPage()
|
||||||
|
content = gstr.ReplaceByMap(content, map[string]string{
|
||||||
|
"<span": "<li class=\"page-item disabled\"><span",
|
||||||
|
"/span>": "/span></li>",
|
||||||
|
"<a": "<li class=\"page-item\"><a",
|
||||||
|
"/a>": "/a></li>",
|
||||||
|
})
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
|
// UrlPath 获取当前页面的Url Path.
|
||||||
|
func (s *viewBuildIn) UrlPath() string {
|
||||||
|
return s.httpRequest.URL.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version 随机数 开发环境时间戳,线上为前端版本号
|
||||||
|
func (s *viewBuildIn) Version() string {
|
||||||
|
var rand string
|
||||||
|
if gmode.IsDevelop() {
|
||||||
|
rand = gconv.String(gtime.TimestampMilli())
|
||||||
|
} else {
|
||||||
|
rand = consts.VersionApp
|
||||||
|
}
|
||||||
|
return rand
|
||||||
|
}
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
// Package model
|
||||||
|
// @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 model
|
||||||
|
|
||||||
|
// 后台配置.
|
||||||
|
|
||||||
|
// BasicConfig 基础配置
|
||||||
|
type BasicConfig struct {
|
||||||
|
CaptchaSwitch int `json:"basicCaptchaSwitch"`
|
||||||
|
CloseText string `json:"basicCloseText"`
|
||||||
|
Copyright string `json:"basicCopyright"`
|
||||||
|
IcpCode string `json:"basicIcpCode"`
|
||||||
|
Logo string `json:"basicLogo"`
|
||||||
|
Name string `json:"basicName"`
|
||||||
|
Domain string `json:"basicDomain"`
|
||||||
|
WsAddr string `json:"basicWsAddr"`
|
||||||
|
RegisterSwitch int `json:"basicRegisterSwitch"`
|
||||||
|
SystemOpen bool `json:"basicSystemOpen"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EmailTemplate 邮件模板
|
||||||
|
type EmailTemplate struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EmailConfig 邮箱配置
|
||||||
|
type EmailConfig struct {
|
||||||
|
User string `json:"smtpUser"`
|
||||||
|
Password string `json:"smtpPass"`
|
||||||
|
Addr string `json:"smtpAddr"`
|
||||||
|
Host string `json:"smtpHost"`
|
||||||
|
Port int64 `json:"smtpPort"`
|
||||||
|
SendName string `json:"smtpSendName"`
|
||||||
|
AdminMailbox string `json:"smtpAdminMailbox"`
|
||||||
|
MinInterval int `json:"smtpMinInterval"`
|
||||||
|
MaxIpLimit int `json:"smtpMaxIpLimit"`
|
||||||
|
CodeExpire int `json:"smtpCodeExpire"`
|
||||||
|
Template []*EmailTemplate `json:"smtpTemplate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CashConfig 提现配置
|
||||||
|
type CashConfig struct {
|
||||||
|
Switch bool `json:"cashSwitch"`
|
||||||
|
MinFee float64 `json:"cashMinFee"`
|
||||||
|
MinFeeRatio float64 `json:"cashMinFeeRatio"`
|
||||||
|
MinMoney float64 `json:"cashMinMoney"`
|
||||||
|
Tips string `json:"cashTips"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadConfig 上传配置
|
||||||
|
type UploadConfig struct {
|
||||||
|
// 通用配置
|
||||||
|
Drive string `json:"uploadDrive"`
|
||||||
|
FileSize int64 `json:"uploadFileSize"`
|
||||||
|
FileType string `json:"uploadFileType"`
|
||||||
|
ImageSize int64 `json:"uploadImageSize"`
|
||||||
|
ImageType string `json:"uploadImageType"`
|
||||||
|
// 本地存储配置
|
||||||
|
LocalPath string `json:"uploadLocalPath"`
|
||||||
|
// UCloud对象存储配置
|
||||||
|
UCloudBucketHost string `json:"uploadUCloudBucketHost"`
|
||||||
|
UCloudBucketName string `json:"uploadUCloudBucketName"`
|
||||||
|
UCloudEndpoint string `json:"uploadUCloudEndpoint"`
|
||||||
|
UCloudFileHost string `json:"uploadUCloudFileHost"`
|
||||||
|
UCloudPath string `json:"uploadUCloudPath"`
|
||||||
|
UCloudPrivateKey string `json:"uploadUCloudPrivateKey"`
|
||||||
|
UCloudPublicKey string `json:"uploadUCloudPublicKey"`
|
||||||
|
// 腾讯云cos配置
|
||||||
|
CosSecretId string `json:"uploadCosSecretId"`
|
||||||
|
CosSecretKey string `json:"uploadCosSecretKey"`
|
||||||
|
CosBucketURL string `json:"uploadCosBucketURL"`
|
||||||
|
CosPath string `json:"uploadCosPath"`
|
||||||
|
// 阿里云oss配置
|
||||||
|
OssSecretId string `json:"uploadOssSecretId"`
|
||||||
|
OssSecretKey string `json:"uploadOssSecretKey"`
|
||||||
|
OssEndpoint string `json:"uploadOssEndpoint"`
|
||||||
|
OssBucketURL string `json:"uploadOssBucketURL"`
|
||||||
|
OssPath string `json:"uploadOssPath"`
|
||||||
|
OssBucket string `json:"uploadOssBucket"`
|
||||||
|
// 七牛云对象存储配置
|
||||||
|
QiNiuAccessKey string `json:"uploadQiNiuAccessKey"`
|
||||||
|
QiNiuSecretKey string `json:"uploadQiNiuSecretKey"`
|
||||||
|
QiNiuDomain string `json:"uploadQiNiuDomain"`
|
||||||
|
QiNiuPath string `json:"uploadQiNiuPath"`
|
||||||
|
QiNiuBucket string `json:"uploadQiNiuBucket"`
|
||||||
|
// minio配置
|
||||||
|
MinioAccessKey string `json:"uploadMinioAccessKey"`
|
||||||
|
MinioSecretKey string `json:"uploadMinioSecretKey"`
|
||||||
|
MinioEndpoint string `json:"uploadMinioEndpoint"`
|
||||||
|
MinioUseSSL int `json:"uploadMinioUseSSL"`
|
||||||
|
MinioPath string `json:"uploadMinioPath"`
|
||||||
|
MinioBucket string `json:"uploadMinioBucket"`
|
||||||
|
MinioDomain string `json:"uploadMinioDomain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GeoConfig 地理配置
|
||||||
|
type GeoConfig struct {
|
||||||
|
GeoAmapWebKey string `json:"geoAmapWebKey"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsTemplate 短信模板
|
||||||
|
type SmsTemplate struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SmsConfig 短信配置
|
||||||
|
type SmsConfig struct {
|
||||||
|
// 基础
|
||||||
|
SmsDrive string `json:"smsDrive"`
|
||||||
|
SmsMinInterval int `json:"smsMinInterval"`
|
||||||
|
SmsMaxIpLimit int `json:"smsMaxIpLimit"`
|
||||||
|
SmsCodeExpire int `json:"smsCodeExpire"`
|
||||||
|
// 阿里云
|
||||||
|
AliYunAccessKeyID string `json:"smsAliYunAccessKeyID"`
|
||||||
|
AliYunAccessKeySecret string `json:"smsAliYunAccessKeySecret"`
|
||||||
|
AliYunSign string `json:"smsAliYunSign"`
|
||||||
|
AliYunTemplate []*SmsTemplate `json:"smsAliYunTemplate"`
|
||||||
|
// 腾讯云
|
||||||
|
TencentSecretId string `json:"smsTencentSecretId"`
|
||||||
|
TencentSecretKey string `json:"smsTencentSecretKey"`
|
||||||
|
TencentEndpoint string `json:"smsTencentEndpoint"`
|
||||||
|
TencentRegion string `json:"smsTencentRegion"`
|
||||||
|
TencentAppId string `json:"smsTencentAppId"`
|
||||||
|
TencentSign string `json:"smsTencentSign"`
|
||||||
|
TencentTemplate []*SmsTemplate `json:"smsTencentTemplate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PayConfig struct {
|
||||||
|
Debug bool `json:"payDebug"`
|
||||||
|
// 支付宝
|
||||||
|
AliPayAppId string `json:"payAliPayAppId"`
|
||||||
|
AliPayPrivateKey string `json:"payAliPayPrivateKey"`
|
||||||
|
AliPayAppCertPublicKey string `json:"payAliPayAppCertPublicKey"`
|
||||||
|
AliPayRootCert string `json:"payAliPayRootCert"`
|
||||||
|
AliPayCertPublicKeyRSA2 string `json:"payAliPayCertPublicKeyRSA2"`
|
||||||
|
// 微信支付
|
||||||
|
WxPayAppId string `json:"payWxPayAppId"`
|
||||||
|
WxPayMchId string `json:"payWxPayMchId"`
|
||||||
|
WxPaySerialNo string `json:"payWxPaySerialNo"`
|
||||||
|
WxPayAPIv3Key string `json:"payWxPayAPIv3Key"`
|
||||||
|
WxPayPrivateKey string `json:"payWxPayPrivateKey"`
|
||||||
|
// QQ支付
|
||||||
|
QQPayAppId string `json:"payQQPayAppId"`
|
||||||
|
QQPayMchId string `json:"payQQPayMchId"`
|
||||||
|
QQPayApiKey string `json:"payQQPayApiKey"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WechatOfficialAccountConfig 微信公众号配置
|
||||||
|
type WechatOfficialAccountConfig struct {
|
||||||
|
OfficialAppID string `json:"officialAccountAppId"` // appid
|
||||||
|
OfficialAppSecret string `json:"officialAccountAppSecret"` // app secret
|
||||||
|
OfficialToken string `json:"officialAccountToken"` // token
|
||||||
|
OfficialEncodingAESKey string `json:"officialAccountEncodingAESKey"` // EncodingAESKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// WechatOpenPlatformConfig 微信开放平台配置
|
||||||
|
type WechatOpenPlatformConfig struct {
|
||||||
|
OpenAppID string `json:"openPlatformAppId"` // appid
|
||||||
|
OpenAppSecret string `json:"openPlatformAppSecret"` // app secret
|
||||||
|
OpenToken string `json:"openPlatformToken"` // token
|
||||||
|
OpenEncodingAESKey string `json:"openPlatformEncodingAESKey"` // EncodingAESKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// WechatConfig 微信配置
|
||||||
|
type WechatConfig struct {
|
||||||
|
*WechatOfficialAccountConfig
|
||||||
|
*WechatOpenPlatformConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginConfig 登录配置
|
||||||
|
type LoginConfig struct {
|
||||||
|
RegisterSwitch int `json:"loginRegisterSwitch"`
|
||||||
|
CaptchaSwitch int `json:"loginCaptchaSwitch"`
|
||||||
|
CaptchaType int `json:"loginCaptchaType"`
|
||||||
|
Avatar string `json:"loginAvatar"`
|
||||||
|
RoleId int64 `json:"loginRoleId"`
|
||||||
|
DeptId int64 `json:"loginDeptId"`
|
||||||
|
PostIds []int64 `json:"loginPostIds"`
|
||||||
|
Protocol string `json:"loginProtocol"`
|
||||||
|
Policy string `json:"loginPolicy"`
|
||||||
|
AutoOpenId int `json:"loginAutoOpenId"`
|
||||||
|
ForceInvite int `json:"loginForceInvite"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
// Package model
|
||||||
|
// @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 model
|
||||||
|
|
||||||
|
// 本地配置.
|
||||||
|
|
||||||
|
// LogConfig 日志配置
|
||||||
|
type LogConfig struct {
|
||||||
|
Switch bool `json:"switch"`
|
||||||
|
Queue bool `json:"queue"`
|
||||||
|
Module []string `json:"module"`
|
||||||
|
SkipCode []string `json:"skipCode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeLogConfig 服务日志配置
|
||||||
|
type ServeLogConfig struct {
|
||||||
|
Switch bool `json:"switch"`
|
||||||
|
Queue bool `json:"queue"`
|
||||||
|
LevelFormat []string `json:"levelFormat"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateAppCrudTemplate curd模板
|
||||||
|
type GenerateAppCrudTemplate struct {
|
||||||
|
Group string `json:"group"`
|
||||||
|
IsAddon bool `json:"isAddon"`
|
||||||
|
MasterPackage string `json:"masterPackage"`
|
||||||
|
TemplatePath string `json:"templatePath"`
|
||||||
|
ApiPath string `json:"apiPath"`
|
||||||
|
InputPath string `json:"inputPath"`
|
||||||
|
ControllerPath string `json:"controllerPath"`
|
||||||
|
LogicPath string `json:"logicPath"`
|
||||||
|
RouterPath string `json:"routerPath"`
|
||||||
|
SqlPath string `json:"sqlPath"`
|
||||||
|
WebApiPath string `json:"webApiPath"`
|
||||||
|
WebViewsPath string `json:"webViewsPath"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateAppQueueTemplate 消息队列模板
|
||||||
|
type GenerateAppQueueTemplate struct {
|
||||||
|
Group string `json:"group"`
|
||||||
|
TemplatePath string `json:"templatePath"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateAppTreeTemplate 关系树列表模板
|
||||||
|
type GenerateAppTreeTemplate struct {
|
||||||
|
Group string `json:"group"`
|
||||||
|
TemplatePath string `json:"templatePath"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateConfig 生成代码配置
|
||||||
|
type GenerateConfig struct {
|
||||||
|
AllowedIPs []string `json:"allowedIPs"`
|
||||||
|
Application struct {
|
||||||
|
Crud struct {
|
||||||
|
Templates []*GenerateAppCrudTemplate `json:"templates"`
|
||||||
|
} `json:"crud"`
|
||||||
|
Queue struct {
|
||||||
|
Templates []*GenerateAppQueueTemplate `json:"templates"`
|
||||||
|
} `json:"queue"`
|
||||||
|
Tree struct {
|
||||||
|
Templates []*GenerateAppTreeTemplate `json:"templates"`
|
||||||
|
} `json:"tree"`
|
||||||
|
} `json:"application"`
|
||||||
|
Delimiters []string `json:"delimiters"`
|
||||||
|
DevPath string `json:"devPath"`
|
||||||
|
DisableTables []string `json:"disableTables"`
|
||||||
|
SelectDbs []string `json:"selectDbs"`
|
||||||
|
Addon *BuildAddonConfig `json:"addon"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildAddonConfig 构建插件模块配置
|
||||||
|
type BuildAddonConfig struct {
|
||||||
|
SrcPath string `json:"srcPath"`
|
||||||
|
WebApiPath string `json:"webApiPath"`
|
||||||
|
WebViewsPath string `json:"webViewsPath"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCPServerConfig tcp服务器配置
|
||||||
|
type TCPServerConfig struct {
|
||||||
|
Address string `json:"address"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCPClientConfig tcp客户端配置
|
||||||
|
type TCPClientConfig struct {
|
||||||
|
Cron *TCPClientConnConfig `json:"cron"`
|
||||||
|
Auth *TCPClientConnConfig `json:"auth"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCPClientConnConfig tcp客户端认证
|
||||||
|
type TCPClientConnConfig struct {
|
||||||
|
Group string `json:"group"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
AppId string `json:"appId"`
|
||||||
|
SecretKey string `json:"secretKey"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCPConfig tcp服务器配置
|
||||||
|
type TCPConfig struct {
|
||||||
|
Server *TCPServerConfig `json:"server"`
|
||||||
|
Client *TCPClientConfig `json:"client"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TokenConfig 登录令牌配置
|
||||||
|
type TokenConfig struct {
|
||||||
|
SecretKey string `json:"secretKey"`
|
||||||
|
Expires int64 `json:"expires"`
|
||||||
|
AutoRefresh bool `json:"autoRefresh"`
|
||||||
|
RefreshInterval int64 `json:"refreshInterval"`
|
||||||
|
MaxRefreshTimes int64 `json:"maxRefreshTimes"`
|
||||||
|
MultiLogin bool `json:"multiLogin"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
// Package model
|
||||||
|
// @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 model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Context 请求上下文结构
|
||||||
|
type Context struct {
|
||||||
|
Module string // 应用模块 admin|api|home|websocket
|
||||||
|
AddonName string // 插件名称 如果不是插件模块请求,可能为空
|
||||||
|
User *Identity // 上下文用户信息
|
||||||
|
Response *Response // 请求响应
|
||||||
|
Data g.Map // 自定kv变量 业务模块根据需要设置,不固定
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identity 通用身份模型
|
||||||
|
type Identity struct {
|
||||||
|
Id int64 `json:"id" description:"用户ID"`
|
||||||
|
Pid int64 `json:"pid" description:"上级ID"`
|
||||||
|
DeptId int64 `json:"deptId" description:"部门ID"`
|
||||||
|
DeptType string `json:"deptType" description:"部门类型"`
|
||||||
|
RoleId int64 `json:"roleId" description:"角色ID"`
|
||||||
|
RoleKey string `json:"roleKey" description:"角色唯一标识符"`
|
||||||
|
Username string `json:"username" description:"用户名"`
|
||||||
|
RealName string `json:"realName" description:"姓名"`
|
||||||
|
Avatar string `json:"avatar" description:"头像"`
|
||||||
|
Email string `json:"email" description:"邮箱"`
|
||||||
|
Mobile string `json:"mobile" description:"手机号码"`
|
||||||
|
App string `json:"app" description:"登录应用"`
|
||||||
|
LoginAt *gtime.Time `json:"loginAt" description:"登录时间"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// Package model
|
||||||
|
// @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 model
|
||||||
|
|
||||||
|
// Option 字典数据选项
|
||||||
|
type Option struct {
|
||||||
|
Key interface{} `json:"key"`
|
||||||
|
Label string `json:"label" description:"字典标签"`
|
||||||
|
Value interface{} `json:"value" description:"字典键值"`
|
||||||
|
ValueType string `json:"valueType" description:"键值数据类型"`
|
||||||
|
Type string `json:"type" description:"字典类型"`
|
||||||
|
ListClass string `json:"listClass" description:"表格回显样式"`
|
||||||
|
Extra interface{} `json:"extra" description:"额外数据配置"`
|
||||||
|
}
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/encoding/gjson"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddonHgexampleTable is the golang structure of table hg_addon_hgexample_table for DAO operations like Where/Data.
|
||||||
|
type AddonHgexampleTable struct {
|
||||||
|
g.Meta `orm:"table:hg_addon_hgexample_table, do:true"`
|
||||||
|
Id any // ID
|
||||||
|
Pid any // 上级ID
|
||||||
|
Level any // 树等级
|
||||||
|
Tree any // 关系树
|
||||||
|
CategoryId any // 分类ID
|
||||||
|
Flag *gjson.Json // 标签
|
||||||
|
Title any // 标题
|
||||||
|
Description any // 描述
|
||||||
|
Content any // 内容
|
||||||
|
Image any // 单图
|
||||||
|
Images *gjson.Json // 多图
|
||||||
|
Attachfile any // 附件
|
||||||
|
Attachfiles *gjson.Json // 多附件
|
||||||
|
Map *gjson.Json // 动态键值对
|
||||||
|
Star any // 推荐星
|
||||||
|
Price any // 价格
|
||||||
|
Views any // 浏览次数
|
||||||
|
ActivityAt *gtime.Time // 活动时间
|
||||||
|
StartAt *gtime.Time // 开启时间
|
||||||
|
EndAt *gtime.Time // 结束时间
|
||||||
|
Switch any // 开关
|
||||||
|
Sort any // 排序
|
||||||
|
Avatar any // 头像
|
||||||
|
Sex any // 性别
|
||||||
|
Qq any // qq
|
||||||
|
Email any // 邮箱
|
||||||
|
Mobile any // 手机号码
|
||||||
|
Hobby *gjson.Json // 爱好
|
||||||
|
Channel any // 渠道
|
||||||
|
CityId any // 所在城市
|
||||||
|
Remark any // 备注
|
||||||
|
Status any // 状态
|
||||||
|
CreatedBy any // 创建者
|
||||||
|
UpdatedBy any // 更新者
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 修改时间
|
||||||
|
DeletedAt *gtime.Time // 删除时间
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddonHgexampleTenantOrder is the golang structure of table hg_addon_hgexample_tenant_order for DAO operations like Where/Data.
|
||||||
|
type AddonHgexampleTenantOrder struct {
|
||||||
|
g.Meta `orm:"table:hg_addon_hgexample_tenant_order, do:true"`
|
||||||
|
Id any // 主键
|
||||||
|
TenantId any // 租户ID
|
||||||
|
MerchantId any // 商户ID
|
||||||
|
UserId any // 用户ID
|
||||||
|
ProductName any // 购买产品
|
||||||
|
OrderSn any // 订单号
|
||||||
|
Money any // 充值金额
|
||||||
|
Remark any // 备注
|
||||||
|
Status any // 订单状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 修改时间
|
||||||
|
}
|
||||||
Executable
+25
@@ -0,0 +1,25 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdminCash is the golang structure of table hg_admin_cash for DAO operations like Where/Data.
|
||||||
|
type AdminCash struct {
|
||||||
|
g.Meta `orm:"table:hg_admin_cash, do:true"`
|
||||||
|
Id any // ID
|
||||||
|
MemberId any // 管理员ID
|
||||||
|
Money any // 提现金额
|
||||||
|
Fee any // 手续费
|
||||||
|
LastMoney any // 最终到账金额
|
||||||
|
Ip any // 申请人IP
|
||||||
|
Status any // 状态码
|
||||||
|
Msg any // 处理结果
|
||||||
|
HandleAt *gtime.Time // 处理时间
|
||||||
|
CreatedAt *gtime.Time // 申请时间
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdminCreditsLog is the golang structure of table hg_admin_credits_log for DAO operations like Where/Data.
|
||||||
|
type AdminCreditsLog struct {
|
||||||
|
g.Meta `orm:"table:hg_admin_credits_log, do:true"`
|
||||||
|
Id any // 变动ID
|
||||||
|
MemberId any // 管理员ID
|
||||||
|
AppId any // 应用id
|
||||||
|
AddonsName any // 插件名称
|
||||||
|
CreditType any // 变动类型
|
||||||
|
CreditGroup any // 变动组别
|
||||||
|
BeforeNum any // 变动前
|
||||||
|
Num any // 变动数据
|
||||||
|
AfterNum any // 变动后
|
||||||
|
Remark any // 备注
|
||||||
|
Ip any // 操作人IP
|
||||||
|
MapId any // 关联ID
|
||||||
|
Status any // 状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 修改时间
|
||||||
|
}
|
||||||
Executable
+29
@@ -0,0 +1,29 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdminDept is the golang structure of table hg_admin_dept for DAO operations like Where/Data.
|
||||||
|
type AdminDept struct {
|
||||||
|
g.Meta `orm:"table:hg_admin_dept, do:true"`
|
||||||
|
Id any // 部门ID
|
||||||
|
Pid any // 父部门ID
|
||||||
|
Name any // 部门名称
|
||||||
|
Code any // 部门编码
|
||||||
|
Type any // 部门类型
|
||||||
|
Leader any // 负责人
|
||||||
|
Phone any // 联系电话
|
||||||
|
Email any // 邮箱
|
||||||
|
Level any // 关系树等级
|
||||||
|
Tree any // 关系树
|
||||||
|
Sort any // 排序
|
||||||
|
Status any // 部门状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 更新时间
|
||||||
|
}
|
||||||
Executable
+44
@@ -0,0 +1,44 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/encoding/gjson"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdminMember is the golang structure of table hg_admin_member for DAO operations like Where/Data.
|
||||||
|
type AdminMember struct {
|
||||||
|
g.Meta `orm:"table:hg_admin_member, do:true"`
|
||||||
|
Id any // 管理员ID
|
||||||
|
DeptId any // 部门ID
|
||||||
|
RoleId any // 角色ID
|
||||||
|
RealName any // 真实姓名
|
||||||
|
Username any // 帐号
|
||||||
|
PasswordHash any // 密码
|
||||||
|
Salt any // 密码盐
|
||||||
|
PasswordResetToken any // 密码重置令牌
|
||||||
|
Integral any // 积分
|
||||||
|
Balance any // 余额
|
||||||
|
Avatar any // 头像
|
||||||
|
Sex any // 性别
|
||||||
|
Qq any // qq
|
||||||
|
Email any // 邮箱
|
||||||
|
Mobile any // 手机号码
|
||||||
|
Birthday *gtime.Time // 生日
|
||||||
|
CityId any // 城市编码
|
||||||
|
Address any // 联系地址
|
||||||
|
Pid any // 上级管理员ID
|
||||||
|
Level any // 关系树等级
|
||||||
|
Tree any // 关系树
|
||||||
|
InviteCode any // 邀请码
|
||||||
|
Cash *gjson.Json // 提现配置
|
||||||
|
LastActiveAt *gtime.Time // 最后活跃时间
|
||||||
|
Remark any // 备注
|
||||||
|
Status any // 状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 修改时间
|
||||||
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdminMemberPost is the golang structure of table hg_admin_member_post for DAO operations like Where/Data.
|
||||||
|
type AdminMemberPost struct {
|
||||||
|
g.Meta `orm:"table:hg_admin_member_post, do:true"`
|
||||||
|
MemberId any // 管理员ID
|
||||||
|
PostId any // 岗位ID
|
||||||
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdminMemberRole is the golang structure of table hg_admin_member_role for DAO operations like Where/Data.
|
||||||
|
type AdminMemberRole struct {
|
||||||
|
g.Meta `orm:"table:hg_admin_member_role, do:true"`
|
||||||
|
MemberId any // 管理员ID
|
||||||
|
RoleId any // 角色ID
|
||||||
|
}
|
||||||
Executable
+41
@@ -0,0 +1,41 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdminMenu is the golang structure of table hg_admin_menu for DAO operations like Where/Data.
|
||||||
|
type AdminMenu struct {
|
||||||
|
g.Meta `orm:"table:hg_admin_menu, do:true"`
|
||||||
|
Id any // 菜单ID
|
||||||
|
Pid any // 父菜单ID
|
||||||
|
Level any // 关系树等级
|
||||||
|
Tree any // 关系树
|
||||||
|
Title any // 菜单名称
|
||||||
|
Name any // 名称编码
|
||||||
|
Path any // 路由地址
|
||||||
|
Icon any // 菜单图标
|
||||||
|
Type any // 菜单类型(1目录 2菜单 3按钮)
|
||||||
|
Redirect any // 重定向地址
|
||||||
|
Permissions any // 菜单包含权限集合
|
||||||
|
PermissionName any // 权限名称
|
||||||
|
Component any // 组件路径
|
||||||
|
AlwaysShow any // 取消自动计算根路由模式
|
||||||
|
ActiveMenu any // 高亮菜单编码
|
||||||
|
IsRoot any // 是否跟路由
|
||||||
|
IsFrame any // 是否内嵌
|
||||||
|
FrameSrc any // 内联外部地址
|
||||||
|
KeepAlive any // 缓存该路由
|
||||||
|
Hidden any // 是否隐藏
|
||||||
|
Affix any // 是否固定
|
||||||
|
Sort any // 排序
|
||||||
|
Remark any // 备注
|
||||||
|
Status any // 菜单状态
|
||||||
|
UpdatedAt *gtime.Time // 更新时间
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
}
|
||||||
Executable
+30
@@ -0,0 +1,30 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/encoding/gjson"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdminNotice is the golang structure of table hg_admin_notice for DAO operations like Where/Data.
|
||||||
|
type AdminNotice struct {
|
||||||
|
g.Meta `orm:"table:hg_admin_notice, do:true"`
|
||||||
|
Id any // 公告ID
|
||||||
|
Title any // 公告标题
|
||||||
|
Type any // 公告类型
|
||||||
|
Tag any // 标签
|
||||||
|
Content any // 公告内容
|
||||||
|
Receiver *gjson.Json // 接收者
|
||||||
|
Remark any // 备注
|
||||||
|
Sort any // 排序
|
||||||
|
Status any // 公告状态
|
||||||
|
CreatedBy any // 发送人
|
||||||
|
UpdatedBy any // 修改人
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 更新时间
|
||||||
|
DeletedAt *gtime.Time // 删除时间
|
||||||
|
}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdminNoticeRead is the golang structure of table hg_admin_notice_read for DAO operations like Where/Data.
|
||||||
|
type AdminNoticeRead struct {
|
||||||
|
g.Meta `orm:"table:hg_admin_notice_read, do:true"`
|
||||||
|
Id any // 记录ID
|
||||||
|
NoticeId any // 公告ID
|
||||||
|
MemberId any // 会员ID
|
||||||
|
Clicks any // 已读次数
|
||||||
|
UpdatedAt *gtime.Time // 更新时间
|
||||||
|
CreatedAt *gtime.Time // 阅读时间
|
||||||
|
}
|
||||||
Executable
+30
@@ -0,0 +1,30 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdminOauth is the golang structure of table hg_admin_oauth for DAO operations like Where/Data.
|
||||||
|
type AdminOauth struct {
|
||||||
|
g.Meta `orm:"table:hg_admin_oauth, do:true"`
|
||||||
|
Id any // 主键
|
||||||
|
MemberId any // 用户ID
|
||||||
|
Unionid any // 唯一ID
|
||||||
|
OauthClient any // 授权组别
|
||||||
|
OauthOpenid any // 授权开放ID
|
||||||
|
Sex any // 性别
|
||||||
|
Nickname any // 昵称
|
||||||
|
HeadPortrait any // 头像
|
||||||
|
Birthday *gtime.Time // 生日
|
||||||
|
Country any // 国家
|
||||||
|
Province any // 省
|
||||||
|
City any // 市
|
||||||
|
Status any // 状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 修改时间
|
||||||
|
}
|
||||||
Executable
+27
@@ -0,0 +1,27 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdminOrder is the golang structure of table hg_admin_order for DAO operations like Where/Data.
|
||||||
|
type AdminOrder struct {
|
||||||
|
g.Meta `orm:"table:hg_admin_order, do:true"`
|
||||||
|
Id any // 主键
|
||||||
|
MemberId any // 管理员id
|
||||||
|
OrderType any // 订单类型
|
||||||
|
ProductId any // 产品id
|
||||||
|
OrderSn any // 关联订单号
|
||||||
|
Money any // 充值金额
|
||||||
|
Remark any // 备注
|
||||||
|
RefundReason any // 退款原因
|
||||||
|
RejectRefundReason any // 拒绝退款原因
|
||||||
|
Status any // 状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 修改时间
|
||||||
|
}
|
||||||
Executable
+23
@@ -0,0 +1,23 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdminPost is the golang structure of table hg_admin_post for DAO operations like Where/Data.
|
||||||
|
type AdminPost struct {
|
||||||
|
g.Meta `orm:"table:hg_admin_post, do:true"`
|
||||||
|
Id any // 岗位ID
|
||||||
|
Code any // 岗位编码
|
||||||
|
Name any // 岗位名称
|
||||||
|
Remark any // 备注
|
||||||
|
Sort any // 排序
|
||||||
|
Status any // 状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 更新时间
|
||||||
|
}
|
||||||
Executable
+29
@@ -0,0 +1,29 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/encoding/gjson"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdminRole is the golang structure of table hg_admin_role for DAO operations like Where/Data.
|
||||||
|
type AdminRole struct {
|
||||||
|
g.Meta `orm:"table:hg_admin_role, do:true"`
|
||||||
|
Id any // 角色ID
|
||||||
|
Name any // 角色名称
|
||||||
|
Key any // 角色权限字符串
|
||||||
|
DataScope any // 数据范围
|
||||||
|
CustomDept *gjson.Json // 自定义部门权限
|
||||||
|
Pid any // 上级角色ID
|
||||||
|
Level any // 关系树等级
|
||||||
|
Tree any // 关系树
|
||||||
|
Remark any // 备注
|
||||||
|
Sort any // 排序
|
||||||
|
Status any // 角色状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 更新时间
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdminRoleCasbin is the golang structure of table hg_admin_role_casbin for DAO operations like Where/Data.
|
||||||
|
type AdminRoleCasbin struct {
|
||||||
|
g.Meta `orm:"table:hg_admin_role_casbin, do:true"`
|
||||||
|
Id any //
|
||||||
|
PType any //
|
||||||
|
V0 any //
|
||||||
|
V1 any //
|
||||||
|
V2 any //
|
||||||
|
V3 any //
|
||||||
|
V4 any //
|
||||||
|
V5 any //
|
||||||
|
}
|
||||||
Executable
+16
@@ -0,0 +1,16 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdminRoleMenu is the golang structure of table hg_admin_role_menu for DAO operations like Where/Data.
|
||||||
|
type AdminRoleMenu struct {
|
||||||
|
g.Meta `orm:"table:hg_admin_role_menu, do:true"`
|
||||||
|
RoleId any // 角色ID
|
||||||
|
MenuId any // 菜单ID
|
||||||
|
}
|
||||||
Executable
+46
@@ -0,0 +1,46 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/encoding/gjson"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PayLog is the golang structure of table hg_pay_log for DAO operations like Where/Data.
|
||||||
|
type PayLog struct {
|
||||||
|
g.Meta `orm:"table:hg_pay_log, do:true"`
|
||||||
|
Id any // 主键
|
||||||
|
MemberId any // 会员ID
|
||||||
|
AppId any // 应用ID
|
||||||
|
AddonsName any // 插件名称
|
||||||
|
OrderSn any // 关联订单号
|
||||||
|
OrderGroup any // 组别[默认统一支付类型]
|
||||||
|
Openid any // openid
|
||||||
|
MchId any // 商户支付账户
|
||||||
|
Subject any // 订单标题
|
||||||
|
Detail *gjson.Json // 支付商品详情
|
||||||
|
AuthCode any // 刷卡码
|
||||||
|
OutTradeNo any // 商户订单号
|
||||||
|
TransactionId any // 交易号
|
||||||
|
PayType any // 支付类型
|
||||||
|
PayAmount any // 支付金额
|
||||||
|
ActualAmount any // 实付金额
|
||||||
|
PayStatus any // 支付状态
|
||||||
|
PayAt *gtime.Time // 支付时间
|
||||||
|
TradeType any // 交易类型
|
||||||
|
RefundSn any // 退款单号
|
||||||
|
IsRefund any // 是否退款
|
||||||
|
Custom any // 自定义参数
|
||||||
|
CreateIp any // 创建者IP
|
||||||
|
PayIp any // 支付者IP
|
||||||
|
NotifyUrl any // 支付通知回调地址
|
||||||
|
ReturnUrl any // 买家付款成功跳转地址
|
||||||
|
TraceIds *gjson.Json // 链路ID集合
|
||||||
|
Status any // 状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 修改时间
|
||||||
|
}
|
||||||
Executable
+28
@@ -0,0 +1,28 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PayRefund is the golang structure of table hg_pay_refund for DAO operations like Where/Data.
|
||||||
|
type PayRefund struct {
|
||||||
|
g.Meta `orm:"table:hg_pay_refund, do:true"`
|
||||||
|
Id any // 主键ID
|
||||||
|
MemberId any // 会员ID
|
||||||
|
AppId any // 应用ID
|
||||||
|
OrderSn any // 业务订单号
|
||||||
|
RefundTradeNo any // 退款交易号
|
||||||
|
RefundMoney any // 退款金额
|
||||||
|
RefundWay any // 退款方式
|
||||||
|
Ip any // 申请者IP
|
||||||
|
Reason any // 申请退款原因
|
||||||
|
Remark any // 退款备注
|
||||||
|
Status any // 退款状态
|
||||||
|
CreatedAt *gtime.Time // 申请时间
|
||||||
|
UpdatedAt *gtime.Time // 更新时间
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SysAddonsConfig is the golang structure of table hg_sys_addons_config for DAO operations like Where/Data.
|
||||||
|
type SysAddonsConfig struct {
|
||||||
|
g.Meta `orm:"table:hg_sys_addons_config, do:true"`
|
||||||
|
Id any // 配置ID
|
||||||
|
AddonName any // 插件名称
|
||||||
|
Group any // 分组
|
||||||
|
Name any // 参数名称
|
||||||
|
Type any // 键值类型:string,int,uint,bool,TIMESTAMP,date
|
||||||
|
Key any // 参数键名
|
||||||
|
Value any // 参数键值
|
||||||
|
DefaultValue any // 默认值
|
||||||
|
Sort any // 排序
|
||||||
|
Tip any // 变量描述
|
||||||
|
IsDefault any // 是否为系统默认
|
||||||
|
Status any // 状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 更新时间
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SysAddonsInstall is the golang structure of table hg_sys_addons_install for DAO operations like Where/Data.
|
||||||
|
type SysAddonsInstall struct {
|
||||||
|
g.Meta `orm:"table:hg_sys_addons_install, do:true"`
|
||||||
|
Id interface{} // 主键
|
||||||
|
Name interface{} // 插件名称
|
||||||
|
Version interface{} // 版本号
|
||||||
|
Status interface{} // 状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 更新时间
|
||||||
|
}
|
||||||
Executable
+32
@@ -0,0 +1,32 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SysAttachment is the golang structure of table hg_sys_attachment for DAO operations like Where/Data.
|
||||||
|
type SysAttachment struct {
|
||||||
|
g.Meta `orm:"table:hg_sys_attachment, do:true"`
|
||||||
|
Id any // 文件ID
|
||||||
|
AppId any // 应用ID
|
||||||
|
MemberId any // 管理员ID
|
||||||
|
CateId any // 上传分类
|
||||||
|
Drive any // 上传驱动
|
||||||
|
Name any // 文件原始名
|
||||||
|
Kind any // 上传类型
|
||||||
|
MimeType any // 扩展类型
|
||||||
|
NaiveType any // NaiveUI类型
|
||||||
|
Path any // 本地路径
|
||||||
|
FileUrl any // url
|
||||||
|
Size any // 文件大小
|
||||||
|
Ext any // 扩展名
|
||||||
|
Md5 any // md5校验码
|
||||||
|
Status any // 状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 修改时间
|
||||||
|
}
|
||||||
Executable
+21
@@ -0,0 +1,21 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SysBlacklist is the golang structure of table hg_sys_blacklist for DAO operations like Where/Data.
|
||||||
|
type SysBlacklist struct {
|
||||||
|
g.Meta `orm:"table:hg_sys_blacklist, do:true"`
|
||||||
|
Id any // 黑名单ID
|
||||||
|
Ip any // IP地址
|
||||||
|
Remark any // 备注
|
||||||
|
Status any // 状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 更新时间
|
||||||
|
}
|
||||||
Executable
+28
@@ -0,0 +1,28 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SysConfig is the golang structure of table hg_sys_config for DAO operations like Where/Data.
|
||||||
|
type SysConfig struct {
|
||||||
|
g.Meta `orm:"table:hg_sys_config, do:true"`
|
||||||
|
Id any // 配置ID
|
||||||
|
Group any // 配置分组
|
||||||
|
Name any // 参数名称
|
||||||
|
Type any // 键值类型:string,int,uint,bool,TIMESTAMP,date
|
||||||
|
Key any // 参数键名
|
||||||
|
Value any // 参数键值
|
||||||
|
DefaultValue any // 默认值
|
||||||
|
Sort any // 排序
|
||||||
|
Tip any // 变量描述
|
||||||
|
IsDefault any // 是否为系统默认
|
||||||
|
Status any // 状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 更新时间
|
||||||
|
}
|
||||||
Executable
+28
@@ -0,0 +1,28 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SysCron is the golang structure of table hg_sys_cron for DAO operations like Where/Data.
|
||||||
|
type SysCron struct {
|
||||||
|
g.Meta `orm:"table:hg_sys_cron, do:true"`
|
||||||
|
Id any // 任务ID
|
||||||
|
GroupId any // 分组ID
|
||||||
|
Title any // 任务标题
|
||||||
|
Name any // 任务方法
|
||||||
|
Params any // 函数参数
|
||||||
|
Pattern any // 表达式
|
||||||
|
Policy any // 策略
|
||||||
|
Count any // 执行次数
|
||||||
|
Sort any // 排序
|
||||||
|
Remark any // 备注
|
||||||
|
Status any // 任务状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 更新时间
|
||||||
|
}
|
||||||
Executable
+24
@@ -0,0 +1,24 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SysCronGroup is the golang structure of table hg_sys_cron_group for DAO operations like Where/Data.
|
||||||
|
type SysCronGroup struct {
|
||||||
|
g.Meta `orm:"table:hg_sys_cron_group, do:true"`
|
||||||
|
Id any // 任务分组ID
|
||||||
|
Pid any // 父类任务分组ID
|
||||||
|
Name any // 分组名称
|
||||||
|
IsDefault any // 是否默认
|
||||||
|
Sort any // 排序
|
||||||
|
Remark any // 备注
|
||||||
|
Status any // 分组状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 更新时间
|
||||||
|
}
|
||||||
Executable
+27
@@ -0,0 +1,27 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SysDictData is the golang structure of table hg_sys_dict_data for DAO operations like Where/Data.
|
||||||
|
type SysDictData struct {
|
||||||
|
g.Meta `orm:"table:hg_sys_dict_data, do:true"`
|
||||||
|
Id any // 字典数据ID
|
||||||
|
Label any // 字典标签
|
||||||
|
Value any // 字典键值
|
||||||
|
ValueType any // 键值数据类型:string,int,uint,bool,TIMESTAMP,date
|
||||||
|
Type any // 字典类型
|
||||||
|
ListClass any // 表格回显样式
|
||||||
|
IsDefault any // 是否为系统默认
|
||||||
|
Sort any // 字典排序
|
||||||
|
Remark any // 备注
|
||||||
|
Status any // 状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 更新时间
|
||||||
|
}
|
||||||
Executable
+24
@@ -0,0 +1,24 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SysDictType is the golang structure of table hg_sys_dict_type for DAO operations like Where/Data.
|
||||||
|
type SysDictType struct {
|
||||||
|
g.Meta `orm:"table:hg_sys_dict_type, do:true"`
|
||||||
|
Id any // 字典类型ID
|
||||||
|
Pid any // 父类字典类型ID
|
||||||
|
Name any // 字典类型名称
|
||||||
|
Type any // 字典类型
|
||||||
|
Sort any // 排序
|
||||||
|
Remark any // 备注
|
||||||
|
Status any // 字典类型状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 更新时间
|
||||||
|
}
|
||||||
Executable
+25
@@ -0,0 +1,25 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SysEmsLog is the golang structure of table hg_sys_ems_log for DAO operations like Where/Data.
|
||||||
|
type SysEmsLog struct {
|
||||||
|
g.Meta `orm:"table:hg_sys_ems_log, do:true"`
|
||||||
|
Id any // 主键
|
||||||
|
Event any // 事件
|
||||||
|
Email any // 邮箱地址,多个用;隔开
|
||||||
|
Code any // 验证码
|
||||||
|
Times any // 验证次数
|
||||||
|
Content any // 邮件内容
|
||||||
|
Ip any // ip地址
|
||||||
|
Status any // 状态(1未验证,2已验证)
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 更新时间
|
||||||
|
}
|
||||||
Executable
+30
@@ -0,0 +1,30 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/encoding/gjson"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SysGenCodes is the golang structure of table hg_sys_gen_codes for DAO operations like Where/Data.
|
||||||
|
type SysGenCodes struct {
|
||||||
|
g.Meta `orm:"table:hg_sys_gen_codes, do:true"`
|
||||||
|
Id any // 生成ID
|
||||||
|
GenType any // 生成类型
|
||||||
|
GenTemplate any // 生成模板
|
||||||
|
VarName any // 实体命名
|
||||||
|
Options *gjson.Json // 配置选项
|
||||||
|
DbName any // 数据库名称
|
||||||
|
TableName any // 主表名称
|
||||||
|
TableComment any // 主表注释
|
||||||
|
DaoName any // 主表dao模型
|
||||||
|
MasterColumns *gjson.Json // 主表字段
|
||||||
|
AddonName any // 插件名称
|
||||||
|
Status any // 生成状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 更新时间
|
||||||
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SysGenCurdDemo is the golang structure of table hg_sys_gen_curd_demo for DAO operations like Where/Data.
|
||||||
|
type SysGenCurdDemo struct {
|
||||||
|
g.Meta `orm:"table:hg_sys_gen_curd_demo, do:true"`
|
||||||
|
Id any // ID
|
||||||
|
CategoryId any // 分类ID
|
||||||
|
Title any // 标题
|
||||||
|
Description any // 描述
|
||||||
|
Content any // 内容
|
||||||
|
Image any // 单图
|
||||||
|
Attachfile any // 附件
|
||||||
|
CityId any // 所在城市
|
||||||
|
Switch any // 显示开关
|
||||||
|
Sort any // 排序
|
||||||
|
Status any // 状态
|
||||||
|
CreatedBy any // 创建者
|
||||||
|
UpdatedBy any // 更新者
|
||||||
|
DeletedBy any // 删除者
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 修改时间
|
||||||
|
DeletedAt *gtime.Time // 删除时间
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SysGenTreeDemo is the golang structure of table hg_sys_gen_tree_demo for DAO operations like Where/Data.
|
||||||
|
type SysGenTreeDemo struct {
|
||||||
|
g.Meta `orm:"table:hg_sys_gen_tree_demo, do:true"`
|
||||||
|
Id any // ID
|
||||||
|
Pid any // 上级ID
|
||||||
|
Level any // 关系树级别
|
||||||
|
Tree any // 关系树
|
||||||
|
CategoryId any // 分类ID
|
||||||
|
Title any // 标题
|
||||||
|
Description any // 描述
|
||||||
|
Sort any // 排序
|
||||||
|
Status any // 状态
|
||||||
|
CreatedBy any // 创建者
|
||||||
|
UpdatedBy any // 更新者
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 修改时间
|
||||||
|
DeletedAt *gtime.Time // 删除时间
|
||||||
|
}
|
||||||
Executable
+39
@@ -0,0 +1,39 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/encoding/gjson"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SysLog is the golang structure of table hg_sys_log for DAO operations like Where/Data.
|
||||||
|
type SysLog struct {
|
||||||
|
g.Meta `orm:"table:hg_sys_log, do:true"`
|
||||||
|
Id any // 日志ID
|
||||||
|
ReqId any // 对外ID
|
||||||
|
AppId any // 应用ID
|
||||||
|
MerchantId any // 商户ID
|
||||||
|
MemberId any // 用户ID
|
||||||
|
Method any // 提交类型
|
||||||
|
Module any // 访问模块
|
||||||
|
Url any // 提交url
|
||||||
|
GetData *gjson.Json // get数据
|
||||||
|
PostData *gjson.Json // post数据
|
||||||
|
HeaderData *gjson.Json // header数据
|
||||||
|
Ip any // IP地址
|
||||||
|
ProvinceId any // 省编码
|
||||||
|
CityId any // 市编码
|
||||||
|
ErrorCode any // 报错code
|
||||||
|
ErrorMsg any // 对外错误提示
|
||||||
|
ErrorData *gjson.Json // 报错日志
|
||||||
|
UserAgent any // UA信息
|
||||||
|
TakeUpTime any // 请求耗时
|
||||||
|
Timestamp any // 响应时间
|
||||||
|
Status any // 状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 修改时间
|
||||||
|
}
|
||||||
Executable
+30
@@ -0,0 +1,30 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/encoding/gjson"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SysLoginLog is the golang structure of table hg_sys_login_log for DAO operations like Where/Data.
|
||||||
|
type SysLoginLog struct {
|
||||||
|
g.Meta `orm:"table:hg_sys_login_log, do:true"`
|
||||||
|
Id any // 日志ID
|
||||||
|
ReqId any // 请求ID
|
||||||
|
MemberId any // 用户ID
|
||||||
|
Username any // 用户名
|
||||||
|
Response *gjson.Json // 响应数据
|
||||||
|
LoginAt *gtime.Time // 登录时间
|
||||||
|
LoginIp any // 登录IP
|
||||||
|
ProvinceId any // 省编码
|
||||||
|
CityId any // 市编码
|
||||||
|
UserAgent any // UA信息
|
||||||
|
ErrMsg any // 错误提示
|
||||||
|
Status any // 状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 修改时间
|
||||||
|
}
|
||||||
Executable
+27
@@ -0,0 +1,27 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SysProvinces is the golang structure of table hg_sys_provinces for DAO operations like Where/Data.
|
||||||
|
type SysProvinces struct {
|
||||||
|
g.Meta `orm:"table:hg_sys_provinces, do:true"`
|
||||||
|
Id any // 省市区ID
|
||||||
|
Title any // 栏目名称
|
||||||
|
Pinyin any // 拼音
|
||||||
|
Lng any // 经度
|
||||||
|
Lat any // 纬度
|
||||||
|
Pid any // 父栏目
|
||||||
|
Level any // 关系树等级
|
||||||
|
Tree any // 关系
|
||||||
|
Sort any // 排序
|
||||||
|
Status any // 状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 更新时间
|
||||||
|
}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/encoding/gjson"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SysServeLicense is the golang structure of table hg_sys_serve_license for DAO operations like Where/Data.
|
||||||
|
type SysServeLicense struct {
|
||||||
|
g.Meta `orm:"table:hg_sys_serve_license, do:true"`
|
||||||
|
Id any // 许可ID
|
||||||
|
Group any // 分组
|
||||||
|
Name any // 许可名称
|
||||||
|
Appid any // 应用ID
|
||||||
|
SecretKey any // 应用秘钥
|
||||||
|
RemoteAddr any // 最后连接地址
|
||||||
|
OnlineLimit any // 在线限制
|
||||||
|
LoginTimes any // 登录次数
|
||||||
|
LastLoginAt *gtime.Time // 最后登录时间
|
||||||
|
LastActiveAt *gtime.Time // 最后心跳
|
||||||
|
Routes *gjson.Json // 路由表,空使用默认分组路由
|
||||||
|
AllowedIps any // IP白名单
|
||||||
|
EndAt *gtime.Time // 授权有效期
|
||||||
|
Remark any // 备注
|
||||||
|
Status any // 状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 修改时间
|
||||||
|
}
|
||||||
Executable
+26
@@ -0,0 +1,26 @@
|
|||||||
|
// =================================================================================
|
||||||
|
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||||
|
// =================================================================================
|
||||||
|
|
||||||
|
package do
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gogf/gf/v2/encoding/gjson"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SysServeLog is the golang structure of table hg_sys_serve_log for DAO operations like Where/Data.
|
||||||
|
type SysServeLog struct {
|
||||||
|
g.Meta `orm:"table:hg_sys_serve_log, do:true"`
|
||||||
|
Id any // 日志ID
|
||||||
|
TraceId any // 链路ID
|
||||||
|
LevelFormat any // 日志级别
|
||||||
|
Content any // 日志内容
|
||||||
|
Stack *gjson.Json // 打印堆栈
|
||||||
|
Line any // 调用行
|
||||||
|
TriggerNs any // 触发时间(ns)
|
||||||
|
Status any // 状态
|
||||||
|
CreatedAt *gtime.Time // 创建时间
|
||||||
|
UpdatedAt *gtime.Time // 修改时间
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user