docs:初始化go-view-server
This commit is contained in:
commit
92f5e57614
|
@ -0,0 +1 @@
|
|||
* linguist-language=GO
|
|
@ -0,0 +1,22 @@
|
|||
.buildpath
|
||||
.hgignore.swp
|
||||
.project
|
||||
.orig
|
||||
.swp
|
||||
.idea/
|
||||
.settings/
|
||||
.vscode/
|
||||
bin/
|
||||
**/.DS_Store
|
||||
gf
|
||||
main
|
||||
main.exe
|
||||
main.exe~
|
||||
output/
|
||||
manifest/output/
|
||||
temp/
|
||||
temp.yaml
|
||||
bin
|
||||
resource/public/attachment
|
||||
storage/cache
|
||||
logs/
|
|
@ -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.
|
|
@ -0,0 +1,16 @@
|
|||
ROOT_DIR = $(shell pwd)
|
||||
NAMESPACE = "default"
|
||||
DEPLOY_NAME = "template-single"
|
||||
DOCKER_NAME = "template-single"
|
||||
|
||||
include ./hack/hack.mk
|
||||
|
||||
|
||||
# 通过热编译启动所有服务
|
||||
.PHONY: all
|
||||
all:
|
||||
gf run main.go --args "all"
|
||||
|
||||
.PHONY: http
|
||||
http:
|
||||
gf run main.go
|
|
@ -0,0 +1,215 @@
|
|||
## 项目介绍
|
||||
|
||||
- 本项目是由 `GoLang` 实现的 `GoView` 后端接口,基于 `GoFrame` 开发
|
||||
- `GoView` 是一个高效的拖拽式低代码数据可视化开发平台,将图表或页面元素封装为基础组件,无需编写代码即可制作数据大屏,减少心智负担。当然低代码也不是 “银弹”,希望所有人员都能理智看待此技术。
|
||||
- 如果在使用中遇到问题,请提交 [issues](https://gitee.com/bufanyun/go-view-server/issues/new) 或联系作者
|
||||
- 作者QQ:133814250
|
||||
|
||||
|
||||
### 主要技术栈
|
||||
|
||||
| 名称 | 版本 |
|
||||
| ------------------- | ------ |
|
||||
| goframe | 2.6.3 |
|
||||
| golang-jwt | 5.2.0 |
|
||||
| 详见 `go.mod` | 😁 |
|
||||
|
||||
|
||||
### 运行环境
|
||||
1. 下载golang安装 版本号需>=1.19
|
||||
2. 国际: https://golang.org/dl/
|
||||
3. 国内: https://golang.google.cn/dl/
|
||||
4. 命令行运行 go 若控制台输出各类提示命令 则安装成功 输入 `go version` 确认版本大于1.19
|
||||
5. 开发工具推荐 [Goland](https://www.jetbrains.com/go/)
|
||||
|
||||
|
||||
### 使用说明
|
||||
|
||||
> 需要本地具有 git node golang mysql 环境
|
||||
|
||||
- node版本 16.14.x
|
||||
- golang版本 >= v1.19
|
||||
- mysql版本 >=5.7
|
||||
- IDE推荐:Goland
|
||||
|
||||
|
||||
### 安装go-view-server
|
||||
|
||||
一、克隆项目
|
||||
|
||||
```
|
||||
git clone https://gitee.com/bufanyun/go-view-server.git
|
||||
```
|
||||
|
||||
二、初始化数据库
|
||||
|
||||
- 项目数据库文件 [storage/data/goview.sql](storage/data/goview.sql) 创建数据库并导入
|
||||
- 将`manifest/config/config.yaml`中的数据库配置改为你自己的:
|
||||
```yaml
|
||||
database:
|
||||
logger:
|
||||
path: "logs/database" # 日志文件路径。默认为空,表示关闭,仅输出到终端
|
||||
<<: *defaultLogger
|
||||
stdout: true
|
||||
default:
|
||||
link: "mysql:goview:nWkG43Xxnbi2Y44C@tcp(127.0.0.1:3306)/goview?loc=Local&parseTime=true&charset=utf8mb4"
|
||||
debug: true
|
||||
Prefix: "hg_"
|
||||
```
|
||||
|
||||
三、 启动服务
|
||||
|
||||
```shell script
|
||||
# 设置国内代理,如果已经设置好了代理可以跳过
|
||||
go env -w GOPROXY=https://goproxy.io,direct
|
||||
|
||||
# 更新包
|
||||
go mod tidy
|
||||
|
||||
# 启动所有服务
|
||||
go run main.go # 热编译启动: gf run main.go
|
||||
```
|
||||
|
||||
## 安装go-view
|
||||
|
||||
一、克隆项目
|
||||
|
||||
- 注意:`master-fetch` 分支是带有后端接口请求的分支!!
|
||||
```
|
||||
git clone --branch master-fetch https://gitee.com/dromara/go-view.git
|
||||
```
|
||||
|
||||
二、修改和`go-view-server`一致的端口号
|
||||
|
||||
- 接口地址修改:.env
|
||||
```env
|
||||
# port
|
||||
VITE_DEV_PORT = '8080'
|
||||
|
||||
# development path
|
||||
VITE_DEV_PATH = 'http://127.0.0.1:8090'
|
||||
|
||||
# production path
|
||||
VITE_PRO_PATH = 'http://127.0.0.1:8090'
|
||||
```
|
||||
|
||||
三、 启动服务
|
||||
|
||||
- 安装
|
||||
- 推荐使用 pnpm 管理项目,并使用 nrm 切换到阿里镜像,整体安装步骤如下:
|
||||
```shell script
|
||||
# 1. 安装 pnpm
|
||||
npm install -g pnpm
|
||||
|
||||
# 2. 安装 nrm
|
||||
npm install -g nrm
|
||||
|
||||
# 3. 使用 nrm 添加阿里镜像
|
||||
nrm add taobao https://registry.npmmirror.com/
|
||||
|
||||
# 4. nrm 查看镜像列表
|
||||
nrm ls
|
||||
|
||||
# 5. nrm 应用对应镜像
|
||||
nrm use taobao
|
||||
```
|
||||
|
||||
- 安装项目依赖
|
||||
```shell script
|
||||
# 推荐使用 pnpm
|
||||
pnpm install
|
||||
|
||||
# 或 yarn
|
||||
yarn install
|
||||
```
|
||||
|
||||
- 启动
|
||||
```shell script
|
||||
# 推荐使用 pnpm
|
||||
pnpm dev
|
||||
|
||||
# 或 yarn
|
||||
yarn dev
|
||||
|
||||
# 或 Makefile(需要自行配置系统环境,谷歌 make 命令环境搭建)
|
||||
make dev
|
||||
```
|
||||
|
||||
- 编译
|
||||
```shell script
|
||||
# 推荐使用 pnpm
|
||||
pnpm run build
|
||||
|
||||
# 或 yarn
|
||||
yarn run build
|
||||
|
||||
# 或 Makefile
|
||||
make dist
|
||||
```
|
||||
|
||||
四、 登录go-view
|
||||
- 如果一切正常,启动 `go-view` 后可以通过浏览器打开下方地址进入 `go-view` 登录页面
|
||||
```
|
||||
登录地址:http://localhost:3000/
|
||||
账号:admin
|
||||
密码:admin
|
||||
```
|
||||
|
||||
|
||||
## 测试项目
|
||||
- 在满足 `go-view` 后台接口使用的基础上,我们内置了一个测试项目和测试设备数据接口,方便日常配置大屏和报表的数据调试需求。
|
||||
- 测试接口,获取测试数据:
|
||||
```http request
|
||||
GET http://127.0.0.1:8090/api/goview/sys/testData
|
||||
```
|
||||
|
||||
- 响应数据内容
|
||||
- `data.deviceInfo` 为模拟的设备基本信息,数据固定
|
||||
- `data.deviceData` 为模拟的设备动态上报数据,数据和数值类型每次请求都会随机生成
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"deviceInfo": {
|
||||
"GPSLatitude": "-42.12345",
|
||||
"GPSLongitude": "73.56789",
|
||||
"deviceId": "IoTDevice001",
|
||||
"deviceStatus": true,
|
||||
"deviceTime": "2024-03-04 10:38:20",
|
||||
"location": "河南郑州高新区科学大道001号"
|
||||
},
|
||||
"deviceData": {
|
||||
"airQuality": 66,
|
||||
"ambientLight": 22.13,
|
||||
"current": 66.3,
|
||||
"deviceCurrent": 87.85,
|
||||
"deviceTemperature": 21,
|
||||
"deviceVibration": 62.04,
|
||||
"deviceVoltage": 70,
|
||||
"factoryData": "WQLZbKQf",
|
||||
"humidity": "QMEHBqmT",
|
||||
"noiseLevel": 14,
|
||||
"powerConsumption": 5.18,
|
||||
"pressure": "mOiEPEiZ",
|
||||
"temperature": 8,
|
||||
"vibration": "DPemPsNi",
|
||||
"voltage": "YLkSKFXm",
|
||||
"waterLevel": 30,
|
||||
"windSpeed": 62.51
|
||||
}
|
||||
},
|
||||
"msg": "获取设备数据成功"
|
||||
}
|
||||
```
|
||||
|
||||
## 相关开源地址
|
||||
|
||||
- [go-view](https://gitee.com/dromara/go-view)
|
||||
- [goframe](https://gitee.com/johng/gf)
|
||||
- [go-view-server](https://gitee.com/bufanyun/go-view-server)
|
||||
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [go-view](https://www.mtruning.club/)
|
||||
- [goframe](https://goframe.org/)
|
|
@ -0,0 +1,22 @@
|
|||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package project
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"hotgo/api/project/v1"
|
||||
)
|
||||
|
||||
type IProjectV1 interface {
|
||||
List(ctx context.Context, req *v1.ListReq) (res *v1.ListRes, err error)
|
||||
GetData(ctx context.Context, req *v1.GetDataReq) (res *v1.GetDataRes, err error)
|
||||
Create(ctx context.Context, req *v1.CreateReq) (res *v1.CreateRes, err error)
|
||||
Edit(ctx context.Context, req *v1.EditReq) (res *v1.EditRes, err error)
|
||||
SaveData(ctx context.Context, req *v1.SaveDataReq) (res *v1.SaveDataRes, err error)
|
||||
Delete(ctx context.Context, req *v1.DeleteReq) (res *v1.DeleteRes, err error)
|
||||
Publish(ctx context.Context, req *v1.PublishReq) (res *v1.PublishRes, err error)
|
||||
Upload(ctx context.Context, req *v1.UploadReq) (res *v1.UploadRes, err error)
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
// Package v1
|
||||
// @Description
|
||||
// @Author Ms <133814250@qq.com>
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"hotgo/internal/model/input/adminin"
|
||||
"hotgo/internal/model/input/form"
|
||||
)
|
||||
|
||||
// ListReq 查询列表
|
||||
type ListReq struct {
|
||||
g.Meta `path:"/project/list" method:"get" tags:"项目" summary:"获取项目列表"`
|
||||
adminin.ProjectListInp
|
||||
}
|
||||
|
||||
type ListRes struct {
|
||||
g.Meta `mime:"custom"`
|
||||
List []*adminin.ProjectListModel `json:"list" dc:"数据列表"`
|
||||
form.PageRes
|
||||
}
|
||||
|
||||
// GetDataReq 获取信息
|
||||
type GetDataReq struct {
|
||||
g.Meta `path:"/project/getData" method:"get" tags:"项目" summary:"获取指定信息"`
|
||||
adminin.ProjectGetDataInp
|
||||
}
|
||||
|
||||
type GetDataRes struct {
|
||||
*adminin.ProjectGetDataModel
|
||||
}
|
||||
|
||||
// CreateReq 新增项目
|
||||
type CreateReq struct {
|
||||
g.Meta `path:"/project/create" method:"post" tags:"项目" summary:"新增项目"`
|
||||
adminin.ProjectCreateInp
|
||||
}
|
||||
|
||||
type CreateRes struct {
|
||||
*adminin.ProjectCreateModel
|
||||
}
|
||||
|
||||
// EditReq 修改/新增
|
||||
type EditReq struct {
|
||||
g.Meta `path:"/project/edit" method:"post" tags:"项目" summary:"修改/新增项目"`
|
||||
adminin.ProjectEditInp
|
||||
}
|
||||
|
||||
type EditRes struct{}
|
||||
|
||||
// SaveDataReq 保存项目数据
|
||||
type SaveDataReq struct {
|
||||
g.Meta `path:"/project/save/data" method:"post" tags:"项目" summary:"保存项目数据"`
|
||||
adminin.ProjectSaveDataInp
|
||||
}
|
||||
|
||||
type SaveDataRes struct{}
|
||||
|
||||
// DeleteReq 删除
|
||||
type DeleteReq struct {
|
||||
g.Meta `path:"/project/delete" method:"post" tags:"项目" summary:"删除项目"`
|
||||
adminin.ProjectDeleteInp
|
||||
}
|
||||
|
||||
type DeleteRes struct{}
|
||||
|
||||
// PublishReq 修改发布状态
|
||||
type PublishReq struct {
|
||||
g.Meta `path:"/project/publish" method:"put" tags:"项目" summary:"修改发布状态"`
|
||||
adminin.ProjectPublishInp
|
||||
}
|
||||
|
||||
type PublishRes struct{}
|
||||
|
||||
// UploadReq 文件上传
|
||||
type UploadReq struct {
|
||||
g.Meta `path:"/project/upload" method:"post" tags:"项目" summary:"文件上传"`
|
||||
adminin.ProjectUploadInp
|
||||
}
|
||||
|
||||
type UploadRes struct {
|
||||
FileUrl string `json:"fileurl"`
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package site
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"hotgo/api/site/v1"
|
||||
)
|
||||
|
||||
type ISiteV1 interface {
|
||||
Register(ctx context.Context, req *v1.RegisterReq) (res *v1.RegisterRes, err error)
|
||||
AccountLogin(ctx context.Context, req *v1.AccountLoginReq) (res *v1.AccountLoginRes, err error)
|
||||
LoginLogout(ctx context.Context, req *v1.LoginLogoutReq) (res *v1.LoginLogoutRes, err error)
|
||||
GetOssInfo(ctx context.Context, req *v1.GetOssInfoReq) (res *v1.GetOssInfoRes, err error)
|
||||
TestData(ctx context.Context, req *v1.TestDataReq) (res *v1.TestDataRes, err error)
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
// Package v1
|
||||
// @Description
|
||||
// @Author Ms <133814250@qq.com>
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"hotgo/internal/model/input/adminin"
|
||||
)
|
||||
|
||||
// RegisterReq 提交账号注册
|
||||
type RegisterReq struct {
|
||||
g.Meta `path:"/sys/register" method:"post" tags:"后台基础" summary:"账号注册"`
|
||||
adminin.RegisterInp
|
||||
}
|
||||
|
||||
type RegisterRes struct {
|
||||
*adminin.LoginModel
|
||||
}
|
||||
|
||||
// AccountLoginReq 提交账号登录
|
||||
type AccountLoginReq struct {
|
||||
g.Meta `path:"/sys/login" method:"post" tags:"后台基础" summary:"账号登录"`
|
||||
adminin.AccountLoginInp
|
||||
}
|
||||
|
||||
type AccountLoginRes struct {
|
||||
*adminin.LoginModel
|
||||
}
|
||||
|
||||
// LoginLogoutReq 注销登录
|
||||
type LoginLogoutReq struct {
|
||||
g.Meta `path:"/sys/logout" method:"get" tags:"后台基础" summary:"注销登录"`
|
||||
}
|
||||
|
||||
type LoginLogoutRes struct{}
|
||||
|
||||
// GetOssInfoReq 获取文件上传oss信息
|
||||
type GetOssInfoReq struct {
|
||||
g.Meta `path:"/sys/getOssInfo" method:"get" tags:"后台基础" summary:"获取文件上传oss信息"`
|
||||
}
|
||||
|
||||
type GetOssInfoRes struct {
|
||||
BucketURL string `json:"bucketUrl"`
|
||||
}
|
||||
|
||||
// TestDataReq 模拟测试数据
|
||||
type TestDataReq struct {
|
||||
g.Meta `path:"/sys/testData" method:"all" tags:"后台基础" summary:"模拟测试数据"`
|
||||
}
|
||||
|
||||
type TestDataRes struct {
|
||||
g.Meta `mime:"custom"`
|
||||
DeviceInfo map[string]interface{} `json:"deviceInfo" dc:"设备信息"`
|
||||
DeviceData map[string]interface{} `json:"deviceData" dc:"随机设备数值"`
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
module hotgo
|
||||
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.6.3
|
||||
github.com/gogf/gf/contrib/nosql/redis/v2 v2.7.2
|
||||
github.com/gogf/gf/v2 v2.7.2
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/clbanning/mxj/v2 v2.7.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.1 // indirect
|
||||
github.com/grokify/html-strip-tags-go v0.1.0 // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/redis/go-redis/v9 v9.5.1 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rogpeppe/go-internal v1.8.0 // indirect
|
||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||
golang.org/x/net v0.24.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
|
@ -0,0 +1,98 @@
|
|||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
|
||||
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.6.3 h1:dRGGKKiT9FEnxhfHFerojy34uCKHgReKgpMbAOtqhsY=
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.6.3/go.mod h1:sGdaCPgN1AY0tho+WYAgYdUHJkXwuDf76M3ASgHXWRQ=
|
||||
github.com/gogf/gf/contrib/nosql/redis/v2 v2.7.2 h1:V1hdGnyjU9kT0I3DDDFDl6Ll8yC6aAIFJa/lMQwB8V4=
|
||||
github.com/gogf/gf/contrib/nosql/redis/v2 v2.7.2/go.mod h1:XzkPv3G8TdKczqoB/ydR3bxvBRdQLQNCOCEgxso/c3o=
|
||||
github.com/gogf/gf/v2 v2.7.2 h1:uZDfyblasI12lZUtFd1mfxsIr8b14cd/F88DJUTCSDM=
|
||||
github.com/gogf/gf/v2 v2.7.2/go.mod h1:EBXneAg/wes86rfeh68XC0a2JBNQylmT7Sp6/8Axk88=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4=
|
||||
github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8=
|
||||
github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
||||
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
||||
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
|
||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
# CLI tool, only in development environment.
|
||||
# https://goframe.org/pages/viewpage.action?pageId=3673173
|
||||
gfcli:
|
||||
build:
|
||||
name: "goview" # 编译后的可执行文件名称
|
||||
# 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/goview" # 可执行文件生成路径
|
||||
extra: ""
|
||||
|
||||
docker:
|
||||
build: "-a amd64 -s linux -p temp -ew"
|
||||
tagPrefixes:
|
||||
- my.image.pub/my-app
|
||||
|
||||
gen:
|
||||
dao:
|
||||
- link: "mysql:goview:nWkG43Xxnbi2Y44C@tcp(127.0.0.1:3306)/goview?loc=Local&parseTime=true"
|
||||
removePrefix: "hg_"
|
||||
descriptionTag: true
|
||||
noModelComment: true
|
||||
jsonCase: "CamelLower"
|
||||
gJsonSupport: true
|
||||
clear: false
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
# Install/Update to the latest CLI tool.
|
||||
.PHONY: cli
|
||||
cli:
|
||||
@set -e; \
|
||||
wget -O gf https://github.com/gogf/gf/releases/latest/download/gf_$(shell go env GOOS)_$(shell go env GOARCH) && \
|
||||
chmod +x gf && \
|
||||
./gf install -y && \
|
||||
rm ./gf
|
||||
|
||||
|
||||
# Check and install CLI tool.
|
||||
.PHONY: cli.install
|
||||
cli.install:
|
||||
@set -e; \
|
||||
gf -v > /dev/null 2>&1 || if [[ "$?" -ne "0" ]]; then \
|
||||
echo "GoFame CLI is not installed, start proceeding auto installation..."; \
|
||||
make cli; \
|
||||
fi;
|
|
@ -0,0 +1,75 @@
|
|||
include ./hack/hack-cli.mk
|
||||
|
||||
# Update GoFrame and its CLI to latest stable version.
|
||||
.PHONY: up
|
||||
up: cli.install
|
||||
@gf up -a
|
||||
|
||||
# Build binary using configuration from hack/config.yaml.
|
||||
.PHONY: build
|
||||
build: cli.install
|
||||
@gf build -ew
|
||||
|
||||
# Parse api and generate controller/sdk.
|
||||
.PHONY: ctrl
|
||||
ctrl: cli.install
|
||||
@gf gen ctrl
|
||||
|
||||
# Generate Go files for DAO/DO/Entity.
|
||||
.PHONY: dao
|
||||
dao: cli.install
|
||||
@gf gen dao
|
||||
|
||||
# Parse current project go files and generate enums go file.
|
||||
.PHONY: enums
|
||||
enums: cli.install
|
||||
@gf gen enums
|
||||
|
||||
# Generate Go files for Service.
|
||||
.PHONY: service
|
||||
service: cli.install
|
||||
@gf gen service
|
||||
|
||||
|
||||
# Build docker image.
|
||||
.PHONY: image
|
||||
image: cli.install
|
||||
$(eval _TAG = $(shell git describe --dirty --always --tags --abbrev=8 --match 'v*' | sed 's/-/./2' | sed 's/-/./2'))
|
||||
ifneq (, $(shell git status --porcelain 2>/dev/null))
|
||||
$(eval _TAG = $(_TAG).dirty)
|
||||
endif
|
||||
$(eval _TAG = $(if ${TAG}, ${TAG}, $(_TAG)))
|
||||
$(eval _PUSH = $(if ${PUSH}, ${PUSH}, ))
|
||||
@gf docker ${_PUSH} -tn $(DOCKER_NAME):${_TAG};
|
||||
|
||||
|
||||
# Build docker image and automatically push to docker repo.
|
||||
.PHONY: image.push
|
||||
image.push:
|
||||
@make image PUSH=-p;
|
||||
|
||||
|
||||
# Deploy image and yaml to current kubectl environment.
|
||||
.PHONY: deploy
|
||||
deploy:
|
||||
$(eval _TAG = $(if ${TAG}, ${TAG}, develop))
|
||||
|
||||
@set -e; \
|
||||
mkdir -p $(ROOT_DIR)/temp/kustomize;\
|
||||
cd $(ROOT_DIR)/manifest/deploy/kustomize/overlays/${_ENV};\
|
||||
kustomize build > $(ROOT_DIR)/temp/kustomize.yaml;\
|
||||
kubectl apply -f $(ROOT_DIR)/temp/kustomize.yaml; \
|
||||
if [ $(DEPLOY_NAME) != "" ]; then \
|
||||
kubectl patch -n $(NAMESPACE) deployment/$(DEPLOY_NAME) -p "{\"spec\":{\"template\":{\"metadata\":{\"labels\":{\"date\":\"$(shell date +%s)\"}}}}}"; \
|
||||
fi;
|
||||
|
||||
|
||||
# Parsing protobuf files and generating go files.
|
||||
.PHONY: pb
|
||||
pb: cli.install
|
||||
@gf gen pb
|
||||
|
||||
# Generate protobuf files for database tables.
|
||||
.PHONY: pbentity
|
||||
pbentity: cli.install
|
||||
@gf gen pbentity
|
|
@ -0,0 +1,54 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/controller/project"
|
||||
"hotgo/internal/controller/site"
|
||||
"hotgo/internal/library/cache"
|
||||
"hotgo/internal/library/storager"
|
||||
"hotgo/internal/library/token"
|
||||
"hotgo/internal/service"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
)
|
||||
|
||||
var (
|
||||
Main = gcmd.Command{
|
||||
Name: "main",
|
||||
Usage: "main",
|
||||
Brief: "start http server",
|
||||
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
|
||||
|
||||
// 设置缓存适配器
|
||||
cache.SetAdapter(ctx)
|
||||
|
||||
token.InitConfig(ctx)
|
||||
|
||||
storager.InitConfig(ctx)
|
||||
|
||||
s := g.Server()
|
||||
|
||||
// 注册全局中间件
|
||||
s.BindMiddleware("/*any", []ghttp.HandlerFunc{
|
||||
service.Middleware().Ctx, // 初始化请求上下文,一般需要第一个进行加载,后续中间件存在依赖关系
|
||||
service.Middleware().CORS, // 跨域中间件,自动处理跨域问题
|
||||
service.Middleware().PreFilter, // 请求输入预处理,api使用gf规范路由并且XxxReq结构体实现了validate.Filter接口即可隐式预处理
|
||||
service.Middleware().ResponseHandler, // HTTP响应预处理,在业务处理完成后,对响应结果进行格式化和错误过滤,将处理后的数据发送给请求方
|
||||
}...)
|
||||
|
||||
s.Group("/api/goview", func(group *ghttp.RouterGroup) {
|
||||
group.Bind(
|
||||
site.NewV1(),
|
||||
)
|
||||
group.Middleware(service.Middleware().AdminAuth)
|
||||
group.Bind(
|
||||
project.NewV1(),
|
||||
)
|
||||
})
|
||||
s.Run()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
)
|
|
@ -0,0 +1,12 @@
|
|||
// Package consts
|
||||
// @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 consts
|
||||
|
||||
// 应用类型
|
||||
const (
|
||||
AppAdmin = "admin"
|
||||
AppDefault = "default"
|
||||
)
|
|
@ -0,0 +1,12 @@
|
|||
// Package consts
|
||||
// @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 consts
|
||||
|
||||
// cache
|
||||
const (
|
||||
CacheToken = "token" // 登录token
|
||||
CacheTokenBind = "token_bind" // 登录用户身份绑定
|
||||
)
|
|
@ -0,0 +1,14 @@
|
|||
// Package consts
|
||||
// @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 consts
|
||||
|
||||
type CtxKey string
|
||||
|
||||
// ContextKey 上下文
|
||||
|
||||
const (
|
||||
ContextHTTPKey CtxKey = "httpContext" // http上下文变量名称
|
||||
)
|
|
@ -0,0 +1,14 @@
|
|||
// Package consts
|
||||
// @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 consts
|
||||
|
||||
// 碎片
|
||||
|
||||
// curd.
|
||||
const (
|
||||
DefaultPage = 10 // 默认列表分页加载数量
|
||||
DefaultPageSize = 1 // 默认列表分页加载页码
|
||||
)
|
|
@ -0,0 +1,35 @@
|
|||
// Package consts
|
||||
// @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 consts
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
// 错误解释
|
||||
const (
|
||||
ErrorORM = "sql执行异常"
|
||||
ErrorNotData = "数据不存在"
|
||||
ErrorRotaPointer = "指针转换异常"
|
||||
)
|
||||
|
||||
// 需要隐藏真实错误的Wrap,开启访问日志后仍然会将真实错误记录
|
||||
var concealErrorSlice = []string{ErrorORM, ErrorRotaPointer}
|
||||
|
||||
// ErrorMessage 错误显示信息,非debug模式有效
|
||||
func ErrorMessage(err error) (message string) {
|
||||
if err == nil {
|
||||
return "操作失败!~"
|
||||
}
|
||||
|
||||
message = err.Error()
|
||||
for _, e := range concealErrorSlice {
|
||||
if gstr.Contains(message, e) {
|
||||
return "操作失败,请稍后重试!~"
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// Package consts
|
||||
// @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 consts
|
||||
|
||||
const (
|
||||
HTTPContentTypeXml = "text/xml"
|
||||
HTTPContentTypeHtml = "text/html"
|
||||
HTTPContentTypeStream = "text/event-stream"
|
||||
HTTPContentTypeJson = "application/json"
|
||||
HTTPContentTypeCustom = "custom"
|
||||
)
|
||||
|
||||
const (
|
||||
ApiStatusOk = 200
|
||||
ApiStatusTokenOverdue = 886
|
||||
)
|
|
@ -0,0 +1,14 @@
|
|||
// Package consts
|
||||
// @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 consts
|
||||
|
||||
// 状态码
|
||||
const (
|
||||
StatusALL int = -1 // 全部状态
|
||||
StatusEnabled int = 1 // 启用
|
||||
StatusDisable int = 2 // 禁用
|
||||
StatusDelete int = 3 // 已删除
|
||||
)
|
|
@ -0,0 +1,12 @@
|
|||
// Package consts
|
||||
// @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 consts
|
||||
|
||||
// 上传存储驱动
|
||||
|
||||
const (
|
||||
UploadDriveLocal = "local" // 本地驱动
|
||||
)
|
|
@ -0,0 +1,5 @@
|
|||
// =================================================================================
|
||||
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
|
||||
// =================================================================================
|
||||
|
||||
package project
|
|
@ -0,0 +1,15 @@
|
|||
// =================================================================================
|
||||
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
|
||||
// =================================================================================
|
||||
|
||||
package project
|
||||
|
||||
import (
|
||||
"hotgo/api/project"
|
||||
)
|
||||
|
||||
type ControllerV1 struct{}
|
||||
|
||||
func NewV1() project.IProjectV1 {
|
||||
return &ControllerV1{}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package project
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/service"
|
||||
|
||||
"hotgo/api/project/v1"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) Create(ctx context.Context, req *v1.CreateReq) (res *v1.CreateRes, err error) {
|
||||
res = new(v1.CreateRes)
|
||||
res.ProjectCreateModel, err = service.AdminProject().Create(ctx, &req.ProjectCreateInp)
|
||||
return
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package project
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"hotgo/api/project/v1"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) Delete(ctx context.Context, req *v1.DeleteReq) (res *v1.DeleteRes, err error) {
|
||||
return nil, gerror.NewCode(gcode.CodeNotImplemented)
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package project
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/service"
|
||||
|
||||
"hotgo/api/project/v1"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) Edit(ctx context.Context, req *v1.EditReq) (res *v1.EditRes, err error) {
|
||||
err = service.AdminProject().Edit(ctx, &req.ProjectEditInp)
|
||||
return
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package project
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/service"
|
||||
|
||||
"hotgo/api/project/v1"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) GetData(ctx context.Context, req *v1.GetDataReq) (res *v1.GetDataRes, err error) {
|
||||
res = new(v1.GetDataRes)
|
||||
res.ProjectGetDataModel, err = service.AdminProject().GetData(ctx, &req.ProjectGetDataInp)
|
||||
return
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package project
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"hotgo/internal/library/response"
|
||||
"hotgo/internal/service"
|
||||
|
||||
"hotgo/api/project/v1"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) List(ctx context.Context, req *v1.ListReq) (res *v1.ListRes, err error) {
|
||||
list, count, err := service.AdminProject().List(ctx, &req.ProjectListInp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response.CustomJson(ghttp.RequestFromCtx(ctx), g.Map{
|
||||
"code": 200,
|
||||
"count": count,
|
||||
"data": list,
|
||||
"msg": "获取成功",
|
||||
})
|
||||
return
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package project
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/service"
|
||||
|
||||
"hotgo/api/project/v1"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) Publish(ctx context.Context, req *v1.PublishReq) (res *v1.PublishRes, err error) {
|
||||
err = service.AdminProject().Publish(ctx, &req.ProjectPublishInp)
|
||||
return
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package project
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/service"
|
||||
|
||||
"hotgo/api/project/v1"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) SaveData(ctx context.Context, req *v1.SaveDataReq) (res *v1.SaveDataRes, err error) {
|
||||
err = service.AdminProject().SaveData(ctx, &req.ProjectSaveDataInp)
|
||||
return
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package project
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/api/project/v1"
|
||||
"hotgo/internal/library/storager"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) Upload(ctx context.Context, req *v1.UploadReq) (res *v1.UploadRes, err error) {
|
||||
attachment, err := storager.DoUpload(ctx, "default", req.File)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res = new(v1.UploadRes)
|
||||
res.FileUrl = storager.LastUrl(ctx, attachment.FileUrl, attachment.Drive)
|
||||
return
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
// =================================================================================
|
||||
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
|
||||
// =================================================================================
|
||||
|
||||
package site
|
|
@ -0,0 +1,15 @@
|
|||
// =================================================================================
|
||||
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
|
||||
// =================================================================================
|
||||
|
||||
package site
|
||||
|
||||
import (
|
||||
"hotgo/api/site"
|
||||
)
|
||||
|
||||
type ControllerV1 struct{}
|
||||
|
||||
func NewV1() site.ISiteV1 {
|
||||
return &ControllerV1{}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package site
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"hotgo/internal/service"
|
||||
|
||||
"hotgo/api/site/v1"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) AccountLogin(ctx context.Context, req *v1.AccountLoginReq) (res *v1.AccountLoginRes, err error) {
|
||||
model, err := service.AdminSite().AccountLogin(ctx, &req.AccountLoginInp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = gconv.Scan(model, &res)
|
||||
return
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package site
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/utility/url"
|
||||
|
||||
"hotgo/api/site/v1"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) GetOssInfo(ctx context.Context, req *v1.GetOssInfoReq) (res *v1.GetOssInfoRes, err error) {
|
||||
res = new(v1.GetOssInfoRes)
|
||||
res.BucketURL = url.GetAddr(ctx)
|
||||
return
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package site
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"hotgo/internal/library/token"
|
||||
|
||||
"hotgo/api/site/v1"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) LoginLogout(ctx context.Context, req *v1.LoginLogoutReq) (res *v1.LoginLogoutRes, err error) {
|
||||
err = token.Logout(ghttp.RequestFromCtx(ctx))
|
||||
return
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package site
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/service"
|
||||
|
||||
"hotgo/api/site/v1"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) Register(ctx context.Context, req *v1.RegisterReq) (res *v1.RegisterRes, err error) {
|
||||
err = service.AdminSite().Register(ctx, &req.RegisterInp)
|
||||
return
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package site
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"hotgo/internal/library/response"
|
||||
"hotgo/utility/format"
|
||||
"math/rand"
|
||||
|
||||
"hotgo/api/site/v1"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) TestData(ctx context.Context, req *v1.TestDataReq) (res *v1.TestDataRes, err error) {
|
||||
res = new(v1.TestDataRes)
|
||||
|
||||
// 模拟设备信息
|
||||
res.DeviceInfo = map[string]interface{}{
|
||||
"deviceId": "IoTDevice001",
|
||||
"GPSLongitude": "73.56789",
|
||||
"GPSLatitude": "-42.12345",
|
||||
"location": "河南郑州高新区科学大道001号",
|
||||
"deviceStatus": true,
|
||||
"deviceTime": gtime.Now(),
|
||||
}
|
||||
|
||||
fields := []string{
|
||||
"temperature", "humidity", "pressure", "flow",
|
||||
"factoryData", "voltage", "current", "waterLevel",
|
||||
"airQuality", "vibration", "noiseLevel", "ambientLight",
|
||||
"windSpeed", "deviceTemperature", "deviceVibration",
|
||||
"deviceVoltage", "deviceCurrent", "powerConsumption",
|
||||
}
|
||||
res.DeviceData = make(map[string]interface{}, len(fields))
|
||||
for _, field := range fields {
|
||||
// 模拟设备上报随机数值
|
||||
res.DeviceData[field] = format.Round2Float64(rand.Float64() * 100)
|
||||
}
|
||||
|
||||
// 自定义返回字段格式
|
||||
response.CustomJson(ghttp.RequestFromCtx(ctx), g.Map{
|
||||
"code": 200,
|
||||
"data": res,
|
||||
"msg": "获取设备数据成功",
|
||||
})
|
||||
return
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// =================================================================================
|
||||
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
|
||||
// =================================================================================
|
||||
|
||||
package dao
|
||||
|
||||
import (
|
||||
"hotgo/internal/dao/internal"
|
||||
)
|
||||
|
||||
// internalAdminAttachmentDao is internal type for wrapping internal DAO implements.
|
||||
type internalAdminAttachmentDao = *internal.AdminAttachmentDao
|
||||
|
||||
// adminAttachmentDao is the data access object for table hg_admin_attachment.
|
||||
// You can define custom methods on it to extend its functionality as you wish.
|
||||
type adminAttachmentDao struct {
|
||||
internalAdminAttachmentDao
|
||||
}
|
||||
|
||||
var (
|
||||
// AdminAttachment is globally public accessible object for table hg_admin_attachment operations.
|
||||
AdminAttachment = adminAttachmentDao{
|
||||
internal.NewAdminAttachmentDao(),
|
||||
}
|
||||
)
|
||||
|
||||
// Fill with you ideas below.
|
|
@ -0,0 +1,27 @@
|
|||
// =================================================================================
|
||||
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
|
||||
// =================================================================================
|
||||
|
||||
package dao
|
||||
|
||||
import (
|
||||
"hotgo/internal/dao/internal"
|
||||
)
|
||||
|
||||
// internalAdminMemberDao is internal type for wrapping internal DAO implements.
|
||||
type internalAdminMemberDao = *internal.AdminMemberDao
|
||||
|
||||
// adminMemberDao is the data access object for table hg_admin_member.
|
||||
// You can define custom methods on it to extend its functionality as you wish.
|
||||
type adminMemberDao struct {
|
||||
internalAdminMemberDao
|
||||
}
|
||||
|
||||
var (
|
||||
// AdminMember is globally public accessible object for table hg_admin_member operations.
|
||||
AdminMember = adminMemberDao{
|
||||
internal.NewAdminMemberDao(),
|
||||
}
|
||||
)
|
||||
|
||||
// Fill with you ideas below.
|
|
@ -0,0 +1,27 @@
|
|||
// =================================================================================
|
||||
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
|
||||
// =================================================================================
|
||||
|
||||
package dao
|
||||
|
||||
import (
|
||||
"hotgo/internal/dao/internal"
|
||||
)
|
||||
|
||||
// internalAdminProjectDao is internal type for wrapping internal DAO implements.
|
||||
type internalAdminProjectDao = *internal.AdminProjectDao
|
||||
|
||||
// adminProjectDao is the data access object for table hg_admin_project.
|
||||
// You can define custom methods on it to extend its functionality as you wish.
|
||||
type adminProjectDao struct {
|
||||
internalAdminProjectDao
|
||||
}
|
||||
|
||||
var (
|
||||
// AdminProject is globally public accessible object for table hg_admin_project operations.
|
||||
AdminProject = adminProjectDao{
|
||||
internal.NewAdminProjectDao(),
|
||||
}
|
||||
)
|
||||
|
||||
// Fill with you ideas below.
|
|
@ -0,0 +1,105 @@
|
|||
// ==========================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// ==========================================================================
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// AdminAttachmentDao is the data access object for table hg_admin_attachment.
|
||||
type AdminAttachmentDao struct {
|
||||
table string // table is the underlying table name of the DAO.
|
||||
group string // group is the database configuration group name of current DAO.
|
||||
columns AdminAttachmentColumns // columns contains all the column names of Table for convenient usage.
|
||||
}
|
||||
|
||||
// AdminAttachmentColumns defines and stores column names for table hg_admin_attachment.
|
||||
type AdminAttachmentColumns struct {
|
||||
Id string // 文件ID
|
||||
AppId string // 应用ID
|
||||
MemberId string // 管理员ID
|
||||
CateId string // 上传分类
|
||||
Drive string // 上传驱动
|
||||
Name string // 文件原始名
|
||||
Kind string // 上传类型
|
||||
MimeType string // 扩展类型
|
||||
NaiveType string // NaiveUI类型
|
||||
Path string // 本地路径
|
||||
FileUrl string // url
|
||||
Size string // 文件大小
|
||||
Ext string // 扩展名
|
||||
Md5 string // md5校验码
|
||||
Status string // 状态
|
||||
CreatedAt string // 创建时间
|
||||
UpdatedAt string // 修改时间
|
||||
}
|
||||
|
||||
// adminAttachmentColumns holds the columns for table hg_admin_attachment.
|
||||
var adminAttachmentColumns = AdminAttachmentColumns{
|
||||
Id: "id",
|
||||
AppId: "app_id",
|
||||
MemberId: "member_id",
|
||||
CateId: "cate_id",
|
||||
Drive: "drive",
|
||||
Name: "name",
|
||||
Kind: "kind",
|
||||
MimeType: "mime_type",
|
||||
NaiveType: "naive_type",
|
||||
Path: "path",
|
||||
FileUrl: "file_url",
|
||||
Size: "size",
|
||||
Ext: "ext",
|
||||
Md5: "md5",
|
||||
Status: "status",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
}
|
||||
|
||||
// NewAdminAttachmentDao creates and returns a new DAO object for table data access.
|
||||
func NewAdminAttachmentDao() *AdminAttachmentDao {
|
||||
return &AdminAttachmentDao{
|
||||
group: "default",
|
||||
table: "hg_admin_attachment",
|
||||
columns: adminAttachmentColumns,
|
||||
}
|
||||
}
|
||||
|
||||
// DB retrieves and returns the underlying raw database management object of current DAO.
|
||||
func (dao *AdminAttachmentDao) DB() gdb.DB {
|
||||
return g.DB(dao.group)
|
||||
}
|
||||
|
||||
// Table returns the table name of current dao.
|
||||
func (dao *AdminAttachmentDao) Table() string {
|
||||
return dao.table
|
||||
}
|
||||
|
||||
// Columns returns all column names of current dao.
|
||||
func (dao *AdminAttachmentDao) Columns() AdminAttachmentColumns {
|
||||
return dao.columns
|
||||
}
|
||||
|
||||
// Group returns the configuration group name of database of current dao.
|
||||
func (dao *AdminAttachmentDao) Group() string {
|
||||
return dao.group
|
||||
}
|
||||
|
||||
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
|
||||
func (dao *AdminAttachmentDao) Ctx(ctx context.Context) *gdb.Model {
|
||||
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
|
||||
}
|
||||
|
||||
// Transaction wraps the transaction logic using function f.
|
||||
// It rollbacks the transaction and returns the error from function f if it returns non-nil error.
|
||||
// It commits the transaction and returns nil if function f returns nil.
|
||||
//
|
||||
// Note that, you should not Commit or Rollback the transaction in function f
|
||||
// as it is automatically handled by this function.
|
||||
func (dao *AdminAttachmentDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
|
||||
return dao.Ctx(ctx).Transaction(ctx, f)
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
// ==========================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// ==========================================================================
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// AdminMemberDao is the data access object for table hg_admin_member.
|
||||
type AdminMemberDao struct {
|
||||
table string // table is the underlying table name of the DAO.
|
||||
group string // group is the database configuration group name of current DAO.
|
||||
columns AdminMemberColumns // columns contains all the column names of Table for convenient usage.
|
||||
}
|
||||
|
||||
// AdminMemberColumns defines and stores column names for table hg_admin_member.
|
||||
type AdminMemberColumns struct {
|
||||
Id string // 管理员ID
|
||||
Nickname string // 昵称
|
||||
Username string // 帐号
|
||||
PasswordHash string // 密码
|
||||
Salt string // 密码盐
|
||||
Avatar string // 头像
|
||||
LastActiveAt string // 最后活跃时间
|
||||
Remark string // 备注
|
||||
Status string // 状态
|
||||
CreatedAt string // 创建时间
|
||||
UpdatedAt string // 修改时间
|
||||
}
|
||||
|
||||
// adminMemberColumns holds the columns for table hg_admin_member.
|
||||
var adminMemberColumns = AdminMemberColumns{
|
||||
Id: "id",
|
||||
Nickname: "nickname",
|
||||
Username: "username",
|
||||
PasswordHash: "password_hash",
|
||||
Salt: "salt",
|
||||
Avatar: "avatar",
|
||||
LastActiveAt: "last_active_at",
|
||||
Remark: "remark",
|
||||
Status: "status",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
}
|
||||
|
||||
// NewAdminMemberDao creates and returns a new DAO object for table data access.
|
||||
func NewAdminMemberDao() *AdminMemberDao {
|
||||
return &AdminMemberDao{
|
||||
group: "default",
|
||||
table: "hg_admin_member",
|
||||
columns: adminMemberColumns,
|
||||
}
|
||||
}
|
||||
|
||||
// DB retrieves and returns the underlying raw database management object of current DAO.
|
||||
func (dao *AdminMemberDao) DB() gdb.DB {
|
||||
return g.DB(dao.group)
|
||||
}
|
||||
|
||||
// Table returns the table name of current dao.
|
||||
func (dao *AdminMemberDao) Table() string {
|
||||
return dao.table
|
||||
}
|
||||
|
||||
// Columns returns all column names of current dao.
|
||||
func (dao *AdminMemberDao) Columns() AdminMemberColumns {
|
||||
return dao.columns
|
||||
}
|
||||
|
||||
// Group returns the configuration group name of database of current dao.
|
||||
func (dao *AdminMemberDao) Group() string {
|
||||
return dao.group
|
||||
}
|
||||
|
||||
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
|
||||
func (dao *AdminMemberDao) Ctx(ctx context.Context) *gdb.Model {
|
||||
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
|
||||
}
|
||||
|
||||
// Transaction wraps the transaction logic using function f.
|
||||
// It rollbacks the transaction and returns the error from function f if it returns non-nil error.
|
||||
// It commits the transaction and returns nil if function f returns nil.
|
||||
//
|
||||
// Note that, you should not Commit or Rollback the transaction in function f
|
||||
// as it is automatically handled by this function.
|
||||
func (dao *AdminMemberDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
|
||||
return dao.Ctx(ctx).Transaction(ctx, f)
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
// ==========================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// ==========================================================================
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// AdminProjectDao is the data access object for table hg_admin_project.
|
||||
type AdminProjectDao struct {
|
||||
table string // table is the underlying table name of the DAO.
|
||||
group string // group is the database configuration group name of current DAO.
|
||||
columns AdminProjectColumns // columns contains all the column names of Table for convenient usage.
|
||||
}
|
||||
|
||||
// AdminProjectColumns defines and stores column names for table hg_admin_project.
|
||||
type AdminProjectColumns struct {
|
||||
Id string // 项目id
|
||||
ProjectName string // 项目名称
|
||||
IndexImage string // 预览图片url
|
||||
Content string // 项目数据
|
||||
Status string // 项目状态,-1: 未发布'1: 已发布
|
||||
Remarks string // 项目备注
|
||||
CreatedBy string // 创建人
|
||||
UpdatedAt string // 更新时间
|
||||
CreatedAt string // 创建时间
|
||||
DeletedAt string // 删除时间
|
||||
}
|
||||
|
||||
// adminProjectColumns holds the columns for table hg_admin_project.
|
||||
var adminProjectColumns = AdminProjectColumns{
|
||||
Id: "id",
|
||||
ProjectName: "project_name",
|
||||
IndexImage: "index_image",
|
||||
Content: "content",
|
||||
Status: "status",
|
||||
Remarks: "remarks",
|
||||
CreatedBy: "created_by",
|
||||
UpdatedAt: "updated_at",
|
||||
CreatedAt: "created_at",
|
||||
DeletedAt: "deleted_at",
|
||||
}
|
||||
|
||||
// NewAdminProjectDao creates and returns a new DAO object for table data access.
|
||||
func NewAdminProjectDao() *AdminProjectDao {
|
||||
return &AdminProjectDao{
|
||||
group: "default",
|
||||
table: "hg_admin_project",
|
||||
columns: adminProjectColumns,
|
||||
}
|
||||
}
|
||||
|
||||
// DB retrieves and returns the underlying raw database management object of current DAO.
|
||||
func (dao *AdminProjectDao) DB() gdb.DB {
|
||||
return g.DB(dao.group)
|
||||
}
|
||||
|
||||
// Table returns the table name of current dao.
|
||||
func (dao *AdminProjectDao) Table() string {
|
||||
return dao.table
|
||||
}
|
||||
|
||||
// Columns returns all column names of current dao.
|
||||
func (dao *AdminProjectDao) Columns() AdminProjectColumns {
|
||||
return dao.columns
|
||||
}
|
||||
|
||||
// Group returns the configuration group name of database of current dao.
|
||||
func (dao *AdminProjectDao) Group() string {
|
||||
return dao.group
|
||||
}
|
||||
|
||||
// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation.
|
||||
func (dao *AdminProjectDao) Ctx(ctx context.Context) *gdb.Model {
|
||||
return dao.DB().Model(dao.table).Safe().Ctx(ctx)
|
||||
}
|
||||
|
||||
// Transaction wraps the transaction logic using function f.
|
||||
// It rollbacks the transaction and returns the error from function f if it returns non-nil error.
|
||||
// It commits the transaction and returns nil if function f returns nil.
|
||||
//
|
||||
// Note that, you should not Commit or Rollback the transaction in function f
|
||||
// as it is automatically handled by this function.
|
||||
func (dao *AdminProjectDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
|
||||
return dao.Ctx(ctx).Transaction(ctx, f)
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
// Package cache
|
||||
// @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 cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcache"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"hotgo/internal/library/cache/file"
|
||||
)
|
||||
|
||||
// cache 缓存驱动
|
||||
var cache *gcache.Cache
|
||||
|
||||
// Instance 缓存实例
|
||||
func Instance() *gcache.Cache {
|
||||
if cache == nil {
|
||||
panic("cache uninitialized.")
|
||||
}
|
||||
return cache
|
||||
}
|
||||
|
||||
// SetAdapter 设置缓存适配器
|
||||
func SetAdapter(ctx context.Context) {
|
||||
var adapter gcache.Adapter
|
||||
|
||||
switch g.Cfg().MustGet(ctx, "cache.adapter").String() {
|
||||
case "redis":
|
||||
adapter = gcache.NewAdapterRedis(g.Redis())
|
||||
case "file":
|
||||
fileDir := g.Cfg().MustGet(ctx, "cache.fileDir").String()
|
||||
if fileDir == "" {
|
||||
g.Log().Fatal(ctx, "file path must be configured for file caching.")
|
||||
return
|
||||
}
|
||||
|
||||
if !gfile.Exists(fileDir) {
|
||||
if err := gfile.Mkdir(fileDir); err != nil {
|
||||
g.Log().Fatalf(ctx, "failed to create the cache directory. procedure, err:%+v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
adapter = file.NewAdapterFile(fileDir)
|
||||
default:
|
||||
adapter = gcache.NewAdapterMemory()
|
||||
}
|
||||
|
||||
// 数据库缓存,默认和通用缓冲驱动一致,如果你不想使用默认的,可以自行调整
|
||||
//g.DB().GetCache().SetAdapter(adapter)
|
||||
|
||||
// 通用缓存
|
||||
cache = gcache.New()
|
||||
cache.SetAdapter(adapter)
|
||||
}
|
|
@ -0,0 +1,322 @@
|
|||
// Package file
|
||||
// @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 file
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/os/gcache"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
// AdapterFile is the gcache adapter implements using file server.
|
||||
AdapterFile struct {
|
||||
dir string
|
||||
}
|
||||
|
||||
fileContent struct {
|
||||
Duration int64 `json:"duration"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
}
|
||||
)
|
||||
|
||||
const perm = 0o666
|
||||
|
||||
var (
|
||||
CacheExpiredErr = errors.New("cache expired")
|
||||
)
|
||||
|
||||
// NewAdapterFile creates and returns a new memory cache object.
|
||||
func NewAdapterFile(dir string) gcache.Adapter {
|
||||
return &AdapterFile{
|
||||
dir: dir,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AdapterFile) Set(ctx context.Context, key interface{}, value interface{}, lifeTime time.Duration) (err error) {
|
||||
fileKey := gconv.String(key)
|
||||
if value == nil || lifeTime < 0 {
|
||||
return c.Delete(fileKey)
|
||||
}
|
||||
return c.Save(fileKey, gconv.String(value), lifeTime)
|
||||
}
|
||||
|
||||
func (c *AdapterFile) SetMap(ctx context.Context, data map[interface{}]interface{}, duration time.Duration) (err error) {
|
||||
return gerror.New("implement me")
|
||||
}
|
||||
|
||||
func (c *AdapterFile) SetIfNotExist(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (ok bool, err error) {
|
||||
return false, gerror.New("implement me")
|
||||
}
|
||||
|
||||
func (c *AdapterFile) SetIfNotExistFunc(ctx context.Context, key interface{}, f gcache.Func, duration time.Duration) (ok bool, err error) {
|
||||
return false, gerror.New("implement me")
|
||||
}
|
||||
|
||||
func (c *AdapterFile) SetIfNotExistFuncLock(ctx context.Context, key interface{}, f gcache.Func, duration time.Duration) (ok bool, err error) {
|
||||
return false, gerror.New("implement me")
|
||||
}
|
||||
|
||||
func (c *AdapterFile) Get(ctx context.Context, key interface{}) (*gvar.Var, error) {
|
||||
fetch, err := c.Fetch(gconv.String(key))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gvar.New(fetch), nil
|
||||
}
|
||||
|
||||
func (c *AdapterFile) GetOrSet(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (result *gvar.Var, err error) {
|
||||
result, err = c.Get(ctx, key)
|
||||
if err != nil && !errors.Is(err, CacheExpiredErr) {
|
||||
return nil, err
|
||||
}
|
||||
if result.IsNil() {
|
||||
return gvar.New(value), c.Set(ctx, key, value, duration)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *AdapterFile) GetOrSetFunc(ctx context.Context, key interface{}, f gcache.Func, duration time.Duration) (result *gvar.Var, err error) {
|
||||
v, err := c.Get(ctx, key)
|
||||
if err != nil && !errors.Is(err, CacheExpiredErr) {
|
||||
return nil, err
|
||||
}
|
||||
if v.IsNil() {
|
||||
value, err := f(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if value == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return gvar.New(value), c.Set(ctx, key, value, duration)
|
||||
} else {
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AdapterFile) GetOrSetFuncLock(ctx context.Context, key interface{}, f gcache.Func, duration time.Duration) (result *gvar.Var, err error) {
|
||||
return c.GetOrSetFunc(ctx, key, f, duration)
|
||||
}
|
||||
|
||||
func (c *AdapterFile) Contains(ctx context.Context, key interface{}) (bool, error) {
|
||||
return c.Has(gconv.String(key)), nil
|
||||
}
|
||||
|
||||
func (c *AdapterFile) Size(ctx context.Context) (size int, err error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (c *AdapterFile) Data(ctx context.Context) (data map[interface{}]interface{}, err error) {
|
||||
return nil, gerror.New("implement me")
|
||||
}
|
||||
|
||||
func (c *AdapterFile) Keys(ctx context.Context) (keys []interface{}, err error) {
|
||||
return nil, gerror.New("implement me")
|
||||
}
|
||||
|
||||
func (c *AdapterFile) Values(ctx context.Context) (values []interface{}, err error) {
|
||||
return nil, gerror.New("implement me")
|
||||
}
|
||||
|
||||
func (c *AdapterFile) Update(ctx context.Context, key interface{}, value interface{}) (oldValue *gvar.Var, exist bool, err error) {
|
||||
return nil, false, gerror.New("implement me")
|
||||
}
|
||||
|
||||
func (c *AdapterFile) UpdateExpire(ctx context.Context, key interface{}, duration time.Duration) (oldDuration time.Duration, err error) {
|
||||
var (
|
||||
v *gvar.Var
|
||||
oldTTL int64
|
||||
fileKey = gconv.String(key)
|
||||
)
|
||||
// TTL.
|
||||
expire, err := c.GetExpire(ctx, fileKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
oldTTL = int64(expire)
|
||||
if oldTTL == -2 {
|
||||
// It does not exist.
|
||||
return
|
||||
}
|
||||
oldDuration = time.Duration(oldTTL) * time.Second
|
||||
// DEL.
|
||||
if duration < 0 {
|
||||
err = c.Delete(fileKey)
|
||||
return
|
||||
}
|
||||
v, err = c.Get(ctx, fileKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = c.Set(ctx, fileKey, v.Val(), duration)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *AdapterFile) GetExpire(ctx context.Context, key interface{}) (time.Duration, error) {
|
||||
content, err := c.read(gconv.String(key))
|
||||
if err != nil {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
if content.Duration <= time.Now().Unix() {
|
||||
return -1, nil
|
||||
}
|
||||
return time.Duration(time.Now().Unix()-content.Duration) * time.Second, nil
|
||||
}
|
||||
|
||||
func (c *AdapterFile) Remove(ctx context.Context, keys ...interface{}) (lastValue *gvar.Var, err error) {
|
||||
if len(keys) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
// Retrieves the last key value.
|
||||
if lastValue, err = c.Get(ctx, gconv.String(keys[len(keys)-1])); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Deletes all given keys.
|
||||
err = c.DeleteMulti(gconv.Strings(keys)...)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *AdapterFile) Clear(ctx context.Context) error {
|
||||
return c.Flush()
|
||||
}
|
||||
|
||||
func (c *AdapterFile) Close(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *AdapterFile) createName(key string) string {
|
||||
h := sha256.New()
|
||||
_, _ = h.Write([]byte(key))
|
||||
hash := hex.EncodeToString(h.Sum(nil))
|
||||
return filepath.Join(c.dir, fmt.Sprintf("%s.cache", hash))
|
||||
}
|
||||
|
||||
func (c *AdapterFile) read(key string) (*fileContent, error) {
|
||||
rp := gfile.RealPath(c.createName(key))
|
||||
if rp == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
value, err := os.ReadFile(rp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
content := &fileContent{}
|
||||
if err := json.Unmarshal(value, content); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if content.Duration == 0 {
|
||||
return content, nil
|
||||
}
|
||||
|
||||
if content.Duration <= time.Now().Unix() {
|
||||
_ = c.Delete(key)
|
||||
return nil, CacheExpiredErr
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
|
||||
// Has checks if the cached key exists into the File storage
|
||||
func (c *AdapterFile) Has(key string) bool {
|
||||
fc, err := c.read(key)
|
||||
return err == nil && fc != nil
|
||||
}
|
||||
|
||||
// Delete the cached key from File storage
|
||||
func (c *AdapterFile) Delete(key string) error {
|
||||
_, err := os.Stat(c.createName(key))
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return os.Remove(c.createName(key))
|
||||
}
|
||||
|
||||
// DeleteMulti the cached key from File storage
|
||||
func (c *AdapterFile) DeleteMulti(keys ...string) (err error) {
|
||||
for _, key := range keys {
|
||||
if err = c.Delete(key); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Fetch retrieves the cached value from key of the File storage
|
||||
func (c *AdapterFile) Fetch(key string) (interface{}, error) {
|
||||
content, err := c.read(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if content == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return content.Data, nil
|
||||
}
|
||||
|
||||
// FetchMulti retrieve multiple cached values from keys of the File storage
|
||||
func (c *AdapterFile) FetchMulti(keys []string) map[string]interface{} {
|
||||
result := make(map[string]interface{})
|
||||
for _, key := range keys {
|
||||
if value, err := c.Fetch(key); err == nil {
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Flush removes all cached keys of the File storage
|
||||
func (c *AdapterFile) Flush() error {
|
||||
dir, err := os.Open(c.dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = dir.Close()
|
||||
}()
|
||||
|
||||
names, _ := dir.Readdirnames(-1)
|
||||
|
||||
for _, name := range names {
|
||||
_ = os.Remove(filepath.Join(c.dir, name))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Save a value in File storage by key
|
||||
func (c *AdapterFile) Save(key string, value string, lifeTime time.Duration) error {
|
||||
duration := int64(0)
|
||||
|
||||
if lifeTime > 0 {
|
||||
duration = time.Now().Unix() + int64(lifeTime.Seconds())
|
||||
}
|
||||
|
||||
content := &fileContent{duration, value}
|
||||
|
||||
data, err := json.Marshal(content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.WriteFile(c.createName(key), data, perm)
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
// Package contexts
|
||||
// @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 contexts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/model"
|
||||
)
|
||||
|
||||
// Init 初始化上下文对象指针到上下文对象中,以便后续的请求流程中可以修改
|
||||
func Init(r *ghttp.Request, customCtx *model.Context) {
|
||||
r.SetCtxVar(consts.ContextHTTPKey, customCtx)
|
||||
}
|
||||
|
||||
// Get 获得上下文变量,如果没有设置,那么返回nil
|
||||
func Get(ctx context.Context) *model.Context {
|
||||
value := ctx.Value(consts.ContextHTTPKey)
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
if localCtx, ok := value.(*model.Context); ok {
|
||||
return localCtx
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetUser 将上下文信息设置到上下文请求中,注意是完整覆盖
|
||||
func SetUser(ctx context.Context, user *model.Identity) {
|
||||
c := Get(ctx)
|
||||
if c == nil {
|
||||
g.Log().Warning(ctx, "contexts.SetUser, c == nil ")
|
||||
return
|
||||
}
|
||||
c.User = user
|
||||
}
|
||||
|
||||
// SetResponse 设置组件响应 用于访问日志使用
|
||||
func SetResponse(ctx context.Context, response *model.Response) {
|
||||
c := Get(ctx)
|
||||
if c == nil {
|
||||
g.Log().Warning(ctx, "contexts.SetResponse, c == nil ")
|
||||
return
|
||||
}
|
||||
c.Response = response
|
||||
}
|
||||
|
||||
// SetModule 设置应用模块
|
||||
func SetModule(ctx context.Context, module string) {
|
||||
c := Get(ctx)
|
||||
if c == nil {
|
||||
g.Log().Warning(ctx, "contexts.SetModule, c == nil ")
|
||||
return
|
||||
}
|
||||
c.Module = module
|
||||
}
|
||||
|
||||
// GetUser 获取用户信息
|
||||
func GetUser(ctx context.Context) *model.Identity {
|
||||
c := Get(ctx)
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.User
|
||||
}
|
||||
|
||||
// GetUserId 获取用户ID
|
||||
func GetUserId(ctx context.Context) int64 {
|
||||
user := GetUser(ctx)
|
||||
if user == nil {
|
||||
return 0
|
||||
}
|
||||
return user.Id
|
||||
}
|
||||
|
||||
// GetModule 获取应用模块
|
||||
func GetModule(ctx context.Context) string {
|
||||
c := Get(ctx)
|
||||
if c == nil {
|
||||
return ""
|
||||
}
|
||||
return c.Module
|
||||
}
|
||||
|
||||
// SetData 设置额外数据
|
||||
func SetData(ctx context.Context, k string, v interface{}) {
|
||||
c := Get(ctx)
|
||||
if c == nil {
|
||||
g.Log().Warning(ctx, "contexts.SetData, c == nil ")
|
||||
return
|
||||
}
|
||||
Get(ctx).Data[k] = v
|
||||
}
|
||||
|
||||
// SetDataMap 设置额外数据
|
||||
func SetDataMap(ctx context.Context, vs g.Map) {
|
||||
c := Get(ctx)
|
||||
if c == nil {
|
||||
g.Log().Warning(ctx, "contexts.SetData, c == nil ")
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range vs {
|
||||
Get(ctx).Data[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// GetData 获取额外数据
|
||||
func GetData(ctx context.Context) g.Map {
|
||||
c := Get(ctx)
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.Data
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
// Package contexts
|
||||
// @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 contexts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type detached struct {
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (detached) Deadline() (time.Time, bool) {
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
func (detached) Done() <-chan struct{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (detached) Err() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d detached) Value(key interface{}) interface{} {
|
||||
return d.ctx.Value(key)
|
||||
}
|
||||
|
||||
func Detach(ctx context.Context) context.Context {
|
||||
return detached{ctx: ctx}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
// Package response
|
||||
// @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 response
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/library/contexts"
|
||||
"hotgo/internal/model"
|
||||
)
|
||||
|
||||
// JsonExit 返回JSON数据并退出当前HTTP执行函数
|
||||
func JsonExit(r *ghttp.Request, code int, message string, data ...interface{}) {
|
||||
RJson(r, code, message, data...)
|
||||
r.Exit()
|
||||
}
|
||||
|
||||
// RXml xml
|
||||
func RXml(r *ghttp.Request, code int, message string, data ...interface{}) {
|
||||
responseData := interface{}(nil)
|
||||
if len(data) > 0 {
|
||||
responseData = data[0]
|
||||
}
|
||||
res := &model.Response{
|
||||
Code: code,
|
||||
Message: message,
|
||||
Timestamp: gtime.Timestamp(),
|
||||
TraceID: gctx.CtxId(r.Context()),
|
||||
}
|
||||
|
||||
// 如果不是正常的返回,则将data转为error
|
||||
if gcode.CodeOK.Code() == code {
|
||||
res.Data = responseData
|
||||
} else {
|
||||
res.Error = responseData
|
||||
}
|
||||
|
||||
// 清空响应
|
||||
r.Response.ClearBuffer()
|
||||
|
||||
// 写入响应
|
||||
r.Response.WriteXml(gconv.Map(res))
|
||||
|
||||
// 加入到上下文
|
||||
contexts.SetResponse(r.Context(), res)
|
||||
}
|
||||
|
||||
// RJson 标准返回结果数据结构封装
|
||||
func RJson(r *ghttp.Request, code int, message string, data ...interface{}) {
|
||||
responseData := interface{}(nil)
|
||||
if len(data) > 0 {
|
||||
responseData = data[0]
|
||||
}
|
||||
res := &model.Response{
|
||||
Code: code,
|
||||
Message: message,
|
||||
Timestamp: gtime.Timestamp(),
|
||||
TraceID: gctx.CtxId(r.Context()),
|
||||
}
|
||||
|
||||
// 如果不是正常的返回,则将data转为error
|
||||
if consts.ApiStatusOk == code {
|
||||
res.Data = responseData
|
||||
} else {
|
||||
res.Error = responseData
|
||||
}
|
||||
|
||||
// 清空响应
|
||||
r.Response.ClearBuffer()
|
||||
|
||||
// 写入响应
|
||||
r.Response.WriteJson(res)
|
||||
|
||||
// 加入到上下文
|
||||
contexts.SetResponse(r.Context(), res)
|
||||
}
|
||||
|
||||
// CustomJson 自定义JSON
|
||||
func CustomJson(r *ghttp.Request, content interface{}) {
|
||||
// 清空响应
|
||||
r.Response.ClearBuffer()
|
||||
|
||||
// 写入响应
|
||||
r.Response.WriteJson(content)
|
||||
|
||||
// 加入到上下文
|
||||
contexts.SetResponse(r.Context(), &model.Response{
|
||||
Code: consts.ApiStatusOk,
|
||||
Message: "",
|
||||
Data: content,
|
||||
Error: nil,
|
||||
Timestamp: gtime.Timestamp(),
|
||||
TraceID: gctx.CtxId(r.Context()),
|
||||
})
|
||||
}
|
||||
|
||||
// Redirect 重定向
|
||||
func Redirect(r *ghttp.Request, location string, code ...int) {
|
||||
r.Response.RedirectTo(location, code...)
|
||||
}
|
||||
|
||||
// RText 返回成功文本
|
||||
func RText(r *ghttp.Request, message string) {
|
||||
// 清空响应
|
||||
r.Response.ClearBuffer()
|
||||
|
||||
// 写入响应
|
||||
r.Response.Write(message)
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// Package storager
|
||||
// @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 storager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"hotgo/internal/model"
|
||||
)
|
||||
|
||||
var config *model.UploadConfig
|
||||
|
||||
func InitConfig(ctx context.Context) {
|
||||
err := g.Cfg().MustGet(ctx, "storager").Scan(&config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
panic("没有找到storager配置")
|
||||
}
|
||||
}
|
||||
|
||||
func GetModel(ctx context.Context) *gdb.Model {
|
||||
return g.Model("admin_attachment").Ctx(ctx)
|
||||
}
|
|
@ -0,0 +1,232 @@
|
|||
// Package storager
|
||||
// @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 storager
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"io"
|
||||
"path"
|
||||
)
|
||||
|
||||
// 文件归属分类
|
||||
const (
|
||||
KindImg = "image" // 图片
|
||||
KindDoc = "doc" // 文档
|
||||
KindAudio = "audio" // 音频
|
||||
KindVideo = "video" // 视频
|
||||
KindZip = "zip" // 压缩包
|
||||
KindOther = "other" // 其他
|
||||
)
|
||||
|
||||
var KindSlice = []string{KindImg, KindDoc, KindAudio, KindVideo, KindZip, KindOther}
|
||||
|
||||
var (
|
||||
// 图片类型
|
||||
imgType = g.MapStrStr{
|
||||
"jpeg": "image/jpeg",
|
||||
"jpg": "image/jpeg",
|
||||
"png": "image/png",
|
||||
"gif": "image/gif",
|
||||
"webp": "image/webp",
|
||||
"cr2": "image/x-canon-cr2",
|
||||
"tif": "image/tiff",
|
||||
"bmp": "image/bmp",
|
||||
"heif": "image/heif",
|
||||
"jxr": "image/vnd.ms-photo",
|
||||
"psd": "image/vnd.adobe.photoshop",
|
||||
"ico": "image/vnd.microsoft.icon",
|
||||
"dwg": "image/vnd.dwg",
|
||||
}
|
||||
|
||||
// 文档类型
|
||||
docType = g.MapStrStr{
|
||||
"doc": "application/msword",
|
||||
"docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
"dot": "application/msword",
|
||||
"xls": "application/vnd.ms-excel",
|
||||
"xlt": "application/vnd.ms-excel",
|
||||
"xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
"xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
|
||||
"ppt": "application/vnd.ms-powerpoint",
|
||||
"pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||
"pdf": "application/pdf",
|
||||
"txt": "text/plain",
|
||||
"csv": "text/csv",
|
||||
"html": "text/html",
|
||||
"xml": "text/xml",
|
||||
"pptm": "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
|
||||
"pot": "application/vnd.ms-powerpoint",
|
||||
"wpd": "application/wordperfect",
|
||||
"md": "text/markdown",
|
||||
"json": "application/json",
|
||||
"yaml": "application/x-yaml",
|
||||
"markdown": "text/markdown",
|
||||
"asciidoc": "text/asciidoc",
|
||||
"xsl": "application/xml",
|
||||
"wps": "application/vnd.ms-works",
|
||||
"sxi": "application/vnd.sun.xml.impress",
|
||||
"sti": "application/vnd.sun.xml.impress.template",
|
||||
"odp": "application/vnd.oasis.opendocument.presentation-template",
|
||||
}
|
||||
|
||||
// 音频类型
|
||||
audioType = g.MapStrStr{
|
||||
"mid": "audio/midi",
|
||||
"mp3": "audio/mpeg",
|
||||
"m4a": "audio/mp4",
|
||||
"ogg": "audio/ogg",
|
||||
"flac": "audio/x-flac",
|
||||
"wav": "audio/x-wav",
|
||||
"amr": "audio/amr",
|
||||
"aac": "audio/aac",
|
||||
"aiff": "audio/x-aiff",
|
||||
}
|
||||
|
||||
// 视频类型
|
||||
videoType = g.MapStrStr{
|
||||
"mp4": "video/mp4",
|
||||
"m4v": "video/x-m4v",
|
||||
"mkv": "video/x-matroska",
|
||||
"webm": "video/webm",
|
||||
"mov": "video/quicktime",
|
||||
"avi": "video/x-msvideo",
|
||||
"wmv": "video/x-ms-wmv",
|
||||
"mpg": "video/mpeg",
|
||||
"flv": "video/x-flv",
|
||||
"3gp": "video/3gpp",
|
||||
}
|
||||
|
||||
// 压缩包
|
||||
zipType = g.MapStrStr{
|
||||
"zip": "application/zip",
|
||||
"rar": "application/x-rar-compressed",
|
||||
"tar": "application/x-tar",
|
||||
"gz": "application/gzip",
|
||||
"7z": "application/octet-stream",
|
||||
"tar.gz": "application/octet-stream",
|
||||
}
|
||||
|
||||
// 其他
|
||||
otherType = g.MapStrStr{
|
||||
"dwf": "model/vnd.dwf",
|
||||
"ics": "text/calendar",
|
||||
"vcard": "text/vcard",
|
||||
"apk": "application/vnd.android.package-archive",
|
||||
"ipa": "application/octet-stream",
|
||||
}
|
||||
)
|
||||
|
||||
// IsImgType 判断是否为图片
|
||||
func IsImgType(ext string) bool {
|
||||
_, ok := imgType[ext]
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsDocType 判断是否为文档
|
||||
func IsDocType(ext string) bool {
|
||||
_, ok := docType[ext]
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsAudioType 判断是否为音频
|
||||
func IsAudioType(ext string) bool {
|
||||
_, ok := audioType[ext]
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsVideoType 判断是否为视频
|
||||
func IsVideoType(ext string) bool {
|
||||
_, ok := videoType[ext]
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsZipType 判断是否为压缩包
|
||||
func IsZipType(ext string) bool {
|
||||
_, ok := zipType[ext]
|
||||
return ok
|
||||
}
|
||||
|
||||
// GetFileMimeType 获取文件扩展类型
|
||||
// 如果文件类型没有加入系统映射类型,默认认为不是合法的文件类型。建议将常用的上传文件类型加入映射关系。
|
||||
// 当然你也可以不做限制,可以上传任意文件。但需要谨慎处理和设置相应的安全措施。
|
||||
// 获取任意扩展名的扩展类型:mime.TypeByExtension(".xls")
|
||||
func GetFileMimeType(ext string) (string, error) {
|
||||
if mime, ok := imgType[ext]; ok {
|
||||
return mime, nil
|
||||
}
|
||||
if mime, ok := docType[ext]; ok {
|
||||
return mime, nil
|
||||
}
|
||||
if mime, ok := audioType[ext]; ok {
|
||||
return mime, nil
|
||||
}
|
||||
if mime, ok := videoType[ext]; ok {
|
||||
return mime, nil
|
||||
}
|
||||
if mime, ok := zipType[ext]; ok {
|
||||
return mime, nil
|
||||
}
|
||||
if mime, ok := otherType[ext]; ok {
|
||||
return mime, nil
|
||||
}
|
||||
return "", gerror.Newf("Invalid file type:%v", ext)
|
||||
}
|
||||
|
||||
// GetFileKind 获取文件上传类型
|
||||
func GetFileKind(ext string) string {
|
||||
if _, ok := imgType[ext]; ok {
|
||||
return KindImg
|
||||
}
|
||||
if _, ok := docType[ext]; ok {
|
||||
return KindDoc
|
||||
}
|
||||
if _, ok := audioType[ext]; ok {
|
||||
return KindAudio
|
||||
}
|
||||
if _, ok := videoType[ext]; ok {
|
||||
return KindVideo
|
||||
}
|
||||
if _, ok := zipType[ext]; ok {
|
||||
return KindZip
|
||||
}
|
||||
return KindOther
|
||||
}
|
||||
|
||||
// Ext 获取文件后缀
|
||||
func Ext(baseName string) string {
|
||||
return gstr.ToLower(gstr.StrEx(path.Ext(baseName), "."))
|
||||
}
|
||||
|
||||
// UploadFileByte 获取上传文件的byte
|
||||
func UploadFileByte(file *ghttp.UploadFile) ([]byte, error) {
|
||||
open, err := file.Open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return io.ReadAll(open)
|
||||
}
|
||||
|
||||
// CalcFileMd5 计算文件md5值
|
||||
func CalcFileMd5(file *ghttp.UploadFile) (string, error) {
|
||||
f, err := file.Open()
|
||||
if err != nil {
|
||||
err = gerror.Wrapf(err, `os.Open failed for name "%s"`, file.Filename)
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
h := md5.New()
|
||||
_, err = io.Copy(h, f)
|
||||
if err != nil {
|
||||
err = gerror.Wrap(err, `io.Copy failed`)
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%x", h.Sum(nil)), nil
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Package storager
|
||||
// @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 storager
|
||||
|
||||
// FileMeta 文件元数据
|
||||
type FileMeta struct {
|
||||
Filename string `json:"filename"` // 文件名称
|
||||
Size int64 `json:"size"` // 文件大小
|
||||
Kind string `json:"kind"` // 文件上传类型
|
||||
MimeType string `json:"mimeType"` // 文件扩展类型
|
||||
NaiveType string `json:"naiveType"` // NaiveUI类型
|
||||
Ext string `json:"ext"` // 文件扩展名
|
||||
Md5 string `json:"md5"` // 文件hash
|
||||
}
|
|
@ -0,0 +1,236 @@
|
|||
// Package storager
|
||||
// @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 storager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/library/contexts"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/utility/url"
|
||||
"hotgo/utility/validate"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// UploadDrive 存储驱动
|
||||
type UploadDrive interface {
|
||||
// Upload 上传
|
||||
Upload(ctx context.Context, file *ghttp.UploadFile) (fullPath string, err error)
|
||||
}
|
||||
|
||||
// New 初始化存储驱动
|
||||
func New(name ...string) UploadDrive {
|
||||
var (
|
||||
driveType = consts.UploadDriveLocal
|
||||
drive UploadDrive
|
||||
)
|
||||
|
||||
if len(name) > 0 && name[0] != "" {
|
||||
driveType = name[0]
|
||||
}
|
||||
|
||||
switch driveType {
|
||||
case consts.UploadDriveLocal:
|
||||
drive = &LocalDrive{}
|
||||
default:
|
||||
panic(fmt.Sprintf("暂不支持的存储驱动:%v", driveType))
|
||||
}
|
||||
return drive
|
||||
}
|
||||
|
||||
// DoUpload 上传入口
|
||||
func DoUpload(ctx context.Context, typ string, file *ghttp.UploadFile) (result *entity.AdminAttachment, err error) {
|
||||
if file == nil {
|
||||
err = gerror.New("文件必须!")
|
||||
return
|
||||
}
|
||||
|
||||
meta, err := GetFileMeta(file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = ValidateFileMeta(typ, meta); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result, err = HasFile(ctx, meta.Md5)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 相同存储相同身份才复用
|
||||
if result != nil && result.Drive == config.Drive && result.MemberId == contexts.GetUserId(ctx) && result.AppId == contexts.GetModule(ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
// 上传到驱动
|
||||
fullPath, err := New(config.Drive).Upload(ctx, file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 写入附件记录
|
||||
return write(ctx, meta, fullPath)
|
||||
}
|
||||
|
||||
// ValidateFileMeta 验证文件元数据
|
||||
func ValidateFileMeta(typ string, meta *FileMeta) (err error) {
|
||||
if _, err = GetFileMimeType(meta.Ext); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch typ {
|
||||
case KindImg:
|
||||
if !IsImgType(meta.Ext) {
|
||||
err = gerror.New("上传的文件不是图片")
|
||||
return
|
||||
}
|
||||
if config.ImageSize > 0 && meta.Size > config.ImageSize*1024*1024 {
|
||||
err = gerror.Newf("图片大小不能超过%vMB", config.ImageSize)
|
||||
return
|
||||
}
|
||||
|
||||
if len(config.ImageType) > 0 && !validate.InSlice(strings.Split(config.ImageType, `,`), meta.Ext) {
|
||||
err = gerror.New("上传图片类型未经允许")
|
||||
return
|
||||
}
|
||||
case KindDoc:
|
||||
if !IsDocType(meta.Ext) {
|
||||
err = gerror.New("上传的文件不是文档")
|
||||
return
|
||||
}
|
||||
case KindAudio:
|
||||
if !IsAudioType(meta.Ext) {
|
||||
err = gerror.New("上传的文件不是音频")
|
||||
return
|
||||
}
|
||||
case KindVideo:
|
||||
if !IsVideoType(meta.Ext) {
|
||||
err = gerror.New("上传的文件不是视频")
|
||||
return
|
||||
}
|
||||
case KindZip:
|
||||
if !IsZipType(meta.Ext) {
|
||||
err = gerror.New("上传的文件不是压缩文件")
|
||||
return
|
||||
}
|
||||
case KindOther:
|
||||
fallthrough
|
||||
default:
|
||||
// 默认为通用的文件上传
|
||||
if config.FileSize > 0 && meta.Size > config.FileSize*1024*1024 {
|
||||
err = gerror.Newf("文件大小不能超过%vMB", config.FileSize)
|
||||
return
|
||||
}
|
||||
|
||||
if len(config.FileType) > 0 && !validate.InSlice(strings.Split(config.FileType, `,`), meta.Ext) {
|
||||
err = gerror.New("上传文件类型未经允许")
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// LastUrl 根据驱动获取最终文件访问地址
|
||||
func LastUrl(ctx context.Context, fullPath, drive string) string {
|
||||
if validate.IsURL(fullPath) {
|
||||
return fullPath
|
||||
}
|
||||
|
||||
switch drive {
|
||||
case consts.UploadDriveLocal:
|
||||
return url.GetAddr(ctx) + "/" + fullPath
|
||||
default:
|
||||
return fullPath
|
||||
}
|
||||
}
|
||||
|
||||
// GetFileMeta 获取上传文件元数据
|
||||
func GetFileMeta(file *ghttp.UploadFile) (meta *FileMeta, err error) {
|
||||
meta = new(FileMeta)
|
||||
meta.Filename = file.Filename
|
||||
meta.Size = file.Size
|
||||
meta.Ext = Ext(file.Filename)
|
||||
meta.Kind = GetFileKind(meta.Ext)
|
||||
meta.MimeType, err = GetFileMimeType(meta.Ext)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 兼容naiveUI
|
||||
naiveType := "text/plain"
|
||||
if IsImgType(Ext(file.Filename)) {
|
||||
naiveType = ""
|
||||
}
|
||||
meta.NaiveType = naiveType
|
||||
|
||||
// 计算md5值
|
||||
meta.Md5, err = CalcFileMd5(file)
|
||||
return
|
||||
}
|
||||
|
||||
// GenFullPath 根据目录和文件类型生成一个绝对地址
|
||||
func GenFullPath(basePath, ext string) string {
|
||||
fileName := strconv.FormatInt(gtime.TimestampNano(), 36) + grand.S(6)
|
||||
fileName = fileName + ext
|
||||
return basePath + gtime.Date() + "/" + strings.ToLower(fileName)
|
||||
}
|
||||
|
||||
// write 写入附件记录
|
||||
func write(ctx context.Context, meta *FileMeta, fullPath string) (models *entity.AdminAttachment, err error) {
|
||||
models = &entity.AdminAttachment{
|
||||
Id: 0,
|
||||
AppId: contexts.GetModule(ctx),
|
||||
MemberId: contexts.GetUserId(ctx),
|
||||
Drive: config.Drive,
|
||||
Size: meta.Size,
|
||||
Path: fullPath,
|
||||
FileUrl: fullPath,
|
||||
Name: meta.Filename,
|
||||
Kind: meta.Kind,
|
||||
MimeType: meta.MimeType,
|
||||
NaiveType: meta.NaiveType,
|
||||
Ext: meta.Ext,
|
||||
Md5: meta.Md5,
|
||||
Status: consts.StatusEnabled,
|
||||
}
|
||||
|
||||
id, err := GetModel(ctx).Data(models).InsertAndGetId()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
models.Id = id
|
||||
return
|
||||
}
|
||||
|
||||
// HasFile 检查附件是否存在
|
||||
func HasFile(ctx context.Context, md5 string) (res *entity.AdminAttachment, err error) {
|
||||
if err = GetModel(ctx).Where("md5", md5).Scan(&res); err != nil {
|
||||
err = gerror.Wrap(err, "检查文件hash时出现错误")
|
||||
return
|
||||
}
|
||||
|
||||
if res == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 只有在上传时才会检查md5值,如果附件存在则更新最后上传时间,保证上传列表更新显示在最前面
|
||||
if res.Id > 0 {
|
||||
update := g.Map{
|
||||
"status": consts.StatusEnabled,
|
||||
"updated_at": gtime.Now(),
|
||||
}
|
||||
_, _ = GetModel(ctx).WherePri(res.Id).Data(update).Update()
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// Package storager
|
||||
// @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 storager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// LocalDrive 本地驱动
|
||||
type LocalDrive struct {
|
||||
}
|
||||
|
||||
// Upload 上传到本地
|
||||
func (d *LocalDrive) Upload(ctx context.Context, file *ghttp.UploadFile) (fullPath string, err error) {
|
||||
var (
|
||||
sp = g.Cfg().MustGet(ctx, "server.serverRoot")
|
||||
nowDate = gtime.Date()
|
||||
)
|
||||
|
||||
if sp.IsEmpty() {
|
||||
err = gerror.New("本地上传驱动必须配置静态路径!")
|
||||
return
|
||||
}
|
||||
|
||||
if config.LocalPath == "" {
|
||||
err = gerror.New("本地上传驱动必须配置本地存储路径!")
|
||||
return
|
||||
}
|
||||
|
||||
// 包含静态文件夹的路径
|
||||
fullDirPath := strings.Trim(sp.String(), "/") + "/" + config.LocalPath + nowDate
|
||||
fileName, err := file.Save(fullDirPath, true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 不含静态文件夹的路径
|
||||
fullPath = config.LocalPath + nowDate + "/" + fileName
|
||||
return
|
||||
}
|
|
@ -0,0 +1,313 @@
|
|||
// Package token
|
||||
// @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 token
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/crypto/gmd5"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/library/cache"
|
||||
"hotgo/internal/library/contexts"
|
||||
"hotgo/internal/model"
|
||||
"hotgo/utility/simple"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Claims struct {
|
||||
*model.Identity
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
type Token struct {
|
||||
ExpireAt int64 `json:"exp"` // token过期时间
|
||||
RefreshAt int64 `json:"ra"` // 刷新时间
|
||||
RefreshCount int64 `json:"rc"` // 刷新次数
|
||||
}
|
||||
|
||||
var (
|
||||
config *model.TokenConfig
|
||||
errorLogin = gerror.New("登录身份已失效,请重新登录!")
|
||||
errorMultiLogin = gerror.New("账号已在其他地方登录,如非本人操作请及时修改登录密码!")
|
||||
)
|
||||
|
||||
func InitConfig(ctx context.Context) {
|
||||
err := g.Cfg().MustGet(ctx, "token").Scan(&config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
panic("没有找到token配置")
|
||||
}
|
||||
}
|
||||
|
||||
// Login 登录
|
||||
func Login(ctx context.Context, user *model.Identity) (string, int64, error) {
|
||||
claims := Claims{
|
||||
user,
|
||||
jwt.RegisteredClaims{},
|
||||
}
|
||||
|
||||
header, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(config.SecretKey))
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
var (
|
||||
now = gtime.Now()
|
||||
// 认证key
|
||||
authKey = GetAuthKey(header)
|
||||
// 登录token
|
||||
tokenKey = GetTokenKey(user.App, authKey)
|
||||
// 身份绑定
|
||||
bindKey = GetBindKey(user.App, user.Id)
|
||||
// 有效时长
|
||||
duration = time.Second * gconv.Duration(config.Expires)
|
||||
)
|
||||
|
||||
token := &Token{
|
||||
ExpireAt: now.Unix() + config.Expires,
|
||||
RefreshAt: now.Unix(),
|
||||
RefreshCount: 0,
|
||||
}
|
||||
|
||||
if err = cache.Instance().Set(ctx, tokenKey, token, duration); err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
if err = cache.Instance().Set(ctx, bindKey, tokenKey, duration); err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
return header, config.Expires, nil
|
||||
}
|
||||
|
||||
// Logout 注销登录
|
||||
func Logout(r *ghttp.Request) (err error) {
|
||||
var (
|
||||
ctx = r.Context()
|
||||
header = GetAuthorization(r)
|
||||
)
|
||||
|
||||
if header == "" {
|
||||
err = errorLogin
|
||||
return
|
||||
}
|
||||
|
||||
claims, err := parseToken(ctx, header)
|
||||
if err != nil {
|
||||
g.Log().Debugf(ctx, "logout parseToken err:%+v", err)
|
||||
err = errorLogin
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
// 认证key
|
||||
authKey = GetAuthKey(header)
|
||||
// 登录token
|
||||
tokenKey = GetTokenKey(contexts.GetModule(ctx), authKey)
|
||||
// 身份绑定
|
||||
bindKey = GetBindKey(contexts.GetModule(ctx), claims.Id)
|
||||
)
|
||||
|
||||
// 删除token
|
||||
if _, err = cache.Instance().Remove(ctx, tokenKey); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !config.MultiLogin {
|
||||
if _, err = cache.Instance().Remove(ctx, bindKey); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ParseLoginUser 解析登录用户信息
|
||||
func ParseLoginUser(r *ghttp.Request) (user *model.Identity, err error) {
|
||||
var (
|
||||
ctx = r.Context()
|
||||
header = GetAuthorization(r)
|
||||
)
|
||||
|
||||
if header == "" {
|
||||
g.Log().Debugf(ctx, "ParseLoginUser header:%v", header)
|
||||
err = errorLogin
|
||||
return
|
||||
}
|
||||
|
||||
claims, err := parseToken(ctx, header)
|
||||
if err != nil {
|
||||
g.Log().Debugf(ctx, "parseToken err:%+v", err)
|
||||
err = errorLogin
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
// 认证key
|
||||
authKey = GetAuthKey(header)
|
||||
// 登录token
|
||||
tokenKey = GetTokenKey(claims.App, authKey)
|
||||
// 身份绑定
|
||||
bindKey = GetBindKey(claims.App, claims.Id)
|
||||
)
|
||||
|
||||
// 检查token是否存在
|
||||
tk, err := cache.Instance().Get(ctx, tokenKey)
|
||||
if err != nil {
|
||||
g.Log().Debugf(ctx, "get tokenKey err:%+v", err)
|
||||
err = errorLogin
|
||||
return
|
||||
}
|
||||
|
||||
if tk.IsEmpty() {
|
||||
g.Log().Debug(ctx, "token isEmpty")
|
||||
err = errorLogin
|
||||
return
|
||||
}
|
||||
|
||||
var token *Token
|
||||
if err = tk.Scan(&token); err != nil {
|
||||
g.Log().Debugf(ctx, "token scan err:%+v", err)
|
||||
err = errorLogin
|
||||
return
|
||||
}
|
||||
|
||||
if token == nil {
|
||||
g.Log().Debugf(ctx, "token = nil")
|
||||
err = errorLogin
|
||||
return
|
||||
}
|
||||
|
||||
now := gtime.Now()
|
||||
if token.ExpireAt < now.Unix() {
|
||||
g.Log().Debugf(ctx, "token expired.")
|
||||
err = errorLogin
|
||||
return
|
||||
}
|
||||
|
||||
// 是否允许多端登录
|
||||
if !config.MultiLogin {
|
||||
origin, err := cache.Instance().Get(ctx, bindKey)
|
||||
if err != nil {
|
||||
g.Log().Debugf(ctx, "bindKey get err:%+v", err)
|
||||
err = errorLogin
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if origin == nil || origin.IsEmpty() {
|
||||
g.Log().Debug(ctx, "bindKey isEmpty")
|
||||
err = errorLogin
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if tokenKey != origin.String() {
|
||||
g.Log().Debugf(ctx, "bindKey offsite login tokenKey:%v, origin:%v", tokenKey, origin.String())
|
||||
err = errorMultiLogin
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 自动刷新token有效期
|
||||
refreshToken := func() {
|
||||
// 未开启自动刷新
|
||||
if !config.AutoRefresh {
|
||||
return
|
||||
}
|
||||
|
||||
// 刷新次数已达上限
|
||||
if config.MaxRefreshTimes != -1 && token.RefreshCount >= config.MaxRefreshTimes {
|
||||
return
|
||||
}
|
||||
|
||||
// 未达到刷新间隔
|
||||
if gtime.New(token.RefreshAt).Unix()+config.RefreshInterval > now.Unix() {
|
||||
return
|
||||
}
|
||||
|
||||
// 刷新有效期
|
||||
token.ExpireAt = now.Unix() + config.Expires
|
||||
token.RefreshAt = now.Unix()
|
||||
token.RefreshCount += 1
|
||||
|
||||
duration := time.Second * gconv.Duration(config.Expires)
|
||||
|
||||
if err = cache.Instance().Set(ctx, tokenKey, token, duration); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = cache.Instance().Set(ctx, bindKey, tokenKey, duration); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
simple.SafeGo(ctx, func(ctx context.Context) {
|
||||
refreshToken()
|
||||
})
|
||||
|
||||
user = claims.Identity
|
||||
return
|
||||
}
|
||||
|
||||
// parseToken 解析jwt令牌
|
||||
func parseToken(ctx context.Context, header string) (*Claims, error) {
|
||||
token, err := jwt.ParseWithClaims(header, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(config.SecretKey), nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
g.Log().Debugf(ctx, "parseToken err:%+v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !token.Valid {
|
||||
return nil, errorLogin
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(*Claims)
|
||||
if !ok {
|
||||
return nil, errorLogin
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
// GetAuthorization 获取authorization
|
||||
func GetAuthorization(r *ghttp.Request) string {
|
||||
// 默认从请求头获取
|
||||
var authorization = r.Header.Get("Authorization")
|
||||
|
||||
if authorization == "" {
|
||||
authorization = r.Cookie.Get("Authorization").String()
|
||||
}
|
||||
|
||||
// 如果请求头不存在则从get参数获取
|
||||
if authorization == "" {
|
||||
return r.Get("authorization").String()
|
||||
}
|
||||
return gstr.Replace(authorization, "Bearer ", "")
|
||||
}
|
||||
|
||||
// GetAuthKey 认证key
|
||||
func GetAuthKey(token string) string {
|
||||
return gmd5.MustEncryptString("hotgo" + token)
|
||||
}
|
||||
|
||||
// GetTokenKey 令牌缓存key
|
||||
func GetTokenKey(appName, authKey string) string {
|
||||
return fmt.Sprintf("%v:%v:%v", consts.CacheToken, appName, authKey)
|
||||
}
|
||||
|
||||
// GetBindKey 令牌身份绑定key
|
||||
func GetBindKey(appName string, userId int64) string {
|
||||
return fmt.Sprintf("%v:%v:%v", consts.CacheTokenBind, appName, userId)
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
// Package admin
|
||||
// @Description
|
||||
// @Author Ms <133814250@qq.com>
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/contexts"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/adminin"
|
||||
"hotgo/internal/service"
|
||||
)
|
||||
|
||||
type sAdminProject struct{}
|
||||
|
||||
func NewAdminProject() *sAdminProject {
|
||||
return &sAdminProject{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterAdminProject(NewAdminProject())
|
||||
}
|
||||
|
||||
// Model ORM模型
|
||||
func (s *sAdminProject) Model(ctx context.Context) *gdb.Model {
|
||||
return dao.AdminProject.Ctx(ctx).Handler(func(m *gdb.Model) *gdb.Model {
|
||||
return m.Where(dao.AdminProject.Columns().CreatedBy, contexts.GetUserId(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
// List 查询列表
|
||||
func (s *sAdminProject) List(ctx context.Context, in *adminin.ProjectListInp) (list []*adminin.ProjectListModel, totalCount int, err error) {
|
||||
mod := s.Model(ctx)
|
||||
totalCount, err = mod.Count()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if totalCount == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if err = mod.Page(in.Page, in.PerPage).OrderDesc(dao.AdminProject.Columns().Id).Scan(&list); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range list {
|
||||
v.State = v.Status
|
||||
v.CreateUserId = v.CreatedBy
|
||||
v.CreateTime = v.CreatedAt
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete 删除
|
||||
func (s *sAdminProject) Delete(ctx context.Context, in *adminin.ProjectDeleteInp) (err error) {
|
||||
if exists, err := s.ExistById(ctx, in.Id); err != nil || !exists {
|
||||
err = gerror.New("没有找到项目!")
|
||||
return err
|
||||
}
|
||||
_, err = s.Model(ctx).WherePri(in.Id).Delete()
|
||||
return
|
||||
}
|
||||
|
||||
// Create 新增
|
||||
func (s *sAdminProject) Create(ctx context.Context, in *adminin.ProjectCreateInp) (res *adminin.ProjectCreateModel, err error) {
|
||||
data := &entity.AdminProject{
|
||||
Id: gconv.Int64(fmt.Sprintf("%v%v", gtime.Now().Format("YmdH"), grand.N(10000, 99999))),
|
||||
ProjectName: in.ProjectName,
|
||||
IndexImage: in.IndexImage,
|
||||
Status: -1,
|
||||
Remarks: in.Remarks,
|
||||
CreatedBy: contexts.GetUserId(ctx),
|
||||
}
|
||||
|
||||
if _, err = dao.AdminProject.Ctx(ctx).Data(data).Insert(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = &adminin.ProjectCreateModel{
|
||||
Id: data.Id,
|
||||
ProjectName: data.ProjectName,
|
||||
State: data.Status,
|
||||
CreateTime: gtime.Now(),
|
||||
CreateUserId: contexts.GetUserId(ctx),
|
||||
IndexImage: data.IndexImage,
|
||||
Remarks: data.Remarks,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Edit 修改
|
||||
func (s *sAdminProject) Edit(ctx context.Context, in *adminin.ProjectEditInp) (err error) {
|
||||
if exists, err := s.ExistById(ctx, in.Id); err != nil || !exists {
|
||||
err = gerror.New("没有找到项目!")
|
||||
return err
|
||||
}
|
||||
|
||||
data := make(g.Map)
|
||||
if len(in.IndexImage) > 0 {
|
||||
data["index_image"] = in.IndexImage
|
||||
}
|
||||
if len(in.ProjectName) > 0 {
|
||||
data["project_name"] = in.ProjectName
|
||||
}
|
||||
if len(in.Remarks) > 0 {
|
||||
data["remarks"] = in.Remarks
|
||||
}
|
||||
|
||||
if len(data) > 0 {
|
||||
_, err = s.Model(ctx).WherePri(in.Id).Data(data).Update()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SaveData 保存数据
|
||||
func (s *sAdminProject) SaveData(ctx context.Context, in *adminin.ProjectSaveDataInp) (err error) {
|
||||
if exists, err := s.ExistById(ctx, in.Id); err != nil || !exists {
|
||||
err = gerror.New("没有找到项目!")
|
||||
return err
|
||||
}
|
||||
_, err = s.Model(ctx).WherePri(in.Id).Data("content", in.Content).Update()
|
||||
return
|
||||
}
|
||||
|
||||
// Publish 修改发布状态
|
||||
func (s *sAdminProject) Publish(ctx context.Context, in *adminin.ProjectPublishInp) (err error) {
|
||||
if exists, err := s.ExistById(ctx, in.Id); err != nil || !exists {
|
||||
err = gerror.New("没有找到项目!")
|
||||
return err
|
||||
}
|
||||
_, err = s.Model(ctx).WherePri(in.Id).Data("status", in.State).Update()
|
||||
return
|
||||
}
|
||||
|
||||
// GetData 获取指定信息
|
||||
func (s *sAdminProject) GetData(ctx context.Context, in *adminin.ProjectGetDataInp) (res *adminin.ProjectGetDataModel, err error) {
|
||||
if err = dao.AdminProject.Ctx(ctx).WherePri(in.Id).Scan(&res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if res == nil {
|
||||
err = gerror.New("项目不存在")
|
||||
return
|
||||
}
|
||||
|
||||
res.State = res.Status
|
||||
return
|
||||
}
|
||||
|
||||
// ExistById 判断指定项目是否存在
|
||||
func (s *sAdminProject) ExistById(ctx context.Context, id int64) (exists bool, err error) {
|
||||
count, err := s.Model(ctx).WherePri(id).Count()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
exists = count > 0
|
||||
return
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/dao"
|
||||
"hotgo/internal/library/contexts"
|
||||
"hotgo/internal/library/token"
|
||||
"hotgo/internal/model"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/adminin"
|
||||
"hotgo/internal/service"
|
||||
"hotgo/utility/simple"
|
||||
)
|
||||
|
||||
type sAdminSite struct{}
|
||||
|
||||
func NewAdminSite() *sAdminSite {
|
||||
return &sAdminSite{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterAdminSite(NewAdminSite())
|
||||
}
|
||||
|
||||
// Register 账号注册
|
||||
func (s *sAdminSite) Register(ctx context.Context, in *adminin.RegisterInp) (err error) {
|
||||
err = gerror.New("暂未开放注册")
|
||||
return
|
||||
}
|
||||
|
||||
// AccountLogin 账号登录
|
||||
func (s *sAdminSite) AccountLogin(ctx context.Context, in *adminin.AccountLoginInp) (res *adminin.LoginModel, err error) {
|
||||
var mb *entity.AdminMember
|
||||
if err = dao.AdminMember.Ctx(ctx).Where("username", in.Username).Scan(&mb); err != nil {
|
||||
err = gerror.Wrap(err, consts.ErrorORM)
|
||||
return
|
||||
}
|
||||
|
||||
if mb == nil {
|
||||
err = gerror.New("账号不存在")
|
||||
return
|
||||
}
|
||||
|
||||
if mb.Salt == "" {
|
||||
err = gerror.New("用户信息错误")
|
||||
return
|
||||
}
|
||||
|
||||
if err = simple.CheckPassword(in.Password, mb.Salt, mb.PasswordHash); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if mb.Status != consts.StatusEnabled {
|
||||
err = gerror.New("账号已被禁用")
|
||||
return
|
||||
}
|
||||
|
||||
res, err = s.handleLogin(ctx, mb)
|
||||
return
|
||||
}
|
||||
|
||||
// handleLogin .
|
||||
func (s *sAdminSite) handleLogin(ctx context.Context, mb *entity.AdminMember) (res *adminin.LoginModel, err error) {
|
||||
user := &model.Identity{
|
||||
Id: mb.Id,
|
||||
Username: mb.Username,
|
||||
Nickname: mb.Nickname,
|
||||
Avatar: mb.Avatar,
|
||||
App: consts.AppAdmin,
|
||||
LoginAt: gtime.Now(),
|
||||
}
|
||||
|
||||
tk, expires, err := token.Login(ctx, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = &adminin.LoginModel{
|
||||
UserInfo: &adminin.LoginUserInfo{
|
||||
Id: user.Id,
|
||||
Username: user.Username,
|
||||
Nickname: user.Nickname,
|
||||
},
|
||||
Token: &adminin.LoginToken{
|
||||
TokenName: "Authorization",
|
||||
TokenValue: tk,
|
||||
Expires: expires,
|
||||
},
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// BindUserContext 绑定用户上下文
|
||||
func (s *sAdminSite) BindUserContext(ctx context.Context, claims *model.Identity) (err error) {
|
||||
var mb *entity.AdminMember
|
||||
if err = dao.AdminMember.Ctx(ctx).Where("id", claims.Id).Scan(&mb); err != nil {
|
||||
err = gerror.Wrap(err, "获取用户信息失败,请稍后重试!")
|
||||
return
|
||||
}
|
||||
|
||||
if mb == nil {
|
||||
err = gerror.Wrap(err, "账号不存在或已被删除!")
|
||||
return
|
||||
}
|
||||
|
||||
if mb.Status != consts.StatusEnabled {
|
||||
err = gerror.New("账号已被禁用,如有疑问请联系管理员")
|
||||
return
|
||||
}
|
||||
|
||||
user := &model.Identity{
|
||||
Id: mb.Id,
|
||||
Username: mb.Username,
|
||||
Nickname: mb.Nickname,
|
||||
Avatar: mb.Avatar,
|
||||
App: claims.App,
|
||||
LoginAt: claims.LoginAt,
|
||||
}
|
||||
|
||||
contexts.SetUser(ctx, user)
|
||||
return
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
// ==========================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// ==========================================================================
|
||||
|
||||
package logic
|
||||
|
||||
import (
|
||||
_ "hotgo/internal/logic/admin"
|
||||
_ "hotgo/internal/logic/middleware"
|
||||
)
|
|
@ -0,0 +1,35 @@
|
|||
// Package middleware
|
||||
// @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 middleware
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/library/response"
|
||||
"hotgo/utility/simple"
|
||||
)
|
||||
|
||||
// AdminAuth 后台鉴权中间件
|
||||
func (s *sMiddleware) AdminAuth(r *ghttp.Request) {
|
||||
var (
|
||||
ctx = r.Context()
|
||||
path = gstr.Replace(r.URL.Path, simple.RouterPrefix(ctx, consts.AppAdmin), "", 1)
|
||||
)
|
||||
|
||||
// 不需要验证登录的路由地址
|
||||
if s.IsExceptLogin(ctx, consts.AppAdmin, path) {
|
||||
r.Middleware.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// 将用户信息传递到上下文中
|
||||
if err := s.DeliverUserContext(r); err != nil {
|
||||
response.JsonExit(r, consts.ApiStatusTokenOverdue, err.Error())
|
||||
return
|
||||
}
|
||||
r.Middleware.Next()
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
// Package middleware
|
||||
// @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 middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/library/contexts"
|
||||
"hotgo/internal/library/token"
|
||||
"hotgo/internal/model"
|
||||
"hotgo/internal/service"
|
||||
"hotgo/utility/validate"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type sMiddleware struct {
|
||||
LoginUrl string // 登录路由地址
|
||||
NotRecordRequest g.Map // 不记录请求数据的路由(当前请求数据过大时会影响响应效率,可以将路径放到该选项中改善)
|
||||
FilterRoutes map[string]ghttp.RouterItem // 支持预处理的web路由
|
||||
routeMutex sync.Mutex
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterMiddleware(NewMiddleware())
|
||||
}
|
||||
|
||||
func NewMiddleware() *sMiddleware {
|
||||
return &sMiddleware{
|
||||
NotRecordRequest: g.Map{
|
||||
"/api/goview/project/upload": struct{}{}, // 上传文件
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Ctx 初始化请求上下文
|
||||
func (s *sMiddleware) Ctx(r *ghttp.Request) {
|
||||
data := make(g.Map)
|
||||
if _, ok := s.NotRecordRequest[r.URL.Path]; ok {
|
||||
data["request.body"] = gjson.New(nil)
|
||||
} else {
|
||||
data["request.body"] = gjson.New(r.GetBodyString())
|
||||
}
|
||||
|
||||
contexts.Init(r, &model.Context{
|
||||
Data: data,
|
||||
Module: getModule(r.URL.Path),
|
||||
})
|
||||
|
||||
if len(r.Cookie.GetSessionId()) == 0 {
|
||||
r.Cookie.SetSessionId(gctx.CtxId(r.Context()))
|
||||
}
|
||||
r.Middleware.Next()
|
||||
}
|
||||
|
||||
func getModule(path string) (module string) {
|
||||
slice := strings.Split(path, "/")
|
||||
if len(slice) < 2 {
|
||||
module = consts.AppDefault
|
||||
return
|
||||
}
|
||||
|
||||
if slice[1] == "" {
|
||||
module = consts.AppDefault
|
||||
return
|
||||
}
|
||||
return slice[1]
|
||||
}
|
||||
|
||||
// CORS allows Cross-origin resource sharing.
|
||||
func (s *sMiddleware) CORS(r *ghttp.Request) {
|
||||
r.Response.CORSDefault()
|
||||
r.Middleware.Next()
|
||||
}
|
||||
|
||||
// DeliverUserContext 将用户信息传递到上下文中
|
||||
func (s *sMiddleware) DeliverUserContext(r *ghttp.Request) (err error) {
|
||||
user, err := token.ParseLoginUser(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch user.App {
|
||||
case consts.AppAdmin:
|
||||
if err = service.AdminSite().BindUserContext(r.Context(), user); err != nil {
|
||||
return
|
||||
}
|
||||
default:
|
||||
contexts.SetUser(r.Context(), user)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// IsExceptAuth 是否是不需要验证权限的路由地址
|
||||
func (s *sMiddleware) IsExceptAuth(ctx context.Context, appName, path string) bool {
|
||||
pathList := g.Cfg().MustGet(ctx, fmt.Sprintf("router.%v.exceptAuth", appName)).Strings()
|
||||
|
||||
for i := 0; i < len(pathList); i++ {
|
||||
if validate.InSliceExistStr(pathList[i], path) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsExceptLogin 是否是不需要登录的路由地址
|
||||
func (s *sMiddleware) IsExceptLogin(ctx context.Context, appName, path string) bool {
|
||||
pathList := g.Cfg().MustGet(ctx, fmt.Sprintf("router.%v.exceptLogin", appName)).Strings()
|
||||
for i := 0; i < len(pathList); i++ {
|
||||
if validate.InSliceExistStr(pathList[i], path) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"hotgo/internal/library/response"
|
||||
"hotgo/utility/validate"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// GetFilterRoutes 获取支持预处理的web路由
|
||||
func (s *sMiddleware) GetFilterRoutes(r *ghttp.Request) map[string]ghttp.RouterItem {
|
||||
// 首次访问时加载
|
||||
if s.FilterRoutes == nil {
|
||||
s.routeMutex.Lock()
|
||||
defer s.routeMutex.Unlock()
|
||||
|
||||
if s.FilterRoutes != nil {
|
||||
return s.FilterRoutes
|
||||
}
|
||||
|
||||
s.FilterRoutes = make(map[string]ghttp.RouterItem)
|
||||
for _, v := range r.Server.GetRoutes() {
|
||||
// 非规范路由不加载
|
||||
if v.Handler.Info.Type.NumIn() != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := s.GenFilterRouteKey(v.Handler.Router)
|
||||
if _, ok := s.FilterRoutes[key]; !ok {
|
||||
s.FilterRoutes[key] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
return s.FilterRoutes
|
||||
}
|
||||
|
||||
// GenFilterRequestKey 根据请求生成唯一key
|
||||
func (s *sMiddleware) GenFilterRequestKey(r *ghttp.Request) string {
|
||||
return s.GenRouteKey(r.Method, r.Request.URL.Path)
|
||||
}
|
||||
|
||||
// GenFilterRouteKey 根据路由生成唯一key
|
||||
func (s *sMiddleware) GenFilterRouteKey(r *ghttp.Router) string {
|
||||
return s.GenRouteKey(r.Method, r.Uri)
|
||||
}
|
||||
|
||||
// GenRouteKey 生成唯一key
|
||||
func (s *sMiddleware) GenRouteKey(method, path string) string {
|
||||
return method + " " + path
|
||||
}
|
||||
|
||||
// PreFilter 请求输入预处理
|
||||
// api使用gf规范路由并且XxxReq结构体实现了validate.Filter接口即可
|
||||
func (s *sMiddleware) PreFilter(r *ghttp.Request) {
|
||||
router, ok := s.GetFilterRoutes(r)[s.GenFilterRequestKey(r)]
|
||||
if !ok {
|
||||
r.Middleware.Next()
|
||||
return
|
||||
}
|
||||
|
||||
funcInfo := router.Handler.Info
|
||||
|
||||
// 非规范路由不处理
|
||||
if funcInfo.Type.NumIn() != 2 {
|
||||
r.Middleware.Next()
|
||||
return
|
||||
}
|
||||
|
||||
inputType := funcInfo.Type.In(1)
|
||||
var inputObject reflect.Value
|
||||
if inputType.Kind() == reflect.Ptr {
|
||||
inputObject = reflect.New(inputType.Elem())
|
||||
} else {
|
||||
inputObject = reflect.New(inputType.Elem()).Elem()
|
||||
}
|
||||
|
||||
// 先验证基本校验规则
|
||||
if err := r.Parse(inputObject.Interface()); err != nil {
|
||||
resp := gerror.Code(err)
|
||||
response.JsonExit(r, resp.Code(), gerror.Current(err).Error(), resp.Detail())
|
||||
return
|
||||
}
|
||||
|
||||
// 没有实现预处理
|
||||
if _, ok = inputObject.Interface().(validate.Filter); !ok {
|
||||
r.Middleware.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// 执行预处理
|
||||
if err := validate.PreFilter(r.Context(), inputObject.Interface()); err != nil {
|
||||
resp := gerror.Code(err)
|
||||
response.JsonExit(r, resp.Code(), gerror.Current(err).Error(), resp.Detail())
|
||||
return
|
||||
}
|
||||
|
||||
// 过滤后的参数
|
||||
r.SetParamMap(gconv.Map(inputObject.Interface()))
|
||||
r.Middleware.Next()
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
// Package middleware
|
||||
// @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 middleware
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/util/gmeta"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/library/contexts"
|
||||
"hotgo/internal/library/response"
|
||||
"hotgo/utility/charset"
|
||||
"hotgo/utility/simple"
|
||||
)
|
||||
|
||||
// ResponseHandler HTTP响应预处理
|
||||
func (s *sMiddleware) ResponseHandler(r *ghttp.Request) {
|
||||
r.Middleware.Next()
|
||||
|
||||
// 错误状态码接管
|
||||
switch r.Response.Status {
|
||||
case 403:
|
||||
r.Response.Writeln("403 - 网站拒绝显示此网页")
|
||||
return
|
||||
case 404:
|
||||
r.Response.Writeln("404 - 你似乎来到了没有知识存在的荒原…")
|
||||
return
|
||||
}
|
||||
|
||||
contentType := getContentType(r)
|
||||
// 已存在响应
|
||||
if contentType != consts.HTTPContentTypeStream && r.Response.BufferLength() > 0 && contexts.Get(r.Context()).Response != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch contentType {
|
||||
case consts.HTTPContentTypeHtml:
|
||||
s.responseHtml(r)
|
||||
return
|
||||
case consts.HTTPContentTypeXml:
|
||||
s.responseXml(r)
|
||||
return
|
||||
case consts.HTTPContentTypeStream:
|
||||
default:
|
||||
responseJson(r)
|
||||
}
|
||||
}
|
||||
|
||||
// responseHtml html模板响应
|
||||
func (s *sMiddleware) responseHtml(r *ghttp.Request) {
|
||||
code, message, resp := parseResponse(r)
|
||||
if code == gcode.CodeOK.Code() {
|
||||
return
|
||||
}
|
||||
|
||||
r.Response.ClearBuffer()
|
||||
_ = r.Response.WriteTplContent(simple.DefaultErrorTplContent(r.Context()), g.Map{"code": code, "message": message, "stack": resp})
|
||||
}
|
||||
|
||||
// responseXml xml响应
|
||||
func (s *sMiddleware) responseXml(r *ghttp.Request) {
|
||||
code, message, data := parseResponse(r)
|
||||
response.RXml(r, code, message, data)
|
||||
}
|
||||
|
||||
// responseJson json响应
|
||||
func responseJson(r *ghttp.Request) {
|
||||
code, message, data := parseResponse(r)
|
||||
response.RJson(r, code, message, data)
|
||||
}
|
||||
|
||||
// parseResponse 解析响应数据
|
||||
func parseResponse(r *ghttp.Request) (code int, message string, resp interface{}) {
|
||||
ctx := r.Context()
|
||||
err := r.GetError()
|
||||
if err == nil {
|
||||
return consts.ApiStatusOk, "操作成功", r.GetHandlerResponse()
|
||||
}
|
||||
|
||||
// 是否输出错误堆栈到页面
|
||||
if g.Cfg().MustGet(ctx, "hotgo.debug", true).Bool() {
|
||||
message = gerror.Current(err).Error()
|
||||
if getContentType(r) == consts.HTTPContentTypeHtml {
|
||||
resp = charset.SerializeStack(err)
|
||||
} else {
|
||||
resp = charset.ParseErrStack(err)
|
||||
}
|
||||
} else {
|
||||
message = consts.ErrorMessage(gerror.Current(err))
|
||||
}
|
||||
|
||||
code = gerror.Code(err).Code()
|
||||
|
||||
// 记录异常日志
|
||||
// 如果你想对错误做不同的处理,可以通过定义不同的错误码来区分
|
||||
// 默认-1为安全可控错误码只记录文件日志,非-1为不可控错误,记录文件日志+服务日志并打印堆栈
|
||||
if code == gcode.CodeNil.Code() {
|
||||
g.Log().Stdout(false).Infof(ctx, "exception:%v", err)
|
||||
} else {
|
||||
g.Log().Errorf(ctx, "exception:%v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getContentType(r *ghttp.Request) (contentType string) {
|
||||
contentType = r.Response.Header().Get("Content-Type")
|
||||
if contentType != "" {
|
||||
return
|
||||
}
|
||||
|
||||
mime := gmeta.Get(r.GetHandlerResponse(), "mime").String()
|
||||
if mime == "" {
|
||||
contentType = consts.HTTPContentTypeJson
|
||||
} else {
|
||||
contentType = mime
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// Package model
|
||||
// @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 model
|
||||
|
||||
// 本地配置.
|
||||
|
||||
// TokenConfig 登录令牌配置
|
||||
type TokenConfig struct {
|
||||
SecretKey string `json:"secretKey"`
|
||||
Expires int64 `json:"expires"`
|
||||
AutoRefresh bool `json:"autoRefresh"`
|
||||
RefreshInterval int64 `json:"refreshInterval"`
|
||||
MaxRefreshTimes int64 `json:"maxRefreshTimes"`
|
||||
MultiLogin bool `json:"multiLogin"`
|
||||
}
|
||||
|
||||
// UploadConfig 上传配置
|
||||
type UploadConfig struct {
|
||||
Drive string `json:"drive"`
|
||||
FileSize int64 `json:"fileSize"`
|
||||
FileType string `json:"fileType"`
|
||||
ImageSize int64 `json:"imageSize"`
|
||||
ImageType string `json:"imageType"`
|
||||
LocalPath string `json:"localPath"`
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// Package model
|
||||
// @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 model
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// Context 请求上下文结构
|
||||
type Context struct {
|
||||
Module string // 应用模块 admin|api|home|websocket
|
||||
AddonName string // 插件名称 如果不是插件模块请求,可能为空
|
||||
User *Identity // 上下文用户信息
|
||||
Response *Response // 请求响应
|
||||
Data g.Map // 自定kv变量 业务模块根据需要设置,不固定
|
||||
}
|
||||
|
||||
// Identity 通用身份模型
|
||||
type Identity struct {
|
||||
Id int64 `json:"id" description:"用户ID"`
|
||||
Username string `json:"username" description:"用户名"`
|
||||
Nickname string `json:"nickname" description:"昵称"`
|
||||
Avatar string `json:"avatar" description:"头像"`
|
||||
App string `json:"app" description:"登录应用"`
|
||||
LoginAt *gtime.Time `json:"loginAt" description:"登录时间"`
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package do
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// AdminAttachment is the golang structure of table hg_admin_attachment for DAO operations like Where/Data.
|
||||
type AdminAttachment struct {
|
||||
g.Meta `orm:"table:hg_admin_attachment, do:true"`
|
||||
Id interface{} // 文件ID
|
||||
AppId interface{} // 应用ID
|
||||
MemberId interface{} // 管理员ID
|
||||
CateId interface{} // 上传分类
|
||||
Drive interface{} // 上传驱动
|
||||
Name interface{} // 文件原始名
|
||||
Kind interface{} // 上传类型
|
||||
MimeType interface{} // 扩展类型
|
||||
NaiveType interface{} // NaiveUI类型
|
||||
Path interface{} // 本地路径
|
||||
FileUrl interface{} // url
|
||||
Size interface{} // 文件大小
|
||||
Ext interface{} // 扩展名
|
||||
Md5 interface{} // md5校验码
|
||||
Status interface{} // 状态
|
||||
CreatedAt *gtime.Time // 创建时间
|
||||
UpdatedAt *gtime.Time // 修改时间
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package do
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// AdminMember is the golang structure of table hg_admin_member for DAO operations like Where/Data.
|
||||
type AdminMember struct {
|
||||
g.Meta `orm:"table:hg_admin_member, do:true"`
|
||||
Id interface{} // 管理员ID
|
||||
Nickname interface{} // 昵称
|
||||
Username interface{} // 帐号
|
||||
PasswordHash interface{} // 密码
|
||||
Salt interface{} // 密码盐
|
||||
Avatar interface{} // 头像
|
||||
LastActiveAt *gtime.Time // 最后活跃时间
|
||||
Remark interface{} // 备注
|
||||
Status interface{} // 状态
|
||||
CreatedAt *gtime.Time // 创建时间
|
||||
UpdatedAt *gtime.Time // 修改时间
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package do
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// AdminProject is the golang structure of table hg_admin_project for DAO operations like Where/Data.
|
||||
type AdminProject struct {
|
||||
g.Meta `orm:"table:hg_admin_project, do:true"`
|
||||
Id interface{} // 项目id
|
||||
ProjectName interface{} // 项目名称
|
||||
IndexImage interface{} // 预览图片url
|
||||
Content interface{} // 项目数据
|
||||
Status interface{} // 项目状态,-1: 未发布'1: 已发布
|
||||
Remarks interface{} // 项目备注
|
||||
CreatedBy interface{} // 创建人
|
||||
UpdatedAt *gtime.Time // 更新时间
|
||||
CreatedAt *gtime.Time // 创建时间
|
||||
DeletedAt *gtime.Time // 删除时间
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package entity
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// AdminAttachment is the golang structure for table admin_attachment.
|
||||
type AdminAttachment struct {
|
||||
Id int64 `json:"id" description:"文件ID"`
|
||||
AppId string `json:"appId" description:"应用ID"`
|
||||
MemberId int64 `json:"memberId" description:"管理员ID"`
|
||||
CateId uint64 `json:"cateId" description:"上传分类"`
|
||||
Drive string `json:"drive" description:"上传驱动"`
|
||||
Name string `json:"name" description:"文件原始名"`
|
||||
Kind string `json:"kind" description:"上传类型"`
|
||||
MimeType string `json:"mimeType" description:"扩展类型"`
|
||||
NaiveType string `json:"naiveType" description:"NaiveUI类型"`
|
||||
Path string `json:"path" description:"本地路径"`
|
||||
FileUrl string `json:"fileUrl" description:"url"`
|
||||
Size int64 `json:"size" description:"文件大小"`
|
||||
Ext string `json:"ext" description:"扩展名"`
|
||||
Md5 string `json:"md5" description:"md5校验码"`
|
||||
Status int `json:"status" description:"状态"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" description:"修改时间"`
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package entity
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// AdminMember is the golang structure for table admin_member.
|
||||
type AdminMember struct {
|
||||
Id int64 `json:"id" description:"管理员ID"`
|
||||
Nickname string `json:"nickname" description:"昵称"`
|
||||
Username string `json:"username" description:"帐号"`
|
||||
PasswordHash string `json:"passwordHash" description:"密码"`
|
||||
Salt string `json:"salt" description:"密码盐"`
|
||||
Avatar string `json:"avatar" description:"头像"`
|
||||
LastActiveAt *gtime.Time `json:"lastActiveAt" description:"最后活跃时间"`
|
||||
Remark string `json:"remark" description:"备注"`
|
||||
Status int `json:"status" description:"状态"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" description:"修改时间"`
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package entity
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// AdminProject is the golang structure for table admin_project.
|
||||
type AdminProject struct {
|
||||
Id int64 `json:"id" description:"项目id"`
|
||||
ProjectName string `json:"projectName" description:"项目名称"`
|
||||
IndexImage string `json:"indexImage" description:"预览图片url"`
|
||||
Content string `json:"content" description:"项目数据"`
|
||||
Status int `json:"status" description:"项目状态,-1: 未发布'1: 已发布"`
|
||||
Remarks string `json:"remarks" description:"项目备注"`
|
||||
CreatedBy int64 `json:"createdBy" description:"创建人"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"`
|
||||
DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"`
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
// Package adminin
|
||||
// @Description
|
||||
// @Author Ms <133814250@qq.com>
|
||||
package adminin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"hotgo/internal/model/entity"
|
||||
"hotgo/internal/model/input/form"
|
||||
)
|
||||
|
||||
// ProjectListInp 查询列表
|
||||
type ProjectListInp struct {
|
||||
form.PageReq
|
||||
}
|
||||
|
||||
type ProjectListModel struct {
|
||||
entity.AdminProject
|
||||
State int `json:"state"`
|
||||
CreateTime *gtime.Time `json:"createTime"`
|
||||
CreateUserId int64 `json:"createUserId"`
|
||||
}
|
||||
|
||||
// ProjectCreateInp 新增项目
|
||||
type ProjectCreateInp struct {
|
||||
ProjectName string `json:"projectName"`
|
||||
Remarks string `json:"remarks"`
|
||||
IndexImage string `json:"indexImage"`
|
||||
}
|
||||
|
||||
type ProjectCreateModel struct {
|
||||
Id int64 `json:"id"`
|
||||
ProjectName string `json:"projectName"`
|
||||
State int `json:"state"`
|
||||
CreateTime *gtime.Time `json:"createTime"`
|
||||
CreateUserId int64 `json:"createUserId"`
|
||||
IndexImage string `json:"indexImage"`
|
||||
Remarks string `json:"remarks"`
|
||||
}
|
||||
|
||||
// ProjectEditInp 修改项目数据
|
||||
type ProjectEditInp struct {
|
||||
entity.AdminProject
|
||||
}
|
||||
|
||||
type ProjectEditModel struct{}
|
||||
|
||||
func (in *ProjectEditInp) Filter(ctx context.Context) (err error) {
|
||||
if in.Id <= 0 {
|
||||
err = gerror.New("ID不能为空")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ProjectSaveDataInp 保存项目数据
|
||||
type ProjectSaveDataInp struct {
|
||||
Id int64 `json:"projectId"`
|
||||
Content string `json:"content" description:"项目数据"`
|
||||
}
|
||||
|
||||
type ProjectSaveDataModel struct{}
|
||||
|
||||
func (in *ProjectSaveDataInp) Filter(ctx context.Context) (err error) {
|
||||
if in.Id <= 0 {
|
||||
err = gerror.New("ID不能为空")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ProjectDeleteInp 删除项目类型
|
||||
type ProjectDeleteInp struct {
|
||||
Id int64 `json:"id" v:"required#项目ID不能为空" dc:"项目ID"`
|
||||
}
|
||||
|
||||
type ProjectDeleteModel struct{}
|
||||
|
||||
// ProjectGetDataInp 获取信息
|
||||
type ProjectGetDataInp struct {
|
||||
Id int64 `json:"projectId" dc:"项目ID"`
|
||||
}
|
||||
|
||||
func (in *ProjectGetDataInp) Filter(ctx context.Context) (err error) {
|
||||
if in.Id <= 0 {
|
||||
err = gerror.New("ID不能为空")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ProjectGetDataModel struct {
|
||||
Id int64 `json:"id"`
|
||||
ProjectName string `json:"projectName"`
|
||||
State int `json:"state"`
|
||||
CreateTime *gtime.Time `json:"createTime"`
|
||||
CreateUserId int64 `json:"createUserId"`
|
||||
IndexImage string `json:"indexImage"`
|
||||
Remarks string `json:"remarks"`
|
||||
Content string `json:"content" description:"项目数据"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
// ProjectPublishInp 修改发布状态
|
||||
type ProjectPublishInp struct {
|
||||
Id int64 `json:"id"`
|
||||
ProjectName string `json:"projectName"`
|
||||
State int `json:"state"`
|
||||
}
|
||||
|
||||
func (in *ProjectPublishInp) Filter(ctx context.Context) (err error) {
|
||||
if in.Id <= 0 {
|
||||
err = gerror.New("ID不能为空")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ProjectPublishModel struct{}
|
||||
|
||||
// ProjectUploadInp 文件上传
|
||||
type ProjectUploadInp struct {
|
||||
File *ghttp.UploadFile `json:"object" type:"file" dc:"文件"`
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package adminin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// RegisterInp 账号注册
|
||||
type RegisterInp struct {
|
||||
Username string `json:"username" v:"required#用户名不能为空" dc:"用户名"`
|
||||
Password string `json:"password" v:"required#密码不能为空" dc:"密码"`
|
||||
Nickname string `json:"nickname" dc:"昵称"`
|
||||
}
|
||||
|
||||
func (in *RegisterInp) Filter(ctx context.Context) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
type LoginToken struct {
|
||||
TokenValue string `json:"tokenValue"`
|
||||
TokenName string `json:"tokenName"`
|
||||
Expires int64 `json:"expires"`
|
||||
}
|
||||
|
||||
type LoginUserInfo struct {
|
||||
Id int64 `json:"id" dc:"用户ID"`
|
||||
Username string `json:"username" dc:"用户名"`
|
||||
Nickname string `json:"nickname" dc:"昵称"`
|
||||
}
|
||||
|
||||
// LoginModel 统一登录响应
|
||||
type LoginModel struct {
|
||||
UserInfo *LoginUserInfo `json:"userinfo" dc:"用户信息"`
|
||||
Token *LoginToken `json:"token" dc:"登录token"`
|
||||
}
|
||||
|
||||
// AccountLoginInp 账号登录
|
||||
type AccountLoginInp struct {
|
||||
Username string `json:"username" v:"required#用户名不能为空" dc:"用户名"`
|
||||
Password string `json:"password" v:"required#密码不能为空" dc:"密码"`
|
||||
}
|
||||
|
||||
// MobileLoginInp 手机号登录
|
||||
type MobileLoginInp struct {
|
||||
Mobile string `json:"mobile" v:"required|phone-loose#手机号不能为空|手机号格式不正确" dc:"手机号"`
|
||||
Code string `json:"code" v:"required#验证码不能为空" dc:"验证码"`
|
||||
}
|
||||
|
||||
// MemberLoginPermissions 登录用户角色信息
|
||||
type MemberLoginPermissions []string
|
||||
|
||||
// MemberLoginStatInp 用户登录统计
|
||||
type MemberLoginStatInp struct {
|
||||
MemberId int64
|
||||
}
|
||||
|
||||
type MemberLoginStatModel struct {
|
||||
LoginCount int `json:"loginCount" dc:"登录次数"`
|
||||
LastLoginAt *gtime.Time `json:"lastLoginAt" dc:"最后登录时间"`
|
||||
LastLoginIp string `json:"lastLoginIp" dc:"最后登录IP"`
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
// Package form
|
||||
// @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 form
|
||||
|
||||
import (
|
||||
"hotgo/internal/consts"
|
||||
)
|
||||
|
||||
type ReqPageFunc interface {
|
||||
GetPage() int
|
||||
GetPerPage() int
|
||||
}
|
||||
|
||||
// PageReq 分页请求
|
||||
type PageReq struct {
|
||||
Page int `json:"page" example:"10" d:"1" v:"min:1#页码最小值不能低于1" dc:"当前页码"`
|
||||
PerPage int `json:"limit" example:"1" d:"10" v:"min:1|max:200#每页数量最小值不能低于1|最大值不能大于200" dc:"每页数量"`
|
||||
}
|
||||
|
||||
// GetPage 获取当前页码
|
||||
func (req *PageReq) GetPage() int {
|
||||
return req.Page
|
||||
}
|
||||
|
||||
// GetPerPage 获取每页数量
|
||||
func (req *PageReq) GetPerPage() int {
|
||||
return req.PerPage
|
||||
}
|
||||
|
||||
// PageRes 分页响应
|
||||
type PageRes struct {
|
||||
PageReq
|
||||
PageCount int `json:"pageCount" example:"0" dc:"分页个数"`
|
||||
TotalCount int `json:"count" example:"0" dc:"数据总行数"`
|
||||
}
|
||||
|
||||
// Pack 打包分页数据
|
||||
func (res *PageRes) Pack(req ReqPageFunc, totalCount int) {
|
||||
res.TotalCount = totalCount
|
||||
res.PageCount = CalPageCount(totalCount, req.GetPerPage())
|
||||
res.Page = req.GetPage()
|
||||
res.PerPage = req.GetPerPage()
|
||||
}
|
||||
|
||||
func CalPageCount(totalCount int, perPage int) int {
|
||||
return (totalCount + perPage - 1) / perPage
|
||||
}
|
||||
|
||||
// CalPage 计算分页偏移量
|
||||
func CalPage(page, perPage int) (newPage, newPerPage int, offset int) {
|
||||
if page <= 0 {
|
||||
newPage = consts.DefaultPage
|
||||
} else {
|
||||
newPage = page
|
||||
}
|
||||
if perPage <= 0 {
|
||||
newPerPage = consts.DefaultPageSize
|
||||
} else {
|
||||
newPerPage = perPage
|
||||
}
|
||||
|
||||
offset = (newPage - 1) * newPerPage
|
||||
return
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// Package model
|
||||
// @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 model
|
||||
|
||||
// Response HTTP响应
|
||||
type Response struct {
|
||||
Code int `json:"code" example:"0" description:"状态码"`
|
||||
Message string `json:"msg,omitempty" example:"操作成功" description:"提示消息"`
|
||||
Data interface{} `json:"data,omitempty" description:"数据集"`
|
||||
Error interface{} `json:"error,omitempty" description:"错误信息"`
|
||||
Timestamp int64 `json:"timestamp" example:"1640966400" description:"服务器时间戳"`
|
||||
TraceID string `json:"traceID" v:"0" example:"d0bb93048bc5c9164cdee845dcb7f820" description:"链路ID"`
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
package packed
|
|
@ -0,0 +1,66 @@
|
|||
// ================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// You can delete these comments if you wish manually maintain this interface file.
|
||||
// ================================================================================
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"hotgo/internal/model"
|
||||
"hotgo/internal/model/input/adminin"
|
||||
)
|
||||
|
||||
type (
|
||||
IAdminProject interface {
|
||||
// List 查询列表
|
||||
List(ctx context.Context, in *adminin.ProjectListInp) (list []*adminin.ProjectListModel, totalCount int, err error)
|
||||
// Delete 删除
|
||||
Delete(ctx context.Context, in *adminin.ProjectDeleteInp) (err error)
|
||||
// Create 新增
|
||||
Create(ctx context.Context, in *adminin.ProjectCreateInp) (res *adminin.ProjectCreateModel, err error)
|
||||
// Edit 修改
|
||||
Edit(ctx context.Context, in *adminin.ProjectEditInp) (err error)
|
||||
// SaveData 保存数据
|
||||
SaveData(ctx context.Context, in *adminin.ProjectSaveDataInp) (err error)
|
||||
// Publish 修改发布状态
|
||||
Publish(ctx context.Context, in *adminin.ProjectPublishInp) (err error)
|
||||
// GetData 获取指定信息
|
||||
GetData(ctx context.Context, in *adminin.ProjectGetDataInp) (res *adminin.ProjectGetDataModel, err error)
|
||||
}
|
||||
IAdminSite interface {
|
||||
// Register 账号注册
|
||||
Register(ctx context.Context, in *adminin.RegisterInp) (err error)
|
||||
// AccountLogin 账号登录
|
||||
AccountLogin(ctx context.Context, in *adminin.AccountLoginInp) (res *adminin.LoginModel, err error)
|
||||
// BindUserContext 绑定用户上下文
|
||||
BindUserContext(ctx context.Context, claims *model.Identity) (err error)
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
localAdminProject IAdminProject
|
||||
localAdminSite IAdminSite
|
||||
)
|
||||
|
||||
func AdminProject() IAdminProject {
|
||||
if localAdminProject == nil {
|
||||
panic("implement not found for interface IAdminProject, forgot register?")
|
||||
}
|
||||
return localAdminProject
|
||||
}
|
||||
|
||||
func RegisterAdminProject(i IAdminProject) {
|
||||
localAdminProject = i
|
||||
}
|
||||
|
||||
func AdminSite() IAdminSite {
|
||||
if localAdminSite == nil {
|
||||
panic("implement not found for interface IAdminSite, forgot register?")
|
||||
}
|
||||
return localAdminSite
|
||||
}
|
||||
|
||||
func RegisterAdminSite(i IAdminSite) {
|
||||
localAdminSite = i
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
// ================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// You can delete these comments if you wish manually maintain this interface file.
|
||||
// ================================================================================
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
)
|
||||
|
||||
type (
|
||||
IMiddleware interface {
|
||||
// AdminAuth 后台鉴权中间件
|
||||
AdminAuth(r *ghttp.Request)
|
||||
// Ctx 初始化请求上下文
|
||||
Ctx(r *ghttp.Request)
|
||||
// CORS allows Cross-origin resource sharing.
|
||||
CORS(r *ghttp.Request)
|
||||
// DeliverUserContext 将用户信息传递到上下文中
|
||||
DeliverUserContext(r *ghttp.Request) (err error)
|
||||
// IsExceptAuth 是否是不需要验证权限的路由地址
|
||||
IsExceptAuth(ctx context.Context, appName, path string) bool
|
||||
// IsExceptLogin 是否是不需要登录的路由地址
|
||||
IsExceptLogin(ctx context.Context, appName, path string) bool
|
||||
// GetFilterRoutes 获取支持预处理的web路由
|
||||
GetFilterRoutes(r *ghttp.Request) map[string]ghttp.RouterItem
|
||||
// GenFilterRequestKey 根据请求生成唯一key
|
||||
GenFilterRequestKey(r *ghttp.Request) string
|
||||
// GenFilterRouteKey 根据路由生成唯一key
|
||||
GenFilterRouteKey(r *ghttp.Router) string
|
||||
// GenRouteKey 生成唯一key
|
||||
GenRouteKey(method, path string) string
|
||||
// PreFilter 请求输入预处理
|
||||
// api使用gf规范路由并且XxxReq结构体实现了validate.Filter接口即可
|
||||
PreFilter(r *ghttp.Request)
|
||||
// ResponseHandler HTTP响应预处理
|
||||
ResponseHandler(r *ghttp.Request)
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
localMiddleware IMiddleware
|
||||
)
|
||||
|
||||
func Middleware() IMiddleware {
|
||||
if localMiddleware == nil {
|
||||
panic("implement not found for interface IMiddleware, forgot register?")
|
||||
}
|
||||
return localMiddleware
|
||||
}
|
||||
|
||||
func RegisterMiddleware(i IMiddleware) {
|
||||
localMiddleware = i
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
|
||||
_ "github.com/gogf/gf/contrib/nosql/redis/v2"
|
||||
|
||||
_ "hotgo/internal/packed"
|
||||
|
||||
_ "hotgo/internal/logic"
|
||||
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
|
||||
"hotgo/internal/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd.Main.Run(gctx.GetInitCtx())
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
|
||||
# hotgo配置
|
||||
hotgo:
|
||||
# debug开关,开启后接口出现错误时会向前端输出堆栈信息,可选:false|true,默认为true
|
||||
debug: true
|
||||
|
||||
|
||||
# 路由配置
|
||||
router:
|
||||
# 后台
|
||||
admin:
|
||||
# 前缀
|
||||
prefix: "/admin"
|
||||
# 不需要验证登录的路由地址
|
||||
exceptLogin: [
|
||||
"/api/goview/project/getData", # 获取项目数据
|
||||
]
|
||||
|
||||
|
||||
# 统一默认日志配置
|
||||
defaultLogger: &defaultLogger
|
||||
level: "all"
|
||||
flags: 42
|
||||
file: "{Y-m-d}.log" # 日志文件格式。默认为"{Y-m-d}.log"
|
||||
stdoutColorDisabled: false # 关闭终端的颜色打印。可选:false|true,默认false
|
||||
writerColorEnable: false # 日志文件是否带上颜色。可选:false|true,默认false,表示不带颜色
|
||||
rotateExpire: "7d" # 日志保留天数
|
||||
rotateBackupLimit: 2 # 最大备份数量
|
||||
rotateBackupCompress: 2 # 日志文件压缩级别,0-9,9最高
|
||||
|
||||
|
||||
# gf配置,配置参考:https://goframe.org/pages/viewpage.action?pageId=44449486
|
||||
server:
|
||||
address: ":8090" # 本地监听地址
|
||||
openapiPath: "/api.json" # OpenAPI接口文档地址
|
||||
swaggerPath: "/swagger" # 内置SwaggerUI展示地址
|
||||
serverRoot: "resource/public" # 静态文件服务的目录根路径,配置时自动开启静态文件服务。
|
||||
DumpRouterMap: false # 是否在Server启动时打印所有的路由列表。
|
||||
logPath: "logs/server" # 服务日志保存路径
|
||||
ErrorStack: true # 当Server捕获到异常时是否记录堆栈信息到日志中。默认为true
|
||||
ErrorLogEnabled: true # 是否记录异常日志信息到日志中。默认为true
|
||||
errorLogPattern: "error/{Y-m-d}.log" # 异常错误日志文件格式。默认为"error-{Ymd}.log"
|
||||
accessLogEnabled: true # 是否记录访问日志。默认为false
|
||||
accessLogPattern: "access/{Y-m-d}.log" # 访问日志文件格式。默认为"access-{Ymd}.log"
|
||||
maxHeaderBytes: "100KB" # 请求头大小限制,请求头包括客户端提交的Cookie数据,默认设置为100KB
|
||||
clientMaxBodySize: "300MB" # 客户端提交的Body大小限制,同时也影响文件上传大小,默认设置为300MB
|
||||
serverAgent: "HG HTTP Server" # 服务端Agent信息。默认为"HG HTTP Server"
|
||||
# PProf配置
|
||||
pprofEnabled: true # 是否开启PProf性能调试特性。默认为false
|
||||
pprofPattern: "/pprof" # 开启PProf时有效,表示PProf特性的页面访问路径,对当前Server绑定的所有域名有效。
|
||||
# 服务日志配置
|
||||
logger:
|
||||
path: "logs/server" # 日志文件路径。默认为空,表示关闭,仅输出到终端
|
||||
<<: *defaultLogger
|
||||
|
||||
|
||||
#缓存
|
||||
cache:
|
||||
adapter: "file" # 缓存驱动方式,支持:memory|redis|file,不填默认memory
|
||||
fileDir: "./storage/cache" # 文件缓存路径,adapter=file时必填
|
||||
|
||||
|
||||
# 登录令牌
|
||||
token:
|
||||
secretKey: "hotgo123" # 令牌加密秘钥,考虑安全问题生产环境中请修改默认值
|
||||
expires: 604800 # 令牌有效期,单位:秒。默认7天
|
||||
autoRefresh: true # 是否开启自动刷新过期时间, false|true 默认为true
|
||||
refreshInterval: 86400 # 刷新间隔,单位:秒。必须小于expires,否则无法触发。默认1天内只允许刷新一次
|
||||
maxRefreshTimes: 30 # 最大允许刷新次数,-1不限制。默认30次
|
||||
multiLogin: true # 是否允许多端登录, false|true 默认为true
|
||||
|
||||
# 上传驱动
|
||||
storager:
|
||||
# 通用配置
|
||||
drive: "local" # 上传驱动。local:本地存储
|
||||
fileSize: "10" # 上传图片大小限制,单位:MB
|
||||
fileType: "doc,docx,pdf,zip,tar,xls,xlsx,rar,jpg,jpeg,gif,npm,png,svg" # 上传文件类型限制,文件上传后缀类型限制
|
||||
imageSize: "your_image_size_value" # 上传图片大小限制,单位:MB
|
||||
imageType: "jpg,jpeg,gif,npm,png,svg" # 上传图片类型限制,图片上传后缀类型限制
|
||||
localPath: "attachment/" # 本地存储路径,对外访问的相对路径
|
||||
|
||||
|
||||
# Redis. 配置参考:https://goframe.org/pages/viewpage.action?pageId=1114217
|
||||
redis:
|
||||
default:
|
||||
address: "192.168.1.100:6379"
|
||||
db: "3"
|
||||
pass: "jhkdjhkjdhsIUTYURTU_37fMei"
|
||||
idleTimeout: "20"
|
||||
|
||||
|
||||
# Database. 配置参考:https://goframe.org/pages/viewpage.action?pageId=1114245
|
||||
database:
|
||||
logger:
|
||||
path: "logs/database" # 日志文件路径。默认为空,表示关闭,仅输出到终端
|
||||
<<: *defaultLogger
|
||||
stdout: true
|
||||
default:
|
||||
link: "mysql:goview:ZZ6amaa8xnHJQffb@tcp(192.168.1.100:3306)/goview?loc=Local&parseTime=true&charset=utf8mb4"
|
||||
debug: true
|
||||
Prefix: "hg_"
|
||||
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: template-single
|
||||
labels:
|
||||
app: template-single
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: template-single
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: template-single
|
||||
spec:
|
||||
containers:
|
||||
- name : main
|
||||
image: template-single
|
||||
imagePullPolicy: Always
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: template-single
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
protocol: TCP
|
||||
targetPort: 8000
|
||||
selector:
|
||||
app: template-single
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: template-single-configmap
|
||||
data:
|
||||
config.yaml: |
|
||||
server:
|
||||
address: ":8000"
|
||||
openapiPath: "/api.json"
|
||||
swaggerPath: "/swagger"
|
||||
|
||||
logger:
|
||||
level : "all"
|
||||
stdout: true
|
|
@ -0,0 +1,10 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: template-single
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name : main
|
||||
image: template-single:develop
|
|
@ -0,0 +1,14 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- ../../base
|
||||
- configmap.yaml
|
||||
|
||||
patchesStrategicMerge:
|
||||
- deployment.yaml
|
||||
|
||||
namespace: default
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
FROM loads/alpine:3.8
|
||||
|
||||
###############################################################################
|
||||
# INSTALLATION
|
||||
###############################################################################
|
||||
|
||||
ENV WORKDIR /app
|
||||
ADD resource $WORKDIR/
|
||||
ADD ./temp/linux_amd64/main $WORKDIR/main
|
||||
RUN chmod +x $WORKDIR/main
|
||||
|
||||
###############################################################################
|
||||
# START
|
||||
###############################################################################
|
||||
WORKDIR $WORKDIR
|
||||
CMD ./main
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
# This shell is executed before docker build.
|
||||
|
||||
|
||||
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue