优化服务退出流程,增加中间件文档

This commit is contained in:
孟帅 2023-05-15 18:37:40 +08:00
parent fdefb42253
commit c511a2e6b3
30 changed files with 335 additions and 97 deletions

View File

@ -69,7 +69,7 @@
16. 插件应用:支持一键生成插件模板,每个插件之间开发隔离,拥有独立多应用入口、独立配置。完美支持多人协同开发、插件插拔不会对原系统产生影响等。 16. 插件应用:支持一键生成插件模板,每个插件之间开发隔离,拥有独立多应用入口、独立配置。完美支持多人协同开发、插件插拔不会对原系统产生影响等。
17. 服务监控监视当前系统CPU、内存、磁盘、网络、堆栈等相关信息。 17. 服务监控监视当前系统CPU、内存、磁盘、网络、堆栈等相关信息。
18. 附件管理文件图片上传支持本地、阿里云oss、腾讯云cos、ucloud对象存储、七牛云对象存储等多种上传驱动后台一键切换配置。 18. 附件管理文件图片上传支持本地、阿里云oss、腾讯云cos、ucloud对象存储、七牛云对象存储等多种上传驱动后台一键切换配置。
19. TCP服务基于gtcp的应用实例,支持长连接、断线重连、自动维护心跳、服务登录、服务授权等。主要用于网络服务进程之间的消息通讯。 19. TCP服务基于gtcp的应用实例支持长连接、断线重连、自动维护心跳、签名、服务登录、服务授权等。主要用于C/S服务器和服务进程之间的数据通讯。
20. 消息队列:同时兼容 kafka、redis、rocketmq、磁盘队列一键配置切换到场景适用的MQ。 20. 消息队列:同时兼容 kafka、redis、rocketmq、磁盘队列一键配置切换到场景适用的MQ。
21. 通知公告采用websocket实时推送在线用户最新通知、公告、私信消息。 21. 通知公告采用websocket实时推送在线用户最新通知、公告、私信消息。
22. 地区编码:整合国内通用省市区编码,运用于项目于一身,支持动态省市区选项。 22. 地区编码:整合国内通用省市区编码,运用于项目于一身,支持动态省市区选项。

View File

@ -16,7 +16,8 @@
- [目录结构](sys-catalog.md) - [目录结构](sys-catalog.md)
- [开发规范](sys-exploit.md) - [开发规范](sys-exploit.md)
- [控制台](sys-console.md) - [控制台](sys-console.md)
- 请求中间件和WebHook - [中间件/拦截器](sys-middleware.md)
- [WebHook](sys-webhook.md)
- [权限控制](sys-auth.md) - [权限控制](sys-auth.md)
- [支付网关](sys-payment.md) - [支付网关](sys-payment.md)
- [数据库](sys-db.md) - [数据库](sys-db.md)

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

@ -13,6 +13,7 @@
- 仅推荐在开发期间快速调试使用,线上实际部署时建议将各个服务分开部署,这样重新部署某个服务时无需全部重启。 - 仅推荐在开发期间快速调试使用,线上实际部署时建议将各个服务分开部署,这样重新部署某个服务时无需全部重启。
```shell ```shell
# 默认 # 默认
go run main.go go run main.go
@ -22,6 +23,7 @@ gf run main.go
### HTTP服务 ### HTTP服务
- 启动HTTP服务包含websocket。 - 启动HTTP服务包含websocket。
```shell ```shell
# 默认 # 默认
go run main.go http go run main.go http
@ -41,7 +43,7 @@ go run main.go queue
gf run main.go --args "queue" gf run main.go --args "queue"
``` ```
### 定时任务暂未拆分目前随HTTP服务启动 ### 定时任务
- 启动系统中统一注册的定时任务。 - 启动系统中统一注册的定时任务。
```shell ```shell
@ -55,6 +57,8 @@ gf run main.go --args "cron"
### 常用工具 ### 常用工具
- 释放casbin权限用于清理无效的权限设置。 - 释放casbin权限用于清理无效的权限设置。
```shell ```shell
go run main.go tools -m=casbin -a1=refresh go run main.go tools -m=casbin -a1=refresh
``` ```
@ -62,6 +66,7 @@ go run main.go tools -m=casbin -a1=refresh
### Makefile ### Makefile
- 通过make提供一些快捷命令 - 通过make提供一些快捷命令
```shell ```shell
# 一键编译,打包前后端代码到可执行文件 # 一键编译,打包前后端代码到可执行文件
make build make build

View File

@ -0,0 +1,204 @@
## 中间件/拦截器
目录
- 介绍
- 全局中间件
- 鉴权中间件
- 响应中间件
- 更多
### 介绍
- 在hotgo中中间件/拦截器主要作用于web请求的上下文预设、跨域请求处理、鉴权处理、请求拦截和请求结束后统一响应处理等。
### 全局中间件
```go
package main
import (
"hotgo/internal/service"
)
func main() {
// 初始化请求上下文,一般需要第一个进行加载,后续中间件存在依赖关系
service.Middleware().Ctx()
// 跨域中间件,自动处理跨域问题
service.Middleware().CORS()
// IP黑名单中间件如果请求IP被后台拉黑所有请求将被拒绝
service.Middleware().Blacklist()
// 演示系統操作限制当开启演示模式时所有POST请求将被拒绝
service.Middleware().DemoLimit()
// HTTP响应预处理在业务处理完成后对响应结果进行格式化和错误过滤将处理后的数据发送给请求方
service.Middleware().ResponseHandler()
}
```
### 鉴权中间件
```go
package main
import (
"github.com/gogf/gf/v2/frame/g"
)
func main() {
// 在鉴权中间件下的路由如果没有通过权限验证,后续请求将被拒绝
// 在hotgo中鉴权中间件一般是配合一个业务模块下的路由组进行使用
// 目前admin、api、home、websocket模块都已接入
// 如果你需要创建一个新的模块也需要用到鉴权中间件可以参考server/internal/logic/middleware/admin_auth.go
// 一个简单例子
s := g.Server()
s.Group("/api", func(group *ghttp.RouterGroup) {
group.Middleware(service.Middleware().ApiAuth)
group.Bind(
member.Member, // 管理员
)
})
}
```
### 响应中间件
- 文件路径server/internal/logic/middleware/response.go
#### 自定义响应
- 由于响应中间件是全局的并且是统一使用json格式进行响应的但是在实际的开发中可能存在一些需要使用非json格式的响应所以你需要进行单独的处理。
- 推荐以下几种处理方式,可做参考:
1. 使用`ghttp.ExitAll()`需要注意的是此方法会终止后续所有的http处理
```go
package main
import (
"github.com/gogf/gf/v2/net/ghttp"
)
func main() {
r := new(ghttp.Request) // 当前请求对象
// 清空响应
r.Response.ClearBuffer()
// 写入响应
r.Response.Write("自定义响应内容")
// 终止后续http处理
r.ExitAll()
}
```
2. 在`server/internal/logic/middleware/response.go`中根据请求的独有特征进行单独的处理兼容后续http处理。
#### 重写响应错误提示
- 在实际开发中我们可能想要隐藏一些敏感错误返回给客户端友好的错误提示但开发者同时又想需要看到真实的敏感错误。对此hotgo已经进行了过滤处理下面是一个简单的例子
```go
package main
import (
"github.com/gogf/gf/v2/errors/gerror"
)
func test() error {
err = gerror.New("这是一个sql执行错误")
err = gerror.Wrap(err, "用户创建失败,请稍后重试!~")
return err
}
```
- 开启debug时的客户端响应
```json
{
"code": -1,
"message": "用户创建失败,请稍后重试!~",
"error": [
"1. 用户创建失败,请稍后重试!~",
" 1). hotgo/internal/logic/admin.(*sAdminMember).List",
" E:/Users/Administrator/Desktop/gosrc/hotgo_dev/server/internal/logic/admin/member.go:526",
"2. 这是一个sql执行错误", " 1). hotgo/internal/logic/admin.(*sAdminMember).List",
" E:/Users/Administrator/Desktop/gosrc/hotgo_dev/server/internal/logic/admin/member.go:525",
" 2). hotgo/internal/controller/admin/admin.(*cMember).List",
" E:/Users/Administrator/Desktop/gosrc/hotgo_dev/server/internal/controller/admin/admin/member.go:157", " 3). github.com/gogf/gf/v2/net/ghttp.(*middleware).callHandlerFunc.func1", " E:/gopath/pkg/mod/github.com/gogf/gf/v2@v2.4.1/net/ghttp/ghttp_request_middleware.go:152", " 4). github.com/gogf/gf/v2/net/ghttp.niceCallFunc", " E:/gopath/pkg/mod/github.com/gogf/gf/v2@v2.4.1/net/ghttp/ghttp_func.go:55", " 5). github.com/gogf/gf/v2/net/ghttp.(*middleware).callHandlerFunc", " E:/gopath/pkg/mod/github.com/gogf/gf/v2@v2.4.1/net/ghttp/ghttp_request_middleware.go:129", " 6). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next.func1", " E:/gopath/pkg/mod/github.com/gogf/gf/v2@v2.4.1/net/ghttp/ghttp_request_middleware.go:75", " 7). github.com/gogf/gf/v2/util/gutil.TryCatch", " E:/gopath/pkg/mod/github.com/gogf/gf/v2@v2.4.1/util/gutil/gutil.go:56", " 8). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next", " E:/gopath/pkg/mod/github.com/gogf/gf/v2@v2.4.1/net/ghttp/ghttp_request_middleware.go:49", " 9). hotgo/internal/logic/middleware.(*sMiddleware).AdminAuth", " E:/Users/Administrator/Desktop/gosrc/hotgo_dev/server/internal/logic/middleware/admin_auth.go:53", " 10). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next.func1.1", " E:/gopath/pkg/mod/github.com/gogf/gf/v2@v2.4.1/net/ghttp/ghttp_request_middleware.go:55", " 11). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next.func1", " E:/gopath/pkg/mod/github.com/gogf/gf/v2@v2.4.1/net/ghttp/ghttp_request_middleware.go:54", " 12). hotgo/internal/logic/middleware.(*sMiddleware).ResponseHandler", " E:/Users/Administrator/Desktop/gosrc/hotgo_dev/server/internal/logic/middleware/response.go:24", " 13). hotgo/internal/logic/middleware.(*sMiddleware).DemoLimit", " E:/Users/Administrator/Desktop/gosrc/hotgo_dev/server/internal/logic/middleware/init.go:90", " 14). hotgo/internal/logic/middleware.(*sMiddleware).Blacklist", " E:/Users/Administrator/Desktop/gosrc/hotgo_dev/server/internal/logic/middleware/limit_blacklist.go:22", " 15). hotgo/internal/logic/middleware.(*sMiddleware).CORS", " E:/Users/Administrator/Desktop/gosrc/hotgo_dev/server/internal/logic/middleware/init.go:83", " 16). hotgo/internal/logic/middleware.(*sMiddleware).Ctx", " E:/Users/Administrator/Desktop/gosrc/hotgo_dev/server/internal/logic/middleware/init.go:62", " 17). github.com/gogf/gf/v2/net/ghttp.internalMiddlewareServerTracing", " E:/gopath/pkg/mod/github.com/gogf/gf/v2@v2.4.1/net/ghttp/ghttp_middleware_tracing.go:79", " 18). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next.func1.5", " E:/gopath/pkg/mod/github.com/gogf/gf/v2@v2.4.1/net/ghttp/ghttp_request_middleware.go:96", " 19). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next.func1", " E:/gopath/pkg/mod/github.com/gogf/gf/v2@v2.4.1/net/ghttp/ghttp_request_middleware.go:95", " 20). github.com/gogf/gf/v2/net/ghttp.(*Server).ServeHTTP", " E:/gopath/pkg/mod/github.com/gogf/gf/v2@v2.4.1/net/ghttp/ghttp_server_handler.go:132", ""
],
"timestamp": 1684145107,
"traceID": "084022730d495f17f19e550140f3e1a8"
}
```
- 关闭debug时的客户端响应
```json
{
"code": -1,
"message": "用户创建失败,请稍后重试!~",
"timestamp": 1684145107,
"traceID": "084022730d495f17f19e550140f3e1a8"
}
```
- 控制台的输出日志:
```shell
2023-05-15 18:05:07.776 {084022730d495f17f19e550140f3e1a8} 200 "GET http localhost:8000 /admin/member/list?page=1&pageSize=10&roleId=-1 HTTP/1.1" 0.002, 127.0.0.1, "http://192.168.0.207:8001/login", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Co
re/1.94.197.400 QQBrowser/11.7.5287.400", -1, "", ""
Stack:
1. 用户创建失败,请稍后重试!~
1). hotgo/internal/logic/admin.(*sAdminMember).List
E:/Users/Administrator/Desktop/gosrc/hotgo_dev/server/internal/logic/admin/member.go:526
2. 这是一个sql执行错误
1). hotgo/internal/logic/admin.(*sAdminMember).List
E:/Users/Administrator/Desktop/gosrc/hotgo_dev/server/internal/logic/middleware/response.go:24
13). hotgo/internal/logic/middleware.(*sMiddleware).DemoLimit
E:/Users/Administrator/Desktop/gosrc/hotgo_dev/server/internal/logic/middleware/init.go:90
```
- 如果你开启了访问日志,那么日志记录中会详细记录本次请求的相关信息,内容如下:
![./images/sys-middleware-error-log.png](./images/sys-middleware-error-log.png)
#### 重写错误码
- hotgo默认使用了gf内置的错误码进行业务处理通常情况下成功状态码为`0`,失败状态码为`-1`
- 查看gf内置错误码https://goframe.org/pages/viewpage.action?pageId=30739587
- 以下是自定义错误码的简单例子:
```go
package main
import (
"github.com/gogf/gf/v2/errors/gerror"
)
func test() error {
// 使用自定义状态码30001响应客户端
err = gerror.NewCode(gcode.New(30001, "用户创建失败,请稍后重试!~", nil))
return err
}
```
- 客户端响应如下:
```json
{
"code": 30001,
"message": "用户创建失败,请稍后重试!~",
"timestamp": 1684146313,
"traceID": "b4f90e16264a5f17cd3fc27141aba448"
}
```
### 更多
- 更多关于中间件/拦截器的介绍请参考https://goframe.org/pages/viewpage.action?pageId=55289881

View File

@ -0,0 +1,3 @@
## WebHook
待写

View File

@ -7,9 +7,9 @@ package cmd
import ( import (
"context" "context"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcmd" "github.com/gogf/gf/v2/os/gcmd"
"hotgo/internal/service" "hotgo/internal/service"
"os"
) )
var ( var (
@ -20,17 +20,17 @@ var (
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) { Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
service.AuthClient().Start(ctx) service.AuthClient().Start(ctx)
// 退出信号监听 serverWg.Add(1)
signalListen(ctx, func(sig os.Signal) {
service.AuthClient().Stop(ctx)
})
// 信号监听 // 信号监听
signalListen(ctx, signalHandlerForOverall) signalListen(ctx, signalHandlerForOverall)
select { select {
case <-serverCloseSignal: case <-serverCloseSignal:
// ... service.AuthClient().Stop(ctx)
serverWg.Done()
} }
g.Log().Debug(ctx, "auth successfully closed ..")
return return
}, },
} }

View File

@ -13,7 +13,6 @@ import (
) )
var ( var (
serverCloseSignal chan struct{}
Main = &gcmd.Command{ Main = &gcmd.Command{
Description: `默认启动所有服务`, Description: `默认启动所有服务`,
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) { Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
@ -76,10 +75,10 @@ var (
select { select {
case <-serverCloseSignal: case <-serverCloseSignal:
// ... serverWg.Wait()
} }
g.Log().Debug(ctx, "service successfully closed ..") g.Log().Debug(ctx, "all service successfully closed ..")
return return
}, },
} }
@ -89,5 +88,4 @@ func init() {
if err := Main.AddCommand(All, Http, Queue, Cron, Auth, Tools, Help); err != nil { if err := Main.AddCommand(All, Http, Queue, Cron, Auth, Tools, Help); err != nil {
panic(err) panic(err)
} }
serverCloseSignal = make(chan struct{}, 1)
} }

