Commit bdb8936e by Lizh

调整integrate目录结构,删除无用代码

parent 1c5f75aa
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
/logs/ /logs/
.trae/ .trae/
.vscode/ .vscode/
.claude/
CLAUDE.md CLAUDE.md
docs docs
package com.jomalls.custom.app.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jomalls.custom.app.vo.CustomProductWarehouseRelPageVO;
import com.jomalls.custom.app.vo.CustomProductWarehouseRelVO;
import com.jomalls.custom.dal.entity.CustomProductWarehouseRelEntity;
import java.util.List;
/**
* @author Lizh
* @version 0.01
* @description: 接口
* @date 2026-05-29 10:43:29
*/
public interface CustomProductWarehouseRelService {
/**
* 列表查询接口
*
* @param customProductWarehouseRelVO 条件model
* @return list集合
*/
List<CustomProductWarehouseRelVO> list(CustomProductWarehouseRelVO customProductWarehouseRelVO);
/**
* 根据条件查询分页列表接口
*
* @param customProductWarehouseRelPageVO 分页入参model
* @return 分页对象
*/
IPage<CustomProductWarehouseRelVO> pageList(CustomProductWarehouseRelPageVO customProductWarehouseRelPageVO);
/**
* 根据id查询详情
*
* @param id 主键
* @return 实体model
*/
CustomProductWarehouseRelVO info(Integer id);
/**
* 保存对象
*
* @param customProductWarehouseRelVO 保存对象
*/
void save(CustomProductWarehouseRelVO customProductWarehouseRelVO);
/**
* 根据id修改对象
*
* @param customProductWarehouseRelVO 修改对象
*/
void updateById(CustomProductWarehouseRelVO customProductWarehouseRelVO);
/**
* 根据主键ID进行删除
*
* @param id 主键
*/
void deleteById(Integer id);
/**
* 根据仓库ID获取关联列表
*/
List<CustomProductWarehouseRelEntity> getListByWareHouseId(Long warehouseId);
/**
* 根据产品ID获取关联列表
*/
List<CustomProductWarehouseRelEntity> getListByProductId(Long productId);
/**
* 根据产品ID获取仓库ID列表
*/
List<Long> getWarehouseIdsByProductId(Long productId);
/**
* 批量保存产品与仓库的关联
* 逻辑:先删除该产品现有的所有关联,再批量插入新的
*/
void saveBatch(Integer productId, List<Long> warehouseIds);
/**
* 根据产品ID删除关联
* 注意:原 TS 代码支持手动传入 transaction,Java 中通常使用 @Transactional 注解管理事务
*/
void deleteByProductId(Integer productId);
}
...@@ -83,6 +83,12 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -83,6 +83,12 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
/** 并行查询超时时间(秒) */ /** 并行查询超时时间(秒) */
private static final long QUERY_TIMEOUT_SECONDS = 30; private static final long QUERY_TIMEOUT_SECONDS = 30;
/** 最小列表长度 */
private static final int MIN_LIST_SIZE = 1;
/** 负数 1 */
private static final int NEGATIVE_ONE = -1;
@Override @Override
public IPage<CustomProductInfoPageSnakeVO> pageList(CustomProductInfoQuerySnakeDTO param) { public IPage<CustomProductInfoPageSnakeVO> pageList(CustomProductInfoQuerySnakeDTO param) {
...@@ -111,8 +117,9 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -111,8 +117,9 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
* @param isErp true=ERP 模式(title 双字段 OR 搜索,processing 支持 2=IS NULL,跳过 DIY/黑名单过滤) * @param isErp true=ERP 模式(title 双字段 OR 搜索,processing 支持 2=IS NULL,跳过 DIY/黑名单过滤)
*/ */
private void toQueryWrapper(CustomProductInfoQuerySnakeDTO param, LambdaQueryWrapper<CustomProductInfoEntity> queryWrapper, boolean isErp) { private void toQueryWrapper(CustomProductInfoQuerySnakeDTO param, LambdaQueryWrapper<CustomProductInfoEntity> queryWrapper, boolean isErp) {
// 分类层级过滤 // 分类层级过滤,获取该分类以及其子类, page查询条件
if (param.getCategory_id() != null) { if (param.getCategory_id() != null) {
// 调用saasAdmin服务,获取所有分类信息
List<CategoryInfoModel> cateList = saasAdminService.getAllList(); List<CategoryInfoModel> cateList = saasAdminService.getAllList();
if (CollectionUtils.isEmpty(cateList)) { if (CollectionUtils.isEmpty(cateList)) {
cateList = Collections.emptyList(); cateList = Collections.emptyList();
...@@ -121,9 +128,11 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -121,9 +128,11 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
CategoryInfoModel cate = cateList.stream().filter(c -> c.getId().equals(param.getCategory_id())) CategoryInfoModel cate = cateList.stream().filter(c -> c.getId().equals(param.getCategory_id()))
.findFirst().orElseThrow(() -> new ServiceException("不存在该类别, category_id=" + param.getCategory_id())); .findFirst().orElseThrow(() -> new ServiceException("不存在该类别, category_id=" + param.getCategory_id()));
String pids = String.valueOf(cate.getId()); String pids = String.valueOf(cate.getId());
// 获取该类别的父类
if (StringUtils.isNotBlank(cate.getPids())) { if (StringUtils.isNotBlank(cate.getPids())) {
pids = cate.getPids() + "," + pids; pids = cate.getPids() + "," + pids;
} }
// 父类id,如: 0,19,26,33
String finalPids = pids; String finalPids = pids;
// 获取该类别及子类别的 ID // 获取该类别及子类别的 ID
List<Integer> cateIds = cateList.stream() List<Integer> cateIds = cateList.stream()
...@@ -136,7 +145,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -136,7 +145,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
queryWrapper.in(CustomProductInfoEntity::getCategoryId, cateIds); queryWrapper.in(CustomProductInfoEntity::getCategoryId, cateIds);
} }
// 工厂 ID 过滤(通过 product_factory_rel M2M 表 // 工厂 ID 过滤(通过 product_factory_rel M2M 表,每个工厂可以有不一样的价格), page查询条件
if (param.getFactory_id() != null) { if (param.getFactory_id() != null) {
List<Integer> factoryProductIds = productFactoryRelDomainService.list( List<Integer> factoryProductIds = productFactoryRelDomainService.list(
new LambdaQueryWrapper<ProductFactoryRelEntity>() new LambdaQueryWrapper<ProductFactoryRelEntity>()
...@@ -150,13 +159,13 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -150,13 +159,13 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
} }
} }
// DIY 用户过滤与黑名单过滤(仅标准分页使用,ERP权限过滤) // DIY 用户过滤与黑名单过滤(仅标准分页使用,ERP权限过滤), page查询条件, erpPage查询跳过
if (!isErp) { if (!isErp) {
applyDiyAndBlacklistFilter(param, queryWrapper); applyDiyAndBlacklistFilter(param, queryWrapper);
queryWrapper.orderByDesc(CustomProductInfoEntity::getId); queryWrapper.orderByDesc(CustomProductInfoEntity::getId);
} }
// 是否九猫处理过滤(0=否 1=是 2=未设置,也支持 true/false) // 是否九猫处理过滤(0=否 1=是 2=未设置,也支持 true/false), page查询条件, erpPage查询跳过
Integer processing = param.getProcessing(); Integer processing = param.getProcessing();
if (processing != null ) { if (processing != null ) {
if (processing == ProcessingStatus.NOT_SET.getCode()) { if (processing == ProcessingStatus.NOT_SET.getCode()) {
...@@ -170,48 +179,51 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -170,48 +179,51 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
if (param.getId() != null) { if (param.getId() != null) {
queryWrapper.eq(CustomProductInfoEntity::getId, param.getId()); queryWrapper.eq(CustomProductInfoEntity::getId, param.getId());
} }
// sku 支持逗号分隔多值 IN 查询 // sku 支持逗号分隔多值 IN 查询, erpPage查询条件 TODO
if (StringUtils.isNotBlank(param.getSku())) { if (StringUtils.isNotBlank(param.getSku())) {
String[] skuArr = param.getSku().split(","); String[] skuArr = param.getSku().split(",");
if (skuArr.length > 1) { if (skuArr.length > MIN_LIST_SIZE) {
queryWrapper.in(CustomProductInfoEntity::getSku, (Object[]) skuArr); queryWrapper.in(CustomProductInfoEntity::getSku, (Object[]) skuArr);
} else { } else {
queryWrapper.eq(CustomProductInfoEntity::getSku, param.getSku()); queryWrapper.eq(CustomProductInfoEntity::getSku, param.getSku());
} }
} }
// 状态过滤(支持逗号分隔多值,对齐 TS status split(",")) // ERP: title 关键词同时搜索 name 和 title 两列, erpPage查询条件
if (param.getStatusList() != null && !param.getStatusList().isEmpty()) { if (StringUtils.isNotBlank(param.getTitle())) {
queryWrapper.in(CustomProductInfoEntity::getStatus, param.getStatusList()); if (isErp) {
} else if (param.getStatus() != null) { queryWrapper.and(w -> w.like(CustomProductInfoEntity::getName, param.getTitle())
queryWrapper.eq(CustomProductInfoEntity::getStatus, param.getStatus()); .or().like(CustomProductInfoEntity::getTitle, param.getTitle()));
} else {
queryWrapper.like(CustomProductInfoEntity::getTitle, param.getTitle());
}
} }
// 产品归属(平台共享/客户私有), erpPage查询条件
if (StringUtils.isNotBlank(param.getProduct_type())) { if (StringUtils.isNotBlank(param.getProduct_type())) {
queryWrapper.eq(CustomProductInfoEntity::getProductType, param.getProduct_type()); queryWrapper.eq(CustomProductInfoEntity::getProductType, param.getProduct_type());
} }
// 货号:逗号分隔多值 IN 查询,单个值模糊 LIKE 查询 // 货号:逗号分隔多值 IN 查询,单个值模糊 LIKE 查询, erpPage查询条件 TODO
if (StringUtils.isNotBlank(param.getProduct_no())) { if (StringUtils.isNotBlank(param.getProduct_no())) {
String[] productNoArr = param.getProduct_no().split(","); String[] productNoArr = param.getProduct_no().split(",");
if (productNoArr.length > 1) { if (productNoArr.length > MIN_LIST_SIZE) {
queryWrapper.in(CustomProductInfoEntity::getProductNo, (Object[]) productNoArr); queryWrapper.in(CustomProductInfoEntity::getProductNo, (Object[]) productNoArr);
} else { } else {
queryWrapper.like(CustomProductInfoEntity::getProductNo, param.getProduct_no()); queryWrapper.like(CustomProductInfoEntity::getProductNo, param.getProduct_no());
} }
} }
// 状态过滤(支持逗号分隔多值,对齐 TS status split(",")), erpPage查询条件
if (CollectionUtils.isNotEmpty(param.getStatusList())) {
queryWrapper.in(CustomProductInfoEntity::getStatus, param.getStatusList());
} else if (param.getStatus() != null) {
queryWrapper.eq(CustomProductInfoEntity::getStatus, param.getStatus());
}
// 商品类型(局部印/满印), erpPage查询条件
if (param.getPrint_type() != null) { if (param.getPrint_type() != null) {
queryWrapper.eq(CustomProductInfoEntity::getPrintType, param.getPrint_type()); queryWrapper.eq(CustomProductInfoEntity::getPrintType, param.getPrint_type());
} }
if (StringUtils.isNotBlank(param.getName())) { if (StringUtils.isNotBlank(param.getName())) {
queryWrapper.like(CustomProductInfoEntity::getName, param.getName()); queryWrapper.like(CustomProductInfoEntity::getName, param.getName());
} }
// ERP: title 关键词同时搜索 name 和 title 两列
if (StringUtils.isNotBlank(param.getTitle())) {
if (isErp) {
queryWrapper.and(w -> w.like(CustomProductInfoEntity::getName, param.getTitle())
.or().like(CustomProductInfoEntity::getTitle, param.getTitle()));
} else {
queryWrapper.like(CustomProductInfoEntity::getTitle, param.getTitle());
}
}
// 工厂过滤(factoryIds 直接列 — 用于创建/更新场景,保留兼容) // 工厂过滤(factoryIds 直接列 — 用于创建/更新场景,保留兼容)
if (param.getFactoryIds() != null) { if (param.getFactoryIds() != null) {
...@@ -220,18 +232,18 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -220,18 +232,18 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
} }
/** /**
* DIY 用户与黑名单过滤(标准分页专用) * DIY 用户与黑名单过滤(标准分页专用), page查询条件
* <p> * <p>
* diyUserId 与 blackUserId 互斥(else if), * diyUserId 与 blackUserId 互斥(else if),
* diyUserId=-1 使用 LEFT JOIN 语义查找无绑定的商品。 * diyUserId=-1 使用 LEFT JOIN 语义查找无绑定的商品。
*/ */
private void applyDiyAndBlacklistFilter(CustomProductInfoQuerySnakeDTO param, LambdaQueryWrapper<CustomProductInfoEntity> queryWrapper) { private void applyDiyAndBlacklistFilter(CustomProductInfoQuerySnakeDTO param, LambdaQueryWrapper<CustomProductInfoEntity> queryWrapper) {
if (param.getDiyUserId() != null) { if (param.getDiyUserId() != null) {
if (param.getDiyUserId() == -1) { if (param.getDiyUserId() == NEGATIVE_ONE) {
// 查找无任何 diy_user 绑定的商品(通过 Domain 层 LEFT JOIN 查询) // 查找无任何 diy_user 绑定的商品(通过 Domain 层 LEFT JOIN 查询)
List<Integer> unboundIds = customProductInfoDomainService.selectIdsWithoutDiyUserBind(); List<Integer> unboundIds = customProductInfoDomainService.selectIdsWithoutDiyUserBind();
if (CollectionUtils.isEmpty(unboundIds)) { if (CollectionUtils.isEmpty(unboundIds)) {
queryWrapper.eq(CustomProductInfoEntity::getId, -1); queryWrapper.eq(CustomProductInfoEntity::getId, NEGATIVE_ONE);
} else { } else {
queryWrapper.in(CustomProductInfoEntity::getId, unboundIds); queryWrapper.in(CustomProductInfoEntity::getId, unboundIds);
} }
...@@ -242,7 +254,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -242,7 +254,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
.stream().map(CustomProductDiyUserRelEntity::getProductId) .stream().map(CustomProductDiyUserRelEntity::getProductId)
.distinct().collect(Collectors.toList()); .distinct().collect(Collectors.toList());
if (CollectionUtils.isEmpty(productIds)) { if (CollectionUtils.isEmpty(productIds)) {
queryWrapper.eq(CustomProductInfoEntity::getId, -1); queryWrapper.eq(CustomProductInfoEntity::getId, NEGATIVE_ONE);
} else { } else {
queryWrapper.in(CustomProductInfoEntity::getId, productIds); queryWrapper.in(CustomProductInfoEntity::getId, productIds);
} }
...@@ -255,7 +267,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -255,7 +267,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
.stream().map(CustomProductBlacklistEntity::getProductId) .stream().map(CustomProductBlacklistEntity::getProductId)
.distinct().collect(Collectors.toList()); .distinct().collect(Collectors.toList());
if (productIds.isEmpty()) { if (productIds.isEmpty()) {
queryWrapper.eq(CustomProductInfoEntity::getId, -1); queryWrapper.eq(CustomProductInfoEntity::getId, NEGATIVE_ONE);
} else { } else {
queryWrapper.in(CustomProductInfoEntity::getId, productIds); queryWrapper.in(CustomProductInfoEntity::getId, productIds);
} }
...@@ -416,7 +428,11 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -416,7 +428,11 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
return fullVO; return fullVO;
} }
/** 校验并查询商品主表实体 */ /**
* 校验并查询商品主表实体 , getByIdOrSku 接口检查
* @param id 商品 ID
* @param sku 商品 SKU
*/
private CustomProductInfoEntity validateAndQueryEntity(Integer id, String sku) { private CustomProductInfoEntity validateAndQueryEntity(Integer id, String sku) {
CustomProductInfoEntity entity; CustomProductInfoEntity entity;
if (id != null) { if (id != null) {
...@@ -433,7 +449,8 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -433,7 +449,8 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
} }
/** /**
* 并行查询所有子表数据,带超时保护 * 并行查询所有子表数据,带超时保护, getByIdOrSku 接口组装数据
* @param productId 商品 ID
*/ */
private ProductRelatedData queryRelatedDataInParallel(Integer productId) { private ProductRelatedData queryRelatedDataInParallel(Integer productId) {
CompletableFuture<List<CustomProductItemEntity>> itemsFuture = CompletableFuture.supplyAsync(() -> CompletableFuture<List<CustomProductItemEntity>> itemsFuture = CompletableFuture.supplyAsync(() ->
...@@ -520,7 +537,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -520,7 +537,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
} }
/** /**
* 并行查询结果聚合 * 并行查询结果聚合, getByIdOrSku 接口组装数据
*/ */
private record ProductRelatedData( private record ProductRelatedData(
List<CustomProductItemEntity> items, List<CustomProductItemEntity> items,
...@@ -537,14 +554,14 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -537,14 +554,14 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
/** /**
* 通过 AdminPropertyService 解析属性名称,填充到 FullVO 的 skuProperties / normalProperties 中 * 通过 AdminPropertyService 解析属性名称,填充到 FullVO 的 skuProperties / normalProperties 中
* <p> * <p>
* 对齐ts代码,将数据库中存储的 property_id/value_id 转换为带名称的属性列表。 * 对齐ts代码,将数据库中存储的 property_id/value_id 转换为带名称的属性列表。getByIdOrSku 接口组装数据
* *
* @param properties 数据库原始属性记录 * @param properties 数据库原始属性记录
* @param fullVO 待填充的商品完整详情 * @param fullVO 待填充的商品完整详情
*/ */
private void resolvePropertyNames(List<CustomProductInfoPropertyEntity> properties, private void resolvePropertyNames(List<CustomProductInfoPropertyEntity> properties,
CustomProductInfoSnakeVO fullVO) { CustomProductInfoSnakeVO fullVO) {
if (properties == null || properties.isEmpty()) { if (CollectionUtils.isEmpty(properties)) {
return; return;
} }
fullVO.setProperties(properties.stream().map(e -> BeanMapper.snakeCase() fullVO.setProperties(properties.stream().map(e -> BeanMapper.snakeCase()
...@@ -875,20 +892,19 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -875,20 +892,19 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
diyWrapper.eq(DbDiyEntity::getNamespace, param.getSource()); diyWrapper.eq(DbDiyEntity::getNamespace, param.getSource());
} }
List<DbDiyEntity> diys = dbDiyDomainService.list(diyWrapper); List<DbDiyEntity> diys = dbDiyDomainService.list(diyWrapper);
if (diys.isEmpty()) { if (CollectionUtils.isEmpty(diys)) {
return emptyPage(param); return emptyPage(param);
} }
List<Integer> diyIds = diys.stream().map(DbDiyEntity::getId).collect(Collectors.toList()); List<Integer> diyIds = diys.stream().map(DbDiyEntity::getId).collect(Collectors.toList());
List<ProductTemplateInfoEntity> temps = productTemplateInfoDomainService.selectByDiyIds(diyIds); List<ProductTemplateInfoEntity> temps = productTemplateInfoDomainService.selectByDiyIds(diyIds);
if (temps.isEmpty()) { if (CollectionUtils.isEmpty(temps)) {
return emptyPage(param); return emptyPage(param);
} }
List<Integer> productIds = temps.stream() List<Integer> productIds = temps.stream()
.map(ProductTemplateInfoEntity::getProductId) .map(ProductTemplateInfoEntity::getProductId)
.filter(Objects::nonNull) .filter(Objects::nonNull)
.distinct() .distinct().collect(Collectors.toList());
.collect(Collectors.toList()); if (CollectionUtils.isEmpty(productIds)) {
if (productIds.isEmpty()) {
return emptyPage(param); return emptyPage(param);
} }
queryWrapper.in(CustomProductInfoEntity::getId, productIds); queryWrapper.in(CustomProductInfoEntity::getId, productIds);
...@@ -899,14 +915,14 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -899,14 +915,14 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
List<CustomWarehouseInfoEntity> warehouses = customWarehouseInfoDomainService.list( List<CustomWarehouseInfoEntity> warehouses = customWarehouseInfoDomainService.list(
new LambdaQueryWrapper<CustomWarehouseInfoEntity>() new LambdaQueryWrapper<CustomWarehouseInfoEntity>()
.eq(CustomWarehouseInfoEntity::getCountryCode, param.getWarehouseCountry())); .eq(CustomWarehouseInfoEntity::getCountryCode, param.getWarehouseCountry()));
if (warehouses.isEmpty()) { if (CollectionUtils.isEmpty(warehouses)) {
return emptyPage(param); return emptyPage(param);
} }
List<Integer> wIds = warehouses.stream() List<Integer> wIds = warehouses.stream()
.map(w -> w.getId().intValue()) .map(w -> w.getId().intValue())
.collect(Collectors.toList()); .collect(Collectors.toList());
List<CustomProductWarehouseRelEntity> rels = customProductWarehouseRelDomainService.selectByWarehouseIds(wIds); List<CustomProductWarehouseRelEntity> rels = customProductWarehouseRelDomainService.selectByWarehouseIds(wIds);
if (rels.isEmpty()) { if (CollectionUtils.isEmpty(rels)) {
return emptyPage(param); return emptyPage(param);
} }
List<Integer> productIds = rels.stream() List<Integer> productIds = rels.stream()
...@@ -917,7 +933,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -917,7 +933,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
// 5. ERP 权限过滤 // 5. ERP 权限过滤
if (user != null) { if (user != null) {
List<Integer> allowedIds = customProductInfoDomainService.selectIdsByErpPermission(user.getId()); List<Integer> allowedIds = customProductInfoDomainService.selectIdsByErpPermission(user.getId());
if (allowedIds.isEmpty()) { if (CollectionUtils.isEmpty(allowedIds)) {
return emptyPage(param); return emptyPage(param);
} }
queryWrapper.in(CustomProductInfoEntity::getId, allowedIds); queryWrapper.in(CustomProductInfoEntity::getId, allowedIds);
...@@ -931,8 +947,10 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -931,8 +947,10 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
queryWrapper.orderByDesc(CustomProductInfoEntity::getId); queryWrapper.orderByDesc(CustomProductInfoEntity::getId);
} }
} else { } else {
// 默认排序需要 sort IS NULL ASC 确保 NULL 排在非 NULL 之后(对齐 TS Sequelize.literal) // 默认排序:sort 升序(未设置排序的排最后),同值按 id 倒序
queryWrapper.last(" ORDER BY sort IS NULL ASC, sort ASC, id DESC"); // sort 字段在 save/update 时已将 NULL 替换为 SORT_UNSET,无需 sort IS NULL 表达式
queryWrapper.orderByAsc(CustomProductInfoEntity::getSort)
.orderByDesc(CustomProductInfoEntity::getId);
} }
// 7. 执行分页查询 // 7. 执行分页查询
...@@ -995,7 +1013,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -995,7 +1013,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
} }
/** /**
* 批量查询 DIY 模板上架状态,返回 diyId → isShelf 映射 * 批量查询 DIY 模板上架状态,返回 diyId → isShelf 映射,erpPage 接口中使用
* <p> * <p>
* 使用单次 IN 查询代替 N+1 逐条查询。 * 使用单次 IN 查询代替 N+1 逐条查询。
*/ */
...@@ -1005,7 +1023,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -1005,7 +1023,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
.filter(Objects::nonNull) .filter(Objects::nonNull)
.distinct() .distinct()
.collect(Collectors.toList()); .collect(Collectors.toList());
if (diyIds.isEmpty()) { if (CollectionUtils.isEmpty(diyIds)) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
List<DbDiyEntity> diys = dbDiyDomainService.list( List<DbDiyEntity> diys = dbDiyDomainService.list(
...@@ -1021,19 +1039,19 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -1021,19 +1039,19 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
* 批量查询工艺关联(对齐 TS erpPage include: CraftCenter → BelongsToMany → CustomProductCraftRel) * 批量查询工艺关联(对齐 TS erpPage include: CraftCenter → BelongsToMany → CustomProductCraftRel)
* <p> * <p>
* 每个 craft VO 嵌套一份 CustomProductCraftRel 数据(product_id + craft_id), * 每个 craft VO 嵌套一份 CustomProductCraftRel 数据(product_id + craft_id),
* 对齐 TS Sequelize BelongsToMany 自动包含的 junction 表数据 * 对齐 TS Sequelize BelongsToMany 自动包含的 junction 表数据,erpPage 接口中使用
* *
* @return productId → List&lt;CraftCenterSnakeVO&gt; * @return productId → List&lt;CraftCenterSnakeVO&gt;
*/ */
private Map<Integer, List<CraftCenterSnakeVO>> batchLoadCraftsByProductIds(List<Integer> productIds) { private Map<Integer, List<CraftCenterSnakeVO>> batchLoadCraftsByProductIds(List<Integer> productIds) {
if (productIds.isEmpty()) { if (CollectionUtils.isEmpty(productIds)) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
// 1. 关联表 // 1. 关联表
List<CustomProductCraftRelEntity> rels = customProductCraftRelDomainService.list( List<CustomProductCraftRelEntity> rels = customProductCraftRelDomainService.list(
new LambdaQueryWrapper<CustomProductCraftRelEntity>() new LambdaQueryWrapper<CustomProductCraftRelEntity>()
.in(CustomProductCraftRelEntity::getProductId, productIds)); .in(CustomProductCraftRelEntity::getProductId, productIds));
if (rels.isEmpty()) { if (CollectionUtils.isEmpty(rels)) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
// 2. 工艺实体 // 2. 工艺实体
...@@ -1061,18 +1079,20 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -1061,18 +1079,20 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
/** /**
* 批量查询仓库关联(对齐 TS erpPage include: CustomWarehouseInfo → BelongsToMany → CustomProductWarehouseRel) * 批量查询仓库关联(对齐 TS erpPage include: CustomWarehouseInfo → BelongsToMany → CustomProductWarehouseRel)
* <p>
* erpPage 接口中使用
* *
* @return productId → List&lt;CustomWarehouseInfoSnakeVO&gt; * @return productId → List<CustomWarehouseInfoSnakeVO>
*/ */
private Map<Integer, List<CustomWarehouseInfoSnakeVO>> batchLoadWarehousesByProductIds(List<Integer> productIds) { private Map<Integer, List<CustomWarehouseInfoSnakeVO>> batchLoadWarehousesByProductIds(List<Integer> productIds) {
if (productIds.isEmpty()) { if (CollectionUtils.isEmpty(productIds)) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
// 1. 关联表 // 1. 关联表
List<CustomProductWarehouseRelEntity> rels = customProductWarehouseRelDomainService.list( List<CustomProductWarehouseRelEntity> rels = customProductWarehouseRelDomainService.list(
new LambdaQueryWrapper<CustomProductWarehouseRelEntity>() new LambdaQueryWrapper<CustomProductWarehouseRelEntity>()
.in(CustomProductWarehouseRelEntity::getProductId, productIds)); .in(CustomProductWarehouseRelEntity::getProductId, productIds));
if (rels.isEmpty()) { if (CollectionUtils.isEmpty(rels)) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
// 2. 仓库实体 // 2. 仓库实体
...@@ -1099,17 +1119,18 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -1099,17 +1119,18 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
/** /**
* 批量查询工厂价格区间关联(对齐 TS erpPage include: CustomProductFactoryPriceIntervalRel → HasMany) * 批量查询工厂价格区间关联(对齐 TS erpPage include: CustomProductFactoryPriceIntervalRel → HasMany)
* * <p>
* @return productId → List&lt;FactoryPriceIntervalRelSnakeVO&gt; * erpPage 接口中使用
* @return productId → List<FactoryPriceIntervalRelSnakeVO>
*/ */
private Map<Integer, List<FactoryPriceIntervalRelSnakeVO>> batchLoadFactoryPriceIntervalsByProductIds(List<Integer> productIds) { private Map<Integer, List<FactoryPriceIntervalRelSnakeVO>> batchLoadFactoryPriceIntervalsByProductIds(List<Integer> productIds) {
if (productIds.isEmpty()) { if (CollectionUtils.isEmpty(productIds)) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
List<CustomProductFactoryPriceIntervalRelEntity> rels = customProductFactoryPriceIntervalRelDomainService.list( List<CustomProductFactoryPriceIntervalRelEntity> rels = customProductFactoryPriceIntervalRelDomainService.list(
new LambdaQueryWrapper<CustomProductFactoryPriceIntervalRelEntity>() new LambdaQueryWrapper<CustomProductFactoryPriceIntervalRelEntity>()
.in(CustomProductFactoryPriceIntervalRelEntity::getProductId, productIds)); .in(CustomProductFactoryPriceIntervalRelEntity::getProductId, productIds));
if (rels.isEmpty()) { if (CollectionUtils.isEmpty(rels)) {
return Collections.emptyMap(); return Collections.emptyMap();
} }
List<FactoryPriceIntervalRelSnakeVO> voList = rels.stream().map(r -> { List<FactoryPriceIntervalRelSnakeVO> voList = rels.stream().map(r -> {
...@@ -1128,7 +1149,21 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -1128,7 +1149,21 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
/** /**
* 从事务外的子项列表中计算主表的 factory_price / sales_price / sales_price_max * 从事务外的子项列表中计算主表的 factory_price / sales_price / sales_price_max
* <p> * <p>
* factory_price 取子项中的最小值,sales_price 取子项中的最大值,sales_price_max 取子项中的最大值。 * factory_price 取子项中的最大值,sales_price 取子项中的最大值,sales_price_max 取子项中的最大值。
* <p>
* 参考TS 代码如下:
* if (productInfo.factory_price == null || new Decimal(productInfo.factory_price).lessThan(new Decimal(item.factory_price))) {
* productInfo.factory_price = item.factory_price;
* }
* if (productInfo.sales_price == null || new Decimal(productInfo.sales_price).lessThan(new Decimal(item.sales_price))) {
* productInfo.sales_price = item.sales_price;
* }
* if (productInfo.sales_price_max == null || new Decimal(productInfo.sales_price_max).greaterThan(new Decimal(item.sales_price))) {
* productInfo.sales_price_max = item.sales_price;
* }
*
* saveFull 批量保存时使用
*
*/ */
private void computeProductPricesFromItems(CustomProductInfoSaveSnakeDTO dto) { private void computeProductPricesFromItems(CustomProductInfoSaveSnakeDTO dto) {
if (dto.getProductList() == null || dto.getProductList().isEmpty()) { if (dto.getProductList() == null || dto.getProductList().isEmpty()) {
...@@ -1171,6 +1206,8 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -1171,6 +1206,8 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
* <p> * <p>
* SaveDTO 使用驼峰命名,与 Entity 字段名一致,直接用默认 mapper 转换。 * SaveDTO 使用驼峰命名,与 Entity 字段名一致,直接用默认 mapper 转换。
* 对于蛇形命名的 {@link CustomProductInfoSaveSnakeDTO},调用方需先用 {@code BeanMapper.snakeCase()} 转为 SaveDTO。 * 对于蛇形命名的 {@link CustomProductInfoSaveSnakeDTO},调用方需先用 {@code BeanMapper.snakeCase()} 转为 SaveDTO。
* <p>
* saveFull 批量保存时使用
* *
* @param dto 保存 DTO(驼峰命名) * @param dto 保存 DTO(驼峰命名)
* @param sku 生成的 SKU(创建时传入,更新时传 null) * @param sku 生成的 SKU(创建时传入,更新时传 null)
...@@ -1188,6 +1225,8 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -1188,6 +1225,8 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
* 逐条保存子项并返回原始 SKU → 已保存实体的映射 * 逐条保存子项并返回原始 SKU → 已保存实体的映射
* <p> * <p>
* 逐条保存是为了获取每条记录的自增 ID,供后续工厂价格关联的 item_id 填充使用。 * 逐条保存是为了获取每条记录的自增 ID,供后续工厂价格关联的 item_id 填充使用。
* <p>
* saveFull 批量保存时使用
* *
* @return Map<原始DTO中的SKU, 已保存的实体(含自增ID和替换后的SKU)> * @return Map<原始DTO中的SKU, 已保存的实体(含自增ID和替换后的SKU)>
*/ */
...@@ -1222,6 +1261,9 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -1222,6 +1261,9 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
* 保存工厂价格关联(利用 itemMap 替换 item_sku 为生成的 SKU 并填充 item_id) * 保存工厂价格关联(利用 itemMap 替换 item_sku 为生成的 SKU 并填充 item_id)
* <p> * <p>
* 对齐 TS {@code save} * 对齐 TS {@code save}
* <p>
* saveFull 批量保存时使用
*
*/ */
private void saveFactoryPriceRels(List<FactoryPriceRelSnakeDTO> factoryPriceList, private void saveFactoryPriceRels(List<FactoryPriceRelSnakeDTO> factoryPriceList,
Integer productId, Integer productId,
...@@ -1669,7 +1711,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -1669,7 +1711,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
} }
/** /**
* 重建关联关系(先删后插) * 重建关联关系(先删后插),update接口使用
*/ */
private void rebuildRels(List<Integer> factoryIds, List<Integer> warehouseIds, private void rebuildRels(List<Integer> factoryIds, List<Integer> warehouseIds,
List<Integer> craftIds, List<Integer> diyUserIds, List<Integer> craftIds, List<Integer> diyUserIds,
...@@ -1750,7 +1792,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService { ...@@ -1750,7 +1792,7 @@ public class CustomProductInfoServiceImpl implements CustomProductInfoService {
} }
/** /**
* 将并行查询结果组装为 FullVO * 将并行查询结果组装为 FullVO, getByIdOrSku接口使用
*/ */
private CustomProductInfoSnakeVO buildProductFullVO(CustomProductInfoEntity entity, private CustomProductInfoSnakeVO buildProductFullVO(CustomProductInfoEntity entity,
List<CustomProductItemEntity> items, List<CustomProductItemEntity> items,
......
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.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.jomalls.custom.app.exception.ServiceException;
import com.jomalls.custom.app.vo.CustomProductWarehouseRelPageVO;
import com.jomalls.custom.app.vo.CustomProductWarehouseRelVO;
import com.jomalls.custom.app.service.CustomProductWarehouseRelService;
import com.jomalls.custom.app.utils.BeanMapper;
import com.jomalls.custom.app.utils.CustomAsserts;
import com.jomalls.custom.dal.entity.CustomProductWarehouseRelEntity;
import com.jomalls.custom.domain.service.CustomProductWarehouseRelDomainService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author Lizh
* @version 0.01
* @description: 接口实现
* @date 2026-05-29 10:43:29
*/
@Slf4j
@Service
public class CustomProductWarehouseRelServiceImpl implements CustomProductWarehouseRelService {
private final CustomProductWarehouseRelDomainService customProductWarehouseRelDomainService;
@Autowired
public CustomProductWarehouseRelServiceImpl(CustomProductWarehouseRelDomainService customProductWarehouseRelDomainService) {
this.customProductWarehouseRelDomainService = customProductWarehouseRelDomainService;
}
@Override
public List<CustomProductWarehouseRelVO> list(CustomProductWarehouseRelVO customProductWarehouseRelVO) {
QueryWrapper<CustomProductWarehouseRelEntity> queryWrapper = new QueryWrapper<>();
// TODO 根据业务条件组装入参
List<CustomProductWarehouseRelEntity> list = customProductWarehouseRelDomainService.list(queryWrapper);
return list.stream().map(e -> BeanMapper.mapper().convert(e, CustomProductWarehouseRelVO.class)).collect(Collectors.toList());
}
@Override
public IPage<CustomProductWarehouseRelVO> pageList(CustomProductWarehouseRelPageVO customProductWarehouseRelPageVO) {
CustomAsserts.nonNull(customProductWarehouseRelPageVO, "分页查询参数不能为空");
QueryWrapper<CustomProductWarehouseRelEntity> queryWrapper = new QueryWrapper<>();
// TODO 根据业务条件组装入参
IPage<CustomProductWarehouseRelEntity> page = customProductWarehouseRelDomainService.selectPage(queryWrapper, customProductWarehouseRelPageVO);
return page.convert(e -> BeanMapper.mapper().convert(e, CustomProductWarehouseRelVO.class));
}
@Override
public CustomProductWarehouseRelVO info(Integer id) {
CustomAsserts.nonNull(id, "主键id不能为空");
CustomProductWarehouseRelEntity customProductWarehouseRel = customProductWarehouseRelDomainService.getById(id);
return BeanMapper.mapper().convert(customProductWarehouseRel, CustomProductWarehouseRelVO.class);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void save(CustomProductWarehouseRelVO customProductWarehouseRelVO) {
CustomAsserts.nonNull(customProductWarehouseRelVO, "实体对象不能为空");
CustomProductWarehouseRelEntity customProductWarehouseRelEntity = BeanMapper.mapper().convert(customProductWarehouseRelVO, CustomProductWarehouseRelEntity.class);
try {
customProductWarehouseRelDomainService.save(customProductWarehouseRelEntity);
} catch (DuplicateKeyException e) {
log.info("[ CustomProductWarehouseRelServiceImpl save ] 实体对象唯一约束重复,请调整后再试!", e);
throw new ServiceException("实体对象唯一约束重复,请调整后再试!");
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public void updateById(CustomProductWarehouseRelVO customProductWarehouseRelVO) {
CustomAsserts.nonNull(customProductWarehouseRelVO, "实体对象不能为空");
CustomProductWarehouseRelEntity customProductWarehouseRel = BeanMapper.mapper().convert(customProductWarehouseRelVO, CustomProductWarehouseRelEntity.class);
try {
customProductWarehouseRelDomainService.updateById(customProductWarehouseRel);
} catch (DuplicateKeyException e) {
log.info("[ CustomProductWarehouseRelServiceImpl updateById ] 实体对象唯一约束重复,请调整后再试!", e);
throw new ServiceException("实体对象唯一约束重复,请调整后再试!");
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public void deleteById(Integer id) {
CustomAsserts.nonNull(id, "主键id不能为空");
customProductWarehouseRelDomainService.removeById(id);
}
/**
* 根据仓库ID获取关联列表
*/
@Override
public List<CustomProductWarehouseRelEntity> getListByWareHouseId(Long warehouseId) {
return customProductWarehouseRelDomainService.list(new LambdaQueryWrapper<CustomProductWarehouseRelEntity>()
.eq(CustomProductWarehouseRelEntity::getWarehouseId, warehouseId));
}
/**
* 根据产品ID获取关联列表
*/
@Override
public List<CustomProductWarehouseRelEntity> getListByProductId(Long productId) {
return customProductWarehouseRelDomainService.list(new LambdaQueryWrapper<CustomProductWarehouseRelEntity>()
.eq(CustomProductWarehouseRelEntity::getProductId, productId));
}
/**
* 根据产品ID获取仓库ID列表
*/
@Override
public List<Long> getWarehouseIdsByProductId(Long productId) {
List<CustomProductWarehouseRelEntity> rels = customProductWarehouseRelDomainService.list(new LambdaQueryWrapper<CustomProductWarehouseRelEntity>()
.eq(CustomProductWarehouseRelEntity::getProductId, productId)
.select(CustomProductWarehouseRelEntity::getWarehouseId)); // 只查询需要的字段以提高性能
if (CollectionUtils.isEmpty(rels)) {
return null;
}
return rels.stream()
.map(CustomProductWarehouseRelEntity::getWarehouseId)
.collect(Collectors.toList());
}
/**
* 根据产品ID删除关联
* 注意:原 TS 代码支持手动传入 transaction,Java 中通常使用 @Transactional 注解管理事务
*/
@Override
public void deleteByProductId(Integer productId) {
customProductWarehouseRelDomainService.remove(new LambdaQueryWrapper<CustomProductWarehouseRelEntity>()
.eq(CustomProductWarehouseRelEntity::getProductId, productId));
}
/**
* 批量保存产品与仓库的关联
* 逻辑:先删除该产品现有的所有关联,再批量插入新的
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void saveBatch(Integer productId, List<Long> warehouseIds) {
this.deleteByProductId(productId);
if (CollectionUtils.isEmpty(warehouseIds)) {
return;
}
List<CustomProductWarehouseRelEntity> relList = new ArrayList<>();
for (Long warehouseId : warehouseIds) {
CustomProductWarehouseRelEntity rel = new CustomProductWarehouseRelEntity();
rel.setProductId(productId);
rel.setWarehouseId(warehouseId);
relList.add(rel);
}
customProductWarehouseRelDomainService.saveBatch(relList);
}
}
...@@ -42,15 +42,14 @@ public class DiyUserServiceImpl implements DiyUserService { ...@@ -42,15 +42,14 @@ public class DiyUserServiceImpl implements DiyUserService {
if (StringUtils.isBlank(namespace)) { if (StringUtils.isBlank(namespace)) {
return null; return null;
} }
DbDiyUserEntity user = dbDiyUserDomainService.getByNamespace(namespace); return dbDiyUserDomainService.getByNamespace(namespace);
if (user == null) {
throw new ServiceException("用户不存在, namespace=" + namespace);
}
return user;
} }
@Override @Override
public DbDiyUserEntity getByUserMark(String userMark) { public DbDiyUserEntity getByUserMark(String userMark) {
if (StringUtils.isBlank(userMark)) {
return null;
}
return dbDiyUserDomainService.getByUserMark(userMark); return dbDiyUserDomainService.getByUserMark(userMark);
} }
......
...@@ -24,7 +24,7 @@ import java.util.List; ...@@ -24,7 +24,7 @@ import java.util.List;
*/ */
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public class PageAdaptter<T> implements Serializable { public class PageAdapter<T> implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
...@@ -51,8 +51,8 @@ public class PageAdaptter<T> implements Serializable { ...@@ -51,8 +51,8 @@ public class PageAdaptter<T> implements Serializable {
* @param <T> 数据类型 * @param <T> 数据类型
* @return 老服务格式的分页结果 * @return 老服务格式的分页结果
*/ */
public static <T> PageAdaptter<T> from(IPage<T> page) { public static <T> PageAdapter<T> from(IPage<T> page) {
return new PageAdaptter<>( return new PageAdapter<>(
page.getRecords(), page.getRecords(),
page.getCurrent(), page.getCurrent(),
page.getTotal(), page.getTotal(),
......
...@@ -8,6 +8,7 @@ import lombok.Data; ...@@ -8,6 +8,7 @@ import lombok.Data;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
/** /**
...@@ -50,11 +51,11 @@ public class CraftCenterEntity implements Serializable { ...@@ -50,11 +51,11 @@ public class CraftCenterEntity implements Serializable {
/** 工艺成本 */ /** 工艺成本 */
@TableField("craft_cost") @TableField("craft_cost")
private String craftCost; private BigDecimal craftCost;
/** 其他面的价格 */ /** 其他面的价格 */
@TableField("other_side_cost") @TableField("other_side_cost")
private String otherSideCost; private BigDecimal otherSideCost;
/** 是否启用(1启用,0未启用) */ /** 是否启用(1启用,0未启用) */
@TableField("craft_enable") @TableField("craft_enable")
......
...@@ -33,44 +33,6 @@ ...@@ -33,44 +33,6 @@
create_by, create_time, update_by, update_time, remark create_by, create_time, update_by, update_time, remark
</sql> </sql>
<!-- 根据用户名查询用户 -->
<select id="selectByUsername" resultMap="sysUserMap">
SELECT <include refid="tableColumns"/>
FROM sys_user
WHERE user_name = #{userName}
AND status = '0'
AND del_flag = '0'
</select>
<!-- 根据邮箱查询用户 -->
<select id="selectByEmail" resultMap="sysUserMap">
SELECT <include refid="tableColumns"/>
FROM sys_user
WHERE email = #{email}
AND status = '0'
AND del_flag = '0'
</select>
<!-- 分页查询用户列表 -->
<select id="selectUserPage" resultMap="sysUserMap">
SELECT <include refid="tableColumns"/>
FROM sys_user
WHERE del_flag = '0'
<if test="status != null">
AND status = #{status}
</if>
ORDER BY create_time DESC
</select>
<!-- 查询所有启用状态的用户 -->
<select id="selectAllActiveUsers" resultMap="sysUserMap">
SELECT <include refid="tableColumns"/>
FROM sys_user
WHERE status = '0'
AND del_flag = '0'
ORDER BY create_time DESC
</select>
<!-- 批量插入 --> <!-- 批量插入 -->
<insert id="insertBatchSomeColumn"> <insert id="insertBatchSomeColumn">
INSERT INTO sys_user (user_name, nick_name, user_type, INSERT INTO sys_user (user_name, nick_name, user_type,
......
...@@ -44,7 +44,7 @@ public class WebClientConfig { ...@@ -44,7 +44,7 @@ public class WebClientConfig {
public ConnectionProvider connectionProvider() { public ConnectionProvider connectionProvider() {
return ConnectionProvider.builder("custom-server-http-pool") return ConnectionProvider.builder("custom-server-http-pool")
.maxConnections(poolMaxConnections) .maxConnections(poolMaxConnections)
.pendingAcquireMaxCount(-1) .pendingAcquireMaxCount(100)
.pendingAcquireTimeout(Duration.ofMillis(poolAcquireTimeout)) .pendingAcquireTimeout(Duration.ofMillis(poolAcquireTimeout))
.maxIdleTime(Duration.ofSeconds(60)) .maxIdleTime(Duration.ofSeconds(60))
.maxLifeTime(Duration.ofMinutes(5)) .maxLifeTime(Duration.ofMinutes(5))
......
...@@ -27,44 +27,10 @@ import java.util.concurrent.atomic.AtomicReference; ...@@ -27,44 +27,10 @@ import java.util.concurrent.atomic.AtomicReference;
* 调用 admin API 获取商品分类数据。 * 调用 admin API 获取商品分类数据。
* 对齐 TS 项目 {@code baseCategoryInfoService}。 * 对齐 TS 项目 {@code baseCategoryInfoService}。
* *
* @author zhengcunwen
* @author Lizh (Java 迁移) * @author Lizh (Java 迁移)
* @since 2019-09-02 * @since 2019-09-02
*/ */
@Slf4j public interface SaasAdminService {
@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;
/** 分类列表缓存 TTL(毫秒):5 分钟 */
private static final long CACHE_TTL_MS = 5 * 60 * 1000;
private final RemoteApiClient remoteApiClient;
/** 分类列表本地缓存 */
private final AtomicReference<CacheEntry<List<CategoryInfoModel>>> categoryCache = new AtomicReference<>();
@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 列表批量查询属性(含属性值列表) * 根据属性 ID 列表批量查询属性(含属性值列表)
...@@ -74,115 +40,7 @@ public class SaasAdminService { ...@@ -74,115 +40,7 @@ public class SaasAdminService {
* @param ids 属性 ID,逗号分隔(如 "1,2,3") * @param ids 属性 ID,逗号分隔(如 "1,2,3")
* @return 属性列表,包含 valueList 和 skuProperty 等字段 * @return 属性列表,包含 valueList 和 skuProperty 等字段
*/ */
public List<PropertyModel> getPropertyByIds(String ids) { List<PropertyModel> getPropertyByIds(String ids);
if (!StringUtils.hasText(ids)) {
return Collections.emptyList();
}
try {
String url = adminBaseUrl + GET_PROPERTY_BY_IDS_URL + "?ids=" + ids;
ResponseEntity<SaasAdminApiResponseModel<List<PropertyModel>>> response = remoteApiClient.get(
url,
new ParameterizedTypeReference<>() {},
getHeader());
if (response != null && response.getBody() != null) {
log.debug("[ SaasAdminService ] getByIds 成功, ids={}, 返回: {}", ids, response.toString());
SaasAdminApiResponseModel<List<PropertyModel>> responseBody = response.getBody();
if (responseBody.getCode() == CodeEnum.SUCCESS.getCode()) {
return responseBody.getData();
}
}
log.warn("[ SaasAdminService ] getByIds 返回空, ids={}", ids);
return Collections.emptyList();
} catch (Exception e) {
log.error("[ SaasAdminService ] getByIds 调用失败, ids={}", ids, e);
return Collections.emptyList();
}
}
/**
* 获取分类树
* <p>
* 对齐 TS {@code getTree()}
*/
public List<CategoryInfoModel> getTree() {
try {
ResponseEntity<List<CategoryInfoModel>> response = remoteApiClient.get(
adminBaseUrl + GET_TREE_URL,
new ParameterizedTypeReference<>() {},
getHeader());
if (response != null && response.getBody() != null) {
return response.getBody();
}
} catch (Exception e) {
log.error("[ SaasAdminService ] getTree 调用失败", e);
}
return Collections.emptyList();
}
/**
* 根据 ID 列表批量查询
* <p>
* 对齐 TS {@code getByIds(ids)}
*/
public List<CategoryInfoModel> getByIds(String ids) {
if (ids == null || ids.isEmpty()) {
return Collections.emptyList();
}
try {
String url = adminBaseUrl + GET_BY_IDS_URL + "?ids=" + ids;
ResponseEntity<List<CategoryInfoModel>> response = remoteApiClient.get(
url,
new ParameterizedTypeReference<>() {},
getHeader());
if (response != null && response.getBody() != null) {
return response.getBody();
}
} catch (Exception e) {
log.error("[ SaasAdminService ] getByIds 调用失败, ids={}", ids, e);
}
return Collections.emptyList();
}
/**
* 获取带有风格属性的树形结构
* <p>
* 对齐 TS {@code treeList()}
*/
public List<CategoryInfoModel> treeList() {
try {
ResponseEntity<List<CategoryInfoModel>> response = remoteApiClient.get(
adminBaseUrl + GET_TREE_URL,
new ParameterizedTypeReference<>() {},
getHeader());
if (response != null && response.getBody() != null) {
return response.getBody();
}
} catch (Exception e) {
log.error("[ SaasAdminService ] treeList 调用失败", e);
}
return Collections.emptyList();
}
/**
* 根据 ID 查询单个分类
* <p>
* 对齐 TS {@code getById(id)}
*/
public CategoryInfoModel getById(Integer id) {
if (id == null) {
return null;
}
try {
String url = adminBaseUrl + GET_BY_ID_URL + "?id=" + id;
ResponseEntity<CategoryInfoModel> response = remoteApiClient.get(
url, CategoryInfoModel.class, null);
if (response != null) {
return response.getBody();
}
} catch (Exception e) {
log.error("[ SaasAdminService ] getById 调用失败, id={}", id, e);
}
return null;
}
/** /**
* 查询所有分类(带本地缓存,TTL 5 分钟) * 查询所有分类(带本地缓存,TTL 5 分钟)
...@@ -190,49 +48,5 @@ public class SaasAdminService { ...@@ -190,49 +48,5 @@ public class SaasAdminService {
* 对齐 TS {@code allList()}。分类数据不频繁变动, * 对齐 TS {@code allList()}。分类数据不频繁变动,
* 使用本地缓存避免每次分页查询都发起远程 HTTP 调用。 * 使用本地缓存避免每次分页查询都发起远程 HTTP 调用。
*/ */
public List<CategoryInfoModel> getAllList() { List<CategoryInfoModel> getAllList();
// 命中缓存且未过期 → 直接返回
CacheEntry<List<CategoryInfoModel>> entry = categoryCache.get();
if (entry != null && !entry.isExpired()) {
return entry.data;
}
// 未命中或已过期 → 远程调用刷新
try {
ResponseEntity<SaasAdminApiResponseModel<List<CategoryInfoModel>>> response = remoteApiClient.get(
adminBaseUrl + GET_ALL_LIST_URL,
new ParameterizedTypeReference<>() {},
getHeader());
if (response != null && response.getBody() != null) {
SaasAdminApiResponseModel<List<CategoryInfoModel>> responseBody = response.getBody();
if (responseBody.getCode() == CodeEnum.SUCCESS.getCode()) {
List<CategoryInfoModel> data = responseBody.getData();
categoryCache.set(new CacheEntry<>(data));
return data;
}
}
} catch (Exception e) {
log.error("[ SaasAdminService ] getAllList 调用失败", e);
}
// 远程调用失败但有旧缓存 → 降级返回旧缓存
if (entry != null) {
log.warn("[ SaasAdminService ] getAllList 远程失败,降级使用过期缓存");
return entry.data;
}
return Collections.emptyList();
}
/** 简单 TTL 缓存条目 */
private static class CacheEntry<T> {
final T data;
final long expireAt;
CacheEntry(T data) {
this.data = data;
this.expireAt = System.currentTimeMillis() + CACHE_TTL_MS;
}
boolean isExpired() {
return System.currentTimeMillis() > expireAt;
}
}
} }
package com.jomalls.custom.integrate.service.impl;
import com.jomalls.custom.enums.CodeEnum;
import com.jomalls.custom.integrate.client.RemoteApiClient;
import com.jomalls.custom.integrate.model.CategoryInfoModel;
import com.jomalls.custom.integrate.model.PropertyModel;
import com.jomalls.custom.integrate.model.SaasAdminApiResponseModel;
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.springframework.beans.factory.annotation.Value;
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;
import java.util.concurrent.atomic.AtomicReference;
/**
* 商品分类服务
* <p>
* 调用 admin API 获取商品分类数据。
* 对齐 TS 项目 {@code baseCategoryInfoService}。
*
* @author Lizh (Java 迁移)
* @since 2019-09-02
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class SaasAdminServiceImpl implements SaasAdminService {
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;
/** 分类列表缓存 TTL(毫秒):5 分钟 */
private static final long CACHE_TTL_MS = 5 * 60 * 1000;
private final RemoteApiClient remoteApiClient;
/** 分类列表本地缓存 */
private final AtomicReference<CacheEntry<List<CategoryInfoModel>>> categoryCache = new AtomicReference<>();
@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<PropertyModel> getPropertyByIds(String ids) {
if (!StringUtils.hasText(ids)) {
return Collections.emptyList();
}
try {
String url = adminBaseUrl + GET_PROPERTY_BY_IDS_URL + "?ids=" + ids;
ResponseEntity<SaasAdminApiResponseModel<List<PropertyModel>>> response = remoteApiClient.get(
url,
new ParameterizedTypeReference<>() {},
getHeader());
if (response != null && response.getBody() != null) {
log.debug("[ SaasAdminService ] getByIds 成功, ids={}, 返回: {}", ids, response.toString());
SaasAdminApiResponseModel<List<PropertyModel>> responseBody = response.getBody();
if (responseBody.getCode() == CodeEnum.SUCCESS.getCode()) {
return responseBody.getData();
}
}
log.warn("[ SaasAdminService ] getByIds 返回空, ids={}", ids);
return Collections.emptyList();
} catch (Exception e) {
log.error("[ SaasAdminService ] getByIds 调用失败, ids={}", ids, e);
return Collections.emptyList();
}
}
/**
* 查询所有分类(带本地缓存,TTL 5 分钟)
* <p>
* 对齐 TS {@code allList()}。分类数据不频繁变动,
* 使用本地缓存避免每次分页查询都发起远程 HTTP 调用。
*/
public List<CategoryInfoModel> getAllList() {
// 命中缓存且未过期 → 直接返回
CacheEntry<List<CategoryInfoModel>> entry = categoryCache.get();
if (entry != null && !entry.isExpired()) {
return entry.data;
}
// 未命中或已过期 → 远程调用刷新
try {
ResponseEntity<SaasAdminApiResponseModel<List<CategoryInfoModel>>> response = remoteApiClient.get(
adminBaseUrl + GET_ALL_LIST_URL,
new ParameterizedTypeReference<>() {},
getHeader());
if (response != null && response.getBody() != null) {
SaasAdminApiResponseModel<List<CategoryInfoModel>> responseBody = response.getBody();
if (responseBody.getCode() == CodeEnum.SUCCESS.getCode()) {
List<CategoryInfoModel> data = responseBody.getData();
categoryCache.set(new CacheEntry<>(data));
return data;
}
}
} catch (Exception e) {
log.error("[ SaasAdminService ] getAllList 调用失败", e);
}
// 远程调用失败但有旧缓存 → 降级返回旧缓存
if (entry != null) {
log.warn("[ SaasAdminService ] getAllList 远程失败,降级使用过期缓存");
return entry.data;
}
return Collections.emptyList();
}
/** 简单 TTL 缓存条目 */
private static class CacheEntry<T> {
final T data;
final long expireAt;
CacheEntry(T data) {
this.data = data;
this.expireAt = System.currentTimeMillis() + CACHE_TTL_MS;
}
boolean isExpired() {
return System.currentTimeMillis() > expireAt;
}
}
}
...@@ -3,7 +3,7 @@ package com.jomalls.custom.config; ...@@ -3,7 +3,7 @@ package com.jomalls.custom.config;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.jomalls.custom.enums.CodeEnum; import com.jomalls.custom.enums.CodeEnum;
import com.jomalls.custom.page.PageAdaptter; import com.jomalls.custom.page.PageAdapter;
import org.jspecify.annotations.NonNull; import org.jspecify.annotations.NonNull;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotatedElementUtils;
...@@ -63,7 +63,7 @@ public class RestResponseBodyConfig implements ResponseBodyAdvice<Object> { ...@@ -63,7 +63,7 @@ public class RestResponseBodyConfig implements ResponseBodyAdvice<Object> {
} else { } else {
// 分页结果转为老服务(TS)格式:records→list, current→curPage, total→totalRow, pages→totalPage // 分页结果转为老服务(TS)格式:records→list, current→curPage, total→totalRow, pages→totalPage
if (body instanceof IPage<?> page) { if (body instanceof IPage<?> page) {
body = PageAdaptter.from(page); body = PageAdapter.from(page);
} }
if (body instanceof String) { if (body instanceof String) {
try { try {
......
...@@ -8,7 +8,7 @@ spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver ...@@ -8,7 +8,7 @@ spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#最小空闲连接数 #最小空闲连接数
spring.datasource.hikari.minimum-idle=5 spring.datasource.hikari.minimum-idle=5
#最大连接池大小 #最大连接池大小
spring.datasource.hikari.maximum-pool-size=20 spring.datasource.hikari.maximum-pool-size=50
#连接超时时间(60秒) #连接超时时间(60秒)
spring.datasource.hikari.connection-timeout=60000 spring.datasource.hikari.connection-timeout=60000
#空闲连接超时时间(600秒) #空闲连接超时时间(600秒)
......
...@@ -5,7 +5,7 @@ server.servlet.context-path=/ ...@@ -5,7 +5,7 @@ server.servlet.context-path=/
## Tomcat配置 ## Tomcat配置
server.tomcat.uri-encoding=UTF-8 server.tomcat.uri-encoding=UTF-8
server.tomcat.accept-count=1000 server.tomcat.accept-count=1000
server.tomcat.threads.max=800 server.tomcat.threads.max=200
server.tomcat.threads.min-spare=100 server.tomcat.threads.min-spare=100
## Spring配置 ## Spring配置
...@@ -35,7 +35,7 @@ server.needAuthentication=true ...@@ -35,7 +35,7 @@ server.needAuthentication=true
token.header=Authorization token.header=Authorization
# 令牌密钥(兼容旧版本) # 令牌密钥(兼容旧版本)
token.secret=custom token.secret=custom
# 令牌有效期(默认30分钟) # 令牌有效期(单位:分钟)
token.expireTime=720 token.expireTime=720
# HTTP Client Configuration # HTTP Client Configuration
......
...@@ -69,7 +69,7 @@ public class CustomProductInfoController { ...@@ -69,7 +69,7 @@ public class CustomProductInfoController {
} }
@Operation(summary = "绑定默认模型", description = "设置商品的默认 DIY 模板") @Operation(summary = "绑定默认模型", description = "设置商品的默认 DIY 模板")
@GetMapping("/bindDefaultDiy") @PostMapping("/bindDefaultDiy")
public void bindDefaultDiy( public void bindDefaultDiy(
@Parameter(description = "商品 ID") @RequestParam Integer id, @Parameter(description = "商品 ID") @RequestParam Integer id,
@Parameter(description = "DIY 模板 ID") @RequestParam Integer diyId, @Parameter(description = "DIY 模板 ID") @RequestParam Integer diyId,
......
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