This commit is contained in:
孟帅
2023-07-20 18:01:10 +08:00
parent 9113fc5297
commit 373d9627fb
492 changed files with 12170 additions and 6982 deletions

View File

@@ -22,15 +22,16 @@
- [支付网关](sys-payment.md)
- [数据库](sys-db.md)
- [代码生成](sys-code.md)
- 定时任务
- [定时任务](sys-cron.md)
- [消息队列](sys-queue.md)
- [功能扩展库](sys-library.md)
- 工具方法
- [工具方法](sys-utility.md)
- RESTful Api
- Websocket服务器
- TCP服务器
- 单元测试
- [TCP服务器](sys-tcp-server.md)
- [单元测试](sys-test.md)
#### 插件模块开发
- [模块介绍及目录](addon-introduce-catalog.md)
@@ -38,16 +39,12 @@
- [模块辅助说明](addon-helper.md)
#### 实战开发
- 服务端
- web前端
### 前端开发
- [表单组件](web-form.md)
- Websocket客户端
- 工具库
- [独立部署](web-deploy.md)
#### 附录
- [网址收录](append-website.md)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -12,6 +12,19 @@
> 如果升级(覆盖)代码后打开会出现 sql 报错, 请检查更新的数据库格式或自行调整
### v2.8.4
updated 2023.07.22
- 增加:增加输入预处理中间件
- 增加:增加文件选择器
- 增加:增加在线服务监控,服务许可证管理
- 增加TCP服务器增加RPC路由消息注册、路由分发、拦截器注册更方便的进行应用开发
- 优化gf版本升级到v2.5.0
- 优化优化CURD代码生成简化控制器代码逻辑升级列表数据查询方式
- 修复:修复角色菜单子权限取消导致父级权限失效问题
- 修复:修复上传驱动路径错误
- 修复修复CURD代码生成时间类型字段默认值获取异常
### v2.7.6
updated 2023.06.19
@@ -19,7 +32,6 @@ updated 2023.06.19
- 修复:部门管理查询空指针问题
- 优化附件md5生成方式调整与更新前已上传的文件md5不兼容如果业务有影响请注意调整
### v2.7.3
updated 2023.05.14

View File

