Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
custom-server
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
lizhonghong
custom-server
Commits
43164dcf
Commit
43164dcf
authored
Jun 10, 2026
by
Lizh
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
对照TS代码逻辑,迁移商品更新接口
parent
7b931415
Hide whitespace changes
Inline
Side-by-side
Showing
28 changed files
with
750 additions
and
472 deletions
+750
-472
custom-server-app/src/main/java/com/jomalls/custom/app/dto/CustomProductInfoSaveDTO1.java
+0
-160
custom-server-app/src/main/java/com/jomalls/custom/app/dto/CustomProductInfoSnakeDTO.java
+21
-7
custom-server-app/src/main/java/com/jomalls/custom/app/dto/CustomProductInfoUpdateSnakeDTO.java
+21
-9
custom-server-app/src/main/java/com/jomalls/custom/app/dto/FactoryPriceIntervalRelSnakeDTO.java
+7
-5
custom-server-app/src/main/java/com/jomalls/custom/app/dto/FactoryPriceRelSnakeDTO.java
+9
-9
custom-server-app/src/main/java/com/jomalls/custom/app/dto/ProductChangeSnakeDTO.java
+1
-1
custom-server-app/src/main/java/com/jomalls/custom/app/service/CustomProductFactoryPriceRelService.java
+9
-9
custom-server-app/src/main/java/com/jomalls/custom/app/service/CustomProductInfoService.java
+2
-2
custom-server-app/src/main/java/com/jomalls/custom/app/service/impl/CustomProductFactoryPriceRelServiceImpl.java
+13
-13
custom-server-app/src/main/java/com/jomalls/custom/app/service/impl/CustomProductInfoServiceImpl.java
+497
-215
custom-server-app/src/main/java/com/jomalls/custom/app/service/impl/DiyUserServiceImpl.java
+3
-3
custom-server-app/src/main/java/com/jomalls/custom/app/vo/CustomProductFactoryPriceRelSnakeVO.java
+11
-11
custom-server-app/src/main/java/com/jomalls/custom/app/vo/CustomProductInfoSnakeVO.java
+1
-1
custom-server-app/src/main/java/com/jomalls/custom/app/vo/CustomProductInfoVO.java
+5
-1
custom-server-app/src/main/java/com/jomalls/custom/app/vo/DbDiyVO.java
+5
-1
custom-server-app/src/main/java/com/jomalls/custom/app/vo/DbDiyXiaoguotuVO.java
+60
-0
custom-server-domain/src/main/java/com/jomalls/custom/dal/entity/LogCustomProductEntity.java
+1
-1
custom-server-domain/src/main/java/com/jomalls/custom/dal/mapper/CustomProductInfoMapper.java
+14
-0
custom-server-domain/src/main/java/com/jomalls/custom/domain/service/CustomProductInfoDomainService.java
+10
-0
custom-server-domain/src/main/java/com/jomalls/custom/domain/service/impl/CustomProductInfoDomainServiceImpl.java
+8
-0
custom-server-domain/src/main/resources/mapper/CustomProductFactoryPriceRelMapper.xml
+2
-2
custom-server-domain/src/main/resources/mapper/CustomProductImageMapper.xml
+2
-2
custom-server-domain/src/main/resources/mapper/CustomProductInfoMapper.xml
+15
-0
custom-server-starter/src/main/java/com/jomalls/custom/config/CommonExceptionHandlerAdvice.java
+18
-1
custom-server-starter/src/main/resources/application.properties
+0
-4
custom-server-starter/src/main/resources/logback-spring.xml
+1
-1
custom-server-webapp/src/main/java/com/jomalls/custom/webapp/controller/CustomProductFactoryPriceRelController.java
+12
-12
custom-server-webapp/src/main/java/com/jomalls/custom/webapp/controller/CustomProductInfoController.java
+2
-2
No files found.
custom-server-app/src/main/java/com/jomalls/custom/app/dto/CustomProductInfoSaveDTO1.java
deleted
100644 → 0
View file @
7b931415
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
;
}
custom-server-app/src/main/java/com/jomalls/custom/app/dto/CustomProductInfoSnakeDTO.java
View file @
43164dcf
package
com
.
jomalls
.
custom
.
app
.
dto
;
import
com.fasterxml.jackson.annotation.JsonSetter
;
import
com.jomalls.custom.page.PageRequest
;
import
io.swagger.v3.oas.annotations.media.Schema
;
import
jakarta.validation.constraints.Digits
;
...
...
@@ -135,10 +136,24 @@ public class CustomProductInfoSnakeDTO extends PageRequest {
private
Integer
status
;
/**
* 是否九猫处理
* 是否九猫处理(0=否 1=是 2=不确定/未设置)
* <p>
* 兼容前端发送 Boolean(true→1, false→0)和 Number(0/1/2)。
*/
@Schema
(
description
=
"是否九猫处理"
)
private
Boolean
processing
;
@Schema
(
description
=
"是否九猫处理(0=否 1=是 2=未设置,也支持 true/false)"
)
private
Integer
processing
;
/** Jackson 兼容 Boolean→Integer 反序列化 */
@JsonSetter
(
"processing"
)
public
void
setProcessingValue
(
Object
value
)
{
if
(
value
instanceof
Boolean
)
{
this
.
processing
=
(
Boolean
)
value
?
1
:
0
;
}
else
if
(
value
instanceof
Number
)
{
this
.
processing
=
((
Number
)
value
).
intValue
();
}
else
{
this
.
processing
=
null
;
}
}
/**
* 英文备注
...
...
@@ -180,7 +195,7 @@ public class CustomProductInfoSnakeDTO extends PageRequest {
* 工厂价格关联列表
*/
@Schema
(
description
=
"工厂价格关联列表"
)
private
List
<
FactoryPriceRelDTO
>
factoryPriceList
;
private
List
<
FactoryPriceRel
Snake
DTO
>
factoryPriceList
;
/**
* SKU 属性集合
...
...
@@ -271,7 +286,7 @@ public class CustomProductInfoSnakeDTO extends PageRequest {
* 工厂价格区间关联列表
*/
@Schema
(
description
=
"工厂价格区间关联列表"
)
private
List
<
FactoryPriceIntervalRelDTO
>
factoryPriceIntervalList
;
private
List
<
FactoryPriceIntervalRel
Snake
DTO
>
factoryPriceIntervalList
;
/**
* DIY 用户 ID 列表(绑定客户)
...
...
@@ -296,8 +311,7 @@ public class CustomProductInfoSnakeDTO extends PageRequest {
@Schema
(
description
=
"工厂 ID 列表"
)
private
List
<
Integer
>
factoryIds
;
// ==================== ERP 专用查询字段(对齐 TS ERPQueryProductInfoDTO) ====================
/** 以下为ERP 专用查询字段 */
@Schema
(
description
=
"DIY 模板 SKU 筛选(ERP)"
)
private
String
diySku
;
...
...
custom-server-app/src/main/java/com/jomalls/custom/app/dto/CustomProductInfoUpdateDTO.java
→
custom-server-app/src/main/java/com/jomalls/custom/app/dto/CustomProductInfoUpdate
Snake
DTO.java
View file @
43164dcf
package
com
.
jomalls
.
custom
.
app
.
dto
;
import
com.fasterxml.jackson.annotation.JsonProperty
;
import
io.swagger.v3.oas.annotations.media.Schema
;
import
lombok.Data
;
import
lombok.EqualsAndHashCode
;
...
...
@@ -15,12 +16,12 @@ import java.util.List;
* 继承 SaveDTO 的字段,额外增加变更列表用于差异化更新。
*
* @author Lizh
* @date 2026-06-
06
* @date 2026-06-
10
*/
@EqualsAndHashCode
(
callSuper
=
true
)
@Data
@Schema
(
description
=
"组合更新商品请求"
)
public
class
CustomProductInfoUpdate
DTO
extends
CustomProductInfoSaveDTO1
implements
Serializable
{
public
class
CustomProductInfoUpdate
SnakeDTO
extends
CustomProductInfoSnakeDTO
implements
Serializable
{
@Serial
private
static
final
long
serialVersionUID
=
1L
;
...
...
@@ -28,17 +29,23 @@ public class CustomProductInfoUpdateDTO extends CustomProductInfoSaveDTO1 implem
@Schema
(
description
=
"商品 ID(必填)"
)
private
Integer
id
;
@Schema
(
description
=
"子项变更列表(增/删/改)"
,
implementation
=
ProductChangeDTO
.
class
)
private
ProductChangeDTO
productChange
;
@Schema
(
description
=
"子项变更列表(增/删/改)"
,
implementation
=
ProductChange
Snake
DTO
.
class
)
private
ProductChange
Snake
DTO
productChange
;
@JsonProperty
(
"factoryPriceChange"
)
@Schema
(
description
=
"工厂价格变更列表(增/删/改)"
,
implementation
=
ProductFactoryPriceChangeDTO
.
class
)
private
ProductFactoryPriceChangeDTO
productF
actoryPriceChange
;
private
ProductFactoryPriceChangeDTO
f
actoryPriceChange
;
@JsonProperty
(
"imageChange"
)
@Schema
(
description
=
"图片变更列表(增/删/改)"
,
implementation
=
ProductImageChangeDTO
.
class
)
private
ProductImageChangeDTO
productI
mageChange
;
private
ProductImageChangeDTO
i
mageChange
;
@JsonProperty
(
"sizeChange"
)
@Schema
(
description
=
"尺码图变更列表(增/删/改)"
,
implementation
=
ProductImageChangeDTO
.
class
)
private
ProductImageChangeDTO
productSizeChange
;
private
ProductImageChangeDTO
sizeChange
;
@Schema
(
description
=
"DIY 用户 ID 列表(绑定客户)"
)
private
List
<
Integer
>
diyUserIds
;
/**
* 工厂价格变更 DTO
...
...
@@ -46,13 +53,14 @@ public class CustomProductInfoUpdateDTO extends CustomProductInfoSaveDTO1 implem
@Data
@Schema
(
description
=
"工厂价格变更"
)
public
static
class
ProductFactoryPriceChangeDTO
implements
Serializable
{
@Serial
private
static
final
long
serialVersionUID
=
1L
;
@Schema
(
description
=
"新增列表"
)
private
List
<
FactoryPriceRelDTO
>
addList
;
private
List
<
FactoryPriceRel
Snake
DTO
>
addList
;
@Schema
(
description
=
"修改列表"
)
private
List
<
FactoryPriceRelDTO
>
updateList
;
private
List
<
FactoryPriceRel
Snake
DTO
>
updateList
;
@Schema
(
description
=
"删除 ID 列表"
)
private
List
<
Integer
>
removeList
;
...
...
@@ -64,11 +72,15 @@ public class CustomProductInfoUpdateDTO extends CustomProductInfoSaveDTO1 implem
@Data
@Schema
(
description
=
"图片变更"
)
public
static
class
ProductImageChangeDTO
implements
Serializable
{
@Serial
private
static
final
long
serialVersionUID
=
1L
;
@Schema
(
description
=
"新增列表"
)
private
List
<
CustomProductImageSnakeDTO
>
addList
;
@Schema
(
description
=
"修改列表"
)
private
List
<
CustomProductImageSnakeDTO
>
updateList
;
@Schema
(
description
=
"删除 ID 列表"
)
private
List
<
Integer
>
removeList
;
}
...
...
custom-server-app/src/main/java/com/jomalls/custom/app/dto/FactoryPriceIntervalRelDTO.java
→
custom-server-app/src/main/java/com/jomalls/custom/app/dto/FactoryPriceIntervalRel
Snake
DTO.java
View file @
43164dcf
...
...
@@ -6,6 +6,7 @@ import lombok.Builder;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
import
java.io.Serial
;
import
java.io.Serializable
;
import
java.math.BigDecimal
;
...
...
@@ -15,23 +16,24 @@ import java.math.BigDecimal;
* 对齐 TS 项目 {@code dto/custom.product.dto.ts} 中的 {@code FactoryPriceIntervalRelDTO}。
*
* @author Lizh
* @date 2026-06-
06
* @date 2026-06-
10
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema
(
description
=
"工厂价格区间关联"
)
public
class
FactoryPriceIntervalRelDTO
implements
Serializable
{
public
class
FactoryPriceIntervalRel
Snake
DTO
implements
Serializable
{
@Serial
private
static
final
long
serialVersionUID
=
1L
;
@Schema
(
description
=
"币种编码"
)
private
String
currency
C
ode
;
private
String
currency
_c
ode
;
@Schema
(
description
=
"系统成本最低价"
)
private
BigDecimal
price
M
in
;
private
BigDecimal
price
_m
in
;
@Schema
(
description
=
"系统成本最高价"
)
private
BigDecimal
price
M
ax
;
private
BigDecimal
price
_m
ax
;
}
custom-server-app/src/main/java/com/jomalls/custom/app/dto/FactoryPriceRelDTO.java
→
custom-server-app/src/main/java/com/jomalls/custom/app/dto/FactoryPriceRel
Snake
DTO.java
View file @
43164dcf
...
...
@@ -16,36 +16,36 @@ import java.math.BigDecimal;
* 对齐 TS 项目 {@code dto/custom.product.dto.ts} 中的 {@code CreateFactoryPriceRelDTO}。
*
* @author Lizh
* @date 2026-06-
06
* @date 2026-06-
10
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema
(
description
=
"工厂价格关联"
)
public
class
FactoryPriceRelDTO
implements
Serializable
{
public
class
FactoryPriceRel
Snake
DTO
implements
Serializable
{
@Serial
private
static
final
long
serialVersionUID
=
1L
;
@Schema
(
description
=
"子项 ID(custom_product_item 的 id)"
)
private
Integer
item
I
d
;
private
Integer
item
_i
d
;
@Schema
(
description
=
"子项 SKU"
)
private
String
item
S
ku
;
private
String
item
_s
ku
;
@Schema
(
description
=
"工厂 ID"
)
private
Integer
factory
I
d
;
private
Integer
factory
_i
d
;
@Schema
(
description
=
"工厂价格"
)
private
BigDecimal
factory
P
rice
;
private
BigDecimal
factory
_p
rice
;
@Schema
(
description
=
"销售价格"
)
private
BigDecimal
sales
P
rice
;
private
BigDecimal
sales
_p
rice
;
@Schema
(
description
=
"工厂币种编码"
)
private
String
factory
CurrencyC
ode
;
private
String
factory
_currency_c
ode
;
@Schema
(
description
=
"销售币种编码"
)
private
String
sales
CurrencyC
ode
;
private
String
sales
_currency_c
ode
;
}
custom-server-app/src/main/java/com/jomalls/custom/app/dto/ProductChangeDTO.java
→
custom-server-app/src/main/java/com/jomalls/custom/app/dto/ProductChange
Snake
DTO.java
View file @
43164dcf
...
...
@@ -14,7 +14,7 @@ import java.util.List;
@NoArgsConstructor
@AllArgsConstructor
@Schema
(
description
=
"商品变更Dto"
)
public
class
ProductChangeDTO
implements
Serializable
{
public
class
ProductChange
Snake
DTO
implements
Serializable
{
@Schema
(
description
=
"添加集合"
,
implementation
=
CustomProductItemSnakeDTO
.
class
)
private
List
<
CustomProductItemSnakeDTO
>
addList
;
...
...
custom-server-app/src/main/java/com/jomalls/custom/app/service/CustomProductFactoryPriceRelService.java
View file @
43164dcf
...
...
@@ -2,7 +2,7 @@ package com.jomalls.custom.app.service;
import
com.baomidou.mybatisplus.core.metadata.IPage
;
import
com.jomalls.custom.app.vo.CustomProductFactoryPriceRelPageVO
;
import
com.jomalls.custom.app.vo.CustomProductFactoryPriceRelVO
;
import
com.jomalls.custom.app.vo.CustomProductFactoryPriceRel
Snake
VO
;
import
java.util.List
;
...
...
@@ -17,10 +17,10 @@ public interface CustomProductFactoryPriceRelService {
/**
* 列表查询接口
*
* @param customProductFactoryPriceRelVO 条件model
* @param customProductFactoryPriceRel
Snake
VO 条件model
* @return list集合
*/
List
<
CustomProductFactoryPriceRel
VO
>
list
(
CustomProductFactoryPriceRelVO
customProductFactoryPriceRel
VO
);
List
<
CustomProductFactoryPriceRel
SnakeVO
>
list
(
CustomProductFactoryPriceRelSnakeVO
customProductFactoryPriceRelSnake
VO
);
/**
* 根据条件查询分页列表接口
...
...
@@ -28,7 +28,7 @@ public interface CustomProductFactoryPriceRelService {
* @param customProductFactoryPriceRelPageVO 分页入参model
* @return 分页对象
*/
IPage
<
CustomProductFactoryPriceRelVO
>
pageList
(
CustomProductFactoryPriceRelPageVO
customProductFactoryPriceRelPageVO
);
IPage
<
CustomProductFactoryPriceRel
Snake
VO
>
pageList
(
CustomProductFactoryPriceRelPageVO
customProductFactoryPriceRelPageVO
);
/**
* 根据id查询详情
...
...
@@ -36,21 +36,21 @@ public interface CustomProductFactoryPriceRelService {
* @param id 主键
* @return 实体model
*/
CustomProductFactoryPriceRelVO
info
(
Integer
id
);
CustomProductFactoryPriceRel
Snake
VO
info
(
Integer
id
);
/**
* 保存对象
*
* @param customProductFactoryPriceRelVO 保存对象
* @param customProductFactoryPriceRel
Snake
VO 保存对象
*/
void
save
(
CustomProductFactoryPriceRel
VO
customProductFactoryPriceRel
VO
);
void
save
(
CustomProductFactoryPriceRel
SnakeVO
customProductFactoryPriceRelSnake
VO
);
/**
* 根据id修改对象
*
* @param customProductFactoryPriceRelVO 修改对象
* @param customProductFactoryPriceRel
Snake
VO 修改对象
*/
void
updateById
(
CustomProductFactoryPriceRel
VO
customProductFactoryPriceRel
VO
);
void
updateById
(
CustomProductFactoryPriceRel
SnakeVO
customProductFactoryPriceRelSnake
VO
);
/**
* 根据主键ID进行删除
...
...
custom-server-app/src/main/java/com/jomalls/custom/app/service/CustomProductInfoService.java
View file @
43164dcf
...
...
@@ -4,7 +4,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
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.dto.CustomProductInfoUpdate
Snake
DTO
;
import
com.jomalls.custom.app.vo.CustomProductInfoSnakeVO
;
import
com.jomalls.custom.app.vo.CustomProductInfoVO
;
import
com.jomalls.custom.app.vo.DbDiyVO
;
...
...
@@ -33,7 +33,7 @@ public interface CustomProductInfoService {
*
* @param dto 组合更新 DTO
*/
void
updateFull
(
CustomProductInfoUpdateDTO
dto
);
void
updateFull
(
CustomProductInfoUpdate
Snake
DTO
dto
);
/**
* 根据 ID 或 SKU 加载商品完整数据(并行单表查询后在 Java 层组合)
...
...
custom-server-app/src/main/java/com/jomalls/custom/app/service/impl/CustomProductFactoryPriceRelServiceImpl.java
View file @
43164dcf
...
...
@@ -4,7 +4,7 @@ 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.vo.CustomProductFactoryPriceRelPageVO
;
import
com.jomalls.custom.app.vo.CustomProductFactoryPriceRelVO
;
import
com.jomalls.custom.app.vo.CustomProductFactoryPriceRel
Snake
VO
;
import
com.jomalls.custom.app.service.CustomProductFactoryPriceRelService
;
import
com.jomalls.custom.app.utils.BeanMapper
;
import
com.jomalls.custom.app.utils.CustomAsserts
;
...
...
@@ -37,34 +37,34 @@ public class CustomProductFactoryPriceRelServiceImpl implements CustomProductFac
}
@Override
public
List
<
CustomProductFactoryPriceRel
VO
>
list
(
CustomProductFactoryPriceRelVO
customProductFactoryPriceRel
VO
)
{
public
List
<
CustomProductFactoryPriceRel
SnakeVO
>
list
(
CustomProductFactoryPriceRelSnakeVO
customProductFactoryPriceRelSnake
VO
)
{
QueryWrapper
<
CustomProductFactoryPriceRelEntity
>
queryWrapper
=
new
QueryWrapper
<>();
// TODO 根据业务条件组装入参
List
<
CustomProductFactoryPriceRelEntity
>
list
=
customProductFactoryPriceRelDomainService
.
list
(
queryWrapper
);
return
list
.
stream
().
map
(
e
->
BeanMapper
.
mapper
().
convert
(
e
,
CustomProductFactoryPriceRelVO
.
class
)).
collect
(
Collectors
.
toList
());
return
list
.
stream
().
map
(
e
->
BeanMapper
.
mapper
().
convert
(
e
,
CustomProductFactoryPriceRel
Snake
VO
.
class
)).
collect
(
Collectors
.
toList
());
}
@Override
public
IPage
<
CustomProductFactoryPriceRelVO
>
pageList
(
CustomProductFactoryPriceRelPageVO
customProductFactoryPriceRelPageVO
)
{
public
IPage
<
CustomProductFactoryPriceRel
Snake
VO
>
pageList
(
CustomProductFactoryPriceRelPageVO
customProductFactoryPriceRelPageVO
)
{
CustomAsserts
.
nonNull
(
customProductFactoryPriceRelPageVO
,
"分页查询参数不能为空"
);
QueryWrapper
<
CustomProductFactoryPriceRelEntity
>
queryWrapper
=
new
QueryWrapper
<>();
// TODO 根据业务条件组装入参
IPage
<
CustomProductFactoryPriceRelEntity
>
page
=
customProductFactoryPriceRelDomainService
.
selectPage
(
queryWrapper
,
customProductFactoryPriceRelPageVO
);
return
page
.
convert
(
e
->
BeanMapper
.
mapper
().
convert
(
e
,
CustomProductFactoryPriceRelVO
.
class
));
return
page
.
convert
(
e
->
BeanMapper
.
mapper
().
convert
(
e
,
CustomProductFactoryPriceRel
Snake
VO
.
class
));
}
@Override
public
CustomProductFactoryPriceRelVO
info
(
Integer
id
)
{
public
CustomProductFactoryPriceRel
Snake
VO
info
(
Integer
id
)
{
CustomAsserts
.
nonNull
(
id
,
"主键id不能为空"
);
CustomProductFactoryPriceRelEntity
customProductFactoryPriceRel
=
customProductFactoryPriceRelDomainService
.
getById
(
id
);
return
BeanMapper
.
mapper
().
convert
(
customProductFactoryPriceRel
,
CustomProductFactoryPriceRelVO
.
class
);
return
BeanMapper
.
mapper
().
convert
(
customProductFactoryPriceRel
,
CustomProductFactoryPriceRel
Snake
VO
.
class
);
}
@Transactional
(
rollbackFor
=
Exception
.
class
)
@Override
public
void
save
(
CustomProductFactoryPriceRel
VO
customProductFactoryPriceRel
VO
)
{
CustomAsserts
.
nonNull
(
customProductFactoryPriceRelVO
,
"实体对象不能为空"
);
CustomProductFactoryPriceRelEntity
customProductFactoryPriceRelEntity
=
BeanMapper
.
mapper
().
convert
(
customProductFactoryPriceRelVO
,
CustomProductFactoryPriceRelEntity
.
class
);
public
void
save
(
CustomProductFactoryPriceRel
SnakeVO
customProductFactoryPriceRelSnake
VO
)
{
CustomAsserts
.
nonNull
(
customProductFactoryPriceRel
Snake
VO
,
"实体对象不能为空"
);
CustomProductFactoryPriceRelEntity
customProductFactoryPriceRelEntity
=
BeanMapper
.
mapper
().
convert
(
customProductFactoryPriceRel
Snake
VO
,
CustomProductFactoryPriceRelEntity
.
class
);
try
{
customProductFactoryPriceRelDomainService
.
save
(
customProductFactoryPriceRelEntity
);
}
catch
(
DuplicateKeyException
e
)
{
...
...
@@ -75,9 +75,9 @@ public class CustomProductFactoryPriceRelServiceImpl implements CustomProductFac
@Transactional
(
rollbackFor
=
Exception
.
class
)
@Override
public
void
updateById
(
CustomProductFactoryPriceRel
VO
customProductFactoryPriceRel
VO
)
{
CustomAsserts
.
nonNull
(
customProductFactoryPriceRelVO
,
"实体对象不能为空"
);
CustomProductFactoryPriceRelEntity
customProductFactoryPriceRel
=
BeanMapper
.
mapper
().
convert
(
customProductFactoryPriceRelVO
,
CustomProductFactoryPriceRelEntity
.
class
);
public
void
updateById
(
CustomProductFactoryPriceRel
SnakeVO
customProductFactoryPriceRelSnake
VO
)
{
CustomAsserts
.
nonNull
(
customProductFactoryPriceRel
Snake
VO
,
"实体对象不能为空"
);
CustomProductFactoryPriceRelEntity
customProductFactoryPriceRel
=
BeanMapper
.
mapper
().
convert
(
customProductFactoryPriceRel
Snake
VO
,
CustomProductFactoryPriceRelEntity
.
class
);
try
{
customProductFactoryPriceRelDomainService
.
updateById
(
customProductFactoryPriceRel
);
}
catch
(
DuplicateKeyException
e
)
{
...
...
custom-server-app/src/main/java/com/jomalls/custom/app/service/impl/CustomProductInfoServiceImpl.java
View file @
43164dcf
...
...
@@ -4,10 +4,11 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.extension.plugins.pagination.Page
;
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.enums.TemplateStatus
;
import
com.jomalls.custom.app.exception.ServiceException
;
import
com.jomalls.custom.app.service.CustomProductInfoService
;
import
com.jomalls.custom.app.service.DiyUserService
;
import
com.jomalls.custom.app.service.SysBillRuleService
;
...
...
@@ -20,11 +21,12 @@ 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
com.jomalls.custom.security.LoginUser
;
import
com.jomalls.custom.security.SecurityUtils
;
import
lombok.RequiredArgsConstructor
;
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.stereotype.Service
;
import
org.springframework.transaction.support.TransactionTemplate
;
...
...
@@ -91,7 +93,17 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
return
page
.
convert
(
e
->
BeanMapper
.
mapper
().
convert
(
e
,
CustomProductInfoVO
.
class
));
}
/** 标准分页查询条件构建(非 ERP) */
private
void
toQueryWrapper
(
CustomProductInfoSnakeDTO
param
,
QueryWrapper
<
CustomProductInfoEntity
>
queryWrapper
)
{
toQueryWrapper
(
param
,
queryWrapper
,
false
);
}
/**
* 查询条件构建
*
* @param isErp true=ERP 模式(title 双字段 OR 搜索,processing 支持 2=IS NULL,跳过 DIY/黑名单过滤)
*/
private
void
toQueryWrapper
(
CustomProductInfoSnakeDTO
param
,
QueryWrapper
<
CustomProductInfoEntity
>
queryWrapper
,
boolean
isErp
)
{
// 分类层级过滤
if
(
param
.
getCategory_id
()
!=
null
)
{
List
<
BaseCategoryInfoModel
>
cateList
=
saasAdminService
.
getAllList
();
...
...
@@ -120,10 +132,14 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
}
// 是否九猫处理过滤
if
(
param
.
getProcessing
())
{
queryWrapper
.
eq
(
"processing"
,
1
);
}
else
{
queryWrapper
.
eq
(
"processing"
,
0
);
Integer
processing
=
param
.
getProcessing
();
if
(
processing
!=
null
)
{
if
(
processing
==
2
)
{
// ERP: processing=2 → IS NULL(对齐 TS:626)
queryWrapper
.
isNull
(
"processing"
);
}
else
{
queryWrapper
.
eq
(
"processing"
,
processing
);
}
}
// 直接列等值过滤
...
...
@@ -148,8 +164,13 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
if
(
param
.
getName
()
!=
null
&&
!
param
.
getName
().
isEmpty
())
{
queryWrapper
.
like
(
"name"
,
param
.
getName
());
}
// ERP: title 关键词同时搜索 name 和 title 两列(对齐 TS:610-611)
if
(
param
.
getTitle
()
!=
null
&&
!
param
.
getTitle
().
isEmpty
())
{
queryWrapper
.
like
(
"title"
,
param
.
getTitle
());
if
(
isErp
)
{
queryWrapper
.
and
(
w
->
w
.
like
(
"name"
,
param
.
getTitle
()).
or
().
like
(
"title"
,
param
.
getTitle
()));
}
else
{
queryWrapper
.
like
(
"title"
,
param
.
getTitle
());
}
}
// 工厂过滤(factory_id 是 custom_product_info 的直接列)
...
...
@@ -157,13 +178,19 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
queryWrapper
.
in
(
"factory_id"
,
param
.
getFactoryIds
());
}
// DIY 用户过滤 与 黑名单过滤:需要查询关联表取 productId 列表,两者取交集
// DIY 用户过滤与黑名单过滤(仅标准分页使用,ERP 走 native SQL 权限过滤)
if
(!
isErp
)
{
applyDiyAndBlacklistFilter
(
param
,
queryWrapper
);
}
}
/** DIY 用户与黑名单过滤(标准分页专用) */
private
void
applyDiyAndBlacklistFilter
(
CustomProductInfoSnakeDTO
param
,
QueryWrapper
<
CustomProductInfoEntity
>
queryWrapper
)
{
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
()
...
...
@@ -180,7 +207,6 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
.
distinct
()
.
collect
(
Collectors
.
toList
());
if
(
idListFromDiyUser
.
isEmpty
())
{
// 该 DIY 用户未绑定任何商品,直接返回空结果
queryWrapper
.
eq
(
"id"
,
-
1
);
}
}
...
...
@@ -196,13 +222,12 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
.
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
);
// 交集为空,返回空结果
queryWrapper
.
eq
(
"id"
,
-
1
);
}
else
{
queryWrapper
.
in
(
"id"
,
intersection
);
}
...
...
@@ -211,8 +236,6 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
}
else
if
(
idListFromBlacklist
!=
null
)
{
queryWrapper
.
in
(
"id"
,
idListFromBlacklist
);
}
queryWrapper
.
orderByDesc
(
"id"
);
}
@Override
...
...
@@ -229,92 +252,121 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
computeProductPricesFromItems
(
dto
);
// 3. 编程式事务写入所有表
transactionTemplate
.
executeWithoutResult
(
status
->
{
// 3a. 保存主表
CustomProductInfoEntity
entity
=
buildEntityFromSaveDTO
(
dto
,
sku
);
customProductInfoDomainService
.
save
(
entity
);
Integer
productId
=
entity
.
getId
();
log
.
debug
(
"[ saveFull ] 主表保存成功, productId: {}"
,
productId
);
try
{
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
);
// 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
);
// 3c. 保存工厂价格关联(利用 itemMap 替换 item_sku 为生成的 SKU 并填充 item_id)
saveFactoryPriceRels
(
dto
.
getFactoryPriceList
(),
productId
,
itemMap
);
// 3d. 保存产品-工厂关联(product_factory_rel 表)
saveFactoryRels
(
dto
.
getFactoryIds
(),
productId
);
// 3d. 保存产品-工厂关联(product_factory_rel 表)
saveFactoryRels
(
dto
.
getFactoryIds
(),
productId
);
// 3e. 保存 DIY 用户关联
//saveDiyUserRels(dto.getDiyUserIds(), productId);
// 3e. 保存 DIY 用户关联
//saveDiyUserRels(dto.getDiyUserIds(), productId);
// 3f. 保存工艺关联
saveCraftRels
(
dto
.
getCraftIds
(),
productId
);
// 3f. 保存工艺关联
saveCraftRels
(
dto
.
getCraftIds
(),
productId
);
// 3g. 保存价格区间关联
saveFactoryPriceIntervalRels
(
dto
.
getFactoryPriceIntervalList
(),
productId
);
// 3g. 保存价格区间关联
saveFactoryPriceIntervalRels
(
dto
.
getFactoryPriceIntervalList
(),
productId
);
// 3h. 保存图片(普通图 + 尺码图)
saveImages
(
dto
.
getImageList
(),
dto
.
getSizeList
(),
productId
);
// 3h. 保存图片(普通图 + 尺码图)
saveImages
(
dto
.
getImageList
(),
dto
.
getSizeList
(),
productId
);
// 3i. 保存备注(英文 + 中文)
saveRemarks
(
dto
.
getRemark
(),
dto
.
getCnRemark
(),
productId
);
// 3i. 保存备注(英文 + 中文)
saveRemarks
(
dto
.
getRemark
(),
dto
.
getCnRemark
(),
productId
);
// 3j. 保存属性(SKU 属性 + 普通属性)
saveProperties
(
dto
.
getSkuProperties
(),
dto
.
getNormalProperties
(),
productId
);
// 3j. 保存属性(SKU 属性 + 普通属性)
saveProperties
(
dto
.
getSkuProperties
(),
dto
.
getNormalProperties
(),
productId
);
// 3k. 保存仓库关联
saveWarehouseRels
(
dto
.
getWarehouseIds
(),
productId
);
// 3k. 保存仓库关联
saveWarehouseRels
(
dto
.
getWarehouseIds
(),
productId
);
// 3l. 事务内记录日志(对齐 TS:日志与数据写入同一事务)
saveLogTransaction
(
productId
,
"创建商品"
);
});
// 3l. 事务内记录日志(对齐 TS:日志与数据写入同一事务)
saveLogTransaction
(
productId
,
"创建商品"
);
});
}
catch
(
Exception
e
)
{
log
.
error
(
"[ saveFull ] 创建商品失败, sku: {}"
,
sku
,
e
);
throw
new
ServiceException
(
"创建商品失败,sku= "
+
sku
);
}
}
@Override
public
void
updateFull
(
CustomProductInfoUpdateDTO
dto
)
{
/*
CustomAsserts.nonNull(dto, "更新参数不能为空");
public
void
updateFull
(
CustomProductInfoUpdate
Snake
DTO
dto
)
{
CustomAsserts
.
nonNull
(
dto
,
"更新参数不能为空"
);
CustomAsserts
.
nonNull
(
dto
.
getId
(),
"商品 ID 不能为空"
);
transactionTemplate.executeWithoutResult(status -> {
Integer productId = dto.getId();
// 查询旧记录,校验商品存在性
CustomProductInfoEntity
oldEntity
=
customProductInfoDomainService
.
getById
(
dto
.
getId
());
if
(
oldEntity
==
null
)
{
throw
new
ServiceException
(
"商品信息不存在, id="
+
dto
.
getId
());
}
// 1. 更新主表
CustomProductInfoEntity entity = buildEntityFromSaveDTO(dto, null);
entity.setId(productId);
customProductInfoDomainService.updateById(entity);
// SKU属性类不能新增(对齐 TS:357-359)
if
((
oldEntity
.
getProperty2CateId
()
==
null
&&
dto
.
getProperty2_cate_id
()
!=
null
)
||
(
oldEntity
.
getProperty3CateId
()
==
null
&&
dto
.
getProperty3_cate_id
()
!=
null
))
{
throw
new
ServiceException
(
"SKU属性类不能新增"
);
}
// 2. 处理子项的增/删/改
if (dto.getProductChange() != null) {
handleItemChanges(dto.getProductChange(), productId);
}
// 将 sizeChange 合并到 imageChange 一起处理
CustomProductInfoUpdateSnakeDTO
.
ProductImageChangeDTO
mergedImageChange
=
mergeImageChanges
(
dto
.
getImageChange
(),
dto
.
getSizeChange
());
// 3. 处理工厂价格变更
if (dto.getProductFactoryPriceChange() != null) {
handleFactoryPriceChanges(dto.getProductFactoryPriceChange(), productId);
}
// 编程式事务写入所有表
try
{
transactionTemplate
.
executeWithoutResult
(
status
->
{
Integer
productId
=
oldEntity
.
getId
();
// 1. 更新主表(显式保留原 create_time)
CustomProductInfoEntity
entity
=
buildEntityFromSaveDTO
(
dto
,
null
);
entity
.
setId
(
productId
);
entity
.
setCreateTime
(
oldEntity
.
getCreateTime
());
customProductInfoDomainService
.
updateById
(
entity
);
// 2. 处理子项的增/删/改,返回新增子项的 SKU→Entity 映射
Map
<
String
,
CustomProductItemEntity
>
newItemMap
=
null
;
if
(
dto
.
getProductChange
()
!=
null
)
{
newItemMap
=
handleItemChanges
(
dto
.
getProductChange
(),
productId
,
dto
);
}
// 4. 销毁并重建属性
rebuildProperties(dto.getSkuProperties(), dto.getNormalProperties(), productId
);
// 2b. 批量同步所有子项的 product_no 和 print_type
syncItemsProductNoAndPrintType
(
productId
,
dto
.
getProduct_no
(),
dto
.
getPrint_type
()
);
// 5. 处理图片变更
if (dto.getProductImag
eChange() != null) {
handleImageChanges(dto.getProductImageChange(), productId, IMAGE_TYPE_NORMAL
);
}
if (dto.getProductSizeChange() != null) {
handleImageChanges(dto.getProductSizeChange(), productId, IMAGE_TYPE_SIZE);
}
// 3. 处理工厂价格变更(传入 newItemMap 用于回填 item_id)
if
(
dto
.
getFactoryPric
eChange
()
!=
null
)
{
handleFactoryPriceChanges
(
dto
.
getFactoryPriceChange
(),
productId
,
newItemMap
);
}
// 4. 销毁并重建属性
rebuildProperties
(
dto
.
getSkuProperties
(),
dto
.
getNormalProperties
(),
productId
);
// 6. 更新备注(先删后插)
updateRemarks(dto.getRemark(), dto.getCnRemark(), productId);
// 5. 处理图片变更(统一处理合并后的 imageChange)
if
(
mergedImageChange
!=
null
)
{
handleImageChanges
(
mergedImageChange
,
productId
);
}
// 6. 更新备注
updateRemarks
(
dto
.
getRemark
(),
dto
.
getCnRemark
(),
productId
);
// 7. 重建工厂/仓库/工艺/DIY用户/价格区间关联(先删后插)
rebuildRels(dto.getFactoryIds(), dto.getWarehouseIds(), dto.getCraftIds(),
dto.getDiyUserIds(), dto.getFactoryPriceIntervalList(), productId);
// 7. 重建工厂/仓库/工艺/DIY用户/价格区间关联(先删后插)
rebuildRels
(
dto
.
getFactoryIds
(),
dto
.
getWarehouseIds
(),
dto
.
getCraftIds
(),
dto
.
getDiyUserIds
(),
dto
.
getFactoryPriceIntervalList
(),
productId
);
// 8. 事务内记日志
saveLogTransaction(productId, "修改商品信息");
});*/
// 8. 事务内记日志
saveLogTransaction
(
productId
,
"修改商品信息"
);
});
}
catch
(
Exception
e
)
{
log
.
error
(
"[ updateFull ] 修改商品信息失败, productId: {}"
,
dto
.
getId
(),
e
);
throw
new
ServiceException
(
"修改商品信息失败, productId="
+
dto
.
getId
());
}
}
@Override
...
...
@@ -449,7 +501,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
/**
* 通过 AdminPropertyService 解析属性名称,填充到 FullVO 的 skuProperties / normalProperties 中
* <p>
* 对齐
TS:217-236 —
将数据库中存储的 property_id/value_id 转换为带名称的属性列表。
* 对齐
ts代码,
将数据库中存储的 property_id/value_id 转换为带名称的属性列表。
*
* @param properties 数据库原始属性记录
* @param fullVO 待填充的商品完整详情
...
...
@@ -469,7 +521,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
}
// 调用 saas admin API 获取属性定义(含 valueList)
List
<
BasePropertyModel
>
publicProperties
=
saasAdminService
.
getPropertyByIds
(
propIds
);
if
(
publicProperties
==
null
||
publicProperties
.
isEmpty
(
))
{
if
(
CollectionUtils
.
isEmpty
(
publicProperties
))
{
return
;
}
// 收集当前商品使用的 value_id
...
...
@@ -503,28 +555,34 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
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
);
try
{
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
());
});
saveLogBatch
(
"绑定客户"
,
dto
.
getProductIds
());
});
}
catch
(
Exception
e
)
{
log
.
error
(
"[ bindsDiyUser ] 绑定客户失败, productIds: {}"
,
dto
.
getProductIds
(),
e
);
throw
new
ServiceException
(
"绑定客户失败: "
+
e
.
getMessage
())
.
setDetailMessage
(
e
.
toString
());
}
}
@Override
...
...
@@ -532,28 +590,34 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
CustomAsserts
.
nonNull
(
dto
,
"黑名单参数不能为空"
);
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
);
try
{
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
());
});
saveLogBatch
(
"加入黑名单"
,
dto
.
getProductIds
());
});
}
catch
(
Exception
e
)
{
log
.
error
(
"[ addBlackList ] 加入黑名单失败, productIds: {}"
,
dto
.
getProductIds
(),
e
);
throw
new
ServiceException
(
"加入黑名单失败: "
+
e
.
getMessage
())
.
setDetailMessage
(
e
.
toString
());
}
}
@Override
...
...
@@ -621,7 +685,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
@Override
public
List
<
DbDiyVO
>
getErpBindsDiyById
(
Integer
id
,
String
userMark
,
String
namespace
)
{
CustomAsserts
.
nonNull
(
id
,
"商品 ID 不能为空"
);
// 1. 根据 userMark 或 namespace 查询用户
(对齐 TS:751-759)
// 1. 根据 userMark 或 namespace 查询用户
DbDiyUserEntity
user
;
if
(
StringUtils
.
isNotBlank
(
userMark
))
{
user
=
diyUserService
.
getByUserMark
(
userMark
);
...
...
@@ -631,57 +695,91 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
if
(
user
==
null
)
{
throw
new
ServiceException
(
"客户不存在"
);
}
// 2. 查询 product_template_info 获取 diy_id 列表
(对齐 TS:760-762)
// 2. 查询 product_template_info 获取 diy_id 列表
List
<
ProductTemplateInfoEntity
>
templates
=
productTemplateInfoDomainService
.
selectByProductId
(
id
);
if
(
templates
.
isEmpty
(
))
{
if
(
CollectionUtils
.
isEmpty
(
templates
))
{
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
>
diyIds
=
templates
.
stream
().
map
(
ProductTemplateInfoEntity:
:
getDiyId
)
.
filter
(
Objects:
:
nonNull
).
distinct
().
collect
(
Collectors
.
toList
());
// 3. 查询 db_diy,根据用户类型过滤状态
List
<
Integer
>
statusList
;
String
userName
=
user
.
getName
();
if
(
"demo"
.
equals
(
userName
))
{
statusList
=
Arrays
.
asList
(
TemplateStatus
.
SHELF_CODE
,
TemplateStatus
.
WAIT_SHELF
.
getCode
());
// demo 用户可见待上架
statusList
=
Arrays
.
asList
(
TemplateStatus
.
SHELF_CODE
,
TemplateStatus
.
WAIT_SHELF
.
getCode
());
}
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
())
{
List
<
DbDiyEntity
>
diys
=
dbDiyDomainService
.
list
(
new
LambdaQueryWrapper
<
DbDiyEntity
>()
.
in
(
DbDiyEntity:
:
getId
,
diyIds
).
in
(
DbDiyEntity:
:
getStatus
,
statusList
));
if
(
CollectionUtils
.
isEmpty
(
diys
))
{
return
Collections
.
emptyList
();
}
// 4. 权限过滤:user_ids / ban_user_ids
final
Integer
userId
=
user
.
getId
();
diys
=
diys
.
stream
().
filter
(
d
->
isUserAuthorized
(
d
.
getUserIds
(),
userId
))
.
filter
(
d
->
!
isUserBanned
(
d
.
getBanUserIds
(),
userId
)).
collect
(
Collectors
.
toList
());
if
(
CollectionUtils
.
isEmpty
(
diys
))
{
return
Collections
.
emptyList
();
}
// 4. 转换为 VO 并附带效果图(对齐 TS:783 include DbDiyXiaoguotu)
// 5. 批量查询所有效果图,按 diyId 分组(1 次 IN 查询替代 N 次逐条查询)
List
<
Integer
>
ids
=
diys
.
stream
().
map
(
DbDiyEntity:
:
getId
).
collect
(
Collectors
.
toList
());
Map
<
Integer
,
List
<
DbDiyXiaoguotuEntity
>>
xiaoguotuMap
=
dbDiyXiaoguotuDomainService
.
selectByDiyIds
(
ids
)
.
stream
().
collect
(
Collectors
.
groupingBy
(
DbDiyXiaoguotuEntity:
:
getDiyId
));
// 6. 转换为 VO 并附带效果图
return
diys
.
stream
().
map
(
diy
->
{
DbDiyVO
vo
=
BeanMapper
.
mapper
().
convert
(
diy
,
DbDiyVO
.
class
);
List
<
DbDiyXiaoguotuEntity
>
xiaoguotus
=
dbDiyXiaoguotuDomainService
.
selectByDiyId
(
diy
.
getId
());
// 将效果图数据附加到 VO(通过扩展方式,当前 DbDiyVO 无 xiaoguotu 字段,
// 如有需要可扩展 DbDiyVO)
List
<
DbDiyXiaoguotuEntity
>
xiaoguotus
=
xiaoguotuMap
.
getOrDefault
(
diy
.
getId
(),
Collections
.
emptyList
());
if
(
CollectionUtils
.
isNotEmpty
(
xiaoguotus
))
{
vo
.
setXiaoguotuList
(
xiaoguotus
.
stream
()
.
map
(
e
->
BeanMapper
.
mapper
().
convert
(
e
,
DbDiyXiaoguotuVO
.
class
)).
collect
(
Collectors
.
toList
()));
}
return
vo
;
}).
collect
(
Collectors
.
toList
());
}
/**
* 检查用户是否在授权名单中(对齐 TS:774-777 FIND_IN_SET)
* <p>
* user_ids 为 null 或空字符串 = 对所有人开放。
*/
private
boolean
isUserAuthorized
(
String
userIds
,
Integer
userId
)
{
if
(
StringUtils
.
isBlank
(
userIds
))
{
return
true
;
}
return
Arrays
.
asList
(
userIds
.
split
(
","
)).
contains
(
String
.
valueOf
(
userId
));
}
/**
* 检查用户是否在黑名单中(对齐 TS:778-781 FIND_IN_SET)
* <p>
* ban_user_ids 为 null 或空字符串 = 无人被禁止。
*/
private
boolean
isUserBanned
(
String
banUserIds
,
Integer
userId
)
{
if
(
StringUtils
.
isBlank
(
banUserIds
))
{
return
false
;
}
return
Arrays
.
asList
(
banUserIds
.
split
(
","
)).
contains
(
String
.
valueOf
(
userId
));
}
@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
());
return
emptyPage
(
param
);
}
}
// 2. 构建查询条件(复用 + ERP 特有逻辑)
// 2. 构建查询条件(ERP 模式:title OR 搜索,processing 支持 2=IS NULL,跳过 DIY/黑名单)
QueryWrapper
<
CustomProductInfoEntity
>
queryWrapper
=
new
QueryWrapper
<>();
toQueryWrapper
(
param
,
queryWrapper
);
toQueryWrapper
(
param
,
queryWrapper
,
true
);
// 3. DIY 模板过滤(对齐 TS:630-657)
if
(
StringUtils
.
isNotBlank
(
param
.
getDiySku
())
||
StringUtils
.
isNotBlank
(
param
.
getSource
())
||
param
.
getDiyId
()
!=
null
)
{
...
...
@@ -697,55 +795,82 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
}
List
<
DbDiyEntity
>
diys
=
dbDiyDomainService
.
list
(
diyWrapper
);
if
(
diys
.
isEmpty
())
{
return
new
com
.
baomidou
.
mybatisplus
.
extension
.
plugins
.
pagination
.
Page
<>(
param
.
getCurrent
(),
param
.
getSize
()
);
return
emptyPage
(
param
);
}
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
()
);
return
emptyPage
(
param
);
}
List
<
Integer
>
productIds
=
temps
.
stream
()
.
map
(
ProductTemplateInfoEntity:
:
getProductId
)
.
filter
(
pid
->
pid
!=
n
ull
)
.
filter
(
Objects:
:
nonN
ull
)
.
distinct
()
.
collect
(
Collectors
.
toList
());
if
(
productIds
.
isEmpty
())
{
return
new
com
.
baomidou
.
mybatisplus
.
extension
.
plugins
.
pagination
.
Page
<>(
param
.
getCurrent
(),
param
.
getSize
()
);
return
emptyPage
(
param
);
}
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
()
);
return
emptyPage
(
param
);
}
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
()
);
return
emptyPage
(
param
);
}
List
<
Integer
>
productIds
=
rels
.
stream
()
.
map
(
CustomProductWarehouseRelEntity:
:
getProductId
).
distinct
().
collect
(
Collectors
.
toList
());
queryWrapper
.
in
(
"id"
,
productIds
);
}
// 5. 执行分页查询
queryWrapper
.
orderByDesc
(
"id"
);
// 5. ERP 权限过滤(对齐 TS:686-706 — native SQL 黑名单排除 + 用户绑定过滤)
// 使用单次 JOIN 查询,避免 Java 层多次查询+set 操作,保证性能
if
(
user
!=
null
)
{
List
<
Integer
>
allowedIds
=
customProductInfoDomainService
.
selectIdsByErpPermission
(
user
.
getId
());
if
(
allowedIds
.
isEmpty
())
{
return
emptyPage
(
param
);
}
// MyBatis-Plus 多个 in("id", ...) 叠加 = SQL 层 AND 交集
queryWrapper
.
in
(
"id"
,
allowedIds
);
}
// 6. 排序(对齐 TS:716-721:sort IS NULL ASC, sort ASC, id DESC)
// MySQL 默认 ASC 时空值排最前,等价于 sort IS NULL ASC
queryWrapper
.
orderByAsc
(
"sort"
).
orderByDesc
(
"id"
);
// 7. 执行分页查询
IPage
<
CustomProductInfoEntity
>
page
=
customProductInfoDomainService
.
selectPage
(
queryWrapper
,
param
);
// 6. 转换并应用外部定价(对齐 TS:731-738)
// 预计算折扣率(放入 final 变量,供 lambda 使用)
// 8. 批量查询 DIY 上架状态(对齐 TS:723-738,单次 IN 查询避免 N+1)
List
<
CustomProductInfoEntity
>
rows
=
page
.
getRecords
();
final
Map
<
Integer
,
Boolean
>
diyShelfStatusMap
;
if
(!
rows
.
isEmpty
())
{
diyShelfStatusMap
=
batchQueryDiyShelfStatus
(
rows
);
}
else
{
diyShelfStatusMap
=
Collections
.
emptyMap
();
}
// 9. 预计算折扣率(对齐 TS:731-733 setProductExternalPrice)
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
));
...
...
@@ -754,10 +879,40 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
vo
.
setSalesPriceMax
(
vo
.
getSalesPriceMax
().
multiply
(
discountRate
).
setScale
(
2
,
RoundingMode
.
HALF_UP
));
}
}
// 标记绑定的 DIY 模板是否已上架
vo
.
setDiyShelfStatus
(
diyShelfStatusMap
.
getOrDefault
(
e
.
getDiyId
(),
false
));
return
vo
;
});
}
/** 返回空分页结果 */
private
IPage
<
CustomProductInfoVO
>
emptyPage
(
CustomProductInfoSnakeDTO
param
)
{
return
new
Page
<>(
param
.
getCurrent
(),
param
.
getSize
());
}
/**
* 批量查询 DIY 模板上架状态,返回 diyId → isShelf 映射
* <p>
* 对齐 TS:724-731,使用单次 IN 查询代替 N+1 逐条查询。
*/
private
Map
<
Integer
,
Boolean
>
batchQueryDiyShelfStatus
(
List
<
CustomProductInfoEntity
>
rows
)
{
List
<
Integer
>
diyIds
=
rows
.
stream
()
.
map
(
CustomProductInfoEntity:
:
getDiyId
)
.
filter
(
Objects:
:
nonNull
)
.
distinct
()
.
collect
(
Collectors
.
toList
());
if
(
diyIds
.
isEmpty
())
{
return
Collections
.
emptyMap
();
}
List
<
DbDiyEntity
>
diys
=
dbDiyDomainService
.
list
(
new
LambdaQueryWrapper
<
DbDiyEntity
>()
.
select
(
DbDiyEntity:
:
getId
,
DbDiyEntity:
:
getStatus
)
.
in
(
DbDiyEntity:
:
getId
,
diyIds
));
return
diys
.
stream
().
collect
(
Collectors
.
toMap
(
DbDiyEntity:
:
getId
,
d
->
d
.
getStatus
()
!=
null
&&
d
.
getStatus
().
equals
(
TemplateStatus
.
SHELF_CODE
)));
}
/**
* 从事务外的子项列表中计算主表的 factory_price / sales_price / sales_price_max
* <p>
...
...
@@ -857,18 +1012,18 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
* <p>
* 对齐 TS {@code save} 方法 132-139 行。
*/
private
void
saveFactoryPriceRels
(
List
<
FactoryPriceRelDTO
>
factoryPriceList
,
private
void
saveFactoryPriceRels
(
List
<
FactoryPriceRel
Snake
DTO
>
factoryPriceList
,
Integer
productId
,
Map
<
String
,
CustomProductItemEntity
>
itemMap
)
{
if
(
factoryPriceList
==
null
||
factoryPriceList
.
isEmpty
(
))
{
if
(
CollectionUtils
.
isEmpty
(
factoryPriceList
))
{
return
;
}
List
<
CustomProductFactoryPriceRelEntity
>
rels
=
new
ArrayList
<>();
for
(
FactoryPriceRelDTO
dto
:
factoryPriceList
)
{
for
(
FactoryPriceRel
Snake
DTO
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
.
getItem
S
ku
());
CustomProductItemEntity
item
=
itemMap
.
get
(
dto
.
getItem
_s
ku
());
if
(
item
!=
null
)
{
rel
.
setItemSku
(
item
.
getSku
());
// 替换为替换后的 SKU
rel
.
setItemId
(
item
.
getId
());
// 填充自增 ID
...
...
@@ -931,12 +1086,12 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
/**
* 保存价格区间关联
*/
private
void
saveFactoryPriceIntervalRels
(
List
<
FactoryPriceIntervalRelDTO
>
intervals
,
Integer
productId
)
{
private
void
saveFactoryPriceIntervalRels
(
List
<
FactoryPriceIntervalRel
Snake
DTO
>
intervals
,
Integer
productId
)
{
if
(
intervals
==
null
||
intervals
.
isEmpty
())
{
return
;
}
List
<
CustomProductFactoryPriceIntervalRelEntity
>
rels
=
intervals
.
stream
().
map
(
dto
->
{
CustomProductFactoryPriceIntervalRelEntity
rel
=
BeanMapper
.
mapper
().
convert
(
dto
,
CustomProductFactoryPriceIntervalRelEntity
.
class
);
CustomProductFactoryPriceIntervalRelEntity
rel
=
BeanMapper
.
snakeCase
().
convert
(
dto
,
CustomProductFactoryPriceIntervalRelEntity
.
class
);
rel
.
setProductId
(
productId
);
return
rel
;
}).
collect
(
Collectors
.
toList
());
...
...
@@ -951,7 +1106,6 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
CustomProductImageEntity
entity
=
BeanMapper
.
snakeCase
().
convert
(
dto
,
CustomProductImageEntity
.
class
);
entity
.
setProductId
(
productId
);
entity
.
setType
(
type
);
entity
.
setCreateTime
(
new
Date
());
return
entity
;
}).
collect
(
Collectors
.
toList
());
}
...
...
@@ -980,7 +1134,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
r
.
setRemark
(
remark
);
customProductRemarkDomainService
.
save
(
r
);
}
if
(
StringUtils
.
isNotBlank
(
r
emark
))
{
if
(
StringUtils
.
isNotBlank
(
cnR
emark
))
{
CustomProductCnRemarkEntity
cr
=
new
CustomProductCnRemarkEntity
();
cr
.
setProductId
(
productId
);
cr
.
setRemark
(
cnRemark
);
...
...
@@ -1068,55 +1222,154 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
/**
* 处理子项变更(增/删/改)
* <p>
* 对齐 TS update:407-420 — 新增子项逐条保存以获取自增 ID,
* 返回 SKU→Entity 映射供后续工厂价格 item_id 回填。
*
* @return 新增子项的原始 SKU → 已保存实体映射(含自增 ID)
*/
private
void
handleItemChanges
(
com
.
jomalls
.
custom
.
app
.
dto
.
ProductChangeDTO
change
,
Integer
productId
)
{
private
Map
<
String
,
CustomProductItemEntity
>
handleItemChanges
(
ProductChangeSnakeDTO
change
,
Integer
productId
,
CustomProductInfoSnakeDTO
dto
)
{
Map
<
String
,
CustomProductItemEntity
>
newItemMap
=
new
HashMap
<>();
if
(
change
==
null
)
{
return
;
return
newItemMap
;
}
// 删除
if
(
change
.
getRemoveList
()
!=
null
&&
!
change
.
getRemoveList
().
isEmpty
(
))
{
if
(
CollectionUtils
.
isNotEmpty
(
change
.
getRemoveList
()
))
{
customProductItemDomainService
.
removeByIds
(
change
.
getRemoveList
());
}
// 修改
if
(
change
.
getUpdateList
()
!=
null
)
{
for
(
var
itemDTO
:
change
.
getUpdateList
())
{
CustomProductItemEntity
item
=
BeanMapper
.
mapper
().
convert
(
itemDTO
,
CustomProductItemEntity
.
class
);
if
(
CollectionUtils
.
isNotEmpty
(
change
.
getUpdateList
())
)
{
for
(
CustomProductItemSnakeDTO
itemDTO
:
change
.
getUpdateList
())
{
CustomProductItemEntity
item
=
BeanMapper
.
snakeCase
().
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
);
// 新增
(逐条保存以获取自增 ID,构建 SKU→Entity 映射)
if
(
CollectionUtils
.
isNotEmpty
(
change
.
getAddList
())
)
{
for
(
CustomProductItemSnakeDTO
itemDTO
:
change
.
getAddList
())
{
CustomProductItemEntity
item
=
BeanMapper
.
snakeCase
().
convert
(
itemDTO
,
CustomProductItemEntity
.
class
);
item
.
setProductId
(
productId
);
// 同步主表的 print_type 和 product_no 到新子项
item
.
setPrintType
(
dto
.
getPrint_type
());
item
.
setProductNo
(
dto
.
getProduct_no
());
customProductItemDomainService
.
save
(
item
);
newItemMap
.
put
(
itemDTO
.
getSku
(),
item
);
}
}
return
newItemMap
;
}
/**
* 批量同步所有子项的 product_no 和 print_type
* <p>
* 当主表的 product_no 或 print_type 变更时,统一更新该商品下所有子项的对应字段。
*/
private
void
syncItemsProductNoAndPrintType
(
Integer
productId
,
String
productNo
,
Integer
printType
)
{
LambdaUpdateWrapper
<
CustomProductItemEntity
>
wrapper
=
new
LambdaUpdateWrapper
<>();
wrapper
.
eq
(
CustomProductItemEntity:
:
getProductId
,
productId
);
if
(
productNo
!=
null
)
{
wrapper
.
set
(
CustomProductItemEntity:
:
getProductNo
,
productNo
);
}
if
(
printType
!=
null
)
{
wrapper
.
set
(
CustomProductItemEntity:
:
getPrintType
,
printType
);
}
customProductItemDomainService
.
update
(
wrapper
);
}
/**
* 将 sizeChange 合并到 imageChange(对齐 TS:369-379)
* <p>
* sizeChange 的 addList/updateList 设置 type=1 后合并到 imageChange,
* removeList 也合并,最终统一处理。
*/
private
CustomProductInfoUpdateSnakeDTO
.
ProductImageChangeDTO
mergeImageChanges
(
CustomProductInfoUpdateSnakeDTO
.
ProductImageChangeDTO
imageChange
,
CustomProductInfoUpdateSnakeDTO
.
ProductImageChangeDTO
sizeChange
)
{
List
<
CustomProductImageSnakeDTO
>
addList
=
new
ArrayList
<>();
List
<
CustomProductImageSnakeDTO
>
updateList
=
new
ArrayList
<>();
List
<
Integer
>
removeList
=
new
ArrayList
<>();
// 按类型收集,对齐
collectFromSource
(
imageChange
,
IMAGE_TYPE_NORMAL
,
addList
,
updateList
,
removeList
);
collectFromSource
(
sizeChange
,
IMAGE_TYPE_SIZE
,
addList
,
updateList
,
removeList
);
// 无任何变更数据时直接返回 null
if
(
CollectionUtils
.
isEmpty
(
addList
)
&&
CollectionUtils
.
isEmpty
(
updateList
)
&&
CollectionUtils
.
isEmpty
(
removeList
))
{
return
null
;
}
CustomProductInfoUpdateSnakeDTO
.
ProductImageChangeDTO
merged
=
new
CustomProductInfoUpdateSnakeDTO
.
ProductImageChangeDTO
();
merged
.
setAddList
(
CollectionUtils
.
isNotEmpty
(
addList
)
?
addList
:
null
);
merged
.
setUpdateList
(
CollectionUtils
.
isNotEmpty
(
updateList
)
?
updateList
:
null
);
merged
.
setRemoveList
(
CollectionUtils
.
isNotEmpty
(
removeList
)
?
removeList
:
null
);
return
merged
;
}
/**
* 将 source 中的图片变更按指定 type 写入目标集合
* <p>
* 副作用:会直接修改 source 中各元素的 type 字段(与 TS 行为一致)。
*/
private
void
collectFromSource
(
CustomProductInfoUpdateSnakeDTO
.
ProductImageChangeDTO
source
,
int
type
,
List
<
CustomProductImageSnakeDTO
>
addTarget
,
List
<
CustomProductImageSnakeDTO
>
updateTarget
,
List
<
Integer
>
removeTarget
)
{
if
(
source
==
null
)
{
return
;
}
if
(
CollectionUtils
.
isNotEmpty
(
source
.
getAddList
()))
{
source
.
getAddList
().
forEach
(
item
->
item
.
setType
(
type
));
addTarget
.
addAll
(
source
.
getAddList
());
}
if
(
CollectionUtils
.
isNotEmpty
(
source
.
getUpdateList
()))
{
source
.
getUpdateList
().
forEach
(
item
->
item
.
setType
(
type
));
updateTarget
.
addAll
(
source
.
getUpdateList
());
}
if
(
CollectionUtils
.
isNotEmpty
(
source
.
getRemoveList
()))
{
removeTarget
.
addAll
(
source
.
getRemoveList
());
}
}
/**
* 处理工厂价格变更(增/删/改)
* <p>
* 对齐 TS update:422-430 — 顺序:新增 → 修改 → 删除。
* 新增时从 newItemMap 回填 item_id(对齐 TS:413-416)。
*/
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
);
CustomProductInfoUpdateSnakeDTO
.
ProductFactoryPriceChangeDTO
change
,
Integer
productId
,
Map
<
String
,
CustomProductItemEntity
>
newItemMap
)
{
// 新增(对齐 TS 先新增)
if
(
CollectionUtils
.
isNotEmpty
(
change
.
getAddList
()))
{
for
(
FactoryPriceRelSnakeDTO
dto
:
change
.
getAddList
())
{
CustomProductFactoryPriceRelEntity
rel
=
BeanMapper
.
snakeCase
().
convert
(
dto
,
CustomProductFactoryPriceRelEntity
.
class
);
rel
.
setProductId
(
productId
);
customProductFactoryPriceRelDomainService
.
updateById
(
rel
);
// 从新增子项映射中回填 item_id 和 item_sku(对齐 TS:413-416)
if
(
newItemMap
!=
null
&&
StringUtils
.
isNotBlank
(
dto
.
getItem_sku
()))
{
CustomProductItemEntity
newItem
=
newItemMap
.
get
(
dto
.
getItem_sku
());
if
(
newItem
!=
null
)
{
rel
.
setItemId
(
newItem
.
getId
());
rel
.
setItemSku
(
newItem
.
getSku
());
}
}
customProductFactoryPriceRelDomainService
.
save
(
rel
);
}
}
if
(
change
.
getAddList
()
!=
null
)
{
for
(
var
dto
:
change
.
getAddList
())
{
CustomProductFactoryPriceRelEntity
rel
=
BeanMapper
.
mapper
().
convert
(
dto
,
CustomProductFactoryPriceRelEntity
.
class
);
// 修改
if
(
CollectionUtils
.
isNotEmpty
(
change
.
getUpdateList
()))
{
for
(
FactoryPriceRelSnakeDTO
dto
:
change
.
getUpdateList
())
{
CustomProductFactoryPriceRelEntity
rel
=
BeanMapper
.
snakeCase
().
convert
(
dto
,
CustomProductFactoryPriceRelEntity
.
class
);
rel
.
setProductId
(
productId
);
customProductFactoryPriceRelDomainService
.
save
(
rel
);
customProductFactoryPriceRelDomainService
.
updateById
(
rel
);
}
}
// 删除
if
(
CollectionUtils
.
isNotEmpty
(
change
.
getRemoveList
()))
{
customProductFactoryPriceRelDomainService
.
removeByIds
(
change
.
getRemoveList
());
}
}
/**
...
...
@@ -1134,46 +1387,75 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
}
/**
* 处理图片变更
* 处理图片变更(增/删/改)
* <p>
* 图片的 type 已在 mergeImageChanges 中预设,此处不再注入。
*/
private
void
handleImageChanges
(
CustomProductInfoUpdateDTO
.
ProductImageChangeDTO
change
,
Integer
productId
,
Integer
type
)
{
if
(
change
.
getRemoveList
()
!=
null
&&
!
change
.
getRemoveList
().
isEmpty
())
{
private
void
handleImageChanges
(
CustomProductInfoUpdateSnakeDTO
.
ProductImageChangeDTO
change
,
Integer
productId
)
{
if
(
change
==
null
)
{
return
;
}
// 删除
if
(
CollectionUtils
.
isNotEmpty
(
change
.
getRemoveList
()))
{
customProductImageDomainService
.
removeByIds
(
change
.
getRemoveList
());
}
if
(
change
.
getAddList
()
!=
null
)
{
for
(
var
dto
:
change
.
getAddList
())
{
CustomProductImageEntity
img
=
BeanMapper
.
mapper
().
convert
(
dto
,
CustomProductImageEntity
.
class
);
// 修改
if
(
CollectionUtils
.
isNotEmpty
(
change
.
getUpdateList
()))
{
for
(
CustomProductImageSnakeDTO
dto
:
change
.
getUpdateList
())
{
CustomProductImageEntity
img
=
BeanMapper
.
snakeCase
().
convert
(
dto
,
CustomProductImageEntity
.
class
);
img
.
setProductId
(
productId
);
customProductImageDomainService
.
updateById
(
img
);
}
}
// 新增
if
(
CollectionUtils
.
isNotEmpty
(
change
.
getAddList
()))
{
for
(
CustomProductImageSnakeDTO
dto
:
change
.
getAddList
())
{
CustomProductImageEntity
img
=
BeanMapper
.
snakeCase
().
convert
(
dto
,
CustomProductImageEntity
.
class
);
img
.
setProductId
(
productId
);
img
.
setType
(
type
);
customProductImageDomainService
.
save
(
img
);
}
}
}
/**
* 更新备注(先删后插)
*/
* 更新备注
* @param remark : 英文备注
* @param cnRemark : 中文备注
* @param productId : 产品 ID
*/
private
void
updateRemarks
(
String
remark
,
String
cnRemark
,
Integer
productId
)
{
// 英文备注
customProductRemarkDomainService
.
remove
(
new
LambdaQueryWrapper
<
CustomProductRemarkEntity
>()
.
eq
(
CustomProductRemarkEntity:
:
getProductId
,
productId
));
if
(
remark
!=
null
&&
!
remark
.
isEmpty
())
{
// 英文备注,不为空
if
(
StringUtils
.
isNotBlank
(
remark
))
{
// productId是唯一索引,先查询,如果有则更新,没有则插入
LambdaQueryWrapper
<
CustomProductRemarkEntity
>
enWrapper
=
new
LambdaQueryWrapper
<
CustomProductRemarkEntity
>()
.
eq
(
CustomProductRemarkEntity:
:
getProductId
,
productId
);
CustomProductRemarkEntity
enEntity
=
customProductRemarkDomainService
.
getOne
(
enWrapper
);
CustomProductRemarkEntity
r
=
new
CustomProductRemarkEntity
();
r
.
setProductId
(
productId
);
r
.
setRemark
(
remark
);
customProductRemarkDomainService
.
save
(
r
);
if
(
enEntity
!=
null
)
{
r
.
setId
(
enEntity
.
getId
());
customProductRemarkDomainService
.
updateById
(
r
);
}
else
{
customProductRemarkDomainService
.
save
(
r
);
}
}
// 中文备注
customProductCnRemarkDomainService
.
remove
(
new
LambdaQueryWrapper
<
CustomProductCnRemarkEntity
>()
.
eq
(
CustomProductCnRemarkEntity:
:
getProductId
,
productId
));
if
(
cnRemark
!=
null
&&
!
cnRemark
.
isEmpty
())
{
// 中文备注,不为空
if
(
StringUtils
.
isNotBlank
(
cnRemark
))
{
// productId是唯一索引,先查询,如果有则更新,没有则插入
LambdaQueryWrapper
<
CustomProductCnRemarkEntity
>
cnWrapper
=
new
LambdaQueryWrapper
<
CustomProductCnRemarkEntity
>()
.
eq
(
CustomProductCnRemarkEntity:
:
getProductId
,
productId
);
CustomProductCnRemarkEntity
cnEntity
=
customProductCnRemarkDomainService
.
getOne
(
cnWrapper
);
CustomProductCnRemarkEntity
cr
=
new
CustomProductCnRemarkEntity
();
cr
.
setProductId
(
productId
);
cr
.
setRemark
(
cnRemark
);
customProductCnRemarkDomainService
.
save
(
cr
);
if
(
cnEntity
!=
null
)
{
cr
.
setId
(
cnEntity
.
getId
());
customProductCnRemarkDomainService
.
updateById
(
cr
);
}
else
{
customProductCnRemarkDomainService
.
save
(
cr
);
}
}
}
...
...
@@ -1181,8 +1463,8 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
* 重建关联关系(先删后插)
*/
private
void
rebuildRels
(
List
<
Integer
>
factoryIds
,
List
<
Integer
>
warehouseIds
,
List
<
Integer
>
craftIds
,
List
<
Integer
>
diyUserIds
,
List
<
FactoryPriceIntervalRel
DTO
>
intervals
,
Integer
productId
)
{
List
<
Integer
>
craftIds
,
List
<
Integer
>
diyUserIds
,
List
<
FactoryPriceIntervalRelSnake
DTO
>
intervals
,
Integer
productId
)
{
// 产品-工厂关联
productFactoryRelDomainService
.
remove
(
new
LambdaQueryWrapper
<
ProductFactoryRelEntity
>()
...
...
@@ -1214,17 +1496,16 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
saveFactoryPriceIntervalRels
(
intervals
,
productId
);
}
// -------- 日志辅助方法 --------
/**
* 事务内记录日志
*/
private
void
saveLogTransaction
(
Integer
productId
,
String
description
)
{
LoginUser
loginUser
=
SecurityUtils
.
getLoginUser
();
LogCustomProductEntity
logEntry
=
new
LogCustomProductEntity
();
logEntry
.
setProductId
(
productId
);
logEntry
.
setDescription
(
description
);
logEntry
.
setEmployeeId
(
-
1
);
logEntry
.
setEmployeeAccount
(
"系统"
);
logEntry
.
setEmployeeId
(
loginUser
.
getUserId
()
);
logEntry
.
setEmployeeAccount
(
loginUser
.
getUsername
()
);
logCustomProductDomainService
.
save
(
logEntry
);
}
...
...
@@ -1232,13 +1513,14 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
* 批量记录产品日志
*/
private
void
saveLogBatch
(
String
action
,
List
<
Integer
>
productIds
)
{
LoginUser
loginUser
=
SecurityUtils
.
getLoginUser
();
List
<
LogCustomProductEntity
>
logs
=
new
ArrayList
<>();
for
(
Integer
productId
:
productIds
)
{
LogCustomProductEntity
logEntry
=
new
LogCustomProductEntity
();
logEntry
.
setProductId
(
productId
);
logEntry
.
setDescription
(
action
);
logEntry
.
setEmployeeId
(
-
1
);
logEntry
.
setEmployeeAccount
(
"系统"
);
logEntry
.
setEmployeeId
(
loginUser
.
getUserId
()
);
logEntry
.
setEmployeeAccount
(
loginUser
.
getUsername
()
);
logs
.
add
(
logEntry
);
}
logCustomProductDomainService
.
saveBatch
(
logs
);
...
...
@@ -1294,7 +1576,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
.
collect
(
Collectors
.
toList
()));
// 工厂价格
fullVO
.
setFactoryPriceList
(
factoryPrices
.
stream
()
.
map
(
e
->
BeanMapper
.
mapper
().
convert
(
e
,
CustomProductFactoryPriceRel
VO
.
class
))
.
map
(
e
->
BeanMapper
.
snakeCase
().
convert
(
e
,
CustomProductFactoryPriceRelSnake
VO
.
class
))
.
collect
(
Collectors
.
toList
()));
// 关联 ID 列表
...
...
custom-server-app/src/main/java/com/jomalls/custom/app/service/impl/DiyUserServiceImpl.java
View file @
43164dcf
...
...
@@ -70,9 +70,9 @@ public class DiyUserServiceImpl implements DiyUserService {
// 工厂价格关联的 sales_price(对齐 TS:348-352)
if
(
vo
.
getFactoryPriceList
()
!=
null
)
{
for
(
CustomProductFactoryPriceRelVO
fp
:
vo
.
getFactoryPriceList
())
{
if
(
fp
.
getSales
P
rice
()
!=
null
)
{
fp
.
setSales
Price
(
fp
.
getSalesP
rice
().
multiply
(
discountRate
).
setScale
(
2
,
RoundingMode
.
HALF_UP
));
for
(
CustomProductFactoryPriceRel
Snake
VO
fp
:
vo
.
getFactoryPriceList
())
{
if
(
fp
.
getSales
_p
rice
()
!=
null
)
{
fp
.
setSales
_price
(
fp
.
getSales_p
rice
().
multiply
(
discountRate
).
setScale
(
2
,
RoundingMode
.
HALF_UP
));
}
}
}
...
...
custom-server-app/src/main/java/com/jomalls/custom/app/vo/CustomProductFactoryPriceRelVO.java
→
custom-server-app/src/main/java/com/jomalls/custom/app/vo/CustomProductFactoryPriceRel
Snake
VO.java
View file @
43164dcf
...
...
@@ -22,7 +22,7 @@ import java.util.Date;
@NoArgsConstructor
@AllArgsConstructor
@Schema
(
description
=
"VO"
)
public
class
CustomProductFactoryPriceRelVO
implements
Serializable
{
public
class
CustomProductFactoryPriceRel
Snake
VO
implements
Serializable
{
@Serial
private
static
final
long
serialVersionUID
=
1L
;
...
...
@@ -36,61 +36,61 @@ public class CustomProductFactoryPriceRelVO implements Serializable {
* custom_product_info表id
*/
@Schema
(
description
=
"custom_product_info表id"
)
private
Integer
product
I
d
;
private
Integer
product
_i
d
;
/**
* custom_product_item表id
*/
@Schema
(
description
=
"custom_product_item表id"
)
private
Integer
item
I
d
;
private
Integer
item
_i
d
;
/**
* custom_product_item表sku
*/
@Schema
(
description
=
"custom_product_item表sku"
)
private
String
item
S
ku
;
private
String
item
_s
ku
;
/**
* db_diy表id
*/
@Schema
(
description
=
"db_diy表id"
)
private
Integer
factory
I
d
;
private
Integer
factory
_i
d
;
/**
* 工厂价格
*/
@Schema
(
description
=
"工厂价格"
)
private
BigDecimal
factory
P
rice
;
private
BigDecimal
factory
_p
rice
;
/**
* 销售价
*/
@Schema
(
description
=
"销售价"
)
private
BigDecimal
sales
P
rice
;
private
BigDecimal
sales
_p
rice
;
/**
* 工厂币种
*/
@Schema
(
description
=
"工厂币种"
)
private
String
factory
CurrencyC
ode
;
private
String
factory
_currency_c
ode
;
/**
* 售卖币种
*/
@Schema
(
description
=
"售卖币种"
)
private
String
sales
CurrencyC
ode
;
private
String
sales
_currency_c
ode
;
/**
*
*/
@Schema
(
description
=
""
)
private
Date
create
T
ime
;
private
Date
create
_t
ime
;
/**
*
*/
@Schema
(
description
=
""
)
private
Date
update
T
ime
;
private
Date
update
_t
ime
;
}
custom-server-app/src/main/java/com/jomalls/custom/app/vo/CustomProductInfoSnakeVO.java
View file @
43164dcf
...
...
@@ -153,7 +153,7 @@ public class CustomProductInfoSnakeVO implements Serializable {
private
ProductRemarkVO
productCnRemark
;
@Schema
(
description
=
"工厂价格关联列表"
)
private
List
<
CustomProductFactoryPriceRelVO
>
factoryPriceList
;
private
List
<
CustomProductFactoryPriceRel
Snake
VO
>
factoryPriceList
;
@Schema
(
description
=
"尺码图片列表(type=1)"
)
private
List
<
CustomProductImageSnakeVO
>
sizeList
;
...
...
custom-server-app/src/main/java/com/jomalls/custom/app/vo/CustomProductInfoVO.java
View file @
43164dcf
...
...
@@ -10,6 +10,7 @@ import java.io.Serial;
import
java.io.Serializable
;
import
java.math.BigDecimal
;
import
java.util.Date
;
import
java.util.List
;
/**
* Model
...
...
@@ -242,5 +243,8 @@ public class CustomProductInfoVO implements Serializable {
@Schema
(
description
=
"默认模SKU"
)
private
String
diySku
;
@Schema
(
description
=
"绑定的DIY模板是否已上架(ERP专用)"
)
private
Boolean
diyShelfStatus
;
}
custom-server-app/src/main/java/com/jomalls/custom/app/vo/DbDiyVO.java
View file @
43164dcf
...
...
@@ -9,6 +9,7 @@ import java.io.Serial;
import
java.io.Serializable
;
import
java.math.BigDecimal
;
import
java.util.Date
;
import
java.util.List
;
/**
* Model
...
...
@@ -319,5 +320,8 @@ public class DbDiyVO implements Serializable {
@Schema
(
description
=
"模备注"
)
private
String
diyRemark
;
@Schema
(
description
=
"效果图列表"
)
private
List
<
DbDiyXiaoguotuVO
>
xiaoguotuList
;
}
custom-server-app/src/main/java/com/jomalls/custom/app/vo/DbDiyXiaoguotuVO.java
0 → 100644
View file @
43164dcf
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
;
/**
* DIY 效果图 VO
*
* @author Lizh
* @date 2026-06-10
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema
(
description
=
"DIY 效果图"
)
public
class
DbDiyXiaoguotuVO
implements
Serializable
{
@Serial
private
static
final
long
serialVersionUID
=
1L
;
@Schema
(
description
=
"主键"
)
private
Integer
id
;
@Schema
(
description
=
"标题"
)
private
String
title
;
@Schema
(
description
=
"排序"
)
private
Integer
idx
;
@Schema
(
description
=
"效果图主图"
)
private
String
imgUrl
;
@Schema
(
description
=
"PSD 链接"
)
private
String
psdUrl
;
@Schema
(
description
=
"颜色 ID"
)
private
Integer
colorId
;
@Schema
(
description
=
"宽度"
)
private
Float
width
;
@Schema
(
description
=
"高度"
)
private
Float
height
;
@Schema
(
description
=
"分辨率"
)
private
Integer
dpi
;
@Schema
(
description
=
"关联的 DIY 模板 ID"
)
private
Integer
diyId
;
@Schema
(
description
=
"状态:1 正常 0 禁用"
)
private
Integer
status
;
}
custom-server-domain/src/main/java/com/jomalls/custom/dal/entity/LogCustomProductEntity.java
View file @
43164dcf
...
...
@@ -37,7 +37,7 @@ public class LogCustomProductEntity implements Serializable {
* 操作人id
*/
@TableField
(
"employee_id"
)
private
Integer
employeeId
;
private
Long
employeeId
;
/**
* 操作人账号
*/
...
...
custom-server-domain/src/main/java/com/jomalls/custom/dal/mapper/CustomProductInfoMapper.java
View file @
43164dcf
...
...
@@ -5,6 +5,8 @@ import com.jomalls.custom.mapper.BaseMapper;
import
org.apache.ibatis.annotations.Mapper
;
import
org.apache.ibatis.annotations.Param
;
import
java.util.List
;
/**
* @author Lizh
* @version 0.01
...
...
@@ -21,4 +23,16 @@ public interface CustomProductInfoMapper extends BaseMapper<CustomProductInfoEnt
* @return 商品实体,未找到返回 null
*/
CustomProductInfoEntity
selectBySku
(
@Param
(
"sku"
)
String
sku
);
/**
* ERP 权限过滤:查询指定用户可见的商品 ID 列表
* <p>
* 对齐 TS erpPage 中的 native SQL(TS:686-706)。
* 产品可见条件:(用户已绑定 OR 产品未绑定任何人) AND 用户未被拉黑。
* 使用单次 JOIN 查询避免 N+1 性能问题。
*
* @param userId 当前用户 ID
* @return 符合条件的商品 ID 列表(已去重)
*/
List
<
Integer
>
selectIdsByErpPermission
(
@Param
(
"userId"
)
Integer
userId
);
}
custom-server-domain/src/main/java/com/jomalls/custom/domain/service/CustomProductInfoDomainService.java
View file @
43164dcf
...
...
@@ -3,6 +3,8 @@ package com.jomalls.custom.domain.service;
import
com.jomalls.custom.dal.entity.CustomProductInfoEntity
;
import
com.jomalls.custom.service.IBaseService
;
import
java.util.List
;
/**
* @author Lizh
* @version 0.01
...
...
@@ -18,5 +20,13 @@ public interface CustomProductInfoDomainService extends IBaseService<CustomProdu
* @return 商品实体,未找到返回 null
*/
CustomProductInfoEntity
getBySku
(
String
sku
);
/**
* ERP 权限过滤:查询指定用户可见的商品 ID 列表
*
* @param userId 当前用户 ID
* @return 符合条件的商品 ID 列表(已去重)
*/
List
<
Integer
>
selectIdsByErpPermission
(
Integer
userId
);
}
custom-server-domain/src/main/java/com/jomalls/custom/domain/service/impl/CustomProductInfoDomainServiceImpl.java
View file @
43164dcf
...
...
@@ -9,6 +9,8 @@ import org.apache.ibatis.session.SqlSessionFactory;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
import
java.util.List
;
/**
* @author Lizh
...
...
@@ -28,4 +30,9 @@ public class CustomProductInfoDomainServiceImpl extends BaseServiceImpl<CustomPr
public
CustomProductInfoEntity
getBySku
(
String
sku
)
{
return
baseMapper
.
selectBySku
(
sku
);
}
@Override
public
List
<
Integer
>
selectIdsByErpPermission
(
Integer
userId
)
{
return
baseMapper
.
selectIdsByErpPermission
(
userId
);
}
}
\ No newline at end of file
custom-server-domain/src/main/resources/mapper/CustomProductFactoryPriceRelMapper.xml
View file @
43164dcf
...
...
@@ -34,9 +34,9 @@
<!-- 批量插入 -->
<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
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) 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}
)
(#{item.productId}, #{item.itemId}, #{item.itemSku}, #{item.factoryId}, #{item.factoryPrice}, #{item.salesPrice}, #{item.factoryCurrencyCode}, #{item.salesCurrencyCode})
</foreach>
</insert>
</mapper>
custom-server-domain/src/main/resources/mapper/CustomProductImageMapper.xml
View file @
43164dcf
...
...
@@ -24,9 +24,9 @@
<!-- 批量插入 -->
<insert
id=
"insertBatchSomeColumn"
>
INSERT INTO custom_product_image (product_id, image_url, sort, type
, create_time
) VALUES
INSERT INTO custom_product_image (product_id, image_url, sort, type) VALUES
<foreach
collection=
"list"
item=
"item"
separator=
","
>
(#{item.productId}, #{item.imageUrl}, #{item.sort}, #{item.type}
, #{item.createTime}
)
(#{item.productId}, #{item.imageUrl}, #{item.sort}, #{item.type})
</foreach>
</insert>
</mapper>
custom-server-domain/src/main/resources/mapper/CustomProductInfoMapper.xml
View file @
43164dcf
...
...
@@ -90,6 +90,21 @@
LIMIT 1
</select>
<!-- ERP 权限过滤:查询用户可见的商品 ID 列表 -->
<!-- 对齐 TS erpPage native SQL(TS:686-706)
产品可见条件:
(rel.diy_user_id = :userId OR rel.diy_user_id IS NULL) — 用户已绑定 OR 产品未绑定任何人
AND
(bl.diy_user_id != :userId OR bl.diy_user_id IS NULL) — 用户未被拉黑 -->
<select
id=
"selectIdsByErpPermission"
resultType=
"java.lang.Integer"
>
SELECT DISTINCT info.id
FROM custom_product_info info
LEFT JOIN custom_product_diy_user_rel rel ON info.id = rel.product_id
LEFT JOIN custom_product_blacklist bl ON info.id = bl.product_id
WHERE (bl.diy_user_id != #{userId} OR bl.diy_user_id IS NULL)
AND (rel.diy_user_id = #{userId} OR rel.diy_user_id IS NULL)
</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
...
...
custom-server-starter/src/main/java/com/jomalls/custom/config/CommonExceptionHandlerAdvice.java
View file @
43164dcf
...
...
@@ -9,6 +9,7 @@ import com.jomalls.custom.utils.R;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.http.HttpStatus
;
import
org.springframework.http.ResponseEntity
;
import
org.springframework.http.converter.HttpMessageNotReadableException
;
import
org.springframework.web.bind.annotation.ExceptionHandler
;
import
org.springframework.web.bind.annotation.RestControllerAdvice
;
...
...
@@ -20,10 +21,21 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
public
class
CommonExceptionHandlerAdvice
{
/**
* JSON 反序列化失败(请求参数格式不匹配)
*/
@ExceptionHandler
(
HttpMessageNotReadableException
.
class
)
public
ResponseEntity
<
R
<
Object
>>
handleHttpMessageNotReadableException
(
HttpMessageNotReadableException
e
)
{
log
.
debug
(
"[ JSON反序列化失败 ] {}"
,
e
.
getMessage
(),
e
);
return
ResponseEntity
.
status
(
HttpStatus
.
BAD_REQUEST
)
.
body
(
R
.
fail
(
CodeEnum
.
FAIL
.
getCode
(),
"请求参数格式错误: "
+
e
.
getLocalizedMessage
()));
}
/**
* token验证失败(返回401 未登录或登录已过期)
*/
@ExceptionHandler
(
InvalidTokenException
.
class
)
public
ResponseEntity
<
R
<
Object
>>
handleInvalidTokenException
(
InvalidTokenException
e
)
{
log
.
debug
(
"[ Token验证失败 ] {}"
,
e
.
getMessage
());
return
ResponseEntity
.
status
(
HttpStatus
.
UNAUTHORIZED
)
.
body
(
R
.
fail
(
CodeEnum
.
UNAUTHORIZED
.
getCode
(),
e
.
getMessage
()));
}
...
...
@@ -33,6 +45,7 @@ public class CommonExceptionHandlerAdvice {
*/
@ExceptionHandler
(
PermissionDeniedException
.
class
)
public
ResponseEntity
<
R
<
Object
>>
handlePermissionDeniedException
(
PermissionDeniedException
e
)
{
log
.
debug
(
"[ 权限拒绝 ] {}"
,
e
.
getMessage
());
return
ResponseEntity
.
status
(
HttpStatus
.
FORBIDDEN
)
.
body
(
R
.
fail
(
CodeEnum
.
FORBIDDEN
.
getCode
(),
e
.
getMessage
()));
}
...
...
@@ -42,8 +55,9 @@ public class CommonExceptionHandlerAdvice {
*/
@ExceptionHandler
(
ServiceException
.
class
)
public
ResponseEntity
<
R
<
Object
>>
handleServiceException
(
ServiceException
e
)
{
log
.
debug
(
"[ 业务异常 ] {}"
,
e
.
getMessage
(),
e
);
return
ResponseEntity
.
status
(
HttpStatus
.
INTERNAL_SERVER_ERROR
)
.
body
(
R
.
fail
(
e
.
get
Code
(),
e
.
get
Message
()));
.
body
(
R
.
fail
(
e
.
getMessage
()));
}
/**
...
...
@@ -51,6 +65,7 @@ public class CommonExceptionHandlerAdvice {
*/
@ExceptionHandler
(
RemoteServiceException
.
class
)
public
ResponseEntity
<
R
<
Object
>>
handleRemoteServiceException
(
RemoteServiceException
e
)
{
log
.
debug
(
"[ 远程服务异常 ] status={}, {}"
,
e
.
getStatusCode
(),
e
.
getMessage
(),
e
);
return
ResponseEntity
.
status
(
HttpStatus
.
INTERNAL_SERVER_ERROR
)
.
body
(
R
.
fail
(
e
.
getStatusCode
(),
e
.
getMessage
()));
}
...
...
@@ -60,6 +75,7 @@ public class CommonExceptionHandlerAdvice {
*/
@ExceptionHandler
(
RuntimeException
.
class
)
public
ResponseEntity
<
R
<
Object
>>
handleRuntimeException
(
Exception
e
)
{
log
.
debug
(
"[ 运行时异常 ] {}"
,
e
.
getMessage
(),
e
);
return
ResponseEntity
.
status
(
HttpStatus
.
INTERNAL_SERVER_ERROR
)
.
body
(
R
.
fail
(
e
.
getMessage
()));
}
...
...
@@ -69,6 +85,7 @@ public class CommonExceptionHandlerAdvice {
*/
@ExceptionHandler
(
Exception
.
class
)
public
ResponseEntity
<
R
<
Object
>>
handleException
(
Exception
e
)
{
log
.
debug
(
"[ 未捕获异常 ] {}"
,
e
.
getMessage
(),
e
);
return
ResponseEntity
.
status
(
HttpStatus
.
INTERNAL_SERVER_ERROR
)
.
body
(
R
.
fail
(
CodeEnum
.
FAIL
.
getCode
(),
e
.
getMessage
()));
}
...
...
custom-server-starter/src/main/resources/application.properties
View file @
43164dcf
...
...
@@ -26,10 +26,6 @@ mybatis-plus.mapper-locations=classpath*:mapper/**/*.xml
mybatis-plus.type-aliases-package
=
com.jomalls.custom.domain.entity
mybatis-plus.configuration.call-setters-on-nulls
=
true
## 数据版本控制
data.version.control.switch
=
false
default.scp.data.version
=
1.0
## 时区配置
TZ
=
Asia/Shanghai
...
...
custom-server-starter/src/main/resources/logback-spring.xml
View file @
43164dcf
...
...
@@ -103,7 +103,7 @@
<logger
name=
"org.springdoc"
level=
"WARN"
/>
<!-- ==================== 根配置 ==================== -->
<root
level=
"
INFO
"
>
<root
level=
"
DEBUG
"
>
<appender-ref
ref=
"console"
/>
<appender-ref
ref=
"file_info"
/>
<appender-ref
ref=
"file_warn"
/>
...
...
custom-server-webapp/src/main/java/com/jomalls/custom/webapp/controller/CustomProductFactoryPriceRelController.java
View file @
43164dcf
...
...
@@ -2,7 +2,7 @@ package com.jomalls.custom.webapp.controller;
import
com.baomidou.mybatisplus.core.metadata.IPage
;
import
com.jomalls.custom.app.vo.CustomProductFactoryPriceRelPageVO
;
import
com.jomalls.custom.app.vo.CustomProductFactoryPriceRelVO
;
import
com.jomalls.custom.app.vo.CustomProductFactoryPriceRel
Snake
VO
;
import
com.jomalls.custom.app.service.CustomProductFactoryPriceRelService
;
import
io.swagger.v3.oas.annotations.Operation
;
import
io.swagger.v3.oas.annotations.Parameter
;
...
...
@@ -33,13 +33,13 @@ public class CustomProductFactoryPriceRelController {
/**
* 列表查询接口
*
* @param customProductFactoryPriceRelVO 条件model
* @param customProductFactoryPriceRel
Snake
VO 条件model
* @return list集合
*/
@Operation
(
summary
=
"列表查询接口"
,
description
=
"根据条件查询列表接口(不分页)"
)
@RequestMapping
(
value
=
"/list"
,
method
=
RequestMethod
.
POST
)
public
List
<
CustomProductFactoryPriceRel
VO
>
list
(
@RequestBody
CustomProductFactoryPriceRelVO
customProductFactoryPriceRel
VO
)
{
return
customProductFactoryPriceRelService
.
list
(
customProductFactoryPriceRelVO
);
public
List
<
CustomProductFactoryPriceRel
SnakeVO
>
list
(
@RequestBody
CustomProductFactoryPriceRelSnakeVO
customProductFactoryPriceRelSnake
VO
)
{
return
customProductFactoryPriceRelService
.
list
(
customProductFactoryPriceRel
Snake
VO
);
}
/**
...
...
@@ -50,7 +50,7 @@ public class CustomProductFactoryPriceRelController {
*/
@Operation
(
summary
=
"分页列表接口"
,
description
=
"根据条件查询分页列表接口"
)
@RequestMapping
(
value
=
"/pageList"
,
method
=
RequestMethod
.
POST
)
public
IPage
<
CustomProductFactoryPriceRelVO
>
pageList
(
@RequestBody
CustomProductFactoryPriceRelPageVO
customProductFactoryPriceRelPageVO
)
{
public
IPage
<
CustomProductFactoryPriceRel
Snake
VO
>
pageList
(
@RequestBody
CustomProductFactoryPriceRelPageVO
customProductFactoryPriceRelPageVO
)
{
return
customProductFactoryPriceRelService
.
pageList
(
customProductFactoryPriceRelPageVO
);
}
...
...
@@ -63,31 +63,31 @@ public class CustomProductFactoryPriceRelController {
*/
@Operation
(
summary
=
"根据主键id查询详情"
,
description
=
"根据主键id查询详情"
)
@RequestMapping
(
value
=
"/info/{id}"
,
method
=
RequestMethod
.
GET
)
public
CustomProductFactoryPriceRelVO
info
(
@Parameter
(
description
=
"主键id"
,
required
=
true
)
@PathVariable
(
"id"
)
Integer
id
)
{
public
CustomProductFactoryPriceRel
Snake
VO
info
(
@Parameter
(
description
=
"主键id"
,
required
=
true
)
@PathVariable
(
"id"
)
Integer
id
)
{
return
customProductFactoryPriceRelService
.
info
(
id
);
}
/**
* 保存对象
*
* @param customProductFactoryPriceRelVO 保存对象
* @param customProductFactoryPriceRel
Snake
VO 保存对象
*/
@Operation
(
summary
=
"保存对象"
,
description
=
"保存对象"
)
@RequestMapping
(
value
=
"/save"
,
method
=
RequestMethod
.
POST
)
public
void
save
(
@RequestBody
@Valid
CustomProductFactoryPriceRel
VO
customProductFactoryPriceRel
VO
)
{
customProductFactoryPriceRelService
.
save
(
customProductFactoryPriceRelVO
);
public
void
save
(
@RequestBody
@Valid
CustomProductFactoryPriceRel
SnakeVO
customProductFactoryPriceRelSnake
VO
)
{
customProductFactoryPriceRelService
.
save
(
customProductFactoryPriceRel
Snake
VO
);
}
/**
* 根据id修改对象
*
* @param customProductFactoryPriceRelVO 修改对象
* @param customProductFactoryPriceRel
Snake
VO 修改对象
*/
@Operation
(
summary
=
"根据id修改对象"
,
description
=
"根据id修改对象"
)
@RequestMapping
(
value
=
"/updateById"
,
method
=
RequestMethod
.
PUT
)
public
void
updateById
(
@RequestBody
CustomProductFactoryPriceRel
VO
customProductFactoryPriceRel
VO
)
{
customProductFactoryPriceRelService
.
updateById
(
customProductFactoryPriceRelVO
);
public
void
updateById
(
@RequestBody
CustomProductFactoryPriceRel
SnakeVO
customProductFactoryPriceRelSnake
VO
)
{
customProductFactoryPriceRelService
.
updateById
(
customProductFactoryPriceRel
Snake
VO
);
}
/**
...
...
custom-server-webapp/src/main/java/com/jomalls/custom/webapp/controller/CustomProductInfoController.java
View file @
43164dcf
...
...
@@ -4,7 +4,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
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.dto.CustomProductInfoUpdate
Snake
DTO
;
import
com.jomalls.custom.app.enums.CustomProductInfoStatusEnum
;
import
com.jomalls.custom.app.service.CustomProductInfoService
;
import
com.jomalls.custom.app.vo.CustomProductInfoSnakeVO
;
...
...
@@ -51,7 +51,7 @@ public class CustomProductInfoController {
@Operation
(
summary
=
"组合更新商品"
,
description
=
"事务内处理主表及子表的增/删/改差异"
)
@PostMapping
(
"/update"
)
public
void
update
(
@RequestBody
@Valid
CustomProductInfoUpdateDTO
dto
)
{
public
void
update
(
@RequestBody
@Valid
CustomProductInfoUpdate
Snake
DTO
dto
)
{
customProductInfoService
.
updateFull
(
dto
);
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment