This commit is contained in:
孟帅
2022-11-24 23:37:34 +08:00
parent 4ffe54b6ac
commit 29bda0dcdd
1487 changed files with 97869 additions and 96539 deletions

View File

View File

@@ -0,0 +1,349 @@
// Package admin
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 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"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/adminin"
"hotgo/internal/service"
"hotgo/utility/convert"
"hotgo/utility/tree"
)
type sAdminDept struct{}
func NewAdminDept() *sAdminDept {
return &sAdminDept{}
}
func init() {
service.RegisterAdminDept(NewAdminDept())
}
// NameUnique 菜单名称是否唯一
func (s *sAdminDept) NameUnique(ctx context.Context, in adminin.DeptNameUniqueInp) (*adminin.DeptNameUniqueModel, error) {
var res adminin.DeptNameUniqueModel
isUnique, err := dao.AdminDept.IsUniqueName(ctx, in.Id, in.Name)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
res.IsUnique = isUnique
return &res, nil
}
// Delete 删除
func (s *sAdminDept) Delete(ctx context.Context, in adminin.DeptDeleteInp) error {
exist, err := dao.AdminRoleDept.Ctx(ctx).Where("dept_id", in.Id).One()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
if !exist.IsEmpty() {
return gerror.New("请先解除该部门下所有已关联用户关联关系!")
}
_, err = dao.AdminDept.Ctx(ctx).Where("id", in.Id).Delete()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// Edit 修改/新增
func (s *sAdminDept) Edit(ctx context.Context, in adminin.DeptEditInp) (err error) {
if in.Name == "" {
err = gerror.New("名称不能为空")
return err
}
uniqueName, err := dao.AdminDept.IsUniqueName(ctx, in.Id, in.Name)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
if !uniqueName {
err = gerror.New("名称已存在")
return err
}
// 修改
in.UpdatedAt = gtime.Now()
if in.Id > 0 {
_, err = dao.AdminDept.Ctx(ctx).Where("id", in.Id).Data(in).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// 新增
in.CreatedAt = gtime.Now()
_, err = dao.AdminDept.Ctx(ctx).Data(in).Insert()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// Status 更新部门状态
func (s *sAdminDept) Status(ctx context.Context, in adminin.DeptStatusInp) (err error) {
if in.Id <= 0 {
err = gerror.New("ID不能为空")
return err
}
if in.Status <= 0 {
err = gerror.New("状态不能为空")
return err
}
if !convert.InSliceInt(consts.StatusMap, in.Status) {
err = gerror.New("状态不正确")
return err
}
// 修改
in.UpdatedAt = gtime.Now()
_, err = dao.AdminDept.Ctx(ctx).Where("id", in.Id).Data("status", in.Status).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// MaxSort 最大排序
func (s *sAdminDept) MaxSort(ctx context.Context, in adminin.DeptMaxSortInp) (*adminin.DeptMaxSortModel, error) {
var res adminin.DeptMaxSortModel
if in.Id > 0 {
if err := dao.AdminDept.Ctx(ctx).Where("id", in.Id).Order("sort desc").Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
}
res.Sort = res.Sort + 10
return &res, nil
}
// View 获取指定字典类型信息
func (s *sAdminDept) View(ctx context.Context, in adminin.DeptViewInp) (res *adminin.DeptViewModel, err error) {
if err = dao.AdminDept.Ctx(ctx).Where("id", in.Id).Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
return res, nil
}
// List 获取列表
func (s *sAdminDept) List(ctx context.Context, in adminin.DeptListInp) (list adminin.DeptListModel, err error) {
var (
mod = dao.AdminDept.Ctx(ctx)
models []*entity.AdminDept
ids []int64
pids []int64
deptList []g.Map
)
// 部门名称
if in.Name != "" {
values, err := dao.AdminDept.Ctx(ctx).Fields("pid").WhereLike("name", "%"+in.Name+"%").Array()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
for i := 0; i < len(values); i++ {
ids = append(ids, values[i].Int64())
pids = append(pids, values[i].Int64())
}
if len(ids) == 0 {
return nil, nil
}
}
if in.Code != "" {
values, err := dao.AdminDept.Ctx(ctx).Fields("pid").WhereLike("code", "%"+in.Code+"%").Array()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
for i := 0; i < len(values); i++ {
ids = append(ids, values[i].Int64())
pids = append(pids, values[i].Int64())
}
if len(ids) == 0 {
return nil, nil
}
}
if len(ids) > 0 {
ids = convert.UniqueSliceInt64(ids)
pids = convert.UniqueSliceInt64(pids)
mod = mod.Wheref(`id in (?) or pid in (?)`, ids, pids)
}
if err = mod.Order("pid asc,sort asc").Scan(&models); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, err
}
for i := 0; i < len(models); i++ {
deptList = append(deptList, g.Map{
"index": models[i].Id,
"key": models[i].Id,
"label": models[i].Name,
"id": models[i].Id,
"pid": models[i].Pid,
"name": models[i].Name,
"code": models[i].Code,
"leader": models[i].Leader,
"phone": models[i].Phone,
"email": models[i].Email,
"sort": models[i].Sort,
"created_at": models[i].CreatedAt,
"status": models[i].Status,
})
}
return tree.GenTree(deptList), nil
}
type DeptTree struct {
entity.AdminDept
Children []*DeptTree `json:"children"`
}
// getDeptChildIds 将列表转为父子关系列表
func (s *sAdminDept) getDeptChildIds(ctx context.Context, lists []*DeptTree, pid int64) []*DeptTree {
var (
count = len(lists)
newLists []*DeptTree
)
if count == 0 {
return nil
}
for i := 0; i < len(lists); i++ {
if lists[i].Id > 0 && lists[i].Pid == pid {
var row *DeptTree
if err := gconv.Structs(lists[i], &row); err != nil {
panic(err)
}
row.Children = s.getDeptChildIds(ctx, lists, row.Id)
newLists = append(newLists, row)
}
}
return newLists
}
type DeptListTree struct {
Id int64 `json:"id" `
Key int64 `json:"key" `
Pid int64 `json:"pid" `
Label string `json:"label"`
Title string `json:"title"`
Name string `json:"name"`
Type string `json:"type"`
Children []*DeptListTree `json:"children"`
}
// ListTree 获取列表树
func (s *sAdminDept) ListTree(ctx context.Context, in adminin.DeptListTreeInp) (list []*adminin.DeptListTreeModel, err error) {
var (
mod = dao.AdminDept.Ctx(ctx)
dataList []*entity.AdminDept
models []*DeptListTree
)
err = mod.Order("id desc").Scan(&dataList)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, err
}
_ = gconv.Structs(dataList, &models)
// 重写树入参
for i := 0; i < len(models); i++ {
models[i].Key = models[i].Id
models[i].Title = models[i].Name
models[i].Label = models[i].Name
}
childIds := s.getDeptTreeChildIds(ctx, models, 0)
_ = gconv.Structs(childIds, &list)
return list, nil
}
// getDeptTreeChildIds 将列表转为父子关系列表
func (s *sAdminDept) getDeptTreeChildIds(ctx context.Context, lists []*DeptListTree, pid int64) []*DeptListTree {
var (
count = len(lists)
newLists []*DeptListTree
)
if count == 0 {
return nil
}
for i := 0; i < len(lists); i++ {
if lists[i].Id > 0 && lists[i].Pid == pid {
var row *DeptListTree
if err := gconv.Structs(lists[i], &row); err != nil {
panic(err)
}
row.Children = s.getDeptTreeChildIds(ctx, lists, row.Id)
newLists = append(newLists, row)
}
}
return newLists
}
// GetName 获取部门名称
func (s *sAdminDept) GetName(ctx context.Context, id int64) (name string, err error) {
var data entity.AdminDept
err = dao.AdminDept.Ctx(ctx).
Where("id", id).
Fields("name").
Scan(&data)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return name, err
}
return data.Name, nil
}

View File

@@ -0,0 +1,576 @@
// Package admin
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 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/crypto/gmd5"
"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/gconv"
"github.com/gogf/gf/v2/util/grand"
"hotgo/api/backend/member"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/library/contexts"
"hotgo/internal/library/jwt"
"hotgo/internal/model"
"hotgo/internal/model/do"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/adminin"
"hotgo/internal/service"
"hotgo/utility/convert"
)
type sAdminMember struct{}
func NewAdminMember() *sAdminMember {
return &sAdminMember{}
}
func init() {
service.RegisterAdminMember(NewAdminMember())
}
// UpdateProfile 修改登录密码
func (s *sAdminMember) UpdateProfile(ctx context.Context, in adminin.MemberUpdateProfileInp) (err error) {
memberId := contexts.Get(ctx).User.Id
if memberId <= 0 {
err := gerror.New("获取用户信息失败!")
return err
}
var memberInfo entity.AdminMember
if err = dao.AdminMember.Ctx(ctx).Where("id", memberId).Scan(&memberInfo); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
_, err = dao.AdminMember.Ctx(ctx).
Where("id", memberId).
Data(g.Map{
"mobile": in.Mobile,
"email": in.Email,
"realname": in.Realname,
}).
Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return
}
// UpdatePwd 修改登录密码
func (s *sAdminMember) UpdatePwd(ctx context.Context, in adminin.MemberUpdatePwdInp) (err error) {
var memberInfo entity.AdminMember
if err = dao.AdminMember.Ctx(ctx).Where("id", in.Id).Scan(&memberInfo); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
if gmd5.MustEncryptString(in.OldPassword+memberInfo.Salt) != memberInfo.PasswordHash {
err = gerror.New("原密码不正确")
return err
}
_, err = dao.AdminMember.Ctx(ctx).
Where("id", in.Id).
Data(g.Map{
"password_hash": gmd5.MustEncryptString(in.NewPassword + memberInfo.Salt),
"updated_at": gtime.Now(),
}).
Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return
}
// ResetPwd 重置密码
func (s *sAdminMember) ResetPwd(ctx context.Context, in adminin.MemberResetPwdInp) (err error) {
var memberInfo entity.AdminMember
if err = dao.AdminMember.Ctx(ctx).Where("id", in.Id).Scan(&memberInfo); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
_, err = dao.AdminMember.Ctx(ctx).
Where("id", in.Id).
Data(g.Map{
"password_hash": gmd5.MustEncryptString(in.Password + memberInfo.Salt),
"updated_at": gtime.Now(),
}).
Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return
}
// EmailUnique 菜单名称是否唯一
func (s *sAdminMember) EmailUnique(ctx context.Context, in adminin.MemberEmailUniqueInp) (*adminin.MemberEmailUniqueModel, error) {
var res adminin.MemberEmailUniqueModel
isUnique, err := dao.AdminMember.IsUniqueEmail(ctx, in.Id, in.Email)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
res.IsUnique = isUnique
return &res, nil
}
// MobileUnique 手机号是否唯一
func (s *sAdminMember) MobileUnique(ctx context.Context, in adminin.MemberMobileUniqueInp) (*adminin.MemberMobileUniqueModel, error) {
var res adminin.MemberMobileUniqueModel
isUnique, err := dao.AdminMember.IsUniqueMobile(ctx, in.Id, in.Mobile)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
res.IsUnique = isUnique
return &res, nil
}
// NameUnique 菜单名称是否唯一
func (s *sAdminMember) NameUnique(ctx context.Context, in adminin.MemberNameUniqueInp) (*adminin.MemberNameUniqueModel, error) {
var res adminin.MemberNameUniqueModel
isUnique, err := dao.AdminMember.IsUniqueName(ctx, in.Id, in.Username)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
res.IsUnique = isUnique
return &res, nil
}
// VerifySuperId 验证是否为超管
func (s *sAdminMember) VerifySuperId(ctx context.Context, verifyId int64) bool {
superIds, _ := g.Cfg().Get(ctx, "hotgo.admin.superIds")
for _, id := range superIds.Int64s() {
if id == verifyId {
return true
}
}
return false
}
// Delete 删除
func (s *sAdminMember) Delete(ctx context.Context, in adminin.MemberDeleteInp) error {
if s.VerifySuperId(ctx, gconv.Int64(in.Id)) {
return gerror.New("超管账号禁止删除!")
}
_, err := dao.AdminMember.Ctx(ctx).Where("id", in.Id).Delete()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// Edit 修改/新增
func (s *sAdminMember) Edit(ctx context.Context, in adminin.MemberEditInp) (err error) {
if in.Username == "" {
return gerror.New("帐号不能为空")
}
uniqueName, err := dao.AdminMember.IsUniqueName(ctx, in.Id, in.Username)
if err != nil {
return gerror.Wrap(err, consts.ErrorORM)
}
if !uniqueName {
return gerror.New("帐号已存在")
}
if in.Mobile != "" {
uniqueMobile, err := dao.AdminMember.IsUniqueMobile(ctx, in.Id, in.Mobile)
if err != nil {
return gerror.Wrap(err, consts.ErrorORM)
}
if !uniqueMobile {
return gerror.New("手机号已存在")
}
}
if in.Email != "" {
uniqueEmail, err := dao.AdminMember.IsUniqueEmail(ctx, in.Id, in.Email)
if err != nil {
return gerror.Wrap(err, consts.ErrorORM)
}
if !uniqueEmail {
return gerror.New("邮箱已存在")
}
}
// 修改
in.UpdatedAt = gtime.Now()
if in.Id > 0 {
if s.VerifySuperId(ctx, in.Id) {
return gerror.New("超管账号禁止编辑!")
}
_, err = dao.AdminMember.Ctx(ctx).Where("id", in.Id).Data(in).Update()
if err != nil {
return gerror.Wrap(err, consts.ErrorORM)
}
// 更新岗位
if err = dao.AdminMemberPost.UpdatePostIds(ctx, in.Id, in.PostIds); err != nil {
return err
}
return nil
}
// 新增
in.CreatedAt = gtime.Now()
// 新增用户时的额外属性
var data adminin.MemberAddInp
data.MemberEditInp = in
data.Salt = grand.S(6)
data.PasswordHash = gmd5.MustEncryptString(data.Password + data.Salt)
insert, err := dao.AdminMember.Ctx(ctx).Data(data).Insert()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
// 更新岗位
id, err := insert.LastInsertId()
if err != nil {
return err
}
err = dao.AdminMemberPost.UpdatePostIds(ctx, id, in.PostIds)
if err != nil {
return err
}
return nil
}
// MaxSort 最大排序
func (s *sAdminMember) MaxSort(ctx context.Context, in adminin.MemberMaxSortInp) (*adminin.MemberMaxSortModel, error) {
var res adminin.MemberMaxSortModel
if in.Id > 0 {
if err := dao.AdminMember.Ctx(ctx).Where("id", in.Id).Order("sort desc").Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
}
res.Sort = res.Sort + 10
return &res, nil
}
// View 获取信息
func (s *sAdminMember) View(ctx context.Context, in adminin.MemberViewInp) (res *adminin.MemberViewModel, err error) {
if err = dao.AdminMember.Ctx(ctx).Where("id", in.Id).Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
return res, nil
}
// List 获取列表
func (s *sAdminMember) List(ctx context.Context, in adminin.MemberListInp) (list []*adminin.MemberListModel, totalCount int, err error) {
g.Log().Printf(ctx, "in:%#v", in)
mod := dao.AdminMember.Ctx(ctx)
if in.Realname != "" {
mod = mod.WhereLike("realname", "%"+in.Realname+"%")
}
if in.Username != "" {
mod = mod.WhereLike("username", "%"+in.Username+"%")
}
if in.Mobile > 0 {
mod = mod.Where("mobile", in.Mobile)
}
if in.Status > 0 {
mod = mod.Where("status", in.Status)
}
if in.DeptId > 0 {
mod = mod.Where("dept_id", in.DeptId)
}
if len(in.CreatedAt) == 2 {
mod = mod.WhereBetween("created_at", gtime.New(in.CreatedAt[0]), gtime.New(in.CreatedAt[1]))
}
totalCount, err = mod.Count()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, 0, err
}
if totalCount == 0 {
return list, totalCount, nil
}
if err = mod.Page(in.Page, in.PerPage).Order("id desc").Scan(&list); err != nil {
return nil, 0, gerror.Wrap(err, consts.ErrorORM)
}
// 重写树入参
for i := 0; i < len(list); i++ {
// 部门
deptName, err := dao.AdminDept.Ctx(ctx).
Fields("name").
Where("id", list[i].DeptId).
Value()
if err != nil {
return nil, 0, gerror.Wrap(err, consts.ErrorORM)
}
list[i].DeptName = deptName.String()
// 角色
roleName, err := dao.AdminRole.Ctx(ctx).
Fields("name").
Where("id", list[i].Role).
Value()
if err != nil {
return nil, 0, gerror.Wrap(err, consts.ErrorORM)
}
list[i].RoleName = roleName.String()
// 岗位
post, err := dao.AdminMemberPost.Ctx(ctx).
Fields("post_id").
Where("member_id", list[i].Id).
Value()
if err != nil {
return nil, 0, gerror.Wrap(err, consts.ErrorORM)
}
list[i].PostIds = post.Int64s()
}
return list, totalCount, nil
}
// LoginMemberInfo 获取登录用户信息
func (s *sAdminMember) LoginMemberInfo(ctx context.Context, req *member.InfoReq) (res *adminin.MemberLoginModel, err error) {
var (
permissions adminin.MemberLoginPermissions
identity *model.Identity
)
identity = contexts.Get(ctx).User
if identity == nil {
err = gerror.New("用户身份异常,请重新登录!")
return
}
permissions.Label = "主控台"
permissions.Value = "value"
res = &adminin.MemberLoginModel{
UserId: identity.Id,
Username: identity.Username,
RealName: identity.RealName,
Avatar: identity.Avatar,
Permissions: []adminin.MemberLoginPermissions{permissions},
Token: jwt.GetAuthorization(ghttp.RequestFromCtx(ctx)),
}
return
}
// Login 提交登录
func (s *sAdminMember) Login(ctx context.Context, in adminin.MemberLoginInp) (res *adminin.MemberLoginModel, err error) {
var (
roleInfo *entity.AdminRole
memberInfo *entity.AdminMember
identity *model.Identity
)
err = dao.AdminMember.Ctx(ctx).Where("username", in.Username).Scan(&memberInfo)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return
}
if memberInfo == nil {
err = gerror.New("账号不存在")
return
}
if memberInfo.Salt == "" {
err = gerror.New("用户信息错误")
return
}
if memberInfo.PasswordHash != gmd5.MustEncryptString(in.Password+memberInfo.Salt) {
err = gerror.New("用户密码不正确")
return
}
//// 默认设备
//if in.Device != consts.AppAdmin && in.Device != consts.AppApi {
// in.Device = consts.AppAdmin
//}
err = dao.AdminRole.Ctx(ctx).
Fields("id,key,status").
Where("id", memberInfo.Role).
Scan(&roleInfo)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return
}
if roleInfo == nil {
err = gerror.New("角色不存在")
return
}
if roleInfo.Status != consts.StatusEnabled {
err = gerror.New("角色权限已被禁用")
return
}
// 生成token
jwtExpires, err := g.Cfg().Get(ctx, "jwt.expires", 1)
if err != nil {
err := gerror.New(err.Error())
return nil, err
}
// 有效期
expires := jwtExpires.Int64()
// 过期时间戳
exp := gconv.Int64(gtime.Timestamp()) + expires
identity = &model.Identity{
Id: memberInfo.Id,
Username: memberInfo.Username,
RealName: memberInfo.Realname,
Avatar: memberInfo.Avatar,
Email: memberInfo.Email,
Mobile: memberInfo.Mobile,
VisitCount: memberInfo.VisitCount,
LastTime: memberInfo.LastTime,
LastIp: memberInfo.LastIp,
Role: roleInfo.Id,
RoleKey: roleInfo.Key,
Exp: exp,
Expires: expires,
App: consts.AppAdmin,
}
token, err := jwt.GenerateLoginToken(ctx, identity, false)
if err != nil {
err = gerror.New(err.Error())
return
}
// 更新登录信息
authKey := gmd5.MustEncryptString(gconv.String(token))
_, err = dao.AdminMember.Ctx(ctx).Data(do.AdminMember{
AuthKey: gmd5.MustEncryptString(authKey),
VisitCount: memberInfo.VisitCount + 1,
LastTime: gtime.Timestamp(),
LastIp: ghttp.RequestFromCtx(ctx).GetClientIp(),
}).Where(do.AdminMember{
Id: memberInfo.Id,
}).Update()
if err != nil {
err = gerror.New(err.Error())
return
}
res = &adminin.MemberLoginModel{
UserId: identity.Id,
Username: identity.Username,
RealName: identity.RealName,
Avatar: identity.Avatar,
Token: gconv.String(token),
}
return res, nil
}
// RoleMemberList 获取角色下的会员列表
func (s *sAdminMember) RoleMemberList(ctx context.Context, in adminin.RoleMemberListInp) (list []*adminin.MemberListModel, totalCount int, err error) {
mod := dao.AdminMember.Ctx(ctx)
if in.Role > 0 {
mod = mod.Where("role", in.Role)
}
totalCount, err = mod.Count()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
err = mod.Page(in.Page, in.PerPage).Order("id desc").Scan(&list)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
return list, totalCount, err
}
// Status 更新状态
func (s *sAdminMember) Status(ctx context.Context, in adminin.MemberStatusInp) (err error) {
if in.Id <= 0 {
err = gerror.New("ID不能为空")
return err
}
if s.VerifySuperId(ctx, in.Id) {
return gerror.New("超管账号不能更改状态")
}
if in.Status <= 0 {
err = gerror.New("状态不能为空")
return err
}
if !convert.InSliceInt(consts.StatusMap, in.Status) {
err = gerror.New("状态不正确")
return err
}
// 修改
in.UpdatedAt = gtime.Now()
_, err = dao.AdminMember.Ctx(ctx).Where("id", in.Id).Data("status", in.Status).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// GetIdByCode 通过邀请码获取会员ID
func (s *sAdminMember) GetIdByCode(ctx context.Context, in adminin.GetIdByCodeInp) (res *adminin.GetIdByCodeModel, err error) {
if err = dao.AdminMember.Ctx(ctx).
Fields("invite_code").
Where("invite_code", in.Code).
Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
return res, nil
}

View File

@@ -0,0 +1,67 @@
// Package admin
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 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"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/model/entity"
"hotgo/internal/service"
)
type sAdminMemberPost struct{}
func NewAdminMemberPost() *sAdminMemberPost {
return &sAdminMemberPost{}
}
func init() {
service.RegisterAdminMemberPost(NewAdminMemberPost())
}
func (s *sAdminMemberPost) UpdatePostIds(ctx context.Context, member_id int64, post_ids []int64) (err error) {
_, err = dao.AdminMemberPost.Ctx(ctx).Where("member_id", member_id).Delete()
if err != nil {
err = gerror.Wrap(err, "删除失败")
return err
}
for i := 0; i < len(post_ids); i++ {
_, err = dao.AdminMemberPost.Ctx(ctx).
Insert(entity.AdminMemberPost{
MemberId: member_id,
PostId: post_ids[i],
})
if err != nil {
err = gerror.Wrap(err, "插入会员岗位失败")
return err
}
}
return nil
}
// GetMemberByIds 获取指定会员的岗位ids
func (s *sAdminMemberPost) GetMemberByIds(ctx context.Context, member_id int64) (post_ids []int64, err error) {
var list []*entity.AdminMemberPost
err = dao.AdminMemberPost.Ctx(ctx).
Fields("post_id").
Where("member_id", member_id).
Scan(&list)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return post_ids, err
}
for i := 0; i < len(list); i++ {
post_ids = append(post_ids, list[i].PostId)
}
return post_ids, nil
}

