发布v2.3.5版本,本次为优化版本。更新内容请查看:https://github.com/bufanyun/hotgo/blob/v2.0/docs/guide-zh-CN/start-update-log.md
20
README.md
@ -2,7 +2,7 @@
|
||||
<div align="center">
|
||||
<img width="140px" src="https://bufanyun.cn-bj.ufileos.com/hotgo/logo.sig.png">
|
||||
<p>
|
||||
<h1>HotGo V2.1</h1>
|
||||
<h1>HotGo V2</h1>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://goframe.org/pages/viewpage.action?pageId=1114119" target="_blank">
|
||||
@ -29,7 +29,15 @@
|
||||
|
||||
## 平台简介
|
||||
* 基于全新Go Frame 2+Vue3+Naive UI+UinApp开发的全栖框架,为二次开发而生,适合中小型完整应用开发。
|
||||
* 前端采用naive-ui-admin 、Vue、Naive UI、UinApp。
|
||||
* 前端采用Naive-Ui-Admin、Vue、Naive UI、UinApp。
|
||||
|
||||
## 演示地址
|
||||
- [https://hotgo.facms.cn/admin](https://hotgo.facms.cn/admin)
|
||||
> 账号:admin 密码:123456
|
||||
|
||||
### 使用文档
|
||||
|
||||
[安装文档](docs/guide-zh-CN/start-installation.md) · [本地文档](docs/guide-zh-CN/README.md) · [更新历史](docs/guide-zh-CN/start-update-log.md) · [常见问题](docs/guide-zh-CN/start-issue.md)
|
||||
|
||||
|
||||
## 特征
|
||||
@ -43,7 +51,6 @@
|
||||
|
||||
|
||||
## 后台内置功能
|
||||
|
||||
1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
|
||||
2. 部门管理:配置系统组织机构(公司、部门、岗位),树结构展现支持数据权限。
|
||||
3. 岗位管理:配置系统用户所属担任职务。
|
||||
@ -68,13 +75,6 @@
|
||||
|
||||
> HotGo开源以来得到了大家的很多支持,本项目初衷只为互相学习交流,没有任何盈利性目的!欢迎为HotGo贡献代码或提供建议!
|
||||
|
||||
## 演示地址
|
||||
- [https://hotgo.facms.cn/admin](https://hotgo.facms.cn/admin)
|
||||
> 账号:admin 密码:123456
|
||||
|
||||
### 使用文档
|
||||
|
||||
[安装文档](docs/guide-zh-CN/start-installation.md) · [本地文档](docs/guide-zh-CN/README.md) · [更新历史](docs/guide-zh-CN/start-update-log.md) · [常见问题](docs/guide-zh-CN/start-issue.md)
|
||||
|
||||
## 演示图
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
- [系统介绍](../../README.md)
|
||||
- [环境搭建](start-environment.md)
|
||||
- [系统安装](start-installation.md)
|
||||
- 生产部署
|
||||
- [生产部署](start-deploy.md)
|
||||
- [如何提问](start-questions.md)
|
||||
- [常见问题](start-issue.md)
|
||||
- [更新历史](start-update-log.md)
|
||||
@ -14,11 +14,12 @@
|
||||
#### 系统开发
|
||||
|
||||
- [目录结构](sys-catalog.md)
|
||||
- 开发规范
|
||||
- [开发规范](sys-exploit.md)
|
||||
- [控制台](sys-console.md)
|
||||
- 请求中间件和WebHook
|
||||
- 权限控制
|
||||
- 代码生成
|
||||
- [权限控制](sys-auth.md)
|
||||
- [数据库](sys-db.md)
|
||||
- [代码生成](sys-code.md)
|
||||
- 定时任务
|
||||
- [消息队列](sys-queue.md)
|
||||
- [功能扩展库](sys-library.md)
|
||||
@ -40,11 +41,10 @@
|
||||
|
||||
|
||||
### 前端开发
|
||||
- 表单组件
|
||||
- 权限
|
||||
- [表单组件](web-form.md)
|
||||
- Websocket客户端
|
||||
- 工具库
|
||||
- 发布部署
|
||||
- [独立部署](web-deploy.md)
|
||||
|
||||
#### 附录
|
||||
- [网址收录](append-website.md)
|
@ -14,10 +14,21 @@
|
||||
|
||||
1、HotGo 后台进入 开发工具->插件管理->找到创建新插件,根据引导进行创建即可。
|
||||
|
||||
> 创建成功后会在 根目录的 addons 目录下生成插件文件
|
||||
```
|
||||
创建成功后默认情况下会在以下目录中生成插件文件,假设新生成的插件名为:hgexample
|
||||
|
||||
1. /server/addons/hgexample/ # 插件模块目录
|
||||
2. /server/addons/modules/hgexample.go # 隐式注册插件文件
|
||||
3. /server/resource/template/addons/hgexample # pc模板目录
|
||||
4. /web/src/api/addons/hgexample # webApi目录
|
||||
5. /web/src/views/addons/hgexample # web页面目录
|
||||
|
||||
# 默认情况下没有为web页面生成菜单权限,因为在实际场景中插件不一定需要用到web页面,所以如有需要请手动到后台 权限管理 -> 菜单权限->自行添加菜单和配置权限
|
||||
```
|
||||
|
||||
|
||||
2、创建插件完毕重启服务端后,插件管理中会出现你新创建的插件信息。操作栏有几个按钮,在此进行说明
|
||||
- 安装:会自动执行 server/xxx插件/main.go 文件中的Install方法,方法中的具体逻辑默认为空,可以根据实际情况自行配置。如生成后台菜单、生成插件配置表初始化数据、迁移home页面、web项目文件等。
|
||||
- 安装:会自动执行 server/hgexample/main.go 文件中的Install方法,方法中的具体逻辑默认为空,可以根据实际情况自行配置。如生成后台菜单、生成插件配置表初始化数据、迁移home页面、web项目文件等。
|
||||
```
|
||||
// Install 安装模块
|
||||
func (m *module) Install(ctx context.Context) (err error) {
|
||||
@ -26,7 +37,7 @@ func (m *module) Install(ctx context.Context) (err error) {
|
||||
}
|
||||
```
|
||||
|
||||
- 更新:会自动执行 server/xxx插件/main.go 文件中的Upgrade方法,方法中的具体逻辑默认为空,可以根据实际情况自行配置。
|
||||
- 更新:会自动执行 server/hgexample/main.go 文件中的Upgrade方法,方法中的具体逻辑默认为空,可以根据实际情况自行配置。
|
||||
```
|
||||
// Upgrade 更新模块
|
||||
func (m *module) Upgrade(ctx context.Context) (err error) {
|
||||
@ -35,7 +46,7 @@ func (m *module) Upgrade(ctx context.Context) (err error) {
|
||||
}
|
||||
```
|
||||
|
||||
- 卸载:会自动执行 server/xxx插件/main.go 文件中的UnInstall方法,方法中的具体逻辑默认为空,可以根据实际情况自行配置。如会清除所有的数据表和已安装的信息等。
|
||||
- 卸载:会自动执行 server/hgexample/main.go 文件中的UnInstall方法,方法中的具体逻辑默认为空,可以根据实际情况自行配置。如会清除所有的数据表和已安装的信息等。
|
||||
```
|
||||
// UnInstall 卸载模块
|
||||
func (m *module) UnInstall(ctx context.Context) (err error) {
|
||||
@ -57,7 +68,7 @@ func (m *module) UnInstall(ctx context.Context) (err error) {
|
||||
一个简单的例子:
|
||||
> 假设hgexample插件模块要通过主模块的服务接口更新插件配置
|
||||
|
||||
文件:\server\addons\hgexample\model\input\sysin\config.go
|
||||
插件模块input:\server\addons\hgexample\model\input\sysin\config.go
|
||||
```go
|
||||
package sysin
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
|
||||
#### 模块结构
|
||||
- 文件路径:server/internal/library/addons/module.go
|
||||
```go
|
||||
// Skeleton 模块骨架
|
||||
type Skeleton struct {
|
||||
@ -74,7 +75,7 @@ func test(ctx context.Context) {
|
||||
}
|
||||
```
|
||||
|
||||
- 更多辅助方法请参考:\server\internal\library\addons
|
||||
- 更多辅助方法请参考插件功能库:server/internal/library/addons
|
||||
|
||||
#### 插件路由规则
|
||||
- 如果你不喜欢现在的路由风格,可以自行调整。修改位置在:\server\internal\library\addons\addons.go的RouterPrefix方法。
|
||||
|
BIN
docs/guide-zh-CN/images/start-deploy-report.png
Normal file
After Width: | Height: | Size: 1012 KiB |
BIN
docs/guide-zh-CN/images/sys-code-add.png
Normal file
After Width: | Height: | Size: 83 KiB |
BIN
docs/guide-zh-CN/images/sys-code-config-init.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
docs/guide-zh-CN/images/sys-code-config-join-save.png
Normal file
After Width: | Height: | Size: 93 KiB |
BIN
docs/guide-zh-CN/images/sys-code-config-join.png
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
docs/guide-zh-CN/images/sys-code-config-post.png
Normal file
After Width: | Height: | Size: 85 KiB |
BIN
docs/guide-zh-CN/images/sys-code-list-edit-ok.png
Normal file
After Width: | Height: | Size: 174 KiB |
BIN
docs/guide-zh-CN/images/sys-code-list-ok.png
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
docs/guide-zh-CN/images/sys-code-list.png
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
docs/guide-zh-CN/images/sys-code-master-save.png
Normal file
After Width: | Height: | Size: 171 KiB |
BIN
docs/guide-zh-CN/images/sys-code-master.png
Normal file
After Width: | Height: | Size: 191 KiB |
BIN
docs/guide-zh-CN/images/sys-code-preview.png
Normal file
After Width: | Height: | Size: 67 KiB |
94
docs/guide-zh-CN/start-deploy.md
Normal file
@ -0,0 +1,94 @@
|
||||
## 生产部署
|
||||
|
||||
目录
|
||||
|
||||
- 编译配置
|
||||
- 编译
|
||||
- 修改生产配置文件
|
||||
- 启动服务
|
||||
- Nginx配置
|
||||
|
||||
### 编译配置
|
||||
|
||||
- 配置文件: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: ""
|
||||
```
|
||||
|
||||
|
||||
### 编译
|
||||
|
||||
- 以下方式任选其一即可
|
||||
|
||||
1、 make一键编译 (linux或mac环境推荐)
|
||||
```shell
|
||||
cd server && make build
|
||||
```
|
||||
|
||||
2、 按步骤手动编译
|
||||
```shell
|
||||
cd server # 切换到服务端目录下
|
||||
rm -rf ./resource/public/admin/ # 删除之前的web资源
|
||||
mkdir ./resource/public/admin/ # 重新创建web资源存放目录,除首次编译后续可以跳过执行此步骤
|
||||
cd ../web && yarn build # 切换到web项目下,编译web项目
|
||||
\cp -rf ./dist/* ../server/resource/public/admin/ # 将编译好的web资源复制到server对应的资源存放路径下
|
||||
echo "y" | gf build # 编译hotgo服务端
|
||||
|
||||
# 不出意外你已经编译好了hotgo可执行文件!
|
||||
```
|
||||
|
||||
3、分服务编译
|
||||
|
||||
待写。
|
||||
|
||||
|
||||
### 修改生产配置文件
|
||||
- 配置文件:server/manifest/config/config.yaml
|
||||
> 如关闭代码生成功能、修改数据库地址、缓存驱动、队列驱动、日志路径等
|
||||
|
||||
|
||||
|
||||
### 启动服务
|
||||
> 这里推可以接使用gf官方推荐的启动方式,请参考:https://goframe.org/pages/viewpage.action?pageId=1114403
|
||||
|
||||
|
||||
### Nginx配置
|
||||
```
|
||||
# websocket
|
||||
location ^~ /socket {
|
||||
proxy_pass http://127.0.0.1:8000/socket;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-NginX-Proxy true;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_read_timeout 600;
|
||||
proxy_send_timeout 600s;
|
||||
}
|
||||
|
||||
# http
|
||||
location ^~ / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_pass http://127.0.0.1:8000/; # 设置代理服务器的协议和地址
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection upgrade;
|
||||
}
|
||||
```
|
@ -46,11 +46,6 @@ database:
|
||||
* /hotgo/web/.env.production
|
||||
* /hotgo/web/.env
|
||||
|
||||
其中必改配置
|
||||
```
|
||||
VITE_PROXY=[["/admin","http://你的IP:8000/admin"]]
|
||||
```
|
||||
|
||||
|
||||
三、 启动服务
|
||||
|
||||
|
@ -11,6 +11,14 @@
|
||||
|
||||
> 如果升级(覆盖)代码后打开会出现 sql 报错, 请检查更新的数据库格式或自行调整
|
||||
|
||||
### v2.3.5
|
||||
updated 2023.02.26
|
||||
|
||||
- 优化: 调整消息队列消费初始化方式,支持在插件模块下注册消费者
|
||||
- 修复:后台用户添加模态框无法正常显示问题
|
||||
- 优化:创建新插件生成增加web页面的生成
|
||||
- 增加:增加生产部署、开发规范、生成代码、表单组件等相关文档
|
||||
- 修复:修复后台已知的一些小bug
|
||||
|
||||
### v2.2.10
|
||||
updated 2023.02.23
|
||||
|
155
docs/guide-zh-CN/sys-auth.md
Normal file
@ -0,0 +1,155 @@
|
||||
## 权限控制
|
||||
|
||||
目录
|
||||
|
||||
- 一、菜单权限
|
||||
- 二、数据权限
|
||||
|
||||
|
||||
### 一、菜单权限
|
||||
|
||||
> 菜单权限可以控制web后台菜单功能,每个菜单可以绑定一个或多个API权限,API权限决定了用户是否可以调用服务端接口。
|
||||
|
||||
#### 为角色添加菜单权限
|
||||
|
||||
1、HotGo后台 -> 权限管理 -> 菜单权限 -> 找到添加菜单,按照表单提示添加你的菜单信息。
|
||||
|
||||
2、添加菜单完成后,到 权限管理 -> 角色权限 -> 找到一位已有角色或者添加新角色 -> 在列表右侧操作栏找到菜单权限打开 -> 将第1步中添加的菜单分配给该角色即可。
|
||||
|
||||
#### 细粒度权限
|
||||
|
||||
> 一般用于web后台页面上的功能按钮,对不同的角色展示不同的按钮时会用到。
|
||||
|
||||
1、参考【为角色添加菜单权限】添加一个按钮类型的菜单。并为菜单分配API权限,假设你分配权限是:/test/index
|
||||
|
||||
2、在web页面中创建一个按钮,如下:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<n-button v-if="hasPermission(['/test/index'])">拥有[/test/index]权限可见</n-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { usePermission } from '@/hooks/web/usePermission';
|
||||
const { hasPermission } = usePermission();
|
||||
|
||||
</script>
|
||||
|
||||
```
|
||||
|
||||
#### 菜单表主要字段解释
|
||||
|
||||
- 代码片段:[server/internal/model/input/adminin/menu.go](../../server/internal/model/input/adminin/menu.go)
|
||||
|
||||
```go
|
||||
type MenuRouteMeta struct {
|
||||
// 解释参考:https://naive-ui-admin-docs.vercel.app/guide/router.html#%E5%A4%9A%E7%BA%A7%E8%B7%AF%E7%94%B1
|
||||
Title string `json:"title"` // 菜单名称 一般必填
|
||||
//Disabled bool `json:"disabled,omitempty"` // 禁用菜单
|
||||
Icon string `json:"icon,omitempty"` // 菜单图标
|
||||
KeepAlive bool `json:"keepAlive,omitempty"` // 缓存该路由
|
||||
Hidden bool `json:"hidden,omitempty"` // 隐藏菜单
|
||||
Sort int `json:"sort,omitempty"` // 排序越小越排前
|
||||
AlwaysShow bool `json:"alwaysShow,omitempty"` // 取消自动计算根路由模式
|
||||
ActiveMenu string `json:"activeMenu,omitempty"` // 当路由设置了该属性,则会高亮相对应的侧边栏。
|
||||
// 这在某些场景非常有用,比如:一个列表页路由为:/list/basic-list
|
||||
// 点击进入详情页,这时候路由为/list/basic-info/1,但你想在侧边栏高亮列表的路由,就可以进行如下设置
|
||||
// 注意是配置高亮路由 `name`,不是path
|
||||
IsRoot bool `json:"isRoot,omitempty"` // 是否跟路由 顶部混合菜单,必须传 true,否则左侧会显示异常(场景就是,分割菜单之后,当一级菜单没有子菜单)
|
||||
FrameSrc string `json:"frameSrc,omitempty" ` // 内联外部地址
|
||||
Permissions string `json:"permissions,omitempty"` // 菜单包含权限集合,满足其中一个就会显示
|
||||
Affix bool `json:"affix,omitempty"` // 是否固定 设置为 true 之后 多页签不可删除
|
||||
Type int `json:"type"` // 菜单类型
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### API权限验证流程
|
||||
|
||||
```mermaid
|
||||
|
||||
graph TD
|
||||
A(用户请求API) --> B(验证中间件<br> server/internal/logic/middleware/admin_auth.go)
|
||||
B -->|没有登录,但API需要登录| H(提示登录)
|
||||
B -->|没有登录,但API不需要登录| G(验证通过,进入业务)
|
||||
B -->|已登录| C(检查用户角色是否拥有API权限<br> server/internal/logic/admin/role.go,方法:Verify) -->|有权限| G(验证通过,进入业务)
|
||||
C -->|API无需验证权限| G(验证通过,进入业务流程)
|
||||
C -->|无权限| D(提示无权限)
|
||||
```
|
||||
|
||||
#### 菜单权限添加或修改后多久生效?
|
||||
|
||||
- API权限实时生效,web后台菜单刷新页面后生效,无需重启服务
|
||||
|
||||
|
||||
### 二、数据权限
|
||||
|
||||
> 数据权限是某人只能看到某些数据,可以为某人设置一定的数据范围,让其只能看到允许他看到的数据。
|
||||
|
||||
> 例如:公司存在多个销售部门,不同的部门仅能查看到自己所属部门下的数据;再如:用户有多级代理商,只能查看自己和自己下级的数据。
|
||||
|
||||
#### 目前已经支持的数据权限如下
|
||||
|
||||
| 数据范围 | 描述 |
|
||||
|---------|--------------------------------------------|
|
||||
| 全部权限 | 不做过滤,可以查看所有数据 |
|
||||
| 当前部门 | 仅可以看到所属部门下的数据 |
|
||||
| 当前以及下部门 | 仅可以看到所属以及下级部门的数据 |
|
||||
| 自定义部门 | 可以看到设置的指定部门,支持多个 |
|
||||
| 仅自己 | 仅可以看到自己的数据 |
|
||||
| 自己和直属下级 | 仅可以看到自己和直属下级的数据,直属下级是指当前用户往下1级的用户 |
|
||||
| 自己和全部下级 | 仅可以看到自己和全部下级的数据,全部下级是指当前用户所有的下级用户,包含其下级的下级 |
|
||||
|
||||
#### 如何区分部门和下级用户?
|
||||
|
||||
- 在实际使用时,部门更多的是在公司或机构中使用,可以通过在 组织管理 -> 后台用户 ->为用户绑定部门
|
||||
- 下级用户在代理商或分销系统中比较常见,后台用户由谁添加的,那么被添加的用户就是其下级用户。后续也将开放邀请码绑定下级功能。
|
||||
|
||||
#### 如何判断数据是谁的?
|
||||
|
||||
- 在数据库建表时,只要表中包含字段:`created_by`或`member_id`即可过滤 [仅自己、自己和直属下级、自己和全部下级] 类型的数据权限
|
||||
|
||||
#### 如何在具体业务中使用数据权限过滤?
|
||||
|
||||
- 只需在查询、更新或删除等ORM链式操作中加入相应`Handler`方法即可
|
||||
|
||||
- 如果你想对Handler有更多详细了解,请查看:https://goframe.org/pages/viewpage.action?pageId=20087340
|
||||
|
||||
- 下面提供一个简单使用例子:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/hgorm/handler"
|
||||
)
|
||||
|
||||
func test(ctx context.Context) {
|
||||
dao.AdminPost.Ctx(ctx).Where("id", 1).Handler(handler.FilterAuth).Scan(&res)
|
||||
}
|
||||
```
|
||||
|
||||
- 如果表中没有字段:`created_by`或`member_id`,也可以使用自定义字段方法来过滤数据权限,如下:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/hgorm/handler"
|
||||
)
|
||||
|
||||
func test(ctx context.Context) {
|
||||
dao.AdminPost.Ctx(ctx).Where("id", 1).Handler(handler.FilterAuthWithField("test_field")).Scan(&res)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
- 过滤方法实现请参考:[server/internal/library/hgorm/handler/filter_auth.go](../../server/internal/library/hgorm/handler/filter_auth.go)
|
||||
|
||||
|
@ -71,7 +71,7 @@
|
||||
| --- --- global | 项目内主要的全局变量和系统的一些初始化操作 |
|
||||
| --- --- logic | 业务逻辑封装管理,特定的业务逻辑实现和封装往往是项目中最复杂的部分 |
|
||||
| --- --- model | 数据结构管理模块,管理数据实体对象,以及输入与输出数据结构定义 |
|
||||
| --- --- --- input | 对内接口。用于controller调用service或service之间调用时的输入/输出结构定义和输入过滤和预处理 |
|
||||
| --- --- --- input | 对内接口。用于controller调用service或service之间调用时的输入/输出结构定义,以及输入过滤和预处理 |
|
||||
| --- --- router | 注册对外接口和分组中间件 |
|
||||
| --- --- service | 用于业务模块解耦的接口定义层具体的接口实现在logic中进行注入 |
|
||||
| --- main.go | 插件始化文件和模块插拔接口 |
|
||||
@ -93,7 +93,7 @@
|
||||
| --- model | 数据结构管理模块,管理数据实体对象,以及输入与输出数据结构定义 |
|
||||
| --- --- do | 用于dao数据操作中业务模型与实例模型转换,由工具维护,用户不能修改 |
|
||||
| --- --- entity | 与数据集合绑定的程序数据结构定义,通常和数据表一一对应 |
|
||||
| --- --- input | 对内接口。用于controller调用service或service之间调用时的输入/输出结构定义和输入过滤和预处理 |
|
||||
| --- --- input | 对内接口。用于controller调用service或service之间调用时的输入/输出结构定义,以及输入过滤和预处理 |
|
||||
| --- packed | 将静态资源打包进可执行文件,无需单独部署 |
|
||||
| --- queues | 为项目内所有的消息队列的消费者提供统一的初始化和处理 |
|
||||
| --- router | 注册对外接口和分组中间件 |
|
||||
|
235
docs/guide-zh-CN/sys-code.md
Normal file
@ -0,0 +1,235 @@
|
||||
## 代码生成
|
||||
|
||||
目录
|
||||
|
||||
- 使用条件
|
||||
- 生成配置
|
||||
- 一个生成增删改查列表例子
|
||||
- 内置gf-cli
|
||||
- 自定义生成模板
|
||||
|
||||
|
||||
> 在HotGo中可以通过后台开发工具快速的一键生成CRUD,自动生成Api、控制器、业务逻辑、Web页面、表单组件、菜单权限等。
|
||||
|
||||
|
||||
### 使用条件
|
||||
|
||||
- hotgo 版本号 >= 2.2.10
|
||||
- 使用前必须先看 [数据库](sys-db.md)
|
||||
|
||||
#### 增删改查列表
|
||||
|
||||
- 表自增长字段为 `id`
|
||||
|
||||
#### 关系树列表
|
||||
|
||||
- 表自增长字段为 `id`
|
||||
|
||||
|
||||
### 生成配置
|
||||
|
||||
- 注意:线上环境务必将allowedIPs参数设为空,考虑到项目安全问题请勿线上生成使用!
|
||||
|
||||
```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" # gfApi生成路径
|
||||
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页面生成路径
|
||||
|
||||
# 默认的插件包模板
|
||||
- group: "addon" # 分组名称
|
||||
isAddon: true # 是否为插件模板 false|true
|
||||
masterPackage: "sys" # 主包名称,需和controllerPath、logicPath、inputPath保持关联
|
||||
templatePath: "./resource/generate/default/curd" # 模板路径
|
||||
apiPath: "./addons/{$name}/api/admin" # gfApi生成路径
|
||||
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页面生成路径
|
||||
|
||||
# 关系树列表模板
|
||||
tree:
|
||||
templates:
|
||||
- group: "default"
|
||||
templatePath: "./resource/generate/default/tree"
|
||||
|
||||
# 消息队列模板
|
||||
queue:
|
||||
templates:
|
||||
- group: "default"
|
||||
templatePath: "./resource/generate/default/queue"
|
||||
|
||||
# 定时任务模板
|
||||
cron:
|
||||
templates:
|
||||
- group: "default"
|
||||
templatePath: "./resource/generate/default/cron"
|
||||
```
|
||||
|
||||
|
||||
### 一个生成增删改查列表例子
|
||||
|
||||
- 推荐使用热编译方式启动HotGo,这样生成完成页面自动刷新即可看到新生成内容,无需手动重启
|
||||
- 服务端热编译启动:`gf run main.go`, web前端启动:`yarn dev`
|
||||
|
||||
1、创建数据表
|
||||
|
||||
1.1 创建测试表格表`hg_test_table`:
|
||||
```mysql
|
||||
CREATE TABLE `hg_test_table` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`category_id` bigint(20) NOT NULL COMMENT '分类ID',
|
||||
`title` varchar(64) NOT NULL COMMENT '标题',
|
||||
`description` varchar(255) NOT NULL COMMENT '描述',
|
||||
`content` text NOT NULL 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) NOT 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=2 DEFAULT CHARSET=utf8 COMMENT='测试表格';
|
||||
|
||||
```
|
||||
|
||||
1.2 测试分类表`hg_test_category`:
|
||||
- 注意:该表为了方便功能演示已经内置无需再次创建,此处提示表结构只是为了方便大家梳理关联表关系
|
||||
```mysql
|
||||
CREATE TABLE `hg_test_category` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '分类ID',
|
||||
`name` varchar(255) NOT 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=10 DEFAULT CHARSET=utf8 COMMENT='测试分类';
|
||||
|
||||
```
|
||||
|
||||
1.3 插入测试数据
|
||||
```mysql
|
||||
INSERT INTO `hg_test_table` (`id`, `category_id`, `title`, `description`, `content`, `image`, `attachfile`, `city_id`, `switch`, `sort`, `status`, `created_by`, `updated_by`, `created_at`, `updated_at`, `deleted_at`) VALUES (1, 1, '测试标题', '描述', '<h2><strong>不知道写点啥!</strong></h2><p><br></p><iframe class=\"ql-video\" frameborder=\"0\" allowfullscreen=\"true\" src=\"https://media.w3.org/2010/05/sintel/trailer.mp4\"></iframe><p><br></p><p><img src=\"http://bufanyun.cn-bj.ufileos.com/hotgo/attachment/2023-02-09/cqdq9iuv0phsg8patk.png\"></p>', 'https://bufanyun.cn-bj.ufileos.com/hotgo/logo.sig.png', 'http://bufanyun.cn-bj.ufileos.com/hotgo/attachment/2022-12-30/cpf1x44idoycrtajf2.xlsx', 110102, 1, 10, 1, 0, 1, '2022-12-15 19:30:14', '2023-02-23 13:55:32', NULL);
|
||||
|
||||
```
|
||||
|
||||
|
||||
2、创建生成配置
|
||||
- 登录HotGo后台 -> 开发工具 -> 代码生成 -> 找到立即生成按钮并打开,选择和填写如下参数:
|
||||
|
||||
![生成添加演示图](images/sys-code-add.png)
|
||||
|
||||
|
||||
3、自定义配置
|
||||
- 确认配置无误后,点击生成配置会自动跳转到生成配置页面,如下:
|
||||
|
||||
![生成配置页面](images/sys-code-config-init.png)
|
||||
|
||||
- 你可以在该页面点击`预览代码`查看生成的内容,如果无需调整亦可以直接点击`提交生成`,以下是预览代码效果:
|
||||
|
||||
![生成预览](images/sys-code-preview.png)
|
||||
|
||||
- 如果你对默认的生成配置不满意,可以根据页面表单提示,自定义表格属性和字段属性
|
||||
|
||||
- 这里假设我们要一个关联表,让表`hg_test_table`字段`category_id`去关联 表`hg_test_category`字段`id`,我们可以这样做:
|
||||
|
||||
![生成关联配置](images/sys-code-config-join.png)
|
||||
|
||||
|
||||
- 如果你想调整主表每列字段的显示方式,可以点击上方导航栏中的 [主表字段] 进行调整
|
||||
- 这里我们不做任何调整直接进入下一步。目的是为了后续演示对最终生成结果不满意的再次调整方案
|
||||
|
||||
![生成关联配置](images/sys-code-master.png)
|
||||
|
||||
4、以上内容都已配置无误后,点击提交生成即可。
|
||||
|
||||
- 如果你使用的热编译,那么页面会在生成成功后立即刷新,刷新完成你即可在后台菜单栏中看到`测试表格`菜单。如果不是使用热编译启动,请手动重启服务后刷新。
|
||||
|
||||
> 注意:热编译自动刷新时,考虑到实际开发环境电脑配置不同,web端可能会优先于服务端重启加载完成,此时会出现新生成菜单没有加载出来或某接口请求超时问题,这是服务端正在启动中导致的,一般稍等几秒再试即可。
|
||||
|
||||
- 接下来让我们看看生成的表格页面,效果如下:
|
||||
|
||||
![生成测试表格页面](images/sys-code-list.png)
|
||||
|
||||
|
||||
5、假设我们对生成结果不满意,有新的优化需求如下:
|
||||
1. 把`单图`、`附件`改换成上传组件
|
||||
2. 把`创建者`、`更新者`隐藏
|
||||
3. 把`分类ID`隐藏,然后把关联表中的`分类名称`显示出来
|
||||
|
||||
> 那么我们可以回到 开发工具 -> 代码生成 -> 找到刚刚生成的记录 -> 在右侧操作栏中点击生成配置,再次进入配置页面做如下操作即可:
|
||||
|
||||
1. 点击上方导航栏[主表字段],调整字段选项:
|
||||
|
||||
![修改主表配置](images/sys-code-master-save.png)
|
||||
|
||||
|
||||
2. 点击上方导航栏[关联表],调整字段选项:
|
||||
|
||||
![修改关联表配置](images/sys-code-config-join-save.png)
|
||||
|
||||
|
||||
3. 返回[基本信息],勾选强制覆盖,然后点击`预览代码`,确认生成代码无误后点击`提交生成`
|
||||
|
||||
![强制覆盖并提交生成](images/sys-code-config-post.png)
|
||||
|
||||
|
||||
- 生成完成刷新页面后,再次来到`测试表格`,发现表格已经调整到了我们预期效果
|
||||
|
||||
1. 列表效果:
|
||||
|
||||
![最终列表效果](images/sys-code-list-ok.png)
|
||||
|
||||
2. 编辑表单效果
|
||||
|
||||
![最终编辑表单效果](images/sys-code-list-edit-ok.png)
|
||||
|
||||
|
||||
- 至此生成增删改查列表示例结束!
|
||||
|
||||
|
||||
### 内置gf-cli
|
||||
|
||||
> 由于gf版本更新较常出现向下不兼容的情况,所以我们为了保证生成代码的依赖稳定性,我们将gf-cli工具内置到了系统中并做了一些在线执行的调整。
|
||||
|
||||
- 我们会定期更新和gf最新版本生成功能保持一致,这样不论是你通过gf命令还是通过后台生成的代码格式都是一样的,遵循相同的代码规范和开发方式
|
||||
|
||||
- 后续我们也将开放在线运行`gf gen dao`、`gf gen service`功能。在做插件开发时也会支持到在线生成插件下的service接口,这将会使得插件开发更加方便
|
||||
|
||||
### 自定义生成模板
|
||||
|
||||
> 系统内置了两组CURD生成模板,请参考[生成配置]。default:是默认的生成到主模块下;addon:是默认生成到指定的插件下
|
||||
|
||||
- 如果你在实际的开发过程中默认模板需要调整的地方较多时,HotGo允许你新建新的模板分组来满足你的需求。新的模板可根据现有模板基础拷贝一份出来做改造,默认模板目录:[server/resource/generate/default](../../server/resource/generate/default)
|
||||
|
113
docs/guide-zh-CN/sys-db.md
Normal file
@ -0,0 +1,113 @@
|
||||
## 数据库
|
||||
|
||||
目录
|
||||
|
||||
- 字段类型
|
||||
- 特殊字段默认表单组件
|
||||
- 特殊字段默认表单验证器
|
||||
- SQL默认查询方式
|
||||
- 其他默认选项
|
||||
- 常见问题
|
||||
|
||||
### 字段类型
|
||||
|
||||
- 创建数据库表当按如下的规则进行字段命名、类型、属性设置和备注后,再生成CRUD代码时会自动生成对应的Api、控制器、业务逻辑、Web页面、[表单组件](web-form.md)等的一些默认属性
|
||||
- 当你了解这些默认技巧后,会有效提高你在实际开发中的生产效率
|
||||
|
||||
| 数据库类型 | 额外属性 | 转换Go类型 | 转换Ts类型 | 表单组件 |
|
||||
|---------------------------------------------------------------|--------------|--------------|---------|-----------------------|
|
||||
| int, tinyint,small_int,smallint,medium_int,mediumint,serial | / | int | number | InputNumber(数字输入框) |
|
||||
| int, tinyint,small_int,smallint,medium_int,mediumint,serial | unsigned | uint | number | InputNumber(数字输入框) |
|
||||
| big_int,bigint,bigserial | / | int64 | number | InputNumber(数字输入框) |
|
||||
| big_int,bigint,bigserial | unsigned | uint64 | number | InputNumber(数字输入框) |
|
||||
| real | / | float32 | number | InputNumber(数字输入框) |
|
||||
| float,double,decimal,money,numeric,smallmoney | / | float64 | number | InputNumber(数字输入框) |
|
||||
| bit(1) 、bit(true)、bit(false) | / | bool | boolean | Input(文本输入框,默认) |
|
||||
| bit | / | int64-bytes | array | InputDynamic(动态KV表单) |
|
||||
| bit | unsigned | uint64-bytes | array | InputDynamic (动态KV表单) |
|
||||
| bool | / | bool | boolean | Input(文本输入框,默认) |
|
||||
| date | / | *gtime.Time | string | Date(日期选择器) |
|
||||
| datetime,timestamp,timestamptz | / | *gtime.Time | string | Time(时间选择器) |
|
||||
| json | / | *gjson.Json | string | Input(文本输入框) |
|
||||
| jsonb | / | *gjson.Json | string | Input(文本输入框) |
|
||||
| 以下为物理类型中包含字段部分时的转换方式,默认情况 | / | / | / | / |
|
||||
| text,char,character | / | string | string | Input(文本输入框) |
|
||||
| float,double,numeric | / | string | string | Input(文本输入框) |
|
||||
| bool | / | bool | boolean | Input(文本输入框,默认) |
|
||||
| binary,blob | / | []byte | string | Input(文本输入框,默认) |
|
||||
| int | / | int | number | InputNumber(数字输入框) |
|
||||
| int | unsigned | int | number | InputNumber(数字输入框) |
|
||||
| time | / | *gtime.Time | string | Time(时间选择器) |
|
||||
| date | / | *gtime.Time | string | Date(日期选择器) |
|
||||
| 没有满足以上任何条件的 | / | string | string | Input(文本输入框) |
|
||||
|
||||
|
||||
### 特殊字段默认表单组件
|
||||
- 以下字段在不设置表单组件时会默认使用的表单组件
|
||||
|
||||
| 数据库字段 | 字段名称 | 表单组件 |
|
||||
|--------------|----------------------|----------------------|
|
||||
| status | 状态字段(任意int类型) | Select (单选下拉框) |
|
||||
| created_at | 创建时间字段 | TimeRange (时间范围选择) |
|
||||
| province_id | 省份字段(任意int类型) | CitySelector (省市区选择) |
|
||||
| city_id | 省份字段(任意int类型) | CitySelector (省市区选择) |
|
||||
| 任意字串符字段 | 长度>= 200 and <= 500 | InputTextarea (文本域) |
|
||||
| 任意字串符字段 | 长度> 500 | InputEditor (富文本) |
|
||||
|
||||
|
||||
### 特殊字段默认表单验证器
|
||||
- 以下字段在不设置表单组件时会默认使用的表单验证器
|
||||
|
||||
| 数据库字段/Go类型 | 字段名称 | 表单验证规则 |
|
||||
|-------------------|--------|-----------------------|
|
||||
| mobile | 手机号 | 不为空时必须是手机号码(国内) |
|
||||
| qq | QQ | 不为空时必须是QQ号码 |
|
||||
| email | 邮箱地址 | 不为空时必须是邮箱格式 |
|
||||
| id_card | 身份证号码 | 不为空时必须是15或18位身份证号码 |
|
||||
| bank_card | 银行卡号码 | 银行卡号码 |
|
||||
| password | 密码 | 密码验证,必须包含6-18为字母和数字 |
|
||||
| price | 价格 | 金额验证,最多允许输入10位整数及2位小数 |
|
||||
| Go类型为uint、uint64 | 正整数 | 非零正整数验证 |
|
||||
|
||||
### SQL默认查询方式
|
||||
- Go类型取决于数据库物理类型,请参考 [字段类型] 部分
|
||||
|
||||
| Go类型 | 查询方式 |
|
||||
|-------------------------|--------------------------------------|
|
||||
| string | LIKE |
|
||||
| date,datetime | = |
|
||||
| int,uint,int64,uint64 | = |
|
||||
| []int,[]int64,[]uint64 | IN (...) |
|
||||
| float32,float64 | = |
|
||||
| []byte4 | =(默认) |
|
||||
| time.Time,*gtime.Time | = |
|
||||
| *gjson.Json | JSON_CONTAINS(json_doc, val[, path]) |
|
||||
|
||||
|
||||
|
||||
### 其他默认选项
|
||||
|
||||
#### 默认字典选项
|
||||
|
||||
- 数据库字段为 `status`且类型为任意数字类型的会使用系统默认的状态字典
|
||||
|
||||
#### 默认属性
|
||||
|
||||
- 默认必填,当数据库字段存在非空`IS_NULLABLE`属性时,默认勾选必填验证
|
||||
- 默认唯一,当数据库字段属性存在`UNI`时,默认勾选唯一值验证
|
||||
- 默认主键,当数据库字段属性存在`PRI`时,默认为主键,不允许编辑
|
||||
- 默认最大排序,当数据库字段存在`sort`时,默认开启排序,添加表单自动获取最大排序增量值并填充表单
|
||||
- 默认列名,默认使用字段注释作为表格的列名。当数据库字段未设置注释时,默认使用字段名称作为列名
|
||||
|
||||
#### 自动更新/插入
|
||||
|
||||
- 自动更新,当数据库字段为`updated_at`(更新时间),`updated_by`(更新者)
|
||||
- 自动插入,当数据库字段为`created_at`(创建时间),`created_by`(创建者)
|
||||
- 软删除,表存在字段`deleted_at`时,使用表的Orm模型查询条件将会自动加入[ `deleted_at` IS NULL ],删除时只更新删除时间而不会真的删除数据
|
||||
- 树表:不论更新插入,都会根据表中字段`pid`(上级ID)自动维护`level`(树等级)和`tree`(关系树)
|
||||
|
||||
> 这里只列举了较为常用的默认规则,其他更多默认规则请参考:[server/internal/library/hggen/views/column_default.go](../../server/internal/library/hggen/views/column_default.go)
|
||||
|
||||
#### 常见问题
|
||||
|
||||
待补充。
|
197
docs/guide-zh-CN/sys-exploit.md
Normal file
@ -0,0 +1,197 @@
|
||||
## 开发规范
|
||||
|
||||
目录
|
||||
|
||||
- 规范说明
|
||||
- 规范建议
|
||||
- 命名规范
|
||||
- 编码规范
|
||||
- 框架规范
|
||||
- 插件规范
|
||||
- 数据库规范
|
||||
- 包功能规范
|
||||
|
||||
### 规范说明
|
||||
|
||||
- 开发规范意义是是让大家尽可能写出风格统一的代码,让团队代码有更好的可读性与可维护性。
|
||||
- 在实际业务开发中,除了要提高业务开发效率,保证线上业务高性能外,好的编程习惯也是一个开发人员基本素养之一。
|
||||
|
||||
|
||||
### 规范建议
|
||||
|
||||
- 在我们实际开发中,有很多开发人可能是由某一语言转到另外一个语言领域,在转到另外一门语言后, 我们都会保留着对旧语言的编程习惯,在这里,我建议的是,虽然不同语言之前的某些规范可能是相通的, 但是我们最好能够按照官方的一些demo来熟悉是渐渐适应当前语言的编程规范,而不是直接将原来语言的编程规范也随之迁移过来。
|
||||
|
||||
|
||||
### 命名规范
|
||||
|
||||
|
||||
#### 命名准则
|
||||
|
||||
* 当变量名称在定义和最后一次使用之间的距离很短时,简短的名称看起来会更好
|
||||
* 变量命名应尽量描述其内容,而不是类型
|
||||
* 常量命名应尽量描述其值,而不是如何使用这个值
|
||||
* 在遇到for,if等循环或分支时,推荐单个字母命名来标识参数和返回值
|
||||
* package名称也是命名的一部分,推荐全部使用小写,请尽量将其利用起来
|
||||
* 使用一致的命名风格
|
||||
|
||||
#### 文件命名规范
|
||||
* 全部小写
|
||||
* 除unit test外避免下划线(_)
|
||||
* 文件名称不宜过长,但不必为了简短而忽略含义
|
||||
|
||||
#### 变量命名规范参考
|
||||
* 驼峰命名
|
||||
* 见名知义,避免拼音替代英文
|
||||
* 不建议包含下划线(_)
|
||||
* 不建议包含数字
|
||||
|
||||
|
||||
#### 函数、常量命名规范
|
||||
* 驼峰式命名
|
||||
* 可导出的必须首字母大写
|
||||
* 不可导出的必须首字母小写
|
||||
* 避免全部大写与下划线(_)组合
|
||||
|
||||
|
||||
#### 参考文档
|
||||
|
||||
* [Practical Go: Real world advice for writing maintainable Go programs](https://dave.cheney.net/practical-go/presentations/gophercon-singapore-2019.html#_simplicity)
|
||||
|
||||
|
||||
### 编码规范
|
||||
|
||||
#### import
|
||||
* 单行import不建议用圆括号包裹
|
||||
* 按照`官方包`,NEW LINE,`当前工程包`,NEW LINE,`第三方依赖包`顺序引入
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
"string"
|
||||
|
||||
"greet/user/internal/config"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
```
|
||||
|
||||
#### 函数返回
|
||||
* 对象避免非指针返回
|
||||
* 遵循有正常值返回则一定无error,有error则一定无正常值返回的原则
|
||||
|
||||
#### 错误处理
|
||||
* 有error必须处理,如果不能处理就必须抛出。
|
||||
* 避免下划线(_)接收error
|
||||
|
||||
#### 函数体编码
|
||||
* 建议一个block结束空一行,如if、for等
|
||||
```go
|
||||
func main (){
|
||||
if x==1{
|
||||
// do something
|
||||
}
|
||||
|
||||
fmt.println("xxx")
|
||||
}
|
||||
```
|
||||
* return前尽可能空一行
|
||||
```go
|
||||
func getUser(id string)(string,error){
|
||||
....
|
||||
|
||||
return "xx",nil
|
||||
}
|
||||
```
|
||||
|
||||
### 框架规范
|
||||
|
||||
- 项目启动初始化方法都放在`server/internal/global/init.go`,插件放在:`server/addons/xxx插件/global/init.go`
|
||||
- Api 对外接口层 都放在`server/api`,根据实际应用和应用具体功能划分子目录,插件放在:`server/addons/xxx插件/api`
|
||||
- Router 路由注册方式应尽可能按gf2.0版本风格定义,参考:https://goframe.org/pages/viewpage.action?pageId=30736904
|
||||
- Controller 控制器层 都放在`server/internal/controller`,根据应用和具体功能拆分子目录,插件放在:`server/addons/xxx插件/controller`
|
||||
- Input 对内接口层 都放在`server/internal/model/input`,根据应用和具体功能拆分子目录并以`in`结尾,插件放在:`server/addons/xxx插件/model/input`
|
||||
- Logic 业务逻辑层 统一放在`server/internal/logic` 根据实际业务需求按logic/xxx/*.go拆分,插件放在:`server/addons/xxx插件/logic`
|
||||
- Orm 模型实体层 统一放在`server/internal/model/entity`
|
||||
- Dao 数据访问层 统一放在`server/internal/dao`
|
||||
- 自定义工具函数都放在 `server/utility`,且应根据不同类型的方法做分包,相同类型的方法放在同一个包内
|
||||
- 状态枚举统一调用`server/internal/consts`中的常量和属性
|
||||
|
||||
### 插件规范
|
||||
|
||||
- 插件命名统一全部小写
|
||||
- 资源文件在 `addons/xxx插件/resource`或`server/resource`
|
||||
- 每个插件之前应该是完全独立的,尽可能避免插件于插件之间的调用依赖
|
||||
- 插件和主模块之前的调用应尽可能通过 Service 接口来调用
|
||||
- 插件业务初始化顺序下应在主模块之后,应尽可能避免使用init方法隐式初始化
|
||||
|
||||
### 数据库规范
|
||||
|
||||
#### 命名规范
|
||||
|
||||
- 数据表名小写,多关键字使用下划线分割(关键字尽量全称)
|
||||
- 字段名小写,多关键字使用下划线分割(关键字尽量全称)
|
||||
- 禁止使用保留字并且尽量少用含有关键词来命名
|
||||
- 临时表必须以tmp_开头、以日期结尾,备份表必须以bak_开头、以日期结尾
|
||||
- 插件模块表名建议以`hg_addon_`开头,如:`hg_addon_hgexample_table`,含义:`插件_案例_表格`。在生成代码时可自动识别实体命名为:`Table`
|
||||
|
||||
#### 基础规范
|
||||
|
||||
- 所有的字段必须添加注释
|
||||
- 尽可能地使用InnoDB作为表的存储引擎
|
||||
- 所有的表和字段必须添加注释
|
||||
- 尽量控制表行数在500万以内
|
||||
- 尽可能采用冷热数据分离策略
|
||||
- 禁止以图片、文件等二进制数据
|
||||
|
||||
#### 表设计规范
|
||||
|
||||
- 尽可能每张表的索引数量控制在5个以内
|
||||
- 每一张InnoDB表都必须含有一个主键,自增主键必须是`bigint`类型
|
||||
- 尽可能避免使用外键约束
|
||||
- 设置数据表架构应考虑后期扩展型
|
||||
- 遵循范式与冗余平衡原则
|
||||
|
||||
#### 字段设计规范
|
||||
|
||||
- 尽可能将所有的数据列定义为 `NOT NULL` 类型
|
||||
- 避免 ENUM 数据类型
|
||||
- json 存储的数据用 `json`字段代替 `text`
|
||||
- 表与表关联的键名保持一致或以关联表名的缩写为前缀
|
||||
- 固定长度的字符串字段务必使用 `char`
|
||||
- 使用 `UNSIGNEG` 存储非负整数
|
||||
- 禁止敏感数据以明文形式存储
|
||||
- 金额相关的数据必须使用 `DECIMAL` 数据类型
|
||||
- 尽量所有表有 `status` 字段来标注数据状态(1:启用,2:禁用,3:已删除),业务状态请使用其他字段;`status`字段类型 为带符号的 `tinyint(1)`。如果还需要其他的数据状态 请先判断该状态的数据是有用的数据还是无意义的数据,有用的数据状态 > 0,无意义的数据状态 < -1
|
||||
- 所有的删除(除开清空回收站操作) 请标记 `status` 为 3
|
||||
- 创建时间字段为`created_at`,修改时间字段为`updated_at`,删除时间字段为`deleted_at`,类型`datetime`
|
||||
- 用户关联 id 字段为 `member_id` 或 `created_by`
|
||||
- 排序字段为 `sort`
|
||||
- 区分应用字段为 `app_id`
|
||||
- 区分插件来源需要增加字段为 `addon_name` 和 `is_addon`
|
||||
- 关系树表中必须包含字段:`id`(主键)、`pid`(上级ID)、`level`(树等级)、`tree`(关系树)
|
||||
- 多选、多图、多文件字段类型应尽量使用类型`json`
|
||||
|
||||
### 包功能规范
|
||||
|
||||
##### Input(对内接口 - 过滤验证)
|
||||
|
||||
- 接收Api 对外接口数据和参数过滤
|
||||
- 对提交的复杂数据参数进行验证处理返回
|
||||
|
||||
##### View(视图)
|
||||
|
||||
- 数据处理显示
|
||||
- 页面渲染
|
||||
|
||||
##### Controller(控制器)
|
||||
|
||||
- 数据接收
|
||||
- 服务调用
|
||||
- 简单CURD入库
|
||||
- 简单业务逻辑
|
||||
|
||||
|
||||
##### Logic(业务逻辑 - 服务接口功能)
|
||||
|
||||
- 数据逻辑处理
|
||||
- 数据入库
|
||||
- 数据查询
|
@ -3,7 +3,7 @@
|
||||
目录
|
||||
|
||||
- 缓存驱动
|
||||
- 上下文(待写)
|
||||
- 请求上下文(待写)
|
||||
- JWT(待写)
|
||||
- 地理定位(待写)
|
||||
- 通知(待写)
|
||||
@ -48,9 +48,42 @@ func test() {
|
||||
|
||||
```
|
||||
|
||||
### 上下文
|
||||
### 请求上下文
|
||||
|
||||
- 主要用于在处理HTTP和websocket请求时通过中间件将用户、应用、插件等信息绑定到上下文中,方便在做业务处理时用到这些信息
|
||||
|
||||
```go
|
||||
// 待写
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"context"
|
||||
"hotgo/internal/library/contexts"
|
||||
"hotgo/internal/library/addons"
|
||||
)
|
||||
|
||||
|
||||
func test(ctx context.Context) {
|
||||
// 获取当前请求的所有上下文变量
|
||||
var ctxModel = contexts.Get(ctx)
|
||||
fmt.Printf("当前请求的所有上下文变量:%+v\n", ctxModel)
|
||||
|
||||
// 获取当前请求的应用模块
|
||||
var module = contexts.GetModule(ctx)
|
||||
fmt.Printf("当前请求的应用:%+v\n", module)
|
||||
|
||||
// 获取当前请求的用户信息
|
||||
var member = contexts.GetUser(ctx)
|
||||
fmt.Printf("当前访问用户信息:%+v\n", member)
|
||||
|
||||
// 获取当前请求的插件模块
|
||||
fmt.Printf("当前是否为插件请求:%v", contexts.IsAddonRequest(ctx))
|
||||
if contexts.IsAddonRequest(ctx) {
|
||||
fmt.Printf("当前插件名称:%v", contexts.GetAddonName(ctx))
|
||||
fmt.Printf("当前插件信息:%v", addons.GetModule(contexts.GetAddonName(ctx)))
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### JWT
|
||||
|
@ -44,7 +44,7 @@ queue:
|
||||
|
||||
### 一个例子
|
||||
|
||||
每个被发送到队列的消息应该被定义为一个文件的结构。
|
||||
每个被发送到队列的消息应该被定义为一个单独的文件结构。
|
||||
|
||||
例如,如果您需要异步记录系统日志,内容大致如下:
|
||||
|
||||
@ -63,7 +63,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
jobList = append(jobList, SysLog)
|
||||
queue.RegisterConsumer(SysLog)
|
||||
}
|
||||
|
||||
// SysLog 系统日志
|
||||
@ -71,13 +71,13 @@ var SysLog = &qSysLog{}
|
||||
|
||||
type qSysLog struct{}
|
||||
|
||||
// getTopic 主题
|
||||
func (q *qSysLog) getTopic() string {
|
||||
// GetTopic 主题
|
||||
func (q *qSysLog) GetTopic() string {
|
||||
return consts.QueueLogTopic
|
||||
}
|
||||
|
||||
// handle 处理消息
|
||||
func (q *qSysLog) handle(ctx context.Context, mqMsg queue.MqMsg) (err error) {
|
||||
// Handle 处理消息
|
||||
func (q *qSysLog) Handle(ctx context.Context, mqMsg queue.MqMsg) (err error) {
|
||||
var data entity.SysLog
|
||||
if err = json.Unmarshal(mqMsg.Body, &data); err != nil {
|
||||
return err
|
||||
@ -85,7 +85,6 @@ func (q *qSysLog) handle(ctx context.Context, mqMsg queue.MqMsg) (err error) {
|
||||
return service.SysLog().RealWrite(ctx, data)
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
下面是将消息添加到队列的方式,大概内容如下:
|
||||
@ -101,7 +100,9 @@ import (
|
||||
)
|
||||
|
||||
func test() {
|
||||
data := &entity.SysLog{}
|
||||
data := &entity.SysLog{
|
||||
//...
|
||||
}
|
||||
if err := queue.Push(consts.QueueLogTopic, data); err != nil {
|
||||
fmt.Printf("queue.Push err:%+v", err)
|
||||
}
|
||||
|
261
docs/guide-zh-CN/web-deploy.md
Normal file
@ -0,0 +1,261 @@
|
||||
## 独立部署
|
||||
|
||||
目录
|
||||
|
||||
- 构建
|
||||
- 旧版浏览器兼容
|
||||
- 预览
|
||||
- 分析构建文件体积
|
||||
- 压缩
|
||||
- 部署
|
||||
|
||||
> 在实际开发中,web前端也可能需要独立部署,所以在此提供一下部署方案。
|
||||
|
||||
### 构建
|
||||
|
||||
项目开发完成之后,执行以下命令进行构建
|
||||
```shell
|
||||
yarn build
|
||||
```
|
||||
构建打包成功之后,会在根目录生成 dist 文件夹,里面就是构建打包好的文件
|
||||
|
||||
|
||||
### 旧版浏览器兼容
|
||||
|
||||
在 .env.production 内
|
||||
|
||||
设置 `VITE_LEGACY=true` 即可打包出兼容旧版浏览器的代码
|
||||
```shell
|
||||
VITE_LEGACY = true
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 预览
|
||||
|
||||
发布之前可以在本地进行预览,有多种方式,这里介绍两种
|
||||
|
||||
##### 不能直接打开构建后的 html 文件
|
||||
|
||||
使用项目自定的命令进行预览(推荐)
|
||||
```shell
|
||||
# 先打包在进行预览
|
||||
yarn preview
|
||||
# 直接预览本地 dist 文件目录
|
||||
yarn preview:dist
|
||||
```
|
||||
|
||||
- 本地服务器预览(通过 live-server)
|
||||
```shell
|
||||
# 1.全局安装live-server
|
||||
yarn global add live-server
|
||||
# 2. 进入打包的后目录
|
||||
cd ./dist
|
||||
# 本地预览,默认端口8080
|
||||
live-server
|
||||
# 指定端口
|
||||
live-server --port 9000
|
||||
```
|
||||
|
||||
### 分析构建文件体积
|
||||
如果你的构建文件很大,可以通过项目内置 [rollup-plugin-analyzer](https://github.com/doesdev/rollup-plugin-analyzer) 插件进行代码体积分析,从而优化你的代码。
|
||||
```shell
|
||||
yarn report
|
||||
```
|
||||
运行之后,在自动打开的页面可以看到具体的体积分布,以分析哪些依赖有问题。
|
||||
|
||||
- 左上角可以切换 显示 gzip 或者 brotli
|
||||
|
||||
![start-deploy-report.png](./images/start-deploy-report.png)
|
||||
|
||||
|
||||
### 压缩
|
||||
|
||||
#### 开启 gzip 压缩
|
||||
|
||||
开启 gzip,并配合 nginx 的 `gzip_static` 功能可以大大加快页面访问速度
|
||||
|
||||
- 只需开启 `VITE_BUILD_COMPRESS='gzip'` 即可在打包的同时生成 .gz 文件
|
||||
|
||||
```shell
|
||||
# 根据自己路径来配置更改
|
||||
# 例如部署在nginx /next/路径下 则VITE_PUBLIC_PATH=/next/
|
||||
VITE_PUBLIC_PATH=/
|
||||
```
|
||||
|
||||
#### 开启 brotli 压缩
|
||||
|
||||
brotli 是比 gzip 压缩率更高的算法,可以与 gzip 共存不会冲突,需要 nginx 安装指定模块并开启即可。
|
||||
|
||||
- 只需开启 VITE_BUILD_COMPRESS='brotli' 即可在打包的同时生成 .br 文件
|
||||
|
||||
```shell
|
||||
# 根据自己路径来配置更改
|
||||
# 例如部署在nginx /next/路径下 则VITE_PUBLIC_PATH=/next/
|
||||
VITE_PUBLIC_PATH=/
|
||||
```
|
||||
|
||||
|
||||
#### 同时开启 gzip 与 brotli
|
||||
|
||||
只需开启 VITE_BUILD_COMPRESS='brotli,gzip' 即可在打包的同时生成 .gz 和 .br 文件。
|
||||
|
||||
|
||||
#### gzip 与 brotli 在 nginx 内的配置
|
||||
```
|
||||
http {
|
||||
# 开启gzip
|
||||
gzip on;
|
||||
# 开启gzip_static
|
||||
# gzip_static 开启后可能会报错,需要安装相应的模块, 具体安装方式可以自行查询
|
||||
# 只有这个开启,vue文件打包的.gz文件才会有效果,否则不需要开启gzip进行打包
|
||||
gzip_static on;
|
||||
gzip_proxied any;
|
||||
gzip_min_length 1k;
|
||||
gzip_buffers 4 16k;
|
||||
#如果nginx中使用了多层代理 必须设置这个才可以开启gzip。
|
||||
gzip_http_version 1.0;
|
||||
gzip_comp_level 2;
|
||||
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
|
||||
gzip_vary off;
|
||||
gzip_disable "MSIE [1-6]\.";
|
||||
|
||||
# 开启 brotli压缩
|
||||
# 需要安装对应的nginx模块,具体安装方式可以自行查询
|
||||
# 可以与gzip共存不会冲突
|
||||
brotli on;
|
||||
brotli_comp_level 6;
|
||||
brotli_buffers 16 8k;
|
||||
brotli_min_length 20;
|
||||
brotli_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 部署
|
||||
|
||||
- 注意:项目默认是在生产环境开启 Mock,这样做非常不好,只是为了演示环境有数据,不建议在生产环境使用 Mock,而应该使用真实的后台接口,并将 Mock 关闭。
|
||||
|
||||
#### 发布
|
||||
|
||||
简单的部署只需要将最终生成的静态文件,dist 文件夹的静态文件发布到你的 cdn 或者静态服务器即可,需要注意的是其中的 index.html 通常会是你后台服务的入口页面,在确定了 js 和 css 的静态之后可能需要改变页面的引入路径。
|
||||
|
||||
例如上传到 nginx `/srv/www/project/index.html`
|
||||
|
||||
```
|
||||
# nginx配置
|
||||
location / {
|
||||
# 不缓存html,防止程序更新后缓存继续生效
|
||||
if ($request_filename ~* .*\.(?:htm|html)$) {
|
||||
add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";
|
||||
access_log on;
|
||||
}
|
||||
# 这里是vue打包文件dist内的文件的存放路径
|
||||
root /srv/www/project/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
部署时可能会发现资源路径不对,只需要修改`.env.production`文件即可。
|
||||
|
||||
```shell
|
||||
# 根据自己路径来配置更改
|
||||
# 注意需要以 / 开头和结尾
|
||||
VITE_PUBLIC_PATH=/
|
||||
VITE_PUBLIC_PATH=/xxx/
|
||||
|
||||
```
|
||||
|
||||
#### 前端路由与服务端的结合
|
||||
|
||||
项目前端路由使用的是 vue-router,所以你可以选择两种方式:history 和 hash。
|
||||
|
||||
- hash 默认会在 url 后面拼接#
|
||||
- history 则不会,不过 history 需要服务器配合
|
||||
|
||||
可在 src/router/index.ts 内进行 mode 修改
|
||||
|
||||
```vue
|
||||
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router';
|
||||
|
||||
createRouter({
|
||||
history: createWebHashHistory(),
|
||||
// or
|
||||
history: createWebHistory(),
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
#### history 路由模式下服务端配置
|
||||
|
||||
开启 history 模式需要服务器配置,更多的服务器配置详情可以看 [history-mode](https://router.vuejs.org/guide/essentials/history-mode.html#html5-mode)
|
||||
|
||||
这里以 nginx 配置为例
|
||||
|
||||
|
||||
##### 部署到根目录
|
||||
```
|
||||
server {
|
||||
listen 80;
|
||||
location / {
|
||||
# 用于配合 History 使用
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 部署到非根目录
|
||||
|
||||
1. 首先需要在打包的时候更改配置
|
||||
|
||||
```shell
|
||||
# 在.env.production内,配置子目录路径
|
||||
VITE_PUBLIC_PATH = /sub/
|
||||
```
|
||||
|
||||
```
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
location /sub/ {
|
||||
# 这里是vue打包文件dist内的文件的存放路径
|
||||
alias /srv/www/project/;
|
||||
index index.html index.htm;
|
||||
try_files $uri $uri/ /sub/index.html;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用 nginx 处理跨域
|
||||
|
||||
使用 nginx 处理项目部署后的跨域问题
|
||||
|
||||
1. 配置前端项目接口地址
|
||||
|
||||
```
|
||||
# 在.env.production内,配置接口地址
|
||||
VITE_GLOB_API_URL=/api
|
||||
```
|
||||
|
||||
2. 在 nginx 配置请求转发到后台
|
||||
|
||||
```
|
||||
server {
|
||||
listen 8080;
|
||||
server_name localhost;
|
||||
# 接口代理,用于解决跨域问题
|
||||
location /api {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# 后台接口地址
|
||||
proxy_pass http://110.110.1.1:8080/api;
|
||||
proxy_redirect default;
|
||||
add_header Access-Control-Allow-Origin *;
|
||||
add_header Access-Control-Allow-Headers X-Requested-With;
|
||||
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
|
||||
}
|
||||
}
|
||||
```
|
821
docs/guide-zh-CN/web-form.md
Normal file
@ -0,0 +1,821 @@
|
||||
## 表单组件
|
||||
|
||||
目录
|
||||
|
||||
- 文本输入 Input
|
||||
- 数字输入 Input Number
|
||||
- 文本域 InputTextarea
|
||||
- 富文本 InputEditor
|
||||
- 动态键值对 InputDynamic
|
||||
- 日期选择 Date(Y-M-D)
|
||||
- 日期范围选择 DateRange
|
||||
- 时间选择 Time(Y-M-D H:i:s)
|
||||
- 时间范围选择 TimeRange
|
||||
- 单选按钮 Radio
|
||||
- 复选框 Checkbox
|
||||
- 单选下拉框 Select
|
||||
- 多选下拉框 SelectMultiple
|
||||
- 树型选择 Tree Select
|
||||
- 单图上传 UploadImage
|
||||
- 多图上传 UploadImage
|
||||
- 单文件上传 UploadFile
|
||||
- 多文件上传 UploadFile
|
||||
- 开关 Switch
|
||||
- 评分 Rate
|
||||
- 省市区选择器 CitySelector
|
||||
- 图标选择器 IconSelector
|
||||
|
||||
### 文本输入 Input
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<n-input v-model:value="value" type="text" placeholder="基本的 Input" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
const value = ref(null);
|
||||
</script>
|
||||
```
|
||||
|
||||
### 数字输入 Input Number
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<n-input-number v-model:value="value" clearable />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
const value = ref(null);
|
||||
</script>
|
||||
```
|
||||
|
||||
### 文本域 InputTextarea
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<n-input
|
||||
v-model:value="value"
|
||||
type="textarea"
|
||||
placeholder="基本的 Textarea"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
const value = ref(null);
|
||||
</script>
|
||||
```
|
||||
|
||||
### 富文本 InputEditor
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<Editor style="height: 450px" v-model:value="value" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import Editor from '@/components/Editor/editor.vue';
|
||||
const value = ref(null);
|
||||
</script>
|
||||
```
|
||||
|
||||
### 动态键值对 InputDynamic
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<n-dynamic-input
|
||||
v-model:value="value"
|
||||
preset="pair"
|
||||
key-placeholder="键名"
|
||||
value-placeholder="键值"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
const value = ref(null);
|
||||
</script>
|
||||
```
|
||||
|
||||
### 日期选择 Date(Y-M-D)
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<DatePicker v-model:formValue="value" type="date" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import DatePicker from '@/components/DatePicker/datePicker.vue';
|
||||
const value = ref(null);
|
||||
</script>
|
||||
```
|
||||
|
||||
### 日期范围选择 DateRange
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<DatePicker
|
||||
v-model:startValue="startValue"
|
||||
v-model:endValue="endValue"
|
||||
type="datetimerange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import DatePicker from '@/components/DatePicker/datePicker.vue';
|
||||
const startValue = ref(null);
|
||||
const endValue = ref(null);
|
||||
</script>
|
||||
```
|
||||
|
||||
### 时间选择 Time(Y-M-D H:i:s)
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<n-time-picker :default-formatted-value="value" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
const value = ref(null);
|
||||
</script>
|
||||
```
|
||||
|
||||
### 时间范围选择 TimeRange
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<template>
|
||||
<n-space>
|
||||
<n-time-picker :default-value="startValue" />
|
||||
<n-time-picker :default-value="endValue" />
|
||||
</n-space>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
const startValue = ref(null);
|
||||
const endValue = ref(null);
|
||||
</script>
|
||||
```
|
||||
|
||||
### 单选按钮 Radio
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<n-space vertical>
|
||||
<n-radio-group v-model:value="value" name="radiobuttongroup1">
|
||||
<n-radio-button
|
||||
v-for="song in songs"
|
||||
:key="song.value"
|
||||
:value="song.value"
|
||||
:disabled="
|
||||
(song.label === 'Live Forever' && disabled1) ||
|
||||
(song.label === 'Shakermaker' && disabled2)
|
||||
"
|
||||
:label="song.label"
|
||||
/>
|
||||
</n-radio-group>
|
||||
<n-space>
|
||||
<n-checkbox v-model:checked="disabled2" style="margin-right: 12px">
|
||||
禁用 Shakemaker
|
||||
</n-checkbox>
|
||||
<n-checkbox v-model:checked="disabled1">
|
||||
禁用 Live Forever
|
||||
</n-checkbox>
|
||||
</n-space>
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
return {
|
||||
value: ref(null),
|
||||
disabled2: ref(false),
|
||||
disabled1: ref(false),
|
||||
songs: [
|
||||
{
|
||||
value: "Rock'n'Roll Star",
|
||||
label: "Rock'n'Roll Star"
|
||||
},
|
||||
{
|
||||
value: 'Shakermaker',
|
||||
label: 'Shakermaker'
|
||||
},
|
||||
{
|
||||
value: 'Live Forever',
|
||||
label: 'Live Forever'
|
||||
},
|
||||
{
|
||||
value: 'Up in the Sky',
|
||||
label: 'Up in the Sky'
|
||||
},
|
||||
{
|
||||
value: '...',
|
||||
label: '...'
|
||||
}
|
||||
].map((s) => {
|
||||
s.value = s.value.toLowerCase()
|
||||
return s
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
### 复选框 Checkbox
|
||||
```vue
|
||||
<template>
|
||||
<n-space item-style="display: flex;" align="center">
|
||||
<n-checkbox v-model:checked="value">
|
||||
复选框
|
||||
</n-checkbox>
|
||||
<n-checkbox v-model:checked="value" />
|
||||
<n-checkbox v-model:checked="value" :disabled="disabled">
|
||||
复选框
|
||||
</n-checkbox>
|
||||
<n-button size="small" @click="disabled = !disabled">
|
||||
禁用
|
||||
</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
return {
|
||||
value: ref(false),
|
||||
disabled: ref(true)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
### 单选下拉框 Select
|
||||
```vue
|
||||
<template>
|
||||
<n-space vertical>
|
||||
<n-select v-model:value="value" :options="options" />
|
||||
<n-select v-model:value="value" disabled :options="options" />
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
return {
|
||||
value: ref(null),
|
||||
options: [
|
||||
{
|
||||
label: "Everybody's Got Something to Hide Except Me and My Monkey",
|
||||
value: 'song0',
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
label: 'Drive My Car',
|
||||
value: 'song1'
|
||||
},
|
||||
{
|
||||
label: 'Norwegian Wood',
|
||||
value: 'song2'
|
||||
},
|
||||
{
|
||||
label: "You Won't See",
|
||||
value: 'song3',
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
label: 'Nowhere Man',
|
||||
value: 'song4'
|
||||
},
|
||||
{
|
||||
label: 'Think For Yourself',
|
||||
value: 'song5'
|
||||
},
|
||||
{
|
||||
label: 'The Word',
|
||||
value: 'song6'
|
||||
},
|
||||
{
|
||||
label: 'Michelle',
|
||||
value: 'song7',
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
label: 'What goes on',
|
||||
value: 'song8'
|
||||
},
|
||||
{
|
||||
label: 'Girl',
|
||||
value: 'song9'
|
||||
},
|
||||
{
|
||||
label: "I'm looking through you",
|
||||
value: 'song10'
|
||||
},
|
||||
{
|
||||
label: 'In My Life',
|
||||
value: 'song11'
|
||||
},
|
||||
{
|
||||
label: 'Wait',
|
||||
value: 'song12'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
### 多选下拉框 SelectMultiple
|
||||
```vue
|
||||
<template>
|
||||
<n-space vertical>
|
||||
<n-select v-model:value="value" multiple :options="options" />
|
||||
<n-select v-model:value="value" multiple disabled :options="options" />
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
return {
|
||||
value: ref(['song3']),
|
||||
options: [
|
||||
{
|
||||
label: "Everybody's Got Something to Hide Except Me and My Monkey",
|
||||
value: 'song0',
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
label: 'Drive My Car',
|
||||
value: 'song1'
|
||||
},
|
||||
{
|
||||
label: 'Norwegian Wood',
|
||||
value: 'song2'
|
||||
},
|
||||
{
|
||||
label: "You Won't See",
|
||||
value: 'song3',
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
label: 'Nowhere Man',
|
||||
value: 'song4'
|
||||
},
|
||||
{
|
||||
label: 'Think For Yourself',
|
||||
value: 'song5'
|
||||
},
|
||||
{
|
||||
label: 'The Word',
|
||||
value: 'song6'
|
||||
},
|
||||
{
|
||||
label: 'Michelle',
|
||||
value: 'song7',
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
label: 'What goes on',
|
||||
value: 'song8'
|
||||
},
|
||||
{
|
||||
label: 'Girl',
|
||||
value: 'song9'
|
||||
},
|
||||
{
|
||||
label: "I'm looking through you",
|
||||
value: 'song10'
|
||||
},
|
||||
{
|
||||
label: 'In My Life',
|
||||
value: 'song11'
|
||||
},
|
||||
{
|
||||
label: 'Wait',
|
||||
value: 'song12'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
### 树型选择 Tree Select
|
||||
```vue
|
||||
<template>
|
||||
<n-tree-select
|
||||
:options="options"
|
||||
default-value="Drive My Car"
|
||||
@update:value="handleUpdateValue"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { TreeSelectOption } from 'naive-ui'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
return {
|
||||
handleUpdateValue (
|
||||
value: string | number | Array<string | number> | null,
|
||||
option: TreeSelectOption | null | Array<TreeSelectOption | null>
|
||||
) {
|
||||
console.log(value, option)
|
||||
},
|
||||
options: [
|
||||
{
|
||||
label: 'Rubber Soul',
|
||||
key: 'Rubber Soul',
|
||||
children: [
|
||||
{
|
||||
label:
|
||||
"Everybody's Got Something to Hide Except Me and My Monkey",
|
||||
key: "Everybody's Got Something to Hide Except Me and My Monkey"
|
||||
},
|
||||
{
|
||||
label: 'Drive My Car',
|
||||
key: 'Drive My Car',
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
label: 'Norwegian Wood',
|
||||
key: 'Norwegian Wood'
|
||||
},
|
||||
{
|
||||
label: "You Won't See",
|
||||
key: "You Won't See",
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
label: 'Nowhere Man',
|
||||
key: 'Nowhere Man'
|
||||
},
|
||||
{
|
||||
label: 'Think For Yourself',
|
||||
key: 'Think For Yourself'
|
||||
},
|
||||
{
|
||||
label: 'The Word',
|
||||
key: 'The Word'
|
||||
},
|
||||
{
|
||||
label: 'Michelle',
|
||||
key: 'Michelle',
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
label: 'What goes on',
|
||||
key: 'What goes on'
|
||||
},
|
||||
{
|
||||
label: 'Girl',
|
||||
key: 'Girl'
|
||||
},
|
||||
{
|
||||
label: "I'm looking through you",
|
||||
key: "I'm looking through you"
|
||||
},
|
||||
{
|
||||
label: 'In My Life',
|
||||
key: 'In My Life'
|
||||
},
|
||||
{
|
||||
label: 'Wait',
|
||||
key: 'Wait'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Let It Be',
|
||||
key: 'Let It Be Album',
|
||||
children: [
|
||||
{
|
||||
label: 'Two Of Us',
|
||||
key: 'Two Of Us'
|
||||
},
|
||||
{
|
||||
label: 'Dig A Pony',
|
||||
key: 'Dig A Pony'
|
||||
},
|
||||
{
|
||||
label: 'Across The Universe',
|
||||
key: 'Across The Universe'
|
||||
},
|
||||
{
|
||||
label: 'I Me Mine',
|
||||
key: 'I Me Mine'
|
||||
},
|
||||
{
|
||||
label: 'Dig It',
|
||||
key: 'Dig It'
|
||||
},
|
||||
{
|
||||
label: 'Let It Be',
|
||||
key: 'Let It Be'
|
||||
},
|
||||
{
|
||||
label: 'Maggie Mae',
|
||||
key: 'Maggie Mae'
|
||||
},
|
||||
{
|
||||
label: "I've Got A Feeling",
|
||||
key: "I've Got A Feeling"
|
||||
},
|
||||
{
|
||||
label: 'One After 909',
|
||||
key: 'One After 909'
|
||||
},
|
||||
{
|
||||
label: 'The Long And Winding Road',
|
||||
key: 'The Long And Winding Road'
|
||||
},
|
||||
{
|
||||
label: 'For You Blue',
|
||||
key: 'For You Blue'
|
||||
},
|
||||
{
|
||||
label: 'Get Back',
|
||||
key: 'Get Back'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
</script><template>
|
||||
<n-tree-select
|
||||
:options="options"
|
||||
default-value="Drive My Car"
|
||||
@update:value="handleUpdateValue"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { TreeSelectOption } from 'naive-ui'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
return {
|
||||
handleUpdateValue (
|
||||
value: string | number | Array<string | number> | null,
|
||||
option: TreeSelectOption | null | Array<TreeSelectOption | null>
|
||||
) {
|
||||
console.log(value, option)
|
||||
},
|
||||
options: [
|
||||
{
|
||||
label: 'Rubber Soul',
|
||||
key: 'Rubber Soul',
|
||||
children: [
|
||||
{
|
||||
label:
|
||||
"Everybody's Got Something to Hide Except Me and My Monkey",
|
||||
key: "Everybody's Got Something to Hide Except Me and My Monkey"
|
||||
},
|
||||
{
|
||||
label: 'Drive My Car',
|
||||
key: 'Drive My Car',
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
label: 'Norwegian Wood',
|
||||
key: 'Norwegian Wood'
|
||||
},
|
||||
{
|
||||
label: "You Won't See",
|
||||
key: "You Won't See",
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
label: 'Nowhere Man',
|
||||
key: 'Nowhere Man'
|
||||
},
|
||||
{
|
||||
label: 'Think For Yourself',
|
||||
key: 'Think For Yourself'
|
||||
},
|
||||
{
|
||||
label: 'The Word',
|
||||
key: 'The Word'
|
||||
},
|
||||
{
|
||||
label: 'Michelle',
|
||||
key: 'Michelle',
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
label: 'What goes on',
|
||||
key: 'What goes on'
|
||||
},
|
||||
{
|
||||
label: 'Girl',
|
||||
key: 'Girl'
|
||||
},
|
||||
{
|
||||
label: "I'm looking through you",
|
||||
key: "I'm looking through you"
|
||||
},
|
||||
{
|
||||
label: 'In My Life',
|
||||
key: 'In My Life'
|
||||
},
|
||||
{
|
||||
label: 'Wait',
|
||||
key: 'Wait'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Let It Be',
|
||||
key: 'Let It Be Album',
|
||||
children: [
|
||||
{
|
||||
label: 'Two Of Us',
|
||||
key: 'Two Of Us'
|
||||
},
|
||||
{
|
||||
label: 'Dig A Pony',
|
||||
key: 'Dig A Pony'
|
||||
},
|
||||
{
|
||||
label: 'Across The Universe',
|
||||
key: 'Across The Universe'
|
||||
},
|
||||
{
|
||||
label: 'I Me Mine',
|
||||
key: 'I Me Mine'
|
||||
},
|
||||
{
|
||||
label: 'Dig It',
|
||||
key: 'Dig It'
|
||||
},
|
||||
{
|
||||
label: 'Let It Be',
|
||||
key: 'Let It Be'
|
||||
},
|
||||
{
|
||||
label: 'Maggie Mae',
|
||||
key: 'Maggie Mae'
|
||||
},
|
||||
{
|
||||
label: "I've Got A Feeling",
|
||||
key: "I've Got A Feeling"
|
||||
},
|
||||
{
|
||||
label: 'One After 909',
|
||||
key: 'One After 909'
|
||||
},
|
||||
{
|
||||
label: 'The Long And Winding Road',
|
||||
key: 'The Long And Winding Road'
|
||||
},
|
||||
{
|
||||
label: 'For You Blue',
|
||||
key: 'For You Blue'
|
||||
},
|
||||
{
|
||||
label: 'Get Back',
|
||||
key: 'Get Back'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
### 单图上传 UploadImage
|
||||
```vue
|
||||
<template>
|
||||
<UploadImage :maxNumber="1" v-model:value="value" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import UploadImage from '@/components/Upload/uploadImage.vue';
|
||||
const value = ref(null);
|
||||
</script>
|
||||
```
|
||||
|
||||
### 多图上传 UploadImage
|
||||
```vue
|
||||
<template>
|
||||
<UploadImage :maxNumber="10" v-model:value="value" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import UploadImage from '@/components/Upload/uploadImage.vue';
|
||||
const value = ref(null);
|
||||
</script>
|
||||
```
|
||||
|
||||
### 单文件上传 UploadFile
|
||||
```vue
|
||||
<template>
|
||||
<UploadFile :maxNumber="1" v-model:value="value" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import UploadFile from '@/components/Upload/uploadFile.vue';
|
||||
const value = ref(null);
|
||||
</script>
|
||||
```
|
||||
|
||||
### 多文件上传 UploadFile
|
||||
```vue
|
||||
<template>
|
||||
<UploadFile :maxNumber="10" v-model:value="value" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import UploadFile from '@/components/Upload/uploadFile.vue';
|
||||
const value = ref(null);
|
||||
</script>
|
||||
```
|
||||
|
||||
### 开关 Switch
|
||||
```vue
|
||||
<template>
|
||||
<n-switch v-model:value="value" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
const value = ref(null);
|
||||
</script>
|
||||
```
|
||||
|
||||
### 评分 Rate
|
||||
```vue
|
||||
<template>
|
||||
<n-rate allow-half :default-value="value" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
const value = ref(null);
|
||||
</script>
|
||||
```
|
||||
|
||||
### 省市区选择器 CitySelector
|
||||
```vue
|
||||
<template>
|
||||
<CitySelector v-model:value="value" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import CitySelector from '@/components/CitySelector/citySelector.vue';
|
||||
const value = ref(null);
|
||||
</script>
|
||||
```
|
||||
|
||||
### 图标选择器 IconSelector
|
||||
```vue
|
||||
<template>
|
||||
<IconSelector style="width: 100%" v-model:value="value" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import IconSelector from '@/components/IconSelector/index.vue';
|
||||
const value = ref(null);
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
更多组件请参考:https://www.naiveui.com/zh-CN/os-theme/components/button
|
||||
|
||||
|
@ -72,4 +72,6 @@ type GroupSelectReq struct {
|
||||
g.Meta `path:"/cronGroup/select" method:"get" tags:"定时任务分组" summary:"定时任务分组选项"`
|
||||
}
|
||||
|
||||
type GroupSelectRes sysin.DictTypeSelectModel
|
||||
type GroupSelectRes struct {
|
||||
*sysin.CronGroupSelectModel
|
||||
}
|
||||
|
@ -22,14 +22,6 @@ type NameUniqueRes struct {
|
||||
IsUnique bool `json:"is_unique" dc:"是否唯一"`
|
||||
}
|
||||
|
||||
// ListTreeReq 查询列表树
|
||||
type ListTreeReq struct {
|
||||
Id int64 `json:"id" dc:"部门ID"`
|
||||
g.Meta `path:"/dept/list_tree" method:"get" tags:"部门" summary:"获取部门列表树"`
|
||||
}
|
||||
|
||||
type ListTreeRes []*adminin.DeptListTreeModel
|
||||
|
||||
// ListReq 查询列表
|
||||
type ListReq struct {
|
||||
Name string `json:"name" dc:"部门名称"`
|
||||
@ -37,7 +29,9 @@ type ListReq struct {
|
||||
g.Meta `path:"/dept/list" method:"get" tags:"部门" summary:"获取部门列表"`
|
||||
}
|
||||
|
||||
type ListRes adminin.DeptListModel
|
||||
type ListRes struct {
|
||||
adminin.DeptListModel
|
||||
}
|
||||
|
||||
// ViewReq 获取指定信息
|
||||
type ViewReq struct {
|
||||
|
@ -17,7 +17,7 @@ type TypeTreeReq struct {
|
||||
g.Meta `path:"/dictType/tree" tags:"字典类型" method:"get" summary:"字典类型树列表"`
|
||||
}
|
||||
type TypeTreeRes struct {
|
||||
List []map[string]interface{} `json:"list" dc:"数据列表"`
|
||||
List []*sysin.DictTypeTree `json:"list" dc:"数据列表"`
|
||||
}
|
||||
|
||||
// TypeEditReq 修改/新增字典数据
|
||||
@ -34,10 +34,3 @@ type TypeDeleteReq struct {
|
||||
g.Meta `path:"/dictType/delete" method:"post" tags:"字典类型" summary:"删除字典类型"`
|
||||
}
|
||||
type TypeDeleteRes struct{}
|
||||
|
||||
// TypeSelectReq 修改/新增字典数据
|
||||
type TypeSelectReq struct {
|
||||
g.Meta `path:"/dictType/select" method:"get" tags:"字典类型" summary:"字典类型选项"`
|
||||
}
|
||||
|
||||
type TypeSelectRes sysin.DictTypeSelectModel
|
||||
|
@ -38,21 +38,11 @@ type MemberListRes struct {
|
||||
// ListReq 查询列表
|
||||
type ListReq struct {
|
||||
g.Meta `path:"/role/list" method:"get" tags:"角色" summary:"获取角色列表"`
|
||||
form.PageReq
|
||||
form.RangeDateReq
|
||||
form.StatusReq
|
||||
DeptId int `json:"deptId" description:"部门ID"`
|
||||
Mobile int `json:"mobile" description:"手机号"`
|
||||
Username string `json:"username" description:"用户名"`
|
||||
Realname string `json:"realname" description:"真实姓名"`
|
||||
StartTime string `json:"start_time" description:"开始时间"`
|
||||
EndTime string `json:"end_time" description:"结束时间"`
|
||||
Name string `json:"name" description:"岗位名称"`
|
||||
Code string `json:"code" description:"岗位编码"`
|
||||
adminin.RoleListInp
|
||||
}
|
||||
|
||||
type ListRes struct {
|
||||
List []g.Map `json:"list" description:"数据列表"`
|
||||
*adminin.RoleListModel
|
||||
form.PageRes
|
||||
}
|
||||
|
||||
|
@ -2,15 +2,15 @@
|
||||
# https://goframe.org/pages/viewpage.action?pageId=3673173
|
||||
gfcli:
|
||||
build:
|
||||
name: "hotgo"
|
||||
# arch: "all" #amd64
|
||||
# system: "all" #linux
|
||||
name: "hotgo" # 编译后的可执行文件名称
|
||||
# arch: "all" #不填默认当前系统架构,可选:386,amd64,arm,all
|
||||
# system: "all" #不填默认当前系统平台,可选:linux,darwin,windows,all
|
||||
mod: "none"
|
||||
cgo: 0
|
||||
packSrc: "resource"
|
||||
packDst: "internal/packed/packed.go"
|
||||
packSrc: "resource" # 将resource目录打包进可执行文件,静态资源无需单独部署
|
||||
packDst: "internal/packed/packed.go" # 打包后生成的Go文件路径,一般使用相对路径指定到本项目目录中
|
||||
version: ""
|
||||
output: "./temp/hotgo"
|
||||
output: "./temp/hotgo" # 可执行文件生成路径
|
||||
extra: ""
|
||||
|
||||
# gf生成代码,如果你想调整hotgo中代码生成的相关dao、service代码,同样也受用于此配置
|
||||
|
@ -41,7 +41,7 @@ var (
|
||||
---------------------------------------------------------------------------------
|
||||
更多
|
||||
github地址:https://github.com/bufanyun/hotgo
|
||||
文档地址:https://github.com/bufanyun/hotgo/tree/v2.0/docs
|
||||
文档地址:https://github.com/bufanyun/hotgo/tree/v2.0/docs/guide-zh-CN
|
||||
HotGo框架交流1群:190966648
|
||||
`,
|
||||
}
|
||||
|
@ -3,14 +3,13 @@
|
||||
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||
// @Author Ms <133814250@qq.com>
|
||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||
//
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"hotgo/internal/queues"
|
||||
"hotgo/internal/library/queue"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -20,7 +19,8 @@ var (
|
||||
Description: ``,
|
||||
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
|
||||
g.Log().Debug(ctx, "start queue consumer..")
|
||||
queues.Run(ctx)
|
||||
queue.StartConsumersListener(ctx)
|
||||
g.Log().Debug(ctx, "start queue consumer success..")
|
||||
return
|
||||
},
|
||||
}
|
||||
|
@ -7,5 +7,5 @@ package consts
|
||||
|
||||
// VersionApp HotGo版本
|
||||
const (
|
||||
VersionApp = "2.2.10"
|
||||
VersionApp = "2.3.5"
|
||||
)
|
||||
|
@ -90,23 +90,8 @@ func (c *cDept) List(ctx context.Context, req *dept.ListReq) (res *dept.ListRes,
|
||||
return
|
||||
}
|
||||
|
||||
res = (*dept.ListRes)(&data)
|
||||
return
|
||||
}
|
||||
|
||||
// ListTree 查看列表树
|
||||
func (c *cDept) ListTree(ctx context.Context, req *dept.ListTreeReq) (res *dept.ListTreeRes, err error) {
|
||||
var in adminin.DeptListTreeInp
|
||||
if err = gconv.Scan(req, &in); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := service.AdminDept().ListTree(ctx, in)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res = (*dept.ListTreeRes)(&data)
|
||||
res = new(dept.ListRes)
|
||||
res.List = data.List
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"hotgo/internal/model/input/adminin"
|
||||
"hotgo/internal/model/input/form"
|
||||
"hotgo/internal/service"
|
||||
"hotgo/utility/validate"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -165,6 +166,10 @@ func (c *cMember) Edit(ctx context.Context, req *member.EditReq) (res *member.Ed
|
||||
return
|
||||
}
|
||||
|
||||
if err = validate.PreFilter(ctx, &in); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
in.PostIds = req.PostIds
|
||||
err = service.AdminMember().Edit(ctx, in)
|
||||
return
|
||||
|
@ -55,7 +55,7 @@ func (c *cRole) List(ctx context.Context, req *role.ListReq) (res *role.ListRes,
|
||||
}
|
||||
|
||||
res = new(role.ListRes)
|
||||
res.List = list
|
||||
res.RoleListModel = list
|
||||
res.PageCount = form.CalPageCount(totalCount, req.PerPage)
|
||||
res.Page = req.Page
|
||||
res.PerPage = req.PerPage
|
||||
|
@ -100,11 +100,12 @@ func (c *cCronGroup) Status(ctx context.Context, req *cron.GroupStatusReq) (res
|
||||
|
||||
// Select 选项
|
||||
func (c *cCronGroup) Select(ctx context.Context, req *cron.GroupSelectReq) (res *cron.GroupSelectRes, err error) {
|
||||
list, err := service.SysCronGroup().Select(ctx, sysin.CronGroupSelectInp{})
|
||||
data, err := service.SysCronGroup().Select(ctx, sysin.CronGroupSelectInp{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res = (*cron.GroupSelectRes)(&list)
|
||||
res = new(cron.GroupSelectRes)
|
||||
res.CronGroupSelectModel = data
|
||||
return
|
||||
}
|
||||
|
@ -48,14 +48,3 @@ func (c *cDictType) Edit(ctx context.Context, req *dict.TypeEditReq) (res *dict.
|
||||
err = service.SysDictType().Edit(ctx, in)
|
||||
return
|
||||
}
|
||||
|
||||
// Select 选项
|
||||
func (c *cDictType) Select(ctx context.Context, req *dict.TypeSelectReq) (res *dict.TypeSelectRes, err error) {
|
||||
list, err := service.SysDictType().Select(ctx, sysin.DictTypeSelectInp{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res = (*dict.TypeSelectRes)(&list)
|
||||
return
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||
// @Author Ms <133814250@qq.com>
|
||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||
//
|
||||
package admin
|
||||
|
||||
import (
|
||||
@ -71,7 +70,7 @@ func (c *cMonitor) RunInfo(client *websocket.Client, req *websocket.WRequest) {
|
||||
"goName": "Golang",
|
||||
"version": runtime.Version(),
|
||||
"startTime": meta.STartTime,
|
||||
"runTime": gtime.Now().Timestamp() - meta.STartTime.Timestamp(),
|
||||
"runTime": gtime.Now().Timestamp() - meta.STartTime,
|
||||
"rootPath": runtime.GOROOT(),
|
||||
"pwd": pwd,
|
||||
"goroutine": runtime.NumGoroutine(),
|
||||
@ -108,7 +107,7 @@ func (c *cMonitor) Trends(client *websocket.Client, req *websocket.WRequest) {
|
||||
mMemUsed float64
|
||||
mDisk, _ = disk.Usage("/")
|
||||
mProcess, _ = process.Pids()
|
||||
mLoadAvg *model.LoadAvgStats
|
||||
mLoadAvg = new(model.LoadAvgStats)
|
||||
data = g.Map{}
|
||||
monitorHeads []MonitorHead
|
||||
nets []NetC
|
||||
|
@ -13,20 +13,24 @@ import (
|
||||
|
||||
// Build 构建新插件
|
||||
func Build(ctx context.Context, sk Skeleton, conf *model.BuildAddonConfig) (err error) {
|
||||
buildPath := "./" + consts.AddonsDir + "/" + sk.Name
|
||||
modulesPath := "./" + consts.AddonsDir + "/modules/" + sk.Name + ".go"
|
||||
templatePath := gstr.Replace(conf.TemplatePath, "{$name}", sk.Name)
|
||||
replaces := map[string]string{
|
||||
"@{.label}": sk.Label,
|
||||
"@{.name}": sk.Name,
|
||||
"@{.group}": strconv.Itoa(sk.Group),
|
||||
"@{.brief}": sk.Brief,
|
||||
"@{.description}": sk.Description,
|
||||
"@{.author}": sk.Author,
|
||||
"@{.version}": sk.Version,
|
||||
}
|
||||
var (
|
||||
buildPath = "./" + consts.AddonsDir + "/" + sk.Name
|
||||
modulesPath = "./" + consts.AddonsDir + "/modules/" + sk.Name + ".go"
|
||||
templatePath = gstr.Replace(conf.TemplatePath, "{$name}", sk.Name)
|
||||
webApiPath = gstr.Replace(conf.WebApiPath, "{$name}", sk.Name)
|
||||
webViewsPath = gstr.Replace(conf.WebViewsPath, "{$name}", sk.Name)
|
||||
replaces = map[string]string{
|
||||
"@{.label}": sk.Label,
|
||||
"@{.name}": sk.Name,
|
||||
"@{.group}": strconv.Itoa(sk.Group),
|
||||
"@{.brief}": sk.Brief,
|
||||
"@{.description}": sk.Description,
|
||||
"@{.author}": sk.Author,
|
||||
"@{.version}": sk.Version,
|
||||
}
|
||||
)
|
||||
|
||||
if err = checkBuildDir(buildPath, modulesPath, templatePath); err != nil {
|
||||
if err = checkBuildDir(buildPath, modulesPath, templatePath, webApiPath, webViewsPath); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -49,7 +53,6 @@ func Build(ctx context.Context, sk Skeleton, conf *model.BuildAddonConfig) (err
|
||||
gfile.RealPath(conf.SrcPath): "",
|
||||
".template": "",
|
||||
})
|
||||
|
||||
flowFile = buildPath + "/" + flowFile
|
||||
|
||||
content := gstr.ReplaceByMap(gfile.GetContents(path), replaces)
|
||||
@ -59,11 +62,31 @@ func Build(ctx context.Context, sk Skeleton, conf *model.BuildAddonConfig) (err
|
||||
}
|
||||
}
|
||||
|
||||
if err = gfile.PutContents(templatePath+"/home/index.html", homeLayout); err != nil {
|
||||
// 隐式注入插件
|
||||
if err = gfile.PutContents(modulesPath, gstr.ReplaceByMap(importModules, replaces)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// home默认页面
|
||||
if err = gfile.PutContents(templatePath+"/home/index.html", gstr.ReplaceByMap(homeLayout, replaces)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// webApi
|
||||
if err = gfile.PutContents(webApiPath+"/config/index.ts", gstr.ReplaceByMap(webApiLayout, replaces)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// web插件配置主页面
|
||||
if err = gfile.PutContents(webViewsPath+"/config/BasicSetting.vue", gstr.ReplaceByMap(webConfigBasicSetting, replaces)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// web插件基础配置页面
|
||||
if err = gfile.PutContents(webViewsPath+"/config/system.vue", gstr.ReplaceByMap(webConfigSystem, replaces)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = gfile.PutContents(modulesPath, gstr.ReplaceByMap(importModules, replaces))
|
||||
return
|
||||
}
|
||||
|
||||
@ -79,48 +102,3 @@ func checkBuildDir(paths ...string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
importModules = `// Package modules
|
||||
// @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 modules
|
||||
|
||||
import _ "hotgo/addons/@{.name}"
|
||||
`
|
||||
|
||||
homeLayout = `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">
|
||||
<meta name="keywords" content="@{.Keywords}"/>
|
||||
<meta name="description" content="@{.Description}"/>
|
||||
<title>@{.Title}</title>
|
||||
<script type="text/javascript" src="/resource/home/js/jquery-3.6.0.min.js"></script>
|
||||
<style>
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="padding-top: 100px;text-align:center;">
|
||||
<h1><p>Hello,@{.Data.name}!!</p></h1>
|
||||
<h2><p>@{.Data.module}</p></h2>
|
||||
<h2><p>服务器时间:@{.Data.time}</p></h2>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
<script>
|
||||
|
||||
</script>
|
||||
</html>`
|
||||
)
|
||||
|
225
server/internal/library/addons/build_layout.go
Normal file
@ -0,0 +1,225 @@
|
||||
package addons
|
||||
|
||||
const (
|
||||
importModules = `// Package modules
|
||||
// @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 modules
|
||||
|
||||
import _ "hotgo/addons/@{.name}"
|
||||
`
|
||||
|
||||
homeLayout = `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">
|
||||
<meta name="keywords" content="@{.Keywords}"/>
|
||||
<meta name="description" content="@{.Description}"/>
|
||||
<title>@{.Title}</title>
|
||||
<script type="text/javascript" src="/resource/home/js/jquery-3.6.0.min.js"></script>
|
||||
<style>
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="padding-top: 100px;text-align:center;">
|
||||
<h1><p>Hello,@{.Data.name}!!</p></h1>
|
||||
<h2><p>@{.Data.module}</p></h2>
|
||||
<h2><p>服务器时间:@{.Data.time}</p></h2>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
<script>
|
||||
|
||||
</script>
|
||||
</html>`
|
||||
|
||||
webApiLayout = `import { http } from '@/utils/http/axios';
|
||||
|
||||
export function getConfig(params) {
|
||||
return http.request({
|
||||
url: '/@{.name}/config/get',
|
||||
method: 'get',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateConfig(params) {
|
||||
return http.request({
|
||||
url: '/@{.name}/config/update',
|
||||
method: 'post',
|
||||
params,
|
||||
});
|
||||
}
|
||||
`
|
||||
|
||||
webConfigBasicSetting = `<template>
|
||||
<div>
|
||||
<n-spin :show="show" description="请稍候...">
|
||||
<n-form :label-width="80" :model="formValue" :rules="rules" ref="formRef">
|
||||
<n-form-item label="测试参数" path="basicTest">
|
||||
<n-input v-model:value="formValue.basicTest" placeholder="请输入测试参数" />
|
||||
<template #feedback>
|
||||
这是一个测试参数,每个插件都可以有独立的配置项,可以按需添加</template
|
||||
>
|
||||
</n-form-item>
|
||||
|
||||
<div>
|
||||
<n-space>
|
||||
<n-button type="primary" @click="formSubmit">保存更新</n-button>
|
||||
</n-space>
|
||||
</div>
|
||||
</n-form>
|
||||
</n-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { getConfig, updateConfig } from '@/api/addons/@{.name}/config';
|
||||
|
||||
const group = ref('basic');
|
||||
|
||||
const show = ref(false);
|
||||
const rules = {
|
||||
basicTest: {
|
||||
required: true,
|
||||
message: '请输入测试参数',
|
||||
trigger: 'blur',
|
||||
},
|
||||
};
|
||||
|
||||
const formRef: any = ref(null);
|
||||
const message = useMessage();
|
||||
|
||||
const formValue = ref({
|
||||
basicTest: 'HotGo',
|
||||
});
|
||||
|
||||
function formSubmit() {
|
||||
formRef.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
updateConfig({ group: group.value, list: formValue.value }).then((_res) => {
|
||||
message.success('更新成功');
|
||||
load();
|
||||
});
|
||||
} else {
|
||||
message.error('验证失败,请填写完整信息');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
load();
|
||||
});
|
||||
|
||||
function load() {
|
||||
show.value = true;
|
||||
new Promise((_resolve, _reject) => {
|
||||
getConfig({ group: group.value })
|
||||
.then((res) => {
|
||||
formValue.value = res.list;
|
||||
})
|
||||
.finally(() => {
|
||||
show.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
`
|
||||
|
||||
webConfigSystem = `<template>
|
||||
<div>
|
||||
<n-grid cols="24 300:1 600:24" :x-gap="24">
|
||||
<n-grid-item span="6">
|
||||
<n-card :bordered="false" size="small" class="proCard">
|
||||
<n-thing
|
||||
class="thing-cell"
|
||||
v-for="item in typeTabList"
|
||||
:key="item.key"
|
||||
:class="{ 'thing-cell-on': type === item.key }"
|
||||
@click="switchType(item)"
|
||||
>
|
||||
<template #header>{{ item.name }}</template>
|
||||
<template #description>{{ item.desc }}</template>
|
||||
</n-thing>
|
||||
</n-card>
|
||||
</n-grid-item>
|
||||
<n-grid-item span="18">
|
||||
<n-card :bordered="false" size="small" :title="typeTitle" class="proCard">
|
||||
<BasicSetting v-if="type === 1" />
|
||||
</n-card>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive, toRefs } from 'vue';
|
||||
import BasicSetting from './BasicSetting.vue';
|
||||
const typeTabList = [
|
||||
{
|
||||
name: '基本设置',
|
||||
desc: '系统常规设置',
|
||||
key: 1,
|
||||
},
|
||||
];
|
||||
export default defineComponent({
|
||||
components: {
|
||||
BasicSetting,
|
||||
},
|
||||
setup() {
|
||||
const state = reactive({
|
||||
type: 1,
|
||||
typeTitle: '基本设置',
|
||||
});
|
||||
|
||||
function switchType(e) {
|
||||
state.type = e.key;
|
||||
state.typeTitle = e.name;
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
switchType,
|
||||
typeTabList,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.thing-cell {
|
||||
margin: 0 -16px 10px;
|
||||
padding: 5px 16px;
|
||||
|
||||
&:hover {
|
||||
background: #f3f3f3;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.thing-cell-on {
|
||||
background: #f0faff;
|
||||
color: #2d8cf0;
|
||||
|
||||
::v-deep(.n-thing-main .n-thing-header .n-thing-header__title) {
|
||||
color: #2d8cf0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #f0faff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
`
|
||||
)
|
@ -66,11 +66,12 @@ func RegisterModulesRouter(ctx context.Context, group *ghttp.RouterGroup) {
|
||||
func RegisterModule(m Module) Module {
|
||||
mLock.Lock()
|
||||
defer mLock.Unlock()
|
||||
_, ok := modules[m.GetSkeleton().Name]
|
||||
name := m.GetSkeleton().Name
|
||||
_, ok := modules[name]
|
||||
if ok {
|
||||
panic("module repeat registration, name:" + m.GetSkeleton().Name)
|
||||
panic("module repeat registration, name:" + name)
|
||||
}
|
||||
modules[m.GetSkeleton().Name] = m
|
||||
modules[name] = m
|
||||
return m
|
||||
}
|
||||
|
||||
|
@ -107,6 +107,15 @@ func GetRoleKey(ctx context.Context) string {
|
||||
return user.RoleKey
|
||||
}
|
||||
|
||||
// GetModule 获取应用模块
|
||||
func GetModule(ctx context.Context) string {
|
||||
c := Get(ctx)
|
||||
if c == nil {
|
||||
return ""
|
||||
}
|
||||
return c.Module
|
||||
}
|
||||
|
||||
// SetAddonName 设置插件信息
|
||||
func SetAddonName(ctx context.Context, name string) {
|
||||
c := Get(ctx)
|
||||
|
@ -256,11 +256,6 @@ func setDefaultQuery(field *sysin.GenCodesColumnListModel) {
|
||||
return
|
||||
}
|
||||
|
||||
if field.Index == consts.GenCodesIndexPK {
|
||||
field.IsQuery = true
|
||||
return
|
||||
}
|
||||
|
||||
if gstr.HasSuffix(field.GoName, "Status") && IsNumberType(field.GoType) {
|
||||
field.IsQuery = true
|
||||
return
|
||||
|
@ -126,7 +126,7 @@ func (l *gCurd) generateWebEditScript(ctx context.Context, in *CurdPreviewInput)
|
||||
}
|
||||
setupBuffer.WriteString(" function loadForm(value) {\n loading.value = true;\n\n // 新增\n if (value.id < 1) {\n params.value = newState(value);\n MaxSort()\n .then((res) => {\n params.value.sort = res.sort;\n })\n .finally(() => {\n loading.value = false;\n });\n return;\n }\n\n // 编辑\n View({ id: value.id })\n .then((res) => {\n params.value = res;\n })\n .finally(() => {\n loading.value = false;\n });\n }\n\n watch(\n () => props.formParams,\n (value) => {\n loadForm(value);\n }\n );")
|
||||
} else {
|
||||
importBuffer.WriteString(" import { onMounted, ref, computed } from 'vue';\n")
|
||||
importBuffer.WriteString(" import { onMounted, ref, computed, watch } 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 {
|
||||
|
@ -52,11 +52,11 @@ func FilterAuthWithField(filterField string) func(m *gdb.Model) *gdb.Model {
|
||||
|
||||
err := g.Model("admin_role").Where("id", co.User.RoleId).Scan(&role)
|
||||
if err != nil {
|
||||
g.Log().Fatalf(ctx, "failed to role information err:%+v", err)
|
||||
g.Log().Panicf(ctx, "failed to role information err:%+v", err)
|
||||
}
|
||||
|
||||
if role == nil {
|
||||
g.Log().Fatalf(ctx, "failed to role information roleModel == nil")
|
||||
g.Log().Panicf(ctx, "failed to role information roleModel == nil")
|
||||
}
|
||||
|
||||
sq := g.Model("admin_member").Fields("id")
|
||||
@ -77,7 +77,7 @@ func FilterAuthWithField(filterField string) func(m *gdb.Model) *gdb.Model {
|
||||
case consts.RoleDataSelfAndAllSub: // 自己和全部下级
|
||||
m = m.WhereIn(filterField, GetSelfAndAllSub(co.User.Id))
|
||||
default:
|
||||
g.Log().Fatalf(ctx, "dataScope is not registered")
|
||||
g.Log().Panicf(ctx, "dataScope is not registered")
|
||||
}
|
||||
|
||||
return m
|
||||
|
73
server/internal/library/queue/consumer.go
Normal file
@ -0,0 +1,73 @@
|
||||
package queue
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// consumerStrategy 消费者策略,实现该接口即可加入到消费队列中
|
||||
type consumerStrategy interface {
|
||||
GetTopic() string // 获取消费主题
|
||||
Handle(ctx context.Context, mqMsg MqMsg) (err error) // 处理消息
|
||||
}
|
||||
|
||||
// consumerManager 消费者管理
|
||||
type consumerManager struct {
|
||||
sync.Mutex
|
||||
list map[string]consumerStrategy // 维护的消费者列表
|
||||
}
|
||||
|
||||
var consumers = &consumerManager{
|
||||
list: make(map[string]consumerStrategy),
|
||||
}
|
||||
|
||||
// RegisterConsumer 注册任务到消费者队列
|
||||
func RegisterConsumer(cs consumerStrategy) {
|
||||
consumers.Lock()
|
||||
defer consumers.Unlock()
|
||||
topic := cs.GetTopic()
|
||||
if _, ok := consumers.list[topic]; ok {
|
||||
g.Log().Debugf(ctx, "queue.RegisterConsumer topic:%v duplicate registration.", topic)
|
||||
return
|
||||
}
|
||||
consumers.list[topic] = cs
|
||||
}
|
||||
|
||||
// StartConsumersListener 启动所有已注册的消费者监听
|
||||
func StartConsumersListener(ctx context.Context) {
|
||||
for _, consumer := range consumers.list {
|
||||
go func(consumer consumerStrategy) {
|
||||
consumerListen(ctx, consumer)
|
||||
}(consumer)
|
||||
}
|
||||
}
|
||||
|
||||
// consumerListen 消费者监听
|
||||
func consumerListen(ctx context.Context, job consumerStrategy) {
|
||||
var (
|
||||
topic = job.GetTopic()
|
||||
consumer, err = InstanceConsumer()
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
g.Log().Fatalf(ctx, "InstanceConsumer %s err:%+v", topic, err)
|
||||
return
|
||||
}
|
||||
|
||||
if listenErr := consumer.ListenReceiveMsgDo(topic, func(mqMsg MqMsg) {
|
||||
err = job.Handle(ctx, mqMsg)
|
||||
|
||||
if err != nil {
|
||||
// 遇到错误,重新加入到队列
|
||||
//queue.Push(topic, mqMsg.Body)
|
||||
}
|
||||
|
||||
// 记录消费队列日志
|
||||
ConsumerLog(ctx, topic, mqMsg, err)
|
||||
|
||||
}); listenErr != nil {
|
||||
g.Log().Fatalf(ctx, "消费队列:%s 监听失败, err:%+v", topic, listenErr)
|
||||
}
|
||||
|
||||
}
|
@ -11,6 +11,8 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Disk 磁盘队列
|
||||
|
||||
type DiskProducerMq struct {
|
||||
config *disk.Config
|
||||
producers map[string]*disk.Queue
|
||||
|
@ -66,7 +66,7 @@ func CreateClient(accessKeyId *string, accessKeySecret *string) (_result *dysmsa
|
||||
return _result, _err
|
||||
}
|
||||
|
||||
func Send(accessKeyId string, accessKeySecret string) (_err error) {
|
||||
func TestSend(accessKeyId string, accessKeySecret string) (_err error) {
|
||||
// 工程代码泄露可能会导致AccessKey泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378661.html
|
||||
client, _err := CreateClient(tea.String(accessKeyId), tea.String(accessKeySecret))
|
||||
if _err != nil {
|
||||
|
@ -10,7 +10,6 @@ import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/hgorm"
|
||||
@ -18,7 +17,6 @@ import (
|
||||
"hotgo/internal/model/input/adminin"
|
||||
"hotgo/internal/service"
|
||||
"hotgo/utility/convert"
|
||||
"hotgo/utility/tree"
|
||||
"hotgo/utility/validate"
|
||||
)
|
||||
|
||||
@ -33,27 +31,21 @@ func init() {
|
||||
}
|
||||
|
||||
// NameUnique 菜单名称是否唯一
|
||||
func (s *sAdminDept) NameUnique(ctx context.Context, in adminin.DeptNameUniqueInp) (*adminin.DeptNameUniqueModel, error) {
|
||||
|
||||
var res adminin.DeptNameUniqueModel
|
||||
func (s *sAdminDept) NameUnique(ctx context.Context, in adminin.DeptNameUniqueInp) (res *adminin.DeptNameUniqueModel, err error) {
|
||||
isUnique, err := dao.AdminDept.IsUniqueName(ctx, in.Id, in.Name)
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return nil, err
|
||||
return
|
||||
}
|
||||
|
||||
res = new(adminin.DeptNameUniqueModel)
|
||||
res.IsUnique = isUnique
|
||||
return &res, nil
|
||||
return
|
||||
}
|
||||
|
||||
// Delete 删除
|
||||
func (s *sAdminDept) Delete(ctx context.Context, in adminin.DeptDeleteInp) (err error) {
|
||||
|
||||
var (
|
||||
models *entity.AdminDept
|
||||
)
|
||||
err = dao.AdminDept.Ctx(ctx).Where("id", in.Id).Scan(&models)
|
||||
if err != nil {
|
||||
var models *entity.AdminDept
|
||||
if err = dao.AdminDept.Ctx(ctx).Where("id", in.Id).Scan(&models); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -71,115 +63,86 @@ func (s *sAdminDept) Delete(ctx context.Context, in adminin.DeptDeleteInp) (err
|
||||
}
|
||||
|
||||
_, err = dao.AdminDept.Ctx(ctx).Where("id", in.Id).Delete()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改/新增
|
||||
func (s *sAdminDept) Edit(ctx context.Context, in adminin.DeptEditInp) (err error) {
|
||||
|
||||
if in.Name == "" {
|
||||
err = gerror.New("名称不能为空")
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
uniqueName, err := dao.AdminDept.IsUniqueName(ctx, in.Id, in.Name)
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
return
|
||||
}
|
||||
if !uniqueName {
|
||||
err = gerror.New("名称已存在")
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
in.Pid, in.Level, in.Tree, err = hgorm.GenSubTree(ctx, dao.AdminDept, in.Pid)
|
||||
if err != nil {
|
||||
return err
|
||||
if in.Pid, in.Level, in.Tree, err = hgorm.GenSubTree(ctx, dao.AdminDept, in.Pid); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 修改
|
||||
if in.Id > 0 {
|
||||
_, err = dao.AdminDept.Ctx(ctx).Where("id", in.Id).Data(in).Update()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
// 新增
|
||||
_, err = dao.AdminDept.Ctx(ctx).Data(in).Insert()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
// Status 更新部门状态
|
||||
func (s *sAdminDept) Status(ctx context.Context, in adminin.DeptStatusInp) (err error) {
|
||||
|
||||
if in.Id <= 0 {
|
||||
err = gerror.New("ID不能为空")
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
if in.Status <= 0 {
|
||||
err = gerror.New("状态不能为空")
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
if !validate.InSliceInt(consts.StatusMap, in.Status) {
|
||||
err = gerror.New("状态不正确")
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
// 修改
|
||||
in.UpdatedAt = gtime.Now()
|
||||
_, err = dao.AdminDept.Ctx(ctx).Where("id", in.Id).Data("status", in.Status).Update()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
// MaxSort 最大排序
|
||||
func (s *sAdminDept) MaxSort(ctx context.Context, in adminin.DeptMaxSortInp) (*adminin.DeptMaxSortModel, error) {
|
||||
var res adminin.DeptMaxSortModel
|
||||
|
||||
func (s *sAdminDept) MaxSort(ctx context.Context, in adminin.DeptMaxSortInp) (res *adminin.DeptMaxSortModel, err error) {
|
||||
res = new(adminin.DeptMaxSortModel)
|
||||
if in.Id > 0 {
|
||||
if err := dao.AdminDept.Ctx(ctx).Where("id", in.Id).Order("sort desc").Scan(&res); err != nil {
|
||||
if err = dao.AdminDept.Ctx(ctx).Where("id", in.Id).Order("sort desc").Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return nil, err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
res.Sort = res.Sort + 10
|
||||
|
||||
return &res, nil
|
||||
return
|
||||
}
|
||||
|
||||
// View 获取指定字典类型信息
|
||||
func (s *sAdminDept) View(ctx context.Context, in adminin.DeptViewInp) (res *adminin.DeptViewModel, err error) {
|
||||
|
||||
if err = dao.AdminDept.Ctx(ctx).Where("id", in.Id).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
err = dao.AdminDept.Ctx(ctx).Where("id", in.Id).Scan(&res)
|
||||
return
|
||||
}
|
||||
|
||||
// List 获取列表
|
||||
func (s *sAdminDept) List(ctx context.Context, in adminin.DeptListInp) (list adminin.DeptListModel, err error) {
|
||||
func (s *sAdminDept) List(ctx context.Context, in adminin.DeptListInp) (res *adminin.DeptListModel, err error) {
|
||||
var (
|
||||
mod = dao.AdminDept.Ctx(ctx)
|
||||
models []*entity.AdminDept
|
||||
@ -228,114 +191,12 @@ func (s *sAdminDept) List(ctx context.Context, in adminin.DeptListInp) (list adm
|
||||
|
||||
if err = mod.Order("pid asc,sort asc").Scan(&models); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return list, err
|
||||
return
|
||||
}
|
||||
|
||||
list = gconv.SliceMap(models)
|
||||
for k, v := range list {
|
||||
list[k]["index"] = v["id"]
|
||||
list[k]["key"] = v["id"]
|
||||
list[k]["label"] = v["name"]
|
||||
}
|
||||
|
||||
return tree.GenTree(list), nil
|
||||
}
|
||||
|
||||
type DeptTree struct {
|
||||
entity.AdminDept
|
||||
Children []*DeptTree `json:"children"`
|
||||
}
|
||||
|
||||
// getDeptChildIds 将列表转为父子关系列表
|
||||
func (s *sAdminDept) getDeptChildIds(ctx context.Context, lists []*DeptTree, pid int64) []*DeptTree {
|
||||
|
||||
var (
|
||||
count = len(lists)
|
||||
newLists []*DeptTree
|
||||
)
|
||||
|
||||
if count == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(lists); i++ {
|
||||
if lists[i].Id > 0 && lists[i].Pid == pid {
|
||||
var row *DeptTree
|
||||
if err := gconv.Structs(lists[i], &row); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
row.Children = s.getDeptChildIds(ctx, lists, row.Id)
|
||||
newLists = append(newLists, row)
|
||||
}
|
||||
}
|
||||
|
||||
return newLists
|
||||
}
|
||||
|
||||
type DeptListTree struct {
|
||||
Id int64 `json:"id" `
|
||||
Key int64 `json:"key" `
|
||||
Pid int64 `json:"pid" `
|
||||
Label string `json:"label"`
|
||||
Title string `json:"title"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Children []*DeptListTree `json:"children"`
|
||||
}
|
||||
|
||||
// ListTree 获取列表树
|
||||
func (s *sAdminDept) ListTree(ctx context.Context, in adminin.DeptListTreeInp) (list []*adminin.DeptListTreeModel, err error) {
|
||||
var (
|
||||
mod = dao.AdminDept.Ctx(ctx)
|
||||
dataList []*entity.AdminDept
|
||||
models []*DeptListTree
|
||||
)
|
||||
|
||||
err = mod.Order("id desc").Scan(&dataList)
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return list, err
|
||||
}
|
||||
|
||||
_ = gconv.Structs(dataList, &models)
|
||||
|
||||
// 重写树入参
|
||||
for i := 0; i < len(models); i++ {
|
||||
models[i].Key = models[i].Id
|
||||
models[i].Title = models[i].Name
|
||||
models[i].Label = models[i].Name
|
||||
}
|
||||
|
||||
childIds := s.getDeptTreeChildIds(ctx, models, 0)
|
||||
|
||||
_ = gconv.Structs(childIds, &list)
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// getDeptTreeChildIds 将列表转为父子关系列表
|
||||
func (s *sAdminDept) getDeptTreeChildIds(ctx context.Context, lists []*DeptListTree, pid int64) []*DeptListTree {
|
||||
var (
|
||||
count = len(lists)
|
||||
newLists []*DeptListTree
|
||||
)
|
||||
|
||||
if count == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(lists); i++ {
|
||||
if lists[i].Id > 0 && lists[i].Pid == pid {
|
||||
var row *DeptListTree
|
||||
if err := gconv.Structs(lists[i], &row); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
row.Children = s.getDeptTreeChildIds(ctx, lists, row.Id)
|
||||
newLists = append(newLists, row)
|
||||
}
|
||||
}
|
||||
|
||||
return newLists
|
||||
res = new(adminin.DeptListModel)
|
||||
res.List = s.treeList(0, models)
|
||||
return
|
||||
}
|
||||
|
||||
// GetName 获取部门名称
|
||||
@ -352,3 +213,23 @@ func (s *sAdminDept) GetName(ctx context.Context, id int64) (name string, err er
|
||||
|
||||
return data.Name, nil
|
||||
}
|
||||
|
||||
// treeList 树状列表
|
||||
func (s *sAdminDept) treeList(pid int64, nodes []*entity.AdminDept) (list []*adminin.DeptTree) {
|
||||
list = make([]*adminin.DeptTree, 0)
|
||||
for _, v := range nodes {
|
||||
if v.Pid == pid {
|
||||
item := new(adminin.DeptTree)
|
||||
item.AdminDept = *v
|
||||
item.Label = v.Name
|
||||
item.Value = v.Id
|
||||
|
||||
child := s.treeList(v.Id, nodes)
|
||||
if len(child) > 0 {
|
||||
item.Children = child
|
||||
}
|
||||
list = append(list, item)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -256,6 +256,7 @@ func (s *sAdminMember) ResetPwd(ctx context.Context, in adminin.MemberResetPwdIn
|
||||
memberInfo *entity.AdminMember
|
||||
memberId = contexts.GetUserId(ctx)
|
||||
)
|
||||
|
||||
if err = s.FilterAuthModel(ctx, memberId).Where("id", in.Id).Scan(&memberInfo); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
@ -400,10 +401,18 @@ func (s *sAdminMember) Edit(ctx context.Context, in adminin.MemberEditInp) (err
|
||||
return gerror.New("超管账号禁止编辑!")
|
||||
}
|
||||
|
||||
// 权限验证
|
||||
var mm = s.FilterAuthModel(ctx, opMemberId).Where("id", in.Id)
|
||||
_, err = mm.Data(in).Update()
|
||||
if err != nil {
|
||||
mod := s.FilterAuthModel(ctx, opMemberId)
|
||||
if in.Password != "" {
|
||||
salt, err := s.FilterAuthModel(ctx, contexts.GetUserId(ctx)).Fields(dao.AdminMember.Columns().Salt).Where("id", in.Id).Value()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
in.PasswordHash = gmd5.MustEncryptString(in.Password + salt.String())
|
||||
} else {
|
||||
mod = mod.FieldsEx(dao.AdminMember.Columns().PasswordHash)
|
||||
}
|
||||
|
||||
if _, err = mod.Where("id", in.Id).Data(in).Update(); err != nil {
|
||||
return gerror.Wrap(err, consts.ErrorORM)
|
||||
}
|
||||
|
||||
|
@ -25,18 +25,18 @@ func init() {
|
||||
service.RegisterAdminMemberPost(NewAdminMemberPost())
|
||||
}
|
||||
|
||||
func (s *sAdminMemberPost) UpdatePostIds(ctx context.Context, member_id int64, post_ids []int64) (err error) {
|
||||
_, err = dao.AdminMemberPost.Ctx(ctx).Where("member_id", member_id).Delete()
|
||||
func (s *sAdminMemberPost) UpdatePostIds(ctx context.Context, memberId int64, postIds []int64) (err error) {
|
||||
_, err = dao.AdminMemberPost.Ctx(ctx).Where("member_id", memberId).Delete()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, "删除失败")
|
||||
return err
|
||||
}
|
||||
|
||||
for i := 0; i < len(post_ids); i++ {
|
||||
for i := 0; i < len(postIds); i++ {
|
||||
_, err = dao.AdminMemberPost.Ctx(ctx).
|
||||
Insert(entity.AdminMemberPost{
|
||||
MemberId: member_id,
|
||||
PostId: post_ids[i],
|
||||
MemberId: memberId,
|
||||
PostId: postIds[i],
|
||||
})
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, "插入用户岗位失败")
|
||||
@ -48,20 +48,20 @@ func (s *sAdminMemberPost) UpdatePostIds(ctx context.Context, member_id int64, p
|
||||
}
|
||||
|
||||
// GetMemberByIds 获取指定用户的岗位ids
|
||||
func (s *sAdminMemberPost) GetMemberByIds(ctx context.Context, member_id int64) (post_ids []int64, err error) {
|
||||
func (s *sAdminMemberPost) GetMemberByIds(ctx context.Context, memberId int64) (postIds []int64, err error) {
|
||||
var list []*entity.AdminMemberPost
|
||||
err = dao.AdminMemberPost.Ctx(ctx).
|
||||
Fields("post_id").
|
||||
Where("member_id", member_id).
|
||||
Where("member_id", memberId).
|
||||
Scan(&list)
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return post_ids, err
|
||||
return postIds, err
|
||||
}
|
||||
|
||||
for i := 0; i < len(list); i++ {
|
||||
post_ids = append(post_ids, list[i].PostId)
|
||||
postIds = append(postIds, list[i].PostId)
|
||||
}
|
||||
|
||||
return post_ids, nil
|
||||
return postIds, nil
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ func init() {
|
||||
// StartMonitor 启动服务监控
|
||||
func (s *sAdminMonitor) StartMonitor(ctx context.Context) {
|
||||
simple.SafeGo(ctx, func(ctx context.Context) {
|
||||
s.data.STartTime = gtime.Now()
|
||||
s.data.STartTime = gtime.Now().Timestamp()
|
||||
intranetIP, err := location.GetLocalIP()
|
||||
if err != nil {
|
||||
g.Log().Infof(ctx, "parse intranetIP err:%+v", err)
|
||||
|
@ -12,7 +12,6 @@ import (
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"hotgo/api/admin/role"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
@ -25,9 +24,7 @@ import (
|
||||
"hotgo/internal/service"
|
||||
"hotgo/utility/auth"
|
||||
"hotgo/utility/convert"
|
||||
"hotgo/utility/tree"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type sAdminRole struct{}
|
||||
@ -69,31 +66,26 @@ func (s *sAdminRole) Verify(ctx context.Context, path, method string) bool {
|
||||
}
|
||||
|
||||
// List 获取列表
|
||||
func (s *sAdminRole) List(ctx context.Context, in adminin.RoleListInp) (list []g.Map, totalCount int, err error) {
|
||||
func (s *sAdminRole) List(ctx context.Context, in adminin.RoleListInp) (res *adminin.RoleListModel, totalCount int, err error) {
|
||||
var (
|
||||
mod = dao.AdminRole.Ctx(ctx)
|
||||
models []*adminin.RoleListModel
|
||||
models []*entity.AdminRole
|
||||
)
|
||||
|
||||
totalCount, err = mod.Count()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return list, totalCount, err
|
||||
return
|
||||
}
|
||||
|
||||
err = mod.Page(in.Page, in.PerPage).Order("id asc").Scan(&models)
|
||||
if err != nil {
|
||||
if err = mod.Page(in.Page, in.PerPage).Order("sort asc,id asc").Scan(&models); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return list, totalCount, err
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range models {
|
||||
v.Label = v.Name
|
||||
v.Value = v.Id
|
||||
v.Key = strconv.FormatInt(v.Id, 10)
|
||||
}
|
||||
|
||||
return tree.GenTree(gconv.SliceMap(models)), totalCount, err
|
||||
res = new(adminin.RoleListModel)
|
||||
res.List = s.treeList(0, models)
|
||||
return
|
||||
}
|
||||
|
||||
// GetName 获取指定角色的名称
|
||||
@ -134,6 +126,7 @@ func (s *sAdminRole) GetPermissions(ctx context.Context, reqInfo *role.GetPermis
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(values) == 0 {
|
||||
return
|
||||
}
|
||||
@ -156,6 +149,10 @@ func (s *sAdminRole) UpdatePermissions(ctx context.Context, reqInfo *role.Update
|
||||
if len(reqInfo.MenuIds) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 去重
|
||||
reqInfo.MenuIds = convert.UniqueSliceInt64(reqInfo.MenuIds)
|
||||
|
||||
addMap := make(g.List, 0, len(reqInfo.MenuIds))
|
||||
for _, v := range reqInfo.MenuIds {
|
||||
addMap = append(addMap, g.Map{
|
||||
@ -163,8 +160,8 @@ func (s *sAdminRole) UpdatePermissions(ctx context.Context, reqInfo *role.Update
|
||||
"menu_id": v,
|
||||
})
|
||||
}
|
||||
_, err = dao.AdminRoleMenu.Ctx(ctx).Data(addMap).Insert()
|
||||
if err != nil {
|
||||
|
||||
if _, err = dao.AdminRoleMenu.Ctx(ctx).Data(addMap).Insert(); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
}
|
||||
@ -176,56 +173,47 @@ func (s *sAdminRole) UpdatePermissions(ctx context.Context, reqInfo *role.Update
|
||||
func (s *sAdminRole) Edit(ctx context.Context, in *role.EditReq) (err error) {
|
||||
if in.Name == "" {
|
||||
err = gerror.New("名称不能为空")
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
if in.Key == "" {
|
||||
err = gerror.New("编码不能为空")
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
uniqueName, err := dao.AdminRole.IsUniqueName(ctx, in.Id, in.Name)
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
return
|
||||
}
|
||||
if !uniqueName {
|
||||
err = gerror.New("名称已存在")
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
uniqueCode, err := dao.AdminRole.IsUniqueCode(ctx, in.Id, in.Key)
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
return
|
||||
}
|
||||
if !uniqueCode {
|
||||
err = gerror.New("编码已存在")
|
||||
return err
|
||||
return
|
||||
}
|
||||
|
||||
in.Pid, in.Level, in.Tree, err = hgorm.GenSubTree(ctx, dao.AdminRole, in.Pid)
|
||||
if err != nil {
|
||||
return err
|
||||
if in.Pid, in.Level, in.Tree, err = hgorm.GenSubTree(ctx, dao.AdminRole, in.Pid); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 修改
|
||||
if in.Id > 0 {
|
||||
_, err = dao.AdminRole.Ctx(ctx).Where("id", in.Id).Data(in).Update()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
// 新增
|
||||
_, err = dao.AdminRole.Ctx(ctx).Data(in).Insert()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sAdminRole) Delete(ctx context.Context, in *role.DeleteReq) (err error) {
|
||||
@ -233,12 +221,9 @@ func (s *sAdminRole) Delete(ctx context.Context, in *role.DeleteReq) (err error)
|
||||
return gerror.New("ID不正确!")
|
||||
}
|
||||
|
||||
var (
|
||||
models *entity.AdminRole
|
||||
)
|
||||
err = dao.AdminRole.Ctx(ctx).Where("id", in.Id).Scan(&models)
|
||||
if err != nil {
|
||||
return err
|
||||
var models *entity.AdminRole
|
||||
if err = dao.AdminRole.Ctx(ctx).Where("id", in.Id).Scan(&models); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if models == nil {
|
||||
@ -255,12 +240,7 @@ func (s *sAdminRole) Delete(ctx context.Context, in *role.DeleteReq) (err error)
|
||||
}
|
||||
|
||||
_, err = dao.AdminRole.Ctx(ctx).Where("id", in.Id).Delete()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
func (s *sAdminRole) DataScopeSelect(ctx context.Context) (res form.Selects) {
|
||||
@ -285,8 +265,7 @@ func (s *sAdminRole) DataScopeEdit(ctx context.Context, in *adminin.DataScopeEdi
|
||||
superRoleKey = g.Cfg().MustGet(ctx, "hotgo.admin.superRoleKey")
|
||||
)
|
||||
|
||||
err = dao.AdminRole.Ctx(ctx).Where("id", in.Id).Scan(&models)
|
||||
if err != nil {
|
||||
if err = dao.AdminRole.Ctx(ctx).Where("id", in.Id).Scan(&models); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -310,10 +289,26 @@ func (s *sAdminRole) DataScopeEdit(ctx context.Context, in *adminin.DataScopeEdi
|
||||
Where("id", in.Id).
|
||||
Data(models).
|
||||
Update()
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
// treeList 树状列表
|
||||
func (s *sAdminRole) treeList(pid int64, nodes []*entity.AdminRole) (list []*adminin.RoleTree) {
|
||||
list = make([]*adminin.RoleTree, 0)
|
||||
for _, v := range nodes {
|
||||
if v.Pid == pid {
|
||||
item := new(adminin.RoleTree)
|
||||
item.AdminRole = *v
|
||||
item.Label = v.Name
|
||||
item.Value = v.Id
|
||||
|
||||
child := s.treeList(v.Id, nodes)
|
||||
if len(child) > 0 {
|
||||
item.Children = child
|
||||
}
|
||||
list = append(list, item)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -20,11 +20,7 @@ import (
|
||||
|
||||
// AdminAuth 后台鉴权中间件
|
||||
func (s *sMiddleware) AdminAuth(r *ghttp.Request) {
|
||||
|
||||
var (
|
||||
ctx = r.Context()
|
||||
)
|
||||
|
||||
var ctx = r.Context()
|
||||
// 替换掉模块前缀
|
||||
routerPrefix := g.Cfg().MustGet(ctx, "router.admin.prefix", "/admin")
|
||||
path := gstr.Replace(r.URL.Path, routerPrefix.String(), "", 1)
|
||||
|
@ -9,14 +9,12 @@ package sys
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
"hotgo/utility/tree"
|
||||
"hotgo/utility/validate"
|
||||
)
|
||||
|
||||
@ -157,31 +155,39 @@ func (s *sSysCronGroup) List(ctx context.Context, in sysin.CronGroupListInp) (li
|
||||
}
|
||||
|
||||
// Select 选项
|
||||
func (s *sSysCronGroup) Select(ctx context.Context, in sysin.CronGroupSelectInp) (list sysin.CronGroupSelectModel, err error) {
|
||||
func (s *sSysCronGroup) Select(ctx context.Context, in sysin.CronGroupSelectInp) (res *sysin.CronGroupSelectModel, err error) {
|
||||
var (
|
||||
mod = dao.SysCronGroup.Ctx(ctx)
|
||||
models []*entity.SysCronGroup
|
||||
typeList []g.Map
|
||||
mod = dao.SysCronGroup.Ctx(ctx)
|
||||
models []*entity.SysCronGroup
|
||||
)
|
||||
|
||||
if err = mod.Order("pid asc,sort asc").Scan(&models); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return list, err
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < len(models); i++ {
|
||||
typeList = append(typeList, g.Map{
|
||||
"index": models[i].Id,
|
||||
"key": models[i].Id,
|
||||
"label": models[i].Name,
|
||||
"id": models[i].Id,
|
||||
"pid": models[i].Pid,
|
||||
"name": models[i].Name,
|
||||
"sort": models[i].Sort,
|
||||
"created_at": models[i].CreatedAt,
|
||||
"status": models[i].Status,
|
||||
})
|
||||
}
|
||||
|
||||
return tree.GenTree(typeList), nil
|
||||
res = new(sysin.CronGroupSelectModel)
|
||||
res.List = s.treeList(0, models)
|
||||
return
|
||||
}
|
||||
|
||||
// treeList 树状列表
|
||||
func (s *sSysCronGroup) treeList(pid int64, nodes []*entity.SysCronGroup) (list []*sysin.CronGroupTree) {
|
||||
list = make([]*sysin.CronGroupTree, 0)
|
||||
for _, v := range nodes {
|
||||
if v.Pid == pid {
|
||||
item := new(sysin.CronGroupTree)
|
||||
item.SysCronGroup = *v
|
||||
item.Label = v.Name
|
||||
item.Value = v.Id
|
||||
item.Key = v.Id
|
||||
|
||||
child := s.treeList(v.Id, nodes)
|
||||
if len(child) > 0 {
|
||||
item.Children = child
|
||||
}
|
||||
list = append(list, item)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -9,14 +9,12 @@ package sys
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/sysin"
|
||||
"hotgo/internal/service"
|
||||
"hotgo/utility/tree"
|
||||
)
|
||||
|
||||
type sSysDictType struct{}
|
||||
@ -30,35 +28,19 @@ func init() {
|
||||
}
|
||||
|
||||
// Tree 树
|
||||
func (s *sSysDictType) Tree(ctx context.Context) (list []g.Map, err error) {
|
||||
func (s *sSysDictType) Tree(ctx context.Context) (list []*sysin.DictTypeTree, err error) {
|
||||
var (
|
||||
mod = dao.SysDictType.Ctx(ctx)
|
||||
models []*entity.SysDictType
|
||||
)
|
||||
|
||||
if err = mod.Order("pid asc,sort asc").Scan(&models); err != nil {
|
||||
if err = mod.Order("sort asc,id asc").Scan(&models); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return list, err
|
||||
}
|
||||
|
||||
for i := 0; i < len(models); i++ {
|
||||
list = append(list, g.Map{
|
||||
"index": models[i].Id,
|
||||
"key": models[i].Id,
|
||||
"label": models[i].Name,
|
||||
"id": models[i].Id,
|
||||
"pid": models[i].Pid,
|
||||
"name": models[i].Name,
|
||||
"type": models[i].Type,
|
||||
"sort": models[i].Sort,
|
||||
"remark": models[i].Remark,
|
||||
"status": models[i].Status,
|
||||
"updated_at": models[i].UpdatedAt,
|
||||
"created_at": models[i].CreatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
return tree.GenTree(list), nil
|
||||
list = s.treeList(0, models)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete 删除
|
||||
@ -141,42 +123,11 @@ func (s *sSysDictType) Edit(ctx context.Context, in sysin.DictTypeEditInp) (err
|
||||
return nil
|
||||
}
|
||||
|
||||
// Select 选项
|
||||
func (s *sSysDictType) Select(ctx context.Context, in sysin.DictTypeSelectInp) (list sysin.DictTypeSelectModel, err error) {
|
||||
var (
|
||||
mod = dao.SysDictType.Ctx(ctx)
|
||||
models []*entity.SysDictType
|
||||
typeList []g.Map
|
||||
)
|
||||
|
||||
if err = mod.Order("pid asc,sort asc").Scan(&models); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return list, err
|
||||
}
|
||||
|
||||
for i := 0; i < len(models); i++ {
|
||||
typeList = append(typeList, g.Map{
|
||||
"index": models[i].Id,
|
||||
"key": models[i].Id,
|
||||
"label": models[i].Name,
|
||||
"id": models[i].Id,
|
||||
"pid": models[i].Pid,
|
||||
"name": models[i].Name,
|
||||
"sort": models[i].Sort,
|
||||
"created_at": models[i].CreatedAt,
|
||||
"status": models[i].Status,
|
||||
})
|
||||
}
|
||||
|
||||
return tree.GenTree(typeList), nil
|
||||
}
|
||||
|
||||
// TreeSelect 获取类型关系树选项
|
||||
func (s *sSysDictType) TreeSelect(ctx context.Context, in sysin.DictTreeSelectInp) (list sysin.DictTreeSelectModel, err error) {
|
||||
func (s *sSysDictType) TreeSelect(ctx context.Context, in sysin.DictTreeSelectInp) (list []*sysin.DictTypeTree, err error) {
|
||||
var (
|
||||
mod = dao.SysDictType.Ctx(ctx)
|
||||
models []*entity.SysDictType
|
||||
typeList []g.Map
|
||||
mod = dao.SysDictType.Ctx(ctx)
|
||||
models []*entity.SysDictType
|
||||
)
|
||||
|
||||
if err = mod.Order("pid asc,sort asc").Scan(&models); err != nil {
|
||||
@ -184,26 +135,39 @@ func (s *sSysDictType) TreeSelect(ctx context.Context, in sysin.DictTreeSelectIn
|
||||
return list, err
|
||||
}
|
||||
|
||||
for i := 0; i < len(models); i++ {
|
||||
typeList = append(typeList, g.Map{
|
||||
"index": models[i].Id,
|
||||
"key": models[i].Id,
|
||||
"label": models[i].Name,
|
||||
"id": models[i].Id,
|
||||
"pid": models[i].Pid,
|
||||
"name": models[i].Name,
|
||||
"sort": models[i].Sort,
|
||||
"created_at": models[i].CreatedAt,
|
||||
"status": models[i].Status,
|
||||
})
|
||||
}
|
||||
list = s.treeList(0, models)
|
||||
|
||||
maps := tree.GenTree(typeList)
|
||||
for _, v := range maps {
|
||||
for _, v := range list {
|
||||
// 父类一律禁止选中
|
||||
if _, ok := v["children"]; ok {
|
||||
v["disabled"] = true
|
||||
if len(v.Children) > 0 {
|
||||
v.Disabled = true
|
||||
for _, v2 := range v.Children {
|
||||
if len(v2.Children) > 0 {
|
||||
v2.Disabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return tree.GenTree(typeList), nil
|
||||
return
|
||||
}
|
||||
|
||||
// treeList 树状列表
|
||||
func (s *sSysDictType) treeList(pid int64, nodes []*entity.SysDictType) (list []*sysin.DictTypeTree) {
|
||||
list = make([]*sysin.DictTypeTree, 0)
|
||||
for _, v := range nodes {
|
||||
if v.Pid == pid {
|
||||
item := new(sysin.DictTypeTree)
|
||||
item.SysDictType = *v
|
||||
item.Label = v.Name
|
||||
item.Value = v.Id
|
||||
item.Key = v.Id
|
||||
|
||||
child := s.treeList(v.Id, nodes)
|
||||
if len(child) > 0 {
|
||||
item.Children = child
|
||||
}
|
||||
list = append(list, item)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -166,6 +166,8 @@ type GenerateConfig struct {
|
||||
type BuildAddonConfig struct {
|
||||
SrcPath string `json:"srcPath"`
|
||||
TemplatePath string `json:"templatePath"`
|
||||
WebApiPath string `json:"webApiPath"`
|
||||
WebViewsPath string `json:"webViewsPath"`
|
||||
}
|
||||
|
||||
// CacheConfig 缓存配置
|
||||
|
@ -7,7 +7,6 @@
|
||||
package adminin
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"hotgo/internal/model/entity"
|
||||
)
|
||||
|
||||
@ -57,34 +56,18 @@ type DeptListInp struct {
|
||||
Code string
|
||||
}
|
||||
|
||||
// DeptTreeDept 树
|
||||
type DeptTreeDept struct {
|
||||
// DeptTree 树
|
||||
type DeptTree struct {
|
||||
entity.AdminDept
|
||||
Children []*DeptTreeDept `json:"children"`
|
||||
Label string `json:"label" dc:"标签"`
|
||||
Value int64 `json:"value" dc:"键值"`
|
||||
Children []*DeptTree `json:"children"`
|
||||
}
|
||||
|
||||
type DeptListModel []g.Map
|
||||
|
||||
// DeptListTreeInp 获取列表树
|
||||
type DeptListTreeInp struct {
|
||||
Name string
|
||||
Code string
|
||||
type DeptListModel struct {
|
||||
List []*DeptTree `json:"list"`
|
||||
}
|
||||
|
||||
// DeptListTreeDept 树
|
||||
type DeptListTreeDept struct {
|
||||
Id int64 `json:"id" `
|
||||
Key int64 `json:"key" `
|
||||
Pid int64 `json:"pid" `
|
||||
Label string `json:"label"`
|
||||
Title string `json:"title"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Children []*DeptListTreeDept `json:"children"`
|
||||
}
|
||||
|
||||
type DeptListTreeModel DeptListTreeDept
|
||||
|
||||
// DeptStatusInp 更新部门状态
|
||||
type DeptStatusInp struct {
|
||||
entity.AdminDept
|
||||
|
@ -7,6 +7,8 @@
|
||||
package adminin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/form"
|
||||
@ -44,13 +46,13 @@ type MemberProfileInp struct {
|
||||
Id int64
|
||||
}
|
||||
type MemberProfileModel struct {
|
||||
PostGroup string `json:"postGroup" description:"岗位名称"`
|
||||
RoleGroup string `json:"roleGroup" description:"角色名称"`
|
||||
User *MemberViewModel `json:"member" description:"用户基本信息"`
|
||||
SysDept *DeptViewModel `json:"sysDept" description:"部门信息"`
|
||||
SysRoles []*RoleListModel `json:"sysRoles" description:"角色列表"`
|
||||
PostIds int64 `json:"postIds" description:"当前岗位"`
|
||||
RoleIds int64 `json:"roleIds" description:"当前角色"`
|
||||
PostGroup string `json:"postGroup" dc:"岗位名称"`
|
||||
RoleGroup string `json:"roleGroup" dc:"角色名称"`
|
||||
User *MemberViewModel `json:"member" dc:"用户基本信息"`
|
||||
SysDept *DeptViewModel `json:"sysDept" dc:"部门信息"`
|
||||
SysRoles []*RoleListModel `json:"sysRoles" dc:"角色列表"`
|
||||
PostIds int64 `json:"postIds" dc:"当前岗位"`
|
||||
RoleIds int64 `json:"roleIds" dc:"当前角色"`
|
||||
}
|
||||
|
||||
// MemberUpdateProfileInp 更新用户资料
|
||||
@ -118,36 +120,48 @@ type MemberMaxSortModel struct {
|
||||
|
||||
// MemberEditInp 修改/新增管理员
|
||||
type MemberEditInp struct {
|
||||
Id int64 `json:"id" description:""`
|
||||
RoleId int `json:"roleId" v:"required#角色不能为空" description:"角色ID"`
|
||||
PostIds []int64 `json:"postIds" v:"required#岗位不能为空" description:"岗位ID"`
|
||||
DeptId int64 `json:"deptId" v:"required#部门不能为空" description:"部门ID"`
|
||||
Username string `json:"username" v:"required#账号不能为空" description:"帐号"`
|
||||
Password string `json:"password" description:"密码"`
|
||||
RealName string `json:"realName" description:"真实姓名"`
|
||||
Avatar string `json:"avatar" description:"头像"`
|
||||
Sex string `json:"sex" description:"性别"`
|
||||
Qq string `json:"qq" description:"qq"`
|
||||
Email string `json:"email" description:"邮箱"`
|
||||
Birthday *gtime.Time `json:"birthday" description:"生日"`
|
||||
ProvinceId int `json:"provinceId" description:"省"`
|
||||
CityId int `json:"cityId" description:"城市"`
|
||||
AreaId int `json:"areaId" description:"地区"`
|
||||
Address string `json:"address" description:"默认地址"`
|
||||
Mobile string `json:"mobile" description:"手机号码"`
|
||||
Remark string `json:"remark" description:"备注"`
|
||||
Status string `json:"status" description:"状态"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" description:"修改时间"`
|
||||
Id int64 `json:"id" dc:""`
|
||||
RoleId int `json:"roleId" v:"required#角色不能为空" dc:"角色ID"`
|
||||
PostIds []int64 `json:"postIds" v:"required#岗位不能为空" dc:"岗位ID"`
|
||||
DeptId int64 `json:"deptId" v:"required#部门不能为空" dc:"部门ID"`
|
||||
Username string `json:"username" v:"required#账号不能为空" dc:"帐号"`
|
||||
PasswordHash string `json:"passwordHash" dc:"密码hash"`
|
||||
Password string `json:"password" dc:"密码"`
|
||||
RealName string `json:"realName" dc:"真实姓名"`
|
||||
Avatar string `json:"avatar" dc:"头像"`
|
||||
Sex string `json:"sex" dc:"性别"`
|
||||
Qq string `json:"qq" dc:"qq"`
|
||||
Email string `json:"email" dc:"邮箱"`
|
||||
Birthday *gtime.Time `json:"birthday" dc:"生日"`
|
||||
ProvinceId int `json:"provinceId" dc:"省"`
|
||||
CityId int `json:"cityId" dc:"城市"`
|
||||
AreaId int `json:"areaId" dc:"地区"`
|
||||
Address string `json:"address" dc:"默认地址"`
|
||||
Mobile string `json:"mobile" dc:"手机号码"`
|
||||
Remark string `json:"remark" dc:"备注"`
|
||||
Status string `json:"status" dc:"状态"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" dc:"修改时间"`
|
||||
}
|
||||
|
||||
type MemberAddInp struct {
|
||||
MemberEditInp
|
||||
PasswordHash string `json:"passwordHash" description:"密码hash"`
|
||||
Salt string `json:"salt" description:"密码盐"`
|
||||
Pid int64 `json:"pid" description:"上级ID"`
|
||||
Level int `json:"level" description:"等级"`
|
||||
Tree string `json:"tree" description:"关系树"`
|
||||
Salt string `json:"salt" dc:"密码盐"`
|
||||
Pid int64 `json:"pid" dc:"上级ID"`
|
||||
Level int `json:"level" dc:"等级"`
|
||||
Tree string `json:"tree" dc:"关系树"`
|
||||
}
|
||||
|
||||
func (in *MemberEditInp) Filter(ctx context.Context) (err error) {
|
||||
if in.Password != "" {
|
||||
if err := g.Validator().
|
||||
Rules("length:6,16").
|
||||
Messages("#新密码不能为空#新密码需在6~16之间").
|
||||
Data(in.Password).Run(ctx); err != nil {
|
||||
return err.Current()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type MemberEditModel struct{}
|
||||
@ -165,8 +179,8 @@ type MemberViewInp struct {
|
||||
|
||||
type MemberViewModel struct {
|
||||
entity.AdminMember
|
||||
DeptName string `json:"deptName" description:"所属部门"`
|
||||
RoleName string `json:"roleName" description:"所属角色"`
|
||||
DeptName string `json:"deptName" dc:"所属部门"`
|
||||
RoleName string `json:"roleName" dc:"所属角色"`
|
||||
}
|
||||
|
||||
// MemberListInp 获取列表
|
||||
@ -174,21 +188,21 @@ type MemberListInp struct {
|
||||
form.PageReq
|
||||
form.RangeDateReq
|
||||
form.StatusReq
|
||||
DeptId int `json:"deptId" dc:"部门ID"`
|
||||
Mobile int `json:"mobile" dc:"手机号"`
|
||||
DeptId int `json:"deptId" dc:"部门ID"`
|
||||
Mobile int `json:"mobile" dc:"手机号"`
|
||||
Username string `json:"username" dc:"用户名"`
|
||||
RealName string `json:"realName" dc:"真实姓名"`
|
||||
Name string `json:"name" dc:"岗位名称"`
|
||||
Code string `json:"code" dc:"岗位编码"`
|
||||
CreatedAt []int64 `json:"createdAt" dc:"创建时间"`
|
||||
Name string `json:"name" dc:"岗位名称"`
|
||||
Code string `json:"code" dc:"岗位编码"`
|
||||
CreatedAt []int64 `json:"createdAt" dc:"创建时间"`
|
||||
}
|
||||
|
||||
type MemberListModel struct {
|
||||
entity.AdminMember
|
||||
DeptName string `json:"deptName" description:"所属部门"`
|
||||
RoleName string `json:"roleName" description:"所属角色"`
|
||||
PostIds []int64 `json:"postIds" description:"岗位"`
|
||||
DeptId int64 `json:"deptId" description:"部门ID"`
|
||||
DeptName string `json:"deptName" dc:"所属部门"`
|
||||
RoleName string `json:"roleName" dc:"所属角色"`
|
||||
PostIds []int64 `json:"postIds" dc:"岗位"`
|
||||
DeptId int64 `json:"deptId" dc:"部门ID"`
|
||||
}
|
||||
|
||||
// MemberLoginInp 登录
|
||||
@ -197,31 +211,31 @@ type MemberLoginInp struct {
|
||||
Password string
|
||||
}
|
||||
type MemberLoginModel struct {
|
||||
Id int64 `json:"id" description:"用户ID"`
|
||||
Token string `json:"token" description:"登录token"`
|
||||
Expires int64 `json:"expires" description:"登录有效期"`
|
||||
Id int64 `json:"id" dc:"用户ID"`
|
||||
Token string `json:"token" dc:"登录token"`
|
||||
Expires int64 `json:"expires" dc:"登录有效期"`
|
||||
}
|
||||
|
||||
type LoginMemberInfoModel struct {
|
||||
Id int64 `json:"id" description:"用户ID"`
|
||||
DeptName string `json:"deptName" description:"所属部门"`
|
||||
RoleName string `json:"roleName" description:"所属角色"`
|
||||
Permissions []string `json:"permissions" description:"角色信息"`
|
||||
DeptId int64 `json:"-" description:"部门ID"`
|
||||
RoleId int64 `json:"-" description:"角色ID"`
|
||||
Username string `json:"username" description:"用户名"`
|
||||
RealName string `json:"realName" description:"姓名"`
|
||||
Avatar string `json:"avatar" description:"头像"`
|
||||
Balance float64 `json:"balance" description:"余额"`
|
||||
Sex int `json:"sex" description:"性别"`
|
||||
Qq string `json:"qq" description:"qq"`
|
||||
Email string `json:"email" description:"邮箱"`
|
||||
Mobile string `json:"mobile" description:"手机号码"`
|
||||
Birthday *gtime.Time `json:"birthday" description:"生日"`
|
||||
CityId int64 `json:"cityId" description:"城市编码"`
|
||||
Address string `json:"address" description:"联系地址"`
|
||||
Cash *MemberCash `json:"cash" description:"收款信息"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"`
|
||||
Id int64 `json:"id" dc:"用户ID"`
|
||||
DeptName string `json:"deptName" dc:"所属部门"`
|
||||
RoleName string `json:"roleName" dc:"所属角色"`
|
||||
Permissions []string `json:"permissions" dc:"角色信息"`
|
||||
DeptId int64 `json:"-" dc:"部门ID"`
|
||||
RoleId int64 `json:"-" dc:"角色ID"`
|
||||
Username string `json:"username" dc:"用户名"`
|
||||
RealName string `json:"realName" dc:"姓名"`
|
||||
Avatar string `json:"avatar" dc:"头像"`
|
||||
Balance float64 `json:"balance" dc:"余额"`
|
||||
Sex int `json:"sex" dc:"性别"`
|
||||
Qq string `json:"qq" dc:"qq"`
|
||||
Email string `json:"email" dc:"邮箱"`
|
||||
Mobile string `json:"mobile" dc:"手机号码"`
|
||||
Birthday *gtime.Time `json:"birthday" dc:"生日"`
|
||||
CityId int64 `json:"cityId" dc:"城市编码"`
|
||||
Address string `json:"address" dc:"联系地址"`
|
||||
Cash *MemberCash `json:"cash" dc:"收款信息"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
|
||||
*MemberLoginStatModel
|
||||
}
|
||||
|
||||
@ -246,10 +260,10 @@ type MemberSelectInp struct {
|
||||
}
|
||||
|
||||
type MemberSelectModel struct {
|
||||
Value int64 `json:"value" dc:"用户ID"`
|
||||
Label string `json:"label" dc:"真实姓名"`
|
||||
Value int64 `json:"value" dc:"用户ID"`
|
||||
Label string `json:"label" dc:"真实姓名"`
|
||||
Username string `json:"username" dc:"用户名"`
|
||||
Avatar string `json:"avatar" dc:"头像"`
|
||||
Avatar string `json:"avatar" dc:"头像"`
|
||||
}
|
||||
|
||||
// MemberLoginStatInp 用户登录统计
|
||||
|
@ -3,7 +3,6 @@
|
||||
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||
// @Author Ms <133814250@qq.com>
|
||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||
//
|
||||
package adminin
|
||||
|
||||
import (
|
||||
@ -123,9 +122,7 @@ type MenuRouteMeta struct {
|
||||
FrameSrc string `json:"frameSrc,omitempty" ` // 内联外部地址
|
||||
Permissions string `json:"permissions,omitempty"` // 菜单包含权限集合,满足其中一个就会显示
|
||||
Affix bool `json:"affix,omitempty"` // 是否固定 设置为 true 之后 多页签不可删除
|
||||
|
||||
// 自定义
|
||||
Type int `json:"type"` // 菜单类型
|
||||
Type int `json:"type"` // 菜单类型
|
||||
}
|
||||
|
||||
type MenuRoute struct {
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"hotgo/internal/model"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/form"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// RoleListInp 获取列表
|
||||
@ -17,10 +18,27 @@ type RoleListInp struct {
|
||||
form.PageReq
|
||||
}
|
||||
|
||||
type RoleListModel struct {
|
||||
type RoleTree struct {
|
||||
entity.AdminRole
|
||||
Label string `json:"label" dc:"标签"`
|
||||
Value int64 `json:"value" dc:"键值"`
|
||||
Label string `json:"label" dc:"标签"`
|
||||
Value int64 `json:"value" dc:"键值"`
|
||||
Children []*RoleTree `json:"children" dc:"子级"`
|
||||
}
|
||||
|
||||
type RoleListModel struct {
|
||||
List []*RoleTree `json:"list"`
|
||||
}
|
||||
|
||||
func Sort(v []*RoleTree) {
|
||||
sort.SliceStable(v, func(i, j int) bool {
|
||||
if v[i].Sort < v[j].Sort {
|
||||
return true
|
||||
}
|
||||
if v[i].Sort > v[j].Sort {
|
||||
return false
|
||||
}
|
||||
return v[i].Id < v[j].Id
|
||||
})
|
||||
}
|
||||
|
||||
// RoleMemberListInp 查询列表
|
||||
@ -28,15 +46,15 @@ type RoleMemberListInp struct {
|
||||
form.PageReq
|
||||
form.RangeDateReq
|
||||
form.StatusReq
|
||||
Role int `json:"role" description:"角色ID"`
|
||||
DeptId int `json:"deptId" description:"部门ID"`
|
||||
Mobile int `json:"mobile" description:"手机号"`
|
||||
Username string `json:"username" description:"用户名"`
|
||||
Realname string `json:"realname" description:"真实姓名"`
|
||||
StartTime string `json:"start_time" description:"开始时间"`
|
||||
EndTime string `json:"end_time" description:"结束时间"`
|
||||
Name string `json:"name" description:"岗位名称"`
|
||||
Code string `json:"code" description:"岗位编码"`
|
||||
Role int `json:"role" dc:"角色ID"`
|
||||
DeptId int `json:"deptId" dc:"部门ID"`
|
||||
Mobile int `json:"mobile" dc:"手机号"`
|
||||
Username string `json:"username" dc:"用户名"`
|
||||
RealName string `json:"realName" dc:"真实姓名"`
|
||||
StartTime string `json:"start_time" dc:"开始时间"`
|
||||
EndTime string `json:"end_time" dc:"结束时间"`
|
||||
Name string `json:"name" dc:"岗位名称"`
|
||||
Code string `json:"code" dc:"岗位编码"`
|
||||
}
|
||||
|
||||
type RoleMemberListModel []*MemberListModel
|
||||
@ -46,12 +64,12 @@ type MenuRoleListInp struct {
|
||||
RoleId int64
|
||||
}
|
||||
type MenuRoleListModel struct {
|
||||
Menus []*model.LabelTreeMenu `json:"menus" description:"菜单列表"`
|
||||
CheckedKeys []int64 `json:"checkedKeys" description:"选择的菜单ID"`
|
||||
Menus []*model.LabelTreeMenu `json:"menus" dc:"菜单列表"`
|
||||
CheckedKeys []int64 `json:"checkedKeys" dc:"选择的菜单ID"`
|
||||
}
|
||||
|
||||
type DataScopeEditInp struct {
|
||||
Id int64 `json:"id" v:"required" dc:"角色ID"`
|
||||
Id int64 `json:"id" v:"required" dc:"角色ID"`
|
||||
DataScope int `json:"dataScope" v:"required" dc:"数据范围"`
|
||||
CustomDept []int64 `json:"customDept" dc:"自定义部门权限"`
|
||||
CustomDept []int64 `json:"customDept" dc:"自定义部门权限"`
|
||||
}
|
||||
|
@ -7,7 +7,6 @@
|
||||
package sysin
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/form"
|
||||
)
|
||||
@ -64,4 +63,15 @@ type CronGroupStatusModel struct{}
|
||||
type CronGroupSelectInp struct {
|
||||
}
|
||||
|
||||
type CronGroupSelectModel []g.Map
|
||||
type CronGroupSelectModel struct {
|
||||
List []*CronGroupTree `json:"list"`
|
||||
}
|
||||
|
||||
type CronGroupTree struct {
|
||||
entity.SysCronGroup
|
||||
Disabled bool `json:"disabled" dc:"是否禁用"`
|
||||
Label string `json:"label" dc:"标签"`
|
||||
Value int64 `json:"value" dc:"键值"`
|
||||
Key int64 `json:"key" dc:"键名"`
|
||||
Children []*CronGroupTree `json:"children" dc:"子级"`
|
||||
}
|
||||
|
@ -23,14 +23,17 @@ type DictTypeDeleteInp struct {
|
||||
}
|
||||
type DictTypeDeleteModel struct{}
|
||||
|
||||
// DictTypeSelectInp 获取类型选项
|
||||
type DictTypeSelectInp struct {
|
||||
}
|
||||
|
||||
type DictTypeSelectModel []g.Map
|
||||
|
||||
// DictTreeSelectInp 获取类型关系树选项
|
||||
type DictTreeSelectInp struct {
|
||||
}
|
||||
|
||||
type DictTreeSelectModel []g.Map
|
||||
|
||||
type DictTypeTree struct {
|
||||
entity.SysDictType
|
||||
Disabled bool `json:"disabled" dc:"是否禁用"`
|
||||
Label string `json:"label" dc:"标签"`
|
||||
Value int64 `json:"value" dc:"键值"`
|
||||
Key int64 `json:"key" dc:"键名"`
|
||||
Children []*DictTypeTree `json:"children" dc:"子级"`
|
||||
}
|
||||
|
@ -83,11 +83,11 @@ type GenCodesSelectsModel struct {
|
||||
LinkMode form.Selects `json:"linkMode" dc:"关联表方式"`
|
||||
BuildMeth form.Selects `json:"buildMeth" dc:"生成方式"`
|
||||
// 字段表格选项
|
||||
FormMode form.Selects `json:"formMode" dc:"表单组件"`
|
||||
FormRole form.Selects `json:"formRole" dc:"表单验证"`
|
||||
DictMode DictTreeSelectModel `json:"dictMode" dc:"字典类型"`
|
||||
WhereMode form.Selects `json:"whereMode" dc:"查询条件"`
|
||||
Addons form.Selects `json:"addons" dc:"插件选项"`
|
||||
FormMode form.Selects `json:"formMode" dc:"表单组件"`
|
||||
FormRole form.Selects `json:"formRole" dc:"表单验证"`
|
||||
DictMode []*DictTypeTree `json:"dictMode" dc:"字典类型"`
|
||||
WhereMode form.Selects `json:"whereMode" dc:"查询条件"`
|
||||
Addons form.Selects `json:"addons" dc:"插件选项"`
|
||||
}
|
||||
|
||||
type GenTypeSelects []*GenTypeSelect
|
||||
|
@ -3,7 +3,6 @@
|
||||
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||
// @Author Ms <133814250@qq.com>
|
||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||
//
|
||||
package model
|
||||
|
||||
import (
|
||||
@ -12,7 +11,7 @@ import (
|
||||
|
||||
type MonitorData struct {
|
||||
// STartTime 启动时间
|
||||
STartTime *gtime.Time
|
||||
STartTime int64
|
||||
// 内网IP
|
||||
IntranetIP string
|
||||
// 公网IP
|
||||
|
@ -1,70 +0,0 @@
|
||||
// Package queues
|
||||
// @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 queues
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"hotgo/internal/library/queue"
|
||||
)
|
||||
|
||||
type jobStrategy interface {
|
||||
getTopic() string
|
||||
handle(ctx context.Context, mqMsg queue.MqMsg) (err error)
|
||||
}
|
||||
|
||||
var jobList []jobStrategy
|
||||
|
||||
func Run(ctx context.Context) {
|
||||
for _, job := range uniqueJob(jobList) {
|
||||
go func(job jobStrategy) {
|
||||
listen(ctx, job)
|
||||
}(job)
|
||||
}
|
||||
}
|
||||
|
||||
func listen(ctx context.Context, job jobStrategy) {
|
||||
var (
|
||||
topic = job.getTopic()
|
||||
consumer, err = queue.InstanceConsumer()
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
g.Log().Fatalf(ctx, "InstanceConsumer %s err:%+v", topic, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 访问日志
|
||||
if listenErr := consumer.ListenReceiveMsgDo(topic, func(mqMsg queue.MqMsg) {
|
||||
err = job.handle(ctx, mqMsg)
|
||||
|
||||
if err != nil {
|
||||
// 遇到错误,重新加入到队列
|
||||
//queue.Push(topic, mqMsg.Body)
|
||||
}
|
||||
|
||||
// 记录队列日志
|
||||
queue.ConsumerLog(ctx, topic, mqMsg, err)
|
||||
|
||||
}); listenErr != nil {
|
||||
g.Log().Fatalf(ctx, "队列:%s 监听失败, err:%+v", topic, listenErr)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// uniqueJob 去重
|
||||
func uniqueJob(languages []jobStrategy) []jobStrategy {
|
||||
result := make([]jobStrategy, 0, len(languages))
|
||||
temp := map[jobStrategy]struct{}{}
|
||||
for _, item := range languages {
|
||||
if _, ok := temp[item]; !ok {
|
||||
temp[item] = struct{}{}
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
@ -3,7 +3,6 @@
|
||||
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||
// @Author Ms <133814250@qq.com>
|
||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||
//
|
||||
package queues
|
||||
|
||||
import (
|
||||
@ -16,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
jobList = append(jobList, LoginLog)
|
||||
queue.RegisterConsumer(LoginLog)
|
||||
}
|
||||
|
||||
// LoginLog 登录日志
|
||||
@ -24,13 +23,13 @@ var LoginLog = &qLoginLog{}
|
||||
|
||||
type qLoginLog struct{}
|
||||
|
||||
// getTopic 主题
|
||||
func (q *qLoginLog) getTopic() string {
|
||||
// GetTopic 主题
|
||||
func (q *qLoginLog) GetTopic() string {
|
||||
return consts.QueueLoginLogTopic
|
||||
}
|
||||
|
||||
// handle 处理消息
|
||||
func (q *qLoginLog) handle(ctx context.Context, mqMsg queue.MqMsg) (err error) {
|
||||
// Handle 处理消息
|
||||
func (q *qLoginLog) Handle(ctx context.Context, mqMsg queue.MqMsg) (err error) {
|
||||
var data entity.SysLoginLog
|
||||
if err = json.Unmarshal(mqMsg.Body, &data); err != nil {
|
||||
return err
|
||||
|
@ -3,7 +3,6 @@
|
||||
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||
// @Author Ms <133814250@qq.com>
|
||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||
//
|
||||
package queues
|
||||
|
||||
import (
|
||||
@ -16,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
jobList = append(jobList, ServeLog)
|
||||
queue.RegisterConsumer(ServeLog)
|
||||
}
|
||||
|
||||
// ServeLog 登录日志
|
||||
@ -24,13 +23,13 @@ var ServeLog = &qServeLog{}
|
||||
|
||||
type qServeLog struct{}
|
||||
|
||||
// getTopic 主题
|
||||
func (q *qServeLog) getTopic() string {
|
||||
// GetTopic 主题
|
||||
func (q *qServeLog) GetTopic() string {
|
||||
return consts.QueueServeLogTopic
|
||||
}
|
||||
|
||||
// handle 处理消息
|
||||
func (q *qServeLog) handle(ctx context.Context, mqMsg queue.MqMsg) (err error) {
|
||||
// Handle 处理消息
|
||||
func (q *qServeLog) Handle(ctx context.Context, mqMsg queue.MqMsg) (err error) {
|
||||
var data entity.SysServeLog
|
||||
if err = json.Unmarshal(mqMsg.Body, &data); err != nil {
|
||||
return err
|
||||
|
@ -3,7 +3,6 @@
|
||||
// @Copyright Copyright (c) 2023 HotGo CLI
|
||||
// @Author Ms <133814250@qq.com>
|
||||
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
||||
//
|
||||
package queues
|
||||
|
||||
import (
|
||||
@ -16,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
jobList = append(jobList, SysLog)
|
||||
queue.RegisterConsumer(SysLog)
|
||||
}
|
||||
|
||||
// SysLog 系统日志
|
||||
@ -24,13 +23,13 @@ var SysLog = &qSysLog{}
|
||||
|
||||
type qSysLog struct{}
|
||||
|
||||
// getTopic 主题
|
||||
func (q *qSysLog) getTopic() string {
|
||||
// GetTopic 主题
|
||||
func (q *qSysLog) GetTopic() string {
|
||||
return consts.QueueLogTopic
|
||||
}
|
||||
|
||||
// handle 处理消息
|
||||
func (q *qSysLog) handle(ctx context.Context, mqMsg queue.MqMsg) (err error) {
|
||||
// Handle 处理消息
|
||||
func (q *qSysLog) Handle(ctx context.Context, mqMsg queue.MqMsg) (err error) {
|
||||
var data entity.SysLog
|
||||
if err = json.Unmarshal(mqMsg.Body, &data); err != nil {
|
||||
return err
|
||||
|
@ -38,11 +38,11 @@ func WebSocket(ctx context.Context, group *ghttp.RouterGroup) {
|
||||
|
||||
// 注册消息路由
|
||||
websocket.RegisterMsg(websocket.EventHandlers{
|
||||
"ping": common.Site.Ping, // 心跳
|
||||
"join": common.Site.Join, // 加入组
|
||||
"quit": common.Site.Quit, // 退出组
|
||||
"adminMonitorTrends": admin.Monitor.Trends, // 后台监控,动态数据
|
||||
"adminMonitorRunInfo": admin.Monitor.RunInfo, // 后台监控,运行信息
|
||||
"ping": common.Site.Ping, // 心跳
|
||||
"join": common.Site.Join, // 加入组
|
||||
"quit": common.Site.Quit, // 退出组
|
||||
"admin/monitor/trends": admin.Monitor.Trends, // 后台监控,动态数据
|
||||
"admin/monitor/runInfo": admin.Monitor.RunInfo, // 后台监控,运行信息
|
||||
})
|
||||
|
||||
}
|
||||
|
@ -15,14 +15,9 @@ import (
|
||||
"hotgo/internal/model/input/form"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
type (
|
||||
IAdminMonitor interface {
|
||||
StartMonitor(ctx context.Context)
|
||||
GetMeta(ctx context.Context) *model.MonitorData
|
||||
}
|
||||
IAdminNotice interface {
|
||||
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
|
||||
Delete(ctx context.Context, in adminin.NoticeDeleteInp) error
|
||||
@ -50,7 +45,7 @@ type (
|
||||
}
|
||||
IAdminRole interface {
|
||||
Verify(ctx context.Context, path, method string) bool
|
||||
List(ctx context.Context, in adminin.RoleListInp) (list []g.Map, totalCount int, err error)
|
||||
List(ctx context.Context, in adminin.RoleListInp) (res *adminin.RoleListModel, totalCount int, err error)
|
||||
GetName(ctx context.Context, RoleId int64) (name string, err error)
|
||||
GetMemberList(ctx context.Context, RoleId int64) (list []*adminin.RoleListModel, err error)
|
||||
GetPermissions(ctx context.Context, reqInfo *role.GetPermissionsReq) (MenuIds []int64, err error)
|
||||
@ -61,14 +56,13 @@ type (
|
||||
DataScopeEdit(ctx context.Context, in *adminin.DataScopeEditInp) (err error)
|
||||
}
|
||||
IAdminDept interface {
|
||||
NameUnique(ctx context.Context, in adminin.DeptNameUniqueInp) (*adminin.DeptNameUniqueModel, error)
|
||||
NameUnique(ctx context.Context, in adminin.DeptNameUniqueInp) (res *adminin.DeptNameUniqueModel, err error)
|
||||
Delete(ctx context.Context, in adminin.DeptDeleteInp) (err error)
|
||||
Edit(ctx context.Context, in adminin.DeptEditInp) (err error)
|
||||
Status(ctx context.Context, in adminin.DeptStatusInp) (err error)
|
||||
MaxSort(ctx context.Context, in adminin.DeptMaxSortInp) (*adminin.DeptMaxSortModel, error)
|
||||
MaxSort(ctx context.Context, in adminin.DeptMaxSortInp) (res *adminin.DeptMaxSortModel, err error)
|
||||
View(ctx context.Context, in adminin.DeptViewInp) (res *adminin.DeptViewModel, err error)
|
||||
List(ctx context.Context, in adminin.DeptListInp) (list adminin.DeptListModel, err error)
|
||||
ListTree(ctx context.Context, in adminin.DeptListTreeInp) (list []*adminin.DeptListTreeModel, err error)
|
||||
List(ctx context.Context, in adminin.DeptListInp) (res *adminin.DeptListModel, err error)
|
||||
GetName(ctx context.Context, id int64) (name string, err error)
|
||||
}
|
||||
IAdminMember interface {
|
||||
@ -97,8 +91,8 @@ type (
|
||||
MemberLoginStat(ctx context.Context, in adminin.MemberLoginStatInp) (res *adminin.MemberLoginStatModel, err error)
|
||||
}
|
||||
IAdminMemberPost interface {
|
||||
UpdatePostIds(ctx context.Context, member_id int64, post_ids []int64) (err error)
|
||||
GetMemberByIds(ctx context.Context, member_id int64) (post_ids []int64, err error)
|
||||
UpdatePostIds(ctx context.Context, memberId int64, postIds []int64) (err error)
|
||||
GetMemberByIds(ctx context.Context, memberId int64) (postIds []int64, err error)
|
||||
}
|
||||
IAdminMenu interface {
|
||||
RoleList(ctx context.Context, in adminin.MenuRoleListInp) (*adminin.MenuRoleListModel, error)
|
||||
@ -113,9 +107,14 @@ type (
|
||||
GetMenuList(ctx context.Context, memberId int64) (lists role.DynamicRes, err error)
|
||||
LoginPermissions(ctx context.Context, memberId int64) (lists adminin.MemberLoginPermissions, err error)
|
||||
}
|
||||
IAdminMonitor interface {
|
||||
StartMonitor(ctx context.Context)
|
||||
GetMeta(ctx context.Context) *model.MonitorData
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
localAdminPost IAdminPost
|
||||
localAdminRole IAdminRole
|
||||
localAdminDept IAdminDept
|
||||
localAdminMember IAdminMember
|
||||
@ -123,7 +122,6 @@ var (
|
||||
localAdminMenu IAdminMenu
|
||||
localAdminMonitor IAdminMonitor
|
||||
localAdminNotice IAdminNotice
|
||||
localAdminPost IAdminPost
|
||||
)
|
||||
|
||||
func AdminDept() IAdminDept {
|
||||
|
@ -17,6 +17,20 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
ISysAddonsConfig interface {
|
||||
GetConfigByGroup(ctx context.Context, in sysin.GetAddonsConfigInp) (res *sysin.GetAddonsConfigModel, err error)
|
||||
ConversionType(ctx context.Context, models *entity.SysAddonsConfig) (value interface{}, err error)
|
||||
UpdateConfigByGroup(ctx context.Context, in sysin.UpdateAddonsConfigInp) error
|
||||
}
|
||||
ISysAttachment interface {
|
||||
Delete(ctx context.Context, in sysin.AttachmentDeleteInp) error
|
||||
Edit(ctx context.Context, in sysin.AttachmentEditInp) (err error)
|
||||
Status(ctx context.Context, in sysin.AttachmentStatusInp) (err error)
|
||||
MaxSort(ctx context.Context, in sysin.AttachmentMaxSortInp) (*sysin.AttachmentMaxSortModel, error)
|
||||
View(ctx context.Context, in sysin.AttachmentViewInp) (res *sysin.AttachmentViewModel, err error)
|
||||
List(ctx context.Context, in sysin.AttachmentListInp) (list []*sysin.AttachmentListModel, totalCount int, err error)
|
||||
Add(ctx context.Context, meta *sysin.UploadFileMeta, fullPath, drive string) (data *entity.SysAttachment, err error)
|
||||
}
|
||||
ISysBlacklist interface {
|
||||
Delete(ctx context.Context, in sysin.BlacklistDeleteInp) (err error)
|
||||
Edit(ctx context.Context, in sysin.BlacklistEditInp) (err error)
|
||||
@ -27,12 +41,6 @@ type (
|
||||
VariableLoad(ctx context.Context, err error)
|
||||
Load(ctx context.Context)
|
||||
}
|
||||
ISysDictData interface {
|
||||
Delete(ctx context.Context, in sysin.DictDataDeleteInp) error
|
||||
Edit(ctx context.Context, in sysin.DictDataEditInp) (err error)
|
||||
List(ctx context.Context, in sysin.DictDataListInp) (list []*sysin.DictDataListModel, totalCount int, err error)
|
||||
Select(ctx context.Context, in sysin.DataSelectInp) (list sysin.DataSelectModel, err error)
|
||||
}
|
||||
ISysEmsLog interface {
|
||||
Delete(ctx context.Context, in sysin.EmsLogDeleteInp) error
|
||||
Edit(ctx context.Context, in sysin.EmsLogEditInp) (err error)
|
||||
@ -44,60 +52,6 @@ type (
|
||||
AllowSend(ctx context.Context, models *entity.SysEmsLog, config *model.EmailConfig) (err error)
|
||||
VerifyCode(ctx context.Context, in sysin.VerifyEmsCodeInp) (err error)
|
||||
}
|
||||
ISysLog interface {
|
||||
Export(ctx context.Context, in sysin.LogListInp) (err error)
|
||||
RealWrite(ctx context.Context, commonLog entity.SysLog) (err error)
|
||||
AutoLog(ctx context.Context) error
|
||||
AnalysisLog(ctx context.Context) entity.SysLog
|
||||
View(ctx context.Context, in sysin.LogViewInp) (res *sysin.LogViewModel, err error)
|
||||
Delete(ctx context.Context, in sysin.LogDeleteInp) (err error)
|
||||
List(ctx context.Context, in sysin.LogListInp) (list []*sysin.LogListModel, totalCount int, err error)
|
||||
}
|
||||
ISysConfig interface {
|
||||
GetLoadCache(ctx context.Context) (conf *model.CacheConfig, err error)
|
||||
GetLoadGenerate(ctx context.Context) (conf *model.GenerateConfig, err error)
|
||||
GetSms(ctx context.Context) (conf *model.SmsConfig, err error)
|
||||
GetGeo(ctx context.Context) (conf *model.GeoConfig, err error)
|
||||
GetUpload(ctx context.Context) (conf *model.UploadConfig, err error)
|
||||
GetSmtp(ctx context.Context) (conf *model.EmailConfig, err error)
|
||||
GetBasic(ctx context.Context) (conf *model.BasicConfig, err error)
|
||||
GetLoadSSL(ctx context.Context) (conf *model.SSLConfig, err error)
|
||||
GetLoadLog(ctx context.Context) (conf *model.LogConfig, err error)
|
||||
GetLoadServeLog(ctx context.Context) (conf *model.ServeLogConfig, err error)
|
||||
GetConfigByGroup(ctx context.Context, in sysin.GetConfigInp) (*sysin.GetConfigModel, error)
|
||||
ConversionType(ctx context.Context, models *entity.SysConfig) (value interface{}, err error)
|
||||
UpdateConfigByGroup(ctx context.Context, in sysin.UpdateConfigInp) error
|
||||
}
|
||||
ISysCron interface {
|
||||
StartCron(ctx context.Context)
|
||||
Delete(ctx context.Context, in sysin.CronDeleteInp) (err error)
|
||||
Edit(ctx context.Context, in sysin.CronEditInp) (err error)
|
||||
Status(ctx context.Context, in sysin.CronStatusInp) (err error)
|
||||
MaxSort(ctx context.Context, in sysin.CronMaxSortInp) (res *sysin.CronMaxSortModel, err error)
|
||||
View(ctx context.Context, in sysin.CronViewInp) (res *sysin.CronViewModel, err error)
|
||||
List(ctx context.Context, in sysin.CronListInp) (list []*sysin.CronListModel, totalCount int, err error)
|
||||
OnlineExec(ctx context.Context, in sysin.OnlineExecInp) (err error)
|
||||
}
|
||||
ISysCronGroup interface {
|
||||
Delete(ctx context.Context, in sysin.CronGroupDeleteInp) error
|
||||
Edit(ctx context.Context, in sysin.CronGroupEditInp) (err error)
|
||||
Status(ctx context.Context, in sysin.CronGroupStatusInp) (err error)
|
||||
MaxSort(ctx context.Context, in sysin.CronGroupMaxSortInp) (*sysin.CronGroupMaxSortModel, error)
|
||||
View(ctx context.Context, in sysin.CronGroupViewInp) (res *sysin.CronGroupViewModel, err error)
|
||||
List(ctx context.Context, in sysin.CronGroupListInp) (list []*sysin.CronGroupListModel, totalCount int, err error)
|
||||
Select(ctx context.Context, in sysin.CronGroupSelectInp) (list sysin.CronGroupSelectModel, err error)
|
||||
}
|
||||
ISysCurdDemo interface {
|
||||
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
|
||||
List(ctx context.Context, in sysin.CurdDemoListInp) (list []*sysin.CurdDemoListModel, totalCount int, err error)
|
||||
Export(ctx context.Context, in sysin.CurdDemoListInp) (err error)
|
||||
Edit(ctx context.Context, in sysin.CurdDemoEditInp) (err error)
|
||||
Delete(ctx context.Context, in sysin.CurdDemoDeleteInp) (err error)
|
||||
MaxSort(ctx context.Context, in sysin.CurdDemoMaxSortInp) (res *sysin.CurdDemoMaxSortModel, err error)
|
||||
View(ctx context.Context, in sysin.CurdDemoViewInp) (res *sysin.CurdDemoViewModel, err error)
|
||||
Status(ctx context.Context, in sysin.CurdDemoStatusInp) (err error)
|
||||
Switch(ctx context.Context, in sysin.CurdDemoSwitchInp) (err error)
|
||||
}
|
||||
ISysGenCodes interface {
|
||||
Delete(ctx context.Context, in sysin.GenCodesDeleteInp) error
|
||||
Edit(ctx context.Context, in sysin.GenCodesEditInp) (res *sysin.GenCodesEditModel, err error)
|
||||
@ -112,56 +66,6 @@ type (
|
||||
Preview(ctx context.Context, in sysin.GenCodesPreviewInp) (res *sysin.GenCodesPreviewModel, err error)
|
||||
Build(ctx context.Context, in sysin.GenCodesBuildInp) (err error)
|
||||
}
|
||||
ISysAddons interface {
|
||||
List(ctx context.Context, in sysin.AddonsListInp) (list []*sysin.AddonsListModel, totalCount int, err error)
|
||||
Selects(ctx context.Context, in sysin.AddonsSelectsInp) (res *sysin.AddonsSelectsModel, err error)
|
||||
Build(ctx context.Context, in sysin.AddonsBuildInp) (err error)
|
||||
Install(ctx context.Context, in sysin.AddonsInstallInp) (err error)
|
||||
Upgrade(ctx context.Context, in sysin.AddonsUpgradeInp) (err error)
|
||||
UnInstall(ctx context.Context, in sysin.AddonsUnInstallInp) (err error)
|
||||
}
|
||||
ISysAddonsConfig interface {
|
||||
GetConfigByGroup(ctx context.Context, in sysin.GetAddonsConfigInp) (res *sysin.GetAddonsConfigModel, err error)
|
||||
ConversionType(ctx context.Context, models *entity.SysAddonsConfig) (value interface{}, err error)
|
||||
UpdateConfigByGroup(ctx context.Context, in sysin.UpdateAddonsConfigInp) error
|
||||
}
|
||||
ISysDictType interface {
|
||||
Tree(ctx context.Context) (list []g.Map, err error)
|
||||
Delete(ctx context.Context, in sysin.DictTypeDeleteInp) error
|
||||
Edit(ctx context.Context, in sysin.DictTypeEditInp) (err error)
|
||||
Select(ctx context.Context, in sysin.DictTypeSelectInp) (list sysin.DictTypeSelectModel, err error)
|
||||
TreeSelect(ctx context.Context, in sysin.DictTreeSelectInp) (list sysin.DictTreeSelectModel, err error)
|
||||
}
|
||||
ISysLoginLog interface {
|
||||
Model(ctx context.Context) *gdb.Model
|
||||
List(ctx context.Context, in sysin.LoginLogListInp) (list []*sysin.LoginLogListModel, totalCount int, err error)
|
||||
Export(ctx context.Context, in sysin.LoginLogListInp) (err error)
|
||||
Delete(ctx context.Context, in sysin.LoginLogDeleteInp) (err error)
|
||||
View(ctx context.Context, in sysin.LoginLogViewInp) (res *sysin.LoginLogViewModel, err error)
|
||||
Push(ctx context.Context, in sysin.LoginLogPushInp)
|
||||
RealWrite(ctx context.Context, models entity.SysLoginLog) (err error)
|
||||
}
|
||||
ISysAttachment interface {
|
||||
Delete(ctx context.Context, in sysin.AttachmentDeleteInp) error
|
||||
Edit(ctx context.Context, in sysin.AttachmentEditInp) (err error)
|
||||
Status(ctx context.Context, in sysin.AttachmentStatusInp) (err error)
|
||||
MaxSort(ctx context.Context, in sysin.AttachmentMaxSortInp) (*sysin.AttachmentMaxSortModel, error)
|
||||
View(ctx context.Context, in sysin.AttachmentViewInp) (res *sysin.AttachmentViewModel, err error)
|
||||
List(ctx context.Context, in sysin.AttachmentListInp) (list []*sysin.AttachmentListModel, totalCount int, err error)
|
||||
Add(ctx context.Context, meta *sysin.UploadFileMeta, fullPath, drive string) (data *entity.SysAttachment, err error)
|
||||
}
|
||||
ISysProvinces interface {
|
||||
Tree(ctx context.Context) (list []g.Map, err error)
|
||||
Delete(ctx context.Context, in sysin.ProvincesDeleteInp) error
|
||||
Edit(ctx context.Context, in sysin.ProvincesEditInp) (err error)
|
||||
Status(ctx context.Context, in sysin.ProvincesStatusInp) (err error)
|
||||
MaxSort(ctx context.Context, in sysin.ProvincesMaxSortInp) (res *sysin.ProvincesMaxSortModel, err error)
|
||||
View(ctx context.Context, in sysin.ProvincesViewInp) (res *sysin.ProvincesViewModel, err error)
|
||||
List(ctx context.Context, in sysin.ProvincesListInp) (list []*sysin.ProvincesListModel, totalCount int, err error)
|
||||
ChildrenList(ctx context.Context, in sysin.ProvincesChildrenListInp) (list []*sysin.ProvincesChildrenListModel, totalCount int, err error)
|
||||
UniqueId(ctx context.Context, in sysin.ProvincesUniqueIdInp) (res *sysin.ProvincesUniqueIdModel, err error)
|
||||
Select(ctx context.Context, in sysin.ProvincesSelectInp) (res *sysin.ProvincesSelectModel, err error)
|
||||
}
|
||||
ISysServeLog interface {
|
||||
Model(ctx context.Context) *gdb.Model
|
||||
List(ctx context.Context, in sysin.ServeLogListInp) (list []*sysin.ServeLogListModel, totalCount int, err error)
|
||||
@ -182,28 +86,178 @@ type (
|
||||
AllowSend(ctx context.Context, models *entity.SysSmsLog, config *model.SmsConfig) (err error)
|
||||
VerifyCode(ctx context.Context, in sysin.VerifyCodeInp) (err error)
|
||||
}
|
||||
ISysAddons interface {
|
||||
List(ctx context.Context, in sysin.AddonsListInp) (list []*sysin.AddonsListModel, totalCount int, err error)
|
||||
Selects(ctx context.Context, in sysin.AddonsSelectsInp) (res *sysin.AddonsSelectsModel, err error)
|
||||
Build(ctx context.Context, in sysin.AddonsBuildInp) (err error)
|
||||
Install(ctx context.Context, in sysin.AddonsInstallInp) (err error)
|
||||
Upgrade(ctx context.Context, in sysin.AddonsUpgradeInp) (err error)
|
||||
UnInstall(ctx context.Context, in sysin.AddonsUnInstallInp) (err error)
|
||||
}
|
||||
ISysConfig interface {
|
||||
GetLoadCache(ctx context.Context) (conf *model.CacheConfig, err error)
|
||||
GetLoadGenerate(ctx context.Context) (conf *model.GenerateConfig, err error)
|
||||
GetSms(ctx context.Context) (conf *model.SmsConfig, err error)
|
||||
GetGeo(ctx context.Context) (conf *model.GeoConfig, err error)
|
||||
GetUpload(ctx context.Context) (conf *model.UploadConfig, err error)
|
||||
GetSmtp(ctx context.Context) (conf *model.EmailConfig, err error)
|
||||
GetBasic(ctx context.Context) (conf *model.BasicConfig, err error)
|
||||
GetLoadSSL(ctx context.Context) (conf *model.SSLConfig, err error)
|
||||
GetLoadLog(ctx context.Context) (conf *model.LogConfig, err error)
|
||||
GetLoadServeLog(ctx context.Context) (conf *model.ServeLogConfig, err error)
|
||||
GetConfigByGroup(ctx context.Context, in sysin.GetConfigInp) (*sysin.GetConfigModel, error)
|
||||
ConversionType(ctx context.Context, models *entity.SysConfig) (value interface{}, err error)
|
||||
UpdateConfigByGroup(ctx context.Context, in sysin.UpdateConfigInp) error
|
||||
}
|
||||
ISysLog interface {
|
||||
Export(ctx context.Context, in sysin.LogListInp) (err error)
|
||||
RealWrite(ctx context.Context, commonLog entity.SysLog) (err error)
|
||||
AutoLog(ctx context.Context) error
|
||||
AnalysisLog(ctx context.Context) entity.SysLog
|
||||
View(ctx context.Context, in sysin.LogViewInp) (res *sysin.LogViewModel, err error)
|
||||
Delete(ctx context.Context, in sysin.LogDeleteInp) (err error)
|
||||
List(ctx context.Context, in sysin.LogListInp) (list []*sysin.LogListModel, totalCount int, err error)
|
||||
}
|
||||
ISysLoginLog interface {
|
||||
Model(ctx context.Context) *gdb.Model
|
||||
List(ctx context.Context, in sysin.LoginLogListInp) (list []*sysin.LoginLogListModel, totalCount int, err error)
|
||||
Export(ctx context.Context, in sysin.LoginLogListInp) (err error)
|
||||
Delete(ctx context.Context, in sysin.LoginLogDeleteInp) (err error)
|
||||
View(ctx context.Context, in sysin.LoginLogViewInp) (res *sysin.LoginLogViewModel, err error)
|
||||
Push(ctx context.Context, in sysin.LoginLogPushInp)
|
||||
RealWrite(ctx context.Context, models entity.SysLoginLog) (err error)
|
||||
}
|
||||
ISysCronGroup interface {
|
||||
Delete(ctx context.Context, in sysin.CronGroupDeleteInp) error
|
||||
Edit(ctx context.Context, in sysin.CronGroupEditInp) (err error)
|
||||
Status(ctx context.Context, in sysin.CronGroupStatusInp) (err error)
|
||||
MaxSort(ctx context.Context, in sysin.CronGroupMaxSortInp) (*sysin.CronGroupMaxSortModel, error)
|
||||
View(ctx context.Context, in sysin.CronGroupViewInp) (res *sysin.CronGroupViewModel, err error)
|
||||
List(ctx context.Context, in sysin.CronGroupListInp) (list []*sysin.CronGroupListModel, totalCount int, err error)
|
||||
Select(ctx context.Context, in sysin.CronGroupSelectInp) (res *sysin.CronGroupSelectModel, err error)
|
||||
}
|
||||
ISysCurdDemo interface {
|
||||
Model(ctx context.Context, option ...*handler.Option) *gdb.Model
|
||||
List(ctx context.Context, in sysin.CurdDemoListInp) (list []*sysin.CurdDemoListModel, totalCount int, err error)
|
||||
Export(ctx context.Context, in sysin.CurdDemoListInp) (err error)
|
||||
Edit(ctx context.Context, in sysin.CurdDemoEditInp) (err error)
|
||||
Delete(ctx context.Context, in sysin.CurdDemoDeleteInp) (err error)
|
||||
MaxSort(ctx context.Context, in sysin.CurdDemoMaxSortInp) (res *sysin.CurdDemoMaxSortModel, err error)
|
||||
View(ctx context.Context, in sysin.CurdDemoViewInp) (res *sysin.CurdDemoViewModel, err error)
|
||||
Status(ctx context.Context, in sysin.CurdDemoStatusInp) (err error)
|
||||
Switch(ctx context.Context, in sysin.CurdDemoSwitchInp) (err error)
|
||||
}
|
||||
ISysDictData interface {
|
||||
Delete(ctx context.Context, in sysin.DictDataDeleteInp) error
|
||||
Edit(ctx context.Context, in sysin.DictDataEditInp) (err error)
|
||||
List(ctx context.Context, in sysin.DictDataListInp) (list []*sysin.DictDataListModel, totalCount int, err error)
|
||||
Select(ctx context.Context, in sysin.DataSelectInp) (list sysin.DataSelectModel, err error)
|
||||
}
|
||||
ISysDictType interface {
|
||||
Tree(ctx context.Context) (list []*sysin.DictTypeTree, err error)
|
||||
Delete(ctx context.Context, in sysin.DictTypeDeleteInp) error
|
||||
Edit(ctx context.Context, in sysin.DictTypeEditInp) (err error)
|
||||
TreeSelect(ctx context.Context, in sysin.DictTreeSelectInp) (list []*sysin.DictTypeTree, err error)
|
||||
}
|
||||
ISysProvinces interface {
|
||||
Tree(ctx context.Context) (list []g.Map, err error)
|
||||
Delete(ctx context.Context, in sysin.ProvincesDeleteInp) error
|
||||
Edit(ctx context.Context, in sysin.ProvincesEditInp) (err error)
|
||||
Status(ctx context.Context, in sysin.ProvincesStatusInp) (err error)
|
||||
MaxSort(ctx context.Context, in sysin.ProvincesMaxSortInp) (res *sysin.ProvincesMaxSortModel, err error)
|
||||
View(ctx context.Context, in sysin.ProvincesViewInp) (res *sysin.ProvincesViewModel, err error)
|
||||
List(ctx context.Context, in sysin.ProvincesListInp) (list []*sysin.ProvincesListModel, totalCount int, err error)
|
||||
ChildrenList(ctx context.Context, in sysin.ProvincesChildrenListInp) (list []*sysin.ProvincesChildrenListModel, totalCount int, err error)
|
||||
UniqueId(ctx context.Context, in sysin.ProvincesUniqueIdInp) (res *sysin.ProvincesUniqueIdModel, err error)
|
||||
Select(ctx context.Context, in sysin.ProvincesSelectInp) (res *sysin.ProvincesSelectModel, err error)
|
||||
}
|
||||
ISysCron interface {
|
||||
StartCron(ctx context.Context)
|
||||
Delete(ctx context.Context, in sysin.CronDeleteInp) (err error)
|
||||
Edit(ctx context.Context, in sysin.CronEditInp) (err error)
|
||||
Status(ctx context.Context, in sysin.CronStatusInp) (err error)
|
||||
MaxSort(ctx context.Context, in sysin.CronMaxSortInp) (res *sysin.CronMaxSortModel, err error)
|
||||
View(ctx context.Context, in sysin.CronViewInp) (res *sysin.CronViewModel, err error)
|
||||
List(ctx context.Context, in sysin.CronListInp) (list []*sysin.CronListModel, totalCount int, err error)
|
||||
OnlineExec(ctx context.Context, in sysin.OnlineExecInp) (err error)
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
localSysCron ISysCron
|
||||
localSysCronGroup ISysCronGroup
|
||||
localSysCurdDemo ISysCurdDemo
|
||||
localSysAttachment ISysAttachment
|
||||
localSysBlacklist ISysBlacklist
|
||||
localSysEmsLog ISysEmsLog
|
||||
localSysGenCodes ISysGenCodes
|
||||
localSysLog ISysLog
|
||||
localSysConfig ISysConfig
|
||||
localSysAddonsConfig ISysAddonsConfig
|
||||
localSysDictType ISysDictType
|
||||
localSysLoginLog ISysLoginLog
|
||||
localSysAddons ISysAddons
|
||||
localSysProvinces ISysProvinces
|
||||
localSysServeLog ISysServeLog
|
||||
localSysSmsLog ISysSmsLog
|
||||
localSysAttachment ISysAttachment
|
||||
localSysAddonsConfig ISysAddonsConfig
|
||||
localSysConfig ISysConfig
|
||||
localSysLog ISysLog
|
||||
localSysLoginLog ISysLoginLog
|
||||
localSysAddons ISysAddons
|
||||
localSysCurdDemo ISysCurdDemo
|
||||
localSysDictData ISysDictData
|
||||
localSysEmsLog ISysEmsLog
|
||||
localSysBlacklist ISysBlacklist
|
||||
localSysDictType ISysDictType
|
||||
localSysProvinces ISysProvinces
|
||||
localSysCronGroup ISysCronGroup
|
||||
)
|
||||
|
||||
func SysGenCodes() ISysGenCodes {
|
||||
if localSysGenCodes == nil {
|
||||
panic("implement not found for interface ISysGenCodes, forgot register?")
|
||||
}
|
||||
return localSysGenCodes
|
||||
}
|
||||
|
||||
func RegisterSysGenCodes(i ISysGenCodes) {
|
||||
localSysGenCodes = i
|
||||
}
|
||||
|
||||
func SysServeLog() ISysServeLog {
|
||||
if localSysServeLog == nil {
|
||||
panic("implement not found for interface ISysServeLog, forgot register?")
|
||||
}
|
||||
return localSysServeLog
|
||||
}
|
||||
|
||||
func RegisterSysServeLog(i ISysServeLog) {
|
||||
localSysServeLog = i
|
||||
}
|
||||
|
||||
func SysSmsLog() ISysSmsLog {
|
||||
if localSysSmsLog == nil {
|
||||
panic("implement not found for interface ISysSmsLog, forgot register?")
|
||||
}
|
||||
return localSysSmsLog
|
||||
}
|
||||
|
||||
func RegisterSysSmsLog(i ISysSmsLog) {
|
||||
localSysSmsLog = i
|
||||
}
|
||||
|
||||
func SysAddonsConfig() ISysAddonsConfig {
|
||||
if localSysAddonsConfig == nil {
|
||||
panic("implement not found for interface ISysAddonsConfig, forgot register?")
|
||||
}
|
||||
return localSysAddonsConfig
|
||||
}
|
||||
|
||||
func RegisterSysAddonsConfig(i ISysAddonsConfig) {
|
||||
localSysAddonsConfig = i
|
||||
}
|
||||
|
||||
func SysAttachment() ISysAttachment {
|
||||
if localSysAttachment == nil {
|
||||
panic("implement not found for interface ISysAttachment, forgot register?")
|
||||
}
|
||||
return localSysAttachment
|
||||
}
|
||||
|
||||
func RegisterSysAttachment(i ISysAttachment) {
|
||||
localSysAttachment = i
|
||||
}
|
||||
|
||||
func SysBlacklist() ISysBlacklist {
|
||||
if localSysBlacklist == nil {
|
||||
panic("implement not found for interface ISysBlacklist, forgot register?")
|
||||
@ -215,17 +269,6 @@ func RegisterSysBlacklist(i ISysBlacklist) {
|
||||
localSysBlacklist = i
|
||||
}
|
||||
|
||||
func SysDictData() ISysDictData {
|
||||
if localSysDictData == nil {
|
||||
panic("implement not found for interface ISysDictData, forgot register?")
|
||||
}
|
||||
return localSysDictData
|
||||
}
|
||||
|
||||
func RegisterSysDictData(i ISysDictData) {
|
||||
localSysDictData = i
|
||||
}
|
||||
|
||||
func SysEmsLog() ISysEmsLog {
|
||||
if localSysEmsLog == nil {
|
||||
panic("implement not found for interface ISysEmsLog, forgot register?")
|
||||
@ -237,6 +280,17 @@ func RegisterSysEmsLog(i ISysEmsLog) {
|
||||
localSysEmsLog = i
|
||||
}
|
||||
|
||||
func SysAddons() ISysAddons {
|
||||
if localSysAddons == nil {
|
||||
panic("implement not found for interface ISysAddons, forgot register?")
|
||||
}
|
||||
return localSysAddons
|
||||
}
|
||||
|
||||
func RegisterSysAddons(i ISysAddons) {
|
||||
localSysAddons = i
|
||||
}
|
||||
|
||||
func SysConfig() ISysConfig {
|
||||
if localSysConfig == nil {
|
||||
panic("implement not found for interface ISysConfig, forgot register?")
|
||||
@ -248,15 +302,37 @@ func RegisterSysConfig(i ISysConfig) {
|
||||
localSysConfig = i
|
||||
}
|
||||
|
||||
func SysCron() ISysCron {
|
||||
if localSysCron == nil {
|
||||
panic("implement not found for interface ISysCron, forgot register?")
|
||||
func SysLog() ISysLog {
|
||||
if localSysLog == nil {
|
||||
panic("implement not found for interface ISysLog, forgot register?")
|
||||
}
|
||||
return localSysCron
|
||||
return localSysLog
|
||||
}
|
||||
|
||||
func RegisterSysCron(i ISysCron) {
|
||||
localSysCron = i
|
||||
func RegisterSysLog(i ISysLog) {
|
||||
localSysLog = i
|
||||
}
|
||||
|
||||
func SysLoginLog() ISysLoginLog {
|
||||
if localSysLoginLog == nil {
|
||||
panic("implement not found for interface ISysLoginLog, forgot register?")
|
||||
}
|
||||
return localSysLoginLog
|
||||
}
|
||||
|
||||
func RegisterSysLoginLog(i ISysLoginLog) {
|
||||
localSysLoginLog = i
|
||||
}
|
||||
|
||||
func SysProvinces() ISysProvinces {
|
||||
if localSysProvinces == nil {
|
||||
panic("implement not found for interface ISysProvinces, forgot register?")
|
||||
}
|
||||
return localSysProvinces
|
||||
}
|
||||
|
||||
func RegisterSysProvinces(i ISysProvinces) {
|
||||
localSysProvinces = i
|
||||
}
|
||||
|
||||
func SysCronGroup() ISysCronGroup {
|
||||
@ -281,48 +357,15 @@ func RegisterSysCurdDemo(i ISysCurdDemo) {
|
||||
localSysCurdDemo = i
|
||||
}
|
||||
|
||||
func SysGenCodes() ISysGenCodes {
|
||||
if localSysGenCodes == nil {
|
||||
panic("implement not found for interface ISysGenCodes, forgot register?")
|
||||
func SysDictData() ISysDictData {
|
||||
if localSysDictData == nil {
|
||||
panic("implement not found for interface ISysDictData, forgot register?")
|
||||
}
|
||||
return localSysGenCodes
|
||||
return localSysDictData
|
||||
}
|
||||
|
||||
func RegisterSysGenCodes(i ISysGenCodes) {
|
||||
localSysGenCodes = i
|
||||
}
|
||||
|
||||
func SysLog() ISysLog {
|
||||
if localSysLog == nil {
|
||||
panic("implement not found for interface ISysLog, forgot register?")
|
||||
}
|
||||
return localSysLog
|
||||
}
|
||||
|
||||
func RegisterSysLog(i ISysLog) {
|
||||
localSysLog = i
|
||||
}
|
||||
|
||||
func SysAddons() ISysAddons {
|
||||
if localSysAddons == nil {
|
||||
panic("implement not found for interface ISysAddons, forgot register?")
|
||||
}
|
||||
return localSysAddons
|
||||
}
|
||||
|
||||
func RegisterSysAddons(i ISysAddons) {
|
||||
localSysAddons = i
|
||||
}
|
||||
|
||||
func SysAddonsConfig() ISysAddonsConfig {
|
||||
if localSysAddonsConfig == nil {
|
||||
panic("implement not found for interface ISysAddonsConfig, forgot register?")
|
||||
}
|
||||
return localSysAddonsConfig
|
||||
}
|
||||
|
||||
func RegisterSysAddonsConfig(i ISysAddonsConfig) {
|
||||
localSysAddonsConfig = i
|
||||
func RegisterSysDictData(i ISysDictData) {
|
||||
localSysDictData = i
|
||||
}
|
||||
|
||||
func SysDictType() ISysDictType {
|
||||
@ -336,57 +379,13 @@ func RegisterSysDictType(i ISysDictType) {
|
||||
localSysDictType = i
|
||||
}
|
||||
|
||||
func SysLoginLog() ISysLoginLog {
|
||||
if localSysLoginLog == nil {
|
||||
panic("implement not found for interface ISysLoginLog, forgot register?")
|
||||
func SysCron() ISysCron {
|
||||
if localSysCron == nil {
|
||||
panic("implement not found for interface ISysCron, forgot register?")
|
||||
}
|
||||
return localSysLoginLog
|
||||
return localSysCron
|
||||
}
|
||||
|
||||
func RegisterSysLoginLog(i ISysLoginLog) {
|
||||
localSysLoginLog = i
|
||||
}
|
||||
|
||||
func SysAttachment() ISysAttachment {
|
||||
if localSysAttachment == nil {
|
||||
panic("implement not found for interface ISysAttachment, forgot register?")
|
||||
}
|
||||
return localSysAttachment
|
||||
}
|
||||
|
||||
func RegisterSysAttachment(i ISysAttachment) {
|
||||
localSysAttachment = i
|
||||
}
|
||||
|
||||
func SysProvinces() ISysProvinces {
|
||||
if localSysProvinces == nil {
|
||||
panic("implement not found for interface ISysProvinces, forgot register?")
|
||||
}
|
||||
return localSysProvinces
|
||||
}
|
||||
|
||||
func RegisterSysProvinces(i ISysProvinces) {
|
||||
localSysProvinces = i
|
||||
}
|
||||
|
||||
func SysServeLog() ISysServeLog {
|
||||
if localSysServeLog == nil {
|
||||
panic("implement not found for interface ISysServeLog, forgot register?")
|
||||
}
|
||||
return localSysServeLog
|
||||
}
|
||||
|
||||
func RegisterSysServeLog(i ISysServeLog) {
|
||||
localSysServeLog = i
|
||||
}
|
||||
|
||||
func SysSmsLog() ISysSmsLog {
|
||||
if localSysSmsLog == nil {
|
||||
panic("implement not found for interface ISysSmsLog, forgot register?")
|
||||
}
|
||||
return localSysSmsLog
|
||||
}
|
||||
|
||||
func RegisterSysSmsLog(i ISysSmsLog) {
|
||||
localSysSmsLog = i
|
||||
func RegisterSysCron(i ISysCron) {
|
||||
localSysCron = i
|
||||
}
|
||||
|
@ -31,9 +31,9 @@ var (
|
||||
// Start 启动
|
||||
func Start(c context.Context) {
|
||||
ctxManager = c
|
||||
g.Log().Debug(ctxManager, "start websocket..")
|
||||
go clientManager.start()
|
||||
go clientManager.ping()
|
||||
g.Log().Debug(ctxManager, "start websocket..")
|
||||
}
|
||||
|
||||
// Stop 关闭
|
||||
|
@ -19,9 +19,9 @@ func handlerMsg(client *Client, message []byte) {
|
||||
g.Log().Warningf(ctxManager, "handlerMsg recover, err:%+v, stack:%+v", r, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
request := &WRequest{}
|
||||
err := gconv.Struct(message, request)
|
||||
if err != nil {
|
||||
|
||||
var request *WRequest
|
||||
if err := gconv.Struct(message, &request); err != nil {
|
||||
g.Log().Warningf(ctxManager, "handlerMsg 数据解析失败,err:%+v, message:%+v", err, string(message))
|
||||
return
|
||||
}
|
||||
@ -31,8 +31,6 @@ func handlerMsg(client *Client, message []byte) {
|
||||
return
|
||||
}
|
||||
|
||||
//g.Log().Infof(ctxManager, "websocket handlerMsg:%+v", request)
|
||||
|
||||
fun, ok := routers[request.Event]
|
||||
if !ok {
|
||||
g.Log().Warningf(ctxManager, "handlerMsg function id %v: not registered", request.Event)
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"hotgo/internal/cmd"
|
||||
"hotgo/internal/global"
|
||||
_ "hotgo/internal/logic"
|
||||
_ "hotgo/internal/queues"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -246,3 +246,5 @@ hggen:
|
||||
addon:
|
||||
srcPath: "./resource/generate/default/addon" # 生成模板路径
|
||||
templatePath: "./resource/template/addons/{$name}" # 页面模板路径
|
||||
webApiPath: "../web/src/api/addons/{$name}" # webApi生成路径
|
||||
webViewsPath: "../web/src/views/addons/{$name}" # web页面生成路径
|
0
server/storage/data/generate/addons/.gitkeep
Normal file
@ -29,7 +29,7 @@ var (
|
||||
defaultRowStyle = `{"font":{"color":"#666666","size":13,"family":"arial"},"alignment":{"vertical":"center","horizontal":"center"}}`
|
||||
)
|
||||
|
||||
// ExportByStructs 导出切片结构体
|
||||
// ExportByStructs 导出切片结构体到excel表格
|
||||
func ExportByStructs(ctx context.Context, tags []string, list interface{}, fileName string, sheetName string) (err error) {
|
||||
f := excelize.NewFile()
|
||||
f.SetSheetName("Sheet1", sheetName)
|
||||
|
@ -10,7 +10,6 @@ import (
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"hotgo/utility/format"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
@ -25,53 +24,6 @@ type fileInfo struct { //文件信息
|
||||
size int64
|
||||
}
|
||||
|
||||
func PathExists(path string) (bool, error) {
|
||||
info, err := os.Stat(path)
|
||||
if err == nil {
|
||||
return info.IsDir(), nil
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
func FileExists(path string) (bool, error) {
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
// HasDir 判断文件夹是否存在
|
||||
func HasDir(path string) (bool, error) {
|
||||
_, _err := os.Stat(path)
|
||||
if _err == nil {
|
||||
return true, nil
|
||||
}
|
||||
if os.IsNotExist(_err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, _err
|
||||
}
|
||||
|
||||
// CreateDir 创建文件夹
|
||||
func CreateDir(path string) (err error) {
|
||||
_exist, err := HasDir(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !_exist {
|
||||
err = os.Mkdir(path, os.ModePerm)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// WalkDir 递归获取目录下文件的名称和大小
|
||||
func WalkDir(dirname string) (error, []fileInfo) {
|
||||
op, err := filepath.Abs(dirname) //获取目录的绝对路径
|
||||
|
@ -38,6 +38,7 @@ func CheckPassword(input, salt, hash string) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// SafeGo 安全的调用协程,遇到错误时输出错误日志而不是抛出panic
|
||||
func SafeGo(ctx context.Context, f func(ctx context.Context), level ...interface{}) {
|
||||
go func() {
|
||||
defer func() {
|
||||
|
@ -10,6 +10,10 @@ import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// Filter 通用过滤器
|
||||
|
||||
// Filter 预处理和数据过滤接口,目前主要用于input层的输入过滤和内部效验。
|
||||
// 你可以在任意地方使用它,只要实现了Filter接口即可
|
||||
type Filter interface {
|
||||
// Filter gf效验规则 https://goframe.org/pages/viewpage.action?pageId=1114367
|
||||
Filter(ctx context.Context) error
|
||||
|
@ -9,22 +9,9 @@ package validate
|
||||
import (
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// InSameDay 是否为同一天
|
||||
func InSameDay(t1, t2 int64) bool {
|
||||
y1, m1, d1 := time.Unix(t1, 0).Date()
|
||||
y2, m2, d2 := time.Unix(t2, 0).Date()
|
||||
return y1 == y2 && m1 == m2 && d1 == d2
|
||||
}
|
||||
|
||||
// InSameMinute 是否为同一分钟
|
||||
func InSameMinute(t1, t2 int64) bool {
|
||||
d1 := time.Unix(t1, 0).Format("2006-01-02 15:04")
|
||||
d2 := time.Unix(t2, 0).Format("2006-01-02 15:04")
|
||||
return d1 == d2
|
||||
}
|
||||
// 是否包含判断
|
||||
|
||||
// InSliceExistStr 判断字符或切片字符是否存在指定字符
|
||||
func InSliceExistStr(elems interface{}, search string) bool {
|
||||
|
@ -14,14 +14,19 @@ import (
|
||||
"net"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 是否判断
|
||||
|
||||
// IsDNSName 是否是域名地址
|
||||
func IsDNSName(s string) bool {
|
||||
DNSName := `^([a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*[\._]?$`
|
||||
rxDNSName := regexp.MustCompile(DNSName)
|
||||
return s != "" && rxDNSName.MatchString(s)
|
||||
}
|
||||
|
||||
// IsHTTPS 是否是https请求
|
||||
func IsHTTPS(ctx context.Context) bool {
|
||||
r := ghttp.RequestFromCtx(ctx)
|
||||
if r == nil {
|
||||
@ -130,3 +135,17 @@ func IsIDCard(idCard string) bool {
|
||||
m := sum % 11
|
||||
return validate[m] == idCard[sz-1]
|
||||
}
|
||||
|
||||
// IsSameDay 是否为同一天
|
||||
func IsSameDay(t1, t2 int64) bool {
|
||||
y1, m1, d1 := time.Unix(t1, 0).Date()
|
||||
y2, m2, d2 := time.Unix(t2, 0).Date()
|
||||
return y1 == y2 && m1 == m2 && d1 == d2
|
||||
}
|
||||
|
||||
// IsSameMinute 是否为同一分钟
|
||||
func IsSameMinute(t1, t2 int64) bool {
|
||||
d1 := time.Unix(t1, 0).Format("2006-01-02 15:04")
|
||||
d2 := time.Unix(t2, 0).Format("2006-01-02 15:04")
|
||||
return d1 == d2
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hotgo",
|
||||
"version": "2.2.10",
|
||||
"version": "2.3.5",
|
||||
"author": {
|
||||
"name": "MengShuai",
|
||||
"email": "133814250@qq.com",
|
||||
|
0
web/src/api/addons/.gitkeep
Normal file
@ -44,7 +44,7 @@ export function DeleteDict(params?) {
|
||||
*/
|
||||
export function getDictSelect(params?) {
|
||||
return http.request({
|
||||
url: '/dictType/select',
|
||||
url: '/dictType/tree',
|
||||
method: 'GET',
|
||||
params,
|
||||
});
|
||||
|
@ -1,8 +1,8 @@
|
||||
export enum SocketEnum {
|
||||
EventPing = 'ping',
|
||||
EventConnected = 'connected',
|
||||
EventAdminMonitorTrends = 'adminMonitorTrends',
|
||||
EventAdminMonitorRunInfo = 'adminMonitorRunInfo',
|
||||
EventAdminMonitorTrends = 'admin/monitor/trends',
|
||||
EventAdminMonitorRunInfo = 'admin/monitor/runInfo',
|
||||
TypeQueryUser = 2,
|
||||
TypeBoardCastMsg = 3,
|
||||
TypeQuerySwitcher = 4,
|
||||
|
@ -22,7 +22,10 @@
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="文档地址">
|
||||
<div class="flex items-center">
|
||||
<a href="https://github.com/bufanyun/hotgo/tree/v2.0/docs" class="py-2" target="_blank"
|
||||
<a
|
||||
href="https://github.com/bufanyun/hotgo/tree/v2.0/docs/guide-zh-CN"
|
||||
class="py-2"
|
||||
target="_blank"
|
||||
>查看文档地址</a
|
||||
>
|
||||
</div>
|
||||
|
0
web/src/views/addons/.gitkeep
Normal file
@ -160,7 +160,7 @@
|
||||
width: 220,
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
// fixed: 'right',
|
||||
render(record) {
|
||||
return h(TableAction as any, {
|
||||
style: 'button',
|
||||
|
@ -114,7 +114,7 @@
|
||||
width: 220,
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
// fixed: 'right',
|
||||
render(record) {
|
||||
return h(TableAction as any, {
|
||||
style: 'button',
|
||||
|
@ -49,7 +49,7 @@
|
||||
}"
|
||||
>
|
||||
<n-alert :show-icon="false" type="info">
|
||||
注意:插件创建成功后会在服务端对应的项目目录中创建插件模块,并自动注册到当前项目中。
|
||||
注意:插件创建成功后会在服务端对应的项目目录中生成插件模块文件,并自动注册到当前项目中。
|
||||
</n-alert>
|
||||
<n-form
|
||||
:model="formParams"
|
||||
@ -427,10 +427,21 @@
|
||||
formBtnLoading.value = true;
|
||||
formRef.value.validate((errors) => {
|
||||
if (!errors) {
|
||||
console.log('formParams:' + JSON.stringify(formParams.value));
|
||||
Build(formParams.value).then((_res) => {
|
||||
showModal.value = false;
|
||||
buildSuccessNotify();
|
||||
dialog.info({
|
||||
title: '提示',
|
||||
content:
|
||||
'你确定要提交生成吗?热编译生成后如果列表自动刷新没有出现新插件请等几秒刷新即可。否则请手动重启服务后刷新页面!',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
Build(formParams.value).then((_res) => {
|
||||
showModal.value = false;
|
||||
buildSuccessNotify();
|
||||
});
|
||||
},
|
||||
onNegativeClick: () => {
|
||||
// message.error('取消');
|
||||
},
|
||||
});
|
||||
} else {
|
||||
message.error('请填写完整信息');
|
||||
|
@ -183,7 +183,7 @@
|
||||
width: 220,
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
// fixed: 'right',
|
||||
render(record) {
|
||||
return h(TableAction as any, {
|
||||
style: 'button',
|
||||
|
@ -186,7 +186,7 @@
|
||||
width: 220,
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
// fixed: 'right',
|
||||
render(record) {
|
||||
return h(TableAction as any, {
|
||||
style: 'button',
|
||||
|