feat: 弃用腾讯tts,改用火山引擎tts
This commit is contained in:
@@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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
@@ -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:
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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"` // 订单数
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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"`
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ type ServiceGroup struct {
|
|||||||
VipService
|
VipService
|
||||||
TTSService
|
TTSService
|
||||||
AnalyticsService
|
AnalyticsService
|
||||||
|
UserService
|
||||||
}
|
}
|
||||||
|
|
||||||
var GroupApp = new(ServiceGroup)
|
var GroupApp = new(ServiceGroup)
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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))
|
||||||
|
|||||||
Reference in New Issue
Block a user