Commit 7b931415 by Lizh

按照ts代码逻辑迁移商品保存与根据商品id或sku查询商品详情接口

parent 627d26f8
package com.jomalls.custom.app.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 批量加入黑名单 DTO
* <p>
* 对齐 TS 项目 {@code dto/custom.product.dto.ts} 中的 {@code AddBlackListDTO}。
*
* @author Lizh
* @date 2026-06-06
*/
@Data
@Schema(description = "批量加入黑名单请求")
public class AddBlackListDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "商品 ID 列表", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "商品 ID 列表不能为空")
private List<Integer> productIds;
@Schema(description = "DIY 用户 ID 列表", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "DIY 用户 ID 列表不能为空")
private List<Integer> diyUserIds;
}
package com.jomalls.custom.app.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 批量绑定/解绑 DIY 用户 DTO
* <p>
* 对齐 TS 项目 {@code dto/custom.product.dto.ts} 中的 {@code BindDiyUserDTO}。
*
* @author Lizh
* @date 2026-06-06
*/
@Data
@Schema(description = "批量绑定DIY用户请求")
public class BindDiyUserDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "商品 ID 列表", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "商品 ID 列表不能为空")
private List<Integer> productIds;
@Schema(description = "DIY 用户 ID 列表(传空列表表示解绑所有)", requiredMode = Schema.RequiredMode.REQUIRED)
private List<Integer> diyUserIds;
}
...@@ -8,7 +8,6 @@ import lombok.NoArgsConstructor; ...@@ -8,7 +8,6 @@ import lombok.NoArgsConstructor;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date;
/** /**
* *
...@@ -20,7 +19,7 @@ import java.util.Date; ...@@ -20,7 +19,7 @@ import java.util.Date;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@Schema(description = "图片Dto") @Schema(description = "图片Dto")
public class CustomProductImageDTO implements Serializable { public class CustomProductImageSnakeDTO implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
...@@ -34,13 +33,13 @@ public class CustomProductImageDTO implements Serializable { ...@@ -34,13 +33,13 @@ public class CustomProductImageDTO implements Serializable {
* 商品ID * 商品ID
*/ */
@Schema(description = "商品ID") @Schema(description = "商品ID")
private Integer productId; private Integer product_id;
/** /**
* 图片地址 * 图片地址
*/ */
@Schema(description = "图片地址") @Schema(description = "图片地址")
private String imageUrl; private String image_url;
/** /**
* 排序 * 排序
...@@ -54,11 +53,4 @@ public class CustomProductImageDTO implements Serializable { ...@@ -54,11 +53,4 @@ public class CustomProductImageDTO implements Serializable {
@Schema(description = "类型 0普通图片 1尺码图") @Schema(description = "类型 0普通图片 1尺码图")
private Integer type; private Integer type;
/**
*
*/
@Schema(description = "")
private Date createTime;
} }
package com.jomalls.custom.app.dto;
import com.baomidou.mybatisplus.annotation.TableField;
import com.jomalls.custom.page.PageRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
/**
* Entity
*
* @author huanying
* @date 2026-06-02 19:07:12
*/
@Data
public class CustomProductInfoDTO extends PageRequest implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private Integer id;
/**
* sku
*/
@Schema(description = "sku", example = "JM260602001")
@Size(max = 20, message = "sku长度不能超过20个字符")
private String sku;
/**
*
*/
@Schema(description = "title", example = "")
@Size(max = 255, message = "title长度不能超过255个字符")
private String title;
/**
* 商品名称
*/
@Schema(description = "name", example = "")
@NotNull(message= "商品名称不能为空")
@Size(max = 255, message = "sku长度不能超过255个字符")
private String name;
/**
* 商品主图
*/
@Schema(description = "图片主图", example = "")
@NotBlank(message = "商品主图不能为空")
@Size(max = 255, message = "图片主图长度不能超过255个字符")
private String imgUrl;
/**
* 商品类别ID
*/
@Schema(description = "商品类别ID", example = "")
@NotBlank(message = "商品类别不能为空")
private Integer category_id;
/**
* 重量kg
*/
@Schema(description = "重量(kg)", example = "")
@NotNull(message= "重量不能为空")
@Digits(integer = 15, fraction = 2, message = "重量(kg)数值最多保留2位小数")
private BigDecimal weight;
/**
* 最小采购量
*/
@Schema(description = "最小采购量", example = "")
private Integer purchasingMin;
/**
* 工厂价(¥)
*/
@Schema(description = "工厂价(¥)", example = "")
@Digits(integer = 15, fraction = 2, message = "工厂价数值最多保留2位小数")
private BigDecimal factoryPrice;
/**
* 销售价(¥)
*/
@Schema(description = "销售价(¥)", example = "")
@Digits(integer = 15, fraction = 2, message = "销售价数值最多保留2位小数")
private BigDecimal salesPrice;
/**
* 销售价最大值(¥)
*/
@Schema(description = "销售价最高价(¥)", example = "")
@Digits(integer = 15, fraction = 2, message = "销售价最高价数值最多保留2位小数")
private BigDecimal salesPriceMax;
/**
* 1待上架 10已上架 20已下架 30待下架 40已作废
*/
@Schema(description = "状态:1待上架 10已上架 20已下架 30待下架 40已作废", example = "")
private Integer status;
/**
* 挂起前的状态
*/
private Integer preSuspendStatus;
/**
*
*/
@Schema(description = "商品属性ID 1", example = "")
@NotNull(message= "商品属性不能为空")
private Integer property1CateId;
/**
*
*/
@Schema(description = "商品属性ID 2", example = "")
private Integer property2CateId;
/**
*
*/
@Schema(description = "商品属性ID 3", example = "")
private Integer property3CateId;
/**
*
*/
@Schema(description = "商品属性英文名称 1", example = "")
@NotNull(message= "商品属性名不能为空")
private String property1Enname;
/**
*
*/
@Schema(description = "商品属性英文名称 2", example = "")
private String property2Enname;
/**
*
*/
@Schema(description = "商品属性英文名称 3", example = "")
private String property3Enname;
/**
* 颜色图
*/
@Schema(description = "颜色图", example = "")
private String colorImages;
/**
* 材质
*/
@Schema(description = "材质", example = "")
@NotNull(message= "材质不能为空")
private String material;
/**
* 印花类型 0满印 1局部印
*/
@Schema(description = "印花类型 0满印 1局部印", example = "")
private Integer printType;
/**
* 货号
*/
@Schema(description = "货号", example = "")
@Size(max = 20, message = "货号长度不能超过20个字符")
private String productNo;
/**
* 产地code
*/
@Schema(description = "产地code", example = "")
@NotNull(message= "产地不能为空")
@Size(max = 20, message = "产地编码长度不能超过20个字符")
private String originCode;
/**
* 产地中文名字
*/
@Schema(description = "产地中文名字", example = "")
@NotNull(message= "产地中文名字不能为空")
@Size(max = 20, message = "产地中文名字长度不能超过20个字符")
private String originNameCn;
/**
* 产地英文名字
*/
@Schema(description = "产地英文名字", example = "")
@NotNull(message= "产地英文名字不能为空")
@Size(max = 20, message = "产地英文名字长度不能超过20个字符")
private String originNameEn;
/**
* 币种code
*/
@Schema(description = "币种编码", example = "")
@NotNull(message= "币种名字不能为空")
@Size(max = 20, message = "币种编码长度不能超过20个字符")
private String currencyCode;
/**
* 币种
*/
@Schema(description = "currency_name", example = "")
@NotNull(message= "币种名字不能为空")
@Size(max = 20, message = "币种名称长度不能超过20个字符")
private String currencyName;
/**
* 平台直营-platform 客户自营-customer
*/
@Schema(description = "平台直营-platform 客户自营-customer", example = "")
@NotNull(message= "产品类型不能为空")
private String productType;
/**
* 工厂id
*/
@Schema(description = "工厂ID", example = "")
@NotNull(message= "工厂ID不能为空")
private Integer factoryId;
/**
* 是否九猫处理
*/
@Schema(description = "是否九猫处理", example = "")
private Integer processing;
/**
*
*/
@TableField("sort")
private Integer sort;
/**
* 默认模ID
*/
@Schema(description = "模型ID", example = "")
private Integer diyId;
@Schema(description = "商品明细", implementation = ProductChangeDTO.class)
private ProductChangeDTO productChange;
@Schema(description = "商品明细", implementation = CustomProductItemDTO.class)
private List<CustomProductItemDTO> productList;
@Schema(description = "普通属性集合", implementation = CustomProductInfoPropertyDTO.class)
private List<CustomProductInfoPropertyDTO> normalProperties;
@Schema(description = "sku属性集合", implementation = CustomProductInfoPropertyDTO.class)
private List<CustomProductInfoPropertyDTO> skuProperties;
@Schema(description = "商品图片集合", implementation = CustomProductImageDTO.class)
private List<CustomProductImageDTO> imageList;
@Schema(description = "尺码图片集合", implementation = CustomProductImageDTO.class)
private List<CustomProductImageDTO> sizeList;
}
...@@ -8,6 +8,7 @@ import lombok.NoArgsConstructor; ...@@ -8,6 +8,7 @@ import lombok.NoArgsConstructor;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.List;
/** /**
* @author huanying * @author huanying
...@@ -23,28 +24,29 @@ public class CustomProductInfoPropertyDTO implements Serializable { ...@@ -23,28 +24,29 @@ public class CustomProductInfoPropertyDTO implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* * ID
*/ */
@Schema(description = "") @Schema(description = "")
private Integer id; private Integer id;
/** /**
* custom_product_info表id * 中文名称
*/ */
@Schema(description = "custom_product_info表id") @Schema(description = "中文名称")
private Integer infoId; private String cnname;
/** /**
* 属性类ID * 英文名称
*/ */
@Schema(description = "属性类ID") @Schema(description = "英文名称")
private Integer propertyId; private String enname;
/** /**
* 属性值ID * 排序
*/ */
@Schema(description = "属性值ID") @Schema(description = "排序")
private Integer valueId; private Integer sort;
/** /**
* 是否为SKU属性 * 是否为SKU属性
...@@ -52,5 +54,39 @@ public class CustomProductInfoPropertyDTO implements Serializable { ...@@ -52,5 +54,39 @@ public class CustomProductInfoPropertyDTO implements Serializable {
@Schema(description = "是否为SKU属性") @Schema(description = "是否为SKU属性")
private Boolean skuProperty; private Boolean skuProperty;
/**
* SKU属性
*/
@Schema(description = "SKU属性")
private Boolean multi;
/**
* SKU属性
*/
@Schema(description = "SKU属性")
private Boolean enable;
/**
* SKU属性
*/
@Schema(description = "SKU属性")
private String categoryInfoId;
/**
* SKU属性值列表
*/
@Schema(description = "SKU属性值列表")
private List<CustomProductPropertiesValueDTO> valueList;
/**
* publicData
*/
@Schema(description = "publicData")
private Boolean publicData;
/**
* publicData
*/
@Schema(description = "propertyValueIds")
private List<String> propertyValueIds;
} }
package com.jomalls.custom.app.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
/**
* 组合创建商品 DTO
* <p>
* 对齐 TS 项目 {@code dto/custom.product.dto.ts} 中的 {@code CreateCustomProductInfoDTO}。
* 包含商品基本信息 + 所有子实体列表,由 App Service 编排写入多张表。
*
* @author Lizh
* @date 2026-06-06
*/
@Data
@Schema(description = "组合创建商品请求")
public class CustomProductInfoSaveDTO1 implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "商品名称")
@NotNull(message = "商品名称不能为空")
@Size(max = 255, message = "商品名称长度不能超过255个字符")
private String name;
@Schema(description = "title")
@Size(max = 255, message = "title长度不能超过255个字符")
private String title;
@Schema(description = "商品主图")
private String imgUrl;
@Schema(description = "商品类别ID")
private Integer categoryId;
@Schema(description = "重量(kg)")
private BigDecimal weight;
@Schema(description = "最小采购量")
private Integer purchasingMin;
@Schema(description = "工厂价(¥)")
private BigDecimal factoryPrice;
@Schema(description = "销售价(¥)")
private BigDecimal salesPrice;
@Schema(description = "销售价最高价(¥)")
private BigDecimal salesPriceMax;
@Schema(description = "状态:1待上架 10已上架 20已下架 30待下架 40已作废")
private Integer status;
@Schema(description = "商品属性分类ID 1")
private Integer property1CateId;
@Schema(description = "商品属性分类ID 2")
private Integer property2CateId;
@Schema(description = "商品属性分类ID 3")
private Integer property3CateId;
@Schema(description = "商品属性英文名称 1")
private String property1Enname;
@Schema(description = "商品属性英文名称 2")
private String property2Enname;
@Schema(description = "商品属性英文名称 3")
private String property3Enname;
@Schema(description = "颜色图(JSON字符串)")
private String colorImages;
@Schema(description = "材质")
private String material;
@Schema(description = "印花类型:0满印 1局部印")
private Integer printType;
@Schema(description = "货号")
@Size(max = 20, message = "货号长度不能超过20个字符")
private String productNo;
@Schema(description = "产地编码")
private String originCode;
@Schema(description = "产地中文名")
private String originNameCn;
@Schema(description = "产地英文名")
private String originNameEn;
@Schema(description = "币种编码")
private String currencyCode;
@Schema(description = "币种名称")
private String currencyName;
@Schema(description = "产品类型:platform/customer")
private String productType;
@Schema(description = "工厂ID")
private Integer factoryId;
@Schema(description = "是否九猫处理")
private Integer processing;
@Schema(description = "排序")
private Integer sort;
// ==================== 子实体列表 ====================
@Schema(description = "SKU 子项列表")
private List<CustomProductItemSnakeDTO> productList;
@Schema(description = "普通图片列表")
private List<CustomProductImageSnakeDTO> imageList;
@Schema(description = "尺码图片列表(type=1)")
private List<CustomProductImageSnakeDTO> sizeList;
@Schema(description = "SKU 属性集合")
private List<CustomProductInfoPropertyDTO> skuProperties;
@Schema(description = "普通属性集合")
private List<CustomProductInfoPropertyDTO> normalProperties;
@Schema(description = "工厂价格关联列表")
private List<FactoryPriceRelDTO> factoryPriceList;
@Schema(description = "工厂 ID 列表")
private List<Integer> factoryIds;
@Schema(description = "仓库 ID 列表")
private List<Integer> warehouseIds;
@Schema(description = "DIY 用户 ID 列表")
private List<Integer> diyUserIds;
@Schema(description = "工艺 ID 列表")
private List<Integer> craftIds;
@Schema(description = "工厂价格区间关联列表")
private List<FactoryPriceIntervalRelDTO> factoryPriceIntervalList;
@Schema(description = "英文备注")
private String remark;
@Schema(description = "中文备注")
private String cnRemark;
}
package com.jomalls.custom.app.dto;
import com.jomalls.custom.page.PageRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.math.BigDecimal;
import java.util.List;
/**
* Entity
*
* @author huanying
* @date 2026-06-02 19:07:12
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class CustomProductInfoSnakeDTO extends PageRequest {
@Serial
private static final long serialVersionUID = 1L;
/**
* 商品 ID(必填)
*/
@Schema(description = "商品 ID(必填)")
private Integer id;
/**
* SKU 筛选
*/
@Schema(description = "SKU 筛选")
@Size(max = 20, message = "sku长度不能超过20个字符")
private String sku;
/**
* 商品名称
*/
@Schema(description = "name", example = "")
@NotNull(message= "商品名称不能为空")
@Size(max = 255, message = "商品名称长度不能超过255个字符")
private String name;
/**
* 商品类别ID
*/
@Schema(description = "商品类别ID", example = "")
@NotNull(message = "商品类别不能为空")
private Integer category_id;
/**
* 材质
*/
@Schema(description = "材质", example = "")
@NotNull(message= "材质不能为空")
private String material;
/**
* 印花类型:0满印 1局部印
*/
@Schema(description = "印花类型:0满印 1局部印")
private Integer print_type;
/**
* 货号
*/
@Schema(description = "货号")
@Size(max = 20, message = "货号长度不能超过20个字符")
private String product_no;
/**
* 产地编码
*/
@Schema(description = "产地编码")
private String origin_code;
/**
* 产地中文名
*/
@Schema(description = "产地中文名")
private String origin_name_cn;
/**
* 产地英文名
*/
@Schema(description = "产地英文名")
private String origin_name_en;
/**
* 币种编码
*/
@Schema(description = "币种编码")
private String currency_code;
/**
* 币种名称
*/
@Schema(description = "币种名称")
private String currency_name;
/**
* 产品类型:platform/customer
*/
@Schema(description = "产品类型:platform/customer")
private String product_type;
/**
* 仓库 ID 列表
*/
@Schema(description = "仓库 ID 列表")
private List<Integer> warehouseIds;
/**
* 商品title
*/
@Schema(description = "title")
@Size(max = 255, message = "title长度不能超过255个字符")
private String title;
/**
* 工艺 ID 列表
*/
@Schema(description = "工艺 ID 列表")
private List<Integer> craftIds;
/**
* 状态:1待上架 10已上架 20已下架 30待下架 40已作废
*/
@Schema(description = "状态:1待上架 10已上架 20已下架 30待下架 40已作废")
private Integer status;
/**
* 是否九猫处理
*/
@Schema(description = "是否九猫处理")
private Boolean processing;
/**
* 英文备注
*/
@Schema(description = "英文备注")
private String remark;
/**
* 中文备注
*/
@Schema(description = "中文备注")
private String cnRemark;
/**
* 图片列表
*/
@Schema(description = "普通图片列表")
private List<CustomProductImageSnakeDTO> imageList;
/**
* 尺码图片列表
*/
@Schema(description = "尺码图片列表(type=1)")
private List<CustomProductImageSnakeDTO> sizeList;
/**
* 颜色图
*/
@Schema(description = "颜色图(JSON字符串)")
private String color_images;
/**
* 商品主图
*/
@Schema(description = "商品主图")
private String img_url;
/**
* 工厂价格关联列表
*/
@Schema(description = "工厂价格关联列表")
private List<FactoryPriceRelDTO> factoryPriceList;
/**
* SKU 属性集合
*/
@Schema(description = "SKU 属性集合")
private List<CustomProductInfoPropertyDTO> skuProperties;
/**
* 商品明细
*/
@Schema(description = "商品明细", implementation = CustomProductItemSnakeDTO.class)
private List<CustomProductItemSnakeDTO> productList;
/**
* 工厂价(¥)
*/
@Schema(description = "工厂价(¥)", example = "")
@Digits(integer = 15, fraction = 2, message = "工厂价数值最多保留2位小数")
private BigDecimal factory_price;
/**
* 销售价(¥)
*/
@Schema(description = "销售价(¥)")
@Digits(integer = 15, fraction = 2, message = "工厂价数值最多保留2位小数")
private BigDecimal sales_price;
/**
* 销售价最高价(¥)
*/
@Schema(description = "销售价最高价(¥)")
@Digits(integer = 15, fraction = 2, message = "工厂价数值最多保留2位小数")
private BigDecimal sales_price_max;
/**
* 排序
*/
@Schema(description = "排序")
private Integer sort;
/**
*
*/
@Schema(description = "商品属性ID 1", example = "")
@NotNull(message= "商品属性不能为空")
private Integer property1_cate_id;
/**
*
*/
@Schema(description = "商品属性英文名称 1", example = "")
@NotNull(message= "商品属性名不能为空")
private String property1_enname;
/**
*
*/
@Schema(description = "商品属性ID 2", example = "")
private Integer property2_cate_id;
/**
*
*/
@Schema(description = "商品属性英文名称 2", example = "")
private String property2_enname;
/**
*
*/
@Schema(description = "商品属性ID 3", example = "")
private Integer property3_cate_id;
/**
*
*/
@Schema(description = "商品属性英文名称 3", example = "")
private String property3_enname;
/**
* 重量kg
*/
@Schema(description = "重量(kg)", example = "")
@NotNull(message= "重量不能为空")
@Digits(integer = 15, fraction = 2, message = "重量(kg)数值最多保留2位小数")
private BigDecimal weight;
/**
* 工厂价格区间关联列表
*/
@Schema(description = "工厂价格区间关联列表")
private List<FactoryPriceIntervalRelDTO> factoryPriceIntervalList;
/**
* DIY 用户 ID 列表(绑定客户)
*/
@Schema(description = "DIY 用户 ID 列表")
private Integer diyUserId;
/**
* 普通属性集合
*/
@Schema(description = "普通属性集合")
private List<CustomProductInfoPropertyDTO> normalProperties;
/**
* 黑名单ID
*/
@Schema(description = "黑名单ID")
private Integer blackUserId;
@Schema(description = "工厂 ID 列表")
private List<Integer> factoryIds;
// ==================== ERP 专用查询字段(对齐 TS ERPQueryProductInfoDTO) ====================
@Schema(description = "DIY 模板 SKU 筛选(ERP)")
private String diySku;
@Schema(description = "用户标识(ERP userMark)")
private String userMark;
@Schema(description = "来源筛选(ERP namespace)")
private String source;
@Schema(description = "仓库国家筛选(ERP)")
private String warehouseCountry;
@Schema(description = "DIY 模板 ID(ERP)")
private Integer diyId;
}
package com.jomalls.custom.app.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 组合更新商品 DTO
* <p>
* 对齐 TS 项目 {@code dto/custom.product.dto.ts} 中的 {@code UpdateCustomProductInfoDTO}。
* 继承 SaveDTO 的字段,额外增加变更列表用于差异化更新。
*
* @author Lizh
* @date 2026-06-06
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Schema(description = "组合更新商品请求")
public class CustomProductInfoUpdateDTO extends CustomProductInfoSaveDTO1 implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "商品 ID(必填)")
private Integer id;
@Schema(description = "子项变更列表(增/删/改)", implementation = ProductChangeDTO.class)
private ProductChangeDTO productChange;
@Schema(description = "工厂价格变更列表(增/删/改)", implementation = ProductFactoryPriceChangeDTO.class)
private ProductFactoryPriceChangeDTO productFactoryPriceChange;
@Schema(description = "图片变更列表(增/删/改)", implementation = ProductImageChangeDTO.class)
private ProductImageChangeDTO productImageChange;
@Schema(description = "尺码图变更列表(增/删/改)", implementation = ProductImageChangeDTO.class)
private ProductImageChangeDTO productSizeChange;
/**
* 工厂价格变更 DTO
*/
@Data
@Schema(description = "工厂价格变更")
public static class ProductFactoryPriceChangeDTO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "新增列表")
private List<FactoryPriceRelDTO> addList;
@Schema(description = "修改列表")
private List<FactoryPriceRelDTO> updateList;
@Schema(description = "删除 ID 列表")
private List<Integer> removeList;
}
/**
* 图片变更 DTO
*/
@Data
@Schema(description = "图片变更")
public static class ProductImageChangeDTO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "新增列表")
private List<CustomProductImageSnakeDTO> addList;
@Schema(description = "删除 ID 列表")
private List<Integer> removeList;
}
}
...@@ -9,7 +9,7 @@ import lombok.NoArgsConstructor; ...@@ -9,7 +9,7 @@ import lombok.NoArgsConstructor;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date; import java.util.List;
/** /**
* Model * Model
...@@ -22,7 +22,7 @@ import java.util.Date; ...@@ -22,7 +22,7 @@ import java.util.Date;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@Schema(description = "skuItem Dto") @Schema(description = "skuItem Dto")
public class CustomProductItemDTO implements Serializable { public class CustomProductItemSnakeDTO implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
...@@ -33,10 +33,10 @@ public class CustomProductItemDTO implements Serializable { ...@@ -33,10 +33,10 @@ public class CustomProductItemDTO implements Serializable {
private Integer id; private Integer id;
/** /**
* 商品ID * 排序
*/ */
@Schema(description = "商品ID") @Schema(description = "排序")
private Integer productId; private Integer sort;
/** /**
* sku * sku
...@@ -48,163 +48,144 @@ public class CustomProductItemDTO implements Serializable { ...@@ -48,163 +48,144 @@ public class CustomProductItemDTO implements Serializable {
* sku商品名称 * sku商品名称
*/ */
@Schema(description = "sku商品名称") @Schema(description = "sku商品名称")
private String skuName; private String sku_name;
/** /**
* 封面图 * sku克重
*/ */
@Schema(description = "封面图") @Schema(description = "sku克重")
private String image; private BigDecimal sku_weight;
/** /**
* 图片集 *
*/ */
@Schema(description = "图片集") @Schema(description = "")
private String imageAry; private Integer property1_id;
/** /**
* 属性分类ID1 * 属性名称1
*/ */
@Schema(description = "属性分类ID1") @Schema(description = "属性名称1")
private Integer propertyCateId1; private String option_enname1;
/** /**
* 属性分类ID2 *
*/ */
@Schema(description = "属性分类ID2") @Schema(description = "")
private Integer propertyCateId2; private String custom_value1;
/** /**
* 属性分类ID3 * 属性分类ID1
*/ */
@Schema(description = "属性分类ID3") @Schema(description = "属性分类ID1")
private Integer propertyCateId3; private String property_code1;
/** /**
* * 属性分类ID1
*/ */
@Schema(description = "") @Schema(description = "属性分类ID1")
private Integer property1Id; private Integer property_cate_id1;
/** /**
* *
*/ */
@Schema(description = "") @Schema(description = "")
private Integer property2Id; private Integer property2_id;
/**
* 属性名称2
*/
@Schema(description = "属性名称2")
private String option_enname2;
/** /**
* *
*/ */
@Schema(description = "") @Schema(description = "")
private Integer property3Id; private String custom_value2;
/** /**
* 属性编码1 * 属性分类ID2
*/ */
@Schema(description = "属性编码1") @Schema(description = "属性分类ID2")
private String propertyCode1; private String property_code2;
/** /**
* 属性编码2 * 属性分类ID2
*/ */
@Schema(description = "属性编码2") @Schema(description = "属性分类ID2")
private String propertyCode2; private Integer property_cate_id2;
/** /**
* *
*/ */
@Schema(description = "") @Schema(description = "")
private String propertyCode3; private Integer property3_id;
/** /**
* 属性名称1 * 属性名称3
*/ */
@Schema(description = "属性名称1") @Schema(description = "属性名称3")
private String optionEnname1; private String option_enname3;
/** /**
* 属性名称2 *
*/ */
@Schema(description = "属性名称2") @Schema(description = "")
private String optionEnname2; private String custom_value3;
/** /**
* * 属性分类ID3
*/ */
@Schema(description = "") @Schema(description = "属性分类ID3")
private String optionEnname3; private String property_code3;
/** /**
* * 属性分类ID3
*/ */
@Schema(description = "") @Schema(description = "属性分类ID3")
private String customValue1; private Integer property_cate_id3;
/** /**
* *
*/ */
@Schema(description = "") @Schema(description = "")
private String customValue2; private String relation_ids;
/** /**
* *
*/ */
@Schema(description = "") @Schema(description = "")
private String customValue3; private List<String> relation;
/** /**
* 工厂价 * 工厂价
*/ */
@Schema(description = "工厂价") @Schema(description = "工厂价")
private BigDecimal factoryPrice; private BigDecimal factory_price;
/** /**
* 销售价 * 销售价
*/ */
@Schema(description = "销售价") @Schema(description = "销售价")
private BigDecimal salesPrice; private BigDecimal sales_price;
/** /**
* sku克重 * 封面图
*/
@Schema(description = "sku克重")
private BigDecimal skuWeight;
/**
* 印花类型 0满印 1局部印
*/
@Schema(description = "印花类型 0满印 1局部印")
private Integer printType;
/**
* 排序
*/ */
@Schema(description = "排序") @Schema(description = "封面图")
private Integer sort; private String image;
/** /**
* 货号 * 图片集Json串
*/ */
@Schema(description = "货号") @Schema(description = "图片集Json串")
private String productNo; private String image_ary;
/** /**
* 1正常码 2大码 * 1正常码 2大码
*/ */
@Schema(description = "1正常码 2大码") @Schema(description = "1正常码 2大码")
private Integer sizeType; private Integer size_type;
/**
* 创建时间
*/
@Schema(description = "创建时间")
private Date createTime;
/**
* 更新时间
*/
@Schema(description = "更新时间")
private Date updateTime;
} }
package com.jomalls.custom.app.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
*
* @author huanying
* @date 2026-06-03 11:57:01
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "图片Dto")
public class CustomProductPropertiesValueDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
*
*/
@Schema(description = "")
private Integer id;
/**
* 类别编码
*/
@Schema(description = "类别编码")
private String cateCode;
/**
* 类别名称
*/
@Schema(description = "类别名称")
private String cateName;
/**
* 编码
*/
@Schema(description = "编码")
private String code;
/**
* 中文名称
*/
@Schema(description = "中文名称")
private String cnname;
/**
* 英文名称
*/
@Schema(description = "英文名称")
private String enname;
/**
* 前景色
*/
@Schema(description = "前景色")
private String fontColor;
/**
* 背景色
*/
@Schema(description = "背景色")
private String bgColor;
/**
*
*/
@Schema(description = "")
private Boolean battery;
/**
*
*/
@Schema(description = "")
private Boolean liquid;
/**
*
*/
@Schema(description = "")
private Boolean knife;
/**
*
*/
@Schema(description = "")
private Integer selected;
/**
*
*/
@Schema(description = "")
private Boolean publicData;
}
package com.jomalls.custom.app.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 工厂价格区间关联 DTO
* <p>
* 对齐 TS 项目 {@code dto/custom.product.dto.ts} 中的 {@code FactoryPriceIntervalRelDTO}。
*
* @author Lizh
* @date 2026-06-06
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "工厂价格区间关联")
public class FactoryPriceIntervalRelDTO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "币种编码")
private String currencyCode;
@Schema(description = "系统成本最低价")
private BigDecimal priceMin;
@Schema(description = "系统成本最高价")
private BigDecimal priceMax;
}
package com.jomalls.custom.app.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 工厂价格关联 DTO
* <p>
* 对齐 TS 项目 {@code dto/custom.product.dto.ts} 中的 {@code CreateFactoryPriceRelDTO}。
*
* @author Lizh
* @date 2026-06-06
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "工厂价格关联")
public class FactoryPriceRelDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "子项 ID(custom_product_item 的 id)")
private Integer itemId;
@Schema(description = "子项 SKU")
private String itemSku;
@Schema(description = "工厂 ID")
private Integer factoryId;
@Schema(description = "工厂价格")
private BigDecimal factoryPrice;
@Schema(description = "销售价格")
private BigDecimal salesPrice;
@Schema(description = "工厂币种编码")
private String factoryCurrencyCode;
@Schema(description = "销售币种编码")
private String salesCurrencyCode;
}
...@@ -15,11 +15,11 @@ import java.util.List; ...@@ -15,11 +15,11 @@ import java.util.List;
@AllArgsConstructor @AllArgsConstructor
@Schema(description = "商品变更Dto") @Schema(description = "商品变更Dto")
public class ProductChangeDTO implements Serializable { public class ProductChangeDTO implements Serializable {
@Schema(description = "添加集合", implementation = CustomProductItemDTO.class) @Schema(description = "添加集合", implementation = CustomProductItemSnakeDTO.class)
private List<CustomProductItemDTO> addList; private List<CustomProductItemSnakeDTO> addList;
@Schema(description = "修改集合", implementation = CustomProductItemDTO.class) @Schema(description = "修改集合", implementation = CustomProductItemSnakeDTO.class)
private List<CustomProductItemDTO> updateList; private List<CustomProductItemSnakeDTO> updateList;
@Schema(description = "删除集合", implementation = Integer.class) @Schema(description = "删除集合", implementation = Integer.class)
private List<Integer> removeList; private List<Integer> removeList;
......
package com.jomalls.custom.app.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 定制商品状态枚举
* <p>
* 对齐 TS 项目 {@code enums/CustomProductInfoStatus.ts} 中的状态定义。
*
* @author Lizh
* @date 2026-06-06
*/
@Getter
public enum CustomProductInfoStatusEnum {
/** 待上架 */
TO_BE_SHELF(1, "待上架"),
/** 已上架 */
SHELF(10, "已上架"),
/** 已下架 */
REMOVED(20, "已下架"),
/** 待下架 */
TO_BE_REMOVED(30, "待下架"),
/** 已作废 */
VOIDED(40, "已作废");
private final int code;
private final String label;
CustomProductInfoStatusEnum(int code, String label) {
this.code = code;
this.label = label;
}
/**
* 根据状态码获取枚举
*
* @param code 状态码
* @return 对应的枚举实例,未匹配时返回 null
*/
public static CustomProductInfoStatusEnum getByCode(int code) {
return Arrays.stream(values())
.filter(e -> e.code == code)
.findFirst()
.orElse(null);
}
/**
* 获取所有状态列表(供前端下拉框使用)
*
* @return [{code, label}, ...]
*/
public static List<Map<String, Object>> getAllStatusList() {
return Arrays.stream(values())
.map(e -> {
Map<String, Object> map = new HashMap<>();
map.put("code", e.code);
map.put("label", e.label);
return map;
})
.collect(Collectors.toList());
}
}
package com.jomalls.custom.app.enums;
import lombok.Getter;
import java.util.Arrays;
import java.util.List;
/**
* 模板状态枚举
* <p>
* 对齐 TS 项目 {@code TemplateStatus}。
*
* @author Lizh
* @date 2026-06-08
*/
@Getter
public enum TemplateStatus {
REMOVED(0, "已下架"),
SHELF(1, "已上架"),
WAIT_SHELF(20, "待上架"),
COMPLETE(40, "建模完成"),
REFINE(50, "多变体完善"),
TEST(60, "产前测试"),
IN_BUILD(70, "建模中"),
TO_BE_CONFIRMED(75, "待确认"),
WAIT_ASSIGNMENT(80, "待分派"),
IN_THE_PLATE(85, "打板中"),
TO_BE_REVIEWED(90, "待审核");
private final int code;
private final String remark;
TemplateStatus(int code, String remark) {
this.code = code;
this.remark = remark;
}
public static TemplateStatus getByCode(int code) {
return Arrays.stream(values())
.filter(s -> s.code == code)
.findFirst()
.orElse(null);
}
/** demo 用户可见的状态列表 */
public static final List<Integer> DEMO_CODE_LIST = Arrays.asList(
TEST.code, REFINE.code, COMPLETE.code, WAIT_SHELF.code, SHELF.code);
/** 上架状态码 */
public static final int SHELF_CODE = SHELF.code;
}
...@@ -2,7 +2,7 @@ package com.jomalls.custom.app.service; ...@@ -2,7 +2,7 @@ package com.jomalls.custom.app.service;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jomalls.custom.app.vo.CustomProductImagePageVO; import com.jomalls.custom.app.vo.CustomProductImagePageVO;
import com.jomalls.custom.app.vo.CustomProductImageVO; import com.jomalls.custom.app.vo.CustomProductImageSnakeVO;
import java.util.List; import java.util.List;
...@@ -17,10 +17,10 @@ public interface CustomProductImageService { ...@@ -17,10 +17,10 @@ public interface CustomProductImageService {
/** /**
* 列表查询接口 * 列表查询接口
* *
* @param customProductImageVO 条件model * @param customProductImageSnakeVO 条件model
* @return list集合 * @return list集合
*/ */
List<CustomProductImageVO> list(CustomProductImageVO customProductImageVO); List<CustomProductImageSnakeVO> list(CustomProductImageSnakeVO customProductImageSnakeVO);
/** /**
* 根据条件查询分页列表接口 * 根据条件查询分页列表接口
...@@ -28,7 +28,7 @@ public interface CustomProductImageService { ...@@ -28,7 +28,7 @@ public interface CustomProductImageService {
* @param customProductImagePageVO 分页入参model * @param customProductImagePageVO 分页入参model
* @return 分页对象 * @return 分页对象
*/ */
IPage<CustomProductImageVO> pageList(CustomProductImagePageVO customProductImagePageVO); IPage<CustomProductImageSnakeVO> pageList(CustomProductImagePageVO customProductImagePageVO);
/** /**
* 根据id查询详情 * 根据id查询详情
...@@ -36,21 +36,21 @@ public interface CustomProductImageService { ...@@ -36,21 +36,21 @@ public interface CustomProductImageService {
* @param id 主键 * @param id 主键
* @return 实体model * @return 实体model
*/ */
CustomProductImageVO info(Integer id); CustomProductImageSnakeVO info(Integer id);
/** /**
* 保存对象 * 保存对象
* *
* @param customProductImageVO 保存对象 * @param customProductImageSnakeVO 保存对象
*/ */
void save(CustomProductImageVO customProductImageVO); void save(CustomProductImageSnakeVO customProductImageSnakeVO);
/** /**
* 根据id修改对象 * 根据id修改对象
* *
* @param customProductImageVO 修改对象 * @param customProductImageSnakeVO 修改对象
*/ */
void updateById(CustomProductImageVO customProductImageVO); void updateById(CustomProductImageSnakeVO customProductImageSnakeVO);
/** /**
* 根据主键ID进行删除 * 根据主键ID进行删除
......
...@@ -2,7 +2,7 @@ package com.jomalls.custom.app.service; ...@@ -2,7 +2,7 @@ package com.jomalls.custom.app.service;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jomalls.custom.app.vo.CustomProductInfoPropertyPageVO; import com.jomalls.custom.app.vo.CustomProductInfoPropertyPageVO;
import com.jomalls.custom.app.vo.CustomProductInfoPropertyVO; import com.jomalls.custom.app.vo.CustomProductInfoPropertySnakeVO;
import java.util.List; import java.util.List;
...@@ -17,10 +17,10 @@ public interface CustomProductInfoPropertyService { ...@@ -17,10 +17,10 @@ public interface CustomProductInfoPropertyService {
/** /**
* 列表查询接口 * 列表查询接口
* *
* @param customProductInfoPropertyVO 条件model * @param customProductInfoPropertySnakeVO 条件model
* @return list集合 * @return list集合
*/ */
List<CustomProductInfoPropertyVO> list(CustomProductInfoPropertyVO customProductInfoPropertyVO); List<CustomProductInfoPropertySnakeVO> list(CustomProductInfoPropertySnakeVO customProductInfoPropertySnakeVO);
/** /**
* 根据条件查询分页列表接口 * 根据条件查询分页列表接口
...@@ -28,7 +28,7 @@ public interface CustomProductInfoPropertyService { ...@@ -28,7 +28,7 @@ public interface CustomProductInfoPropertyService {
* @param customProductInfoPropertyPageVO 分页入参model * @param customProductInfoPropertyPageVO 分页入参model
* @return 分页对象 * @return 分页对象
*/ */
IPage<CustomProductInfoPropertyVO> pageList(CustomProductInfoPropertyPageVO customProductInfoPropertyPageVO); IPage<CustomProductInfoPropertySnakeVO> pageList(CustomProductInfoPropertyPageVO customProductInfoPropertyPageVO);
/** /**
* 根据id查询详情 * 根据id查询详情
...@@ -36,21 +36,21 @@ public interface CustomProductInfoPropertyService { ...@@ -36,21 +36,21 @@ public interface CustomProductInfoPropertyService {
* @param id 主键 * @param id 主键
* @return 实体model * @return 实体model
*/ */
CustomProductInfoPropertyVO info(Integer id); CustomProductInfoPropertySnakeVO info(Integer id);
/** /**
* 保存对象 * 保存对象
* *
* @param customProductInfoPropertyVO 保存对象 * @param customProductInfoPropertySnakeVO 保存对象
*/ */
void save(CustomProductInfoPropertyVO customProductInfoPropertyVO); void save(CustomProductInfoPropertySnakeVO customProductInfoPropertySnakeVO);
/** /**
* 根据id修改对象 * 根据id修改对象
* *
* @param customProductInfoPropertyVO 修改对象 * @param customProductInfoPropertySnakeVO 修改对象
*/ */
void updateById(CustomProductInfoPropertyVO customProductInfoPropertyVO); void updateById(CustomProductInfoPropertySnakeVO customProductInfoPropertySnakeVO);
/** /**
* 根据主键ID进行删除 * 根据主键ID进行删除
......
package com.jomalls.custom.app.service; package com.jomalls.custom.app.service;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jomalls.custom.app.dto.CustomProductInfoDTO; import com.jomalls.custom.app.dto.AddBlackListDTO;
import com.jomalls.custom.app.dto.BindDiyUserDTO;
import com.jomalls.custom.app.dto.CustomProductInfoSnakeDTO;
import com.jomalls.custom.app.dto.CustomProductInfoUpdateDTO;
import com.jomalls.custom.app.vo.CustomProductInfoSnakeVO;
import com.jomalls.custom.app.vo.CustomProductInfoVO; import com.jomalls.custom.app.vo.CustomProductInfoVO;
import com.jomalls.custom.app.vo.DbDiyVO;
import java.util.List; import java.util.List;
/** /**
* @author Lizh * @author Lizh
* @version 0.01 * @version 0.01
* @description: 接口 * @description: 定制商品服务接口
* @date 2026-05-29 10:43:29 * @date 2026-05-29 10:43:29
*/ */
public interface CustomProductInfoService { public interface CustomProductInfoService {
IPage<CustomProductInfoVO> pageList(CustomProductInfoSnakeDTO param);
/** /**
* 列表查询接口 * 组合创建商品(SKU 生成 + 事务内写入主表及所有子表)
* *
* @param customProductInfoVO 条件model * @param dto 组合创建 DTO
* @return list集合
*/ */
List<CustomProductInfoVO> list(CustomProductInfoVO customProductInfoVO); void saveFull(CustomProductInfoSnakeDTO dto);
/** /**
* 根据条件查询分页列表接口 * 组合更新商品(事务内处理主表及子表的增/删/改差异)
* *
* @param param 分页入参model * @param dto 组合更新 DTO
* @return 分页对象
*/ */
IPage<CustomProductInfoVO> pageList(CustomProductInfoDTO param); void updateFull(CustomProductInfoUpdateDTO dto);
/** /**
* 根据id查询详情 * 根据 ID 或 SKU 加载商品完整数据(并行单表查询后在 Java 层组合)
* *
* @param id 主键 * @param id 商品 ID(可为 null)
* @return 实体model * @param sku 商品 SKU(可为 null)
* @param namespace 命名空间(可选)
* @return 完整商品 VO
*/ */
CustomProductInfoVO info(Integer id); CustomProductInfoSnakeVO getByIdOrSku(Integer id, String sku, String namespace);
/** /**
* 保存对象 * 批量绑定/解绑 DIY 用户到商品
* *
* @param customProductInfoVO 保存对象 * @param dto 绑定请求 DTO
*/ */
void save(CustomProductInfoVO customProductInfoVO); void bindsDiyUser(BindDiyUserDTO dto);
/** /**
* 根据id修改对象 * 批量将 DIY 用户加入商品黑名单
* *
* @param customProductInfoVO 修改对象 * @param dto 黑名单请求 DTO
*/ */
void updateById(CustomProductInfoVO customProductInfoVO); void addBlackList(AddBlackListDTO dto);
/** /**
* 根据主键ID进行删除 * 设置商品的默认 DIY 模板
* *
* @param id 主键 * @param id 商品 ID
* @param diyId DIY 模板 ID
* @param diySku DIY 模板 SKU
*/ */
void deleteById(Integer id); void bindDefaultDiy(Integer id, Integer diyId, String diySku);
} /**
* 获取商品绑定的 DIY 模板列表
*
* @param id 商品 ID
* @return DIY 模板列表
*/
List<DbDiyVO> getBindsDiyById(Integer id);
/**
* 获取商品绑定的 DIY 用户 ID 列表
*
* @param id 商品 ID
* @return DIY 用户 ID 列表
*/
List<Integer> getBindsDiyUserById(Integer id);
/**
* 获取商品黑名单 DIY 用户 ID 列表
*
* @param id 商品 ID
* @return 黑名单用户 ID 列表
*/
List<Integer> getBlackListById(Integer id);
/**
* 获取商品绑定的工艺 ID 列表
*
* @param id 商品 ID
* @return 工艺 ID 列表
*/
List<Long> getCraftById(Integer id);
// ==================== ERP 专用接口(对齐 TS) ====================
/**
* ERP 分页查询(对齐 TS erpPage)
* <p>
* 包含黑名单过滤、用户折扣、模板上架状态等 ERP 特定逻辑。
*/
IPage<CustomProductInfoVO> erpPage(CustomProductInfoSnakeDTO param);
/**
* ERP 获取绑定 DIY(对齐 TS getBindsDiyByIdAndUserMark)
*/
List<DbDiyVO> getErpBindsDiyById(Integer id, String userMark, String namespace);
}
...@@ -2,7 +2,7 @@ package com.jomalls.custom.app.service; ...@@ -2,7 +2,7 @@ package com.jomalls.custom.app.service;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jomalls.custom.app.vo.CustomProductItemPageVO; import com.jomalls.custom.app.vo.CustomProductItemPageVO;
import com.jomalls.custom.app.vo.CustomProductItemVO; import com.jomalls.custom.app.vo.CustomProductItemSnakeVO;
import java.util.List; import java.util.List;
...@@ -17,10 +17,10 @@ public interface CustomProductItemService { ...@@ -17,10 +17,10 @@ public interface CustomProductItemService {
/** /**
* 列表查询接口 * 列表查询接口
* *
* @param customProductItemVO 条件model * @param customProductItemSnakeVO 条件model
* @return list集合 * @return list集合
*/ */
List<CustomProductItemVO> list(CustomProductItemVO customProductItemVO); List<CustomProductItemSnakeVO> list(CustomProductItemSnakeVO customProductItemSnakeVO);
/** /**
* 根据条件查询分页列表接口 * 根据条件查询分页列表接口
...@@ -28,7 +28,7 @@ public interface CustomProductItemService { ...@@ -28,7 +28,7 @@ public interface CustomProductItemService {
* @param customProductItemPageVO 分页入参model * @param customProductItemPageVO 分页入参model
* @return 分页对象 * @return 分页对象
*/ */
IPage<CustomProductItemVO> pageList(CustomProductItemPageVO customProductItemPageVO); IPage<CustomProductItemSnakeVO> pageList(CustomProductItemPageVO customProductItemPageVO);
/** /**
* 根据id查询详情 * 根据id查询详情
...@@ -36,21 +36,21 @@ public interface CustomProductItemService { ...@@ -36,21 +36,21 @@ public interface CustomProductItemService {
* @param id 主键 * @param id 主键
* @return 实体model * @return 实体model
*/ */
CustomProductItemVO info(Integer id); CustomProductItemSnakeVO info(Integer id);
/** /**
* 保存对象 * 保存对象
* *
* @param customProductItemVO 保存对象 * @param customProductItemSnakeVO 保存对象
*/ */
void save(CustomProductItemVO customProductItemVO); void save(CustomProductItemSnakeVO customProductItemSnakeVO);
/** /**
* 根据id修改对象 * 根据id修改对象
* *
* @param customProductItemVO 修改对象 * @param customProductItemSnakeVO 修改对象
*/ */
void updateById(CustomProductItemVO customProductItemVO); void updateById(CustomProductItemSnakeVO customProductItemSnakeVO);
/** /**
* 根据主键ID进行删除 * 根据主键ID进行删除
......
package com.jomalls.custom.app.service;
import com.jomalls.custom.app.vo.CustomProductInfoSnakeVO;
import com.jomalls.custom.dal.entity.DbDiyUserEntity;
/**
* DIY 用户服务
* <p>
* 对齐 TS 项目 {@code diyUserService} 中与商品详情相关的功能。
*
* @author Lizh
* @date 2026-06-08
*/
public interface DiyUserService {
/**
* 根据 namespace 查询用户
* <p>
* 对齐 TS {@code getByNamespace(namespace)}
*
* @param namespace 用户名称
* @return 用户实体,未找到返回 null
*/
DbDiyUserEntity getByNamespace(String namespace);
/**
* 根据 userMark 查询用户
* <p>
* 对齐 TS {@code getByUserMark(userMark)}
*/
DbDiyUserEntity getByUserMark(String userMark);
/**
* 对商品详情应用用户折扣外部定价
* <p>
* 对齐 TS {@code setProductExternalPrice(user, customProductInfo)}。
* 根据用户折扣比例,对商品以及子项、工厂价格、价格区间的销售价进行打折。
*
* @param user 用户(含 discount 折扣字段)
* @param vo 商品完整详情
*/
void setProductExternalPrice(DbDiyUserEntity user, CustomProductInfoSnakeVO vo);
}
package com.jomalls.custom.app.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jomalls.custom.app.vo.ProductFactoryRelPageVO;
import com.jomalls.custom.app.vo.ProductFactoryRelVO;
import java.util.List;
/**
* 产品-工厂关联 App Service 接口
*
* @author Lizh
* @date 2026-06-06
*/
public interface ProductFactoryRelService {
List<ProductFactoryRelVO> list(ProductFactoryRelVO vo);
IPage<ProductFactoryRelVO> pageList(ProductFactoryRelPageVO param);
ProductFactoryRelVO info(Integer id);
void save(ProductFactoryRelVO vo);
void updateById(ProductFactoryRelVO vo);
void deleteById(Integer id);
}
...@@ -4,7 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; ...@@ -4,7 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jomalls.custom.app.exception.ServiceException; import com.jomalls.custom.app.exception.ServiceException;
import com.jomalls.custom.app.vo.CustomProductImagePageVO; import com.jomalls.custom.app.vo.CustomProductImagePageVO;
import com.jomalls.custom.app.vo.CustomProductImageVO; import com.jomalls.custom.app.vo.CustomProductImageSnakeVO;
import com.jomalls.custom.app.service.CustomProductImageService; import com.jomalls.custom.app.service.CustomProductImageService;
import com.jomalls.custom.app.utils.BeanMapper; import com.jomalls.custom.app.utils.BeanMapper;
import com.jomalls.custom.app.utils.CustomAsserts; import com.jomalls.custom.app.utils.CustomAsserts;
...@@ -37,34 +37,34 @@ public class CustomProductImageServiceImpl implements CustomProductImageService ...@@ -37,34 +37,34 @@ public class CustomProductImageServiceImpl implements CustomProductImageService
} }
@Override @Override
public List<CustomProductImageVO> list(CustomProductImageVO customProductImageVO) { public List<CustomProductImageSnakeVO> list(CustomProductImageSnakeVO customProductImageSnakeVO) {
QueryWrapper<CustomProductImageEntity> queryWrapper = new QueryWrapper<>(); QueryWrapper<CustomProductImageEntity> queryWrapper = new QueryWrapper<>();
// TODO 根据业务条件组装入参 // TODO 根据业务条件组装入参
List<CustomProductImageEntity> list = customProductImageDomainService.list(queryWrapper); List<CustomProductImageEntity> list = customProductImageDomainService.list(queryWrapper);
return list.stream().map(e -> BeanMapper.mapper().convert(e, CustomProductImageVO.class)).collect(Collectors.toList()); return list.stream().map(e -> BeanMapper.mapper().convert(e, CustomProductImageSnakeVO.class)).collect(Collectors.toList());
} }
@Override @Override
public IPage<CustomProductImageVO> pageList(CustomProductImagePageVO customProductImagePageVO) { public IPage<CustomProductImageSnakeVO> pageList(CustomProductImagePageVO customProductImagePageVO) {
CustomAsserts.nonNull(customProductImagePageVO, "分页查询参数不能为空"); CustomAsserts.nonNull(customProductImagePageVO, "分页查询参数不能为空");
QueryWrapper<CustomProductImageEntity> queryWrapper = new QueryWrapper<>(); QueryWrapper<CustomProductImageEntity> queryWrapper = new QueryWrapper<>();
// TODO 根据业务条件组装入参 // TODO 根据业务条件组装入参
IPage<CustomProductImageEntity> page = customProductImageDomainService.selectPage(queryWrapper, customProductImagePageVO); IPage<CustomProductImageEntity> page = customProductImageDomainService.selectPage(queryWrapper, customProductImagePageVO);
return page.convert(e -> BeanMapper.mapper().convert(e, CustomProductImageVO.class)); return page.convert(e -> BeanMapper.mapper().convert(e, CustomProductImageSnakeVO.class));
} }
@Override @Override
public CustomProductImageVO info(Integer id) { public CustomProductImageSnakeVO info(Integer id) {
CustomAsserts.nonNull(id, "主键id不能为空"); CustomAsserts.nonNull(id, "主键id不能为空");
CustomProductImageEntity customProductImage = customProductImageDomainService.getById(id); CustomProductImageEntity customProductImage = customProductImageDomainService.getById(id);
return BeanMapper.mapper().convert(customProductImage, CustomProductImageVO.class); return BeanMapper.mapper().convert(customProductImage, CustomProductImageSnakeVO.class);
} }
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@Override @Override
public void save(CustomProductImageVO customProductImageVO) { public void save(CustomProductImageSnakeVO customProductImageSnakeVO) {
CustomAsserts.nonNull(customProductImageVO, "实体对象不能为空"); CustomAsserts.nonNull(customProductImageSnakeVO, "实体对象不能为空");
CustomProductImageEntity customProductImageEntity = BeanMapper.mapper().convert(customProductImageVO, CustomProductImageEntity.class); CustomProductImageEntity customProductImageEntity = BeanMapper.mapper().convert(customProductImageSnakeVO, CustomProductImageEntity.class);
try { try {
customProductImageDomainService.save(customProductImageEntity); customProductImageDomainService.save(customProductImageEntity);
} catch (DuplicateKeyException e) { } catch (DuplicateKeyException e) {
...@@ -75,9 +75,9 @@ public class CustomProductImageServiceImpl implements CustomProductImageService ...@@ -75,9 +75,9 @@ public class CustomProductImageServiceImpl implements CustomProductImageService
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@Override @Override
public void updateById(CustomProductImageVO customProductImageVO) { public void updateById(CustomProductImageSnakeVO customProductImageSnakeVO) {
CustomAsserts.nonNull(customProductImageVO, "实体对象不能为空"); CustomAsserts.nonNull(customProductImageSnakeVO, "实体对象不能为空");
CustomProductImageEntity customProductImage = BeanMapper.mapper().convert(customProductImageVO, CustomProductImageEntity.class); CustomProductImageEntity customProductImage = BeanMapper.mapper().convert(customProductImageSnakeVO, CustomProductImageEntity.class);
try { try {
customProductImageDomainService.updateById(customProductImage); customProductImageDomainService.updateById(customProductImage);
} catch (DuplicateKeyException e) { } catch (DuplicateKeyException e) {
......
...@@ -4,7 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; ...@@ -4,7 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jomalls.custom.app.exception.ServiceException; import com.jomalls.custom.app.exception.ServiceException;
import com.jomalls.custom.app.vo.CustomProductInfoPropertyPageVO; import com.jomalls.custom.app.vo.CustomProductInfoPropertyPageVO;
import com.jomalls.custom.app.vo.CustomProductInfoPropertyVO; import com.jomalls.custom.app.vo.CustomProductInfoPropertySnakeVO;
import com.jomalls.custom.app.service.CustomProductInfoPropertyService; import com.jomalls.custom.app.service.CustomProductInfoPropertyService;
import com.jomalls.custom.app.utils.BeanMapper; import com.jomalls.custom.app.utils.BeanMapper;
import com.jomalls.custom.app.utils.CustomAsserts; import com.jomalls.custom.app.utils.CustomAsserts;
...@@ -37,34 +37,34 @@ public class CustomProductInfoPropertyServiceImpl implements CustomProductInfoPr ...@@ -37,34 +37,34 @@ public class CustomProductInfoPropertyServiceImpl implements CustomProductInfoPr
} }
@Override @Override
public List<CustomProductInfoPropertyVO> list(CustomProductInfoPropertyVO customProductInfoPropertyVO) { public List<CustomProductInfoPropertySnakeVO> list(CustomProductInfoPropertySnakeVO customProductInfoPropertySnakeVO) {
QueryWrapper<CustomProductInfoPropertyEntity> queryWrapper = new QueryWrapper<>(); QueryWrapper<CustomProductInfoPropertyEntity> queryWrapper = new QueryWrapper<>();
// TODO 根据业务条件组装入参 // TODO 根据业务条件组装入参
List<CustomProductInfoPropertyEntity> list = customProductInfoPropertyDomainService.list(queryWrapper); List<CustomProductInfoPropertyEntity> list = customProductInfoPropertyDomainService.list(queryWrapper);
return list.stream().map(e -> BeanMapper.mapper().convert(e, CustomProductInfoPropertyVO.class)).collect(Collectors.toList()); return list.stream().map(e -> BeanMapper.mapper().convert(e, CustomProductInfoPropertySnakeVO.class)).collect(Collectors.toList());
} }
@Override @Override
public IPage<CustomProductInfoPropertyVO> pageList(CustomProductInfoPropertyPageVO customProductInfoPropertyPageVO) { public IPage<CustomProductInfoPropertySnakeVO> pageList(CustomProductInfoPropertyPageVO customProductInfoPropertyPageVO) {
CustomAsserts.nonNull(customProductInfoPropertyPageVO, "分页查询参数不能为空"); CustomAsserts.nonNull(customProductInfoPropertyPageVO, "分页查询参数不能为空");
QueryWrapper<CustomProductInfoPropertyEntity> queryWrapper = new QueryWrapper<>(); QueryWrapper<CustomProductInfoPropertyEntity> queryWrapper = new QueryWrapper<>();
// TODO 根据业务条件组装入参 // TODO 根据业务条件组装入参
IPage<CustomProductInfoPropertyEntity> page = customProductInfoPropertyDomainService.selectPage(queryWrapper, customProductInfoPropertyPageVO); IPage<CustomProductInfoPropertyEntity> page = customProductInfoPropertyDomainService.selectPage(queryWrapper, customProductInfoPropertyPageVO);
return page.convert(e -> BeanMapper.mapper().convert(e, CustomProductInfoPropertyVO.class)); return page.convert(e -> BeanMapper.mapper().convert(e, CustomProductInfoPropertySnakeVO.class));
} }
@Override @Override
public CustomProductInfoPropertyVO info(Integer id) { public CustomProductInfoPropertySnakeVO info(Integer id) {
CustomAsserts.nonNull(id, "主键id不能为空"); CustomAsserts.nonNull(id, "主键id不能为空");
CustomProductInfoPropertyEntity customProductInfoProperty = customProductInfoPropertyDomainService.getById(id); CustomProductInfoPropertyEntity customProductInfoProperty = customProductInfoPropertyDomainService.getById(id);
return BeanMapper.mapper().convert(customProductInfoProperty, CustomProductInfoPropertyVO.class); return BeanMapper.mapper().convert(customProductInfoProperty, CustomProductInfoPropertySnakeVO.class);
} }
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@Override @Override
public void save(CustomProductInfoPropertyVO customProductInfoPropertyVO) { public void save(CustomProductInfoPropertySnakeVO customProductInfoPropertySnakeVO) {
CustomAsserts.nonNull(customProductInfoPropertyVO, "实体对象不能为空"); CustomAsserts.nonNull(customProductInfoPropertySnakeVO, "实体对象不能为空");
CustomProductInfoPropertyEntity customProductInfoPropertyEntity = BeanMapper.mapper().convert(customProductInfoPropertyVO, CustomProductInfoPropertyEntity.class); CustomProductInfoPropertyEntity customProductInfoPropertyEntity = BeanMapper.mapper().convert(customProductInfoPropertySnakeVO, CustomProductInfoPropertyEntity.class);
try { try {
customProductInfoPropertyDomainService.save(customProductInfoPropertyEntity); customProductInfoPropertyDomainService.save(customProductInfoPropertyEntity);
} catch (DuplicateKeyException e) { } catch (DuplicateKeyException e) {
...@@ -75,9 +75,9 @@ public class CustomProductInfoPropertyServiceImpl implements CustomProductInfoPr ...@@ -75,9 +75,9 @@ public class CustomProductInfoPropertyServiceImpl implements CustomProductInfoPr
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@Override @Override
public void updateById(CustomProductInfoPropertyVO customProductInfoPropertyVO) { public void updateById(CustomProductInfoPropertySnakeVO customProductInfoPropertySnakeVO) {
CustomAsserts.nonNull(customProductInfoPropertyVO, "实体对象不能为空"); CustomAsserts.nonNull(customProductInfoPropertySnakeVO, "实体对象不能为空");
CustomProductInfoPropertyEntity customProductInfoProperty = BeanMapper.mapper().convert(customProductInfoPropertyVO, CustomProductInfoPropertyEntity.class); CustomProductInfoPropertyEntity customProductInfoProperty = BeanMapper.mapper().convert(customProductInfoPropertySnakeVO, CustomProductInfoPropertyEntity.class);
try { try {
customProductInfoPropertyDomainService.updateById(customProductInfoProperty); customProductInfoPropertyDomainService.updateById(customProductInfoProperty);
} catch (DuplicateKeyException e) { } catch (DuplicateKeyException e) {
......
package com.jomalls.custom.app.service.impl; package com.jomalls.custom.app.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jomalls.custom.app.dto.CustomProductInfoDTO; import com.jomalls.custom.app.dto.*;
import com.jomalls.custom.app.enums.SkuGenerateEnums;
import com.jomalls.custom.app.exception.ServiceException; import com.jomalls.custom.app.exception.ServiceException;
import com.jomalls.custom.integrate.service.BaseCategoryInfoService; import com.jomalls.custom.app.enums.TemplateStatus;
import com.jomalls.custom.integrate.model.BaseCategoryInfoVO;
import com.jomalls.custom.app.vo.CustomProductInfoVO;
import com.jomalls.custom.app.service.CustomProductInfoService; import com.jomalls.custom.app.service.CustomProductInfoService;
import com.jomalls.custom.app.service.DiyUserService;
import com.jomalls.custom.app.service.SysBillRuleService;
import com.jomalls.custom.app.utils.BeanMapper; import com.jomalls.custom.app.utils.BeanMapper;
import com.jomalls.custom.app.utils.CustomAsserts; import com.jomalls.custom.app.utils.CustomAsserts;
import com.jomalls.custom.dal.entity.CustomProductInfoEntity; import com.jomalls.custom.app.vo.*;
import com.jomalls.custom.domain.service.CustomProductInfoDomainService; import com.jomalls.custom.dal.entity.*;
import com.jomalls.custom.domain.service.*;
import com.jomalls.custom.integrate.model.BaseCategoryInfoModel;
import com.jomalls.custom.integrate.model.BasePropertyModel;
import com.jomalls.custom.integrate.model.BasePropertyValueModel;
import com.jomalls.custom.integrate.service.SaasAdminService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionTemplate;
import java.util.List; import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
/** /**
* @author Lizh * @author Lizh
* @version 0.01 * @version 0.02
* @description: 接口实现 * @description: 定制商品服务实现(含组合编排逻辑)
* @date 2026-05-29 10:43:29 * @date 2026-05-29 10:43:29
*/ */
@Slf4j @Slf4j
...@@ -33,42 +50,62 @@ import java.util.stream.Collectors; ...@@ -33,42 +50,62 @@ import java.util.stream.Collectors;
public class CustomProductInfoServiceImpl implements CustomProductInfoService { public class CustomProductInfoServiceImpl implements CustomProductInfoService {
private final CustomProductInfoDomainService customProductInfoDomainService; private final CustomProductInfoDomainService customProductInfoDomainService;
private final BaseCategoryInfoService baseCategoryInfoService; private final SaasAdminService saasAdminService;
private final SysBillRuleService sysBillRuleService;
private final TransactionTemplate transactionTemplate;
private final CustomProductItemDomainService customProductItemDomainService;
private final CustomProductImageDomainService customProductImageDomainService;
private final CustomProductRemarkDomainService customProductRemarkDomainService;
private final CustomProductCnRemarkDomainService customProductCnRemarkDomainService;
private final CustomProductInfoPropertyDomainService customProductInfoPropertyDomainService;
private final CustomProductFactoryPriceRelDomainService customProductFactoryPriceRelDomainService;
private final CustomProductFactoryPriceIntervalRelDomainService customProductFactoryPriceIntervalRelDomainService;
private final CustomProductDiyUserRelDomainService customProductDiyUserRelDomainService;
private final CustomProductCraftRelDomainService customProductCraftRelDomainService;
private final CustomProductWarehouseRelDomainService customProductWarehouseRelDomainService;
private final CustomWarehouseInfoDomainService customWarehouseInfoDomainService;
private final CustomProductBlacklistDomainService customProductBlacklistDomainService;
private final LogCustomProductDomainService logCustomProductDomainService;
private final DbDiyDomainService dbDiyDomainService;
private final ProductFactoryRelDomainService productFactoryRelDomainService;
private final ProductTemplateInfoDomainService productTemplateInfoDomainService;
private final DbDiyXiaoguotuDomainService dbDiyXiaoguotuDomainService;
private final DiyUserService diyUserService;
private final ThreadPoolExecutor threadPoolExecutor;
/** 图片类型:普通图片 */
private static final int IMAGE_TYPE_NORMAL = 0;
/** 图片类型:尺码图 */
private static final int IMAGE_TYPE_SIZE = 1;
/** 并行查询超时时间(秒) */
private static final long QUERY_TIMEOUT_SECONDS = 30;
@Override
public List<CustomProductInfoVO> list(CustomProductInfoVO customProductInfoVO) {
QueryWrapper<CustomProductInfoEntity> queryWrapper = new QueryWrapper<>();
// TODO 根据业务条件组装入参
List<CustomProductInfoEntity> list = customProductInfoDomainService.list(queryWrapper);
return list.stream().map(e -> BeanMapper.mapper().convert(e, CustomProductInfoVO.class)).collect(Collectors.toList());
}
@Override @Override
public IPage<CustomProductInfoVO> pageList(CustomProductInfoDTO param) { public IPage<CustomProductInfoVO> pageList(CustomProductInfoSnakeDTO param) {
CustomAsserts.nonNull(param, "分页查询参数不能为空"); CustomAsserts.nonNull(param, "分页查询参数不能为空");
QueryWrapper<CustomProductInfoEntity> queryWrapper = new QueryWrapper<>(); QueryWrapper<CustomProductInfoEntity> queryWrapper = new QueryWrapper<>();
toQueryWrapper(param,queryWrapper); // 构造查询条件
// TODO 根据业务条件组装入参 toQueryWrapper(param, queryWrapper);
IPage<CustomProductInfoEntity> page = customProductInfoDomainService.selectPage(queryWrapper, param); IPage<CustomProductInfoEntity> page = customProductInfoDomainService.selectPage(queryWrapper, param);
return page.convert(e -> BeanMapper.mapper().convert(e, CustomProductInfoVO.class)); return page.convert(e -> BeanMapper.mapper().convert(e, CustomProductInfoVO.class));
} }
private void toQueryWrapper(CustomProductInfoDTO param, QueryWrapper<CustomProductInfoEntity> queryWrapper) { private void toQueryWrapper(CustomProductInfoSnakeDTO param, QueryWrapper<CustomProductInfoEntity> queryWrapper) {
if(param.getCategory_id() !=null ){ // 分类层级过滤
// 获取所有分类列表 if (param.getCategory_id() != null) {
List<BaseCategoryInfoVO> cateList = baseCategoryInfoService.getAllList(); List<BaseCategoryInfoModel> cateList = saasAdminService.getAllList();
// 查找匹配的分类 if (cateList == null) {
BaseCategoryInfoVO cate = cateList.stream() cateList = Collections.emptyList();
}
BaseCategoryInfoModel cate = cateList.stream()
.filter(c -> c.getId().equals(param.getCategory_id())) .filter(c -> c.getId().equals(param.getCategory_id()))
.findFirst() .findFirst()
.orElseThrow(() -> new ServiceException("不存在该类别")); .orElseThrow(() -> new ServiceException("不存在该类别, category_id=" + param.getCategory_id()));
// 构建 pids
String pids = String.valueOf(cate.getId()); String pids = String.valueOf(cate.getId());
if (cate.getPids() != null && !cate.getPids().isEmpty()) { if (cate.getPids() != null && !cate.getPids().isEmpty()) {
pids = cate.getPids() + "," + pids; pids = cate.getPids() + "," + pids;
} }
// 筛选子分类
String finalPids = pids; String finalPids = pids;
List<Integer> cateIds = cateList.stream() List<Integer> cateIds = cateList.stream()
.filter(item -> { .filter(item -> {
...@@ -76,63 +113,1203 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -76,63 +113,1203 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
return finalPids.equals(itemPids) || return finalPids.equals(itemPids) ||
(itemPids != null && itemPids.startsWith(finalPids + ",")); (itemPids != null && itemPids.startsWith(finalPids + ","));
}) })
.map(BaseCategoryInfoVO::getId) .map(BaseCategoryInfoModel::getId)
.collect(Collectors.toList()); .collect(Collectors.toList());
// 添加当前分类ID
cateIds.add(cate.getId()); cateIds.add(cate.getId());
// 设置参数 queryWrapper.in("category_id", cateIds);
queryWrapper.in("category_id",cateIds); }
// 是否九猫处理过滤
if (param.getProcessing()) {
queryWrapper.eq("processing", 1);
} else {
queryWrapper.eq("processing", 0);
}
// 直接列等值过滤
if (param.getId() != null) {
queryWrapper.eq("id", param.getId());
}
if (param.getSku() != null && !param.getSku().isEmpty()) {
queryWrapper.eq("sku", param.getSku());
}
if (param.getStatus() != null) {
queryWrapper.eq("status", param.getStatus());
}
if (param.getProduct_type() != null && !param.getProduct_type().isEmpty()) {
queryWrapper.eq("product_type", param.getProduct_type());
}
if (param.getProduct_no() != null && !param.getProduct_no().isEmpty()) {
queryWrapper.eq("product_no", param.getProduct_no());
}
if (param.getPrint_type() != null) {
queryWrapper.eq("print_type", param.getPrint_type());
}
if (param.getName() != null && !param.getName().isEmpty()) {
queryWrapper.like("name", param.getName());
}
if (param.getTitle() != null && !param.getTitle().isEmpty()) {
queryWrapper.like("title", param.getTitle());
}
// 工厂过滤(factory_id 是 custom_product_info 的直接列)
if (param.getFactoryIds() != null) {
queryWrapper.in("factory_id", param.getFactoryIds());
}
// DIY 用户过滤 与 黑名单过滤:需要查询关联表取 productId 列表,两者取交集
List<Integer> idListFromDiyUser = null;
List<Integer> idListFromBlacklist = null;
if (param.getDiyUserId() != null) {
if (param.getDiyUserId() == -1) {
// 查询未绑定任何 DIY 用户的产品:先找出已绑定的 productId,再 NOT IN
List<Integer> boundIds = customProductDiyUserRelDomainService.list().stream()
.map(CustomProductDiyUserRelEntity::getProductId)
.distinct()
.collect(Collectors.toList());
if (!boundIds.isEmpty()) {
queryWrapper.notIn("id", boundIds);
}
} else {
idListFromDiyUser = customProductDiyUserRelDomainService.list(
new LambdaQueryWrapper<CustomProductDiyUserRelEntity>()
.eq(CustomProductDiyUserRelEntity::getDiyUserId, param.getDiyUserId()))
.stream()
.map(CustomProductDiyUserRelEntity::getProductId)
.distinct()
.collect(Collectors.toList());
if (idListFromDiyUser.isEmpty()) {
// 该 DIY 用户未绑定任何商品,直接返回空结果
queryWrapper.eq("id", -1);
}
} }
if (param.getProcessing() != null || param.getProcessing() == 0) {
if (param.getProcessing() == 1) {
queryWrapper.eq("processing",1);
} else if (param.getProcessing() == 0) {
queryWrapper.eq("processing",0);
} else if (param.getProcessing() == 2) {
queryWrapper.isNull("processing");
} }
if (param.getBlackUserId() != null) {
idListFromBlacklist = customProductBlacklistDomainService.list(
new LambdaQueryWrapper<CustomProductBlacklistEntity>()
.eq(CustomProductBlacklistEntity::getDiyUserId, param.getBlackUserId()))
.stream()
.map(CustomProductBlacklistEntity::getProductId)
.distinct()
.collect(Collectors.toList());
}
// diyUserId 和 blackUserId 同时存在时取交集
if (idListFromDiyUser != null && idListFromBlacklist != null) {
List<Integer> intersection = idListFromDiyUser.stream()
.filter(idListFromBlacklist::contains)
.collect(Collectors.toList());
if (intersection.isEmpty()) {
queryWrapper.eq("id", -1); // 交集为空,返回空结果
} else {
queryWrapper.in("id", intersection);
} }
} else if (idListFromDiyUser != null) {
queryWrapper.in("id", idListFromDiyUser);
} else if (idListFromBlacklist != null) {
queryWrapper.in("id", idListFromBlacklist);
}
queryWrapper.orderByDesc("id"); queryWrapper.orderByDesc("id");
} }
@Override @Override
public CustomProductInfoVO info(Integer id) { public void saveFull(CustomProductInfoSnakeDTO dto) {
CustomAsserts.nonNull(id, "主键id不能为空"); CustomAsserts.nonNull(dto, "创建参数不能为空");
CustomProductInfoEntity customProductInfo = customProductInfoDomainService.getById(id);
return BeanMapper.mapper().convert(customProductInfo, CustomProductInfoVO.class); // 1. 生成 SKU 调用SysBillRuleService
String sku = sysBillRuleService.getProductSku(SkuGenerateEnums.TEMPLATE_PRODUCT.getCode());
log.debug("[ saveFull ] 生成 SKU: {}", sku);
if (StringUtils.isBlank(sku)) {
throw new ServiceException("生成 SKU 失败");
}
// 2. 事务外预先计算主表的工厂价/销售价(从子项取 min/max)
computeProductPricesFromItems(dto);
// 3. 编程式事务写入所有表
transactionTemplate.executeWithoutResult(status -> {
// 3a. 保存主表
CustomProductInfoEntity entity = buildEntityFromSaveDTO(dto, sku);
customProductInfoDomainService.save(entity);
Integer productId = entity.getId();
log.debug("[ saveFull ] 主表保存成功, productId: {}", productId);
// 3b. 保存子项(逐条保存以获取自增 ID,返回 originalSku → savedEntity 映射)
Map<String, CustomProductItemEntity> itemMap = saveProductItemsIndividually(dto.getProductList(), sku, productId, entity);
// 3c. 保存工厂价格关联(利用 itemMap 替换 item_sku 为生成的 SKU 并填充 item_id)
saveFactoryPriceRels(dto.getFactoryPriceList(), productId, itemMap);
// 3d. 保存产品-工厂关联(product_factory_rel 表)
saveFactoryRels(dto.getFactoryIds(), productId);
// 3e. 保存 DIY 用户关联
//saveDiyUserRels(dto.getDiyUserIds(), productId);
// 3f. 保存工艺关联
saveCraftRels(dto.getCraftIds(), productId);
// 3g. 保存价格区间关联
saveFactoryPriceIntervalRels(dto.getFactoryPriceIntervalList(), productId);
// 3h. 保存图片(普通图 + 尺码图)
saveImages(dto.getImageList(), dto.getSizeList(), productId);
// 3i. 保存备注(英文 + 中文)
saveRemarks(dto.getRemark(), dto.getCnRemark(), productId);
// 3j. 保存属性(SKU 属性 + 普通属性)
saveProperties(dto.getSkuProperties(), dto.getNormalProperties(), productId);
// 3k. 保存仓库关联
saveWarehouseRels(dto.getWarehouseIds(), productId);
// 3l. 事务内记录日志(对齐 TS:日志与数据写入同一事务)
saveLogTransaction(productId, "创建商品");
});
} }
@Transactional(rollbackFor = Exception.class)
@Override @Override
public void save(CustomProductInfoVO customProductInfoVO) { public void updateFull(CustomProductInfoUpdateDTO dto) {
CustomAsserts.nonNull(customProductInfoVO, "实体对象不能为空"); /*CustomAsserts.nonNull(dto, "更新参数不能为空");
CustomProductInfoEntity customProductInfoEntity = BeanMapper.mapper().convert(customProductInfoVO, CustomProductInfoEntity.class); CustomAsserts.nonNull(dto.getId(), "商品 ID 不能为空");
try {
customProductInfoDomainService.save(customProductInfoEntity); transactionTemplate.executeWithoutResult(status -> {
} catch (DuplicateKeyException e) { Integer productId = dto.getId();
log.info("[ CustomProductInfoServiceImpl save ] 实体对象唯一约束重复,请调整后再试!", e);
throw new ServiceException("实体对象唯一约束重复,请调整后再试!"); // 1. 更新主表
CustomProductInfoEntity entity = buildEntityFromSaveDTO(dto, null);
entity.setId(productId);
customProductInfoDomainService.updateById(entity);
// 2. 处理子项的增/删/改
if (dto.getProductChange() != null) {
handleItemChanges(dto.getProductChange(), productId);
} }
// 3. 处理工厂价格变更
if (dto.getProductFactoryPriceChange() != null) {
handleFactoryPriceChanges(dto.getProductFactoryPriceChange(), productId);
}
// 4. 销毁并重建属性
rebuildProperties(dto.getSkuProperties(), dto.getNormalProperties(), productId);
// 5. 处理图片变更
if (dto.getProductImageChange() != null) {
handleImageChanges(dto.getProductImageChange(), productId, IMAGE_TYPE_NORMAL);
}
if (dto.getProductSizeChange() != null) {
handleImageChanges(dto.getProductSizeChange(), productId, IMAGE_TYPE_SIZE);
}
// 6. 更新备注(先删后插)
updateRemarks(dto.getRemark(), dto.getCnRemark(), productId);
// 7. 重建工厂/仓库/工艺/DIY用户/价格区间关联(先删后插)
rebuildRels(dto.getFactoryIds(), dto.getWarehouseIds(), dto.getCraftIds(),
dto.getDiyUserIds(), dto.getFactoryPriceIntervalList(), productId);
// 8. 事务内记日志
saveLogTransaction(productId, "修改商品信息");
});*/
} }
@Transactional(rollbackFor = Exception.class)
@Override @Override
public void updateById(CustomProductInfoVO customProductInfoVO) { public CustomProductInfoSnakeVO getByIdOrSku(Integer id, String sku, String namespace) {
CustomAsserts.nonNull(customProductInfoVO, "实体对象不能为空"); // 1. 查询主表
CustomProductInfoEntity customProductInfo = BeanMapper.mapper().convert(customProductInfoVO, CustomProductInfoEntity.class); CustomProductInfoEntity entity;
if (id != null) {
entity = customProductInfoDomainService.getById(id);
} else if (StringUtils.isNotBlank(sku)) {
entity = customProductInfoDomainService.getBySku(sku);
} else {
throw new ServiceException("id 和 sku 至少需要提供一个");
}
if (entity == null) {
throw new ServiceException("商品信息不存在, id=" + id + ", sku=" + sku);
}
final Integer productId = entity.getId();
// 1a. 根据 namespace 查询 DIY 用户(对齐 TS:197-203)
DbDiyUserEntity user = null;
if (StringUtils.isNotBlank(namespace)) {
user = diyUserService.getByNamespace(namespace);
if (user == null) {
throw new ServiceException("用户不存在, namespace=" + namespace);
}
}
// 2. 并行查询所有子表(单表查询,Java 层组合,使用自定义线程池 + 超时保护)
CompletableFuture<List<CustomProductItemEntity>> itemsFuture = CompletableFuture.supplyAsync(() ->
customProductItemDomainService.list(new LambdaQueryWrapper<CustomProductItemEntity>()
.eq(CustomProductItemEntity::getProductId, productId)
.orderByAsc(CustomProductItemEntity::getSort)), threadPoolExecutor);
CompletableFuture<List<CustomProductImageEntity>> imagesFuture = CompletableFuture.supplyAsync(() ->
customProductImageDomainService.list(new LambdaQueryWrapper<CustomProductImageEntity>()
.eq(CustomProductImageEntity::getProductId, productId)), threadPoolExecutor);
CompletableFuture<List<CustomProductInfoPropertyEntity>> propertiesFuture = CompletableFuture.supplyAsync(() ->
customProductInfoPropertyDomainService.list(new LambdaQueryWrapper<CustomProductInfoPropertyEntity>()
.eq(CustomProductInfoPropertyEntity::getInfoId, productId)), threadPoolExecutor);
CompletableFuture<List<CustomProductFactoryPriceRelEntity>> factoryPriceFuture = CompletableFuture.supplyAsync(() ->
customProductFactoryPriceRelDomainService.list(new LambdaQueryWrapper<CustomProductFactoryPriceRelEntity>()
.eq(CustomProductFactoryPriceRelEntity::getProductId, productId)), threadPoolExecutor);
CompletableFuture<List<Integer>> diyUserIdsFuture = CompletableFuture.supplyAsync(() ->
customProductDiyUserRelDomainService.list(
new LambdaQueryWrapper<CustomProductDiyUserRelEntity>()
.eq(CustomProductDiyUserRelEntity::getProductId, productId))
.stream().map(CustomProductDiyUserRelEntity::getDiyUserId)
.collect(Collectors.toList()), threadPoolExecutor);
CompletableFuture<List<Long>> craftIdsFuture = CompletableFuture.supplyAsync(() ->
customProductCraftRelDomainService.list(
new LambdaQueryWrapper<CustomProductCraftRelEntity>()
.eq(CustomProductCraftRelEntity::getProductId, productId))
.stream().map(CustomProductCraftRelEntity::getCraftId)
.collect(Collectors.toList()), threadPoolExecutor);
CompletableFuture<List<Integer>> warehouseIdsFuture = CompletableFuture.supplyAsync(() ->
customProductWarehouseRelDomainService.list(
new LambdaQueryWrapper<CustomProductWarehouseRelEntity>()
.eq(CustomProductWarehouseRelEntity::getProductId, productId))
.stream().map(r -> r.getWarehouseId().intValue())
.collect(Collectors.toList()), threadPoolExecutor);
CompletableFuture<CustomProductRemarkEntity> remarkFuture = CompletableFuture.supplyAsync(() -> customProductRemarkDomainService.getOne(
new LambdaQueryWrapper<CustomProductRemarkEntity>()
.eq(CustomProductRemarkEntity::getProductId, productId)), threadPoolExecutor);
CompletableFuture<CustomProductCnRemarkEntity> cnRemarkFuture = CompletableFuture.supplyAsync(() -> customProductCnRemarkDomainService.getOne(
new LambdaQueryWrapper<CustomProductCnRemarkEntity>()
.eq(CustomProductCnRemarkEntity::getProductId, productId)), threadPoolExecutor);
// 工厂关联 ID 列表(product_factory_rel 表)
CompletableFuture<List<Integer>> factoryIdsFuture = CompletableFuture.supplyAsync(() ->
productFactoryRelDomainService.list(
new LambdaQueryWrapper<ProductFactoryRelEntity>()
.eq(ProductFactoryRelEntity::getProductId, productId))
.stream().map(ProductFactoryRelEntity::getFactoryId)
.collect(Collectors.toList()), threadPoolExecutor);
// 3. 等待所有查询完成(带超时保护,防止某个查询永久阻塞)
try {
CompletableFuture.allOf(itemsFuture, imagesFuture, propertiesFuture,
factoryPriceFuture, diyUserIdsFuture, craftIdsFuture,
warehouseIdsFuture, remarkFuture, cnRemarkFuture,
factoryIdsFuture)
.orTimeout(QUERY_TIMEOUT_SECONDS, TimeUnit.SECONDS)
.join();
} catch (CompletionException e) {
// 超时或异常时取消所有未完成的任务
cancelAll(itemsFuture, imagesFuture, propertiesFuture,
factoryPriceFuture, diyUserIdsFuture, craftIdsFuture,
warehouseIdsFuture, remarkFuture, cnRemarkFuture,
factoryIdsFuture);
throw new ServiceException("查询商品详情超时, productId=" + productId)
.setDetailMessage(e.toString());
}
// 4. Java 层组合结果
CustomProductInfoSnakeVO fullVO;
List<CustomProductInfoPropertyEntity> properties;
try { try {
customProductInfoDomainService.updateById(customProductInfo); properties = propertiesFuture.get();
} catch (DuplicateKeyException e) { fullVO = buildProductFullVO(entity,
log.info("[ CustomProductInfoServiceImpl updateById ] 实体对象唯一约束重复,请调整后再试!", e); itemsFuture.get(),
throw new ServiceException("实体对象唯一约束重复,请调整后再试!"); imagesFuture.get(),
factoryPriceFuture.get(),
diyUserIdsFuture.get(),
craftIdsFuture.get(),
warehouseIdsFuture.get(),
remarkFuture.get(),
cnRemarkFuture.get(),
factoryIdsFuture.get());
} catch (Exception e) {
throw new ServiceException("组装商品详情失败, productId=" + productId + ": " + e.getMessage())
.setDetailMessage(e.toString());
}
// 5. 通过 AdminPropertyService 解析属性名称(对齐 TS:217-236)
resolvePropertyNames(properties, fullVO);
// 6. 如果查询了用户,应用外部定价折扣(对齐 TS:241-243)
if (user != null) {
diyUserService.setProductExternalPrice(user, fullVO);
}
return fullVO;
}
/**
* 通过 AdminPropertyService 解析属性名称,填充到 FullVO 的 skuProperties / normalProperties 中
* <p>
* 对齐 TS:217-236 — 将数据库中存储的 property_id/value_id 转换为带名称的属性列表。
*
* @param properties 数据库原始属性记录
* @param fullVO 待填充的商品完整详情
*/
private void resolvePropertyNames(List<CustomProductInfoPropertyEntity> properties,
CustomProductInfoSnakeVO fullVO) {
if (properties == null || properties.isEmpty()) {
return;
}
fullVO.setProperties(properties.stream().map(e -> BeanMapper.snakeCase()
.convert(e, CustomProductInfoPropertySnakeVO.class)).collect(Collectors.toList()));
// 收集所有 property_id
String propIds = properties.stream().map(p -> String.valueOf(p.getPropertyId()))
.distinct().collect(Collectors.joining(","));
if (propIds.isEmpty()) {
return;
}
// 调用 saas admin API 获取属性定义(含 valueList)
List<BasePropertyModel> publicProperties = saasAdminService.getPropertyByIds(propIds);
if (publicProperties == null || publicProperties.isEmpty()) {
return;
}
// 收集当前商品使用的 value_id
Set<Integer> valueIds = properties.stream().map(CustomProductInfoPropertyEntity::getValueId)
.filter(Objects::nonNull).collect(Collectors.toSet());
// 按 skuProperty 拆分为 SKU 属性和普通属性(对齐 TS:234-235)
// 转换为 CustomProductInfoPropertyVO 列表
List<CustomProductInfoSkuPropertiesVO> skuProps = new ArrayList<>();
List<CustomProductInfoSkuPropertiesVO> normalProps = new ArrayList<>();
for (BasePropertyModel prop : publicProperties) {
CustomProductInfoSkuPropertiesVO propVO = BeanMapper.snakeCase().convert(prop, CustomProductInfoSkuPropertiesVO.class);
if (CollectionUtils.isNotEmpty(prop.getValueList())) {
// 过滤每个属性的 valueList,只保留当前商品使用的值(对齐 TS:222-233)
List<BasePropertyValueModel> filtered = prop.getValueList().stream()
.filter(v -> valueIds.contains(v.getId())).toList();
// 转换为 CustomProductPropertiesValueVO 列表,并赋值给valueList
propVO.setValueList(filtered.stream().map(v -> BeanMapper.mapper().convert(v, CustomProductPropertiesValueVO.class)).toList());
}
// 检查是否是SKU属性,如果是则添加到skuProps,否则添加到normalProps
(prop.getSkuProperty() ? skuProps : normalProps).add(propVO);
}
// 填充到 FullVO 中
fullVO.setSkuProperties(skuProps);
fullVO.setNormalProperties(normalProps);
}
@Override
public void bindsDiyUser(BindDiyUserDTO dto) {
CustomAsserts.nonNull(dto, "绑定参数不能为空");
CustomAsserts.nonNull(dto.getProductIds(), "商品 ID 列表不能为空");
transactionTemplate.executeWithoutResult(status -> {
for (Integer productId : dto.getProductIds()) {
// 先删除该商品的所有现有绑定
customProductDiyUserRelDomainService.remove(
new LambdaQueryWrapper<CustomProductDiyUserRelEntity>()
.eq(CustomProductDiyUserRelEntity::getProductId, productId));
// 再插入新的绑定
if (dto.getDiyUserIds() != null && !dto.getDiyUserIds().isEmpty()) {
List<CustomProductDiyUserRelEntity> rels = dto.getDiyUserIds().stream()
.map(diyUserId -> {
CustomProductDiyUserRelEntity rel = new CustomProductDiyUserRelEntity();
rel.setProductId(productId);
rel.setDiyUserId(diyUserId);
return rel;
})
.collect(Collectors.toList());
customProductDiyUserRelDomainService.saveBatch(rels);
} }
} }
saveLogBatch("绑定客户", dto.getProductIds());
});
}
@Transactional(rollbackFor = Exception.class)
@Override @Override
public void deleteById(Integer id) { public void addBlackList(AddBlackListDTO dto) {
CustomAsserts.nonNull(id, "主键id不能为空"); CustomAsserts.nonNull(dto, "黑名单参数不能为空");
customProductInfoDomainService.removeById(id); CustomAsserts.nonNull(dto.getProductIds(), "商品 ID 列表不能为空");
transactionTemplate.executeWithoutResult(status -> {
for (Integer productId : dto.getProductIds()) {
// 先删除该商品的所有现有黑名单
customProductBlacklistDomainService.remove(
new LambdaQueryWrapper<CustomProductBlacklistEntity>()
.eq(CustomProductBlacklistEntity::getProductId, productId));
// 再插入新的黑名单
if (dto.getDiyUserIds() != null && !dto.getDiyUserIds().isEmpty()) {
List<CustomProductBlacklistEntity> blacklists = dto.getDiyUserIds().stream()
.map(diyUserId -> {
CustomProductBlacklistEntity bl = new CustomProductBlacklistEntity();
bl.setProductId(productId);
bl.setDiyUserId(diyUserId);
return bl;
})
.collect(Collectors.toList());
customProductBlacklistDomainService.saveBatch(blacklists);
}
}
saveLogBatch("加入黑名单", dto.getProductIds());
});
}
@Override
public void bindDefaultDiy(Integer id, Integer diyId, String diySku) {
CustomAsserts.nonNull(id, "商品 ID 不能为空");
// 使用 LambdaUpdateWrapper 仅更新指定字段,避免 updateById 清空其他字段
customProductInfoDomainService.update(
new LambdaUpdateWrapper<CustomProductInfoEntity>()
.eq(CustomProductInfoEntity::getId, id)
.set(CustomProductInfoEntity::getDiyId, diyId)
.set(CustomProductInfoEntity::getDiySku, diySku));
} }
@Override
public List<DbDiyVO> getBindsDiyById(Integer id) {
CustomAsserts.nonNull(id, "商品 ID 不能为空");
// 注:当前 custom-server 中没有 product_template_info 表,
// TS 版本通过该表关联 DIY。此处暂时通过默认 DIY ID 返回。
CustomProductInfoEntity entity = customProductInfoDomainService.getById(id);
if (entity == null || entity.getDiyId() == null) {
return Collections.emptyList();
}
DbDiyEntity diy = dbDiyDomainService.getById(entity.getDiyId());
if (diy == null) {
return Collections.emptyList();
}
return Collections.singletonList(BeanMapper.mapper().convert(diy, DbDiyVO.class));
}
@Override
public List<Integer> getBindsDiyUserById(Integer id) {
CustomAsserts.nonNull(id, "商品 ID 不能为空");
return customProductDiyUserRelDomainService.list(
new LambdaQueryWrapper<CustomProductDiyUserRelEntity>()
.eq(CustomProductDiyUserRelEntity::getProductId, id))
.stream()
.map(CustomProductDiyUserRelEntity::getDiyUserId)
.collect(Collectors.toList());
}
@Override
public List<Integer> getBlackListById(Integer id) {
CustomAsserts.nonNull(id, "商品 ID 不能为空");
return customProductBlacklistDomainService.list(
new LambdaQueryWrapper<CustomProductBlacklistEntity>()
.eq(CustomProductBlacklistEntity::getProductId, id))
.stream()
.map(CustomProductBlacklistEntity::getDiyUserId)
.collect(Collectors.toList());
}
@Override
public List<Long> getCraftById(Integer id) {
CustomAsserts.nonNull(id, "商品 ID 不能为空");
return customProductCraftRelDomainService.list(
new LambdaQueryWrapper<CustomProductCraftRelEntity>()
.eq(CustomProductCraftRelEntity::getProductId, id))
.stream()
.map(CustomProductCraftRelEntity::getCraftId)
.collect(Collectors.toList());
}
// ==================== ERP 专用接口 ====================
@Override
public List<DbDiyVO> getErpBindsDiyById(Integer id, String userMark, String namespace) {
CustomAsserts.nonNull(id, "商品 ID 不能为空");
// 1. 根据 userMark 或 namespace 查询用户(对齐 TS:751-759)
DbDiyUserEntity user;
if (StringUtils.isNotBlank(userMark)) {
user = diyUserService.getByUserMark(userMark);
} else {
user = diyUserService.getByNamespace(namespace);
}
if (user == null) {
throw new ServiceException("客户不存在");
}
// 2. 查询 product_template_info 获取 diy_id 列表(对齐 TS:760-762)
List<ProductTemplateInfoEntity> templates = productTemplateInfoDomainService.selectByProductId(id);
if (templates.isEmpty()) {
return Collections.emptyList();
}
List<Integer> diyIds = templates.stream()
.map(ProductTemplateInfoEntity::getDiyId)
.filter(d -> d != null)
.distinct()
.collect(Collectors.toList());
// 3. 查询 db_diy,根据用户类型过滤状态(对齐 TS:766-783)
List<Integer> statusList;
String userName = user.getName();
if ("demo".equals(userName)) {
statusList = Arrays.asList(TemplateStatus.SHELF_CODE,
TemplateStatus.WAIT_SHELF.getCode()); // demo 用户可见待上架
} else {
statusList = Collections.singletonList(TemplateStatus.SHELF_CODE);
}
List<DbDiyEntity> diys = dbDiyDomainService.list(
new LambdaQueryWrapper<DbDiyEntity>()
.in(DbDiyEntity::getId, diyIds)
.in(DbDiyEntity::getStatus, statusList));
if (diys.isEmpty()) {
return Collections.emptyList();
}
// 4. 转换为 VO 并附带效果图(对齐 TS:783 include DbDiyXiaoguotu)
return diys.stream().map(diy -> {
DbDiyVO vo = BeanMapper.mapper().convert(diy, DbDiyVO.class);
List<DbDiyXiaoguotuEntity> xiaoguotus = dbDiyXiaoguotuDomainService.selectByDiyId(diy.getId());
// 将效果图数据附加到 VO(通过扩展方式,当前 DbDiyVO 无 xiaoguotu 字段,
// 如有需要可扩展 DbDiyVO)
return vo;
}).collect(Collectors.toList());
}
@Override
public IPage<CustomProductInfoVO> erpPage(CustomProductInfoSnakeDTO param) {
CustomAsserts.nonNull(param, "分页查询参数不能为空");
// 1. 查询用户(对齐 TS:683-684)
DbDiyUserEntity user = null;
if (StringUtils.isNotBlank(param.getUserMark())) {
user = diyUserService.getByUserMark(param.getUserMark());
if (user == null) {
// 用户不存在,返回空结果
return new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(param.getCurrent(), param.getSize());
}
}
// 2. 构建查询条件(复用 + ERP 特有逻辑)
QueryWrapper<CustomProductInfoEntity> queryWrapper = new QueryWrapper<>();
toQueryWrapper(param, queryWrapper);
// 3. DIY 模板过滤(对齐 TS:630-657)
if (StringUtils.isNotBlank(param.getDiySku()) || StringUtils.isNotBlank(param.getSource())
|| param.getDiyId() != null) {
LambdaQueryWrapper<DbDiyEntity> diyWrapper = new LambdaQueryWrapper<>();
if (StringUtils.isNotBlank(param.getDiySku())) {
diyWrapper.eq(DbDiyEntity::getSku, param.getDiySku());
}
if (param.getDiyId() != null) {
diyWrapper.eq(DbDiyEntity::getId, param.getDiyId());
}
if (StringUtils.isNotBlank(param.getSource())) {
diyWrapper.eq(DbDiyEntity::getNamespace, param.getSource());
}
List<DbDiyEntity> diys = dbDiyDomainService.list(diyWrapper);
if (diys.isEmpty()) {
return new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(param.getCurrent(), param.getSize());
}
List<Integer> diyIds = diys.stream().map(DbDiyEntity::getId).collect(Collectors.toList());
List<ProductTemplateInfoEntity> temps = productTemplateInfoDomainService.selectByDiyIds(diyIds);
if (temps.isEmpty()) {
return new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(param.getCurrent(), param.getSize());
}
List<Integer> productIds = temps.stream()
.map(ProductTemplateInfoEntity::getProductId)
.filter(pid -> pid != null)
.distinct()
.collect(Collectors.toList());
if (productIds.isEmpty()) {
return new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(param.getCurrent(), param.getSize());
}
queryWrapper.in("id", productIds);
}
// 4. 仓库国家过滤(对齐 TS:673-681)
if (StringUtils.isNotBlank(param.getWarehouseCountry())) {
List<CustomWarehouseInfoEntity> warehouses = customWarehouseInfoDomainService.list(
new LambdaQueryWrapper<CustomWarehouseInfoEntity>()
.eq(CustomWarehouseInfoEntity::getCountryCode, param.getWarehouseCountry()));
if (warehouses.isEmpty()) {
return new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(param.getCurrent(), param.getSize());
}
List<Integer> wIds = warehouses.stream()
.map(w -> w.getId().intValue())
.collect(Collectors.toList());
List<CustomProductWarehouseRelEntity> rels = customProductWarehouseRelDomainService.selectByWarehouseIds(wIds);
if (rels.isEmpty()) {
return new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(param.getCurrent(), param.getSize());
}
List<Integer> productIds = rels.stream()
.map(CustomProductWarehouseRelEntity::getProductId).distinct().collect(Collectors.toList());
queryWrapper.in("id", productIds);
}
// 5. 执行分页查询
queryWrapper.orderByDesc("id");
IPage<CustomProductInfoEntity> page = customProductInfoDomainService.selectPage(queryWrapper, param);
// 6. 转换并应用外部定价(对齐 TS:731-738)
// 预计算折扣率(放入 final 变量,供 lambda 使用)
final BigDecimal discountRate;
if (user != null && user.getDiscount() != null) {
discountRate = user.getDiscount().divide(new BigDecimal("100"), 4, RoundingMode.HALF_UP);
} else {
discountRate = null;
}
return page.convert(e -> {
CustomProductInfoVO vo = BeanMapper.mapper().convert(e, CustomProductInfoVO.class);
if (discountRate != null) {
if (vo.getSalesPrice() != null) {
vo.setSalesPrice(vo.getSalesPrice().multiply(discountRate).setScale(2, RoundingMode.HALF_UP));
}
if (vo.getSalesPriceMax() != null) {
vo.setSalesPriceMax(vo.getSalesPriceMax().multiply(discountRate).setScale(2, RoundingMode.HALF_UP));
}
}
return vo;
});
}
/**
* 从事务外的子项列表中计算主表的 factory_price / sales_price / sales_price_max
* <p>
* factory_price 取子项中的最小值,sales_price 取子项中的最大值,sales_price_max 取子项中的最大值。
*/
private void computeProductPricesFromItems(CustomProductInfoSnakeDTO dto) {
if (dto.getProductList() == null || dto.getProductList().isEmpty()) {
return;
}
BigDecimal factoryPrice = null;
BigDecimal salesPrice = null;
BigDecimal salesPriceMax = null;
for (CustomProductItemSnakeDTO item : dto.getProductList()) {
if (item.getFactory_price() != null) {
// 当前值 < 子项值时替换 取最大值
if (factoryPrice == null || factoryPrice.compareTo(item.getFactory_price()) < 0) {
factoryPrice = item.getFactory_price();
}
}
if (item.getSales_price() != null) {
// 当前值 < 子项值时替换 取最大值
if (salesPrice == null || salesPrice.compareTo(item.getSales_price()) < 0) {
salesPrice = item.getSales_price();
}
// 当前值 > 子项值时替换 取最小值
if (salesPriceMax == null || salesPriceMax.compareTo(item.getSales_price()) > 0) {
salesPriceMax = item.getSales_price();
}
}
}
if (factoryPrice != null) {
dto.setFactory_price(factoryPrice);
}
if (salesPrice != null) {
dto.setSales_price(salesPrice);
}
if (salesPriceMax != null) {
dto.setSales_price_max(salesPriceMax);
}
}
/**
* 从 SaveDTO 构建 Entity
* <p>
* SaveDTO 使用驼峰命名,与 Entity 字段名一致,直接用默认 mapper 转换。
* 对于蛇形命名的 {@link CustomProductInfoSnakeDTO},调用方需先用 {@code BeanMapper.snakeCase()} 转为 SaveDTO。
*
* @param dto 保存 DTO(驼峰命名)
* @param sku 生成的 SKU(创建时传入,更新时传 null)
* @return 商品实体
*/
private CustomProductInfoEntity buildEntityFromSaveDTO(CustomProductInfoSnakeDTO dto, String sku) {
CustomProductInfoEntity entity = BeanMapper.snakeCase().convert(dto, CustomProductInfoEntity.class);
if (sku != null) {
entity.setSku(sku);
}
return entity;
}
/**
* 逐条保存子项并返回原始 SKU → 已保存实体的映射
* <p>
* 逐条保存是为了获取每条记录的自增 ID,供后续工厂价格关联的 item_id 填充使用。
* 对齐 TS {@code save} 方法 100-117 行。
*
* @return Map<原始DTO中的SKU, 已保存的实体(含自增ID和替换后的SKU)>
*/
private Map<String, CustomProductItemEntity> saveProductItemsIndividually(
List<CustomProductItemSnakeDTO> itemDTOs,
String generatedSku, Integer productId,
CustomProductInfoEntity mainProduct) {
Map<String, CustomProductItemEntity> itemMap = new HashMap<>();
if (itemDTOs == null || itemDTOs.isEmpty()) {
return itemMap;
}
for (CustomProductItemSnakeDTO dto : itemDTOs) {
String originalSku = dto.getSku(); // 保存原始 SKU 作为 map key
CustomProductItemEntity item = BeanMapper.snakeCase().convert(dto, CustomProductItemEntity.class);
item.setProductId(productId);
// 替换 SKU 占位符
if (item.getSku() != null) {
item.setSku(item.getSku().replace("SKU", generatedSku));
}
// 同步主表的 print_type 和 product_no 到子项(对齐 TS:102,105)
item.setPrintType(mainProduct.getPrintType());
item.setProductNo(mainProduct.getProductNo());
// 逐条保存以获取自增 ID
customProductItemDomainService.save(item);
itemMap.put(originalSku, item);
}
log.info("[ saveProductItems ] 逐条保存 {} 个子项, productId: {}", itemMap.size(), productId);
return itemMap;
}
/**
* 保存工厂价格关联(利用 itemMap 替换 item_sku 为生成的 SKU 并填充 item_id)
* <p>
* 对齐 TS {@code save} 方法 132-139 行。
*/
private void saveFactoryPriceRels(List<FactoryPriceRelDTO> factoryPriceList,
Integer productId,
Map<String, CustomProductItemEntity> itemMap) {
if (factoryPriceList == null || factoryPriceList.isEmpty()) {
return;
}
List<CustomProductFactoryPriceRelEntity> rels = new ArrayList<>();
for (FactoryPriceRelDTO dto : factoryPriceList) {
CustomProductFactoryPriceRelEntity rel = BeanMapper.snakeCase().convert(dto, CustomProductFactoryPriceRelEntity.class);
rel.setProductId(productId);
// 从 itemMap 中查找对应的子项,替换 item_sku 为生成的 SKU 并填充 item_id(对齐 TS:134-137)
CustomProductItemEntity item = itemMap.get(dto.getItemSku());
if (item != null) {
rel.setItemSku(item.getSku()); // 替换为替换后的 SKU
rel.setItemId(item.getId()); // 填充自增 ID
}
rels.add(rel);
}
customProductFactoryPriceRelDomainService.saveBatch(rels);
}
/**
* 保存产品-工厂关联(product_factory_rel 表)
* <p>
* 对齐 TS {@code save} 方法 162-163 行。
*/
private void saveFactoryRels(List<Integer> factoryIds, Integer productId) {
if (factoryIds == null || factoryIds.isEmpty()) {
return;
}
List<ProductFactoryRelEntity> rels = factoryIds.stream().map(factoryId -> {
ProductFactoryRelEntity rel = new ProductFactoryRelEntity();
rel.setProductId(productId);
rel.setFactoryId(factoryId);
return rel;
}).collect(Collectors.toList());
productFactoryRelDomainService.saveBatch(rels);
}
/**
* 保存 DIY 用户关联
*/
private void saveDiyUserRels(List<Integer> diyUserIds, Integer productId) {
if (diyUserIds == null || diyUserIds.isEmpty()) {
return;
}
List<CustomProductDiyUserRelEntity> rels = diyUserIds.stream().map(diyUserId -> {
CustomProductDiyUserRelEntity rel = new CustomProductDiyUserRelEntity();
rel.setProductId(productId);
rel.setDiyUserId(diyUserId);
return rel;
}).collect(Collectors.toList());
customProductDiyUserRelDomainService.saveBatch(rels);
}
/**
* 保存工艺关联
*/
private void saveCraftRels(List<Integer> craftIds, Integer productId) {
if (craftIds == null || craftIds.isEmpty()) {
return;
}
List<CustomProductCraftRelEntity> rels = craftIds.stream().map(craftId -> {
CustomProductCraftRelEntity rel = new CustomProductCraftRelEntity();
rel.setProductId(productId);
rel.setCraftId(craftId.longValue());
return rel;
}).collect(Collectors.toList());
customProductCraftRelDomainService.saveBatch(rels);
}
/**
* 保存价格区间关联
*/
private void saveFactoryPriceIntervalRels(List<FactoryPriceIntervalRelDTO> intervals, Integer productId) {
if (intervals == null || intervals.isEmpty()) {
return;
}
List<CustomProductFactoryPriceIntervalRelEntity> rels = intervals.stream().map(dto -> {
CustomProductFactoryPriceIntervalRelEntity rel = BeanMapper.mapper().convert(dto, CustomProductFactoryPriceIntervalRelEntity.class);
rel.setProductId(productId);
return rel;
}).collect(Collectors.toList());
customProductFactoryPriceIntervalRelDomainService.saveBatch(rels);
}
/**
* 构建图片实体列表
*/
private List<CustomProductImageEntity> buildImageEntities(List<CustomProductImageSnakeDTO> imageList, Integer productId, Integer type) {
return Optional.ofNullable(imageList).orElse(Collections.emptyList()).stream().map(dto -> {
CustomProductImageEntity entity = BeanMapper.snakeCase().convert(dto, CustomProductImageEntity.class);
entity.setProductId(productId);
entity.setType(type);
entity.setCreateTime(new Date());
return entity;
}).collect(Collectors.toList());
}
/**
* 保存图片(普通图 type=0 + 尺码图 type=1)
*/
private void saveImages(List<CustomProductImageSnakeDTO> imageList, List<CustomProductImageSnakeDTO> sizeList, Integer productId) {
List<CustomProductImageEntity> allImages = Stream.of(
buildImageEntities(imageList, productId, IMAGE_TYPE_NORMAL),
buildImageEntities(sizeList, productId, IMAGE_TYPE_SIZE)
).flatMap(List::stream).collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(allImages)) {
customProductImageDomainService.saveBatch(allImages);
}
}
/**
* 保存备注(英文 + 中文)
*/
private void saveRemarks(String remark, String cnRemark, Integer productId) {
if (StringUtils.isNotBlank(remark)) {
CustomProductRemarkEntity r = new CustomProductRemarkEntity();
r.setProductId(productId);
r.setRemark(remark);
customProductRemarkDomainService.save(r);
}
if (StringUtils.isNotBlank(remark)) {
CustomProductCnRemarkEntity cr = new CustomProductCnRemarkEntity();
cr.setProductId(productId);
cr.setRemark(cnRemark);
customProductCnRemarkDomainService.save(cr);
}
}
/**
* 保存属性(SKU 属性 + 普通属性)
*/
private void saveProperties(List<CustomProductInfoPropertyDTO> skuProperties,
List<CustomProductInfoPropertyDTO> normalProperties,
Integer productId) {
List<CustomProductInfoPropertyEntity> allProps = Stream.of(
buildPropertyEntities(skuProperties, productId, true),
buildPropertyEntities(normalProperties, productId, false)
)
.flatMap(List::stream)
.collect(Collectors.toList());
if (!allProps.isEmpty()) {
customProductInfoPropertyDomainService.saveBatch(allProps);
}
}
/**
* 创建属性实体
*
* @param properties 属性值DTO
* @param productId 商品ID
* @param isSkuProperty 是否为SKU属性
* @return 属性实体
*/
private List<CustomProductInfoPropertyEntity> buildPropertyEntities(List<CustomProductInfoPropertyDTO> properties,
Integer productId,
boolean isSkuProperty) {
if (CollectionUtils.isEmpty(properties)) {
return Collections.emptyList();
}
return properties.stream().filter(dto -> CollectionUtils.isNotEmpty(dto.getValueList()))
.flatMap(dto -> dto.getValueList().stream().map(
valueDTO -> createPropertyEntity(dto, valueDTO, productId, isSkuProperty)))
.collect(Collectors.toList());
}
/**
* 创建属性实体
*
* @param dto 属性DTO
* @param valueDTO 属性值DTO
* @param productId 商品ID
* @param isSkuProperty 是否为SKU属性
* @return 属性实体
*/
private CustomProductInfoPropertyEntity createPropertyEntity(CustomProductInfoPropertyDTO dto,
CustomProductPropertiesValueDTO valueDTO,
Integer productId,
boolean isSkuProperty) {
CustomProductInfoPropertyEntity prop = new CustomProductInfoPropertyEntity();
prop.setInfoId(productId);
prop.setPropertyId(dto.getId());
prop.setValueId(valueDTO.getId());
prop.setSkuProperty(isSkuProperty);
return prop;
}
/**
* 保存仓库关联
*/
private void saveWarehouseRels(List<Integer> warehouseIds, Integer productId) {
if (warehouseIds == null || warehouseIds.isEmpty()) {
return;
}
List<CustomProductWarehouseRelEntity> rels = warehouseIds.stream().map(wid -> {
CustomProductWarehouseRelEntity rel = new CustomProductWarehouseRelEntity();
rel.setProductId(productId);
rel.setWarehouseId(wid.longValue());
return rel;
}).collect(Collectors.toList());
customProductWarehouseRelDomainService.saveBatch(rels);
}
// -------- updateFull 辅助方法 --------
/**
* 处理子项变更(增/删/改)
*/
private void handleItemChanges(com.jomalls.custom.app.dto.ProductChangeDTO change, Integer productId) {
if (change == null) {
return;
}
// 删除
if (change.getRemoveList() != null && !change.getRemoveList().isEmpty()) {
customProductItemDomainService.removeByIds(change.getRemoveList());
}
// 修改
if (change.getUpdateList() != null) {
for (var itemDTO : change.getUpdateList()) {
CustomProductItemEntity item = BeanMapper.mapper().convert(itemDTO, CustomProductItemEntity.class);
item.setProductId(productId);
customProductItemDomainService.updateById(item);
}
}
// 新增
if (change.getAddList() != null) {
for (var itemDTO : change.getAddList()) {
CustomProductItemEntity item = BeanMapper.mapper().convert(itemDTO, CustomProductItemEntity.class);
item.setProductId(productId);
customProductItemDomainService.save(item);
}
}
}
/**
* 处理工厂价格变更(增/删/改)
*/
private void handleFactoryPriceChanges(
CustomProductInfoUpdateDTO.ProductFactoryPriceChangeDTO change, Integer productId) {
if (change.getRemoveList() != null && !change.getRemoveList().isEmpty()) {
customProductFactoryPriceRelDomainService.removeByIds(change.getRemoveList());
}
if (change.getUpdateList() != null) {
for (var dto : change.getUpdateList()) {
CustomProductFactoryPriceRelEntity rel = BeanMapper.mapper().convert(dto, CustomProductFactoryPriceRelEntity.class);
rel.setProductId(productId);
customProductFactoryPriceRelDomainService.updateById(rel);
}
}
if (change.getAddList() != null) {
for (var dto : change.getAddList()) {
CustomProductFactoryPriceRelEntity rel = BeanMapper.mapper().convert(dto, CustomProductFactoryPriceRelEntity.class);
rel.setProductId(productId);
customProductFactoryPriceRelDomainService.save(rel);
}
}
}
/**
* 销毁并重建属性
*/
private void rebuildProperties(List<CustomProductInfoPropertyDTO> skuProperties,
List<CustomProductInfoPropertyDTO> normalProperties,
Integer productId) {
// 删除旧属性
customProductInfoPropertyDomainService.remove(
new LambdaQueryWrapper<CustomProductInfoPropertyEntity>()
.eq(CustomProductInfoPropertyEntity::getInfoId, productId));
// 重建新属性
saveProperties(skuProperties, normalProperties, productId);
}
/**
* 处理图片变更
*/
private void handleImageChanges(
CustomProductInfoUpdateDTO.ProductImageChangeDTO change, Integer productId, Integer type) {
if (change.getRemoveList() != null && !change.getRemoveList().isEmpty()) {
customProductImageDomainService.removeByIds(change.getRemoveList());
}
if (change.getAddList() != null) {
for (var dto : change.getAddList()) {
CustomProductImageEntity img = BeanMapper.mapper().convert(dto, CustomProductImageEntity.class);
img.setProductId(productId);
img.setType(type);
customProductImageDomainService.save(img);
}
}
}
/**
* 更新备注(先删后插)
*/
private void updateRemarks(String remark, String cnRemark, Integer productId) {
// 英文备注
customProductRemarkDomainService.remove(
new LambdaQueryWrapper<CustomProductRemarkEntity>()
.eq(CustomProductRemarkEntity::getProductId, productId));
if (remark != null && !remark.isEmpty()) {
CustomProductRemarkEntity r = new CustomProductRemarkEntity();
r.setProductId(productId);
r.setRemark(remark);
customProductRemarkDomainService.save(r);
}
// 中文备注
customProductCnRemarkDomainService.remove(
new LambdaQueryWrapper<CustomProductCnRemarkEntity>()
.eq(CustomProductCnRemarkEntity::getProductId, productId));
if (cnRemark != null && !cnRemark.isEmpty()) {
CustomProductCnRemarkEntity cr = new CustomProductCnRemarkEntity();
cr.setProductId(productId);
cr.setRemark(cnRemark);
customProductCnRemarkDomainService.save(cr);
}
}
/**
* 重建关联关系(先删后插)
*/
private void rebuildRels(List<Integer> factoryIds, List<Integer> warehouseIds,
List<Integer> craftIds, List<Integer> diyUserIds,
List<FactoryPriceIntervalRelDTO> intervals, Integer productId) {
// 产品-工厂关联
productFactoryRelDomainService.remove(
new LambdaQueryWrapper<ProductFactoryRelEntity>()
.eq(ProductFactoryRelEntity::getProductId, productId));
saveFactoryRels(factoryIds, productId);
// 仓库关联
customProductWarehouseRelDomainService.remove(
new LambdaQueryWrapper<CustomProductWarehouseRelEntity>()
.eq(CustomProductWarehouseRelEntity::getProductId, productId));
saveWarehouseRels(warehouseIds, productId);
// 工艺关联
customProductCraftRelDomainService.remove(
new LambdaQueryWrapper<CustomProductCraftRelEntity>()
.eq(CustomProductCraftRelEntity::getProductId, productId));
saveCraftRels(craftIds, productId);
// DIY 用户关联
customProductDiyUserRelDomainService.remove(
new LambdaQueryWrapper<CustomProductDiyUserRelEntity>()
.eq(CustomProductDiyUserRelEntity::getProductId, productId));
saveDiyUserRels(diyUserIds, productId);
// 价格区间关联
customProductFactoryPriceIntervalRelDomainService.remove(
new LambdaQueryWrapper<CustomProductFactoryPriceIntervalRelEntity>()
.eq(CustomProductFactoryPriceIntervalRelEntity::getProductId, productId));
saveFactoryPriceIntervalRels(intervals, productId);
}
// -------- 日志辅助方法 --------
/**
* 事务内记录日志
*/
private void saveLogTransaction(Integer productId, String description) {
LogCustomProductEntity logEntry = new LogCustomProductEntity();
logEntry.setProductId(productId);
logEntry.setDescription(description);
logEntry.setEmployeeId(-1);
logEntry.setEmployeeAccount("系统");
logCustomProductDomainService.save(logEntry);
}
/**
* 批量记录产品日志
*/
private void saveLogBatch(String action, List<Integer> productIds) {
List<LogCustomProductEntity> logs = new ArrayList<>();
for (Integer productId : productIds) {
LogCustomProductEntity logEntry = new LogCustomProductEntity();
logEntry.setProductId(productId);
logEntry.setDescription(action);
logEntry.setEmployeeId(-1);
logEntry.setEmployeeAccount("系统");
logs.add(logEntry);
}
logCustomProductDomainService.saveBatch(logs);
}
/**
* 取消所有 CompletableFuture(超时或异常时调用)
*/
private void cancelAll(CompletableFuture<?>... futures) {
for (CompletableFuture<?> f : futures) {
if (f != null && !f.isDone()) {
f.cancel(true);
}
}
}
// -------- getByIdOrSku 辅助方法 --------
/**
* 将并行查询结果组装为 FullVO
*/
private CustomProductInfoSnakeVO buildProductFullVO(CustomProductInfoEntity entity,
List<CustomProductItemEntity> items,
List<CustomProductImageEntity> images,
List<CustomProductFactoryPriceRelEntity> factoryPrices,
List<Integer> diyUserIds,
List<Long> craftIds,
List<Integer> warehouseIds,
CustomProductRemarkEntity remark,
CustomProductCnRemarkEntity cnRemark,
List<Integer> factoryIds) {
CustomProductInfoSnakeVO fullVO = BeanMapper.snakeCase().convert(entity, CustomProductInfoSnakeVO.class);
// 子项(对齐 TS:209-214 — 同步主表的 print_type 到每个子项)
fullVO.setProductList(items.stream()
.map(e -> {
CustomProductItemSnakeVO vo = BeanMapper.snakeCase().convert(e, CustomProductItemSnakeVO.class);
// 同步主表 printType 到子项(对齐 TS:212)
vo.setPrint_type(entity.getPrintType());
return vo;
})
.collect(Collectors.toList()));
// 图片拆分:按类型分为普通图和尺码图(对齐 TS:205-206)
fullVO.setColorImageList(Arrays.asList(entity.getColorImages().split(",")));
fullVO.setImageList(images.stream()
.filter(img -> img.getType() == null || img.getType() == IMAGE_TYPE_NORMAL)
.map(img -> BeanMapper.snakeCase().convert(img, CustomProductImageSnakeVO.class))
.collect(Collectors.toList()));
fullVO.setSizeList(images.stream()
.filter(img -> img.getType() != null && img.getType() == IMAGE_TYPE_SIZE)
.map(img -> BeanMapper.snakeCase().convert(img, CustomProductImageSnakeVO.class))
.collect(Collectors.toList()));
// 工厂价格
fullVO.setFactoryPriceList(factoryPrices.stream()
.map(e -> BeanMapper.mapper().convert(e, CustomProductFactoryPriceRelVO.class))
.collect(Collectors.toList()));
// 关联 ID 列表
fullVO.setDiyUserIds(diyUserIds);
fullVO.setCraftIds(craftIds.stream().map(String::valueOf).collect(Collectors.toList()));
fullVO.setWarehouseIds(warehouseIds);
// 工厂关联 ID 列表(对齐 TS:238)
fullVO.setFactoryIds(factoryIds);
// 备注
fullVO.setProductCnRemark(BeanMapper.snakeCase().convert(cnRemark, ProductRemarkVO.class));
fullVO.setProductRemark(BeanMapper.snakeCase().convert(remark, ProductRemarkVO.class));
fullVO.setRemark(remark.getRemark());
fullVO.setCnRemark(cnRemark.getRemark());
return fullVO;
}
} }
...@@ -4,7 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; ...@@ -4,7 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jomalls.custom.app.exception.ServiceException; import com.jomalls.custom.app.exception.ServiceException;
import com.jomalls.custom.app.vo.CustomProductItemPageVO; import com.jomalls.custom.app.vo.CustomProductItemPageVO;
import com.jomalls.custom.app.vo.CustomProductItemVO; import com.jomalls.custom.app.vo.CustomProductItemSnakeVO;
import com.jomalls.custom.app.service.CustomProductItemService; import com.jomalls.custom.app.service.CustomProductItemService;
import com.jomalls.custom.app.utils.BeanMapper; import com.jomalls.custom.app.utils.BeanMapper;
import com.jomalls.custom.app.utils.CustomAsserts; import com.jomalls.custom.app.utils.CustomAsserts;
...@@ -37,34 +37,34 @@ public class CustomProductItemServiceImpl implements CustomProductItemService { ...@@ -37,34 +37,34 @@ public class CustomProductItemServiceImpl implements CustomProductItemService {
} }
@Override @Override
public List<CustomProductItemVO> list(CustomProductItemVO customProductItemVO) { public List<CustomProductItemSnakeVO> list(CustomProductItemSnakeVO customProductItemSnakeVO) {
QueryWrapper<CustomProductItemEntity> queryWrapper = new QueryWrapper<>(); QueryWrapper<CustomProductItemEntity> queryWrapper = new QueryWrapper<>();
// TODO 根据业务条件组装入参 // TODO 根据业务条件组装入参
List<CustomProductItemEntity> list = customProductItemDomainService.list(queryWrapper); List<CustomProductItemEntity> list = customProductItemDomainService.list(queryWrapper);
return list.stream().map(e -> BeanMapper.mapper().convert(e, CustomProductItemVO.class)).collect(Collectors.toList()); return list.stream().map(e -> BeanMapper.mapper().convert(e, CustomProductItemSnakeVO.class)).collect(Collectors.toList());
} }
@Override @Override
public IPage<CustomProductItemVO> pageList(CustomProductItemPageVO customProductItemPageVO) { public IPage<CustomProductItemSnakeVO> pageList(CustomProductItemPageVO customProductItemPageVO) {
CustomAsserts.nonNull(customProductItemPageVO, "分页查询参数不能为空"); CustomAsserts.nonNull(customProductItemPageVO, "分页查询参数不能为空");
QueryWrapper<CustomProductItemEntity> queryWrapper = new QueryWrapper<>(); QueryWrapper<CustomProductItemEntity> queryWrapper = new QueryWrapper<>();
// TODO 根据业务条件组装入参 // TODO 根据业务条件组装入参
IPage<CustomProductItemEntity> page = customProductItemDomainService.selectPage(queryWrapper, customProductItemPageVO); IPage<CustomProductItemEntity> page = customProductItemDomainService.selectPage(queryWrapper, customProductItemPageVO);
return page.convert(e -> BeanMapper.mapper().convert(e, CustomProductItemVO.class)); return page.convert(e -> BeanMapper.mapper().convert(e, CustomProductItemSnakeVO.class));
} }
@Override @Override
public CustomProductItemVO info(Integer id) { public CustomProductItemSnakeVO info(Integer id) {
CustomAsserts.nonNull(id, "主键id不能为空"); CustomAsserts.nonNull(id, "主键id不能为空");
CustomProductItemEntity customProductItem = customProductItemDomainService.getById(id); CustomProductItemEntity customProductItem = customProductItemDomainService.getById(id);
return BeanMapper.mapper().convert(customProductItem, CustomProductItemVO.class); return BeanMapper.mapper().convert(customProductItem, CustomProductItemSnakeVO.class);
} }
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@Override @Override
public void save(CustomProductItemVO customProductItemVO) { public void save(CustomProductItemSnakeVO customProductItemSnakeVO) {
CustomAsserts.nonNull(customProductItemVO, "实体对象不能为空"); CustomAsserts.nonNull(customProductItemSnakeVO, "实体对象不能为空");
CustomProductItemEntity customProductItemEntity = BeanMapper.mapper().convert(customProductItemVO, CustomProductItemEntity.class); CustomProductItemEntity customProductItemEntity = BeanMapper.mapper().convert(customProductItemSnakeVO, CustomProductItemEntity.class);
try { try {
customProductItemDomainService.save(customProductItemEntity); customProductItemDomainService.save(customProductItemEntity);
} catch (DuplicateKeyException e) { } catch (DuplicateKeyException e) {
...@@ -75,9 +75,9 @@ public class CustomProductItemServiceImpl implements CustomProductItemService { ...@@ -75,9 +75,9 @@ public class CustomProductItemServiceImpl implements CustomProductItemService {
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@Override @Override
public void updateById(CustomProductItemVO customProductItemVO) { public void updateById(CustomProductItemSnakeVO customProductItemSnakeVO) {
CustomAsserts.nonNull(customProductItemVO, "实体对象不能为空"); CustomAsserts.nonNull(customProductItemSnakeVO, "实体对象不能为空");
CustomProductItemEntity customProductItem = BeanMapper.mapper().convert(customProductItemVO, CustomProductItemEntity.class); CustomProductItemEntity customProductItem = BeanMapper.mapper().convert(customProductItemSnakeVO, CustomProductItemEntity.class);
try { try {
customProductItemDomainService.updateById(customProductItem); customProductItemDomainService.updateById(customProductItem);
} catch (DuplicateKeyException e) { } catch (DuplicateKeyException e) {
......
package com.jomalls.custom.app.service.impl;
import com.jomalls.custom.app.service.DiyUserService;
import com.jomalls.custom.app.vo.*;
import com.jomalls.custom.dal.entity.DbDiyUserEntity;
import com.jomalls.custom.domain.service.DbDiyUserDomainService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* DIY 用户服务实现
* <p>
* 对齐 TS 项目 {@code diyUserService}。
*
* @author Lizh
* @date 2026-06-08
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class DiyUserServiceImpl implements DiyUserService {
private final DbDiyUserDomainService dbDiyUserDomainService;
/** 折扣基数(100 表示无折扣) */
private static final BigDecimal DISCOUNT_BASE = new BigDecimal("100");
@Override
public DbDiyUserEntity getByNamespace(String namespace) {
return dbDiyUserDomainService.getByNamespace(namespace);
}
@Override
public DbDiyUserEntity getByUserMark(String userMark) {
return dbDiyUserDomainService.getByUserMark(userMark);
}
@Override
public void setProductExternalPrice(DbDiyUserEntity user, CustomProductInfoSnakeVO vo) {
if (user == null || user.getDiscount() == null) {
return;
}
// discount 如 85 → 折扣率 0.85(对齐 TS:317)
BigDecimal discountRate = user.getDiscount().divide(DISCOUNT_BASE, 4, RoundingMode.HALF_UP);
if (discountRate.compareTo(BigDecimal.ONE) == 0) {
return;
}
log.debug("[ setProductExternalPrice ] 用户={}, 折扣率={}", user.getName(), discountRate);
// 主表 sales_price / sales_price_max(对齐 TS:322-327)
if (vo.getSales_price() != null) {
vo.setSales_price(vo.getSales_price().multiply(discountRate).setScale(2, RoundingMode.HALF_UP));
}
if (vo.getSales_price_max() != null) {
vo.setSales_price_max(vo.getSales_price_max().multiply(discountRate).setScale(2, RoundingMode.HALF_UP));
}
// 子项的 sales_price(对齐 TS:328-332)
if (vo.getProductList() != null) {
for (CustomProductItemSnakeVO item : vo.getProductList()) {
if (item.getSales_price() != null) {
item.setSales_price(item.getSales_price().multiply(discountRate).setScale(2, RoundingMode.HALF_UP));
}
}
}
// 工厂价格关联的 sales_price(对齐 TS:348-352)
if (vo.getFactoryPriceList() != null) {
for (CustomProductFactoryPriceRelVO fp : vo.getFactoryPriceList()) {
if (fp.getSalesPrice() != null) {
fp.setSalesPrice(fp.getSalesPrice().multiply(discountRate).setScale(2, RoundingMode.HALF_UP));
}
}
}
}
}
package com.jomalls.custom.app.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jomalls.custom.app.exception.ServiceException;
import com.jomalls.custom.app.service.ProductFactoryRelService;
import com.jomalls.custom.app.utils.BeanMapper;
import com.jomalls.custom.app.utils.CustomAsserts;
import com.jomalls.custom.app.vo.ProductFactoryRelPageVO;
import com.jomalls.custom.app.vo.ProductFactoryRelVO;
import com.jomalls.custom.dal.entity.ProductFactoryRelEntity;
import com.jomalls.custom.domain.service.ProductFactoryRelDomainService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
/**
* 产品-工厂关联 App Service 实现
*
* @author Lizh
* @date 2026-06-06
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class ProductFactoryRelServiceImpl implements ProductFactoryRelService {
private final ProductFactoryRelDomainService productFactoryRelDomainService;
@Override
public List<ProductFactoryRelVO> list(ProductFactoryRelVO vo) {
QueryWrapper<ProductFactoryRelEntity> queryWrapper = new QueryWrapper<>();
List<ProductFactoryRelEntity> list = productFactoryRelDomainService.list(queryWrapper);
return list.stream().map(e -> BeanMapper.mapper().convert(e, ProductFactoryRelVO.class)).collect(Collectors.toList());
}
@Override
public IPage<ProductFactoryRelVO> pageList(ProductFactoryRelPageVO param) {
CustomAsserts.nonNull(param, "分页查询参数不能为空");
QueryWrapper<ProductFactoryRelEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("id");
IPage<ProductFactoryRelEntity> page = productFactoryRelDomainService.selectPage(queryWrapper, param);
return page.convert(e -> BeanMapper.mapper().convert(e, ProductFactoryRelVO.class));
}
@Override
public ProductFactoryRelVO info(Integer id) {
CustomAsserts.nonNull(id, "主键id不能为空");
ProductFactoryRelEntity entity = productFactoryRelDomainService.getById(id);
return BeanMapper.mapper().convert(entity, ProductFactoryRelVO.class);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void save(ProductFactoryRelVO vo) {
CustomAsserts.nonNull(vo, "实体对象不能为空");
ProductFactoryRelEntity entity = BeanMapper.mapper().convert(vo, ProductFactoryRelEntity.class);
try {
productFactoryRelDomainService.save(entity);
} catch (DuplicateKeyException e) {
log.info("[ save ] 实体对象唯一约束重复", e);
throw new ServiceException("实体对象唯一约束重复,请调整后再试!");
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public void updateById(ProductFactoryRelVO vo) {
CustomAsserts.nonNull(vo, "实体对象不能为空");
ProductFactoryRelEntity entity = BeanMapper.mapper().convert(vo, ProductFactoryRelEntity.class);
try {
productFactoryRelDomainService.updateById(entity);
} catch (DuplicateKeyException e) {
log.info("[ updateById ] 实体对象唯一约束重复", e);
throw new ServiceException("实体对象唯一约束重复,请调整后再试!");
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public void deleteById(Integer id) {
CustomAsserts.nonNull(id, "主键id不能为空");
productFactoryRelDomainService.removeById(id);
}
}
...@@ -2,6 +2,7 @@ package com.jomalls.custom.app.utils; ...@@ -2,6 +2,7 @@ package com.jomalls.custom.app.utils;
import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
...@@ -25,10 +26,18 @@ import java.util.List; ...@@ -25,10 +26,18 @@ import java.util.List;
public class BeanMapper { public class BeanMapper {
/** /**
* Jackson ObjectMapper配置 * Jackson ObjectMapper配置(默认驼峰命名)
*/ */
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
/**
* Jackson ObjectMapper配置(蛇形命名 ↔ 驼峰命名双向转换)
* <p>
* 用于 DTO(snake_case 字段名)与 Entity/VO(camelCase 字段名)之间的互转。
* 由于命名策略在序列化和反序列化两端一致生效,驼峰↔驼峰、蛇形↔驼峰的转换都能正确处理。
*/
private static final ObjectMapper SNAKE_CASE_MAPPER = new ObjectMapper();
static { static {
// 注册Java 8时间模块,支持LocalDate, LocalDateTime等 // 注册Java 8时间模块,支持LocalDate, LocalDateTime等
OBJECT_MAPPER.registerModule(new JavaTimeModule()); OBJECT_MAPPER.registerModule(new JavaTimeModule());
...@@ -40,16 +49,44 @@ public class BeanMapper { ...@@ -40,16 +49,44 @@ public class BeanMapper {
OBJECT_MAPPER.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); OBJECT_MAPPER.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
// 允许单值包装数组 // 允许单值包装数组
OBJECT_MAPPER.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY); OBJECT_MAPPER.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
// 蛇形命名 mapper:复制默认配置,叠加 SNAKE_CASE 命名策略
SNAKE_CASE_MAPPER.registerModule(new JavaTimeModule());
SNAKE_CASE_MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
SNAKE_CASE_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
SNAKE_CASE_MAPPER.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
SNAKE_CASE_MAPPER.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
// 核心:设置蛇形命名策略,使得 category_id ↔ categoryId 可以互相转换
SNAKE_CASE_MAPPER.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
} }
private BeanMapper() { private BeanMapper() {
this.objectMapper = OBJECT_MAPPER;
}
private BeanMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
} }
private final ObjectMapper objectMapper;
public static BeanMapper mapper() { public static BeanMapper mapper() {
return new BeanMapper(); return new BeanMapper();
} }
/** /**
* 获取使用蛇形命名策略的 BeanMapper 实例
* <p>
* 适用于 snake_case 字段名的 DTO 与 camelCase 字段名的 Entity/VO 之间的双向转换。
* 例如:CustomProductInfoDTO(category_id)→ CustomProductInfoEntity(categoryId)。
*
* @return 配置了 SNAKE_CASE 命名策略的 BeanMapper
*/
public static BeanMapper snakeCase() {
return new BeanMapper(SNAKE_CASE_MAPPER);
}
/**
* 将源对象转换为目标类型 * 将源对象转换为目标类型
* 支持嵌套对象、集合等复杂结构 * 支持嵌套对象、集合等复杂结构
* *
...@@ -64,7 +101,7 @@ public class BeanMapper { ...@@ -64,7 +101,7 @@ public class BeanMapper {
return null; return null;
} }
try { try {
return OBJECT_MAPPER.convertValue(source, destinationClass); return this.objectMapper.convertValue(source, destinationClass);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("BeanMapper convert failed: " + throw new RuntimeException("BeanMapper convert failed: " +
"source=" + source.getClass().getName() + "source=" + source.getClass().getName() +
...@@ -88,7 +125,7 @@ public class BeanMapper { ...@@ -88,7 +125,7 @@ public class BeanMapper {
try { try {
List<D> result = new ArrayList<>(sourceList.size()); List<D> result = new ArrayList<>(sourceList.size());
for (S source : sourceList) { for (S source : sourceList) {
result.add(OBJECT_MAPPER.convertValue(source, destinationClass)); result.add(this.objectMapper.convertValue(source, destinationClass));
} }
return result; return result;
} catch (Exception e) { } catch (Exception e) {
...@@ -110,7 +147,7 @@ public class BeanMapper { ...@@ -110,7 +147,7 @@ public class BeanMapper {
return null; return null;
} }
try { try {
return (T) OBJECT_MAPPER.convertValue(source, source.getClass()); return (T) this.objectMapper.convertValue(source, source.getClass());
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("BeanMapper copy failed: " + throw new RuntimeException("BeanMapper copy failed: " +
"source=" + source.getClass().getName(), e); "source=" + source.getClass().getName(), e);
......
...@@ -21,7 +21,7 @@ import java.util.Date; ...@@ -21,7 +21,7 @@ import java.util.Date;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@Schema(description = "VO") @Schema(description = "VO")
public class CustomProductImageVO implements Serializable { public class CustomProductImageSnakeVO implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
...@@ -35,13 +35,13 @@ public class CustomProductImageVO implements Serializable { ...@@ -35,13 +35,13 @@ public class CustomProductImageVO implements Serializable {
* 商品ID * 商品ID
*/ */
@Schema(description = "商品ID") @Schema(description = "商品ID")
private Integer productId; private Integer product_id;
/** /**
* 图片地址 * 图片地址
*/ */
@Schema(description = "图片地址") @Schema(description = "图片地址")
private String imageUrl; private String image_url;
/** /**
* 排序 * 排序
...@@ -59,7 +59,7 @@ public class CustomProductImageVO implements Serializable { ...@@ -59,7 +59,7 @@ public class CustomProductImageVO implements Serializable {
* *
*/ */
@Schema(description = "") @Schema(description = "")
private Date createTime; private Date create_time;
} }
...@@ -20,7 +20,7 @@ import java.io.Serializable; ...@@ -20,7 +20,7 @@ import java.io.Serializable;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@Schema(description = "VO") @Schema(description = "VO")
public class CustomProductInfoPropertyVO implements Serializable { public class CustomProductInfoPropertySnakeVO implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
...@@ -34,19 +34,19 @@ public class CustomProductInfoPropertyVO implements Serializable { ...@@ -34,19 +34,19 @@ public class CustomProductInfoPropertyVO implements Serializable {
* custom_product_info表id * custom_product_info表id
*/ */
@Schema(description = "custom_product_info表id") @Schema(description = "custom_product_info表id")
private Integer infoId; private Integer info_id;
/** /**
* 属性类ID * 属性类ID
*/ */
@Schema(description = "属性类ID") @Schema(description = "属性类ID")
private Integer propertyId; private Integer property_id;
/** /**
* 属性值ID * 属性值ID
*/ */
@Schema(description = "属性值ID") @Schema(description = "属性值ID")
private Integer valueId; private Integer value_id;
/** /**
* 是否为SKU属性 * 是否为SKU属性
...@@ -54,5 +54,4 @@ public class CustomProductInfoPropertyVO implements Serializable { ...@@ -54,5 +54,4 @@ public class CustomProductInfoPropertyVO implements Serializable {
@Schema(description = "是否为SKU属性") @Schema(description = "是否为SKU属性")
private Boolean skuProperty; private Boolean skuProperty;
} }
package com.jomalls.custom.app.vo;
import com.jomalls.custom.app.dto.CustomProductPropertiesValueDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* @Author: Lizh
* @Date: 2026/6/9 11:06
* @Description:
* @Version: 1.0
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "CustomProductInfoSkuPropertiesVO")
public class CustomProductInfoSkuPropertiesVO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* ID
*/
@Schema(description = "")
private Integer id;
/**
* 中文名称
*/
@Schema(description = "中文名称")
private String cnname;
/**
* 英文名称
*/
@Schema(description = "英文名称")
private String enname;
/**
* 排序
*/
@Schema(description = "排序")
private Integer sort;
/**
* 是否为SKU属性
*/
@Schema(description = "是否为SKU属性")
private Boolean skuProperty;
/**
* SKU属性
*/
@Schema(description = "SKU属性")
private Boolean multi;
/**
* SKU属性
*/
@Schema(description = "SKU属性")
private Boolean enable;
/**
* SKU属性
*/
@Schema(description = "SKU属性")
private String categoryInfoId;
/**
* SKU属性值列表
*/
@Schema(description = "SKU属性值列表")
private List<CustomProductPropertiesValueVO> valueList;
/**
* publicData
*/
@Schema(description = "publicData")
private Boolean publicData;
/**
* publicData
*/
@Schema(description = "propertyValueIds")
private List<String> propertyValueIds;
}
package com.jomalls.custom.app.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
* 商品完整详情 VO
* <p>
* 对齐 TS 项目 {@code entity/custom_product_info.ts} 的返回结构。
* 包含主表字段 + 所有子实体列表,由 App Service 通过并行单表查询后在 Java 层组合。
*
* @author Lizh
* @date 2026-06-06
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "商品完整详情")
public class CustomProductInfoSnakeVO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "主键 ID")
private Integer id;
@Schema(description = "SKU")
private String sku;
@Schema(description = "货号")
private String product_no;
@Schema(description = "title")
private String title;
@Schema(description = "商品名称")
private String name;
@Schema(description = "图片集")
private List<String> colorImageList;
@Schema(description = "图片集")
private String color_images;
@Schema(description = "商品主图")
private String img_url;
@Schema(description = "商品类别ID")
private Integer category_id;
@Schema(description = "重量(kg)")
//private BigDecimal weight;
private String weight;
@Schema(description = "最小采购量")
private Integer purchasing_min;
@Schema(description = "工厂价(¥)")
private BigDecimal factory_price;
@Schema(description = "销售价(¥)")
private BigDecimal sales_price;
@Schema(description = "销售价最高价(¥)")
private BigDecimal sales_price_max;
@Schema(description = "状态:1待上架 10已上架 20已下架 30待下架 40已作废")
private Integer status;
@Schema(description = "商品属性分类ID 1")
private Integer property1_cate_id;
@Schema(description = "商品属性分类ID 2")
private Integer property2_cate_id;
@Schema(description = "商品属性分类ID 3")
private Integer property3_cate_id;
@Schema(description = "商品属性英文名称 1")
private String property1_enname;
@Schema(description = "商品属性英文名称 2")
private String property2_enname;
@Schema(description = "商品属性英文名称 3")
private String property3_enname;
@Schema(description = "材质")
private String material;
@Schema(description = "印花类型:0满印 1局部印")
private Integer print_type;
@Schema(description = "产地编码")
private String origin_code;
@Schema(description = "产地中文名")
private String origin_name_cn;
@Schema(description = "产地英文名")
private String origin_name_en;
@Schema(description = "币种编码")
private String currency_code;
@Schema(description = "币种名称")
private String currency_name;
@Schema(description = "产品类型:platform/customer")
private String product_type;
@Schema(description = "工厂ID")
private Integer factory_id;
@Schema(description = "工厂编码")
private String factory_code;
@Schema(description = "是否九猫处理")
private Boolean processing;
@Schema(description = "创建时间")
private Date create_time;
@Schema(description = "更新时间")
private Date update_time;
@Schema(description = "排序")
private Integer sort;
@Schema(description = "默认模ID")
private Integer diy_id;
@Schema(description = "默认模SKU")
private String diy_sku;
@Schema(description = "普通图片列表(type=0)")
private List<CustomProductImageSnakeVO> imageList;
@Schema(description = "商品描述")
private ProductRemarkVO productRemark;
@Schema(description = "商品中文描述")
private ProductRemarkVO productCnRemark;
@Schema(description = "工厂价格关联列表")
private List<CustomProductFactoryPriceRelVO> factoryPriceList;
@Schema(description = "尺码图片列表(type=1)")
private List<CustomProductImageSnakeVO> sizeList;
@Schema(description = "DIY 用户 ID 列表")
private List<Integer> diyUserIds;
@Schema(description = "工艺 ID 列表")
private List<String> craftIds;
@Schema(description = "SKU 子项列表")
private List<CustomProductItemSnakeVO> productList;
@Schema(description = "普通属性列表")
private List<CustomProductInfoPropertySnakeVO> properties;
@Schema(description = "SKU 属性列表")
private List<CustomProductInfoSkuPropertiesVO> skuProperties;
@Schema(description = "普通属性列表")
private List<CustomProductInfoSkuPropertiesVO> normalProperties;
@Schema(description = "工厂 ID 列表")
private List<Integer> factoryIds;
@Schema(description = "仓库 ID 列表")
private List<Integer> warehouseIds;
@Schema(description = "英文备注内容")
private String remark;
@Schema(description = "中文备注内容")
private String cnRemark;
}
...@@ -22,7 +22,7 @@ import java.util.Date; ...@@ -22,7 +22,7 @@ import java.util.Date;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@Schema(description = "VO") @Schema(description = "VO")
public class CustomProductItemVO implements Serializable { public class CustomProductItemSnakeVO implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
...@@ -36,7 +36,7 @@ public class CustomProductItemVO implements Serializable { ...@@ -36,7 +36,7 @@ public class CustomProductItemVO implements Serializable {
* 商品ID * 商品ID
*/ */
@Schema(description = "商品ID") @Schema(description = "商品ID")
private Integer productId; private Integer product_id;
/** /**
* sku * sku
...@@ -48,7 +48,13 @@ public class CustomProductItemVO implements Serializable { ...@@ -48,7 +48,13 @@ public class CustomProductItemVO implements Serializable {
* sku商品名称 * sku商品名称
*/ */
@Schema(description = "sku商品名称") @Schema(description = "sku商品名称")
private String skuName; private String sku_name;
/**
* 货号
*/
@Schema(description = "货号")
private String product_no;
/** /**
* 封面图 * 封面图
...@@ -60,151 +66,138 @@ public class CustomProductItemVO implements Serializable { ...@@ -60,151 +66,138 @@ public class CustomProductItemVO implements Serializable {
* 图片集 * 图片集
*/ */
@Schema(description = "图片集") @Schema(description = "图片集")
private String imageAry; private String image_ary;
/** /**
* 属性分类ID1 * 属性分类ID1
*/ */
@Schema(description = "属性分类ID1") @Schema(description = "属性分类ID1")
private Integer propertyCateId1; private Integer property_cate_id1;
/** /**
* 属性分类ID2 * 属性分类ID2
*/ */
@Schema(description = "属性分类ID2") @Schema(description = "属性分类ID2")
private Integer propertyCateId2; private Integer property_cate_id2;
/** /**
* 属性分类ID3 * 属性分类ID3
*/ */
@Schema(description = "属性分类ID3") @Schema(description = "属性分类ID3")
private Integer propertyCateId3; private Integer property_cate_id3;
/** /**
* *
*/ */
@Schema(description = "") @Schema(description = "")
private Integer property1Id; private Integer property1_id;
/** /**
* *
*/ */
@Schema(description = "") @Schema(description = "")
private Integer property2Id; private Integer property2_id;
/** /**
* *
*/ */
@Schema(description = "") @Schema(description = "")
private Integer property3Id; private Integer property3_id;
/** /**
* 属性编码1 * 属性编码1
*/ */
@Schema(description = "属性编码1") @Schema(description = "属性编码1")
private String propertyCode1; private String property_code1;
/** /**
* 属性编码2 * 属性编码2
*/ */
@Schema(description = "属性编码2") @Schema(description = "属性编码2")
private String propertyCode2; private String property_code2;
/** /**
* *
*/ */
@Schema(description = "") @Schema(description = "")
private String propertyCode3; private String property_code3;
/** /**
* 属性名称1 * 属性名称1
*/ */
@Schema(description = "属性名称1") @Schema(description = "属性名称1")
private String optionEnname1; private String option_enname1;
/** /**
* 属性名称2 * 属性名称2
*/ */
@Schema(description = "属性名称2") @Schema(description = "属性名称2")
private String optionEnname2; private String option_enname2;
/** /**
* *
*/ */
@Schema(description = "") @Schema(description = "")
private String optionEnname3; private String option_enname3;
/** /**
* *
*/ */
@Schema(description = "") @Schema(description = "")
private String customValue1; private String custom_value1;
/** /**
* *
*/ */
@Schema(description = "") @Schema(description = "")
private String customValue2; private String custom_value2;
/** /**
* *
*/ */
@Schema(description = "") @Schema(description = "")
private String customValue3; private String custom_value3;
/** /**
* 工厂价 * 工厂价
*/ */
@Schema(description = "工厂价") @Schema(description = "工厂价")
private BigDecimal factoryPrice; private BigDecimal factory_price;
/** /**
* 销售价 * 销售价
*/ */
@Schema(description = "销售价") @Schema(description = "销售价")
private BigDecimal salesPrice; private BigDecimal sales_price;
/** /**
* sku克重 * sku克重
*/ */
@Schema(description = "sku克重") @Schema(description = "sku克重")
private BigDecimal skuWeight; private BigDecimal sku_weight;
/** /**
* 印花类型 0满印 1局部印 * 印花类型 0满印 1局部印
*/ */
@Schema(description = "印花类型 0满印 1局部印") @Schema(description = "印花类型 0满印 1局部印")
private Integer printType; private Integer print_type;
/**
* 排序
*/
@Schema(description = "排序")
private Integer sort;
/**
* 货号
*/
@Schema(description = "货号")
private String productNo;
/** /**
* 1正常码 2大码 * 1正常码 2大码
*/ */
@Schema(description = "1正常码 2大码") @Schema(description = "1正常码 2大码")
private Integer sizeType; private Integer size_type;
/** /**
* 创建时间 * 排序
*/ */
@Schema(description = "创建时间") @Schema(description = "排序")
private Date createTime; private Integer sort;
/** /**
* 更新时间 * 创建时间
*/ */
@Schema(description = "更新时间") @Schema(description = "创建时间")
private Date updateTime; private Date create_time;
} }
package com.jomalls.custom.app.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
/**
* @Author: Lizh
* @Date: 2026/6/9 11:10
* @Description:
* @Version: 1.0
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "CustomProductPropertiesValueVO")
public class CustomProductPropertiesValueVO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
*
*/
@Schema(description = "")
private Integer id;
/**
* 类别编码
*/
@Schema(description = "类别编码")
private String cateCode;
/**
* 类别名称
*/
@Schema(description = "类别名称")
private String cateName;
/**
* 编码
*/
@Schema(description = "编码")
private String code;
/**
* 中文名称
*/
@Schema(description = "中文名称")
private String cnname;
/**
* 英文名称
*/
@Schema(description = "英文名称")
private String enname;
/**
* 前景色
*/
@Schema(description = "前景色")
private String fontColor;
/**
* 背景色
*/
@Schema(description = "背景色")
private String bgColor;
/**
*
*/
@Schema(description = "")
private Boolean battery;
/**
*
*/
@Schema(description = "")
private Boolean liquid;
/**
*
*/
@Schema(description = "")
private Boolean knife;
/**
*
*/
@Schema(description = "")
private Integer selected;
/**
* 排序
*/
@Schema(description = "")
private Integer sort;
/**
*
*/
@Schema(description = "")
private Boolean enable;
/**
*
*/
@Schema(description = "")
private Boolean publicData;
}
package com.jomalls.custom.app.vo;
import com.jomalls.custom.page.PageRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 产品-工厂关联 分页 VO
*
* @author Lizh
* @date 2026-06-06
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Schema(description = "产品-工厂关联分页VO")
public class ProductFactoryRelPageVO extends PageRequest {
@Serial
private static final long serialVersionUID = 1L;
}
package com.jomalls.custom.app.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
/**
* 产品-工厂关联 VO
*
* @author Lizh
* @date 2026-06-06
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "产品-工厂关联VO")
public class ProductFactoryRelVO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "主键")
private Integer id;
@Schema(description = "产品 ID")
private Integer productId;
@Schema(description = "工厂 ID")
private Integer factoryId;
}
package com.jomalls.custom.app.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @Author: Lizh
* @Date: 2026/6/9 10:50
* @Description: 商品描述
* @Version: 1.0
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "商品描述信息")
public class ProductRemarkVO implements Serializable {
/**
*
*/
@Schema(description = "")
private Integer id;
/**
* 商品ID
*/
@Schema(description = "商品ID")
private Integer product_id;
/**
* 描述
*/
@Schema(description = "图片地址")
private String remark;
}
package com.jomalls.custom.dal.entity;
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 lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* DIY 用户实体
* <p>
* 对齐 TS 项目 {@code DbDiyUser} 实体。
* 用于 namespace 用户查询和外部定价折扣计算。
*
* @author Lizh
* @date 2026-06-08
*/
@Data
@TableName("db_diy_user")
public class DbDiyUserEntity implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/** 用户唯一标识 */
@TableField("sku")
private String sku;
/** 用户名称(namespace 即此字段) */
@TableField("name")
private String name;
/** 用户名称-中文 */
@TableField("name_cn")
private String nameCn;
/** 用户类型:1 ERP客户 2 非ERP客户 */
@TableField("user_type")
private Integer userType;
/** 用户标识 */
@TableField("user_mark")
private String userMark;
/** 折扣(百分比,如 85 表示 85%) */
@TableField("discount")
private BigDecimal discount;
/** 余额 */
@TableField("balance")
private BigDecimal balance;
/** 状态 */
@TableField("status")
private Integer status;
}
package com.jomalls.custom.dal.entity;
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 lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* DIY 效果图实体
* <p>
* 对齐 TS 项目 {@code DbDiyXiaoguotu} 实体(db_diy_xiaoguotu 表)。
*
* @author Lizh
* @date 2026-06-08
*/
@Data
@TableName("db_diy_xiaoguotu")
public class DbDiyXiaoguotuEntity implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/** 标题 */
@TableField("title")
private String title;
/** 排序 */
@TableField("idx")
private Integer idx;
/** 效果图主图 */
@TableField("img_url")
private String imgUrl;
/** PSD 链接 */
@TableField("psd_url")
private String psdUrl;
/** 颜色 ID */
@TableField("color_id")
private Integer colorId;
/** 宽度 */
@TableField("width")
private Float width;
/** 高度 */
@TableField("height")
private Float height;
/** 分辨率 */
@TableField("dpi")
private Integer dpi;
/** db_diy 表 ID */
@TableField("diy_id")
private Integer diyId;
/** 状态:1 正常 0 禁用 */
@TableField("status")
private Integer status;
/** 层面 IDs */
@TableField("face_ids")
private String faceIds;
/** 是否允许设计 */
@TableField("enable_design")
private Boolean enableDesign;
/** 创建时间 */
@TableField("create_date")
private Date createDate;
}
package com.jomalls.custom.dal.entity;
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 lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 产品-工厂关联表 Entity
* <p>
* 对齐 TS 项目 {@code entity/product_factory_rel.ts},表 {@code product_factory_rel}。
*
* @author Lizh
* @date 2026-06-06
*/
@Data
@TableName("product_factory_rel")
public class ProductFactoryRelEntity implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/** custom_product_info 表 ID */
@TableField("product_id")
private Integer productId;
/** 工厂 ID */
@TableField("factory_id")
private Integer factoryId;
}
package com.jomalls.custom.dal.entity;
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 lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 产品模板信息实体
* <p>
* 对齐 TS 项目 {@code ProductTemplateInfo} 实体(product_template_info 表)。
*
* @author Lizh
* @date 2026-06-08
*/
@Data
@TableName("product_template_info")
public class ProductTemplateInfoEntity implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@TableField("sku")
private String sku;
/** custom_product_info 表 ID */
@TableField("product_id")
private Integer productId;
@TableField("product_sku")
private String productSku;
/** 模板 ID(db_diy 表) */
@TableField("diy_id")
private Integer diyId;
@TableField("name")
private String name;
@TableField("title")
private String title;
@TableField("category_id")
private Integer categoryId;
@TableField("style_id")
private Integer styleId;
@TableField("cost_price")
private BigDecimal costPrice;
@TableField("cost_price_max")
private BigDecimal costPriceMax;
@TableField("sales_price")
private BigDecimal salesPrice;
@TableField("sales_price_max")
private BigDecimal salesPriceMax;
@TableField("property1_cate_id")
private Integer property1CateId;
@TableField("property2_cate_id")
private Integer property2CateId;
@TableField("property3_cate_id")
private Integer property3CateId;
@TableField("property1_enname")
private String property1Enname;
@TableField("property2_enname")
private String property2Enname;
@TableField("property3_enname")
private String property3Enname;
@TableField("color_images")
private String colorImages;
@TableField("material")
private String material;
@TableField("print_type")
private Integer printType;
@TableField("create_time")
private Date createTime;
@TableField("update_time")
private Date updateTime;
}
...@@ -3,6 +3,7 @@ package com.jomalls.custom.dal.mapper; ...@@ -3,6 +3,7 @@ package com.jomalls.custom.dal.mapper;
import com.jomalls.custom.dal.entity.CustomProductInfoEntity; import com.jomalls.custom.dal.entity.CustomProductInfoEntity;
import com.jomalls.custom.mapper.BaseMapper; import com.jomalls.custom.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/** /**
* @author Lizh * @author Lizh
...@@ -12,4 +13,12 @@ import org.apache.ibatis.annotations.Mapper; ...@@ -12,4 +13,12 @@ import org.apache.ibatis.annotations.Mapper;
*/ */
@Mapper @Mapper
public interface CustomProductInfoMapper extends BaseMapper<CustomProductInfoEntity> { public interface CustomProductInfoMapper extends BaseMapper<CustomProductInfoEntity> {
/**
* 根据 SKU 查询商品
*
* @param sku 商品 SKU
* @return 商品实体,未找到返回 null
*/
CustomProductInfoEntity selectBySku(@Param("sku") String sku);
} }
...@@ -3,6 +3,9 @@ package com.jomalls.custom.dal.mapper; ...@@ -3,6 +3,9 @@ package com.jomalls.custom.dal.mapper;
import com.jomalls.custom.dal.entity.CustomProductWarehouseRelEntity; import com.jomalls.custom.dal.entity.CustomProductWarehouseRelEntity;
import com.jomalls.custom.mapper.BaseMapper; import com.jomalls.custom.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/** /**
* @author Lizh * @author Lizh
...@@ -12,4 +15,9 @@ import org.apache.ibatis.annotations.Mapper; ...@@ -12,4 +15,9 @@ import org.apache.ibatis.annotations.Mapper;
*/ */
@Mapper @Mapper
public interface CustomProductWarehouseRelMapper extends BaseMapper<CustomProductWarehouseRelEntity> { public interface CustomProductWarehouseRelMapper extends BaseMapper<CustomProductWarehouseRelEntity> {
/**
* 根据仓库 ID 列表查询关联关系
*/
List<CustomProductWarehouseRelEntity> selectByWarehouseIds(@Param("warehouseIds") List<Integer> warehouseIds);
} }
package com.jomalls.custom.dal.mapper;
import com.jomalls.custom.dal.entity.DbDiyUserEntity;
import com.jomalls.custom.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* DbDiyUser Mapper
*
* @author Lizh
* @date 2026-06-08
*/
@Mapper
public interface DbDiyUserMapper extends BaseMapper<DbDiyUserEntity> {
/**
* 根据 namespace(name 字段)查询用户
*
* @param name 用户名称
* @return 用户实体
*/
DbDiyUserEntity selectByName(@Param("name") String name);
/**
* 根据 userMark 查询用户(对齐 TS getByUserMark)
*
* @param userMark 用户标识
* @return 用户实体
*/
DbDiyUserEntity selectByUserMark(@Param("userMark") String userMark);
}
package com.jomalls.custom.dal.mapper;
import com.jomalls.custom.dal.entity.DbDiyXiaoguotuEntity;
import com.jomalls.custom.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* DbDiyXiaoguotu Mapper
*
* @author Lizh
* @date 2026-06-08
*/
@Mapper
public interface DbDiyXiaoguotuMapper extends BaseMapper<DbDiyXiaoguotuEntity> {
/**
* 根据模板 ID 查询效果图列表
*/
List<DbDiyXiaoguotuEntity> selectByDiyId(@Param("diyId") Integer diyId);
/**
* 根据模板 ID 列表批量查询效果图
*/
List<DbDiyXiaoguotuEntity> selectByDiyIds(@Param("diyIds") List<Integer> diyIds);
}
package com.jomalls.custom.dal.mapper;
import com.jomalls.custom.dal.entity.ProductFactoryRelEntity;
import com.jomalls.custom.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 产品-工厂关联 Mapper
*
* @author Lizh
* @date 2026-06-06
*/
@Mapper
public interface ProductFactoryRelMapper extends BaseMapper<ProductFactoryRelEntity> {
}
package com.jomalls.custom.dal.mapper;
import com.jomalls.custom.dal.entity.ProductTemplateInfoEntity;
import com.jomalls.custom.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* ProductTemplateInfo Mapper
*
* @author Lizh
* @date 2026-06-08
*/
@Mapper
public interface ProductTemplateInfoMapper extends BaseMapper<ProductTemplateInfoEntity> {
/**
* 根据产品 ID 查询模板关联列表
*
* @param productId 产品 ID
* @return 模板信息列表
*/
List<ProductTemplateInfoEntity> selectByProductId(@Param("productId") Integer productId);
/**
* 根据模板 ID 列表查询
*
* @param diyIds 模板 ID 列表
* @return 模板信息列表
*/
List<ProductTemplateInfoEntity> selectByDiyIds(@Param("diyIds") List<Integer> diyIds);
}
...@@ -11,5 +11,12 @@ import com.jomalls.custom.service.IBaseService; ...@@ -11,5 +11,12 @@ import com.jomalls.custom.service.IBaseService;
*/ */
public interface CustomProductInfoDomainService extends IBaseService<CustomProductInfoEntity> { public interface CustomProductInfoDomainService extends IBaseService<CustomProductInfoEntity> {
/**
* 根据 SKU 查询商品
*
* @param sku 商品 SKU
* @return 商品实体,未找到返回 null
*/
CustomProductInfoEntity getBySku(String sku);
} }
...@@ -3,6 +3,8 @@ package com.jomalls.custom.domain.service; ...@@ -3,6 +3,8 @@ package com.jomalls.custom.domain.service;
import com.jomalls.custom.dal.entity.CustomProductWarehouseRelEntity; import com.jomalls.custom.dal.entity.CustomProductWarehouseRelEntity;
import com.jomalls.custom.service.IBaseService; import com.jomalls.custom.service.IBaseService;
import java.util.List;
/** /**
* @author Lizh * @author Lizh
* @version 0.01 * @version 0.01
...@@ -11,5 +13,8 @@ import com.jomalls.custom.service.IBaseService; ...@@ -11,5 +13,8 @@ import com.jomalls.custom.service.IBaseService;
*/ */
public interface CustomProductWarehouseRelDomainService extends IBaseService<CustomProductWarehouseRelEntity> { public interface CustomProductWarehouseRelDomainService extends IBaseService<CustomProductWarehouseRelEntity> {
/**
* 根据仓库 ID 列表查询关联
*/
List<CustomProductWarehouseRelEntity> selectByWarehouseIds(List<Integer> warehouseIds);
} }
package com.jomalls.custom.domain.service;
import com.jomalls.custom.dal.entity.DbDiyUserEntity;
import com.jomalls.custom.service.IBaseService;
/**
* DbDiyUser 领域服务接口
*
* @author Lizh
* @date 2026-06-08
*/
public interface DbDiyUserDomainService extends IBaseService<DbDiyUserEntity> {
/**
* 根据 namespace(name 字段)查询用户
* <p>
* 对齐 TS {@code diyUserService.getByNamespace()}
*
* @param namespace 用户名称
* @return 用户实体,未找到返回 null
*/
DbDiyUserEntity getByNamespace(String namespace);
/**
* 根据 userMark 查询用户
* <p>
* 对齐 TS {@code diyUserService.getByUserMark()}
*/
DbDiyUserEntity getByUserMark(String userMark);
}
package com.jomalls.custom.domain.service;
import com.jomalls.custom.dal.entity.DbDiyXiaoguotuEntity;
import com.jomalls.custom.service.IBaseService;
import java.util.List;
/**
* DbDiyXiaoguotu 领域服务接口
*
* @author Lizh
* @date 2026-06-08
*/
public interface DbDiyXiaoguotuDomainService extends IBaseService<DbDiyXiaoguotuEntity> {
List<DbDiyXiaoguotuEntity> selectByDiyId(Integer diyId);
List<DbDiyXiaoguotuEntity> selectByDiyIds(List<Integer> diyIds);
}
package com.jomalls.custom.domain.service;
import com.jomalls.custom.dal.entity.ProductFactoryRelEntity;
import com.jomalls.custom.service.IBaseService;
/**
* 产品-工厂关联 Domain Service 接口
*
* @author Lizh
* @date 2026-06-06
*/
public interface ProductFactoryRelDomainService extends IBaseService<ProductFactoryRelEntity> {
}
package com.jomalls.custom.domain.service;
import com.jomalls.custom.dal.entity.ProductTemplateInfoEntity;
import com.jomalls.custom.service.IBaseService;
import java.util.List;
/**
* ProductTemplateInfo 领域服务接口
*
* @author Lizh
* @date 2026-06-08
*/
public interface ProductTemplateInfoDomainService extends IBaseService<ProductTemplateInfoEntity> {
/**
* 根据产品 ID 查询模板关联列表
*/
List<ProductTemplateInfoEntity> selectByProductId(Integer productId);
/**
* 根据模板 ID 列表查询
*/
List<ProductTemplateInfoEntity> selectByDiyIds(List<Integer> diyIds);
}
...@@ -4,6 +4,7 @@ import com.jomalls.custom.dal.mapper.CustomProductInfoMapper; ...@@ -4,6 +4,7 @@ import com.jomalls.custom.dal.mapper.CustomProductInfoMapper;
import com.jomalls.custom.dal.entity.CustomProductInfoEntity; import com.jomalls.custom.dal.entity.CustomProductInfoEntity;
import com.jomalls.custom.domain.service.CustomProductInfoDomainService; import com.jomalls.custom.domain.service.CustomProductInfoDomainService;
import com.jomalls.custom.service.impl.BaseServiceImpl; import com.jomalls.custom.service.impl.BaseServiceImpl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
...@@ -22,6 +23,9 @@ public class CustomProductInfoDomainServiceImpl extends BaseServiceImpl<CustomPr ...@@ -22,6 +23,9 @@ public class CustomProductInfoDomainServiceImpl extends BaseServiceImpl<CustomPr
public CustomProductInfoDomainServiceImpl(SqlSessionFactory sqlSessionFactory) { public CustomProductInfoDomainServiceImpl(SqlSessionFactory sqlSessionFactory) {
super(sqlSessionFactory); super(sqlSessionFactory);
} }
// 自定义方法或者基础方法重写
@Override
public CustomProductInfoEntity getBySku(String sku) {
return baseMapper.selectBySku(sku);
}
} }
\ No newline at end of file
...@@ -8,6 +8,8 @@ import org.apache.ibatis.session.SqlSessionFactory; ...@@ -8,6 +8,8 @@ import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List;
/** /**
* @author Lizh * @author Lizh
...@@ -22,6 +24,8 @@ public class CustomProductWarehouseRelDomainServiceImpl extends BaseServiceImpl< ...@@ -22,6 +24,8 @@ public class CustomProductWarehouseRelDomainServiceImpl extends BaseServiceImpl<
public CustomProductWarehouseRelDomainServiceImpl(SqlSessionFactory sqlSessionFactory) { public CustomProductWarehouseRelDomainServiceImpl(SqlSessionFactory sqlSessionFactory) {
super(sqlSessionFactory); super(sqlSessionFactory);
} }
// 自定义方法或者基础方法重写 @Override
public List<CustomProductWarehouseRelEntity> selectByWarehouseIds(List<Integer> warehouseIds) {
return baseMapper.selectByWarehouseIds(warehouseIds);
}
} }
\ No newline at end of file
package com.jomalls.custom.domain.service.impl;
import com.jomalls.custom.dal.entity.DbDiyUserEntity;
import com.jomalls.custom.dal.mapper.DbDiyUserMapper;
import com.jomalls.custom.domain.service.DbDiyUserDomainService;
import com.jomalls.custom.service.impl.BaseServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* DbDiyUser 领域服务实现
*
* @author Lizh
* @date 2026-06-08
*/
@Slf4j
@Service
public class DbDiyUserDomainServiceImpl extends BaseServiceImpl<DbDiyUserMapper, DbDiyUserEntity> implements DbDiyUserDomainService {
@Autowired
public DbDiyUserDomainServiceImpl(SqlSessionFactory sqlSessionFactory) {
super(sqlSessionFactory);
}
@Override
public DbDiyUserEntity getByNamespace(String namespace) {
return baseMapper.selectByName(namespace);
}
@Override
public DbDiyUserEntity getByUserMark(String userMark) {
return baseMapper.selectByUserMark(userMark);
}
}
package com.jomalls.custom.domain.service.impl;
import com.jomalls.custom.dal.entity.DbDiyXiaoguotuEntity;
import com.jomalls.custom.dal.mapper.DbDiyXiaoguotuMapper;
import com.jomalls.custom.domain.service.DbDiyXiaoguotuDomainService;
import com.jomalls.custom.service.impl.BaseServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
/**
* DbDiyXiaoguotu 领域服务实现
*
* @author Lizh
* @date 2026-06-08
*/
@Slf4j
@Service
public class DbDiyXiaoguotuDomainServiceImpl
extends BaseServiceImpl<DbDiyXiaoguotuMapper, DbDiyXiaoguotuEntity>
implements DbDiyXiaoguotuDomainService {
@Autowired
public DbDiyXiaoguotuDomainServiceImpl(SqlSessionFactory sqlSessionFactory) {
super(sqlSessionFactory);
}
@Override
public List<DbDiyXiaoguotuEntity> selectByDiyId(Integer diyId) {
return baseMapper.selectByDiyId(diyId);
}
@Override
public List<DbDiyXiaoguotuEntity> selectByDiyIds(List<Integer> diyIds) {
if (diyIds == null || diyIds.isEmpty()) {
return Collections.emptyList();
}
return baseMapper.selectByDiyIds(diyIds);
}
}
package com.jomalls.custom.domain.service.impl;
import com.jomalls.custom.dal.mapper.ProductFactoryRelMapper;
import com.jomalls.custom.dal.entity.ProductFactoryRelEntity;
import com.jomalls.custom.domain.service.ProductFactoryRelDomainService;
import com.jomalls.custom.service.impl.BaseServiceImpl;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 产品-工厂关联 Domain Service 实现
*
* @author Lizh
* @date 2026-06-06
*/
@Service
public class ProductFactoryRelDomainServiceImpl
extends BaseServiceImpl<ProductFactoryRelMapper, ProductFactoryRelEntity>
implements ProductFactoryRelDomainService {
@Autowired
public ProductFactoryRelDomainServiceImpl(SqlSessionFactory sqlSessionFactory) {
super(sqlSessionFactory);
}
}
package com.jomalls.custom.domain.service.impl;
import com.jomalls.custom.dal.entity.ProductTemplateInfoEntity;
import com.jomalls.custom.dal.mapper.ProductTemplateInfoMapper;
import com.jomalls.custom.domain.service.ProductTemplateInfoDomainService;
import com.jomalls.custom.service.impl.BaseServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* ProductTemplateInfo 领域服务实现
*
* @author Lizh
* @date 2026-06-08
*/
@Slf4j
@Service
public class ProductTemplateInfoDomainServiceImpl
extends BaseServiceImpl<ProductTemplateInfoMapper, ProductTemplateInfoEntity>
implements ProductTemplateInfoDomainService {
@Autowired
public ProductTemplateInfoDomainServiceImpl(SqlSessionFactory sqlSessionFactory) {
super(sqlSessionFactory);
}
@Override
public List<ProductTemplateInfoEntity> selectByProductId(Integer productId) {
return baseMapper.selectByProductId(productId);
}
@Override
public List<ProductTemplateInfoEntity> selectByDiyIds(List<Integer> diyIds) {
if (diyIds == null || diyIds.isEmpty()) {
return List.of();
}
return baseMapper.selectByDiyIds(diyIds);
}
}
...@@ -15,4 +15,12 @@ ...@@ -15,4 +15,12 @@
product_id, product_id,
diy_user_id diy_user_id
</sql> </sql>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO custom_product_blacklist (product_id, diy_user_id) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.productId}, #{item.diyUserId})
</foreach>
</insert>
</mapper> </mapper>
...@@ -17,4 +17,12 @@ ...@@ -17,4 +17,12 @@
remark, remark,
create_time create_time
</sql> </sql>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO custom_product_cn_remark (product_id, remark, create_time) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.productId}, #{item.remark}, #{item.createTime})
</foreach>
</insert>
</mapper> </mapper>
...@@ -15,4 +15,12 @@ ...@@ -15,4 +15,12 @@
product_id, product_id,
craft_id craft_id
</sql> </sql>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO custom_product_craft_rel (product_id, craft_id) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.productId}, #{item.craftId})
</foreach>
</insert>
</mapper> </mapper>
...@@ -15,4 +15,12 @@ ...@@ -15,4 +15,12 @@
product_id, product_id,
diy_user_id diy_user_id
</sql> </sql>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO custom_product_diy_user_rel (product_id, diy_user_id) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.productId}, #{item.diyUserId})
</foreach>
</insert>
</mapper> </mapper>
...@@ -19,4 +19,12 @@ ...@@ -19,4 +19,12 @@
price_max, price_max,
price_min price_min
</sql> </sql>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO custom_product_factory_price_interval_rel (product_id, currency_code, price_max, price_min) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.productId}, #{item.currencyCode}, #{item.priceMax}, #{item.priceMin})
</foreach>
</insert>
</mapper> </mapper>
...@@ -31,4 +31,12 @@ ...@@ -31,4 +31,12 @@
create_time, create_time,
update_time update_time
</sql> </sql>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO custom_product_factory_price_rel (product_id, item_id, item_sku, factory_id, factory_price, sales_price, factory_currency_code, sales_currency_code, create_time, update_time) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.productId}, #{item.itemId}, #{item.itemSku}, #{item.factoryId}, #{item.factoryPrice}, #{item.salesPrice}, #{item.factoryCurrencyCode}, #{item.salesCurrencyCode}, #{item.createTime}, #{item.updateTime})
</foreach>
</insert>
</mapper> </mapper>
...@@ -21,4 +21,12 @@ ...@@ -21,4 +21,12 @@
type, type,
create_time create_time
</sql> </sql>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO custom_product_image (product_id, image_url, sort, type, create_time) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.productId}, #{item.imageUrl}, #{item.sort}, #{item.type}, #{item.createTime})
</foreach>
</insert>
</mapper> </mapper>
...@@ -81,4 +81,20 @@ ...@@ -81,4 +81,20 @@
diy_id, diy_id,
diy_sku diy_sku
</sql> </sql>
<!-- 根据 SKU 查询商品 -->
<select id="selectBySku" resultMap="customProductInfoMap">
SELECT <include refid="tableColumns"/>
FROM custom_product_info
WHERE sku = #{sku}
LIMIT 1
</select>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO custom_product_info (sku, title, name, img_url, category_id, weight, purchasing_min, factory_price, sales_price, sales_price_max, status, property1_cate_id, property2_cate_id, property3_cate_id, property1_enname, property2_enname, property3_enname, color_images, material, print_type, product_no, origin_code, origin_name_cn, origin_name_en, currency_code, currency_name, product_type, factory_id, factory_code, processing, create_time, update_time, sort, diy_id, diy_sku) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.sku}, #{item.title}, #{item.name}, #{item.imgUrl}, #{item.categoryId}, #{item.weight}, #{item.purchasingMin}, #{item.factoryPrice}, #{item.salesPrice}, #{item.salesPriceMax}, #{item.status}, #{item.property1CateId}, #{item.property2CateId}, #{item.property3CateId}, #{item.property1Enname}, #{item.property2Enname}, #{item.property3Enname}, #{item.colorImages}, #{item.material}, #{item.printType}, #{item.productNo}, #{item.originCode}, #{item.originNameCn}, #{item.originNameEn}, #{item.currencyCode}, #{item.currencyName}, #{item.productType}, #{item.factoryId}, #{item.factoryCode}, #{item.processing}, #{item.createTime}, #{item.updateTime}, #{item.sort}, #{item.diyId}, #{item.diySku})
</foreach>
</insert>
</mapper> </mapper>
...@@ -19,4 +19,12 @@ ...@@ -19,4 +19,12 @@
value_id, value_id,
sku_property sku_property
</sql> </sql>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO custom_product_info_property (info_id, property_id, value_id, sku_property) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.infoId}, #{item.propertyId}, #{item.valueId}, #{item.skuProperty})
</foreach>
</insert>
</mapper> </mapper>
...@@ -69,4 +69,12 @@ ...@@ -69,4 +69,12 @@
create_time, create_time,
update_time update_time
</sql> </sql>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO custom_product_item (product_id, sku, sku_name, image, image_ary, property_cate_id1, property_cate_id2, property_cate_id3, property1_id, property2_id, property3_id, property_code1, property_code2, property_code3, option_enname1, option_enname2, option_enname3, custom_value1, custom_value2, custom_value3, factory_price, sales_price, sku_weight, print_type, sort, product_no, size_type, create_time, update_time) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.productId}, #{item.sku}, #{item.skuName}, #{item.image}, #{item.imageAry}, #{item.propertyCateId1}, #{item.propertyCateId2}, #{item.propertyCateId3}, #{item.property1Id}, #{item.property2Id}, #{item.property3Id}, #{item.propertyCode1}, #{item.propertyCode2}, #{item.propertyCode3}, #{item.optionEnname1}, #{item.optionEnname2}, #{item.optionEnname3}, #{item.customValue1}, #{item.customValue2}, #{item.customValue3}, #{item.factoryPrice}, #{item.salesPrice}, #{item.skuWeight}, #{item.printType}, #{item.sort}, #{item.productNo}, #{item.sizeType}, #{item.createTime}, #{item.updateTime})
</foreach>
</insert>
</mapper> </mapper>
...@@ -17,4 +17,12 @@ ...@@ -17,4 +17,12 @@
remark, remark,
create_time create_time
</sql> </sql>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO custom_product_remark (product_id, remark, create_time) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.productId}, #{item.remark}, #{item.createTime})
</foreach>
</insert>
</mapper> </mapper>
...@@ -15,4 +15,22 @@ ...@@ -15,4 +15,22 @@
product_id, product_id,
warehouse_id warehouse_id
</sql> </sql>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO custom_product_warehouse_rel (product_id, warehouse_id) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.productId}, #{item.warehouseId})
</foreach>
</insert>
<!-- 根据仓库 ID 列表查询关联 -->
<select id="selectByWarehouseIds" resultMap="customProductWarehouseRelMap">
SELECT <include refid="tableColumns"/>
FROM custom_product_warehouse_rel
WHERE warehouse_id IN
<foreach collection="warehouseIds" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
</mapper> </mapper>
...@@ -79,4 +79,12 @@ ...@@ -79,4 +79,12 @@
settlement_currency, settlement_currency,
company_name_cn company_name_cn
</sql> </sql>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO custom_warehouse_info (warehouse_name, warehouse_code, overall_logistics, super_factory, system_logistics, overall_logistics_scope, contact_name, contact_phone, contact_email, country_code, country_name, province, province_code, province_abb, city, city_code, district, street, postcode, company_name, social_credit_code, remarks, update_time, create_time, status, contact_name_cn, country_name_cn, province_cn, city_cn, district_cn, street_cn, formal, settlement_currency, company_name_cn) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.warehouseName}, #{item.warehouseCode}, #{item.overallLogistics}, #{item.superFactory}, #{item.systemLogistics}, #{item.overallLogisticsScope}, #{item.contactName}, #{item.contactPhone}, #{item.contactEmail}, #{item.countryCode}, #{item.countryName}, #{item.province}, #{item.provinceCode}, #{item.provinceAbb}, #{item.city}, #{item.cityCode}, #{item.district}, #{item.street}, #{item.postcode}, #{item.companyName}, #{item.socialCreditCode}, #{item.remarks}, #{item.updateTime}, #{item.createTime}, #{item.status}, #{item.contactNameCn}, #{item.countryNameCn}, #{item.provinceCn}, #{item.cityCn}, #{item.districtCn}, #{item.streetCn}, #{item.formal}, #{item.settlementCurrency}, #{item.companyNameCn})
</foreach>
</insert>
</mapper> </mapper>
...@@ -107,4 +107,12 @@ ...@@ -107,4 +107,12 @@
new_standard, new_standard,
diy_remark diy_remark
</sql> </sql>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO db_diy (sku, title, en_name, idx, img_url, img_arr, bianma, content, leixing, sc_img_type, chima_now_render, render, parent_id, user_ids, ban_user_ids, type_id, status, create_date, default_diy_id, factory_id, print_type, material, craft_id, min_price, max_price, picture_status, remark, namespace, audit_id, audit_name, audit_date, shelf_date, template_id, category_id, allocation_id, allocation_name, expedited, bind_diy_ids, is_bind, usa_made, manufacturer, style_num, type, production_client, erp_sku_properties, push_user, new_standard, diy_remark) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.sku}, #{item.title}, #{item.enName}, #{item.idx}, #{item.imgUrl}, #{item.imgArr}, #{item.bianma}, #{item.content}, #{item.leixing}, #{item.scImgType}, #{item.chimaNowRender}, #{item.render}, #{item.parentId}, #{item.userIds}, #{item.banUserIds}, #{item.typeId}, #{item.status}, #{item.createDate}, #{item.defaultDiyId}, #{item.factoryId}, #{item.printType}, #{item.material}, #{item.craftId}, #{item.minPrice}, #{item.maxPrice}, #{item.pictureStatus}, #{item.remark}, #{item.namespace}, #{item.auditId}, #{item.auditName}, #{item.auditDate}, #{item.shelfDate}, #{item.templateId}, #{item.categoryId}, #{item.allocationId}, #{item.allocationName}, #{item.expedited}, #{item.bindDiyIds}, #{item.isBind}, #{item.usaMade}, #{item.manufacturer}, #{item.styleNum}, #{item.type}, #{item.productionClient}, #{item.erpSkuProperties, typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler}, #{item.pushUser}, #{item.newStandard}, #{item.diyRemark})
</foreach>
</insert>
</mapper> </mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jomalls.custom.dal.mapper.DbDiyUserMapper">
<resultMap type="com.jomalls.custom.dal.entity.DbDiyUserEntity" id="dbDiyUserMap">
<result property="id" column="id"/>
<result property="sku" column="sku"/>
<result property="name" column="name"/>
<result property="nameCn" column="name_cn"/>
<result property="userType" column="user_type"/>
<result property="userMark" column="user_mark"/>
<result property="discount" column="discount"/>
<result property="balance" column="balance"/>
<result property="status" column="status"/>
</resultMap>
<sql id="tableColumns">
id,
sku,
name,
name_cn,
user_type,
user_mark,
discount,
balance,
status
</sql>
<!-- 根据 namespace 查询用户(对齐 TS getByNamespace) -->
<select id="selectByName" resultMap="dbDiyUserMap">
SELECT <include refid="tableColumns"/>
FROM db_diy_user
WHERE name = #{name}
LIMIT 1
</select>
<!-- 根据 userMark 查询用户(对齐 TS getByUserMark) -->
<select id="selectByUserMark" resultMap="dbDiyUserMap">
SELECT <include refid="tableColumns"/>
FROM db_diy_user
WHERE user_mark = #{userMark}
LIMIT 1
</select>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO db_diy_user (sku, name, name_cn, user_type, user_mark, discount, balance, status) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.sku}, #{item.name}, #{item.nameCn}, #{item.userType}, #{item.userMark}, #{item.discount}, #{item.balance}, #{item.status})
</foreach>
</insert>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jomalls.custom.dal.mapper.DbDiyXiaoguotuMapper">
<resultMap type="com.jomalls.custom.dal.entity.DbDiyXiaoguotuEntity" id="dbDiyXiaoguotuMap">
<result property="id" column="id"/>
<result property="title" column="title"/>
<result property="idx" column="idx"/>
<result property="imgUrl" column="img_url"/>
<result property="psdUrl" column="psd_url"/>
<result property="colorId" column="color_id"/>
<result property="width" column="width"/>
<result property="height" column="height"/>
<result property="dpi" column="dpi"/>
<result property="diyId" column="diy_id"/>
<result property="status" column="status"/>
<result property="faceIds" column="face_ids"/>
<result property="enableDesign" column="enable_design"/>
<result property="createDate" column="create_date"/>
</resultMap>
<sql id="tableColumns">
id, title, idx, img_url, psd_url, color_id, width, height, dpi, diy_id,
status, face_ids, enable_design, create_date
</sql>
<select id="selectByDiyId" resultMap="dbDiyXiaoguotuMap">
SELECT <include refid="tableColumns"/>
FROM db_diy_xiaoguotu
WHERE diy_id = #{diyId}
ORDER BY idx ASC
</select>
<select id="selectByDiyIds" resultMap="dbDiyXiaoguotuMap">
SELECT <include refid="tableColumns"/>
FROM db_diy_xiaoguotu
WHERE diy_id IN
<foreach collection="diyIds" item="id" open="(" separator="," close=")">
#{id}
</foreach>
ORDER BY idx ASC
</select>
<insert id="insertBatchSomeColumn">
INSERT INTO db_diy_xiaoguotu (title, idx, img_url, psd_url, color_id, width, height,
dpi, diy_id, status, face_ids, enable_design, create_date) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.title}, #{item.idx}, #{item.imgUrl}, #{item.psdUrl}, #{item.colorId},
#{item.width}, #{item.height}, #{item.dpi}, #{item.diyId}, #{item.status},
#{item.faceIds}, #{item.enableDesign}, #{item.createDate})
</foreach>
</insert>
</mapper>
...@@ -21,4 +21,12 @@ ...@@ -21,4 +21,12 @@
description, description,
create_time create_time
</sql> </sql>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO log_custom_product (product_id, employee_id, employee_account, description, create_time) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.productId}, #{item.employeeId}, #{item.employeeAccount}, #{item.description}, #{item.createTime})
</foreach>
</insert>
</mapper> </mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jomalls.custom.dal.mapper.ProductFactoryRelMapper">
<resultMap type="com.jomalls.custom.dal.entity.ProductFactoryRelEntity" id="productFactoryRelMap">
<result property="id" column="id"/>
<result property="productId" column="product_id"/>
<result property="factoryId" column="factory_id"/>
</resultMap>
<sql id="tableColumns">
id,
product_id,
factory_id
</sql>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO product_factory_rel (product_id, factory_id) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.productId}, #{item.factoryId})
</foreach>
</insert>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jomalls.custom.dal.mapper.ProductTemplateInfoMapper">
<resultMap type="com.jomalls.custom.dal.entity.ProductTemplateInfoEntity" id="productTemplateInfoMap">
<result property="id" column="id"/>
<result property="sku" column="sku"/>
<result property="productId" column="product_id"/>
<result property="productSku" column="product_sku"/>
<result property="diyId" column="diy_id"/>
<result property="name" column="name"/>
<result property="title" column="title"/>
<result property="categoryId" column="category_id"/>
<result property="styleId" column="style_id"/>
<result property="costPrice" column="cost_price"/>
<result property="costPriceMax" column="cost_price_max"/>
<result property="salesPrice" column="sales_price"/>
<result property="salesPriceMax" column="sales_price_max"/>
<result property="property1CateId" column="property1_cate_id"/>
<result property="property2CateId" column="property2_cate_id"/>
<result property="property3CateId" column="property3_cate_id"/>
<result property="property1Enname" column="property1_enname"/>
<result property="property2Enname" column="property2_enname"/>
<result property="property3Enname" column="property3_enname"/>
<result property="colorImages" column="color_images"/>
<result property="material" column="material"/>
<result property="printType" column="print_type"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<sql id="tableColumns">
id, sku, product_id, product_sku, diy_id, name, title, category_id, style_id,
cost_price, cost_price_max, sales_price, sales_price_max,
property1_cate_id, property2_cate_id, property3_cate_id,
property1_enname, property2_enname, property3_enname,
color_images, material, print_type, create_time, update_time
</sql>
<!-- 根据产品 ID 查询模板关联 -->
<select id="selectByProductId" resultMap="productTemplateInfoMap">
SELECT <include refid="tableColumns"/>
FROM product_template_info
WHERE product_id = #{productId}
</select>
<!-- 根据模板 ID 列表查询 -->
<select id="selectByDiyIds" resultMap="productTemplateInfoMap">
SELECT <include refid="tableColumns"/>
FROM product_template_info
WHERE diy_id IN
<foreach collection="diyIds" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO product_template_info (sku, product_id, product_sku, diy_id, name, title, category_id, style_id,
cost_price, cost_price_max, sales_price, sales_price_max,
property1_cate_id, property2_cate_id, property3_cate_id,
property1_enname, property2_enname, property3_enname,
color_images, material, print_type, create_time, update_time) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.sku}, #{item.productId}, #{item.productSku}, #{item.diyId}, #{item.name}, #{item.title},
#{item.categoryId}, #{item.styleId}, #{item.costPrice}, #{item.costPriceMax}, #{item.salesPrice},
#{item.salesPriceMax}, #{item.property1CateId}, #{item.property2CateId}, #{item.property3CateId},
#{item.property1Enname}, #{item.property2Enname}, #{item.property3Enname},
#{item.colorImages}, #{item.material}, #{item.printType}, #{item.createTime}, #{item.updateTime})
</foreach>
</insert>
</mapper>
...@@ -38,4 +38,12 @@ ...@@ -38,4 +38,12 @@
FOR UPDATE FOR UPDATE
</select> </select>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO sys_bill_rule (name, code, short_name, date_type, example, space_mark, start_number, current_number, enable_flag, last_no) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.name}, #{item.code}, #{item.shortName}, #{item.dateType}, #{item.example}, #{item.spaceMark}, #{item.startNumber}, #{item.currentNumber}, #{item.enableFlag}, #{item.lastNo})
</foreach>
</insert>
</mapper> </mapper>
...@@ -62,4 +62,12 @@ ...@@ -62,4 +62,12 @@
AND m.perms IS NOT NULL AND m.perms IS NOT NULL
AND m.perms != '' AND m.perms != ''
</select> </select>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.menuName}, #{item.parentId}, #{item.orderNum}, #{item.path}, #{item.component}, #{item.query}, #{item.routeName}, #{item.isFrame}, #{item.isCache}, #{item.menuType}, #{item.visible}, #{item.status}, #{item.perms}, #{item.icon}, #{item.createBy}, #{item.createTime}, #{item.updateBy}, #{item.updateTime}, #{item.remark})
</foreach>
</insert>
</mapper> </mapper>
...@@ -13,4 +13,12 @@ ...@@ -13,4 +13,12 @@
role_id, role_id,
dept_id, dept_id,
</sql> </sql>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO sys_role_dept (role_id, dept_id) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.roleId}, #{item.deptId})
</foreach>
</insert>
</mapper> </mapper>
...@@ -45,4 +45,12 @@ ...@@ -45,4 +45,12 @@
LEFT JOIN sys_role r ON ur.role_id = r.role_id LEFT JOIN sys_role r ON ur.role_id = r.role_id
WHERE ur.user_id = #{userId} AND r.del_flag = '0' AND r.status = '0' WHERE ur.user_id = #{userId} AND r.del_flag = '0' AND r.status = '0'
</select> </select>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO sys_role (role_name, role_key, role_sort, data_scope, menu_check_strictly, dept_check_strictly, status, del_flag, create_by, create_time, update_by, update_time, remark) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.roleName}, #{item.roleKey}, #{item.roleSort}, #{item.dataScope}, #{item.menuCheckStrictly}, #{item.deptCheckStrictly}, #{item.status}, #{item.delFlag}, #{item.createBy}, #{item.createTime}, #{item.updateBy}, #{item.updateTime}, #{item.remark})
</foreach>
</insert>
</mapper> </mapper>
...@@ -13,4 +13,12 @@ ...@@ -13,4 +13,12 @@
role_id, role_id,
menu_id, menu_id,
</sql> </sql>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO sys_role_menu (role_id, menu_id) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.roleId}, #{item.menuId})
</foreach>
</insert>
</mapper> </mapper>
...@@ -48,4 +48,13 @@ ...@@ -48,4 +48,13 @@
ORDER BY created_at DESC ORDER BY created_at DESC
</select> </select>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO sys_user (username, password, email, phone, status, created_at, updated_at) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.username}, #{item.password}, #{item.email}, #{item.phone}, #{item.status}, #{item.createdAt}, #{item.updatedAt})
</foreach>
</insert>
</mapper> </mapper>
\ No newline at end of file
...@@ -23,4 +23,12 @@ ...@@ -23,4 +23,12 @@
remark, remark,
create_time, create_time,
</sql> </sql>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO sys_user_old (account, img_url, password, status, remark, create_time) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.account}, #{item.imgUrl}, #{item.password}, #{item.status}, #{item.remark}, #{item.createTime})
</foreach>
</insert>
</mapper> </mapper>
...@@ -13,4 +13,12 @@ ...@@ -13,4 +13,12 @@
user_id, user_id,
role_id, role_id,
</sql> </sql>
<!-- 批量插入 -->
<insert id="insertBatchSomeColumn">
INSERT INTO sys_user_role (user_id, role_id) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.userId}, #{item.roleId})
</foreach>
</insert>
</mapper> </mapper>
...@@ -6,6 +6,7 @@ import lombok.Builder; ...@@ -6,6 +6,7 @@ import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date; import java.util.Date;
...@@ -19,8 +20,9 @@ import java.util.Date; ...@@ -19,8 +20,9 @@ import java.util.Date;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@Schema(description = "商品分类") @Schema(description = "商品分类")
public class BaseCategoryInfoVO implements Serializable { public class BaseCategoryInfoModel implements Serializable {
@Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private Integer id; private Integer id;
......
package com.jomalls.custom.integrate.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 基础属性 VO(admin API 返回)
* <p>
* 对齐 TS 项目 {@code basePropertyService.getByIds()} 的返回结构。
*
* @author Lizh
* @date 2026-06-08
*/
@Data
public class BasePropertyModel implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/** 属性 ID */
private Integer id;
/** 中文名称 */
private String cnname;
/** 英文名称 */
private String enname;
/** 排序 */
private Integer sort;
/** 是否是 SKU 属性 */
private Boolean skuProperty;
/** 是否多选 */
private Boolean multi;
/** 是否启用 */
private Boolean enable;
/** 分类信息 ID */
private String categoryInfoId;
/** 属性值列表 */
@JsonProperty("valueList")
private List<BasePropertyValueModel> valueList;
/** 是否公共数据 */
private Boolean publicData;
/** 属性值 ID 列表 */
private String propertyValueIds;
}
package com.jomalls.custom.integrate.model;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 属性值 VO
*
* @author Lizh
* @date 2026-06-08
*/
@Data
public class BasePropertyValueModel implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/** 属性值 ID */
private Integer id;
/** 分类编码 */
private String cateCode;
/** 分类名称 */
private String cateName;
/** 编码 */
private String code;
/** 属性值名称(中文) */
private String cnname;
/** 属性值名称(英文) */
private String enname;
/** 前景色 */
private String fontColor;
/** 背景色 */
private String bgColor;
/** 是否为电池属性 */
private Boolean battery;
/** 是否为液体属性 */
private Boolean liquid;
/** 是否为刀片属性 */
private Boolean knife;
/** 是否为选中属性 */
private Integer selected;
/** 排序 */
private Integer sort;
/** 是否启用 */
private Boolean enable;
/** 是否为公共数据 */
private Boolean publicData;
}
package com.jomalls.custom.integrate.model;
import lombok.Data;
/**
* @Author: Lizh
* @Date: 2026/6/9 14:39
* @Description: SaasAdmin返回数据
* @Version: 1.0
*/
@Data
public class SaasAdminApiResponseModel<T> {
private Integer code;
private T data;
private String requestId;
private String message;
}
package com.jomalls.custom.integrate.service;
import com.jomalls.custom.integrate.model.BaseCategoryInfoVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
@Slf4j
@Service
@RequiredArgsConstructor
public class BaseCategoryInfoService {
private static final String GET_TREE_URL = "/api/manage/rest/baseCategoryInfo/tree_list";
private static final String GET_BY_IDS_URL = "/api/manage/rest/baseCategoryInfo/getDataByIds";
private static final String GET_BY_ID_URL = "/api/manage/rest/baseCategoryInfo/get";
private static final String GET_TREE_LIST_URL = "/api/manage/rest/baseCategoryInfo/getDataByIds";
private static final String GET_ALL_LIST_URL = "/api/manage/rest/baseCategoryInfo/all_list";
/**
* 获取类别树
*/
public void getTree() {
}
public void getByIds(String ids) {
}
/**
* 获取带有风格属性的树形结构
*/
public void getTreeList() {
}
/**
* 根据id获取
* @param id
*/
public void getById(Integer id) {
}
/**
*查询所有类别
*/
public List<BaseCategoryInfoVO> getAllList() {
return null;
}
}
package com.jomalls.custom.integrate.service;
import com.jomalls.custom.enums.CodeEnum;
import com.jomalls.custom.integrate.client.RemoteApiClient;
import com.jomalls.custom.integrate.model.BaseCategoryInfoModel;
import com.jomalls.custom.integrate.model.BasePropertyModel;
import com.jomalls.custom.integrate.model.SaasAdminApiResponseModel;
import com.jomalls.custom.security.LoginUser;
import com.jomalls.custom.security.SecurityUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 商品分类服务
* <p>
* 调用 admin API 获取商品分类数据。
* 对齐 TS 项目 {@code baseCategoryInfoService}。
*
* @author zhengcunwen
* @author Lizh (Java 迁移)
* @since 2019-09-02
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class SaasAdminService {
private static final String GET_TREE_URL = "/api/manage/rest/baseCategoryInfo/tree_list";
private static final String GET_BY_IDS_URL = "/api/manage/rest/baseCategoryInfo/getDataByIds";
private static final String GET_BY_ID_URL = "/api/manage/rest/baseCategoryInfo/get";
private static final String GET_ALL_LIST_URL = "/api/manage/rest/baseCategoryInfo/all_list";
private static final String GET_PROPERTY_BY_IDS_URL = "/api/manage/rest/baseProperty/getByIds";
private static final int DEFAULT_MAP_SIZE = 2;
private final RemoteApiClient remoteApiClient;
@Value("${server.admin.base-url:https://admin.jomalls.com}")
private String adminBaseUrl;
private Map<String, String> getHeader() {
Map<String, String> headers = new HashMap<>(DEFAULT_MAP_SIZE);
headers.put("Content-Type", "application/json");
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser == null) {
return headers;
}
headers.put("jwt-token", loginUser.getToken());
return headers;
}
/**
* 根据属性 ID 列表批量查询属性(含属性值列表)
* <p>
* 对齐 TS {@code getByIds(ids)} 方法。
*
* @param ids 属性 ID,逗号分隔(如 "1,2,3")
* @return 属性列表,包含 valueList 和 skuProperty 等字段
*/
public List<BasePropertyModel> getPropertyByIds(String ids) {
if (!StringUtils.hasText(ids)) {
return Collections.emptyList();
}
try {
String url = adminBaseUrl + GET_PROPERTY_BY_IDS_URL + "?ids=" + ids;
ResponseEntity<SaasAdminApiResponseModel<List<BasePropertyModel>>> response = remoteApiClient.get(
url,
new ParameterizedTypeReference<>() {},
getHeader());
if (response != null && response.getBody() != null) {
log.debug("[ BasePropertyService ] getByIds 成功, ids={}, 返回 {} 条", ids, response.toString());
SaasAdminApiResponseModel<List<BasePropertyModel>> responseBody = response.getBody();
if (responseBody.getCode() == CodeEnum.SUCCESS.getCode()) {
return responseBody.getData();
}
}
log.warn("[ BasePropertyService ] getByIds 返回空, ids={}", ids);
return Collections.emptyList();
} catch (Exception e) {
log.error("[ BasePropertyService ] getByIds 调用失败, ids={}", ids, e);
return Collections.emptyList();
}
}
/**
* 获取分类树
* <p>
* 对齐 TS {@code getTree()}
*/
public List<BaseCategoryInfoModel> getTree() {
try {
ResponseEntity<List<BaseCategoryInfoModel>> response = remoteApiClient.get(
adminBaseUrl + GET_TREE_URL,
new ParameterizedTypeReference<>() {},
getHeader());
if (response != null && response.getBody() != null) {
return response.getBody();
}
} catch (Exception e) {
log.error("[ BaseCategoryInfoService ] getTree 调用失败", e);
}
return Collections.emptyList();
}
/**
* 根据 ID 列表批量查询
* <p>
* 对齐 TS {@code getByIds(ids)}
*/
public List<BaseCategoryInfoModel> getByIds(String ids) {
if (ids == null || ids.isEmpty()) {
return Collections.emptyList();
}
try {
String url = adminBaseUrl + GET_BY_IDS_URL + "?ids=" + ids;
ResponseEntity<List<BaseCategoryInfoModel>> response = remoteApiClient.get(
url,
new ParameterizedTypeReference<>() {},
getHeader());
if (response != null && response.getBody() != null) {
return response.getBody();
}
} catch (Exception e) {
log.error("[ BaseCategoryInfoService ] getByIds 调用失败, ids={}", ids, e);
}
return Collections.emptyList();
}
/**
* 获取带有风格属性的树形结构
* <p>
* 对齐 TS {@code treeList()}
*/
public List<BaseCategoryInfoModel> treeList() {
try {
ResponseEntity<List<BaseCategoryInfoModel>> response = remoteApiClient.get(
adminBaseUrl + GET_TREE_URL,
new ParameterizedTypeReference<>() {},
getHeader());
if (response != null && response.getBody() != null) {
return response.getBody();
}
} catch (Exception e) {
log.error("[ BaseCategoryInfoService ] treeList 调用失败", e);
}
return Collections.emptyList();
}
/**
* 根据 ID 查询单个分类
* <p>
* 对齐 TS {@code getById(id)}
*/
public BaseCategoryInfoModel getById(Integer id) {
if (id == null) {
return null;
}
try {
String url = adminBaseUrl + GET_BY_ID_URL + "?id=" + id;
ResponseEntity<BaseCategoryInfoModel> response = remoteApiClient.get(
url, BaseCategoryInfoModel.class, null);
if (response != null) {
return response.getBody();
}
} catch (Exception e) {
log.error("[ BaseCategoryInfoService ] getById 调用失败, id={}", id, e);
}
return null;
}
/**
* 查询所有分类
* <p>
* 对齐 TS {@code allList()}
*/
public List<BaseCategoryInfoModel> getAllList() {
try {
ResponseEntity<List<BaseCategoryInfoModel>> response = remoteApiClient.get(
adminBaseUrl + GET_ALL_LIST_URL,
new ParameterizedTypeReference<>() {},
getHeader());
if (response != null && response.getBody() != null) {
return response.getBody();
}
} catch (Exception e) {
log.error("[ BaseCategoryInfoService ] getAllList 调用失败", e);
}
return Collections.emptyList();
}
}
...@@ -6,6 +6,7 @@ import org.redisson.config.Config; ...@@ -6,6 +6,7 @@ import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
...@@ -23,20 +24,83 @@ import org.springframework.data.redis.serializer.StringRedisSerializer; ...@@ -23,20 +24,83 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
*/ */
@Configuration @Configuration
public class RedisConfig { public class RedisConfig {
/**
* Redis 地址配置
*/
@Value("${spring.data.redis.host:localhost}") @Value("${spring.data.redis.host:localhost}")
private String host; private String host;
/**
* Redis 端口
*/
@Value("${spring.data.redis.port:6379}") @Value("${spring.data.redis.port:6379}")
private int port; private int port;
/**
* Redis 密码
*/
@Value("${spring.data.redis.password:}") @Value("${spring.data.redis.password:}")
private String password; private String password;
/**
* Redis 数据库索引
*/
@Value("${spring.data.redis.database:0}") @Value("${spring.data.redis.database:0}")
private int database; private int database;
// ==================== RedisTemplate ==================== /**
* Redis 连接超时时间
*/
@Value("${spring.data.redis.connect-timeout:3000}")
private int connectTimeout;
/**
* Redis 读取超时
*/
@Value("${spring.data.redis.timeout:3000}")
private int timeout;
/**
* Redis 连接池最大连接数
*/
@Value("${spring.data.redis.pool.max-active:8}")
private int maxActive;
/**
* Redis 连接池最大空闲连接数
*/
@Value("${spring.data.redis.pool.max-idle:4}")
private int maxIdle;
/**
* Redis 连接池最小空闲连接数
*/
@Value("${spring.data.redis.pool.min-idle:2}")
private int minIdle;
/**
* Redisson 重试次数
*/
@Value("${spring.data.redis.redisson.retry-attempts:3}")
private int retryAttempts;
/**
* Redisson 重试间隔(毫秒)
*/
@Value("${spring.data.redis.redisson.retry-interval:1000}")
private int retryInterval;
/**
* Redisson 心跳间隔(毫秒)
*/
@Value("${spring.data.redis.redisson.ping-interval:60000}")
private int pingInterval;
/**
* Redisson 空闲连接超时(毫秒)
*/
@Value("${spring.data.redis.redisson.idle-timeout:10000}")
private int idleTimeout;
@Bean @Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
...@@ -59,9 +123,12 @@ public class RedisConfig { ...@@ -59,9 +123,12 @@ public class RedisConfig {
return new StringRedisTemplate(factory); return new StringRedisTemplate(factory);
} }
// ==================== Redisson ==================== /**
* Redisson 分布式锁客户端
* 使用 @Lazy 延迟初始化,避免启动时阻塞等待 Redis 连接
*/
@Bean(destroyMethod = "shutdown") @Bean(destroyMethod = "shutdown")
@Lazy
public RedissonClient redissonClient() { public RedissonClient redissonClient() {
Config config = new Config(); Config config = new Config();
String address = "redis://" + host + ":" + port; String address = "redis://" + host + ":" + port;
...@@ -70,8 +137,17 @@ public class RedisConfig { ...@@ -70,8 +137,17 @@ public class RedisConfig {
.setAddress(address) .setAddress(address)
.setPassword(password.isBlank() ? null : password) .setPassword(password.isBlank() ? null : password)
.setDatabase(database) .setDatabase(database)
.setConnectionPoolSize(16) .setConnectionPoolSize(maxActive)
.setConnectionMinimumIdleSize(4); .setConnectionMinimumIdleSize(minIdle)
.setConnectTimeout(connectTimeout)
.setTimeout(timeout)
// 重试配置,避免连接失败时无限等待
.setRetryAttempts(retryAttempts)
.setRetryInterval(retryInterval)
// 心跳配置,定期检测连接可用性
.setPingConnectionInterval(pingInterval)
// 空闲连接超时,及时释放无用连接
.setIdleConnectionTimeout(idleTimeout);
return Redisson.create(config); return Redisson.create(config);
} }
......
...@@ -3,12 +3,32 @@ spring.data.redis.host=172.16.19.100 ...@@ -3,12 +3,32 @@ spring.data.redis.host=172.16.19.100
spring.data.redis.port=6379 spring.data.redis.port=6379
spring.data.redis.password=joshine.dev spring.data.redis.password=joshine.dev
spring.data.redis.database=7 spring.data.redis.database=7
spring.data.redis.timeout=5000ms
spring.data.redis.connect-timeout=10000ms
## Lettuce连接池配置 ## 超时配置(毫秒)
# 读取/写入超时
spring.data.redis.timeout=5000
# 连接超时(缩短,避免长时间等待)
spring.data.redis.connect-timeout=3000
## 连接池配置(适配RedissonClient)
spring.data.redis.pool.max-active=8
spring.data.redis.pool.max-idle=4
spring.data.redis.pool.min-idle=2
spring.data.redis.pool.max-wait=5000
## Lettuce连接池配置(Spring Data Redis使用)
spring.data.redis.lettuce.pool.enabled=true spring.data.redis.lettuce.pool.enabled=true
spring.data.redis.lettuce.pool.max-active=16 spring.data.redis.lettuce.pool.max-active=8
spring.data.redis.lettuce.pool.max-idle=8 spring.data.redis.lettuce.pool.max-idle=4
spring.data.redis.lettuce.pool.min-idle=4 spring.data.redis.lettuce.pool.min-idle=2
spring.data.redis.lettuce.pool.max-wait=5000ms spring.data.redis.lettuce.pool.max-wait=5000
## Redisson特有配置
# 重试次数
spring.data.redis.redisson.retry-attempts=3
# 重试间隔(毫秒)
spring.data.redis.redisson.retry-interval=1000
# 心跳间隔(毫秒)
spring.data.redis.redisson.ping-interval=60000
# 空闲连接超时(毫秒)
spring.data.redis.redisson.idle-timeout=10000
\ No newline at end of file
...@@ -68,3 +68,6 @@ resilience4j.retry.configs.default.retry-exceptions=java.util.concurrent.Timeout ...@@ -68,3 +68,6 @@ resilience4j.retry.configs.default.retry-exceptions=java.util.concurrent.Timeout
resilience4j.retry.configs.default.ignore-exceptions=com.jomalls.custom.integrate.exception.RemoteServiceException resilience4j.retry.configs.default.ignore-exceptions=com.jomalls.custom.integrate.exception.RemoteServiceException
# 远程 API 重试实例使用默认配置 # 远程 API 重试实例使用默认配置
resilience4j.retry.instances.remoteApi.base-config=default resilience4j.retry.instances.remoteApi.base-config=default
server.admin.base-url=https://admin.jomalls.com
\ No newline at end of file
...@@ -30,16 +30,17 @@ ...@@ -30,16 +30,17 @@
<pattern>${log.pattern}</pattern> <pattern>${log.pattern}</pattern>
<charset>UTF-8</charset> <charset>UTF-8</charset>
</encoder> </encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level> <level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter> </filter>
</appender> </appender>
<!-- 错误日志:仅 ERROR --> <appender name="file_warn" class="ch.qos.logback.core.rolling.RollingFileAppender">
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/sys-warn.log</file>
<file>${log.path}/sys-error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern> <fileNamePattern>${log.path}/sys-warn.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>60</maxHistory> <maxHistory>60</maxHistory>
</rollingPolicy> </rollingPolicy>
<encoder> <encoder>
...@@ -47,40 +48,45 @@ ...@@ -47,40 +48,45 @@
<charset>UTF-8</charset> <charset>UTF-8</charset>
</encoder> </encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter"> <filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level> <level>WARN</level>
<onMatch>ACCEPT</onMatch> <onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch> <onMismatch>DENY</onMismatch>
</filter> </filter>
</appender> </appender>
<!-- 用户操作日志 --> <!-- 错误日志:仅 ERROR -->
<appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-user.log</file> <file>${log.path}/sys-error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern> <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>60</maxHistory> <maxHistory>60</maxHistory>
</rollingPolicy> </rollingPolicy>
<encoder> <encoder>
<pattern>${log.pattern}</pattern> <pattern>${log.pattern}</pattern>
<charset>UTF-8</charset> <charset>UTF-8</charset>
</encoder> </encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender> </appender>
<!-- ==================== 框架日志级别控制 ==================== --> <!-- ==================== 框架日志级别控制 ==================== -->
<!-- 项目代码 --> <!-- 项目代码 -->
<logger name="com.jomalls.custom" level="INFO"/> <logger name="com.jomalls.custom" level="DEBUG"/>
<!-- Spring 框架 --> <!-- Spring 框架 -->
<logger name="org.springframework" level="WARN"/> <logger name="org.springframework" level="DEBUG"/>
<logger name="org.springframework.web" level="WARN"/> <logger name="org.springframework.web" level="DEBUG"/>
<!-- MyBatis / MyBatis-Plus --> <!-- MyBatis / MyBatis-Plus -->
<logger name="org.mybatis" level="WARN"/> <logger name="org.mybatis" level="DEBUG"/>
<logger name="com.baomidou.mybatisplus" level="WARN"/> <logger name="com.baomidou.mybatisplus" level="DEBUG"/>
<!-- 数据库连接池 --> <!-- 数据库连接池 -->
<logger name="com.zaxxer.hikari" level="WARN"/> <logger name="com.zaxxer.hikari" level="DEBUG"/>
<!-- WebClient / Reactor Netty --> <!-- WebClient / Reactor Netty -->
<logger name="io.netty" level="WARN"/> <logger name="io.netty" level="WARN"/>
...@@ -91,7 +97,7 @@ ...@@ -91,7 +97,7 @@
<logger name="io.github.resilience4j" level="INFO"/> <logger name="io.github.resilience4j" level="INFO"/>
<!-- Redis / Lettuce --> <!-- Redis / Lettuce -->
<logger name="io.lettuce" level="WARN"/> <logger name="io.lettuce" level="DEBUG"/>
<!-- API 文档 --> <!-- API 文档 -->
<logger name="org.springdoc" level="WARN"/> <logger name="org.springdoc" level="WARN"/>
...@@ -100,11 +106,7 @@ ...@@ -100,11 +106,7 @@
<root level="INFO"> <root level="INFO">
<appender-ref ref="console"/> <appender-ref ref="console"/>
<appender-ref ref="file_info"/> <appender-ref ref="file_info"/>
<appender-ref ref="file_warn"/>
<appender-ref ref="file_error"/> <appender-ref ref="file_error"/>
</root> </root>
<!-- 用户操作日志(独立 logger,不继承 root 的 appender,避免重复打印到控制台) -->
<logger name="sys-user" level="INFO" additivity="false">
<appender-ref ref="sys-user"/>
</logger>
</configuration> </configuration>
...@@ -2,7 +2,7 @@ package com.jomalls.custom.webapp.controller; ...@@ -2,7 +2,7 @@ package com.jomalls.custom.webapp.controller;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jomalls.custom.app.vo.CustomProductImagePageVO; import com.jomalls.custom.app.vo.CustomProductImagePageVO;
import com.jomalls.custom.app.vo.CustomProductImageVO; import com.jomalls.custom.app.vo.CustomProductImageSnakeVO;
import com.jomalls.custom.app.service.CustomProductImageService; import com.jomalls.custom.app.service.CustomProductImageService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
...@@ -33,13 +33,13 @@ public class CustomProductImageController { ...@@ -33,13 +33,13 @@ public class CustomProductImageController {
/** /**
* 列表查询接口 * 列表查询接口
* *
* @param customProductImageVO 条件model * @param customProductImageSnakeVO 条件model
* @return list集合 * @return list集合
*/ */
@Operation(summary = "列表查询接口", description = "根据条件查询列表接口(不分页)") @Operation(summary = "列表查询接口", description = "根据条件查询列表接口(不分页)")
@RequestMapping(value = "/list", method = RequestMethod.POST) @RequestMapping(value = "/list", method = RequestMethod.POST)
public List<CustomProductImageVO> list(@RequestBody CustomProductImageVO customProductImageVO) { public List<CustomProductImageSnakeVO> list(@RequestBody CustomProductImageSnakeVO customProductImageSnakeVO) {
return customProductImageService.list(customProductImageVO); return customProductImageService.list(customProductImageSnakeVO);
} }
/** /**
...@@ -50,7 +50,7 @@ public class CustomProductImageController { ...@@ -50,7 +50,7 @@ public class CustomProductImageController {
*/ */
@Operation(summary = "分页列表接口", description = "根据条件查询分页列表接口") @Operation(summary = "分页列表接口", description = "根据条件查询分页列表接口")
@RequestMapping(value = "/pageList", method = RequestMethod.POST) @RequestMapping(value = "/pageList", method = RequestMethod.POST)
public IPage<CustomProductImageVO> pageList(@RequestBody CustomProductImagePageVO customProductImagePageVO) { public IPage<CustomProductImageSnakeVO> pageList(@RequestBody CustomProductImagePageVO customProductImagePageVO) {
return customProductImageService.pageList(customProductImagePageVO); return customProductImageService.pageList(customProductImagePageVO);
} }
...@@ -63,31 +63,31 @@ public class CustomProductImageController { ...@@ -63,31 +63,31 @@ public class CustomProductImageController {
*/ */
@Operation(summary = "根据主键id查询详情", description = "根据主键id查询详情") @Operation(summary = "根据主键id查询详情", description = "根据主键id查询详情")
@RequestMapping(value = "/info/{id}", method = RequestMethod.GET) @RequestMapping(value = "/info/{id}", method = RequestMethod.GET)
public CustomProductImageVO info(@Parameter(description = "主键id", required = true) @PathVariable("id") Integer id) { public CustomProductImageSnakeVO info(@Parameter(description = "主键id", required = true) @PathVariable("id") Integer id) {
return customProductImageService.info(id); return customProductImageService.info(id);
} }
/** /**
* 保存对象 * 保存对象
* *
* @param customProductImageVO 保存对象 * @param customProductImageSnakeVO 保存对象
*/ */
@Operation(summary = "保存对象", description = "保存对象") @Operation(summary = "保存对象", description = "保存对象")
@RequestMapping(value = "/save", method = RequestMethod.POST) @RequestMapping(value = "/save", method = RequestMethod.POST)
public void save(@RequestBody @Valid CustomProductImageVO customProductImageVO) { public void save(@RequestBody @Valid CustomProductImageSnakeVO customProductImageSnakeVO) {
customProductImageService.save(customProductImageVO); customProductImageService.save(customProductImageSnakeVO);
} }
/** /**
* 根据id修改对象 * 根据id修改对象
* *
* @param customProductImageVO 修改对象 * @param customProductImageSnakeVO 修改对象
*/ */
@Operation(summary = "根据id修改对象", description = "根据id修改对象") @Operation(summary = "根据id修改对象", description = "根据id修改对象")
@RequestMapping(value = "/updateById", method = RequestMethod.PUT) @RequestMapping(value = "/updateById", method = RequestMethod.PUT)
public void updateById(@RequestBody CustomProductImageVO customProductImageVO) { public void updateById(@RequestBody CustomProductImageSnakeVO customProductImageSnakeVO) {
customProductImageService.updateById(customProductImageVO); customProductImageService.updateById(customProductImageSnakeVO);
} }
/** /**
......
package com.jomalls.custom.webapp.controller; package com.jomalls.custom.webapp.controller;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jomalls.custom.app.dto.CustomProductInfoDTO; import com.jomalls.custom.app.dto.AddBlackListDTO;
import com.jomalls.custom.app.vo.CustomProductInfoVO; import com.jomalls.custom.app.dto.BindDiyUserDTO;
import com.jomalls.custom.app.dto.CustomProductInfoSnakeDTO;
import com.jomalls.custom.app.dto.CustomProductInfoUpdateDTO;
import com.jomalls.custom.app.enums.CustomProductInfoStatusEnum;
import com.jomalls.custom.app.service.CustomProductInfoService; import com.jomalls.custom.app.service.CustomProductInfoService;
import com.jomalls.custom.app.vo.CustomProductInfoSnakeVO;
import com.jomalls.custom.app.vo.CustomProductInfoVO;
import com.jomalls.custom.app.vo.DbDiyVO;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
...@@ -13,92 +19,116 @@ import org.springframework.beans.factory.annotation.Autowired; ...@@ -13,92 +19,116 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* @author Lizh * @author Lizh
* @version 0.01 * @version 0.02
* @description: Controller * @description: 定制商品 Controller(含组合编排操作端点)
* @date 2026-05-29 11:43:04 * @date 2026-05-29 11:43:04
*/ */
@Slf4j @Slf4j
@RestController @RestController
@Tag(name = "/customProductInfo", description = "Controller") @Tag(name = "/api/v2/product/info", description = "定制商品接口")
@RequestMapping("/api/product/info") @RequestMapping("/api/v2/product/info")
public class CustomProductInfoController { public class CustomProductInfoController {
@Autowired @Autowired
private CustomProductInfoService customProductInfoService; private CustomProductInfoService customProductInfoService;
/**
* 列表查询接口
*
* @param customProductInfoVO 条件model
* @return list集合
*/
@Operation(summary = "列表查询接口", description = "根据条件查询列表接口(不分页)")
@RequestMapping(value = "/list", method = RequestMethod.POST)
public List<CustomProductInfoVO> list(@RequestBody CustomProductInfoVO customProductInfoVO) {
return customProductInfoService.list(customProductInfoVO);
}
/**
* 根据条件查询分页列表接口
*
* @param param 分页入参model
* @return 分页对象
*/
@Operation(summary = "分页列表接口", description = "根据条件查询分页列表接口") @Operation(summary = "分页列表接口", description = "根据条件查询分页列表接口")
@RequestMapping(value = "/page", method = RequestMethod.POST) @RequestMapping(value = "/page", method = RequestMethod.POST)
public IPage<CustomProductInfoVO> pageList(@RequestBody CustomProductInfoDTO param) { public IPage<CustomProductInfoVO> pageList(@RequestBody CustomProductInfoSnakeDTO param) {
return customProductInfoService.pageList(param); return customProductInfoService.pageList(param);
} }
@Operation(summary = "创建商品", description = "创建商品")
@PostMapping("/create")
public void create(@RequestBody @Valid CustomProductInfoSnakeDTO dto) {
customProductInfoService.saveFull(dto);
}
/** @Operation(summary = "组合更新商品", description = "事务内处理主表及子表的增/删/改差异")
* 根据主键id查询详情 @PostMapping("/update")
* public void update(@RequestBody @Valid CustomProductInfoUpdateDTO dto) {
* @param id 主键 customProductInfoService.updateFull(dto);
* @return 实体model
*/
@Operation(summary = "根据主键id查询详情", description = "根据主键id查询详情")
@RequestMapping(value = "/info/{id}", method = RequestMethod.GET)
public CustomProductInfoVO info(@Parameter(description = "主键id", required = true) @PathVariable("id") Integer id) {
return customProductInfoService.info(id);
} }
/** @Operation(summary = "根据 ID 或 SKU 获取完整数据", description = "并行加载主表及所有子表数据后在 Java 层组合")
* 保存对象 @GetMapping("/get")
* public CustomProductInfoSnakeVO getDetail(
* @param customProductInfoVO 保存对象 @Parameter(description = "商品 ID") @RequestParam(required = false) Integer id,
*/ @Parameter(description = "商品 SKU") @RequestParam(required = false) String sku,
@Operation(summary = "保存对象", description = "保存对象") @Parameter(description = "命名空间") @RequestParam(required = false) String namespace) {
@RequestMapping(value = "/save", method = RequestMethod.POST) return customProductInfoService.getByIdOrSku(id, sku, namespace);
public void save(@RequestBody @Valid CustomProductInfoVO customProductInfoVO) {
customProductInfoService.save(customProductInfoVO);
} }
@Operation(summary = "获取状态列表", description = "返回所有商品状态枚举")
@GetMapping("/getStatusList")
public List<Map<String, Object>> getStatusList() {
return CustomProductInfoStatusEnum.getAllStatusList();
}
/** @Operation(summary = "绑定默认模型", description = "设置商品的默认 DIY 模板")
* 根据id修改对象 @GetMapping("/bindDefaultDiy")
* public void bindDefaultDiy(
* @param customProductInfoVO 修改对象 @Parameter(description = "商品 ID") @RequestParam Integer id,
*/ @Parameter(description = "DIY 模板 ID") @RequestParam Integer diyId,
@Operation(summary = "根据id修改对象", description = "根据id修改对象") @Parameter(description = "DIY 模板 SKU") @RequestParam String diySku) {
@RequestMapping(value = "/updateById", method = RequestMethod.PUT) customProductInfoService.bindDefaultDiy(id, diyId, diySku);
public void updateById(@RequestBody CustomProductInfoVO customProductInfoVO) {
customProductInfoService.updateById(customProductInfoVO);
} }
/** @Operation(summary = "获取绑定的模型", description = "获取商品绑定的 DIY 模板列表")
* 根据主键id进行删除 @GetMapping("/getBindsDiyById")
* public List<DbDiyVO> getBindsDiyById(@Parameter(description = "商品 ID") @RequestParam Integer id) {
* @param id 主键 return customProductInfoService.getBindsDiyById(id);
*/ }
@Operation(summary = "根据主键id进行删除", description = "根据主键id进行删除")
@RequestMapping(value = "/deleteById/{id}", method = RequestMethod.DELETE) @Operation(summary = "获取绑定的客户", description = "获取商品绑定的 DIY 用户 ID 列表")
public void deleteById(@Parameter(description = "主键id", required = true) @PathVariable("id") Integer id) { @GetMapping("/getBindsDiyUserIdsById")
customProductInfoService.deleteById(id); public List<Integer> getBindsDiyUserIdsById(@Parameter(description = "商品 ID") @RequestParam Integer id) {
return customProductInfoService.getBindsDiyUserById(id);
}
@Operation(summary = "获取黑名单", description = "获取商品黑名单 DIY 用户 ID 列表")
@GetMapping("/getBlackListById")
public List<Integer> getBlackListById(@Parameter(description = "商品 ID") @RequestParam Integer id) {
return customProductInfoService.getBlackListById(id);
}
@Operation(summary = "批量绑定客户", description = "批量绑定/解绑商品与 DIY 用户的关联")
@PostMapping("/bindsDiyUser")
public void bindsDiyUser(@RequestBody @Valid BindDiyUserDTO dto) {
customProductInfoService.bindsDiyUser(dto);
}
@Operation(summary = "加入黑名单", description = "批量将 DIY 用户加入商品黑名单")
@PostMapping("/addBlackList")
public void addBlackList(@RequestBody @Valid AddBlackListDTO dto) {
customProductInfoService.addBlackList(dto);
} }
@Operation(summary = "获取绑定的工艺", description = "获取商品绑定的工艺 ID 列表")
@GetMapping("/getCraftById")
public List<Long> getCraftById(@Parameter(description = "商品 ID") @RequestParam Integer id) {
return customProductInfoService.getCraftById(id);
}
// ==================== ERP 专用端点(对齐 TS ApiCustomProductInfoController) ====================
@Operation(summary = "ERP 分页查询", description = "包含黑名单过滤、用户折扣等 ERP 特定逻辑")
@PostMapping("/erpPage")
public IPage<CustomProductInfoVO> erpPage(@RequestBody CustomProductInfoSnakeDTO param) {
return customProductInfoService.erpPage(param);
}
@Operation(summary = "ERP 获取绑定 DIY", description = "获取商品绑定的 DIY 模板(含效果图),支持按 userMark/namespace 过滤")
@GetMapping("/getErpBindsDiyById")
public List<DbDiyVO> getErpBindsDiyById(
@Parameter(description = "商品 ID") @RequestParam Integer id,
@Parameter(description = "用户标识") @RequestParam(required = false) String userMark,
@Parameter(description = "命名空间") @RequestParam(required = false) String namespace) {
return customProductInfoService.getErpBindsDiyById(id, userMark, namespace);
}
} }
...@@ -2,7 +2,7 @@ package com.jomalls.custom.webapp.controller; ...@@ -2,7 +2,7 @@ package com.jomalls.custom.webapp.controller;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jomalls.custom.app.vo.CustomProductInfoPropertyPageVO; import com.jomalls.custom.app.vo.CustomProductInfoPropertyPageVO;
import com.jomalls.custom.app.vo.CustomProductInfoPropertyVO; import com.jomalls.custom.app.vo.CustomProductInfoPropertySnakeVO;
import com.jomalls.custom.app.service.CustomProductInfoPropertyService; import com.jomalls.custom.app.service.CustomProductInfoPropertyService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
...@@ -33,13 +33,13 @@ public class CustomProductInfoPropertyController { ...@@ -33,13 +33,13 @@ public class CustomProductInfoPropertyController {
/** /**
* 列表查询接口 * 列表查询接口
* *
* @param customProductInfoPropertyVO 条件model * @param customProductInfoPropertySnakeVO 条件model
* @return list集合 * @return list集合
*/ */
@Operation(summary = "列表查询接口", description = "根据条件查询列表接口(不分页)") @Operation(summary = "列表查询接口", description = "根据条件查询列表接口(不分页)")
@RequestMapping(value = "/list", method = RequestMethod.POST) @RequestMapping(value = "/list", method = RequestMethod.POST)
public List<CustomProductInfoPropertyVO> list(@RequestBody CustomProductInfoPropertyVO customProductInfoPropertyVO) { public List<CustomProductInfoPropertySnakeVO> list(@RequestBody CustomProductInfoPropertySnakeVO customProductInfoPropertySnakeVO) {
return customProductInfoPropertyService.list(customProductInfoPropertyVO); return customProductInfoPropertyService.list(customProductInfoPropertySnakeVO);
} }
/** /**
...@@ -50,7 +50,7 @@ public class CustomProductInfoPropertyController { ...@@ -50,7 +50,7 @@ public class CustomProductInfoPropertyController {
*/ */
@Operation(summary = "分页列表接口", description = "根据条件查询分页列表接口") @Operation(summary = "分页列表接口", description = "根据条件查询分页列表接口")
@RequestMapping(value = "/pageList", method = RequestMethod.POST) @RequestMapping(value = "/pageList", method = RequestMethod.POST)
public IPage<CustomProductInfoPropertyVO> pageList(@RequestBody CustomProductInfoPropertyPageVO customProductInfoPropertyPageVO) { public IPage<CustomProductInfoPropertySnakeVO> pageList(@RequestBody CustomProductInfoPropertyPageVO customProductInfoPropertyPageVO) {
return customProductInfoPropertyService.pageList(customProductInfoPropertyPageVO); return customProductInfoPropertyService.pageList(customProductInfoPropertyPageVO);
} }
...@@ -63,31 +63,31 @@ public class CustomProductInfoPropertyController { ...@@ -63,31 +63,31 @@ public class CustomProductInfoPropertyController {
*/ */
@Operation(summary = "根据主键id查询详情", description = "根据主键id查询详情") @Operation(summary = "根据主键id查询详情", description = "根据主键id查询详情")
@RequestMapping(value = "/info/{id}", method = RequestMethod.GET) @RequestMapping(value = "/info/{id}", method = RequestMethod.GET)
public CustomProductInfoPropertyVO info(@Parameter(description = "主键id", required = true) @PathVariable("id") Integer id) { public CustomProductInfoPropertySnakeVO info(@Parameter(description = "主键id", required = true) @PathVariable("id") Integer id) {
return customProductInfoPropertyService.info(id); return customProductInfoPropertyService.info(id);
} }
/** /**
* 保存对象 * 保存对象
* *
* @param customProductInfoPropertyVO 保存对象 * @param customProductInfoPropertySnakeVO 保存对象
*/ */
@Operation(summary = "保存对象", description = "保存对象") @Operation(summary = "保存对象", description = "保存对象")
@RequestMapping(value = "/save", method = RequestMethod.POST) @RequestMapping(value = "/save", method = RequestMethod.POST)
public void save(@RequestBody @Valid CustomProductInfoPropertyVO customProductInfoPropertyVO) { public void save(@RequestBody @Valid CustomProductInfoPropertySnakeVO customProductInfoPropertySnakeVO) {
customProductInfoPropertyService.save(customProductInfoPropertyVO); customProductInfoPropertyService.save(customProductInfoPropertySnakeVO);
} }
/** /**
* 根据id修改对象 * 根据id修改对象
* *
* @param customProductInfoPropertyVO 修改对象 * @param customProductInfoPropertySnakeVO 修改对象
*/ */
@Operation(summary = "根据id修改对象", description = "根据id修改对象") @Operation(summary = "根据id修改对象", description = "根据id修改对象")
@RequestMapping(value = "/updateById", method = RequestMethod.PUT) @RequestMapping(value = "/updateById", method = RequestMethod.PUT)
public void updateById(@RequestBody CustomProductInfoPropertyVO customProductInfoPropertyVO) { public void updateById(@RequestBody CustomProductInfoPropertySnakeVO customProductInfoPropertySnakeVO) {
customProductInfoPropertyService.updateById(customProductInfoPropertyVO); customProductInfoPropertyService.updateById(customProductInfoPropertySnakeVO);
} }
/** /**
......
...@@ -2,7 +2,7 @@ package com.jomalls.custom.webapp.controller; ...@@ -2,7 +2,7 @@ package com.jomalls.custom.webapp.controller;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jomalls.custom.app.vo.CustomProductItemPageVO; import com.jomalls.custom.app.vo.CustomProductItemPageVO;
import com.jomalls.custom.app.vo.CustomProductItemVO; import com.jomalls.custom.app.vo.CustomProductItemSnakeVO;
import com.jomalls.custom.app.service.CustomProductItemService; import com.jomalls.custom.app.service.CustomProductItemService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
...@@ -33,13 +33,13 @@ public class CustomProductItemController { ...@@ -33,13 +33,13 @@ public class CustomProductItemController {
/** /**
* 列表查询接口 * 列表查询接口
* *
* @param customProductItemVO 条件model * @param customProductItemSnakeVO 条件model
* @return list集合 * @return list集合
*/ */
@Operation(summary = "列表查询接口", description = "根据条件查询列表接口(不分页)") @Operation(summary = "列表查询接口", description = "根据条件查询列表接口(不分页)")
@RequestMapping(value = "/list", method = RequestMethod.POST) @RequestMapping(value = "/list", method = RequestMethod.POST)
public List<CustomProductItemVO> list(@RequestBody CustomProductItemVO customProductItemVO) { public List<CustomProductItemSnakeVO> list(@RequestBody CustomProductItemSnakeVO customProductItemSnakeVO) {
return customProductItemService.list(customProductItemVO); return customProductItemService.list(customProductItemSnakeVO);
} }
/** /**
...@@ -50,7 +50,7 @@ public class CustomProductItemController { ...@@ -50,7 +50,7 @@ public class CustomProductItemController {
*/ */
@Operation(summary = "分页列表接口", description = "根据条件查询分页列表接口") @Operation(summary = "分页列表接口", description = "根据条件查询分页列表接口")
@RequestMapping(value = "/pageList", method = RequestMethod.POST) @RequestMapping(value = "/pageList", method = RequestMethod.POST)
public IPage<CustomProductItemVO> pageList(@RequestBody CustomProductItemPageVO customProductItemPageVO) { public IPage<CustomProductItemSnakeVO> pageList(@RequestBody CustomProductItemPageVO customProductItemPageVO) {
return customProductItemService.pageList(customProductItemPageVO); return customProductItemService.pageList(customProductItemPageVO);
} }
...@@ -63,31 +63,31 @@ public class CustomProductItemController { ...@@ -63,31 +63,31 @@ public class CustomProductItemController {
*/ */
@Operation(summary = "根据主键id查询详情", description = "根据主键id查询详情") @Operation(summary = "根据主键id查询详情", description = "根据主键id查询详情")
@RequestMapping(value = "/info/{id}", method = RequestMethod.GET) @RequestMapping(value = "/info/{id}", method = RequestMethod.GET)
public CustomProductItemVO info(@Parameter(description = "主键id", required = true) @PathVariable("id") Integer id) { public CustomProductItemSnakeVO info(@Parameter(description = "主键id", required = true) @PathVariable("id") Integer id) {
return customProductItemService.info(id); return customProductItemService.info(id);
} }
/** /**
* 保存对象 * 保存对象
* *
* @param customProductItemVO 保存对象 * @param customProductItemSnakeVO 保存对象
*/ */
@Operation(summary = "保存对象", description = "保存对象") @Operation(summary = "保存对象", description = "保存对象")
@RequestMapping(value = "/save", method = RequestMethod.POST) @RequestMapping(value = "/save", method = RequestMethod.POST)
public void save(@RequestBody @Valid CustomProductItemVO customProductItemVO) { public void save(@RequestBody @Valid CustomProductItemSnakeVO customProductItemSnakeVO) {
customProductItemService.save(customProductItemVO); customProductItemService.save(customProductItemSnakeVO);
} }
/** /**
* 根据id修改对象 * 根据id修改对象
* *
* @param customProductItemVO 修改对象 * @param customProductItemSnakeVO 修改对象
*/ */
@Operation(summary = "根据id修改对象", description = "根据id修改对象") @Operation(summary = "根据id修改对象", description = "根据id修改对象")
@RequestMapping(value = "/updateById", method = RequestMethod.PUT) @RequestMapping(value = "/updateById", method = RequestMethod.PUT)
public void updateById(@RequestBody CustomProductItemVO customProductItemVO) { public void updateById(@RequestBody CustomProductItemSnakeVO customProductItemSnakeVO) {
customProductItemService.updateById(customProductItemVO); customProductItemService.updateById(customProductItemSnakeVO);
} }
/** /**
......
package com.jomalls.custom.webapp.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jomalls.custom.app.service.ProductFactoryRelService;
import com.jomalls.custom.app.vo.ProductFactoryRelPageVO;
import com.jomalls.custom.app.vo.ProductFactoryRelVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 产品-工厂关联 Controller
*
* @author Lizh
* @date 2026-06-06
*/
@Slf4j
@RestController
@Tag(name = "/productFactoryRel", description = "产品-工厂关联接口")
@RequestMapping("/productFactoryRel")
public class ProductFactoryRelController {
@Autowired
private ProductFactoryRelService productFactoryRelService;
@Operation(summary = "列表查询接口", description = "根据条件查询列表接口(不分页)")
@RequestMapping(value = "/list", method = RequestMethod.POST)
public List<ProductFactoryRelVO> list(@RequestBody ProductFactoryRelVO vo) {
return productFactoryRelService.list(vo);
}
@Operation(summary = "分页列表接口", description = "根据条件查询分页列表接口")
@RequestMapping(value = "/pageList", method = RequestMethod.POST)
public IPage<ProductFactoryRelVO> pageList(@RequestBody ProductFactoryRelPageVO param) {
return productFactoryRelService.pageList(param);
}
@Operation(summary = "根据主键id查询详情", description = "根据主键id查询详情")
@RequestMapping(value = "/info/{id}", method = RequestMethod.GET)
public ProductFactoryRelVO info(@Parameter(description = "主键id", required = true) @PathVariable("id") Integer id) {
return productFactoryRelService.info(id);
}
@Operation(summary = "保存对象", description = "保存对象")
@RequestMapping(value = "/save", method = RequestMethod.POST)
public void save(@RequestBody @Valid ProductFactoryRelVO vo) {
productFactoryRelService.save(vo);
}
@Operation(summary = "根据id修改对象", description = "根据id修改对象")
@RequestMapping(value = "/updateById", method = RequestMethod.PUT)
public void updateById(@RequestBody ProductFactoryRelVO vo) {
productFactoryRelService.updateById(vo);
}
@Operation(summary = "根据主键id进行删除", description = "根据主键id进行删除")
@RequestMapping(value = "/deleteById/{id}", method = RequestMethod.DELETE)
public void deleteById(@Parameter(description = "主键id", required = true) @PathVariable("id") Integer id) {
productFactoryRelService.deleteById(id);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment