diff --git a/README.md b/README.md
index 520d0d1..add98f3 100644
--- a/README.md
+++ b/README.md
@@ -6,13 +6,13 @@
-
+
-
+
@@ -83,24 +83,28 @@
-  |
-  |
+  |
+  |
-  |
-  |
+  |
+  |
-  |
-  |
+  |
+  |
-  |
-  |
+  |
+  |
-  |
-  |
+  |
+  |
+
+
+  |
+  |
diff --git a/docs/guide-zh-CN/README.md b/docs/guide-zh-CN/README.md
index 35d8450..4a5a6b9 100644
--- a/docs/guide-zh-CN/README.md
+++ b/docs/guide-zh-CN/README.md
@@ -20,17 +20,16 @@
- [WebHook](sys-webhook.md)
- [权限控制](sys-auth.md)
- [支付网关](sys-payment.md)
-- [数据库](sys-db.md)
-- [代码生成](sys-code.md)
- [定时任务](sys-cron.md)
- [消息队列](sys-queue.md)
- [功能扩展库](sys-library.md)
- [工具方法](sys-utility.md)
- [WebSocket服务器](sys-websocket-server.md)
- [TCP服务器](sys-tcp-server.md)
+- [SaaS多租户](sys-tenant.md)
- [单元测试](sys-test.md)
-
+
#### 插件模块开发
- [模块介绍及目录](addon-introduce-catalog.md)
@@ -38,6 +37,18 @@
- [模块辅助说明](addon-helper.md)
+#### 生成代码
+- [使用前提](code-start.md)
+- [数据库](sys-db.md)
+- [生成配置](code-config.md)
+- [生成CURD](code-curd.md)
+- [生成关联表CURD](code-curd-join.md)
+- [生成树型CURD](code-tree.md)
+- [生成业务模板](code-business.md)
+- [生成模板开发](code-template-dev.md)
+- [生成常见问题](code-help.md)
+
+
### 前端开发
- [表单组件](web-form.md)
- [WebSocket客户端](sys-websocket-client.md)
diff --git a/docs/guide-zh-CN/addon-introduce-catalog.md b/docs/guide-zh-CN/addon-introduce-catalog.md
index 3158900..c7f98f1 100644
--- a/docs/guide-zh-CN/addon-introduce-catalog.md
+++ b/docs/guide-zh-CN/addon-introduce-catalog.md
@@ -25,6 +25,7 @@ HotGo 入口文件->隐式注入(hotgo/addons/modules)->注册所有插件->初
│ ├── modules
│ ├── xxx插件
│ | ├── api
+│ | ├── consts
│ | ├── controller
│ | ├── crons
│ | ├── global
diff --git a/docs/guide-zh-CN/code-business.md b/docs/guide-zh-CN/code-business.md
new file mode 100644
index 0000000..2564b47
--- /dev/null
+++ b/docs/guide-zh-CN/code-business.md
@@ -0,0 +1,5 @@
+## 生成业务模板
+
+根据`api`接口文件一键生成业务模板:api、controller、logic、service
+
+待写。
diff --git a/docs/guide-zh-CN/code-config.md b/docs/guide-zh-CN/code-config.md
new file mode 100644
index 0000000..11029a0
--- /dev/null
+++ b/docs/guide-zh-CN/code-config.md
@@ -0,0 +1,170 @@
+## 生成配置
+
+目录
+
+- 模板配置
+- CLI配置
+- 多数据库使用
+
+### 模板配置
+
+- 配置路径:server/manifest/config/config.yaml
+
+```yaml
+# 生成代码
+hggen:
+ allowedIPs: [ "127.0.0.1", "*" ] # 白名单,*代表所有,只有允许的IP后台才能使用生成代码功能
+ selectDbs: [ "default" ] # 可选生成表的数据库配置名称,支持多库
+ disableTables: [ "hg_sys_gen_codes","hg_admin_role_casbin" ] # 禁用的表,禁用以后将不会在选择表中看到
+ delimiters: [ "@{", "}" ] # 模板引擎变量分隔符号
+ # 生成应用模型,所有生成模板允许自定义,可以参考default模板进行改造
+ application:
+ # CRUD和关系树列表模板
+ crud:
+ templates:
+ # 默认的主包模板
+ - group: "default" # 分组名称
+ isAddon: false # 是否为插件模板 false|true
+ masterPackage: "sys" # 主包名称,需和controllerPath、logicPath、inputPath保持关联
+ templatePath: "./resource/generate/default/curd" # 模板路径
+ apiPath: "./api/admin" # goApi生成路径
+ controllerPath: "./internal/controller/admin/sys" # 控制器生成路径
+ logicPath: "./internal/logic/sys" # 主要业务生成路径
+ inputPath: "./internal/model/input/sysin" # 表单过滤器生成路径
+ routerPath: "./internal/router/genrouter" # 生成路由表路径
+ sqlPath: "./storage/data/generate" # 生成sql语句路径
+ webApiPath: "../web/src/api" # webApi生成路径
+ webViewsPath: "../web/src/views" # web页面生成路径
+
+ # 默认的插件包模板,{$name}会自动替换成实际的插件名称
+ - group: "addon" # 分组名称
+ isAddon: true # 是否为插件模板 false|true
+ masterPackage: "sys" # 主包名称,需和controllerPath、logicPath、inputPath保持关联
+ templatePath: "./resource/generate/default/curd" # 模板路径
+ apiPath: "./addons/{$name}/api/admin" # goApi生成路径
+ controllerPath: "./addons/{$name}/controller/admin/sys" # 控制器生成路径
+ logicPath: "./addons/{$name}/logic/sys" # 主要业务生成路径
+ inputPath: "./addons/{$name}/model/input/sysin" # 表单过滤器生成路径
+ routerPath: "./addons/{$name}/router/genrouter" # 生成路由表路径
+ sqlPath: "./storage/data/generate/addons" # 生成sql语句路径
+ webApiPath: "../web/src/api/addons/{$name}" # webApi生成路径
+ webViewsPath: "../web/src/views/addons/{$name}" # web页面生成路径
+
+ # 消息队列模板
+ queue:
+ templates:
+ - group: "default"
+ templatePath: "./resource/generate/default/queue"
+
+ # 定时任务模板
+ cron:
+ templates:
+ - group: "default"
+ templatePath: "./resource/generate/default/cron"
+
+ # 生成插件模块,通过后台创建新插件时使用的模板,允许自定义,可以参考default模板进行改造
+ addon:
+ srcPath: "./resource/generate/default/addon" # 生成模板路径
+ webApiPath: "../web/src/api/addons/{$name}" # webApi生成路径
+ webViewsPath: "../web/src/views/addons/{$name}" # web页面生成路径
+```
+
+### CLI配置
+
+- hotgo在生成dao、service配置时,默认了和gf官方一致的配置方式和代码生成规则。所以无论你是通过hotgo亦或gf命令生成,最终代码格式完全一致,遵循一致的代码规范。
+
+- 配置路径:[server/hack/config.yaml](../../server/hack/config.yaml)
+
+```yaml
+gfcli:
+ build:
+ name: "hotgo" # 编译后的可执行文件名称
+ # arch: "all" #不填默认当前系统架构,可选:386,amd64,arm,all
+ # system: "all" #不填默认当前系统平台,可选:linux,darwin,windows,all
+ mod: "none"
+ cgo: 0
+ packSrc: "resource" # 将resource目录打包进可执行文件,静态资源无需单独部署
+ packDst: "internal/packed/packed.go" # 打包后生成的Go文件路径,一般使用相对路径指定到本项目目录中
+ version: ""
+ output: "./temp/hotgo" # 可执行文件生成路径
+ extra: ""
+
+ gen:
+ dao:
+ - link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true"
+ group: "default" # 分组 使用hotgo代码生成功能时必须填
+ # tables: "" # 指定当前数据库中需要执行代码生成的数据表。如果为空,表示数据库的所有表都会生成。
+ tablesEx: "hg_sys_addons_install" # 指定当前数据库中需要排除代码生成的数据表。
+ removePrefix: "hg_"
+ descriptionTag: true
+ noModelComment: true
+ jsonCase: "CamelLower"
+ gJsonSupport: true
+ clear: true
+
+ service: # 生成业务配置
+ srcFolder: "internal/logic"
+ dstFolder: "internal/service"
+ dstFileNameCase: "CamelLower"
+ clear: true
+```
+
+### 多数据库使用
+
+- 假设我们要增加一个库名为`hotgo2`、分组为`default2`的数据库,并要为其生成代码
+
+1. 配置[server/hack/config.yaml](../../server/hack/config.yaml) 如下:
+```yaml
+ gen:
+ dao:
+ - link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true"
+ group: "default" # 分组 使用hotgo代码生成功能时必须填
+ tablesEx: "hg_sys_addons_install" # 指定当前数据库中需要排除代码生成的数据表。
+ removePrefix: "hg_"
+ descriptionTag: true
+ noModelComment: true
+ jsonCase: "CamelLower"
+ gJsonSupport: true
+ clear: false
+ - link: "mysql:hotgo2:hg123456.@tcp(127.0.0.1:3306)/hotgo2?loc=Local&parseTime=true"
+ group: "default2" # 分组 使用hotgo代码生成功能时必须填
+ tablesEx: "hg_sys_addons_install" # 指定当前数据库中需要排除代码生成的数据表。
+ removePrefix: ""
+ descriptionTag: true
+ noModelComment: true
+ jsonCase: "CamelLower"
+ gJsonSupport: true
+ clear: false
+```
+
+2. 配置`server/manifest/config/config.yaml`,
+
+`database`配置如下:
+```yaml
+database:
+ logger:
+ level: "all"
+ stdout: true
+ default:
+ link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true"
+ debug: true
+ Prefix: "hg_"
+ default2:
+ link: "mysql:hotgo2:hg123456.@tcp(127.0.0.1:3306)/hotgo2?loc=Local&parseTime=true"
+ debug: true
+ Prefix: ""
+```
+
+`hggen`配置如下:
+```yaml
+hggen:
+ allowedIPs: ["127.0.0.1", "*"] # 白名单,*代表所有,只有允许的IP后台才能使用生成代码功能
+ selectDbs: [ "default", "default2" ] # 可选生成表的数据库配置名称,支持多库
+ disableTables : ["hg_sys_gen_codes","hg_admin_role_casbin"] # 禁用的表,禁用以后将不会在选择表中看到
+ delimiters: ["@{", "}"] # 模板引擎变量分隔符号
+```
+
+3. 登录HotGo后台 -> 开发工具 -> 代码生成 -> 找到立即生成按钮并打开,就会发现`数据库`选项增加了一个`default2`,后续生成步骤和生成例子完全一样
+
+> 注意:上述的配置中所有的`default2`名称必须保持一致
+
diff --git a/docs/guide-zh-CN/code-curd-join.md b/docs/guide-zh-CN/code-curd-join.md
new file mode 100644
index 0000000..88fdf3d
--- /dev/null
+++ b/docs/guide-zh-CN/code-curd-join.md
@@ -0,0 +1,115 @@
+## 生成关联表CURD
+
+### 热编译启动
+- 推荐使用热编译方式启动HotGo,这样生成完成页面自动刷新即可看到新生成内容,无需手动重启
+
+```shell
+# 服务端
+cd server
+gf run main.go
+
+# web端
+cd web
+yarn dev
+```
+
+以下是一个关联表的CURD生成流程
+
+### 创建表结构
+- 以下表结构和数据为了方便功能演示已经内置无需再次创建
+
+
+- 创建主表:hg_sys_gen_curd_demo
+```sql
+CREATE TABLE `hg_sys_gen_curd_demo` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
+ `category_id` bigint(20) DEFAULT '0' COMMENT '分类ID',
+ `title` varchar(64) NOT NULL COMMENT '标题',
+ `description` varchar(255) DEFAULT '' COMMENT '描述',
+ `content` text COMMENT '内容',
+ `image` varchar(255) DEFAULT NULL COMMENT '单图',
+ `attachfile` varchar(255) DEFAULT NULL COMMENT '附件',
+ `city_id` bigint(20) DEFAULT '0' COMMENT '所在城市',
+ `switch` int(11) DEFAULT '1' COMMENT '显示开关',
+ `sort` int(11) DEFAULT NULL COMMENT '排序',
+ `status` tinyint(1) DEFAULT '1' COMMENT '状态',
+ `created_by` bigint(20) DEFAULT '0' COMMENT '创建者',
+ `updated_by` bigint(20) DEFAULT '0' COMMENT '更新者',
+ `created_at` datetime DEFAULT NULL COMMENT '创建时间',
+ `updated_at` datetime DEFAULT NULL COMMENT '修改时间',
+ `deleted_at` datetime DEFAULT NULL COMMENT '删除时间',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COMMENT='系统_生成curd演示';
+```
+
+创建关联表:hg_test_category
+```sql
+CREATE TABLE `hg_test_category` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '分类ID',
+ `name` varchar(255) NOT NULL COMMENT '分类名称',
+ `short_name` varchar(128) DEFAULT NULL COMMENT '简称',
+ `description` varchar(255) DEFAULT NULL COMMENT '描述',
+ `sort` int(11) NOT NULL COMMENT '排序',
+ `remark` varchar(255) DEFAULT NULL COMMENT '备注',
+ `status` tinyint(1) DEFAULT '1' COMMENT '状态',
+ `created_at` datetime DEFAULT NULL COMMENT '创建时间',
+ `updated_at` datetime DEFAULT NULL COMMENT '修改时间',
+ `deleted_at` datetime DEFAULT NULL COMMENT '删除时间',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COMMENT='测试分类';
+```
+
+### 表字段要求
+- 使用生成时,表中必须具有以下字段和属性
+
+| 字段名称 | 字段含义 | 字段类型 | 可为空 |
+|--------|----------------------|---------------------|-----|
+| id | 主键ID | bigint(20) | 否 |
+
+
+
+### 创建生成配置
+- 登录HotGo后台 -> 开发工具 -> 代码生成 -> 找到立即生成按钮并打开,选择和填写如下参数:
+
+
+
+
+### 基本设置
+- 确认无误后,点击生成配置会跳转到生成配置页面,如下:
+- 你可以在该页面调整生成表格表头/表列的接口功能、菜单权限、高级设置和关联表设置
+
+
+
+### 主表字段设置
+
+- 在该页面你可以调整生成表格字段名称、表单组件、表单编辑/验证项、列表展示/查询项、字段排序、重置和同步字段
+
+
+
+
+### 关联表字段设置
+- 在该页面你可以调整生成表格关联表字段名称、列表展示/查询项、字段排序、重置和同步字段
+- 如果存在多个关联表,也可以对多个关联表字段进行设置
+
+
+
+### 预览并生成
+
+点击`预览代码`查看生成的代码内容。如果无需继续调整直接点击`提交生成`即可,以下是预览代码效果:
+
+
+
+- 如果你使用的热编译,那么页面会在生成成功后立即刷新,刷新完成你即可在后台菜单栏中看到`测试表格`菜单。如果不是使用热编译启动,请手动重启服务后刷新。
+
+
+### 生成完成
+
+- 让我们看看生成的表格页面,效果如下:
+
+
+
+### 常见问题
+
+- [生成常见问题](code-help.md)
+
+
diff --git a/docs/guide-zh-CN/code-curd.md b/docs/guide-zh-CN/code-curd.md
new file mode 100644
index 0000000..0d89392
--- /dev/null
+++ b/docs/guide-zh-CN/code-curd.md
@@ -0,0 +1,84 @@
+## 生成CURD
+
+### 热编译启动
+- 推荐使用热编译方式启动HotGo,这样生成完成页面自动刷新即可看到新生成内容,无需手动重启
+
+```shell
+# 服务端
+cd server
+gf run main.go
+
+# web端
+cd web
+yarn dev
+```
+
+以下是一个基本的CURD生成流程
+
+### 创建表结构
+- 以下表结构和数据为了方便功能演示已经内置无需再次创建
+
+```sql
+CREATE TABLE `hg_test_category` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '分类ID',
+ `name` varchar(255) NOT NULL COMMENT '分类名称',
+ `short_name` varchar(128) DEFAULT NULL COMMENT '简称',
+ `description` varchar(255) DEFAULT NULL COMMENT '描述',
+ `sort` int(11) NOT NULL COMMENT '排序',
+ `remark` varchar(255) DEFAULT NULL COMMENT '备注',
+ `status` tinyint(1) DEFAULT '1' COMMENT '状态',
+ `created_at` datetime DEFAULT NULL COMMENT '创建时间',
+ `updated_at` datetime DEFAULT NULL COMMENT '修改时间',
+ `deleted_at` datetime DEFAULT NULL COMMENT '删除时间',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COMMENT='测试分类';
+```
+
+### 表字段要求
+- 使用生成时,表中必须具有以下字段和属性
+
+| 字段名称 | 字段含义 | 字段类型 | 可为空 |
+|--------|----------------------|---------------------|-----|
+| id | 主键ID | bigint(20) | 否 |
+
+
+### 创建生成配置
+- 登录HotGo后台 -> 开发工具 -> 代码生成 -> 找到立即生成按钮并打开,选择和填写如下参数:
+
+
+
+
+### 基本设置
+- 确认无误后,点击生成配置会跳转到生成配置页面,如下:
+- 你可以在该页面调整生成表格表头/表列的接口功能、菜单权限、高级设置和关联表设置
+
+
+
+### 表字段设置
+
+- 在该页面你可以调整生成表格字段名称、表单组件、表单编辑/验证项、列表展示/查询项、字段排序、重置和同步字段
+
+
+
+### 预览并生成
+
+点击`预览代码`查看生成的代码内容。如果无需继续调整直接点击`提交生成`即可,以下是预览代码效果:
+
+
+
+- 如果你使用的热编译,那么页面会在生成成功后立即刷新,刷新完成你即可在后台菜单栏中看到`测试表格`菜单。如果不是使用热编译启动,请手动重启服务后刷新。
+
+
+### 生成完成
+
+- 让我们看看生成的表格页面,效果如下:
+
+
+
+
+### 常见问题
+
+- [生成常见问题](code-help.md)
+
+
+
diff --git a/docs/guide-zh-CN/code-help.md b/docs/guide-zh-CN/code-help.md
new file mode 100644
index 0000000..8f220b1
--- /dev/null
+++ b/docs/guide-zh-CN/code-help.md
@@ -0,0 +1,18 @@
+## 生成常见问题
+
+### 生成完后页面提示:服务器错误,请稍候重试!
+
+- 热编译环境下,web端往往会快于服务端重启并加载完成,此时接口访问会出现`服务器错误,请稍候重试!`或`404`。这是服务端正在重启导致的,一般稍等几秒就好,如果不行就手动重启下服务端。
+
+
+### fetching tables failed: SHOW TABLES: Error 1045 (28000): Access denied for user * (using password: YES)
+
+- 请去确认`server/manifest/config/config.yaml`和`server/hack/config.yaml`下的数据库配置一致并且权限正确
+- 参考:[生成配置](code-config.md)
+
+
+### 为什么后台找不到开发工具菜单
+
+- 请去确认`server/manifest/config/config.yaml`中的`system.mode`不为`product`。product模式下后台【开发工具】菜单自动隐藏
+
+
diff --git a/docs/guide-zh-CN/code-start.md b/docs/guide-zh-CN/code-start.md
new file mode 100644
index 0000000..ddeea5e
--- /dev/null
+++ b/docs/guide-zh-CN/code-start.md
@@ -0,0 +1,20 @@
+## 使用前提
+
+
+在HotGo中可以通过后台开发工具快速的一键生成CRUD,自动生成Api、控制器、业务逻辑、Web页面、表单组件、菜单权限等。
+
+
+- hotgo 版本号 >= 2.13.1
+- 使用前必须配置 [生成配置](code-config.md)
+- 使用前必须了解 [数据库](sys-db.md)
+
+
+相关目录
+
+- [生成配置](code-config.md)
+- [生成CURD](code-curd.md)
+- [生成关联表CURD](code-curd-join.md)
+- [生成树型CURD](code-tree.md)
+- [生成业务模板](code-business.md)
+- [生成模板开发](code-template-dev.md)
+- [生成常见问题](code-help.md)
\ No newline at end of file
diff --git a/docs/guide-zh-CN/code-template-dev.md b/docs/guide-zh-CN/code-template-dev.md
new file mode 100644
index 0000000..fe8b6fa
--- /dev/null
+++ b/docs/guide-zh-CN/code-template-dev.md
@@ -0,0 +1,59 @@
+## 生成模板开发
+
+### 自定义生成模板
+
+- HotGo允许你新建新的模板分组来满足你的需求,模板可根据现有模板基础拷贝一份出来做改造,默认模板目录:[server/resource/generate/default](../../server/resource/generate/default)
+
+- 系统内置了两组CURD生成模板,请参考[生成模板配置](sys-code.md#生成模板配置)。default:是默认的生成到主模块下;addon:是默认生成到指定的插件下
+
+
+
+### 内置gf-cli
+
+- 为了确保生成代码的依赖稳定性,在面对`gf`版本更新可能导致向下不兼容情况时,HotGo将`gf-cli`工具内置到系统中并进行在线执行调整,从而提供更可靠和一致的生成代码功能。
+
+- 后续我们也将开放在线运行`gf gen ...`功能。在做插件开发时也会支持到在线生成插件下的service接口,这将会使得插件开发更加方便
+
+
+
+### 指定gf-cli版本
+
+- HotGo多数情况下会和最新版本的gf-cli保持同步,如果更新不及时或你不想使用最新版本的gf-cli来生成代码,可以找到自己想要的版本进行替换即可。
+
+- 下面大致做一些替换步骤说明:
+
+1. 打开`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](../../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`
diff --git a/docs/guide-zh-CN/code-tree.md b/docs/guide-zh-CN/code-tree.md
new file mode 100644
index 0000000..8d7cc22
--- /dev/null
+++ b/docs/guide-zh-CN/code-tree.md
@@ -0,0 +1,93 @@
+## 生成树型CURD
+
+### 热编译启动
+- 推荐使用热编译方式启动HotGo,这样生成完成页面自动刷新即可看到新生成内容,无需手动重启
+
+```shell
+# 服务端
+cd server
+gf run main.go
+
+# web端
+cd web
+yarn dev
+```
+
+以下是一个基本的树形CURD生成流程
+
+### 创建表结构
+- 以下表结构和数据为了方便功能演示已经内置无需再次创建
+
+```sql
+CREATE TABLE `hg_sys_gen_tree_demo` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
+ `pid` bigint(20) DEFAULT NULL COMMENT '上级ID',
+ `level` int(11) DEFAULT '1' COMMENT '关系树级别',
+ `tree` varchar(512) COMMENT '关系树',
+ `category_id` bigint(20) DEFAULT '0' COMMENT '分类ID',
+ `title` varchar(64) NOT NULL COMMENT '标题',
+ `description` varchar(255) DEFAULT '' COMMENT '描述',
+ `sort` int(11) DEFAULT NULL COMMENT '排序',
+ `status` tinyint(1) DEFAULT '1' COMMENT '状态',
+ `created_by` bigint(20) DEFAULT '0' COMMENT '创建者',
+ `updated_by` bigint(20) DEFAULT '0' COMMENT '更新者',
+ `created_at` datetime DEFAULT NULL COMMENT '创建时间',
+ `updated_at` datetime DEFAULT NULL COMMENT '修改时间',
+ `deleted_at` datetime DEFAULT NULL COMMENT '删除时间',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8mb4 COMMENT='系统_生成树表演示';
+```
+
+### 表字段要求
+- 使用生成时,表中必须具有以下字段和属性
+
+| 字段名称 | 字段含义 | 字段类型 | 可为空 |
+|--------|----------------------|---------------------|-----|
+| id | 主键ID | bigint(20) | 否 |
+| pid | 上级树ID | bigint(20) | 是 |
+| level | 关系树级别 | int(11) | 否 |
+| tree | 关系树 | varchar(512) | 是 |
+
+### 创建生成配置
+- 登录HotGo后台 -> 开发工具 -> 代码生成 -> 找到立即生成按钮并打开,选择和填写如下参数:
+
+
+
+
+### 基本设置
+- 确认无误后,点击生成配置会跳转到生成配置页面,如下:
+- 你可以在该页面调整生成表格表头/表列的接口功能、菜单权限、高级设置和关联表设置
+- 相比普通CURD,这里多了`树名称字段`和`树表格样式`选项
+
+
+
+### 表字段设置
+
+- 在该页面你可以调整生成表格字段名称、表单组件、表单编辑/验证项、列表展示/查询项、字段排序、重置和同步字段
+- 树表中的pid、level、tree字段由系统维护,单独设置编辑表单功能无效!
+
+
+
+### 预览并生成
+
+点击`预览代码`查看生成的代码内容。如果无需继续调整直接点击`提交生成`即可,以下是预览代码效果:
+
+
+
+- 如果你使用的热编译,那么页面会在生成成功后立即刷新,刷新完成你即可在后台菜单栏中看到`测试表格`菜单。如果不是使用热编译启动,请手动重启服务后刷新。
+
+
+### 生成完成
+
+- 让我们看看生成的表格页面,效果如下:
+
+- 普通树表
+
+
+- 选项式树表
+
+
+### 常见问题
+
+- [生成常见问题](code-help.md)
+
diff --git a/docs/guide-zh-CN/images/code/curd-add.png b/docs/guide-zh-CN/images/code/curd-add.png
new file mode 100644
index 0000000..14ef083
Binary files /dev/null and b/docs/guide-zh-CN/images/code/curd-add.png differ
diff --git a/docs/guide-zh-CN/images/code/curd-fields.png b/docs/guide-zh-CN/images/code/curd-fields.png
new file mode 100644
index 0000000..00fe790
Binary files /dev/null and b/docs/guide-zh-CN/images/code/curd-fields.png differ
diff --git a/docs/guide-zh-CN/images/code/curd-init.png b/docs/guide-zh-CN/images/code/curd-init.png
new file mode 100644
index 0000000..c0179f2
Binary files /dev/null and b/docs/guide-zh-CN/images/code/curd-init.png differ
diff --git a/docs/guide-zh-CN/images/code/curd-list.png b/docs/guide-zh-CN/images/code/curd-list.png
new file mode 100644
index 0000000..11ed610
Binary files /dev/null and b/docs/guide-zh-CN/images/code/curd-list.png differ
diff --git a/docs/guide-zh-CN/images/code/curd-preview.png b/docs/guide-zh-CN/images/code/curd-preview.png
new file mode 100644
index 0000000..1d73d27
Binary files /dev/null and b/docs/guide-zh-CN/images/code/curd-preview.png differ
diff --git a/docs/guide-zh-CN/images/code/join-add.png b/docs/guide-zh-CN/images/code/join-add.png
new file mode 100644
index 0000000..07cab88
Binary files /dev/null and b/docs/guide-zh-CN/images/code/join-add.png differ
diff --git a/docs/guide-zh-CN/images/code/join-fields.png b/docs/guide-zh-CN/images/code/join-fields.png
new file mode 100644
index 0000000..39dca79
Binary files /dev/null and b/docs/guide-zh-CN/images/code/join-fields.png differ
diff --git a/docs/guide-zh-CN/images/code/join-fields2.png b/docs/guide-zh-CN/images/code/join-fields2.png
new file mode 100644
index 0000000..e078942
Binary files /dev/null and b/docs/guide-zh-CN/images/code/join-fields2.png differ
diff --git a/docs/guide-zh-CN/images/code/join-init.png b/docs/guide-zh-CN/images/code/join-init.png
new file mode 100644
index 0000000..6e387e2
Binary files /dev/null and b/docs/guide-zh-CN/images/code/join-init.png differ
diff --git a/docs/guide-zh-CN/images/code/join-list.png b/docs/guide-zh-CN/images/code/join-list.png
new file mode 100644
index 0000000..5d60678
Binary files /dev/null and b/docs/guide-zh-CN/images/code/join-list.png differ
diff --git a/docs/guide-zh-CN/images/code/join-preview.png b/docs/guide-zh-CN/images/code/join-preview.png
new file mode 100644
index 0000000..741a44b
Binary files /dev/null and b/docs/guide-zh-CN/images/code/join-preview.png differ
diff --git a/docs/guide-zh-CN/images/code/tree-add.png b/docs/guide-zh-CN/images/code/tree-add.png
new file mode 100644
index 0000000..b5dce67
Binary files /dev/null and b/docs/guide-zh-CN/images/code/tree-add.png differ
diff --git a/docs/guide-zh-CN/images/code/tree-fields.png b/docs/guide-zh-CN/images/code/tree-fields.png
new file mode 100644
index 0000000..a998a20
Binary files /dev/null and b/docs/guide-zh-CN/images/code/tree-fields.png differ
diff --git a/docs/guide-zh-CN/images/code/tree-init.png b/docs/guide-zh-CN/images/code/tree-init.png
new file mode 100644
index 0000000..d5c7bdc
Binary files /dev/null and b/docs/guide-zh-CN/images/code/tree-init.png differ
diff --git a/docs/guide-zh-CN/images/code/tree-list.png b/docs/guide-zh-CN/images/code/tree-list.png
new file mode 100644
index 0000000..e93b4b3
Binary files /dev/null and b/docs/guide-zh-CN/images/code/tree-list.png differ
diff --git a/docs/guide-zh-CN/images/code/tree-list2.png b/docs/guide-zh-CN/images/code/tree-list2.png
new file mode 100644
index 0000000..ec4a7c4
Binary files /dev/null and b/docs/guide-zh-CN/images/code/tree-list2.png differ
diff --git a/docs/guide-zh-CN/images/code/tree-preview.png b/docs/guide-zh-CN/images/code/tree-preview.png
new file mode 100644
index 0000000..79d7115
Binary files /dev/null and b/docs/guide-zh-CN/images/code/tree-preview.png differ
diff --git a/docs/guide-zh-CN/images/demo/1.png b/docs/guide-zh-CN/images/demo/1.png
new file mode 100644
index 0000000..f140c18
Binary files /dev/null and b/docs/guide-zh-CN/images/demo/1.png differ
diff --git a/docs/guide-zh-CN/images/demo/10.png b/docs/guide-zh-CN/images/demo/10.png
new file mode 100644
index 0000000..90c4623
Binary files /dev/null and b/docs/guide-zh-CN/images/demo/10.png differ
diff --git a/docs/guide-zh-CN/images/demo/11.png b/docs/guide-zh-CN/images/demo/11.png
new file mode 100644
index 0000000..ef5d41c
Binary files /dev/null and b/docs/guide-zh-CN/images/demo/11.png differ
diff --git a/docs/guide-zh-CN/images/demo/12.png b/docs/guide-zh-CN/images/demo/12.png
new file mode 100644
index 0000000..ff37ff4
Binary files /dev/null and b/docs/guide-zh-CN/images/demo/12.png differ
diff --git a/docs/guide-zh-CN/images/demo/2.png b/docs/guide-zh-CN/images/demo/2.png
new file mode 100644
index 0000000..722d49e
Binary files /dev/null and b/docs/guide-zh-CN/images/demo/2.png differ
diff --git a/docs/guide-zh-CN/images/demo/3.png b/docs/guide-zh-CN/images/demo/3.png
new file mode 100644
index 0000000..c5580ca
Binary files /dev/null and b/docs/guide-zh-CN/images/demo/3.png differ
diff --git a/docs/guide-zh-CN/images/demo/4.png b/docs/guide-zh-CN/images/demo/4.png
new file mode 100644
index 0000000..be6746c
Binary files /dev/null and b/docs/guide-zh-CN/images/demo/4.png differ
diff --git a/docs/guide-zh-CN/images/demo/5.png b/docs/guide-zh-CN/images/demo/5.png
new file mode 100644
index 0000000..a1302b1
Binary files /dev/null and b/docs/guide-zh-CN/images/demo/5.png differ
diff --git a/docs/guide-zh-CN/images/demo/6.png b/docs/guide-zh-CN/images/demo/6.png
new file mode 100644
index 0000000..46501dc
Binary files /dev/null and b/docs/guide-zh-CN/images/demo/6.png differ
diff --git a/docs/guide-zh-CN/images/demo/7.png b/docs/guide-zh-CN/images/demo/7.png
new file mode 100644
index 0000000..7d11c0e
Binary files /dev/null and b/docs/guide-zh-CN/images/demo/7.png differ
diff --git a/docs/guide-zh-CN/images/demo/8.png b/docs/guide-zh-CN/images/demo/8.png
new file mode 100644
index 0000000..6e54af8
Binary files /dev/null and b/docs/guide-zh-CN/images/demo/8.png differ
diff --git a/docs/guide-zh-CN/images/demo/9.png b/docs/guide-zh-CN/images/demo/9.png
new file mode 100644
index 0000000..0dc54fb
Binary files /dev/null and b/docs/guide-zh-CN/images/demo/9.png differ
diff --git a/docs/guide-zh-CN/images/sys-code-add.png b/docs/guide-zh-CN/images/sys-code-add.png
deleted file mode 100644
index 322ff80..0000000
Binary files a/docs/guide-zh-CN/images/sys-code-add.png and /dev/null differ
diff --git a/docs/guide-zh-CN/images/sys-code-config-init.png b/docs/guide-zh-CN/images/sys-code-config-init.png
deleted file mode 100644
index 9f395f1..0000000
Binary files a/docs/guide-zh-CN/images/sys-code-config-init.png and /dev/null differ
diff --git a/docs/guide-zh-CN/images/sys-code-config-join-save.png b/docs/guide-zh-CN/images/sys-code-config-join-save.png
deleted file mode 100644
index 8c639cf..0000000
Binary files a/docs/guide-zh-CN/images/sys-code-config-join-save.png and /dev/null differ
diff --git a/docs/guide-zh-CN/images/sys-code-config-join.png b/docs/guide-zh-CN/images/sys-code-config-join.png
deleted file mode 100644
index d7fff64..0000000
Binary files a/docs/guide-zh-CN/images/sys-code-config-join.png and /dev/null differ
diff --git a/docs/guide-zh-CN/images/sys-code-config-post.png b/docs/guide-zh-CN/images/sys-code-config-post.png
deleted file mode 100644
index e44337c..0000000
Binary files a/docs/guide-zh-CN/images/sys-code-config-post.png and /dev/null differ
diff --git a/docs/guide-zh-CN/images/sys-code-list-edit-ok.png b/docs/guide-zh-CN/images/sys-code-list-edit-ok.png
deleted file mode 100644
index 8eb9ab7..0000000
Binary files a/docs/guide-zh-CN/images/sys-code-list-edit-ok.png and /dev/null differ
diff --git a/docs/guide-zh-CN/images/sys-code-list-ok.png b/docs/guide-zh-CN/images/sys-code-list-ok.png
deleted file mode 100644
index e2d878d..0000000
Binary files a/docs/guide-zh-CN/images/sys-code-list-ok.png and /dev/null differ
diff --git a/docs/guide-zh-CN/images/sys-code-list.png b/docs/guide-zh-CN/images/sys-code-list.png
deleted file mode 100644
index 6cedd97..0000000
Binary files a/docs/guide-zh-CN/images/sys-code-list.png and /dev/null differ
diff --git a/docs/guide-zh-CN/images/sys-code-master-save.png b/docs/guide-zh-CN/images/sys-code-master-save.png
deleted file mode 100644
index ba72388..0000000
Binary files a/docs/guide-zh-CN/images/sys-code-master-save.png and /dev/null differ
diff --git a/docs/guide-zh-CN/images/sys-code-master.png b/docs/guide-zh-CN/images/sys-code-master.png
deleted file mode 100644
index 4017ddb..0000000
Binary files a/docs/guide-zh-CN/images/sys-code-master.png and /dev/null differ
diff --git a/docs/guide-zh-CN/images/sys-code-preview.png b/docs/guide-zh-CN/images/sys-code-preview.png
deleted file mode 100644
index b48c720..0000000
Binary files a/docs/guide-zh-CN/images/sys-code-preview.png and /dev/null differ
diff --git a/docs/guide-zh-CN/images/sys-db-by.png b/docs/guide-zh-CN/images/sys-db-by.png
new file mode 100644
index 0000000..4dd6c3e
Binary files /dev/null and b/docs/guide-zh-CN/images/sys-db-by.png differ
diff --git a/docs/guide-zh-CN/images/sys-db-by2.png b/docs/guide-zh-CN/images/sys-db-by2.png
new file mode 100644
index 0000000..bf25dfa
Binary files /dev/null and b/docs/guide-zh-CN/images/sys-db-by2.png differ
diff --git a/docs/guide-zh-CN/start-environment.md b/docs/guide-zh-CN/start-environment.md
index 275150d..2741e17 100644
--- a/docs/guide-zh-CN/start-environment.md
+++ b/docs/guide-zh-CN/start-environment.md
@@ -15,7 +15,7 @@
5. 命令行运行 `yarn -v` 若控制台输出版本号则前端环境搭建成功
### 后端环境
-1. 下载golang安装 版本号需>=1.19
+1. 下载golang安装 版本号需>=1.21
2. 国际: https://golang.org/dl/
3. 国内: https://golang.google.cn/dl/
4. 命令行运行 go 若控制台输出各类提示命令 则安装成功 输入 `go version` 确认版本大于1.19
@@ -26,6 +26,6 @@
> 需要本地具有 git node golang 环境
- node版本 >= 16.0.0
-- golang版本 >= 1.19
+- golang版本 >= 1.21
- mysql版本 >= 5.7,引擎需要是 innoDB
- IDE推荐:Goland
diff --git a/docs/guide-zh-CN/start-installation.md b/docs/guide-zh-CN/start-installation.md
index 1831f13..4c05b19 100644
--- a/docs/guide-zh-CN/start-installation.md
+++ b/docs/guide-zh-CN/start-installation.md
@@ -8,8 +8,8 @@
### 环境要求
- node版本 >= v16.0.0
-- golang版本 >= v1.19
-- goframe版本 >=v2.6.4
+- golang版本 >= v1.21
+- goframe版本 >=v2.7.0
- mysql版本 >=5.7
> 必须先看[环境搭建文档](start-environment.md),如果安装遇到问题务必先查看[常见问题文档](start-issue.md)
@@ -28,18 +28,37 @@ git clone https://github.com/bufanyun/hotgo.git && cd hotgo
1、服务端:
- 项目数据库文件 `storage/data/hotgo.sql` 创建数据库并导入
- 将配置文件 `manifest/config/config.yaml.bak` 复制后改为`manifest/config/config.yaml`
-- 将`manifest/config/config.yaml`中的数据库配置改为你自己的:
+- 将`manifest/config/config.yaml`中的`database.default.link`数据库配置改为你自己的:
```yaml
+# Database. 配置参考:https://goframe.org/pages/viewpage.action?pageId=1114245
database:
logger:
- level: "all"
+ path: "logs/database" # 日志文件路径。默认为空,表示关闭,仅输出到终端
+ <<: *defaultLogger
stdout: true
default:
- link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true"
+ link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true&charset=utf8mb4"
debug: true
Prefix: "hg_"
```
+- 将`hack/config.yaml`中的`gfcli.gen.dao[0].link`数据库配置改为你自己的:
+```yaml
+gfcli:
+ gen:
+ dao:
+ - link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true&charset=utf8mb4"
+ group: "default" # 分组 使用hotgo代码生成功能时必须填
+ # tables: "" # 指定当前数据库中需要执行代码生成的数据表。如果为空,表示数据库的所有表都会生成。
+ tablesEx: "hg_sys_addons_install" # 指定当前数据库中需要排除代码生成的数据表。
+ removePrefix: "hg_"
+ descriptionTag: true
+ noModelComment: true
+ jsonCase: "CamelLower"
+ gJsonSupport: true
+ clear: false
+```
+
2、web前端:
- 配置服务端地址,包含在以下文件中:
* /hotgo/web/.env.development
diff --git a/docs/guide-zh-CN/start-issue.md b/docs/guide-zh-CN/start-issue.md
index cadd9a7..4434817 100644
--- a/docs/guide-zh-CN/start-issue.md
+++ b/docs/guide-zh-CN/start-issue.md
@@ -45,6 +45,13 @@
详细请参考 - [系统安装](start-installation.md)
+#### 3、cannot find "hack/config.yaml" in following paths:
+> 报错信息:get cli configuration file:hack/config.yaml, err:cannot find "hack/config.yaml" in following paths:
+
+系统运行目录下配置hack/config.yaml文件。如果是生产环境运行,并且不需要开发工具相关功能,可以将`manifest/config/config.yaml`配置文件中的`system.mode`值改为`product`,这样启动时不会加载开发工具相关功能
+
+
+
### 四、前端相关
#### 1、Error: connect ECONNREFUSED ::1:8000
diff --git a/docs/guide-zh-CN/start-update-log.md b/docs/guide-zh-CN/start-update-log.md
index 8e9fe0b..a307e3e 100644
--- a/docs/guide-zh-CN/start-update-log.md
+++ b/docs/guide-zh-CN/start-update-log.md
@@ -11,6 +11,31 @@
> 如果升级(覆盖)代码后打开会出现 sql 报错, 请检查更新的数据库格式或自行调整
+### v2.15.1
+updated 2024.4.22
+
+- 增加:生成代码增加树表生成、字段排序、字段重置、操作人维护,调整关联表生成方式
+- 增加:角色菜单权限增加一键导入支持
+- 增加:访问日志增加忽略请求方式
+- 增加:插件生成增加`consts`目录维护
+- 增加:IP归属地定位增加默认重试,增加新的缓存机制
+- 增加:登录日志增加UA信息、IP归属信息,解藕访问日志
+- 增加:消息队列`rocketmq`驱动更新版本问题,增加延迟队列,支持自动创建主题
+- 增加:部门增加类型支持,通过部门类型实现多租户业务支持
+- 增加:增加多租户业务开发演示
+- 修复:修复部门列表搜索算法在部分情况下无效问题
+- 修复:修复短信、邮件IP发送数量限制
+- 修复:修复分布式锁内存泄漏问题
+- 优化:gf版本升级到v2.7.0
+- 优化:naive-ui版本升级到2.38.1
+- 优化:优化`cmd`服务退出流程
+- 优化:优化生成代码格式化和移除未使用的包
+- 优化:服务端配置文件参数`hotgo`改为`system`
+- 优化:web端表格/表单增加自适应宽度计算
+- 优化:日期选择组件不再使用默认值
+- 优化:优化菜单添加/编辑表单,使其操作更加简单方便
+
+
### v2.13.1
updated 2024.3.7
diff --git a/docs/guide-zh-CN/sys-catalog.md b/docs/guide-zh-CN/sys-catalog.md
index e61d0a5..bf9d040 100644
--- a/docs/guide-zh-CN/sys-catalog.md
+++ b/docs/guide-zh-CN/sys-catalog.md
@@ -70,6 +70,7 @@
| --- --- --- api | 前台通用接口,包含PC端、移动端接口等 |
| --- --- --- home | 前台PC端、H5端页面 |
| --- --- --- websocket | 可同时为多应用提供websocket接口 |
+| --- --- consts | 插件内主要的常量定义 |
| --- --- controller | 接收/解析用户输入参数的入口/接口层,也可以理解为控制器 |
| --- --- crons | 项目中由系统统一接管的定时任务处理 |
| --- --- global | 项目内主要的全局变量和系统的一些初始化操作 |
diff --git a/docs/guide-zh-CN/sys-code.md b/docs/guide-zh-CN/sys-code.md
index 9dee11e..63c69be 100644
--- a/docs/guide-zh-CN/sys-code.md
+++ b/docs/guide-zh-CN/sys-code.md
@@ -3,13 +3,15 @@
目录
- 使用条件
-- 生成配置
-- 一个生成增删改查列表例子
-- 多数据库生成配置
+- 模板配置
+- CLI配置
+- 生成CRUD表格
+- 生成树形表格
+- 多数据库使用
- 自定义生成模板
- 内置gf-cli
- 指定gf-cli版本
-- 指定数据库驱动类型
+- 指定数据库驱动
> 在HotGo中可以通过后台开发工具快速的一键生成CRUD,自动生成Api、控制器、业务逻辑、Web页面、表单组件、菜单权限等。
@@ -17,33 +19,23 @@
### 使用条件
-- hotgo 版本号 >= 2.2.10
+- hotgo 版本号 >= 2.13.1
- 使用前必须先看 [数据库](sys-db.md)
-#### 增删改查列表
-
-- 表自增长字段为 `id`
-
-#### 关系树列表
-
-- 表自增长字段为 `id`
-
### 生成模板配置
-- 注意:线上环境务必将`allowedIPs`参数设为空,考虑到项目安全问题请勿线上生成使用!
-
-- 默认配置路径:server/manifest/config/config.yaml
-
+- 配置路径:server/manifest/config/config.yaml
```yaml
+# 生成代码
hggen:
- allowedIPs: ["127.0.0.1", "*"] # 白名单,*代表所有,只有允许的IP后台才能使用生成代码功能
- selectDbs: [ "default" ] # 可选生成表的数据库配置名称,支持多库
- disableTables : ["hg_sys_gen_codes","hg_admin_role_casbin"] # 禁用的表,禁用以后将不会在选择表中看到
- delimiters: ["@{", "}"] # 模板引擎变量分隔符号
+ allowedIPs: [ "127.0.0.1", "*" ] # 白名单,*代表所有,只有允许的IP后台才能使用生成代码功能
+ selectDbs: [ "default" ] # 可选生成表的数据库配置名称,支持多库
+ disableTables: [ "hg_sys_gen_codes","hg_admin_role_casbin" ] # 禁用的表,禁用以后将不会在选择表中看到
+ delimiters: [ "@{", "}" ] # 模板引擎变量分隔符号
# 生成应用模型,所有生成模板允许自定义,可以参考default模板进行改造
application:
- # CRUD模板
+ # CRUD和关系树列表模板
crud:
templates:
# 默认的主包模板
@@ -51,34 +43,28 @@ hggen:
isAddon: false # 是否为插件模板 false|true
masterPackage: "sys" # 主包名称,需和controllerPath、logicPath、inputPath保持关联
templatePath: "./resource/generate/default/curd" # 模板路径
- apiPath: "./api/admin" # gfApi生成路径
+ apiPath: "./api/admin" # goApi生成路径
controllerPath: "./internal/controller/admin/sys" # 控制器生成路径
- logicPath : "./internal/logic/sys" # 主要业务生成路径
+ logicPath: "./internal/logic/sys" # 主要业务生成路径
inputPath: "./internal/model/input/sysin" # 表单过滤器生成路径
- routerPath : "./internal/router/genrouter" # 生成路由表路径
- sqlPath : "./storage/data/generate" # 生成sql语句路径
+ routerPath: "./internal/router/genrouter" # 生成路由表路径
+ sqlPath: "./storage/data/generate" # 生成sql语句路径
webApiPath: "../web/src/api" # webApi生成路径
- webViewsPath : "../web/src/views" # web页面生成路径
+ webViewsPath: "../web/src/views" # web页面生成路径
- # 默认的插件包模板
+ # 默认的插件包模板,{$name}会自动替换成实际的插件名称
- group: "addon" # 分组名称
isAddon: true # 是否为插件模板 false|true
masterPackage: "sys" # 主包名称,需和controllerPath、logicPath、inputPath保持关联
templatePath: "./resource/generate/default/curd" # 模板路径
- apiPath: "./addons/{$name}/api/admin" # gfApi生成路径
+ apiPath: "./addons/{$name}/api/admin" # goApi生成路径
controllerPath: "./addons/{$name}/controller/admin/sys" # 控制器生成路径
- logicPath : "./addons/{$name}/logic/sys" # 主要业务生成路径
+ logicPath: "./addons/{$name}/logic/sys" # 主要业务生成路径
inputPath: "./addons/{$name}/model/input/sysin" # 表单过滤器生成路径
- routerPath : "./addons/{$name}/router/genrouter" # 生成路由表路径
- sqlPath : "./storage/data/generate/addons" # 生成sql语句路径
+ routerPath: "./addons/{$name}/router/genrouter" # 生成路由表路径
+ sqlPath: "./storage/data/generate/addons" # 生成sql语句路径
webApiPath: "../web/src/api/addons/{$name}" # webApi生成路径
- webViewsPath : "../web/src/views/addons/{$name}" # web页面生成路径
-
- # 关系树列表模板
- tree:
- templates:
- - group: "default"
- templatePath: "./resource/generate/default/tree"
+ webViewsPath: "../web/src/views/addons/{$name}" # web页面生成路径
# 消息队列模板
queue:
@@ -91,14 +77,19 @@ hggen:
templates:
- group: "default"
templatePath: "./resource/generate/default/cron"
+
+ # 生成插件模块,通过后台创建新插件时使用的模板,允许自定义,可以参考default模板进行改造
+ addon:
+ srcPath: "./resource/generate/default/addon" # 生成模板路径
+ webApiPath: "../web/src/api/addons/{$name}" # webApi生成路径
+ webViewsPath: "../web/src/views/addons/{$name}" # web页面生成路径
```
-### 生成dao、service配置
-
+### 生成CLI配置
- hotgo在生成dao、service配置时,默认了和gf官方一致的配置方式和代码生成规则。所以无论你是通过hotgo亦或gf命令生成,最终代码格式完全一致,遵循一致的代码规范。
-- 默认配置路径:server/hack/config.yaml
+- 配置路径:[server/hack/config.yaml](../../server/hack/config.yaml)
```yaml
gfcli:
@@ -134,14 +125,14 @@ gfcli:
clear: true
```
-### 一个生成增删改查列表例子
+### 生成CRUD表格
- 推荐使用热编译方式启动HotGo,这样生成完成页面自动刷新即可看到新生成内容,无需手动重启
- 服务端热编译启动:`gf run main.go`, web前端启动:`yarn dev`
1、创建数据表
-1.1 创建测试表格表`hg_test_table`:
+1.1 创建测试表格表`hg_test_table`
```mysql
CREATE TABLE `hg_test_table` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
@@ -221,7 +212,7 @@ INSERT INTO `hg_test_table` (`id`, `category_id`, `title`, `description`, `conte
- 如果你使用的热编译,那么页面会在生成成功后立即刷新,刷新完成你即可在后台菜单栏中看到`测试表格`菜单。如果不是使用热编译启动,请手动重启服务后刷新。
-#### 注意:热编译环境下,web端往往会快于服务端重启并加载完成,此时访问新生成的菜单页面可能会存在某接口请求超时或404问题,这是服务端正在重启导致的,属于正常现象,一般稍等几秒再试即可。
+- 注意:热编译环境下,web端往往会快于服务端重启并加载完成,此时访问新生成的菜单页面可能会存在某接口请求超时或404问题,这是服务端正在重启导致的,属于正常现象,一般稍等几秒再试即可。
- 接下来让我们看看生成的表格页面,效果如下:
@@ -263,19 +254,20 @@ INSERT INTO `hg_test_table` (`id`, `category_id`, `title`, `description`, `conte
- 至此生成增删改查列表示例结束!
+### 生成树形表格
+待写。
-## 多数据库生成配置
+### 多数据库使用
-#### 假设我们要增加一个库名为`hotgo2`、分组为`default2`的数据库,并要为其生成代码
+- 假设我们要增加一个库名为`hotgo2`、分组为`default2`的数据库,并要为其生成代码
-1. 配置`server/hack/config.yaml`,如下:
+1. 配置[server/hack/config.yaml](../../server/hack/config.yaml) 如下:
```yaml
gen:
dao:
- link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true"
group: "default" # 分组 使用hotgo代码生成功能时必须填
- # tables: "" # 指定当前数据库中需要执行代码生成的数据表。如果为空,表示数据库的所有表都会生成。
tablesEx: "hg_sys_addons_install" # 指定当前数据库中需要排除代码生成的数据表。
removePrefix: "hg_"
descriptionTag: true
@@ -285,8 +277,7 @@ INSERT INTO `hg_test_table` (`id`, `category_id`, `title`, `description`, `conte
clear: false
- link: "mysql:hotgo2:hg123456.@tcp(127.0.0.1:3306)/hotgo2?loc=Local&parseTime=true"
group: "default2" # 分组 使用hotgo代码生成功能时必须填
- # tables: "" # 指定当前数据库中需要执行代码生成的数据表。如果为空,表示数据库的所有表都会生成。
- tablesEx: "hg_sys_addons_install" # 指定当前数据库中需要排除代码生成的数据表。
+ tablesEx: "hg_sys_addons_install" # 指定当前数据库中需要排除代码生成的数据表。
removePrefix: ""
descriptionTag: true
noModelComment: true
@@ -297,7 +288,6 @@ INSERT INTO `hg_test_table` (`id`, `category_id`, `title`, `description`, `conte
2. 配置`server/manifest/config/config.yaml`,
-
`database`配置如下:
```yaml
database:
@@ -318,21 +308,21 @@ database:
```yaml
hggen:
allowedIPs: ["127.0.0.1", "*"] # 白名单,*代表所有,只有允许的IP后台才能使用生成代码功能
- selectDbs: [ "default", "default2" ] # 可选生成表的数据库配置名称,支持多库
+ selectDbs: [ "default", "default2" ] # 可选生成表的数据库配置名称,支持多库
disableTables : ["hg_sys_gen_codes","hg_admin_role_casbin"] # 禁用的表,禁用以后将不会在选择表中看到
delimiters: ["@{", "}"] # 模板引擎变量分隔符号
```
3. 登录HotGo后台 -> 开发工具 -> 代码生成 -> 找到立即生成按钮并打开,就会发现`数据库`选项增加了一个`default2`,后续生成步骤和生成例子完全一样
-
> 注意:上述的配置中所有的`default2`名称必须保持一致
+
### 自定义生成模板
-> 系统内置了两组CURD生成模板,请参考[生成配置]。default:是默认的生成到主模块下;addon:是默认生成到指定的插件下
+> 系统内置了两组CURD生成模板,请参考[生成模板配置](sys-code.md#生成模板配置)。default:是默认的生成到主模块下;addon:是默认生成到指定的插件下
-- 如果你在实际的开发过程中默认模板需要调整的地方较多时,HotGo允许你新建新的模板分组来满足你的需求。新的模板可根据现有模板基础拷贝一份出来做改造,默认模板目录:[server/resource/generate/default](../../server/resource/generate/default)
+- HotGo允许你新建新的模板分组来满足你的需求,模板可根据现有模板基础拷贝一份出来做改造,默认模板目录:[server/resource/generate/default](../../server/resource/generate/default)
@@ -358,7 +348,7 @@ hggen:
-### 指定数据库驱动类型
+### 指定数据库驱动
> HotGo默认使用mysql驱动,如果你想用其他数据库驱动打开下方文件中注释即可
diff --git a/docs/guide-zh-CN/sys-db.md b/docs/guide-zh-CN/sys-db.md
index 7a96fed..369a132 100644
--- a/docs/guide-zh-CN/sys-db.md
+++ b/docs/guide-zh-CN/sys-db.md
@@ -6,8 +6,7 @@
- 特殊字段默认表单组件
- 特殊字段默认表单验证器
- SQL默认查询方式
-- 其他默认选项
-- 常见问题
+- 其他
### 字段类型
@@ -85,7 +84,7 @@
-### 其他默认选项
+### 其他
#### 默认字典选项
@@ -94,8 +93,8 @@
#### 默认属性
- 默认必填,当数据库字段存在非空`IS_NULLABLE`属性时,默认勾选必填验证
-- 默认唯一,当数据库字段属性存在`UNI`时,默认勾选唯一值验证
-- 默认主键,当数据库字段属性存在`PRI`时,默认为主键,不允许编辑
+- 默认唯一,当数据库字段索引存在`UNI`时,默认勾选唯一值验证
+- 默认主键,当数据库字段索引存在`PRI`时,默认为主键,不允许编辑
- 默认排序,当数据库字段存在`sort`时,默认开启排序,添加表单自动获取最大排序增量值并填充表单
- 默认列名,默认使用字段注释作为表格的列名。当数据库字段未设置注释时,默认使用字段名称作为列名
@@ -106,10 +105,45 @@
- 软删除,表存在字段`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)
-
-#### 常见问题
-
-#### 1、生成表格字段如何排序?
-
-表格字端排序默认使用的是数据库物理字段的排序位置,所以如果你对字段的位置顺序有要求,请在生成前调整数据库物理字段位置
diff --git a/docs/guide-zh-CN/sys-library.md b/docs/guide-zh-CN/sys-library.md
index e359633..99471c1 100644
--- a/docs/guide-zh-CN/sys-library.md
+++ b/docs/guide-zh-CN/sys-library.md
@@ -154,7 +154,7 @@ func test(ctx context.Context) {
### 数据字典
-- hotgo增加了对枚举字典和自定义方法字典的内置支持,从而在系统中经常使用的一些特定数据维护基础上做出了增强。
+- hotgo增加了对枚举字典和自定义方法字典的内置支持,从而在系统中经常使用的一些特定数据上做出了增强。
#### 字典数据选项
- 文件路径:server/internal/model/dict.go
diff --git a/docs/guide-zh-CN/sys-queue.md b/docs/guide-zh-CN/sys-queue.md
index e22d849..f08b280 100644
--- a/docs/guide-zh-CN/sys-queue.md
+++ b/docs/guide-zh-CN/sys-queue.md
@@ -14,33 +14,33 @@
- 配置文件:server/manifest/config/config.yaml
```yaml
-#消息队列
+# 消息队列
queue:
switch: true # 队列开关,可选:true|false,默认为true
- driver: "disk" # 队列驱动,可选:redis|rocketmq|kafka,默认为disk
- retry: 2 # 重试次数,仅rocketmq|redis支持
+ driver: "disk" # 队列驱动,可选:disk|redis|rocketmq|kafka,默认为disk
groupName: "hotgo" # mq群组名称
- #磁盘队列
+ # 磁盘队列
disk:
path: "./storage/diskqueue" # 数据存放路径
batchSize: 100 # 每100条消息同步一次,batchSize和batchTime满足其一就会同步一次
batchTime: 1 # 每1秒消息同步一次
segmentSize: 10485760 # 每个topic分片数据文件最大字节,默认10M
segmentLimit: 3000 # 每个topic最大分片数据文件数量,超出部分将会丢弃
+ # redis,默认使用全局redis运行队列
redis:
- address: "127.0.0.1:6379" # redis服务地址,默认为127.0.0.1:6379
- db: 2 # 指定redis库
- pass: "" # redis密码
- timeout: 0 # 队列超时时间(s) ,0为永不超时,当队列一直没有被消费到达超时时间则队列会被销毁
+ timeout: 0 # 队列超时时间以秒为单位,0表示永不超时。如果队列在设定的超时时间内没有被消费,则会被销毁
rocketmq:
- address: "127.0.0.1:9876" # brocker地址+端口
- logLevel: "all" # 系统日志级别,可选:all|close|debug|info|warn|error|fatal
+ nameSrvAdders: ["127.0.0.1:9876"] # nameSrvAdder+端口,支持多个
+ accessKey: "" # 选填。如果开启了acl控制就必填
+ secretKey: "" # 选填。如果开启了acl控制就必填
+ brokerAddr: "127.0.0.1:10911" # brokerAddr+端口,选填。用于消费者订阅主题前会检查主题是否存在,不存在会自动创建。你也可以在rocketmq控制台手动创建主题
+ retry: 0 # 重试次数
+ logLevel: "info" # 系统日志级别,可选:all|close|debug|info|warn|error|fatal
kafka:
address: "127.0.0.1:9092" # kafka地址+端口
version: "2.0.0.0" # kafka专属配置,默认2.0.0.0
randClient: true # 开启随机生成clientID,可以实现启动多实例同时一起消费相同topic,加速消费能力的特性,默认为true
multiConsumer: true # 是否支持创建多个消费者
-
```
### 实现接口
@@ -123,7 +123,7 @@ func test() {
```
-延迟队列,目前只有redis驱动支持:
+延迟队列,目前只有redis、rocketmq驱动支持:
```go
package main
@@ -140,10 +140,19 @@ func test() {
//...
}
- // 延迟10秒
+ // redis 延迟10秒
if err := queue.SendDelayMsg(consts.QueueLogTopic, data, 10); err != nil {
fmt.Printf("queue.Push err:%+v", err)
}
+
+ // rocketmq 延迟5秒
+ // 注意rocketmq这里传入的是延迟级别,而不是秒
+ // 消息的延时级别level一共有18级,分别为:
+ // 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
+ // 参考:https://github.com/apache/rocketmq-client-go/blob/0e19ee654819bda396a08d950c883f9008b8222b/primitive/message.go#L174
+ if err := queue.SendDelayMsg(consts.QueueLogTopic, data, 2); err != nil {
+ fmt.Printf("queue.Push err:%+v", err)
+ }
}
```
@@ -178,10 +187,10 @@ type MqMsg struct {
Body []byte `json:"body"`
}
-
type MqProducer interface {
SendMsg(topic string, body string) (mqMsg MqMsg, err error)
SendByteMsg(topic string, body []byte) (mqMsg MqMsg, err error)
+ SendDelayMsg(topic string, body string, delay int64) (mqMsg MqMsg, err error)
}
type MqConsumer interface {
diff --git a/docs/guide-zh-CN/sys-tenant.md b/docs/guide-zh-CN/sys-tenant.md
new file mode 100644
index 0000000..931224e
--- /dev/null
+++ b/docs/guide-zh-CN/sys-tenant.md
@@ -0,0 +1,172 @@
+## SaaS多租户
+
+
+目录
+
+- 介绍
+- 职责划分
+- 如何在HotGo开发多租户业务
+- 多租户数据库设计
+- 多租户功能演示
+
+### 介绍
+
+SaaS系统多租户多应用设计,已成为互联网企业的重要发展建设方向。核心在于多租户SAAS系统独立前台、共享后台、共享数据库的平台应用架构。
+
+> 目前HotGo的部分基础功能并不完全支持多租户设计,但这并不妨碍开发者基于HotGo构建自己的多租户产品
+
+
+### 职责划分
+
+在SaaS系统多租户中,不同身份用户的职责功能划分假设可以是这样:
+
+> 这只是一个粗略的概念,实际开发时应根据业务来进行调整,每个身份也都不是必须的。
+
+| 身份 | 标识 | 职责和功能划分 |
+|-----------------|----------|-------------------------------------------------------|
+| 公司| company | 管理整个平台,包括商户和用户账户、系统设置以及其他全局性业务流程。 |
+| 租户 | tenant | 多租户系统中顶层实体客户、组织或实体。有自己的多个商户、用户、产品、订单等。拥有独立的数据隔离和安全边界。 |
+| 商户 | merchant | 受租户的监管和管理,可独立经营的实体。提供产品或服务,管理自己的业务,包括库存管理、订单处理、结算等。 |
+| 用户 | user | 真正购买产品或享受服务的人,与商户互动,管理个人信息等个性化功能。 |
+
+
+### 如何在HotGo开发多租户业务
+#### 一、应用功能
+
+根据角色来划分用户的后台功能,在创建用户时为其绑定角色,然后为不同角色分配不同的功能菜单
+
+请参考: [权限控制](sys-auth.md)
+
+
+#### 二、 数据隔离
+
+根据部门来划定用户的数据权限范围,在创建用户时为其绑定部门
+
+- 在用户登录成功后,server端可通过上下文来获取用户部门类型来确定用户身份
+- 文件路径:server/internal/library/contexts/context.go
+```go
+package contexts
+
+import (
+ "context"
+ "github.com/gogf/gf/v2/frame/g"
+ "github.com/gogf/gf/v2/net/ghttp"
+ "hotgo/internal/consts"
+ "hotgo/internal/model"
+)
+
+// GetDeptType 获取用户部门类型
+func GetDeptType(ctx context.Context) string {
+ user := GetUser(ctx)
+ if user == nil {
+ return ""
+ }
+ return user.DeptType
+}
+
+// IsCompanyDept 是否为公司部门
+func IsCompanyDept(ctx context.Context) bool {
+ return GetDeptType(ctx) == consts.DeptTypeCompany
+}
+
+// IsTenantDept 是否为租户部门
+func IsTenantDept(ctx context.Context) bool {
+ return GetDeptType(ctx) == consts.DeptTypeTenant
+}
+
+// IsMerchantDept 是否为商户部门
+func IsMerchantDept(ctx context.Context) bool {
+ return GetDeptType(ctx) == consts.DeptTypeMerchant
+}
+
+// IsUserDept 是否为普通用户部门
+func IsUserDept(ctx context.Context) bool {
+ return GetDeptType(ctx) == consts.DeptTypeUser
+}
+```
+
+- 在用户登录成功后,web端可通`useUserStore`来获取用户部门类型来确定用户身份
+- 文件路径:web/src/store/modules/user.ts
+```vue
+
+```
+
+### 多租户数据库设计
+
+HotGo定位是中小型应用开发,推荐采用一套数据库不同Schema。就是在多租户业务表中加入用户标识字段,来区分不同用户的数据,如:`tenant_id`
+
+- 参考文章:https://blog.csdn.net/haponchang/article/details/104246317
+
+
+### 多租户功能演示
+
+请登录后台【插件应用】-【功能案例】-【多租户功能演示】查看
+
+#### 自动维护租户关系
+
+- 只需在表设计时包含以下字段,即可使用handler和hook实现租户权限过滤和租户关系维护
+
+| 字段名称 | 数据类型 | 字段注释 | 必选 |
+|------|----------|------|-----------------------------------------------------|
+| tenant_id | bigint(20) | 租户ID | 否 |
+| merchant_id | bigint(20) | 商户ID | 否 |
+| user_id | bigint(20) | 用户ID | 否 |
+
+下面是多租户功能演示例子代码中的使用片段
+
+- 封装查询Model
+```go
+// Model 多租户功能演示ORM模型
+func (s *sSysTenantOrder) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
+ if len(option) == 0 {
+ // 过滤多租户数据权限
+ option = append(option, &handler.Option{
+ FilterTenant: true,
+ //FilterAuth: true, // 如果还需要维护created_by、member_id等部门数据权限范围可放开注释
+ })
+ }
+ return handler.Model(dao.AddonHgexampleTenantOrder.Ctx(ctx), option...)
+}
+```
+
+- 增改数据自动维护租户关系
+```go
+// Edit 修改/新增多租户功能演示
+func (s *sSysTenantOrder) Edit(ctx context.Context, in *sysin.TenantOrderEditInp) (err error) {
+ return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
+
+ // 修改
+ if in.Id > 0 {
+ if _, err = s.Model(ctx).
+ Fields(sysin.TenantOrderUpdateFields{}).
+ WherePri(in.Id).Data(in).
+ Hook(hook.SaveTenant). // 自动维护租户关系更新
+ Update(); err != nil {
+ }
+ return
+ }
+
+ // 新增
+ if _, err = dao.AddonHgexampleTenantOrder.Ctx(ctx).
+ Fields(sysin.TenantOrderInsertFields{}).
+ Hook(hook.SaveTenant). // 自动维护租户关系更新
+ Data(in).
+ Insert(); err != nil {
+ }
+ return
+ })
+}
+```
+
+相关代码文件:/server/addons/hgexample/logic/sys/tenant_order.go
+
diff --git a/server/addons/hgexample/api/admin/table/table.go b/server/addons/hgexample/api/admin/table/table.go
index 32f8b85..368c4dc 100644
--- a/server/addons/hgexample/api/admin/table/table.go
+++ b/server/addons/hgexample/api/admin/table/table.go
@@ -13,18 +13,18 @@ import (
// ListReq 查询列表
type ListReq struct {
- g.Meta `path:"/table/list" method:"get" tags:"表格" summary:"获取表格列表"`
+ g.Meta `path:"/table/list" method:"get" tags:"表格例子" summary:"获取表格列表"`
sysin.TableListInp
}
type ListRes struct {
form.PageRes
- List []*sysin.TableListModel `json:"list" dc:"数据列表"`
+ List []*sysin.TableListModel `json:"list" dc:"数据列表"`
}
// ExportReq 导出列表
type ExportReq struct {
- g.Meta `path:"/table/export" method:"get" tags:"表格" summary:"导出表格列表"`
+ g.Meta `path:"/table/export" method:"get" tags:"表格例子" summary:"导出表格列表"`
sysin.TableListInp
}
@@ -32,7 +32,7 @@ type ExportRes struct{}
// ViewReq 获取信息
type ViewReq struct {
- g.Meta `path:"/table/view" method:"get" tags:"表格" summary:"获取指定信息"`
+ g.Meta `path:"/table/view" method:"get" tags:"表格例子" summary:"获取指定信息"`
sysin.TableViewInp
}
@@ -42,7 +42,7 @@ type ViewRes struct {
// EditReq 修改/新增
type EditReq struct {
- g.Meta `path:"/table/edit" method:"post" tags:"表格" summary:"修改/新增表格"`
+ g.Meta `path:"/table/edit" method:"post" tags:"表格例子" summary:"修改/新增表格"`
sysin.TableEditInp
}
@@ -50,7 +50,7 @@ type EditRes struct{}
// DeleteReq 删除
type DeleteReq struct {
- g.Meta `path:"/table/delete" method:"post" tags:"表格" summary:"删除表格"`
+ g.Meta `path:"/table/delete" method:"post" tags:"表格例子" summary:"删除表格"`
sysin.TableDeleteInp
}
@@ -58,7 +58,7 @@ type DeleteRes struct{}
// MaxSortReq 最大排序
type MaxSortReq struct {
- g.Meta `path:"/table/maxSort" method:"get" tags:"表格" summary:"表格最大排序"`
+ g.Meta `path:"/table/maxSort" method:"get" tags:"表格例子" summary:"表格最大排序"`
sysin.TableMaxSortInp
}
@@ -68,7 +68,7 @@ type MaxSortRes struct {
// StatusReq 更新状态
type StatusReq struct {
- g.Meta `path:"/table/status" method:"post" tags:"表格" summary:"更新表格状态"`
+ g.Meta `path:"/table/status" method:"post" tags:"表格例子" summary:"更新表格状态"`
sysin.TableStatusInp
}
@@ -76,7 +76,7 @@ type StatusRes struct{}
// SwitchReq 更新开关状态
type SwitchReq struct {
- g.Meta `path:"/table/switch" method:"post" tags:"表格" summary:"更新表格状态"`
+ g.Meta `path:"/table/switch" method:"post" tags:"表格例子" summary:"更新表格状态"`
sysin.TableSwitchInp
}
diff --git a/server/addons/hgexample/api/admin/tenantorder/tenantorder.go b/server/addons/hgexample/api/admin/tenantorder/tenantorder.go
new file mode 100644
index 0000000..edb8c2e
--- /dev/null
+++ b/server/addons/hgexample/api/admin/tenantorder/tenantorder.go
@@ -0,0 +1,59 @@
+// Package tenantorder
+// @Link https://github.com/bufanyun/hotgo
+// @Copyright Copyright (c) 2024 HotGo CLI
+// @Author Ms <133814250@qq.com>
+// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
+// @AutoGenerate Version 2.13.1
+package tenantorder
+
+import (
+ "hotgo/addons/hgexample/model/input/sysin"
+ "hotgo/internal/model/input/form"
+
+ "github.com/gogf/gf/v2/frame/g"
+)
+
+// ListReq 查询多租户功能演示列表
+type ListReq struct {
+ g.Meta `path:"/tenantOrder/list" method:"get" tags:"多租户功能演示" summary:"获取多租户功能演示列表"`
+ sysin.TenantOrderListInp
+}
+
+type ListRes struct {
+ form.PageRes
+ List []*sysin.TenantOrderListModel `json:"list" dc:"数据列表"`
+}
+
+// ExportReq 导出多租户功能演示列表
+type ExportReq struct {
+ g.Meta `path:"/tenantOrder/export" method:"get" tags:"多租户功能演示" summary:"导出多租户功能演示列表"`
+ sysin.TenantOrderListInp
+}
+
+type ExportRes struct{}
+
+// ViewReq 获取多租户功能演示指定信息
+type ViewReq struct {
+ g.Meta `path:"/tenantOrder/view" method:"get" tags:"多租户功能演示" summary:"获取多租户功能演示指定信息"`
+ sysin.TenantOrderViewInp
+}
+
+type ViewRes struct {
+ *sysin.TenantOrderViewModel
+}
+
+// EditReq 修改/新增多租户功能演示
+type EditReq struct {
+ g.Meta `path:"/tenantOrder/edit" method:"post" tags:"多租户功能演示" summary:"修改/新增多租户功能演示"`
+ sysin.TenantOrderEditInp
+}
+
+type EditRes struct{}
+
+// DeleteReq 删除多租户功能演示
+type DeleteReq struct {
+ g.Meta `path:"/tenantOrder/delete" method:"post" tags:"多租户功能演示" summary:"删除多租户功能演示"`
+ sysin.TenantOrderDeleteInp
+}
+
+type DeleteRes struct{}
\ No newline at end of file
diff --git a/server/addons/hgexample/api/admin/treetable/treetable.go b/server/addons/hgexample/api/admin/treetable/treetable.go
index 93ebc3d..d5ebb96 100644
--- a/server/addons/hgexample/api/admin/treetable/treetable.go
+++ b/server/addons/hgexample/api/admin/treetable/treetable.go
@@ -13,7 +13,7 @@ import (
// ListReq 查询列表
type ListReq struct {
- g.Meta `path:"/treeTable/list" method:"get" tags:"表格" summary:"获取表格列表"`
+ g.Meta `path:"/treeTable/list" method:"get" tags:"树形表格例子" summary:"获取表格列表"`
sysin.TreeTableListInp
}
@@ -24,7 +24,7 @@ type ListRes struct {
// ExportReq 导出列表
type ExportReq struct {
- g.Meta `path:"/treeTable/export" method:"get" tags:"表格" summary:"导出表格列表"`
+ g.Meta `path:"/treeTable/export" method:"get" tags:"树形表格例子" summary:"导出表格列表"`
sysin.TableListInp
}
@@ -32,7 +32,7 @@ type ExportRes struct{}
// ViewReq 获取信息
type ViewReq struct {
- g.Meta `path:"/treeTable/view" method:"get" tags:"表格" summary:"获取指定信息"`
+ g.Meta `path:"/treeTable/view" method:"get" tags:"树形表格例子" summary:"获取指定信息"`
sysin.TableViewInp
}
@@ -42,7 +42,7 @@ type ViewRes struct {
// EditReq 修改/新增
type EditReq struct {
- g.Meta `path:"/treeTable/edit" method:"post" tags:"表格" summary:"修改/新增表格"`
+ g.Meta `path:"/treeTable/edit" method:"post" tags:"树形表格例子" summary:"修改/新增表格"`
sysin.TableEditInp
}
@@ -50,7 +50,7 @@ type EditRes struct{}
// DeleteReq 删除
type DeleteReq struct {
- g.Meta `path:"/treeTable/delete" method:"post" tags:"表格" summary:"删除表格"`
+ g.Meta `path:"/treeTable/delete" method:"post" tags:"树形表格例子" summary:"删除表格"`
sysin.TableDeleteInp
}
@@ -58,7 +58,7 @@ type DeleteRes struct{}
// MaxSortReq 最大排序
type MaxSortReq struct {
- g.Meta `path:"/treeTable/maxSort" method:"get" tags:"表格" summary:"表格最大排序"`
+ g.Meta `path:"/treeTable/maxSort" method:"get" tags:"树形表格例子" summary:"表格最大排序"`
sysin.TableMaxSortInp
}
@@ -68,7 +68,7 @@ type MaxSortRes struct {
// StatusReq 更新状态
type StatusReq struct {
- g.Meta `path:"/treeTable/status" method:"post" tags:"表格" summary:"更新表格状态"`
+ g.Meta `path:"/treeTable/status" method:"post" tags:"树形表格例子" summary:"更新表格状态"`
sysin.TableStatusInp
}
@@ -76,7 +76,7 @@ type StatusRes struct{}
// SwitchReq 更新开关状态
type SwitchReq struct {
- g.Meta `path:"/treeTable/switch" method:"post" tags:"表格" summary:"更新表格状态"`
+ g.Meta `path:"/treeTable/switch" method:"post" tags:"树形表格例子" summary:"更新表格状态"`
sysin.TableSwitchInp
}
@@ -84,7 +84,7 @@ type SwitchRes struct{}
// SelectReq 树形选项
type SelectReq struct {
- g.Meta `path:"/treeTable/select" method:"get" tags:"表格" summary:"树形选项"`
+ g.Meta `path:"/treeTable/select" method:"get" tags:"树形表格例子" summary:"树形选项"`
}
type SelectRes struct {
diff --git a/server/addons/hgexample/api/home/index/index.go b/server/addons/hgexample/api/home/index/index.go
index 3a876ed..7a44e68 100644
--- a/server/addons/hgexample/api/home/index/index.go
+++ b/server/addons/hgexample/api/home/index/index.go
@@ -12,7 +12,7 @@ import (
// TestReq 测试
type TestReq struct {
- g.Meta `path:"/index/test" method:"get" summary:"功能案例" tags:"测试首页"`
+ g.Meta `path:"/index/test" method:"get" tags:"功能案例" summary:"测试首页"`
sysin.IndexTestInp
}
diff --git a/server/addons/hgexample/controller/admin/sys/comp.go b/server/addons/hgexample/controller/admin/sys/comp.go
index 9eb5978..ba57d0b 100644
--- a/server/addons/hgexample/controller/admin/sys/comp.go
+++ b/server/addons/hgexample/controller/admin/sys/comp.go
@@ -20,10 +20,10 @@ type cComp struct{}
// ImportExcel 导入Excel
func (c *cComp) ImportExcel(ctx context.Context, req *comp.ImportExcelReq) (res *comp.ImportExcelRes, err error) {
file, err := req.File.Open()
- defer file.Close()
if err != nil {
return
}
+ defer file.Close()
excel, err := excelize.OpenReader(file)
if err != nil {
diff --git a/server/addons/hgexample/controller/admin/sys/tenant_order.go b/server/addons/hgexample/controller/admin/sys/tenant_order.go
new file mode 100644
index 0000000..95977a3
--- /dev/null
+++ b/server/addons/hgexample/controller/admin/sys/tenant_order.go
@@ -0,0 +1,67 @@
+// Package sys
+// @Link https://github.com/bufanyun/hotgo
+// @Copyright Copyright (c) 2024 HotGo CLI
+// @Author Ms <133814250@qq.com>
+// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
+// @AutoGenerate Version 2.13.1
+package sys
+
+import (
+ "context"
+ "hotgo/addons/hgexample/api/admin/tenantorder"
+ "hotgo/addons/hgexample/model/input/sysin"
+ "hotgo/addons/hgexample/service"
+)
+
+var (
+ TenantOrder = cTenantOrder{}
+)
+
+type cTenantOrder struct{}
+
+// List 查看多租户功能演示列表
+func (c *cTenantOrder) List(ctx context.Context, req *tenantorder.ListReq) (res *tenantorder.ListRes, err error) {
+ list, totalCount, err := service.SysTenantOrder().List(ctx, &req.TenantOrderListInp)
+ if err != nil {
+ return
+ }
+
+ if list == nil {
+ list = []*sysin.TenantOrderListModel{}
+ }
+
+ res = new(tenantorder.ListRes)
+ res.List = list
+ res.PageRes.Pack(req, totalCount)
+ return
+}
+
+// Export 导出多租户功能演示列表
+func (c *cTenantOrder) Export(ctx context.Context, req *tenantorder.ExportReq) (res *tenantorder.ExportRes, err error) {
+ err = service.SysTenantOrder().Export(ctx, &req.TenantOrderListInp)
+ return
+}
+
+// Edit 更新多租户功能演示
+func (c *cTenantOrder) Edit(ctx context.Context, req *tenantorder.EditReq) (res *tenantorder.EditRes, err error) {
+ err = service.SysTenantOrder().Edit(ctx, &req.TenantOrderEditInp)
+ return
+}
+
+// View 获取指定多租户功能演示信息
+func (c *cTenantOrder) View(ctx context.Context, req *tenantorder.ViewReq) (res *tenantorder.ViewRes, err error) {
+ data, err := service.SysTenantOrder().View(ctx, &req.TenantOrderViewInp)
+ if err != nil {
+ return
+ }
+
+ res = new(tenantorder.ViewRes)
+ res.TenantOrderViewModel = data
+ return
+}
+
+// Delete 删除多租户功能演示
+func (c *cTenantOrder) Delete(ctx context.Context, req *tenantorder.DeleteReq) (res *tenantorder.DeleteRes, err error) {
+ err = service.SysTenantOrder().Delete(ctx, &req.TenantOrderDeleteInp)
+ return
+}
\ No newline at end of file
diff --git a/server/addons/hgexample/logic/sys/table.go b/server/addons/hgexample/logic/sys/table.go
index ebf8c4b..71b58c5 100644
--- a/server/addons/hgexample/logic/sys/table.go
+++ b/server/addons/hgexample/logic/sys/table.go
@@ -119,7 +119,7 @@ func (s *sSysTable) Export(ctx context.Context, in *sysin.TableListInp) (err err
}
var (
- fileName = "表格例子导出-" + gctx.CtxId(ctx) + ".xlsx"
+ fileName = "表格例子导出-" + gctx.CtxId(ctx)
sheetName = fmt.Sprintf("索引条件共%v行,共%v页,当前导出是第%v页,本页共%v行", totalCount, form.CalPageCount(totalCount, in.PerPage), in.Page, len(list))
exports []sysin.TableExportModel
)
diff --git a/server/addons/hgexample/logic/sys/tenant_order.go b/server/addons/hgexample/logic/sys/tenant_order.go
new file mode 100644
index 0000000..22e27ef
--- /dev/null
+++ b/server/addons/hgexample/logic/sys/tenant_order.go
@@ -0,0 +1,176 @@
+// Package sys
+// @Link https://github.com/bufanyun/hotgo
+// @Copyright Copyright (c) 2024 HotGo CLI
+// @Author Ms <133814250@qq.com>
+// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
+// @AutoGenerate Version 2.13.1
+package sys
+
+import (
+ "context"
+ "fmt"
+ "hotgo/addons/hgexample/model/input/sysin"
+ "hotgo/addons/hgexample/service"
+ "hotgo/internal/dao"
+ "hotgo/internal/library/hgorm/handler"
+ "hotgo/internal/library/hgorm/hook"
+ "hotgo/internal/model/input/form"
+ "hotgo/utility/convert"
+ "hotgo/utility/excel"
+
+ "github.com/gogf/gf/v2/database/gdb"
+ "github.com/gogf/gf/v2/errors/gerror"
+ "github.com/gogf/gf/v2/frame/g"
+ "github.com/gogf/gf/v2/os/gctx"
+ "github.com/gogf/gf/v2/util/gconv"
+)
+
+type sSysTenantOrder struct{}
+
+func NewSysTenantOrder() *sSysTenantOrder {
+ return &sSysTenantOrder{}
+}
+
+func init() {
+ service.RegisterSysTenantOrder(NewSysTenantOrder())
+}
+
+// Model 多租户功能演示ORM模型
+func (s *sSysTenantOrder) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
+ if len(option) == 0 {
+ // 过滤多租户数据权限
+ option = append(option, &handler.Option{
+ FilterTenant: true,
+ //FilterAuth: true, // 如果还需要维护created_by、member_id等部门数据权限范围可放开注释
+ })
+ }
+ return handler.Model(dao.AddonHgexampleTenantOrder.Ctx(ctx), option...)
+}
+
+// List 获取多租户功能演示列表
+func (s *sSysTenantOrder) List(ctx context.Context, in *sysin.TenantOrderListInp) (list []*sysin.TenantOrderListModel, totalCount int, err error) {
+ mod := s.Model(ctx)
+
+ // 字段过滤
+ mod = mod.Fields(sysin.TenantOrderListModel{})
+
+ // 查询主键
+ if in.Id > 0 {
+ mod = mod.Where(dao.AddonHgexampleTenantOrder.Columns().Id, in.Id)
+ }
+
+ // 查询租户ID
+ if in.TenantId > 0 {
+ mod = mod.Where(dao.AddonHgexampleTenantOrder.Columns().TenantId, in.TenantId)
+ }
+
+ // 查询商户ID
+ if in.MerchantId > 0 {
+ mod = mod.Where(dao.AddonHgexampleTenantOrder.Columns().MerchantId, in.MerchantId)
+ }
+
+ // 查询用户ID
+ if in.UserId > 0 {
+ mod = mod.Where(dao.AddonHgexampleTenantOrder.Columns().UserId, in.UserId)
+ }
+
+ // 查询关联订单号
+ if in.OrderSn != "" {
+ mod = mod.Where(dao.AddonHgexampleTenantOrder.Columns().OrderSn, in.OrderSn)
+ }
+
+ // 查询支付状态
+ if in.Status > 0 {
+ mod = mod.Where(dao.AddonHgexampleTenantOrder.Columns().Status, in.Status)
+ }
+
+ // 查询创建时间
+ if len(in.CreatedAt) == 2 {
+ mod = mod.WhereBetween(dao.AddonHgexampleTenantOrder.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1])
+ }
+
+ // 分页
+ mod = mod.Page(in.Page, in.PerPage)
+
+ // 排序
+ mod = mod.OrderDesc(dao.AddonHgexampleTenantOrder.Columns().Id)
+
+ // 查询数据
+ if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
+ err = gerror.Wrap(err, "获取多租户功能演示列表失败,请稍后重试!")
+ return
+ }
+ return
+}
+
+// Export 导出多租户功能演示
+func (s *sSysTenantOrder) Export(ctx context.Context, in *sysin.TenantOrderListInp) (err error) {
+ list, totalCount, err := s.List(ctx, in)
+ if err != nil {
+ return
+ }
+
+ // 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...}
+ tags, err := convert.GetEntityDescTags(sysin.TenantOrderExportModel{})
+ if err != nil {
+ return
+ }
+
+ var (
+ fileName = "导出多租户功能演示-" + gctx.CtxId(ctx)
+ sheetName = fmt.Sprintf("索引条件共%v行,共%v页,当前导出是第%v页,本页共%v行", totalCount, form.CalPageCount(totalCount, in.PerPage), in.Page, len(list))
+ exports []sysin.TenantOrderExportModel
+ )
+
+ if err = gconv.Scan(list, &exports); err != nil {
+ return
+ }
+
+ err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName)
+ return
+}
+
+// Edit 修改/新增多租户功能演示
+func (s *sSysTenantOrder) Edit(ctx context.Context, in *sysin.TenantOrderEditInp) (err error) {
+ return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
+
+ // 修改
+ if in.Id > 0 {
+ if _, err = s.Model(ctx).
+ Fields(sysin.TenantOrderUpdateFields{}).
+ WherePri(in.Id).Data(in).
+ Hook(hook.SaveTenant). // 自动维护租户关系更新
+ Update(); err != nil {
+ }
+ return
+ }
+
+ // 新增
+ if _, err = dao.AddonHgexampleTenantOrder.Ctx(ctx).
+ Fields(sysin.TenantOrderInsertFields{}).
+ Hook(hook.SaveTenant). // 自动维护租户关系更新
+ Data(in).
+ Insert(); err != nil {
+ }
+ return
+ })
+}
+
+// Delete 删除多租户功能演示
+func (s *sSysTenantOrder) Delete(ctx context.Context, in *sysin.TenantOrderDeleteInp) (err error) {
+
+ if _, err = s.Model(ctx).WherePri(in.Id).Delete(); err != nil {
+ err = gerror.Wrap(err, "删除多租户功能演示失败,请稍后重试!")
+ return
+ }
+ return
+}
+
+// View 获取多租户功能演示指定信息
+func (s *sSysTenantOrder) View(ctx context.Context, in *sysin.TenantOrderViewInp) (res *sysin.TenantOrderViewModel, err error) {
+ if err = s.Model(ctx).WherePri(in.Id).Scan(&res); err != nil {
+ err = gerror.Wrap(err, "获取多租户功能演示信息,请稍后重试!")
+ return
+ }
+ return
+}
diff --git a/server/addons/hgexample/model/input/sysin/tenant_order.go b/server/addons/hgexample/model/input/sysin/tenant_order.go
new file mode 100644
index 0000000..61b44a6
--- /dev/null
+++ b/server/addons/hgexample/model/input/sysin/tenant_order.go
@@ -0,0 +1,123 @@
+// Package sysin
+// @Link https://github.com/bufanyun/hotgo
+// @Copyright Copyright (c) 2024 HotGo CLI
+// @Author Ms <133814250@qq.com>
+// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
+// @AutoGenerate Version 2.13.1
+package sysin
+
+import (
+ "context"
+ "hotgo/internal/model/entity"
+ "hotgo/internal/model/input/form"
+
+ "github.com/gogf/gf/v2/frame/g"
+ "github.com/gogf/gf/v2/os/gtime"
+)
+
+// TenantOrderUpdateFields 修改多租户功能演示字段过滤
+type TenantOrderUpdateFields struct {
+ TenantId int64 `json:"tenantId" dc:"租户ID"`
+ MerchantId int64 `json:"merchantId" dc:"商户ID"`
+ UserId int64 `json:"userId" dc:"用户ID"`
+ ProductName string `json:"productName" dc:"购买产品"`
+ OrderSn string `json:"orderSn" dc:"关联订单号"`
+ Money float64 `json:"money" dc:"充值金额"`
+ Remark string `json:"remark" dc:"备注"`
+ Status int `json:"status" dc:"支付状态"`
+}
+
+// TenantOrderInsertFields 新增多租户功能演示字段过滤
+type TenantOrderInsertFields struct {
+ TenantId int64 `json:"tenantId" dc:"租户ID"`
+ MerchantId int64 `json:"merchantId" dc:"商户ID"`
+ UserId int64 `json:"userId" dc:"用户ID"`
+ ProductName string `json:"productName" dc:"购买产品"`
+ OrderSn string `json:"orderSn" dc:"关联订单号"`
+ Money float64 `json:"money" dc:"充值金额"`
+ Remark string `json:"remark" dc:"备注"`
+ Status int `json:"status" dc:"支付状态"`
+}
+
+// TenantOrderEditInp 修改/新增多租户功能演示
+type TenantOrderEditInp struct {
+ entity.AddonHgexampleTenantOrder
+}
+
+func (in *TenantOrderEditInp) Filter(ctx context.Context) (err error) {
+ // 验证充值金额
+ if err := g.Validator().Rules("required").Data(in.Money).Messages("充值金额不能为空").Run(ctx); err != nil {
+ return err.Current()
+ }
+
+ return
+}
+
+type TenantOrderEditModel struct{}
+
+// TenantOrderDeleteInp 删除多租户功能演示
+type TenantOrderDeleteInp struct {
+ Id interface{} `json:"id" v:"required#主键不能为空" dc:"主键"`
+}
+
+func (in *TenantOrderDeleteInp) Filter(ctx context.Context) (err error) {
+ return
+}
+
+type TenantOrderDeleteModel struct{}
+
+// TenantOrderViewInp 获取指定多租户功能演示信息
+type TenantOrderViewInp struct {
+ Id int64 `json:"id" v:"required#主键不能为空" dc:"主键"`
+}
+
+func (in *TenantOrderViewInp) Filter(ctx context.Context) (err error) {
+ return
+}
+
+type TenantOrderViewModel struct {
+ entity.AddonHgexampleTenantOrder
+}
+
+// TenantOrderListInp 获取多租户功能演示列表
+type TenantOrderListInp struct {
+ form.PageReq
+ Id int64 `json:"id" dc:"主键"`
+ TenantId int64 `json:"tenantId" dc:"租户ID"`
+ MerchantId int64 `json:"merchantId" dc:"商户ID"`
+ UserId int64 `json:"userId" dc:"用户ID"`
+ OrderSn string `json:"orderSn" dc:"关联订单号"`
+ Status int `json:"status" dc:"支付状态"`
+ CreatedAt []*gtime.Time `json:"createdAt" dc:"创建时间"`
+}
+
+func (in *TenantOrderListInp) Filter(ctx context.Context) (err error) {
+ return
+}
+
+type TenantOrderListModel struct {
+ Id int64 `json:"id" dc:"主键"`
+ TenantId int64 `json:"tenantId" dc:"租户ID"`
+ MerchantId int64 `json:"merchantId" dc:"商户ID"`
+ UserId int64 `json:"userId" dc:"用户ID"`
+ ProductName string `json:"productName" dc:"购买产品"`
+ OrderSn string `json:"orderSn" dc:"关联订单号"`
+ Money float64 `json:"money" dc:"充值金额"`
+ Remark string `json:"remark" dc:"备注"`
+ Status int `json:"status" dc:"支付状态"`
+ CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
+}
+
+// TenantOrderExportModel 导出多租户功能演示
+type TenantOrderExportModel struct {
+ Id int64 `json:"id" dc:"主键"`
+ TenantId int64 `json:"tenantId" dc:"租户ID"`
+ MerchantId int64 `json:"merchantId" dc:"商户ID"`
+ UserId int64 `json:"userId" dc:"用户ID"`
+ ProductName string `json:"productName" dc:"购买产品"`
+ OrderSn string `json:"orderSn" dc:"关联订单号"`
+ Money float64 `json:"money" dc:"充值金额"`
+ Remark string `json:"remark" dc:"备注"`
+ Status int `json:"status" dc:"支付状态"`
+ CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
+}
diff --git a/server/addons/hgexample/router/genrouter/tenant_order.go b/server/addons/hgexample/router/genrouter/tenant_order.go
new file mode 100644
index 0000000..15aa5b0
--- /dev/null
+++ b/server/addons/hgexample/router/genrouter/tenant_order.go
@@ -0,0 +1,13 @@
+// Package genrouter
+// @Link https://github.com/bufanyun/hotgo
+// @Copyright Copyright (c) 2024 HotGo CLI
+// @Author Ms <133814250@qq.com>
+// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
+// @AutoGenerate Version 2.13.1
+package genrouter
+
+import "hotgo/addons/hgexample/controller/admin/sys"
+
+func init() {
+ LoginRequiredRouter = append(LoginRequiredRouter, sys.TenantOrder) // 多租户功能演示
+}
\ No newline at end of file
diff --git a/server/addons/hgexample/service/sys.go b/server/addons/hgexample/service/sys.go
index b0eb4c7..9ad290d 100644
--- a/server/addons/hgexample/service/sys.go
+++ b/server/addons/hgexample/service/sys.go
@@ -47,6 +47,20 @@ type (
// View 获取指定信息
View(ctx context.Context, in *sysin.TableViewInp) (res *sysin.TableViewModel, err error)
}
+ ISysTenantOrder interface {
+ // Model 多租户功能演示ORM模型
+ Model(ctx context.Context, option ...*handler.Option) *gdb.Model
+ // List 获取多租户功能演示列表
+ List(ctx context.Context, in *sysin.TenantOrderListInp) (list []*sysin.TenantOrderListModel, totalCount int, err error)
+ // Export 导出多租户功能演示
+ Export(ctx context.Context, in *sysin.TenantOrderListInp) (err error)
+ // Edit 修改/新增多租户功能演示
+ Edit(ctx context.Context, in *sysin.TenantOrderEditInp) (err error)
+ // Delete 删除多租户功能演示
+ Delete(ctx context.Context, in *sysin.TenantOrderDeleteInp) (err error)
+ // View 获取多租户功能演示指定信息
+ View(ctx context.Context, in *sysin.TenantOrderViewInp) (res *sysin.TenantOrderViewModel, err error)
+ }
ISysTreeTable interface {
// Model Orm模型
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
@@ -62,10 +76,11 @@ type (
)
var (
- localSysConfig ISysConfig
- localSysIndex ISysIndex
- localSysTable ISysTable
- localSysTreeTable ISysTreeTable
+ localSysConfig ISysConfig
+ localSysIndex ISysIndex
+ localSysTable ISysTable
+ localSysTenantOrder ISysTenantOrder
+ localSysTreeTable ISysTreeTable
)
func SysConfig() ISysConfig {
@@ -101,6 +116,17 @@ func RegisterSysTable(i ISysTable) {
localSysTable = i
}
+func SysTenantOrder() ISysTenantOrder {
+ if localSysTenantOrder == nil {
+ panic("implement not found for interface ISysTenantOrder, forgot register?")
+ }
+ return localSysTenantOrder
+}
+
+func RegisterSysTenantOrder(i ISysTenantOrder) {
+ localSysTenantOrder = i
+}
+
func SysTreeTable() ISysTreeTable {
if localSysTreeTable == nil {
panic("implement not found for interface ISysTreeTable, forgot register?")
diff --git a/server/api/admin/common/upload.go b/server/api/admin/common/upload.go
index 8771f1a..85f48a5 100644
--- a/server/api/admin/common/upload.go
+++ b/server/api/admin/common/upload.go
@@ -12,14 +12,14 @@ import (
// UploadFileReq 上传文件
type UploadFileReq struct {
- g.Meta `path:"/upload/file" tags:"上传" method:"post" summary:"上传附件"`
+ g.Meta `path:"/upload/file" tags:"附件" method:"post" summary:"上传附件"`
}
type UploadFileRes *sysin.AttachmentListModel
// CheckMultipartReq 检查文件分片
type CheckMultipartReq struct {
- g.Meta `path:"/upload/checkMultipart" tags:"上传" method:"post" summary:"检查文件分片"`
+ g.Meta `path:"/upload/checkMultipart" tags:"附件" method:"post" summary:"检查文件分片"`
sysin.CheckMultipartInp
}
@@ -29,7 +29,7 @@ type CheckMultipartRes struct {
// UploadPartReq 分片上传
type UploadPartReq struct {
- g.Meta `path:"/upload/uploadPart" tags:"上传" method:"post" summary:"分片上传"`
+ g.Meta `path:"/upload/uploadPart" tags:"附件" method:"post" summary:"分片上传"`
sysin.UploadPartInp
}
diff --git a/server/api/admin/curddemo/curddemo.go b/server/api/admin/curddemo/curddemo.go
index 6c0f1dc..5d7423b 100644
--- a/server/api/admin/curddemo/curddemo.go
+++ b/server/api/admin/curddemo/curddemo.go
@@ -3,7 +3,7 @@
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
-// @AutoGenerate Version 2.12.10
+// @AutoGenerate Version 2.13.1
package curddemo
import (
@@ -13,9 +13,9 @@ import (
"github.com/gogf/gf/v2/frame/g"
)
-// ListReq 查询生成演示列表
+// ListReq 查询CURD列表列表
type ListReq struct {
- g.Meta `path:"/curdDemo/list" method:"get" tags:"生成演示" summary:"获取生成演示列表"`
+ g.Meta `path:"/curdDemo/list" method:"get" tags:"CURD列表" summary:"获取CURD列表列表"`
sysin.CurdDemoListInp
}
@@ -24,17 +24,17 @@ type ListRes struct {
List []*sysin.CurdDemoListModel `json:"list" dc:"数据列表"`
}
-// ExportReq 导出生成演示列表
+// ExportReq 导出CURD列表列表
type ExportReq struct {
- g.Meta `path:"/curdDemo/export" method:"get" tags:"生成演示" summary:"导出生成演示列表"`
+ g.Meta `path:"/curdDemo/export" method:"get" tags:"CURD列表" summary:"导出CURD列表列表"`
sysin.CurdDemoListInp
}
type ExportRes struct{}
-// ViewReq 获取生成演示指定信息
+// ViewReq 获取CURD列表指定信息
type ViewReq struct {
- g.Meta `path:"/curdDemo/view" method:"get" tags:"生成演示" summary:"获取生成演示指定信息"`
+ g.Meta `path:"/curdDemo/view" method:"get" tags:"CURD列表" summary:"获取CURD列表指定信息"`
sysin.CurdDemoViewInp
}
@@ -42,25 +42,25 @@ type ViewRes struct {
*sysin.CurdDemoViewModel
}
-// EditReq 修改/新增生成演示
+// EditReq 修改/新增CURD列表
type EditReq struct {
- g.Meta `path:"/curdDemo/edit" method:"post" tags:"生成演示" summary:"修改/新增生成演示"`
+ g.Meta `path:"/curdDemo/edit" method:"post" tags:"CURD列表" summary:"修改/新增CURD列表"`
sysin.CurdDemoEditInp
}
type EditRes struct{}
-// DeleteReq 删除生成演示
+// DeleteReq 删除CURD列表
type DeleteReq struct {
- g.Meta `path:"/curdDemo/delete" method:"post" tags:"生成演示" summary:"删除生成演示"`
+ g.Meta `path:"/curdDemo/delete" method:"post" tags:"CURD列表" summary:"删除CURD列表"`
sysin.CurdDemoDeleteInp
}
type DeleteRes struct{}
-// MaxSortReq 获取生成演示最大排序
+// MaxSortReq 获取CURD列表最大排序
type MaxSortReq struct {
- g.Meta `path:"/curdDemo/maxSort" method:"get" tags:"生成演示" summary:"获取生成演示最大排序"`
+ g.Meta `path:"/curdDemo/maxSort" method:"get" tags:"CURD列表" summary:"获取CURD列表最大排序"`
sysin.CurdDemoMaxSortInp
}
@@ -68,17 +68,9 @@ type MaxSortRes struct {
*sysin.CurdDemoMaxSortModel
}
-// StatusReq 更新生成演示状态
-type StatusReq struct {
- g.Meta `path:"/curdDemo/status" method:"post" tags:"生成演示" summary:"更新生成演示状态"`
- sysin.CurdDemoStatusInp
-}
-
-type StatusRes struct{}
-
-// SwitchReq 更新生成演示开关状态
+// SwitchReq 更新CURD列表开关状态
type SwitchReq struct {
- g.Meta `path:"/curdDemo/switch" method:"post" tags:"生成演示" summary:"更新生成演示状态"`
+ g.Meta `path:"/curdDemo/switch" method:"post" tags:"CURD列表" summary:"更新CURD列表状态"`
sysin.CurdDemoSwitchInp
}
diff --git a/server/api/admin/dept/dept.go b/server/api/admin/dept/dept.go
index 2467f0b..565ec74 100644
--- a/server/api/admin/dept/dept.go
+++ b/server/api/admin/dept/dept.go
@@ -9,6 +9,7 @@ import (
"github.com/gogf/gf/v2/frame/g"
"hotgo/internal/model/input/adminin"
"hotgo/internal/model/input/form"
+ "hotgo/utility/tree"
)
// ListReq 查询列表
@@ -55,17 +56,9 @@ type MaxSortRes struct {
*adminin.DeptMaxSortModel
}
-// StatusReq 更新部门状态
-type StatusReq struct {
- g.Meta `path:"/dept/status" method:"post" tags:"部门" summary:"更新部门状态"`
- adminin.DeptStatusInp
-}
-
-type StatusRes struct{}
-
-// OptionReq 获取部门选项树
+// OptionReq 获取当前登录用户可选的部门选项
type OptionReq struct {
- g.Meta `path:"/dept/option" method:"get" tags:"部门" summary:"获取部门选项树"`
+ g.Meta `path:"/dept/option" method:"get" tags:"部门" summary:"获取当前登录用户可选的部门选项"`
adminin.DeptOptionInp
}
@@ -73,3 +66,10 @@ type OptionRes struct {
*adminin.DeptOptionModel
form.PageRes
}
+
+// TreeOptionReq 获取部门关系树选项
+type TreeOptionReq struct {
+ g.Meta `path:"/dept/treeOption" method:"get" tags:"部门" summary:"获取部门关系树选项"`
+}
+
+type TreeOptionRes []tree.Node
diff --git a/server/api/admin/dict/dict_data.go b/server/api/admin/dict/dict_data.go
index 3c7e4d9..8ecdea7 100644
--- a/server/api/admin/dict/dict_data.go
+++ b/server/api/admin/dict/dict_data.go
@@ -39,14 +39,14 @@ type DataListRes struct {
}
type DataSelectReq struct {
- g.Meta `path:"/dictData/option/{Type}" method:"get" summary:"字典数据" tags:"获取指定字典选项"`
+ g.Meta `path:"/dictData/option/{Type}" method:"get" tags:"字典数据" summary:"获取指定字典选项"`
sysin.DataSelectInp
}
type DataSelectRes sysin.DataSelectModel
type DataSelectsReq struct {
- g.Meta `path:"/dictData/options" method:"get" summary:"字典数据" tags:"获取多个字典选项"`
+ g.Meta `path:"/dictData/options" method:"get" tags:"字典数据" summary:"获取多个字典选项"`
Types []string `json:"types"`
}
diff --git a/server/api/admin/log/log.go b/server/api/admin/log/log.go
index d2c8ad3..981751d 100644
--- a/server/api/admin/log/log.go
+++ b/server/api/admin/log/log.go
@@ -13,14 +13,14 @@ import (
// ClearReq 清空日志
type ClearReq struct {
- g.Meta `path:"/log/clear" method:"post" tags:"日志" summary:"清空日志"`
+ g.Meta `path:"/log/clear" method:"post" tags:"访问日志" summary:"清空日志"`
}
type ClearRes struct{}
// ExportReq 导出
type ExportReq struct {
- g.Meta `path:"/log/export" method:"get" tags:"日志" summary:"导出日志"`
+ g.Meta `path:"/log/export" method:"get" tags:"访问日志" summary:"导出日志"`
sysin.LogListInp
}
@@ -28,18 +28,18 @@ type ExportRes struct{}
// ListReq 获取菜单列表
type ListReq struct {
- g.Meta `path:"/log/list" method:"get" tags:"日志" summary:"获取日志列表"`
+ g.Meta `path:"/log/list" method:"get" tags:"访问日志" summary:"获取日志列表"`
sysin.LogListInp
}
type ListRes struct {
- List []*sysin.LogListModel `json:"list" dc:"数据列表"`
+ List []*sysin.LogListModel `json:"list" dc:"数据列表"`
form.PageRes
}
// DeleteReq 删除
type DeleteReq struct {
- g.Meta `path:"/log/delete" method:"post" tags:"日志" summary:"删除日志"`
+ g.Meta `path:"/log/delete" method:"post" tags:"访问日志" summary:"删除日志"`
sysin.LogDeleteInp
}
@@ -47,7 +47,7 @@ type DeleteRes struct{}
// ViewReq 获取指定信息
type ViewReq struct {
- g.Meta `path:"/log/view" method:"get" tags:"日志" summary:"获取指定信息"`
+ g.Meta `path:"/log/view" method:"get" tags:"访问日志" summary:"获取指定信息"`
sysin.LogViewInp
}
diff --git a/server/api/admin/normaltreedemo/normaltreedemo.go b/server/api/admin/normaltreedemo/normaltreedemo.go
new file mode 100644
index 0000000..37dc525
--- /dev/null
+++ b/server/api/admin/normaltreedemo/normaltreedemo.go
@@ -0,0 +1,69 @@
+// Package normaltreedemo
+// @Link https://github.com/bufanyun/hotgo
+// @Copyright Copyright (c) 2024 HotGo CLI
+// @Author Ms <133814250@qq.com>
+// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
+// @AutoGenerate Version 2.13.1
+package normaltreedemo
+
+import (
+ "hotgo/internal/model/input/form"
+ "hotgo/internal/model/input/sysin"
+ "hotgo/utility/tree"
+
+ "github.com/gogf/gf/v2/frame/g"
+)
+
+// ListReq 查询普通树表列表
+type ListReq struct {
+ g.Meta `path:"/normalTreeDemo/list" method:"get" tags:"普通树表" summary:"获取普通树表列表"`
+ sysin.NormalTreeDemoListInp
+}
+
+type ListRes struct {
+ form.PageRes
+ List []*sysin.NormalTreeDemoListModel `json:"list" dc:"数据列表"`
+}
+
+// ViewReq 获取普通树表指定信息
+type ViewReq struct {
+ g.Meta `path:"/normalTreeDemo/view" method:"get" tags:"普通树表" summary:"获取普通树表指定信息"`
+ sysin.NormalTreeDemoViewInp
+}
+
+type ViewRes struct {
+ *sysin.NormalTreeDemoViewModel
+}
+
+// EditReq 修改/新增普通树表
+type EditReq struct {
+ g.Meta `path:"/normalTreeDemo/edit" method:"post" tags:"普通树表" summary:"修改/新增普通树表"`
+ sysin.NormalTreeDemoEditInp
+}
+
+type EditRes struct{}
+
+// DeleteReq 删除普通树表
+type DeleteReq struct {
+ g.Meta `path:"/normalTreeDemo/delete" method:"post" tags:"普通树表" summary:"删除普通树表"`
+ sysin.NormalTreeDemoDeleteInp
+}
+
+type DeleteRes struct{}
+
+// MaxSortReq 获取普通树表最大排序
+type MaxSortReq struct {
+ g.Meta `path:"/normalTreeDemo/maxSort" method:"get" tags:"普通树表" summary:"获取普通树表最大排序"`
+ sysin.NormalTreeDemoMaxSortInp
+}
+
+type MaxSortRes struct {
+ *sysin.NormalTreeDemoMaxSortModel
+}
+
+// TreeOptionReq 获取普通树表关系树选项
+type TreeOptionReq struct {
+ g.Meta `path:"/normalTreeDemo/treeOption" method:"get" tags:"普通树表" summary:"获取普通树表关系树选项"`
+}
+
+type TreeOptionRes []tree.Node
\ No newline at end of file
diff --git a/server/api/admin/optiontreedemo/optiontreedemo.go b/server/api/admin/optiontreedemo/optiontreedemo.go
new file mode 100644
index 0000000..0caf3cb
--- /dev/null
+++ b/server/api/admin/optiontreedemo/optiontreedemo.go
@@ -0,0 +1,69 @@
+// Package optiontreedemo
+// @Link https://github.com/bufanyun/hotgo
+// @Copyright Copyright (c) 2024 HotGo CLI
+// @Author Ms <133814250@qq.com>
+// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
+// @AutoGenerate Version 2.13.1
+package optiontreedemo
+
+import (
+ "hotgo/internal/model/input/form"
+ "hotgo/internal/model/input/sysin"
+ "hotgo/utility/tree"
+
+ "github.com/gogf/gf/v2/frame/g"
+)
+
+// ListReq 查询选项树表列表
+type ListReq struct {
+ g.Meta `path:"/optionTreeDemo/list" method:"get" tags:"选项树表" summary:"获取选项树表列表"`
+ sysin.OptionTreeDemoListInp
+}
+
+type ListRes struct {
+ form.PageRes
+ List []*sysin.OptionTreeDemoListModel `json:"list" dc:"数据列表"`
+}
+
+// ViewReq 获取选项树表指定信息
+type ViewReq struct {
+ g.Meta `path:"/optionTreeDemo/view" method:"get" tags:"选项树表" summary:"获取选项树表指定信息"`
+ sysin.OptionTreeDemoViewInp
+}
+
+type ViewRes struct {
+ *sysin.OptionTreeDemoViewModel
+}
+
+// EditReq 修改/新增选项树表
+type EditReq struct {
+ g.Meta `path:"/optionTreeDemo/edit" method:"post" tags:"选项树表" summary:"修改/新增选项树表"`
+ sysin.OptionTreeDemoEditInp
+}
+
+type EditRes struct{}
+
+// DeleteReq 删除选项树表
+type DeleteReq struct {
+ g.Meta `path:"/optionTreeDemo/delete" method:"post" tags:"选项树表" summary:"删除选项树表"`
+ sysin.OptionTreeDemoDeleteInp
+}
+
+type DeleteRes struct{}
+
+// MaxSortReq 获取选项树表最大排序
+type MaxSortReq struct {
+ g.Meta `path:"/optionTreeDemo/maxSort" method:"get" tags:"选项树表" summary:"获取选项树表最大排序"`
+ sysin.OptionTreeDemoMaxSortInp
+}
+
+type MaxSortRes struct {
+ *sysin.OptionTreeDemoMaxSortModel
+}
+
+// TreeOptionReq 获取选项树表关系树选项
+type TreeOptionReq struct {
+ g.Meta `path:"/optionTreeDemo/treeOption" method:"get" tags:"选项树表" summary:"获取选项树表关系树选项"`
+}
+
+type TreeOptionRes []tree.Node
\ No newline at end of file
diff --git a/server/api/admin/provinces/provinces.go b/server/api/admin/provinces/provinces.go
index 1c23fb1..d6bc9fd 100644
--- a/server/api/admin/provinces/provinces.go
+++ b/server/api/admin/provinces/provinces.go
@@ -98,7 +98,7 @@ type UniqueIdRes struct {
// SelectReq 省市区选项
type SelectReq struct {
- g.Meta `path:"/provinces/select" method:"get" summary:"省市区" tags:"省市区选项"`
+ g.Meta `path:"/provinces/select" method:"get" tags:"省市区" summary:"省市区选项"`
sysin.ProvincesSelectInp
}
@@ -108,7 +108,7 @@ type SelectRes struct {
// CityLabelReq 获取指定城市标签
type CityLabelReq struct {
- g.Meta `path:"/provinces/cityLabel" method:"get" summary:"省市区" tags:"获取指定城市标签"`
+ g.Meta `path:"/provinces/cityLabel" method:"get" tags:"省市区" summary:"获取指定城市标签" `
sysin.ProvincesCityLabelInp
}
diff --git a/server/api/admin/role/role.go b/server/api/admin/role/role.go
index 08719ac..bbae5a1 100644
--- a/server/api/admin/role/role.go
+++ b/server/api/admin/role/role.go
@@ -25,7 +25,7 @@ type ListRes struct {
// DynamicReq 动态路由
type DynamicReq struct {
- g.Meta `path:"/role/dynamic" method:"get" tags:"路由" summary:"获取动态路由" description:"获取登录用户动态路由"`
+ g.Meta `path:"/role/dynamic" method:"get" tags:"角色" summary:"获取动态路由" description:"获取登录用户动态路由"`
}
type DynamicRes struct {
@@ -66,7 +66,7 @@ type DeleteRes struct{}
// DataScopeSelectReq 获取数据权限选项
type DataScopeSelectReq struct {
- g.Meta `path:"/role/dataScope/select" method:"get" summary:"角色" tags:"获取数据权限选项"`
+ g.Meta `path:"/role/dataScope/select" method:"get" tags:"角色" summary:"获取数据权限选项"`
}
type DataScopeSelectRes struct {
diff --git a/server/api/admin/smslog/smslog.go b/server/api/admin/smslog/smslog.go
index 004b31d..ed97c2d 100644
--- a/server/api/admin/smslog/smslog.go
+++ b/server/api/admin/smslog/smslog.go
@@ -13,7 +13,7 @@ import (
// ListReq 查询列表
type ListReq struct {
- g.Meta `path:"/smsLog/list" method:"get" tags:"短信记录" summary:"获取短信记录列表"`
+ g.Meta `path:"/smsLog/list" method:"get" tags:"短信" summary:"获取短信记录列表"`
sysin.SmsLogListInp
}
@@ -24,7 +24,7 @@ type ListRes struct {
// ViewReq 获取指定信息
type ViewReq struct {
- g.Meta `path:"/smsLog/view" method:"get" tags:"短信记录" summary:"获取指定信息"`
+ g.Meta `path:"/smsLog/view" method:"get" tags:"短信" summary:"获取指定短信信息"`
sysin.SmsLogViewInp
}
@@ -32,26 +32,10 @@ type ViewRes struct {
*sysin.SmsLogViewModel
}
-// EditReq 修改/新增数据
-type EditReq struct {
- g.Meta `path:"/smsLog/edit" method:"post" tags:"短信记录" summary:"修改/新增短信记录"`
- sysin.SmsLogEditInp
-}
-
-type EditRes struct{}
-
// DeleteReq 删除
type DeleteReq struct {
- g.Meta `path:"/smsLog/delete" method:"post" tags:"短信记录" summary:"删除短信记录"`
+ g.Meta `path:"/smsLog/delete" method:"post" tags:"短信" summary:"删除短信记录"`
sysin.SmsLogDeleteInp
}
type DeleteRes struct{}
-
-// StatusReq 更新状态
-type StatusReq struct {
- g.Meta `path:"/smsLog/status" method:"post" tags:"短信记录" summary:"更新短信记录状态"`
- sysin.SmsLogStatusInp
-}
-
-type StatusRes struct{}
diff --git a/server/api/admin/testcategory/testcategory.go b/server/api/admin/testcategory/testcategory.go
new file mode 100644
index 0000000..e4b9dbc
--- /dev/null
+++ b/server/api/admin/testcategory/testcategory.go
@@ -0,0 +1,69 @@
+// Package testcategory
+// @Link https://github.com/bufanyun/hotgo
+// @Copyright Copyright (c) 2024 HotGo CLI
+// @Author Ms <133814250@qq.com>
+// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
+// @AutoGenerate Version 2.13.1
+package testcategory
+
+import (
+ "hotgo/internal/model/input/form"
+ "hotgo/internal/model/input/sysin"
+
+ "github.com/gogf/gf/v2/frame/g"
+)
+
+// ListReq 查询测试分类列表
+type ListReq struct {
+ g.Meta `path:"/testCategory/list" method:"get" tags:"测试分类" summary:"获取测试分类列表"`
+ sysin.TestCategoryListInp
+}
+
+type ListRes struct {
+ form.PageRes
+ List []*sysin.TestCategoryListModel `json:"list" dc:"数据列表"`
+}
+
+// ViewReq 获取测试分类指定信息
+type ViewReq struct {
+ g.Meta `path:"/testCategory/view" method:"get" tags:"测试分类" summary:"获取测试分类指定信息"`
+ sysin.TestCategoryViewInp
+}
+
+type ViewRes struct {
+ *sysin.TestCategoryViewModel
+}
+
+// EditReq 修改/新增测试分类
+type EditReq struct {
+ g.Meta `path:"/testCategory/edit" method:"post" tags:"测试分类" summary:"修改/新增测试分类"`
+ sysin.TestCategoryEditInp
+}
+
+type EditRes struct{}
+
+// DeleteReq 删除测试分类
+type DeleteReq struct {
+ g.Meta `path:"/testCategory/delete" method:"post" tags:"测试分类" summary:"删除测试分类"`
+ sysin.TestCategoryDeleteInp
+}
+
+type DeleteRes struct{}
+
+// MaxSortReq 获取测试分类最大排序
+type MaxSortReq struct {
+ g.Meta `path:"/testCategory/maxSort" method:"get" tags:"测试分类" summary:"获取测试分类最大排序"`
+ sysin.TestCategoryMaxSortInp
+}
+
+type MaxSortRes struct {
+ *sysin.TestCategoryMaxSortModel
+}
+
+// StatusReq 更新测试分类状态
+type StatusReq struct {
+ g.Meta `path:"/testCategory/status" method:"post" tags:"测试分类" summary:"更新测试分类状态"`
+ sysin.TestCategoryStatusInp
+}
+
+type StatusRes struct{}
\ No newline at end of file
diff --git a/server/api/api/user/hello.go b/server/api/api/user/hello.go
deleted file mode 100644
index 40a4708..0000000
--- a/server/api/api/user/hello.go
+++ /dev/null
@@ -1,19 +0,0 @@
-// Package user
-// @Link https://github.com/bufanyun/hotgo
-// @Copyright Copyright (c) 2023 HotGo CLI
-// @Author Ms <133814250@qq.com>
-// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
-package user
-
-import (
- "github.com/gogf/gf/v2/frame/g"
-)
-
-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 {
- Tips string `json:"tips"`
-}
diff --git a/server/go.mod b/server/go.mod
index 58da87f..f2c9f78 100644
--- a/server/go.mod
+++ b/server/go.mod
@@ -1,6 +1,8 @@
module hotgo
-go 1.19
+go 1.21
+
+toolchain go1.22.1
require (
github.com/Shopify/sarama v1.34.1
@@ -9,20 +11,20 @@ require (
github.com/alibabacloud-go/tea v1.1.20
github.com/alibabacloud-go/tea-utils/v2 v2.0.1
github.com/aliyun/aliyun-oss-go-sdk v2.2.6+incompatible
- github.com/apache/rocketmq-client-go/v2 v2.1.0
+ github.com/apache/rocketmq-client-go/v2 v2.1.2
github.com/casbin/casbin/v2 v2.55.0
github.com/forgoer/openssl v1.4.0
github.com/go-pay/gopay v1.5.91
- github.com/gogf/gf/contrib/drivers/mysql/v2 v2.6.4
- github.com/gogf/gf/contrib/nosql/redis/v2 v2.6.4
- github.com/gogf/gf/contrib/trace/jaeger/v2 v2.6.4
- github.com/gogf/gf/v2 v2.6.4
+ github.com/gogf/gf/contrib/drivers/mysql/v2 v2.7.0
+ github.com/gogf/gf/contrib/nosql/redis/v2 v2.7.0
+ github.com/gogf/gf/contrib/trace/jaeger/v2 v2.7.0
+ github.com/gogf/gf/v2 v2.7.0
github.com/gogf/selfupdate v0.0.0-20231215043001-5c48c528462f
github.com/golang-jwt/jwt/v5 v5.0.0
github.com/gorilla/websocket v1.5.1
github.com/kayon/iploc v0.0.0-20200312105652-bda3e968a794
github.com/minio/minio-go/v7 v7.0.63
- github.com/mojocn/base64Captcha v1.3.5
+ github.com/mojocn/base64Captcha v1.3.6
github.com/olekukonko/tablewriter v0.0.5
github.com/qiniu/go-sdk/v7 v7.14.0
github.com/shirou/gopsutil/v3 v3.23.3
@@ -32,14 +34,16 @@ require (
github.com/tencentyun/cos-go-sdk-v5 v0.7.45
github.com/ufilesdk-dev/ufile-gosdk v1.0.3
github.com/xuri/excelize/v2 v2.6.0
- go.opentelemetry.io/otel v1.24.0
+ go.opentelemetry.io/otel v1.25.0
golang.org/x/mod v0.9.0
+ golang.org/x/net v0.24.0
golang.org/x/tools v0.7.0
gopkg.in/yaml.v3 v3.0.1
)
require (
aead.dev/minisign v0.2.0 // indirect
+ filippo.io/edwards25519 v1.1.0 // indirect
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect
@@ -50,7 +54,7 @@ require (
github.com/alibabacloud-go/tea-xml v1.1.2 // indirect
github.com/aliyun/credentials-go v1.1.2 // indirect
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d // indirect
- github.com/cespare/xxhash/v2 v2.2.0 // indirect
+ github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/clbanning/mxj v1.8.4 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
@@ -67,7 +71,7 @@ require (
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-redis/redis/v8 v8.11.5 // indirect
- github.com/go-sql-driver/mysql v1.7.1 // indirect
+ github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/snappy v0.0.4 // indirect
@@ -98,6 +102,7 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/mozillazg/go-httpheader v0.2.1 // indirect
+ github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pierrec/lz4/v4 v4.1.18 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
@@ -120,17 +125,17 @@ require (
github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect
- go.opentelemetry.io/otel/metric v1.24.0 // indirect
- go.opentelemetry.io/otel/sdk v1.24.0 // indirect
- go.opentelemetry.io/otel/trace v1.24.0 // indirect
+ go.opentelemetry.io/otel/metric v1.25.0 // indirect
+ go.opentelemetry.io/otel/sdk v1.25.0 // indirect
+ go.opentelemetry.io/otel/trace v1.25.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
- golang.org/x/crypto v0.21.0 // indirect
- golang.org/x/image v0.1.0 // indirect
- golang.org/x/net v0.22.0 // indirect
+ golang.org/x/crypto v0.22.0 // indirect
+ golang.org/x/image v0.13.0 // indirect
golang.org/x/sync v0.1.0 // indirect
- golang.org/x/sys v0.18.0 // indirect
+ golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
+ gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
stathat.com/c/consistent v1.0.0 // indirect
)
diff --git a/server/go.sum b/server/go.sum
index e0a15bd..3547699 100644
--- a/server/go.sum
+++ b/server/go.sum
@@ -33,7 +33,10 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
+filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
@@ -80,22 +83,24 @@ github.com/aliyun/aliyun-oss-go-sdk v2.2.6+incompatible h1:KXeJoM1wo9I/6xPTyt6qC
github.com/aliyun/aliyun-oss-go-sdk v2.2.6+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/aliyun/credentials-go v1.1.2 h1:qU1vwGIBb3UJ8BwunHDRFtAhS6jnQLnde/yk0+Ih2GY=
github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
-github.com/apache/rocketmq-client-go/v2 v2.1.0 h1:3eABKfxc1WmS2lLTTbKMe1gZfZV6u1Sx9orFnOfABV0=
-github.com/apache/rocketmq-client-go/v2 v2.1.0/go.mod h1:oEZKFDvS7sz/RWU0839+dQBupazyBV7WX5cP6nrio0Q=
+github.com/apache/rocketmq-client-go/v2 v2.1.2 h1:yt73olKe5N6894Dbm+ojRf/JPiP0cxfDNNffKwhpJVg=
+github.com/apache/rocketmq-client-go/v2 v2.1.2/go.mod h1:6I6vgxHR3hzrvn+6n/4mrhS+UTulzK/X9LB2Vk1U5gE=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d h1:pVrfxiGfwelyab6n21ZBkbkmbevaf+WvMIiR7sr97hw=
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
+github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
+github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/casbin/casbin/v2 v2.55.0 h1:RyU+OacnVzjxof1U3bmxHM7oCRdx9+gNnkclrvof/zI=
github.com/casbin/casbin/v2 v2.55.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
-github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@@ -166,18 +171,18 @@ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl
github.com/go-playground/validator/v10 v10.8.0/go.mod h1:9JhgTzTaE31GZDpH/HSvHiRJrJ3iKAgqqH0Bl/Ocjdk=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
-github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
-github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
+github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
+github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
-github.com/gogf/gf/contrib/drivers/mysql/v2 v2.6.4 h1:ScG3YcYMDEP/UrwNtwQPt2noySa5ZpoV7BxrwaeBaro=
-github.com/gogf/gf/contrib/drivers/mysql/v2 v2.6.4/go.mod h1:oFFE9u1zHkxVXk7ZkNipXQR9JFyDZDiixZy243ywhfQ=
-github.com/gogf/gf/contrib/nosql/redis/v2 v2.6.4 h1:43517FF//GKgGpb4pxHl3NWLxW/inTAQ7rUFnfUIoYY=
-github.com/gogf/gf/contrib/nosql/redis/v2 v2.6.4/go.mod h1:9qNdKgqB+tHC9XczIoMzfSHmWkphQMXqxJXF6g9Icr4=
-github.com/gogf/gf/contrib/trace/jaeger/v2 v2.6.4 h1:PV0V2CPspwC+qgmqMC7qjCFkxH/SkfLwC0fg26ZTY54=
-github.com/gogf/gf/contrib/trace/jaeger/v2 v2.6.4/go.mod h1:Wnu7ASD+BWWlPn9NlSNOmCip7tHnYSXRSSjFJ5cCTEo=
-github.com/gogf/gf/v2 v2.6.4 h1:w7HXdH9mcTsn/aE13CkaDbRArmAL1KS3FuQqDi6u74Y=
-github.com/gogf/gf/v2 v2.6.4/go.mod h1:x2XONYcI4hRQ/4gMNbWHmZrNzSEIg20s2NULbzom5k0=
+github.com/gogf/gf/contrib/drivers/mysql/v2 v2.7.0 h1:5Igvtz4gy5UMvH+Ut4kLIpwSzggV9ZgDVBsIiOctH5E=
+github.com/gogf/gf/contrib/drivers/mysql/v2 v2.7.0/go.mod h1:0+flZ0clMKjtH1sTI7YD2KG4FPr8xz0L9h1WMd5M2Z8=
+github.com/gogf/gf/contrib/nosql/redis/v2 v2.7.0 h1:hJxshC0gZyFZaGo2HItXd5XMzIMbCRcgShr1ljMYwUc=
+github.com/gogf/gf/contrib/nosql/redis/v2 v2.7.0/go.mod h1:is3Q3ItZSPMZ1RBJ3xIcEasyGZnOg8eNeG9dubOx/zc=
+github.com/gogf/gf/contrib/trace/jaeger/v2 v2.7.0 h1:P75Jfq5rP8TUUTyobAooULJDDCaSOkgL14gnXudgY1E=
+github.com/gogf/gf/contrib/trace/jaeger/v2 v2.7.0/go.mod h1:hRnwcGAFG8c6+9rg//zbr0Ve3XZVzAuALfzfbz3xBBk=
+github.com/gogf/gf/v2 v2.7.0 h1:CjxhbMiE7oqf6K8ZtGuKt3dQEwK4vL6LhiI+dI7tJGU=
+github.com/gogf/gf/v2 v2.7.0/go.mod h1:Qu8nimKt9aupJQcdUL85tWF4Mfxocz97zUt8UC4abVI=
github.com/gogf/selfupdate v0.0.0-20231215043001-5c48c528462f h1:7xfXR/BhG3JDqO1s45n65Oyx9t4E/UqDOXep6jXdLCM=
github.com/gogf/selfupdate v0.0.0-20231215043001-5c48c528462f/go.mod h1:HnYoio6S7VaFJdryKcD/r9HgX+4QzYfr00XiXUo/xz0=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -232,6 +237,7 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -288,7 +294,6 @@ github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZ
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@@ -350,8 +355,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
-github.com/mojocn/base64Captcha v1.3.5 h1:Qeilr7Ta6eDtG4S+tQuZ5+hO+QHbiGAJdi4PfoagaA0=
-github.com/mojocn/base64Captcha v1.3.5/go.mod h1:/tTTXn4WTpX9CfrmipqRytCpJ27Uw3G6I7NcP2WwcmY=
+github.com/mojocn/base64Captcha v1.3.6 h1:gZEKu1nsKpttuIAQgWHO+4Mhhls8cAKyiV2Ew03H+Tw=
+github.com/mojocn/base64Captcha v1.3.6/go.mod h1:i5CtHvm+oMbj1UzEPXaA8IH/xHFZ3DGY3Wh3dBpZ28E=
github.com/mozillazg/go-httpheader v0.2.1 h1:geV7TrjbL8KXSyvghnFm+NyTux/hxwueTSrwhe88TQQ=
github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@@ -374,6 +379,8 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
+github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
+github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ=
github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
@@ -436,7 +443,7 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
github.com/silenceper/wechat/v2 v2.1.4 h1:X+G9C/EiBET5AK0zhrflX3ESCP/yxhJUvoRoSXHm0js=
github.com/silenceper/wechat/v2 v2.1.4/go.mod h1:F0PKqImb15THnwoqRNrZO1z3vpwyWuiHr5zzfnjdECY=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
-github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
+github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
@@ -446,7 +453,6 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.1.0 h1:MkTeG1DMwsrdH7QtLXy5W+fUxWq+vmb6cLmyJ7aRtF0=
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
-github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
@@ -466,7 +472,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.633 h1:Yj8s35IjbgaHp4Ic9BZLVGWdN2gXBMtwYi1JJ+qYbrc=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.633/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
@@ -475,13 +482,11 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms v1.0.633 h1:rtgRqgZ
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms v1.0.633/go.mod h1:9q29WcGkZ7R0uQjoY10Tzb8A18C2cNggbq2ZC2HRXZE=
github.com/tencentyun/cos-go-sdk-v5 v0.7.45 h1:5/ZGOv846tP6+2X7w//8QjLgH2KcUK+HciFbfjWquFU=
github.com/tencentyun/cos-go-sdk-v5 v0.7.45/go.mod h1:DH9US8nB+AJXqwu/AMOrCFN1COv3dpytXuJWHgdg7kE=
-github.com/tidwall/gjson v1.2.1/go.mod h1:c/nTNbUr0E0OrXEhq1pwa8iEgc2DOt4ZZqAt1HtCkPA=
+github.com/tidwall/gjson v1.13.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.14.1 h1:iymTbGkQBhveq21bEvAQ81I0LEBork8BFe1CUZXdyuo=
github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
-github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
-github.com/tidwall/pretty v0.0.0-20190325153808-1166b9ac2b65/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM=
@@ -516,16 +521,16 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
-go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
+go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k=
+go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI=
-go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
-go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
-go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
-go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
-go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
-go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
+go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA=
+go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s=
+go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo=
+go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw=
+go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM=
+go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I=
go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
@@ -544,8 +549,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220408190544-5352b0902921/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
-golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
+golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
+golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -557,11 +562,10 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
-golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
-golang.org/x/image v0.1.0 h1:r8Oj8ZA2Xy12/b5KZYj3tuv7NG/fBz3TwQVvpJ9l8Rk=
-golang.org/x/image v0.1.0/go.mod h1:iyPr49SD/G/TBxYVB/9RRtGUT5eNbo2u4NamWeQcD5c=
+golang.org/x/image v0.13.0 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg=
+golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -582,6 +586,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -623,8 +628,9 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
-golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
+golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -705,11 +711,12 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
-golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
+golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -717,7 +724,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -770,6 +778,7 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -865,6 +874,8 @@ gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaD
gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
+gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/server/hack/config.yaml b/server/hack/config.yaml
index d98f903..3e75ed9 100644
--- a/server/hack/config.yaml
+++ b/server/hack/config.yaml
@@ -22,7 +22,7 @@ gfcli:
gen:
dao:
- - link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true"
+ - link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true&charset=utf8mb4"
group: "default" # 分组 使用hotgo代码生成功能时必须填
# tables: "" # 指定当前数据库中需要执行代码生成的数据表。如果为空,表示数据库的所有表都会生成。
tablesEx: "hg_sys_addons_install" # 指定当前数据库中需要排除代码生成的数据表。
diff --git a/server/internal/cmd/cmd.go b/server/internal/cmd/cmd.go
index 4e759ba..154016c 100644
--- a/server/internal/cmd/cmd.go
+++ b/server/internal/cmd/cmd.go
@@ -36,6 +36,8 @@ var (
---------------------------------------------------------------------------------
工具
>> 释放casbin权限,用于清理无效的权限设置 [go run main.go tools -m=casbin -a1=refresh]
+ >> 打印所有打包的资源文件列表 [go run main.go tools -m=gres -a1=dump]
+ >> 打印指定打包的资源文件内容 [go run main.go tools -m=gres -a1=content -a2=resource/template/home/index.html]
---------------------------------------------------------------------------------
升级更新
>> 修复菜单关系树 [go run main.go up -m=fix -a1=menuTree]
diff --git a/server/internal/cmd/handler_shutdown.go b/server/internal/cmd/handler_shutdown.go
index 8a105ad..61a51ba 100644
--- a/server/internal/cmd/handler_shutdown.go
+++ b/server/internal/cmd/handler_shutdown.go
@@ -23,8 +23,8 @@ var (
// signalHandlerForOverall 关闭信号处理
func signalHandlerForOverall(sig os.Signal) {
- serverCloseSignal <- struct{}{}
serverCloseEvent(gctx.GetInitCtx())
+ serverCloseSignal <- struct{}{}
}
// signalListen 信号监听
diff --git a/server/internal/cmd/tools.go b/server/internal/cmd/tools.go
index 7f6dbbe..c098f08 100644
--- a/server/internal/cmd/tools.go
+++ b/server/internal/cmd/tools.go
@@ -7,9 +7,11 @@ package cmd
import (
"context"
+ "fmt"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcmd"
+ "github.com/gogf/gf/v2/os/gres"
"hotgo/internal/library/casbin"
)
@@ -35,6 +37,8 @@ var (
switch method {
case "casbin":
err = handleCasbin(ctx, args)
+ case "gres":
+ err = handleGRes(ctx, args)
default:
err = gerror.Newf("tools method[%v] does not exist", method)
}
@@ -66,3 +70,38 @@ func handleCasbin(ctx context.Context, args map[string]string) (err error) {
}
return
}
+
+func handleGRes(ctx context.Context, args map[string]string) (err error) {
+ a1, ok := args["a1"]
+ if !ok {
+ err = gerror.New("gres args cannot be empty.")
+ return
+ }
+
+ switch a1 {
+ case "dump":
+ gres.Dump()
+ case "content":
+ path, ok := args["a2"]
+ if !ok {
+ err = gerror.New("缺少查看文件路径参数:`a2`")
+ return
+ }
+
+ if !gres.Contains(path) {
+ err = gerror.Newf("没有找到资源文件:%v", path)
+ return
+ }
+ content := string(gres.GetContent(path))
+
+ if len(content) == 0 {
+ err = gerror.Newf("没有找到资源文件内容,请确认传入`a2`参数是一个文件,a2:%v", path)
+ return
+ }
+ fmt.Println("以下是资源文件内容:")
+ fmt.Println(content)
+ default:
+ err = gerror.Newf("handleGRes a1 is invalid, a1:%v", a1)
+ }
+ return
+}
diff --git a/server/internal/consts/debris.go b/server/internal/consts/debris.go
index 2153f5b..b512d20 100644
--- a/server/internal/consts/debris.go
+++ b/server/internal/consts/debris.go
@@ -22,3 +22,10 @@ const (
DefaultPageSize = 1 // 默认列表分页加载页码
MaxSortIncr = 10 // 最大排序值增量
)
+
+// TenantField 租户字段
+const (
+ TenantId = "tenant_id" // 租户ID
+ MerchantId = "merchant_id" // 商户ID
+ UserId = "user_id" // 用户ID
+)
diff --git a/server/internal/consts/dept.go b/server/internal/consts/dept.go
new file mode 100644
index 0000000..7c2f2d6
--- /dev/null
+++ b/server/internal/consts/dept.go
@@ -0,0 +1,30 @@
+// Package consts
+// @Link https://github.com/bufanyun/hotgo
+// @Copyright Copyright (c) 2024 HotGo CLI
+// @Author Ms <133814250@qq.com>
+// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
+package consts
+
+import (
+ "hotgo/internal/library/dict"
+ "hotgo/internal/model"
+)
+
+func init() {
+ dict.RegisterEnums("deptType", "部门类型选项", DeptTypeOptions)
+}
+
+const (
+ DeptTypeCompany = "company" // 公司
+ DeptTypeTenant = "tenant" // 租户
+ DeptTypeMerchant = "merchant" // 商户
+ DeptTypeUser = "user" // 用户
+)
+
+// DeptTypeOptions 部门类型选项
+var DeptTypeOptions = []*model.Option{
+ dict.GenSuccessOption(DeptTypeCompany, "公司"),
+ dict.GenErrorOption(DeptTypeTenant, "租户"),
+ dict.GenInfoOption(DeptTypeMerchant, "商户"),
+ dict.GenWarningOption(DeptTypeUser, "用户"),
+}
diff --git a/server/internal/consts/gencodes.go b/server/internal/consts/gencodes.go
index 5f87aaa..88da4e7 100644
--- a/server/internal/consts/gencodes.go
+++ b/server/internal/consts/gencodes.go
@@ -3,9 +3,13 @@
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
-//
package consts
+import (
+ "hotgo/internal/library/dict"
+ "hotgo/internal/model"
+)
+
// 生成代码类型
const (
GenCodesTypeCurd = 10 // 增删改查列表
@@ -16,7 +20,7 @@ const (
var GenCodesTypeNameMap = map[int]string{
GenCodesTypeCurd: "增删改查列表",
- GenCodesTypeTree: "关系树列表(未实现)",
+ GenCodesTypeTree: "关系树列表",
GenCodesTypeQueue: "队列消费者(未实现)",
GenCodesTypeCron: "定时任务(未实现)",
}
@@ -79,3 +83,14 @@ const (
GenCodesIndexPK = "PRI" // 主键索引
GenCodesIndexUNI = "UNI" // 唯一索引
)
+
+const (
+ GenCodesTreeStyleTypeNormal = 1 // 普通树表格
+ GenCodesTreeStyleTypeOption = 2 // 选项式树表
+)
+
+// GenCodesTreeStyleTypeOptions 树表样式选项
+var GenCodesTreeStyleTypeOptions = []*model.Option{
+ dict.GenSuccessOption(GenCodesTreeStyleTypeNormal, "普通树表格"),
+ dict.GenInfoOption(GenCodesTreeStyleTypeOption, "选项式树表"),
+}
diff --git a/server/internal/consts/pay.go b/server/internal/consts/pay.go
index c6fba7e..084dd4c 100644
--- a/server/internal/consts/pay.go
+++ b/server/internal/consts/pay.go
@@ -14,6 +14,7 @@ import (
func init() {
dict.RegisterEnums("payType", "支付方式", PayTypeOptions)
+ dict.RegisterEnums("payStatus", "支付状态", PayStatusOptions)
}
const (
@@ -72,6 +73,12 @@ const (
PayStatusOk = 2 // 已支付
)
+// PayStatusOptions 支付状态选项
+var PayStatusOptions = []*model.Option{
+ dict.GenDefaultOption(PayStatusWait, "待支付"),
+ dict.GenSuccessOption(PayStatusOk, "已支付"),
+}
+
// 退款状态
const (
diff --git a/server/internal/consts/version.go b/server/internal/consts/version.go
index 4071a71..ed5c34e 100644
--- a/server/internal/consts/version.go
+++ b/server/internal/consts/version.go
@@ -7,5 +7,5 @@ package consts
// VersionApp HotGo版本
const (
- VersionApp = "2.13.1"
+ VersionApp = "2.15.1"
)
diff --git a/server/internal/controller/admin/admin/dept.go b/server/internal/controller/admin/admin/dept.go
index 2b2fc3d..5730ce6 100644
--- a/server/internal/controller/admin/admin/dept.go
+++ b/server/internal/controller/admin/admin/dept.go
@@ -54,12 +54,6 @@ func (c *cDept) List(ctx context.Context, req *dept.ListReq) (res *dept.ListRes,
return
}
-// Status 更新部门状态
-func (c *cDept) Status(ctx context.Context, req *dept.StatusReq) (res *dept.StatusRes, err error) {
- err = service.AdminDept().Status(ctx, &req.DeptStatusInp)
- return
-}
-
// Option 获取部门选项树
func (c *cDept) Option(ctx context.Context, req *dept.OptionReq) (res *dept.OptionRes, err error) {
list, totalCount, err := service.AdminDept().Option(ctx, &req.DeptOptionInp)
@@ -72,3 +66,10 @@ func (c *cDept) Option(ctx context.Context, req *dept.OptionReq) (res *dept.Opti
res.PageRes.Pack(req, totalCount)
return
}
+
+// TreeOption 获取部门管理关系树选项
+func (c *cDept) TreeOption(ctx context.Context, req *dept.TreeOptionReq) (res *dept.TreeOptionRes, err error) {
+ data, err := service.AdminDept().TreeOption(ctx)
+ res = (*dept.TreeOptionRes)(&data)
+ return
+}
diff --git a/server/internal/controller/admin/admin/monitor.go b/server/internal/controller/admin/admin/monitor.go
index 702398a..ea8657a 100644
--- a/server/internal/controller/admin/admin/monitor.go
+++ b/server/internal/controller/admin/admin/monitor.go
@@ -8,7 +8,6 @@ package admin
import (
"context"
"github.com/gogf/gf/v2/errors/gerror"
- "github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/text/gstr"
"hotgo/api/admin/monitor"
@@ -103,12 +102,10 @@ func (c *cMonitor) UserOnlineList(ctx context.Context, req *monitor.UserOnlineLi
return clients[i].FirstTime < clients[j].FirstTime
})
- isDemo := g.Cfg().MustGet(ctx, "hotgo.isDemo", false).Bool()
_, perPage, offset := form.CalPage(req.Page, req.PerPage)
-
for k, v := range clients {
if k >= offset && i <= perPage {
- if isDemo {
+ if simple.IsDemo(ctx) {
v.IP = consts.DemoTips
}
res.List = append(res.List, v)
diff --git a/server/internal/controller/admin/sys/curd_demo.go b/server/internal/controller/admin/sys/curd_demo.go
index 5cb7e84..38b1b8b 100644
--- a/server/internal/controller/admin/sys/curd_demo.go
+++ b/server/internal/controller/admin/sys/curd_demo.go
@@ -3,7 +3,7 @@
// @Copyright Copyright (c) 2024 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
-// @AutoGenerate Version 2.12.10
+// @AutoGenerate Version 2.13.1
package sys
import (
@@ -19,7 +19,7 @@ var (
type cCurdDemo struct{}
-// List 查看生成演示列表
+// List 查看CURD列表列表
func (c *cCurdDemo) List(ctx context.Context, req *curddemo.ListReq) (res *curddemo.ListRes, err error) {
list, totalCount, err := service.SysCurdDemo().List(ctx, &req.CurdDemoListInp)
if err != nil {
@@ -36,19 +36,19 @@ func (c *cCurdDemo) List(ctx context.Context, req *curddemo.ListReq) (res *curdd
return
}
-// Export 导出生成演示列表
+// Export 导出CURD列表列表
func (c *cCurdDemo) Export(ctx context.Context, req *curddemo.ExportReq) (res *curddemo.ExportRes, err error) {
err = service.SysCurdDemo().Export(ctx, &req.CurdDemoListInp)
return
}
-// Edit 更新生成演示
+// Edit 更新CURD列表
func (c *cCurdDemo) Edit(ctx context.Context, req *curddemo.EditReq) (res *curddemo.EditRes, err error) {
err = service.SysCurdDemo().Edit(ctx, &req.CurdDemoEditInp)
return
}
-// MaxSort 获取生成演示最大排序
+// MaxSort 获取CURD列表最大排序
func (c *cCurdDemo) MaxSort(ctx context.Context, req *curddemo.MaxSortReq) (res *curddemo.MaxSortRes, err error) {
data, err := service.SysCurdDemo().MaxSort(ctx, &req.CurdDemoMaxSortInp)
if err != nil {
@@ -60,7 +60,7 @@ func (c *cCurdDemo) MaxSort(ctx context.Context, req *curddemo.MaxSortReq) (res
return
}
-// View 获取指定生成演示信息
+// View 获取指定CURD列表信息
func (c *cCurdDemo) View(ctx context.Context, req *curddemo.ViewReq) (res *curddemo.ViewRes, err error) {
data, err := service.SysCurdDemo().View(ctx, &req.CurdDemoViewInp)
if err != nil {
@@ -72,19 +72,13 @@ func (c *cCurdDemo) View(ctx context.Context, req *curddemo.ViewReq) (res *curdd
return
}
-// Delete 删除生成演示
+// Delete 删除CURD列表
func (c *cCurdDemo) Delete(ctx context.Context, req *curddemo.DeleteReq) (res *curddemo.DeleteRes, err error) {
err = service.SysCurdDemo().Delete(ctx, &req.CurdDemoDeleteInp)
return
}
-// Status 更新生成演示状态
-func (c *cCurdDemo) Status(ctx context.Context, req *curddemo.StatusReq) (res *curddemo.StatusRes, err error) {
- err = service.SysCurdDemo().Status(ctx, &req.CurdDemoStatusInp)
- return
-}
-
-// Switch 更新生成演示开关状态
+// Switch 更新CURD列表开关状态
func (c *cCurdDemo) Switch(ctx context.Context, req *curddemo.SwitchReq) (res *curddemo.SwitchRes, err error) {
err = service.SysCurdDemo().Switch(ctx, &req.CurdDemoSwitchInp)
return
diff --git a/server/internal/controller/admin/sys/normal_tree_demo.go b/server/internal/controller/admin/sys/normal_tree_demo.go
new file mode 100644
index 0000000..0a91fb9
--- /dev/null
+++ b/server/internal/controller/admin/sys/normal_tree_demo.go
@@ -0,0 +1,80 @@
+// Package sys
+// @Link https://github.com/bufanyun/hotgo
+// @Copyright Copyright (c) 2024 HotGo CLI
+// @Author Ms <133814250@qq.com>
+// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
+// @AutoGenerate Version 2.13.1
+package sys
+
+import (
+ "context"
+ "hotgo/api/admin/normaltreedemo"
+ "hotgo/internal/model/input/sysin"
+ "hotgo/internal/service"
+)
+
+var (
+ NormalTreeDemo = cNormalTreeDemo{}
+)
+
+type cNormalTreeDemo struct{}
+
+// List 查看普通树表列表
+func (c *cNormalTreeDemo) List(ctx context.Context, req *normaltreedemo.ListReq) (res *normaltreedemo.ListRes, err error) {
+ list, totalCount, err := service.SysNormalTreeDemo().List(ctx, &req.NormalTreeDemoListInp)
+ if err != nil {
+ return
+ }
+
+ if list == nil {
+ list = []*sysin.NormalTreeDemoListModel{}
+ }
+
+ res = new(normaltreedemo.ListRes)
+ res.List = list
+ res.PageRes.Pack(req, totalCount)
+ return
+}
+
+// Edit 更新普通树表
+func (c *cNormalTreeDemo) Edit(ctx context.Context, req *normaltreedemo.EditReq) (res *normaltreedemo.EditRes, err error) {
+ err = service.SysNormalTreeDemo().Edit(ctx, &req.NormalTreeDemoEditInp)
+ return
+}
+
+// MaxSort 获取普通树表最大排序
+func (c *cNormalTreeDemo) MaxSort(ctx context.Context, req *normaltreedemo.MaxSortReq) (res *normaltreedemo.MaxSortRes, err error) {
+ data, err := service.SysNormalTreeDemo().MaxSort(ctx, &req.NormalTreeDemoMaxSortInp)
+ if err != nil {
+ return
+ }
+
+ res = new(normaltreedemo.MaxSortRes)
+ res.NormalTreeDemoMaxSortModel = data
+ return
+}
+
+// View 获取指定普通树表信息
+func (c *cNormalTreeDemo) View(ctx context.Context, req *normaltreedemo.ViewReq) (res *normaltreedemo.ViewRes, err error) {
+ data, err := service.SysNormalTreeDemo().View(ctx, &req.NormalTreeDemoViewInp)
+ if err != nil {
+ return
+ }
+
+ res = new(normaltreedemo.ViewRes)
+ res.NormalTreeDemoViewModel = data
+ return
+}
+
+// Delete 删除普通树表
+func (c *cNormalTreeDemo) Delete(ctx context.Context, req *normaltreedemo.DeleteReq) (res *normaltreedemo.DeleteRes, err error) {
+ err = service.SysNormalTreeDemo().Delete(ctx, &req.NormalTreeDemoDeleteInp)
+ return
+}
+
+// TreeOption 获取普通树表关系树选项
+func (c *cNormalTreeDemo) TreeOption(ctx context.Context, req *normaltreedemo.TreeOptionReq) (res *normaltreedemo.TreeOptionRes, err error) {
+ data, err := service.SysNormalTreeDemo().TreeOption(ctx)
+ res = (*normaltreedemo.TreeOptionRes)(&data)
+ return
+}
\ No newline at end of file
diff --git a/server/internal/controller/admin/sys/option_tree_demo.go b/server/internal/controller/admin/sys/option_tree_demo.go
new file mode 100644
index 0000000..00b003f
--- /dev/null
+++ b/server/internal/controller/admin/sys/option_tree_demo.go
@@ -0,0 +1,80 @@
+// Package sys
+// @Link https://github.com/bufanyun/hotgo
+// @Copyright Copyright (c) 2024 HotGo CLI
+// @Author Ms <133814250@qq.com>
+// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
+// @AutoGenerate Version 2.13.1
+package sys
+
+import (
+ "context"
+ "hotgo/api/admin/optiontreedemo"
+ "hotgo/internal/model/input/sysin"
+ "hotgo/internal/service"
+)
+
+var (
+ OptionTreeDemo = cOptionTreeDemo{}
+)
+
+type cOptionTreeDemo struct{}
+
+// List 查看选项树表列表
+func (c *cOptionTreeDemo) List(ctx context.Context, req *optiontreedemo.ListReq) (res *optiontreedemo.ListRes, err error) {
+ list, totalCount, err := service.SysOptionTreeDemo().List(ctx, &req.OptionTreeDemoListInp)
+ if err != nil {
+ return
+ }
+
+ if list == nil {
+ list = []*sysin.OptionTreeDemoListModel{}
+ }
+
+ res = new(optiontreedemo.ListRes)
+ res.List = list
+ res.PageRes.Pack(req, totalCount)
+ return
+}
+
+// Edit 更新选项树表
+func (c *cOptionTreeDemo) Edit(ctx context.Context, req *optiontreedemo.EditReq) (res *optiontreedemo.EditRes, err error) {
+ err = service.SysOptionTreeDemo().Edit(ctx, &req.OptionTreeDemoEditInp)
+ return
+}
+
+// MaxSort 获取选项树表最大排序
+func (c *cOptionTreeDemo) MaxSort(ctx context.Context, req *optiontreedemo.MaxSortReq) (res *optiontreedemo.MaxSortRes, err error) {
+ data, err := service.SysOptionTreeDemo().MaxSort(ctx, &req.OptionTreeDemoMaxSortInp)
+ if err != nil {
+ return
+ }
+
+ res = new(optiontreedemo.MaxSortRes)
+ res.OptionTreeDemoMaxSortModel = data
+ return
+}
+
+// View 获取指定选项树表信息
+func (c *cOptionTreeDemo) View(ctx context.Context, req *optiontreedemo.ViewReq) (res *optiontreedemo.ViewRes, err error) {
+ data, err := service.SysOptionTreeDemo().View(ctx, &req.OptionTreeDemoViewInp)
+ if err != nil {
+ return
+ }
+
+ res = new(optiontreedemo.ViewRes)
+ res.OptionTreeDemoViewModel = data
+ return
+}
+
+// Delete 删除选项树表
+func (c *cOptionTreeDemo) Delete(ctx context.Context, req *optiontreedemo.DeleteReq) (res *optiontreedemo.DeleteRes, err error) {
+ err = service.SysOptionTreeDemo().Delete(ctx, &req.OptionTreeDemoDeleteInp)
+ return
+}
+
+// TreeOption 获取选项树表关系树选项
+func (c *cOptionTreeDemo) TreeOption(ctx context.Context, req *optiontreedemo.TreeOptionReq) (res *optiontreedemo.TreeOptionRes, err error) {
+ data, err := service.SysOptionTreeDemo().TreeOption(ctx)
+ res = (*optiontreedemo.TreeOptionRes)(&data)
+ return
+}
\ No newline at end of file
diff --git a/server/internal/controller/admin/sys/sms_log.go b/server/internal/controller/admin/sys/sms_log.go
index f7f3872..b718efb 100644
--- a/server/internal/controller/admin/sys/sms_log.go
+++ b/server/internal/controller/admin/sys/sms_log.go
@@ -23,12 +23,6 @@ func (c *cSmsLog) Delete(ctx context.Context, req *smslog.DeleteReq) (res *smslo
return
}
-// Edit 更新
-func (c *cSmsLog) Edit(ctx context.Context, req *smslog.EditReq) (res *smslog.EditRes, err error) {
- err = service.SysSmsLog().Edit(ctx, &req.SmsLogEditInp)
- return
-}
-
// View 获取指定信息
func (c *cSmsLog) View(ctx context.Context, req *smslog.ViewReq) (res *smslog.ViewRes, err error) {
data, err := service.SysSmsLog().View(ctx, &req.SmsLogViewInp)
@@ -53,9 +47,3 @@ func (c *cSmsLog) List(ctx context.Context, req *smslog.ListReq) (res *smslog.Li
res.PageRes.Pack(req, totalCount)
return
}
-
-// Status 更新状态
-func (c *cSmsLog) Status(ctx context.Context, req *smslog.StatusReq) (res *smslog.StatusRes, err error) {
- err = service.SysSmsLog().Status(ctx, &req.SmsLogStatusInp)
- return
-}
diff --git a/server/internal/controller/admin/sys/test_category.go b/server/internal/controller/admin/sys/test_category.go
new file mode 100644
index 0000000..ac68ee1
--- /dev/null
+++ b/server/internal/controller/admin/sys/test_category.go
@@ -0,0 +1,79 @@
+// Package sys
+// @Link https://github.com/bufanyun/hotgo
+// @Copyright Copyright (c) 2024 HotGo CLI
+// @Author Ms <133814250@qq.com>
+// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
+// @AutoGenerate Version 2.13.1
+package sys
+
+import (
+ "context"
+ "hotgo/api/admin/testcategory"
+ "hotgo/internal/model/input/sysin"
+ "hotgo/internal/service"
+)
+
+var (
+ TestCategory = cTestCategory{}
+)
+
+type cTestCategory struct{}
+
+// List 查看测试分类列表
+func (c *cTestCategory) List(ctx context.Context, req *testcategory.ListReq) (res *testcategory.ListRes, err error) {
+ list, totalCount, err := service.SysTestCategory().List(ctx, &req.TestCategoryListInp)
+ if err != nil {
+ return
+ }
+
+ if list == nil {
+ list = []*sysin.TestCategoryListModel{}
+ }
+
+ res = new(testcategory.ListRes)
+ res.List = list
+ res.PageRes.Pack(req, totalCount)
+ return
+}
+
+// Edit 更新测试分类
+func (c *cTestCategory) Edit(ctx context.Context, req *testcategory.EditReq) (res *testcategory.EditRes, err error) {
+ err = service.SysTestCategory().Edit(ctx, &req.TestCategoryEditInp)
+ return
+}
+
+// MaxSort 获取测试分类最大排序
+func (c *cTestCategory) MaxSort(ctx context.Context, req *testcategory.MaxSortReq) (res *testcategory.MaxSortRes, err error) {
+ data, err := service.SysTestCategory().MaxSort(ctx, &req.TestCategoryMaxSortInp)
+ if err != nil {
+ return
+ }
+
+ res = new(testcategory.MaxSortRes)
+ res.TestCategoryMaxSortModel = data
+ return
+}
+
+// View 获取指定测试分类信息
+func (c *cTestCategory) View(ctx context.Context, req *testcategory.ViewReq) (res *testcategory.ViewRes, err error) {
+ data, err := service.SysTestCategory().View(ctx, &req.TestCategoryViewInp)
+ if err != nil {
+ return
+ }
+
+ res = new(testcategory.ViewRes)
+ res.TestCategoryViewModel = data
+ return
+}
+
+// Delete 删除测试分类
+func (c *cTestCategory) Delete(ctx context.Context, req *testcategory.DeleteReq) (res *testcategory.DeleteRes, err error) {
+ err = service.SysTestCategory().Delete(ctx, &req.TestCategoryDeleteInp)
+ return
+}
+
+// Status 更新测试分类状态
+func (c *cTestCategory) Status(ctx context.Context, req *testcategory.StatusReq) (res *testcategory.StatusRes, err error) {
+ err = service.SysTestCategory().Status(ctx, &req.TestCategoryStatusInp)
+ return
+}
\ No newline at end of file
diff --git a/server/internal/controller/api/user/hello.go b/server/internal/controller/api/user/hello.go
deleted file mode 100644
index 015f753..0000000
--- a/server/internal/controller/api/user/hello.go
+++ /dev/null
@@ -1,26 +0,0 @@
-// Package user
-// @Link https://github.com/bufanyun/hotgo
-// @Copyright Copyright (c) 2023 HotGo CLI
-// @Author Ms <133814250@qq.com>
-// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
-package user
-
-import (
- "context"
- "fmt"
- "hotgo/api/api/user"
- "hotgo/utility/simple"
-)
-
-var (
- Hello = cHello{}
-)
-
-type cHello struct{}
-
-func (c *cHello) Hello(ctx context.Context, req *user.HelloReq) (res *user.HelloRes, err error) {
- res = &user.HelloRes{
- Tips: fmt.Sprintf("hello %v, this is the api for %v applications.", req.Name, simple.AppName(ctx)),
- }
- return
-}
diff --git a/server/internal/controller/websocket/handler/admin/monitor.go b/server/internal/controller/websocket/handler/admin/monitor.go
index 33dfc77..f10ed93 100644
--- a/server/internal/controller/websocket/handler/admin/monitor.go
+++ b/server/internal/controller/websocket/handler/admin/monitor.go
@@ -21,6 +21,7 @@ import (
"hotgo/internal/websocket"
"hotgo/utility/file"
"hotgo/utility/format"
+ "hotgo/utility/simple"
"os"
"runtime"
"time"
@@ -76,8 +77,7 @@ func (c *cMonitor) RunInfo(client *websocket.Client, req *websocket.WRequest) {
"goSize": file.DirSize(pwd),
}
- isDemo := g.Cfg().MustGet(client.Context(), "hotgo.isDemo", false).Bool()
- if isDemo {
+ if simple.IsDemo(client.Context()) {
data["rootPath"] = consts.DemoTips
data["pwd"] = consts.DemoTips
data["intranet_ip"] = consts.DemoTips
diff --git a/server/internal/dao/addon_hgexample_tenant_order.go b/server/internal/dao/addon_hgexample_tenant_order.go
new file mode 100644
index 0000000..b5a0354
--- /dev/null
+++ b/server/internal/dao/addon_hgexample_tenant_order.go
@@ -0,0 +1,27 @@
+// =================================================================================
+// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
+// =================================================================================
+
+package dao
+
+import (
+ "hotgo/internal/dao/internal"
+)
+
+// internalAddonHgexampleTenantOrderDao is internal type for wrapping internal DAO implements.
+type internalAddonHgexampleTenantOrderDao = *internal.AddonHgexampleTenantOrderDao
+
+// addonHgexampleTenantOrderDao is the data access object for table hg_addon_hgexample_tenant_order.
+// You can define custom methods on it to extend its functionality as you wish.
+type addonHgexampleTenantOrderDao struct {
+ internalAddonHgexampleTenantOrderDao
+}
+
+var (
+ // AddonHgexampleTenantOrder is globally public accessible object for table hg_addon_hgexample_tenant_order operations.
+ AddonHgexampleTenantOrder = addonHgexampleTenantOrderDao{
+ internal.NewAddonHgexampleTenantOrderDao(),
+ }
+)
+
+// Fill with you ideas below.
diff --git a/server/internal/dao/admin_dept.go b/server/internal/dao/admin_dept.go
index d684b0f..22cf952 100644
--- a/server/internal/dao/admin_dept.go
+++ b/server/internal/dao/admin_dept.go
@@ -18,7 +18,7 @@ type adminDeptDao struct {
}
var (
- // AdminDept is globally common accessible object for table hg_admin_dept operations.
+ // AdminDept is globally public accessible object for table hg_admin_dept operations.
AdminDept = adminDeptDao{
internal.NewAdminDeptDao(),
}
diff --git a/server/internal/dao/admin_member.go b/server/internal/dao/admin_member.go
index d6d13ef..8c8d581 100644
--- a/server/internal/dao/admin_member.go
+++ b/server/internal/dao/admin_member.go
@@ -18,7 +18,7 @@ type adminMemberDao struct {
}
var (
- // AdminMember is globally common accessible object for table hg_admin_member operations.
+ // AdminMember is globally public accessible object for table hg_admin_member operations.
AdminMember = adminMemberDao{
internal.NewAdminMemberDao(),
}
diff --git a/server/internal/dao/admin_member_post.go b/server/internal/dao/admin_member_post.go
index 9dfa331..51b737c 100644
--- a/server/internal/dao/admin_member_post.go
+++ b/server/internal/dao/admin_member_post.go
@@ -18,7 +18,7 @@ type adminMemberPostDao struct {
}
var (
- // AdminMemberPost is globally common accessible object for table hg_admin_member_post operations.
+ // AdminMemberPost is globally public accessible object for table hg_admin_member_post operations.
AdminMemberPost = adminMemberPostDao{
internal.NewAdminMemberPostDao(),
}
diff --git a/server/internal/dao/admin_member_role.go b/server/internal/dao/admin_member_role.go
index 3966ca5..c762549 100644
--- a/server/internal/dao/admin_member_role.go
+++ b/server/internal/dao/admin_member_role.go
@@ -18,7 +18,7 @@ type adminMemberRoleDao struct {
}
var (
- // AdminMemberRole is globally common accessible object for table hg_admin_member_role operations.
+ // AdminMemberRole is globally public accessible object for table hg_admin_member_role operations.
AdminMemberRole = adminMemberRoleDao{
internal.NewAdminMemberRoleDao(),
}
diff --git a/server/internal/dao/admin_menu.go b/server/internal/dao/admin_menu.go
index 3699ed2..16816db 100644
--- a/server/internal/dao/admin_menu.go
+++ b/server/internal/dao/admin_menu.go
@@ -18,7 +18,7 @@ type adminMenuDao struct {
}
var (
- // AdminMenu is globally common accessible object for table hg_admin_menu operations.
+ // AdminMenu is globally public accessible object for table hg_admin_menu operations.
AdminMenu = adminMenuDao{
internal.NewAdminMenuDao(),
}
diff --git a/server/internal/dao/admin_notice.go b/server/internal/dao/admin_notice.go
index d722b58..399024b 100644
--- a/server/internal/dao/admin_notice.go
+++ b/server/internal/dao/admin_notice.go
@@ -18,7 +18,7 @@ type adminNoticeDao struct {
}
var (
- // AdminNotice is globally common accessible object for table hg_admin_notice operations.
+ // AdminNotice is globally public accessible object for table hg_admin_notice operations.
AdminNotice = adminNoticeDao{
internal.NewAdminNoticeDao(),
}
diff --git a/server/internal/dao/admin_post.go b/server/internal/dao/admin_post.go
index bb8cdc0..56c6735 100644
--- a/server/internal/dao/admin_post.go
+++ b/server/internal/dao/admin_post.go
@@ -18,7 +18,7 @@ type adminPostDao struct {
}
var (
- // AdminPost is globally common accessible object for table hg_admin_post operations.
+ // AdminPost is globally public accessible object for table hg_admin_post operations.
AdminPost = adminPostDao{
internal.NewAdminPostDao(),
}
diff --git a/server/internal/dao/admin_role.go b/server/internal/dao/admin_role.go
index 890b17e..39dbdc9 100644
--- a/server/internal/dao/admin_role.go
+++ b/server/internal/dao/admin_role.go
@@ -18,7 +18,7 @@ type adminRoleDao struct {
}
var (
- // AdminRole is globally common accessible object for table hg_admin_role operations.
+ // AdminRole is globally public accessible object for table hg_admin_role operations.
AdminRole = adminRoleDao{
internal.NewAdminRoleDao(),
}
diff --git a/server/internal/dao/admin_role_menu.go b/server/internal/dao/admin_role_menu.go
index 528ee5e..e967071 100644
--- a/server/internal/dao/admin_role_menu.go
+++ b/server/internal/dao/admin_role_menu.go
@@ -18,7 +18,7 @@ type adminRoleMenuDao struct {
}
var (
- // AdminRoleMenu is globally common accessible object for table hg_admin_role_menu operations.
+ // AdminRoleMenu is globally public accessible object for table hg_admin_role_menu operations.
AdminRoleMenu = adminRoleMenuDao{
internal.NewAdminRoleMenuDao(),
}
diff --git a/server/internal/dao/internal/addon_hgexample_table.go b/server/internal/dao/internal/addon_hgexample_table.go
index 8b7e93e..dd92a93 100644
--- a/server/internal/dao/internal/addon_hgexample_table.go
+++ b/server/internal/dao/internal/addon_hgexample_table.go
@@ -21,6 +21,9 @@ type AddonHgexampleTableDao struct {
// AddonHgexampleTableColumns defines and stores column names for table hg_addon_hgexample_table.
type AddonHgexampleTableColumns struct {
Id string // ID
+ Pid string // 上级ID
+ Level string // 树等级
+ Tree string // 关系树
CategoryId string // 分类ID
Flag string // 标签
Title string // 标题
@@ -47,9 +50,6 @@ type AddonHgexampleTableColumns struct {
Hobby string // 爱好
Channel string // 渠道
CityId string // 所在城市
- Pid string // 上级ID
- Level string // 树等级
- Tree string // 关系树
Remark string // 备注
Status string // 状态
CreatedBy string // 创建者
@@ -62,6 +62,9 @@ type AddonHgexampleTableColumns struct {
// addonHgexampleTableColumns holds the columns for table hg_addon_hgexample_table.
var addonHgexampleTableColumns = AddonHgexampleTableColumns{
Id: "id",
+ Pid: "pid",
+ Level: "level",
+ Tree: "tree",
CategoryId: "category_id",
Flag: "flag",
Title: "title",
@@ -88,9 +91,6 @@ var addonHgexampleTableColumns = AddonHgexampleTableColumns{
Hobby: "hobby",
Channel: "channel",
CityId: "city_id",
- Pid: "pid",
- Level: "level",
- Tree: "tree",
Remark: "remark",
Status: "status",
CreatedBy: "created_by",
diff --git a/server/internal/dao/internal/addon_hgexample_tenant_order.go b/server/internal/dao/internal/addon_hgexample_tenant_order.go
new file mode 100644
index 0000000..84111b9
--- /dev/null
+++ b/server/internal/dao/internal/addon_hgexample_tenant_order.go
@@ -0,0 +1,93 @@
+// ==========================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// ==========================================================================
+
+package internal
+
+import (
+ "context"
+
+ "github.com/gogf/gf/v2/database/gdb"
+ "github.com/gogf/gf/v2/frame/g"
+)
+
+// AddonHgexampleTenantOrderDao is the data access object for table hg_addon_hgexample_tenant_order.
+type AddonHgexampleTenantOrderDao struct {
+ table string // table is the underlying table name of the DAO.
+ group string // group is the database configuration group name of current DAO.
+ columns AddonHgexampleTenantOrderColumns // columns contains all the column names of Table for convenient usage.
+}
+
+// AddonHgexampleTenantOrderColumns defines and stores column names for table hg_addon_hgexample_tenant_order.
+type AddonHgexampleTenantOrderColumns struct {
+ Id string // 主键
+ TenantId string // 租户ID
+ MerchantId string // 商户ID
+ UserId string // 用户ID
+ ProductName string // 购买产品
+ OrderSn string // 订单号
+ Money string // 充值金额
+ Remark string // 备注
+ Status string // 订单状态
+ CreatedAt string // 创建时间
+ UpdatedAt string // 修改时间
+}
+
+// addonHgexampleTenantOrderColumns holds the columns for table hg_addon_hgexample_tenant_order.
+var addonHgexampleTenantOrderColumns = AddonHgexampleTenantOrderColumns{
+ Id: "id",
+ TenantId: "tenant_id",
+ MerchantId: "merchant_id",
+ UserId: "user_id",
+ ProductName: "product_name",
+ OrderSn: "order_sn",
+ Money: "money",
+ Remark: "remark",
+ Status: "status",
+ CreatedAt: "created_at",
+ UpdatedAt: "updated_at",
+}
+
+// NewAddonHgexampleTenantOrderDao creates and returns a new DAO object for table data access.
+func NewAddonHgexampleTenantOrderDao() *AddonHgexampleTenantOrderDao {
+ return &AddonHgexampleTenantOrderDao{
+ group: "default",
+ table: "hg_addon_hgexample_tenant_order",
+ columns: addonHgexampleTenantOrderColumns,
+ }
+}
+
+// DB retrieves and returns the underlying raw database management object of current DAO.
+func (dao *AddonHgexampleTenantOrderDao) DB() gdb.DB {
+ return g.DB(dao.group)
+}
+
+// Table returns the table name of current dao.
+func (dao *AddonHgexampleTenantOrderDao) Table() string {
+ return dao.table
+}
+
+// Columns returns all column names of current dao.
+func (dao *AddonHgexampleTenantOrderDao) Columns() AddonHgexampleTenantOrderColumns {
+ return dao.columns
+}
+
+// Group returns the configuration group name of database of current dao.
+func (dao *AddonHgexampleTenantOrderDao) Group() string {
+ return dao.group
+}
+
+// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
+func (dao *AddonHgexampleTenantOrderDao) Ctx(ctx context.Context) *gdb.Model {
+ return dao.DB().Model(dao.table).Safe().Ctx(ctx)
+}
+
+// Transaction wraps the transaction logic using function f.
+// It rollbacks the transaction and returns the error from function f if it returns non-nil error.
+// It commits the transaction and returns nil if function f returns nil.
+//
+// Note that, you should not Commit or Rollback the transaction in function f
+// as it is automatically handled by this function.
+func (dao *AddonHgexampleTenantOrderDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
+ return dao.Ctx(ctx).Transaction(ctx, f)
+}
diff --git a/server/internal/dao/internal/sys_gen_tree_demo.go b/server/internal/dao/internal/sys_gen_tree_demo.go
new file mode 100644
index 0000000..583f210
--- /dev/null
+++ b/server/internal/dao/internal/sys_gen_tree_demo.go
@@ -0,0 +1,99 @@
+// ==========================================================================
+// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
+// ==========================================================================
+
+package internal
+
+import (
+ "context"
+
+ "github.com/gogf/gf/v2/database/gdb"
+ "github.com/gogf/gf/v2/frame/g"
+)
+
+// SysGenTreeDemoDao is the data access object for table hg_sys_gen_tree_demo.
+type SysGenTreeDemoDao struct {
+ table string // table is the underlying table name of the DAO.
+ group string // group is the database configuration group name of current DAO.
+ columns SysGenTreeDemoColumns // columns contains all the column names of Table for convenient usage.
+}
+
+// SysGenTreeDemoColumns defines and stores column names for table hg_sys_gen_tree_demo.
+type SysGenTreeDemoColumns struct {
+ Id string // ID
+ Pid string // 上级ID
+ Level string // 关系树级别
+ Tree string // 关系树
+ CategoryId string // 分类ID
+ Title string // 标题
+ Description string // 描述
+ Sort string // 排序
+ Status string // 状态
+ CreatedBy string // 创建者
+ UpdatedBy string // 更新者
+ CreatedAt string // 创建时间
+ UpdatedAt string // 修改时间
+ DeletedAt string // 删除时间
+}
+
+// sysGenTreeDemoColumns holds the columns for table hg_sys_gen_tree_demo.
+var sysGenTreeDemoColumns = SysGenTreeDemoColumns{
+ Id: "id",
+ Pid: "pid",
+ Level: "level",
+ Tree: "tree",
+ CategoryId: "category_id",
+ Title: "title",
+ Description: "description",
+ Sort: "sort",
+ Status: "status",
+ CreatedBy: "created_by",
+ UpdatedBy: "updated_by",
+ CreatedAt: "created_at",
+ UpdatedAt: "updated_at",
+ DeletedAt: "deleted_at",
+}
+
+// NewSysGenTreeDemoDao creates and returns a new DAO object for table data access.
+func NewSysGenTreeDemoDao() *SysGenTreeDemoDao {
+ return &SysGenTreeDemoDao{
+ group: "default",
+ table: "hg_sys_gen_tree_demo",
+ columns: sysGenTreeDemoColumns,
+ }
+}
+
+// DB retrieves and returns the underlying raw database management object of current DAO.
+func (dao *SysGenTreeDemoDao) DB() gdb.DB {
+ return g.DB(dao.group)
+}
+
+// Table returns the table name of current dao.
+func (dao *SysGenTreeDemoDao) Table() string {
+ return dao.table
+}
+
+// Columns returns all column names of current dao.
+func (dao *SysGenTreeDemoDao) Columns() SysGenTreeDemoColumns {
+ return dao.columns
+}
+
+// Group returns the configuration group name of database of current dao.
+func (dao *SysGenTreeDemoDao) Group() string {
+ return dao.group
+}
+
+// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
+func (dao *SysGenTreeDemoDao) Ctx(ctx context.Context) *gdb.Model {
+ return dao.DB().Model(dao.table).Safe().Ctx(ctx)
+}
+
+// Transaction wraps the transaction logic using function f.
+// It rollbacks the transaction and returns the error from function f if it returns non-nil error.
+// It commits the transaction and returns nil if function f returns nil.
+//
+// Note that, you should not Commit or Rollback the transaction in function f
+// as it is automatically handled by this function.
+func (dao *SysGenTreeDemoDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
+ return dao.Ctx(ctx).Transaction(ctx, f)
+}
diff --git a/server/internal/dao/internal/sys_login_log.go b/server/internal/dao/internal/sys_login_log.go
index 01ad0fa..5d0cfac 100644
--- a/server/internal/dao/internal/sys_login_log.go
+++ b/server/internal/dao/internal/sys_login_log.go
@@ -20,32 +20,38 @@ type SysLoginLogDao struct {
// SysLoginLogColumns defines and stores column names for table hg_sys_login_log.
type SysLoginLogColumns struct {
- Id string // 日志ID
- ReqId string // 请求ID
- MemberId string // 用户ID
- Username string // 用户名
- Response string // 响应数据
- LoginAt string // 登录时间
- LoginIp string // 登录IP
- ErrMsg string // 错误提示
- Status string // 状态
- CreatedAt string // 创建时间
- UpdatedAt string // 修改时间
+ Id string // 日志ID
+ ReqId string // 请求ID
+ MemberId string // 用户ID
+ Username string // 用户名
+ Response string // 响应数据
+ LoginAt string // 登录时间
+ LoginIp string // 登录IP
+ ProvinceId string // 省编码
+ CityId string // 市编码
+ UserAgent string // UA信息
+ ErrMsg string // 错误提示
+ Status string // 状态
+ CreatedAt string // 创建时间
+ UpdatedAt string // 修改时间
}
// sysLoginLogColumns holds the columns for table hg_sys_login_log.
var sysLoginLogColumns = SysLoginLogColumns{
- Id: "id",
- ReqId: "req_id",
- MemberId: "member_id",
- Username: "username",
- Response: "response",
- LoginAt: "login_at",
- LoginIp: "login_ip",
- ErrMsg: "err_msg",
- Status: "status",
- CreatedAt: "created_at",
- UpdatedAt: "updated_at",
+ Id: "id",
+ ReqId: "req_id",
+ MemberId: "member_id",
+ Username: "username",
+ Response: "response",
+ LoginAt: "login_at",
+ LoginIp: "login_ip",
+ ProvinceId: "province_id",
+ CityId: "city_id",
+ UserAgent: "user_agent",
+ ErrMsg: "err_msg",
+ Status: "status",
+ CreatedAt: "created_at",
+ UpdatedAt: "updated_at",
}
// NewSysLoginLogDao creates and returns a new DAO object for table data access.
diff --git a/server/internal/dao/internal/test_category.go b/server/internal/dao/internal/test_category.go
index 0a76820..d6d895a 100644
--- a/server/internal/dao/internal/test_category.go
+++ b/server/internal/dao/internal/test_category.go
@@ -22,6 +22,7 @@ type TestCategoryDao struct {
type TestCategoryColumns struct {
Id string // 分类ID
Name string // 分类名称
+ ShortName string // 简称
Description string // 描述
Sort string // 排序
Remark string // 备注
@@ -35,6 +36,7 @@ type TestCategoryColumns struct {
var testCategoryColumns = TestCategoryColumns{
Id: "id",
Name: "name",
+ ShortName: "short_name",
Description: "description",
Sort: "sort",
Remark: "remark",
diff --git a/server/internal/dao/sys_config.go b/server/internal/dao/sys_config.go
index d91258f..626c315 100644
--- a/server/internal/dao/sys_config.go
+++ b/server/internal/dao/sys_config.go
@@ -18,7 +18,7 @@ type sysConfigDao struct {
}
var (
- // SysConfig is globally common accessible object for table hg_sys_config operations.
+ // SysConfig is globally public accessible object for table hg_sys_config operations.
SysConfig = sysConfigDao{
internal.NewSysConfigDao(),
}
diff --git a/server/internal/dao/sys_dict_data.go b/server/internal/dao/sys_dict_data.go
index b2c08ef..a6f66db 100644
--- a/server/internal/dao/sys_dict_data.go
+++ b/server/internal/dao/sys_dict_data.go
@@ -18,7 +18,7 @@ type sysDictDataDao struct {
}
var (
- // SysDictData is globally common accessible object for table hg_sys_dict_data operations.
+ // SysDictData is globally public accessible object for table hg_sys_dict_data operations.
SysDictData = sysDictDataDao{
internal.NewSysDictDataDao(),
}
diff --git a/server/internal/dao/sys_dict_type.go b/server/internal/dao/sys_dict_type.go
index 00b691f..42c3432 100644
--- a/server/internal/dao/sys_dict_type.go
+++ b/server/internal/dao/sys_dict_type.go
@@ -18,7 +18,7 @@ type sysDictTypeDao struct {
}
var (
- // SysDictType is globally common accessible object for table hg_sys_dict_type operations.
+ // SysDictType is globally public accessible object for table hg_sys_dict_type operations.
SysDictType = sysDictTypeDao{
internal.NewSysDictTypeDao(),
}
diff --git a/server/internal/dao/sys_gen_tree_demo.go b/server/internal/dao/sys_gen_tree_demo.go
new file mode 100644
index 0000000..e3a9bfb
--- /dev/null
+++ b/server/internal/dao/sys_gen_tree_demo.go
@@ -0,0 +1,27 @@
+// =================================================================================
+// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
+// =================================================================================
+
+package dao
+
+import (
+ "hotgo/internal/dao/internal"
+)
+
+// internalSysGenTreeDemoDao is internal type for wrapping internal DAO implements.
+type internalSysGenTreeDemoDao = *internal.SysGenTreeDemoDao
+
+// sysGenTreeDemoDao is the data access object for table hg_sys_gen_tree_demo.
+// You can define custom methods on it to extend its functionality as you wish.
+type sysGenTreeDemoDao struct {
+ internalSysGenTreeDemoDao
+}
+
+var (
+ // SysGenTreeDemo is globally public accessible object for table hg_sys_gen_tree_demo operations.
+ SysGenTreeDemo = sysGenTreeDemoDao{
+ internal.NewSysGenTreeDemoDao(),
+ }
+)
+
+// Fill with you ideas below.
diff --git a/server/internal/dao/sys_log.go b/server/internal/dao/sys_log.go
index eaaad61..2a427b3 100644
--- a/server/internal/dao/sys_log.go
+++ b/server/internal/dao/sys_log.go
@@ -18,7 +18,7 @@ type sysLogDao struct {
}
var (
- // SysLog is globally common accessible object for table hg_sys_log operations.
+ // SysLog is globally public accessible object for table hg_sys_log operations.
SysLog = sysLogDao{
internal.NewSysLogDao(),
}
diff --git a/server/internal/dao/sys_provinces.go b/server/internal/dao/sys_provinces.go
index ce46858..ce8d18f 100644
--- a/server/internal/dao/sys_provinces.go
+++ b/server/internal/dao/sys_provinces.go
@@ -18,7 +18,7 @@ type sysProvincesDao struct {
}
var (
- // SysProvinces is globally common accessible object for table hg_sys_provinces operations.
+ // SysProvinces is globally public accessible object for table hg_sys_provinces operations.
SysProvinces = sysProvincesDao{
internal.NewSysProvincesDao(),
}
diff --git a/server/internal/global/cluster.go b/server/internal/global/cluster.go
index f1bda98..6a12ca1 100644
--- a/server/internal/global/cluster.go
+++ b/server/internal/global/cluster.go
@@ -13,12 +13,12 @@ import (
"hotgo/internal/library/hgrds/lock"
"hotgo/internal/library/hgrds/pubsub"
"hotgo/internal/service"
+ "hotgo/utility/simple"
)
// SubscribeClusterSync 订阅集群同步,可以用来集中同步数据、状态等
func SubscribeClusterSync(ctx context.Context) {
- isCluster := g.Cfg().MustGet(ctx, "hotgo.isCluster").Bool()
- if !isCluster {
+ if !simple.IsCluster(ctx) {
return
}
@@ -35,8 +35,7 @@ func SubscribeClusterSync(ctx context.Context) {
// PublishClusterSync 推送集群同步消息,如果没有开启集群部署,则不进行推送
func PublishClusterSync(ctx context.Context, channel string, message interface{}) {
- isCluster := g.Cfg().MustGet(ctx, "hotgo.isCluster").Bool()
- if !isCluster {
+ if !simple.IsCluster(ctx) {
return
}
diff --git a/server/internal/global/init.go b/server/internal/global/init.go
index 3071c8b..3666117 100644
--- a/server/internal/global/init.go
+++ b/server/internal/global/init.go
@@ -154,7 +154,7 @@ func InitTrace(ctx context.Context) {
// SetGFMode 设置gf运行模式
func SetGFMode(ctx context.Context) {
- mode := g.Cfg().MustGet(ctx, "hotgo.mode").String()
+ mode := g.Cfg().MustGet(ctx, "system.mode").String()
if len(mode) == 0 {
mode = gmode.NOT_SET
}
diff --git a/server/internal/library/addons/addons.go b/server/internal/library/addons/addons.go
index 417b360..dd5f128 100644
--- a/server/internal/library/addons/addons.go
+++ b/server/internal/library/addons/addons.go
@@ -18,9 +18,9 @@ func GetResourcePath(ctx context.Context) string {
if len(cacheResourcePath) > 0 {
return cacheResourcePath
}
- basePath := g.Cfg().MustGet(ctx, "hotgo.addonsResourcePath").String()
+ basePath := g.Cfg().MustGet(ctx, "system.addonsResourcePath").String()
if basePath == "" {
- g.Log().Warning(ctx, "addons GetResourcePath not config found:'hotgo.addonsResourcePath', use default values:'resource'")
+ g.Log().Warning(ctx, "addons GetResourcePath not config found:'system.addonsResourcePath', use default values:'resource'")
basePath = "resource"
}
diff --git a/server/internal/library/addons/build.go b/server/internal/library/addons/build.go
index adf8caa..6fab466 100644
--- a/server/internal/library/addons/build.go
+++ b/server/internal/library/addons/build.go
@@ -45,7 +45,7 @@ func Build(ctx context.Context, option *BuildOption) (err error) {
)
if resourcePath == "" {
- err = gerror.New("请先设置一个有效的插件资源路径,配置名称:'hotgo.addonsResourcePath'")
+ err = gerror.New("请先设置一个有效的插件资源路径,配置名称:'system.addonsResourcePath'")
return
}
diff --git a/server/internal/library/addons/module.go b/server/internal/library/addons/module.go
index 32d66fb..092887f 100644
--- a/server/internal/library/addons/module.go
+++ b/server/internal/library/addons/module.go
@@ -11,7 +11,6 @@ import (
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gres"
- "github.com/gogf/gf/v2/os/gview"
"hotgo/internal/model/input/form"
"sort"
"sync"
@@ -27,16 +26,15 @@ type Option struct {
// Skeleton 模块骨架
type Skeleton struct {
- Label string `json:"label"` // 标识
- Name string `json:"name"` // 名称
- Group int `json:"group"` // 分组
- Logo string `json:"logo"` // logo
- Brief string `json:"brief"` // 简介
- Description string `json:"description"` // 详细描述
- Author string `json:"author"` // 作者
- Version string `json:"version"` // 版本号
- RootPath string `json:"rootPath"` // 根路径
- View *gview.View `json:"view"` // 模板引擎
+ Label string `json:"label"` // 标识
+ Name string `json:"name"` // 名称
+ Group int `json:"group"` // 分组
+ Logo string `json:"logo"` // logo
+ Brief string `json:"brief"` // 简介
+ Description string `json:"description"` // 详细描述
+ Author string `json:"author"` // 作者
+ Version string `json:"version"` // 版本号
+ RootPath string `json:"rootPath"` // 根路径
}
func (s *Skeleton) GetModule() Module {
@@ -99,7 +97,6 @@ func RegisterModule(m Module) Module {
}
sk.RootPath = GetModulePath(name)
- sk.View = NewView(m.Ctx(), name)
modules[name] = m
return m
}
@@ -139,40 +136,6 @@ func GetModuleRealPath(name string) string {
return path
}
-// NewView 初始化一个插件的模板引擎
-func NewView(ctx context.Context, name string) *gview.View {
- basePath := GetResourcePath(ctx)
- if basePath == "" {
- return nil
- }
-
- view := gview.New()
- path := ViewPath(name, basePath)
-
- if !gfile.IsDir(gfile.RealPath(path)) {
- g.Log().Warningf(ctx, "NewView template path does not exist:%v,default use of main module template.", path)
- return nil
- }
-
- if err := view.SetPath(path); err != nil {
- g.Log().Warningf(ctx, "NewView SetPath err:%+v", err)
- return nil
- }
-
- // 默认和主模块使用一致的变量分隔符号
- delimiters := g.Cfg().MustGet(ctx, "viewer.delimiters", []string{"@{", "}"}).Strings()
- if len(delimiters) != 2 {
- g.Log().Warning(ctx, "NewView delimiters config error")
- return nil
- }
- view.SetDelimiters(delimiters[0], delimiters[1])
-
- // 更多配置
- // view.SetI18n()
- // ...
- return view
-}
-
// AddStaticPath 设置插件静态目录映射
func AddStaticPath(ctx context.Context, server *ghttp.Server) {
basePath := GetResourcePath(ctx)
diff --git a/server/internal/library/captcha/captcha.go b/server/internal/library/captcha/captcha.go
index d914a6d..cdcccdc 100644
--- a/server/internal/library/captcha/captcha.go
+++ b/server/internal/library/captcha/captcha.go
@@ -48,7 +48,7 @@ func Generate(ctx context.Context) (id string, base64 string) {
}
c := base64Captcha.NewCaptcha(driver.ConvertFonts(), base64Captcha.DefaultMemStore)
- id, base64, err := c.Generate()
+ id, base64, _, err := c.Generate()
if err != nil {
g.Log().Errorf(ctx, "captcha.Generate err:%+v", err)
}
diff --git a/server/internal/library/casbin/enforcer.go b/server/internal/library/casbin/enforcer.go
index 7744397..797bf8a 100644
--- a/server/internal/library/casbin/enforcer.go
+++ b/server/internal/library/casbin/enforcer.go
@@ -8,8 +8,11 @@ package casbin
import (
"context"
"github.com/casbin/casbin/v2"
+ "github.com/casbin/casbin/v2/model"
_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
"github.com/gogf/gf/v2/frame/g"
+ "github.com/gogf/gf/v2/os/gfile"
+ "github.com/gogf/gf/v2/os/gres"
"hotgo/internal/consts"
"net/http"
"strings"
@@ -37,12 +40,29 @@ func InitEnforcer(ctx context.Context) {
return
}
- Enforcer, err = casbin.NewEnforcer("./manifest/config/casbin.conf", a)
- if err != nil {
- g.Log().Panicf(ctx, "casbin.NewEnforcer err . %v", err)
- return
+ path := "manifest/config/casbin.conf"
+
+ // 优先从本地加载casbin.conf,如果不存在就从资源文件中找
+ modelContent := gfile.GetContents(path)
+ if len(modelContent) == 0 {
+ if !gres.IsEmpty() && gres.Contains(path) {
+ modelContent = string(gres.GetContent(path))
+ }
}
+ if len(modelContent) == 0 {
+ g.Log().Panicf(ctx, "casbin model file does not exist:%v", path)
+ }
+
+ m, err := model.NewModelFromString(modelContent)
+ if err != nil {
+ g.Log().Panicf(ctx, "casbin NewModelFromString err:%v", err)
+ }
+
+ Enforcer, err = casbin.NewEnforcer(m, a)
+ if err != nil {
+ g.Log().Panicf(ctx, "casbin NewEnforcer err:%v", err)
+ }
loadPermissions(ctx)
}
diff --git a/server/internal/library/contexts/context.go b/server/internal/library/contexts/context.go
index b6606ff..b9cd6bd 100644
--- a/server/internal/library/contexts/context.go
+++ b/server/internal/library/contexts/context.go
@@ -96,6 +96,35 @@ func GetRoleKey(ctx context.Context) string {
return user.RoleKey
}
+// GetDeptType 获取用户部门类型
+func GetDeptType(ctx context.Context) string {
+ user := GetUser(ctx)
+ if user == nil {
+ return ""
+ }
+ return user.DeptType
+}
+
+// IsCompanyDept 是否为公司部门
+func IsCompanyDept(ctx context.Context) bool {
+ return GetDeptType(ctx) == consts.DeptTypeCompany
+}
+
+// IsTenantDept 是否为租户部门
+func IsTenantDept(ctx context.Context) bool {
+ return GetDeptType(ctx) == consts.DeptTypeTenant
+}
+
+// IsMerchantDept 是否为商户部门
+func IsMerchantDept(ctx context.Context) bool {
+ return GetDeptType(ctx) == consts.DeptTypeMerchant
+}
+
+// IsUserDept 是否为普通用户部门
+func IsUserDept(ctx context.Context) bool {
+ return GetDeptType(ctx) == consts.DeptTypeUser
+}
+
// GetModule 获取应用模块
func GetModule(ctx context.Context) string {
c := Get(ctx)
diff --git a/server/internal/library/dict/dict.go b/server/internal/library/dict/dict.go
index 105ab14..0bb04e5 100644
--- a/server/internal/library/dict/dict.go
+++ b/server/internal/library/dict/dict.go
@@ -9,6 +9,7 @@ import (
"context"
"errors"
"fmt"
+ "github.com/gogf/gf/v2/frame/g"
"hash/fnv"
"hotgo/internal/model"
"strconv"
@@ -40,6 +41,7 @@ func GetOptionsById(ctx context.Context, id int64) (opts []*model.Option, err er
}
for _, v := range GetAllFunc() {
+ g.Log().Warningf(ctx, "GetAllFunc GetOptionsById v:%v, %v", v.Id, v.Key)
if v.Id == id {
return LoadFuncOptions(ctx, v)
}
@@ -49,6 +51,24 @@ func GetOptionsById(ctx context.Context, id int64) (opts []*model.Option, err er
return
}
+// GetTypeById 通过类型ID获取内置选项类型
+func GetTypeById(ctx context.Context, id int64) (typ string, err error) {
+ for _, v := range GetAllEnums() {
+ if v.Id == id {
+ return v.Key, nil
+ }
+ }
+
+ for _, v := range GetAllFunc() {
+ if v.Id == id {
+ return v.Key, nil
+ }
+ }
+
+ err = NotExistKeyError
+ return
+}
+
// GenIdHash 生成字典id
func GenIdHash(str string, t int64) int64 {
prefix := 10000 * t
diff --git a/server/internal/library/dict/dict_register_enums.go b/server/internal/library/dict/dict_register_enums.go
index 5518c9c..434698b 100644
--- a/server/internal/library/dict/dict_register_enums.go
+++ b/server/internal/library/dict/dict_register_enums.go
@@ -56,9 +56,7 @@ func RegisterEnums(key, label string, opts []*model.Option) {
func SaveEnums(key, label string, opts []*model.Option) {
eLock.Lock()
defer eLock.Unlock()
- if _, ok := enumsOptions[key]; ok {
- delete(enumsOptions, key)
- }
+ delete(enumsOptions, key)
RegisterEnums(key, label, opts)
}
diff --git a/server/internal/library/dict/dict_register_func.go b/server/internal/library/dict/dict_register_func.go
index fcb8dc5..cf75dcd 100644
--- a/server/internal/library/dict/dict_register_func.go
+++ b/server/internal/library/dict/dict_register_func.go
@@ -67,9 +67,7 @@ func RegisterFunc(key, label string, fun FuncDict, cache ...bool) {
func SaveFunc(key, label string, fun FuncDict, cache ...bool) {
fLock.Lock()
defer fLock.Unlock()
- if _, ok := funcOptions[key]; ok {
- delete(funcOptions, key)
- }
+ delete(funcOptions, key)
RegisterFunc(key, label, fun, cache...)
}
diff --git a/server/internal/library/hggen/hggen.go b/server/internal/library/hggen/hggen.go
index 1ae1543..a42738e 100644
--- a/server/internal/library/hggen/hggen.go
+++ b/server/internal/library/hggen/hggen.go
@@ -6,7 +6,9 @@
package hggen
import (
+ "github.com/gogf/gf/v2/os/gfile"
_ "hotgo/internal/library/hggen/internal/cmd/gendao"
+ "hotgo/internal/library/hggen/internal/utility/utils"
_ "unsafe"
"context"
@@ -31,13 +33,37 @@ func doGenDaoForArray(ctx context.Context, index int, in gendao.CGenDaoInput)
// Dao 生成数据库实体
func Dao(ctx context.Context) (err error) {
+
+ // 在执行gf gen dao时,先将生成文件放在临时路径,生成完成后再拷贝到项目
+ // 目的是希望减少触发gf热编译的几率,防止热编译运行时代码生成流程未结束被自动重启打断
+ // gf gen dao 的执行时长主要取决于需要生成数据库表的数量,表越多速度越慢
+ tempPathPrefix := views.GetTempGeneratePath(ctx) + "/dao"
+
for _, v := range daoConfig {
inp := defaultGenDaoInput
- err = gconv.Scan(v, &inp)
- if err != nil {
+ if err = gconv.Scan(v, &inp); err != nil {
return
}
+ oldPath := inp.Path
+ inp.ImportPrefix = utils.GetImportPath(inp.Path)
+ inp.Path = tempPathPrefix + "/" + inp.Path
+
+ if err = gfile.Remove(inp.Path); err != nil {
+ err = gerror.Newf("清理临时生成目录失败:%v", err)
+ return err
+ }
+
+ if err = gfile.Mkdir(inp.Path); err != nil {
+ err = gerror.Newf("创建临时生成目录失败:%v", err)
+ return err
+ }
+
doGenDaoForArray(ctx, -1, inp)
+
+ if err = gfile.CopyDir(inp.Path, gfile.Pwd()+"/"+oldPath); err != nil {
+ err = gerror.Newf("拷贝生成文件失败:%v", err)
+ return err
+ }
}
return
}
@@ -129,8 +155,16 @@ func TableSelects(ctx context.Context, in *sysin.GenCodesSelectsInp) (res *sysin
Label: v,
})
}
+ for _, v := range views.TableAligns {
+ res.TableAlign = append(res.TableAlign, &form.Select{
+ Value: v,
+ Name: views.TableAlignMap[v],
+ Label: views.TableAlignMap[v],
+ })
+ }
res.Addons = addons.ModuleSelect()
+ res.TreeStyleType = consts.GenCodesTreeStyleTypeOptions
return
}
@@ -196,15 +230,12 @@ func Preview(ctx context.Context, in *sysin.GenCodesPreviewInp) (res *sysin.GenC
}
switch in.GenType {
- case consts.GenCodesTypeCurd:
+ case consts.GenCodesTypeCurd, consts.GenCodesTypeTree:
return views.Curd.DoPreview(ctx, &views.CurdPreviewInput{
In: in,
DaoConfig: GetDaoConfig(in.DbName),
Config: genConfig,
})
- case consts.GenCodesTypeTree:
- err = gerror.Newf("生成类型开发中!")
- return
case consts.GenCodesTypeQueue:
err = gerror.Newf("生成类型开发中!")
return
@@ -222,7 +253,7 @@ func Build(ctx context.Context, in *sysin.GenCodesBuildInp) (err error) {
}
switch in.GenType {
- case consts.GenCodesTypeCurd:
+ case consts.GenCodesTypeCurd, consts.GenCodesTypeTree:
pin := &sysin.GenCodesPreviewInp{SysGenCodes: in.SysGenCodes}
return views.Curd.DoBuild(ctx, &views.CurdBuildInput{
PreviewIn: &views.CurdPreviewInput{
@@ -233,25 +264,17 @@ func Build(ctx context.Context, in *sysin.GenCodesBuildInp) (err error) {
BeforeEvent: views.CurdBuildEvent{"runDao": Dao},
AfterEvent: views.CurdBuildEvent{"runService": func(ctx context.Context) (err error) {
cfg := GetServiceConfig()
- if err = ServiceWithCfg(ctx, cfg); err != nil {
- return
- }
- // 插件模块,同时运行模块下的gen service
+ // 插件模块,切换到插件下运行gen service
if genConfig.Application.Crud.Templates[pin.GenTemplate].IsAddon {
// 依然使用配置中的参数,只是将生成路径指向插件模块路径
cfg.SrcFolder = "addons/" + pin.AddonName + "/logic"
cfg.DstFolder = "addons/" + pin.AddonName + "/service"
- if err = ServiceWithCfg(ctx, cfg); err != nil {
- return
- }
}
+ err = ServiceWithCfg(ctx, cfg)
return
}},
})
- case consts.GenCodesTypeTree:
- err = gerror.Newf("生成类型开发中!")
- return
case consts.GenCodesTypeQueue:
err = gerror.Newf("生成类型开发中!")
return
diff --git a/server/internal/library/hggen/internal/cmd/cmd_init.go b/server/internal/library/hggen/internal/cmd/cmd_init.go
index fc08f81..db93862 100644
--- a/server/internal/library/hggen/internal/cmd/cmd_init.go
+++ b/server/internal/library/hggen/internal/cmd/cmd_init.go
@@ -25,6 +25,7 @@ import (
)
var (
+ // Init .
Init = cInit{}
)
@@ -64,14 +65,13 @@ type cInitInput struct {
Name string `name:"NAME" arg:"true" v:"required" brief:"{cInitNameBrief}"`
Mono bool `name:"mono" short:"m" brief:"initialize a mono-repo instead a single-repo" orphan:"true"`
Update bool `name:"update" short:"u" brief:"update to the latest goframe version" orphan:"true"`
+ Module string `name:"module" short:"g" brief:"custom go module"`
}
type cInitOutput struct{}
func (c cInit) Index(ctx context.Context, in cInitInput) (out *cInitOutput, err error) {
- var (
- overwrote = false
- )
+ var overwrote = false
if !gfile.IsEmpty(in.Name) && !allyes.Check() {
s := gcmd.Scanf(`the folder "%s" is not empty, files might be overwrote, continue? [y/n]: `, in.Name)
if strings.EqualFold(s, "n") {
@@ -105,7 +105,7 @@ func (c cInit) Index(ctx context.Context, in cInitInput) (out *cInitOutput, err
err = gfile.ReadLines(gitignoreFile, func(line string) error {
// Add only hidden files or directories
// If other directories are added, it may cause the entire directory to be ignored
- // such as 'main' in the .gitignore file, but the path is 'D:\main\my-project'
+ // such as 'main' in the .gitignore file, but the path is ' D:\main\my-project '
if line != "" && strings.HasPrefix(line, ".") {
ignoreFiles = append(ignoreFiles, line)
}
@@ -118,6 +118,11 @@ func (c cInit) Index(ctx context.Context, in cInitInput) (out *cInitOutput, err
}
}
+ // Replace module name.
+ if in.Module == "" {
+ in.Module = gfile.Basename(gfile.RealPath(in.Name))
+ }
+
// Replace template name to project name.
err = gfile.ReplaceDirFunc(func(path, content string) string {
for _, ignoreFile := range ignoreFiles {
@@ -125,7 +130,7 @@ func (c cInit) Index(ctx context.Context, in cInitInput) (out *cInitOutput, err
return content
}
}
- return gstr.Replace(gfile.GetContents(path), cInitRepoPrefix+templateRepoName, gfile.Basename(gfile.RealPath(in.Name)))
+ return gstr.Replace(gfile.GetContents(path), cInitRepoPrefix+templateRepoName, in.Module)
}, in.Name, "*", true)
if err != nil {
return
diff --git a/server/internal/library/hggen/internal/cmd/gendao/gendao_structure.go b/server/internal/library/hggen/internal/cmd/gendao/gendao_structure.go
index c4398c8..f6c585e 100644
--- a/server/internal/library/hggen/internal/cmd/gendao/gendao_structure.go
+++ b/server/internal/library/hggen/internal/cmd/gendao/gendao_structure.go
@@ -135,9 +135,14 @@ func generateStructFieldDefinition(
" #" + gstr.CaseCamel(newFiledName),
" #" + localTypeNameStr,
}
- attrLines = append(attrLines, " #"+fmt.Sprintf(tagKey+`json:"%s"`, jsonTag))
- attrLines = append(attrLines, " #"+fmt.Sprintf(`description:"%s"`+tagKey, descriptionTag))
- attrLines = append(attrLines, " #"+fmt.Sprintf(`// %s`, formatComment(field.Comment)))
+ attrLines = append(attrLines, fmt.Sprintf(` #%sjson:"%s"`, tagKey, jsonTag))
+ // orm tag
+ if !in.IsDo {
+ // entity
+ attrLines = append(attrLines, fmt.Sprintf(` #orm:"%s"`, field.Name))
+ }
+ attrLines = append(attrLines, fmt.Sprintf(` #description:"%s"%s`, descriptionTag, tagKey))
+ attrLines = append(attrLines, fmt.Sprintf(` #// %s`, formatComment(field.Comment)))
for k, v := range attrLines {
if in.NoJsonTag {
diff --git a/server/internal/library/hggen/views/column_default.go b/server/internal/library/hggen/views/column_default.go
index e207d6e..040f9c2 100644
--- a/server/internal/library/hggen/views/column_default.go
+++ b/server/internal/library/hggen/views/column_default.go
@@ -181,12 +181,12 @@ func setDefaultFormMode(field *sysin.GenCodesColumnListModel) {
return
}
- if field.GoType == GoTypeString && field.Length >= 200 && field.Length <= 500 {
+ if field.GoType == GoTypeString && field.Length >= 256 && field.Length <= 512 {
field.FormMode = FormModeInputTextarea
return
}
- if field.GoType == GoTypeString && field.Length > 500 {
+ if field.GoType == GoTypeString && field.Length > 512 {
field.FormMode = FormModeInputEditor
return
}
diff --git a/server/internal/library/hggen/views/column_map.go b/server/internal/library/hggen/views/column_map.go
index 43d03da..6e9d28b 100644
--- a/server/internal/library/hggen/views/column_map.go
+++ b/server/internal/library/hggen/views/column_map.go
@@ -3,12 +3,12 @@
// @Copyright Copyright (c) 2023 HotGo CLI
// @Author Ms <133814250@qq.com>
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
-//
package views
import (
"github.com/gogf/gf/v2/text/gstr"
"hotgo/internal/model/input/sysin"
+ "hotgo/utility/validate"
)
// 字段映射关系
@@ -109,6 +109,8 @@ const (
FormModeCheckbox = "Checkbox" // 复选按钮
FormModeSelect = "Select" // 单选下拉框
FormModeSelectMultiple = "SelectMultiple" // 多选下拉框
+ FormModeTreeSelect = "TreeSelect" // 树型选择
+ FormModeCascader = "Cascader" // 级联选择
FormModeUploadImage = "UploadImage" // 单图上传
FormModeUploadImages = "UploadImages" // 多图上传
FormModeUploadFile = "UploadFile" // 单文件上传
@@ -116,12 +118,13 @@ const (
FormModeSwitch = "Switch" // 开关
FormModeRate = "Rate" // 评分
FormModeCitySelector = "CitySelector" // 省市区选择
+ FormModePidTreeSelect = "PidTreeSelect" // 树型上级选择,树表生成专用
)
var FormModes = []string{
FormModeInput, FormModeInputNumber, FormModeInputTextarea, FormModeInputEditor, FormModeInputDynamic,
FormModeDate, FormModeDateRange, FormModeTime, FormModeTimeRange,
- FormModeRadio, FormModeCheckbox, FormModeSelect, FormModeSelectMultiple,
+ FormModeRadio, FormModeCheckbox, FormModeSelect, FormModeSelectMultiple, FormModeTreeSelect, FormModeCascader,
FormModeUploadImage, FormModeUploadImages, FormModeUploadFile, FormModeUploadFiles,
FormModeSwitch,
FormModeRate,
@@ -142,6 +145,8 @@ var FormModeMap = map[string]string{
FormModeCheckbox: "复选按钮",
FormModeSelect: "单选下拉框",
FormModeSelectMultiple: "多选下拉框",
+ FormModeTreeSelect: "树型选择",
+ FormModeCascader: "级联选择",
FormModeUploadImage: "单图上传",
FormModeUploadImages: "多图上传",
FormModeUploadFile: "单文件上传",
@@ -190,20 +195,20 @@ var FormRoleMap = map[string]string{
// 查询条件
const (
- WhereModeEq = "=" // =
- WhereModeNeq = "!=" // !=
- WhereModeGt = ">" // >
- WhereModeGte = ">=" // >=
- WhereModeLt = "<" // <
- WhereModeLte = "<=" // <=
- WhereModeIn = "IN" // IN (...)
- WhereModeNotIn = "NOT IN" // NOT IN (...)
- WhereModeBetween = "BETWEEN" // BETWEEN
- WhereModeNotBetween = "NOT BETWEEN" // NOT BETWEEN
- WhereModeLike = "LIKE" // LIKE
- WhereModeLikeAll = "LIKE %...%" // LIKE %...%
- WhereModeNotLike = "NOT LIKE" // NOT LIKE
- WhereModeJsonContains = "JSON_CONTAINS(json_doc, val)" // JSON_CONTAINS(json_doc, val[, path]) // 判断是否包含某个json值
+ WhereModeEq = "=" // =
+ WhereModeNeq = "!=" // !=
+ WhereModeGt = ">" // >
+ WhereModeGte = ">=" // >=
+ WhereModeLt = "<" // <
+ WhereModeLte = "<=" // <=
+ WhereModeIn = "IN" // IN (...)
+ WhereModeNotIn = "NOT IN" // NOT IN (...)
+ WhereModeBetween = "BETWEEN" // BETWEEN
+ WhereModeNotBetween = "NOT BETWEEN" // NOT BETWEEN
+ WhereModeLike = "LIKE" // LIKE
+ WhereModeLikeAll = "LIKE %...%" // LIKE %...%
+ WhereModeNotLike = "NOT LIKE" // NOT LIKE
+ WhereModeJsonContains = "JSON_CONTAINS(doc, val)" // JSON_CONTAINS(json_doc, val[, path]) // 判断是否包含某个json值
)
var WhereModes = []string{WhereModeEq,
@@ -214,6 +219,21 @@ var WhereModes = []string{WhereModeEq,
WhereModeJsonContains,
}
+// 表格列的排序方式
+const (
+ TableAlignLeft = "left"
+ TableAlignRight = "right"
+ TableAlignCenter = "center"
+)
+
+var TableAligns = []string{TableAlignLeft, TableAlignRight, TableAlignCenter}
+
+var TableAlignMap = map[string]string{
+ TableAlignLeft: "居左",
+ TableAlignRight: "居右",
+ TableAlignCenter: "居中",
+}
+
// IsNumberType 是否是数字类型
func IsNumberType(goType string) bool {
switch goType {
@@ -225,8 +245,17 @@ func IsNumberType(goType string) bool {
return false
}
-func HasColumn(masterFields []*sysin.GenCodesColumnListModel, column string) bool {
- for _, field := range masterFields {
+// IsSelectFormMode 是否是选择器组件
+func IsSelectFormMode(formMode string) bool {
+ switch formMode {
+ case FormModeRadio, FormModeCheckbox, FormModeSelect, FormModeSelectMultiple, FormModeCitySelector, FormModeTreeSelect, FormModeCascader:
+ return true
+ }
+ return false
+}
+
+func HasColumn(fields []*sysin.GenCodesColumnListModel, column string) bool {
+ for _, field := range fields {
if field.GoName == column {
return true
}
@@ -234,29 +263,77 @@ func HasColumn(masterFields []*sysin.GenCodesColumnListModel, column string) boo
return false
}
-func HasColumnWithFormMode(masterFields []*sysin.GenCodesColumnListModel, column string) bool {
- for _, field := range masterFields {
- if field.FormMode == column {
+func HasColumnWithFormMode(fields []*sysin.GenCodesColumnListModel, formMode string) bool {
+ for _, field := range fields {
+ if field.FormMode == formMode {
return true
}
}
return false
}
-func HasMaxSort(masterFields []*sysin.GenCodesColumnListModel) bool {
- return HasColumn(masterFields, "Sort")
+func HasMaxSort(fields []*sysin.GenCodesColumnListModel) bool {
+ return HasColumn(fields, "Sort")
}
-func HasStatus(headOps []string, masterFields []*sysin.GenCodesColumnListModel) bool {
+func HasStatus(headOps []string, fields []*sysin.GenCodesColumnListModel) bool {
if !gstr.InArray(headOps, "status") {
return false
}
- return HasColumn(masterFields, "Status")
+ return HasColumn(fields, "Status")
}
-func HasSwitch(headOps []string, masterFields []*sysin.GenCodesColumnListModel) bool {
- if !gstr.InArray(headOps, "switch") {
- return false
- }
- return HasColumnWithFormMode(masterFields, "Switch")
+func HasSwitch(fields []*sysin.GenCodesColumnListModel) bool {
+ return HasColumnWithFormMode(fields, FormModeSwitch)
+}
+
+func HasHookMemberSummary(fields []*sysin.GenCodesColumnListModel) bool {
+ for _, field := range fields {
+ if IsMemberSummaryField(field.Name) {
+ if field.IsList {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+func HasQueryMemberSummary(fields []*sysin.GenCodesColumnListModel) bool {
+ for _, field := range fields {
+ if IsMemberSummaryField(field.Name) {
+ if field.IsQuery {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+func IsMemberSummaryField(name string) bool {
+ switch name {
+ case "created_by", "updated_by", "deleted_by":
+ return true
+ }
+ return false
+}
+
+// ReviseFields 校正字段值,兼容版本升级前的老数据格式
+func ReviseFields(fields []*sysin.GenCodesColumnListModel) []*sysin.GenCodesColumnListModel {
+ for _, field := range fields {
+ if !validate.InSlice(TableAligns, field.Align) {
+ field.Align = TableAlignLeft
+ }
+
+ if field.Width < 1 {
+ field.Width = -1
+ }
+ if field.Width > 2000 {
+ field.Width = 2000
+ }
+
+ if field.FormGridSpan < 1 {
+ field.FormGridSpan = 1
+ }
+ }
+ return fields
}
diff --git a/server/internal/library/hggen/views/curd.go b/server/internal/library/hggen/views/curd.go
index af590d8..9acde1b 100644
--- a/server/internal/library/hggen/views/curd.go
+++ b/server/internal/library/hggen/views/curd.go
@@ -7,6 +7,7 @@ package views
import (
"context"
+ "github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
@@ -17,12 +18,13 @@ import (
"hotgo/internal/consts"
"hotgo/internal/dao"
"hotgo/internal/library/hggen/internal/cmd/gendao"
- "hotgo/internal/library/hggen/internal/utility/utils"
"hotgo/internal/library/hgorm"
"hotgo/internal/model"
"hotgo/internal/model/input/sysin"
+ "hotgo/internal/service"
"hotgo/utility/convert"
"hotgo/utility/file"
+ "hotgo/utility/tree"
"runtime"
"strings"
)
@@ -32,18 +34,41 @@ var Curd = gCurd{}
type gCurd struct{}
type CurdStep struct {
- HasMaxSort bool `json:"hasMaxSort"`
- HasAdd bool `json:"hasAdd"`
- HasBatchDel bool `json:"hasBatchDel"`
- HasExport bool `json:"hasExport"`
- HasNotFilterAuth bool `json:"hasNotFilterAuth"`
- HasEdit bool `json:"hasEdit"`
- HasDel bool `json:"hasDel"`
- HasView bool `json:"hasView"`
- HasStatus bool `json:"hasStatus"`
- HasSwitch bool `json:"hasSwitch"`
- HasCheck bool `json:"hasCheck"`
- HasMenu bool `json:"hasMenu"`
+ HasMaxSort bool // 最大排序
+ HasAdd bool // 表单添加
+ HasBatchDel bool // 批量删除
+ HasExport bool // 表格导出
+ HasNotFilterAuth bool // 不过滤认证权限
+ HasEdit bool // 表单编辑
+ HasDel bool // 删除
+ HasView bool // 查看详情
+ HasStatus bool // 修改状态
+ HasSwitch bool // 数值开关
+ HasCheck bool // 勾选列
+ HasMenu bool // 菜单权限
+ IsTreeTable bool // 树型列表
+ IsOptionTreeTable bool // 选项式树型列表
+ HasRules bool // 表单验证规则
+ HasRulesValidator bool // 表单验证器
+ HasSearchForm bool // 列表搜索
+ HasDict bool // 字典
+ HasFuncDict bool // 注册方法字典
+ HasQueryMemberSummary bool // 查询用户摘要
+ HasHookMemberSummary bool // hook用户摘要
+ ImportModel ImportModel // 公用导包 - model.ts
+ ActionColumnWidth int64 // 列表操作栏宽度
+ IsAddon bool // 是否是插件
+}
+
+// ImportModel 导包 - model.ts
+type ImportModel struct {
+ NaiveUI []string
+ UtilsIs []string
+ UtilsUrl []string
+ UtilsDate []string
+ UtilsValidate []string
+ UtilsHotGo []string
+ UtilsIndex []string
}
type CurdOptionsJoin struct {
@@ -63,18 +88,52 @@ type CurdOptionsMenu struct {
Sort int `json:"sort"`
}
+type OptionsTree struct {
+ TitleColumn string `json:"titleColumn"`
+ StyleType int `json:"styleType"`
+ TitleField *sysin.GenCodesColumnListModel
+}
+
+// PresetStep 预设生成流程参数
+type PresetStep struct {
+ FormGridCols int `json:"formGridCols" dc:"表单显示的栅格数量"`
+}
+
type CurdOptions struct {
AutoOps []string `json:"autoOps"`
ColumnOps []string `json:"columnOps"`
HeadOps []string `json:"headOps"`
Join []*CurdOptionsJoin `json:"join"`
Menu *CurdOptionsMenu `json:"menu"`
+ Tree *OptionsTree `json:"tree"`
TemplateGroup string `json:"templateGroup"`
ApiPrefix string `json:"apiPrefix"`
+ ImportWebApi string `json:"importWebApi"`
+ FuncDict *FuncDict `json:"funcDict"`
+ PresetStep *PresetStep `json:"presetStep"`
Step *CurdStep // 转换后的流程控制条件
+ DictOps CurdOptionsDict // 字典选项
dictMap g.Map // 字典选项 -> 字段映射关系
}
+type FuncDict struct {
+ ValueColumn string // 选项值
+ LabelColumn string //选项名称
+ Value *sysin.GenCodesColumnListModel
+ Label *sysin.GenCodesColumnListModel
+}
+
+type CurdOptionsDict struct {
+ Has bool
+ Types []string
+ Schemas []*OptionsSchemasField
+}
+
+type OptionsSchemasField struct {
+ Field string
+ Type string
+}
+
type CurdPreviewInput struct {
In *sysin.GenCodesPreviewInp // 提交参数
DaoConfig gendao.CGenDaoInput // 生成dao配置
@@ -98,31 +157,44 @@ func (l *gCurd) initInput(ctx context.Context, in *CurdPreviewInput) (err error)
in.content = new(sysin.GenCodesPreviewModel)
in.content.Views = make(map[string]*sysin.GenFile)
- // 加载主表配置
- if err = in.In.MasterColumns.Scan(&in.masterFields); err != nil {
- return
+ // 初始化生成选项
+ if err = initOptions(in); err != nil {
+ return err
}
- if len(in.masterFields) == 0 {
- if in.masterFields, err = DoTableColumns(ctx, &sysin.GenCodesColumnListInp{Name: in.In.DbName, Table: in.In.TableName}, in.DaoConfig); err != nil {
- return
- }
+ // 初始化表字段配置
+ if err = initTableField(ctx, in); err != nil {
+ return err
}
- // 主键属性
- in.pk = l.getPkField(in)
- if in.pk == nil {
- return gerror.New("initInput no primary key is set in the table!")
- }
-
- // 加载选项
- if err = in.In.Options.Scan(&in.options); err != nil {
- return
+ // 初始化树表
+ if err = initTableTree(in); err != nil {
+ return err
}
initStep(in)
- in.options.dictMap = make(g.Map)
+ // 初始化方法字典
+ if err = initFuncDict(in); err != nil {
+ return err
+ }
+
+ // 初始化生成模板
+ if err = initTemplate(in); err != nil {
+ return err
+ }
+ return
+}
+
+func initOptions(in *CurdPreviewInput) (err error) {
+ if err = in.In.Options.Scan(&in.options); err != nil {
+ return
+ }
+ in.options.dictMap = make(g.Map)
+ return
+}
+
+func initTemplate(in *CurdPreviewInput) (err error) {
if len(in.Config.Application.Crud.Templates)-1 < in.In.GenTemplate {
return gerror.New("没有找到生成模板的配置,请检查!")
}
@@ -141,20 +213,126 @@ func (l *gCurd) initInput(ctx context.Context, in *CurdPreviewInput) (err error)
return
}
+func initFuncDict(in *CurdPreviewInput) (err error) {
+ if !in.options.Step.HasFuncDict || in.options.FuncDict == nil {
+ return
+ }
+
+ if len(in.options.FuncDict.LabelColumn) == 0 || len(in.options.FuncDict.ValueColumn) == 0 {
+ err = gerror.New("生成字典选项必须设置选项值和选项名称")
+ return err
+ }
+
+ for _, field := range in.masterFields {
+ if field.Name == in.options.FuncDict.ValueColumn {
+ in.options.FuncDict.Value = field
+ }
+
+ if field.Name == in.options.FuncDict.LabelColumn {
+ in.options.FuncDict.Label = field
+ }
+ }
+ return
+}
+
+func initTableField(ctx context.Context, in *CurdPreviewInput) (err error) {
+ // 加载主表配置
+ if err = in.In.MasterColumns.Scan(&in.masterFields); err != nil {
+ return
+ }
+
+ if len(in.masterFields) == 0 {
+ if in.masterFields, err = DoTableColumns(ctx, &sysin.GenCodesColumnListInp{Name: in.In.DbName, Table: in.In.TableName}, in.DaoConfig); err != nil {
+ return
+ }
+ }
+
+ // 主键属性
+ in.pk = getPkField(in)
+ if in.pk == nil {
+ return gerror.New("initInput no primary key is set in the table!")
+ }
+
+ in.masterFields = ReviseFields(in.masterFields)
+
+ // 检查表命名
+ var names = []string{in.In.DaoName}
+ for _, v := range in.options.Join {
+ v.Columns = ReviseFields(v.Columns)
+ names = append(names, v.DaoName)
+ }
+ if err = CheckIllegalName("数据库表名", names...); err != nil {
+ return
+ }
+
+ if err = CheckIllegalName("实体命名", in.In.VarName); err != nil {
+ return
+ }
+ return
+}
+
+func initTableTree(in *CurdPreviewInput) (err error) {
+ // 检查树表字段
+ if in.In.GenType == consts.GenCodesTypeTree {
+ if err = CheckTreeTableFields(in.masterFields); err != nil {
+ return err
+ }
+
+ // 解析选项树名称字段
+ has := false
+ for _, field := range in.masterFields {
+ if in.options.Tree.TitleColumn == field.Name {
+ in.options.Tree.TitleField = field
+ has = true
+ break
+ }
+ }
+ if !has {
+ err = gerror.New("请选择一个有效的树名称字段")
+ return
+ }
+ }
+ return err
+}
+
func initStep(in *CurdPreviewInput) {
in.options.Step = new(CurdStep)
in.options.Step.HasMaxSort = HasMaxSort(in.masterFields)
in.options.Step.HasAdd = gstr.InArray(in.options.HeadOps, "add")
- in.options.Step.HasBatchDel = gstr.InArray(in.options.HeadOps, "batchDel")
+ in.options.Step.HasBatchDel = gstr.InArray(in.options.HeadOps, "batchDel") && gstr.InArray(in.options.ColumnOps, "check")
in.options.Step.HasExport = gstr.InArray(in.options.HeadOps, "export")
in.options.Step.HasNotFilterAuth = gstr.InArray(in.options.ColumnOps, "notFilterAuth")
in.options.Step.HasEdit = gstr.InArray(in.options.ColumnOps, "edit")
in.options.Step.HasDel = gstr.InArray(in.options.ColumnOps, "del")
in.options.Step.HasView = gstr.InArray(in.options.ColumnOps, "view")
in.options.Step.HasStatus = HasStatus(in.options.ColumnOps, in.masterFields)
- in.options.Step.HasSwitch = HasSwitch(in.options.ColumnOps, in.masterFields)
+ in.options.Step.HasSwitch = HasSwitch(in.masterFields)
in.options.Step.HasCheck = gstr.InArray(in.options.ColumnOps, "check")
in.options.Step.HasMenu = gstr.InArray(in.options.AutoOps, "genMenuPermissions")
+ in.options.Step.HasQueryMemberSummary = HasQueryMemberSummary(in.masterFields)
+ in.options.Step.HasHookMemberSummary = HasHookMemberSummary(in.masterFields)
+ in.options.Step.IsTreeTable = in.In.GenType == consts.GenCodesTypeTree
+ if in.options.Step.IsTreeTable {
+ in.options.Step.IsOptionTreeTable = in.options.Tree.StyleType == consts.GenCodesTreeStyleTypeOption
+ }
+ in.options.Step.HasFuncDict = gstr.InArray(in.options.AutoOps, "genFuncDict")
+ in.options.Step.IsAddon = in.Config.Application.Crud.Templates[in.In.GenTemplate].IsAddon
+ if in.options.PresetStep.FormGridCols < 1 {
+ in.options.PresetStep.FormGridCols = 1
+ }
+}
+
+// getPkField 获取主键
+func getPkField(in *CurdPreviewInput) *sysin.GenCodesColumnListModel {
+ if len(in.masterFields) == 0 {
+ panic("getPkField masterFields uninitialized.")
+ }
+ for _, field := range in.masterFields {
+ if IsIndexPK(field.Index) {
+ return field
+ }
+ }
+ return nil
}
func (l *gCurd) loadView(ctx context.Context, in *CurdPreviewInput) (err error) {
@@ -170,14 +348,14 @@ func (l *gCurd) loadView(ctx context.Context, in *CurdPreviewInput) (err error)
now := gtime.Now()
view.BindFuncMap(g.Map{
- "NowYear": now.Year, // 当前年
- "ToLower": strings.ToLower, // 全部小写
- "LcFirst": gstr.LcFirst, // 首字母小写
- "UcFirst": gstr.UcFirst, // 首字母大写
+ "NowYear": now.Year, // 当前年
+ "ToLower": strings.ToLower, // 全部小写
+ "LcFirst": gstr.LcFirst, // 首字母小写
+ "UcFirst": gstr.UcFirst, // 首字母大写
+ "ToTSArray": ToTSArray, // 转为ts数组格式
})
- dictOptions, err := l.generateWebModelDictOptions(ctx, in)
- if err != nil {
+ if err = l.generateWebModelDictOptions(ctx, in); err != nil {
return
}
@@ -193,9 +371,9 @@ func (l *gCurd) loadView(ctx context.Context, in *CurdPreviewInput) (err error)
importService = "hotgo/addons/" + in.In.AddonName + "/service"
}
- importWebApi := "@/api/" + gstr.LcFirst(in.In.VarName)
+ in.options.ImportWebApi = "@/api/" + gstr.LcFirst(in.In.VarName)
if temp.IsAddon {
- importWebApi = "@/api/addons/" + in.In.AddonName + "/" + gstr.LcFirst(in.In.VarName)
+ in.options.ImportWebApi = "@/api/addons/" + in.In.AddonName + "/" + gstr.LcFirst(in.In.VarName)
}
componentPrefix := gstr.LcFirst(in.In.VarName)
@@ -216,12 +394,12 @@ func (l *gCurd) loadView(ctx context.Context, in *CurdPreviewInput) (err error)
"masterFields": in.masterFields, // 主表字段
"pk": in.pk, // 主键属性
"options": in.options, // 提交选项
- "dictOptions": dictOptions, // web字典选项
+ "dictOptions": in.options.DictOps, // web字典选项
"importApi": importApi, // 导入goApi包
"importInput": importInput, // 导入input包
"importController": importController, // 导入控制器包
"importService": importService, // 导入业务服务
- "importWebApi": importWebApi, // 导入webApi
+ "importWebApi": in.options.ImportWebApi, // 导入webApi
"apiPrefix": in.options.ApiPrefix, // api前缀
"componentPrefix": componentPrefix, // vue子组件前缀
})
@@ -231,6 +409,7 @@ func (l *gCurd) loadView(ctx context.Context, in *CurdPreviewInput) (err error)
}
func (l *gCurd) DoBuild(ctx context.Context, in *CurdBuildInput) (err error) {
+ st := gtime.Now()
preview, err := l.DoPreview(ctx, in.PreviewIn)
if err != nil {
return
@@ -296,10 +475,6 @@ func (l *gCurd) DoBuild(ctx context.Context, in *CurdBuildInput) (err error) {
if err = gfile.PutContents(vi.Path, strings.TrimSpace(vi.Content)); err != nil {
return gerror.Newf("writing content to '%s' failed: %v", vi.Path, err)
}
-
- if gstr.Str(vi.Path, `.`) == ".go" {
- utils.GoFmt(vi.Path)
- }
}
// 后置操作
@@ -312,6 +487,7 @@ func (l *gCurd) DoBuild(ctx context.Context, in *CurdBuildInput) (err error) {
}
}
}
+ g.Log().Debugf(ctx, "generate code operation completed, %vms", gtime.Now().Sub(st).Milliseconds())
return
}
@@ -386,6 +562,11 @@ func (l *gCurd) generateApiContent(ctx context.Context, in *CurdPreviewInput) (e
return err
}
+ genFile.Content, err = FormatGo(ctx, name, genFile.Content)
+ if err != nil {
+ return err
+ }
+
genFile.Path = file.MergeAbs(in.Config.Application.Crud.Templates[in.In.GenTemplate].ApiPath, strings.ToLower(in.In.VarName), strings.ToLower(in.In.VarName)+".go")
genFile.Meth = consts.GenCodesBuildMethCreate
if gfile.Exists(genFile.Path) {
@@ -417,6 +598,12 @@ func (l *gCurd) generateInputContent(ctx context.Context, in *CurdPreviewInput)
if err != nil {
return err
}
+
+ genFile.Content, err = FormatGo(ctx, name, genFile.Content)
+ if err != nil {
+ return err
+ }
+
genFile.Path = file.MergeAbs(in.Config.Application.Crud.Templates[in.In.GenTemplate].InputPath, convert.CamelCaseToUnderline(in.In.VarName)+".go")
genFile.Meth = consts.GenCodesBuildMethCreate
if gfile.Exists(genFile.Path) {
@@ -443,6 +630,12 @@ func (l *gCurd) generateControllerContent(ctx context.Context, in *CurdPreviewIn
if err != nil {
return err
}
+
+ genFile.Content, err = FormatGo(ctx, name, genFile.Content)
+ if err != nil {
+ return err
+ }
+
genFile.Path = file.MergeAbs(in.Config.Application.Crud.Templates[in.In.GenTemplate].ControllerPath, convert.CamelCaseToUnderline(in.In.VarName)+".go")
genFile.Meth = consts.GenCodesBuildMethCreate
if gfile.Exists(genFile.Path) {
@@ -473,6 +666,12 @@ func (l *gCurd) generateLogicContent(ctx context.Context, in *CurdPreviewInput)
if err != nil {
return err
}
+
+ genFile.Content, err = FormatGo(ctx, name, genFile.Content)
+ if err != nil {
+ return err
+ }
+
genFile.Path = file.MergeAbs(in.Config.Application.Crud.Templates[in.In.GenTemplate].LogicPath, convert.CamelCaseToUnderline(in.In.VarName)+".go")
genFile.Meth = consts.GenCodesBuildMethCreate
if gfile.Exists(genFile.Path) {
@@ -499,6 +698,11 @@ func (l *gCurd) generateRouterContent(ctx context.Context, in *CurdPreviewInput)
return err
}
+ genFile.Content, err = FormatGo(ctx, name, genFile.Content)
+ if err != nil {
+ return err
+ }
+
genFile.Path = file.MergeAbs(in.Config.Application.Crud.Templates[in.In.GenTemplate].RouterPath, convert.CamelCaseToUnderline(in.In.VarName)+".go")
genFile.Meth = consts.GenCodesBuildMethCreate
if gfile.Exists(genFile.Path) {
@@ -525,6 +729,8 @@ func (l *gCurd) generateWebApiContent(ctx context.Context, in *CurdPreviewInput)
return err
}
+ genFile.Content = FormatTs(genFile.Content)
+
genFile.Path = file.MergeAbs(in.Config.Application.Crud.Templates[in.In.GenTemplate].WebApiPath, gstr.LcFirst(in.In.VarName), "index.ts")
genFile.Meth = consts.GenCodesBuildMethCreate
if gfile.Exists(genFile.Path) {
@@ -556,6 +762,8 @@ func (l *gCurd) generateWebModelContent(ctx context.Context, in *CurdPreviewInpu
return
}
+ genFile.Content = FormatTs(genFile.Content)
+
genFile.Path = file.MergeAbs(in.Config.Application.Crud.Templates[in.In.GenTemplate].WebViewsPath, gstr.LcFirst(in.In.VarName), "model.ts")
genFile.Meth = consts.GenCodesBuildMethCreate
if gfile.Exists(genFile.Path) {
@@ -586,6 +794,8 @@ func (l *gCurd) generateWebIndexContent(ctx context.Context, in *CurdPreviewInpu
return err
}
+ genFile.Content = FormatVue(genFile.Content)
+
genFile.Path = file.MergeAbs(in.Config.Application.Crud.Templates[in.In.GenTemplate].WebViewsPath, gstr.LcFirst(in.In.VarName), "index.vue")
genFile.Meth = consts.GenCodesBuildMethCreate
if gfile.Exists(genFile.Path) {
@@ -616,6 +826,8 @@ func (l *gCurd) generateWebEditContent(ctx context.Context, in *CurdPreviewInput
return err
}
+ genFile.Content = FormatVue(genFile.Content)
+
genFile.Path = file.MergeAbs(in.Config.Application.Crud.Templates[in.In.GenTemplate].WebViewsPath, gstr.LcFirst(in.In.VarName), "edit.vue")
genFile.Meth = consts.GenCodesBuildMethCreate
if gfile.Exists(genFile.Path) {
@@ -651,6 +863,8 @@ func (l *gCurd) generateWebViewContent(ctx context.Context, in *CurdPreviewInput
return err
}
+ genFile.Content = FormatVue(genFile.Content)
+
genFile.Path = file.MergeAbs(in.Config.Application.Crud.Templates[in.In.GenTemplate].WebViewsPath, gstr.LcFirst(in.In.VarName), "view.vue")
genFile.Meth = consts.GenCodesBuildMethCreate
if gfile.Exists(genFile.Path) {
@@ -683,6 +897,11 @@ func (l *gCurd) generateSqlContent(ctx context.Context, in *CurdPreviewInput) (e
genFile = new(sysin.GenFile)
)
+ menus, err := service.AdminMenu().GetFastList(ctx)
+ if err != nil {
+ return err
+ }
+
tplData["dirPid"], tplData["dirLevel"], tplData["dirTree"], err = hgorm.AutoUpdateTree(ctx, &dao.AdminMenu, 0, int64(in.options.Menu.Pid))
if err != nil {
return err
@@ -692,9 +911,30 @@ func (l *gCurd) generateSqlContent(ctx context.Context, in *CurdPreviewInput) (e
tplData["btnLevel"] = tplData["dirLevel"].(int) + 2
tplData["sortLevel"] = tplData["dirLevel"].(int) + 3
+ pageRedirect := ""
if in.options.Menu.Pid > 0 {
tplData["mainComponent"] = "ParentLayout"
+ menu, ok := menus[int64(in.options.Menu.Pid)]
+ if !ok {
+ err = gerror.New("选择的上级菜单不存在")
+ return
+ }
+ for _, id := range tree.GetIds(menu.Tree) {
+ if v, ok2 := menus[id]; ok2 {
+ if !gstr.HasSuffix(pageRedirect, "/") && !gstr.HasPrefix(v.Path, "/") {
+ pageRedirect += "/"
+ }
+ pageRedirect += v.Path
+ }
+ }
+
+ if !gstr.HasSuffix(pageRedirect, "/") && !gstr.HasPrefix(menu.Path, "/") {
+ pageRedirect += "/"
+ }
+ pageRedirect += menu.Path
}
+ pageRedirect += "/" + gstr.LcFirst(in.In.VarName) + "/index"
+ tplData["pageRedirect"] = pageRedirect
genFile.Path = file.MergeAbs(in.Config.Application.Crud.Templates[in.In.GenTemplate].SqlPath, convert.CamelCaseToUnderline(in.In.VarName)+"_menu.sql")
genFile.Meth = consts.GenCodesBuildMethCreate
@@ -708,6 +948,48 @@ func (l *gCurd) generateSqlContent(ctx context.Context, in *CurdPreviewInput) (e
genFile.Required = false
}
+ // 需要生成时,检查菜单命名是否存在
+ if genFile.Meth == consts.GenCodesBuildMethCreate {
+ menuNamePrefix := gstr.LcFirst(in.In.VarName)
+ menuNames := []string{menuNamePrefix, menuNamePrefix + "Index"}
+ if in.options.Step.HasEdit {
+ menuNames = append(menuNames, menuNamePrefix+"Edit")
+ menuNames = append(menuNames, menuNamePrefix+"View")
+ }
+ if in.options.Step.HasView {
+ menuNames = append(menuNames, menuNamePrefix+"View")
+ }
+ if in.options.Step.HasMaxSort {
+ menuNames = append(menuNames, menuNamePrefix+"MaxSort")
+ }
+ if in.options.Step.HasDel {
+ menuNames = append(menuNames, menuNamePrefix+"Delete")
+ }
+ if in.options.Step.HasStatus {
+ menuNames = append(menuNames, menuNamePrefix+"Status")
+ }
+ if in.options.Step.HasSwitch {
+ menuNames = append(menuNames, menuNamePrefix+"Switch")
+ }
+ if in.options.Step.HasExport {
+ menuNames = append(menuNames, menuNamePrefix+"Export")
+ }
+ if in.options.Step.IsTreeTable {
+ menuNames = append(menuNames, menuNamePrefix+"TreeOption")
+ }
+
+ menuNames = convert.UniqueSlice(menuNames)
+ hasMenus, err := service.AdminMenu().Model(ctx).Fields("name").WhereIn("name", menuNames).Array()
+ if err != nil {
+ return err
+ }
+
+ if len(hasMenus) > 0 {
+ err = gerror.Newf("要生成的菜单中有已存在的路由别名,请检查并删除:%v", strings.Join(gvar.New(hasMenus).Strings(), `、`))
+ return err
+ }
+ }
+
tplData["generatePath"] = genFile.Path
genFile.Content, err = in.view.Parse(ctx, name+".template", tplData)
if err != nil {
diff --git a/server/internal/library/hggen/views/curd_generate_input.go b/server/internal/library/hggen/views/curd_generate_input.go
index 1d98abd..2861b0c 100644
--- a/server/internal/library/hggen/views/curd_generate_input.go
+++ b/server/internal/library/hggen/views/curd_generate_input.go
@@ -28,6 +28,7 @@ const (
InputTypeEditInpValidator = 4 // 添加&编辑验证器
InputTypeUpdateFields = 5 // 编辑修改过滤字段
InputTypeInsertFields = 6 // 编辑新增过滤字段
+ InputTypeTreeOptionFields = 7 // 关系树查询字段
EditInpValidatorGenerally = "if err := g.Validator().Rules(\"%s\").Data(in.%s).Messages(\"%s\").Run(ctx); err != nil {\n\t\treturn err.Current()\n\t}\n"
)
@@ -39,21 +40,79 @@ func (l *gCurd) inputTplData(ctx context.Context, in *CurdPreviewInput) (data g.
data["editInpValidator"] = l.generateInputListColumns(ctx, in, InputTypeEditInpValidator)
data["updateFieldsColumns"] = l.generateInputListColumns(ctx, in, InputTypeUpdateFields)
data["insertFieldsColumns"] = l.generateInputListColumns(ctx, in, InputTypeInsertFields)
+ data["viewModelColumns"] = l.generateInputViewColumns(ctx, in)
+ if in.options.Step.IsTreeTable {
+ data["treeOptionFields"] = l.generateInputListColumns(ctx, in, InputTypeTreeOptionFields)
+ }
return
}
+func (l *gCurd) generateInputViewColumns(ctx context.Context, in *CurdPreviewInput) string {
+ buffer := bytes.NewBuffer(nil)
+
+ index := 0
+ array := make([][]string, 1000)
+ // 主表
+ for _, field := range in.masterFields {
+ // 查询用户摘要
+ if field.IsList && in.options.Step.HasHookMemberSummary && IsMemberSummaryField(field.Name) {
+ tagKey := "`"
+ descriptionTag := gstr.Replace(formatComment(field.Dc)+"摘要信息", `"`, `\"`)
+ result := []string{" #" + field.GoName + "Summa"}
+ result = append(result, " #*hook.MemberSumma")
+ result = append(result, " #"+fmt.Sprintf(tagKey+`json:"%s"`, field.TsName+"Summa"))
+ result = append(result, " #"+fmt.Sprintf(`dc:"%s"`+tagKey, descriptionTag))
+ array[index] = result
+ index++
+ }
+ }
+
+ tw := tablewriter.NewWriter(buffer)
+ tw.SetBorder(false)
+ tw.SetRowLine(false)
+ tw.SetAutoWrapText(false)
+ tw.SetColumnSeparator("")
+ tw.AppendBulk(array)
+ tw.Render()
+ stContent := buffer.String()
+ // Let's do this hack of table writer for indent!
+ stContent = gstr.Replace(stContent, " #", "")
+ stContent = gstr.Replace(stContent, "` ", "`")
+ stContent = gstr.Replace(stContent, "``", "")
+ stContent = removeEndWrap(stContent)
+
+ buffer.Reset()
+ buffer.WriteString(stContent)
+ return "\tentity." + in.In.DaoName + "\n" + buffer.String()
+}
+
func (l *gCurd) generateInputListColumns(ctx context.Context, in *CurdPreviewInput, inputType int) string {
buffer := bytes.NewBuffer(nil)
index := 0
array := make([][]string, 1000)
// 主表
for _, field := range in.masterFields {
- row := l.generateStructFieldDefinition(field, inputType)
+ row := l.generateStructFieldDefinition(in, field, inputType, true)
if row == nil {
continue
}
array[index] = row
index++
+
+ switch inputType {
+ case InputTypeListModel:
+ // 查询用户摘要
+ if field.IsList && in.options.Step.HasHookMemberSummary && IsMemberSummaryField(field.Name) {
+ tagKey := "`"
+ descriptionTag := gstr.Replace(formatComment(field.Dc)+"摘要信息", `"`, `\"`)
+ result := []string{" #" + field.GoName + "Summa"}
+ result = append(result, " #*hook.MemberSumma")
+ result = append(result, " #"+fmt.Sprintf(tagKey+`json:"%s"`, field.TsName+"Summa"))
+ result = append(result, " #"+fmt.Sprintf(`dc:"%s"`+tagKey, descriptionTag))
+ array[index] = result
+ index++
+ }
+ }
}
// 关联表
@@ -63,7 +122,7 @@ func (l *gCurd) generateInputListColumns(ctx context.Context, in *CurdPreviewInp
continue
}
for _, field := range v.Columns {
- row := l.generateStructFieldDefinition(field, inputType)
+ row := l.generateStructFieldDefinition(in, field, inputType, false)
if row != nil {
array[index] = row
index++
@@ -92,43 +151,62 @@ func (l *gCurd) generateInputListColumns(ctx context.Context, in *CurdPreviewInp
}
// generateStructFieldForModel generates and returns the attribute definition for specified field.
-func (l *gCurd) generateStructFieldDefinition(field *sysin.GenCodesColumnListModel, inputType int) []string {
+func (l *gCurd) generateStructFieldDefinition(in *CurdPreviewInput, field *sysin.GenCodesColumnListModel, inputType int, isMaster bool) []string {
var (
tagKey = "`"
result = []string{" #" + field.GoName}
descriptionTag = gstr.Replace(formatComment(field.Dc), `"`, `\"`)
)
+ addResult := func() []string {
+ result = append(result, " #"+field.GoType)
+ result = append(result, " #"+fmt.Sprintf(tagKey+`json:"%s"`, field.TsName))
+ result = append(result, " #"+fmt.Sprintf(`dc:"%s"`+tagKey, descriptionTag))
+ return result
+ }
+
+ isQuery := false
+
switch inputType {
case InputTypeListInp:
- if !field.IsQuery {
+ if in.options.Step.IsTreeTable && IsPidName(field.Name) {
+ isQuery = true
+ field.QueryWhere = WhereModeEq
+ }
+ if !field.IsQuery && !isQuery {
return nil
}
if field.QueryWhere == WhereModeBetween {
result = append(result, " #[]"+field.GoType)
} else {
- result = append(result, " #"+field.GoType)
+ // 查询用户摘要时,固定接收字符串类型
+ if field.IsQuery && in.options.Step.HasQueryMemberSummary && IsMemberSummaryField(field.Name) {
+ result = append(result, " #string")
+ } else {
+ result = append(result, " #"+field.GoType)
+ }
}
result = append(result, " #"+fmt.Sprintf(tagKey+`json:"%s"`, field.TsName))
result = append(result, " #"+fmt.Sprintf(`dc:"%s"`+tagKey, descriptionTag))
case InputTypeListModel:
- if !field.IsList {
+ // 主表的主键
+ if IsIndexPK(field.Index) && isMaster {
+ addResult()
+ // 树表的pid字段
+ } else if in.options.Step.IsTreeTable && IsPidName(field.Name) {
+ addResult()
+ } else if field.IsList {
+ addResult()
+ } else {
return nil
}
-
- result = append(result, " #"+field.GoType)
- result = append(result, " #"+fmt.Sprintf(tagKey+`json:"%s"`, field.TsName))
- result = append(result, " #"+fmt.Sprintf(`dc:"%s"`+tagKey, descriptionTag))
case InputTypeExportModel:
if !field.IsExport {
return nil
}
-
- result = append(result, " #"+field.GoType)
- result = append(result, " #"+fmt.Sprintf(tagKey+`json:"%s"`, field.TsName))
- result = append(result, " #"+fmt.Sprintf(`dc:"%s"`+tagKey, descriptionTag))
+ addResult()
case InputTypeEditInpValidator:
if !field.IsEdit {
return nil
@@ -150,18 +228,23 @@ func (l *gCurd) generateStructFieldDefinition(field *sysin.GenCodesColumnListMod
if !field.IsEdit && field.GoName != "UpdatedBy" {
return nil
}
-
- result = append(result, " #"+field.GoType)
- result = append(result, " #"+fmt.Sprintf(tagKey+`json:"%s"`, field.TsName))
- result = append(result, " #"+fmt.Sprintf(`dc:"%s"`+tagKey, descriptionTag))
+ addResult()
case InputTypeInsertFields:
if !field.IsEdit && field.GoName != "CreatedBy" {
return nil
}
-
- result = append(result, " #"+field.GoType)
- result = append(result, " #"+fmt.Sprintf(tagKey+`json:"%s"`, field.TsName))
- result = append(result, " #"+fmt.Sprintf(`dc:"%s"`+tagKey, descriptionTag))
+ addResult()
+ case InputTypeTreeOptionFields:
+ if IsIndexPK(field.Index) {
+ return addResult()
+ }
+ if IsPidName(field.Name) {
+ return addResult()
+ }
+ if in.options.Tree.TitleColumn == field.Name {
+ return addResult()
+ }
+ return nil
default:
panic("inputType is invalid")
}
diff --git a/server/internal/library/hggen/views/curd_generate_logic.go b/server/internal/library/hggen/views/curd_generate_logic.go
index fd889d9..08db2de 100644
--- a/server/internal/library/hggen/views/curd_generate_logic.go
+++ b/server/internal/library/hggen/views/curd_generate_logic.go
@@ -16,22 +16,20 @@ import (
)
const (
- LogicWhereComments = "\n\t// 查询%s\n"
- LogicWhereNoSupport = "\t// TODO 暂不支持生成[ %s ]查询方式,请自行补充此处代码!"
- LogicListSimpleSelect = "\tfields, err := hgorm.GenSelect(ctx, sysin.%sListModel{}, dao.%s)\n\tif err != nil {\n\t\treturn\n\t}"
- LogicListJoinSelect = "\t// 关联表select\n\tfields, err := hgorm.GenJoinSelect(ctx, %sin.%sListModel{}, &dao.%s, []*hgorm.Join{\n%v\t})\n\n\tif err != nil {\n\t\terr = gerror.Wrap(err, \"获取%s关联字段失败,请稍后重试!\")\n\t\treturn\n\t}"
- LogicListJoinOnRelation = "\t// 关联表%s\n\tmod = mod.%s(hgorm.GenJoinOnRelation(\n\t\tdao.%s.Table(), dao.%s.Columns().%s, // 主表表名,关联字段\n\t\tdao.%s.Table(), \"%s\", dao.%s.Columns().%s, // 关联表表名,别名,关联字段\n\t)...)\n\n"
- LogicEditUpdate = "\tif _, err = s.Model(ctx%s).\n\t\t\tFields(%sin.%sUpdateFields{}).\n\t\t\tWherePri(in.%s).Data(in).Update(); err != nil {\n\t\t\terr = gerror.Wrap(err, \"修改%s失败,请稍后重试!\")\n\t\t}\n\t\treturn"
- LogicEditInsert = "\tif _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).\n\t\tFields(%sin.%sInsertFields{}).\n\t\tData(in).Insert(); err != nil {\n\t\terr = gerror.Wrap(err, \"新增%s失败,请稍后重试!\")\n\t}"
- LogicEditUnique = "\t// 验证'%s'唯一\n\tif err = hgorm.IsUnique(ctx, &dao.%s, g.Map{dao.%s.Columns().%s: in.%s}, \"%s已存在\", in.Id); err != nil {\n\t\treturn\n\t}\n"
- LogicSwitchUpdate = "g.Map{\n\t\tin.Key: in.Value,\n%s}"
- LogicStatusUpdate = "g.Map{\n\t\tdao.%s.Columns().Status: in.Status,\n%s}"
+ LogicWhereComments = "\n\t// 查询%s\n"
+ LogicWhereNoSupport = "\t// TODO 暂不支持生成[ %s ]查询方式,请自行补充此处代码!"
+ LogicEditUpdate = "\tif _, err = s.Model(ctx%s).\n\t\t\tFields(%sin.%sUpdateFields{}).\n\t\t\tWherePri(in.%s).Data(in).Update(); err != nil {\n\t\t\terr = gerror.Wrap(err, \"修改%s失败,请稍后重试!\")\n\t\t}\n\t\treturn"
+ LogicEditInsert = "\tif _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).\n\t\tFields(%sin.%sInsertFields{}).\n\t\tData(in).Insert(); err != nil {\n\t\terr = gerror.Wrap(err, \"新增%s失败,请稍后重试!\")\n\t}"
+ LogicEditUnique = "\t// 验证'%s'唯一\n\tif err = hgorm.IsUnique(ctx, &dao.%s, g.Map{dao.%s.Columns().%s: in.%s}, \"%s已存在\", in.Id); err != nil {\n\t\treturn\n\t}\n"
+ LogicSwitchUpdate = "g.Map{\n\t\tin.Key: in.Value,\n%s}"
+ LogicStatusUpdate = "g.Map{\n\t\tdao.%s.Columns().Status: in.Status,\n%s}"
)
func (l *gCurd) logicTplData(ctx context.Context, in *CurdPreviewInput) (data g.Map, err error) {
data = make(g.Map)
data["listWhere"] = l.generateLogicListWhere(ctx, in)
data["listJoin"] = l.generateLogicListJoin(ctx, in)
+ data["listFields"] = l.generateLogicListFields(ctx, in)
data["listOrder"] = l.generateLogicListOrder(ctx, in)
data["edit"] = l.generateLogicEdit(ctx, in)
data["switchFields"] = l.generateLogicSwitchFields(ctx, in)
@@ -113,76 +111,109 @@ func (l *gCurd) generateLogicEdit(ctx context.Context, in *CurdPreviewInput) g.M
}
func (l *gCurd) generateLogicListOrder(ctx context.Context, in *CurdPreviewInput) string {
+ statement := ""
+ if hasEffectiveJoins(in.options.Join) {
+ statement = "dao." + in.In.DaoName + ".Table() + \".\" +"
+ }
buffer := bytes.NewBuffer(nil)
if in.options.Step.HasMaxSort {
- buffer.WriteString("OrderAsc(dao." + in.In.DaoName + ".Columns().Sort).")
+ buffer.WriteString("OrderAsc(" + statement + "dao." + in.In.DaoName + ".Columns().Sort).")
}
- buffer.WriteString("OrderDesc(dao." + in.In.DaoName + ".Columns()." + in.pk.GoName + ")")
+ buffer.WriteString("OrderDesc(" + statement + "dao." + in.In.DaoName + ".Columns()." + in.pk.GoName + ")")
return buffer.String()
}
-func (l *gCurd) generateLogicListJoin(ctx context.Context, in *CurdPreviewInput) g.Map {
- var data = make(g.Map)
- data["link"] = ""
+func (l *gCurd) generateLogicListJoin(ctx context.Context, in *CurdPreviewInput) (link string) {
+ connector := `"="`
if hasEffectiveJoins(in.options.Join) {
- var (
- selectBuffer = bytes.NewBuffer(nil)
- linkBuffer = bytes.NewBuffer(nil)
- joinSelectRows string
- )
-
+ linkBuffer := bytes.NewBuffer(nil)
for _, join := range in.options.Join {
if isEffectiveJoin(join) {
- joinSelectRows = joinSelectRows + fmt.Sprintf("\t\t{Dao: &dao.%s, Alias: \"%s\"},\n", join.DaoName, join.Alias)
- linkBuffer.WriteString(fmt.Sprintf(LogicListJoinOnRelation, join.Alias, consts.GenCodesJoinLinkMap[join.LinkMode], in.In.DaoName, in.In.DaoName, gstr.CaseCamel(join.MasterField), join.DaoName, join.Alias, join.DaoName, gstr.CaseCamel(join.Field)))
+ linkBuffer.WriteString("\tmod = mod." + consts.GenCodesJoinLinkMap[join.LinkMode] + "OnFields(dao." + join.DaoName + ".Table(), dao." + in.In.DaoName + ".Columns()." + gstr.CaseCamel(join.MasterField) + "," + connector + ", dao." + join.DaoName + ".Columns()." + gstr.CaseCamel(join.Field) + ")\n")
}
}
-
- selectBuffer.WriteString(fmt.Sprintf(LogicListJoinSelect, in.options.TemplateGroup, in.In.VarName, in.In.DaoName, joinSelectRows, in.In.TableComment))
-
- data["select"] = selectBuffer.String()
- data["fields"] = "fields"
- data["link"] = linkBuffer.String()
- } else {
- data["fields"] = fmt.Sprintf("%sin.%sListModel{}", in.options.TemplateGroup, in.In.VarName)
+ link = linkBuffer.String()
}
- return data
+ return
+}
+
+func (l *gCurd) generateLogicListFields(ctx context.Context, in *CurdPreviewInput) (fields string) {
+ selectBuffer := bytes.NewBuffer(nil)
+ if hasEffectiveJoins(in.options.Join) {
+ selectBuffer.WriteString("mod = mod.FieldsPrefix(dao." + in.In.DaoName + ".Table(), " + in.options.TemplateGroup + "in." + in.In.VarName + "ListModel{})\n")
+ for _, join := range in.options.Join {
+ if isEffectiveJoin(join) {
+ selectBuffer.WriteString("mod = mod.Fields(hgorm.JoinFields(ctx, " + in.options.TemplateGroup + "in." + in.In.VarName + "ListModel{}, &dao." + join.DaoName + ", \"" + join.Alias + "\"))\n")
+ }
+ }
+ fields = selectBuffer.String()
+ } else {
+ fields = fmt.Sprintf("mod = mod.Fields(%sin.%sListModel{})", in.options.TemplateGroup, in.In.VarName)
+ }
+ return
}
func (l *gCurd) generateLogicListWhere(ctx context.Context, in *CurdPreviewInput) string {
buffer := bytes.NewBuffer(nil)
// 主表
- l.generateLogicListWhereEach(buffer, in.masterFields, in.In.DaoName, "")
+ l.generateLogicListWhereEach(buffer, in, in.masterFields, in.In.DaoName, "")
// 关联表
if hasEffectiveJoins(in.options.Join) {
for _, v := range in.options.Join {
if isEffectiveJoin(v) {
- l.generateLogicListWhereEach(buffer, v.Columns, v.DaoName, v.Alias)
+ l.generateLogicListWhereEach(buffer, in, v.Columns, v.DaoName, v.Alias)
}
}
}
return buffer.String()
}
-func (l *gCurd) generateLogicListWhereEach(buffer *bytes.Buffer, fields []*sysin.GenCodesColumnListModel, daoName string, alias string) {
+func (l *gCurd) generateLogicListWhereEach(buffer *bytes.Buffer, in *CurdPreviewInput, fields []*sysin.GenCodesColumnListModel, daoName string, alias string) {
isLink := false
if alias != "" {
alias = `"` + alias + `."+`
isLink = true
}
+
+ tablePrefix := ""
+ wherePrefix := "Where"
+ if isLink {
+ wherePrefix = "WherePrefix"
+ tablePrefix = "dao." + daoName + ".Table(), "
+ }
+
for _, field := range fields {
- if !field.IsQuery || field.QueryWhere == "" {
+ isQuery := false
+ // 树表查询上级
+ if in.options.Step.IsTreeTable && IsPidName(field.Name) {
+ isQuery = true
+ field.QueryWhere = WhereModeEq
+ }
+
+ if (!field.IsQuery && !isQuery) || field.QueryWhere == "" {
continue
}
+ buffer.WriteString(fmt.Sprintf(LogicWhereComments, field.Dc))
+
var (
linkMode string
whereTag string
columnName string
)
+ // 查询用户摘要
+ if field.IsQuery && in.options.Step.HasQueryMemberSummary && IsMemberSummaryField(field.Name) {
+ servicePackName := "service"
+ if in.options.Step.IsAddon {
+ servicePackName = "isc"
+ }
+ buffer.WriteString(fmt.Sprintf("if in.%v != \"\" {\n\t\t\t\tids, err := %v.AdminMember().GetIdsByKeyword(ctx, in.%v)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, 0, err\n\t\t\t\t}\n\t\t\t\tmod = mod.WhereIn(dao.%v.Columns().%v, ids)\n\t\t\t}\n", field.GoName, servicePackName, field.GoName, in.In.DaoName, field.GoName))
+ continue
+ }
+
if IsNumberType(field.GoType) {
linkMode = `in.` + field.GoName + ` > 0`
} else if field.GoType == GoTypeGTime {
@@ -197,8 +228,6 @@ func (l *gCurd) generateLogicListWhereEach(buffer *bytes.Buffer, fields []*sysin
linkMode = `len(in.` + field.GoName + `) == 2`
}
- buffer.WriteString(fmt.Sprintf(LogicWhereComments, field.Dc))
-
// 如果是关联表重新转换字段
columnName = field.GoName
if isLink {
@@ -207,35 +236,35 @@ func (l *gCurd) generateLogicListWhereEach(buffer *bytes.Buffer, fields []*sysin
switch field.QueryWhere {
case WhereModeEq:
- whereTag = "\tif " + linkMode + " {\n\t\tmod = mod.Where(" + alias + "dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")\n\t}"
+ whereTag = "\tif " + linkMode + " {\n\t\tmod = mod." + wherePrefix + "(" + tablePrefix + "dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")\n\t}"
case WhereModeNeq:
- whereTag = "\tif " + linkMode + " {\n\t\tmod = mod.WhereNot(dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")\n\t}"
+ whereTag = "\tif " + linkMode + " {\n\t\tmod = mod." + wherePrefix + "Not(" + tablePrefix + "dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")\n\t}"
case WhereModeGt:
- whereTag = "\tif " + linkMode + " {\n\t\tmod = mod.WhereGT(dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")\n\t}"
+ whereTag = "\tif " + linkMode + " {\n\t\tmod = mod." + wherePrefix + "GT(" + tablePrefix + "dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")\n\t}"
case WhereModeGte:
- whereTag = "\tif " + linkMode + " {\n\t\tmod = mod.WhereGTE(dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")\n\t}"
+ whereTag = "\tif " + linkMode + " {\n\t\tmod = mod." + wherePrefix + "GTE(" + tablePrefix + "dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")\n\t}"
case WhereModeLt:
- whereTag = "\tif " + linkMode + " {\n\t\tmod = mod.WhereLT(dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")\n\t}"
+ whereTag = "\tif " + linkMode + " {\n\t\tmod = mod." + wherePrefix + "LT(" + tablePrefix + "dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")\n\t}"
case WhereModeLte:
- whereTag = "\tif " + linkMode + " {\n\t\tmod = mod.WhereLTE(dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")\n\t}"
+ whereTag = "\tif " + linkMode + " {\n\t\tmod = mod." + wherePrefix + "LTE(" + tablePrefix + "dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")\n\t}"
case WhereModeIn:
- whereTag = "\tif " + linkMode + " {\n\t\tmod = mod.WhereIn(dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")\n\t}"
+ whereTag = "\tif " + linkMode + " {\n\t\tmod = mod." + wherePrefix + "In(" + tablePrefix + "dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")\n\t}"
case WhereModeNotIn:
- whereTag = "\tif " + linkMode + " {\n\t\tmod = mod.WhereNotIn(dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")\n\t}"
+ whereTag = "\tif " + linkMode + " {\n\t\tmod = mod." + wherePrefix + "NotIn(" + tablePrefix + "dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")\n\t}"
case WhereModeBetween:
- whereTag = "\tif " + linkMode + " {\n\t\tmod = mod.WhereBetween(dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + "[0], in." + field.GoName + "[1])\n\t}"
+ whereTag = "\tif " + linkMode + " {\n\t\tmod = mod." + wherePrefix + "Between(" + tablePrefix + "dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + "[0], in." + field.GoName + "[1])\n\t}"
case WhereModeNotBetween:
- whereTag = "\tif " + linkMode + " {\n\t\tmod = mod.WhereNotBetween(dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + "[0], in." + field.GoName + "[1])\n\t}"
+ whereTag = "\tif " + linkMode + " {\n\t\tmod = mod." + wherePrefix + "NotBetween(" + tablePrefix + "dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + "[0], in." + field.GoName + "[1])\n\t}"
case WhereModeLike:
- whereTag = "\tif " + linkMode + " {\n\t\tmod = mod.WhereLike(dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")\n\t}"
+ whereTag = "\tif " + linkMode + " {\n\t\tmod = mod." + wherePrefix + "Like(" + tablePrefix + "dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")\n\t}"
case WhereModeLikeAll:
val := `"%"+in.` + field.GoName + `+"%"`
- whereTag = "\tif " + linkMode + " {\n\t\tmod = mod.WhereLike(dao." + daoName + ".Columns()." + columnName + ", " + val + ")\n\t}"
+ whereTag = "\tif " + linkMode + " {\n\t\tmod = mod." + wherePrefix + "Like(" + tablePrefix + "dao." + daoName + ".Columns()." + columnName + ", " + val + ")\n\t}"
case WhereModeNotLike:
- whereTag = "\tif " + linkMode + " {\n\t\tmod = mod.WhereNotLike(dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")\n\t}"
+ whereTag = "\tif " + linkMode + " {\n\t\tmod = mod." + wherePrefix + "NotLike(" + tablePrefix + "dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")\n\t}"
case WhereModeJsonContains:
- val := "fmt.Sprintf(`JSON_CONTAINS(%s,'%v')`, dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")"
- whereTag = "\tif in." + field.GoName + linkMode + " {\n\t\tmod = mod.Where(" + val + ")\n\t}"
+ val := tablePrefix + "fmt.Sprintf(`JSON_CONTAINS(%s,'%v')`, dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")"
+ whereTag = "\tif in." + field.GoName + linkMode + " {\n\t\tmod = mod." + wherePrefix + "(" + val + ")\n\t}"
default:
buffer.WriteString(fmt.Sprintf(LogicWhereNoSupport, field.QueryWhere))
diff --git a/server/internal/library/hggen/views/curd_generate_web_edit.go b/server/internal/library/hggen/views/curd_generate_web_edit.go
index fc6cb72..dd4ceb9 100644
--- a/server/internal/library/hggen/views/curd_generate_web_edit.go
+++ b/server/internal/library/hggen/views/curd_generate_web_edit.go
@@ -22,7 +22,7 @@ func (l *gCurd) webEditTplData(ctx context.Context, in *CurdPreviewInput) (data
func (l *gCurd) generateWebEditFormItem(ctx context.Context, in *CurdPreviewInput) string {
buffer := bytes.NewBuffer(nil)
- for k, field := range in.masterFields {
+ for _, field := range in.masterFields {
if !field.IsEdit {
continue
}
@@ -36,6 +36,10 @@ func (l *gCurd) generateWebEditFormItem(ctx context.Context, in *CurdPreviewInpu
component string
)
+ if in.options.Step.IsTreeTable && IsPidName(field.Name) {
+ field.FormMode = FormModePidTreeSelect
+ }
+
switch field.FormMode {
case FormModeInput:
component = defaultComponent
@@ -98,16 +102,45 @@ func (l *gCurd) generateWebEditFormItem(ctx context.Context, in *CurdPreviewInpu
case FormModeCitySelector:
component = fmt.Sprintf("\n \n ", field.Dc, field.TsName, field.TsName)
-
+ case FormModePidTreeSelect:
+ component = fmt.Sprintf(`
+
+ `, field.Dc, in.pk.TsName, in.options.Tree.TitleField.TsName)
+ case FormModeTreeSelect:
+ component = fmt.Sprintf(`
+
+ `, field.Dc, field.TsName, field.Dc, field.TsName)
+ case FormModeCascader:
+ component = fmt.Sprintf(`
+
+ `, field.Dc, field.TsName, field.Dc, field.TsName)
default:
component = defaultComponent
}
- if len(in.masterFields) == k {
- buffer.WriteString(" " + component)
- } else {
- buffer.WriteString(" " + component + "\n\n")
- }
+ buffer.WriteString(fmt.Sprintf("%v\n\n", field.FormGridSpan, component))
}
return buffer.String()
}
@@ -119,23 +152,25 @@ func (l *gCurd) generateWebEditScript(ctx context.Context, in *CurdPreviewInput)
setupBuffer = bytes.NewBuffer(nil)
)
+ importBuffer.WriteString(" import { ref, computed } from 'vue';\n")
+
+ // 导入api
+ var importApiMethod = []string{"Edit", "View"}
if in.options.Step.HasMaxSort {
- importBuffer.WriteString(" import { ref } from 'vue';\n")
- if in.Config.Application.Crud.Templates[in.In.GenTemplate].IsAddon {
- importBuffer.WriteString(" import { Edit, MaxSort, View } from '@/api/addons/" + in.In.AddonName + "/" + gstr.LcFirst(in.In.VarName) + "';\n")
- } else {
- importBuffer.WriteString(" import { Edit, MaxSort, View } from '@/api/" + gstr.LcFirst(in.In.VarName) + "';\n")
- }
- setupBuffer.WriteString(" function openModal(state: State) {\n adaModalWidth(dialogWidth);\n showModal.value = true;\n loading.value = true;\n\n // 新增\n if (!state || state.id < 1) {\n formValue.value = newState(state);\n MaxSort()\n .then((res) => {\n formValue.value.sort = res.sort;\n })\n .finally(() => {\n loading.value = false;\n });\n return;\n }\n\n // 编辑\n View({ id: state.id })\n .then((res) => {\n formValue.value = res;\n })\n .finally(() => {\n loading.value = false;\n });\n }")
- } else {
- importBuffer.WriteString(" import { ref } from 'vue';\n")
- if in.Config.Application.Crud.Templates[in.In.GenTemplate].IsAddon {
- importBuffer.WriteString(" import { Edit, View } from '@/api/addons/" + in.In.AddonName + "/" + gstr.LcFirst(in.In.VarName) + "';\n")
- } else {
- importBuffer.WriteString(" import { Edit, View } from '@/api/" + gstr.LcFirst(in.In.VarName) + "';\n")
- }
- setupBuffer.WriteString(" function openModal(state: State) {\n adaModalWidth(dialogWidth);\n showModal.value = true;\n loading.value = true;\n\n // 新增\n if (!state || state.id < 1) {\n formValue.value = newState(state);\n return;\n }\n\n // 编辑\n View({ id: state.id })\n .then((res) => {\n formValue.value = res;\n })\n .finally(() => {\n loading.value = false;\n });\n }")
+ importApiMethod = append(importApiMethod, "MaxSort")
}
+ importBuffer.WriteString(" import " + ImportWebMethod(importApiMethod) + " from '" + in.options.ImportWebApi + "';\n")
+
+ // 导入model
+ var importModelMethod = []string{"options", "State", "newState"}
+ if in.options.Step.IsTreeTable {
+ importModelMethod = append(importModelMethod, []string{"treeOption", "loadTreeOption"}...)
+ }
+
+ if in.options.Step.HasRules {
+ importModelMethod = append(importModelMethod, "rules")
+ }
+ importBuffer.WriteString(" import " + ImportWebMethod(importModelMethod) + " from './model';\n")
for _, field := range in.masterFields {
if !field.IsEdit {
diff --git a/server/internal/library/hggen/views/curd_generate_web_index.go b/server/internal/library/hggen/views/curd_generate_web_index.go
index 4b6c262..6674765 100644
--- a/server/internal/library/hggen/views/curd_generate_web_index.go
+++ b/server/internal/library/hggen/views/curd_generate_web_index.go
@@ -6,59 +6,135 @@
package views
import (
+ "bytes"
"context"
- "fmt"
-
"github.com/gogf/gf/v2/frame/g"
- "github.com/gogf/gf/v2/text/gstr"
-)
-
-const (
- IndexApiImport = " import {%v } from '@/api/%s';" // 这里将导入的包路径写死了,后面可以优化成根据配置动态读取
- IndexApiAddonsImport = " import {%v } from '@/api/addons/%s/%s';"
- IndexIconsImport = " import {%v } from '@vicons/antd';"
)
func (l *gCurd) webIndexTplData(ctx context.Context, in *CurdPreviewInput) (g.Map, error) {
var (
- data = make(g.Map)
- apiImport = []string{" List"}
- iconsImport []string
+ data = make(g.Map)
+ importBuffer = bytes.NewBuffer(nil)
+ importVueMethod = []string{"h", "reactive", "ref", "computed"}
+ importApiMethod = []string{"List"}
+ importModelMethod = []string{"columns", "schemas"}
+ importUtilsMethod = []string{"adaTableScrollX"}
+ importIcons []string
+ actionWidth int64 = 72
)
// 添加
if in.options.Step.HasAdd {
- iconsImport = append(iconsImport, " PlusOutlined")
+ importIcons = append(importIcons, "PlusOutlined")
}
// 编辑
- // if in.options.Step.HasEdit {
- // }
+ if in.options.Step.HasEdit {
+ in.options.Step.ActionColumnWidth += actionWidth
+ if in.options.Step.IsTreeTable && !in.options.Step.IsOptionTreeTable {
+ in.options.Step.ActionColumnWidth += actionWidth
+ }
+ if in.options.Step.IsOptionTreeTable {
+ importIcons = append(importIcons, "EditOutlined")
+ }
+ }
// 导出
if in.options.Step.HasExport {
- iconsImport = append(iconsImport, " ExportOutlined")
- apiImport = append(apiImport, " Export")
+ importIcons = append(importIcons, "ExportOutlined")
+ importApiMethod = append(importApiMethod, "Export")
}
// 删除
- if in.options.Step.HasDel || in.options.Step.HasBatchDel {
- iconsImport = append(iconsImport, " DeleteOutlined")
- apiImport = append(apiImport, " Delete")
+ if in.options.Step.HasDel {
+ importApiMethod = append(importApiMethod, "Delete")
+ in.options.Step.ActionColumnWidth += actionWidth
}
- // 导出
+ // 批量删除
+ if in.options.Step.HasBatchDel {
+ importIcons = append(importIcons, "DeleteOutlined")
+ importApiMethod = append(importApiMethod, "Delete")
+ }
+
+ // 修改状态
if in.options.Step.HasStatus {
- apiImport = append(apiImport, " Status")
+ importApiMethod = append(importApiMethod, "Status")
+ importUtilsMethod = append(importUtilsMethod, "getOptionLabel")
+ importModelMethod = append(importModelMethod, "options")
+ in.options.Step.ActionColumnWidth += actionWidth
}
- if in.Config.Application.Crud.Templates[in.In.GenTemplate].IsAddon {
- data["apiImport"] = fmt.Sprintf(IndexApiAddonsImport, gstr.Implode(",", apiImport), in.In.AddonName, gstr.LcFirst(in.In.VarName))
- } else {
- data["apiImport"] = fmt.Sprintf(IndexApiImport, gstr.Implode(",", apiImport), gstr.LcFirst(in.In.VarName))
+ // 更多
+ // 查看详情
+ if in.options.Step.HasView {
+ in.options.Step.ActionColumnWidth += actionWidth
}
- if len(iconsImport) > 0 {
- data["iconsImport"] = fmt.Sprintf(IndexIconsImport, gstr.Implode(",", iconsImport))
+
+ // 展开树
+ if in.options.Step.IsTreeTable {
+ importIcons = append(importIcons, "AlignLeftOutlined")
+ }
+
+ // 存在字典数据选项
+ if in.options.DictOps.Has {
+ importVueMethod = append(importVueMethod, "onMounted")
+ importModelMethod = append(importModelMethod, "loadOptions")
+ }
+
+ // 普通树表
+ if in.options.Step.IsTreeTable && !in.options.Step.IsOptionTreeTable {
+ importUtilsMethod = append(importUtilsMethod, "convertListToTree")
+ }
+
+ // 选项式树表
+ if in.options.Step.IsOptionTreeTable {
+ importVueMethod = append(importVueMethod, []string{"onMounted", "unref"}...)
+ importIcons = append(importIcons, []string{"FormOutlined", "SearchOutlined"}...)
+ importApiMethod = append(importApiMethod, "TreeOption")
+ importUtilsMethod = append(importUtilsMethod, "getTreeKeys")
+ importModelMethod = append(importModelMethod, []string{"loadTreeOption", "treeOption", "State"}...)
+ }
+
+ // 操作按钮宽度最小值
+ if in.options.Step.ActionColumnWidth > 0 && in.options.Step.ActionColumnWidth < actionWidth*2 {
+ in.options.Step.ActionColumnWidth = 100
+ }
+
+ // 导入基础包
+ importBuffer.WriteString(" import " + ImportWebMethod(importVueMethod) + " from 'vue';\n")
+ importBuffer.WriteString(" import { useDialog, useMessage } from 'naive-ui';\n")
+ importBuffer.WriteString(" import { BasicTable, TableAction } from '@/components/Table';\n")
+ importBuffer.WriteString(" import { BasicForm, useForm } from '@/components/Form/index';\n")
+ importBuffer.WriteString(" import { usePermission } from '@/hooks/web/usePermission';\n")
+
+ // 导入api
+ importBuffer.WriteString(" import " + ImportWebMethod(importApiMethod) + " from '" + in.options.ImportWebApi + "';\n")
+
+ // 导入icons
+ if len(importIcons) > 0 {
+ importBuffer.WriteString(" import " + ImportWebMethod(importIcons) + " from '@vicons/antd';\n")
+ }
+
+ // 导入model
+ if in.options.Step.IsTreeTable {
+ importModelMethod = append(importModelMethod, "newState")
+ }
+ importBuffer.WriteString(" import " + ImportWebMethod(importModelMethod) + " from './model';\n")
+
+ // 导入utils
+ if len(importUtilsMethod) > 0 {
+ importBuffer.WriteString(" import " + ImportWebMethod(importUtilsMethod) + " from '@/utils/hotgo';\n")
+ }
+
+ // 导入edit组件
+ if in.options.Step.HasEdit {
+ importBuffer.WriteString(" import Edit from './edit.vue';\n")
+ }
+
+ // 导入view组件
+ if in.options.Step.HasView {
+ importBuffer.WriteString(" import View from './view.vue';\n")
}
// 没有需要查询的字段则隐藏搜索表单
@@ -83,5 +159,6 @@ func (l *gCurd) webIndexTplData(ctx context.Context, in *CurdPreviewInput) (g.Ma
}
}
data["isSearchForm"] = isSearchForm
+ data["import"] = importBuffer.String()
return data, nil
}
diff --git a/server/internal/library/hggen/views/curd_generate_web_model.go b/server/internal/library/hggen/views/curd_generate_web_model.go
index 33b87e5..6331050 100644
--- a/server/internal/library/hggen/views/curd_generate_web_model.go
+++ b/server/internal/library/hggen/views/curd_generate_web_model.go
@@ -12,61 +12,148 @@ import (
"fmt"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
+ "github.com/gogf/gf/v2/util/gconv"
"hotgo/internal/library/dict"
"hotgo/internal/model/input/sysin"
"hotgo/utility/convert"
)
-const (
- ModelLoadOptionsTemplate = "async function loadOptions() {\n options.value = await Dicts({\n types: [\n %v ],\n });\n for (const item of schemas.value) {\n switch (item.field) {\n%v }\n }\n}\n\nawait loadOptions();"
-)
+type StateItem struct {
+ Name string
+ DefaultValue interface{}
+ Dc string
+}
func (l *gCurd) webModelTplData(ctx context.Context, in *CurdPreviewInput) (data g.Map, err error) {
data = make(g.Map)
- data["state"] = l.generateWebModelState(ctx, in)
+ data["stateItems"] = l.generateWebModelStateItems(ctx, in)
data["rules"] = l.generateWebModelRules(ctx, in)
data["formSchema"] = l.generateWebModelFormSchema(ctx, in)
if data["columns"], err = l.generateWebModelColumns(ctx, in); err != nil {
return nil, err
}
+
+ // 根据表单生成情况,按需导包
+ data["import"] = l.generateWebModelImport(ctx, in)
return
}
-func (l *gCurd) generateWebModelState(ctx context.Context, in *CurdPreviewInput) string {
- buffer := bytes.NewBuffer(nil)
- buffer.WriteString("export class State {\n")
+func (l *gCurd) generateWebModelImport(ctx context.Context, in *CurdPreviewInput) string {
+ importBuffer := bytes.NewBuffer(nil)
+
+ importBuffer.WriteString("import { h, ref } from 'vue';\n")
+
+ // 导入基础组件
+ if len(in.options.Step.ImportModel.NaiveUI) > 0 {
+ importBuffer.WriteString("import " + ImportWebMethod(in.options.Step.ImportModel.NaiveUI) + " from 'naive-ui';\n")
+ }
+
+ importBuffer.WriteString("import { cloneDeep } from 'lodash-es';\n")
+
+ // 导入表单搜索
+ if in.options.Step.HasSearchForm {
+ importBuffer.WriteString("import { FormSchema } from '@/components/Form';\n")
+ }
+
+ // 导入字典选项
+ if in.options.DictOps.Has {
+ importBuffer.WriteString("import { Dicts } from '@/api/dict/dict';\n")
+ }
+
+ // 导入工具类
+ if len(in.options.Step.ImportModel.UtilsIs) > 0 {
+ importBuffer.WriteString("import " + ImportWebMethod(in.options.Step.ImportModel.UtilsIs) + " from '@/utils/is';\n")
+ }
+
+ if len(in.options.Step.ImportModel.UtilsUrl) > 0 {
+ importBuffer.WriteString("import " + ImportWebMethod(in.options.Step.ImportModel.UtilsUrl) + " from '@/utils/urlUtils';\n")
+ }
+
+ if len(in.options.Step.ImportModel.UtilsDate) > 0 {
+ importBuffer.WriteString("import " + ImportWebMethod(in.options.Step.ImportModel.UtilsDate) + " from '@/utils/dateUtil';\n")
+ }
+
+ if in.options.Step.HasRulesValidator {
+ importBuffer.WriteString("import { validate } from '@/utils/validateUtil';\n")
+ }
+
+ if len(in.options.Step.ImportModel.UtilsHotGo) > 0 {
+ importBuffer.WriteString("import " + ImportWebMethod(in.options.Step.ImportModel.UtilsHotGo) + " from '@/utils/hotgo';\n")
+ }
+
+ if len(in.options.Step.ImportModel.UtilsIndex) > 0 {
+ importBuffer.WriteString("import " + ImportWebMethod(in.options.Step.ImportModel.UtilsIndex) + " from '@/utils';\n")
+ }
+
+ // 导入api
+ var importApiMethod []string
+ if in.options.Step.HasSwitch {
+ importApiMethod = append(importApiMethod, "Switch")
+ }
+ if in.options.Step.IsTreeTable {
+ importApiMethod = append(importApiMethod, "TreeOption")
+ }
+ if len(importApiMethod) > 0 {
+ importBuffer.WriteString("import " + ImportWebMethod(importApiMethod) + " from '" + in.options.ImportWebApi + "';\n")
+ }
+
+ if in.options.Step.HasSwitch {
+ importBuffer.WriteString("import { usePermission } from '@/hooks/web/usePermission';\n")
+ importBuffer.WriteString("const { hasPermission } = usePermission();\n")
+ importBuffer.WriteString("const $message = window['$message'];\n")
+ }
+ return importBuffer.String()
+}
+
+func (l *gCurd) generateWebModelStateItems(ctx context.Context, in *CurdPreviewInput) (items []*StateItem) {
for _, field := range in.masterFields {
var value = field.DefaultValue
if value == nil {
value = "null"
}
if value == "" {
- value = "''"
+ value = `''`
}
+
+ // 选项组件默认值调整
+ if gconv.Int(value) == 0 && IsSelectFormMode(field.FormMode) {
+ value = "null"
+ }
+
if field.Name == "status" {
value = 1
}
- if field.FormMode == "Switch" {
+ if field.FormMode == FormModeSwitch {
value = 2
}
- if field.FormMode == "InputDynamic" {
+ if field.FormMode == FormModeInputDynamic {
value = "[]"
}
- buffer.WriteString(fmt.Sprintf(" public %s = %v; // %s\n", field.TsName, value, field.Dc))
+ items = append(items, &StateItem{
+ Name: field.TsName,
+ DefaultValue: value,
+ Dc: field.Dc,
+ })
+
+ // 查询用户摘要
+ if field.IsList && in.options.Step.HasHookMemberSummary && IsMemberSummaryField(field.Name) {
+ items = append(items, &StateItem{
+ Name: field.TsName + "Summa?: null | MemberSumma",
+ DefaultValue: "null",
+ Dc: field.Dc + "摘要信息",
+ })
+ }
}
- buffer.WriteString("\n constructor(state?: Partial) {\n if (state) {\n Object.assign(this, state);\n }\n }")
- buffer.WriteString("}")
- return buffer.String()
+ return
}
-func (l *gCurd) generateWebModelDictOptions(ctx context.Context, in *CurdPreviewInput) (g.Map, error) {
+func (l *gCurd) generateWebModelDictOptions(ctx context.Context, in *CurdPreviewInput) error {
type DictType struct {
Id int64 `json:"id"`
Type string `json:"type"`
}
var (
- options = make(g.Map)
dictTypeIds []int64
dictTypeList []*DictType
builtinDictTypeIds []int64
@@ -87,8 +174,7 @@ func (l *gCurd) generateWebModelDictOptions(ctx context.Context, in *CurdPreview
builtinDictTypeIds = convert.UniqueSlice(builtinDictTypeIds)
if len(dictTypeIds) == 0 && len(builtinDictTypeIds) == 0 {
- options["has"] = false
- return options, nil
+ return nil
}
if len(dictTypeIds) > 0 {
@@ -97,71 +183,52 @@ func (l *gCurd) generateWebModelDictOptions(ctx context.Context, in *CurdPreview
WhereIn("id", dictTypeIds).
Scan(&dictTypeList)
if err != nil {
- return nil, err
+ return err
}
}
if len(builtinDictTypeIds) > 0 {
for _, id := range builtinDictTypeIds {
- opts, err := dict.GetOptionsById(ctx, id)
+ typ, err := dict.GetTypeById(ctx, id)
if err != nil && !errors.Is(err, dict.NotExistKeyError) {
- return nil, err
+ return err
}
-
- if len(opts) > 0 {
+ if len(typ) > 0 {
row := new(DictType)
row.Id = id
- row.Type = opts[0].Type
+ row.Type = typ
builtinDictTypeList = append(builtinDictTypeList, row)
}
}
}
if len(dictTypeList) == 0 && len(builtinDictTypeList) == 0 {
- options["has"] = false
- return options, nil
+ return nil
}
if len(builtinDictTypeList) > 0 {
dictTypeList = append(dictTypeList, builtinDictTypeList...)
}
- options["has"] = true
+ in.options.DictOps.Has = true
- var (
- awaitLoadOptions string
- switchLoadOptions string
- )
-
- interfaceOptionsBuffer := bytes.NewBuffer(nil)
- interfaceOptionsBuffer.WriteString("export interface IOptions extends Options {\n")
- constOptionsBuffer := bytes.NewBuffer(nil)
- constOptionsBuffer.WriteString("export const options = ref({\n")
+ // 导入选项包
+ in.options.Step.ImportModel.UtilsHotGo = append(in.options.Step.ImportModel.UtilsHotGo, "Option")
for _, v := range dictTypeList {
// 字段映射字典
for _, field := range in.masterFields {
if field.DictType != 0 && v.Id == field.DictType {
in.options.dictMap[field.TsName] = v.Type
- switchLoadOptions = fmt.Sprintf("%s case '%s':\n item.componentProps.options = options.value.%s;\n break;\n", switchLoadOptions, field.TsName, v.Type)
+ in.options.DictOps.Schemas = append(in.options.DictOps.Schemas, &OptionsSchemasField{
+ Field: field.TsName,
+ Type: v.Type,
+ })
}
}
-
- awaitLoadOptions = fmt.Sprintf("%s '%s',\n", awaitLoadOptions, v.Type)
- interfaceOptionsBuffer.WriteString(" " + v.Type + ": Option[]; \n")
- constOptionsBuffer.WriteString(" " + v.Type + ": [],\n")
+ in.options.DictOps.Types = append(in.options.DictOps.Types, v.Type)
}
-
- interfaceOptionsBuffer.WriteString("};\n")
- constOptionsBuffer.WriteString("});\n")
-
- loadOptionsBuffer := bytes.NewBuffer(nil)
- loadOptionsBuffer.WriteString(fmt.Sprintf(ModelLoadOptionsTemplate, awaitLoadOptions, switchLoadOptions))
-
- options["interface"] = interfaceOptionsBuffer.String()
- options["const"] = constOptionsBuffer.String()
- options["load"] = loadOptionsBuffer.String()
- return options, nil
+ return nil
}
func (l *gCurd) generateWebModelRules(ctx context.Context, in *CurdPreviewInput) string {
@@ -172,9 +239,11 @@ func (l *gCurd) generateWebModelRules(ctx context.Context, in *CurdPreviewInput)
continue
}
+ in.options.Step.HasRules = true
if field.FormRole == "" || field.FormRole == FormRoleNone || field.FormRole == "required" {
buffer.WriteString(fmt.Sprintf(" %s: {\n required: %v,\n trigger: ['blur', 'input'],\n type: '%s',\n message: '请输入%s',\n },\n", field.TsName, field.Required, field.TsType, field.Dc))
} else {
+ in.options.Step.HasRulesValidator = true
buffer.WriteString(fmt.Sprintf(" %s: {\n required: %v,\n trigger: ['blur', 'input'],\n type: '%s',\n validator: validate.%v,\n },\n", field.TsName, field.Required, field.TsType, field.FormRole))
}
}
@@ -187,7 +256,7 @@ func (l *gCurd) generateWebModelFormSchema(ctx context.Context, in *CurdPreviewI
buffer.WriteString("export const schemas = ref([\n")
// 主表
- l.generateWebModelFormSchemaEach(buffer, in.masterFields)
+ l.generateWebModelFormSchemaEach(buffer, in.masterFields, in)
// 关联表
if len(in.options.Join) > 0 {
@@ -195,7 +264,7 @@ func (l *gCurd) generateWebModelFormSchema(ctx context.Context, in *CurdPreviewI
if !isEffectiveJoin(v) {
continue
}
- l.generateWebModelFormSchemaEach(buffer, v.Columns)
+ l.generateWebModelFormSchemaEach(buffer, v.Columns, in)
}
}
@@ -203,11 +272,18 @@ func (l *gCurd) generateWebModelFormSchema(ctx context.Context, in *CurdPreviewI
return buffer.String()
}
-func (l *gCurd) generateWebModelFormSchemaEach(buffer *bytes.Buffer, fields []*sysin.GenCodesColumnListModel) {
+func (l *gCurd) generateWebModelFormSchemaEach(buffer *bytes.Buffer, fields []*sysin.GenCodesColumnListModel, in *CurdPreviewInput) {
for _, field := range fields {
if !field.IsQuery {
continue
}
+ in.options.Step.HasSearchForm = true
+
+ // 查询用户摘要
+ if field.IsQuery && in.options.Step.HasQueryMemberSummary && IsMemberSummaryField(field.Name) {
+ buffer.WriteString(fmt.Sprintf(" {\n field: '%s',\n component: '%s',\n label: '%s',\n componentProps: {\n placeholder: '请输入ID|用户名|姓名|手机号',\n onUpdateValue: (e: any) => {\n console.log(e);\n },\n },\n },\n", field.TsName, "NInput", field.Dc))
+ continue
+ }
var (
defaultComponent = fmt.Sprintf(" {\n field: '%s',\n component: '%s',\n label: '%s',\n componentProps: {\n placeholder: '请输入%s',\n onUpdateValue: (e: any) => {\n console.log(e);\n },\n },\n },\n", field.TsName, "NInput", field.Dc, field.Dc)
@@ -224,15 +300,19 @@ func (l *gCurd) generateWebModelFormSchemaEach(buffer *bytes.Buffer, fields []*s
case FormModeDate:
component = fmt.Sprintf(" {\n field: '%s',\n component: '%s',\n label: '%s',\n componentProps: {\n type: '%s',\n clearable: true,\n shortcuts: %s,\n onUpdateValue: (e: any) => {\n console.log(e);\n },\n },\n },\n", field.TsName, "NDatePicker", field.Dc, "date", "defShortcuts()")
+ in.options.Step.ImportModel.UtilsDate = append(in.options.Step.ImportModel.UtilsDate, "defShortcuts")
case FormModeDateRange:
component = fmt.Sprintf(" {\n field: '%s',\n component: '%s',\n label: '%s',\n componentProps: {\n type: '%s',\n clearable: true,\n shortcuts: %s,\n onUpdateValue: (e: any) => {\n console.log(e);\n },\n },\n },\n", field.TsName, "NDatePicker", field.Dc, "daterange", "defRangeShortcuts()")
+ in.options.Step.ImportModel.UtilsDate = append(in.options.Step.ImportModel.UtilsDate, "defRangeShortcuts")
case FormModeTime:
component = fmt.Sprintf(" {\n field: '%s',\n component: '%s',\n label: '%s',\n componentProps: {\n type: '%s',\n clearable: true,\n shortcuts: %s,\n onUpdateValue: (e: any) => {\n console.log(e);\n },\n },\n },\n", field.TsName, "NDatePicker", field.Dc, "datetime", "defShortcuts()")
+ in.options.Step.ImportModel.UtilsDate = append(in.options.Step.ImportModel.UtilsDate, "defShortcuts")
case FormModeTimeRange:
component = fmt.Sprintf(" {\n field: '%s',\n component: '%s',\n label: '%s',\n componentProps: {\n type: '%s',\n clearable: true,\n shortcuts: %s,\n onUpdateValue: (e: any) => {\n console.log(e);\n },\n },\n },\n", field.TsName, "NDatePicker", field.Dc, "datetimerange", "defRangeShortcuts()")
+ in.options.Step.ImportModel.UtilsDate = append(in.options.Step.ImportModel.UtilsDate, "defRangeShortcuts")
case FormModeSwitch:
fallthrough
@@ -287,14 +367,22 @@ func (l *gCurd) generateWebModelColumnsEach(buffer *bytes.Buffer, in *CurdPrevie
continue
}
var (
- defaultComponent = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n },\n", field.Dc, field.TsName)
+ defaultComponent = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n align: '%v',\n width: %v,\n },\n", field.Dc, field.TsName, field.Align, field.Width)
component string
)
+ // 查询用户摘要
+ if in.options.Step.HasHookMemberSummary && IsMemberSummaryField(field.Name) {
+ buffer.WriteString(fmt.Sprintf(" {\n title: '%v',\n key: '%v',\n align: '%v',\n width: %v,\n render(row) {\n return renderPopoverMemberSumma(row.%vSumma);\n },\n },\n", field.Dc, field.TsName, field.Align, field.Width, field.TsName))
+ in.options.Step.ImportModel.UtilsIndex = append(in.options.Step.ImportModel.UtilsIndex, []string{"renderPopoverMemberSumma", "MemberSumma"}...)
+ continue
+ }
+
// 这里根据编辑表单组件来进行推断,如果没有则使用默认input,这可能会导致和查询条件所需参数不符的情况
switch field.FormMode {
case FormModeDate:
- component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n render(row) {\n return formatToDate(row.%s);\n },\n },\n", field.Dc, field.TsName, field.TsName)
+ component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n align: '%v',\n width: %v,\n render(row) {\n return formatToDate(row.%s);\n },\n },\n", field.Dc, field.TsName, field.Align, field.Width, field.TsName)
+ in.options.Step.ImportModel.UtilsDate = append(in.options.Step.ImportModel.UtilsDate, "formatToDate")
case FormModeRadio:
fallthrough
@@ -303,32 +391,50 @@ func (l *gCurd) generateWebModelColumnsEach(buffer *bytes.Buffer, in *CurdPrevie
err = gerror.Newf("设置单选下拉框选项时,必须选择字典类型,字段名称:%v", field.Name)
return
}
- component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n render(row) {\n if (isNullObject(row.%s)) {\n return ``;\n }\n return h(\n NTag,\n {\n style: {\n marginRight: '6px',\n },\n type: getOptionTag(options.value.%s, row.%s),\n bordered: false,\n },\n {\n default: () => getOptionLabel(options.value.%s, row.%s),\n }\n );\n },\n },\n", field.Dc, field.TsName, field.TsName, in.options.dictMap[field.TsName], field.TsName, in.options.dictMap[field.TsName], field.TsName)
+ component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n align: '%v',\n width: %v,\n render(row) {\n if (isNullObject(row.%s)) {\n return ``;\n }\n return h(\n NTag,\n {\n style: {\n marginRight: '6px',\n },\n type: getOptionTag(options.value.%s, row.%s),\n bordered: false,\n },\n {\n default: () => getOptionLabel(options.value.%s, row.%s),\n }\n );\n },\n },\n", field.Dc, field.TsName, field.Align, field.Width, field.TsName, in.options.dictMap[field.TsName], field.TsName, in.options.dictMap[field.TsName], field.TsName)
+ in.options.Step.ImportModel.NaiveUI = append(in.options.Step.ImportModel.NaiveUI, "NTag")
+ in.options.Step.ImportModel.UtilsIs = append(in.options.Step.ImportModel.UtilsIs, "isNullObject")
+ in.options.Step.ImportModel.UtilsHotGo = append(in.options.Step.ImportModel.UtilsHotGo, []string{"getOptionLabel", "getOptionTag"}...)
case FormModeSelectMultiple:
if g.IsEmpty(in.options.dictMap[field.TsName]) {
err = gerror.Newf("设置多选下拉框选项时,必须选择字典类型,字段名称:%v", field.Name)
return
}
- component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n render(row) {\n if (isNullObject(row.%s) || !isArray(row.%s)) {\n return ``;\n }\n return row.%s.map((tagKey) => {\n return h(\n NTag,\n {\n style: {\n marginRight: '6px',\n },\n type: getOptionTag(options.value.%s, tagKey),\n bordered: false,\n },\n {\n default: () => getOptionLabel(options.value.%s, tagKey),\n }\n );\n });\n },\n },\n", field.Dc, field.TsName, field.TsName, field.TsName, field.TsName, in.options.dictMap[field.TsName], in.options.dictMap[field.TsName])
+ component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n align: '%v',\n width: %v,\n render(row) {\n if (isNullObject(row.%s) || !isArray(row.%s)) {\n return ``;\n }\n return row.%s.map((tagKey) => {\n return h(\n NTag,\n {\n style: {\n marginRight: '6px',\n },\n type: getOptionTag(options.value.%s, tagKey),\n bordered: false,\n },\n {\n default: () => getOptionLabel(options.value.%s, tagKey),\n }\n );\n });\n },\n },\n", field.Dc, field.TsName, field.Align, field.Width, field.TsName, field.TsName, field.TsName, in.options.dictMap[field.TsName], in.options.dictMap[field.TsName])
+ in.options.Step.ImportModel.NaiveUI = append(in.options.Step.ImportModel.NaiveUI, "NTag")
+ in.options.Step.ImportModel.UtilsIs = append(in.options.Step.ImportModel.UtilsIs, "isNullObject")
+ in.options.Step.ImportModel.UtilsHotGo = append(in.options.Step.ImportModel.UtilsHotGo, []string{"getOptionLabel", "getOptionTag"}...)
case FormModeUploadImage:
- component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n render(row) {\n return h(%s, {\n width: 32,\n height: 32,\n src: row.%s,\n onError: errorImg,\n style: {\n width: '32px',\n height: '32px',\n 'max-width': '100%%',\n 'max-height': '100%%',\n },\n });\n },\n },\n", field.Dc, field.TsName, "NImage", field.TsName)
+ component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n align: '%v',\n width: %v,\n render(row) {\n return h(%s, {\n width: 32,\n height: 32,\n src: row.%s,\n fallbackSrc: errorImg,\n onError: errorImg,\n style: {\n width: '32px',\n height: '32px',\n 'max-width': '100%%',\n 'max-height': '100%%',\n },\n });\n },\n },\n", field.Dc, field.TsName, field.Align, field.Width, "NImage", field.TsName)
+ in.options.Step.ImportModel.NaiveUI = append(in.options.Step.ImportModel.NaiveUI, "NImage")
+ in.options.Step.ImportModel.UtilsHotGo = append(in.options.Step.ImportModel.UtilsHotGo, "errorImg")
case FormModeUploadImages:
- component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n render(row) {\n if (isNullObject(row.%s)) {\n return ``;\n }\n return row.%s.map((image) => {\n return h(%s, {\n width: 32,\n height: 32,\n src: image,\n onError: errorImg,\n style: {\n width: '32px',\n height: '32px',\n 'max-width': '100%%',\n 'max-height': '100%%',\n 'margin-left': '2px',\n },\n });\n });\n },\n },\n", field.Dc, field.TsName, field.TsName, field.TsName, "NImage")
+ component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n align: '%v',\n width: %v,\n render(row) {\n if (isNullObject(row.%s)) {\n return ``;\n }\n return row.%s.map((image) => {\n return h(%s, {\n width: 32,\n height: 32,\n src: image,\n onError: errorImg,\n style: {\n width: '32px',\n height: '32px',\n 'max-width': '100%%',\n 'max-height': '100%%',\n 'margin-left': '2px',\n },\n });\n });\n },\n },\n", field.Dc, field.TsName, field.Align, field.Width, field.TsName, field.TsName, "NImage")
+ in.options.Step.ImportModel.NaiveUI = append(in.options.Step.ImportModel.NaiveUI, "NImage")
+ in.options.Step.ImportModel.UtilsIs = append(in.options.Step.ImportModel.UtilsIs, "isArray")
+ in.options.Step.ImportModel.UtilsHotGo = append(in.options.Step.ImportModel.UtilsHotGo, "errorImg")
case FormModeUploadFile:
- component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n render(row) {\n if (row.%s === '') {\n return ``;\n }\n return h(\n %s,\n {\n size: 'small',\n },\n {\n default: () => getFileExt(row.%s),\n }\n );\n },\n },\n", field.Dc, field.TsName, field.TsName, "NAvatar", field.TsName)
+ component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n align: '%v',\n width: %v,\n render(row) {\n if (row.%s === '') {\n return ``;\n }\n return h(\n %s,\n {\n size: 'small',\n },\n {\n default: () => getFileExt(row.%s),\n }\n );\n },\n },\n", field.Dc, field.TsName, field.Align, field.Width, field.TsName, "NAvatar", field.TsName)
+ in.options.Step.ImportModel.NaiveUI = append(in.options.Step.ImportModel.NaiveUI, "NAvatar")
+ in.options.Step.ImportModel.UtilsUrl = append(in.options.Step.ImportModel.UtilsUrl, "getFileExt")
case FormModeUploadFiles:
- component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n render(row) {\n if (isNullObject(row.%s)) {\n return ``;\n }\n return row.%s.map((attachfile) => {\n return h(\n %s,\n {\n size: 'small',\n style: {\n 'margin-left': '2px',\n },\n },\n {\n default: () => getFileExt(attachfile),\n }\n );\n });\n },\n },\n", field.Dc, field.TsName, field.TsName, field.TsName, "NAvatar")
+ component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n align: '%v',\n width: %v,\n render(row) {\n if (isNullObject(row.%s)) {\n return ``;\n }\n return row.%s.map((attachfile) => {\n return h(\n %s,\n {\n size: 'small',\n style: {\n 'margin-left': '2px',\n },\n },\n {\n default: () => getFileExt(attachfile),\n }\n );\n });\n },\n },\n", field.Dc, field.TsName, field.Align, field.Width, field.TsName, field.TsName, "NAvatar")
+ in.options.Step.ImportModel.NaiveUI = append(in.options.Step.ImportModel.NaiveUI, "NAvatar")
+ in.options.Step.ImportModel.UtilsIs = append(in.options.Step.ImportModel.UtilsIs, "isNullObject")
+ in.options.Step.ImportModel.UtilsUrl = append(in.options.Step.ImportModel.UtilsUrl, "getFileExt")
case FormModeSwitch:
- component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n width: 100,\n render(row) {\n return h(%s, {\n value: row.%s === 1,\n checked: '开启',\n unchecked: '关闭',\n disabled: !hasPermission(['%s']),\n onUpdateValue: function (e) {\n console.log('onUpdateValue e:' + JSON.stringify(e));\n row.%s = e ? 1 : 2;\n Switch({ %s: row.%s, key: '%s', value: row.%s }).then((_res) => {\n $message.success('操作成功');\n });\n },\n });\n },\n },\n", field.Dc, field.TsName, "NSwitch", field.TsName, "/"+in.options.ApiPrefix+"/switch", field.TsName, in.pk.TsName, in.pk.TsName, convert.CamelCaseToUnderline(field.TsName), field.TsName)
+ component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n align: '%v',\n width: %v,\n render(row) {\n return h(%s, {\n value: row.%s === 1,\n checked: '开启',\n unchecked: '关闭',\n disabled: !hasPermission(['%s']),\n onUpdateValue: function (e) {\n console.log('onUpdateValue e:' + JSON.stringify(e));\n row.%s = e ? 1 : 2;\n Switch({ %s: row.%s, key: '%s', value: row.%s }).then((_res) => {\n $message.success('操作成功');\n });\n },\n });\n },\n },\n", field.Dc, field.TsName, field.Align, field.Width, "NSwitch", field.TsName, "/"+in.options.ApiPrefix+"/switch", field.TsName, in.pk.TsName, in.pk.TsName, convert.CamelCaseToUnderline(field.TsName), field.TsName)
+ in.options.Step.ImportModel.NaiveUI = append(in.options.Step.ImportModel.NaiveUI, "NSwitch")
case FormModeRate:
- component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n render(row) {\n return h(%s, {\n allowHalf: true,\n readonly: true,\n defaultValue: row.%s,\n });\n },\n },\n", field.Dc, field.TsName, "NRate", field.TsName)
+ component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n align: '%v',\n width: %v,\n render(row) {\n return h(%s, {\n allowHalf: true,\n readonly: true,\n defaultValue: row.%s,\n });\n },\n },\n", field.Dc, field.TsName, field.Align, field.Width, "NRate", field.TsName)
+ in.options.Step.ImportModel.NaiveUI = append(in.options.Step.ImportModel.NaiveUI, "NRate")
default:
component = defaultComponent
@@ -336,6 +442,5 @@ func (l *gCurd) generateWebModelColumnsEach(buffer *bytes.Buffer, in *CurdPrevie
buffer.WriteString(component)
}
-
return
}
diff --git a/server/internal/library/hggen/views/curd_generate_web_view.go b/server/internal/library/hggen/views/curd_generate_web_view.go
index 1d9166a..39363a7 100644
--- a/server/internal/library/hggen/views/curd_generate_web_view.go
+++ b/server/internal/library/hggen/views/curd_generate_web_view.go
@@ -44,25 +44,25 @@ func (l *gCurd) generateWebViewItem(ctx context.Context, in *CurdPreviewInput) s
component = defaultComponent
case FormModeRadio, FormModeSelect:
- component = fmt.Sprintf("\n {{ getOptionLabel(options.%s, formValue?.%s) }}\n ", field.Dc, in.options.dictMap[field.TsName], field.TsName, in.options.dictMap[field.TsName], field.TsName)
+ component = fmt.Sprintf("\n {{ getOptionLabel(options.%s, formValue?.%s) }}\n ", field.Dc, in.options.dictMap[field.TsName], field.TsName, in.options.dictMap[field.TsName], field.TsName)
case FormModeCheckbox, FormModeSelectMultiple:
- component = fmt.Sprintf("\n \n {{ getOptionLabel(options.%s, item) }}\n \n ", field.Dc, field.TsName, in.options.dictMap[field.TsName], in.options.dictMap[field.TsName])
+ component = fmt.Sprintf("\n \n {{ getOptionLabel(options.%s, item) }}\n \n ", field.Dc, field.TsName, in.options.dictMap[field.TsName], in.options.dictMap[field.TsName])
case FormModeUploadImage:
- component = fmt.Sprintf("\n %s\n ", field.Dc, field.TsName)
+ component = fmt.Sprintf("\n %s\n ", field.Dc, field.TsName)
case FormModeUploadImages:
component = fmt.Sprintf("\n %s\n \n \n \n \n \n \n \n ", field.Dc, field.TsName)
case FormModeUploadFile:
- component = fmt.Sprintf("\n %s\n \n
\n
\n
\n {{ getFileExt(formValue.%s) }}\n
\n
\n
\n
\n ", field.Dc, field.TsName, field.TsName, field.TsName)
+ component = fmt.Sprintf("\n %s\n \n
\n
\n
\n {{ getFileExt(formValue.%s) }}\n
\n
\n
\n
\n ", field.Dc, field.TsName, field.TsName, field.TsName)
case FormModeUploadFiles:
component = fmt.Sprintf("\n %s\n \n
\n \n
\n
\n {{\n getFileExt(item)\n }}\n
\n
\n
\n \n
\n ", field.Dc, field.TsName)
case FormModeSwitch:
- component = fmt.Sprintf("\n ", field.Dc, field.TsName)
+ component = fmt.Sprintf("\n ", field.Dc, field.TsName)
case FormModeRate:
component = fmt.Sprintf("", field.Dc, field.TsName)
diff --git a/server/internal/library/hggen/views/gohtml/consts.go b/server/internal/library/hggen/views/gohtml/consts.go
new file mode 100644
index 0000000..625118c
--- /dev/null
+++ b/server/internal/library/hggen/views/gohtml/consts.go
@@ -0,0 +1,7 @@
+package gohtml
+
+const (
+ defaultIndentString = " "
+ startIndent = 0
+ defaultLastElement = "