feat: 长文本语音合成
This commit is contained in:
Generated
+6
-1
@@ -2,7 +2,12 @@
|
|||||||
<module type="WEB_MODULE" version="4">
|
<module type="WEB_MODULE" version="4">
|
||||||
<component name="Go" enabled="true" />
|
<component name="Go" enabled="true" />
|
||||||
<component name="NewModuleRootManager">
|
<component name="NewModuleRootManager">
|
||||||
<content url="file://$MODULE_DIR$" />
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/log/2026-03-02" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/log/2026-03-03" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/log/2026-03-04" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/log/2026-03-05" />
|
||||||
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
@@ -144,3 +144,26 @@ func (a *ProgramApi) DeleteProgram(c *gin.Context) {
|
|||||||
|
|
||||||
response.OkWithMsg("删除成功", c)
|
response.OkWithMsg("删除成功", c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateTTS 生成TTS语音
|
||||||
|
// @Tags 节目管理
|
||||||
|
// @Summary 生成TTS语音
|
||||||
|
// @Produce json
|
||||||
|
// @Param id query string true "节目ID"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Router /radio/program/generate-tts [get]
|
||||||
|
func (a *ProgramApi) GenerateTTS(c *gin.Context) {
|
||||||
|
id := c.Query("id")
|
||||||
|
if id == "" {
|
||||||
|
response.FailWithMsg("参数错误: id不能为空", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := programService.GenerateTTS(id); err != nil {
|
||||||
|
global.Logger.Error("生成TTS语音失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response.OkWithMsg("TTS生成成功", c)
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -40,7 +40,7 @@ func (a *VipApi) UpdateVipConfig(c *gin.Context) {
|
|||||||
// @Produce application/json
|
// @Produce application/json
|
||||||
// @Param id query string true "id"
|
// @Param id query string true "id"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Router /vip/config/detail [get]
|
// @Router /vip/config/detail [post]
|
||||||
func (a *VipApi) VipConfigDetail(c *gin.Context) {
|
func (a *VipApi) VipConfigDetail(c *gin.Context) {
|
||||||
vipConfig, err := vipService.VipConfigDetail()
|
vipConfig, err := vipService.VipConfigDetail()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -122,7 +122,8 @@ func (a *AuthApi) GetToken(c *gin.Context, user system.User) {
|
|||||||
// @Router /auth/miniLogin [get]
|
// @Router /auth/miniLogin [get]
|
||||||
func (a *AuthApi) MiniLogin(c *gin.Context) {
|
func (a *AuthApi) MiniLogin(c *gin.Context) {
|
||||||
jsCode := c.Query("code")
|
jsCode := c.Query("code")
|
||||||
user, err := userService.MiniLogin(jsCode)
|
ip := c.ClientIP()
|
||||||
|
user, err := userService.MiniLogin(jsCode, ip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Logger.Error("登录失败!", zap.Error(err))
|
global.Logger.Error("登录失败!", zap.Error(err))
|
||||||
response.FailWithMsg("登录失败", c)
|
response.FailWithMsg("登录失败", c)
|
||||||
|
|||||||
@@ -15,4 +15,5 @@ type Config struct {
|
|||||||
|
|
||||||
MiniProgram MiniProgram `mapstructure:"mini-program" json:"mini-program" yaml:"mini-program"` //小程序
|
MiniProgram MiniProgram `mapstructure:"mini-program" json:"mini-program" yaml:"mini-program"` //小程序
|
||||||
WechatPay WechatPay `mapstructure:"wechat-pay" json:"wechat-pay" yaml:"wechat-pay"` //微信支付
|
WechatPay WechatPay `mapstructure:"wechat-pay" json:"wechat-pay" yaml:"wechat-pay"` //微信支付
|
||||||
|
TTS TTS `mapstructure:"tencent-tts" json:"tencent-tts" yaml:"tencent-tts"` //腾讯文字转语音
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type TTS struct {
|
||||||
|
AppId string `mapstructure:"app-id" json:"app-id" yaml:"app-id"`
|
||||||
|
SecretId string `mapstructure:"secret-id" json:"secret-id" yaml:"secret-id"`
|
||||||
|
SecretKey string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"`
|
||||||
|
}
|
||||||
+213
-2
@@ -676,6 +676,36 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/favorite/removeAll": {
|
||||||
|
"get": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"用户互动"
|
||||||
|
],
|
||||||
|
"summary": "清空所有收藏",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "节目ID",
|
||||||
|
"name": "data",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/request.RemoveFavorite"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/history/add": {
|
"/history/add": {
|
||||||
"post": {
|
"post": {
|
||||||
"produces": [
|
"produces": [
|
||||||
@@ -706,6 +736,53 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/history/delete": {
|
||||||
|
"post": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"用户互动"
|
||||||
|
],
|
||||||
|
"summary": "删除收听历史",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "节目ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/history/deleteAllHistory": {
|
||||||
|
"get": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"用户互动"
|
||||||
|
],
|
||||||
|
"summary": "删除所有收听历史",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/history/list": {
|
"/history/list": {
|
||||||
"post": {
|
"post": {
|
||||||
"produces": [
|
"produces": [
|
||||||
@@ -2557,6 +2634,86 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/vip/config/detail": {
|
||||||
|
"get": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"VIP管理"
|
||||||
|
],
|
||||||
|
"summary": "获取VIP配置详情",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "id",
|
||||||
|
"name": "id",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/vip/config/update": {
|
||||||
|
"post": {
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"VIP管理"
|
||||||
|
],
|
||||||
|
"summary": "更新VIP配置",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "VIP配置信息",
|
||||||
|
"name": "data",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/request.UpdateVipConfig"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/vip/vip": {
|
||||||
|
"post": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"VIP管理"
|
||||||
|
],
|
||||||
|
"summary": "开通vip",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
@@ -3242,6 +3399,27 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"request.UpdateVipConfig": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"price"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"discountedPrice": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"price": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"remark": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"response.CaptchaRes": {
|
"response.CaptchaRes": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -3462,20 +3640,45 @@ const docTemplate = `{
|
|||||||
"avatarId": {
|
"avatarId": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"city": {
|
||||||
|
"description": "城市",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"clientId": {
|
"clientId": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"country": {
|
||||||
|
"description": "国家",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"createdAt": {
|
"createdAt": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"createdAtStr": {
|
"createdAtStr": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"gender": {
|
||||||
|
"description": "性别 0:未知 1:男 2:女",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"description": "主键ID",
|
"description": "主键ID",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"miniOpenId": {
|
"isVip": {
|
||||||
|
"description": "是否VIP 0:否 1:是",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"language": {
|
||||||
|
"description": "语言",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"lastLoginAt": {
|
||||||
|
"description": "最后登录时间",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"lastLoginIp": {
|
||||||
|
"description": "最后登录IP",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
@@ -3484,10 +3687,14 @@ const docTemplate = `{
|
|||||||
"nickName": {
|
"nickName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"openId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"phone": {
|
"phone": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"saOpenId": {
|
"province": {
|
||||||
|
"description": "省份",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"sessionKey": {
|
"sessionKey": {
|
||||||
@@ -3501,6 +3708,10 @@ const docTemplate = `{
|
|||||||
},
|
},
|
||||||
"updatedAt": {
|
"updatedAt": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"vipExpireAt": {
|
||||||
|
"description": "VIP过期时间",
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+213
-2
@@ -669,6 +669,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/favorite/removeAll": {
|
||||||
|
"get": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"用户互动"
|
||||||
|
],
|
||||||
|
"summary": "清空所有收藏",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "节目ID",
|
||||||
|
"name": "data",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/request.RemoveFavorite"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/history/add": {
|
"/history/add": {
|
||||||
"post": {
|
"post": {
|
||||||
"produces": [
|
"produces": [
|
||||||
@@ -699,6 +729,53 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/history/delete": {
|
||||||
|
"post": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"用户互动"
|
||||||
|
],
|
||||||
|
"summary": "删除收听历史",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "节目ID",
|
||||||
|
"name": "id",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/history/deleteAllHistory": {
|
||||||
|
"get": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"用户互动"
|
||||||
|
],
|
||||||
|
"summary": "删除所有收听历史",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/history/list": {
|
"/history/list": {
|
||||||
"post": {
|
"post": {
|
||||||
"produces": [
|
"produces": [
|
||||||
@@ -2550,6 +2627,86 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/vip/config/detail": {
|
||||||
|
"get": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"VIP管理"
|
||||||
|
],
|
||||||
|
"summary": "获取VIP配置详情",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "id",
|
||||||
|
"name": "id",
|
||||||
|
"in": "query",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/vip/config/update": {
|
||||||
|
"post": {
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"VIP管理"
|
||||||
|
],
|
||||||
|
"summary": "更新VIP配置",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "VIP配置信息",
|
||||||
|
"name": "data",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/request.UpdateVipConfig"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/vip/vip": {
|
||||||
|
"post": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"VIP管理"
|
||||||
|
],
|
||||||
|
"summary": "开通vip",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
@@ -3235,6 +3392,27 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"request.UpdateVipConfig": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"price"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"discountedPrice": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"price": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"remark": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"response.CaptchaRes": {
|
"response.CaptchaRes": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -3455,20 +3633,45 @@
|
|||||||
"avatarId": {
|
"avatarId": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"city": {
|
||||||
|
"description": "城市",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"clientId": {
|
"clientId": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"country": {
|
||||||
|
"description": "国家",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"createdAt": {
|
"createdAt": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"createdAtStr": {
|
"createdAtStr": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"gender": {
|
||||||
|
"description": "性别 0:未知 1:男 2:女",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"description": "主键ID",
|
"description": "主键ID",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"miniOpenId": {
|
"isVip": {
|
||||||
|
"description": "是否VIP 0:否 1:是",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"language": {
|
||||||
|
"description": "语言",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"lastLoginAt": {
|
||||||
|
"description": "最后登录时间",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"lastLoginIp": {
|
||||||
|
"description": "最后登录IP",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
@@ -3477,10 +3680,14 @@
|
|||||||
"nickName": {
|
"nickName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"openId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"phone": {
|
"phone": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"saOpenId": {
|
"province": {
|
||||||
|
"description": "省份",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"sessionKey": {
|
"sessionKey": {
|
||||||
@@ -3494,6 +3701,10 @@
|
|||||||
},
|
},
|
||||||
"updatedAt": {
|
"updatedAt": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"vipExpireAt": {
|
||||||
|
"description": "VIP过期时间",
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+141
-2
@@ -480,6 +480,20 @@ definitions:
|
|||||||
required:
|
required:
|
||||||
- id
|
- id
|
||||||
type: object
|
type: object
|
||||||
|
request.UpdateVipConfig:
|
||||||
|
properties:
|
||||||
|
discountedPrice:
|
||||||
|
type: integer
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
price:
|
||||||
|
type: integer
|
||||||
|
remark:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- price
|
||||||
|
type: object
|
||||||
response.CaptchaRes:
|
response.CaptchaRes:
|
||||||
properties:
|
properties:
|
||||||
captcha:
|
captcha:
|
||||||
@@ -626,24 +640,46 @@ definitions:
|
|||||||
$ref: '#/definitions/system.Oss'
|
$ref: '#/definitions/system.Oss'
|
||||||
avatarId:
|
avatarId:
|
||||||
type: string
|
type: string
|
||||||
|
city:
|
||||||
|
description: 城市
|
||||||
|
type: string
|
||||||
clientId:
|
clientId:
|
||||||
type: string
|
type: string
|
||||||
|
country:
|
||||||
|
description: 国家
|
||||||
|
type: string
|
||||||
createdAt:
|
createdAt:
|
||||||
type: string
|
type: string
|
||||||
createdAtStr:
|
createdAtStr:
|
||||||
type: string
|
type: string
|
||||||
|
gender:
|
||||||
|
description: 性别 0:未知 1:男 2:女
|
||||||
|
type: integer
|
||||||
id:
|
id:
|
||||||
description: 主键ID
|
description: 主键ID
|
||||||
type: string
|
type: string
|
||||||
miniOpenId:
|
isVip:
|
||||||
|
description: 是否VIP 0:否 1:是
|
||||||
|
type: integer
|
||||||
|
language:
|
||||||
|
description: 语言
|
||||||
|
type: string
|
||||||
|
lastLoginAt:
|
||||||
|
description: 最后登录时间
|
||||||
|
type: string
|
||||||
|
lastLoginIp:
|
||||||
|
description: 最后登录IP
|
||||||
type: string
|
type: string
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
nickName:
|
nickName:
|
||||||
type: string
|
type: string
|
||||||
|
openId:
|
||||||
|
type: string
|
||||||
phone:
|
phone:
|
||||||
type: string
|
type: string
|
||||||
saOpenId:
|
province:
|
||||||
|
description: 省份
|
||||||
type: string
|
type: string
|
||||||
sessionKey:
|
sessionKey:
|
||||||
type: string
|
type: string
|
||||||
@@ -653,6 +689,9 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
updatedAt:
|
updatedAt:
|
||||||
type: string
|
type: string
|
||||||
|
vipExpireAt:
|
||||||
|
description: VIP过期时间
|
||||||
|
type: string
|
||||||
type: object
|
type: object
|
||||||
info:
|
info:
|
||||||
contact: {}
|
contact: {}
|
||||||
@@ -1053,6 +1092,25 @@ paths:
|
|||||||
summary: 取消收藏
|
summary: 取消收藏
|
||||||
tags:
|
tags:
|
||||||
- 用户互动
|
- 用户互动
|
||||||
|
/favorite/removeAll:
|
||||||
|
get:
|
||||||
|
parameters:
|
||||||
|
- description: 节目ID
|
||||||
|
in: body
|
||||||
|
name: data
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/request.RemoveFavorite'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
summary: 清空所有收藏
|
||||||
|
tags:
|
||||||
|
- 用户互动
|
||||||
/history/add:
|
/history/add:
|
||||||
post:
|
post:
|
||||||
parameters:
|
parameters:
|
||||||
@@ -1072,6 +1130,36 @@ paths:
|
|||||||
summary: 添加收听历史
|
summary: 添加收听历史
|
||||||
tags:
|
tags:
|
||||||
- 用户互动
|
- 用户互动
|
||||||
|
/history/delete:
|
||||||
|
post:
|
||||||
|
parameters:
|
||||||
|
- description: 节目ID
|
||||||
|
in: query
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
summary: 删除收听历史
|
||||||
|
tags:
|
||||||
|
- 用户互动
|
||||||
|
/history/deleteAllHistory:
|
||||||
|
get:
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
summary: 删除所有收听历史
|
||||||
|
tags:
|
||||||
|
- 用户互动
|
||||||
/history/list:
|
/history/list:
|
||||||
post:
|
post:
|
||||||
parameters:
|
parameters:
|
||||||
@@ -2178,6 +2266,57 @@ paths:
|
|||||||
summary: 更新用户
|
summary: 更新用户
|
||||||
tags:
|
tags:
|
||||||
- 用户管理
|
- 用户管理
|
||||||
|
/vip/config/detail:
|
||||||
|
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: 获取VIP配置详情
|
||||||
|
tags:
|
||||||
|
- VIP管理
|
||||||
|
/vip/config/update:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- description: VIP配置信息
|
||||||
|
in: body
|
||||||
|
name: data
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/request.UpdateVipConfig'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
summary: 更新VIP配置
|
||||||
|
tags:
|
||||||
|
- VIP管理
|
||||||
|
/vip/vip:
|
||||||
|
post:
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
summary: 开通vip
|
||||||
|
tags:
|
||||||
|
- VIP管理
|
||||||
securityDefinitions:
|
securityDefinitions:
|
||||||
BearerAuth:
|
BearerAuth:
|
||||||
type: basic
|
type: basic
|
||||||
|
|||||||
@@ -97,6 +97,8 @@ require (
|
|||||||
github.com/spf13/cast v1.7.1 // indirect
|
github.com/spf13/cast v1.7.1 // indirect
|
||||||
github.com/spf13/pflag v1.0.6 // indirect
|
github.com/spf13/pflag v1.0.6 // indirect
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.51 // indirect
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tts v1.3.43 // indirect
|
||||||
github.com/tinylib/msgp v1.3.0 // indirect
|
github.com/tinylib/msgp v1.3.0 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.3.0 // indirect
|
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||||
|
|||||||
@@ -205,7 +205,12 @@ github.com/swaggo/gin-swagger v1.6.1/go.mod h1:LQ+hJStHakCWRiK/YNYtJOu4mR2FP+pxL
|
|||||||
github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
|
github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
|
||||||
github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
|
github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.43/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.51 h1:yuvTAokQAdxbCr06NGOdPJpgO3z46IDinINBM0r9R9I=
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.51/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0=
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tts v1.3.43 h1:BE8/iU1JruJoxFyYqCoeTmD42TlDmShf6BSIOeBo8+c=
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tts v1.3.43/go.mod h1:LospHTzrMXwZxzdu8rJi8ODtjGn64tVcwVRg6uMsGjc=
|
||||||
github.com/tencentyun/cos-go-sdk-v5 v0.7.70 h1:gkBkSfrDvUg4ZIjwYAfjbNCCclen9LCRNHhBNz+yjEQ=
|
github.com/tencentyun/cos-go-sdk-v5 v0.7.70 h1:gkBkSfrDvUg4ZIjwYAfjbNCCclen9LCRNHhBNz+yjEQ=
|
||||||
github.com/tencentyun/cos-go-sdk-v5 v0.7.70/go.mod h1:STbTNaNKq03u+gscPEGOahKzLcGSYOj6Dzc5zNay7Pg=
|
github.com/tencentyun/cos-go-sdk-v5 v0.7.70/go.mod h1:STbTNaNKq03u+gscPEGOahKzLcGSYOj6Dzc5zNay7Pg=
|
||||||
github.com/tencentyun/qcloud-cos-sts-sdk v0.0.0-20250515025012-e0eec8a5d123/go.mod h1:b18KQa4IxHbxeseW1GcZox53d7J0z39VNONTxvvlkXw=
|
github.com/tencentyun/qcloud-cos-sts-sdk v0.0.0-20250515025012-e0eec8a5d123/go.mod h1:b18KQa4IxHbxeseW1GcZox53d7J0z39VNONTxvvlkXw=
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
[sundynix-radio-server]2026-02-28 15:39:45 [31merror[0m /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 [31merror[0m /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 [31merror[0m /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/internal/gorm_logger_writer.go:29 [35m[/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=<nil>) %!s(*system.Oss=<nil>) %!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]
|
|
||||||
[0m[31m[error] [0mfailed to parse value %!v(MISSING), got error %!v(MISSING)
|
|
||||||
[sundynix-radio-server]2026-02-28 15:39:51 [31merror[0m /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/internal/gorm_logger_writer.go:29 [35m[/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=<nil>) %!s(*system.Oss=<nil>) %!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]
|
|
||||||
[0m[31m[error] [0mfailed to parse value %!v(MISSING), got error %!v(MISSING)
|
|
||||||
[sundynix-radio-server]2026-02-28 15:39:51 [31merror[0m /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 [31merror[0m /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 [31merror[0m /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 16:03:16 [31merror[0m /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 16:03:16 [31merror[0m /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"}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
[sundynix-radio-server]2026-02-28 15:39:45 [34minfo[0m /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 [34minfo[0m /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 [34minfo[0m /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/gorm.go:52 Migrate table success
|
|
||||||
[sundynix-radio-server]2026-02-28 16:03:16 [34minfo[0m /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/gorm_mysql.go:40 Mysql connect success
|
|
||||||
[sundynix-radio-server]2026-02-28 16:03:30 [34minfo[0m /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/gorm.go:52 Migrate table success
|
|
||||||
[sundynix-radio-server]2026-02-28 16:03:45 [34minfo[0m /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/gorm_mysql.go:40 Mysql connect success
|
|
||||||
[sundynix-radio-server]2026-02-28 16:03:45 [34minfo[0m /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-28 16:03:59 [34minfo[0m /Users/blizzard/sourceCode/GolandProjects/src/morning-radio/morning-radio-backend/initialize/gorm.go:52 Migrate table success
|
|
||||||
@@ -5,7 +5,7 @@ import common "sundynix-go/model/commom/request"
|
|||||||
// GetProgramList 获取节目列表请求
|
// GetProgramList 获取节目列表请求
|
||||||
type GetProgramList struct {
|
type GetProgramList struct {
|
||||||
common.PageInfo
|
common.PageInfo
|
||||||
ChannelId string `json:"channelId" binding:"required" form:"channelId"` // 频道ID
|
ChannelId string `json:"channelId" form:"channelId"` // 频道ID
|
||||||
Title string `json:"title" form:"title"` // 节目标题
|
Title string `json:"title" form:"title"` // 节目标题
|
||||||
Status int `json:"status" form:"status"` // 状态
|
Status int `json:"status" form:"status"` // 状态
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ type User struct {
|
|||||||
Language string `gorm:"size:20" json:"language"` // 语言
|
Language string `gorm:"size:20" json:"language"` // 语言
|
||||||
IsVip int `gorm:"default:0" json:"isVip"` // 是否VIP 0:否 1:是
|
IsVip int `gorm:"default:0" json:"isVip"` // 是否VIP 0:否 1:是
|
||||||
VipExpireAt *time.Time `gorm:"column:vip_expire_at" json:"vipExpireAt"` // VIP过期时间
|
VipExpireAt *time.Time `gorm:"column:vip_expire_at" json:"vipExpireAt"` // VIP过期时间
|
||||||
|
LastLoginIp string `gorm:"size:20;column:last_login_ip" json:"lastLoginIp"` // 最后登录IP
|
||||||
|
LastLoginAt *time.Time `gorm:"column:last_login_at" json:"lastLoginAt"` // 最后登录时间
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) GetAccount() string {
|
func (u *User) GetAccount() string {
|
||||||
|
|||||||
@@ -14,5 +14,6 @@ func (r *ProgramRouter) InitProgramRouter(Router *gin.RouterGroup) {
|
|||||||
programRouter.POST("save", programApi.SaveProgram)
|
programRouter.POST("save", programApi.SaveProgram)
|
||||||
programRouter.POST("update", programApi.UpdateProgram)
|
programRouter.POST("update", programApi.UpdateProgram)
|
||||||
programRouter.POST("delete", programApi.DeleteProgram)
|
programRouter.POST("delete", programApi.DeleteProgram)
|
||||||
|
programRouter.GET("generate-tts", programApi.GenerateTTS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ type CategoryService struct{}
|
|||||||
|
|
||||||
// GetCategoryList 获取分类列表
|
// GetCategoryList 获取分类列表
|
||||||
func (s *CategoryService) GetCategoryList(info radioReq.GetCategoryList) ([]radio.RadioCategory, int64, error) {
|
func (s *CategoryService) GetCategoryList(info radioReq.GetCategoryList) ([]radio.RadioCategory, int64, error) {
|
||||||
db := global.DB.Model(&radio.RadioCategory{}).Preload("Icon").Preload("Cover")
|
db := global.DB.Model(&radio.RadioCategory{})
|
||||||
var list []radio.RadioCategory
|
var list []radio.RadioCategory
|
||||||
var total int64
|
var total int64
|
||||||
|
|
||||||
@@ -43,9 +43,6 @@ func (s *CategoryService) GetCategoryTree() ([]radio.RadioCategory, error) {
|
|||||||
// Preload("Icon") 和 Preload("Cover") 用于加载 OSS 信息
|
// Preload("Icon") 和 Preload("Cover") 用于加载 OSS 信息
|
||||||
err := global.DB.Model(&radio.RadioCategory{}).
|
err := global.DB.Model(&radio.RadioCategory{}).
|
||||||
Preload("Channels", "status = ?", 1). // 只加载启用的频道
|
Preload("Channels", "status = ?", 1). // 只加载启用的频道
|
||||||
Preload("Channels.Cover"). // 级联加载频道的封面
|
|
||||||
Preload("Icon").
|
|
||||||
Preload("Cover").
|
|
||||||
Order("sort desc").
|
Order("sort desc").
|
||||||
Find(&res).Error
|
Find(&res).Error
|
||||||
return res, err
|
return res, err
|
||||||
@@ -53,14 +50,14 @@ func (s *CategoryService) GetCategoryTree() ([]radio.RadioCategory, error) {
|
|||||||
|
|
||||||
func (s *CategoryService) GetAllCategory() ([]radio.RadioCategory, error) {
|
func (s *CategoryService) GetAllCategory() ([]radio.RadioCategory, error) {
|
||||||
var res []radio.RadioCategory
|
var res []radio.RadioCategory
|
||||||
err := global.DB.Find(&res).Preload("Icon").Preload("Cover").Error
|
err := global.DB.Find(&res).Error
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCategoryById 获取分类详情
|
// GetCategoryById 获取分类详情
|
||||||
func (s *CategoryService) GetCategoryById(id string) (*radio.RadioCategory, error) {
|
func (s *CategoryService) GetCategoryById(id string) (*radio.RadioCategory, error) {
|
||||||
var category radio.RadioCategory
|
var category radio.RadioCategory
|
||||||
err := global.DB.Where("id = ?", id).Preload("Icon").Preload("Cover").First(&category).Error
|
err := global.DB.Where("id = ?", id).First(&category).Error
|
||||||
return &category, err
|
return &category, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ func (s *ChannelService) UpdateChannel(req radioReq.UpdateChannel) error {
|
|||||||
"description": req.Description,
|
"description": req.Description,
|
||||||
"cover": req.Cover,
|
"cover": req.Cover,
|
||||||
"tags": req.Tags,
|
"tags": req.Tags,
|
||||||
|
"is_free": req.IsFree,
|
||||||
"is_vip_only": req.IsVipOnly,
|
"is_vip_only": req.IsVipOnly,
|
||||||
"monthly_price": req.MonthlyPrice,
|
"monthly_price": req.MonthlyPrice,
|
||||||
"quarterly_price": req.QuarterlyPrice,
|
"quarterly_price": req.QuarterlyPrice,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ type ServiceGroup struct {
|
|||||||
PayService
|
PayService
|
||||||
OrderService
|
OrderService
|
||||||
VipService
|
VipService
|
||||||
|
TTSService
|
||||||
}
|
}
|
||||||
|
|
||||||
var GroupApp = new(ServiceGroup)
|
var GroupApp = new(ServiceGroup)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package radio
|
package radio
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"sundynix-go/global"
|
"sundynix-go/global"
|
||||||
"sundynix-go/model/radio"
|
"sundynix-go/model/radio"
|
||||||
radioReq "sundynix-go/model/radio/request"
|
radioReq "sundynix-go/model/radio/request"
|
||||||
@@ -111,3 +112,32 @@ func (s *ProgramService) IncrementPlayCount(id string) error {
|
|||||||
return global.DB.Model(&radio.RadioProgram{}).Where("id = ?", id).
|
return global.DB.Model(&radio.RadioProgram{}).Where("id = ?", id).
|
||||||
UpdateColumn("play_count", gorm.Expr("play_count + ?", 1)).Error
|
UpdateColumn("play_count", gorm.Expr("play_count + ?", 1)).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateTTS 生成TTS语音并更新节目 (异步)
|
||||||
|
func (s *ProgramService) GenerateTTS(programId string) error {
|
||||||
|
// 1. 获取节目内容
|
||||||
|
var program radio.RadioProgram
|
||||||
|
if err := global.DB.Where("id = ?", programId).First(&program).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if program.Content == "" {
|
||||||
|
return fmt.Errorf("节目内容为空")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 调用TTS提交任务 (异步,后台处理)
|
||||||
|
ttsReq := TTSTextToSpeechRequest{
|
||||||
|
Text: program.Content,
|
||||||
|
VoiceType: 101021, // 亲和女声
|
||||||
|
Speed: 0, // 正常语速
|
||||||
|
Volume: 0, // 正常音量
|
||||||
|
ProgramId: programId,
|
||||||
|
}
|
||||||
|
_, err := TTSServiceApp.SubmitTTSTask(ttsReq)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 任务已提交,异步处理中
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,283 @@
|
|||||||
|
package radio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"sundynix-go/global"
|
||||||
|
"sundynix-go/model/radio"
|
||||||
|
"sundynix-go/model/system"
|
||||||
|
"sundynix-go/utils/upload"
|
||||||
|
|
||||||
|
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||||
|
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
|
||||||
|
tts "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tts/v20190823"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TTSService TTS服务
|
||||||
|
type TTSService struct{}
|
||||||
|
|
||||||
|
// TTSTextToSpeechRequest TTS请求参数
|
||||||
|
type TTSTextToSpeechRequest struct {
|
||||||
|
Text string // 要转换的文本
|
||||||
|
VoiceType int // 声音类型
|
||||||
|
Speed int // 语速 -2到6
|
||||||
|
Volume int // 音量 -10到10
|
||||||
|
ProgramId string // 节目ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// TTSResult TTS任务结果
|
||||||
|
type TTSResult struct {
|
||||||
|
TaskId string // 任务ID
|
||||||
|
ProgramId string // 节目ID
|
||||||
|
Status int // 0:处理中 1:成功 2:失败
|
||||||
|
StatusMsg string // 状态描述
|
||||||
|
AudioId string // OSS文件ID
|
||||||
|
AudioUrl string // OSS文件URL
|
||||||
|
CreatedAt time.Time // 创建时间
|
||||||
|
}
|
||||||
|
|
||||||
|
var TTSServiceApp = new(TTSService)
|
||||||
|
|
||||||
|
// newTTSClient 创建腾讯云TTS客户端
|
||||||
|
func (t *TTSService) newTTSClient() (*tts.Client, error) {
|
||||||
|
credential := common.NewCredential(
|
||||||
|
global.Config.TTS.SecretId,
|
||||||
|
global.Config.TTS.SecretKey,
|
||||||
|
)
|
||||||
|
cpf := profile.NewClientProfile()
|
||||||
|
cpf.HttpProfile.Endpoint = "tts.tencentcloudapi.com"
|
||||||
|
return tts.NewClient(credential, "ap-guangzhou", cpf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubmitTTSTask 提交长文本TTS任务 (异步)
|
||||||
|
func (t *TTSService) SubmitTTSTask(req TTSTextToSpeechRequest) (string, error) {
|
||||||
|
if req.Text == "" {
|
||||||
|
return "", fmt.Errorf("文本内容不能为空")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交长文本TTS任务
|
||||||
|
taskId, err := t.doSubmitTTSTask(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("提交TTS任务失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步处理结果
|
||||||
|
go t.asyncProcessResult(req.ProgramId, taskId)
|
||||||
|
|
||||||
|
return taskId, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// asyncProcessResult 异步处理TTS结果
|
||||||
|
func (t *TTSService) asyncProcessResult(programId, taskId string) {
|
||||||
|
// 轮询查询任务结果,获取音频下载URL
|
||||||
|
resultUrl, err := t.waitForResult(taskId)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error(fmt.Sprintf("TTS任务失败, TaskId: %s, Error: %v", taskId, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从ResultUrl下载音频数据
|
||||||
|
audioData, err := t.downloadAudio(resultUrl)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error(fmt.Sprintf("下载音频失败, TaskId: %s, Error: %v", taskId, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传到OSS
|
||||||
|
audioId, err := t.uploadToOSS(audioData, programId)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error(fmt.Sprintf("上传OSS失败, TaskId: %s, Error: %v", taskId, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新节目的audio_id
|
||||||
|
if err := global.DB.Model(&radio.RadioProgram{}).Where("id = ?", programId).
|
||||||
|
Update("audio_id", audioId).Error; err != nil {
|
||||||
|
global.Logger.Error(fmt.Sprintf("更新节目音频ID失败, TaskId: %s, Error: %v", taskId, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
global.Logger.Info(fmt.Sprintf("TTS任务完成, TaskId: %s, ProgramId: %s, AudioId: %s", taskId, programId, audioId))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubmitTTSTaskOnly 仅提交任务,返回TaskId (供外部调用)
|
||||||
|
func (t *TTSService) SubmitTTSTaskOnly(req TTSTextToSpeechRequest) (string, error) {
|
||||||
|
if req.Text == "" {
|
||||||
|
return "", fmt.Errorf("文本内容不能为空")
|
||||||
|
}
|
||||||
|
return t.doSubmitTTSTask(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryTTSTask 查询任务状态
|
||||||
|
func (t *TTSService) QueryTTSTask(taskId string) (int, string, error) {
|
||||||
|
_, status, statusMsg, err := t.queryTaskResult(taskId)
|
||||||
|
return status, statusMsg, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAudioByTaskId 根据TaskId获取音频下载URL
|
||||||
|
func (t *TTSService) GetAudioByTaskId(taskId string) ([]byte, error) {
|
||||||
|
resultUrl, status, _, err := t.queryTaskResult(taskId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if status != 1 {
|
||||||
|
return nil, fmt.Errorf("任务未完成或失败")
|
||||||
|
}
|
||||||
|
return t.downloadAudio(resultUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// doSubmitTTSTask 使用官方SDK提交长文本TTS任务
|
||||||
|
func (t *TTSService) doSubmitTTSTask(req TTSTextToSpeechRequest) (string, error) {
|
||||||
|
client, err := t.newTTSClient()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("创建TTS客户端失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
request := tts.NewCreateTtsTaskRequest()
|
||||||
|
request.Text = common.StringPtr(req.Text)
|
||||||
|
request.VoiceType = common.Int64Ptr(int64(req.VoiceType))
|
||||||
|
request.Speed = common.Float64Ptr(float64(req.Speed))
|
||||||
|
request.Volume = common.Float64Ptr(float64(req.Volume))
|
||||||
|
request.Codec = common.StringPtr("mp3")
|
||||||
|
request.ModelType = common.Int64Ptr(1)
|
||||||
|
|
||||||
|
response, err := client.CreateTtsTask(request)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("调用CreateTtsTask失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.Response == nil || response.Response.Data == nil || response.Response.Data.TaskId == nil {
|
||||||
|
return "", fmt.Errorf("未获取到TaskId")
|
||||||
|
}
|
||||||
|
|
||||||
|
return *response.Response.Data.TaskId, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// waitForResult 轮询查询任务结果,返回音频下载URL
|
||||||
|
func (t *TTSService) waitForResult(taskId string) (string, error) {
|
||||||
|
maxRetries := 30
|
||||||
|
interval := 5 * time.Second
|
||||||
|
|
||||||
|
for i := 0; i < maxRetries; i++ {
|
||||||
|
time.Sleep(interval)
|
||||||
|
|
||||||
|
resultUrl, status, statusMsg, err := t.queryTaskResult(taskId)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch status {
|
||||||
|
case 1: // 成功
|
||||||
|
return resultUrl, nil
|
||||||
|
case 2: // 失败
|
||||||
|
return "", fmt.Errorf("TTS合成失败: %s", statusMsg)
|
||||||
|
default:
|
||||||
|
global.Logger.Debug(fmt.Sprintf("TTS任务处理中, TaskId: %s, 重试次数: %d", taskId, i+1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("TTS任务超时, TaskId: %s", taskId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryTaskResult 使用官方SDK查询任务结果
|
||||||
|
// 返回: resultUrl, status, statusMsg, error
|
||||||
|
// status: 0-等待中 1-成功 2-失败 3-执行中
|
||||||
|
func (t *TTSService) queryTaskResult(taskId string) (string, int, string, error) {
|
||||||
|
client, err := t.newTTSClient()
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, "", fmt.Errorf("创建TTS客户端失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
request := tts.NewDescribeTtsTaskStatusRequest()
|
||||||
|
request.TaskId = common.StringPtr(taskId)
|
||||||
|
|
||||||
|
response, err := client.DescribeTtsTaskStatus(request)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, "", fmt.Errorf("调用DescribeTtsTaskStatus失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.Response == nil || response.Response.Data == nil {
|
||||||
|
return "", 0, "", fmt.Errorf("查询任务状态返回为空")
|
||||||
|
}
|
||||||
|
|
||||||
|
data := response.Response.Data
|
||||||
|
|
||||||
|
// 将SDK的StatusStr转换为数字状态
|
||||||
|
status := 0
|
||||||
|
statusMsg := ""
|
||||||
|
if data.StatusStr != nil {
|
||||||
|
statusMsg = *data.StatusStr
|
||||||
|
switch *data.StatusStr {
|
||||||
|
case "success":
|
||||||
|
status = 1
|
||||||
|
case "failed":
|
||||||
|
status = 2
|
||||||
|
case "waiting", "doing":
|
||||||
|
status = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resultUrl := ""
|
||||||
|
if data.ResultUrl != nil {
|
||||||
|
resultUrl = *data.ResultUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultUrl, status, statusMsg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// downloadAudio 从URL下载音频数据
|
||||||
|
func (t *TTSService) downloadAudio(audioUrl string) ([]byte, error) {
|
||||||
|
resp, err := http.Get(audioUrl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("下载音频失败: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("下载音频HTTP错误: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
audioData, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("读取音频数据失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return audioData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// uploadToOSS 上传音频到OSS并保存到数据库
|
||||||
|
func (t *TTSService) uploadToOSS(audioData []byte, programId string) (string, error) {
|
||||||
|
instance := upload.OssInstance()
|
||||||
|
minioClient, ok := instance.(*upload.Minio)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("获取MinIO客户端失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
key := fmt.Sprintf("audio/%s/%s.mp3", time.Now().Format("2006-01-02"), programId)
|
||||||
|
filename := fmt.Sprintf("program-%s.mp3", programId)
|
||||||
|
|
||||||
|
fileURL, err := minioClient.UploadBytes(audioData, key, "audio/mpeg")
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("上传文件到OSS失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hashStr := fmt.Sprintf("%x", md5.Sum(audioData))
|
||||||
|
|
||||||
|
oss := system.Oss{
|
||||||
|
Name: filename,
|
||||||
|
Url: fileURL,
|
||||||
|
Key: key,
|
||||||
|
Suffix: "mp3",
|
||||||
|
Tag: "mp3",
|
||||||
|
MD5: hashStr,
|
||||||
|
}
|
||||||
|
if err := global.DB.Create(&oss).Error; err != nil {
|
||||||
|
return "", fmt.Errorf("保存文件记录失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return oss.Id, nil
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@ type VipService struct{}
|
|||||||
func (s *VipService) UpdateVipConfig(req request.UpdateVipConfig) error {
|
func (s *VipService) UpdateVipConfig(req request.UpdateVipConfig) error {
|
||||||
updateData := map[string]interface{}{
|
updateData := map[string]interface{}{
|
||||||
"price": req.Price,
|
"price": req.Price,
|
||||||
"discountedPrice": req.DiscountedPrice,
|
"discounted_price": req.DiscountedPrice,
|
||||||
"remark": req.Remark,
|
"remark": req.Remark,
|
||||||
}
|
}
|
||||||
err := global.DB.Model(&radio.Vip{}).Where("id = ?", req.Id).Updates(updateData).Error
|
err := global.DB.Model(&radio.Vip{}).Where("id = ?", req.Id).Updates(updateData).Error
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ func (userService *UserService) ChangePassword(id string, pwd string) (err error
|
|||||||
return global.DB.Model(&system.User{}).Where("id = ?", id).Update("password", utils.BcryptHash(pwd)).Error
|
return global.DB.Model(&system.User{}).Where("id = ?", id).Update("password", utils.BcryptHash(pwd)).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (userService *UserService) MiniLogin(code string) (result *system.User, err error) {
|
func (userService *UserService) MiniLogin(code, ip string) (result *system.User, err error) {
|
||||||
//构建参数
|
//构建参数
|
||||||
params := url2.Values{}
|
params := url2.Values{}
|
||||||
params.Set("appid", global.Config.MiniProgram.AppId)
|
params.Set("appid", global.Config.MiniProgram.AppId)
|
||||||
@@ -153,6 +153,7 @@ func (userService *UserService) MiniLogin(code string) (result *system.User, err
|
|||||||
Name: uniqueid.GenerateRadioUsername(),
|
Name: uniqueid.GenerateRadioUsername(),
|
||||||
OpenId: wxResp.Openid,
|
OpenId: wxResp.Openid,
|
||||||
SessionKey: wxResp.SessionKey,
|
SessionKey: wxResp.SessionKey,
|
||||||
|
LastLoginIp: ip,
|
||||||
}
|
}
|
||||||
if err := tx.Create(&newUser).Error; err != nil {
|
if err := tx.Create(&newUser).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -170,7 +171,11 @@ func (userService *UserService) MiniLogin(code string) (result *system.User, err
|
|||||||
}
|
}
|
||||||
if err == nil && user.Id != "" {
|
if err == nil && user.Id != "" {
|
||||||
// UpdateColumn:只更新字段,不触发模型钩子,比Update更高效
|
// UpdateColumn:只更新字段,不触发模型钩子,比Update更高效
|
||||||
if err = global.DB.Model(&user).UpdateColumn("session_key", wxResp.SessionKey).Error; err != nil {
|
updateData := map[string]interface{}{
|
||||||
|
"session_key": wxResp.SessionKey,
|
||||||
|
"last_login_ip": ip,
|
||||||
|
}
|
||||||
|
if err = global.DB.Model(&user).Updates(updateData).Error; err != nil {
|
||||||
global.Logger.Error("更新session_key失败", zap.Error(err))
|
global.Logger.Error("更新session_key失败", zap.Error(err))
|
||||||
return nil, fmt.Errorf("更新session_key失败: %w", err)
|
return nil, fmt.Errorf("更新session_key失败: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -104,3 +105,19 @@ func (m *Minio) DeleteFile(key string) error {
|
|||||||
err := m.Client.RemoveObject(ctx, m.bucket, key, minio.RemoveObjectOptions{})
|
err := m.Client.RemoveObject(ctx, m.bucket, key, minio.RemoveObjectOptions{})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UploadBytes 上传字节数据
|
||||||
|
func (m *Minio) UploadBytes(data []byte, key, contentType string) (string, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*10)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
buffer := bytes.NewReader(data)
|
||||||
|
info, err := m.Client.PutObject(ctx, m.bucket, key, buffer, int64(len(data)), minio.PutObjectOptions{
|
||||||
|
ContentType: contentType,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("上传文件到minio失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s/%s", global.Config.Minio.BucketUrl, info.Key), nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user