fonchain-fiee/pkg/service/cast/media.go
2025-12-19 20:08:11 +08:00

651 lines
18 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package cast
import (
"context"
"encoding/json"
"errors"
"fmt"
"fonchain-fiee/api/accountFiee"
"fonchain-fiee/api/aryshare"
"fonchain-fiee/api/bundle"
"fonchain-fiee/api/cast"
"fonchain-fiee/cmd/config"
"fonchain-fiee/pkg/e"
modelCast "fonchain-fiee/pkg/model/cast"
"fonchain-fiee/pkg/model/login"
"fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/utils"
"net/http"
"net/url"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/xuri/excelize/v2"
"go.uber.org/zap"
)
func MediaUserList(ctx *gin.Context) {
var req *cast.MediaUserListReq
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
if err = req.Validate(); err != nil {
service.Error(ctx, err)
return
}
resp, err := service.CastProvider.MediaUserList(ctx, req)
if err != nil {
service.Error(ctx, err)
return
}
var statResp *bundle.ArtistUploadStatsResponse
if req.NeedStats {
for _, v := range resp.Data {
statResp, err = service.BundleProvider.GetArtistUploadStatsList(context.Background(), &bundle.TaskQueryRequest{
Keyword: v.ArtistPhone,
Page: 0,
PageSize: 0,
SortBy: "",
SortType: "",
LastTaskAssignee: "",
SubNums: nil,
})
if err != nil {
service.Error(ctx, errors.New("查询艺人数据错误"))
return
}
if statResp != nil && len(statResp.Items) > 0 {
info := statResp.Items[0]
// "pendingVideoCount": 10, // 待上传视频数 "uploadedVideoCount": 25, // 已上传视频数 "releasedVideoTotal": 35, // 已释放视频总数
//"pendingPostCount": 14, // 待上传图文数 "uploadedPostCount": 21, // 已上传图文数 "releasedPostTotal": 35, // 已释放图文总数
v.PendingVideoCount = info.PendingVideoCount
v.UploadedVideoCount = info.UploadedVideoCount
v.ReleasedVideoTotal = info.ReleasedVideoTotal
v.PendingPostCount = info.PendingPostCount
v.UploadedPostCount = info.UploadedPostCount
v.ReleasedPostTotal = info.ReleasedPostTotal
}
}
}
go func() {
for _, v := range resp.Data {
_ = SyncAsAuth(v.ArtistUuid)
}
}()
service.Success(ctx, resp)
return
}
func UnbindManager(ctx *gin.Context) {
var req *cast.UnbindManagerReq
var resp *cast.UnbindManagerResp
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
if err = req.Validate(); err != nil {
service.Error(ctx, err)
return
}
resp, err = service.CastProvider.UnbindManager(ctx, req)
if err != nil {
service.Error(ctx, err)
return
}
zap.L().Info("UnbindManager resp", zap.Any("resp", resp))
if resp.OldBindArtistUuid != "" {
userID, _ := strconv.ParseInt(resp.OldBindArtistUuid, 10, 64)
_, err = service.BundleProvider.AddBundleBalance(context.Background(), &bundle.AddBundleBalanceReq{
UserId: int32(userID),
AccountConsumptionNumber: -1,
})
if err != nil {
service.Error(ctx, err)
return
}
}
service.Success(ctx, nil)
return
}
func BindManager(ctx *gin.Context) {
var req *cast.BindManagerReq
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
if err = req.Validate(); err != nil {
service.Error(ctx, err)
return
}
_, err = service.CastProvider.BindManager(ctx, req)
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, nil)
return
}
func UpdateMediaAccount(ctx *gin.Context) {
var req *cast.UpdateMediaAccountReq
var infoResp *accountFiee.UserInfoResponse
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
if err = req.Validate(); err != nil {
service.Error(ctx, err)
return
}
// 查询艺人的信息
userResp, err := service.CastProvider.MediaUserList(context.Background(), &cast.MediaUserListReq{
ArtistUuid: req.ArtistUuid,
Page: 1,
PageSize: 10,
})
if err != nil {
service.Error(ctx, err)
return
}
if userResp != nil && len(userResp.Data) > 0 {
for _, v := range userResp.Data {
if v.PlatformID == uint32(req.PlatformID) {
service.Error(ctx, errors.New("账号已存在"))
return
}
}
}
// 字符串转整型
artistID, _ := strconv.ParseUint(req.ArtistUuid, 10, 64)
infoResp, err = GetArtistAccountInfo(artistID)
if err != nil {
service.Error(ctx, err)
return
}
if infoResp.SubNum == "" {
service.Error(ctx, errors.New("用户不存在"))
return
}
if err = CheckAsProfile(infoResp); err != nil {
service.Error(ctx, err)
return
}
req.ArtistName = infoResp.Name
req.ArtistPhone = infoResp.TelNum
req.ArtistPhoneAreaCode = infoResp.TelAreaCode
req.ArtistSubNum = infoResp.SubNum
if _, ok := cast.PlatformIDENUM_name[int32(req.PlatformID)]; !ok {
service.Error(ctx, errors.New(e.GetMsg(e.InvalidParams)))
return
}
userID, _ := strconv.ParseInt(req.ArtistUuid, 10, 64)
if req.MediaAccountUuid == "" {
if _, err = CheckUserBundleBalance(int32(userID), modelCast.BalanceTypeAccountValue); err != nil {
service.Error(ctx, err)
return
}
_, err = service.BundleProvider.AddBundleBalance(context.Background(), &bundle.AddBundleBalanceReq{
UserId: int32(userID),
AccountConsumptionNumber: 1,
})
if err != nil {
service.Error(ctx, err)
return
}
}
resp, err := service.CastProvider.UpdateMediaAccount(ctx, req)
if err != nil {
service.Error(ctx, err)
if req.MediaAccountUuid == "" {
_, err = service.BundleProvider.AddBundleBalance(context.Background(), &bundle.AddBundleBalanceReq{
UserId: int32(userID),
AccountConsumptionNumber: -1,
})
if err != nil {
service.Error(ctx, err)
return
}
}
return
}
service.Success(ctx, resp)
return
}
// 账号授权
func OAuthAccount(ctx *gin.Context) {
var req *cast.OAuthAccountV2Req
var resp *cast.OAuthAccountV2Resp
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
if err = req.Validate(); err != nil {
service.Error(ctx, err)
return
}
mediaResp, err := service.CastProvider.MediaUserList(ctx, &cast.MediaUserListReq{
MediaUserID: req.MediaAccountUuid,
Page: 1,
PageSize: 1,
})
if err != nil {
service.Error(ctx, err)
return
}
if mediaResp == nil || len(mediaResp.Data) == 0 {
service.Error(ctx, errors.New("未找到该账号"))
return
}
if err = SyncAsAuth(mediaResp.Data[0].ArtistUuid); err != nil {
service.Error(ctx, err)
return
}
if resp, err = service.CastProvider.OAuthAccountV2(ctx, req); err != nil {
service.Error(ctx, err)
return
}
if cast.PlatformIDENUM_DM == cast.PlatformIDENUM(resp.PlatformID) {
service.Success(ctx, map[string]interface{}{
"url": resp.AuthUrl,
})
return
}
if resp.ProfileKey == "" {
service.Error(ctx, errors.New("艺人未添加平台账号"))
return
}
jwtReq := &aryshare.GenerateJWTRequest{
Domain: "",
PrivateKey: "",
ProfileKey: resp.ProfileKey,
Logout: true,
Redirect: fmt.Sprintf("%s?artistUuid=%s&platformID=%d", config.AppConfig.System.AuthCallback, resp.ArtistUuid, resp.PlatformID),
AllowedSocial: []string{modelCast.PlatformNameKv[resp.PlatformID]},
Verify: false,
Base64: false,
ExpiresIn: 0,
Email: nil,
}
jwtResp, err := service.AyrshareProvider.GenerateJWT(context.Background(), jwtReq)
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, jwtResp)
return
}
func OAuth2Callback(ctx *gin.Context) {
var (
platformIds string
userID string
)
code := ctx.Query("code")
state := ctx.Query("state")
stateMM, _ := url.ParseQuery(state)
if len(stateMM["platform_id"]) > 0 {
platformIds = stateMM["platform_id"][0]
}
if len(stateMM["user_id"]) > 0 {
userID = stateMM["user_id"][0]
}
platformID, _ := strconv.ParseInt(platformIds, 10, 64)
req := &cast.OAuthCodeToTokenReq{
Code: code,
UserID: userID,
}
switch platformID {
case int64(cast.PlatformIDENUM_DM):
req.PlatformID = cast.PlatformIDENUM_DM
case int64(cast.PlatformIDENUM_TIKTOK):
req.PlatformID = cast.PlatformIDENUM_TIKTOK
default:
service.Error(ctx, errors.New(e.GetMsg(e.InvalidParams)))
return
}
zap.L().Info("OAuth2Callback", zap.Any("req", req), zap.Any("code", code), zap.Any("state", state))
_, err := service.CastProvider.OAuthCodeToToken(ctx, req)
if err != nil {
zap.L().Info("OAuth2Callback error", zap.Error(err))
ctx.Redirect(http.StatusFound, fmt.Sprintf("%s?%s", config.AppConfig.System.AuthRedirectUrl, "status=1"))
return
}
// TODO 跳转到前端页面
ctx.Redirect(http.StatusFound, fmt.Sprintf("%s?%s", config.AppConfig.System.AuthRedirectUrl, "status=0"))
//service.Success(ctx, map[string]interface{}{
// "req": req,
// "resp": resp,
//})
return
}
func AsOAuth2Callback(ctx *gin.Context) {
var (
//platformIds string
//userID string
)
artistUuid := ctx.Query("artistUuid")
//platformIDs := ctx.Query("platformID")
//platformID, _ := strconv.ParseInt(platformIDs, 10, 64)
// 刷新授权
var err error
var req modelCast.OAuthPlatformReq
if err = ctx.ShouldBind(&req); err != nil {
//service.Error(ctx, err)
ctx.Redirect(http.StatusFound, fmt.Sprintf("%s?status=%d&message=%s", config.AppConfig.System.AuthRedirectUrl, 1, "参数错误"))
return
}
err = SyncAsAuth(artistUuid)
if err != nil {
ctx.Redirect(http.StatusFound, fmt.Sprintf("%s?status=%d&message=%s", config.AppConfig.System.AuthRedirectUrl, 1, err.Error()))
return
}
ctx.Redirect(http.StatusFound, fmt.Sprintf("%s?status=%d&message=%s", config.AppConfig.System.AuthRedirectUrl, 0, ""))
return
}
func SyncAsAuth(artistUuid string) error {
resp, err := service.CastProvider.GetArtist(context.Background(), &cast.GetArtistReq{ArtistUuid: artistUuid})
if err != nil {
return errors.New("获取艺人信息错误")
}
if resp == nil || resp.ArtistInfo == nil || resp.ArtistInfo.ProfileKey == "" {
return errors.New("艺人未注册")
}
userResp, err := service.AyrshareProvider.GetUser(context.Background(), &aryshare.GetUserRequest{
ProfileKey: resp.ArtistInfo.ProfileKey,
InstagramDetails: true,
})
if err != nil {
return errors.New("获取艺人绑定信息错误")
}
authReq := &cast.UpdateOAuthReq{Data: make([]*cast.UpdateOAuthReq_Info, 0)}
if len(userResp.DisplayNames) == 0 {
//return errors.New("没有授权信息")
authReq.Data = append(authReq.Data, &cast.UpdateOAuthReq_Info{
ArtistUuid: artistUuid,
PlatformID: cast.PlatformIDENUM_UNKNOWN,
AsID: "",
PlatformUserName: "",
AutInfo: "",
})
_, err = service.CastProvider.UpdateOAuth(context.Background(), authReq)
if err != nil {
return errors.New("同步授权信息失败")
}
return nil
}
var asInfoB []byte
for _, v := range userResp.DisplayNames {
asInfoB, _ = json.Marshal(v)
platformIDENUM := cast.PlatformIDENUM(modelCast.NamePlatformIDKv[v.Platform])
if platformIDENUM == cast.PlatformIDENUM_UNKNOWN {
continue
}
authReq.Data = append(authReq.Data, &cast.UpdateOAuthReq_Info{
ArtistUuid: artistUuid,
PlatformID: platformIDENUM,
AsID: v.Id,
PlatformUserName: v.Username,
AutInfo: string(asInfoB),
})
}
if len(authReq.Data) != 0 {
_, err = service.CastProvider.UpdateOAuth(context.Background(), authReq)
if err != nil {
return errors.New("同步授权信息失败")
}
}
return nil
}
func RefreshToken(ctx *gin.Context) {
var req *cast.RefreshTokenReq
var resp *cast.RefreshTokenResp
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
if err = req.Validate(); err != nil {
service.Error(ctx, err)
return
}
if resp, err = service.CastProvider.RefreshToken(ctx, req); err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, resp)
return
}
func ArtistInfo(ctx *gin.Context) {
var req *cast.ArtistInfoReq
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
if err = req.Validate(); err != nil {
service.Error(ctx, err)
return
}
resp, err := service.CastProvider.ArtistInfo(ctx, req)
if err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, resp)
return
}
func CheckAsProfile(infoResp *accountFiee.UserInfoResponse) error {
var asArtistResp *cast.GetArtistResp
var err error
title := fmt.Sprintf("%s_%s", config.AppConfig.System.AppMode, infoResp.SubNum)
// 查询艺人的信息
if asArtistResp, err = service.CastProvider.GetArtist(context.Background(), &cast.GetArtistReq{
ArtistUuid: fmt.Sprint(infoResp.Id),
}); err != nil {
return err
}
if asArtistResp != nil && asArtistResp.ArtistInfo != nil && asArtistResp.ArtistInfo.ProfileKey != "" {
return nil
}
createProfileResp, err := service.AyrshareProvider.CreateProfile(context.Background(), &aryshare.CreateProfileRequest{
Title: title,
MessagingActive: true,
HideTopHeader: false,
TopHeader: "",
DisableSocial: nil,
Team: false,
Email: "",
SubHeader: "",
Tags: []string{config.AppConfig.System.AppMode},
})
if err != nil {
zap.L().Error("CreateProfile error", zap.Error(err))
err = errors.New("创建平台艺人账号失败")
return err
}
zap.L().Info("CreateProfile success", zap.Any("createProfileResp", createProfileResp), zap.Any("title", title))
_, err = service.CastProvider.UpdateArtist(context.Background(), &cast.UpdateArtistReq{
Uuid: asArtistResp.Uuid,
ArtistInfo: &cast.ArtistInfo{
ArtistUuid: fmt.Sprint(infoResp.Id),
RefID: createProfileResp.RefId,
ProfileKey: createProfileResp.ProfileKey,
SubNum: infoResp.SubNum,
},
})
if err != nil {
return err
}
return nil
}
func SyncAsProfile(ctx *gin.Context) {
var req *modelCast.SyncAsProfileReq
var err error
if err = ctx.ShouldBind(&req); err != nil {
service.Error(ctx, err)
return
}
infoResp, err := GetArtistAccountInfo(req.ID)
if err != nil {
service.Error(ctx, err)
return
}
if infoResp.SubNum == "" {
service.Error(ctx, errors.New("用户不存在"))
return
}
if err = CheckAsProfile(infoResp); err != nil {
service.Error(ctx, err)
return
}
service.Success(ctx, infoResp)
return
}
// ImportMediaAccount 导入自媒体账号
func ImportMediaAccount(ctx *gin.Context) {
excelFile, err := ctx.FormFile("file")
if err != nil {
service.Error(ctx, err)
return
}
tempDir := "./runtime/media"
_, err = utils.CheckDirPath(tempDir, true)
if err != nil {
service.Error(ctx, err)
return
}
fileName := fmt.Sprintf("%d_media_account.xlsx", time.Now().UnixMicro())
excelPath := filepath.Join(tempDir, fileName)
if err = ctx.SaveUploadedFile(excelFile, excelPath); err != nil {
service.Error(ctx, err)
return
}
excelData, err := excelize.OpenFile(excelPath)
if err != nil {
service.Error(ctx, err)
return
}
defer excelData.Close()
rows, err := excelData.GetRows("Sheet1")
if err != nil {
service.Error(ctx, err)
return
}
loginInfo := login.GetUserInfoFromC(ctx)
for line, row := range rows {
if line == 0 {
continue
}
if len(row) < 3 {
continue
}
subNum := strings.TrimSpace(row[1])
if subNum == "" {
continue
}
var subInfoResp *accountFiee.UserInfoResponse
//查询艺人信息
if config.AppConfig.System.AppMode == "dev" {
subInfoResp = &accountFiee.UserInfoResponse{
Id: 0,
SubNum: "",
}
} else {
subInfoResp, err = service.AccountFieeProvider.SubNumGetInfo(context.Background(), &accountFiee.SubNumGetInfoRequest{
SubNum: subNum,
Domain: "app",
})
}
if err != nil {
excelSetRemark(excelData, line, "查询艺人出错")
continue
}
if subInfoResp.Id == 0 {
excelSetRemark(excelData, line, "艺人不存在")
continue
}
var tiktokName, insName, dmName string
if len(row) >= 3 {
tiktokName = strings.TrimSpace(row[2])
}
if len(row) >= 4 {
insName = strings.TrimSpace(row[3])
}
if len(row) >= 5 {
dmName = strings.TrimSpace(row[4])
}
if tiktokName == "" && insName == "" && dmName == "" {
excelSetRemark(excelData, line, "请填写账号")
continue
}
if tiktokName != "" {
if err = updateMediaAccount(tiktokName, cast.PlatformIDENUM_TIKTOK, subInfoResp, loginInfo); err != nil {
excelSetRemark(excelData, line, err.Error())
}
}
if insName != "" {
if err = updateMediaAccount(tiktokName, cast.PlatformIDENUM_INS, subInfoResp, loginInfo); err != nil {
excelSetRemark(excelData, line, err.Error())
}
}
if dmName != "" {
if err = updateMediaAccount(tiktokName, cast.PlatformIDENUM_DM, subInfoResp, loginInfo); err != nil {
excelSetRemark(excelData, line, err.Error())
}
}
}
urlHost := config.AppConfig.System.FieeHost
urlResult := fmt.Sprintf("%s/api/fiee/static/media/%s", urlHost, fileName)
service.Success(ctx, map[string]interface{}{
"successCount": 0,
"failCount": 0,
"url": urlResult,
})
return
}
func excelSetRemark(excelData *excelize.File, line int, remark string) {
oldRemark, _ := excelData.GetCellValue("Sheet1", fmt.Sprintf("%s%d", "F", line+1))
if oldRemark != "" {
remark = fmt.Sprintf("%s\n%s", oldRemark, remark)
}
excelData.SetCellValue("Sheet1", fmt.Sprintf("%s%d", "F", line+1), remark)
}
func updateMediaAccount(platformName string, platformId cast.PlatformIDENUM, subInfoResp *accountFiee.UserInfoResponse, loginInfo login.Info) error {
_, err := service.CastProvider.UpdateMediaAccount(context.Background(), &cast.UpdateMediaAccountReq{
PlatformID: platformId,
PlatformUserName: platformName,
PlatformUserID: "",
ArtistUuid: fmt.Sprint(subInfoResp.Id),
ArtistName: subInfoResp.Name,
ArtistPhone: subInfoResp.TelNum,
MediaAccountUuid: "",
ManagerUuid: fmt.Sprint(loginInfo.ID),
ManagerUserName: loginInfo.Name,
ArtistSubNum: subInfoResp.SubNum,
})
return err
}