This commit is contained in:
孟帅
2023-02-23 17:53:04 +08:00
parent 7cf1b8ce8e
commit 61d0988d2c
402 changed files with 18340 additions and 35547 deletions

View File

@@ -0,0 +1,50 @@
## 目录
#### 介绍安装
- [系统介绍](../../README.md)
- [环境搭建](start-environment.md)
- [系统安装](start-installation.md)
- 生产部署
- [如何提问](start-questions.md)
- [常见问题](start-issue.md)
- [更新历史](start-update-log.md)
#### 系统开发
- [目录结构](sys-catalog.md)
- 开发规范
- [控制台](sys-console.md)
- 请求中间件和WebHook
- 权限控制
- 代码生成
- 定时任务
- [消息队列](sys-queue.md)
- [功能扩展库](sys-library.md)
- 工具方法
- RESTful Api
- Websocket服务器
- 单元测试
#### 插件模块开发
- [模块介绍及目录](addon-introduce-catalog.md)
- [模块开发流程](addon-flow.md)
- [模块辅助说明](addon-helper.md)
#### 实战开发
- 服务端
- web前端
### 前端开发
- 表单组件
- 权限
- Websocket客户端
- 工具库
- 发布部署
#### 附录
- [网址收录](append-website.md)

View File

@@ -0,0 +1,195 @@
## 模块开发流程
目录
- 创建新插件
- 开发
- 调用主模块服务接口
- 访问路径
- 数据迁移
### 创建新插件
1、HotGo 后台进入 开发工具->插件管理->找到创建新插件,根据引导进行创建即可。
> 创建成功后会在 根目录的 addons 目录下生成插件文件
2、创建插件完毕重启服务端后插件管理中会出现你新创建的插件信息。操作栏有几个按钮在此进行说明
- 安装:会自动执行 server/xxx插件/main.go 文件中的Install方法方法中的具体逻辑默认为空可以根据实际情况自行配置。如生成后台菜单、生成插件配置表初始化数据、迁移home页面、web项目文件等。
```
// Install 安装模块
func (m *module) Install(ctx context.Context) (err error) {
// ...
return
}
```
- 更新:会自动执行 server/xxx插件/main.go 文件中的Upgrade方法方法中的具体逻辑默认为空可以根据实际情况自行配置。
```
// Upgrade 更新模块
func (m *module) Upgrade(ctx context.Context) (err error) {
// ...
return
}
```
- 卸载:会自动执行 server/xxx插件/main.go 文件中的UnInstall方法方法中的具体逻辑默认为空可以根据实际情况自行配置。如会清除所有的数据表和已安装的信息等。
```
// UnInstall 卸载模块
func (m *module) UnInstall(ctx context.Context) (err error) {
// ...
return
}
```
### 开发
完全可以根据HotGo正常的开发流程去开发对应的API、控制器、业务逻辑、插件内的应用
### 调用主模块服务接口
这里推荐的方式是在插件input层新建一个结构继承主模块中的input结构。这样做的目的是为了服务与服务之间的输入/输出关系解耦,便于参数扩展和避免插件模块下使用`gf gen service`时出现`import cycle not allowed`
一个简单的例子:
> 假设hgexample插件模块要通过主模块的服务接口更新插件配置
文件:\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`
#### 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方法中并遵循系统规范进行数据安装

View File

@@ -0,0 +1,109 @@
## 模块辅助说明
目录
- 模块结构
- 获取模块信息
- 插件路由规则
#### 模块结构
```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"` // 根路径
}
func (s *Skeleton) GetModule() Module {
return GetModule(s.Name)
}
// Module 插件模块
type Module interface {
Init(ctx context.Context) // 初始化
InitRouter(ctx context.Context, group *ghttp.RouterGroup) // 初始化并注册路由
Ctx() context.Context // 上下文
GetSkeleton() *Skeleton // 架子
Install(ctx context.Context) error // 安装模块
Upgrade(ctx context.Context) error // 更新模块
UnInstall(ctx context.Context) 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前端页面中有之前的路由风格也需同步修改。
```go
package main
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"hotgo/internal/consts"
)
// RouterPrefix 路由前缀
// 最终效果:/应用名称/插件模块名称/xxx/xxx
func RouterPrefix(ctx context.Context, app, name string) string {
var prefix = "/"
switch app {
case consts.AppAdmin:
prefix = g.Cfg().MustGet(ctx, "router.admin.prefix", "/admin").String()
case consts.AppApi:
prefix = g.Cfg().MustGet(ctx, "router.api.prefix", "/api").String()
case consts.AppHome:
prefix = g.Cfg().MustGet(ctx, "router.home.prefix", "/home").String()
case consts.AppWebSocket:
prefix = g.Cfg().MustGet(ctx, "router.ws.prefix", "/socket").String()
}
return prefix + "/" + name
}
```

View File

@@ -0,0 +1,36 @@
## 模块介绍及目录
目录
- 模块介绍
- 启动流程
- 目录结构
### 模块介绍
> 定位:开发独立、临时性、工具类型的功能时推荐使用插件化开发,例如:小游戏(大转盘/消消乐/抽奖/大屏互动/红包等)、小插件(广告管理/文章管理/友情链接等等)、小模块(报名/投票/签到)、小程序、大型插件微商城等等。
> 插件模块方便多项目复用,同时完美支持多人协同开发,每个插件模块都有独立的微架构目录结构,多插件之间完全隔离。
### 启动流程
HotGo 入口文件->隐式注入(hotgo/addons/modules)->注册所有插件->初始化已安装的插件->写入路由组->根据 HotGo 正常的开发和访问流程去开发访问插件
### 目录结构
- 详细介绍请参考:[目录结构](sys-catalog.md)
```
/server
├── addons
│ ├── modules
│ ├── xxx插件
│ | ├── api
│ | ├── controller
│ | ├── global
│ | ├── logic
│ | ├── model
│ | ├── router
│ | ├── service
│ | ├── main.go
│ | └── README.md
```

View File

@@ -0,0 +1,104 @@
## RageFrame 2.x 插件升级到 3.x
目录
- 路由替换
- 配置移除
- UI 替换
- Icons 替换
- 表单组件 替换
- 安装
> 注意:默认当已安装 PhpStorm 等可以批量替换的编译器/软件
### 路由替换
1. 查找 `插件/common/config` 目录
2. 替换 `route``name`
### 配置移除
1. 查找 `插件/AddonConfig.php` 文件
2. 找到 `appsConfig` 变量的 `merapi` 删除掉,移除掉以后的配置
```
public $appsConfig = [
'backend' => 'common/config/backend.php',
'frontend' => 'common/config/frontend.php',
'merchant' => 'common/config/merchant.php',
'html5' => 'common/config/html5.php',
'api' => 'common/config/api.php',
'oauth2' => 'common/config/oauth2.php',
];
```
### UI 替换
1. 查找 `插件` 目录
2. 替换 `<div class='col-sm-1 text-right'>{label}</div><div class='col-sm-11'>{input}{hint}{error}</div>``<div class='row'><div class='col-sm-1 text-right'>{label}</div><div class='col-sm-11'>{input}\n{hint}\n{error}</div></div>`
3. 替换 `<div class='col-sm-2 text-right'>{label}</div><div class='col-sm-10'>{input}{hint}{error}</div>``<div class='row'><div class='col-sm-2 text-right'>{label}</div><div class='col-sm-10'>{input}\n{hint}\n{error}</div></div>`
4. 替换 `<div class='col-sm-2 text-right'>{label}</div><div class='col-sm-10'>{input}\n{hint}\n{error}</div>``<div class='row'><div class='col-sm-2 text-right'>{label}</div><div class='col-sm-10'>{input}\n{hint}\n{error}</div></div>`
5. 替换 `<div class='col-sm-3 text-right'>{label}</div><div class='col-sm-9'>{input}{hint}{error}</div>``<div class='row'><div class='col-sm-3 text-right'>{label}</div><div class='col-sm-9'>{input}\n{hint}\n{error}</div></div>`
6. 替换 `modal`
> 批量替换估计难查找到,可以看见了手动替换掉,关键词搜索 `基本信息`
```
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">关闭</span></button>
<h4 class="modal-title">基本信息</h4>
</div>
```
```
<div class="modal-header">
<h4 class="modal-title">基本信息</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
</div>
```
7. 替换布局
如果单独使用了 `col-*` 这种布局出现页面失调的,需要在 `col-*` 外层加上 `<div class='row'></div>`
例如:
```
<div class="row">
<div class="col-6">
</div>
<div class="col-6">
</div>
</div>
```
### Icons 替换
Icons 找不到请使用最新的 Icons 库 https://fontawesome.com/v5/search?s=solid
### 表单组件替换
个别表单组件报错请参考最新的使用文档 [表单控件](sys-widget.md)
### 安装
1. 放入 3.x 的 `addons` 目录,在后台->应用管理查找安装即可
2. 个别页面出现大小表格缩短显示问题可以修改替换
- `col-xs-12``col-12`
- `col-lg-12``col-12`
- `col-sm-12``col-12`
> 个别报错找不到的组件需要自己替换修复

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/)
- [naiveui](https://www.naiveui.com)
- 常用组件
- [websocket](https://github.com/gorilla/websocket)
- [casbin](https://github.com/casbin/casbin)
- 系统通用
- [redis](https://redis.io/)
- 其他
- [awesome-go](https://github.com/avelino/awesome-go)

View File

@@ -0,0 +1,31 @@
## 环境搭建
目录
- 前端环境
- 后端环境
- 使用说明
### 前端环境
1. 前往https://nodejs.org/zh-cn/下载当前版本node
2. 命令行运行 `node -v` 若控制台输出版本号则node安装成功
3. node 版本需大于 `16.0`
4. 安装yarn`npm install -g yarn`
5. 命令行运行 `yarn -v` 若控制台输出版本号则前端环境搭建成功
### 后端环境
1. 下载golang安装 版本号需>=1.18
2. 国际: https://golang.org/dl/
3. 国内: https://golang.google.cn/dl/
4. 命令行运行 go 若控制台输出各类提示命令 则安装成功 输入 `go version` 确认版本大于1.18
5. 开发工具推荐 [Goland](https://www.jetbrains.com/go/)
### 使用说明
> 需要本地具有 git node golang 环境
- node版本 >= 16.0.0
- golang版本 >= v1.18
- mysql 引擎需要是 innoDB
- IDE推荐Goland

View File

@@ -0,0 +1,92 @@
## 系统安装
目录
- 环境要求
- 安装
### 环境要求
- node版本 >= v16.0.0
- golang版本 >= v1.18
- goframe版本 >=v2.3.2
- mysql版本 >=5.7
> 必须先看[环境搭建文档](start-environment.md),如果安装遇到问题务必先查看[常见问题文档](start-issue.md)
### 安装
一、克隆项目
```
git clone https://github.com/bufanyun/hotgo.git && cd hotgo
```
二、配置你的站点信息
1、服务端
- 项目数据库文件 `storage/data/db.sql` 创建数据库并导入
- 修改配置 `manifest/config/config.yaml.bak` 复制改为`manifest/config/config.yaml`
-`manifest/config/config.yaml`中的数据库配置改为你自己的:
```yaml
database:
logger:
level: "all"
stdout: true
default:
link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true"
debug: true
Prefix: "hg_"
```
2、web前端
- 配置服务端地址,包含在以下文件中:
* /hotgo/web/.env.development
* /hotgo/web/.env.production
* /hotgo/web/.env
其中必改配置
```
VITE_PROXY=[["/admin","http://你的IP:8000/admin"]]
```
三、 启动服务
1、服务端
```shell script
cd server
# 设置国内代理,如果已经设置好了代理可以跳过
go env -w GOPROXY=https://goproxy.io,direct
# 更新包
go mod tidy
# 查看命令行方法
go run main.go hlep
# 启动所有服务
go run main.go # 热编译启动: gf run main.go
```
2、web前端
```shell script
cd web
# 首先确定你以安装node16.0以上版本并安装了包[npm、yarn],否则可能会出现一些未知报错
# 安装依赖
yarn install
# 启动web项目
yarn dev
# 如果顺利至此到浏览器打开http://你的IP:8001/admin
# 登录账号admin, 密码123456
```

View File

@@ -0,0 +1,34 @@
## 常见问题
目录
- 一、后台相关
- 二、数据库相关
- 三、环境相关
### 一、后台相关
#### 1、连接超时请刷新重试。如仍未解决请检查websocket连接是否正确
线上或非本地运行时,请到 系统设置 -> 配置管理 -> 基本设置 -> 找到网站域名和websocket地址改成你自己实际的地址保存刷新页面即可
### 二、数据库相关
#### 1、安装数据库出现 json 报错不支持
请安装 mysql5.7 及以上版本的数据库
### 三、环境相关
#### 1、not found in resource manager or following system searching paths
> 报错信息panic: possible config files "config" or "config.toml/yaml/yml/json/ini/xml/properties" not found in resource manager or following system searching paths:
这是因为系统没有找到配置文件,修改配置 `manifest/config/config.yaml.bak` 复制改为`manifest/config/config.yaml`
详细请参考 - [系统安装](start-installation.md)

View File

@@ -0,0 +1,21 @@
## 如何提问
#### 说明环境情况
- 平台: windows/linux
- 软件与版本golang 1.18, Mysql 5.7 ...
- 系统版本gotho 2.1.4
#### 你做了什么?
- 我按文档附链接上写了一个控制器xxx代码如下xxxx
- 然后我使用xxx访问
- 这其中我还做了啥
- ...
#### 出现什么错误?
- 当我试图xxx的时候xxx报了如下错误截图
- 我确认过我的目录权限是正确的xxx是正确的必要时带上截图
以上是举例提问时所应该描述的情况,当别人看到详细的情况时,也便于快速定位问题所在。

View File

@@ -0,0 +1,130 @@
## 伪静态
目录
- Nginx
- Apache
- IIS
### Nginx
推荐配置
```
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location /backend {
try_files $uri $uri/ /backend/index.php$is_args$args;
}
location /api {
try_files $uri $uri/ /api/index.php$is_args$args;
}
location /merchant {
try_files $uri $uri/ /merchant/index.php$is_args$args;
}
location /html5 {
try_files $uri $uri/ /html5/index.php$is_args$args;
}
location /oauth2 {
try_files $uri $uri/ /oauth2/index.php$is_args$args;
}
location ~* ^/attachment/.*\.(php|php5)$
{
deny all;
}
```
类似Apache的配置
```
location /
{
index index.html index.htm index.php;
if (!-e $request_filename) {
rewrite ^/backend(.*)$ /backend/index.php?s=$1 last;
rewrite ^/merchant(.*)$ /merchant/index.php?s=$1 last;
rewrite ^/api(.*)$ /api/index.php?s=$1 last;
rewrite ^/html5(.*)$ /html5/index.php?s=$1 last;
rewrite ^/oauth2(.*)$ /oauth2/index.php?s=$1 last;
rewrite ^/(.*)$ /index.php?s=$1 last;
break;
}
#autoindex on;
}
```
### Apache
> 注意系统默认自带了.htaccess所以环境如果是apache可以不用再配置
```
Options +FollowSymLinks
IndexIgnore */*
RewriteEngine on
# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# otherwise forward it to index.php
RewriteRule . index.php
```
### IIS
> rule 部分配置
```
<rule name="backend" stopProcessing="true">
<match url="^backend/(.*)" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
</conditions>
<action type="Rewrite" url="backend/index.php/{R:1}" />
</rule>
<rule name="merchant" stopProcessing="true">
<match url="^merchant/(.*)" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
</conditions>
<action type="Rewrite" url="merchant/index.php/{R:1}" />
</rule>
<rule name="html5" stopProcessing="true">
<match url="^html5/(.*)" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
</conditions>
<action type="Rewrite" url="html5/index.php/{R:1}" />
</rule>
<rule name="api" stopProcessing="true">
<match url="^api/(.*)" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
</conditions>
<action type="Rewrite" url="api/index.php/{R:1}" />
</rule>
<rule name="oauth2" stopProcessing="true">
<match url="^oauth2/(.*)" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
</conditions>
<action type="Rewrite" url="oauth2/index.php/{R:1}" />
</rule>
<rule name="frontend" stopProcessing="true">
<match url="^(.*)$" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
</conditions>
<action type="Rewrite" url="index.php/{R:1}" />
</rule>
```

View File

@@ -0,0 +1,76 @@
## 更新历史
升级说明:
> 创建一个Git版本库并创建二个分支比如A、BA分支默认为你的二开分支、B分支为你的版本库分支。
> 等需要升级的时候,把版本库分支(B)升级为最新的版本然后切换到A分支把B分支合并过来解决掉合并冲突即可。
注意:
> 各个小版本升级例如 2.1.x 升级到 2.2.x 以上不能完美升级最好重新安装
> 如果升级(覆盖)代码后打开会出现 sql 报错, 请检查更新的数据库格式或自行调整
### v2.2.10
updated 2023.02.23
- 增加: 增加插件管理、设计新插件等插件模块
- 增加: 增加插件应用:`功能案例`
- 增加: 增加使用文档
- 优化: 生成代码适配插件模块
- 优化:消息队列增加磁盘队列,默认队列从`redis`改为`disk`
- 优化:缓存驱动增加文件适配器,默认驱动从`redis`改为`file`,项目安装不再依赖`redis`
- 优化:优化系统监控加载流程
- 优化gf版本升级到v2.3.2
- 修复:生成代码刷新后偶尔出现加载失败问题
### v2.2.1
updated 2023.01.12
- 优化: 缓存清理各个应用的缓存文件夹读写判断
- 修复: Linux 环境下创建插件报找不到模板文件
- 修复: Excel 导入找不到最后一列数据
- 增加: 无权限菜单不显示
- 优化: 省市区数据为最新的 2023.01.11 的国家统计局省市区数据
- 优化: 前台关于图片上传的 js 和 css 引入,避免资源依赖找不到
- 优化: 插件模块查询机制, 增加数据缓存依赖,依赖时间为 360 秒
- 优化: 文件上传的处理
- 修复: 网站配置的多图上传引入路径错误
- 修复: 定时任务协程问题导致执行失败
-
### v2.1.1
updated 2022.9.26
- 优化: 多图上传的显示样式及功能
- 修复: 由于上传视频和语音开启了全域名返回导致的上传到服务器错误
- 增加: 由增加home页面入口前台页面
### v2.0.8
updated 2022.9.21
- 优化: 优化菜单和角色数据权限,支持数据权限和按钮细分权限
- 优化: 优化附件上传,增加文件上传选项
### v2.0.6
updated 2022.9.18
- 增加: 增加阿里云短信配置和发送短信API
- 增加: 增加高德地图配置
- 优化: 优化数据字典,增加数据类型和标签支持
-
### v2.0.3
updated 2022.9.10
- 2.0 全新上线
### v2.0.2
updated 2022.6.11
- 增加: 增加生成代码功能目前已支持一键生成CURD和关联表
- 增加: 增加云存储UCloud配置后台一键切换驱动
### v2.0.0
updated 2022.5.20
- 初始化: 2.0 基础框架

View File

@@ -0,0 +1,215 @@
## 权限控制
- 控制说明
- 如何页面块/按钮自由控制显示权限
- 不进行权限认证的路由
- 新增应用的 RBAC 授权
> RageFrame已经内置的 RBAC 权限管理,并对其进行了二次开发,无限父子级权限分组、可自由分配子级权限
### 控制说明
可以把权限理解为一个大的库,往里面丢各种名称,可以是路由、自定义名称等
系统自定义了几个默认的名称
- menuCate:1 => 平台管理导航菜单
- menuCate:2 => 微信公众号导航菜单
- menuCate:3 => 系统管理导航菜单
- menuCate:4 => 应用中心导航菜单
> 为什么要命名为cate:1这种名称呢因为是后面带的是菜单分类数据库自带的id如果自己有改动比如删除后添加注意修改权限名称
> 注意!!!
> 注意!!!
> 注意!!!
>
> 权限的路由一定要包含菜单路由才会显示
**举个例子**
已授权权限
```
[
'/test/index',
'/test/system',
...
],
```
你的菜单
```
[
'testList', // 这里是菜单的顶级别名
'child' => [
'/test/index', // 子菜单
'/test/system', // 子菜单
'/test/test', // 子菜单
...
]
...
],
```
这里看到你授权菜单的子菜单但是没有授权 testList 别名,那么你整个菜单都会被隐藏,如果想显示你必须把 testList 也加入权限
### 如何页面块/按钮自由控制显示权限
按钮快捷方式:使用 [Html](helper-html.md) 辅助类
自定义验证如下:使用 [Auth](helper-auth.md) 辅助类
### 不进行权限认证的路由
> 修改地址 `backend/config/params` 找到 `noAuthRoute`
```
/**
* 不需要验证的路由全称
*
* 注意: 前面以绝对路径/为开头
*/
'noAuthRoute' => [
'/main/index',// 系统主页
'/main/system',// 系统首页
'/menu-provinces/index',// 微信个性化菜单省市区
'/wechat/common/select-news',// 微信自动回复获取图文
'/wechat/common/select-attachment',// 微信自动回复获取图片/视频/
'/wechat/analysis/image',// 微信显示素材图片
],
```
### 新增应用的 RBAC 授权
获取分配角色列表
```
/**
* @param string $app_id 应用id
* @param bool $sourceAuthChild 权限来源(false:所有权限true当前角色)
* @return array
*/
Yii::$app->services->rbacAuthRole->getDropDown($app_id, $sourceAuthChild)
```
分配为用户角色
```
/**
* @param array $role_ids 角色id
* @param int $user_id 用户id
* @param string $app_id 应用id
* @throws UnprocessableEntityHttpException
*/
Yii::$app->services->rbacAuthAssignment->assign($role_ids, $user_id, $app_id);
```
权限管理
> 下面为新增应用权限(比如增加前台会员的权限)案例
```
<?php
namespace addons\Merchants\backend\controllers;
use common\enums\AppEnum;
use common\models\rbac\AuthItem;
use common\traits\AuthItemTrait;
/**
* Class AuthItemController
* @package backend\modules\common\controllers
* @author jianyan74 <751393839@qq.com>
*/
class AuthItemController extends BaseController
{
use AuthItemTrait;
/**
* @var AuthItem
*/
public $modelClass = AuthItem::class;
/**
* 默认应用
*
* @var string
*/
public $appId = AppEnum::MERCHANT;
/**
* 渲染视图前缀(默认)
*
* @var string
*/
public $viewPrefix = '@backend/modules/base/views/auth-item/';
}
```
角色管理
> 下面为新增应用权限(比如增加前台会员的角色)案例
```
<?php
namespace addons\Merchants\backend\controllers;
use Yii;
use common\traits\AuthRoleTrait;
use common\models\rbac\AuthRole;
use common\enums\AppEnum;
/**
* Class RoleController
* @package addons\Merchants\backend\controllers
* @author jianyan74 <751393839@qq.com>
*/
class AuthRoleController extends BaseController
{
use AuthRoleTrait;
/**
* @var AuthRole
*/
public $modelClass = AuthRole::class;
/**
* 默认应用
*
* @var string
*/
public $appId = AppEnum::MERCHANT;
/**
* 权限来源
*
* false:所有权限true当前角色
*
* @var bool
*/
public $sourceAuthChild = false;
/**
* 渲染视图前缀(默认)
*
* @var string
*/
public $viewPrefix = '@backend/modules/base/views/auth-role/';
/**
* @throws \yii\base\InvalidConfigException
*/
public function init()
{
parent::init();
$this->merchant_id = Yii::$app->request->get('merchant_id');
$this->merchant_id && Yii::$app->services->merchant->setId($this->merchant_id);
}
}
```

View File

@@ -0,0 +1,169 @@
## 目录结构
目录
- 服务端
- web前端
- uinapp端待开放
#### 服务端
```
/server
├── addons
│ ├── modules
│ ├── xxx插件
│ | ├── api
│ | ├── controller
│ | ├── global
│ | ├── logic
│ | ├── model
│ | ├── router
│ | ├── service
│ | ├── main.go
│ | └── README.md
├── api
│ ├── admin
│ ├── api
│ ├── home
│ ├── websocket
├── hack
├── internal
│ ├── cmd
│ ├── consts
│ ├── controller
│ ├── crons
│ ├── dao
│ ├── global
│ ├── library
│ ├── logic
│ ├── model
│ | ├── do
│ │ ├── entity
│ │ └── input
│ ├── packed
│ ├── queues
│ ├── router
│ ├── service
│ └── websocket
├── manifest
├── resource
├── storage
├── utility
├── go.mod
├── main.go
├── Makefile
└── README.md
```
| 目录 | 描述 |
|--------------------------|-----------------------------------------------------------------|
| 基于gf的工程目录结构做了部分调整 | 参考地址: https://goframe.org/pages/viewpage.action?pageId=30740166 |
| **addons** | 所有的插件模块都放在这里 |
| --- modules | 为插件模块提供隐式初始化 |
| --- xxx插件 | 插件模块名称 |
| --- --- api | 对外接口。提供服务的输入/输出数据结构定义 |
| --- --- --- admin | 后台接口 |
| --- --- --- api | 前台通用接口包含PC页面、uinapp接口等 |
| --- --- --- home | 前台PC端页面 |
| --- --- --- websocket | 可同时为多应用提供websocket接口 |
| --- --- controller | 接收/解析用户输入参数的入口/接口层,也可以理解为控制器 |
| --- --- global | 项目内主要的全局变量和系统的一些初始化操作 |
| --- --- logic | 业务逻辑封装管理,特定的业务逻辑实现和封装往往是项目中最复杂的部分 |
| --- --- model | 数据结构管理模块,管理数据实体对象,以及输入与输出数据结构定义 |
| --- --- --- input | 对内接口。用于controller调用service或service之间调用时的输入/输出结构定义和输入过滤和预处理 |
| --- --- router | 注册对外接口和分组中间件 |
| --- --- service | 用于业务模块解耦的接口定义层具体的接口实现在logic中进行注入 |
| --- main.go | 插件始化文件和模块插拔接口 |
| **api** | 对外接口。提供服务的输入/输出数据结构定义 |
| --- admin | 后台接口 |
| --- api | 前台通用接口包含PC页面、uinapp接口等 |
| --- home | 前台PC端页面 |
| --- websocket | 可同时为多应用提供websocket接口 |
| **hack** | 存放项目开发工具、脚本等内容例如CLI工具的配置各种shell/bat脚本等文件 |
| **internal** | 业务逻辑存放目录通过Golang internal特性对外部隐藏可见性 |
| --- cmd | 命令行管理目录可以管理维护多个命令行 |
| --- consts | 项目内主要的常量定义 |
| --- controller | 接收/解析用户输入参数的入口/接口层,也可以理解为控制器 |
| --- crons | 项目中由系统统一接管的定时任务处理 |
| --- dao | 数据访问对象,这是一层抽象对象,用于和底层数据库交互,仅包含最基础的 CURD 方法 |
| --- global | 项目内主要的全局变量和系统的一些初始化操作 |
| --- library | 项目内常用功能的扩展库 |
| --- logic | 业务逻辑封装管理,特定的业务逻辑实现和封装往往是项目中最复杂的部分 |
| --- model | 数据结构管理模块,管理数据实体对象,以及输入与输出数据结构定义 |
| --- --- do | 用于dao数据操作中业务模型与实例模型转换由工具维护用户不能修改 |
| --- --- entity | 与数据集合绑定的程序数据结构定义,通常和数据表一一对应 |
| --- --- input | 对内接口。用于controller调用service或service之间调用时的输入/输出结构定义和输入过滤和预处理 |
| --- packed | 将静态资源打包进可执行文件,无需单独部署 |
| --- queues | 为项目内所有的消息队列的消费者提供统一的初始化和处理 |
| --- router | 注册对外接口和分组中间件 |
| --- service | 用于业务模块解耦的接口定义层具体的接口实现在logic中进行注入 |
| **manifest** | 包含程序编译、部署、运行、配置的文件常见内容如下: |
| --- config | 配置文件存放目录 |
| --- docker | Docker镜像相关依赖文件脚本文件等等 |
| --- deploy | 部署相关的文件默认提供了Kubernetes集群化部署的Yaml模板通过kustomize管理 |
| **resource** | 静态资源文件。这些文件往往可以通过 资源打包/镜像编译 的形式注入到发布文件中 |
| **storage** | 本地数据存储目录例如文件缓存、磁盘队列数据、sql数据文件、SSL证书等 |
| **utility** | 一些常用的工具方法 |
| go.mod | 使用Go Module包管理的依赖描述文件 |
| main.go | 程序入口文件 |
| Makefile | 程序构建发布和开发快捷指令 |
| README.md | 项目介绍文件 |
#### web前端
```
/web
├── build # 打包脚本相关
│ ├── config # 配置文件
│ ├── generate # 生成器
│ ├── script # 脚本
│ └── vite # vite配置
├── mock # mock文件夹
├── public # 公共静态资源目录
├── src # 主目录
│ ├── api # 接口文件
│ ├── assets # 资源文件
│ │ ├── icons # icon sprite 图标文件夹
│ │ ├── images # 项目存放图片的文件夹
│ │ └── svg # 项目存放svg图片的文件夹
│ ├── components # 公共组件
│ ├── design # 样式文件
│ ├── directives # 指令
│ ├── enums # 枚举/常量
│ ├── hooks # hook
│ │ ├── component # 组件相关hook
│ │ ├── core # 基础hook
│ │ ├── event # 事件相关hook
│ │ ├── setting # 配置相关hook
│ │ └── web # web相关hook
│ ├── layouts # 布局文件
│ │ ├── default # 默认布局
│ │ ├── iframe # iframe布局
│ │ └── page # 页面布局
│ ├── locales # 多语言
│ ├── logics # 逻辑
│ ├── main.ts # 主入口
│ ├── router # 路由配置
│ ├── settings # 项目配置
│ │ ├── componentSetting.ts # 组件配置
│ │ ├── designSetting.ts # 样式配置
│ │ ├── encryptionSetting.ts # 加密配置
│ │ ├── localeSetting.ts # 多语言配置
│ │ ├── projectSetting.ts # 项目配置
│ │ └── siteSetting.ts # 站点配置
│ ├── store # 数据仓库
│ ├── utils # 工具类
│ └── views # 页面
├── types # 类型文件
├── vite.config.ts # vite配置文件
└── windi.config.ts # windcss配置文件
```
#### uinapp端
```
// 待开放
```

View File

@@ -0,0 +1,70 @@
## 控制台
目录
- 启动所有服务
- HTTP服务
- 消息队列
- 定时任务
- 常用工具
- Makefile
### 启动所有服务
- 仅推荐在开发期间快速调试使用,线上实际部署时建议将各个服务分开部署,这样重新部署某个服务时无需全部重启。
```shell
# 默认
go run main.go
# 通过热编译启动
gf run main.go
```
### HTTP服务
- 启动HTTP服务包含websocket。
```shell
# 默认
go run main.go http
# 通过热编译启动
gf run main.go --args "http"
```
### 消息队列
- 启动消息队列的消费者。
```shell
# 默认
go run main.go queue
# 通过热编译启动
gf run main.go --args "queue"
```
### 定时任务暂未拆分目前随HTTP服务启动
- 启动系统中统一注册的定时任务。
```shell
# 默认
go run main.go cron
# 通过热编译启动
gf run main.go --args "cron"
```
### 常用工具
- 释放casbin权限用于清理无效的权限设置。
```shell
go run main.go tools -m=casbin -a1=refresh
```
### Makefile
- 通过make提供一些快捷命令
```shell
# 一键编译,打包前后端代码到可执行文件
make build
# 更多请查看 /server/Makefile文件
```

View File

@@ -0,0 +1,69 @@
## 消息队列
目录
- 缓存驱动
- 上下文(待写)
- JWT待写
- 地理定位(待写)
- 通知(待写)
### 缓存驱动
> 系统默认的缓存驱动为file目前已支持memory|redis|file等多种驱动。请自行选择适合你的驱动使用。
- 配置文件server/manifest/config/config.yaml
```yaml
#缓存
cache:
adapter: "file" # 缓存驱动方式支持memory|redis|file不填默认memory
fileDir: "./storage/cache" # 文件缓存路径adapter=file时必填
```
#### 使用方式
```go
package main
import (
"hotgo/internal/library/cache"
"github.com/gogf/gf/v2/os/gctx"
)
func test() {
ctx := gctx.New()
// 添加/修改
cache.Instance().Set(ctx, "qwe", 123, 0)
// 查询
cache.Instance().Get(ctx, "qwe")
// 删除
cache.Instance().Remove(ctx, "qwe")
// 更多方法请参考https://goframe.org/pages/viewpage.action?pageId=27755640
}
```
### 上下文
```go
// 待写
```
### JWT
```go
// 待写
```
### 地理定位
```go
// 待写
```
### 通知
```go
// 待写
```

View File

@@ -0,0 +1,155 @@
## 消息队列
目录
- 配置文件
- 一个例子
- 控制台
- 自定义队列驱动
> 系统默认的队列驱动为disk(磁盘队列)目前已支持disk、redis、rocketmq、kafka等多种驱动。请自行选择适合你的驱动使用。
### 配置文件
- 配置文件server/manifest/config/config.yaml
```yaml
#消息队列
queue:
switch: true # 队列开关可选true|false默认为true
driver: "disk" # 队列驱动可选redis|rocketmq|kafka默认为disk
retry: 2 # 重试次数仅rocketmq|redis支持
groupName: "hotgo" # mq群组名称
#磁盘队列
disk:
path: "./storage/diskqueue" # 数据存放路径
batchSize: 100 # 每100条消息同步一次batchSize和batchTime满足其一就会同步一次
batchTime: 1 # 每1秒消息同步一次
segmentSize: 10485760 # 每个topic分片数据文件最大字节默认10M
segmentLimit: 3000 # 每个topic最大分片数据文件数量超出部分将会丢弃
redis:
address: "127.0.0.1:6379" # redis服务地址默认为127.0.0.1:6379
db: 2 # 指定redis库
pass: "" # redis密码
timeout: 0 # 队列超时时间(s) 0为永不超时当队列一直没有被消费到达超时时间则队列会被销毁
rocketmq:
address: "127.0.0.1:9876" # brocker地址+端口
logLevel: "all" # 系统日志级别可选all|close|debug|info|warn|error|fatal
kafka:
address: "127.0.0.1:9092" # kafka地址+端口
version: "2.0.0.0" # kafka专属配置默认2.0.0.0
randClient: true # 开启随机生成clientID可以实现启动多实例同时一起消费相同topic加速消费能力的特性默认为true
multiConsumer: true # 是否支持创建多个消费者
```
### 一个例子
每个被发送到队列的消息应该被定义为一个文件的结构。
例如,如果您需要异步记录系统日志,内容大致如下:
- 文件路径server/internal/queues/sys_log.go
```go
package queues
import (
"context"
"encoding/json"
"hotgo/internal/consts"
"hotgo/internal/library/queue"
"hotgo/internal/model/entity"
"hotgo/internal/service"
)
func init() {
jobList = append(jobList, SysLog)
}
// SysLog 系统日志
var SysLog = &qSysLog{}
type qSysLog struct{}
// getTopic 主题
func (q *qSysLog) getTopic() string {
return consts.QueueLogTopic
}
// 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
}
return service.SysLog().RealWrite(ctx, data)
}
```
下面是将消息添加到队列的方式,大概内容如下:
```go
package main
import (
"fmt"
"hotgo/internal/consts"
"hotgo/internal/library/queue"
"hotgo/internal/model/entity"
)
func test() {
data := &entity.SysLog{}
if err := queue.Push(consts.QueueLogTopic, data); err != nil {
fmt.Printf("queue.Push err:%+v", err)
}
}
```
### 控制台
控制台用于处理队列消息,即消费者。
相关命令请参考: [控制台](sys-console.md)
### 自定义队列驱动
只需实现消息队列的生成者和消费者接口,并加入到初始化中进行相应调用即可。
- 接口片段server/internal/library/queue/init.go
```go
package queue
import (
"time"
)
type MqMsg struct {
RunType int `json:"run_type"`
Topic string `json:"topic"`
MsgId string `json:"msg_id"`
Offset int64 `json:"offset"`
Partition int32 `json:"partition"`
Timestamp time.Time `json:"timestamp"`
Body []byte `json:"body"`
}
type MqProducer interface {
SendMsg(topic string, body string) (mqMsg MqMsg, err error)
SendByteMsg(topic string, body []byte) (mqMsg MqMsg, err error)
}
type MqConsumer interface {
ListenReceiveMsgDo(topic string, receiveDo func(mqMsg MqMsg)) (err error)
}
```
将实现过接口MqProducer、MqConsumer的实例方法分别加入到NewProducer、NewConsumer中进行相应调用即可。