From eeb7b5b32e2dcbb21bbd0a8254f6208ae8c5ad2d Mon Sep 17 00:00:00 2001 From: Timi Date: Tue, 1 Sep 2026 01:16:52 +0800 Subject: [PATCH] add GaoPackage --- .../AttachmentVariantServiceImplement.java | 17 +++- .../modules/gao/bean/GaoPermissionCode.java | 2 +- .../api/modules/gao/bean/GaoRoleCode.java | 2 +- .../gao/controller/GaoEventController.java | 30 ++++++- .../controller/GaoEventRecordController.java | 3 +- .../api/modules/gao/entity/GaoEvent.java | 7 ++ .../modules/gao/entity/GaoEventRecord.java | 7 ++ .../modules/gao/mapper/GaoEventMapper.java | 4 + .../gao/mapper/GaoEventRecordMapper.java | 11 ++- .../gao/service/GaoPackageConfigService.java | 6 ++ .../GaoCustomerPackageServiceImplement.java | 82 +++++++++++-------- .../GaoEventRecordServiceImplement.java | 6 +- .../implement/GaoEventServiceImplement.java | 13 +++ .../GaoPackageConfigServiceImplement.java | 12 +++ .../GaoPackageDeliveryServiceImplement.java | 2 +- .../vo/GaoCustomerPackagePurchaseRequest.java | 7 +- .../com/imyeyu/api/util/InitBuiltinUser.java | 1 + ...V50__reconcile_attachment_name_length.java | 76 +++++++++++++++++ .../V49__add_gao_event_record_updated_by.sql | 9 ++ .../V51__bind_gao_package_purchase_event.sql | 3 + ...__remove_gao_store_manager_inheritance.sql | 10 +++ .../gao/GaoCustomerPackageItemMapper.xml | 8 +- .../timi-server/gao/GaoEventRecordMapper.xml | 1 + .../timi-server/user/RolePermissionMapper.xml | 2 +- 24 files changed, 272 insertions(+), 49 deletions(-) create mode 100644 src/main/java/db/migration/timiserver/V50__reconcile_attachment_name_length.java create mode 100644 src/main/resources/db/migration/timiserver/V49__add_gao_event_record_updated_by.sql create mode 100644 src/main/resources/db/migration/timiserver/V51__bind_gao_package_purchase_event.sql create mode 100644 src/main/resources/db/migration/timiserver/V52__remove_gao_store_manager_inheritance.sql diff --git a/src/main/java/com/imyeyu/api/modules/common/service/implement/AttachmentVariantServiceImplement.java b/src/main/java/com/imyeyu/api/modules/common/service/implement/AttachmentVariantServiceImplement.java index fe0bb3e..7b45bc0 100644 --- a/src/main/java/com/imyeyu/api/modules/common/service/implement/AttachmentVariantServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/common/service/implement/AttachmentVariantServiceImplement.java @@ -59,6 +59,7 @@ public class AttachmentVariantServiceImplement implements AttachmentVariantServi private static final long THUMB_CACHE_TTL = Time.D * 7; private static final long THUMB_LOCK_TTL = Time.M * 10; private static final double THUMB_TARGET_SECONDS = 2; + private static final int VARIANT_NAME_MAX_LENGTH = 255; private static final String THUMB_OUTPUT_FORMAT = "png"; private static final String THUMB_ALGORITHM_VERSION = "v3"; private static final String THUMB_LOCK_KEY_PREFIX = "ATTACHMENT:THUMB:LOCK:"; @@ -158,7 +159,7 @@ public class AttachmentVariantServiceImplement implements AttachmentVariantServi thumbFile = createThumbFile(source, requestWidth, requestHeight, fitMode, isImage); variant = new AttachmentVariant(); variant.setId(UUID.randomUUID().toString()); - variant.setName(Network.simpleURIFileName(source.getName()) + "." + THUMB_OUTPUT_FORMAT); + variant.setName(buildVariantName(source.getName())); variant.setMimeType("image/" + THUMB_OUTPUT_FORMAT); long expireAt = Time.now() + THUMB_CACHE_TTL; variant.setSourceAttachmentId(source.getId()); @@ -257,6 +258,20 @@ public class AttachmentVariantServiceImplement implements AttachmentVariantServi ); } + /// 构建不超过数据库字段长度的变体名称 + /// + /// @param sourceName 源附件名称 + /// @return 变体名称 + private String buildVariantName(String sourceName) { + String suffix = "." + THUMB_OUTPUT_FORMAT; + String baseName = Network.simpleURIFileName(sourceName); + int maxBaseLength = VARIANT_NAME_MAX_LENGTH - suffix.length(); + if (maxBaseLength < baseName.codePointCount(0, baseName.length())) { + baseName = baseName.substring(0, baseName.offsetByCodePoints(0, maxBaseLength)); + } + return baseName + suffix; + } + private File createThumbFile( Attachment source, Integer requestWidth, diff --git a/src/main/java/com/imyeyu/api/modules/gao/bean/GaoPermissionCode.java b/src/main/java/com/imyeyu/api/modules/gao/bean/GaoPermissionCode.java index 7ca5690..0609c98 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/bean/GaoPermissionCode.java +++ b/src/main/java/com/imyeyu/api/modules/gao/bean/GaoPermissionCode.java @@ -73,7 +73,7 @@ public enum GaoPermissionCode implements BuiltinPermissionCode { CUSTOMER_PACKAGE_CANCEL("CUSTOMER_PACKAGE:CANCEL", "客户套餐取消", true, true), - CUSTOMER_PACKAGE_PRICE_OVERRIDE("CUSTOMER_PACKAGE:PRICE_OVERRIDE", "客户套餐成交价和产品定制", true, true), + CUSTOMER_PACKAGE_PRICE_OVERRIDE("CUSTOMER_PACKAGE:PRICE_OVERRIDE", "客户套餐成交价和权益配额调整", true, true), PACKAGE_DELIVERY_CREATE("PACKAGE_DELIVERY:CREATE", "套餐发放创建", true, true), diff --git a/src/main/java/com/imyeyu/api/modules/gao/bean/GaoRoleCode.java b/src/main/java/com/imyeyu/api/modules/gao/bean/GaoRoleCode.java index b3101f1..93ba36e 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/bean/GaoRoleCode.java +++ b/src/main/java/com/imyeyu/api/modules/gao/bean/GaoRoleCode.java @@ -16,7 +16,7 @@ public enum GaoRoleCode implements BuiltinRoleCode { GLOBAL_MANAGER("门店管理", null), /// 店长 - STORE_MANAGER("店长", GLOBAL_MANAGER.name()); + STORE_MANAGER("店长", null); final String defaultName; final String parentCode; diff --git a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoEventController.java b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoEventController.java index 8056038..1cd87f1 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoEventController.java +++ b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoEventController.java @@ -4,7 +4,10 @@ import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission; import com.imyeyu.api.modules.gao.bean.GaoPermissionCode; import com.imyeyu.api.modules.gao.entity.GaoEvent; import com.imyeyu.api.modules.gao.entity.GaoEventOptionGroup; +import com.imyeyu.api.modules.gao.entity.GaoPackageConfig; +import com.imyeyu.api.modules.gao.entity.GaoPackageConfigItem; import com.imyeyu.api.modules.gao.service.GaoEventService; +import com.imyeyu.api.modules.gao.service.GaoPackageConfigService; import com.imyeyu.api.modules.gao.service.GaoStoreScopeService; import com.imyeyu.java.TimiJava; import com.imyeyu.java.bean.timi.TimiException; @@ -32,6 +35,7 @@ import java.util.Map; public class GaoEventController { private final GaoEventService service; + private final GaoPackageConfigService packageConfigService; private final GaoStoreScopeService storeScopeService; @AOPLog @@ -69,15 +73,38 @@ public class GaoEventController { event.setRoleCodeList(roleMap.get(event.getId())); } service.attachOptionGroupList(result.getList()); + attachPackageConfig(result.getList()); return result; } + private void attachPackageConfig(List eventList) { + List packageConfigIdList = eventList.stream() + .map(GaoEvent::getPackageConfigId) + .filter(TimiJava::isNotEmpty) + .distinct() + .toList(); + if (packageConfigIdList.isEmpty()) { + return; + } + Map packageConfigMap = packageConfigService.mapByIdList(packageConfigIdList); + Map> itemMap = packageConfigService.mapItemListByPackageConfigIdList(packageConfigIdList); + for (GaoEvent event : eventList) { + GaoPackageConfig packageConfig = packageConfigMap.get(event.getPackageConfigId()); + if (packageConfig != null) { + packageConfig.setItemList(itemMap.getOrDefault(packageConfig.getId(), List.of())); + event.setPackageConfig(packageConfig); + } + } + } + @AOPLog @RequestRateLimit @RequireGaoPermission(GaoPermissionCode.EVENT_READ) @PostMapping("/list/valid") public List listValid(@RequestParam String storeId) { - return service.listValid(storeScopeService.resolveStoreId(storeId)); + List result = service.listValid(storeScopeService.resolveStoreId(storeId)); + attachPackageConfig(result); + return result; } @AOPLog @@ -90,6 +117,7 @@ public class GaoEventController { storeScopeService.checkStoreId(event.getStoreId()); event.setRoleCodeList(service.listRoleCode(event.getId())); service.attachOptionGroupList(List.of(event)); + attachPackageConfig(List.of(event)); return event; } diff --git a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoEventRecordController.java b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoEventRecordController.java index 4361af7..4a4073f 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoEventRecordController.java +++ b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoEventRecordController.java @@ -13,10 +13,10 @@ import com.imyeyu.api.modules.gao.service.GaoEventRecordService; import com.imyeyu.api.modules.gao.service.GaoEventService; import com.imyeyu.api.modules.gao.service.GaoStoreScopeService; import com.imyeyu.api.modules.gao.vo.GaoCustomerEventRankView; +import com.imyeyu.api.modules.gao.vo.GaoEventOptionStatView; import com.imyeyu.api.modules.gao.vo.GaoEventRecordPage; import com.imyeyu.api.modules.gao.vo.GaoEventRecordPageResult; import com.imyeyu.api.modules.gao.vo.GaoEventRecordRankPage; -import com.imyeyu.api.modules.gao.vo.GaoEventOptionStatView; import com.imyeyu.api.modules.user.service.UserService; import com.imyeyu.java.TimiJava; import com.imyeyu.java.bean.timi.TimiException; @@ -114,6 +114,7 @@ public class GaoEventRecordController { record.setEvent(eventService.get(record.getEventId())); eventService.attachOptionGroupList(List.of(record.getEvent())); record.setOperator(userService.get(record.getOperatorUserId())); + record.setUpdater(userService.get(record.getUpdatedBy())); if (record.getVerifierUserId() != null) { record.setVerifier(userService.get(record.getVerifierUserId())); } diff --git a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoEvent.java b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoEvent.java index 00694c6..c48b270 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoEvent.java +++ b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoEvent.java @@ -106,6 +106,13 @@ public class GaoEvent extends UUIDEntity { /// 业务类型 private BizType bizType; + /// 购买套餐事件绑定的套餐配置 ID + private String packageConfigId; + + /// 购买套餐事件绑定的套餐配置 + @Transient + private GaoPackageConfig packageConfig; + /// 登记事件名称 private String name; diff --git a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoEventRecord.java b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoEventRecord.java index 410ce81..581a961 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoEventRecord.java +++ b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoEventRecord.java @@ -87,6 +87,9 @@ public class GaoEventRecord extends UUIDEntity { /// 营业日递增序号 private Long businessDayNo; + /// 最后修改用户 ID + private String updatedBy; + @Transient private GaoCustomer customer; @@ -99,6 +102,10 @@ public class GaoEventRecord extends UUIDEntity { @Transient private User verifier; + /// 最后修改用户 + @Transient + private User updater; + @Transient private List attachmentList; diff --git a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoEventMapper.java b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoEventMapper.java index 62edb8b..23e1696 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoEventMapper.java +++ b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoEventMapper.java @@ -4,6 +4,7 @@ import com.imyeyu.api.modules.gao.entity.GaoEvent; import com.imyeyu.spring.bean.Page; import com.imyeyu.spring.mapper.BaseMapper; import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.SelectProvider; import java.util.Collection; @@ -25,4 +26,7 @@ public interface GaoEventMapper extends BaseMapper { List selectByIdList(Collection idList); List selectValidList(@Param("storeId") String storeId, @Param("now") Long now); + + @Select("SELECT COUNT(*) FROM `gao_event` WHERE `package_config_id` = #{packageConfigId} AND `deleted_at` IS NULL") + long countByPackageConfigId(String packageConfigId); } diff --git a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoEventRecordMapper.java b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoEventRecordMapper.java index b829bbd..6b473da 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoEventRecordMapper.java +++ b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoEventRecordMapper.java @@ -12,6 +12,7 @@ import com.imyeyu.api.modules.gao.vo.GaoEventOptionStatView; import com.imyeyu.api.modules.gao.vo.GaoStoreChartReq; import com.imyeyu.spring.mapper.BaseMapper; import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; import java.util.List; @@ -21,6 +22,13 @@ import java.util.List; /// @since 2026-07-27 14:11 public interface GaoEventRecordMapper extends BaseMapper { + /// 查询事件登记记录数量 + /// + /// @param eventId 事件 ID + /// @return 登记记录数量 + @Select("SELECT COUNT(*) FROM `gao_event_record` WHERE `event_id` = #{eventId} AND `deleted_at` IS NULL") + long countByEventId(String eventId); + Long count(String storeId, String customerId, String eventId, Long beginAt, Long endAt); /// 查询事件中已携带数值的登记记录数量 @@ -50,8 +58,9 @@ public interface GaoEventRecordMapper extends BaseMapper /// @param id 登记记录 ID /// @param verifierUserId 核销用户 ID /// @param verifiedAt 核销时间 + /// @param updatedBy 最后修改用户 ID /// @return 更新行数 - int verify(@Param("id") String id, @Param("verifierUserId") String verifierUserId, @Param("verifiedAt") Long verifiedAt); + int verify(@Param("id") String id, @Param("verifierUserId") String verifierUserId, @Param("verifiedAt") Long verifiedAt, @Param("updatedBy") String updatedBy); /// 按门店和登记日期统计登记记录数 /// diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/GaoPackageConfigService.java b/src/main/java/com/imyeyu/api/modules/gao/service/GaoPackageConfigService.java index 11c20b8..602d2d3 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/GaoPackageConfigService.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/GaoPackageConfigService.java @@ -22,6 +22,12 @@ public interface GaoPackageConfigService extends BaseService listValid(String storeId); + /// 批量查询套餐配置 + /// + /// @param idList 套餐配置 ID 列表 + /// @return 按套餐配置 ID 映射的配置 + Map mapByIdList(Collection idList); + /// 查询套餐配置产品明细 /// /// @param packageConfigId 套餐配置 ID diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoCustomerPackageServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoCustomerPackageServiceImplement.java index bbd90a0..78f8b6d 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoCustomerPackageServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoCustomerPackageServiceImplement.java @@ -171,23 +171,25 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService configItemList = packageConfigService.listItemByPackageConfigId(config.getId()); + request.setSaleAmount(TimiJava.defaultIfNull(request.getSaleAmount(), config.getStandardAmount())); + TimiException.requiredTrue(0 <= request.getSaleAmount(), "成交价不能为负数"); + if (request.getItemList() == null) { + request.setItemList(defaultPurchaseItems(configItemList)); } - TimiException.required(config, "not found package config"); - storeScopeService.checkStoreId(config.getStoreId()); + List itemList = new java.util.ArrayList<>(request.getItemList()); + verifyPurchaseItems(itemList); + itemList.sort(java.util.Comparator.comparing(GaoCustomerPackagePurchaseItem::getSort)); + request.setItemList(itemList); + GaoCustomerPackage existing = mapper.selectByRequestNo(config.getStoreId(), request.getRequestNo()); if (existing != null) { verifyPurchaseIdempotency(existing, request); @@ -199,17 +201,12 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService configItemList = packageConfigService.listItemByPackageConfigId(config.getId()); GaoCustomer customer = validateCustomer(request.getCustomerId(), config.getStoreId()); - GaoEvent event = validatePurchaseEvent(request.getEventId(), config.getStoreId()); - - List itemList = new java.util.ArrayList<>(TimiJava.defaultIfNull(request.getItemList(), List.of())); - verifyPurchaseItems(itemList); - itemList.sort(java.util.Comparator.comparing(GaoCustomerPackagePurchaseItem::getSort)); - boolean canOverride = permissionChecker.hasAny(ModuleCode.GAO, com.imyeyu.api.modules.gao.bean.GaoPermissionCode.CUSTOMER_PACKAGE_PRICE_OVERRIDE.getValue()); - if (!canOverride) { + verifySameStructureAsConfig(itemList, configItemList); + boolean canOverridePriceAndQuantity = permissionChecker.hasAny(ModuleCode.GAO, com.imyeyu.api.modules.gao.bean.GaoPermissionCode.CUSTOMER_PACKAGE_PRICE_OVERRIDE.getValue()); + if (!canOverridePriceAndQuantity) { TimiException.requiredTrue(request.getSaleAmount().equals(config.getStandardAmount()), "普通员工不能修改套餐成交价"); - verifySameAsConfig(itemList, configItemList); + verifySameQuantityAsConfig(itemList, configItemList); } GaoEventRecord eventRecord = new GaoEventRecord(); @@ -299,14 +296,26 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService defaultPurchaseItems(List configItemList) { + return configItemList.stream().map(configItem -> { + GaoCustomerPackagePurchaseItem item = new GaoCustomerPackagePurchaseItem(); + item.setProductName(configItem.getProductName()); + item.setUnit(configItem.getUnit()); + item.setTotalQuantity(configItem.getDefaultQuantity()); + item.setSort(configItem.getSort()); + item.setRemark(configItem.getRemark()); + return item; + }).toList(); + } + private void verifyPurchaseItems(List itemList) { TimiException.requiredTrue(!itemList.isEmpty(), "购买套餐至少需要一个产品"); Set keySet = new HashSet<>(); @@ -326,20 +335,27 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService itemList, List configItemList) { - TimiException.requiredTrue(itemList.size() == configItemList.size(), "普通员工不能修改套餐产品明细"); + private void verifySameStructureAsConfig(List itemList, List configItemList) { + TimiException.requiredTrue(itemList.size() == configItemList.size(), "套餐权益项目必须与事件绑定套餐一致"); Map itemMap = itemList.stream().collect(Collectors.toMap(item -> item.getProductName() + "|" + item.getUnit(), Function.identity())); for (GaoPackageConfigItem configItem : configItemList) { GaoCustomerPackagePurchaseItem item = itemMap.get(configItem.getProductName() + "|" + configItem.getUnit()); - TimiException.required(item, "普通员工不能修改套餐产品明细"); - TimiException.requiredTrue(item.getTotalQuantity().compareTo(configItem.getDefaultQuantity()) == 0 - && item.getSort().equals(configItem.getSort()), "普通员工不能修改套餐产品明细"); + TimiException.required(item, "套餐权益项目必须与事件绑定套餐一致"); + TimiException.requiredTrue(item.getSort().equals(configItem.getSort()) + && sameText(item.getRemark(), configItem.getRemark()), "套餐权益项目的名称、单位、排序和备注不可修改"); + } + } + + private void verifySameQuantityAsConfig(List itemList, List configItemList) { + Map itemMap = itemList.stream().collect(Collectors.toMap(item -> item.getProductName() + "|" + item.getUnit(), Function.identity())); + for (GaoPackageConfigItem configItem : configItemList) { + GaoCustomerPackagePurchaseItem item = itemMap.get(configItem.getProductName() + "|" + configItem.getUnit()); + TimiException.requiredTrue(item.getTotalQuantity().compareTo(configItem.getDefaultQuantity()) == 0, "普通员工不能修改套餐权益配额"); } } private void verifyPurchaseIdempotency(GaoCustomerPackage existing, GaoCustomerPackagePurchaseRequest request) { - TimiException.requiredTrue(existing.getPackageConfigId().equals(request.getPackageConfigId()) - && existing.getCustomerId().equals(request.getCustomerId()) + TimiException.requiredTrue(existing.getCustomerId().equals(request.getCustomerId()) && existing.getSaleAmount().equals(request.getSaleAmount()), "requestNo 已被其他购买请求使用"); GaoEventRecord eventRecord = eventRecordService.get(existing.getPurchaseEventRecordId()); TimiException.required(eventRecord, "购买事件登记不存在"); @@ -354,7 +370,9 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService mapper() { @@ -130,6 +133,10 @@ public class GaoEventServiceImplement extends AbstractEntityService mapByIdList(Collection idList) { + if (idList == null || idList.isEmpty()) { + return Map.of(); + } + return mapper.selectByIdList(idList).stream() + .collect(java.util.stream.Collectors.toMap(GaoPackageConfig::getId, item -> item)); + } + @Override public List listItemByPackageConfigId(String packageConfigId) { TimiException.required(packageConfigId, "not found packageConfigId"); @@ -134,6 +145,7 @@ public class GaoPackageConfigServiceImplement extends AbstractEntityService { GaoPackageConfig config = get(id); TimiException.required(config, "not found package config"); + TimiException.requiredTrue(0 == eventMapper.countByPackageConfigId(id), "套餐配置已绑定购买事件,不能删除"); super.delete(id); return id; }); diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoPackageDeliveryServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoPackageDeliveryServiceImplement.java index ee9370f..aed6420 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoPackageDeliveryServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoPackageDeliveryServiceImplement.java @@ -182,7 +182,7 @@ public class GaoPackageDeliveryServiceImplement extends AbstractEntityService itemList; /// 购买照片 diff --git a/src/main/java/com/imyeyu/api/util/InitBuiltinUser.java b/src/main/java/com/imyeyu/api/util/InitBuiltinUser.java index b77d8df..4e1c281 100644 --- a/src/main/java/com/imyeyu/api/util/InitBuiltinUser.java +++ b/src/main/java/com/imyeyu/api/util/InitBuiltinUser.java @@ -206,6 +206,7 @@ public class InitBuiltinUser implements ApplicationRunner { private void bindBuiltinRoleParent(Map roleMap) { setRoleParent(roleMap, ModuleCode.CORE, CoreRoleCode.ADMIN.name(), ModuleCode.CORE, CoreRoleCode.SYSTEM.name()); setRoleParent(roleMap, ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name(), ModuleCode.CORE, CoreRoleCode.ADMIN.name()); + setRoleParent(roleMap, ModuleCode.GAO, GaoRoleCode.STORE_MANAGER.name(), ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name()); } /// 按权限定义补齐 SYSTEM 和 ADMIN 的默认权限 diff --git a/src/main/java/db/migration/timiserver/V50__reconcile_attachment_name_length.java b/src/main/java/db/migration/timiserver/V50__reconcile_attachment_name_length.java new file mode 100644 index 0000000..72dd463 --- /dev/null +++ b/src/main/java/db/migration/timiserver/V50__reconcile_attachment_name_length.java @@ -0,0 +1,76 @@ +package db.migration.timiserver; + +import org.flywaydb.core.api.migration.BaseJavaMigration; +import org.flywaydb.core.api.migration.Context; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +/// 对账附件名称字段长度 +/// +/// 生产环境可能已经紧急扩容,达到目标长度时不重复执行 DDL +public class V50__reconcile_attachment_name_length extends BaseJavaMigration { + + private static final int NAME_LENGTH = 255; + + @Override + public void migrate(Context context) throws Exception { + Connection connection = context.getConnection(); + int attachmentNameLength = queryCurrentLength(connection, "attachment"); + int variantNameLength = queryCurrentLength(connection, "attachment_variant"); + reconcile(connection, "attachment", attachmentNameLength); + reconcile(connection, "attachment_variant", variantNameLength); + } + + /// 将名称字段扩容到目标长度 + /// + /// @param connection Flyway 数据库连接 + /// @param tableName 表名 + /// @param currentLength 当前字符长度 + /// @throws SQLException DDL 执行失败 + private void reconcile(Connection connection, String tableName, int currentLength) throws SQLException { + if (NAME_LENGTH <= currentLength) { + return; + } + + String sql = """ + ALTER TABLE `%s` + MODIFY COLUMN `name` VARCHAR(%d) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL + """.formatted(tableName, NAME_LENGTH); + try (Statement statement = connection.createStatement()) { + statement.executeUpdate(sql); + } + } + + /// 查询名称字段当前长度 + /// + /// @param connection Flyway 数据库连接 + /// @param tableName 表名 + /// @return 当前字符长度 + /// @throws SQLException 表或字段不存在、字段类型不正确或查询失败 + private int queryCurrentLength(Connection connection, String tableName) throws SQLException { + String sql = """ + SELECT `DATA_TYPE`, `CHARACTER_MAXIMUM_LENGTH` + FROM `INFORMATION_SCHEMA`.`COLUMNS` + WHERE `TABLE_SCHEMA` = DATABASE() + AND `TABLE_NAME` = ? + AND `COLUMN_NAME` = 'name' + """; + try (PreparedStatement statement = connection.prepareStatement(sql)) { + statement.setString(1, tableName); + try (ResultSet resultSet = statement.executeQuery()) { + if (!resultSet.next()) { + throw new SQLException("表 %s 的 name 字段不存在".formatted(tableName)); + } + String dataType = resultSet.getString("DATA_TYPE"); + if (!"varchar".equalsIgnoreCase(dataType)) { + throw new SQLException("表 %s 的 name 字段类型不是 VARCHAR,而是 %s".formatted(tableName, dataType)); + } + return resultSet.getInt("CHARACTER_MAXIMUM_LENGTH"); + } + } + } +} diff --git a/src/main/resources/db/migration/timiserver/V49__add_gao_event_record_updated_by.sql b/src/main/resources/db/migration/timiserver/V49__add_gao_event_record_updated_by.sql new file mode 100644 index 0000000..5706015 --- /dev/null +++ b/src/main/resources/db/migration/timiserver/V49__add_gao_event_record_updated_by.sql @@ -0,0 +1,9 @@ +ALTER TABLE `gao_event_record` + ADD COLUMN `updated_by` VARCHAR(36) NULL COMMENT '最后修改用户 ID' AFTER `updated_at`; + +UPDATE `gao_event_record` +SET `updated_by` = `operator_user_id` +WHERE `updated_by` IS NULL; + +ALTER TABLE `gao_event_record` + MODIFY COLUMN `updated_by` VARCHAR(36) NOT NULL COMMENT '最后修改用户 ID'; diff --git a/src/main/resources/db/migration/timiserver/V51__bind_gao_package_purchase_event.sql b/src/main/resources/db/migration/timiserver/V51__bind_gao_package_purchase_event.sql new file mode 100644 index 0000000..0fd52d5 --- /dev/null +++ b/src/main/resources/db/migration/timiserver/V51__bind_gao_package_purchase_event.sql @@ -0,0 +1,3 @@ +ALTER TABLE `gao_event` + ADD COLUMN `package_config_id` VARCHAR(36) NULL COMMENT '购买套餐事件绑定的套餐配置 ID' AFTER `biz_type`, + ADD KEY `idx_gao_event_package_config` (`package_config_id`, `deleted_at`); diff --git a/src/main/resources/db/migration/timiserver/V52__remove_gao_store_manager_inheritance.sql b/src/main/resources/db/migration/timiserver/V52__remove_gao_store_manager_inheritance.sql new file mode 100644 index 0000000..f9abc4e --- /dev/null +++ b/src/main/resources/db/migration/timiserver/V52__remove_gao_store_manager_inheritance.sql @@ -0,0 +1,10 @@ +UPDATE `role_permission_inherit` inherit +JOIN `role` parent_role ON parent_role.id = inherit.parent_role_id +JOIN `role` child_role ON child_role.id = inherit.child_role_id +SET inherit.deleted_at = UNIX_TIMESTAMP() * 1000 +WHERE + inherit.deleted_at IS NULL + AND parent_role.module_code = 'GAO' + AND parent_role.code = 'GLOBAL_MANAGER' + AND child_role.module_code = 'GAO' + AND child_role.code = 'STORE_MANAGER'; diff --git a/src/main/resources/mapper/timi-server/gao/GaoCustomerPackageItemMapper.xml b/src/main/resources/mapper/timi-server/gao/GaoCustomerPackageItemMapper.xml index 606831d..fbb8168 100644 --- a/src/main/resources/mapper/timi-server/gao/GaoCustomerPackageItemMapper.xml +++ b/src/main/resources/mapper/timi-server/gao/GaoCustomerPackageItemMapper.xml @@ -25,11 +25,15 @@ UPDATE `gao_customer_package_item` SET `reserved_quantity` = `reserved_quantity` + #{delta}, `updated_at` = FLOOR(UNIX_TIMESTAMP(NOW(3)) * 1000) - WHERE `id` = #{id} AND `deleted_at` IS NULL AND `reserved_quantity` + #{delta} >= 0 + WHERE `id` = #{id} AND `deleted_at` IS NULL + AND `reserved_quantity` + #{delta} >= 0 + AND `reserved_quantity` + `delivered_quantity` + #{delta} <= `total_quantity` UPDATE `gao_customer_package_item` SET `delivered_quantity` = `delivered_quantity` + #{delta}, `updated_at` = FLOOR(UNIX_TIMESTAMP(NOW(3)) * 1000) - WHERE `id` = #{id} AND `deleted_at` IS NULL AND `delivered_quantity` + #{delta} >= 0 + WHERE `id` = #{id} AND `deleted_at` IS NULL + AND `delivered_quantity` + #{delta} >= 0 + AND `reserved_quantity` + `delivered_quantity` + #{delta} <= `total_quantity` diff --git a/src/main/resources/mapper/timi-server/gao/GaoEventRecordMapper.xml b/src/main/resources/mapper/timi-server/gao/GaoEventRecordMapper.xml index 3f315ae..869d9c6 100644 --- a/src/main/resources/mapper/timi-server/gao/GaoEventRecordMapper.xml +++ b/src/main/resources/mapper/timi-server/gao/GaoEventRecordMapper.xml @@ -65,6 +65,7 @@ `verification_status` = 'VERIFIED', `verifier_user_id` = #{verifierUserId}, `verified_at` = #{verifiedAt}, + `updated_by` = #{updatedBy}, `updated_at` = ROUND(UNIX_TIMESTAMP(CURRENT_TIMESTAMP(3)) * 1000) WHERE `id` = #{id} diff --git a/src/main/resources/mapper/timi-server/user/RolePermissionMapper.xml b/src/main/resources/mapper/timi-server/user/RolePermissionMapper.xml index 3c223c7..48ce336 100644 --- a/src/main/resources/mapper/timi-server/user/RolePermissionMapper.xml +++ b/src/main/resources/mapper/timi-server/user/RolePermissionMapper.xml @@ -16,7 +16,7 @@ SELECT DISTINCT permission.* FROM `role_permission` - LEFT JOIN `permission` ON permission.id = role_permission.permission_id + INNER JOIN `permission` ON permission.id = role_permission.permission_id WHERE `role_permission`.`role_id` IN