View File

@@ -0,0 +1,369 @@
// Package admin
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 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/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
"hotgo/api/backend/menu"
"hotgo/api/backend/role"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/library/casbin"
"hotgo/internal/library/contexts"
"hotgo/internal/model"
"hotgo/internal/model/do"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/adminin"
"hotgo/internal/service"
"hotgo/utility/tree"
)
type sAdminMenu struct{}
func NewAdminMenu() *sAdminMenu {
return &sAdminMenu{}
}
func init() {
service.RegisterAdminMenu(NewAdminMenu())
}
// RoleList 查询角色菜单列表
func (s *sAdminMenu) RoleList(ctx context.Context, in adminin.MenuRoleListInp) (*adminin.MenuRoleListModel, error) {
var (
mod = dao.AdminRoleMenu.Ctx(ctx)
roleMenu []*entity.AdminRoleMenu
lst []*model.LabelTreeMenu
res adminin.MenuRoleListModel
err error
checkedKeys []int64
)
// 获取选中菜单ID
if in.RoleId > 0 {
mod = mod.Where("role_id", in.RoleId)
}
if err = mod.Fields().Scan(&roleMenu); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
for i := 0; i < len(roleMenu); i++ {
checkedKeys = append(checkedKeys, roleMenu[i].MenuId)
}
res.CheckedKeys = checkedKeys
// 获取菜单树
lst, err = dao.AdminMenu.GenLabelTreeList(ctx, 0)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
_ = gconv.Structs(lst, &res.Menus)
return &res, nil
}
// SearchList 查询菜单列表
func (s *sAdminMenu) SearchList(ctx context.Context, req *menu.SearchListReq) (*menu.SearchListRes, error) {
var (
mod = dao.AdminMenu.Ctx(ctx)
lst []*model.TreeMenu
res menu.SearchListRes
searchResult []*entity.AdminMenu
id int64
ids []int64
err error
)
if req.Name != "" {
mod = mod.WhereLike("name", "%"+req.Name+"%")
}
if req.Status > 0 {
mod = mod.Where("status", req.Status)
}
if req.Name != "" || req.Status > 0 {
err = mod.Scan(&searchResult)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
for i := 0; i < len(searchResult); i++ {
id, err = dao.AdminMenu.TopPid(ctx, searchResult[i])
ids = append(ids, id)
}
}
lst, err = dao.AdminMenu.GenTreeList(ctx, 0, ids)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
_ = gconv.Structs(lst, &res)
return &res, nil
}
// MaxSort 最大排序
func (s *sAdminMenu) MaxSort(ctx context.Context, req *menu.MaxSortReq) (*menu.MaxSortRes, error) {
var (
res menu.MaxSortRes
err error
)
if req.Id > 0 {
if err = dao.AdminMenu.Ctx(ctx).Where("id", req.Id).Order("sort desc").Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
}
res.Sort = res.Sort + 10
return &res, nil
}
// NameUnique 菜单名称是否唯一
func (s *sAdminMenu) NameUnique(ctx context.Context, req *menu.NameUniqueReq) (*menu.NameUniqueRes, error) {
var (
res menu.NameUniqueRes
err error
)
res.IsUnique, err = dao.AdminMenu.IsUniqueName(ctx, req.Id, req.Name)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
return &res, nil
}
// CodeUnique 菜单编码是否唯一
func (s *sAdminMenu) CodeUnique(ctx context.Context, req *menu.CodeUniqueReq) (*menu.CodeUniqueRes, error) {
var (
res menu.CodeUniqueRes
err error
)
res.IsUnique, err = dao.AdminMenu.IsUniqueName(ctx, req.Id, req.Code)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
return &res, nil
}
// Delete 删除
func (s *sAdminMenu) Delete(ctx context.Context, req *menu.DeleteReq) error {
exist, err := dao.AdminMenu.Ctx(ctx).Where("pid", req.Id).One()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
if !exist.IsEmpty() {
return gerror.New("请先删除该菜单下的所有菜单!")
}
_, err = dao.AdminMenu.Ctx(ctx).Where("id", req.Id).Delete()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// Edit 修改/新增
func (s *sAdminMenu) Edit(ctx context.Context, req *menu.EditReq) (err error) {
var (
pidData *do.AdminMenu
uniqueName bool
uniqueCode bool
)
if req.Title == "" {
err = gerror.New("菜单名称不能为空")
return err
}
if req.Path == "" {
err = gerror.New("菜单路径不能为空")
return err
}
if req.Name == "" {
err = gerror.New("路由名称不能为空")
return err
}
uniqueName, err = dao.AdminMenu.IsUniqueTitle(ctx, req.Id, req.Title)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
if !uniqueName {
err = gerror.New("菜单名称已存在")
return err
}
uniqueCode, err = dao.AdminMenu.IsUniqueName(ctx, req.Id, req.Name)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
if !uniqueCode {
err = gerror.New("菜单编码已存在")
return err
}
// 维护菜单等级
if req.Pid == 0 {
req.Level = 1
} else {
if err = dao.AdminMenu.Ctx(ctx).Where("id", req.Pid).Scan(&pidData); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
if pidData == nil {
return gerror.New("上级菜单信息错误")
}
req.Level = gconv.Int(pidData.Level) + 1
}
// 修改
req.UpdatedAt = gtime.Now()
if req.Id > 0 {
if req.Pid == req.Id {
return gerror.New("上级菜单不能是当前菜单")
}
_, err = dao.AdminMenu.Ctx(ctx).Where("id", req.Id).Data(req).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return casbin.Refresh(ctx)
}
// 新增
req.CreatedAt = gtime.Now()
_, err = dao.AdminMenu.Ctx(ctx).Data(req).Insert()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return casbin.Refresh(ctx)
}
// View 获取信息
func (s *sAdminMenu) View(ctx context.Context, req *menu.ViewReq) (res *menu.ViewRes, err error) {
if err = dao.AdminMenu.Ctx(ctx).Where("id", req.Id).Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
return res, nil
}
// List 获取菜单列表
func (s *sAdminMenu) List(ctx context.Context, req *menu.ListReq) (lists []map[string]interface{}, err error) {
var models []*adminin.MenuTree
err = dao.AdminMenu.Ctx(ctx).Order("sort asc,id desc").Scan(&models)
if err != nil {
return lists, err
}
return tree.GenTree(gconv.SliceMap(models)), nil
}
// genNaiveMenus 生成NaiveUI菜单格式
func (s *sAdminMenu) genNaiveMenus(menus []adminin.MenuRouteSummary) (sources []adminin.MenuRoute) {
for _, men := range menus {
var source adminin.MenuRoute
source.Name = men.Name
source.Path = men.Path
source.Redirect = men.Redirect
source.Component = men.Component
source.Meta = adminin.MenuRouteMeta{
Title: men.Title,
Icon: men.Icon,
KeepAlive: men.KeepAlive == 1,
Hidden: men.Hidden == 1,
Sort: men.Sort,
AlwaysShow: men.AlwaysShow == 1,
ActiveMenu: men.ActiveMenu,
IsRoot: men.IsRoot == 1,
FrameSrc: men.FrameSrc,
//Permissions: men.Permissions,
Affix: men.Affix == 1,
Type: men.Type,
}
if len(men.Children) > 0 {
source.Children = append(source.Children, s.genNaiveMenus(men.Children)...)
}
sources = append(sources, source)
}
return
}
// getChildrenList 生成菜单树
func (s *sAdminMenu) getChildrenList(menu *adminin.MenuRouteSummary, treeMap map[string][]adminin.MenuRouteSummary) (err error) {
menu.Children = treeMap[gconv.String(menu.Id)]
for i := 0; i < len(menu.Children); i++ {
err = s.getChildrenList(&menu.Children[i], treeMap)
}
return err
}
// GetMenuList 获取菜单列表
func (s *sAdminMenu) GetMenuList(ctx context.Context, memberId int64) (lists role.DynamicRes, err error) {
var (
allMenus []adminin.MenuRouteSummary
menus []adminin.MenuRouteSummary
treeMap = make(map[string][]adminin.MenuRouteSummary)
mod = dao.AdminMenu.Ctx(ctx).Where("status", consts.StatusEnabled).WhereIn("type", []int{1, 2})
)
// 非超管验证允许的菜单列表
if !service.AdminMember().VerifySuperId(ctx, memberId) {
array, err := dao.AdminRoleMenu.Ctx(ctx).
Fields("menu_id").
Where("role_id", contexts.GetRoleId(ctx)).
Array()
if err != nil {
return role.DynamicRes{}, err
}
mod = mod.Where("id", array)
}
if err = mod.Order("sort asc,id desc").Scan(&allMenus); err != nil {
return lists, err
}
if len(allMenus) == 0 {
return lists, nil
}
for _, v := range allMenus {
treeMap[gconv.String(v.Pid)] = append(treeMap[gconv.String(v.Pid)], v)
}
menus = treeMap["0"]
for i := 0; i < len(menus); i++ {
err = s.getChildrenList(&menus[i], treeMap)
}
lists.List = append(lists.List, s.genNaiveMenus(menus)...)
return
}

View File

@@ -0,0 +1,180 @@
// Package admin
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 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/os/grpool"
"github.com/gogf/gf/v2/os/gtime"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/model/input/adminin"
"hotgo/internal/service"
"hotgo/internal/websocket"
"hotgo/utility/charset"
"hotgo/utility/convert"
"strings"
)
type sAdminNotice struct{}
func NewAdminNotice() *sAdminNotice {
return &sAdminNotice{}
}
func init() {
service.RegisterAdminNotice(NewAdminNotice())
}
// Delete 删除
func (s *sAdminNotice) Delete(ctx context.Context, in adminin.NoticeDeleteInp) error {
_, err := dao.AdminNotice.Ctx(ctx).Where("id", in.Id).Delete()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// Edit 修改/新增
func (s *sAdminNotice) Edit(ctx context.Context, in adminin.NoticeEditInp) (err error) {
if in.Title == "" {
err = gerror.New("标题不能为空")
return err
}
// 修改
in.UpdatedAt = gtime.Now()
if in.Id > 0 {
_, err = dao.AdminNotice.Ctx(ctx).Where("id", in.Id).Data(in).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// 新增
in.CreatedAt = gtime.Now()
_, err = dao.AdminNotice.Ctx(ctx).Data(in).Insert()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
// 推送通知
memberIds := charset.SplitMemberIds(in.Receiver, ",")
response := &websocket.WResponse{
Event: "notice",
Data: in,
}
grpool.AddWithRecover(ctx, func(ctx context.Context) {
if len(memberIds) == 0 {
websocket.SendToAll(response)
} else {
for _, memberId := range memberIds {
websocket.SendToUser(memberId, response)
}
}
})
return nil
}
// Status 更新部门状态
func (s *sAdminNotice) Status(ctx context.Context, in adminin.NoticeStatusInp) (err error) {
if in.Id <= 0 {
err = gerror.New("ID不能为空")
return err
}
if in.Status <= 0 {
err = gerror.New("状态不能为空")
return err
}
if !convert.InSliceInt(consts.StatusMap, in.Status) {
err = gerror.New("状态不正确")
return err
}
// 修改
in.UpdatedAt = gtime.Now()
_, err = dao.AdminNotice.Ctx(ctx).Where("id", in.Id).Data("status", in.Status).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// MaxSort 最大排序
func (s *sAdminNotice) MaxSort(ctx context.Context, in adminin.NoticeMaxSortInp) (*adminin.NoticeMaxSortModel, error) {
var res adminin.NoticeMaxSortModel
if in.Id > 0 {
if err := dao.AdminNotice.Ctx(ctx).Where("id", in.Id).Order("sort desc").Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
}
res.Sort = res.Sort + 10
return &res, nil
}
// View 获取指定字典类型信息
func (s *sAdminNotice) View(ctx context.Context, in adminin.NoticeViewInp) (res *adminin.NoticeViewModel, err error) {
if err = dao.AdminNotice.Ctx(ctx).Where("id", in.Id).Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
return res, nil
}
// List 获取列表
func (s *sAdminNotice) List(ctx context.Context, in adminin.NoticeListInp) (list []*adminin.NoticeListModel, totalCount int, err error) {
mod := dao.AdminNotice.Ctx(ctx)
// 访问路径
if in.Title != "" {
mod = mod.WhereLike("title", "%"+in.Title+"%")
}
// 模块
if in.Content != "" {
mod = mod.WhereLike("content", "%"+in.Content+"%")
}
// 请求方式
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("id desc").Scan(&list); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
for k, v := range list {
list[k].ReceiveNum = len(strings.Split(v.Reader, ","))
}
return list, totalCount, err
}

View File

@@ -0,0 +1,243 @@
// Package admin
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 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/os/gtime"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/model/input/adminin"
"hotgo/internal/service"
"hotgo/utility/convert"
)
type sAdminPost struct{}
func NewAdminPost() *sAdminPost {
return &sAdminPost{}
}
func init() {
service.RegisterAdminPost(NewAdminPost())
}
// Delete 删除
func (s *sAdminPost) Delete(ctx context.Context, in adminin.PostDeleteInp) error {
exist, err := dao.AdminMemberPost.Ctx(ctx).Where("post_id", 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).Where("id", in.Id).Delete()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// Edit 修改/新增
func (s *sAdminPost) Edit(ctx context.Context, in adminin.PostEditInp) (err error) {
if in.Name == "" {
err = gerror.New("名称不能为空")
return err
}
if in.Code == "" {
err = gerror.New("编码不能为空")
return err
}
uniqueName, err := dao.AdminPost.IsUniqueName(ctx, in.Id, in.Name)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
if !uniqueName {
err = gerror.New("名称已存在")
return err
}
uniqueCode, err := dao.AdminPost.IsUniqueCode(ctx, in.Id, in.Code)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
if !uniqueCode {
err = gerror.New("编码已存在")
return err
}
// 修改
in.UpdatedAt = gtime.Now()
if in.Id > 0 {
_, err = dao.AdminPost.Ctx(ctx).Where("id", in.Id).Data(in).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// 新增
in.CreatedAt = gtime.Now()
_, err = dao.AdminPost.Ctx(ctx).Data(in).Insert()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// MaxSort 最大排序
func (s *sAdminPost) MaxSort(ctx context.Context, in adminin.PostMaxSortInp) (*adminin.PostMaxSortModel, error) {
var res adminin.PostMaxSortModel
if in.Id > 0 {
if err := dao.AdminMenu.Ctx(ctx).Where("id", in.Id).Order("sort desc").Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
}
res.Sort = res.Sort + 10
return &res, nil
}
// NameUnique 菜单名称是否唯一
func (s *sAdminPost) NameUnique(ctx context.Context, in adminin.PostNameUniqueInp) (*adminin.PostNameUniqueModel, error) {
var res adminin.PostNameUniqueModel
isUnique, err := dao.AdminPost.IsUniqueName(ctx, in.Id, in.Name)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
res.IsUnique = isUnique
return &res, nil
}
// CodeUnique 编码是否唯一
func (s *sAdminPost) CodeUnique(ctx context.Context, in adminin.PostCodeUniqueInp) (*adminin.PostCodeUniqueModel, error) {
var res adminin.PostCodeUniqueModel
isUnique, err := dao.AdminPost.IsUniqueCode(ctx, in.Id, in.Code)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
res.IsUnique = isUnique
return &res, nil
}
// View 获取指定字典类型信息
func (s *sAdminPost) View(ctx context.Context, in adminin.PostViewInp) (res *adminin.PostViewModel, err error) {
if err = dao.AdminPost.Ctx(ctx).Where("id", in.Id).Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
return res, nil
}
// List 获取列表
func (s *sAdminPost) List(ctx context.Context, in adminin.PostListInp) (list []*adminin.PostListModel, totalCount int, err error) {
mod := dao.AdminPost.Ctx(ctx)
// 访问路径
if in.Name != "" {
mod = mod.WhereLike("name", "%"+in.Name+"%")
}
// 模块
if in.Code != "" {
mod = mod.Where("code", in.Code)
}
// 请求方式
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("id desc").Scan(&list); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
return list, totalCount, err
}
// GetMemberByStartName 获取指定用户的第一岗位
func (s *sAdminPost) GetMemberByStartName(ctx context.Context, memberId int64) (name string, err error) {
// 默认取第一岗位
postId, err := dao.AdminMemberPost.Ctx(ctx).
Fields("post_id").
Where("member_id", memberId).
Limit(1).
Value()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return name, err
}
val, err := dao.AdminPost.Ctx(ctx).
Fields("name").
Where("id", postId.Int()).
Order("id desc").
Value()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return name, err
}
return val.String(), nil
}
// Status 更新状态
func (s *sAdminPost) Status(ctx context.Context, in adminin.PostStatusInp) (err error) {
if in.Id <= 0 {
err = gerror.New("ID不能为空")
return err
}
if in.Status <= 0 {
err = gerror.New("状态不能为空")
return err
}
if !convert.InSliceInt(consts.StatusMap, in.Status) {
err = gerror.New("状态不正确")
return err
}
// 修改
in.UpdatedAt = gtime.Now()
_, err = dao.AdminPost.Ctx(ctx).Where("id", in.Id).Data("status", in.Status).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}

View File

@@ -0,0 +1,221 @@
// Package admin
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 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/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"hotgo/api/backend/role"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/library/casbin"
"hotgo/internal/library/contexts"
"hotgo/internal/model/input/adminin"
"hotgo/internal/service"
"hotgo/utility/auth"
)
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 {
if auth.IsExceptAuth(ctx, path) {
return true
}
var (
user = contexts.Get(ctx).User
superRoleKey, _ = g.Cfg().Get(ctx, "hotgo.admin.superRoleKey")
err error
)
if user == nil {
g.Log().Warning(ctx, "admin Verify user = nil")
return false
}
if service.AdminMember().VerifySuperId(ctx, user.Id) || user.RoleKey == superRoleKey.String() {
return true
}
ok, err := casbin.Enforcer.Enforce(user.RoleKey, path, method)
if err != nil {
g.Log().Warningf(ctx, "admin Verify Enforce err:%v", err)
return false
}
return ok
}
// List 获取列表
func (s *sAdminRole) List(ctx context.Context, in adminin.RoleListInp) (list []*adminin.RoleListModel, totalCount int, err error) {
mod := dao.AdminRole.Ctx(ctx)
totalCount, err = mod.Count()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
err = mod.Page(in.Page, in.PerPage).Order("id asc").Scan(&list)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
return list, totalCount, err
}
// GetName 获取指定角色的名称
func (s *sAdminRole) GetName(ctx context.Context, RoleId int64) (name string, err error) {
roleName, err := dao.AdminRole.Ctx(ctx).
Fields("name").
Where("id", RoleId).
Order("id desc").
Value()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return name, err
}
return roleName.String(), nil
}
// GetMemberList 获取指定会员的岗位列表
func (s *sAdminRole) GetMemberList(ctx context.Context, RoleId int64) (list []*adminin.RoleListModel, err error) {
err = dao.AdminRole.Ctx(ctx).
Where("id", RoleId).
Order("id desc").
Scan(&list)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, err
}
return list, err
}
// GetPermissions 更改角色菜单权限
func (s *sAdminRole) GetPermissions(ctx context.Context, reqInfo *role.GetPermissionsReq) (MenuIds []int64, err error) {
values, err := dao.AdminRoleMenu.Ctx(ctx).
Fields("menu_id").
Where("role_id", reqInfo.RoleId).
Array()
if err != nil {
return nil, err
}
if len(values) == 0 {
return
}
for i := 0; i < len(values); i++ {
MenuIds = append(MenuIds, values[i].Int64())
}
return
}
// UpdatePermissions 更改角色菜单权限
func (s *sAdminRole) UpdatePermissions(ctx context.Context, reqInfo *role.UpdatePermissionsReq) error {
return dao.AdminRoleMenu.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) (err error) {
_, err = dao.AdminRoleMenu.Ctx(ctx).Where("role_id", reqInfo.RoleId).Delete()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
if len(reqInfo.MenuIds) == 0 {
return nil
}
addMap := make(g.List, 0, len(reqInfo.MenuIds))
for _, v := range reqInfo.MenuIds {
addMap = append(addMap, g.Map{
"role_id": reqInfo.RoleId,
"menu_id": v,
})
}
_, err = dao.AdminRoleMenu.Ctx(ctx).Data(addMap).Insert()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return casbin.Refresh(ctx)
})
}
func (s *sAdminRole) Edit(ctx context.Context, in *role.EditReq) (err error) {
if in.Name == "" {
err = gerror.New("名称不能为空")
return err
}
if in.Key == "" {
err = gerror.New("编码不能为空")
return err
}
uniqueName, err := dao.AdminRole.IsUniqueName(ctx, in.Id, in.Name)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
if !uniqueName {
err = gerror.New("名称已存在")
return err
}
uniqueCode, err := dao.AdminRole.IsUniqueCode(ctx, in.Id, in.Key)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
if !uniqueCode {
err = gerror.New("编码已存在")
return err
}
// 修改
in.UpdatedAt = gtime.Now()
if in.Id > 0 {
_, err = dao.AdminRole.Ctx(ctx).Where("id", in.Id).Data(in).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// 新增
in.CreatedAt = gtime.Now()
_, err = dao.AdminRole.Ctx(ctx).Data(in).Insert()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
func (s *sAdminRole) Delete(ctx context.Context, in *role.DeleteReq) (err error) {
if in.Id <= 0 {
return gerror.New("ID不正确")
}
_, err = dao.AdminRole.Ctx(ctx).Where("id", in.Id).Delete()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}

View File

@@ -0,0 +1,139 @@
// Package common
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 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/util/gconv"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"hotgo/utility/encrypt"
f "hotgo/utility/file"
"hotgo/utility/format"
"hotgo/utility/validate"
"strings"
"time"
)
type sCommonUpload struct{}
func NewCommonUpload() *sCommonUpload {
return &sCommonUpload{}
}
func init() {
service.RegisterCommonUpload(NewCommonUpload())
}
// UploadImage 上传图片
func (s *sCommonUpload) UploadImage(ctx context.Context, file *ghttp.UploadFile) (result *sysin.AttachmentListModel, err error) {
if file == nil {
err = gerror.New("文件必须!")
return
}
meta, err := s.fileMeta(file)
if err != nil {
return
}
if !f.IsImgType(meta.Ext) {
return nil, gerror.New("上传的文件不是图片")
}
return s.UploadLocal(ctx, file, meta)
}
// UploadLocal 上传本地
func (s *sCommonUpload) UploadLocal(ctx context.Context, file *ghttp.UploadFile, meta *sysin.UploadFileMeta) (result *sysin.AttachmentListModel, err error) {
result, err = dao.SysAttachment.GetMd5File(ctx, meta.Md5)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return
}
if result != nil {
return
}
var (
value, _ = g.Cfg().Get(ctx, "server.serverRoot")
nowDate = time.Now().Format("2006-01-02")
)
if value.IsEmpty() {
err = gerror.New("本地上传驱动必须配置静态路径!")
return
}
// 包含静态文件夹的路径
fullDirPath := strings.Trim(value.String(), "/") + "/attachment/" + nowDate
fileName, err := file.Save(fullDirPath, true)
if err != nil {
return
}
// 不含静态文件夹的路径
fullPath := "attachment/" + nowDate + "/" + fileName
attachment, err := service.SysAttachment().Add(ctx, meta, fullPath, consts.UploadDriveLocal)
if err != nil {
return nil, err
}
attachment.FileUrl = s.LastUrl(ctx, attachment.FileUrl, attachment.Drive)
result = &sysin.AttachmentListModel{
SysAttachment: *attachment,
SizeFormat: format.FileSize(attachment.Size),
}
return
}
// LastUrl 根据驱动获取最终文件访问地址
func (s *sCommonUpload) LastUrl(ctx context.Context, fullPath, drive string) string {
if validate.IsURL(fullPath) {
return fullPath
}
if drive == consts.UploadDriveLocal {
return fmt.Sprintf("http://%s/", g.RequestFromCtx(ctx).Host) + "/" + fullPath
}
return fullPath
}
// fileMeta 上传文件元数据
func (s *sCommonUpload) fileMeta(file *ghttp.UploadFile) (meta *sysin.UploadFileMeta, err error) {
meta = new(sysin.UploadFileMeta)
meta.Filename = file.Filename
meta.Size = file.Size
meta.Ext = f.Ext(file.Filename)
meta.Kind = f.GetFileKind(meta.Ext)
meta.MetaType, err = f.GetFileType(meta.Ext)
if err != nil {
return
}
// 兼容naiveUI
naiveType := "text/plain"
if f.IsImgType(f.Ext(file.Filename)) {
naiveType = ""
}
meta.NaiveType = naiveType
// 文件hash
b, err := f.UploadFileByte(file)
if err != nil {
return
}
meta.Md5 = encrypt.Md5ToString(gconv.String(encrypt.Hash32(b)))
return
}

