Compare commits

...

4 Commits

Author SHA1 Message Date
6e01f585f6 Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	.idea/workspace.xml
#	src/main/java/com/gxwebsoft/shop/controller/GoodsController.java
2025-12-31 09:57:42 +08:00
be52c21ee9 新增:批量导入商品 2025-12-31 09:56:45 +08:00
67fa6a1f14 新增:批量导入商品功能 2025-12-30 17:19:06 +08:00
33a04d7f07 feat(goods): 添加Excel批量导入商品功能
- 集成POI库支持Excel文件读取和解析
- 实现商品信息从Excel表格到数据库的批量导入
- 支持商品分类自动创建和映射
- 添加商品图片从Excel单元格提取和保存功能
- 实现导入过程中的数据验证和错误处理
- 提供导入结果统计和反馈信息
- 支持跳过已存在商品的可选配置
- 添加导入参数的灵活配置选项
2025-12-29 15:07:24 +08:00
11 changed files with 739 additions and 4 deletions

199
.idea/workspace.xml generated Normal file
View File

@@ -0,0 +1,199 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="6e090a32-8359-42c2-9d7b-de1a55875c40" name="更改" comment="新增:批量导入商品功能">
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/CreditBreachOfTrustController.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/CreditCaseFilingController.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/com/gxwebsoft/credit/controller/CreditCompanyController.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" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"git-widget-placeholder": "dev",
"last_opened_file_path": "/Users/gxwebsoft/JAVA/guofu-shop/src/main/java/com/gxwebsoft",
"应用程序.WebSoftApplication.executor": "Run"
}
}]]></component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/src/main/java/com/gxwebsoft" />
</key>
</component>
<component name="RunManager">
<configuration name="WebSoftApplication" type="Application" factoryName="Application" temporary="true" nameIsGenerated="true">
<option name="MAIN_CLASS_NAME" value="com.gxwebsoft.WebSoftApplication" />
<module name="com-gxwebsoft-modules" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="com.gxwebsoft.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<recent_temporary>
<list>
<item itemvalue="应用程序.WebSoftApplication" />
</list>
</recent_temporary>
</component>
<component name="TaskManager">
<servers />
</component>
</project>

BIN
.mvn/wrapper/maven-wrapper.jar vendored Normal file

Binary file not shown.

2
.mvn/wrapper/maven-wrapper.properties vendored Normal file
View 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

0
mvnw vendored Normal file → Executable file
View File

View File

@@ -10,7 +10,7 @@ import java.io.Serializable;
@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value = "幻灯片广告", description = "幻灯片广告")
@ApiModel(value = "幻灯片广告", description = "幻灯片广告")
public class SideItemVo implements Serializable {
@ApiModelProperty("ID")

View File

@@ -15,7 +15,7 @@ import java.io.Serializable;
*/
@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value = "Setting对象", description = "缓存管理")
@ApiModel(value = "Cache对象", description = "缓存管理")
public class Cache implements Serializable {
private static final long serialVersionUID = 1L;

View File

@@ -21,7 +21,7 @@ import java.util.Set;
@Data
@EqualsAndHashCode(callSuper = false)
@JsonInclude(JsonInclude.Include.NON_NULL)
@ApiModel(value = "CompanyParam对象", description = "企业信息查询参数")
@ApiModel(value = "OaCompanyParam对象", description = "企业信息查询参数(OA)")
public class CompanyParam extends BaseParam {
private static final long serialVersionUID = 1L;

View File

@@ -1,5 +1,9 @@
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.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaQrcodeServiceImpl;
@@ -18,6 +22,7 @@ import com.gxwebsoft.common.system.entity.DictData;
import com.gxwebsoft.shop.entity.*;
import com.gxwebsoft.shop.service.*;
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.PageResult;
import com.gxwebsoft.common.core.web.PageParam;
@@ -26,20 +31,32 @@ import com.gxwebsoft.common.core.annotation.OperationLog;
import com.gxwebsoft.common.system.entity.User;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.poi.ss.usermodel.Cell;
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.Value;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 商品记录表控制器
@@ -240,6 +257,195 @@ public class GoodsController extends BaseController {
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("统计信息")
@GetMapping("/data")
public ApiResult<Map<String, Integer>> data(GoodsParam param) {
@@ -418,4 +624,239 @@ public class GoodsController extends BaseController {
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;
}
}

View 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;
}

View File

@@ -15,7 +15,7 @@ import java.util.List;
@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value = "Cart对象", description = "购物车")
@ApiModel(value = "CartVo对象", description = "购物车(返回结构)")
@TableName("shop_cart")
public class CartVo implements Serializable {

View File

@@ -0,0 +1,36 @@
package com.gxwebsoft.shop.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
@Data
@ApiModel(value = "GoodsExcelImportResult", description = "商品Excel导入结果")
public class GoodsExcelImportResult implements Serializable {
@ApiModelProperty("Excel文件路径")
private String excelPath;
@ApiModelProperty("工作表名称")
private String sheetName;
@ApiModelProperty("导入到的商户ID")
private Integer merchantId;
@ApiModelProperty("扫描到的有效行数(有品种/商品名)")
private int totalRows;
@ApiModelProperty("成功入库数量")
private int inserted;
@ApiModelProperty("跳过(已存在同名商品)")
private int skippedExists;
@ApiModelProperty("自动创建的分类数量")
private int createdCategories;
@ApiModelProperty("成功导出的图片数量")
private int savedImages;
}