更新2.1.2版本,优化部门、角色权限,增加上下级关系;增加登录、系统、短信日志;优化省市区编码

This commit is contained in:
孟帅
2023-01-25 11:49:21 +08:00
parent 11fad0132d
commit 93e0fe7250
190 changed files with 35896 additions and 7208 deletions

View File

@@ -9,11 +9,11 @@ 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/library/hgorm"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/adminin"
"hotgo/internal/service"
@@ -47,8 +47,30 @@ func (s *sAdminDept) NameUnique(ctx context.Context, in adminin.DeptNameUniqueIn
}
// Delete 删除
func (s *sAdminDept) Delete(ctx context.Context, in adminin.DeptDeleteInp) error {
_, err := dao.AdminDept.Ctx(ctx).Where("id", in.Id).Delete()
func (s *sAdminDept) Delete(ctx context.Context, in adminin.DeptDeleteInp) (err error) {
var (
models *entity.AdminDept
)
err = dao.AdminDept.Ctx(ctx).Where("id", in.Id).Scan(&models)
if err != nil {
return err
}
if models == nil {
return gerror.New("数据不存在或已删除!")
}
pidExist, err := dao.AdminDept.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.AdminDept.Ctx(ctx).Where("id", in.Id).Delete()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
@@ -75,8 +97,12 @@ func (s *sAdminDept) Edit(ctx context.Context, in adminin.DeptEditInp) (err erro
return err
}
in.Pid, in.Level, in.Tree, err = hgorm.GenSubTree(ctx, dao.AdminDept, in.Pid)
if err != nil {
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 {
@@ -88,7 +114,6 @@ func (s *sAdminDept) Edit(ctx context.Context, in adminin.DeptEditInp) (err erro
}
// 新增
in.CreatedAt = gtime.Now()
_, err = dao.AdminDept.Ctx(ctx).Data(in).Insert()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
@@ -156,11 +181,10 @@ func (s *sAdminDept) View(ctx context.Context, in adminin.DeptViewInp) (res *adm
// 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
mod = dao.AdminDept.Ctx(ctx)
models []*entity.AdminDept
ids []int64
pids []int64
)
// 部门名称
@@ -207,25 +231,14 @@ func (s *sAdminDept) List(ctx context.Context, in adminin.DeptListInp) (list adm
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,
})
list = gconv.SliceMap(models)
for k, v := range list {
list[k]["index"] = v["id"]
list[k]["key"] = v["id"]
list[k]["label"] = v["name"]
}
return tree.GenTree(deptList), nil
return tree.GenTree(list), nil
}
type DeptTree struct {

View File

@@ -9,6 +9,7 @@ package admin
import (
"context"
"github.com/gogf/gf/v2/crypto/gmd5"
"github.com/gogf/gf/v2/encoding/gbase64"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
@@ -20,11 +21,13 @@ import (
"hotgo/internal/dao"
"hotgo/internal/library/contexts"
"hotgo/internal/library/jwt"
"hotgo/internal/library/location"
"hotgo/internal/model"
"hotgo/internal/model/do"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/adminin"
"hotgo/internal/service"
"hotgo/utility/encrypt"
"hotgo/utility/tree"
"hotgo/utility/validate"
)
@@ -441,7 +444,12 @@ func (s *sAdminMember) Login(ctx context.Context, in adminin.MemberLoginInp) (re
roleInfo *entity.AdminRole
memberInfo *entity.AdminMember
identity *model.Identity
timestamp = gtime.Timestamp()
expires = g.Cfg().MustGet(ctx, "jwt.expires", 1).Int64()
exp = gconv.Int64(timestamp) + expires
lastIp = location.GetClientIp(ghttp.RequestFromCtx(ctx))
)
err = dao.AdminMember.Ctx(ctx).Where("username", in.Username).Scan(&memberInfo)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
@@ -457,16 +465,21 @@ func (s *sAdminMember) Login(ctx context.Context, in adminin.MemberLoginInp) (re
return
}
if memberInfo.PasswordHash != gmd5.MustEncryptString(in.Password+memberInfo.Salt) {
// 解密密码
password, err := gbase64.Decode([]byte(in.Password))
if err != nil {
return nil, err
}
password, err = encrypt.AesECBDecrypt(password, consts.RequestEncryptKey)
if err != nil {
return nil, err
}
if memberInfo.PasswordHash != gmd5.MustEncryptString(string(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.RoleId).
@@ -485,12 +498,6 @@ func (s *sAdminMember) Login(ctx context.Context, in adminin.MemberLoginInp) (re
return
}
// 有效期
expires := g.Cfg().MustGet(ctx, "jwt.expires", 1).Int64()
// 过期时间戳
exp := gconv.Int64(gtime.Timestamp()) + expires
identity = &model.Identity{
Id: memberInfo.Id,
Pid: memberInfo.Pid,
@@ -504,7 +511,7 @@ func (s *sAdminMember) Login(ctx context.Context, in adminin.MemberLoginInp) (re
Mobile: memberInfo.Mobile,
VisitCount: memberInfo.VisitCount,
LastTime: memberInfo.LastTime,
LastIp: memberInfo.LastIp,
LastIp: lastIp,
Exp: exp,
Expires: expires,
App: consts.AppAdmin,
@@ -517,13 +524,11 @@ func (s *sAdminMember) Login(ctx context.Context, in adminin.MemberLoginInp) (re
}
// 更新登录信息
authKey := gmd5.MustEncryptString(gconv.String(token))
_, err = dao.AdminMember.Ctx(ctx).Data(do.AdminMember{
AuthKey: gmd5.MustEncryptString(authKey),
AuthKey: gmd5.MustEncryptString(token),
VisitCount: memberInfo.VisitCount + 1,
LastTime: gtime.Timestamp(),
LastIp: ghttp.RequestFromCtx(ctx).GetClientIp(),
LastTime: timestamp,
LastIp: lastIp,
}).Where(do.AdminMember{
Id: memberInfo.Id,
}).Update()
@@ -538,7 +543,7 @@ func (s *sAdminMember) Login(ctx context.Context, in adminin.MemberLoginInp) (re
Username: identity.Username,
RealName: identity.RealName,
Avatar: identity.Avatar,
Token: gconv.String(token),
Token: token,
}
return res, nil

View File

@@ -78,7 +78,6 @@ func (s *sAdminPost) Edit(ctx context.Context, in adminin.PostEditInp) (err erro
}
// 修改
in.UpdatedAt = gtime.Now()
if in.Id > 0 {
_, err = dao.AdminPost.Ctx(ctx).Where("id", in.Id).Data(in).Update()
if err != nil {
@@ -90,7 +89,6 @@ func (s *sAdminPost) Edit(ctx context.Context, in adminin.PostEditInp) (err erro
}
// 新增
in.CreatedAt = gtime.Now()
_, err = dao.AdminPost.Ctx(ctx).Data(in).Insert()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
@@ -181,7 +179,7 @@ func (s *sAdminPost) List(ctx context.Context, in adminin.PostListInp) (list []*
return list, totalCount, nil
}
if err = mod.Page(in.Page, in.PerPage).Order("id desc").Scan(&list); err != nil {
if err = mod.Page(in.Page, in.PerPage).Order("id asc").Scan(&list); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}

View File

@@ -12,7 +12,7 @@ import (
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
"hotgo/api/backend/role"
"hotgo/internal/consts"
"hotgo/internal/dao"
@@ -25,6 +25,7 @@ import (
"hotgo/internal/service"
"hotgo/utility/auth"
"hotgo/utility/convert"
"hotgo/utility/tree"
"sort"
)
@@ -67,21 +68,25 @@ func (s *sAdminRole) Verify(ctx context.Context, path, method string) bool {
}
// List 获取列表
func (s *sAdminRole) List(ctx context.Context, in adminin.RoleListInp) (list []*adminin.RoleListModel, totalCount int, err error) {
mod := dao.AdminRole.Ctx(ctx)
func (s *sAdminRole) List(ctx context.Context, in adminin.RoleListInp) (list []g.Map, totalCount int, err error) {
var (
mod = dao.AdminRole.Ctx(ctx)
models []*adminin.RoleListModel
)
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)
err = mod.Page(in.Page, in.PerPage).Order("id asc").Scan(&models)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
return list, totalCount, err
return tree.GenTree(gconv.SliceMap(models)), totalCount, err
}
// GetName 获取指定角色的名称
@@ -197,7 +202,6 @@ func (s *sAdminRole) Edit(ctx context.Context, in *role.EditReq) (err error) {
}
// 修改
in.UpdatedAt = gtime.Now()
if in.Id > 0 {
_, err = dao.AdminRole.Ctx(ctx).Where("id", in.Id).Data(in).Update()
if err != nil {
@@ -209,7 +213,6 @@ func (s *sAdminRole) Edit(ctx context.Context, in *role.EditReq) (err error) {
}
// 新增
in.CreatedAt = gtime.Now()
_, err = dao.AdminRole.Ctx(ctx).Data(in).Insert()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
@@ -222,6 +225,28 @@ func (s *sAdminRole) Delete(ctx context.Context, in *role.DeleteReq) (err error)
if in.Id <= 0 {
return gerror.New("ID不正确")
}
var (
models *entity.AdminRole
)
err = dao.AdminRole.Ctx(ctx).Where("id", in.Id).Scan(&models)
if err != nil {
return err
}
if models == nil {
return gerror.New("数据不存在或已删除!")
}
pidExist, err := dao.AdminRole.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.AdminRole.Ctx(ctx).Where("id", in.Id).Delete()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)

View File

@@ -14,7 +14,7 @@ import (
"hotgo/internal/service"
)
// GlobalLog 全局日志
// GlobalLog 访问日志
func (s *sHook) GlobalLog(r *ghttp.Request) {
var (
ctx = r.Context()

View File

@@ -37,7 +37,8 @@ func New() *sMiddleware {
return &sMiddleware{
LoginUrl: "/common",
DemoWhiteList: g.Map{
"/admin/site/login": struct{}{}, // 后台登录
"/admin/site/login": struct{}{}, // 后台登录
"/admin/genCodes/preview": struct{}{}, // 预览代码
},
}
}

View File

@@ -0,0 +1,23 @@
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/global"
"hotgo/internal/library/location"
"hotgo/internal/library/response"
)
// Blacklist IP黑名单限制中间件
func (s *sMiddleware) Blacklist(r *ghttp.Request) {
if global.Blacklists != nil {
if _, ok := global.Blacklists[location.GetClientIp(r)]; ok {
response.JsonExit(r, gcode.CodeServerBusy.Code(), "请求异常,已被封禁,如有疑问请联系管理员!")
}
} else {
g.Log().Warningf(r.Context(), "blacklists uninitialized")
}
r.Middleware.Next()
}

View File

@@ -42,14 +42,14 @@ func (s *sMiddleware) ResponseHandler(r *ghttp.Request) {
if err = r.GetError(); err != nil {
// 记录到自定义错误日志文件
g.Log("exception").Print(ctx, "exception:", err)
g.Log().Warningf(ctx, "exception:%v", err)
code = gerror.Code(err).Code()
message = err.Error()
// 是否输出错误到页面
if g.Cfg().MustGet(ctx, "hotgo.debug", true).Bool() {
data = charset.GetStack(err)
data = charset.ParseErrStack(err)
}
} else {
data = r.GetHandlerResponse()

View File

@@ -9,9 +9,13 @@ 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"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/global"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"hotgo/utility/validate"
@@ -28,8 +32,9 @@ func init() {
}
// Delete 删除
func (s *sSysBlacklist) Delete(ctx context.Context, in sysin.BlacklistDeleteInp) error {
_, err := dao.SysBlacklist.Ctx(ctx).Where("id", in.Id).Delete()
func (s *sSysBlacklist) Delete(ctx context.Context, in sysin.BlacklistDeleteInp) (err error) {
defer s.VariableLoad(ctx, err)
_, err = dao.SysBlacklist.Ctx(ctx).Where("id", in.Id).Delete()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
@@ -40,6 +45,7 @@ func (s *sSysBlacklist) Delete(ctx context.Context, in sysin.BlacklistDeleteInp)
// Edit 修改/新增
func (s *sSysBlacklist) Edit(ctx context.Context, in sysin.BlacklistEditInp) (err error) {
defer s.VariableLoad(ctx, err)
if in.Ip == "" {
err = gerror.New("ip不能为空")
return err
@@ -69,6 +75,7 @@ func (s *sSysBlacklist) Edit(ctx context.Context, in sysin.BlacklistEditInp) (er
// Status 更新部门状态
func (s *sSysBlacklist) Status(ctx context.Context, in sysin.BlacklistStatusInp) (err error) {
defer s.VariableLoad(ctx, err)
if in.Id <= 0 {
err = gerror.New("ID不能为空")
return err
@@ -148,3 +155,112 @@ func (s *sSysBlacklist) List(ctx context.Context, in sysin.BlacklistListInp) (li
return list, totalCount, err
}
// VariableLoad 变化加载
func (s *sSysBlacklist) VariableLoad(ctx context.Context, err error) {
if err == nil {
s.Load(ctx)
}
}
// Load 加载黑名单
func (s *sSysBlacklist) Load(ctx context.Context) {
global.Blacklists = make(map[string]struct{})
array, err := dao.SysBlacklist.Ctx(ctx).
Fields(dao.SysBlacklist.Columns().Ip).
Where(dao.SysBlacklist.Columns().Status, consts.StatusEnabled).
Array()
if err != nil {
g.Log().Fatal(ctx, "load blacklist fail%+v", err)
return
}
matchStrategy := func(originIp string) {
// 多个IP
if gstr.Contains(originIp, ",") {
ips := gstr.Explode(",", originIp)
if len(ips) > 0 {
for _, ip := range ips {
if !validate.IsIp(ip) {
continue
}
global.Blacklists[ip] = struct{}{}
}
}
return
}
// IP段
if gstr.Contains(originIp, "/24") {
segment := gstr.Replace(originIp, "/24", "")
if !validate.IsIp(segment) {
return
}
var (
start = gstr.Explode(".", segment)
prefix = gstr.Implode(".", start[:len(start)-1]) + "."
index = gconv.Int(start[len(start)-1])
)
if index < 1 {
index = 1
}
for i := index; i <= 254; i++ {
global.Blacklists[prefix+gconv.String(i)] = struct{}{}
}
return
}
// IP范围
if gstr.Contains(originIp, "-") {
originIps := gstr.Explode("-", originIp)
if len(originIps) != 2 {
return
}
if !validate.IsIp(originIps[0]) || !validate.IsIp(originIps[1]) {
return
}
var (
start = gstr.Explode(".", originIps[0])
prefix = gstr.Implode(".", start[:len(start)-1]) + "."
startIndex = gconv.Int(gstr.SubStrFromREx(originIps[0], "."))
endIndex = gconv.Int(gstr.SubStrFromREx(originIps[1], "."))
)
if startIndex >= endIndex {
global.Blacklists[originIps[0]] = struct{}{}
return
}
if startIndex < 1 {
startIndex = 1
}
if endIndex > 254 {
endIndex = 254
}
for i := startIndex; i <= endIndex; i++ {
global.Blacklists[prefix+gconv.String(i)] = struct{}{}
}
return
}
// 指定IP
if validate.IsIp(originIp) {
global.Blacklists[originIp] = struct{}{}
return
}
}
for _, v := range array {
matchStrategy(v.String())
}
}

View File

@@ -112,6 +112,14 @@ func (s *sSysConfig) GetLoadLog(ctx context.Context) (conf *model.LogConfig, err
return conf, nil
}
// GetLoadServeLog 获取本地服务日志配置
func (s *sSysConfig) GetLoadServeLog(ctx context.Context) (conf *model.ServeLogConfig, err error) {
if err = g.Cfg().MustGet(ctx, "hotgo.serveLog").Struct(&conf); err != nil {
return nil, err
}
return conf, nil
}
// GetConfigByGroup 获取指定分组的配置
func (s *sSysConfig) GetConfigByGroup(ctx context.Context, in sysin.GetConfigInp) (*sysin.GetConfigModel, error) {
if in.Group == "" {

View File

@@ -10,6 +10,7 @@ import (
"context"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/os/gtime"
"hotgo/internal/consts"
"hotgo/internal/crons"
@@ -18,6 +19,7 @@ import (
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"hotgo/utility/validate"
"strings"
)
type sSysCron struct{}
@@ -181,3 +183,20 @@ func (s *sSysCron) List(ctx context.Context, in sysin.CronListInp) (list []*sysi
return list, totalCount, err
}
// OnlineExec 在线执行
func (s *sSysCron) OnlineExec(ctx context.Context, in sysin.OnlineExecInp) (err error) {
var data *entity.SysCron
err = dao.SysCron.Ctx(ctx).Where(dao.SysCron.Columns().Id, in.Id).Scan(&data)
if err != nil {
return
}
if data == nil {
return gerror.New("定时任务不存在")
}
newCtx := context.WithValue(gctx.New(), consts.ContextKeyCronArgs, strings.Split(data.Params, consts.CronSplitStr))
return crons.Once(newCtx, data)
}

View File

@@ -63,7 +63,6 @@ func (s *sSysDictType) Tree(ctx context.Context) (list []g.Map, err error) {
// Delete 删除
func (s *sSysDictType) Delete(ctx context.Context, in sysin.DictTypeDeleteInp) error {
var (
models *entity.SysDictType
)
@@ -72,7 +71,7 @@ func (s *sSysDictType) Delete(ctx context.Context, in sysin.DictTypeDeleteInp) e
return err
}
if models.Id < 1 {
if models == nil {
return gerror.New("数据不存在或已删除!")
}

View File

@@ -60,7 +60,7 @@ func (s *sSysLog) Export(ctx context.Context, in sysin.LogListInp) (err error) {
var (
titleList = []string{"ID", "应用", "提交类型", "模块", "提交url", "ip地址", "报错code", "报错信息", "对外id", "请求耗时", "创建时间", "用户", "访问地"}
fileName = "全局日志导出-" + gctx.CtxId(ctx) + ".xlsx"
fileName = "访问日志导出-" + gctx.CtxId(ctx) + ".xlsx"
sheetName = "HotGo"
exportList []exportImage
row exportImage
@@ -97,15 +97,9 @@ func (s *sSysLog) Export(ctx context.Context, in sysin.LogListInp) (err error) {
}
// 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
func (s *sSysLog) RealWrite(ctx context.Context, commonLog entity.SysLog) (err error) {
_, err = dao.SysLog.Ctx(ctx).Data(commonLog).Insert()
return
}
// AutoLog 根据配置自动记录请求日志
@@ -129,48 +123,31 @@ func (s *sSysLog) AutoLog(ctx context.Context) (err error) {
}
if config.Queue {
q, err := queue.InstanceProducer()
if err != nil {
queue.FatalLog(ctx, "queue.InstanceProducer err:%+v", err)
return err
}
mqMsg, err := q.SendMsg(consts.QueueLogTopic, gconv.String(data))
queue.ProducerLog(ctx, consts.QueueLogTopic, mqMsg.MsgId, err)
return err
return queue.Push(consts.QueueLogTopic, data)
}
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()
postData = gjson.New(consts.NilJsonToString)
getData = gjson.New(consts.NilJsonToString)
headerData = gjson.New(consts.NilJsonToString)
data = entity.SysLog{}
memberId int64 = 0
errorCode = 0
errorMsg = ""
errorData = gjson.New(consts.NilJsonToString)
traceID = ""
timestamp int64 = 0
appId = ""
modelContext = contexts.Get(ctx)
response = modelContext.Response
user = modelContext.User
request = ghttp.RequestFromCtx(ctx)
module = modelContext.Module
clientIp = location.GetClientIp(request)
postData = gjson.New(request.GetBodyString())
getData = gjson.New(request.URL.Query())
headerData = gjson.New(consts.NilJsonToString)
errorData = gjson.New(consts.NilJsonToString)
data entity.SysLog
memberId int64
errorCode int
errorMsg string
traceID string
timestamp int64
appId string
)
// 响应数据
@@ -190,17 +167,14 @@ func (s *sSysLog) AnalysisLog(ctx context.Context) entity.SysLog {
}
// post参数
if gconv.String(request.PostForm) != "" {
postData = gjson.New(gconv.String(request.PostForm))
postForm := gjson.New(gconv.String(request.PostForm)).Map()
if len(postForm) > 0 {
for k, v := range postForm {
postData.MustAppend(k, v)
}
}
if postData.IsNil() {
postData = gjson.New(request.GetBodyString())
}
// get参数
if len(request.URL.Query()) > 0 {
getData = gjson.New(request.URL.Query())
if postData.IsNil() || len(postData.Map()) == 0 {
postData = gjson.New(consts.NilJsonToString)
}
// 当前登录用户
@@ -210,22 +184,14 @@ func (s *sSysLog) AnalysisLog(ctx context.Context) entity.SysLog {
}
var ipData = new(location.IpLocationData)
//if validate.IsPublicIp(clientIp) {
// ipData, err := location.GetLocation(ctx, clientIp)
// if err != nil {
// g.Log().Errorf(ctx, "location.GetLocation err:%+v", err)
// }
// if ipData == nil {
// ipData = new(location.IpLocationData)
// }
//}
ipData, err := location.GetLocation(ctx, clientIp)
if err != nil {
g.Log().Errorf(ctx, "location.GetLocation err:%+v", err)
}
if ipData == nil {
ipData = new(location.IpLocationData)
if validate.IsPublicIp(clientIp) {
ipData, err := location.GetLocation(ctx, clientIp)
if err != nil {
g.Log().Errorf(ctx, "location.GetLocation err:%+v", err)
}
if ipData == nil {
ipData = new(location.IpLocationData)
}
}
data = entity.SysLog{

View File

@@ -0,0 +1,190 @@
// Package sys
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
//
package sys
import (
"context"
"fmt"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/library/hgorm"
"hotgo/internal/library/queue"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"hotgo/utility/convert"
"hotgo/utility/excel"
"hotgo/utility/useragent"
)
type sSysLoginLog struct{}
func NewSysLoginLog() *sSysLoginLog {
return &sSysLoginLog{}
}
func init() {
service.RegisterSysLoginLog(NewSysLoginLog())
}
// Model 登录日志Orm模型
func (s *sSysLoginLog) Model(ctx context.Context) *gdb.Model {
return dao.SysLoginLog.Ctx(ctx)
}
// List 获取登录日志列表
func (s *sSysLoginLog) List(ctx context.Context, in sysin.LoginLogListInp) (list []*sysin.LoginLogListModel, totalCount int, err error) {
mod := dao.SysLoginLog.Ctx(ctx)
// 查询状态
if in.Status > 0 {
mod = mod.Where(dao.SysLoginLog.Columns().Status, in.Status)
}
// 查询登录时间
if len(in.LoginAt) == 2 {
mod = mod.WhereBetween(dao.SysLoginLog.Columns().LoginAt, in.LoginAt[0], in.LoginAt[1])
}
// 查询IP地址
if in.SysLogIp != "" {
mod = mod.Where("sysLog."+dao.SysLog.Columns().Ip, in.SysLogIp)
}
// 用户名
if in.Username != "" {
mod = mod.Where(dao.SysLoginLog.Columns().Username, in.Username)
}
// 关联表sysLog
mod = mod.LeftJoin(hgorm.GenJoinOnRelation(
dao.SysLoginLog.Table(), dao.SysLoginLog.Columns().ReqId, // 主表表名,关联条件
dao.SysLog.Table(), "sysLog", dao.SysLog.Columns().ReqId, // 关联表表名,别名,关联条件
)...)
totalCount, err = mod.Clone().Count(1)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
if totalCount == 0 {
return list, totalCount, nil
}
//关联表select
fields, err := hgorm.GenJoinSelect(ctx, sysin.LoginLogListModel{}, dao.SysLoginLog, []*hgorm.Join{
{Dao: dao.SysLog, Alias: "sysLog"},
})
if err = mod.Fields(fields).Handler(hgorm.HandlerFilterAuth).Page(in.Page, in.PerPage).OrderDesc(dao.SysLoginLog.Columns().Id).Scan(&list); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
for _, v := range list {
// 获取省市编码对应的地区名称
region, err := dao.SysProvinces.GetRegion(ctx, v.SysLogProvinceId, v.SysLogCityId)
if err != nil {
return list, totalCount, err
}
v.Region = region
v.Os = useragent.GetOs(v.SysLogUserAgent)
v.Browser = useragent.GetBrowser(v.SysLogUserAgent)
}
return list, totalCount, err
}
// Export 导出登录日志
func (s *sSysLoginLog) Export(ctx context.Context, in sysin.LoginLogListInp) (err error) {
list, totalCount, err := s.List(ctx, in)
if err != nil {
return err
}
// 字段的排序是依据tags的字段顺序如果你不想使用默认的排序方式可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
tags, err := convert.GetEntityDescTags(sysin.LoginLogExportModel{})
if err != nil {
return err
}
var (
fileName = "导出登录日志-" + gctx.CtxId(ctx) + ".xlsx"
sheetName = fmt.Sprintf("索引条件共%v行,共%v页,当前导出是第%v页,本页共%v行", totalCount, form.CalPageCount(totalCount, in.PerPage), in.Page, len(list))
exports []sysin.LoginLogExportModel
)
err = gconv.Scan(list, &exports)
if err != nil {
return err
}
if err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName); err != nil {
return
}
return
}
// Delete 删除登录日志
func (s *sSysLoginLog) Delete(ctx context.Context, in sysin.LoginLogDeleteInp) (err error) {
_, err = dao.SysLoginLog.Ctx(ctx).Where(dao.SysLoginLog.Columns().Id, in.Id).Delete()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// View 获取登录日志指定信息
func (s *sSysLoginLog) View(ctx context.Context, in sysin.LoginLogViewInp) (res *sysin.LoginLogViewModel, err error) {
if err = dao.SysLoginLog.Ctx(ctx).Where(dao.SysLoginLog.Columns().Id, in.Id).Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
return res, nil
}
// Push 推送登录日志
func (s *sSysLoginLog) Push(ctx context.Context, in sysin.LoginLogPushInp) {
var models entity.SysLoginLog
models.ReqId = gctx.CtxId(ctx)
models.MemberId = in.Response.UserId
models.Username = in.Input.Username
models.LoginAt = gtime.Now()
models.Status = consts.StatusEnabled
if in.Err != nil {
models.Status = consts.StatusDisable
models.ErrMsg = in.Err.Error()
}
models.Response = gjson.New(consts.NilJsonToString)
if in.Response != nil && in.Response.UserId > 0 {
models.Response = gjson.New(in.Response)
}
if err := queue.Push(consts.QueueLoginLogTopic, models); err != nil {
g.Log().Warningf(ctx, "sSysLoginLog.Push err:%+v", err)
}
return
}
// RealWrite 真实写入
func (s *sSysLoginLog) RealWrite(ctx context.Context, models entity.SysLoginLog) (err error) {
_, err = dao.SysLoginLog.Ctx(ctx).Data(models).Insert()
return
}

View File

@@ -9,11 +9,16 @@ 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"
"github.com/gogf/gf/v2/util/gconv"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/library/hgorm"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"hotgo/utility/tree"
"hotgo/utility/validate"
)
@@ -27,10 +32,48 @@ func init() {
service.RegisterSysProvinces(NewSysProvinces())
}
// Tree 关系树选项列表
func (s *sSysProvinces) Tree(ctx context.Context) (list []g.Map, err error) {
var models []*entity.SysProvinces
if err = dao.SysProvinces.Ctx(ctx).Order("pid asc,id asc,sort asc").Scan(&models); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, err
}
list = gconv.SliceMap(models)
for k, v := range list {
list[k]["key"] = v["id"]
list[k]["label"] = v["title"]
}
return tree.GenTree(list), nil
}
// Delete 删除
func (s *sSysProvinces) Delete(ctx context.Context, in sysin.ProvincesDeleteInp) error {
_, err := dao.SysProvinces.Ctx(ctx).Where("id", in.Id).Delete()
var (
models *entity.SysProvinces
)
err := dao.SysProvinces.Ctx(ctx).Where("id", in.Id).Scan(&models)
if err != nil {
return err
}
if models == nil {
return gerror.New("数据不存在或已删除!")
}
pidExist, err := dao.SysProvinces.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.SysProvinces.Ctx(ctx).Where("id", in.Id).Delete()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
@@ -43,12 +86,32 @@ func (s *sSysProvinces) Delete(ctx context.Context, in sysin.ProvincesDeleteInp)
func (s *sSysProvinces) Edit(ctx context.Context, in sysin.ProvincesEditInp) (err error) {
if in.Title == "" {
err = gerror.New("标题不能为空")
return
}
if in.Id <= 0 {
err = gerror.New("地区Id必须大于0")
return
}
// 关系树
in.Pid, in.Level, in.Tree, err = hgorm.GenSubTree(ctx, dao.SysProvinces, in.Pid)
if err != nil {
return err
}
isUpdate := false
models, err := s.View(ctx, sysin.ProvincesViewInp{Id: in.Id})
if err != nil {
return
}
if models != nil {
isUpdate = true
}
// 修改
in.UpdatedAt = gtime.Now()
if in.Id > 0 {
if isUpdate {
_, err = dao.SysProvinces.Ctx(ctx).Where("id", in.Id).Data(in).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
@@ -59,7 +122,6 @@ func (s *sSysProvinces) Edit(ctx context.Context, in sysin.ProvincesEditInp) (er
}
// 新增
in.CreatedAt = gtime.Now()
_, err = dao.SysProvinces.Ctx(ctx).Data(in).Insert()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
@@ -97,19 +159,14 @@ func (s *sSysProvinces) Status(ctx context.Context, in sysin.ProvincesStatusInp)
}
// 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
}
func (s *sSysProvinces) MaxSort(ctx context.Context, in sysin.ProvincesMaxSortInp) (res *sysin.ProvincesMaxSortModel, err error) {
if err = dao.SysProvinces.Ctx(ctx).Fields(dao.SysProvinces.Columns().Sort).OrderDesc(dao.SysProvinces.Columns().Sort).Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
res.Sort = res.Sort + 10
return &res, nil
res.Sort = res.Sort + g.Cfg().MustGet(ctx, "hotgo.admin.maxSortIncrement").Int()
return res, nil
}
// View 获取指定字典类型信息
@@ -126,12 +183,10 @@ func (s *sSysProvinces) View(ctx context.Context, in sysin.ProvincesViewInp) (re
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)
}
@@ -153,3 +208,53 @@ func (s *sSysProvinces) List(ctx context.Context, in sysin.ProvincesListInp) (li
return list, totalCount, err
}
// ChildrenList 获取省市区下级列表
func (s *sSysProvinces) ChildrenList(ctx context.Context, in sysin.ProvincesChildrenListInp) (list []*sysin.ProvincesChildrenListModel, totalCount int, err error) {
mod := dao.SysProvinces.Ctx(ctx)
if in.Title != "" {
mod = mod.WhereLike("title", "%"+in.Title+"%")
}
if in.Pid > 0 {
mod = mod.Where("pid", in.Pid)
}
if in.Id > 0 {
mod = mod.Where("id", in.Id)
}
totalCount, err = mod.Count()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
if totalCount == 0 {
return list, totalCount, nil
}
if err = mod.Page(in.Page, in.PerPage).Order("sort asc,id desc").Scan(&list); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
return list, totalCount, err
}
// UniqueId 获取省市区下级列表
func (s *sSysProvinces) UniqueId(ctx context.Context, in sysin.ProvincesUniqueIdInp) (res *sysin.ProvincesUniqueIdModel, err error) {
res = new(sysin.ProvincesUniqueIdModel)
res.IsUnique = true
if in.NewId == 0 {
return
}
if err = hgorm.IsUnique(ctx, dao.SysProvinces, g.Map{dao.Test.Columns().Id: in.NewId}, "", in.OldId); err != nil {
res.IsUnique = false
return res, nil
}
return res, nil
}

View File

@@ -0,0 +1,148 @@
// Package sys
// @Link https://github.com/bufanyun/hotgo
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
//
package sys
import (
"context"
"fmt"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/util/gconv"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/library/hgorm"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/form"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"hotgo/utility/convert"
"hotgo/utility/excel"
)
type sSysServeLog struct{}
func NewSysServeLog() *sSysServeLog {
return &sSysServeLog{}
}
func init() {
service.RegisterSysServeLog(NewSysServeLog())
}
// Model 服务日志Orm模型
func (s *sSysServeLog) Model(ctx context.Context) *gdb.Model {
return dao.SysServeLog.Ctx(ctx)
}
// List 获取服务日志列表
func (s *sSysServeLog) List(ctx context.Context, in sysin.ServeLogListInp) (list []*sysin.ServeLogListModel, totalCount int, err error) {
mod := dao.SysServeLog.Ctx(ctx)
// 查询链路ID
if in.TraceId != "" {
mod = mod.Where(dao.SysServeLog.Columns().TraceId, in.TraceId)
}
// 查询日志级别
if in.LevelFormat != "" {
mod = mod.WhereLike(dao.SysServeLog.Columns().LevelFormat, in.LevelFormat)
}
// 查询触发时间(ns)
if len(in.TriggerNs) == 2 {
mod = mod.WhereBetween(dao.SysServeLog.Columns().TriggerNs, in.TriggerNs[0], in.TriggerNs[1])
}
// 查询创建时间
if len(in.CreatedAt) == 2 {
mod = mod.WhereBetween(dao.SysServeLog.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
}
// 关联表sysLog
mod = mod.LeftJoin(hgorm.GenJoinOnRelation(
dao.SysServeLog.Table(), dao.SysServeLog.Columns().TraceId, // 主表表名,关联条件
dao.SysLog.Table(), "sysLog", dao.SysLog.Columns().ReqId, // 关联表表名,别名,关联条件
)...)
totalCount, err = mod.Clone().Count(1)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
if totalCount == 0 {
return list, totalCount, nil
}
//关联表select
fields, err := hgorm.GenJoinSelect(ctx, sysin.ServeLogListModel{}, dao.SysServeLog, []*hgorm.Join{
{Dao: dao.SysLog, Alias: "sysLog"},
})
if err = mod.Fields(fields).Handler(hgorm.HandlerFilterAuth).Page(in.Page, in.PerPage).OrderDesc(dao.SysServeLog.Columns().Id).Scan(&list); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
return list, totalCount, err
}
// Export 导出服务日志
func (s *sSysServeLog) Export(ctx context.Context, in sysin.ServeLogListInp) (err error) {
list, totalCount, err := s.List(ctx, in)
if err != nil {
return err
}
// 字段的排序是依据tags的字段顺序如果你不想使用默认的排序方式可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
tags, err := convert.GetEntityDescTags(sysin.ServeLogExportModel{})
if err != nil {
return err
}
var (
fileName = "导出服务日志-" + gctx.CtxId(ctx) + ".xlsx"
sheetName = fmt.Sprintf("索引条件共%v行,共%v页,当前导出是第%v页,本页共%v行", totalCount, form.CalPageCount(totalCount, in.PerPage), in.Page, len(list))
exports []sysin.ServeLogExportModel
)
err = gconv.Scan(list, &exports)
if err != nil {
return err
}
if err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName); err != nil {
return
}
return
}
// Delete 删除服务日志
func (s *sSysServeLog) Delete(ctx context.Context, in sysin.ServeLogDeleteInp) (err error) {
_, err = dao.SysServeLog.Ctx(ctx).Where(dao.SysServeLog.Columns().Id, in.Id).Delete()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// View 获取服务日志指定信息
func (s *sSysServeLog) View(ctx context.Context, in sysin.ServeLogViewInp) (res *sysin.ServeLogViewModel, err error) {
if err = dao.SysServeLog.Ctx(ctx).Where(dao.SysServeLog.Columns().Id, in.Id).Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return nil, err
}
return res, nil
}
// RealWrite 真实写入
func (s *sSysServeLog) RealWrite(ctx context.Context, models entity.SysServeLog) (err error) {
_, err = dao.SysServeLog.Ctx(ctx).Data(models).Insert()
return
}

View File

@@ -0,0 +1,343 @@
// 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/net/ghttp"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/grand"
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/library/location"
"hotgo/internal/library/sms/aliyun"
"hotgo/internal/model"
"hotgo/internal/model/entity"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"hotgo/utility/validate"
"time"
)
type sSysSmsLog struct{}
func NewSysSmsLog() *sSysSmsLog {
return &sSysSmsLog{}
}
func init() {
service.RegisterSysSmsLog(NewSysSmsLog())
}
// Delete 删除
func (s *sSysSmsLog) Delete(ctx context.Context, in sysin.SmsLogDeleteInp) error {
_, err := dao.SysSmsLog.Ctx(ctx).Where("id", in.Id).Delete()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// Edit 修改/新增
func (s *sSysSmsLog) Edit(ctx context.Context, in sysin.SmsLogEditInp) (err error) {
if in.Ip == "" {
err = gerror.New("ip不能为空")
return err
}
// 修改
in.UpdatedAt = gtime.Now()
if in.Id > 0 {
_, err = dao.SysSmsLog.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.SysSmsLog.Ctx(ctx).Data(in).Insert()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return nil
}
// Status 更新部门状态
func (s *sSysSmsLog) Status(ctx context.Context, in sysin.SmsLogStatusInp) (err error) {
if in.Id <= 0 {
err = gerror.New("ID不能为空")
return err
}
if in.Status <= 0 {
err = gerror.New("状态不能为空")
return err
}
if !validate.InSliceInt(consts.StatusMap, in.Status) {
err = gerror.New("状态不正确")
return err
}
// 修改
in.UpdatedAt = gtime.Now()
_, err = dao.SysSmsLog.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 *sSysSmsLog) MaxSort(ctx context.Context, in sysin.SmsLogMaxSortInp) (*sysin.SmsLogMaxSortModel, error) {
var res sysin.SmsLogMaxSortModel
if in.Id > 0 {
if err := dao.SysSmsLog.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 *sSysSmsLog) View(ctx context.Context, in sysin.SmsLogViewInp) (res *sysin.SmsLogViewModel, err error) {
if err = dao.SysSmsLog.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 *sSysSmsLog) List(ctx context.Context, in sysin.SmsLogListInp) (list []*sysin.SmsLogListModel, totalCount int, err error) {
mod := dao.SysSmsLog.Ctx(ctx)
if in.Status > 0 {
mod = mod.Where("status", in.Status)
}
totalCount, err = mod.Count()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
if totalCount == 0 {
return list, totalCount, nil
}
if err = mod.Page(int(in.Page), int(in.PerPage)).Order("id desc").Scan(&list); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return list, totalCount, err
}
return list, totalCount, err
}
// SendCode 发送验证码
func (s *sSysSmsLog) SendCode(ctx context.Context, in sysin.SendCodeInp) (err error) {
if in.Event == "" {
return gerror.New("事件不能为空")
}
if in.Mobile == "" {
return gerror.New("手机号不能为空")
}
var (
models *entity.SysSmsLog
)
if err = dao.SysSmsLog.Ctx(ctx).Where("event", in.Event).Where("mobile", in.Mobile).Scan(&models); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
config, err := service.SysConfig().GetSms(ctx)
if err != nil {
return err
}
in.Template, err = s.GetTemplate(ctx, in.Event, config)
if err != nil {
return err
}
err = s.AllowSend(ctx, models, config)
if err != nil {
return err
}
if in.Code == "" {
in.Code = grand.Digits(4)
}
switch config.SmsDrive {
case consts.SmsDriveAliYun:
err = aliyun.SendCode(ctx, in, config)
if err != nil {
return err
}
case consts.SmsDriveTencent:
return gerror.Newf("暂不支持短信驱动:%v", config.SmsDrive)
default:
return gerror.Newf("暂不支持短信驱动:%v", config.SmsDrive)
}
var data = new(entity.SysSmsLog)
data.Event = in.Event
data.Mobile = in.Mobile
data.Code = in.Code
data.Ip = location.GetClientIp(ghttp.RequestFromCtx(ctx))
data.Status = consts.SmsStatusNotUsed
data.CreatedAt = gtime.Now()
data.UpdatedAt = gtime.Now()
_, err = dao.SysSmsLog.Ctx(ctx).Data(data).Insert()
if err != nil {
return err
}
return nil
}
// GetTemplate 获取指定短信模板
func (s *sSysSmsLog) GetTemplate(ctx context.Context, template string, config *model.SmsConfig) (val string, err error) {
if template == "" {
return "", gerror.New("模板不能为空")
}
if config == nil {
config, err = service.SysConfig().GetSms(ctx)
if err != nil {
return "", err
}
}
switch config.SmsDrive {
case consts.SmsDriveAliYun:
if len(config.SmsAliyunTemplate) == 0 {
return "", gerror.New("管理员还没有配置任何模板!")
}
for _, v := range config.SmsAliyunTemplate {
if v.Key == template {
return v.Value, nil
}
}
case consts.SmsDriveTencent:
return "", gerror.Newf("暂不支持短信驱动:%v", config.SmsDrive)
default:
return "", gerror.Newf("暂不支持短信驱动:%v", config.SmsDrive)
}
return
}
// AllowSend 是否允许发送
func (s *sSysSmsLog) AllowSend(ctx context.Context, models *entity.SysSmsLog, config *model.SmsConfig) (err error) {
if models == nil {
return nil
}
if config == nil {
config, err = service.SysConfig().GetSms(ctx)
if err != nil {
return err
}
}
if gtime.Now().Before(models.CreatedAt.Add(time.Second * time.Duration(config.SmsMinInterval))) {
return gerror.New("发送频繁,请稍后再试!")
}
if config.SmsMaxIpLimit > 0 {
count, err := dao.SysSmsLog.NowDayCount(ctx, models.Event, models.Mobile)
if err != nil {
return err
}
if count >= config.SmsMaxIpLimit {
return gerror.New("今天发送短信过多,请次日后再试!")
}
}
return
}
// VerifyCode 效验验证码
func (s *sSysSmsLog) VerifyCode(ctx context.Context, in sysin.VerifyCodeInp) (err error) {
if in.Event == "" {
return gerror.New("事件不能为空")
}
if in.Mobile == "" {
return gerror.New("手机号不能为空")
}
config, err := service.SysConfig().GetSms(ctx)
if err != nil {
return err
}
var (
models *entity.SysSmsLog
)
if err = dao.SysSmsLog.Ctx(ctx).Where("event", in.Event).Where("mobile", in.Mobile).Order("id desc").Scan(&models); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
if models == nil {
return gerror.New("验证码错误")
}
if models.Times >= 10 {
return gerror.New("验证码错误次数过多,请重新发送!")
}
if in.Event != consts.SmsTemplateCode {
if models.Status == consts.SmsStatusUsed {
return gerror.New("验证码已使用,请重新发送!")
}
}
if gtime.Now().After(models.CreatedAt.Add(time.Second * time.Duration(config.SmsCodeExpire))) {
return gerror.New("验证码已过期,请重新发送")
}
if models.Code != in.Code {
_, err = dao.SysSmsLog.Ctx(ctx).Where("id", models.Id).Increment("times", 1)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return gerror.New("验证码错误!")
}
_, err = dao.SysSmsLog.Ctx(ctx).Where("id", models.Id).Data(g.Map{
"times": models.Times + 1,
"status": consts.SmsStatusUsed,
"updated_at": gtime.Now(),
}).Update()
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return err
}
return
}