From 3115b58cb295305a3d53d15f02da663e860182ab Mon Sep 17 00:00:00 2001 From: Blizzard Date: Fri, 6 Feb 2026 14:44:06 +0800 Subject: [PATCH] init: initial commit --- .gitignore | 2 + OPTIMIZATION_REPORT.md | 88 + README.md | 74 + README.zh.md | 73 + api/v1/enter.go | 15 + api/v1/plant/enter.go | 11 + api/v1/plant/my_plant.go | 159 + api/v1/system/auth.go | 185 + api/v1/system/enter.go | 23 + api/v1/system/oss.go | 116 + api/v1/system/sys_client.go | 141 + api/v1/system/sys_menu.go | 159 + api/v1/system/sys_operation_record.go | 79 + api/v1/system/sys_role.go | 163 + api/v1/system/sys_user.go | 188 + config-dev.yaml | 90 + config-prod.yaml | 80 + config/config.go | 19 + config/config_db.go | 8 + config/config_jwt.go | 8 + config/config_redis.go | 10 + config/db_list.go | 52 + config/gorm_mysql.go | 9 + config/gorm_pgsql.go | 17 + config/gorm_sqlite.go | 13 + config/mini_program.go | 6 + config/oss_minio.go | 11 + config/oss_tencent.go | 10 + config/rocket_mq.go | 12 + config/service_account.go | 6 + config/system.go | 9 + config/wechat_pay.go | 12 + config/zap.go | 81 + core/internal/constant.go | 8 + core/internal/cutter.go | 161 + core/internal/zap_core.go | 68 + core/viper.go | 58 + core/zap.go | 44 + docs/docs.go | 7084 ++++++++++++++++++ docs/swagger.json | 7058 +++++++++++++++++ docs/swagger.yaml | 4336 +++++++++++ global/global.go | 27 + global/model.go | 35 + go.mod | 124 + go.sum | 360 + initialize/gorm.go | 53 + initialize/gorm_mysql.go | 43 + initialize/gorm_pgsql.go | 40 + initialize/gorm_sqlite.go | 37 + initialize/internal/gorm.go | 57 + initialize/internal/gorm_logger_writer.go | 39 + initialize/redis.go | 48 + initialize/router.go | 70 + initialize/timer.go | 41 + main.go | 51 + middleware/auth.go | 56 + middleware/operation.go | 77 + model/commom/request/common.go | 63 + model/commom/response/common.go | 12 + model/commom/response/response.go | 60 + model/plant/my_plant.go | 50 + model/plant/my_plant_care_plan.go | 15 + model/plant/my_plant_care_record.go | 13 + model/plant/my_plant_care_task.go | 18 + model/plant/request/my_plant.go | 36 + model/plant/response/badge.go | 6 + model/plant/response/my_plant.go | 12 + model/plant/response/ocr.go | 22 + model/system/oss.go | 17 + model/system/request/captcha.go | 15 + model/system/request/jwt.go | 18 + model/system/request/oss.go | 8 + model/system/request/sys_client.go | 9 + model/system/request/sys_menu.go | 6 + model/system/request/sys_operation_record.go | 14 + model/system/request/sys_role.go | 14 + model/system/request/sys_user.go | 26 + model/system/response/WxCode2SessionResp.go | 9 + model/system/response/oss.go | 7 + model/system/response/sys_captcha.go | 6 + model/system/response/sys_user.go | 9 + model/system/sys_client.go | 12 + model/system/sys_menu.go | 17 + model/system/sys_operation_record.go | 21 + model/system/sys_role.go | 11 + model/system/sys_role_menu.go | 6 + model/system/sys_user.go | 38 + model/system/sys_user_role.go | 6 + pkg/httpclient/http_client.go | 59 + router/enter.go | 14 + router/plant/enter.go | 12 + router/plant/plant_router.go | 22 + router/system/auth_router.go | 22 + router/system/client_router.go | 17 + router/system/enter.go | 24 + router/system/menu_router.go | 19 + router/system/operation_record.go | 16 + router/system/oss_router.go | 16 + router/system/role_router.go | 18 + router/system/user_router.go | 21 + service/enter.go | 13 + service/plant/enter.go | 5 + service/plant/my_plant.go | 224 + service/plant/pay.go | 206 + service/system/enter.go | 11 + service/system/oss.go | 135 + service/system/sys_client.go | 61 + service/system/sys_jwt.go | 26 + service/system/sys_menu.go | 126 + service/system/sys_operation_record.go | 57 + service/system/sys_role.go | 89 + service/system/sys_user.go | 269 + task/care_message_send_task.go | 117 + task/care_reminder.go | 117 + utils/auth/claims.go | 130 + utils/auth/jwt.go | 89 + utils/captcha/redis.go | 5 + utils/directory.go | 20 + utils/hash.go | 32 + utils/hash_test.go | 111 + utils/human_duration.go | 30 + utils/location/location.go | 168 + utils/location/weather.go | 186 + utils/timer/interval.go | 46 + utils/timer/timed_task.go | 219 + utils/uniqueid/id_generator.go | 41 + utils/uniqueid/out_trade_no.go | 32 + utils/upload/minio_oss.go | 106 + utils/upload/oss_instance.go | 31 + utils/upload/tencent_cos.go | 60 + utils/validator.go | 294 + utils/wechat/access_token.go | 54 + utils/wechat/pay.go | 39 + 133 files changed, 25889 insertions(+) create mode 100644 .gitignore create mode 100644 OPTIMIZATION_REPORT.md create mode 100644 README.md create mode 100644 README.zh.md create mode 100644 api/v1/enter.go create mode 100644 api/v1/plant/enter.go create mode 100644 api/v1/plant/my_plant.go create mode 100644 api/v1/system/auth.go create mode 100644 api/v1/system/enter.go create mode 100644 api/v1/system/oss.go create mode 100644 api/v1/system/sys_client.go create mode 100644 api/v1/system/sys_menu.go create mode 100644 api/v1/system/sys_operation_record.go create mode 100644 api/v1/system/sys_role.go create mode 100644 api/v1/system/sys_user.go create mode 100644 config-dev.yaml create mode 100644 config-prod.yaml create mode 100644 config/config.go create mode 100644 config/config_db.go create mode 100644 config/config_jwt.go create mode 100644 config/config_redis.go create mode 100644 config/db_list.go create mode 100644 config/gorm_mysql.go create mode 100644 config/gorm_pgsql.go create mode 100644 config/gorm_sqlite.go create mode 100644 config/mini_program.go create mode 100644 config/oss_minio.go create mode 100644 config/oss_tencent.go create mode 100644 config/rocket_mq.go create mode 100644 config/service_account.go create mode 100644 config/system.go create mode 100644 config/wechat_pay.go create mode 100644 config/zap.go create mode 100644 core/internal/constant.go create mode 100644 core/internal/cutter.go create mode 100644 core/internal/zap_core.go create mode 100644 core/viper.go create mode 100644 core/zap.go create mode 100644 docs/docs.go create mode 100644 docs/swagger.json create mode 100644 docs/swagger.yaml create mode 100644 global/global.go create mode 100644 global/model.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 initialize/gorm.go create mode 100644 initialize/gorm_mysql.go create mode 100644 initialize/gorm_pgsql.go create mode 100644 initialize/gorm_sqlite.go create mode 100644 initialize/internal/gorm.go create mode 100644 initialize/internal/gorm_logger_writer.go create mode 100644 initialize/redis.go create mode 100644 initialize/router.go create mode 100644 initialize/timer.go create mode 100644 main.go create mode 100644 middleware/auth.go create mode 100644 middleware/operation.go create mode 100644 model/commom/request/common.go create mode 100644 model/commom/response/common.go create mode 100644 model/commom/response/response.go create mode 100644 model/plant/my_plant.go create mode 100644 model/plant/my_plant_care_plan.go create mode 100644 model/plant/my_plant_care_record.go create mode 100644 model/plant/my_plant_care_task.go create mode 100644 model/plant/request/my_plant.go create mode 100644 model/plant/response/badge.go create mode 100644 model/plant/response/my_plant.go create mode 100644 model/plant/response/ocr.go create mode 100644 model/system/oss.go create mode 100644 model/system/request/captcha.go create mode 100644 model/system/request/jwt.go create mode 100644 model/system/request/oss.go create mode 100644 model/system/request/sys_client.go create mode 100644 model/system/request/sys_menu.go create mode 100644 model/system/request/sys_operation_record.go create mode 100644 model/system/request/sys_role.go create mode 100644 model/system/request/sys_user.go create mode 100644 model/system/response/WxCode2SessionResp.go create mode 100644 model/system/response/oss.go create mode 100644 model/system/response/sys_captcha.go create mode 100644 model/system/response/sys_user.go create mode 100644 model/system/sys_client.go create mode 100644 model/system/sys_menu.go create mode 100644 model/system/sys_operation_record.go create mode 100644 model/system/sys_role.go create mode 100644 model/system/sys_role_menu.go create mode 100644 model/system/sys_user.go create mode 100644 model/system/sys_user_role.go create mode 100644 pkg/httpclient/http_client.go create mode 100644 router/enter.go create mode 100644 router/plant/enter.go create mode 100644 router/plant/plant_router.go create mode 100644 router/system/auth_router.go create mode 100644 router/system/client_router.go create mode 100644 router/system/enter.go create mode 100644 router/system/menu_router.go create mode 100644 router/system/operation_record.go create mode 100644 router/system/oss_router.go create mode 100644 router/system/role_router.go create mode 100644 router/system/user_router.go create mode 100644 service/enter.go create mode 100644 service/plant/enter.go create mode 100644 service/plant/my_plant.go create mode 100644 service/plant/pay.go create mode 100644 service/system/enter.go create mode 100644 service/system/oss.go create mode 100644 service/system/sys_client.go create mode 100644 service/system/sys_jwt.go create mode 100644 service/system/sys_menu.go create mode 100644 service/system/sys_operation_record.go create mode 100644 service/system/sys_role.go create mode 100644 service/system/sys_user.go create mode 100644 task/care_message_send_task.go create mode 100644 task/care_reminder.go create mode 100644 utils/auth/claims.go create mode 100644 utils/auth/jwt.go create mode 100644 utils/captcha/redis.go create mode 100644 utils/directory.go create mode 100644 utils/hash.go create mode 100644 utils/hash_test.go create mode 100644 utils/human_duration.go create mode 100644 utils/location/location.go create mode 100644 utils/location/weather.go create mode 100644 utils/timer/interval.go create mode 100644 utils/timer/timed_task.go create mode 100644 utils/uniqueid/id_generator.go create mode 100644 utils/uniqueid/out_trade_no.go create mode 100644 utils/upload/minio_oss.go create mode 100644 utils/upload/oss_instance.go create mode 100644 utils/upload/tencent_cos.go create mode 100644 utils/validator.go create mode 100644 utils/wechat/access_token.go create mode 100644 utils/wechat/pay.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6c28e8e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +log \ No newline at end of file diff --git a/OPTIMIZATION_REPORT.md b/OPTIMIZATION_REPORT.md new file mode 100644 index 0000000..e30cbad --- /dev/null +++ b/OPTIMIZATION_REPORT.md @@ -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` 闭包 | diff --git a/README.md b/README.md new file mode 100644 index 0000000..f8ee538 --- /dev/null +++ b/README.md @@ -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 & +~~~ \ No newline at end of file diff --git a/README.zh.md b/README.zh.md new file mode 100644 index 0000000..a5e28c4 --- /dev/null +++ b/README.zh.md @@ -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` diff --git a/api/v1/enter.go b/api/v1/enter.go new file mode 100644 index 0000000..40a28a9 --- /dev/null +++ b/api/v1/enter.go @@ -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 +} diff --git a/api/v1/plant/enter.go b/api/v1/plant/enter.go new file mode 100644 index 0000000..b0efb12 --- /dev/null +++ b/api/v1/plant/enter.go @@ -0,0 +1,11 @@ +package plant + +import "sundynix-go/service" + +type ApiGroup struct { + MyPlantApi +} + +var ( + plantService = service.GroupApp.PlantServiceGroup.MyPlantService +) diff --git a/api/v1/plant/my_plant.go b/api/v1/plant/my_plant.go new file mode 100644 index 0000000..5fd933b --- /dev/null +++ b/api/v1/plant/my_plant.go @@ -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) +} diff --git a/api/v1/system/auth.go b/api/v1/system/auth.go new file mode 100644 index 0000000..b83a6af --- /dev/null +++ b/api/v1/system/auth.go @@ -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) + +} diff --git a/api/v1/system/enter.go b/api/v1/system/enter.go new file mode 100644 index 0000000..343112f --- /dev/null +++ b/api/v1/system/enter.go @@ -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 +) diff --git a/api/v1/system/oss.go b/api/v1/system/oss.go new file mode 100644 index 0000000..cac3606 --- /dev/null +++ b/api/v1/system/oss.go @@ -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) +} diff --git a/api/v1/system/sys_client.go b/api/v1/system/sys_client.go new file mode 100644 index 0000000..3aea782 --- /dev/null +++ b/api/v1/system/sys_client.go @@ -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) + +} diff --git a/api/v1/system/sys_menu.go b/api/v1/system/sys_menu.go new file mode 100644 index 0000000..e0cddd1 --- /dev/null +++ b/api/v1/system/sys_menu.go @@ -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) +} diff --git a/api/v1/system/sys_operation_record.go b/api/v1/system/sys_operation_record.go new file mode 100644 index 0000000..6d06d92 --- /dev/null +++ b/api/v1/system/sys_operation_record.go @@ -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) +} diff --git a/api/v1/system/sys_role.go b/api/v1/system/sys_role.go new file mode 100644 index 0000000..c08bdca --- /dev/null +++ b/api/v1/system/sys_role.go @@ -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) +} diff --git a/api/v1/system/sys_user.go b/api/v1/system/sys_user.go new file mode 100644 index 0000000..a0e4be4 --- /dev/null +++ b/api/v1/system/sys_user.go @@ -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) +} diff --git a/config-dev.yaml b/config-dev.yaml new file mode 100644 index 0000000..a747928 --- /dev/null +++ b/config-dev.yaml @@ -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 \ No newline at end of file diff --git a/config-prod.yaml b/config-prod.yaml new file mode 100644 index 0000000..64c0f4d --- /dev/null +++ b/config-prod.yaml @@ -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 \ No newline at end of file diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..7e7d181 --- /dev/null +++ b/config/config.go @@ -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"` //微信支付 +} diff --git a/config/config_db.go b/config/config_db.go new file mode 100644 index 0000000..92bb5c6 --- /dev/null +++ b/config/config_db.go @@ -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"` +} diff --git a/config/config_jwt.go b/config/config_jwt.go new file mode 100644 index 0000000..c95d30d --- /dev/null +++ b/config/config_jwt.go @@ -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"` // 签发者 +} diff --git a/config/config_redis.go b/config/config_redis.go new file mode 100644 index 0000000..011cbb8 --- /dev/null +++ b/config/config_redis.go @@ -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"` // 集群模式下的节点地址列表 +} diff --git a/config/db_list.go b/config/db_list.go new file mode 100644 index 0000000..5c9edf4 --- /dev/null +++ b/config/db_list.go @@ -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"` +} diff --git a/config/gorm_mysql.go b/config/gorm_mysql.go new file mode 100644 index 0000000..314c342 --- /dev/null +++ b/config/gorm_mysql.go @@ -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 +} diff --git a/config/gorm_pgsql.go b/config/gorm_pgsql.go new file mode 100644 index 0000000..acfbde4 --- /dev/null +++ b/config/gorm_pgsql.go @@ -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 +} diff --git a/config/gorm_sqlite.go b/config/gorm_sqlite.go new file mode 100644 index 0000000..d585670 --- /dev/null +++ b/config/gorm_sqlite.go @@ -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") +} diff --git a/config/mini_program.go b/config/mini_program.go new file mode 100644 index 0000000..2f3d146 --- /dev/null +++ b/config/mini_program.go @@ -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"` +} diff --git a/config/oss_minio.go b/config/oss_minio.go new file mode 100644 index 0000000..a0faac7 --- /dev/null +++ b/config/oss_minio.go @@ -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"` +} diff --git a/config/oss_tencent.go b/config/oss_tencent.go new file mode 100644 index 0000000..39a29d1 --- /dev/null +++ b/config/oss_tencent.go @@ -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"` +} diff --git a/config/rocket_mq.go b/config/rocket_mq.go new file mode 100644 index 0000000..5841bbe --- /dev/null +++ b/config/rocket_mq.go @@ -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"` +} diff --git a/config/service_account.go b/config/service_account.go new file mode 100644 index 0000000..3194e2c --- /dev/null +++ b/config/service_account.go @@ -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"` +} diff --git a/config/system.go b/config/system.go new file mode 100644 index 0000000..092626a --- /dev/null +++ b/config/system.go @@ -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"` +} diff --git a/config/wechat_pay.go b/config/wechat_pay.go new file mode 100644 index 0000000..5d3e4b9 --- /dev/null +++ b/config/wechat_pay.go @@ -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"` +} diff --git a/config/zap.go b/config/zap.go new file mode 100644 index 0000000..068ded3 --- /dev/null +++ b/config/zap.go @@ -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 + } +} diff --git a/core/internal/constant.go b/core/internal/constant.go new file mode 100644 index 0000000..a1e84e6 --- /dev/null +++ b/core/internal/constant.go @@ -0,0 +1,8 @@ +package internal + +const ( + ConfigDefaultFile = "config-dev.yaml" + ConfigProdFile = "config-prod.yaml" + ConfigDebugFile = "config-debug.yaml" + ConfigReleaseFile = "config-release.yaml" +) diff --git a/core/internal/cutter.go b/core/internal/cutter.go new file mode 100644 index 0000000..4eaa3ad --- /dev/null +++ b/core/internal/cutter.go @@ -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 + }) +} diff --git a/core/internal/zap_core.go b/core/internal/zap_core.go new file mode 100644 index 0000000..9121ebe --- /dev/null +++ b/core/internal/zap_core.go @@ -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() +} diff --git a/core/viper.go b/core/viper.go new file mode 100644 index 0000000..5217867 --- /dev/null +++ b/core/viper.go @@ -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 +} diff --git a/core/zap.go b/core/zap.go new file mode 100644 index 0000000..c31117f --- /dev/null +++ b/core/zap.go @@ -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 +} diff --git a/docs/docs.go b/docs/docs.go new file mode 100644 index 0000000..01db91d --- /dev/null +++ b/docs/docs.go @@ -0,0 +1,7084 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": {}, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/auth/captcha": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "登录相关" + ], + "summary": "获取验证码", + "responses": { + "200": { + "description": "获取验证码", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.CaptchaRes" + } + } + } + ] + } + } + } + } + }, + "/auth/getLocation": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "登录相关" + ], + "summary": "获取位置信息", + "parameters": [ + { + "type": "string", + "description": "longitude", + "name": "longitude", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "latitude", + "name": "latitude", + "in": "query", + "required": true + } + ], + "responses": {} + } + }, + "/auth/getPhone": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "登录相关" + ], + "summary": "获取手机号", + "parameters": [ + { + "type": "string", + "description": "code", + "name": "code", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "openId", + "name": "openId", + "in": "query", + "required": true + } + ], + "responses": {} + } + }, + "/auth/getWeather": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "登录相关" + ], + "summary": "获取天气信息", + "parameters": [ + { + "type": "string", + "description": "adcode", + "name": "adcode", + "in": "query", + "required": true + } + ], + "responses": {} + } + }, + "/auth/login": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "登录相关" + ], + "summary": "pc登录", + "parameters": [ + { + "description": "用户名, 密码, 验证码,验证码id", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.Login" + } + } + ], + "responses": { + "200": { + "description": "登录成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/auth/logout": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "登录相关" + ], + "summary": "pc登出", + "responses": { + "200": { + "description": "登出成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/auth/miniLogin": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "登录相关" + ], + "summary": "小程序登录", + "parameters": [ + { + "type": "string", + "description": "code", + "name": "code", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "小程序登录", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.LoginResponse" + } + } + } + ] + } + } + } + } + }, + "/badge/add": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "添加", + "parameters": [ + { + "description": "添加", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateBadge" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"添加成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/all": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "获取所有无分页", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/class/add": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "添加分类", + "parameters": [ + { + "description": "添加分类", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateBadgeCategory" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"添加成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/class/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "删除分类", + "parameters": [ + { + "description": "删除分类", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/class/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "分类详情", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/class/list": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "分类列表", + "parameters": [ + { + "description": "分类列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageCategory" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/class/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "修改分类", + "parameters": [ + { + "description": "修改分类", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateBadgeCategory" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"修改成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "删除ByIds", + "parameters": [ + { + "description": "删除ByIds", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "详情", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/list": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "获取列表有分页", + "parameters": [ + { + "description": "获取列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageBadge" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/listByKeyword": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "获取列表byKeyword无分页", + "parameters": [ + { + "type": "string", + "description": "keyword", + "name": "keyword", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/my/all": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "我的所有徽章", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/my/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "我的徽章详情", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/my/list": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "我的徽章", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "修改", + "parameters": [ + { + "description": "修改", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateBadge" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"修改成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/uploadImg": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "上传图片", + "parameters": [ + { + "description": "上传图片", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UploadFile" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"上传成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/claim/add": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "兑换植物" + ], + "summary": "配置植物", + "parameters": [ + { + "description": "添加", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateClaimPlant" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"添加成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/claim/cancelUnion": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "兑换植物" + ], + "summary": "取消关联百科植物", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"取消关联成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/claim/claim": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "兑换植物" + ], + "summary": "认养", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "addressId", + "name": "addressId", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"认养成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/claim/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "兑换植物" + ], + "summary": "删除ByIds", + "parameters": [ + { + "description": "删除", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/claim/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "兑换植物" + ], + "summary": "详情", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/claim/list": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "兑换植物" + ], + "summary": "获取列表", + "parameters": [ + { + "description": "获取列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageClaimPlant" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/claim/myClaims": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "兑换植物" + ], + "summary": "我的认养", + "parameters": [ + { + "description": "获取列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageClaimPlant" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/claim/unionLibrary": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "兑换植物" + ], + "summary": "关联百科植物", + "parameters": [ + { + "description": "关联", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UnionLibrary" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"关联成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/claim/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "兑换植物" + ], + "summary": "修改植物", + "parameters": [ + { + "description": "修改", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateClaimPlant" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"修改成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/class/add": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库分类" + ], + "summary": "新增分类", + "parameters": [ + { + "description": "新增分类", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateClassification" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"新增成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/class/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库分类" + ], + "summary": "删除分类", + "parameters": [ + { + "description": "删除分类", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/class/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库分类" + ], + "summary": "分类详情", + "parameters": [ + { + "type": "string", + "description": "分类id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/class/list": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库分类" + ], + "summary": "分类列表", + "parameters": [ + { + "description": "分类列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageInfo" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/class/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库分类" + ], + "summary": "修改分类", + "parameters": [ + { + "description": "修改分类", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateClassification" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"修改成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/client/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "客户端管理" + ], + "summary": "删除client", + "parameters": [ + { + "description": "ids", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "删除client", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/client/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "id获取详情", + "produces": [ + "application/json" + ], + "tags": [ + "客户端管理" + ], + "summary": "获取client详情", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "获取client详情", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/system.Client" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/client/getClientList": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "客户端管理" + ], + "summary": "获取client列表", + "parameters": [ + { + "description": "client", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetClientList" + } + } + ], + "responses": { + "200": { + "description": "获取client列表", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/client/save": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "客户端管理" + ], + "summary": "创建client", + "parameters": [ + { + "description": "client", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.Client" + } + } + ], + "responses": { + "200": { + "description": "创建client", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/client/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "客户端管理" + ], + "summary": "更新client", + "parameters": [ + { + "description": "client", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.Client" + } + } + ], + "responses": { + "200": { + "description": "更新client", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/comment/add": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子评论" + ], + "summary": "发表评论", + "parameters": [ + { + "description": "添加评论", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateComment" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"添加成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/comment/delete": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子评论" + ], + "summary": "删除评论", + "parameters": [ + { + "description": "删除评论", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/invitation/add": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "配置中心" + ], + "summary": "配置邀请码", + "parameters": [ + { + "description": "配置邀请码", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateInvitationConfig" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"配置成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/invitation/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "配置中心" + ], + "summary": "删除邀请码配置", + "parameters": [ + { + "description": "删除邀请码", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/invitation/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "配置中心" + ], + "summary": "获取邀请码配置", + "parameters": [ + { + "type": "string", + "description": "邀请码id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/invitation/list": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "配置中心" + ], + "summary": "获取邀请码列表", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/invitation/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "配置中心" + ], + "summary": "更新邀请码配置", + "parameters": [ + { + "description": "更新邀请码", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateInvitationConfig" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"更新成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/qrcode/add": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "配置中心" + ], + "summary": "配置二维码", + "parameters": [ + { + "description": "配置二维码", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateQrcode" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"配置成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/qrcode/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "配置中心" + ], + "summary": "删除二维码", + "parameters": [ + { + "description": "二维码id", + "name": "ids", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/qrcode/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "配置中心" + ], + "summary": "获取二维码详情", + "parameters": [ + { + "type": "string", + "description": "二维码id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/qrcode/list": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "tags": [ + "配置中心" + ], + "summary": "获取二维码", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/qrcode/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "配置中心" + ], + "summary": "更新二维码", + "parameters": [ + { + "description": "更新二维码", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateQrcode" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"更新成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/qrcode/uploadImg": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "配置中心" + ], + "summary": "上传二维码图片", + "parameters": [ + { + "description": "上传二维码图片", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UploadFile" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"上传成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/library/add": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "新增植物库", + "parameters": [ + { + "description": "新增植物库", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateLibrary" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/all": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "获取所有植物库列表", + "parameters": [ + { + "description": "获取所有植物库列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.LibraryList" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/alterClass": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "修改分类", + "parameters": [ + { + "description": "修改分类", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.AlterClass" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "删除植物库", + "parameters": [ + { + "description": "删除植物库", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/deleteClass": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "取消关联分类", + "parameters": [ + { + "description": "取消关联分类", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.DeleteClass" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/deleteImg": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "上传图片", + "parameters": [ + { + "description": "上传图片", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.DeleteOss" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/deleteRelateLibrary": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "删除关联植物", + "parameters": [ + { + "description": "删除关联", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.DeleteRelatedLibrary" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "获取植物库详情", + "parameters": [ + { + "type": "string", + "description": "获取植物库详情", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/hot": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "设置或取消热门", + "parameters": [ + { + "description": "设置热门", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/page": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "获取植物库列表", + "parameters": [ + { + "description": "获取植物库列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.LibraryPage" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/reateLibrary": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "添加关联植物", + "parameters": [ + { + "description": "添加关联", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.AlterRelatedLibrary" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "修改植物库", + "parameters": [ + { + "description": "修改植物库", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateLibrary" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/uploadImg": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "上传图片", + "parameters": [ + { + "description": "上传图片", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UploadOss" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/delete": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "删除menu", + "produces": [ + "application/json" + ], + "tags": [ + "菜单管理" + ], + "summary": "删除menu", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "详情", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "id获取详情", + "produces": [ + "application/json" + ], + "tags": [ + "菜单管理" + ], + "summary": "获取menu详情", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "详情", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/system.Menu" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/getAllMenuTree": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "菜单管理" + ], + "summary": "获取所有菜单树", + "parameters": [ + { + "description": "菜单信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetMenuTree" + } + } + ], + "responses": { + "200": { + "description": "获取所有菜单树", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/system.Menu" + } + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/getUserMenuTree": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "菜单管理" + ], + "summary": "用户菜单数据", + "responses": { + "200": { + "description": "用户菜单数据", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/system.Menu" + } + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/route": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "菜单管理" + ], + "summary": "用户路由", + "responses": { + "200": { + "description": "用户route", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/system.Menu" + } + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/save": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "菜单管理" + ], + "summary": "新增菜单", + "parameters": [ + { + "description": "menu", + "name": "data", + "in": "body", + "schema": { + "$ref": "#/definitions/system.Menu" + } + } + ], + "responses": { + "200": { + "description": "新建菜单/按钮", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "菜单管理" + ], + "summary": "更新菜单", + "parameters": [ + { + "description": "menu", + "name": "data", + "in": "body", + "schema": { + "$ref": "#/definitions/system.Menu" + } + } + ], + "responses": { + "200": { + "description": "更新菜单", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/ocr/base64": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "识别相关" + ], + "summary": "base64植物识别", + "parameters": [ + { + "type": "file", + "description": "植物识别", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "文件OCR", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/ocr/url": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "识别相关" + ], + "summary": "url植物识别", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "文件OCR", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/order/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "订单" + ], + "summary": "删除订单", + "parameters": [ + { + "description": "删除订单", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/order/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "订单" + ], + "summary": "订单详情", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/order/export": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "订单" + ], + "summary": "导出订单", + "parameters": [ + { + "type": "integer", + "description": "页码", + "name": "current", + "in": "query" + }, + { + "type": "integer", + "description": "是否发货 0 1", + "name": "isShipped", + "in": "query" + }, + { + "type": "string", + "description": "关键字", + "name": "keyword", + "in": "query" + }, + { + "type": "integer", + "description": "每页大小", + "name": "pageSize", + "in": "query" + }, + { + "type": "integer", + "description": "支付状态 1.SUCCESS 2.REFUND 3.NOTPAY 4.CLOSED", + "name": "status", + "in": "query" + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/order/page": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "订单" + ], + "summary": "订单列表", + "parameters": [ + { + "description": "获取列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.OrderPage" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/order/ship": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "订单" + ], + "summary": "发货", + "parameters": [ + { + "description": "发货", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/oss/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "文件相关" + ], + "summary": "删除文件", + "parameters": [ + { + "description": "批量删除文件", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "删除文件", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/oss/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "文件相关" + ], + "summary": "文件详情", + "parameters": [ + { + "type": "string", + "description": "文件id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "文件详情", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/oss/getFileList": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "文件相关" + ], + "summary": "文件列表", + "parameters": [ + { + "description": "文件列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetOssFileList" + } + } + ], + "responses": { + "200": { + "description": "文件列表", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/oss/upload": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "文件相关" + ], + "summary": "文件上传", + "parameters": [ + { + "type": "file", + "description": "上传文件", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "上传文件", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/pay/prePay": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "微信支付" + ], + "summary": "支付", + "parameters": [ + { + "type": "string", + "description": "支付", + "name": "orderId", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"支付成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/personal/address/add": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "个人中心" + ], + "summary": "添加地址", + "parameters": [ + { + "description": "添加地址", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateAddress" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"添加成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/personal/address/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "个人中心" + ], + "summary": "删除地址", + "parameters": [ + { + "description": "删除地址", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/personal/address/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "个人中心" + ], + "summary": "地址详情", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/personal/address/list": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "个人中心" + ], + "summary": "地址列表", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/personal/address/setDefault": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "个人中心" + ], + "summary": "设置默认地址", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"设置成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/personal/address/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "个人中心" + ], + "summary": "修改地址", + "parameters": [ + { + "description": "修改地址", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateAddress" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"修改成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/personal/centerCount": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "个人中心" + ], + "summary": "个人中心统计", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/personal/inviteCode/accept": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "个人中心" + ], + "summary": "接受邀请", + "parameters": [ + { + "type": "string", + "description": "inviteCode", + "name": "inviteCode", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"接受成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/personal/inviteCode/code": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "个人中心" + ], + "summary": "生成邀请码", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"生成成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/personal/inviteCode/records": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "个人中心" + ], + "summary": "邀请记录", + "parameters": [ + { + "description": "分页参数", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageInfo" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/add": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "添加植物", + "parameters": [ + { + "description": "创建植物", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateMyPlant" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"添加成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/carePlant": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "立即养护", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"操作成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/careRecords": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "养护记录", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"操作成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "删除ByIds", + "parameters": [ + { + "description": "删除ByIds", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/deleteImg": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "删除图片", + "parameters": [ + { + "description": "删除图片", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.DeleteOss" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "获取ById", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/grow/addRecord": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "添加成长记录", + "parameters": [ + { + "description": "添加成长记录", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateGrowRecord" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"操作成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/grow/recordDetail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "成长记录详情", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"操作成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/grow/recordList": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "成长记录列表", + "parameters": [ + { + "description": "成长记录列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageGrowRecord" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"操作成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/makeCare": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "根据任务完成养护", + "parameters": [ + { + "description": "养护操作", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.MakeCare" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"操作成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/myPlants": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "植物列表不分页", + "parameters": [ + { + "type": "string", + "description": "name", + "name": "name", + "in": "query" + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/needCare": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "待养护", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"操作成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/page": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "植物列表", + "parameters": [ + { + "description": "获取植物列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PagePlant" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/taskProgress": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "任务进度", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"操作成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/todayCare": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "tags": [ + "我的植物" + ], + "summary": "今日养护", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"操作成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "修改植物", + "parameters": [ + { + "description": "修改植物", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateMyPlant" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"修改成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/updateCarePlan": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "修改养护事项周期", + "parameters": [ + { + "description": "修改养护周期", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateCarePlan" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"修改成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/uploadImg": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "上传图片", + "parameters": [ + { + "description": "上传图片", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UploadOss" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"上传成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/post/add": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子" + ], + "summary": "发布", + "parameters": [ + { + "description": "创建帖子", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreatePost" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"添加成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/post/cancelLike": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子" + ], + "summary": "取消点赞", + "parameters": [ + { + "type": "string", + "description": "帖子id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"取消点赞成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/post/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子" + ], + "summary": "删除帖子", + "parameters": [ + { + "description": "删除帖子", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/post/deleteImg": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子" + ], + "summary": "删除帖子图片", + "parameters": [ + { + "description": "删除帖子图片", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.DeleteOss" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/post/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子" + ], + "summary": "帖子详情", + "parameters": [ + { + "type": "string", + "description": "帖子id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/post/like": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子" + ], + "summary": "点赞帖子", + "parameters": [ + { + "type": "string", + "description": "帖子id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"点赞成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/post/myPosts": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子" + ], + "summary": "我的帖子", + "parameters": [ + { + "type": "integer", + "description": "页码", + "name": "current", + "in": "query" + }, + { + "type": "string", + "description": "关键字", + "name": "keyword", + "in": "query" + }, + { + "type": "integer", + "description": "每页大小", + "name": "pageSize", + "in": "query" + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/post/page": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子" + ], + "summary": "帖子列表", + "parameters": [ + { + "description": "帖子列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PostPage" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/post/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子" + ], + "summary": "修改帖子", + "parameters": [ + { + "description": "修改帖子", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdatePost" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"修改成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/post/uploadImg": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子" + ], + "summary": "上传帖子图片", + "parameters": [ + { + "description": "上传帖子图片", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UploadOss" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"上传成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/role/delete": { + "post": { + "description": "删除角色", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "角色管理" + ], + "summary": "删除角色", + "parameters": [ + { + "description": "批量删除角色", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "删除角色", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/role/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "角色管理" + ], + "summary": "角色详情", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "角色详情", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/system.Role" + } + } + } + ] + } + } + } + } + }, + "/role/getRoleList": { + "post": { + "description": "获取角色列表", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "角色管理" + ], + "summary": "获取角色列表", + "parameters": [ + { + "description": "页码, 每页大小, 搜索条件", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetRoleList" + } + } + ], + "responses": { + "200": { + "description": "获取角色列表,返回包括列表,总数,页码,每页大小", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/role/grantMenu": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "角色管理" + ], + "summary": "授权菜单给角色", + "parameters": [ + { + "description": "授权菜单给角色", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GrantMenu" + } + } + ], + "responses": { + "200": { + "description": "授权菜单给角色", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/role/save": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "角色管理" + ], + "summary": "创建角色", + "parameters": [ + { + "description": "角色信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.Role" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/role/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "角色管理" + ], + "summary": "修改角色", + "parameters": [ + { + "description": "角色ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.Role" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/user/changePassword": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "修改密码", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "用户管理" + ], + "summary": "修改密码", + "parameters": [ + { + "description": "用户id", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ChangePwd" + } + } + ], + "responses": { + "200": { + "description": "修改密码成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/system.User" + } + } + } + ] + } + } + } + } + }, + "/user/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "用户管理" + ], + "summary": "删除用户", + "parameters": [ + { + "description": "批量删除用户", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "删除用户", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "用户管理" + ], + "summary": "获取用户详情", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "获取用户详情成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/system.User" + } + } + } + ] + } + } + } + } + }, + "/user/getUserList": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "用户管理" + ], + "summary": "获取用户列表", + "parameters": [ + { + "description": "页码, 每页大小, 搜索条件", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetUserList" + } + } + ], + "responses": { + "200": { + "description": "获取用户列表,返回包括列表,总数,页码,每页大小", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/grantRole": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "用户管理" + ], + "summary": "给用户分配角色", + "parameters": [ + { + "description": "用户ID, 角色ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GrantRole" + } + } + ], + "responses": { + "200": { + "description": "{\"code\": 200, \"data\": [...]}", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/user/save": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "用户管理" + ], + "summary": "新增用户", + "parameters": [ + { + "description": "用户信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.User" + } + } + ], + "responses": { + "200": { + "description": "{\"code\": 200, \"data\": {}, \"msg\": \"添加成功\"}", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/user/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "用户管理" + ], + "summary": "更新用户", + "parameters": [ + { + "description": "用户ID,用户信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.User" + } + } + ], + "responses": { + "200": { + "description": "{\"code\": 200, \"data\": [...]}", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + } + }, + "definitions": { + "request.AlterClass": { + "type": "object", + "properties": { + "id": { + "description": "植物id", + "type": "string" + }, + "ids": { + "description": "要关联的分类ids", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "request.AlterRelatedLibrary": { + "type": "object", + "properties": { + "id": { + "description": "植物id", + "type": "string" + }, + "ids": { + "description": "要关联的植物ids", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "request.CareItem": { + "type": "object", + "properties": { + "desc": { + "type": "string" + }, + "name": { + "type": "string" + }, + "period": { + "type": "integer" + } + } + }, + "request.CarePlan": { + "type": "object", + "properties": { + "desc": { + "type": "string" + }, + "name": { + "description": "农事名称", + "type": "string" + }, + "period": { + "description": "周期", + "type": "integer" + } + } + }, + "request.CareSchedule": { + "type": "object", + "properties": { + "careItems": { + "description": "养护项目", + "type": "array", + "items": { + "$ref": "#/definitions/request.CareItem" + } + }, + "desc": { + "description": "描述", + "type": "string" + }, + "season": { + "description": "季节", + "type": "string" + }, + "sort": { + "description": "排序", + "type": "integer" + } + } + }, + "request.ChangePwd": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "newPwd": { + "type": "string" + } + } + }, + "request.CreateAddress": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "isDefault": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "phone": { + "type": "string" + } + } + }, + "request.CreateBadge": { + "type": "object", + "required": [ + "countLimit" + ], + "properties": { + "categoryId": { + "type": "string" + }, + "countLimit": { + "description": "获取徽章需要完成的操作次数", + "type": "integer" + }, + "desc": { + "type": "string" + }, + "keyword": { + "type": "string" + }, + "name": { + "type": "string" + }, + "ossId": { + "type": "string" + } + } + }, + "request.CreateBadgeCategory": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "desc": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "request.CreateClaimPlant": { + "type": "object", + "properties": { + "content": { + "type": "string" + }, + "libraryId": { + "description": "百科id", + "type": "string" + }, + "name": { + "type": "string" + }, + "ossIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "points": { + "type": "integer" + }, + "postage": { + "description": "邮费 单位(分)", + "type": "integer" + }, + "stock": { + "description": "库存", + "type": "integer" + }, + "tag": { + "type": "string" + }, + "videoUrl": { + "type": "string" + } + } + }, + "request.CreateClassification": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "description": "分类名称", + "type": "string" + }, + "ossId": { + "description": "图片id", + "type": "string" + }, + "tag": { + "description": "标签", + "type": "string" + } + } + }, + "request.CreateComment": { + "type": "object", + "required": [ + "content", + "parentId", + "postId", + "rootId" + ], + "properties": { + "content": { + "description": "评论内容", + "type": "string" + }, + "parentId": { + "description": "父级评论id\" 默认为0", + "type": "string" + }, + "postId": { + "description": "帖子id", + "type": "string" + }, + "rootId": { + "description": "根评论id 默认为0 如评论1 评论2 评论3 1是根评论 2是1的子评论 3是2的子评论 那么2和3的rootId都是1的主键id", + "type": "string" + } + } + }, + "request.CreateGrowRecord": { + "type": "object", + "required": [ + "plantId" + ], + "properties": { + "content": { + "type": "string" + }, + "desc": { + "type": "string" + }, + "name": { + "type": "string" + }, + "ossIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "plantId": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "request.CreateInvitationConfig": { + "type": "object", + "properties": { + "codeExpireDays": { + "description": "邀请码有效期(天)", + "type": "integer" + }, + "codeLength": { + "description": "邀请码长度", + "type": "integer" + }, + "enableAutoAward": { + "description": "是否自动发放积分", + "type": "integer" + }, + "isActive": { + "description": "是否启用", + "type": "integer" + }, + "maxInvitesPerDay": { + "description": "每日最大邀请奖励次数", + "type": "integer" + }, + "maxPointsPerUser": { + "description": "单用户最大邀请奖励积分", + "type": "integer" + }, + "name": { + "description": "配置名称", + "type": "string" + }, + "pointsPerInvite": { + "description": "每成功邀请奖励积分", + "type": "integer" + } + } + }, + "request.CreateLibrary": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "aliases": { + "description": "别名", + "type": "string" + }, + "careSchedule": { + "description": "养护计划", + "type": "array", + "items": { + "$ref": "#/definitions/request.CareSchedule" + } + }, + "classIds": { + "description": "分类id", + "type": "array", + "items": { + "type": "string" + } + }, + "difficulty": { + "description": "种植难度 1-5级", + "type": "integer" + }, + "distributionArea": { + "description": "分布区域", + "type": "string" + }, + "flowerDiameter": { + "description": "花直径(cm)", + "type": "integer" + }, + "floweringColor": { + "description": "花色", + "type": "string" + }, + "floweringPeriod": { + "description": "开花特征", + "type": "string" + }, + "floweringShape": { + "description": "花形", + "type": "string" + }, + "foliageColor": { + "description": "叶色", + "type": "string" + }, + "foliageShape": { + "description": "叶形", + "type": "string" + }, + "foliageType": { + "description": "植物特征", + "type": "string" + }, + "fruit": { + "description": "果", + "type": "string" + }, + "genus": { + "description": "属", + "type": "string" + }, + "growthHabit": { + "description": "生长习性", + "type": "string" + }, + "height": { + "description": "高度(cm)", + "type": "integer" + }, + "isHot": { + "description": "是否热门", + "type": "integer" + }, + "latinName": { + "description": "拉丁名", + "type": "string" + }, + "lifeCycle": { + "description": "生命周期", + "type": "string" + }, + "lightIntensity": { + "description": "光照强度", + "type": "string" + }, + "lightType": { + "description": "光照类型(直射,散射等)", + "type": "string" + }, + "name": { + "description": "基本信息", + "type": "string" + }, + "optimalTempPeriod": { + "description": "最佳温度区间", + "type": "string" + }, + "ossIds": { + "description": "图片", + "type": "array", + "items": { + "type": "string" + } + }, + "pestsDiseases": { + "description": "常见病虫害", + "type": "string" + }, + "relatedLibraryIds": { + "description": "相关推荐 library", + "type": "array", + "items": { + "type": "string" + } + }, + "reproductionMethod": { + "description": "繁殖方法", + "type": "string" + }, + "stem": { + "description": "茎", + "type": "string" + }, + "tag": { + "description": "标签", + "type": "string" + } + } + }, + "request.CreateMyPlant": { + "type": "object", + "properties": { + "carePlans": { + "description": "养护计划", + "type": "array", + "items": { + "$ref": "#/definitions/request.CarePlan" + } + }, + "name": { + "description": "植物名称", + "type": "string" + }, + "ossIds": { + "description": "图片", + "type": "array", + "items": { + "type": "string" + } + }, + "placement": { + "description": "摆放位置", + "type": "string" + }, + "plantTime": { + "description": "种植时间", + "type": "string" + }, + "plantingMaterial": { + "description": "植料(即土的材质)", + "type": "string" + }, + "potMaterial": { + "description": "花盆材质", + "type": "string" + }, + "potSize": { + "description": "花盆大小 如直径 20cm × 高度 18cm", + "type": "string" + }, + "sunlight": { + "description": "光照条件如每日12小时", + "type": "string" + } + } + }, + "request.CreatePost": { + "type": "object", + "properties": { + "content": { + "description": "内容", + "type": "string" + }, + "location": { + "description": "位置", + "type": "string" + }, + "ossIds": { + "description": "图片id[]", + "type": "array", + "items": { + "type": "string" + } + }, + "title": { + "description": "标题 必须", + "type": "string" + } + } + }, + "request.CreateQrcode": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "ossId": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "request.DeleteClass": { + "type": "object", + "properties": { + "id": { + "description": "植物id", + "type": "string" + }, + "ids": { + "description": "要取消关联的分类ids", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "request.DeleteOss": { + "type": "object", + "required": [ + "id", + "ossIds" + ], + "properties": { + "id": { + "description": "数据主键", + "type": "string" + }, + "ossIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "request.DeleteRelatedLibrary": { + "type": "object", + "properties": { + "id": { + "description": "植物id", + "type": "string" + }, + "ids": { + "description": "要取消关联的植物ids", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "request.GetClientList": { + "type": "object", + "properties": { + "clientId": { + "type": "string" + }, + "current": { + "description": "页码", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "name": { + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, + "request.GetMenuTree": { + "type": "object", + "properties": { + "category": { + "type": "integer" + }, + "parentId": { + "type": "string" + } + } + }, + "request.GetOssFileList": { + "type": "object", + "properties": { + "current": { + "description": "页码", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "name": { + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, + "request.GetRoleList": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "current": { + "description": "页码", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "name": { + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, + "request.GetUserList": { + "type": "object", + "properties": { + "account": { + "type": "string" + }, + "current": { + "description": "页码", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + }, + "phone": { + "type": "string" + } + } + }, + "request.GrantMenu": { + "type": "object", + "properties": { + "menuIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "roleId": { + "type": "string" + } + } + }, + "request.GrantRole": { + "type": "object", + "properties": { + "roleIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "userId": { + "type": "string" + } + } + }, + "request.IdsReq": { + "type": "object", + "properties": { + "ids": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "request.LibraryList": { + "type": "object", + "properties": { + "isHot": { + "description": "是否热门 0否 1是", + "type": "integer" + }, + "name": { + "description": "植物名称", + "type": "string" + } + } + }, + "request.LibraryPage": { + "type": "object", + "properties": { + "classId": { + "description": "分类id", + "type": "string" + }, + "current": { + "description": "页码", + "type": "integer" + }, + "isHot": { + "description": "是否热门 0否 1是", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "name": { + "description": "植物名称", + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, + "request.Login": { + "type": "object", + "properties": { + "account": { + "type": "string" + }, + "captcha": { + "type": "string" + }, + "captchaId": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "request.MakeCare": { + "type": "object", + "required": [ + "id", + "status" + ], + "properties": { + "id": { + "description": "今日养护id(任务id)", + "type": "string" + }, + "remark": { + "description": "备注", + "type": "string" + }, + "status": { + "description": "状态 1:未完成 2:完成 3:跳过 4:逾期", + "type": "integer" + } + } + }, + "request.OrderPage": { + "type": "object", + "properties": { + "current": { + "description": "页码", + "type": "integer" + }, + "isShipped": { + "description": "是否发货 0 1", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + }, + "status": { + "description": "支付状态 1.SUCCESS 2.REFUND 3.NOTPAY 4.CLOSED", + "type": "integer" + } + } + }, + "request.PageBadge": { + "type": "object", + "properties": { + "categoryId": { + "type": "string" + }, + "current": { + "description": "页码", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "name": { + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, + "request.PageCategory": { + "type": "object", + "properties": { + "current": { + "description": "页码", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "name": { + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, + "request.PageClaimPlant": { + "type": "object", + "properties": { + "current": { + "description": "页码", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "name": { + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, + "request.PageGrowRecord": { + "type": "object", + "properties": { + "current": { + "description": "页码", + "type": "integer" + }, + "id": { + "description": "植物id", + "type": "string" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, + "request.PageInfo": { + "type": "object", + "properties": { + "current": { + "description": "页码", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, + "request.PagePlant": { + "type": "object", + "properties": { + "current": { + "description": "页码", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "name": { + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, + "request.PostPage": { + "type": "object", + "properties": { + "current": { + "description": "页码", + "type": "integer" + }, + "hasReviewed": { + "description": "是否审核通过", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + }, + "title": { + "description": "标题", + "type": "string" + } + } + }, + "request.UnionLibrary": { + "type": "object", + "properties": { + "id": { + "description": "植物id", + "type": "string" + }, + "libraryId": { + "description": "百科植物id", + "type": "string" + } + } + }, + "request.UpdateAddress": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "detail": { + "type": "string" + }, + "id": { + "type": "string" + }, + "isDefault": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "phone": { + "type": "string" + } + } + }, + "request.UpdateBadge": { + "type": "object", + "required": [ + "countLimit", + "id" + ], + "properties": { + "categoryId": { + "type": "string" + }, + "countLimit": { + "description": "获取徽章需要完成的操作次数", + "type": "integer" + }, + "desc": { + "type": "string" + }, + "id": { + "type": "string" + }, + "keyword": { + "type": "string" + }, + "name": { + "type": "string" + }, + "ossId": { + "type": "string" + } + } + }, + "request.UpdateBadgeCategory": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "desc": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "request.UpdateCarePlan": { + "type": "object", + "required": [ + "id", + "period" + ], + "properties": { + "id": { + "description": "养护计划id", + "type": "string" + }, + "period": { + "description": "周期", + "type": "integer" + } + } + }, + "request.UpdateClaimPlant": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "content": { + "type": "string" + }, + "id": { + "type": "string" + }, + "libraryId": { + "description": "百科id", + "type": "string" + }, + "name": { + "type": "string" + }, + "points": { + "type": "integer" + }, + "postage": { + "description": "邮费 单位(分)", + "type": "integer" + }, + "stock": { + "description": "库存", + "type": "integer" + }, + "tag": { + "type": "string" + }, + "videoUrl": { + "type": "string" + } + } + }, + "request.UpdateClassification": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "name": { + "description": "分类名称", + "type": "string" + }, + "ossId": { + "description": "图片id", + "type": "string" + }, + "tag": { + "description": "标签", + "type": "string" + } + } + }, + "request.UpdateInvitationConfig": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "codeExpireDays": { + "description": "邀请码有效期(天)", + "type": "integer" + }, + "codeLength": { + "description": "邀请码长度", + "type": "integer" + }, + "enableAutoAward": { + "description": "是否自动发放积分", + "type": "integer" + }, + "id": { + "type": "string" + }, + "isActive": { + "description": "是否启用", + "type": "integer" + }, + "maxInvitesPerDay": { + "description": "每日最大邀请奖励次数", + "type": "integer" + }, + "maxPointsPerUser": { + "description": "单用户最大邀请奖励积分", + "type": "integer" + }, + "name": { + "description": "配置名称", + "type": "string" + }, + "pointsPerInvite": { + "description": "每成功邀请奖励积分", + "type": "integer" + } + } + }, + "request.UpdateLibrary": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "aliases": { + "description": "别名", + "type": "string" + }, + "difficulty": { + "description": "种植难度 1-5级", + "type": "integer" + }, + "distributionArea": { + "description": "分布区域", + "type": "string" + }, + "flowerDiameter": { + "description": "花直径(cm)", + "type": "integer" + }, + "floweringColor": { + "description": "花色", + "type": "string" + }, + "floweringPeriod": { + "description": "开花特征", + "type": "string" + }, + "floweringShape": { + "description": "花形", + "type": "string" + }, + "foliageColor": { + "description": "叶色", + "type": "string" + }, + "foliageShape": { + "description": "叶形", + "type": "string" + }, + "foliageType": { + "description": "植物特征", + "type": "string" + }, + "fruit": { + "description": "果", + "type": "string" + }, + "genus": { + "description": "属", + "type": "string" + }, + "growthHabit": { + "description": "生长习性", + "type": "string" + }, + "height": { + "description": "高度(cm)", + "type": "integer" + }, + "id": { + "type": "string" + }, + "isHot": { + "description": "是否热门", + "type": "integer" + }, + "latinName": { + "description": "拉丁名", + "type": "string" + }, + "lifeCycle": { + "description": "生命周期", + "type": "string" + }, + "lightIntensity": { + "description": "光照强度", + "type": "string" + }, + "lightType": { + "description": "光照类型(直射,散射等)", + "type": "string" + }, + "name": { + "description": "名称", + "type": "string" + }, + "optimalTempPeriod": { + "description": "温度", + "type": "string" + }, + "ossId": { + "description": "ossId", + "type": "string" + }, + "pestsDiseases": { + "description": "常见病虫害", + "type": "string" + }, + "stem": { + "description": "茎", + "type": "string" + }, + "tag": { + "description": "标签", + "type": "string" + } + } + }, + "request.UpdateMyPlant": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "name": { + "description": "植物名称", + "type": "string" + }, + "placement": { + "description": "摆放位置", + "type": "string" + }, + "plantingMaterial": { + "description": "植料(即土的材质)", + "type": "string" + }, + "potMaterial": { + "description": "花盆材质", + "type": "string" + }, + "potSize": { + "description": "花盆大小 如直径 20cm × 高度 18cm", + "type": "string" + }, + "sunlight": { + "description": "光照条件如每日12小时", + "type": "string" + } + } + }, + "request.UpdatePost": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "content": { + "description": "内容", + "type": "string" + }, + "id": { + "type": "string" + }, + "title": { + "description": "标题", + "type": "string" + } + } + }, + "request.UpdateQrcode": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "request.UploadFile": { + "type": "object", + "required": [ + "id", + "ossIds" + ], + "properties": { + "id": { + "description": "数据主键", + "type": "string" + }, + "ossIds": { + "description": "ossId", + "type": "string" + } + } + }, + "request.UploadOss": { + "type": "object", + "required": [ + "id", + "ossIds" + ], + "properties": { + "id": { + "description": "数据主键", + "type": "string" + }, + "ossIds": { + "description": "ossIds", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "response.CaptchaRes": { + "type": "object", + "properties": { + "captcha": { + "type": "string" + }, + "captchaId": { + "type": "string" + } + } + }, + "response.LoginResponse": { + "type": "object", + "properties": { + "expiresAt": { + "type": "integer" + }, + "token": { + "type": "string" + }, + "user": { + "$ref": "#/definitions/system.User" + } + } + }, + "response.PageResult": { + "type": "object", + "properties": { + "list": {}, + "page": { + "type": "integer" + }, + "pageSize": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + }, + "response.Response": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "data": {}, + "msg": { + "type": "string" + } + } + }, + "system.Client": { + "type": "object", + "properties": { + "activeTimeout": { + "type": "integer" + }, + "additionalInfo": { + "type": "string" + }, + "clientId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "createdAtStr": { + "type": "string" + }, + "grantType": { + "type": "string" + }, + "id": { + "description": "主键ID", + "type": "string" + }, + "name": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "system.Menu": { + "type": "object", + "properties": { + "category": { + "type": "integer" + }, + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/system.Menu" + } + }, + "code": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "createdAtStr": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "id": { + "description": "主键ID", + "type": "string" + }, + "locale": { + "type": "string" + }, + "name": { + "type": "string" + }, + "parentId": { + "type": "string" + }, + "permission": { + "type": "string" + }, + "sort": { + "type": "integer" + }, + "title": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "system.Oss": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "createdAtStr": { + "type": "string" + }, + "height": { + "type": "integer" + }, + "id": { + "description": "主键ID", + "type": "string" + }, + "key": { + "type": "string" + }, + "md5": { + "type": "string" + }, + "name": { + "type": "string" + }, + "suffix": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "url": { + "type": "string" + }, + "width": { + "type": "integer" + } + } + }, + "system.Role": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "createdAtStr": { + "type": "string" + }, + "id": { + "description": "主键ID", + "type": "string" + }, + "menus": { + "type": "array", + "items": { + "$ref": "#/definitions/system.Menu" + } + }, + "name": { + "type": "string" + }, + "sort": { + "type": "integer" + }, + "updatedAt": { + "type": "string" + } + } + }, + "system.User": { + "type": "object", + "properties": { + "account": { + "type": "string" + }, + "avatar": { + "$ref": "#/definitions/system.Oss" + }, + "avatarId": { + "type": "string" + }, + "clientId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "createdAtStr": { + "type": "string" + }, + "id": { + "description": "主键ID", + "type": "string" + }, + "miniOpenId": { + "description": "植趣小程序openid", + "type": "string" + }, + "name": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "saOpenId": { + "description": "植趣服务号openid", + "type": "string" + }, + "sessionKey": { + "type": "string" + }, + "tenantId": { + "type": "string" + }, + "unionId": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + } + }, + "securityDefinitions": { + "ApiKeyAuth": { + "type": "apiKey", + "name": "Authorization", + "in": "header" + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "v1.0.0", + Host: "", + BasePath: "", + Schemes: []string{}, + Title: "Swagger API接口文档", + Description: "使用gin + gorm进行极速开发的全栈开发基础平台", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/docs/swagger.json b/docs/swagger.json new file mode 100644 index 0000000..f412c2f --- /dev/null +++ b/docs/swagger.json @@ -0,0 +1,7058 @@ +{ + "swagger": "2.0", + "info": { + "description": "使用gin + gorm进行极速开发的全栈开发基础平台", + "title": "Swagger API接口文档", + "contact": {}, + "version": "v1.0.0" + }, + "paths": { + "/auth/captcha": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "登录相关" + ], + "summary": "获取验证码", + "responses": { + "200": { + "description": "获取验证码", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.CaptchaRes" + } + } + } + ] + } + } + } + } + }, + "/auth/getLocation": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "登录相关" + ], + "summary": "获取位置信息", + "parameters": [ + { + "type": "string", + "description": "longitude", + "name": "longitude", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "latitude", + "name": "latitude", + "in": "query", + "required": true + } + ], + "responses": {} + } + }, + "/auth/getPhone": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "登录相关" + ], + "summary": "获取手机号", + "parameters": [ + { + "type": "string", + "description": "code", + "name": "code", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "openId", + "name": "openId", + "in": "query", + "required": true + } + ], + "responses": {} + } + }, + "/auth/getWeather": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "登录相关" + ], + "summary": "获取天气信息", + "parameters": [ + { + "type": "string", + "description": "adcode", + "name": "adcode", + "in": "query", + "required": true + } + ], + "responses": {} + } + }, + "/auth/login": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "登录相关" + ], + "summary": "pc登录", + "parameters": [ + { + "description": "用户名, 密码, 验证码,验证码id", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.Login" + } + } + ], + "responses": { + "200": { + "description": "登录成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/auth/logout": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "登录相关" + ], + "summary": "pc登出", + "responses": { + "200": { + "description": "登出成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/auth/miniLogin": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "登录相关" + ], + "summary": "小程序登录", + "parameters": [ + { + "type": "string", + "description": "code", + "name": "code", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "小程序登录", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.LoginResponse" + } + } + } + ] + } + } + } + } + }, + "/badge/add": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "添加", + "parameters": [ + { + "description": "添加", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateBadge" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"添加成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/all": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "获取所有无分页", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/class/add": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "添加分类", + "parameters": [ + { + "description": "添加分类", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateBadgeCategory" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"添加成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/class/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "删除分类", + "parameters": [ + { + "description": "删除分类", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/class/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "分类详情", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/class/list": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "分类列表", + "parameters": [ + { + "description": "分类列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageCategory" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/class/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "修改分类", + "parameters": [ + { + "description": "修改分类", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateBadgeCategory" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"修改成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "删除ByIds", + "parameters": [ + { + "description": "删除ByIds", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "详情", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/list": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "获取列表有分页", + "parameters": [ + { + "description": "获取列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageBadge" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/listByKeyword": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "获取列表byKeyword无分页", + "parameters": [ + { + "type": "string", + "description": "keyword", + "name": "keyword", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/my/all": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "我的所有徽章", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/my/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "我的徽章详情", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/my/list": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "我的徽章", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "修改", + "parameters": [ + { + "description": "修改", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateBadge" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"修改成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/badge/uploadImg": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "徽章" + ], + "summary": "上传图片", + "parameters": [ + { + "description": "上传图片", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UploadFile" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"上传成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/claim/add": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "兑换植物" + ], + "summary": "配置植物", + "parameters": [ + { + "description": "添加", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateClaimPlant" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"添加成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/claim/cancelUnion": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "兑换植物" + ], + "summary": "取消关联百科植物", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"取消关联成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/claim/claim": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "兑换植物" + ], + "summary": "认养", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "addressId", + "name": "addressId", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"认养成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/claim/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "兑换植物" + ], + "summary": "删除ByIds", + "parameters": [ + { + "description": "删除", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/claim/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "兑换植物" + ], + "summary": "详情", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/claim/list": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "兑换植物" + ], + "summary": "获取列表", + "parameters": [ + { + "description": "获取列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageClaimPlant" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/claim/myClaims": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "兑换植物" + ], + "summary": "我的认养", + "parameters": [ + { + "description": "获取列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageClaimPlant" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/claim/unionLibrary": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "兑换植物" + ], + "summary": "关联百科植物", + "parameters": [ + { + "description": "关联", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UnionLibrary" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"关联成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/claim/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "兑换植物" + ], + "summary": "修改植物", + "parameters": [ + { + "description": "修改", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateClaimPlant" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"修改成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/class/add": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库分类" + ], + "summary": "新增分类", + "parameters": [ + { + "description": "新增分类", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateClassification" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"新增成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/class/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库分类" + ], + "summary": "删除分类", + "parameters": [ + { + "description": "删除分类", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/class/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库分类" + ], + "summary": "分类详情", + "parameters": [ + { + "type": "string", + "description": "分类id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/class/list": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库分类" + ], + "summary": "分类列表", + "parameters": [ + { + "description": "分类列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageInfo" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/class/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库分类" + ], + "summary": "修改分类", + "parameters": [ + { + "description": "修改分类", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateClassification" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"修改成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/client/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "客户端管理" + ], + "summary": "删除client", + "parameters": [ + { + "description": "ids", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "删除client", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/client/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "id获取详情", + "produces": [ + "application/json" + ], + "tags": [ + "客户端管理" + ], + "summary": "获取client详情", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "获取client详情", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/system.Client" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/client/getClientList": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "客户端管理" + ], + "summary": "获取client列表", + "parameters": [ + { + "description": "client", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetClientList" + } + } + ], + "responses": { + "200": { + "description": "获取client列表", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/client/save": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "客户端管理" + ], + "summary": "创建client", + "parameters": [ + { + "description": "client", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.Client" + } + } + ], + "responses": { + "200": { + "description": "创建client", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/client/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "客户端管理" + ], + "summary": "更新client", + "parameters": [ + { + "description": "client", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.Client" + } + } + ], + "responses": { + "200": { + "description": "更新client", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/comment/add": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子评论" + ], + "summary": "发表评论", + "parameters": [ + { + "description": "添加评论", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateComment" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"添加成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/comment/delete": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子评论" + ], + "summary": "删除评论", + "parameters": [ + { + "description": "删除评论", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/invitation/add": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "配置中心" + ], + "summary": "配置邀请码", + "parameters": [ + { + "description": "配置邀请码", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateInvitationConfig" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"配置成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/invitation/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "配置中心" + ], + "summary": "删除邀请码配置", + "parameters": [ + { + "description": "删除邀请码", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/invitation/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "配置中心" + ], + "summary": "获取邀请码配置", + "parameters": [ + { + "type": "string", + "description": "邀请码id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/invitation/list": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "配置中心" + ], + "summary": "获取邀请码列表", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/invitation/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "配置中心" + ], + "summary": "更新邀请码配置", + "parameters": [ + { + "description": "更新邀请码", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateInvitationConfig" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"更新成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/qrcode/add": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "配置中心" + ], + "summary": "配置二维码", + "parameters": [ + { + "description": "配置二维码", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateQrcode" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"配置成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/qrcode/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "配置中心" + ], + "summary": "删除二维码", + "parameters": [ + { + "description": "二维码id", + "name": "ids", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/qrcode/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "配置中心" + ], + "summary": "获取二维码详情", + "parameters": [ + { + "type": "string", + "description": "二维码id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/qrcode/list": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "tags": [ + "配置中心" + ], + "summary": "获取二维码", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/qrcode/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "配置中心" + ], + "summary": "更新二维码", + "parameters": [ + { + "description": "更新二维码", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateQrcode" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"更新成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/config/qrcode/uploadImg": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "配置中心" + ], + "summary": "上传二维码图片", + "parameters": [ + { + "description": "上传二维码图片", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UploadFile" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"上传成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/library/add": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "新增植物库", + "parameters": [ + { + "description": "新增植物库", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateLibrary" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/all": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "获取所有植物库列表", + "parameters": [ + { + "description": "获取所有植物库列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.LibraryList" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/alterClass": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "修改分类", + "parameters": [ + { + "description": "修改分类", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.AlterClass" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "删除植物库", + "parameters": [ + { + "description": "删除植物库", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/deleteClass": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "取消关联分类", + "parameters": [ + { + "description": "取消关联分类", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.DeleteClass" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/deleteImg": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "上传图片", + "parameters": [ + { + "description": "上传图片", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.DeleteOss" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/deleteRelateLibrary": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "删除关联植物", + "parameters": [ + { + "description": "删除关联", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.DeleteRelatedLibrary" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "获取植物库详情", + "parameters": [ + { + "type": "string", + "description": "获取植物库详情", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/hot": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "设置或取消热门", + "parameters": [ + { + "description": "设置热门", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/page": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "获取植物库列表", + "parameters": [ + { + "description": "获取植物库列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.LibraryPage" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/reateLibrary": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "添加关联植物", + "parameters": [ + { + "description": "添加关联", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.AlterRelatedLibrary" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "修改植物库", + "parameters": [ + { + "description": "修改植物库", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateLibrary" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/library/uploadImg": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "植物库" + ], + "summary": "上传图片", + "parameters": [ + { + "description": "上传图片", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UploadOss" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/delete": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "删除menu", + "produces": [ + "application/json" + ], + "tags": [ + "菜单管理" + ], + "summary": "删除menu", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "详情", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "id获取详情", + "produces": [ + "application/json" + ], + "tags": [ + "菜单管理" + ], + "summary": "获取menu详情", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "详情", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/system.Menu" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/getAllMenuTree": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "菜单管理" + ], + "summary": "获取所有菜单树", + "parameters": [ + { + "description": "菜单信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetMenuTree" + } + } + ], + "responses": { + "200": { + "description": "获取所有菜单树", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/system.Menu" + } + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/getUserMenuTree": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "菜单管理" + ], + "summary": "用户菜单数据", + "responses": { + "200": { + "description": "用户菜单数据", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/system.Menu" + } + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/route": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "菜单管理" + ], + "summary": "用户路由", + "responses": { + "200": { + "description": "用户route", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/system.Menu" + } + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/save": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "菜单管理" + ], + "summary": "新增菜单", + "parameters": [ + { + "description": "menu", + "name": "data", + "in": "body", + "schema": { + "$ref": "#/definitions/system.Menu" + } + } + ], + "responses": { + "200": { + "description": "新建菜单/按钮", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/menu/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "菜单管理" + ], + "summary": "更新菜单", + "parameters": [ + { + "description": "menu", + "name": "data", + "in": "body", + "schema": { + "$ref": "#/definitions/system.Menu" + } + } + ], + "responses": { + "200": { + "description": "更新菜单", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/ocr/base64": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "识别相关" + ], + "summary": "base64植物识别", + "parameters": [ + { + "type": "file", + "description": "植物识别", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "文件OCR", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/ocr/url": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "识别相关" + ], + "summary": "url植物识别", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "文件OCR", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/order/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "订单" + ], + "summary": "删除订单", + "parameters": [ + { + "description": "删除订单", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/order/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "订单" + ], + "summary": "订单详情", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/order/export": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "订单" + ], + "summary": "导出订单", + "parameters": [ + { + "type": "integer", + "description": "页码", + "name": "current", + "in": "query" + }, + { + "type": "integer", + "description": "是否发货 0 1", + "name": "isShipped", + "in": "query" + }, + { + "type": "string", + "description": "关键字", + "name": "keyword", + "in": "query" + }, + { + "type": "integer", + "description": "每页大小", + "name": "pageSize", + "in": "query" + }, + { + "type": "integer", + "description": "支付状态 1.SUCCESS 2.REFUND 3.NOTPAY 4.CLOSED", + "name": "status", + "in": "query" + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/order/page": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "订单" + ], + "summary": "订单列表", + "parameters": [ + { + "description": "获取列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.OrderPage" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/order/ship": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "订单" + ], + "summary": "发货", + "parameters": [ + { + "description": "发货", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/oss/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "文件相关" + ], + "summary": "删除文件", + "parameters": [ + { + "description": "批量删除文件", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "删除文件", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/oss/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "文件相关" + ], + "summary": "文件详情", + "parameters": [ + { + "type": "string", + "description": "文件id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "文件详情", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/oss/getFileList": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "文件相关" + ], + "summary": "文件列表", + "parameters": [ + { + "description": "文件列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetOssFileList" + } + } + ], + "responses": { + "200": { + "description": "文件列表", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/oss/upload": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "文件相关" + ], + "summary": "文件上传", + "parameters": [ + { + "type": "file", + "description": "上传文件", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "上传文件", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/pay/prePay": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "微信支付" + ], + "summary": "支付", + "parameters": [ + { + "type": "string", + "description": "支付", + "name": "orderId", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"支付成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/personal/address/add": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "个人中心" + ], + "summary": "添加地址", + "parameters": [ + { + "description": "添加地址", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateAddress" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"添加成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/personal/address/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "个人中心" + ], + "summary": "删除地址", + "parameters": [ + { + "description": "删除地址", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/personal/address/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "个人中心" + ], + "summary": "地址详情", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/personal/address/list": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "个人中心" + ], + "summary": "地址列表", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/personal/address/setDefault": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "个人中心" + ], + "summary": "设置默认地址", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"设置成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/personal/address/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "个人中心" + ], + "summary": "修改地址", + "parameters": [ + { + "description": "修改地址", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateAddress" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"修改成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/personal/centerCount": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "个人中心" + ], + "summary": "个人中心统计", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/personal/inviteCode/accept": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "个人中心" + ], + "summary": "接受邀请", + "parameters": [ + { + "type": "string", + "description": "inviteCode", + "name": "inviteCode", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"接受成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/personal/inviteCode/code": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "个人中心" + ], + "summary": "生成邀请码", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"生成成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/personal/inviteCode/records": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "个人中心" + ], + "summary": "邀请记录", + "parameters": [ + { + "description": "分页参数", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageInfo" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/add": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "添加植物", + "parameters": [ + { + "description": "创建植物", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateMyPlant" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"添加成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/carePlant": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "立即养护", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"操作成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/careRecords": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "养护记录", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query" + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"操作成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "删除ByIds", + "parameters": [ + { + "description": "删除ByIds", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/deleteImg": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "删除图片", + "parameters": [ + { + "description": "删除图片", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.DeleteOss" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "获取ById", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/grow/addRecord": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "添加成长记录", + "parameters": [ + { + "description": "添加成长记录", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreateGrowRecord" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"操作成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/grow/recordDetail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "成长记录详情", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"操作成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/grow/recordList": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "成长记录列表", + "parameters": [ + { + "description": "成长记录列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PageGrowRecord" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"操作成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/makeCare": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "根据任务完成养护", + "parameters": [ + { + "description": "养护操作", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.MakeCare" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"操作成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/myPlants": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "植物列表不分页", + "parameters": [ + { + "type": "string", + "description": "name", + "name": "name", + "in": "query" + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/needCare": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "待养护", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"操作成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/page": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "植物列表", + "parameters": [ + { + "description": "获取植物列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PagePlant" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/taskProgress": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "任务进度", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"操作成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/todayCare": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "tags": [ + "我的植物" + ], + "summary": "今日养护", + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"操作成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "修改植物", + "parameters": [ + { + "description": "修改植物", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateMyPlant" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"修改成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/updateCarePlan": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "修改养护事项周期", + "parameters": [ + { + "description": "修改养护周期", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdateCarePlan" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"修改成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/plant/uploadImg": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "我的植物" + ], + "summary": "上传图片", + "parameters": [ + { + "description": "上传图片", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UploadOss" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"上传成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/post/add": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子" + ], + "summary": "发布", + "parameters": [ + { + "description": "创建帖子", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.CreatePost" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"添加成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/post/cancelLike": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子" + ], + "summary": "取消点赞", + "parameters": [ + { + "type": "string", + "description": "帖子id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"取消点赞成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/post/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子" + ], + "summary": "删除帖子", + "parameters": [ + { + "description": "删除帖子", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/post/deleteImg": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子" + ], + "summary": "删除帖子图片", + "parameters": [ + { + "description": "删除帖子图片", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.DeleteOss" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"删除成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/post/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子" + ], + "summary": "帖子详情", + "parameters": [ + { + "type": "string", + "description": "帖子id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/post/like": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子" + ], + "summary": "点赞帖子", + "parameters": [ + { + "type": "string", + "description": "帖子id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"点赞成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/post/myPosts": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子" + ], + "summary": "我的帖子", + "parameters": [ + { + "type": "integer", + "description": "页码", + "name": "current", + "in": "query" + }, + { + "type": "string", + "description": "关键字", + "name": "keyword", + "in": "query" + }, + { + "type": "integer", + "description": "每页大小", + "name": "pageSize", + "in": "query" + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/post/page": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子" + ], + "summary": "帖子列表", + "parameters": [ + { + "description": "帖子列表", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.PostPage" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/post/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子" + ], + "summary": "修改帖子", + "parameters": [ + { + "description": "修改帖子", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UpdatePost" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"修改成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/post/uploadImg": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "帖子" + ], + "summary": "上传帖子图片", + "parameters": [ + { + "description": "上传帖子图片", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UploadOss" + } + } + ], + "responses": { + "200": { + "description": "{\"success\":true,\"data\":{},\"msg\":\"上传成功\"}", + "schema": { + "type": "string" + } + } + } + } + }, + "/role/delete": { + "post": { + "description": "删除角色", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "角色管理" + ], + "summary": "删除角色", + "parameters": [ + { + "description": "批量删除角色", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "删除角色", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/role/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "角色管理" + ], + "summary": "角色详情", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "角色详情", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/system.Role" + } + } + } + ] + } + } + } + } + }, + "/role/getRoleList": { + "post": { + "description": "获取角色列表", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "角色管理" + ], + "summary": "获取角色列表", + "parameters": [ + { + "description": "页码, 每页大小, 搜索条件", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetRoleList" + } + } + ], + "responses": { + "200": { + "description": "获取角色列表,返回包括列表,总数,页码,每页大小", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/role/grantMenu": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "角色管理" + ], + "summary": "授权菜单给角色", + "parameters": [ + { + "description": "授权菜单给角色", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GrantMenu" + } + } + ], + "responses": { + "200": { + "description": "授权菜单给角色", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/role/save": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "角色管理" + ], + "summary": "创建角色", + "parameters": [ + { + "description": "角色信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.Role" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/role/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "角色管理" + ], + "summary": "修改角色", + "parameters": [ + { + "description": "角色ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.Role" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/user/changePassword": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "修改密码", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "用户管理" + ], + "summary": "修改密码", + "parameters": [ + { + "description": "用户id", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.ChangePwd" + } + } + ], + "responses": { + "200": { + "description": "修改密码成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/system.User" + } + } + } + ] + } + } + } + } + }, + "/user/delete": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "用户管理" + ], + "summary": "删除用户", + "parameters": [ + { + "description": "批量删除用户", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.IdsReq" + } + } + ], + "responses": { + "200": { + "description": "删除用户", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/detail": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "用户管理" + ], + "summary": "获取用户详情", + "parameters": [ + { + "type": "string", + "description": "id", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "获取用户详情成功", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/system.User" + } + } + } + ] + } + } + } + } + }, + "/user/getUserList": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "用户管理" + ], + "summary": "获取用户列表", + "parameters": [ + { + "description": "页码, 每页大小, 搜索条件", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetUserList" + } + } + ], + "responses": { + "200": { + "description": "获取用户列表,返回包括列表,总数,页码,每页大小", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/response.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/response.PageResult" + }, + "msg": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "/user/grantRole": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "用户管理" + ], + "summary": "给用户分配角色", + "parameters": [ + { + "description": "用户ID, 角色ID", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GrantRole" + } + } + ], + "responses": { + "200": { + "description": "{\"code\": 200, \"data\": [...]}", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/user/save": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "用户管理" + ], + "summary": "新增用户", + "parameters": [ + { + "description": "用户信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.User" + } + } + ], + "responses": { + "200": { + "description": "{\"code\": 200, \"data\": {}, \"msg\": \"添加成功\"}", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/user/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "用户管理" + ], + "summary": "更新用户", + "parameters": [ + { + "description": "用户ID,用户信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/system.User" + } + } + ], + "responses": { + "200": { + "description": "{\"code\": 200, \"data\": [...]}", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + } + }, + "definitions": { + "request.AlterClass": { + "type": "object", + "properties": { + "id": { + "description": "植物id", + "type": "string" + }, + "ids": { + "description": "要关联的分类ids", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "request.AlterRelatedLibrary": { + "type": "object", + "properties": { + "id": { + "description": "植物id", + "type": "string" + }, + "ids": { + "description": "要关联的植物ids", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "request.CareItem": { + "type": "object", + "properties": { + "desc": { + "type": "string" + }, + "name": { + "type": "string" + }, + "period": { + "type": "integer" + } + } + }, + "request.CarePlan": { + "type": "object", + "properties": { + "desc": { + "type": "string" + }, + "name": { + "description": "农事名称", + "type": "string" + }, + "period": { + "description": "周期", + "type": "integer" + } + } + }, + "request.CareSchedule": { + "type": "object", + "properties": { + "careItems": { + "description": "养护项目", + "type": "array", + "items": { + "$ref": "#/definitions/request.CareItem" + } + }, + "desc": { + "description": "描述", + "type": "string" + }, + "season": { + "description": "季节", + "type": "string" + }, + "sort": { + "description": "排序", + "type": "integer" + } + } + }, + "request.ChangePwd": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "newPwd": { + "type": "string" + } + } + }, + "request.CreateAddress": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "isDefault": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "phone": { + "type": "string" + } + } + }, + "request.CreateBadge": { + "type": "object", + "required": [ + "countLimit" + ], + "properties": { + "categoryId": { + "type": "string" + }, + "countLimit": { + "description": "获取徽章需要完成的操作次数", + "type": "integer" + }, + "desc": { + "type": "string" + }, + "keyword": { + "type": "string" + }, + "name": { + "type": "string" + }, + "ossId": { + "type": "string" + } + } + }, + "request.CreateBadgeCategory": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "desc": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "request.CreateClaimPlant": { + "type": "object", + "properties": { + "content": { + "type": "string" + }, + "libraryId": { + "description": "百科id", + "type": "string" + }, + "name": { + "type": "string" + }, + "ossIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "points": { + "type": "integer" + }, + "postage": { + "description": "邮费 单位(分)", + "type": "integer" + }, + "stock": { + "description": "库存", + "type": "integer" + }, + "tag": { + "type": "string" + }, + "videoUrl": { + "type": "string" + } + } + }, + "request.CreateClassification": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "description": "分类名称", + "type": "string" + }, + "ossId": { + "description": "图片id", + "type": "string" + }, + "tag": { + "description": "标签", + "type": "string" + } + } + }, + "request.CreateComment": { + "type": "object", + "required": [ + "content", + "parentId", + "postId", + "rootId" + ], + "properties": { + "content": { + "description": "评论内容", + "type": "string" + }, + "parentId": { + "description": "父级评论id\" 默认为0", + "type": "string" + }, + "postId": { + "description": "帖子id", + "type": "string" + }, + "rootId": { + "description": "根评论id 默认为0 如评论1 评论2 评论3 1是根评论 2是1的子评论 3是2的子评论 那么2和3的rootId都是1的主键id", + "type": "string" + } + } + }, + "request.CreateGrowRecord": { + "type": "object", + "required": [ + "plantId" + ], + "properties": { + "content": { + "type": "string" + }, + "desc": { + "type": "string" + }, + "name": { + "type": "string" + }, + "ossIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "plantId": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "request.CreateInvitationConfig": { + "type": "object", + "properties": { + "codeExpireDays": { + "description": "邀请码有效期(天)", + "type": "integer" + }, + "codeLength": { + "description": "邀请码长度", + "type": "integer" + }, + "enableAutoAward": { + "description": "是否自动发放积分", + "type": "integer" + }, + "isActive": { + "description": "是否启用", + "type": "integer" + }, + "maxInvitesPerDay": { + "description": "每日最大邀请奖励次数", + "type": "integer" + }, + "maxPointsPerUser": { + "description": "单用户最大邀请奖励积分", + "type": "integer" + }, + "name": { + "description": "配置名称", + "type": "string" + }, + "pointsPerInvite": { + "description": "每成功邀请奖励积分", + "type": "integer" + } + } + }, + "request.CreateLibrary": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "aliases": { + "description": "别名", + "type": "string" + }, + "careSchedule": { + "description": "养护计划", + "type": "array", + "items": { + "$ref": "#/definitions/request.CareSchedule" + } + }, + "classIds": { + "description": "分类id", + "type": "array", + "items": { + "type": "string" + } + }, + "difficulty": { + "description": "种植难度 1-5级", + "type": "integer" + }, + "distributionArea": { + "description": "分布区域", + "type": "string" + }, + "flowerDiameter": { + "description": "花直径(cm)", + "type": "integer" + }, + "floweringColor": { + "description": "花色", + "type": "string" + }, + "floweringPeriod": { + "description": "开花特征", + "type": "string" + }, + "floweringShape": { + "description": "花形", + "type": "string" + }, + "foliageColor": { + "description": "叶色", + "type": "string" + }, + "foliageShape": { + "description": "叶形", + "type": "string" + }, + "foliageType": { + "description": "植物特征", + "type": "string" + }, + "fruit": { + "description": "果", + "type": "string" + }, + "genus": { + "description": "属", + "type": "string" + }, + "growthHabit": { + "description": "生长习性", + "type": "string" + }, + "height": { + "description": "高度(cm)", + "type": "integer" + }, + "isHot": { + "description": "是否热门", + "type": "integer" + }, + "latinName": { + "description": "拉丁名", + "type": "string" + }, + "lifeCycle": { + "description": "生命周期", + "type": "string" + }, + "lightIntensity": { + "description": "光照强度", + "type": "string" + }, + "lightType": { + "description": "光照类型(直射,散射等)", + "type": "string" + }, + "name": { + "description": "基本信息", + "type": "string" + }, + "optimalTempPeriod": { + "description": "最佳温度区间", + "type": "string" + }, + "ossIds": { + "description": "图片", + "type": "array", + "items": { + "type": "string" + } + }, + "pestsDiseases": { + "description": "常见病虫害", + "type": "string" + }, + "relatedLibraryIds": { + "description": "相关推荐 library", + "type": "array", + "items": { + "type": "string" + } + }, + "reproductionMethod": { + "description": "繁殖方法", + "type": "string" + }, + "stem": { + "description": "茎", + "type": "string" + }, + "tag": { + "description": "标签", + "type": "string" + } + } + }, + "request.CreateMyPlant": { + "type": "object", + "properties": { + "carePlans": { + "description": "养护计划", + "type": "array", + "items": { + "$ref": "#/definitions/request.CarePlan" + } + }, + "name": { + "description": "植物名称", + "type": "string" + }, + "ossIds": { + "description": "图片", + "type": "array", + "items": { + "type": "string" + } + }, + "placement": { + "description": "摆放位置", + "type": "string" + }, + "plantTime": { + "description": "种植时间", + "type": "string" + }, + "plantingMaterial": { + "description": "植料(即土的材质)", + "type": "string" + }, + "potMaterial": { + "description": "花盆材质", + "type": "string" + }, + "potSize": { + "description": "花盆大小 如直径 20cm × 高度 18cm", + "type": "string" + }, + "sunlight": { + "description": "光照条件如每日12小时", + "type": "string" + } + } + }, + "request.CreatePost": { + "type": "object", + "properties": { + "content": { + "description": "内容", + "type": "string" + }, + "location": { + "description": "位置", + "type": "string" + }, + "ossIds": { + "description": "图片id[]", + "type": "array", + "items": { + "type": "string" + } + }, + "title": { + "description": "标题 必须", + "type": "string" + } + } + }, + "request.CreateQrcode": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "ossId": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "request.DeleteClass": { + "type": "object", + "properties": { + "id": { + "description": "植物id", + "type": "string" + }, + "ids": { + "description": "要取消关联的分类ids", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "request.DeleteOss": { + "type": "object", + "required": [ + "id", + "ossIds" + ], + "properties": { + "id": { + "description": "数据主键", + "type": "string" + }, + "ossIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "request.DeleteRelatedLibrary": { + "type": "object", + "properties": { + "id": { + "description": "植物id", + "type": "string" + }, + "ids": { + "description": "要取消关联的植物ids", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "request.GetClientList": { + "type": "object", + "properties": { + "clientId": { + "type": "string" + }, + "current": { + "description": "页码", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "name": { + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, + "request.GetMenuTree": { + "type": "object", + "properties": { + "category": { + "type": "integer" + }, + "parentId": { + "type": "string" + } + } + }, + "request.GetOssFileList": { + "type": "object", + "properties": { + "current": { + "description": "页码", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "name": { + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, + "request.GetRoleList": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "current": { + "description": "页码", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "name": { + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, + "request.GetUserList": { + "type": "object", + "properties": { + "account": { + "type": "string" + }, + "current": { + "description": "页码", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + }, + "phone": { + "type": "string" + } + } + }, + "request.GrantMenu": { + "type": "object", + "properties": { + "menuIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "roleId": { + "type": "string" + } + } + }, + "request.GrantRole": { + "type": "object", + "properties": { + "roleIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "userId": { + "type": "string" + } + } + }, + "request.IdsReq": { + "type": "object", + "properties": { + "ids": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "request.LibraryList": { + "type": "object", + "properties": { + "isHot": { + "description": "是否热门 0否 1是", + "type": "integer" + }, + "name": { + "description": "植物名称", + "type": "string" + } + } + }, + "request.LibraryPage": { + "type": "object", + "properties": { + "classId": { + "description": "分类id", + "type": "string" + }, + "current": { + "description": "页码", + "type": "integer" + }, + "isHot": { + "description": "是否热门 0否 1是", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "name": { + "description": "植物名称", + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, + "request.Login": { + "type": "object", + "properties": { + "account": { + "type": "string" + }, + "captcha": { + "type": "string" + }, + "captchaId": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "request.MakeCare": { + "type": "object", + "required": [ + "id", + "status" + ], + "properties": { + "id": { + "description": "今日养护id(任务id)", + "type": "string" + }, + "remark": { + "description": "备注", + "type": "string" + }, + "status": { + "description": "状态 1:未完成 2:完成 3:跳过 4:逾期", + "type": "integer" + } + } + }, + "request.OrderPage": { + "type": "object", + "properties": { + "current": { + "description": "页码", + "type": "integer" + }, + "isShipped": { + "description": "是否发货 0 1", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + }, + "status": { + "description": "支付状态 1.SUCCESS 2.REFUND 3.NOTPAY 4.CLOSED", + "type": "integer" + } + } + }, + "request.PageBadge": { + "type": "object", + "properties": { + "categoryId": { + "type": "string" + }, + "current": { + "description": "页码", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "name": { + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, + "request.PageCategory": { + "type": "object", + "properties": { + "current": { + "description": "页码", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "name": { + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, + "request.PageClaimPlant": { + "type": "object", + "properties": { + "current": { + "description": "页码", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "name": { + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, + "request.PageGrowRecord": { + "type": "object", + "properties": { + "current": { + "description": "页码", + "type": "integer" + }, + "id": { + "description": "植物id", + "type": "string" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, + "request.PageInfo": { + "type": "object", + "properties": { + "current": { + "description": "页码", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, + "request.PagePlant": { + "type": "object", + "properties": { + "current": { + "description": "页码", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "name": { + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, + "request.PostPage": { + "type": "object", + "properties": { + "current": { + "description": "页码", + "type": "integer" + }, + "hasReviewed": { + "description": "是否审核通过", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + }, + "title": { + "description": "标题", + "type": "string" + } + } + }, + "request.UnionLibrary": { + "type": "object", + "properties": { + "id": { + "description": "植物id", + "type": "string" + }, + "libraryId": { + "description": "百科植物id", + "type": "string" + } + } + }, + "request.UpdateAddress": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "detail": { + "type": "string" + }, + "id": { + "type": "string" + }, + "isDefault": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "phone": { + "type": "string" + } + } + }, + "request.UpdateBadge": { + "type": "object", + "required": [ + "countLimit", + "id" + ], + "properties": { + "categoryId": { + "type": "string" + }, + "countLimit": { + "description": "获取徽章需要完成的操作次数", + "type": "integer" + }, + "desc": { + "type": "string" + }, + "id": { + "type": "string" + }, + "keyword": { + "type": "string" + }, + "name": { + "type": "string" + }, + "ossId": { + "type": "string" + } + } + }, + "request.UpdateBadgeCategory": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "desc": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "request.UpdateCarePlan": { + "type": "object", + "required": [ + "id", + "period" + ], + "properties": { + "id": { + "description": "养护计划id", + "type": "string" + }, + "period": { + "description": "周期", + "type": "integer" + } + } + }, + "request.UpdateClaimPlant": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "content": { + "type": "string" + }, + "id": { + "type": "string" + }, + "libraryId": { + "description": "百科id", + "type": "string" + }, + "name": { + "type": "string" + }, + "points": { + "type": "integer" + }, + "postage": { + "description": "邮费 单位(分)", + "type": "integer" + }, + "stock": { + "description": "库存", + "type": "integer" + }, + "tag": { + "type": "string" + }, + "videoUrl": { + "type": "string" + } + } + }, + "request.UpdateClassification": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "name": { + "description": "分类名称", + "type": "string" + }, + "ossId": { + "description": "图片id", + "type": "string" + }, + "tag": { + "description": "标签", + "type": "string" + } + } + }, + "request.UpdateInvitationConfig": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "codeExpireDays": { + "description": "邀请码有效期(天)", + "type": "integer" + }, + "codeLength": { + "description": "邀请码长度", + "type": "integer" + }, + "enableAutoAward": { + "description": "是否自动发放积分", + "type": "integer" + }, + "id": { + "type": "string" + }, + "isActive": { + "description": "是否启用", + "type": "integer" + }, + "maxInvitesPerDay": { + "description": "每日最大邀请奖励次数", + "type": "integer" + }, + "maxPointsPerUser": { + "description": "单用户最大邀请奖励积分", + "type": "integer" + }, + "name": { + "description": "配置名称", + "type": "string" + }, + "pointsPerInvite": { + "description": "每成功邀请奖励积分", + "type": "integer" + } + } + }, + "request.UpdateLibrary": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "aliases": { + "description": "别名", + "type": "string" + }, + "difficulty": { + "description": "种植难度 1-5级", + "type": "integer" + }, + "distributionArea": { + "description": "分布区域", + "type": "string" + }, + "flowerDiameter": { + "description": "花直径(cm)", + "type": "integer" + }, + "floweringColor": { + "description": "花色", + "type": "string" + }, + "floweringPeriod": { + "description": "开花特征", + "type": "string" + }, + "floweringShape": { + "description": "花形", + "type": "string" + }, + "foliageColor": { + "description": "叶色", + "type": "string" + }, + "foliageShape": { + "description": "叶形", + "type": "string" + }, + "foliageType": { + "description": "植物特征", + "type": "string" + }, + "fruit": { + "description": "果", + "type": "string" + }, + "genus": { + "description": "属", + "type": "string" + }, + "growthHabit": { + "description": "生长习性", + "type": "string" + }, + "height": { + "description": "高度(cm)", + "type": "integer" + }, + "id": { + "type": "string" + }, + "isHot": { + "description": "是否热门", + "type": "integer" + }, + "latinName": { + "description": "拉丁名", + "type": "string" + }, + "lifeCycle": { + "description": "生命周期", + "type": "string" + }, + "lightIntensity": { + "description": "光照强度", + "type": "string" + }, + "lightType": { + "description": "光照类型(直射,散射等)", + "type": "string" + }, + "name": { + "description": "名称", + "type": "string" + }, + "optimalTempPeriod": { + "description": "温度", + "type": "string" + }, + "ossId": { + "description": "ossId", + "type": "string" + }, + "pestsDiseases": { + "description": "常见病虫害", + "type": "string" + }, + "stem": { + "description": "茎", + "type": "string" + }, + "tag": { + "description": "标签", + "type": "string" + } + } + }, + "request.UpdateMyPlant": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "name": { + "description": "植物名称", + "type": "string" + }, + "placement": { + "description": "摆放位置", + "type": "string" + }, + "plantingMaterial": { + "description": "植料(即土的材质)", + "type": "string" + }, + "potMaterial": { + "description": "花盆材质", + "type": "string" + }, + "potSize": { + "description": "花盆大小 如直径 20cm × 高度 18cm", + "type": "string" + }, + "sunlight": { + "description": "光照条件如每日12小时", + "type": "string" + } + } + }, + "request.UpdatePost": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "content": { + "description": "内容", + "type": "string" + }, + "id": { + "type": "string" + }, + "title": { + "description": "标题", + "type": "string" + } + } + }, + "request.UpdateQrcode": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "request.UploadFile": { + "type": "object", + "required": [ + "id", + "ossIds" + ], + "properties": { + "id": { + "description": "数据主键", + "type": "string" + }, + "ossIds": { + "description": "ossId", + "type": "string" + } + } + }, + "request.UploadOss": { + "type": "object", + "required": [ + "id", + "ossIds" + ], + "properties": { + "id": { + "description": "数据主键", + "type": "string" + }, + "ossIds": { + "description": "ossIds", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "response.CaptchaRes": { + "type": "object", + "properties": { + "captcha": { + "type": "string" + }, + "captchaId": { + "type": "string" + } + } + }, + "response.LoginResponse": { + "type": "object", + "properties": { + "expiresAt": { + "type": "integer" + }, + "token": { + "type": "string" + }, + "user": { + "$ref": "#/definitions/system.User" + } + } + }, + "response.PageResult": { + "type": "object", + "properties": { + "list": {}, + "page": { + "type": "integer" + }, + "pageSize": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + }, + "response.Response": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "data": {}, + "msg": { + "type": "string" + } + } + }, + "system.Client": { + "type": "object", + "properties": { + "activeTimeout": { + "type": "integer" + }, + "additionalInfo": { + "type": "string" + }, + "clientId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "createdAtStr": { + "type": "string" + }, + "grantType": { + "type": "string" + }, + "id": { + "description": "主键ID", + "type": "string" + }, + "name": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "system.Menu": { + "type": "object", + "properties": { + "category": { + "type": "integer" + }, + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/system.Menu" + } + }, + "code": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "createdAtStr": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "id": { + "description": "主键ID", + "type": "string" + }, + "locale": { + "type": "string" + }, + "name": { + "type": "string" + }, + "parentId": { + "type": "string" + }, + "permission": { + "type": "string" + }, + "sort": { + "type": "integer" + }, + "title": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "system.Oss": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "createdAtStr": { + "type": "string" + }, + "height": { + "type": "integer" + }, + "id": { + "description": "主键ID", + "type": "string" + }, + "key": { + "type": "string" + }, + "md5": { + "type": "string" + }, + "name": { + "type": "string" + }, + "suffix": { + "type": "string" + }, + "tag": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "url": { + "type": "string" + }, + "width": { + "type": "integer" + } + } + }, + "system.Role": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "createdAtStr": { + "type": "string" + }, + "id": { + "description": "主键ID", + "type": "string" + }, + "menus": { + "type": "array", + "items": { + "$ref": "#/definitions/system.Menu" + } + }, + "name": { + "type": "string" + }, + "sort": { + "type": "integer" + }, + "updatedAt": { + "type": "string" + } + } + }, + "system.User": { + "type": "object", + "properties": { + "account": { + "type": "string" + }, + "avatar": { + "$ref": "#/definitions/system.Oss" + }, + "avatarId": { + "type": "string" + }, + "clientId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "createdAtStr": { + "type": "string" + }, + "id": { + "description": "主键ID", + "type": "string" + }, + "miniOpenId": { + "description": "植趣小程序openid", + "type": "string" + }, + "name": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "saOpenId": { + "description": "植趣服务号openid", + "type": "string" + }, + "sessionKey": { + "type": "string" + }, + "tenantId": { + "type": "string" + }, + "unionId": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + } + }, + "securityDefinitions": { + "ApiKeyAuth": { + "type": "apiKey", + "name": "Authorization", + "in": "header" + } + } +} \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml new file mode 100644 index 0000000..93884a8 --- /dev/null +++ b/docs/swagger.yaml @@ -0,0 +1,4336 @@ +definitions: + request.AlterClass: + properties: + id: + description: 植物id + type: string + ids: + description: 要关联的分类ids + items: + type: string + type: array + type: object + request.AlterRelatedLibrary: + properties: + id: + description: 植物id + type: string + ids: + description: 要关联的植物ids + items: + type: string + type: array + type: object + request.CareItem: + properties: + desc: + type: string + name: + type: string + period: + type: integer + type: object + request.CarePlan: + properties: + desc: + type: string + name: + description: 农事名称 + type: string + period: + description: 周期 + type: integer + type: object + request.CareSchedule: + properties: + careItems: + description: 养护项目 + items: + $ref: '#/definitions/request.CareItem' + type: array + desc: + description: 描述 + type: string + season: + description: 季节 + type: string + sort: + description: 排序 + type: integer + type: object + request.ChangePwd: + properties: + id: + type: string + newPwd: + type: string + type: object + request.CreateAddress: + properties: + detail: + type: string + isDefault: + type: integer + name: + type: string + phone: + type: string + type: object + request.CreateBadge: + properties: + categoryId: + type: string + countLimit: + description: 获取徽章需要完成的操作次数 + type: integer + desc: + type: string + keyword: + type: string + name: + type: string + ossId: + type: string + required: + - countLimit + type: object + request.CreateBadgeCategory: + properties: + desc: + type: string + name: + type: string + required: + - name + type: object + request.CreateClaimPlant: + properties: + content: + type: string + libraryId: + description: 百科id + type: string + name: + type: string + ossIds: + items: + type: string + type: array + points: + type: integer + postage: + description: 邮费 单位(分) + type: integer + stock: + description: 库存 + type: integer + tag: + type: string + videoUrl: + type: string + type: object + request.CreateClassification: + properties: + name: + description: 分类名称 + type: string + ossId: + description: 图片id + type: string + tag: + description: 标签 + type: string + required: + - name + type: object + request.CreateComment: + properties: + content: + description: 评论内容 + type: string + parentId: + description: 父级评论id" 默认为0 + type: string + postId: + description: 帖子id + type: string + rootId: + description: 根评论id 默认为0 如评论1 评论2 评论3 1是根评论 2是1的子评论 3是2的子评论 那么2和3的rootId都是1的主键id + type: string + required: + - content + - parentId + - postId + - rootId + type: object + request.CreateGrowRecord: + properties: + content: + type: string + desc: + type: string + name: + type: string + ossIds: + items: + type: string + type: array + plantId: + type: string + tag: + type: string + required: + - plantId + type: object + request.CreateInvitationConfig: + properties: + codeExpireDays: + description: 邀请码有效期(天) + type: integer + codeLength: + description: 邀请码长度 + type: integer + enableAutoAward: + description: 是否自动发放积分 + type: integer + isActive: + description: 是否启用 + type: integer + maxInvitesPerDay: + description: 每日最大邀请奖励次数 + type: integer + maxPointsPerUser: + description: 单用户最大邀请奖励积分 + type: integer + name: + description: 配置名称 + type: string + pointsPerInvite: + description: 每成功邀请奖励积分 + type: integer + type: object + request.CreateLibrary: + properties: + aliases: + description: 别名 + type: string + careSchedule: + description: 养护计划 + items: + $ref: '#/definitions/request.CareSchedule' + type: array + classIds: + description: 分类id + items: + type: string + type: array + difficulty: + description: 种植难度 1-5级 + type: integer + distributionArea: + description: 分布区域 + type: string + flowerDiameter: + description: 花直径(cm) + type: integer + floweringColor: + description: 花色 + type: string + floweringPeriod: + description: 开花特征 + type: string + floweringShape: + description: 花形 + type: string + foliageColor: + description: 叶色 + type: string + foliageShape: + description: 叶形 + type: string + foliageType: + description: 植物特征 + type: string + fruit: + description: 果 + type: string + genus: + description: 属 + type: string + growthHabit: + description: 生长习性 + type: string + height: + description: 高度(cm) + type: integer + isHot: + description: 是否热门 + type: integer + latinName: + description: 拉丁名 + type: string + lifeCycle: + description: 生命周期 + type: string + lightIntensity: + description: 光照强度 + type: string + lightType: + description: 光照类型(直射,散射等) + type: string + name: + description: 基本信息 + type: string + optimalTempPeriod: + description: 最佳温度区间 + type: string + ossIds: + description: 图片 + items: + type: string + type: array + pestsDiseases: + description: 常见病虫害 + type: string + relatedLibraryIds: + description: 相关推荐 library + items: + type: string + type: array + reproductionMethod: + description: 繁殖方法 + type: string + stem: + description: 茎 + type: string + tag: + description: 标签 + type: string + required: + - name + type: object + request.CreateMyPlant: + properties: + carePlans: + description: 养护计划 + items: + $ref: '#/definitions/request.CarePlan' + type: array + name: + description: 植物名称 + type: string + ossIds: + description: 图片 + items: + type: string + type: array + placement: + description: 摆放位置 + type: string + plantTime: + description: 种植时间 + type: string + plantingMaterial: + description: 植料(即土的材质) + type: string + potMaterial: + description: 花盆材质 + type: string + potSize: + description: 花盆大小 如直径 20cm × 高度 18cm + type: string + sunlight: + description: 光照条件如每日12小时 + type: string + type: object + request.CreatePost: + properties: + content: + description: 内容 + type: string + location: + description: 位置 + type: string + ossIds: + description: 图片id[] + items: + type: string + type: array + title: + description: 标题 必须 + type: string + type: object + request.CreateQrcode: + properties: + name: + type: string + ossId: + type: string + url: + type: string + type: object + request.DeleteClass: + properties: + id: + description: 植物id + type: string + ids: + description: 要取消关联的分类ids + items: + type: string + type: array + type: object + request.DeleteOss: + properties: + id: + description: 数据主键 + type: string + ossIds: + items: + type: string + type: array + required: + - id + - ossIds + type: object + request.DeleteRelatedLibrary: + properties: + id: + description: 植物id + type: string + ids: + description: 要取消关联的植物ids + items: + type: string + type: array + type: object + request.GetClientList: + properties: + clientId: + type: string + current: + description: 页码 + type: integer + keyword: + description: 关键字 + type: string + name: + type: string + pageSize: + description: 每页大小 + type: integer + type: object + request.GetMenuTree: + properties: + category: + type: integer + parentId: + type: string + type: object + request.GetOssFileList: + properties: + current: + description: 页码 + type: integer + keyword: + description: 关键字 + type: string + name: + type: string + pageSize: + description: 每页大小 + type: integer + type: object + request.GetRoleList: + properties: + code: + type: string + current: + description: 页码 + type: integer + keyword: + description: 关键字 + type: string + name: + type: string + pageSize: + description: 每页大小 + type: integer + type: object + request.GetUserList: + properties: + account: + type: string + current: + description: 页码 + type: integer + keyword: + description: 关键字 + type: string + pageSize: + description: 每页大小 + type: integer + phone: + type: string + type: object + request.GrantMenu: + properties: + menuIds: + items: + type: string + type: array + roleId: + type: string + type: object + request.GrantRole: + properties: + roleIds: + items: + type: string + type: array + userId: + type: string + type: object + request.IdsReq: + properties: + ids: + items: + type: string + type: array + type: object + request.LibraryList: + properties: + isHot: + description: 是否热门 0否 1是 + type: integer + name: + description: 植物名称 + type: string + type: object + request.LibraryPage: + properties: + classId: + description: 分类id + type: string + current: + description: 页码 + type: integer + isHot: + description: 是否热门 0否 1是 + type: integer + keyword: + description: 关键字 + type: string + name: + description: 植物名称 + type: string + pageSize: + description: 每页大小 + type: integer + type: object + request.Login: + properties: + account: + type: string + captcha: + type: string + captchaId: + type: string + password: + type: string + type: object + request.MakeCare: + properties: + id: + description: 今日养护id(任务id) + type: string + remark: + description: 备注 + type: string + status: + description: 状态 1:未完成 2:完成 3:跳过 4:逾期 + type: integer + required: + - id + - status + type: object + request.OrderPage: + properties: + current: + description: 页码 + type: integer + isShipped: + description: 是否发货 0 1 + type: integer + keyword: + description: 关键字 + type: string + pageSize: + description: 每页大小 + type: integer + status: + description: 支付状态 1.SUCCESS 2.REFUND 3.NOTPAY 4.CLOSED + type: integer + type: object + request.PageBadge: + properties: + categoryId: + type: string + current: + description: 页码 + type: integer + keyword: + description: 关键字 + type: string + name: + type: string + pageSize: + description: 每页大小 + type: integer + type: object + request.PageCategory: + properties: + current: + description: 页码 + type: integer + keyword: + description: 关键字 + type: string + name: + type: string + pageSize: + description: 每页大小 + type: integer + type: object + request.PageClaimPlant: + properties: + current: + description: 页码 + type: integer + keyword: + description: 关键字 + type: string + name: + type: string + pageSize: + description: 每页大小 + type: integer + type: object + request.PageGrowRecord: + properties: + current: + description: 页码 + type: integer + id: + description: 植物id + type: string + keyword: + description: 关键字 + type: string + pageSize: + description: 每页大小 + type: integer + type: object + request.PageInfo: + properties: + current: + description: 页码 + type: integer + keyword: + description: 关键字 + type: string + pageSize: + description: 每页大小 + type: integer + type: object + request.PagePlant: + properties: + current: + description: 页码 + type: integer + keyword: + description: 关键字 + type: string + name: + type: string + pageSize: + description: 每页大小 + type: integer + type: object + request.PostPage: + properties: + current: + description: 页码 + type: integer + hasReviewed: + description: 是否审核通过 + type: integer + keyword: + description: 关键字 + type: string + pageSize: + description: 每页大小 + type: integer + title: + description: 标题 + type: string + type: object + request.UnionLibrary: + properties: + id: + description: 植物id + type: string + libraryId: + description: 百科植物id + type: string + type: object + request.UpdateAddress: + properties: + detail: + type: string + id: + type: string + isDefault: + type: integer + name: + type: string + phone: + type: string + required: + - id + type: object + request.UpdateBadge: + properties: + categoryId: + type: string + countLimit: + description: 获取徽章需要完成的操作次数 + type: integer + desc: + type: string + id: + type: string + keyword: + type: string + name: + type: string + ossId: + type: string + required: + - countLimit + - id + type: object + request.UpdateBadgeCategory: + properties: + desc: + type: string + id: + type: string + name: + type: string + required: + - id + type: object + request.UpdateCarePlan: + properties: + id: + description: 养护计划id + type: string + period: + description: 周期 + type: integer + required: + - id + - period + type: object + request.UpdateClaimPlant: + properties: + content: + type: string + id: + type: string + libraryId: + description: 百科id + type: string + name: + type: string + points: + type: integer + postage: + description: 邮费 单位(分) + type: integer + stock: + description: 库存 + type: integer + tag: + type: string + videoUrl: + type: string + required: + - id + type: object + request.UpdateClassification: + properties: + id: + type: string + name: + description: 分类名称 + type: string + ossId: + description: 图片id + type: string + tag: + description: 标签 + type: string + required: + - id + type: object + request.UpdateInvitationConfig: + properties: + codeExpireDays: + description: 邀请码有效期(天) + type: integer + codeLength: + description: 邀请码长度 + type: integer + enableAutoAward: + description: 是否自动发放积分 + type: integer + id: + type: string + isActive: + description: 是否启用 + type: integer + maxInvitesPerDay: + description: 每日最大邀请奖励次数 + type: integer + maxPointsPerUser: + description: 单用户最大邀请奖励积分 + type: integer + name: + description: 配置名称 + type: string + pointsPerInvite: + description: 每成功邀请奖励积分 + type: integer + required: + - id + type: object + request.UpdateLibrary: + properties: + aliases: + description: 别名 + type: string + difficulty: + description: 种植难度 1-5级 + type: integer + distributionArea: + description: 分布区域 + type: string + flowerDiameter: + description: 花直径(cm) + type: integer + floweringColor: + description: 花色 + type: string + floweringPeriod: + description: 开花特征 + type: string + floweringShape: + description: 花形 + type: string + foliageColor: + description: 叶色 + type: string + foliageShape: + description: 叶形 + type: string + foliageType: + description: 植物特征 + type: string + fruit: + description: 果 + type: string + genus: + description: 属 + type: string + growthHabit: + description: 生长习性 + type: string + height: + description: 高度(cm) + type: integer + id: + type: string + isHot: + description: 是否热门 + type: integer + latinName: + description: 拉丁名 + type: string + lifeCycle: + description: 生命周期 + type: string + lightIntensity: + description: 光照强度 + type: string + lightType: + description: 光照类型(直射,散射等) + type: string + name: + description: 名称 + type: string + optimalTempPeriod: + description: 温度 + type: string + ossId: + description: ossId + type: string + pestsDiseases: + description: 常见病虫害 + type: string + stem: + description: 茎 + type: string + tag: + description: 标签 + type: string + required: + - id + type: object + request.UpdateMyPlant: + properties: + id: + type: string + name: + description: 植物名称 + type: string + placement: + description: 摆放位置 + type: string + plantingMaterial: + description: 植料(即土的材质) + type: string + potMaterial: + description: 花盆材质 + type: string + potSize: + description: 花盆大小 如直径 20cm × 高度 18cm + type: string + sunlight: + description: 光照条件如每日12小时 + type: string + required: + - id + type: object + request.UpdatePost: + properties: + content: + description: 内容 + type: string + id: + type: string + title: + description: 标题 + type: string + required: + - id + type: object + request.UpdateQrcode: + properties: + id: + type: string + name: + type: string + url: + type: string + required: + - id + type: object + request.UploadFile: + properties: + id: + description: 数据主键 + type: string + ossIds: + description: ossId + type: string + required: + - id + - ossIds + type: object + request.UploadOss: + properties: + id: + description: 数据主键 + type: string + ossIds: + description: ossIds + items: + type: string + type: array + required: + - id + - ossIds + type: object + response.CaptchaRes: + properties: + captcha: + type: string + captchaId: + type: string + type: object + response.LoginResponse: + properties: + expiresAt: + type: integer + token: + type: string + user: + $ref: '#/definitions/system.User' + type: object + response.PageResult: + properties: + list: {} + page: + type: integer + pageSize: + type: integer + total: + type: integer + type: object + response.Response: + properties: + code: + type: integer + data: {} + msg: + type: string + type: object + system.Client: + properties: + activeTimeout: + type: integer + additionalInfo: + type: string + clientId: + type: string + createdAt: + type: string + createdAtStr: + type: string + grantType: + type: string + id: + description: 主键ID + type: string + name: + type: string + updatedAt: + type: string + type: object + system.Menu: + properties: + category: + type: integer + children: + items: + $ref: '#/definitions/system.Menu' + type: array + code: + type: string + createdAt: + type: string + createdAtStr: + type: string + icon: + type: string + id: + description: 主键ID + type: string + locale: + type: string + name: + type: string + parentId: + type: string + permission: + type: string + sort: + type: integer + title: + type: string + updatedAt: + type: string + type: object + system.Oss: + properties: + createdAt: + type: string + createdAtStr: + type: string + height: + type: integer + id: + description: 主键ID + type: string + key: + type: string + md5: + type: string + name: + type: string + suffix: + type: string + tag: + type: string + updatedAt: + type: string + url: + type: string + width: + type: integer + type: object + system.Role: + properties: + code: + type: string + createdAt: + type: string + createdAtStr: + type: string + id: + description: 主键ID + type: string + menus: + items: + $ref: '#/definitions/system.Menu' + type: array + name: + type: string + sort: + type: integer + updatedAt: + type: string + type: object + system.User: + properties: + account: + type: string + avatar: + $ref: '#/definitions/system.Oss' + avatarId: + type: string + clientId: + type: string + createdAt: + type: string + createdAtStr: + type: string + id: + description: 主键ID + type: string + miniOpenId: + description: 植趣小程序openid + type: string + name: + type: string + phone: + type: string + saOpenId: + description: 植趣服务号openid + type: string + sessionKey: + type: string + tenantId: + type: string + unionId: + type: string + updatedAt: + type: string + type: object +info: + contact: {} + description: 使用gin + gorm进行极速开发的全栈开发基础平台 + title: Swagger API接口文档 + version: v1.0.0 +paths: + /auth/captcha: + get: + produces: + - application/json + responses: + "200": + description: 获取验证码 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.CaptchaRes' + type: object + summary: 获取验证码 + tags: + - 登录相关 + /auth/getLocation: + get: + parameters: + - description: longitude + in: query + name: longitude + required: true + type: string + - description: latitude + in: query + name: latitude + required: true + type: string + produces: + - application/json + responses: {} + summary: 获取位置信息 + tags: + - 登录相关 + /auth/getPhone: + get: + parameters: + - description: code + in: query + name: code + required: true + type: string + - description: openId + in: query + name: openId + required: true + type: string + produces: + - application/json + responses: {} + summary: 获取手机号 + tags: + - 登录相关 + /auth/getWeather: + get: + parameters: + - description: adcode + in: query + name: adcode + required: true + type: string + produces: + - application/json + responses: {} + summary: 获取天气信息 + tags: + - 登录相关 + /auth/login: + post: + consumes: + - application/json + parameters: + - description: 用户名, 密码, 验证码,验证码id + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.Login' + produces: + - application/json + responses: + "200": + description: 登录成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + summary: pc登录 + tags: + - 登录相关 + /auth/logout: + get: + produces: + - application/json + responses: + "200": + description: 登出成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: pc登出 + tags: + - 登录相关 + /auth/miniLogin: + get: + parameters: + - description: code + in: query + name: code + required: true + type: string + produces: + - application/json + responses: + "200": + description: 小程序登录 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.LoginResponse' + type: object + summary: 小程序登录 + tags: + - 登录相关 + /badge/add: + post: + consumes: + - application/json + parameters: + - description: 添加 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.CreateBadge' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"添加成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 添加 + tags: + - 徽章 + /badge/all: + get: + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + summary: 获取所有无分页 + tags: + - 徽章 + /badge/class/add: + post: + consumes: + - application/json + parameters: + - description: 添加分类 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.CreateBadgeCategory' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"添加成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 添加分类 + tags: + - 徽章 + /badge/class/delete: + post: + consumes: + - application/json + parameters: + - description: 删除分类 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.IdsReq' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"删除成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 删除分类 + tags: + - 徽章 + /badge/class/detail: + get: + parameters: + - description: id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 分类详情 + tags: + - 徽章 + /badge/class/list: + post: + consumes: + - application/json + parameters: + - description: 分类列表 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.PageCategory' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 分类列表 + tags: + - 徽章 + /badge/class/update: + post: + consumes: + - application/json + parameters: + - description: 修改分类 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.UpdateBadgeCategory' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"修改成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 修改分类 + tags: + - 徽章 + /badge/delete: + post: + consumes: + - application/json + parameters: + - description: 删除ByIds + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.IdsReq' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"删除成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 删除ByIds + tags: + - 徽章 + /badge/detail: + get: + parameters: + - description: id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 详情 + tags: + - 徽章 + /badge/list: + post: + consumes: + - application/json + parameters: + - description: 获取列表 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.PageBadge' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 获取列表有分页 + tags: + - 徽章 + /badge/listByKeyword: + get: + parameters: + - description: keyword + in: query + name: keyword + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 获取列表byKeyword无分页 + tags: + - 徽章 + /badge/my/all: + get: + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 我的所有徽章 + tags: + - 徽章 + /badge/my/detail: + get: + parameters: + - description: id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 我的徽章详情 + tags: + - 徽章 + /badge/my/list: + post: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 我的徽章 + tags: + - 徽章 + /badge/update: + post: + consumes: + - application/json + parameters: + - description: 修改 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.UpdateBadge' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"修改成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 修改 + tags: + - 徽章 + /badge/uploadImg: + post: + consumes: + - application/json + parameters: + - description: 上传图片 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.UploadFile' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"上传成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 上传图片 + tags: + - 徽章 + /claim/add: + post: + consumes: + - application/json + parameters: + - description: 添加 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.CreateClaimPlant' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"添加成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 配置植物 + tags: + - 兑换植物 + /claim/cancelUnion: + post: + parameters: + - description: id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"取消关联成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 取消关联百科植物 + tags: + - 兑换植物 + /claim/claim: + get: + consumes: + - application/json + parameters: + - description: id + in: query + name: id + required: true + type: string + - description: addressId + in: query + name: addressId + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"认养成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 认养 + tags: + - 兑换植物 + /claim/delete: + post: + consumes: + - application/json + parameters: + - description: 删除 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.IdsReq' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"删除成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 删除ByIds + tags: + - 兑换植物 + /claim/detail: + get: + parameters: + - description: id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 详情 + tags: + - 兑换植物 + /claim/list: + post: + consumes: + - application/json + parameters: + - description: 获取列表 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.PageClaimPlant' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 获取列表 + tags: + - 兑换植物 + /claim/myClaims: + get: + consumes: + - application/json + parameters: + - description: 获取列表 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.PageClaimPlant' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 我的认养 + tags: + - 兑换植物 + /claim/unionLibrary: + post: + consumes: + - application/json + parameters: + - description: 关联 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.UnionLibrary' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"关联成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 关联百科植物 + tags: + - 兑换植物 + /claim/update: + post: + consumes: + - application/json + parameters: + - description: 修改 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.UpdateClaimPlant' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"修改成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 修改植物 + tags: + - 兑换植物 + /class/add: + post: + consumes: + - application/json + parameters: + - description: 新增分类 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.CreateClassification' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"新增成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 新增分类 + tags: + - 植物库分类 + /class/delete: + post: + consumes: + - application/json + parameters: + - description: 删除分类 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.IdsReq' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"删除成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 删除分类 + tags: + - 植物库分类 + /class/detail: + get: + parameters: + - description: 分类id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 分类详情 + tags: + - 植物库分类 + /class/list: + post: + consumes: + - application/json + parameters: + - description: 分类列表 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.PageInfo' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 分类列表 + tags: + - 植物库分类 + /class/update: + post: + consumes: + - application/json + parameters: + - description: 修改分类 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.UpdateClassification' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"修改成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 修改分类 + tags: + - 植物库分类 + /client/delete: + post: + consumes: + - application/json + parameters: + - description: ids + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.IdsReq' + produces: + - application/json + responses: + "200": + description: 删除client + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 删除client + tags: + - 客户端管理 + /client/detail: + get: + description: id获取详情 + parameters: + - description: id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: 获取client详情 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/system.Client' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取client详情 + tags: + - 客户端管理 + /client/getClientList: + post: + consumes: + - application/json + parameters: + - description: client + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.GetClientList' + produces: + - application/json + responses: + "200": + description: 获取client列表 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.PageResult' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取client列表 + tags: + - 客户端管理 + /client/save: + post: + consumes: + - application/json + parameters: + - description: client + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.Client' + produces: + - application/json + responses: + "200": + description: 创建client + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 创建client + tags: + - 客户端管理 + /client/update: + post: + consumes: + - application/json + parameters: + - description: client + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.Client' + produces: + - application/json + responses: + "200": + description: 更新client + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 更新client + tags: + - 客户端管理 + /comment/add: + post: + consumes: + - application/json + parameters: + - description: 添加评论 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.CreateComment' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"添加成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 发表评论 + tags: + - 帖子评论 + /comment/delete: + delete: + consumes: + - application/json + parameters: + - description: 删除评论 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.IdsReq' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"删除成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 删除评论 + tags: + - 帖子评论 + /config/invitation/add: + post: + consumes: + - application/json + parameters: + - description: 配置邀请码 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.CreateInvitationConfig' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"配置成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 配置邀请码 + tags: + - 配置中心 + /config/invitation/delete: + post: + consumes: + - application/json + parameters: + - description: 删除邀请码 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.IdsReq' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"删除成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 删除邀请码配置 + tags: + - 配置中心 + /config/invitation/detail: + get: + parameters: + - description: 邀请码id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 获取邀请码配置 + tags: + - 配置中心 + /config/invitation/list: + post: + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 获取邀请码列表 + tags: + - 配置中心 + /config/invitation/update: + post: + consumes: + - application/json + parameters: + - description: 更新邀请码 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.UpdateInvitationConfig' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"更新成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 更新邀请码配置 + tags: + - 配置中心 + /config/qrcode/add: + post: + consumes: + - application/json + parameters: + - description: 配置二维码 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.CreateQrcode' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"配置成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 配置二维码 + tags: + - 配置中心 + /config/qrcode/delete: + post: + parameters: + - description: 二维码id + in: body + name: ids + required: true + schema: + $ref: '#/definitions/request.IdsReq' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"删除成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 删除二维码 + tags: + - 配置中心 + /config/qrcode/detail: + get: + parameters: + - description: 二维码id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 获取二维码详情 + tags: + - 配置中心 + /config/qrcode/list: + post: + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 获取二维码 + tags: + - 配置中心 + /config/qrcode/update: + post: + consumes: + - application/json + parameters: + - description: 更新二维码 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.UpdateQrcode' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"更新成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 更新二维码 + tags: + - 配置中心 + /config/qrcode/uploadImg: + post: + consumes: + - application/json + parameters: + - description: 上传二维码图片 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.UploadFile' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"上传成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 上传二维码图片 + tags: + - 配置中心 + /library/add: + post: + consumes: + - application/json + parameters: + - description: 新增植物库 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.CreateLibrary' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 新增植物库 + tags: + - 植物库 + /library/all: + post: + consumes: + - application/json + parameters: + - description: 获取所有植物库列表 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.LibraryList' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取所有植物库列表 + tags: + - 植物库 + /library/alterClass: + post: + consumes: + - application/json + parameters: + - description: 修改分类 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.AlterClass' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 修改分类 + tags: + - 植物库 + /library/delete: + post: + consumes: + - application/json + parameters: + - description: 删除植物库 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.IdsReq' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 删除植物库 + tags: + - 植物库 + /library/deleteClass: + post: + consumes: + - application/json + parameters: + - description: 取消关联分类 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.DeleteClass' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 取消关联分类 + tags: + - 植物库 + /library/deleteImg: + post: + consumes: + - application/json + parameters: + - description: 上传图片 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.DeleteOss' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 上传图片 + tags: + - 植物库 + /library/deleteRelateLibrary: + post: + consumes: + - application/json + parameters: + - description: 删除关联 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.DeleteRelatedLibrary' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 删除关联植物 + tags: + - 植物库 + /library/detail: + get: + parameters: + - description: 获取植物库详情 + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取植物库详情 + tags: + - 植物库 + /library/hot: + post: + consumes: + - application/json + parameters: + - description: 设置热门 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.IdsReq' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 设置或取消热门 + tags: + - 植物库 + /library/page: + post: + consumes: + - application/json + parameters: + - description: 获取植物库列表 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.LibraryPage' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取植物库列表 + tags: + - 植物库 + /library/reateLibrary: + post: + consumes: + - application/json + parameters: + - description: 添加关联 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.AlterRelatedLibrary' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 添加关联植物 + tags: + - 植物库 + /library/update: + post: + consumes: + - application/json + parameters: + - description: 修改植物库 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.UpdateLibrary' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 修改植物库 + tags: + - 植物库 + /library/uploadImg: + post: + consumes: + - application/json + parameters: + - description: 上传图片 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.UploadOss' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 上传图片 + tags: + - 植物库 + /menu/delete: + get: + description: 删除menu + parameters: + - description: id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: 详情 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 删除menu + tags: + - 菜单管理 + /menu/detail: + get: + description: id获取详情 + parameters: + - description: id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: 详情 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/system.Menu' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取menu详情 + tags: + - 菜单管理 + /menu/getAllMenuTree: + post: + consumes: + - application/json + parameters: + - description: 菜单信息 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.GetMenuTree' + produces: + - application/json + responses: + "200": + description: 获取所有菜单树 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + items: + $ref: '#/definitions/system.Menu' + type: array + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取所有菜单树 + tags: + - 菜单管理 + /menu/getUserMenuTree: + get: + produces: + - application/json + responses: + "200": + description: 用户菜单数据 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + items: + $ref: '#/definitions/system.Menu' + type: array + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 用户菜单数据 + tags: + - 菜单管理 + /menu/route: + get: + produces: + - application/json + responses: + "200": + description: 用户route + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + items: + $ref: '#/definitions/system.Menu' + type: array + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 用户路由 + tags: + - 菜单管理 + /menu/save: + post: + consumes: + - application/json + parameters: + - description: menu + in: body + name: data + schema: + $ref: '#/definitions/system.Menu' + produces: + - application/json + responses: + "200": + description: 新建菜单/按钮 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 新增菜单 + tags: + - 菜单管理 + /menu/update: + post: + consumes: + - application/json + parameters: + - description: menu + in: body + name: data + schema: + $ref: '#/definitions/system.Menu' + produces: + - application/json + responses: + "200": + description: 更新菜单 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 更新菜单 + tags: + - 菜单管理 + /ocr/base64: + post: + consumes: + - multipart/form-data + parameters: + - description: 植物识别 + in: formData + name: file + required: true + type: file + produces: + - application/json + responses: + "200": + description: 文件OCR + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: base64植物识别 + tags: + - 识别相关 + /ocr/url: + get: + consumes: + - multipart/form-data + parameters: + - description: id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: 文件OCR + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: url植物识别 + tags: + - 识别相关 + /order/delete: + post: + consumes: + - application/json + parameters: + - description: 删除订单 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.IdsReq' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 删除订单 + tags: + - 订单 + /order/detail: + get: + parameters: + - description: id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 订单详情 + tags: + - 订单 + /order/export: + post: + parameters: + - description: 页码 + in: query + name: current + type: integer + - description: 是否发货 0 1 + in: query + name: isShipped + type: integer + - description: 关键字 + in: query + name: keyword + type: string + - description: 每页大小 + in: query + name: pageSize + type: integer + - description: 支付状态 1.SUCCESS 2.REFUND 3.NOTPAY 4.CLOSED + in: query + name: status + type: integer + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 导出订单 + tags: + - 订单 + /order/page: + post: + consumes: + - application/json + parameters: + - description: 获取列表 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.OrderPage' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 订单列表 + tags: + - 订单 + /order/ship: + post: + consumes: + - application/json + parameters: + - description: 发货 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.IdsReq' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 发货 + tags: + - 订单 + /oss/delete: + post: + consumes: + - application/json + parameters: + - description: 批量删除文件 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.IdsReq' + produces: + - application/json + responses: + "200": + description: 删除文件 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 删除文件 + tags: + - 文件相关 + /oss/detail: + get: + parameters: + - description: 文件id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: 文件详情 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 文件详情 + tags: + - 文件相关 + /oss/getFileList: + post: + consumes: + - application/json + parameters: + - description: 文件列表 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.GetOssFileList' + produces: + - application/json + responses: + "200": + description: 文件列表 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 文件列表 + tags: + - 文件相关 + /oss/upload: + post: + consumes: + - multipart/form-data + parameters: + - description: 上传文件 + in: formData + name: file + required: true + type: file + produces: + - application/json + responses: + "200": + description: 上传文件 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 文件上传 + tags: + - 文件相关 + /pay/prePay: + get: + consumes: + - application/json + parameters: + - description: 支付 + in: query + name: orderId + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"支付成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 支付 + tags: + - 微信支付 + /personal/address/add: + post: + consumes: + - application/json + parameters: + - description: 添加地址 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.CreateAddress' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"添加成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 添加地址 + tags: + - 个人中心 + /personal/address/delete: + post: + consumes: + - application/json + parameters: + - description: 删除地址 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.IdsReq' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"删除成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 删除地址 + tags: + - 个人中心 + /personal/address/detail: + get: + parameters: + - description: id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 地址详情 + tags: + - 个人中心 + /personal/address/list: + get: + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 地址列表 + tags: + - 个人中心 + /personal/address/setDefault: + post: + consumes: + - application/json + parameters: + - description: id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"设置成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 设置默认地址 + tags: + - 个人中心 + /personal/address/update: + post: + consumes: + - application/json + parameters: + - description: 修改地址 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.UpdateAddress' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"修改成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 修改地址 + tags: + - 个人中心 + /personal/centerCount: + get: + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 个人中心统计 + tags: + - 个人中心 + /personal/inviteCode/accept: + get: + parameters: + - description: inviteCode + in: query + name: inviteCode + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"接受成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 接受邀请 + tags: + - 个人中心 + /personal/inviteCode/code: + get: + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"生成成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 生成邀请码 + tags: + - 个人中心 + /personal/inviteCode/records: + post: + consumes: + - application/json + parameters: + - description: 分页参数 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.PageInfo' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 邀请记录 + tags: + - 个人中心 + /plant/add: + post: + consumes: + - application/json + parameters: + - description: 创建植物 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.CreateMyPlant' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"添加成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 添加植物 + tags: + - 我的植物 + /plant/carePlant: + get: + parameters: + - description: id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"操作成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 立即养护 + tags: + - 我的植物 + /plant/careRecords: + get: + parameters: + - description: id + in: query + name: id + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"操作成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 养护记录 + tags: + - 我的植物 + /plant/delete: + post: + consumes: + - application/json + parameters: + - description: 删除ByIds + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.IdsReq' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"删除成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 删除ByIds + tags: + - 我的植物 + /plant/deleteImg: + post: + consumes: + - application/json + parameters: + - description: 删除图片 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.DeleteOss' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"删除成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 删除图片 + tags: + - 我的植物 + /plant/detail: + get: + parameters: + - description: id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 获取ById + tags: + - 我的植物 + /plant/grow/addRecord: + post: + consumes: + - application/json + parameters: + - description: 添加成长记录 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.CreateGrowRecord' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"操作成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 添加成长记录 + tags: + - 我的植物 + /plant/grow/recordDetail: + get: + parameters: + - description: id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"操作成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 成长记录详情 + tags: + - 我的植物 + /plant/grow/recordList: + post: + consumes: + - application/json + parameters: + - description: 成长记录列表 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.PageGrowRecord' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"操作成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 成长记录列表 + tags: + - 我的植物 + /plant/makeCare: + post: + consumes: + - application/json + parameters: + - description: 养护操作 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.MakeCare' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"操作成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 根据任务完成养护 + tags: + - 我的植物 + /plant/myPlants: + get: + parameters: + - description: name + in: query + name: name + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 植物列表不分页 + tags: + - 我的植物 + /plant/needCare: + get: + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"操作成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 待养护 + tags: + - 我的植物 + /plant/page: + post: + consumes: + - application/json + parameters: + - description: 获取植物列表 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.PagePlant' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 植物列表 + tags: + - 我的植物 + /plant/taskProgress: + get: + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"操作成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 任务进度 + tags: + - 我的植物 + /plant/todayCare: + get: + responses: + "200": + description: '{"success":true,"data":{},"msg":"操作成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 今日养护 + tags: + - 我的植物 + /plant/update: + post: + consumes: + - application/json + parameters: + - description: 修改植物 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.UpdateMyPlant' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"修改成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 修改植物 + tags: + - 我的植物 + /plant/updateCarePlan: + post: + consumes: + - application/json + parameters: + - description: 修改养护周期 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.UpdateCarePlan' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"修改成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 修改养护事项周期 + tags: + - 我的植物 + /plant/uploadImg: + post: + consumes: + - application/json + parameters: + - description: 上传图片 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.UploadOss' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"上传成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 上传图片 + tags: + - 我的植物 + /post/add: + post: + consumes: + - application/json + parameters: + - description: 创建帖子 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.CreatePost' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"添加成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 发布 + tags: + - 帖子 + /post/cancelLike: + get: + parameters: + - description: 帖子id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"取消点赞成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 取消点赞 + tags: + - 帖子 + /post/delete: + post: + consumes: + - application/json + parameters: + - description: 删除帖子 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.IdsReq' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"删除成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 删除帖子 + tags: + - 帖子 + /post/deleteImg: + post: + consumes: + - application/json + parameters: + - description: 删除帖子图片 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.DeleteOss' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"删除成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 删除帖子图片 + tags: + - 帖子 + /post/detail: + get: + parameters: + - description: 帖子id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 帖子详情 + tags: + - 帖子 + /post/like: + get: + parameters: + - description: 帖子id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"点赞成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 点赞帖子 + tags: + - 帖子 + /post/myPosts: + post: + consumes: + - application/json + parameters: + - description: 页码 + in: query + name: current + type: integer + - description: 关键字 + in: query + name: keyword + type: string + - description: 每页大小 + in: query + name: pageSize + type: integer + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 我的帖子 + tags: + - 帖子 + /post/page: + post: + consumes: + - application/json + parameters: + - description: 帖子列表 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.PostPage' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"获取成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 帖子列表 + tags: + - 帖子 + /post/update: + post: + consumes: + - application/json + parameters: + - description: 修改帖子 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.UpdatePost' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"修改成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 修改帖子 + tags: + - 帖子 + /post/uploadImg: + post: + consumes: + - application/json + parameters: + - description: 上传帖子图片 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.UploadOss' + produces: + - application/json + responses: + "200": + description: '{"success":true,"data":{},"msg":"上传成功"}' + schema: + type: string + security: + - ApiKeyAuth: [] + summary: 上传帖子图片 + tags: + - 帖子 + /role/delete: + post: + consumes: + - application/json + description: 删除角色 + parameters: + - description: 批量删除角色 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.IdsReq' + produces: + - application/json + responses: + "200": + description: 删除角色 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + summary: 删除角色 + tags: + - 角色管理 + /role/detail: + get: + parameters: + - description: id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: 角色详情 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/system.Role' + type: object + security: + - ApiKeyAuth: [] + summary: 角色详情 + tags: + - 角色管理 + /role/getRoleList: + post: + consumes: + - application/json + description: 获取角色列表 + parameters: + - description: 页码, 每页大小, 搜索条件 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.GetRoleList' + produces: + - application/json + responses: + "200": + description: 获取角色列表,返回包括列表,总数,页码,每页大小 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.PageResult' + msg: + type: string + type: object + summary: 获取角色列表 + tags: + - 角色管理 + /role/grantMenu: + post: + consumes: + - application/json + parameters: + - description: 授权菜单给角色 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.GrantMenu' + produces: + - application/json + responses: + "200": + description: 授权菜单给角色 + schema: + $ref: '#/definitions/response.Response' + security: + - ApiKeyAuth: [] + summary: 授权菜单给角色 + tags: + - 角色管理 + /role/save: + post: + consumes: + - application/json + parameters: + - description: 角色信息 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.Role' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + security: + - ApiKeyAuth: [] + summary: 创建角色 + tags: + - 角色管理 + /role/update: + post: + consumes: + - application/json + parameters: + - description: 角色ID + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.Role' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + security: + - ApiKeyAuth: [] + summary: 修改角色 + tags: + - 角色管理 + /user/changePassword: + post: + consumes: + - application/json + description: 修改密码 + parameters: + - description: 用户id + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.ChangePwd' + produces: + - application/json + responses: + "200": + description: 修改密码成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/system.User' + type: object + security: + - ApiKeyAuth: [] + summary: 修改密码 + tags: + - 用户管理 + /user/delete: + post: + consumes: + - application/json + parameters: + - description: 批量删除用户 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.IdsReq' + produces: + - application/json + responses: + "200": + description: 删除用户 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 删除用户 + tags: + - 用户管理 + /user/detail: + get: + parameters: + - description: id + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: 获取用户详情成功 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/system.User' + type: object + security: + - ApiKeyAuth: [] + summary: 获取用户详情 + tags: + - 用户管理 + /user/getUserList: + post: + consumes: + - application/json + parameters: + - description: 页码, 每页大小, 搜索条件 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.GetUserList' + produces: + - application/json + responses: + "200": + description: 获取用户列表,返回包括列表,总数,页码,每页大小 + schema: + allOf: + - $ref: '#/definitions/response.Response' + - properties: + data: + $ref: '#/definitions/response.PageResult' + msg: + type: string + type: object + security: + - ApiKeyAuth: [] + summary: 获取用户列表 + tags: + - 用户管理 + /user/grantRole: + post: + consumes: + - application/json + parameters: + - description: 用户ID, 角色ID + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.GrantRole' + produces: + - application/json + responses: + "200": + description: '{"code": 200, "data": [...]}' + schema: + $ref: '#/definitions/response.Response' + security: + - ApiKeyAuth: [] + summary: 给用户分配角色 + tags: + - 用户管理 + /user/save: + post: + consumes: + - application/json + parameters: + - description: 用户信息 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.User' + produces: + - application/json + responses: + "200": + description: '{"code": 200, "data": {}, "msg": "添加成功"}' + schema: + $ref: '#/definitions/response.Response' + security: + - ApiKeyAuth: [] + summary: 新增用户 + tags: + - 用户管理 + /user/update: + post: + consumes: + - application/json + parameters: + - description: 用户ID,用户信息 + in: body + name: data + required: true + schema: + $ref: '#/definitions/system.User' + produces: + - application/json + responses: + "200": + description: '{"code": 200, "data": [...]}' + schema: + $ref: '#/definitions/response.Response' + security: + - ApiKeyAuth: [] + summary: 更新用户 + tags: + - 用户管理 +securityDefinitions: + ApiKeyAuth: + in: header + name: Authorization + type: apiKey +swagger: "2.0" diff --git a/global/global.go b/global/global.go new file mode 100644 index 0000000..7b9ec7c --- /dev/null +++ b/global/global.go @@ -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 +) diff --git a/global/model.go b/global/model.go new file mode 100644 index 0000000..27e1fc9 --- /dev/null +++ b/global/model.go @@ -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 +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..5b2675e --- /dev/null +++ b/go.mod @@ -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 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..641ffb9 --- /dev/null +++ b/go.sum @@ -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= diff --git a/initialize/gorm.go b/initialize/gorm.go new file mode 100644 index 0000000..1c1fd03 --- /dev/null +++ b/initialize/gorm.go @@ -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") +} diff --git a/initialize/gorm_mysql.go b/initialize/gorm_mysql.go new file mode 100644 index 0000000..fd2dd58 --- /dev/null +++ b/initialize/gorm_mysql.go @@ -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 + } +} diff --git a/initialize/gorm_pgsql.go b/initialize/gorm_pgsql.go new file mode 100644 index 0000000..809735d --- /dev/null +++ b/initialize/gorm_pgsql.go @@ -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 + } +} diff --git a/initialize/gorm_sqlite.go b/initialize/gorm_sqlite.go new file mode 100644 index 0000000..ac0e467 --- /dev/null +++ b/initialize/gorm_sqlite.go @@ -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 + } +} diff --git a/initialize/internal/gorm.go b/initialize/internal/gorm.go new file mode 100644 index 0000000..247807f --- /dev/null +++ b/initialize/internal/gorm.go @@ -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, + } +} diff --git a/initialize/internal/gorm_logger_writer.go b/initialize/internal/gorm_logger_writer.go new file mode 100644 index 0000000..691e9aa --- /dev/null +++ b/initialize/internal/gorm_logger_writer.go @@ -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 + } +} diff --git a/initialize/redis.go b/initialize/redis.go new file mode 100644 index 0000000..2616e4e --- /dev/null +++ b/initialize/redis.go @@ -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 +} diff --git a/initialize/router.go b/initialize/router.go new file mode 100644 index 0000000..38e8a2e --- /dev/null +++ b/initialize/router.go @@ -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)) + +} diff --git a/initialize/timer.go b/initialize/timer.go new file mode 100644 index 0000000..682bee6 --- /dev/null +++ b/initialize/timer.go @@ -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) +// } +// }() +//} diff --git a/main.go b/main.go new file mode 100644 index 0000000..17798d4 --- /dev/null +++ b/main.go @@ -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() +} diff --git a/middleware/auth.go b/middleware/auth.go new file mode 100644 index 0000000..6bb49f0 --- /dev/null +++ b/middleware/auth.go @@ -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() + } +} diff --git a/middleware/operation.go b/middleware/operation.go new file mode 100644 index 0000000..825a5ee --- /dev/null +++ b/middleware/operation.go @@ -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)) + } + } +} diff --git a/model/commom/request/common.go b/model/commom/request/common.go new file mode 100644 index 0000000..0e98c78 --- /dev/null +++ b/model/commom/request/common.go @@ -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 +} diff --git a/model/commom/response/common.go b/model/commom/response/common.go new file mode 100644 index 0000000..19b618c --- /dev/null +++ b/model/commom/response/common.go @@ -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"` +} diff --git a/model/commom/response/response.go b/model/commom/response/response.go new file mode 100644 index 0000000..503168b --- /dev/null +++ b/model/commom/response/response.go @@ -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, + }) +} diff --git a/model/plant/my_plant.go b/model/plant/my_plant.go new file mode 100644 index 0000000..4a38aaf --- /dev/null +++ b/model/plant/my_plant.go @@ -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 +} diff --git a/model/plant/my_plant_care_plan.go b/model/plant/my_plant_care_plan.go new file mode 100644 index 0000000..df10daa --- /dev/null +++ b/model/plant/my_plant_care_plan.go @@ -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:周期"` +} diff --git a/model/plant/my_plant_care_record.go b/model/plant/my_plant_care_record.go new file mode 100644 index 0000000..ed6379d --- /dev/null +++ b/model/plant/my_plant_care_record.go @@ -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"` +} diff --git a/model/plant/my_plant_care_task.go b/model/plant/my_plant_care_task.go new file mode 100644 index 0000000..147245d --- /dev/null +++ b/model/plant/my_plant_care_task.go @@ -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:已跳过 +} diff --git a/model/plant/request/my_plant.go b/model/plant/request/my_plant.go new file mode 100644 index 0000000..1b5b00c --- /dev/null +++ b/model/plant/request/my_plant.go @@ -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"` +} diff --git a/model/plant/response/badge.go b/model/plant/response/badge.go new file mode 100644 index 0000000..ed513db --- /dev/null +++ b/model/plant/response/badge.go @@ -0,0 +1,6 @@ +package response + +//type BadgeGroup struct { +// CategoryName string `json:"categoryName" comment:"分类名称"` +// BadgeList []plant.Badge `json:"badgeList"` +//} diff --git a/model/plant/response/my_plant.go b/model/plant/response/my_plant.go new file mode 100644 index 0000000..27f885e --- /dev/null +++ b/model/plant/response/my_plant.go @@ -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"` +} diff --git a/model/plant/response/ocr.go b/model/plant/response/ocr.go new file mode 100644 index 0000000..acc7c43 --- /dev/null +++ b/model/plant/response/ocr.go @@ -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"` // 植物识别结果列表 +} diff --git a/model/system/oss.go b/model/system/oss.go new file mode 100644 index 0000000..dc84216 --- /dev/null +++ b/model/system/oss.go @@ -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:图片宽度"` +} diff --git a/model/system/request/captcha.go b/model/system/request/captcha.go new file mode 100644 index 0000000..3da0d01 --- /dev/null +++ b/model/system/request/captcha.go @@ -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 +} diff --git a/model/system/request/jwt.go b/model/system/request/jwt.go new file mode 100644 index 0000000..28c8448 --- /dev/null +++ b/model/system/request/jwt.go @@ -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 +} diff --git a/model/system/request/oss.go b/model/system/request/oss.go new file mode 100644 index 0000000..d0d6573 --- /dev/null +++ b/model/system/request/oss.go @@ -0,0 +1,8 @@ +package request + +import common "sundynix-go/model/commom/request" + +type GetOssFileList struct { + common.PageInfo + Name string `json:"name" form:"name"` +} diff --git a/model/system/request/sys_client.go b/model/system/request/sys_client.go new file mode 100644 index 0000000..2f6d4b9 --- /dev/null +++ b/model/system/request/sys_client.go @@ -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"` +} diff --git a/model/system/request/sys_menu.go b/model/system/request/sys_menu.go new file mode 100644 index 0000000..b204b49 --- /dev/null +++ b/model/system/request/sys_menu.go @@ -0,0 +1,6 @@ +package request + +type GetMenuTree struct { + Category int `json:"category" form:"category"` + ParentId string `json:"parentId" form:"parentId"` +} diff --git a/model/system/request/sys_operation_record.go b/model/system/request/sys_operation_record.go new file mode 100644 index 0000000..a6359c4 --- /dev/null +++ b/model/system/request/sys_operation_record.go @@ -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"` +} diff --git a/model/system/request/sys_role.go b/model/system/request/sys_role.go new file mode 100644 index 0000000..d669b38 --- /dev/null +++ b/model/system/request/sys_role.go @@ -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"` +} diff --git a/model/system/request/sys_user.go b/model/system/request/sys_user.go new file mode 100644 index 0000000..efb5e85 --- /dev/null +++ b/model/system/request/sys_user.go @@ -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"` +} diff --git a/model/system/response/WxCode2SessionResp.go b/model/system/response/WxCode2SessionResp.go new file mode 100644 index 0000000..cacc3f3 --- /dev/null +++ b/model/system/response/WxCode2SessionResp.go @@ -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"` +} diff --git a/model/system/response/oss.go b/model/system/response/oss.go new file mode 100644 index 0000000..56b90e4 --- /dev/null +++ b/model/system/response/oss.go @@ -0,0 +1,7 @@ +package response + +import "sundynix-go/model/system" + +type UploadFileResponse struct { + File system.Oss `json:"file"` +} diff --git a/model/system/response/sys_captcha.go b/model/system/response/sys_captcha.go new file mode 100644 index 0000000..40a1842 --- /dev/null +++ b/model/system/response/sys_captcha.go @@ -0,0 +1,6 @@ +package response + +type CaptchaRes struct { + CaptchaId string `json:"captchaId"` + Captcha string `json:"captcha"` +} diff --git a/model/system/response/sys_user.go b/model/system/response/sys_user.go new file mode 100644 index 0000000..d83fb3d --- /dev/null +++ b/model/system/response/sys_user.go @@ -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"` +} diff --git a/model/system/sys_client.go b/model/system/sys_client.go new file mode 100644 index 0000000..7801e10 --- /dev/null +++ b/model/system/sys_client.go @@ -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"` +} diff --git a/model/system/sys_menu.go b/model/system/sys_menu.go new file mode 100644 index 0000000..2d014d8 --- /dev/null +++ b/model/system/sys_menu.go @@ -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:"-"` +} diff --git a/model/system/sys_operation_record.go b/model/system/sys_operation_record.go new file mode 100644 index 0000000..f979edc --- /dev/null +++ b/model/system/sys_operation_record.go @@ -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"` +} diff --git a/model/system/sys_role.go b/model/system/sys_role.go new file mode 100644 index 0000000..9524930 --- /dev/null +++ b/model/system/sys_role.go @@ -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;"` +} diff --git a/model/system/sys_role_menu.go b/model/system/sys_role_menu.go new file mode 100644 index 0000000..7c9d0e9 --- /dev/null +++ b/model/system/sys_role_menu.go @@ -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"` +} diff --git a/model/system/sys_user.go b/model/system/sys_user.go new file mode 100644 index 0000000..78c5da5 --- /dev/null +++ b/model/system/sys_user.go @@ -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 +} diff --git a/model/system/sys_user_role.go b/model/system/sys_user_role.go new file mode 100644 index 0000000..9ff8b92 --- /dev/null +++ b/model/system/sys_user_role.go @@ -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"` +} diff --git a/pkg/httpclient/http_client.go b/pkg/httpclient/http_client.go new file mode 100644 index 0000000..6fd2eb4 --- /dev/null +++ b/pkg/httpclient/http_client.go @@ -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 +} diff --git a/router/enter.go b/router/enter.go new file mode 100644 index 0000000..c22d323 --- /dev/null +++ b/router/enter.go @@ -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 +} diff --git a/router/plant/enter.go b/router/plant/enter.go new file mode 100644 index 0000000..9b63d75 --- /dev/null +++ b/router/plant/enter.go @@ -0,0 +1,12 @@ +package plant + +import v1 "sundynix-go/api/v1" + +type RouterGroup struct { + MyPlantRouter +} + +// 初始化路由 +var ( + myPlantApi = v1.ApiGroupApp.PlantApiGroup.MyPlantApi +) diff --git a/router/plant/plant_router.go b/router/plant/plant_router.go new file mode 100644 index 0000000..61b6a20 --- /dev/null +++ b/router/plant/plant_router.go @@ -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) // 完成任务 + + } + +} diff --git a/router/system/auth_router.go b/router/system/auth_router.go new file mode 100644 index 0000000..b12da76 --- /dev/null +++ b/router/system/auth_router.go @@ -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) // 获取天气 + } +} diff --git a/router/system/client_router.go b/router/system/client_router.go new file mode 100644 index 0000000..fbbc9b4 --- /dev/null +++ b/router/system/client_router.go @@ -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) + } +} diff --git a/router/system/enter.go b/router/system/enter.go new file mode 100644 index 0000000..ad78fb8 --- /dev/null +++ b/router/system/enter.go @@ -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 +) diff --git a/router/system/menu_router.go b/router/system/menu_router.go new file mode 100644 index 0000000..033f473 --- /dev/null +++ b/router/system/menu_router.go @@ -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) + } +} diff --git a/router/system/operation_record.go b/router/system/operation_record.go new file mode 100644 index 0000000..d752b13 --- /dev/null +++ b/router/system/operation_record.go @@ -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) // 批量删除操作记录 + } +} diff --git a/router/system/oss_router.go b/router/system/oss_router.go new file mode 100644 index 0000000..1408478 --- /dev/null +++ b/router/system/oss_router.go @@ -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) + } +} diff --git a/router/system/role_router.go b/router/system/role_router.go new file mode 100644 index 0000000..2b9ef6f --- /dev/null +++ b/router/system/role_router.go @@ -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) + } +} diff --git a/router/system/user_router.go b/router/system/user_router.go new file mode 100644 index 0000000..c7fc885 --- /dev/null +++ b/router/system/user_router.go @@ -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) + } +} diff --git a/service/enter.go b/service/enter.go new file mode 100644 index 0000000..9fac09b --- /dev/null +++ b/service/enter.go @@ -0,0 +1,13 @@ +package service + +import ( + "sundynix-go/service/plant" + "sundynix-go/service/system" +) + +var GroupApp = new(Group) + +type Group struct { + SystemServiceGroup system.ServiceGroup + PlantServiceGroup plant.ServiceGroup +} diff --git a/service/plant/enter.go b/service/plant/enter.go new file mode 100644 index 0000000..e03779c --- /dev/null +++ b/service/plant/enter.go @@ -0,0 +1,5 @@ +package plant + +type ServiceGroup struct { + MyPlantService +} diff --git a/service/plant/my_plant.go b/service/plant/my_plant.go new file mode 100644 index 0000000..a3d8474 --- /dev/null +++ b/service/plant/my_plant.go @@ -0,0 +1,224 @@ +package plant + +import ( + "sundynix-go/global" + common "sundynix-go/model/commom/request" + "sundynix-go/model/plant" + plantReq "sundynix-go/model/plant/request" + plantRes "sundynix-go/model/plant/response" + "sundynix-go/model/system" + "sundynix-go/utils/timer" + "time" + + "gorm.io/gorm" +) + +type MyPlantService struct{} + +var MyPlantServiceApp = new(MyPlantService) + +// AddPlant 添加植物 +func (s *MyPlantService) AddPlant(req plantReq.CreateMyPlant, id string) error { + err := global.DB.Transaction(func(tx *gorm.DB) error { + //1. 验证oss是否存在 + var ossList []*system.Oss + err := tx.Where("id in ?", req.OssIds).Find(&ossList).Error + if err != nil { + return err + } + bgTime := timer.GetZeroTime() + //2. 处理养护计划 + var carePlans []*plant.CarePlan + for _, v := range req.CarePlans { + carePlans = append(carePlans, &plant.CarePlan{ + UserId: id, + Name: v.Name, + Icon: v.Icon, + Period: v.Period, + }) + } + //3.保存数据 + myPlant := plant.MyPlant{ + UserId: id, + Name: req.Name, + PlantTime: bgTime, + Status: 1, + Placement: req.Placement, + PotMaterial: req.PotMaterial, + PotSize: req.PotSize, + Sunlight: req.Sunlight, + PlantingMaterial: req.PlantingMaterial, + CarePlans: carePlans, + } + err = tx.Create(&myPlant).Error + if err != nil { + return err + } + + //4.处理图片关系 + if len(ossList) > 0 { + var relations []map[string]interface{} + for _, oss := range ossList { + relations = append(relations, map[string]interface{}{ + "my_plant_id": myPlant.Id, + "oss_id": oss.Id, + }) + } + err = tx.Table("sundynix_my_plant_oss").Create(relations).Error + if err != nil { + return err + } + } + return nil + }) + return err + +} + +// PlantPage 植物列表 +func (s *MyPlantService) PlantPage(req common.PageInfo, userId string) (list interface{}, total int64, err error) { + limit := req.PageSize + offset := req.PageSize * (req.Current - 1) + db := global.DB.Model(&plant.MyPlant{}).Preload("ImgList", func(db *gorm.DB) *gorm.DB { + return db.Order("created_at desc") + }) + var myPlants []*plant.MyPlant + db = db.Where("user_id = ?", userId) + err = db.Count(&total).Error + if err != nil { + return + } + err = db.Limit(limit).Offset(offset).Order("created_at desc").Find(&myPlants).Error + return myPlants, total, err +} + +// PlantDetail 植物详情 +func (s *MyPlantService) PlantDetail(id string) (p plant.MyPlant, err error) { + var res plant.MyPlant + err = global.DB.Where("id = ?", id). + Preload("ImgList", func(db *gorm.DB) *gorm.DB { + return db.Order("created_at desc") + }). + Preload("CarePlans"). + Preload("CareRecords"). + First(&res).Error + + //不存在的时候不要返回错误,而是返回nil + if err != nil { + return res, err + } + return res, nil +} + +func (s *MyPlantService) UpdatePlant(req plantReq.UpdateMyPlant) error { + // 以map形式更新 先构建map + updateMap := map[string]interface{}{ + "name": req.Name, + "placement": req.Placement, + "planting_material": req.PlantingMaterial, + "pot_material": req.PotMaterial, + "pot_size": req.PotSize, + "sunlight": req.Sunlight, + } + return global.DB.Model(&plant.MyPlant{}).Where("id = ?", req.Id).Updates(updateMap).Error +} + +// TodayTask 今日任务 +func (s *MyPlantService) TodayTask(userId string) ([]plantRes.PlantTaskVO, error) { + today := timer.GetZeroTime() + endOfToday := today.Add(24 * time.Hour) + + var tasks []*plant.CareTask + // 查询条件:1.未完成的任务(包含今天和以前逾期的) 2.今天已经完成的任务 + err := global.DB.Where("user_id = ? AND ("+ + "(status = 1 AND due_date < ?) OR "+ + "(status = 2 AND completed_at >= ? AND completed_at < ?)"+ + ")", userId, endOfToday, today, endOfToday). + Order("due_date ASC"). + Find(&tasks).Error + if err != nil { + return nil, err + } + // 2.内存处理:聚合数据 + plantTaskMap := make(map[string][]*plant.CareTask) + expiredMap := make(map[string]bool) + var plantIds []string + for _, t := range tasks { + plantIds = append(plantIds, t.PlantId) + plantTaskMap[t.PlantId] = append(plantTaskMap[t.PlantId], t) + // 判定右上角红标:状态为待办 且 应做时间早于今天 + if t.Status == 1 && t.DueDate.Before(today) { + expiredMap[t.PlantId] = true + } + } + //3.批量查询植物 + var myPlants []*plant.MyPlant + err = global.DB.Where("id in ?", plantIds).Preload("ImgList", func(db *gorm.DB) *gorm.DB { + return db.Order("created_at desc") + }).Find(&myPlants).Error + if err != nil { + return nil, err + } + //4.组装结果 + var res []plantRes.PlantTaskVO + for _, p := range myPlants { + plantTaskVO := plantRes.PlantTaskVO{ + MyPlant: p, + HasExpired: expiredMap[p.Id], + Tasks: plantTaskMap[p.Id], + } + res = append(res, plantTaskVO) + } + return res, nil +} + +// CompleteTask 完成任务 +func (s *MyPlantService) CompleteTask(req plantReq.CompleteTask) error { + return global.DB.Transaction(func(tx *gorm.DB) error { + var task plant.CareTask + if err := tx.Where("id = ?", req.TaskId).First(&task).Error; err != nil { + return err + } + //1.更新当前任务为完成 + updateData := map[string]interface{}{ + "status": 2, + "completed_at": time.Now(), + } + if err := tx.Model(&task).Where("id = ?", req.TaskId).Updates(updateData).Error; err != nil { + return err + } + //2.获取计划模版 + var plan plant.CarePlan + if err := tx.Where("id = ?", task.PlanId).First(&plan).Error; err != nil { + return err + } + //3.生成下个周期的任务 + today := timer.GetZeroTime() + nextDueDate := today.AddDate(0, 0, plan.Period) + newTask := plant.CareTask{ + UserId: plan.UserId, + PlantId: plan.PlantId, + PlanId: plan.Id, + Name: plan.Name, + Icon: plan.Icon, + DueDate: nextDueDate, + Status: 1, + } + if err := tx.Create(&newTask).Error; err != nil { + return err + } + //4.保存养护记录 + record := plant.CareRecord{ + UserId: plan.UserId, + PlantId: plan.PlantId, + PlanId: plan.Id, + Name: plan.Name, + Icon: plan.Icon, + Remark: req.Remark, + } + if err := tx.Create(&record).Error; err != nil { + return err + } + return nil + }) +} diff --git a/service/plant/pay.go b/service/plant/pay.go new file mode 100644 index 0000000..65e0234 --- /dev/null +++ b/service/plant/pay.go @@ -0,0 +1,206 @@ +package plant + +// +//import ( +// "context" +// "errors" +// "net/http" +// "sundynix-go/global" +// "sundynix-go/model/plant" +// "sundynix-go/model/system" +// "sundynix-go/utils/wechat" +// +// "github.com/gin-gonic/gin" +// "github.com/wechatpay-apiv3/wechatpay-go/core" +// "github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers" +// "github.com/wechatpay-apiv3/wechatpay-go/core/notify" +// "github.com/wechatpay-apiv3/wechatpay-go/services/payments" +// "github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi" +// "github.com/wechatpay-apiv3/wechatpay-go/utils" +// "go.uber.org/zap" +// "gorm.io/gorm" +//) +// +//type PayService struct{} +// +//var PayServiceApp = new(PayService) +// +//// PrePay 预支付 +//func (s *PayService) PrePay(orderId, userId string) (resp *jsapi.PrepayWithRequestPaymentResponse, err error) { +// //1.查询订单和 用户 +// var order plant.Order +// err = global.DB.Where("id = ?", orderId).First(&order).Error +// if err != nil { +// return nil, err +// } +// var user system.User +// err = global.DB.Where("id = ?", userId).First(&user).Error +// if err != nil { +// return nil, err +// } +// payClient, err := wechat.GetWxPayClient() +// if err != nil { +// return nil, err +// } +// +// svc := jsapi.JsapiApiService{Client: payClient} +// result, _, err := svc.PrepayWithRequestPayment(context.Background(), +// jsapi.PrepayRequest{ +// Appid: core.String(global.Config.MiniProgram.AppId), +// Mchid: core.String(global.Config.WechatPay.MchId), +// Description: core.String(order.Name), +// OutTradeNo: core.String(order.OutTradeNo), +// //TimeExpire: core.Time(time.Now()), //选填 +// //Attach: core.String("自定义数据说明"), //选填 +// NotifyUrl: core.String(global.Config.WechatPay.NotifyUrl), +// //GoodsTag: core.String("WXG"), //选填 +// //SupportFapiao: core.Bool(false), //选填 +// Amount: &jsapi.Amount{ +// Currency: core.String("CNY"), +// Total: core.Int64(int64(order.Amount)), +// }, +// Payer: &jsapi.Payer{ +// Openid: core.String(user.MiniOpenId), +// }, +// //Detail: &jsapi.Detail{ +// // CostPrice: core.Int64(608800), +// // GoodsDetail: []jsapi.GoodsDetail{jsapi.GoodsDetail{ +// // GoodsName: core.String("iPhoneX 256G"), +// // MerchantGoodsId: core.String("ABC"), +// // Quantity: core.Int64(1), +// // UnitPrice: core.Int64(828800), +// // WechatpayGoodsId: core.String("1001"), +// // }}, +// // InvoiceId: core.String("wx123"), +// //}, //选填 +// //SceneInfo: &jsapi.SceneInfo{ +// // DeviceId: core.String("013467007045764"), +// // PayerClientIp: core.String("14.23.150.211"), +// // StoreInfo: &jsapi.StoreInfo{ +// // Address: core.String("广东省深圳市南山区科技中一道10000号"), +// // AreaCode: core.String("440305"), +// // Id: core.String("0001"), +// // Name: core.String("腾讯大厦分店"), +// // }, +// //}, +// //SettleInfo: &jsapi.SettleInfo{ +// // ProfitSharing: core.Bool(false), +// //}, //选填 +// }) +// if err != nil { +// return nil, err +// } +// +// return result, nil +// +//} +// +//// PayCallback 支付回调 +//func (s *PayService) PayCallback(c *gin.Context) error { +// //1.加载共钥 +// mchPublicKeyPath := global.Config.WechatPay.PublicKeyPath +// mchPublicKey, err := utils.LoadPublicKeyWithPath(mchPublicKeyPath) +// if err != nil { +// return err +// } +// ctx := context.Background() +// //2.创建客户端 +// handler, err := notify.NewRSANotifyHandler(global.Config.WechatPay.MchAPIv3Key, +// verifiers.NewSHA256WithRSAPubkeyVerifier(global.Config.WechatPay.PublicKeyId, *mchPublicKey)) +// if err != nil { +// return err +// } +// //3.验签 解密 +// //将支付回调通知中的内容,解析为 payments.Transaction。 +// transaction := new(payments.Transaction) +// notifyReq, err := handler.ParseNotifyRequest(ctx, c.Request, transaction) +// // 4.如果验签未通过,或者解密失败 +// if err != nil { +// //应答微信 +// c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{ +// "code": "FAIL", +// "message": "失败", +// }) +// global.Logger.Error("wxPay回调-验签或解密:", zap.Error(err)) +// return err +// } +// //5.应答微信 +// c.Status(http.StatusOK) +// // 6.处理通知内容 +// global.Logger.Info("wxPay回调-成功:", zap.Any("notifyReq", notifyReq.Summary)) +// //7. 异步处理数据 +// go func() { +// payNotify := plant.PayNotify{ +// Amount: *transaction.Amount.Total, +// Currency: *transaction.Amount.Currency, +// PayerCurrency: *transaction.Amount.PayerCurrency, +// PayerTotal: *transaction.Amount.PayerTotal, +// Appid: *transaction.Appid, +// Mchid: *transaction.Mchid, +// OutTradeNo: *transaction.OutTradeNo, +// Attach: *transaction.Attach, +// BankType: *transaction.BankType, +// Payer: *transaction.Payer.Openid, +// SuccessTime: *transaction.SuccessTime, +// TradeState: *transaction.TradeState, +// TradeStateDesc: *transaction.TradeStateDesc, +// TradeType: *transaction.TradeType, +// TransactionId: *transaction.TransactionId, +// } +// //7.1 回调记录 +// err = global.DB.Create(&payNotify).Error +// if err != nil { +// global.Logger.Error("wxPay回调-存储数据异常:", zap.Error(err)) +// } +// if payNotify.TradeState == "SUCCESS" { +// //7.2 扣商品库存 +// var order plant.Order +// err = global.DB.Where("out_trade_no = ?", *transaction.OutTradeNo).First(&order).Error +// if err != nil { +// if errors.Is(err, gorm.ErrRecordNotFound) { +// global.Logger.Error("wxPay回调-订单不存在:", zap.Error(err)) +// } +// } +// //根据订单找到领取记录 +// var record plant.ClaimPlantRecord +// err = global.DB.Where("order_id = ?", order.Id).First(&record).Error +// if err != nil { +// if errors.Is(err, gorm.ErrRecordNotFound) { +// global.Logger.Error("wxPay回调-领取记录不存在:", zap.Error(err)) +// } +// } +// //根据领取记录找到植物 +// var claimPlant plant.ClaimPlant +// err = global.DB.Where("id = ?", record.ClaimPlantId).First(&claimPlant).Error +// if err != nil { +// if errors.Is(err, gorm.ErrRecordNotFound) { +// global.Logger.Error("wxPay回调-植物不存在:", zap.Error(err)) +// } +// } +// err = global.DB.Transaction(func(tx *gorm.DB) error { +// //扣库存 +// if err = tx.Model(&claimPlant).Update("stock", gorm.Expr("stock - ?", 1)).Error; err != nil { +// return err +// } +// //订单状态 +// if err = tx.Model(&order).Update("pay_status", "SUCCESS").Error; err != nil { +// return err +// } +// //修改领取记录为成功 +// if err = tx.Model(&record).Update("status", 1).Error; err != nil { +// return err +// } +// //减用户积分 +// if err = tx.Model(&plant.Personal{}).Where("user_id = ?", order.UserId).Update("points_count", gorm.Expr("points_count - ?", claimPlant.Points)).Error; err != nil { +// return err +// } +// return nil +// }) +// } +// if err != nil { +// return +// } +// }() +// return nil +// +//} diff --git a/service/system/enter.go b/service/system/enter.go new file mode 100644 index 0000000..f9f4f48 --- /dev/null +++ b/service/system/enter.go @@ -0,0 +1,11 @@ +package system + +type ServiceGroup struct { + JwtService + UserService + ClientService + RoleService + MenuService + OperationRecordService + OssService +} diff --git a/service/system/oss.go b/service/system/oss.go new file mode 100644 index 0000000..87258f5 --- /dev/null +++ b/service/system/oss.go @@ -0,0 +1,135 @@ +package system + +import ( + "crypto/md5" + "errors" + "fmt" + "image" + _ "image/jpeg" + _ "image/png" + "io" + "mime/multipart" + "strings" + "sundynix-go/global" + common "sundynix-go/model/commom/request" + "sundynix-go/model/system" + sysReq "sundynix-go/model/system/request" + "sundynix-go/utils/upload" + + "go.uber.org/zap" + "gorm.io/gorm" +) + +type OssService struct { +} + +var OssServiceApp = new(OssService) + +func (o *OssService) Save(file system.Oss) error { + return global.DB.Create(&file).Error +} + +func (o *OssService) Upload(multipartFile multipart.File, header *multipart.FileHeader) (file system.Oss, err error) { + //1.检查是否已有此文件 + temp, err := header.Open() + if err != nil { + return file, err + } + defer temp.Close() + + hasher := md5.New() + if _, copyErr := io.Copy(hasher, temp); copyErr != nil { + return file, copyErr + } + // 步骤3: 计算哈希值并转换为十六进制字符串 + hashBytes := hasher.Sum(nil) + hashString := fmt.Sprintf("%x", hashBytes) + var exist system.Oss + findErr := global.DB.Where("md5 = ?", hashString).First(&exist).Error + if findErr == nil && exist.Id != "" { + return exist, nil + } + if errors.Is(findErr, gorm.ErrRecordNotFound) { + //不存在的时候保存 + instance := upload.OssInstance() + filepath, key, uploadErr := instance.UploadFile(header) + if uploadErr != nil { + return file, uploadErr + } + //文件后缀 + s := strings.Split(header.Filename, ".") + height := 0 + width := 0 + //mime类型 + contentType := header.Header.Get("Content-Type") + allowedImageTypes := map[string]bool{ + "image/jpeg": true, + "image/png": true, + } + isPossibleImage := allowedImageTypes[contentType] + //仅当可能是图片时 才计算图片宽高 + if isPossibleImage { + img, _, err1 := image.Decode(multipartFile) + if err1 != nil { + return file, err + } + height = img.Bounds().Max.Y + width = img.Bounds().Max.X + } + f := system.Oss{ + Key: key, // uploads/2025-09-17/ + Name: header.Filename, + Suffix: s[len(s)-1], + Tag: s[len(s)-1], + Url: filepath, // http://127.0.0.1:9000/planting-fun/uploads/2025-09-17/211476f3837fc7acbaebf0f901c1bd68.png + MD5: hashString, + Height: height, + Width: width, + } + return f, global.DB.Create(&f).Error + } + return file, err +} + +func (o *OssService) DeleteFileByIds(ids common.IdsReq) error { + //循环删除 + instance := upload.OssInstance() + for _, id := range ids.Ids { + file, err := o.GetById(id) + if err != nil { + return err + } + if err = instance.DeleteFile(file.Key); err != nil { + global.Logger.Error("删除文件失败!", zap.Error(err)) + return err + } + } + err := global.DB.Where("id IN (?)", ids.Ids).Delete(&system.Oss{}).Error + return err +} + +func (o *OssService) GetById(id string) (system.Oss, error) { + var file system.Oss + err := global.DB.Where("id = ?", id).First(&file).Error + //不存在的时候不要返回错误,而是返回nil + if err != nil { + return file, nil + } + return file, err +} + +func (o *OssService) GetFileList(info sysReq.GetOssFileList) (list interface{}, total int64, err error) { + limit := info.PageSize + offset := info.PageSize * (info.Current - 1) + db := global.DB.Model(&system.Oss{}) + var files []system.Oss + if info.Name != "" { + db = db.Where("name LIKE ?", "%"+info.Name+"%") + } + err = db.Count(&total).Error + if err != nil { + return + } + err = db.Limit(limit).Offset(offset).Order("created_at desc").Find(&files).Error + return files, total, err +} diff --git a/service/system/sys_client.go b/service/system/sys_client.go new file mode 100644 index 0000000..58bd663 --- /dev/null +++ b/service/system/sys_client.go @@ -0,0 +1,61 @@ +package system + +import ( + "errors" + "sundynix-go/global" + common "sundynix-go/model/commom/request" + "sundynix-go/model/system" + systemReq "sundynix-go/model/system/request" + + "gorm.io/gorm" +) + +type ClientService struct{} + +var ClientServiceApp = new(ClientService) + +func (s *ClientService) SaveClient(client system.Client) error { + if !errors.Is(global.DB.Where("client_id = ?", client.ClientId).First(&system.Client{}).Error, gorm.ErrRecordNotFound) { + return errors.New("存在重复clientId,请修改clientId") + } + return global.DB.Create(&client).Error +} + +func (s *ClientService) UpdateClient(client system.Client) error { + return global.DB.Model(&client).Where("id = ?", client.Id).Updates(&client).Error +} + +func (s *ClientService) GetClientList(info systemReq.GetClientList) (list interface{}, total int64, err error) { + limit := info.PageSize + offset := info.PageSize * (info.Current - 1) + db := global.DB.Model(&system.Client{}) + var clientList []system.Client + if info.ClientId != "" { + db = db.Where("client_id = ?", info.ClientId) + } + if info.Name != "" { + db = db.Where("name LIKE ?", "%"+info.Name+"%") + } + err = db.Count(&total).Error + if err != nil { + return + } + err = db.Limit(limit).Offset(offset).Find(&clientList).Error + return clientList, total, err +} + +func (s *ClientService) DeleteClientByIds(ids common.IdsReq) (err error) { + return global.DB.Where("id IN (?)", ids.Ids).Delete(&system.Client{}).Error +} + +func (s *ClientService) GetClientById(id string) (client system.Client, err error) { + var c system.Client + err = global.DB.Where("id = ?", id).First(&c).Error + return c, err +} + +func (s *ClientService) GetClientByClientId(clientId string) (client *system.Client, err error) { + var c system.Client + err = global.DB.Where("client_id = ?", clientId).First(&c).Error + return &c, err +} diff --git a/service/system/sys_jwt.go b/service/system/sys_jwt.go new file mode 100644 index 0000000..572340a --- /dev/null +++ b/service/system/sys_jwt.go @@ -0,0 +1,26 @@ +package system + +import ( + "context" + "sundynix-go/global" + "sundynix-go/utils" +) + +type JwtService struct{} + +var JwtServiceApp = new(JwtService) + +// 登出,禁用jwt +func (s *JwtService) PutBlacklist(userId string, token string) (err error) { + expire, err := utils.ParseDuration(global.Config.JWT.ExpiresTime) + if err != nil { + return err + } + err = global.Redis.Set(context.Background(), userId, token, expire).Err() + return err +} + +func (s *JwtService) IsInBlacklist(userId string, token string) bool { + val, err := global.Redis.Get(context.Background(), userId).Result() + return err == nil && val == token +} diff --git a/service/system/sys_menu.go b/service/system/sys_menu.go new file mode 100644 index 0000000..4b833ca --- /dev/null +++ b/service/system/sys_menu.go @@ -0,0 +1,126 @@ +package system + +import ( + "errors" + "sundynix-go/global" + "sundynix-go/model/system" + + "gorm.io/gorm" +) + +type MenuService struct{} + +var MenuServiceApp = new(MenuService) + +func (s *MenuService) SaveMenu(menu system.Menu) error { + //1.根据code和name查询是否存在重名 + if err := global.DB.Where("code = ? or name = ?", menu.Code, menu.Name).First(&system.Menu{}).Error; err == nil { + return errors.New("菜单已存在") + } + return global.DB.Create(&menu).Error +} + +func (s *MenuService) UpdateMenu(menu *system.Menu) (err error) { + var sysMenu system.Menu + menuMap := map[string]interface{}{ + "Category": menu.Category, + "Name": menu.Name, + "Title": menu.Title, + "Code": menu.Code, + "Permission": menu.Permission, + "Locale": menu.Locale, + "Icon": menu.Icon, + "Sort": menu.Sort, + } + err = global.DB.Where("id = ?", menu.Id).First(&sysMenu).Error + if err != nil { + global.Logger.Debug(err.Error()) + return errors.New("查询菜单失败") + } + err = global.DB.Model(&sysMenu).Updates(menuMap).Error + return err +} + +func (s *MenuService) DeleteMenu(id string) (err error) { + err = global.DB.First(&system.Menu{}, "parent_id = ?", id).Error + if err == nil { + return errors.New("请先删除子菜单") + } + var menu system.Menu + err = global.DB.Where("id = ?", id).First(&menu).Error + if err != nil { + return errors.New("菜单记录不存在") + } + // 同步删除menu表和role-menu表数据 + return global.DB.Transaction(func(tx *gorm.DB) error { + if err = tx.Where("id = ?", id).Delete(&system.Menu{}).Error; err != nil { + return err + } + if err = tx.Where("menu_id = ?", id).Delete(&system.RoleMenu{}).Error; err != nil { + return err + } + return nil + }) +} + +func (s *MenuService) GetMenuById(id string) (menu system.Menu, err error) { + var m system.Menu + err = global.DB.Where("id = ?", id).First(&m).Error + return m, err +} + +func (s *MenuService) GetAllMenuTree(category int, parentId string) (menus []*system.Menu, err error) { + //1,先根据category和parentId获取所有菜单 category默认为0,parentId默认为0 + //2.讲查询出的列表构建为树结构 + var menuList []*system.Menu + db := global.DB.Model(&system.Menu{}) + if category != 0 { + db.Where("category = ?", category) + } + if parentId != "0" { + db.Where("parent_id = ?", parentId) + } + err = db.Order("sort asc").Find(&menuList).Error + if err != nil { + return nil, err + } + tree := buildMenuTree(menuList) + return tree, nil +} + +func (s *MenuService) GetUserRoutes(userId string) (menus []*system.Menu, err error) { + //1.根据userId 查询角色 根据角色查询菜单 去重 + //2.构建树结构 + var roleIds []string + err = global.DB.Model(&system.UserRole{}).Where("user_id = ?", userId).Pluck("role_id", &roleIds).Error + var menuIds []string + err = global.DB.Model(&system.RoleMenu{}).Where("role_id in ?", roleIds).Pluck("menu_id", &menuIds).Error + var menuList []*system.Menu + err = global.DB.Model(&system.Menu{}).Where("id in ?", menuIds).Order("sort asc").Find(&menuList).Error + return buildMenuTree(menuList), nil +} + +func buildMenuTree(list []*system.Menu) []*system.Menu { + //1.定义一个map + menuMap := make(map[string]*system.Menu) + for _, item := range list { + menuMap[item.Id] = item + } + //构建树结构 + var treeList []*system.Menu + for _, item := range list { + if item.ParentId == "0" { + // 如果没有父节点,直接添加到树中 + treeList = append(treeList, item) + } else { + if parent, exists := menuMap[item.ParentId]; exists { + // 如果有父节点,将当前节点添加到父节点的Children中 + parent.Children = append(parent.Children, item) + } else { + // 如果没有父节点,将当前节点添加到树中 + treeList = append(treeList, item) + } + } + } + return treeList +} diff --git a/service/system/sys_operation_record.go b/service/system/sys_operation_record.go new file mode 100644 index 0000000..c548dfb --- /dev/null +++ b/service/system/sys_operation_record.go @@ -0,0 +1,57 @@ +package system + +import ( + "sundynix-go/global" + common "sundynix-go/model/commom/request" + "sundynix-go/model/system" + systemReq "sundynix-go/model/system/request" +) + +type OperationRecordService struct{} + +var OperationRecordServiceApp = new(OperationRecordService) + +func (o *OperationRecordService) CreateOperationRecord(operationRecord system.SysOperationRecord) (err error) { + return global.DB.Create(&operationRecord).Error +} + +func (o *OperationRecordService) GetRecordList(info systemReq.GetOperationRecordList) (list interface{}, total int64, err error) { + limit := info.PageSize + offset := info.PageSize * (info.Current - 1) + db := global.DB.Model(&system.SysOperationRecord{}) + var operationRecordList []system.SysOperationRecord + + if info.Ip != "" { + db = db.Where("ip = ?", info.Method) + } + if info.Method != "" { + db = db.Where("method = ?", info.Method) + } + if info.Path != "" { + db = db.Where("path = ?", info.Path) + } + if info.UserId != "" { + db = db.Where("status = ?", info.UserId) + } + if info.Status != 0 { + db = db.Where("status = ?", info.Status) + } + err = db.Count(&total).Error + if err != nil { + return + } + err = db.Limit(limit).Offset(offset).Find(&operationRecordList).Error + return operationRecordList, total, err +} + +func (o *OperationRecordService) GetRecordById(id string) (record system.SysOperationRecord, err error) { + var r system.SysOperationRecord + err = global.DB.Where("id = ?", id).First(&r).Error + return r, err +} + +func (o *OperationRecordService) DeleteRecordsByIds(ids common.IdsReq) (err error) { + // Unscoped()禁用软删除 --> 永久物理删除 + err = global.DB.Where("id in ?", ids.Ids).Unscoped().Delete(&system.SysOperationRecord{}).Error + return err +} diff --git a/service/system/sys_role.go b/service/system/sys_role.go new file mode 100644 index 0000000..479a212 --- /dev/null +++ b/service/system/sys_role.go @@ -0,0 +1,89 @@ +package system + +import ( + "errors" + "sundynix-go/global" + common "sundynix-go/model/commom/request" + "sundynix-go/model/system" + systemReq "sundynix-go/model/system/request" + + "gorm.io/gorm" +) + +type RoleService struct { +} + +var RoleServiceApp = new(RoleService) + +func (s *RoleService) SaveRole(role system.Role) error { + if !errors.Is(global.DB.Where("code = ?", role.Code).First(&system.Role{}).Error, gorm.ErrRecordNotFound) { + return errors.New("存在重复角色") + } + return global.DB.Create(&role).Error +} + +func (s *RoleService) UpdateRole(role system.Role) error { + return global.DB.Model(&role).Where("id = ?", role.Id).Updates(&role).Error +} + +func (s *RoleService) GetRoleList(info systemReq.GetRoleList) (list interface{}, total int64, err error) { + limit := info.PageSize + offset := info.PageSize * (info.Current - 1) + db := global.DB.Model(&system.Role{}) + var roleList []system.Role + if info.Code != "" { + db = db.Where("code = ?", info.Code) + } + if info.Name != "" { + db = db.Where("name LIKE ?", "%"+info.Name+"%") + } + err = db.Count(&total).Error + if err != nil { + return + } + err = db.Limit(limit).Offset(offset).Find(&roleList).Error + return roleList, total, err +} + +func (s *RoleService) DeleteRoleByIds(ids common.IdsReq) error { + return global.DB.Where("id in ?", ids.Ids).Delete(&system.Role{}).Error +} + +func (s *RoleService) GetRoleById(id string) (role system.Role, err error) { + var r system.Role + err = global.DB.Where("id = ?", id).First(&r).Error + return r, err +} + +func (s *RoleService) GrantRole(userId string, roleIds []string) error { + //1. 检查是否存在userid的授权记录 存在就删除 不存在就插入 + //2. 插入新的数据 + return global.DB.Transaction(func(tx *gorm.DB) error { + if err := tx.Where("user_id = ?", userId).Delete(&system.UserRole{}).Error; err != nil { + return err + } + for _, roleId := range roleIds { + if err := tx.Create(&system.UserRole{UserId: userId, RoleId: roleId}).Error; err != nil { + return err + } + } + return nil + }) + +} + +func (s *RoleService) GrantMenu(roleId string, menuIds []string) error { + //1. 检查是否存在userid的授权记录 存在就删除 不存在就插入 + //2. 插入新的数据 + return global.DB.Transaction(func(tx *gorm.DB) error { + if err := tx.Where("role_id = ?", roleId).Delete(&system.RoleMenu{}).Error; err != nil { + return err + } + for _, menuId := range menuIds { + if err := tx.Create(&system.RoleMenu{RoleId: roleId, MenuId: menuId}).Error; err != nil { + return err + } + } + return nil + }) +} diff --git a/service/system/sys_user.go b/service/system/sys_user.go new file mode 100644 index 0000000..3d9fe93 --- /dev/null +++ b/service/system/sys_user.go @@ -0,0 +1,269 @@ +package system + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "log" + "net/http" + url2 "net/url" + "strconv" + "sundynix-go/global" + common "sundynix-go/model/commom/request" + "sundynix-go/model/system" + systemReq "sundynix-go/model/system/request" + systemResp "sundynix-go/model/system/response" + "sundynix-go/pkg/httpclient" + "sundynix-go/utils" + location "sundynix-go/utils/location" + "sundynix-go/utils/uniqueid" + "sundynix-go/utils/wechat" + + "go.uber.org/zap" + "gorm.io/gorm" +) + +type UserService struct{} + +var UserServiceApp = new(UserService) + +func (userService *UserService) Login(u *system.User) (userInfo *system.User, err error) { + var user system.User + // 查询出用户信息的同时查询出角色信息 + err = global.DB.Model(&system.User{}).Where("account = ?", u.Account).First(&user).Error + if err == nil { + if ok := utils.BcryptCheck(u.Password, user.Password); !ok { + return nil, errors.New("密码错误") + } + } + return &user, err +} + +func (userService *UserService) SaveUser(user system.User) error { + if !errors.Is(global.DB.Where("account = ?", user.Account).First(&system.User{}).Error, gorm.ErrRecordNotFound) { + return errors.New("存在重复Account,请修改Account") + } + user.Password = utils.BcryptHash(user.Password) + return global.DB.Create(&user).Error +} + +func (userService *UserService) UpdateUser(user *system.User) (err error) { + var sysUser system.User + userMap := map[string]interface{}{ + "account": user.Account, + "phone": user.Phone, + "name": user.Name, + "avatar_id": user.AvatarId, + } + err = global.DB.Where("id = ?", user.Id).First(&sysUser).Error + if err != nil { + global.Logger.Debug(err.Error()) + return errors.New("查询用户失败") + } + err = global.DB.Model(&sysUser).Updates(userMap).Error + return err +} + +func (userService *UserService) GetUserList(info systemReq.GetUserList) (list interface{}, total int64, err error) { + limit := info.PageSize + offset := info.PageSize * (info.Current - 1) + db := global.DB.Model(&system.User{}) + var userList []system.User + + if info.Account != "" { + db = db.Where("account LIKE ?", "%"+info.Account+"%") + } + if info.Phone != "" { + db = db.Where("phone LIKE ?", "%"+info.Phone+"%") + } + err = db.Count(&total).Error + if err != nil { + return + } + err = db.Limit(limit).Offset(offset).Find(&userList).Error + return userList, total, err +} + +func (userService *UserService) DeleteUserByIds(ids common.IdsReq) error { + return global.DB.Where("id IN (?)", ids.Ids).Delete(&system.User{}).Error +} + +func (userService *UserService) GetUserById(id string) (user *system.User, err error) { + var u system.User + err = global.DB.Where("id = ?", id).Preload("Avatar").First(&u).Error + return &u, err +} + +func (userService *UserService) ChangePassword(id string, pwd string) (err error) { + return global.DB.Model(&system.User{}).Where("id = ?", id).Update("password", utils.BcryptHash(pwd)).Error +} + +func (userService *UserService) MiniLogin(code string) (result *system.User, err error) { + //构建参数 + params := url2.Values{} + params.Set("appid", global.Config.MiniProgram.AppId) + params.Set("secret", global.Config.MiniProgram.AppSecret) + params.Set("js_code", code) + params.Set("grant_type", "authorization_code") + fullURL := "https://api.weixin.qq.com/sns/jscode2session?" + params.Encode() + + //1. 获取全局 HTTP Client(复用连接池) + myHttpClient := httpclient.GetClient() + //2.发起请求 + resp, err := myHttpClient.Get(fullURL) + if err != nil { + global.Logger.Error("微信登录接口请求失败", zap.Error(err)) + return nil, fmt.Errorf("微信登录接口请求失败: %w", err) + } + defer resp.Body.Close() + //3.读取响应 + body, err := io.ReadAll(resp.Body) + if err != nil { + global.Logger.Error("读取微信接口响应失败", zap.Error(err)) + return nil, fmt.Errorf("读取微信登录接口响应失败: %w", err) + } + // 4. 解析JSON(用结构体替代map,提升效率+类型安全) + var wxResp systemResp.WxCode2SessionResp + if err = json.Unmarshal(body, &wxResp); err != nil { + global.Logger.Error("解析微信接口响应失败", zap.Error(err)) + return nil, fmt.Errorf("解析微信登录接口响应失败: %w", err) + } + // 5. 检查微信接口错误码(关键:原代码未处理errcode,导致无法定位真实错误) + if wxResp.Errcode != 0 { + errMsg := fmt.Sprintf("微信接口返回错误: errcode=%d, errmsg=%s", wxResp.Errcode, wxResp.Errmsg) + global.Logger.Error(errMsg) + return nil, errors.New(errMsg) + } + // 6. 校验openid(空值直接返回) + if wxResp.Openid == "" { + global.Logger.Error("微信接口返回openid为空") + return nil, errors.New("openid为空") + } + + // 7. 根据openid查询用户 存在--> 更新session_key 返回数据 + var user system.User + err = global.DB.Where("mini_open_id = ?", wxResp.Openid).Preload("Avatar").First(&user).Error + if errors.Is(err, gorm.ErrRecordNotFound) { + // 8. 使用 Transaction 闭包管理事务 + err = global.DB.Transaction(func(tx *gorm.DB) error { + // 创建新用户 + newUser := system.User{ + Name: uniqueid.GenerateName(), + MiniOpenId: wxResp.Openid, + SessionKey: wxResp.SessionKey, + } + if err := tx.Create(&newUser).Error; err != nil { + return err + } + //// 归一化到当天的0点(本地时区) + //now := time.Now() + //today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local) + //personal := plant.Personal{ + // UserId: newUser.Id, + // JoinDate: today, + // PlantCount: 0, + // CareCount: 0, + // BadgeCount: 0, + //} + //if err := tx.Create(&personal).Error; err != nil { + // return err + //} + // 赋值给外部变量以便返回 + user = newUser + return nil + }) + if err != nil { + global.Logger.Error("创建用户失败", zap.Error(err)) + return nil, fmt.Errorf("登录失败: %w", err) + } + return &user, nil + } + if err == nil && user.Id != "" { + // UpdateColumn:只更新字段,不触发模型钩子,比Update更高效 + if err = global.DB.Model(&user).UpdateColumn("session_key", wxResp.SessionKey).Error; err != nil { + global.Logger.Error("更新session_key失败", zap.Error(err)) + return nil, fmt.Errorf("更新session_key失败: %w", err) + } + return &user, nil + } + return nil, errors.New("登录失败") +} + +func (userService *UserService) LoginByPhone(code string, openId string) (result *system.User, err error) { + token := wechat.GetMiniAccessToken() + url := "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + token + data := map[string]interface{}{ + "code": code, + } + jsonData, _ := json.Marshal(data) + resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData)) + if err != nil { + log.Fatalf("Error making POST request: %s", err) + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + log.Fatalf("Error reading response body: %s", err) + } + var dataMap map[string]interface{} + err = json.Unmarshal([]byte(body), &dataMap) + if err != nil { + log.Fatalf("Error unmarshalling JSON: %s", err) + } + //用户已经存在 --> 如果有手机号直接返回user,没有则更新手机号并返回user + if openId != "" { + var user system.User + err = global.DB.Where("mini_open_id = ?", openId).First(&user).Error + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, errors.New("用户不存在") + } + if err == nil && user.Id != "" { + if user.Phone != "" { + return &user, nil + } else { + user.Phone = dataMap["phone_info"].(map[string]interface{})["phoneNumber"].(string) + return &user, global.DB.Save(&user).Error + } + } + } + return nil, errors.New("登录失败") +} + +// GetLocation 获取位置信息 +func (userService *UserService) GetLocation(longitude, latitude string) (res map[string]interface{}, err error) { + long, err := strconv.ParseFloat(longitude, 32) + if err != nil { + return res, err + } + lati, err := strconv.ParseFloat(latitude, 32) + if err != nil { + return res, err + } + + entity, err := location.Point2code(float32(long), float32(lati)) + result := map[string]interface{}{ + "city": entity.AddressComponent.City.String(), + "adcode": entity.AddressComponent.Adcode, + } + return result, err +} + +// GetWeather 获取天气信息 adcode 行政区划代码 +func (userService *UserService) GetWeather(adcode string) (res map[string]interface{}, err error) { + weatherResp, err := location.GetWeather(adcode, "base") + live := weatherResp.Lives[0] // 实时天气数组仅1条数据 + result := map[string]interface{}{ + "province": live.Province, + "city": live.City, + "adcode": live.Adcode, + "weather": live.Weather, + "temperature": live.Temperature, + "windPower": live.WindPower, + "windDirection": live.WindDirection, + "humidity": live.Humidity, + "reportTime": live.ReportTime, + } + return result, err +} diff --git a/task/care_message_send_task.go b/task/care_message_send_task.go new file mode 100644 index 0000000..5bb4b60 --- /dev/null +++ b/task/care_message_send_task.go @@ -0,0 +1,117 @@ +package task + +// +//import ( +// "bytes" +// "encoding/json" +// "fmt" +// "io" +// "sundynix-go/global" +// "sundynix-go/model/plant" +// "sundynix-go/model/system" +// "sundynix-go/pkg/httpclient" +// "sundynix-go/utils/wechat" +// "time" +//) +// +//type TemplateDataItem struct { +// Value string `json:"value"` +//} +// +//// SendMessagePayload 是我们发送给微信服务器的消息体 +//type SendMessagePayload struct { +// TemplateID string `json:"template_id"` +// Page string `json:"page"` +// Touser string `json:"touser"` +// Data map[string]TemplateDataItem `json:"data"` +// MiniProgramState string `json:"miniprogram_state"` //跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版 +// Lang string `json:"lang"` +//} +// +//// SendMessageResponse 是发送消息的响应体 +//type SendMessageResponse struct { +// Errcode int `json:"errcode"` +// Errmsg string `json:"errmsg"` +//} +// +//// SendCareMsg 发送提醒消息 +//func SendCareMsg() error { +// httpUrl := "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + wechat.GetMiniAccessToken() +// +// //1.查询出今日的养护任务 +// now := time.Now() +// today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local) +// var tasks []*plant.TodayCare +// err := global.DB.Where("expected_date = ? and status in (1,4)", today).Find(&tasks).Error +// if err != nil { +// return err +// } +// if len(tasks) > 0 { +// // 将tasks分组,key为用户id 保证无论用户有多少植物,只给用户发送一条消息 +// tasksMap := make(map[string][]*plant.TodayCare) +// for _, task := range tasks { +// tasksMap[task.UserId] = append(tasksMap[task.UserId], task) +// } +// for userId, cares := range tasksMap { +// //1.查询用户 +// var user system.User +// err = global.DB.Where("id = ?", userId).First(&user).Error +// if err != nil { +// return err +// } +// if user.MiniOpenId != "" { +// //2.用户该养护的植物 +// plantId := cares[0].PlantId +// var myPlant plant.MyPlant +// err = global.DB.Where("id = ?", plantId).First(&myPlant).Error +// if err != nil { +// return err +// } +// //3.构造请求参数 发送订阅消息 +// payload := SendMessagePayload{ +// TemplateID: "inVOG9qy5NylOivO4Xb9H1db6PQlfv5doNNVhh_3iFE", +// Page: "pages/garden/index", +// Touser: user.MiniOpenId, +// Data: map[string]TemplateDataItem{ +// "thing2": { +// Value: myPlant.Name + "等", +// }, +// "time3": { +// // 今天的九点 +// Value: time.Date(now.Year(), now.Month(), now.Day(), 9, 0, 0, 0, time.Local).Format("2006-01-02"), +// }, +// }, +// MiniProgramState: "formal", +// //MiniProgramState: "trial", +// Lang: "zh_CN", +// } +// payloadBytes, err := json.Marshal(payload) +// if err != nil { +// return err +// } +// myHttpClient := httpclient.GetClient() +// resp, err := myHttpClient.Post(httpUrl, "application/json", bytes.NewBuffer(payloadBytes)) +// if err != nil { +// return err +// } +// defer resp.Body.Close() +// body, err := io.ReadAll(resp.Body) +// if err != nil { +// return fmt.Errorf("读取订阅消息响应失败: %v", err) +// } +// +// var smr SendMessageResponse +// err = json.Unmarshal(body, &smr) +// if err != nil { +// return fmt.Errorf("解析订阅消息响应失败: %v, body: %s", err, string(body)) +// } +// +// if smr.Errcode != 0 { +// return fmt.Errorf("微信服务器返回错误: errcode=%d, errmsg=%s", smr.Errcode, smr.Errmsg) +// } +// global.Logger.Info("订阅消息发送成功!") +// } +// } +// } +// return nil +//} diff --git a/task/care_reminder.go b/task/care_reminder.go new file mode 100644 index 0000000..a134780 --- /dev/null +++ b/task/care_reminder.go @@ -0,0 +1,117 @@ +package task + +// +//import ( +// "sundynix-go/global" +// "sundynix-go/model/plant" +// "time" +// +// "go.uber.org/zap" +//) +// +//// GeneratorTodayCare 生成今日养护任务 +//func GeneratorTodayCare() error { +// now := time.Now() +// today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local) +// +// //1.获取所有的养护计划 +// var plans []*plant.CarePlan +// if err := global.DB.Find(&plans).Error; err != nil { +// global.Logger.Error("获取所有的养护计划失败", zap.Error(err)) +// return err +// } +// for _, plan := range plans { +// if plan.Period > 0 { +// var todayCare plant.TodayCare +// err := global.DB.Where("care_id = ?", plan.Id).First(&todayCare).Error +// if err != nil { +// return err +// } +// // 如果今日日期满足周期循环 +// if plan.ShouldTriggerOn() { +// // 如果不是逾期的任务 就把ExpectedDate改为今日 +// if todayCare.Status != 4 { +// todayCare.Status = 1 +// todayCare.ExpectedDate = today +// if err = global.DB.Save(&todayCare).Error; err != nil { +// return err +// } +// } +// } +// } +// } +// return nil +//} +// +//// UpdateExpireCare 更新过期的养护任务 +//func UpdateExpireCare() error { +// now := time.Now() +// // 归一化到当天的0点(本地时区) +// today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local) +// var expiredCares []*plant.TodayCare +// // 1.查询所有未完成且预计日期在今天之前的养护任务 +// if err := global.DB.Where("status = ? and expected_date < ?", 1, today). +// Or("status = ?", 4).Find(&expiredCares).Error; err != nil { +// return err +// } +// //2.计算过期天数并更新状态为逾期 +// for _, care := range expiredCares { +// expireDays := int(today.Sub(care.ExpectedDate).Hours() / 24) +// updateMap := map[string]interface{}{ +// "is_expired": 1, +// "expire_days": expireDays, +// "status": 4, +// "expected_date": today, +// } +// if err := global.DB.Model(care).Updates(updateMap).Error; err != nil { +// return err +// } +// } +// return nil +//} +// +//// GenerateUserCenter 更新用户中心数据 +//func GenerateUserCenter() error { +// //1.所有的用户 +// var users []plant.Personal +// if err := global.DB.Find(&users).Error; err != nil { +// return err +// } +// now := time.Now() +// // 归一化到当天的0点(本地时区) +// now = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local) +// for _, user := range users { +// joinDate := user.JoinDate +// //1.加入多少天 +// joinDays := int(now.Sub(joinDate).Hours() / 24) +// //2.植物数量 +// var plantCount int64 +// err := global.DB.Model(&plant.MyPlant{}).Where("user_id = ?", user.Id).Count(&plantCount).Error +// if err != nil { +// return err +// } +// //3.养护次数 +// var careCount int64 +// err = global.DB.Model(&plant.CareRecord{}).Where("user_id = ?", user.Id).Count(&careCount).Error +// if err != nil { +// return err +// } +// +// //4.徽章数量 todo +// var badgeCount int64 +// err = global.DB.Model(&plant.MyBadge{}).Where("user_id = ?", user.Id).Count(&badgeCount).Error +// user.BadgeCount = int(badgeCount) +// // 5. 使用 Updates 方法进行精确、安全的更新 +// // 只更新我们刚刚计算和查询出的这四个字段 +// updateData := map[string]interface{}{ +// "join_days": joinDays, +// "plant_count": plantCount, +// "care_count": careCount, +// "badge_count": badgeCount, +// } +// if err := global.DB.Model(&plant.Personal{}).Where("id = ?", user.Id).Updates(updateData).Error; err != nil { +// return err +// } +// } +// return nil +//} diff --git a/utils/auth/claims.go b/utils/auth/claims.go new file mode 100644 index 0000000..b68049a --- /dev/null +++ b/utils/auth/claims.go @@ -0,0 +1,130 @@ +package auth + +import ( + "net" + "strings" + "sundynix-go/global" + "sundynix-go/model/system" + systemReq "sundynix-go/model/system/request" + + "github.com/gin-gonic/gin" +) + +// GetLoginToken 获取登录token +func GetLoginToken(user system.Login) (token string, claims systemReq.CustomClaims, err error) { + j := NewJWT() + claims = j.CreateClaims(systemReq.BaseClaims{ + Account: user.GetAccount(), + ID: user.GetUserId(), + }) + token, err = j.CreateToken(claims) + return +} + +// GetToken 从请求头中获取JWT token,并确保其有效性 +// +// 参数: +// - c: *gin.Context, Gin框架的上下文对象,用于获取请求信息和设置响应。 +// +// 返回值: +// - string: 获取到的JWT token,如果获取失败则返回空字符串。 +func GetToken(c *gin.Context) string { + // 从请求头中获取Authorization字段的值 + token := c.Request.Header.Get("Authorization") + prefix := strings.HasPrefix(token, "Bearer ") + if prefix { + token = strings.TrimPrefix(token, "Bearer ") + } + // 返回获取到的token + return token +} + +// ClearToken 清除Cookie中的token +func ClearToken(c *gin.Context) { + host, _, err := net.SplitHostPort(c.Request.Host) + if err != nil { + host = c.Request.Host + } + if net.ParseIP(host) != nil { + c.SetCookie("sundynix-token", "", -1, "/", "", false, false) + } else { + c.SetCookie("sundynix-token", "", -1, "/", host, false, false) + } +} + +// GetUserInfo 从 gin.Context 中获取用户信息,并返回 CustomClaims 类型的指针。 +// 该函数首先尝试从上下文中获取已存在的 claims,如果不存在,则调用 GetClaims 函数获取 claims。 +// 如果获取 claims 失败,则返回 nil。 +// +// 参数: +// - c: *gin.Context, gin 框架的上下文对象,用于获取请求相关的信息。 +// +// 返回值: +// - *systemReq.CustomClaims: 返回用户的自定义 claims 信息,如果获取失败则返回 nil。 +func GetUserInfo(c *gin.Context) *systemReq.CustomClaims { + // 尝试从上下文中获取已存在的 claims + if claims, exists := c.Get("claims"); !exists { + // 如果 claims 不存在,则调用 GetClaims 函数获取 claims + if cl, err := GetClaims(c); err != nil { + // 如果获取 claims 失败,返回 nil + return nil + } else { + // 成功获取 claims,返回 claims + return cl + } + } else { + // 如果 claims 存在,将其转换为 CustomClaims 类型并返回 + waitUse := claims.(*systemReq.CustomClaims) + return waitUse + } +} + +// GetUserId 从 gin.Context 中获取用户 ID,并返回 uint 类型的 ID。 +// 该函数首先尝试从上下文中获取已存在的 claims,如果不存在,则调用 GetClaims 函数获取 claims。 +// 如果获取 claims 失败,则返回 0。 +// +// 参数: +// - c: *gin.Context, gin 框架的上下文对象,用于获取请求相关的信息。 +// +// 返回值: +// - uint: 返回用户的 ID,如果获取失败则返回 0。 +func GetUserId(c *gin.Context) string { + if claims, exists := c.Get("claims"); !exists { + if cl, err := GetClaims(c); err != nil { + return "0" + } else { + return cl.BaseClaims.ID + } + } else { + waitUse := claims.(*systemReq.CustomClaims) + return waitUse.BaseClaims.ID + } +} + +// GetClaims 从 Gin 上下文中提取并解析 JWT 令牌,返回自定义的 claims 信息。 +// 该函数首先从请求头中获取 JWT 令牌,然后使用 JWT 解析器解析令牌并返回 claims。 +// 如果解析过程中发生错误,函数会记录错误日志并返回错误信息。 +// +// 参数: +// - c: *gin.Context, Gin 上下文对象,用于获取请求头中的 JWT 令牌。 +// +// 返回值: +// - *systemReq.CustomClaims: 解析后的自定义 claims 信息。 +// - error: 解析过程中发生的错误,如果解析成功则为 nil。 +func GetClaims(c *gin.Context) (*systemReq.CustomClaims, error) { + // 从 Gin 上下文中获取 JWT 令牌 + token := GetToken(c) + + // 创建新的 JWT 解析器 + j := NewJWT() + + // 解析 JWT 令牌并获取 claims 信息 + claims, err := j.ParseToken(token) + if err != nil { + // 如果解析失败,记录错误日志 + global.Logger.Error("获取用户信息失败,请检查请求头是否存在x-token且claims是否为规定结构") + } + + // 返回解析后的 claims 信息和可能的错误 + return claims, err +} diff --git a/utils/auth/jwt.go b/utils/auth/jwt.go new file mode 100644 index 0000000..2ae70b0 --- /dev/null +++ b/utils/auth/jwt.go @@ -0,0 +1,89 @@ +package auth + +import ( + "errors" + "sundynix-go/global" + "sundynix-go/model/system/request" + "sundynix-go/utils" + "time" + + "github.com/golang-jwt/jwt/v5" +) + +type JWT struct { + SigningKey []byte +} + +var ( + TokenValid = errors.New("未知错误") + TokenExpired = errors.New("token已过期") + TokenNotValidYet = errors.New("token尚未激活") + TokenMalformed = errors.New("这不是一个token") + TokenSignatureInvalid = errors.New("无效签名") + TokenInvalid = errors.New("无法处理此token") +) + +// NewJWT 初始化JWT +func NewJWT() *JWT { + return &JWT{ + SigningKey: []byte("gin-blog-key"), + } +} + +// CreateClaims 创建Claims +func (j *JWT) CreateClaims(baseClaims request.BaseClaims) request.CustomClaims { + bf, _ := utils.ParseDuration(global.Config.JWT.BufferTime) + ep, _ := utils.ParseDuration(global.Config.JWT.ExpiresTime) + claims := request.CustomClaims{ + BaseClaims: baseClaims, + BufferTime: int64(bf / time.Second), // 缓冲时间1天 缓冲时间内会获得新的token刷新令牌 此时一个用户会存在两个有效令牌 但是前端只留一个 另一个会丢失 + RegisteredClaims: jwt.RegisteredClaims{ + Audience: jwt.ClaimStrings{"sundynix"}, + NotBefore: jwt.NewNumericDate(time.Now().Add(-1000)), // 签名生效时间 + ExpiresAt: jwt.NewNumericDate(time.Now().Add(ep)), // 过期时间 7天 配置文件 + Issuer: global.Config.JWT.Issuer, + }, + } + return claims +} + +// CreateToken 创建一个token +func (j *JWT) CreateToken(claims request.CustomClaims) (string, error) { + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + return token.SignedString(j.SigningKey) +} + +// RefreshToken 刷新token +func (j *JWT) RefreshToken(oldTokenString string, claims request.CustomClaims) (string, error) { + v, err, _ := global.ConcurrencyControl.Do("JWT:"+oldTokenString, func() (interface{}, error) { + return j.CreateToken(claims) + }) + return v.(string), err +} + +// ParseToken 解析token +func (j *JWT) ParseToken(tokenString string) (*request.CustomClaims, error) { + token, err := jwt.ParseWithClaims(tokenString, &request.CustomClaims{}, func(token *jwt.Token) (i interface{}, e error) { + return j.SigningKey, nil + }) + if err != nil { + switch { + case errors.Is(err, jwt.ErrTokenExpired): + return nil, TokenExpired + case errors.Is(err, jwt.ErrTokenNotValidYet): + return nil, TokenNotValidYet + case errors.Is(err, jwt.ErrTokenMalformed): + return nil, TokenMalformed + case errors.Is(err, jwt.ErrTokenSignatureInvalid): + return nil, TokenSignatureInvalid + default: + return nil, TokenInvalid + } + } + if token != nil { + if claims, ok := token.Claims.(*request.CustomClaims); ok && token.Valid { + return claims, nil + } + } + return nil, TokenInvalid +} diff --git a/utils/captcha/redis.go b/utils/captcha/redis.go new file mode 100644 index 0000000..6e09bc6 --- /dev/null +++ b/utils/captcha/redis.go @@ -0,0 +1,5 @@ +package captcha + +import "github.com/mojocn/base64Captcha" + +var CaptchaStore = base64Captcha.DefaultMemStore diff --git a/utils/directory.go b/utils/directory.go new file mode 100644 index 0000000..c2bd8ae --- /dev/null +++ b/utils/directory.go @@ -0,0 +1,20 @@ +package utils + +import ( + "errors" + "os" +) + +func PathExist(path string) (bool, error) { + stat, err := os.Stat(path) + if err == nil { + if stat.IsDir() { + return true, nil + } + return false, errors.New("存在同名文件") + } + if os.IsNotExist(err) { + return false, nil + } + return false, err +} diff --git a/utils/hash.go b/utils/hash.go new file mode 100644 index 0000000..e7f23aa --- /dev/null +++ b/utils/hash.go @@ -0,0 +1,32 @@ +package utils + +import ( + "crypto/md5" + "encoding/hex" + + "golang.org/x/crypto/bcrypt" +) + +// BcryptHash 使用 bcrypt 对密码进行加密 +func BcryptHash(password string) string { + bytes, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + return string(bytes) +} + +// BcryptCheck 对比明文密码和数据库的哈希值 +func BcryptCheck(password, hash string) bool { + err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) + return err == nil +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: MD5V +//@description: md5加密 +//@param: str []byte +//@return: string + +func MD5V(str []byte, b ...byte) string { + h := md5.New() + h.Write(str) + return hex.EncodeToString(h.Sum(b)) +} diff --git a/utils/hash_test.go b/utils/hash_test.go new file mode 100644 index 0000000..e04d239 --- /dev/null +++ b/utils/hash_test.go @@ -0,0 +1,111 @@ +package utils + +import ( + "fmt" + "log" + "sundynix-go/utils/location" + "sundynix-go/utils/timer" + "sundynix-go/utils/uniqueid" + "testing" + "time" +) + +func TestHashPwd(t *testing.T) { + hash := BcryptHash("sundynix") + fmt.Println(hash) // $2a$10$QC/zkQ/ohPmvjF/goDyicu7cHgAEj8gHg6OTDHWhbYQMHHn4dwxX2 + + //check := BcryptCheck("sundynix", "$2a$10$QC/zkQ/ohPmvjF/goDyicu7cHgAEj8gHg6OTDHWhbYQMHHn4dwxX2") + //fmt.Println(check) +} + +func TestUuid(t *testing.T) { + id := uniqueid.GenerateId() + fmt.Println(id) +} + +func TestTimePeriod(t *testing.T) { + str := "2025-11-19 10:29:20.597" + parse, _ := time.ParseInLocation("2006-01-02 15:04:05.999999999", str, time.Local) + interval := timer.TimeInterval(parse) + fmt.Println(interval) +} + +func TestPoint2Code(t *testing.T) { + //latitude := float32(39.90882) + //longitude := float32(116.39748) + longitude := float32(102.74837) + latitude := float32(25.02847) + // 调用逆地理编码 + entity, err := location.Point2code(longitude, latitude) + if err != nil { + log.Fatalf("获取地址失败: %v", err) + } + + // 打印结果 + // 打印结果 + fmt.Println("=== 解析结果 ===") + fmt.Println("格式化地址:", entity.Address) + fmt.Println("省份:", entity.AddressComponent.Province.String()) + fmt.Println("城市:", entity.AddressComponent.City.String()) + fmt.Println("区县:", entity.AddressComponent.District.String()) + fmt.Println("乡镇:", entity.AddressComponent.Township) + fmt.Println("街道:", entity.AddressComponent.Street) + fmt.Println("门牌号:", entity.AddressComponent.StreetNumber.Number) + fmt.Println("门牌号所属街道:", entity.AddressComponent.StreetNumber.Street) + fmt.Println("行政区划编码:", entity.AddressComponent.Adcode) + fmt.Println("国家:", entity.AddressComponent.Country) +} + +func TestWeather(t *testing.T) { + + adcode := "532325" + // 可选:extensions="all" 查询实时+未来3天预报 + weatherResp, err := location.GetWeather(adcode, "base") + if err != nil { + log.Fatalf("查询天气失败: %v", err) + } + + // 打印实时天气 + fmt.Println("=== 实时天气 ===") + live := weatherResp.Lives[0] // 实时天气数组仅1条数据 + fmt.Printf("省份:%s\n", live.Province) + fmt.Printf("城市:%s\n", live.City) + fmt.Printf("行政区划编码:%s\n", live.Adcode) + fmt.Printf("天气:%s\n", live.Weather) + fmt.Printf("实时气温:%s℃\n", live.Temperature) + fmt.Printf("风向:%s\n", live.WindDirection) + fmt.Printf("风力:%s级\n", live.WindPower) + fmt.Printf("湿度:%s%%\n", live.Humidity) + fmt.Printf("数据更新时间:%s\n", live.ReportTime) + + // 若查询的是all(实时+预报),打印预报数据 + // if len(weatherResp.Forecasts) > 0 { + // fmt.Println("\n=== 未来3天预报 ===") + // forecast := weatherResp.Forecasts[0] + // for _, day := range forecast.Casts { + // fmt.Printf("\n日期:%s(星期%s)\n", day.Date, day.Week) + // fmt.Printf("白天天气:%s,温度:%s℃\n", day.DayWeather, day.DayTemp) + // fmt.Printf("夜间天气:%s,温度:%s℃\n", day.NightWeather, day.NightTemp) + // fmt.Printf("白天风向/风力:%s/%s级\n", day.DayWindDir, day.DayWindPower) + // } + // } +} + +func TestGenOrderNo(t *testing.T) { + no := uniqueid.GenOrderNo() + fmt.Println(no) +} + +func TestTime(t *testing.T) { + milliTimestamp := time.Now().UnixMilli() + microTimestamp := time.Now().UnixMicro() + + fmt.Printf("毫秒级时间戳: %d\n", milliTimestamp) + fmt.Printf("微秒级时间戳: %d\n", microTimestamp) +} + +func TestGetZeroTime(t *testing.T) { + zeroTime := timer.GetZeroTime() + fmt.Printf("当天零点: %v\n", zeroTime) + fmt.Printf("时间戳(秒): %v\n", zeroTime.Unix()) +} diff --git a/utils/human_duration.go b/utils/human_duration.go new file mode 100644 index 0000000..abdb3b1 --- /dev/null +++ b/utils/human_duration.go @@ -0,0 +1,30 @@ +package utils + +import ( + "strconv" + "strings" + "time" +) + +// ParseDuration 解析时间 +func ParseDuration(d string) (time.Duration, error) { + d = strings.TrimSpace(d) + dr, err := time.ParseDuration(d) + if err == nil { + return dr, nil + } + if strings.Contains(d, "d") { + index := strings.Index(d, "d") + + hour, _ := strconv.Atoi(d[:index]) + dr = time.Hour * 24 * time.Duration(hour) + ndr, err := time.ParseDuration(d[index+1:]) + if err != nil { + return dr, nil + } + return dr + ndr, nil + } + + dv, err := strconv.ParseInt(d, 10, 64) + return time.Duration(dv), err +} diff --git a/utils/location/location.go b/utils/location/location.go new file mode 100644 index 0000000..ee50e4d --- /dev/null +++ b/utils/location/location.go @@ -0,0 +1,168 @@ +package location + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "log" + "net/http" + "net/url" + "time" +) + +// StringOrArray ########################### 核心:兼容字符串/数组的自定义类型 ########################### +type StringOrArray string + +// UnmarshalJSON 处理字符串/数组两种格式 +func (s *StringOrArray) UnmarshalJSON(data []byte) error { + // 尝试解析为纯字符串 + var str string + if err := json.Unmarshal(data, &str); err == nil { + *s = StringOrArray(str) + return nil + } + + // 尝试解析为字符串数组 + var strArr []string + if err := json.Unmarshal(data, &strArr); err != nil { + return fmt.Errorf("字段解析失败: %v, 原始数据: %s", err, string(data)) + } + + // 数组取第一个元素(高德返回的数组通常仅1个元素) + if len(strArr) > 0 { + *s = StringOrArray(strArr[0]) + } else { + *s = StringOrArray("") + } + return nil +} + +// String 转为普通字符串,方便使用 +func (s StringOrArray) String() string { + return string(s) +} + +// ########################### 严格对齐高德API的结构体 ########################### +// AMapEntity 最终返回的实体(对应Java的AMapEntity) +type AMapEntity struct { + AddressComponent AddressComponent `json:"addresscomponent"` + Address StringOrArray `json:"formatted_address"` // 改为兼容类型 +} + +// StreetNumber 门牌号嵌套对象(高德固定返回对象) +type StreetNumber struct { + Number StringOrArray `json:"number"` // 门牌号 + Location StringOrArray `json:"location"` // 经纬度 + Direction StringOrArray `json:"direction"` // 方向 + Distance StringOrArray `json:"distance"` // 距离 + Street StringOrArray `json:"street"` // 所属街道 +} + +// AddressComponent 地址组件(全量兼容) +type AddressComponent struct { + Province StringOrArray `json:"province"` // 省(字符串/数组) + City StringOrArray `json:"city"` // 市(字符串/数组) + District StringOrArray `json:"district"` // 区(字符串/数组) + Township StringOrArray `json:"township"` // 乡镇(纯字符串) + Street StringOrArray `json:"street"` // 街道(纯字符串) + StreetNumber StreetNumber `json:"streetnumber"` // 门牌号(对象) + Adcode StringOrArray `json:"adcode"` // 行政区划编码(纯字符串) + Country StringOrArray `json:"country"` // 国家(纯字符串) + CountryCode StringOrArray `json:"countrycode"` // 国家编码(纯字符串) +} + +// AMapResponse 高德API顶层响应 +type AMapResponse struct { + Regeocode Regeocode `json:"regeocode"` // 核心数据 + Status string `json:"status"` // 1=成功,0=失败 + Info string `json:"info"` // 错误信息 + Infocode string `json:"infocode"` // 错误码(精准定位问题) +} + +// Regeocode 逆地理编码核心数据 +type Regeocode struct { + FormattedAddress StringOrArray `json:"formatted_address"` // 改为兼容类型 + AddressComponent AddressComponent `json:"addresscomponent"` // 地址组件 +} + +// ########################### 配置常量 ########################### +const ( + appKey = "1b8dd8848bcc062ff1f2cec6db683673" // 替换为你的有效Key + amapApiUrl = "https://restapi.amap.com/v3/geocode/regeo" +) + +// Point2code 经纬度转地址(完整错误处理+兼容) +func Point2code(longitude, latitude float32) (*AMapEntity, error) { + // 1. 基础参数校验 + if longitude == 0 || latitude == 0 { + return nil, errors.New("经纬度不能为0") + } + + // 2. 构建请求URL + baseURL, err := url.Parse(amapApiUrl) + if err != nil { + log.Printf("URL解析失败: %v", err) + return nil, err + } + + // 设置查询参数 + params := url.Values{} + params.Set("key", appKey) + params.Set("location", fmt.Sprintf("%f,%f", longitude, latitude)) + baseURL.RawQuery = params.Encode() + + // 3. 全局HTTP客户端(建议单例复用) + client := &http.Client{Timeout: 10 * time.Second} + + // 4. 发送GET请求 + req, err := http.NewRequest(http.MethodGet, baseURL.String(), nil) + if err != nil { + log.Printf("请求构建失败: %v", err) + return nil, err + } + + resp, err := client.Do(req) + if err != nil { + log.Printf("请求发送失败: %v", err) + return nil, err + } + defer resp.Body.Close() // 强制关闭响应体,避免内存泄漏 + + // 5. 校验HTTP状态码 + if resp.StatusCode != http.StatusOK { + errMsg := fmt.Sprintf("HTTP请求失败,状态码: %d", resp.StatusCode) + log.Println(errMsg) + return nil, errors.New(errMsg) + } + + // 6. 读取响应体 + bodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Printf("响应体读取失败: %v", err) + return nil, err + } + + // 7. 解析JSON响应 + var amapResp AMapResponse + if err := json.Unmarshal(bodyBytes, &amapResp); err != nil { + log.Printf("JSON解析失败: %v, 响应内容: %s", err, string(bodyBytes)) + return nil, err + } + + // 8. 校验高德API业务状态 + if amapResp.Status != "1" { + errMsg := fmt.Sprintf("高德API返回失败: status=%s, info=%s, infocode=%s", + amapResp.Status, amapResp.Info, amapResp.Infocode) + log.Println(errMsg) + return nil, errors.New(errMsg) + } + + // 9. 构造最终返回实体 + result := &AMapEntity{ + AddressComponent: amapResp.Regeocode.AddressComponent, + Address: amapResp.Regeocode.FormattedAddress, + } + + return result, nil +} diff --git a/utils/location/weather.go b/utils/location/weather.go new file mode 100644 index 0000000..b3f73b5 --- /dev/null +++ b/utils/location/weather.go @@ -0,0 +1,186 @@ +package location + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "log" + "net/http" + "net/url" + "time" +) + +// ########################### 1. 结构体定义(对齐高德天气API返回格式) ########################### +// WeatherResponse 高德天气API顶层响应结构体 +type WeatherResponse struct { + Status string `json:"status"` // 1=成功,0=失败 + Info string `json:"info"` // 错误信息 + Infocode string `json:"infocode"` // 错误码 + Lives []LiveWeather `json:"lives"` // 实时天气(数组,仅1条数据) + Forecasts []Forecast `json:"forecasts"` // 天气预报(数组,仅1条数据) +} + +// LiveWeather 实时天气结构体 +type LiveWeather struct { + Province string `json:"province"` // 省份 + City string `json:"city"` // 城市 + Adcode string `json:"adcode"` // 行政区划编码 + Weather string `json:"weather"` // 天气现象(如晴、阴) + Temperature string `json:"temperature"` // 实时气温(℃) + WindDirection string `json:"winddirection"` // 风向(如东、西南) + WindPower string `json:"windpower"` // 风力(如3级) + Humidity string `json:"humidity"` // 湿度(%) + ReportTime string `json:"reporttime"` // 数据更新时间 +} + +// Forecast 天气预报顶层结构体(包含多日预报) +type Forecast struct { + Province string `json:"province"` // 省份 + City string `json:"city"` // 城市 + Adcode string `json:"adcode"` // 行政区划编码 + ReportTime string `json:"reporttime"` // 预报发布时间 + Casts []ForecastDay `json:"casts"` // 每日预报(未来3天) +} + +// ForecastDay 单日天气预报 +type ForecastDay struct { + Date string `json:"date"` // 日期(yyyy-MM-dd) + Week string `json:"week"` // 星期(1=周一,7=周日) + DayWeather string `json:"dayweather"` // 白天天气 + NightWeather string `json:"nightweather"` // 夜间天气 + DayTemp string `json:"daytemp"` // 白天温度 + NightTemp string `json:"nighttemp"` // 夜间温度 + DayWindDir string `json:"daywinddir"` // 白天风向 + NightWindDir string `json:"nightwinddir"` // 夜间风向 + DayWindPower string `json:"daywindpower"` // 白天风力 + NightWindPower string `json:"nightwindpower"` // 夜间风力 +} + +// ########################### 2. 配置常量 ########################### +const ( + amapWeatherApi = "https://restapi.amap.com/v3/weather/weatherInfo" +) + +// GetWeather 根据行政区划编码查询天气 +// adcode: 行政区划编码(如110101=北京市东城区) +// extensions: 查询类型(base=实时,all=实时+预报) +func GetWeather(adcode string, extensions string) (*WeatherResponse, error) { + // 1. 参数校验 + if adcode == "" { + return nil, errors.New("行政区划编码adcode不能为空") + } + if extensions == "" { + extensions = "base" // 默认查实时天气 + } + if extensions != "base" && extensions != "all" { + return nil, errors.New("extensions仅支持base(实时)或all(实时+预报)") + } + + // 2. 构建请求URL和参数 + baseURL, err := url.Parse(amapWeatherApi) + if err != nil { + log.Printf("解析基础URL失败: %v", err) + return nil, err + } + + // 设置查询参数 + params := url.Values{} + params.Set("key", appKey) + params.Set("city", adcode) // 核心参数:行政区划编码 + params.Set("extensions", extensions) + params.Set("output", "json") // 固定返回JSON格式 + baseURL.RawQuery = params.Encode() + + // 3. 发送HTTP请求(全局客户端,复用连接) + client := &http.Client{ + Timeout: 10 * time.Second, // 10秒超时 + } + req, err := http.NewRequest(http.MethodGet, baseURL.String(), nil) + if err != nil { + log.Printf("构建请求失败: %v", err) + return nil, err + } + + resp, err := client.Do(req) + if err != nil { + log.Printf("发送请求失败: %v", err) + return nil, err + } + defer resp.Body.Close() // 强制关闭响应体 + + // 4. 检查HTTP状态码 + if resp.StatusCode != http.StatusOK { + errMsg := fmt.Sprintf("请求失败,状态码: %d", resp.StatusCode) + log.Println(errMsg) + return nil, errors.New(errMsg) + } + + // 5. 读取响应体 + bodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Printf("读取响应体失败: %v", err) + return nil, err + } + + // 6. 解析JSON响应 + var weatherResp WeatherResponse + if err := json.Unmarshal(bodyBytes, &weatherResp); err != nil { + log.Printf("解析JSON失败: %v, 响应内容: %s", err, string(bodyBytes)) + return nil, err + } + + // 7. 检查API业务状态 + if weatherResp.Status != "1" { + errMsg := fmt.Sprintf("高德天气API返回失败: status=%s, info=%s, infocode=%s", + weatherResp.Status, weatherResp.Info, weatherResp.Infocode) + log.Println(errMsg) + return nil, errors.New(errMsg) + } + + // 8. 校验返回数据非空 + if extensions == "base" && len(weatherResp.Lives) == 0 { + return nil, errors.New("未查询到实时天气数据") + } + if extensions == "all" && (len(weatherResp.Lives) == 0 || len(weatherResp.Forecasts) == 0) { + return nil, errors.New("未查询到天气数据(实时/预报)") + } + + return &weatherResp, nil +} + +// ########################### 4. 测试示例 ########################### +func main() { + // 示例1:查询北京市东城区(adcode=110101)实时天气 + adcode := "110101" + // 可选:extensions="all" 查询实时+未来3天预报 + weatherResp, err := GetWeather(adcode, "base") + if err != nil { + log.Fatalf("查询天气失败: %v", err) + } + + // 打印实时天气 + fmt.Println("=== 实时天气 ===") + live := weatherResp.Lives[0] // 实时天气数组仅1条数据 + fmt.Printf("省份:%s\n", live.Province) + fmt.Printf("城市:%s\n", live.City) + fmt.Printf("行政区划编码:%s\n", live.Adcode) + fmt.Printf("天气:%s\n", live.Weather) + fmt.Printf("实时气温:%s℃\n", live.Temperature) + fmt.Printf("风向:%s\n", live.WindDirection) + fmt.Printf("风力:%s级\n", live.WindPower) + fmt.Printf("湿度:%s%%\n", live.Humidity) + fmt.Printf("数据更新时间:%s\n", live.ReportTime) + + // 若查询的是all(实时+预报),打印预报数据 + // if len(weatherResp.Forecasts) > 0 { + // fmt.Println("\n=== 未来3天预报 ===") + // forecast := weatherResp.Forecasts[0] + // for _, day := range forecast.Casts { + // fmt.Printf("\n日期:%s(星期%s)\n", day.Date, day.Week) + // fmt.Printf("白天天气:%s,温度:%s℃\n", day.DayWeather, day.DayTemp) + // fmt.Printf("夜间天气:%s,温度:%s℃\n", day.NightWeather, day.NightTemp) + // fmt.Printf("白天风向/风力:%s/%s级\n", day.DayWindDir, day.DayWindPower) + // } + // } +} diff --git a/utils/timer/interval.go b/utils/timer/interval.go new file mode 100644 index 0000000..723fbf2 --- /dev/null +++ b/utils/timer/interval.go @@ -0,0 +1,46 @@ +package timer + +import ( + "fmt" + "time" +) + +// TimeInterval 计算两个时间的间隔,超过24小时返回天数,否则返回几小时前 +func TimeInterval(targetTime time.Time) string { + // 1. 将当前时间和目标时间统一转换为本地时区(time.Local) + now := time.Now().In(time.Local) // 当前时间转为本地时区 + target := targetTime.In(time.Local) // 目标时间转为本地时区 + + // 2. 计算时间差(取绝对值,避免因目标时间在未来导致负数) + var diff time.Duration + if now.After(target) { + diff = now.Sub(target) + } else { + diff = target.Sub(now) + } + + // 3. 转换为总小时数(取整数部分,自动截断小数) + // 转换为总分钟数(取整数部分,自动截断秒数) + totalMinutes := int(diff.Minutes()) + + // 按不同阈值返回对应格式 + switch { + case totalMinutes >= 24*60: // 24小时 = 1440分钟 + days := totalMinutes / (24 * 60) + return fmt.Sprintf("%d天前", days) + case totalMinutes >= 60: // 1小时 = 60分钟 + hours := totalMinutes / 60 + return fmt.Sprintf("%d小时前", hours) + default: // 不足1小时 + return fmt.Sprintf("%d分钟前", totalMinutes) + } +} + +// GetZeroTime 获取当天零点时间 +func GetZeroTime() time.Time { + now := time.Now() + + // 2. 使用当天的年月日,将时分秒纳秒设为0,并保留原时区(Location) + zeroTime := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local) + return zeroTime +} diff --git a/utils/timer/timed_task.go b/utils/timer/timed_task.go new file mode 100644 index 0000000..0a93dd2 --- /dev/null +++ b/utils/timer/timed_task.go @@ -0,0 +1,219 @@ +package timer + +import ( + "sync" + + "github.com/robfig/cron/v3" +) + +type Timer interface { + FindCronList() map[string]*taskManager + AddTaskByFuncWithSecond(cronName string, spec string, fun func(), taskName string, options ...cron.Option) (cron.EntryID, error) // 添加Task Func以秒的形式加入 + AddTaskByJobWithSecond(cronName string, spec string, job interface{ Run() }, taskName string, options ...cron.Option) (cron.EntryID, error) // 添加Task Job以秒的形式加入 + AddTaskByFunc(cronName string, spec string, task func(), taskName string, options ...cron.Option) (cron.EntryID, error) // 添加Task Func加入 + AddTaskByJob(cronName string, spec string, job interface{ Run() }, taskName string, options ...cron.Option) (cron.EntryID, error) // 添加Task Job加入 + FindCron(cronName string) (*taskManager, bool) //获取对应taskName的cron 可能会为空 + StartCron(cronName string) // 启动对应cron + StopCron(cronName string) // 停止对应cron + FindTask(cronName string, taskName string) (*task, bool) //获取对应taskName的task + RemoveTask(cronName string, id int) //删除对应taskName的task + RemoveTaskByName(cronName string, taskName string) //删除对应taskName的task + Clear(cronName string) //清空对应cronName的task + Close() // 关闭所有定时任务 +} + +type task struct { + EntryId cron.EntryID + Spec string + TaskName string +} +type taskManager struct { + corn *cron.Cron + tasks map[cron.EntryID]*task +} + +// timer 定时任务管理 +type timer struct { + cronList map[string]*taskManager + sync.Mutex +} + +// AddTaskByFuncWithSecond 通过函数的方法使用WithSeconds添加任务 +func (t *timer) AddTaskByFuncWithSecond(cronName string, spec string, fun func(), taskName string, option ...cron.Option) (cron.EntryID, error) { + t.Lock() + defer t.Unlock() + option = append(option, cron.WithSeconds()) + if _, ok := t.cronList[cronName]; !ok { + tasks := make(map[cron.EntryID]*task) + t.cronList[cronName] = &taskManager{ + corn: cron.New(option...), + tasks: tasks, + } + } + id, err := t.cronList[cronName].corn.AddFunc(spec, fun) + t.cronList[cronName].corn.Start() + t.cronList[cronName].tasks[id] = &task{ + EntryId: id, + Spec: spec, + TaskName: taskName, + } + return id, err +} + +// AddTaskByFunc 通过函数的方法添加任务 +func (t *timer) AddTaskByFunc(cronName string, spec string, fun func(), taskName string, option ...cron.Option) (cron.EntryID, error) { + t.Lock() + defer t.Unlock() + if _, ok := t.cronList[cronName]; !ok { + tasks := make(map[cron.EntryID]*task) + t.cronList[cronName] = &taskManager{ + corn: cron.New(option...), + tasks: tasks, + } + } + id, err := t.cronList[cronName].corn.AddFunc(spec, fun) + t.cronList[cronName].corn.Start() + t.cronList[cronName].tasks[id] = &task{ + EntryId: id, + Spec: spec, + TaskName: taskName, + } + return id, err +} + +// AddTaskByJobWithSecond 通过Job的方法使用WithSeconds添加任务 +func (t *timer) AddTaskByJobWithSecond(cronName string, spec string, job interface{ Run() }, taskName string, option ...cron.Option) (cron.EntryID, error) { + t.Lock() + defer t.Unlock() + option = append(option, cron.WithSeconds()) + if _, ok := t.cronList[cronName]; !ok { + tasks := make(map[cron.EntryID]*task) + t.cronList[cronName] = &taskManager{ + corn: cron.New(option...), + tasks: tasks, + } + } + id, err := t.cronList[cronName].corn.AddJob(spec, job) + t.cronList[cronName].corn.Start() + t.cronList[cronName].tasks[id] = &task{ + EntryId: id, + Spec: spec, + TaskName: taskName, + } + return id, err +} + +// AddTaskByJob 通过Job的方法添加任务 +func (t *timer) AddTaskByJob(cronName string, spec string, job interface{ Run() }, taskName string, option ...cron.Option) (cron.EntryID, error) { + t.Lock() + defer t.Unlock() + if _, ok := t.cronList[cronName]; !ok { + tasks := make(map[cron.EntryID]*task) + t.cronList[cronName] = &taskManager{ + corn: cron.New(option...), + tasks: tasks, + } + } + id, err := t.cronList[cronName].corn.AddJob(spec, job) + t.cronList[cronName].corn.Start() + t.cronList[cronName].tasks[id] = &task{ + EntryId: id, + Spec: spec, + TaskName: taskName, + } + return id, err +} + +// FindCron 获取对应cronName的cron 可能会为空 +func (t *timer) FindCron(cronName string) (*taskManager, bool) { + t.Lock() + defer t.Unlock() + v, ok := t.cronList[cronName] + return v, ok +} + +// FindTask 获取对应taskName的task +func (t *timer) FindTask(cronName string, taskName string) (*task, bool) { + t.Lock() + defer t.Unlock() + v, ok := t.cronList[cronName] + if !ok { + return nil, false + } + for _, t2 := range v.tasks { + if t2.TaskName == taskName { + return t2, true + } + } + return nil, false +} + +// FindCronList 获取所有cron +func (t *timer) FindCronList() map[string]*taskManager { + t.Lock() + defer t.Unlock() + return t.cronList +} + +// StartCron 启动对应cron +func (t *timer) StartCron(cronName string) { + t.Lock() + defer t.Unlock() + if v, ok := t.cronList[cronName]; ok { + v.corn.Start() + } +} + +// StopCron 停止对应cron +func (t *timer) StopCron(cronName string) { + t.Lock() + defer t.Unlock() + if v, ok := t.cronList[cronName]; ok { + v.corn.Stop() + } +} + +// RemoveTask 从cronName 删除指定任务 +func (t *timer) RemoveTask(cronName string, id int) { + t.Lock() + defer t.Unlock() + if v, ok := t.cronList[cronName]; ok { + v.corn.Remove(cron.EntryID(id)) + delete(v.tasks, cron.EntryID(id)) + } +} + +// RemoveTaskByName 从cronName 删除指定任务 +func (t *timer) RemoveTaskByName(cronName string, taskName string) { + fTask, ok := t.FindTask(cronName, taskName) + if !ok { + return + } + t.RemoveTask(cronName, int(fTask.EntryId)) +} + +// Clear 清空对应cronName的task +func (t *timer) Clear(cronName string) { + t.Lock() + defer t.Unlock() + if v, ok := t.cronList[cronName]; ok { + v.corn.Stop() + delete(t.cronList, cronName) + } +} + +// Close 关闭所有定时任务 释放资源 +func (t *timer) Close() { + t.Lock() + defer t.Unlock() + for _, v := range t.cronList { + v.corn.Stop() + } +} + +// NewTimerTask 创建定时任务 +func NewTimerTask() Timer { + return &timer{ + cronList: make(map[string]*taskManager), + } +} diff --git a/utils/uniqueid/id_generator.go b/utils/uniqueid/id_generator.go new file mode 100644 index 0000000..26baa94 --- /dev/null +++ b/utils/uniqueid/id_generator.go @@ -0,0 +1,41 @@ +package uniqueid + +import ( + "crypto/rand" + "fmt" + "math/big" + "strings" + + "github.com/google/uuid" +) + +func GenerateId() string { + uuidV1, err := uuid.NewUUID() + if err != nil { + panic(err) + } + return uuidV1.String() +} + +func GenerateName() string { + str := uuid.New().String() + //生成一个用户名 比如花友u278bb 中文后的字符随机生成 不可重复 取str的前六位 + return "花友" + str[6:12] + +} + +// GenerateRandomCode 生成邀请码 +func GenerateRandomCode(length int) string { + const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + var sb strings.Builder + for i := 0; i < length; i++ { + n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(charset)))) + sb.WriteByte(charset[n.Int64()]) + } + return sb.String() +} + +// GenCodeKey 生成邀请码的key +func GenCodeKey(userId string) string { + return fmt.Sprintf("code:%s", userId) +} diff --git a/utils/uniqueid/out_trade_no.go b/utils/uniqueid/out_trade_no.go new file mode 100644 index 0000000..e652148 --- /dev/null +++ b/utils/uniqueid/out_trade_no.go @@ -0,0 +1,32 @@ +package uniqueid + +import ( + "fmt" + "time" +) + +func GenOrderNo() string { + /* + 支付宝订单号示例:1217752501201407033233368028 + 分析可能格式: + - 前14位:时间戳(年月日时分秒)20250112 151420 + - 中间6位:商户/业务标识 + - 最后8位:随机数或序列号 + */ + + now := time.Now() + + // 时间部分:年月日时分秒 + timePart := now.Format("20060102150405") // 14位 + + // 业务标识:机器ID + 进程ID + 随机数 + machineID := 1 + pid := now.Nanosecond() % 1000 + businessPart := fmt.Sprintf("%02d%03d%01d", machineID, pid, now.Second()%10) // 6位 + + // 随机部分:纳秒取模 + random1 := fmt.Sprintf("%04d", now.Nanosecond()%10000) + random2 := fmt.Sprintf("%04d", (now.Nanosecond()/10000)%10000) + + return timePart + businessPart + random1 + random2 +} diff --git a/utils/upload/minio_oss.go b/utils/upload/minio_oss.go new file mode 100644 index 0000000..07ae159 --- /dev/null +++ b/utils/upload/minio_oss.go @@ -0,0 +1,106 @@ +package upload + +import ( + "bytes" + "context" + "errors" + "io" + "mime/multipart" + "path/filepath" + "strconv" + "strings" + "sundynix-go/global" + "sundynix-go/utils" + "time" + + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" + "go.uber.org/zap" +) + +var MinioClient *Minio // 优化性能,但是不支持动态配置 +type Minio struct { + Client *minio.Client + bucket string +} + +func GetMinio(endpoint, accessKey, secretKey, bucketName string, useSSL bool) (*Minio, error) { + if MinioClient != nil { + return MinioClient, nil + } + // Initialize minio client object. + minioClient, err := minio.New(endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(accessKey, secretKey, ""), + Secure: useSSL, // Set to true if using https + }) + if err != nil { + return nil, err + } + + // 创建bucket + err = minioClient.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{}) + if err != nil { + // 判断是否已经存在 + exists, errBucketExists := minioClient.BucketExists(context.Background(), bucketName) + if errBucketExists == nil && exists { + global.Logger.Info("Bucket already exists") + } else { + return nil, err + } + } + MinioClient = &Minio{ + Client: minioClient, + bucket: bucketName, + } + return MinioClient, nil +} + +func (m *Minio) UploadFile(file *multipart.FileHeader) (filePathres, key string, uploadErr error) { + f, openErr := file.Open() + // mutipart.File to os.File + if openErr != nil { + global.Logger.Error("function file.Open() Failed", zap.Any("err", openErr.Error())) + return "", "", errors.New("function file.Open() Failed, err:" + openErr.Error()) + } + buffer := bytes.Buffer{} + _, err := io.Copy(&buffer, f) + if err != nil { + global.Logger.Error("读取文件失败", zap.Any("err", err.Error())) + return "", "", errors.New("读取文件失败, err:" + err.Error()) + } + f.Close() // 创建文件 defer 关闭 + + //对文件名进行加密存储 + ext := filepath.Ext(file.Filename) + filename := utils.MD5V([]byte(strings.TrimSuffix(file.Filename, ext))) + ext + timestamp := time.Now().UnixMicro() + timestr := strconv.FormatInt(timestamp, 10) + if global.Config.Minio.BasePath == "" { + filePathres = "uploads/" + time.Now().Format("2006-01-02") + "/" + timestr + "-" + filename // uploads/2025-09-17/xxxx.png + } else { + filePathres = global.Config.Minio.BasePath + "/" + time.Now().Format("2006-01-02") + "/" + timestr + "-" + filename + } + // 设置超时10分钟 + ctx, cancel := context.WithTimeout(context.Background(), time.Minute*10) + defer cancel() + + //大文件自动切换为分片上传 + info, err := m.Client.PutObject(ctx, global.Config.Minio.BucketName, filePathres, &buffer, file.Size, minio.PutObjectOptions{ + ContentType: "application/octet-stream", + }) + if err != nil { + global.Logger.Error("上传文件到minio失败", zap.Any("err", err.Error())) + return "", "", errors.New("上传文件到minio失败, err:" + err.Error()) + } + //http://127.0.0.1:9000/planting-fun/uploads/2025-09-17/211476f3837fc7acbaebf0f901c1bd68.png + return global.Config.Minio.BucketUrl + "/" + info.Key, filePathres, nil + +} + +func (m *Minio) DeleteFile(key string) error { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + + err := m.Client.RemoveObject(ctx, m.bucket, key, minio.RemoveObjectOptions{}) + return err +} diff --git a/utils/upload/oss_instance.go b/utils/upload/oss_instance.go new file mode 100644 index 0000000..fd64016 --- /dev/null +++ b/utils/upload/oss_instance.go @@ -0,0 +1,31 @@ +package upload + +import ( + "fmt" + "mime/multipart" + "sundynix-go/global" +) + +// Oss 对象存储接口 +type Oss interface { + UploadFile(file *multipart.FileHeader) (string, string, error) + DeleteFile(key string) error +} + +// OssInstance 实例化oos方法 +func OssInstance() Oss { + switch global.Config.System.OssType { + case "local": + fmt.Println("local") + case "tencent-cos": + return &TencentCOS{} + case "minio": + minioClient, err := GetMinio(global.Config.Minio.Endpoint, global.Config.Minio.AccessKeyId, global.Config.Minio.AccessKeySecret, global.Config.Minio.BucketName, global.Config.Minio.UseSSL) + if err != nil { + global.Logger.Warn("minio初始化失败,请检查minio可用性或安全配置:" + err.Error()) + panic("minio初始化失败,请检查minio可用性或安全配置") + } + return minioClient + } + return nil +} diff --git a/utils/upload/tencent_cos.go b/utils/upload/tencent_cos.go new file mode 100644 index 0000000..440f7b6 --- /dev/null +++ b/utils/upload/tencent_cos.go @@ -0,0 +1,60 @@ +package upload + +import ( + "context" + "errors" + "fmt" + "mime/multipart" + "net/http" + "net/url" + "sundynix-go/global" + "time" + + "github.com/tencentyun/cos-go-sdk-v5" + "go.uber.org/zap" +) + +type TencentCOS struct{} + +// NewClient 创建一个腾讯云COS客户端 +func NewClient() *cos.Client { + urlStr, _ := url.Parse("https://" + global.Config.TencentCOS.Bucket + ".cos." + global.Config.TencentCOS.Region + ".myqcloud.com") + baseURL := &cos.BaseURL{BucketURL: urlStr} + client := cos.NewClient(baseURL, &http.Client{ + Transport: &cos.AuthorizationTransport{ + SecretID: global.Config.TencentCOS.SecretID, + SecretKey: global.Config.TencentCOS.SecretKey, + }, + }) + return client +} + +// UploadFile upload file to COS +func (*TencentCOS) UploadFile(file *multipart.FileHeader) (string, string, error) { + client := NewClient() + f, openError := file.Open() + if openError != nil { + global.Logger.Error("function file.Open() failed", zap.Any("err", openError.Error())) + return "", "", errors.New("function file.Open() failed, err:" + openError.Error()) + } + defer f.Close() // 创建文件 defer 关闭 + fileKey := fmt.Sprintf("%d%s", time.Now().Unix(), file.Filename) + + _, err := client.Object.Put(context.Background(), global.Config.TencentCOS.PathPrefix+"/"+fileKey, f, nil) + if err != nil { + panic(err) + } + return global.Config.TencentCOS.BaseURL + "/" + global.Config.TencentCOS.PathPrefix + "/" + fileKey, fileKey, nil +} + +// DeleteFile delete file form COS +func (*TencentCOS) DeleteFile(key string) error { + client := NewClient() + name := global.Config.TencentCOS.PathPrefix + "/" + key + _, err := client.Object.Delete(context.Background(), name) + if err != nil { + global.Logger.Error("function bucketManager.Delete() failed", zap.Any("err", err.Error())) + return errors.New("function bucketManager.Delete() failed, err:" + err.Error()) + } + return nil +} diff --git a/utils/validator.go b/utils/validator.go new file mode 100644 index 0000000..a56dac0 --- /dev/null +++ b/utils/validator.go @@ -0,0 +1,294 @@ +package utils + +import ( + "errors" + "reflect" + "regexp" + "strconv" + "strings" +) + +type Rules map[string][]string + +type RulesMap map[string]Rules + +var CustomizeMap = make(map[string]Rules) + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: RegisterRule +//@description: 注册自定义规则方案建议在路由初始化层即注册 +//@param: key string, rule Rules +//@return: err error + +func RegisterRule(key string, rule Rules) (err error) { + if CustomizeMap[key] != nil { + return errors.New(key + "已注册,无法重复注册") + } else { + CustomizeMap[key] = rule + return nil + } +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: NotEmpty +//@description: 非空 不能为其对应类型的0值 +//@return: string + +func NotEmpty() string { + return "notEmpty" +} + +// @author: [zooqkl](https://github.com/zooqkl) +// @function: RegexpMatch +// @description: 正则校验 校验输入项是否满足正则表达式 +// @param: rule string +// @return: string + +func RegexpMatch(rule string) string { + return "regexp=" + rule +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: Lt +//@description: 小于入参(<) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较 +//@param: mark string +//@return: string + +func Lt(mark string) string { + return "lt=" + mark +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: Le +//@description: 小于等于入参(<=) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较 +//@param: mark string +//@return: string + +func Le(mark string) string { + return "le=" + mark +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: Eq +//@description: 等于入参(==) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较 +//@param: mark string +//@return: string + +func Eq(mark string) string { + return "eq=" + mark +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: Ne +//@description: 不等于入参(!=) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较 +//@param: mark string +//@return: string + +func Ne(mark string) string { + return "ne=" + mark +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: Ge +//@description: 大于等于入参(>=) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较 +//@param: mark string +//@return: string + +func Ge(mark string) string { + return "ge=" + mark +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: Gt +//@description: 大于入参(>) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较 +//@param: mark string +//@return: string + +func Gt(mark string) string { + return "gt=" + mark +} + +// +//@author: [piexlmax](https://github.com/piexlmax) +//@function: Verify +//@description: 校验方法 +//@param: st interface{}, roleMap Rules(入参实例,规则map) +//@return: err error + +func Verify(st interface{}, roleMap Rules) (err error) { + compareMap := map[string]bool{ + "lt": true, + "le": true, + "eq": true, + "ne": true, + "ge": true, + "gt": true, + } + + typ := reflect.TypeOf(st) + val := reflect.ValueOf(st) // 获取reflect.Type类型 + + kd := val.Kind() // 获取到st对应的类别 + if kd != reflect.Struct { + return errors.New("expect struct") + } + num := val.NumField() + // 遍历结构体的所有字段 + for i := 0; i < num; i++ { + tagVal := typ.Field(i) + val := val.Field(i) + if tagVal.Type.Kind() == reflect.Struct { + if err = Verify(val.Interface(), roleMap); err != nil { + return err + } + } + if len(roleMap[tagVal.Name]) > 0 { + for _, v := range roleMap[tagVal.Name] { + switch { + case v == "notEmpty": + if isBlank(val) { + return errors.New(tagVal.Name + "值不能为空") + } + case strings.Split(v, "=")[0] == "regexp": + if !regexpMatch(strings.Split(v, "=")[1], val.String()) { + return errors.New(tagVal.Name + "格式校验不通过") + } + case compareMap[strings.Split(v, "=")[0]]: + if !compareVerify(val, v) { + return errors.New(tagVal.Name + "长度或值不在合法范围," + v) + } + } + } + } + } + return nil +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: compareVerify +//@description: 长度和数字的校验方法 根据类型自动校验 +//@param: value reflect.Value, VerifyStr string +//@return: bool + +func compareVerify(value reflect.Value, VerifyStr string) bool { + switch value.Kind() { + case reflect.String: + return compare(len([]rune(value.String())), VerifyStr) + case reflect.Slice, reflect.Array: + return compare(value.Len(), VerifyStr) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return compare(value.Uint(), VerifyStr) + case reflect.Float32, reflect.Float64: + return compare(value.Float(), VerifyStr) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return compare(value.Int(), VerifyStr) + default: + return false + } +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: isBlank +//@description: 非空校验 +//@param: value reflect.Value +//@return: bool + +func isBlank(value reflect.Value) bool { + switch value.Kind() { + case reflect.String, reflect.Slice: + return value.Len() == 0 + case reflect.Bool: + return !value.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return value.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return value.Uint() == 0 + case reflect.Float32, reflect.Float64: + return value.Float() == 0 + case reflect.Interface, reflect.Ptr: + return value.IsNil() + } + return reflect.DeepEqual(value.Interface(), reflect.Zero(value.Type()).Interface()) +} + +//@author: [piexlmax](https://github.com/piexlmax) +//@function: compare +//@description: 比较函数 +//@param: value interface{}, VerifyStr string +//@return: bool + +func compare(value interface{}, VerifyStr string) bool { + VerifyStrArr := strings.Split(VerifyStr, "=") + val := reflect.ValueOf(value) + switch val.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + VInt, VErr := strconv.ParseInt(VerifyStrArr[1], 10, 64) + if VErr != nil { + return false + } + switch { + case VerifyStrArr[0] == "lt": + return val.Int() < VInt + case VerifyStrArr[0] == "le": + return val.Int() <= VInt + case VerifyStrArr[0] == "eq": + return val.Int() == VInt + case VerifyStrArr[0] == "ne": + return val.Int() != VInt + case VerifyStrArr[0] == "ge": + return val.Int() >= VInt + case VerifyStrArr[0] == "gt": + return val.Int() > VInt + default: + return false + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + VInt, VErr := strconv.Atoi(VerifyStrArr[1]) + if VErr != nil { + return false + } + switch { + case VerifyStrArr[0] == "lt": + return val.Uint() < uint64(VInt) + case VerifyStrArr[0] == "le": + return val.Uint() <= uint64(VInt) + case VerifyStrArr[0] == "eq": + return val.Uint() == uint64(VInt) + case VerifyStrArr[0] == "ne": + return val.Uint() != uint64(VInt) + case VerifyStrArr[0] == "ge": + return val.Uint() >= uint64(VInt) + case VerifyStrArr[0] == "gt": + return val.Uint() > uint64(VInt) + default: + return false + } + case reflect.Float32, reflect.Float64: + VFloat, VErr := strconv.ParseFloat(VerifyStrArr[1], 64) + if VErr != nil { + return false + } + switch { + case VerifyStrArr[0] == "lt": + return val.Float() < VFloat + case VerifyStrArr[0] == "le": + return val.Float() <= VFloat + case VerifyStrArr[0] == "eq": + return val.Float() == VFloat + case VerifyStrArr[0] == "ne": + return val.Float() != VFloat + case VerifyStrArr[0] == "ge": + return val.Float() >= VFloat + case VerifyStrArr[0] == "gt": + return val.Float() > VFloat + default: + return false + } + default: + return false + } +} + +func regexpMatch(rule, matchStr string) bool { + return regexp.MustCompile(rule).MatchString(matchStr) +} diff --git a/utils/wechat/access_token.go b/utils/wechat/access_token.go new file mode 100644 index 0000000..cd6a0cf --- /dev/null +++ b/utils/wechat/access_token.go @@ -0,0 +1,54 @@ +package wechat + +import ( + "context" + "encoding/json" + "errors" + "io" + "log" + "net/http" + "sundynix-go/global" + "time" + + "github.com/redis/go-redis/v9" +) + +// GetMiniAccessToken 获取小程序的access_token +func GetMiniAccessToken() string { + ak, err := global.Redis.Get(context.Background(), "mini_access_token").Result() + if errors.Is(err, redis.Nil) { + // 从微信服务器获取 + //重新从微信服务器获取 + url := "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + global.Config.MiniProgram.AppId + "&secret=" + global.Config.MiniProgram.AppSecret + resp, err := http.Get(url) + if err != nil { + log.Fatalf("Error making GET request: %s", err) + } + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + log.Fatalf("Error closing response body: %s", err) + } + }(resp.Body) + body, err := io.ReadAll(resp.Body) + if err != nil { + log.Fatalf("Error reading response body: %s", err) + } + res := string(body) + + var data map[string]interface{} + err = json.Unmarshal([]byte(res), &data) + if err != nil { + log.Fatalf("Error unmarshalling JSON: %s", err) + } + ak = data["access_token"].(string) + ex := data["expires_in"].(float64) + global.Redis.Set(context.Background(), "mini_access_token", ak, time.Duration(ex)*time.Second) //秒 + } else if err != nil { + log.Fatalf("Error getting access token from Redis: %s", err) + } else { + return ak + } + return ak + +} diff --git a/utils/wechat/pay.go b/utils/wechat/pay.go new file mode 100644 index 0000000..bcd97b4 --- /dev/null +++ b/utils/wechat/pay.go @@ -0,0 +1,39 @@ +package wechat + +import ( + "context" + "fmt" + "sundynix-go/global" + + "github.com/wechatpay-apiv3/wechatpay-go/core" + "github.com/wechatpay-apiv3/wechatpay-go/core/option" + "github.com/wechatpay-apiv3/wechatpay-go/utils" +) + +// GetWxPayClient 初始化微信支付客户端 +func GetWxPayClient() (*core.Client, error) { + + //2.加载私钥 + mchPrivateKey, err := utils.LoadPrivateKeyWithPath(global.Config.WechatPay.PrivateKeyPath) + if err != nil { + return nil, err + } + mchPublicKey, err := utils.LoadPublicKeyWithPath(global.Config.WechatPay.PublicKeyPath) + if err != nil { + return nil, err + } + ctx := context.Background() + // 3. 创建客户端配置 使用商户私钥等初始化 client,并使它具有自动定时获取微信支付平台证书的能力 + opts := []core.ClientOption{ + option.WithWechatPayPublicKeyAuthCipher(global.Config.WechatPay.MchId, + global.Config.WechatPay.MchCertificateSerialNumber, + mchPrivateKey, global.Config.WechatPay.PublicKeyId, mchPublicKey), // 自动处理签名/验签 + } + client, err := core.NewClient(ctx, opts...) + + if err != nil { + fmt.Printf("new wechat pay client err:%s", err) + } + return client, err + +}