View File

@ -7,10 +7,10 @@ package cmd
import ( import (
"context" "context"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcmd" "github.com/gogf/gf/v2/os/gcmd"
"hotgo/internal/crons" "hotgo/internal/crons"
"hotgo/internal/service" "hotgo/internal/service"
"os"
) )
var ( var (
@ -25,17 +25,18 @@ var (
// tcp客户端 // tcp客户端
service.CronClient().Start(ctx) service.CronClient().Start(ctx)
// 退出信号监听 serverWg.Add(1)
signalListen(ctx, func(sig os.Signal) {
service.CronClient().Stop(ctx)
crons.StopALL()
serverCloseSignal <- struct{}{}
})
// 信号监听
signalListen(ctx, signalHandlerForOverall)
select { select {
case <-serverCloseSignal: case <-serverCloseSignal:
// ... service.CronClient().Stop(ctx)
crons.StopALL()
serverWg.Done()
} }
g.Log().Debug(ctx, "cron successfully closed ..")
return return
}, },
} }

View File

@ -3,7 +3,6 @@
// @Copyright Copyright (c) 2023 HotGo CLI // @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com> // @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE // @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
//
package cmd package cmd
import ( import (
@ -11,6 +10,12 @@ import (
"github.com/gogf/gf/v2/os/gproc" "github.com/gogf/gf/v2/os/gproc"
"hotgo/utility/simple" "hotgo/utility/simple"
"os" "os"
"sync"
)
var (
serverCloseSignal = make(chan struct{}, 1)
serverWg = sync.WaitGroup{}
) )
func signalHandlerForOverall(sig os.Signal) { func signalHandlerForOverall(sig os.Signal) {

View File

@ -10,13 +10,11 @@ import (
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gcmd" "github.com/gogf/gf/v2/os/gcmd"
"hotgo/internal/crons"
"hotgo/internal/library/addons" "hotgo/internal/library/addons"
"hotgo/internal/library/casbin" "hotgo/internal/library/casbin"
"hotgo/internal/router" "hotgo/internal/router"
"hotgo/internal/service" "hotgo/internal/service"
"hotgo/internal/websocket" "hotgo/internal/websocket"
"os"
) )
var ( var (
@ -77,13 +75,20 @@ var (
// https // https
setSSL(ctx, s) setSSL(ctx, s)
// 退出信号监听 serverWg.Add(1)
signalListen(ctx, func(sig os.Signal) {
s.Shutdown() // 信号监听
crons.StopALL() signalListen(ctx, signalHandlerForOverall)
go func() {
select {
case <-serverCloseSignal:
websocket.Stop() websocket.Stop()
service.TCPServer().Stop(ctx) service.TCPServer().Stop(ctx)
}) s.Shutdown() // 主服务建议放在最后一个关闭
g.Log().Debug(ctx, "http successfully closed ..")
serverWg.Done()
}
}()
// Just run the server. // Just run the server.
s.Run() s.Run()

View File

@ -25,12 +25,14 @@ var (
g.Log().Debug(ctx, "start queue consumer success..") g.Log().Debug(ctx, "start queue consumer success..")
}) })
serverWg.Add(1)
// 信号监听 // 信号监听
signalListen(ctx, signalHandlerForOverall) signalListen(ctx, signalHandlerForOverall)
select { select {
case <-serverCloseSignal: case <-serverCloseSignal:
// ... serverWg.Done()
} }
g.Log().Debug(ctx, "queue successfully closed ..") g.Log().Debug(ctx, "queue successfully closed ..")

View File

@ -7,7 +7,9 @@ package consts
import "github.com/gogf/gf/v2/util/gconv" import "github.com/gogf/gf/v2/util/gconv"
// RequestEncryptKey 请求加密密钥用于敏感数据加密16位字符前后端需保持一致。安全起见请修改此值 // RequestEncryptKey
// 请求加密密钥用于敏感数据加密16位字符前后端需保持一致
// 安全起见,生产环境运行时请注意修改
var RequestEncryptKey = []byte("f080a463654b2279") var RequestEncryptKey = []byte("f080a463654b2279")
// 配置数据类型 // 配置数据类型

View File

@ -27,6 +27,7 @@ func Build(ctx context.Context, sk Skeleton, conf *model.BuildAddonConfig) (err
"@{.description}": sk.Description, "@{.description}": sk.Description,
"@{.author}": sk.Author, "@{.author}": sk.Author,
"@{.version}": sk.Version, "@{.version}": sk.Version,
"@{.hgVersion}": consts.VersionApp, // HG 版本
} }
) )

