update 修复文件问题
All checks were successful
Build image / build (push) Successful in 1m24s

This commit is contained in:
timerzz 2025-03-30 21:20:13 +08:00
parent a3f7314ece
commit 00e4f26d71
5 changed files with 49 additions and 74 deletions

View File

@ -15,19 +15,17 @@ import (
"github.com/gofiber/fiber/v3/middleware/cors" "github.com/gofiber/fiber/v3/middleware/cors"
"github.com/gofiber/fiber/v3/middleware/recover" "github.com/gofiber/fiber/v3/middleware/recover"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/golang/glog"
) )
func main() { func main() {
flag.Parse() flag.Parse()
glog.Info(">>> BEGIN INIT<<<") logrus.Info(">>> BEGIN INIT<<<")
ctx, cancel := signal.NotifyContext(context.Background(), os.Kill, os.Interrupt) ctx, cancel := signal.NotifyContext(context.Background(), os.Kill, os.Interrupt)
defer cancel() defer cancel()
// 初始化数据库 // 初始化数据库
db, err := database.InitDefaultDatabase() db, err := database.InitDefaultDatabase()
if err != nil { if err != nil {
glog.Fatalf("init database failed: %v", err) logrus.Fatalf("init database failed: %v", err)
} }
client, err := dw_sdk.InitDefaultDWClient() client, err := dw_sdk.InitDefaultDWClient()
@ -59,6 +57,6 @@ func main() {
EnablePrintRoutes: true, EnablePrintRoutes: true,
GracefulContext: ctx, GracefulContext: ctx,
}); err != nil { }); err != nil {
glog.Warningf("service over: %v", err) logrus.Warningf("service over: %v", err)
} }
} }

2
go.mod
View File

@ -8,7 +8,6 @@ require (
gitea.timerzz.com/kedaya_haitao/common v0.0.0-20250329125718-37b1ee0b6a4b gitea.timerzz.com/kedaya_haitao/common v0.0.0-20250329125718-37b1ee0b6a4b
gitea.timerzz.com/kedaya_haitao/dw-sdk v0.0.0-20240904075121-552ef11ea87c gitea.timerzz.com/kedaya_haitao/dw-sdk v0.0.0-20240904075121-552ef11ea87c
github.com/gofiber/fiber/v3 v3.0.0-beta.4 github.com/gofiber/fiber/v3 v3.0.0-beta.4
github.com/golang/glog v1.2.2
github.com/samber/lo v1.49.1 github.com/samber/lo v1.49.1
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/xuri/excelize/v2 v2.9.0 github.com/xuri/excelize/v2 v2.9.0
@ -50,6 +49,7 @@ require (
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // indirect github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // indirect
golang.org/x/arch v0.9.0 // indirect golang.org/x/arch v0.9.0 // indirect
golang.org/x/crypto v0.33.0 // indirect golang.org/x/crypto v0.33.0 // indirect
golang.org/x/image v0.25.0 // indirect
golang.org/x/net v0.35.0 // indirect golang.org/x/net v0.35.0 // indirect
golang.org/x/sys v0.30.0 // indirect golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.23.0 // indirect golang.org/x/text v0.23.0 // indirect

6
go.sum
View File

@ -25,8 +25,6 @@ github.com/gofiber/schema v1.2.0 h1:j+ZRrNnUa/0ZuWrn/6kAtAufEr4jCJ+JuTURAMxNSZg=
github.com/gofiber/schema v1.2.0/go.mod h1:YYwj01w3hVfaNjhtJzaqetymL56VW642YS3qZPhuE6c= github.com/gofiber/schema v1.2.0/go.mod h1:YYwj01w3hVfaNjhtJzaqetymL56VW642YS3qZPhuE6c=
github.com/gofiber/utils/v2 v2.0.0-beta.7 h1:NnHFrRHvhrufPABdWajcKZejz9HnCWmT/asoxRsiEbQ= github.com/gofiber/utils/v2 v2.0.0-beta.7 h1:NnHFrRHvhrufPABdWajcKZejz9HnCWmT/asoxRsiEbQ=
github.com/gofiber/utils/v2 v2.0.0-beta.7/go.mod h1:J/M03s+HMdZdvhAeyh76xT72IfVqBzuz/OJkrMa7cwU= github.com/gofiber/utils/v2 v2.0.0-beta.7/go.mod h1:J/M03s+HMdZdvhAeyh76xT72IfVqBzuz/OJkrMa7cwU=
github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY=
github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@ -112,8 +110,8 @@ golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k=
golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

@ -57,16 +57,15 @@ func (s *Tools) CoachOutletExcel(c fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, "导出工具未初始化") return fiber.NewError(fiber.StatusInternalServerError, "导出工具未初始化")
} }
// 设置文件名和响应头
fileName := fmt.Sprintf("coach_outlet_%s.xlsx", providerId)
c.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, fileName))
c.Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
// 直接将Excel数据写入响应 // 直接将Excel数据写入响应
if err := s.export.ExportCheapProduct(providerId, c); err != nil { fileName, err := s.export.ExportCheapProduct(providerId)
if err != nil {
logrus.Errorf("导出Coach Outlet商品数据失败: %v", err) logrus.Errorf("导出Coach Outlet商品数据失败: %v", err)
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("导出失败: %v", err)) return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("导出失败: %v", err))
} }
// 设置文件名和响应头
c.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, fileName))
c.Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
return nil return c.SendFile(fileName)
} }

View File

@ -5,14 +5,12 @@ import (
"io" "io"
"net/http" "net/http"
"strconv" "strconv"
"strings"
"time" "time"
"gitea.timerzz.com/kedaya_haitao/common/structs/storage" "gitea.timerzz.com/kedaya_haitao/common/structs/storage"
"gitea.timerzz.com/kedaya_haitao/common/structs/utils" "gitea.timerzz.com/kedaya_haitao/common/structs/utils"
v2 "gitea.timerzz.com/kedaya_haitao/common/structs/v2" v2 "gitea.timerzz.com/kedaya_haitao/common/structs/v2"
dw_sdk "gitea.timerzz.com/kedaya_haitao/dw-sdk" dw_sdk "gitea.timerzz.com/kedaya_haitao/dw-sdk"
"github.com/gofiber/fiber/v3"
"github.com/samber/lo" "github.com/samber/lo"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/xuri/excelize/v2" "github.com/xuri/excelize/v2"
@ -37,19 +35,23 @@ func NewExporter(storage *storage.Storage, dw *dw_sdk.Client) (e *Exporter, err
} }
// ExportCheapProduct 导出价格较低的商品数据到Excel直接写入到HTTP响应 // ExportCheapProduct 导出价格较低的商品数据到Excel直接写入到HTTP响应
func (e *Exporter) ExportCheapProduct(providerId string, c fiber.Ctx) error { func (e *Exporter) ExportCheapProduct(providerId string) (string, error) {
// 创建Excel文件 // 创建Excel文件
f := excelize.NewFile() f := excelize.NewFile()
defer f.Close() defer func() {
if err := f.Close(); err != nil {
fmt.Println(err)
}
}()
// 创建工作表 // 创建工作表
sheetName := providerId sheetName := providerId
_, err := f.NewSheet(sheetName) _, err := f.NewSheet(sheetName)
if err != nil { if err != nil {
return fmt.Errorf("创建工作表失败: %w", err) return "", fmt.Errorf("创建工作表失败: %w", err)
} }
_ = f.DeleteSheet("Sheet1") // 删除默认的Sheet1 _ = f.DeleteSheet("Sheet1") // 删除默认的Sheet1
// 确定查询的供应商ID // 确定查询的供应商ID
var cheapId, otherId string var cheapId, otherId string
cheapId = providerId cheapId = providerId
@ -70,47 +72,47 @@ func (e *Exporter) ExportCheapProduct(providerId string, c fiber.Ctx) error {
Where("pa1.cost->>'finalPrice' <= pa2.cost->>'finalPrice'"). Where("pa1.cost->>'finalPrice' <= pa2.cost->>'finalPrice'").
FindInBatches(&articles, 20, func(tx *gorm.DB, batch int) error { FindInBatches(&articles, 20, func(tx *gorm.DB, batch int) error {
for _, article := range articles { for _, article := range articles {
// 更新DW价格如果需要 pArticle, _ := lo.Find(article.Providers, func(p v2.ProviderArticle) bool {
dwSeller, idx, exist := lo.FindIndexOf(article.Sellers, func(seller v2.SellerArticle) bool { return p.ProviderId == v2.ProviderId(cheapId)
return seller.SellerId == "dw-normal"
}) })
if exist && dwSeller.Sell.CreatedAt.Before(time.Now().AddDate(0, 0, -1)) { if pArticle.Cost.OriginalPrice != 0 {
// 如果已经是一天前的数据,那么就要更新下 // 更新DW价格如果需要
dwSeller = e.pullDwPrice(dwSeller) dwSeller, idx, exist := lo.FindIndexOf(article.Sellers, func(seller v2.SellerArticle) bool {
article.Sellers[idx] = dwSeller return seller.SellerId == "dw-normal"
} })
if exist && dwSeller.Sell.CreatedAt.Before(time.Now().AddDate(0, 0, -1)) {
// 如果已经是一天前的数据,那么就要更新下
dwSeller = e.pullDwPrice(dwSeller)
article.Sellers[idx] = dwSeller
}
// 计算利润率 // 计算利润率
utils.ProfitRate(article) utils.ProfitRate(article)
// 添加到Excel // 添加到Excel
rowIndex++ rowIndex++
if err = addRow(f, sheetName, rowIndex, article); err != nil { if err = addRow(f, sheetName, rowIndex, article); err != nil {
logrus.Errorf("添加行失败: %v", err) logrus.Errorf("添加行失败: %v", err)
continue continue
}
} }
} }
return nil return nil
}).Error }).Error
if err != nil { if err != nil {
return fmt.Errorf("查询数据失败: %w", err) return "", fmt.Errorf("查询数据失败: %w", err)
} }
// 保存Excel文件 // 保存Excel文件
filename := fmt.Sprintf("coach_outlet_%s.xlsx", providerId) filename := fmt.Sprintf("coach_outlet_%s.xlsx", providerId)
if err = f.SaveAs(filename); err != nil { if err = f.SaveAs(filename); err != nil {
return fmt.Errorf("保存Excel文件失败: %w", err) return "", fmt.Errorf("保存Excel文件失败: %w", err)
}
// 直接将Excel文件写入到HTTP响应
if err = f.Write(c.Response().BodyWriter()); err != nil {
return fmt.Errorf("写入Excel数据失败: %w", err)
} }
logrus.Infof("成功导出 %d 条数据", rowIndex-1) logrus.Infof("成功导出 %d 条数据", rowIndex-1)
return nil return filename, nil
} }
// 设置标题行 // 设置标题行
@ -172,11 +174,13 @@ func addRow(f *excelize.File, sheetName string, rowIndex int, article *v2.Articl
cell, _ := excelize.CoordinatesToCellName(i+1, rowIndex) cell, _ := excelize.CoordinatesToCellName(i+1, rowIndex)
_ = f.SetCellValue(sheetName, cell, value) _ = f.SetCellValue(sheetName, cell, value)
} }
// 设置行高以适应图片 // 设置行高以适应图片
_ = f.SetRowHeight(sheetName, rowIndex, 80) _ = f.SetRowHeight(sheetName, rowIndex, 80)
// 处理图片 // 处理图片
linkCell, _ := excelize.CoordinatesToCellName(1, rowIndex)
_ = f.SetCellHyperLink(sheetName, linkCell, caProvider.Link, "External")
if len(article.Image) > 0 { if len(article.Image) > 0 {
imgCell, _ := excelize.CoordinatesToCellName(2, rowIndex) imgCell, _ := excelize.CoordinatesToCellName(2, rowIndex)
@ -188,8 +192,8 @@ func addRow(f *excelize.File, sheetName string, rowIndex int, article *v2.Articl
_ = f.SetCellValue(sheetName, imgCell, article.Image) _ = f.SetCellValue(sheetName, imgCell, article.Image)
} else { } else {
// 添加图片到Excel // 添加图片到Excel
if err := f.AddPictureFromBytes(sheetName, imgCell, &excelize.Picture{ if err = f.AddPictureFromBytes(sheetName, imgCell, &excelize.Picture{
Extension: getImageExtension(article.Image), Extension: ".jpg",
File: imgData, File: imgData,
Format: &excelize.GraphicOptions{ Format: &excelize.GraphicOptions{
AutoFit: true, AutoFit: true,
@ -202,6 +206,7 @@ func addRow(f *excelize.File, sheetName string, rowIndex int, article *v2.Articl
// 如果添加失败显示URL // 如果添加失败显示URL
_ = f.SetCellValue(sheetName, imgCell, article.Image) _ = f.SetCellValue(sheetName, imgCell, article.Image)
} }
} }
} }
@ -236,31 +241,6 @@ func downloadImage(url string) ([]byte, error) {
return imgData, nil return imgData, nil
} }
// getImageExtension 从URL获取图片扩展名
func getImageExtension(url string) string {
// 默认扩展名
ext := ".jpg"
// 从URL中提取扩展名
if strings.Contains(url, ".") {
parts := strings.Split(url, ".")
lastPart := parts[len(parts)-1]
// 处理可能的查询参数
if strings.Contains(lastPart, "?") {
lastPart = strings.Split(lastPart, "?")[0]
}
// 常见图片扩展名
switch strings.ToLower(lastPart) {
case "jpg", "jpeg", "png", "gif", "bmp", "webp":
ext = "." + strings.ToLower(lastPart)
}
}
return ext
}
func (e *Exporter) pullDwPrice(sArticle v2.SellerArticle) v2.SellerArticle { func (e *Exporter) pullDwPrice(sArticle v2.SellerArticle) v2.SellerArticle {
skuId, _ := strconv.Atoi(sArticle.SkuID) skuId, _ := strconv.Atoi(sArticle.SkuID)
resp, err := e.dw.NormalBidClient().LowestPrice(skuId) resp, err := e.dw.NormalBidClient().LowestPrice(skuId)