feat: 增加刷数据分析接口
This commit is contained in:
parent
fcddb6a732
commit
7f9d069190
6392
api/cast/cast.pb.go
6392
api/cast/cast.pb.go
File diff suppressed because it is too large
Load Diff
@ -11213,6 +11213,268 @@ var _ interface {
|
||||
ErrorName() string
|
||||
} = CreateWorkAnalysisRespValidationError{}
|
||||
|
||||
// Validate checks the field values on ImportWorkAnalysisReq with the rules
|
||||
// defined in the proto definition for this message. If any rules are
|
||||
// violated, the first error encountered is returned, or nil if there are no violations.
|
||||
func (m *ImportWorkAnalysisReq) Validate() error {
|
||||
return m.validate(false)
|
||||
}
|
||||
|
||||
// ValidateAll checks the field values on ImportWorkAnalysisReq with the rules
|
||||
// defined in the proto definition for this message. If any rules are
|
||||
// violated, the result is a list of violation errors wrapped in
|
||||
// ImportWorkAnalysisReqMultiError, or nil if none found.
|
||||
func (m *ImportWorkAnalysisReq) ValidateAll() error {
|
||||
return m.validate(true)
|
||||
}
|
||||
|
||||
func (m *ImportWorkAnalysisReq) validate(all bool) error {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errors []error
|
||||
|
||||
// no validation rules for Uuid
|
||||
|
||||
// no validation rules for SubNum
|
||||
|
||||
// no validation rules for ArtistName
|
||||
|
||||
// no validation rules for ArtistID
|
||||
|
||||
// no validation rules for ArtistPhone
|
||||
|
||||
// no validation rules for Analysis
|
||||
|
||||
// no validation rules for Title
|
||||
|
||||
// no validation rules for PdfUrl
|
||||
|
||||
// no validation rules for MediaAccountCount
|
||||
|
||||
// no validation rules for WorkVideoCount
|
||||
|
||||
// no validation rules for WorkImageCount
|
||||
|
||||
// no validation rules for Views
|
||||
|
||||
// no validation rules for Likes
|
||||
|
||||
// no validation rules for Comments
|
||||
|
||||
// no validation rules for Shares
|
||||
|
||||
// no validation rules for FansCount
|
||||
|
||||
// no validation rules for TopCities
|
||||
|
||||
// no validation rules for MostActiveDay
|
||||
|
||||
// no validation rules for BestPostTime
|
||||
|
||||
// no validation rules for PeriodTypeFans
|
||||
|
||||
// no validation rules for PeriodTypeViews
|
||||
|
||||
// no validation rules for PeriodTypeLikes
|
||||
|
||||
// no validation rules for PeriodTypeComments
|
||||
|
||||
// no validation rules for PeriodTypeShares
|
||||
|
||||
// no validation rules for BundleOrderUuid
|
||||
|
||||
// no validation rules for Date
|
||||
|
||||
// no validation rules for SubmitTime
|
||||
|
||||
// no validation rules for IsRefreshData
|
||||
|
||||
if len(errors) > 0 {
|
||||
return ImportWorkAnalysisReqMultiError(errors)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ImportWorkAnalysisReqMultiError is an error wrapping multiple validation
|
||||
// errors returned by ImportWorkAnalysisReq.ValidateAll() if the designated
|
||||
// constraints aren't met.
|
||||
type ImportWorkAnalysisReqMultiError []error
|
||||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m ImportWorkAnalysisReqMultiError) Error() string {
|
||||
var msgs []string
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
return strings.Join(msgs, "; ")
|
||||
}
|
||||
|
||||
// AllErrors returns a list of validation violation errors.
|
||||
func (m ImportWorkAnalysisReqMultiError) AllErrors() []error { return m }
|
||||
|
||||
// ImportWorkAnalysisReqValidationError is the validation error returned by
|
||||
// ImportWorkAnalysisReq.Validate if the designated constraints aren't met.
|
||||
type ImportWorkAnalysisReqValidationError struct {
|
||||
field string
|
||||
reason string
|
||||
cause error
|
||||
key bool
|
||||
}
|
||||
|
||||
// Field function returns field value.
|
||||
func (e ImportWorkAnalysisReqValidationError) Field() string { return e.field }
|
||||
|
||||
// Reason function returns reason value.
|
||||
func (e ImportWorkAnalysisReqValidationError) Reason() string { return e.reason }
|
||||
|
||||
// Cause function returns cause value.
|
||||
func (e ImportWorkAnalysisReqValidationError) Cause() error { return e.cause }
|
||||
|
||||
// Key function returns key value.
|
||||
func (e ImportWorkAnalysisReqValidationError) Key() bool { return e.key }
|
||||
|
||||
// ErrorName returns error name.
|
||||
func (e ImportWorkAnalysisReqValidationError) ErrorName() string {
|
||||
return "ImportWorkAnalysisReqValidationError"
|
||||
}
|
||||
|
||||
// Error satisfies the builtin error interface
|
||||
func (e ImportWorkAnalysisReqValidationError) Error() string {
|
||||
cause := ""
|
||||
if e.cause != nil {
|
||||
cause = fmt.Sprintf(" | caused by: %v", e.cause)
|
||||
}
|
||||
|
||||
key := ""
|
||||
if e.key {
|
||||
key = "key for "
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"invalid %sImportWorkAnalysisReq.%s: %s%s",
|
||||
key,
|
||||
e.field,
|
||||
e.reason,
|
||||
cause)
|
||||
}
|
||||
|
||||
var _ error = ImportWorkAnalysisReqValidationError{}
|
||||
|
||||
var _ interface {
|
||||
Field() string
|
||||
Reason() string
|
||||
Key() bool
|
||||
Cause() error
|
||||
ErrorName() string
|
||||
} = ImportWorkAnalysisReqValidationError{}
|
||||
|
||||
// Validate checks the field values on ImportWorkAnalysisResp with the rules
|
||||
// defined in the proto definition for this message. If any rules are
|
||||
// violated, the first error encountered is returned, or nil if there are no violations.
|
||||
func (m *ImportWorkAnalysisResp) Validate() error {
|
||||
return m.validate(false)
|
||||
}
|
||||
|
||||
// ValidateAll checks the field values on ImportWorkAnalysisResp with the rules
|
||||
// defined in the proto definition for this message. If any rules are
|
||||
// violated, the result is a list of violation errors wrapped in
|
||||
// ImportWorkAnalysisRespMultiError, or nil if none found.
|
||||
func (m *ImportWorkAnalysisResp) ValidateAll() error {
|
||||
return m.validate(true)
|
||||
}
|
||||
|
||||
func (m *ImportWorkAnalysisResp) validate(all bool) error {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errors []error
|
||||
|
||||
// no validation rules for Uuid
|
||||
|
||||
if len(errors) > 0 {
|
||||
return ImportWorkAnalysisRespMultiError(errors)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ImportWorkAnalysisRespMultiError is an error wrapping multiple validation
|
||||
// errors returned by ImportWorkAnalysisResp.ValidateAll() if the designated
|
||||
// constraints aren't met.
|
||||
type ImportWorkAnalysisRespMultiError []error
|
||||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m ImportWorkAnalysisRespMultiError) Error() string {
|
||||
var msgs []string
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
return strings.Join(msgs, "; ")
|
||||
}
|
||||
|
||||
// AllErrors returns a list of validation violation errors.
|
||||
func (m ImportWorkAnalysisRespMultiError) AllErrors() []error { return m }
|
||||
|
||||
// ImportWorkAnalysisRespValidationError is the validation error returned by
|
||||
// ImportWorkAnalysisResp.Validate if the designated constraints aren't met.
|
||||
type ImportWorkAnalysisRespValidationError struct {
|
||||
field string
|
||||
reason string
|
||||
cause error
|
||||
key bool
|
||||
}
|
||||
|
||||
// Field function returns field value.
|
||||
func (e ImportWorkAnalysisRespValidationError) Field() string { return e.field }
|
||||
|
||||
// Reason function returns reason value.
|
||||
func (e ImportWorkAnalysisRespValidationError) Reason() string { return e.reason }
|
||||
|
||||
// Cause function returns cause value.
|
||||
func (e ImportWorkAnalysisRespValidationError) Cause() error { return e.cause }
|
||||
|
||||
// Key function returns key value.
|
||||
func (e ImportWorkAnalysisRespValidationError) Key() bool { return e.key }
|
||||
|
||||
// ErrorName returns error name.
|
||||
func (e ImportWorkAnalysisRespValidationError) ErrorName() string {
|
||||
return "ImportWorkAnalysisRespValidationError"
|
||||
}
|
||||
|
||||
// Error satisfies the builtin error interface
|
||||
func (e ImportWorkAnalysisRespValidationError) Error() string {
|
||||
cause := ""
|
||||
if e.cause != nil {
|
||||
cause = fmt.Sprintf(" | caused by: %v", e.cause)
|
||||
}
|
||||
|
||||
key := ""
|
||||
if e.key {
|
||||
key = "key for "
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"invalid %sImportWorkAnalysisResp.%s: %s%s",
|
||||
key,
|
||||
e.field,
|
||||
e.reason,
|
||||
cause)
|
||||
}
|
||||
|
||||
var _ error = ImportWorkAnalysisRespValidationError{}
|
||||
|
||||
var _ interface {
|
||||
Field() string
|
||||
Reason() string
|
||||
Key() bool
|
||||
Cause() error
|
||||
ErrorName() string
|
||||
} = ImportWorkAnalysisRespValidationError{}
|
||||
|
||||
// Validate checks the field values on UpdateWorkAnalysisReq with the rules
|
||||
// defined in the proto definition for this message. If any rules are
|
||||
// violated, the first error encountered is returned, or nil if there are no violations.
|
||||
|
||||
@ -85,6 +85,7 @@ type CastClient interface {
|
||||
GetArtist(ctx context.Context, in *GetArtistReq, opts ...grpc_go.CallOption) (*GetArtistResp, common.ErrorWithAttachment)
|
||||
// 作品分析相关接口
|
||||
CreateWorkAnalysis(ctx context.Context, in *CreateWorkAnalysisReq, opts ...grpc_go.CallOption) (*CreateWorkAnalysisResp, common.ErrorWithAttachment)
|
||||
ImportWorkAnalysis(ctx context.Context, in *ImportWorkAnalysisReq, opts ...grpc_go.CallOption) (*ImportWorkAnalysisResp, common.ErrorWithAttachment)
|
||||
UpdateWorkAnalysis(ctx context.Context, in *UpdateWorkAnalysisReq, opts ...grpc_go.CallOption) (*emptypb.Empty, common.ErrorWithAttachment)
|
||||
UpdateWorkAnalysisStatus(ctx context.Context, in *UpdateWorkAnalysisStatusReq, opts ...grpc_go.CallOption) (*emptypb.Empty, common.ErrorWithAttachment)
|
||||
GetWorkAnalysis(ctx context.Context, in *GetWorkAnalysisDetailReq, opts ...grpc_go.CallOption) (*GetWorkAnalysisDetailResp, common.ErrorWithAttachment)
|
||||
@ -200,6 +201,7 @@ type CastClientImpl struct {
|
||||
UpdateArtist func(ctx context.Context, in *UpdateArtistReq) (*UpdateArtistResp, error)
|
||||
GetArtist func(ctx context.Context, in *GetArtistReq) (*GetArtistResp, error)
|
||||
CreateWorkAnalysis func(ctx context.Context, in *CreateWorkAnalysisReq) (*CreateWorkAnalysisResp, error)
|
||||
ImportWorkAnalysis func(ctx context.Context, in *ImportWorkAnalysisReq) (*ImportWorkAnalysisResp, error)
|
||||
UpdateWorkAnalysis func(ctx context.Context, in *UpdateWorkAnalysisReq) (*emptypb.Empty, error)
|
||||
UpdateWorkAnalysisStatus func(ctx context.Context, in *UpdateWorkAnalysisStatusReq) (*emptypb.Empty, error)
|
||||
GetWorkAnalysis func(ctx context.Context, in *GetWorkAnalysisDetailReq) (*GetWorkAnalysisDetailResp, error)
|
||||
@ -569,6 +571,12 @@ func (c *castClient) CreateWorkAnalysis(ctx context.Context, in *CreateWorkAnaly
|
||||
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/CreateWorkAnalysis", in, out)
|
||||
}
|
||||
|
||||
func (c *castClient) ImportWorkAnalysis(ctx context.Context, in *ImportWorkAnalysisReq, opts ...grpc_go.CallOption) (*ImportWorkAnalysisResp, common.ErrorWithAttachment) {
|
||||
out := new(ImportWorkAnalysisResp)
|
||||
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
|
||||
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/ImportWorkAnalysis", in, out)
|
||||
}
|
||||
|
||||
func (c *castClient) UpdateWorkAnalysis(ctx context.Context, in *UpdateWorkAnalysisReq, opts ...grpc_go.CallOption) (*emptypb.Empty, common.ErrorWithAttachment) {
|
||||
out := new(emptypb.Empty)
|
||||
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
|
||||
@ -887,6 +895,7 @@ type CastServer interface {
|
||||
GetArtist(context.Context, *GetArtistReq) (*GetArtistResp, error)
|
||||
// 作品分析相关接口
|
||||
CreateWorkAnalysis(context.Context, *CreateWorkAnalysisReq) (*CreateWorkAnalysisResp, error)
|
||||
ImportWorkAnalysis(context.Context, *ImportWorkAnalysisReq) (*ImportWorkAnalysisResp, error)
|
||||
UpdateWorkAnalysis(context.Context, *UpdateWorkAnalysisReq) (*emptypb.Empty, error)
|
||||
UpdateWorkAnalysisStatus(context.Context, *UpdateWorkAnalysisStatusReq) (*emptypb.Empty, error)
|
||||
GetWorkAnalysis(context.Context, *GetWorkAnalysisDetailReq) (*GetWorkAnalysisDetailResp, error)
|
||||
@ -1107,6 +1116,9 @@ func (UnimplementedCastServer) GetArtist(context.Context, *GetArtistReq) (*GetAr
|
||||
func (UnimplementedCastServer) CreateWorkAnalysis(context.Context, *CreateWorkAnalysisReq) (*CreateWorkAnalysisResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CreateWorkAnalysis not implemented")
|
||||
}
|
||||
func (UnimplementedCastServer) ImportWorkAnalysis(context.Context, *ImportWorkAnalysisReq) (*ImportWorkAnalysisResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ImportWorkAnalysis not implemented")
|
||||
}
|
||||
func (UnimplementedCastServer) UpdateWorkAnalysis(context.Context, *UpdateWorkAnalysisReq) (*emptypb.Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method UpdateWorkAnalysis not implemented")
|
||||
}
|
||||
@ -2772,6 +2784,35 @@ func _Cast_CreateWorkAnalysis_Handler(srv interface{}, ctx context.Context, dec
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Cast_ImportWorkAnalysis_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ImportWorkAnalysisReq)
|
||||
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("ImportWorkAnalysis", 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_UpdateWorkAnalysis_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UpdateWorkAnalysisReq)
|
||||
if err := dec(in); err != nil {
|
||||
@ -4234,6 +4275,10 @@ var Cast_ServiceDesc = grpc_go.ServiceDesc{
|
||||
MethodName: "CreateWorkAnalysis",
|
||||
Handler: _Cast_CreateWorkAnalysis_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ImportWorkAnalysis",
|
||||
Handler: _Cast_ImportWorkAnalysis_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpdateWorkAnalysis",
|
||||
Handler: _Cast_UpdateWorkAnalysis_Handler,
|
||||
|
||||
@ -33,6 +33,9 @@ func AnalysisRouter(r *gin.RouterGroup) {
|
||||
analysis.POST("update-approval-id", serviceCast.UpdateWorkAnalysisApprovalID) // 更新作品分析审批ID
|
||||
analysis.POST("update-pdf-url", serviceCast.UpdateWorkAnalysisPdfUrl) // 更新作品分析PDF链接
|
||||
|
||||
// 刷数据专用的导入接口
|
||||
analysis.POST("import-batch", serviceCast.ImportWorkAnalysisBatch) // Excel 批量导入数据分析
|
||||
|
||||
analysis.POST("trigger-ayrshare-metrics", serviceCast.TriggerAyrshareMetricsCollector) // 手动触发 Ayrshare 指标采集任务
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package cast
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@ -19,14 +20,18 @@ import (
|
||||
"fonchain-fiee/pkg/service/bundle/common"
|
||||
"fonchain-fiee/pkg/utils"
|
||||
"fonchain-fiee/pkg/utils/stime"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"math/rand"
|
||||
|
||||
"dubbo.apache.org/dubbo-go/v3/common/constant"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/xuri/excelize/v2"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@ -92,6 +97,286 @@ func CreateWorkAnalysisCore(ctx *gin.Context, req *cast.CreateWorkAnalysisReq) (
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// ImportWorkAnalysisBatch 通过 Excel 批量导入数据分析
|
||||
// Excel 列顺序:SubNum | ArtistName | SubmitTime | PeriodTypeFans | PeriodTypeViews | PeriodTypeLikes | PeriodTypeComments | PeriodTypeShares | IsRefreshData(1=false,2=true)
|
||||
func ImportWorkAnalysisBatch(ctx *gin.Context) {
|
||||
excelFile, err := ctx.FormFile("file")
|
||||
if err != nil {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
|
||||
loginInfo := login.GetUserInfoFromC(ctx)
|
||||
lockKey := fmt.Sprintf("import_work_analysis_batch:%d", loginInfo.ID)
|
||||
replay := cache.RedisClient.SetNX(lockKey, time.Now().Format("20060102150405"), 5*time.Minute)
|
||||
if !replay.Val() {
|
||||
service.Error(ctx, errors.New("有导入任务正在进行,请稍后再试"))
|
||||
return
|
||||
}
|
||||
defer cache.RedisClient.Del(lockKey)
|
||||
|
||||
tempDir := "./runtime/report_pdf"
|
||||
_, err = utils.CheckDirPath(tempDir, true)
|
||||
if err != nil {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
|
||||
fileName := fmt.Sprintf("%d_work_analysis.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
|
||||
}
|
||||
|
||||
newCtx := NewCtxWithUserInfo(ctx)
|
||||
|
||||
// 在 J1 写入表头
|
||||
_ = excelData.SetCellValue("Sheet1", "J1", "结果")
|
||||
|
||||
successCount := 0
|
||||
|
||||
for line, row := range rows {
|
||||
if line == 0 {
|
||||
continue // 跳过表头
|
||||
}
|
||||
if len(row) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Excel 行号(1-based),line=1 → 行号 2
|
||||
cellJ := fmt.Sprintf("J%d", line+1)
|
||||
|
||||
// 第一列:SubNum
|
||||
subNum := ""
|
||||
if len(row) > 0 {
|
||||
subNum = utils.CleanString(row[0])
|
||||
}
|
||||
if subNum == "" {
|
||||
_ = excelData.SetCellValue("Sheet1", cellJ, "SubNum 不能为空")
|
||||
continue
|
||||
}
|
||||
|
||||
// 第二列:ArtistName
|
||||
artistName := ""
|
||||
if len(row) > 1 {
|
||||
artistName = utils.CleanString(row[1])
|
||||
}
|
||||
|
||||
// 第三列:SubmitTime
|
||||
submitTime := ""
|
||||
if len(row) > 2 {
|
||||
submitTime = utils.CleanString(row[2])
|
||||
}
|
||||
|
||||
// 第四列:PeriodTypeFans
|
||||
var periodTypeFans uint32
|
||||
if len(row) > 3 && utils.CleanString(row[3]) != "" {
|
||||
v, _ := strconv.ParseUint(utils.CleanString(row[3]), 10, 32)
|
||||
periodTypeFans = uint32(v)
|
||||
}
|
||||
|
||||
// 第五列:PeriodTypeViews
|
||||
var periodTypeViews uint32
|
||||
if len(row) > 4 && utils.CleanString(row[4]) != "" {
|
||||
v, _ := strconv.ParseUint(utils.CleanString(row[4]), 10, 32)
|
||||
periodTypeViews = uint32(v)
|
||||
}
|
||||
|
||||
// 第六列:PeriodTypeLikes
|
||||
var periodTypeLikes uint32
|
||||
if len(row) > 5 && utils.CleanString(row[5]) != "" {
|
||||
v, _ := strconv.ParseUint(utils.CleanString(row[5]), 10, 32)
|
||||
periodTypeLikes = uint32(v)
|
||||
}
|
||||
|
||||
// 第七列:PeriodTypeComments
|
||||
var periodTypeComments uint32
|
||||
if len(row) > 6 && utils.CleanString(row[6]) != "" {
|
||||
v, _ := strconv.ParseUint(utils.CleanString(row[6]), 10, 32)
|
||||
periodTypeComments = uint32(v)
|
||||
}
|
||||
|
||||
// 第八列:PeriodTypeShares
|
||||
var periodTypeShares uint32
|
||||
if len(row) > 7 && utils.CleanString(row[7]) != "" {
|
||||
v, _ := strconv.ParseUint(utils.CleanString(row[7]), 10, 32)
|
||||
periodTypeShares = uint32(v)
|
||||
}
|
||||
|
||||
// 第九列:IsRefreshData(1 → false, 2 → true)
|
||||
isRefreshData := false
|
||||
if len(row) > 8 && utils.CleanString(row[8]) == "2" {
|
||||
isRefreshData = true
|
||||
}
|
||||
|
||||
// 根据 subNum 查询艺人信息
|
||||
subInfoResp, err := service.AccountFieeProvider.SubNumGetInfo(context.Background(), &accountFiee.SubNumGetInfoRequest{
|
||||
SubNum: subNum,
|
||||
Domain: "app",
|
||||
})
|
||||
if err != nil {
|
||||
zap.L().Error("ImportWorkAnalysisBatch SubNumGetInfo", zap.Error(err), zap.String("subNum", subNum))
|
||||
_ = excelData.SetCellValue("Sheet1", cellJ, fmt.Sprintf("自媒体用户查询失败:%s", err.Error()))
|
||||
continue
|
||||
}
|
||||
if subInfoResp == nil || subInfoResp.Id == 0 {
|
||||
_ = excelData.SetCellValue("Sheet1", cellJ, "自媒体用户不存在")
|
||||
continue
|
||||
}
|
||||
|
||||
artistID := uint64(subInfoResp.Id)
|
||||
|
||||
// 查询艺人套餐订单
|
||||
balanceResp, err := service.BundleProvider.GetBundleBalanceByUserId(context.Background(), &bundle.GetBundleBalanceByUserIdReq{
|
||||
UserId: int32(artistID),
|
||||
})
|
||||
if err != nil {
|
||||
zap.L().Error("ImportWorkAnalysisBatch GetBundleBalanceByUserId", zap.Error(err), zap.Uint64("artistID", artistID))
|
||||
_ = excelData.SetCellValue("Sheet1", cellJ, fmt.Sprintf("获取套餐订单失败:%s", err.Error()))
|
||||
continue
|
||||
}
|
||||
if balanceResp.OrderUUID == "" {
|
||||
_ = excelData.SetCellValue("Sheet1", cellJ, "订单不存在")
|
||||
continue
|
||||
}
|
||||
|
||||
// 若 artistName 为空则使用账号服务中的姓名
|
||||
if artistName == "" {
|
||||
artistName = subInfoResp.Name
|
||||
}
|
||||
|
||||
// 将 submitTime(YYYY-MM-DD 00:00:00)加随机 9~15 小时,使提交时间落在 09:00~15:00 之间
|
||||
if submitTime != "" {
|
||||
if parsedTime, parseErr := time.Parse("2006-01-02 15:04:05", submitTime); parseErr == nil {
|
||||
randomHours := time.Duration(rand.Intn(7)+9) * time.Hour
|
||||
submitTime = parsedTime.Add(randomHours).Format("2006-01-02 15:04:05")
|
||||
}
|
||||
}
|
||||
|
||||
// 将 submitTime 解析为 YYYYMMDD 格式的 int32,作为 ArtistMetricsSeries 的 date
|
||||
var dateInt int32
|
||||
if submitTime != "" {
|
||||
if parsedTime, parseErr := time.Parse("2006-01-02 15:04:05", submitTime); parseErr == nil {
|
||||
dateInt = int32(parsedTime.Year()*10000 + int(parsedTime.Month())*100 + parsedTime.Day())
|
||||
}
|
||||
}
|
||||
|
||||
// 提升到外部作用域,供 importReq 使用
|
||||
var aiAnalysis string
|
||||
var mediaAccountCount int32
|
||||
var workVideoCount, workImageCount int32
|
||||
|
||||
// 调用 ArtistMetricsSeries 获取艺人指标数据并写入 Analysis
|
||||
metricsReq := &cast.ArtistMetricsSeriesReq{
|
||||
ArtistUUID: fmt.Sprint(subInfoResp.Id),
|
||||
Date: dateInt,
|
||||
PeriodTypeFans: periodTypeFans,
|
||||
PeriodTypeViews: periodTypeViews,
|
||||
PeriodTypeLikes: periodTypeLikes,
|
||||
PeriodTypeComments: periodTypeComments,
|
||||
PeriodTypeShares: periodTypeShares,
|
||||
}
|
||||
|
||||
// 并行调用 ArtistMetricsSeries 和 GetArtistWorkStats
|
||||
var metricsResp *cast.ArtistMetricsSeriesResp
|
||||
var workStatsResp *cast.GetArtistWorkStatsResp
|
||||
var metricsErr, workStatsErr error
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
metricsResp, metricsErr = service.CastProvider.ArtistMetricsSeries(context.Background(), metricsReq)
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
workStatsResp, workStatsErr = service.CastProvider.GetArtistWorkStats(newCtx, &cast.GetArtistWorkStatsReq{
|
||||
ArtistUuid: fmt.Sprint(subInfoResp.Id),
|
||||
StatusUpdateTime: submitTime,
|
||||
})
|
||||
}()
|
||||
wg.Wait()
|
||||
|
||||
if workStatsErr == nil && workStatsResp != nil {
|
||||
mediaAccountCount = int32(workStatsResp.AccountCount)
|
||||
workVideoCount = int32(workStatsResp.VideoCount)
|
||||
workImageCount = int32(workStatsResp.ImageCount)
|
||||
} else if workStatsErr != nil {
|
||||
zap.L().Warn("ImportWorkAnalysisBatch GetArtistWorkStats failed", zap.Error(workStatsErr), zap.String("subNum", subNum))
|
||||
}
|
||||
|
||||
if metricsErr != nil {
|
||||
zap.L().Warn("ImportWorkAnalysisBatch ArtistMetricsSeries failed", zap.Error(metricsErr), zap.String("subNum", subNum))
|
||||
} else if metricsResp != nil {
|
||||
// 构建与 ArtistMetricsSeries HTTP 接口相同的 respMap
|
||||
raw, _ := json.Marshal(metricsResp)
|
||||
respMap := make(map[string]interface{})
|
||||
_ = json.Unmarshal(raw, &respMap)
|
||||
respMap["accountConsumptionNumber"] = mediaAccountCount
|
||||
respMap["videoCount"] = workVideoCount
|
||||
respMap["imageCount"] = workImageCount
|
||||
|
||||
// 调用 AI 生成分析文本
|
||||
aiAnalysis, _ = generateArtistMetricsAnalysis(metricsResp)
|
||||
if aiAnalysis == "" {
|
||||
zap.L().Warn("ImportWorkAnalysisBatch generateArtistMetricsAnalysis returned empty", zap.String("subNum", subNum))
|
||||
}
|
||||
respMap["analysis"] = aiAnalysis
|
||||
}
|
||||
|
||||
importReq := &cast.ImportWorkAnalysisReq{
|
||||
SubNum: subNum,
|
||||
ArtistID: fmt.Sprint(subInfoResp.Id),
|
||||
ArtistName: artistName,
|
||||
ArtistPhone: subInfoResp.TelNum,
|
||||
BundleOrderUuid: balanceResp.OrderUUID,
|
||||
SubmitTime: submitTime,
|
||||
PeriodTypeFans: periodTypeFans,
|
||||
PeriodTypeViews: periodTypeViews,
|
||||
PeriodTypeLikes: periodTypeLikes,
|
||||
PeriodTypeComments: periodTypeComments,
|
||||
PeriodTypeShares: periodTypeShares,
|
||||
IsRefreshData: isRefreshData,
|
||||
Analysis: aiAnalysis,
|
||||
MediaAccountCount: mediaAccountCount,
|
||||
WorkVideoCount: workVideoCount,
|
||||
WorkImageCount: workImageCount,
|
||||
}
|
||||
|
||||
importResp, err := service.CastProvider.ImportWorkAnalysis(newCtx, importReq)
|
||||
if err != nil {
|
||||
zap.L().Error("ImportWorkAnalysisBatch ImportWorkAnalysis", zap.Error(err), zap.String("subNum", subNum))
|
||||
_ = excelData.SetCellValue("Sheet1", cellJ, fmt.Sprintf("导入失败:%s", err.Error()))
|
||||
continue
|
||||
}
|
||||
|
||||
// 导入成功,将返回的 UUID 写入 J 列
|
||||
_ = excelData.SetCellValue("Sheet1", cellJ, importResp.Uuid)
|
||||
successCount++
|
||||
}
|
||||
|
||||
// 将修改后的 Excel 写入 buffer 并返回给客户端下载
|
||||
buf, err := excelData.WriteToBuffer()
|
||||
if err != nil {
|
||||
service.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
utils.ResponseXls(ctx, bytes.NewReader(buf.Bytes()), fmt.Sprintf("数据分析导入结果_%d成功", successCount))
|
||||
}
|
||||
|
||||
// UpdateWorkAnalysis 更新作品分析
|
||||
func UpdateWorkAnalysis(ctx *gin.Context) {
|
||||
var req *cast.UpdateWorkAnalysisReq
|
||||
|
||||
Loading…
Reference in New Issue
Block a user