feat: 长文本语音合成
This commit is contained in:
@@ -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 获取收藏列表
|
||||
|
||||
+8
-1
@@ -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:
|
||||
|
||||
+95
-4
@@ -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",
|
||||
|
||||
+95
-4
@@ -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",
|
||||
|
||||
+62
-3
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -55,6 +55,11 @@ type GetHistoryList struct {
|
||||
common.PageInfo
|
||||
}
|
||||
|
||||
// GetLikeList 获取点赞列表请求
|
||||
type GetLikeList struct {
|
||||
common.PageInfo
|
||||
}
|
||||
|
||||
// GetFavoriteList 获取收藏列表请求
|
||||
type GetFavoriteList struct {
|
||||
common.PageInfo
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
// 收藏
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user