From bff10a587ef44d38da28d2bd07e9172e34aeadaf Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Tue, 20 May 2025 18:10:37 +0800 Subject: [PATCH 1/6] =?UTF-8?q?im=E9=A1=B9=E7=9B=AE=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E9=95=BF=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../websocket/service/onchat/MallsuiteImSocketHandler.java | 4 +++- mall-im/src/main/java/com/suisung/mall/im/pojo/vo/SendVO.java | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/mall-im/src/main/java/com/suisung/mall/im/common/websocket/service/onchat/MallsuiteImSocketHandler.java b/mall-im/src/main/java/com/suisung/mall/im/common/websocket/service/onchat/MallsuiteImSocketHandler.java index b106263b..3e552707 100644 --- a/mall-im/src/main/java/com/suisung/mall/im/common/websocket/service/onchat/MallsuiteImSocketHandler.java +++ b/mall-im/src/main/java/com/suisung/mall/im/common/websocket/service/onchat/MallsuiteImSocketHandler.java @@ -205,7 +205,9 @@ public class MallsuiteImSocketHandler implements WebSocketHandler { if (!mine.getId().equals(to.getId())) { sendVO.setMine(false); - + if ("voice".equals(receiveDTO.getMine().getType()) || "video".equals(receiveDTO.getMine().getType())) { + sendVO.setMessage_length(receiveDTO.getMine().getContent()); + } //保存聊天记录 if ("friend".equals(type) || "user".equals(type)) { //如果是私聊 diff --git a/mall-im/src/main/java/com/suisung/mall/im/pojo/vo/SendVO.java b/mall-im/src/main/java/com/suisung/mall/im/pojo/vo/SendVO.java index ddf36383..1d71302c 100644 --- a/mall-im/src/main/java/com/suisung/mall/im/pojo/vo/SendVO.java +++ b/mall-im/src/main/java/com/suisung/mall/im/pojo/vo/SendVO.java @@ -76,6 +76,9 @@ public class SendVO implements Serializable { @ApiModelProperty(value = "和状态一致") private Map msg; + @ApiModelProperty(value = "消息长度") + private String message_length; + @Override public String toString() { From 1459581e371b8ef4f910aaa6f81f51fd28d6ae84 Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Tue, 20 May 2025 17:43:02 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E5=95=86=E7=91=9E9.7=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=9C=8D=E5=8A=A1=E7=AB=AF=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../suisung/mall/common/enums/DicEnum.java | 88 ++ .../common/modules/sixun/SxSyncGoods.java | 3 +- .../common/modules/sync/StoreDbConfig.java | 87 ++ .../mall/common/modules/sync/SyncFileLog.java | 103 +++ .../mall/common/utils/ContextUtil.java | 24 +- mall-shop/pom.xml | 6 + .../ShopBaseProductCategoryService.java | 6 + .../impl/AccountBaseConfigServiceImpl.java | 1 + .../ShopBaseProductCategoryServiceImpl.java | 23 + .../impl/ShopBaseProductTypeServiceImpl.java | 3 +- .../ShopChainUserRightsBaseServiceImpl.java | 1 - .../mall/shop/config/GlobalCorsConfig.java | 30 + .../suisung/mall/shop/config/WebConfig.java | 12 + .../number/mapper/ShopNumberSeqMapper.java | 5 + .../number/service/ShopNumberSeqService.java | 7 + .../impl/ShopNumberSeqServiceImpl.java | 130 +++ .../mall/shop/page/service/OssService.java | 24 + .../page/service/impl/OssServiceImpl.java | 51 +- .../ShopProductAssistIndexService.java | 4 + .../service/ShopProductBaseService.java | 6 + .../service/ShopProductImageService.java | 5 + .../ShopProductAssistIndexServiceImpl.java | 26 + .../impl/ShopProductBaseServiceImpl.java | 855 +++++++++++++++++- .../impl/ShopProductImageServiceImpl.java | 29 + .../suisung/mall/shop/sixun/dao/BaseDao.java | 148 +++ .../mall/shop/sixun/dao/SxDataDao.java | 238 +++++ .../mall/shop/sixun/dto/DataBaseInfo.java | 16 + .../mall/shop/sixun/dto/ProductImage.java | 17 + .../mall/shop/sixun/dto/PromotionDetail.java | 17 + .../mall/shop/sixun/dto/ResultDto.java | 15 + .../mall/shop/sixun/dto/SxCategoryModel.java | 34 + .../mall/shop/sixun/dto/SxGoosModel.java | 103 +++ .../sixun/service/SxSyncCategoryService.java | 20 + .../sixun/service/SxSyncGoodsService.java | 17 + .../shop/sixun/service/SxSyncVipService.java | 15 + .../impl/SxSyncCategoryServiceImpl.java | 84 +- .../service/impl/SxSyncGoodsServiceImpl.java | 16 +- .../service/impl/SxSyncVipServiceImpl.java | 16 +- .../mall/shop/sixun/utils/FileUtils.java | 65 ++ .../shop/sync/Utils/BatchInsertUtils.java | 91 ++ .../mall/shop/sync/Utils/CryptoUtils.java | 117 +++ .../mall/shop/sync/Utils/ThreadFileUtils.java | 37 + .../sync/controller/ClientController.java | 29 + .../controller/StoreDbConfigController.java | 107 +++ .../controller/SyncThirdDataController.java | 65 +- .../mall/shop/sync/keymanage/RedisKey.java | 6 + .../shop/sync/mapper/StoreDbConfigMapper.java | 18 + .../shop/sync/mapper/SyncFileLogMapper.java | 17 + .../shop/sync/pool/ThreadPoolManager.java | 414 +++++++++ .../sync/service/StoreDbConfigService.java | 33 + .../shop/sync/service/SyncAppService.java | 11 + .../shop/sync/service/SyncFileLogService.java | 23 + .../sync/service/SyncThirdDataService.java | 29 + .../impl/StoreDbConfigServiceImpl.java | 187 ++++ .../sync/service/impl/SyncAppServiceImpl.java | 53 ++ .../service/impl/SyncBaseThirdSxAbstract.java | 695 ++++++++++++++ .../service/impl/SyncConfigServiceImpl.java | 2 + .../service/impl/SyncFileLogServiceImpl.java | 23 + .../impl/SyncThirdDataServiceImpl.java | 797 ++++++++-------- .../src/main/resources/bootstrap-dev.yml | 2 +- .../src/main/resources/bootstrap-local.yml | 7 +- .../src/main/resources/bootstrap-prod.yml | 2 +- .../src/main/resources/bootstrap-test.yml | 2 +- .../src/main/resources/bootstrap-uat.yml | 2 +- .../mapper/number/ShopNumberSeqMapper.xml | 27 + sql/shop/dev/20250520_ddl.sql | 51 ++ 66 files changed, 4790 insertions(+), 407 deletions(-) create mode 100644 mall-common/src/main/java/com/suisung/mall/common/enums/DicEnum.java create mode 100644 mall-common/src/main/java/com/suisung/mall/common/modules/sync/StoreDbConfig.java create mode 100644 mall-common/src/main/java/com/suisung/mall/common/modules/sync/SyncFileLog.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/config/GlobalCorsConfig.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sixun/dao/BaseDao.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sixun/dao/SxDataDao.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/DataBaseInfo.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/ProductImage.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/PromotionDetail.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/ResultDto.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/SxCategoryModel.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/SxGoosModel.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sixun/utils/FileUtils.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/Utils/BatchInsertUtils.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/Utils/CryptoUtils.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/Utils/ThreadFileUtils.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/ClientController.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/StoreDbConfigController.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/keymanage/RedisKey.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/mapper/StoreDbConfigMapper.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/mapper/SyncFileLogMapper.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/pool/ThreadPoolManager.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/service/StoreDbConfigService.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncFileLogService.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/StoreDbConfigServiceImpl.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncBaseThirdSxAbstract.java create mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncFileLogServiceImpl.java create mode 100644 sql/shop/dev/20250520_ddl.sql diff --git a/mall-common/src/main/java/com/suisung/mall/common/enums/DicEnum.java b/mall-common/src/main/java/com/suisung/mall/common/enums/DicEnum.java new file mode 100644 index 00000000..4629dd01 --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/enums/DicEnum.java @@ -0,0 +1,88 @@ +package com.suisung.mall.common.enums; + +public enum DicEnum { + + ERR_1003("1003","缺少必要参数","errCode","错误码","缺少必要参数"), + ERR_1001("1001","签名有误","errCode","错误码","签名有误"), + ERR_1004("1004","缺少必要参数","errCode","错误码","缺少必要参数"), + + DATA_SOURCE_1("1","自添加","dtaSource","数据来源","数据来源"), + DATA_SOURCE_2("2","缺少必要参数","dtaSource","数据来源","数据来源"), + + PRODUCT("1", "商品","product","同步类型","同步类型"), + PRODUCT_CATEGORY("2", "商品分类","productCategory","同步类型","数据来源"), + BRAND("3", "品牌","brand","同步类型","同步类型"), + MEMBER("4", "会员","member","同步类型","同步类型"), + + PENDING("0", "等待中","brand","同步类型","同步类型"), + PROCESSING("1", "进行中","brand","同步类型","同步类型"), + SUCCESS("2", "成功","brand","同步类型","同步类型"), + FAILED("3", "失败","brand","同步类型","同步类型"), + + SOURCE_SYSTEM_TYPE_1005("1005", "思迅","sourceSystemType","数据来源","数据来源"), + SOURCE_SYSTEM_TYPE_SELF("self", "蓝驰","sourceSystemType","数据来源","数据来源"), + TIMED("1", "定时同步","syncMode","同步类型","同步类型"), + INTERVAL("2", "间隔同步","syncMode","同步类型","同步类型"), + + YESORNO_0("0", "否","yesOrno","是否","是否"), + YESORNO_1("1", "是","yesOrno","是否","是否"), + ; + ; + private String code; + + private String value; + + private String dicType; + + private String dicName; + + private String desc; + + DicEnum(String code, String value, String dicType, String dicName, String desc) { + this.code = code; + this.value = value; + this.dicType = dicType; + this.dicName = dicName; + this.desc = desc; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getDicType() { + return dicType; + } + + public void setDicType(String dicType) { + this.dicType = dicType; + } + + public String getDicName() { + return dicName; + } + + public void setDicName(String dicName) { + this.dicName = dicName; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } +} diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/sixun/SxSyncGoods.java b/mall-common/src/main/java/com/suisung/mall/common/modules/sixun/SxSyncGoods.java index a5745389..40209967 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/sixun/SxSyncGoods.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/sixun/SxSyncGoods.java @@ -91,7 +91,7 @@ public class SxSyncGoods implements Serializable { private BigDecimal item_brand_name; @ApiModelProperty(value = "商品助记号") - private Integer item_rem; + private String item_rem; @ApiModelProperty(value = "生产日期") private String build_date; @@ -113,5 +113,6 @@ public class SxSyncGoods implements Serializable { @ApiModelProperty(value = "更新时间") private Date updated_at; + } diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/sync/StoreDbConfig.java b/mall-common/src/main/java/com/suisung/mall/common/modules/sync/StoreDbConfig.java new file mode 100644 index 00000000..7c979547 --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/sync/StoreDbConfig.java @@ -0,0 +1,87 @@ +package com.suisung.mall.common.modules.sync; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +@Data +@TableName("store_db_config") +@ApiModel(value = "StoreDbConfig对象", description = "店铺数据库连接配置表") +public class StoreDbConfig implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.AUTO) + @ApiModelProperty(value = "主键ID") + private Long id; + + @TableField("store_id") + @ApiModelProperty(value = "店铺ID") + private String storeId; + + @TableField("db_type") + @ApiModelProperty(value = "数据库类型(mysql/oracle/sqlserver等)") + private String dbType = "sqlserver"; + + @TableField("db_name") + @ApiModelProperty(value = "数据库名称") + private String dbName; + + @TableField("db_ip") + @ApiModelProperty(value = "数据库IP地址") + private String dbIp; + + @TableField("db_port") + @ApiModelProperty(value = "数据库端口") + private Integer dbPort = 3306; + + @TableField("db_username") + @ApiModelProperty(value = "数据库用户名") + private String dbUsername; + + @TableField("db_password") + @ApiModelProperty(value = "数据库密码(建议加密存储)") + private String dbPassword; + + @TableField("has_internet") + @ApiModelProperty(value = "是否有外网访问(0:无,1:有)") + private String hasInternet; + + @TableField("sync_mode") + @ApiModelProperty(value = "同步模式(1:定时同步,2:间隔同步)") + private String syncMode; + + @TableField("has_start") + @ApiModelProperty(value = "是否启用(0:否,1:是)") + private String hasStart; + + @TableField("cron_expression") + @ApiModelProperty(value = "定时同步的cron表达式") + private String cronExpression; + + @TableField("category_name") + @ApiModelProperty(value = "商品分类") + private String categoryName; + + @TableField("create_time") + @ApiModelProperty(value = "创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @TableField("update_time") + @ApiModelProperty(value = "更新时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + @TableField("remark") + @ApiModelProperty(value = "备注信息") + private String remark; +} \ No newline at end of file diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/sync/SyncFileLog.java b/mall-common/src/main/java/com/suisung/mall/common/modules/sync/SyncFileLog.java new file mode 100644 index 00000000..f4694574 --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/sync/SyncFileLog.java @@ -0,0 +1,103 @@ +package com.suisung.mall.common.modules.sync; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.*; + +import javax.validation.constraints.NotEmpty; +import java.util.Date; +import java.util.Map; +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode +@ApiModel(value = "文件同步日志表实体类", description = "文件同步日志表实体类") +@TableName("sync_file_log") +@Data +public class SyncFileLog { + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.AUTO) + @ApiModelProperty(value = "自增ID") + private Long id; + + @TableField(value = "sync_task_id") + @ApiModelProperty(value = "同步任务ID") + private String syncTaskId; + + @TableField(value = "sync_store_id") + @ApiModelProperty(value = "店铺id") + private String syncStoreId; + + @TableField(value = "file_path") + @ApiModelProperty(value = "文件路径") + private String filePath; + + @TableField(value = "file_name") + @ApiModelProperty(value = "文件名") + private String fileName; + + @TableField(value = "file_size") + @ApiModelProperty(value = "文件大小(字节)") + private Long fileSize; + + @TableField(value = "file_md5") + @ApiModelProperty(value = "文件MD5值") + private String fileMd5; + + @TableField(value = "source_system") + @ApiModelProperty(value = "源系统标识") + @NotEmpty + private String sourceSystem; + + @TableField(value = "target_system") + @ApiModelProperty(value = "目标系统标识") + @NotEmpty + private String targetSystem; + + @TableField(value = "sync_type") + @ApiModelProperty(value = "同步类型(1:商品,2:商品分类,3:会员,4:品牌)") + private String syncType; + + @TableField(value = "sync_status") + @ApiModelProperty(value = "同步状态(0:等待中,1:进行中,2:成功,3:失败)") + private String syncStatus; + + @TableField(value = "retry_count") + @ApiModelProperty(value = "重试次数") + private Integer retryCount; + + @TableField(value = "error_message") + @ApiModelProperty(value = "错误信息") + private String errorMessage; + + @TableField(value = "start_time") + @ApiModelProperty(value = "开始时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date startTime; + + @TableField(value = "end_time") + @ApiModelProperty(value = "结束时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; + + @TableField(value = "duration") + @ApiModelProperty(value = "耗时(毫秒)") + private Integer duration; + + @TableField(value = "create_time", fill = FieldFill.DEFAULT) + @ApiModelProperty(value = "创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @TableField(value = "update_time", fill = FieldFill.DEFAULT) + @ApiModelProperty(value = "更新时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + @TableField(value = "extra_info", typeHandler = com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler.class) + @ApiModelProperty(value = "额外信息(JSON格式)") + private Map extraInfo; + +} diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/ContextUtil.java b/mall-common/src/main/java/com/suisung/mall/common/utils/ContextUtil.java index 60ac8005..b5214cff 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/utils/ContextUtil.java +++ b/mall-common/src/main/java/com/suisung/mall/common/utils/ContextUtil.java @@ -8,6 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; +import java.util.Objects; @Component @Slf4j @@ -39,8 +40,12 @@ public class ContextUtil { public static UserDto getCurrentUser() { try { UserDto loginUser = staticUserInfoService.getUser(); - // log.info("##### 当前登录用户:{}###", JsonUtil.object2json(loginUser)); - return loginUser; + log.info("##### 当前登录用户:{}###", JsonUtil.object2json(loginUser)); + return loginUser;//todo 测试去除 +// UserDto user= new UserDto(); +// user.setStore_id("1"); +// user.setRole_id(9); +// return user; } catch (Exception e) { System.out.println(e.getMessage()); } @@ -73,6 +78,21 @@ public class ContextUtil { return loginUser.getId(); } + /** + * 传入的 + * @param storeId + * @return + */ + public static String getStoreId(String storeId){ + if(getCurrentUser()==null){ + throw new RuntimeException("未登录"); + } + if(Objects.requireNonNull(getCurrentUser()).getRole_id()==9){//平台 + return storeId; + } + return Objects.requireNonNull(getCurrentUser()).getStore_id(); + } + @PostConstruct public void init() { ContextUtil.staticUserInfoService = userInfoService; diff --git a/mall-shop/pom.xml b/mall-shop/pom.xml index 1942428b..49369c3e 100644 --- a/mall-shop/pom.xml +++ b/mall-shop/pom.xml @@ -294,6 +294,12 @@ 2.8.0 compile + + + com.microsoft.sqlserver + mssql-jdbc + 9.2.1.jre8 + diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/base/service/ShopBaseProductCategoryService.java b/mall-shop/src/main/java/com/suisung/mall/shop/base/service/ShopBaseProductCategoryService.java index 880fba4f..b50f1792 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/base/service/ShopBaseProductCategoryService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/base/service/ShopBaseProductCategoryService.java @@ -143,4 +143,10 @@ public interface ShopBaseProductCategoryService extends IBaseService configMap = configList.stream() .collect(Collectors.toMap(AccountBaseConfig::getConfig_key, config -> config)); + redisService.hSetAll(RedisConstant.Config_Cache_Key, configMap); redisService.incr(RedisConstant.Config_Cache_Version, 1); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseProductCategoryServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseProductCategoryServiceImpl.java index d2e941d9..7670f63c 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseProductCategoryServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseProductCategoryServiceImpl.java @@ -26,6 +26,7 @@ import com.suisung.mall.common.modules.product.ShopProductIndex; import com.suisung.mall.common.modules.product.ShopProductItem; import com.suisung.mall.common.modules.sixun.SxSyncCategory; import com.suisung.mall.common.modules.store.ShopStoreActivityItem; +import com.suisung.mall.common.modules.store.ShopStoreProductCategory; import com.suisung.mall.common.modules.user.ShopUserCart; import com.suisung.mall.common.pojo.dto.ProductSearchDTO; import com.suisung.mall.common.utils.CheckUtil; @@ -1235,4 +1236,26 @@ public class ShopBaseProductCategoryServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("store_id", storeId); + List categoryList = find(queryWrapper); + // 类似数据可以放到前端整理 + List category_tmp_rows = Convert.toList(Map.class, categoryList); + map=new HashMap(); + for (Map category_row : category_tmp_rows) { + map.put(category_row.get("category_name"),category_row.get("category_id")); + } + if (CollUtil.isNotEmpty(map)) redisService.set(cache_key, map, 60 * 60); + + } + return map; + } } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseProductTypeServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseProductTypeServiceImpl.java index db6ae310..bd1ca3d1 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseProductTypeServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseProductTypeServiceImpl.java @@ -85,7 +85,8 @@ public class ShopBaseProductTypeServiceImpl extends BaseServiceImpl * Id管理表 Mapper 接口 @@ -16,4 +18,7 @@ import org.springframework.stereotype.Repository; @Repository public interface ShopNumberSeqMapper extends BaseMapper { + List findNumberFromShopBaseAndItem(ShopNumberSeq shopNumberSeq); + List findProductAndImtemId(ShopNumberSeq shopNumberSeq); + void myUpdateSeq(ShopNumberSeq shopNumberSeq); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/number/service/ShopNumberSeqService.java b/mall-shop/src/main/java/com/suisung/mall/shop/number/service/ShopNumberSeqService.java index eaa5e852..8d3ca7f6 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/number/service/ShopNumberSeqService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/number/service/ShopNumberSeqService.java @@ -3,6 +3,8 @@ package com.suisung.mall.shop.number.service; import com.suisung.mall.common.modules.number.ShopNumberSeq; import com.suisung.mall.core.web.service.IBaseService; +import java.util.List; + /** *

* Id管理表 服务类 @@ -17,4 +19,9 @@ public interface ShopNumberSeqService extends IBaseService { Long createNextNo(String prefix); + List batchCreateNextNo(String seqName, int batchSize); + + void clearRelateGoodsId(); + void clearKey(); + } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/number/service/impl/ShopNumberSeqServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/number/service/impl/ShopNumberSeqServiceImpl.java index 1a1df401..7384c498 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/number/service/impl/ShopNumberSeqServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/number/service/impl/ShopNumberSeqServiceImpl.java @@ -1,15 +1,25 @@ package com.suisung.mall.shop.number.service.impl; import cn.hutool.core.date.DateUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.suisung.mall.common.modules.number.ShopNumberSeq; +import com.suisung.mall.core.web.service.RedisService; import com.suisung.mall.core.web.service.impl.BaseServiceImpl; import com.suisung.mall.shop.number.mapper.ShopNumberSeqMapper; import com.suisung.mall.shop.number.service.ShopNumberSeqService; +import org.apache.ibatis.session.ExecutorType; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; +import java.util.Collections; import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.LongStream; /** @@ -21,8 +31,15 @@ import java.util.Date; * @since 2021-09-18 */ @Service +@lombok.extern.slf4j.Slf4j public class ShopNumberSeqServiceImpl extends BaseServiceImpl implements ShopNumberSeqService { + @Autowired + private ShopNumberSeqMapper shopNumberSeqMapper; + @Autowired + private RedisService redisService; + private String CACHE_PREFIX = "shop_number_seq:%S"; + /** * 得到下一个Id * 方法走到这里会产生串行化,集群部署这里不能使用单机锁 @@ -65,6 +82,10 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl1; + while (!flag){ + try { + Thread.sleep(3600); + } catch (InterruptedException e) { + log.error("checkPrimaryKey枷锁失败--"+e.getMessage()); + break; + } + flag=shopNumberSeqMapper.findNumberFromShopBaseAndItem(new ShopNumberSeq()).size()>1; + } + + } + + + /** + * 批量获取id + * @param seqName + * @param batchSize + * @return + */ + @Transactional(propagation = Propagation.NOT_SUPPORTED) + public synchronized List batchCreateNextNo(String seqName, int batchSize) { + if (batchSize <= 0) { + return Collections.emptyList(); + } + long number= 0; + if (null==redisService.get(String.format(CACHE_PREFIX, seqName))) { + // 1. 获取当前序列值 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("prefix", seqName); + ShopNumberSeq seq = getById(seqName); + + if (seq == null) { + seq = new ShopNumberSeq(); + seq.setPrefix(seqName); + seq.setNumber((long) batchSize); + save(seq); + return LongStream.range(1, batchSize + 1).boxed().collect(Collectors.toList()); + } + number = seq.getNumber(); + }else { + int numberCache=(Integer) redisService.get(String.format(CACHE_PREFIX, seqName))+1; + number= numberCache; + + } + + // 2. 计算新值范围 + long start = number; + long end = start + batchSize - 1; + + // 3. 更新序列值到缓存,在并发时使用 + redisService.set(String.format(CACHE_PREFIX, seqName), end); + // 4. 返回生成的ID范围 + return LongStream.rangeClosed(start, end).boxed().collect(Collectors.toList()); + } + + public void batchUpdateSeq(List shopNumberSeqList){ + int count=0; + for (int i = 0; i < shopNumberSeqList.size(); i++) { + shopNumberSeqMapper.myUpdateSeq(shopNumberSeqList.get(i)); + count++; + } + log.info("更新成功{}条数据",count); + } + public void clearKey(){ + redisService.del(String.format(CACHE_PREFIX,"item_id")); + redisService.del(String.format(CACHE_PREFIX,"product_id")); + } + + /** + * 清除缓存,专门给并发使用,防止redis的缓存没有加载 + */ + @Override + public void clearRelateGoodsId(){ + boolean flag = shopNumberSeqMapper.findNumberFromShopBaseAndItem(new ShopNumberSeq()).size()>1; + if(flag){ + return; + } + List shopNumberSeqList=shopNumberSeqMapper.findProductAndImtemId(new ShopNumberSeq()); + for (ShopNumberSeq shopNumberSeq:shopNumberSeqList) { + if(shopNumberSeq.getPrefix().equals("product_id")){ + clearCache("product_id",shopNumberSeq.getNumber()); + } + if(shopNumberSeq.getPrefix().equals("item_id")){ + clearCache("item_id",shopNumberSeq.getNumber()); + } + } + } + /** + * 刷新缓存 + */ + private void clearCache(String seqName,long number){ + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("prefix", seqName); + ShopNumberSeq seq = getById(seqName); + if (seq != null) { + seq = new ShopNumberSeq(); + seq.setNumber(number-1); + seq.setPrefix(seqName); + } + edit(seq); + } } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/page/service/OssService.java b/mall-shop/src/main/java/com/suisung/mall/shop/page/service/OssService.java index 92324c09..4e5c53b8 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/page/service/OssService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/page/service/OssService.java @@ -1,12 +1,14 @@ package com.suisung.mall.shop.page.service; +import com.qcloud.cos.model.COSObjectSummary; import com.suisung.mall.common.pojo.dto.OssCallbackResultDTO; import com.suisung.mall.common.pojo.dto.OssPolicyResultDTO; import javax.servlet.http.HttpServletRequest; import java.io.File; import java.io.InputStream; +import java.util.List; /** * oss上传管理Service @@ -42,4 +44,26 @@ public interface OssService { */ String uploadObject4OSS(String fileUrl, String concat); + /** + * 根据目录查询目录下的目录列表 + * @param folder + * @return + */ + List listFolders(String folder); + + /** + * 根据目录查询目录下的文件 + * @param folder + * @return + */ + List listDocuments(String folder); + + /** + * 下载文件返回路径 + * @param ossFolder + * @param localFolder + * @return + */ + String download(String ossFolder, String localFolder); + } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/page/service/impl/OssServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/page/service/impl/OssServiceImpl.java index 3199fbde..342b5142 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/page/service/impl/OssServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/page/service/impl/OssServiceImpl.java @@ -14,9 +14,7 @@ import com.qcloud.cos.COSClient; import com.qcloud.cos.ClientConfig; import com.qcloud.cos.auth.BasicCOSCredentials; import com.qcloud.cos.auth.COSCredentials; -import com.qcloud.cos.model.CannedAccessControlList; -import com.qcloud.cos.model.PutObjectRequest; -import com.qcloud.cos.model.PutObjectResult; +import com.qcloud.cos.model.*; import com.qcloud.cos.region.Region; import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.constant.ConfigConstant; @@ -49,7 +47,9 @@ import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; /** * oss上传管理Service实现类 @@ -569,4 +569,49 @@ public class OssServiceImpl implements OssService { } } } + + @Override + public List listFolders(String folder) { + COSClient ossCli = initCOSClient(); + ListObjectsRequest listObjectsRequest = new ListObjectsRequest(); + listObjectsRequest.setBucketName(TENGXUN_BUCKET_NAME); + listObjectsRequest.setPrefix(folder); + listObjectsRequest.setDelimiter("/"); + ObjectListing objectListing = ossCli.listObjects(listObjectsRequest); + List commonPrefixes = objectListing.getCommonPrefixes(); + logger.info(JSONUtil.toJsonStr(commonPrefixes)); + return commonPrefixes; + } + + @Override + public List listDocuments(String folder) { + COSClient ossCli = initCOSClient(); + ListObjectsRequest listObjectsRequest = new ListObjectsRequest(); + listObjectsRequest.setBucketName(TENGXUN_BUCKET_NAME); + listObjectsRequest.setPrefix(folder); + listObjectsRequest.setDelimiter(""); + ObjectListing objectListing = ossCli.listObjects(listObjectsRequest); + List files = objectListing.getObjectSummaries() + .stream() + // 过滤掉目录(目录通常以 '/' 结尾或大小为0) + .filter(obj -> !obj.getKey().endsWith("/") || obj.getSize() > 0) + .collect(Collectors.toList()); + // 获取目录下的文件 + return files; + } + + /** + * + * @param ossFolder 如folder/example.txt + * @param localFolder 如/path/to/local/example.txt + * @return + */ + @Override + public String download(String ossFolder, String localFolder) { + COSClient ossCli = initCOSClient(); + ossCli.getObject(new GetObjectRequest(TENGXUN_BUCKET_NAME, ossFolder), + new File(localFolder)); + return localFolder; + } + } \ No newline at end of file diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/product/service/ShopProductAssistIndexService.java b/mall-shop/src/main/java/com/suisung/mall/shop/product/service/ShopProductAssistIndexService.java index 2a81ed24..9cd955f9 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/product/service/ShopProductAssistIndexService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/product/service/ShopProductAssistIndexService.java @@ -3,6 +3,8 @@ package com.suisung.mall.shop.product.service; import com.suisung.mall.common.modules.product.ShopProductAssistIndex; import com.suisung.mall.core.web.service.IBaseService; +import java.util.List; + /** *

* 商品与属性对应表 服务类 @@ -12,5 +14,7 @@ import com.suisung.mall.core.web.service.IBaseService; * @since 2021-05-19 */ public interface ShopProductAssistIndexService extends IBaseService { + List getShopProductAssistIndexStoreId(Integer storeId); + void clearCacheShopProductAssistIndexByStoreId(Integer storeId); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/product/service/ShopProductBaseService.java b/mall-shop/src/main/java/com/suisung/mall/shop/product/service/ShopProductBaseService.java index 7f12ca15..43ba5b21 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/product/service/ShopProductBaseService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/product/service/ShopProductBaseService.java @@ -283,4 +283,10 @@ public interface ShopProductBaseService extends IBaseService { Pair saveProduct(ShopProductBase shopProductBase, ShopProductIndex shopProductIndex, ShopProductData shopProductData, ShopProductDetail shopProductDetail, ShopProductInfo shopProductInfo, List shopProductItemList, List shopProductImageList, ShopProductValidPeriod shopProductValidPeriod, List shopProductAssistIndexList); + Pair saveProductBatch(List shopProductBaseList, List shopProductIndexList, + List shopProductDataList, List shopProductDetailList, + List shopProductInfoList, List> shopProductItemList, + List> shopProductImageList, + List shopProductValidPeriodList, + List shopProductAssistIndexList); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/product/service/ShopProductImageService.java b/mall-shop/src/main/java/com/suisung/mall/shop/product/service/ShopProductImageService.java index 03dfccef..04f15e22 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/product/service/ShopProductImageService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/product/service/ShopProductImageService.java @@ -3,6 +3,8 @@ package com.suisung.mall.shop.product.service; import com.suisung.mall.common.modules.product.ShopProductImage; import com.suisung.mall.core.web.service.IBaseService; +import java.util.List; + /** *

* 产品图片表,按照颜色规格 服务类 @@ -13,4 +15,7 @@ import com.suisung.mall.core.web.service.IBaseService; */ public interface ShopProductImageService extends IBaseService { + List getShopProductImageByStoreId(Integer storeId); + + void clearCacheShopProductImageByStoreId(Integer storeId); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductAssistIndexServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductAssistIndexServiceImpl.java index 28a03fbe..3a108074 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductAssistIndexServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductAssistIndexServiceImpl.java @@ -1,11 +1,17 @@ package com.suisung.mall.shop.product.service.impl; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.suisung.mall.common.modules.product.ShopProductAssistIndex; +import com.suisung.mall.core.web.service.RedisService; import com.suisung.mall.core.web.service.impl.BaseServiceImpl; import com.suisung.mall.shop.product.mapper.ShopProductAssistIndexMapper; import com.suisung.mall.shop.product.service.ShopProductAssistIndexService; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.Collections; +import java.util.List; + /** *

@@ -17,4 +23,24 @@ import org.springframework.stereotype.Service; */ @Service public class ShopProductAssistIndexServiceImpl extends BaseServiceImpl implements ShopProductAssistIndexService { + + @Autowired + private RedisService redisService; + + @Override + public List getShopProductAssistIndexStoreId(Integer storeId) { + List shopProductAssistIndexList= (List) redisService.get("ShopProductAssistIndexStoreId:"+storeId); + if(shopProductAssistIndexList==null){ + QueryWrapper queryWrapper= new QueryWrapper(); + queryWrapper.eq("store_id",storeId); + shopProductAssistIndexList=this.list(queryWrapper); + redisService.set("ShopProductAssistIndexStoreId:"+storeId,shopProductAssistIndexList); + } + return shopProductAssistIndexList; + } + + @Override + public void clearCacheShopProductAssistIndexByStoreId(Integer storeId) { + redisService.del("ShopProductAssistIndexStoreId:"+storeId); + } } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductBaseServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductBaseServiceImpl.java index dcd17f5c..3829658c 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductBaseServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductBaseServiceImpl.java @@ -6,6 +6,7 @@ import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUtil; +import cn.hutool.core.date.StopWatch; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; @@ -15,6 +16,8 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.mapper.Mapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.suisung.mall.common.api.CommonResult; @@ -62,14 +65,22 @@ import com.suisung.mall.shop.product.pojo.vo.ProductVo; import com.suisung.mall.shop.product.service.*; import com.suisung.mall.shop.sixun.service.SxSyncGoodsService; import com.suisung.mall.shop.store.service.*; +import com.suisung.mall.shop.sync.Utils.BatchInsertUtils; import com.suisung.mall.shop.user.service.*; import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; +import org.mybatis.spring.SqlSessionFactoryBean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.util.Pair; import org.springframework.stereotype.Service; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionTemplate; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @@ -81,7 +92,9 @@ import java.math.BigDecimal; import java.math.RoundingMode; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadPoolExecutor; +import java.util.function.Function; import java.util.stream.Collectors; import static com.suisung.mall.common.utils.ContextUtil.getCurrentUser; @@ -195,6 +208,11 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl saveProduct(ShopProductBase shopProductBase, ShopProductIndex shopProductIndex, ShopProductData shopProductData, ShopProductDetail shopProductDetail, ShopProductInfo shopProductInfo, List shopProductItemList, List shopProductImageList, ShopProductValidPeriod shopProductValidPeriod, List shopProductAssistIndexList) { + public synchronized Pair saveProduct(ShopProductBase shopProductBase, ShopProductIndex shopProductIndex, ShopProductData shopProductData, ShopProductDetail shopProductDetail, ShopProductInfo shopProductInfo, List shopProductItemList, List shopProductImageList, ShopProductValidPeriod shopProductValidPeriod, List shopProductAssistIndexList) { Integer store_id = shopProductBase.getStore_id(); if (store_id == null) { return Pair.of(false, I18nUtil._("缺少店铺ID!")); @@ -718,7 +736,7 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl wrapper, Integer pageNum, Integer pageSize) { Map map = new HashMap(); - Integer store_id = Convert.toInt(getCurrentUser().getStore_id()); + Integer store_id = Convert.toInt(getCurrentUser().getStore_id(),1); Integer nodeid = Convert.toInt(getParameter("nodeid"), 0); if (CheckUtil.isNotEmpty(nodeid)) { @@ -5243,4 +5261,835 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl saveProductBatch(List shopProductBaseList, List shopProductIndexList, + List shopProductDataList, List shopProductDetailList, + List shopProductInfoList, List> shopProductItemList, + List> shopProductImageList, List shopProductValidPeriodList, + List shopProductAssistIndexList) { + + // 1. 参数校验 + if (shopProductBaseList == null || shopProductBaseList.isEmpty()) { + return Pair.of(false, "商品列表不能为空"); + } + + // 2. 检查并设置已存在商品的ID + Map existingProducts = checkExistingProducts(shopProductBaseList); + + // 3. 分离新增和更新的商品 + List newProducts = new ArrayList<>(); + List updateProducts = new ArrayList<>(); + + List newShopProductIndexList = new ArrayList<>(); + List updateShopProductIndexList = new ArrayList<>(); + + List newProductDataList=new ArrayList<>(); + List updateProductDataList=new ArrayList<>(); + + List newShopProductDetailList=new ArrayList<>(); + List updateShopProductDetailList=new ArrayList<>(); + + List newShopProductInfoList=new ArrayList<>(); + List updateShopProductInfoList=new ArrayList<>(); + + List> newShopProductItemList=new ArrayList<>(); + List> updateShopProductItemList=new ArrayList<>(); + + List> newShopProductImageList=new ArrayList<>(); + List> updateShopProductImageList=new ArrayList<>(); + + List newShopProductValidPeriodList=new ArrayList<>(); + List updateShopProductValidPeriodList=new ArrayList<>(); + + List newShopProductAssistIndexList=new ArrayList<>(); + List updateShopProductAssistIndexList=new ArrayList<>(); + try { + for (int i = 0; i < shopProductBaseList.size(); i++) { + ShopProductBase base = shopProductBaseList.get(i); + String key = base.getStore_id() + "_" + base.getProduct_number(); + if (existingProducts.containsKey(key)) { + + // 已存在商品,设置ID并加入更新列表 + Long existId = existingProducts.get(key); + base.setProduct_id(existId); + + shopProductBaseList.get(i).setProduct_id(existId); + //shopProductIndexList.get(i).setProduct_id(existId); + // shopProductIndexList.get(i).setProduct_unit_points(BigDecimal.ZERO); + shopProductIndexList.get(i).setProduct_unit_price_max(base.getProduct_market_price()); + shopProductIndexList.get(i).setProduct_unit_sp(Convert.toBigDecimal(base.getProduct_unit_sp())); + shopProductIndexList.get(i).setProduct_sale_time(base.getProduct_sale_time().getTime()); + shopProductIndexList.get(i).setProduct_verify_id(base.getProduct_verify_id()); + shopProductIndexList.get(i).setProduct_state_id(base.getProduct_state_id()); + // 判断店铺是否开启 +// shopProductIndexList.get(i).setStore_is_open(1); +// shopProductIndexList.get(i).setStore_is_selfsupport(1); +// shopProductIndexList.get(i).setStore_type(1); +// shopProductIndexList.get(i).setSubsite_id(0); +// shopProductIndexList.get(i).setProduct_number(base.getProduct_number()); + +// shopProductDataList.get(i).setProduct_id(existId); +// shopProductDetailList.get(i).setProduct_id(existId); +// shopProductInfoList.get(i).setProduct_id(existId); + if(CollUtil.isNotEmpty(shopProductIndexList)){ + updateShopProductIndexList.add(shopProductIndexList.get(i)); + } + if(CollUtil.isNotEmpty(shopProductDataList)){ + updateProductDataList.add(shopProductDataList.get(i)); + } + if(CollUtil.isNotEmpty(shopProductDetailList)){ + updateShopProductDetailList.add(shopProductDetailList.get(i)); + } + if(CollUtil.isNotEmpty(shopProductInfoList)){ + updateShopProductInfoList.add(shopProductInfoList.get(i)); + } + if(CollUtil.isNotEmpty(shopProductItemList)){ + updateShopProductItemList.add(shopProductItemList.get(i)); + } + + if(CollUtil.isNotEmpty(shopProductImageList)){ + updateShopProductImageList.add(shopProductImageList.get(i)); + } + if(CollUtil.isNotEmpty(shopProductValidPeriodList)){ + updateShopProductValidPeriodList.add(shopProductValidPeriodList.get(i)); + } + if(CollUtil.isNotEmpty(shopProductAssistIndexList)){ + updateShopProductAssistIndexList.add(shopProductAssistIndexList.get(i)); + } + + updateProducts.add(base); + + } else { + base.setProduct_verify_id(StateCode.PRODUCT_VERIFY_PASSED);//审核通过 + base.setProduct_state_id(StateCode.PRODUCT_STATE_OFF_THE_SHELF);//下架状态 + + shopProductIndexList.get(i).setProduct_unit_points(BigDecimal.ZERO); + shopProductIndexList.get(i).setProduct_unit_price_max(base.getProduct_market_price()); + shopProductIndexList.get(i).setProduct_unit_sp(Convert.toBigDecimal(base.getProduct_unit_sp())); + shopProductIndexList.get(i).setProduct_sale_time(base.getProduct_sale_time().getTime()); + shopProductIndexList.get(i).setProduct_verify_id(base.getProduct_verify_id()); + shopProductIndexList.get(i).setProduct_state_id(base.getProduct_state_id()); + // 判断店铺是否开启 + shopProductIndexList.get(i).setStore_is_open(1); + shopProductIndexList.get(i).setStore_is_selfsupport(1); + shopProductIndexList.get(i).setStore_type(1); + shopProductIndexList.get(i).setSubsite_id(0); + shopProductIndexList.get(i).setProduct_number(base.getProduct_number()); + + if(CollUtil.isNotEmpty(shopProductIndexList)){ + newShopProductIndexList.add(shopProductIndexList.get(i)); + } + if(CollUtil.isNotEmpty(shopProductDataList)){ + newProductDataList.add(shopProductDataList.get(i)); + } + if(CollUtil.isNotEmpty(shopProductDetailList)){ + newShopProductDetailList.add(shopProductDetailList.get(i)); + } + if(CollUtil.isNotEmpty(shopProductInfoList)){ + newShopProductInfoList.add(shopProductInfoList.get(i)); + } + if(CollUtil.isNotEmpty(shopProductItemList)){ + newShopProductItemList.add(shopProductItemList.get(i)); + } + if(CollUtil.isNotEmpty(shopProductImageList)){ + newShopProductImageList.add(shopProductImageList.get(i)); + } + if(CollUtil.isNotEmpty(shopProductValidPeriodList)){ + newShopProductValidPeriodList.add(shopProductValidPeriodList.get(i)); + } + if(CollUtil.isNotEmpty(shopProductAssistIndexList)){ + newShopProductAssistIndexList.add(shopProductAssistIndexList.get(i)); + } + // 新商品,加入新增列表 + newProducts.add(base); + } + + } + }catch (RuntimeException e){ + logger.info("异常报错:{}",e.getMessage()); + } + + return executeBatchOperations(newProducts,updateProducts,newShopProductIndexList,updateShopProductIndexList, + newProductDataList,updateProductDataList,newShopProductDetailList,updateShopProductDetailList, + newShopProductInfoList,updateShopProductInfoList,newShopProductItemList,updateShopProductItemList, + newShopProductImageList,updateShopProductImageList,newShopProductValidPeriodList,updateShopProductValidPeriodList, + newShopProductAssistIndexList,updateShopProductAssistIndexList); + } + + /** + * 执行批量操作(新增和更新分开处理) + */ + private Pair executeBatchOperations(List newProducts, List updateProducts, + List newShopProductIndexList,List updateShopProductIndexList, + List newShopProductDataList, List updateShopProductDataList, + List newShopProductDetailList, List updateShopProductDetailList, + List newShopProductInfoList, List updateShopProductInfoList, + List> newShopProductItemList,List> udpateShopProductItemList, + List> newShopProductImageList, List> udpteShopProductImageList, + List newShopProductValidPeriodList, List updateShopProductValidPeriodList, + List newShopProductAssistIndexList, List udpateShopProductAssistIndexList + ) { + TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); + transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); + transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); + transactionTemplate.setTimeout(60); // 60秒超时 + return transactionTemplate.execute(status -> { + try { + // Map> productToItemsMap = new HashMap<>(); + List addAnalyticsList = new ArrayList<>(); + List udpateAnalyticsList = new ArrayList<>(); + + List addItemSeqs = new ArrayList<>(); + List updateItemSeqs = new ArrayList<>(); + + List addItemIds = new ArrayList<>(); + List udpateItemIds = new ArrayList<>(); + List addShopProductItems=new ArrayList<>(); + List updateShopProductItems=new ArrayList<>(); + int taskCount = 2; + CountDownLatch latch = new CountDownLatch(taskCount); + // 1. 批量新增 + if (CollUtil.isNotEmpty(newProducts)) { + // 4. 批量生成新商品的ID + List newIds = shopNumberSeqService.batchCreateNextNo("product_id", newProducts.size()); + if (newIds == null || newIds.size() != newProducts.size()) { + return Pair.of(false, "生成商品编号异常"); + } + for (int i = 0; i < newIds.size(); i++) { + Long productId = newIds.get(i); + ShopProductBase base = newProducts.get(i); + base.setProduct_id(productId); + // 设置关联ID + newProducts.get(i).setProduct_id(productId); + newShopProductIndexList.get(i).setProduct_id(productId); + newShopProductDataList.get(i).setProduct_id(productId); + newShopProductDetailList.get(i).setProduct_id(productId); + newShopProductInfoList.get(i).setProduct_id(productId); + + // 处理商品项 + List items = newShopProductItemList.get(i); + processProductItems(items, productId, addItemSeqs); + addShopProductItems.addAll(items); + + // 处理图片 + if(CollUtil.isNotEmpty(newShopProductImageList)){ + processProductImages(newShopProductImageList.get(i), productId,base.getStore_id()); + } + + // 处理辅助属性 + if(CollUtil.isNotEmpty(newShopProductAssistIndexList)){ + processAssistIndices(newShopProductAssistIndexList, base.getStore_id(),false); + } + + // 准备分析数据 + ShopProductAnalytics shopProductAnalytics= new ShopProductAnalytics(); + shopProductAnalytics.setProduct_id(productId); + shopProductAnalytics.setProduct_click(0); + addAnalyticsList.add(shopProductAnalytics); + } + List itemIds = processProductItemsId(addShopProductItems, addItemSeqs); + addItemIds.addAll(itemIds); + // 2. 批量更新 + if (CollUtil.isNotEmpty(newProducts)) { + // new Thread(() -> { + logger.info("保存任务开始执行"); + try { + long startTime=System.nanoTime(); + saveBatch(newProducts,newProducts.size()); + long endTime = System.nanoTime(); + long duration = (endTime - startTime); + logger.info("新增newProducts--{}条数据耗时:{}",newProducts.size(),duration/1000000000); + latch.countDown(); + } catch (Exception e) { + latch.countDown(); + logger.error("系统异常"+e.getMessage()); + } + // }).start(); + }else{ + // latch.countDown(); + } + }else { + //latch.countDown(); + } + if (CollUtil.isNotEmpty(updateProducts)) { + for(int i=0;i items = udpateShopProductItemList.get(i); + processProductItems(items, productId, updateItemSeqs);// + updateShopProductItems.addAll(items); + //allItemIds.addAll(itemIds); + // productToItemsMap.put(productId, itemIds); + + // 处理图片 + if(CollUtil.isNotEmpty(udpteShopProductImageList)){ + processProductImages(udpteShopProductImageList.get(i), productId,base.getStore_id()); + } + + // 处理辅助属性 + if(CollUtil.isNotEmpty(udpateShopProductAssistIndexList)){ + processAssistIndices(udpateShopProductAssistIndexList, base.getStore_id(),true); + } + + // 准备分析数据 + ShopProductAnalytics shopProductAnalytics= new ShopProductAnalytics(); + shopProductAnalytics.setProduct_id(productId); + shopProductAnalytics.setProduct_click(0); + udpateAnalyticsList.add(shopProductAnalytics); + } + List itemIds = processProductItemsId(updateShopProductItems, updateItemSeqs); + udpateItemIds.addAll(itemIds); + // 2. 批量更新 + if (CollUtil.isNotEmpty(updateProducts)) { + // new Thread(() -> { + logger.info("更新任务开始执行"); + try { + long startTime=System.nanoTime(); + updateBatchById(updateProducts); + long endTime = System.nanoTime(); + long duration = (endTime - startTime); + logger.info("更新updateProducts-{}条数据耗时:{}",updateProducts.size(),duration/1000000000); + latch.countDown(); + } catch (Exception e) { + latch.countDown(); + logger.error("系统异常"+e.getMessage()); + } + // }).start(); + }else { + //latch.countDown(); + } + }else { + // latch.countDown(); + } + // latch.await(); + // 3. 处理其他表的批量操作(同样需要区分新增和更新) + batchProcessOtherTables(newShopProductIndexList,updateShopProductIndexList,newShopProductDataList,updateShopProductDataList, + newShopProductDetailList,updateShopProductDetailList,newShopProductInfoList,updateShopProductInfoList, + newShopProductValidPeriodList,updateShopProductValidPeriodList,addShopProductItems,updateShopProductItems, + addAnalyticsList,udpateAnalyticsList,addItemSeqs,updateItemSeqs); + logger.info("处理成功,新增{}条,更新{}条", newProducts.size(), updateProducts.size()); + return Pair.of(true, String.format("处理成功,新增%d条,更新%d条", + newProducts.size(), updateProducts.size())); + } catch (RuntimeException e) { + status.setRollbackOnly(); + logger.error("批量操作异常", e); + return Pair.of(false, "批量操作异常: " + e.getMessage()); + } +// catch (InterruptedException e) { +// status.setRollbackOnly(); +// logger.error("批量操作异常", e); +// return Pair.of(false, "批量操作异常: " + e.getMessage()); +// } + }); + + + } + + // 批量保存操作 + private void batchProcessOtherTables(List newShopProductIndex, List updateShopProductIndex, + List newShopProductDataList, List updateShopProductDataList, + List newshopProductDetailList, List updateShopProductDetailList, + List newShopProductInfoList,List updateShopProductInfoList, + List newShopProductValidPeriodList,List updateShopProductValidPeriodList, + List newshopProductItemList,List updateshopProductItemList, + List newShopProductAnalyticList,List updateShopProductAnalyticList, + List newShopProductItemSeqList,List updateShopProductItemSeqList) { + TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); + transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); + transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); + transactionTemplate.setTimeout(60); // 60秒超时 + transactionTemplate.execute(status -> { + // 并行执行批量保存 +//CompletableFuture future1 = CompletableFuture.runAsync(() -> { + try { + if (CollUtil.isNotEmpty(newShopProductIndex)) { + // synchronized (this){ + long startTime=System.nanoTime(); + shopProductIndexService.saveBatch(newShopProductIndex,newShopProductIndex.size()); + long endTime = System.nanoTime(); + long duration = (endTime - startTime); + logger.info("新增newShopProductIndex-{}条数据耗时:{}",newShopProductIndex.size(),duration/1000000000); + + //} + + } + if (CollUtil.isNotEmpty(updateShopProductIndex)) { + // synchronized (this) { + long startTime=System.nanoTime(); + shopProductIndexService.updateBatchById(updateShopProductIndex); + long endTime = System.nanoTime(); + long duration = (endTime - startTime); + logger.info("更新updateShopProductIndex-{}条数据耗时:{}",updateShopProductIndex.size(),duration/1000000000); + // } + } + + if (CollUtil.isNotEmpty(newShopProductDataList)) { + //synchronized (this) { + long startTime=System.nanoTime(); + shopProductDataService.saveBatch(newShopProductDataList, newShopProductDataList.size()); + long endTime = System.nanoTime(); + long duration = (endTime - startTime); + logger.info("新增newShopProductDataList-{}条数据耗时:{}",newShopProductDataList.size(),duration/1000000000); + //} + } + if (CollUtil.isNotEmpty(updateShopProductDataList)) { + // synchronized (this) { + long startTime=System.nanoTime(); + shopProductDataService.updateBatchById(updateShopProductDataList); + long endTime = System.nanoTime(); + long duration = (endTime - startTime); + logger.info("更新updateShopProductDataList-{}条数据耗时:{}",updateShopProductDataList.size(),duration/1000000000); + // } + } + }catch (RuntimeException e){ + logger.info("批量报错附表失败{}",e.getMessage()); + } + + // }); + + // CompletableFuture future2 = CompletableFuture.runAsync(() -> { + try { + if (CollUtil.isNotEmpty(newshopProductDetailList)) { + // synchronized (this) { + long startTime=System.nanoTime(); + shopProductDetailService.saveBatch(newshopProductDetailList, newshopProductDetailList.size()); + long endTime = System.nanoTime(); + long duration = (endTime - startTime); + logger.info("新增newshopProductDetailList-{}条数据耗时:{}",newshopProductDetailList.size(),duration/1000000000); + + // } + } + if (CollUtil.isNotEmpty(updateShopProductDetailList)) { + // synchronized (this) { + long startTime=System.nanoTime(); + shopProductDetailService.updateBatchById(updateShopProductDetailList); + long endTime = System.nanoTime(); + long duration = (endTime - startTime); + logger.info("更新updateShopProductDetailList-{}条数据耗时:{}",updateShopProductDetailList.size(),duration/1000000000); + // } + } + if (CollUtil.isNotEmpty(newShopProductInfoList)) { + // synchronized (this) { + long startTime=System.nanoTime(); + shopProductInfoService.saveBatch(newShopProductInfoList, newShopProductInfoList.size()); + long endTime = System.nanoTime(); + long duration = (endTime - startTime); + logger.info("新增updateShopProductDetailList-{}条数据耗时:{}",newShopProductInfoList.size(),duration/1000000000); + // } + } + if (CollUtil.isNotEmpty(updateShopProductInfoList)) { + // synchronized (this) { + long startTime=System.nanoTime(); + shopProductInfoService.updateBatchById(updateShopProductInfoList); + long endTime = System.nanoTime(); + long duration = (endTime - startTime); + logger.info("更新updateShopProductInfoList-{}条数据耗时:{}",updateShopProductInfoList.size(),duration/1000000000); + // } + } + }catch (RuntimeException e){ + logger.info("批量报错附表失败{}",e.getMessage()); + } + //}); + + //CompletableFuture future3 = CompletableFuture.runAsync(() -> { + try { + if (CollUtil.isNotEmpty(newShopProductValidPeriodList)) { + // synchronized (this) { + long startTime=System.nanoTime(); + validPeriodService.saveBatch(newShopProductValidPeriodList, newShopProductValidPeriodList.size()); + long endTime = System.nanoTime(); + long duration = (endTime - startTime); + logger.info("新增newShopProductValidPeriodList-{}条数据耗时:{}",newShopProductValidPeriodList.size(),duration/1000000000); + // } + } + if (CollUtil.isNotEmpty(updateShopProductValidPeriodList)) { + //synchronized (this) { + long startTime=System.nanoTime(); + validPeriodService.updateBatchById(updateShopProductValidPeriodList); + long endTime = System.nanoTime(); + long duration = (endTime - startTime); + logger.info("更新updateShopProductValidPeriodList-{}条数据耗时:{}",updateShopProductValidPeriodList.size(),duration/1000000000); + // } + } + if (CollUtil.isNotEmpty(newShopProductAnalyticList)) { + // synchronized (this) { + long startTime=System.nanoTime(); + shopProductAnalyticsService.saveBatch(newShopProductAnalyticList, newShopProductAnalyticList.size()); + long endTime = System.nanoTime(); + long duration = (endTime - startTime); + logger.info("新增newShopProductAnalyticList-{}条数据耗时:{}",newShopProductAnalyticList.size(),duration/1000000000); + // } + } + if (CollUtil.isNotEmpty(updateShopProductAnalyticList)) { + // synchronized (this) { + long startTime=System.nanoTime(); + shopProductAnalyticsService.updateBatchById(updateShopProductAnalyticList); + long endTime = System.nanoTime(); + long duration = (endTime - startTime); + logger.info("更新updateShopProductAnalyticList-{}条数据耗时:{}",updateShopProductAnalyticList.size(),duration/1000000000); + // } + } + }catch (RuntimeException e){ + logger.info("批量报错附表失败{}",e.getMessage()); + } + + //}); + + //CompletableFuture future4 = CompletableFuture.runAsync(() -> { + try { + if (CollUtil.isNotEmpty(newshopProductItemList)) { + // synchronized (this) { + long startTime=System.nanoTime(); + shopProductItemService.saveBatch(newshopProductItemList, newshopProductItemList.size()); + long endTime = System.nanoTime(); + long duration = (endTime - startTime); + logger.info("新增newshopProductItemList-{}条数据耗时:{}",newshopProductItemList.size(),duration/1000000000); + //} + } + if (CollUtil.isNotEmpty(updateshopProductItemList)) { + // synchronized (this) { + long startTime = System.nanoTime(); + shopProductItemService.updateBatchById(updateshopProductItemList); + long endTime = System.nanoTime(); + long duration = (endTime - startTime); + logger.info("更新updateshopProductItemList-{}条数据耗时:{}",updateshopProductItemList.size(),duration/1000000000); + //} + } + if (CollUtil.isNotEmpty(newShopProductItemSeqList)) { + // synchronized (this) { + long startTime = System.nanoTime(); + shopProductItemSeqService.saveBatch(newShopProductItemSeqList, newShopProductItemSeqList.size()); + long endTime = System.nanoTime(); + long duration = (endTime - startTime); + logger.info("新曾newShopProductItemSeqList-{}条数据耗时:{}",newShopProductItemSeqList.size(),duration/1000000000); + // } + } + if (CollUtil.isNotEmpty(updateShopProductItemSeqList)) { + // synchronized (this) { + long startTime = System.nanoTime(); + shopProductItemSeqService.updateBatchById(updateShopProductItemSeqList); + long endTime = System.nanoTime(); + long duration = (endTime - startTime); + logger.info("更新updateShopProductItemSeqList-{}条数据耗时:{}",updateShopProductItemSeqList.size(),duration/1000000000); + // } + } + }catch (RuntimeException e){ + logger.info("批量报错附表失败{}",e.getMessage()); + } + //}); + // 等待所有操作完成 + // CompletableFuture.allOf(future1, future2, future3, future4).join(); + return null; + }); + + } + + /** + * 批量生产itemid + * @param items + * @param itemSeqs + * @return + */ + private List processProductItemsId(List items,List itemSeqs){ + List itemIds = new ArrayList<>(); + if (CollUtil.isEmpty(items)) return itemIds; + List generatedIds = shopNumberSeqService.batchCreateNextNo("item_id", items.size()); + for (int i = 0; i < items.size(); i++) { + Long itemId = generatedIds.get(i); + ShopProductItem item = items.get(i); + item.setItem_id(itemId); + itemIds.add(itemId); + ShopProductItemSeq field_row= itemSeqs.get(i); + field_row.setItem_id(itemId); + + } + + return itemIds; + } + + // 处理商品项 + private void processProductItems(List items, Long productId, + List itemSeqs) { + + for (int i = 0; i < items.size(); i++) { + ShopProductItem item = items.get(i); + item.setProduct_id(productId); + // itemIds.add(itemId); + + // ITEM_ID/SKU唯一编号表。 + // product_id + sort(spec_item_id) = 构造唯一ITEM ID + String item_spec = item.getItem_spec(); + cn.hutool.json.JSONArray array_item_spec = cn.hutool.json.JSONUtil.parseArray(item_spec); + List spec_item_ids = new ArrayList<>(); + List item_names = new ArrayList<>(); + for (Object josn_item_spec : array_item_spec) { + cn.hutool.json.JSONObject itemJ = (cn.hutool.json.JSONObject) ((cn.hutool.json.JSONObject) josn_item_spec).get("item"); + Integer id = Convert.toInt(itemJ.get("id")); + String name = Convert.toStr(itemJ.get("name")); + if (ObjectUtil.isNotNull(id)) spec_item_ids.add(id); + item_names.add(name); + } + + if (CollUtil.isNotEmpty(spec_item_ids)) Collections.sort(spec_item_ids); + + //用来判断是否使用 + item.setSpec_item_ids(CollUtil.join(spec_item_ids, ",")); + + // 创建itemSeq记录 + String skuUniqid = generateSkuUniqid(item); // 生成SKU唯一标识 + String seqVal = productId + ":" + skuUniqid; + ShopProductItemSeq field_row = new ShopProductItemSeq(); + field_row.setProduct_id(productId); + field_row.setProduct_item_seq_val(seqVal); + field_row.setProduct_item_seq_id(SecureUtil.md5(seqVal)); + //field_row.setItem_id(itemId); + + // 修正默认价格, 防止出现不合理价格 + BigDecimal item_platform_price = NumberUtil.max(item.getItem_cost_price(), item.getItem_platform_price()); + BigDecimal _item_unit_price = NumberUtil.max(item.getItem_unit_price(), item.getItem_platform_price()); + item.setItem_platform_price(item_platform_price); + item.setItem_unit_price(_item_unit_price); + item.setItem_name(CollUtil.join(item_names, ",")); + item.setItem_title(""); + item.setItem_freetime(0); + + itemSeqs.add(field_row); + } + } + + /** + * 生成SKU唯一标识符 + * @param item 商品项 + * @return SKU唯一标识字符串 + */ + private String generateSkuUniqid(ShopProductItem item) { + // 1. 获取商品规格信息 + String itemSpec = item.getItem_spec(); + if (StrUtil.isBlank(itemSpec)) { + return "default"; // 无规格商品的默认标识 + } + + // 2. 解析规格JSON + cn.hutool.json.JSONArray specArray; + try { + specArray = JSONUtil.parseArray(itemSpec); + } catch (Exception e) { + logger.warn("商品规格解析失败,使用默认标识", e); + return "default"; + } + + // 3. 提取所有规格项ID并排序 + List specItemIds = new ArrayList<>(); + for (Object specObj : specArray) { + JSONObject spec = (JSONObject) specObj; + JSONObject specItem = spec.getJSONObject("item"); + if (specItem != null) { + Integer id = specItem.getInteger("id"); + if (id != null) { + specItemIds.add(id); + } + } + } + + // 4. 排序确保顺序一致性 + Collections.sort(specItemIds); + + // 5. 生成唯一标识字符串 + return CollUtil.join(specItemIds, "-"); + } + + + + /** + * 检查哪些商品已存在 + */ + private Map checkExistingProducts(List productBases) { + // 1. 按店铺和货号分组 + Map> storeProductMap = productBases.stream() + .collect(Collectors.groupingBy( + p -> p.getStore_id() + "_" + p.getProduct_number() + )); + + // 2. 批量查询已存在的商品 + List> storeProductPairs = productBases.stream() + .map(p -> Pair.of(p.getStore_id(), p.getProduct_number())) + .distinct() + .collect(Collectors.toList()); + + // 3. 批量查询(优化为一次查询) + List existing = batchGetByStoreAndProductNumber(storeProductPairs,productBases.get(0).getStore_id()); + + // 4. 构建存在商品的映射表 + return existing.stream() + .collect(Collectors.toMap( + p -> p.getStore_id() + "_" + p.getProduct_number(), + ShopProductBase::getProduct_id + )); + } + + + /** + * 批量根据店铺和货号查询商品 + */ + private List batchGetByStoreAndProductNumber(List> storeProductPairs,Integer storeId) { + if (CollUtil.isEmpty(storeProductPairs)) { + return Collections.emptyList(); + } + + // 使用IN查询优化(根据数据库特性可能需要分批) + QueryWrapper query = new QueryWrapper<>(); + query.select("product_id", "store_id", "product_number"); + + // 构建OR条件 (store_id=X AND product_number=Y) OR (store_id=A AND product_number=B)... + storeProductPairs.forEach(pair -> { + query.or(q -> q.eq("store_id", pair.getFirst()) + .eq("product_number", pair.getSecond())); + }); + + return list(query); + } + + + /** + * 处理商品图片批量操作 + * @param productImages 商品图片列表 + * @param productId 商品ID + */ + private void processProductImages(List productImages, Long productId, Integer storeId) { + if (CollUtil.isEmpty(productImages)) { + return; + } + + // 1. 查询现有图片 + List existingImages = shopProductImageService.getShopProductImageByStoreId(storeId); + Map existingImageMap = existingImages.stream() + .collect(Collectors.toMap(ShopProductImage::getProduct_image_id, Function.identity())); + + // 2. 分离需要新增、更新和删除的图片 + List toAdd = new ArrayList<>(); + List toUpdate = new ArrayList<>(); + List toDelete = new ArrayList<>(); + List delBaiduImages = new ArrayList<>(); + List addBaiduImages = new ArrayList<>(); + + for (ShopProductImage newImage : productImages) { + newImage.setProduct_id(productId); + // 设置默认值 + newImage.setItem_image_ext_1(""); + newImage.setItem_image_ext_2(""); + + // 判断是新增还是更新 + if (newImage.getProduct_image_id() == null) { + toAdd.add(newImage); + if (StrUtil.isNotBlank(newImage.getItem_image_default())) { + addBaiduImages.add(newImage.getItem_image_default()); + } + } else { + ShopProductImage oldImage = existingImageMap.get(newImage.getProduct_image_id()); + if (oldImage != null) { + // 检查图片是否有变化 + if (!StrUtil.equals(oldImage.getItem_image_default(), newImage.getItem_image_default())) { + if (StrUtil.isNotBlank(oldImage.getItem_image_default())) { + delBaiduImages.add(oldImage.getItem_image_default()); + } + if (StrUtil.isNotBlank(newImage.getItem_image_default())) { + addBaiduImages.add(newImage.getItem_image_default()); + } + } + toUpdate.add(newImage); + } + } + } + + // 找出需要删除的图片(存在但不在新列表中) + existingImages.forEach(oldImage -> { + boolean stillExists = productImages.stream() + .anyMatch(img -> Objects.equals(img.getProduct_image_id(), oldImage.getProduct_image_id())); + if (!stillExists) { + toDelete.add(oldImage.getProduct_image_id()); + if (StrUtil.isNotBlank(oldImage.getItem_image_default())) { + delBaiduImages.add(oldImage.getItem_image_default()); + } + } + }); + + // 3. 执行批量操作 + if (CollUtil.isNotEmpty(toAdd)) { + shopProductImageService.saveBatch(toAdd,toAdd.size()); + } + if (CollUtil.isNotEmpty(toUpdate)) { + shopProductImageService.updateBatchById(toUpdate); + } + if (CollUtil.isNotEmpty(toDelete)) { + shopProductImageService.removeByIds(toDelete); + } + + } + /** + * 处理商品辅助属性批量操作 + * @param assistIndices 辅助属性列表 + * @param storeId 商品ID + */ + private void processAssistIndices(List assistIndices, Integer storeId,boolean isUpdate) { + if (CollUtil.isEmpty(assistIndices)) { + return; + } + // 查询现有辅助属性 + // QueryWrapper query = new QueryWrapper<>(); + //query.eq("product_id", productId); + List existingAssists = assistIndexService.getShopProductAssistIndexStoreId(storeId); + + // 分离需要新增、更新和删除的辅助属性 + List toDelete = new ArrayList<>(); + // 找出需要删除的辅助属性(存在但不在新列表中) + existingAssists.forEach(oldAssist -> { + boolean stillExists = assistIndices.stream() + .anyMatch(a -> Objects.equals( + a.getProduct_assist_index_id(), + oldAssist.getProduct_assist_index_id() + )); + if (!stillExists) { + toDelete.add(oldAssist.getProduct_assist_index_id()); + } + }); + + // 执行批量操作 + if (CollUtil.isNotEmpty(assistIndices)) { + if(isUpdate){ + assistIndexService.updateBatchById(assistIndices); + }else { + assistIndexService.saveBatch(assistIndices,assistIndices.size()); + } + + } + + if (CollUtil.isNotEmpty(toDelete)) { + assistIndexService.removeByIds(toDelete); + } + } + + + // +// private void myShopProductBaseBatch(List list){ +// SqlSession sqlSession=sqlSessionFactory.openSession(); +// StopWatch stopWatch=new StopWatch(); +// stopWatch.start(); +// shopProductBaseService.saveBatch(list); +// sqlSession.commit(); +// stopWatch.stop(); +// } + + + } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductImageServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductImageServiceImpl.java index d1f10917..4a6854fc 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductImageServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductImageServiceImpl.java @@ -1,11 +1,18 @@ package com.suisung.mall.shop.product.service.impl; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.suisung.mall.common.modules.product.ShopProductImage; +import com.suisung.mall.core.web.service.RedisService; import com.suisung.mall.core.web.service.impl.BaseServiceImpl; import com.suisung.mall.shop.product.mapper.ShopProductImageMapper; import com.suisung.mall.shop.product.service.ShopProductImageService; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.Collections; +import java.util.List; +import java.util.Map; + /** *

@@ -17,4 +24,26 @@ import org.springframework.stereotype.Service; */ @Service public class ShopProductImageServiceImpl extends BaseServiceImpl implements ShopProductImageService { + + @Autowired + private RedisService redisService; + + @Override + public List getShopProductImageByStoreId(Integer storeId) { + + List existingImages = (List) redisService.get("ShopProductImageStoreId:"+storeId); + if(existingImages==null){ + QueryWrapper query = new QueryWrapper<>(); + query.eq("store_id", storeId); + existingImages = this.list(query); + redisService.set(""+storeId, existingImages); + } + + return existingImages; + } + + @Override + public void clearCacheShopProductImageByStoreId(Integer storeId) { + redisService.del("ShopProductImageStoreId:"+storeId); + } } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dao/BaseDao.java b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dao/BaseDao.java new file mode 100644 index 00000000..315034ea --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dao/BaseDao.java @@ -0,0 +1,148 @@ +package com.suisung.mall.shop.sixun.dao; + +import com.microsoft.sqlserver.jdbc.SQLServerDataSource; +import com.microsoft.sqlserver.jdbc.SQLServerException; +import com.suisung.mall.shop.sixun.dto.ResultDto; +import lombok.extern.slf4j.Slf4j; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +@Slf4j +public class BaseDao { + + private final static String DRIVER = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; + private final static String DEFAULT_IP="127.0.0.1"; + private final static String DEFAULT_DATABASE="hbposev9"; + private final static String DEFAULT_USERNAME="sa"; + private final static String DEFAULT_PWD="123456"; + private final static int PortNumber=1433; + private final static int LoginTimeout=10; + /** + * + * 动态获取数据库连接 + * @param ip 数据库ip+ + * @param username 用户名 + * @param password 密码 todo 需要加密 + * @param dataBaseName 数据库名称 + * @return + */ + public Connection getConnection(String ip, String username, String password, String dataBaseName){ + Connection conn=null; + SQLServerDataSource sqlServerDataSource=new SQLServerDataSource(); + sqlServerDataSource.setDatabaseName(dataBaseName==null?DEFAULT_DATABASE:dataBaseName); + sqlServerDataSource.setServerName(ip==null?DEFAULT_IP:ip); + sqlServerDataSource.setPortNumber(PortNumber); + sqlServerDataSource.setLoginTimeout(LoginTimeout); + sqlServerDataSource.setPassword(password==null?DEFAULT_PWD:password); + sqlServerDataSource.setUser(username==null?DEFAULT_USERNAME:username); + try { + conn=sqlServerDataSource.getConnection(); + } catch (SQLServerException e) { + log.info("数据库连接异常方法{}异常信息{}","com.suisung.mall.shop.sixun.dao.BaseDao.getConnection",e.getMessage()); + throw new RuntimeException(e); + } + + return conn; + } + + /** + * 关闭数据库连接 + * @param conn + */ + public void Close(Connection conn){ + if(conn!=null){ + try { + conn.close(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + } + + /** + * 返回游标和连接 减少服务器的占用内存 + * @param ip + * @param username + * @param password + * @param dataBaseName + * @return ResultDto + */ + public ResultDto baseFindList(String ip, String username, String password, String dataBaseName, String table){ + Connection connection=getConnection(ip,username,password,dataBaseName); + String sql="select * from %s where 1=1"; + sql=String.format(sql, table); + ResultDto resultDto=new ResultDto(); + ResultSet rs=null; + try { + PreparedStatement ps= connection.prepareStatement(sql); + rs = ps.executeQuery(); + } catch (SQLException e) { + log.info("数据库查询异常方法{},异常信息{}","com.suisung.mall.shop.sixun.dao.BaseDao.baseFindList",e.getMessage()); + throw new RuntimeException(e); + } + resultDto.setResultSet(rs); + resultDto.setConnection(connection); + return resultDto; + + } + + /** + * 带分页数据 + * @param ip + * @param username + * @param password + * @param dataBaseName + * @param table + * @return + */ + public ResultDto baseFindListPage(String ip, String username, String password, String dataBaseName, String table,String orderColumn, int pageNo, int pageSize){ + Connection connection=getConnection(ip,username,password,dataBaseName); + int start=(pageNo-1)*pageSize+1; + int end=pageNo*pageSize; + String sql=" select * from( " + + " select ROW_NUMBER() OVER(ORDER BY %s) as rowId,* from %s " + + " ) as r where rowId between %s and %s"; + sql=String.format(sql, orderColumn,table,start,end); + ResultDto resultDto=new ResultDto(); + ResultSet rs=null; + try { + PreparedStatement ps= connection.prepareStatement(sql); + rs = ps.executeQuery(); + } catch (SQLException e) { + log.info("数据库查询异常方法{},异常信息{}","com.suisung.mall.shop.sixun.dao.BaseDao.baseFindListPage",e.getMessage()); + throw new RuntimeException(e); + } + resultDto.setResultSet(rs); + resultDto.setConnection(connection); + return resultDto; + } + + public Integer getBaseTotal(String ip, String username, String password, String dataBaseName, String table){ + int total=0; + Connection connection=getConnection(ip,username,password,dataBaseName); + try { + String sql="select count(*) from %s where 1=1"; + sql=String.format(sql, table); + PreparedStatement ps= connection.prepareStatement(sql); + ResultSet rs=ps.executeQuery(); + while (rs.next()){ + total=rs.getInt(1); + } + } catch (SQLException e) { + log.info("数据库查询异常方法{},异常信息{}","com.suisung.mall.shop.sixun.dao.BaseDao.getBaseTotal",e.getMessage()); + throw new RuntimeException(e); + } + finally { + try { + connection.close(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + return total; + } + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dao/SxDataDao.java b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dao/SxDataDao.java new file mode 100644 index 00000000..0e334389 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dao/SxDataDao.java @@ -0,0 +1,238 @@ +package com.suisung.mall.shop.sixun.dao; + +import com.suisung.mall.common.modules.sixun.SxSyncCategory; +import com.suisung.mall.common.modules.sixun.SxSyncGoods; +import com.suisung.mall.common.modules.sixun.SxSyncVip; +import com.suisung.mall.shop.sixun.dto.DataBaseInfo; +import com.suisung.mall.shop.sixun.dto.ResultDto; +import com.suisung.mall.shop.sixun.dto.SxCategoryModel; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.sql.*; +import java.util.ArrayList; +import java.util.List; + +/** + * 考虑到每个思迅软件都是自己的数据,所以采用动态获取的方式获取数据 + * 数据库为MS SQL + * todo 如果考虑到数据量需要分页多线程 + */ +@Service +public class SxDataDao extends BaseDao{ + + private final static String T_BD_ITEM_CLS="t_bd_item_cls";//商品分类 + private final static String T_BD_ITEM_INFO="t_bd_item_info";//商品表 + private final static String RM_VIP_INFO="rm_vip_info";//会员表 + + private final static String ITEM_CLSNO="item_clsno";//商品分类排序字段 + private final static String ITEM_NO="item_no";//商品排序字段 + private final static String CARD_ID="card_id";//会员表排序字段 + + public final static Integer PAGESIZE=500; + + public final static String DEFAULT_IMG="https://digitalassets.tesla.com/tesla-contents/image/upload/f_auto,q_auto/Homepage-Model-Y-2-Promo-Hero-Tablet-CN.png"; + + + /** + * 查找商品分类数据 + * @param ip + * @param username + * @param password + * @param dataBaseName + */ + public void findTBdItemClsList(String ip,String username,String password,String dataBaseName){ + ResultDto resultDto=baseFindList(ip,username,password,dataBaseName,T_BD_ITEM_CLS); + ResultSet rs= resultDto.getResultSet(); + try { + while (rs.next()) { + System.out.printf(rs.getString("item_clsno"));//分类编码 + System.out.printf(rs.getString("item_clsname")+"\t");//分类名称 + System.out.print(rs.getString("cls_parent")+"\t");//父级编码 + System.out.print(rs.getString("item_flag")+"\t" + "\n");//显示标识 + } + } catch (SQLException e) { + throw new RuntimeException(e); + } finally { + try { + resultDto.getConnection().close(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + } + + /** + * 分页查找商品分类数据 + * @param dataBaseInfo + * @param pageNo + * @param pageSize + */ + public List findTBdItemClsListPage(DataBaseInfo dataBaseInfo, int pageNo, int pageSize){ + ResultDto resultDto=baseFindListPage(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDataBaseName(),T_BD_ITEM_CLS,ITEM_CLSNO,pageNo,pageSize); + ResultSet rs= resultDto.getResultSet(); + List sxSyncCategories=new ArrayList<>(); + SxSyncCategory sxSyncCategory=null; + try { + while (rs.next()) { + sxSyncCategory=new SxSyncCategory(); + sxSyncCategory.setItem_clsname(rs.getString("item_clsname"));//分类名称 + sxSyncCategory.setCls_parent(rs.getString("cls_parent"));//父级编码 + sxSyncCategory.setItem_clsno(rs.getString("item_clsno"));//分类编码 +// System.out.printf(rs.getString("item_clsno"));//分类编码 +// System.out.printf(rs.getString("item_clsname")+"\t");//分类名称 +// System.out.print(rs.getString("cls_parent")+"\t");//父级编码 +// System.out.print(rs.getString("item_flag")+"\t" + "\n");//显示标识 + sxSyncCategories.add(sxSyncCategory); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } finally { + try { + resultDto.getConnection().close(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + return sxSyncCategories; + } + + /** + * 获取商品分类TBdItemCls表的数量 + * @param dataBaseInfo + * @return + */ + public Integer getTBdItemClsTotal(DataBaseInfo dataBaseInfo){ + return getBaseTotal(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDataBaseName(),T_BD_ITEM_CLS); + } + + /** + * 获取商品表t_bd_item_info表的数量 + * @param dataBaseInfo + * @return + */ + public int getTBditemInfoTotal(DataBaseInfo dataBaseInfo){ + return getBaseTotal(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDataBaseName(),ITEM_NO); + } + + /** + * 获取会员表t_rm_vip_info表的数量 + * @param dataBaseInfo + * @return + */ + public int getTrmVipInfoTotal(DataBaseInfo dataBaseInfo){ + return getBaseTotal(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDataBaseName(),CARD_ID); + } + + + /** + * 分页查找商品数据 + * 表T_BD_ITEM_INFO + * @param dataBaseInfo + * @param pageNo + * @param pageSize + */ + public List findBditemInfoListPage(DataBaseInfo dataBaseInfo,int pageNo,int pageSize){ + ResultDto resultDto=baseFindListPage(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDataBaseName(),T_BD_ITEM_INFO,ITEM_CLSNO,pageNo,pageSize); + ResultSet rs= resultDto.getResultSet(); + List sxSyncGoodses=new ArrayList<>(); + SxSyncGoods sxSyncGoods=null; + try { + while (rs.next()) { + BigDecimal price=new BigDecimal(rs.getString("price")); + BigDecimal salePrice=new BigDecimal(rs.getString("sale_price")); + BigDecimal gross= salePrice.subtract(price).divide(salePrice,2, RoundingMode.HALF_UP); + sxSyncGoods=new SxSyncGoods(); + sxSyncGoods.setItem_no(rs.getString("item_no"));//货号 + sxSyncGoods.setItem_subname(rs.getString("item_subname"));//商品名称 + sxSyncGoods.setItem_subno(rs.getString("item_subno"));//商品条码 + sxSyncGoods.setBig_cls_name("9999");//商品大类 todo 如何关联 + sxSyncGoods.setSmall_cls_name("9999");//商品小类 todo 如何关联 + sxSyncGoods.setItem_size(rs.getString("item_size"));//规格 + sxSyncGoods.setUnit_no(rs.getString("unit_no"));//单位 todo + sxSyncGoods.setStock(rs.getBigDecimal("item_flag"));//库存数量 todo item_stock? + + + sxSyncGoods.setGross_margin(gross);//毛利率 todo + sxSyncGoods.setPrice(rs.getBigDecimal("price"));//进货价 + sxSyncGoods.setSale_price(rs.getBigDecimal("sale_price"));//零售价 + + sxSyncGoods.setVip_price(rs.getBigDecimal("vip_price"));//会员价 + sxSyncGoods.setVip_acc_flag(rs.getBigDecimal("vip_acc_flag"));//允许积分 + sxSyncGoods.setVip_acc_num(rs.getBigDecimal("vip_acc_num"));//积分值 + sxSyncGoods.setSale_flag(rs.getInt("main_Sale_flag"));//商品状态 todo 是main_Sale_flag? + sxSyncGoods.setItem_rem(rs.getString("item_rem"));//助记码 + sxSyncGoods.setBuild_date(rs.getString("build_date"));//生产日期 + sxSyncGoods.setValid_days(rs.getString("item_flag"));//保质期 todo stop_date-build_date? + + sxSyncGoodses.add(sxSyncGoods); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } finally { + try { + resultDto.getConnection().close(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + return sxSyncGoodses; + } + + + /** + * 分页查找会员数据 + * RM_VIP_INFO + * @param dataBaseInfo + * @param pageNo + * @param pageSize + */ + public List findRmVipInfoListPage(DataBaseInfo dataBaseInfo, int pageNo, int pageSize){ + ResultDto resultDto=baseFindListPage(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDataBaseName(),RM_VIP_INFO,CARD_ID,pageNo,pageSize); + ResultSet rs= resultDto.getResultSet(); + List sxSyncVips=new ArrayList<>(); + SxSyncVip sxSyncVip=null; + try { + while (rs.next()) { + sxSyncVip = new SxSyncVip(); + int cardStatus=rs.getInt("card_status"); + if(cardStatus!=1){ + sxSyncVip.setVip_name(rs.getString("vip_name"));//会员名称 + sxSyncVip.setVip_sex(rs.getString("vip_sex"));//会员名称 + sxSyncVip.setMobile(rs.getString("mobile"));//会员名称 + sxSyncVip.setBirthday(rs.getString("birthday"));//会员生日 + sxSyncVip.setCard_type(rs.getString("card_type")==null?"v1":rs.getString("card_type"));//会员生日 + sxSyncVip.setCard_no(rs.getString("card_no"));//会员卡号 + sxSyncVip.setCard_no(rs.getString("now_acc_num"));//会员积分 + sxSyncVip.setCard_no(rs.getString("residual_amt"));//储值余额 + sxSyncVip.setCard_no(rs.getString("vip_start_date"));//建档日期 + + System.out.println(rs.getString("vip_name"));//会员名称 + System.out.println(rs.getString("mobile"));//会员手机号 + System.out.println(rs.getString("vip_sex"));//会员性别 + System.out.println(rs.getString("birthday"));//会员生日 + System.out.println(rs.getString("card_no"));//会员卡号 + System.out.println(rs.getString("card_type"));//会员等级 + System.out.println(rs.getBigDecimal("residual_amt"));//储值余额 + System.out.println(rs.getBigDecimal("now_acc_num"));//会员积分 + System.out.println(rs.getString("vip_start_date"));//建档日期 + System.out.println(rs.getInt("card_status"));//会员状态 + } + + sxSyncVips.add(sxSyncVip); + } + }catch (SQLException e){ + throw new RuntimeException(e); + } + return sxSyncVips; + } + + + public static void main(String[] args) { + //new SxDataUtils().findTBdItemClsList("127.0.0.1","sa","123456","hbposev9"); + new SxDataDao().findTBdItemClsListPage(new DataBaseInfo(),4,10); + } + + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/DataBaseInfo.java b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/DataBaseInfo.java new file mode 100644 index 00000000..2537931f --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/DataBaseInfo.java @@ -0,0 +1,16 @@ +package com.suisung.mall.shop.sixun.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class DataBaseInfo { + @ApiModelProperty("数据库IP") + private String ip; + @ApiModelProperty("用户名") + private String userName; + @ApiModelProperty("密码") + private String password; + @ApiModelProperty("数据库名称") + private String dataBaseName; +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/ProductImage.java b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/ProductImage.java new file mode 100644 index 00000000..f7d48cdf --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/ProductImage.java @@ -0,0 +1,17 @@ +package com.suisung.mall.shop.sixun.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class ProductImage { + @ApiModelProperty("图片路径") + private String image_url; + @ApiModelProperty("推荐值") + private String seq; + @ApiModelProperty("介绍") + private String desc; + @ApiModelProperty("是否默认") + private String is_default; + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/PromotionDetail.java b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/PromotionDetail.java new file mode 100644 index 00000000..021be2be --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/PromotionDetail.java @@ -0,0 +1,17 @@ +package com.suisung.mall.shop.sixun.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class PromotionDetail { + + @ApiModelProperty("活动id") + private String activity_id; + @ApiModelProperty("活动名称") + private String activity_name; + @ApiModelProperty("价格") + private String price; + @ApiModelProperty("介绍") + private String intro; +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/ResultDto.java b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/ResultDto.java new file mode 100644 index 00000000..f5059bca --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/ResultDto.java @@ -0,0 +1,15 @@ +package com.suisung.mall.shop.sixun.dto; + +import lombok.Data; + +import java.sql.Connection; +import java.sql.ResultSet; + +@Data +public class ResultDto { + + private Connection connection; + + private ResultSet resultSet; + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/SxCategoryModel.java b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/SxCategoryModel.java new file mode 100644 index 00000000..5775db46 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/SxCategoryModel.java @@ -0,0 +1,34 @@ +package com.suisung.mall.shop.sixun.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import java.math.BigDecimal; + +/** + * 模型对应 + */ +@Data +public class SxCategoryModel { + //模型对应ShopBaseProductCategory start + private String parent_name;//暂时不用 + @NotEmpty + @ApiModelProperty(value = "商品分类名称") + private String category_name; + @ApiModelProperty(value = "分类图片") + private String category_image; + @ApiModelProperty(value = "是否允许虚拟商品(ENUM):1-是; 0-否") + private Integer category_virtual_enable; + @ApiModelProperty(value = "分佣比例-百分比") + private BigDecimal category_commission_rate; + // private String type_name;//todo 看代码没有用,使用的是product_type + //模型对应ShopBaseProductCategory end + @ApiModelProperty(value = "产品类型") + private String product_type; + @ApiModelProperty(value = "第一级父类") + private String first_category_name; + @ApiModelProperty(value = "第二级父类") + private String second_category_name; + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/SxGoosModel.java b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/SxGoosModel.java new file mode 100644 index 00000000..ea863797 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/dto/SxGoosModel.java @@ -0,0 +1,103 @@ +package com.suisung.mall.shop.sixun.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +/** + * 思迅同步商品数据入口数据 + */ +@Data +public class SxGoosModel { + + @ApiModelProperty("商品名称") + private String product_name; + + @ApiModelProperty("商品货号") + private String product_number; + + @ApiModelProperty("商品条形码") + private String product_barcode; + + @ApiModelProperty("一级分类") + private String first_category_name; + + @ApiModelProperty("二级分类") + private String second_category_name; + + @ApiModelProperty("三级分类") + private String three_category_name; + + @ApiModelProperty("产品类型") + private String product_type; + + @ApiModelProperty("商品种类") + private String product_kind; + + @ApiModelProperty("成本价") + private BigDecimal cost_price; + + @ApiModelProperty("价格") + private BigDecimal price; + + @ApiModelProperty("原价") + private BigDecimal original_price; + + @ApiModelProperty("商品价格") + private BigDecimal retail_price; + + @ApiModelProperty("会员价") + private BigDecimal member_price; + + @ApiModelProperty("库存") + private BigDecimal stock; + + @ApiModelProperty("") + private BigDecimal gross_margin; + + @ApiModelProperty("规格单位") + private String unit; + + @ApiModelProperty("可用积分") + private BigDecimal can_piont; + + @ApiModelProperty("总积分") + private BigDecimal points; + + @ApiModelProperty("助记码") + private String mnemonic; + + + @ApiModelProperty("最大购买商品量") + private Integer buy_limit; + + @ApiModelProperty("品牌名称") + private String brand_name; + + @ApiModelProperty("标签") + private String tags; + + @ApiModelProperty("辅助属性") + private List product_assist; + + @ApiModelProperty("规格(JSON)-规格、规格值、goods_id 规格不需要全选就可以添加对应数据[") + private List product_spec; + + @ApiModelProperty("商品卖点特征") + private String product_value; + + @ApiModelProperty("商品视频") + private String product_video; + + @ApiModelProperty("产品描述") + private String product_desc; + + @ApiModelProperty("商品图片库") + private List product_images; + + + @ApiModelProperty("商品详情") + private List promotion_detail; +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/SxSyncCategoryService.java b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/SxSyncCategoryService.java index e4f2e2af..8ae201ba 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/SxSyncCategoryService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/SxSyncCategoryService.java @@ -11,6 +11,8 @@ package com.suisung.mall.shop.sixun.service; import com.baomidou.mybatisplus.core.metadata.IPage; import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.modules.sixun.SxSyncCategory; +import com.suisung.mall.shop.sixun.dto.DataBaseInfo; +import com.suisung.mall.shop.sixun.dto.SxCategoryModel; import java.util.List; @@ -82,4 +84,22 @@ public interface SxSyncCategoryService { * @return */ Boolean updateStatusBatch(List list, Integer status); + + /** + * 通过数据库获取店铺的商品分类 + * + * @param dataBaseInfo + * @return + */ + List getCategoryByDataBasePage( DataBaseInfo dataBaseInfo,int startPage,int pageSize); + + /** + * 通过数据库获取店铺的商品分类总数 + * @param dataBaseInfo + * @return + */ + Integer getCategoryTotal( DataBaseInfo dataBaseInfo); + + + } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/SxSyncGoodsService.java b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/SxSyncGoodsService.java index 80a00e08..db5e7a33 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/SxSyncGoodsService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/SxSyncGoodsService.java @@ -11,6 +11,7 @@ package com.suisung.mall.shop.sixun.service; import com.baomidou.mybatisplus.core.metadata.IPage; import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.modules.sixun.SxSyncGoods; +import com.suisung.mall.shop.sixun.dto.DataBaseInfo; import java.util.List; @@ -75,4 +76,20 @@ public interface SxSyncGoodsService { * @return */ IPage pageByStoreId(String storeId, Integer status, Integer pageNum, Integer pageSize); + + /** + * 通过思迅数据库获取店铺的商品总数 + * @param dataBaseInfo + * @return + */ + Integer getGoodsTotal(DataBaseInfo dataBaseInfo); + + /** + * 通过思迅数据库分页查找店铺的商品信息 + * @param dataBaseInfo + * @param pageNum + * @param pageSize + * @return + */ + List findGoodsListPage(DataBaseInfo dataBaseInfo,int pageNum,int pageSize); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/SxSyncVipService.java b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/SxSyncVipService.java index 57648960..889dddef 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/SxSyncVipService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/SxSyncVipService.java @@ -10,6 +10,7 @@ package com.suisung.mall.shop.sixun.service; import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.modules.sixun.SxSyncVip; +import com.suisung.mall.shop.sixun.dto.DataBaseInfo; import java.util.List; @@ -57,4 +58,18 @@ public interface SxSyncVipService { * @return */ SxSyncVip getByVipNo(String storeId, String vipNo); + + /** + * 通过数据库获取会员总数 + * @param dataBaseInfo + * @return + */ + Integer getVipMembersTotal( DataBaseInfo dataBaseInfo); + + /** + * 分页通过数据库获取会员数据 + * @param dataBaseInfo + * @return + */ + List findVipMemberPage( DataBaseInfo dataBaseInfo,int pageNum,int pageSize); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/impl/SxSyncCategoryServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/impl/SxSyncCategoryServiceImpl.java index f46c1efc..b60372b4 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/impl/SxSyncCategoryServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/impl/SxSyncCategoryServiceImpl.java @@ -9,7 +9,9 @@ package com.suisung.mall.shop.sixun.service.impl; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; @@ -20,17 +22,23 @@ import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.constant.CommonConstant; import com.suisung.mall.common.modules.sixun.SxSyncCategory; import com.suisung.mall.core.web.service.impl.BaseServiceImpl; +import com.suisung.mall.shop.sixun.dao.SxDataDao; +import com.suisung.mall.shop.sixun.dto.DataBaseInfo; +import com.suisung.mall.shop.sixun.dto.SxCategoryModel; import com.suisung.mall.shop.sixun.mapper.SxSyncCategoryMapper; import com.suisung.mall.shop.sixun.service.SxSyncCategoryService; import com.suisung.mall.shop.sixun.utils.CommonUtil; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.Date; -import java.util.List; -import java.util.Objects; +import javax.annotation.Resource; +import java.util.*; +import java.util.stream.Collectors; @Service public class SxSyncCategoryServiceImpl extends BaseServiceImpl implements SxSyncCategoryService { + @Resource + private SxDataDao sxDataDao; /** * 同步店铺的商品分类 * @@ -238,4 +246,74 @@ public class SxSyncCategoryServiceImpl extends BaseServiceImpl getCategoryByDataBasePage(DataBaseInfo dataBaseInfo,int startPage,int pageSize) { + List sxSyncCategories=sxDataDao.findTBdItemClsListPage(dataBaseInfo,startPage, pageSize); + if(CollectionUtil.isEmpty(sxSyncCategories)){ + return new ArrayList<>(); + } + List sxSyncCategoriesCp = new ArrayList<>(sxSyncCategories); + Iterator iterator= sxSyncCategories.iterator(); + List sxCategoryModels=new ArrayList<>(); + SxCategoryModel sxCategoryModel=null; + while (iterator.hasNext()){ + SxSyncCategory sxSyncCategory= iterator.next(); + sxCategoryModel=new SxCategoryModel(); + sxCategoryModel.setCategory_image(SxDataDao.DEFAULT_IMG); + sxCategoryModel.setCategory_name(sxSyncCategory.getItem_clsname()); + //寻找父级 + if(null!=sxSyncCategory.getCls_parent()){ + SxSyncCategory firstNode=getParentNode(sxSyncCategoriesCp,sxSyncCategory.getCls_parent()); + sxCategoryModel.setParent_name(firstNode.getItem_clsname());//todo 暂时无用 + //如何存在上级的上级,则上级为第二层,上上及为第一层 + if(null!=firstNode.getCls_parent()) {//还存在上级 + SxSyncCategory secondNode=getParentNode(sxSyncCategoriesCp,sxSyncCategory.getCls_parent()); + sxCategoryModel.setFirst_category_name(secondNode.getItem_clsname()); + sxCategoryModel.setSecond_category_name(firstNode.getItem_clsname()); + }else { + sxCategoryModel.setFirst_category_name(firstNode.getItem_clsname()); + } + } + sxCategoryModels.add(sxCategoryModel); + } + return sxCategoryModels; + } + + /** + * 通过流查找父节点 + * @param sxSyncCategories + * @param parentId + */ + private SxSyncCategory getParentNode(List sxSyncCategories,String parentId){ + List list= sxSyncCategories.stream().filter(cc-> + cc.getItem_clsno().trim().equals(parentId.trim())) + .collect(Collectors.toList()); + return CollectionUtil.isNotEmpty(list)?list.get(0):new SxSyncCategory(); + } + + @Override + public Integer getCategoryTotal(DataBaseInfo dataBaseInfo) { + // 记录总数 + return sxDataDao.getTBdItemClsTotal(dataBaseInfo); + } + + + public static void main(String[] args) { + List list= new SxSyncCategoryServiceImpl().getCategoryByDataBasePage(new DataBaseInfo(),1,50); + list.forEach(e->{ + System.out.println("node"+e.getCategory_name()+"--First_category_name--"+e.getFirst_category_name()+"--Second_category_name--"+e.getSecond_category_name()); + }); + } + + //########### 从思迅数据库获取数据区域 end } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/impl/SxSyncGoodsServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/impl/SxSyncGoodsServiceImpl.java index 5185e34f..ff2738c1 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/impl/SxSyncGoodsServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/impl/SxSyncGoodsServiceImpl.java @@ -20,18 +20,22 @@ import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.constant.CommonConstant; import com.suisung.mall.common.modules.sixun.SxSyncGoods; import com.suisung.mall.core.web.service.impl.BaseServiceImpl; +import com.suisung.mall.shop.sixun.dao.SxDataDao; +import com.suisung.mall.shop.sixun.dto.DataBaseInfo; import com.suisung.mall.shop.sixun.mapper.SxSyncGoodsMapper; import com.suisung.mall.shop.sixun.service.SxSyncGoodsService; import com.suisung.mall.shop.sixun.utils.CommonUtil; import org.springframework.stereotype.Service; +import javax.annotation.Resource; import java.util.Date; import java.util.List; import java.util.Objects; @Service public class SxSyncGoodsServiceImpl extends BaseServiceImpl implements SxSyncGoodsService { - + @Resource + private SxDataDao sxDataDao; /** * 同步记录 * @@ -185,7 +189,6 @@ public class SxSyncGoodsServiceImpl extends BaseServiceImpl(pageNum, pageSize), queryWrapper); } - // ########### 从思迅商锐9.7系统获取数据区域 开始 public List pageGoodsListFromSiXun(Integer pageNo, Integer pageSize) { @@ -223,5 +226,12 @@ public class SxSyncGoodsServiceImpl extends BaseServiceImpl findGoodsListPage(DataBaseInfo dataBaseInfo, int pageNum, int pageSize) { + return sxDataDao.findBditemInfoListPage(dataBaseInfo,pageNum,pageSize); + } } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/impl/SxSyncVipServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/impl/SxSyncVipServiceImpl.java index e5f044e6..95f6d11f 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/impl/SxSyncVipServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/service/impl/SxSyncVipServiceImpl.java @@ -18,18 +18,22 @@ import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.constant.CommonConstant; import com.suisung.mall.common.modules.sixun.SxSyncVip; import com.suisung.mall.core.web.service.impl.BaseServiceImpl; +import com.suisung.mall.shop.sixun.dao.SxDataDao; +import com.suisung.mall.shop.sixun.dto.DataBaseInfo; import com.suisung.mall.shop.sixun.mapper.SxSyncVipMapper; import com.suisung.mall.shop.sixun.service.SxSyncVipService; import com.suisung.mall.shop.sixun.utils.CommonUtil; import org.springframework.stereotype.Service; +import javax.annotation.Resource; import java.util.Date; import java.util.List; import java.util.Objects; @Service public class SxSyncVipServiceImpl extends BaseServiceImpl implements SxSyncVipService { - + @Resource + private SxDataDao sxDataDao; /** * 同步店铺的 * @@ -188,4 +192,14 @@ public class SxSyncVipServiceImpl extends BaseServiceImpl findVipMemberPage(DataBaseInfo dataBaseInfo,int pageNum,int pageSize) { + return sxDataDao.findRmVipInfoListPage(dataBaseInfo,pageNum,pageSize); + } + } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sixun/utils/FileUtils.java b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/utils/FileUtils.java new file mode 100644 index 00000000..a0eb6077 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sixun/utils/FileUtils.java @@ -0,0 +1,65 @@ +package com.suisung.mall.shop.sixun.utils; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.IOException; +import java.util.Calendar; + +@Component +public class FileUtils { + public static final String pathSeparator = System.getProperty("file.separator"); + public static final String GOODS = "goods"+pathSeparator;//商品 + public static final String CATEGORY= "category"+pathSeparator;//分类 + public static final String BRAND = "brand"+pathSeparator;//品牌 + public static final String MEMBER= "member"+pathSeparator;//会员 + + public String getSyncTypeFlag(String syncType,String path){ + Calendar calendar=Calendar.getInstance(); + int year=calendar.get(Calendar.YEAR); + int month=calendar.get(Calendar.MONTH)+1; + int date=calendar.get(Calendar.DAY_OF_MONTH); + String result = path+pathSeparator+"uploaded"+pathSeparator; + switch (syncType){ + case "1": + result+=GOODS; + break; + case "2": + result+=CATEGORY; + break; + case "3": + result+=BRAND; + break; + case "4": + result+=MEMBER; + break; + default: + break; + } + return result+year+pathSeparator+month+pathSeparator+date+pathSeparator; + } + + /** + * 创建文件 + * @param folderPath + * @param fileName + */ + public static String createFolderAndFileUsingFile(String folderPath, String fileName) { + String filePath = folderPath + fileName; + File folder = new File(folderPath); + if (!folder.exists()) { + folder.mkdirs(); + } + + File file = new File(filePath); + try { + if (!file.exists()) { + file.createNewFile(); + } + } catch (IOException e) { + e.printStackTrace(); + } + return filePath; + } +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/Utils/BatchInsertUtils.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/Utils/BatchInsertUtils.java new file mode 100644 index 00000000..4a5f7951 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/Utils/BatchInsertUtils.java @@ -0,0 +1,91 @@ +package com.suisung.mall.shop.sync.Utils; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.suisung.mall.common.modules.product.ShopProductBase; +import com.suisung.mall.shop.product.mapper.ShopProductBaseMapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.session.ExecutorType; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.lang.reflect.Method; +import java.util.List; + +@Component +@Slf4j +public class BatchInsertUtils { + private static final int DEFAULT_BATCH_SIZE = 1000; + + @Autowired + private SqlSessionFactory sqlSessionFactory; + /** + * 高性能批量插入 + */ + public static boolean batchInsert(Class mapperClass, List dataList, SqlSessionFactory sqlSessionFactory) { + return batchInsert(mapperClass, dataList, DEFAULT_BATCH_SIZE,sqlSessionFactory); + } + + public static boolean batchInsert(Class mapperClass, List dataList, int batchSize, SqlSessionFactory sqlSessionFactory) { + if (CollectionUtils.isEmpty(dataList)) { + return true; + } + + try (SqlSession sqlSession = SqlSessionUtils.getSqlSession( + sqlSessionFactory, + ExecutorType.BATCH, + null)) { + + BaseMapper mapper = (BaseMapper) sqlSession.getMapper(mapperClass); + int size = dataList.size(); + + for (int i = 0; i < size; i++) { + mapper.insert(dataList.get(i)); + // 分批提交 + if (i > 0 && i % batchSize == 0 || i == size - 1) { + sqlSession.flushStatements(); + } + } + return true; + } catch (Exception e) { + log.error("批量插入失败", e); + return false; + } + } + + /** + * 使用VALUES列表批量插入 + */ + public static boolean batchInsertWithValues(Class> mapperClass, + String methodName, + List dataList,SqlSessionFactory sqlSessionFactory) { + if (CollectionUtils.isEmpty(dataList)) { + return true; + } + + try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) { + BaseMapper mapper = sqlSession.getMapper(mapperClass); + Method method = mapper.getClass().getMethod(methodName, List.class); + + int total = dataList.size(); + int batchCount = (total + DEFAULT_BATCH_SIZE - 1) / DEFAULT_BATCH_SIZE; + + for (int i = 0; i < batchCount; i++) { + int from = i * DEFAULT_BATCH_SIZE; + int to = Math.min(from + DEFAULT_BATCH_SIZE, total); + List subList = dataList.subList(from, to); + + method.invoke(mapper, subList); + sqlSession.flushStatements(); + } + return true; + } catch (Exception e) { + log.error("批量插入失败", e); + return false; + } + } + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/Utils/CryptoUtils.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/Utils/CryptoUtils.java new file mode 100644 index 00000000..49170209 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/Utils/CryptoUtils.java @@ -0,0 +1,117 @@ +package com.suisung.mall.shop.sync.Utils; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +public class CryptoUtils { + private static final String ALGORITHM = "AES"; + private static final String TRANSFORMATION = "AES/ECB/PKCS5Padding"; + private static final String SECRET_KEY = "9f823ea6ab22785caf040e4cc3930619"; // 必须16/24/32字符 + + private static final String HASH_ALGORITHM_KEY = "appKey=a&sign=b&storeId=c"; + + /** + * 打包并加密字段 + */ + public static String packAndEncrypt(String appKey, String sign, String storeId) throws Exception { + String combined = String.format("appKey=%s&sign=%s&storeId=%s", appKey, sign, storeId); + return encrypt(combined); + } + + /** + * 32位字符串生成 + * @param input + * @return + * @throws NoSuchAlgorithmException + */ + public static String generate32CharMD5(String input) throws NoSuchAlgorithmException { + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] hashBytes = md.digest(input.getBytes()); + + StringBuilder hexString = new StringBuilder(); + for (byte b : hashBytes) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + + return hexString.toString(); + } + + /** + * 解密并解包字段 + */ + public static Map decryptAndUnpack(String encryptedData) throws Exception { + String decrypted = decrypt(encryptedData); + Map result = new HashMap<>(); + String[] pairs = decrypted.split("&"); + for (String pair : pairs) { + String[] keyValue = pair.split("="); + if (keyValue.length == 2) { + result.put(keyValue[0], keyValue[1]); + } + } + return result; + } + + /** + * 加密 + * @param value + * @return + * @throws Exception + */ + private static String encrypt(String value) throws Exception { + SecretKeySpec secretKey = new SecretKeySpec(SECRET_KEY.getBytes(StandardCharsets.UTF_8), ALGORITHM); + Cipher cipher = Cipher.getInstance(TRANSFORMATION); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + byte[] encryptedBytes = cipher.doFinal(value.getBytes(StandardCharsets.UTF_8)); + return Base64.getEncoder().encodeToString(encryptedBytes); + } + + /** + * 解密 + * @param encryptedValue + * @return + * @throws Exception + */ + private static String decrypt(String encryptedValue) throws Exception { + SecretKeySpec secretKey = new SecretKeySpec(SECRET_KEY.getBytes(StandardCharsets.UTF_8), ALGORITHM); + Cipher cipher = Cipher.getInstance(TRANSFORMATION); + cipher.init(Cipher.DECRYPT_MODE, secretKey); + byte[] decodedBytes = Base64.getDecoder().decode(encryptedValue); + byte[] decryptedBytes = cipher.doFinal(decodedBytes); + return new String(decryptedBytes, StandardCharsets.UTF_8); + } + + public static String dealBlankStr(String original) throws UnsupportedEncodingException { + return original.replaceAll(" ", "+"); + } + + + // 示例用法 + public static void main(String[] args) throws Exception { + + String appKey = "d68397c4fb671bc024e24e1964b067cc35388818"; + String sign = "d68397c4fb671bc024e24e1964b067cc35388818"; + String storeId = "1"; + + // 打包加密 + String encrypted = packAndEncrypt(appKey, sign, storeId); + System.out.println("加密结果: " + encrypted); + + // 解密解包 + Map result = decryptAndUnpack(encrypted); + System.out.println("解密结果: " + result); + } + + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/Utils/ThreadFileUtils.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/Utils/ThreadFileUtils.java new file mode 100644 index 00000000..790552d1 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/Utils/ThreadFileUtils.java @@ -0,0 +1,37 @@ +package com.suisung.mall.shop.sync.Utils; + +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONUtil; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.rmi.RemoteException; + +@lombok.extern.slf4j.Slf4j +public class ThreadFileUtils { + + public JSONArray processFolder(String taskName, String folderPath) throws RuntimeException { + File folder = new File(folderPath); + if (!folder.exists() || !folder.isDirectory()) { + throw new RuntimeException("文件夹不存在或不是目录: " + folderPath); + } + + File[] jsonFiles = folder.listFiles((dir, name) -> name.endsWith(".txt")); + if (jsonFiles == null || jsonFiles.length == 0) { + log.info("[{}]文件夹中没有JSON文件: {}",taskName,folderPath); + return null; + } + File jsonFile=jsonFiles[0]; + JSONArray jsonArray =null; + try { + String content = new String(Files.readAllBytes(jsonFile.toPath()), StandardCharsets.UTF_8); + jsonArray = JSONUtil.parseArray(content); + log.info("[{}] 成功处理文件: {}",taskName,jsonFile.getAbsolutePath()); + } catch (RuntimeException | IOException e) { + throw new RuntimeException("处理文件 " + jsonFile.getName() + " 失败: " + e.getMessage(), e); + } + return jsonArray; + } +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/ClientController.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/ClientController.java new file mode 100644 index 00000000..f2a4daa3 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/ClientController.java @@ -0,0 +1,29 @@ +package com.suisung.mall.shop.sync.controller; + +import com.suisung.mall.shop.sync.service.SyncThirdDataService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.Resource; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Api(tags = "第三方数据同步") +@Controller +@RequestMapping("/shop/sync/app") +public class ClientController { + + @Autowired + private SyncThirdDataService syncThirdDataService; + + @ApiOperation(value = "下载客户端应用", notes = "获取加密密钥") + @RequestMapping(value = "/downClientJar",produces = MediaType.APPLICATION_OCTET_STREAM_VALUE, method = RequestMethod.GET) + public ResponseEntity downClientJar(@RequestParam String primaryKey) { + return syncThirdDataService.downloadToClient(primaryKey); + } +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/StoreDbConfigController.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/StoreDbConfigController.java new file mode 100644 index 00000000..e1745d30 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/StoreDbConfigController.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.shop.sync.controller; + +import com.suisung.mall.common.api.CommonResult; +import com.suisung.mall.common.modules.sync.StoreDbConfig; +import com.suisung.mall.common.modules.sync.SyncApp; +import com.suisung.mall.common.modules.sync.SyncFileLog; +import com.suisung.mall.shop.sync.service.StoreDbConfigService; +import com.suisung.mall.shop.sync.service.SyncFileLogService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + *

+ * 页面导航表 前端控制器 + *

+ * + * @author lyj + * @since 2025-05-16 + */ +@Api(tags = "页面导航表-店铺数据库连接配置") +@RestController +@RequestMapping("/admin/shop/shop-sync-storeDbConfig") +public class StoreDbConfigController { + + @Autowired + private StoreDbConfigService storeDbConfigService; + + /** + * 分页列表查询 + * @param storeDbConfig + * @param pageNum + * @param pageSize + * @return + */ + @ApiOperation(value = "列表查询-分页列表查询店铺数据库连接配置", notes = "列表查询-分页列表查询店铺数据库连接配置") + @RequestMapping(value = "/list", method = RequestMethod.GET) + public CommonResult list(@RequestBody StoreDbConfig storeDbConfig , + @RequestParam(name = "pageNum", defaultValue = "1") Integer pageNum, + @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) { + return storeDbConfigService.findStoreDbConfigPageList(storeDbConfig, pageNum, pageSize); + } + + /** + * 单笔获取 + * @param storeDbConfig + * @return + */ + @ApiOperation(value = "单笔获取-分页列表查询店铺数据库连接配置", notes = "单笔获取-分页列表查询店铺数据库连接配置") + @RequestMapping(value = "/getStoreDbConfig", method = RequestMethod.GET) + public CommonResult getStoreDbConfig(@RequestBody StoreDbConfig storeDbConfig) { + return CommonResult.success(storeDbConfigService.getStroeConfig(storeDbConfig)); + } + + /** + * 新增 + * @param storeDbConfig + * @return + */ + @ApiOperation(value = "新增-店铺数据库连接配置", notes = "新增-店铺数据库连接配置") + @RequestMapping(value = "/saveStoreDbConfig", method = RequestMethod.POST) + public CommonResult saveStoreDbConfig(@RequestBody StoreDbConfig storeDbConfig) { + return storeDbConfigService.addStoreDbConfig(storeDbConfig); + } + + /** + * 更新 + * @param storeDbConfig + * @return + */ + @ApiOperation(value = "更新-店铺数据库连接配置", notes = "更新-店铺数据库连接配置") + @RequestMapping(value = "/udpateStoreDbConfig", method = RequestMethod.PUT) + public CommonResult udpateStoreDbConfig(@RequestBody StoreDbConfig storeDbConfig) { + return storeDbConfigService.updateStoreDbConfig(storeDbConfig); + } + + /** + * 删除 + * @param storeDbConfig + * @return + */ + @ApiOperation(value = "删除-店铺数据库连接配置", notes = "删除-店铺数据库连接配置") + @RequestMapping(value = "/delStoreDbConfig", method = RequestMethod.PUT) + public CommonResult delStoreDbConfig(@RequestBody StoreDbConfig storeDbConfig) { + return storeDbConfigService.delStoreDbConfig(storeDbConfig); + } + + /** + * 生产密钥,用来放到app/bin目录,替换原来的密钥 + * @param syncApp + * @return + */ + @ApiOperation(value = "客户端primaryKey.txt生产", notes = "密钥生成器") + @RequestMapping(value = "/getPrimaryKey", method = RequestMethod.PUT) + public String getPrimaryKey(@RequestBody SyncApp syncApp) { + return storeDbConfigService.getPrimaryKey(syncApp); + } +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/SyncThirdDataController.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/SyncThirdDataController.java index f0c71612..4a7eab46 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/SyncThirdDataController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/controller/SyncThirdDataController.java @@ -10,22 +10,30 @@ package com.suisung.mall.shop.sync.controller; import cn.hutool.json.JSONArray; import com.suisung.mall.common.api.CommonResult; +import com.suisung.mall.common.modules.sync.StoreDbConfig; import com.suisung.mall.common.pojo.req.SyncThirdMemberReq; import com.suisung.mall.common.pojo.res.ThirdApiRes; +import com.suisung.mall.shop.sync.service.SyncAppService; import com.suisung.mall.shop.sync.service.SyncThirdDataService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.List; +import java.util.Map; @Api(tags = "第三方数据同步") @RestController @RequestMapping("/shop/sync/third") public class SyncThirdDataController { + @Resource + private SyncAppService syncAppService; + @Resource private SyncThirdDataService syncThirdDataService; @@ -82,4 +90,59 @@ public class SyncThirdDataController { return syncThirdDataService.syncManual(storeId, syncType); } + + @ApiOperation(value = "思迅文件上传", notes = "死讯软件客户端文件上传") + @RequestMapping(value = "/uploudSxData", method = RequestMethod.POST) + public ThirdApiRes uploudSxData(@RequestParam("file") MultipartFile file, + @RequestParam String appKey, + @RequestParam String sign, + @RequestParam String page, + @RequestParam String syncType) { + return syncThirdDataService.fileUpload(appKey,sign,page,syncType,file); + } + + @ApiOperation(value = "读取数据", notes = "当思迅发送完成数据后,调用该地址读取本地文件") + @RequestMapping(value = "/readSxData", method = RequestMethod.POST) + public ThirdApiRes readSxData(@RequestBody List folders, + @RequestParam String appKey, + @RequestParam String sign, + @RequestParam String syncType) { + new Thread(new Runnable() { + @Override + public void run() { + syncThirdDataService.SyncReadSxFileData(appKey,sign,syncType,folders); + } + }).start(); + return new ThirdApiRes().success("服务器已处理文件"); + } + + @ApiOperation(value = "读取数据", notes = "手动补偿商品同步失败的数据使用") + @RequestMapping(value = "/retryReadSxData", method = RequestMethod.POST) + public ThirdApiRes RetryReadSxData(@RequestParam List folders, + @RequestParam String appKey, + @RequestParam String sign, + @RequestParam String syncType) { + return readSxData(folders,appKey,sign,syncType); + } + + @ApiOperation(value = "获取加密密钥", notes = "获取加密密钥") + @RequestMapping(value = "/getAppSign", method = RequestMethod.POST) + public Map getAppSign(@RequestParam String primaryKey) { + return syncAppService.getAppSign(primaryKey); + } + + /** + * 客户端查询配置 + * @param appKey + * @param sign + * @return + */ + @ApiOperation(value = "客户端查询配置", notes = "客户端查询配置") + @RequestMapping(value = "/getStoreDbConfig", method = RequestMethod.POST) + public ThirdApiRes getStoreDbConfig(@RequestParam String appKey, + @RequestParam String sign){ + return syncThirdDataService.getStoreDbConfig(appKey,sign); + } + + } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/keymanage/RedisKey.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/keymanage/RedisKey.java new file mode 100644 index 00000000..09483b2a --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/keymanage/RedisKey.java @@ -0,0 +1,6 @@ +package com.suisung.mall.shop.sync.keymanage; + +public class RedisKey { + + public static final String SXCLIENTKEYVERSION="sxclientKey:version"; +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/mapper/StoreDbConfigMapper.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/mapper/StoreDbConfigMapper.java new file mode 100644 index 00000000..e836db91 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/mapper/StoreDbConfigMapper.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.shop.sync.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.suisung.mall.common.modules.sync.StoreDbConfig; +import com.suisung.mall.common.modules.sync.SyncFileLog; +import org.springframework.stereotype.Repository; + +@Repository +public interface StoreDbConfigMapper extends BaseMapper { +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/mapper/SyncFileLogMapper.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/mapper/SyncFileLogMapper.java new file mode 100644 index 00000000..f6d38220 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/mapper/SyncFileLogMapper.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.shop.sync.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.suisung.mall.common.modules.sync.SyncFileLog; +import org.springframework.stereotype.Repository; + +@Repository +public interface SyncFileLogMapper extends BaseMapper { +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/pool/ThreadPoolManager.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/pool/ThreadPoolManager.java new file mode 100644 index 00000000..762ed133 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/pool/ThreadPoolManager.java @@ -0,0 +1,414 @@ +package com.suisung.mall.shop.sync.pool; + +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONUtil; + +import java.io.File; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class ThreadPoolManager { + private final ThreadPoolExecutor executor; + private final String successFile; + private final String failureFile; + private final AtomicInteger successCount = new AtomicInteger(0); + private final AtomicInteger failureCount = new AtomicInteger(0); + private final BlockingQueue pendingTasks = new LinkedBlockingQueue<>(); + private volatile boolean isShutdown = false; + + + + public ThreadPoolManager(int corePoolSize, int maxPoolSize, String successFile, String failureFile) { + this.successFile = successFile; + this.failureFile = failureFile; + + this.executor = new ThreadPoolExecutor( + corePoolSize, + maxPoolSize, + 60L, + TimeUnit.SECONDS, + new LinkedBlockingQueue<>(100), + new CustomRejectedExecutionHandler()); + + this.executor.allowCoreThreadTimeOut(true); + startMonitorThread(); + startPendingTaskConsumer(); + } + + public ThreadPoolManager(String successFile, String failureFile) { + this(Runtime.getRuntime().availableProcessors(), + Runtime.getRuntime().availableProcessors() * 2, + successFile, failureFile); + } + // 自定义线程工厂 + private static class CustomThreadFactory implements ThreadFactory { + private final AtomicInteger counter = new AtomicInteger(0); + + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r, "pool-thread-" + counter.incrementAndGet()); + thread.setPriority(Thread.NORM_PRIORITY); + thread.setDaemon(false); + return thread; + } + } + + + // 自定义拒绝策略 + private class CustomRejectedExecutionHandler implements RejectedExecutionHandler { + @Override + public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { + if (!isShutdown) { + // 1. 尝试直接执行(调用者线程) + try { + System.out.println("线程池饱和,由调用线程直接执行"); + r.run(); + } catch (Exception e) { + System.err.println("调用者执行任务失败: " + e.getMessage()); + } + + // 2. 记录指标 + failureCount.incrementAndGet(); + } else { + System.err.println("线程池已关闭,拒绝新任务"); + } + } + } + + + // 消费待处理任务的线程 + private void startPendingTaskConsumer() { + Thread consumerThread = new Thread(() -> { + while (!isShutdown || !pendingTasks.isEmpty()) { + try { + Runnable task = pendingTasks.take(); + // 尝试重新提交任务 + executor.submit(task); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } catch (Exception e) { + System.err.println("处理待处理任务时出错: " + e.getMessage()); + } + } + }); + consumerThread.setDaemon(true); + consumerThread.start(); + } + + + /** + * 提交文件夹处理任务(带重试机制) + */ + public void submitFolderTask(String taskName, String folderPath, + JsonProcessCallback callback, int maxRetries) { + Runnable task = () -> { + int retryCount = 0; + boolean success = false; + + while (retryCount <= maxRetries && !success && !isShutdown) { + try { + processFolder(taskName, folderPath, callback); + successCount.incrementAndGet(); + logSuccess(taskName, folderPath); + success = true; + } catch (Exception e) { + retryCount++; + if (retryCount > maxRetries) { + failureCount.incrementAndGet(); + logFailure(taskName, folderPath, + "重试" + maxRetries + "次后失败: " + e.getMessage()); + } else { + System.out.printf("[%s] 任务处理失败,第%d次重试...%n", + taskName, retryCount); + try { + Thread.sleep(1000 * retryCount); // 指数退避 + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + break; + } + } + } + } + }; + + try { + executor.submit(task); + } catch (Exception e) { + System.err.println("提交任务失败: " + e.getMessage()); + } + } + + /** + * 批量提交文件夹处理任务 + * @param taskName 任务名称(用于日志记录) + * @param folderPaths 要处理的文件夹路径列表 + * @param callback 处理完成的回调接口 + * @return CountDownLatch 用于等待所有任务完成 + */ +// public void submitBatchFolderTasks(String taskName, List folderPaths, +// JsonProcessCallback callback, int maxRetries) { +// Semaphore semaphore = new Semaphore(executor.getMaximumPoolSize() * 2); +// +// for (String folderPath : folderPaths) { +// try { +// semaphore.acquire(); +// submitFolderTask(taskName, folderPath, callback, maxRetries); +// } catch (InterruptedException e) { +// Thread.currentThread().interrupt(); +// break; +// } finally { +// semaphore.release(); +// } +// } +// } + public void submitBatchFolderTasks(String taskName, List folderPaths, + JsonProcessCallback callback, int maxRetries) { + // 使用CountDownLatch跟踪批次完成 + CountDownLatch batchLatch = new CountDownLatch(folderPaths.size()); + + // 控制并发度的信号量 + Semaphore concurrencyLimiter = new Semaphore(executor.getMaximumPoolSize() * 2); + + for (String folderPath : folderPaths) { + try { + concurrencyLimiter.acquire(); // 获取许可 + + executor.execute(() -> { + try { + submitFolderTask(taskName, folderPath, callback, maxRetries); + } finally { + concurrencyLimiter.release(); + batchLatch.countDown(); + } + }); + + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + } + + try { + // 等待批次完成,带超时 + batchLatch.await(30, TimeUnit.MINUTES); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + private void processFolder(String taskName, String folderPath, JsonProcessCallback callback) throws IOException { + File folder = new File(folderPath); + // ... 参数校验 ... + + // 使用try-with-resources确保资源释放 + try (Stream paths = Files.list(folder.toPath())) { + paths.filter(path -> path.toString().endsWith(".txt")) + .parallel() // 并行处理文件 + .forEach(path -> { + try { + String content = new String(Files.readAllBytes(path), StandardCharsets.UTF_8); + JSONArray jsonArray = JSONUtil.parseArray(content); + + if (callback != null) { + callback.process(jsonArray, taskName, path.getFileName().toString()); + } + + System.out.println("[" + taskName + "] 成功处理文件: " + path); + } catch (Exception e) { + throw new RuntimeException("处理文件失败: " + path, e); + } + }); + } + } + + /** + * 记录成功处理 + */ + private synchronized void logSuccess(String taskName, String folderPath) { + Path path = Paths.get(successFile); + String record = String.format("%s | %s | %d%n", + taskName, folderPath, System.currentTimeMillis()); + try { + Files.write(path, record.getBytes(StandardCharsets.UTF_8), + StandardOpenOption.CREATE, StandardOpenOption.APPEND); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * 记录处理失败 + */ + private synchronized void logFailure(String taskName, String folderPath, String error) { + Path path = Paths.get(failureFile); + String record = String.format("%s | %s | %s | %d%n", + taskName, folderPath, error, System.currentTimeMillis()); + try { + Files.write(path, record.getBytes(StandardCharsets.UTF_8), + StandardOpenOption.CREATE, StandardOpenOption.APPEND); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * 启动监控线程 + */ + private void startMonitorThread() { + Thread monitorThread = new Thread(() -> { + while (!executor.isShutdown()) { + try { + printPoolStatus(); + checkForDeadlock(); + adjustPoolSize(); + TimeUnit.SECONDS.sleep(5); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + } + }); + monitorThread.setName("pool-monitor-thread"); + monitorThread.setDaemon(true); + monitorThread.start(); + } + private void printPoolStatus() { + System.out.println("\n=== 线程池状态 ==="); + System.out.println("活跃线程: " + executor.getActiveCount()); + System.out.println("核心线程: " + executor.getCorePoolSize()); + System.out.println("最大线程: " + executor.getMaximumPoolSize()); + System.out.println("队列大小: " + executor.getQueue().size()); + System.out.println("待处理队列: " + pendingTasks.size()); + System.out.println("完成任务: " + successCount.get()); + System.out.println("失败任务: " + failureCount.get()); + System.out.println("================\n"); + } + + /** + * 检测线程池死锁情况 + */ + private void checkForDeadlock() { + // 1. 获取线程池工作线程 + ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); + long[] threadIds = threadMXBean.getAllThreadIds(); + + // 2. 查找死锁线程 + long[] deadlockedThreadIds = threadMXBean.findDeadlockedThreads(); + if (deadlockedThreadIds != null && deadlockedThreadIds.length > 0) { + System.err.println("⚠️ 检测到死锁线程:"); + for (long threadId : deadlockedThreadIds) { + ThreadInfo threadInfo = threadMXBean.getThreadInfo(threadId); + System.err.println("死锁线程: " + threadInfo.getThreadName()); + System.err.println("阻塞状态: " + threadInfo.getLockName() + + " 被 " + threadInfo.getLockOwnerName() + " 持有"); + + // 打印堆栈跟踪 + System.err.println("堆栈跟踪:"); + for (StackTraceElement ste : threadInfo.getStackTrace()) { + System.err.println("\t" + ste); + } + } + + // 3. 尝试恢复 - 中断死锁线程 + if (executor instanceof ThreadPoolExecutor) { + ThreadPoolExecutor tpe = (ThreadPoolExecutor) executor; + BlockingQueue queue = tpe.getQueue(); + + System.err.println("尝试中断死锁线程并清空队列(" + queue.size() + "个任务)"); + tpe.purge(); // 清除队列中所有任务 + + // 中断所有工作线程 + for (long threadId : deadlockedThreadIds) { + ThreadInfo threadInfo = threadMXBean.getThreadInfo(threadId); + if (threadInfo != null) { + for (Thread thread : getAllThreads()) { + if (thread.getId() == threadId) { + thread.interrupt(); + System.err.println("已中断线程: " + thread.getName()); + } + } + } + } + } + } + } + + /** + * 获取JVM中所有活动线程 + */ + private List getAllThreads() { + ThreadGroup rootGroup = Thread.currentThread().getThreadGroup(); + while (rootGroup.getParent() != null) { + rootGroup = rootGroup.getParent(); + } + + Thread[] threads = new Thread[rootGroup.activeCount()]; + while (rootGroup.enumerate(threads, true) == threads.length) { + threads = new Thread[threads.length * 2]; + } + + return Arrays.stream(threads) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + private void adjustPoolSize() { + // 动态调整线程池大小 + if (executor.getQueue().size() > 50 && + executor.getMaximumPoolSize() < 100) { + executor.setMaximumPoolSize(executor.getMaximumPoolSize() + 5); + } + } + /** + * 关闭线程池(优雅关闭) + */ + public void shutdown() { + isShutdown = true; + executor.shutdown(); + try { + if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { + executor.shutdownNow(); + } + } catch (InterruptedException e) { + executor.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + + /** + * 立即关闭线程池 + */ + public void shutdownNow() { + executor.shutdownNow(); + } + + /** + * 等待所有任务完成 + */ + public void awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + executor.awaitTermination(timeout, unit); + } + + /** + * JSON处理回调接口 + */ + @FunctionalInterface + public interface JsonProcessCallback { + void process(JSONArray jsonArray, String taskName, String fileName); + } +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/StoreDbConfigService.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/StoreDbConfigService.java new file mode 100644 index 00000000..39e84951 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/StoreDbConfigService.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.shop.sync.service; + + +import com.baomidou.mybatisplus.extension.service.IService; +import com.suisung.mall.common.api.CommonResult; +import com.suisung.mall.common.modules.sync.StoreDbConfig; +import com.suisung.mall.common.modules.sync.SyncApp; +import com.suisung.mall.common.pojo.res.ThirdApiRes; + +import java.util.List; + +public interface StoreDbConfigService extends IService { + + CommonResult findStoreDbConfigPageList(StoreDbConfig storeDbConfig, Integer pageNum, Integer pageSize); + + + StoreDbConfig getStroeConfig(StoreDbConfig storeDbConfig); + + CommonResult addStoreDbConfig(StoreDbConfig storeDbConfig); + + CommonResult updateStoreDbConfig(StoreDbConfig storeDbConfig); + + CommonResult delStoreDbConfig(StoreDbConfig storeDbConfig); + String getPrimaryKey(SyncApp syncApp); +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncAppService.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncAppService.java index 3351450f..e9fa1cc2 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncAppService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncAppService.java @@ -12,6 +12,8 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; import com.suisung.mall.common.modules.sync.SyncApp; +import java.util.Map; + public interface SyncAppService extends IService { /** @@ -83,4 +85,13 @@ public interface SyncAppService extends IService { * @return */ SyncApp checkAppSign(String appKey, String sign, String postData); + + /** + * 获取加密的appKey,sign,streId + * @param primaryKey + * @return + */ + Map getAppSign(String primaryKey); + + boolean checkPrimaryKey(String primaryKey); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncFileLogService.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncFileLogService.java new file mode 100644 index 00000000..07e3eb10 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncFileLogService.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.shop.sync.service; + + +import com.suisung.mall.common.api.CommonResult; +import com.suisung.mall.common.modules.sync.StoreDbConfig; +import com.suisung.mall.common.modules.sync.SyncFileLog; +import com.suisung.mall.common.pojo.res.ThirdApiRes; +import com.suisung.mall.core.web.service.IBaseService; + +import java.util.List; + +public interface SyncFileLogService extends IBaseService { + + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncThirdDataService.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncThirdDataService.java index e0bfcb1d..bb641cf4 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncThirdDataService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/SyncThirdDataService.java @@ -10,9 +10,14 @@ package com.suisung.mall.shop.sync.service; import cn.hutool.json.JSONArray; import com.suisung.mall.common.api.CommonResult; +import com.suisung.mall.common.modules.sync.StoreDbConfig; import com.suisung.mall.common.pojo.req.SyncThirdMemberReq; import com.suisung.mall.common.pojo.res.ThirdApiRes; +import org.springframework.core.io.Resource; +import org.springframework.http.ResponseEntity; +import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; import java.util.List; public interface SyncThirdDataService { @@ -54,4 +59,28 @@ public interface SyncThirdDataService { * @return */ CommonResult syncManual(String storeId, Integer syncType); + + /** + * + * @param appKey + * @param sign + * @param multipartFile + * @return + */ + ThirdApiRes fileUpload(String appKey, String sign,String path,String syncType, MultipartFile multipartFile); + + + /** + * + * @param appKey + * @param sign + * @param folders + * @return + */ + void SyncReadSxFileData(String appKey, String sign,String syncType, List folders); + + + ResponseEntity downloadToClient(String primaryKey); + + ThirdApiRes getStoreDbConfig(String appKey,String sign); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/StoreDbConfigServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/StoreDbConfigServiceImpl.java new file mode 100644 index 00000000..7e895f9d --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/StoreDbConfigServiceImpl.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.shop.sync.service.impl; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.suisung.mall.common.api.CommonResult; +import com.suisung.mall.common.enums.DicEnum; +import com.suisung.mall.common.modules.store.ShopStoreBase; +import com.suisung.mall.common.modules.sync.StoreDbConfig; +import com.suisung.mall.common.modules.sync.SyncApp; +import com.suisung.mall.common.pojo.res.ThirdApiRes; +import com.suisung.mall.common.utils.ContextUtil; +import com.suisung.mall.common.utils.StringUtils; +import com.suisung.mall.core.web.service.impl.BaseServiceImpl; +import com.suisung.mall.shop.store.service.ShopStoreBaseService; +import com.suisung.mall.shop.sync.Utils.CryptoUtils; +import com.suisung.mall.shop.sync.mapper.StoreDbConfigMapper; +import com.suisung.mall.shop.sync.service.StoreDbConfigService; + +import com.suisung.mall.shop.sync.service.SyncAppService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.swing.*; +import java.util.Objects; + +import static com.suisung.mall.common.utils.ContextUtil.getStoreId; + + +@Service +public class StoreDbConfigServiceImpl extends BaseServiceImpl implements StoreDbConfigService { + + @Autowired + private ShopStoreBaseService shopStoreBaseService; + @Autowired + private SyncAppService syncAppService; + + @Override + public CommonResult findStoreDbConfigPageList(StoreDbConfig storeDbConfig,Integer pageNum,Integer pageSize) { + checked(); + String storeId= getStoreId(storeDbConfig.getStoreId()); + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("store_id", storeId); + queryWrapper.eq("has_internet", storeDbConfig.getHasInternet()); + queryWrapper.eq("has_start", storeDbConfig.getHasInternet()); + return CommonResult.success(this.lists(queryWrapper,pageNum,pageSize)); + } + + @Override + public StoreDbConfig getStroeConfig(StoreDbConfig storeDbConfig) { + checked(); + if(ObjectUtil.isEmpty(storeDbConfig)){ + return null; + } + String storeId= getStoreId(storeDbConfig.getStoreId()); + storeDbConfig.setStoreId(storeId); + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("store_id", storeId); + if(null!=storeDbConfig.getId()){ + queryWrapper.eq("id", storeDbConfig.getId()); + } + return this.getOne(queryWrapper); + } + + @Override + public CommonResult addStoreDbConfig(StoreDbConfig storeDbConfig) { + checked(); + if(ObjectUtil.isEmpty(storeDbConfig)){ + return CommonResult.failed("店铺数据库连接配置缺少必要参数"); + } + String storeId= getStoreId(storeDbConfig.getStoreId());//保证不能跨数据操作 + if(StringUtils.isEmpty(storeId)){ + return CommonResult.failed("店铺数据库连接配置新增失败"); + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("store_id", storeId); + if(null!=storeDbConfig.getId()){ + queryWrapper.eq("id", storeDbConfig.getId()); + } + queryWrapper.eq("has_start",DicEnum.YESORNO_1.getCode()); + if(null!=this.getOne(queryWrapper)){ + return CommonResult.failed("店铺已存在有效配置,不能重复添加"); + } + ShopStoreBase shopStoreBase=shopStoreBaseService.get(Convert.toInt(storeId)); + if(shopStoreBase==null){ + return CommonResult.failed("店铺数据库连接配置新增失败:失败原因是店铺不存在"); + } + storeDbConfig.setStoreId(storeId); + if(!this.save(storeDbConfig)){ + return CommonResult.failed("店铺数据库连接配置新增失败"); + } + return CommonResult.success(); + } + + @Override + public CommonResult updateStoreDbConfig(StoreDbConfig storeDbConfig) { + checked(); + if(ObjectUtil.isEmpty(storeDbConfig)){ + return CommonResult.failed("店铺数据库连接配置缺少必要参数"); + } + if(ObjectUtil.isEmpty(storeDbConfig.getId())){ + return CommonResult.failed("店铺数据库连接配置缺少必要参数"); + } + String storeId= getStoreId(storeDbConfig.getStoreId());//保证不能跨数据操作 + ShopStoreBase shopStoreBase=shopStoreBaseService.get(Convert.toInt(storeId)); + if(shopStoreBase==null){ + return CommonResult.failed("店铺数据库连接配置新增失败:失败原因是店铺不存在"); + } + storeDbConfig.setStoreId(storeId); + if(!this.updateById(storeDbConfig)){ + return CommonResult.failed("店铺数据库连接配置更新失败"); + } + return CommonResult.success(); + } + + @Override + public CommonResult delStoreDbConfig(StoreDbConfig storeDbConfig) { + checked(); + if(ObjectUtil.isEmpty(storeDbConfig)){ + return CommonResult.failed("店铺数据库连接配置缺少必要参数"); + } + if(ObjectUtil.isEmpty(storeDbConfig.getId())){ + return CommonResult.failed("店铺数据库连接配置缺少必要参数"); + } + String storeId= getStoreId(storeDbConfig.getStoreId());//保证不能跨数据操作 + ShopStoreBase shopStoreBase=shopStoreBaseService.get(Convert.toInt(storeId)); + if(shopStoreBase==null){ + return CommonResult.failed("店铺数据库连接配置删除失败:失败原因是店铺不存在"); + } + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("store_id", storeId); + queryWrapper.eq("id", storeDbConfig.getId()); + StoreDbConfig delDb= getOne(queryWrapper); + if(delDb==null){ + return CommonResult.failed("店铺数据库连接配置删除失败"); + } + if(!this.removeById(delDb.getId())){ + return CommonResult.failed("店铺数据库连接配置删除失败"); + } + return CommonResult.success(); + } + + + @Override + public String getPrimaryKey(SyncApp syncApp) { + checked(); + if(ObjectUtil.isEmpty(syncApp)){ + throw new RuntimeException("对象不能为空"); + } + if(ObjectUtil.isEmpty(syncApp.getStore_id())){ + throw new RuntimeException("商店编号不能为空"); + } + String storeId= getStoreId(syncApp.getStore_id());//保证不能跨数据操作 + ShopStoreBase shopStoreBase=shopStoreBaseService.get(Convert.toInt(storeId)); + if(shopStoreBase==null){ + throw new RuntimeException("商品不存在"); + } + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("store_id", storeId); + SyncApp result= syncAppService.getOne(queryWrapper); + String primaryKey=""; + try { + primaryKey= CryptoUtils.packAndEncrypt(result.getApp_key(),result.getApp_secret(),result.getStore_id()); + } catch (Exception e) { + throw new RuntimeException(e); + } + return primaryKey; + } + + private void checked(){ + int roleId= Objects.requireNonNull(ContextUtil.getCurrentUser()).getRole_id(); + if(roleId!=9){ + throw new RuntimeException("仅为平台操作"); + } + } + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncAppServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncAppServiceImpl.java index e4c5bb91..fe01fa9d 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncAppServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncAppServiceImpl.java @@ -8,6 +8,7 @@ package com.suisung.mall.shop.sync.service.impl; +import cn.hutool.core.convert.Convert; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; @@ -16,19 +17,25 @@ import com.suisung.mall.common.modules.sync.SyncApp; import com.suisung.mall.common.utils.CommonUtil; import com.suisung.mall.common.utils.StringUtils; import com.suisung.mall.core.web.service.impl.BaseServiceImpl; +import com.suisung.mall.shop.sync.Utils.CryptoUtils; import com.suisung.mall.shop.sync.mapper.SyncAppMapper; import com.suisung.mall.shop.sync.service.SyncAppService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; +import java.util.HashMap; +import java.util.Map; import java.util.function.Consumer; @Service +@lombok.extern.slf4j.Slf4j public class SyncAppServiceImpl extends BaseServiceImpl implements SyncAppService { @Autowired private SyncAppMapper syncAppMapper; + /** * 根据 appKey 获取一条记录 * @@ -181,4 +188,50 @@ public class SyncAppServiceImpl extends BaseServiceImpl return result; } + + @Override + public Map getAppSign(String primaryKey) { + Map resultMap=new HashMap<>(); + try { + primaryKey=CryptoUtils.dealBlankStr(primaryKey); + Map map=CryptoUtils.decryptAndUnpack(primaryKey); +// String appKey=map.get("appKey"); +// String sign=map.get("sign"); + String storeId=map.get("storeId"); + QueryWrapper queryWrapper = new QueryWrapper<>(); + SyncApp syncApp= getOne(queryWrapper.select("store_id","app_secret","app_key").eq("store_id", storeId)); + if(syncApp==null){ + return null; + } + resultMap.put("resultCode", HttpStatus.OK); + resultMap.put("primaryKey",CryptoUtils.packAndEncrypt(syncApp.getApp_key(),syncApp.getApp_secret(),syncApp.getStore_id())); + } catch (Exception e) { + log.info("解析失败:"+e.getMessage()); + resultMap.put("resultCode",500); + resultMap.put("msg:","解析失败:"+e.getMessage()); + } + return resultMap; + } + + @Override + public boolean checkPrimaryKey(String primaryKey) { + try { + primaryKey=CryptoUtils.dealBlankStr(primaryKey); + Map map=CryptoUtils.decryptAndUnpack(primaryKey); + String appKey=map.get("appKey"); + String sign=map.get("sign"); + String storeId=map.get("storeId"); + log.info("店铺:{}校验",storeId); + QueryWrapper queryWrapper = new QueryWrapper<>(); + SyncApp syncApp= getOne(queryWrapper.select("store_id","app_secret","app_key"). + eq("store_id", storeId). + eq("app_key", appKey).eq("app_secret", sign)); + if(syncApp==null){ + return false; + } + } catch (Exception e) { + return false; + } + return true; + } } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncBaseThirdSxAbstract.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncBaseThirdSxAbstract.java new file mode 100644 index 00000000..4a32bc65 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncBaseThirdSxAbstract.java @@ -0,0 +1,695 @@ +package com.suisung.mall.shop.sync.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import com.alibaba.excel.util.DateUtils; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.suisung.mall.common.api.StateCode; +import com.suisung.mall.common.constant.CommonConstant; +import com.suisung.mall.common.exception.ApiException; +import com.suisung.mall.common.feignService.AccountService; +import com.suisung.mall.common.feignService.PayService; +import com.suisung.mall.common.modules.account.AccountUserBase; +import com.suisung.mall.common.modules.account.AccountUserInfo; +import com.suisung.mall.common.modules.base.ShopBaseProductBrand; +import com.suisung.mall.common.modules.base.ShopBaseProductCategory; +import com.suisung.mall.common.modules.base.ShopBaseProductType; +import com.suisung.mall.common.modules.number.ShopNumberSeq; +import com.suisung.mall.common.modules.pay.PayUserResource; +import com.suisung.mall.common.modules.product.*; +import com.suisung.mall.common.modules.sixun.SxSyncGoods; +import com.suisung.mall.common.modules.sixun.SxSyncVip; +import com.suisung.mall.common.modules.store.ShopStoreBase; +import com.suisung.mall.common.pojo.req.SyncThirdMemberReq; +import com.suisung.mall.common.utils.DateTimeUtils; +import com.suisung.mall.common.utils.I18nUtil; +import com.suisung.mall.common.utils.StringUtils; +import com.suisung.mall.shop.base.service.ShopBaseProductBrandService; +import com.suisung.mall.shop.base.service.ShopBaseProductCategoryService; +import com.suisung.mall.shop.base.service.ShopBaseProductTypeService; +import com.suisung.mall.shop.number.service.impl.ShopNumberSeqServiceImpl; +import com.suisung.mall.shop.product.service.ShopProductBaseService; +import com.suisung.mall.shop.product.service.impl.ShopProductBaseServiceImpl; +import com.suisung.mall.shop.product.service.impl.ShopProductItemServiceImpl; +import com.suisung.mall.shop.sixun.dto.SxGoosModel; +import com.suisung.mall.shop.store.service.ShopStoreBaseService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.util.Pair; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.text.ParseException; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; + +@Service +public abstract class SyncBaseThirdSxAbstract{ + private final Logger logger = LoggerFactory.getLogger(SyncBaseThirdSxAbstract.class); + @Autowired + private ShopBaseProductBrandService productBrandService; + @Autowired + private ShopBaseProductCategoryService productCategoryService; + @Autowired + private ShopBaseProductTypeService productTypeService; + @Autowired + private ShopProductBaseService shopProductBaseService; + @Autowired + private ShopStoreBaseService shopStoreBaseService; + @Autowired + private AccountService accountService; + @Autowired + private PayService payService; + @Autowired + private ShopNumberSeqServiceImpl shopNumberSeqServiceImpl; + @Autowired + private ShopProductBaseServiceImpl shopProductBaseServiceImpl; + @Autowired + private ShopProductItemServiceImpl shopProductItemServiceImpl; + + /** + * 对商品分类进行保存 + * @param list + * @param categoryListJSON + * @param storeId + * @return + */ + public int baseSaveOrUpdateShopBaseProductCategoryBatch(List list ,JSONArray categoryListJSON, + String storeId){ + int count = 0; + for (int i = 0; i < list.size(); i++) { + list.get(i).setStore_id(storeId); // app 记录传进来 + list.get(i).setData_source(2); // 思迅数据来源 + list.get(i).setCategory_is_enable(1); + + JSONObject o = (JSONObject) categoryListJSON.get(i); + if (o != null) { + // 重要:分类类型处理(强调共性) + Integer typeId = 1001; + if (StrUtil.isNotBlank(o.getStr("product_type"))) { + ShopBaseProductType productType = productTypeService.getProductTypeByName(o.getStr("product_type")); + if (productType != null) { + list.get(i).setType_id(productType.getType_id()); + } else { + // 新增一个类型 + ShopBaseProductType newProductType = new ShopBaseProductType(); + newProductType.setType_name(o.getStr("product_type")); + newProductType.setType_buildin(0); + + if (productTypeService.save(newProductType)) { + typeId = newProductType.getType_id(); + } + } + + list.get(i).setType_id(typeId); + } + + + // 处理(第一级)父类字段 产品分类 + Integer firstParentId = 0; + if (StrUtil.isNotBlank(o.getStr("first_category_name"))) { + // TODO storeId 不判断一下吗? + ShopBaseProductCategory cate = productCategoryService.getCategoryByName(o.getStr("first_category_name")); + if (cate != null) { + list.get(i).setParent_id(cate.getCategory_id()); + } else { + // 新增一个(第一级)父类 + ShopBaseProductCategory firstCate = new ShopBaseProductCategory(); + firstCate.setCategory_name(o.getStr("first_category_name")); + firstCate.setParent_id(0); + firstCate.setStore_id(storeId); + firstCate.setType_id(typeId); + firstCate.setData_source(2); + if (productCategoryService.saveOrUpdate(firstCate)) { + // 当前子分类的父类id + firstParentId = firstCate.getId(); + list.get(i).setParent_id(firstParentId); + } + } + + list.get(i).setParent_id(firstParentId); + } + + // 处理(第二级)父类字段 产品分类 + if (StrUtil.isNotBlank(o.getStr("second_category_name"))) { + // TODO storeId 不判断一下吗? + ShopBaseProductCategory cate = productCategoryService.getCategoryByName(o.getStr("second_category_name")); + if (cate != null) { + list.get(i).setParent_id(cate.getCategory_id()); + } else { + // 新增一个(第二级)父类 + ShopBaseProductCategory secondCate = new ShopBaseProductCategory(); + secondCate.setCategory_name(o.getStr("second_category_name")); + secondCate.setParent_id(firstParentId); + secondCate.setStore_id(storeId); + secondCate.setType_id(typeId); + secondCate.setData_source(2); + if (productCategoryService.saveOrUpdate(secondCate)) { + // 当前子分类的第二级父类id + list.get(i).setParent_id(secondCate.getId()); + } + } + } + + } + + ShopBaseProductCategory productCategoryTemp = productCategoryService.getCategoryByName(list.get(i).getParent_id(), list.get(i).getCategory_name(), list.get(i).getStore_id()); + if (productCategoryTemp != null) { + // 更改记录 + if (!productCategoryTemp.getCategory_image().equals(list.get(i).getCategory_image()) + || !productCategoryTemp.getType_id().equals(list.get(i).getType_id()) + || !productCategoryTemp.getCategory_virtual_enable().equals(list.get(i).getCategory_virtual_enable()) + || !productCategoryTemp.getCategory_commission_rate().equals(list.get(i).getCategory_commission_rate()) + || !productCategoryTemp.getCategory_show_type().equals(list.get(i).getCategory_show_type())) { + list.get(i).setCategory_id(productCategoryTemp.getCategory_id()); + } else { + continue; + } + } + + if (productCategoryService.saveOrUpdate(list.get(i))) { + count++; + } + } + + return count; + } + + /** + * 保存品牌 + * @param goodBrandList + * @param storeId + * @param brandListJSON + * @return + */ + public int baseSaveOrUpdateShopBaseProductBrandBatch(List goodBrandList,String storeId,JSONArray brandListJSON){ + int count = 0; + for (int i = 0; i < goodBrandList.size(); i++) { + goodBrandList.get(i).setStore_id(Integer.valueOf(storeId)); // app 记录传进来 + JSONObject o = (JSONObject) brandListJSON.get(i); + if (o != null && StrUtil.isNotBlank(o.getStr("category"))) { + // category 一般是父分类名 + ShopBaseProductCategory cate = productCategoryService.getCategoryByName(o.getStr("category")); + if (cate != null) { + goodBrandList.get(i).setCategory_id(cate.getCategory_id()); + } else { + // TODO 如果没有分类,是否新建? + } + } + + count += productBrandService.saveOrUpdateBrand2(goodBrandList.get(i)); + } + + return count; + } + + /** + * 保存商品 + * @param goodsListJSON + * @param storeId + * @return + */ + public int baseSaveOrUpdateGoods(JSONArray goodsListJSON,String storeId){ + int count = 0; + Map categoryMap= productCategoryService.getCategoryListByStoreId(storeId);//热数据加载 + for (JSONObject jsonObj : goodsListJSON.jsonIter()) { + Date currentDate = new Date(); + String cateGoryId=""; + if(null!=categoryMap.get(jsonObj.get("first_category_name"))){ + cateGoryId=categoryMap.get(jsonObj.get("first_category_name")).toString(); + } + Integer categoryId = Convert.toInt(cateGoryId, 0); + Integer storeIdInt = Convert.toInt(storeId); + ShopStoreBase store_row = shopStoreBaseService.get(storeId); + if (store_row == null) { + throw new ApiException(I18nUtil._("无法获取店铺信息!")); + } + + ShopProductBase shopProductBase = new ShopProductBase(); + shopProductBase.setProduct_sale_time(Convert.toDate(DateUtil.current() + 600)); //10分钟 + shopProductBase.setStore_id(storeIdInt); + + shopProductBase.setProduct_number((String) jsonObj.get("product_number")); + shopProductBase.setProduct_name((String) jsonObj.get("product_name")); + shopProductBase.setStore_name(store_row.getStore_name()); + shopProductBase.setProduct_tips(""); + shopProductBase.setProduct_video(""); + shopProductBase.setTransport_type_id(StateCode.DELIVERY_TYPE_SAME_CITY); + shopProductBase.setProduct_state_id(StateCode.PRODUCT_STATE_NORMAL); + shopProductBase.setProduct_inventory_lock(1002); //库存锁定(ENUM):1001-下单锁定;1002-支付锁定; + shopProductBase.setProduct_fx_enable(0); // 供应商是否允许批发市场分销 + shopProductBase.setProduct_dist_enable(0); // 是否允许三级分销 + shopProductBase.setProduct_from(1005);// 商品来源(ENUM):1000-发布;1001-天猫;1002-淘宝;1003-阿里巴巴;1004-京东;1005-思迅; + shopProductBase.setProduct_add_time(currentDate.getTime()); + + // ShopProductIndex + ShopProductIndex shopProductIndex = new ShopProductIndex(); + shopProductIndex.setProduct_add_time(currentDate.getTime()); + shopProductIndex.setProduct_sale_time(DateUtil.current() + 600); //10分钟 + shopProductIndex.setStore_category_ids(""); // 店铺分类编号(DOT) + shopProductIndex.setProduct_tags("");// 商品标签(DOT) + shopProductIndex.setBrand_id(0); + shopProductIndex.setProduct_name(shopProductBase.getProduct_name()); // 产品名称:店铺平台先在对用表中检索后通过id检索,检索使用 + shopProductIndex.setProduct_name_index(shopProductIndex.getProduct_name()); // 名称索引关键字(DOT) + shopProductIndex.setCategory_id(categoryId); // 商品分类 + shopProductIndex.setProduct_fx_enable(0); // 供应商是否允许批发市场分销 + shopProductIndex.setProduct_dist_enable(0); // 是否允许三级分销 + shopProductIndex.setStore_id(storeIdInt); + shopProductIndex.setStore_type(store_row.getStore_type()); + shopProductIndex.setKind_id(StateCode.PRODUCT_KIND_ENTITY);// 实体商品 + shopProductIndex.setStore_is_open(store_row.getStore_is_open()); + shopProductIndex.setStore_is_selfsupport(store_row.getStore_is_selfsupport()); + shopProductIndex.setStore_longitude(store_row.getStore_longitude()); + shopProductIndex.setStore_latitude(store_row.getStore_latitude()); + + shopProductIndex.setCard_type_id(0); + shopProductIndex.setVoucher_activity_id(""); + shopProductIndex.setCoupon_type_id(0);// product_assist_data // 辅助属性值列(DOT) + shopProductIndex.setProduct_transport_id(String.valueOf(StateCode.DELIVERY_TYPE_SAME_CITY)); + + ShopBaseProductCategory category_row = productCategoryService.get(categoryId); + if (ObjectUtil.isNotEmpty(category_row)) { + Integer type_id = category_row.getType_id(); + shopProductIndex.setType_id(type_id); + } + + // shop_product_data + ShopProductData shopProductData = new ShopProductData(); + // product_id // 产品id + shopProductData.setProduct_edit_time(currentDate); + + // shop_product_detail + ShopProductDetail shopProductDetail = new ShopProductDetail(); + shopProductDetail.setProduct_detail(""); + shopProductDetail.setProduct_param(""); + shopProductDetail.setProduct_service(""); + shopProductDetail.setProduct_tags(""); + + // shop_product_info + ShopProductInfo shopProductInfo = new ShopProductInfo(); + shopProductInfo.setProduct_number((String) jsonObj.get("product_number"));// SPU商家编码:货号 + shopProductInfo.setProduct_assist(""); + shopProductInfo.setProduct_spec(""); + + shopProductInfo.setProduct_buy_limit(0); // 不限购 + //商品规格 + shopProductInfo.setSpec_ids(""); + + List shopProductItemList = new ArrayList<>(); + ShopProductItem shopProductItem = new ShopProductItem(); + shopProductItem.setStore_id(storeIdInt); + shopProductItem.setCategory_id(categoryId); + //零售价 + shopProductItem.setItem_unit_price(BigDecimal.valueOf(jsonObj.getDouble("retail_price"))); + shopProductItem.setItem_advice_price(BigDecimal.valueOf(jsonObj.getDouble("retail_price"))); + shopProductItem.setItem_market_price(BigDecimal.valueOf(jsonObj.getDouble("original_price"))); + //积分价 + shopProductItem.setItem_unit_points(new BigDecimal(jsonObj.getInt("points"))); + shopProductItem.setItem_quantity(jsonObj.getInt("stock")); // 库存 + shopProductItem.setItem_quantity_frozen(0); + shopProductItem.setItem_number(jsonObj.getStr("product_number"));// SKU商家编码 + shopProductItem.setItem_barcode(jsonObj.getStr("product_number")); // 条形码正常情况下就是货号 + shopProductItem.setItem_is_default(1); + shopProductItem.setItem_enable(1); + + shopProductItemList.add(shopProductItem); + + // 保存数据 + Pair pair = shopProductBaseService.saveProduct(shopProductBase, shopProductIndex, shopProductData, shopProductDetail, shopProductInfo, shopProductItemList, new ArrayList(), new ShopProductValidPeriod(), new ArrayList()); + if (!pair.getFirst()) { + logger.error(pair.getSecond()); + continue; + } + + count += 1; + } + return count; + } + + /** + * 保存会员 + * @param memberList + * @param storeId + * @return + */ + public int baseSaveOrUpdateMemberBatch(List memberList,String storeId){ + int count = 0; + for (SyncThirdMemberReq member : memberList) { + // account_user_base + AccountUserBase accountUserBase = new AccountUserBase(); + accountUserBase.setUser_account(StringUtils.generateUniqueCode(8)); + accountUserBase.setUser_nickname(member.getUser_nickname()); + accountUserBase.setUser_state(2);// 状态(ENUM):0-锁定;1-未激活;2-已激活; + accountUserBase.setUser_is_admin(CommonConstant.USER_TYPE_NORMAL); + accountUserBase.setStore_ids(storeId); + accountUserBase.setRights_group_id("0");// 普通用户,不是商家 + accountUserBase.setUser_type(CommonConstant.USER_TYPE_NORMAL); + + // 默认给了随机密码和盐,token + String user_key = IdUtil.simpleUUID(); + String user_salt = IdUtil.simpleUUID(); + String user_token = IdUtil.simpleUUID(); + accountUserBase.setUser_salt(user_salt); + accountUserBase.setUser_token(user_token); + accountUserBase.setUser_key(user_key); + accountUserBase.setUser_password(SecureUtil.md5(user_salt + SecureUtil.md5(IdUtil.simpleUUID()))); + + // 判断店铺是不是存在该昵称的会员了? + Boolean exists = accountService.existByNickname(member.getUser_nickname(), storeId); + if (exists) { + continue; + } + + AccountUserBase accountUserBase2 = accountService.saveOrUpdateUserBase2(accountUserBase); + + // 新增用户记录后,返回 userId ? + Integer userId = accountUserBase2.getUser_id(); + if (userId == null || userId <= 0) { + continue; + } + + // account_user_info + AccountUserInfo accountUserInfo = new AccountUserInfo(); + accountUserInfo.setUser_id(userId); + accountUserInfo.setUser_type_id(0); + accountUserInfo.setUser_mobile(member.getUser_mobile()); + accountUserInfo.setUser_level_card(member.getUser_level_card()); + //user_level_id + accountUserInfo.setUser_level_id(1); // todo select id + accountUserInfo.setUser_gender(member.getUser_gender()); + accountUserInfo.setUser_mobile(member.getUser_mobile()); + accountUserInfo.setUser_intl(CommonConstant.IDD_ZH_CN); + accountUserInfo.setUser_birthday(DateTimeUtils.parseDate(member.getUser_birthday(), "yyyy-MM-dd")); + boolean success = accountService.saveOrUpdateUserInfo(accountUserInfo); + if (member.getUser_money() != null || member.getUser_points() != null) { + // pay_user_resource 用户支付资源,积分,余额 + PayUserResource payUserResource = new PayUserResource(); + payUserResource.setUser_id(userId); + payUserResource.setUser_money(member.getUser_money()); + payUserResource.setUser_money_frozen(BigDecimal.ZERO); + payUserResource.setUser_points(member.getUser_points()); + payUserResource.setUser_points_frozen(BigDecimal.ZERO); + success = payService.saveOrUpdatePayUserResource(payUserResource); + } + + if (success) { + count += 1; + } + } + return count; + } + + /** + * List 转换为 List + * @param sxSyncVipList + * @return + */ + public List ConverList(List sxSyncVipList){ + List syncThirdMemberReqList=new ArrayList<>(); + if(CollUtil.isNotEmpty(sxSyncVipList)){ + SyncThirdMemberReq syncThirdMemberReq=null; + for (SxSyncVip sxSyncVip : sxSyncVipList) { + syncThirdMemberReq = new SyncThirdMemberReq(); + syncThirdMemberReq.setUser_nickname(sxSyncVip.getVip_name()); + syncThirdMemberReq.setUser_realname(sxSyncVip.getVip_name()); + if (sxSyncVip.getVip_name().equals("男")) {//todo 需要确认数据是不是这样判断 + syncThirdMemberReq.setUser_gender(1); + } + if (sxSyncVip.getVip_name().equals("女")) { + syncThirdMemberReq.setUser_gender(2); + } + syncThirdMemberReq.setUser_mobile(sxSyncVip.getMobile()); + syncThirdMemberReq.setUser_birthday(sxSyncVip.getBirthday()); + syncThirdMemberReq.setUser_level(sxSyncVip.getCard_type());//todo 涉及会员等级字典转换 + syncThirdMemberReq.setUser_level_card(sxSyncVip.getCard_no()); + syncThirdMemberReq.setUser_points(sxSyncVip.getNow_acc_num()); + syncThirdMemberReq.setUser_money(sxSyncVip.getResidual_amt()); + if(sxSyncVip.getVip_date()!=null){ + try { + syncThirdMemberReq.setJoin_time(DateUtils.parseDate(sxSyncVip.getVip_date())); + } catch (ParseException e) { + logger.info("时间转换异常{0}",e); + throw new RuntimeException(e); + } + } + + } + } + return syncThirdMemberReqList; + } + + /** + * 将List转换为List + * @param sxSyncGoods + * @return + */ + public List CvtToGoosModel(List sxSyncGoods){ + if(CollectionUtil.isEmpty(sxSyncGoods)){ + return null; + } + List sxGoosModelList=new ArrayList<>(); + SxGoosModel sxGoosModel=null; + for (SxSyncGoods sxSyncGood:sxSyncGoods){ + sxGoosModel=new SxGoosModel(); + sxGoosModel.setProduct_name(sxSyncGood.getItem_subname()); + sxGoosModel.setProduct_number("");// todo + sxGoosModel.setProduct_barcode("");// todo + sxGoosModel.setFirst_category_name("");// todo + sxGoosModel.setSecond_category_name("");// todo + sxGoosModel.setThree_category_name("");// todo + sxGoosModel.setProduct_type("");// todo + sxGoosModel.setProduct_kind("");// todo + sxGoosModel.setCost_price(sxSyncGood.getPrice());//成本价 todo + sxGoosModel.setOriginal_price(sxSyncGood.getSale_price());//原价 todo + sxGoosModel.setOriginal_price(sxSyncGood.getSale_price());//零售价=原价? + sxGoosModel.setMember_price(sxSyncGood.getVip_price());//会员价 + sxGoosModel.setStock(sxSyncGood.getStock());//库存 todo + sxGoosModel.setGross_margin(sxSyncGood.getGross_margin()); //毛利率 todo + sxGoosModel.setUnit(sxSyncGood.getUnit_no());//单位 + sxGoosModel.setCan_piont(new BigDecimal(0));//可用积分 todo + sxGoosModel.setPoints(sxSyncGood.getVip_acc_num());//总积分 todo + sxGoosModel.setMnemonic(sxSyncGood.getItem_rem());//助记码 todo + sxGoosModel.setBuy_limit(0);//最大购买商品量 todo + sxGoosModel.setBrand_name("");//品牌 todo + sxGoosModel.setTags("");//标签 todo + + sxGoosModel.setProduct_assist(Arrays.asList("时令果蔬","生鲜"));//帮助 todo + + sxGoosModel.setProduct_spec(Collections.singletonList(sxSyncGood.getItem_size()));//规格 + + sxGoosModel.setProduct_value("");//商品卖点特征 todo + + sxGoosModel.setProduct_video("");//商品视频 todo + + sxGoosModel.setProduct_desc("");//商品描述 todo + + sxGoosModel.setProduct_images(new ArrayList<>());//介绍图片 todo + + sxGoosModel.setPromotion_detail(new ArrayList<>());//活动列表 todo + + sxGoosModelList.add(sxGoosModel); + } + return sxGoosModelList; + } + + + /** + * 批量保存商品 + * @param goodsListJSON + * @param storeId + * @return + */ + public int baseSaveOrUpdateGoodsBatch(JSONArray goodsListJSON,String storeId){ + AtomicInteger resultCount = new AtomicInteger(); + Map categoryMap= productCategoryService.getCategoryListByStoreId(storeId);//热数据加载 + List shopProductBaseList=new ArrayList<>(); + List shopProductIndexList=new ArrayList<>(); ; + List shopProductDataList=new ArrayList<>(); ; + List shopProductDetailList=new ArrayList<>(); ; + List shopProductInfoList=new ArrayList<>(); ; + List> shopProductItemLists=new ArrayList<>(); + ShopStoreBase store_row = shopStoreBaseService.get(storeId); + if (store_row == null) { + throw new ApiException(I18nUtil._("无法获取店铺信息!")); + } + goodsListJSON.stream().parallel().forEach(object -> { + final JSONObject jsonObj= (JSONObject) object; + Date currentDate = new Date(); + String cateGoryId=""; + if(null!=categoryMap.get(jsonObj.get("first_category_name"))){ + cateGoryId=categoryMap.get(jsonObj.get("first_category_name")).toString(); + } + Integer categoryId = Convert.toInt(cateGoryId, 0); + Integer storeIdInt = Convert.toInt(storeId); + + + ShopProductBase shopProductBase = new ShopProductBase(); + shopProductBase.setProduct_sale_time(Convert.toDate(DateUtil.current() + 600)); //10分钟 + shopProductBase.setStore_id(storeIdInt); + + shopProductBase.setProduct_number((String) jsonObj.get("product_number")); + shopProductBase.setProduct_name((String) jsonObj.get("product_name")); + shopProductBase.setStore_name(store_row.getStore_name()); + shopProductBase.setProduct_tips(""); + shopProductBase.setProduct_video(""); + shopProductBase.setTransport_type_id(StateCode.DELIVERY_TYPE_SAME_CITY); + shopProductBase.setProduct_state_id(StateCode.PRODUCT_STATE_NORMAL); + shopProductBase.setProduct_inventory_lock(1002); //库存锁定(ENUM):1001-下单锁定;1002-支付锁定; + shopProductBase.setProduct_fx_enable(0); // 供应商是否允许批发市场分销 + shopProductBase.setProduct_dist_enable(0); // 是否允许三级分销 + shopProductBase.setProduct_from(1005);// 商品来源(ENUM):1000-发布;1001-天猫;1002-淘宝;1003-阿里巴巴;1004-京东;1005-思迅; + shopProductBase.setProduct_add_time(currentDate.getTime()); + + // ShopProductIndex + ShopProductIndex shopProductIndex = new ShopProductIndex(); + shopProductIndex.setProduct_add_time(currentDate.getTime()); + shopProductIndex.setProduct_sale_time(DateUtil.current() + 600); //10分钟 + shopProductIndex.setStore_category_ids(""); // 店铺分类编号(DOT) + shopProductIndex.setProduct_tags("");// 商品标签(DOT) + shopProductIndex.setBrand_id(0); + shopProductIndex.setProduct_name(shopProductBase.getProduct_name()); // 产品名称:店铺平台先在对用表中检索后通过id检索,检索使用 + shopProductIndex.setProduct_name_index(shopProductIndex.getProduct_name()); // 名称索引关键字(DOT) + shopProductIndex.setCategory_id(categoryId); // 商品分类 + shopProductIndex.setProduct_fx_enable(0); // 供应商是否允许批发市场分销 + shopProductIndex.setProduct_dist_enable(0); // 是否允许三级分销 + shopProductIndex.setStore_id(storeIdInt); + shopProductIndex.setStore_type(store_row.getStore_type()); + shopProductIndex.setKind_id(StateCode.PRODUCT_KIND_ENTITY);// 实体商品 + shopProductIndex.setStore_is_open(store_row.getStore_is_open()); + shopProductIndex.setStore_is_selfsupport(store_row.getStore_is_selfsupport()); + shopProductIndex.setStore_longitude(store_row.getStore_longitude()); + shopProductIndex.setStore_latitude(store_row.getStore_latitude()); + + shopProductIndex.setCard_type_id(0); + shopProductIndex.setVoucher_activity_id(""); + shopProductIndex.setCoupon_type_id(0);// product_assist_data // 辅助属性值列(DOT) + shopProductIndex.setProduct_transport_id(String.valueOf(StateCode.DELIVERY_TYPE_SAME_CITY)); + + ShopBaseProductCategory category_row = productCategoryService.get(categoryId); + if (ObjectUtil.isNotEmpty(category_row)) { + Integer type_id = category_row.getType_id(); + shopProductIndex.setType_id(type_id); + } + + // shop_product_data + ShopProductData shopProductData = new ShopProductData(); + // product_id // 产品id + shopProductData.setProduct_edit_time(currentDate); + + // shop_product_detail + ShopProductDetail shopProductDetail = new ShopProductDetail(); + shopProductDetail.setProduct_detail(""); + shopProductDetail.setProduct_param(""); + shopProductDetail.setProduct_service(""); + shopProductDetail.setProduct_tags(""); + + // shop_product_info + ShopProductInfo shopProductInfo = new ShopProductInfo(); + shopProductInfo.setProduct_number((String) jsonObj.get("product_number"));// SPU商家编码:货号 + shopProductInfo.setProduct_assist(""); + shopProductInfo.setProduct_spec(""); + + shopProductInfo.setProduct_buy_limit(0); // 不限购 + //商品规格 + shopProductInfo.setSpec_ids(""); + + List shopProductItemList = new ArrayList<>(); + ShopProductItem shopProductItem = new ShopProductItem(); + shopProductItem.setStore_id(storeIdInt); + shopProductItem.setCategory_id(categoryId); + //零售价 + shopProductItem.setItem_unit_price(BigDecimal.valueOf(jsonObj.getDouble("retail_price"))); + shopProductItem.setItem_advice_price(BigDecimal.valueOf(jsonObj.getDouble("retail_price"))); + shopProductItem.setItem_market_price(BigDecimal.valueOf(jsonObj.getDouble("original_price"))); + //积分价 + shopProductItem.setItem_unit_points(new BigDecimal(jsonObj.getInt("points"))); + shopProductItem.setItem_quantity(jsonObj.getInt("stock")); // 库存 + shopProductItem.setItem_quantity_frozen(0); + shopProductItem.setItem_number(jsonObj.getStr("product_number"));// SKU商家编码 + shopProductItem.setItem_barcode(jsonObj.getStr("product_number")); // 条形码正常情况下就是货号 + shopProductItem.setItem_is_default(1); + shopProductItem.setItem_enable(1); + + //添加数据到list + synchronized(shopProductBaseList){ + shopProductItemList.add(shopProductItem); + shopProductBaseList.add(shopProductBase); + shopProductIndexList.add(shopProductIndex); + shopProductDataList.add(shopProductData); + shopProductDetailList.add(shopProductDetail); + shopProductInfoList.add(shopProductInfo); + shopProductItemLists.add(shopProductItemList); + } +// synchronized(shopProductIndexList){ +// shopProductIndexList.add(shopProductIndex); +// } +// +// synchronized(shopProductDataList){ +// shopProductDataList.add(shopProductData); +// } +// +// synchronized(shopProductDetailList){ +// shopProductDetailList.add(shopProductDetail); +// } +// +// synchronized(shopProductInfoList){ +// shopProductInfoList.add(shopProductInfo); +// } +// +// synchronized(shopProductItemLists){ +// shopProductItemLists.add(shopProductItemList); +// } + + // 保存数据 + + resultCount.addAndGet(1); + }); + shopProductBaseService.saveProductBatch(shopProductBaseList,shopProductIndexList,shopProductDataList,shopProductDetailList,shopProductInfoList,shopProductItemLists, + new ArrayList>(), + new ArrayList(), + new ArrayList()); + + return resultCount.get(); + } + + + public void syncPrimaryKey(){ + + List shopNumberSeqList=new ArrayList<>(); + QueryWrapper baseWrapper=new QueryWrapper<>(); + baseWrapper.select("MAX(product_id) as product_id"); + ShopProductBase shopProductBase= shopProductBaseServiceImpl.getOne(baseWrapper); + Long productId= shopProductBase.getProduct_id()+1; + //QueryWrapper baseSeWrapper=new QueryWrapper(); + //baseSeWrapper.eq("prefix", "product_id"); + ShopNumberSeq shopNumberSeqBase= new ShopNumberSeq(); + shopNumberSeqBase.setPrefix("product_id"); + shopNumberSeqBase.setNumber(productId); + //shopNumberSeqServiceImpl.edit(shopNumberSeqBase,baseWrapper); + //查询产品item + QueryWrapper itemQuery=new QueryWrapper<>(); + itemQuery.select("MAX(item_id) as item_id"); + ShopProductItem shopProductItem= shopProductItemServiceImpl.getOne(itemQuery); + Long itemtId= shopProductItem.getItem_id()+1; + // QueryWrapper itemWrapper=new QueryWrapper(); + //itemWrapper.eq("prefix", "item_id"); + ShopNumberSeq shopNumberSeqItem= new ShopNumberSeq(); + shopNumberSeqItem.setNumber(itemtId); + shopNumberSeqItem.setPrefix("item_id"); + shopNumberSeqList.add(shopNumberSeqBase); + shopNumberSeqList.add(shopNumberSeqItem); + shopNumberSeqServiceImpl.batchUpdateSeq(shopNumberSeqList); + shopNumberSeqServiceImpl.clearRelateGoodsId(); + } + + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncConfigServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncConfigServiceImpl.java index f4d0bff0..09937aa2 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncConfigServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncConfigServiceImpl.java @@ -8,4 +8,6 @@ import org.springframework.stereotype.Service; @Service public class SyncConfigServiceImpl extends ServiceImpl implements SyncConfigService { + + } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncFileLogServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncFileLogServiceImpl.java new file mode 100644 index 00000000..53645adf --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncFileLogServiceImpl.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.shop.sync.service.impl; +import com.suisung.mall.common.modules.sync.SyncFileLog; +import com.suisung.mall.core.web.service.impl.BaseServiceImpl; +import com.suisung.mall.shop.sync.mapper.SyncFileLogMapper; + +import com.suisung.mall.shop.sync.service.SyncFileLogService; +import org.springframework.stereotype.Service; + + + +@Service +public class SyncFileLogServiceImpl extends BaseServiceImpl implements SyncFileLogService { + + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncThirdDataServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncThirdDataServiceImpl.java index d9f3fded..08fb43bc 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncThirdDataServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sync/service/impl/SyncThirdDataServiceImpl.java @@ -9,79 +9,120 @@ package com.suisung.mall.shop.sync.service.impl; import cn.hutool.core.collection.CollUtil; + +import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.convert.Convert; -import cn.hutool.core.date.DateUtil; -import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; -import cn.hutool.crypto.SecureUtil; +import cn.hutool.http.HttpException; import cn.hutool.json.JSONArray; -import cn.hutool.json.JSONObject; + import cn.hutool.json.JSONUtil; + import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.suisung.mall.common.api.CommonResult; -import com.suisung.mall.common.api.StateCode; -import com.suisung.mall.common.constant.CommonConstant; -import com.suisung.mall.common.exception.ApiException; -import com.suisung.mall.common.feignService.AccountService; -import com.suisung.mall.common.feignService.PayService; -import com.suisung.mall.common.modules.account.AccountUserBase; -import com.suisung.mall.common.modules.account.AccountUserInfo; + +import com.suisung.mall.common.domain.UserDto; +import com.suisung.mall.common.enums.DicEnum; import com.suisung.mall.common.modules.base.ShopBaseProductBrand; import com.suisung.mall.common.modules.base.ShopBaseProductCategory; -import com.suisung.mall.common.modules.base.ShopBaseProductType; -import com.suisung.mall.common.modules.pay.PayUserResource; -import com.suisung.mall.common.modules.product.*; -import com.suisung.mall.common.modules.store.ShopStoreBase; + +import com.suisung.mall.common.modules.sixun.SxSyncGoods; +import com.suisung.mall.common.modules.sixun.SxSyncVip; +import com.suisung.mall.common.modules.sync.StoreDbConfig; import com.suisung.mall.common.modules.sync.SyncApp; import com.suisung.mall.common.modules.sync.SyncConfig; +import com.suisung.mall.common.modules.sync.SyncFileLog; import com.suisung.mall.common.pojo.req.SyncThirdMemberReq; import com.suisung.mall.common.pojo.res.ThirdApiRes; -import com.suisung.mall.common.utils.DateTimeUtils; + +import com.suisung.mall.common.utils.ContextUtil; import com.suisung.mall.common.utils.I18nUtil; + import com.suisung.mall.common.utils.StringUtils; -import com.suisung.mall.shop.base.service.ShopBaseProductBrandService; -import com.suisung.mall.shop.base.service.ShopBaseProductCategoryService; -import com.suisung.mall.shop.base.service.ShopBaseProductTypeService; -import com.suisung.mall.shop.product.service.ShopProductBaseService; -import com.suisung.mall.shop.product.service.impl.ShopProductBaseServiceImpl; -import com.suisung.mall.shop.store.service.ShopStoreBaseService; -import com.suisung.mall.shop.sync.service.SyncAppService; -import com.suisung.mall.shop.sync.service.SyncConfigService; -import com.suisung.mall.shop.sync.service.SyncThirdDataService; +import com.suisung.mall.core.web.service.RedisService; +import com.suisung.mall.shop.number.service.ShopNumberSeqService; +import com.suisung.mall.shop.page.service.OssService; +import com.suisung.mall.shop.sixun.dao.SxDataDao; +import com.suisung.mall.shop.sixun.dto.DataBaseInfo; +import com.suisung.mall.shop.sixun.dto.SxCategoryModel; +import com.suisung.mall.shop.sixun.dto.SxGoosModel; +import com.suisung.mall.shop.sixun.service.SxSyncCategoryService; +import com.suisung.mall.shop.sixun.service.SxSyncGoodsService; +import com.suisung.mall.shop.sixun.service.SxSyncVipService; +import com.suisung.mall.shop.sixun.utils.CommonUtil; +import com.suisung.mall.shop.sixun.utils.FileUtils; +import com.suisung.mall.shop.sync.Utils.CryptoUtils; +import com.suisung.mall.shop.sync.Utils.ThreadFileUtils; +import com.suisung.mall.shop.sync.keymanage.RedisKey; +import com.suisung.mall.shop.sync.service.*; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.util.Pair; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.datasource.DriverManagerDataSource; -import org.springframework.stereotype.Service; -import java.math.BigDecimal; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.InputStreamSource; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; + + @Service -public class SyncThirdDataServiceImpl implements SyncThirdDataService { - private final Logger logger = LoggerFactory.getLogger(ShopProductBaseServiceImpl.class); +public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements SyncThirdDataService { + private static Logger logger = LoggerFactory.getLogger(SyncThirdDataServiceImpl.class); private final int limitCnt = 300; - @Autowired - private ShopBaseProductBrandService productBrandService; - @Autowired - private ShopBaseProductCategoryService productCategoryService; - @Autowired - private ShopBaseProductTypeService productTypeService; - @Autowired - private AccountService accountService; - @Autowired - private PayService payService; - @Autowired - private ShopProductBaseService shopProductBaseService; - @Autowired - private ShopStoreBaseService shopStoreBaseService; + @Value("${client.path}") + public String clientPath; @Autowired private SyncAppService syncAppService; @Autowired private SyncConfigService syncConfigService; + @Autowired + private SxSyncCategoryService sxSyncCategoryService; + + @Autowired + private SxSyncGoodsService sxSyncGoodsService; + + @Autowired + private SxSyncVipService sxSyncVipService; + @Autowired + private ShopNumberSeqService shopNumberSeqService; + private final AtomicLong threadNum=new AtomicLong(0); + @Autowired + private SyncFileLogService syncFileLogService; + @Autowired + private OssService ossService; + + @Autowired + private RedisService redisService; + + @Autowired + private StoreDbConfigService storeDbConfigService; + + private final static String CLIENTFILEPATH="sxclient/"; + + /** * 批量保存商品的分类 @@ -111,103 +152,7 @@ public class SyncThirdDataServiceImpl implements SyncThirdDataService { return new ThirdApiRes().fail(1004, I18nUtil._("单次同步记录最多" + limitCnt + "条!")); } - int count = 0; - for (int i = 0; i < list.size(); i++) { - list.get(i).setStore_id(storeId); // app 记录传进来 - list.get(i).setData_source(2); // 思迅数据来源 - list.get(i).setCategory_is_enable(1); - - JSONObject o = (JSONObject) categoryListJSON.get(i); - if (o != null) { - - // 重要:分类类型处理(强调共性) - Integer typeId = 1001; - if (StrUtil.isNotBlank(o.getStr("product_type"))) { - ShopBaseProductType productType = productTypeService.getProductTypeByName(o.getStr("product_type")); - if (productType != null) { - list.get(i).setType_id(productType.getType_id()); - } else { - // 新增一个类型 - ShopBaseProductType newProductType = new ShopBaseProductType(); - newProductType.setType_name(o.getStr("product_type")); - newProductType.setType_buildin(0); - - if (productTypeService.save(newProductType)) { - typeId = newProductType.getType_id(); - } - } - - list.get(i).setType_id(typeId); - } - - - // 处理(第一级)父类字段 产品分类 - Integer firstParentId = 0; - if (StrUtil.isNotBlank(o.getStr("first_category_name"))) { - // TODO storeId 不判断一下吗? - ShopBaseProductCategory cate = productCategoryService.getCategoryByName(o.getStr("first_category_name")); - if (cate != null) { - list.get(i).setParent_id(cate.getCategory_id()); - } else { - // 新增一个(第一级)父类 - ShopBaseProductCategory firstCate = new ShopBaseProductCategory(); - firstCate.setCategory_name(o.getStr("first_category_name")); - firstCate.setParent_id(0); - firstCate.setStore_id(storeId); - firstCate.setType_id(typeId); - firstCate.setData_source(2); - if (productCategoryService.saveOrUpdate(firstCate)) { - // 当前子分类的父类id - firstParentId = firstCate.getId(); - list.get(i).setParent_id(firstParentId); - } - } - - list.get(i).setParent_id(firstParentId); - } - - // 处理(第二级)父类字段 产品分类 - if (StrUtil.isNotBlank(o.getStr("second_category_name"))) { - // TODO storeId 不判断一下吗? - ShopBaseProductCategory cate = productCategoryService.getCategoryByName(o.getStr("second_category_name")); - if (cate != null) { - list.get(i).setParent_id(cate.getCategory_id()); - } else { - // 新增一个(第二级)父类 - ShopBaseProductCategory secondCate = new ShopBaseProductCategory(); - secondCate.setCategory_name(o.getStr("second_category_name")); - secondCate.setParent_id(firstParentId); - secondCate.setStore_id(storeId); - secondCate.setType_id(typeId); - secondCate.setData_source(2); - if (productCategoryService.saveOrUpdate(secondCate)) { - // 当前子分类的第二级父类id - list.get(i).setParent_id(secondCate.getId()); - } - } - } - - } - - ShopBaseProductCategory productCategoryTemp = productCategoryService.getCategoryByName(list.get(i).getParent_id(), list.get(i).getCategory_name(), list.get(i).getStore_id()); - if (productCategoryTemp != null) { - // 更改记录 - if (!productCategoryTemp.getCategory_image().equals(list.get(i).getCategory_image()) - || !productCategoryTemp.getType_id().equals(list.get(i).getType_id()) - || !productCategoryTemp.getCategory_virtual_enable().equals(list.get(i).getCategory_virtual_enable()) - || !productCategoryTemp.getCategory_commission_rate().equals(list.get(i).getCategory_commission_rate()) - || !productCategoryTemp.getCategory_show_type().equals(list.get(i).getCategory_show_type())) { - list.get(i).setCategory_id(productCategoryTemp.getCategory_id()); - } else { - continue; - } - } - - if (productCategoryService.saveOrUpdate(list.get(i))) { - count++; - } - } - + int count=baseSaveOrUpdateShopBaseProductCategoryBatch(list,categoryListJSON,storeId); Map resp = new HashMap<>(); resp.put("count", count); @@ -242,23 +187,7 @@ public class SyncThirdDataServiceImpl implements SyncThirdDataService { return new ThirdApiRes().fail(1004, I18nUtil._("单次同步记录最多" + limitCnt + "条!")); } - int count = 0; - for (int i = 0; i < goodBrandList.size(); i++) { - goodBrandList.get(i).setStore_id(Integer.valueOf(storeId)); // app 记录传进来 - JSONObject o = (JSONObject) brandListJSON.get(i); - if (o != null && StrUtil.isNotBlank(o.getStr("category"))) { - // category 一般是父分类名 - ShopBaseProductCategory cate = productCategoryService.getCategoryByName(o.getStr("category")); - if (cate != null) { - goodBrandList.get(i).setCategory_id(cate.getCategory_id()); - } else { - // TODO 如果没有分类,是否新建? - } - } - - count += productBrandService.saveOrUpdateBrand2(goodBrandList.get(i)); - } - + int count=baseSaveOrUpdateShopBaseProductBrandBatch(goodBrandList,storeId,brandListJSON); Map resp = new HashMap<>(); resp.put("count", count); @@ -276,7 +205,6 @@ public class SyncThirdDataServiceImpl implements SyncThirdDataService { if (StrUtil.isBlank(appKey) || StrUtil.isBlank(sign) || ObjectUtil.isEmpty(goodsListJSON)) { return new ThirdApiRes().fail(1003, I18nUtil._("缺少必要参数!")); } - // 验签、appid,必要参数判断 SyncApp syncApp = syncAppService.checkAppSign(appKey, sign, goodsListJSON.toString()); if (syncApp == null) { @@ -288,115 +216,7 @@ public class SyncThirdDataServiceImpl implements SyncThirdDataService { return new ThirdApiRes().fail(1004, I18nUtil._("单次同步记录最多" + limitCnt + "条!")); } - - int count = 0; - for (JSONObject jsonObj : goodsListJSON.jsonIter()) { - Date currentDate = new Date(); - - Integer categoryId = Convert.toInt(jsonObj.get("first_category_name"), 0); - Integer storeIdInt = Convert.toInt(storeId); - ShopStoreBase store_row = shopStoreBaseService.get(storeId); - if (store_row == null) { - throw new ApiException(I18nUtil._("无法获取店铺信息!")); - } - - ShopProductBase shopProductBase = new ShopProductBase(); - shopProductBase.setProduct_sale_time(Convert.toDate(DateUtil.current() + 600)); //10分钟 - shopProductBase.setStore_id(storeIdInt); - shopProductBase.setProduct_number((String) jsonObj.get("product_number")); - shopProductBase.setProduct_name((String) jsonObj.get("product_name")); - shopProductBase.setStore_name(store_row.getStore_name()); - shopProductBase.setProduct_tips(""); - shopProductBase.setProduct_video(""); - shopProductBase.setTransport_type_id(StateCode.DELIVERY_TYPE_SAME_CITY); - shopProductBase.setProduct_state_id(StateCode.PRODUCT_STATE_NORMAL); - shopProductBase.setProduct_inventory_lock(1002); //库存锁定(ENUM):1001-下单锁定;1002-支付锁定; - shopProductBase.setProduct_fx_enable(0); // 供应商是否允许批发市场分销 - shopProductBase.setProduct_dist_enable(0); // 是否允许三级分销 - shopProductBase.setProduct_from(1005);// 商品来源(ENUM):1000-发布;1001-天猫;1002-淘宝;1003-阿里巴巴;1004-京东;1005-思迅; - shopProductBase.setProduct_add_time(currentDate.getTime()); - - // ShopProductIndex - ShopProductIndex shopProductIndex = new ShopProductIndex(); - shopProductIndex.setProduct_add_time(currentDate.getTime()); - shopProductIndex.setProduct_sale_time(DateUtil.current() + 600); //10分钟 - shopProductIndex.setStore_category_ids(""); // 店铺分类编号(DOT) - shopProductIndex.setProduct_tags("");// 商品标签(DOT) - shopProductIndex.setBrand_id(0); - shopProductIndex.setProduct_name(shopProductBase.getProduct_name()); // 产品名称:店铺平台先在对用表中检索后通过id检索,检索使用 - shopProductIndex.setProduct_name_index(shopProductIndex.getProduct_name()); // 名称索引关键字(DOT) - shopProductIndex.setCategory_id(categoryId); // 商品分类 - shopProductIndex.setProduct_fx_enable(0); // 供应商是否允许批发市场分销 - shopProductIndex.setProduct_dist_enable(0); // 是否允许三级分销 - shopProductIndex.setStore_id(storeIdInt); - shopProductIndex.setStore_type(store_row.getStore_type()); - shopProductIndex.setKind_id(StateCode.PRODUCT_KIND_ENTITY);// 实体商品 - shopProductIndex.setStore_is_open(store_row.getStore_is_open()); - shopProductIndex.setStore_is_selfsupport(store_row.getStore_is_selfsupport()); - shopProductIndex.setStore_longitude(store_row.getStore_longitude()); - shopProductIndex.setStore_latitude(store_row.getStore_latitude()); - - shopProductIndex.setCard_type_id(0); - shopProductIndex.setVoucher_activity_id(""); - shopProductIndex.setCoupon_type_id(0);// product_assist_data // 辅助属性值列(DOT) - shopProductIndex.setProduct_transport_id(String.valueOf(StateCode.DELIVERY_TYPE_SAME_CITY)); - - ShopBaseProductCategory category_row = productCategoryService.get(categoryId); - if (ObjectUtil.isNotEmpty(category_row)) { - Integer type_id = category_row.getType_id(); - shopProductIndex.setType_id(type_id); - } - - // shop_product_data - ShopProductData shopProductData = new ShopProductData(); - // product_id // 产品id - shopProductData.setProduct_edit_time(currentDate); - - // shop_product_detail - ShopProductDetail shopProductDetail = new ShopProductDetail(); - shopProductDetail.setProduct_detail(""); - shopProductDetail.setProduct_param(""); - shopProductDetail.setProduct_service(""); - shopProductDetail.setProduct_tags(""); - - // shop_product_info - ShopProductInfo shopProductInfo = new ShopProductInfo(); - shopProductInfo.setProduct_number((String) jsonObj.get("product_number"));// SPU商家编码:货号 - shopProductInfo.setProduct_assist(""); - shopProductInfo.setProduct_spec(""); - shopProductInfo.setProduct_buy_limit(0); // 不限购 - //商品规格 - shopProductInfo.setSpec_ids(""); - - List shopProductItemList = new ArrayList<>(); - ShopProductItem shopProductItem = new ShopProductItem(); - shopProductItem.setStore_id(storeIdInt); - shopProductItem.setCategory_id(categoryId); - //零售价 - shopProductItem.setItem_unit_price((BigDecimal) jsonObj.get("retail_price")); - shopProductItem.setItem_advice_price((BigDecimal) jsonObj.get("retail_price")); - shopProductItem.setItem_market_price((BigDecimal) jsonObj.get("retail_price")); - //积分价 - shopProductItem.setItem_unit_points((BigDecimal) jsonObj.get("points")); - shopProductItem.setItem_quantity((Integer) jsonObj.get("stock")); // 库存 - shopProductItem.setItem_quantity_frozen(0); - shopProductItem.setItem_number((String) jsonObj.get("product_number"));// SKU商家编码 - shopProductItem.setItem_barcode((String) jsonObj.get("product_number")); // 条形码正常情况下就是货号 - shopProductItem.setItem_is_default(1); - shopProductItem.setItem_enable(1); - - shopProductItemList.add(shopProductItem); - - // 保存数据 - Pair pair = shopProductBaseService.saveProduct(shopProductBase, shopProductIndex, shopProductData, shopProductDetail, shopProductInfo, shopProductItemList, new ArrayList(), new ShopProductValidPeriod(), new ArrayList()); - if (!pair.getFirst()) { - logger.error(pair.getSecond()); - continue; - } - - count += 1; - } - + int count=baseSaveOrUpdateGoods(goodsListJSON,storeId); Map resp = new HashMap<>(); resp.put("count", count); @@ -426,72 +246,7 @@ public class SyncThirdDataServiceImpl implements SyncThirdDataService { return new ThirdApiRes().fail(1004, I18nUtil._("单次同步记录最多" + limitCnt + "条!")); } - int count = 0; - for (SyncThirdMemberReq member : memberList) { - // account_user_base - AccountUserBase accountUserBase = new AccountUserBase(); - accountUserBase.setUser_account(StringUtils.generateUniqueCode(8)); - accountUserBase.setUser_nickname(member.getUser_nickname()); - accountUserBase.setUser_state(2);// 状态(ENUM):0-锁定;1-未激活;2-已激活; - accountUserBase.setUser_is_admin(CommonConstant.USER_TYPE_NORMAL); - accountUserBase.setStore_ids(storeId); - accountUserBase.setRights_group_id("0");// 普通用户,不是商家 - accountUserBase.setUser_type(CommonConstant.USER_TYPE_NORMAL); - - // 默认给了随机密码和盐,token - String user_key = IdUtil.simpleUUID(); - String user_salt = IdUtil.simpleUUID(); - String user_token = IdUtil.simpleUUID(); - accountUserBase.setUser_salt(user_salt); - accountUserBase.setUser_token(user_token); - accountUserBase.setUser_key(user_key); - accountUserBase.setUser_password(SecureUtil.md5(user_salt + SecureUtil.md5(IdUtil.simpleUUID()))); - - // 判断店铺是不是存在该昵称的会员了? - Boolean exists = accountService.existByNickname(member.getUser_nickname(), storeId); - if (exists) { - continue; - } - - AccountUserBase accountUserBase2 = accountService.saveOrUpdateUserBase2(accountUserBase); - - // 新增用户记录后,返回 userId ? - Integer userId = accountUserBase2.getUser_id(); - if (userId == null || userId <= 0) { - continue; - } - - // account_user_info - AccountUserInfo accountUserInfo = new AccountUserInfo(); - accountUserInfo.setUser_id(userId); - accountUserInfo.setUser_type_id(0); - accountUserInfo.setUser_mobile(member.getUser_mobile()); - accountUserInfo.setUser_level_card(member.getUser_level_card()); - //user_level_id - accountUserInfo.setUser_level_id(1); // todo select id - accountUserInfo.setUser_gender(member.getUser_gender()); - accountUserInfo.setUser_mobile(member.getUser_mobile()); - accountUserInfo.setUser_intl(CommonConstant.IDD_ZH_CN); - accountUserInfo.setUser_birthday(DateTimeUtils.parseDate(member.getUser_birthday(), "yyyy-MM-dd")); - boolean success = accountService.saveOrUpdateUserInfo(accountUserInfo); - - if (member.getUser_money() != null || member.getUser_points() != null) { - // pay_user_resource 用户支付资源,积分,余额 - PayUserResource payUserResource = new PayUserResource(); - payUserResource.setUser_id(userId); - payUserResource.setUser_money(member.getUser_money()); - payUserResource.setUser_money_frozen(BigDecimal.ZERO); - payUserResource.setUser_points(member.getUser_points()); - payUserResource.setUser_points_frozen(BigDecimal.ZERO); - - success = payService.saveOrUpdatePayUserResource(payUserResource); - } - - if (success) { - count += 1; - } - } - + int count =baseSaveOrUpdateMemberBatch(memberList,storeId); Map resp = new HashMap<>(); resp.put("count", count); @@ -530,42 +285,18 @@ public class SyncThirdDataServiceImpl implements SyncThirdDataService { return CommonResult.failed(); } - //jdbcTemplate - JdbcTemplate jdbcTemplate = getJDBCTemplate(syncConfig); - if (null == jdbcTemplate) { - return CommonResult.failed(); - } - //1-品牌,2-分类,3-商品,4-会员 switch (syncType) { case 1: - return syncProductBrand(jdbcTemplate); + return syncProductBrand(new DataBaseInfo(),storeId);//todo case 2: - return syncProductClazz(jdbcTemplate); + return syncProductClazz(new DataBaseInfo(),storeId);//todo 测试 case 3: - return syncProduct(jdbcTemplate); + return syncProduct(new DataBaseInfo(),storeId);//todo 没做完 case 4: - return syncVip(jdbcTemplate); + return syncVip(new DataBaseInfo(),storeId);//todo 测试 } - return null; - } - - //创建Template - private JdbcTemplate getJDBCTemplate(SyncConfig syncConfig) { - - switch (syncConfig.getSys_version()) { - case "hbposev9": - DriverManagerDataSource dataSource = new DriverManagerDataSource(); - dataSource.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); - String url = String.format("jdbc:sqlserver://%s:%s;databaseName=%s", - syncConfig.getSever_ip(), syncConfig.getSql_port(), syncConfig.getSql_db()); - dataSource.setUrl(url); - dataSource.setUsername(syncConfig.getSql_acc()); - dataSource.setPassword(syncConfig.getSql_pwd()); - // 创建 JdbcTemplate - return new JdbcTemplate(dataSource); - } - return null; + return CommonResult.success(); } @@ -574,13 +305,27 @@ public class SyncThirdDataServiceImpl implements SyncThirdDataService { * * @return */ - public CommonResult syncProduct(JdbcTemplate template) { - //商品编码 item_no、商品名称 item_name、零售价 sale_price、会员价 vip_price - String sql = "SELECT item_no, item_name, sale_price, vip_price FROM t_bd_item_info"; - - List oList = template.queryForList(sql, String.class); - //todo 第三方 商品同步接口 - return null; + public CommonResult syncProduct(DataBaseInfo dataBaseInfo,String storeId) { + int total= sxSyncGoodsService.getGoodsTotal(dataBaseInfo); + // 总页数 + int pages = CommonUtil.getPagesCount(total, SxDataDao.PAGESIZE); + int syncCount =0; + for (int i = 1; i < pages; i++) { + int count=0; + List sxSyncGoodsList= sxSyncGoodsService.findGoodsListPage(dataBaseInfo,i,pages); + //todo 数据转换 + List sxGoosModelList= CvtToGoosModel(sxSyncGoodsList); + if(CollectionUtil.isEmpty(sxSyncGoodsList)){ + continue; + } + count= baseSaveOrUpdateGoods(JSONUtil.parseArray(sxGoosModelList),storeId); + if(count<=0){ + continue; + } + syncCount+=count; + } + logger.info("同步商品的总数为{},成功数量为{}",total,syncCount); + return CommonResult.success(); } @@ -589,12 +334,30 @@ public class SyncThirdDataServiceImpl implements SyncThirdDataService { * * @return */ - public CommonResult syncVip(JdbcTemplate template) { - //会员卡号card_no、card_id、会员姓名vip_name、手机号vip_tel | mobile、性别 vip_sex、生日 birthday - String sql = "SELECT card_no, card_id, vip_name, vip_tel, vip_sex, birthday FROM t_rm_vip_info"; - List oList = template.queryForList(sql, String.class); - //todo 第三方 会员同步接口 - return null; + public CommonResult syncVip(DataBaseInfo dataBaseInfo,String storeId) { + // 记录总数 + Integer total = sxSyncVipService.getVipMembersTotal(dataBaseInfo); + // 总页数 + int pages = CommonUtil.getPagesCount(total, SxDataDao.PAGESIZE); + List memberList=new ArrayList<>(); + SyncThirdMemberReq syncThirdMemberReq=null; + int syncCount =0; + for (int i = 1; i < pages; i++) { + memberList.clear(); + int count = 0; + syncThirdMemberReq=new SyncThirdMemberReq(); + List sxSyncVipList= sxSyncVipService.findVipMemberPage(dataBaseInfo,i,SxDataDao.PAGESIZE); + //处理数据转换SxSyncVip>SyncThirdMemberReq + memberList=ConverList(sxSyncVipList); + memberList.add(syncThirdMemberReq); + count=baseSaveOrUpdateMemberBatch(memberList,storeId); + if (count <= 0) { + continue; + } + syncCount+=count; + } + logger.info("vip会员总共有{}条数据,同步完成{}条",total,syncCount); + return CommonResult.success(); } /** @@ -602,17 +365,287 @@ public class SyncThirdDataServiceImpl implements SyncThirdDataService { * * @return */ - public CommonResult syncProductBrand(JdbcTemplate template) { + public CommonResult syncProductBrand(DataBaseInfo dataBaseInfo,String storeId) { return null; } /** * 同步商品分类 - * * @return */ - public CommonResult syncProductClazz(JdbcTemplate template) { - return null; + public CommonResult syncProductClazz(DataBaseInfo dataBaseInfo,String storeId) { + // 记录总数 + Integer total = sxSyncCategoryService.getCategoryTotal(dataBaseInfo); + // 总页数 + int pages = CommonUtil.getPagesCount(total, SxDataDao.PAGESIZE); + int syncCount =0; + for (int i = 1; i <= pages; i++) { + int count = 0; + List list = sxSyncCategoryService.getCategoryByDataBasePage(dataBaseInfo,i,SxDataDao.PAGESIZE); + if (CollUtil.isEmpty(list)) { + continue; + } + JSONArray categoryListJSON=JSONUtil.parseArray(list); + List shopBaseProductCategories = JSONUtil.toList(categoryListJSON, ShopBaseProductCategory.class); + if (shopBaseProductCategories == null) { + logger.info("转换类型为空,类方法为{}","com.suisung.mall.shop.sync.service.impl.SyncThirdDataServiceImpl.syncProductClazz"); + continue; + } + count = baseSaveOrUpdateShopBaseProductCategoryBatch(shopBaseProductCategories,categoryListJSON,storeId); + if (count <= 0) { + continue; + } + syncCount+=count; + } + logger.info("商品分类总共有{}条数据,同步完成{}条",total,syncCount); + return CommonResult.success(); + } + + /** + * 文件上传 + * @param appKey + * @param sign + * @param page 分页 + * @param syncType + * @param multipartFile + * @return + */ + @Override + public ThirdApiRes fileUpload(String appKey, String sign,String page,String syncType, MultipartFile multipartFile) { + if (StrUtil.isBlank(appKey) || StrUtil.isBlank(sign) ) { + return new ThirdApiRes().fail(1003, I18nUtil._("缺少必要参数!")); + } + // 验签、appid,必要参数判断 + SyncApp syncAppO = syncAppService.getOne(new LambdaQueryWrapper() + .select(SyncApp::getApp_key, SyncApp::getApp_secret,SyncApp::getStore_id) + .eq(SyncApp::getApp_key, appKey) + .eq(SyncApp::getApp_secret,sign)); + if (syncAppO == null) { + return new ThirdApiRes().fail(1001, I18nUtil._("签名有误!")); + } + String storeId = syncAppO.getStore_id(); + try { + if (multipartFile.isEmpty()) { + return new ThirdApiRes().fail(1001, I18nUtil._("文件不能为空!")); + } + byte[] bytes = multipartFile.getBytes(); + String folder=new FileUtils().getSyncTypeFlag(syncType,clientPath)+storeId+FileUtils.pathSeparator+page+FileUtils.pathSeparator; + String filName=multipartFile.getOriginalFilename(); + String filePath= FileUtils.createFolderAndFileUsingFile(folder,filName); + Path path = Paths.get(filePath); + Files.write(path, bytes); +// String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath() +// .path("/download/") +// .path(Objects.requireNonNull(multipartFile.getOriginalFilename())) +// .toUriString(); + return new ThirdApiRes().success("文件上传成功"); + } catch (IOException e) { + return new ThirdApiRes().fail(500,"文件上传失败"); + } + } + + /** + * 多线程处理文件 + * @param appKey + * @param sign + * @param syncType + * @param folders + */ + @Override + public void SyncReadSxFileData(String appKey, String sign, String syncType, List folders) { + SyncApp syncApp = syncAppService.getOne(new LambdaQueryWrapper() + .select(SyncApp::getApp_key, SyncApp::getApp_secret,SyncApp::getStore_id) + .eq(SyncApp::getApp_key, appKey) + .eq(SyncApp::getApp_secret,sign)); + String storeId = syncApp.getStore_id(); + if(null==syncApp.getStore_id()|| syncApp.getStore_id().isEmpty()){ + logger.info("商品id为空"); + return; + } + if(folders==null||folders.isEmpty()){ + logger.info("没有商品数据"); + return; + } + List newFolders=new ArrayList<>(); + folders.forEach(page->{ + String newfolder=new FileUtils().getSyncTypeFlag(syncType,clientPath)+storeId+FileUtils.pathSeparator+page+FileUtils.pathSeparator; + newFolders.add(newfolder); + }); + syncPrimaryKey(); + shopNumberSeqService.clearKey(); + ExecutorService executor = Executors.newFixedThreadPool(6); + List> futures = new ArrayList<>(); + // 提交任务 + AtomicInteger success= new AtomicInteger(); + AtomicInteger fails= new AtomicInteger(); + List failFolders=new ArrayList<>(); + List failMessage=new ArrayList<>(); + for (int i = 0; i < newFolders.size(); i++) { + final int taskId = i; + threadNum.incrementAndGet(); + futures.add(executor.submit(() -> { + int count = 0;//失败重试机制,当失败重试一次,再次失败则记录到数据库中 + while (true){ + count++; + String taskName=newFolders.get(taskId); + String fileName="good_"+taskId+".txt"; + JSONArray jsonArray=new ThreadFileUtils().processFolder(taskName,newFolders.get(taskId)); + try { + baseSaveOrUpdateGoodsBatch(jsonArray,storeId); + success.getAndIncrement(); + threadNum.decrementAndGet(); + return "成功" + taskId; + }catch (Exception e){ + if(count<2){ + continue; + } + fails.getAndIncrement(); + failFolders.add(newFolders.get(taskId)+fileName); + failMessage.add(taskId+"_"+e.getMessage()); + return "失败"+newFolders.get(taskId); + } + } + })); + } + // 等待所有任务完成 + for (Future future : futures) { + try { + System.out.println("任务结果: " + future.get()); + } catch (Exception e) { + System.err.println("任务执行异常: " + e.getMessage()); + } + } + executor.shutdown(); + //记录到数据库 + syncPrimaryKey(); + shopNumberSeqService.clearKey(); + List syncFileLogs=new ArrayList<>(); + for (int i = 0; i < failFolders.size(); i++) { + String path=failFolders.get(i); + String taskId=failMessage.get(i).split("_")[0]; + SyncFileLog syncFileLog=new SyncFileLog(); + syncFileLog.setSyncType(syncType); + syncFileLog.setFileName(path.substring(path.lastIndexOf(FileUtils.pathSeparator)+1)); + syncFileLog.setSyncStatus(DicEnum.FAILED.getCode()); + syncFileLog.setSyncTaskId(taskId); + syncFileLog.setSyncStoreId(storeId); + syncFileLog.setFilePath(path); + syncFileLog.setErrorMessage(failMessage.get(i)); + syncFileLog.setSourceSystem(DicEnum.SOURCE_SYSTEM_TYPE_1005.getCode()); + syncFileLog.setTargetSystem(DicEnum.SOURCE_SYSTEM_TYPE_SELF.getCode()); + syncFileLogs.add(syncFileLog); + } + if(CollUtil.isNotEmpty(syncFileLogs)){ + syncFileLogService.saveBatch(syncFileLogs,syncFileLogs.size()); + } + //todo 定时清理文件,建议用服务器脚本 + logger.info("执行成功{}个文件,失败{}个文件",success,fails); + logger.info("同步商品数据执行结束"); + } + + @Override + public ResponseEntity downloadToClient(String primaryKey) { + logger.info("primaryKey:{}",primaryKey); + boolean checked= syncAppService.checkPrimaryKey(primaryKey); + // UserDto userDto=ContextUtil.getCurrentUser(); + //redisService.del(RedisKey.SXCLIENTKEYVERSION);//todo 删除 + String version= (String) redisService.get(RedisKey.SXCLIENTKEYVERSION); + if(checked){ + String tempFilePath=System.getProperty("user.home"); + String clientJarPath=""; + if(StringUtils.isEmpty(version)){ + version="v1"; + //String ossFolder="/sxclient/"+version; + redisService.set(RedisKey.SXCLIENTKEYVERSION,version); + logger.error("没有版本更新"); + return ResponseEntity.ok() + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .header(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=error.txt") + .header("error","noVersion") + .body(new ByteArrayResource(version.getBytes(StandardCharsets.UTF_8))); + }else { + List folderList= ossService.listFolders(CLIENTFILEPATH); + version= String.valueOf(Convert.toInt(version.split("v")[1])+1); + version="v"+version; + String finalVersion =CLIENTFILEPATH + version+"/"; + String folder= folderList.stream().filter(finalVersion::equals).collect(Collectors.joining()); + if(StringUtils.isNotEmpty(folder)){ + //String ossFolder="/sxclient/"+folder; + String filePath= ossService.listDocuments(folder).get(0).getKey(); + clientJarPath= ossService.download(filePath,tempFilePath+FileUtils.pathSeparator+filePath); + }else { + logger.error("查找没有版本更新"); + return ResponseEntity.ok() + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .header(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=error.txt") + .header("error","noVersion") + .body(new ByteArrayResource(version.getBytes(StandardCharsets.UTF_8))); + } + } + if(StringUtils.isNotEmpty(clientJarPath)){ + // 构建文件路径 + Path filePath = Paths.get(clientJarPath); + File file = filePath.toFile(); + // 检查文件是否存在 + if (!file.exists()) { + return ResponseEntity.status(HttpStatus.NOT_FOUND) + .header("error" ,"noFile") + .body(new ByteArrayResource(version.getBytes())); + } + // 创建Resource对象 + Resource resource = new FileSystemResource(file); + + // 获取文件MIME类型 + String contentType = null; + try { + contentType = Files.probeContentType(filePath); + } catch (IOException e) { + return ResponseEntity.status(HttpStatus.NOT_FOUND) + .header("error" ,"500") + .body(new ByteArrayResource(version.getBytes())); + } + if (contentType == null) { + contentType = "application/octet-stream"; + } + redisService.set(RedisKey.SXCLIENTKEYVERSION,version); + // 构建响应 + return ResponseEntity.ok() + .contentType(MediaType.parseMediaType(contentType)) + .header(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=\"" + file.getName() + "\"") + .body(resource); + } + } + logger.info("!校验失败"); + return ResponseEntity.status(HttpStatus.NOT_FOUND) + .header("error" ,"noValid") + .body(new ByteArrayResource("version".getBytes())); + } + + @Override + public ThirdApiRes getStoreDbConfig(String appKey,String sign) { + if (StrUtil.isBlank(appKey) || StrUtil.isBlank(sign) ) { + return new ThirdApiRes().fail(1003, I18nUtil._("缺少必要参数!")); + } + // 验签、appid,必要参数判断 + SyncApp syncAppO = syncAppService.getOne(new LambdaQueryWrapper() + .select(SyncApp::getApp_key, SyncApp::getApp_secret,SyncApp::getStore_id) + .eq(SyncApp::getApp_key, appKey) + .eq(SyncApp::getApp_secret,sign)); + if (syncAppO == null) { + return new ThirdApiRes().fail(1001, I18nUtil._("签名有误!")); + } + String storeId = syncAppO.getStore_id(); + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("store_id", storeId); + queryWrapper.eq("has_start",DicEnum.YESORNO_1.getCode()); + StoreDbConfig storeDbConfig= storeDbConfigService.getOne(queryWrapper); + if (storeDbConfig == null) { + return new ThirdApiRes().fail(1003, I18nUtil._("服务器配置缺少配置信息!")); + } + return new ThirdApiRes().success("成功",storeDbConfig); } } diff --git a/mall-shop/src/main/resources/bootstrap-dev.yml b/mall-shop/src/main/resources/bootstrap-dev.yml index 1358c1bf..fb7aab8d 100644 --- a/mall-shop/src/main/resources/bootstrap-dev.yml +++ b/mall-shop/src/main/resources/bootstrap-dev.yml @@ -8,7 +8,7 @@ spring: mvc: static-path-pattern: /shop/static/** datasource: - url: jdbc:mysql://@mysql.host@:@mysql.port@/@mysql.db@?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&&zeroDateTimeBehavior=convertToNull + url: jdbc:mysql://@mysql.host@:@mysql.port@/@mysql.db@?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&&zeroDateTimeBehavior=convertToNull username: @mysql.user@ password: @mysql.pwd@ driver-class-name: @mysql.driver@ diff --git a/mall-shop/src/main/resources/bootstrap-local.yml b/mall-shop/src/main/resources/bootstrap-local.yml index f79745fd..cc07f8a6 100644 --- a/mall-shop/src/main/resources/bootstrap-local.yml +++ b/mall-shop/src/main/resources/bootstrap-local.yml @@ -8,7 +8,7 @@ spring: mvc: static-path-pattern: /shop/static/** datasource: - url: jdbc:mysql://@mysql.host@:@mysql.port@/@mysql.db@?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&&zeroDateTimeBehavior=convertToNull + url: jdbc:mysql://@mysql.host@:@mysql.port@/@mysql.db@?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&&zeroDateTimeBehavior=convertToNull username: @mysql.user@ password: @mysql.pwd@ driver-class-name: @mysql.driver@ @@ -181,4 +181,7 @@ esign: app_id: 7439053575 app_secret: 8da2e1eeeaf88e09bcf432a2fdd3e4d7 app_rsa: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiC+fUc0+O9m45VEcGciJQ5QQNXs3NkHoHM2qDAdrXOnTwku0Be1IPeWUZ4s7w8xqubyirAAJDc3LpRkwCK84NicA2VwraD4on8MNtX8MLALZjLc1jZTmRAPKVfTKAcainR7ET78Y+QKJgezNvI7u45FO4Db+dWCC7pbedxBo+kHKA8im+/G0hpQaklxw1wjIMNv+x+YBnm8FOXRPWJZ+eItF5qJOT2C16QCY7hdeHknom+NMpZD8E/WAMtf03BcgigsoavTVnPI0xnN8BCrgykDWgO5bUXeIgNEF1LJS6r8s6BaMl+ZWbuODtbsrQ941GbFOe6x8tnhPIeehIa1AWQIDAQAB - debug: true \ No newline at end of file + debug: true +#客户端上传文件地址 +client: + path: @client.path@ diff --git a/mall-shop/src/main/resources/bootstrap-prod.yml b/mall-shop/src/main/resources/bootstrap-prod.yml index 841800e5..4750e715 100644 --- a/mall-shop/src/main/resources/bootstrap-prod.yml +++ b/mall-shop/src/main/resources/bootstrap-prod.yml @@ -6,7 +6,7 @@ spring: mvc: static-path-pattern: /shop/static/** datasource: - url: jdbc:mysql://@mysql.host@:@mysql.port@/@mysql.db@?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false&maxReconnects=5 + url: jdbc:mysql://@mysql.host@:@mysql.port@/@mysql.db@?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false&maxReconnects=5 username: @mysql.user@ password: @mysql.pwd@ driver-class-name: @mysql.driver@ diff --git a/mall-shop/src/main/resources/bootstrap-test.yml b/mall-shop/src/main/resources/bootstrap-test.yml index 96dbafd4..73384e68 100644 --- a/mall-shop/src/main/resources/bootstrap-test.yml +++ b/mall-shop/src/main/resources/bootstrap-test.yml @@ -6,7 +6,7 @@ spring: mvc: static-path-pattern: /shop/static/** datasource: - url: jdbc:mysql://@mysql.host@:@mysql.port@/@mysql.db@?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&&zeroDateTimeBehavior=convertToNull + url: jdbc:mysql://@mysql.host@:@mysql.port@/@mysql.db@?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&&zeroDateTimeBehavior=convertToNull username: @mysql.user@ password: @mysql.pwd@ driver-class-name: @mysql.driver@ diff --git a/mall-shop/src/main/resources/bootstrap-uat.yml b/mall-shop/src/main/resources/bootstrap-uat.yml index 96dbafd4..73384e68 100644 --- a/mall-shop/src/main/resources/bootstrap-uat.yml +++ b/mall-shop/src/main/resources/bootstrap-uat.yml @@ -6,7 +6,7 @@ spring: mvc: static-path-pattern: /shop/static/** datasource: - url: jdbc:mysql://@mysql.host@:@mysql.port@/@mysql.db@?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&&zeroDateTimeBehavior=convertToNull + url: jdbc:mysql://@mysql.host@:@mysql.port@/@mysql.db@?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&&zeroDateTimeBehavior=convertToNull username: @mysql.user@ password: @mysql.pwd@ driver-class-name: @mysql.driver@ diff --git a/mall-shop/src/main/resources/mapper/number/ShopNumberSeqMapper.xml b/mall-shop/src/main/resources/mapper/number/ShopNumberSeqMapper.xml index b353b471..bfedffa4 100644 --- a/mall-shop/src/main/resources/mapper/number/ShopNumberSeqMapper.xml +++ b/mall-shop/src/main/resources/mapper/number/ShopNumberSeqMapper.xml @@ -6,5 +6,32 @@ prefix, number + + + + + + + + + + + UPDATE shop_number_seq + SET + prefix = #{prefix}, + number = #{number} + WHERE prefix = #{prefix} + + + + diff --git a/sql/shop/dev/20250520_ddl.sql b/sql/shop/dev/20250520_ddl.sql new file mode 100644 index 00000000..be3b7129 --- /dev/null +++ b/sql/shop/dev/20250520_ddl.sql @@ -0,0 +1,51 @@ +CREATE TABLE `sync_file_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `sync_task_id` varchar(64) NOT NULL COMMENT '同步任务ID', + `sync_store_id` varchar(64) NOT NULL COMMENT '店铺id', + `file_path` varchar(1024) NOT NULL COMMENT '文件路径', + `file_name` varchar(255) NOT NULL COMMENT '文件名', + `file_size` bigint(20) DEFAULT NULL COMMENT '文件大小(字节)', + `file_md5` varchar(32) DEFAULT NULL COMMENT '文件MD5值', + `source_system` varchar(64) NOT NULL COMMENT '源系统标识', + `target_system` varchar(64) NOT NULL COMMENT '目标系统标识', + `sync_type` char(1) NOT NULL COMMENT '同步类型(1:商品,2:商品分类,3:会员,4.品牌)', + `sync_status` char(1) NOT NULL COMMENT '同步状态(0:等待中,1:进行中,2:成功,3:失败)', + `retry_count` tinyint(4) DEFAULT '0' COMMENT '重试次数', + `error_message` text , + `start_time` datetime DEFAULT NULL COMMENT '开始时间', + `end_time` datetime DEFAULT NULL COMMENT '结束时间', + `duration` int(11) DEFAULT NULL COMMENT '耗时(毫秒)', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `extra_info` json DEFAULT NULL COMMENT '额外信息(JSON格式)', + PRIMARY KEY (`id`), + KEY `idx_sync_task_id` (`sync_task_id`), + KEY `idx_file_path` (`file_path`(255)), + KEY `idx_file_name` (`file_name`), + KEY `idx_source_target` (`source_system`, `target_system`), + KEY `idx_sync_status` (`sync_status`), + KEY `idx_create_time` (`create_time`), + KEY `idx_update_time` (`update_time`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文件同步日志表'; + +CREATE TABLE `store_db_config` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `store_id` varchar(64) NOT NULL COMMENT '店铺ID', + `db_type` varchar(20) NOT NULL DEFAULT 'sqlserver' COMMENT '数据库类型(mysql/oracle/sqlserver等)', + `db_name` varchar(64) NOT NULL COMMENT '数据库名称', + `db_ip` varchar(64) NOT NULL COMMENT '数据库IP地址', + `db_port` int(11) NOT NULL DEFAULT 3306 COMMENT '数据库端口', + `db_username` varchar(64) NOT NULL COMMENT '数据库用户名', + `db_password` varchar(256) NOT NULL COMMENT '数据库密码(建议加密存储)', + `has_internet` char(1) NOT NULL DEFAULT 0 COMMENT '是否有外网访问(0:无,1:有)', + `sync_mode` char(1) NOT NULL DEFAULT 2 COMMENT '同步模式(1:定时同步,2:间隔同步)', + `has_start` char(1) NOT NULL DEFAULT 1 COMMENT '是否启用(0:否,1:是)', + `cron_expression` varchar(64) DEFAULT NULL COMMENT '定时同步的cron表达式', + `category_name` text NULL DEFAULT NULL COMMENT '商品分类', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `remark` varchar(512) DEFAULT NULL COMMENT '备注信息', + PRIMARY KEY (`id`), + KEY `idx_store_id` (`store_id`), + KEY `idx_has_internet` (`has_internet`), + KEY `idx_has_start` (`has_start`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='店铺数据库连接配置表'; From db25392dbb47422fafbfad5c69d78e35c3b06ff6 Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Tue, 20 May 2025 17:56:01 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E5=95=86=E7=91=9E9.7=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AF=E4=BB=A3=E7=A0=81=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/pom.xml | 126 ++++ client/readme.txt | 21 + .../com/small/client/Cache/CommonCache.java | 48 ++ .../com/small/client/ClientApplication.java | 27 + .../client/Schedule/DynamicTaskScheduler.java | 103 +++ .../com/small/client/Utils/CommonUtil.java | 98 +++ .../com/small/client/Utils/CryptoUtils.java | 110 +++ .../com/small/client/Utils/FileUtils.java | 176 +++++ .../com/small/client/Utils/HttpUtils.java | 107 +++ .../com/small/client/Utils/JarPathUtil.java | 70 ++ .../com/small/client/comment/DicEnum.java | 30 + .../com/small/client/comment/UrlComment.java | 11 + .../com/small/client/config/ClientConfig.java | 14 + .../com/small/client/config/TaskConfig.java | 18 + .../client/controller/WebController.java | 79 +++ .../java/com/small/client/dao/BaseDao.java | 246 +++++++ .../java/com/small/client/dao/SxDataDao.java | 448 ++++++++++++ .../java/com/small/client/dto/BrandModel.java | 27 + .../com/small/client/dto/CommentModel.java | 17 + .../com/small/client/dto/DataBaseInfo.java | 33 + .../com/small/client/dto/ImBranchStock.java | 18 + .../com/small/client/dto/ProductImage.java | 17 + .../com/small/client/dto/PromotionDetail.java | 17 + .../java/com/small/client/dto/ResultDto.java | 15 + .../com/small/client/dto/SpecPriceDto.java | 19 + .../com/small/client/dto/StoreDbConfig.java | 64 ++ .../com/small/client/dto/SxCategoryModel.java | 32 + .../com/small/client/dto/SxGoosModel.java | 103 +++ .../com/small/client/dto/SxSyncCategory.java | 68 ++ .../com/small/client/dto/SxSyncGoods.java | 118 ++++ .../java/com/small/client/dto/SxSyncVip.java | 80 +++ .../client/dto/SyncGoodsSearchModel.java | 22 + .../small/client/dto/SyncThirdMemberReq.java | 54 ++ .../com/small/client/dto/UploadModel.java | 15 + .../java/com/small/client/enums/DicEnum.java | 72 ++ .../service/SxDataAbst/SxDataAbstService.java | 180 +++++ .../small/client/service/SxDataService.java | 53 ++ .../client/service/WebClientService.java | 88 +++ .../client/service/imp/SxDataServiceImp.java | 645 ++++++++++++++++++ client/src/main/resources/application.yml | 15 + client/src/main/resources/logback.xml | 19 + 41 files changed, 3523 insertions(+) create mode 100644 client/pom.xml create mode 100644 client/readme.txt create mode 100644 client/src/main/java/com/small/client/Cache/CommonCache.java create mode 100644 client/src/main/java/com/small/client/ClientApplication.java create mode 100644 client/src/main/java/com/small/client/Schedule/DynamicTaskScheduler.java create mode 100644 client/src/main/java/com/small/client/Utils/CommonUtil.java create mode 100644 client/src/main/java/com/small/client/Utils/CryptoUtils.java create mode 100644 client/src/main/java/com/small/client/Utils/FileUtils.java create mode 100644 client/src/main/java/com/small/client/Utils/HttpUtils.java create mode 100644 client/src/main/java/com/small/client/Utils/JarPathUtil.java create mode 100644 client/src/main/java/com/small/client/comment/DicEnum.java create mode 100644 client/src/main/java/com/small/client/comment/UrlComment.java create mode 100644 client/src/main/java/com/small/client/config/ClientConfig.java create mode 100644 client/src/main/java/com/small/client/config/TaskConfig.java create mode 100644 client/src/main/java/com/small/client/controller/WebController.java create mode 100644 client/src/main/java/com/small/client/dao/BaseDao.java create mode 100644 client/src/main/java/com/small/client/dao/SxDataDao.java create mode 100644 client/src/main/java/com/small/client/dto/BrandModel.java create mode 100644 client/src/main/java/com/small/client/dto/CommentModel.java create mode 100644 client/src/main/java/com/small/client/dto/DataBaseInfo.java create mode 100644 client/src/main/java/com/small/client/dto/ImBranchStock.java create mode 100644 client/src/main/java/com/small/client/dto/ProductImage.java create mode 100644 client/src/main/java/com/small/client/dto/PromotionDetail.java create mode 100644 client/src/main/java/com/small/client/dto/ResultDto.java create mode 100644 client/src/main/java/com/small/client/dto/SpecPriceDto.java create mode 100644 client/src/main/java/com/small/client/dto/StoreDbConfig.java create mode 100644 client/src/main/java/com/small/client/dto/SxCategoryModel.java create mode 100644 client/src/main/java/com/small/client/dto/SxGoosModel.java create mode 100644 client/src/main/java/com/small/client/dto/SxSyncCategory.java create mode 100644 client/src/main/java/com/small/client/dto/SxSyncGoods.java create mode 100644 client/src/main/java/com/small/client/dto/SxSyncVip.java create mode 100644 client/src/main/java/com/small/client/dto/SyncGoodsSearchModel.java create mode 100644 client/src/main/java/com/small/client/dto/SyncThirdMemberReq.java create mode 100644 client/src/main/java/com/small/client/dto/UploadModel.java create mode 100644 client/src/main/java/com/small/client/enums/DicEnum.java create mode 100644 client/src/main/java/com/small/client/service/SxDataAbst/SxDataAbstService.java create mode 100644 client/src/main/java/com/small/client/service/SxDataService.java create mode 100644 client/src/main/java/com/small/client/service/WebClientService.java create mode 100644 client/src/main/java/com/small/client/service/imp/SxDataServiceImp.java create mode 100644 client/src/main/resources/application.yml create mode 100644 client/src/main/resources/logback.xml diff --git a/client/pom.xml b/client/pom.xml new file mode 100644 index 00000000..8c99599a --- /dev/null +++ b/client/pom.xml @@ -0,0 +1,126 @@ + + + 4.0.0 + + com.small + client + v1 + jar + + org.springframework.boot + spring-boot-starter-parent + 2.3.0.RELEASE + + + + + UTF-8 + UTF-8 + 8 + 8 + 5.8.20 + + + + org.springframework.boot + spring-boot-starter-web + + + org.apache.commons + commons-text + 1.10.0 + + + org.projectlombok + lombok + + + io.springfox + springfox-swagger2 + 2.9.2 + + + + com.microsoft.sqlserver + mssql-jdbc + 9.4.1.jre8 + + + ch.qos.logback + logback-classic + + + + cn.hutool + hutool-all + ${hutool.version} + + + + + + + + ali-maven + https://maven.aliyun.com/repository/central + + true + + + true + always + fail + + + + repo.maven.apache.org + https://repo.maven.apache.org/maven2 + + true + + + true + always + fail + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + repackage + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + + + \ No newline at end of file diff --git a/client/readme.txt b/client/readme.txt new file mode 100644 index 00000000..7bcd7ded --- /dev/null +++ b/client/readme.txt @@ -0,0 +1,21 @@ +本系统主要安装在客户端 +主要有以下几个功能 + +第一阶段 +1、通过http获取远程数据小发服务配置,基础数据包括主要包括数据库地址ip,数据库名称,数据库密码,以及同步定时时间 + +2.采集本地数据库,通过多线程调用同步接口,同步商品,种类,会员 + +第二阶段 +由于系统的数据同步存在性能瓶颈,需要使用文件传输的方式同步数据, +文件采用分页形式同步数据,每一页使用一个文件,服务器解析文件采用多线程解析方式解析 + + + +打包功能更,需要写一个shell脚本自动打包放到服务器供给下载和java的jkd安装包,这些下载放到后端的网页端,暴露下载地址 + +todo +1、通过http获取dataInfo没有写 +2、文件传输没有写 +3.商品同步没有写 +4.商品分类同步没有写 diff --git a/client/src/main/java/com/small/client/Cache/CommonCache.java b/client/src/main/java/com/small/client/Cache/CommonCache.java new file mode 100644 index 00000000..d90da612 --- /dev/null +++ b/client/src/main/java/com/small/client/Cache/CommonCache.java @@ -0,0 +1,48 @@ +package com.small.client.Cache; + +import cn.hutool.cache.Cache; +import cn.hutool.cache.impl.FIFOCache; +import cn.hutool.core.date.DateUnit; +import com.small.client.dto.BrandModel; +import com.small.client.dto.SpecPriceDto; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 用于本地缓存 + */ +@Component +public class CommonCache { + public final static String CACHE_CATEGROY = "CACHE_CATEGROY";//分类缓存 + + private Cache cache =new FIFOCache<>(20000); + + private Cache> spriceCache =new FIFOCache<>(20); + + private Cache> brandCahce =new FIFOCache<>(20); + + public void put(String key, String value) { + cache.put(key, value, DateUnit.MINUTE.getMillis()*20); + } + + public String get(String key) { + return cache.get(key); + } + + public void putSpecPrice(String key, List value) { + spriceCache.put(key, value, DateUnit.MINUTE.getMillis()*20); + } + + public List getSpecPrice(String key) { + return spriceCache.get(key); + } + + public List getBrandCahce(String key) { + return brandCahce.get(key); + } + + public void setBrandCahce(String key, List value) { + brandCahce.put(key, value, DateUnit.MINUTE.getMillis()*20); + } +} diff --git a/client/src/main/java/com/small/client/ClientApplication.java b/client/src/main/java/com/small/client/ClientApplication.java new file mode 100644 index 00000000..1a61fead --- /dev/null +++ b/client/src/main/java/com/small/client/ClientApplication.java @@ -0,0 +1,27 @@ +package com.small.client; + +import com.small.client.Utils.JarPathUtil; +import com.small.client.service.SxDataService; +import com.small.client.service.WebClientService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +import javax.annotation.Resource; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; + +@SpringBootApplication +@Slf4j +public class ClientApplication{ + public static void main(String[] args) { + SpringApplication.run(ClientApplication.class, args); + } + +} \ No newline at end of file diff --git a/client/src/main/java/com/small/client/Schedule/DynamicTaskScheduler.java b/client/src/main/java/com/small/client/Schedule/DynamicTaskScheduler.java new file mode 100644 index 00000000..56d0c47c --- /dev/null +++ b/client/src/main/java/com/small/client/Schedule/DynamicTaskScheduler.java @@ -0,0 +1,103 @@ +package com.small.client.Schedule; + +import com.small.client.dto.CommentModel; +import com.small.client.dto.DataBaseInfo; +import com.small.client.service.SxDataService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.support.CronTrigger; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.time.Duration; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; + +@Service +@Slf4j +public class DynamicTaskScheduler { + private final TaskScheduler taskScheduler; + private final SxDataService sxDataService; + private final Map> scheduledTasks = new ConcurrentHashMap<>(); + + private boolean isRuning =false; + + @Autowired + public DynamicTaskScheduler(TaskScheduler taskScheduler, SxDataService sxDataService) { + this.taskScheduler = taskScheduler; + this.sxDataService = sxDataService; + } + + @PostConstruct + public void initTasks() { + refreshTasks(); + // 每5分钟检查一次数据库更新 + taskScheduler.scheduleAtFixedRate(this::refreshTasks, Duration.ofHours(1)); + } + + public void refreshTasks() { + if(!isRuning){ + sxDataService.checkForUpdates();//检查app更新 + } + CommentModel commentModel =sxDataService.getCommentModel(); + DataBaseInfo enabledTask = sxDataService.getDataBaseInfo(commentModel); + + List enabledTasks=new ArrayList<>(); + enabledTasks.add(enabledTask); + // 移除已禁用或删除的任务 + scheduledTasks.keySet().removeIf(taskKey -> + enabledTasks.stream().noneMatch(task -> task.getDataBaseName().equals(taskKey))); + + // 新增或更新任务 + enabledTasks.forEach(task -> { + if (!scheduledTasks.containsKey(task.getDataBaseName()) || + isCronModified(task)) { + cancelExistingTask(task.getDataBaseName()); + scheduleTask(task,commentModel); + } + }); + } + + private void scheduleTask(DataBaseInfo task, CommentModel commentModel) { + ScheduledFuture future = taskScheduler.schedule( + () -> executeTask(task.getDataBaseName(),commentModel), + new CronTrigger(task.getCronExpression()) + ); + scheduledTasks.put(task.getDataBaseName(), future); + } + + /** + * 业务逻辑执行 + * @param taskKey + */ + private void executeTask(String taskKey, CommentModel commentModel) { + isRuning=true; + log.info("execute task key:{}, commentModel:{}", taskKey, commentModel); + if(commentModel==null){ + commentModel =sxDataService.getCommentModel(); + } + if(StringUtils.isEmpty(commentModel.getSyncTime())){ + commentModel =sxDataService.getCommentModel(); + } + DataBaseInfo dataBaseInfo=sxDataService.getDataBaseInfo(commentModel); + sxDataService.SyncBranchList(dataBaseInfo,commentModel); + sxDataService.SyncCategory(dataBaseInfo,commentModel); + sxDataService.SyncGoods(dataBaseInfo,commentModel);//todo 暂时同步全部的商品如果后期修改,需要增加服务器的字段test + sxDataService.SyncVipList(dataBaseInfo,commentModel); + isRuning=false; + } + + private void cancelExistingTask(String taskKey) { + Optional.ofNullable(scheduledTasks.remove(taskKey)) + .ifPresent(future -> future.cancel(false)); + } + + private boolean isCronModified(DataBaseInfo newTask) { + DataBaseInfo dataBaseInfo= sxDataService.getDataBaseInfo(sxDataService.getCommentModel()); + return scheduledTasks.containsKey(newTask.getDataBaseName()) && + !dataBaseInfo.getCronExpression().equals(newTask.getCronExpression()); + } +} diff --git a/client/src/main/java/com/small/client/Utils/CommonUtil.java b/client/src/main/java/com/small/client/Utils/CommonUtil.java new file mode 100644 index 00000000..4f8db7e3 --- /dev/null +++ b/client/src/main/java/com/small/client/Utils/CommonUtil.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.small.client.Utils; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; + +public class CommonUtil { + + private final static String apiUrl = "http://4ei8850868ux.vicp.fun"; + + public static JSONObject sendPostRequestToSiXun(String urlPath, JSONObject params) { + String resp = HttpUtil.post(apiUrl + urlPath, params.toString()); + if (StrUtil.isBlank(resp)) { + return null; + } + + JSONObject respObj = JSONUtil.parseObj(resp); + + return respObj; + } + + /** + * 根据总条数和分页大小,求页数 + * + * @param total + * @param pageSize + * @return + */ + public static Integer getPagesCount(Integer total, Integer pageSize) { + if (total == null || pageSize == null || pageSize <= 0 || total <= 0) { + return 0; + } + + int pagesCount = 0; + pagesCount = total / pageSize; + + if (total % pageSize > 0) { + // 有余数 + pagesCount++; + } else { + if (pagesCount == 0) { + pagesCount = 1; + } + } + + return pagesCount; + } + + /** + * 接口是否成功执行返回 + * + * @param jsonObject + * @return + */ + public static Boolean isSuccess(JSONObject jsonObject) { + if (jsonObject == null) { + return false; + } + + return jsonObject.get("code") != null && jsonObject.getStr("code").equals("0"); + } + + /** + * 接口是否成功执行返回 + * + * @param jsonObject + * @return + */ + public static Boolean hasSuccessData(JSONObject jsonObject) { + if (jsonObject == null) { + return false; + } + + return jsonObject.get("code") != null && jsonObject.getStr("code").equals("0") && jsonObject.get("data") != null; + } + + /** + * 通过json节点表达式,获取节点json字符串,注:驼峰命名改成下划线命名 + * + * @param jsonObject + * @param expression json 节点表达式比如: data.list, msg, code + * @return + */ + public static String toUnderlineJson(JSONObject jsonObject, String expression) { + return StrUtil.toUnderlineCase(jsonObject.getByPath(expression, String.class)); + } + + +} diff --git a/client/src/main/java/com/small/client/Utils/CryptoUtils.java b/client/src/main/java/com/small/client/Utils/CryptoUtils.java new file mode 100644 index 00000000..6c99f73d --- /dev/null +++ b/client/src/main/java/com/small/client/Utils/CryptoUtils.java @@ -0,0 +1,110 @@ +package com.small.client.Utils; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +public class CryptoUtils { + private static final String ALGORITHM = "AES"; + private static final String TRANSFORMATION = "AES/ECB/PKCS5Padding"; + private static final String SECRET_KEY = "9f823ea6ab22785caf040e4cc3930619"; // 必须16/24/32字符 + + private static final String HASH_ALGORITHM_KEY = "appKey=a&sign=b&storeId=c"; + + /** + * 打包并加密字段 + */ + public static String packAndEncrypt(String appKey, String sign, String storeId) throws Exception { + String combined = String.format("appKey=%s&sign=%s&storeId=%s", appKey, sign, storeId); + return encrypt(combined); + } + + /** + * 32位字符串生成 + * @param input + * @return + * @throws NoSuchAlgorithmException + */ + public static String generate32CharMD5(String input) throws NoSuchAlgorithmException { + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] hashBytes = md.digest(input.getBytes()); + + StringBuilder hexString = new StringBuilder(); + for (byte b : hashBytes) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + + return hexString.toString(); + } + + /** + * 解密并解包字段 + */ + public static Map decryptAndUnpack(String encryptedData) throws Exception { + String decrypted = decrypt(encryptedData); + Map result = new HashMap<>(); + String[] pairs = decrypted.split("&"); + for (String pair : pairs) { + String[] keyValue = pair.split("="); + if (keyValue.length == 2) { + result.put(keyValue[0], keyValue[1]); + } + } + return result; + } + + /** + * 加密 + * @param value + * @return + * @throws Exception + */ + private static String encrypt(String value) throws Exception { + SecretKeySpec secretKey = new SecretKeySpec(SECRET_KEY.getBytes(StandardCharsets.UTF_8), ALGORITHM); + Cipher cipher = Cipher.getInstance(TRANSFORMATION); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + byte[] encryptedBytes = cipher.doFinal(value.getBytes(StandardCharsets.UTF_8)); + return Base64.getEncoder().encodeToString(encryptedBytes); + } + + /** + * 解密 + * @param encryptedValue + * @return + * @throws Exception + */ + private static String decrypt(String encryptedValue) throws Exception { + SecretKeySpec secretKey = new SecretKeySpec(SECRET_KEY.getBytes(StandardCharsets.UTF_8), ALGORITHM); + Cipher cipher = Cipher.getInstance(TRANSFORMATION); + cipher.init(Cipher.DECRYPT_MODE, secretKey); + byte[] decodedBytes = Base64.getDecoder().decode(encryptedValue); + byte[] decryptedBytes = cipher.doFinal(decodedBytes); + return new String(decryptedBytes, StandardCharsets.UTF_8); + } + + + // 示例用法 + public static void main(String[] args) throws Exception { + + String appKey = "d68397c4fb671bc024e24e1964b067cc35388818"; + String sign = "d68397c4fb671bc024e24e1964b067cc35388818"; + String storeId = "1"; + + // 打包加密 + String encrypted = packAndEncrypt(appKey, sign, storeId); + System.out.println("加密结果: " + encrypted); + + // 解密解包 + Map result = decryptAndUnpack(encrypted); + System.out.println("解密结果: " + result); + } +} diff --git a/client/src/main/java/com/small/client/Utils/FileUtils.java b/client/src/main/java/com/small/client/Utils/FileUtils.java new file mode 100644 index 00000000..b865a8da --- /dev/null +++ b/client/src/main/java/com/small/client/Utils/FileUtils.java @@ -0,0 +1,176 @@ +package com.small.client.Utils; + + +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Calendar; +import java.util.Date; + +@Slf4j +public class FileUtils { + + public static final String pathSeparator = System.getProperty("file.separator"); + public static final String FOLDER = System.getProperty("user.home") + pathSeparator+"uploaded"+pathSeparator; + public static final String GOODS = pathSeparator+"goods"+pathSeparator;//商品 + public static final String CATEGORY= pathSeparator+ "category"+pathSeparator;//分类 + public static final String BRAND = pathSeparator+"brand/"+pathSeparator;//品牌 + public static final String MEMBER= pathSeparator+"member"+pathSeparator;//会员 + + public static final String CLIENTSTALLPATH = System.getProperty("user.home") + pathSeparator+"cientStorePath"+pathSeparator; + + public static final String REFLESHDATE="refleshdate.txt"; + public static final String PRIMARYKEY="primaryKey.txt"; + + public static final String GOODS_TYPE = "goods";//商品 + public static final String CATEGORY_TYPE= "category";//分类 + public static final String BRAND_TYPE = "brand";//品牌 + public static final String MEMBER_TYPE= "member";//会员 + + public static final String okEnd = "ok";//后缀 + public static final String txtEnd = "txt";//后缀 + + public static String fileFormat = "%s_%s.%s";//good_1 + + public static String getSyncTypeFlag(String syncType){ + Calendar calendar=Calendar.getInstance(); + int year=calendar.get(Calendar.YEAR); + int month=calendar.get(Calendar.MONTH)+1; + int date=calendar.get(Calendar.DAY_OF_MONTH); + String result =FOLDER; + switch (syncType){ + case "1": + result=GOODS; + break; + case "2": + result=CATEGORY; + break; + case "3": + result=BRAND; + break; + case "4": + result=MEMBER; + break; + default: + break; + } + return result+year+pathSeparator+month+pathSeparator+date+pathSeparator; + } + + /** + * 创建文件 + * @param syncType + * @param page + * @return + */ + public File createFile(String syncType,Integer page){ + String path= getSyncTypeFlag(syncType+pathSeparator+page+pathSeparator); + File file=new File(path); + if(!file.exists()){ + file.mkdirs(); + } + return file; + } + + /** + * 创建文件 + * @param path + * @return + */ + public File createFile(String path){ + File file=new File(path); + if(!file.exists()){ + file.mkdirs(); + } + return file; + } + + /** + * 文件写入 + * @param filePath + */ + public void writeFile(String filePath,String fileName,String content){ + try { + FileWriter writer = new FileWriter(filePath+pathSeparator+fileName); + writer.write(content); + writer.close(); + log.info("文件写入成功!"); + } catch (IOException e) { + log.info("文件写入失败:{}", e.getMessage()); + } + } + + /** + * 根据类型获取文件名称 + * @param syncType + * @param page + * @return + */ + public String getFileName(String syncType ,Integer page,String endFix){ + String result=""; + switch (syncType){ + case "1": + result=GOODS_TYPE; + break; + case "2": + result=CATEGORY_TYPE; + break; + case "3": + result=BRAND_TYPE; + break; + case "4": + result=MEMBER_TYPE; + break; + default: + break; + } + return String.format(fileFormat, result,page,endFix); + } + + /** + * 创建文件 + * @param folderPath + * @param filePath + */ + public static void createFolderAndFileUsingFile(String folderPath, String filePath) { + File folder = new File(folderPath); + if (!folder.exists()) { + folder.mkdir(); + } + + File file = new File(filePath); + try { + if (!file.exists()) { + file.createNewFile(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + public static void copyFile(String srcFile, String destFile) { + String path= JarPathUtil.getRuntimePath(); + Path sourceFile = Paths.get(srcFile); + Path targetDir =Paths.get(destFile); + try { + Files.copy(sourceFile, targetDir.resolve(sourceFile.getFileName()), + StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + + public static void main(String[] args) { + FileUtils fileUtils= new FileUtils(); + File file=fileUtils.createFile("1",1); + System.out.printf("--"+file.getAbsoluteFile()); + fileUtils.writeFile(file.getAbsolutePath(),fileUtils.getFileName("1",2,txtEnd),"456"); + } + +} diff --git a/client/src/main/java/com/small/client/Utils/HttpUtils.java b/client/src/main/java/com/small/client/Utils/HttpUtils.java new file mode 100644 index 00000000..5bf17681 --- /dev/null +++ b/client/src/main/java/com/small/client/Utils/HttpUtils.java @@ -0,0 +1,107 @@ +package com.small.client.Utils; + +import cn.hutool.json.JSON; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.small.client.dto.StoreDbConfig; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import java.util.Map; + +@Slf4j +public class HttpUtils { + + public static final String SUCCESSCODE="0";//上传文件 + + public static final String URL_UPLOUP="/shop/sync/third/uploudSxData";//上传文件 + + + public static final String URL_SYNC_CATEGORY="/shop/sync/third/goods/category";//商品分类数据同步 + + public static final String URL_SYNC_BRAND="/shop/sync/third/goods/brand";//商品品牌数据同步 + + public static final String URL_SYNC_GOODS="/shop/sync/third/goods";//商品数据同步 + + public static final String URL_SYNC_MEMBER="/shop/sync/third/member"; + + public static final String URL_SYNC_GOODS_READ="/shop/sync/third/readSxData";//商品数据同步 + + public static final String URL_SYNC_GET_APPSIGN="/shop/sync/third/getAppSign";//获取密文 + + public static final String URL_SYNC_GET_DOWNCLIENTJAR="/shop/sync/app/downClientJar";//文件下载 + + public static final String URL_SYNC_GET_STOREdBCONFIG="/shop/sync/third/getStoreDbConfig";//文件下载 + + public static String postData(RestTemplate restTemplate, String url,Object modelObject){ + // 创建表单参数 +// MultiValueMap map = new LinkedMultiValueMap<>(); +// map.add("key1", "value1"); +// map.add("key2", "value2"); + + // 设置Content-Type为application/x-www-form-urlencoded + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + log.info(modelObject.toString()); + HttpEntity request = new HttpEntity<>(modelObject, headers); + + // 发送POST请求 + JSONObject jsonObject = restTemplate.postForObject(url, request, JSONObject.class); + assert jsonObject != null; + log.info(jsonObject.toString()); + return jsonObject.getStr("error_code"); + } + + public static String postData(RestTemplate restTemplate, String url,Object modelObject,String contentName){ + // 创建表单参数 +// MultiValueMap map = new LinkedMultiValueMap<>(); +// map.add("key1", "value1"); +// map.add("key2", "value2"); + + // 设置Content-Type为application/x-www-form-urlencoded + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + log.info(modelObject.toString()); + HttpEntity request = new HttpEntity<>(modelObject, headers); + + // 发送POST请求 + JSONObject jsonObject = restTemplate.postForObject(url, request, JSONObject.class); + assert jsonObject != null; + log.info(jsonObject.toString()); + if("OK".equals(jsonObject.get("resultCode"))){ + return jsonObject.getStr(contentName); + } + return null; + } + + public static StoreDbConfig postDataGetConfig(RestTemplate restTemplate, String url, Object modelObject){ + // 创建表单参数 +// MultiValueMap map = new LinkedMultiValueMap<>(); +// map.add("key1", "value1"); +// map.add("key2", "value2"); + + // 设置Content-Type为application/x-www-form-urlencoded + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + log.info(modelObject.toString()); + HttpEntity request = new HttpEntity<>(modelObject, headers); + + // 发送POST请求 + JSONObject jsonObject = restTemplate.postForObject(url, request, JSONObject.class); + assert jsonObject != null; + log.info(jsonObject.toString()); + if(0==jsonObject.getInt("error_code")){ + JSONObject object= jsonObject.getJSONObject("result"); + if(null!=object){ + return JSONUtil.toBean(object,StoreDbConfig.class); + } + } + return null; + } + +} diff --git a/client/src/main/java/com/small/client/Utils/JarPathUtil.java b/client/src/main/java/com/small/client/Utils/JarPathUtil.java new file mode 100644 index 00000000..78bc506f --- /dev/null +++ b/client/src/main/java/com/small/client/Utils/JarPathUtil.java @@ -0,0 +1,70 @@ +package com.small.client.Utils; + +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; + +@Slf4j +public class JarPathUtil { + /** + * 更健壮的获取JAR路径方法 + */ + public static String getJarPath() { + try { + // 获取当前类的URI + String jarPath = JarPathUtil.class + .getProtectionDomain() + .getCodeSource() + .getLocation() + .toURI() + .getPath(); + + // 处理Windows路径问题 + if (jarPath.startsWith("/") && System.getProperty("os.name").contains("Windows")) { + jarPath = jarPath.substring(1); + } + + return new File(jarPath).getAbsolutePath(); + } catch (URISyntaxException e) { + throw new RuntimeException("无法解析JAR文件URI", e); + } + } + + /** + * 使用NIO获取JAR所在目录 + */ + public static String getJarParentPath() { + try { + Path path = Paths.get(JarPathUtil.class.getProtectionDomain() + .getCodeSource() + .getLocation() + .toURI()); + return path.getParent().toString(); + } catch (URISyntaxException e) { + throw new RuntimeException("无法解析JAR文件URI", e); + } + } + public static String getRuntimePath() { + URL location = JarPathUtil.class.getProtectionDomain() + .getCodeSource() + .getLocation(); + String path = location.getPath(); + + // 判断是否在IDE中运行(class文件) + if (path.endsWith(".jar")) { + // JAR运行模式 + return new File(path).getParent(); + } else { + // IDE运行模式,返回项目目录 + return new File("").getAbsolutePath(); + } + } + + public static void main(String[] args) { + log.info(getRuntimePath()); + } +} diff --git a/client/src/main/java/com/small/client/comment/DicEnum.java b/client/src/main/java/com/small/client/comment/DicEnum.java new file mode 100644 index 00000000..a88f1031 --- /dev/null +++ b/client/src/main/java/com/small/client/comment/DicEnum.java @@ -0,0 +1,30 @@ +package com.small.client.comment; + +import lombok.Getter; + +public enum DicEnum { + //1-品牌,2-分类,3-商品,4-会员 + SYNCTYPE_1("1","品牌","syncType","同步类型"), + SYNCTYPE_2("2","分类","syncType","同步类型"), + SYNCTYPE_3("3","商品","syncType","同步类型"), + SYNCTYPE_4("4","会员","syncType","同步类型"), + ; + + @Getter + private String code; + @Getter + private String name; + @Getter + private String dicType; + @Getter + private String description; + + DicEnum(String code, String name, String dicType, String description) { + this.code = code; + this.name = name; + this.dicType = dicType; + this.description = description; + } + + +} diff --git a/client/src/main/java/com/small/client/comment/UrlComment.java b/client/src/main/java/com/small/client/comment/UrlComment.java new file mode 100644 index 00000000..2c02301c --- /dev/null +++ b/client/src/main/java/com/small/client/comment/UrlComment.java @@ -0,0 +1,11 @@ +package com.small.client.comment; + +public class UrlComment { + + public static String url="";//获取shop的数据库基本信息 todo + + public static String UrlSynShop="";//商品同步接口 + public static String UrlSynCategory="";//商品种类同步接口 + + public static String UrlSynProduct=""; +} diff --git a/client/src/main/java/com/small/client/config/ClientConfig.java b/client/src/main/java/com/small/client/config/ClientConfig.java new file mode 100644 index 00000000..c062e31c --- /dev/null +++ b/client/src/main/java/com/small/client/config/ClientConfig.java @@ -0,0 +1,14 @@ +package com.small.client.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class ClientConfig { + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} diff --git a/client/src/main/java/com/small/client/config/TaskConfig.java b/client/src/main/java/com/small/client/config/TaskConfig.java new file mode 100644 index 00000000..a4f41f72 --- /dev/null +++ b/client/src/main/java/com/small/client/config/TaskConfig.java @@ -0,0 +1,18 @@ +package com.small.client.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler; + +import java.util.concurrent.Executors; + +@Configuration +@EnableScheduling +public class TaskConfig { + @Bean + public TaskScheduler taskScheduler() { + return new ConcurrentTaskScheduler(Executors.newScheduledThreadPool(10)); // 使用默认线程池 + } +} diff --git a/client/src/main/java/com/small/client/controller/WebController.java b/client/src/main/java/com/small/client/controller/WebController.java new file mode 100644 index 00000000..737b0be9 --- /dev/null +++ b/client/src/main/java/com/small/client/controller/WebController.java @@ -0,0 +1,79 @@ +package com.small.client.controller; + +import com.small.client.Utils.HttpUtils; +import com.small.client.dto.DataBaseInfo; +import com.small.client.dto.SyncGoodsSearchModel; +import com.small.client.service.SxDataService; +import com.small.client.service.WebClientService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/client") +@Slf4j +public class WebController { + @Autowired + private WebClientService webClientService; + @Autowired + private SxDataService sxDataService; + + @RequestMapping("/upload") + public void upload(){ + System.out.println("upload"); + //webClientService.uploudSxData(); + } + + @RequestMapping("/synBrand") + public void synBrand(){ + log.info("synBrand"); + sxDataService.getAppSign(); + + sxDataService.SyncBranchList(new DataBaseInfo(),sxDataService.getCommentModel()); + } + + @RequestMapping("/syncCategory") + public void syncCategory(){ + log.info("syncCategory"); + sxDataService.getAppSign(); + sxDataService.SyncCategory(new DataBaseInfo(),sxDataService.getCommentModel()); + } + + @RequestMapping("/syncGoods") + public void syncGoods(){ + log.info("syncGoods"); + sxDataService.getAppSign(); + sxDataService.SyncGoods(new DataBaseInfo(),sxDataService.getCommentModel()); + } + + @RequestMapping("/synvip") + public void synvip(){ + log.info("synvip"); + sxDataService.getAppSign(); + sxDataService.SyncVipList(new DataBaseInfo(),sxDataService.getCommentModel()); + } + + @RequestMapping("/getAppSign") + public void getAppSign(){ + log.info("getAppSign"); + sxDataService.getAppSign(); + } + + @RequestMapping("/downLoadClient") + public void downLoadClient(){ + log.info("downLoadClient"); + sxDataService.downLoadClient(); + } + + @RequestMapping("/udpateAndStart") + public void updateAndStart(){ + sxDataService.checkForUpdates(); + } + + @RequestMapping("/getDataBaseInfo") + public void getDataBaseInfo(){ + sxDataService.getDataBaseInfo(sxDataService.getCommentModel()); + } + +} diff --git a/client/src/main/java/com/small/client/dao/BaseDao.java b/client/src/main/java/com/small/client/dao/BaseDao.java new file mode 100644 index 00000000..d9d3e948 --- /dev/null +++ b/client/src/main/java/com/small/client/dao/BaseDao.java @@ -0,0 +1,246 @@ +package com.small.client.dao; + +import com.microsoft.sqlserver.jdbc.SQLServerDataSource; +import com.microsoft.sqlserver.jdbc.SQLServerException; + +import com.small.client.dto.ResultDto; +import lombok.extern.slf4j.Slf4j; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +@Slf4j +public class BaseDao { + + private final static String DEFAULT_IP="127.0.0.1"; + private final static String DEFAULT_DATABASE="hbposev9"; + private final static String DEFAULT_USERNAME="sa"; + private final static String DEFAULT_PWD="123456"; + private final static int PortNumber=1433; + private final static int LoginTimeout=10; + /** + * + * 动态获取数据库连接 + * @param ip 数据库ip+ + * @param username 用户名 + * @param password 密码 todo 需要加密 + * @param dataBaseName 数据库名称 + * @return + */ + public Connection getConnection(String ip, String username, String password,Integer port, String dataBaseName){ + Connection conn=null; + SQLServerDataSource sqlServerDataSource=new SQLServerDataSource(); + sqlServerDataSource.setDatabaseName(dataBaseName==null?DEFAULT_DATABASE:dataBaseName); + sqlServerDataSource.setServerName(ip==null?DEFAULT_IP:ip); + sqlServerDataSource.setPortNumber(port==null?PortNumber:port); + sqlServerDataSource.setLoginTimeout(LoginTimeout); + sqlServerDataSource.setPassword(password==null?DEFAULT_PWD:password); + sqlServerDataSource.setUser(username==null?DEFAULT_USERNAME:username); + try { + conn=sqlServerDataSource.getConnection(); + } catch (SQLServerException e) { + log.info("数据库连接异常方法{}异常信息{}","com.suisung.mall.shop.sixun.dao.BaseDao.getConnection",e.getMessage()); + throw new RuntimeException(e); + } + + return conn; + } + + /** + * 关闭数据库连接 + * @param conn + */ + public void Close(Connection conn){ + if(conn!=null){ + try { + conn.close(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + } + + /** + * 返回游标和连接 减少服务器的占用内存 + * @param ip + * @param username + * @param password + * @param dataBaseName + * @return ResultDto + */ + public ResultDto baseFindList(String ip, String username, String password,Integer portNumber, String dataBaseName, String table,String where){ + Connection connection=getConnection(ip,username,password,portNumber,dataBaseName); + String sql="select * from %s %s"; + sql=String.format(sql, table,where); + ResultDto resultDto=new ResultDto(); + ResultSet rs=null; + log.info(sql); + try { + PreparedStatement ps= connection.prepareStatement(sql); + rs = ps.executeQuery(); + } catch (SQLException e) { + log.info("数据库查询异常方法{},异常信息{}","com.suisung.mall.shop.sixun.dao.BaseDao.baseFindList",e.getMessage()); + throw new RuntimeException(e); + } + resultDto.setResultSet(rs); + resultDto.setConnection(connection); + return resultDto; + + } + + /** + * 带分页数据 + * @param ip + * @param username + * @param password + * @param dataBaseName + * @param table + * @return + */ + public ResultDto baseFindListPage(String ip, String username, String password,Integer portNumber, String dataBaseName, String table,String orderColumn, int pageNo, int pageSize,String where){ + Connection connection=getConnection(ip,username,password,portNumber,dataBaseName); + int start=(pageNo-1)*pageSize+1; + int end=pageNo*pageSize; + String sql=" select * from( " + + " select ROW_NUMBER() OVER(ORDER BY %s) as rowId,* from %s %s" + + " ) as r where rowId between %s and %s"; + sql=String.format(sql, orderColumn,table,where,start,end); + log.info(sql); + ResultDto resultDto=new ResultDto(); + ResultSet rs=null; + try { + PreparedStatement ps= connection.prepareStatement(sql); + rs = ps.executeQuery(); + } catch (SQLException e) { + log.info("数据库查询异常方法{},异常信息{}","com.suisung.mall.shop.sixun.dao.BaseDao.baseFindListPage",e.getMessage()); + throw new RuntimeException(e); + } + resultDto.setResultSet(rs); + resultDto.setConnection(connection); + return resultDto; + } + + public Integer getBaseTotal(String ip, String username, String password,Integer portNumber, String dataBaseName, String table,String where){ + int total=0; + Connection connection=getConnection(ip,username,password,portNumber,dataBaseName); + try { + String sql="select count(*) from %s %s"; + sql=String.format(sql, table,where); + log.info(sql); + PreparedStatement ps= connection.prepareStatement(sql); + ResultSet rs=ps.executeQuery(); + while (rs.next()){ + total=rs.getInt(1); + } + } catch (SQLException e) { + log.info("数据库查询异常方法{},异常信息{}","com.suisung.mall.shop.sixun.dao.BaseDao.getBaseTotal",e.getMessage()); + throw new RuntimeException(e); + } + finally { + try { + connection.close(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + return total; + } + + + public Integer getBaseJoinTotal(String ip, String username, String password,Integer portNumber, String dataBaseName, String table,String joinTable,String onLeft,String onRight,String orderColumn,String rightSelect,String where){ + int total=0; + Connection connection=getConnection(ip,username,password,portNumber,dataBaseName); + try { + String sql=" select COUNT(*) from( " + + " select ROW_NUMBER() OVER(ORDER BY b.%s) as rowId,b.*,%s from %s b left join %s t on b.%s=t.%s %s" + + " ) r"; + sql=String.format(sql, orderColumn,rightSelect,table,joinTable,onLeft,onRight,where); + log.info(sql); + PreparedStatement ps= connection.prepareStatement(sql); + ResultSet rs=ps.executeQuery(); + while (rs.next()){ + total=rs.getInt(1); + } + } catch (SQLException e) { + log.info("数据库查询异常方法{},异常信息{}","com.suisung.mall.shop.sixun.dao.BaseDao.getBaseTotal",e.getMessage()); + throw new RuntimeException(e); + } + finally { + try { + connection.close(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + return total; + } + + + /** + * 带分页数据关联分页查询 + * @param ip + * @param username + * @param password + * @param dataBaseName + * @param table + * @return + */ + public ResultDto baseFindListJoinPage(String ip, String username, String password,Integer portNumber, String dataBaseName, String table,String joinTable,String onLeft,String onRight,String orderColumn,String rightSelect, int pageNo, int pageSize,String where){ + Connection connection=getConnection(ip,username,password,portNumber,dataBaseName); + int start=(pageNo-1)*pageSize+1; + int end=pageNo*pageSize; + String sql=" select * from( " + + " select ROW_NUMBER() OVER(ORDER BY b.%s) as rowId,b.*,%s from %s b left join %s t on b.%s=t.%s %s" + + " ) as r where rowId between %s and %s"; + sql=String.format(sql, orderColumn,rightSelect,table,joinTable,onLeft,onRight,where,start,end); + log.info(sql); + ResultDto resultDto=new ResultDto(); + ResultSet rs=null; + try { + PreparedStatement ps= connection.prepareStatement(sql); + rs = ps.executeQuery(); + } catch (SQLException e) { + log.info("数据库查询异常方法{},异常信息{}","com.suisung.mall.shop.sixun.dao.BaseDao.baseFindListJoinPage",e.getMessage()); + throw new RuntimeException(e); + } + resultDto.setResultSet(rs); + resultDto.setConnection(connection); + return resultDto; + } + + /** + * + * @param ip + * @param username + * @param password + * @param dataBaseName + * @param table + * @param joinTable + * @param onLeft + * @param onRight + * @param rightSelect + * @param where + * @return + */ + public ResultDto baseFindListJoin(String ip, String username, String password,Integer portNumber, String dataBaseName, String table,String joinTable,String onLeft,String onRight,String rightSelect, String where){ + Connection connection=getConnection(ip,username,password,portNumber,dataBaseName); + String sql= "select b.*,%s from %s b left join %s t on b.%s=t.%s %s " ; + sql=String.format(sql,rightSelect,table,joinTable,onLeft,onRight,where); + log.info(sql); + ResultDto resultDto=new ResultDto(); + ResultSet rs=null; + try { + PreparedStatement ps= connection.prepareStatement(sql); + rs = ps.executeQuery(); + } catch (SQLException e) { + log.info("数据库查询异常方法{},异常信息{}","com.suisung.mall.shop.sixun.dao.BaseDao.baseFindListJoin",e.getMessage()); + throw new RuntimeException(e); + } + resultDto.setResultSet(rs); + resultDto.setConnection(connection); + return resultDto; + } + +} diff --git a/client/src/main/java/com/small/client/dao/SxDataDao.java b/client/src/main/java/com/small/client/dao/SxDataDao.java new file mode 100644 index 00000000..85fc645e --- /dev/null +++ b/client/src/main/java/com/small/client/dao/SxDataDao.java @@ -0,0 +1,448 @@ +package com.small.client.dao; + + +import com.small.client.dto.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.nio.charset.Charset; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * 考虑到每个思迅软件都是自己的数据,所以采用动态获取的方式获取数据 + * 数据库为MS SQL + * todo 如果考虑到数据量需要分页多线程 + */ +@Service +@Slf4j +public class SxDataDao extends BaseDao{ + + private final static String T_BD_ITEM_CLS="t_bd_item_cls";//商品分类 + private final static String T_BD_ITEM_INFO="t_bd_item_info";//商品表 + private final static String T_RM_VIP_INFO="t_rm_vip_info";//会员表 + + private final static String ITEM_CLSNO="item_clsno";//商品分类排序字段 + private final static String ITEM_NO="item_no";//商品排序字段 + private final static String CARD_ID="card_id";//会员表排序字段 + + + private final static String T_BD_BASE_CODE="t_bd_base_code";//品牌表 + private final static String T_BD_BASECODE_TYPE="t_bd_basecode_type";//品牌表 + private final static String TYPE_NO="type_no";//品牌排序字段 + + private final static String T_IM_BRANCH_STOCK="t_im_branch_stock";//库存表 + + private final static String T_RM_SPEC_PRICE="t_rm_spec_price";//活动表 + + public final static Integer PAGESIZE=500; + + public final static String DEFALTWHERE="where 1=1"; + + public final static String DEFAULT_IMG="https://digitalassets.tesla.com/tesla-contents/image/upload/f_auto,q_auto/Homepage-Model-Y-2-Promo-Hero-Tablet-CN.png"; + + + /** + * 查找商品分类数据 + * @param dataBaseInfo + */ + public List findTBdItemClsList(DataBaseInfo dataBaseInfo){ + ResultDto resultDto=baseFindList(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName(),T_BD_ITEM_CLS,DEFALTWHERE); + ResultSet rs= resultDto.getResultSet(); + List sxSyncCategories=new ArrayList<>(); + SxSyncCategory sxSyncCategory=null; + try { + while (rs.next()) { + sxSyncCategory=new SxSyncCategory(); + sxSyncCategory.setItem_clsname(rs.getString("item_clsname").trim());//分类名称 + if(null!=rs.getString("cls_parent")){ + sxSyncCategory.setCls_parent(rs.getString("cls_parent").trim());//父级编码 + } + sxSyncCategory.setItem_clsno(rs.getString("item_clsno").trim());//分类编码 +// System.out.printf(rs.getString("item_clsno"));//分类编码 +// System.out.printf(rs.getString("item_clsname")+"\t");//分类名称 +// System.out.print(rs.getString("cls_parent")+"\t");//父级编码 +// System.out.print(rs.getString("item_flag")+"\t" + "\n");//显示标识 + sxSyncCategories.add(sxSyncCategory); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } finally { + try { + resultDto.getConnection().close(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + return sxSyncCategories; + } + + /** + * 分页查找商品分类数据 + * @param dataBaseInfo + * @param pageNo + * @param pageSize + */ + public List findTBdItemClsListPage(DataBaseInfo dataBaseInfo, int pageNo, int pageSize){ + ResultDto resultDto=baseFindListPage(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName(),T_BD_ITEM_CLS,ITEM_CLSNO,pageNo,pageSize,DEFALTWHERE); + ResultSet rs= resultDto.getResultSet(); + List sxSyncCategories=new ArrayList<>(); + SxSyncCategory sxSyncCategory=null; + try { + while (rs.next()) { + sxSyncCategory=new SxSyncCategory(); + sxSyncCategory.setItem_clsname(rs.getString("item_clsname"));//分类名称 + sxSyncCategory.setCls_parent(rs.getString("cls_parent"));//父级编码 + sxSyncCategory.setItem_clsno(rs.getString("item_clsno"));//分类编码 +// System.out.printf(rs.getString("item_clsno"));//分类编码 + // log.info(rs.getString("item_clsname")+"\t");//分类名称 + //log.info(rs.getString("cls_parent")+"\t");//父级编码 + //log.info(rs.getString("item_flag")+"\t" + "\n");//显示标识 + sxSyncCategories.add(sxSyncCategory); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } finally { + try { + resultDto.getConnection().close(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + return sxSyncCategories; + } + + /** + * 获取商品分类TBdItemCls表的数量 + * @param dataBaseInfo + * @return + */ + public Integer getTBdItemClsTotal(DataBaseInfo dataBaseInfo){ + return getBaseTotal(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName(),T_BD_ITEM_CLS,DEFALTWHERE); + } + + /** + * 获取商品表t_bd_item_info表的数量 + * @param dataBaseInfo + * @return + */ + public int getTBditemInfoTotal(DataBaseInfo dataBaseInfo){ +// String where =DEFALTWHERE; +// if(syncGoodsSearchModel!=null){ +// if(dataBaseInfo.getWhere()!=null){ +// where+= dataBaseInfo.getWhere(); +// } +// } + return getBaseTotal(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName(),T_BD_ITEM_INFO,dataBaseInfo.getWhere()); + } + + /** + * 获取商品表t_bd_item_info表的数量 + * @param dataBaseInfo + * @return + */ + public int getTBditemInfoJoninTotal(DataBaseInfo dataBaseInfo){ + return getBaseJoinTotal(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName() + , T_BD_ITEM_INFO + ,T_IM_BRANCH_STOCK + ,"item_no" + ,"item_no" + ,ITEM_CLSNO + ,"t.stock_qty,t.oper_date" + ,dataBaseInfo.getWhere()==null?DEFALTWHERE:dataBaseInfo.getWhere()); + } + + /** + * 获取会员表t_rm_vip_info表的数量 + * @param dataBaseInfo + * @return + */ + public int getTrmVipInfoTotal(DataBaseInfo dataBaseInfo){ + return getBaseTotal(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName(),T_RM_VIP_INFO,dataBaseInfo.getWhere()); + } + + + /** + * 分页查找商品数据 + * 表T_BD_ITEM_INFO + * @param dataBaseInfo + * @param pageNo + * @param pageSize + */ + public List findBditemInfoListPage(DataBaseInfo dataBaseInfo,int pageNo,int pageSize){ + ResultDto resultDto=baseFindListJoinPage(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName() + , T_BD_ITEM_INFO + ,T_IM_BRANCH_STOCK + ,"item_no" + ,"item_no" + ,ITEM_CLSNO + ,"t.stock_qty,t.oper_date" + ,pageNo,pageSize,dataBaseInfo.getWhere()==null?DEFALTWHERE:dataBaseInfo.getWhere()); + ResultSet rs= resultDto.getResultSet(); + List sxSyncGoodses=new ArrayList<>(); + SxSyncGoods sxSyncGoods=null; + try { + while (rs.next()) { + sxSyncGoods=new SxSyncGoods(); + BigDecimal price=new BigDecimal(rs.getString("price")); + BigDecimal salePrice=new BigDecimal(rs.getString("sale_price")); + if(salePrice.compareTo(new BigDecimal("0"))>0){ + try{ + BigDecimal gross= salePrice.subtract(price).divide(salePrice,4, RoundingMode.HALF_UP); + sxSyncGoods.setGross_margin(gross);//毛利率 + }catch (RuntimeException e){ + log.info("运行错误:{}",e.getMessage()); + log.info(String.valueOf(rs.getString("sale_price"))); + } + + }else { + sxSyncGoods.setGross_margin(new BigDecimal("0"));//毛利率 + } + sxSyncGoods.setItem_no(rs.getString("item_no"));//货号 + sxSyncGoods.setItem_subname(rs.getString("item_subname"));//商品名称 + sxSyncGoods.setItem_subno(rs.getString("item_subno"));//商品条码 + + sxSyncGoods.setBig_cls_name("9999");//商品大类 todo 如何关联 + sxSyncGoods.setSmall_cls_name(rs.getString("item_clsno").trim());//商品小类 todo 如何关联 + + sxSyncGoods.setItem_size(rs.getString("item_size"));//规格 + sxSyncGoods.setUnit_no(rs.getString("unit_no"));//单位 todo + sxSyncGoods.setStock(rs.getBigDecimal("stock_qty"));//库存数量 todo item_stock? + + sxSyncGoods.setPrice(rs.getBigDecimal("price"));//进货价 + sxSyncGoods.setSale_price(rs.getBigDecimal("sale_price"));//零售价 + + sxSyncGoods.setVip_price(rs.getBigDecimal("vip_price"));//会员价 + sxSyncGoods.setVip_acc_flag(rs.getBigDecimal("vip_acc_flag"));//允许积分 + sxSyncGoods.setVip_acc_num(rs.getBigDecimal("vip_acc_num"));//积分值 + sxSyncGoods.setSale_flag(rs.getInt("main_Sale_flag"));//商品状态 todo 是main_Sale_flag? + sxSyncGoods.setItem_rem(rs.getString("item_rem"));//助记码 + sxSyncGoods.setBuild_date(rs.getString("build_date"));//生产日期 todo + sxSyncGoods.setValid_days(getStopDate(rs));//保质期 todo stop_date-build_date? + + sxSyncGoods.setItem_brand_name(rs.getString("item_brandname")); + sxSyncGoods.setItemBrand(rs.getString("item_brand")); + sxSyncGoods.setItem_clsno(rs.getString("item_clsno").trim()); + sxSyncGoods.setItem_size(rs.getString("item_size")); + + sxSyncGoodses.add(sxSyncGoods); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } finally { + try { + resultDto.getConnection().close(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + return sxSyncGoodses; + } + + private static String getStopDate(ResultSet rs) throws SQLException { + return rs.getString("stop_date"); + } + + + /** + * 分页查找会员数据 + * RM_VIP_INFO + * @param dataBaseInfo + * @param pageNo + * @param pageSize + */ + public List findRmVipInfoListPage(DataBaseInfo dataBaseInfo, int pageNo, int pageSize){ + ResultDto resultDto=baseFindListPage(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName(),T_RM_VIP_INFO,CARD_ID,pageNo,pageSize,dataBaseInfo.getWhere()); + ResultSet rs= resultDto.getResultSet(); + List sxSyncVips=new ArrayList<>(); + SxSyncVip sxSyncVip=null; + try { + while (rs.next()) { + sxSyncVip = new SxSyncVip(); + int cardStatus=rs.getInt("card_status"); + if(cardStatus!=1){ + sxSyncVip.setVip_name(rs.getString("vip_name"));//会员名称 + sxSyncVip.setVip_sex(rs.getString("vip_sex"));//会员名称 + sxSyncVip.setMobile(rs.getString("mobile"));//会员名称 + sxSyncVip.setBirthday(rs.getString("birthday"));//会员生日 + sxSyncVip.setCard_type(rs.getString("card_type")==null?"v1":rs.getString("card_type"));//会员生日 + sxSyncVip.setCard_no(rs.getString("card_no"));//会员卡号 + sxSyncVip.setCard_no(rs.getString("now_acc_num"));//会员积分 + sxSyncVip.setCard_no(rs.getString("residual_amt"));//储值余额 + sxSyncVip.setCard_no(rs.getString("vip_start_date"));//建档日期 + + log.info(rs.getString("vip_name"));//会员名称 + log.info(rs.getString("mobile"));//会员手机号 + log.info(rs.getString("vip_sex"));//会员性别 + log.info(rs.getString("birthday"));//会员生日 + log.info(rs.getString("card_no"));//会员卡号 + log.info(rs.getString("card_type"));//会员等级 + log.info("{}",rs.getBigDecimal("residual_amt"));//储值余额 + log.info("{}",rs.getBigDecimal("now_acc_num"));//会员积分 + log.info(rs.getString("vip_start_date"));//建档日期 + log.info("{}",rs.getInt("card_status"));//会员状态 + } + + sxSyncVips.add(sxSyncVip); + } + }catch (SQLException e){ + throw new RuntimeException(e); + } + return sxSyncVips; + } + + /** + * 获取品牌数据 + * @param dataBaseInfo + */ + public List getBdBrandList(DataBaseInfo dataBaseInfo) { + String where="where t.type_name='品牌'"; + ResultDto resultDto=baseFindListJoin(dataBaseInfo.getIp() + ,dataBaseInfo.getUserName() + ,dataBaseInfo.getPassword() + ,dataBaseInfo.getDbPort() + ,dataBaseInfo.getDataBaseName() + ,T_BD_BASE_CODE + ,"t_bd_basecode_type" + ,"type_no" + ,"type_no" + ,"t.type_name" + ,where); + ResultSet rs= resultDto.getResultSet(); + List brandModels=new ArrayList<>(); + try { + while (rs.next()) { + BrandModel brandModel=new BrandModel(); + brandModel.setBrand_name(rs.getString("code_name")); + brandModel.setBrand_desc(rs.getString("code_name")); + brandModel.setCodeId(rs.getString("code_id")); + brandModel.setBrand_image(""); + brandModel.setCategory("0"); + brandModel.setBrand_recommend("0");//是否推荐 + brandModels.add(brandModel); + // log.info(rs.getString("type_no")+"--"+rs.getString("code_name"));//分类编码-分类名称 + } + } catch (SQLException e) { + throw new RuntimeException(e); + } finally { + try { + resultDto.getConnection().close(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + return brandModels; + } + + /** + * 获取库存数据 + * @return + */ + public List getImBranchStockList(DataBaseInfo dataBaseInfo,String where){ + ResultDto resultDto=baseFindList(dataBaseInfo.getIp() + ,dataBaseInfo.getUserName() + ,dataBaseInfo.getPassword() + ,dataBaseInfo.getDbPort() + ,dataBaseInfo.getDataBaseName() + ,T_IM_BRANCH_STOCK + ,where); + ResultSet rs= resultDto.getResultSet(); + List branchStocks=new ArrayList<>(); + try { + while (rs.next()) { + ImBranchStock brandModel=new ImBranchStock(); + brandModel.setBranchNo(rs.getString("branch_no")); + brandModel.setStockQty(rs.getBigDecimal("stock_qty")); + brandModel.setItemNo(rs.getString("item_no")); + brandModel.setPerDate(rs.getString("oper_date")); + branchStocks.add(brandModel); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } finally { + try { + resultDto.getConnection().close(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + return branchStocks; + } + + /** + *获取促销活动价格 时段特价单 + * @param dataBaseInfo + * @return + */ + public List getSpecPriceList(DataBaseInfo dataBaseInfo){ + ResultDto resultDto=baseFindList(dataBaseInfo.getIp() + ,dataBaseInfo.getUserName() + ,dataBaseInfo.getPassword() + ,dataBaseInfo.getDbPort() + ,dataBaseInfo.getDataBaseName() + ,"T_RM_SPEC_PRICE" + ,"where special_type ='0'"); + ResultSet rs= resultDto.getResultSet(); + List specPriceDtos=new ArrayList<>(); + try { + while (rs.next()) { + SpecPriceDto specPriceDto=new SpecPriceDto(); + specPriceDto.setItemNo(rs.getString("item_no"));// + specPriceDto.setOldPrice(rs.getBigDecimal("old_price"));//原价 + specPriceDto.setSpecPrice(rs.getBigDecimal("spe_price"));//特价 + specPriceDto.setSpecPrice(rs.getBigDecimal("sale_qty"));//限购 + specPriceDto.setDiscountType("0"); + specPriceDtos.add(specPriceDto); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } finally { + try { + resultDto.getConnection().close(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + return specPriceDtos; + } + + /** + * 折扣商品 + * @param dataBaseInfo + * @return + */ + public List getDiscountPriceList(DataBaseInfo dataBaseInfo){ + ResultDto resultDto=baseFindList(dataBaseInfo.getIp() + ,dataBaseInfo.getUserName() + ,dataBaseInfo.getPassword() + ,dataBaseInfo.getDbPort() + ,dataBaseInfo.getDataBaseName() + ,T_RM_SPEC_PRICE + ,"where special_type in('6','G')"); + ResultSet rs= resultDto.getResultSet(); + List specPriceDtos=new ArrayList<>(); + try { + while (rs.next()) { + SpecPriceDto specPriceDto=new SpecPriceDto(); + specPriceDto.setItemNo(rs.getString("item_no"));// + specPriceDto.setDiscount(rs.getBigDecimal("discount"));//原价 + specPriceDto.setDiscountType("1"); + specPriceDtos.add(specPriceDto); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } finally { + try { + resultDto.getConnection().close(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + return specPriceDtos; + } + +} diff --git a/client/src/main/java/com/small/client/dto/BrandModel.java b/client/src/main/java/com/small/client/dto/BrandModel.java new file mode 100644 index 00000000..dbfaf276 --- /dev/null +++ b/client/src/main/java/com/small/client/dto/BrandModel.java @@ -0,0 +1,27 @@ +package com.small.client.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +@Data +public class BrandModel implements Serializable { + @ApiModelProperty("品牌名称") + private String brand_name; + + @ApiModelProperty("品牌编号") + private String codeId; + + @ApiModelProperty("品牌描述") + private String brand_desc; + + @ApiModelProperty("品牌分类") + private String category; + + @ApiModelProperty("图片") + private String brand_image; + + @ApiModelProperty("是否推荐") + private String brand_recommend; +} diff --git a/client/src/main/java/com/small/client/dto/CommentModel.java b/client/src/main/java/com/small/client/dto/CommentModel.java new file mode 100644 index 00000000..2b39159e --- /dev/null +++ b/client/src/main/java/com/small/client/dto/CommentModel.java @@ -0,0 +1,17 @@ +package com.small.client.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class CommentModel { + @ApiModelProperty("店铺的key") + private String appKey; + @ApiModelProperty("店铺的密钥") + private String sign; + @ApiModelProperty("店铺的id") + private String storeId; + + @ApiModelProperty(value = "同步时间-用于增量同步,大于这个时间证明是增量") + private String syncTime;//同步时间 +} diff --git a/client/src/main/java/com/small/client/dto/DataBaseInfo.java b/client/src/main/java/com/small/client/dto/DataBaseInfo.java new file mode 100644 index 00000000..422ff5de --- /dev/null +++ b/client/src/main/java/com/small/client/dto/DataBaseInfo.java @@ -0,0 +1,33 @@ +package com.small.client.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class DataBaseInfo { + @ApiModelProperty("数据库IP") + private String ip; + @ApiModelProperty("用户名") + private String userName; + @ApiModelProperty("密码") + private String password; + @ApiModelProperty("数据库名称") + private String dataBaseName; + @ApiModelProperty("条件") + private String where; + @ApiModelProperty(value = "数据库端口") + private Integer dbPort; + @ApiModelProperty(value = "定时同步的cron表达式") + private String cronExpression; + @ApiModelProperty(value = "同步模式(1:定时同步,2:间隔同步)") + private String syncMode; + + @ApiModelProperty(value = "商品分类") + private String categoryName; + + @ApiModelProperty(value = "01全量,02增量") + private String syncType; + + @ApiModelProperty(value = "操作时间") + private String operDate; +} diff --git a/client/src/main/java/com/small/client/dto/ImBranchStock.java b/client/src/main/java/com/small/client/dto/ImBranchStock.java new file mode 100644 index 00000000..836a55ac --- /dev/null +++ b/client/src/main/java/com/small/client/dto/ImBranchStock.java @@ -0,0 +1,18 @@ +package com.small.client.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class ImBranchStock { + @ApiModelProperty("商品编号") + private String itemNo; + @ApiModelProperty("商标编号") + private String branchNo; + @ApiModelProperty("库存") + private BigDecimal stockQty; + @ApiModelProperty("操作时间") + private String perDate; +} diff --git a/client/src/main/java/com/small/client/dto/ProductImage.java b/client/src/main/java/com/small/client/dto/ProductImage.java new file mode 100644 index 00000000..ff28ae69 --- /dev/null +++ b/client/src/main/java/com/small/client/dto/ProductImage.java @@ -0,0 +1,17 @@ +package com.small.client.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class ProductImage { + @ApiModelProperty("图片路径") + private String image_url; + @ApiModelProperty("推荐值") + private String seq; + @ApiModelProperty("介绍") + private String desc; + @ApiModelProperty("是否默认") + private String is_default; + +} diff --git a/client/src/main/java/com/small/client/dto/PromotionDetail.java b/client/src/main/java/com/small/client/dto/PromotionDetail.java new file mode 100644 index 00000000..bad64429 --- /dev/null +++ b/client/src/main/java/com/small/client/dto/PromotionDetail.java @@ -0,0 +1,17 @@ +package com.small.client.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class PromotionDetail { + + @ApiModelProperty("活动id") + private String activity_id; + @ApiModelProperty("活动名称") + private String activity_name; + @ApiModelProperty("价格") + private String price; + @ApiModelProperty("介绍") + private String intro; +} diff --git a/client/src/main/java/com/small/client/dto/ResultDto.java b/client/src/main/java/com/small/client/dto/ResultDto.java new file mode 100644 index 00000000..a5cd2057 --- /dev/null +++ b/client/src/main/java/com/small/client/dto/ResultDto.java @@ -0,0 +1,15 @@ +package com.small.client.dto; + +import lombok.Data; + +import java.sql.Connection; +import java.sql.ResultSet; + +@Data +public class ResultDto { + + private Connection connection; + + private ResultSet resultSet; + +} diff --git a/client/src/main/java/com/small/client/dto/SpecPriceDto.java b/client/src/main/java/com/small/client/dto/SpecPriceDto.java new file mode 100644 index 00000000..e03dd15b --- /dev/null +++ b/client/src/main/java/com/small/client/dto/SpecPriceDto.java @@ -0,0 +1,19 @@ +package com.small.client.dto; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class SpecPriceDto { + + private String itemNo; + private BigDecimal oldPrice;//原价 + private BigDecimal specPrice;//折后价 + + private BigDecimal discount;//折扣('6','G') + + private Integer saleQty;//限购 + + private String discountType;//折扣类型 0-特价,1打折 +} diff --git a/client/src/main/java/com/small/client/dto/StoreDbConfig.java b/client/src/main/java/com/small/client/dto/StoreDbConfig.java new file mode 100644 index 00000000..1678039a --- /dev/null +++ b/client/src/main/java/com/small/client/dto/StoreDbConfig.java @@ -0,0 +1,64 @@ +package com.small.client.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +@Data +public class StoreDbConfig implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "主键ID") + private Long id; + + @ApiModelProperty(value = "店铺ID") + private String storeId; + + @ApiModelProperty(value = "数据库类型(mysql/oracle/sqlserver等)") + private String dbType = "sqlserver"; + + @ApiModelProperty(value = "数据库名称") + private String dbName; + + @ApiModelProperty(value = "数据库IP地址") + private String dbIp; + + @ApiModelProperty(value = "数据库端口") + private Integer dbPort; + + @ApiModelProperty(value = "数据库用户名") + private String dbUsername; + + @ApiModelProperty(value = "数据库密码(建议加密存储)") + private String dbPassword; + + @ApiModelProperty(value = "是否有外网访问(0:无,1:有)") + private String hasInternet; + + @ApiModelProperty(value = "同步模式(1:定时同步,2:间隔同步)") + private String syncMode; + + @ApiModelProperty(value = "是否启用(0:否,1:是)") + private String hasStart; + + @ApiModelProperty(value = "定时同步的cron表达式") + private String cronExpression; + + @ApiModelProperty(value = "商品分类") + private String categoryName; + + @ApiModelProperty(value = "创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @ApiModelProperty(value = "更新时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + @ApiModelProperty(value = "备注信息") + private String remark; +} diff --git a/client/src/main/java/com/small/client/dto/SxCategoryModel.java b/client/src/main/java/com/small/client/dto/SxCategoryModel.java new file mode 100644 index 00000000..168e5b9b --- /dev/null +++ b/client/src/main/java/com/small/client/dto/SxCategoryModel.java @@ -0,0 +1,32 @@ +package com.small.client.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 模型对应 + */ +@Data +public class SxCategoryModel { + //模型对应ShopBaseProductCategory start + private String parent_name;//暂时不用 + @ApiModelProperty(value = "商品分类名称") + private String category_name; + @ApiModelProperty(value = "分类图片") + private String category_image; + @ApiModelProperty(value = "是否允许虚拟商品(ENUM):1-是; 0-否") + private Integer category_virtual_enable; + @ApiModelProperty(value = "分佣比例-百分比") + private BigDecimal category_commission_rate; + // private String type_name;//todo 看代码没有用,使用的是product_type + //模型对应ShopBaseProductCategory end + @ApiModelProperty(value = "产品类型") + private String product_type; + @ApiModelProperty(value = "第一级父类") + private String first_category_name; + @ApiModelProperty(value = "第二级父类") + private String second_category_name; + +} diff --git a/client/src/main/java/com/small/client/dto/SxGoosModel.java b/client/src/main/java/com/small/client/dto/SxGoosModel.java new file mode 100644 index 00000000..ed570893 --- /dev/null +++ b/client/src/main/java/com/small/client/dto/SxGoosModel.java @@ -0,0 +1,103 @@ +package com.small.client.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +/** + * 思迅同步商品数据入口数据 + */ +@Data +public class SxGoosModel { + + @ApiModelProperty("商品名称") + private String product_name; + + @ApiModelProperty("商品货号") + private String product_number; + + @ApiModelProperty("商品条形码") + private String product_barcode; + + @ApiModelProperty("一级分类") + private String first_category_name; + + @ApiModelProperty("二级分类") + private String second_category_name; + + @ApiModelProperty("三级分类") + private String three_category_name; + + @ApiModelProperty("产品类型") + private String product_type; + + @ApiModelProperty("商品种类") + private String product_kind; + + @ApiModelProperty("成本价") + private BigDecimal cost_price; + + @ApiModelProperty("零售价") + private BigDecimal price; + + @ApiModelProperty("原价") + private BigDecimal original_price; + + @ApiModelProperty("商品价格") + private BigDecimal retail_price; + + @ApiModelProperty("会员价") + private BigDecimal member_price; + + @ApiModelProperty("库存") + private BigDecimal stock; + + @ApiModelProperty("毛利率") + private BigDecimal gross_margin; + + @ApiModelProperty("规格单位") + private String unit; + + @ApiModelProperty("可用积分") + private BigDecimal can_piont; + + @ApiModelProperty("总积分") + private BigDecimal points; + + @ApiModelProperty("助记码") + private String mnemonic; + + + @ApiModelProperty("最大购买商品量") + private Integer buy_limit; + + @ApiModelProperty("品牌名称") + private String brand_name; + + @ApiModelProperty("标签") + private String tags; + + @ApiModelProperty("辅助属性") + private List product_assist; + + @ApiModelProperty("规格(JSON)-规格、规格值、goods_id 规格不需要全选就可以添加对应数据[") + private List product_spec; + + @ApiModelProperty("商品卖点特征") + private String product_value; + + @ApiModelProperty("商品视频") + private String product_video; + + @ApiModelProperty("产品描述") + private String product_desc; + + @ApiModelProperty("商品图片库") + private List product_images; + + + @ApiModelProperty("商品详情") + private List promotion_detail; +} diff --git a/client/src/main/java/com/small/client/dto/SxSyncCategory.java b/client/src/main/java/com/small/client/dto/SxSyncCategory.java new file mode 100644 index 00000000..40e24a9d --- /dev/null +++ b/client/src/main/java/com/small/client/dto/SxSyncCategory.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2024. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.small.client.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * 思迅同步的商品分类表 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode +@ApiModel(value = "商品分类表", description = "商品分类表") +public class SxSyncCategory implements Serializable { + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "自增ID") + private Long id; + + @ApiModelProperty(value = "店铺Id") + private String store_id; + + @ApiModelProperty(value = "分类编码") + private String item_clsno; + + @ApiModelProperty(value = "分类名称") + private String item_clsname; + + @ApiModelProperty(value = "父类编号") + private String cls_parent; + + @ApiModelProperty(value = "分类排序") + private Integer item_order; + + @ApiModelProperty(value = "是否显示:1-前台显示 0-前台不显示") + private Integer display_flag; + + @ApiModelProperty(value = "状态") + private Integer status; + + @ApiModelProperty(value = "同步时间戳") + private Long sync_time; + + @ApiModelProperty(value = "新增时间") + private Date created_at; + + @ApiModelProperty(value = "更新时间") + private Date updated_at; + + @ApiModelProperty(value = "子节点") + List children; +} diff --git a/client/src/main/java/com/small/client/dto/SxSyncGoods.java b/client/src/main/java/com/small/client/dto/SxSyncGoods.java new file mode 100644 index 00000000..ff437912 --- /dev/null +++ b/client/src/main/java/com/small/client/dto/SxSyncGoods.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2024. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.small.client.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 思迅同步的商品表 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode +@ApiModel(value = "商品信息表", description = "商品信息表") +public class SxSyncGoods implements Serializable { + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "自增ID") + private Long id; + + @ApiModelProperty(value = "店铺Id") + private String store_id; + + @ApiModelProperty(value = "商品编号") + private String item_no; + + @ApiModelProperty(value = "商品名称") + private String item_subname; + + @ApiModelProperty(value = "商品条码") + private String item_subno; + + @ApiModelProperty(value = "小分类编号") + private String item_clsno; + + @ApiModelProperty(value = "大分类名称") + private String big_cls_name; + + @ApiModelProperty(value = "小分类名称") + private String small_cls_name; + + @ApiModelProperty(value = "规格") + private String item_size; + + @ApiModelProperty(value = "单位") + private String unit_no; + + @ApiModelProperty(value = "库存") + private BigDecimal stock; + + @ApiModelProperty(value = "毛利率") + private BigDecimal gross_margin; + + @ApiModelProperty(value = "进货价") + private BigDecimal price; + + @ApiModelProperty(value = "零售价") + private BigDecimal sale_price; + + @ApiModelProperty(value = "会员价") + private BigDecimal vip_price; + + @ApiModelProperty(value = "允许积分") + private BigDecimal vip_acc_flag; + + @ApiModelProperty(value = "积分值") + private BigDecimal vip_acc_num; + + @ApiModelProperty(value = "商品状态") + private Integer sale_flag; + + @ApiModelProperty(value = "品牌名称") + private String item_brand_name; + + @ApiModelProperty(value = "商品助记号") + private String item_rem; + + @ApiModelProperty(value = "生产日期") + private String build_date; + + @ApiModelProperty(value = "质保期") + private String valid_days; + + @ApiModelProperty(value = "状态") + private Integer status; + + @ApiModelProperty(value = "同步时间戳") + private Long sync_time; + + @ApiModelProperty(value = "同步备注") + private String remark; + + @ApiModelProperty(value = "新增时间") + private Date created_at; + + @ApiModelProperty(value = "更新时间") + private Date updated_at; + + @ApiModelProperty(value = "品牌") + private String itemBrand; + +} + diff --git a/client/src/main/java/com/small/client/dto/SxSyncVip.java b/client/src/main/java/com/small/client/dto/SxSyncVip.java new file mode 100644 index 00000000..ff2d1fb4 --- /dev/null +++ b/client/src/main/java/com/small/client/dto/SxSyncVip.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.small.client.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 思迅同步的商品表 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode +@ApiModel(value = "会员表", description = "会员表") +public class SxSyncVip implements Serializable { + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "自增ID") + private Long id; + + @ApiModelProperty(value = "店铺Id") + private String store_id; + + @ApiModelProperty(value = "会员名称") + private String vip_name; + + @ApiModelProperty(value = "手机号") + private String mobile; + + @ApiModelProperty(value = "会员性别") + private String vip_sex; + + @ApiModelProperty(value = "会员生日") + private String birthday; + + @ApiModelProperty(value = "会员卡号(唯一键)") + private String card_no; + + @ApiModelProperty(value = "会员等级") + private String card_type; + + @ApiModelProperty(value = "储值余额") + private BigDecimal residual_amt; + + @ApiModelProperty(value = "会员积分") + private BigDecimal now_acc_num; + + @ApiModelProperty(value = "加入时间") + private String vip_date; + + @ApiModelProperty(value = "会员状态") + private Integer card_status; + + @ApiModelProperty(value = "同步时间") + private Long sync_time; + + @ApiModelProperty(value = "状态") + private Integer status; + + @ApiModelProperty(value = "新增时间") + private Date created_at; + + @ApiModelProperty(value = "更新时间") + private Date updated_at; +} diff --git a/client/src/main/java/com/small/client/dto/SyncGoodsSearchModel.java b/client/src/main/java/com/small/client/dto/SyncGoodsSearchModel.java new file mode 100644 index 00000000..67f03145 --- /dev/null +++ b/client/src/main/java/com/small/client/dto/SyncGoodsSearchModel.java @@ -0,0 +1,22 @@ +package com.small.client.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +@Data +public class SyncGoodsSearchModel implements Serializable { + + @ApiModelProperty(value = "分类名称") + private String categoryName;//分类名称 + + @ApiModelProperty(value = "同步分类编号") + private String itemClsno; + + @ApiModelProperty(value = "操作时间") + private String operDate; + + @ApiModelProperty(value = "01全量,02增量") + private String syncType; +} diff --git a/client/src/main/java/com/small/client/dto/SyncThirdMemberReq.java b/client/src/main/java/com/small/client/dto/SyncThirdMemberReq.java new file mode 100644 index 00000000..41e500cd --- /dev/null +++ b/client/src/main/java/com/small/client/dto/SyncThirdMemberReq.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.small.client.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +@ApiModel(description = "第三方会员同步请求参数") +public class SyncThirdMemberReq{ + private static final long serialVersionUID = 1L; + + @ApiModelProperty("会员手机号") + private String user_mobile; + @ApiModelProperty("会员昵称") + private String user_nickname; + @ApiModelProperty("会员真实姓名") + private String user_realname; + @ApiModelProperty("会员性别:1-男;2-女;") + private Integer user_gender; + @ApiModelProperty("会员生日 yyyy-MM-dd") + private String user_birthday; + @ApiModelProperty("会员等级:v1...v9") + private String user_level; + @ApiModelProperty("会员卡号") + private String user_level_card; + @ApiModelProperty("会员积分值") + private BigDecimal user_points; + @ApiModelProperty("会员余额") + private BigDecimal user_money; + @ApiModelProperty("加入时间") + private Date join_time; + @ApiModelProperty("秒级别时间戳") + private Long time_stamp; + @ApiModelProperty("随机字符串") + private String nonce_str; +} diff --git a/client/src/main/java/com/small/client/dto/UploadModel.java b/client/src/main/java/com/small/client/dto/UploadModel.java new file mode 100644 index 00000000..8a1c967f --- /dev/null +++ b/client/src/main/java/com/small/client/dto/UploadModel.java @@ -0,0 +1,15 @@ +package com.small.client.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +public class UploadModel extends CommentModel{ + @ApiModelProperty("页数作为路径") + private String page; + @ApiModelProperty("同步类型1商品,2分类,3品牌,4会员") + private String syncType; + +} diff --git a/client/src/main/java/com/small/client/enums/DicEnum.java b/client/src/main/java/com/small/client/enums/DicEnum.java new file mode 100644 index 00000000..514156cd --- /dev/null +++ b/client/src/main/java/com/small/client/enums/DicEnum.java @@ -0,0 +1,72 @@ +package com.small.client.enums; + + + +public enum DicEnum { + + SYNCTYPE_01("01","全量","syncType","同步类型","全量同步"), + SYNCTYPE_02("02","增量","syncType","同步类型","增量同步"), + + MUAL_1("1","商品","mual","商品","全量同步"), + MUAL_2("2","分类","mual","分类","分类"), + MUAL_3("3","品牌","mual","品牌","品牌"), + MUAL_4("4","会员","mual","会员","会员"), + ; + private String code; + + private String value; + + private String dicType; + + private String dicName; + + private String desc; + + DicEnum(String code, String value, String dicType, String dicName, String desc) { + this.code = code; + this.value = value; + this.dicType = dicType; + this.dicName = dicName; + this.desc = desc; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getDicType() { + return dicType; + } + + public void setDicType(String dicType) { + this.dicType = dicType; + } + + public String getDicName() { + return dicName; + } + + public void setDicName(String dicName) { + this.dicName = dicName; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } +} diff --git a/client/src/main/java/com/small/client/service/SxDataAbst/SxDataAbstService.java b/client/src/main/java/com/small/client/service/SxDataAbst/SxDataAbstService.java new file mode 100644 index 00000000..6b1e5284 --- /dev/null +++ b/client/src/main/java/com/small/client/service/SxDataAbst/SxDataAbstService.java @@ -0,0 +1,180 @@ +package com.small.client.service.SxDataAbst; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import com.small.client.Cache.CommonCache; +import com.small.client.dao.SxDataDao; +import com.small.client.dto.*; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.time.DateUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.text.ParseException; +import java.util.*; +import java.util.stream.Collectors; + +@Service +@Slf4j +public abstract class SxDataAbstService { + @Autowired + private CommonCache commonCache; + + /** + * List 转换为 List + * @param sxSyncVipList + * @return + */ + public List ConverList(List sxSyncVipList){ + List syncThirdMemberReqList=new ArrayList<>(); + if(CollUtil.isNotEmpty(sxSyncVipList)){ + SyncThirdMemberReq syncThirdMemberReq=null; + for (SxSyncVip sxSyncVip : sxSyncVipList) { + syncThirdMemberReq = new SyncThirdMemberReq(); + syncThirdMemberReq.setUser_nickname(sxSyncVip.getVip_name()); + syncThirdMemberReq.setUser_realname(sxSyncVip.getVip_name()); + if ("男".equals(sxSyncVip.getVip_name())) {//todo 需要确认数据是不是这样判断 + syncThirdMemberReq.setUser_gender(1); + } + if ("男".equals(sxSyncVip.getVip_name())) { + syncThirdMemberReq.setUser_gender(2); + } + syncThirdMemberReq.setUser_mobile(sxSyncVip.getMobile()); + syncThirdMemberReq.setUser_birthday(sxSyncVip.getBirthday()); + syncThirdMemberReq.setUser_level(sxSyncVip.getCard_type());//todo 涉及会员等级字典转换 + syncThirdMemberReq.setUser_level_card(sxSyncVip.getCard_no()); + syncThirdMemberReq.setUser_points(sxSyncVip.getNow_acc_num()); + syncThirdMemberReq.setUser_money(sxSyncVip.getResidual_amt()); + if(sxSyncVip.getVip_date()!=null){ + try { + syncThirdMemberReq.setJoin_time(DateUtils.parseDate(sxSyncVip.getVip_date())); + } catch (ParseException e) { + log.info("时间转换异常{0}",e); + throw new RuntimeException(e); + } + } + syncThirdMemberReqList.add(syncThirdMemberReq); + } + } + return syncThirdMemberReqList; + } + + /** + * + * @param sxSyncCategories + * @param allSxSyncCategories 所有分类 + * @return + */ + public List ConVToSxCategoryModel(List sxSyncCategories,List allSxSyncCategories) { + if(CollectionUtil.isEmpty(sxSyncCategories)){ + return new ArrayList<>(); + } + Iterator iterator= sxSyncCategories.iterator(); + List sxCategoryModels=new ArrayList<>(); + SxCategoryModel sxCategoryModel=null; + while (iterator.hasNext()){ + SxSyncCategory sxSyncCategory= iterator.next(); + sxCategoryModel=new SxCategoryModel(); + sxCategoryModel.setCategory_image(SxDataDao.DEFAULT_IMG); + sxCategoryModel.setCategory_name(sxSyncCategory.getItem_clsname()); + //寻找父级 + if(null!=sxSyncCategory.getCls_parent()){ + SxSyncCategory firstNode=getParentNode(allSxSyncCategories,sxSyncCategory.getCls_parent()); + sxCategoryModel.setParent_name(firstNode.getItem_clsname());//todo 暂时无用 + //如何存在上级的上级,则上级为第二层,上上及为第一层 + if(null!=firstNode.getCls_parent()) {//还存在上级 + SxSyncCategory secondNode=getParentNode(allSxSyncCategories,sxSyncCategory.getCls_parent()); + sxCategoryModel.setFirst_category_name(secondNode.getItem_clsname()); + sxCategoryModel.setSecond_category_name(firstNode.getItem_clsname()); + }else { + sxCategoryModel.setFirst_category_name(firstNode.getItem_clsname()); + } + } + sxCategoryModels.add(sxCategoryModel); + } + return sxCategoryModels; + } + + /** + * 通过流查找父节点 + * @param sxSyncCategories + * @param parentId + */ + private SxSyncCategory getParentNode(List sxSyncCategories,String parentId){ + List list= sxSyncCategories.stream().filter(cc-> + cc.getItem_clsno().trim().equals(parentId.trim())) + .collect(Collectors.toList()); + return CollectionUtil.isNotEmpty(list)?list.get(0):new SxSyncCategory(); + } + + /** + * 将List转换为List + * @param sxSyncGoods + * @return + */ + public List CvtToGoosModel(List sxSyncGoods,List specPriceDtoList){ + if(CollectionUtil.isEmpty(sxSyncGoods)){ + return null; + } + List sxGoosModelList=new ArrayList<>(); + SxGoosModel sxGoosModel=null; + for (SxSyncGoods sxSyncGood:sxSyncGoods){ + sxGoosModel=new SxGoosModel(); + sxGoosModel.setProduct_name(sxSyncGood.getItem_subname()); + sxGoosModel.setProduct_number(sxSyncGood.getItem_no().trim());// todo + sxGoosModel.setProduct_barcode(sxSyncGood.getItem_subno());// todo + + sxGoosModel.setFirst_category_name(commonCache.get(sxSyncGood.getSmall_cls_name()));// todo + sxGoosModel.setSecond_category_name("");// todo + + sxGoosModel.setThree_category_name("");// todo + sxGoosModel.setProduct_type("");// todo + sxGoosModel.setProduct_kind("");// todo + sxGoosModel.setCost_price(sxSyncGood.getPrice());//成本价 todo + sxGoosModel.setOriginal_price(sxSyncGood.getSale_price());//原价 todo + sxGoosModel.setPrice(sxSyncGood.getSale_price());// + sxGoosModel.setMember_price(sxSyncGood.getVip_price());//会员价 + sxGoosModel.setStock(sxSyncGood.getStock());//库存 todo + sxGoosModel.setGross_margin(sxSyncGood.getGross_margin()); //毛利率 todo + sxGoosModel.setUnit(sxSyncGood.getUnit_no());//单位 + sxGoosModel.setCan_piont(new BigDecimal(0));//可用积分 todo + sxGoosModel.setPoints(sxSyncGood.getVip_acc_num());//总积分 todo + sxGoosModel.setMnemonic(sxSyncGood.getItem_rem());//助记码 todo + sxGoosModel.setRetail_price(sxSyncGood.getSale_price()); + // sxGoosModel.setBuy_limit(10);//最大购买商品量 todo + sxGoosModel.setBrand_name(commonCache.get(sxSyncGood.getItemBrand()));//品牌 todo + sxGoosModel.setTags("");//标签 todo + + sxGoosModel.setProduct_assist(Arrays.asList(sxSyncGood.getItem_subname()));//帮助 todo + + sxGoosModel.setProduct_spec(Collections.singletonList(sxSyncGood.getItem_size()==null?"":sxSyncGood.getItem_size()));//规格 + + sxGoosModel.setProduct_value("");//商品卖点特征 todo + + sxGoosModel.setProduct_video("");//商品视频 todo + + sxGoosModel.setProduct_desc("");//商品描述 todo + + sxGoosModel.setProduct_images(new ArrayList<>());//介绍图片 todo + + sxGoosModel.setPromotion_detail(new ArrayList<>());//活动列表 todo + + SxGoosModel finalSxGoosModel = sxGoosModel; + specPriceDtoList.forEach(m->{ + if(sxSyncGood.getItem_no().equals(m.getItemNo())){ + String type=m.getDiscountType(); + if(type.equals("1")){ + finalSxGoosModel.setPrice(m.getSpecPrice()); + finalSxGoosModel.setBuy_limit(m.getSaleQty());//最大购买商品量 todo + } + if(type.equals("2")){ + finalSxGoosModel.setPrice(finalSxGoosModel.getPrice().multiply(m.getDiscount())); + } + } + }); + sxGoosModelList.add(sxGoosModel); + } + return sxGoosModelList; + } +} diff --git a/client/src/main/java/com/small/client/service/SxDataService.java b/client/src/main/java/com/small/client/service/SxDataService.java new file mode 100644 index 00000000..3c3cc48d --- /dev/null +++ b/client/src/main/java/com/small/client/service/SxDataService.java @@ -0,0 +1,53 @@ +package com.small.client.service; + + +import com.small.client.dto.*; + + +public interface SxDataService { + + /** + * 同步商品分类数据 + * @param dataBaseInfo + * @param commentModel + */ + void SyncCategory(DataBaseInfo dataBaseInfo, CommentModel commentModel); + + /** + * 同步商品数据 + * @param dataBaseInfo + * @param commentModel + */ + void SyncGoods(DataBaseInfo dataBaseInfo, CommentModel commentModel); + + /** + * 品牌同步 + * @param dataBaseInfo + * @return + */ + void SyncBranchList(DataBaseInfo dataBaseInfo, CommentModel commentModel); + + /** + * 同步会员数据 + * @param dataBaseInfo + */ + void SyncVipList(DataBaseInfo dataBaseInfo, CommentModel commentModel); + + + CommentModel getCommentModel(); + + void getAppSign(); + + String downLoadClient(); + + void checkForUpdates(); + + /** + * 获取服务器配置 + * @param commentModel + * @return + */ + DataBaseInfo getDataBaseInfo(CommentModel commentModel); + + +} diff --git a/client/src/main/java/com/small/client/service/WebClientService.java b/client/src/main/java/com/small/client/service/WebClientService.java new file mode 100644 index 00000000..b94a63dc --- /dev/null +++ b/client/src/main/java/com/small/client/service/WebClientService.java @@ -0,0 +1,88 @@ +package com.small.client.service; + +import cn.hutool.json.JSONObject; +import com.small.client.Utils.FileUtils; +import com.small.client.Utils.HttpUtils; +import com.small.client.dto.CommentModel; +import com.small.client.dto.UploadModel; +import lombok.extern.slf4j.Slf4j; +import netscape.javascript.JSObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.FileSystemResource; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +@Service +@Slf4j +@Component +public class WebClientService { + @Value("${remoteIp}") + private String remoteIp; + + private final RestTemplate restTemplate; + + public WebClientService(RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } + + public String get(String url) { + return restTemplate.getForObject(url, String.class); + } + + /** + * 文件上传 + */ + public String uploudSxData(String filePath, CommentModel commentModel,String page,String syncType){ + UploadModel uploadModel=new UploadModel(); + uploadModel.setAppKey(commentModel.getAppKey()); + uploadModel.setSign(commentModel.getSign()); + uploadModel.setPage(page); + uploadModel.setSyncType(syncType); + //"C:\\Users\\Administrator\\uploaded\\2025\\3\\25\\goods_1.txt" + return this.uploadFile(filePath, remoteIp+HttpUtils.URL_UPLOUP,uploadModel); + } + + + public String uploadFile(String filePath, String url, UploadModel uploadModel) { + // 创建MultiValueMap来封装文件数据和请求参数(如果有的话) + MultiValueMap body = new LinkedMultiValueMap<>(); + FileSystemResource file = new FileSystemResource(filePath); // 文件路径 + body.add("file", file); // "file"是与服务器期望的表单字段名称相对应的键,例如在Spring Controller中用@RequestParam("file") String file接收文件数据 + body.add("appKey",uploadModel.getAppKey()); + body.add("sign",uploadModel.getSign()); + body.add("page",uploadModel.getPage());//页数 + body.add("syncType",uploadModel.getSyncType());//商品类型 + // 设置请求头,指定为multipart/form-data类型,并设置boundary(可选) + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + + // 创建HttpEntity对象,它将用于POST请求体中包含文件数据和请求头信息 + HttpEntity> requestEntity = new HttpEntity<>(body, headers); + + // 执行POST请求进行文件上传 + JSONObject jsonObject= restTemplate.exchange(url, HttpMethod.POST, requestEntity, JSONObject.class).getBody(); + log.info(jsonObject.toString()); + log.info(jsonObject.getStr("error_msg")); + log.info(jsonObject.getStr("error_code")); + return jsonObject.getStr("error_code"); + } + + public static void main(String[] args) { + + // String responseStr= new WebClientService(new RestTemplate()).get("http://localhost:8088/mobile/account/login/testcase"); + +// String responseStr= new WebClientService(new RestTemplate()).get("http://localhost:8088/mobile/account/login/testcase"); +// System.out.printf(responseStr); + // new WebClientService(new RestTemplate()).uploudSxData(); + log.info("测试"); + } + +} diff --git a/client/src/main/java/com/small/client/service/imp/SxDataServiceImp.java b/client/src/main/java/com/small/client/service/imp/SxDataServiceImp.java new file mode 100644 index 00000000..1aea50fd --- /dev/null +++ b/client/src/main/java/com/small/client/service/imp/SxDataServiceImp.java @@ -0,0 +1,645 @@ +package com.small.client.service.imp; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.small.client.Cache.CommonCache; +import com.small.client.Utils.*; +import com.small.client.dao.SxDataDao; +import com.small.client.dto.*; +import com.small.client.enums.DicEnum; +import com.small.client.service.SxDataAbst.SxDataAbstService; +import com.small.client.service.SxDataService; + +import com.small.client.service.WebClientService; +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.util.StreamUtils; +import org.springframework.web.client.RestTemplate; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.*; +import java.util.stream.Collectors; + +import static com.small.client.Utils.FileUtils.copyFile; + + +@Service +@Slf4j +public class SxDataServiceImp extends SxDataAbstService implements SxDataService { + @Autowired + private SxDataDao sxDataDao; + + @Autowired + private RestTemplate restTemplate; + + @Value("${remoteIp}") + private String remoteIp; + + @Autowired + private CommonCache commonCache; + + @Autowired + private WebClientService webClientService; + + + /** + * 同步商品分类数据 + * @param dataBaseInfo + * @param commentModel + */ + @Override + public void SyncCategory(DataBaseInfo dataBaseInfo, CommentModel commentModel) { + dataBaseInfo= getDataBaseInfo(commentModel); + // 记录总数 + Integer total = sxDataDao.getTBdItemClsTotal(dataBaseInfo); + if(total==0){ + log.info("暂无商品分类同步"); + return; + } + // 总页数 + int pages = CommonUtil.getPagesCount(total, SxDataDao.PAGESIZE); + List allSxSyncCategories= sxDataDao.findTBdItemClsList(dataBaseInfo); + int syncCount =0; + for (int i = 1; i <=pages; i++) { + List sxSyncCategories= sxDataDao.findTBdItemClsListPage(dataBaseInfo,i,SxDataDao.PAGESIZE); + List sxCategoryModelList= ConVToSxCategoryModel(sxSyncCategories,allSxSyncCategories); + JSONArray jsonArray =null; + String jsonString=""; + ObjectMapper objectMapper = new ObjectMapper(); + try { + jsonString = objectMapper.writeValueAsString(sxCategoryModelList); + jsonArray = JSONUtil.parseArray(jsonString); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + String code= HttpUtils.postData(restTemplate,remoteIp+HttpUtils.URL_SYNC_CATEGORY + +"?appKey="+commentModel.getAppKey() + +"&sign="+commentModel.getSign(),jsonArray);//todo 后期改为文件传输 + if (!HttpUtils.SUCCESSCODE.equals(code)) { + continue; + } + syncCount+=sxCategoryModelList.size(); + } + log.info("商品分类总共有{}条数据,同步完成{}条",total,syncCount); + } + + /** + * 同步商品数据 + * @param dataBaseInfo + * @param commentModel + */ + @Override + public void SyncGoods(DataBaseInfo dataBaseInfo, CommentModel commentModel) { + //dataBaseInfo= getDataBaseInfo(commentModel); + String syncType ="01"; + if(StringUtils.isNotEmpty(commentModel.getSyncTime())){//如果有同步时间,则为增量 + syncType="02"; + dataBaseInfo.setSyncType("02"); + } + if(StringUtils.isEmpty(syncType)){ + syncType="01"; + dataBaseInfo.setSyncType("01"); + } + switch (syncType) { + case "01"://全量 + syncAllGoods(dataBaseInfo, commentModel); + break; + case "02"://增量 todo test + syncIncrementAddGoods(dataBaseInfo, commentModel); + syncIncrementModifyGoods(dataBaseInfo, commentModel); + syncIncrementStock(dataBaseInfo, commentModel); + break; + } + + } + + /** + * 同步品牌数据 + * @param dataBaseInfo + * @param commentModel + */ + @Override + public void SyncBranchList(DataBaseInfo dataBaseInfo, CommentModel commentModel) { + dataBaseInfo= getDataBaseInfo(commentModel); + List brandModels= sxDataDao.getBdBrandList(dataBaseInfo); + if(brandModels!=null&&brandModels.size()>0){ + String jsonString =""; + JSONArray jsonArray =new JSONArray(); + ObjectMapper objectMapper = new ObjectMapper(); + try { + jsonString = objectMapper.writeValueAsString(brandModels); + jsonArray = JSONUtil.parseArray(jsonString); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + String code= HttpUtils.postData(restTemplate,remoteIp+HttpUtils.URL_SYNC_BRAND + +"?appKey="+commentModel.getAppKey() + +"&sign="+commentModel.getSign(),jsonArray);//todo 后期改为文件传输 + if(code!=null){ + log.info("品牌总共有{}条数据,同步完成{}条",brandModels.size(),brandModels.size()); + } + }else { + log.info("品牌数据为空"); + } + + } + + /** + * 同步会员数据 + * @param dataBaseInfo + * @param commentModel + */ + @Override + public void SyncVipList(DataBaseInfo dataBaseInfo, CommentModel commentModel) { + dataBaseInfo= getDataBaseInfo(commentModel); + String where="where 1=1 "; + if(StringUtils.isNotEmpty(commentModel.getSyncTime())){ + where+="and oper_date > '"+commentModel.getSyncTime()+"'"; + } + dataBaseInfo.setWhere(where); + // 记录总数 + Integer total = sxDataDao.getTrmVipInfoTotal(dataBaseInfo); + if(total==0){ + log.info("暂无会员数据同步"); + return; + } + // 总页数 + int pages = CommonUtil.getPagesCount(total, SxDataDao.PAGESIZE); + List memberList=new ArrayList<>(); + int syncCount =0; + for (int i = 1; i <=pages; i++) { + memberList.clear(); + List sxSyncVipList= sxDataDao.findRmVipInfoListPage(dataBaseInfo,i,SxDataDao.PAGESIZE); + //处理数据转换SxSyncVip>SyncThirdMemberReq + memberList=ConverList(sxSyncVipList); + String jsonString =""; + ObjectMapper objectMapper = new ObjectMapper(); + try { + jsonString = objectMapper.writeValueAsString(memberList); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + String code= HttpUtils.postData(restTemplate,remoteIp+HttpUtils.URL_SYNC_MEMBER + +"?appKey="+commentModel.getAppKey() + +"&sign="+commentModel.getSign(),memberList);//todo 后期改为文件传输 + if (!HttpUtils.SUCCESSCODE.equals(code)) { + continue; + } + syncCount+=memberList.size(); + } + log.info("vip会员总共有{}条数据,同步完成{}条",total,syncCount); + } + + /** + * 获取并缓存父节点的根节点,用户查找数据 + * @param dataBaseInfo + * @param parentName + */ + public String getAndCacheTree(DataBaseInfo dataBaseInfo,String parentName){ + List sxSyncCategories=new SxDataDao().findTBdItemClsList(dataBaseInfo); + String parentId= sxSyncCategories.stream().filter(m->m.getItem_clsname().equals(parentName)). + map(SxSyncCategory::getItem_clsno).collect(Collectors.joining()); + getBdBrandCacheList(dataBaseInfo); + String childrens=commonCache.get(CommonCache.CACHE_CATEGROY+parentId); + if(childrens==null){ + log.info(JSONUtil.toJsonStr(buildTree(sxSyncCategories,parentId))); + } + return parentId; + } + + /** + * 构建树节点 + * @param sxSyncCategories + * @return + */ + public List buildTree(List sxSyncCategories,String parentId){ + List treeNodes=new ArrayList<>(); + List rootSxSyncCategory=getRootNode(sxSyncCategories); + for (SxSyncCategory node : rootSxSyncCategory) { + node=buildChildTree(node,sxSyncCategories,parentId); + treeNodes.add(node); + } + return treeNodes; + } + + /** + * 获取所有的根节点 + * @param sxSyncCategories + * @return + */ + public List getRootNode(List sxSyncCategories){ + List rootNodeList=new ArrayList<>(); + for (SxSyncCategory node : sxSyncCategories) { + if (null==node.getCls_parent()) { + rootNodeList.add(node); + } + } + return rootNodeList; + } + + /** + * 构建子节点 + * @param root + * @param sxSyncCategories + * @return + */ + public SxSyncCategory buildChildTree(SxSyncCategory root,List sxSyncCategories,String findByParentId) { + List children = new ArrayList<>(); + for (SxSyncCategory node : sxSyncCategories) { + commonCache.put(node.getItem_clsno(),node.getItem_clsname());//把数据加入缓存 + if (root.getItem_clsno().equals(node.getCls_parent())) { + children.add(buildChildTree(node,sxSyncCategories,findByParentId)); + } + } + if(root.getItem_clsno().equals(findByParentId)){ + commonCache.put(CommonCache.CACHE_CATEGROY+findByParentId,children.stream().map(SxSyncCategory::getItem_clsno).collect(Collectors.joining(","))); + } + root.setChildren(children); + return root; + } + + /** + *同步所有商品 + * @param dataBaseInfo + * @param commentModel + */ + private void syncAllGoods(DataBaseInfo dataBaseInfo, CommentModel commentModel){ + String where="where 1=1"; + Integer total =0; + if(DicEnum.SYNCTYPE_02.getCode().equals(dataBaseInfo.getSyncType())){ + if (StringUtils.isNotEmpty(dataBaseInfo.getCategoryName())) { + String parentId=getAndCacheTree(dataBaseInfo,dataBaseInfo.getCategoryName()); + String childrens= commonCache.get(CommonCache.CACHE_CATEGROY+parentId); + //syncGoodsSearchModel.setItemClsno(childrens); + where += " and b.item_clsno in ('" + childrens + "')"; + } + if(StringUtils.isNotEmpty(commentModel.getSyncTime())){ + where+=" and b.modify_date>'"+commentModel.getSyncTime()+"' "; + where+=" or b.build_date>'"+commentModel.getSyncTime()+"' "; + } + if(StringUtils.isNotEmpty(dataBaseInfo.getOperDate())){ + where+=" and t.oper_date>'"+dataBaseInfo.getOperDate()+"' "; + } + dataBaseInfo.setWhere(where); + // 记录总数 + total = sxDataDao.getTBditemInfoJoninTotal(dataBaseInfo); + }else { + dataBaseInfo.setWhere(where); + total = sxDataDao.getTBditemInfoTotal(dataBaseInfo); + } + if(total==0){ + log.info("暂无商品同步"); + return; + } + + // 总页数 + int pages = CommonUtil.getPagesCount(total, SxDataDao.PAGESIZE); + int syncCount =0; + + List discountList= getDiscountFromCache(dataBaseInfo); + List specPriceDtoList= getSpecPriceFromCache(dataBaseInfo); + specPriceDtoList.addAll(discountList); + + List folders=new ArrayList<>(); + for (int i = 1; i <=pages; i++) { + List sxSyncGoods= sxDataDao.findBditemInfoListPage(dataBaseInfo,i,SxDataDao.PAGESIZE); + List sxGoosModelList= CvtToGoosModel(sxSyncGoods,specPriceDtoList); + String jsonString=""; + ObjectMapper objectMapper = new ObjectMapper(); + try { + jsonString = objectMapper.writeValueAsString(sxGoosModelList); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + String code= writeToFileAndUploud(i,jsonString,commentModel,DicEnum.MUAL_1.getCode()); + if (!HttpUtils.SUCCESSCODE.equals(code)) { + continue; + } + folders.add(String.valueOf(i)); + syncCount+=sxSyncGoods.size(); + } + // folders.add(String.valueOf(2)); + //folders.add(String.valueOf(4)); + //folders.add(String.valueOf(5)); + log.info("商品分类总共有{}条数据,同步完成{}条",total,syncCount); + String code= HttpUtils.postData(restTemplate,remoteIp+HttpUtils.URL_SYNC_GOODS_READ + +"?appKey="+commentModel.getAppKey() + +"&sign="+commentModel.getSign() + +"&syncType="+DicEnum.MUAL_1.getCode(), + JSONUtil.parseArray(folders)); + if (HttpUtils.SUCCESSCODE.equals(code)) { + log.info("思迅商品同步完成,通知服务器处理数据相应成功"); + //记录同步时间 + createDateFile(); + } + } + + /** + * 文件上传到服务器 + * @param page + * @param content + * @param commentModel + * @return + */ + private String writeToFileAndUploud(Integer page,String content,CommentModel commentModel,String syncType){ + FileUtils fileUtils= new FileUtils(); + File file=fileUtils.createFile(syncType,page); + String fileName=fileUtils.getFileName(syncType,page,FileUtils.txtEnd); + String filePath=file.getAbsolutePath(); + fileUtils.writeFile(filePath,fileName,content); + return webClientService.uploudSxData(filePath+FileUtils.pathSeparator+fileName,commentModel,page.toString(),syncType); + } + + /** + * 记录同步时间 + */ + private void createDateFile(){ + String path=JarPathUtil.getRuntimePath(); + FileUtils fileUtils= new FileUtils(); + File file=fileUtils.createFile(path); + String filePath=file.getAbsolutePath(); + String date= DateUtil.format(new Date(),"yyyy-MM-dd"); + fileUtils.writeFile(filePath,FileUtils.REFLESHDATE,date); + } + + /** + * 增量新增数据,新增商品数据 + * @param dataBaseInfo + * @param commentModel + */ + private void syncIncrementAddGoods(DataBaseInfo dataBaseInfo, CommentModel commentModel){ + syncAllGoods(dataBaseInfo,commentModel); + } + + /** + * 增量同步 商品内容变化 + * @param dataBaseInfo + * @param commentModel + */ + private void syncIncrementModifyGoods(DataBaseInfo dataBaseInfo, CommentModel commentModel){ + syncAllGoods(dataBaseInfo,commentModel); + } + + /** + * 增量同步 库存变化 + */ + private void syncIncrementStock(DataBaseInfo dataBaseInfo, CommentModel commentModel){ + dataBaseInfo.setOperDate(commentModel.getSyncTime()); + commentModel.setSyncTime(""); + syncAllGoods(dataBaseInfo,commentModel); + } + + /** + *获取特价商品 + * @param dataBaseInfo + * @return + */ + public List getDiscountFromCache(DataBaseInfo dataBaseInfo){ + List specPriceDtos= commonCache.getSpecPrice("specDiscountCache"); + if (null!=specPriceDtos){ + return specPriceDtos; + }else { + specPriceDtos= sxDataDao.getSpecPriceList(dataBaseInfo); + commonCache.putSpecPrice("specDiscountCache",specPriceDtos); + } + return specPriceDtos; + } + + + /** + *获取促销产品数据 + * @param dataBaseInfo + * @return + */ + public List getSpecPriceFromCache(DataBaseInfo dataBaseInfo){ + List specPriceDtos= commonCache.getSpecPrice("specPriceCache"); + if(null!=specPriceDtos){ + return specPriceDtos; + }else { + specPriceDtos= sxDataDao.getDiscountPriceList(dataBaseInfo); + commonCache.putSpecPrice("specDiscountCache",specPriceDtos); + } + return specPriceDtos; + } + + /** + * 获取品牌数据并加入到缓存 + * @param dataBaseInfo + * @return + */ + public void getBdBrandCacheList(DataBaseInfo dataBaseInfo){ + List specPriceDtos= commonCache.getBrandCahce("brandCache"); + if(null==specPriceDtos){ + specPriceDtos= sxDataDao.getBdBrandList(dataBaseInfo); + if(CollectionUtil.isNotEmpty(specPriceDtos)){ + for (BrandModel brandModel : specPriceDtos) { + commonCache.put(brandModel.getCodeId(),brandModel.getBrand_name()); + } + commonCache.setBrandCahce("brandCache",specPriceDtos); + } + + } + } + + @Override + public CommentModel getCommentModel() { + String path=JarPathUtil.getRuntimePath(); + log.info(JarPathUtil.getRuntimePath()); + File folder = new File(path); + //读取文件 + try { + String encryptedData = getPrimaryKey(); + Map result= CryptoUtils.decryptAndUnpack(encryptedData); + CommentModel commentModel=new CommentModel(); + commentModel.setSign(result.get("sign")); + commentModel.setAppKey(result.get("appKey")); + commentModel.setStoreId(result.get("storeId")); + //获取上次同步的最大时间 + File[] lastDateJsonFile = folder.listFiles((dir, name) -> name.endsWith(FileUtils.REFLESHDATE)); + if(lastDateJsonFile!=null&&lastDateJsonFile.length>0){ + String lastDate = new String(Files.readAllBytes(lastDateJsonFile[0].toPath()), StandardCharsets.UTF_8).trim(); + commentModel.setSyncTime(lastDate); + } + return commentModel; + } catch (RuntimeException | IOException e) { + throw new RuntimeException("密钥获取失败"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public void getAppSign() { + String encryptedData= getPrimaryKey(); + JSONObject jsonObject=new JSONObject(); + jsonObject.putOnce("primaryKey",encryptedData); + String primaryKey= HttpUtils.postData(restTemplate,remoteIp+HttpUtils.URL_SYNC_GET_APPSIGN + +"?primaryKey="+encryptedData, jsonObject,"primaryKey"); + if(null!=primaryKey){ + String path=JarPathUtil.getRuntimePath(); + FileUtils fileUtils= new FileUtils(); + File file=fileUtils.createFile(path); + String filePath=file.getAbsolutePath(); + fileUtils.writeFile(filePath,FileUtils.PRIMARYKEY,primaryKey); + }else { + log.info("获取服务器密钥失败"); + } + } + + + /** + * 获取文本密钥 + * @return + */ + private String getPrimaryKey(){ + String path=JarPathUtil.getRuntimePath(); + log.info("RuntimePath:{}",JarPathUtil.getRuntimePath()); + File folder = new File(path); + File[] jsonFiles = folder.listFiles((dir, name) -> name.endsWith(FileUtils.PRIMARYKEY)); + if(null!=jsonFiles&&jsonFiles.length>0){ + try { + return new String(Files.readAllBytes(jsonFiles[0].toPath()), StandardCharsets.UTF_8); + }catch (RuntimeException | IOException e){ + throw new RuntimeException("读取[fileName]文件错误"); + } + + } + return null; + } + + @Override + public String downLoadClient() { + String downloadDirectory=FileUtils.CLIENTSTALLPATH; + String originalFileName = ""; + try { + File file= new File(downloadDirectory); + if(!file.exists()){ + file.mkdirs(); // 确保目录存在 + } + log.info("文件下载目录: {}", downloadDirectory); + HttpHeaders headers = new HttpHeaders(); + HttpEntity requestEntity = new HttpEntity<>(headers); + String encryptedData = getPrimaryKey(); + + ResponseEntity response = restTemplate.exchange( + remoteIp+HttpUtils.URL_SYNC_GET_DOWNCLIENTJAR+"?primaryKey="+encryptedData, + HttpMethod.GET, + requestEntity, + Resource.class); + if(response.getStatusCode().is2xxSuccessful() && null!= response.getHeaders().getFirst("error")){ + String error=response.getHeaders().getFirst("error"); + switch (Objects.requireNonNull(error)){ + case "noVersion": + log.info("没有版本更新"); + break; + case "noFile": + log.info("系统错误:文件不存在"); + break; + case "500":; + log.info("系统错误:系统内部错误"); + break; + case "noValid": + log.info("密钥校验失败:密钥过期"); + break; + } + return null; + } + if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) { + // 从Content-Disposition头获取文件名 + String contentDisposition = response.getHeaders().getFirst(HttpHeaders.CONTENT_DISPOSITION); + if (contentDisposition != null && contentDisposition.contains("filename=")) { + originalFileName = contentDisposition + .split("filename=")[1] + .replace("\"", ""); // 去除引号 + } + // 如果无法从header获取,使用默认名 + if (originalFileName == null || originalFileName.isEmpty()) { + originalFileName = "new_client" + System.currentTimeMillis()+".jar"; + } + File outputFile = new File(downloadDirectory + originalFileName); + try (FileOutputStream outputStream = new FileOutputStream(outputFile)) { + StreamUtils.copy(response.getBody().getInputStream(), outputStream); + log.info("文件下载成功: {}" , outputFile.getAbsolutePath()); + }catch (Exception e){ + log.error("下载失败,下载流异常:{}",e.getMessage()); + return null; + } + } else { + log.error("文件下载失败: {}", response.getStatusCode()); + return null; + } + }catch (Exception e){ + log.error("文件下载失败:{}",e.getMessage()); + return null; + } + return downloadDirectory+originalFileName; + } + + @Override + public void checkForUpdates() { + log.info("curentPath:{}",JarPathUtil.getRuntimePath()); + File classDirFile = new File(JarPathUtil.getRuntimePath()); + File parentDir = classDirFile.getParentFile(); + String filePath= this.downLoadClient(); + if (filePath != null) { + copyFile(filePath,parentDir.getAbsolutePath()+"/lib"); + applyUpdate(); + } + } + + private void applyUpdate() { + try { + File classDirFile = new File(JarPathUtil.getRuntimePath()); + File parentDir = classDirFile.getParentFile(); + // 执行脚本并退出当前应用 + log.info(parentDir.getAbsolutePath()+"/bin/run.bat"); + Runtime.getRuntime().exec(parentDir.getAbsolutePath()+"/bin/run.bat"); + log.info(parentDir.getAbsolutePath()+"/bin/run.bat"); + System.exit(0); + } catch (IOException e) { + log.error("异常{}", e.getMessage()); + } + } + + + @Override + public DataBaseInfo getDataBaseInfo(CommentModel commentModel) { + JSONObject jsonObject=new JSONObject(); + jsonObject.putOnce("appKey",commentModel.getAppKey()); + jsonObject.putOnce("sign",commentModel.getSign()); + StoreDbConfig storeDbConfig= HttpUtils.postDataGetConfig(restTemplate,remoteIp+HttpUtils.URL_SYNC_GET_STOREdBCONFIG + +"?appKey="+commentModel.getAppKey() + +"&sign="+commentModel.getSign(), jsonObject); + DataBaseInfo dataBaseInfo=new DataBaseInfo(); + if(null!=storeDbConfig){ + dataBaseInfo.setIp(storeDbConfig.getDbIp()); + dataBaseInfo.setPassword(storeDbConfig.getDbPassword()); + dataBaseInfo.setDataBaseName(storeDbConfig.getDbName()); + dataBaseInfo.setDbPort(storeDbConfig.getDbPort()); + dataBaseInfo.setSyncMode(storeDbConfig.getSyncMode()); + dataBaseInfo.setCronExpression(storeDbConfig.getCronExpression()); + dataBaseInfo.setCategoryName(storeDbConfig.getCategoryName()); + return dataBaseInfo; + } + return new DataBaseInfo(); + } + +} diff --git a/client/src/main/resources/application.yml b/client/src/main/resources/application.yml new file mode 100644 index 00000000..6b48c137 --- /dev/null +++ b/client/src/main/resources/application.yml @@ -0,0 +1,15 @@ +server: + port: 9099 + servlet: + encoding: + charset: UTF-8 + enabled: true + force: true + +#配置远程模板信息 +#服务器地址 +logging: + charset: + console: UTF-8 + +remoteIp: https://mall.gpxscs.cn \ No newline at end of file diff --git a/client/src/main/resources/logback.xml b/client/src/main/resources/logback.xml new file mode 100644 index 00000000..79a5a80e --- /dev/null +++ b/client/src/main/resources/logback.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + UTF-8 + %d{yyyy-MM-dd HH:mm:ss} - %msg%n + + + + + + From 46af45ebd93d3f25cd50d023603a1c55de2dc0ed Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Tue, 20 May 2025 18:09:38 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E8=BF=98=E5=8E=9F=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/ShopBaseProductTypeServiceImpl.java | 4 +-- .../mall/shop/config/GlobalCorsConfig.java | 30 ------------------- .../suisung/mall/shop/config/WebConfig.java | 11 ------- 3 files changed, 2 insertions(+), 43 deletions(-) delete mode 100644 mall-shop/src/main/java/com/suisung/mall/shop/config/GlobalCorsConfig.java diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseProductTypeServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseProductTypeServiceImpl.java index bd1ca3d1..82ab1afa 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseProductTypeServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/base/service/impl/ShopBaseProductTypeServiceImpl.java @@ -85,8 +85,8 @@ public class ShopBaseProductTypeServiceImpl extends BaseServiceImpl Date: Tue, 20 May 2025 18:49:34 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mall-shop/src/main/resources/bootstrap-dev.yml | 3 +++ mall-shop/src/main/resources/bootstrap-prod.yml | 6 +++++- mall-shop/src/main/resources/bootstrap-test.yml | 5 ++++- mall-shop/src/main/resources/bootstrap-uat.yml | 5 ++++- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/mall-shop/src/main/resources/bootstrap-dev.yml b/mall-shop/src/main/resources/bootstrap-dev.yml index fb7aab8d..ca7f8efe 100644 --- a/mall-shop/src/main/resources/bootstrap-dev.yml +++ b/mall-shop/src/main/resources/bootstrap-dev.yml @@ -181,3 +181,6 @@ esign: app_secret: 8da2e1eeeaf88e09bcf432a2fdd3e4d7 app_rsa: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiC+fUc0+O9m45VEcGciJQ5QQNXs3NkHoHM2qDAdrXOnTwku0Be1IPeWUZ4s7w8xqubyirAAJDc3LpRkwCK84NicA2VwraD4on8MNtX8MLALZjLc1jZTmRAPKVfTKAcainR7ET78Y+QKJgezNvI7u45FO4Db+dWCC7pbedxBo+kHKA8im+/G0hpQaklxw1wjIMNv+x+YBnm8FOXRPWJZ+eItF5qJOT2C16QCY7hdeHknom+NMpZD8E/WAMtf03BcgigsoavTVnPI0xnN8BCrgykDWgO5bUXeIgNEF1LJS6r8s6BaMl+ZWbuODtbsrQ941GbFOe6x8tnhPIeehIa1AWQIDAQAB debug: true +#客户端上传文件地址 +client: + path: E:/data \ No newline at end of file diff --git a/mall-shop/src/main/resources/bootstrap-prod.yml b/mall-shop/src/main/resources/bootstrap-prod.yml index 4750e715..e0c242cb 100644 --- a/mall-shop/src/main/resources/bootstrap-prod.yml +++ b/mall-shop/src/main/resources/bootstrap-prod.yml @@ -214,4 +214,8 @@ esign_prod: app_id: 5111986290 app_secret: 4408a0395464596cf7d33dda5f9c7608 app_rsa: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApzK8BGqUdq9eufyPieCux2bMuKnXHQXgCT2kVcHqwFW1LqZMxm/npuf94C8XLK481klD15WPTVi5YUrqnXYZpIawbuH1+2bfHnZL4aSdZ+QUvlol2WLigoxlgPU9gFYWj/ykovQKi78myWbrIzaM6VJxB6RSJKBVzxm2v8M93J73S8btT7E/RRBtX+dGP7In0PpTwugsHG/w7NQ/e3budt4FLIobGzZT26GkuQ6k8fme6M+aAWgfmQIEGi+a8HLl8hP21wHHCxLJKHxxH/OADQGnzRiK632yaXmGD9/cj2xo/6zzNKDkHK9Ud64E7Pj2LrZPBaXPoxhLqJ7j6v6MgQIDAQAB - debug: false \ No newline at end of file + debug: false + +#客户端上传文件地址 +client: + path: /home/user/data \ No newline at end of file diff --git a/mall-shop/src/main/resources/bootstrap-test.yml b/mall-shop/src/main/resources/bootstrap-test.yml index 73384e68..0a13acd5 100644 --- a/mall-shop/src/main/resources/bootstrap-test.yml +++ b/mall-shop/src/main/resources/bootstrap-test.yml @@ -184,4 +184,7 @@ esign: app_id: 7439053575 app_secret: 8da2e1eeeaf88e09bcf432a2fdd3e4d7 app_rsa: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiC+fUc0+O9m45VEcGciJQ5QQNXs3NkHoHM2qDAdrXOnTwku0Be1IPeWUZ4s7w8xqubyirAAJDc3LpRkwCK84NicA2VwraD4on8MNtX8MLALZjLc1jZTmRAPKVfTKAcainR7ET78Y+QKJgezNvI7u45FO4Db+dWCC7pbedxBo+kHKA8im+/G0hpQaklxw1wjIMNv+x+YBnm8FOXRPWJZ+eItF5qJOT2C16QCY7hdeHknom+NMpZD8E/WAMtf03BcgigsoavTVnPI0xnN8BCrgykDWgO5bUXeIgNEF1LJS6r8s6BaMl+ZWbuODtbsrQ941GbFOe6x8tnhPIeehIa1AWQIDAQAB - debug: true \ No newline at end of file + debug: true +#客户端上传文件地址 +client: + path: /home/user/data \ No newline at end of file diff --git a/mall-shop/src/main/resources/bootstrap-uat.yml b/mall-shop/src/main/resources/bootstrap-uat.yml index 73384e68..0a13acd5 100644 --- a/mall-shop/src/main/resources/bootstrap-uat.yml +++ b/mall-shop/src/main/resources/bootstrap-uat.yml @@ -184,4 +184,7 @@ esign: app_id: 7439053575 app_secret: 8da2e1eeeaf88e09bcf432a2fdd3e4d7 app_rsa: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiC+fUc0+O9m45VEcGciJQ5QQNXs3NkHoHM2qDAdrXOnTwku0Be1IPeWUZ4s7w8xqubyirAAJDc3LpRkwCK84NicA2VwraD4on8MNtX8MLALZjLc1jZTmRAPKVfTKAcainR7ET78Y+QKJgezNvI7u45FO4Db+dWCC7pbedxBo+kHKA8im+/G0hpQaklxw1wjIMNv+x+YBnm8FOXRPWJZ+eItF5qJOT2C16QCY7hdeHknom+NMpZD8E/WAMtf03BcgigsoavTVnPI0xnN8BCrgykDWgO5bUXeIgNEF1LJS6r8s6BaMl+ZWbuODtbsrQ941GbFOe6x8tnhPIeehIa1AWQIDAQAB - debug: true \ No newline at end of file + debug: true +#客户端上传文件地址 +client: + path: /home/user/data \ No newline at end of file From 7cdb9f88f20e2310611fc29579cfb72006a55345 Mon Sep 17 00:00:00 2001 From: liyj <1617420630@qq.com> Date: Tue, 20 May 2025 18:51:55 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mall-shop/src/main/resources/bootstrap-local.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mall-shop/src/main/resources/bootstrap-local.yml b/mall-shop/src/main/resources/bootstrap-local.yml index cc07f8a6..7dd1406f 100644 --- a/mall-shop/src/main/resources/bootstrap-local.yml +++ b/mall-shop/src/main/resources/bootstrap-local.yml @@ -184,4 +184,4 @@ esign: debug: true #客户端上传文件地址 client: - path: @client.path@ + path: E:/data \ No newline at end of file