From bdcd96a058c700602c6c7c7258519e584384730d Mon Sep 17 00:00:00 2001 From: Blizzard Date: Mon, 9 Mar 2026 17:25:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=95=BF=E6=96=87=E6=9C=AC=E8=AF=AD?= =?UTF-8?q?=E9=9F=B3=E5=90=88=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/v1/radio/interaction.go | 55 ++++++++++++++++ config-dev.yaml | 9 ++- docs/docs.go | 99 ++++++++++++++++++++++++++-- docs/swagger.json | 99 ++++++++++++++++++++++++++-- docs/swagger.yaml | 65 +++++++++++++++++- model/radio/radio_favorite.go | 5 +- model/radio/radio_like.go | 5 +- model/radio/radio_program.go | 2 +- model/radio/request/interaction.go | 5 ++ router/radio/interaction_router.go | 2 + service/radio/interaction_service.go | 22 ++++++- service/radio/program_service.go | 5 +- service/radio/tts_service.go | 8 ++- 13 files changed, 358 insertions(+), 23 deletions(-) diff --git a/api/v1/radio/interaction.go b/api/v1/radio/interaction.go index a42cc59..a5a53ed 100644 --- a/api/v1/radio/interaction.go +++ b/api/v1/radio/interaction.go @@ -154,6 +154,61 @@ func (a *InteractionApi) ToggleLike(c *gin.Context) { response.OkWithData(isLiked, c) } +// GetLikeList 获取点赞列表 +// @Tags 用户互动 +// @Summary 获取收藏列表 +// @Produce application/json +// @Param data body request.GetLikeList true "分页查询" +// @Success 200 {object} response.Response +// @Router /like/list [post] +func (a *InteractionApi) GetLikeList(c *gin.Context) { + userId := auth.GetUserId(c) + if userId == "" || userId == "0" { + response.FailWithMsg("用户未登录", c) + return + } + var req request.GetLikeList + err := c.ShouldBindJSON(&req) + if err != nil { + response.FailWithMsg("参数错误: "+err.Error(), c) + return + } + + list, total, err := interactionService.GetLikeList(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) +} + +// RemoveAllLike 清空所有赞 +// @Tags 用户互动 +// @Summary 清空所有赞 +// @Produce application/json +// @Success 200 {object} response.Response +// @Router /like/removeAll [get] +func (a *InteractionApi) RemoveAllLike(c *gin.Context) { + userId := auth.GetUserId(c) + if userId == "" || userId == "0" { + response.FailWithMsg("用户未登录", c) + return + } + err := interactionService.RemoveAllLike(userId) + if err != nil { + global.Logger.Error("清空所有赞失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + response.OkWithMsg("清空所有赞成功", c) +} + // GetFavoriteList 获取收藏列表 // @Tags 用户互动 // @Summary 获取收藏列表 diff --git a/config-dev.yaml b/config-dev.yaml index 70a2fc1..38dcfc7 100644 --- a/config-dev.yaml +++ b/config-dev.yaml @@ -22,6 +22,13 @@ mini-program: app-id: wx52dfc635739a9c19 app-secret: 84c6ddab1f24d0222da57bedb681c81f +# 腾讯文字转语音 +tencent-tts: + app-id: 1312892187 + secret-id: AKIDKaeU7XjhSzIOGuKWUEk26wY1MUP6asyr + secret-key: lU0JOFrGSSGqDMLKBoIbnmX6TcXIqKbe + + # 微信支付 wechat-pay: mch-id: 1735188493 # 商户号 @@ -75,7 +82,7 @@ redis: - 172.21.0.2:7002 db: 1 # name: "" - # password: "sundynix" + password: "sundynix" cluster: false zap: diff --git a/docs/docs.go b/docs/docs.go index 0c8d5f9..0ec548e 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -813,6 +813,55 @@ const docTemplate = `{ } } }, + "/like/list": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "用户互动" + ], + "summary": "获取收藏列表", + "parameters": [ + { + "description": "分页查询", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetLikeList" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/like/removeAll": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "用户互动" + ], + "summary": "清空所有赞", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, "/like/toggle": { "post": { "produces": [ @@ -1880,6 +1929,34 @@ const docTemplate = `{ } } }, + "/radio/program/generate-tts": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "节目管理" + ], + "summary": "生成TTS语音", + "parameters": [ + { + "type": "string", + "description": "节目ID", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, "/radio/program/list": { "post": { "produces": [ @@ -2636,7 +2713,7 @@ const docTemplate = `{ } }, "/vip/config/detail": { - "get": { + "post": { "produces": [ "application/json" ], @@ -2934,6 +3011,23 @@ const docTemplate = `{ } } }, + "request.GetLikeList": { + "type": "object", + "properties": { + "current": { + "description": "页码", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, "request.GetMenuTree": { "type": "object", "properties": { @@ -2967,9 +3061,6 @@ const docTemplate = `{ }, "request.GetProgramList": { "type": "object", - "required": [ - "channelId" - ], "properties": { "channelId": { "description": "频道ID", diff --git a/docs/swagger.json b/docs/swagger.json index 08ada91..14e2b62 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -806,6 +806,55 @@ } } }, + "/like/list": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "用户互动" + ], + "summary": "获取收藏列表", + "parameters": [ + { + "description": "分页查询", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.GetLikeList" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/like/removeAll": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "用户互动" + ], + "summary": "清空所有赞", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, "/like/toggle": { "post": { "produces": [ @@ -1873,6 +1922,34 @@ } } }, + "/radio/program/generate-tts": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "节目管理" + ], + "summary": "生成TTS语音", + "parameters": [ + { + "type": "string", + "description": "节目ID", + "name": "id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, "/radio/program/list": { "post": { "produces": [ @@ -2629,7 +2706,7 @@ } }, "/vip/config/detail": { - "get": { + "post": { "produces": [ "application/json" ], @@ -2927,6 +3004,23 @@ } } }, + "request.GetLikeList": { + "type": "object", + "properties": { + "current": { + "description": "页码", + "type": "integer" + }, + "keyword": { + "description": "关键字", + "type": "string" + }, + "pageSize": { + "description": "每页大小", + "type": "integer" + } + } + }, "request.GetMenuTree": { "type": "object", "properties": { @@ -2960,9 +3054,6 @@ }, "request.GetProgramList": { "type": "object", - "required": [ - "channelId" - ], "properties": { "channelId": { "description": "频道ID", diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 41f97e1..b625ad4 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -152,6 +152,18 @@ definitions: description: 每页大小 type: integer type: object + request.GetLikeList: + properties: + current: + description: 页码 + type: integer + keyword: + description: 关键字 + type: string + pageSize: + description: 每页大小 + type: integer + type: object request.GetMenuTree: properties: category: @@ -193,8 +205,6 @@ definitions: title: description: 节目标题 type: string - required: - - channelId type: object request.GetRoleList: properties: @@ -1179,6 +1189,37 @@ paths: summary: 获取收听历史列表 tags: - 用户互动 + /like/list: + post: + parameters: + - description: 分页查询 + in: body + name: data + required: true + schema: + $ref: '#/definitions/request.GetLikeList' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + summary: 获取收藏列表 + tags: + - 用户互动 + /like/removeAll: + get: + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + summary: 清空所有赞 + tags: + - 用户互动 /like/toggle: post: parameters: @@ -1816,6 +1857,24 @@ paths: summary: 获取节目详情 tags: - 节目管理 + /radio/program/generate-tts: + get: + parameters: + - description: 节目ID + in: query + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + summary: 生成TTS语音 + tags: + - 节目管理 /radio/program/list: post: parameters: @@ -2267,7 +2326,7 @@ paths: tags: - 用户管理 /vip/config/detail: - get: + post: parameters: - description: id in: query diff --git a/model/radio/radio_favorite.go b/model/radio/radio_favorite.go index 5f30386..23fafe7 100644 --- a/model/radio/radio_favorite.go +++ b/model/radio/radio_favorite.go @@ -7,8 +7,9 @@ import ( // RadioFavorite 用户收藏表 type RadioFavorite struct { global.BaseModel - UserId string `gorm:"size:50;index" json:"userId"` // 用户ID - ProgramId string `gorm:"size:50;index" json:"programId"` // 节目ID + UserId string `gorm:"size:50;index" json:"userId"` // 用户ID + ProgramId string `gorm:"size:50;index" json:"programId"` // 节目ID + RadioProgram *RadioProgram `gorm:"foreignKey:ProgramId" json:"program"` } func (RadioFavorite) TableName() string { diff --git a/model/radio/radio_like.go b/model/radio/radio_like.go index 8b2b581..d25f949 100644 --- a/model/radio/radio_like.go +++ b/model/radio/radio_like.go @@ -7,8 +7,9 @@ import ( // RadioLike 用户点赞表 type RadioLike struct { global.BaseModel - UserId string `gorm:"size:50;index" json:"userId"` // 用户ID - ProgramId string `gorm:"size:50;index" json:"programId"` // 节目ID + UserId string `gorm:"size:50;index" json:"userId"` // 用户ID + ProgramId string `gorm:"size:50;index" json:"programId"` // 节目ID + RadioProgram *RadioProgram `gorm:"foreignKey:ProgramId" json:"program"` } func (RadioLike) TableName() string { diff --git a/model/radio/radio_program.go b/model/radio/radio_program.go index d18504d..6d9c835 100644 --- a/model/radio/radio_program.go +++ b/model/radio/radio_program.go @@ -22,7 +22,7 @@ type RadioProgram struct { Status int `gorm:"default:1" json:"status"` // 状态 0:下架 1:上架 Channel *RadioChannel `gorm:"foreignKey:ChannelId" json:"channel"` HasLiked int `gorm:"-" json:"hasLiked"` // 是否点赞 - HasFavorite int `gorm:"-" json:"HasFavorite"` // 是否收藏 + HasFavorite int `gorm:"-" json:"hasFavorite"` // 是否收藏 } func (RadioProgram) TableName() string { diff --git a/model/radio/request/interaction.go b/model/radio/request/interaction.go index 8fde8c8..7aa497b 100644 --- a/model/radio/request/interaction.go +++ b/model/radio/request/interaction.go @@ -55,6 +55,11 @@ type GetHistoryList struct { common.PageInfo } +// GetLikeList 获取点赞列表请求 +type GetLikeList struct { + common.PageInfo +} + // GetFavoriteList 获取收藏列表请求 type GetFavoriteList struct { common.PageInfo diff --git a/router/radio/interaction_router.go b/router/radio/interaction_router.go index 19f415f..0de065b 100644 --- a/router/radio/interaction_router.go +++ b/router/radio/interaction_router.go @@ -20,6 +20,8 @@ func (r *InteractionRouter) InitInteractionRouter(Router *gin.RouterGroup) { likeRouter := Router.Group("like") { likeRouter.POST("toggle", interactionApi.ToggleLike) + likeRouter.POST("list", interactionApi.GetLikeList) + likeRouter.GET("removeAll", interactionApi.RemoveAllLike) } // 收藏 diff --git a/service/radio/interaction_service.go b/service/radio/interaction_service.go index 142cbfe..9baac5c 100644 --- a/service/radio/interaction_service.go +++ b/service/radio/interaction_service.go @@ -91,6 +91,22 @@ func (s *InteractionService) ToggleLike(userId, programId string) (bool, error) return false, nil } +// GetLikeList 获取点赞列表 +func (s *InteractionService) GetLikeList(userId string, req radioReq.GetLikeList) ([]radio.RadioLike, int64, error) { + db := global.DB.Model(&radio.RadioLike{}).Where("user_id = ?", userId).Preload("RadioProgram") + var list []radio.RadioLike + var total int64 + + err := db.Count(&total).Error + if err != nil { + return nil, 0, err + } + + offset := (req.Current - 1) * req.PageSize + err = db.Offset(offset).Limit(req.PageSize).Order("created_at DESC").Find(&list).Error + return list, total, err +} + // IsLiked 检查是否已点赞 func (s *InteractionService) IsLiked(userId, programId string) (bool, error) { var count int64 @@ -124,7 +140,7 @@ func (s *InteractionService) RemoveFavorite(userId, programId string) 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) + db := global.DB.Model(&radio.RadioFavorite{}).Where("user_id = ?", userId).Preload("RadioProgram") var list []radio.RadioFavorite var total int64 @@ -188,3 +204,7 @@ func (s *InteractionService) DeleteAllHistory(userId string) error { func (s *InteractionService) RemoveAllFavorite(userId string) error { return global.DB.Where("user_id = ?", userId).Delete(&radio.RadioFavorite{}).Error } + +func (s *InteractionService) RemoveAllLike(userId string) error { + return global.DB.Where("user_id = ?", userId).Delete(&radio.RadioLike{}).Error +} diff --git a/service/radio/program_service.go b/service/radio/program_service.go index 0777135..6014613 100644 --- a/service/radio/program_service.go +++ b/service/radio/program_service.go @@ -127,8 +127,9 @@ func (s *ProgramService) GenerateTTS(programId string) error { // 2. 调用TTS提交任务 (异步,后台处理) ttsReq := TTSTextToSpeechRequest{ - Text: program.Content, - VoiceType: 101021, // 亲和女声 + Text: program.Content, + //VoiceType: 101001, // 智瑜 情感女声 + VoiceType: 101013, // 智辉 新闻男声 Speed: 0, // 正常语速 Volume: 0, // 正常音量 ProgramId: programId, diff --git a/service/radio/tts_service.go b/service/radio/tts_service.go index 5468a32..3db15c8 100644 --- a/service/radio/tts_service.go +++ b/service/radio/tts_service.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "net/http" + "strconv" "time" "sundynix-go/global" @@ -160,7 +161,7 @@ func (t *TTSService) doSubmitTTSTask(req TTSTextToSpeechRequest) (string, error) // waitForResult 轮询查询任务结果,返回音频下载URL func (t *TTSService) waitForResult(taskId string) (string, error) { maxRetries := 30 - interval := 5 * time.Second + interval := 15 * time.Second for i := 0; i < maxRetries; i++ { time.Sleep(interval) @@ -256,8 +257,9 @@ func (t *TTSService) uploadToOSS(audioData []byte, programId string) (string, er if !ok { return "", fmt.Errorf("获取MinIO客户端失败") } - - key := fmt.Sprintf("audio/%s/%s.mp3", time.Now().Format("2006-01-02"), programId) + timestamp := time.Now().UnixMicro() + timestr := strconv.FormatInt(timestamp, 10) + key := fmt.Sprintf("audio/%s/%s.mp3", time.Now().Format("2006-01-02"), programId+"-"+timestr) filename := fmt.Sprintf("program-%s.mp3", programId) fileURL, err := minioClient.UploadBytes(audioData, key, "audio/mpeg")