feat: 弃用腾讯tts,改用火山引擎tts

This commit is contained in:
Blizzard
2026-03-23 16:24:27 +08:00
parent f4bfe2d609
commit df74da48bd
12 changed files with 251 additions and 9 deletions
+2
View File
@@ -11,6 +11,7 @@ type ApiGroup struct {
PayApi PayApi
VipApi VipApi
AnalyticsApi AnalyticsApi
UserApi
} }
var ApiGroupApp = new(ApiGroup) var ApiGroupApp = new(ApiGroup)
@@ -24,4 +25,5 @@ var (
payService = service.GroupApp.RadioServiceGroup.PayService payService = service.GroupApp.RadioServiceGroup.PayService
vipService = service.GroupApp.RadioServiceGroup.VipService vipService = service.GroupApp.RadioServiceGroup.VipService
analyticsService = service.GroupApp.RadioServiceGroup.AnalyticsService analyticsService = service.GroupApp.RadioServiceGroup.AnalyticsService
userService = service.GroupApp.RadioServiceGroup.UserService
) )
+48
View File
@@ -0,0 +1,48 @@
package radio
import (
"strconv"
"sundynix-go/global"
common "sundynix-go/model/commom/request"
"sundynix-go/model/commom/response"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type UserApi struct{}
// GetRadioUserList 获取电台用户列表
// @Tags 用户管理
// @Summary 获取电台用户列表(含订阅/收听统计)
// @Accept application/json
// @Produce application/json
// @Param current query int false "页码"
// @Param pageSize query int false "每页大小"
// @Param keyword query string false "搜索关键字"
// @Param isVip query int false "VIP筛选: 0全部 1VIP 2非VIP"
// @Success 200 {object} response.Response
// @Router /radio/user/list [get]
func (a *UserApi) GetRadioUserList(c *gin.Context) {
var info common.PageInfo
info.Keyword = c.Query("keyword")
current, _ := strconv.Atoi(c.DefaultQuery("current", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
info.Current = current
info.PageSize = pageSize
isVip, _ := strconv.Atoi(c.DefaultQuery("isVip", "0"))
list, total, err := userService.GetRadioUserList(info, isVip, info.Keyword)
if err != nil {
global.Logger.Error("获取用户列表失败!", zap.Error(err))
response.FailWithMsg(err.Error(), c)
return
}
response.OkWithData(response.PageResult{
List: list,
Total: total,
Page: info.Current,
PageSize: info.PageSize,
}, c)
}
+5 -6
View File
@@ -22,12 +22,11 @@ mini-program:
app-id: wx52dfc635739a9c19 app-id: wx52dfc635739a9c19
app-secret: 84c6ddab1f24d0222da57bedb681c81f app-secret: 84c6ddab1f24d0222da57bedb681c81f
# 腾讯文字转语音 # 统一音频TTS服务(火山引擎)
tencent-tts: tts:
app-id: 1312892187 app-id: "9604175735"
secret-id: AKIDKaeU7XjhSzIOGuKWUEk26wY1MUP6asyr resource-id: "seed-tts-2.0" # 火山引擎TTS服务资源ID (原cluster)
secret-key: lU0JOFrGSSGqDMLKBoIbnmX6TcXIqKbe access-key: "IMSrxDQgXWOwaJnuF-5G7uppRQutwBny" # 接口调用的Token
# 微信支付 # 微信支付
wechat-pay: wechat-pay:
+1
View File
@@ -56,6 +56,7 @@ func Routers() {
radioRouter.InitPayRouter(NeedAuthGroup, PublicGroup) //支付和回调 radioRouter.InitPayRouter(NeedAuthGroup, PublicGroup) //支付和回调
radioRouter.InitInteractionRouter(NeedAuthGroup) //用户互动相关 radioRouter.InitInteractionRouter(NeedAuthGroup) //用户互动相关
radioRouter.InitAnalyticsRouter(NeedAuthGroup) //数据分析相关 radioRouter.InitAnalyticsRouter(NeedAuthGroup) //数据分析相关
radioRouter.InitUserRouter(NeedAuthGroup) //用户管理相关
} }
address := fmt.Sprintf(":%d", global.Config.System.Addr) address := fmt.Sprintf(":%d", global.Config.System.Addr)
+24
View File
@@ -0,0 +1,24 @@
package response
import "time"
// RadioUserItem 电台用户列表项
type RadioUserItem struct {
Id string `json:"id"`
Name string `json:"name"`
NickName string `json:"nickName"`
Account string `json:"account"`
Phone string `json:"phone"`
AvatarUrl string `json:"avatarUrl"`
Gender int `json:"gender"` // 0:未知 1:男 2:女
IsVip int `json:"isVip"` // 0:否 1:是
VipExpireAt *time.Time `json:"vipExpireAt"` // VIP过期时间
LastLoginAt *time.Time `json:"lastLoginAt"` // 最后登录时间
LastLoginIp string `json:"lastLoginIp"` // 最后登录IP
CreatedAt time.Time `json:"createdAt"` // 注册时间
SubscribeCount int64 `json:"subscribeCount"` // 订阅频道数
ListenCount int64 `json:"listenCount"` // 收听次数
FavoriteCount int64 `json:"favoriteCount"` // 收藏数
TotalSpent int64 `json:"totalSpent"` // 累计消费(分)
OrderCount int64 `json:"orderCount"` // 订单数
}
+1
View File
@@ -13,6 +13,7 @@ type GetUserList struct {
common.PageInfo common.PageInfo
Account string `json:"account" form:"account"` Account string `json:"account" form:"account"`
Phone string `json:"phone" form:"phone"` Phone string `json:"phone" form:"phone"`
IsVip *int `json:"isVip" form:"isVip"`
} }
type ChangePwd struct { type ChangePwd struct {
+2 -2
View File
@@ -14,10 +14,10 @@ type User struct {
global.BaseModel global.BaseModel
TenantId string `gorm:"size:20;" json:"tenantId" form:"tenantId"` TenantId string `gorm:"size:20;" json:"tenantId" form:"tenantId"`
ClientId string `gorm:"size:20;" json:"clientId"` ClientId string `gorm:"size:20;" json:"clientId"`
Name string `gorm:"size:20" json:"name" form:"name"` Name string `gorm:"size:100" json:"name" form:"name"`
Account string `gorm:"size:11;" json:"account" form:"account"` Account string `gorm:"size:11;" json:"account" form:"account"`
Password string `gorm:"size:100;" json:"-" form:"password"` Password string `gorm:"size:100;" json:"-" form:"password"`
NickName string `gorm:"size:20;column:nick_name" json:"nickName" form:"nickName"` NickName string `gorm:"size:100;column:nick_name" json:"nickName" form:"nickName"`
Phone string `gorm:"size:20;column:phone" json:"phone" form:"phone"` Phone string `gorm:"size:20;column:phone" json:"phone" form:"phone"`
SessionKey string `gorm:"size:80;column:session_key" json:"sessionKey" form:"sessionKey"` SessionKey string `gorm:"size:80;column:session_key" json:"sessionKey" form:"sessionKey"`
UnionId string `gorm:"size:80;column:union_id" json:"unionId"` UnionId string `gorm:"size:80;column:union_id" json:"unionId"`
+2
View File
@@ -11,6 +11,7 @@ type RadioRouterGroup struct {
PayRouter PayRouter
VipRouter VipRouter
AnalyticsRouter AnalyticsRouter
UserRouter
} }
var GroupApp = new(RadioRouterGroup) var GroupApp = new(RadioRouterGroup)
@@ -24,4 +25,5 @@ var (
payApi = v1.ApiGroupApp.RadioApiGroup.PayApi payApi = v1.ApiGroupApp.RadioApiGroup.PayApi
vipApi = v1.ApiGroupApp.RadioApiGroup.VipApi vipApi = v1.ApiGroupApp.RadioApiGroup.VipApi
analyticsApi = v1.ApiGroupApp.RadioApiGroup.AnalyticsApi analyticsApi = v1.ApiGroupApp.RadioApiGroup.AnalyticsApi
userApi = v1.ApiGroupApp.RadioApiGroup.UserApi
) )
+14
View File
@@ -0,0 +1,14 @@
package radio
import (
"github.com/gin-gonic/gin"
)
type UserRouter struct{}
func (r *UserRouter) InitUserRouter(Router *gin.RouterGroup) {
userRouter := Router.Group("/radio/user")
{
userRouter.GET("list", userApi.GetRadioUserList)
}
}
+1
View File
@@ -11,6 +11,7 @@ type ServiceGroup struct {
VipService VipService
TTSService TTSService
AnalyticsService AnalyticsService
UserService
} }
var GroupApp = new(ServiceGroup) var GroupApp = new(ServiceGroup)
+143
View File
@@ -0,0 +1,143 @@
package radio
import (
"sundynix-go/global"
common "sundynix-go/model/commom/request"
"sundynix-go/model/radio/response"
"sundynix-go/model/system"
)
type UserService struct{}
// GetRadioUserList 获取电台用户列表(带订阅/收听统计)
func (s *UserService) GetRadioUserList(info common.PageInfo, isVip int, keyword string) ([]response.RadioUserItem, int64, error) {
var total int64
var users []system.User
db := global.DB.Model(&system.User{})
// 关键字搜索
if keyword != "" {
db = db.Where("nick_name LIKE ? OR phone LIKE ? OR account LIKE ?",
"%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%")
}
// VIP 筛选: 1=VIP, 2=非VIP, 0或其他=全部
if isVip == 1 {
db = db.Where("is_vip = 1")
} else if isVip == 2 {
db = db.Where("is_vip = 0")
}
// 统计总数
if err := db.Count(&total).Error; err != nil {
return nil, 0, err
}
// 分页查询用户列表
if err := db.Preload("Avatar").
Scopes(info.Paginate()).
Order("created_at DESC").
Find(&users).Error; err != nil {
return nil, 0, err
}
// 批量获取用户ID
userIds := make([]string, len(users))
for i, u := range users {
userIds[i] = u.Id
}
if len(userIds) == 0 {
return []response.RadioUserItem{}, total, nil
}
// 批量查询订阅数
type UserCount struct {
UserId string
Count int64
}
var subCounts []UserCount
global.DB.Table("sundynix_radio_subscription").
Select("user_id, COUNT(*) as count").
Where("user_id IN ? AND status = 1 AND deleted_at IS NULL", userIds).
Group("user_id").
Scan(&subCounts)
subMap := make(map[string]int64)
for _, sc := range subCounts {
subMap[sc.UserId] = sc.Count
}
// 批量查询收听次数
var listenCounts []UserCount
global.DB.Table("sundynix_radio_listen_log").
Select("user_id, COUNT(*) as count").
Where("user_id IN ? AND deleted_at IS NULL", userIds).
Group("user_id").
Scan(&listenCounts)
listenMap := make(map[string]int64)
for _, lc := range listenCounts {
listenMap[lc.UserId] = lc.Count
}
// 批量查询收藏数
var favCounts []UserCount
global.DB.Table("sundynix_radio_favorite").
Select("user_id, COUNT(*) as count").
Where("user_id IN ? AND deleted_at IS NULL", userIds).
Group("user_id").
Scan(&favCounts)
favMap := make(map[string]int64)
for _, fc := range favCounts {
favMap[fc.UserId] = fc.Count
}
// 批量查询订单总额(分)
type UserAmount struct {
UserId string
TotalAmount int64
OrderCount int64
}
var orderStats []UserAmount
global.DB.Table("sundynix_order").
Select("user_id, SUM(amount) as total_amount, COUNT(*) as order_count").
Where("user_id IN ? AND status = 1 AND deleted_at IS NULL", userIds).
Group("user_id").
Scan(&orderStats)
amountMap := make(map[string]int64)
orderCountMap := make(map[string]int64)
for _, os := range orderStats {
amountMap[os.UserId] = os.TotalAmount
orderCountMap[os.UserId] = os.OrderCount
}
// 组装结果
result := make([]response.RadioUserItem, len(users))
for i, u := range users {
avatarUrl := ""
if u.Avatar != nil {
avatarUrl = u.Avatar.Url
}
result[i] = response.RadioUserItem{
Id: u.Id,
Name: u.Name,
NickName: u.NickName,
Account: u.Account,
Phone: u.Phone,
AvatarUrl: avatarUrl,
Gender: u.Gender,
IsVip: u.IsVip,
VipExpireAt: u.VipExpireAt,
LastLoginAt: u.LastLoginAt,
LastLoginIp: u.LastLoginIp,
CreatedAt: u.CreatedAt,
SubscribeCount: subMap[u.Id],
ListenCount: listenMap[u.Id],
FavoriteCount: favMap[u.Id],
TotalSpent: amountMap[u.Id],
OrderCount: orderCountMap[u.Id],
}
}
return result, total, nil
}
+8 -1
View File
@@ -20,6 +20,7 @@ import (
location "sundynix-go/utils/location" location "sundynix-go/utils/location"
"sundynix-go/utils/uniqueid" "sundynix-go/utils/uniqueid"
"sundynix-go/utils/wechat" "sundynix-go/utils/wechat"
"time"
"go.uber.org/zap" "go.uber.org/zap"
"gorm.io/gorm" "gorm.io/gorm"
@@ -72,6 +73,9 @@ func (userService *UserService) GetUserList(info systemReq.GetUserList) (list in
db := global.DB.Model(&system.User{}) db := global.DB.Model(&system.User{})
var userList []system.User var userList []system.User
if info.IsVip != nil {
db = db.Where("is_vip = ?", *info.IsVip)
}
if info.Account != "" { if info.Account != "" {
db = db.Where("account LIKE ?", "%"+info.Account+"%") db = db.Where("account LIKE ?", "%"+info.Account+"%")
} }
@@ -82,7 +86,7 @@ func (userService *UserService) GetUserList(info systemReq.GetUserList) (list in
if err != nil { if err != nil {
return return
} }
err = db.Limit(limit).Offset(offset).Find(&userList).Error err = db.Limit(limit).Offset(offset).Order("created_at desc").Find(&userList).Error
return userList, total, err return userList, total, err
} }
@@ -144,6 +148,7 @@ func (userService *UserService) MiniLogin(code, ip string) (result *system.User,
// 7. 根据openid查询用户 存在--> 更新session_key 返回数据 // 7. 根据openid查询用户 存在--> 更新session_key 返回数据
var user system.User var user system.User
now := time.Now()
err = global.DB.Where("open_id = ?", wxResp.Openid).Preload("Avatar").First(&user).Error err = global.DB.Where("open_id = ?", wxResp.Openid).Preload("Avatar").First(&user).Error
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
// 8. 使用 Transaction 闭包管理事务 // 8. 使用 Transaction 闭包管理事务
@@ -154,6 +159,7 @@ func (userService *UserService) MiniLogin(code, ip string) (result *system.User,
OpenId: wxResp.Openid, OpenId: wxResp.Openid,
SessionKey: wxResp.SessionKey, SessionKey: wxResp.SessionKey,
LastLoginIp: ip, LastLoginIp: ip,
LastLoginAt: &now,
} }
if err := tx.Create(&newUser).Error; err != nil { if err := tx.Create(&newUser).Error; err != nil {
return err return err
@@ -174,6 +180,7 @@ func (userService *UserService) MiniLogin(code, ip string) (result *system.User,
updateData := map[string]interface{}{ updateData := map[string]interface{}{
"session_key": wxResp.SessionKey, "session_key": wxResp.SessionKey,
"last_login_ip": ip, "last_login_ip": ip,
"last_login_at": &now,
} }
if err = global.DB.Model(&user).Updates(updateData).Error; err != nil { 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))