商品同步新增映射功能

This commit is contained in:
liyj 2025-06-10 16:59:26 +08:00
parent f694db11e9
commit d2791afab2
42 changed files with 2044 additions and 150 deletions

View File

@ -33,8 +33,8 @@ public class SinglePushController {
return "success";
}
@PostMapping("/pushTocid")
public CommonResult pushTocid(@RequestParam String userId, @RequestParam String message) {
@PostMapping("/pushTo")
public CommonResult pushTo(@RequestParam String userId, @RequestParam String message) {
log.info("pushTocid");
return accountSinglePushService.pushTocid(message, userId);
}

View File

@ -42,6 +42,8 @@ public class StateCode {
public static final int PRODUCT_STATE_NORMAL = 1001; //正常
public static final int PRODUCT_STATE_OFF_THE_SHELF = 1002; //下架
public static final int PRODUCT_STATE_OFF_THE_SHELF_UNCHECK = 1003; //同步数据的状态下架未分配商品
public static final int DEMAND_STATE_CONDUCT = 1000; //采购中
public static final int DEMAND_STATE_REJECT = 1030; //被驳回
public static final int DEMAND_STATE_EXAMINE = 1040; //审核中

View File

@ -26,6 +26,8 @@ public enum DicEnum {
YESORNO_0("0", "","yesOrno","是否","是否"),
YESORNO_1("1", "","yesOrno","是否","是否"),
GOODS_UN_SYNC_SX("1", "白条猪","unSyncGoodsSX","思迅非同步商品","白条猪"),
;
;
private String code;

View File

@ -14,6 +14,8 @@ import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.List;
import static com.baomidou.mybatisplus.annotation.FieldStrategy.NOT_EMPTY;
/**
* <p>
* 商品规格表
@ -36,24 +38,33 @@ public class ShopBaseProductSpec implements Serializable {
private Integer spec_id;
@ApiModelProperty(value = "规格类别名称")
@TableField(updateStrategy=NOT_EMPTY)
private String spec_name;
@ApiModelProperty(value = "规格类别注释")
@TableField(updateStrategy=NOT_EMPTY)
private String spec_remark;
@ApiModelProperty(value = "显示类型(ENUM): text-文字; image-图片")
@TableField(updateStrategy=NOT_EMPTY)
private String spec_format;
@ApiModelProperty(value = "排序")
@TableField(updateStrategy=NOT_EMPTY)
private Integer spec_order;
@ApiModelProperty(value = "规格分类编号:不是商品类型编号选择分类可关联到任意级分类。可以使用一级分类category_id只在后台快捷定位中起作用 - 不用新建分类表管理。")
@TableField(updateStrategy=NOT_EMPTY)
private Integer spec_category_id;
@ApiModelProperty(value = "系统内置(ENUM):1-是; 0-否 | 系统内置不可删除 默认安装中会添加一个默认颜色规格")
@TableField(updateStrategy=NOT_EMPTY)
private Integer spec_buildin;
@ApiModelProperty(value = "规格属性")
@TableField(exist = false)
private List<ShopProductSpecItem> specItems;
@ApiModelProperty(value = "所属店铺")
private Integer store_id;
}

View File

@ -1,6 +1,7 @@
package com.suisung.mall.common.modules.base;
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 io.swagger.annotations.ApiModel;
@ -11,6 +12,8 @@ import lombok.experimental.Accessors;
import java.io.Serializable;
import static com.baomidou.mybatisplus.annotation.FieldStrategy.NOT_EMPTY;
/**
* <p>
* 商品类型表-强调共性,类别cat是强调区别.
@ -33,39 +36,51 @@ public class ShopBaseProductType implements Serializable {
private Integer type_id;
@ApiModelProperty(value = "类型名称")
@TableField(updateStrategy=NOT_EMPTY)
private String type_name;
@ApiModelProperty(value = "备注")
@TableField(updateStrategy=NOT_EMPTY)
private String type_remark;
@ApiModelProperty(value = "是否启用辅助属性(ENUM):1-是; 0-否")
@TableField(updateStrategy=NOT_EMPTY)
private Integer type_is_assist;
@ApiModelProperty(value = "是否启用参数(ENUM):1-是; 0-否")
@TableField(updateStrategy=NOT_EMPTY)
private Integer type_is_param;
@ApiModelProperty(value = "是否启用品牌(ENUM):1-是; 0-否")
@TableField(updateStrategy=NOT_EMPTY)
private Integer type_is_brand;
@ApiModelProperty(value = "是否启用实体商品(ENUM):1-是; 0-否")
@TableField(updateStrategy=NOT_EMPTY)
private Integer type_is_entity;
@ApiModelProperty(value = "分类编号-可关联到任意级分类。可以使用一级分类category_id只在后台快捷定位中起作用 - 要取消快捷定位功能通过UI交互来更好实现")
@TableField(updateStrategy=NOT_EMPTY)
private Integer type_category_id;
@ApiModelProperty(value = "是否草稿(ENUM):1-草稿;0-发布")
@TableField(updateStrategy=NOT_EMPTY)
private Integer type_is_draft;
@ApiModelProperty(value = "规格id(DOT)")
@TableField(updateStrategy=NOT_EMPTY)
private String type_spec_ids;
@ApiModelProperty(value = "品牌编号")
@TableField(updateStrategy=NOT_EMPTY)
private String type_brand_ids;
@ApiModelProperty(value = "辅助属性(DOT)")
@TableField(updateStrategy=NOT_EMPTY)
private String type_assist_ids;
@ApiModelProperty(value = "系统内置(ENUM):1-内置; 0-非内置")
@TableField(updateStrategy=NOT_EMPTY)
private Integer type_buildin;
}

View File

@ -15,6 +15,8 @@ import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import static com.baomidou.mybatisplus.annotation.FieldStrategy.NOT_EMPTY;
/**
* <p>
* 商品基础表-SPU表
@ -28,7 +30,7 @@ import java.util.List;
@Accessors(chain = true)
@TableName("shop_product_base")
@ApiModel(value = "ShopProductBase对象", description = "商品基础表-SPU表")
public class ShopProductBase implements Serializable {
public class ShopProductBase implements Serializable{
private static final long serialVersionUID = 1L;
@ -76,9 +78,11 @@ public class ShopProductBase implements Serializable {
private Integer layout_route_id;
@ApiModelProperty(value = "商品审核(ENUM):3001-审核通过;3002-审核中;3000-审核未通过")
@TableField(updateStrategy=NOT_EMPTY)
private Integer product_verify_id;
@ApiModelProperty(value = "商品状态(LIST):1001-正常;1002-下架仓库中;1000-违规禁售")
@TableField(updateStrategy=NOT_EMPTY)
private Integer product_state_id;
@ApiModelProperty(value = "库存锁定(ENUM):1001-下单锁定;1002-支付锁定;")
@ -130,5 +134,21 @@ public class ShopProductBase implements Serializable {
@TableField(exist = false)
private List<ShopProductItem> productItems;
//在同步数据的时候生鲜需要按平台的规格拆分售卖这些主要记录原始数据总重量一般为kgKG公斤
@ApiModelProperty(value = "总量单位")
private String unit_name;
@ApiModelProperty(value = "总重量")
@TableField(updateStrategy=NOT_EMPTY)
private BigDecimal shop_weight;
@ApiModelProperty(value = "单价")
@TableField(updateStrategy=NOT_EMPTY)
private BigDecimal unit_price;
@ApiModelProperty(value = "商品分类编号,通过type决定规格但是分类下的规格值都不同")
@TableField(exist = false)
private Integer categoryId;
}

View File

@ -30,7 +30,7 @@ import java.util.Date;
@Accessors(chain = true)
@TableName("shop_product_comment")
@ApiModel(value = "ShopProductComment对象", description = "商品评价表")
public class ShopProductComment implements Serializable {
public class ShopProductComment implements Serializable{
private static final long serialVersionUID = 1L;

View File

@ -1,6 +1,7 @@
package com.suisung.mall.common.modules.product;
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 io.swagger.annotations.ApiModel;
@ -12,6 +13,8 @@ import lombok.experimental.Accessors;
import java.io.Serializable;
import java.math.BigDecimal;
import static com.baomidou.mybatisplus.annotation.FieldStrategy.NOT_EMPTY;
/**
* <p>
* 商品信息表
@ -34,54 +37,74 @@ public class ShopProductInfo implements Serializable {
private Long product_id;
@ApiModelProperty(value = "SPU商家编码:货号")
@TableField(updateStrategy=NOT_EMPTY)
private String product_number;
@ApiModelProperty(value = "商品条形码")
@TableField(updateStrategy=NOT_EMPTY)
private String product_barcode;
@ApiModelProperty(value = "商品型号")
@TableField(updateStrategy=NOT_EMPTY)
private String product_model;
@ApiModelProperty(value = "属性(JSON) - 辅助属性及VAL")
@TableField(updateStrategy=NOT_EMPTY)
private String product_assist;
@ApiModelProperty(value = "规格(JSON)-规格、规格值、goods_id")
@TableField(updateStrategy=NOT_EMPTY)
private String product_spec;
@ApiModelProperty(value = "规格类别编号")
@TableField(updateStrategy=NOT_EMPTY)
private String spec_ids;
@ApiModelProperty(value = "商品SKU(JSON):{'uniq_id':[item_id, price, url]}")
@TableField(updateStrategy=NOT_EMPTY)
private String product_uniqid;
@ApiModelProperty(value = "长度单位")
@TableField(updateStrategy=NOT_EMPTY)
private Integer unit_length_id;
@ApiModelProperty(value = "重量单位")
@TableField(updateStrategy=NOT_EMPTY)
private Integer unit_weight_id;
@ApiModelProperty(value = "商品重量")
@TableField(updateStrategy=NOT_EMPTY)
private BigDecimal product_weight;
@ApiModelProperty(value = "尺寸长 x 宽 x 高(DOT):商品体积")
private String product_cubage;
@ApiModelProperty(value = "扣减库存")
@TableField(updateStrategy=NOT_EMPTY)
private Integer product_subtract;
@ApiModelProperty(value = "计量单位")
@TableField(updateStrategy=NOT_EMPTY)
private Integer unit_type_id;
@ApiModelProperty(value = "商品税别:商品税率可以按照商品分类来设置")
@TableField(updateStrategy=NOT_EMPTY)
private Integer tax_class_id;
@ApiModelProperty(value = "每人限购")
@TableField(updateStrategy=NOT_EMPTY)
private Integer product_buy_limit;
@ApiModelProperty(value = "参加会员等级折扣")
@TableField(updateStrategy=NOT_EMPTY)
private Integer product_user_level_discount;
@ApiModelProperty(value = "参加店铺发放的会员卡折扣")
@TableField(updateStrategy=NOT_EMPTY)
private Integer product_shop_card_discount;
// @ApiModelProperty(value = "产品名称")
// @TableField(exist=false)
// private String productName;
}

View File

@ -1,9 +1,6 @@
package com.suisung.mall.common.modules.product;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.Version;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@ -13,6 +10,8 @@ import lombok.experimental.Accessors;
import java.io.Serializable;
import java.math.BigDecimal;
import static com.baomidou.mybatisplus.annotation.FieldStrategy.NOT_EMPTY;
/**
* <p>
* 商品表-SKU表 商品名称产品名称+颜色规格名称 =shop_product_item
@ -47,27 +46,34 @@ public class ShopProductItem implements Serializable {
private Long color_id;
@ApiModelProperty(value = "是否为默认展示的商品-列表页展示必须为item_enable")
@TableField(updateStrategy=NOT_EMPTY)
private Integer item_is_default;
@ApiModelProperty(value = "SKU商家编码:SKU商家编码为非必填项若不填写系统会自动生成一个SKU商家编码。")
@TableField(updateStrategy=NOT_EMPTY)
private String item_number;
@ApiModelProperty(value = "条形码")
@TableField(updateStrategy=NOT_EMPTY)
private String item_barcode;
@ApiModelProperty(value = "成本价")
@TableField(updateStrategy=NOT_EMPTY)
private BigDecimal item_cost_price;
@ApiModelProperty(value = "平台价")
@TableField(updateStrategy=NOT_EMPTY)
private BigDecimal item_platform_price;
@ApiModelProperty(value = "商品建议零售价")
private BigDecimal item_advice_price;
@ApiModelProperty(value = "商品价格")
@TableField(updateStrategy=NOT_EMPTY)
private BigDecimal item_unit_price;
@ApiModelProperty(value = "市场价")
@TableField(updateStrategy=NOT_EMPTY)
private BigDecimal item_market_price;
@ApiModelProperty(value = "积分价格")
@ -77,24 +83,31 @@ public class ShopProductItem implements Serializable {
private BigDecimal item_unit_sp;
@ApiModelProperty(value = "商品库存")
@TableField(updateStrategy=NOT_EMPTY)
private Integer item_quantity;
@ApiModelProperty(value = "商品冻结库存")
@TableField(updateStrategy=NOT_EMPTY)
private Integer item_quantity_frozen;
@ApiModelProperty(value = "库存预警值")
@TableField(updateStrategy=NOT_EMPTY)
private Integer item_warn_quantity;
@ApiModelProperty(value = "商品规格序列化(JSON):{spec_id:spec_item_id, spec_id:spec_item_id, spec_id:spec_item_id}")
@TableField(updateStrategy=NOT_EMPTY)
private String item_spec;
@ApiModelProperty(value = "商品规格值编号")
@TableField(updateStrategy=NOT_EMPTY)
private String spec_item_ids;
@ApiModelProperty(value = "是否启用(LIST):1001-正常;1002-下架仓库中;1000-违规禁售")
@TableField(updateStrategy=NOT_EMPTY)
private Integer item_enable;
@ApiModelProperty(value = "被改动(BOOL):0-未改动;1-已改动分销使用")
@TableField(updateStrategy=NOT_EMPTY)
private Integer item_is_change;
@ApiModelProperty(value = "商品重量")
@ -160,4 +173,14 @@ public class ShopProductItem implements Serializable {
@Version
@ApiModelProperty(value = "乐观锁")
private Integer version;
// @ApiModelProperty(value = "产品名称")
// @TableField(exist=false)
// private String productName;
// @ApiModelProperty(value = "商品SKU(JSON):{'uniq_id':[item_id, price, url]}")
// @TableField(exist=false)
// private String product_uniqid;
}

View File

@ -1,6 +1,7 @@
package com.suisung.mall.common.modules.product;
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 io.swagger.annotations.ApiModel;

View File

@ -1,6 +1,7 @@
package com.suisung.mall.common.modules.product;
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 io.swagger.annotations.ApiModel;
@ -10,6 +11,7 @@ import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* <p>
@ -50,5 +52,24 @@ public class ShopProductSpecItem implements Serializable {
@ApiModelProperty(value = "是否启用(BOOL):0-不显示;1-显示")
private Integer spec_item_enable;
@ApiModelProperty(value = "切割后的价格")
@TableField(exist=false)
private BigDecimal itemPrice;
@ApiModelProperty(value = "切割后的库存")
@TableField(exist=false)
private BigDecimal itemQuantity;
@ApiModelProperty(value = "规格值")
@TableField(exist=false)
private String item_spec;
@ApiModelProperty(value = "规格值")
@TableField(exist=false)
private String product_spec;
@ApiModelProperty(value = "是否新增,批次使用")
@TableField(exist=false)
private boolean isUpdate;
}

View File

@ -0,0 +1,68 @@
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 io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
import static com.baomidou.mybatisplus.annotation.FieldStrategy.NOT_EMPTY;
@Data
@TableName("product_mapping")
@ApiModel("商品映射实体")
public class ProductMapping implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id",type = IdType.AUTO)
@ApiModelProperty("主键ID")
private Long id;
@TableField(value = "product_name",updateStrategy=NOT_EMPTY)
@NotEmpty(message="商品名称不能为空")
@ApiModelProperty("商品名称")
private String productName;
@TableField(value ="store_id",updateStrategy=NOT_EMPTY)
@ApiModelProperty(value = "店铺编号")
@NotNull(message="店铺编号不能为空")
private Integer storeId;
@TableField(value ="spec_value",updateStrategy=NOT_EMPTY)
@ApiModelProperty("规格数据")
@NotNull(message="规格数据不能为空")
private BigDecimal specValue;
@TableField(value ="spec_unit",updateStrategy=NOT_EMPTY)
@ApiModelProperty("规格单位")
@NotEmpty(message="规格单位不能为空")
private String specUnit;
@TableField(value ="description",updateStrategy=NOT_EMPTY)
@ApiModelProperty("商品描述")
private String description;
@TableField(value = "sort_order",updateStrategy=NOT_EMPTY)
@ApiModelProperty("排序值")
private Integer sortOrder;
/**
* 生成唯一键productName + storeId + specValue + specUnit
*/
public String getUniqueKey() {
return String.format("%s|%d|%s|%s",
productName,
storeId,
specValue.stripTrailingZeros().toPlainString(),
specUnit);
}
}

View File

@ -4,10 +4,12 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.suisung.mall.common.api.CommonResult;
import com.suisung.mall.common.domain.UserDto;
import com.suisung.mall.common.exception.ApiException;
import com.suisung.mall.common.modules.base.ShopBaseProductSpec;
import com.suisung.mall.common.modules.product.ShopProductSpecItem;
import com.suisung.mall.common.utils.CheckUtil;
import com.suisung.mall.common.utils.ContextUtil;
import com.suisung.mall.common.utils.I18nUtil;
import com.suisung.mall.shop.base.service.ShopBaseProductSpecService;
import com.suisung.mall.shop.product.service.ShopProductSpecItemService;
@ -54,6 +56,9 @@ public class ShopBaseProductSpecController {
@RequestParam(name = "pageNum", defaultValue = "1") Integer pageNum,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
QueryWrapper<ShopBaseProductSpec> queryWrapper = new QueryWrapper<>();
UserDto userDto= ContextUtil.getCurrentUser();
Integer storeId = Integer.valueOf(userDto.getStore_id());
queryWrapper.eq("store_id",storeId);
queryWrapper.orderByAsc("spec_id");
if (CheckUtil.isNotEmpty(shopBaseProductSpec.getSpec_name())) {
queryWrapper.like("spec_name", shopBaseProductSpec.getSpec_name());
@ -74,7 +79,10 @@ public class ShopBaseProductSpecController {
@RequestParam(value = "spec_category_id", required = false) Integer spec_category_id,
@RequestParam(value = "spec_order") Integer spec_order,
@RequestParam(value = "spec_id", required = false) Integer spec_id) {
UserDto userDto= ContextUtil.getCurrentUser();
Integer storeId = Integer.valueOf(userDto.getStore_id());
ShopBaseProductSpec shopBaseProductSpec = new ShopBaseProductSpec();
shopBaseProductSpec.setStore_id(storeId);
shopBaseProductSpec.setSpec_id(spec_id);
shopBaseProductSpec.setSpec_name(spec_name);
shopBaseProductSpec.setSpec_format(spec_format);
@ -92,19 +100,19 @@ public class ShopBaseProductSpecController {
@ApiOperation(value = "商品规格表-通过spec_id删除", notes = "商品规格表-通过spec_id删除")
@RequestMapping(value = "/delete", method = RequestMethod.POST)
public CommonResult delete(@RequestParam(name = "spec_ids") String spec_ids) {
List<Integer> specIds = Convert.toList(Integer.class, spec_ids);
if (CollUtil.isEmpty(specIds)) {
List<Integer> spes = Convert.toList(Integer.class, spec_ids);
if (CollUtil.isEmpty(spes)) {
throw new ApiException(I18nUtil._("商品规格编号异常spec_id: ") + spec_ids);
}
QueryWrapper<ShopProductSpecItem> queryWrapper = new QueryWrapper<>();
queryWrapper.in("spec_id", specIds);
queryWrapper.in("spec_id", spes);
List<Serializable> spec_item_ids = shopProductSpecItemService.findKey(queryWrapper);
if (CollUtil.isNotEmpty(spec_item_ids)) {
throw new ApiException(String.format(I18nUtil._("不能删除正在被商品规格值表使用的规格!商品规格值编号【%s】"), CollUtil.join(spec_item_ids, ",")));
}
return CommonResult.success(shopBaseProductSpecService.remove(specIds));
return CommonResult.success(shopBaseProductSpecService.remove(spes));
}
@ApiOperation(value = "商品规格表-获取分类规格参数", notes = "商品规格表-获取分类规格参数")

View File

@ -19,4 +19,9 @@ public interface ShopBaseProductSpecService extends IBaseService<ShopBaseProduct
Map<String, List<ShopBaseProductSpec>> specMap();
List<ShopBaseProductSpec> getSpecsByIds(String type_spec_ids, Integer store_id);
Map getShopBaseProductSpecMap(Integer store_id);
void clearShopBaseProductSpecMap(Integer store_id);
}

View File

@ -1,23 +1,25 @@
package com.suisung.mall.shop.base.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.suisung.mall.common.domain.UserDto;
import com.suisung.mall.common.modules.base.ShopBaseProductCategory;
import com.suisung.mall.common.modules.base.ShopBaseProductSpec;
import com.suisung.mall.common.modules.product.ShopProductSpecItem;
import com.suisung.mall.common.utils.ContextUtil;
import com.suisung.mall.common.utils.I18nUtil;
import com.suisung.mall.core.web.service.RedisService;
import com.suisung.mall.core.web.service.impl.BaseServiceImpl;
import com.suisung.mall.shop.base.mapper.ShopBaseProductSpecMapper;
import com.suisung.mall.shop.base.service.ShopBaseProductCategoryService;
import com.suisung.mall.shop.base.service.ShopBaseProductSpecService;
import com.suisung.mall.shop.product.service.ShopProductSpecItemService;
import com.suisung.mall.shop.sync.keymanage.RedisKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
@ -37,9 +39,16 @@ public class ShopBaseProductSpecServiceImpl extends BaseServiceImpl<ShopBaseProd
@Autowired
private ShopProductSpecItemService itemService;
@Autowired
private RedisService redisService;
@Override
public Map<String, List<ShopBaseProductSpec>> specMap() {
List<ShopBaseProductSpec> specs = find(new QueryWrapper<>());
QueryWrapper<ShopBaseProductSpec> queryWrapper= new QueryWrapper<>();
UserDto userDto= ContextUtil.getCurrentUser();
Integer store_id = Integer.valueOf(userDto.getStore_id());
queryWrapper.eq("store_id",store_id);
List<ShopBaseProductSpec> specs = find(queryWrapper);
Map<String, List<ShopBaseProductSpec>> map = new HashMap<>();
for (ShopBaseProductSpec spec : specs) {
if (spec.getSpec_category_id() == 0) {
@ -82,4 +91,30 @@ public class ShopBaseProductSpecServiceImpl extends BaseServiceImpl<ShopBaseProd
});
return specs;
}
@Override
public Map getShopBaseProductSpecMap(Integer store_id) {
Map map=new HashMap<>();
String redisKey=RedisKey.STOREDATASHOPBASEPRODUCTSPEC+":"+store_id;
if(null!=redisService.get(redisKey)){
map= (Map) redisService.get(redisKey);
}else {
QueryWrapper<ShopBaseProductSpec> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("store_id",store_id);
List<ShopBaseProductSpec> shopBaseProductSpecList=this.list(queryWrapper);
for(ShopBaseProductSpec spec:shopBaseProductSpecList){
map.put(String.valueOf(spec.getSpec_category_id()),spec);
}
if (CollUtil.isNotEmpty(map)) redisService.set(redisKey, map, 60 * 60);
}
return map;
}
@Override
public void clearShopBaseProductSpecMap(Integer store_id) {
String redisKey=RedisKey.STOREDATASHOPBASEPRODUCTSPEC+":"+store_id;
if(null!=redisService.get(redisKey)){
redisService.del(redisKey);
}
}
}

View File

@ -85,8 +85,8 @@ public class ShopBaseProductTypeServiceImpl extends BaseServiceImpl<ShopBaseProd
data.put("assists", assistService.getAssistsByIds(shopBaseProductType.getType_assist_ids()));
data.put("brands", brandService.getBrandsByIds(shopBaseProductType.getType_brand_ids()));
Integer store_id = Convert.toInt(userInfoService.getUser().getStore_id());
// Integer store_id =1;
// Integer store_id = Convert.toInt(userInfoService.getUser().getStore_id());
Integer store_id =1;
data.put("specs", specService.getSpecsByIds(shopBaseProductType.getType_spec_ids(), store_id));

View File

@ -24,4 +24,9 @@ public interface ShopNumberSeqService extends IBaseService<ShopNumberSeq> {
void clearRelateGoodsId();
void clearKey();
void batchUpdateSeq(List<ShopNumberSeq> shopNumberSeqList);
List<Integer> getBatchSpecItemId(int batchSize);
void clearKeyStoreItemSepcId();
}

View File

@ -3,22 +3,24 @@ 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.common.modules.product.ShopProductSpecItem;
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 com.suisung.mall.shop.product.service.ShopProductSpecItemService;
import com.suisung.mall.shop.sync.keymanage.RedisKey;
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.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
@ -40,6 +42,9 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
private RedisService redisService;
private String CACHE_PREFIX = "shop_number_seq:%S";
@Autowired
private ShopProductSpecItemService shopProductSpecItemService;
/**
* 得到下一个Id
* 方法走到这里会产生串行化集群部署这里不能使用单机锁
@ -185,11 +190,17 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
}
log.info("更新成功{}条数据",count);
}
public void clearKey(){
public void clearKey(){
redisService.del(String.format(CACHE_PREFIX,"item_id"));
redisService.del(String.format(CACHE_PREFIX,"product_id"));
}
public void clearKeyStoreItemSepcId(){
redisService.del(RedisKey.STOREDATASPECITEMID);
}
/**
* 清除缓存专门给并发使用,防止redis的缓存没有加载
*/
@ -223,4 +234,35 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
}
edit(seq);
}
/**
* 存的是最大值取的是范围如存最大值1批量是2则取范围1+11+2,如果没有则从1开始算即取范围[0+1,0+2]
* @param batchSize
* @return
*/
@Override
public synchronized List<Integer> getBatchSpecItemId(int batchSize) {
int start=0;
if(null!=redisService.get(RedisKey.STOREDATASPECITEMID)){
start=(Integer) redisService.get(RedisKey.STOREDATASPECITEMID);
redisService.set(RedisKey.STOREDATASPECITEMID,start+batchSize);
}
QueryWrapper<ShopProductSpecItem> queryWrapper= new QueryWrapper<>();
queryWrapper.select("max(spec_item_id) as spec_item_id");
ShopProductSpecItem shopProductSpecItem=shopProductSpecItemService.getOne(queryWrapper);
if(null!=shopProductSpecItem){
start=shopProductSpecItem.getSpec_item_id();
redisService.set(RedisKey.STOREDATASPECITEMID,start+batchSize);
}
if(start==0){
redisService.set(RedisKey.STOREDATASPECITEMID,start+batchSize);
return IntStream.rangeClosed(start+1, start+batchSize).boxed().collect(Collectors.toList());
}
return IntStream.rangeClosed(start+1, start+batchSize).boxed().collect(Collectors.toList());
}
public static void main(String[] args) {
System.out.printf(IntStream.rangeClosed(1, 1).boxed().collect(Collectors.toList()).toString());
}
}

View File

@ -66,7 +66,8 @@ public class ShopProductSpecItemController extends BaseControllerImpl {
public CommonResult specItems(ShopProductSpecItem shopProductSpecItem) {
UserDto user = getCurrentUser();
Integer store_id = Convert.toInt(user.getStore_id());
//Integer store_id = Convert.toInt(user.getStore_id());
Integer store_id = 1;
QueryWrapper<ShopProductSpecItem> queryWrapper = new QueryWrapper<>();
Integer spec_id = shopProductSpecItem.getSpec_id();
@ -86,7 +87,8 @@ public class ShopProductSpecItemController extends BaseControllerImpl {
@RequestMapping(value = "/edit", method = RequestMethod.POST)
public CommonResult edit(ShopProductSpecItem shopProductSpecItem) {
UserDto user = getCurrentUser();
shopProductSpecItem.setStore_id(Integer.valueOf(user.getStore_id()));
// shopProductSpecItem.setStore_id(Integer.valueOf(user.getStore_id()));
shopProductSpecItem.setStore_id(1);
return CommonResult.success(shopProductSpecItemService.saveOrUpdate(shopProductSpecItem));
}

View File

@ -5,6 +5,7 @@ import com.suisung.mall.common.modules.product.ShopProductSpecItem;
import com.suisung.mall.core.web.service.IBaseService;
import java.util.List;
import java.util.Map;
/**
* <p>
@ -17,4 +18,8 @@ import java.util.List;
public interface ShopProductSpecItemService extends IBaseService<ShopProductSpecItem> {
List<ShopProductSpecItem> getItems(QueryWrapper<ShopProductSpecItem> queryWrapper);
Map<String,Integer> getExistItem(Integer storeId);
void clearExistItem(Integer storeId);
}

View File

@ -38,6 +38,7 @@ import com.suisung.mall.common.modules.sixun.SxSyncGoods;
import com.suisung.mall.common.modules.store.ShopStoreActivityBase;
import com.suisung.mall.common.modules.store.ShopStoreActivityItem;
import com.suisung.mall.common.modules.store.ShopStoreBase;
import com.suisung.mall.common.modules.sync.ProductMapping;
import com.suisung.mall.common.modules.user.ShopUserCart;
import com.suisung.mall.common.modules.user.ShopUserFavoritesItem;
import com.suisung.mall.common.modules.user.ShopUserProductBrowse;
@ -65,13 +66,12 @@ 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.sync.Utils.ProductPriceCalculator;
import com.suisung.mall.shop.sync.Utils.ShopJsonUtils;
import com.suisung.mall.shop.sync.service.ProductMappingService;
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;
@ -83,8 +83,6 @@ 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;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Serializable;
@ -210,8 +208,11 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl<ShopProductBaseM
@Autowired
private PlatformTransactionManager transactionManager;
@Autowired
private SqlSessionFactory sqlSessionFactory;
private ProductMappingService productMappingService;
@Override
public boolean trySaveProduct(String productObj, String productItems) {
@ -459,14 +460,16 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl<ShopProductBaseM
spec_rows = JSONArray.parseArray(JSON.toJSONString(productSpecList));
}
for (Object spec_row : spec_rows) {
JSONObject _spec_row = (JSONObject) spec_row;
String spec_format = Convert.toStr(_spec_row.get("spec_format"));
if ("image".equals(spec_format)) {
// 存在图片规格
spec_img_flag = true;
buildin_spec_id = Convert.toInt(_spec_row.get("id"), Convert.toInt(_spec_row.get("spec_id"), 0));
break;
if(null!=spec_rows){
for (Object spec_row : spec_rows) {
JSONObject _spec_row = (JSONObject) spec_row;
String spec_format = Convert.toStr(_spec_row.get("spec_format"));
if ("image".equals(spec_format)) {
// 存在图片规格
spec_img_flag = true;
buildin_spec_id = Convert.toInt(_spec_row.get("id"), Convert.toInt(_spec_row.get("spec_id"), 0));
break;
}
}
}
}
@ -5313,7 +5316,6 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl<ShopProductBaseM
// 已存在商品设置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);
@ -5357,13 +5359,11 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl<ShopProductBaseM
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);//下架状态
// 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()));
@ -5376,7 +5376,7 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl<ShopProductBaseM
shopProductIndexList.get(i).setStore_type(1);
shopProductIndexList.get(i).setSubsite_id(0);
shopProductIndexList.get(i).setProduct_number(base.getProduct_number());
base.setProduct_market_price(base.getProduct_unit_price());
if(CollUtil.isNotEmpty(shopProductIndexList)){
newShopProductIndexList.add(shopProductIndexList.get(i));
}
@ -5447,8 +5447,11 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl<ShopProductBaseM
List<Serializable> udpateItemIds = new ArrayList<>();
List<ShopProductItem> addShopProductItems=new ArrayList<>();
List<ShopProductItem> updateShopProductItems=new ArrayList<>();
int taskCount = 2;
CountDownLatch latch = new CountDownLatch(taskCount);
List<ShopProductSpecItem> addShopProductSpecItems=new ArrayList<>();
List<ShopProductSpecItem> updateShopProductSpecItems=new ArrayList<>();
// int taskCount = 2;
// CountDownLatch latch = new CountDownLatch(taskCount);
// 1. 批量新增
if (CollUtil.isNotEmpty(newProducts)) {
// 4. 批量生成新商品的ID
@ -5466,7 +5469,16 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl<ShopProductBaseM
newShopProductDataList.get(i).setProduct_id(productId);
newShopProductDetailList.get(i).setProduct_id(productId);
newShopProductInfoList.get(i).setProduct_id(productId);
// newShopProductInfoList.get(i).setProductName(newProducts.get(i).getProduct_name());
//计算规格
// ShopProductSpecItem shopProductSpecItem=processShopProductSpecItem(base);
// if(null!=shopProductSpecItem){
// if(shopProductSpecItem.isUpdate()){
// updateShopProductSpecItems.add(shopProductSpecItem);
// }else {
// addShopProductSpecItems.add(shopProductSpecItem);
// }
// }
// 处理商品项
List<ShopProductItem> items = newShopProductItemList.get(i);
processProductItems(items, productId, addItemSeqs);
@ -5488,11 +5500,10 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl<ShopProductBaseM
shopProductAnalytics.setProduct_click(0);
addAnalyticsList.add(shopProductAnalytics);
}
List<Serializable> itemIds = processProductItemsId(addShopProductItems, addItemSeqs);
List<Serializable> itemIds = processProductItemsId(addShopProductItems, addItemSeqs,newShopProductInfoList);
addItemIds.addAll(itemIds);
// 2. 批量更新
if (CollUtil.isNotEmpty(newProducts)) {
// new Thread(() -> {
logger.info("保存任务开始执行");
try {
long startTime=System.nanoTime();
@ -5500,17 +5511,10 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl<ShopProductBaseM
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<updateProducts.size();i++){
@ -5524,7 +5528,16 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl<ShopProductBaseM
updateShopProductDataList.get(i).setProduct_id(productId);
updateShopProductDetailList.get(i).setProduct_id(productId);
updateShopProductInfoList.get(i).setProduct_id(productId);
//updateShopProductInfoList.get(i).setProductName(updateProducts.get(i).getProduct_name());
//计算规格
// ShopProductSpecItem shopProductSpecItem=processShopProductSpecItem(base);
// if(null!=shopProductSpecItem){
// if(shopProductSpecItem.isUpdate()){
// updateShopProductSpecItems.add(shopProductSpecItem);
// }else {
// addShopProductSpecItems.add(shopProductSpecItem);
// }
// }
// 处理商品项
List<ShopProductItem> items = udpateShopProductItemList.get(i);
processProductItems(items, productId, updateItemSeqs);//
@ -5548,11 +5561,10 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl<ShopProductBaseM
shopProductAnalytics.setProduct_click(0);
udpateAnalyticsList.add(shopProductAnalytics);
}
List<Serializable> itemIds = processProductItemsId(updateShopProductItems, updateItemSeqs);
List<Serializable> itemIds = processProductItemsId(updateShopProductItems, updateItemSeqs,newShopProductInfoList);
udpateItemIds.addAll(itemIds);
// 2. 批量更新
if (CollUtil.isNotEmpty(updateProducts)) {
// new Thread(() -> {
logger.info("更新任务开始执行");
try {
long startTime=System.nanoTime();
@ -5560,25 +5572,25 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl<ShopProductBaseM
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());
//计算规格
if(CollUtil.isNotEmpty(newProducts)){
productMappingService.computeProductMapping(newProducts,newProducts.get(0).getStore_id(),false);
}
if(CollUtil.isNotEmpty(updateProducts)){
productMappingService.computeProductMapping(updateProducts,updateProducts.get(0).getStore_id(),true);
}
return Pair.of(true, String.format("处理成功,新增%d条更新%d条",
newProducts.size(), updateProducts.size()));
} catch (RuntimeException e) {
@ -5586,14 +5598,7 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl<ShopProductBaseM
logger.error("批量操作异常", e);
return Pair.of(false, "批量操作异常: " + e.getMessage());
}
// catch (InterruptedException e) {
// status.setRollbackOnly();
// logger.error("批量操作异常", e);
// return Pair.of(false, "批量操作异常: " + e.getMessage());
// }
});
}
// 批量保存操作
@ -5784,6 +5789,7 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl<ShopProductBaseM
logger.info("更新updateShopProductItemSeqList-{}条数据耗时:{}",updateShopProductItemSeqList.size(),duration/1000000000);
// }
}
}catch (RuntimeException e){
logger.info("批量报错附表失败{}",e.getMessage());
}
@ -5801,35 +5807,104 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl<ShopProductBaseM
* @param itemSeqs
* @return
*/
private List<Serializable> processProductItemsId(List<ShopProductItem> items,List<ShopProductItemSeq> itemSeqs){
private List<Serializable> processProductItemsId(List<ShopProductItem> items,List<ShopProductItemSeq> itemSeqs,
List<ShopProductInfo> newShopProductInfoList){
List<Serializable> itemIds = new ArrayList<>();
if (CollUtil.isEmpty(items)) return itemIds;
List<Long> generatedIds = shopNumberSeqService.batchCreateNextNo("item_id", items.size());
// Map<String,String> cacheMap=new HashMap<>();
for (int i = 0; i < items.size(); i++) {
Long itemId = generatedIds.get(i);
ShopProductItem item = items.get(i);
item.setItem_id(itemId);
// if(StringUtils.isNotEmpty(item.getItem_spec())){
// String product_uniqid=ShopJsonUtils.generateJsonWithOrgJson(item.getSpec_item_ids(),new Object[]{itemId,item.getItem_unit_price(),"",1002});
// cacheMap.put(item.getProductName(),product_uniqid);
// }
itemIds.add(itemId);
ShopProductItemSeq field_row= itemSeqs.get(i);
field_row.setItem_id(itemId);
}
// for(ShopProductInfo shopProductInfo:newShopProductInfoList){
// if(ObjectUtil.isNotEmpty(cacheMap.get(shopProductInfo.getProductName()))){
// String product_uniqid=cacheMap.get(shopProductInfo.getProductName());
// shopProductInfo.setProduct_uniqid(product_uniqid);
// }
// }
return itemIds;
}
// /**
// * 计算并产生规格 与商品配置表匹配匹配正确则新增规格商品
// * @return
// */
// private ShopProductSpecItem processShopProductSpecItem(ShopProductBase base){
// Map shopProductSpecItemMap = shopProductSpecItemService.getExistItem(base.getStore_id());
// Map productMappingMap = productMappingService.getProductMapping();
// Map ShopBaseProductSpecMap = baseProductSpecService.getShopBaseProductSpecMap(base.getStore_id());
// String productName=base.getProduct_name();
//
// if(null!=productMappingMap.get(productName)){
// Integer specId= (Integer) ShopBaseProductSpecMap.get(base.getProduct_name()+"规格");
// ProductMapping productMapping= (ProductMapping) productMappingMap.get(productName);
// String Spec_item_name=productMapping.getProductName()+productMapping.getSpecValue()+productMapping.getSpecUnit();//规格名称
// Integer Spec_item_id= (Integer) shopProductSpecItemMap.get(Spec_item_name);
// ShopProductSpecItem addShopProductSpecItem=new ShopProductSpecItem();
// addShopProductSpecItem.setUpdate(true);
// if(ObjectUtil.isEmpty(Spec_item_id)){
// Spec_item_id=shopNumberSeqService.getSpecItemId();
// addShopProductSpecItem.setUpdate(false);
// }
// addShopProductSpecItem.setStore_id(base.getStore_id());
// addShopProductSpecItem.setCategory_id(base.getCategoryId());
// addShopProductSpecItem.setSpec_item_id(Spec_item_id);
// addShopProductSpecItem.setSpec_id(specId);//根据规格获取
// addShopProductSpecItem.setSpec_item_name(Spec_item_name);
// addShopProductSpecItem.setSpec_item_enable(1);//上架
// addShopProductSpecItem.setSpec_item_order("10");
// BigDecimal[] bigDecimals= ProductPriceCalculator.calculatePriceAndQuantity(base.getUnit_price(),base.getShop_weight(),productMapping.getSpecValue(),productMapping.getSpecUnit());
// addShopProductSpecItem.setItemPrice(bigDecimals[0]);
// addShopProductSpecItem.setItemQuantity(bigDecimals[1]);
// List<Map<String, Object>> items=new ArrayList<>();
// Map<String,Object> item=new HashMap<>();
// item.put("name",addShopProductSpecItem.getSpec_item_name());//规格列表
// item.put("id",Spec_item_id);
// items.add(item);
// List<Map<String, Object>> specItems=new ArrayList<>();
// Map<String,Object> specItem=new HashMap<>();
// specItem.put("name",base.getProduct_name()+"规格");//规格名称
// specItem.put("id",specId);
// specItems.add(specItem);
// String item_spec=ShopJsonUtils.generateJsonWithOrgJson(items,specItems);
// addShopProductSpecItem.setItem_spec(item_spec);
// // addShopProductSpecItem.setProduct_uniqid(product_uniqid);
// return addShopProductSpecItem;
// }
// return null;
// }
// 处理商品项
private void processProductItems(List<ShopProductItem> items, Long productId,
List<ShopProductItemSeq> itemSeqs) {
for (int i = 0; i < items.size(); i++) {
ShopProductItem item = items.get(i);
// item.setProductName(productName);
item.setProduct_id(productId);
String item_spec = item.getItem_spec();
// if(null!=shopProductSpecItem){
// item_spec=shopProductSpecItem.getItem_spec();
// if(isUpdate){
// item.setItem_unit_price(shopProductSpecItem.getItemPrice());
// }else {
// item.setItem_unit_price(shopProductSpecItem.getItemPrice());
// item.setItem_market_price(shopProductSpecItem.getItemPrice());
// }
// }
// itemIds.add(itemId);
// item.setItem_quantity(null);
// 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<Integer> spec_item_ids = new ArrayList<>();
List<String> item_names = new ArrayList<>();
@ -5840,12 +5915,9 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl<ShopProductBaseM
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;
@ -6080,16 +6152,5 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl<ShopProductBaseM
}
//
// private void myShopProductBaseBatch(List<ShopProductBase> list){
// SqlSession sqlSession=sqlSessionFactory.openSession();
// StopWatch stopWatch=new StopWatch();
// stopWatch.start();
// shopProductBaseService.saveBatch(list);
// sqlSession.commit();
// stopWatch.stop();
// }
}

View File

@ -1,13 +1,20 @@
package com.suisung.mall.shop.product.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.suisung.mall.common.modules.product.ShopProductSpecItem;
import com.suisung.mall.core.web.service.RedisService;
import com.suisung.mall.core.web.service.impl.BaseServiceImpl;
import com.suisung.mall.shop.product.mapper.ShopProductSpecItemMapper;
import com.suisung.mall.shop.product.service.ShopProductSpecItemService;
import com.suisung.mall.shop.sync.keymanage.RedisKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@ -20,8 +27,39 @@ import java.util.List;
*/
@Service
public class ShopProductSpecItemServiceImpl extends BaseServiceImpl<ShopProductSpecItemMapper, ShopProductSpecItem> implements ShopProductSpecItemService {
@Autowired
private RedisService redisService;
@Override
public List<ShopProductSpecItem> getItems(QueryWrapper<ShopProductSpecItem> queryWrapper) {
return find(queryWrapper);
}
@Override
public Map getExistItem(Integer storeId) {
Map map=new HashMap<>();
String redisKey=RedisKey.STOREDATAPRODUCTSPECITEM+"storeId:"+storeId;
if(null!=redisService.get(redisKey)){
map=(Map)redisService.get(redisKey);
return map;
}
QueryWrapper<ShopProductSpecItem> queryWrapper=new QueryWrapper<>();
queryWrapper.eq("store_id",storeId);
List<ShopProductSpecItem> list= list(queryWrapper);
for(ShopProductSpecItem shopProductSpecItem:list){
map.put(shopProductSpecItem.getSpec_item_name(),shopProductSpecItem.getSpec_item_id());
}
if (CollUtil.isNotEmpty(map)) redisService.set(redisKey, map, 60 * 60);
return map;
}
@Override
public void clearExistItem(Integer storeId) {
String redisKey=RedisKey.STOREDATAPRODUCTSPECITEM+"storeId:"+storeId;
if(null!=redisService.get(redisKey)){
redisService.del(redisKey);
}
}
}

View File

@ -0,0 +1,29 @@
package com.suisung.mall.shop.sync.Utils;
import java.math.BigDecimal;
public class BigDecimalFormatter {
public static String formatWithoutTrailingZeros(BigDecimal number) {
if (number == null) {
return null;
}
// 去除末尾0并转换为普通字符串表示
BigDecimal stripped = number.stripTrailingZeros();
String result = stripped.toPlainString();
// 处理结果为负0的情况返回"0"而不是"-0"
if (result.startsWith("-0")) {
if (result.indexOf('.') == -1) {
if (result.length() == 2) { // "-0"
return "0";
} else if (result.matches("-0+")) { // "-000"
return "0";
}
} else if (result.matches("-0\\.0*")) { // "-0.0" "-0.00"
return "0";
}
}
return result;
}
}

View File

@ -0,0 +1,94 @@
package com.suisung.mall.shop.sync.Utils;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class ProductPriceCalculator {
/**
* 计算切割后的商品单价和数量
* @param unitPricePerKg 千克单价/千克
* @param totalWeightKg 总重量千克
* @param pieceWeight 切割单位重量数值
* @param weightUnit 重量单位"g"="kg"=千克
* @return 包含两个元素的数组[切割后单价/单位, 完整单位数量]
*/
public static BigDecimal[] calculatePriceAndQuantity(
BigDecimal unitPricePerKg,
BigDecimal totalWeightKg,
BigDecimal pieceWeight,
String weightUnit) {
// 参数校验
if (unitPricePerKg == null || totalWeightKg == null || pieceWeight == null) {
throw new IllegalArgumentException("参数不能为空");
}
if (unitPricePerKg.compareTo(BigDecimal.ZERO) <= 0 ||
pieceWeight.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("单价和切割重量必须大于0");
}
if (!"g".equalsIgnoreCase(weightUnit) && !"kg".equalsIgnoreCase(weightUnit)) {
throw new IllegalArgumentException("单位必须是'g'或'kg'");
}
// 将切割单位统一转换为千克
BigDecimal pieceWeightInKg = "g".equalsIgnoreCase(weightUnit) ?
pieceWeight.divide(BigDecimal.valueOf(1000), 10, RoundingMode.HALF_UP) :
pieceWeight;
// 计算切割后单价不受总重量影响
BigDecimal piecePrice = unitPricePerKg.multiply(pieceWeightInKg)
.setScale(2, RoundingMode.HALF_UP); // 保留两位小数
// 特殊处理总重量为负数时数量固定返回99
if (totalWeightKg.compareTo(BigDecimal.ZERO) < 0) {
return new BigDecimal[]{piecePrice, BigDecimal.valueOf(99)};
}
// 计算完整单位数量向下取整
BigDecimal quantity = totalWeightKg.divide(pieceWeightInKg, 0, RoundingMode.FLOOR);
return new BigDecimal[]{piecePrice, quantity};
}
// 示例用法
public static void main(String[] args) {
// 正常情况示例
BigDecimal[] result1 = calculatePriceAndQuantity(
new BigDecimal("30"),
new BigDecimal("5"),
new BigDecimal("500"),
"g");
System.out.println("正常情况 - 单价: " + result1[0] + " 元/单位, 数量: " + result1[1]);
// 总重量为负数示例
BigDecimal[] result2 = calculatePriceAndQuantity(
new BigDecimal("25"),
new BigDecimal("-3.5"), // 负数总重量
new BigDecimal("250"),
"g");
System.out.println("负总重量 - 单价: " + result2[0] + " 元/单位, 数量: " + result2[1]);
// 千克单位示例
BigDecimal[] result3 = calculatePriceAndQuantity(
new BigDecimal("100"),
new BigDecimal("2.5"),
new BigDecimal("0.5"),
"kg");
System.out.println("千克单位 - 单价: " + result3[0] + " 元/单位, 数量: " + result3[1]);
// 总重量不足一个单位示例
BigDecimal[] result4 = calculatePriceAndQuantity(
new BigDecimal("40"),
new BigDecimal("0.3"),
new BigDecimal("0.5"),
"kg");
System.out.println("不足单位 - 单价: " + result4[0] + " 元/单位, 数量: " + result4[1]);
/* 预期输出
正常情况 - 单价: 15.00 /单位, 数量: 10
负总重量 - 单价: 6.25 /单位, 数量: 99
千克单位 - 单价: 50.00 /单位, 数量: 5
不足单位 - 单价: 20.00 /单位, 数量: 0
*/
}
}

View File

@ -0,0 +1,87 @@
package com.suisung.mall.shop.sync.Utils;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.List;
import java.util.Map;
public class ShopJsonUtils {
/**
*shop_item的item_spec生成
* 使用org.json库需要org.json依赖
* @param items 规格项列表
* @param specItems 规格大类
* @return JSON字符串
*/
public static String generateJsonWithOrgJsonItemSpec(List<Map<String, Object>> items,
List<Map<String, Object>> specItems) {
JSONArray jsonArray = new JSONArray();
for (int i = 0; i < Math.min(items.size(), specItems.size()); i++) {
JSONObject itemObj = new JSONObject();
itemObj.put("name", items.get(i).get("name"));
itemObj.put("id", items.get(i).get("id"));
JSONObject categoryObj = new JSONObject();
categoryObj.put("item", itemObj);
categoryObj.put("name", specItems.get(i).get("name"));
categoryObj.put("id", specItems.get(i).get("id"));
jsonArray.put(categoryObj);
}
return jsonArray.toString();
}
/**
*shop_index的product_spec生成
* 使用org.json库需要org.json依赖
* @param items 规格项列表
* @param specItems 规格大类
* @return JSON字符串
*/
public static String generateJsonWithOrgJsonProducSpec(List<Map<String, Object>> items,
List<Map<String, Object>> specItems) {
JSONArray jsonArray = new JSONArray();
for (int i = 0; i < Math.min(items.size(), specItems.size()); i++) {
JSONArray specItemObj = new JSONArray();
JSONObject itemObj = new JSONObject();
itemObj.put("name", items.get(i).get("name"));
itemObj.put("id", items.get(i).get("id"));
specItemObj.put(itemObj);
JSONObject categoryObj = new JSONObject();
categoryObj.put("item", specItemObj);
categoryObj.put("name", specItems.get(i).get("name"));
categoryObj.put("id", specItems.get(i).get("id"));
jsonArray.put(categoryObj);
}
return jsonArray.toString();
}
/**
* shop_index 的product_uniqid生成
* 使用org.json库生成JSON无需Gson依赖
* @param key 主键 规格列表id
* @param values 值数组
* @return JSON字符串
*/
public static String generateJsonWithOrgJson(String key, Object[] values) {
// 参数校验
if (values == null || values.length != 4) {
throw new IllegalArgumentException("值数组必须包含4个元素");
}
JSONObject jsonObj = new JSONObject();
org.json.JSONArray jsonArray = new org.json.JSONArray();
jsonArray.put(values[0]); // 整数
jsonArray.put(values[1]); // 浮点数
jsonArray.put(values[2]); // 字符串
jsonArray.put(values[3]); // 整数
jsonObj.put(key, jsonArray);
return jsonObj.toString();
}
}

View File

@ -18,7 +18,7 @@ public class ClientController {
@Autowired
private SyncThirdDataService syncThirdDataService;
@ApiOperation(value = "下载客户端应用", notes = "获取加密密钥")
@ApiOperation(value = "下载客户端应用", notes = "下载客户端应用")
@RequestMapping(value = "/downClientJar",produces = MediaType.APPLICATION_OCTET_STREAM_VALUE, method = RequestMethod.GET)
public ResponseEntity<Resource> downClientJar(@RequestParam String primaryKey,String clienVersionName) {
return syncThirdDataService.downloadToClient(primaryKey,clienVersionName);

View File

@ -0,0 +1,220 @@
package com.suisung.mall.shop.sync.controller;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONArray;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.suisung.mall.common.api.CommonResult;
import com.suisung.mall.common.api.IErrorCode;
import com.suisung.mall.common.modules.sync.ProductMapping;
import com.suisung.mall.common.service.impl.BaseControllerImpl;
import com.suisung.mall.shop.sync.exelModel.ImportResult;
import com.suisung.mall.shop.sync.service.ProductMappingService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;
@RestController
@RequestMapping("/admin/shop/shop-sync-productMapper")
@lombok.extern.slf4j.Slf4j
public class ProductMappingController extends BaseControllerImpl {
@Autowired
private ProductMappingService productMappingService;
@Value("${file.upload-dir}")
private String uploadDir;
/**
* 分页列表查询
* @param pageNum
* @param pageSize
* @return
*/
@ApiOperation(value = "列表查询-分页列表查询商品映射实体", notes = "列表查询-分页列表查询商品映射实体")
@RequestMapping(value = "/list", method = RequestMethod.GET)
public CommonResult list(@RequestParam(name = "pageNum", defaultValue = "1") Integer pageNum,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
ProductMapping productMapping=new ProductMapping();
productMapping.setProductName(getParameter("productName"));
productMapping.setStoreId(getParameter("storeId",Integer.class));
return productMappingService.findPageProductMapping(productMapping,pageNum,pageSize);
}
/**
* 单笔获取
* @param productMapping
* @return
*/
@ApiOperation(value = "单笔获取-商品映射实体", notes = "单笔获取-商品映射实体")
@RequestMapping(value = "/getProductMapping", method = RequestMethod.GET)
public CommonResult getProductMapping(@RequestBody ProductMapping productMapping) {
if (productMapping == null|| null==productMapping.getId()) {
return CommonResult.failed("id不能为空");
}
return CommonResult.success(productMappingService.getById(productMapping.getId()));
}
/**
* 新增
* @param productMapping
* @return
*/
@ApiOperation(value = "新增-店商品映射实体", notes = "新增-商品映射实体")
@RequestMapping(value = "/saveProductMapping", method = RequestMethod.POST)
public CommonResult saveProductMapping(@Valid @RequestBody ProductMapping productMapping, BindingResult result) {
if(result.hasErrors()) {
return CommonResult.failed(Objects.requireNonNull(result.getFieldError()).getDefaultMessage());
}
return productMappingService.saveProductMapping(productMapping);
}
/**
* 批量新增
* @param
* @return
*/
@ApiOperation(value = "批量新增-店商品映射实体", notes = "批量新增-商品映射实体")
@RequestMapping(value = "/saveProductMappingBatch", method = RequestMethod.POST)
public CommonResult saveProductMappingBatch(@RequestBody JSONArray jsonProductMappings) {
if(jsonProductMappings==null|| jsonProductMappings.isEmpty()){
return CommonResult.failed("数据不能为空");
}
Gson gson = new Gson();
List<ProductMapping> productMappings= gson.fromJson(jsonProductMappings.toString(),new TypeToken<List<ProductMapping>>(){}.getType());
productMappingService.saveBatch(productMappings,productMappings.size());
return CommonResult.success(true);
}
/**
* 更新
* @param productMapping
* @return
*/
@ApiOperation(value = "更新-商品映射实体", notes = "更新-商品映射实体")
@RequestMapping(value = "/udpateProductMapping", method = RequestMethod.PUT)
public CommonResult udpateProductMapping(@RequestBody ProductMapping productMapping) {
if(productMapping==null|| null==productMapping.getId()) {
return CommonResult.failed("缺少必要参数");
}
if(ObjectUtil.isNotEmpty(productMappingService.getById(productMapping.getId()))) {
if(productMappingService.saveOrUpdate(productMapping)){
return CommonResult.success(true);
}
}else {
return CommonResult.failed("不存在更新参数");
}
return CommonResult.failed("更新失败");
}
/**
* 删除
* @param productMapping
* @return
*/
@ApiOperation(value = "删除-商品映射实体", notes = "删除-商品映射实体")
@RequestMapping(value = "/delProductMapping", method = RequestMethod.DELETE)
public CommonResult delProductMapping(@RequestBody ProductMapping productMapping) {
if(productMapping==null|| null==productMapping.getId()) {
return CommonResult.failed("缺少必要参数");
}
if(ObjectUtil.isNotEmpty(productMappingService.getById(productMapping.getId()))) {
if(productMappingService.removeById(productMapping.getId())){
return CommonResult.success(true);
}
}else {
return CommonResult.failed("不存在更新参数");
}
return CommonResult.failed("删除失败");
}
/**
* 自动计算并上架商品
* @return
*/
@ApiOperation(value = "自动计算并上架商品", notes = "自动计算并上架商品")
@RequestMapping(value = "/syncProductMaping", method = RequestMethod.PUT)
public CommonResult syncProductMaping() {
return productMappingService.syncAllProductMapping();
}
/**
* 查找为同步未分配的商品数据批量手填
* @return
*/
@ApiOperation(value = "查找为同步的商品数据", notes = "删除查找为同步的商品数据")
@RequestMapping(value = "/getSyncBaseMapingProducts", method = RequestMethod.GET)
public CommonResult getSyncBaseMapingProducts() {
return productMappingService.getSyncProductUnchecked();
}
/**
* 模板下载
* @return
*/
@ApiOperation(value = "导入模板下载", notes = "模板下载")
@RequestMapping(value = "/template", method = RequestMethod.GET)
public void downloadTemplate(HttpServletResponse response) {
productMappingService.downloadTemplate(response);
}
/**
* 导入数据
* @return
*/
@ApiOperation(value = "模板数据导入", notes = "模板数据导入")
@RequestMapping(value = "/importData", method = RequestMethod.POST)
public CommonResult importData(@RequestParam("file") MultipartFile file) {
ImportResult result = productMappingService.importData(file);
return !result.getErrorMessages().isEmpty() ?CommonResult.failed((IErrorCode) result.getErrorMessages()):CommonResult.success(result);
}
/**
* 导出筛选数据
* @param ids
* @param response
*/
@PostMapping("/exportSelected")
public void exportSelectedData(@RequestBody List<Long> ids, HttpServletResponse response) {
List<ProductMapping> data = productMappingService.listByIds(ids);
productMappingService.exportData(response, data);
}
/**
* 下载错误报告
* @param file
* @param response
*/
@GetMapping("/download")
public void downloadFile(@RequestParam String file, HttpServletResponse response) {
try {
Path filePath = Paths.get(uploadDir, file);
if (!Files.exists(filePath)) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=" + file);
Files.copy(filePath, response.getOutputStream());
response.getOutputStream().flush();
} catch (IOException e) {
log.error("下载文件失败", e);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
}

View File

@ -12,6 +12,8 @@ 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.common.service.impl.BaseControllerImpl;
import com.suisung.mall.core.web.controller.BaseController;
import com.suisung.mall.shop.sync.service.StoreDbConfigService;
import com.suisung.mall.shop.sync.service.SyncFileLogService;
import io.swagger.annotations.Api;
@ -30,23 +32,26 @@ import org.springframework.web.bind.annotation.*;
@Api(tags = "页面导航表-店铺数据库连接配置")
@RestController
@RequestMapping("/admin/shop/shop-sync-storeDbConfig")
public class StoreDbConfigController {
public class StoreDbConfigController extends BaseControllerImpl {
@Autowired
private StoreDbConfigService storeDbConfigService;
/**
* 分页列表查询
* @param storeDbConfig
* @param
* @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,
public CommonResult list(@RequestParam(name = "pageNum", defaultValue = "1") Integer pageNum,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
StoreDbConfig storeDbConfig=new StoreDbConfig();
storeDbConfig.setStoreId(getParameter("storeId"));
storeDbConfig.setHasInternet(getParameter("hasInternet"));
storeDbConfig.setHasStart(getParameter("hasStart"));
return storeDbConfigService.findStoreDbConfigPageList(storeDbConfig, pageNum, pageSize);
}

View File

@ -107,12 +107,7 @@ public class SyncThirdDataController {
@RequestParam String appKey,
@RequestParam String sign,
@RequestParam String syncType) {
// new Thread(new Runnable() {
// @Override
// public void run() {
syncThirdDataService.SyncReadSxFileData(appKey,sign,syncType,folders);
// }
// }).start();
syncThirdDataService.SyncReadSxFileData(appKey,sign,syncType,folders);
return new ThirdApiRes().success("服务器已处理文件");
}
@ -150,4 +145,5 @@ public class SyncThirdDataController {
return syncThirdDataService.getStoreDataRelease(appKey,sign);
}
}

View File

@ -0,0 +1,39 @@
package com.suisung.mall.shop.sync.excleHandle;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.AbstractCellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import java.util.List;
public class ExportStyleHandler extends AbstractCellWriteHandler {
// 设置数据行样式
@Override
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex,
Boolean isHead) {
if (isHead) {
// 表头样式
CellStyle cellStyle = cell.getSheet().getWorkbook().createCellStyle();
Font font = cell.getSheet().getWorkbook().createFont();
font.setBold(true);
cellStyle.setFont(font);
cell.setCellStyle(cellStyle);
} else {
// 数据行样式 - 设置边框
CellStyle cellStyle = cell.getSheet().getWorkbook().createCellStyle();
cellStyle.setBorderTop(BorderStyle.THIN);
cellStyle.setBorderBottom(BorderStyle.THIN);
cellStyle.setBorderLeft(BorderStyle.THIN);
cellStyle.setBorderRight(BorderStyle.THIN);
cell.setCellStyle(cellStyle);
}
}
}

View File

@ -0,0 +1,33 @@
package com.suisung.mall.shop.sync.excleHandle;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.AbstractCellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.ss.usermodel.*;
import java.util.List;
public class TemplateStyleHandler extends AbstractCellWriteHandler {
// 设置表头样式
@Override
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex,
Boolean isHead) {
if (isHead) {
// 设置表头背景色
CellStyle cellStyle = cell.getSheet().getWorkbook().createCellStyle();
Font font = cell.getSheet().getWorkbook().createFont();
font.setBold(true);
font.setColor(IndexedColors.WHITE.getIndex());
cellStyle.setFont(font);
cellStyle.setFillForegroundColor(IndexedColors.DARK_BLUE.getIndex());
cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
cell.setCellStyle(cellStyle);
}
}
}

View File

@ -0,0 +1,20 @@
package com.suisung.mall.shop.sync.exelModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ImportResult {
private int totalCount; // 总记录数
private int successCount; // 成功记录数
private int failCount; // 失败记录数
private int insertCount; // 新增记录数
private int updateCount; // 更新记录数
private List<String> errorMessages; // 错误信息
private String downloadUrl; // 错误报告下载URL
}

View File

@ -0,0 +1,63 @@
package com.suisung.mall.shop.sync.exelModel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.suisung.mall.common.modules.sync.ProductMapping;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class ProductMappingExcel {
@ExcelProperty(value = "商品名称", index = 0)
private String productName;
@ExcelProperty(value = "店铺编号", index = 1)
private Integer storeId;
@ExcelProperty(value = "规格数据", index = 2)
private BigDecimal specValue;
@ExcelProperty(value = "规格单位", index = 3)
private String specUnit;
@ExcelProperty(value = "商品描述", index = 4)
private String description;
@ExcelProperty(value = "排序值", index = 5)
private Integer sortOrder;
// 转换为实体对象
public ProductMapping toEntity() {
ProductMapping entity = new ProductMapping();
entity.setProductName(this.productName);
entity.setStoreId(this.storeId);
entity.setSpecValue(this.specValue);
entity.setSpecUnit(this.specUnit);
entity.setDescription(this.description);
entity.setSortOrder(this.sortOrder);
return entity;
}
// 从实体对象转换
public static ProductMappingExcel fromEntity(ProductMapping entity) {
ProductMappingExcel excel = new ProductMappingExcel();
excel.setProductName(entity.getProductName());
excel.setStoreId(entity.getStoreId());
excel.setSpecValue(entity.getSpecValue());
excel.setSpecUnit(entity.getSpecUnit());
excel.setDescription(entity.getDescription());
excel.setSortOrder(entity.getSortOrder());
return excel;
}
/**
* 生成唯一键productName + storeId + specValue + specUnit
*/
public String getUniqueKey() {
return String.format("%s|%d|%s|%s",
productName,
storeId,
specValue.stripTrailingZeros().toPlainString(),
specUnit);
}
}

View File

@ -5,4 +5,14 @@ public class RedisKey {
//public static final String SXCLIENTKEYVERSION="sxclientKey:version";//客户端版本
public static final String STOREDATARELEASE="storedata:release";
public static final String STOREDATAPRODUCTMAPING="storedata:productMaping";
public static final String STOREDATASHOPBASEPRODUCTSPEC="storedata:shopBaseProductSpec";
public static final String STOREDATASPECITEMID="storedata:SpecItemId";
public static final String STOREDATAPRODUCTSPECITEM="storedata:ProductSpecItem";
}

View File

@ -0,0 +1,17 @@
package com.suisung.mall.shop.sync.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.suisung.mall.common.modules.sync.ProductMapping;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface ProductMappingMapper extends BaseMapper<ProductMapping> {
List<ProductMapping> findPageProductMapping(Page<ProductMapping> page);
Integer getCount();
}

View File

@ -0,0 +1,39 @@
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.product.ShopProductBase;
import com.suisung.mall.common.modules.sync.ProductMapping;
import com.suisung.mall.shop.sync.exelModel.ImportResult;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
public interface ProductMappingService extends IService<ProductMapping> {
CommonResult findPageProductMapping(ProductMapping productMapping,Integer pageNum,Integer pageSize);
void computeProductMapping(List<ShopProductBase> shopProductBaseList,Integer storeId, boolean isUpdate);
Map getProductMapping(Integer storeId);
CommonResult syncAllProductMapping();
CommonResult getSyncProductUnchecked();
// 下载导入模板
void downloadTemplate(HttpServletResponse response);
// 导入Excel数据
ImportResult importData(MultipartFile file);
// 导出数据到Excel
void exportData(HttpServletResponse response, List<ProductMapping> data);
// 保存数据
CommonResult saveProductMapping(ProductMapping data);
}

View File

@ -0,0 +1,699 @@
/*
* 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.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
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.modules.base.ShopBaseProductSpec;
import com.suisung.mall.common.modules.product.*;
import com.suisung.mall.common.modules.sync.ProductMapping;
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.base.service.ShopBaseProductSpecService;
import com.suisung.mall.shop.number.service.ShopNumberSeqService;
import com.suisung.mall.shop.product.service.*;
import com.suisung.mall.shop.sixun.utils.CommonUtil;
import com.suisung.mall.shop.sync.Utils.BigDecimalFormatter;
import com.suisung.mall.shop.sync.Utils.ProductPriceCalculator;
import com.suisung.mall.shop.sync.Utils.ShopJsonUtils;
import com.suisung.mall.shop.sync.excleHandle.ExportStyleHandler;
import com.suisung.mall.shop.sync.excleHandle.TemplateStyleHandler;
import com.suisung.mall.shop.sync.exelModel.ImportResult;
import com.suisung.mall.shop.sync.exelModel.ProductMappingExcel;
import com.suisung.mall.shop.sync.mapper.ProductMappingMapper;
import com.suisung.mall.shop.sync.service.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.validation.ValidationException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
@Service
@Transactional(rollbackFor = Exception.class)
@Slf4j
public class ProductMappingServiceImpl extends BaseServiceImpl<ProductMappingMapper, ProductMapping> implements ProductMappingService {
@Autowired
private ShopProductBaseService shopProductBaseService;
@Autowired
private ShopProductItemService shopProductItemService;
@Autowired
private ShopProductInfoService shopProductInfoService;
@Autowired
private ShopProductSpecItemService shopProductSpecItemService;
@Autowired
private ShopBaseProductSpecService baseProductSpecService;
@Autowired
private ShopNumberSeqService shopNumberSeqService;
@Autowired
private ShopProductIndexService shopProductIndexService;
@Autowired
private ProductMappingService productMappingService;
@Value("${file.upload-dir}")
private String uploadDir;
private static final String TEMPLATE_NAME = "商品映射模板.xlsx";
private static final String EXPORT_NAME = "商品映射数据.xlsx";
private final Integer SHOPBASEPAGE=500;
@Override
public CommonResult findPageProductMapping(ProductMapping productMapping,Integer pageNum,Integer pageSize) {
QueryWrapper<ProductMapping> queryWrapper = new QueryWrapper<>();
if(StringUtils.isNotEmpty(productMapping.getProductName())){
queryWrapper.eq("product_name",productMapping.getProductName());
// queryWrapper.and(mapping ->mapping.like("product_name", productMapping.getProductName()));
}
if(ObjectUtil.isNotEmpty(productMapping.getStoreId())){
queryWrapper.and(m->m.eq("store_id",productMapping.getStoreId()).or().eq("store_id",0));
// queryWrapper.and(mapping ->mapping.like("product_name", productMapping.getProductName()));
}
return CommonResult.success(this.lists(queryWrapper,pageNum,pageSize));
}
@Override
public void computeProductMapping(List<ShopProductBase> shopProductBaseList,Integer storeId,boolean isUpdate) {
shopProductBaseList= shopProductBaseList.stream().filter(base ->StateCode.PRODUCT_STATE_OFF_THE_SHELF_UNCHECK==(base.getProduct_state_id())).collect(Collectors.toList());
if (CollUtil.isEmpty(shopProductBaseList)) {
log.info("没有规格数据要处理");
return;
}
Map shopProductSpecItemMap = shopProductSpecItemService.getExistItem(storeId);
Map productMappingMap = this.getProductMapping(storeId);
Map ShopBaseProductSpecMap = baseProductSpecService.getShopBaseProductSpecMap(storeId);
dealData(shopProductBaseList,shopProductSpecItemMap,ShopBaseProductSpecMap,productMappingMap,isUpdate);
}
@Override
public Map getProductMapping(Integer storeId) {
Map map=new HashMap();
QueryWrapper<ProductMapping> queryWrapper= new QueryWrapper<>();
//queryWrapper.eq("store_id", storeId);
queryWrapper.and(m->m.eq("store_id",storeId).or().eq("store_id",0)).orderByDesc("sort_order");
List<ProductMapping> list= this.list(queryWrapper);
for (ProductMapping productMapping:list){
map.put(productMapping.getProductName(),productMapping);
}
return map;
}
/**
* 处理数据生成规格列表
* @param shopProductBaseList
* @param shopProductSpecItemMap
* @param ShopBaseProductSpecMap
* @param productMappingMap
*/
public void dealData(List<ShopProductBase> shopProductBaseList,Map shopProductSpecItemMap,Map ShopBaseProductSpecMap, Map productMappingMap,boolean isUpdate){
List<ShopProductItem> shopProductItems=findShopProductItemsList(shopProductBaseList);
List<ShopProductInfo> shopProductInfoList=findShopProductInfoList(shopProductBaseList);
List<ShopProductSpecItem> addShopProductSpecItemList=new ArrayList<>();
List<ShopProductSpecItem> updateShopProductSpecItemList=new ArrayList<>();
List<ShopProductBase> updateShopProductBaseList=new ArrayList<>();
List<ShopProductItem> updateShopProductItemList=new ArrayList<>();
List<ShopProductInfo> updateShopProductInfoList=new ArrayList<>();
//找出需要更新的列表
for (int i = 0; i < shopProductBaseList.size(); i++){
ShopProductSpecItem shopProductSpecItem=processShopProductSpecItem(shopProductBaseList.get(i),shopProductItems.get(i).getCategory_id(),shopProductSpecItemMap,ShopBaseProductSpecMap,productMappingMap,null);
if(shopProductSpecItem!=null){
shopProductBaseList.get(i).setProduct_state_id(StateCode.PRODUCT_STATE_NORMAL);
shopProductItems.get(i).setItem_enable(StateCode.PRODUCT_STATE_NORMAL);
shopProductItems.get(i).setItem_is_default(1);
if(shopProductSpecItem.isUpdate()){
shopProductBaseList.get(i).setProduct_unit_price(shopProductSpecItem.getItemPrice());
updateShopProductSpecItemList.add(shopProductSpecItem);
}else {
shopProductBaseList.get(i).setProduct_unit_price(shopProductSpecItem.getItemPrice());
shopProductBaseList.get(i).setProduct_market_price(shopProductSpecItem.getItemPrice());
addShopProductSpecItemList.add(shopProductSpecItem);
}
updateShopProductBaseList.add(shopProductBaseList.get(i));
updateShopProductItemList.add(shopProductItems.get(i));
updateShopProductInfoList.add(shopProductInfoList.get(i));
proccessItemAndInfo(shopProductItems.get(i),shopProductInfoList.get(i),shopProductSpecItem,isUpdate);
}
}
//再次变量根据addShopProductSpecItemList算出id,这样防止多次链接redis
if(!addShopProductSpecItemList.isEmpty()){
List<Integer> integers= shopNumberSeqService.getBatchSpecItemId(addShopProductSpecItemList.size());
int index=0;
for (int i = 0; i < shopProductBaseList.size(); i++){
if(index==integers.size()){
break;
}
ShopProductSpecItem shopProductSpecItem=processShopProductSpecItem(shopProductBaseList.get(i),shopProductItems.get(i).getCategory_id(),shopProductSpecItemMap,ShopBaseProductSpecMap,productMappingMap,integers.get(index));
if(shopProductSpecItem!=null){
if(!shopProductSpecItem.isUpdate()){
addShopProductSpecItemList.set(index,shopProductSpecItem);
index++;
}
proccessItemAndInfo(shopProductItems.get(i),shopProductInfoList.get(i),shopProductSpecItem,isUpdate);
}
}
}
if(CollUtil.isNotEmpty(addShopProductSpecItemList)){
shopProductSpecItemService.saveBatch(addShopProductSpecItemList,addShopProductSpecItemList.size());
}
if(CollUtil.isNotEmpty(updateShopProductSpecItemList)){
shopProductSpecItemService.updateBatchById(updateShopProductSpecItemList);
}
if(CollUtil.isNotEmpty(updateShopProductBaseList)){
shopProductBaseService.updateBatchById(updateShopProductBaseList);
saveShopProductIndexList(updateShopProductBaseList);
}
if(CollUtil.isNotEmpty(updateShopProductItemList)){
shopProductItemService.updateBatchById(updateShopProductItemList);
}
if(CollUtil.isNotEmpty(updateShopProductInfoList)){
shopProductInfoService.updateBatchById(updateShopProductInfoList);
}
}
/**
* @param item
*/
private void proccessItemAndInfo(ShopProductItem item,ShopProductInfo shopProductInfo,ShopProductSpecItem shopProductSpecItem,boolean isUpdate){
String item_spec = item.getItem_spec();
String productSpec="";
if(null!=shopProductSpecItem){
item_spec=shopProductSpecItem.getItem_spec();
productSpec=shopProductSpecItem.getProduct_spec();
if(isUpdate){
item.setItem_unit_price(shopProductSpecItem.getItemPrice());
}else {
item.setItem_unit_price(shopProductSpecItem.getItemPrice());
item.setItem_market_price(shopProductSpecItem.getItemPrice());
}
}
JSONArray array_item_spec = JSONUtil.parseArray(item_spec);
List<Integer> spec_item_ids = new ArrayList<>();
for (Object josn_item_spec : array_item_spec) {
JSONObject itemJ = (JSONObject) ((JSONObject) josn_item_spec).get("item");
Integer id = Convert.toInt(itemJ.get("id"));
if (ObjectUtil.isNotNull(id)) spec_item_ids.add(id);
}
if (CollUtil.isNotEmpty(spec_item_ids)) Collections.sort(spec_item_ids);
//用来判断是否使用
item.setSpec_item_ids(CollUtil.join(spec_item_ids, ","));
if(StringUtils.isNotEmpty(item_spec)){
item.setItem_spec(item_spec);
shopProductInfo.setProduct_spec(productSpec);
}
String product_uniqid=ShopJsonUtils.generateJsonWithOrgJson(item.getSpec_item_ids(),new Object[]{item.getItem_id(),item.getItem_unit_price(),"",1002});
shopProductInfo.setProduct_uniqid(product_uniqid);
}
/**
* 更新index状态为正常
* @param shopProductBaseList
*/
private void saveShopProductIndexList(List<ShopProductBase> shopProductBaseList){
if (CollUtil.isEmpty(shopProductBaseList)) {
return ;
}
// 使用IN查询优化根据数据库特性可能需要分批
QueryWrapper<ShopProductIndex> query = new QueryWrapper<>();
query.select("product_id", "product_name", "store_id");
// 构建OR条件 (store_id=X AND product_number=Y) OR (store_id=A AND product_number=B)...
shopProductBaseList.forEach(base -> {
query.or(q -> q.eq("store_id", base.getStore_id())
.eq("product_id", base.getProduct_id()));
});
List<ShopProductIndex> updateShopProductIndexList= shopProductIndexService.list(query);
updateShopProductIndexList.forEach(shopProductIndex -> {
shopProductIndex.setProduct_state_id(StateCode.PRODUCT_STATE_NORMAL);
});
shopProductIndexService.updateBatchById(updateShopProductIndexList);
}
/**
*
* @param shopProductBaseList
* @return
*/
private List<ShopProductItem> findShopProductItemsList(List<ShopProductBase> shopProductBaseList){
if (CollUtil.isEmpty(shopProductBaseList)) {
return Collections.emptyList();
}
// 使用IN查询优化根据数据库特性可能需要分批
QueryWrapper<ShopProductItem> query = new QueryWrapper<>();
query.select("product_id", "store_id", "item_id","item_unit_price","item_unit_price","item_market_price","category_id");
// 构建OR条件 (store_id=X AND product_number=Y) OR (store_id=A AND product_number=B)...
shopProductBaseList.forEach(base -> {
query.or(q -> q.eq("store_id", base.getStore_id())
.eq("product_id", base.getProduct_id()));
});
return shopProductItemService.list(query);
}
/**
*
* @param shopProductBaseList
* @return
*/
private List<ShopProductInfo> findShopProductInfoList(List<ShopProductBase> shopProductBaseList){
if (CollUtil.isEmpty(shopProductBaseList)) {
return Collections.emptyList();
}
// 使用IN查询优化根据数据库特性可能需要分批
QueryWrapper<ShopProductInfo> query = new QueryWrapper<>();
query.select("product_id", "product_number", "product_uniqid","product_weight");
// 构建OR条件 (store_id=X AND product_number=Y) OR (store_id=A AND product_number=B)...
shopProductBaseList.forEach(base -> {
query.or(q -> q.eq("product_id", base.getProduct_id()));
});
return shopProductInfoService.list(query);
}
/**
* 计算并产生规格 与商品配置表匹配匹配正确则新增规格商品
* @return
*/
private ShopProductSpecItem processShopProductSpecItem(ShopProductBase base,Integer categoryId,Map shopProductSpecItemMap,Map ShopBaseProductSpecMap, Map productMappingMap,Integer specItemId){
String productName=base.getProduct_name();
if(null!=productMappingMap.get(productName)){
ShopBaseProductSpec shopBaseProductSpec= (ShopBaseProductSpec) ShopBaseProductSpecMap.get(categoryId);
if(null==shopBaseProductSpec){
shopBaseProductSpec= (ShopBaseProductSpec) ShopBaseProductSpecMap.get(String.valueOf(categoryId));
}
Integer specId= shopBaseProductSpec.getSpec_id();
ProductMapping productMapping= (ProductMapping) productMappingMap.get(productName);
String Spec_item_name=productMapping.getProductName()+ BigDecimalFormatter.formatWithoutTrailingZeros(productMapping.getSpecValue())+productMapping.getSpecUnit();//
Integer Spec_item_id = null;
ShopProductSpecItem addShopProductSpecItem=new ShopProductSpecItem();
addShopProductSpecItem.setUpdate(true);
if(ObjectUtil.isNotEmpty(shopProductSpecItemMap.get(Spec_item_name))){
Spec_item_id= (Integer) shopProductSpecItemMap.get(Spec_item_name);
}else {
if(ObjectUtil.isNotEmpty(specItemId)){
Spec_item_id=specItemId;
}
addShopProductSpecItem.setUpdate(false);
}
addShopProductSpecItem.setStore_id(base.getStore_id());
addShopProductSpecItem.setCategory_id(categoryId);
addShopProductSpecItem.setSpec_item_id(Spec_item_id);
addShopProductSpecItem.setSpec_id(specId);//根据规格获取
addShopProductSpecItem.setSpec_item_name(Spec_item_name);
addShopProductSpecItem.setSpec_item_enable(1);//上架
addShopProductSpecItem.setSpec_item_order("10");
BigDecimal[] bigDecimals= ProductPriceCalculator.calculatePriceAndQuantity(base.getUnit_price(),base.getShop_weight(),productMapping.getSpecValue(),productMapping.getSpecUnit());
addShopProductSpecItem.setItemPrice(bigDecimals[0]);
addShopProductSpecItem.setItemQuantity(bigDecimals[1]);
List<Map<String, Object>> items=new ArrayList<>();
Map<String,Object> item=new HashMap<>();
item.put("name",addShopProductSpecItem.getSpec_item_name());//规格列表
item.put("id",Spec_item_id);
items.add(item);
List<Map<String, Object>> specItems=new ArrayList<>();
Map<String,Object> specItem=new HashMap<>();
specItem.put("name",shopBaseProductSpec.getSpec_name());//规格名称
specItem.put("id",specId);
specItems.add(specItem);
String item_spec= ShopJsonUtils.generateJsonWithOrgJsonItemSpec(items,specItems);
String product_spec= ShopJsonUtils.generateJsonWithOrgJsonProducSpec(items,specItems);
addShopProductSpecItem.setItem_spec(item_spec);
addShopProductSpecItem.setProduct_spec(product_spec);
// addShopProductSpecItem.setProduct_uniqid(product_uniqid);
return addShopProductSpecItem;
}
return null;
}
@Override
public CommonResult syncAllProductMapping() {
Integer storeId= Integer.valueOf(Objects.requireNonNull(ContextUtil.getCurrentUser()).getStore_id());
//找出范围内的规格产品
QueryWrapper<ShopProductBase> queryWrapper= new QueryWrapper<>();
queryWrapper.eq("product_state_id", StateCode.PRODUCT_STATE_OFF_THE_SHELF_UNCHECK);
queryWrapper.eq("store_id", storeId);
long total=shopProductBaseService.count(queryWrapper);
int pages= CommonUtil.getPagesCount(Math.toIntExact(total),SHOPBASEPAGE);
ExecutorService executor = Executors.newFixedThreadPool(6);
List<Future<?>> futures = new ArrayList<>();
for (int i=1;i<=pages;i++){
int finalI = i;
futures.add(executor.submit(() -> {
this.computeProductMapping(shopProductBaseService.lists(queryWrapper, finalI,SHOPBASEPAGE).getRecords(),storeId,false);
return "成功" + finalI;
}));
}
// 等待所有任务完成
for (Future<?> future : futures) {
try {
System.out.println("任务结果: " + future.get());
} catch (Exception e) {
System.err.println("任务执行异常: " + e.getMessage());
}
}
executor.shutdown();
shopNumberSeqService.clearKeyStoreItemSepcId();
return CommonResult.success(true);
}
@Override
public CommonResult getSyncProductUnchecked() {
Integer storeId= Integer.valueOf(Objects.requireNonNull(ContextUtil.getCurrentUser()).getStore_id());
//找出范围内的规格产品
QueryWrapper<ShopProductBase> queryWrapper= new QueryWrapper<>();
queryWrapper.eq("product_state_id", StateCode.PRODUCT_STATE_OFF_THE_SHELF_UNCHECK);
queryWrapper.eq("store_id", storeId);
return CommonResult.success(shopProductBaseService.list(queryWrapper));
}
@Override
public void downloadTemplate(HttpServletResponse response) {
try {
// 设置响应头
setExcelResponseHeader(response, TEMPLATE_NAME);
// 创建空模板
EasyExcel.write(response.getOutputStream(), ProductMappingExcel.class)
.sheet("商品映射")
.registerWriteHandler(new TemplateStyleHandler())
.doWrite(new ArrayList<>());
} catch (IOException e) {
log.error("下载模板失败", e);
throw new RuntimeException("下载模板失败");
}
}
@Override
public ImportResult importData(MultipartFile file) {
ImportResult result = new ImportResult( 0,0, 0, 0,0, new ArrayList<>(), null);
String fileName = storeUploadedFile(file);
try {
// 1. 读取Excel数据
List<ProductMappingExcel> excelList = readExcelData(fileName);
result.setTotalCount(excelList.size());
// 2. 构建唯一键映射
Map<String, ProductMapping> existingMap = loadExistingMappings(excelList);
// 3. 处理导入数据
List<ProductMapping> toUpdate = new ArrayList<>();
List<ProductMapping> toInsert = new ArrayList<>();
for (int i = 0; i < excelList.size(); i++) {
ProductMappingExcel excel = excelList.get(i);
int rowNum = i + 2; // Excel行号标题行+1
try {
// 验证数据
validateExcelData(excel, rowNum);
// 转换为实体
ProductMapping entity = excel.toEntity();
String uniqueKey = excel.getUniqueKey();
// 检查是否已存在
if (existingMap.containsKey(uniqueKey)) {
ProductMapping existing = existingMap.get(uniqueKey);
updateExistingEntity(existing, entity);
toUpdate.add(existing);
result.setUpdateCount(result.getUpdateCount() + 1);
} else {
toInsert.add(entity);
result.setInsertCount(result.getInsertCount() + 1);
}
result.setSuccessCount(result.getSuccessCount() + 1);
} catch (ValidationException e) {
result.setFailCount(result.getFailCount() + 1);
result.getErrorMessages().add(e.getMessage());
}
}
// 4. 批量操作
if (!toInsert.isEmpty()) {
productMappingService.saveBatch(toInsert,toInsert.size());
}
if (!toUpdate.isEmpty()) {
productMappingService.updateBatchById(toUpdate,toInsert.size());
}
// 5. 生成错误报告如果有错误
if (result.getFailCount() > 0) {
result.setDownloadUrl(generateErrorReport(result.getErrorMessages(), fileName));
}
return result;
} catch (Exception e) {
log.error("导入数据失败", e);
throw new RuntimeException("导入数据失败: " + e.getMessage());
}
}
@Override
public void exportData(HttpServletResponse response, List<ProductMapping> data) {
try {
// 设置响应头
setExcelResponseHeader(response, EXPORT_NAME);
// 转换为Excel对象
List<ProductMappingExcel> excelData = data.stream()
.map(ProductMappingExcel::fromEntity)
.collect(Collectors.toList());
// 导出Excel
EasyExcel.write(response.getOutputStream(), ProductMappingExcel.class)
.sheet("商品映射数据")
.registerWriteHandler(new ExportStyleHandler())
.doWrite(excelData);
} catch (IOException e) {
log.error("导出数据失败", e);
throw new RuntimeException("导出数据失败");
}
}
// ================ 私有方法 ================
// 加载数据库中已存在的映射
private Map<String, ProductMapping> loadExistingMappings(List<ProductMappingExcel> excelList) {
// 收集所有唯一键
Set<String> uniqueKeys = excelList.stream()
.map(ProductMappingExcel::getUniqueKey)
.collect(Collectors.toSet());
if (uniqueKeys.isEmpty()) {
return Collections.emptyMap();
}
// 分批查询数据库避免SQL过长
int batchSize = 100;
Map<String, ProductMapping> resultMap = new HashMap<>();
List<String> keyList = new ArrayList<>(uniqueKeys);
for (int i = 0; i < keyList.size(); i += batchSize) {
int end = Math.min(i + batchSize, keyList.size());
List<String> batchKeys = keyList.subList(i, end);
// 构建查询条件
QueryWrapper<ProductMapping> wrapper = new QueryWrapper<>();
wrapper.apply(buildUniqueKeyCondition(batchKeys));
// 查询数据库
List<ProductMapping> existingList = productMappingService.list(wrapper);
// 添加到结果映射
existingList.forEach(entity ->
resultMap.put(entity.getUniqueKey(), entity));
}
return resultMap;
}
// 构建唯一键查询条件
private String buildUniqueKeyCondition(List<String> uniqueKeys) {
StringBuilder sb = new StringBuilder();
sb.append("CONCAT_WS('|', product_name, store_id, TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM CAST(spec_value AS CHAR))), spec_unit) IN (");
for (int i = 0; i < uniqueKeys.size(); i++) {
if (i > 0) sb.append(",");
sb.append("'").append(uniqueKeys.get(i).replace("'", "''")).append("'");
}
sb.append(")");
return sb.toString();
}
// 更新现有实体
private void updateExistingEntity(ProductMapping existing, ProductMapping newEntity) {
existing.setDescription(newEntity.getDescription());
existing.setSortOrder(newEntity.getSortOrder());
// 可根据需要更新其他字段
}
// 验证Excel数据
private void validateExcelData(ProductMappingExcel data, int rowNum) {
if (StringUtils.isBlank(data.getProductName())) {
throw new ValidationException("" + rowNum + "行: 商品名称不能为空");
}
if (data.getStoreId() == null || data.getStoreId() <= 0) {
throw new ValidationException("" + rowNum + "行: 店铺编号必须大于0");
}
if (data.getSpecValue() == null || data.getSpecValue().compareTo(BigDecimal.ZERO) <= 0) {
throw new ValidationException("" + rowNum + "行: 规格数据必须大于0");
}
if (StringUtils.isBlank(data.getSpecUnit())) {
throw new ValidationException("" + rowNum + "行: 规格单位不能为空");
}
if (data.getSortOrder() == null || data.getSortOrder() < 0) {
throw new ValidationException("" + rowNum + "行: 排序值不能为负数");
}
}
// 设置Excel响应头
private void setExcelResponseHeader(HttpServletResponse response, String fileName) {
try {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
String encodedFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + encodedFileName);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("文件名编码失败");
}
}
// 存储上传文件
private String storeUploadedFile(MultipartFile file) {
if (file.isEmpty()) {
throw new RuntimeException("上传文件为空");
}
try {
String fileName = System.currentTimeMillis() + "_" + file.getOriginalFilename();
Path filePath = Paths.get(uploadDir, fileName);
Files.createDirectories(filePath.getParent());
Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING);
return filePath.toString();
} catch (IOException e) {
log.error("存储上传文件失败", e);
throw new RuntimeException("存储上传文件失败");
}
}
// 读取Excel数据
private List<ProductMappingExcel> readExcelData(String filePath) {
return EasyExcel.read(filePath)
.head(ProductMappingExcel.class)
.sheet()
.doReadSync();
}
// 生成错误报告
private String generateErrorReport(List<String> errors, String originalFilePath) {
try {
String errorFileName = "ERROR_" + Paths.get(originalFilePath).getFileName().toString();
Path errorFilePath = Paths.get(uploadDir, errorFileName);
List<ErrorInfo> errorData = new ArrayList<>();
for (int i = 0; i < errors.size(); i++) {
errorData.add(new ErrorInfo(i + 1, errors.get(i)));
}
EasyExcel.write(errorFilePath.toString())
.head(ErrorInfo.class)
.sheet("导入错误")
.doWrite(errorData);
return "/admin/shop/shop-sync-productMapper/download?file=" + URLEncoder.encode(errorFileName, "UTF-8");
} catch (Exception e) {
log.error("生成错误报告失败", e);
return null;
}
}
@Override
public CommonResult saveProductMapping(ProductMapping data) {
List<String> batchKeys = Arrays.asList(data.getUniqueKey().split(","));
// 构建查询条件
QueryWrapper<ProductMapping> wrapper = new QueryWrapper<>();
wrapper.apply(buildUniqueKeyCondition(batchKeys));
List<ProductMapping> productMappings= productMappingService.list(wrapper);
if(!productMappings.isEmpty()){
return CommonResult.failed("存在相同的映射商品数据");
}
boolean result= productMappingService.save(data);
if(result){
return CommonResult.success();
}
return CommonResult.success(false);
}
// 错误信息类
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class ErrorInfo {
@ExcelProperty("行号")
private Integer rowNum;
@ExcelProperty("错误信息")
private String errorMessage;
}
}

View File

@ -45,12 +45,19 @@ public class StoreDbConfigServiceImpl extends BaseServiceImpl<StoreDbConfigMappe
@Override
public CommonResult findStoreDbConfigPageList(StoreDbConfig storeDbConfig,Integer pageNum,Integer pageSize) {
checked();
String storeId= getStoreId(storeDbConfig.getStoreId());
QueryWrapper<StoreDbConfig> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("store_id", storeId);
queryWrapper.eq("has_internet", storeDbConfig.getHasInternet());
queryWrapper.eq("has_start", storeDbConfig.getHasInternet());
if(!StringUtils.isEmpty(storeId)){
queryWrapper.eq("store_id", storeId);
}
if(!ObjectUtil.isEmpty(storeDbConfig.getHasInternet())){
queryWrapper.eq("has_internet", storeDbConfig.getHasInternet());
}
if(!ObjectUtil.isEmpty(storeDbConfig.getHasStart())){
queryWrapper.eq("has_start", storeDbConfig.getHasStart());
}
return CommonResult.success(this.lists(queryWrapper,pageNum,pageSize));
}

View File

@ -14,6 +14,7 @@ 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.enums.DicEnum;
import com.suisung.mall.common.exception.ApiException;
import com.suisung.mall.common.feignService.AccountService;
import com.suisung.mall.common.feignService.PayService;
@ -21,6 +22,7 @@ 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.ShopBaseProductSpec;
import com.suisung.mall.common.modules.base.ShopBaseProductType;
import com.suisung.mall.common.modules.number.ShopNumberSeq;
import com.suisung.mall.common.modules.pay.PayUserResource;
@ -34,13 +36,18 @@ 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.ShopBaseProductSpecService;
import com.suisung.mall.shop.base.service.ShopBaseProductTypeService;
import com.suisung.mall.shop.number.service.ShopNumberSeqService;
import com.suisung.mall.shop.number.service.impl.ShopNumberSeqServiceImpl;
import com.suisung.mall.shop.product.service.ShopProductBaseService;
import com.suisung.mall.shop.product.service.ShopProductItemService;
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 com.suisung.mall.shop.sync.service.ProductMappingService;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@ -51,6 +58,9 @@ import java.math.BigDecimal;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import static com.baomidou.mybatisplus.extension.toolkit.Db.list;
@Service
public abstract class SyncBaseThirdSxAbstract{
@ -70,11 +80,16 @@ public abstract class SyncBaseThirdSxAbstract{
@Autowired
private PayService payService;
@Autowired
private ShopNumberSeqServiceImpl shopNumberSeqServiceImpl;
private ShopNumberSeqService shopNumberSeqService;
@Autowired
private ShopProductBaseServiceImpl shopProductBaseServiceImpl;
private ShopProductItemService shopProductItemService;
@Autowired
private ShopProductItemServiceImpl shopProductItemServiceImpl;
private ShopBaseProductSpecService shopBaseProductSpecService;
@Autowired
private ProductMappingService productMappingService;
/**
* 对商品分类进行保存
@ -86,17 +101,20 @@ public abstract class SyncBaseThirdSxAbstract{
public int baseSaveOrUpdateShopBaseProductCategoryBatch(List<ShopBaseProductCategory> list ,JSONArray categoryListJSON,
String storeId){
int count = 0;
List<ShopBaseProductType> productTypeList = new ArrayList<>();
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);
ShopBaseProductType productType=new ShopBaseProductType();
if (o != null) {
// 重要分类类型处理强调共性
Integer typeId = 1001;
if (StrUtil.isNotBlank(o.getStr("product_type"))) {
ShopBaseProductType productType = productTypeService.getProductTypeByName(o.getStr("product_type"));
productType = productTypeService.getProductTypeByName(o.getStr("product_type"));
if (productType != null) {
typeId = productType.getType_id();
} else {
@ -107,6 +125,7 @@ public abstract class SyncBaseThirdSxAbstract{
if (productTypeService.save(newProductType)) {
typeId = newProductType.getType_id();
}
productType=newProductType;
}
list.get(i).setType_id(typeId);
}
@ -157,8 +176,6 @@ public abstract class SyncBaseThirdSxAbstract{
}
}
}
}
ShopBaseProductCategory productCategoryTemp = productCategoryService.getCategoryByName(list.get(i).getCategory_parent_id(), list.get(i).getCategory_name(), list.get(i).getStore_id());
@ -173,13 +190,16 @@ public abstract class SyncBaseThirdSxAbstract{
} else {
continue;
}
}
if (productCategoryService.saveOrUpdate(list.get(i))) {
count++;
}
productType.setType_category_id(list.get(i).getCategory_id());
productTypeList.add(productType);
}
saveBatchShopProductProductSpec(storeId,productTypeList);
return count;
}
@ -522,6 +542,10 @@ public abstract class SyncBaseThirdSxAbstract{
}
goodsListJSON.stream().parallel().forEach(object -> {
final JSONObject jsonObj= (JSONObject) object;
String productName= (String) jsonObj.get("product_name");
if(productName.equals(DicEnum.GOODS_UN_SYNC_SX.getValue())){//todo 后期改为数据库存储的黑名单
return;
}
Date currentDate = new Date();
String cateGoryId="";
if(null!=categoryMap.get(jsonObj.get("first_category_name"))){
@ -530,24 +554,37 @@ public abstract class SyncBaseThirdSxAbstract{
Integer categoryId = Convert.toInt(cateGoryId, 0);
Integer storeIdInt = Convert.toInt(storeId);
ShopProductBase shopProductBase = new ShopProductBase();
shopProductBase.setCategoryId(categoryId);
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());
shopProductBase.setProduct_unit_price(BigDecimal.valueOf(jsonObj.getDouble("retail_price")));
//商品总量
if(ObjectUtil.isNotEmpty(jsonObj.getStr("unit"))&&ObjectUtil.isNotEmpty(jsonObj.getStr("stock"))
&& "KG,kg,公斤".contains(jsonObj.getStr("unit"))){
shopProductBase.setShop_weight(jsonObj.getBigDecimal("stock"));
shopProductBase.setUnit_name(jsonObj.getStr("unit"));
shopProductBase.setProduct_state_id(StateCode.PRODUCT_STATE_OFF_THE_SHELF_UNCHECK);
shopProductBase.setUnit_price(BigDecimal.valueOf(jsonObj.getDouble("retail_price")));
}else {
shopProductBase.setShop_weight(jsonObj.getBigDecimal("stock"));
shopProductBase.setProduct_state_id(StateCode.PRODUCT_STATE_NORMAL);
shopProductBase.setUnit_price(BigDecimal.valueOf(jsonObj.getDouble("retail_price")));
}
// ShopProductIndex
ShopProductIndex shopProductIndex = new ShopProductIndex();
shopProductIndex.setProduct_add_time(currentDate.getTime());
@ -616,12 +653,19 @@ public abstract class SyncBaseThirdSxAbstract{
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")); // 库存
//todo 特色商品切割
//负数产品判断
if(jsonObj.getInt("stock",99)<0){
shopProductItem.setItem_quantity(99); // 库存
shopProductItem.setItem_weight(new BigDecimal("0"));//切割重量
}
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);
shopProductItem.setItem_enable(shopProductBase.getProduct_state_id());
//添加数据到list
synchronized(shopProductBaseList){
@ -633,30 +677,9 @@ public abstract class SyncBaseThirdSxAbstract{
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<List<ShopProductImage>>(),
new ArrayList<ShopProductValidPeriod>(),
@ -671,7 +694,7 @@ public abstract class SyncBaseThirdSxAbstract{
List<ShopNumberSeq> shopNumberSeqList=new ArrayList<>();
QueryWrapper<ShopProductBase> baseWrapper=new QueryWrapper<>();
baseWrapper.select("MAX(product_id) as product_id");
ShopProductBase shopProductBase= shopProductBaseServiceImpl.getOne(baseWrapper);
ShopProductBase shopProductBase= shopProductBaseService.getOne(baseWrapper);
Long productId= shopProductBase.getProduct_id();
//QueryWrapper<ShopNumberSeq> baseSeWrapper=new QueryWrapper();
//baseSeWrapper.eq("prefix", "product_id");
@ -682,7 +705,7 @@ public abstract class SyncBaseThirdSxAbstract{
//查询产品item
QueryWrapper<ShopProductItem> itemQuery=new QueryWrapper<>();
itemQuery.select("MAX(item_id) as item_id");
ShopProductItem shopProductItem= shopProductItemServiceImpl.getOne(itemQuery);
ShopProductItem shopProductItem= shopProductItemService.getOne(itemQuery);
Long itemtId= shopProductItem.getItem_id();
// QueryWrapper<ShopNumberSeq> itemWrapper=new QueryWrapper();
//itemWrapper.eq("prefix", "item_id");
@ -691,9 +714,101 @@ public abstract class SyncBaseThirdSxAbstract{
shopNumberSeqItem.setPrefix("item_id");
shopNumberSeqList.add(shopNumberSeqBase);
shopNumberSeqList.add(shopNumberSeqItem);
shopNumberSeqServiceImpl.batchUpdateSeq(shopNumberSeqList);
shopNumberSeqServiceImpl.clearRelateGoodsId();
shopNumberSeqService.batchUpdateSeq(shopNumberSeqList);
shopNumberSeqService.clearRelateGoodsId();
}
/**
* 根据商品分类生成规格主体
* @param storeId
*/
private synchronized void saveBatchShopProductProductSpec(String storeId,List<ShopBaseProductType> shopBaseProductTypes){
Map categoryMap= productCategoryService.getCategoryListByStoreId(storeId);
List<ShopBaseProductSpec> shopBaseProductSpecList=new ArrayList<>();
Set<Map.Entry> categoryMapSet= categoryMap.entrySet();
for(Map.Entry categoryMapEntry:categoryMapSet){
ShopBaseProductSpec shopBaseProductSpec=new ShopBaseProductSpec();
if(categoryMapEntry.getKey()!=null&& !(NumberUtils.isCreatable(String.valueOf(categoryMapEntry.getKey())))){
shopBaseProductSpec.setStore_id(Integer.valueOf(storeId));
shopBaseProductSpec.setSpec_category_id((Integer) categoryMapEntry.getValue());
shopBaseProductSpec.setSpec_name(categoryMapEntry.getKey()+"规格");
shopBaseProductSpecList.add(shopBaseProductSpec);
}
}
Map<String, Integer> existIdMap= checkExistingShopBaseProductSpec(shopBaseProductSpecList);
List<ShopBaseProductSpec> insertShopBaseProductSpecList=new ArrayList<>();
List<ShopBaseProductSpec> updateShopBaseProductSpecList=new ArrayList<>();
QueryWrapper<ShopBaseProductSpec> queryWrapper= new QueryWrapper<>();
queryWrapper.select("max(spec_id) as spec_id");
int spec_id=shopBaseProductSpecService.getOne(queryWrapper).getSpec_id()+1;
//int i=0;
for(int i=0;i<shopBaseProductSpecList.size();i++){
if(existIdMap.containsKey(shopBaseProductSpecList.get(i).getSpec_name())){
shopBaseProductSpecList.get(i).setSpec_id(existIdMap.get(shopBaseProductSpecList.get(i).getSpec_name()));
updateShopBaseProductSpecList.add(shopBaseProductSpecList.get(i));
int finalI = i;
shopBaseProductTypes.forEach(shopBaseProductType -> {
if((shopBaseProductType.getType_name()+"规格").equals(shopBaseProductSpecList.get(finalI).getSpec_name())){
shopBaseProductType.setType_spec_ids(String.valueOf(existIdMap.get(shopBaseProductSpecList.get(finalI).getSpec_name())));
}
});
}else {
shopBaseProductSpecList.get(i).setSpec_id(spec_id);
int finalSpec_id = spec_id;
int finalI1 = i;
shopBaseProductTypes.forEach(shopBaseProductType -> {
if((shopBaseProductType.getType_name()+"规格").equals(shopBaseProductSpecList.get(finalI1).getSpec_name())){
shopBaseProductType.setType_spec_ids(String.valueOf(finalSpec_id));
}
});
insertShopBaseProductSpecList.add(shopBaseProductSpecList.get(i));
}
spec_id++;
}
if(CollectionUtil.isNotEmpty(insertShopBaseProductSpecList)){
shopBaseProductSpecService.saveBatch(insertShopBaseProductSpecList,insertShopBaseProductSpecList.size());
}
if(CollectionUtil.isNotEmpty(updateShopBaseProductSpecList)){
shopBaseProductSpecService.updateBatchById(updateShopBaseProductSpecList,updateShopBaseProductSpecList.size());
}
if(CollectionUtil.isNotEmpty(shopBaseProductTypes)){
productTypeService.updateBatchById(shopBaseProductTypes,shopBaseProductTypes.size());
}
}
/**
* 检查哪些商品已存在
*/
private Map<String, Integer> checkExistingShopBaseProductSpec(List<ShopBaseProductSpec> productSpecs) {
List<String> storeProductPairs = productSpecs.stream()
.map(ShopBaseProductSpec::getSpec_name)
.distinct()
.collect(Collectors.toList());
List<ShopBaseProductSpec> existing = batchGetByStoreAndShopBaseProductSpec(storeProductPairs);
return existing.stream()
.collect(Collectors.toMap(
ShopBaseProductSpec::getSpec_name,
ShopBaseProductSpec::getSpec_id
));
}
/**
* 批量根据店铺和货号查询商品
*/
private List<ShopBaseProductSpec> batchGetByStoreAndShopBaseProductSpec(List<String> ShopBaseProductSpecStr) {
if (CollUtil.isEmpty(ShopBaseProductSpecStr)) {
return Collections.emptyList();
}
QueryWrapper<ShopBaseProductSpec> query = new QueryWrapper<>();
query.select("spec_id", "spec_name");
ShopBaseProductSpecStr.forEach(specName -> {
query.or(q -> q.eq("spec_name", specName));
});
return shopBaseProductSpecService.list(query);
}
}

View File

@ -39,14 +39,15 @@ 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.ContextUtil;
import com.suisung.mall.common.utils.I18nUtil;
import com.suisung.mall.common.utils.StringUtils;
import com.suisung.mall.core.web.service.RedisService;
import com.suisung.mall.shop.base.service.ShopBaseProductCategoryService;
import com.suisung.mall.shop.base.service.ShopBaseProductSpecService;
import com.suisung.mall.shop.number.service.ShopNumberSeqService;
import com.suisung.mall.shop.page.service.OssService;
import com.suisung.mall.shop.product.service.ShopProductSpecItemService;
import com.suisung.mall.shop.sixun.dao.SxDataDao;
import com.suisung.mall.shop.sixun.dto.DataBaseInfo;
import com.suisung.mall.shop.sixun.dto.SxCategoryModel;
@ -68,7 +69,6 @@ import org.springframework.beans.factory.annotation.Autowired;
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;
@ -130,6 +130,11 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements
@Autowired
private FileUtils fileUtils;
@Autowired
private ShopProductSpecItemService shopProductSpecItemService;
@Autowired
private ShopBaseProductSpecService baseProductSpecService;
/**
* 批量保存商品的分类
@ -531,6 +536,8 @@ public class SyncThirdDataServiceImpl extends SyncBaseThirdSxAbstract implements
syncPrimaryKey();
shopNumberSeqService.clearKey();
shopBaseProductCategoryService.clearCategoryCache(storeId);
shopProductSpecItemService.clearExistItem(Integer.valueOf(storeId));
baseProductSpecService.clearShopBaseProductSpecMap(Integer.valueOf(storeId));
List<SyncFileLog> syncFileLogs=new ArrayList<>();
for (int i = 0; i < failFolders.size(); i++) {
String path=failFolders.get(i);

View File

@ -5,4 +5,6 @@ spring:
name: mall-shop
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
time-zone: GMT+8
file:
upload-dir: /tmp/excel_uploads

View File

@ -0,0 +1,25 @@
CREATE TABLE `product_mapping` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`product_name` varchar(255) NOT NULL COMMENT '商品名称',
`spec_value` decimal(18,4) NOT NULL COMMENT '规格数据',
`spec_unit` varchar(20) NOT NULL COMMENT '规格单位',
`description` text ,
`sort_order` int(11) NOT NULL DEFAULT '0' COMMENT '排序值',
`store_id` int unsigned NOT NULL DEFAULT '0' COMMENT '店铺编号',
PRIMARY KEY (`id`),
KEY `idx_product_name` (`product_name`) USING BTREE,
KEY `idx_store_id` (`store_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品映射表';
alter table shop_product_base add column `unit_name` varchar(2) DEFAULT NULL COMMENT '总量单位';
alter table shop_product_base add column `shop_weight` DECIMAL(18,4) NOT NULL COMMENT '总重量'
alter table shop_product_base add column `unit_price` DECIMAL(18,4) NOT NULL COMMENT '单价';
alter table shop_base_product_spec add column `store_id` int unsigned NOT NULL DEFAULT '0' COMMENT '店铺编号';
alter table shop_base_product_spec add index `store_id` (`store_id`) USING BTREE;
INSERT INTO mall_dev.shop_base_product_state
(product_state_id, product_state_name, product_state_text_1, product_state_text_2, product_state_remark)
VALUES(1003, 'PRODUCT_STATE_OFF_THE_SHELF_UNCHECK', '下架未分配商品', '同步数据未分配', '');
alter table store_db_config add column refresh_time datetime DEFAULT NULL COMMENT '刷新时间';