View File

@ -195,7 +195,9 @@ func (client *Client) connect() {
reconnect: reconnect:
conn := client.dial() conn := client.dial()
if conn == nil { if conn == nil {
if !client.stopFlag {
client.Logger.Debugf(client.Ctx, "client dial failed") client.Logger.Debugf(client.Ctx, "client dial failed")
}
return return
} }
@ -311,6 +313,11 @@ func (client *Client) Stop() {
client.Close() client.Close()
} }
// IsStop 是否已停止
func (client *Client) IsStop() bool {
return client.stopFlag
}
// Destroy 销毁当前连接 // Destroy 销毁当前连接
func (client *Client) Destroy() { func (client *Client) Destroy() {
client.stopCron() client.stopCron()

View File

@ -294,6 +294,11 @@ func (server *Server) Close() {
server.wgLn.Wait() server.wgLn.Wait()
} }
// IsClose 服务是否关闭
func (server *Server) IsClose() bool {
return server.closeFlag
}
// Write 向指定客户端发送消息 // Write 向指定客户端发送消息
func (server *Server) Write(conn *gtcp.Conn, data interface{}) (err error) { func (server *Server) Write(conn *gtcp.Conn, data interface{}) (err error) {
if server.closeFlag { if server.closeFlag {

View File

@ -99,6 +99,8 @@ func Logout(r *ghttp.Request) (err error) {
claims, err := parseToken(ctx, header) claims, err := parseToken(ctx, header)
if err != nil { if err != nil {
g.Log().Debugf(ctx, "logout parseToken err:%+v", err)
err = errorLogin
return return
} }
@ -138,10 +140,13 @@ func ParseLoginUser(r *ghttp.Request) (user *model.Identity, err error) {
claims, err := parseToken(ctx, header) claims, err := parseToken(ctx, header)
if err != nil { if err != nil {
g.Log().Debugf(ctx, "parseToken err:%+v", err)
err = errorLogin
return return
} }
var ( var (
// 认证key
authKey = GetAuthKey(header) authKey = GetAuthKey(header)
// 登录token // 登录token
tokenKey = GetTokenKey(claims.App, authKey) tokenKey = GetTokenKey(claims.App, authKey)
@ -285,7 +290,7 @@ func GetAuthorization(r *ghttp.Request) string {
// GetAuthKey 认证key // GetAuthKey 认证key
func GetAuthKey(token string) string { func GetAuthKey(token string) string {
return gmd5.MustEncryptString(token) return gmd5.MustEncryptString("hotgo" + token)
} }
// GetTokenKey 令牌缓存key // GetTokenKey 令牌缓存key

View File

@ -227,18 +227,18 @@ func (s *sAdminMember) UpdateMobile(ctx context.Context, in adminin.MemberUpdate
return err return err
} }
var memberInfo *entity.AdminMember var mb *entity.AdminMember
if err = dao.AdminMember.Ctx(ctx).Where("id", memberId).Scan(&memberInfo); err != nil { if err = dao.AdminMember.Ctx(ctx).Where("id", memberId).Scan(&mb); err != nil {
err = gerror.Wrap(err, consts.ErrorORM) err = gerror.Wrap(err, consts.ErrorORM)
return err return err
} }
if memberInfo == nil { if mb == nil {
err = gerror.New("用户信息不存在") err = gerror.New("用户信息不存在")
return err return err
} }
if memberInfo.Mobile == in.Mobile { if mb.Mobile == in.Mobile {
err = gerror.New("新旧手机号不能一样") err = gerror.New("新旧手机号不能一样")
return return
} }
@ -249,10 +249,10 @@ func (s *sAdminMember) UpdateMobile(ctx context.Context, in adminin.MemberUpdate
} }
// 存在原绑定号码,需要进行验证 // 存在原绑定号码,需要进行验证
if memberInfo.Mobile != "" { if mb.Mobile != "" {
err = service.SysSmsLog().VerifyCode(ctx, sysin.VerifyCodeInp{ err = service.SysSmsLog().VerifyCode(ctx, sysin.VerifyCodeInp{
Event: consts.SmsTemplateBind, Event: consts.SmsTemplateBind,
Mobile: memberInfo.Mobile, Mobile: mb.Mobile,
Code: in.Code, Code: in.Code,
}) })
if err != nil { if err != nil {
@ -264,8 +264,7 @@ func (s *sAdminMember) UpdateMobile(ctx context.Context, in adminin.MemberUpdate
dao.AdminMember.Columns().Mobile: in.Mobile, dao.AdminMember.Columns().Mobile: in.Mobile,
} }
_, err = dao.AdminMember.Ctx(ctx).Where("id", memberId).Data(update).Update() if _, err = dao.AdminMember.Ctx(ctx).Where("id", memberId).Data(update).Update(); err != nil {
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM) err = gerror.Wrap(err, consts.ErrorORM)
return err return err
} }
@ -281,13 +280,13 @@ func (s *sAdminMember) UpdateProfile(ctx context.Context, in adminin.MemberUpdat
return err return err
} }
var memberInfo *entity.AdminMember var mb *entity.AdminMember
if err = dao.AdminMember.Ctx(ctx).Where("id", memberId).Scan(&memberInfo); err != nil { if err = dao.AdminMember.Ctx(ctx).Where("id", memberId).Scan(&mb); err != nil {
err = gerror.Wrap(err, consts.ErrorORM) err = gerror.Wrap(err, consts.ErrorORM)
return err return err
} }
if memberInfo == nil { if mb == nil {
err = gerror.New("用户信息不存在") err = gerror.New("用户信息不存在")
return err return err
} }
@ -302,8 +301,7 @@ func (s *sAdminMember) UpdateProfile(ctx context.Context, in adminin.MemberUpdat
dao.AdminMember.Columns().Address: in.Address, dao.AdminMember.Columns().Address: in.Address,
} }
_, err = dao.AdminMember.Ctx(ctx).Where("id", memberId).Data(update).Update() if _, err = dao.AdminMember.Ctx(ctx).Where("id", memberId).Data(update).Update(); err != nil {
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM) err = gerror.Wrap(err, consts.ErrorORM)
return err return err
} }
@ -718,7 +716,9 @@ func (s *sAdminMember) MemberLoginStat(ctx context.Context, in adminin.MemberLog
// GetIdByCode 通过邀请码获取用户ID // GetIdByCode 通过邀请码获取用户ID
func (s *sAdminMember) GetIdByCode(ctx context.Context, in adminin.GetIdByCodeInp) (res *adminin.GetIdByCodeModel, err error) { func (s *sAdminMember) GetIdByCode(ctx context.Context, in adminin.GetIdByCodeInp) (res *adminin.GetIdByCodeModel, err error) {
err = dao.AdminMember.Ctx(ctx).Fields(adminin.GetIdByCodeModel{}).Where("invite_code", in.Code).Scan(&res) if err = dao.AdminMember.Ctx(ctx).Fields(adminin.GetIdByCodeModel{}).Where("invite_code", in.Code).Scan(&res); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
}
return return
} }
@ -728,6 +728,9 @@ func (s *sAdminMember) Select(ctx context.Context, in adminin.MemberSelectInp) (
Fields("id as value,real_name as label,username,avatar"). Fields("id as value,real_name as label,username,avatar").
Handler(handler.FilterAuthWithField("id")). Handler(handler.FilterAuthWithField("id")).
Scan(&res) Scan(&res)
if err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
}
return return
} }

View File

@ -12,7 +12,6 @@ import (
"hotgo/internal/dao" "hotgo/internal/dao"
"hotgo/internal/library/token" "hotgo/internal/library/token"
"hotgo/internal/model" "hotgo/internal/model"
"hotgo/internal/model/do"
"hotgo/internal/model/entity" "hotgo/internal/model/entity"
"hotgo/internal/model/input/adminin" "hotgo/internal/model/input/adminin"
"hotgo/internal/model/input/sysin" "hotgo/internal/model/input/sysin"
@ -255,16 +254,6 @@ func (s *sAdminSite) handleLogin(ctx context.Context, mb *entity.AdminMember) (r
return nil, err return nil, err
} }
update := do.AdminMember{
LastActiveAt: user.LoginAt,
}
// 更新登录信息
if _, err = dao.AdminMember.Ctx(ctx).Data(update).Where(do.AdminMember{Id: mb.Id}).Update(); err != nil {
err = gerror.Wrap(err, consts.ErrorORM)
return
}
res = &adminin.LoginModel{ res = &adminin.LoginModel{
Username: user.Username, Username: user.Username,
Id: user.Id, Id: user.Id,

View File

@ -20,9 +20,9 @@ func (s *sHook) accessLog(r *ghttp.Request) {
if r.IsFileRequest() { if r.IsFileRequest() {
return return
} }
var ctx = contexts.Detach(r.Context()) var ctx = contexts.Detach(r.Context())
modelCtx := contexts.Get(ctx) if contexts.Get(ctx) == nil {
if modelCtx == nil {
return return
} }

View File

@ -79,7 +79,6 @@ func (s *sHook) lastAdminActive(r *ghttp.Request) {
_, err := g.Model("admin_member"). _, err := g.Model("admin_member").
Ctx(ctx). Ctx(ctx).
Where("id", member.Id). Where("id", member.Id).
WhereLT("last_active_at", gtime.Now()).
Data(g.Map{"last_active_at": gtime.Now()}). Data(g.Map{"last_active_at": gtime.Now()}).
Update() Update()
if err != nil { if err != nil {

View File

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

View File

@ -105,7 +105,7 @@ func (s *sSysLog) AutoLog(ctx context.Context) error {
var err error var err error
defer func() { defer func() {
if err != nil { if err != nil {
panic(err) g.Log().Error(ctx, "autoLog err:%+v", err)
} }
}() }()

View File

@ -75,7 +75,7 @@ func (s *sAuthClient) Start(ctx context.Context) {
// Stop 停止服务 // Stop 停止服务
func (s *sAuthClient) Stop(ctx context.Context) { func (s *sAuthClient) Stop(ctx context.Context) {
if s.client != nil { if s.client != nil && !s.client.IsStop() {
s.client.Stop() s.client.Stop()
g.Log().Debug(ctx, "AuthClient stop..") g.Log().Debug(ctx, "AuthClient stop..")
} }

View File

@ -76,7 +76,7 @@ func (s *sCronClient) Start(ctx context.Context) {
// Stop 停止服务 // Stop 停止服务
func (s *sCronClient) Stop(ctx context.Context) { func (s *sCronClient) Stop(ctx context.Context) {
if s.client != nil { if s.client != nil && !s.client.IsStop() {
s.client.Stop() s.client.Stop()
g.Log().Debug(ctx, "CronClient stop..") g.Log().Debug(ctx, "CronClient stop..")
} }

View File

@ -53,15 +53,17 @@ func (s *sTCPServer) Start(ctx context.Context) {
}) })
// 服务监听 // 服务监听
if err := s.serv.Listen(); err != nil { if err = s.serv.Listen(); err != nil {
if !s.serv.IsClose() {
g.Log().Warningf(ctx, "TCPServer Listen err:%v", err) g.Log().Warningf(ctx, "TCPServer Listen err:%v", err)
} }
}
}) })
} }
// Stop 关闭服务 // Stop 关闭服务
func (s *sTCPServer) Stop(ctx context.Context) { func (s *sTCPServer) Stop(ctx context.Context) {
if s.serv != nil { if s.serv != nil && !s.serv.IsClose() {
s.serv.Close() s.serv.Close()
g.Log().Debug(ctx, "TCPServer stop..") g.Log().Debug(ctx, "TCPServer stop..")
} }

View File

@ -2,11 +2,9 @@ package adminin
import ( import (
"context" "context"
"github.com/gogf/gf/v2/encoding/gbase64"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/os/gtime"
"hotgo/internal/consts" "hotgo/utility/simple"
"hotgo/utility/encrypt"
) )
// RegisterInp 账号注册 // RegisterInp 账号注册
@ -20,18 +18,11 @@ type RegisterInp struct {
func (in *RegisterInp) Filter(ctx context.Context) (err error) { func (in *RegisterInp) Filter(ctx context.Context) (err error) {
// 解密密码 // 解密密码
str, err := gbase64.Decode([]byte(in.Password)) password, err := simple.DecryptText(in.Password)
if err != nil { if err != nil {
return err return err
} }
str, err = encrypt.AesECBDecrypt(str, consts.RequestEncryptKey)
if err != nil {
return err
}
password := string(str)
if err = g.Validator().Data(password).Rules("password").Messages("密码长度在6~18之间").Run(ctx); err != nil { if err = g.Validator().Data(password).Rules("password").Messages("密码长度在6~18之间").Run(ctx); err != nil {
return return
} }

View File

@ -12,7 +12,7 @@
### 迁移或安装 ### 迁移或安装
1安装 HotGo (2.1.4及以上) 1安装 HotGo (@{.hgVersion}及以上)
项目介绍https://github.com/bufanyun/hotgo 项目介绍https://github.com/bufanyun/hotgo

View File

@ -18,20 +18,30 @@ import (
"hotgo/utility/encrypt" "hotgo/utility/encrypt"
) )
// DecryptText 解密文本
func DecryptText(text string) (string, error) {
str, err := gbase64.Decode([]byte(text))
if err != nil {
return "", err
}
str, err = encrypt.AesECBDecrypt(str, consts.RequestEncryptKey)
if err != nil {
return "", err
}
return string(str), nil
}
// CheckPassword 检查密码 // CheckPassword 检查密码
func CheckPassword(input, salt, hash string) (err error) { func CheckPassword(input, salt, hash string) (err error) {
// 解密密码 // 解密密码
password, err := gbase64.Decode([]byte(input)) password, err := DecryptText(input)
if err != nil { if err != nil {
return err return err
} }
password, err = encrypt.AesECBDecrypt(password, consts.RequestEncryptKey) if hash != gmd5.MustEncryptString(password+salt) {
if err != nil {
return err
}
if hash != gmd5.MustEncryptString(string(password)+salt) {
err = gerror.New("用户密码不正确") err = gerror.New("用户密码不正确")
return return
} }

View File

@ -1,6 +1,6 @@
<template> <template>
<QuillEditor <QuillEditor
ref="quillEditor" ref="quillEditorRef"
toolbar="full" toolbar="full"
v-model:content="content" v-model:content="content"
@ready="readyQuill" @ready="readyQuill"
@ -33,7 +33,7 @@
const emit = defineEmits(['update:value']); const emit = defineEmits(['update:value']);
const message = useMessage(); const message = useMessage();
const initFinish = ref(false); const initFinish = ref(false);
const quillEditor = ref(); const quillEditorRef = ref();
const content = ref(); const content = ref();
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
value: '', value: '',
@ -41,14 +41,14 @@
}); });
function readyQuill() { function readyQuill() {
quillEditor.value.setHTML(props.value); quillEditorRef.value.setHTML(props.value);
} }
watch( watch(
() => props.value, () => props.value,
(newValue) => { (newValue) => {
if (!initFinish.value) { if (!initFinish.value) {
quillEditor.value?.setHTML(newValue); quillEditorRef.value?.setHTML(newValue);
} }
}, },
{ {
@ -67,7 +67,7 @@
} }
function onUpdateContent() { function onUpdateContent() {
emit('update:value', quillEditor.value.getHTML()); emit('update:value', quillEditorRef.value.getHTML());
} }
function checkFileType(map: string[], fileType: string) { function checkFileType(map: string[], fileType: string) {