package imports import ( "context" "fmt" "fonchain-fiee/api/accountFiee" apiCast "fonchain-fiee/api/cast" "fonchain-fiee/api/files" "fonchain-fiee/pkg/config" "fonchain-fiee/pkg/model" modelCast "fonchain-fiee/pkg/model/cast" "fonchain-fiee/pkg/service" "fonchain-fiee/pkg/service/cast" "fonchain-fiee/pkg/service/upload" "log" "os" "path/filepath" "strconv" "strings" "time" "github.com/gin-gonic/gin" "github.com/mholt/archiver" "github.com/xuri/excelize/v2" ) func ImportPublish(c *gin.Context) { // 1. 上传画家短视频详情文件 excelFile, err := c.FormFile("excel") if err != nil { c.JSON(400, gin.H{"error": "缺少 Excel 文件 excel"}) return } zipFile, err := c.FormFile("zip") if err != nil { c.JSON(400, gin.H{"error": "缺少 ZIP 文件"}) return } // 2. 保存临时文件 tempDir := "tmp" os.MkdirAll(tempDir, 0755) excelPath := filepath.Join(tempDir, "artists.xlsx") zipPath := filepath.Join(tempDir, "archive.zip") if err = c.SaveUploadedFile(excelFile, excelPath); err != nil { c.JSON(500, gin.H{"error": "保存 Excel 失败"}) return } if err = c.SaveUploadedFile(zipFile, zipPath); err != nil { c.JSON(500, gin.H{"error": "保存 ZIP 文件失败"}) return } // 3. 解压 ZIP unzipPath := filepath.Join(tempDir, "unzipped") if _, err = os.Stat(unzipPath); err == nil { // 路径已存在,删除 if removeErr := os.RemoveAll(unzipPath); removeErr != nil { c.JSON(500, gin.H{"error": "清理已存在解压目录失败: " + removeErr.Error()}) return } } os.MkdirAll(unzipPath, 0755) if err = archiver.Unarchive(zipPath, unzipPath); err != nil { c.JSON(500, gin.H{"error": "解压 ZIP 失败: " + err.Error()}) return } entries, err := os.ReadDir(unzipPath) if err != nil || len(entries) == 0 { c.JSON(500, gin.H{"error": "读取解压目录失败或目录为空"}) return } if len(entries) == 1 && entries[0].IsDir() { // 说明解压后多了一层目录,把它设为新的 unzipPath unzipPath = filepath.Join(unzipPath, entries[0].Name()) } defer os.RemoveAll(tempDir) // 4. 读取 Excel 画家名单, 匹配视频和图片 artists, err := readArtistVideoInfo(excelPath, unzipPath) if err != nil { c.JSON(500, gin.H{"error": "读取 Excel 失败"}) return } // 5.发布视频 var failedRecords []failedRecord for _, artist := range artists { var infoResp *accountFiee.UserInfoResponse var err error list, err := service.AccountFieeProvider.UserList(context.Background(), &accountFiee.UserListRequest{ Name: artist.Name, }) if err != nil { failedRecords = append(failedRecords, failedRecord{ name: artist.Name, msg: fmt.Sprintf("获取用户信息失败: %s", err.Error()), }) log.Printf(fmt.Sprintf("获取用户信息失败: %s", err.Error())) continue } if list != nil && len(list.UserList) > 0 { infoResp, err = service.AccountFieeProvider.Info(context.Background(), &accountFiee.InfoRequest{ ID: list.UserList[0].Id, Domain: "app", }) if err != nil { failedRecords = append(failedRecords, failedRecord{ name: artist.Name, msg: fmt.Sprintf("获取用户信息失败: %s", err.Error()), }) log.Printf(fmt.Sprintf("获取用户信息失败: %s", err.Error())) continue } } if err = cast.CheckUserBundleBalance(int32(list.UserList[0].Id), modelCast.BalanceTypeAccountValue); err != nil { failedRecords = append(failedRecords, failedRecord{ name: artist.Name, msg: fmt.Sprintf("检查用户账户数量: %s", err.Error()), }) log.Printf(fmt.Sprintf("检查用户账户数量: %s", err.Error())) continue } //自媒体账号 accountList, err := service.CastProvider.MediaUserList(c, &apiCast.MediaUserListReq{ ArtistUuid: strconv.FormatUint(list.UserList[0].Id, 10), }) if err != nil || accountList == nil { failedRecords = append(failedRecords, failedRecord{ name: artist.Name, msg: fmt.Sprintf("自媒体账号数量获取失败: %s,账号数量:%d", err.Error(), len(accountList.Data)), }) log.Printf(fmt.Sprintf("自媒体账号数量获取失败: %s,账号数量:%d", err.Error(), len(accountList.Data))) continue } mediaAccountUuids := []string{} mediaAccountNames := []string{} platformIDs := []apiCast.PlatformIDENUM{} for _, info := range accountList.Data { mediaAccountUuids = append(mediaAccountUuids, info.MediaAccountUuid) mediaAccountNames = append(mediaAccountNames, info.PlatformUserName) platformIDs = append(platformIDs, apiCast.PlatformIDENUM(info.PlatformID)) } newCtx := cast.NewCtxWithUserInfo(c) _, err = service.CastProvider.UpdateWorkVideo(newCtx, &apiCast.UpdateWorkVideoReq{ Title: artist.Title, Content: artist.Title, VideoUrl: artist.Video, CoverUrl: artist.Img, MediaAccountUuids: mediaAccountUuids, MediaAccountNames: mediaAccountNames, PlatformIDs: platformIDs, PublishConfig1: &apiCast.PublishConfig{ CanComment: 1, CanJoin: 1, CanQuote: 1, ForbidComment: 2, IsAI: 1, PublicType: 1, }, PublishConfig2: &apiCast.PublishConfig{ CanComment: 1, CanJoin: 1, CanQuote: 1, ForbidComment: 2, IsAI: 1, PublicType: 1, }, PublishConfig3: &apiCast.PublishConfig{ CanComment: 1, CanJoin: 1, CanQuote: 1, ForbidComment: 1, IsAI: 1, PublicType: 1, }, Action: "submit", ArtistUuid: strconv.FormatUint(list.UserList[0].Id, 10), ArtistName: infoResp.Name, ArtistPhone: infoResp.TelNum, ArtistPhoneAreaCode: infoResp.TelAreaCode, Source: 2, }) if err != nil { failedRecords = append(failedRecords, failedRecord{ name: artist.Name, msg: fmt.Sprintf("发布"+artist.Name+"视频"+artist.Title+"失败: %s", err.Error()), }) log.Printf(fmt.Sprintf("发布"+artist.Name+"视频"+artist.Title+"失败: %s", err.Error())) continue } } // 6. 返回结果 service.Success(c, failedRecords) } func readArtistVideoInfo(excelPath, unzipPath string) ([]ArtistMedia, error) { log.Println(unzipPath) f, err := excelize.OpenFile(excelPath) if err != nil { return nil, err } defer f.Close() sheetName := f.GetSheetName(0) rows, err := f.GetRows(sheetName) if err != nil { return nil, err } log.Println("start read excel") var artists []ArtistMedia for i, row := range rows { if i == 0 || i == 1 || len(row) < 2 { continue } if i == 165 { break } id, _ := f.GetCellValue(sheetName, fmt.Sprintf("A%d", i+1)) if id != "" { id = strings.TrimSpace(id) } artistName, _ := f.GetCellValue(sheetName, fmt.Sprintf("B%d", i+1)) if artistName != "" { artistName = strings.TrimSpace(artistName) } title, _ := f.GetCellValue(sheetName, fmt.Sprintf("C%d", i+1)) if title != "" { title = strings.TrimSpace(title) } artists = append(artists, ArtistMedia{ Id: id, Name: artistName, Title: title, }) } artists, err = matchArtistMedia(artists, unzipPath) return artists, nil } func matchArtistMedia(artists []ArtistMedia, unzipPath string) ([]ArtistMedia, error) { var err error var res []ArtistMedia for _, artist := range artists { oldImgPath := fmt.Sprintf("%s/%s/%s.jpg", unzipPath, artist.Name, artist.Id) oldVideoPath := fmt.Sprintf("%s/%s/%s.mp4", unzipPath, artist.Name, artist.Id) // 检查源文件是否存在 if _, err = os.Stat(oldImgPath); os.IsNotExist(err) { continue } if _, err = os.Stat(oldVideoPath); os.IsNotExist(err) { continue } baseDir := filepath.Join(unzipPath, artist.Name) if err = os.MkdirAll(baseDir, 0755); err != nil { log.Println("创建目录失败:", err) return nil, err } log.Println("创建目录成功:", baseDir) // 重命名 now := time.Now().Unix() imgPath := fmt.Sprintf("%s/%s/%s_%d.jpg", unzipPath, artist.Name, artist.Id, now) videoPath := fmt.Sprintf("%s/%s/%s_%d.mp4", unzipPath, artist.Name, artist.Id, now) if err = os.Rename(oldImgPath, imgPath); err != nil { log.Println("图片:"+oldImgPath+"重命名失败:", err) return nil, err } if err = os.Rename(oldVideoPath, videoPath); err != nil { log.Println("视频:"+oldVideoPath+"重命名失败:", err) return nil, err } //转为url content, err := os.ReadFile(videoPath) if err != nil { return nil, err } if err = UploadToAnotherService(context.Background(), content, filepath.Base(videoPath)); err != nil { log.Println("上传视频失败:", err) return nil, err } var httpType string if config.IsHttps { url := "saas.fiee.com" httpType = fmt.Sprintf("%s%s", model.HttpsType, url) } else { url := "114.218.158.24:9020" httpType = fmt.Sprintf("%s%s", model.HttpType, url) } baseUrl := fmt.Sprintf("%s/api/fiee/resource/raw/", httpType) videoUrl := baseUrl + filepath.Base(videoPath) imgUrl, err := upload.PutBos(filepath.ToSlash(imgPath), "image", false) if err != nil { log.Println("上传图片失败:", err) return nil, err } tmp := artist tmp.Id = artist.Id tmp.Name = artist.Name tmp.Title = artist.Title tmp.Img = imgUrl //tmp.Video = filepath.ToSlash(videoPath) tmp.Video = videoUrl res = append(res, tmp) } return res, nil } func UploadToAnotherService(ctx context.Context, fileData []byte, path string) error { const chunkSize = 4*1024*1024 - 100 _, err := service.FilesProvider.TusCreate(ctx, &files.TusCreateReq{ Path: path, UserSpacePath: "", Override: true, }) if err != nil { return err } log.Println("create success ......**********") offset := int64(0) totalSize := int64(len(fileData)) for offset < totalSize { end := offset + chunkSize if end > totalSize { end = totalSize } chunk := fileData[offset:end] _, err = service.FilesProvider.TusUpload(ctx, &files.TusUploadReq{ Path: path, UploadOffset: offset, Content: chunk, UserSpacePath: "", }) if err != nil { return fmt.Errorf("上传 offset=%d chunk 失败: %w", offset, err) } log.Printf("upload chunk: %d - %d success\n", offset, end) offset = end } return nil }