From 8fe00fbb7c146e1197c808b75a307a24a882175d Mon Sep 17 00:00:00 2001 From: Timi Date: Sat, 26 Sep 2026 09:58:49 +0800 Subject: [PATCH 1/3] update sms max customer --- .../implement/GaoOrderReminderServiceImplement.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoOrderReminderServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoOrderReminderServiceImplement.java index a08e4b2..96f402c 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoOrderReminderServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoOrderReminderServiceImplement.java @@ -46,6 +46,8 @@ public class GaoOrderReminderServiceImplement implements GaoOrderReminderService private static final String SMS_TEMPLATE_ID = "2725097"; private static final String ZONE_ID = "Asia/Shanghai"; private static final String INTERNAL_TEMPLATE_NAME = "GaoOrderReminder.ftl"; + private static final int MAX_SMS_CUSTOMER_NAME_COUNT = 6; + private static final int MAX_SMS_CUSTOMER_COUNT = 99; private final GaoOrderReminderMapper mapper; private final GaoUserMapper gaoUserMapper; @@ -262,10 +264,15 @@ public class GaoOrderReminderServiceImplement implements GaoOrderReminderService nameMap.putIfAbsent(order.getCustomerId(), displayCustomerName(order.getCustomerName())); } List nameList = new ArrayList<>(nameMap.values()); - if (nameList.size() <= 2) { - return String.join("、", nameList); + int customerCount = nameList.size(); + int displayCount = Math.min(customerCount, MAX_SMS_CUSTOMER_NAME_COUNT); + String customerNames = String.join("、", nameList.subList(0, displayCount)); + if (MAX_SMS_CUSTOMER_NAME_COUNT < customerCount) { + String customerCountText = MAX_SMS_CUSTOMER_COUNT < customerCount + ? "%d+".formatted(MAX_SMS_CUSTOMER_COUNT) : String.valueOf(customerCount); + return "%s等 %s 位客户".formatted(customerNames, customerCountText); } - return "%s、%s等 %d 位".formatted(nameList.getFirst(), nameList.get(1), nameList.size()); + return customerNames; } /// 获取待核销收款金额 -- 2.54.0 From 12fb82de340e9d2d9a2b7a52cd430c2c1722010f Mon Sep 17 00:00:00 2001 From: Timi Date: Mon, 5 Oct 2026 16:09:35 +0800 Subject: [PATCH 2/3] update gao product edit --- .../gao/controller/GaoProductController.java | 41 ++++ .../api/modules/gao/entity/GaoProductSku.java | 5 + .../gao/mapper/GaoProductEditMapper.java | 74 +++++++ .../gao/mapper/GaoProductSpecMapper.java | 3 + .../gao/mapper/GaoProductSpecValueMapper.java | 3 + .../gao/service/GaoProductSkuService.java | 3 + .../gao/service/GaoProductSpecService.java | 12 ++ .../implement/GaoProductServiceImplement.java | 179 ++++++++++++++++- .../GaoProductSkuServiceImplement.java | 180 +++++++++++++++++- .../GaoProductSpecServiceImplement.java | 68 +++++++ ...GaoProductSkuBaseUnitMigrationRequest.java | 19 ++ .../system/audit/OperationAuditAspect.java | 7 +- .../system/audit/OperationAuditContext.java | 45 +++++ .../timi-server/gao/GaoProductEditMapper.xml | 107 +++++++++++ .../timi-server/gao/GaoProductSpecMapper.xml | 1 + .../gao/GaoProductSpecValueMapper.xml | 1 + .../resources/templates/GaoOrderReminder.ftl | 1 - 17 files changed, 735 insertions(+), 14 deletions(-) create mode 100644 src/main/java/com/imyeyu/api/modules/gao/mapper/GaoProductEditMapper.java create mode 100644 src/main/java/com/imyeyu/api/modules/gao/vo/GaoProductSkuBaseUnitMigrationRequest.java create mode 100644 src/main/java/com/imyeyu/api/modules/system/audit/OperationAuditContext.java create mode 100644 src/main/resources/mapper/timi-server/gao/GaoProductEditMapper.xml diff --git a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoProductController.java b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoProductController.java index 101003b..1f19f65 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoProductController.java +++ b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoProductController.java @@ -149,6 +149,17 @@ public class GaoProductController { productService.disable(id); } + @AOPLog + @RequestRateLimit + @RequireGaoPermission(GaoPermissionCode.PRODUCT_UPDATE) + @PostMapping("/delete") + public void delete(@RequestParam String id) { + GaoProduct product = productService.get(id); + TimiException.required(product, "not found product"); + storeScopeService.checkStoreId(product.getStoreId()); + productService.destroy(id); + } + @AOPLog @RequestRateLimit @RequireGaoPermission(GaoPermissionCode.PRODUCT_READ) @@ -221,6 +232,21 @@ public class GaoProductController { return spec.getId(); } + @AOPLog + @RequestRateLimit + @RequireGaoPermission(GaoPermissionCode.PRODUCT_UPDATE) + @PostMapping("/spec/update") + public void specUpdate(@RequestBody GaoProductSpec request) { + TimiException.required(request, "not found product spec"); + TimiException.required(request.getId(), "not found product spec.id"); + String productId = specService.getProductIdBySpecId(request.getId()); + TimiException.required(productId, "产品规格不存在"); + GaoProduct product = productService.get(productId); + TimiException.required(product, "not found product"); + storeScopeService.checkStoreId(product.getStoreId()); + specService.updateSpec(request); + } + @AOPLog @RequestRateLimit @RequireGaoPermission(GaoPermissionCode.PRODUCT_UPDATE) @@ -236,6 +262,21 @@ public class GaoProductController { return value.getId(); } + @AOPLog + @RequestRateLimit + @RequireGaoPermission(GaoPermissionCode.PRODUCT_UPDATE) + @PostMapping("/spec/value/update") + public void specValueUpdate(@RequestBody GaoProductSpecValue request) { + TimiException.required(request, "not found product spec value"); + TimiException.required(request.getId(), "not found product spec value.id"); + String productId = specService.getProductIdBySpecValueId(request.getId()); + TimiException.required(productId, "产品规格值不存在"); + GaoProduct product = productService.get(productId); + TimiException.required(product, "not found product"); + storeScopeService.checkStoreId(product.getStoreId()); + specService.updateValue(request); + } + @AOPLog @RequestRateLimit @RequireGaoPermission(GaoPermissionCode.PRODUCT_READ) diff --git a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoProductSku.java b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoProductSku.java index 732cc79..bb85a9a 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoProductSku.java +++ b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoProductSku.java @@ -1,6 +1,7 @@ package com.imyeyu.api.modules.gao.entity; import com.imyeyu.api.modules.common.entity.Attachment; +import com.imyeyu.api.modules.gao.vo.GaoProductSkuBaseUnitMigrationRequest; import com.imyeyu.spring.annotation.table.Transient; import com.imyeyu.spring.entity.UUIDEntity; import lombok.Data; @@ -77,6 +78,10 @@ public class GaoProductSku extends UUIDEntity { @Transient private List unitList; + /// 基础单位迁移请求,仅用于 SKU 更新,不映射数据库列 + @Transient + private GaoProductSkuBaseUnitMigrationRequest baseUnitMigrationRequest; + /// SKU 附件列表 @Transient private List attachmentList; diff --git a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoProductEditMapper.java b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoProductEditMapper.java new file mode 100644 index 0000000..406eca9 --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoProductEditMapper.java @@ -0,0 +1,74 @@ +package com.imyeyu.api.modules.gao.mapper; + +import com.fasterxml.jackson.databind.JsonNode; +import org.apache.ibatis.annotations.Param; + +import java.math.BigDecimal; + +/// 产品编辑、引用检查与单位迁移 Mapper +public interface GaoProductEditMapper { + + int countProductReferences(@Param("productId") String productId); + + int countSkuReferences(@Param("skuId") String skuId); + + int countUnitReferences(@Param("unitId") String unitId); + + int countMigrationPrecisionLoss(@Param("skuId") String skuId, @Param("targetUnitId") String targetUnitId, + @Param("ratio") BigDecimal ratio); + + int updateProductOrderSnapshots(@Param("productId") String productId, @Param("productName") String productName); + + int updateProductFulfillmentSnapshots(@Param("productId") String productId, @Param("productName") String productName); + + int updateProductStockRecordSnapshots(@Param("productId") String productId, @Param("productName") String productName); + + int updateProductPackageSnapshots(@Param("productId") String productId, @Param("productName") String productName); + + int updateProductRedemptionSnapshots(@Param("productId") String productId, @Param("productName") String productName); + + int updateSkuOrderSnapshots(@Param("skuId") String skuId, @Param("skuCode") String skuCode, + @Param("skuName") String skuName, @Param("specSummary") String specSummary, + @Param("skuSpecSnapshot") JsonNode skuSpecSnapshot); + + int updateSkuFulfillmentSnapshots(@Param("skuId") String skuId, @Param("skuCode") String skuCode, + @Param("skuName") String skuName, @Param("specSummary") String specSummary, + @Param("skuSpecSnapshot") JsonNode skuSpecSnapshot); + + int updateSkuStockRecordSnapshots(@Param("skuId") String skuId, @Param("skuCode") String skuCode, + @Param("skuName") String skuName, @Param("specSummary") String specSummary, + @Param("skuSpecSnapshot") JsonNode skuSpecSnapshot); + + int updateSkuPackageSnapshots(@Param("skuId") String skuId, @Param("skuCode") String skuCode, + @Param("skuName") String skuName, @Param("specSummary") String specSummary); + + int updateSkuRedemptionSnapshots(@Param("skuId") String skuId, @Param("skuCode") String skuCode, + @Param("skuName") String skuName, @Param("skuSpecSnapshot") JsonNode skuSpecSnapshot); + + int updateUnitOrderSnapshots(@Param("unitId") String unitId, @Param("unitName") String unitName); + + int updateUnitFulfillmentSnapshots(@Param("unitId") String unitId, @Param("unitName") String unitName); + + int updateUnitStockRecordSnapshots(@Param("unitId") String unitId, @Param("unitName") String unitName); + + int updateUnitPackageSnapshots(@Param("unitId") String unitId, @Param("unitName") String unitName); + + int updateUnitRedemptionSnapshots(@Param("unitId") String unitId, @Param("unitName") String unitName); + + int scaleSkuMinimumStock(@Param("skuId") String skuId, @Param("ratio") BigDecimal ratio); + + int scaleProductStock(@Param("skuId") String skuId, @Param("ratio") BigDecimal ratio); + + int scaleSalesOrderItems(@Param("skuId") String skuId, @Param("ratio") BigDecimal ratio); + + int scaleFulfillmentItems(@Param("skuId") String skuId, @Param("ratio") BigDecimal ratio); + + int scaleStockRecords(@Param("skuId") String skuId, @Param("ratio") BigDecimal ratio); + + int scalePackageConfigItems(@Param("skuId") String skuId, @Param("ratio") BigDecimal ratio); + + int scalePointRedemptions(@Param("skuId") String skuId, @Param("ratio") BigDecimal ratio); + + int scalePointRedemptionReturns(@Param("skuId") String skuId, @Param("ratio") BigDecimal ratio); + +} diff --git a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoProductSpecMapper.java b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoProductSpecMapper.java index bc5eef0..c82bf46 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoProductSpecMapper.java +++ b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoProductSpecMapper.java @@ -24,4 +24,7 @@ public interface GaoProductSpecMapper extends BaseMapper /// 查询规格项所属产品 String selectProductIdBySpecId(@Param("specId") String specId); + + /// 更新规格名称和排序 + int updateSpec(GaoProductSpec spec); } diff --git a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoProductSpecValueMapper.java b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoProductSpecValueMapper.java index 0caba09..2639994 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoProductSpecValueMapper.java +++ b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoProductSpecValueMapper.java @@ -18,4 +18,7 @@ public interface GaoProductSpecValueMapper extends BaseMapper selectBySpecIdList(@Param("specIdList") Collection specIdList); + + /// 更新规格值名称和排序 + int updateValue(GaoProductSpecValue value); } diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/GaoProductSkuService.java b/src/main/java/com/imyeyu/api/modules/gao/service/GaoProductSkuService.java index aaacd35..42f4046 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/GaoProductSkuService.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/GaoProductSkuService.java @@ -59,6 +59,9 @@ public interface GaoProductSkuService extends BaseService /// @return SKU 规格结构快照;无规格时返回空数组 JsonNode getSkuSpecSnapshot(GaoProductSku sku); + /// 按当前 SKU 规格资料刷新所有历史快照 + void refreshSpecHistorySnapshots(String productId); + /// 停用 SKU void disable(String id); } diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/GaoProductSpecService.java b/src/main/java/com/imyeyu/api/modules/gao/service/GaoProductSpecService.java index 353ccbc..f5f0acb 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/GaoProductSpecService.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/GaoProductSpecService.java @@ -17,9 +17,21 @@ public interface GaoProductSpecService { /// 查询规格项所属产品 ID String getProductIdBySpecId(String specId); + /// 查询规格值所属产品 ID + String getProductIdBySpecValueId(String specValueId); + /// 创建规格项 void createSpec(GaoProductSpec spec); + /// 修正规格名称和排序 + void updateSpec(GaoProductSpec spec); + /// 创建规格值 void createValue(GaoProductSpecValue value); + + /// 修正规格值名称和排序 + void updateValue(GaoProductSpecValue value); + + /// 删除产品下的规格数据,仅用于产品物理删除 + void destroyByProductId(String productId); } diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoProductServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoProductServiceImplement.java index e303c13..92c165f 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoProductServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoProductServiceImplement.java @@ -9,7 +9,9 @@ import com.imyeyu.api.modules.gao.entity.GaoProductSpec; import com.imyeyu.api.modules.gao.entity.GaoProductSpecValue; import com.imyeyu.api.modules.gao.entity.GaoProductSku; import com.imyeyu.api.modules.gao.entity.GaoProductStock; +import com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit; import com.imyeyu.api.modules.gao.mapper.GaoProductMapper; +import com.imyeyu.api.modules.gao.mapper.GaoProductEditMapper; import com.imyeyu.api.modules.gao.mapper.GaoProductStockMapper; import com.imyeyu.api.modules.gao.service.GaoProductCategoryService; import com.imyeyu.api.modules.gao.service.GaoProductService; @@ -17,6 +19,7 @@ import com.imyeyu.api.modules.gao.service.GaoProductSkuService; import com.imyeyu.api.modules.gao.service.GaoProductSpecService; import com.imyeyu.api.modules.gao.vo.GaoProductPage; import com.imyeyu.api.modules.system.audit.AuditOperation; +import com.imyeyu.api.modules.system.audit.OperationAuditContext; import com.imyeyu.api.modules.system.audit.OperationAudit; import com.imyeyu.api.modules.user.service.UserLoginService; import com.imyeyu.api.util.PinyinUtil; @@ -38,6 +41,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -50,6 +54,7 @@ import java.util.stream.Collectors; public class GaoProductServiceImplement extends AbstractEntityService implements GaoProductService { private final GaoProductMapper mapper; + private final GaoProductEditMapper editMapper; private final GaoProductStockMapper stockMapper; private final AttachmentService attachmentService; private final ObjectProvider categoryServiceProvider; @@ -111,7 +116,7 @@ public class GaoProductServiceImplement extends AbstractEntityService specValueIdMap = saveSpecs(product, product.getSpecList()); normalizeSkuSpecValueIds(product.getSkuList(), specValueIdMap); - saveSkus(product, product.getSkuList()); + saveSkus(product, product.getSkuList(), true); attachmentService.updateByBizId(Attachment.BizType.GAO_PRODUCT, product.getId(), product.getAttachmentList()); } @@ -123,6 +128,23 @@ public class GaoProductServiceImplement extends AbstractEntityService existingSkuList = skuServiceProvider.getObject().listByProductId(product.getId()); + boolean hasHistory = editMapper.countProductReferences(product.getId()) > 0 + || existingSkuList.stream() + .anyMatch(item -> editMapper.countSkuReferences(item.getId()) > 0 + || stockMapper.selectByStoreIdAndSkuIdList(storeId, List.of(item.getId())).stream() + .anyMatch(stock -> isNonZero(stock.getOnHandQuantity()) || isNonZero(stock.getReservedQuantity()))); + if (hasHistory && isHistoricalCorrectionOnly(product, request, existingSkuList)) { + OperationAuditContext.skipSuccessAudit(); + } + if (hasHistory && (request.getProductType() != null && request.getProductType() != product.getProductType() + || request.getStockEnabled() != null && !request.getStockEnabled().equals(product.getStockEnabled()))) { + throw new TimiException(TimiCode.ERROR, "产品已有历史业务记录,不能修改产品类型或库存管理属性"); + } if (request.getProductType() == GaoProduct.ProductType.SERVICE && product.getProductType() != GaoProduct.ProductType.SERVICE) { verifyNoReservedStock(product); } @@ -138,13 +160,128 @@ public class GaoProductServiceImplement extends AbstractEntityService currentSkuList) { + if (!Objects.equals(current.getCategoryId(), request.getCategoryId()) + || TimiJava.defaultIfNull(request.getProductType(), current.getProductType()) != current.getProductType() + || !Objects.equals(TimiJava.defaultIfNull(request.getStockEnabled(), current.getStockEnabled()), current.getStockEnabled()) + || TimiJava.defaultIfNull(request.getStatus(), current.getStatus()) != current.getStatus() + || !Objects.equals(TimiJava.defaultIfNull(request.getSort(), current.getSort()), current.getSort()) + || !sameText(current.getRemark(), request.getRemark())) { + return false; + } + if (request.getAttachmentList() != null && !sameAttachmentList( + attachmentService.listByBizId(Attachment.BizType.GAO_PRODUCT, current.getId()), request.getAttachmentList())) { + return false; + } + if (request.getSkuList() == null) { + return true; + } + if (request.getSkuList().size() != currentSkuList.size()) { + return false; + } + Map currentById = currentSkuList.stream() + .collect(Collectors.toMap(GaoProductSku::getId, item -> item)); + Set requestedIdSet = new HashSet<>(); + for (GaoProductSku requested : request.getSkuList()) { + GaoProductSku existing = requested.getId() == null ? null : currentById.get(requested.getId()); + if (existing == null || !requestedIdSet.add(requested.getId()) + || !isHistoricalSkuCorrectionOnly(existing, requested)) { + return false; + } + } + return true; + } + + private boolean isHistoricalSkuCorrectionOnly(GaoProductSku current, GaoProductSku request) { + if (!Objects.equals(normalizeText(current.getBarcode()), normalizeText(request.getBarcode())) + || !Objects.equals(TimiJava.defaultIfNull(request.getStockEnabled(), current.getStockEnabled()), current.getStockEnabled()) + || !Objects.equals(TimiJava.defaultIfNull(request.getAllowDecimalQuantity(), current.getAllowDecimalQuantity()), current.getAllowDecimalQuantity()) + || !Objects.equals(current.getMinStockQuantity(), request.getMinStockQuantity()) + || TimiJava.defaultIfNull(request.getStatus(), current.getStatus()) != current.getStatus() + || !Objects.equals(TimiJava.defaultIfNull(request.getSort(), current.getSort()), current.getSort()) + || !sameText(current.getRemark(), request.getRemark()) + || request.getBaseUnitMigrationRequest() != null + || request.getAttachmentList() != null && !sameAttachmentList(current.getAttachmentList(), request.getAttachmentList())) { + return false; + } + List currentSpecValueIdList = current.getSpecValueIdList(); + List requestSpecValueIdList = request.getSpecValueIdList(); + if (requestSpecValueIdList != null && !new HashSet<>(currentSpecValueIdList).equals(new HashSet<>(requestSpecValueIdList))) { + return false; + } + return request.getUnitList() == null || isHistoricalUnitNameCorrectionOnly(current.getUnitList(), request.getUnitList()); + } + + private boolean isHistoricalUnitNameCorrectionOnly(List currentList, List requestList) { + if (requestList.size() != currentList.size()) { + return false; + } + Map currentById = currentList.stream() + .collect(Collectors.toMap(GaoProductSkuUnit::getId, item -> item)); + Set requestedIdSet = new HashSet<>(); + for (GaoProductSkuUnit requested : requestList) { + GaoProductSkuUnit existing = requested.getId() == null ? null : currentById.get(requested.getId()); + if (existing == null || !requestedIdSet.add(requested.getId()) + || !Objects.equals(existing.getBaseQuantityPerUnit(), requested.getBaseQuantityPerUnit()) + || !Objects.equals(existing.getSalePrice(), requested.getSalePrice()) + || !Objects.equals(existing.getIsBase(), requested.getIsBase()) + || !Objects.equals(existing.getAllowPurchase(), requested.getAllowPurchase()) + || !Objects.equals(existing.getAllowSale(), requested.getAllowSale()) + || !Objects.equals(existing.getSort(), requested.getSort())) { + return false; + } + } + return true; + } + + private boolean sameAttachmentList(List currentList, List requestList) { + if (currentList == null || requestList == null || currentList.size() != requestList.size()) { + return false; + } + for (int index = 0; index < currentList.size(); index++) { + Attachment current = currentList.get(index); + Attachment requested = requestList.get(index); + if (!Objects.equals(current.getId(), requested.getId()) + || !Objects.equals(current.getBizType(), requested.getBizType()) + || !Objects.equals(current.getBizId(), requested.getBizId()) + || !Objects.equals(current.getAttachType(), requested.getAttachType()) + || !Objects.equals(current.getMongoId(), requested.getMongoId()) + || !Objects.equals(current.getTitle(), requested.getTitle()) + || !Objects.equals(current.getName(), requested.getName()) + || !Objects.equals(current.getMimeType(), requested.getMimeType()) + || !Objects.equals(current.getMetadata(), requested.getMetadata()) + || !Objects.equals(current.getSize(), requested.getSize()) + || !Objects.equals(current.getMd5(), requested.getMd5()) + || !Objects.equals(current.getIsDestroyed(), requested.getIsDestroyed()) + || !Objects.equals(current.getDestroyAt(), requested.getDestroyAt())) { + return false; + } + } + return true; + } + + private boolean sameText(String current, String request) { + return Objects.equals(normalizeText(current), normalizeText(request)); + } + + private String normalizeText(String value) { + return value == null ? null : value.trim(); + } + private void verifyNoReservedStock(GaoProduct product) { List skuIdList = skuServiceProvider.getObject().listByProductId(product.getId()).stream() .map(GaoProductSku::getId) @@ -158,6 +295,10 @@ public class GaoProductServiceImplement extends AbstractEntityService skuList) { + private void saveSkus(GaoProduct product, List skuList, boolean createDefault) { List sourceList = skuList; - if (sourceList == null || sourceList.isEmpty()) { + if ((sourceList == null || sourceList.isEmpty()) && createDefault) { com.imyeyu.api.modules.gao.entity.GaoProductSku sku = new com.imyeyu.api.modules.gao.entity.GaoProductSku(); sku.setProductId(product.getId()); sku.setCode(product.getCode()); sku.setName(product.getName()); com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit unit = new com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit(); unit.setName("件"); unit.setIsBase(true); unit.setBaseQuantityPerUnit(BigDecimal.ONE); sku.setUnitList(List.of(unit)); sourceList = List.of(sku); + } else if (sourceList == null) { + sourceList = List.of(); } + List existingList = skuServiceProvider.getObject().listByProductId(product.getId()); + Map existingById = existingList.stream() + .collect(Collectors.toMap(com.imyeyu.api.modules.gao.entity.GaoProductSku::getId, item -> item)); + Set retainedIdSet = new HashSet<>(); for (com.imyeyu.api.modules.gao.entity.GaoProductSku source : sourceList) { source.setProductId(product.getId()); - if (source.getId() == null) skuServiceProvider.getObject().create(source); else skuServiceProvider.getObject().update(source); + if (source.getId() == null) { + skuServiceProvider.getObject().create(source); + } else { + TimiException.required(existingById.get(source.getId()), "SKU 不存在或不属于当前产品"); + retainedIdSet.add(source.getId()); + skuServiceProvider.getObject().update(source); + } + } + for (com.imyeyu.api.modules.gao.entity.GaoProductSku existing : existingList) { + if (!retainedIdSet.contains(existing.getId())) { + skuServiceProvider.getObject().destroy(existing.getId()); + } } } diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoProductSkuServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoProductSkuServiceImplement.java index 965575f..3bac3f1 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoProductSkuServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoProductSkuServiceImplement.java @@ -11,16 +11,22 @@ import com.imyeyu.api.modules.gao.entity.GaoProduct; import com.imyeyu.api.modules.gao.entity.GaoProductSku; import com.imyeyu.api.modules.gao.entity.GaoProductSkuSpecValue; import com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit; +import com.imyeyu.api.modules.gao.entity.GaoProductStock; import com.imyeyu.api.modules.gao.entity.GaoProductSpec; import com.imyeyu.api.modules.gao.entity.GaoProductSpecValue; import com.imyeyu.api.modules.gao.mapper.GaoProductSkuMapper; +import com.imyeyu.api.modules.gao.mapper.GaoProductEditMapper; +import com.imyeyu.api.modules.gao.mapper.GaoProductStockMapper; import com.imyeyu.api.modules.gao.mapper.GaoProductSkuSpecValueMapper; import com.imyeyu.api.modules.gao.mapper.GaoProductSkuUnitMapper; import com.imyeyu.api.modules.gao.mapper.GaoProductSpecMapper; import com.imyeyu.api.modules.gao.mapper.GaoProductSpecValueMapper; import com.imyeyu.api.modules.gao.service.GaoProductService; import com.imyeyu.api.modules.gao.service.GaoProductSkuService; +import com.imyeyu.api.modules.gao.vo.GaoProductSkuBaseUnitMigrationRequest; import com.imyeyu.api.modules.gao.vo.GaoProductSkuUnitSelection; +import com.imyeyu.api.modules.system.entity.Logger; +import com.imyeyu.api.modules.system.service.LoggerService; import com.imyeyu.api.modules.user.service.UserLoginService; import com.imyeyu.java.TimiJava; import com.imyeyu.java.bean.timi.TimiCode; @@ -55,11 +61,14 @@ public class GaoProductSkuServiceImplement extends AbstractEntityService productServiceProvider; private final UserLoginService userLoginService; @@ -190,6 +199,19 @@ public class GaoProductSkuServiceImplement extends AbstractEntityService existingUnitList = unitMapper.selectBySkuId(sku.getId()); + int referenceCount = editMapper.countSkuReferences(sku.getId()); + List existingSpecValueIds = currentSpecValueIds(sku.getId()); + if (referenceCount > 0 && request.getSpecValueIdList() != null + && !new HashSet<>(existingSpecValueIds).equals(new HashSet<>(request.getSpecValueIdList()))) { + throw new TimiException(TimiCode.ERROR, "SKU 已有历史业务记录,不能更换规格组合;请新建 SKU"); + } + if (referenceCount > 0 && request.getStockEnabled() != null && !request.getStockEnabled().equals(sku.getStockEnabled())) { + throw new TimiException(TimiCode.ERROR, "SKU 已有历史业务记录,不能修改库存管理属性"); + } + GaoProductSkuUnit oldBaseUnit = existingUnitList.stream().filter(unit -> Boolean.TRUE.equals(unit.getIsBase())).findFirst().orElseThrow(); + List requestedUnitList = request.getUnitList() == null ? existingUnitList : request.getUnitList(); + GaoProductSkuUnit requestedBaseUnit = requestedUnitList.stream().filter(unit -> Boolean.TRUE.equals(unit.getIsBase())).findFirst().orElse(null); + boolean baseUnitChanged = requestedBaseUnit == null || !oldBaseUnit.getId().equals(requestedBaseUnit.getId()); + GaoProductSkuBaseUnitMigrationRequest migrationRequest = request.getBaseUnitMigrationRequest(); + BigDecimal migrationRatio = null; + if (baseUnitChanged) { + migrationRatio = validateBaseUnitMigration(oldBaseUnit, requestedBaseUnit, migrationRequest, sku.getId()); + } else if (migrationRequest != null) { + throw new TimiException(TimiCode.ERROR, "新旧基础单位相同,不需要提交基础单位迁移比例"); + } + if (request.getUnitList() != null && !baseUnitChanged) { + validateUnitConversionUnchanged(request.getUnitList(), existingUnitList); + } sku.setCode(TimiJava.defaultIfNull(request.getCode(), sku.getCode())); sku.setBarcode(request.getBarcode()); sku.setName(TimiJava.defaultIfNull(request.getName(), sku.getName())); @@ -218,18 +264,49 @@ public class GaoProductSkuServiceImplement extends AbstractEntityService 0) { + throw new TimiException(TimiCode.ERROR, "基础单位迁移会产生超出字段精度的数量,未执行任何变更"); + } + editMapper.scaleSkuMinimumStock(sku.getId(), migrationRatio); + editMapper.scaleProductStock(sku.getId(), migrationRatio); + editMapper.scaleSalesOrderItems(sku.getId(), migrationRatio); + editMapper.scaleFulfillmentItems(sku.getId(), migrationRatio); + editMapper.scaleStockRecords(sku.getId(), migrationRatio); + editMapper.scalePackageConfigItems(sku.getId(), migrationRatio); + editMapper.scalePointRedemptions(sku.getId(), migrationRatio); + editMapper.scalePointRedemptionReturns(sku.getId(), migrationRatio); + } prepare(sku, sku.getId()); super.update(sku); if (request.getSpecValueIdList() != null) { saveSpecValues(sku.getId(), sku.getSpecValueIdList()); } if (request.getUnitList() != null) { - saveUnits(sku.getId(), sku.getUnitList(), unitMapper.selectBySkuId(sku.getId())); + saveUnits(sku.getId(), sku.getUnitList(), existingUnitList); } if (request.getAttachmentList() != null) { attachmentService.updateByBizId(Attachment.BizType.GAO_PRODUCT_SKU, sku.getId(), request.getAttachmentList()); } + JsonNode skuSpecSnapshot = getSkuSpecSnapshot(sku); + editMapper.updateSkuOrderSnapshots(sku.getId(), sku.getCode(), sku.getName(), sku.getSpecSummary(), skuSpecSnapshot); + editMapper.updateSkuFulfillmentSnapshots(sku.getId(), sku.getCode(), sku.getName(), sku.getSpecSummary(), skuSpecSnapshot); + editMapper.updateSkuStockRecordSnapshots(sku.getId(), sku.getCode(), sku.getName(), sku.getSpecSummary(), skuSpecSnapshot); + editMapper.updateSkuPackageSnapshots(sku.getId(), sku.getCode(), sku.getName(), sku.getSpecSummary()); + editMapper.updateSkuRedemptionSnapshots(sku.getId(), sku.getCode(), sku.getName(), skuSpecSnapshot); + if (migrationRatio != null) { + GaoProductSkuUnit migratedTarget = unitMapper.selectBySkuId(sku.getId()).stream() + .filter(unit -> unit.getIsBase() && unit.getName().equals(requestedBaseUnit.getName().trim())) + .findFirst().orElseThrow(); + Logger migrationLog = new Logger(Logger.Module.GAO, Logger.Level.INFO, "GAO_PRODUCT_SKU_BASE_UNIT_MIGRATION"); + migrationLog.setContent("skuId=%s;oldBaseUnitId=%s;oldBaseUnitName=%s;newBaseUnitId=%s;newBaseUnitName=%s;oldToNewBaseRatio=%s".formatted( + sku.getId(), oldBaseUnit.getId(), oldBaseUnit.getName(), migratedTarget.getId(), migratedTarget.getName(), migrationRatio.toPlainString())); + migrationLog.setResult(sku.getId()); + loggerService.createSync(migrationLog); + } } @Transactional(TimiServerDBConfig.ROLLBACKER) @@ -249,8 +326,24 @@ public class GaoProductSkuServiceImplement extends AbstractEntityService stockList = stockMapper.selectByStoreIdAndSkuIdList(sku.getStoreId(), List.of(id)); + TimiException.requiredTrue(stockList.stream().allMatch(stock -> isZero(stock.getOnHandQuantity()) && isZero(stock.getReservedQuantity())), + "SKU 存在库存或预占库存,不能物理移除"); + for (GaoProductSkuUnit unit : unitMapper.selectBySkuId(id)) { + TimiException.requiredTrue(editMapper.countUnitReferences(unit.getId()) == 0, "SKU 单位存在历史业务引用,不能物理移除 SKU"); + unitMapper.destroy(unit.getId()); + } + skuSpecValueMapper.deleteBySkuId(id); + for (GaoProductStock stock : stockList) { + stockMapper.destroy(stock.getId()); + } + attachmentService.updateByBizId(Attachment.BizType.GAO_PRODUCT_SKU, id, List.of()); + super.destroy(id); } private void prepare(GaoProductSku sku, String excludeId) { @@ -326,6 +419,7 @@ public class GaoProductSkuServiceImplement extends AbstractEntityService= unit.getBaseQuantityPerUnit().scale(), "基础单位数量系数最多保留六位小数"); + TimiException.requiredTrue(12 >= unit.getBaseQuantityPerUnit().precision() - unit.getBaseQuantityPerUnit().scale(), "基础单位数量系数超出最大值"); if (source.getSalePrice() != null) { TimiException.requiredTrue(0 <= source.getSalePrice(), "单位建议售价不能为负数"); } @@ -345,6 +439,69 @@ public class GaoProductSkuServiceImplement extends AbstractEntityService= ratio.scale(), "基础单位迁移比例最多保留六位小数"); + TimiException.requiredTrue(12 >= ratio.precision() - ratio.scale(), "基础单位迁移比例超出最大值"); + return ratio; + } + + private void applyBaseUnitMigration(GaoProductSku sku, GaoProductSkuUnit oldBaseUnit, + GaoProductSkuUnit newBaseUnit, List existingUnitList, + GaoProductSkuBaseUnitMigrationRequest request, BigDecimal ratio) { + Map existingById = existingUnitList.stream() + .collect(Collectors.toMap(GaoProductSkuUnit::getId, item -> item)); + for (GaoProductSkuUnit unit : sku.getUnitList()) { + boolean target = newBaseUnit.getId() != null + ? newBaseUnit.getId().equals(unit.getId()) + : newBaseUnit.getName().trim().equals(unit.getName().trim()); + if (target) { + unit.setBaseQuantityPerUnit(ONE); + continue; + } + GaoProductSkuUnit existing = unit.getId() == null ? null : existingById.get(unit.getId()); + BigDecimal oldFactor = existing == null + ? TimiJava.defaultIfNull(unit.getBaseQuantityPerUnit(), ONE) + : existing.getBaseQuantityPerUnit(); + BigDecimal newFactor = oldFactor.multiply(ratio).stripTrailingZeros(); + TimiException.requiredTrue(6 >= Math.max(newFactor.scale(), 0), "单位换算系数迁移后超过六位小数,请调整换算比例"); + unit.setBaseQuantityPerUnit(newFactor); + } + if (sku.getMinStockQuantity() != null) { + BigDecimal migratedMinimumStock = sku.getMinStockQuantity().multiply(ratio).stripTrailingZeros(); + TimiException.requiredTrue(3 >= Math.max(migratedMinimumStock.scale(), 0), "最低库存迁移后超过三位小数,请调整换算比例"); + sku.setMinStockQuantity(migratedMinimumStock); + } + } + + private void validateUnitConversionUnchanged(List requestedUnitList, + List existingUnitList) { + Map existingById = existingUnitList.stream() + .collect(Collectors.toMap(GaoProductSkuUnit::getId, item -> item)); + for (GaoProductSkuUnit requested : requestedUnitList) { + GaoProductSkuUnit existing = requested.getId() == null ? null : existingById.get(requested.getId()); + BigDecimal requestedFactor = TimiJava.defaultIfNull(requested.getBaseQuantityPerUnit(), ONE); + if (existing != null && existing.getBaseQuantityPerUnit().compareTo(requestedFactor) != 0) { + throw new TimiException(TimiCode.ERROR, "已保存单位的换算系数不能直接修改;切换基础单位时请提交迁移比例"); + } + } + } + + private boolean isZero(BigDecimal value) { + return value == null || BigDecimal.ZERO.compareTo(value) == 0; + } + private void saveUnits(String skuId, List unitList, List existingUnitList) { Map existingById = existingUnitList.stream().collect(Collectors.toMap(GaoProductSkuUnit::getId, item -> item)); Set retainedIdSet = new HashSet<>(); @@ -368,10 +525,21 @@ public class GaoProductSkuServiceImplement extends AbstractEntityService retainedIdSet.contains(existing.getId())), - "已保存的 SKU 单位不能删除,请关闭允许销售和允许采购"); } private void saveSpecValues(String skuId, List specValueIdList) { diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoProductSpecServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoProductSpecServiceImplement.java index 15669e8..f4d23f8 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoProductSpecServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoProductSpecServiceImplement.java @@ -1,5 +1,6 @@ package com.imyeyu.api.modules.gao.service.implement; +import com.imyeyu.api.config.dbsource.TimiServerDBConfig; import com.imyeyu.api.modules.gao.entity.GaoProduct; import com.imyeyu.api.modules.gao.entity.GaoProductSpec; import com.imyeyu.api.modules.gao.entity.GaoProductSpecValue; @@ -7,10 +8,13 @@ import com.imyeyu.api.modules.gao.mapper.GaoProductSpecMapper; import com.imyeyu.api.modules.gao.mapper.GaoProductSpecValueMapper; import com.imyeyu.api.modules.gao.service.GaoProductService; import com.imyeyu.api.modules.gao.service.GaoProductSpecService; +import com.imyeyu.api.modules.gao.service.GaoProductSkuService; import com.imyeyu.java.TimiJava; import com.imyeyu.java.bean.timi.TimiException; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.Map; @@ -27,6 +31,7 @@ public class GaoProductSpecServiceImplement implements GaoProductSpecService { private final GaoProductSpecMapper specMapper; private final GaoProductSpecValueMapper valueMapper; private final GaoProductService productService; + private final ObjectProvider skuServiceProvider; @Override public List listByProductId(String productId) { @@ -41,6 +46,12 @@ public class GaoProductSpecServiceImplement implements GaoProductSpecService { return specMapper.selectProductIdBySpecId(specId); } + @Override + public String getProductIdBySpecValueId(String specValueId) { + GaoProductSpecValue value = valueMapper.selectById(specValueId); + return value == null ? null : specMapper.selectProductIdBySpecId(value.getSpecId()); + } + @Override public void createSpec(GaoProductSpec spec) { TimiException.required(spec, "not found product spec"); @@ -51,6 +62,28 @@ public class GaoProductSpecServiceImplement implements GaoProductSpecService { specMapper.insert(spec); } + @Transactional(TimiServerDBConfig.ROLLBACKER) + @Override + public void updateSpec(GaoProductSpec request) { + TimiException.required(request, "not found product spec"); + TimiException.required(request.getId(), "not found product spec.id"); + String productId = specMapper.selectProductIdBySpecId(request.getId()); + TimiException.required(productId, "产品规格不存在"); + GaoProductSpec existing = specMapper.selectByProductId(productId).stream() + .filter(item -> item.getId().equals(request.getId())).findFirst().orElseThrow(); + String name = requiredText(request.getName(), "规格名称不能为空"); + Integer sort = TimiJava.defaultIfNull(request.getSort(), existing.getSort()); + if (existing.getName().equals(name) && existing.getSort().equals(sort)) { + return; + } + GaoProductSpec spec = new GaoProductSpec(); + spec.setId(request.getId()); + spec.setName(name); + spec.setSort(sort); + TimiException.requiredTrue(1 == specMapper.updateSpec(spec), "产品规格更新失败"); + skuServiceProvider.getObject().refreshSpecHistorySnapshots(productId); + } + @Override public void createValue(GaoProductSpecValue value) { TimiException.required(value, "not found product spec value"); @@ -61,5 +94,40 @@ public class GaoProductSpecServiceImplement implements GaoProductSpecService { valueMapper.insert(value); } + @Transactional(TimiServerDBConfig.ROLLBACKER) + @Override + public void updateValue(GaoProductSpecValue request) { + TimiException.required(request, "not found product spec value"); + TimiException.required(request.getId(), "not found product spec value.id"); + GaoProductSpecValue existing = valueMapper.selectById(request.getId()); + TimiException.required(existing, "产品规格值不存在"); + String productId = specMapper.selectProductIdBySpecId(existing.getSpecId()); + TimiException.required(productId, "产品规格不存在"); + String name = requiredText(request.getName(), "规格值名称不能为空"); + Integer sort = TimiJava.defaultIfNull(request.getSort(), existing.getSort()); + if (existing.getName().equals(name) && existing.getSort().equals(sort)) { + return; + } + GaoProductSpecValue value = new GaoProductSpecValue(); + value.setId(existing.getId()); + value.setSpecId(existing.getSpecId()); + value.setName(name); + value.setSort(sort); + TimiException.requiredTrue(1 == valueMapper.updateValue(value), "产品规格值更新失败"); + skuServiceProvider.getObject().refreshSpecHistorySnapshots(productId); + } + + @Override + public void destroyByProductId(String productId) { + List specList = specMapper.selectByProductId(productId); + List specIdList = specList.stream().map(GaoProductSpec::getId).toList(); + for (GaoProductSpecValue value : valueMapper.selectBySpecIdList(specIdList)) { + valueMapper.destroy(value.getId()); + } + for (GaoProductSpec spec : specList) { + specMapper.destroy(spec.getId()); + } + } + private String requiredText(String value, String message) { TimiException.required(value, message); String result = value.trim(); TimiException.requiredTrue(!result.isEmpty(), message); return result; } } diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoProductSkuBaseUnitMigrationRequest.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoProductSkuBaseUnitMigrationRequest.java new file mode 100644 index 0000000..e632920 --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoProductSkuBaseUnitMigrationRequest.java @@ -0,0 +1,19 @@ +package com.imyeyu.api.modules.gao.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +/// SKU 基础单位迁移请求 +@Data +public class GaoProductSkuBaseUnitMigrationRequest { + + /// 新基础单位 ID;切换到本次新增单位时允许为空 + private String targetUnitId; + + /// 新基础单位名称;按 ID 定位失败时用于匹配本次提交的新单位 + private String targetUnitName; + + /// 1 个旧基础单位等于的新基础单位数量 + private BigDecimal oldToNewBaseRatio; +} diff --git a/src/main/java/com/imyeyu/api/modules/system/audit/OperationAuditAspect.java b/src/main/java/com/imyeyu/api/modules/system/audit/OperationAuditAspect.java index 71368ad..9024e66 100644 --- a/src/main/java/com/imyeyu/api/modules/system/audit/OperationAuditAspect.java +++ b/src/main/java/com/imyeyu/api/modules/system/audit/OperationAuditAspect.java @@ -42,12 +42,15 @@ public class OperationAuditAspect { Logger logger = new Logger(operation.getModule(), operation.name()); logger.setContent(payloadSerializer.serializeArguments(arguments)); long startNanos = System.nanoTime(); + boolean previousAuditState = OperationAuditContext.begin(); try { Object result = joinPoint.proceed(); logger.setLevel(Logger.Level.INFO); logger.setResult(payloadSerializer.serializeResult(result, arguments)); logger.setDuration(duration(startNanos)); - record(logger); + if (!OperationAuditContext.isSuccessAuditSkipped()) { + record(logger); + } return result; } catch (TimiException e) { logger.setLevel(Logger.Level.WARN); @@ -62,6 +65,8 @@ public class OperationAuditAspect { log.error("业务操作失败: {}", operation.name(), e); record(logger); throw e; + } finally { + OperationAuditContext.end(previousAuditState); } } diff --git a/src/main/java/com/imyeyu/api/modules/system/audit/OperationAuditContext.java b/src/main/java/com/imyeyu/api/modules/system/audit/OperationAuditContext.java new file mode 100644 index 0000000..a08fed2 --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/system/audit/OperationAuditContext.java @@ -0,0 +1,45 @@ +package com.imyeyu.api.modules.system.audit; + +/// 当前线程中的操作审计上下文 +/// +/// @author Codex +/// @since 2026-09-30 +public final class OperationAuditContext { + + private static final ThreadLocal SKIP_SUCCESS_AUDIT = ThreadLocal.withInitial(() -> false); + + private OperationAuditContext() { + } + + /// 开始一次审计操作并返回外层状态 + /// + /// @return 外层是否跳过成功审计 + public static boolean begin() { + boolean previous = SKIP_SUCCESS_AUDIT.get(); + SKIP_SUCCESS_AUDIT.set(false); + return previous; + } + + /// 跳过当前操作的成功审计 + public static void skipSuccessAudit() { + SKIP_SUCCESS_AUDIT.set(true); + } + + /// 判断当前操作是否跳过成功审计 + /// + /// @return 是否跳过 + public static boolean isSuccessAuditSkipped() { + return SKIP_SUCCESS_AUDIT.get(); + } + + /// 恢复外层审计状态 + /// + /// @param previous 外层是否跳过成功审计 + public static void end(boolean previous) { + if (previous) { + SKIP_SUCCESS_AUDIT.set(true); + } else { + SKIP_SUCCESS_AUDIT.remove(); + } + } +} diff --git a/src/main/resources/mapper/timi-server/gao/GaoProductEditMapper.xml b/src/main/resources/mapper/timi-server/gao/GaoProductEditMapper.xml new file mode 100644 index 0000000..a02afcd --- /dev/null +++ b/src/main/resources/mapper/timi-server/gao/GaoProductEditMapper.xml @@ -0,0 +1,107 @@ + + + + + + + + UPDATE `gao_sales_order_item` SET `product_name_snapshot`=#{productName} WHERE `product_id_snapshot`=#{productId} + UPDATE `gao_order_fulfillment_item` SET `product_name_snapshot`=#{productName} WHERE `product_id_snapshot`=#{productId} + UPDATE `gao_product_stock_record` SET `product_name_snapshot`=#{productName} WHERE `product_id_snapshot`=#{productId} + UPDATE `gao_package_config_item` SET `product_name_snapshot`=#{productName} WHERE `product_id_snapshot`=#{productId} + UPDATE `gao_point_redemption` SET `product_name_snapshot`=#{productName} WHERE `product_id_snapshot`=#{productId} + UPDATE `gao_sales_order_item` SET `sku_code_snapshot`=#{skuCode}, `sku_name_snapshot`=#{skuName}, `spec_summary_snapshot`=#{specSummary}, `sku_spec_snapshot`=#{skuSpecSnapshot} WHERE `sku_id`=#{skuId} + UPDATE `gao_order_fulfillment_item` SET `sku_code_snapshot`=#{skuCode}, `sku_name_snapshot`=#{skuName}, `spec_summary_snapshot`=#{specSummary}, `sku_spec_snapshot`=#{skuSpecSnapshot} WHERE `sku_id`=#{skuId} + UPDATE `gao_product_stock_record` SET `sku_code_snapshot`=#{skuCode}, `sku_name_snapshot`=#{skuName}, `spec_summary_snapshot`=#{specSummary}, `sku_spec_snapshot`=#{skuSpecSnapshot} WHERE `sku_id`=#{skuId} + UPDATE `gao_package_config_item` SET `sku_code_snapshot`=#{skuCode}, `sku_name_snapshot`=#{skuName}, `spec_summary_snapshot`=#{specSummary} WHERE `sku_id`=#{skuId} + UPDATE `gao_point_redemption` SET `sku_code_snapshot`=#{skuCode}, `sku_name_snapshot`=#{skuName}, `sku_spec_snapshot`=#{skuSpecSnapshot} WHERE `sku_id_snapshot`=#{skuId} + UPDATE `gao_sales_order_item` SET `unit_snapshot`=#{unitName} WHERE `sku_unit_id`=#{unitId} + UPDATE `gao_order_fulfillment_item` SET `unit_snapshot`=#{unitName} WHERE `sku_unit_id`=#{unitId} + UPDATE `gao_product_stock_record` SET `unit_snapshot`=#{unitName} WHERE `sku_unit_id`=#{unitId} + UPDATE `gao_package_config_item` SET `unit_snapshot`=#{unitName} WHERE `sku_unit_id`=#{unitId} + UPDATE `gao_point_redemption` SET `unit_snapshot`=#{unitName} WHERE `sku_unit_id_snapshot`=#{unitId} + UPDATE `gao_product_sku` SET `min_stock_quantity`=`min_stock_quantity`*#{ratio} WHERE `id`=#{skuId} AND `min_stock_quantity` IS NOT NULL + UPDATE `gao_product_stock` SET `on_hand_quantity`=`on_hand_quantity`*#{ratio}, `reserved_quantity`=`reserved_quantity`*#{ratio}, `updated_at`=FLOOR(UNIX_TIMESTAMP(NOW(3))*1000) WHERE `sku_id`=#{skuId} + UPDATE `gao_sales_order_item` SET `base_quantity`=`base_quantity`*#{ratio}, `reserved_base_quantity`=`reserved_base_quantity`*#{ratio}, `fulfilled_base_quantity`=`fulfilled_base_quantity`*#{ratio}, `base_quantity_per_unit_snapshot`=`base_quantity_per_unit_snapshot`*#{ratio} WHERE `sku_id`=#{skuId} + UPDATE `gao_order_fulfillment_item` SET `base_quantity`=`base_quantity`*#{ratio}, `reserved_base_before`=`reserved_base_before`*#{ratio}, `reserved_base_after`=`reserved_base_after`*#{ratio}, `fulfilled_base_before`=`fulfilled_base_before`*#{ratio}, `fulfilled_base_after`=`fulfilled_base_after`*#{ratio}, `base_quantity_per_unit_snapshot`=`base_quantity_per_unit_snapshot`*#{ratio} WHERE `sku_id`=#{skuId} + UPDATE `gao_product_stock_record` SET `base_quantity`=`base_quantity`*#{ratio}, `on_hand_delta`=`on_hand_delta`*#{ratio}, `reserved_delta`=`reserved_delta`*#{ratio}, `on_hand_before`=`on_hand_before`*#{ratio}, `on_hand_after`=`on_hand_after`*#{ratio}, `reserved_before`=`reserved_before`*#{ratio}, `reserved_after`=`reserved_after`*#{ratio}, `base_quantity_per_unit_snapshot`=`base_quantity_per_unit_snapshot`*#{ratio} WHERE `sku_id`=#{skuId} + UPDATE `gao_package_config_item` SET `base_quantity_per_unit_snapshot`=`base_quantity_per_unit_snapshot`*#{ratio} WHERE `sku_id`=#{skuId} + UPDATE `gao_point_redemption` SET `base_quantity`=`base_quantity`*#{ratio}, `base_quantity_per_unit_snapshot`=`base_quantity_per_unit_snapshot`*#{ratio} WHERE `sku_id_snapshot`=#{skuId} + + UPDATE `gao_point_redemption_return` r + JOIN `gao_point_redemption` d ON d.`id`=r.`redemption_id` + SET r.`base_quantity`=r.`base_quantity`*#{ratio} + WHERE d.`sku_id_snapshot`=#{skuId} + + diff --git a/src/main/resources/mapper/timi-server/gao/GaoProductSpecMapper.xml b/src/main/resources/mapper/timi-server/gao/GaoProductSpecMapper.xml index 60f3e16..5c60b9a 100644 --- a/src/main/resources/mapper/timi-server/gao/GaoProductSpecMapper.xml +++ b/src/main/resources/mapper/timi-server/gao/GaoProductSpecMapper.xml @@ -5,4 +5,5 @@ + UPDATE `gao_product_spec` SET `name`=#{name},`sort`=#{sort},`updated_at`=FLOOR(UNIX_TIMESTAMP(NOW(3))*1000) WHERE `id`=#{id} AND `deleted_at` IS NULL diff --git a/src/main/resources/mapper/timi-server/gao/GaoProductSpecValueMapper.xml b/src/main/resources/mapper/timi-server/gao/GaoProductSpecValueMapper.xml index 1976e34..80652e2 100644 --- a/src/main/resources/mapper/timi-server/gao/GaoProductSpecValueMapper.xml +++ b/src/main/resources/mapper/timi-server/gao/GaoProductSpecValueMapper.xml @@ -3,4 +3,5 @@ + UPDATE `gao_product_spec_value` SET `name`=#{name},`sort`=#{sort},`updated_at`=FLOOR(UNIX_TIMESTAMP(NOW(3))*1000) WHERE `id`=#{id} AND `deleted_at` IS NULL diff --git a/src/main/resources/templates/GaoOrderReminder.ftl b/src/main/resources/templates/GaoOrderReminder.ftl index 9904f5e..69bdffb 100644 --- a/src/main/resources/templates/GaoOrderReminder.ftl +++ b/src/main/resources/templates/GaoOrderReminder.ftl @@ -1,5 +1,4 @@
-

订单状态提醒

以下订单存在待处理事项,请及时处理。

<#list customerList as customer>

客户:${customer.name?html}

-- 2.54.0 From 0dab1ad0390d9e65afa038cd7f5c7fac95ecbc35 Mon Sep 17 00:00:00 2001 From: Timi Date: Mon, 5 Oct 2026 16:10:07 +0800 Subject: [PATCH 3/3] v1.0.57 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4ad3ccf..68c61f0 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ com.imyeyu.timiserverapi TimiServerAPI - 1.0.56 + 1.0.57 jar TimiServerAPI imyeyu.com API -- 2.54.0