View File

@@ -0,0 +1,37 @@
// Package hook
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
//
package hook
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gtime"
"hotgo/internal/library/contexts"
"hotgo/internal/service"
)
// GlobalLog 全局日志
func (s *sHook) GlobalLog(r *ghttp.Request) {
var (
ctx = r.Context()
)
// 没有上下文的请求不记录doc、favicon.ico等非功能类业务
modelCtx := contexts.Get(ctx)
if modelCtx == nil {
return
}
// 计算运行耗时
contexts.SetTakeUpTime(ctx, gtime.TimestampMilli()-r.EnterTime)
go func() {
if err := service.SysLog().AutoLog(ctx); err != nil {
g.Log().Info(ctx, "GlobalLog AutoLog err:", err)
}
}()
}

View File

@@ -0,0 +1,22 @@
// Package hook
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
//
package hook
import (
"hotgo/internal/service"
)
type sHook struct {
}
func init() {
service.RegisterHook(New())
}
func New() *sHook {
return &sHook{}
}

View File

@@ -0,0 +1,13 @@
// ==========================================================================
// Code generated 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/sys"
)

View File

@@ -0,0 +1,51 @@
// Package middleware
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 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/auth"
)
// AdminAuth 后台鉴权中间件
func (s *sMiddleware) AdminAuth(r *ghttp.Request) {
var (
ctx = r.Context()
)
// 替换掉模块前缀
routerPrefix, _ := g.Cfg().Get(ctx, "router.admin.prefix", "/admin")
path := gstr.Replace(r.URL.Path, routerPrefix.String(), "", 1)
/// 不需要验证登录的路由地址
if auth.IsExceptLogin(ctx, path) {
r.Middleware.Next()
return
}
if err := inspectAuth(r, consts.AppAdmin); err != nil {
response.JsonExit(r, gcode.CodeNotAuthorized.Code(), err.Error())
return
}
// 验证路由访问权限
if !service.AdminRole().Verify(ctx, path, r.Method) {
g.Log().Warningf(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()
}

View File

@@ -0,0 +1,49 @@
// Package middleware
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 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/response"
"hotgo/utility/auth"
)
// ApiAuth API鉴权中间件
func (s *sMiddleware) ApiAuth(r *ghttp.Request) {
var (
ctx = r.Context()
)
// 替换掉模块前缀
routerPrefix, _ := g.Cfg().Get(ctx, "router.api.prefix", "/api")
path := gstr.Replace(r.URL.Path, routerPrefix.String(), "", 1)
/// 不需要验证登录的路由地址
if auth.IsExceptLogin(ctx, path) {
r.Middleware.Next()
return
}
if err := inspectAuth(r, consts.AppAdmin); err != nil {
response.JsonExit(r, gcode.CodeNotAuthorized.Code(), err.Error())
return
}
//// 验证路由访问权限
//verify := adminService.Role.Verify(ctx, customCtx.User.Id, path)
//if !verify {
// response.JsonExit(r, gcode.CodeSecurityReason.Code(), "你没有访问权限!")
// return
//}
r.Middleware.Next()
}

View File

@@ -0,0 +1,172 @@
// Package middleware
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
//
package middleware
import (
"github.com/gogf/gf/v2/crypto/gmd5"
"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/gconv"
"hotgo/internal/consts"
"hotgo/internal/library/cache"
"hotgo/internal/library/contexts"
"hotgo/internal/library/jwt"
"hotgo/internal/library/response"
"hotgo/internal/model"
"hotgo/internal/service"
"net/http"
"strings"
)
type sMiddleware struct {
LoginUrl string // 登录路由地址
DemoWhiteList g.Map // 演示模式放行的路由白名單
}
func init() {
service.RegisterMiddleware(New())
}
func New() *sMiddleware {
return &sMiddleware{
LoginUrl: "/common",
DemoWhiteList: g.Map{
"/admin/site/login": struct{}{}, // 后台登录
},
}
}
// Ctx 初始化请求上下文
func (s *sMiddleware) Ctx(r *ghttp.Request) {
contexts.Init(r, &model.Context{
Data: make(g.Map),
Module: getModule(r.URL.Path),
})
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) {
isDemo, _ := g.Cfg().Get(r.Context(), "hotgo.isDemo", false)
if !isDemo.Bool() {
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.CodeInvalidRequest.Code(), "演示系統禁止操作!")
return
}
r.Middleware.Next()
}
// inspectAuth 检查并完成身份认证
func inspectAuth(r *ghttp.Request, appName string) error {
var (
ctx = r.Context()
user = new(model.Identity)
authorization = jwt.GetAuthorization(r)
c = cache.New()
customCtx = &model.Context{}
)
if authorization == "" {
return gerror.New("请先登录!")
}
// 获取jwtToken
jwtToken := consts.RedisJwtToken + gmd5.MustEncryptString(authorization)
jwtSign, _ := g.Cfg().Get(ctx, "jwt.sign", "hotgo")
data, ParseErr := jwt.ParseToken(authorization, jwtSign.Bytes())
if ParseErr != nil {
return gerror.Newf("token不正确或已过期! err :%+v", ParseErr.Error())
}
parseErr := gconv.Struct(data, &user)
if parseErr != nil {
return gerror.Newf("登录信息解析异常,请重新登录! err :%+v", ParseErr.Error())
}
// 判断token跟redis的缓存的token是否一样
isContains, containsErr := c.Contains(ctx, jwtToken)
if containsErr != nil {
return gerror.Newf("token无效 err :%+v", ParseErr.Error())
}
if !isContains {
return gerror.New("token已过期")
}
// 是否开启多端登录
if multiPort, _ := g.Cfg().Get(ctx, "jwt.multiPort", true); !multiPort.Bool() {
key := consts.RedisJwtUserBind + appName + ":" + gconv.String(user.Id)
originJwtToken, originErr := c.Get(ctx, key)
if originErr != nil {
return gerror.Newf("信息异常,请重新登录! err :%+v", originErr.Error())
}
if originJwtToken == nil || originJwtToken.IsEmpty() {
return gerror.New("token已过期")
}
if jwtToken != originJwtToken.String() {
return gerror.New("账号已在其他地方登录!")
}
}
// 保存到上下文
if user != nil {
customCtx.User = &model.Identity{
Id: user.Id,
Username: user.Username,
RealName: user.RealName,
Avatar: user.Avatar,
Email: user.Email,
Mobile: user.Mobile,
VisitCount: user.VisitCount,
LastTime: user.LastTime,
LastIp: user.LastIp,
Role: user.Role,
RoleKey: user.RoleKey,
Exp: user.Exp,
Expires: user.Expires,
App: user.App,
}
}
contexts.SetUser(ctx, customCtx.User)
return nil
}

