Compare commits
No commits in common. "f10c36d9fa9b5a9f13e47aa196c5c6ae3acaad2f" and "151324b4138a3352835e42cf198cfd6388e34d3b" have entirely different histories.
f10c36d9fa
...
151324b413
13449
api/cast/cast.pb.go
13449
api/cast/cast.pb.go
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go-triple. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-triple v1.0.8
|
||||
// - protoc v3.21.1
|
||||
// - protoc v6.32.0--rc2
|
||||
// source: pb/fiee/cast.proto
|
||||
|
||||
package cast
|
||||
@ -52,8 +52,6 @@ type CastClient interface {
|
||||
UpdateWorkPlatformInfo(ctx context.Context, in *UpdateWorkPlatformInfoReq, opts ...grpc_go.CallOption) (*UpdateWorkPlatformInfoResp, common.ErrorWithAttachment)
|
||||
UpdateWorkPublishLog(ctx context.Context, in *UpdateWorkPublishLogReq, opts ...grpc_go.CallOption) (*emptypb.Empty, common.ErrorWithAttachment)
|
||||
RefreshWorkList(ctx context.Context, in *RefreshWorkListReq, opts ...grpc_go.CallOption) (*RefreshWorkListResp, common.ErrorWithAttachment)
|
||||
WorkResource(ctx context.Context, in *WorkResourceReq, opts ...grpc_go.CallOption) (*WorkResourceResp, common.ErrorWithAttachment)
|
||||
UpdateWorkResource(ctx context.Context, in *UpdateWorkResourceReq, opts ...grpc_go.CallOption) (*UpdateWorkResourceResp, common.ErrorWithAttachment)
|
||||
OAuthAccount(ctx context.Context, in *OAuthAccountReq, opts ...grpc_go.CallOption) (*OAuthAccountResp, common.ErrorWithAttachment)
|
||||
OAuthAccountV2(ctx context.Context, in *OAuthAccountV2Req, opts ...grpc_go.CallOption) (*OAuthAccountV2Resp, common.ErrorWithAttachment)
|
||||
OAuthCodeToToken(ctx context.Context, in *OAuthCodeToTokenReq, opts ...grpc_go.CallOption) (*OAuthCodeToTokenResp, common.ErrorWithAttachment)
|
||||
@ -162,8 +160,6 @@ type CastClientImpl struct {
|
||||
UpdateWorkPlatformInfo func(ctx context.Context, in *UpdateWorkPlatformInfoReq) (*UpdateWorkPlatformInfoResp, error)
|
||||
UpdateWorkPublishLog func(ctx context.Context, in *UpdateWorkPublishLogReq) (*emptypb.Empty, error)
|
||||
RefreshWorkList func(ctx context.Context, in *RefreshWorkListReq) (*RefreshWorkListResp, error)
|
||||
WorkResource func(ctx context.Context, in *WorkResourceReq) (*WorkResourceResp, error)
|
||||
UpdateWorkResource func(ctx context.Context, in *UpdateWorkResourceReq) (*UpdateWorkResourceResp, error)
|
||||
OAuthAccount func(ctx context.Context, in *OAuthAccountReq) (*OAuthAccountResp, error)
|
||||
OAuthAccountV2 func(ctx context.Context, in *OAuthAccountV2Req) (*OAuthAccountV2Resp, error)
|
||||
OAuthCodeToToken func(ctx context.Context, in *OAuthCodeToTokenReq) (*OAuthCodeToTokenResp, error)
|
||||
@ -379,18 +375,6 @@ func (c *castClient) RefreshWorkList(ctx context.Context, in *RefreshWorkListReq
|
||||
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/RefreshWorkList", in, out)
|
||||
}
|
||||
|
||||
func (c *castClient) WorkResource(ctx context.Context, in *WorkResourceReq, opts ...grpc_go.CallOption) (*WorkResourceResp, common.ErrorWithAttachment) {
|
||||
out := new(WorkResourceResp)
|
||||
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
|
||||
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/WorkResource", in, out)
|
||||
}
|
||||
|
||||
func (c *castClient) UpdateWorkResource(ctx context.Context, in *UpdateWorkResourceReq, opts ...grpc_go.CallOption) (*UpdateWorkResourceResp, common.ErrorWithAttachment) {
|
||||
out := new(UpdateWorkResourceResp)
|
||||
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
|
||||
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/UpdateWorkResource", in, out)
|
||||
}
|
||||
|
||||
func (c *castClient) OAuthAccount(ctx context.Context, in *OAuthAccountReq, opts ...grpc_go.CallOption) (*OAuthAccountResp, common.ErrorWithAttachment) {
|
||||
out := new(OAuthAccountResp)
|
||||
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
|
||||
@ -796,8 +780,6 @@ type CastServer interface {
|
||||
UpdateWorkPlatformInfo(context.Context, *UpdateWorkPlatformInfoReq) (*UpdateWorkPlatformInfoResp, error)
|
||||
UpdateWorkPublishLog(context.Context, *UpdateWorkPublishLogReq) (*emptypb.Empty, error)
|
||||
RefreshWorkList(context.Context, *RefreshWorkListReq) (*RefreshWorkListResp, error)
|
||||
WorkResource(context.Context, *WorkResourceReq) (*WorkResourceResp, error)
|
||||
UpdateWorkResource(context.Context, *UpdateWorkResourceReq) (*UpdateWorkResourceResp, error)
|
||||
OAuthAccount(context.Context, *OAuthAccountReq) (*OAuthAccountResp, error)
|
||||
OAuthAccountV2(context.Context, *OAuthAccountV2Req) (*OAuthAccountV2Resp, error)
|
||||
OAuthCodeToToken(context.Context, *OAuthCodeToTokenReq) (*OAuthCodeToTokenResp, error)
|
||||
@ -953,12 +935,6 @@ func (UnimplementedCastServer) UpdateWorkPublishLog(context.Context, *UpdateWork
|
||||
func (UnimplementedCastServer) RefreshWorkList(context.Context, *RefreshWorkListReq) (*RefreshWorkListResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method RefreshWorkList not implemented")
|
||||
}
|
||||
func (UnimplementedCastServer) WorkResource(context.Context, *WorkResourceReq) (*WorkResourceResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method WorkResource not implemented")
|
||||
}
|
||||
func (UnimplementedCastServer) UpdateWorkResource(context.Context, *UpdateWorkResourceReq) (*UpdateWorkResourceResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method UpdateWorkResource not implemented")
|
||||
}
|
||||
func (UnimplementedCastServer) OAuthAccount(context.Context, *OAuthAccountReq) (*OAuthAccountResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method OAuthAccount not implemented")
|
||||
}
|
||||
@ -1843,64 +1819,6 @@ func _Cast_RefreshWorkList_Handler(srv interface{}, ctx context.Context, dec fun
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Cast_WorkResource_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(WorkResourceReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
base := srv.(dubbo3.Dubbo3GrpcService)
|
||||
args := []interface{}{}
|
||||
args = append(args, in)
|
||||
md, _ := metadata.FromIncomingContext(ctx)
|
||||
invAttachment := make(map[string]interface{}, len(md))
|
||||
for k, v := range md {
|
||||
invAttachment[k] = v
|
||||
}
|
||||
invo := invocation.NewRPCInvocation("WorkResource", args, invAttachment)
|
||||
if interceptor == nil {
|
||||
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
|
||||
return result, result.Error()
|
||||
}
|
||||
info := &grpc_go.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ctx.Value("XXX_TRIPLE_GO_INTERFACE_NAME").(string),
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
|
||||
return result, result.Error()
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Cast_UpdateWorkResource_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UpdateWorkResourceReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
base := srv.(dubbo3.Dubbo3GrpcService)
|
||||
args := []interface{}{}
|
||||
args = append(args, in)
|
||||
md, _ := metadata.FromIncomingContext(ctx)
|
||||
invAttachment := make(map[string]interface{}, len(md))
|
||||
for k, v := range md {
|
||||
invAttachment[k] = v
|
||||
}
|
||||
invo := invocation.NewRPCInvocation("UpdateWorkResource", args, invAttachment)
|
||||
if interceptor == nil {
|
||||
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
|
||||
return result, result.Error()
|
||||
}
|
||||
info := &grpc_go.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ctx.Value("XXX_TRIPLE_GO_INTERFACE_NAME").(string),
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
|
||||
return result, result.Error()
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Cast_OAuthAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(OAuthAccountReq)
|
||||
if err := dec(in); err != nil {
|
||||
@ -3827,14 +3745,6 @@ var Cast_ServiceDesc = grpc_go.ServiceDesc{
|
||||
MethodName: "RefreshWorkList",
|
||||
Handler: _Cast_RefreshWorkList_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "WorkResource",
|
||||
Handler: _Cast_WorkResource_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpdateWorkResource",
|
||||
Handler: _Cast_UpdateWorkResource_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "OAuthAccount",
|
||||
Handler: _Cast_OAuthAccount_Handler,
|
||||
|
||||
Binary file not shown.
179
pkg/cron/task.go
179
pkg/cron/task.go
@ -5,7 +5,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"fonchain-fiee/api/aryshare"
|
||||
"fonchain-fiee/api/bundle"
|
||||
"fonchain-fiee/api/cast"
|
||||
"fonchain-fiee/pkg/cache"
|
||||
@ -17,7 +16,6 @@ import (
|
||||
"log"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis"
|
||||
@ -30,7 +28,7 @@ func InitTasks() error {
|
||||
err := cm.AddTask("refreshWorkApprovalStatus", "0 */1 * * * *", RefreshWorkApprovalStatusTask)
|
||||
err = cm.AddTask("artistAutoConfirm", "0 */1 * * * *", ArtistAutoConfirmTask)
|
||||
err = cm.AddTask("refreshPublishStatus", "0 */1 * * * *", PublishTask)
|
||||
err = cm.AddTask("scheduledPublish", "0 */10 * * * *", ScheduledPublishTask)
|
||||
err = cm.AddTask("scheduledPublish", "0 */1 * * * *", ScheduledPublishTask)
|
||||
|
||||
err = cm.AddTask("artistAutoConfirmAnalysis", "0 */1 * * * *", ArtistAutoConfirmAnalysisTask)
|
||||
err = cm.AddTask("refreshWorkAnalysisApprovalStatus", "0 */1 * * * *", RefreshWorkAnalysisApprovalStatusTask)
|
||||
@ -42,9 +40,6 @@ func InitTasks() error {
|
||||
if err != nil {
|
||||
log.Printf("添加定时任务失败: %v", err)
|
||||
}
|
||||
|
||||
// 每2分钟执行一次标签观看次数更新任务
|
||||
// err = cm.AddTask("updateCastTagWatchCount", "0 */1 * * * *", UpdateCastTagWatchCountTask)
|
||||
cm.Start()
|
||||
|
||||
// 启动队列消费者
|
||||
@ -285,7 +280,7 @@ func PublishTask() {
|
||||
func ScheduledPublishTask() {
|
||||
// 加上锁
|
||||
lockKey := "scheduled_publish:lock"
|
||||
reply := cache.RedisClient.SetNX(lockKey, time.Now().Format("2006-01-02 15:04:05"), 2*time.Hour)
|
||||
reply := cache.RedisClient.SetNX(lockKey, time.Now().Format("2006-01-02 15:04:05"), 1*time.Hour)
|
||||
if !reply.Val() {
|
||||
zap.L().Warn("定时发布任务正在被其他实例处理")
|
||||
return
|
||||
@ -315,12 +310,9 @@ func ScheduledPublishTask() {
|
||||
const batchSize = 8 // 每批发布8个,与PublishWork的workerCount保持一致
|
||||
|
||||
zap.L().Info("发现定时发布任务", zap.Int("total_count", len(workList)))
|
||||
zap.L().Info("发现定时发布任务 列表", zap.Any("workList", workList))
|
||||
|
||||
now := float64(time.Now().Unix())
|
||||
publishBatch := make([]string, 0, batchSize)
|
||||
// 使用 map 进行去重,防止同一个 work_uuid 被多次添加
|
||||
publishedUuids := make(map[string]bool)
|
||||
|
||||
// 遍历所有数据,根据score判断处理
|
||||
for _, item := range workList {
|
||||
@ -343,32 +335,11 @@ func ScheduledPublishTask() {
|
||||
continue
|
||||
}
|
||||
|
||||
// 先从 Redis 中删除,采用"先删后处理"的策略,防止重复处理
|
||||
removed, delErr := cache.RedisClient.ZRem(modelCast.ScheduledPublishQueueKey, workUuid).Result()
|
||||
if delErr != nil {
|
||||
zap.L().Error("删除定时发布任务失败", zap.Error(delErr), zap.String("work_uuid", workUuid))
|
||||
continue // 删除失败则跳过,避免重复处理
|
||||
}
|
||||
if removed == 0 {
|
||||
zap.L().Warn("定时发布任务已被删除,跳过",
|
||||
zap.String("work_uuid", workUuid))
|
||||
continue // 已被其他实例删除,跳过
|
||||
}
|
||||
|
||||
// 检查是否已经在当前批次中(去重)
|
||||
if publishedUuids[workUuid] {
|
||||
zap.L().Warn("发现重复的定时发布任务,跳过",
|
||||
zap.String("work_uuid", workUuid))
|
||||
continue
|
||||
}
|
||||
|
||||
// score大于等于当前时间,添加到待发布批次
|
||||
zap.L().Info("添加到发布批次",
|
||||
zap.String("work_uuid", workUuid),
|
||||
zap.Float64("score", score))
|
||||
publishBatch = append(publishBatch, workUuid)
|
||||
publishedUuids[workUuid] = true
|
||||
publishCount++
|
||||
|
||||
// 当批次达到指定大小时,批量发布
|
||||
if len(publishBatch) >= batchSize {
|
||||
@ -376,6 +347,15 @@ func ScheduledPublishTask() {
|
||||
_ = serverCast.PublishWork(context.Background(), &cast.PublishReq{
|
||||
WorkUuids: publishBatch,
|
||||
})
|
||||
// 批量删除已发布的任务
|
||||
for _, uuid := range publishBatch {
|
||||
removed, delErr := cache.RedisClient.ZRem(modelCast.ScheduledPublishQueueKey, uuid).Result()
|
||||
if delErr != nil {
|
||||
zap.L().Error("删除定时发布任务失败", zap.Error(delErr), zap.String("work_uuid", uuid))
|
||||
} else if removed > 0 {
|
||||
publishCount++
|
||||
}
|
||||
}
|
||||
zap.L().Info("批次发布完成", zap.Int("published", len(publishBatch)))
|
||||
// 清空批次,准备下一批
|
||||
publishBatch = make([]string, 0, batchSize)
|
||||
@ -388,6 +368,15 @@ func ScheduledPublishTask() {
|
||||
_ = serverCast.PublishWork(context.Background(), &cast.PublishReq{
|
||||
WorkUuids: publishBatch,
|
||||
})
|
||||
// 批量删除已发布的任务
|
||||
for _, uuid := range publishBatch {
|
||||
removed, delErr := cache.RedisClient.ZRem(modelCast.ScheduledPublishQueueKey, uuid).Result()
|
||||
if delErr != nil {
|
||||
zap.L().Error("删除定时发布任务失败", zap.Error(delErr), zap.String("work_uuid", uuid))
|
||||
} else if removed > 0 {
|
||||
publishCount++
|
||||
}
|
||||
}
|
||||
zap.L().Info("剩余批次发布完成", zap.Int("published", len(publishBatch)))
|
||||
}
|
||||
|
||||
@ -578,131 +567,3 @@ func AyrshareMetricsCollectorTask() {
|
||||
func RefreshArtistOrderTask() {
|
||||
_, _ = service.CastProvider.Tools(context.Background(), &cast.ToolsReq{Action: "artistOrderInfo"})
|
||||
}
|
||||
|
||||
// UpdateCastTagWatchCountTask 更新标签观看次数的定时任务(每5分钟执行一次)
|
||||
func UpdateCastTagWatchCountTask() {
|
||||
ctx := context.Background()
|
||||
|
||||
// 计算两天前的00:00:00
|
||||
now := time.Now()
|
||||
twoDaysAgo := now.AddDate(0, 0, -2)
|
||||
createdAtStart := time.Date(twoDaysAgo.Year(), twoDaysAgo.Month(), twoDaysAgo.Day(), 0, 0, 0, 0, twoDaysAgo.Location())
|
||||
createdAtEnd := now
|
||||
|
||||
// 格式化时间字符串:2026-01-01 00:00:00
|
||||
createdAtStartStr := createdAtStart.Format("2006-01-02 15:04:05")
|
||||
createdAtEndStr := createdAtEnd.Format("2006-01-02 15:04:05")
|
||||
|
||||
// 调用 ListCastTags 接口,筛选 IsWatchCountCalled = 2 的数据
|
||||
listReq := &cast.ListCastTagsReq{
|
||||
CreatedAtStart: createdAtStartStr,
|
||||
CreatedAtEnd: createdAtEndStr,
|
||||
IsWatchCountCalled: 2, // 2表示未调用
|
||||
Page: 1,
|
||||
PageSize: 10,
|
||||
}
|
||||
|
||||
listResp, err := service.CastProvider.ListCastTags(ctx, listReq)
|
||||
if err != nil {
|
||||
zap.L().Error("获取标签列表失败", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
if listResp.Data == nil || len(listResp.Data) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
zap.L().Info("获取到需要更新的标签", zap.Int("count", len(listResp.Data)))
|
||||
|
||||
// 获取有效的 profileKey
|
||||
profileKey, err := serverCast.GetValidProfileKey(ctx, []uint32{1})
|
||||
if err != nil {
|
||||
zap.L().Error("获取有效profileKey失败", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
// 准备批量更新的数据
|
||||
updateData := make([]*cast.CastTagInfo, 0, len(listResp.Data))
|
||||
|
||||
// 遍历每个标签,调用 RecommendHashtags 接口
|
||||
for _, tag := range listResp.Data {
|
||||
if tag.HashTag == "" {
|
||||
zap.L().Warn("标签HashTag为空,跳过", zap.String("uuid", tag.Uuid))
|
||||
// 即使HashTag为空,也要更新IsWatchCountCalled为1
|
||||
updateData = append(updateData, &cast.CastTagInfo{
|
||||
Uuid: tag.Uuid,
|
||||
WatchCount: 1,
|
||||
IsWatchCountCalled: 1,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// 调用 RecommendHashtags 接口
|
||||
recommendReq := &aryshare.RecommendHashtagsRequest{
|
||||
Keyword: tag.HashTag,
|
||||
ProfileKey: profileKey,
|
||||
}
|
||||
|
||||
recommendResp, err := service.AyrshareProvider.RecommendHashtags(ctx, recommendReq)
|
||||
if err != nil {
|
||||
zap.L().Error("调用RecommendHashtags接口失败",
|
||||
zap.String("hashTag", tag.HashTag),
|
||||
zap.String("uuid", tag.Uuid),
|
||||
zap.Error(err))
|
||||
// 调用失败时,将WatchCount更新为1,IsWatchCountCalled更新为1
|
||||
updateData = append(updateData, &cast.CastTagInfo{
|
||||
Uuid: tag.Uuid,
|
||||
WatchCount: 1,
|
||||
IsWatchCountCalled: 1,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// 对比返回结果,查找完全一致的标签
|
||||
var matchedViewCount int64 = 0
|
||||
if recommendResp.Recommendations != nil {
|
||||
for _, recommendation := range recommendResp.Recommendations {
|
||||
// 完全一致匹配(不区分大小写)
|
||||
if strings.EqualFold(recommendation.Name, tag.HashTag) {
|
||||
matchedViewCount = recommendation.ViewCount
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 根据匹配结果更新WatchCount
|
||||
var watchCount int32 = 1
|
||||
if matchedViewCount > 0 {
|
||||
watchCount = int32(matchedViewCount)
|
||||
}
|
||||
|
||||
// 添加到更新列表
|
||||
updateData = append(updateData, &cast.CastTagInfo{
|
||||
Uuid: tag.Uuid,
|
||||
WatchCount: watchCount,
|
||||
IsWatchCountCalled: 1,
|
||||
})
|
||||
|
||||
zap.L().Debug("处理标签完成",
|
||||
zap.String("hashTag", tag.HashTag),
|
||||
zap.String("uuid", tag.Uuid),
|
||||
zap.Int64("matchedViewCount", matchedViewCount),
|
||||
zap.Int32("watchCount", watchCount))
|
||||
}
|
||||
|
||||
// 如果没有需要更新的数据,直接返回
|
||||
if len(updateData) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// 批量更新标签
|
||||
batchUpdateReq := &cast.BatchUpdateCastTagsReq{
|
||||
Data: updateData,
|
||||
}
|
||||
|
||||
_, err = service.CastProvider.BatchUpdateCastTags(ctx, batchUpdateReq)
|
||||
if err != nil {
|
||||
zap.L().Error("批量更新标签失败", zap.Error(err), zap.Int("count", len(updateData)))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,18 +81,6 @@ func MediaRouter(r *gin.RouterGroup) {
|
||||
prompt.POST("delete", serviceCast.DeletePrompt)
|
||||
}
|
||||
|
||||
tag := auth.Group("tag")
|
||||
{
|
||||
tag.POST("update", serviceCast.UpdateCastTag)
|
||||
tag.POST("list", serviceCast.ListCastTags)
|
||||
tag.POST("import-batch", serviceCast.ImportTagBatch)
|
||||
tag.POST("recalculate-quote-count", serviceCast.RecalculateCastTagQuoteCount)
|
||||
// tag.POST("auto-hashtags", serviceCast.AutoHashtags)
|
||||
// 这两个接口需要关闭ins,通过facebook授权
|
||||
// tag.POST("recommend-hashtags", serviceCast.RecommendHashtags)
|
||||
// tag.POST("search-hashtags", serviceCast.SearchHashtags)
|
||||
}
|
||||
|
||||
//AI 生图
|
||||
aiNoAuth := noAuth.Group("ai")
|
||||
{
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
package cast
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fonchain-fiee/api/cast"
|
||||
"fonchain-fiee/pkg/service"
|
||||
)
|
||||
|
||||
func CheckImage(workUuid string) error {
|
||||
detailResp, err := service.CastProvider.WorkDetail(context.Background(), &cast.WorkDetailReq{WorkUuid: workUuid})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if detailResp.CoverUrl == "" {
|
||||
return nil
|
||||
}
|
||||
if detailResp.WorkCategory == 1 {
|
||||
return nil
|
||||
}
|
||||
resourceResp, err := service.CastProvider.WorkResource(context.Background(), &cast.WorkResourceReq{WorkUuid: workUuid})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if detailResp.CoverUrl == resourceResp.OldCoverUrl {
|
||||
return nil
|
||||
}
|
||||
mediaUrls, err := ProcessImg([]string{detailResp.CoverUrl})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(mediaUrls) > 0 {
|
||||
_, _ = service.CastProvider.UpdateWorkResource(context.Background(), &cast.UpdateWorkResourceReq{
|
||||
Uuid: "",
|
||||
WorkUuid: detailResp.WorkUuid,
|
||||
OldCoverUrl: detailResp.CoverUrl,
|
||||
NewCoverUrl: mediaUrls[0],
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -1,440 +0,0 @@
|
||||
package cast
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"fonchain-fiee/api/aryshare"
|
||||
"fonchain-fiee/api/cast"
|
||||
"fonchain-fiee/cmd/config"
|
||||
"fonchain-fiee/pkg/model/login"
|
||||
"fonchain-fiee/pkg/service"
|
||||
"fonchain-fiee/pkg/utils"
|
||||
"math/rand"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/xuri/excelize/v2"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
// UpdateCastTag 更新话题标签
|
||||
func UpdateCastTag(ctx *gin.Context) {
|
||||
var req *cast.UpdateCastTagReq
|
||||
var err error
|
||||
if err = ctx.ShouldBind(&req); err != nil {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
newCtx := NewCtxWithUserInfo(ctx)
|
||||
resp, err := service.CastProvider.UpdateCastTag(newCtx, req)
|
||||
if err != nil {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
service.Success(ctx, resp)
|
||||
return
|
||||
}
|
||||
|
||||
// ListCastTags 获取话题标签列表
|
||||
func ListCastTags(ctx *gin.Context) {
|
||||
var req *cast.ListCastTagsReq
|
||||
var err error
|
||||
if err = ctx.ShouldBind(&req); err != nil {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
newCtx := NewCtxWithUserInfo(ctx)
|
||||
resp, err := service.CastProvider.ListCastTags(newCtx, req)
|
||||
if err != nil {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
service.Success(ctx, resp)
|
||||
return
|
||||
}
|
||||
|
||||
// ImportTagBatch 批量导入话题标签
|
||||
func ImportTagBatch(ctx *gin.Context) {
|
||||
// 接收form表单的Excel保存到本地进行解析
|
||||
excelFile, err := ctx.FormFile("file")
|
||||
if err != nil {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
tempDir := "./runtime/tag"
|
||||
_, err = utils.CheckDirPath(tempDir, true)
|
||||
fileName := time.Now().Format("20060102150405") + "_in_tag.xlsx"
|
||||
excelPath := filepath.Join(tempDir, fileName)
|
||||
if err = ctx.SaveUploadedFile(excelFile, excelPath); err != nil {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
// 读取Excel中的数据
|
||||
excelData, err := excelize.OpenFile(excelPath)
|
||||
if err != nil {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
defer excelData.Close()
|
||||
// 解析Excel中的数据
|
||||
rows, err := excelData.GetRows("Sheet1")
|
||||
if err != nil {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
req := cast.UpdateCastTagBatchReq{
|
||||
Data: make([]*cast.CastTagInfo, 0),
|
||||
}
|
||||
userInfo := login.GetUserInfoFromC(ctx)
|
||||
for line, row := range rows {
|
||||
if line == 0 {
|
||||
continue
|
||||
}
|
||||
temp := cast.CastTagInfo{
|
||||
CreatorUuid: fmt.Sprint(userInfo.ID),
|
||||
CreatorName: userInfo.Name,
|
||||
Source: 1, // 固定来源:人工导入
|
||||
Status: 1, // 固定状态:有效
|
||||
}
|
||||
// 解析Excel列:A-话题标签,B-备注
|
||||
if len(row) > 0 {
|
||||
temp.HashTag = strings.TrimSpace(row[0])
|
||||
}
|
||||
if len(row) > 1 {
|
||||
temp.Remark = strings.TrimSpace(row[1])
|
||||
}
|
||||
zap.L().Info("ImportTagBatch row", zap.Int("line", line), zap.Strings("row", row))
|
||||
// 验证必填字段:话题标签不能为空
|
||||
if utils.CleanString(temp.HashTag) == "" {
|
||||
temp.Remark = "必填项未填"
|
||||
req.Data = append(req.Data, &temp)
|
||||
continue
|
||||
}
|
||||
req.Data = append(req.Data, &temp)
|
||||
}
|
||||
newCtx := NewCtxWithUserInfo(ctx)
|
||||
resp, _err := service.CastProvider.UpdateCastTagBatch(newCtx, &req)
|
||||
if _err != nil {
|
||||
service.Error(ctx, _err)
|
||||
return
|
||||
}
|
||||
// 打开模板 写入resp 返回的数据
|
||||
templatePath := "./data/话题标签导入模板.xlsx"
|
||||
template, err := excelize.OpenFile(templatePath)
|
||||
if err != nil {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
defer template.Close()
|
||||
var urlResult string
|
||||
if resp.FailCount != 0 {
|
||||
rowIndex := 2 // 从第2行开始写入(第1行是表头)
|
||||
for _, v := range resp.Data {
|
||||
if v.Success {
|
||||
continue
|
||||
}
|
||||
// 写入失败的数据到Excel模板(只写入话题标签和备注两列)
|
||||
template.SetCellValue("Sheet1", fmt.Sprintf("A%d", rowIndex), v.HashTag)
|
||||
template.SetCellValue("Sheet1", fmt.Sprintf("B%d", rowIndex), v.Remark)
|
||||
rowIndex++
|
||||
}
|
||||
resultFilename := strings.Replace(fileName, "_in_tag.xlsx", "_out_tag.xlsx", -1)
|
||||
resultPath := fmt.Sprintf("./runtime/tag/%s", resultFilename)
|
||||
if err = template.SaveAs(resultPath); err != nil {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
urlHost := config.AppConfig.System.FieeHost
|
||||
urlResult = fmt.Sprintf("%s/api/fiee/static/tag/%s", urlHost, resultFilename)
|
||||
}
|
||||
|
||||
service.Success(ctx, map[string]interface{}{
|
||||
"successCount": resp.SuccessCount,
|
||||
"failCount": resp.FailCount,
|
||||
"resultUrl": urlResult,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// RecalculateCastTagQuoteCount 重新计算话题标签引用数量
|
||||
func RecalculateCastTagQuoteCount(ctx *gin.Context) {
|
||||
newCtx := NewCtxWithUserInfo(ctx)
|
||||
// 创建空的请求对象
|
||||
req := &emptypb.Empty{}
|
||||
resp, err := service.CastProvider.RecalculateCastTagQuoteCount(newCtx, req)
|
||||
if err != nil {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
service.Success(ctx, resp)
|
||||
return
|
||||
}
|
||||
|
||||
// findNewTags 对比两次标签,找出新增的标签
|
||||
func findNewTags(beforeTags, afterTags []string) []string {
|
||||
// 将 beforeTags 转换为 map,方便查找
|
||||
beforeMap := make(map[string]bool)
|
||||
for _, tag := range beforeTags {
|
||||
cleanTag := strings.TrimSpace(tag)
|
||||
if cleanTag != "" {
|
||||
beforeMap[cleanTag] = true
|
||||
}
|
||||
}
|
||||
|
||||
// 找出 afterTags 中不在 beforeTags 中的标签
|
||||
newTags := make([]string, 0)
|
||||
for _, tag := range afterTags {
|
||||
cleanTag := strings.TrimSpace(tag)
|
||||
if cleanTag != "" && !beforeMap[cleanTag] {
|
||||
newTags = append(newTags, cleanTag)
|
||||
}
|
||||
}
|
||||
|
||||
return newTags
|
||||
}
|
||||
|
||||
func GetValidProfileKey(ctx context.Context, platformIDs []uint32) (string, error) {
|
||||
if len(platformIDs) == 0 {
|
||||
platformIDs = []uint32{1, 2, 3, 5}
|
||||
}
|
||||
profileKeys, err := service.CastProvider.GetArtistAyrShareInfoByPlatformIDs(ctx, &cast.GetArtistAyrShareInfoByPlatformIDsReq{
|
||||
PlatformIDs: platformIDs,
|
||||
Page: 1,
|
||||
PageSize: 20,
|
||||
})
|
||||
if err != nil {
|
||||
zap.L().Error("GetArtistAyrShareInfoByPlatformIDs failed", zap.Error(err))
|
||||
return "", errors.New("获取有效profileKey失败")
|
||||
}
|
||||
if len(profileKeys.Data) == 0 {
|
||||
return "", errors.New("当前没有有效的profileKey")
|
||||
}
|
||||
|
||||
// 过滤出所有非空的 profileKey
|
||||
validProfileKeys := make([]string, 0)
|
||||
for _, item := range profileKeys.Data {
|
||||
if item.ProfileKey != "" {
|
||||
validProfileKeys = append(validProfileKeys, item.ProfileKey)
|
||||
}
|
||||
}
|
||||
|
||||
if len(validProfileKeys) == 0 {
|
||||
return "", errors.New("profileKey为空")
|
||||
}
|
||||
|
||||
// 从有效的 profileKey 中随机选择一个
|
||||
randIndex := rand.Intn(len(validProfileKeys))
|
||||
return validProfileKeys[randIndex], nil
|
||||
}
|
||||
|
||||
// SaveTagsToDatabase 将标签保存到数据库
|
||||
func SaveTagsToDatabase(ctx *gin.Context, tags []string, source uint32) error {
|
||||
if len(tags) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
userInfo := login.GetUserInfoFromC(ctx)
|
||||
newCtx := NewCtxWithUserInfo(ctx)
|
||||
|
||||
// 构建批量导入请求
|
||||
req := cast.UpdateCastTagBatchReq{
|
||||
Data: make([]*cast.CastTagInfo, 0, len(tags)),
|
||||
}
|
||||
|
||||
for _, tag := range tags {
|
||||
tagInfo := &cast.CastTagInfo{
|
||||
HashTag: tag,
|
||||
CreatorUuid: fmt.Sprint(userInfo.ID),
|
||||
CreatorName: userInfo.Name,
|
||||
Source: source, // 4: 自动标签(从内容中自动提取)
|
||||
Status: 1, // 1: 有效
|
||||
}
|
||||
req.Data = append(req.Data, tagInfo)
|
||||
}
|
||||
|
||||
// 调用批量导入接口
|
||||
_, err := service.CastProvider.UpdateCastTagBatch(newCtx, &req)
|
||||
if err != nil {
|
||||
err = errors.New("标签保存到数据库失败")
|
||||
zap.L().Error("SaveTagsToDatabase UpdateCastTagBatch failed", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
zap.L().Info("SaveTagsToDatabase success", zap.Int("tagCount", len(tags)), zap.Strings("tags", tags), zap.Uint32("source", source))
|
||||
return nil
|
||||
}
|
||||
|
||||
func GenerateAutoHashtags(ctx context.Context, post string, max int32, position, language string) (*aryshare.AutoHashtagsResponse, []string, bool, error) {
|
||||
// 验证帖子内容
|
||||
if post == "" {
|
||||
return nil, nil, false, errors.New("帖子内容不能为空")
|
||||
}
|
||||
// post 的长度不能超过1000个字符
|
||||
if len(post) > 1000 {
|
||||
return nil, nil, false, errors.New("自动生成标签的帖子内容不能超过1000个字符")
|
||||
}
|
||||
|
||||
// 提取生成前的标签
|
||||
beforeTags := utils.ExtractTags(post)
|
||||
zap.L().Info("GenerateAutoHashtags beforeTags", zap.Strings("beforeTags", beforeTags))
|
||||
|
||||
// 如果标签数量已经达到或超过5个,不需要生成
|
||||
if len(beforeTags) >= 5 {
|
||||
return nil, nil, false, nil
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
if position == "" {
|
||||
position = "end"
|
||||
}
|
||||
if language == "" {
|
||||
language = "zh"
|
||||
}
|
||||
|
||||
// 如果 max 为0,则根据现有标签数自动计算,确保总数为5
|
||||
if max == 0 {
|
||||
max = int32(5 - len(beforeTags))
|
||||
}
|
||||
|
||||
// 如果此时 max 小于等于0,则直接返回
|
||||
if max <= 0 {
|
||||
return nil, nil, false, nil
|
||||
}
|
||||
|
||||
profileKey, err := GetValidProfileKey(ctx, []uint32{1, 2, 3, 5})
|
||||
if err != nil {
|
||||
return nil, nil, false, err
|
||||
}
|
||||
|
||||
// 构建请求
|
||||
req := &aryshare.AutoHashtagsRequest{
|
||||
Post: post,
|
||||
Max: max,
|
||||
Position: position,
|
||||
Language: language,
|
||||
ProfileKey: profileKey,
|
||||
}
|
||||
|
||||
// 调用 Ayrshare 的 AutoHashtags 接口
|
||||
resp, err := service.AyrshareProvider.AutoHashtags(ctx, req)
|
||||
if err != nil {
|
||||
zap.L().Error("AutoHashtags failed", zap.Error(err))
|
||||
return nil, nil, false, errors.New("自动生成标签失败")
|
||||
}
|
||||
|
||||
if resp.Post == "" {
|
||||
return nil, nil, false, errors.New("自动生成标签返回的帖子内容为空")
|
||||
}
|
||||
|
||||
// 去掉自动标签返回的帖子内容多余的引号
|
||||
resp.Post = utils.CleanAutoHashtagsQuote(resp.Post)
|
||||
|
||||
// 提取生成后的标签
|
||||
afterTags := utils.ExtractTags(resp.Post)
|
||||
zap.L().Info("GenerateAutoHashtags afterTags", zap.Strings("afterTags", afterTags))
|
||||
|
||||
// 对比两次标签,找出新增的标签
|
||||
newTags := findNewTags(beforeTags, afterTags)
|
||||
|
||||
return resp, newTags, true, nil
|
||||
}
|
||||
|
||||
// AutoHashtags 自动生成标签
|
||||
func AutoHashtags(ctx *gin.Context) {
|
||||
var req *aryshare.AutoHashtagsRequest
|
||||
var err error
|
||||
if err = ctx.ShouldBind(&req); err != nil {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 调用核心逻辑生成标签
|
||||
resp, newTags, needMore, err := GenerateAutoHashtags(
|
||||
context.Background(),
|
||||
req.Post,
|
||||
req.Max,
|
||||
req.Position,
|
||||
req.Language,
|
||||
)
|
||||
if err != nil {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 如果标签已满5个,直接返回
|
||||
if !needMore {
|
||||
service.Success(ctx, map[string]interface{}{
|
||||
"message": "当前帖子的标签已经有5个了",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 保存新增的标签到数据库,Source 设置为 4(自动标签)
|
||||
if len(newTags) > 0 {
|
||||
if err = SaveTagsToDatabase(ctx, newTags, 4); err != nil {
|
||||
zap.L().Error("SaveTagsToDatabase failed", zap.Error(err), zap.Strings("newTags", newTags))
|
||||
err = errors.New("标签保存到数据库失败")
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
service.Success(ctx, resp)
|
||||
return
|
||||
}
|
||||
|
||||
// RecommendHashtags 推荐标签
|
||||
func RecommendHashtags(ctx *gin.Context) {
|
||||
var req *aryshare.RecommendHashtagsRequest
|
||||
var err error
|
||||
if err = ctx.ShouldBind(&req); err != nil {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
profileKey, err := GetValidProfileKey(context.Background(), []uint32{1})
|
||||
if err != nil {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
req.ProfileKey = profileKey
|
||||
resp, err := service.AyrshareProvider.RecommendHashtags(context.Background(), req)
|
||||
if err != nil {
|
||||
fmt.Println("err", err)
|
||||
zap.L().Error("RecommendHashtags failed", zap.Error(err))
|
||||
err = errors.New("推荐标签失败")
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
service.Success(ctx, resp)
|
||||
return
|
||||
}
|
||||
|
||||
// SearchHashtags 搜索标签
|
||||
func SearchHashtags(ctx *gin.Context) {
|
||||
var req *aryshare.SearchHashtagsRequest
|
||||
var err error
|
||||
if err = ctx.ShouldBind(&req); err != nil {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
profileKey, err := GetValidProfileKey(context.Background(), []uint32{3})
|
||||
if err != nil {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
req.ProfileKey = profileKey
|
||||
resp, err := service.AyrshareProvider.SearchHashtags(context.Background(), req)
|
||||
if err != nil {
|
||||
fmt.Println("err", err)
|
||||
err = errors.New("获取热门话题标签失败")
|
||||
zap.L().Error("SearchHashtags failed", zap.Error(err))
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
service.Success(ctx, resp)
|
||||
return
|
||||
}
|
||||
@ -17,13 +17,6 @@ import (
|
||||
func Test(ctx *gin.Context) {
|
||||
action := ctx.PostForm("action")
|
||||
if action == "" {
|
||||
workUuid := ctx.PostForm("workUuid")
|
||||
err := CheckImage(workUuid)
|
||||
if err != nil {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
service.Success(ctx, nil)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@ -122,14 +122,6 @@ func UpdateWorkImageCore(ctx *gin.Context, req *cast.UpdateWorkImageReq) (*cast.
|
||||
//if _, err = CheckUserBundleBalance(int32(artistID), modelCast.BalanceTypeImageValue); err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
|
||||
// 处理内容中的标签:提取、验证并批量导入,以及自动生成标签
|
||||
content, err := processContentAndAutoTags(ctx, req.Content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 将自动生成标签后的内容更新到请求中
|
||||
req.Content = content
|
||||
newCtx := NewCtxWithUserInfo(ctx)
|
||||
req.Source = 1
|
||||
resp, err := service.CastProvider.UpdateWorkImage(newCtx, req)
|
||||
@ -179,100 +171,6 @@ func UpdateWorkImage(ctx *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
func processContentTags(ctx *gin.Context, content string) error {
|
||||
// 如果内容为空,直接返回
|
||||
if content == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 提取标签
|
||||
tags := utils.ExtractTags(content)
|
||||
if len(tags) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 第一步:检查标签格式(验证标签不为空且有效)
|
||||
validTags := make([]string, 0, len(tags))
|
||||
for _, tag := range tags {
|
||||
// 去除空白字符后检查
|
||||
cleanTag := strings.TrimSpace(tag)
|
||||
if cleanTag != "" {
|
||||
validTags = append(validTags, cleanTag)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有有效标签,直接返回
|
||||
if len(validTags) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 第二步:检查是否有重复的标签
|
||||
tagMap := make(map[string]bool)
|
||||
for _, tag := range validTags {
|
||||
tagLower := strings.ToLower(tag)
|
||||
if tagMap[tagLower] {
|
||||
return errors.New("帖子标签不能重复")
|
||||
}
|
||||
tagMap[tagLower] = true
|
||||
}
|
||||
|
||||
// 第三步:检查标签数量是否超过5个
|
||||
if len(validTags) > 5 {
|
||||
return errors.New("帖子标签数量不能超过5个")
|
||||
}
|
||||
|
||||
fmt.Println("validTags", validTags)
|
||||
|
||||
// 第四步:调用 SaveTagsToDatabase 函数批量导入标签,Source 设置为 3(推荐标签)
|
||||
if err := SaveTagsToDatabase(ctx, validTags, 3); err != nil {
|
||||
zap.L().Error("processContentTags SaveTagsToDatabase failed", zap.Error(err))
|
||||
return errors.New("批量导入标签失败")
|
||||
}
|
||||
|
||||
zap.L().Info("processContentTags success", zap.Int("tagCount", len(validTags)), zap.Strings("tags", validTags))
|
||||
return nil
|
||||
}
|
||||
|
||||
// processContentAndAutoTags 处理内容标签并自动生成标签
|
||||
func processContentAndAutoTags(ctx *gin.Context, content string) (string, error) {
|
||||
// 如果内容为空,直接返回
|
||||
if content == "" {
|
||||
return "", nil
|
||||
}
|
||||
// 处理内容中的标签:提取、验证并批量导入
|
||||
if err := processContentTags(ctx, content); err != nil {
|
||||
return content, err
|
||||
}
|
||||
// 处理完内容标签后,自动生成标签并存入数据库
|
||||
resp, newTags, needMore, err := GenerateAutoHashtags(
|
||||
context.Background(),
|
||||
content,
|
||||
0, // max 为0时自动计算
|
||||
"", // position 使用默认值
|
||||
"", // language 使用默认值
|
||||
)
|
||||
if err != nil {
|
||||
return content, err
|
||||
}
|
||||
|
||||
if resp == nil {
|
||||
return content, nil
|
||||
}
|
||||
|
||||
// 保存新生成的标签到数据库
|
||||
if needMore && len(newTags) > 0 {
|
||||
if saveErr := SaveTagsToDatabase(ctx, newTags, 4); saveErr != nil {
|
||||
zap.L().Error("processContentAndAutoTags SaveTagsToDatabase failed", zap.Error(saveErr))
|
||||
return content, errors.New("自动生成标签保存到数据库失败")
|
||||
}
|
||||
}
|
||||
// 检查一下 resp.Post 是否为空
|
||||
if resp.Post == "" {
|
||||
return content, nil
|
||||
}
|
||||
return resp.Post, nil
|
||||
}
|
||||
|
||||
// UpdateWorkVideoCore 更新作品视频的核心逻辑,可以被其他函数复用
|
||||
func UpdateWorkVideoCore(ctx *gin.Context, req *cast.UpdateWorkVideoReq) (*cast.UpdateWorkVideoResp, error) {
|
||||
var infoResp *accountFiee.UserInfoResponse
|
||||
@ -352,15 +250,6 @@ func UpdateWorkVideoCore(ctx *gin.Context, req *cast.UpdateWorkVideoReq) (*cast.
|
||||
req.ArtistPhone = infoResp.TelNum
|
||||
req.ArtistPhoneAreaCode = infoResp.TelAreaCode
|
||||
req.ArtistSubNum = infoResp.SubNum
|
||||
|
||||
// 处理内容中的标签:提取、验证并批量导入,以及自动生成标签
|
||||
fmt.Println("UpdateWorkVideoCore: req.Content=", req.Content, "req.Title=", req.Title)
|
||||
content, err := processContentAndAutoTags(ctx, req.Content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 将自动生成标签后的内容更新到请求中
|
||||
req.Content = content
|
||||
newCtx := NewCtxWithUserInfo(ctx)
|
||||
req.Source = 1
|
||||
resp, err := service.CastProvider.UpdateWorkVideo(newCtx, req)
|
||||
@ -659,8 +548,6 @@ func PublishWork(ctx context.Context, req *cast.PublishReq) error {
|
||||
|
||||
func PostAS(ctx context.Context, workUuid string) error {
|
||||
var err error
|
||||
//检查封面
|
||||
_ = CheckImage(workUuid)
|
||||
_, err = service.CastProvider.Publish(ctx, &cast.PublishReq{WorkUuids: []string{workUuid}})
|
||||
if err != nil {
|
||||
zap.L().Error("Publish err", zap.String("workUuid", workUuid), zap.Error(err))
|
||||
@ -1001,6 +888,7 @@ func RePublish(ctx *gin.Context) {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
|
||||
service.Success(ctx, req)
|
||||
return
|
||||
}
|
||||
@ -1526,15 +1414,6 @@ func ImportWorkBatch(ctx *gin.Context) {
|
||||
req.ImageWorks = append(req.ImageWorks, temp)
|
||||
break
|
||||
}
|
||||
// 处理内容中的标签:提取、验证并批量导入,以及自动生成标签
|
||||
processedContent, err := processContentAndAutoTags(ctx, temp.Content)
|
||||
if err != nil {
|
||||
temp.Remark = fmt.Sprintf("%s", err.Error())
|
||||
req.ImageWorks = append(req.ImageWorks, temp)
|
||||
break
|
||||
}
|
||||
// 将处理后的内容更新到 temp.Content
|
||||
temp.Content = processedContent
|
||||
}
|
||||
}
|
||||
for i := 10; i <= 20; i++ {
|
||||
|
||||
@ -297,36 +297,35 @@ func (p *BatchProcessor) submitTask(req *excelData) error {
|
||||
if !tiktokFound {
|
||||
return fmt.Errorf("未找到匹配的TikTok账号: %s", req.TikTok)
|
||||
}
|
||||
if req.Instagram != "" {
|
||||
// 获取 Instagram 自媒体账号
|
||||
accountListIns, err := service.CastProvider.MediaUserList(context.Background(), &apiCast.MediaUserListReq{
|
||||
ArtistVal: req.ArtistName,
|
||||
PlatformID: 3,
|
||||
Page: 1,
|
||||
PageSize: 10,
|
||||
ArtistUuid: strconv.FormatUint(list.UserList[0].Id, 10),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取 Instagram 账号失败: %s", err.Error())
|
||||
}
|
||||
if accountListIns == nil || len(accountListIns.Data) == 0 {
|
||||
return fmt.Errorf("ins自媒体账号数量为0")
|
||||
}
|
||||
// 查找 Instagram 账号
|
||||
insFound := false
|
||||
for _, user := range accountListIns.Data {
|
||||
if user.PlatformUserName == req.Instagram {
|
||||
req.MediaAccountNames = append(req.MediaAccountNames, user.PlatformUserName)
|
||||
req.MediaAccountUuids = append(req.MediaAccountUuids, user.MediaAccountUuid)
|
||||
insFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !insFound {
|
||||
return fmt.Errorf("未找到匹配的Instagram账号: %s", req.Instagram)
|
||||
}
|
||||
|
||||
// 获取 Instagram 自媒体账号
|
||||
accountListIns, err := service.CastProvider.MediaUserList(context.Background(), &apiCast.MediaUserListReq{
|
||||
ArtistVal: req.ArtistName,
|
||||
PlatformID: 3,
|
||||
Page: 1,
|
||||
PageSize: 10,
|
||||
ArtistUuid: strconv.FormatUint(list.UserList[0].Id, 10),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取 Instagram 账号失败: %s", err.Error())
|
||||
}
|
||||
if accountListIns == nil || len(accountListIns.Data) == 0 {
|
||||
return fmt.Errorf("ins自媒体账号数量为0")
|
||||
}
|
||||
// 查找 Instagram 账号
|
||||
insFound := false
|
||||
for _, user := range accountListIns.Data {
|
||||
if user.PlatformUserName == req.Instagram {
|
||||
req.MediaAccountNames = append(req.MediaAccountNames, user.PlatformUserName)
|
||||
req.MediaAccountUuids = append(req.MediaAccountUuids, user.MediaAccountUuid)
|
||||
insFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !insFound {
|
||||
return fmt.Errorf("未找到匹配的Instagram账号: %s", req.Instagram)
|
||||
}
|
||||
|
||||
// 获取 Bluesky 自媒体账号
|
||||
accountListBlueSky, err := service.CastProvider.MediaUserList(context.Background(), &apiCast.MediaUserListReq{
|
||||
ArtistVal: req.ArtistName,
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
import "strings"
|
||||
|
||||
// CleanString 移除所有空白字符
|
||||
func CleanString(s string) string {
|
||||
@ -21,33 +18,3 @@ func TruncateString(s string, maxLen int) string {
|
||||
}
|
||||
return string(runes[:maxLen])
|
||||
}
|
||||
|
||||
// ExtractTags 从文本中提取标签,标签以 # 开头,后面不能直接跟空格
|
||||
func ExtractTags(s string) []string {
|
||||
if len(s) == 0 {
|
||||
return []string{}
|
||||
}
|
||||
re := regexp.MustCompile(`#[^\s#\p{P}]+`)
|
||||
matches := re.FindAllString(s, -1)
|
||||
|
||||
tags := make([]string, 0, len(matches))
|
||||
for _, match := range matches {
|
||||
// 去掉开头的 #,只保留标签内容
|
||||
tag := match[1:]
|
||||
if len(tag) > 0 {
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
}
|
||||
|
||||
return tags
|
||||
}
|
||||
|
||||
// 去掉自动标签里面多余的引号
|
||||
func CleanAutoHashtagsQuote(input string) string {
|
||||
if input == "" {
|
||||
return ""
|
||||
}
|
||||
cleaned := strings.ReplaceAll(input, "\\\"", "")
|
||||
cleaned = strings.ReplaceAll(cleaned, "\"", "")
|
||||
return cleaned
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user