init: initial commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
.idea
|
||||||
|
log
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
# 代码分析与优化建议报告
|
||||||
|
|
||||||
|
在分析了项目的核心模块(`System`, `Plant/Claim`, `Plant/Order`)后,发现以下几个在性能、数据一致性及可维护性方面可以优化的地方。
|
||||||
|
|
||||||
|
## 1. 性能优化 (Performance)
|
||||||
|
|
||||||
|
### 1.1 **`ClaimPlantList` 中的 N+1 查询问题**
|
||||||
|
**位置**: `service/plant/claim_plant.go` -> `ClaimPlantList`
|
||||||
|
**问题**:
|
||||||
|
在 `ClaimPlantList` 函数中,代码在遍历列表 (`res`) 时,对**每一项**都执行了一次数据库查询来判断用户是否已领取:
|
||||||
|
```go
|
||||||
|
for i := range res {
|
||||||
|
// ...
|
||||||
|
// 循环内查询!
|
||||||
|
err2 := global.DB.Where("claim_plant_id = ? ...", res[i].Id).First(&claim).Error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
如果每页显示 20 条数据,就会产生 **21 次数据库查询**。随着数据量增长,接口响应会显著变慢。
|
||||||
|
|
||||||
|
**优化建议**:
|
||||||
|
- **批量查询**: 先收集列表中所有的 `ClaimPlantId`。
|
||||||
|
- **单次查询**: 使用 `IN` 查询一次性获取当前用户关联的所有记录 (`WHERE user_id = ? AND claim_plant_id IN (?)`)。
|
||||||
|
- **内存映射**: 将查询结果转为 Map,在内存中进行匹配。
|
||||||
|
- **效果**: 将 N+1 次查询减少为 **2 次查询**。
|
||||||
|
|
||||||
|
### 1.2 **订单导出中的高内存占用 (OOM 风险)**
|
||||||
|
**位置**: `service/plant/order.go` -> `ExportOrder`
|
||||||
|
**问题**:
|
||||||
|
该函数使用 `db.Find(&orders)` 一次性查询符合条件的所有订单。
|
||||||
|
如果订单量达到 10 万级别,将所有数据加载到切片中会导致内存暴涨,甚至引发 **OOM (内存溢出)** 崩溃。
|
||||||
|
|
||||||
|
**优化建议**:
|
||||||
|
- **流式处理**: 使用 Gorm 的 `Rows()` 或 `FindInBatches` 分批获取数据。
|
||||||
|
- **流式写入**: 逐行向 Excel 写入数据,而不是构建好整个大对象后再写入。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 数据一致性与并发 (Data Integrity)
|
||||||
|
|
||||||
|
### 2.1 **植物认养中的竞态条件 (Race Condition)**
|
||||||
|
**位置**: `service/plant/claim_plant.go` -> `ClaimPlant`
|
||||||
|
**问题**:
|
||||||
|
代码在开启事务前就进行了库存和积分的检查:
|
||||||
|
```go
|
||||||
|
// 1. 读库存
|
||||||
|
if claimPlant.Stock <= 0 { ... }
|
||||||
|
// 2. 读积分
|
||||||
|
if personal.PointsCount < claimPlant.Points { ... }
|
||||||
|
// 3. 开启事务写数据
|
||||||
|
err = global.DB.Transaction(...)
|
||||||
|
```
|
||||||
|
在高并发场景(如秒杀)下,两个用户可能同时通过 `Stock > 0` 的检查,但在库存仅剩 1 个时,都会进入事务扣减库存,导致**超卖**(库存变成 -1)。
|
||||||
|
|
||||||
|
**优化建议**:
|
||||||
|
- **悲观锁**: 在查询植物和用户积分时使用 `clause.Locking{Strength: "UPDATE"}` 锁定行。
|
||||||
|
- **原子更新**: 在 Update 语句中加入条件判断:
|
||||||
|
```sql
|
||||||
|
UPDATE claim_plant SET stock = stock - 1 WHERE id = ? AND stock > 0
|
||||||
|
```
|
||||||
|
通过检查 `RowsAffected` 判断是否扣减成功。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 代码质量与可维护性 (Maintainability)
|
||||||
|
|
||||||
|
### 3.1 **硬编码的魔法值**
|
||||||
|
**位置**: `service/plant/order.go`
|
||||||
|
**问题**:
|
||||||
|
订单状态(1=已支付, 2=退款 等)在代码多处直接使用数字或字符串硬编码。
|
||||||
|
**优化建议**:
|
||||||
|
- 在 `model` 包中定义常量或枚举(Enum),并在全局统一使用,避免拼写错误并提高可读性。
|
||||||
|
|
||||||
|
### 3.2 **手动事务管理**
|
||||||
|
**位置**: `service/system/sys_user.go` -> `MiniLogin`
|
||||||
|
**问题**:
|
||||||
|
该函数手动使用了 `tx.Begin()`, `tx.Rollback()` 和 `defer recover()`,这种写法容易出错且不简洁。
|
||||||
|
**优化建议**:
|
||||||
|
- 使用 `global.DB.Transaction(func(tx *gorm.DB) error { ... })` 闭包模式。这是 Gorm 推荐的写法,能自动处理 Panic 和错误回滚,代码更清晰。
|
||||||
|
|
||||||
|
## 4. 汇总表
|
||||||
|
|
||||||
|
| 模块 | 问题 | 严重程度 | 类型 | 建议 |
|
||||||
|
| :--- | :--- | :--- | :--- | :--- |
|
||||||
|
| **Claim** | 列表 N+1 查询 | 高 | 性能 | 批量查询 ID |
|
||||||
|
| **Claim** | 库存竞态条件 | 高 | 数据安全 | 悲观锁 / 原子更新 |
|
||||||
|
| **Order** | 导出全部加载 | 中 | 性能 | 分批流式处理 |
|
||||||
|
| **Order** | 魔法值硬编码 | 低 | 可维护性 | 使用常量常量定义 |
|
||||||
|
| **User** | 手动事务写法 | 低 | 代码风格 | 使用 `DB.Transaction` 闭包 |
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
## server项目结构
|
||||||
|
|
||||||
|
```shell
|
||||||
|
├── api
|
||||||
|
│ └── v1
|
||||||
|
├── config
|
||||||
|
├── core
|
||||||
|
├── docs
|
||||||
|
├── global
|
||||||
|
├── initialize
|
||||||
|
│ └── internal
|
||||||
|
├── middleware
|
||||||
|
├── model
|
||||||
|
│ ├── request
|
||||||
|
│ └── response
|
||||||
|
├── pkg
|
||||||
|
│ ├── example
|
||||||
|
├── packfile
|
||||||
|
├── resource
|
||||||
|
│ ├── excel
|
||||||
|
│ ├── page
|
||||||
|
│ └── template
|
||||||
|
├── router
|
||||||
|
├── service
|
||||||
|
├── source
|
||||||
|
└── utils
|
||||||
|
├── timer
|
||||||
|
└── upload
|
||||||
|
```
|
||||||
|
|
||||||
|
| 文件夹 | 说明 | 描述 |
|
||||||
|
| ------------ | ----------------------- | --------------------------- |
|
||||||
|
| `api` | api层 | api层 |
|
||||||
|
| `--v1` | v1版本接口 | v1版本接口 |
|
||||||
|
| `config` | 配置包 | config.yaml对应的配置结构体 |
|
||||||
|
| `core` | 核心文件 | 核心组件(zap, viper, server)的初始化 |
|
||||||
|
| `docs` | swagger文档目录 | swagger文档目录 |
|
||||||
|
| `global` | 全局对象 | 全局对象 |
|
||||||
|
| `initialize` | 初始化 | router,redis,gorm,validator, timer的初始化 |
|
||||||
|
| `--internal` | 初始化内部函数 | gorm 的 logger 自定义,在此文件夹的函数只能由 `initialize` 层进行调用 |
|
||||||
|
| `middleware` | 中间件层 | 用于存放 `gin` 中间件代码 |
|
||||||
|
| `model` | 模型层 | 模型对应数据表 |
|
||||||
|
| `pkg` | 公共包 | 存放一些公共函数 |
|
||||||
|
| `--request` | 入参结构体 | 接收前端发送到后端的数据。 |
|
||||||
|
| `--response` | 出参结构体 | 返回给前端的数据结构体 |
|
||||||
|
| `packfile` | 静态文件打包 | 静态文件打包 |
|
||||||
|
| `resource` | 静态资源文件夹 | 负责存放静态文件 |
|
||||||
|
| `--excel` | excel导入导出默认路径 | excel导入导出默认路径 |
|
||||||
|
| `--page` | 表单生成器 | 表单生成器 打包后的dist |
|
||||||
|
| `--template` | 模板 | 模板文件夹,存放的是代码生成器的模板 |
|
||||||
|
| `router` | 路由层 | 路由层 |
|
||||||
|
| `service` | service层 | 存放业务逻辑问题 |
|
||||||
|
| `source` | source层 | 存放初始化数据的函数 |
|
||||||
|
| `utils` | 工具包 | 工具函数封装 |
|
||||||
|
| `--timer` | timer | 定时器接口封装 |
|
||||||
|
| `--upload` | oss | oss接口封装 |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 一、部署
|
||||||
|
### 1.1 传统方式
|
||||||
|
1.1.1 Mac下交叉编译到Windows和Linux:
|
||||||
|
~~~
|
||||||
|
# 交叉编译到Windows
|
||||||
|
GOOS=windows GOARCH=amd64 go build -o sundynix-plant-go.exe
|
||||||
|
|
||||||
|
# 交叉编译到Linux
|
||||||
|
GOOS=linux GOARCH=amd64 go build -o sundynix-plant-go
|
||||||
|
~~~
|
||||||
|
1.1.2 Rocky9.x下运行
|
||||||
|
上传build好的可执行文件和配置文件到生产目录下 执行如下命令
|
||||||
|
~~~
|
||||||
|
nohup ./sundynix-plant -c config-prod.yaml > console.log 2>&1 &
|
||||||
|
~~~
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# Sundynix Plant Server 项目文档
|
||||||
|
|
||||||
|
## 项目简介
|
||||||
|
本项目是一个基于 Go 语言 (Gin 框架) 开发的后台管理系统,主要用于植物领养、种植乐趣及周边社区的运营管理。项目采用了模块化的架构设计,集成了系统管理(RBAC、日志、OSS)与业务模块(植物图鉴、领养、订单、社区)。
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
```shell
|
||||||
|
├── api # 接口控制层 (Controller)
|
||||||
|
│ └── v1 # v1 版本接口
|
||||||
|
├── config # 配置文件结构定义
|
||||||
|
├── core # 核心组件初始化 (Zap, Viper, Server)
|
||||||
|
├── global # 全局对象 (DB, Redis, Config)
|
||||||
|
├── initialize # 初始化流程 (Router, Redis, Gorm, Validator, Timer)
|
||||||
|
├── middleware # Gin 中间件
|
||||||
|
├── model # 数据模型 (Structs)
|
||||||
|
├── router # 路由定义
|
||||||
|
│ ├── plant # 业务路由 (植物、订单、社区等)
|
||||||
|
│ └── system # 系统路由 (用户、角色、菜单等)
|
||||||
|
├── service # 业务逻辑层 (Service)
|
||||||
|
└── utils # 工具函数集合
|
||||||
|
```
|
||||||
|
|
||||||
|
## 基本功能逻辑
|
||||||
|
|
||||||
|
项目主要分为 **系统管理** 和 **业务管理 (Plant)** 两大模块:
|
||||||
|
|
||||||
|
### 1. 系统管理模块 (System)
|
||||||
|
- **用户鉴权 (Auth/User)**: 支持用户注册、登录及 JWT 鉴权。
|
||||||
|
- **权限管理 (RBAC)**: 基于角色 (Role) 和菜单 (Menu) 的权限控制体系,支持动态菜单。
|
||||||
|
- **操作日志 (Operation Record)**: 记录用户的敏感操作行为,便于审计。
|
||||||
|
- **文件上传 (OSS)**: 集成对象存储,用于处理图片、文件上传。
|
||||||
|
- **客户端管理 (Client)**: 管理接入的客户端信息。
|
||||||
|
|
||||||
|
### 2. 业务管理模块 (Plant)
|
||||||
|
- **植物图鉴 (Library/Classification)**: 维护植物的基础信息与分类,构建植物百科。
|
||||||
|
- **植物领养 (Claim Plant)**:
|
||||||
|
- 用户可以浏览并领养植物。
|
||||||
|
- 支持配置领养规则。
|
||||||
|
- 用户可查看 "我的领养" 记录。
|
||||||
|
- **订单系统 (Order/Pay)**:
|
||||||
|
- 处理领养或购买产生的订单。
|
||||||
|
- 支持订单发货、详情查询、删除及导出功能。
|
||||||
|
- 集成支付功能。
|
||||||
|
- **社区互动 (Post/Comment)**:
|
||||||
|
- 用户发布帖子 (Post) 分享种植心得。
|
||||||
|
- 支持对帖子进行评论 (Comment)。
|
||||||
|
- **其他功能**:
|
||||||
|
- **徽章系统 (Badge)**: 用户成就体系。
|
||||||
|
- **OCR 识别**: 植物图片识别功能。
|
||||||
|
- **个人中心 (Personal)**: 用户个人数据管理。
|
||||||
|
|
||||||
|
## 基本流程 (Workflow)
|
||||||
|
|
||||||
|
### 1. 植物领养流程
|
||||||
|
1. **浏览图鉴**: 用户查看植物图鉴 (Library),选择感兴趣的植物。
|
||||||
|
2. **发起领养**: 用户发起领养请求 (Claim),系统根据配置 (`claimPlantApi`) 处理领养逻辑。
|
||||||
|
3. **生成记录**: 成功后在 "我的领养" (`MyClaim`) 中生成记录,并可能关联具体的植物 ID。
|
||||||
|
|
||||||
|
### 2. 订单处理流程
|
||||||
|
1. **创建订单**: 用户进行支付或确认领养后生成订单。
|
||||||
|
2. **后台发货**: 管理员在后台查看订单列表 (`OrderPage`),核实信息后进行发货操作 (`ShipOrder`)。
|
||||||
|
3. **订单完成**: 用户确认收货,订单流程结束。
|
||||||
|
4. **数据导出**: 运营人员可将订单数据导出 (`ExportOrder`) 用于分析。
|
||||||
|
|
||||||
|
### 3. 内容社区流程
|
||||||
|
1. **发布内容**: 用户上传植物照片或心得,发布帖子 (`Post`)。
|
||||||
|
2. **互动交流**: 其他用户浏览帖子,并进行评论 (`PostComment`) 或点赞。
|
||||||
|
3. **审核管理**: 管理员可对违规内容进行管理或删除。
|
||||||
|
|
||||||
|
## 部署说明 (参考)
|
||||||
|
项目支持跨平台编译:
|
||||||
|
- **Windows**: `GOOS=windows GOARCH=amd64 go build -o planting-fun.exe`
|
||||||
|
- **Linux**: `GOOS=linux GOARCH=amd64 go build -o sundynix-plant`
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
//"sundynix-go/api/v1/plant"
|
||||||
|
"sundynix-go/api/v1/plant"
|
||||||
|
"sundynix-go/api/v1/system"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ApiGroupApp = new(ApiGroup)
|
||||||
|
|
||||||
|
// ApiGroup 路由组
|
||||||
|
type ApiGroup struct {
|
||||||
|
SystemApiGroup system.ApiGroup
|
||||||
|
PlantApiGroup plant.ApiGroup
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package plant
|
||||||
|
|
||||||
|
import "sundynix-go/service"
|
||||||
|
|
||||||
|
type ApiGroup struct {
|
||||||
|
MyPlantApi
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
plantService = service.GroupApp.PlantServiceGroup.MyPlantService
|
||||||
|
)
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
package plant
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"sundynix-go/global"
|
||||||
|
"sundynix-go/model/commom/request"
|
||||||
|
"sundynix-go/model/commom/response"
|
||||||
|
plantReq "sundynix-go/model/plant/request"
|
||||||
|
"sundynix-go/utils/auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MyPlantApi struct{}
|
||||||
|
|
||||||
|
// AddPlant 添加植物
|
||||||
|
// @Tags 我的植物
|
||||||
|
// @Summary 添加植物
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @accept json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body plantReq.CreateMyPlant true "创建植物"
|
||||||
|
// @Success 200 {string} string "{"success":true,"data":{},"msg":"添加成功"}"
|
||||||
|
// @Router /plant/add [post]
|
||||||
|
func (a *MyPlantApi) AddPlant(c *gin.Context) {
|
||||||
|
var req plantReq.CreateMyPlant
|
||||||
|
err := c.ShouldBindJSON(&req)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg("请求参数错误", c)
|
||||||
|
}
|
||||||
|
userId := auth.GetUserId(c)
|
||||||
|
err = plantService.AddPlant(req, userId)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("添加植物失败", zap.Error(err))
|
||||||
|
response.FailWithMsg("添加失败", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithMsg("添加成功", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlantPage 植物列表
|
||||||
|
// @Tags 我的植物
|
||||||
|
// @Summary 植物列表
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @accept json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body request.PageInfo true "分页获取植物列表"
|
||||||
|
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||||
|
// @Router /plant/page [post]
|
||||||
|
func (a *MyPlantApi) PlantPage(c *gin.Context) {
|
||||||
|
var req request.PageInfo
|
||||||
|
err := c.ShouldBindJSON(&req)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg("请求参数错误", c)
|
||||||
|
}
|
||||||
|
userId := auth.GetUserId(c)
|
||||||
|
list, total, err := plantService.PlantPage(req, userId)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("获取植物列表失败", zap.Error(err))
|
||||||
|
response.FailWithMsg("获取植物列表失败", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(response.PageResult{
|
||||||
|
List: list,
|
||||||
|
Total: total,
|
||||||
|
Page: req.Current,
|
||||||
|
PageSize: req.PageSize,
|
||||||
|
}, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlantDetail 植物详情
|
||||||
|
// @Tags 我的植物
|
||||||
|
// @Summary ById植物详情
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param id query string true "id"
|
||||||
|
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取ById成功"}"
|
||||||
|
// @Router /plant/detail [get]
|
||||||
|
func (a *MyPlantApi) PlantDetail(c *gin.Context) {
|
||||||
|
id := c.Query("id")
|
||||||
|
plant, err := plantService.PlantDetail(id)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg("获取失败", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(plant, c)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatePlant 修改植物
|
||||||
|
// @Tags 我的植物
|
||||||
|
// @Summary 修改ById植物
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @accept json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body plantReq.UpdateMyPlant true "修改ById植物"
|
||||||
|
// @Success 200 {string} string "{"success":true,"data":{},"msg":"修改ById成功"}"
|
||||||
|
// @Router /plant/update [post]
|
||||||
|
func (a *MyPlantApi) UpdatePlant(c *gin.Context) {
|
||||||
|
var req plantReq.UpdateMyPlant
|
||||||
|
err := c.ShouldBindJSON(&req)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg("请求参数错误", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = plantService.UpdatePlant(req)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("更新植物失败", zap.Error(err))
|
||||||
|
response.FailWithMsg("更新植物失败", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithMsg("更新植物成功", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TodayTask 今日任务
|
||||||
|
// @Tags 我的植物
|
||||||
|
// @Summary 今日任务
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @accept json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||||
|
// @Router /plant/todayTask [get]
|
||||||
|
func (a *MyPlantApi) TodayTask(c *gin.Context) {
|
||||||
|
userId := auth.GetUserId(c)
|
||||||
|
list, err := plantService.TodayTask(userId)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("获取今日任务失败", zap.Error(err))
|
||||||
|
response.FailWithMsg("获取今日任务失败", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(response.ListResult{
|
||||||
|
List: list,
|
||||||
|
}, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompleteTask plantReq.CompleteMyPlant
|
||||||
|
// @Tags 我的植物
|
||||||
|
// @Summary 完成任务
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @accept json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body plantReq.CompleteTask true "完成任务"
|
||||||
|
// @Success 200 {string} string "{"success":true,"data":{},"msg":"完成任务"}"
|
||||||
|
// @Router /plant/completeTask [post]
|
||||||
|
func (a *MyPlantApi) CompleteTask(c *gin.Context) {
|
||||||
|
var req plantReq.CompleteTask
|
||||||
|
err := c.ShouldBindJSON(&req)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg("请求参数错误", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = plantService.CompleteTask(req)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("完成任务失败", zap.Error(err))
|
||||||
|
response.FailWithMsg("完成任务失败", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithMsg("完成任务成功", c)
|
||||||
|
}
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sundynix-go/global"
|
||||||
|
"sundynix-go/model/commom/response"
|
||||||
|
"sundynix-go/model/system"
|
||||||
|
systemReq "sundynix-go/model/system/request"
|
||||||
|
systemRes "sundynix-go/model/system/response"
|
||||||
|
"sundynix-go/utils/auth"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/mojocn/base64Captcha"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
var store = base64Captcha.DefaultMemStore
|
||||||
|
|
||||||
|
type AuthApi struct{}
|
||||||
|
|
||||||
|
// Login
|
||||||
|
// @Tags 登录相关
|
||||||
|
// @Summary pc登录
|
||||||
|
// @accept application/json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body systemReq.Login true "用户名, 密码, 验证码,验证码id"
|
||||||
|
// @Success 200 {object} response.Response{msg=string} "登录成功"
|
||||||
|
// @Router /auth/login [post]
|
||||||
|
func (a *AuthApi) Login(c *gin.Context) {
|
||||||
|
var l systemReq.Login
|
||||||
|
err := c.ShouldBindJSON(&l)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if l.CaptchaId != "" && l.Captcha != "" && store.Verify(l.CaptchaId, l.Captcha, true) {
|
||||||
|
u := &system.User{Account: l.Account, Password: l.Password}
|
||||||
|
user, err := userService.Login(u)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("登录失败! 用户名不存在或者密码错误!", zap.Error(err))
|
||||||
|
response.FailWithMsg("用户名不存在或者密码错误", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a.GetToken(c, *user)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.FailWithMsg("验证码错误", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logout
|
||||||
|
// @Tags 登录相关
|
||||||
|
// @Summary pc登出
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Produce application/json
|
||||||
|
// @Success 200 {object} response.Response{msg=string} "登出成功"
|
||||||
|
// @Router /auth/logout [get]
|
||||||
|
func (a *AuthApi) Logout(c *gin.Context) {
|
||||||
|
token := auth.GetToken(c)
|
||||||
|
userId := auth.GetUserId(c)
|
||||||
|
err := jwtService.PutBlacklist(userId, token)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("登出失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg("登出失败", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
auth.ClearToken(c)
|
||||||
|
response.OkWithMsg("登出成功", c)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Captcha
|
||||||
|
// @Tags 登录相关
|
||||||
|
// @Summary 获取验证码
|
||||||
|
// @Produce application/json
|
||||||
|
// @Success 200 {object} response.Response{data=systemRes.CaptchaRes} "获取验证码"
|
||||||
|
// @Router /auth/captcha [get]
|
||||||
|
func (a *AuthApi) Captcha(c *gin.Context) {
|
||||||
|
var driver = base64Captcha.DriverString{
|
||||||
|
Height: 80,
|
||||||
|
Width: 240,
|
||||||
|
NoiseCount: 2,
|
||||||
|
ShowLineOptions: 4,
|
||||||
|
Length: 4,
|
||||||
|
Source: "1234567890",
|
||||||
|
}
|
||||||
|
|
||||||
|
cp := base64Captcha.NewCaptcha(&driver, store)
|
||||||
|
id, b64s, _, err := cp.Generate()
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("GenerateCaptcha err", zap.Error(err))
|
||||||
|
response.FailWithMsg("GenerateCaptcha err", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(systemRes.CaptchaRes{
|
||||||
|
CaptchaId: id,
|
||||||
|
Captcha: b64s,
|
||||||
|
}, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthApi) GetToken(c *gin.Context, user system.User) {
|
||||||
|
token, claims, err := auth.GetLoginToken(&user)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("GetToken err", zap.Error(err))
|
||||||
|
response.FailWithMsg("GetToken err", c)
|
||||||
|
}
|
||||||
|
response.OkWithData(systemRes.LoginResponse{
|
||||||
|
ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
|
||||||
|
Token: token,
|
||||||
|
User: user,
|
||||||
|
}, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MiniLogin
|
||||||
|
// @Tags 登录相关
|
||||||
|
// @Summary 小程序登录
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param code query string true "code"
|
||||||
|
// @Success 200 {object} response.Response{data=systemRes.LoginResponse} "小程序登录"
|
||||||
|
// @Router /auth/miniLogin [get]
|
||||||
|
func (a *AuthApi) MiniLogin(c *gin.Context) {
|
||||||
|
jsCode := c.Query("code")
|
||||||
|
user, err := userService.MiniLogin(jsCode)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("登录失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg("登录失败", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a.GetToken(c, *user)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPhone
|
||||||
|
// @Tags 登录相关
|
||||||
|
// @Summary 获取手机号
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param code query string true "code"
|
||||||
|
// @Param openId query string true "openId"
|
||||||
|
// @Router /auth/getPhone [get]
|
||||||
|
func (a *AuthApi) GetPhone(c *gin.Context) {
|
||||||
|
jsCode := c.Query("code")
|
||||||
|
openId := c.Query("openId")
|
||||||
|
user, err := userService.LoginByPhone(jsCode, openId)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("登录失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg("登录失败", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(user, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLocation
|
||||||
|
// @Tags 登录相关
|
||||||
|
// @Summary 获取位置信息
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param longitude query string true "longitude"
|
||||||
|
// @Param latitude query string true "latitude"
|
||||||
|
// @Router /auth/getLocation [get]
|
||||||
|
func (a *AuthApi) GetLocation(c *gin.Context) {
|
||||||
|
longitude := c.Query("longitude") //经度
|
||||||
|
latitude := c.Query("latitude")
|
||||||
|
location, err := userService.GetLocation(longitude, latitude)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("获取位置信息失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg("获取位置信息失败", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(location, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWeather
|
||||||
|
// @Tags 登录相关
|
||||||
|
// @Summary 获取天气信息
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param adcode query string true "adcode"
|
||||||
|
// @Router /auth/getWeather [get]
|
||||||
|
func (a *AuthApi) GetWeather(c *gin.Context) {
|
||||||
|
value := c.Query("adcode")
|
||||||
|
weather, err := userService.GetWeather(value)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("获取天气信息失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg("获取天气信息失败", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(weather, c)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import "sundynix-go/service"
|
||||||
|
|
||||||
|
type ApiGroup struct {
|
||||||
|
AuthApi
|
||||||
|
UserApi
|
||||||
|
ClientApi
|
||||||
|
RoleApi
|
||||||
|
MenuApi
|
||||||
|
OperationRecordApi
|
||||||
|
OssApi
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
jwtService = service.GroupApp.SystemServiceGroup.JwtService
|
||||||
|
userService = service.GroupApp.SystemServiceGroup.UserService
|
||||||
|
clientService = service.GroupApp.SystemServiceGroup.ClientService
|
||||||
|
roleService = service.GroupApp.SystemServiceGroup.RoleService
|
||||||
|
menuService = service.GroupApp.SystemServiceGroup.MenuService
|
||||||
|
operationRecordService = service.GroupApp.SystemServiceGroup.OperationRecordService
|
||||||
|
ossService = service.GroupApp.SystemServiceGroup.OssService
|
||||||
|
)
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sundynix-go/global"
|
||||||
|
"sundynix-go/model/commom/request"
|
||||||
|
"sundynix-go/model/commom/response"
|
||||||
|
"sundynix-go/model/system"
|
||||||
|
sysReq "sundynix-go/model/system/request"
|
||||||
|
sysResp "sundynix-go/model/system/response"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OssApi struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadFile
|
||||||
|
// @tags 文件相关
|
||||||
|
// @Summary 文件上传
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @accept multipart/form-data
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param file formData file true "上传文件"
|
||||||
|
// @Success 200 {object} response.Response{msg=string} "上传文件"
|
||||||
|
// @router /oss/upload [post]
|
||||||
|
func (o *OssApi) UploadFile(c *gin.Context) {
|
||||||
|
var file system.Oss
|
||||||
|
multipartFile, header, err := c.Request.FormFile("file")
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("接收文件失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg("接收文件失败!", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
file, err = ossService.Upload(multipartFile, header) //上传完成后拿到文件信息
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("上传文件失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg("上传文件失败!", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(sysResp.UploadFileResponse{File: file}, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteFile
|
||||||
|
// @tags 文件相关
|
||||||
|
// @Summary 删除文件
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @accept application/json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body request.IdsReq true "批量删除文件"
|
||||||
|
// @Success 200 {object} response.Response{msg=string} "删除文件"
|
||||||
|
// @router /oss/delete [post]
|
||||||
|
func (o *OssApi) DeleteFile(c *gin.Context) {
|
||||||
|
var ids request.IdsReq
|
||||||
|
err := c.ShouldBindJSON(&ids)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = ossService.DeleteFileByIds(ids)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("删除文件失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg("删除文件失败!", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithMsg("删除文件成功!", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileList
|
||||||
|
// @tags 文件相关
|
||||||
|
// @Summary 文件列表
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Accept application/json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body sysReq.GetOssFileList true "文件列表"
|
||||||
|
// @Success 200 {object} response.Response{data=string} "文件列表"
|
||||||
|
// @router /oss/getFileList [post]
|
||||||
|
func (o *OssApi) GetFileList(c *gin.Context) {
|
||||||
|
var pageInfo sysReq.GetOssFileList
|
||||||
|
err := c.ShouldBindJSON(&pageInfo)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list, total, err := ossService.GetFileList(pageInfo)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("获取文件列表失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg("获取文件列表失败!", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(response.PageResult{
|
||||||
|
List: list,
|
||||||
|
Total: total,
|
||||||
|
Page: pageInfo.Current,
|
||||||
|
PageSize: pageInfo.PageSize,
|
||||||
|
}, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detail
|
||||||
|
// @tags 文件相关
|
||||||
|
// @Summary 文件详情
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param id query string true "文件id"
|
||||||
|
// @Success 200 {object} response.Response{data=string} "文件详情"
|
||||||
|
// @router /oss/detail [get]
|
||||||
|
func (o *OssApi) Detail(c *gin.Context) {
|
||||||
|
id := c.Query("id")
|
||||||
|
file, err := ossService.GetById(id)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("获取文件详情失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(file, c)
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sundynix-go/global"
|
||||||
|
"sundynix-go/model/commom/request"
|
||||||
|
"sundynix-go/model/commom/response"
|
||||||
|
"sundynix-go/model/system"
|
||||||
|
systemReq "sundynix-go/model/system/request"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ClientApi struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveClient
|
||||||
|
// @Tags 客户端管理
|
||||||
|
// @Summary 创建client
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @accept application/json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body system.Client true "client"
|
||||||
|
// @Success 200 {object} response.Response{msg=string} "创建client"
|
||||||
|
// @Router /client/save [post]
|
||||||
|
func (s *ClientApi) SaveClient(c *gin.Context) {
|
||||||
|
var client system.Client
|
||||||
|
err := c.ShouldBindJSON(&client)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = clientService.SaveClient(client)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("保存客户端失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithMsg("保存客户端成功!", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateClient
|
||||||
|
// @Tags 客户端管理
|
||||||
|
// @Summary 更新client
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @accept application/json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body system.Client true "client"
|
||||||
|
// @Success 200 {object} response.Response{msg=string} "更新client"
|
||||||
|
// @Router /client/update [post]
|
||||||
|
func (s *ClientApi) UpdateClient(c *gin.Context) {
|
||||||
|
var client system.Client
|
||||||
|
err := c.ShouldBindJSON(&client)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = clientService.UpdateClient(client)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("更新客户端失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithMsg("更新客户端成功!", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetClientList
|
||||||
|
// @Tags 客户端管理
|
||||||
|
// @Summary 获取client列表
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @accept application/json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body systemReq.GetClientList true "client"
|
||||||
|
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取client列表"
|
||||||
|
// @Router /client/getClientList [post]
|
||||||
|
func (s *ClientApi) GetClientList(c *gin.Context) {
|
||||||
|
var pageInfo systemReq.GetClientList
|
||||||
|
err := c.ShouldBindJSON(&pageInfo)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list, total, err := clientService.GetClientList(pageInfo)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("获取客户端列表失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(response.PageResult{
|
||||||
|
List: list,
|
||||||
|
Total: total,
|
||||||
|
Page: pageInfo.Current,
|
||||||
|
PageSize: pageInfo.PageSize,
|
||||||
|
}, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete
|
||||||
|
// @Tags 客户端管理
|
||||||
|
// @Summary 删除client
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @accept application/json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body request.IdsReq true "ids"
|
||||||
|
// @Success 200 {object} response.Response{msg=string} "删除client"
|
||||||
|
// @Router /client/delete [post]
|
||||||
|
func (s *ClientApi) Delete(c *gin.Context) {
|
||||||
|
var ids request.IdsReq
|
||||||
|
err := c.ShouldBindJSON(&ids)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = clientService.DeleteClientByIds(ids)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("删除客户端失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithMsg("删除客户端成功!", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detail
|
||||||
|
// @Tags 客户端管理
|
||||||
|
// @Summary 获取client详情
|
||||||
|
// @Description id获取详情
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param id query string true "id"
|
||||||
|
// @Success 200 {object} response.Response{data=system.Client,msg=string} "获取client详情"
|
||||||
|
// @Router /client/detail [get]
|
||||||
|
func (s *ClientApi) Detail(c *gin.Context) {
|
||||||
|
id := c.Query("id")
|
||||||
|
client, err := clientService.GetClientById(id)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("获取客户端详情失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(client, c)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sundynix-go/global"
|
||||||
|
"sundynix-go/model/commom/response"
|
||||||
|
"sundynix-go/model/system"
|
||||||
|
systemReq "sundynix-go/model/system/request"
|
||||||
|
"sundynix-go/utils/auth"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MenuApi struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveMenu
|
||||||
|
// @Tags 菜单管理
|
||||||
|
// @Summary 新增菜单
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @accept application/json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body system.Menu false "menu"
|
||||||
|
// @Success 200 {object} response.Response{msg=string} "新建菜单/按钮"
|
||||||
|
// @Router /menu/save [post]
|
||||||
|
func (m *MenuApi) SaveMenu(c *gin.Context) {
|
||||||
|
var menu system.Menu
|
||||||
|
err := c.ShouldBindJSON(&menu)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg("参数错误:"+err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = menuService.SaveMenu(menu); err != nil {
|
||||||
|
global.Logger.Error("保存菜单失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
response.OkWithMsg("保存菜单成功!", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateMenu
|
||||||
|
// @Tags 菜单管理
|
||||||
|
// @Summary 更新菜单
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @accept application/json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body system.Menu false "menu"
|
||||||
|
// @Success 200 {object} response.Response{msg=string} "更新菜单"
|
||||||
|
// @Router /menu/update [post]
|
||||||
|
func (m *MenuApi) UpdateMenu(c *gin.Context) {
|
||||||
|
var menu system.Menu
|
||||||
|
err := c.ShouldBindJSON(&menu)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg("参数错误:"+err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = menuService.UpdateMenu(&menu); err != nil {
|
||||||
|
global.Logger.Error("更新菜单失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
response.OkWithMsg("更新菜单成功!", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteMenu
|
||||||
|
// @Tags 菜单管理
|
||||||
|
// @Summary 删除menu
|
||||||
|
// @Description 删除menu
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param id query string true "id"
|
||||||
|
// @Success 200 {object} response.Response{msg=string} "详情"
|
||||||
|
// @Router /menu/delete [get]
|
||||||
|
func (m *MenuApi) DeleteMenu(c *gin.Context) {
|
||||||
|
id := c.Query("id")
|
||||||
|
err := menuService.DeleteMenu(id)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("删除菜单失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithMsg("删除菜单成功!", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detail
|
||||||
|
// @Tags 菜单管理
|
||||||
|
// @Summary 获取menu详情
|
||||||
|
// @Description id获取详情
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param id query string true "id"
|
||||||
|
// @Success 200 {object} response.Response{data=system.Menu,msg=string} "详情"
|
||||||
|
// @Router /menu/detail [get]
|
||||||
|
func (m *MenuApi) Detail(c *gin.Context) {
|
||||||
|
id := c.Query("id")
|
||||||
|
menu, err := menuService.GetMenuById(id)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("获取菜单详情失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(menu, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllMenuTree
|
||||||
|
// @Tags 菜单管理
|
||||||
|
// @Summary 获取所有菜单树
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param data body systemReq.GetMenuTree true "菜单信息"
|
||||||
|
// @Success 200 {object} response.Response{data=[]system.Menu,msg=string} "获取所有菜单树"
|
||||||
|
// @Router /menu/getAllMenuTree [post]
|
||||||
|
func (m *MenuApi) GetAllMenuTree(c *gin.Context) {
|
||||||
|
var param systemReq.GetMenuTree
|
||||||
|
err := c.ShouldBindJSON(¶m)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
menus, err := menuService.GetAllMenuTree(param.Category, param.ParentId)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("获取菜单树结构失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(menus, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserMenuTree
|
||||||
|
// @Tags 菜单管理
|
||||||
|
// @Summary 用户菜单数据
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} response.Response{data=[]system.Menu,msg=string} "用户菜单数据"
|
||||||
|
// @Router /menu/getUserMenuTree [get]
|
||||||
|
func (m *MenuApi) GetUserMenuTree(c *gin.Context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route
|
||||||
|
// @Tags 菜单管理
|
||||||
|
// @Summary 用户路由
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} response.Response{data=[]system.Menu,msg=string} "用户route"
|
||||||
|
// @Router /menu/route [get]
|
||||||
|
func (m *MenuApi) Route(c *gin.Context) {
|
||||||
|
userId := auth.GetUserId(c)
|
||||||
|
routes, err := menuService.GetUserRoutes(userId)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("获取用户菜单失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(routes, c)
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sundynix-go/global"
|
||||||
|
"sundynix-go/model/commom/request"
|
||||||
|
"sundynix-go/model/commom/response"
|
||||||
|
"sundynix-go/model/system"
|
||||||
|
systemReq "sundynix-go/model/system/request"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OperationRecordApi struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OperationRecordApi) CreateOperationRecord(c *gin.Context) {
|
||||||
|
var sysOperationRecord system.SysOperationRecord
|
||||||
|
err := c.ShouldBindJSON(&sysOperationRecord)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = operationRecordService.CreateOperationRecord(sysOperationRecord)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("创建操作记录失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithMsg("创建操作记录成功!", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OperationRecordApi) GetRecordList(c *gin.Context) {
|
||||||
|
var pageInfo systemReq.GetOperationRecordList
|
||||||
|
err := c.ShouldBindJSON(&pageInfo)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list, total, err := operationRecordService.GetRecordList(pageInfo)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("获取操作记录列表失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(response.PageResult{
|
||||||
|
List: list,
|
||||||
|
Total: total,
|
||||||
|
Page: pageInfo.Current,
|
||||||
|
PageSize: pageInfo.PageSize,
|
||||||
|
}, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OperationRecordApi) GetRecordById(c *gin.Context) {
|
||||||
|
id := c.Query("id")
|
||||||
|
record, err := operationRecordService.GetRecordById(id)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("获取操作记录详情失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(record, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OperationRecordApi) DeleteRecordsByIds(c *gin.Context) {
|
||||||
|
var ids request.IdsReq
|
||||||
|
err := c.ShouldBindJSON(&ids)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = operationRecordService.DeleteRecordsByIds(ids)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("删除操作记录失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithMsg("删除操作记录成功!", c)
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sundynix-go/global"
|
||||||
|
"sundynix-go/model/commom/request"
|
||||||
|
"sundynix-go/model/commom/response"
|
||||||
|
"sundynix-go/model/system"
|
||||||
|
systemreq "sundynix-go/model/system/request"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RoleApi struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveRole
|
||||||
|
// @tags 角色管理
|
||||||
|
// @Summary 创建角色
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param data body system.Role true "角色信息"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Router /role/save [post]
|
||||||
|
func (a *RoleApi) SaveRole(context *gin.Context) {
|
||||||
|
var role system.Role
|
||||||
|
err := context.ShouldBindJSON(&role)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg("参数错误"+err.Error(), context)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = roleService.SaveRole(role)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("保存角色失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), context)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithMsg("保存角色成功!", context)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateRole
|
||||||
|
// @tags 角色管理
|
||||||
|
// @Summary 修改角色
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @accept application/json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body system.Role true "角色ID"
|
||||||
|
// @Success 200 {object} response.Response"
|
||||||
|
// @Router /role/update [post]
|
||||||
|
func (a *RoleApi) UpdateRole(context *gin.Context) {
|
||||||
|
var role system.Role
|
||||||
|
err := context.ShouldBindJSON(&role)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg(err.Error(), context)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = roleService.UpdateRole(role)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("更新角色失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), context)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithMsg("更新角色成功!", context)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRoleList
|
||||||
|
// @tags 角色管理
|
||||||
|
// @Summary 获取角色列表
|
||||||
|
// @Description 获取角色列表
|
||||||
|
// @Accept application/json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body systemreq.GetRoleList true "页码, 每页大小, 搜索条件"
|
||||||
|
// @success 200 {object} response.Response{data=response.PageResult,msg=string} "获取角色列表,返回包括列表,总数,页码,每页大小"
|
||||||
|
// @Router /role/getRoleList [post]
|
||||||
|
func (a *RoleApi) GetRoleList(c *gin.Context) {
|
||||||
|
var pageInfo systemreq.GetRoleList
|
||||||
|
err := c.ShouldBindJSON(&pageInfo)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list, total, err := roleService.GetRoleList(pageInfo)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("获取角色列表失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
}
|
||||||
|
response.OkWithData(response.PageResult{
|
||||||
|
List: list,
|
||||||
|
Total: total,
|
||||||
|
Page: pageInfo.Current,
|
||||||
|
PageSize: pageInfo.PageSize,
|
||||||
|
}, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete
|
||||||
|
// @Tags 角色管理
|
||||||
|
// @Summary 删除角色
|
||||||
|
// @Description 删除角色
|
||||||
|
// @Accept application/json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body request.IdsReq true "批量删除角色"
|
||||||
|
// @Success 200 {object} response.Response{msg=string} "删除角色"
|
||||||
|
// @Router /role/delete [post]
|
||||||
|
func (a *RoleApi) Delete(context *gin.Context) {
|
||||||
|
var ids request.IdsReq
|
||||||
|
err := context.ShouldBindJSON(&ids)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg(err.Error(), context)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = roleService.DeleteRoleByIds(ids)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("删除角色失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), context)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithMsg("删除角色成功!", context)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detail
|
||||||
|
// @Tags 角色管理
|
||||||
|
// @Summary 角色详情
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param id query string true "id"
|
||||||
|
// @Success 200 {object} response.Response{data=system.Role} "角色详情"
|
||||||
|
// @Router /role/detail [get]
|
||||||
|
func (a *RoleApi) Detail(context *gin.Context) {
|
||||||
|
id := context.Query("id")
|
||||||
|
role, err := roleService.GetRoleById(id)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("获取角色详情失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), context)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(role, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GrantMenu
|
||||||
|
// @tags 角色管理
|
||||||
|
// @Summary 授权菜单给角色
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @accept application/json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body systemreq.GrantMenu true "授权菜单给角色"
|
||||||
|
// @success 200 {object} response.Response "授权菜单给角色"
|
||||||
|
// @Router /role/grantMenu [post]
|
||||||
|
func (a *RoleApi) GrantMenu(c *gin.Context) {
|
||||||
|
var grantMenu systemreq.GrantMenu
|
||||||
|
err := c.ShouldBindJSON(&grantMenu)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = roleService.GrantMenu(grantMenu.RoleId, grantMenu.MenuIds)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("授权菜单失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithMsg("授权菜单成功!", c)
|
||||||
|
}
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sundynix-go/global"
|
||||||
|
"sundynix-go/model/commom/request"
|
||||||
|
"sundynix-go/model/commom/response"
|
||||||
|
"sundynix-go/model/system"
|
||||||
|
systemReq "sundynix-go/model/system/request"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserApi struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveUser
|
||||||
|
// @tags 用户管理
|
||||||
|
// @Summary 新增用户
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param data body system.User true "用户信息"
|
||||||
|
// @Success 200 {object} response.Response "{"code": 200, "data": {}, "msg": "添加成功"}"
|
||||||
|
// @Router /user/save [post]
|
||||||
|
func (u *UserApi) SaveUser(c *gin.Context) {
|
||||||
|
var user system.User
|
||||||
|
err := c.ShouldBindJSON(&user)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg("参数错误:"+err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = userService.SaveUser(user); err != nil {
|
||||||
|
global.Logger.Error("保存用户失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
} else {
|
||||||
|
response.OkWithMsg("保存用户成功!", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateUser
|
||||||
|
// @tags 用户管理
|
||||||
|
// @Summary 更新用户
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @accept application/json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body system.User true "用户ID,用户信息"
|
||||||
|
// @Success 200 {object} response.Response "{"code": 200, "data": [...]}"
|
||||||
|
// @Router /user/update [post]
|
||||||
|
func (u *UserApi) UpdateUser(c *gin.Context) {
|
||||||
|
var user system.User
|
||||||
|
err := c.ShouldBindJSON(&user)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg("参数错误:"+err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = userService.UpdateUser(&user); err != nil {
|
||||||
|
global.Logger.Error("更新用户失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
} else {
|
||||||
|
response.OkWithMsg("更新用户成功!", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserList
|
||||||
|
// @tags 用户管理
|
||||||
|
// @Summary 获取用户列表
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @accept application/json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body systemReq.GetUserList true "页码, 每页大小, 搜索条件"
|
||||||
|
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取用户列表,返回包括列表,总数,页码,每页大小"
|
||||||
|
// @Router /user/getUserList [post]
|
||||||
|
func (u *UserApi) GetUserList(c *gin.Context) {
|
||||||
|
var pageInfo systemReq.GetUserList
|
||||||
|
err := c.ShouldBindJSON(&pageInfo)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list, total, err := userService.GetUserList(pageInfo)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("获取用户列表失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(response.PageResult{
|
||||||
|
List: list,
|
||||||
|
Total: total,
|
||||||
|
Page: pageInfo.Current,
|
||||||
|
PageSize: pageInfo.PageSize,
|
||||||
|
}, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete
|
||||||
|
// @Tags 用户管理
|
||||||
|
// @Summary 删除用户
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @accept application/json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body request.IdsReq true "批量删除用户"
|
||||||
|
// @Success 200 {object} response.Response{msg=string} "删除用户"
|
||||||
|
// @Router /user/delete [post]
|
||||||
|
func (u *UserApi) Delete(c *gin.Context) {
|
||||||
|
var ids request.IdsReq
|
||||||
|
err := c.ShouldBindJSON(&ids)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = userService.DeleteUserByIds(ids)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("删除用户失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithMsg("删除用户成功!", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detail
|
||||||
|
// @Tags 用户管理
|
||||||
|
// @Summary 获取用户详情
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param id query string true "id"
|
||||||
|
// @Success 200 {object} response.Response{data=system.User} "获取用户详情成功"
|
||||||
|
// @Router /user/detail [get]
|
||||||
|
func (u *UserApi) Detail(c *gin.Context) {
|
||||||
|
id := c.Query("id")
|
||||||
|
user, err := userService.GetUserById(id)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("获取用户详情失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(user, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangePassword
|
||||||
|
// @Tags 用户管理
|
||||||
|
// @Summary 修改密码
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Description 修改密码
|
||||||
|
// @accept json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body request.ChangePwd true "用户id"
|
||||||
|
// @Success 200 {object} response.Response{data=system.User} "修改密码成功"
|
||||||
|
// @Router /user/changePassword [post]
|
||||||
|
func (u *UserApi) ChangePassword(c *gin.Context) {
|
||||||
|
var changePwd systemReq.ChangePwd
|
||||||
|
err := c.ShouldBindJSON(&changePwd)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = userService.ChangePassword(changePwd.Id, changePwd.NewPwd)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("修改密码失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithMsg("修改密码成功", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GrantRole
|
||||||
|
// @Tags 用户管理
|
||||||
|
// @Summary 给用户分配角色
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @accept application/json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body systemReq.GrantRole true "用户ID, 角色ID"
|
||||||
|
// @Success 200 {object} response.Response "{"code": 200, "data": [...]}"
|
||||||
|
// @Router /user/grantRole [post]
|
||||||
|
func (u *UserApi) GrantRole(c *gin.Context) {
|
||||||
|
var grantRole systemReq.GrantRole
|
||||||
|
err := c.ShouldBindJSON(&grantRole)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = roleService.GrantRole(grantRole.UserId, grantRole.RoleIds)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("授权角色失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithMsg("授权角色成功!", c)
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
system:
|
||||||
|
addr: 8888
|
||||||
|
db-type: mysql
|
||||||
|
router-prefix: ""
|
||||||
|
enable-captcha: 0
|
||||||
|
oss-type: minio
|
||||||
|
# oss-type: tencent-cos
|
||||||
|
|
||||||
|
jwt:
|
||||||
|
buffer-time: 2h
|
||||||
|
expires-time: 2h
|
||||||
|
issuer: sundynix
|
||||||
|
signing-key: 9149f2eb-d517-4a50-a03a-231dbcf0d872
|
||||||
|
|
||||||
|
|
||||||
|
# 植趣微信小程序
|
||||||
|
mini-program:
|
||||||
|
app-id: wxb463820bf36dd5d6
|
||||||
|
app-secret: 731784a74c76c6d31fa00bb847af2c7d
|
||||||
|
|
||||||
|
# 植趣服务号
|
||||||
|
service-account:
|
||||||
|
app-id: wxc236cddde8e7f863
|
||||||
|
app-secret: 26c1fcecfc98a748d8916355623c975c
|
||||||
|
|
||||||
|
wechat-pay:
|
||||||
|
mch-id: 1735188493 # 商户号
|
||||||
|
mch-certificate-serial-number: 3725BFCA9CA3AF819AEC5D0CB7D3540BBC67F2CF # 商户证书序列号
|
||||||
|
public-key-id: PUB_KEY_ID_0117351884932025120900181833003602 # 商户APIv3密钥对应的公钥
|
||||||
|
mch-api-v3-key: a1B2c3D4e5F6g7H8i9J0k1L2m3N4o5P6 # 商户APIv3密钥
|
||||||
|
private-key-path: /Users/blizzard/privateFolder/cert/apiclient_key.pem # 商户APIv3密钥对应的私钥
|
||||||
|
public-key-path: /Users/blizzard/privateFolder/cert/pub_key.pem # 商户APIv3密钥对应的公钥
|
||||||
|
notify-url: https://prod.sundynix.cn/api/wechatpay/notify # 微信支付结果通知回调地址
|
||||||
|
|
||||||
|
|
||||||
|
minio:
|
||||||
|
access-key-id: qP5QXP3g6Axw1hkwX21Y
|
||||||
|
access-key-secret: sddT6J3S6yDn9m1wfth0pzelPg9KWmbHjMAUF5S9
|
||||||
|
base-path: ""
|
||||||
|
bucket-name: sundynix-plant
|
||||||
|
bucket-url: https://res.sundynix.cn/sundynix-plant
|
||||||
|
endpoint: 129.28.103.17:3407
|
||||||
|
use-ssl: false
|
||||||
|
|
||||||
|
rocket-mq:
|
||||||
|
name-space: 192.168.100.140:9876
|
||||||
|
endpoint: 192.168.100.140:8081 # 5.x版本使用了proxy 默认proxy地址是8081
|
||||||
|
consumer-group: sundynix-plant
|
||||||
|
topic: sundynix-plant-generate-library
|
||||||
|
access-key: ""
|
||||||
|
secret-key: ""
|
||||||
|
enable-ssl: false
|
||||||
|
log-enabled: false
|
||||||
|
|
||||||
|
mysql:
|
||||||
|
config: charset=utf8mb4&parseTime=True&loc=Local
|
||||||
|
db-name: sundynix_zq_go
|
||||||
|
engine: ""
|
||||||
|
log-mode: error
|
||||||
|
log-zap: true
|
||||||
|
max-idle-conns: 10
|
||||||
|
max-open-conns: 100
|
||||||
|
host: 129.28.103.17
|
||||||
|
port: "3413"
|
||||||
|
prefix: "sundynix_"
|
||||||
|
singular: true
|
||||||
|
user: root
|
||||||
|
password: root
|
||||||
|
|
||||||
|
redis:
|
||||||
|
addr: 127.0.0.1:6379
|
||||||
|
clusteraddrs:
|
||||||
|
- 172.21.0.3:7000
|
||||||
|
- 172.21.0.4:7001
|
||||||
|
- 172.21.0.2:7002
|
||||||
|
db: 1
|
||||||
|
# name: ""
|
||||||
|
# password: "sundynix"
|
||||||
|
cluster: false
|
||||||
|
|
||||||
|
zap:
|
||||||
|
director: log
|
||||||
|
encode-level: LowercaseColorLevelEncoder
|
||||||
|
format: console
|
||||||
|
level: debug
|
||||||
|
log-in-console: true
|
||||||
|
prefix: '[sundynix-zq-server]'
|
||||||
|
retention-day: 5
|
||||||
|
show-line: true
|
||||||
|
stacktrace-key: stacktrace
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
system:
|
||||||
|
addr: 8889
|
||||||
|
db-type: mysql
|
||||||
|
router-prefix: ""
|
||||||
|
enable-captcha: 0
|
||||||
|
oss-type: minio
|
||||||
|
# oss-type: tencent-cos
|
||||||
|
|
||||||
|
jwt:
|
||||||
|
buffer-time: 2h
|
||||||
|
expires-time: 2h
|
||||||
|
issuer: sundynix
|
||||||
|
signing-key: 9149f2eb-d517-4a50-a03a-231dbcf0d872
|
||||||
|
|
||||||
|
|
||||||
|
# 植趣微信小程序
|
||||||
|
mini-program:
|
||||||
|
app-id: wxb463820bf36dd5d6
|
||||||
|
app-secret: 731784a74c76c6d31fa00bb847af2c7d
|
||||||
|
|
||||||
|
# 植趣服务号
|
||||||
|
service-account:
|
||||||
|
app-id: wxc236cddde8e7f863
|
||||||
|
app-secret: 26c1fcecfc98a748d8916355623c975c
|
||||||
|
|
||||||
|
minio:
|
||||||
|
access-key-id: qP5QXP3g6Axw1hkwX21Y
|
||||||
|
access-key-secret: sddT6J3S6yDn9m1wfth0pzelPg9KWmbHjMAUF5S9
|
||||||
|
base-path: ""
|
||||||
|
bucket-name: sundynix-plant
|
||||||
|
bucket-url: https://res.sundynix.cn/sundynix-plant
|
||||||
|
endpoint: 129.28.103.17:3407
|
||||||
|
use-ssl: false
|
||||||
|
|
||||||
|
rocket-mq:
|
||||||
|
name-space: 192.168.100.140:9876
|
||||||
|
endpoint: 192.168.100.140:8081 # 5.x版本使用了proxy 默认proxy地址是8081
|
||||||
|
consumer-group: sundynix-plant
|
||||||
|
topic: sundynix-plant-generate-library
|
||||||
|
access-key: ""
|
||||||
|
secret-key: ""
|
||||||
|
enable-ssl: false
|
||||||
|
log-enabled: false
|
||||||
|
|
||||||
|
mysql:
|
||||||
|
config: charset=utf8mb4&parseTime=True&loc=Local
|
||||||
|
db-name: sundynix_plant
|
||||||
|
engine: ""
|
||||||
|
log-mode: error
|
||||||
|
log-zap: true
|
||||||
|
max-idle-conns: 10
|
||||||
|
max-open-conns: 100
|
||||||
|
host: 192.168.100.127
|
||||||
|
port: "3306"
|
||||||
|
prefix: "sundynix_"
|
||||||
|
singular: true
|
||||||
|
user: root
|
||||||
|
password: root
|
||||||
|
|
||||||
|
redis:
|
||||||
|
addr: 192.168.100.127:6379
|
||||||
|
clusteraddrs:
|
||||||
|
- 172.21.0.3:7000
|
||||||
|
- 172.21.0.4:7001
|
||||||
|
- 172.21.0.2:7002
|
||||||
|
db: 5
|
||||||
|
name: ""
|
||||||
|
password: ""
|
||||||
|
cluster: false
|
||||||
|
|
||||||
|
zap:
|
||||||
|
director: log
|
||||||
|
encode-level: LowercaseColorLevelEncoder
|
||||||
|
format: console
|
||||||
|
level: debug
|
||||||
|
log-in-console: true
|
||||||
|
prefix: '[sundynix-go]'
|
||||||
|
retention-day: 5
|
||||||
|
show-line: true
|
||||||
|
stacktrace-key: stacktrace
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
JWT JWT `mapstructure:"jwt" json:"jwt" yaml:"jwt"`
|
||||||
|
System System `mapstructure:"system" json:"system" yaml:"system"`
|
||||||
|
Mysql Mysql `mapstructure:"mysql" json:"mysql" yaml:"mysql"`
|
||||||
|
Pgsql Pgsql `mapstructure:"pgsql" json:"pgsql" yaml:"pgsql"`
|
||||||
|
Sqlite Sqlite `mapstructure:"sqlite" json:"sqlite" yaml:"sqlite"`
|
||||||
|
Redis Redis `mapstructure:"redis" json:"redis" yaml:"redis"`
|
||||||
|
Zap Zap `mapstructure:"zap" json:"zap" yaml:"zap"`
|
||||||
|
|
||||||
|
Minio Minio `mapstructure:"minio" json:"minio" yaml:"minio"`
|
||||||
|
RocketMQConfig RocketMQConfig `mapstructure:"rocket-mq" json:"rocket-mq" yaml:"rocket-mq"`
|
||||||
|
TencentCOS TencentCOS `mapstructure:"tencent-cos" json:"tencent-cos" yaml:"tencent-cos"`
|
||||||
|
|
||||||
|
MiniProgram MiniProgram `mapstructure:"mini-program" json:"mini-program" yaml:"mini-program"` //小程序
|
||||||
|
ServiceAccount ServiceAccount `mapstructure:"service-account" json:"service-account" yaml:"service-account"` //服务号
|
||||||
|
WechatPay WechatPay `mapstructure:"wechat-pay" json:"wechat-pay" yaml:"wechat-pay"` //微信支付
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type DB struct {
|
||||||
|
Host string `mapstructure:"host" json:"host" yaml:"host"`
|
||||||
|
Port int `mapstructure:"port" json:"port" yaml:"port"`
|
||||||
|
User string `mapstructure:"user" json:"user" yaml:"user"`
|
||||||
|
Password string `mapstructure:"password" json:"password" yaml:"password"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type JWT struct {
|
||||||
|
SigningKey string `mapstructure:"signing-key" json:"signing-key" yaml:"signing-key"` // jwt签名
|
||||||
|
ExpiresTime string `mapstructure:"expires-time" json:"expires-time" yaml:"expires-time"` // 过期时间
|
||||||
|
BufferTime string `mapstructure:"buffer-time" json:"buffer-time" yaml:"buffer-time"` // 缓冲时间
|
||||||
|
Issuer string `mapstructure:"issuer" json:"issuer" yaml:"issuer"` // 签发者
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type Redis struct {
|
||||||
|
Name string `mapstructure:"name" json:"name" yaml:"name"` // 代表当前实例的名字
|
||||||
|
Addr string `mapstructure:"addr" json:"addr" yaml:"addr"` // 服务器地址:端口
|
||||||
|
Password string `mapstructure:"password" json:"password" yaml:"password"` // 密码
|
||||||
|
DB int `mapstructure:"db" json:"db" yaml:"db"` // 单实例模式下redis的哪个数据库
|
||||||
|
Cluster bool `mapstructure:"cluster" json:"cluster" yaml:"cluster"` // 是否使用集群模式
|
||||||
|
ClusterAddrs []string `mapstructure:"clusterAddrs" json:"clusterAddrs" yaml:"clusterAddrs"` // 集群模式下的节点地址列表
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gorm.io/gorm/logger"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DsnProvider interface {
|
||||||
|
Dsn() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Embeded 结构体可以压平到上一层,从而保持 config 文件的结构和原来一样
|
||||||
|
// 见 playground: https://go.dev/play/p/KIcuhqEoxmY
|
||||||
|
|
||||||
|
// GeneralDB 也被 Pgsql 和 Mysql 原样使用
|
||||||
|
type GeneralDB struct {
|
||||||
|
Prefix string `mapstructure:"prefix" json:"prefix" yaml:"prefix"` // 数据库前缀
|
||||||
|
Port string `mapstructure:"port" json:"port" yaml:"port"` // 数据库端口
|
||||||
|
Config string `mapstructure:"config" json:"config" yaml:"config"` // 高级配置
|
||||||
|
Dbname string `mapstructure:"db-name" json:"db-name" yaml:"db-name"` // 数据库名
|
||||||
|
User string `mapstructure:"user" json:"user" yaml:"user"` // 数据库账号
|
||||||
|
Password string `mapstructure:"password" json:"password" yaml:"password"` // 数据库密码
|
||||||
|
Host string `mapstructure:"host" json:"host" yaml:"host"` // 数据库地址
|
||||||
|
Engine string `mapstructure:"engine" json:"engine" yaml:"engine" default:"InnoDB"` // 数据库引擎,默认InnoDB
|
||||||
|
LogMode string `mapstructure:"log-mode" json:"log-mode" yaml:"log-mode"` // 是否开启Gorm全局日志
|
||||||
|
MaxIdleConns int `mapstructure:"max-idle-conns" json:"max-idle-conns" yaml:"max-idle-conns"` // 空闲中的最大连接数
|
||||||
|
MaxOpenConns int `mapstructure:"max-open-conns" json:"max-open-conns" yaml:"max-open-conns"` // 打开到数据库的最大连接数
|
||||||
|
Singular bool `mapstructure:"singular" json:"singular" yaml:"singular"` // 是否开启全局禁用复数,true表示开启
|
||||||
|
LogZap bool `mapstructure:"log-zap" json:"log-zap" yaml:"log-zap"` // 是否通过zap写入日志文件
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c GeneralDB) LogLevel() logger.LogLevel {
|
||||||
|
switch strings.ToLower(c.LogMode) {
|
||||||
|
case "silent", "Silent":
|
||||||
|
return logger.Silent
|
||||||
|
case "error", "Error":
|
||||||
|
return logger.Error
|
||||||
|
case "warn", "Warn":
|
||||||
|
return logger.Warn
|
||||||
|
case "info", "Info":
|
||||||
|
return logger.Info
|
||||||
|
default:
|
||||||
|
return logger.Info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type SpecializedDB struct {
|
||||||
|
Type string `mapstructure:"type" json:"type" yaml:"type"`
|
||||||
|
AliasName string `mapstructure:"alias-name" json:"alias-name" yaml:"alias-name"`
|
||||||
|
GeneralDB `yaml:",inline" mapstructure:",squash"`
|
||||||
|
Disable bool `mapstructure:"disable" json:"disable" yaml:"disable"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type Mysql struct {
|
||||||
|
GeneralDB `yaml:",inline" mapstructure:",squash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Mysql) Dsn() string {
|
||||||
|
return m.User + ":" + m.Password + "@tcp(" + m.Host + ":" + m.Port + ")/" + m.Dbname + "?" + m.Config
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type Pgsql struct {
|
||||||
|
GeneralDB `yaml:",inline" mapstructure:",squash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dsn 基于配置文件获取 dsn
|
||||||
|
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||||
|
func (p *Pgsql) Dsn() string {
|
||||||
|
return "host=" + p.Host + " user=" + p.User + " password=" + p.Password + " dbname=" + p.Dbname + " port=" + p.Port + " " + p.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// LinkDsn 根据 dbname 生成 dsn
|
||||||
|
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||||
|
func (p *Pgsql) LinkDsn(dbname string) string {
|
||||||
|
return "host=" + p.Host + " user=" + p.User + " password=" + p.Password + " dbname=" + dbname + " port=" + p.Port + " " + p.Config
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Sqlite struct {
|
||||||
|
GeneralDB `yaml:",inline" mapstructure:",squash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sqlite) Dsn() string {
|
||||||
|
return filepath.Join(s.Host, s.Dbname+".db")
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type MiniProgram struct {
|
||||||
|
AppId string `mapstructure:"app-id" json:"app-id" yaml:"app-id"`
|
||||||
|
AppSecret string `mapstructure:"app-secret" json:"app-secret" yaml:"app-secret"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type Minio struct {
|
||||||
|
Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"`
|
||||||
|
AccessKeyId string `mapstructure:"access-key-id" json:"access-key-id" yaml:"access-key-id"`
|
||||||
|
AccessKeySecret string `mapstructure:"access-key-secret" json:"access-key-secret" yaml:"access-key-secret"`
|
||||||
|
BucketName string `mapstructure:"bucket-name" json:"bucket-name" yaml:"bucket-name"`
|
||||||
|
UseSSL bool `mapstructure:"use-ssl" json:"use-ssl" yaml:"use-ssl"`
|
||||||
|
BasePath string `mapstructure:"base-path" json:"base-path" yaml:"base-path"`
|
||||||
|
BucketUrl string `mapstructure:"bucket-url" json:"bucket-url" yaml:"bucket-url"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type TencentCOS struct {
|
||||||
|
Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"`
|
||||||
|
Region string `mapstructure:"region" json:"region" yaml:"region"`
|
||||||
|
SecretID string `mapstructure:"secret-id" json:"secret-id" yaml:"secret-id"`
|
||||||
|
SecretKey string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"`
|
||||||
|
BaseURL string `mapstructure:"base-url" json:"base-url" yaml:"base-url"`
|
||||||
|
PathPrefix string `mapstructure:"path-prefix" json:"path-prefix" yaml:"path-prefix"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type RocketMQConfig struct {
|
||||||
|
NameSpace string `mapstructure:"name-space" json:"nameSpace" yaml:"name-space"`
|
||||||
|
Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"`
|
||||||
|
ConsumerGroup string `mapstructure:"consumer-group" json:"consumerGroup" yaml:"consumer-group"`
|
||||||
|
AccessKey string `mapstructure:"access-key" json:"accessKey" yaml:"access-key"`
|
||||||
|
SecretKey string `mapstructure:"secret-key" json:"secretKey" yaml:"secret-key"`
|
||||||
|
Topic string `mapstructure:"topic" json:"topic" yaml:"topic"`
|
||||||
|
EnableSSL bool `mapstructure:"enable-ssl" json:"enableSSL" yaml:"enable-ssl"`
|
||||||
|
LogEnabled bool `mapstructure:"log-enabled" json:"logEnabled" yaml:"log-enabled"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type ServiceAccount struct {
|
||||||
|
AppId string `mapstructure:"app-id" json:"app-id" yaml:"app-id"`
|
||||||
|
AppSecret string `mapstructure:"app-secret" json:"app-secret" yaml:"app-secret"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type System struct {
|
||||||
|
Addr int `mapstructure:"addr" json:"addr" yaml:"addr"`
|
||||||
|
DbType string `mapstructure:"db-type" json:"db-type" yaml:"db-type"`
|
||||||
|
RouterPrefix string `mapstructure:"router-prefix" json:"router-prefix" yaml:"router-prefix"`
|
||||||
|
EnableCaptcha int `mapstructure:"enable-captcha" json:"enable-captcha" yaml:"enable-captcha"`
|
||||||
|
OssType string `mapstructure:"oss-type" json:"oss-type" yaml:"oss-type"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
// WechatPay 微信支付
|
||||||
|
type WechatPay struct {
|
||||||
|
MchId string `mapstructure:"mch-id" json:"mch-id" yaml:"mch-id"`
|
||||||
|
PublicKeyId string `mapstructure:"public-key-id" json:"public-key-id" yaml:"public-key-id"`
|
||||||
|
MchCertificateSerialNumber string `mapstructure:"mch-certificate-serial-number" json:"mch-certificate-serial-number" yaml:"mch-certificate-serial-number"`
|
||||||
|
MchAPIv3Key string `mapstructure:"mch-api-v3-key" json:"mch-api-v3-key" yaml:"mch-api-v3-key"`
|
||||||
|
PrivateKeyPath string `mapstructure:"private-key-path" json:"private-key-path" yaml:"private-key-path"`
|
||||||
|
PublicKeyPath string `mapstructure:"public-key-path" json:"public-key-path" yaml:"public-key-path"`
|
||||||
|
NotifyUrl string `mapstructure:"notify-url" json:"notify-url" yaml:"notify-url"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Zap struct {
|
||||||
|
Level string `mapstructure:"level" json:"level" yaml:"level"`
|
||||||
|
Prefix string `mapstructure:"prefix" json:"prefix" yaml:"prefix"`
|
||||||
|
Format string `mapstructure:"format" json:"format" yaml:"format"`
|
||||||
|
Director string `mapstructure:"director" json:"director" yaml:"director"`
|
||||||
|
EncodeLevel string `mapstructure:"encode-level" json:"encode-level" yaml:"encode-level"`
|
||||||
|
StacktraceKey string `mapstructure:"stacktrace-key" json:"stacktrace-key" yaml:"stacktrace-key"`
|
||||||
|
ShowLine bool `mapstructure:"show-line" json:"show-line" yaml:"show-line"`
|
||||||
|
LogInConsole bool `mapstructure:"log-in-console" json:"log-in-console" yaml:"log-in-console"`
|
||||||
|
RetentionDay int `mapstructure:"retention-day" json:"retention-day" yaml:"retention-day"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Levels 返回一个基于 Zap 实例中配置的日志级别的 zapcore.Level 切片。
|
||||||
|
// 该切片从配置的日志级别开始,包含所有更高严重性级别,直到 FatalLevel。
|
||||||
|
// 如果无法解析配置的日志级别,则默认使用 DebugLevel。
|
||||||
|
//
|
||||||
|
// 返回值:
|
||||||
|
// - []zapcore.Level: 包含从配置的日志级别(或解析失败时的 DebugLevel)到 FatalLevel 的所有日志级别的切片。
|
||||||
|
func (c *Zap) Levels() []zapcore.Level {
|
||||||
|
// 初始化一个容量为 7 的空切片,用于存储日志级别。
|
||||||
|
levels := make([]zapcore.Level, 0, 7)
|
||||||
|
|
||||||
|
// 解析配置的日志级别。如果解析失败,则默认使用 DebugLevel。
|
||||||
|
level, err := zapcore.ParseLevel(c.Level)
|
||||||
|
if err != nil {
|
||||||
|
level = zapcore.DebugLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从解析的(或默认的)日志级别开始,迭代到 FatalLevel,并将每个级别追加到切片中 按照日志级别分片存储
|
||||||
|
for ; level <= zapcore.FatalLevel; level++ {
|
||||||
|
levels = append(levels, level)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回填充好的日志级别切片
|
||||||
|
return levels
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encoder 返回一个 zapcore.Encoder,用于编码日志记录。
|
||||||
|
func (c *Zap) Encoder() zapcore.Encoder {
|
||||||
|
config := zapcore.EncoderConfig{
|
||||||
|
TimeKey: "time",
|
||||||
|
NameKey: "name",
|
||||||
|
LevelKey: "level",
|
||||||
|
CallerKey: "caller",
|
||||||
|
MessageKey: "message",
|
||||||
|
StacktraceKey: c.StacktraceKey,
|
||||||
|
LineEnding: zapcore.DefaultLineEnding,
|
||||||
|
EncodeTime: func(t time.Time, encoder zapcore.PrimitiveArrayEncoder) {
|
||||||
|
encoder.AppendString(c.Prefix + t.Format("2006-01-02 15:04:05"))
|
||||||
|
},
|
||||||
|
EncodeLevel: c.LevelEncoder(),
|
||||||
|
EncodeCaller: zapcore.FullCallerEncoder,
|
||||||
|
EncodeDuration: zapcore.SecondsDurationEncoder,
|
||||||
|
}
|
||||||
|
if c.Format == "json" {
|
||||||
|
return zapcore.NewJSONEncoder(config)
|
||||||
|
}
|
||||||
|
return zapcore.NewConsoleEncoder(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Zap) LevelEncoder() zapcore.LevelEncoder {
|
||||||
|
switch {
|
||||||
|
case c.EncodeLevel == "LowercaseLevelEncoder":
|
||||||
|
return zapcore.LowercaseLevelEncoder
|
||||||
|
case c.EncodeLevel == "LowercaseColorLevelEncoder":
|
||||||
|
return zapcore.LowercaseColorLevelEncoder
|
||||||
|
case c.EncodeLevel == "CapitalLevelEncoder":
|
||||||
|
return zapcore.CapitalLevelEncoder
|
||||||
|
case c.EncodeLevel == "CapitalColorLevelEncoder":
|
||||||
|
return zapcore.CapitalColorLevelEncoder
|
||||||
|
default:
|
||||||
|
return zapcore.LowercaseLevelEncoder
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
const (
|
||||||
|
ConfigDefaultFile = "config-dev.yaml"
|
||||||
|
ConfigProdFile = "config-prod.yaml"
|
||||||
|
ConfigDebugFile = "config-debug.yaml"
|
||||||
|
ConfigReleaseFile = "config-release.yaml"
|
||||||
|
)
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cutter struct {
|
||||||
|
level string // 日志级别
|
||||||
|
layout string //时间格式
|
||||||
|
formats []string //自定义参数
|
||||||
|
director string //日志文件夹
|
||||||
|
retentionDay int //保留天数
|
||||||
|
file *os.File //文件
|
||||||
|
mutex *sync.RWMutex // 读写锁
|
||||||
|
}
|
||||||
|
|
||||||
|
type CutterOption func(c *Cutter)
|
||||||
|
|
||||||
|
// 设置时间格式
|
||||||
|
func CutterWithLayout(layout string) CutterOption {
|
||||||
|
return func(c *Cutter) {
|
||||||
|
c.layout = layout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化参数
|
||||||
|
func CutterWithFormats(format ...string) CutterOption {
|
||||||
|
return func(c *Cutter) {
|
||||||
|
if len(format) > 0 {
|
||||||
|
c.formats = format
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCutter 创建一个新的 Cutter 实例,用于管理日志文件的切割和保留。
|
||||||
|
//
|
||||||
|
// 参数:
|
||||||
|
// - directory: 日志文件存储的目录路径。
|
||||||
|
// - level: 日志级别,用于标识日志的严重程度。
|
||||||
|
// - retentionDay: 日志文件保留的天数,超过该天数的日志文件将被删除。
|
||||||
|
// - options: 可选的 CutterOption 函数,用于对 Cutter 实例进行额外的配置。
|
||||||
|
//
|
||||||
|
// 返回值:
|
||||||
|
// - *Cutter: 返回一个初始化后的 Cutter 实例。
|
||||||
|
func NewCutter(directory string, level string, retentionDay int, options ...CutterOption) *Cutter {
|
||||||
|
// 初始化 Cutter 实例,设置日志级别、目录、保留天数以及互斥锁
|
||||||
|
rotate := &Cutter{
|
||||||
|
level: level,
|
||||||
|
director: directory,
|
||||||
|
retentionDay: retentionDay,
|
||||||
|
mutex: new(sync.RWMutex),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应用所有传入的 CutterOption 配置函数
|
||||||
|
for i := 0; i < len(options); i++ {
|
||||||
|
options[i](rotate)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rotate
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write 方法将给定的字节数据写入到日志文件中。该方法会确保日志文件的目录存在,并根据配置的格式生成文件名。
|
||||||
|
// 如果日志文件已经存在,数据将被追加到文件末尾。如果文件不存在,则会创建新文件。
|
||||||
|
// 该方法还会定期清理超过保留天数的日志文件夹。
|
||||||
|
//
|
||||||
|
// 参数:
|
||||||
|
// - bytes: 要写入的字节数据。
|
||||||
|
//
|
||||||
|
// 返回值:
|
||||||
|
// - n: 成功写入的字节数。
|
||||||
|
// - err: 如果发生错误,返回错误信息;否则返回 nil。
|
||||||
|
func (c *Cutter) Write(bytes []byte) (n int, err error) {
|
||||||
|
// 加锁以确保并发安全
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer func() {
|
||||||
|
// 在函数结束时关闭文件并释放锁
|
||||||
|
if c.file != nil {
|
||||||
|
_ = c.file.Close()
|
||||||
|
c.file = nil
|
||||||
|
}
|
||||||
|
c.mutex.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 生成日志文件名
|
||||||
|
length := len(c.formats)
|
||||||
|
values := make([]string, 0, 3+length)
|
||||||
|
values = append(values, c.director)
|
||||||
|
if c.layout != "" {
|
||||||
|
values = append(values, time.Now().Format(c.layout))
|
||||||
|
}
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
values = append(values, c.formats[i])
|
||||||
|
}
|
||||||
|
values = append(values, c.level+".log")
|
||||||
|
filename := filepath.Join(values...)
|
||||||
|
|
||||||
|
// 确保日志文件所在的目录存在
|
||||||
|
directory := filepath.Dir(filename)
|
||||||
|
err = os.MkdirAll(directory, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理超过保留天数的日志文件夹
|
||||||
|
err = removeNDaysFolders(c.director, c.retentionDay)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开或创建日志文件,并追加写入数据
|
||||||
|
c.file, err = os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将数据写入文件并返回写入的字节数
|
||||||
|
return c.file.Write(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync 方法用于将当前文件的内容同步到磁盘,确保所有缓冲区的数据都写入磁盘。
|
||||||
|
// 该方法在调用时会先获取互斥锁,以确保在同步过程中不会有其他操作干扰。
|
||||||
|
// 如果当前 Cutter 实例中的文件对象不为 nil,则调用文件对象的 Sync 方法进行同步操作。
|
||||||
|
// 如果文件对象为 nil,则直接返回 nil,表示无需同步。
|
||||||
|
//
|
||||||
|
// 返回值:
|
||||||
|
// - error: 如果同步过程中发生错误,则返回该错误;否则返回 nil。
|
||||||
|
func (c *Cutter) Sync() error {
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
|
|
||||||
|
// 如果文件对象存在,则调用其 Sync 方法进行同步
|
||||||
|
if c.file != nil {
|
||||||
|
return c.file.Sync()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件对象不存在,直接返回 nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeNDaysFolders 删除指定目录下,指定天数前的文件夹
|
||||||
|
func removeNDaysFolders(dir string, days int) error {
|
||||||
|
if days <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cutoff := time.Now().AddDate(0, 0, -days)
|
||||||
|
return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if info.IsDir() && info.ModTime().Before(cutoff) && path != dir {
|
||||||
|
err = os.RemoveAll(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
"os"
|
||||||
|
"sundynix-go/global"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ZapCore struct {
|
||||||
|
level zapcore.Level
|
||||||
|
zapcore.Core
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewZapCore 创建一个 zapcore.Core
|
||||||
|
func NewZapCore(level zapcore.Level) *ZapCore {
|
||||||
|
entity := &ZapCore{level: level}
|
||||||
|
syncer := entity.WriteSyncer()
|
||||||
|
levelEnabler := zap.LevelEnablerFunc(func(l zapcore.Level) bool { return l == level })
|
||||||
|
entity.Core = zapcore.NewCore(global.Config.Zap.Encoder(), syncer, levelEnabler)
|
||||||
|
return entity
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteSyncer 创建一个 zapcore.WriteSyncer
|
||||||
|
func (z *ZapCore) WriteSyncer(formats ...string) zapcore.WriteSyncer {
|
||||||
|
cutter := NewCutter(
|
||||||
|
global.Config.Zap.Director,
|
||||||
|
z.level.String(),
|
||||||
|
global.Config.Zap.RetentionDay,
|
||||||
|
CutterWithLayout(time.DateOnly),
|
||||||
|
CutterWithFormats(formats...),
|
||||||
|
)
|
||||||
|
if global.Config.Zap.LogInConsole {
|
||||||
|
multiSyncer := zapcore.NewMultiWriteSyncer(os.Stdout, cutter)
|
||||||
|
return zapcore.AddSync(multiSyncer)
|
||||||
|
}
|
||||||
|
return zapcore.AddSync(cutter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *ZapCore) Enabled(level zapcore.Level) bool {
|
||||||
|
return z.level == level
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *ZapCore) With(fields []zapcore.Field) zapcore.Core {
|
||||||
|
return z.Core.With(fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *ZapCore) Check(entry zapcore.Entry, check *zapcore.CheckedEntry) *zapcore.CheckedEntry {
|
||||||
|
if z.Enabled(entry.Level) {
|
||||||
|
return check.AddCore(entry, z)
|
||||||
|
}
|
||||||
|
return check
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *ZapCore) Write(entry zapcore.Entry, fields []zapcore.Field) error {
|
||||||
|
for i := 0; i < len(fields); i++ {
|
||||||
|
if fields[i].Key == "business" || fields[i].Key == "folder" || fields[i].Key == "directory" {
|
||||||
|
syncer := z.WriteSyncer(fields[i].String)
|
||||||
|
z.Core = zapcore.NewCore(global.Config.Zap.Encoder(), syncer, z.level)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return z.Core.Write(entry, fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *ZapCore) Sync() error {
|
||||||
|
return z.Core.Sync()
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"os"
|
||||||
|
"sundynix-go/core/internal"
|
||||||
|
"sundynix-go/global"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Viper() *viper.Viper {
|
||||||
|
config := getConfigPath()
|
||||||
|
v := viper.New()
|
||||||
|
v.SetConfigFile(config)
|
||||||
|
v.SetConfigType("yaml")
|
||||||
|
|
||||||
|
err := v.ReadInConfig()
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("fatal error config file: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
//监听配置文件变化并热加载
|
||||||
|
v.WatchConfig()
|
||||||
|
|
||||||
|
//监听配置文件变化事件
|
||||||
|
v.OnConfigChange(func(e fsnotify.Event) {
|
||||||
|
fmt.Println("config file changed:", e.Name)
|
||||||
|
if err = v.Unmarshal(&global.Config); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//将读取的配置信息反序列化到全局变量Conf中
|
||||||
|
if err = v.Unmarshal(&global.Config); err != nil {
|
||||||
|
panic(fmt.Errorf("fatal error unmarshal config: %w", err))
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取配置文件路径 优先级: 命令行 > 环境变量 > 默认值
|
||||||
|
func getConfigPath() (config string) {
|
||||||
|
flag.StringVar(&config, "c", "", "choose config file")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// 命令行参数不为空 将值赋值于config
|
||||||
|
if config != "" {
|
||||||
|
fmt.Printf("正在使用命令行的 '-c' 参数传递的值, config 的路径为 %s\n", config)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err := os.Stat(config)
|
||||||
|
if err != nil || os.IsNotExist(err) {
|
||||||
|
config = internal.ConfigDefaultFile
|
||||||
|
fmt.Printf("配置文件路径不存在, 使用默认配置文件路径: %s\n", config)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
"os"
|
||||||
|
"sundynix-go/core/internal"
|
||||||
|
"sundynix-go/global"
|
||||||
|
"sundynix-go/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Zap 函数用于初始化并返回一个 zap.Logger 实例。
|
||||||
|
// 该函数会检查日志目录是否存在,如果不存在则创建该目录。
|
||||||
|
// 根据配置中的日志级别,创建对应的 zapcore.Core,并将它们合并为一个 zap.Logger。
|
||||||
|
// 如果配置中启用了显示行号,则会在日志中添加调用者信息。
|
||||||
|
// 返回值:
|
||||||
|
// - logger: 初始化后的 zap.Logger 实例,用于记录日志。
|
||||||
|
func Zap() (logger *zap.Logger) {
|
||||||
|
// 检查日志目录是否存在,如果不存在则创建
|
||||||
|
if ok, _ := utils.PathExist(global.Config.Zap.Director); !ok {
|
||||||
|
fmt.Printf("日志目录 %v 不存在,创建中...\n", global.Config.Zap.Director)
|
||||||
|
_ = os.Mkdir(global.Config.Zap.Director, os.ModePerm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取配置中的日志级别,并初始化对应的 zapcore.Core
|
||||||
|
levels := global.Config.Zap.Levels()
|
||||||
|
length := len(levels)
|
||||||
|
cores := make([]zapcore.Core, 0, length)
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
core := internal.NewZapCore(levels[i])
|
||||||
|
cores = append(cores, core)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将所有的 zapcore.Core 合并为一个 zap.Logger
|
||||||
|
logger = zap.New(zapcore.NewTee(cores...))
|
||||||
|
|
||||||
|
// 如果配置中启用了显示行号,则添加调用者信息
|
||||||
|
if global.Config.Zap.ShowLine {
|
||||||
|
logger = logger.WithOptions(zap.AddCaller())
|
||||||
|
}
|
||||||
|
|
||||||
|
return logger
|
||||||
|
}
|
||||||
+7084
File diff suppressed because it is too large
Load Diff
+7058
File diff suppressed because it is too large
Load Diff
+4336
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
|||||||
|
package global
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sundynix-go/config"
|
||||||
|
"sundynix-go/utils/timer"
|
||||||
|
|
||||||
|
"github.com/bsm/redislock"
|
||||||
|
"github.com/redis/go-redis/v9"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"github.com/wechatpay-apiv3/wechatpay-go/core"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"golang.org/x/sync/singleflight"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 全局变量 加载在内存中
|
||||||
|
var (
|
||||||
|
Viper *viper.Viper
|
||||||
|
Logger *zap.Logger
|
||||||
|
Config *config.Config
|
||||||
|
DB *gorm.DB
|
||||||
|
Redis redis.UniversalClient
|
||||||
|
Locker *redislock.Client // 分布式锁
|
||||||
|
ConcurrencyControl = &singleflight.Group{}
|
||||||
|
Timer timer.Timer = timer.NewTimerTask()
|
||||||
|
WxPayClient *core.Client
|
||||||
|
)
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package global
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sundynix-go/utils/uniqueid"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BaseModel struct {
|
||||||
|
Id string `gorm:"size:50;primaryKey" json:"id"` // 主键ID
|
||||||
|
CreatedAt time.Time `json:"createdAt" gorm:"autoCreateTime"`
|
||||||
|
UpdatedAt time.Time `json:"updatedAt" gorm:"autoCreateTime;autoUpdateTime"`
|
||||||
|
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // 删除时间
|
||||||
|
CreatedAtStr string `json:"createdAtStr" gorm:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeCreate 定义一个钩子,在创建之前执行自动插入字段
|
||||||
|
func (model *BaseModel) BeforeCreate(db *gorm.DB) (err error) {
|
||||||
|
//生成主键的string uniqueid
|
||||||
|
db.Statement.SetColumn("id", uniqueid.GenerateId())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeUpdate 定义一个钩子,在更新之前执行自动更新字段
|
||||||
|
func (model *BaseModel) BeforeUpdate(db *gorm.DB) (err error) {
|
||||||
|
db.Statement.SetColumn("updated_at", time.Now())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterFind 钩子,在查询之后执行
|
||||||
|
func (model *BaseModel) AfterFind(tx *gorm.DB) (err error) {
|
||||||
|
model.CreatedAtStr = model.CreatedAt.Format("2006-01-02 15:04:05")
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
module sundynix-go
|
||||||
|
|
||||||
|
go 1.24.0
|
||||||
|
|
||||||
|
toolchain go1.24.2
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/bsm/redislock v0.9.4
|
||||||
|
github.com/fsnotify/fsnotify v1.8.0
|
||||||
|
github.com/gin-gonic/gin v1.10.1
|
||||||
|
github.com/glebarez/sqlite v1.11.0
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.3
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/minio/minio-go/v7 v7.0.95
|
||||||
|
github.com/mojocn/base64Captcha v1.3.8
|
||||||
|
github.com/redis/go-redis/v9 v9.7.3
|
||||||
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
|
github.com/spf13/viper v1.20.1
|
||||||
|
github.com/swaggo/files v1.0.1
|
||||||
|
github.com/swaggo/gin-swagger v1.6.1
|
||||||
|
github.com/swaggo/swag v1.16.6
|
||||||
|
github.com/tencentyun/cos-go-sdk-v5 v0.7.70
|
||||||
|
github.com/wechatpay-apiv3/wechatpay-go v0.2.21
|
||||||
|
github.com/xuri/excelize/v2 v2.10.0
|
||||||
|
go.uber.org/zap v1.27.0
|
||||||
|
golang.org/x/crypto v0.45.0
|
||||||
|
golang.org/x/sync v0.18.0
|
||||||
|
gorm.io/driver/mysql v1.5.7
|
||||||
|
gorm.io/driver/postgres v1.5.11
|
||||||
|
gorm.io/gorm v1.26.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
|
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||||
|
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||||
|
github.com/bytedance/sonic v1.14.1 // indirect
|
||||||
|
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/clbanning/mxj v1.8.4 // indirect
|
||||||
|
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
|
||||||
|
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||||
|
github.com/glebarez/go-sqlite v1.22.0 // indirect
|
||||||
|
github.com/go-ini/ini v1.67.0 // indirect
|
||||||
|
github.com/go-openapi/jsonpointer v0.22.0 // indirect
|
||||||
|
github.com/go-openapi/jsonreference v0.21.1 // indirect
|
||||||
|
github.com/go-openapi/spec v0.21.0 // indirect
|
||||||
|
github.com/go-openapi/swag v0.24.1 // indirect
|
||||||
|
github.com/go-openapi/swag/cmdutils v0.24.0 // indirect
|
||||||
|
github.com/go-openapi/swag/conv v0.24.0 // indirect
|
||||||
|
github.com/go-openapi/swag/fileutils v0.24.0 // indirect
|
||||||
|
github.com/go-openapi/swag/jsonname v0.24.0 // indirect
|
||||||
|
github.com/go-openapi/swag/jsonutils v0.24.0 // indirect
|
||||||
|
github.com/go-openapi/swag/loading v0.24.0 // indirect
|
||||||
|
github.com/go-openapi/swag/mangling v0.24.0 // indirect
|
||||||
|
github.com/go-openapi/swag/netutils v0.24.0 // indirect
|
||||||
|
github.com/go-openapi/swag/stringutils v0.24.0 // indirect
|
||||||
|
github.com/go-openapi/swag/typeutils v0.24.0 // indirect
|
||||||
|
github.com/go-openapi/swag/yamlutils v0.24.0 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/go-playground/validator/v10 v10.27.0 // indirect
|
||||||
|
github.com/go-sql-driver/mysql v1.9.2 // indirect
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||||
|
github.com/google/go-querystring v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
|
github.com/jackc/pgx/v5 v5.7.4 // indirect
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||||
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
|
github.com/mailru/easyjson v0.9.1 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/minio/crc64nvme v1.0.2 // indirect
|
||||||
|
github.com/minio/md5-simd v1.1.2 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/mozillazg/go-httpheader v0.2.1 // indirect
|
||||||
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
|
github.com/philhofer/fwd v1.2.0 // indirect
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
|
github.com/richardlehane/mscfb v1.0.4 // indirect
|
||||||
|
github.com/richardlehane/msoleps v1.0.4 // indirect
|
||||||
|
github.com/rs/xid v1.6.0 // indirect
|
||||||
|
github.com/sagikazarmark/locafero v0.7.0 // indirect
|
||||||
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
|
github.com/spf13/afero v1.12.0 // indirect
|
||||||
|
github.com/spf13/cast v1.7.1 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.6 // indirect
|
||||||
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
|
github.com/tiendc/go-deepcopy v1.7.1 // indirect
|
||||||
|
github.com/tinylib/msgp v1.3.0 // indirect
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||||
|
github.com/xuri/efp v0.0.1 // indirect
|
||||||
|
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect
|
||||||
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
|
golang.org/x/arch v0.21.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
|
||||||
|
golang.org/x/image v0.26.0 // indirect
|
||||||
|
golang.org/x/mod v0.29.0 // indirect
|
||||||
|
golang.org/x/net v0.47.0 // indirect
|
||||||
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
|
golang.org/x/text v0.31.0 // indirect
|
||||||
|
golang.org/x/tools v0.38.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.9 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
modernc.org/libc v1.64.0 // indirect
|
||||||
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
|
modernc.org/memory v1.10.0 // indirect
|
||||||
|
modernc.org/sqlite v1.37.0 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,360 @@
|
|||||||
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
|
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||||
|
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||||
|
github.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw=
|
||||||
|
github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw=
|
||||||
|
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/bsm/redislock v0.9.4 h1:X/Wse1DPpiQgHbVYRE9zv6m070UcKoOGekgvpNhiSvw=
|
||||||
|
github.com/bsm/redislock v0.9.4/go.mod h1:Epf7AJLiSFwLCiZcfi6pWFO/8eAYrYpQXFxEDPoDeAk=
|
||||||
|
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
|
||||||
|
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
|
||||||
|
github.com/bytedance/sonic v1.14.1 h1:FBMC0zVz5XUmE4z9wF4Jey0An5FueFvOsTKKKtwIl7w=
|
||||||
|
github.com/bytedance/sonic v1.14.1/go.mod h1:gi6uhQLMbTdeP0muCnrjHLeCUPyb70ujhnNlhOylAFc=
|
||||||
|
github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
|
||||||
|
github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
|
||||||
|
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
|
||||||
|
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||||
|
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
|
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||||
|
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||||
|
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
|
||||||
|
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
|
||||||
|
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||||
|
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||||
|
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
||||||
|
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||||
|
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
|
||||||
|
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
|
||||||
|
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
||||||
|
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
||||||
|
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||||
|
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||||
|
github.com/go-openapi/jsonpointer v0.22.0 h1:TmMhghgNef9YXxTu1tOopo+0BGEytxA+okbry0HjZsM=
|
||||||
|
github.com/go-openapi/jsonpointer v0.22.0/go.mod h1:xt3jV88UtExdIkkL7NloURjRQjbeUgcxFblMjq2iaiU=
|
||||||
|
github.com/go-openapi/jsonreference v0.21.1 h1:bSKrcl8819zKiOgxkbVNRUBIr6Wwj9KYrDbMjRs0cDA=
|
||||||
|
github.com/go-openapi/jsonreference v0.21.1/go.mod h1:PWs8rO4xxTUqKGu+lEvvCxD5k2X7QYkKAepJyCmSTT8=
|
||||||
|
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
|
||||||
|
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
|
||||||
|
github.com/go-openapi/swag v0.24.1 h1:DPdYTZKo6AQCRqzwr/kGkxJzHhpKxZ9i/oX0zag+MF8=
|
||||||
|
github.com/go-openapi/swag v0.24.1/go.mod h1:sm8I3lCPlspsBBwUm1t5oZeWZS0s7m/A+Psg0ooRU0A=
|
||||||
|
github.com/go-openapi/swag/cmdutils v0.24.0 h1:KlRCffHwXFI6E5MV9n8o8zBRElpY4uK4yWyAMWETo9I=
|
||||||
|
github.com/go-openapi/swag/cmdutils v0.24.0/go.mod h1:uxib2FAeQMByyHomTlsP8h1TtPd54Msu2ZDU/H5Vuf8=
|
||||||
|
github.com/go-openapi/swag/conv v0.24.0 h1:ejB9+7yogkWly6pnruRX45D1/6J+ZxRu92YFivx54ik=
|
||||||
|
github.com/go-openapi/swag/conv v0.24.0/go.mod h1:jbn140mZd7EW2g8a8Y5bwm8/Wy1slLySQQ0ND6DPc2c=
|
||||||
|
github.com/go-openapi/swag/fileutils v0.24.0 h1:U9pCpqp4RUytnD689Ek/N1d2N/a//XCeqoH508H5oak=
|
||||||
|
github.com/go-openapi/swag/fileutils v0.24.0/go.mod h1:3SCrCSBHyP1/N+3oErQ1gP+OX1GV2QYFSnrTbzwli90=
|
||||||
|
github.com/go-openapi/swag/jsonname v0.24.0 h1:2wKS9bgRV/xB8c62Qg16w4AUiIrqqiniJFtZGi3dg5k=
|
||||||
|
github.com/go-openapi/swag/jsonname v0.24.0/go.mod h1:GXqrPzGJe611P7LG4QB9JKPtUZ7flE4DOVechNaDd7Q=
|
||||||
|
github.com/go-openapi/swag/jsonutils v0.24.0 h1:F1vE1q4pg1xtO3HTyJYRmEuJ4jmIp2iZ30bzW5XgZts=
|
||||||
|
github.com/go-openapi/swag/jsonutils v0.24.0/go.mod h1:vBowZtF5Z4DDApIoxcIVfR8v0l9oq5PpYRUuteVu6f0=
|
||||||
|
github.com/go-openapi/swag/loading v0.24.0 h1:ln/fWTwJp2Zkj5DdaX4JPiddFC5CHQpvaBKycOlceYc=
|
||||||
|
github.com/go-openapi/swag/loading v0.24.0/go.mod h1:gShCN4woKZYIxPxbfbyHgjXAhO61m88tmjy0lp/LkJk=
|
||||||
|
github.com/go-openapi/swag/mangling v0.24.0 h1:PGOQpViCOUroIeak/Uj/sjGAq9LADS3mOyjznmHy2pk=
|
||||||
|
github.com/go-openapi/swag/mangling v0.24.0/go.mod h1:Jm5Go9LHkycsz0wfoaBDkdc4CkpuSnIEf62brzyCbhc=
|
||||||
|
github.com/go-openapi/swag/netutils v0.24.0 h1:Bz02HRjYv8046Ycg/w80q3g9QCWeIqTvlyOjQPDjD8w=
|
||||||
|
github.com/go-openapi/swag/netutils v0.24.0/go.mod h1:WRgiHcYTnx+IqfMCtu0hy9oOaPR0HnPbmArSRN1SkZM=
|
||||||
|
github.com/go-openapi/swag/stringutils v0.24.0 h1:i4Z/Jawf9EvXOLUbT97O0HbPUja18VdBxeadyAqS1FM=
|
||||||
|
github.com/go-openapi/swag/stringutils v0.24.0/go.mod h1:5nUXB4xA0kw2df5PRipZDslPJgJut+NjL7D25zPZ/4w=
|
||||||
|
github.com/go-openapi/swag/typeutils v0.24.0 h1:d3szEGzGDf4L2y1gYOSSLeK6h46F+zibnEas2Jm/wIw=
|
||||||
|
github.com/go-openapi/swag/typeutils v0.24.0/go.mod h1:q8C3Kmk/vh2VhpCLaoR2MVWOGP8y7Jc8l82qCTd1DYI=
|
||||||
|
github.com/go-openapi/swag/yamlutils v0.24.0 h1:bhw4894A7Iw6ne+639hsBNRHg9iZg/ISrOVr+sJGp4c=
|
||||||
|
github.com/go-openapi/swag/yamlutils v0.24.0/go.mod h1:DpKv5aYuaGm/sULePoeiG8uwMpZSfReo1HR3Ik0yaG8=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
|
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
|
||||||
|
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||||
|
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
|
github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
|
||||||
|
github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
|
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||||
|
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
|
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||||
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||||
|
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||||
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
|
github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg=
|
||||||
|
github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||||
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
|
github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=
|
||||||
|
github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||||
|
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/minio/crc64nvme v1.0.2 h1:6uO1UxGAD+kwqWWp7mBFsi5gAse66C4NXO8cmcVculg=
|
||||||
|
github.com/minio/crc64nvme v1.0.2/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
|
||||||
|
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||||
|
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||||
|
github.com/minio/minio-go/v7 v7.0.95 h1:ywOUPg+PebTMTzn9VDsoFJy32ZuARN9zhB+K3IYEvYU=
|
||||||
|
github.com/minio/minio-go/v7 v7.0.95/go.mod h1:wOOX3uxS334vImCNRVyIDdXX9OsXDm89ToynKgqUKlo=
|
||||||
|
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
|
||||||
|
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
github.com/mojocn/base64Captcha v1.3.8 h1:rrN9BhCwXKS8ht1e21kvR3iTaMgf4qPC9sRoV52bqEg=
|
||||||
|
github.com/mojocn/base64Captcha v1.3.8/go.mod h1:QFZy927L8HVP3+VV5z2b1EAEiv1KxVJKZbAucVgLUy4=
|
||||||
|
github.com/mozillazg/go-httpheader v0.2.1 h1:geV7TrjbL8KXSyvghnFm+NyTux/hxwueTSrwhe88TQQ=
|
||||||
|
github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60=
|
||||||
|
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||||
|
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||||
|
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
||||||
|
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||||
|
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.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
|
||||||
|
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
|
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
|
||||||
|
github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
|
||||||
|
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
|
||||||
|
github.com/richardlehane/msoleps v1.0.4 h1:WuESlvhX3gH2IHcd8UqyCuFY5yiq/GR/yqaSM/9/g00=
|
||||||
|
github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
|
||||||
|
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||||
|
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||||
|
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||||
|
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||||
|
github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529/go.mod h1:qe5TWALJ8/a1Lqznoc5BDHpYX/8HU60Hm2AwRmqzxqA=
|
||||||
|
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||||
|
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||||
|
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
|
||||||
|
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
|
||||||
|
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||||
|
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||||
|
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
|
||||||
|
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
|
||||||
|
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||||
|
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
|
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||||
|
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
|
||||||
|
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
|
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
|
||||||
|
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
|
||||||
|
github.com/swaggo/gin-swagger v1.6.1 h1:Ri06G4gc9N4t4k8hekMigJ9zKTFSlqj/9paAQCQs7cY=
|
||||||
|
github.com/swaggo/gin-swagger v1.6.1/go.mod h1:LQ+hJStHakCWRiK/YNYtJOu4mR2FP+pxLnILT/qNiTw=
|
||||||
|
github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
|
||||||
|
github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0=
|
||||||
|
github.com/tencentyun/cos-go-sdk-v5 v0.7.70 h1:gkBkSfrDvUg4ZIjwYAfjbNCCclen9LCRNHhBNz+yjEQ=
|
||||||
|
github.com/tencentyun/cos-go-sdk-v5 v0.7.70/go.mod h1:STbTNaNKq03u+gscPEGOahKzLcGSYOj6Dzc5zNay7Pg=
|
||||||
|
github.com/tencentyun/qcloud-cos-sts-sdk v0.0.0-20250515025012-e0eec8a5d123/go.mod h1:b18KQa4IxHbxeseW1GcZox53d7J0z39VNONTxvvlkXw=
|
||||||
|
github.com/tiendc/go-deepcopy v1.7.1 h1:LnubftI6nYaaMOcaz0LphzwraqN8jiWTwm416sitff4=
|
||||||
|
github.com/tiendc/go-deepcopy v1.7.1/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ=
|
||||||
|
github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww=
|
||||||
|
github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
|
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
|
||||||
|
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||||
|
github.com/wechatpay-apiv3/wechatpay-go v0.2.21 h1:uIyMpzvcaHA33W/QPtHstccw+X52HO1gFdvVL9O6Lfs=
|
||||||
|
github.com/wechatpay-apiv3/wechatpay-go v0.2.21/go.mod h1:A254AUBVB6R+EqQFo3yTgeh7HtyqRRtN2w9hQSOrd4Q=
|
||||||
|
github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8=
|
||||||
|
github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
|
||||||
|
github.com/xuri/excelize/v2 v2.10.0 h1:8aKsP7JD39iKLc6dH5Tw3dgV3sPRh8uRVXu/fMstfW4=
|
||||||
|
github.com/xuri/excelize/v2 v2.10.0/go.mod h1:SC5TzhQkaOsTWpANfm+7bJCldzcnU/jrhqkTi/iBHBU=
|
||||||
|
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 h1:+C0TIdyyYmzadGaL/HBLbf3WdLgC29pgyhTjAT/0nuE=
|
||||||
|
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
|
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||||
|
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
|
golang.org/x/arch v0.21.0 h1:iTC9o7+wP6cPWpDWkivCvQFGAHDQ59SrSxsLPcnkArw=
|
||||||
|
golang.org/x/arch v0.21.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||||
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
|
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||||
|
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||||
|
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
|
||||||
|
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
|
||||||
|
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
|
||||||
|
golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
|
||||||
|
golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
|
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
|
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||||
|
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
|
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||||
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
|
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||||
|
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||||
|
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
|
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||||
|
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||||
|
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
|
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||||
|
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||||
|
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
|
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||||
|
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
|
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||||
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||||
|
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||||
|
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||||
|
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
|
||||||
|
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
|
||||||
|
gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314=
|
||||||
|
gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
|
||||||
|
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||||
|
gorm.io/gorm v1.26.0 h1:9lqQVPG5aNNS6AyHdRiwScAVnXHg/L/Srzx55G5fOgs=
|
||||||
|
gorm.io/gorm v1.26.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
|
||||||
|
modernc.org/cc/v4 v4.26.0 h1:QMYvbVduUGH0rrO+5mqF/PSPPRZNpRtg2CLELy7vUpA=
|
||||||
|
modernc.org/cc/v4 v4.26.0/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||||
|
modernc.org/ccgo/v4 v4.26.0 h1:gVzXaDzGeBYJ2uXTOpR8FR7OlksDOe9jxnjhIKCsiTc=
|
||||||
|
modernc.org/ccgo/v4 v4.26.0/go.mod h1:Sem8f7TFUtVXkG2fiaChQtyyfkqhJBg/zjEJBkmuAVY=
|
||||||
|
modernc.org/fileutil v1.3.1 h1:8vq5fe7jdtEvoCf3Zf9Nm0Q05sH6kGx0Op2CPx1wTC8=
|
||||||
|
modernc.org/fileutil v1.3.1/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||||
|
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||||
|
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||||
|
modernc.org/libc v1.64.0 h1:U0k8BD2d3cD3e9I8RLcZgJBHAcsJzbXx5mKGSb5pyJA=
|
||||||
|
modernc.org/libc v1.64.0/go.mod h1:7m9VzGq7APssBTydds2zBcxGREwvIGpuUBaKTXdm2Qs=
|
||||||
|
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||||
|
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||||
|
modernc.org/memory v1.10.0 h1:fzumd51yQ1DxcOxSO+S6X7+QTuVU+n8/Aj7swYjFfC4=
|
||||||
|
modernc.org/memory v1.10.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||||
|
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||||
|
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||||
|
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||||
|
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||||
|
modernc.org/sqlite v1.37.0 h1:s1TMe7T3Q3ovQiK2Ouz4Jwh7dw4ZDqbebSDTlSJdfjI=
|
||||||
|
modernc.org/sqlite v1.37.0/go.mod h1:5YiWv+YviqGMuGw4V+PNplcyaJ5v+vQd7TQOgkACoJM=
|
||||||
|
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||||
|
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||||
|
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||||
|
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package initialize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"sundynix-go/global"
|
||||||
|
"sundynix-go/model/plant"
|
||||||
|
"sundynix-go/model/system"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Gorm 根据全局配置中的数据库类型返回对应的 *gorm.DB 实例。
|
||||||
|
// 该函数通过检查 global.Config.System.DbType 的值来决定使用哪种数据库连接。
|
||||||
|
//
|
||||||
|
// 返回值:
|
||||||
|
// - *gorm.DB: 返回对应数据库类型的 *gorm.DB 实例
|
||||||
|
func Gorm() *gorm.DB {
|
||||||
|
switch global.Config.System.DbType {
|
||||||
|
case "mysql":
|
||||||
|
return GormMysql() // 返回 MySQL 数据库的 *gorm.DB 实例
|
||||||
|
case "pgsql":
|
||||||
|
return GromPgsql() // 返回 PostgreSQL 数据库的 *gorm.DB 实例
|
||||||
|
case "sqlite":
|
||||||
|
return GormSqlite() // 返回 SQLite 数据库的 *gorm.DB 实例
|
||||||
|
default:
|
||||||
|
return GormMysql() // 默认返回 MySQL 数据库的 *gorm.DB 实例
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MigrateTable 创建数据库表结构。
|
||||||
|
func MigrateTable() {
|
||||||
|
db := global.DB
|
||||||
|
err := db.AutoMigrate(
|
||||||
|
system.User{},
|
||||||
|
system.Client{},
|
||||||
|
system.Role{},
|
||||||
|
system.Menu{},
|
||||||
|
system.SysOperationRecord{},
|
||||||
|
system.Oss{},
|
||||||
|
|
||||||
|
plant.MyPlant{}, //我的植物
|
||||||
|
plant.CarePlan{}, //植物养护计划
|
||||||
|
plant.CareTask{}, //植物养护任务
|
||||||
|
plant.CareRecord{}, //植物养护记录
|
||||||
|
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("Migrate table failed,err:", zap.Error(err))
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
global.Logger.Info("Migrate table success")
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package initialize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sundynix-go/config"
|
||||||
|
"sundynix-go/global"
|
||||||
|
"sundynix-go/initialize/internal"
|
||||||
|
|
||||||
|
"gorm.io/driver/mysql"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GormMysql 初始化Mysql数据库
|
||||||
|
func GormMysql() *gorm.DB {
|
||||||
|
m := global.Config.Mysql
|
||||||
|
return initMysqlDatabase(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GromMysqlByConfig 根据配置初始化Mysql数据库
|
||||||
|
func GromMysqlByConfig(m config.Mysql) *gorm.DB {
|
||||||
|
return initMysqlDatabase(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// initMysqlDatabase 初始化Mysql数据库的辅助函数
|
||||||
|
func initMysqlDatabase(m config.Mysql) *gorm.DB {
|
||||||
|
if m.Dbname == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
mysqlConfig := mysql.Config{
|
||||||
|
DSN: m.Dsn(), //dsn
|
||||||
|
DefaultStringSize: 191, // 默认字符串类型长度
|
||||||
|
SkipInitializeWithVersion: false, // 根据版本自动配置
|
||||||
|
}
|
||||||
|
if db, err := gorm.Open(mysql.New(mysqlConfig), internal.Gorm.Config(m.Prefix, m.Singular)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
db.InstanceSet("gorm:table_options", "ENGINE="+m.Engine)
|
||||||
|
sqlDb, _ := db.DB()
|
||||||
|
sqlDb.SetMaxIdleConns(m.MaxIdleConns)
|
||||||
|
sqlDb.SetMaxOpenConns(m.MaxOpenConns)
|
||||||
|
global.Logger.Info("Mysql connect success")
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package initialize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gorm.io/driver/postgres"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"sundynix-go/config"
|
||||||
|
"sundynix-go/global"
|
||||||
|
"sundynix-go/initialize/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GromPgsql 初始化 Postgresql 数据库
|
||||||
|
func GromPgsql() *gorm.DB {
|
||||||
|
p := global.Config.Pgsql
|
||||||
|
return initPgsqlDatabase(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GormPgsqlByConfig 根据配置文件初始化 Postgresql 数据库
|
||||||
|
func GormPgsqlByConfig(p config.Pgsql) *gorm.DB {
|
||||||
|
return initPgsqlDatabase(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// initPgsqlDatabase 初始化 Postgresql 数据库的辅助函数
|
||||||
|
func initPgsqlDatabase(p config.Pgsql) *gorm.DB {
|
||||||
|
if p.Dbname == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
pgsqlConfig := postgres.Config{
|
||||||
|
DSN: p.Dsn(), // DSN 数据库连接串
|
||||||
|
PreferSimpleProtocol: false, // 禁用隐式 prepared statement
|
||||||
|
}
|
||||||
|
if db, err := gorm.Open(postgres.New(pgsqlConfig), internal.Gorm.Config(p.Prefix, p.Singular)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
sqlDb, _ := db.DB()
|
||||||
|
sqlDb.SetMaxIdleConns(p.MaxIdleConns)
|
||||||
|
sqlDb.SetMaxOpenConns(p.MaxOpenConns)
|
||||||
|
global.Logger.Info("postgresql connect success")
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package initialize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/glebarez/sqlite"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"sundynix-go/config"
|
||||||
|
"sundynix-go/global"
|
||||||
|
"sundynix-go/initialize/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GormSqlite 初始化Sqlite数据库
|
||||||
|
func GormSqlite() *gorm.DB {
|
||||||
|
s := global.Config.Sqlite
|
||||||
|
return initSqliteDatabase(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GormSqliteByConfig 初始化Sqlite数据库用过传入配置
|
||||||
|
func GormSqliteByConfig(s config.Sqlite) *gorm.DB {
|
||||||
|
return initSqliteDatabase(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// initSqliteDatabase 初始化Sqlite数据库辅助函数
|
||||||
|
func initSqliteDatabase(s config.Sqlite) *gorm.DB {
|
||||||
|
if s.Dbname == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if db, err := gorm.Open(sqlite.Open(s.Dsn()), internal.Gorm.Config(s.Prefix, s.Singular)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
sqlDB, _ := db.DB()
|
||||||
|
sqlDB.SetMaxIdleConns(s.MaxIdleConns)
|
||||||
|
sqlDB.SetMaxOpenConns(s.MaxOpenConns)
|
||||||
|
global.Logger.Info("sqlite connect success")
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sundynix-go/config"
|
||||||
|
"sundynix-go/global"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/logger"
|
||||||
|
"gorm.io/gorm/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Gorm = new(_gorm)
|
||||||
|
|
||||||
|
type _gorm struct{}
|
||||||
|
|
||||||
|
// Config 函数用于根据数据库类型和配置生成 GORM 的配置对象
|
||||||
|
// 该函数会根据全局配置中的数据库类型选择相应的通用配置,并返回一个配置好的 *gorm.Config 对象。
|
||||||
|
//
|
||||||
|
// 参数:
|
||||||
|
// - prefix: 表名前缀,用于在生成表名时添加到表名前。
|
||||||
|
// - singular: 是否禁用复数表名,true 表示禁用复数表名,false 表示使用复数表名。
|
||||||
|
//
|
||||||
|
// 返回值:
|
||||||
|
// - *gorm.Config: 配置好的 GORM 配置对象,包含日志、命名策略等配置。
|
||||||
|
func (g *_gorm) Config(prefix string, singular bool) *gorm.Config {
|
||||||
|
// 根据全局配置中的数据库类型选择相应的通用配置
|
||||||
|
var general config.GeneralDB
|
||||||
|
switch global.Config.System.DbType {
|
||||||
|
case "mysql":
|
||||||
|
general = global.Config.Mysql.GeneralDB
|
||||||
|
case "pgsql":
|
||||||
|
general = global.Config.Pgsql.GeneralDB
|
||||||
|
case "sqlite":
|
||||||
|
general = global.Config.Sqlite.GeneralDB
|
||||||
|
default:
|
||||||
|
// 默认使用 MySQL 的通用配置
|
||||||
|
general = global.Config.Mysql.GeneralDB
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回配置好的 GORM 配置对象
|
||||||
|
return &gorm.Config{
|
||||||
|
// 配置日志记录器,使用自定义的日志写入器,并设置慢查询阈值、日志级别和颜色输出
|
||||||
|
Logger: logger.New(NewWriter(general), logger.Config{
|
||||||
|
SlowThreshold: 200 * time.Millisecond,
|
||||||
|
LogLevel: general.LogLevel(),
|
||||||
|
Colorful: true,
|
||||||
|
}),
|
||||||
|
// 配置命名策略,设置表前缀和是否禁用复数表名
|
||||||
|
NamingStrategy: schema.NamingStrategy{
|
||||||
|
TablePrefix: prefix, // 表前缀
|
||||||
|
SingularTable: singular, // 禁用复数表名
|
||||||
|
},
|
||||||
|
// 禁用自动创建外键约束
|
||||||
|
DisableForeignKeyConstraintWhenMigrating: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"gorm.io/gorm/logger"
|
||||||
|
"sundynix-go/config"
|
||||||
|
"sundynix-go/global"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Writer struct {
|
||||||
|
config config.GeneralDB
|
||||||
|
writer logger.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWriter 创建一个Writer
|
||||||
|
func NewWriter(config config.GeneralDB) *Writer {
|
||||||
|
return &Writer{config: config}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf 格式化打印日志
|
||||||
|
func (w *Writer) Printf(message string, data ...any) {
|
||||||
|
fmt.Printf(message, data)
|
||||||
|
//当开启了zap的情况下,会打印到日志记录中
|
||||||
|
if w.config.LogZap {
|
||||||
|
switch w.config.LogLevel() {
|
||||||
|
case logger.Silent:
|
||||||
|
global.Logger.Debug(fmt.Sprintf(message, data))
|
||||||
|
case logger.Error:
|
||||||
|
global.Logger.Error(fmt.Sprintf(message, data))
|
||||||
|
case logger.Warn:
|
||||||
|
global.Logger.Warn(fmt.Sprintf(message, data))
|
||||||
|
case logger.Info:
|
||||||
|
global.Logger.Info(fmt.Sprintf(message, data))
|
||||||
|
default:
|
||||||
|
global.Logger.Info(fmt.Sprintf(message, data))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package initialize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sundynix-go/config"
|
||||||
|
"sundynix-go/global"
|
||||||
|
|
||||||
|
"github.com/bsm/redislock"
|
||||||
|
"github.com/redis/go-redis/v9"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Redis
|
||||||
|
func Redis() {
|
||||||
|
client, err := initRedisClient(global.Config.Redis)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("Redis connect failed,err:", zap.Error(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
global.Redis = client
|
||||||
|
global.Locker = redislock.New(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化Redis
|
||||||
|
func initRedisClient(redisConfig config.Redis) (redis.UniversalClient, error) {
|
||||||
|
var client redis.UniversalClient
|
||||||
|
//集群模式
|
||||||
|
if redisConfig.Cluster {
|
||||||
|
client = redis.NewClusterClient(&redis.ClusterOptions{
|
||||||
|
Addrs: redisConfig.ClusterAddrs,
|
||||||
|
Password: redisConfig.Password,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
//单例模式
|
||||||
|
client = redis.NewClient(&redis.Options{
|
||||||
|
Addr: redisConfig.Addr,
|
||||||
|
Password: redisConfig.Password,
|
||||||
|
DB: redisConfig.DB,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pong, err := client.Ping(context.Background()).Result()
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("Redis connect ping failed,err:", zap.String("name", redisConfig.Name), zap.Error(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
global.Logger.Info("Redis connect ping response:", zap.String("name", redisConfig.Name), zap.String("pong", pong))
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package initialize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sundynix-go/docs"
|
||||||
|
"sundynix-go/global"
|
||||||
|
"sundynix-go/middleware"
|
||||||
|
"sundynix-go/router"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
swaggerFiles "github.com/swaggo/files"
|
||||||
|
ginSwagger "github.com/swaggo/gin-swagger"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Routers 初始化总路由
|
||||||
|
func Routers() {
|
||||||
|
Router := gin.New()
|
||||||
|
|
||||||
|
Router.Use(gin.Recovery())
|
||||||
|
if gin.Mode() == gin.DebugMode {
|
||||||
|
Router.Use(gin.Logger())
|
||||||
|
}
|
||||||
|
docs.SwaggerInfo.BasePath = global.Config.System.RouterPrefix
|
||||||
|
Router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||||
|
|
||||||
|
// 系统组路由
|
||||||
|
systemRouter := router.GroupApp.System
|
||||||
|
plantGroup := router.GroupApp.Plant
|
||||||
|
|
||||||
|
NeedAuthGroup := Router.Group(global.Config.System.RouterPrefix)
|
||||||
|
PublicGroup := Router.Group(global.Config.System.RouterPrefix)
|
||||||
|
|
||||||
|
//鉴权中间件
|
||||||
|
NeedAuthGroup.Use(middleware.AuthMiddleware())
|
||||||
|
{
|
||||||
|
//无须鉴权的路由
|
||||||
|
systemRouter.InitAuthRouter(PublicGroup) //登录不需要鉴权
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//需要鉴权的路由
|
||||||
|
systemRouter.InitUserRouter(NeedAuthGroup) //用户相关
|
||||||
|
systemRouter.InitClientRouter(NeedAuthGroup) //客户端相关
|
||||||
|
systemRouter.InitRoleRouter(NeedAuthGroup) //角色相关
|
||||||
|
systemRouter.InitMenuRouter(NeedAuthGroup) //菜单相关
|
||||||
|
systemRouter.InitOssRouter(NeedAuthGroup) //OSS相关
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//需要鉴权的路由
|
||||||
|
plantGroup.InitPlantRouter(NeedAuthGroup)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
address := fmt.Sprintf(":%d", global.Config.System.Addr)
|
||||||
|
fmt.Printf(`
|
||||||
|
欢迎使用 sundynix-plant-go
|
||||||
|
项目地址:
|
||||||
|
默认自动化文档地址:http://127.0.0.1%s/swagger/index.html
|
||||||
|
默认前端文件运行地址:http://127.0.0.1:8080
|
||||||
|
`, address)
|
||||||
|
|
||||||
|
err := Router.Run(address)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("Gin run failed", zap.Error(err))
|
||||||
|
}
|
||||||
|
global.Logger.Info("Gin run success", zap.String("address", address))
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package initialize
|
||||||
|
|
||||||
|
//func InitTimer() {
|
||||||
|
// go func() {
|
||||||
|
// var option []cron.Option
|
||||||
|
// option = append(option, cron.WithSeconds())
|
||||||
|
//
|
||||||
|
// //任务一:每天凌晨00:01点执行 生成今日养护任务
|
||||||
|
// _, err := global.Timer.AddTaskByFuncWithSecond("GenerateToday", "0 1 0 * * *", func() {
|
||||||
|
// err := task.GeneratorTodayCare()
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Println("定时生成今日养护任务失败:", err)
|
||||||
|
// }
|
||||||
|
// }, "定时生成今日带养护记录", option...)
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Println("添加定时任务失败:", err)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 任务二:每天凌晨00:16执行 定时更新未完成的任务
|
||||||
|
// _, err2 := global.Timer.AddTaskByFuncWithSecond("UpdateExpireCare", "0 16 0 * * *", func() {
|
||||||
|
// err3 := task.UpdateExpireCare()
|
||||||
|
// if err3 != nil {
|
||||||
|
// fmt.Println("定时更新未完成任务失败:", err)
|
||||||
|
// }
|
||||||
|
// }, "定时更新未完成任务", option...)
|
||||||
|
// if err2 != nil {
|
||||||
|
// fmt.Println("添加定时任务失败:", err)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 任务三:每天8点执行 发送植物养护提醒
|
||||||
|
// _, err4 := global.Timer.AddTaskByFuncWithSecond("SendCareRemind", "0 0 8 * * *", func() {
|
||||||
|
// err5 := task.SendCareMsg()
|
||||||
|
// if err5 != nil {
|
||||||
|
// global.Logger.Error("定时发送植物养护提醒失败", zap.Error(err5))
|
||||||
|
// }
|
||||||
|
// }, "定时发送植物养护提醒", option...)
|
||||||
|
// if err4 != nil {
|
||||||
|
// fmt.Println("添加定时任务失败:", err)
|
||||||
|
// }
|
||||||
|
// }()
|
||||||
|
//}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"sundynix-go/core"
|
||||||
|
"sundynix-go/global"
|
||||||
|
"sundynix-go/initialize"
|
||||||
|
"sundynix-go/pkg/httpclient"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// @title Swagger API接口文档
|
||||||
|
// @version v1.0.0
|
||||||
|
// @description 使用gin + gorm进行极速开发的全栈开发基础平台
|
||||||
|
// @securityDefinitions.apikey ApiKeyAuth
|
||||||
|
// @in header
|
||||||
|
// @name Authorization
|
||||||
|
// @BasePath /
|
||||||
|
func main() {
|
||||||
|
//viper
|
||||||
|
global.Viper = core.Viper()
|
||||||
|
//canzap
|
||||||
|
global.Logger = core.Zap()
|
||||||
|
//swap
|
||||||
|
zap.ReplaceGlobals(global.Logger)
|
||||||
|
//初始化Gorm 连接数据库
|
||||||
|
global.DB = initialize.Gorm()
|
||||||
|
//redis
|
||||||
|
initialize.Redis()
|
||||||
|
|
||||||
|
// timer
|
||||||
|
//initialize.InitTimer()
|
||||||
|
// httpclient 主动初始化 HTTP Client(可选,也可依赖懒加载)饿汉加载
|
||||||
|
httpclient.InitHttpClient()
|
||||||
|
|
||||||
|
//迁移数据库
|
||||||
|
if global.DB != nil {
|
||||||
|
initialize.MigrateTable() // 迁移数据库结构
|
||||||
|
db, _ := global.DB.DB()
|
||||||
|
defer func(db *sql.DB) {
|
||||||
|
err := db.Close()
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("db close failed", zap.Error(err))
|
||||||
|
}
|
||||||
|
}(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
//初始化路由
|
||||||
|
initialize.Routers()
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"sundynix-go/global"
|
||||||
|
"sundynix-go/model/commom/response"
|
||||||
|
"sundynix-go/service"
|
||||||
|
"sundynix-go/utils"
|
||||||
|
"sundynix-go/utils/auth"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
var jwtService = service.GroupApp.SystemServiceGroup.JwtService
|
||||||
|
|
||||||
|
// AuthMiddleware 验证token有效性
|
||||||
|
func AuthMiddleware() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
token := auth.GetToken(c)
|
||||||
|
if token == "" {
|
||||||
|
response.NoAuth("未登录或非法访问", c)
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userId := auth.GetUserId(c)
|
||||||
|
if jwtService.IsInBlacklist(userId, token) {
|
||||||
|
response.NoAuth("未登录或令牌失效", c)
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
j := auth.NewJWT()
|
||||||
|
// 解析token信息
|
||||||
|
claims, err := j.ParseToken(token)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, auth.TokenExpired) {
|
||||||
|
response.NoAuth("登录过期", c)
|
||||||
|
auth.ClearToken(c)
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.NoAuth(err.Error(), c)
|
||||||
|
auth.ClearToken(c)
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Set("claims", claims)
|
||||||
|
if claims.ExpiresAt.Unix()-time.Now().Unix() < claims.BufferTime {
|
||||||
|
dr, _ := utils.ParseDuration(global.Config.JWT.ExpiresTime)
|
||||||
|
claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(dr))
|
||||||
|
}
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"sundynix-go/global"
|
||||||
|
"sundynix-go/model/system"
|
||||||
|
"sundynix-go/service"
|
||||||
|
"sundynix-go/utils/auth"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
var operationService = service.GroupApp.SystemServiceGroup.OperationRecordService
|
||||||
|
|
||||||
|
var respPool sync.Pool
|
||||||
|
var bufferSize = 1024
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
respPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return make([]byte, bufferSize)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func OperationRecord() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
var body []byte
|
||||||
|
var userId string
|
||||||
|
if c.Request.Method != http.MethodGet {
|
||||||
|
var err error
|
||||||
|
body, err = io.ReadAll(c.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("read body from request error:", zap.Error(err))
|
||||||
|
} else {
|
||||||
|
c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
query := c.Request.URL.RawQuery
|
||||||
|
query, _ = url.QueryUnescape(query)
|
||||||
|
split := strings.Split(query, "&")
|
||||||
|
m := make(map[string]string)
|
||||||
|
for _, v := range split {
|
||||||
|
kv := strings.Split(v, "=")
|
||||||
|
if len(kv) == 2 {
|
||||||
|
m[kv[0]] = kv[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
body, _ = json.Marshal(&m)
|
||||||
|
}
|
||||||
|
claims, _ := auth.GetClaims(c)
|
||||||
|
if claims != nil && claims.BaseClaims.ID != "" {
|
||||||
|
userId = claims.BaseClaims.ID
|
||||||
|
} else {
|
||||||
|
userId = c.Request.Header.Get("x-user-id")
|
||||||
|
}
|
||||||
|
record := system.SysOperationRecord{
|
||||||
|
Ip: c.ClientIP(),
|
||||||
|
Method: c.Request.Method,
|
||||||
|
Path: c.Request.URL.Path,
|
||||||
|
Agent: c.Request.UserAgent(),
|
||||||
|
Body: string(body),
|
||||||
|
UserId: userId,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := operationService.CreateOperationRecord(record); err != nil {
|
||||||
|
global.Logger.Error("create operation record error:", zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package request
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PageInfo Paging common input parameter structure
|
||||||
|
type PageInfo struct {
|
||||||
|
Current int `json:"current" form:"current"` // 页码
|
||||||
|
PageSize int `json:"pageSize" form:"pageSize"` // 每页大小
|
||||||
|
Keyword string `json:"keyword" form:"keyword"` // 关键字
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *PageInfo) Paginate() func(db *gorm.DB) *gorm.DB {
|
||||||
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
|
if r.Current <= 0 {
|
||||||
|
r.Current = 1
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case r.PageSize > 100:
|
||||||
|
r.PageSize = 100
|
||||||
|
case r.PageSize <= 0:
|
||||||
|
r.PageSize = 10
|
||||||
|
}
|
||||||
|
offset := (r.Current - 1) * r.PageSize
|
||||||
|
return db.Offset(offset).Limit(r.PageSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetById Find by id structure
|
||||||
|
type GetById struct {
|
||||||
|
ID string `json:"id" form:"id"` // 主键ID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *GetById) Uint() string {
|
||||||
|
return string(r.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
type IdsReq struct {
|
||||||
|
Ids []string `json:"ids" form:"ids"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAuthorityId Get role by id structure
|
||||||
|
type GetAuthorityId struct {
|
||||||
|
AuthorityId string `json:"authorityId" form:"authorityId"` // 角色ID
|
||||||
|
}
|
||||||
|
|
||||||
|
type Empty struct{}
|
||||||
|
|
||||||
|
type UploadOss struct {
|
||||||
|
Id string `json:"id" binding:"required"` //数据主键
|
||||||
|
OssIds []string `json:"ossIds" binding:"required"` // ossIds
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteOss struct {
|
||||||
|
Id string `json:"id" binding:"required"` //数据主键
|
||||||
|
OssIds []string `json:"ossIds" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UploadFile struct {
|
||||||
|
Id string `json:"id" binding:"required"` //数据主键
|
||||||
|
OssId string `json:"ossIds" binding:"required"` // ossId
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package response
|
||||||
|
|
||||||
|
type PageResult struct {
|
||||||
|
List interface{} `json:"list"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
PageSize int `json:"pageSize"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListResult struct {
|
||||||
|
List interface{} `json:"list"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package response
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Data interface{} `json:"data"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
SUCCESS = 200
|
||||||
|
ERROR = 7
|
||||||
|
)
|
||||||
|
|
||||||
|
// Result 返回结果
|
||||||
|
func Result(code int, data interface{}, msg string, c *gin.Context) {
|
||||||
|
c.JSON(http.StatusOK, Response{
|
||||||
|
code,
|
||||||
|
data,
|
||||||
|
msg,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ok 成功返回
|
||||||
|
func Ok(c *gin.Context) {
|
||||||
|
Result(SUCCESS, map[string]interface{}{}, "操作成功", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OkWithData 带数据成功返回
|
||||||
|
func OkWithData(data interface{}, c *gin.Context) {
|
||||||
|
Result(SUCCESS, data, "操作成功", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OkWithMsg 带信息成功返回
|
||||||
|
func OkWithMsg(msg string, c *gin.Context) {
|
||||||
|
Result(SUCCESS, map[string]interface{}{}, msg, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail 失败返回
|
||||||
|
func Fail(code int, msg string, c *gin.Context) {
|
||||||
|
Result(code, map[string]interface{}{}, "操作失败", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FailWithMsg 带信息失败返回
|
||||||
|
func FailWithMsg(msg string, c *gin.Context) {
|
||||||
|
Result(ERROR, map[string]interface{}{}, msg, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoAuth 未授权返回
|
||||||
|
func NoAuth(message string, c *gin.Context) {
|
||||||
|
c.JSON(http.StatusUnauthorized, Response{
|
||||||
|
7,
|
||||||
|
nil,
|
||||||
|
message,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package plant
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sundynix-go/global"
|
||||||
|
"sundynix-go/model/system"
|
||||||
|
"sundynix-go/utils/timer"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MyPlant 我的植物
|
||||||
|
type MyPlant struct {
|
||||||
|
global.BaseModel
|
||||||
|
UserId string `json:"userId" form:"userId" gorm:"size:50;column:user_id;comment:用户id"`
|
||||||
|
Name string `json:"name" form:"name" gorm:"size:20;column:name;comment:名称"`
|
||||||
|
PlantTime time.Time `json:"plantTime" form:"plantTime" gorm:"column:plant_time;comment:种植时间"`
|
||||||
|
Status int `json:"status" form:"status" gorm:"column:status;comment:生长状态"`
|
||||||
|
Placement string `json:"placement" form:"placement" gorm:"size:50;column:placement;comment:摆放位置"`
|
||||||
|
PotMaterial string `json:"potMaterial" form:"potMaterial" gorm:"column:pot_material;size:50;comment:花盆材质"`
|
||||||
|
PotSize string `json:"potSize" form:"potSize" gorm:"column:pot_size;size:50;comment:花盆大小"` // 花盆大小 如直径 20cm × 高度 18cm
|
||||||
|
Sunlight string `json:"sunlight" form:"sunlight" gorm:"column:sunlight;size:50;comment:光照条件"` // 如每日12小时
|
||||||
|
PlantingMaterial string `json:"plantingMaterial" form:"plantingMaterial" gorm:"column:planting_material;size:50;comment:植料"`
|
||||||
|
|
||||||
|
ImgList []*system.Oss `json:"imgList" form:"imgList" gorm:"many2many:my_plant_oss;comment:图片列表"`
|
||||||
|
CarePlans []*CarePlan `json:"carePlans" form:"carePlans" gorm:"foreignKey:PlantId;comment:养护计划"`
|
||||||
|
CareRecords []*CareRecord `json:"careRecords" form:"careRecords" gorm:"foreignKey:PlantId;comment:养护记录"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterCreate 钩子函数 创建plant时自动创建careTask
|
||||||
|
func (p *MyPlant) AfterCreate(tx *gorm.DB) (err error) {
|
||||||
|
today := timer.GetZeroTime()
|
||||||
|
for _, v := range p.CarePlans {
|
||||||
|
dueDate := today.AddDate(0, 0, v.Period)
|
||||||
|
task := CareTask{
|
||||||
|
UserId: p.UserId,
|
||||||
|
PlantId: p.Id,
|
||||||
|
PlanId: v.Id,
|
||||||
|
Name: v.Name,
|
||||||
|
Icon: v.Icon,
|
||||||
|
DueDate: dueDate,
|
||||||
|
Status: 1,
|
||||||
|
}
|
||||||
|
err = tx.Create(&task).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package plant
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sundynix-go/global"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CarePlan 养护计划
|
||||||
|
type CarePlan struct {
|
||||||
|
global.BaseModel
|
||||||
|
UserId string `json:"userId" form:"userId" gorm:"size:50;column:user_id;comment:用户id"`
|
||||||
|
PlantId string `json:"plantId" form:"plantId" gorm:"size:50;column:plant_id;comment:植物id"`
|
||||||
|
Icon string `json:"icon" form:"icon" gorm:"type:text;"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Period int `json:"period" form:"period" gorm:"column:period;comment:周期"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package plant
|
||||||
|
|
||||||
|
import "sundynix-go/global"
|
||||||
|
|
||||||
|
type CareRecord struct {
|
||||||
|
global.BaseModel
|
||||||
|
UserId string `json:"userId" form:"userId" gorm:"size:50;column:user_id;comment:用户id"`
|
||||||
|
PlantId string `json:"plantId" form:"plantId" gorm:"index;size:50;column:plant_id;comment:植物id"`
|
||||||
|
PlanId string `json:"planId" form:"plantId" gorm:"index;column:plan_id;comment:计划id"`
|
||||||
|
Name string `json:"name" form:"name" gorm:"column:name"`
|
||||||
|
Remark string `json:"remark" form:"remark" gorm:"column:remark"`
|
||||||
|
Icon string `json:"icon" form:"icon" gorm:"type:text;column:icon"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package plant
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sundynix-go/global"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CareTask struct {
|
||||||
|
global.BaseModel
|
||||||
|
PlantId string `json:"plantId" gorm:"index"`
|
||||||
|
PlanId string `json:"planId" gorm:"index"`
|
||||||
|
UserId string `json:"userId" gorm:"index"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Icon string `json:"icon" gorm:"type:text"`
|
||||||
|
DueDate time.Time `json:"dueDate"` // 应做时间(用于判断逾期)
|
||||||
|
CompletedAt *time.Time `json:"completedAt" gorm:"column:completed_at"` // 完成时间(为nil表示未完成)
|
||||||
|
Status int `json:"status"` // 1:待执行 2:已完成 3:已跳过
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package request
|
||||||
|
|
||||||
|
type CarePlan struct {
|
||||||
|
Icon string `json:"icon"` // icon信息
|
||||||
|
Name string `json:"name"` // 农事名称
|
||||||
|
Period int `json:"period"` // 周期
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateMyPlant 创建植物
|
||||||
|
type CreateMyPlant struct {
|
||||||
|
Name string `json:"name"` // 植物名称
|
||||||
|
PlantTime string `json:"plantTime"` // 种植时间
|
||||||
|
PotMaterial string `json:"potMaterial"` //花盆材质
|
||||||
|
PotSize string `json:"potSize"` // 花盆大小 如直径 20cm × 高度 18cm
|
||||||
|
Placement string `json:"placement"` //摆放位置
|
||||||
|
Sunlight string `json:"sunlight"` //光照条件如每日12小时
|
||||||
|
PlantingMaterial string `json:"plantingMaterial"` //植料(即土的材质)
|
||||||
|
OssIds []string `json:"ossIds"` // 图片ids
|
||||||
|
CarePlans []*CarePlan `json:"carePlans"` // 养护计划
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateMyPlant 修改植物
|
||||||
|
type UpdateMyPlant struct {
|
||||||
|
Id string `json:"id" binding:"required"`
|
||||||
|
Name string `json:"name"` // 植物名称
|
||||||
|
PotMaterial string `json:"potMaterial"` // 花盆材质
|
||||||
|
PotSize string `json:"potSize"` // 花盆大小 如直径 20cm × 高度 18cm
|
||||||
|
Placement string `json:"placement"` // 摆放位置
|
||||||
|
Sunlight string `json:"sunlight"` // 光照条件如每日12小时
|
||||||
|
PlantingMaterial string `json:"plantingMaterial"` // 植料(即土的材质)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CompleteTask struct {
|
||||||
|
TaskId string `json:"taskId" binding:"required"`
|
||||||
|
Remark string `json:"remark"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package response
|
||||||
|
|
||||||
|
//type BadgeGroup struct {
|
||||||
|
// CategoryName string `json:"categoryName" comment:"分类名称"`
|
||||||
|
// BadgeList []plant.Badge `json:"badgeList"`
|
||||||
|
//}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package response
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sundynix-go/model/plant"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PlantTaskVO 植物任务
|
||||||
|
type PlantTaskVO struct {
|
||||||
|
MyPlant *plant.MyPlant
|
||||||
|
HasExpired bool `json:"hasExpired"`
|
||||||
|
Tasks []*plant.CareTask `json:"tasks"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package response
|
||||||
|
|
||||||
|
// BaikeInfo 植物百科信息(baike_info子对象)
|
||||||
|
type BaikeInfo struct {
|
||||||
|
BaikeUrl string `json:"baike_url"` // 百度百科链接
|
||||||
|
ImageUrl string `json:"image_url"` // 植物图片链接
|
||||||
|
Description string `json:"description"` // 植物百科描述文本
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResultItem 识别结果项(result数组中的单个元素)
|
||||||
|
type ResultItem struct {
|
||||||
|
Score float64 `json:"score"` // 匹配相似度得分(0-1)
|
||||||
|
Name string `json:"name"` // 植物名称
|
||||||
|
BaikeInfo *BaikeInfo `json:"baike_info"` // 植物百科信息(部分结果可能无此字段,用指针避免空值解析问题)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlantRecognitionResponse 植物识别接口的HTTP响应结构体
|
||||||
|
// 字段标签仅保留 json,严格匹配返回的JSON字段名
|
||||||
|
type PlantRecognitionResponse struct {
|
||||||
|
LogId uint64 `json:"log_id"` // 识别请求日志ID(唯一标识)
|
||||||
|
Result []ResultItem `json:"result"` // 植物识别结果列表
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sundynix-go/global"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Oss struct {
|
||||||
|
global.BaseModel
|
||||||
|
Name string `json:"name" form:"name" gorm:"column:name;comment:文件名"`
|
||||||
|
Url string `json:"url" form:"url" gorm:"column:url;comment:文件地址"`
|
||||||
|
Tag string `json:"tag" form:"tag" gorm:"column:tag;comment:文件标签"`
|
||||||
|
Key string `json:"key" form:"key" gorm:"column:key;comment:文件key"`
|
||||||
|
Suffix string `json:"suffix" form:"suffix" gorm:"column:suffix;comment:文件后缀"`
|
||||||
|
MD5 string `json:"md5" form:"md5" gorm:"column:md5;comment:文件md5"`
|
||||||
|
Height int `json:"height" form:"height" gorm:"column:height;comment:图片高度"`
|
||||||
|
Width int `json:"width" form:"width" gorm:"column:width;comment:图片宽度"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package request
|
||||||
|
|
||||||
|
import "github.com/mojocn/base64Captcha"
|
||||||
|
|
||||||
|
// configJsonBody json request body.
|
||||||
|
type CaptchaReqBody struct {
|
||||||
|
Id string
|
||||||
|
CaptchaType string
|
||||||
|
VerifyValue string
|
||||||
|
DriverAudio *base64Captcha.DriverAudio
|
||||||
|
DriverString *base64Captcha.DriverString
|
||||||
|
DriverChinese *base64Captcha.DriverChinese
|
||||||
|
DriverMath *base64Captcha.DriverMath
|
||||||
|
DriverDigit *base64Captcha.DriverDigit
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package request
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CustomClaims struct {
|
||||||
|
BaseClaims
|
||||||
|
BufferTime int64
|
||||||
|
jwt.RegisteredClaims
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseClaims struct {
|
||||||
|
UUID uuid.UUID
|
||||||
|
ID string
|
||||||
|
Account string
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package request
|
||||||
|
|
||||||
|
import common "sundynix-go/model/commom/request"
|
||||||
|
|
||||||
|
type GetOssFileList struct {
|
||||||
|
common.PageInfo
|
||||||
|
Name string `json:"name" form:"name"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package request
|
||||||
|
|
||||||
|
import common "sundynix-go/model/commom/request"
|
||||||
|
|
||||||
|
type GetClientList struct {
|
||||||
|
common.PageInfo
|
||||||
|
ClientId string `json:"clientId" form:"clientId"`
|
||||||
|
Name string `json:"name" form:"name"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package request
|
||||||
|
|
||||||
|
type GetMenuTree struct {
|
||||||
|
Category int `json:"category" form:"category"`
|
||||||
|
ParentId string `json:"parentId" form:"parentId"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package request
|
||||||
|
|
||||||
|
import (
|
||||||
|
common "sundynix-go/model/commom/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GetOperationRecordList struct {
|
||||||
|
common.PageInfo
|
||||||
|
Ip string `json:"ip" form:"ip"`
|
||||||
|
Method string `json:"method" form:"method"`
|
||||||
|
Path string `json:"path" form:"path"`
|
||||||
|
UserId string `json:"userId" form:"userId"`
|
||||||
|
Status int `json:"status" form:"status"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package request
|
||||||
|
|
||||||
|
import common "sundynix-go/model/commom/request"
|
||||||
|
|
||||||
|
type GetRoleList struct {
|
||||||
|
common.PageInfo
|
||||||
|
Code string `json:"code" form:"code"`
|
||||||
|
Name string `json:"name" form:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GrantMenu struct {
|
||||||
|
RoleId string `json:"roleId"`
|
||||||
|
MenuIds []string `json:"menuIds"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package request
|
||||||
|
|
||||||
|
import common "sundynix-go/model/commom/request"
|
||||||
|
|
||||||
|
type Login struct {
|
||||||
|
Account string `json:"account"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Captcha string `json:"captcha"`
|
||||||
|
CaptchaId string `json:"captchaId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetUserList struct {
|
||||||
|
common.PageInfo
|
||||||
|
Account string `json:"account" form:"account"`
|
||||||
|
Phone string `json:"phone" form:"phone"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChangePwd struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
NewPwd string `json:"newPwd"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GrantRole struct {
|
||||||
|
UserId string `json:"userId"`
|
||||||
|
RoleIds []string `json:"roleIds"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package response
|
||||||
|
|
||||||
|
type WxCode2SessionResp struct {
|
||||||
|
SessionKey string `json:"session_key"`
|
||||||
|
Unionid string `json:"unionid"`
|
||||||
|
Openid string `json:"openid"`
|
||||||
|
Errcode int32 `json:"errcode"`
|
||||||
|
Errmsg string `json:"errmsg"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package response
|
||||||
|
|
||||||
|
import "sundynix-go/model/system"
|
||||||
|
|
||||||
|
type UploadFileResponse struct {
|
||||||
|
File system.Oss `json:"file"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package response
|
||||||
|
|
||||||
|
type CaptchaRes struct {
|
||||||
|
CaptchaId string `json:"captchaId"`
|
||||||
|
Captcha string `json:"captcha"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package response
|
||||||
|
|
||||||
|
import "sundynix-go/model/system"
|
||||||
|
|
||||||
|
type LoginResponse struct {
|
||||||
|
User system.User `json:"user"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
ExpiresAt int64 `json:"expiresAt"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import "sundynix-go/global"
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
global.BaseModel
|
||||||
|
ClientId string `gorm:"size:20;" json:"clientId"`
|
||||||
|
Name string `gorm:"size:50;" json:"name"`
|
||||||
|
GrantType string `gorm:"size:50;" json:"grantType"`
|
||||||
|
AdditionalInfo string `gorm:"type:text" json:"additionalInfo"`
|
||||||
|
ActiveTimeout int64 `json:"activeTimeout"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import "sundynix-go/global"
|
||||||
|
|
||||||
|
type Menu struct {
|
||||||
|
global.BaseModel
|
||||||
|
ParentId string `gorm:"size:100;default:'0'" json:"parentId" form:"parentId"`
|
||||||
|
Category int `json:"category" form:"category"`
|
||||||
|
Name string `gorm:"size:20" json:"name" form:"name"`
|
||||||
|
Title string `gorm:"size:20" json:"title" form:"title"`
|
||||||
|
Code string `gorm:"size:20" json:"code" form:"code"`
|
||||||
|
Permission string `gorm:"size:20" json:"permission" form:"permission"`
|
||||||
|
Locale string `gorm:"size:50" json:"locale" form:"locale"`
|
||||||
|
Icon string `gorm:"size:20" json:"icon" form:"icon"`
|
||||||
|
Sort int `json:"sort" form:"sort"`
|
||||||
|
Children []*Menu `json:"children" gorm:"-"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sundynix-go/global"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SysOperationRecord struct {
|
||||||
|
global.BaseModel
|
||||||
|
Ip string `json:"ip" form:"ip" gorm:"column:ip;comment:请求ip"` // 请求ip
|
||||||
|
Method string `json:"method" form:"method" gorm:"column:method;comment:请求方法"` // 请求方法
|
||||||
|
Path string `json:"path" form:"path" gorm:"column:path;comment:请求路径"` // 请求路径
|
||||||
|
Status int `json:"status" form:"status" gorm:"column:status;comment:请求状态"` // 请求状态
|
||||||
|
Latency time.Duration `json:"latency" form:"latency" gorm:"column:latency;comment:延迟" swaggertype:"string"` // 延迟
|
||||||
|
Agent string `json:"agent" form:"agent" gorm:"type:text;column:agent;comment:代理"` // 代理
|
||||||
|
ErrorMessage string `json:"erroMessage" form:"error_message" gorm:"column:error_message;comment:错误信息"` // 错误信息
|
||||||
|
Body string `json:"body" form:"body" gorm:"type:text;column:body;comment:请求Body"` // 请求Body
|
||||||
|
Resp string `json:"resp" form:"resp" gorm:"type:text;column:resp;comment:响应Body"` // 响应Body
|
||||||
|
UserId string `json:"userId" form:"user_id" gorm:"column:user_id;comment:用户id"` // 用户id
|
||||||
|
User User `json:"user"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import "sundynix-go/global"
|
||||||
|
|
||||||
|
type Role struct {
|
||||||
|
global.BaseModel
|
||||||
|
Name string `gorm:"size:20" json:"name" form:"name"`
|
||||||
|
Code string `gorm:"size:20" json:"code" form:"code"`
|
||||||
|
Sort int `json:"sort" form:"sort"`
|
||||||
|
Menus []*Menu `gorm:"many2many:role_menu;"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
type RoleMenu struct {
|
||||||
|
RoleId string `json:"roleId" gorm:"size:100;column:role_id;comment:角色id"`
|
||||||
|
MenuId string `json:"menuId" gorm:"size:100;column:menu_id;comment:菜单id"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sundynix-go/global"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Login interface {
|
||||||
|
GetAccount() string
|
||||||
|
GetUserId() string
|
||||||
|
GetUserInfo() any
|
||||||
|
}
|
||||||
|
type User struct {
|
||||||
|
global.BaseModel
|
||||||
|
TenantId string `gorm:"size:20;" json:"tenantId" form:"tenantId"`
|
||||||
|
ClientId string `gorm:"size:20;" json:"clientId"`
|
||||||
|
Name string `gorm:"size:20" json:"name" form:"name"`
|
||||||
|
Account string `gorm:"size:11;" json:"account" form:"account"`
|
||||||
|
Password string `gorm:"size:100;" json:"-" form:"password"`
|
||||||
|
NickName string `gorm:"size:20;column:nick_name" json:"nickName" form:"nickName"`
|
||||||
|
Phone string `gorm:"size:20;column:phone" json:"phone" form:"phone"`
|
||||||
|
SessionKey string `gorm:"size:80;column:session_key" json:"sessionKey" form:"sessionKey"`
|
||||||
|
UnionId string `gorm:"size:80;column:union_id" json:"unionId"`
|
||||||
|
MiniOpenId string `gorm:"size:80;column:mini_open_id" json:"miniOpenId" form:"miniOpenId"`
|
||||||
|
SaOpenId string `gorm:"size:80;column:sa_open_id" json:"saOpenId"`
|
||||||
|
AvatarId string `gorm:"size:50;column:avatar_id" json:"avatarId"`
|
||||||
|
Avatar *Oss `gorm:"foreignKey:AvatarId" json:"avatar"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) GetAccount() string {
|
||||||
|
return u.Account
|
||||||
|
}
|
||||||
|
func (u *User) GetUserId() string {
|
||||||
|
return u.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) GetUserInfo() any {
|
||||||
|
return *u
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
type UserRole struct {
|
||||||
|
UserId string `json:"userId" gorm:"size:100;column:user_id;comment:用户id"`
|
||||||
|
RoleId string `json:"roleId" gorm:"size:100;column:role_id;comment:角色id"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package httpclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 全局单例变量
|
||||||
|
var (
|
||||||
|
once sync.Once // 保证初始化只执行一次
|
||||||
|
globalClient *http.Client // 复用的 HTTP Client 实例
|
||||||
|
)
|
||||||
|
|
||||||
|
// InitHttpClient 初始化全局 HTTP Client(手动调用,可选)
|
||||||
|
// 配置连接池参数,优化连接复用
|
||||||
|
func InitHttpClient() {
|
||||||
|
once.Do(func() {
|
||||||
|
// 核心:配置 Transport(连接池核心参数)
|
||||||
|
transport := &http.Transport{
|
||||||
|
// 全局最大空闲连接数(避免连接数过多)
|
||||||
|
MaxIdleConns: 100,
|
||||||
|
// 空闲连接超时时间(超时后关闭,释放资源)
|
||||||
|
IdleConnTimeout: 30 * time.Second,
|
||||||
|
// 每个目标主机的最大空闲连接数(避免单主机占用过多连接)
|
||||||
|
MaxIdleConnsPerHost: 10,
|
||||||
|
MaxConnsPerHost: 20, // 新增:每个目标主机的最大并发连接数(防止打满服务器)
|
||||||
|
// 禁用压缩(根据业务需求调整,比如调用微信接口可开启)
|
||||||
|
DisableCompression: false,
|
||||||
|
// TCP 连接建立超时(防止连接挂死)
|
||||||
|
DialContext: (&net.Dialer{
|
||||||
|
Timeout: 10 * time.Second, // 连接建立超时
|
||||||
|
KeepAlive: 30 * time.Second, // TCP 保活时间(维持长连接)
|
||||||
|
}).DialContext,
|
||||||
|
// TLS 握手超时(HTTPS 场景必配)
|
||||||
|
TLSHandshakeTimeout: 5 * time.Second,
|
||||||
|
/// 等待响应头的超时
|
||||||
|
ResponseHeaderTimeout: 15 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建 HTTP Client
|
||||||
|
globalClient = &http.Client{
|
||||||
|
Timeout: 20 * time.Second, // 整个请求的超时(连接+读取+写入)
|
||||||
|
Transport: transport,
|
||||||
|
// 可选:禁用自动重定向(根据业务需求,比如微信接口无需重定向)
|
||||||
|
// CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
|
// return http.ErrUseLastResponse
|
||||||
|
// },
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetClient 获取全局 HTTP Client 实例(推荐使用此方法调用)
|
||||||
|
// 懒加载:如果未初始化,自动触发初始化
|
||||||
|
func GetClient() *http.Client {
|
||||||
|
InitHttpClient() // 兜底:确保初始化
|
||||||
|
return globalClient
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sundynix-go/router/plant"
|
||||||
|
"sundynix-go/router/system"
|
||||||
|
)
|
||||||
|
|
||||||
|
var GroupApp = new(Group)
|
||||||
|
|
||||||
|
// Group 路由组
|
||||||
|
type Group struct {
|
||||||
|
System system.SysRouterGroup
|
||||||
|
Plant plant.MyPlantRouter
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package plant
|
||||||
|
|
||||||
|
import v1 "sundynix-go/api/v1"
|
||||||
|
|
||||||
|
type RouterGroup struct {
|
||||||
|
MyPlantRouter
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化路由
|
||||||
|
var (
|
||||||
|
myPlantApi = v1.ApiGroupApp.PlantApiGroup.MyPlantApi
|
||||||
|
)
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package plant
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MyPlantRouter struct{}
|
||||||
|
|
||||||
|
func (c *MyPlantRouter) InitPlantRouter(Router *gin.RouterGroup) {
|
||||||
|
myPlantRouter := Router.Group("plant")
|
||||||
|
{
|
||||||
|
myPlantRouter.POST("add", myPlantApi.AddPlant) // 添加植物
|
||||||
|
myPlantRouter.POST("page", myPlantApi.PlantPage) // 分页获取植物
|
||||||
|
myPlantRouter.GET("detail", myPlantApi.PlantDetail) // 获取植物详情
|
||||||
|
myPlantRouter.POST("update", myPlantApi.UpdatePlant) // 修改基本信息
|
||||||
|
|
||||||
|
myPlantRouter.GET("todayTask", myPlantApi.TodayTask) // 获取今日任务
|
||||||
|
myPlantRouter.POST("completeTask", myPlantApi.CompleteTask) // 完成任务
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthRouter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitAuthRouter 初始化登录路由
|
||||||
|
func (s *AuthRouter) InitAuthRouter(Router *gin.RouterGroup) {
|
||||||
|
loginRouter := Router.Group("auth")
|
||||||
|
{
|
||||||
|
loginRouter.POST("login", authApi.Login)
|
||||||
|
loginRouter.GET("captcha", authApi.Captcha)
|
||||||
|
loginRouter.GET("logout", authApi.Logout) // 服务端不保存任何登录状态 退出实际是禁用了当前的jwt
|
||||||
|
loginRouter.GET("miniLogin", authApi.MiniLogin) //小程序登录
|
||||||
|
loginRouter.GET("getPhone", authApi.GetPhone) // 获取手机号
|
||||||
|
loginRouter.GET("getLocation", authApi.GetLocation) // 获取位置
|
||||||
|
loginRouter.GET("getWeather", authApi.GetWeather) // 获取天气
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import "github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
type ClientRouter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ClientRouter) InitClientRouter(Router *gin.RouterGroup) {
|
||||||
|
clientRouter := Router.Group("client")
|
||||||
|
{
|
||||||
|
clientRouter.POST("save", clientApi.SaveClient)
|
||||||
|
clientRouter.POST("update", clientApi.UpdateClient)
|
||||||
|
clientRouter.POST("getClientList", clientApi.GetClientList)
|
||||||
|
clientRouter.POST("delete", clientApi.Delete)
|
||||||
|
clientRouter.GET("detail", clientApi.Detail)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import v1 "sundynix-go/api/v1"
|
||||||
|
|
||||||
|
type SysRouterGroup struct {
|
||||||
|
AuthRouter
|
||||||
|
UserRouter
|
||||||
|
ClientRouter
|
||||||
|
RoleRouter
|
||||||
|
MenuRouter
|
||||||
|
OperationRecordRouter
|
||||||
|
OssRouter
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化路由
|
||||||
|
var (
|
||||||
|
authApi = v1.ApiGroupApp.SystemApiGroup.AuthApi
|
||||||
|
userApi = v1.ApiGroupApp.SystemApiGroup.UserApi
|
||||||
|
clientApi = v1.ApiGroupApp.SystemApiGroup.ClientApi
|
||||||
|
roleApi = v1.ApiGroupApp.SystemApiGroup.RoleApi
|
||||||
|
menuApi = v1.ApiGroupApp.SystemApiGroup.MenuApi
|
||||||
|
operationRecordApi = v1.ApiGroupApp.SystemApiGroup.OperationRecordApi
|
||||||
|
ossApi = v1.ApiGroupApp.SystemApiGroup.OssApi
|
||||||
|
)
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import "github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
type MenuRouter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MenuRouter) InitMenuRouter(Router *gin.RouterGroup) {
|
||||||
|
menuRouter := Router.Group("menu")
|
||||||
|
{
|
||||||
|
menuRouter.GET("route", menuApi.Route)
|
||||||
|
menuRouter.POST("getAllMenuTree", menuApi.GetAllMenuTree)
|
||||||
|
menuRouter.GET("getUserMenuTree", menuApi.GetUserMenuTree)
|
||||||
|
menuRouter.POST("save", menuApi.SaveMenu)
|
||||||
|
menuRouter.POST("update", menuApi.UpdateMenu)
|
||||||
|
menuRouter.GET("delete", menuApi.DeleteMenu)
|
||||||
|
menuRouter.GET("detail", menuApi.Detail)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import "github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
type OperationRecordRouter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *OperationRecordRouter) InitOperationRecordRouter(Router *gin.RouterGroup) {
|
||||||
|
operationRecordRouter := Router.Group("operationRecord")
|
||||||
|
{
|
||||||
|
operationRecordRouter.POST("createOperationRecord", operationRecordApi.CreateOperationRecord) // 新增操作记录
|
||||||
|
operationRecordRouter.GET("getOperationRecordList", operationRecordApi.GetRecordList) // 获取操作记录列表
|
||||||
|
operationRecordRouter.GET("getOperationRecordById", operationRecordApi.GetRecordById) // 获取操作记录
|
||||||
|
operationRecordRouter.DELETE("delete", operationRecordApi.DeleteRecordsByIds) // 批量删除操作记录
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import "github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
type OssRouter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *OssRouter) InitOssRouter(Router *gin.RouterGroup) {
|
||||||
|
ossRouter := Router.Group("oss")
|
||||||
|
{
|
||||||
|
ossRouter.POST("upload", ossApi.UploadFile)
|
||||||
|
ossRouter.POST("delete", ossApi.DeleteFile)
|
||||||
|
ossRouter.POST("getFileList", ossApi.GetFileList)
|
||||||
|
ossRouter.GET("getFile", ossApi.Detail)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import "github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
type RoleRouter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RoleRouter) InitRoleRouter(router *gin.RouterGroup) {
|
||||||
|
roleRouter := router.Group("role")
|
||||||
|
{
|
||||||
|
roleRouter.POST("save", roleApi.SaveRole)
|
||||||
|
roleRouter.POST("update", roleApi.UpdateRole)
|
||||||
|
roleRouter.POST("getRoleList", roleApi.GetRoleList)
|
||||||
|
roleRouter.POST("delete", roleApi.Delete)
|
||||||
|
roleRouter.GET("detail", roleApi.Detail)
|
||||||
|
roleRouter.POST("grantMenu", roleApi.GrantMenu)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserRouter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *UserRouter) InitUserRouter(Router *gin.RouterGroup) {
|
||||||
|
userRouter := Router.Group("user")
|
||||||
|
{
|
||||||
|
userRouter.POST("save", userApi.SaveUser)
|
||||||
|
userRouter.POST("update", userApi.UpdateUser)
|
||||||
|
userRouter.POST("getUserList", userApi.GetUserList)
|
||||||
|
userRouter.POST("delete", userApi.Delete)
|
||||||
|
userRouter.GET("detail", userApi.Detail)
|
||||||
|
userRouter.POST("changePassword", userApi.ChangePassword)
|
||||||
|
userRouter.POST("grantRole", userApi.GrantRole)
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user