View File

@@ -0,0 +1,74 @@
// Package middleware
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 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"
"hotgo/internal/consts"
"hotgo/internal/library/contexts"
"hotgo/internal/library/response"
"hotgo/utility/charset"
)
// ResponseHandler HTTP响应预处理
func (s *sMiddleware) ResponseHandler(r *ghttp.Request) {
r.Middleware.Next()
var (
ctx = r.Context()
comResponse = contexts.Get(ctx).Response
code = gcode.CodeOK.Code()
message = "操作成功"
data interface{}
err error
)
if err := r.GetError(); err != nil {
g.Log().Print(ctx, err)
// 记录到自定义错误日志文件
//g.Log("exception").Error(err)
////返回固定的友好信息
//r.Response.ClearBuffer()
//r.Response.Writeln("服务器居然开小差了,请稍后再试吧!")
}
// 已存在响应内容且是comResponse返回的时中断运行
if r.Response.BufferLength() > 0 && comResponse != nil {
return
}
if err = r.GetError(); err != nil {
// 记录到自定义错误日志文件
g.Log("exception").Print(r.Context(), "exception:", err)
code = consts.CodeInternalError
message = "服务器居然开小差了,请稍后再试吧!"
// 是否输出错误到页面
if debug, _ := g.Cfg().Get(ctx, "hotgo.debug", true); debug.Bool() {
data = charset.GetStack(err)
message = err.Error()
}
//} else if data, err = r.GetHandlerResponse(); err != nil {
// errCode := gerror.Code(err)
// if errCode == gcode.CodeNil {
// errCode = gcode.CodeInternalError
// }
// code = errCode.Code()
// message = err.Error()
//}
} else {
data = r.GetHandlerResponse()
}
// 返回固定的友好信息
response.RJson(r, code, message, data)
}

