341 lines
10 KiB
Go
341 lines
10 KiB
Go
|
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
|
||
|
}
|