@@ -5,9 +5,11 @@
- 使用条件
- 生成配置
- 一个生成增删改查列表例子
- 内置gf-cli
- 多数据库生成配置
- 自定义生成模板
- 内置gf-cli
- 指定gf-cli版本
- 指定数据库驱动类型
> 在HotGo中可以通过后台开发工具快速的一键生成CRUD自动生成Api、控制器、业务逻辑、Web页面、表单组件、菜单权限等。
@@ -263,13 +265,6 @@ INSERT INTO `hg_test_table` (`id`, `category_id`, `title`, `description`, `conte
### 内置gf-cli
> 由于gf版本更新较常出现向下不兼容的情况所以我们为了保证生成代码的依赖稳定性我们将gf-cli工具内置到了系统中并做了一些在线执行的调整。
- 后续我们也将开放在线运行`gf gen dao``gf gen service`功能。在做插件开发时也会支持到在线生成插件下的service接口这将会使得插件开发更加方便
## 多数据库生成配置
#### 假设我们要增加一个库名为`hotgo2`、分组为`default2`的数据库,并要为其生成代码
@@ -339,3 +334,53 @@ hggen:
- 如果你在实际的开发过程中默认模板需要调整的地方较多时HotGo允许你新建新的模板分组来满足你的需求。新的模板可根据现有模板基础拷贝一份出来做改造默认模板目录[server/resource/generate/default](../../server/resource/generate/default)
### 内置gf-cli
> 为了确保生成代码的依赖稳定性,在面对`gf`版本更新可能导致向下不兼容情况时HotGo将`gf-cli`工具内置到系统中并进行在线执行调整,从而提供更可靠和一致的生成代码功能。
- 后续我们也将开放在线运行`gf gen ...`功能。在做插件开发时也会支持到在线生成插件下的service接口这将会使得插件开发更加方便
### 指定gf-cli版本
> HotGo多数情况下会和最新版本的gf-cli保持同步如果更新不及时或你不想使用最新版本的gf-cli来生成代码可以找到自己想要的版本进行替换即可。
- 下面大致做一些替换步骤说明:
1. 打开https://github.com/gogf/gf找到你想要使用的版本`clone`下来
2.`clone`代码中`gf/cmd/gf/internal/`目录覆盖到`server/internal/library/hggen/internal`
3. 将覆盖过来的目录文件中引入包名`github.com/gogf/gf/cmd/gf/v2/`批量改为`hotgo/internal/library/hggen/`
4. 运行`go mod tidy`
5. 运行`go run main.go`,如果没有报错,那么恭喜你已经完成了。如果有报错一般都是版本差异带来的影响,需要根据情况自行调整
### 指定数据库驱动类型
> HotGo默认使用mysql驱动如果你想用其他数据库驱动打开下方文件中注释即可
修改文件路径server/internal/library/hggen/internal/cmd/cmd_gen_dao.go
```go
package cmd
import (
//_ "github.com/gogf/gf/contrib/drivers/clickhouse/v2"
//_ "github.com/gogf/gf/contrib/drivers/mssql/v2"
_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
//_ "github.com/gogf/gf/contrib/drivers/oracle/v2"
//_ "github.com/gogf/gf/contrib/drivers/pgsql/v2"
//_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
"hotgo/internal/library/hggen/internal/cmd/gendao"
)
type (
cGenDao = gendao.CGenDao
)
```
修改完成后运行`go mod tidy`

View File

@@ -0,0 +1,71 @@
## 定时任务
目录
- 实现接口
- 一个例子
- 更多
> 在实际的项目开发中定时任务几乎成为不可或缺的一部分。HotGo为定时任务提供一个方便的后台操作界面让您能够轻松地进行在线启停、修改和立即执行等操作。这样的设计可以极大地改善您在使用定时任务过程中的体验让整个过程更加顺畅、高效。
### 实现接口
- 为了提供高度的扩展性,定时任务在设计上采用了接口化的思路。只需要实现以下接口,您就可以在任何地方注册和使用定时任务功能,从而实现更大的灵活性和可扩展性。
```go
// Cron 定时任务接口
type Cron interface {
// GetName 获取任务名称
GetName() string
// Execute 执行一次任务
Execute(ctx context.Context)
}
```
### 一个例子
定时任务的文件结构可以根据具体需要进行调整,以下是一个常见的参考结构:
- 文件路径server/internal/crons/test.go
```go
package crons
import (
"context"
"hotgo/internal/library/cron"
"time"
)
func init() {
cron.Register(Test)
}
// Test 测试任务(无参数)
var Test = &cTest{name: "test"}
type cTest struct {
name string
}
func (c *cTest) GetName() string {
return c.name
}
// Execute 执行任务
func (c *cTest) Execute(ctx context.Context) {
cron.Logger().Infof(ctx, "cron test Execute:%v", time.Now())
}
```
继续在后台系统设置-定时任务-添加任务,填写的任务名称需要和上面的名称保持一致,再进行简单的策略配置以后,一个后台可控的定时任务就添加好了!
### 更多
定时任务源码路径server/internal/library/cron/cron.go
更多文档请参考https://goframe.org/pages/viewpage.action?pageId=1114187

View File

@@ -33,6 +33,9 @@ func main() {
// 演示系統操作限制当开启演示模式时所有POST请求将被拒绝
service.Middleware().DemoLimit()
// 请求输入预处理api使用gf规范路由并且XxxReq结构体实现了validate.Filter接口即可隐式预处理
service.Middleware().PreFilter()
// HTTP响应预处理在业务处理完成后对响应结果进行格式化和错误过滤将处理后的数据发送给请求方
service.Middleware().ResponseHandler()
@@ -143,7 +146,6 @@ func main() {
2.`server/internal/logic/middleware/response.go`中根据请求的独有特征进行单独的处理兼容后续http处理。
#### 重写响应错误提示
- 在实际开发中我们可能想要隐藏一些敏感错误返回给客户端友好的错误提示但开发者同时又想需要看到真实的敏感错误。对此hotgo已经进行了过滤处理下面是一个简单的例子

View File

@@ -0,0 +1,271 @@
## TCP服务器
目录
- 配置文件
- 一个基本的消息收发例子
- 注册路由
- 拦截器
- 服务认证
- 更多
> HotGo基于GF框架的TCP服务器组件提供了一个简单而灵活的方式快速搭建基于TCP的服务应用。集成了许多常用功能如长连接、服务认证、路由分发、RPC消息、拦截器和数据绑定等大大简化和规范了服务器开发流程。
### 配置文件
- 配置文件server/manifest/config/config.yaml
```yaml
tcp:
# 服务器
server:
address: ":8099"
# 客户端
client:
# 定时任务
cron:
group: "cron" # 分组名称
name: "cron1" # 客户端名称
address: "127.0.0.1:8099" # 服务器地址
appId: "1002" # 应用名称
secretKey: "hotgo" # 密钥
# 系统授权
auth:
group: "auth" # 分组名称
name: "auth1" # 客户端名称
address: "127.0.0.1:8099" # 服务器地址
appId: "mengshuai" # 应用名称
secretKey: "123456" # 密钥
```
- 可以看到,除了服务器配置外,还有两个客户端配置`cron``auth`
- `cron`是HotGo内置的定时任务服务和http服务通过RPC通讯以实现和后台交互使其可以独立、集群部署。
- `auth`可以为第三方平台提供授权服务。如果你需要他,可以将它部署在第三方程序中,在重要的位置进行授权验证。
### 一个基本的消息收发测试用例
- 文件路径server/internal/library/network/tcp/tcp_example_test.go
```go
package tcp_test
import (
"context"
"fmt"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/test/gtest"
"hotgo/internal/library/network/tcp"
"testing"
"time"
)
var T *testing.T // 声明一个全局的 *testing.T 变量
type TestMsgReq struct {
Name string `json:"name"`
}
type TestMsgRes struct {
tcp.ServerRes
}
type TestRPCMsgReq struct {
Name string `json:"name"`
}
type TestRPCMsgRes struct {
tcp.ServerRes
}
func onTestMsg(ctx context.Context, req *TestMsgReq) {
fmt.Printf("服务器收到消息 ==> onTestMsg:%+v\n", req)
conn := tcp.ConnFromCtx(ctx)
gtest.C(T, func(t *gtest.T) {
t.AssertNE(conn, nil)
})
res := new(TestMsgRes)
res.Message = fmt.Sprintf("你的名字:%v", req.Name)
conn.Send(ctx, res)
}
func onResponseTestMsg(ctx context.Context, req *TestMsgRes) {
fmt.Printf("客户端收到响应消息 ==> TestMsgRes:%+v\n", req)
err := req.GetError()
gtest.C(T, func(t *gtest.T) {
t.AssertNil(err)
})
}
func onTestRPCMsg(ctx context.Context, req *TestRPCMsgReq) (res *TestRPCMsgRes, err error) {
fmt.Printf("服务器收到消息 ==> onTestRPCMsg:%+v\n", req)
res = new(TestRPCMsgRes)
res.Message = fmt.Sprintf("你的名字:%v", req.Name)
return
}
func startTCPServer() {
serv := tcp.NewServer(&tcp.ServerConfig{
Name: "hotgo",
Addr: ":8002",
})
// 注册路由
serv.RegisterRouter(
onTestMsg,
)
// 注册RPC路由
serv.RegisterRPCRouter(
onTestRPCMsg,
)
// 服务监听
err := serv.Listen()
gtest.C(T, func(t *gtest.T) {
t.AssertNil(err)
})
}
// 一个基本的消息收发
func TestSendMsg(t *testing.T) {
T = t
go startTCPServer()
ctx := gctx.New()
client := tcp.NewClient(&tcp.ClientConfig{
Addr: "127.0.0.1:8002",
})
// 注册路由
client.RegisterRouter(
onResponseTestMsg,
)
go func() {
err := client.Start()
gtest.C(T, func(t *gtest.T) {
t.AssertNil(err)
})
}()
// 确保服务都启动完成
time.Sleep(time.Second * 1)
// 拿到客户端的连接
conn := client.Conn()
gtest.C(T, func(t *gtest.T) {
t.AssertNE(conn, nil)
})
// 向服务器发送tcp消息不会阻塞程序执行
err := conn.Send(ctx, &TestMsgReq{Name: "Tom"})
gtest.C(T, func(t *gtest.T) {
t.AssertNil(err)
})
// 向服务器发送rpc消息会等待服务器响应结果直到拿到结果或响应超时才会继续
var res TestRPCMsgRes
if err = conn.RequestScan(ctx, &TestRPCMsgReq{Name: "Tony"}, &res); err != nil {
gtest.C(T, func(t *gtest.T) {
t.AssertNil(err)
})
}
fmt.Printf("客户端收到RPC消息响应 ==> TestRPCMsgRes:%+v\n", res)
time.Sleep(time.Second * 1)
}
```
### 注册路由
- 从上面的例子可以看到不管是普通TCP消息和RPC消息的请求/响应结构体都采用类似GF框架的规范路由的结构请求`XxxRes`/响应`XxxRes`的格式,是不是很亲切?
### 拦截器
- 不管是服务端还是客户端,在初始化时都可以注册多个拦截器来满足更多场景的服务开发,下面是一个使用例子:
```go
package main
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"hotgo/internal/library/network/tcp"
)
func main() {
serv = tcp.NewServer(&tcp.ServerConfig{
Name: "hotgo",
Addr: ":8002",
})
// 注册拦截器
// 执行顺序是从前到后即Interceptor -> Interceptor2 -> Interceptor3。如果中间有任意一个抛出错误则会中断后续处理
serv.RegisterInterceptor(Interceptor, Interceptor2, Interceptor3)
// 服务监听
if err := serv.Listen(); err != nil {
if !serv.IsClose() {
g.Log().Warningf(ctx, "TCPServer Listen err:%v", err)
}
}
}
func Interceptor(ctx context.Context, msg *tcp.Message) (err error) {
// 可以在拦截器中通过上下文拿到连接
conn := tcp.ConnFromCtx(ctx)
// 拿到原始请求消息
g.Dump(msg)
// 如果想要中断后续处理只需返回一个错误即可,但注意两种情况
// tcp消息如果你还想对该消息进行回复应在拦截器中进行处理例如conn.Send(ctx, 回复消息内容)
// rpc消息返回一个错误后系统会将错误自动回复到rpc响应中无需单独处理
return
}
func Interceptor2(ctx context.Context, msg *tcp.Message) (err error) {
// ...
return
}
func Interceptor3(ctx context.Context, msg *tcp.Message) (err error) {
// ...
return
}
```
### 服务认证
- 一般情况下,建议客户端连接到服务器时都通过`授权许可证`的方式进行登录认证,当初始化客户端配置认证数据时,连接成功后会自动进行登录认证。
```go
// 创建客户端配置
clientConfig := &tcp.ClientConfig{
Addr: "127.0.0.1:8002",
AutoReconnect: true,
// 认证数据
// 认证数据可以在后台-系统监控-在线服务-许可证列表中添加,同一个授权支持多个服务使用,但多个服务不能使用相同的名称进行连接
Auth: &tcp.AuthMeta{
Name: "服务名称",
Group: "服务分组",
AppId: "APPID",
SecretKey: "SecretKey",
},
}
// 初始化客户端
client = tcp.NewClient(clientConfig)
```
### 更多
TCP服务器源码路径server/internal/library/network/tcp
更多文档请参考https://goframe.org/pages/viewpage.action?pageId=1114625

View File

@@ -0,0 +1,3 @@
## 单元测试
请参考https://goframe.org/pages/viewpage.action?pageId=1114153

View File

@@ -0,0 +1,20 @@
## 工具方法
HotGo还提供一些系统中常用的工具库方法在这里简单说明
```
/server
├── utility
│ ├── charset # 字符串处理
│ ├── convert # 数据类型转换
│ ├── encrypt # 数据加密/解密
│ ├── excel # 电子表格导出/导入
│ ├── file # 文件/目录处理
│ ├── format # 数据格式化
│ ├── simple # 一些简捷函数
│ ├── tree # 树形结构
│ ├── url # URL处理
│ ├── useragent # 请求头代理处理
└── └── validate # 数据验证
```

View File

@@ -20,6 +20,7 @@
- 多图上传 UploadImage
- 单文件上传 UploadFile
- 多文件上传 UploadFile
- 文件选择器 FileChooser
- 开关 Switch
- 评分 Rate
- 省市区选择器 CitySelector
@@ -765,6 +766,35 @@ const value = ref(null);
</script>
```
### 文件选择器 FileChooser
- 基础用法
```vue
<template>
<FileChooser v-model:value="value" />
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import FileChooser from '@/components/FileChooser/index.vue';
const value = ref(null);
</script>
```
- 指定fileType支持多种选择器类型默认情况是全部都可以选择
```ts
type FileType = 'image' | 'doc' | 'audio' | 'video' | 'zip' | 'other' | 'default';
```
- 图片选择器
```vue
<FileChooser v-model:value="value" fileType="image" />
```
- 多选支持,指定`maxNumber`多选数量
```vue
<FileChooser v-model:value="value" :maxNumber="10" fileType="image" />
```
### 开关 Switch
```vue
<template>