Compare commits
8 Commits
main
...
feat-cjy-u
| Author | SHA1 | Date | |
|---|---|---|---|
| fe25f1dc3e | |||
| ef823bcec3 | |||
| bbfa8d3c13 | |||
| 7eb3dffd38 | |||
| 7ed0249410 | |||
| 369326ee3a | |||
| 46ec603b73 | |||
| 23b864a410 |
@ -49,6 +49,7 @@ func AnalysisRouter(r *gin.RouterGroup) {
|
|||||||
competitiveReport.POST("count-by-work-uuids", serviceCast.CountCompetitiveReportByWorkUuids) // 根据作品UUID统计竞品报告数量
|
competitiveReport.POST("count-by-work-uuids", serviceCast.CountCompetitiveReportByWorkUuids) // 根据作品UUID统计竞品报告数量
|
||||||
competitiveReport.POST("export-list", serviceCast.ListCompetitiveReportExport) // 竞品报告列表导出
|
competitiveReport.POST("export-list", serviceCast.ListCompetitiveReportExport) // 竞品报告列表导出
|
||||||
competitiveReport.POST("export-single-list", serviceCast.ListCompetitiveReportSingleExport) // 竞品报告单个列表导出
|
competitiveReport.POST("export-single-list", serviceCast.ListCompetitiveReportSingleExport) // 竞品报告单个列表导出
|
||||||
|
competitiveReport.POST("import-pdf-batch", serviceCast.ImportPdfBatch) // 批量导入PDF(下载、重命名、上传)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 员工任务相关路由(需要App登录验证
|
// 员工任务相关路由(需要App登录验证
|
||||||
|
|||||||
@ -19,11 +19,11 @@ import (
|
|||||||
"fonchain-fiee/pkg/utils/stime"
|
"fonchain-fiee/pkg/utils/stime"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"unicode/utf8"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|
||||||
@ -1354,6 +1354,141 @@ func generateReportFileName(title, artistName string) string {
|
|||||||
return fmt.Sprintf("%s%s老师的竞品报告%d", today, artistName, timestamp)
|
return fmt.Sprintf("%s%s老师的竞品报告%d", today, artistName, timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ImportPdfBatch 批量导入 PDF(下载、重命名、上传)
|
||||||
|
func ImportPdfBatch(ctx *gin.Context) {
|
||||||
|
// 获取上传的Excel文件
|
||||||
|
excelFile, err := ctx.FormFile("file")
|
||||||
|
if err != nil {
|
||||||
|
service.Error(ctx, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
loginInfo := login.GetUserInfoFromC(ctx)
|
||||||
|
lockKey := fmt.Sprintf("import_pdf_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/pdf_import"
|
||||||
|
_, err = utils.CheckDirPath(tempDir, true)
|
||||||
|
if err != nil {
|
||||||
|
service.Error(ctx, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成文件名并保存文件
|
||||||
|
fileName := fmt.Sprintf("%d_pdf_import.xlsx", time.Now().UnixMicro())
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成结果文件URL
|
||||||
|
urlHost := config.AppConfig.System.FieeHost
|
||||||
|
urlResult := fmt.Sprintf("%s/api/fiee/static/pdf_import/%s", urlHost, fileName)
|
||||||
|
|
||||||
|
// 确保临时目录存在
|
||||||
|
_, err = utils.CheckDirPath("./runtime/pdf_import/", true)
|
||||||
|
if err != nil {
|
||||||
|
service.Error(ctx, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录处理结果
|
||||||
|
successCount := 0
|
||||||
|
failCount := 0
|
||||||
|
|
||||||
|
for line, row := range rows {
|
||||||
|
// 跳过表头
|
||||||
|
if line == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// 跳过空行
|
||||||
|
if len(row) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// A列:PDF URL
|
||||||
|
pdfUrl := utils.CleanString(row[0])
|
||||||
|
// B列:新文件名
|
||||||
|
newFileName := utils.CleanString(row[1])
|
||||||
|
|
||||||
|
// 验证必填字段
|
||||||
|
if pdfUrl == "" {
|
||||||
|
excelData.SetCellValue("Sheet1", fmt.Sprintf("D%d", line+1), "PDF URL不能为空")
|
||||||
|
failCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if newFileName == "" {
|
||||||
|
excelData.SetCellValue("Sheet1", fmt.Sprintf("D%d", line+1), "新文件名不能为空")
|
||||||
|
failCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载 PDF(保存到 runtime 目录,加入时间戳避免文件名冲突,上传后 URL 不包含 pdf_import 路径)
|
||||||
|
timestamp := time.Now().UnixMicro()
|
||||||
|
fullPath, err := utils.SaveUrlFileDisk(pdfUrl, "runtime", fmt.Sprintf("%s_%d.pdf", newFileName, timestamp))
|
||||||
|
if err != nil {
|
||||||
|
zap.L().Error("下载PDF失败", zap.String("pdfUrl", pdfUrl), zap.Error(err))
|
||||||
|
excelData.SetCellValue("Sheet1", fmt.Sprintf("D%d", line+1), fmt.Sprintf("下载PDF失败: %s", err.Error()))
|
||||||
|
failCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传到 OSS
|
||||||
|
uploadUrl, uploadErr := upload.PutBos(fullPath, upload.PdfType, true)
|
||||||
|
if uploadErr != nil {
|
||||||
|
zap.L().Error("上传PDF失败", zap.Error(uploadErr))
|
||||||
|
excelData.SetCellValue("Sheet1", fmt.Sprintf("D%d", line+1), fmt.Sprintf("上传PDF失败: %s", uploadErr.Error()))
|
||||||
|
failCount++
|
||||||
|
// 清理临时文件
|
||||||
|
if _, err := os.Stat(fullPath); err == nil {
|
||||||
|
os.Remove(fullPath)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入新URL到C列
|
||||||
|
excelData.SetCellValue("Sheet1", fmt.Sprintf("C%d", line+1), uploadUrl)
|
||||||
|
successCount++
|
||||||
|
zap.L().Info("PDF处理成功", zap.String("pdfUrl", pdfUrl), zap.String("newUrl", uploadUrl))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存结果文件
|
||||||
|
resultPath := fmt.Sprintf("./runtime/pdf_import/%s", fileName)
|
||||||
|
if err = excelData.SaveAs(resultPath); err != nil {
|
||||||
|
service.Error(ctx, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回结果
|
||||||
|
service.Success(ctx, map[string]interface{}{
|
||||||
|
"successCount": successCount,
|
||||||
|
"failCount": failCount,
|
||||||
|
"resultUrl": urlResult,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// truncateCompetitorReportData 截断竞品报告数据中超长的字段
|
// truncateCompetitorReportData 截断竞品报告数据中超长的字段
|
||||||
// 字段长度要求参考 AI 生成竞品报告的限制
|
// 字段长度要求参考 AI 生成竞品报告的限制
|
||||||
func truncateCompetitorReportData(data utils.CompetitorReportData) utils.CompetitorReportData {
|
func truncateCompetitorReportData(data utils.CompetitorReportData) utils.CompetitorReportData {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user