mirror of
https://github.com/bufanyun/hotgo.git
synced 2025-08-24 23:38:35 +08:00
doc: 优化docsify文档体验
1.调整在docsify下代码块乱掉的问题 2.新增docsify下的go/yaml/vue/json/sql代码高亮插件 3.新增docsify下的mermaid图解析插件
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
|
||||
1、HotGo 后台进入 开发工具->插件管理->找到创建新插件,根据引导进行创建即可。
|
||||
|
||||
```
|
||||
```shell
|
||||
创建成功后默认情况下会在以下目录中生成插件文件,假设新生成的插件名为:hgexample
|
||||
|
||||
1. /server/addons/hgexample/ # 插件模块目录
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
2、创建插件完毕重启服务端后,插件管理中会出现你新创建的插件信息。操作栏有几个按钮,在此进行说明
|
||||
- 安装:会自动执行 server/hgexample/main.go 文件中的Install方法,方法中的具体逻辑默认为空,可以根据实际情况自行配置。如生成后台菜单、生成插件配置表初始化数据、迁移home页面、web项目文件等。
|
||||
```
|
||||
```go
|
||||
// Install 安装模块
|
||||
func (m *module) Install(ctx context.Context) (err error) {
|
||||
// ...
|
||||
@@ -38,7 +38,7 @@ func (m *module) Install(ctx context.Context) (err error) {
|
||||
```
|
||||
|
||||
- 更新:会自动执行 server/hgexample/main.go 文件中的Upgrade方法,方法中的具体逻辑默认为空,可以根据实际情况自行配置。
|
||||
```
|
||||
```go
|
||||
// Upgrade 更新模块
|
||||
func (m *module) Upgrade(ctx context.Context) (err error) {
|
||||
// ...
|
||||
@@ -47,7 +47,7 @@ func (m *module) Upgrade(ctx context.Context) (err error) {
|
||||
```
|
||||
|
||||
- 卸载:会自动执行 server/hgexample/main.go 文件中的UnInstall方法,方法中的具体逻辑默认为空,可以根据实际情况自行配置。如会清除所有的数据表和已安装的信息等。
|
||||
```
|
||||
```go
|
||||
// UnInstall 卸载模块
|
||||
func (m *module) UnInstall(ctx context.Context) (err error) {
|
||||
// ...
|
||||
|
@@ -9,6 +9,7 @@
|
||||
|
||||
#### 模块结构
|
||||
- 文件路径:server/internal/library/addons/module.go
|
||||
|
||||
```go
|
||||
// Skeleton 模块骨架
|
||||
type Skeleton struct {
|
||||
@@ -43,6 +44,7 @@ type Module interface {
|
||||
#### 获取模块信息
|
||||
|
||||
- 在插件模块内
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
@@ -57,6 +59,7 @@ func test() {
|
||||
```
|
||||
|
||||
- 在插件模块外
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
|
@@ -69,7 +69,7 @@ gfcli:
|
||||
三、 启动服务
|
||||
|
||||
1、服务端:
|
||||
```shell script
|
||||
```shell
|
||||
cd server
|
||||
|
||||
# 设置国内代理,如果已经设置好了代理可以跳过
|
||||
@@ -86,7 +86,7 @@ gfcli:
|
||||
```
|
||||
|
||||
2、web前端:
|
||||
```shell script
|
||||
```shell
|
||||
cd web
|
||||
# 首先确定你以安装node16.0以上版本并安装了包[npm、pnpm],否则可能会出现一些未知报错
|
||||
|
||||
|
@@ -1,149 +1,150 @@
|
||||
## 数据库
|
||||
|
||||
目录
|
||||
|
||||
- 字段类型
|
||||
- 特殊字段默认表单组件
|
||||
- 特殊字段默认表单验证器
|
||||
- SQL默认查询方式
|
||||
- 其他
|
||||
|
||||
### 字段类型
|
||||
|
||||
- 创建数据库表当按如下的规则进行字段命名、类型、属性设置和备注后,再生成CRUD代码时会自动生成对应的Api、控制器、业务逻辑、Web页面、[表单组件](web-form.md)等的一些默认属性
|
||||
- 当你了解这些默认技巧后,会有效提高你在实际开发中的生产效率
|
||||
|
||||
| 数据库类型 | 额外属性 | 转换Go类型 | 转换Ts类型 | 表单组件 |
|
||||
|---------------------------------------------------------------|--------------|--------------|---------|-----------------------|
|
||||
| int, tinyint,small_int,smallint,medium_int,mediumint,serial | / | int | number | InputNumber(数字输入框) |
|
||||
| int, tinyint,small_int,smallint,medium_int,mediumint,serial | unsigned | uint | number | InputNumber(数字输入框) |
|
||||
| big_int,bigint,bigserial | / | int64 | number | InputNumber(数字输入框) |
|
||||
| big_int,bigint,bigserial | unsigned | uint64 | number | InputNumber(数字输入框) |
|
||||
| real | / | float32 | number | InputNumber(数字输入框) |
|
||||
| float,double,decimal,money,numeric,smallmoney | / | float64 | number | InputNumber(数字输入框) |
|
||||
| bit(1) 、bit(true)、bit(false) | / | bool | boolean | Input(文本输入框,默认) |
|
||||
| bit | / | int64-bytes | array | InputDynamic(动态KV表单) |
|
||||
| bit | unsigned | uint64-bytes | array | InputDynamic (动态KV表单) |
|
||||
| bool | / | bool | boolean | Input(文本输入框,默认) |
|
||||
| date | / | *gtime.Time | string | Date(日期选择器) |
|
||||
| datetime,timestamp,timestamptz | / | *gtime.Time | string | Time(时间选择器) |
|
||||
| json | / | *gjson.Json | string | Input(文本输入框) |
|
||||
| jsonb | / | *gjson.Json | string | Input(文本输入框) |
|
||||
| 以下为物理类型中包含字段部分时的转换方式,默认情况 | / | / | / | / |
|
||||
| text,char,character | / | string | string | Input(文本输入框) |
|
||||
| float,double,numeric | / | string | string | Input(文本输入框) |
|
||||
| bool | / | bool | boolean | Input(文本输入框,默认) |
|
||||
| binary,blob | / | []byte | string | Input(文本输入框,默认) |
|
||||
| int | / | int | number | InputNumber(数字输入框) |
|
||||
| int | unsigned | int | number | InputNumber(数字输入框) |
|
||||
| time | / | *gtime.Time | string | Time(时间选择器) |
|
||||
| date | / | *gtime.Time | string | Date(日期选择器) |
|
||||
| 没有满足以上任何条件的 | / | string | string | Input(文本输入框) |
|
||||
|
||||
|
||||
### 特殊字段默认表单组件
|
||||
- 以下字段在不设置表单组件时会默认使用的表单组件
|
||||
|
||||
| 数据库字段 | 字段名称 | 表单组件 |
|
||||
|--------------|----------------------|----------------------|
|
||||
| status | 状态字段(任意int类型) | Select (单选下拉框) |
|
||||
| created_at | 创建时间字段 | TimeRange (时间范围选择) |
|
||||
| province_id | 省份ID字段(任意int类型) | CitySelector (省市区选择) |
|
||||
| city_id | 城市ID字段(任意int类型) | CitySelector (省市区选择) |
|
||||
| 任意字串符字段 | 长度>= 200 and <= 500 | InputTextarea (文本域) |
|
||||
| 任意字串符字段 | 长度> 500 | InputEditor (富文本) |
|
||||
|
||||
|
||||
### 特殊字段默认表单验证器
|
||||
- 以下字段在不设置表单组件时会默认使用的表单验证器
|
||||
|
||||
| 数据库字段/Go类型 | 字段名称 | 表单验证规则 |
|
||||
|-------------------|--------|-----------------------|
|
||||
| mobile | 手机号 | 不为空时必须是手机号码(国内) |
|
||||
| qq | QQ | 不为空时必须是QQ号码 |
|
||||
| email | 邮箱地址 | 不为空时必须是邮箱格式 |
|
||||
| id_card | 身份证号码 | 不为空时必须是15或18位身份证号码 |
|
||||
| bank_card | 银行卡号码 | 银行卡号码 |
|
||||
| password | 密码 | 密码验证,必须包含6-18为字母和数字 |
|
||||
| price | 价格 | 金额验证,最多允许输入10位整数及2位小数 |
|
||||
| Go类型为uint、uint64 | 正整数 | 非零正整数验证 |
|
||||
|
||||
### SQL默认查询方式
|
||||
- Go类型取决于数据库物理类型,请参考 [字段类型] 部分
|
||||
|
||||
| Go类型 | 查询方式 |
|
||||
|-------------------------|--------------------------------------|
|
||||
| string | LIKE |
|
||||
| date,datetime | = |
|
||||
| int,uint,int64,uint64 | = |
|
||||
| []int,[]int64,[]uint64 | IN (...) |
|
||||
| float32,float64 | = |
|
||||
| []byte4 | =(默认) |
|
||||
| time.Time,*gtime.Time | = |
|
||||
| *gjson.Json | JSON_CONTAINS(json_doc, val[, path]) |
|
||||
|
||||
|
||||
|
||||
### 其他
|
||||
|
||||
#### 默认字典选项
|
||||
|
||||
- 数据库字段为 `status`且类型为任意数字类型的会使用系统默认的状态字典
|
||||
|
||||
#### 默认属性
|
||||
|
||||
- 默认必填,当数据库字段存在非空`IS_NULLABLE`属性时,默认勾选必填验证
|
||||
- 默认唯一,当数据库字段索引存在`UNI`时,默认勾选唯一值验证
|
||||
- 默认主键,当数据库字段索引存在`PRI`时,默认为主键,不允许编辑
|
||||
- 默认排序,当数据库字段存在`sort`时,默认开启排序,添加表单自动获取最大排序增量值并填充表单
|
||||
- 默认列名,默认使用字段注释作为表格的列名。当数据库字段未设置注释时,默认使用字段名称作为列名
|
||||
|
||||
#### 自动更新/插入
|
||||
|
||||
- 自动更新,当数据库字段为`updated_at`(更新时间),`updated_by`(更新者)
|
||||
- 自动插入,当数据库字段为`created_at`(创建时间),`created_by`(创建者)
|
||||
- 软删除,表存在字段`deleted_at`时,使用表的Orm模型查询条件将会自动加入[ `deleted_at` IS NULL ],删除时只更新删除时间而不会真的删除数据
|
||||
- 树表:不论更新插入,都会根据表中字段`pid`(上级ID)自动维护`level`(树等级)和`tree`(关系树)
|
||||
|
||||
|
||||
#### 操作人字段维护
|
||||
|
||||
- 生成列表中存在并且勾选展示字段`created_by`(创建者)、`updated_by`(修改者)、`deleted_by`(删除者)时,会自动到表`hg_admin_member`中获取操作人的基本信息摘要,并渲染到列表中,效果如下:
|
||||
|
||||

|
||||
|
||||
- 生成列表中存在并且勾选查询字段`created_by`(创建者)、`updated_by`(修改者)、`deleted_by`(删除者)时,会强制将查询表单改为关键词查询,从`hg_admin_member`查询操作人。效果如下:
|
||||
|
||||

|
||||
|
||||
- 查询代码片段,参考路径:[server/internal/logic/admin/member.go](../../server/internal/logic/admin/member.go)
|
||||
```go
|
||||
|
||||
// 查询创建者
|
||||
if in.CreatedBy != "" {
|
||||
ids, err := service.AdminMember().GetIdsByKeyword(ctx, in.CreatedBy)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
mod = mod.WhereIn(dao.SysGenCurdDemo.Columns().CreatedBy, ids)
|
||||
}
|
||||
|
||||
// GetIdsByKeyword 根据关键词查找符合条件的用户ID
|
||||
func (s *sAdminMember) GetIdsByKeyword(ctx context.Context, ks string) (res []int64, err error) {
|
||||
ks = gstr.Trim(ks)
|
||||
if len(ks) == 0 {
|
||||
return
|
||||
}
|
||||
array, err := dao.AdminMember.Ctx(ctx).Fields("id").
|
||||
Where("`id` = ? or `real_name` = ? or `username` = ? or `mobile` = ?", ks, ks, ks, ks).
|
||||
Array()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, "根据关键词获取用户ID失败,请稍后重试!")
|
||||
}
|
||||
res = gvar.New(array).Int64s()
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
> 这里只列举了较为常用的默认规则,其他更多默认规则请参考:[server/internal/library/hggen/views/column_default.go](../../server/internal/library/hggen/views/column_default.go)
|
||||
## 数据库
|
||||
|
||||
目录
|
||||
|
||||
- 字段类型
|
||||
- 特殊字段默认表单组件
|
||||
- 特殊字段默认表单验证器
|
||||
- SQL默认查询方式
|
||||
- 其他
|
||||
|
||||
### 字段类型
|
||||
|
||||
- 创建数据库表当按如下的规则进行字段命名、类型、属性设置和备注后,再生成CRUD代码时会自动生成对应的Api、控制器、业务逻辑、Web页面、[表单组件](web-form.md)等的一些默认属性
|
||||
- 当你了解这些默认技巧后,会有效提高你在实际开发中的生产效率
|
||||
|
||||
| 数据库类型 | 额外属性 | 转换Go类型 | 转换Ts类型 | 表单组件 |
|
||||
|---------------------------------------------------------------|--------------|--------------|---------|-----------------------|
|
||||
| int, tinyint,small_int,smallint,medium_int,mediumint,serial | / | int | number | InputNumber(数字输入框) |
|
||||
| int, tinyint,small_int,smallint,medium_int,mediumint,serial | unsigned | uint | number | InputNumber(数字输入框) |
|
||||
| big_int,bigint,bigserial | / | int64 | number | InputNumber(数字输入框) |
|
||||
| big_int,bigint,bigserial | unsigned | uint64 | number | InputNumber(数字输入框) |
|
||||
| real | / | float32 | number | InputNumber(数字输入框) |
|
||||
| float,double,decimal,money,numeric,smallmoney | / | float64 | number | InputNumber(数字输入框) |
|
||||
| bit(1) 、bit(true)、bit(false) | / | bool | boolean | Input(文本输入框,默认) |
|
||||
| bit | / | int64-bytes | array | InputDynamic(动态KV表单) |
|
||||
| bit | unsigned | uint64-bytes | array | InputDynamic (动态KV表单) |
|
||||
| bool | / | bool | boolean | Input(文本输入框,默认) |
|
||||
| date | / | *gtime.Time | string | Date(日期选择器) |
|
||||
| datetime,timestamp,timestamptz | / | *gtime.Time | string | Time(时间选择器) |
|
||||
| json | / | *gjson.Json | string | Input(文本输入框) |
|
||||
| jsonb | / | *gjson.Json | string | Input(文本输入框) |
|
||||
| 以下为物理类型中包含字段部分时的转换方式,默认情况 | / | / | / | / |
|
||||
| text,char,character | / | string | string | Input(文本输入框) |
|
||||
| float,double,numeric | / | string | string | Input(文本输入框) |
|
||||
| bool | / | bool | boolean | Input(文本输入框,默认) |
|
||||
| binary,blob | / | []byte | string | Input(文本输入框,默认) |
|
||||
| int | / | int | number | InputNumber(数字输入框) |
|
||||
| int | unsigned | int | number | InputNumber(数字输入框) |
|
||||
| time | / | *gtime.Time | string | Time(时间选择器) |
|
||||
| date | / | *gtime.Time | string | Date(日期选择器) |
|
||||
| 没有满足以上任何条件的 | / | string | string | Input(文本输入框) |
|
||||
|
||||
|
||||
### 特殊字段默认表单组件
|
||||
- 以下字段在不设置表单组件时会默认使用的表单组件
|
||||
|
||||
| 数据库字段 | 字段名称 | 表单组件 |
|
||||
|--------------|----------------------|----------------------|
|
||||
| status | 状态字段(任意int类型) | Select (单选下拉框) |
|
||||
| created_at | 创建时间字段 | TimeRange (时间范围选择) |
|
||||
| province_id | 省份ID字段(任意int类型) | CitySelector (省市区选择) |
|
||||
| city_id | 城市ID字段(任意int类型) | CitySelector (省市区选择) |
|
||||
| 任意字串符字段 | 长度>= 200 and <= 500 | InputTextarea (文本域) |
|
||||
| 任意字串符字段 | 长度> 500 | InputEditor (富文本) |
|
||||
|
||||
|
||||
### 特殊字段默认表单验证器
|
||||
- 以下字段在不设置表单组件时会默认使用的表单验证器
|
||||
|
||||
| 数据库字段/Go类型 | 字段名称 | 表单验证规则 |
|
||||
|-------------------|--------|-----------------------|
|
||||
| mobile | 手机号 | 不为空时必须是手机号码(国内) |
|
||||
| qq | QQ | 不为空时必须是QQ号码 |
|
||||
| email | 邮箱地址 | 不为空时必须是邮箱格式 |
|
||||
| id_card | 身份证号码 | 不为空时必须是15或18位身份证号码 |
|
||||
| bank_card | 银行卡号码 | 银行卡号码 |
|
||||
| password | 密码 | 密码验证,必须包含6-18为字母和数字 |
|
||||
| price | 价格 | 金额验证,最多允许输入10位整数及2位小数 |
|
||||
| Go类型为uint、uint64 | 正整数 | 非零正整数验证 |
|
||||
|
||||
### SQL默认查询方式
|
||||
- Go类型取决于数据库物理类型,请参考 [字段类型] 部分
|
||||
|
||||
| Go类型 | 查询方式 |
|
||||
|-------------------------|--------------------------------------|
|
||||
| string | LIKE |
|
||||
| date,datetime | = |
|
||||
| int,uint,int64,uint64 | = |
|
||||
| []int,[]int64,[]uint64 | IN (...) |
|
||||
| float32,float64 | = |
|
||||
| []byte4 | =(默认) |
|
||||
| time.Time,*gtime.Time | = |
|
||||
| *gjson.Json | JSON_CONTAINS(json_doc, val[, path]) |
|
||||
|
||||
|
||||
|
||||
### 其他
|
||||
|
||||
#### 默认字典选项
|
||||
|
||||
- 数据库字段为 `status`且类型为任意数字类型的会使用系统默认的状态字典
|
||||
|
||||
#### 默认属性
|
||||
|
||||
- 默认必填,当数据库字段存在非空`IS_NULLABLE`属性时,默认勾选必填验证
|
||||
- 默认唯一,当数据库字段索引存在`UNI`时,默认勾选唯一值验证
|
||||
- 默认主键,当数据库字段索引存在`PRI`时,默认为主键,不允许编辑
|
||||
- 默认排序,当数据库字段存在`sort`时,默认开启排序,添加表单自动获取最大排序增量值并填充表单
|
||||
- 默认列名,默认使用字段注释作为表格的列名。当数据库字段未设置注释时,默认使用字段名称作为列名
|
||||
|
||||
#### 自动更新/插入
|
||||
|
||||
- 自动更新,当数据库字段为`updated_at`(更新时间),`updated_by`(更新者)
|
||||
- 自动插入,当数据库字段为`created_at`(创建时间),`created_by`(创建者)
|
||||
- 软删除,表存在字段`deleted_at`时,使用表的Orm模型查询条件将会自动加入[ `deleted_at` IS NULL ],删除时只更新删除时间而不会真的删除数据
|
||||
- 树表:不论更新插入,都会根据表中字段`pid`(上级ID)自动维护`level`(树等级)和`tree`(关系树)
|
||||
|
||||
|
||||
#### 操作人字段维护
|
||||
|
||||
- 生成列表中存在并且勾选展示字段`created_by`(创建者)、`updated_by`(修改者)、`deleted_by`(删除者)时,会自动到表`hg_admin_member`中获取操作人的基本信息摘要,并渲染到列表中,效果如下:
|
||||
|
||||

|
||||
|
||||
- 生成列表中存在并且勾选查询字段`created_by`(创建者)、`updated_by`(修改者)、`deleted_by`(删除者)时,会强制将查询表单改为关键词查询,从`hg_admin_member`查询操作人。效果如下:
|
||||
|
||||

|
||||
|
||||
- 查询代码片段,参考路径:[server/internal/logic/admin/member.go](../../server/internal/logic/admin/member.go)
|
||||
|
||||
```go
|
||||
|
||||
// 查询创建者
|
||||
if in.CreatedBy != "" {
|
||||
ids, err := service.AdminMember().GetIdsByKeyword(ctx, in.CreatedBy)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
mod = mod.WhereIn(dao.SysGenCurdDemo.Columns().CreatedBy, ids)
|
||||
}
|
||||
|
||||
// GetIdsByKeyword 根据关键词查找符合条件的用户ID
|
||||
func (s *sAdminMember) GetIdsByKeyword(ctx context.Context, ks string) (res []int64, err error) {
|
||||
ks = gstr.Trim(ks)
|
||||
if len(ks) == 0 {
|
||||
return
|
||||
}
|
||||
array, err := dao.AdminMember.Ctx(ctx).Fields("id").
|
||||
Where("`id` = ? or `real_name` = ? or `username` = ? or `mobile` = ?", ks, ks, ks, ks).
|
||||
Array()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, "根据关键词获取用户ID失败,请稍后重试!")
|
||||
}
|
||||
res = gvar.New(array).Int64s()
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
> 这里只列举了较为常用的默认规则,其他更多默认规则请参考:[server/internal/library/hggen/views/column_default.go](../../server/internal/library/hggen/views/column_default.go)
|
||||
|
@@ -63,16 +63,17 @@
|
||||
#### import
|
||||
* 单行import不建议用圆括号包裹
|
||||
* 按照`官方包`,NEW LINE,`当前工程包`,NEW LINE,`第三方依赖包`顺序引入
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
"string"
|
||||
|
||||
"greet/user/internal/config"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
```
|
||||
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
"string"
|
||||
|
||||
"greet/user/internal/config"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
```
|
||||
|
||||
#### 函数返回
|
||||
* 对象避免非指针返回
|
||||
@@ -84,7 +85,8 @@
|
||||
|
||||
#### 函数体编码
|
||||
* 建议一个block结束空一行,如if、for等
|
||||
```go
|
||||
|
||||
```go
|
||||
func main (){
|
||||
if x==1{
|
||||
// do something
|
||||
@@ -92,15 +94,16 @@
|
||||
|
||||
fmt.println("xxx")
|
||||
}
|
||||
```
|
||||
```
|
||||
* return前尽可能空一行
|
||||
```go
|
||||
func getUser(id string)(string,error){
|
||||
....
|
||||
|
||||
return "xx",nil
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
func getUser(id string)(string,error){
|
||||
....
|
||||
|
||||
return "xx",nil
|
||||
}
|
||||
```
|
||||
|
||||
### 框架规范
|
||||
|
||||
|
@@ -1,316 +1,318 @@
|
||||
## 功能扩展库
|
||||
|
||||
目录
|
||||
|
||||
- 缓存驱动
|
||||
- 请求上下文
|
||||
- JWT
|
||||
- 数据字典
|
||||
- 地理定位(待写)
|
||||
- 通知(待写)
|
||||
|
||||
|
||||
### 缓存驱动
|
||||
|
||||
> 系统默认的缓存驱动为file,目前已支持:memory|redis|file等多种驱动。请自行选择适合你的驱动使用。
|
||||
|
||||
- 配置文件:server/manifest/config/config.yaml
|
||||
|
||||
```yaml
|
||||
#缓存
|
||||
cache:
|
||||
adapter: "file" # 缓存驱动方式,支持:memory|redis|file,不填默认memory
|
||||
fileDir: "./storage/cache" # 文件缓存路径,adapter=file时必填
|
||||
```
|
||||
|
||||
#### 使用方式
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"hotgo/internal/library/cache"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
)
|
||||
|
||||
func test() {
|
||||
ctx := gctx.New()
|
||||
|
||||
// 添加/修改
|
||||
cache.Instance().Set(ctx, "qwe", 123, 0)
|
||||
|
||||
// 查询
|
||||
cache.Instance().Get(ctx, "qwe")
|
||||
|
||||
// 删除
|
||||
cache.Instance().Remove(ctx, "qwe")
|
||||
|
||||
// 更多方法请参考:https://goframe.org/pages/viewpage.action?pageId=27755640
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 请求上下文
|
||||
|
||||
- 主要用于在处理HTTP和websocket请求时通过中间件将用户、应用、插件等信息绑定到上下文中,方便在做业务处理时用到这些信息
|
||||
|
||||
```go
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"context"
|
||||
"hotgo/internal/library/contexts"
|
||||
"hotgo/internal/library/addons"
|
||||
)
|
||||
|
||||
|
||||
func test(ctx context.Context) {
|
||||
// 获取当前请求的所有上下文变量
|
||||
var ctxModel = contexts.Get(ctx)
|
||||
fmt.Printf("当前请求的所有上下文变量:%+v\n", ctxModel)
|
||||
|
||||
// 获取当前请求的应用模块
|
||||
var module = contexts.GetModule(ctx)
|
||||
fmt.Printf("当前请求的应用:%+v\n", module)
|
||||
|
||||
// 获取当前请求的用户信息
|
||||
var member = contexts.GetUser(ctx)
|
||||
fmt.Printf("当前访问用户信息:%+v\n", member)
|
||||
|
||||
// 获取当前请求的插件模块
|
||||
fmt.Printf("当前是否为插件请求:%v", contexts.IsAddonRequest(ctx))
|
||||
if contexts.IsAddonRequest(ctx) {
|
||||
fmt.Printf("当前插件名称:%v", contexts.GetAddonName(ctx))
|
||||
fmt.Printf("当前插件信息:%v", addons.GetModule(contexts.GetAddonName(ctx)))
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### JWT
|
||||
|
||||
- 基于jwt+缓存驱动实现的用户登录令牌功能,支持自动续约,解决了jwt服务端无法退出问题和jwt令牌无法主动失效问题
|
||||
|
||||
#### 配置示例
|
||||
```yaml
|
||||
# 登录令牌
|
||||
token:
|
||||
secretKey: "hotgo123" # 令牌加密秘钥,考虑安全问题生产环境中请修改默认值
|
||||
expires: 604800 # 令牌有效期,单位:秒。默认7天
|
||||
autoRefresh: true # 是否开启自动刷新过期时间, false|true 默认为true
|
||||
refreshInterval: 86400 # 刷新间隔,单位:秒。必须小于expires,否则无法触发。默认1天内只允许刷新一次
|
||||
maxRefreshTimes: 30 # 最大允许刷新次数,-1不限制。默认30次
|
||||
multiLogin: true # 是否允许多端登录, false|true 默认为true
|
||||
|
||||
```
|
||||
|
||||
```go
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"context"
|
||||
"hotgo/internal/library/token"
|
||||
"hotgo/internal/model"
|
||||
)
|
||||
|
||||
|
||||
func test(ctx context.Context) {
|
||||
// 登录
|
||||
user := &model.Identity{
|
||||
Id: mb.Id,
|
||||
Pid: mb.Pid,
|
||||
DeptId: mb.DeptId,
|
||||
RoleId: ro.Id,
|
||||
RoleKey: ro.Key,
|
||||
Username: mb.Username,
|
||||
RealName: mb.RealName,
|
||||
Avatar: mb.Avatar,
|
||||
Email: mb.Email,
|
||||
Mobile: mb.Mobile,
|
||||
App: consts.AppAdmin,
|
||||
LoginAt: gtime.Now(),
|
||||
}
|
||||
|
||||
loginToken, expires, err := token.Login(ctx, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// gf请求对象
|
||||
r := *ghttp.Request
|
||||
|
||||
// 获取登录用户信息
|
||||
user, err := token.ParseLoginUser(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 注销登录
|
||||
err = token.Logout(r)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 数据字典
|
||||
|
||||
- hotgo增加了对枚举字典和自定义方法字典的内置支持,从而在系统中经常使用的一些特定数据上做出了增强。
|
||||
|
||||
#### 字典数据选项
|
||||
- 文件路径:server/internal/model/dict.go
|
||||
```go
|
||||
package model
|
||||
|
||||
// Option 字典数据选项
|
||||
type Option struct {
|
||||
Key interface{} `json:"key"`
|
||||
Label string `json:"label" description:"字典标签"`
|
||||
Value interface{} `json:"value" description:"字典键值"`
|
||||
ValueType string `json:"valueType" description:"键值数据类型"`
|
||||
Type string `json:"type" description:"字典类型"`
|
||||
ListClass string `json:"listClass" description:"表格回显样式"`
|
||||
}
|
||||
```
|
||||
|
||||
#### 枚举字典
|
||||
- 适用于系统开发期间内置的枚举数据,这样即维护了枚举值,又关联了数据字典
|
||||
|
||||
##### 一个例子
|
||||
- 定义枚举值和字典数据选项,并注册字典类型
|
||||
- 文件路径:server/internal/consts/credit_log.go
|
||||
|
||||
```go
|
||||
package consts
|
||||
|
||||
import (
|
||||
"hotgo/internal/library/dict"
|
||||
"hotgo/internal/model"
|
||||
)
|
||||
|
||||
func init() {
|
||||
dict.RegisterEnums("creditType", "资金变动类型", CreditTypeOptions)
|
||||
dict.RegisterEnums("creditGroup", "资金变动分组", CreditGroupOptions)
|
||||
}
|
||||
|
||||
const (
|
||||
CreditTypeBalance = "balance" // 余额
|
||||
CreditTypeIntegral = "integral" // 积分
|
||||
)
|
||||
|
||||
const (
|
||||
CreditGroupDecr = "decr" // 扣款
|
||||
CreditGroupIncr = "incr" // 加款
|
||||
CreditGroupOpDecr = "op_decr" // 操作扣款
|
||||
CreditGroupOpIncr = "op_incr" // 操作加款
|
||||
CreditGroupBalanceRecharge = "balance_recharge" // 余额充值
|
||||
CreditGroupBalanceRefund = "balance_refund" // 余额退款
|
||||
CreditGroupApplyCash = "apply_cash" // 申请提现
|
||||
)
|
||||
|
||||
// CreditTypeOptions 变动类型
|
||||
var CreditTypeOptions = []*model.Option{
|
||||
dict.GenSuccessOption(CreditTypeBalance, "余额"),
|
||||
dict.GenInfoOption(CreditTypeIntegral, "积分"),
|
||||
}
|
||||
|
||||
// CreditGroupOptions 变动分组
|
||||
var CreditGroupOptions = []*model.Option{
|
||||
dict.GenWarningOption(CreditGroupDecr, "扣款"),
|
||||
dict.GenSuccessOption(CreditGroupIncr, "加款"),
|
||||
dict.GenWarningOption(CreditGroupOpDecr, "操作扣款"),
|
||||
dict.GenSuccessOption(CreditGroupOpIncr, "操作加款"),
|
||||
dict.GenWarningOption(CreditGroupBalanceRefund, "余额退款"),
|
||||
dict.GenSuccessOption(CreditGroupBalanceRecharge, "余额充值"),
|
||||
dict.GenInfoOption(CreditGroupApplyCash, "申请提现"),
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
#### 自定义方法字典
|
||||
- 适用于非固定选项,如数据是从某个表/文件读取或从第三方读取,数据需要进行转换时使用
|
||||
|
||||
##### 方法字典接口
|
||||
- 文件路径:server/internal/consts/credit_log.go
|
||||
```go
|
||||
package dict
|
||||
|
||||
// FuncDict 方法字典,实现本接口即可使用内置方法字典
|
||||
type FuncDict func(ctx context.Context) (res []*model.Option, err error)
|
||||
```
|
||||
|
||||
##### 一个例子
|
||||
- 定义获取字典数据方法,并注册字典类型
|
||||
- 文件路径:server/internal/logic/admin/post.go
|
||||
|
||||
```go
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/dict"
|
||||
"hotgo/internal/model"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/service"
|
||||
)
|
||||
|
||||
type sAdminPost struct{}
|
||||
|
||||
func NewAdminPost() *sAdminPost {
|
||||
return &sAdminPost{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterAdminPost(NewAdminPost())
|
||||
dict.RegisterFunc("adminPostOption", "岗位选项", service.AdminPost().Option)
|
||||
}
|
||||
|
||||
// Option 岗位选项
|
||||
func (s *sAdminPost) Option(ctx context.Context) (opts []*model.Option, err error) {
|
||||
var list []*entity.AdminPost
|
||||
if err = dao.AdminPost.Ctx(ctx).OrderAsc(dao.AdminPost.Columns().Sort).Scan(&list); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(list) == 0 {
|
||||
opts = make([]*model.Option, 0)
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range list {
|
||||
opts = append(opts, dict.GenHashOption(v.Id, v.Name))
|
||||
}
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
#### 代码生成支持
|
||||
- 内置的枚举字典和自定义方法字典在生成代码时可以直接进行选择,生成代码格式和系统字典管理写法一致
|
||||
|
||||

|
||||
|
||||
|
||||
#### 内置字典和系统字典的区分
|
||||
|
||||
##### 主要区别
|
||||
- 系统字典由表:`hg_sys_dict_type`和`hg_sys_dict_data`共同进行维护,使用时需通过后台到字典管理中进行添加
|
||||
- 内置字典是系统开发期间在代码层面事先定义和注册好的数据选项
|
||||
|
||||
|
||||
##### 数据格式区别
|
||||
- 系统字典所有ID都是大于0的int64类型
|
||||
- 内置字典ID都是小于0的int64类型。枚举字典以20000开头,如:-200001381053496;方法字典以30000开头,如:-30000892528327;开头以外数字是根据数据选项的`key`值进行哈希算法得出
|
||||
|
||||
### 地理定位
|
||||
```go
|
||||
// 待写
|
||||
```
|
||||
|
||||
### 通知
|
||||
```go
|
||||
// 待写
|
||||
## 功能扩展库
|
||||
|
||||
目录
|
||||
|
||||
- 缓存驱动
|
||||
- 请求上下文
|
||||
- JWT
|
||||
- 数据字典
|
||||
- 地理定位(待写)
|
||||
- 通知(待写)
|
||||
|
||||
|
||||
### 缓存驱动
|
||||
|
||||
> 系统默认的缓存驱动为file,目前已支持:memory|redis|file等多种驱动。请自行选择适合你的驱动使用。
|
||||
|
||||
- 配置文件:server/manifest/config/config.yaml
|
||||
|
||||
```yaml
|
||||
#缓存
|
||||
cache:
|
||||
adapter: "file" # 缓存驱动方式,支持:memory|redis|file,不填默认memory
|
||||
fileDir: "./storage/cache" # 文件缓存路径,adapter=file时必填
|
||||
```
|
||||
|
||||
#### 使用方式
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"hotgo/internal/library/cache"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
)
|
||||
|
||||
func test() {
|
||||
ctx := gctx.New()
|
||||
|
||||
// 添加/修改
|
||||
cache.Instance().Set(ctx, "qwe", 123, 0)
|
||||
|
||||
// 查询
|
||||
cache.Instance().Get(ctx, "qwe")
|
||||
|
||||
// 删除
|
||||
cache.Instance().Remove(ctx, "qwe")
|
||||
|
||||
// 更多方法请参考:https://goframe.org/pages/viewpage.action?pageId=27755640
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 请求上下文
|
||||
|
||||
- 主要用于在处理HTTP和websocket请求时通过中间件将用户、应用、插件等信息绑定到上下文中,方便在做业务处理时用到这些信息
|
||||
|
||||
```go
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"context"
|
||||
"hotgo/internal/library/contexts"
|
||||
"hotgo/internal/library/addons"
|
||||
)
|
||||
|
||||
|
||||
func test(ctx context.Context) {
|
||||
// 获取当前请求的所有上下文变量
|
||||
var ctxModel = contexts.Get(ctx)
|
||||
fmt.Printf("当前请求的所有上下文变量:%+v\n", ctxModel)
|
||||
|
||||
// 获取当前请求的应用模块
|
||||
var module = contexts.GetModule(ctx)
|
||||
fmt.Printf("当前请求的应用:%+v\n", module)
|
||||
|
||||
// 获取当前请求的用户信息
|
||||
var member = contexts.GetUser(ctx)
|
||||
fmt.Printf("当前访问用户信息:%+v\n", member)
|
||||
|
||||
// 获取当前请求的插件模块
|
||||
fmt.Printf("当前是否为插件请求:%v", contexts.IsAddonRequest(ctx))
|
||||
if contexts.IsAddonRequest(ctx) {
|
||||
fmt.Printf("当前插件名称:%v", contexts.GetAddonName(ctx))
|
||||
fmt.Printf("当前插件信息:%v", addons.GetModule(contexts.GetAddonName(ctx)))
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### JWT
|
||||
|
||||
- 基于jwt+缓存驱动实现的用户登录令牌功能,支持自动续约,解决了jwt服务端无法退出问题和jwt令牌无法主动失效问题
|
||||
|
||||
#### 配置示例
|
||||
```yaml
|
||||
# 登录令牌
|
||||
token:
|
||||
secretKey: "hotgo123" # 令牌加密秘钥,考虑安全问题生产环境中请修改默认值
|
||||
expires: 604800 # 令牌有效期,单位:秒。默认7天
|
||||
autoRefresh: true # 是否开启自动刷新过期时间, false|true 默认为true
|
||||
refreshInterval: 86400 # 刷新间隔,单位:秒。必须小于expires,否则无法触发。默认1天内只允许刷新一次
|
||||
maxRefreshTimes: 30 # 最大允许刷新次数,-1不限制。默认30次
|
||||
multiLogin: true # 是否允许多端登录, false|true 默认为true
|
||||
|
||||
```
|
||||
|
||||
```go
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"context"
|
||||
"hotgo/internal/library/token"
|
||||
"hotgo/internal/model"
|
||||
)
|
||||
|
||||
|
||||
func test(ctx context.Context) {
|
||||
// 登录
|
||||
user := &model.Identity{
|
||||
Id: mb.Id,
|
||||
Pid: mb.Pid,
|
||||
DeptId: mb.DeptId,
|
||||
RoleId: ro.Id,
|
||||
RoleKey: ro.Key,
|
||||
Username: mb.Username,
|
||||
RealName: mb.RealName,
|
||||
Avatar: mb.Avatar,
|
||||
Email: mb.Email,
|
||||
Mobile: mb.Mobile,
|
||||
App: consts.AppAdmin,
|
||||
LoginAt: gtime.Now(),
|
||||
}
|
||||
|
||||
loginToken, expires, err := token.Login(ctx, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// gf请求对象
|
||||
r := *ghttp.Request
|
||||
|
||||
// 获取登录用户信息
|
||||
user, err := token.ParseLoginUser(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 注销登录
|
||||
err = token.Logout(r)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 数据字典
|
||||
|
||||
- hotgo增加了对枚举字典和自定义方法字典的内置支持,从而在系统中经常使用的一些特定数据上做出了增强。
|
||||
|
||||
#### 字典数据选项
|
||||
- 文件路径:server/internal/model/dict.go
|
||||
|
||||
```go
|
||||
package model
|
||||
|
||||
// Option 字典数据选项
|
||||
type Option struct {
|
||||
Key interface{} `json:"key"`
|
||||
Label string `json:"label" description:"字典标签"`
|
||||
Value interface{} `json:"value" description:"字典键值"`
|
||||
ValueType string `json:"valueType" description:"键值数据类型"`
|
||||
Type string `json:"type" description:"字典类型"`
|
||||
ListClass string `json:"listClass" description:"表格回显样式"`
|
||||
}
|
||||
```
|
||||
|
||||
#### 枚举字典
|
||||
- 适用于系统开发期间内置的枚举数据,这样即维护了枚举值,又关联了数据字典
|
||||
|
||||
##### 一个例子
|
||||
- 定义枚举值和字典数据选项,并注册字典类型
|
||||
- 文件路径:server/internal/consts/credit_log.go
|
||||
|
||||
```go
|
||||
package consts
|
||||
|
||||
import (
|
||||
"hotgo/internal/library/dict"
|
||||
"hotgo/internal/model"
|
||||
)
|
||||
|
||||
func init() {
|
||||
dict.RegisterEnums("creditType", "资金变动类型", CreditTypeOptions)
|
||||
dict.RegisterEnums("creditGroup", "资金变动分组", CreditGroupOptions)
|
||||
}
|
||||
|
||||
const (
|
||||
CreditTypeBalance = "balance" // 余额
|
||||
CreditTypeIntegral = "integral" // 积分
|
||||
)
|
||||
|
||||
const (
|
||||
CreditGroupDecr = "decr" // 扣款
|
||||
CreditGroupIncr = "incr" // 加款
|
||||
CreditGroupOpDecr = "op_decr" // 操作扣款
|
||||
CreditGroupOpIncr = "op_incr" // 操作加款
|
||||
CreditGroupBalanceRecharge = "balance_recharge" // 余额充值
|
||||
CreditGroupBalanceRefund = "balance_refund" // 余额退款
|
||||
CreditGroupApplyCash = "apply_cash" // 申请提现
|
||||
)
|
||||
|
||||
// CreditTypeOptions 变动类型
|
||||
var CreditTypeOptions = []*model.Option{
|
||||
dict.GenSuccessOption(CreditTypeBalance, "余额"),
|
||||
dict.GenInfoOption(CreditTypeIntegral, "积分"),
|
||||
}
|
||||
|
||||
// CreditGroupOptions 变动分组
|
||||
var CreditGroupOptions = []*model.Option{
|
||||
dict.GenWarningOption(CreditGroupDecr, "扣款"),
|
||||
dict.GenSuccessOption(CreditGroupIncr, "加款"),
|
||||
dict.GenWarningOption(CreditGroupOpDecr, "操作扣款"),
|
||||
dict.GenSuccessOption(CreditGroupOpIncr, "操作加款"),
|
||||
dict.GenWarningOption(CreditGroupBalanceRefund, "余额退款"),
|
||||
dict.GenSuccessOption(CreditGroupBalanceRecharge, "余额充值"),
|
||||
dict.GenInfoOption(CreditGroupApplyCash, "申请提现"),
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
#### 自定义方法字典
|
||||
- 适用于非固定选项,如数据是从某个表/文件读取或从第三方读取,数据需要进行转换时使用
|
||||
|
||||
##### 方法字典接口
|
||||
- 文件路径:server/internal/consts/credit_log.go
|
||||
|
||||
```go
|
||||
package dict
|
||||
|
||||
// FuncDict 方法字典,实现本接口即可使用内置方法字典
|
||||
type FuncDict func(ctx context.Context) (res []*model.Option, err error)
|
||||
```
|
||||
|
||||
##### 一个例子
|
||||
- 定义获取字典数据方法,并注册字典类型
|
||||
- 文件路径:server/internal/logic/admin/post.go
|
||||
|
||||
```go
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/dict"
|
||||
"hotgo/internal/model"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/service"
|
||||
)
|
||||
|
||||
type sAdminPost struct{}
|
||||
|
||||
func NewAdminPost() *sAdminPost {
|
||||
return &sAdminPost{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterAdminPost(NewAdminPost())
|
||||
dict.RegisterFunc("adminPostOption", "岗位选项", service.AdminPost().Option)
|
||||
}
|
||||
|
||||
// Option 岗位选项
|
||||
func (s *sAdminPost) Option(ctx context.Context) (opts []*model.Option, err error) {
|
||||
var list []*entity.AdminPost
|
||||
if err = dao.AdminPost.Ctx(ctx).OrderAsc(dao.AdminPost.Columns().Sort).Scan(&list); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(list) == 0 {
|
||||
opts = make([]*model.Option, 0)
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range list {
|
||||
opts = append(opts, dict.GenHashOption(v.Id, v.Name))
|
||||
}
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
#### 代码生成支持
|
||||
- 内置的枚举字典和自定义方法字典在生成代码时可以直接进行选择,生成代码格式和系统字典管理写法一致
|
||||
|
||||

|
||||
|
||||
|
||||
#### 内置字典和系统字典的区分
|
||||
|
||||
##### 主要区别
|
||||
- 系统字典由表:`hg_sys_dict_type`和`hg_sys_dict_data`共同进行维护,使用时需通过后台到字典管理中进行添加
|
||||
- 内置字典是系统开发期间在代码层面事先定义和注册好的数据选项
|
||||
|
||||
|
||||
##### 数据格式区别
|
||||
- 系统字典所有ID都是大于0的int64类型
|
||||
- 内置字典ID都是小于0的int64类型。枚举字典以20000开头,如:-200001381053496;方法字典以30000开头,如:-30000892528327;开头以外数字是根据数据选项的`key`值进行哈希算法得出
|
||||
|
||||
### 地理定位
|
||||
```go
|
||||
// 待写
|
||||
```
|
||||
|
||||
### 通知
|
||||
```go
|
||||
// 待写
|
||||
```
|
@@ -1,249 +1,250 @@
|
||||
## 中间件/拦截器
|
||||
|
||||
目录
|
||||
|
||||
- 介绍
|
||||
- 全局中间件
|
||||
- 鉴权中间件
|
||||
- 响应中间件
|
||||
- 更多
|
||||
|
||||
### 介绍
|
||||
- 在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()
|
||||
|
||||
// 请求输入预处理,api使用gf规范路由并且XxxReq结构体实现了validate.Filter接口即可隐式预处理
|
||||
service.Middleware().PreFilter()
|
||||
|
||||
// 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
|
||||
|
||||
|
||||
#### 常用响应类型
|
||||
|
||||
- hotgo为一些常用的响应类型做了统一格式封装,例如:`application/json`、`text/xml`、`text/html`、`text/event-stream`等,默认使用`application/json`。
|
||||
- 下面我们以`text/xml`为例简单演示几种使用方法:
|
||||
|
||||
1. 当你使用规范化路由时,可直接在XxxRes结构体的`g.Meta`中声明响应类型:
|
||||
```go
|
||||
type HelloReq struct {
|
||||
g.Meta `path:"/hello" tags:"Hello" method:"get" summary:"You first hello api"`
|
||||
Name string `json:"name" d:"hotgo" dc:"名字"`
|
||||
}
|
||||
|
||||
type HelloRes struct {
|
||||
g.Meta `mime:"text/xml" type:"string"`
|
||||
Tips string `json:"tips"`
|
||||
}
|
||||
```
|
||||
|
||||
2. 在响应前设置响应头:
|
||||
```go
|
||||
var (
|
||||
Hello = cHello{}
|
||||
)
|
||||
|
||||
type cHello struct{}
|
||||
|
||||
func (c *cHello) Hello(ctx context.Context, req *user.HelloReq) (res *user.HelloRes, err error) {
|
||||
r := ghttp.RequestFromCtx(ctx)
|
||||
r.Response.Header().Set("Content-Type", "text/xml")
|
||||
|
||||
res = &user.HelloRes{
|
||||
Tips: fmt.Sprintf("hello %v, this is the api for %v applications.", req.Name, simple.AppName(ctx)),
|
||||
}
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
- 浏览器中访问响应内容如下:
|
||||
|
||||

|
||||
|
||||
|
||||
#### 自定义响应
|
||||
- 在实际开发中,可能需要使用自定义的响应类型,由于响应中间件是全局的,因此您需要对其进行单独处理。
|
||||
- 推荐以下几种处理方案,可做参考:
|
||||
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", ""
|
||||
],
|
||||
"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
|
||||
|
||||
```
|
||||
|
||||
- 如果你开启了访问日志,那么日志记录中会详细记录本次请求的相关信息,内容如下:
|
||||

|
||||
|
||||
|
||||
#### 重写错误码
|
||||
- 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
|
||||
|
||||
## 中间件/拦截器
|
||||
|
||||
目录
|
||||
|
||||
- 介绍
|
||||
- 全局中间件
|
||||
- 鉴权中间件
|
||||
- 响应中间件
|
||||
- 更多
|
||||
|
||||
### 介绍
|
||||
- 在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()
|
||||
|
||||
// 请求输入预处理,api使用gf规范路由并且XxxReq结构体实现了validate.Filter接口即可隐式预处理
|
||||
service.Middleware().PreFilter()
|
||||
|
||||
// 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
|
||||
|
||||
|
||||
#### 常用响应类型
|
||||
|
||||
- hotgo为一些常用的响应类型做了统一格式封装,例如:`application/json`、`text/xml`、`text/html`、`text/event-stream`等,默认使用`application/json`。
|
||||
- 下面我们以`text/xml`为例简单演示几种使用方法:
|
||||
|
||||
1. 当你使用规范化路由时,可直接在XxxRes结构体的`g.Meta`中声明响应类型:
|
||||
|
||||
```go
|
||||
type HelloReq struct {
|
||||
g.Meta `path:"/hello" tags:"Hello" method:"get" summary:"You first hello api"`
|
||||
Name string `json:"name" d:"hotgo" dc:"名字"`
|
||||
}
|
||||
|
||||
type HelloRes struct {
|
||||
g.Meta `mime:"text/xml" type:"string"`
|
||||
Tips string `json:"tips"`
|
||||
}
|
||||
```
|
||||
|
||||
2. 在响应前设置响应头:
|
||||
|
||||
```go
|
||||
var (
|
||||
Hello = cHello{}
|
||||
)
|
||||
|
||||
type cHello struct{}
|
||||
|
||||
func (c *cHello) Hello(ctx context.Context, req *user.HelloReq) (res *user.HelloRes, err error) {
|
||||
r := ghttp.RequestFromCtx(ctx)
|
||||
r.Response.Header().Set("Content-Type", "text/xml")
|
||||
|
||||
res = &user.HelloRes{
|
||||
Tips: fmt.Sprintf("hello %v, this is the api for %v applications.", req.Name, simple.AppName(ctx)),
|
||||
}
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
- 浏览器中访问响应内容如下:
|
||||
|
||||

|
||||
|
||||
|
||||
#### 自定义响应
|
||||
- 在实际开发中,可能需要使用自定义的响应类型,由于响应中间件是全局的,因此您需要对其进行单独处理。
|
||||
- 推荐以下几种处理方案,可做参考:
|
||||
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", ""
|
||||
],
|
||||
"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
|
||||
|
||||
```
|
||||
|
||||
- 如果你开启了访问日志,那么日志记录中会详细记录本次请求的相关信息,内容如下:
|
||||

|
||||
|
||||
|
||||
#### 重写错误码
|
||||
- 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
|
||||
|
||||
|
@@ -44,6 +44,7 @@ SaaS系统多租户多应用设计,已成为互联网企业的重要发展建
|
||||
|
||||
- 在用户登录成功后,server端可通过上下文来获取用户部门类型来确定用户身份
|
||||
- 文件路径:server/internal/library/contexts/context.go
|
||||
|
||||
```go
|
||||
package contexts
|
||||
|
||||
@@ -87,6 +88,7 @@ func IsUserDept(ctx context.Context) bool {
|
||||
|
||||
- 在用户登录成功后,web端可通`useUserStore`来获取用户部门类型来确定用户身份
|
||||
- 文件路径:web/src/store/modules/user.ts
|
||||
|
||||
```vue
|
||||
<script lang="ts" setup>
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
@@ -125,6 +127,7 @@ HotGo定位是中小型应用开发,推荐采用一套数据库不同Schema。
|
||||
下面是多租户功能演示例子代码中的使用片段
|
||||
|
||||
- 封装查询Model
|
||||
|
||||
```go
|
||||
// Model 多租户功能演示ORM模型
|
||||
func (s *sSysTenantOrder) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
|
||||
|
@@ -12,6 +12,7 @@
|
||||
### 全局消息监听
|
||||
- 所有全局的消息监听都在这里
|
||||
- 文件路径:web/src/utils/websocket/registerMessage.ts
|
||||
|
||||
```ts
|
||||
import { TABS_ROUTES } from '@/store/mutation-types';
|
||||
import { SocketEnum } from '@/enums/socketEnum';
|
||||
@@ -51,6 +52,7 @@ export function registerGlobalMessage() {
|
||||
#### 单页面消息监听
|
||||
- 当你只需要某个页面使用WebSocket,这将是一个不错的选择,下面是一个简单的演示例子
|
||||
- 文件路径:web/src/views/addons/hgexample/portal/websocketTest.vue
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
@@ -190,6 +192,7 @@ export function registerGlobalMessage() {
|
||||
|
||||
#### 发送消息
|
||||
- 向服务器发送一条消息
|
||||
|
||||
```ts
|
||||
import { sendMsg } from '@/utils/websocket';
|
||||
|
||||
|
@@ -16,6 +16,7 @@
|
||||
#### 1.消息处理接口
|
||||
- 消息处理在设计上采用了接口化的思路。只需要实现以下接口,即可进行WebSocket消息注册
|
||||
- 文件路径:server/internal/websocket/model.go
|
||||
|
||||
```go
|
||||
package websocket
|
||||
|
||||
@@ -26,6 +27,7 @@ type EventHandler func(client *Client, req *WRequest)
|
||||
#### 2.定义消息处理方法
|
||||
- 以下是功能案例中的一个简单演示,实现了消息处理接口,并将收到的消息原样发送给客户端
|
||||
- 文件路径:server/addons/hgexample/controller/websocket/handler/index.go
|
||||
|
||||
```go
|
||||
package handler
|
||||
|
||||
@@ -52,6 +54,7 @@ func (c *cIndex) TestMessage(client *websocket.Client, req *websocket.WRequest)
|
||||
#### 3.注册消息
|
||||
- 定义消息处理方法后,需要将其注册到WebSocket消息处理器,一般放在对应应用模块的`router/websocket.go`下即可
|
||||
- 文件路径:server/addons/hgexample/router/websocket.go
|
||||
|
||||
```go
|
||||
package router
|
||||
|
||||
@@ -80,6 +83,7 @@ func WebSocket(ctx context.Context, group *ghttp.RouterGroup) {
|
||||
|
||||
### 常用方法
|
||||
- websocket服务器还提供了一些常用的方法,下面只对部分进行说明
|
||||
|
||||
```go
|
||||
func test() {
|
||||
websocket.SendToAll() // 发送全部客户端
|
||||
@@ -105,6 +109,7 @@ func test() {
|
||||
### 其他
|
||||
- WebSocket被连接时需验证用户认证中间件,所以用户必须登录成功后才能连接成功
|
||||
- 参考文件:server/internal/logic/middleware/weboscket_auth.go
|
||||
|
||||
```go
|
||||
package middleware
|
||||
|
||||
|
277
index.html
277
index.html
@@ -4,168 +4,183 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1, minimum-scale=1.0, shrink-to-fit=no, viewport-fit=cover">
|
||||
content="width=device-width, initial-scale=1, minimum-scale=1.0, shrink-to-fit=no, viewport-fit=cover">
|
||||
|
||||
<!-- Replace with your own title and description. -->
|
||||
<title>HotGo-V2</title>
|
||||
<meta name="description" content="基于全新GoFrame2+Vue3+NaiveUI+uniapp开发的全栖框架,为二次开发而生,适合中小型完整应用开发。">
|
||||
|
||||
<!-- Default Theme (see https://docsify.js.org/#/themes) -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css">
|
||||
<!-- Default Theme (see //docsify.js.org/#/themes) -->
|
||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css">
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<div id="app"></div>
|
||||
|
||||
<script>
|
||||
// Docsify Configuration (see https://docsify.js.org/#/configuration)
|
||||
window.$docsify = {
|
||||
name: "HotGo-V2",
|
||||
nameLink: {
|
||||
'/': '/hotgo/',
|
||||
<script>
|
||||
// Docsify Configuration (see //docsify.js.org/#/configuration)
|
||||
window.$docsify = {
|
||||
name: "HotGo-V2",
|
||||
nameLink: {
|
||||
'/': '/hotgo/',
|
||||
},
|
||||
relativePath: true,
|
||||
// alias: {
|
||||
// "/.*/_navbar.md": "/_navbar.md",
|
||||
// },
|
||||
// themeColor: "#42b983",
|
||||
// logo: "//bufanyun.cn-bj.ufileos.com/hotgo/logo.sig.png",
|
||||
// coverpage: true,
|
||||
// homepage: "README.md",
|
||||
|
||||
// Sidebar Configuration
|
||||
auto2top: true,
|
||||
loadSidebar: "sidebar.md",
|
||||
// maxLevel: 2,
|
||||
// Set subMaxLevel to 0 to remove automatic display of page table of contents (TOC) in Sidebar
|
||||
// subMaxLevel: 3,
|
||||
|
||||
// Navbar Configuration
|
||||
// loadNavbar: true,
|
||||
|
||||
// Search Plugin Configuration
|
||||
search: {
|
||||
placeholder: {
|
||||
"/": "搜索",
|
||||
// "/": "Type to search"
|
||||
},
|
||||
relativePath: true,
|
||||
// alias: {
|
||||
// "/.*/_navbar.md": "/_navbar.md",
|
||||
// },
|
||||
// themeColor: "#42b983",
|
||||
// logo: "https://bufanyun.cn-bj.ufileos.com/hotgo/logo.sig.png",
|
||||
// coverpage: true,
|
||||
// homepage: "README.md",
|
||||
|
||||
// Sidebar Configuration
|
||||
auto2top: true,
|
||||
loadSidebar: "sidebar.md",
|
||||
// maxLevel: 2,
|
||||
// Set subMaxLevel to 0 to remove automatic display of page table of contents (TOC) in Sidebar
|
||||
// subMaxLevel: 3,
|
||||
|
||||
// Navbar Configuration
|
||||
// loadNavbar: true,
|
||||
|
||||
// Search Plugin Configuration
|
||||
search: {
|
||||
placeholder: {
|
||||
"/": "搜索",
|
||||
// "/": "Type to search"
|
||||
},
|
||||
noData: {
|
||||
"/": "找不到结果",
|
||||
// "/": "No Results"
|
||||
},
|
||||
// Headline depth, 1 - 6
|
||||
// depth: 2,
|
||||
noData: {
|
||||
"/": "找不到结果",
|
||||
// "/": "No Results"
|
||||
},
|
||||
// Headline depth, 1 - 6
|
||||
// depth: 2,
|
||||
},
|
||||
|
||||
// Flexible-alerts Plugin Configuration
|
||||
"flexible-alerts": {
|
||||
important: {
|
||||
label: "Important",
|
||||
// Flexible-alerts Plugin Configuration
|
||||
"flexible-alerts": {
|
||||
important: {
|
||||
label: "Important",
|
||||
|
||||
// localization
|
||||
label: {
|
||||
// "/zh-cn": "重要",
|
||||
"/": "Important"
|
||||
},
|
||||
|
||||
// Assuming that we use Font Awesome
|
||||
icon: "far fa-message",
|
||||
className: "important"
|
||||
// localization
|
||||
label: {
|
||||
// "/zh-cn": "重要",
|
||||
"/": "Important"
|
||||
},
|
||||
warning: {
|
||||
label: "Warning",
|
||||
|
||||
// localization
|
||||
label: {
|
||||
// "/zh-cn": "警告",
|
||||
"/": "Warning"
|
||||
},
|
||||
// Assuming that we use Font Awesome
|
||||
icon: "far fa-message",
|
||||
className: "important"
|
||||
},
|
||||
warning: {
|
||||
label: "Warning",
|
||||
|
||||
// Assuming that we use Font Awesome
|
||||
icon: "fas fa-triangle-exclamation",
|
||||
className: "warning"
|
||||
// localization
|
||||
label: {
|
||||
// "/zh-cn": "警告",
|
||||
"/": "Warning"
|
||||
},
|
||||
caution: {
|
||||
label: "Caution",
|
||||
|
||||
// localization
|
||||
label: {
|
||||
// "/zh-cn": "注意",
|
||||
"/": "Caution"
|
||||
},
|
||||
// Assuming that we use Font Awesome
|
||||
icon: "fas fa-triangle-exclamation",
|
||||
className: "warning"
|
||||
},
|
||||
caution: {
|
||||
label: "Caution",
|
||||
|
||||
// Assuming that we use Font Awesome
|
||||
icon: "fas fa-circle-exclamation",
|
||||
className: "attention"
|
||||
// localization
|
||||
label: {
|
||||
// "/zh-cn": "注意",
|
||||
"/": "Caution"
|
||||
},
|
||||
|
||||
// Assuming that we use Font Awesome
|
||||
icon: "fas fa-circle-exclamation",
|
||||
className: "attention"
|
||||
},
|
||||
},
|
||||
|
||||
// Hide-code Plugin Configuration
|
||||
hideCode: {
|
||||
// scroll: false, // Enable scrolling
|
||||
height: 300 // Max height
|
||||
},
|
||||
// Hide-code Plugin Configuration
|
||||
hideCode: {
|
||||
// scroll: false, // Enable scrolling
|
||||
height: 300 // Max height
|
||||
},
|
||||
|
||||
// Versioned Plugin Configuration
|
||||
// versions: [
|
||||
// { folder: "/", label: "v2", default: true },
|
||||
// ],
|
||||
// versionSelectorLabel: "Version",
|
||||
// Versioned Plugin Configuration
|
||||
// versions: [
|
||||
// { folder: "/", label: "v2", default: true },
|
||||
// ],
|
||||
// versionSelectorLabel: "Version",
|
||||
|
||||
// Progress Plugin Configuration
|
||||
progress: {
|
||||
position: "top",
|
||||
// color: "var(--theme-color,#42b983)",
|
||||
height: "3px",
|
||||
},
|
||||
};
|
||||
</script>
|
||||
// Progress Plugin Configuration
|
||||
progress: {
|
||||
position: "top",
|
||||
// color: "var(--theme-color,#42b983)",
|
||||
height: "3px",
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<!-- Required -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify@4/lib/docsify.min.js"></script>
|
||||
<!-- Required -->
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify@4/lib/docsify.min.js"></script>
|
||||
|
||||
<!-- Recommended -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify@4/lib/plugins/zoom-image.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify@4/lib/plugins/search.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify-pagination/dist/docsify-pagination.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify-hide-code/dist/docsify-hide-code.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify-progress@latest/dist/progress.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify-example-panels"></script>
|
||||
<!-- Recommended -->
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify@4/lib/plugins/zoom-image.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify@4/lib/plugins/search.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify-pagination/dist/docsify-pagination.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify-hide-code/dist/docsify-hide-code.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify-progress@latest/dist/progress.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify-example-panels"></script>
|
||||
|
||||
<!-- Prism code highlight -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/prismjs@1/components/prism-bash.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/prismjs@1/components/prism-matlab.min.js"></script>
|
||||
<!-- Prism code highlight -->
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-bash.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-matlab.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-go.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-yaml.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-vue.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-json.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-sql.min.js"></script>
|
||||
|
||||
<!-- docsify-dark-switcher -->
|
||||
<script src="https://cdn.jsdelivr.net/gh/LIGMATV/docsify-dark-switcher@latest/docsify-dark-switcher.js"></script>
|
||||
<style>
|
||||
:root {
|
||||
--dark-base-background: #222;
|
||||
--dark-base-color: #bbc0c4;
|
||||
--dark-theme-color: var(--theme-color, #42b983);
|
||||
--dark-code-color: var(--dark-color);
|
||||
--dark-heading-color: var(--dark-theme-color);
|
||||
--dark-cover-background: #000000a8;
|
||||
--dark-code-background: #303030;
|
||||
--dark-tip-background: #2c0000;
|
||||
--dark-warn-background: #005842;
|
||||
--dark-icon-size: 25px;
|
||||
--dark-icon-transition: .1s ease-in-out .1s;
|
||||
--dark-moon-color: #000000;
|
||||
--dark-sun-color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
<!--mermaid插件-->
|
||||
<script type="module">
|
||||
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
|
||||
mermaid.initialize({ startOnLoad: true });
|
||||
window.mermaid = mermaid;
|
||||
</script>
|
||||
<script src="//unpkg.com/docsify-mermaid@2.0.1/dist/docsify-mermaid.js"></script>
|
||||
|
||||
<!-- docsify-dark-switcher -->
|
||||
<script src="//cdn.jsdelivr.net/gh/LIGMATV/docsify-dark-switcher@latest/docsify-dark-switcher.js"></script>
|
||||
<style>
|
||||
:root {
|
||||
--dark-base-background: #222;
|
||||
--dark-base-color: #bbc0c4;
|
||||
--dark-theme-color: var(--theme-color, #42b983);
|
||||
--dark-code-color: var(--dark-color);
|
||||
--dark-heading-color: var(--dark-theme-color);
|
||||
--dark-cover-background: #000000a8;
|
||||
--dark-code-background: #303030;
|
||||
--dark-tip-background: #2c0000;
|
||||
--dark-warn-background: #005842;
|
||||
--dark-icon-size: 25px;
|
||||
--dark-icon-transition: .1s ease-in-out .1s;
|
||||
--dark-moon-color: #000000;
|
||||
--dark-sun-color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- docsify-plugin-flexible-alerts -->
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify-plugin-flexible-alerts/dist/docsify-plugin-flexible-alerts.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/js/all.min.js"></script>
|
||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/fontawesome.min.css">
|
||||
|
||||
<!-- docsify-versioned-plugin -->
|
||||
<!-- <script src="//cdn.jsdelivr.net/npm/docsify-versioned-plugin@0.0.1/index.js"></script>
|
||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify-versioned-plugin@0.0.1/styles.css"> -->
|
||||
|
||||
<!-- docsify-plugin-flexible-alerts -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify-plugin-flexible-alerts/dist/docsify-plugin-flexible-alerts.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/js/all.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/fontawesome.min.css">
|
||||
|
||||
<!-- docsify-versioned-plugin -->
|
||||
<!-- <script src="https://cdn.jsdelivr.net/npm/docsify-versioned-plugin@0.0.1/index.js"></script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsify-versioned-plugin@0.0.1/styles.css"> -->
|
||||
|
||||
</body>
|
||||
|
||||
|
Reference in New Issue
Block a user