# 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()