From d79beb4663da18b5cf727aae9e0680a08d868dc2 Mon Sep 17 00:00:00 2001 From: Blizzard Date: Sat, 28 Feb 2026 15:56:26 +0800 Subject: [PATCH] init: radio init commit --- .idea/.gitignore | 10 + .idea/go.imports.xml | 11 + .idea/modules.xml | 8 + .idea/morning-radio-backend.iml | 9 + .idea/vcs.xml | 6 + Walkthrough.md | 173 -- api/v1/enter.go | 2 + api/v1/radio/category.go | 159 ++ api/v1/radio/channel.go | 146 ++ api/v1/radio/enter.go | 21 + api/v1/radio/interaction.go | 253 +++ api/v1/radio/program.go | 140 ++ api/v1/radio/subscription.go | 134 ++ api/v1/system/sys_client.go | 10 +- api/v1/system/sys_menu.go | 14 +- api/v1/system/sys_role.go | 8 +- api/v1/system/sys_user.go | 16 +- config-dev.yaml | 11 +- config/config.go | 5 +- config/constans.go | 22 - config/service_account.go | 6 - docs/docs.go | 2332 ------------------------- docs/swagger.json | 2332 ------------------------- docs/swagger.yaml | 1482 ---------------- initialize/gorm.go | 7 + initialize/router.go | 9 + log/2026-02-25/error.log | 4 - log/2026-02-25/info.log | 12 - log/2026-02-28/error.log | 9 + log/2026-02-28/info.log | 3 + main.go | 2 +- model/commom/request/common.go | 3 + model/radio/radio_category.go | 23 + model/radio/radio_channel.go | 26 + model/radio/radio_comment.go | 19 + model/radio/radio_favorite.go | 16 + model/radio/radio_history.go | 18 + model/radio/radio_like.go | 16 + model/radio/radio_program.go | 28 + model/radio/radio_subscription.go | 17 + model/radio/radio_user.go | 29 + model/radio/request/category.go | 32 + model/radio/request/channel.go | 37 + model/radio/request/interaction.go | 68 + model/radio/request/program.go | 39 + model/radio/response/category.go | 43 + model/radio/response/channel.go | 29 + plans/radio_module_plan.md | 350 ++++ router/enter.go | 2 + router/radio/category_router.go | 19 + router/radio/channel_router.go | 18 + router/radio/enter.go | 21 + router/radio/interaction_router.go | 38 + router/radio/program_router.go | 18 + router/radio/subscription_router.go | 17 + service/enter.go | 2 + service/radio/category_service.go | 87 + service/radio/channel_service.go | 127 ++ service/radio/enter.go | 11 + service/radio/interaction_service.go | 176 ++ service/radio/program_service.go | 98 ++ service/radio/subscription_service.go | 144 ++ service/system/sys_user.go | 12 + 63 files changed, 2540 insertions(+), 6399 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/go.imports.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/morning-radio-backend.iml create mode 100644 .idea/vcs.xml delete mode 100644 Walkthrough.md create mode 100644 api/v1/radio/category.go create mode 100644 api/v1/radio/channel.go create mode 100644 api/v1/radio/enter.go create mode 100644 api/v1/radio/interaction.go create mode 100644 api/v1/radio/program.go create mode 100644 api/v1/radio/subscription.go delete mode 100644 config/constans.go delete mode 100644 config/service_account.go delete mode 100644 log/2026-02-25/error.log delete mode 100644 log/2026-02-25/info.log create mode 100644 log/2026-02-28/error.log create mode 100644 log/2026-02-28/info.log create mode 100644 model/radio/radio_category.go create mode 100644 model/radio/radio_channel.go create mode 100644 model/radio/radio_comment.go create mode 100644 model/radio/radio_favorite.go create mode 100644 model/radio/radio_history.go create mode 100644 model/radio/radio_like.go create mode 100644 model/radio/radio_program.go create mode 100644 model/radio/radio_subscription.go create mode 100644 model/radio/radio_user.go create mode 100644 model/radio/request/category.go create mode 100644 model/radio/request/channel.go create mode 100644 model/radio/request/interaction.go create mode 100644 model/radio/request/program.go create mode 100644 model/radio/response/category.go create mode 100644 model/radio/response/channel.go create mode 100644 plans/radio_module_plan.md create mode 100644 router/radio/category_router.go create mode 100644 router/radio/channel_router.go create mode 100644 router/radio/enter.go create mode 100644 router/radio/interaction_router.go create mode 100644 router/radio/program_router.go create mode 100644 router/radio/subscription_router.go create mode 100644 service/radio/category_service.go create mode 100644 service/radio/channel_service.go create mode 100644 service/radio/enter.go create mode 100644 service/radio/interaction_service.go create mode 100644 service/radio/program_service.go create mode 100644 service/radio/subscription_service.go diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..ab1f416 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/go.imports.xml b/.idea/go.imports.xml new file mode 100644 index 0000000..d7202f0 --- /dev/null +++ b/.idea/go.imports.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..54a733d --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/morning-radio-backend.iml b/.idea/morning-radio-backend.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/morning-radio-backend.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Walkthrough.md b/Walkthrough.md deleted file mode 100644 index 100c6d0..0000000 --- a/Walkthrough.md +++ /dev/null @@ -1,173 +0,0 @@ -# 项目改造 Walkthrough:从"植趣"到"早安电台" - -## 一、项目概述 - -本次改造将后端项目从**植物养护小程序(植趣)**全面转型为**早安电台微信小程序**后端。保留了底层的系统模块(用户认证、OSS 文件管理等),删除了全部植物相关的业务模块,新建了早安电台核心业务模块。 - ---- - -## 二、技术栈(保持不变) - -| 技术 | 版本/框架 | 用途 | -|------|-----------|------| -| Go | 1.24 | 后端语言 | -| Gin | v1.10 | Web 框架 | -| GORM | v1.26 | ORM 框架 | -| MySQL | - | 主数据库 | -| Redis | - | 缓存与分布式锁 | -| MinIO | - | 对象存储(音频文件) | -| Viper | v1.20 | 配置管理 | -| Zap | v1.27 | 结构化日志 | -| robfig/cron | v3 | 定时任务调度 | -| JWT | v5 | 接口鉴权 | - ---- - -## 三、已删除的模块(植物相关) - -### 3.1 删除的目录 - -| 目录 | 文件数 | 说明 | -|------|--------|------| -| `api/v1/plant/` | 12 个文件 | 植物 API 处理器 | -| `router/plant/` | 12 个文件 | 植物路由定义 | -| `service/plant/` | 13 个文件 | 植物业务逻辑 | -| `model/plant/` | 22+ 个文件 | 植物数据模型(含 request/response 子目录) | -| `task/` | 2 个文件 | 植物养护定时任务 | -| `config/baidu_img_classify.go` | 1 个文件 | 百度植物识别配置 | - -### 3.2 删除的植物功能清单 - -- 我的植物管理(CRUD、养护计划、养护任务、成长记录) -- 社区帖子(发帖、评论、点赞、话题) -- 植物百科(分类、百科条目) -- 植物 OCR 识别 -- 等级配置系统 -- 徽章成就系统(植物养护相关) -- 用户资料管理 -- 兑换中心(阳光值兑换商品) -- 微信内容安全回调 -- 植物养护提醒定时任务 - ---- - -## 四、新建的模块(早安电台) - -### 4.1 数据模型层 `model/radio/` - -| 文件 | 表名 | 说明 | -|------|------|------| -| `audio_content.go` | `sundynix_audio_content` | 音频内容表:标题、音频URL、封面、专辑名、场景、时长、脚本文本、来源类型、状态 | -| `crawl_source.go` | `sundynix_crawl_source` | 抓取源配置表:名称、类型(github/producthunt/rss)、API地址、查询参数、场景、抓取间隔 | -| `crawl_record.go` | `sundynix_crawl_record` | 抓取记录表:原始数据、清洗文本、AI摘要、处理状态、关联音频ID | -| `play_record.go` | `sundynix_play_record` | 播放记录表:用户ID、音频ID、播放进度(秒)、是否完成、完成时间 | -| `user_medal.go` | `sundynix_user_medal` | 用户勋章表:用户ID、勋章类型、获得时间 | -| `user_subscription.go` | `sundynix_user_subscription` | 用户订阅表:用户ID、场景、是否启用、推送时间 | -| `request/audio.go` | - | 请求参数:音频列表请求、播放状态更新请求、订阅更新请求 | -| `response/audio.go` | - | 响应参数:音频列表响应、播放状态响应、勋章列表响应、订阅响应 | - -### 4.2 服务层 `service/radio/` - -| 文件 | 核心功能 | -|------|----------| -| `audio_content.go` | 音频列表分页查询(支持场景筛选)、音频详情、最新音频获取 | -| `crawl.go` | 获取启用的抓取源、创建/更新抓取记录、获取待处理记录 | -| `play_record.go` | 更新播放进度、断点续传支持、**完成收听时自动触发勋章解锁** | -| `user_medal.go` | 查询用户勋章列表、检查勋章拥有状态 | -| `user_subscription.go` | 查询/创建/更新用户订阅偏好 | -| `enter.go` | ServiceGroup 聚合入口 | - -### 4.3 API 处理器层 `api/v1/radio/` - -| 文件 | 接口 | 说明 | -|------|------|------| -| `audio_content.go` | `GET /audio/list` | 分页获取音频列表,支持 `scene` 筛选 | -| | `GET /audio/detail` | 根据 ID 获取音频详情 | -| | `GET /audio/latest` | 获取指定场景的最新音频 | -| `play_record.go` | `POST /audio/play-status` | 更新播放状态,完成时返回新解锁的勋章 | -| | `GET /audio/play-record` | 获取播放进度(用于 `startTime` 断点续传) | -| `user_medal.go` | `GET /medal/list` | 获取当前用户的所有勋章 | -| `user_subscription.go` | `GET /subscription/info` | 获取用户的场景订阅列表 | -| | `POST /subscription/update` | 更新/创建订阅设置 | - -### 4.4 路由层 `router/radio/` - -| 文件 | 路由组前缀 | 注册的 API | -|------|-----------|-----------| -| `audio_router.go` | `/audio` | list, detail, latest | -| `play_record_router.go` | `/audio` | play-status, play-record | -| `medal_router.go` | `/medal` | list | -| `subscription_router.go` | `/subscription` | info, update | - ---- - -## 五、修改的全局文件 - -### 5.1 聚合入口文件(enter.go) - -- `api/v1/enter.go` — `PlantApiGroup` → `RadioApiGroup` -- `router/enter.go` — `Plant plant.RouterGroup` → `Radio radio.RouterGroup` -- `service/enter.go` — `PlantServiceGroup` → `RadioServiceGroup` - -### 5.2 初始化文件(initialize/) - -- `router.go` — 全部 `plantGroup.InitXxxRouter()` → `radioGroup.InitXxxRouter()`,项目名称更新为 `sundynix-morning-radio` -- `gorm.go` — 全部 `plant.Xxx{}` 模型迁移 → `radio.Xxx{}` 模型迁移 -- `timer.go` — 植物养护提醒 → 早安电台数据抓取定时任务(每2小时) + 音频生成任务(每天凌晨5点) - -### 5.3 全局常量(global/enums.go) - -移除植物养护相关常量,新增: - -- **勋章类型**:`EARLY_BIRD_7/30/100`, `EXPLORER`, `ALL_ROUNDER`, `FIRST_LISTEN` -- **场景定义**:`morning_career`, `morning_health`, `morning_life`, `morning_study` -- **抓取源类型**:`github`, `producthunt`, `rss`, `huggingface`, `webscrape` -- **音频状态**:草稿(0)、已发布(1)、已下线(2) -- **抓取状态**:pending、processed、failed - -### 5.4 配置文件 - -- `config/config.go` — 移除 `BaiduImgClassify` 字段 -- `config-dev.yaml` — 移除百度植物识别配置,更新 MinIO bucket 为 `sundynix-audios`,更新 RocketMQ topic,更新 Zap 日志前缀 - ---- - -## 六、核心业务流程 - -### 6.1 数据抓取与音频生成流水线 - -``` -定时任务(每2小时) → 抓取数据源 → 清洗HTML(Goquery) → AI摘要改写 → TTS语音合成 → MinIO存储 → CDN分发 -``` - -### 6.2 用户收听与勋章系统 - -``` -获取音频列表 → 播放音频 → 实时上报进度(断点续传) → 完成收听 → 触发勋章检查 → 解锁成就 -``` - -### 6.3 场景化订阅 - -``` -用户选择场景(硬核职场/效率健康/极简生活/知识胶囊) → 设置推送时间 → 按需接收个性化早安电台 -``` - ---- - -## 七、编译验证 - -```bash -$ go build ./... -# 编译通过,无错误 -``` - ---- - -## 八、后续开发建议(TODO) - -1. **抓取服务实现**:接入 GitHub API、Product Hunt GraphQL API、RSS 订阅源 -2. **AI 摘要服务**:集成 DeepSeek/OpenAI API,实现抓取数据 → 口播脚本的自动改写 -3. **TTS 语音合成**:接入腾讯云 TTS 或其他 TTS 服务,生成 AAC 格式音频 -4. **CDN 配置**:为 MinIO 配置 Nginx HTTPS 反向代理 + CDN 边缘缓存 -5. **小程序前端**:集成 `BackgroundAudioManager` 实现后台播放、锁屏控件、断点续传 -6. **AIGC 合规标识**:在前端 UI 添加"AI 生成内容"标识 diff --git a/api/v1/enter.go b/api/v1/enter.go index 592d642..2f0be2f 100644 --- a/api/v1/enter.go +++ b/api/v1/enter.go @@ -1,6 +1,7 @@ package v1 import ( + "sundynix-go/api/v1/radio" "sundynix-go/api/v1/system" ) @@ -9,4 +10,5 @@ var ApiGroupApp = new(ApiGroup) // ApiGroup 路由组 type ApiGroup struct { SystemApiGroup system.ApiGroup + RadioApiGroup radio.ApiGroup } diff --git a/api/v1/radio/category.go b/api/v1/radio/category.go new file mode 100644 index 0000000..56f6c74 --- /dev/null +++ b/api/v1/radio/category.go @@ -0,0 +1,159 @@ +package radio + +import ( + "sundynix-go/global" + common "sundynix-go/model/commom/request" + "sundynix-go/model/commom/response" + "sundynix-go/model/radio/request" + + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type CategoryApi struct{} + +// GetCategoryPage 获取分类列表 +// @Tags 分类管理 +// @Summary 获取分类列表 +// @Accept application/json +// @Produce application/json +// @Param data body request.GetCategoryList true "分页查询" +// @Success 200 {object} response.Response +// @Router /radio/category/page [post] +func (a *CategoryApi) GetCategoryPage(c *gin.Context) { + var req request.GetCategoryList + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + list, total, err := categoryService.GetCategoryList(req) + if err != nil { + global.Logger.Error("获取分类列表失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + response.OkWithData(response.PageResult{ + List: list, + Total: total, + Page: req.Current, + PageSize: req.PageSize, + }, c) +} + +// GetCategoryList 获取分类列表 +// @Tags 分类管理 +// @Summary 获取分类列表 +// @Produce application/json +// @Success 200 {object} response.Response +// @Router /radio/category/list [get] +func (a *CategoryApi) GetCategoryList(c *gin.Context) { + list, err := categoryService.GetAllCategory() + if err != nil { + global.Logger.Error("获取分类列表失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + response.OkWithData(response.ListResult{ + List: list, + }, c) +} + +// GetCategoryDetail 获取分类详情 +// @Tags 分类管理 +// @Summary 获取分类详情 +// @Produce application/json +// @Param id query string true "分类ID" +// @Success 200 {object} response.Response +// @Router /radio/category/detail [get] +func (a *CategoryApi) GetCategoryDetail(c *gin.Context) { + id := c.Query("id") + if id == "" { + response.FailWithMsg("参数错误: id不能为空", c) + return + } + + category, err := categoryService.GetCategoryById(id) + if err != nil { + global.Logger.Error("获取分类详情失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithData(category, c) +} + +// SaveCategory 保存分类 +// @Tags 分类管理 +// @Summary 保存分类 +// @Accept application/json +// @Produce application/json +// @Param data body request.SaveCategory true "分类信息" +// @Success 200 {object} response.Response +// @Router /radio/category/save [post] +func (a *CategoryApi) SaveCategory(c *gin.Context) { + var req request.SaveCategory + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + if err := categoryService.SaveCategory(req); err != nil { + global.Logger.Error("保存分类失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithMsg("保存成功", c) +} + +// UpdateCategory 更新分类 +// @Tags 分类管理 +// @Summary 更新分类 +// @Accept application/json +// @Produce application/json +// @Param data body request.UpdateCategory true "Success 200 {分类信息" +// @object} response.Response +// @Router /radio/category/update [post] +func (a *CategoryApi) UpdateCategory(c *gin.Context) { + var req request.UpdateCategory + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + if err := categoryService.UpdateCategory(req); err != nil { + global.Logger.Error("更新分类失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithMsg("更新成功", c) +} + +// DeleteCategory 删除分类 +// @Tags 分类管理 +// @Summary 删除分类 +// @Produce application/json +// @Param data body common.GetById true "分类ID" +// @Success 200 {object} response.Response +// @Router /radio/category/delete [post] +func (a *CategoryApi) DeleteCategory(c *gin.Context) { + var req common.IdReq + err := c.ShouldBindJSON(&req) + if err != nil || req.Id == "" { + response.FailWithMsg("参数错误: id不能为空", c) + return + } + + if err := categoryService.DeleteCategory(req.Id); err != nil { + global.Logger.Error("删除分类失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithMsg("删除成功", c) +} diff --git a/api/v1/radio/channel.go b/api/v1/radio/channel.go new file mode 100644 index 0000000..b3c93b3 --- /dev/null +++ b/api/v1/radio/channel.go @@ -0,0 +1,146 @@ +package radio + +import ( + "sundynix-go/global" + common "sundynix-go/model/commom/request" + + "sundynix-go/model/commom/response" + "sundynix-go/model/radio/request" + "sundynix-go/utils/auth" + + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type ChannelApi struct{} + +// GetChannelList 获取频道列表 +// @Tags 频道管理 +// @Summary 获取频道列表 +// @Accept application/json +// @Produce application/json +// @Param data body request.GetChannelList true "分页查询" +// @Success 200 {object} response.Response +// @Router /radio/channel/list [post] +func (a *ChannelApi) GetChannelList(c *gin.Context) { + userId := auth.GetUserId(c) + var req request.GetChannelList + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + list, total, err := channelService.GetChannelList(userId, req) + if err != nil { + global.Logger.Error("获取频道列表失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithData(response.PageResult{ + List: list, + Total: total, + Page: req.Current, + PageSize: req.PageSize, + }, c) +} + +// GetChannelDetail 获取频道详情 +// @Tags 频道管理 +// @Summary 获取频道详情 +// @Accept application/json +// @Produce application/json +// @Param id query string true "频道ID" +// @Success 200 {object} response.Response +// @Router /radio/channel/detail [get] +func (a *ChannelApi) GetChannelDetail(c *gin.Context) { + userId := auth.GetUserId(c) + id := c.Query("id") + if id == "" { + response.FailWithMsg("参数错误: id不能为空", c) + return + } + + channel, err := channelService.GetChannelById(userId, id) + if err != nil { + global.Logger.Error("获取频道详情失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + response.OkWithData(channel, c) +} + +// SaveChannel 保存频道 +// @Tags 频道管理 +// @Summary 保存频道 +// @Accept application/json +// @Produce application/json +// @Param data body request.SaveChannel true "频道信息" +// @Success 200 {object} response.Response +// @Router /radio/channel/save [post] +func (a *ChannelApi) SaveChannel(c *gin.Context) { + var req request.SaveChannel + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + if err := channelService.SaveChannel(req); err != nil { + global.Logger.Error("保存频道失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithMsg("保存成功", c) +} + +// UpdateChannel 更新频道 +// @Tags 频道管理 +// @Summary 更新频道 +// @Accept application/json +// @Produce application/json +// @Param data body request.UpdateChannel true "频道信息" +// @Success 200 {object} response.Response +// @Router /radio/channel/update [post] +func (a *ChannelApi) UpdateChannel(c *gin.Context) { + var req request.UpdateChannel + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + if err := channelService.UpdateChannel(req); err != nil { + global.Logger.Error("更新频道失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithMsg("更新成功", c) +} + +// DeleteChannel 删除频道 +// @Tags 频道管理 +// @Summary 删除频道 +// @Produce json +// @Param data body common.GetById true "频道ID" +// @Success 200 {object} response.Response +// @Router /radio/channel/delete [post] +func (a *ChannelApi) DeleteChannel(c *gin.Context) { + var req common.IdReq + err := c.ShouldBindJSON(&req) + if err != nil || req.Id == "" { + response.FailWithMsg("参数错误: id不能为空", c) + return + } + + if err := channelService.DeleteChannel(req.Id); err != nil { + global.Logger.Error("删除频道失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithMsg("删除成功", c) +} diff --git a/api/v1/radio/enter.go b/api/v1/radio/enter.go new file mode 100644 index 0000000..acdf051 --- /dev/null +++ b/api/v1/radio/enter.go @@ -0,0 +1,21 @@ +package radio + +import "sundynix-go/service" + +type ApiGroup struct { + CategoryApi + ChannelApi + ProgramApi + SubscriptionApi + InteractionApi +} + +var ApiGroupApp = new(ApiGroup) + +var ( + categoryService = service.GroupApp.RadioServiceGroup.CategoryService + channelService = service.GroupApp.RadioServiceGroup.ChannelService + programService = service.GroupApp.RadioServiceGroup.ProgramService + subscriptionService = service.GroupApp.RadioServiceGroup.SubscriptionService + interactionService = service.GroupApp.RadioServiceGroup.InteractionService +) diff --git a/api/v1/radio/interaction.go b/api/v1/radio/interaction.go new file mode 100644 index 0000000..39fe50e --- /dev/null +++ b/api/v1/radio/interaction.go @@ -0,0 +1,253 @@ +package radio + +import ( + "sundynix-go/global" + "sundynix-go/model/commom/response" + "sundynix-go/model/radio/request" + "sundynix-go/utils/auth" + + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type InteractionApi struct{} + +// AddHistory 添加收听历史 +// @Tags 用户互动 +// @Summary 添加收听历史 +// @Produce json +// @Param data body request.AddHistory true "收听信息" +// @Success 200 {object} response.Response +// @Router /radio/history/add [post] +func (a *InteractionApi) AddHistory(c *gin.Context) { + userId := auth.GetUserId(c) + var req request.AddHistory + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + if err := interactionService.AddHistory(userId, req); err != nil { + global.Logger.Error("添加收听历史失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithMsg("添加成功", c) +} + +// GetHistoryList 获取收听历史列表 +// @Tags 用户互动 +// @Summary 获取收听历史列表 +// @Produce json +// @Param data body request.GetHistoryList true "分页查询" +// @Success 200 {object} response.Response +// @Router /radio/history/list [post] +func (a *InteractionApi) GetHistoryList(c *gin.Context) { + userId := auth.GetUserId(c) + var req request.GetHistoryList + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + list, total, err := interactionService.GetHistoryList(userId, req) + if err != nil { + global.Logger.Error("获取收听历史失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithData(response.PageResult{ + List: list, + Total: total, + Page: req.Current, + }, c) +} + +// ToggleLike 切换点赞状态 +// @Tags 用户互动 +// @Summary 切换点赞状态 +// @Produce json +// @Param data body request.ToggleLike true "节目ID" +// @Success 200 {object} response.Response +// @Router /radio/like/toggle [post] +func (a *InteractionApi) ToggleLike(c *gin.Context) { + userId := auth.GetUserId(c) + var req request.ToggleLike + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + isLiked, err := interactionService.ToggleLike(userId, req.ProgramId) + if err != nil { + global.Logger.Error("操作失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithData(isLiked, c) +} + +// GetFavoriteList 获取收藏列表 +// @Tags 用户互动 +// @Summary 获取收藏列表 +// @Produce json +// @Param data body request.GetFavoriteList true "分页查询" +// @Success 200 {object} response.Response +// @Router /radio/favorite/list [post] +func (a *InteractionApi) GetFavoriteList(c *gin.Context) { + userId := auth.GetUserId(c) + var req request.GetFavoriteList + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + list, total, err := interactionService.GetFavoriteList(userId, req) + if err != nil { + global.Logger.Error("获取收藏列表失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithData(response.PageResult{ + List: list, + Total: total, + Page: req.Current, + }, c) +} + +// AddFavorite 添加收藏 +// @Tags 用户互动 +// @Summary 添加收藏 +// @Produce json +// @Param data body request.AddFavorite true "节目ID" +// @Success 200 {object} response.Response +// @Router /radio/favorite/add [post] +func (a *InteractionApi) AddFavorite(c *gin.Context) { + userId := auth.GetUserId(c) + var req request.AddFavorite + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + if err := interactionService.AddFavorite(userId, req.ProgramId); err != nil { + global.Logger.Error("添加收藏失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithMsg("收藏成功", c) +} + +// RemoveFavorite 取消收藏 +// @Tags 用户互动 +// @Summary 取消收藏 +// @Produce json +// @Param data body request.RemoveFavorite true "节目ID" +// @Success 200 {object} response.Response +// @Router /radio/favorite/remove [post] +func (a *InteractionApi) RemoveFavorite(c *gin.Context) { + userId := auth.GetUserId(c) + var req request.RemoveFavorite + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + if err := interactionService.RemoveFavorite(userId, req.ProgramId); err != nil { + global.Logger.Error("取消收藏失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithMsg("取消收藏成功", c) +} + +// GetCommentList 获取评论列表 +// @Tags 用户互动 +// @Summary 获取评论列表 +// @Produce json +// @Param data body request.GetCommentList true "分页查询" +// @Success 200 {object} response.Response +// @Router /radio/comment/list [post] +func (a *InteractionApi) GetCommentList(c *gin.Context) { + var req request.GetCommentList + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + list, total, err := interactionService.GetCommentList(req.ProgramId, req) + if err != nil { + global.Logger.Error("获取评论列表失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithData(response.PageResult{ + List: list, + Total: total, + Page: req.Current, + }, c) +} + +// AddComment 添加评论 +// @Tags 用户互动 +// @Summary 添加评论 +// @Produce json +// @Param data body request.AddComment true "评论信息" +// @Success 200 {object} response.Response +// @Router /radio/comment/add [post] +func (a *InteractionApi) AddComment(c *gin.Context) { + userId := auth.GetUserId(c) + var req request.AddComment + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + if err := interactionService.AddComment(userId, req); err != nil { + global.Logger.Error("添加评论失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithMsg("评论成功", c) +} + +// DeleteComment 删除评论 +// @Tags 用户互动 +// @Summary 删除评论 +// @Produce json +// @Param data body request.DeleteComment true "评论ID" +// @Success 200 {object} response.Response +// @Router /radio/comment/delete [post] +func (a *InteractionApi) DeleteComment(c *gin.Context) { + userId := auth.GetUserId(c) + var req request.DeleteComment + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + if err := interactionService.DeleteComment(userId, req.CommentId); err != nil { + global.Logger.Error("删除评论失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithMsg("删除成功", c) +} diff --git a/api/v1/radio/program.go b/api/v1/radio/program.go new file mode 100644 index 0000000..59f3d50 --- /dev/null +++ b/api/v1/radio/program.go @@ -0,0 +1,140 @@ +package radio + +import ( + "sundynix-go/global" + common "sundynix-go/model/commom/request" + "sundynix-go/model/commom/response" + "sundynix-go/model/radio/request" + + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type ProgramApi struct{} + +// GetProgramList 获取节目列表 +// @Tags 节目管理 +// @Summary 获取节目列表 +// @Produce application/json +// @Param data body request.GetProgramList true "分页查询" +// @Success 200 {object} response.Response +// @Router /radio/program/list [post] +func (a *ProgramApi) GetProgramList(c *gin.Context) { + var req request.GetProgramList + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + list, total, err := programService.GetProgramList(req) + if err != nil { + global.Logger.Error("获取节目列表失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithData(response.PageResult{ + List: list, + Total: total, + Page: req.Current, + PageSize: req.PageSize, + }, c) +} + +// GetProgramDetail 获取节目详情 +// @Tags 节目管理 +// @Summary 获取节目详情 +// @Produce application/json +// @Param id query string true "节目ID" +// @Success 200 {object} response.Response +// @Router /radio/program/detail [get] +func (a *ProgramApi) GetProgramDetail(c *gin.Context) { + id := c.Query("id") + if id == "" { + response.FailWithMsg("参数错误: id不能为空", c) + return + } + program, err := programService.GetProgramById(id) + if err != nil { + global.Logger.Error("获取节目详情失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithData(program, c) +} + +// SaveProgram 保存节目 +// @Tags 节目管理 +// @Summary 保存节目 +// @Accept application/json +// @Produce application/json +// @Param data body request.SaveProgram true "节目信息" +// @Success 200 {object} response.Response +// @Router /radio/program/save [post] +func (a *ProgramApi) SaveProgram(c *gin.Context) { + var req request.SaveProgram + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + if err := programService.SaveProgram(req); err != nil { + global.Logger.Error("保存节目失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithMsg("保存成功", c) +} + +// UpdateProgram 更新节目 +// @Tags 节目管理 +// @Summary 更新节目 +// @Accept application/json +// @Produce application/json +// @Param data body request.UpdateProgram true "节目信息" +// @Success 200 {object} response.Response +// @Router /radio/program/update [post] +func (a *ProgramApi) UpdateProgram(c *gin.Context) { + var req request.UpdateProgram + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + if err := programService.UpdateProgram(req); err != nil { + global.Logger.Error("更新节目失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithMsg("更新成功", c) +} + +// DeleteProgram 删除节目 +// @Tags 节目管理 +// @Summary 删除节目 +// @Produce json +// @Param data body common.GetById true "节目ID" +// @Success 200 {object} response.Response +// @Router /radio/program/delete [post] +func (a *ProgramApi) DeleteProgram(c *gin.Context) { + var req common.IdsReq + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: id不能为空", c) + return + } + + if err := programService.DeleteProgram(req.Ids); err != nil { + global.Logger.Error("删除节目失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithMsg("删除成功", c) +} diff --git a/api/v1/radio/subscription.go b/api/v1/radio/subscription.go new file mode 100644 index 0000000..a824ce7 --- /dev/null +++ b/api/v1/radio/subscription.go @@ -0,0 +1,134 @@ +package radio + +import ( + "sundynix-go/global" + common "sundynix-go/model/commom/request" + "sundynix-go/model/commom/response" + "sundynix-go/model/radio/request" + "sundynix-go/utils/auth" + + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type SubscriptionApi struct{} + +// GetSubscriptionList 获取订阅列表 +// @Tags 订阅管理 +// @Summary 获取我的订阅列表 +// @Accept application/json +// @Produce application/json +// @Param data body request.GetSubscriptionList true "分页查询" +// @Success 200 {object} response.Response +// @Router /radio/subscription/list [post] +func (a *SubscriptionApi) GetSubscriptionList(c *gin.Context) { + userId := auth.GetUserId(c) + var req common.PageInfo + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + list, total, err := subscriptionService.GetUserSubscription(userId, req) + if err != nil { + global.Logger.Error("获取订阅列表失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithData(response.PageResult{ + List: list, + Total: total, + Page: req.Current, + }, c) +} + +// CanSubscribe 检查是否可以订阅 +// @Tags 订阅管理 +// @Summary 检查是否可以订阅 +// @Produce application/json +// @Param data body request.SubscribeChannel true "频道ID" +// @Success 200 {object} response.Response +// @Router /radio/subscription/can-subscribe [post] +func (a *SubscriptionApi) CanSubscribe(c *gin.Context) { + userId := auth.GetUserId(c) + var req request.SubscribeChannel + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + can, _, err := subscriptionService.CanSubscribe(userId, req.ChannelId) + if err != nil { + global.Logger.Error("检查订阅权限失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithData(can, c) +} + +// Subscribe 订阅频道 +// @Tags 订阅管理 +// @Summary 订阅频道 +// @Produce application/json +// @Param data body request.SubscribeChannel true "频道ID" +// @Success 200 {object} response.Response +// @Router /radio/subscription/subscribe [post] +func (a *SubscriptionApi) Subscribe(c *gin.Context) { + userId := auth.GetUserId(c) + var req request.SubscribeChannel + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + can, reason, err := subscriptionService.CanSubscribe(userId, req.ChannelId) + if err != nil { + global.Logger.Error("订阅失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + if !can { + response.FailWithMsg(reason, c) + return + } + + // 订阅类型 1:免费 + if err := subscriptionService.Subscribe(userId, req.ChannelId, 1); err != nil { + global.Logger.Error("订阅失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithMsg("订阅成功", c) +} + +// Unsubscribe 退订频道 +// @Tags 订阅管理 +// @Summary 退订频道 +// @Produce application/json +// @Param data body request.UnsubscribeChannel true "频道ID" +// @Success 200 {object} response.Response +// @Router /radio/subscription/unsubscribe [post] +func (a *SubscriptionApi) Unsubscribe(c *gin.Context) { + userId := auth.GetUserId(c) + var req request.UnsubscribeChannel + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + if err := subscriptionService.Unsubscribe(userId, req.ChannelId); err != nil { + global.Logger.Error("退订失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + + response.OkWithMsg("退订成功", c) +} diff --git a/api/v1/system/sys_client.go b/api/v1/system/sys_client.go index 3aea782..9da4bf3 100644 --- a/api/v1/system/sys_client.go +++ b/api/v1/system/sys_client.go @@ -17,7 +17,7 @@ type ClientApi struct { // SaveClient // @Tags 客户端管理 // @Summary 创建client -// @Security ApiKeyAuth +// @Security BasicAuth // @accept application/json // @Produce application/json // @Param data body system.Client true "client" @@ -42,7 +42,7 @@ func (s *ClientApi) SaveClient(c *gin.Context) { // UpdateClient // @Tags 客户端管理 // @Summary 更新client -// @Security ApiKeyAuth +// @Security BasicAuth // @accept application/json // @Produce application/json // @Param data body system.Client true "client" @@ -67,7 +67,7 @@ func (s *ClientApi) UpdateClient(c *gin.Context) { // GetClientList // @Tags 客户端管理 // @Summary 获取client列表 -// @Security ApiKeyAuth +// @Security BasicAuth // @accept application/json // @Produce application/json // @Param data body systemReq.GetClientList true "client" @@ -97,7 +97,7 @@ func (s *ClientApi) GetClientList(c *gin.Context) { // Delete // @Tags 客户端管理 // @Summary 删除client -// @Security ApiKeyAuth +// @Security BasicAuth // @accept application/json // @Produce application/json // @Param data body request.IdsReq true "ids" @@ -123,7 +123,7 @@ func (s *ClientApi) Delete(c *gin.Context) { // @Tags 客户端管理 // @Summary 获取client详情 // @Description id获取详情 -// @Security ApiKeyAuth +// @Security BasicAuth // @Produce application/json // @Param id query string true "id" // @Success 200 {object} response.Response{data=system.Client,msg=string} "获取client详情" diff --git a/api/v1/system/sys_menu.go b/api/v1/system/sys_menu.go index e5dbe2c..7d1c0b6 100644 --- a/api/v1/system/sys_menu.go +++ b/api/v1/system/sys_menu.go @@ -17,7 +17,7 @@ type MenuApi struct { // SaveMenu // @Tags 菜单管理 // @Summary 新增菜单 -// @Security ApiKeyAuth +// @Security BasicAuth // @accept application/json // @Produce application/json // @Param data body system.Menu false "menu" @@ -42,7 +42,7 @@ func (m *MenuApi) SaveMenu(c *gin.Context) { // UpdateMenu // @Tags 菜单管理 // @Summary 更新菜单 -// @Security ApiKeyAuth +// @Security BasicAuth // @accept application/json // @Produce application/json // @Param data body system.Menu false "menu" @@ -68,7 +68,7 @@ func (m *MenuApi) UpdateMenu(c *gin.Context) { // @Tags 菜单管理 // @Summary 删除menu // @Description 删除menu -// @Security ApiKeyAuth +// @Security BasicAuth // @Produce application/json // @Param id query string true "id" // @Success 200 {object} response.Response{msg=string} "详情" @@ -88,7 +88,7 @@ func (m *MenuApi) DeleteMenu(c *gin.Context) { // @Tags 菜单管理 // @Summary 获取menu详情 // @Description id获取详情 -// @Security ApiKeyAuth +// @Security BasicAuth // @Produce application/json // @Param id query string true "id" // @Success 200 {object} response.Response{data=system.Menu,msg=string} "详情" @@ -107,7 +107,7 @@ func (m *MenuApi) Detail(c *gin.Context) { // GetAllMenuTree // @Tags 菜单管理 // @Summary 获取所有菜单树 -// @Security ApiKeyAuth +// @Security BasicAuth // @Accept json // @Produce json // @Param data body systemReq.GetMenuTree true "菜单信息" @@ -132,7 +132,7 @@ func (m *MenuApi) GetAllMenuTree(c *gin.Context) { // GetUserMenuTree // @Tags 菜单管理 // @Summary 用户菜单数据 -// @Security ApiKeyAuth +// @Security BasicAuth // @Produce json // @Success 200 {object} response.Response{data=[]system.Menu,msg=string} "用户菜单数据" // @Router /menu/getUserMenuTree [get] @@ -150,7 +150,7 @@ func (m *MenuApi) GetUserMenuTree(c *gin.Context) { // Route // @Tags 菜单管理 // @Summary 用户路由 -// @Security ApiKeyAuth +// @Security BasicAuth // @Produce json // @Success 200 {object} response.Response{data=[]system.Menu,msg=string} "用户route" // @Router /menu/route [get] diff --git a/api/v1/system/sys_role.go b/api/v1/system/sys_role.go index c08bdca..58965ee 100644 --- a/api/v1/system/sys_role.go +++ b/api/v1/system/sys_role.go @@ -17,7 +17,7 @@ type RoleApi struct { // SaveRole // @tags 角色管理 // @Summary 创建角色 -// @Security ApiKeyAuth +// @Security BasicAuth // @accept json // @Produce json // @Param data body system.Role true "角色信息" @@ -42,7 +42,7 @@ func (a *RoleApi) SaveRole(context *gin.Context) { // UpdateRole // @tags 角色管理 // @Summary 修改角色 -// @Security ApiKeyAuth +// @Security BasicAuth // @accept application/json // @Produce application/json // @Param data body system.Role true "角色ID" @@ -121,7 +121,7 @@ func (a *RoleApi) Delete(context *gin.Context) { // Detail // @Tags 角色管理 // @Summary 角色详情 -// @Security ApiKeyAuth +// @Security BasicAuth // @Produce application/json // @Param id query string true "id" // @Success 200 {object} response.Response{data=system.Role} "角色详情" @@ -140,7 +140,7 @@ func (a *RoleApi) Detail(context *gin.Context) { // GrantMenu // @tags 角色管理 // @Summary 授权菜单给角色 -// @Security ApiKeyAuth +// @Security BasicAuth // @accept application/json // @Produce application/json // @Param data body systemreq.GrantMenu true "授权菜单给角色" diff --git a/api/v1/system/sys_user.go b/api/v1/system/sys_user.go index c0f6824..b133e83 100644 --- a/api/v1/system/sys_user.go +++ b/api/v1/system/sys_user.go @@ -18,7 +18,7 @@ type UserApi struct { // CurrentUser // @tags 用户管理 // @Summary 当前登录用户 -// @Security ApiKeyAuth +// @Security BasicAuth // @Produce json // @Success 200 {object} response.Response "{"code": 200, "data": {}, "msg": "添加成功"}" // @Router /user/info [get] @@ -36,7 +36,7 @@ func (u *UserApi) CurrentUser(c *gin.Context) { // SaveUser // @tags 用户管理 // @Summary 新增用户 -// @Security ApiKeyAuth +// @Security BasicAuth // @accept json // @Produce json // @Param data body system.User true "用户信息" @@ -60,7 +60,7 @@ func (u *UserApi) SaveUser(c *gin.Context) { // UpdateUser // @tags 用户管理 // @Summary 更新用户 -// @Security ApiKeyAuth +// @Security BasicAuth // @accept application/json // @Produce application/json // @Param data body system.User true "用户ID,用户信息" @@ -84,7 +84,7 @@ func (u *UserApi) UpdateUser(c *gin.Context) { // GetUserList // @tags 用户管理 // @Summary 获取用户列表 -// @Security ApiKeyAuth +// @Security BasicAuth // @accept application/json // @Produce application/json // @Param data body systemReq.GetUserList true "页码, 每页大小, 搜索条件" @@ -114,7 +114,7 @@ func (u *UserApi) GetUserList(c *gin.Context) { // Delete // @Tags 用户管理 // @Summary 删除用户 -// @Security ApiKeyAuth +// @Security BasicAuth // @accept application/json // @Produce application/json // @Param data body request.IdsReq true "批量删除用户" @@ -139,7 +139,7 @@ func (u *UserApi) Delete(c *gin.Context) { // Detail // @Tags 用户管理 // @Summary 获取用户详情 -// @Security ApiKeyAuth +// @Security BasicAuth // @Produce application/json // @Param id query string true "id" // @Success 200 {object} response.Response{data=system.User} "获取用户详情成功" @@ -158,7 +158,7 @@ func (u *UserApi) Detail(c *gin.Context) { // ChangePassword // @Tags 用户管理 // @Summary 修改密码 -// @Security ApiKeyAuth +// @Security BasicAuth // @Description 修改密码 // @accept json // @Produce application/json @@ -184,7 +184,7 @@ func (u *UserApi) ChangePassword(c *gin.Context) { // GrantRole // @Tags 用户管理 // @Summary 给用户分配角色 -// @Security ApiKeyAuth +// @Security BasicAuth // @accept application/json // @Produce application/json // @Param data body systemReq.GrantRole true "用户ID, 角色ID" diff --git a/config-dev.yaml b/config-dev.yaml index e2a7909..60f2690 100644 --- a/config-dev.yaml +++ b/config-dev.yaml @@ -17,11 +17,6 @@ mini-program: app-id: wxb463820bf36dd5d6 app-secret: 731784a74c76c6d31fa00bb847af2c7d -# 服务号 -service-account: - app-id: wxc236cddde8e7f863 - app-secret: 26c1fcecfc98a748d8916355623c975c - # 微信支付 wechat-pay: mch-id: 1735188493 # 商户号 @@ -37,8 +32,8 @@ minio: access-key-id: qP5QXP3g6Axw1hkwX21Y access-key-secret: sddT6J3S6yDn9m1wfth0pzelPg9KWmbHjMAUF5S9 base-path: "" - bucket-name: sundynix-audios - bucket-url: https://res.sundynix.cn/sundynix-audios + bucket-name: sundynix-radio + bucket-url: https://res.sundynix.cn/sundynix-radio endpoint: 129.28.103.17:3407 use-ssl: false @@ -46,7 +41,7 @@ rocket-mq: name-space: 192.168.100.140:9876 endpoint: 192.168.100.140:8081 # 5.x版本使用了proxy 默认proxy地址是8081 consumer-group: sundynix-radio - topic: sundynix-radio-audio-generate + topic: sundynix-radio-generate access-key: "" secret-key: "" enable-ssl: false diff --git a/config/config.go b/config/config.go index 7e7d181..d131e8c 100644 --- a/config/config.go +++ b/config/config.go @@ -13,7 +13,6 @@ type Config struct { 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"` //微信支付 + MiniProgram MiniProgram `mapstructure:"mini-program" json:"mini-program" yaml:"mini-program"` //小程序 + WechatPay WechatPay `mapstructure:"wechat-pay" json:"wechat-pay" yaml:"wechat-pay"` //微信支付 } diff --git a/config/constans.go b/config/constans.go deleted file mode 100644 index 1ce60ba..0000000 --- a/config/constans.go +++ /dev/null @@ -1,22 +0,0 @@ -package config - -// ActionType 定义养护行为类型 -type ActionType string - -const ( - ActionWater ActionType = "WATER" // 浇水 - ActionFertilize ActionType = "FERTILIZE" // 施肥 - ActionPrune ActionType = "PRUNE" // 修剪 - ActionPhoto ActionType = "PHOTO" // 拍照 - ActionLogin ActionType = "LOGIN" // 每日签到 -) - -// BadgeTier 定义徽章的稀有度 -type BadgeTier int - -const ( - TierBronze BadgeTier = 1 // 铜 - TierSilver BadgeTier = 2 // 银 - TierGold BadgeTier = 3 // 金 - TierDiamond BadgeTier = 4 // 钻石 -) diff --git a/config/service_account.go b/config/service_account.go deleted file mode 100644 index 3194e2c..0000000 --- a/config/service_account.go +++ /dev/null @@ -1,6 +0,0 @@ -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/docs/docs.go b/docs/docs.go index 010eaca..55203da 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -244,104 +244,6 @@ const docTemplate = `{ } } }, - "/classify/myClassifyLog": { - "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": "识别记录", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/classify/plant": { - "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" - } - } - } - ] - } - } - } - } - }, "/client/delete": { "post": { "security": [ @@ -594,305 +496,6 @@ const docTemplate = `{ } } }, - "/config/badge/add": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/config/badge/delete": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "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/badge/find": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "徽章配置" - ], - "summary": "根据ID获取徽章配置", - "parameters": [ - { - "type": "string", - "description": "id", - "name": "id", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/config/badge/tree": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "徽章配置" - ], - "summary": "获取徽章配置树形结构", - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/config/badge/update": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/config/level/add": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "等级配置" - ], - "summary": "添加等级配置", - "parameters": [ - { - "description": "添加等级配置", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.CreateLevelConf" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"添加成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/config/level/detail": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "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/level/list": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "等级配置" - ], - "summary": "等级配置列表", - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"查询成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/config/level/update": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "等级配置" - ], - "summary": "修改等级配置", - "parameters": [ - { - "description": "修改等级配置", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.UpdateLevelConf" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"修改成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, "/menu/delete": { "get": { "security": [ @@ -1419,657 +1022,6 @@ const docTemplate = `{ } } }, - "/plant/add": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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/completeTask": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "我的植物" - ], - "summary": "完成任务", - "parameters": [ - { - "description": "完成任务", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.CompleteTask" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"完成任务\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/plant/deletePlan": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/plant/deletePlant": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/plant/detail": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "我的植物" - ], - "summary": "ById植物详情", - "parameters": [ - { - "type": "string", - "description": "id", - "name": "id", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"获取ById成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/plant/growth/add": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "我的植物" - ], - "summary": "添加成长记录", - "parameters": [ - { - "description": "添加成长记录", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.CreateGrowthRecord" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"添加成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/plant/page": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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/plan/add": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "我的植物" - ], - "summary": "添加养护事项", - "parameters": [ - { - "description": "添加养护事项", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.CreateCarePlan" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"添加成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/plant/plan/delete": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "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/todayTask": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "我的植物" - ], - "summary": "今日任务", - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/plant/update": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "我的植物" - ], - "summary": "修改ById植物", - "parameters": [ - { - "description": "修改ById植物", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.UpdateMyPlant" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"修改ById成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/post/comment": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/post/like": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "帖子" - ], - "summary": "点赞帖子", - "parameters": [ - { - "type": "string", - "description": "帖子id", - "name": "id", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "点赞类型 1 点赞 2 取消点赞", - "name": "type", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"点赞成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/post/myPost": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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/page": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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/publish": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/profile/detail": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "个人中心" - ], - "summary": "用户详情", - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"查询成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/profile/update": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "个人中心" - ], - "summary": "修改用户信息", - "parameters": [ - { - "description": "修改用户信息", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.UpdateProfile" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"修改成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, "/role/delete": { "post": { "description": "删除角色", @@ -2324,177 +1276,6 @@ const docTemplate = `{ } } }, - "/topic/add": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "帖子话题" - ], - "summary": "修改话题", - "parameters": [ - { - "description": "修改话题", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.UpdateTopic" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"发布成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/topic/delete": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/topic/detail": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/topic/list": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "帖子话题" - ], - "summary": "话题列表", - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/topic/page": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, "/user/changePassword": { "post": { "security": [ @@ -2831,382 +1612,9 @@ const docTemplate = `{ } } } - }, - "/wiki-class/add": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "百科分类" - ], - "summary": "添加分类", - "parameters": [ - { - "description": "添加分类", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.CreateWikiClass" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"发布成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/wiki-class/delete": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/wiki-class/detail": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/wiki-class/list": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "百科分类" - ], - "summary": "分类列表", - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/wiki-class/page": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/wiki-class/update": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "百科分类" - ], - "summary": "修改分类(可直接传入ossId修改图片)", - "parameters": [ - { - "description": "修改分类", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.UpdateWikiClass" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"发布成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/wiki/add": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "百科" - ], - "summary": "添加百科", - "parameters": [ - { - "description": "添加百科", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.CreateWiki" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"发布成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/wiki/detail": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/wiki/page": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "百科" - ], - "summary": "分页", - "parameters": [ - { - "description": "百科分页", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.WikiPage" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"发布成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/wiki/update": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "百科" - ], - "summary": "修改百科", - "parameters": [ - { - "description": "修改百科", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.UpdateWiki" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"发布成功\"}", - "schema": { - "type": "string" - } - } - } - } } }, "definitions": { - "request.CarePlan": { - "type": "object", - "properties": { - "icon": { - "description": "icon信息", - "type": "string" - }, - "name": { - "description": "农事名称", - "type": "string" - }, - "period": { - "description": "周期", - "type": "integer" - } - } - }, "request.ChangePwd": { "type": "object", "properties": { @@ -3218,381 +1626,6 @@ const docTemplate = `{ } } }, - "request.CompleteTask": { - "type": "object", - "required": [ - "taskId" - ], - "properties": { - "remark": { - "type": "string" - }, - "taskId": { - "type": "string" - } - } - }, - "request.CreateBadge": { - "type": "object", - "properties": { - "comparator": { - "type": "string" - }, - "description": { - "type": "string" - }, - "dimension": { - "description": "维度: EXPERTISE(专家), PERSISTENCE(勤勉), JOURNAL(记录)", - "type": "string" - }, - "groupId": { - "description": "组ID: 用于前端聚合显示 (e.g. \"fertilizerMaster\")", - "type": "string" - }, - "iconId": { - "type": "string" - }, - "name": { - "type": "string" - }, - "rewardSunlight": { - "type": "integer" - }, - "sort": { - "type": "integer" - }, - "targetAction": { - "description": "触发动作: ACT_FERTILIZE, ACT_WATER...", - "type": "string" - }, - "threshold": { - "type": "integer" - }, - "tier": { - "description": "等级: 1=铜, 2=银, 3=金", - "type": "integer" - } - } - }, - "request.CreateCarePlan": { - "type": "object", - "required": [ - "plantId" - ], - "properties": { - "icon": { - "description": "icon信息", - "type": "string" - }, - "name": { - "description": "农事名称", - "type": "string" - }, - "period": { - "description": "周期", - "type": "integer" - }, - "plantId": { - "type": "string" - } - } - }, - "request.CreateComment": { - "type": "object", - "required": [ - "content", - "postId" - ], - "properties": { - "content": { - "description": "评论内容", - "type": "string" - }, - "postId": { - "description": "帖子id", - "type": "string" - } - } - }, - "request.CreateGrowthRecord": { - "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.CreateLevelConf": { - "type": "object", - "properties": { - "level": { - "description": "等级数值", - "type": "integer" - }, - "minSunlight": { - "description": "达到该等级所需的最小阳光值", - "type": "integer" - }, - "perks": { - "description": "解锁权益描述 (e.g., \"解锁智能诊断\")", - "type": "string" - }, - "title": { - "description": "等级称号 (e.g., \"萌芽园丁\")", - "type": "string" - } - } - }, - "request.CreateMyPlant": { - "type": "object", - "properties": { - "carePlans": { - "description": "养护计划", - "type": "array", - "items": { - "$ref": "#/definitions/request.CarePlan" - } - }, - "name": { - "description": "植物名称", - "type": "string" - }, - "ossIds": { - "description": "图片ids", - "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.CreateTopic": { - "type": "object", - "properties": { - "endTime": { - "type": "string" - }, - "remark": { - "type": "string" - }, - "startTime": { - "type": "string" - }, - "title": { - "type": "string" - } - } - }, - "request.CreateWiki": { - "type": "object", - "required": [ - "name" - ], - "properties": { - "aliases": { - "description": "别名", - "type": "string" - }, - "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" - }, - "relatedWikiIds": { - "description": "相关推荐", - "type": "array", - "items": { - "type": "string" - } - }, - "reproductionMethod": { - "description": "繁殖方法", - "type": "string" - }, - "stem": { - "description": "茎", - "type": "string" - } - } - }, - "request.CreateWikiClass": { - "type": "object", - "required": [ - "name" - ], - "properties": { - "name": { - "description": "分类名称", - "type": "string" - }, - "ossId": { - "description": "图片id", - "type": "string" - } - } - }, "request.GetClientList": { "type": "object", "properties": { @@ -3749,371 +1782,6 @@ const docTemplate = `{ } } }, - "request.PageInfo": { - "type": "object", - "properties": { - "current": { - "description": "页码", - "type": "integer" - }, - "keyword": { - "description": "关键字", - "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.UpdateBadge": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "comparator": { - "type": "string" - }, - "description": { - "type": "string" - }, - "dimension": { - "description": "维度: EXPERTISE(专家), PERSISTENCE(勤勉), JOURNAL(记录)", - "type": "string" - }, - "groupId": { - "description": "组Id: 用于前端聚合显示 (e.g. \"fertilizerMaster\")", - "type": "string" - }, - "iconId": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "rewardSunlight": { - "type": "integer" - }, - "sort": { - "type": "integer" - }, - "targetAction": { - "description": "触发动作: ACT_FERTILIZE, ACT_WATER...", - "type": "string" - }, - "threshold": { - "type": "integer" - }, - "tier": { - "description": "等级: 1=铜, 2=银, 3=金", - "type": "integer" - } - } - }, - "request.UpdateLevelConf": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "id": { - "type": "string" - }, - "level": { - "description": "等级数值", - "type": "integer" - }, - "minSunlight": { - "description": "达到该等级所需的最小阳光值", - "type": "integer" - }, - "perks": { - "description": "解锁权益描述 (e.g., \"解锁智能诊断\")", - "type": "string" - }, - "title": { - "description": "等级称号 (e.g., \"萌芽园丁\")", - "type": "string" - } - } - }, - "request.UpdateMyPlant": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "carePlans": { - "type": "array", - "items": { - "$ref": "#/definitions/request.UpdatePlan" - } - }, - "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.UpdatePlan": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "icon": { - "description": "icon信息", - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "description": "农事名称", - "type": "string" - }, - "period": { - "description": "周期", - "type": "integer" - } - } - }, - "request.UpdateProfile": { - "type": "object", - "properties": { - "avatarId": { - "type": "string" - }, - "nickname": { - "type": "string" - } - } - }, - "request.UpdateTopic": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "endTime": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "remark": { - "type": "string" - }, - "startTime": { - "type": "string" - }, - "title": { - "type": "string" - } - } - }, - "request.UpdateWiki": { - "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" - }, - "latinName": { - "description": "拉丁名", - "type": "string" - }, - "lifeCycle": { - "description": "生命周期", - "type": "string" - }, - "lightIntensity": { - "description": "光照强度", - "type": "string" - }, - "lightType": { - "description": "光照类型(直射,散射等)", - "type": "string" - }, - "name": { - "description": "名称", - "type": "string" - }, - "optimalTempPeriod": { - "description": "最佳温度区间", - "type": "string" - }, - "pestsDiseases": { - "description": "常见病虫害", - "type": "string" - }, - "stem": { - "description": "茎", - "type": "string" - } - } - }, - "request.UpdateWikiClass": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "id": { - "type": "string" - }, - "name": { - "description": "分类名称", - "type": "string" - }, - "ossId": { - "description": "图片id", - "type": "string" - } - } - }, - "request.WikiPage": { - "type": "object", - "properties": { - "classId": { - "description": "分类id", - "type": "array", - "items": { - "type": "string" - } - }, - "current": { - "description": "页码", - "type": "integer" - }, - "isHot": { - "description": "是否热门 0否 1是", - "type": "integer" - }, - "keyword": { - "description": "关键字", - "type": "string" - }, - "name": { - "description": "植物名称", - "type": "string" - }, - "pageSize": { - "description": "每页大小", - "type": "integer" - } - } - }, "response.CaptchaRes": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index 36d7968..a484875 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -236,104 +236,6 @@ } } }, - "/classify/myClassifyLog": { - "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": "识别记录", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/response.Response" - }, - { - "type": "object", - "properties": { - "msg": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "/classify/plant": { - "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" - } - } - } - ] - } - } - } - } - }, "/client/delete": { "post": { "security": [ @@ -586,305 +488,6 @@ } } }, - "/config/badge/add": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/config/badge/delete": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "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/badge/find": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "徽章配置" - ], - "summary": "根据ID获取徽章配置", - "parameters": [ - { - "type": "string", - "description": "id", - "name": "id", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/config/badge/tree": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "徽章配置" - ], - "summary": "获取徽章配置树形结构", - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/config/badge/update": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/config/level/add": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "等级配置" - ], - "summary": "添加等级配置", - "parameters": [ - { - "description": "添加等级配置", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.CreateLevelConf" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"添加成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/config/level/detail": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "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/level/list": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "等级配置" - ], - "summary": "等级配置列表", - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"查询成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/config/level/update": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "等级配置" - ], - "summary": "修改等级配置", - "parameters": [ - { - "description": "修改等级配置", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.UpdateLevelConf" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"修改成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, "/menu/delete": { "get": { "security": [ @@ -1411,657 +1014,6 @@ } } }, - "/plant/add": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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/completeTask": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "我的植物" - ], - "summary": "完成任务", - "parameters": [ - { - "description": "完成任务", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.CompleteTask" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"完成任务\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/plant/deletePlan": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/plant/deletePlant": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/plant/detail": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "我的植物" - ], - "summary": "ById植物详情", - "parameters": [ - { - "type": "string", - "description": "id", - "name": "id", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"获取ById成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/plant/growth/add": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "我的植物" - ], - "summary": "添加成长记录", - "parameters": [ - { - "description": "添加成长记录", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.CreateGrowthRecord" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"添加成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/plant/page": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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/plan/add": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "我的植物" - ], - "summary": "添加养护事项", - "parameters": [ - { - "description": "添加养护事项", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.CreateCarePlan" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"添加成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/plant/plan/delete": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "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/todayTask": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "我的植物" - ], - "summary": "今日任务", - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/plant/update": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "我的植物" - ], - "summary": "修改ById植物", - "parameters": [ - { - "description": "修改ById植物", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.UpdateMyPlant" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"修改ById成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/post/comment": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/post/like": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "帖子" - ], - "summary": "点赞帖子", - "parameters": [ - { - "type": "string", - "description": "帖子id", - "name": "id", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "点赞类型 1 点赞 2 取消点赞", - "name": "type", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"点赞成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/post/myPost": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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/page": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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/publish": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/profile/detail": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "个人中心" - ], - "summary": "用户详情", - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"查询成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/profile/update": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "个人中心" - ], - "summary": "修改用户信息", - "parameters": [ - { - "description": "修改用户信息", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.UpdateProfile" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"修改成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, "/role/delete": { "post": { "description": "删除角色", @@ -2316,177 +1268,6 @@ } } }, - "/topic/add": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "帖子话题" - ], - "summary": "修改话题", - "parameters": [ - { - "description": "修改话题", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.UpdateTopic" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"发布成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/topic/delete": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/topic/detail": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/topic/list": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "帖子话题" - ], - "summary": "话题列表", - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/topic/page": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, "/user/changePassword": { "post": { "security": [ @@ -2823,382 +1604,9 @@ } } } - }, - "/wiki-class/add": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "百科分类" - ], - "summary": "添加分类", - "parameters": [ - { - "description": "添加分类", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.CreateWikiClass" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"发布成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/wiki-class/delete": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/wiki-class/detail": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/wiki-class/list": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "produces": [ - "application/json" - ], - "tags": [ - "百科分类" - ], - "summary": "分类列表", - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"获取成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/wiki-class/page": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/wiki-class/update": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "百科分类" - ], - "summary": "修改分类(可直接传入ossId修改图片)", - "parameters": [ - { - "description": "修改分类", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.UpdateWikiClass" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"发布成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/wiki/add": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "百科" - ], - "summary": "添加百科", - "parameters": [ - { - "description": "添加百科", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.CreateWiki" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"发布成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/wiki/detail": { - "get": { - "security": [ - { - "BearerAuth": [] - } - ], - "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" - } - } - } - } - }, - "/wiki/page": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "百科" - ], - "summary": "分页", - "parameters": [ - { - "description": "百科分页", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.WikiPage" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"发布成功\"}", - "schema": { - "type": "string" - } - } - } - } - }, - "/wiki/update": { - "post": { - "security": [ - { - "BearerAuth": [] - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "百科" - ], - "summary": "修改百科", - "parameters": [ - { - "description": "修改百科", - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/request.UpdateWiki" - } - } - ], - "responses": { - "200": { - "description": "{\"success\":true,\"data\":{},\"msg\":\"发布成功\"}", - "schema": { - "type": "string" - } - } - } - } } }, "definitions": { - "request.CarePlan": { - "type": "object", - "properties": { - "icon": { - "description": "icon信息", - "type": "string" - }, - "name": { - "description": "农事名称", - "type": "string" - }, - "period": { - "description": "周期", - "type": "integer" - } - } - }, "request.ChangePwd": { "type": "object", "properties": { @@ -3210,381 +1618,6 @@ } } }, - "request.CompleteTask": { - "type": "object", - "required": [ - "taskId" - ], - "properties": { - "remark": { - "type": "string" - }, - "taskId": { - "type": "string" - } - } - }, - "request.CreateBadge": { - "type": "object", - "properties": { - "comparator": { - "type": "string" - }, - "description": { - "type": "string" - }, - "dimension": { - "description": "维度: EXPERTISE(专家), PERSISTENCE(勤勉), JOURNAL(记录)", - "type": "string" - }, - "groupId": { - "description": "组ID: 用于前端聚合显示 (e.g. \"fertilizerMaster\")", - "type": "string" - }, - "iconId": { - "type": "string" - }, - "name": { - "type": "string" - }, - "rewardSunlight": { - "type": "integer" - }, - "sort": { - "type": "integer" - }, - "targetAction": { - "description": "触发动作: ACT_FERTILIZE, ACT_WATER...", - "type": "string" - }, - "threshold": { - "type": "integer" - }, - "tier": { - "description": "等级: 1=铜, 2=银, 3=金", - "type": "integer" - } - } - }, - "request.CreateCarePlan": { - "type": "object", - "required": [ - "plantId" - ], - "properties": { - "icon": { - "description": "icon信息", - "type": "string" - }, - "name": { - "description": "农事名称", - "type": "string" - }, - "period": { - "description": "周期", - "type": "integer" - }, - "plantId": { - "type": "string" - } - } - }, - "request.CreateComment": { - "type": "object", - "required": [ - "content", - "postId" - ], - "properties": { - "content": { - "description": "评论内容", - "type": "string" - }, - "postId": { - "description": "帖子id", - "type": "string" - } - } - }, - "request.CreateGrowthRecord": { - "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.CreateLevelConf": { - "type": "object", - "properties": { - "level": { - "description": "等级数值", - "type": "integer" - }, - "minSunlight": { - "description": "达到该等级所需的最小阳光值", - "type": "integer" - }, - "perks": { - "description": "解锁权益描述 (e.g., \"解锁智能诊断\")", - "type": "string" - }, - "title": { - "description": "等级称号 (e.g., \"萌芽园丁\")", - "type": "string" - } - } - }, - "request.CreateMyPlant": { - "type": "object", - "properties": { - "carePlans": { - "description": "养护计划", - "type": "array", - "items": { - "$ref": "#/definitions/request.CarePlan" - } - }, - "name": { - "description": "植物名称", - "type": "string" - }, - "ossIds": { - "description": "图片ids", - "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.CreateTopic": { - "type": "object", - "properties": { - "endTime": { - "type": "string" - }, - "remark": { - "type": "string" - }, - "startTime": { - "type": "string" - }, - "title": { - "type": "string" - } - } - }, - "request.CreateWiki": { - "type": "object", - "required": [ - "name" - ], - "properties": { - "aliases": { - "description": "别名", - "type": "string" - }, - "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" - }, - "relatedWikiIds": { - "description": "相关推荐", - "type": "array", - "items": { - "type": "string" - } - }, - "reproductionMethod": { - "description": "繁殖方法", - "type": "string" - }, - "stem": { - "description": "茎", - "type": "string" - } - } - }, - "request.CreateWikiClass": { - "type": "object", - "required": [ - "name" - ], - "properties": { - "name": { - "description": "分类名称", - "type": "string" - }, - "ossId": { - "description": "图片id", - "type": "string" - } - } - }, "request.GetClientList": { "type": "object", "properties": { @@ -3741,371 +1774,6 @@ } } }, - "request.PageInfo": { - "type": "object", - "properties": { - "current": { - "description": "页码", - "type": "integer" - }, - "keyword": { - "description": "关键字", - "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.UpdateBadge": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "comparator": { - "type": "string" - }, - "description": { - "type": "string" - }, - "dimension": { - "description": "维度: EXPERTISE(专家), PERSISTENCE(勤勉), JOURNAL(记录)", - "type": "string" - }, - "groupId": { - "description": "组Id: 用于前端聚合显示 (e.g. \"fertilizerMaster\")", - "type": "string" - }, - "iconId": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "rewardSunlight": { - "type": "integer" - }, - "sort": { - "type": "integer" - }, - "targetAction": { - "description": "触发动作: ACT_FERTILIZE, ACT_WATER...", - "type": "string" - }, - "threshold": { - "type": "integer" - }, - "tier": { - "description": "等级: 1=铜, 2=银, 3=金", - "type": "integer" - } - } - }, - "request.UpdateLevelConf": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "id": { - "type": "string" - }, - "level": { - "description": "等级数值", - "type": "integer" - }, - "minSunlight": { - "description": "达到该等级所需的最小阳光值", - "type": "integer" - }, - "perks": { - "description": "解锁权益描述 (e.g., \"解锁智能诊断\")", - "type": "string" - }, - "title": { - "description": "等级称号 (e.g., \"萌芽园丁\")", - "type": "string" - } - } - }, - "request.UpdateMyPlant": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "carePlans": { - "type": "array", - "items": { - "$ref": "#/definitions/request.UpdatePlan" - } - }, - "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.UpdatePlan": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "icon": { - "description": "icon信息", - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "description": "农事名称", - "type": "string" - }, - "period": { - "description": "周期", - "type": "integer" - } - } - }, - "request.UpdateProfile": { - "type": "object", - "properties": { - "avatarId": { - "type": "string" - }, - "nickname": { - "type": "string" - } - } - }, - "request.UpdateTopic": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "endTime": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "remark": { - "type": "string" - }, - "startTime": { - "type": "string" - }, - "title": { - "type": "string" - } - } - }, - "request.UpdateWiki": { - "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" - }, - "latinName": { - "description": "拉丁名", - "type": "string" - }, - "lifeCycle": { - "description": "生命周期", - "type": "string" - }, - "lightIntensity": { - "description": "光照强度", - "type": "string" - }, - "lightType": { - "description": "光照类型(直射,散射等)", - "type": "string" - }, - "name": { - "description": "名称", - "type": "string" - }, - "optimalTempPeriod": { - "description": "最佳温度区间", - "type": "string" - }, - "pestsDiseases": { - "description": "常见病虫害", - "type": "string" - }, - "stem": { - "description": "茎", - "type": "string" - } - } - }, - "request.UpdateWikiClass": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "id": { - "type": "string" - }, - "name": { - "description": "分类名称", - "type": "string" - }, - "ossId": { - "description": "图片id", - "type": "string" - } - } - }, - "request.WikiPage": { - "type": "object", - "properties": { - "classId": { - "description": "分类id", - "type": "array", - "items": { - "type": "string" - } - }, - "current": { - "description": "页码", - "type": "integer" - }, - "isHot": { - "description": "是否热门 0否 1是", - "type": "integer" - }, - "keyword": { - "description": "关键字", - "type": "string" - }, - "name": { - "description": "植物名称", - "type": "string" - }, - "pageSize": { - "description": "每页大小", - "type": "integer" - } - } - }, "response.CaptchaRes": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 0f4c487..b84d07f 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,16 +1,4 @@ definitions: - request.CarePlan: - properties: - icon: - description: icon信息 - type: string - name: - description: 农事名称 - type: string - period: - description: 周期 - type: integer - type: object request.ChangePwd: properties: id: @@ -18,271 +6,6 @@ definitions: newPwd: type: string type: object - request.CompleteTask: - properties: - remark: - type: string - taskId: - type: string - required: - - taskId - type: object - request.CreateBadge: - properties: - comparator: - type: string - description: - type: string - dimension: - description: '维度: EXPERTISE(专家), PERSISTENCE(勤勉), JOURNAL(记录)' - type: string - groupId: - description: '组ID: 用于前端聚合显示 (e.g. "fertilizerMaster")' - type: string - iconId: - type: string - name: - type: string - rewardSunlight: - type: integer - sort: - type: integer - targetAction: - description: '触发动作: ACT_FERTILIZE, ACT_WATER...' - type: string - threshold: - type: integer - tier: - description: '等级: 1=铜, 2=银, 3=金' - type: integer - type: object - request.CreateCarePlan: - properties: - icon: - description: icon信息 - type: string - name: - description: 农事名称 - type: string - period: - description: 周期 - type: integer - plantId: - type: string - required: - - plantId - type: object - request.CreateComment: - properties: - content: - description: 评论内容 - type: string - postId: - description: 帖子id - type: string - required: - - content - - postId - type: object - request.CreateGrowthRecord: - 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.CreateLevelConf: - properties: - level: - description: 等级数值 - type: integer - minSunlight: - description: 达到该等级所需的最小阳光值 - type: integer - perks: - description: 解锁权益描述 (e.g., "解锁智能诊断") - type: string - title: - description: 等级称号 (e.g., "萌芽园丁") - type: string - type: object - request.CreateMyPlant: - properties: - carePlans: - description: 养护计划 - items: - $ref: '#/definitions/request.CarePlan' - type: array - name: - description: 植物名称 - type: string - ossIds: - description: 图片ids - 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.CreateTopic: - properties: - endTime: - type: string - remark: - type: string - startTime: - type: string - title: - type: string - type: object - request.CreateWiki: - properties: - aliases: - description: 别名 - type: string - 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 - relatedWikiIds: - description: 相关推荐 - items: - type: string - type: array - reproductionMethod: - description: 繁殖方法 - type: string - stem: - description: 茎 - type: string - required: - - name - type: object - request.CreateWikiClass: - properties: - name: - description: 分类名称 - type: string - ossId: - description: 图片id - type: string - required: - - name - type: object request.GetClientList: properties: clientId: @@ -388,264 +111,6 @@ definitions: password: type: string type: object - request.PageInfo: - properties: - current: - description: 页码 - type: integer - keyword: - description: 关键字 - 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.UpdateBadge: - properties: - comparator: - type: string - description: - type: string - dimension: - description: '维度: EXPERTISE(专家), PERSISTENCE(勤勉), JOURNAL(记录)' - type: string - groupId: - description: '组Id: 用于前端聚合显示 (e.g. "fertilizerMaster")' - type: string - iconId: - type: string - id: - type: string - name: - type: string - rewardSunlight: - type: integer - sort: - type: integer - targetAction: - description: '触发动作: ACT_FERTILIZE, ACT_WATER...' - type: string - threshold: - type: integer - tier: - description: '等级: 1=铜, 2=银, 3=金' - type: integer - required: - - id - type: object - request.UpdateLevelConf: - properties: - id: - type: string - level: - description: 等级数值 - type: integer - minSunlight: - description: 达到该等级所需的最小阳光值 - type: integer - perks: - description: 解锁权益描述 (e.g., "解锁智能诊断") - type: string - title: - description: 等级称号 (e.g., "萌芽园丁") - type: string - required: - - id - type: object - request.UpdateMyPlant: - properties: - carePlans: - items: - $ref: '#/definitions/request.UpdatePlan' - type: array - 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.UpdatePlan: - properties: - icon: - description: icon信息 - type: string - id: - type: string - name: - description: 农事名称 - type: string - period: - description: 周期 - type: integer - required: - - id - type: object - request.UpdateProfile: - properties: - avatarId: - type: string - nickname: - type: string - type: object - request.UpdateTopic: - properties: - endTime: - type: string - id: - type: integer - remark: - type: string - startTime: - type: string - title: - type: string - required: - - id - type: object - request.UpdateWiki: - 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 - latinName: - description: 拉丁名 - type: string - lifeCycle: - description: 生命周期 - type: string - lightIntensity: - description: 光照强度 - type: string - lightType: - description: 光照类型(直射,散射等) - type: string - name: - description: 名称 - type: string - optimalTempPeriod: - description: 最佳温度区间 - type: string - pestsDiseases: - description: 常见病虫害 - type: string - stem: - description: 茎 - type: string - required: - - id - type: object - request.UpdateWikiClass: - properties: - id: - type: string - name: - description: 分类名称 - type: string - ossId: - description: 图片id - type: string - required: - - id - type: object - request.WikiPage: - properties: - classId: - description: 分类id - items: - type: string - type: array - current: - description: 页码 - type: integer - isHot: - description: 是否热门 0否 1是 - type: integer - keyword: - description: 关键字 - type: string - name: - description: 植物名称 - type: string - pageSize: - description: 每页大小 - type: integer - type: object response.CaptchaRes: properties: captcha: @@ -963,61 +428,6 @@ paths: summary: 小程序登录 tags: - 登录相关 - /classify/myClassifyLog: - post: - consumes: - - application/json - parameters: - - description: 分页 - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.PageInfo' - produces: - - application/json - responses: - "200": - description: 识别记录 - schema: - allOf: - - $ref: '#/definitions/response.Response' - - properties: - msg: - type: string - type: object - security: - - ApiKeyAuth: [] - summary: 我的植物识别记录 - tags: - - 识别相关 - /classify/plant: - 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: - - 识别相关 /client/delete: post: consumes: @@ -1160,186 +570,6 @@ paths: summary: 更新client tags: - 客户端管理 - /config/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: - - BearerAuth: [] - summary: 添加徽章配置 - tags: - - 徽章配置 - /config/badge/delete: - 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: - - BearerAuth: [] - summary: 删除徽章配置 - tags: - - 徽章配置 - /config/badge/find: - 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: - - BearerAuth: [] - summary: 根据ID获取徽章配置 - tags: - - 徽章配置 - /config/badge/tree: - get: - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"获取成功"}' - schema: - type: string - security: - - BearerAuth: [] - summary: 获取徽章配置树形结构 - tags: - - 徽章配置 - /config/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: - - BearerAuth: [] - summary: 更新徽章配置 - tags: - - 徽章配置 - /config/level/add: - post: - consumes: - - application/json - parameters: - - description: 添加等级配置 - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.CreateLevelConf' - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"添加成功"}' - schema: - type: string - security: - - BearerAuth: [] - summary: 添加等级配置 - tags: - - 等级配置 - /config/level/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: - - BearerAuth: [] - summary: 等级配置详情 - tags: - - 等级配置 - /config/level/list: - get: - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"查询成功"}' - schema: - type: string - security: - - BearerAuth: [] - summary: 等级配置列表 - tags: - - 等级配置 - /config/level/update: - post: - consumes: - - application/json - parameters: - - description: 修改等级配置 - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.UpdateLevelConf' - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"修改成功"}' - schema: - type: string - security: - - BearerAuth: [] - summary: 修改等级配置 - tags: - - 等级配置 /menu/delete: get: description: 删除menu @@ -1634,400 +864,6 @@ paths: 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: - - BearerAuth: [] - summary: 添加植物 - tags: - - 我的植物 - /plant/completeTask: - post: - consumes: - - application/json - parameters: - - description: 完成任务 - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.CompleteTask' - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"完成任务"}' - schema: - type: string - security: - - BearerAuth: [] - summary: 完成任务 - tags: - - 我的植物 - /plant/deletePlan: - 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: - - BearerAuth: [] - summary: 删除任务 - tags: - - 我的植物 - /plant/deletePlant: - 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: - - BearerAuth: [] - 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":"获取ById成功"}' - schema: - type: string - security: - - BearerAuth: [] - summary: ById植物详情 - tags: - - 我的植物 - /plant/growth/add: - post: - consumes: - - application/json - parameters: - - description: 添加成长记录 - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.CreateGrowthRecord' - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"添加成功"}' - schema: - type: string - security: - - BearerAuth: [] - summary: 添加成长记录 - tags: - - 我的植物 - /plant/page: - 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: - - BearerAuth: [] - summary: 植物列表 - tags: - - 我的植物 - /plant/plan/add: - post: - consumes: - - application/json - parameters: - - description: 添加养护事项 - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.CreateCarePlan' - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"添加成功"}' - schema: - type: string - security: - - BearerAuth: [] - summary: 添加养护事项 - tags: - - 我的植物 - /plant/plan/delete: - 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: - - BearerAuth: [] - summary: 删除养护事项 - tags: - - 我的植物 - /plant/todayTask: - get: - consumes: - - application/json - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"获取成功"}' - schema: - type: string - security: - - BearerAuth: [] - summary: 今日任务 - tags: - - 我的植物 - /plant/update: - post: - consumes: - - application/json - parameters: - - description: 修改ById植物 - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.UpdateMyPlant' - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"修改ById成功"}' - schema: - type: string - security: - - BearerAuth: [] - summary: 修改ById植物 - tags: - - 我的植物 - /post/comment: - 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: - - BearerAuth: [] - summary: 评论帖子 - tags: - - 帖子 - /post/like: - get: - parameters: - - description: 帖子id - in: query - name: id - required: true - type: string - - description: 点赞类型 1 点赞 2 取消点赞 - in: query - name: type - required: true - type: string - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"点赞成功"}' - schema: - type: string - security: - - BearerAuth: [] - summary: 点赞帖子 - tags: - - 帖子 - /post/myPost: - 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: - - BearerAuth: [] - 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: - - BearerAuth: [] - summary: 帖子列表 - tags: - - 帖子 - /post/publish: - 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: - - BearerAuth: [] - summary: 发布帖子 - tags: - - 帖子 - /profile/detail: - get: - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"查询成功"}' - schema: - type: string - security: - - BearerAuth: [] - summary: 用户详情 - tags: - - 个人中心 - /profile/update: - post: - consumes: - - application/json - parameters: - - description: 修改用户信息 - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.UpdateProfile' - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"修改成功"}' - schema: - type: string - security: - - BearerAuth: [] - summary: 修改用户信息 - tags: - - 个人中心 /role/delete: post: consumes: @@ -2178,109 +1014,6 @@ paths: summary: 修改角色 tags: - 角色管理 - /topic/add: - post: - consumes: - - application/json - parameters: - - description: 修改话题 - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.UpdateTopic' - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"发布成功"}' - schema: - type: string - security: - - BearerAuth: [] - summary: 修改话题 - tags: - - 帖子话题 - /topic/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: - - BearerAuth: [] - summary: 删除任务 - tags: - - 帖子话题 - /topic/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: - - BearerAuth: [] - summary: 话题详情 - tags: - - 帖子话题 - /topic/list: - get: - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"获取成功"}' - schema: - type: string - security: - - BearerAuth: [] - summary: 话题列表 - tags: - - 帖子话题 - /topic/page: - 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: - - BearerAuth: [] - summary: 话题分页 - tags: - - 帖子话题 /user/changePassword: post: consumes: @@ -2476,221 +1209,6 @@ paths: summary: 更新用户 tags: - 用户管理 - /wiki-class/add: - post: - consumes: - - application/json - parameters: - - description: 添加分类 - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.CreateWikiClass' - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"发布成功"}' - schema: - type: string - security: - - BearerAuth: [] - summary: 添加分类 - tags: - - 百科分类 - /wiki-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: - - BearerAuth: [] - summary: 删除分类 - tags: - - 百科分类 - /wiki-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: - - BearerAuth: [] - summary: 分类详情 - tags: - - 百科分类 - /wiki-class/list: - get: - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"获取成功"}' - schema: - type: string - security: - - BearerAuth: [] - summary: 分类列表 - tags: - - 百科分类 - /wiki-class/page: - 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: - - BearerAuth: [] - summary: 分类分页 - tags: - - 百科分类 - /wiki-class/update: - post: - consumes: - - application/json - parameters: - - description: 修改分类 - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.UpdateWikiClass' - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"发布成功"}' - schema: - type: string - security: - - BearerAuth: [] - summary: 修改分类(可直接传入ossId修改图片) - tags: - - 百科分类 - /wiki/add: - post: - consumes: - - application/json - parameters: - - description: 添加百科 - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.CreateWiki' - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"发布成功"}' - schema: - type: string - security: - - BearerAuth: [] - summary: 添加百科 - tags: - - 百科 - /wiki/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: - - BearerAuth: [] - summary: 百科详情 - tags: - - 百科 - /wiki/page: - post: - consumes: - - application/json - parameters: - - description: 百科分页 - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.WikiPage' - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"发布成功"}' - schema: - type: string - security: - - BearerAuth: [] - summary: 分页 - tags: - - 百科 - /wiki/update: - post: - consumes: - - application/json - parameters: - - description: 修改百科 - in: body - name: data - required: true - schema: - $ref: '#/definitions/request.UpdateWiki' - produces: - - application/json - responses: - "200": - description: '{"success":true,"data":{},"msg":"发布成功"}' - schema: - type: string - security: - - BearerAuth: [] - summary: 修改百科 - tags: - - 百科 securityDefinitions: BearerAuth: in: header diff --git a/initialize/gorm.go b/initialize/gorm.go index d4499fb..0c61c15 100644 --- a/initialize/gorm.go +++ b/initialize/gorm.go @@ -3,6 +3,7 @@ package initialize import ( "os" "sundynix-go/global" + "sundynix-go/model/radio" "sundynix-go/model/system" "go.uber.org/zap" @@ -37,6 +38,12 @@ func MigrateTable() { system.Menu{}, system.SysOperationRecord{}, system.Oss{}, + + radio.RadioCategory{}, + radio.RadioChannel{}, + radio.RadioProgram{}, + radio.RadioSubscription{}, + radio.RadioUser{}, ) if err != nil { global.Logger.Error("Migrate table failed,err:", zap.Error(err)) diff --git a/initialize/router.go b/initialize/router.go index 5df0eed..65c9f76 100644 --- a/initialize/router.go +++ b/initialize/router.go @@ -26,6 +26,8 @@ func Routers() { // 系统组路由 systemRouter := router.GroupApp.System + // Radio组路由 + radioRouter := router.GroupApp.Radio NeedAuthGroup := Router.Group(global.Config.System.RouterPrefix) PublicGroup := Router.Group(global.Config.System.RouterPrefix) @@ -44,6 +46,13 @@ func Routers() { systemRouter.InitRoleRouter(NeedAuthGroup) //角色相关 systemRouter.InitMenuRouter(NeedAuthGroup) //菜单相关 systemRouter.InitOssRouter(NeedAuthGroup) //OSS相关 + + // Radio模块路由 + radioRouter.InitCategoryRouter(NeedAuthGroup) //分类相关 + radioRouter.InitChannelRouter(NeedAuthGroup) //频道相关 + radioRouter.InitProgramRouter(NeedAuthGroup) //节目相关 + radioRouter.InitSubscriptionRouter(NeedAuthGroup) //订阅相关 + radioRouter.InitInteractionRouter(NeedAuthGroup) //用户互动相关 } address := fmt.Sprintf(":%d", global.Config.System.Addr) diff --git a/log/2026-02-25/error.log b/log/2026-02-25/error.log deleted file mode 100644 index f35a507..0000000 --- a/log/2026-02-25/error.log +++ /dev/null @@ -1,4 +0,0 @@ -[sundynix-radio-server]2026-02-25 17:24:01 error /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/utils/auth/claims.go:125 获取用户信息失败,请检查请求头是否存在x-token且claims是否为规定结构 -[sundynix-radio-server]2026-02-25 17:24:44 error /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/utils/auth/claims.go:125 获取用户信息失败,请检查请求头是否存在x-token且claims是否为规定结构 -[sundynix-radio-server]2026-02-25 17:25:21 error /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/utils/auth/claims.go:125 获取用户信息失败,请检查请求头是否存在x-token且claims是否为规定结构 -[sundynix-radio-server]2026-02-25 17:26:46 error /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/utils/auth/claims.go:125 获取用户信息失败,请检查请求头是否存在x-token且claims是否为规定结构 diff --git a/log/2026-02-25/info.log b/log/2026-02-25/info.log deleted file mode 100644 index 876821d..0000000 --- a/log/2026-02-25/info.log +++ /dev/null @@ -1,12 +0,0 @@ -[sundynix-radio-server]2026-02-25 16:53:23 info /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/gorm_mysql.go:40 Mysql connect success -[sundynix-radio-server]2026-02-25 16:53:23 info /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/redis.go:46 Redis connect ping response: {"name": "", "pong": "PONG"} -[sundynix-radio-server]2026-02-25 16:53:28 info /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/gorm.go:54 Migrate table success -[sundynix-radio-server]2026-02-25 17:01:39 info /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/gorm_mysql.go:40 Mysql connect success -[sundynix-radio-server]2026-02-25 17:01:39 info /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/redis.go:46 Redis connect ping response: {"name": "", "pong": "PONG"} -[sundynix-radio-server]2026-02-25 17:01:52 info /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/gorm.go:54 Migrate table success -[sundynix-radio-server]2026-02-25 17:12:22 info /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/gorm_mysql.go:40 Mysql connect success -[sundynix-radio-server]2026-02-25 17:12:22 info /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/redis.go:46 Redis connect ping response: {"name": "", "pong": "PONG"} -[sundynix-radio-server]2026-02-25 17:12:37 info /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/gorm.go:54 Migrate table success -[sundynix-radio-server]2026-02-25 17:27:33 info /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/service/radio/crawl.go:79 没有启用的抓取源,跳过本次抓取 -[sundynix-radio-server]2026-02-25 17:30:50 info /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/service/radio/crawl.go:95 开始抓取 GitHub 数据 {"source": "GitHub AI 热榜"} -[sundynix-radio-server]2026-02-25 17:30:51 info /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/service/radio/crawl.go:158 GitHub 数据抓取完成 {"totalResults": 46, "fetched": 10} diff --git a/log/2026-02-28/error.log b/log/2026-02-28/error.log new file mode 100644 index 0000000..360e781 --- /dev/null +++ b/log/2026-02-28/error.log @@ -0,0 +1,9 @@ +[sundynix-radio-server]2026-02-28 15:39:45 error /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/redis.go:43 Redis connect ping failed,err: {"name": "", "error": "dial tcp 127.0.0.1:6379: connect: connection refused"} +[sundynix-radio-server]2026-02-28 15:39:45 error /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/redis.go:17 Redis connect failed,err: {"error": "dial tcp 127.0.0.1:6379: connect: connection refused"} +[sundynix-radio-server]2026-02-28 15:39:45 error /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/internal/gorm_logger_writer.go:29 [/Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/gorm.go:34 {{ 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC {0001-01-01 00:00:00 +0000 UTC %!s(bool=false)} } %!s(*system.Oss=) %!s(*system.Oss=) %!s(int=0) %!s(int=0)} invalid field found for struct sundynix-go/model/radio.RadioCategory's field Cover: define a valid foreign key for relations or implement the Valuer/Scanner interface] +[error] failed to parse value %!v(MISSING), got error %!v(MISSING) +[sundynix-radio-server]2026-02-28 15:39:51 error /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/internal/gorm_logger_writer.go:29 [/Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/gorm.go:34 {{ 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC {0001-01-01 00:00:00 +0000 UTC %!s(bool=false)} } %!s(*system.Oss=) %!s(*system.Oss=) %!s(int=0) %!s(int=0)} invalid field found for struct sundynix-go/model/radio.RadioCategory's field Cover: define a valid foreign key for relations or implement the Valuer/Scanner interface] +[error] failed to parse value %!v(MISSING), got error %!v(MISSING) +[sundynix-radio-server]2026-02-28 15:39:51 error /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/gorm.go:49 Migrate table failed,err: {"error": "invalid field found for struct sundynix-go/model/radio.RadioCategory's field Cover: define a valid foreign key for relations or implement the Valuer/Scanner interface"} +[sundynix-radio-server]2026-02-28 15:40:52 error /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/redis.go:43 Redis connect ping failed,err: {"name": "", "error": "dial tcp 127.0.0.1:6379: connect: connection refused"} +[sundynix-radio-server]2026-02-28 15:40:52 error /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/redis.go:17 Redis connect failed,err: {"error": "dial tcp 127.0.0.1:6379: connect: connection refused"} diff --git a/log/2026-02-28/info.log b/log/2026-02-28/info.log new file mode 100644 index 0000000..2e5d313 --- /dev/null +++ b/log/2026-02-28/info.log @@ -0,0 +1,3 @@ +[sundynix-radio-server]2026-02-28 15:39:45 info /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/gorm_mysql.go:40 Mysql connect success +[sundynix-radio-server]2026-02-28 15:40:52 info /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/gorm_mysql.go:40 Mysql connect success +[sundynix-radio-server]2026-02-28 15:41:00 info /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/gorm.go:52 Migrate table success diff --git a/main.go b/main.go index 41ee800..3b219a5 100644 --- a/main.go +++ b/main.go @@ -13,7 +13,7 @@ import ( // @title Swagger API接口文档 // @version v1.0.0 // @description 使用gin + gorm进行极速开发的全栈开发基础平台 -// @securityDefinitions.apikey BearerAuth +// @securityDefinitions.basic BearerAuth // @in header // @name Authorization // @BasePath / diff --git a/model/commom/request/common.go b/model/commom/request/common.go index 0e98c78..fba890f 100644 --- a/model/commom/request/common.go +++ b/model/commom/request/common.go @@ -36,6 +36,9 @@ func (r *GetById) Uint() string { return string(r.ID) } +type IdReq struct { + Id string `json:"id" form:"id"` +} type IdsReq struct { Ids []string `json:"ids" form:"ids"` } diff --git a/model/radio/radio_category.go b/model/radio/radio_category.go new file mode 100644 index 0000000..6745b2e --- /dev/null +++ b/model/radio/radio_category.go @@ -0,0 +1,23 @@ +package radio + +import ( + "sundynix-go/global" + "sundynix-go/model/system" +) + +// RadioCategory 电台分类表 +type RadioCategory struct { + global.BaseModel + Name string `gorm:"size:50" json:"name"` // 分类名称 + Description string `gorm:"size:255" json:"description"` // 分类描述 + IconId string `gorm:"size:50" json:"iconId"` // 图标OSS ID + Icon *system.Oss `gorm:"foreignKey:IconId" json:"icon"` // 图标OSS + CoverId string `gorm:"size:50" json:"coverId"` // 封面图OSS ID + Cover *system.Oss `gorm:"foreignKey:CoverId" json:"cover"` // 封面图OSS + Sort int `gorm:"default:0" json:"sort"` // 排序 + Status int `gorm:"default:1" json:"status"` // 状态 0:禁用 1:启用 +} + +func (RadioCategory) TableName() string { + return "sundynix_radio_category" +} diff --git a/model/radio/radio_channel.go b/model/radio/radio_channel.go new file mode 100644 index 0000000..ca02696 --- /dev/null +++ b/model/radio/radio_channel.go @@ -0,0 +1,26 @@ +package radio + +import ( + "sundynix-go/global" + "sundynix-go/model/system" +) + +// RadioChannel 电台频道表 +type RadioChannel struct { + global.BaseModel + CategoryId string `gorm:"size:50;index" json:"categoryId"` // 分类ID + Name string `gorm:"size:50" json:"name"` // 频道名称 + Description string `gorm:"size:500" json:"description"` // 频道描述 + Price int `gorm:"default:0;comment:价格,单位,分 " json:"price"` //价格 单位:分 + CoverId string `gorm:"size:50" json:"coverId"` // 封面图OSS ID + Cover *system.Oss `gorm:"foreignKey:CoverId" json:"cover"` // 封面图OSS + Tags string `gorm:"size:255" json:"tags"` // 标签,逗号分隔 + IsVipOnly int `gorm:"default:0" json:"isVipOnly"` // 是否VIP专享 0:否 1:是 + Sort int `gorm:"default:0" json:"sort"` // 排序 + Status int `gorm:"default:1" json:"status"` // 状态 0:禁用 1:启用 + HasSubscribed int `gorm:"-" json:"hasSubscribed"` // 状态 0:未订阅 1:已订阅 +} + +func (RadioChannel) TableName() string { + return "sundynix_radio_channel" +} diff --git a/model/radio/radio_comment.go b/model/radio/radio_comment.go new file mode 100644 index 0000000..979ac92 --- /dev/null +++ b/model/radio/radio_comment.go @@ -0,0 +1,19 @@ +package radio + +import ( + "sundynix-go/global" +) + +// RadioComment 用户评论表 +type RadioComment struct { + global.BaseModel + ProgramId string `gorm:"size:50;index" json:"programId"` // 节目ID + UserId string `gorm:"size:50;index" json:"userId"` // 用户ID + ParentId string `gorm:"size:50" json:"parentId"` // 父评论ID + Content string `gorm:"type:text" json:"content"` // 评论内容 + LikeCount int `gorm:"default:0" json:"likeCount"` // 点赞数 +} + +func (RadioComment) TableName() string { + return "sundynix_radio_comment" +} diff --git a/model/radio/radio_favorite.go b/model/radio/radio_favorite.go new file mode 100644 index 0000000..5f30386 --- /dev/null +++ b/model/radio/radio_favorite.go @@ -0,0 +1,16 @@ +package radio + +import ( + "sundynix-go/global" +) + +// RadioFavorite 用户收藏表 +type RadioFavorite struct { + global.BaseModel + UserId string `gorm:"size:50;index" json:"userId"` // 用户ID + ProgramId string `gorm:"size:50;index" json:"programId"` // 节目ID +} + +func (RadioFavorite) TableName() string { + return "sundynix_radio_favorite" +} diff --git a/model/radio/radio_history.go b/model/radio/radio_history.go new file mode 100644 index 0000000..ad092fd --- /dev/null +++ b/model/radio/radio_history.go @@ -0,0 +1,18 @@ +package radio + +import ( + "sundynix-go/global" +) + +// RadioHistory 用户收听历史表 +type RadioHistory struct { + global.BaseModel + UserId string `gorm:"size:50;index" json:"userId"` // 用户ID + ProgramId string `gorm:"size:50;index" json:"programId"` // 节目ID + Progress int `gorm:"default:0" json:"progress"` // 播放进度(秒) + Duration int `gorm:"default:0" json:"duration"` // 节目总时长(秒) +} + +func (RadioHistory) TableName() string { + return "sundynix_radio_history" +} diff --git a/model/radio/radio_like.go b/model/radio/radio_like.go new file mode 100644 index 0000000..8b2b581 --- /dev/null +++ b/model/radio/radio_like.go @@ -0,0 +1,16 @@ +package radio + +import ( + "sundynix-go/global" +) + +// RadioLike 用户点赞表 +type RadioLike struct { + global.BaseModel + UserId string `gorm:"size:50;index" json:"userId"` // 用户ID + ProgramId string `gorm:"size:50;index" json:"programId"` // 节目ID +} + +func (RadioLike) TableName() string { + return "sundynix_radio_like" +} diff --git a/model/radio/radio_program.go b/model/radio/radio_program.go new file mode 100644 index 0000000..711d389 --- /dev/null +++ b/model/radio/radio_program.go @@ -0,0 +1,28 @@ +package radio + +import ( + "sundynix-go/global" + "sundynix-go/model/system" +) + +// RadioProgram 电台节目表 +type RadioProgram struct { + global.BaseModel + ChannelId string `gorm:"size:50;index" json:"channelId"` // 频道ID + Title string `gorm:"size:100" json:"title"` // 节目标题 + Description string `gorm:"size:500" json:"description"` // 节目描述 + Content string `gorm:"type:text" json:"content"` + CoverId string `gorm:"size:50" json:"coverId"` // 封面图OSS ID + Cover *system.Oss `gorm:"foreignKey:CoverId" json:"coverUrl"` // 封面图OSS + AudioId string `gorm:"size:50" json:"audioId"` // 音频OSS ID + Audio *system.Oss `gorm:"foreignKey:AudioId" json:"audio"` // 音频OSS + Duration int `gorm:"default:0" json:"duration"` // 时长(秒) + Tags string `gorm:"size:255" json:"tags"` // 标签,逗号分隔 + PlayCount int `gorm:"default:0" json:"playCount"` // 播放次数 + LikeCount int `gorm:"default:0" json:"likeCount"` // 点赞次数 + Status int `gorm:"default:1" json:"status"` // 状态 0:下架 1:上架 +} + +func (RadioProgram) TableName() string { + return "sundynix_radio_program" +} diff --git a/model/radio/radio_subscription.go b/model/radio/radio_subscription.go new file mode 100644 index 0000000..beeb3de --- /dev/null +++ b/model/radio/radio_subscription.go @@ -0,0 +1,17 @@ +package radio + +import ( + "sundynix-go/global" +) + +// RadioSubscription 用户订阅表 +type RadioSubscription struct { + global.BaseModel + UserId string `gorm:"size:50;index;not null;uniqueIndex:idx_user_channel" json:"userId"` // 用户ID + ChannelId string `gorm:"size:50;index;uniqueIndex:idx_user_channel" json:"channelId"` // 频道ID + Status int `gorm:"type:tinyint;default:1"` //1-订阅中,2-已取消 +} + +func (RadioSubscription) TableName() string { + return "sundynix_radio_subscription" +} diff --git a/model/radio/radio_user.go b/model/radio/radio_user.go new file mode 100644 index 0000000..9637223 --- /dev/null +++ b/model/radio/radio_user.go @@ -0,0 +1,29 @@ +package radio + +import ( + "sundynix-go/global" + "sundynix-go/model/system" +) + +// RadioUser 小程序用户信息表 +type RadioUser struct { + global.BaseModel + UserId string `gorm:"size:50;uniqueIndex" json:"userId"` // 关联system用户ID + OpenId string `gorm:"size:80;uniqueIndex" json:"openId"` // 微信openid + UnionId string `gorm:"size:80" json:"unionId"` // 微信unionid + SessionKey string `gorm:"size:200" json:"sessionKey"` // 会话密钥 + NickName string `gorm:"size:50" json:"nickName"` // 昵称 + AvatarId string `gorm:"size:50" json:"avatarId"` // 头像OSS ID + Avatar *system.Oss `gorm:"foreignKey:AvatarId" json:"avatar"` // 头像OSS + Gender int `gorm:"default:0" json:"gender"` // 性别 0:未知 1:男 2:女 + Country string `gorm:"size:50" json:"country"` // 国家 + Province string `gorm:"size:50" json:"province"` // 省份 + City string `gorm:"size:50" json:"city"` // 城市 + Language string `gorm:"size:20" json:"language"` // 语言 + IsVip int `gorm:"default:0" json:"isVip"` // 是否VIP 0:否 1:是 + VipExpireAt *int64 `gorm:"type:bigint" json:"vipExpireAt"` // VIP过期时间 +} + +func (RadioUser) TableName() string { + return "sundynix_radio_user" +} diff --git a/model/radio/request/category.go b/model/radio/request/category.go new file mode 100644 index 0000000..4769fbe --- /dev/null +++ b/model/radio/request/category.go @@ -0,0 +1,32 @@ +package request + +import common "sundynix-go/model/commom/request" + +// GetCategoryList 获取分类列表请求 +type GetCategoryList struct { + common.PageInfo + Name string `json:"name" form:"name"` // 分类名称 + Status int `json:"status" form:"status"` // 状态 +} + +// SaveCategory 保存分类请求 +type SaveCategory struct { + Id string `json:"id" form:"id"` // 分类ID(更新时使用) + Name string `json:"name" binding:"required"` // 分类名称 + Description string `json:"description"` // 分类描述 + IconId string `json:"iconId"` // 图标URL + CoverId string `json:"coverId"` // 封面图URL + Sort int `json:"sort"` // 排序 + Status int `json:"status"` // 状态 +} + +// UpdateCategory 更新分类请求 +type UpdateCategory struct { + Id string `json:"id" binding:"required"` // 分类ID + Name string `json:"name"` // 分类名称 + Description string `json:"description"` // 分类描述 + IconId string `json:"iconId"` // 图标URL + CoverId string `json:"coverId"` // 封面图URL + Sort int `json:"sort"` // 排序 + Status int `json:"status"` // 状态 +} diff --git a/model/radio/request/channel.go b/model/radio/request/channel.go new file mode 100644 index 0000000..ce1c9f2 --- /dev/null +++ b/model/radio/request/channel.go @@ -0,0 +1,37 @@ +package request + +import common "sundynix-go/model/commom/request" + +// GetChannelList 获取频道列表请求 +type GetChannelList struct { + common.PageInfo + CategoryId string `json:"categoryId" form:"categoryId"` // 分类ID + Name string `json:"name" form:"name"` // 频道名称 + Status int `json:"status" form:"status"` // 状态 +} + +// SaveChannel 保存频道请求 +type SaveChannel struct { + Id string `json:"id" form:"id"` // 频道ID(更新时使用) + CategoryId string `json:"categoryId" binding:"required"` // 分类ID + Name string `json:"name" binding:"required"` // 频道名称 + Description string `json:"description"` // 频道描述 + CoverId string `json:"coverId"` // 封面图URL + Tags string `json:"tags"` // 标签 + IsVipOnly int `json:"isVipOnly"` // 是否VIP专享 + Sort int `json:"sort"` // 排序 + Status int `json:"status"` // 状态 +} + +// UpdateChannel 更新频道请求 +type UpdateChannel struct { + Id string `json:"id" binding:"required"` // 频道ID + CategoryId string `json:"categoryId"` // 分类ID + Name string `json:"name"` // 频道名称 + Description string `json:"description"` // 频道描述 + CoverId string `json:"coverId"` // 封面图URL + Tags string `json:"tags"` // 标签 + IsVipOnly int `json:"isVipOnly"` // 是否VIP专享 + Sort int `json:"sort"` // 排序 + Status int `json:"status"` // 状态 +} diff --git a/model/radio/request/interaction.go b/model/radio/request/interaction.go new file mode 100644 index 0000000..79eb655 --- /dev/null +++ b/model/radio/request/interaction.go @@ -0,0 +1,68 @@ +package request + +import common "sundynix-go/model/commom/request" + +// SubscribeChannel 订阅频道请求 +type SubscribeChannel struct { + ChannelId string `json:"channelId" binding:"required"` // 频道ID +} + +// UnsubscribeChannel 退订频道请求 +type UnsubscribeChannel struct { + ChannelId string `json:"channelId" binding:"required"` // 频道ID +} + +// AddHistory 添加收听历史请求 +type AddHistory struct { + ProgramId string `json:"programId" binding:"required"` // 节目ID + Progress int `json:"progress"` // 播放进度(秒) + Duration int `json:"duration"` // 节目总时长(秒) +} + +// ToggleLike 切换点赞请求 +type ToggleLike struct { + ProgramId string `json:"programId" binding:"required"` // 节目ID +} + +// AddFavorite 添加收藏请求 +type AddFavorite struct { + ProgramId string `json:"programId" binding:"required"` // 节目ID +} + +// RemoveFavorite 移除收藏请求 +type RemoveFavorite struct { + ProgramId string `json:"programId" binding:"required"` // 节目ID +} + +// AddComment 添加评论请求 +type AddComment struct { + ProgramId string `json:"programId" binding:"required"` // 节目ID + ParentId string `json:"parentId"` // 父评论ID + Content string `json:"content" binding:"required"` // 评论内容 +} + +// DeleteComment 删除评论请求 +type DeleteComment struct { + CommentId string `json:"commentId" binding:"required"` // 评论ID +} + +// GetHistoryList 获取收听历史列表请求 +type GetHistoryList struct { + common.PageInfo +} + +// GetFavoriteList 获取收藏列表请求 +type GetFavoriteList struct { + common.PageInfo +} + +// GetCommentList 获取评论列表请求 +type GetCommentList struct { + common.PageInfo + ProgramId string `json:"programId" form:"programId"` // 节目ID +} + +// GetSubscriptionList 获取订阅列表请求 +type GetSubscriptionList struct { + common.PageInfo +} diff --git a/model/radio/request/program.go b/model/radio/request/program.go new file mode 100644 index 0000000..72e9476 --- /dev/null +++ b/model/radio/request/program.go @@ -0,0 +1,39 @@ +package request + +import common "sundynix-go/model/commom/request" + +// GetProgramList 获取节目列表请求 +type GetProgramList struct { + common.PageInfo + ChannelId string `json:"channelId" form:"channelId"` // 频道ID + Title string `json:"title" form:"title"` // 节目标题 + Status int `json:"status" form:"status"` // 状态 +} + +// SaveProgram 保存节目请求 +type SaveProgram struct { + Id string `json:"id" form:"id"` // 节目ID(更新时使用) + ChannelId string `json:"channelId" binding:"required"` // 频道ID + Title string `json:"title" binding:"required"` // 节目标题 + Description string `json:"description"` // 节目描述 + Content string `json:"content"` + CoverId string `json:"coverId"` // 封面图URL + AudioId string `json:"audioId"` // 音频URL + Duration int `json:"duration"` // 时长(秒) + Tags string `json:"tags"` // 标签 + Status int `json:"status"` // 状态 +} + +// UpdateProgram 更新节目请求 +type UpdateProgram struct { + Id string `json:"id" binding:"required"` // 节目ID + ChannelId string `json:"channelId"` // 频道ID + Title string `json:"title"` // 节目标题 + Description string `json:"description"` // 节目描述 + Content string `json:"content"` + CoverId string `json:"coverId"` // 封面图URL + AudioId string `json:"audioId"` // 音频URL + Duration int `json:"duration"` // 时长(秒) + Tags string `json:"tags"` // 标签 + Status int `json:"status"` // 状态 +} diff --git a/model/radio/response/category.go b/model/radio/response/category.go new file mode 100644 index 0000000..2e0cf4d --- /dev/null +++ b/model/radio/response/category.go @@ -0,0 +1,43 @@ +package response + +import "sundynix-go/model/radio" + +// CategoryResponse 分类响应 +type CategoryResponse struct { + Id string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Icon string `json:"icon"` + CoverUrl string `json:"coverUrl"` + Sort int `json:"sort"` + Status int `json:"status"` +} + +// CategoryDetailResponse 分类详情响应 +type CategoryDetailResponse struct { + Id string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Icon string `json:"icon"` + CoverUrl string `json:"coverUrl"` + Sort int `json:"sort"` + Status int `json:"status"` +} + +// ToCategoryResponse 转换为分类响应 +func ToCategoryResponse(category *radio.RadioCategory) CategoryResponse { + resp := CategoryResponse{ + Id: category.Id, + Name: category.Name, + Description: category.Description, + Sort: category.Sort, + Status: category.Status, + } + if category.Icon != nil { + resp.Icon = category.Icon.Url + } + if category.Cover != nil { + resp.CoverUrl = category.Cover.Url + } + return resp +} diff --git a/model/radio/response/channel.go b/model/radio/response/channel.go new file mode 100644 index 0000000..ce89545 --- /dev/null +++ b/model/radio/response/channel.go @@ -0,0 +1,29 @@ +package response + +// ChannelResponse 频道响应 +type ChannelResponse struct { + Id string `json:"id"` + CategoryId string `json:"categoryId"` + Name string `json:"name"` + Description string `json:"description"` + CoverUrl string `json:"coverUrl"` + StreamUrl string `json:"streamUrl"` + Tags string `json:"tags"` + IsVipOnly int `json:"isVipOnly"` + Sort int `json:"sort"` + Status int `json:"status"` +} + +// ChannelDetailResponse 频道详情响应 +type ChannelDetailResponse struct { + Id string `json:"id"` + CategoryId string `json:"categoryId"` + Name string `json:"name"` + Description string `json:"description"` + CoverUrl string `json:"coverUrl"` + StreamUrl string `json:"streamUrl"` + Tags string `json:"tags"` + IsVipOnly int `json:"isVipOnly"` + Sort int `json:"sort"` + Status int `json:"status"` +} diff --git a/plans/radio_module_plan.md b/plans/radio_module_plan.md new file mode 100644 index 0000000..60d1e73 --- /dev/null +++ b/plans/radio_module_plan.md @@ -0,0 +1,350 @@ +# Radio 模块开发计划 (v2.0) + +## 1. 项目架构分析 + +### 1.1 现有项目结构 +本项目是基于 Gin + Gorm 的 Go 后端服务,采用典型的分层架构: + +``` +morning-radio-backend/ +├── main.go # 程序入口 +├── api/v1/ # API 路由处理层 +├── router/ # 路由定义层 +├── service/ # 业务逻辑层 +├── model/ # 数据模型层 +├── initialize/ # 初始化模块 +├── global/ # 全局变量 +├── middleware/ # 中间件 +├── config/ # 配置管理 +└── utils/ # 工具类 +``` + +## 2. Radio 模块设计 (v2.0) + +### 2.1 功能模块层级 + +``` +分类 (Category) + └── 频道 (Channel) + └── 节目 (Program) +``` + +### 2.2 用户权限体系 + +| 权限类型 | 说明 | +|----------|------| +| 免费用户 | 可订阅2个频道,解锁对应节目 | +| 订阅用户 | 支付订阅特定频道,解锁该频道所有节目 | +| VIP用户 | 解锁全部频道的所有节目 | + +### 2.3 数据库表设计 + +#### 2.3.1 分类表 (radio_category) + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | string | 主键ID (继承BaseModel) | +| name | string | 分类名称 | +| description | string | 分类描述 | +| icon | string | 图标URL | +| sort | int | 排序 | +| status | int | 状态(0:禁用 1:启用) | + +#### 2.3.2 频道表 (radio_channel) + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | string | 主键ID (继承BaseModel) | +| category_id | string | 分类ID | +| name | string | 频道名称 | +| description | string | 频道描述 | +| cover_url | string | 封面图URL | +| stream_url | string | 音频流地址 | +| tags | string | 标签 | +| is_vip_only | int | 是否VIP专享(0:否 1:是) | +| sort | int | 排序 | +| status | int | 状态(0:禁用 1:启用) | + +#### 2.3.3 节目表 (radio_program) + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | string | 主键ID (继承BaseModel) | +| channel_id | string | 频道ID | +| title | string | 节目标题 | +| description | string | 节目描述 | +| cover_url | string | 封面图URL | +| audio_url | string | 音频URL | +| duration | int | 时长(秒) | +| tags | string | 标签 | +| play_count | int | 播放次数 | +| like_count | int | 点赞次数 | +| status | int | 状态(0:下架 1:上架) | + +#### 2.3.4 用户订阅表 (radio_subscription) + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | string | 主键ID (继承BaseModel) | +| user_id | string | 用户ID | +| channel_id | string | 频道ID | +| subscription_type | int | 订阅类型(1:免费 2:付费 3:VIP) | +| expire_at | time | 过期时间 | +| created_at | time | 订阅时间 | + +#### 2.3.5 收听历史表 (radio_history) + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | string | 主键ID (继承BaseModel) | +| user_id | string | 用户ID | +| program_id | string | 节目ID | +| progress | int | 播放进度(秒) | +| duration | int | 节目总时长 | +| created_at | time | 收听时间 | + +#### 2.3.6 点赞表 (radio_like) + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | string | 主键ID (继承BaseModel) | +| user_id | string | 用户ID | +| program_id | string | 节目ID | +| created_at | time | 点赞时间 | + +#### 2.3.7 收藏表 (radio_favorite) + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | string | 主键ID (继承BaseModel) | +| user_id | string | 用户ID | +| program_id | string | 节目ID | +| created_at | time | 收藏时间 | + +#### 2.3.8 评论表 (radio_comment) + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | string | 主键ID (继承BaseModel) | +| program_id | string | 节目ID | +| user_id | string | 用户ID | +| parent_id | string | 父评论ID | +| content | string | 评论内容 | +| like_count | int | 点赞数 | +| created_at | time | 评论时间 | + +### 2.4 Radio 模块目录结构 + +``` +model/radio/ +├── radio_category.go # 分类模型 +├── radio_channel.go # 频道模型 +├── radio_program.go # 节目模型 +├── radio_subscription.go # 订阅模型 +├── radio_favorite.go # 收藏模型 +├── radio_history.go # 收听历史模型 +├── radio_like.go # 点赞模型 +├── radio_comment.go # 评论模型 +├── request/ +│ ├── category.go # 分类请求结构 +│ ├── channel.go # 频道请求结构 +│ ├── program.go # 节目请求结构 +│ └── interaction.go # 互动请求结构 +└── response/ + ├── category.go # 分类响应结构 + ├── channel.go # 频道响应结构 + ├── program.go # 节目响应结构 + └── interaction.go # 互动响应结构 + +api/v1/radio/ +├── enter.go # Radio API组定义 +├── category.go # 分类API +├── channel.go # 频道API +├── program.go # 节目API +├── subscription.go # 订阅API +└── interaction.go # 互动API + +router/radio/ +├── enter.go # Radio 路由组定义 +├── category_router.go # 分类路由 +├── channel_router.go # 频道路由 +├── program_router.go # 节目路由 +├── subscription_router.go # 订阅路由 +└── interaction_router.go # 互动路由 + +service/radio/ +├── enter.go # Radio 服务组定义 +├── category_service.go # 分类业务逻辑 +├── channel_service.go # 频道业务逻辑 +├── program_service.go # 节目业务逻辑 +├── subscription_service.go # 订阅业务逻辑 +└── interaction_service.go # 互动业务逻辑 +``` + +## 3. 开发步骤 + +### 步骤1: 创建模块目录结构和文件骨架 + +- [ ] 创建 model/radio/ 目录及子目录 +- [ ] 创建 api/v1/radio/ 目录 +- [ ] 创建 router/radio/ 目录 +- [ ] 创建 service/radio/ 目录 +- [ ] 创建各层级的 enter.go 入口文件 + +### 步骤2: 实现数据模型层 + +- [ ] 实现 RadioCategory 分类模型 +- [ ] 实现 RadioChannel 频道模型 +- [ ] 实现 RadioProgram 节目模型 +- [ ] 实现 RadioSubscription 订阅模型 +- [ ] 实现 RadioHistory 收听历史模型 +- [ ] 实现 RadioLike 点赞模型 +- [ ] 实现 RadioFavorite 收藏模型 +- [ ] 实现 RadioComment 评论模型 + +### 步骤3: 实现请求/响应数据结构 + +- [ ] 实现分类请求/响应结构 +- [ ] 实现频道请求/响应结构 +- [ ] 实现节目请求/响应结构 +- [ ] 实现订阅请求/响应结构 +- [ ] 实现互动请求/响应结构 + +### 步骤4: 实现业务逻辑层 + +- [ ] 实现分类Service (CRUD) +- [ ] 实现频道Service (CRUD, 获取用户权限下的节目列表) +- [ ] 实现节目Service (CRUD, 根据用户权限过滤 实现订阅Service () +- [ ]订阅/退订/查询/检查权限) +- [ ] 实现收听历史Service (记录/列表) +- [ ] 实现点赞Service (点赞/取消/状态) +- [ ] 实现收藏Service (添加/删除/列表) +- [ ] 实现评论Service (发表/删除/列表) + +### 步骤5: 实现API路由处理层 + +- [ ] 实现分类API +- [ ] 实现频道API +- [ ] 实现节目API +- [ ] 实现订阅API +- [ ] 实现互动API + +### 步骤6: 实现路由定义层 + +- [ ] 实现分类路由初始化 +- [ ] 实现频道路由初始化 +- [ ] 实现节目路由初始化 +- [ ] 实现订阅路由初始化 +- [ ] 实现互动路由初始化 + +### 步骤7: 注册模块到全局 + +- [ ] 修改 api/v1/enter.go 添加 RadioApiGroup +- [ ] 修改 router/enter.go 添加 RadioRouterGroup +- [ ] 修改 service/enter.go 添加 RadioServiceGroup +- [ ] 修改 initialize/router.go 注册Radio路由 + +## 4. API 接口设计 + +### 4.1 分类管理 API + +| 方法 | 路径 | 说明 | 权限 | +|------|------|------|------| +| GET | /radio/category/list | 获取分类列表 | 公开 | +| GET | /radio/category/detail | 获取分类详情 | 公开 | +| POST | /radio/category/save | 新增分类 | 需鉴权 | +| POST | /radio/category/update | 更新分类 | 需鉴权 | +| POST | /radio/category/delete | 删除分类 | 需鉴权 | + +### 4.2 频道管理 API + +| 方法 | 路径 | 说明 | 权限 | +|------|------|------|------| +| GET | /radio/channel/list | 获取频道列表(按分类) | 公开 | +| GET | /radio/channel/detail | 获取频道详情 | 公开 | +| GET | /radio/channel/programs | 获取频道节目(权限过滤) | 需鉴权 | +| POST | /radio/channel/save | 新增频道 | 需鉴权 | +| POST | /radio/channel/update | 更新频道 | 需鉴权 | +| POST | /radio/channel/delete | 删除频道 | 需鉴权 | + +### 4.3 节目管理 API + +| 方法 | 路径 | 说明 | 权限 | +|------|------|------|------| +| GET | /radio/program/list | 获取节目列表 | 公开 | +| GET | /radio/program/detail | 获取节目详情(权限验证) | 需鉴权 | +| POST | /radio/program/save | 新增节目 | 需鉴权 | +| POST | /radio/program/update | 更新节目 | 需鉴权 | +| POST | /radio/program/delete | 删除节目 | 需鉴权 | + +### 4.4 订阅管理 API + +| 方法 | 路径 | 说明 | 权限 | +|------|------|------|------| +| GET | /radio/subscription/list | 获取我的订阅列表 | 需鉴权 | +| GET | /radio/subscription/can-subscribe | 检查是否可以订阅 | 需鉴权 | +| POST | /radio/subscription/subscribe | 订阅频道 | 需鉴权 | +| POST | /radio/subscription/unsubscribe | 退订频道 | 需鉴权 | +| GET | /radio/subscription/my-vip-status | 获取VIP状态 | 需鉴权 | + +### 4.5 用户互动 API + +| 方法 | 路径 | 说明 | 权限 | +|------|------|------|------| +| GET | /radio/history/list | 获取收听历史 | 需鉴权 | +| POST | /radio/history/add | 添加收听历史 | 需鉴权 | +| POST | /radio/like/toggle | 切换点赞状态 | 需鉴权 | +| GET | /radio/favorite/list | 获取收藏列表 | 需鉴权 | +| POST | /radio/favorite/add | 添加收藏 | 需鉴权 | +| POST | /radio/favorite/remove | 取消收藏 | 需鉴权 | +| GET | /radio/comment/list | 获取评论列表 | 公开 | +| POST | /radio/comment/add | 添加评论 | 需鉴权 | +| POST | /radio/comment/delete | 删除评论 | 需鉴权 | + +## 5. 权限检查逻辑 + +### 5.1 节目访问权限检查 + +```go +func CanAccessProgram(userId, programId string) bool { + // 1. 获取节目信息 + program := GetProgram(programId) + channel := GetChannel(program.ChannelId) + + // 2. 检查频道是否为VIP专享 + if channel.IsVipOnly == 1 { + return user.HasVip(userId) + } + + // 3. 检查用户是否订阅了该频道 + return user.HasSubscription(userId, channel.Id) +} +``` + +### 5.2 订阅数量限制检查 + +```go +func CanSubscribe(userId string) (bool, string) { + // 1. 检查是否为VIP + if user.IsVip(userId) { + return true, "" + } + + // 2. 检查免费订阅数量(上限2个) + count := GetFreeSubscriptionCount(userId) + if count >= 2 { + return false, "免费订阅数量已达上限(2个),请开通VIP或订阅付费频道" + } + + return true, "" +} +``` + +## 6. 注意事项 + +1. **继承BaseModel**: 所有模型必须继承 global.BaseModel +2. **分层架构**: 严格按照 api -> service -> model 层级开发 +3. **鉴权中间件**: 新增/修改/删除操作需要使用 NeedAuthGroup +4. **权限验证**: 节目详情API需要验证用户权限 +5. **分页查询**: 使用 common.PageInfo +6. **响应格式**: 使用 response.OkWithData() 和 response.FailWithMsg() diff --git a/router/enter.go b/router/enter.go index f1b3d88..372c4cc 100644 --- a/router/enter.go +++ b/router/enter.go @@ -1,6 +1,7 @@ package router import ( + "sundynix-go/router/radio" "sundynix-go/router/system" ) @@ -9,4 +10,5 @@ var GroupApp = new(Group) // Group 路由组 type Group struct { System system.SysRouterGroup + Radio radio.RadioRouterGroup } diff --git a/router/radio/category_router.go b/router/radio/category_router.go new file mode 100644 index 0000000..2f8943c --- /dev/null +++ b/router/radio/category_router.go @@ -0,0 +1,19 @@ +package radio + +import ( + "github.com/gin-gonic/gin" +) + +type CategoryRouter struct{} + +func (r *CategoryRouter) InitCategoryRouter(Router *gin.RouterGroup) { + categoryRouter := Router.Group("/radio/category") + { + categoryRouter.POST("page", categoryApi.GetCategoryPage) + categoryRouter.POST("list", categoryApi.GetCategoryList) + categoryRouter.GET("detail", categoryApi.GetCategoryDetail) + categoryRouter.POST("save", categoryApi.SaveCategory) + categoryRouter.POST("update", categoryApi.UpdateCategory) + categoryRouter.POST("delete", categoryApi.DeleteCategory) + } +} diff --git a/router/radio/channel_router.go b/router/radio/channel_router.go new file mode 100644 index 0000000..a4d52ff --- /dev/null +++ b/router/radio/channel_router.go @@ -0,0 +1,18 @@ +package radio + +import ( + "github.com/gin-gonic/gin" +) + +type ChannelRouter struct{} + +func (r *ChannelRouter) InitChannelRouter(Router *gin.RouterGroup) { + channelRouter := Router.Group("/radio/channel") + { + channelRouter.POST("list", channelApi.GetChannelList) + channelRouter.GET("detail", channelApi.GetChannelDetail) + channelRouter.POST("save", channelApi.SaveChannel) + channelRouter.POST("update", channelApi.UpdateChannel) + channelRouter.POST("delete", channelApi.DeleteChannel) + } +} diff --git a/router/radio/enter.go b/router/radio/enter.go new file mode 100644 index 0000000..b046952 --- /dev/null +++ b/router/radio/enter.go @@ -0,0 +1,21 @@ +package radio + +import v1 "sundynix-go/api/v1" + +type RadioRouterGroup struct { + CategoryRouter + ChannelRouter + ProgramRouter + SubscriptionRouter + InteractionRouter +} + +var GroupApp = new(RadioRouterGroup) + +var ( + categoryApi = v1.ApiGroupApp.RadioApiGroup.CategoryApi + channelApi = v1.ApiGroupApp.RadioApiGroup.ChannelApi + programApi = v1.ApiGroupApp.RadioApiGroup.ProgramApi + subscriptionApi = v1.ApiGroupApp.RadioApiGroup.SubscriptionApi + interactionApi = v1.ApiGroupApp.RadioApiGroup.InteractionApi +) diff --git a/router/radio/interaction_router.go b/router/radio/interaction_router.go new file mode 100644 index 0000000..db02c7a --- /dev/null +++ b/router/radio/interaction_router.go @@ -0,0 +1,38 @@ +package radio + +import ( + "github.com/gin-gonic/gin" +) + +type InteractionRouter struct{} + +func (r *InteractionRouter) InitInteractionRouter(Router *gin.RouterGroup) { + // 收听历史 + historyRouter := Router.Group("history") + { + historyRouter.POST("list", interactionApi.GetHistoryList) + historyRouter.POST("add", interactionApi.AddHistory) + } + + // 点赞 + likeRouter := Router.Group("like") + { + likeRouter.POST("toggle", interactionApi.ToggleLike) + } + + // 收藏 + favoriteRouter := Router.Group("favorite") + { + favoriteRouter.POST("list", interactionApi.GetFavoriteList) + favoriteRouter.POST("add", interactionApi.AddFavorite) + favoriteRouter.POST("remove", interactionApi.RemoveFavorite) + } + + // 评论 + commentRouter := Router.Group("comment") + { + commentRouter.POST("list", interactionApi.GetCommentList) + commentRouter.POST("add", interactionApi.AddComment) + commentRouter.POST("delete", interactionApi.DeleteComment) + } +} diff --git a/router/radio/program_router.go b/router/radio/program_router.go new file mode 100644 index 0000000..7658d7f --- /dev/null +++ b/router/radio/program_router.go @@ -0,0 +1,18 @@ +package radio + +import ( + "github.com/gin-gonic/gin" +) + +type ProgramRouter struct{} + +func (r *ProgramRouter) InitProgramRouter(Router *gin.RouterGroup) { + programRouter := Router.Group("/radio/program") + { + programRouter.POST("list", programApi.GetProgramList) + programRouter.GET("detail", programApi.GetProgramDetail) + programRouter.POST("save", programApi.SaveProgram) + programRouter.POST("update", programApi.UpdateProgram) + programRouter.POST("delete", programApi.DeleteProgram) + } +} diff --git a/router/radio/subscription_router.go b/router/radio/subscription_router.go new file mode 100644 index 0000000..e0d6d64 --- /dev/null +++ b/router/radio/subscription_router.go @@ -0,0 +1,17 @@ +package radio + +import ( + "github.com/gin-gonic/gin" +) + +type SubscriptionRouter struct{} + +func (r *SubscriptionRouter) InitSubscriptionRouter(Router *gin.RouterGroup) { + subscriptionRouter := Router.Group("radio/subscription") + { + subscriptionRouter.POST("list", subscriptionApi.GetSubscriptionList) + subscriptionRouter.POST("can-subscribe", subscriptionApi.CanSubscribe) + subscriptionRouter.POST("subscribe", subscriptionApi.Subscribe) + subscriptionRouter.POST("unsubscribe", subscriptionApi.Unsubscribe) + } +} diff --git a/service/enter.go b/service/enter.go index 467e190..f7c4575 100644 --- a/service/enter.go +++ b/service/enter.go @@ -1,6 +1,7 @@ package service import ( + "sundynix-go/service/radio" "sundynix-go/service/system" ) @@ -8,4 +9,5 @@ var GroupApp = new(Group) type Group struct { SystemServiceGroup system.ServiceGroup + RadioServiceGroup radio.ServiceGroup } diff --git a/service/radio/category_service.go b/service/radio/category_service.go new file mode 100644 index 0000000..553a58b --- /dev/null +++ b/service/radio/category_service.go @@ -0,0 +1,87 @@ +package radio + +import ( + "errors" + "sundynix-go/global" + "sundynix-go/model/radio" + radioReq "sundynix-go/model/radio/request" + + "gorm.io/gorm" +) + +type CategoryService struct{} + +// GetCategoryList 获取分类列表 +func (s *CategoryService) GetCategoryList(info radioReq.GetCategoryList) ([]radio.RadioCategory, int64, error) { + db := global.DB.Model(&radio.RadioCategory{}).Preload("Icon").Preload("Cover") + var list []radio.RadioCategory + var total int64 + + if info.Name != "" { + db = db.Where("name LIKE ?", "%"+info.Name+"%") + } + if info.Status > 0 { + db = db.Where("status = ?", info.Status) + } + + err := db.Count(&total).Error + if err != nil { + return nil, 0, err + } + + offset := (info.Current - 1) * info.PageSize + err = db.Offset(offset).Limit(info.PageSize).Order("sort ASC").Find(&list).Error + return list, total, err +} + +func (s *CategoryService) GetAllCategory() ([]radio.RadioCategory, error) { + var res []radio.RadioCategory + err := global.DB.Find(&res).Preload(":Icon").Preload("Cover").Error + return res, err +} + +// GetCategoryById 获取分类详情 +func (s *CategoryService) GetCategoryById(id string) (*radio.RadioCategory, error) { + var category radio.RadioCategory + err := global.DB.Where("id = ?", id).Preload("Icon").Preload("Cover").First(&category).Error + return &category, err +} + +// SaveCategory 保存分类 +func (s *CategoryService) SaveCategory(req radioReq.SaveCategory) error { + category := radio.RadioCategory{ + Name: req.Name, + Description: req.Description, + IconId: req.IconId, + CoverId: req.CoverId, + Sort: req.Sort, + Status: req.Status, + } + return global.DB.Create(&category).Error +} + +// UpdateCategory 更新分类 +func (s *CategoryService) UpdateCategory(req radioReq.UpdateCategory) error { + updates := map[string]interface{}{ + "name": req.Name, + "description": req.Description, + "icon_id": req.IconId, + "cover_id": req.CoverId, + "sort": req.Sort, + "status": req.Status, + } + return global.DB.Model(&radio.RadioCategory{}).Where("id = ?", req.Id).Updates(updates).Error +} + +// DeleteCategory 删除分类 +func (s *CategoryService) DeleteCategory(id string) error { + return global.DB.Transaction(func(tx *gorm.DB) error { + // 检查是否有频道使用此分类 + var count int64 + tx.Model(&radio.RadioChannel{}).Where("category_id = ?", id).Count(&count) + if count > 0 { + return errors.New("该分类下存在频道,无法删除") + } + return tx.Where("id = ?", id).Delete(&radio.RadioCategory{}).Error + }) +} diff --git a/service/radio/channel_service.go b/service/radio/channel_service.go new file mode 100644 index 0000000..8d0d896 --- /dev/null +++ b/service/radio/channel_service.go @@ -0,0 +1,127 @@ +package radio + +import ( + "errors" + + "sundynix-go/global" + "sundynix-go/model/radio" + radioReq "sundynix-go/model/radio/request" + + "gorm.io/gorm" +) + +type ChannelService struct{} + +// GetChannelList 获取频道列表 +func (s *ChannelService) GetChannelList(userId string, info radioReq.GetChannelList) ([]radio.RadioChannel, int64, error) { + db := global.DB.Model(&radio.RadioChannel{}) + var list []radio.RadioChannel + var total int64 + + if info.CategoryId != "" { + db = db.Where("category_id = ?", info.CategoryId) + } + if info.Name != "" { + db = db.Where("name LIKE ?", "%"+info.Name+"%") + } + if info.Status > 0 { + db = db.Where("status = ?", info.Status) + } + + err := db.Count(&total).Error + if err != nil { + return nil, 0, err + } + + offset := (info.Current - 1) * info.PageSize + err = db.Offset(offset).Limit(info.PageSize).Order("sort ASC").Find(&list).Error + if err != nil { + return nil, 0, err + } + + // 批量查询用户订阅的频道,避免N+1问题 + if userId != "" { + subService := &SubscriptionService{} + subscribedChannelIds, _ := subService.GetUserSubscriptionHistory(userId) + // 转换为map以便快速查找 + subscribedMap := make(map[string]bool) + for _, cid := range subscribedChannelIds { + subscribedMap[cid] = true + } + // 填充HasSubscribed字段 + for i := range list { + if subscribedMap[list[i].Id] { + list[i].HasSubscribed = 1 + } else { + list[i].HasSubscribed = 0 + } + } + } + + return list, total, nil +} + +// GetChannelById 获取频道详情 +func (s *ChannelService) GetChannelById(userId, id string) (radio.RadioChannel, error) { + var channel radio.RadioChannel + err := global.DB.Where("id = ?", id).Preload("Cover").First(&channel).Error + if err != nil { + return channel, err + } + + // 填充HasSubscribed字段 + if userId != "" { + subService := &SubscriptionService{} + hasSub, _ := subService.HasSubscription(userId, channel.Id) + if hasSub { + channel.HasSubscribed = 1 + } else { + channel.HasSubscribed = 0 + } + } + + return channel, nil +} + +// SaveChannel 保存频道 +func (s *ChannelService) SaveChannel(req radioReq.SaveChannel) error { + channel := radio.RadioChannel{ + CategoryId: req.CategoryId, + Name: req.Name, + Description: req.Description, + CoverId: req.CoverId, + Tags: req.Tags, + IsVipOnly: req.IsVipOnly, + Sort: req.Sort, + Status: req.Status, + } + return global.DB.Create(&channel).Error +} + +// UpdateChannel 更新频道 +func (s *ChannelService) UpdateChannel(req radioReq.UpdateChannel) error { + updates := map[string]interface{}{ + "category_id": req.CategoryId, + "name": req.Name, + "description": req.Description, + "cover_id": req.CoverId, + "tags": req.Tags, + "is_vip_only": req.IsVipOnly, + "sort": req.Sort, + "status": req.Status, + } + return global.DB.Model(&radio.RadioChannel{}).Where("id = ?", req.Id).Updates(updates).Error +} + +// DeleteChannel 删除频道 +func (s *ChannelService) DeleteChannel(id string) error { + return global.DB.Transaction(func(tx *gorm.DB) error { + // 检查是否有节目使用此频道 + var count int64 + tx.Model(&radio.RadioProgram{}).Where("channel_id = ?", id).Count(&count) + if count > 0 { + return errors.New("该频道下存在节目,无法删除") + } + return tx.Where("id = ?", id).Delete(&radio.RadioChannel{}).Error + }) +} diff --git a/service/radio/enter.go b/service/radio/enter.go new file mode 100644 index 0000000..a5d9df8 --- /dev/null +++ b/service/radio/enter.go @@ -0,0 +1,11 @@ +package radio + +type ServiceGroup struct { + CategoryService + ChannelService + ProgramService + SubscriptionService + InteractionService +} + +var GroupApp = new(ServiceGroup) diff --git a/service/radio/interaction_service.go b/service/radio/interaction_service.go new file mode 100644 index 0000000..2b5f241 --- /dev/null +++ b/service/radio/interaction_service.go @@ -0,0 +1,176 @@ +package radio + +import ( + "errors" + "sundynix-go/global" + "sundynix-go/model/radio" + radioReq "sundynix-go/model/radio/request" + + "gorm.io/gorm" +) + +type InteractionService struct{} + +// AddHistory 添加收听历史 +func (s *InteractionService) AddHistory(userId string, req radioReq.AddHistory) error { + // 先查找是否已存在记录 + var history radio.RadioHistory + err := global.DB.Where("user_id = ? AND program_id = ?", userId, req.ProgramId).First(&history).Error + + if errors.Is(err, gorm.ErrRecordNotFound) { + // 不存在,创建新记录 + history = radio.RadioHistory{ + UserId: userId, + ProgramId: req.ProgramId, + Progress: req.Progress, + Duration: req.Duration, + } + return global.DB.Create(&history).Error + } + + if err != nil { + return err + } + + // 存在,更新进度 + return global.DB.Model(&history).Updates(map[string]interface{}{ + "progress": req.Progress, + "duration": req.Duration, + }).Error +} + +// GetHistoryList 获取收听历史列表 +func (s *InteractionService) GetHistoryList(userId string, info radioReq.GetHistoryList) ([]radio.RadioHistory, int64, error) { + db := global.DB.Model(&radio.RadioHistory{}).Where("user_id = ?", userId) + var list []radio.RadioHistory + var total int64 + + err := db.Count(&total).Error + if err != nil { + return nil, 0, err + } + + offset := (info.Current - 1) * info.PageSize + err = db.Offset(offset).Limit(info.PageSize).Order("created_at DESC").Find(&list).Error + return list, total, err +} + +// ToggleLike 切换点赞状态 +func (s *InteractionService) ToggleLike(userId, programId string) (bool, error) { + var like radio.RadioLike + err := global.DB.Where("user_id = ? AND program_id = ?", userId, programId).First(&like).Error + + if errors.Is(err, gorm.ErrRecordNotFound) { + // 未点赞,添加点赞 + like = radio.RadioLike{ + UserId: userId, + ProgramId: programId, + } + if err := global.DB.Create(&like).Error; err != nil { + return false, err + } + // 增加节目点赞数 + global.DB.Model(&radio.RadioProgram{}).Where("id = ?", programId). + UpdateColumn("like_count", gorm.Expr("like_count + ?", 1)) + return true, nil + } + + if err != nil { + return false, err + } + + // 已点赞,取消点赞 + if err := global.DB.Delete(&like).Error; err != nil { + return false, err + } + // 减少节目点赞数 + global.DB.Model(&radio.RadioProgram{}).Where("id = ?", programId). + UpdateColumn("like_count", gorm.Expr("like_count - ?", 1)) + return false, nil +} + +// IsLiked 检查是否已点赞 +func (s *InteractionService) IsLiked(userId, programId string) (bool, error) { + var count int64 + err := global.DB.Model(&radio.RadioLike{}).Where("user_id = ? AND program_id = ?", userId, programId).Count(&count).Error + return count > 0, err +} + +// AddFavorite 添加收藏 +func (s *InteractionService) AddFavorite(userId, programId string) error { + // 检查是否已收藏 + var existing radio.RadioFavorite + err := global.DB.Where("user_id = ? AND program_id = ?", userId, programId).First(&existing).Error + if err == nil { + return errors.New("已经收藏过该节目") + } + if !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } + + favorite := radio.RadioFavorite{ + UserId: userId, + ProgramId: programId, + } + return global.DB.Create(&favorite).Error +} + +// RemoveFavorite 取消收藏 +func (s *InteractionService) RemoveFavorite(userId, programId string) error { + return global.DB.Where("user_id = ? AND program_id = ?", userId, programId).Delete(&radio.RadioFavorite{}).Error +} + +// GetFavoriteList 获取收藏列表 +func (s *InteractionService) GetFavoriteList(userId string, info radioReq.GetFavoriteList) ([]radio.RadioFavorite, int64, error) { + db := global.DB.Model(&radio.RadioFavorite{}).Where("user_id = ?", userId) + var list []radio.RadioFavorite + var total int64 + + err := db.Count(&total).Error + if err != nil { + return nil, 0, err + } + + offset := (info.Current - 1) * info.PageSize + err = db.Offset(offset).Limit(info.PageSize).Order("created_at DESC").Find(&list).Error + return list, total, err +} + +// IsFavorited 检查是否已收藏 +func (s *InteractionService) IsFavorited(userId, programId string) (bool, error) { + var count int64 + err := global.DB.Model(&radio.RadioFavorite{}).Where("user_id = ? AND program_id = ?", userId, programId).Count(&count).Error + return count > 0, err +} + +// AddComment 添加评论 +func (s *InteractionService) AddComment(userId string, req radioReq.AddComment) error { + comment := radio.RadioComment{ + ProgramId: req.ProgramId, + UserId: userId, + ParentId: req.ParentId, + Content: req.Content, + } + return global.DB.Create(&comment).Error +} + +// DeleteComment 删除评论 +func (s *InteractionService) DeleteComment(userId, commentId string) error { + return global.DB.Where("id = ? AND user_id = ?", commentId, userId).Delete(&radio.RadioComment{}).Error +} + +// GetCommentList 获取评论列表 +func (s *InteractionService) GetCommentList(programId string, info radioReq.GetCommentList) ([]radio.RadioComment, int64, error) { + db := global.DB.Model(&radio.RadioComment{}).Where("program_id = ?", programId) + var list []radio.RadioComment + var total int64 + + err := db.Count(&total).Error + if err != nil { + return nil, 0, err + } + + offset := (info.Current - 1) * info.PageSize + err = db.Offset(offset).Limit(info.PageSize).Order("created_at DESC").Find(&list).Error + return list, total, err +} diff --git a/service/radio/program_service.go b/service/radio/program_service.go new file mode 100644 index 0000000..0693121 --- /dev/null +++ b/service/radio/program_service.go @@ -0,0 +1,98 @@ +package radio + +import ( + "sundynix-go/global" + "sundynix-go/model/radio" + radioReq "sundynix-go/model/radio/request" + + "gorm.io/gorm" +) + +type ProgramService struct{} + +// GetProgramList 获取节目列表 +func (s *ProgramService) GetProgramList(info radioReq.GetProgramList) ([]radio.RadioProgram, int64, error) { + db := global.DB.Model(&radio.RadioProgram{}).Preload("Cover").Preload("Audio") + var list []radio.RadioProgram + var total int64 + + if info.ChannelId != "" { + db = db.Where("channel_id = ?", info.ChannelId) + } + if info.Title != "" { + db = db.Where("title LIKE ?", "%"+info.Title+"%") + } + if info.Status > 0 { + db = db.Where("status = ?", info.Status) + } + + err := db.Count(&total).Error + if err != nil { + return nil, 0, err + } + + offset := (info.Current - 1) * info.PageSize + err = db.Offset(offset).Limit(info.PageSize).Order("created_at DESC").Find(&list).Error + return list, total, err +} + +// GetProgramById 获取节目详情 +func (s *ProgramService) GetProgramById(id string) (*radio.RadioProgram, error) { + var program radio.RadioProgram + err := global.DB.Where("id = ?", id).Preload("Cover").Preload("Audio").First(&program).Error + return &program, err +} + +// SaveProgram 保存节目 +func (s *ProgramService) SaveProgram(req radioReq.SaveProgram) error { + program := radio.RadioProgram{ + ChannelId: req.ChannelId, + Title: req.Title, + Description: req.Description, + Content: req.Content, + CoverId: req.CoverId, + AudioId: req.AudioId, + Duration: req.Duration, + Tags: req.Tags, + Status: req.Status, + } + return global.DB.Create(&program).Error +} + +// UpdateProgram 更新节目 +func (s *ProgramService) UpdateProgram(req radioReq.UpdateProgram) error { + updates := map[string]interface{}{ + "channel_id": req.ChannelId, + "title": req.Title, + "description": req.Description, + "content": req.Content, + "cover_id": req.CoverId, + "audio_id": req.AudioId, + "duration": req.Duration, + "tags": req.Tags, + "status": req.Status, + } + return global.DB.Model(&radio.RadioProgram{}).Where("id = ?", req.Id).Updates(updates).Error +} + +// DeleteProgram 删除节目 +func (s *ProgramService) DeleteProgram(ids []string) error { + return global.DB.Transaction(func(tx *gorm.DB) error { + // 删除相关的收藏记录 + tx.Where("program_id in ?", ids).Delete(&radio.RadioFavorite{}) + // 删除相关的点赞记录 + tx.Where("program_id in ?", ids).Delete(&radio.RadioLike{}) + // 删除相关的历史记录 + tx.Where("program_id in ?", ids).Delete(&radio.RadioHistory{}) + // 删除相关的评论 + tx.Where("program_id in ?", ids).Delete(&radio.RadioComment{}) + // 删除节目 + return tx.Where("id in ?", ids).Delete(&radio.RadioProgram{}).Error + }) +} + +// IncrementPlayCount 增加播放次数 +func (s *ProgramService) IncrementPlayCount(id string) error { + return global.DB.Model(&radio.RadioProgram{}).Where("id = ?", id). + UpdateColumn("play_count", gorm.Expr("play_count + ?", 1)).Error +} diff --git a/service/radio/subscription_service.go b/service/radio/subscription_service.go new file mode 100644 index 0000000..07fafa7 --- /dev/null +++ b/service/radio/subscription_service.go @@ -0,0 +1,144 @@ +package radio + +import ( + "errors" + "sundynix-go/global" + common "sundynix-go/model/commom/request" + "sundynix-go/model/radio" + "time" + + "gorm.io/gorm" +) + +type SubscriptionService struct{} + +const MaxFreeSubscription = 2 + +// GetUserSubscription 获取用户订阅列表 +func (s *SubscriptionService) GetUserSubscription(userId string, info common.PageInfo) ([]radio.RadioSubscription, int64, error) { + db := global.DB.Model(&radio.RadioSubscription{}).Where("user_id = ?", userId) + var list []radio.RadioSubscription + var total int64 + + err := db.Count(&total).Error + if err != nil { + return nil, 0, err + } + + offset := (info.Current - 1) * info.PageSize + err = db.Offset(offset).Limit(info.PageSize).Order("created_at DESC").Find(&list).Error + return list, total, err +} + +// GetUserSubscriptionHistory 获取用户历史订阅过的频道ID列表 +func (s *SubscriptionService) GetUserSubscriptionHistory(userId string) ([]string, error) { + var channelIds []string + err := global.DB.Model(&radio.RadioSubscription{}). + Where("user_id = ?", userId). + Pluck("channel_id", &channelIds).Error + return channelIds, err +} + +// HasEverSubscribed 检查用户是否曾经订阅过该频道(包括已取消的) +func (s *SubscriptionService) HasEverSubscribed(userId, channelId string) (bool, error) { + var count int64 + // 使用Unscoped查询包括已软删除的记录 + err := global.DB.Unscoped().Model(&radio.RadioSubscription{}). + Where("user_id = ? AND channel_id = ?", userId, channelId). + Count(&count).Error + return count > 0, err +} + +// HasSubscription 检查用户当前是否订阅该频道(未取消的) +func (s *SubscriptionService) HasSubscription(userId, channelId string) (bool, error) { + var count int64 + err := global.DB.Model(&radio.RadioSubscription{}). + Where("user_id = ? AND channel_id = ?", userId, channelId). + Count(&count).Error + return count > 0, err +} + +// CanSubscribe 检查是否可以订阅 +// 规则: +// 1. 如果用户是VIP且未过期,可以订阅任意频道 +// 2. 如果用户曾经订阅过该频道(取消后再订阅),可以免费订阅 +// 3. 否则检查当前有效订阅数量是否达到上限(2个) +func (s *SubscriptionService) CanSubscribe(userId, channelId string) (bool, string, error) { + // 检查是否已经是订阅用户(未取消的订阅) + var existing radio.RadioSubscription + err := global.DB.Where("user_id = ? AND channel_id = ?", userId, channelId).First(&existing).Error + if err == nil { + return false, "您已订阅该频道", nil + } + if !errors.Is(err, gorm.ErrRecordNotFound) { + return false, "", err + } + + // 获取radio_user检查VIP状态 + var radioUser radio.RadioUser + err = global.DB.Where("user_id = ?", userId).First(&radioUser).Error + if err == nil && radioUser.IsVip == 1 { + // 检查VIP是否过期 + if radioUser.VipExpireAt != nil && *radioUser.VipExpireAt > time.Now().Unix() { + return true, "", nil + } + } + + // 检查用户是否曾经订阅过该频道(取消后又订阅的情况) + hasEverSubscribed, err := s.HasEverSubscribed(userId, channelId) + if err != nil { + return false, "", err + } + if hasEverSubscribed { + // 曾今订阅过,可以免费再次订阅 + return true, "", nil + } + + // 非VIP用户,检查当前有效订阅数量(排除已取消的) + var count int64 + err = global.DB.Model(&radio.RadioSubscription{}).Where("user_id = ?", userId).Count(&count).Error + if err != nil { + return false, "", err + } + if count >= MaxFreeSubscription { + return false, "免费订阅数量已达上限(2个),请开通VIP或订阅付费频道", nil + } + + return true, "", nil +} + +// Subscribe 订阅频道 +func (s *SubscriptionService) Subscribe(userId, channelId string, subType int) error { + subscription := radio.RadioSubscription{ + UserId: userId, + ChannelId: channelId, + } + return global.DB.Create(&subscription).Error +} + +// Unsubscribe 退订频道(逻辑删除,更新删除时间表示已取消) +func (s *SubscriptionService) Unsubscribe(userId, channelId string) error { + // 软删除:将DeletedAt设置为当前时间,表示已取消订阅 + // 这样用户可以再次免费订阅该频道 + return global.DB.Model(&radio.RadioSubscription{}). + Where("user_id = ? AND channel_id = ?", userId, channelId). + Update("deleted_at", time.Now()).Error +} + +// GetVipStatus 获取VIP状态 +func (s *SubscriptionService) GetVipStatus(userId string) (bool, int64, error) { + var radioUser radio.RadioUser + err := global.DB.Where("user_id = ?", userId).First(&radioUser).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return false, 0, nil + } + return false, 0, err + } + + if radioUser.IsVip == 1 && radioUser.VipExpireAt != nil && *radioUser.VipExpireAt > time.Now().Unix() { + return true, *radioUser.VipExpireAt, nil + } + + return false, 0, nil +} diff --git a/service/system/sys_user.go b/service/system/sys_user.go index d6ea35d..c147115 100644 --- a/service/system/sys_user.go +++ b/service/system/sys_user.go @@ -12,6 +12,7 @@ import ( "strconv" "sundynix-go/global" common "sundynix-go/model/commom/request" + "sundynix-go/model/radio" "sundynix-go/model/system" systemReq "sundynix-go/model/system/request" systemResp "sundynix-go/model/system/response" @@ -157,6 +158,17 @@ func (userService *UserService) MiniLogin(code string) (result *system.User, err if err := tx.Create(&newUser).Error; err != nil { return err } + // 创建小程序用户 + mpUser := radio.RadioUser{ + UserId: newUser.Id, + OpenId: wxResp.Openid, + UnionId: wxResp.Unionid, + SessionKey: wxResp.SessionKey, + } + if err := tx.Create(&mpUser).Error; err != nil { + return err + } + // 赋值给外部变量以便返回 user = newUser return nil