View File

@@ -0,0 +1,42 @@
// Package middleware
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 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/response"
"hotgo/utility/auth"
)
// WebSocketToken 检查ws连接token
func (s *sMiddleware) WebSocketToken(r *ghttp.Request) {
var (
ctx = r.Context()
)
// 替换掉模块前缀
routerPrefix, _ := g.Cfg().Get(ctx, "router.ws.prefix", "/socket")
path := gstr.Replace(r.URL.Path, routerPrefix.String(), "", 1)
/// 不需要验证登录的路由地址
if auth.IsExceptLogin(ctx, path) {
r.Middleware.Next()
return
}
if err := inspectAuth(r, consts.AppAdmin); err != nil {
response.JsonExit(r, gcode.CodeNotAuthorized.Code(), err.Error())
return
}
r.Middleware.Next()
}

View File

@@ -0,0 +1,204 @@
// Package sys
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 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/os/gtime"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/library/contexts"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"hotgo/utility/convert"
"hotgo/utility/format"
)
type sSysAttachment struct{}
func NewSysAttachment() *sSysAttachment {
return &sSysAttachment{}
}
func init() {
service.RegisterSysAttachment(NewSysAttachment())
}
// Delete 删除
func (s *sSysAttachment) Delete(ctx context.Context, in sysin.AttachmentDeleteInp) error {
_, err := dao.SysAttachment.Ctx(ctx).Where("id", in.Id).Delete()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// Edit 修改/新增
func (s *sSysAttachment) Edit(ctx context.Context, in sysin.AttachmentEditInp) (err error) {
if in.Name == "" {
err = gerror.New("标题不能为空")
return err
}
// 修改
in.UpdatedAt = gtime.Now()
if in.Id > 0 {
_, err = dao.SysAttachment.Ctx(ctx).Where("id", in.Id).Data(in).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// 新增
in.CreatedAt = gtime.Now()
_, err = dao.SysAttachment.Ctx(ctx).Data(in).Insert()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// Status 更新部门状态
func (s *sSysAttachment) Status(ctx context.Context, in sysin.AttachmentStatusInp) (err error) {
if in.Id <= 0 {
err = gerror.New("ID不能为空")
return err
}
if in.Status <= 0 {
err = gerror.New("状态不能为空")
return err
}
if !convert.InSliceInt(consts.StatusMap, in.Status) {
err = gerror.New("状态不正确")
return err
}
// 修改
in.UpdatedAt = gtime.Now()
_, err = dao.SysAttachment.Ctx(ctx).Where("id", in.Id).Data("status", in.Status).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// MaxSort 最大排序
func (s *sSysAttachment) MaxSort(ctx context.Context, in sysin.AttachmentMaxSortInp) (*sysin.AttachmentMaxSortModel, error) {
var res sysin.AttachmentMaxSortModel
if in.Id > 0 {
if err := dao.SysAttachment.Ctx(ctx).Where("id", in.Id).Order("sort desc").Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
}
res.Sort = res.Sort + 10
return &res, nil
}
// View 获取指定字典类型信息
func (s *sSysAttachment) View(ctx context.Context, in sysin.AttachmentViewInp) (res *sysin.AttachmentViewModel, err error) {
if err = dao.SysAttachment.Ctx(ctx).Where("id", in.Id).Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
return res, nil
}
// List 获取列表
func (s *sSysAttachment) List(ctx context.Context, in sysin.AttachmentListInp) (list []*sysin.AttachmentListModel, totalCount int, err error) {
mod := dao.SysAttachment.Ctx(ctx)
// 访问路径
if in.MemberId > 0 {
mod = mod.Where("member_id", in.MemberId)
}
// 模块
if in.Drive != "" {
mod = mod.Where("drive", in.Drive)
}
// 请求方式
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("id desc").Scan(&list); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
for _, v := range list {
v.SizeFormat = format.FileSize(v.Size)
v.FileUrl = service.CommonUpload().LastUrl(ctx, v.FileUrl, consts.UploadDriveLocal)
}
return list, totalCount, err
}
// Add 新增附件
func (s *sSysAttachment) Add(ctx context.Context, meta *sysin.UploadFileMeta, fullPath, drive string) (data *entity.SysAttachment, err error) {
var (
c = contexts.Get(ctx)
user = c.User
memberId int64 = 0
)
if user != nil {
memberId = user.Id
}
models := &entity.SysAttachment{
Id: 0,
AppId: c.Module,
MemberId: memberId,
Drive: drive,
Size: meta.Size,
Path: fullPath,
FileUrl: fullPath,
Name: meta.Filename,
Kind: meta.Kind,
MetaType: meta.MetaType,
NaiveType: meta.NaiveType,
Ext: meta.Ext,
Md5: meta.Md5,
Status: consts.StatusEnabled,
CreatedAt: gtime.Now(),
UpdatedAt: gtime.Now(),
}
id, err := dao.SysAttachment.Ctx(ctx).Data(models).InsertAndGetId()
if err != nil {
return nil, gerror.Wrap(err, consts.ErrorORM)
}
models.Id = id
return models, nil
}

View File

@@ -0,0 +1,150 @@
// Package sys
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 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/os/gtime"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"hotgo/utility/convert"
)
type sSysBlacklist struct{}
func NewSysBlacklist() *sSysBlacklist {
return &sSysBlacklist{}
}
func init() {
service.RegisterSysBlacklist(NewSysBlacklist())
}
// Delete 删除
func (s *sSysBlacklist) Delete(ctx context.Context, in sysin.BlacklistDeleteInp) error {
_, err := dao.SysBlacklist.Ctx(ctx).Where("id", in.Id).Delete()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// Edit 修改/新增
func (s *sSysBlacklist) Edit(ctx context.Context, in sysin.BlacklistEditInp) (err error) {
if in.Ip == "" {
err = gerror.New("ip不能为空")
return err
}
// 修改
in.UpdatedAt = gtime.Now()
if in.Id > 0 {
_, err = dao.SysBlacklist.Ctx(ctx).Where("id", in.Id).Data(in).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// 新增
in.CreatedAt = gtime.Now()
_, err = dao.SysBlacklist.Ctx(ctx).Data(in).Insert()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// Status 更新部门状态
func (s *sSysBlacklist) Status(ctx context.Context, in sysin.BlacklistStatusInp) (err error) {
if in.Id <= 0 {
err = gerror.New("ID不能为空")
return err
}
if in.Status <= 0 {
err = gerror.New("状态不能为空")
return err
}
if !convert.InSliceInt(consts.StatusMap, in.Status) {
err = gerror.New("状态不正确")
return err
}
// 修改
in.UpdatedAt = gtime.Now()
_, err = dao.SysBlacklist.Ctx(ctx).Where("id", in.Id).Data("status", in.Status).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// MaxSort 最大排序
func (s *sSysBlacklist) MaxSort(ctx context.Context, in sysin.BlacklistMaxSortInp) (*sysin.BlacklistMaxSortModel, error) {
var res sysin.BlacklistMaxSortModel
if in.Id > 0 {
if err := dao.SysBlacklist.Ctx(ctx).Where("id", in.Id).Order("sort desc").Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
}
res.Sort = res.Sort + 10
return &res, nil
}
// View 获取指定字典类型信息
func (s *sSysBlacklist) View(ctx context.Context, in sysin.BlacklistViewInp) (res *sysin.BlacklistViewModel, err error) {
if err = dao.SysBlacklist.Ctx(ctx).Where("id", in.Id).Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
return res, nil
}
// List 获取列表
func (s *sSysBlacklist) List(ctx context.Context, in sysin.BlacklistListInp) (list []*sysin.BlacklistListModel, totalCount int, err error) {
mod := dao.SysBlacklist.Ctx(ctx)
// 访问路径
if in.Ip != "" {
mod = mod.Where("ip", in.Ip)
}
// 请求方式
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("id desc").Scan(&list); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
return list, totalCount, err
}

View File

@@ -0,0 +1,165 @@
// Package sys
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 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/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/model"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
type sSysConfig struct{}
func NewSysConfig() *sSysConfig {
return &sSysConfig{}
}
func init() {
service.RegisterSysConfig(NewSysConfig())
}
// 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 nil, err
}
if err = gconv.Struct(models.List, &conf); err != nil {
return nil, err
}
conf.Addr = fmt.Sprintf("%s:%d", conf.Host, conf.Port)
return conf, nil
}
// GetConfigByGroup 获取指定分组的配置
func (s *sSysConfig) GetConfigByGroup(ctx context.Context, in sysin.GetConfigInp) (*sysin.GetConfigModel, error) {
if in.Group == "" {
return nil, gerror.New("分组不能为空")
}
var (
mod = dao.SysConfig.Ctx(ctx)
models []*entity.SysConfig
res sysin.GetConfigModel
)
if err := mod.Fields("key", "value", "type").Where("group", in.Group).Scan(&models); err != nil {
return nil, err
}
isDemo, _ := g.Cfg().Get(ctx, "hotgo.isDemo", false)
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
if isDemo.Bool() && (v.Key == "smtpUser" || v.Key == "smtpPass") {
res.List[v.Key] = consts.DemoTips
res.List[v.Key] = consts.DemoTips
}
}
}
return &res, nil
}
// ConversionType 转换类型
func (s *sSysConfig) ConversionType(ctx context.Context, models *entity.SysConfig) (value interface{}, err error) {
if models == nil {
return nil, gerror.New("数据不存在")
}
switch models.Type {
case consts.ConfigTypeInt:
value = gconv.Int64(models.Value)
return
case consts.ConfigTypeBool:
value = gconv.Bool(models.Value)
return
default:
value = gconv.String(models.Value)
}
return value, nil
}
// UpdateConfigByGroup 更新指定分组的配置
func (s *sSysConfig) UpdateConfigByGroup(ctx context.Context, in sysin.UpdateConfigInp) error {
if in.Group == "" {
return gerror.New("分组不能为空")
}
var (
mod = dao.SysConfig.Ctx(ctx)
models []*entity.SysConfig
)
if err := mod.Where("group", in.Group).Scan(&models); err != nil {
return err
}
err := dao.SysConfig.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
for k, v := range in.List {
row := s.getConfigByKey(k, models)
// 新增
if row == nil {
//row.Id = 0
//row.Key = k
//row.Value = gconv.String(v)
//row.Group = in.Group
//row.Status = consts.StatusEnabled
//row.CreatedAt = gtime.Now()
//row.UpdatedAt = gtime.Now()
//_, err := dao.SysConfig.Ctx(ctx).Data(row).Insert()
//if err != nil {
// err = gerror.Wrap(err, consts.ErrorORM)
// return err
//}
//continue
return gerror.Newf("暂不支持从前台添加变量,请从数据库中添加变量:%v", k)
}
// 更新
_, err := dao.SysConfig.Ctx(ctx).Where("id", row.Id).Data(g.Map{"value": v, "updated_at": gtime.Now()}).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
}
return nil
})
if err != nil {
return err
}
return nil
}
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
}

