chore: seed migration objects batch 1

This commit is contained in:
dev
2026-10-18 13:20:57 +08:00
parent 0397fe5f22
commit 135e87db8b
26 changed files with 2167 additions and 2 deletions
+8
View File
@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
+9
View File
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/im_manager.iml" filepath="$PROJECT_DIR$/.idea/im_manager.iml" />
</modules>
</component>
</project>
Generated
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
+960
View File
@@ -0,0 +1,960 @@
# HotGo 项目开发规范
**生成或修改任何代码前,必须完整阅读本文档。**
本规范是项目唯一事实来源,所有新增代码必须严格对齐现有模块风格,不得自创新的代码模式。
---
## 技术栈
| 层 | 技术 |
|----|----------------------------------------------------------------|
| 后端语言 | Go,module 名 `hotgo` |
| 后端框架 | GoFrame v2(gdb、ghttp、gcron、gerror、gctx、gerror、gjson、gvar) |
| 前端框架 | Vue 3 + Vite + TypeScript |
| UI 组件库 | **Naive UI**(优先使用,未经允许不得引入其他 UI 库) |
| 状态管理 | Pinia |
| HTTP 客户端 | 项目封装 `@/utils/http/axios`(`http.request`),禁止直接使用 axios |
---
## 后端目录结构
```
server/
├── addons/ # 插件模块(每个插件独立微架构,见"插件开发"章节)
│ ├── modules/ # 隐式注册文件(每个插件一个 .go,仅含 import)
│ └── <addonName>/ # 插件目录,与主模块结构镜像
├── api/admin/<module>/ # 主模块 HTTP 契约:Req/Res + g.Meta 路由声明
├── internal/
│ ├── consts/ # 全局常量与字典选项(枚举在此定义并注册)
│ ├── global/ # 全局公用变量(谨慎添加,尽量少用)
│ ├── controller/admin/sys/ # 薄控制器,只做参数透传,不含业务逻辑
│ ├── crons/ # 主模块定时任务注册与处理
│ ├── queues/ # 主模块消息队列消费者注册与处理
│ ├── logic/sys/ # 业务实现(sSysXxx),init() 注册到 service
│ ├── library/ # 内部基础库(addons、dict、hgorm、cache、queue、cron 等)
│ ├── service/ # 接口声明(ISysXxx)+ 注册函数
│ ├── dao/ # 对外 DAO;internal/dao/internal/ 为 CLI 生成,禁止手改
│ ├── model/
│ │ ├── entity/ # 表结构体(CLI 生成,禁止手改)
│ │ ├── do/ # ORM 写入结构(CLI 生成,禁止手改)
│ │ └── input/sysin/ # 业务入参、出参、过滤结构
│ └── router/
│ ├── admin.go # 主路由,末尾调用 genrouter.Register()
│ └── genrouter/ # 每个生成模块一个文件,init() 追加到 LoginRequiredRouter
├── resource/
│ ├── generate/default/curd/ # 主模块 CRUD 代码生成模板(参考,禁止直接修改)
│ └── generate/default/addon/ # 插件脚手架代码生成模板(参考,禁止直接修改)
└── utility/ # 通用工具:convert、excel、validate 等
```
---
## 后端分层约定
### 分层职责(禁止跨层调用)
```
api(HTTP契约) → controller(参数透传) → service(接口) → logic(业务实现) → dao(数据访问)
```
- **controller 层**:不写任何业务逻辑,只转调 service
- **logic 层**:不出现 HTTP 相关代码,所有校验通过 `Filter()` 或 `g.Validator()` 完成
- **dao 层**:`internal/dao/internal/` 由 CLI 自动生成,禁止手改
---
### 1. API 层 `api/admin/<module>/<module>.go`
- 每个接口一对 `XxxReq` / `XxxRes`,`g.Meta` 声明路由
- GET 接口用 `method:"get"`,写操作用 `method:"post"`
- 请求体嵌入 `sysin.XxxInp`,响应体嵌入对应 Model 或空 `struct{}`
```go
type ListReq struct {
g.Meta `path:"/xxxModule/list" method:"get" tags:"XXX模块" summary:"获取XXX列表"`
sysin.XxxListInp
}
type ListRes struct {
form.PageRes
List []*sysin.XxxListModel `json:"list" dc:"数据列表"`
}
```
### 2. Controller 层 `internal/controller/admin/sys/<module>.go`
- 变量:`var XxxModule = cXxxModule{}`,类型:`type cXxxModule struct{}`
- 只做参数转发和结果组装,禁止直接操作 dao 或写业务逻辑
```go
func (c *cXxxModule) List(ctx context.Context, req *xxxmodule.ListReq) (res *xxxmodule.ListRes, err error) {
list, totalCount, err := service.SysXxxModule().List(ctx, &req.XxxListInp)
if err != nil {
return
}
if list == nil {
list = []*sysin.XxxListModel{}
}
res = new(xxxmodule.ListRes)
res.List = list
res.PageRes.Pack(req, totalCount)
return
}
```
### 3. Service 层 `internal/service/sys.go`
- 在已有文件中**追加**接口声明和注册函数,禁止新建文件
- 声明 `ISysXxxModule` 接口,提供 `SysXxxModule()` 获取函数和 `RegisterSysXxxModule()` 注册函数
### 4. Logic 层 `internal/logic/sys/<module>.go`
- 结构体:`type sSysXxxModule struct{}`
- `init()` 里调用 `service.RegisterSysXxxModule(NewSysXxxModule())`
- `Model()` 方法:`return handler.Model(dao.XxxTable.Ctx(ctx), option...)`
- 列名引用必须用 `dao.XxxTable.Columns().FieldName`,禁止硬编码字符串字段名
- `Edit` 方法以 `in.Id > 0` 区分更新/新增,更新用 `XxxUpdateFields`,新增用 `XxxInsertFields`
- 字段校验在 `sysin` 的 `Filter(ctx)` 方法完成,logic 层不重复校验
### 5. Model 层 `internal/model/input/sysin/<module>.go`
| 类型 | 用途 |
|------|------|
| `XxxUpdateFields` | 更新时字段白名单 |
| `XxxInsertFields` | 新增时字段白名单 |
| `XxxEditInp` | 嵌入 `entity.XxxTable`,含 `Filter()` 校验方法 |
| `XxxListInp` | 列表查询入参,嵌入 `form.PageInp` |
| `XxxListModel` | 列表返回字段 |
| `XxxViewInp` / `XxxViewModel` | 详情入参/出参 |
| `XxxDeleteInp` | 删除入参(通常含 `Ids []int64`) |
| `XxxStatusInp`、`XxxMaxSortInp` 等 | 其他操作 |
### 6. 路由注册 `internal/router/genrouter/<module>.go`
```go
package genrouter
import "hotgo/internal/controller/admin/sys"
func init() {
LoginRequiredRouter = append(LoginRequiredRouter, sys.XxxModule) // XXX模块
}
```
新建此文件即可,`genrouter/init.go` 通过 `init()` 链自动加载,无需修改 `admin.go`。
---
## 字典与枚举规范
**字典由后端统一维护**,前端通过固定接口拉取,禁止在前端硬编码枚举值。
### 后端:定义与注册(`internal/consts/` 下对应文件)
```go
// 1. 定义常量
const (
XxxStatusEnabled = 1 // 启用
XxxStatusDisable = 2 // 禁用
)
// 2. 定义选项(选择合适的样式函数)
var XxxStatusOptions = []*model.Option{
dict.GenSuccessOption(XxxStatusEnabled, "启用"),
dict.GenWarningOption(XxxStatusDisable, "禁用"),
}
// 3. 在 init() 中注册(同文件)
func init() {
dict.RegisterEnums("XxxStatusOptions", "XXX状态选项", XxxStatusOptions)
}
```
**样式函数选择规则:**
| 函数 | 适用场景 |
|------|---------|
| `dict.GenSuccessOption` | 正常、启用、成功 |
| `dict.GenWarningOption` | 待处理、禁用、警告 |
| `dict.GenErrorOption` | 失败、封禁、危险 |
| `dict.GenInfoOption` | 信息类、中性状态 |
| `dict.GenPrimaryOption` | 主要操作、强调 |
| `dict.GenDefaultOption` | 默认、无特殊含义 |
| `dict.GenHashOption` | 不确定数量的动态枚举 |
### 后端:Logic 层中使用字典
```go
// 获取标签文本
label := dict.GetOptionLabel(consts.XxxStatusOptions, in.Status)
// 校验是否为合法值
if !dict.HasOptionKey(consts.XxxStatusOptions, in.Status) {
return gerror.New("状态值不合法")
}
```
### 前端:加载与使用字典
**加载**(在 `model.ts` 的 `loadOptions()` 中):
```ts
export function loadOptions() {
dict.loadOptions(['XxxStatusOptions', 'sys_normal_disable']);
}
```
**在 `model.ts` 的 columns 中渲染字典标签:**
```ts
import { useDictStore } from '@/store/modules/dict';
const dict = useDictStore();
// columns 中
{
title: '状态',
key: 'status',
render(row) {
return dict.getLabel('XxxStatusOptions', row.status);
}
}
```
**在搜索表单 schemas 中使用字典下拉:**
```ts
{
field: 'status',
component: 'NSelect',
label: '状态',
componentProps: {
options: computed(() => dict.getOptions('XxxStatusOptions')),
placeholder: '请选择状态',
},
}
```
---
## 插件(Addon)开发规范
> 定位:独立、临时性、工具类型的功能推荐插件化开发,例如小游戏、广告管理、文章管理、小程序、微商城等。插件之间完全隔离,方便多项目复用。
### 插件目录结构
每个插件拥有完整的独立微架构,与主模块结构镜像,但 **包路径前缀为 `hotgo/addons/<addonName>/`**:
```
server/addons/<addonName>/
├── main.go # 插件入口:定义 module 结构体,实现 Module 接口,init() 注册
├── global/
│ ├── global.go # 插件级全局变量
│ └── init.go # Init(ctx, skeleton),GetSkeleton() 方法
├── api/
│ ├── admin/ # 后台接口契约(同主模块 api 写法)
│ ├── api/ # 前台 API 接口契约
│ └── home/ # 首页接口契约(如有)
├── controller/
│ ├── admin/sys/ # 后台薄控制器
│ ├── api/ # 前台控制器
│ └── home/ # 首页控制器(如有)
├── logic/
│ ├── logic.go # 匿名导入各 logic 子包触发 init()
│ └── sys/ # 业务实现(同主模块 logic 写法)
├── service/ # 插件内接口声明与注册(独立,不与主模块 service 混用)
├── model/
│ └── input/sysin/ # 插件内入参/出参(调用主模块服务时继承主模块 input 结构)
├── router/
│ ├── admin.go # 注册后台路由,使用 addons.RouterPrefix() 获取路由前缀
│ ├── api.go # 注册前台路由
│ ├── home.go # 注册首页路由(如有)
│ ├── websocket.go # 注册 WebSocket 路由(如有)
│ └── genrouter/
│ ├── init.go # 插件版 genrouter:Register() 使用插件前缀
│ └── *.go # 每个生成模块一个文件,追加到插件 LoginRequiredRouter
├── consts/ # 插件内常量与字典(同主模块 consts 写法)
├── crons/ # 插件定时任务(可选)
├── queues/ # 插件消息队列消费者(可选)
└── resource/ # 插件静态资源与模板(可选)
```
**对应前端目录:**
```
web/src/
├── api/addons/<addonName>/ # 插件前端 API 封装
└── views/addons/<addonName>/ # 插件前端页面
```
### 插件入口 `main.go`
```go
package <addonName>
import (
"context"
"github.com/gogf/gf/v2/net/ghttp"
_ "hotgo/addons/<addonName>/crons"
"hotgo/addons/<addonName>/global"
_ "hotgo/addons/<addonName>/logic"
_ "hotgo/addons/<addonName>/queues"
"hotgo/addons/<addonName>/router"
"hotgo/internal/library/addons"
"hotgo/internal/service"
"sync"
)
type module struct {
skeleton *addons.Skeleton
ctx context.Context
sync.Mutex
}
func init() { newModule() }
func newModule() {
m := &module{
skeleton: &addons.Skeleton{
Label: "插件显示名",
Name: "<addonName>", // 与目录名一致
Group: 1,
Brief: "简介",
Description: "详细描述",
Author: "作者",
Version: "v1.0.0",
},
ctx: gctx.New(),
}
addons.RegisterModule(m)
}
func (m *module) Start(option *addons.Option) (err error) {
global.Init(m.ctx, m.skeleton)
option.Server.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(service.Middleware().Addon)
router.Admin(m.ctx, group)
router.Api(m.ctx, group)
})
return
}
// 其余方法:Stop、Ctx、GetSkeleton、Install、Upgrade、UnInstall(默认留空)
```
### 插件路由前缀规则
```
后台:/admin/<addonName>/接口路径 → 对应 controller/admin/sys/
前台:/api/<addonName>/接口路径 → 对应 controller/api/
首页:/home/<addonName>/接口路径 → 对应 controller/home/
WebSocket:/socket/<addonName>/接口路径
```
路由前缀通过 `addons.RouterPrefix(ctx, consts.AppAdmin, global.GetSkeleton().Name)` 自动生成,**禁止在路由文件里硬编码路径前缀**。
### 插件 genrouter 与主模块的区别
插件 `genrouter/init.go` 的 `Register()` 函数需要传入 `ctx` 和 `group`,并使用插件路由前缀:
```go
// 插件 genrouter/init.go
func Register(ctx context.Context, group *ghttp.RouterGroup) {
prefix := addons.RouterPrefix(ctx, consts.AppAdmin, global.GetSkeleton().Name)
group.Group(prefix, func(group *ghttp.RouterGroup) {
group.Middleware(service.Middleware().AdminAuth)
if len(LoginRequiredRouter) > 0 {
group.Bind(LoginRequiredRouter...)
}
})
}
// 插件 genrouter/<module>.go
func init() {
LoginRequiredRouter = append(LoginRequiredRouter, sys.XxxModule)
}
```
参考实现:`server/addons/hgexample/router/genrouter/`
### 插件调用主模块服务
**在插件 input 层继承主模块 input 结构,解耦参数依赖**,避免 import cycle:
```go
// 插件 model/input/sysin/config.go
package sysin
import "hotgo/internal/model/input/sysin"
type UpdateConfigInp struct {
sysin.UpdateAddonsConfigInp
}
```
```go
// 插件 logic 中调用主模块服务
import isc "hotgo/internal/service"
func (s *sSysConfig) UpdateConfigByGroup(ctx context.Context, in sysin.UpdateConfigInp) error {
in.UpdateAddonsConfigInp.AddonName = global.GetSkeleton().Name
return isc.SysAddonsConfig().UpdateConfigByGroup(ctx, in.UpdateAddonsConfigInp)
}
```
### 插件获取自身信息
```go
// 插件内部
global.GetSkeleton()
// 任意位置判断当前请求是否为插件请求
contexts.IsAddonRequest(ctx)
contexts.GetAddonName(ctx)
```
### 插件隐式注册
在 `server/addons/modules/` 下新建 `<addonName>.go`,内容仅含一行匿名导入:
```go
package modules
import _ "hotgo/addons/<addonName>"
```
### 新增插件检查清单
**后端:**
- [ ] `addons/<addonName>/main.go` — 插件入口,实现 Module 接口
- [ ] `addons/<addonName>/global/` — global.go + init.go
- [ ] `addons/modules/<addonName>.go` — 隐式注册
- [ ] `addons/<addonName>/service/` — 插件内接口声明(独立包)
- [ ] `addons/<addonName>/logic/logic.go` — 匿名导入触发 init()
- [ ] `addons/<addonName>/logic/sys/<module>.go` — 业务实现
- [ ] `addons/<addonName>/model/input/sysin/<module>.go` — 入参/出参
- [ ] `addons/<addonName>/api/admin/<module>/<module>.go` — API 契约
- [ ] `addons/<addonName>/controller/admin/sys/<module>.go` — 薄控制器
- [ ] `addons/<addonName>/router/admin.go` — 路由注册
- [ ] `addons/<addonName>/router/genrouter/init.go` — 插件 genrouter
- [ ] `addons/<addonName>/router/genrouter/<module>.go` — 模块路由追加
**前端:**
- [ ] `web/src/api/addons/<addonName>/index.ts` — API 封装
- [ ] `web/src/views/addons/<addonName>/model.ts` — State、schemas、columns
- [ ] `web/src/views/addons/<addonName>/index.vue` — 列表页
- [ ] `web/src/views/addons/<addonName>/edit.vue` — 编辑页
参考实现:`server/addons/hgexample/`(完整插件示例)、`server/resource/generate/default/addon/`(生成模板)
---
## 标准 CRUD 接口清单
| 接口 | method | 说明 |
|------|--------|------|
| `/xxx/list` | GET | 分页列表 |
| `/xxx/view` | GET | 详情 |
| `/xxx/edit` | POST | 新增/编辑(id>0 为编辑) |
| `/xxx/delete` | POST | 删除(支持批量,传 ids) |
| `/xxx/status` | POST | 启用/禁用状态切换 |
| `/xxx/switch` | POST | 开关字段切换 |
| `/xxx/maxSort` | GET | 获取最大排序值 |
| `/xxx/export` | GET | 导出列表 |
按实际需求裁剪,不强制全部实现。
---
## 前端目录结构
```
web/src/
├── api/<module>/index.ts # HTTP 请求封装
├── views/<module>/
│ ├── index.vue # 列表页
│ ├── edit.vue # 新增/编辑弹窗
│ └── model.ts # State 类、rules、schemas、columns、loadOptions
├── components/ # 通用组件(BasicTable、BasicForm、BasicModal 等)
├── store/modules/dict.ts # 字典 store(loadOptions、getLabel、getOptions)
└── utils/
├── http/axios.ts # HTTP 封装(http.request / jumpExport)
└── dateUtil.ts # 日期工具(defRangeShortcuts 等)
```
---
## 前端编码约定
### API 文件 `api/<module>/index.ts`
- 使用 `http.request`,禁止直接使用 axios
- 函数名与接口语义一致:`List`、`Edit`、`Delete`、`View`、`Status`、`Switch`、`Export`、`MaxSort`
- 导出接口用 `jumpExport('/xxx/export', params)`
```ts
import { http, jumpExport } from '@/utils/http/axios';
export function List(params) {
return http.request({ url: '/xxxModule/list', method: 'get', params });
}
export function Edit(params) {
return http.request({ url: '/xxxModule/edit', method: 'POST', params });
}
export function Export(params) {
jumpExport('/xxxModule/export', params);
}
```
### model.ts 结构
- `class State`:属性与后端 entity 字段对应,提供合理默认值
- `newState()`:工厂函数,支持深拷贝(`cloneDeep`)
- `rules`:Naive UI 表单校验规则
- `schemas`:搜索栏 `FormSchema[]`(`ref` 包装)
- `columns`:表格列定义,含权限守卫的操作用 `hasPermission(['/xxx/edit'])`
- `loadOptions()`:调用 `dict.loadOptions([...])`,预加载字典数据
### 组件使用规范
**必须优先使用 Naive UI 原生组件**,能用 Naive UI 实现的禁止自行封装替代组件:
| 场景 | 必须使用的组件 |
|------|--------------|
| 表格 | 项目封装 `BasicTable`(基于 Naive UI) |
| 表单 | 项目封装 `BasicForm`(基于 Naive UI) |
| 弹窗 | `n-modal` 或项目封装 `BasicModal` |
| 按钮 | `n-button`,类型用 `primary`/`default`/`error` 等语义值 |
| 选择器 | `n-select`(字典下拉)、`n-tree-select`(树形) |
| 开关 | `n-switch`,配合 Switch 接口 |
| 输入框 | `n-input` |
| 日期选择 | `n-date-picker`,范围选择配合 `defRangeShortcuts()` |
| 图片渲染 | `renderImage(url)` |
| 文件渲染 | `renderFile(url)` |
| 成员信息 | `renderPopoverMemberSumma(summa)` |
**禁止行为:**
- 禁止引入 Element Plus、Ant Design Vue 等其他 UI 库
- 禁止使用内联 style(使用 Naive UI 的 props 或 class)
- 禁止在模板里硬编码字典值文本,必须通过 `dict.getLabel()` 渲染
### 权限控制
```ts
import { usePermission } from '@/hooks/web/usePermission';
const { hasPermission } = usePermission();
// 模板中
v-if="hasPermission(['/xxxModule/edit'])"
// columns 中开关操作
disabled: !hasPermission(['/xxxModule/switch'])
```
权限标识与后端路由路径保持一致,**路由由后端菜单数据驱动,禁止在源码里硬编码路由路径**。
### Vue 3 代码规范
- 使用 Composition API(`<script setup>`)
- 响应式数据用 `ref` / `reactive`,计算属性用 `computed`
- 字典选项传给组件时用 `computed(() => dict.getOptions('XxxOptions'))`,保持响应式
---
## 新增 CRUD 模块检查清单
**后端(必须全部完成,顺序建议从下往上):**
- [ ] `internal/consts/<module>.go` — 常量定义、选项变量、`init()` 注册字典
- [ ] 使用 goframe CLI 生成:`entity`、`do`、`dao/internal`、`dao`(禁止手写)
- [ ] `internal/model/input/sysin/<module>.go` — 入参/出参/过滤模型
- [ ] `internal/service/sys.go` — 追加接口声明与注册函数
- [ ] `internal/logic/sys/<module>.go` — 业务实现
- [ ] `internal/controller/admin/sys/<module>.go` — 薄控制器
- [ ] `api/admin/<module>/<module>.go` — API 契约
- [ ] `internal/router/genrouter/<module>.go` — 路由注册(`LoginRequiredRouter` 追加)
**前端(必须全部完成):**
- [ ] `web/src/api/<module>/index.ts` — HTTP 封装
- [ ] `web/src/views/<module>/model.ts` — State、schemas、columns、rules、loadOptions
- [ ] `web/src/views/<module>/index.vue` — 列表页
- [ ] `web/src/views/<module>/edit.vue` — 编辑页
---
## 参考实现
**所有新模块必须严格对齐以下文件的风格,生成前请逐一阅读:**
**主模块 CRUD 参考:**
| 文件 | 说明 |
|------|------|
| `server/internal/consts/status.go` | 字典常量定义与注册标准写法 |
| `server/api/admin/curddemo/curddemo.go` | API 契约标准写法 |
| `server/internal/controller/admin/sys/curd_demo.go` | 薄控制器标准写法 |
| `server/internal/logic/sys/curd_demo.go` | Logic 标准写法(含关联查询、导出、字段过滤) |
| `server/internal/model/input/sysin/curd_demo.go` | 入参/出参模型标准写法 |
| `server/internal/router/genrouter/curd_demo.go` | 主模块路由注册标准写法 |
| `web/src/api/curdDemo/index.ts` | 前端 API 封装标准写法 |
| `web/src/views/curdDemo/model.ts` | 前端 model 标准写法(含字典加载) |
| `web/src/views/curdDemo/index.vue` | 列表页标准写法 |
| `web/src/views/curdDemo/edit.vue` | 编辑页标准写法 |
**插件参考(以 hgexample 为标准):**
| 文件 | 说明 |
|------|------|
| `server/addons/hgexample/main.go` | 插件入口标准写法 |
| `server/addons/hgexample/global/init.go` | 插件 global 标准写法 |
| `server/addons/hgexample/router/admin.go` | 插件路由注册标准写法 |
| `server/addons/hgexample/router/genrouter/init.go` | 插件 genrouter 标准写法 |
| `server/addons/hgexample/router/genrouter/tenant_order.go` | 插件模块路由追加标准写法 |
| `server/addons/hgexample/logic/sys/tenant_order.go` | 插件业务逻辑标准写法 |
| `server/addons/hgexample/logic/sys/config.go` | 插件调用主模块服务标准写法 |
代码生成模板:
- 主模块 CRUD:`server/resource/generate/default/curd/`
- 插件脚手架:`server/resource/generate/default/addon/`
以上模板可参考逻辑,**禁止直接修改模板文件**。
---
## 错误处理规范
### 基本原则
- 业务错误直接用 `gerror.New("描述清晰的中文信息")` 返回,GoFrame 框架会统一处理响应码
- 错误信息要面向用户,清晰说明**发生了什么**,而不是堆栈路径
### 错误创建方式
```go
import "github.com/gogf/gf/v2/errors/gerror"
// 普通业务错误(最常用)
return gerror.New("用户名已存在")
// 包装底层错误,追加上下文(调用第三方/dao 层出错时使用)
if err := dao.User.Ctx(ctx).Data(data).Insert(); err != nil {
return gerror.Wrap(err, "创建用户失败")
}
// 带格式化参数
return gerror.Newf("订单 %d 状态不合法,当前状态:%d", in.Id, order.Status)
```
### 错误传递原则
- **logic 层**:遇到底层错误用 `gerror.Wrap` 追加上下文后返回;纯业务校验失败用 `gerror.New` 直接返回
- **controller 层**:直接 `return` 透传 logic 的 error,**不再包装**,不打印日志(框架统一处理)
- **禁止**在 controller 层 `fmt.Println(err)` 或 `g.Log().Error` 打印已经会被框架记录的错误
---
## 缓存使用规范
### 统一使用 `cache.Instance()`
**禁止直接调用 `g.Redis().Do()`**,项目统一通过 `cache.Instance()` 操作缓存,底层适配器(Memory / Redis / File)由配置决定,业务层无需关心。
```go
import (
"hotgo/internal/library/cache"
"github.com/gogf/gf/v2/os/gcache"
"time"
)
// 设置缓存(带过期时间)
err := cache.Instance().Set(ctx, "user:info:1", userInfo, 30*time.Minute)
// 获取缓存
val, err := cache.Instance().Get(ctx, "user:info:1")
if err != nil || val.IsNil() {
// 缓存未命中,查数据库
}
// 删除缓存
_, err = cache.Instance().Remove(ctx, "user:info:1")
// GetOrSet:缓存不存在时自动执行函数并写入(推荐用于只读类缓存)
val, err := cache.Instance().GetOrSet(ctx, "user:info:1", func(ctx context.Context) (interface{}, error) {
return dao.AdminMember.Ctx(ctx).Where(...).One()
}, 30*time.Minute)
```
### 缓存 Key 命名规范
- 格式:`模块:实体:唯一标识`,例如:`user:info:1`、`config:site:basic`、`dict:options:XxxStatus`
- 同一模块的 Key 统一在 `internal/consts/cache.go`(或对应模块的 consts 文件)中定义为常量,禁止散落在 logic 里硬编码
- 插件缓存 Key 加插件名前缀:`hgexample:order:1`
### 缓存使用原则
- 更新或删除数据后,**主动清除**相关缓存,不依赖过期自动失效
- 缓存时间:频繁更新的数据(配置、状态)用较短时间(5~30 分钟);基本不变的数据(字典、枚举)可用更长时间
- 缓存穿透场景:查询结果为空时也写入短时缓存(如 1 分钟)防止击穿
---
## 日志规范
### 日志级别选择
| 级别 | 方法 | 适用场景 |
|------|------|---------|
| Info | `g.Log().Info` / `g.Log().Infof` | 正常业务流程的关键节点(登录成功、订单创建、支付回调等) |
| Warning | `g.Log().Warning` / `g.Log().Warningf` | **遇到错误但不影响主流程**(重试、降级、外部服务异常、非致命校验失败) |
| Panic | `g.Log().Panic` / `g.Log().Panicf` | 极其严重、必须立即终止的情况(配置缺失、核心依赖不可用) |
### 使用示例
```go
// Info:记录正常流程关键事件
g.Log().Infof(ctx, "用户登录成功 uid:%d ip:%s", member.Id, ip)
// Info:记录外部调用结果
g.Log().Info(ctx, "支付回调处理完成", g.Map{"orderId": orderId, "status": status})
// Warning:外部接口失败但已降级处理
g.Log().Warningf(ctx, "短信发送失败,已跳过 mobile:%s err:%+v", mobile, err)
// Warning:业务异常但不中断流程
g.Log().Warning(ctx, "用户余额不足,跳过自动扣款", g.Map{"uid": uid, "balance": balance})
// Panic:启动时核心配置缺失
g.Log().Panicf(ctx, "缓存未初始化,无法启动: %+v", err)
```
### 日志上下文原则
- 日志**必须传入 `ctx`**(第一个参数),便于链路追踪
- 关键日志需携带足够的业务字段(uid、orderId、requestId 等),用 `g.Map{}` 附加结构化信息
- **禁止**用 `fmt.Println` 替代日志,禁止在 logic/controller 层裸打印调试信息
---
## 数据库建表约定
建表时遵循以下字段命名约定,代码生成器和 ORM hook 会自动识别并处理:
| 字段名 | 类型 | 说明 |
|--------|------|------|
| `id` | bigint(20) NOT NULL AUTO_INCREMENT | 主键,必须 |
| `created_at` | datetime | 创建时间,**自动写入**,禁止手动赋值 |
| `updated_at` | datetime | 更新时间,**自动维护**,禁止手动赋值 |
| `deleted_at` | datetime | 软删除时间,存在时查询自动加 `IS NULL`,删除只更新此字段 |
| `created_by` | bigint(20) | 创建者 ID,**自动写入** |
| `updated_by` | bigint(20) | 更新者 ID,**自动维护** |
| `deleted_by` | bigint(20) | 删除者 ID,**自动写入** |
| `status` | tinyint | 状态字段,默认使用系统字典 `sys_normal_disable` |
| `sort` | int | 排序字段,存在时编辑表单自动获取最大值+1填充 |
| `pid` | bigint(20) | 树表上级ID(树表必须) |
| `level` | int | 树等级(树表必须,自动维护) |
| `tree` | varchar(512) | 关系树(树表必须,自动维护) |
| `tenant_id` | bigint(20) | 租户ID(多租户场景按需加) |
**特殊字段查询方式(代码生成器默认生成):**
- `string` 类型字段 → `LIKE` 模糊查询
- `int/uint/int64` → `=` 精确查询
- `created_by`/`updated_by`/`deleted_by` → 关键词查询(ID/用户名/手机号匹配),调用 `service.AdminMember().GetIdsByKeyword()`
**数据类型 → Go 类型映射(影响生成代码的字段类型):**
- `tinyint/int/smallint` → `int`;加 `unsigned` → `uint`
- `bigint` → `int64`;加 `unsigned` → `uint64`
- `varchar/text/char` → `string`
- `datetime/timestamp` → `*gtime.Time`
- `json/jsonb` → `*gjson.Json`
- `decimal/float/double` → `float64`
---
## 数据权限(权限过滤)
业务查询中如需过滤数据权限(仅自己/所属部门/下级等),在 ORM 链式调用中加入 handler:
```go
import "hotgo/internal/library/hgorm/handler"
// 按登录用户的数据权限范围过滤(表需含 created_by 或 member_id 字段)
dao.XxxTable.Ctx(ctx).Handler(handler.FilterAuth).Scan(&res)
// 表中无标准字段时,指定自定义字段
dao.XxxTable.Ctx(ctx).Handler(handler.FilterAuthWithField("custom_field")).Scan(&res)
// 多租户数据权限过滤(表需含 tenant_id 字段)
// 在 Model() 方法中通过 handler.Option 开启
func (s *sSysXxx) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
if len(option) == 0 {
option = append(option, &handler.Option{FilterTenant: true})
}
return handler.Model(dao.XxxTable.Ctx(ctx), option...)
}
```
多租户增改时自动维护租户关系,在 ORM 操作后加 `.Hook(hook.SaveTenant)`:
```go
import "hotgo/internal/library/hgorm/hook"
// 新增时自动写入 tenant_id/merchant_id/user_id
dao.XxxTable.Ctx(ctx).Fields(sysin.XxxInsertFields{}).Hook(hook.SaveTenant).Data(in).Insert()
// 更新时自动维护
s.Model(ctx).Fields(sysin.XxxUpdateFields{}).WherePri(in.Id).Data(in).Hook(hook.SaveTenant).Update()
```
---
## 定时任务(Crons)
定时任务通过实现接口 + `init()` 注册,在后台「系统设置 → 定时任务」中配置执行策略:
**主模块**:新建 `server/internal/crons/<name>.go`
**插件**:新建 `server/addons/<addonName>/crons/<name>.go`
```go
package crons
import (
"context"
"hotgo/internal/library/cron"
)
func init() {
cron.Register(MyTask)
}
var MyTask = &cMyTask{name: "myTask"} // name 与后台配置的任务名称一致
type cMyTask struct{ name string }
func (c *cMyTask) GetName() string { return c.name }
func (c *cMyTask) Execute(ctx context.Context, parser *cron.Parser) (err error) {
parser.Logger.Infof(ctx, "任务执行:%v", ...)
return
}
```
---
## 消息队列(Queues)
队列采用接口 + `init()` 注册,topic 统一定义在 `internal/consts/queue.go`(或对应模块 consts 文件)。
**主模块**:新建 `server/internal/queues/<name>.go`
**插件**:新建 `server/addons/<addonName>/queues/<name>.go`
```go
package queues
import (
"context"
"encoding/json"
"hotgo/internal/consts"
"hotgo/internal/library/queue"
)
func init() {
queue.RegisterConsumer(MyConsumer)
}
var MyConsumer = &qMyConsumer{}
type qMyConsumer struct{}
func (q *qMyConsumer) GetTopic() string { return consts.QueueMyTopic }
func (q *qMyConsumer) Handle(ctx context.Context, mqMsg queue.MqMsg) (err error) {
var data MyData
if err = json.Unmarshal(mqMsg.Body, &data); err != nil {
return
}
// 处理消息...
return
}
```
**发送消息:**
```go
// 即时消息
queue.Push(consts.QueueMyTopic, data)
// 延迟消息(仅 redis/rocketmq 驱动支持)
queue.SendDelayMsg(consts.QueueMyTopic, data, 10) // redis: 延迟秒数;rocketmq: 延迟级别
```
---
## 工具库使用规范
**后端(`server/utility/`)优先使用已有工具,禁止重复造轮子:**
| 包 | 用途 |
|----|------|
| `utility/convert` | 数据类型转换 |
| `utility/encrypt` | 加密/解密 |
| `utility/excel` | 电子表格导出/导入(导出接口用此包) |
| `utility/validate` | 数据验证工具 |
| `utility/tree` | 树形结构处理 |
| `utility/simple` | 简捷函数集合 |
| `utility/format` | 数据格式化 |
**前端(`web/src/utils/`)优先使用已有工具:**
| 工具 | 用途 |
|------|------|
| `@/utils/http/axios`(`http.request`) | HTTP 请求,**唯一入口** |
| `@/utils/dateUtil`(`defRangeShortcuts`) | 日期范围快捷选项 |
| `@/utils`(`renderImage`、`renderFile`、`renderPopoverMemberSumma`) | 表格渲染工具函数 |
| `@/store/modules/dict`(`useDictStore`) | 字典数据加载与渲染 |
| `@/hooks/web/usePermission`(`hasPermission`) | 权限判断 |
---
## 树表(Tree CRUD)特殊约定
树表在普通 CRUD 基础上有以下差异:
- 表中必须含 `pid`、`level`、`tree` 字段(参见建表约定)
- `pid`/`level`/`tree` 字段**由系统自动维护**,禁止在编辑表单中手动设置这三个字段
- 前端列表页使用树形表格(`BasicTable` 的 `childrenKey` 或 `treeData` 模式),而非普通分页列表
- 参考实现:`server/internal/logic/sys/tree_demo.go`、`web/src/views/develop/curd/treeDemo/`
---
## 响应格式约定
所有接口统一响应格式(由框架中间件自动处理,**无需在 controller 手动包装**):
```json
{ "code": 0, "message": "操作成功", "timestamp": 1234567890, "traceID": "xxx", "data": {} }
```
- 成功状态码:`0`;失败状态码:`-1`(GoFrame 内置,无需手动设置)
- `gerror.Wrap(err, "用户友好的提示")` → 客户端只看到最外层提示,开发者日志可见完整堆栈
- 禁止在 controller 层手动 `Write`/`WriteJSON` 等直接写响应(破坏统一格式)
---
## 常见错误禁止清单
**后端:**
- 禁止在 controller 层直接调用 dao
- 禁止硬编码字段名字符串(用 `dao.Table.Columns().Field`)
- 禁止手改 `entity/`、`do/`、`dao/internal/` 目录下的文件
- 禁止在 logic 层重复做 `Filter()` 已完成的校验
- 禁止在 `internal/service/sys.go` 之外新建主模块 service 文件(同文件追加)
- 禁止插件 import 另一个插件的包(插件间完全隔离,通过主模块 service 通信)
- 禁止在插件路由文件里硬编码路由前缀(用 `addons.RouterPrefix()` 生成)
- 禁止在 `internal/library/` 下修改基础库(如需扩展,提 issue 或在 utility/ 添加)
**前端:**
- 禁止直接使用 axios,必须用 `http.request`
- 禁止硬编码字典值文本,必须用 `dict.getLabel()` 渲染
- 禁止引入 Naive UI 以外的 UI 框架
- 禁止内联 style
- 禁止在源码里硬编码菜单路由路径
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021-present HotGo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+166 -2
View File
@@ -1,3 +1,167 @@
# im_manager
# HotGo-V2
<div align="center">
<img width="140px" src="https://gmycos.facms.cn/hotgo/logo.sig.png">
<p>
<h1>HotGo V2</h1>
</p>
<p align="center">
<a href="https://goframe.org/pages/viewpage.action?pageId=1114119" target="_blank">
<img src="https://img.shields.io/badge/goframe-2.10.0-green" alt="goframe">
</a>
<a href="https://v3.vuejs.org/" target="_blank">
<img src="https://img.shields.io/badge/vue.js-vue3.4-green" alt="vue">
</a>
<a href="https://www.naiveui.com" target="_blank">
<img src="https://img.shields.io/badge/naiveui-%3E2.44.1-blue" alt="naiveui">
</a>
<a href="https://www.tslang.cn/" target="_blank">
<img src="https://img.shields.io/badge/typescript-%3E4.0.0-blue" alt="typescript">
</a>
<a href="https://vitejs.dev/" target="_blank">
<img src="https://img.shields.io/badge/vite-%3E5.4.2-yellow" alt="vite">
</a>
<a href="https://github.com/bufanyun/hotgo/blob/v2.0/LICENSE" target="_blank">
<img src="https://img.shields.io/badge/license-MIT-success" alt="license">
</a>
</p>
</div>
## 平台简介
* 基于 GoFrame2 + Vue3 + NaiveUI + uniapp 的企业级全栈框架,为二次开发而生。
* 前端采用 Naive-Ui-Admin、Vue、Naive UI、uniapp。
* 企业级 AI 赋能基础开发平台,内置 AI 开发规范,适配主流 AI 开发工具,实现人机协同开发,大幅提升开发效率。
## 演示地址
- [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)
## 特征
* 高生产率:极强的可扩展性,应用化、模块化、插件化机制敏捷开发,几分钟即可搭建一个应用开发骨架。
* 多应用入口:多入口分为 Admin (后台)、Home (前台页面)、Api (对外通用接口)、WebSocket (即时通讯接口),不同的业务,进入不同的应用入口。
* 极致的插件化: 微核架构,功能隔离,高可定制性,可以渐进式开发,亦可以多人协同开发。支持一键创建插件模板、一键安装、更新、卸载插件、可以非常方便的将插件迁移到新项目中。
* 快速生成代码:无需编写代码,只需创建表进行简单配置就能生成一个完善的 CURD、树表等常用的开发代码,其中所需表单控件也是勾选即可直接生成。
* 认证机制:采用 JWT 的用户状态认证及 casbin 的权限认证
* 路由模式:得益于 GoFrame 提供了规范化的路由注册方式,无需注解自动生成api文档
* 模块化设计,面向接口开发
## 内置功能
1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
2. 部门管理:配置系统组织机构(公司、部门、岗位),树结构展现支持数据权限。
3. 岗位管理:配置系统用户所属担任职务。
4. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
5. 角色管理:角色菜单权限分配、设置角色按机构或按上下级关系进行数据范围权限划分。
6. 字典管理:对系统中经常使用的一些特定数据进行维护,支持枚举字典和自定义方法字典。
7. 配置管理:对系统动态配置常用参数。
8. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
9. 登录日志:系统登录日志记录查询包含登录异常。
10. 服务日志:服务端运行所产生的警告、异常、崩溃日志的详细数据和堆栈信息。
11. 支付网关:集成支付宝、微信支付、QQ支付等多种支付方式,只需简单配置即可使用。
12. 资金管理:支持在线充值、订单申请/原路退款、资金提现、资金/积分变动明细等通用模块。
13. 在线用户:当前系统中活跃用户状态监控。
14. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
15. 代码生成:支持自动化生成前后端代码。CURD关联表、树表、消息队列、定时任务一键生成等。
16. 插件应用:支持一键生成插件模板,每个插件之间开发隔离,拥有独立多应用入口、独立配置。完美支持多人协同开发、插件插拔不会对原系统产生影响等。
17. 服务监控:监视当前系统CPU、内存、磁盘、网络、堆栈等相关信息。
18. 附件管理:文件图片上传,大文件分片上传、断点续传,支持本地、阿里云oss、腾讯云cos、ucloud对象存储、七牛云对象存储、minio等多种上传驱动,后台一键切换配置,并集成了文件选择器。
19. TCP服务:基于gtcp的服务应用,支持长连接、断线重连、服务认证、路由分发、RPC消息、拦截器和数据绑定等。简化和规范了服务器开发流程。
20. 消息队列:同时兼容 kafka、redis、rocketmq、磁盘队列,一键配置切换到场景适用的MQ。
21. 通知公告:采用WebSocket实时推送在线用户最新通知、公告、私信消息。
22. 地区编码:整合国内通用省市区编码,运用于项目于一身,支持动态省市区选项。
23. 常用工具:集成常用的工具包和命令行工具,可以快速开发自定义命令行,多种启动入口。
> HotGo开源以来得到了大家的很多支持,本项目初衷只为互相学习交流,没有任何盈利性目的!欢迎为HotGo贡献代码或提供建议!
## 演示图
<table>
<tr>
<td><img src="./docs/guide-zh-CN/images/demo/1.png"/></td>
<td><img src="./docs/guide-zh-CN/images/demo/2.png"/></td>
</tr>
<tr>
<td><img src="./docs/guide-zh-CN/images/demo/3.png"/></td>
<td><img src="./docs/guide-zh-CN/images/demo/4.png"/></td>
</tr>
<tr>
<td><img src="./docs/guide-zh-CN/images/demo/5.png"/></td>
<td><img src="./docs/guide-zh-CN/images/demo/6.png"/></td>
</tr>
<tr>
<td><img src="./docs/guide-zh-CN/images/demo/7.png"/></td>
<td><img src="./docs/guide-zh-CN/images/demo/8.png"/></td>
</tr>
<tr>
<td><img src="./docs/guide-zh-CN/images/demo/9.png"/></td>
<td><img src="./docs/guide-zh-CN/images/demo/10.png"/></td>
</tr>
<tr>
<td><img src="./docs/guide-zh-CN/images/demo/11.png"/></td>
<td><img src="./docs/guide-zh-CN/images/demo/12.png"/></td>
</tr>
</table>
## 感谢(排名不分先后)
> gf框架 [https://github.com/gogf/gf](https://github.com/gogf/gf)
>
> naive-ui [https://www.naiveui.com](https://www.naiveui.com)
>
> naive-ui-admin [https://github.com/jekip/naive-ui-admin](https://github.com/jekip/naive-ui-admin)
>
> websocket [https://github.com/gorilla/websocket](github.com/gorilla/websocket)
>
> casbin [https://github.com/casbin/casbin](https://github.com/casbin/casbin)
>
> gopay [https://github.com/go-pay/gopay](https://github.com/go-pay/gopay)
## 交流QQ群
交流群①:190966648 <a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=mJafkvme3VNyiQlCFIFNRtY8Xlr7pj9U&jump_from=webapi&authKey=jL10vIESr+vO8wpxwyd6DlChzkrbHpzN9uhAsIHgAinL/Vvd+nvuRyilf2UqUlCy"><img border="0" src="https://gmycos.facms.cn/hotgo/group.png" alt="HotGo框架交流1群" title="HotGo框架交流1群"></a>
> <img src="https://gmycos.facms.cn/hotgo/hotgo1qun.png" width="400px"/>
## 商用说明
> HotGo 是开源免费的,遵循 MIT 开源协议,意味着您无需支付任何费用,也无需授权,即可将它应用到您的产品中。
* 使用本项目必须保留所有版权信息。
* 本项目包含的第三方源码和二进制文件之版权信息另行标注。
* 版权所有Copyright © 2020-2026 by Ms (https://github.com/bufanyun/hotgo)
* All rights reserved。
## 免责声明:
* HotGo为开源学习项目,一切商业行为与HotGo无关。
* 用户不得利用HotGo从事非法行为,用户应当合法合规的使用,发现用户在使用产品时有任何的非法行为,HotGo有权配合有关机关进行调查或向政府部门举报,HotGo不承担用户因非法行为造成的任何法律责任,一切法律责任由用户自行承担,如因用户使用造成第三方损害的,用户应当依法予以赔偿。
* 所有与使用HotGo相关的资源直接风险均由用户承担。
#### 如果对您有帮助,您可以点右上角 💘Star💘支持
## [感谢JetBrains提供的免费GoLand](https://jb.gg/OpenSource)
[![avatar](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://jb.gg/OpenSource)
## License
[MIT © HotGo-2026](./LICENSE)
后台管理系统
+60
View File
@@ -0,0 +1,60 @@
## 目录
#### 介绍安装
- [系统介绍](../../README.md)
- [环境搭建](start-environment.md)
- [系统安装](start-installation.md)
- [生产部署](start-deploy.md)
- [如何提问](start-questions.md)
- [常见问题](start-issue.md)
- [更新历史](start-update-log.md)
#### 系统开发
- [目录结构](sys-catalog.md)
- [开发规范](sys-exploit.md)
- [控制台](sys-console.md)
- [中间件/拦截器](sys-middleware.md)
- [WebHook](sys-webhook.md)
- [权限控制](sys-auth.md)
- [支付网关](sys-payment.md)
- [定时任务](sys-cron.md)
- [消息队列](sys-queue.md)
- [功能扩展库](sys-library.md)
- [工具方法](sys-utility.md)
- [WebSocket服务器](sys-websocket-server.md)
- [TCP服务器](sys-tcp-server.md)
- [SaaS多租户](sys-tenant.md)
- [国际化](sys-i18n.md)
- [单元测试](sys-test.md)
#### 插件模块开发
- [模块介绍及目录](addon-introduce-catalog.md)
- [模块开发流程](addon-flow.md)
- [模块辅助说明](addon-helper.md)
#### 生成代码
- [使用前提](code-start.md)
- [数据库](sys-db.md)
- [生成配置](code-config.md)
- [生成CURD](code-curd.md)
- [生成关联表CURD](code-curd-join.md)
- [生成树型CURD](code-tree.md)
- [生成业务模板](code-business.md)
- [生成模板开发](code-template-dev.md)
- [生成常见问题](code-help.md)
### 前端开发
- [表单组件](web-form.md)
- [WebSocket客户端](sys-websocket-client.md)
- [独立部署](web-deploy.md)
#### 附录
- [网址收录](append-website.md)
+216
View File
@@ -0,0 +1,216 @@
## 模块开发流程
目录
- 创建新插件
- 开发
- 调用主模块服务接口
- 访问路径
- 数据迁移
### 创建新插件
1、HotGo 后台进入 开发工具->插件管理->找到创建新插件,根据引导进行创建即可。
```shell
创建成功后默认情况下会在以下目录中生成插件文件,假设新生成的插件名为:hgexample
1. /server/addons/hgexample/ # 插件模块目录
2. /server/addons/modules/hgexample.go # 隐式注册插件文件
3. /server/resource/addons/hgexample # 静态资源和页面模板目录,属于扩展功能选项,勾选对应选项后才会生成
4. /web/src/api/addons/hgexample # webApi目录
5. /web/src/views/addons/hgexample # web页面目录
# 默认情况下没有为web页面生成菜单权限,因为在实际场景中插件不一定需要用到web页面,所以如有需要请手动到后台 权限管理 -> 菜单权限->自行添加菜单和配置权限
```
2、创建插件完毕重启服务端后,插件管理中会出现你新创建的插件信息。操作栏有几个按钮,在此进行说明
- 安装:会自动执行 server/hgexample/main.go 文件中的Install方法,方法中的具体逻辑默认为空,可以根据实际情况自行配置。如生成后台菜单、生成插件配置表初始化数据、迁移home页面、web项目文件等。
```go
// Install 安装模块
func (m *module) Install(ctx context.Context) (err error) {
// ...
return
}
```
- 更新:会自动执行 server/hgexample/main.go 文件中的Upgrade方法,方法中的具体逻辑默认为空,可以根据实际情况自行配置。
```go
// Upgrade 更新模块
func (m *module) Upgrade(ctx context.Context) (err error) {
// ...
return
}
```
- 卸载:会自动执行 server/hgexample/main.go 文件中的UnInstall方法,方法中的具体逻辑默认为空,可以根据实际情况自行配置。如会清除所有的数据表和已安装的信息等。
```go
// UnInstall 卸载模块
func (m *module) UnInstall(ctx context.Context) (err error) {
// ...
return
}
```
### 开发
完全可以根据HotGo正常的开发流程去开发对应的API、控制器、业务逻辑、插件内的应用
### 调用主模块服务接口
这里推荐的方式是在插件input层新建一个结构,继承主模块中的input结构。这样做的目的是为了服务与服务之间的输入/输出关系解耦,便于参数扩展和避免插件模块下使用`gf gen service`时出现`import cycle not allowed`。
一个简单的例子:
> 假设hgexample插件模块要通过主模块的服务接口更新插件配置
插件模块input:\server\addons\hgexample\model\input\sysin\config.go
```go
package sysin
import (
"hotgo/internal/model/input/sysin"
)
// UpdateConfigInp 更新指定配置
type UpdateConfigInp struct {
sysin.UpdateAddonsConfigInp
}
```
插件模块业务逻辑:\server\addons\hgexample\logic\sys\config.go
```go
package sys
import (
"context"
"hotgo/addons/hgexample/global"
"hotgo/addons/hgexample/model/input/sysin"
"hotgo/addons/hgexample/service"
isc "hotgo/internal/service"
)
type sSysConfig struct{}
func NewSysConfig() *sSysConfig {
return &sSysConfig{}
}
func init() {
service.RegisterSysConfig(NewSysConfig())
}
// UpdateConfigByGroup 更新指定分组的配置
func (s *sSysConfig) UpdateConfigByGroup(ctx context.Context, in sysin.UpdateConfigInp) error {
in.UpdateAddonsConfigInp.AddonName = global.GetSkeleton().Name
return isc.SysAddonsConfig().UpdateConfigByGroup(ctx, in.UpdateAddonsConfigInp)
}
```
主模块input:\server\internal\model\input\sysin\addons_config.go
```go
package sysin
import (
"github.com/gogf/gf/v2/frame/g"
)
// UpdateAddonsConfigInp 更新指定插件的配置
type UpdateAddonsConfigInp struct {
AddonName string `json:"addonName"`
Group string `json:"group"`
List g.Map `json:"list"`
}
```
主模块业务逻辑:\server\internal\logic\sys\addons_config.go
```go
package sys
import (
"context"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
)
type sSysAddonsConfig struct{}
func NewSysAddonsConfig() *sSysAddonsConfig {
return &sSysAddonsConfig{}
}
func init() {
service.RegisterSysAddonsConfig(NewSysAddonsConfig())
}
// UpdateConfigByGroup 更新指定分组的配置
func (s *sSysAddonsConfig) UpdateConfigByGroup(ctx context.Context, in sysin.UpdateAddonsConfigInp) error {
// ...
return nil
}
```
### 访问路径
#### 后台插件访问路径
```
// IP+端口或域名/admin/插件名称/API路径
如:127.0.0.1:8000/admin/hgexample/index/test
```
- 对应控制器路径:`server/addons/hgexample/controller/admin/sys/index.go`
#### 前端API插件访问路径
```
// IP+端口或域名/api/插件名称/API路径
如:127.0.0.1:8000/api/hgexample/index/test
```
- 对应控制器路径:`server/addons/hgexample/controller/api/index.go`
#### 前台页面插件访问路径
```
// IP+端口或域名/home/插件名称/API路径
如:127.0.0.1:8000/home/hgexample/index/test
```
- 对应控制器路径:`server/addons/hgexample/controller/home/index.go`
- 对应模板路径:`server/addons/hgexample/resource/public/template`
#### 静态资源插件访问路径
```
// IP+端口或域名/home/插件名称/API路径
如:127.0.0.1:8000/addons/hgexample/default
```
- 对应资源路径:`server/addons/hgexample/resource/public`
#### Websocket插件访问路径
```
// IP+端口或域名/socket/插件名称/API路径
如:127.0.0.1:8000/socket/hgexample/index/test
```
- 对应控制器路径:`server/addons/hgexample/controller/socket/index.go`
### 数据迁移
- 可以将数据迁移逻辑写进server/xxx插件/main.go 文件中的Install方法中,并遵循系统规范进行数据安装
+86
View File
@@ -0,0 +1,86 @@
## 模块辅助说明
目录
- 模块结构
- 获取模块信息
- 插件路由规则
#### 模块结构
- 文件路径:server/internal/library/addons/module.go
```go
// Skeleton 模块骨架
type Skeleton struct {
Label string `json:"label"` // 标识
Name string `json:"name"` // 名称
Group int `json:"group"` // 分组
Logo string `json:"logo"` // logo
Brief string `json:"brief"` // 简介
Description string `json:"description"` // 详细描述
Author string `json:"author"` // 作者
Version string `json:"version"` // 版本号
RootPath string `json:"rootPath"` // 根路径
View *gview.View `json:"view"` // 模板引擎
}
func (s *Skeleton) GetModule() Module {
return GetModule(s.Name)
}
// Module 插件模块
type Module interface {
Start(option *Option) (err error) // 启动模块
Stop() (err error) // 停止模块
Ctx() context.Context // 上下文
GetSkeleton() *Skeleton // 获取模块
Install(ctx context.Context) (err error) // 安装模块
Upgrade(ctx context.Context) (err error) // 更新模块
UnInstall(ctx context.Context) (err error) // 卸载模块
}
```
#### 获取模块信息
- 在插件模块内
```go
package main
import (
"fmt"
"hotgo/addons/hgexample/global"
)
func test() {
fmt.Printf("当前插件模块是:%+v", global.GetSkeleton())
}
```
- 在插件模块外
```go
package main
import (
"context"
"fmt"
"hotgo/internal/library/addons"
"hotgo/internal/library/contexts"
)
func test(ctx context.Context) {
fmt.Printf("当前是否为插件请求:%v", contexts.IsAddonRequest(ctx))
if contexts.IsAddonRequest(ctx) {
fmt.Printf("当前插件名称:%v", contexts.GetAddonName(ctx))
fmt.Printf("当前插件信息:%v", addons.GetModule(contexts.GetAddonName(ctx)))
}
}
```
- 更多辅助方法请参考插件功能库:server/internal/library/addons
#### 插件路由规则
- 如果你不喜欢现在的路由风格,可以自行调整。修改位置在:\server\internal\library\addons\addons.go的RouterPrefix方法。
- 注意调整后如web前端页面中如有之前的路由风格也需同步修改。
@@ -0,0 +1,40 @@
## 模块介绍及目录
目录
- 模块介绍
- 启动流程
- 目录结构
### 模块介绍
> 定位:开发独立、临时性、工具类型的功能时推荐使用插件化开发,例如:小游戏(大转盘/消消乐/抽奖/大屏互动/红包等)、小插件(广告管理/文章管理/友情链接等等)、小模块(报名/投票/签到)、小程序、大型插件微商城等等。
> 插件模块方便多项目复用,同时完美支持多人协同开发,每个插件模块都有独立的微架构目录结构,多插件之间完全隔离。
### 启动流程
HotGo 入口文件->隐式注入(hotgo/addons/modules)->注册所有插件->初始化已安装的插件->写入路由组->根据 HotGo 正常的开发和访问流程去开发访问插件
### 目录结构
- 详细介绍请参考:[目录结构](sys-catalog.md)
```
/server
├── addons
│ ├── modules
│ ├── xxx插件
│ | ├── api
│ | ├── consts
│ | ├── controller
│ | ├── crons
│ | ├── global
│ | ├── logic
│ | ├── model
│ | ├── queues
│ | ├── resource
│ | ├── router
│ | ├── service
│ | ├── main.go
│ | └── README.md
```
+21
View File
@@ -0,0 +1,21 @@
目录
- 框架文档
- [goframe](https://goframe.org/pages/viewpage.action?pageId=1114119)
- [naiveui](https://www.naiveui.com)
- [naive-ui-admin](https://docs.naiveadmin.com/)
- 常用组件
- [websocket](https://github.com/gorilla/websocket)
- [casbin](https://github.com/casbin/casbin)
- [tailwind](https://tailwind.docs.73zls.com/docs/flex-direction)
- 系统通用
- [redis](https://redis.io/)
- 其他
- [awesome-go](https://github.com/avelino/awesome-go)
+5
View File
@@ -0,0 +1,5 @@
## 生成业务模板
根据`api`接口文件一键生成业务模板:api、controller、logic、service
待写。
+172
View File
@@ -0,0 +1,172 @@
## 生成配置
目录
- 模板配置
- CLI配置
- 多数据库使用
### 模板配置
- 配置路径:server/manifest/config/config.yaml
```yaml
# 生成代码
hggen:
allowedIPs: [ "127.0.0.1", "*" ] # 白名单,*代表所有,只有允许的IP后台才能使用生成代码功能
selectDbs: [ "default" ] # 可选生成表的数据库配置名称,支持多库
disableTables: [ "hg_sys_gen_codes","hg_admin_role_casbin" ] # 禁用的表,禁用以后将不会在选择表中看到
delimiters: [ "@{", "}" ] # 模板引擎变量分隔符号
# 生成应用模型,所有生成模板允许自定义,可以参考default模板进行改造
application:
# CRUD和关系树列表模板
crud:
templates:
# 默认的主包模板
- group: "default" # 分组名称
isAddon: false # 是否为插件模板 false|true
masterPackage: "sys" # 主包名称,需和controllerPath、logicPath、inputPath保持关联
templatePath: "./resource/generate/default/curd" # 模板路径
apiPath: "./api/admin" # goApi生成路径
controllerPath: "./internal/controller/admin/sys" # 控制器生成路径
logicPath: "./internal/logic/sys" # 主要业务生成路径
inputPath: "./internal/model/input/sysin" # 表单过滤器生成路径
routerPath: "./internal/router/genrouter" # 生成路由表路径
sqlPath: "./storage/data/generate" # 生成sql语句路径
webApiPath: "../web/src/api" # webApi生成路径
webViewsPath: "../web/src/views" # web页面生成路径
# 默认的插件包模板,{$name}会自动替换成实际的插件名称
- group: "addon" # 分组名称
isAddon: true # 是否为插件模板 false|true
masterPackage: "sys" # 主包名称,需和controllerPath、logicPath、inputPath保持关联
templatePath: "./resource/generate/default/curd" # 模板路径
apiPath: "./addons/{$name}/api/admin" # goApi生成路径
controllerPath: "./addons/{$name}/controller/admin/sys" # 控制器生成路径
logicPath: "./addons/{$name}/logic/sys" # 主要业务生成路径
inputPath: "./addons/{$name}/model/input/sysin" # 表单过滤器生成路径
routerPath: "./addons/{$name}/router/genrouter" # 生成路由表路径
sqlPath: "./storage/data/generate/addons" # 生成sql语句路径
webApiPath: "../web/src/api/addons/{$name}" # webApi生成路径
webViewsPath: "../web/src/views/addons/{$name}" # web页面生成路径
# 消息队列模板
queue:
templates:
- group: "default"
templatePath: "./resource/generate/default/queue"
# 定时任务模板
cron:
templates:
- group: "default"
templatePath: "./resource/generate/default/cron"
# 生成插件模块,通过后台创建新插件时使用的模板,允许自定义,可以参考default模板进行改造
addon:
srcPath: "./resource/generate/default/addon" # 生成模板路径
webApiPath: "../web/src/api/addons/{$name}" # webApi生成路径
webViewsPath: "../web/src/views/addons/{$name}" # web页面生成路径
```
### CLI配置
- hotgo在生成dao、service配置时,默认了和gf官方一致的配置方式和代码生成规则。所以无论你是通过hotgo亦或gf命令生成,最终代码格式完全一致,遵循一致的代码规范。
- 配置路径:[server/hack/config.yaml](../../server/hack/config.yaml)
```yaml
gfcli:
build:
name: "hotgo" # 编译后的可执行文件名称
# arch: "all" #不填默认当前系统架构,可选:386,amd64,arm,all
# system: "all" #不填默认当前系统平台,可选:linux,darwin,windows,all
mod: "none"
cgo: 0
packSrc: "resource" # 将resource目录打包进可执行文件,静态资源无需单独部署
packDst: "internal/packed/packed.go" # 打包后生成的Go文件路径,一般使用相对路径指定到本项目目录中
version: ""
output: "./temp/hotgo" # 可执行文件生成路径
extra: ""
gen:
dao:
- link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true"
# - link: "pgsql:postgres:hg123456@tcp(127.0.0.1:5432)/hotgo"
group: "default" # 分组 使用hotgo代码生成功能时必须填
# tables: "" # 指定当前数据库中需要执行代码生成的数据表。如果为空,表示数据库的所有表都会生成。
tablesEx: "hg_sys_addons_install" # 指定当前数据库中需要排除代码生成的数据表。
removePrefix: "hg_"
descriptionTag: true
noModelComment: true
jsonCase: "CamelLower"
gJsonSupport: true
clear: true
service: # 生成业务配置
srcFolder: "internal/logic"
dstFolder: "internal/service"
dstFileNameCase: "CamelLower"
clear: true
```
### 多数据库使用
- 假设我们要增加一个库名为`hotgo2`、分组为`default2`的数据库,并要为其生成代码
1. 配置[server/hack/config.yaml](../../server/hack/config.yaml) 如下:
```yaml
gen:
dao:
- link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true"
group: "default" # 分组 使用hotgo代码生成功能时必须填
tablesEx: "hg_sys_addons_install" # 指定当前数据库中需要排除代码生成的数据表。
removePrefix: "hg_"
descriptionTag: true
noModelComment: true
jsonCase: "CamelLower"
gJsonSupport: true
clear: false
- link: "mysql:hotgo2:hg123456.@tcp(127.0.0.1:3306)/hotgo2?loc=Local&parseTime=true"
group: "default2" # 分组 使用hotgo代码生成功能时必须填
tablesEx: "hg_sys_addons_install" # 指定当前数据库中需要排除代码生成的数据表。
removePrefix: ""
descriptionTag: true
noModelComment: true
jsonCase: "CamelLower"
gJsonSupport: true
clear: false
```
2. 配置`server/manifest/config/config.yaml`,
`database`配置如下:
```yaml
database:
logger:
level: "all"
stdout: true
default:
link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true"
# link: "pgsql:postgres:hg123456@tcp(127.0.0.1:5432)/hotgo"
debug: true
Prefix: "hg_"
default2:
link: "mysql:hotgo2:hg123456.@tcp(127.0.0.1:3306)/hotgo2?loc=Local&parseTime=true"
debug: true
Prefix: ""
```
`hggen`配置如下:
```yaml
hggen:
allowedIPs: ["127.0.0.1", "*"] # 白名单,*代表所有,只有允许的IP后台才能使用生成代码功能
selectDbs: [ "default", "default2" ] # 可选生成表的数据库配置名称,支持多库
disableTables : ["hg_sys_gen_codes","hg_admin_role_casbin"] # 禁用的表,禁用以后将不会在选择表中看到
delimiters: ["@{", "}"] # 模板引擎变量分隔符号
```
3. 登录HotGo后台 -> 开发工具 -> 代码生成 -> 找到立即生成按钮并打开,就会发现`数据库`选项增加了一个`default2`,后续生成步骤和生成例子完全一样
> 注意:上述的配置中所有的`default2`名称必须保持一致
+115
View File
@@ -0,0 +1,115 @@
## 生成关联表CURD
### 热编译启动
- 推荐使用热编译方式启动HotGo,这样生成完成页面自动刷新即可看到新生成内容,无需手动重启
```shell
# 服务端
cd server
gf run main.go
# web端
cd web
pnpm run dev 或 npm run dev
```
以下是一个关联表的CURD生成流程
### 创建表结构
- 以下表结构和数据为了方便功能演示已经内置无需再次创建
- 创建主表:hg_sys_gen_curd_demo
```sql
CREATE TABLE `hg_sys_gen_curd_demo` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`category_id` bigint(20) DEFAULT '0' COMMENT '分类ID',
`title` varchar(64) NOT NULL COMMENT '标题',
`description` varchar(255) DEFAULT '' COMMENT '描述',
`content` text COMMENT '内容',
`image` varchar(255) DEFAULT NULL COMMENT '单图',
`attachfile` varchar(255) DEFAULT NULL COMMENT '附件',
`city_id` bigint(20) DEFAULT '0' COMMENT '所在城市',
`switch` int(11) DEFAULT '1' COMMENT '显示开关',
`sort` int(11) DEFAULT NULL COMMENT '排序',
`status` tinyint(1) DEFAULT '1' COMMENT '状态',
`created_by` bigint(20) DEFAULT '0' COMMENT '创建者',
`updated_by` bigint(20) DEFAULT '0' COMMENT '更新者',
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
`updated_at` datetime DEFAULT NULL COMMENT '修改时间',
`deleted_at` datetime DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COMMENT='系统_生成curd演示';
```
创建关联表:hg_test_category
```sql
CREATE TABLE `hg_test_category` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '分类ID',
`name` varchar(255) NOT NULL COMMENT '分类名称',
`short_name` varchar(128) DEFAULT NULL COMMENT '简称',
`description` varchar(255) DEFAULT NULL COMMENT '描述',
`sort` int(11) NOT NULL COMMENT '排序',
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
`status` tinyint(1) DEFAULT '1' COMMENT '状态',
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
`updated_at` datetime DEFAULT NULL COMMENT '修改时间',
`deleted_at` datetime DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COMMENT='测试分类';
```
### 表字段要求
- 使用生成时,表中必须具有以下字段和属性
| 字段名称 | 字段含义 | 字段类型 | 可为空 |
|--------|----------------------|---------------------|-----|
| id | 主键ID | bigint(20) | 否 |
### 创建生成配置
- 登录HotGo后台 -> 开发工具 -> 代码生成 -> 找到立即生成按钮并打开,选择和填写如下参数:
![](images/code/join-add.png)
### 基本设置
- 确认无误后,点击生成配置会跳转到生成配置页面,如下:
- 你可以在该页面调整生成表格表头/表列的接口功能、菜单权限、高级设置和关联表设置
![](images/code/join-init.png)
### 主表字段设置
- 在该页面你可以调整生成表格字段名称、表单组件、表单编辑/验证项、列表展示/查询项、字段排序、重置和同步字段
![](images/code/join-fields.png)
### 关联表字段设置
- 在该页面你可以调整生成表格关联表字段名称、列表展示/查询项、字段排序、重置和同步字段
- 如果存在多个关联表,也可以对多个关联表字段进行设置
![](images/code/join-fields2.png)
### 预览并生成
点击`预览代码`查看生成的代码内容。如果无需继续调整直接点击`提交生成`即可,以下是预览代码效果:
![](images/code/join-preview.png)
- 如果你使用的热编译,那么页面会在生成成功后立即刷新,刷新完成你即可在后台菜单栏中看到`测试表格`菜单。如果不是使用热编译启动,请手动重启服务后刷新。
### 生成完成
- 让我们看看生成的表格页面,效果如下:
![](images/code/join-list.png)
### 常见问题
- [生成常见问题](code-help.md)
+84
View File
@@ -0,0 +1,84 @@
## 生成CURD
### 热编译启动
- 推荐使用热编译方式启动HotGo,这样生成完成页面自动刷新即可看到新生成内容,无需手动重启
```shell
# 服务端
cd server
gf run main.go
# web端
cd web
pnpm run dev 或 npm run dev
```
以下是一个基本的CURD生成流程
### 创建表结构
- 以下表结构和数据为了方便功能演示已经内置无需再次创建
```sql
CREATE TABLE `hg_test_category` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '分类ID',
`name` varchar(255) NOT NULL COMMENT '分类名称',
`short_name` varchar(128) DEFAULT NULL COMMENT '简称',
`description` varchar(255) DEFAULT NULL COMMENT '描述',
`sort` int(11) NOT NULL COMMENT '排序',
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
`status` tinyint(1) DEFAULT '1' COMMENT '状态',
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
`updated_at` datetime DEFAULT NULL COMMENT '修改时间',
`deleted_at` datetime DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COMMENT='测试分类';
```
### 表字段要求
- 使用生成时,表中必须具有以下字段和属性
| 字段名称 | 字段含义 | 字段类型 | 可为空 |
|--------|----------------------|---------------------|-----|
| id | 主键ID | bigint(20) | 否 |
### 创建生成配置
- 登录HotGo后台 -> 开发工具 -> 代码生成 -> 找到立即生成按钮并打开,选择和填写如下参数:
![](images/code/curd-add.png)
### 基本设置
- 确认无误后,点击生成配置会跳转到生成配置页面,如下:
- 你可以在该页面调整生成表格表头/表列的接口功能、菜单权限、高级设置和关联表设置
![](images/code/curd-init.png)
### 表字段设置
- 在该页面你可以调整生成表格字段名称、表单组件、表单编辑/验证项、列表展示/查询项、字段排序、重置和同步字段
![](images/code/curd-fields.png)
### 预览并生成
点击`预览代码`查看生成的代码内容。如果无需继续调整直接点击`提交生成`即可,以下是预览代码效果:
![](images/code/curd-preview.png)
- 如果你使用的热编译,那么页面会在生成成功后立即刷新,刷新完成你即可在后台菜单栏中看到`测试表格`菜单。如果不是使用热编译启动,请手动重启服务后刷新。
### 生成完成
- 让我们看看生成的表格页面,效果如下:
![](images/code/curd-list.png)
### 常见问题
- [生成常见问题](code-help.md)
+18
View File
@@ -0,0 +1,18 @@
## 生成常见问题
### 生成完后页面提示:服务器错误,请稍候重试!
- 热编译环境下,web端往往会快于服务端重启并加载完成,此时接口访问会出现`服务器错误,请稍候重试!`或`404`。这是服务端正在重启导致的,一般稍等几秒就好,如果不行就手动重启下服务端。
### fetching tables failed: SHOW TABLES: Error 1045 (28000): Access denied for user * (using password: YES)
- 请去确认`server/manifest/config/config.yaml`和`server/hack/config.yaml`下的数据库配置一致并且权限正确
- 参考:[生成配置](code-config.md)
### 为什么后台找不到开发工具菜单
- 请去确认`server/manifest/config/config.yaml`中的`system.mode`不为`product`。product模式下后台【开发工具】菜单自动隐藏
+20
View File
@@ -0,0 +1,20 @@
## 使用前提
在HotGo中可以通过后台开发工具快速的一键生成CRUD,自动生成Api、控制器、业务逻辑、Web页面、表单组件、菜单权限等。
- hotgo 版本号 >= 2.13.1
- 使用前必须配置 [生成配置](code-config.md)
- 使用前必须了解 [数据库](sys-db.md)
相关目录
- [生成配置](code-config.md)
- [生成CURD](code-curd.md)
- [生成关联表CURD](code-curd-join.md)
- [生成树型CURD](code-tree.md)
- [生成业务模板](code-business.md)
- [生成模板开发](code-template-dev.md)
- [生成常见问题](code-help.md)
+59
View File
@@ -0,0 +1,59 @@
## 生成模板开发
### 自定义生成模板
- HotGo允许你新建新的模板分组来满足你的需求,模板可根据现有模板基础拷贝一份出来做改造,默认模板目录:[server/resource/generate/default](../../server/resource/generate/default)
- 系统内置了两组CURD生成模板,请参考[生成模板配置](sys-code.md#生成模板配置)。default:是默认的生成到主模块下;addon:是默认生成到指定的插件下
### 内置gf-cli
- 为了确保生成代码的依赖稳定性,在面对`gf`版本更新可能导致向下不兼容情况时,HotGo将`gf-cli`工具内置到系统中并进行在线执行调整,从而提供更可靠和一致的生成代码功能。
- 后续我们也将开放在线运行`gf gen ...`功能。在做插件开发时也会支持到在线生成插件下的service接口,这将会使得插件开发更加方便
### 指定gf-cli版本
- HotGo多数情况下会和最新版本的gf-cli保持同步,如果更新不及时或你不想使用最新版本的gf-cli来生成代码,可以找到自己想要的版本进行替换即可。
- 下面大致做一些替换步骤说明:
1. 打开`github.com/gogf/gf` 找到你想要使用的版本`clone`下来
2. 将`clone`代码中`gf/cmd/gf/internal/`目录覆盖到`server/internal/library/hggen/internal`
3. 将覆盖过来的目录文件中引入包名`github.com/gogf/gf/cmd/gf/v2/`批量改为`hotgo/internal/library/hggen/`
4. 运行`go mod tidy`
5. 运行`go run main.go`,如果没有报错,那么恭喜你已经完成了。如果有报错一般都是版本差异带来的影响,需要根据情况自行调整
### 指定数据库驱动
> HotGo默认使用mysql驱动,如果你想用其他数据库驱动打开下方文件中注释即可,目前已支持mysql、pgsql
- 修改文件路径:[server/internal/library/hggen/internal/cmd/cmd_gen_dao.go](../../server/internal/library/hggen/internal/cmd/cmd_gen_dao.go)
```go
package cmd
import (
//_ "github.com/gogf/gf/contrib/drivers/clickhouse/v2"
//_ "github.com/gogf/gf/contrib/drivers/mssql/v2"
_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
//_ "github.com/gogf/gf/contrib/drivers/oracle/v2"
_ "github.com/gogf/gf/contrib/drivers/pgsql/v2"
//_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
"hotgo/internal/library/hggen/internal/cmd/gendao"
)
type (
cGenDao = gendao.CGenDao
)
```
修改完成后运行`go mod tidy`
+93
View File
@@ -0,0 +1,93 @@
## 生成树型CURD
### 热编译启动
- 推荐使用热编译方式启动HotGo,这样生成完成页面自动刷新即可看到新生成内容,无需手动重启
```shell
# 服务端
cd server
gf run main.go
# web端
cd web
pnpm run dev 或 npm run dev
```
以下是一个基本的树形CURD生成流程
### 创建表结构
- 以下表结构和数据为了方便功能演示已经内置无需再次创建
```sql
CREATE TABLE `hg_sys_gen_tree_demo` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`pid` bigint(20) DEFAULT NULL COMMENT '上级ID',
`level` int(11) DEFAULT '1' COMMENT '关系树级别',
`tree` varchar(512) COMMENT '关系树',
`category_id` bigint(20) DEFAULT '0' COMMENT '分类ID',
`title` varchar(64) NOT NULL COMMENT '标题',
`description` varchar(255) DEFAULT '' COMMENT '描述',
`sort` int(11) DEFAULT NULL COMMENT '排序',
`status` tinyint(1) DEFAULT '1' COMMENT '状态',
`created_by` bigint(20) DEFAULT '0' COMMENT '创建者',
`updated_by` bigint(20) DEFAULT '0' COMMENT '更新者',
`created_at` datetime DEFAULT NULL COMMENT '创建时间',
`updated_at` datetime DEFAULT NULL COMMENT '修改时间',
`deleted_at` datetime DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8mb4 COMMENT='系统_生成树表演示';
```
### 表字段要求
- 使用生成时,表中必须具有以下字段和属性
| 字段名称 | 字段含义 | 字段类型 | 可为空 |
|--------|----------------------|---------------------|-----|
| id | 主键ID | bigint(20) | 否 |
| pid | 上级树ID | bigint(20) | 是 |
| level | 关系树级别 | int(11) | 否 |
| tree | 关系树 | varchar(512) | 是 |
### 创建生成配置
- 登录HotGo后台 -> 开发工具 -> 代码生成 -> 找到立即生成按钮并打开,选择和填写如下参数:
![](images/code/tree-add.png)
### 基本设置
- 确认无误后,点击生成配置会跳转到生成配置页面,如下:
- 你可以在该页面调整生成表格表头/表列的接口功能、菜单权限、高级设置和关联表设置
- 相比普通CURD,这里多了`树名称字段`和`树表格样式`选项
![](images/code/tree-init.png)
### 表字段设置
- 在该页面你可以调整生成表格字段名称、表单组件、表单编辑/验证项、列表展示/查询项、字段排序、重置和同步字段
- 树表中的pid、level、tree字段由系统维护,单独设置编辑表单功能无效!
![](images/code/tree-fields.png)
### 预览并生成
点击`预览代码`查看生成的代码内容。如果无需继续调整直接点击`提交生成`即可,以下是预览代码效果:
![](images/code/tree-preview.png)
- 如果你使用的热编译,那么页面会在生成成功后立即刷新,刷新完成你即可在后台菜单栏中看到`测试表格`菜单。如果不是使用热编译启动,请手动重启服务后刷新。
### 生成完成
- 让我们看看生成的表格页面,效果如下:
- 普通树表
![](images/code/tree-list.png)
- 选项式树表
![](images/code/tree-list2.png)
### 常见问题
- [生成常见问题](code-help.md)
Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB