|
@@ -41,6 +41,7 @@ func RegExcelRouter(router *GinRouter) {
|
|
|
// router.POSTJWT("/excel/import", ExcelImport)
|
|
|
// router.GETJWT("/excel/export", ExcelExport)
|
|
|
router.POSTJWT("/zip/import", ZipImport)
|
|
|
+ router.GET("/zip/export", ZipExport)
|
|
|
}
|
|
|
|
|
|
func ExcelImportWithImages(c *gin.Context, apictx *ApiSession, file io.Reader, goodsDir, textureDir string) (interface{}, error) {
|
|
@@ -152,7 +153,7 @@ func ExcelImportWithImages(c *gin.Context, apictx *ApiSession, file io.Reader, g
|
|
|
|
|
|
imageMat.Categories = append(imageMat.Categories, row3Cate.IdStr)
|
|
|
|
|
|
- // TODO 跳过图片 后面统一处理
|
|
|
+ // 跳过图片 后面统一处理
|
|
|
var str2float64 = func(s string) float64 {
|
|
|
f, err := strconv.ParseFloat(s, 64)
|
|
|
if err != nil {
|
|
@@ -523,6 +524,16 @@ func ZipImport(c *gin.Context, apictx *ApiSession) (interface{}, error) {
|
|
|
}
|
|
|
defer os.RemoveAll(tempDir) // 确保处理完成后删除临时目录
|
|
|
|
|
|
+ // 创建goods和texture子目录
|
|
|
+ goodsDir := filepath.Join(tempDir, "goods")
|
|
|
+ textureDir := filepath.Join(tempDir, "texture")
|
|
|
+ if err := os.Mkdir(goodsDir, 0755); err != nil {
|
|
|
+ return nil, NewError("创建goods目录失败")
|
|
|
+ }
|
|
|
+ if err := os.Mkdir(textureDir, 0755); err != nil {
|
|
|
+ return nil, NewError("创建texture目录失败")
|
|
|
+ }
|
|
|
+
|
|
|
// 保存上传的ZIP文件
|
|
|
zipFilePath := path.Join(tempDir, "upload.zip")
|
|
|
tempZipFile, err := os.Create(zipFilePath)
|
|
@@ -563,8 +574,8 @@ func ZipImport(c *gin.Context, apictx *ApiSession) (interface{}, error) {
|
|
|
defer excelFile.Close()
|
|
|
|
|
|
// 商品图片目录和纹理图片目录
|
|
|
- goodsDir := path.Join(extractDir, "goods")
|
|
|
- textureDir := path.Join(extractDir, "texture")
|
|
|
+ goodsDir = path.Join(extractDir, "goods")
|
|
|
+ textureDir = path.Join(extractDir, "texture")
|
|
|
|
|
|
// 检查目录是否存在
|
|
|
if _, err := os.Stat(goodsDir); os.IsNotExist(err) {
|
|
@@ -699,3 +710,523 @@ func getImageUrl(oss *model.OssType) string {
|
|
|
}
|
|
|
return oss.Url
|
|
|
}
|
|
|
+
|
|
|
+type ZipExportReq struct {
|
|
|
+ MatIds []primitive.ObjectID `json:"matIds"`
|
|
|
+}
|
|
|
+
|
|
|
+// ZIP导出功能
|
|
|
+func ZipExport(c *gin.Context, apictx *ApiSession) (interface{}, error) {
|
|
|
+ // 获取要导出的matIds参数
|
|
|
+ var req ZipExportReq
|
|
|
+ err := c.ShouldBindJSON(&req)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("参数错误!")
|
|
|
+ }
|
|
|
+ if len(req.MatIds) == 0 {
|
|
|
+ return nil, fmt.Errorf("参数错误!")
|
|
|
+ }
|
|
|
+ // matId, _ := primitive.ObjectIDFromHex("67f4711195ac3622e01cc073")
|
|
|
+ // matId2, _ := primitive.ObjectIDFromHex("67f4710695ac3622e01cc072")
|
|
|
+ // req.MatIds = []primitive.ObjectID{matId, matId2}
|
|
|
+
|
|
|
+ // 创建临时目录
|
|
|
+ tempDir, err := os.MkdirTemp("", "zipexport_"+time.Now().Format("2006"))
|
|
|
+ if err != nil {
|
|
|
+ return nil, NewError("创建临时目录失败")
|
|
|
+ }
|
|
|
+ defer os.RemoveAll(tempDir) // 函数结束时清理临时目录
|
|
|
+
|
|
|
+ // 创建goods和texture子目录
|
|
|
+ goodsDir := filepath.Join(tempDir, "goods")
|
|
|
+ textureDir := filepath.Join(tempDir, "texture")
|
|
|
+ if err := os.Mkdir(goodsDir, 0755); err != nil {
|
|
|
+ return nil, NewError("创建goods目录失败")
|
|
|
+ }
|
|
|
+ if err := os.Mkdir(textureDir, 0755); err != nil {
|
|
|
+ return nil, NewError("创建texture目录失败")
|
|
|
+ }
|
|
|
+
|
|
|
+ // 转换matIds为ObjectID数组
|
|
|
+ objectIds := req.MatIds
|
|
|
+
|
|
|
+ // 查询MatImage记录
|
|
|
+ var matImages []model.MatImage
|
|
|
+ err = repo.RepoSeachDocs(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
|
|
|
+ CollectName: repo.CollectionMatImages,
|
|
|
+ Query: repo.Map{"_id": repo.Map{"$in": objectIds}},
|
|
|
+ }, &matImages)
|
|
|
+ if err != nil {
|
|
|
+ return nil, NewError("获取商品数据失败")
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取分类配置
|
|
|
+ cat := []*model.Category{}
|
|
|
+ err = repo.RepoSeachDocs(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
|
|
|
+ CollectName: repo.CollectionCategory,
|
|
|
+ Query: repo.Map{},
|
|
|
+ Project: []string{"name", "type", "children", "createTime"},
|
|
|
+ }, &cat)
|
|
|
+ if err != nil {
|
|
|
+ return nil, NewError("获取分类配置失败")
|
|
|
+ }
|
|
|
+ cates := cat[0].Children
|
|
|
+
|
|
|
+ // 使用模板文件
|
|
|
+ templatePath := "./template.xlsx"
|
|
|
+ // 打开模板文件
|
|
|
+ f, err := excelize.OpenFile(templatePath)
|
|
|
+ if err != nil {
|
|
|
+ return nil, NewError("打开模板文件失败")
|
|
|
+ }
|
|
|
+ defer f.Close()
|
|
|
+
|
|
|
+ sheet := "Sheet1"
|
|
|
+
|
|
|
+ // 填充数据
|
|
|
+ for i, img := range matImages {
|
|
|
+ rowIndex := i + 2 // 跳过表头,从第2行开始
|
|
|
+
|
|
|
+ // 下载图片
|
|
|
+ var goodsImagePath, textureImagePath string
|
|
|
+ if img.ProductImage != nil && img.ProductImage.Url != "" {
|
|
|
+ // 从URL中提取原始文件名
|
|
|
+ productImageFileName := getFileNameFromUrl(img.ProductImage.Url)
|
|
|
+ goodsImagePath = filepath.Join(goodsDir, productImageFileName)
|
|
|
+ downloadImage(img.ProductImage.Url, goodsImagePath)
|
|
|
+ }
|
|
|
+ if img.RawImage != nil && img.RawImage.Url != "" {
|
|
|
+ // 从URL中提取原始文件名
|
|
|
+ rawImageFileName := getFileNameFromUrl(img.RawImage.Url)
|
|
|
+ textureImagePath = filepath.Join(textureDir, rawImageFileName)
|
|
|
+ downloadImage(img.RawImage.Url, textureImagePath)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查找对应的分类名称
|
|
|
+ var typeCategoryName, MainSupplierName, MnemonicSign, TypeCategory2, TypeCategory2Name, productSeriesName, productUsageName string
|
|
|
+ var typeCategory, productSeries, productUsage, baseCloth, baseClothName, surfaceProcess, surfaceProcessName string
|
|
|
+
|
|
|
+ // 先确定商品分类大类/其他大类 排除后用其他id匹配商品分类下面的小类
|
|
|
+ var mainCate *model.Category
|
|
|
+ matCatesMap := map[string]struct{}{}
|
|
|
+ // 复制分类ID到Map中,便于删除已处理的ID
|
|
|
+ for _, ic := range img.Categories {
|
|
|
+ if ic != "" { // 确保分类ID不为空
|
|
|
+ matCatesMap[ic] = struct{}{}
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 第一轮:查找顶级分类(商品分类、首选供应商、报关助记符)
|
|
|
+ for _, catId := range img.Categories {
|
|
|
+ if catId == "" { // 跳过空分类ID
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, parentCat := range cates {
|
|
|
+ if parentCat == nil { // 防止空指针
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, child := range parentCat.Children {
|
|
|
+ if child == nil || child.IdStr == "" { // 防止空指针或空ID
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ if child.IdStr == catId {
|
|
|
+ if parentCat.Name == "商品分类" {
|
|
|
+ mainCate = child
|
|
|
+ typeCategoryName = child.Name
|
|
|
+ typeCategory = catId
|
|
|
+ delete(matCatesMap, catId)
|
|
|
+ } else if parentCat.Name == "首选供应商" {
|
|
|
+ MainSupplierName = child.Name
|
|
|
+ delete(matCatesMap, catId)
|
|
|
+ } else if parentCat.Name == "报关助记符" {
|
|
|
+ MnemonicSign = child.Name
|
|
|
+ delete(matCatesMap, catId)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 第二轮:如果找到了商品分类,查找其子分类
|
|
|
+ if mainCate != nil && len(matCatesMap) > 0 {
|
|
|
+ for k := range matCatesMap {
|
|
|
+ if k == "" { // 跳过空ID
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ // 防止空指针
|
|
|
+ if mainCate.Children == nil {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, mchild := range mainCate.Children {
|
|
|
+ if mchild == nil || mchild.Children == nil { // 防止空指针
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, mc := range mchild.Children {
|
|
|
+ if mc == nil || mc.IdStr == "" { // 防止空指针或空ID
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ if mc.IdStr == k {
|
|
|
+ if mchild.Name == "种类分类" {
|
|
|
+ TypeCategory2 = mc.CusNum
|
|
|
+ TypeCategory2Name = mc.Name
|
|
|
+ } else if mchild.Name == "产品系列" {
|
|
|
+ productSeriesName = mc.Name
|
|
|
+ productSeries = mc.CusNum
|
|
|
+ } else if mchild.Name == "产品用途" {
|
|
|
+ productUsageName = mc.Name
|
|
|
+ productUsage = mc.CusNum
|
|
|
+ } else if mchild.Name == "基布" {
|
|
|
+ baseCloth = mc.CusNum
|
|
|
+ baseClothName = mc.Name
|
|
|
+ } else if mchild.Name == "表面工艺" {
|
|
|
+ surfaceProcess = mc.CusNum
|
|
|
+ surfaceProcessName = mc.Name
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建行数据
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("A%d", rowIndex), img.CusNum) // 公司商品编号
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("B%d", rowIndex), img.NameCN) // 商品中文名
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("C%d", rowIndex), img.NameEN) // 商品英文名
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("D%d", rowIndex), typeCategoryName) // 分类
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("E%d", rowIndex), "") // 图片
|
|
|
+
|
|
|
+ // 体积重量信息
|
|
|
+ if img.PackageGrossWeight != nil {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("F%d", rowIndex), *img.PackageGrossWeight) // 单位包材毛重(KG)
|
|
|
+ } else {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("F%d", rowIndex), 0) // 默认为0
|
|
|
+ }
|
|
|
+
|
|
|
+ if img.PackageVolume != nil {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("G%d", rowIndex), *img.PackageVolume) // 单位包材体积(CBM)
|
|
|
+ } else {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("G%d", rowIndex), 0) // 默认为0
|
|
|
+ }
|
|
|
+
|
|
|
+ if img.PhyHeight != nil {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("H%d", rowIndex), *img.PhyHeight) // 长度(MM)
|
|
|
+ } else {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("H%d", rowIndex), 0) // 默认为0
|
|
|
+ }
|
|
|
+
|
|
|
+ if img.PhyWidth != nil {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("I%d", rowIndex), *img.PhyWidth) // 门幅/宽(MM)
|
|
|
+ } else {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("I%d", rowIndex), 0) // 默认为0
|
|
|
+ }
|
|
|
+
|
|
|
+ if img.Thickness != nil {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("J%d", rowIndex), *img.Thickness) // 厚度/高(MM)
|
|
|
+ } else {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("J%d", rowIndex), 0) // 默认为0
|
|
|
+ }
|
|
|
+
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("K%d", rowIndex), img.Remarks) // 备注
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("L%d", rowIndex), MainSupplierName) // 首选供应商
|
|
|
+
|
|
|
+ // 默认采购单价
|
|
|
+ if img.Price != nil {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("M%d", rowIndex), *img.Price)
|
|
|
+ } else {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("M%d", rowIndex), "")
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询样品搜集人的名称
|
|
|
+ staffName := ""
|
|
|
+ staffId := ""
|
|
|
+ if img.From != "" {
|
|
|
+ staff := model.StaffUser{}
|
|
|
+ found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
|
|
|
+ CollectName: repo.CollectionStaffUser,
|
|
|
+ Query: repo.Map{"name": img.From},
|
|
|
+ }, &staff)
|
|
|
+ if found {
|
|
|
+ staffName = staff.Name
|
|
|
+ staffId = staff.Id.Hex()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 样品搜集人 - 注意Excel示例中有两列样品搜集人
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("N%d", rowIndex), staffId) // 样品搜集人代码
|
|
|
+
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("O%d", rowIndex), staffName) // 样品搜集人名称
|
|
|
+
|
|
|
+ // 开发日期
|
|
|
+ if img.DevTime != nil {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("P%d", rowIndex), img.DevTime.Format("2006/1/2"))
|
|
|
+ } else {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("P%d", rowIndex), "")
|
|
|
+ }
|
|
|
+
|
|
|
+ // 商品属性 - 使用True/False字符串而不是布尔值
|
|
|
+ if img.ExportProperty != nil {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("Q%d", rowIndex), boolToString(*img.ExportProperty)) // 出口属性
|
|
|
+ } else {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("Q%d", rowIndex), "False")
|
|
|
+ }
|
|
|
+
|
|
|
+ if img.DomesticProperty != nil {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("R%d", rowIndex), boolToString(*img.DomesticProperty)) // 内销属性
|
|
|
+ } else {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("R%d", rowIndex), "False")
|
|
|
+ }
|
|
|
+
|
|
|
+ if img.InpurchaseProperty != nil {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("S%d", rowIndex), boolToString(*img.InpurchaseProperty)) // 内购属性
|
|
|
+ } else {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("S%d", rowIndex), "False")
|
|
|
+ }
|
|
|
+
|
|
|
+ if img.OutsourcedProperty != nil {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("T%d", rowIndex), boolToString(*img.OutsourcedProperty)) // 委外属性
|
|
|
+ } else {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("T%d", rowIndex), "False")
|
|
|
+ }
|
|
|
+
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("U%d", rowIndex), MnemonicSign) // 报关助记符
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("V%d", rowIndex), img.TaxGoodsNumber) // 报关商品编码
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("W%d", rowIndex), img.TaxNameCN) // 报关商品中文名
|
|
|
+
|
|
|
+ // 录入人名称
|
|
|
+ // f.SetCellValue(sheet, fmt.Sprintf("X%d", rowIndex), apictx.User.Name) // 录入人名称
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("X%d", rowIndex), "测试默认") // 录入人名称
|
|
|
+
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("Y%d", rowIndex), img.FitMarket) // 适合的市场
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("Z%d", rowIndex), img.SupplierID) // 供应商编号
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AA%d", rowIndex), TypeCategory2) // 种类分类
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AB%d", rowIndex), TypeCategory2Name) // 种类分类名称
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AC%d", rowIndex), baseCloth) // 基布
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AD%d", rowIndex), baseClothName) // 基布名称
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AE%d", rowIndex), surfaceProcess) // 表面工艺
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AF%d", rowIndex), surfaceProcessName) // 表面工艺名称
|
|
|
+
|
|
|
+ // 产品克重
|
|
|
+ if img.ProductWeight != nil {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AG%d", rowIndex), *img.ProductWeight) // 产品克重(KG)
|
|
|
+ } else {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AG%d", rowIndex), 0)
|
|
|
+ }
|
|
|
+
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AH%d", rowIndex), img.OperationCycle) // 运营周期
|
|
|
+
|
|
|
+ // 商品单位体积
|
|
|
+ if img.ProductVolume != nil {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AI%d", rowIndex), *img.ProductVolume) // 商品单位体积
|
|
|
+ } else {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AI%d", rowIndex), 0)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 商品分类代码
|
|
|
+ // !!!! 商品分类代码哪个字段未明确
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AJ%d", rowIndex), typeCategory) // 商品分类代码
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AK%d", rowIndex), productSeries) // 产品系列
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AL%d", rowIndex), productSeriesName) // 产品系列代码
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AM%d", rowIndex), productUsage) // 产品用途
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AN%d", rowIndex), productUsageName) // 产品用途代码
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AO%d", rowIndex), img.SampleNumber) // 样品编号
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AP%d", rowIndex), img.CatalogNumber) // 留样册号
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AQ%d", rowIndex), img.OriginalNumber) // 原命名编号
|
|
|
+
|
|
|
+ // 底布克重
|
|
|
+ if img.BackingWeight != nil {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AR%d", rowIndex), *img.BackingWeight) // 底布克重
|
|
|
+ } else {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AR%d", rowIndex), 0)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 面布克重
|
|
|
+ if img.SurfaceWeight != nil {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AS%d", rowIndex), *img.SurfaceWeight) // 面布克重
|
|
|
+ } else {
|
|
|
+ f.SetCellValue(sheet, fmt.Sprintf("AS%d", rowIndex), 0)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 调整列宽
|
|
|
+ for i := range fixedHeaders {
|
|
|
+ colName := string(rune('A' + i))
|
|
|
+ f.SetColWidth(sheet, colName, colName, 15)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存Excel到临时目录
|
|
|
+ excelPath := filepath.Join(tempDir, "export.xlsx")
|
|
|
+ if err := f.SaveAs(excelPath); err != nil {
|
|
|
+ return nil, NewError("保存Excel文件失败")
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建ZIP文件
|
|
|
+ zipPath := filepath.Join(tempDir, "export.zip")
|
|
|
+ zipFile, err := os.Create(zipPath)
|
|
|
+ if err != nil {
|
|
|
+ return nil, NewError("创建ZIP文件失败")
|
|
|
+ }
|
|
|
+ defer zipFile.Close()
|
|
|
+
|
|
|
+ // 创建ZIP writer
|
|
|
+ zipWriter := zip.NewWriter(zipFile)
|
|
|
+ defer zipWriter.Close()
|
|
|
+
|
|
|
+ // 添加Excel文件到ZIP
|
|
|
+ if err := addFileToZip(zipWriter, excelPath, "export.xlsx"); err != nil {
|
|
|
+ return nil, NewError("添加Excel文件到ZIP失败")
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加goods目录到ZIP
|
|
|
+ if err := addDirToZip(zipWriter, goodsDir, "goods"); err != nil {
|
|
|
+ return nil, NewError("添加goods目录到ZIP失败")
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加texture目录到ZIP
|
|
|
+ if err := addDirToZip(zipWriter, textureDir, "texture"); err != nil {
|
|
|
+ return nil, NewError("添加texture目录到ZIP失败")
|
|
|
+ }
|
|
|
+
|
|
|
+ // 关闭ZIP writer
|
|
|
+ zipWriter.Close()
|
|
|
+
|
|
|
+ // 读取ZIP文件内容
|
|
|
+ zipData, err := os.ReadFile(zipPath)
|
|
|
+ if err != nil {
|
|
|
+ return nil, NewError("读取ZIP文件失败")
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置响应头,使浏览器下载文件
|
|
|
+ c.Header("Content-Description", "File Transfer")
|
|
|
+ c.Header("Content-Disposition", "attachment; filename=export_"+time.Now().Format("20060102150405")+".zip")
|
|
|
+ c.Data(http.StatusOK, "application/zip", zipData)
|
|
|
+
|
|
|
+ return nil, nil
|
|
|
+}
|
|
|
+
|
|
|
+// 下载图片到本地文件
|
|
|
+func downloadImage(url string, destPath string) error {
|
|
|
+ // 发起HTTP GET请求
|
|
|
+ resp, err := http.Get(url)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ defer resp.Body.Close()
|
|
|
+
|
|
|
+ // 检查响应状态
|
|
|
+ if resp.StatusCode != http.StatusOK {
|
|
|
+ return fmt.Errorf("下载图片失败,状态码: %d", resp.StatusCode)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建目标文件
|
|
|
+ out, err := os.Create(destPath)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ defer out.Close()
|
|
|
+
|
|
|
+ // 将响应内容写入文件
|
|
|
+ _, err = io.Copy(out, resp.Body)
|
|
|
+ return err
|
|
|
+}
|
|
|
+
|
|
|
+// 添加文件到ZIP
|
|
|
+func addFileToZip(zipWriter *zip.Writer, filePath, zipPath string) error {
|
|
|
+ file, err := os.Open(filePath)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ defer file.Close()
|
|
|
+
|
|
|
+ info, err := file.Stat()
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ header, err := zip.FileInfoHeader(info)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ header.Name = zipPath
|
|
|
+ header.Method = zip.Deflate
|
|
|
+
|
|
|
+ writer, err := zipWriter.CreateHeader(header)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ _, err = io.Copy(writer, file)
|
|
|
+ return err
|
|
|
+}
|
|
|
+
|
|
|
+// 添加目录到ZIP
|
|
|
+func addDirToZip(zipWriter *zip.Writer, dirPath, zipPath string) error {
|
|
|
+ files, err := os.ReadDir(dirPath)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ // 先创建目录条目
|
|
|
+ if zipPath != "" {
|
|
|
+ dirEntry := &zip.FileHeader{
|
|
|
+ Name: zipPath + "/",
|
|
|
+ Method: zip.Deflate,
|
|
|
+ }
|
|
|
+ dirEntry.SetMode(0755 | os.ModeDir)
|
|
|
+ _, err = zipWriter.CreateHeader(dirEntry)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, file := range files {
|
|
|
+ filePath := filepath.Join(dirPath, file.Name())
|
|
|
+ // 使用斜杠作为ZIP文件中的路径分隔符,确保跨平台兼容性
|
|
|
+ zipFilePath := zipPath + "/" + file.Name()
|
|
|
+ if zipPath == "" {
|
|
|
+ zipFilePath = file.Name()
|
|
|
+ }
|
|
|
+
|
|
|
+ if file.IsDir() {
|
|
|
+ // 递归添加子目录
|
|
|
+ if err := addDirToZip(zipWriter, filePath, zipFilePath); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 添加文件
|
|
|
+ if err := addFileToZip(zipWriter, filePath, zipFilePath); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// 工具函数 - 格式化日期
|
|
|
+func formatDate(t *time.Time) string {
|
|
|
+ if t == nil {
|
|
|
+ return ""
|
|
|
+ }
|
|
|
+ return t.Format("2006-01-02")
|
|
|
+}
|
|
|
+
|
|
|
+// 工具函数 - 将布尔值转换为字符串
|
|
|
+func boolToString(b bool) string {
|
|
|
+ if b {
|
|
|
+ return "True"
|
|
|
+ }
|
|
|
+ return "False"
|
|
|
+}
|
|
|
+
|
|
|
+// 工具函数 - 从URL中提取文件名
|
|
|
+func getFileNameFromUrl(url string) string {
|
|
|
+ // 从URL中提取文件名
|
|
|
+ fileName := path.Base(url)
|
|
|
+ return fileName
|
|
|
+}
|