View File

@@ -0,0 +1,183 @@
// Package sys
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 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/os/gtime"
"hotgo/internal/consts"
"hotgo/internal/crons"
"hotgo/internal/dao"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"hotgo/utility/convert"
)
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 {
g.Log().Fatalf(ctx, "定时任务获取失败, err . %v", err)
return
}
if err := crons.StartALL(list); err != nil {
g.Log().Fatalf(ctx, "定时任务启动失败, err . %v", err)
return
}
}
// Delete 删除
func (s *sSysCron) Delete(ctx context.Context, in sysin.CronDeleteInp) error {
_, err := dao.SysCron.Ctx(ctx).Where("id", in.Id).Delete()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// Edit 修改/新增
func (s *sSysCron) Edit(ctx context.Context, in sysin.CronEditInp) (err error) {
if in.Name == "" {
err = gerror.New("标题不能为空")
return err
}
// 修改
in.UpdatedAt = gtime.Now()
if in.Id > 0 {
_, err = dao.SysCron.Ctx(ctx).Where("id", in.Id).Data(in).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// 新增
in.CreatedAt = gtime.Now()
_, err = dao.SysCron.Ctx(ctx).Data(in).Insert()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// Status 更新部门状态
func (s *sSysCron) Status(ctx context.Context, in sysin.CronStatusInp) (err error) {
if in.Id <= 0 {
err = gerror.New("ID不能为空")
return err
}
if in.Status <= 0 {
err = gerror.New("状态不能为空")
return err
}
if !convert.InSliceInt(consts.StatusMap, in.Status) {
err = gerror.New("状态不正确")
return err
}
// 修改
in.UpdatedAt = gtime.Now()
_, err = dao.SysCron.Ctx(ctx).Where("id", in.Id).Data("status", in.Status).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// MaxSort 最大排序
func (s *sSysCron) MaxSort(ctx context.Context, in sysin.CronMaxSortInp) (*sysin.CronMaxSortModel, error) {
var res sysin.CronMaxSortModel
if in.Id > 0 {
if err := dao.SysCron.Ctx(ctx).Where("id", in.Id).Order("sort desc").Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
}
res.Sort = res.Sort + 10
return &res, nil
}
// 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 {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
return res, nil
}
// 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.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("id desc").Scan(&list); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
for _, v := range list {
v.GroupName, _ = dao.SysCronGroup.GetName(ctx, v.GroupId)
}
return list, totalCount, err
}

View File

@@ -0,0 +1,187 @@
// Package sys
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 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/os/gtime"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"hotgo/utility/convert"
"hotgo/utility/tree"
)
type sSysCronGroup struct{}
func NewSysCronGroup() *sSysCronGroup {
return &sSysCronGroup{}
}
func init() {
service.RegisterSysCronGroup(NewSysCronGroup())
}
// Delete 删除
func (s *sSysCronGroup) Delete(ctx context.Context, in sysin.CronGroupDeleteInp) error {
_, err := dao.SysCronGroup.Ctx(ctx).Where("id", in.Id).Delete()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// Edit 修改/新增
func (s *sSysCronGroup) Edit(ctx context.Context, in sysin.CronGroupEditInp) (err error) {
if in.Name == "" {
err = gerror.New("分组名称不能为空")
return err
}
// 修改
in.UpdatedAt = gtime.Now()
if in.Id > 0 {
_, err = dao.SysCronGroup.Ctx(ctx).Where("id", in.Id).Data(in).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// 新增
in.CreatedAt = gtime.Now()
_, err = dao.SysCronGroup.Ctx(ctx).Data(in).Insert()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// Status 更新部门状态
func (s *sSysCronGroup) Status(ctx context.Context, in sysin.CronGroupStatusInp) (err error) {
if in.Id <= 0 {
err = gerror.New("ID不能为空")
return err
}
if in.Status <= 0 {
err = gerror.New("状态不能为空")
return err
}
if !convert.InSliceInt(consts.StatusMap, in.Status) {
err = gerror.New("状态不正确")
return err
}
// 修改
in.UpdatedAt = gtime.Now()
_, err = dao.SysCronGroup.Ctx(ctx).Where("id", in.Id).Data("status", in.Status).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// MaxSort 最大排序
func (s *sSysCronGroup) MaxSort(ctx context.Context, in sysin.CronGroupMaxSortInp) (*sysin.CronGroupMaxSortModel, error) {
var res sysin.CronGroupMaxSortModel
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 nil, err
}
}
res.Sort = res.Sort + 10
return &res, nil
}
// 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 nil, err
}
return res, nil
}
// 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 list, totalCount, err
}
if totalCount == 0 {
return list, totalCount, nil
}
if err = mod.Page(in.Page, in.PerPage).Order("id desc").Scan(&list); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
return list, totalCount, err
}
// Select 选项
func (s *sSysCronGroup) Select(ctx context.Context, in sysin.CronGroupSelectInp) (list sysin.CronGroupSelectModel, err error) {
var (
mod = dao.SysCronGroup.Ctx(ctx)
models []*entity.SysCronGroup
typeList []g.Map
)
if err = mod.Order("pid asc,sort asc").Scan(&models); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, err
}
for i := 0; i < len(models); i++ {
typeList = append(typeList, g.Map{
"index": models[i].Id,
"key": models[i].Id,
"label": models[i].Name,
"id": models[i].Id,
"pid": models[i].Pid,
"name": models[i].Name,
"sort": models[i].Sort,
"created_at": models[i].CreatedAt,
"status": models[i].Status,
})
}
return tree.GenTree(typeList), nil
}

