a-excel.go 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724
  1. // 导入导出功能
  2. package api
  3. import (
  4. "archive/zip"
  5. "bytes"
  6. "fmt"
  7. "io"
  8. "net/http"
  9. "os"
  10. "path"
  11. "path/filepath"
  12. "sku3dweb/db/model"
  13. "sku3dweb/db/repo"
  14. "sku3dweb/log"
  15. "strconv"
  16. "strings"
  17. "time"
  18. "github.com/gin-gonic/gin"
  19. excelize "github.com/xuri/excelize/v2"
  20. "go.mongodb.org/mongo-driver/bson/primitive"
  21. "github.com/aliyun/aliyun-oss-go-sdk/oss"
  22. )
  23. // 固定Excel模板表头
  24. var fixedHeaders = []string{
  25. "公司商品编号", "商品中文名", "商品英文名", "分类", "图片",
  26. "单位包材毛重(KG)", "单位包材体积(CBM)", "长度(MM)", "门幅/宽(MM)", "厚度/高(MM)",
  27. "备注", "首选供应商", "默认采购单价", "样品搜集人编号", "样品搜集人", "开发日期",
  28. // "出口属性", "内销属性", "内购属性", "委外属性", 2025-4-29沟通去掉这几个属性
  29. "报关助记符", "报关商品编码", "报关商品中文名", "录入人名称", "适合的市场",
  30. "供应商编号", "种类分类", "种类分类名称", "基布", "基布名称",
  31. "表面工艺", "表面工艺名称", "产品克重(KG)", "运营周期", "商品单位体积",
  32. "商品分类代码", "产品系列", "产品系列", "产品用途", "产品用途",
  33. "样品编号", "留样册号", "原命名编号", "底布克重", "面布克重",
  34. // "原始大图", "封面图", "商品图片",
  35. }
  36. func RegExcelRouter(router *GinRouter) {
  37. // router.POSTJWT("/excel/import", ExcelImport)
  38. // router.GETJWT("/excel/export", ExcelExport)
  39. router.POSTJWT("/zip/import", ZipImport)
  40. router.POSTJWT("/zip/export", ZipExport)
  41. }
  42. func ParseMatObject(ctx *repo.RepoSession, row []string, headers []string, cates []*model.Category) (model.MatImage, error) {
  43. imageMat := model.MatImage{}
  44. // 构建基础数据
  45. imageMat.CusNum = row[0]
  46. imageMat.NameCN = row[1]
  47. imageMat.NameEN = row[2]
  48. //1.商品编号
  49. CusNumIndex := -1
  50. for i, header := range headers {
  51. if header == "公司商品编号" {
  52. CusNumIndex = i
  53. break
  54. }
  55. }
  56. if CusNumIndex == -1 {
  57. return imageMat, fmt.Errorf("公司商品编号列未找到")
  58. }
  59. imageMat.CusNum = row[CusNumIndex]
  60. //2中文名称
  61. NameCNIndex := -1
  62. for i, header := range headers {
  63. if header == "商品中文名" {
  64. NameCNIndex = i
  65. break
  66. }
  67. }
  68. if NameCNIndex == -1 {
  69. return imageMat, fmt.Errorf("商品中文名列未找到")
  70. }
  71. imageMat.NameCN = row[NameCNIndex]
  72. //3英文名称
  73. NameENIndex := -1
  74. for i, header := range headers {
  75. if header == "商品英文名" {
  76. NameENIndex = i
  77. break
  78. }
  79. }
  80. if NameENIndex == -1 {
  81. return imageMat, fmt.Errorf("商品英文名列未找到")
  82. }
  83. imageMat.NameEN = row[NameENIndex]
  84. //4.商品分类
  85. CategoryIndex := -1
  86. for i, header := range headers {
  87. if header == "分类" {
  88. CategoryIndex = i
  89. break
  90. }
  91. }
  92. if CategoryIndex == -1 {
  93. return imageMat, fmt.Errorf("分类列未找到")
  94. }
  95. //imageMat.Categories = append(imageMat.Categories, row[CategoryIndex])
  96. // 根据分类层级一的名字获取对应id,遍历一层获取对应数据
  97. rootCate := &model.Category{}
  98. for _, cate := range cates {
  99. if cate.Name == "商品分类" {
  100. for _, c := range cate.Children {
  101. if c.Name == row[CategoryIndex] {
  102. // 记录该分类为其他自动做准备
  103. rootCate = c
  104. break
  105. }
  106. }
  107. break
  108. }
  109. }
  110. if len(rootCate.IdStr) <= 0 {
  111. return imageMat, fmt.Errorf("%s的商品分类未找到定义", row[CategoryIndex])
  112. }
  113. // 获取rootCate下的二级分类
  114. var getRootCate2 = func(rootCate *model.Category, pName string, name string, cusNum string) *model.Category {
  115. for _, cate := range rootCate.Children {
  116. if cate.Name == pName {
  117. for _, c := range cate.Children {
  118. if c.Name == name {
  119. if len(cusNum) > 0 {
  120. if c.CusNum == cusNum {
  121. return c
  122. }
  123. return nil
  124. }
  125. return c
  126. }
  127. }
  128. }
  129. }
  130. return nil
  131. }
  132. //分类rootId
  133. imageMat.Categories = append(imageMat.Categories, rootCate.IdStr)
  134. //5 单位包材毛重(KG)
  135. PackageGrossWeightIndex := -1
  136. for i, header := range headers {
  137. if header == "单位包材毛重(KG)" {
  138. PackageGrossWeightIndex = i
  139. break
  140. }
  141. }
  142. if PackageGrossWeightIndex == -1 {
  143. return imageMat, fmt.Errorf("单位包材毛重(KG)列未找到")
  144. }
  145. var str2float64 = func(s string) *float64 {
  146. if len(s) == 0 {
  147. return nil
  148. }
  149. f := 0.0
  150. f, err := strconv.ParseFloat(s, 64)
  151. if err != nil {
  152. log.Errorf("解析浮点数失败: %v", err)
  153. return &f // 或 math.NaN() 表示无效值
  154. }
  155. return &f
  156. }
  157. row5 := str2float64(row[PackageGrossWeightIndex])
  158. imageMat.PackageGrossWeight = row5
  159. //6.单位包材体积(CBM)
  160. PackageVolumeIndex := -1
  161. for i, header := range headers {
  162. if header == "单位包材体积(CBM)" {
  163. PackageVolumeIndex = i
  164. break
  165. }
  166. }
  167. if PackageVolumeIndex == -1 {
  168. return imageMat, fmt.Errorf("单位包材体积(CBM)列未找到")
  169. }
  170. row6 := str2float64(row[PackageVolumeIndex])
  171. imageMat.PackageVolume = row6
  172. //7.长度(MM)
  173. PhyHeightIndex := -1
  174. for i, header := range headers {
  175. if header == "长度(MM)" {
  176. PhyHeightIndex = i
  177. break
  178. }
  179. }
  180. if PhyHeightIndex == -1 {
  181. return imageMat, fmt.Errorf("长度(MM)列未找到")
  182. }
  183. row7 := str2float64(row[PhyHeightIndex])
  184. imageMat.PhyHeight = row7
  185. //8.门幅/宽(MM)
  186. PhyWidthIndex := -1
  187. for i, header := range headers {
  188. if header == "门幅/宽(MM)" {
  189. PhyWidthIndex = i
  190. break
  191. }
  192. }
  193. if PhyWidthIndex == -1 {
  194. return imageMat, fmt.Errorf("门幅/宽(MM)列未找到")
  195. }
  196. row8 := str2float64(row[PhyWidthIndex])
  197. imageMat.PhyWidth = row8
  198. //9.厚度/高(MM)
  199. ThicknessIndex := -1
  200. for i, header := range headers {
  201. if header == "厚度/高(MM)" {
  202. ThicknessIndex = i
  203. break
  204. }
  205. }
  206. if ThicknessIndex == -1 {
  207. return imageMat, fmt.Errorf("厚度/高(MM)列未找到")
  208. }
  209. row9 := str2float64(row[ThicknessIndex])
  210. imageMat.Thickness = row9
  211. //10.备注
  212. RemarksIndex := -1
  213. for i, header := range headers {
  214. if header == "备注" {
  215. RemarksIndex = i
  216. break
  217. }
  218. }
  219. if RemarksIndex == -1 {
  220. return imageMat, fmt.Errorf("备注列未找到")
  221. }
  222. imageMat.Remarks = row[RemarksIndex]
  223. //11.首选供应商
  224. MainSupplierIndex := -1
  225. for i, header := range headers {
  226. if header == "首选供应商" {
  227. MainSupplierIndex = i
  228. break
  229. }
  230. }
  231. if MainSupplierIndex == -1 {
  232. return imageMat, fmt.Errorf("首选供应商列未找到")
  233. }
  234. row11Cate := &model.Category{}
  235. for _, cate := range cates {
  236. if cate.Name == "首选供应商" {
  237. for _, c := range cate.Children {
  238. if c.Name == row[MainSupplierIndex] {
  239. // 记录该分类为其他自动做准备
  240. row11Cate = c
  241. break
  242. }
  243. }
  244. break
  245. }
  246. }
  247. if len(row11Cate.IdStr) <= 0 {
  248. return imageMat, fmt.Errorf("%s的首选供应商未找到定义", row[MainSupplierIndex])
  249. }
  250. //添加首先供应商
  251. imageMat.Categories = append(imageMat.Categories, row11Cate.IdStr)
  252. //12.默认采购单价
  253. PriceIndex := -1
  254. for i, header := range headers {
  255. if header == "默认采购单价" {
  256. PriceIndex = i
  257. break
  258. }
  259. }
  260. if PriceIndex == -1 {
  261. return imageMat, fmt.Errorf("默认采购单价列未找到")
  262. }
  263. row12 := str2float64(row[PriceIndex])
  264. imageMat.Price = row12
  265. //13.样品搜集人
  266. FromIndex := -1
  267. for i, header := range headers {
  268. if header == "样品搜集人" {
  269. FromIndex = i
  270. if i+1 < len(headers) && headers[i+1] == "样品搜集人" {
  271. FromIndex = i + 1
  272. }
  273. break
  274. }
  275. }
  276. if FromIndex == -1 {
  277. return imageMat, fmt.Errorf("样品搜集人列未找到")
  278. }
  279. staffName := row[FromIndex]
  280. if len(staffName) > 0 {
  281. staff := model.StaffUser{}
  282. // 验证样品收集人是否预设
  283. found, _ := repo.RepoSeachDoc(ctx, &repo.DocSearchOptions{
  284. CollectName: repo.CollectionStaffUser,
  285. Query: repo.Map{"name": staffName},
  286. }, &staff)
  287. if found {
  288. imageMat.From = staffName
  289. } else {
  290. return imageMat, fmt.Errorf("样品搜集人未配置")
  291. }
  292. }
  293. //14.开发日期
  294. DevTimeIndex := -1
  295. for i, header := range headers {
  296. if header == "开发日期" {
  297. DevTimeIndex = i
  298. break
  299. }
  300. }
  301. if DevTimeIndex == -1 {
  302. return imageMat, fmt.Errorf("开发日期列未找到")
  303. }
  304. if len(row[DevTimeIndex]) > 0 {
  305. layout := "2006/1/2"
  306. // 解析字符串
  307. devTime, _ := time.Parse(layout, row[DevTimeIndex])
  308. imageMat.DevTime = &devTime
  309. }
  310. //15.出口属性
  311. ExportPropertyIndex := -1
  312. for i, header := range headers {
  313. if header == "出口属性" {
  314. ExportPropertyIndex = i
  315. break
  316. }
  317. }
  318. if ExportPropertyIndex == -1 {
  319. return imageMat, fmt.Errorf("出口属性列未找到")
  320. }
  321. row15 := str2bool(row[ExportPropertyIndex])
  322. imageMat.ExportProperty = row15
  323. //16.内销属性
  324. DomesticPropertyIndex := -1
  325. for i, header := range headers {
  326. if header == "内销属性" {
  327. DomesticPropertyIndex = i
  328. break
  329. }
  330. }
  331. if DomesticPropertyIndex == -1 {
  332. return imageMat, fmt.Errorf("内销属性列未找到")
  333. }
  334. row16 := str2bool(row[DomesticPropertyIndex])
  335. imageMat.DomesticProperty = row16
  336. //17.内购属性
  337. InpurchasePropertyIndex := -1
  338. for i, header := range headers {
  339. if header == "内购属性" {
  340. InpurchasePropertyIndex = i
  341. break
  342. }
  343. }
  344. if InpurchasePropertyIndex == -1 {
  345. return imageMat, fmt.Errorf("内购属性列未找到")
  346. }
  347. row17 := str2bool(row[InpurchasePropertyIndex])
  348. imageMat.InpurchaseProperty = row17
  349. //18.委外属性
  350. OutsourcedPropertyIndex := -1
  351. for i, header := range headers {
  352. if header == "委外属性" {
  353. OutsourcedPropertyIndex = i
  354. break
  355. }
  356. }
  357. if OutsourcedPropertyIndex == -1 {
  358. return imageMat, fmt.Errorf("委外属性列未找到")
  359. }
  360. row18 := str2bool(row[OutsourcedPropertyIndex])
  361. imageMat.OutsourcedProperty = row18
  362. //19.报关助记符
  363. MnemonicSignIndex := -1
  364. for i, header := range headers {
  365. if header == "报关商品编码" {
  366. MnemonicSignIndex = i
  367. break
  368. }
  369. }
  370. TaxNameCNIndex := -1
  371. for i, header := range headers {
  372. if header == "报关商品中文名" {
  373. TaxNameCNIndex = i
  374. break
  375. }
  376. }
  377. if MnemonicSignIndex == -1 {
  378. return imageMat, fmt.Errorf("报关商品编码列未找到")
  379. }
  380. if TaxNameCNIndex != -1 && len(row[TaxNameCNIndex]) > 0 {
  381. imageMat.TaxNameCN = row[TaxNameCNIndex]
  382. row15Cate := &model.Category{}
  383. for _, cate := range cates {
  384. if cate.Name == "报关助记符" {
  385. for _, c := range cate.Children {
  386. if c.Name == row[TaxNameCNIndex] {
  387. row15Cate = c
  388. break
  389. }
  390. }
  391. break
  392. }
  393. }
  394. if len(row15Cate.IdStr) <= 0 {
  395. return imageMat, fmt.Errorf("%s的报关助记符未找到定义", row[TaxNameCNIndex])
  396. }
  397. imageMat.Categories = append(imageMat.Categories, row15Cate.IdStr)
  398. }
  399. //21.录入人名称
  400. RecordUserIndex := -1
  401. for i, header := range headers {
  402. if header == "录入人名称" {
  403. RecordUserIndex = i
  404. break
  405. }
  406. }
  407. if RecordUserIndex == -1 {
  408. return imageMat, fmt.Errorf("录入人名称列未找到")
  409. }
  410. imageMat.RecordUser = row[RecordUserIndex]
  411. //22.适合的市场
  412. FitMarketIndex := -1
  413. for i, header := range headers {
  414. if header == "适合的市场" {
  415. FitMarketIndex = i
  416. break
  417. }
  418. }
  419. if FitMarketIndex == -1 {
  420. return imageMat, fmt.Errorf("适合的市场列未找到")
  421. }
  422. imageMat.FitMarket = row[FitMarketIndex]
  423. //23.种类分类
  424. TypeCategoryIndex := -1
  425. for i, header := range headers {
  426. if header == "种类分类" {
  427. TypeCategoryIndex = i
  428. break
  429. }
  430. }
  431. if TypeCategoryIndex == -1 {
  432. return imageMat, fmt.Errorf("种类分类列未找到")
  433. }
  434. //24.种类分类名称
  435. TypeCategoryNameIndex := -1
  436. for i, header := range headers {
  437. if header == "种类分类名称" {
  438. TypeCategoryNameIndex = i
  439. break
  440. }
  441. }
  442. if TypeCategoryNameIndex == -1 {
  443. return imageMat, fmt.Errorf("种类分类名称列未找到")
  444. }
  445. typeCate := row[TypeCategoryIndex]
  446. typeCateName := row[TypeCategoryNameIndex]
  447. typeCateObj := getRootCate2(rootCate, "种类分类", typeCateName, typeCate)
  448. if typeCateObj == nil {
  449. return imageMat, fmt.Errorf("%s / %s 的种类分类未找到定义", typeCate, typeCateName)
  450. }
  451. imageMat.Categories = append(imageMat.Categories, typeCateObj.IdStr)
  452. //25.基布
  453. BaseClothIndex := -1
  454. for i, header := range headers {
  455. if header == "基布" {
  456. BaseClothIndex = i
  457. break
  458. }
  459. }
  460. if BaseClothIndex == -1 {
  461. return imageMat, fmt.Errorf("基布列未找到")
  462. }
  463. //26.基布名称
  464. BaseClothNameIndex := -1
  465. for i, header := range headers {
  466. if header == "基布名称" {
  467. BaseClothNameIndex = i
  468. break
  469. }
  470. }
  471. if BaseClothNameIndex == -1 {
  472. return imageMat, fmt.Errorf("基布名称列未找到")
  473. }
  474. baseCloth := row[BaseClothIndex]
  475. baseClothName := row[BaseClothNameIndex]
  476. baseClothObj := getRootCate2(rootCate, "基布", baseClothName, baseCloth)
  477. if baseClothObj == nil {
  478. return imageMat, fmt.Errorf("%s / %s 的基布未找到定义", baseCloth, baseClothName)
  479. }
  480. imageMat.Categories = append(imageMat.Categories, baseClothObj.IdStr)
  481. //27.表面工艺
  482. SurfaceProcessIndex := -1
  483. for i, header := range headers {
  484. if header == "表面工艺" {
  485. SurfaceProcessIndex = i
  486. break
  487. }
  488. }
  489. if SurfaceProcessIndex == -1 {
  490. return imageMat, fmt.Errorf("表面工艺列未找到")
  491. }
  492. //28.表面工艺名称
  493. SurfaceProcessNameIndex := -1
  494. for i, header := range headers {
  495. if header == "表面工艺名称" {
  496. SurfaceProcessNameIndex = i
  497. break
  498. }
  499. }
  500. if SurfaceProcessNameIndex == -1 {
  501. return imageMat, fmt.Errorf("表面工艺名称列未找到")
  502. }
  503. surfaceProcess := row[SurfaceProcessIndex]
  504. surfaceProcessName := row[SurfaceProcessNameIndex]
  505. surfaceProcessObj := getRootCate2(rootCate, "表面工艺", surfaceProcessName, surfaceProcess)
  506. if surfaceProcessObj == nil {
  507. return imageMat, fmt.Errorf("%s / %s 的表面工艺未找到定义", surfaceProcess, surfaceProcessName)
  508. }
  509. imageMat.Categories = append(imageMat.Categories, surfaceProcessObj.IdStr)
  510. //29.产品克重(KG)
  511. ProductWeightIndex := -1
  512. for i, header := range headers {
  513. if header == "产品克重(KG)" {
  514. ProductWeightIndex = i
  515. break
  516. }
  517. }
  518. if ProductWeightIndex == -1 {
  519. return imageMat, fmt.Errorf("产品克重(KG)列未找到")
  520. }
  521. pw := str2float64(row[ProductWeightIndex])
  522. imageMat.ProductWeight = pw
  523. //30.运营周期
  524. OperationCycleIndex := -1
  525. for i, header := range headers {
  526. if header == "运营周期" {
  527. OperationCycleIndex = i
  528. break
  529. }
  530. }
  531. if OperationCycleIndex == -1 {
  532. return imageMat, fmt.Errorf("运营周期列未找到")
  533. }
  534. imageMat.OperationCycle = row[OperationCycleIndex]
  535. //31.商品单位体积
  536. ProductVolumeIndex := -1
  537. for i, header := range headers {
  538. if header == "商品单位体积" {
  539. ProductVolumeIndex = i
  540. break
  541. }
  542. }
  543. if ProductVolumeIndex == -1 {
  544. return imageMat, fmt.Errorf("商品单位体积列未找到")
  545. }
  546. pv := str2float64(row[ProductVolumeIndex])
  547. imageMat.ProductVolume = pv
  548. //32.商品分类代码
  549. // ProductCategoryIndex := -1
  550. // for i, header := range headers {
  551. // if header == "商品分类代码" {
  552. // ProductCategoryIndex = i
  553. // break
  554. // }
  555. // }
  556. // if ProductCategoryIndex == -1 {
  557. // return imageMat, fmt.Errorf("商品分类代码列未找到")
  558. // }
  559. // imageMat.ProductCategory = row[ProductCategoryIndex]
  560. //33.产品系列
  561. ProductSeriesIndex := -1
  562. ProductSeriesCodeIndex := -1
  563. for i, header := range headers {
  564. if header == "产品系列" {
  565. ProductSeriesIndex = i
  566. ProductSeriesCodeIndex = i
  567. if i+1 < len(headers) && headers[i+1] == "产品系列" {
  568. ProductSeriesIndex = i + 1
  569. }
  570. break
  571. }
  572. }
  573. if ProductSeriesIndex == -1 {
  574. return imageMat, fmt.Errorf("产品系列列未找到")
  575. }
  576. imageMat.ProductSeries = row[ProductSeriesIndex]
  577. if ProductSeriesCodeIndex == -1 {
  578. return imageMat, fmt.Errorf("产品系列代码列未找到")
  579. }
  580. productSeriesCode := row[ProductSeriesCodeIndex]
  581. productSeriesCodeName := row[ProductSeriesCodeIndex+1]
  582. productSeriesCodeObj := getRootCate2(rootCate, "产品系列", productSeriesCodeName, productSeriesCode)
  583. if productSeriesCodeObj == nil {
  584. return imageMat, fmt.Errorf("%s / %s 的产品系列代码未找到定义", productSeriesCode, productSeriesCodeName)
  585. }
  586. imageMat.Categories = append(imageMat.Categories, productSeriesCodeObj.IdStr)
  587. //35.产品用途
  588. ProductUsageIndex := -1
  589. ProductUsageNameIndex := -1
  590. for i, header := range headers {
  591. if header == "产品用途" {
  592. ProductUsageIndex = i
  593. if i+1 < len(headers) && headers[i+1] == "产品用途" {
  594. ProductUsageNameIndex = i + 1
  595. }
  596. break
  597. }
  598. }
  599. if ProductUsageIndex == -1 {
  600. return imageMat, fmt.Errorf("产品用途列未找到")
  601. }
  602. if ProductUsageNameIndex == -1 {
  603. return imageMat, fmt.Errorf("产品用途名称列未找到")
  604. }
  605. imageMat.ProductUsage = row[ProductUsageIndex]
  606. ProductUsageCode := row[ProductUsageIndex]
  607. ProductUsageCodeName := row[ProductUsageNameIndex]
  608. ProductUsageObj := getRootCate2(rootCate, "产品用途", ProductUsageCodeName, ProductUsageCode)
  609. if ProductUsageObj == nil {
  610. return imageMat, fmt.Errorf("%s / %s 产品用途未找到定义", ProductUsageCode, ProductUsageCodeName)
  611. }
  612. imageMat.Categories = append(imageMat.Categories, ProductUsageObj.IdStr)
  613. //36.原命名编号
  614. OriginalNumberIndex := -1
  615. for i, header := range headers {
  616. if header == "原命名编号" {
  617. OriginalNumberIndex = i
  618. break
  619. }
  620. }
  621. if OriginalNumberIndex == -1 {
  622. return imageMat, fmt.Errorf("原命名编号列未找到")
  623. }
  624. imageMat.OriginalNumber = row[OriginalNumberIndex]
  625. //37.底布克重
  626. BackingWeightIndex := -1
  627. for i, header := range headers {
  628. if header == "底布克重" {
  629. BackingWeightIndex = i
  630. break
  631. }
  632. }
  633. if BackingWeightIndex == -1 {
  634. return imageMat, fmt.Errorf("底布克重列未找到")
  635. }
  636. bw := str2float64(row[BackingWeightIndex])
  637. imageMat.BackingWeight = bw
  638. //38.面布克重
  639. SurfaceWeightIndex := -1
  640. for i, header := range headers {
  641. if header == "面布克重" {
  642. SurfaceWeightIndex = i
  643. break
  644. }
  645. }
  646. if SurfaceWeightIndex == -1 {
  647. return imageMat, fmt.Errorf("面布克重列未找到")
  648. }
  649. sw := str2float64(row[SurfaceWeightIndex])
  650. imageMat.SurfaceWeight = sw
  651. return imageMat, nil
  652. }
  653. func str2bool(s string) *bool {
  654. s = strings.TrimSpace(strings.ToLower(s))
  655. if s == "true" || s == "1" || s == "yes" || s == "y" {
  656. return BoolValue(true)
  657. } else if s == "false" || s == "0" || s == "no" || s == "n" || s == "" {
  658. return BoolValue(false)
  659. }
  660. return nil
  661. }
  662. func ExcelImportWithImages(c *gin.Context, apictx *ApiSession, file io.Reader, goodsDir, textureDir string) (interface{}, error) {
  663. // 读取Excel文件
  664. xlsx, err := excelize.OpenReader(file)
  665. if err != nil {
  666. fmt.Println(err.Error())
  667. return nil, NewError("解析Excel文件失败")
  668. }
  669. defer func() {
  670. if err := xlsx.Close(); err != nil {
  671. log.Errorf("关闭Excel文件失败: %v", err)
  672. }
  673. }()
  674. // 获取第一个sheet
  675. sheetName := xlsx.GetSheetName(0)
  676. rows, err := xlsx.GetRows(sheetName)
  677. if err != nil {
  678. return nil, NewError("读取Excel内容失败")
  679. }
  680. // 确保至少有表头和一行数据
  681. if len(rows) < 2 {
  682. return nil, NewError("Excel文件内容不足")
  683. }
  684. // 预热云函数
  685. _, _ = QueryFassiImage("http://lymat.oss-cn-hangzhou.aliyuncs.com/images/1744072972845.png", 1, 0, 0)
  686. // 获取分类配置
  687. cat := []*model.Category{}
  688. err = repo.RepoSeachDocs(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  689. CollectName: repo.CollectionCategory,
  690. Query: repo.Map{},
  691. Project: []string{"name", "type", "children", "createTime"},
  692. }, &cat)
  693. if err != nil {
  694. return nil, NewError("获取分类配置失败")
  695. }
  696. cates := cat[0].Children
  697. // 创建导入结果记录
  698. type ImportResult struct {
  699. RowIndex int
  700. Status string // "成功" 或 "失败"
  701. ErrorMessage string
  702. ImageID string
  703. }
  704. importResults := make([]ImportResult, 0, len(rows)-1)
  705. headers := rows[0]
  706. // 根据模板解析每列数据
  707. for i, row := range rows {
  708. // 跳过表头
  709. if i == 0 {
  710. continue
  711. }
  712. // 创建导入结果记录
  713. result := ImportResult{
  714. RowIndex: i,
  715. Status: "成功",
  716. }
  717. imageMat, err := ParseMatObject(apictx.CreateRepoCtx(), row, headers, cates)
  718. if err != nil {
  719. result.Status = "失败"
  720. result.ErrorMessage = err.Error()
  721. fmt.Println(result.ErrorMessage)
  722. importResults = append(importResults, result)
  723. continue
  724. }
  725. imageName := row[0] // 不包含后缀 .jpg .png .jpeg
  726. // 根据图片名称检查goods和texture中是否存在,图片可能是.jpg .png .jpeg后缀
  727. // 上传图片到oss 获取对应url,赋值到imageMat(Thumbnail, RawImage, ProductImage)
  728. // 在goods目录中查找图片
  729. goodsImagePath := findImageFile(goodsDir, imageName)
  730. textureImagePath := findImageFile(textureDir, imageName)
  731. // 如果找到了图片,上传到OSS
  732. if goodsImagePath != "" {
  733. // 上传原始图片作为RawImage
  734. goodsImage, err := uploadLocalImage(goodsImagePath, "goods", apictx)
  735. if err != nil {
  736. log.Errorf("上传图片失败: %v", err)
  737. } else {
  738. imageMat.ProductImage = goodsImage
  739. }
  740. }
  741. if textureImagePath != "" {
  742. // 上传原始图片作为RawImage
  743. textureImage, err := uploadLocalImage(textureImagePath, "texture", apictx)
  744. if err != nil {
  745. log.Errorf("上传图片失败: %v", err)
  746. } else {
  747. imageMat.RawImage = textureImage
  748. imageMat.Thumbnail = textureImage
  749. }
  750. }
  751. // 设置创建时间
  752. imageMat.CreateTime = time.Now()
  753. imageMat.UpdateTime = time.Now()
  754. // 写入到数据库中并获取创建记录的数据库id
  755. imgId, err := repo.RepoAddDoc(apictx.CreateRepoCtx(), repo.CollectionMatImages, &imageMat)
  756. if err != nil {
  757. log.Errorf("写入记录失败: %v", err)
  758. result.Status = "失败"
  759. result.ErrorMessage = "数据库写入失败: " + err.Error()
  760. importResults = append(importResults, result)
  761. continue
  762. }
  763. result.ImageID = imgId
  764. // 如果texture存在,调用AddFassiImage创建图片特征和id的关联
  765. if textureImagePath != "" && imageMat.RawImage != nil {
  766. objId, err := primitive.ObjectIDFromHex(imgId)
  767. if err != nil {
  768. log.Errorf("转换ID失败: %v", err)
  769. } else {
  770. err = AddFassiImage(objId, imageMat.RawImage.Url)
  771. if err != nil {
  772. log.Errorf("创建Fassi图片关联失败: %v", err)
  773. }
  774. }
  775. }
  776. importResults = append(importResults, result)
  777. }
  778. // 创建一个新的Excel文件用于导出结果
  779. resultExcel := excelize.NewFile()
  780. sheet := "Sheet1"
  781. // 复制原始表头并添加状态列
  782. headers2 := append(rows[0], "导入状态", "失败原因", "数据库ID")
  783. for i, header := range headers2 {
  784. colName := columnIndexToName(i)
  785. resultExcel.SetCellValue(sheet, colName+"1", header)
  786. }
  787. // 写入每行数据和导入结果
  788. for i, row := range rows {
  789. if i == 0 {
  790. continue // 跳过表头
  791. }
  792. // 查找对应的导入结果
  793. var result ImportResult
  794. for _, r := range importResults {
  795. if r.RowIndex == i {
  796. result = r
  797. break
  798. }
  799. }
  800. // 写入原始数据
  801. for j, cell := range row {
  802. colName := columnIndexToName(j)
  803. resultExcel.SetCellValue(sheet, colName+strconv.Itoa(i+1), cell)
  804. }
  805. // 添加状态和错误信息
  806. statusCol := columnIndexToName(len(row))
  807. errorCol := columnIndexToName(len(row) + 1)
  808. idCol := columnIndexToName(len(row) + 2)
  809. resultExcel.SetCellValue(sheet, statusCol+strconv.Itoa(i+1), result.Status)
  810. resultExcel.SetCellValue(sheet, errorCol+strconv.Itoa(i+1), result.ErrorMessage)
  811. resultExcel.SetCellValue(sheet, idCol+strconv.Itoa(i+1), result.ImageID)
  812. }
  813. // 设置列宽
  814. for i := range headers2 {
  815. colName := columnIndexToName(i)
  816. resultExcel.SetColWidth(sheet, colName, colName, 15)
  817. }
  818. // 保存到内存缓冲区
  819. buffer := bytes.Buffer{}
  820. if err := resultExcel.Write(&buffer); err != nil {
  821. return nil, NewError("生成导入结果文件失败")
  822. }
  823. // 设置响应头,使浏览器下载文件
  824. fileName := "import_result_" + time.Now().Format("20060102150405") + ".xlsx"
  825. c.Writer.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", fileName))
  826. c.Writer.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
  827. c.Writer.Header().Set("Content-Transfer-Encoding", "binary")
  828. c.Writer.Write(buffer.Bytes())
  829. return nil, nil
  830. // 统计导入结果
  831. // successCount := 0
  832. // failCount := 0
  833. // partialCount := 0
  834. // for _, result := range importResults {
  835. // switch result.Status {
  836. // case "成功":
  837. // successCount++
  838. // case "失败":
  839. // failCount++
  840. // case "部分成功":
  841. // partialCount++
  842. // }
  843. // }
  844. // return map[string]interface{}{
  845. // "total": len(importResults),
  846. // "success": successCount,
  847. // "failed": failCount,
  848. // "partial": partialCount,
  849. // }, nil
  850. }
  851. // 新增ZIP文件导入处理函数
  852. func ZipImport(c *gin.Context, apictx *ApiSession) (interface{}, error) {
  853. // 获取上传的文件
  854. file, header, err := c.Request.FormFile("file")
  855. if err != nil {
  856. return nil, NewError("获取上传文件失败")
  857. }
  858. defer file.Close()
  859. // 检查文件大小
  860. if header.Size > 500*1024*1024 { // 限制100MB
  861. return nil, NewError("上传文件过大,请控制在500MB以内")
  862. }
  863. // 检查文件扩展名
  864. if !strings.HasSuffix(strings.ToLower(header.Filename), ".zip") {
  865. return nil, NewError("只支持上传ZIP格式的文件")
  866. }
  867. // 创建临时目录存放解压文件
  868. tempDir, err := os.MkdirTemp("", "sku3d_import_"+time.Now().Format("20060102150405"))
  869. if err != nil {
  870. return nil, NewError("创建临时目录失败")
  871. }
  872. defer os.RemoveAll(tempDir) // 确保处理完成后删除临时目录
  873. // 创建goods和texture子目录
  874. goodsDir := filepath.Join(tempDir, "goods")
  875. textureDir := filepath.Join(tempDir, "texture")
  876. if err := os.Mkdir(goodsDir, 0755); err != nil {
  877. return nil, NewError("创建goods目录失败")
  878. }
  879. if err := os.Mkdir(textureDir, 0755); err != nil {
  880. return nil, NewError("创建texture目录失败")
  881. }
  882. // 保存上传的ZIP文件
  883. zipFilePath := path.Join(tempDir, "upload.zip")
  884. tempZipFile, err := os.Create(zipFilePath)
  885. if err != nil {
  886. return nil, NewError("创建临时文件失败")
  887. }
  888. // 将上传的文件内容写入临时文件
  889. _, err = io.Copy(tempZipFile, file)
  890. tempZipFile.Close()
  891. if err != nil {
  892. return nil, NewError("保存上传文件失败")
  893. }
  894. // 解压ZIP文件
  895. extractDir := path.Join(tempDir, "extract")
  896. err = os.MkdirAll(extractDir, 0755)
  897. if err != nil {
  898. return nil, NewError("创建解压目录失败")
  899. }
  900. err = unzipFile(zipFilePath, extractDir)
  901. if err != nil {
  902. return nil, NewError("解压文件失败: " + err.Error())
  903. }
  904. // 查找Excel文件
  905. excelFilePath, err := findExcelFile(extractDir)
  906. if err != nil {
  907. return nil, NewError("未找到有效的Excel文件: " + err.Error())
  908. }
  909. // 打开Excel文件
  910. excelFile, err := os.Open(excelFilePath)
  911. if err != nil {
  912. return nil, NewError("打开Excel文件失败")
  913. }
  914. defer excelFile.Close()
  915. // 商品图片目录和纹理图片目录
  916. goodsDir = path.Join(extractDir, "goods")
  917. textureDir = path.Join(extractDir, "texture")
  918. // 检查目录是否存在
  919. if _, err := os.Stat(goodsDir); os.IsNotExist(err) {
  920. log.Errorf("商品图片目录不存在: %s", goodsDir)
  921. goodsDir = ""
  922. }
  923. if _, err := os.Stat(textureDir); os.IsNotExist(err) {
  924. log.Errorf("纹理图片目录不存在: %s", textureDir)
  925. textureDir = ""
  926. }
  927. // 传递给Excel导入函数处理,并指定图片目录
  928. return ExcelImportWithImages(c, apictx, excelFile, goodsDir, textureDir)
  929. }
  930. // 解压ZIP文件到指定目录
  931. func unzipFile(zipFile, destDir string) error {
  932. r, err := zip.OpenReader(zipFile)
  933. if err != nil {
  934. return err
  935. }
  936. defer r.Close()
  937. for _, f := range r.File {
  938. // 处理路径安全问题
  939. filePath := filepath.Join(destDir, f.Name)
  940. if !strings.HasPrefix(filePath, filepath.Clean(destDir)+string(os.PathSeparator)) {
  941. return fmt.Errorf("非法的ZIP文件路径: %s", f.Name)
  942. }
  943. if f.FileInfo().IsDir() {
  944. // 创建目录
  945. if err := os.MkdirAll(filePath, os.ModePerm); err != nil {
  946. return err
  947. }
  948. continue
  949. }
  950. // 确保父目录存在
  951. if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
  952. return err
  953. }
  954. // 创建文件
  955. outFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
  956. if err != nil {
  957. return err
  958. }
  959. // 打开压缩文件
  960. rc, err := f.Open()
  961. if err != nil {
  962. outFile.Close()
  963. return err
  964. }
  965. // 复制内容
  966. _, err = io.Copy(outFile, rc)
  967. outFile.Close()
  968. rc.Close()
  969. if err != nil {
  970. return err
  971. }
  972. }
  973. return nil
  974. }
  975. // 寻找目录中的Excel文件
  976. func findExcelFile(dir string) (string, error) {
  977. var excelFiles []string
  978. err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
  979. if err != nil {
  980. return err
  981. }
  982. if !info.IsDir() {
  983. lowerPath := strings.ToLower(path)
  984. if strings.HasSuffix(lowerPath, ".xlsx") || strings.HasSuffix(lowerPath, ".xls") {
  985. excelFiles = append(excelFiles, path)
  986. }
  987. }
  988. return nil
  989. })
  990. if err != nil {
  991. return "", err
  992. }
  993. if len(excelFiles) == 0 {
  994. return "", fmt.Errorf("未找到Excel文件")
  995. }
  996. // 返回第一个找到的Excel文件
  997. return excelFiles[0], nil
  998. }
  999. // 获取图片文件路径
  1000. func findImageFile(dir, originalName string) string {
  1001. if dir == "" || originalName == "" {
  1002. return ""
  1003. }
  1004. // 检查不同扩展名的图片文件
  1005. for _, ext := range []string{".png", ".jpg", ".jpeg"} {
  1006. filePath := filepath.Join(dir, originalName+ext)
  1007. if _, err := os.Stat(filePath); err == nil {
  1008. return filePath
  1009. }
  1010. }
  1011. return ""
  1012. }
  1013. // 上传本地图片到OSS
  1014. func uploadLocalImage(filePath string, prefix string, apictx *ApiSession) (*model.OssType, error) {
  1015. // 获取Alibaba Cloud OSS Client
  1016. accessKeyID := apictx.Svc.Conf.Obs.AccessKeyId
  1017. secretKey := apictx.Svc.Conf.Obs.SecrateKey
  1018. endpoint := apictx.Svc.Conf.Obs.Endpoint
  1019. // 创建Alibaba Cloud OSS Client
  1020. client, err := oss.New(endpoint, accessKeyID, secretKey)
  1021. if err != nil {
  1022. return nil, fmt.Errorf("创建OSS Client失败: %v", err)
  1023. }
  1024. // 获取Bucket
  1025. bucketName := apictx.Svc.Conf.Obs.Bucket
  1026. bucket, err := client.Bucket(bucketName)
  1027. if err != nil {
  1028. return nil, fmt.Errorf("获取Bucket失败: %v", err)
  1029. }
  1030. // 上传图片到OSS
  1031. ossPath := fmt.Sprintf("u/%s/%s", apictx.User.ID, prefix)
  1032. ossPath = strings.TrimRight(strings.ReplaceAll(ossPath, "\\", "/"), "/")
  1033. _, fileName := filepath.Split(filePath)
  1034. objectKey := ossPath + "/" + fileName
  1035. // 上传文件
  1036. err = bucket.PutObjectFromFile(objectKey, filePath)
  1037. if err != nil {
  1038. // 重试一次
  1039. err = bucket.PutObjectFromFile(objectKey, filePath)
  1040. if err != nil {
  1041. return nil, fmt.Errorf("上传文件到OSS失败: %v", err)
  1042. }
  1043. }
  1044. // 获取文件大小
  1045. fi, err := os.Stat(filePath)
  1046. size := int64(1)
  1047. if err == nil {
  1048. size = fi.Size()
  1049. }
  1050. // 返回OSS文件信息
  1051. return &model.OssType{
  1052. Url: fmt.Sprintf("https://%s.%s/%s", bucketName, endpoint, objectKey),
  1053. Size: size,
  1054. }, nil
  1055. }
  1056. // 工具函数 - 获取图片URL
  1057. func getImageUrl(oss *model.OssType) string {
  1058. if oss == nil {
  1059. return ""
  1060. }
  1061. return oss.Url
  1062. }
  1063. type ZipExportReq struct {
  1064. MatIds []primitive.ObjectID `json:"matIds"`
  1065. }
  1066. // ZIP导出功能
  1067. func ZipExport(c *gin.Context, apictx *ApiSession) (interface{}, error) {
  1068. // 获取要导出的matIds参数
  1069. var req ZipExportReq
  1070. err := c.ShouldBindJSON(&req)
  1071. if err != nil {
  1072. return nil, fmt.Errorf("参数错误!")
  1073. }
  1074. if len(req.MatIds) == 0 {
  1075. return nil, fmt.Errorf("参数错误!")
  1076. }
  1077. // matId, _ := primitive.ObjectIDFromHex("67f4711195ac3622e01cc073")
  1078. // matId2, _ := primitive.ObjectIDFromHex("67f4710695ac3622e01cc072")
  1079. // req.MatIds = []primitive.ObjectID{matId, matId2}
  1080. // 创建临时目录
  1081. tempDir, err := os.MkdirTemp("", "zipexport_"+time.Now().Format("2006"))
  1082. if err != nil {
  1083. return nil, NewError("创建临时目录失败")
  1084. }
  1085. defer os.RemoveAll(tempDir) // 函数结束时清理临时目录
  1086. // 创建goods和texture子目录
  1087. goodsDir := filepath.Join(tempDir, "goods")
  1088. textureDir := filepath.Join(tempDir, "texture")
  1089. if err := os.Mkdir(goodsDir, 0755); err != nil {
  1090. return nil, NewError("创建goods目录失败")
  1091. }
  1092. if err := os.Mkdir(textureDir, 0755); err != nil {
  1093. return nil, NewError("创建texture目录失败")
  1094. }
  1095. // 转换matIds为ObjectID数组
  1096. objectIds := req.MatIds
  1097. // 查询MatImage记录
  1098. var matImages []model.MatImage
  1099. err = repo.RepoSeachDocs(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  1100. CollectName: repo.CollectionMatImages,
  1101. Query: repo.Map{"_id": repo.Map{"$in": objectIds}},
  1102. }, &matImages)
  1103. if err != nil {
  1104. return nil, NewError("获取商品数据失败")
  1105. }
  1106. // 获取分类配置
  1107. cat := []*model.Category{}
  1108. err = repo.RepoSeachDocs(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  1109. CollectName: repo.CollectionCategory,
  1110. Query: repo.Map{},
  1111. Project: []string{"name", "type", "children", "createTime"},
  1112. }, &cat)
  1113. if err != nil {
  1114. return nil, NewError("获取分类配置失败")
  1115. }
  1116. cates := cat[0].Children
  1117. // 使用模板文件
  1118. templatePath := "./template.xlsx"
  1119. // 打开模板文件
  1120. f, err := excelize.OpenFile(templatePath)
  1121. if err != nil {
  1122. return nil, NewError("打开模板文件失败")
  1123. }
  1124. defer f.Close()
  1125. sheet := "Sheet1"
  1126. // 填充数据
  1127. for i, img := range matImages {
  1128. rowIndex := i + 2 // 跳过表头,从第2行开始
  1129. // 下载图片
  1130. var goodsImagePath, textureImagePath string
  1131. if img.ProductImage != nil && img.ProductImage.Url != "" {
  1132. // 从URL中提取原始文件名
  1133. // productImageFileName := getFileNameFromUrl(img.ProductImage.Url)
  1134. productImageFileName := img.CusNum
  1135. goodsImagePath = filepath.Join(goodsDir, productImageFileName)
  1136. downloadImage(img.ProductImage.Url, goodsImagePath)
  1137. }
  1138. if img.RawImage != nil && img.RawImage.Url != "" {
  1139. // 从URL中提取原始文件名
  1140. rawImageFileName := img.CusNum
  1141. // rawImageFileName := getFileNameFromUrl(img.RawImage.Url)
  1142. textureImagePath = filepath.Join(textureDir, rawImageFileName)
  1143. downloadImage(img.RawImage.Url, textureImagePath)
  1144. }
  1145. // 查找对应的分类名称
  1146. var typeCategoryName, MainSupplierName, MnemonicSign, TypeCategory2, TypeCategory2Name, productSeriesName, productUsageName string
  1147. var typeCategory, productSeries, productUsage, baseCloth, baseClothName, surfaceProcess, surfaceProcessName string
  1148. // 先确定商品分类大类/其他大类 排除后用其他id匹配商品分类下面的小类
  1149. var mainCate *model.Category
  1150. matCatesMap := map[string]struct{}{}
  1151. // 复制分类ID到Map中,便于删除已处理的ID
  1152. for _, ic := range img.Categories {
  1153. if ic != "" { // 确保分类ID不为空
  1154. matCatesMap[ic] = struct{}{}
  1155. }
  1156. }
  1157. // 第一轮:查找顶级分类(商品分类、首选供应商、报关助记符)
  1158. for _, catId := range img.Categories {
  1159. if catId == "" { // 跳过空分类ID
  1160. continue
  1161. }
  1162. for _, parentCat := range cates {
  1163. if parentCat == nil { // 防止空指针
  1164. continue
  1165. }
  1166. for _, child := range parentCat.Children {
  1167. if child == nil || child.IdStr == "" { // 防止空指针或空ID
  1168. continue
  1169. }
  1170. if child.IdStr == catId {
  1171. if parentCat.Name == "商品分类" {
  1172. mainCate = child
  1173. typeCategoryName = child.Name
  1174. typeCategory = catId
  1175. delete(matCatesMap, catId)
  1176. } else if parentCat.Name == "首选供应商" {
  1177. MainSupplierName = child.Name
  1178. delete(matCatesMap, catId)
  1179. } else if parentCat.Name == "报关助记符" {
  1180. MnemonicSign = child.Name
  1181. delete(matCatesMap, catId)
  1182. }
  1183. }
  1184. }
  1185. }
  1186. }
  1187. // 第二轮:如果找到了商品分类,查找其子分类
  1188. if mainCate != nil && len(matCatesMap) > 0 {
  1189. for k := range matCatesMap {
  1190. if k == "" { // 跳过空ID
  1191. continue
  1192. }
  1193. // 防止空指针
  1194. if mainCate.Children == nil {
  1195. continue
  1196. }
  1197. for _, mchild := range mainCate.Children {
  1198. if mchild == nil || mchild.Children == nil { // 防止空指针
  1199. continue
  1200. }
  1201. for _, mc := range mchild.Children {
  1202. if mc == nil || mc.IdStr == "" { // 防止空指针或空ID
  1203. continue
  1204. }
  1205. if mc.IdStr == k {
  1206. if mchild.Name == "种类分类" {
  1207. TypeCategory2 = mc.CusNum
  1208. TypeCategory2Name = mc.Name
  1209. } else if mchild.Name == "产品系列" {
  1210. productSeriesName = mc.Name
  1211. productSeries = mc.CusNum
  1212. } else if mchild.Name == "产品用途" {
  1213. productUsageName = mc.Name
  1214. productUsage = mc.CusNum
  1215. } else if mchild.Name == "基布" {
  1216. baseCloth = mc.CusNum
  1217. baseClothName = mc.Name
  1218. } else if mchild.Name == "表面工艺" {
  1219. surfaceProcess = mc.CusNum
  1220. surfaceProcessName = mc.Name
  1221. }
  1222. }
  1223. }
  1224. }
  1225. }
  1226. }
  1227. // 构建行数据
  1228. f.SetCellValue(sheet, fmt.Sprintf("A%d", rowIndex), img.CusNum) // 公司商品编号
  1229. f.SetCellValue(sheet, fmt.Sprintf("B%d", rowIndex), img.NameCN) // 商品中文名
  1230. f.SetCellValue(sheet, fmt.Sprintf("C%d", rowIndex), img.NameEN) // 商品英文名
  1231. f.SetCellValue(sheet, fmt.Sprintf("D%d", rowIndex), typeCategoryName) // 分类
  1232. f.SetCellValue(sheet, fmt.Sprintf("E%d", rowIndex), "") // 图片
  1233. // 体积重量信息
  1234. if img.PackageGrossWeight != nil {
  1235. f.SetCellValue(sheet, fmt.Sprintf("F%d", rowIndex), *img.PackageGrossWeight) // 单位包材毛重(KG)
  1236. } else {
  1237. f.SetCellValue(sheet, fmt.Sprintf("F%d", rowIndex), 0) // 默认为0
  1238. }
  1239. if img.PackageVolume != nil {
  1240. f.SetCellValue(sheet, fmt.Sprintf("G%d", rowIndex), *img.PackageVolume) // 单位包材体积(CBM)
  1241. } else {
  1242. f.SetCellValue(sheet, fmt.Sprintf("G%d", rowIndex), 0) // 默认为0
  1243. }
  1244. if img.PhyHeight != nil {
  1245. f.SetCellValue(sheet, fmt.Sprintf("H%d", rowIndex), *img.PhyHeight) // 长度(MM)
  1246. } else {
  1247. f.SetCellValue(sheet, fmt.Sprintf("H%d", rowIndex), 0) // 默认为0
  1248. }
  1249. if img.PhyWidth != nil {
  1250. f.SetCellValue(sheet, fmt.Sprintf("I%d", rowIndex), *img.PhyWidth) // 门幅/宽(MM)
  1251. } else {
  1252. f.SetCellValue(sheet, fmt.Sprintf("I%d", rowIndex), 0) // 默认为0
  1253. }
  1254. if img.Thickness != nil {
  1255. f.SetCellValue(sheet, fmt.Sprintf("J%d", rowIndex), *img.Thickness) // 厚度/高(MM)
  1256. } else {
  1257. f.SetCellValue(sheet, fmt.Sprintf("J%d", rowIndex), 0) // 默认为0
  1258. }
  1259. f.SetCellValue(sheet, fmt.Sprintf("K%d", rowIndex), img.Remarks) // 备注
  1260. f.SetCellValue(sheet, fmt.Sprintf("L%d", rowIndex), MainSupplierName) // 首选供应商
  1261. // 默认采购单价
  1262. if img.Price != nil {
  1263. f.SetCellValue(sheet, fmt.Sprintf("M%d", rowIndex), *img.Price)
  1264. } else {
  1265. f.SetCellValue(sheet, fmt.Sprintf("M%d", rowIndex), "")
  1266. }
  1267. // 查询样品搜集人的名称
  1268. staffName := ""
  1269. staffId := ""
  1270. if img.From != "" {
  1271. staff := model.StaffUser{}
  1272. // 验证样品收集人是否预设
  1273. found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
  1274. CollectName: repo.CollectionStaffUser,
  1275. Query: repo.Map{"name": img.From},
  1276. }, &staff)
  1277. if found {
  1278. staffName = staff.Name
  1279. staffId = staff.Id.Hex()
  1280. }
  1281. }
  1282. // 样品搜集人 - 注意Excel示例中有两列样品搜集人
  1283. f.SetCellValue(sheet, fmt.Sprintf("N%d", rowIndex), staffId) // 样品搜集人代码
  1284. f.SetCellValue(sheet, fmt.Sprintf("O%d", rowIndex), staffName) // 样品搜集人名称
  1285. // 开发日期
  1286. if img.DevTime != nil {
  1287. f.SetCellValue(sheet, fmt.Sprintf("P%d", rowIndex), img.DevTime.Format("2006/1/2"))
  1288. } else {
  1289. f.SetCellValue(sheet, fmt.Sprintf("P%d", rowIndex), "")
  1290. }
  1291. // // 商品属性 - 使用True/False字符串而不是布尔值
  1292. // if img.ExportProperty != nil {
  1293. // f.SetCellValue(sheet, fmt.Sprintf("Q%d", rowIndex), boolToString(*img.ExportProperty)) // 出口属性
  1294. // } else {
  1295. // f.SetCellValue(sheet, fmt.Sprintf("Q%d", rowIndex), "False")
  1296. // }
  1297. // if img.DomesticProperty != nil {
  1298. // f.SetCellValue(sheet, fmt.Sprintf("R%d", rowIndex), boolToString(*img.DomesticProperty)) // 内销属性
  1299. // } else {
  1300. // f.SetCellValue(sheet, fmt.Sprintf("R%d", rowIndex), "False")
  1301. // }
  1302. // if img.InpurchaseProperty != nil {
  1303. // f.SetCellValue(sheet, fmt.Sprintf("S%d", rowIndex), boolToString(*img.InpurchaseProperty)) // 内购属性
  1304. // } else {
  1305. // f.SetCellValue(sheet, fmt.Sprintf("S%d", rowIndex), "False")
  1306. // }
  1307. // if img.OutsourcedProperty != nil {
  1308. // f.SetCellValue(sheet, fmt.Sprintf("T%d", rowIndex), boolToString(*img.OutsourcedProperty)) // 委外属性
  1309. // } else {
  1310. // f.SetCellValue(sheet, fmt.Sprintf("T%d", rowIndex), "False")
  1311. // }
  1312. f.SetCellValue(sheet, fmt.Sprintf("Q%d", rowIndex), MnemonicSign) // 报关助记符
  1313. f.SetCellValue(sheet, fmt.Sprintf("R%d", rowIndex), img.TaxGoodsNumber) // 报关商品编码
  1314. f.SetCellValue(sheet, fmt.Sprintf("S%d", rowIndex), img.TaxNameCN) // 报关商品中文名
  1315. // 录入人名称
  1316. f.SetCellValue(sheet, fmt.Sprintf("T%d", rowIndex), apictx.User.Name) // 录入人名称
  1317. // f.SetCellValue(sheet, fmt.Sprintf("X%d", rowIndex), "测试默认") // 录入人名称
  1318. f.SetCellValue(sheet, fmt.Sprintf("U%d", rowIndex), img.FitMarket) // 适合的市场
  1319. f.SetCellValue(sheet, fmt.Sprintf("V%d", rowIndex), img.SupplierID) // 供应商编号
  1320. f.SetCellValue(sheet, fmt.Sprintf("W%d", rowIndex), TypeCategory2) // 种类分类
  1321. f.SetCellValue(sheet, fmt.Sprintf("X%d", rowIndex), TypeCategory2Name) // 种类分类名称
  1322. f.SetCellValue(sheet, fmt.Sprintf("Y%d", rowIndex), baseCloth) // 基布
  1323. f.SetCellValue(sheet, fmt.Sprintf("Z%d", rowIndex), baseClothName) // 基布名称
  1324. f.SetCellValue(sheet, fmt.Sprintf("AA%d", rowIndex), surfaceProcess) // 表面工艺
  1325. f.SetCellValue(sheet, fmt.Sprintf("AB%d", rowIndex), surfaceProcessName) // 表面工艺名称
  1326. // 产品克重
  1327. if img.ProductWeight != nil {
  1328. f.SetCellValue(sheet, fmt.Sprintf("AC%d", rowIndex), *img.ProductWeight) // 产品克重(KG)
  1329. } else {
  1330. f.SetCellValue(sheet, fmt.Sprintf("AC%d", rowIndex), 0)
  1331. }
  1332. f.SetCellValue(sheet, fmt.Sprintf("AD%d", rowIndex), img.OperationCycle) // 运营周期
  1333. // 商品单位体积
  1334. if img.ProductVolume != nil {
  1335. f.SetCellValue(sheet, fmt.Sprintf("AE%d", rowIndex), *img.ProductVolume) // 商品单位体积
  1336. } else {
  1337. f.SetCellValue(sheet, fmt.Sprintf("AE%d", rowIndex), 0)
  1338. }
  1339. // 商品分类代码
  1340. // !!!! 商品分类代码哪个字段未明确
  1341. f.SetCellValue(sheet, fmt.Sprintf("AF%d", rowIndex), typeCategory) // 商品分类代码
  1342. f.SetCellValue(sheet, fmt.Sprintf("AG%d", rowIndex), productSeries) // 产品系列
  1343. f.SetCellValue(sheet, fmt.Sprintf("AH%d", rowIndex), productSeriesName) // 产品系列代码
  1344. f.SetCellValue(sheet, fmt.Sprintf("AI%d", rowIndex), productUsage) // 产品用途
  1345. f.SetCellValue(sheet, fmt.Sprintf("AJ%d", rowIndex), productUsageName) // 产品用途代码
  1346. f.SetCellValue(sheet, fmt.Sprintf("AK%d", rowIndex), img.SampleNumber) // 样品编号
  1347. f.SetCellValue(sheet, fmt.Sprintf("AL%d", rowIndex), img.CatalogNumber) // 留样册号
  1348. f.SetCellValue(sheet, fmt.Sprintf("AM%d", rowIndex), img.OriginalNumber) // 原命名编号
  1349. // 底布克重
  1350. if img.BackingWeight != nil {
  1351. f.SetCellValue(sheet, fmt.Sprintf("AN%d", rowIndex), *img.BackingWeight) // 底布克重
  1352. } else {
  1353. f.SetCellValue(sheet, fmt.Sprintf("AN%d", rowIndex), 0)
  1354. }
  1355. // 面布克重
  1356. if img.SurfaceWeight != nil {
  1357. f.SetCellValue(sheet, fmt.Sprintf("AO%d", rowIndex), *img.SurfaceWeight) // 面布克重
  1358. } else {
  1359. f.SetCellValue(sheet, fmt.Sprintf("AO%d", rowIndex), 0)
  1360. }
  1361. }
  1362. // 调整列宽
  1363. for i := range fixedHeaders {
  1364. colName := columnIndexToName(i)
  1365. f.SetColWidth(sheet, colName, colName, 15)
  1366. }
  1367. // 保存Excel到临时目录
  1368. excelPath := filepath.Join(tempDir, "export.xlsx")
  1369. if err := f.SaveAs(excelPath); err != nil {
  1370. return nil, NewError("保存Excel文件失败")
  1371. }
  1372. // 创建ZIP文件
  1373. zipPath := filepath.Join(tempDir, "export.zip")
  1374. zipFile, err := os.Create(zipPath)
  1375. if err != nil {
  1376. return nil, NewError("创建ZIP文件失败")
  1377. }
  1378. defer zipFile.Close()
  1379. // 创建ZIP writer
  1380. zipWriter := zip.NewWriter(zipFile)
  1381. defer zipWriter.Close()
  1382. // 添加Excel文件到ZIP
  1383. if err := addFileToZip(zipWriter, excelPath, "export.xlsx"); err != nil {
  1384. return nil, NewError("添加Excel文件到ZIP失败")
  1385. }
  1386. // 添加goods目录到ZIP
  1387. if err := addDirToZip(zipWriter, goodsDir, "goods"); err != nil {
  1388. return nil, NewError("添加goods目录到ZIP失败")
  1389. }
  1390. // 添加texture目录到ZIP
  1391. if err := addDirToZip(zipWriter, textureDir, "texture"); err != nil {
  1392. return nil, NewError("添加texture目录到ZIP失败")
  1393. }
  1394. // 关闭ZIP writer
  1395. zipWriter.Close()
  1396. // 读取ZIP文件内容
  1397. zipData, err := os.ReadFile(zipPath)
  1398. if err != nil {
  1399. return nil, NewError("读取ZIP文件失败")
  1400. }
  1401. // 设置响应头,使浏览器下载文件
  1402. c.Writer.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=export_%s.zip", time.Now().Format("20060102150405")))
  1403. c.Writer.Header().Set("Content-Type", "application/zip")
  1404. c.Writer.Header().Set("Content-Transfer-Encoding", "binary")
  1405. c.Writer.Write(zipData)
  1406. return nil, nil
  1407. }
  1408. // 下载图片到本地文件
  1409. func downloadImage(url string, destPath string) error {
  1410. // 发起HTTP GET请求
  1411. resp, err := http.Get(url)
  1412. if err != nil {
  1413. return err
  1414. }
  1415. defer resp.Body.Close()
  1416. // 检查响应状态
  1417. if resp.StatusCode != http.StatusOK {
  1418. return fmt.Errorf("下载图片失败,状态码: %d", resp.StatusCode)
  1419. }
  1420. // 创建目标文件
  1421. out, err := os.Create(destPath)
  1422. if err != nil {
  1423. return err
  1424. }
  1425. defer out.Close()
  1426. // 将响应内容写入文件
  1427. _, err = io.Copy(out, resp.Body)
  1428. return err
  1429. }
  1430. // 添加文件到ZIP
  1431. func addFileToZip(zipWriter *zip.Writer, filePath, zipPath string) error {
  1432. file, err := os.Open(filePath)
  1433. if err != nil {
  1434. return err
  1435. }
  1436. defer file.Close()
  1437. info, err := file.Stat()
  1438. if err != nil {
  1439. return err
  1440. }
  1441. header, err := zip.FileInfoHeader(info)
  1442. if err != nil {
  1443. return err
  1444. }
  1445. header.Name = zipPath
  1446. header.Method = zip.Deflate
  1447. writer, err := zipWriter.CreateHeader(header)
  1448. if err != nil {
  1449. return err
  1450. }
  1451. _, err = io.Copy(writer, file)
  1452. return err
  1453. }
  1454. // 添加目录到ZIP
  1455. func addDirToZip(zipWriter *zip.Writer, dirPath, zipPath string) error {
  1456. files, err := os.ReadDir(dirPath)
  1457. if err != nil {
  1458. return err
  1459. }
  1460. // 先创建目录条目
  1461. if zipPath != "" {
  1462. dirEntry := &zip.FileHeader{
  1463. Name: zipPath + "/",
  1464. Method: zip.Deflate,
  1465. }
  1466. dirEntry.SetMode(0755 | os.ModeDir)
  1467. _, err = zipWriter.CreateHeader(dirEntry)
  1468. if err != nil {
  1469. return err
  1470. }
  1471. }
  1472. for _, file := range files {
  1473. filePath := filepath.Join(dirPath, file.Name())
  1474. // 使用斜杠作为ZIP文件中的路径分隔符,确保跨平台兼容性
  1475. zipFilePath := zipPath + "/" + file.Name()
  1476. if zipPath == "" {
  1477. zipFilePath = file.Name()
  1478. }
  1479. if file.IsDir() {
  1480. // 递归添加子目录
  1481. if err := addDirToZip(zipWriter, filePath, zipFilePath); err != nil {
  1482. return err
  1483. }
  1484. } else {
  1485. // 添加文件
  1486. if err := addFileToZip(zipWriter, filePath, zipFilePath); err != nil {
  1487. return err
  1488. }
  1489. }
  1490. }
  1491. return nil
  1492. }
  1493. // 工具函数 - 格式化日期
  1494. func formatDate(t *time.Time) string {
  1495. if t == nil {
  1496. return ""
  1497. }
  1498. return t.Format("2006-01-02")
  1499. }
  1500. // 工具函数 - 将布尔值转换为字符串
  1501. func boolToString(b bool) string {
  1502. if b {
  1503. return "True"
  1504. }
  1505. return "False"
  1506. }
  1507. // 工具函数 - 从URL中提取文件名
  1508. func getFileNameFromUrl(url string) string {
  1509. // 从URL中提取文件名
  1510. fileName := path.Base(url)
  1511. return fileName
  1512. }
  1513. // 工具函数 - 将列索引转换为Excel列名(支持多字母列名,如AA, AB等)
  1514. func columnIndexToName(index int) string {
  1515. result := ""
  1516. for index >= 0 {
  1517. remainder := index % 26
  1518. result = string(rune('A'+remainder)) + result
  1519. index = index/26 - 1
  1520. }
  1521. return result
  1522. }