Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e01f585f6 | |||
| be52c21ee9 |
239
.idea/workspace.xml
generated
239
.idea/workspace.xml
generated
@@ -4,13 +4,154 @@
|
|||||||
<option name="autoReloadType" value="SELECTIVE" />
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="3ac55b60-f0f7-4a9b-af40-ce3c8e3c4479" name="更改" comment="feat(goods): 添加Excel批量导入商品功能 - 集成POI库支持Excel文件读取和解析 - 实现商品信息从Excel表格到数据库的批量导入 - 支持商品分类自动创建和映射 - 添加商品图片从Excel单元格提取和保存功能 - 实现导入过程中的数据验证和错误处理 - 提供导入结果统计和反馈信息 - 支持跳过已存在商品的可选配置 - 添加导入参数的灵活配置选项">
|
<list default="true" id="6e090a32-8359-42c2-9d7b-de1a55875c40" name="更改" comment="新增:批量导入商品功能">
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/CreditBreachOfTrustController.java" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/cms/vo/SideItemVo.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/cms/vo/SideItemVo.java" afterDir="false" />
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/CreditCaseFilingController.java" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/common/system/entity/Cache.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/common/system/entity/Cache.java" afterDir="false" />
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/CreditCompanyController.java" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/oa/param/CompanyParam.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/oa/param/CompanyParam.java" afterDir="false" />
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/CreditCompetitorController.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/CreditCourtAnnouncementController.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/CreditCourtSessionController.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/CreditCustomerController.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/CreditDeliveryNoticeController.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/CreditExternalController.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/CreditFinalVersionController.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/CreditGqdjController.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/CreditJudgmentDebtorController.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/CreditJudicialDocumentController.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/CreditJudiciaryController.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/CreditMediationController.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/CreditRiskRelationController.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/CreditSupplierController.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/CreditUserController.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/CreditXgxfController.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/ExcelImportSupport.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/ImportHelper.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/entity/CreditBreachOfTrust.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/entity/CreditCaseFiling.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/entity/CreditCompany.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/entity/CreditCompetitor.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/entity/CreditCourtAnnouncement.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/entity/CreditCourtSession.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/entity/CreditCustomer.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/entity/CreditDeliveryNotice.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/entity/CreditExternal.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/entity/CreditFinalVersion.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/entity/CreditGqdj.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/entity/CreditJudgmentDebtor.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/entity/CreditJudicialDocument.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/entity/CreditJudiciary.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/entity/CreditMediation.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/entity/CreditRiskRelation.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/entity/CreditSupplier.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/entity/CreditUser.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/entity/CreditXgxf.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/CreditBreachOfTrustMapper.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/CreditCaseFilingMapper.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/CreditCompanyMapper.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/CreditCompetitorMapper.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/CreditCourtAnnouncementMapper.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/CreditCourtSessionMapper.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/CreditCustomerMapper.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/CreditDeliveryNoticeMapper.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/CreditExternalMapper.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/CreditFinalVersionMapper.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/CreditGqdjMapper.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/CreditJudgmentDebtorMapper.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/CreditJudicialDocumentMapper.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/CreditJudiciaryMapper.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/CreditMediationMapper.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/CreditRiskRelationMapper.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/CreditSupplierMapper.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/CreditUserMapper.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/CreditXgxfMapper.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditBreachOfTrustMapper.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditCaseFilingMapper.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditCompanyMapper.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditCompetitorMapper.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditCourtAnnouncementMapper.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditCourtSessionMapper.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditCustomerMapper.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditDeliveryNoticeMapper.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditExternalMapper.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditFinalVersionMapper.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditGqdjMapper.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditJudgmentDebtorMapper.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditJudicialDocumentMapper.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditJudiciaryMapper.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditMediationMapper.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditRiskRelationMapper.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditSupplierMapper.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditUserMapper.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/mapper/xml/CreditXgxfMapper.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditBreachOfTrustParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditCaseFilingParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditCompanyImportParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditCompanyParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditCompetitorImportParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditCompetitorParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditCourtAnnouncementParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditCourtSessionParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditCustomerImportParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditCustomerParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditDeliveryNoticeParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditExternalImportParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditExternalParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditFinalVersionParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditGqdjImportParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditGqdjParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditJudgmentDebtorImportParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditJudgmentDebtorParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditJudicialDocumentParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditJudicialImportParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditJudiciaryImportParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditJudiciaryParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditMediationParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditRiskRelationImportParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditRiskRelationParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditSupplierImportParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditSupplierParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditUserImportParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditUserParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/param/CreditXgxfParam.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/CreditBreachOfTrustService.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/CreditCaseFilingService.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/CreditCompanyService.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/CreditCompetitorService.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/CreditCourtAnnouncementService.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/CreditCourtSessionService.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/CreditCustomerService.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/CreditDeliveryNoticeService.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/CreditExternalService.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/CreditFinalVersionService.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/CreditGqdjService.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/CreditJudgmentDebtorService.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/CreditJudicialDocumentService.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/CreditJudiciaryService.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/CreditMediationService.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/CreditRiskRelationService.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/CreditSupplierService.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/CreditUserService.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/CreditXgxfService.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/impl/CreditBreachOfTrustServiceImpl.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/impl/CreditCaseFilingServiceImpl.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/impl/CreditCompanyServiceImpl.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/impl/CreditCompetitorServiceImpl.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/impl/CreditCourtAnnouncementServiceImpl.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/impl/CreditCourtSessionServiceImpl.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/impl/CreditCustomerServiceImpl.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/impl/CreditDeliveryNoticeServiceImpl.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/impl/CreditExternalServiceImpl.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/impl/CreditFinalVersionServiceImpl.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/impl/CreditGqdjServiceImpl.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/impl/CreditJudgmentDebtorServiceImpl.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/impl/CreditJudicialDocumentServiceImpl.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/impl/CreditJudiciaryServiceImpl.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/impl/CreditMediationServiceImpl.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/impl/CreditRiskRelationServiceImpl.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/impl/CreditSupplierServiceImpl.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/impl/CreditUserServiceImpl.java" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/service/impl/CreditXgxfServiceImpl.java" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/shop/controller/GoodsController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/shop/controller/GoodsController.java" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/shop/controller/GoodsController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/shop/controller/GoodsController.java" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/shop/vo/CartVo.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/shop/vo/CartVo.java" afterDir="false" />
|
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
@@ -20,31 +161,18 @@
|
|||||||
<component name="Git.Settings">
|
<component name="Git.Settings">
|
||||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||||
</component>
|
</component>
|
||||||
<component name="MavenRunner">
|
|
||||||
<option name="skipTests" value="true" />
|
|
||||||
</component>
|
|
||||||
<component name="ProjectColorInfo"><![CDATA[{
|
|
||||||
"associatedIndex": 6
|
|
||||||
}]]></component>
|
|
||||||
<component name="ProjectId" id="37VTGX36DC5AWDQalQxF4yPXvre" />
|
|
||||||
<component name="ProjectViewState">
|
|
||||||
<option name="hideEmptyMiddlePackages" value="true" />
|
|
||||||
<option name="showLibraryContents" value="true" />
|
|
||||||
</component>
|
|
||||||
<component name="PropertiesComponent"><![CDATA[{
|
<component name="PropertiesComponent"><![CDATA[{
|
||||||
"keyToString": {
|
"keyToString": {
|
||||||
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
|
||||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
|
||||||
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
|
|
||||||
"RunOnceActivity.git.unshallow": "true",
|
|
||||||
"git-widget-placeholder": "dev",
|
"git-widget-placeholder": "dev",
|
||||||
"kotlin-language-version-configured": "true",
|
"last_opened_file_path": "/Users/gxwebsoft/JAVA/guofu-shop/src/main/java/com/gxwebsoft",
|
||||||
"project.structure.last.edited": "项目",
|
|
||||||
"project.structure.proportion": "0.0",
|
|
||||||
"project.structure.side.proportion": "0.0",
|
|
||||||
"应用程序.WebSoftApplication.executor": "Run"
|
"应用程序.WebSoftApplication.executor": "Run"
|
||||||
}
|
}
|
||||||
}]]></component>
|
}]]></component>
|
||||||
|
<component name="RecentsManager">
|
||||||
|
<key name="CopyFile.RECENT_KEYS">
|
||||||
|
<recent name="$PROJECT_DIR$/src/main/java/com/gxwebsoft" />
|
||||||
|
</key>
|
||||||
|
</component>
|
||||||
<component name="RunManager">
|
<component name="RunManager">
|
||||||
<configuration name="WebSoftApplication" type="Application" factoryName="Application" temporary="true" nameIsGenerated="true">
|
<configuration name="WebSoftApplication" type="Application" factoryName="Application" temporary="true" nameIsGenerated="true">
|
||||||
<option name="MAIN_CLASS_NAME" value="com.gxwebsoft.WebSoftApplication" />
|
<option name="MAIN_CLASS_NAME" value="com.gxwebsoft.WebSoftApplication" />
|
||||||
@@ -59,72 +187,13 @@
|
|||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
</configuration>
|
</configuration>
|
||||||
<configuration default="true" type="JetRunConfigurationType">
|
|
||||||
<method v="2">
|
|
||||||
<option name="Make" enabled="true" />
|
|
||||||
</method>
|
|
||||||
</configuration>
|
|
||||||
<configuration default="true" type="KotlinStandaloneScriptRunConfigurationType">
|
|
||||||
<option name="filePath" />
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
<recent_temporary>
|
<recent_temporary>
|
||||||
<list>
|
<list>
|
||||||
<item itemvalue="应用程序.WebSoftApplication" />
|
<item itemvalue="应用程序.WebSoftApplication" />
|
||||||
</list>
|
</list>
|
||||||
</recent_temporary>
|
</recent_temporary>
|
||||||
</component>
|
</component>
|
||||||
<component name="SharedIndexes">
|
|
||||||
<attachedChunks>
|
|
||||||
<set>
|
|
||||||
<option value="bundled-jdk-9823dce3aa75-bf35d07a577b-intellij.indexing.shared.core-IU-252.26830.84" />
|
|
||||||
</set>
|
|
||||||
</attachedChunks>
|
|
||||||
</component>
|
|
||||||
<component name="TaskManager">
|
<component name="TaskManager">
|
||||||
<task active="true" id="Default" summary="默认任务">
|
|
||||||
<changelist id="3ac55b60-f0f7-4a9b-af40-ce3c8e3c4479" name="更改" comment="" />
|
|
||||||
<created>1766987144189</created>
|
|
||||||
<option name="number" value="Default" />
|
|
||||||
<option name="presentableId" value="Default" />
|
|
||||||
<updated>1766987144189</updated>
|
|
||||||
</task>
|
|
||||||
<task id="LOCAL-00001" summary="feat(goods): 添加Excel批量导入商品功能 - 集成POI库支持Excel文件读取和解析 - 实现商品信息从Excel表格到数据库的批量导入 - 支持商品分类自动创建和映射 - 添加商品图片从Excel单元格提取和保存功能 - 实现导入过程中的数据验证和错误处理 - 提供导入结果统计和反馈信息 - 支持跳过已存在商品的可选配置 - 添加导入参数的灵活配置选项">
|
|
||||||
<option name="closed" value="true" />
|
|
||||||
<created>1766992044615</created>
|
|
||||||
<option name="number" value="00001" />
|
|
||||||
<option name="presentableId" value="LOCAL-00001" />
|
|
||||||
<option name="project" value="LOCAL" />
|
|
||||||
<updated>1766992044615</updated>
|
|
||||||
</task>
|
|
||||||
<option name="localTasksCounter" value="2" />
|
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
<component name="Vcs.Log.Tabs.Properties">
|
</project>
|
||||||
<option name="TAB_STATES">
|
|
||||||
<map>
|
|
||||||
<entry key="MAIN">
|
|
||||||
<value>
|
|
||||||
<State>
|
|
||||||
<option name="FILTERS">
|
|
||||||
<map>
|
|
||||||
<entry key="branch">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="dev" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</State>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
<component name="VcsManagerConfiguration">
|
|
||||||
<MESSAGE value="feat(goods): 添加Excel批量导入商品功能 - 集成POI库支持Excel文件读取和解析 - 实现商品信息从Excel表格到数据库的批量导入 - 支持商品分类自动创建和映射 - 添加商品图片从Excel单元格提取和保存功能 - 实现导入过程中的数据验证和错误处理 - 提供导入结果统计和反馈信息 - 支持跳过已存在商品的可选配置 - 添加导入参数的灵活配置选项" />
|
|
||||||
<option name="LAST_COMMIT_MESSAGE" value="feat(goods): 添加Excel批量导入商品功能 - 集成POI库支持Excel文件读取和解析 - 实现商品信息从Excel表格到数据库的批量导入 - 支持商品分类自动创建和映射 - 添加商品图片从Excel单元格提取和保存功能 - 实现导入过程中的数据验证和错误处理 - 提供导入结果统计和反馈信息 - 支持跳过已存在商品的可选配置 - 添加导入参数的灵活配置选项" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|||||||
BIN
.mvn/wrapper/maven-wrapper.jar
vendored
Normal file
BIN
.mvn/wrapper/maven-wrapper.jar
vendored
Normal file
Binary file not shown.
2
.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
2
.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
|
||||||
|
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
package com.gxwebsoft.shop.controller;
|
package com.gxwebsoft.shop.controller;
|
||||||
|
|
||||||
|
import cn.afterturn.easypoi.excel.ExcelExportUtil;
|
||||||
|
import cn.afterturn.easypoi.excel.ExcelImportUtil;
|
||||||
|
import cn.afterturn.easypoi.excel.entity.ExportParams;
|
||||||
|
import cn.afterturn.easypoi.excel.entity.ImportParams;
|
||||||
import cn.binarywang.wx.miniapp.api.WxMaQrcodeService;
|
import cn.binarywang.wx.miniapp.api.WxMaQrcodeService;
|
||||||
import cn.binarywang.wx.miniapp.api.WxMaService;
|
import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||||
import cn.binarywang.wx.miniapp.api.impl.WxMaQrcodeServiceImpl;
|
import cn.binarywang.wx.miniapp.api.impl.WxMaQrcodeServiceImpl;
|
||||||
@@ -7,12 +11,10 @@ import cn.binarywang.wx.miniapp.bean.WxMaCodeLineColor;
|
|||||||
import cn.hutool.core.img.ImgUtil;
|
import cn.hutool.core.img.ImgUtil;
|
||||||
import cn.hutool.core.io.FileUtil;
|
import cn.hutool.core.io.FileUtil;
|
||||||
import cn.hutool.http.HttpUtil;
|
import cn.hutool.http.HttpUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import com.freewayso.image.combiner.ImageCombiner;
|
import com.freewayso.image.combiner.ImageCombiner;
|
||||||
import com.freewayso.image.combiner.enums.OutputFormat;
|
import com.freewayso.image.combiner.enums.OutputFormat;
|
||||||
import com.gxwebsoft.common.core.config.ConfigProperties;
|
|
||||||
import com.gxwebsoft.common.core.utils.JSONUtil;
|
import com.gxwebsoft.common.core.utils.JSONUtil;
|
||||||
import com.gxwebsoft.common.core.utils.RequestUtil;
|
import com.gxwebsoft.common.core.utils.RequestUtil;
|
||||||
import com.gxwebsoft.common.core.web.BaseController;
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
@@ -20,46 +22,41 @@ import com.gxwebsoft.common.system.entity.DictData;
|
|||||||
import com.gxwebsoft.shop.entity.*;
|
import com.gxwebsoft.shop.entity.*;
|
||||||
import com.gxwebsoft.shop.service.*;
|
import com.gxwebsoft.shop.service.*;
|
||||||
import com.gxwebsoft.shop.param.GoodsParam;
|
import com.gxwebsoft.shop.param.GoodsParam;
|
||||||
|
import com.gxwebsoft.shop.param.GoodsImportParam;
|
||||||
import com.gxwebsoft.common.core.web.ApiResult;
|
import com.gxwebsoft.common.core.web.ApiResult;
|
||||||
import com.gxwebsoft.common.core.web.PageResult;
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
import com.gxwebsoft.common.core.web.PageParam;
|
import com.gxwebsoft.common.core.web.PageParam;
|
||||||
import com.gxwebsoft.common.core.web.BatchParam;
|
import com.gxwebsoft.common.core.web.BatchParam;
|
||||||
import com.gxwebsoft.common.core.annotation.OperationLog;
|
import com.gxwebsoft.common.core.annotation.OperationLog;
|
||||||
import com.gxwebsoft.common.system.entity.User;
|
import com.gxwebsoft.common.system.entity.User;
|
||||||
import com.gxwebsoft.shop.vo.GoodsExcelImportResult;
|
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import org.apache.poi.ss.usermodel.*;
|
import org.apache.poi.ss.usermodel.Cell;
|
||||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
import org.apache.poi.ss.usermodel.Row;
|
||||||
|
import org.apache.poi.ss.usermodel.Sheet;
|
||||||
|
import org.apache.poi.ss.usermodel.WorkbookFactory;
|
||||||
|
import org.apache.poi.ss.usermodel.Workbook;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.net.URI;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Objects;
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.zip.ZipEntry;
|
|
||||||
import java.util.zip.ZipFile;
|
|
||||||
|
|
||||||
import org.w3c.dom.Document;
|
|
||||||
import org.w3c.dom.Element;
|
|
||||||
import org.w3c.dom.NodeList;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 商品记录表控制器
|
* 商品记录表控制器
|
||||||
@@ -71,8 +68,6 @@ import org.w3c.dom.NodeList;
|
|||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/shop/goods")
|
@RequestMapping("/api/shop/goods")
|
||||||
public class GoodsController extends BaseController {
|
public class GoodsController extends BaseController {
|
||||||
private static final String DEFAULT_IMPORT_EXCEL_PATH = "/Users/gxwebsoft/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/ip170com_ae28/msg/file/2025-12/产品目录-商城储备12.26(2).xlsx";
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private GoodsService goodsService;
|
private GoodsService goodsService;
|
||||||
@Resource
|
@Resource
|
||||||
@@ -83,10 +78,6 @@ public class GoodsController extends BaseController {
|
|||||||
private MerchantService merchantService;
|
private MerchantService merchantService;
|
||||||
@Resource
|
@Resource
|
||||||
private GoodsRoleCommissionService goodsRoleCommissionService;
|
private GoodsRoleCommissionService goodsRoleCommissionService;
|
||||||
@Resource
|
|
||||||
private GoodsCategoryService goodsCategoryService;
|
|
||||||
@Resource
|
|
||||||
private ConfigProperties config;
|
|
||||||
|
|
||||||
@Value("${config.upload-path}")
|
@Value("${config.upload-path}")
|
||||||
private String uploadPath;
|
private String uploadPath;
|
||||||
@@ -266,6 +257,195 @@ public class GoodsController extends BaseController {
|
|||||||
return fail("删除失败");
|
return fail("删除失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量导入商品(对应 goods-import.xlsx 第一页)
|
||||||
|
* 表头:序号、作用分类(除消费者外可见)、品种、品牌、厂家、销量、规格、产品图片、成本(来华+运费)、商城售价(无标注按厂家)、产品分类(所有可见)、公司
|
||||||
|
*/
|
||||||
|
@ApiOperation("批量导入商品")
|
||||||
|
@PostMapping("/import")
|
||||||
|
public ApiResult<List<String>> importBatch(@RequestParam("file") MultipartFile file) {
|
||||||
|
List<String> errorMessages = new ArrayList<>();
|
||||||
|
int insertCount = 0;
|
||||||
|
int updateCount = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
List<GoodsImportParam> list = null;
|
||||||
|
int usedTitleRows = 0;
|
||||||
|
int usedHeadRows = 0;
|
||||||
|
int[][] tryConfigs = new int[][]{{1, 1}, {0, 1}, {0, 2}, {0, 3}};
|
||||||
|
|
||||||
|
for (int[] config : tryConfigs) {
|
||||||
|
list = filterEmptyRows(tryImport(file, config[0], config[1]));
|
||||||
|
if (list != null && !list.isEmpty()) {
|
||||||
|
usedTitleRows = config[0];
|
||||||
|
usedHeadRows = config[1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (list == null || list.isEmpty()) {
|
||||||
|
return fail("未读取到数据,请确认模板表头与示例格式一致", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
User loginUser = getLoginUser();
|
||||||
|
Integer currentUserId = loginUser != null ? loginUser.getUserId() : null;
|
||||||
|
Integer currentTenantId = loginUser != null ? loginUser.getTenantId() : null;
|
||||||
|
Integer defaultMerchantId = loginUser != null ? loginUser.getMerchantId() : null;
|
||||||
|
|
||||||
|
Map<String, Merchant> merchantCache = new HashMap<>();
|
||||||
|
Merchant defaultMerchant = defaultMerchantId != null ? merchantService.getById(defaultMerchantId) : null;
|
||||||
|
Map<Integer, String> imageLinkMap = readHyperlinksByHeader(file, 0, usedTitleRows, usedHeadRows, "产品图片");
|
||||||
|
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
GoodsImportParam param = list.get(i);
|
||||||
|
int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
|
||||||
|
try {
|
||||||
|
Goods item = convertImportParamToEntity(param);
|
||||||
|
String imageLink = imageLinkMap.get(i);
|
||||||
|
if (isBlank(item.getImage()) && !isBlank(imageLink)) {
|
||||||
|
item.setImage(imageLink);
|
||||||
|
}
|
||||||
|
// 兼容数据库 shop_goods.files 非空约束:默认使用image或空串兜底
|
||||||
|
if (isBlank(item.getFiles())) {
|
||||||
|
item.setFiles(!isBlank(item.getImage()) ? item.getImage() : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBlank(item.getGoodsName())) {
|
||||||
|
errorMessages.add("第" + excelRowNumber + "行:品种(商品名称)不能为空");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer merchantId = defaultMerchantId != null ? defaultMerchantId : 0;
|
||||||
|
Merchant merchant = defaultMerchant;
|
||||||
|
if (merchantId == 0 && !isBlank(param.getCompany())) {
|
||||||
|
merchant = resolveMerchantByName(param.getCompany(), merchantCache);
|
||||||
|
if (merchant != null && merchant.getMerchantId() != null) {
|
||||||
|
merchantId = merchant.getMerchantId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item.setMerchantId(merchantId);
|
||||||
|
if (isBlank(item.getMerchantName())) {
|
||||||
|
if (merchant != null && !isBlank(merchant.getMerchantName())) {
|
||||||
|
item.setMerchantName(merchant.getMerchantName());
|
||||||
|
} else if (!isBlank(param.getCompany())) {
|
||||||
|
item.setMerchantName(param.getCompany());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.getUserId() == null && currentUserId != null) {
|
||||||
|
item.setUserId(currentUserId);
|
||||||
|
}
|
||||||
|
if (item.getTenantId() == null) {
|
||||||
|
if (currentTenantId != null) {
|
||||||
|
item.setTenantId(currentTenantId);
|
||||||
|
} else if (merchant != null && merchant.getTenantId() != null) {
|
||||||
|
item.setTenantId(merchant.getTenantId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (item.getType() == null) {
|
||||||
|
item.setType(0);
|
||||||
|
}
|
||||||
|
if (item.getSpecs() == null) {
|
||||||
|
item.setSpecs(0);
|
||||||
|
}
|
||||||
|
if (item.getDeductStockType() == null) {
|
||||||
|
item.setDeductStockType(10);
|
||||||
|
}
|
||||||
|
if (item.getIsShow() == null) {
|
||||||
|
item.setIsShow(0);
|
||||||
|
}
|
||||||
|
if (item.getStatus() == null) {
|
||||||
|
item.setStatus(0);
|
||||||
|
}
|
||||||
|
if (item.getRecommend() == null) {
|
||||||
|
item.setRecommend(0);
|
||||||
|
}
|
||||||
|
if (item.getDeleted() == null) {
|
||||||
|
item.setDeleted(0);
|
||||||
|
}
|
||||||
|
if (item.getStock() == null) {
|
||||||
|
item.setStock(0);
|
||||||
|
}
|
||||||
|
if (item.getSales() == null) {
|
||||||
|
item.setSales(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// upsert:同商户下按商品名称匹配
|
||||||
|
Goods existing = goodsService.getOne(
|
||||||
|
new LambdaQueryWrapper<Goods>()
|
||||||
|
.eq(Goods::getMerchantId, merchantId)
|
||||||
|
.eq(Goods::getGoodsName, item.getGoodsName())
|
||||||
|
.last("limit 1")
|
||||||
|
);
|
||||||
|
|
||||||
|
if (existing == null) {
|
||||||
|
if (goodsService.save(item)) {
|
||||||
|
insertCount++;
|
||||||
|
} else {
|
||||||
|
errorMessages.add("第" + excelRowNumber + "行:保存失败");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mergeImportedToExisting(existing, item);
|
||||||
|
if (goodsService.updateById(existing)) {
|
||||||
|
updateCount++;
|
||||||
|
} else {
|
||||||
|
errorMessages.add("第" + excelRowNumber + "行:更新失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
errorMessages.add("第" + excelRowNumber + "行:" + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String message = "导入完成,新增" + insertCount + "条";
|
||||||
|
if (updateCount > 0) {
|
||||||
|
message += ",更新" + updateCount + "条";
|
||||||
|
}
|
||||||
|
if (errorMessages.isEmpty()) {
|
||||||
|
return success(message, null);
|
||||||
|
}
|
||||||
|
message += ",失败" + errorMessages.size() + "条";
|
||||||
|
return success(message, errorMessages);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return fail("导入失败:" + e.getMessage(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载商品导入模板(仅生成第一张sheet的表头及示例数据)
|
||||||
|
*/
|
||||||
|
@ApiOperation("下载商品导入模板")
|
||||||
|
@GetMapping("/import/template")
|
||||||
|
public void downloadImportTemplate(HttpServletResponse response) throws IOException {
|
||||||
|
List<GoodsImportParam> templateList = new ArrayList<>();
|
||||||
|
|
||||||
|
GoodsImportParam example = new GoodsImportParam();
|
||||||
|
example.setCode("1");
|
||||||
|
example.setEnsureTag("示例分类");
|
||||||
|
example.setGoodsName("示例商品");
|
||||||
|
example.setBrand("示例品牌");
|
||||||
|
example.setSupplierName("示例厂家");
|
||||||
|
example.setSales("2000+");
|
||||||
|
example.setUnitName("40袋/盒");
|
||||||
|
example.setImage("");
|
||||||
|
example.setCostPrice("8.2");
|
||||||
|
example.setSalePrice("44.97");
|
||||||
|
example.setCategory("示例分类");
|
||||||
|
example.setCompany("示例公司");
|
||||||
|
templateList.add(example);
|
||||||
|
|
||||||
|
ExportParams exportParams = new ExportParams("商品导入模板", "商品");
|
||||||
|
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, GoodsImportParam.class, templateList);
|
||||||
|
|
||||||
|
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||||
|
String filename = java.net.URLEncoder.encode("goods_import_template.xlsx", StandardCharsets.UTF_8);
|
||||||
|
response.setHeader("Content-Disposition", "attachment; filename=" + filename);
|
||||||
|
|
||||||
|
workbook.write(response.getOutputStream());
|
||||||
|
workbook.close();
|
||||||
|
}
|
||||||
|
|
||||||
@ApiOperation("统计信息")
|
@ApiOperation("统计信息")
|
||||||
@GetMapping("/data")
|
@GetMapping("/data")
|
||||||
public ApiResult<Map<String, Integer>> data(GoodsParam param) {
|
public ApiResult<Map<String, Integer>> data(GoodsParam param) {
|
||||||
@@ -297,373 +477,6 @@ public class GoodsController extends BaseController {
|
|||||||
return success(data);
|
return success(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation("批量导入商品(Excel)")
|
|
||||||
@PostMapping("/import-excel")
|
|
||||||
public ApiResult<GoodsExcelImportResult> importExcel(@RequestParam(value = "path", required = false) String path,
|
|
||||||
@RequestParam(value = "sheetName", required = false) String sheetName,
|
|
||||||
@RequestParam(value = "skipExisting", required = false, defaultValue = "true") Boolean skipExisting,
|
|
||||||
@RequestParam(value = "createCategory", required = false, defaultValue = "true") Boolean createCategory,
|
|
||||||
@RequestParam(value = "merchantId", required = false) Integer merchantId,
|
|
||||||
@RequestParam(value = "defaultIsShow", required = false) Integer defaultIsShow,
|
|
||||||
@RequestParam(value = "defaultStock", required = false) Integer defaultStock) {
|
|
||||||
String excelPath = StrUtil.blankToDefault(path, DEFAULT_IMPORT_EXCEL_PATH);
|
|
||||||
String targetSheetName = StrUtil.blankToDefault(sheetName, "Sheet1");
|
|
||||||
GoodsExcelImportResult result = new GoodsExcelImportResult();
|
|
||||||
result.setExcelPath(excelPath);
|
|
||||||
result.setSheetName(targetSheetName);
|
|
||||||
ResolvedExcelFile resolvedExcelFile;
|
|
||||||
try {
|
|
||||||
resolvedExcelFile = resolveExcelFile(excelPath);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
return fail(e.getMessage(), result);
|
|
||||||
} catch (Exception e) {
|
|
||||||
return fail("Excel文件读取失败: " + e.getMessage(), result).setError(e.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
User loginUser = getLoginUser();
|
|
||||||
Integer importUserId = loginUser == null ? null : loginUser.getUserId();
|
|
||||||
Integer importMerchantId = merchantId != null ? merchantId : (loginUser == null ? 0 : (loginUser.getMerchantId() == null ? 0 : loginUser.getMerchantId()));
|
|
||||||
int importIsShow = defaultIsShow != null ? defaultIsShow : 0;
|
|
||||||
int importStock = defaultStock != null ? defaultStock : 0;
|
|
||||||
result.setMerchantId(importMerchantId);
|
|
||||||
|
|
||||||
DataFormatter formatter = new DataFormatter();
|
|
||||||
List<Goods> toInsert = new java.util.ArrayList<>();
|
|
||||||
try (ZipFile zipFile = new ZipFile(resolvedExcelFile.file);
|
|
||||||
InputStream in = new FileInputStream(resolvedExcelFile.file);
|
|
||||||
Workbook workbook = new XSSFWorkbook(in)) {
|
|
||||||
Sheet sheet = workbook.getSheet(targetSheetName);
|
|
||||||
if (sheet == null) {
|
|
||||||
return fail("未找到工作表: " + targetSheetName, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, String> cellImageIdToMediaEntry = buildCellImageIdToMediaEntry(zipFile);
|
|
||||||
Map<String, GoodsCategory> categoryByTitle = loadCategoryMap(importMerchantId);
|
|
||||||
|
|
||||||
Set<String> names = collectGoodsNames(sheet, formatter);
|
|
||||||
Set<String> existingNames = java.util.Collections.emptySet();
|
|
||||||
if (skipExisting && !names.isEmpty()) {
|
|
||||||
existingNames = goodsService.list(new LambdaQueryWrapper<Goods>()
|
|
||||||
.eq(Goods::getMerchantId, importMerchantId)
|
|
||||||
.in(Goods::getGoodsName, names))
|
|
||||||
.stream().map(Goods::getGoodsName).collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
int lastRow = sheet.getLastRowNum();
|
|
||||||
for (int i = 1; i <= lastRow; i++) {
|
|
||||||
Row row = sheet.getRow(i);
|
|
||||||
if (row == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String goodsName = getCellString(row, 2, formatter);
|
|
||||||
if (StrUtil.isBlank(goodsName)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
goodsName = goodsName.trim();
|
|
||||||
result.setTotalRows(result.getTotalRows() + 1);
|
|
||||||
|
|
||||||
if (skipExisting && existingNames.contains(goodsName)) {
|
|
||||||
result.setSkippedExists(result.getSkippedExists() + 1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String actionCategory = getCellString(row, 1, formatter);
|
|
||||||
String brand = getCellString(row, 3, formatter);
|
|
||||||
String manufacturer = getCellString(row, 4, formatter);
|
|
||||||
String spec = getCellString(row, 6, formatter);
|
|
||||||
String categoryName = getCellString(row, 10, formatter);
|
|
||||||
String supplierName = getCellString(row, 11, formatter);
|
|
||||||
|
|
||||||
BigDecimal costPrice = getCellDecimal(row, 8, formatter);
|
|
||||||
BigDecimal salePrice = getCellDecimal(row, 9, formatter);
|
|
||||||
Integer sales = parseSales(getCellString(row, 5, formatter));
|
|
||||||
|
|
||||||
Goods goods = new Goods();
|
|
||||||
goods.setGoodsName(goodsName);
|
|
||||||
goods.setType(0);
|
|
||||||
goods.setSpecs(0);
|
|
||||||
goods.setDeductStockType(10);
|
|
||||||
goods.setStock(importStock);
|
|
||||||
goods.setIsShow(importIsShow);
|
|
||||||
goods.setStatus(1);
|
|
||||||
goods.setMerchantId(importMerchantId);
|
|
||||||
goods.setUserId(importUserId);
|
|
||||||
goods.setUnitName(spec);
|
|
||||||
goods.setSupplierName(supplierName);
|
|
||||||
goods.setSales(sales);
|
|
||||||
goods.setPrice(costPrice);
|
|
||||||
goods.setSalePrice(salePrice != null ? salePrice : costPrice);
|
|
||||||
goods.setOriginPrice(goods.getSalePrice());
|
|
||||||
goods.setCategoryName(categoryName);
|
|
||||||
goods.setComments(buildComments(actionCategory, brand, manufacturer));
|
|
||||||
|
|
||||||
if (StrUtil.isNotBlank(categoryName)) {
|
|
||||||
GoodsCategory category = categoryByTitle.get(categoryName.trim());
|
|
||||||
if (category == null && createCategory) {
|
|
||||||
category = createCategory(importMerchantId, categoryName.trim());
|
|
||||||
categoryByTitle.put(category.getTitle(), category);
|
|
||||||
result.setCreatedCategories(result.getCreatedCategories() + 1);
|
|
||||||
}
|
|
||||||
if (category != null) {
|
|
||||||
goods.setCategoryId(category.getCategoryId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String imageId = extractDispimgId(row.getCell(7));
|
|
||||||
if (StrUtil.isNotBlank(imageId)) {
|
|
||||||
String mediaEntry = cellImageIdToMediaEntry.get(imageId);
|
|
||||||
if (StrUtil.isNotBlank(mediaEntry)) {
|
|
||||||
String url = saveCellImage(zipFile, mediaEntry, imageId);
|
|
||||||
if (StrUtil.isNotBlank(url)) {
|
|
||||||
goods.setImage(url);
|
|
||||||
result.setSavedImages(result.getSavedImages() + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toInsert.add(goods);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (toInsert.isEmpty()) {
|
|
||||||
return success("未读取到可导入的数据", result);
|
|
||||||
}
|
|
||||||
if (goodsService.saveBatch(toInsert)) {
|
|
||||||
result.setInserted(toInsert.size());
|
|
||||||
return success("导入成功", result);
|
|
||||||
}
|
|
||||||
return fail("导入失败", result);
|
|
||||||
} catch (Exception e) {
|
|
||||||
return fail("导入异常: " + e.getMessage(), result).setError(e.toString());
|
|
||||||
} finally {
|
|
||||||
resolvedExcelFile.cleanupQuietly();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class ResolvedExcelFile {
|
|
||||||
private final File file;
|
|
||||||
private final boolean deleteOnClose;
|
|
||||||
|
|
||||||
private ResolvedExcelFile(File file, boolean deleteOnClose) {
|
|
||||||
this.file = file;
|
|
||||||
this.deleteOnClose = deleteOnClose;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void cleanupQuietly() {
|
|
||||||
if (!deleteOnClose) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
FileUtil.del(file);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ResolvedExcelFile resolveExcelFile(String excelPath) throws Exception {
|
|
||||||
if (StrUtil.isBlank(excelPath)) {
|
|
||||||
throw new IllegalArgumentException("Excel路径为空");
|
|
||||||
}
|
|
||||||
String trimmed = excelPath.trim();
|
|
||||||
if (StrUtil.startWithIgnoreCase(trimmed, "http://") || StrUtil.startWithIgnoreCase(trimmed, "https://")) {
|
|
||||||
String suffix = ".xlsx";
|
|
||||||
String withoutQuery = StrUtil.subBefore(trimmed, "?", false);
|
|
||||||
int dot = withoutQuery.lastIndexOf('.');
|
|
||||||
if (dot > 0 && dot < withoutQuery.length() - 1) {
|
|
||||||
suffix = "." + withoutQuery.substring(dot + 1);
|
|
||||||
}
|
|
||||||
File tmp = File.createTempFile("goods-import-", suffix);
|
|
||||||
HttpUtil.downloadFile(trimmed, tmp);
|
|
||||||
if (!tmp.exists() || tmp.length() == 0) {
|
|
||||||
FileUtil.del(tmp);
|
|
||||||
throw new IllegalStateException("Excel下载失败或文件为空");
|
|
||||||
}
|
|
||||||
return new ResolvedExcelFile(tmp, true);
|
|
||||||
}
|
|
||||||
if (StrUtil.startWithIgnoreCase(trimmed, "file:")) {
|
|
||||||
File f = new File(URI.create(trimmed));
|
|
||||||
if (!f.exists()) {
|
|
||||||
throw new IllegalArgumentException("Excel文件不存在: " + excelPath);
|
|
||||||
}
|
|
||||||
return new ResolvedExcelFile(f, false);
|
|
||||||
}
|
|
||||||
if (!FileUtil.exist(trimmed)) {
|
|
||||||
throw new IllegalArgumentException("Excel文件不存在: " + excelPath);
|
|
||||||
}
|
|
||||||
return new ResolvedExcelFile(FileUtil.file(trimmed), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, GoodsCategory> loadCategoryMap(Integer merchantId) {
|
|
||||||
List<GoodsCategory> categories = goodsCategoryService.list(new LambdaQueryWrapper<GoodsCategory>()
|
|
||||||
.eq(GoodsCategory::getMerchantId, merchantId)
|
|
||||||
.eq(GoodsCategory::getDeleted, 0));
|
|
||||||
return categories.stream()
|
|
||||||
.filter(c -> StrUtil.isNotBlank(c.getTitle()))
|
|
||||||
.collect(Collectors.toMap(GoodsCategory::getTitle, c -> c, (a, b) -> a));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<String> collectGoodsNames(Sheet sheet, DataFormatter formatter) {
|
|
||||||
int lastRow = sheet.getLastRowNum();
|
|
||||||
return java.util.stream.IntStream.rangeClosed(1, lastRow)
|
|
||||||
.mapToObj(sheet::getRow)
|
|
||||||
.filter(r -> r != null)
|
|
||||||
.map(r -> getCellString(r, 2, formatter))
|
|
||||||
.filter(StrUtil::isNotBlank)
|
|
||||||
.map(String::trim)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
private GoodsCategory createCategory(Integer merchantId, String title) {
|
|
||||||
GoodsCategory category = new GoodsCategory();
|
|
||||||
category.setTitle(title);
|
|
||||||
category.setType(0);
|
|
||||||
category.setParentId(0);
|
|
||||||
category.setMerchantId(merchantId);
|
|
||||||
category.setStatus(0);
|
|
||||||
category.setSortNumber(0);
|
|
||||||
goodsCategoryService.save(category);
|
|
||||||
return category;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String buildComments(String actionCategory, String brand, String manufacturer) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
if (StrUtil.isNotBlank(actionCategory)) sb.append("作用分类:").append(actionCategory.trim()).append(" ");
|
|
||||||
if (StrUtil.isNotBlank(brand)) sb.append("品牌:").append(brand.trim()).append(" ");
|
|
||||||
if (StrUtil.isNotBlank(manufacturer)) sb.append("厂家:").append(manufacturer.trim()).append(" ");
|
|
||||||
return sb.toString().trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getCellString(Row row, int cellIndex, DataFormatter formatter) {
|
|
||||||
Cell cell = row.getCell(cellIndex, Row.MissingCellPolicy.RETURN_BLANK_AS_NULL);
|
|
||||||
if (cell == null) return null;
|
|
||||||
String v = formatter.formatCellValue(cell);
|
|
||||||
return StrUtil.trimToNull(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BigDecimal getCellDecimal(Row row, int cellIndex, DataFormatter formatter) {
|
|
||||||
String v = getCellString(row, cellIndex, formatter);
|
|
||||||
if (StrUtil.isBlank(v)) return null;
|
|
||||||
v = v.replace(",", "").replace("元", "").trim();
|
|
||||||
if (v.endsWith("+")) v = v.substring(0, v.length() - 1);
|
|
||||||
try {
|
|
||||||
return new BigDecimal(v);
|
|
||||||
} catch (Exception e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Integer parseSales(String v) {
|
|
||||||
if (StrUtil.isBlank(v)) return null;
|
|
||||||
Matcher m = Pattern.compile("(\\d+)").matcher(v);
|
|
||||||
if (m.find()) {
|
|
||||||
try {
|
|
||||||
return Integer.parseInt(m.group(1));
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String extractDispimgId(Cell cell) {
|
|
||||||
if (cell == null) return null;
|
|
||||||
String formula = null;
|
|
||||||
try {
|
|
||||||
if (cell.getCellType() == CellType.FORMULA) {
|
|
||||||
formula = cell.getCellFormula();
|
|
||||||
}
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
String v = StrUtil.blankToDefault(formula, cell.toString());
|
|
||||||
Matcher matcher = Pattern.compile("ID_[0-9A-Fa-f]+").matcher(v);
|
|
||||||
return matcher.find() ? matcher.group() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String saveCellImage(ZipFile zipFile, String mediaEntryName, String imageId) {
|
|
||||||
ZipEntry entry = zipFile.getEntry(mediaEntryName);
|
|
||||||
if (entry == null) return null;
|
|
||||||
String ext = ".jpg";
|
|
||||||
int dot = mediaEntryName.lastIndexOf('.');
|
|
||||||
if (dot > 0 && dot < mediaEntryName.length() - 1) {
|
|
||||||
ext = "." + mediaEntryName.substring(dot + 1);
|
|
||||||
}
|
|
||||||
String relativePath = "goodsImport/" + imageId + ext;
|
|
||||||
File out = FileUtil.file(uploadPath, relativePath);
|
|
||||||
try {
|
|
||||||
if (!out.exists()) {
|
|
||||||
FileUtil.mkParentDirs(out);
|
|
||||||
try (InputStream in = zipFile.getInputStream(entry)) {
|
|
||||||
FileUtil.writeFromStream(in, out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String fileServer = config.getFileServer();
|
|
||||||
if (StrUtil.isBlank(fileServer)) {
|
|
||||||
return serverUrl.concat("/").concat(relativePath);
|
|
||||||
}
|
|
||||||
return StrUtil.removeSuffix(fileServer, "/") + "/api/file/" + relativePath;
|
|
||||||
} catch (Exception e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Map<String, String> buildCellImageIdToMediaEntry(ZipFile zipFile) {
|
|
||||||
final String PKG_REL_NS = "http://schemas.openxmlformats.org/package/2006/relationships";
|
|
||||||
final String XDR_NS = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing";
|
|
||||||
final String A_NS = "http://schemas.openxmlformats.org/drawingml/2006/main";
|
|
||||||
final String R_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
|
||||||
|
|
||||||
Map<String, String> rIdToTarget = new HashMap<>();
|
|
||||||
Map<String, String> idToMedia = new HashMap<>();
|
|
||||||
|
|
||||||
try {
|
|
||||||
ZipEntry relsEntry = zipFile.getEntry("xl/_rels/cellimages.xml.rels");
|
|
||||||
ZipEntry cellImagesEntry = zipFile.getEntry("xl/cellimages.xml");
|
|
||||||
if (relsEntry == null || cellImagesEntry == null) {
|
|
||||||
return idToMedia;
|
|
||||||
}
|
|
||||||
|
|
||||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
|
||||||
dbf.setNamespaceAware(true);
|
|
||||||
|
|
||||||
Document relsDoc;
|
|
||||||
try (InputStream relsIn = zipFile.getInputStream(relsEntry)) {
|
|
||||||
relsDoc = dbf.newDocumentBuilder().parse(relsIn);
|
|
||||||
}
|
|
||||||
NodeList relNodes = relsDoc.getElementsByTagNameNS(PKG_REL_NS, "Relationship");
|
|
||||||
for (int i = 0; i < relNodes.getLength(); i++) {
|
|
||||||
Element el = (Element) relNodes.item(i);
|
|
||||||
String id = el.getAttribute("Id");
|
|
||||||
String target = el.getAttribute("Target");
|
|
||||||
if (StrUtil.isNotBlank(id) && StrUtil.isNotBlank(target)) {
|
|
||||||
String normalizedTarget = target.startsWith("/") ? target.substring(1) : target;
|
|
||||||
rIdToTarget.put(id, "xl/" + normalizedTarget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Document imagesDoc;
|
|
||||||
try (InputStream imagesIn = zipFile.getInputStream(cellImagesEntry)) {
|
|
||||||
imagesDoc = dbf.newDocumentBuilder().parse(imagesIn);
|
|
||||||
}
|
|
||||||
NodeList pics = imagesDoc.getElementsByTagNameNS(XDR_NS, "pic");
|
|
||||||
for (int i = 0; i < pics.getLength(); i++) {
|
|
||||||
Element pic = (Element) pics.item(i);
|
|
||||||
NodeList cNvPrNodes = pic.getElementsByTagNameNS(XDR_NS, "cNvPr");
|
|
||||||
NodeList blipNodes = pic.getElementsByTagNameNS(A_NS, "blip");
|
|
||||||
if (cNvPrNodes.getLength() == 0 || blipNodes.getLength() == 0) continue;
|
|
||||||
|
|
||||||
Element cNvPr = (Element) cNvPrNodes.item(0);
|
|
||||||
Element blip = (Element) blipNodes.item(0);
|
|
||||||
String name = cNvPr.getAttribute("name");
|
|
||||||
String embed = blip.getAttributeNS(R_NS, "embed");
|
|
||||||
String target = rIdToTarget.get(embed);
|
|
||||||
if (StrUtil.isNotBlank(name) && StrUtil.isNotBlank(target)) {
|
|
||||||
idToMedia.put(name, target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
return idToMedia;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOperation("生成海报")
|
@ApiOperation("生成海报")
|
||||||
@PostMapping("/make-goods-poster/{goodsId}")
|
@PostMapping("/make-goods-poster/{goodsId}")
|
||||||
public ApiResult<?> makePoster(@PathVariable Integer goodsId) throws Exception {
|
public ApiResult<?> makePoster(@PathVariable Integer goodsId) throws Exception {
|
||||||
@@ -811,4 +624,239 @@ public class GoodsController extends BaseController {
|
|||||||
return success("生成成功", serverUrl.concat("/" + filePath));
|
return success("生成成功", serverUrl.concat("/" + filePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<GoodsImportParam> tryImport(MultipartFile file, int titleRows, int headRows) throws Exception {
|
||||||
|
ImportParams importParams = new ImportParams();
|
||||||
|
importParams.setTitleRows(titleRows);
|
||||||
|
importParams.setHeadRows(headRows);
|
||||||
|
importParams.setStartSheetIndex(0);
|
||||||
|
importParams.setSheetNum(1);
|
||||||
|
return ExcelImportUtil.importExcel(file.getInputStream(), GoodsImportParam.class, importParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取指定表头列的超链接地址,按数据行顺序返回(key从0开始)。
|
||||||
|
*/
|
||||||
|
private Map<Integer, String> readHyperlinksByHeader(MultipartFile file, int sheetIndex, int titleRows, int headRows, String headerName) throws Exception {
|
||||||
|
Map<Integer, String> result = new HashMap<>();
|
||||||
|
if (isBlank(headerName)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
try (InputStream is = file.getInputStream(); org.apache.poi.ss.usermodel.Workbook workbook = WorkbookFactory.create(is)) {
|
||||||
|
Sheet sheet = workbook.getSheetAt(sheetIndex);
|
||||||
|
if (sheet == null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
int headerRowNum = titleRows + headRows - 1;
|
||||||
|
Row headerRow = sheet.getRow(headerRowNum);
|
||||||
|
if (headerRow == null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
int colIndex = -1;
|
||||||
|
for (int c = headerRow.getFirstCellNum(); c < headerRow.getLastCellNum(); c++) {
|
||||||
|
Cell cell = headerRow.getCell(c);
|
||||||
|
if (cell != null && headerName.equals(cell.getStringCellValue())) {
|
||||||
|
colIndex = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (colIndex < 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
int dataStartRow = titleRows + headRows;
|
||||||
|
for (int r = dataStartRow; r <= sheet.getLastRowNum(); r++) {
|
||||||
|
Row row = sheet.getRow(r);
|
||||||
|
if (row == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Cell cell = row.getCell(colIndex);
|
||||||
|
if (cell != null && cell.getHyperlink() != null) {
|
||||||
|
String address = cell.getHyperlink().getAddress();
|
||||||
|
if (!isBlank(address)) {
|
||||||
|
result.put(r - dataStartRow, address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤掉完全空白的导入行,避免空行导致导入失败
|
||||||
|
*/
|
||||||
|
private List<GoodsImportParam> filterEmptyRows(List<GoodsImportParam> rawList) {
|
||||||
|
if (rawList == null || rawList.isEmpty()) {
|
||||||
|
return rawList;
|
||||||
|
}
|
||||||
|
rawList.removeIf(this::isEmptyImportRow);
|
||||||
|
return rawList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEmptyImportRow(GoodsImportParam param) {
|
||||||
|
if (param == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return isBlank(param.getGoodsName())
|
||||||
|
&& isBlank(param.getCode())
|
||||||
|
&& isBlank(param.getCategory())
|
||||||
|
&& isBlank(param.getCompany())
|
||||||
|
&& isBlank(param.getSupplierName())
|
||||||
|
&& isBlank(param.getSalePrice())
|
||||||
|
&& isBlank(param.getCostPrice());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isBlank(String value) {
|
||||||
|
return value == null || value.trim().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Merchant resolveMerchantByName(String merchantName, Map<String, Merchant> cache) {
|
||||||
|
if (isBlank(merchantName)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (cache.containsKey(merchantName)) {
|
||||||
|
return cache.get(merchantName);
|
||||||
|
}
|
||||||
|
Merchant merchant = merchantService.getOne(
|
||||||
|
new LambdaQueryWrapper<Merchant>()
|
||||||
|
.eq(Merchant::getMerchantName, merchantName)
|
||||||
|
.last("limit 1")
|
||||||
|
);
|
||||||
|
cache.put(merchantName, merchant);
|
||||||
|
return merchant;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Goods convertImportParamToEntity(GoodsImportParam param) {
|
||||||
|
Goods entity = new Goods();
|
||||||
|
if (param == null) {
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
entity.setCode(trimToNull(param.getCode()));
|
||||||
|
entity.setGoodsName(trimToNull(param.getGoodsName()));
|
||||||
|
entity.setEnsureTag(trimToNull(param.getEnsureTag()));
|
||||||
|
entity.setCategory(trimToNull(param.getCategory()));
|
||||||
|
entity.setUnitName(trimToNull(param.getUnitName()));
|
||||||
|
entity.setImage(trimToNull(param.getImage()));
|
||||||
|
// 兼容数据库 shop_goods.files 非空约束:默认使用image或空串兜底
|
||||||
|
entity.setFiles(!isBlank(entity.getImage()) ? entity.getImage() : "");
|
||||||
|
entity.setSupplierName(trimToNull(param.getSupplierName()));
|
||||||
|
entity.setMerchantName(trimToNull(param.getCompany()));
|
||||||
|
|
||||||
|
Integer sales = parseSales(param.getSales());
|
||||||
|
if (sales != null) {
|
||||||
|
entity.setSales(sales);
|
||||||
|
}
|
||||||
|
|
||||||
|
BigDecimal cost = parseBigDecimal(param.getCostPrice());
|
||||||
|
BigDecimal sale = parseBigDecimal(param.getSalePrice());
|
||||||
|
if (cost != null) {
|
||||||
|
entity.setPrice(cost);
|
||||||
|
}
|
||||||
|
if (sale != null) {
|
||||||
|
entity.setSalePrice(sale);
|
||||||
|
entity.setOriginPrice(sale);
|
||||||
|
} else if (cost != null) {
|
||||||
|
entity.setSalePrice(cost);
|
||||||
|
entity.setOriginPrice(cost);
|
||||||
|
}
|
||||||
|
|
||||||
|
String comments = buildComments(param);
|
||||||
|
if (!isBlank(comments)) {
|
||||||
|
entity.setComments(comments);
|
||||||
|
}
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mergeImportedToExisting(Goods existing, Goods incoming) {
|
||||||
|
if (existing == null || incoming == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isBlank(incoming.getCode())) {
|
||||||
|
existing.setCode(incoming.getCode());
|
||||||
|
}
|
||||||
|
if (!isBlank(incoming.getEnsureTag())) {
|
||||||
|
existing.setEnsureTag(incoming.getEnsureTag());
|
||||||
|
}
|
||||||
|
if (!isBlank(incoming.getCategory())) {
|
||||||
|
existing.setCategory(incoming.getCategory());
|
||||||
|
}
|
||||||
|
if (!isBlank(incoming.getUnitName())) {
|
||||||
|
existing.setUnitName(incoming.getUnitName());
|
||||||
|
}
|
||||||
|
if (!isBlank(incoming.getImage())) {
|
||||||
|
existing.setImage(incoming.getImage());
|
||||||
|
}
|
||||||
|
if (!isBlank(incoming.getFiles())) {
|
||||||
|
existing.setFiles(incoming.getFiles());
|
||||||
|
}
|
||||||
|
if (!isBlank(incoming.getSupplierName())) {
|
||||||
|
existing.setSupplierName(incoming.getSupplierName());
|
||||||
|
}
|
||||||
|
if (!isBlank(incoming.getMerchantName())) {
|
||||||
|
existing.setMerchantName(incoming.getMerchantName());
|
||||||
|
}
|
||||||
|
if (!isBlank(incoming.getComments())) {
|
||||||
|
existing.setComments(incoming.getComments());
|
||||||
|
}
|
||||||
|
if (incoming.getSales() != null) {
|
||||||
|
existing.setSales(incoming.getSales());
|
||||||
|
}
|
||||||
|
if (incoming.getPrice() != null) {
|
||||||
|
existing.setPrice(incoming.getPrice());
|
||||||
|
}
|
||||||
|
if (incoming.getSalePrice() != null) {
|
||||||
|
existing.setSalePrice(incoming.getSalePrice());
|
||||||
|
}
|
||||||
|
if (incoming.getOriginPrice() != null) {
|
||||||
|
existing.setOriginPrice(incoming.getOriginPrice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal parseBigDecimal(String value) {
|
||||||
|
if (isBlank(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String v = value.trim();
|
||||||
|
// 去掉可能存在的中文/符号
|
||||||
|
v = v.replace(",", ",");
|
||||||
|
v = v.replaceAll("[^0-9+\\-.,]", "");
|
||||||
|
v = v.replace(",", "");
|
||||||
|
if (isBlank(v)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new BigDecimal(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer parseSales(String value) {
|
||||||
|
if (isBlank(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String digits = value.replaceAll("[^0-9]", "");
|
||||||
|
if (isBlank(digits)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(digits);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String trimToNull(String value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String v = value.trim();
|
||||||
|
return v.isEmpty() ? null : v;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildComments(GoodsImportParam param) {
|
||||||
|
if (param == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String brand = trimToNull(param.getBrand());
|
||||||
|
if (brand == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return "品牌:" + brand;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
57
src/main/java/com/gxwebsoft/shop/param/GoodsImportParam.java
Normal file
57
src/main/java/com/gxwebsoft/shop/param/GoodsImportParam.java
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package com.gxwebsoft.shop.param;
|
||||||
|
|
||||||
|
import cn.afterturn.easypoi.excel.annotation.Excel;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品导入参数(对应 goods-import.xlsx 第一页)
|
||||||
|
*
|
||||||
|
* 表头:序号、作用分类(除消费者外可见)、品种、品牌、厂家、销量、规格、产品图片、成本(来华+运费)、商城售价(无标注按厂家)、产品分类(所有可见)、公司
|
||||||
|
*
|
||||||
|
* 说明:产品图片列支持直接填写图片URL,或在单元格中设置超链接(导入时读取超链接地址)。
|
||||||
|
*
|
||||||
|
* @author 科技小王子
|
||||||
|
* @since 2025-12-30
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class GoodsImportParam implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Excel(name = "序号")
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
@Excel(name = "作用分类(除消费者外可见)")
|
||||||
|
private String ensureTag;
|
||||||
|
|
||||||
|
@Excel(name = "品种")
|
||||||
|
private String goodsName;
|
||||||
|
|
||||||
|
@Excel(name = "品牌")
|
||||||
|
private String brand;
|
||||||
|
|
||||||
|
@Excel(name = "厂家")
|
||||||
|
private String supplierName;
|
||||||
|
|
||||||
|
@Excel(name = "销量")
|
||||||
|
private String sales;
|
||||||
|
|
||||||
|
@Excel(name = "规格")
|
||||||
|
private String unitName;
|
||||||
|
|
||||||
|
@Excel(name = "产品图片")
|
||||||
|
private String image;
|
||||||
|
|
||||||
|
@Excel(name = "成本(来华+运费)")
|
||||||
|
private String costPrice;
|
||||||
|
|
||||||
|
@Excel(name = "商城售价(无标注按厂家)")
|
||||||
|
private String salePrice;
|
||||||
|
|
||||||
|
@Excel(name = "产品分类(所有可见)")
|
||||||
|
private String category;
|
||||||
|
|
||||||
|
@Excel(name = "公司")
|
||||||
|
private String company;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user