View File

@@ -0,0 +1,130 @@
// Package sys
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 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/os/gtime"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
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.Label == "" {
err = gerror.New("字典标签不能为空")
return err
}
// 修改
in.UpdatedAt = gtime.Now()
if in.Id > 0 {
_, err = dao.SysDictData.Ctx(ctx).Where("id", in.Id).Data(in).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// 新增
in.CreatedAt = gtime.Now()
if in.TypeID <= 0 {
err = gerror.New("字典类型不能为空")
return err
}
in.Type, err = dao.SysDictType.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).Data(in).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 := dao.SysDictType.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.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("id desc").Scan(&list); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
for _, v := range list {
v.TypeID, _ = dao.SysDictType.GetId(ctx, v.Type)
}
return list, totalCount, err
}

View File

@@ -0,0 +1,173 @@
// Package sys
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 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/os/gtime"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"hotgo/utility/tree"
)
type sSysDictType struct{}
func NewSysDictType() *sSysDictType {
return &sSysDictType{}
}
func init() {
service.RegisterSysDictType(NewSysDictType())
}
// Tree 树
func (s *sSysDictType) Tree(ctx context.Context) (list []g.Map, 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, err
}
for i := 0; i < len(models); i++ {
list = append(list, g.Map{
"index": models[i].Id,
"key": models[i].Id,
"label": models[i].Name,
"id": models[i].Id,
"pid": models[i].Pid,
"name": models[i].Name,
"type": models[i].Type,
"sort": models[i].Sort,
"remark": models[i].Remark,
"status": models[i].Status,
"updated_at": models[i].UpdatedAt,
"created_at": models[i].CreatedAt,
})
}
return tree.GenTree(list), nil
}
// Delete 删除
func (s *sSysDictType) Delete(ctx context.Context, in sysin.DictTypeDeleteInp) error {
var (
models *entity.SysDictType
)
err := dao.SysDictType.Ctx(ctx).Where("id", in.Id).Scan(&models)
if err != nil {
return err
}
if models.Id < 1 {
return gerror.New("数据不存在或已删除!")
}
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() {
return gerror.New("请先删除该字典类型下得所有字典数据!")
}
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() {
return gerror.New("请先删除该字典类型下得所有子级类型!")
}
_, err = dao.SysDictType.Ctx(ctx).Where("id", in.Id).Delete()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// Edit 修改/新增
func (s *sSysDictType) Edit(ctx context.Context, in sysin.DictTypeEditInp) (err error) {
if in.Name == "" {
err = gerror.New("名称不能为空")
return err
}
uniqueName, err := dao.SysDictType.IsUniqueType(ctx, in.Id, in.Name)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
if !uniqueName {
err = gerror.New("名称已存在")
return err
}
// 修改
in.UpdatedAt = gtime.Now()
if in.Id > 0 {
_, err = dao.SysDictType.Ctx(ctx).Where("id", in.Id).Data(in).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// 新增
in.CreatedAt = gtime.Now()
_, err = dao.SysDictType.Ctx(ctx).Data(in).Insert()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// Select 选项
func (s *sSysDictType) Select(ctx context.Context, in sysin.DictTypeSelectInp) (list sysin.DictTypeSelectModel, err error) {
var (
mod = dao.SysDictType.Ctx(ctx)
models []*entity.SysDictType
typeList []g.Map
)
if err = mod.Order("pid asc,sort asc").Scan(&models); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, err
}
for i := 0; i < len(models); i++ {
typeList = append(typeList, g.Map{
"index": models[i].Id,
"key": models[i].Id,
"label": models[i].Name,
"id": models[i].Id,
"pid": models[i].Pid,
"name": models[i].Name,
"sort": models[i].Sort,
"created_at": models[i].CreatedAt,
"status": models[i].Status,
})
}
return tree.GenTree(typeList), nil
}

View File

@@ -0,0 +1,407 @@
// Package sys
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
//
package sys
import (
"context"
"encoding/json"
"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"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/library/contexts"
"hotgo/internal/library/location"
"hotgo/internal/library/queue"
"hotgo/internal/model"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"hotgo/utility/excel"
"hotgo/utility/validate"
"time"
)
type sSysLog struct{}
func NewSysLog() *sSysLog {
return &sSysLog{}
}
func init() {
service.RegisterSysLog(NewSysLog())
}
// 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:"member_name"`
Region string `json:"region"`
}
var (
titleList = []string{"ID", "应用", "提交类型", "模块", "提交url", "ip地址", "报错code", "报错信息", "对外id", "请求耗时", "创建时间", "用户", "访问地"}
fileName = "全局日志导出-" + gctx.CtxId(ctx) + ".xlsx"
sheetName = "HotGo"
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)
}
// 强转类型
writer := ghttp.RequestFromCtx(ctx).Response.Writer
w, _ := interface{}(writer).(*ghttp.ResponseWriter)
if err = excel.ExportByStruct(w, titleList, gconv.Interfaces(exportList), fileName, sheetName); err != nil {
err = gerror.Wrap(err, "ExportByStruct:")
return err
}
// 加入到上下文
contexts.SetResponse(ctx, &model.Response{
Code: consts.CodeOK,
Message: "导出成功",
Timestamp: time.Now().Unix(),
TraceID: gctx.CtxId(ctx),
})
return
}
// RealWrite 真实写入
func (s *sSysLog) RealWrite(ctx context.Context, commonLog entity.SysLog) error {
result, err := dao.SysLog.Ctx(ctx).Data(commonLog).Insert()
if err != nil {
return err
}
if _, err := result.LastInsertId(); err != nil {
return err
}
return nil
}
// AutoLog 根据配置自动记录请求日志
func (s *sSysLog) AutoLog(ctx context.Context) (err error) {
// 日志开关
logSwitch, _ := g.Cfg().Get(ctx, "hotgo.log.switch", true)
if !logSwitch.Bool() {
return nil
}
data := s.AnalysisLog(ctx)
// 判断模块是否需要记录
module, _ := g.Cfg().Get(ctx, "hotgo.log.module", nil)
if module == nil {
return nil
}
if exist := validate.InSliceExistStr(module.Strings(), data.Module); !exist {
return nil
}
// 判断状态码是否需要记录
code, _ := g.Cfg().Get(ctx, "hotgo.log.skipCode", nil)
if code != nil {
if exist := validate.InSliceExistStr(code.Strings(), gconv.String(data.ErrorCode)); exist {
return nil
}
}
// 是否开启队列
queueSwitch, _ := g.Cfg().Get(ctx, "hotgo.log.queue", true)
if queueSwitch.Bool() {
// 获取生产者实例
queueInstance, err := queue.InstanceProducer()
if err != nil {
queue.FatalLog(ctx, "InstanceProducer异常", err)
return err
}
// 生产消息
mqMsg, err := queueInstance.SendMsg(consts.QueueLogTopic, gconv.String(data))
// 记录生产日志
queue.ProducerLog(ctx, consts.QueueLogTopic, mqMsg.MsgId, err)
return err
}
return s.RealWrite(ctx, data)
}
// QueueJob 队列消费
func (s *sSysLog) QueueJob(ctx context.Context, mqMsg queue.MqMsg) (err error) {
var data entity.SysLog
if err = json.Unmarshal(mqMsg.Body, &data); err != nil {
return err
}
return s.RealWrite(ctx, data)
}
// AnalysisLog 解析日志数据
func (s *sSysLog) AnalysisLog(ctx context.Context) entity.SysLog {
var (
modelContext = contexts.Get(ctx)
response = modelContext.Response
user = modelContext.User
request = ghttp.RequestFromCtx(ctx)
module = modelContext.Module
clientIp = request.GetClientIp()
locationData = location.GetLocation(ctx, clientIp)
postData = "{}"
getData = "{}"
headerData = "{}"
data = entity.SysLog{}
memberId int64 = 0
errorCode = 0
errorMsg = ""
errorData = "{}"
traceID = ""
timestamp int64 = 0
appId = ""
)
// 响应数据
if response != nil {
errorCode = response.Code
errorMsg = response.Message
traceID = response.TraceID
timestamp = response.Timestamp
if len(gconv.String(response.Error)) > 0 {
errorData = gconv.String(response.Error)
}
}
// 请求头
if reqHeadersBytes, _ := json.Marshal(request.Header); len(gconv.String(reqHeadersBytes)) > 0 {
headerData = gconv.String(reqHeadersBytes)
}
// post参数
if gconv.String(request.PostForm) != "" {
postData = gconv.String(request.PostForm)
}
// get参数
if len(request.URL.Query()) > 0 {
getData = gconv.String(request.URL.Query())
}
// 当前登录用户
if user != nil {
memberId = user.Id
appId = user.App
}
data = entity.SysLog{
AppId: appId,
MerchantId: 0,
MemberId: memberId,
Method: request.Method,
Module: module,
Url: request.RequestURI,
GetData: getData,
PostData: postData,
HeaderData: headerData,
Ip: clientIp,
ProvinceId: locationData.ProvinceCode,
CityId: locationData.CityCode,
ErrorCode: errorCode,
ErrorMsg: errorMsg,
ErrorData: errorData,
ReqId: traceID,
Timestamp: timestamp,
UserAgent: request.Header.Get("User-Agent"),
Status: consts.StatusEnabled,
TakeUpTime: modelContext.TakeUpTime,
}
return data
}
// View 获取指定字典类型信息
func (s *sSysLog) View(ctx context.Context, in sysin.LogViewInp) (res *sysin.LogViewModel, err error) {
if err = dao.SysLog.Ctx(ctx).Where("id", in.Id).Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
isDemo, _ := g.Cfg().Get(ctx, "hotgo.isDemo", false)
if isDemo.Bool() {
res.HeaderData = `{
"none": [
"` + consts.DemoTips + `"
]
}`
}
return res, nil
}
// Delete 删除
func (s *sSysLog) Delete(ctx context.Context, in sysin.LogDeleteInp) error {
if _, err := dao.SysLog.Ctx(ctx).Where("id", in.Id).Delete(); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// List 列表
func (s *sSysLog) List(ctx context.Context, in sysin.LogListInp) (list []*sysin.LogListModel, totalCount int, err error) {
mod := dao.SysLog.Ctx(ctx)
// 访问路径
if in.Url != "" {
mod = mod.WhereLike("url", "%"+in.Url+"%")
}
// 模块
if in.Module != "" {
mod = mod.Where("module", in.Module)
}
// 请求方式
if in.Method != "" {
mod = mod.Where("method", in.Method)
}
// 用户
if in.MemberId > 0 {
mod = mod.Where("member_id", in.MemberId)
}
// 访问IP
if in.Ip != "" {
mod = mod.Where("ip", in.Ip)
}
// 日期范围
if in.StartTime != "" {
mod = mod.WhereGTE("created_at", in.StartTime)
}
if in.EndTime != "" {
mod = mod.WhereLTE("created_at", in.EndTime)
}
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 in.TakeUpTime > 0 {
mod = mod.WhereGTE("take_up_time", in.TakeUpTime)
}
totalCount, err = mod.Count()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
if totalCount == 0 {
return list, totalCount, err
}
if err = mod.Page(in.Page, in.PerPage).Order("id desc").Scan(&list); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
isDemo, _ := g.Cfg().Get(ctx, "hotgo.isDemo", false)
for i := 0; i < len(list); i++ {
// 管理员
if list[i].AppId == consts.AppAdmin {
memberName, err := dao.AdminMember.Ctx(ctx).Fields("realname").Where("id", list[i].MemberId).Value()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
list[i].MemberName = memberName.String()
}
// 接口
if list[i].AppId == consts.AppApi {
//memberName, err = dao.Member.Ctx(ctx).Fields("realname").Where("id", res.List[i].MemberId).Value()
//if err != nil {
// err = gerror.Wrap(err, consts.ErrorORM)
// return nil, err
//}
}
if list[i].MemberName == "" {
list[i].MemberName = "游客"
}
//// 获取省市编码对应的地区名称
//region, err := dao.SysProvinces.GetRegion(ctx, list[i].ProvinceId, list[i].CityId)
//if err != nil {
// return list, totalCount, err
//}
//list[i].Region = region
// 截取请求url路径
if gstr.Contains(list[i].Url, "?") {
list[i].Url = gstr.StrTillEx(list[i].Url, "?")
}
if isDemo.Bool() {
list[i].HeaderData = `{
"none": [
"` + consts.DemoTips + `"
]
}`
}
}
return list, totalCount, err
}

View File

@@ -0,0 +1,155 @@
// Package sys
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2022 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/os/gtime"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"hotgo/utility/convert"
)
type sSysProvinces struct{}
func NewSysProvinces() *sSysProvinces {
return &sSysProvinces{}
}
func init() {
service.RegisterSysProvinces(NewSysProvinces())
}
// Delete 删除
func (s *sSysProvinces) Delete(ctx context.Context, in sysin.ProvincesDeleteInp) error {
_, err := dao.SysProvinces.Ctx(ctx).Where("id", in.Id).Delete()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// Edit 修改/新增
func (s *sSysProvinces) Edit(ctx context.Context, in sysin.ProvincesEditInp) (err error) {
if in.Title == "" {
err = gerror.New("标题不能为空")
return err
}
// 修改
in.UpdatedAt = gtime.Now()
if in.Id > 0 {
_, err = dao.SysProvinces.Ctx(ctx).Where("id", in.Id).Data(in).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// 新增
in.CreatedAt = gtime.Now()
_, err = dao.SysProvinces.Ctx(ctx).Data(in).Insert()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// Status 更新部门状态
func (s *sSysProvinces) Status(ctx context.Context, in sysin.ProvincesStatusInp) (err error) {
if in.Id <= 0 {
err = gerror.New("ID不能为空")
return err
}
if in.Status <= 0 {
err = gerror.New("状态不能为空")
return err
}
if !convert.InSliceInt(consts.StatusMap, in.Status) {
err = gerror.New("状态不正确")
return err
}
// 修改
in.UpdatedAt = gtime.Now()
_, err = dao.SysProvinces.Ctx(ctx).Where("id", in.Id).Data("status", in.Status).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// MaxSort 最大排序
func (s *sSysProvinces) MaxSort(ctx context.Context, in sysin.ProvincesMaxSortInp) (*sysin.ProvincesMaxSortModel, error) {
var res sysin.ProvincesMaxSortModel
if in.Id > 0 {
if err := dao.SysProvinces.Ctx(ctx).Where("id", in.Id).Order("sort desc").Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
}
res.Sort = res.Sort + 10
return &res, nil
}
// 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, consts.ErrorORM)
return nil, err
}
return res, nil
}
// 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, consts.ErrorORM)
return list, totalCount, err
}
if totalCount == 0 {
return list, totalCount, nil
}
if err = mod.Page(in.Page, in.PerPage).Order("id desc").Scan(&list); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
return list, totalCount, err
}