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 79b16fb..c423608 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 @@ -81,6 +81,8 @@ public enum GaoPermissionCode implements BuiltinPermissionCode { SALES_ORDER_READ("SALES_ORDER:READ", "销售订单读取", true, true), + SALES_ORDER_UPDATE("SALES_ORDER:UPDATE", "销售订单修改", true, true), + SALES_ORDER_CANCEL("SALES_ORDER:CANCEL", "销售订单取消", true, true), SALES_ORDER_PRICE_OVERRIDE("SALES_ORDER:PRICE_OVERRIDE", "销售订单价格修改", true, true), diff --git a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoCustomerPackageController.java b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoCustomerPackageController.java index 1243017..9fdc013 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoCustomerPackageController.java +++ b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoCustomerPackageController.java @@ -8,6 +8,7 @@ import com.imyeyu.api.modules.gao.entity.GaoCustomer; import com.imyeyu.api.modules.gao.entity.GaoCustomerPackage; import com.imyeyu.api.modules.gao.entity.GaoCustomerPackageItem; import com.imyeyu.api.modules.gao.entity.GaoOrderFulfillment; +import com.imyeyu.api.modules.gao.entity.GaoProduct; import com.imyeyu.api.modules.gao.service.GaoCustomerPackageService; import com.imyeyu.api.modules.gao.service.GaoCustomerService; import com.imyeyu.api.modules.gao.service.GaoOrderFulfillmentService; @@ -127,11 +128,34 @@ public class GaoCustomerPackageController { Map userMap = userIdList.isEmpty() ? Map.of() : userService.mapByIdList(userIdList); for (GaoCustomerPackage packageEntity : packageList) { List itemList = itemMap.getOrDefault(packageEntity.getId(), List.of()); + fillItemAttachments(itemList); for (GaoCustomerPackageItem item : itemList) { fillDerived(item); } packageEntity.setItemList(itemList); - packageEntity.setCancelOperator(userMap.get(packageEntity.getCancelOperatorUserId())); + String cancelOperatorUserId = packageEntity.getCancelOperatorUserId(); + packageEntity.setCancelOperator(cancelOperatorUserId == null ? null : userMap.get(cancelOperatorUserId)); + } + } + + private void fillItemAttachments(List itemList) { + if (itemList == null || itemList.isEmpty()) { + return; + } + List productIdList = itemList.stream() + .map(GaoCustomerPackageItem::getProductId) + .filter(value -> value != null && !value.isBlank()) + .distinct() + .toList(); + if (productIdList.isEmpty()) { + return; + } + Map> attachmentMap = attachmentService.mapByBizIdList( + Attachment.BizType.GAO_PRODUCT, + productIdList, + GaoProduct.AttachType.PHOTO.name()); + for (GaoCustomerPackageItem item : itemList) { + item.setAttachmentList(attachmentMap.getOrDefault(item.getProductId(), List.of())); } } diff --git a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoFundRecordController.java b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoFundRecordController.java index 033259b..30d1ae3 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoFundRecordController.java +++ b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoFundRecordController.java @@ -60,8 +60,7 @@ public class GaoFundRecordController { @RequireGaoPermission(GaoPermissionCode.FUND_RECORD_READ) @PostMapping("/detail") public GaoFundRecord detail(@RequestParam String id) { - GaoFundRecord result = service.get(id); - TimiException.required(result, "not found fund record"); + GaoFundRecord result = service.detail(id); storeScopeService.checkStoreId(result.getStoreId()); fillTransient(List.of(result)); return result; diff --git a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoOrderFulfillmentController.java b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoOrderFulfillmentController.java index 5be9243..940c519 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoOrderFulfillmentController.java +++ b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoOrderFulfillmentController.java @@ -4,7 +4,9 @@ import com.imyeyu.api.modules.common.entity.Attachment; import com.imyeyu.api.modules.common.service.AttachmentService; import com.imyeyu.api.modules.gao.annotation.RequireGaoPermission; import com.imyeyu.api.modules.gao.bean.GaoPermissionCode; +import com.imyeyu.api.modules.gao.entity.GaoCustomer; import com.imyeyu.api.modules.gao.entity.GaoOrderFulfillment; +import com.imyeyu.api.modules.gao.service.GaoCustomerService; import com.imyeyu.api.modules.gao.service.GaoOrderFulfillmentService; import com.imyeyu.api.modules.gao.service.GaoStoreScopeService; import com.imyeyu.api.modules.gao.vo.GaoOrderFulfillmentActionRequest; @@ -41,6 +43,7 @@ public class GaoOrderFulfillmentController { private final GaoOrderFulfillmentService service; private final GaoStoreScopeService storeScopeService; private final AttachmentService attachmentService; + private final GaoCustomerService customerService; private final UserService userService; @AOPLog @@ -131,6 +134,15 @@ public class GaoOrderFulfillmentController { } List idList = fulfillmentList.stream().map(GaoOrderFulfillment::getId).toList(); Map> attachmentMap = attachmentService.mapByBizIdList(Attachment.BizType.GAO_ORDER_FULFILLMENT, idList); + List customerIdList = fulfillmentList.stream() + .map(GaoOrderFulfillment::getCustomerId) + .filter(value -> value != null && !value.isBlank()) + .distinct() + .toList(); + Map customerMap = customerIdList.isEmpty() ? Map.of() : customerService.mapByIdList(customerIdList); + Map> customerAttachmentMap = customerIdList.isEmpty() + ? Map.of() + : attachmentService.mapByBizIdList(Attachment.BizType.GAO_CUSTOMER, customerIdList, GaoCustomer.AttachType.PHOTO.name()); List userIdList = fulfillmentList.stream() .flatMap(item -> Stream.of(item.getOperatorUserId(), item.getVerifierUserId())) .filter(value -> value != null && !value.isBlank()) @@ -139,6 +151,11 @@ public class GaoOrderFulfillmentController { Map userMap = userIdList.isEmpty() ? Map.of() : userService.mapByIdList(userIdList); for (GaoOrderFulfillment fulfillment : fulfillmentList) { fulfillment.setAttachmentList(attachmentMap.getOrDefault(fulfillment.getId(), List.of())); + GaoCustomer customer = customerMap.get(fulfillment.getCustomerId()); + if (customer != null) { + customer.setAttachmentList(customerAttachmentMap.getOrDefault(customer.getId(), List.of())); + } + fulfillment.setCustomer(customer); fulfillment.setOperator(userMap.get(fulfillment.getOperatorUserId())); fulfillment.setVerifier(userMap.get(fulfillment.getVerifierUserId())); } diff --git a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoPackageConfigController.java b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoPackageConfigController.java index af4cea9..9ef9dc2 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoPackageConfigController.java +++ b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoPackageConfigController.java @@ -4,8 +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.GaoPackageConfig; import com.imyeyu.api.modules.gao.entity.GaoPackageConfigItem; +import com.imyeyu.api.modules.gao.entity.GaoProduct; import com.imyeyu.api.modules.gao.service.GaoEventService; import com.imyeyu.api.modules.gao.service.GaoPackageConfigService; +import com.imyeyu.api.modules.gao.service.GaoProductService; import com.imyeyu.api.modules.gao.service.GaoStoreScopeService; import com.imyeyu.api.modules.gao.vo.GaoPackageConfigPage; import com.imyeyu.api.modules.user.entity.User; @@ -35,6 +37,7 @@ public class GaoPackageConfigController { private final GaoPackageConfigService service; private final GaoEventService eventService; + private final GaoProductService productService; private final GaoStoreScopeService storeScopeService; private final UserService userService; @@ -46,7 +49,7 @@ public class GaoPackageConfigController { TimiException.required(page, "not found page"); page.setStoreId(storeScopeService.resolveStoreId(page.getStoreId())); PageResult result = service.pageByQuery(page); - fillTransient(result.getList()); + fillTransient(result.getList(), false); return result; } @@ -56,7 +59,7 @@ public class GaoPackageConfigController { @PostMapping("/list/valid") public List listValid(@RequestParam String storeId) { List result = service.listValid(storeScopeService.resolveStoreId(storeId)); - fillTransient(result); + fillTransient(result, false); return result; } @@ -68,11 +71,11 @@ public class GaoPackageConfigController { GaoPackageConfig config = service.get(id); TimiException.required(config, "not found package config"); storeScopeService.checkStoreId(config.getStoreId()); - fillTransient(List.of(config)); + fillTransient(List.of(config), true); return config; } - private void fillTransient(List configList) { + private void fillTransient(List configList, boolean includeProductDetail) { if (configList == null || configList.isEmpty()) { return; } @@ -83,6 +86,28 @@ public class GaoPackageConfigController { config.setItemList(itemMap.getOrDefault(config.getId(), List.of())); config.setUpdater(updaterMap.get(config.getUpdatedBy())); } + if (includeProductDetail) { + fillProductDetails(configList); + } + } + + private void fillProductDetails(List configList) { + List productIdList = configList.stream() + .flatMap(config -> config.getItemList().stream()) + .map(GaoPackageConfigItem::getProductId) + .filter(value -> value != null && !value.isBlank()) + .distinct() + .toList(); + if (productIdList.isEmpty()) { + return; + } + Map productMap = productService.listByIdList(productIdList).stream() + .collect(java.util.stream.Collectors.toMap(GaoProduct::getId, item -> item)); + for (GaoPackageConfig config : configList) { + for (GaoPackageConfigItem item : config.getItemList()) { + item.setProduct(productMap.get(item.getProductId())); + } + } } @AOPLog diff --git a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoSalesOrderController.java b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoSalesOrderController.java index 91d774b..1701140 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/controller/GaoSalesOrderController.java +++ b/src/main/java/com/imyeyu/api/modules/gao/controller/GaoSalesOrderController.java @@ -15,6 +15,7 @@ import com.imyeyu.api.modules.gao.vo.GaoOrderPaymentRefundRequest; import com.imyeyu.api.modules.gao.vo.GaoOrderPaymentRevokeRequest; import com.imyeyu.api.modules.gao.vo.GaoSalesOrderCancelRequest; import com.imyeyu.api.modules.gao.vo.GaoSalesOrderPage; +import com.imyeyu.api.modules.gao.vo.GaoSalesOrderUpdateRequest; import com.imyeyu.java.bean.timi.TimiException; import com.imyeyu.spring.annotation.AOPLog; import com.imyeyu.spring.annotation.RequestRateLimit; @@ -67,6 +68,16 @@ public class GaoSalesOrderController { return service.createDirectProductOrder(request); } + @AOPLog + @RequestRateLimit + @RequireGaoPermission(GaoPermissionCode.SALES_ORDER_UPDATE) + @PostMapping("/update") + public GaoSalesOrder update(@RequestBody GaoSalesOrderUpdateRequest request) { + TimiException.required(request, "not found sales order update request"); + TimiException.required(request.getId(), "not found sales order.id"); + return service.updateRemarkAndAttachments(request); + } + @AOPLog @RequestRateLimit @RequireGaoPermission(GaoPermissionCode.SALES_ORDER_CANCEL) diff --git a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoCustomerPackageItem.java b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoCustomerPackageItem.java index 84fc8b6..c1f605c 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoCustomerPackageItem.java +++ b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoCustomerPackageItem.java @@ -1,11 +1,13 @@ package com.imyeyu.api.modules.gao.entity; +import com.imyeyu.api.modules.common.entity.Attachment; import com.imyeyu.spring.annotation.table.Transient; import com.imyeyu.spring.entity.UUIDEntity; import lombok.Data; import lombok.EqualsAndHashCode; import java.math.BigDecimal; +import java.util.List; /// GAO 客户套餐产品权益快照 /// @@ -24,6 +26,10 @@ public class GaoCustomerPackageItem extends UUIDEntity { /// 产品单位 ID private String productUnitId; + /// 产品图片附件 + @Transient + private List attachmentList; + /// 产品名称快照 private String productNameSnapshot; diff --git a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoFundRecord.java b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoFundRecord.java index 85992c8..20390d0 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoFundRecord.java +++ b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoFundRecord.java @@ -101,6 +101,17 @@ public class GaoFundRecord extends UUIDEntity { /// 备注 private String remark; + /// 关联的冲正流水 ID + @Transient + private String revokeRecordId; + + /// 关联销售订单总价,单位分 + @Transient + private Long salesOrderAmount; + + /// 流水发生时关联销售订单剩余待收款金额快照,单位分 + private Long salesOrderUnpaidAmountSnapshot; + /// 操作人 @Transient private User operator; diff --git a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoOrderFulfillment.java b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoOrderFulfillment.java index c7b5e88..bb662d6 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoOrderFulfillment.java +++ b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoOrderFulfillment.java @@ -2,8 +2,13 @@ package com.imyeyu.api.modules.gao.entity; import com.imyeyu.api.modules.common.entity.Attachment; import com.imyeyu.api.modules.user.entity.User; +import com.imyeyu.spring.annotation.table.AutoUUID; +import com.imyeyu.spring.annotation.table.Id; import com.imyeyu.spring.annotation.table.Transient; +import com.imyeyu.spring.entity.Creatable; +import com.imyeyu.spring.entity.IDEntity; import com.imyeyu.spring.entity.UUIDEntity; +import com.imyeyu.spring.entity.Updatable; import lombok.Data; import lombok.EqualsAndHashCode; @@ -14,8 +19,7 @@ import java.util.List; /// @author Codex /// @since 2026-09-02 @Data -@EqualsAndHashCode(callSuper = true) -public class GaoOrderFulfillment extends UUIDEntity { +public class GaoOrderFulfillment implements IDEntity, Creatable, Updatable { /// 履约来源类型 public enum FulfillmentType { @@ -49,6 +53,10 @@ public class GaoOrderFulfillment extends UUIDEntity { REVOKED } + @Id + @AutoUUID + private String id; + /// 销售订单 ID private String salesOrderId; @@ -88,10 +96,18 @@ public class GaoOrderFulfillment extends UUIDEntity { /// 备注 private String remark; + private Long createdAt; + + private Long updatedAt; + /// 提交人 @Transient private User operator; + /// 客户 + @Transient + private GaoCustomer customer; + /// 核销或拒绝操作人 @Transient private User verifier; diff --git a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoOrderFulfillmentItem.java b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoOrderFulfillmentItem.java index 4c08de0..3f8cf9a 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoOrderFulfillmentItem.java +++ b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoOrderFulfillmentItem.java @@ -1,18 +1,26 @@ package com.imyeyu.api.modules.gao.entity; -import com.imyeyu.spring.entity.UUIDEntity; +import com.imyeyu.api.modules.common.entity.Attachment; +import com.imyeyu.spring.annotation.table.AutoUUID; +import com.imyeyu.spring.annotation.table.Id; +import com.imyeyu.spring.annotation.table.Transient; +import com.imyeyu.spring.entity.Creatable; +import com.imyeyu.spring.entity.IDEntity; import lombok.Data; -import lombok.EqualsAndHashCode; import java.math.BigDecimal; +import java.util.List; /// GAO 统一履约明细 /// /// @author Codex /// @since 2026-09-02 @Data -@EqualsAndHashCode(callSuper = true) -public class GaoOrderFulfillmentItem extends UUIDEntity { +public class GaoOrderFulfillmentItem implements IDEntity, Creatable { + + @Id + @AutoUUID + private String id; /// 履约单 ID private String fulfillmentId; @@ -23,6 +31,10 @@ public class GaoOrderFulfillmentItem extends UUIDEntity { /// 产品单位 ID private String productUnitId; + /// 产品图片附件 + @Transient + private List attachmentList; + /// 直接订单明细 ID private String sourceOrderItemId; @@ -55,4 +67,6 @@ public class GaoOrderFulfillmentItem extends UUIDEntity { /// 基础单位数量系数快照 private BigDecimal baseQuantityPerUnitSnapshot; + + private Long createdAt; } diff --git a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoPackageConfigItem.java b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoPackageConfigItem.java index a3d7a1b..7562100 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoPackageConfigItem.java +++ b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoPackageConfigItem.java @@ -1,5 +1,6 @@ package com.imyeyu.api.modules.gao.entity; +import com.imyeyu.spring.annotation.table.Transient; import com.imyeyu.spring.entity.UUIDEntity; import lombok.Data; import lombok.EqualsAndHashCode; @@ -40,4 +41,8 @@ public class GaoPackageConfigItem extends UUIDEntity { /// 备注 private String remark; + + /// 产品详情 + @Transient + private GaoProduct product; } diff --git a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoSalesOrderItem.java b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoSalesOrderItem.java index b41b8f0..0c0479a 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/entity/GaoSalesOrderItem.java +++ b/src/main/java/com/imyeyu/api/modules/gao/entity/GaoSalesOrderItem.java @@ -1,10 +1,13 @@ package com.imyeyu.api.modules.gao.entity; +import com.imyeyu.api.modules.common.entity.Attachment; +import com.imyeyu.spring.annotation.table.Transient; import com.imyeyu.spring.entity.UUIDEntity; import lombok.Data; import lombok.EqualsAndHashCode; import java.math.BigDecimal; +import java.util.List; /// GAO 直接产品订单明细 /// @@ -23,6 +26,10 @@ public class GaoSalesOrderItem extends UUIDEntity { /// 产品单位 ID private String productUnitId; + /// 产品图片附件 + @Transient + private List attachmentList; + /// 产品名称快照 private String productNameSnapshot; diff --git a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoFundRecordMapper.java b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoFundRecordMapper.java index 4f130b0..31d1289 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoFundRecordMapper.java +++ b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoFundRecordMapper.java @@ -19,6 +19,9 @@ public interface GaoFundRecordMapper extends BaseMapper { /// 锁定资金记录 GaoFundRecord selectForUpdate(@Param("id") String id); + /// 查询资金记录及关联销售订单资金摘要 + GaoFundRecord selectWithSalesOrderSummary(@Param("id") String id); + /// 查询资金冲正记录 GaoFundRecord selectRevokeBySourceRecordId(@Param("sourceRecordId") String sourceRecordId); diff --git a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoOrderFulfillmentItemMapper.java b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoOrderFulfillmentItemMapper.java index ead64ba..513d47b 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoOrderFulfillmentItemMapper.java +++ b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoOrderFulfillmentItemMapper.java @@ -15,6 +15,9 @@ public interface GaoOrderFulfillmentItemMapper extends BaseMapper selectByFulfillmentId(@Param("fulfillmentId") String fulfillmentId); + /// 批量查询履约明细 + List selectByFulfillmentIdList(@Param("fulfillmentIdList") List fulfillmentIdList); + /// 锁定履约明细 List selectByFulfillmentIdForUpdate(@Param("fulfillmentId") String fulfillmentId); } diff --git a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoProductMapper.java b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoProductMapper.java index f46e8da..5841a47 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoProductMapper.java +++ b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoProductMapper.java @@ -5,6 +5,7 @@ import com.imyeyu.api.modules.gao.vo.GaoProductPage; import com.imyeyu.spring.mapper.BaseMapper; import org.apache.ibatis.annotations.Param; +import java.util.Collection; import java.util.List; /// GAO 产品 Mapper @@ -19,6 +20,9 @@ public interface GaoProductMapper extends BaseMapper { /// 按条件查询产品 List selectByQuery(GaoProductPage page); + /// 批量查询产品 + List selectByIdList(@Param("idList") Collection idList); + /// 查询产品编码 GaoProduct selectByStoreIdAndCode(@Param("storeId") String storeId, @Param("code") String code, @Param("excludeId") String excludeId); diff --git a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoSalesOrderItemMapper.java b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoSalesOrderItemMapper.java index cb3a58b..a51eb31 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoSalesOrderItemMapper.java +++ b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoSalesOrderItemMapper.java @@ -16,6 +16,9 @@ public interface GaoSalesOrderItemMapper extends BaseMapper selectBySalesOrderId(@Param("salesOrderId") String salesOrderId); + /// 批量查询订单明细 + List selectBySalesOrderIdList(@Param("salesOrderIdList") List salesOrderIdList); + /// 锁定订单明细 GaoSalesOrderItem selectForUpdate(@Param("id") String id); diff --git a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoSalesOrderMapper.java b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoSalesOrderMapper.java index 7829995..185e0a8 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoSalesOrderMapper.java +++ b/src/main/java/com/imyeyu/api/modules/gao/mapper/GaoSalesOrderMapper.java @@ -37,4 +37,7 @@ public interface GaoSalesOrderMapper extends BaseMapper { /// 更新订单履约状态 int updateStatus(@Param("id") String id, @Param("status") GaoSalesOrder.Status status); + + /// 更新订单备注 + int updateRemark(@Param("id") String id, @Param("remark") String remark); } diff --git a/src/main/java/com/imyeyu/api/modules/gao/script/GaoPointScriptContext.java b/src/main/java/com/imyeyu/api/modules/gao/script/GaoPointScriptContext.java index cd98c2c..944b7bd 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/script/GaoPointScriptContext.java +++ b/src/main/java/com/imyeyu/api/modules/gao/script/GaoPointScriptContext.java @@ -5,6 +5,7 @@ import com.imyeyu.api.modules.gao.entity.GaoPointRule; import com.imyeyu.api.modules.gao.service.GaoCustomerConsumptionRecordService; import com.imyeyu.api.modules.gao.service.GaoEventRecordService; import com.imyeyu.java.bean.timi.TimiException; +import lombok.Getter; import java.time.Instant; import java.time.LocalDate; @@ -28,11 +29,18 @@ public class GaoPointScriptContext { private final GaoCustomerConsumptionRecordService consumptionService; private final GaoEventRecordService eventRecordService; private final GaoPointRule rule; + + @Getter private final String storeId; + @Getter private final String customerId; + @Getter private final String triggerSourceType; + @Getter private final String triggerRefType; + @Getter private final String triggerRefId; + @Getter private final long occurredAt; /// 参与过金额计算的消费事实,ID 到金额摘要 @@ -51,36 +59,6 @@ public class GaoPointScriptContext { this.occurredAt = occurredAt; } - /// 触发来源门店 ID - public String getStoreId() { - return storeId; - } - - /// 触发来源客户 ID - public String getCustomerId() { - return customerId; - } - - /// 直接触发来源类型 - public String getTriggerSourceType() { - return triggerSourceType; - } - - /// 触发对象筛选类型 - public String getTriggerRefType() { - return triggerRefType; - } - - /// 触发对象筛选 ID - public String getTriggerRefId() { - return triggerRefId; - } - - /// 触发发生时间,毫秒 - public long getOccurredAt() { - return occurredAt; - } - /// 查询触发时间往前滚动一年的有效消费金额,窗口按 Asia/Shanghai 自然日对齐 /// /// @return 窗口内 ACTIVE 消费事实金额合计,单位分 diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/GaoFundRecordService.java b/src/main/java/com/imyeyu/api/modules/gao/service/GaoFundRecordService.java index 9595a3f..7263186 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/GaoFundRecordService.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/GaoFundRecordService.java @@ -16,6 +16,9 @@ public interface GaoFundRecordService extends BaseService /// 按条件查询资金流水 PageResult pageByQuery(GaoFundRecordPage page); + /// 查询资金流水详情 + GaoFundRecord detail(String id); + /// 按销售订单查询资金流水 List listByBizId(String salesOrderId); diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/GaoProductService.java b/src/main/java/com/imyeyu/api/modules/gao/service/GaoProductService.java index 139c85f..84814cd 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/GaoProductService.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/GaoProductService.java @@ -7,6 +7,7 @@ import com.imyeyu.api.modules.gao.vo.GaoProductUnitSelection; import com.imyeyu.spring.bean.PageResult; import com.imyeyu.spring.service.BaseService; +import java.util.Collection; import java.util.List; /// GAO 产品服务 @@ -21,6 +22,9 @@ public interface GaoProductService extends BaseService { /// 查询产品详情单位 GaoProduct detail(String id); + /// 批量查询产品详情 + List listByIdList(Collection idList); + /// 查询启用且允许销售的产品单位 List listSellable(GaoProductPage page); diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/GaoSalesOrderService.java b/src/main/java/com/imyeyu/api/modules/gao/service/GaoSalesOrderService.java index e6d9bea..1ba8598 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/GaoSalesOrderService.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/GaoSalesOrderService.java @@ -6,6 +6,7 @@ import com.imyeyu.api.modules.gao.vo.GaoDirectProductOrderCreateRequest; import com.imyeyu.api.modules.gao.vo.GaoPackageOrderCreateData; import com.imyeyu.api.modules.gao.vo.GaoSalesOrderCancelRequest; import com.imyeyu.api.modules.gao.vo.GaoSalesOrderPage; +import com.imyeyu.api.modules.gao.vo.GaoSalesOrderUpdateRequest; import com.imyeyu.spring.bean.PageResult; import com.imyeyu.spring.service.BaseService; @@ -27,6 +28,9 @@ public interface GaoSalesOrderService extends BaseService /// 创建直接产品订单 GaoSalesOrder createDirectProductOrder(GaoDirectProductOrderCreateRequest request); + /// 只更新订单备注和附件 + GaoSalesOrder updateRemarkAndAttachments(GaoSalesOrderUpdateRequest request); + /// 按门店和幂等号查询订单 /// /// @param storeId 门店 ID diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoFundRecordServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoFundRecordServiceImplement.java index f91c4e0..8ac00da 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoFundRecordServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoFundRecordServiceImplement.java @@ -75,6 +75,16 @@ public class GaoFundRecordServiceImplement extends AbstractEntityService listByBizId(String salesOrderId) { TimiException.required(salesOrderId, "not found salesOrderId"); diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoOrderFulfillmentServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoOrderFulfillmentServiceImplement.java index 0b4ec7b..1b9a334 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoOrderFulfillmentServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoOrderFulfillmentServiceImplement.java @@ -126,7 +126,9 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService itemList = itemMapper.selectByFulfillmentId(fulfillment.getId()); + fillItemAttachments(itemList); + fulfillment.setItemList(itemList); fulfillment.setStockRecordList(stockRecordService.listByBiz(GaoProductStockRecord.BizType.ORDER_FULFILLMENT, fulfillment.getId())); return fulfillment; } @@ -659,8 +661,52 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService list) { - for (GaoOrderFulfillment fulfillment : TimiJava.safeIterable(list)) { - fulfillment.setItemList(itemMapper.selectByFulfillmentId(fulfillment.getId())); + if (list == null || list.isEmpty()) { + return; + } + List fulfillmentIdList = list.stream() + .map(GaoOrderFulfillment::getId) + .filter(value -> value != null && !value.isBlank()) + .distinct() + .toList(); + if (fulfillmentIdList.isEmpty()) { + for (GaoOrderFulfillment fulfillment : list) { + fulfillment.setItemList(List.of()); + } + return; + } + Map> itemMap = new HashMap<>(); + List queriedItemList = itemMapper.selectByFulfillmentIdList(fulfillmentIdList); + for (GaoOrderFulfillmentItem item : queriedItemList) { + itemMap.computeIfAbsent(item.getFulfillmentId(), key -> new ArrayList<>()).add(item); + } + List itemList = new ArrayList<>(); + for (GaoOrderFulfillment fulfillment : list) { + List fulfillmentItemList = itemMap.getOrDefault(fulfillment.getId(), List.of()); + fulfillment.setItemList(fulfillmentItemList); + itemList.addAll(fulfillmentItemList); + } + fillItemAttachments(itemList); + } + + private void fillItemAttachments(List itemList) { + if (itemList == null || itemList.isEmpty()) { + return; + } + List productIdList = itemList.stream() + .map(GaoOrderFulfillmentItem::getProductId) + .filter(value -> value != null && !value.isBlank()) + .distinct() + .toList(); + if (productIdList.isEmpty()) { + return; + } + Map> attachmentMap = attachmentService.mapByBizIdList( + Attachment.BizType.GAO_PRODUCT, + productIdList, + GaoProduct.AttachType.PHOTO.name()); + for (GaoOrderFulfillmentItem item : itemList) { + item.setAttachmentList(attachmentMap.getOrDefault(item.getProductId(), List.of())); } } diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoOrderPaymentServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoOrderPaymentServiceImplement.java index 47da3ad..c1dfd6b 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoOrderPaymentServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoOrderPaymentServiceImplement.java @@ -256,6 +256,7 @@ public class GaoOrderPaymentServiceImplement implements GaoOrderPaymentService { record.setBizId(order.getId()); record.setOperationType(operationType); record.setAmount(amount); + record.setSalesOrderUnpaidAmountSnapshot(safe(order.getSaleAmount()) - safe(order.getPaidAmount())); record.setRequestNo(requestNo); record.setOperatorUserId(userLoginService.getRequireLoginUserId()); record.setOccurredAt(TimiJava.defaultIfNull(occurredAt, Time.now())); 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 7030a81..bfb6331 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 @@ -29,6 +29,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -75,6 +76,16 @@ public class GaoProductServiceImplement extends AbstractEntityService listByIdList(Collection idList) { + if (idList == null || idList.isEmpty()) { + return List.of(); + } + List list = mapper.selectByIdList(idList); + fillTransient(list); + return list; + } + @Override public List listSellable(GaoProductPage page) { TimiException.required(page, "not found product page"); diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoSalesOrderServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoSalesOrderServiceImplement.java index 62322d6..addea2c 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoSalesOrderServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoSalesOrderServiceImplement.java @@ -34,6 +34,7 @@ import com.imyeyu.api.modules.gao.vo.GaoProductUnitSelection; import com.imyeyu.api.modules.gao.vo.GaoSalesOrderCancelRequest; import com.imyeyu.api.modules.gao.vo.GaoSalesOrderItemRequest; import com.imyeyu.api.modules.gao.vo.GaoSalesOrderPage; +import com.imyeyu.api.modules.gao.vo.GaoSalesOrderUpdateRequest; import com.imyeyu.api.modules.system.entity.Logger; import com.imyeyu.api.modules.system.service.LoggerService; import com.imyeyu.api.modules.user.service.PermissionChecker; @@ -55,8 +56,10 @@ import java.math.BigDecimal; import java.math.RoundingMode; import java.util.ArrayList; import java.util.Comparator; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.UUID; @@ -100,6 +103,7 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService list = mapper.selectByQuery(page); fillItemList(list); + fillCustomer(list); result.setList(list); return result; } @@ -109,7 +113,11 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService itemList = itemMapper.selectBySalesOrderId(order.getId()); + fillItemAttachments(itemList); + order.setItemList(itemList); + fillCustomer(List.of(order)); + order.setAttachmentList(attachmentService.listByBizId(Attachment.BizType.GAO_SALES_ORDER, order.getId())); order.setFundRecordList(fundRecordService.listByBizId(order.getId())); if (order.getSourceEventRecordId() != null) { order.setSourceEventRecord(eventRecordService.get(order.getSourceEventRecordId())); @@ -132,6 +140,19 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService createDirectInternal(request)); } + @Transactional(TimiServerDBConfig.ROLLBACKER) + @Override + public GaoSalesOrder updateRemarkAndAttachments(GaoSalesOrderUpdateRequest request) { + return loggerService.execute(Logger.Module.GAO, "GAO_SALES_ORDER_UPDATE", request, () -> { + TimiException.required(request, "not found sales order update request"); + TimiException.required(request.getId(), "not found sales order.id"); + GaoSalesOrder order = getForUpdate(request.getId()); + TimiException.requiredTrue(1 == mapper.updateRemark(order.getId(), request.getRemark()), "订单备注更新失败"); + attachmentService.updateByBizId(Attachment.BizType.GAO_SALES_ORDER, order.getId(), request.getAttachmentList()); + return detail(order.getId()); + }); + } + private GaoSalesOrder createDirectInternal(GaoDirectProductOrderCreateRequest request) { TimiException.required(request, "not found direct product order request"); String requestNo = requiredText(request.getRequestNo(), "not found requestNo"); @@ -436,10 +457,74 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService orderList) { + if (orderList == null || orderList.isEmpty()) { + return; + } + List orderIdList = orderList.stream() + .map(GaoSalesOrder::getId) + .filter(value -> value != null && !value.isBlank()) + .distinct() + .toList(); + Map> itemMap = new HashMap<>(); + if (!orderIdList.isEmpty()) { + List queriedItemList = itemMapper.selectBySalesOrderIdList(orderIdList); + for (GaoSalesOrderItem item : TimiJava.safeIterable(queriedItemList)) { + itemMap.computeIfAbsent(item.getSalesOrderId(), key -> new ArrayList<>()).add(item); + } + } + List itemList = new ArrayList<>(); for (GaoSalesOrder order : TimiJava.safeIterable(orderList)) { - order.setItemList(itemMapper.selectBySalesOrderId(order.getId())); + List orderItemList = itemMap.getOrDefault(order.getId(), List.of()); + order.setItemList(orderItemList); + itemList.addAll(orderItemList); order.setUnpaidAmount(safe(order.getSaleAmount()) - safe(order.getPaidAmount())); } + fillItemAttachments(itemList); + } + + private void fillItemAttachments(List itemList) { + if (itemList == null || itemList.isEmpty()) { + return; + } + List productIdList = itemList.stream() + .map(GaoSalesOrderItem::getProductId) + .filter(value -> value != null && !value.isBlank()) + .distinct() + .toList(); + if (productIdList.isEmpty()) { + return; + } + Map> attachmentMap = attachmentService.mapByBizIdList( + Attachment.BizType.GAO_PRODUCT, + productIdList, + GaoProduct.AttachType.PHOTO.name()); + for (GaoSalesOrderItem item : itemList) { + item.setAttachmentList(attachmentMap.getOrDefault(item.getProductId(), List.of())); + } + } + + private void fillCustomer(List orderList) { + Set customerIdSet = new HashSet<>(); + for (GaoSalesOrder order : TimiJava.safeIterable(orderList)) { + if (TimiJava.isNotEmpty(order.getCustomerId())) { + customerIdSet.add(order.getCustomerId()); + } + } + if (customerIdSet.isEmpty()) { + return; + } + + List customerIdList = new ArrayList<>(customerIdSet); + Map customerMap = customerService.mapByIdList(customerIdList); + Map> attachmentMap = attachmentService.mapByBizIdList(Attachment.BizType.GAO_CUSTOMER, customerIdList); + for (GaoSalesOrder order : TimiJava.safeIterable(orderList)) { + GaoCustomer customer = customerMap.get(order.getCustomerId()); + if (customer == null) { + continue; + } + customer.setAttachmentList(attachmentMap.getOrDefault(customer.getId(), List.of())); + order.setCustomer(customer); + } } private BigDecimal validateQuantity(GaoProduct product, BigDecimal quantity) { diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoProductStockRecordPage.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoProductStockRecordPage.java index 7e68124..0851368 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoProductStockRecordPage.java +++ b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoProductStockRecordPage.java @@ -31,6 +31,18 @@ public class GaoProductStockRecordPage extends BasePage { /// 操作类型 private GaoProductStockRecord.OperationType operationType; + /// 仅查询仍然生效的履约预占流水 + private Boolean activeReservationOnly; + + /// 库存变动方向,IN 为入库,OUT 为出库 + public enum Direction { + IN, + OUT + } + + /// 库存变动方向 + private Direction direction; + /// 起始发生时间 private Long beginAt; diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoSalesOrderPage.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoSalesOrderPage.java index 32a73cd..4c87fcb 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoSalesOrderPage.java +++ b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoSalesOrderPage.java @@ -28,6 +28,12 @@ public class GaoSalesOrderPage extends BasePage { /// 付款状态 private GaoSalesOrder.PaymentStatus paymentStatus; + /// 退款状态 + private GaoSalesOrder.RefundStatus refundStatus; + + /// true 为筛选可收款且未付清订单 + private Boolean pendingReceive; + /// true 为筛选已履约但未付清订单 private Boolean fulfilledButUnpaid; diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoSalesOrderUpdateRequest.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoSalesOrderUpdateRequest.java new file mode 100644 index 0000000..237194f --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoSalesOrderUpdateRequest.java @@ -0,0 +1,25 @@ +package com.imyeyu.api.modules.gao.vo; + +import com.imyeyu.api.modules.common.entity.Attachment; +import lombok.Data; + +import java.util.List; + +/// GAO 销售订单备注和附件修改请求 +/// +/// 该请求只允许修改备注和订单附件,不承载订单其他字段。 +/// +/// @author Codex +/// @since 2026-09-05 +@Data +public class GaoSalesOrderUpdateRequest { + + /// 订单 ID + private String id; + + /// 订单附件 + private List attachmentList; + + /// 备注 + private String remark; +} diff --git a/src/main/java/com/imyeyu/api/modules/user/controller/RoleController.java b/src/main/java/com/imyeyu/api/modules/user/controller/RoleController.java index d0f09f6..eac4d8d 100644 --- a/src/main/java/com/imyeyu/api/modules/user/controller/RoleController.java +++ b/src/main/java/com/imyeyu/api/modules/user/controller/RoleController.java @@ -49,10 +49,10 @@ public class RoleController { @RequireCorePermission(CorePermissionCode.ROLE_READ) @PostMapping("/list") public PageResult list(@RequestBody Page page) { - Set manageableRoleIdSet = listManageableRoleId(); + Set visibleRoleIdSet = listVisibleRoleId(); PageResult result = service.page(page); if (result.getList() != null) { - result.setList(result.getList().stream().filter(role -> manageableRoleIdSet.contains(role.getId())).toList()); + result.setList(result.getList().stream().filter(role -> visibleRoleIdSet.contains(role.getId())).toList()); result.getList().forEach(this::fillRoleDetail); } return result; @@ -83,7 +83,7 @@ public class RoleController { @PostMapping("/detail") public Role detail(@RequestParam String id) { Role role = service.get(id); - requireManageableRole(role.getId()); + requireVisibleRole(role.getId()); fillRoleDetail(role); return role; } @@ -105,7 +105,7 @@ public class RoleController { @RequireCorePermission(CorePermissionCode.ROLE_PERMISSION_READ) @PostMapping("/permission/grantable/list") public List listGrantableRolePermission(@RequestParam String roleId) { - requireManageableRole(roleId); + requireVisibleRole(roleId); return service.listGrantableRolePermission(roleId); } @@ -137,6 +137,12 @@ public class RoleController { return service.listGrantableParentRole(roleId); } + @RequireCorePermission(CorePermissionCode.ROLE_CREATE) + @PostMapping("/parent/create/grantable/list") + public List listCreateGrantableParentRole(@RequestParam ModuleCode moduleCode) { + return service.listCreateGrantableParentRole(moduleCode); + } + @AOPLog @RequireCorePermission(CorePermissionCode.ROLE_UPDATE) @PostMapping("/parent/update") @@ -197,6 +203,19 @@ public class RoleController { .collect(Collectors.toSet()); } + private Set listVisibleRoleId() { + Set roleIdSet = listManageableRoleId(); + String loginUserId = userLoginService.getRequireLoginUserId(); + roleIdSet.addAll(userRoleService.listRoleByUserId(loginUserId).stream().map(Role::getId).toList()); + return roleIdSet; + } + + private void requireVisibleRole(String roleId) { + if (!listVisibleRoleId().contains(roleId)) { + throw new TimiException(TimiCode.PERMISSION_ERROR); + } + } + private void requireManageableRole(String roleId) { if (!listManageableRoleId().contains(roleId)) { throw new TimiException(TimiCode.PERMISSION_ERROR); diff --git a/src/main/java/com/imyeyu/api/modules/user/service/RoleService.java b/src/main/java/com/imyeyu/api/modules/user/service/RoleService.java index f3c5332..73f7f2f 100644 --- a/src/main/java/com/imyeyu/api/modules/user/service/RoleService.java +++ b/src/main/java/com/imyeyu/api/modules/user/service/RoleService.java @@ -75,6 +75,12 @@ public interface RoleService extends BaseService { /// @return 角色列表 List listGrantableParentRole(String roleId); + /// 查询创建模块角色时可选的管理父级角色 + /// + /// @param moduleCode 目标模块代码 + /// @return 角色列表 + List listCreateGrantableParentRole(ModuleCode moduleCode); + /// 修改角色管理父级 /// /// @param role 角色 diff --git a/src/main/java/com/imyeyu/api/modules/user/service/implement/AuthorizationBoundaryServiceImplement.java b/src/main/java/com/imyeyu/api/modules/user/service/implement/AuthorizationBoundaryServiceImplement.java index 30baa9c..e927f70 100644 --- a/src/main/java/com/imyeyu/api/modules/user/service/implement/AuthorizationBoundaryServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/user/service/implement/AuthorizationBoundaryServiceImplement.java @@ -83,11 +83,12 @@ public class AuthorizationBoundaryServiceImplement implements AuthorizationBound @Override public void checkCanCreateRole(String operatorUserId, Role role) { checkCorePermission(operatorUserId, CorePermissionCode.ROLE_CREATE); + TimiException.required(role.getModuleCode(), "not found role.moduleCode"); + TimiException.required(role.getOwnerType(), "not found role.ownerType"); + checkOwnerDefinition(role.getModuleCode(), role.getOwnerType(), role.getOwnerId()); if (authorizationScopeService.isSystemAdmin(operatorUserId)) { return; } - TimiException.required(role.getModuleCode(), "not found role.moduleCode"); - TimiException.required(role.getOwnerType(), "not found role.ownerType"); TimiException.requiredTrue(!Boolean.TRUE.equals(role.getBuiltin()), "不能创建内置角色"); TimiException.requiredTrue(!Boolean.TRUE.equals(role.getProtectedRole()), "不能创建受保护角色"); checkOwnerScope(operatorUserId, role.getModuleCode(), role.getOwnerType(), role.getOwnerId()); @@ -98,6 +99,11 @@ public class AuthorizationBoundaryServiceImplement implements AuthorizationBound checkCorePermission(operatorUserId, CorePermissionCode.ROLE_UPDATE); Role dbRole = roleMapper.selectById(role.getId()); TimiException.required(dbRole, "not found role"); + if (role.getOwnerType() != null || role.getOwnerId() != null) { + AuthOwnerType ownerType = TimiJava.defaultIfNull(role.getOwnerType(), dbRole.getOwnerType()); + String ownerId = TimiJava.defaultIfEmpty(role.getOwnerId(), dbRole.getOwnerId()); + checkOwnerDefinition(dbRole.getModuleCode(), ownerType, ownerId); + } if (authorizationScopeService.isSystemAdmin(operatorUserId)) { return; } @@ -339,6 +345,12 @@ public class AuthorizationBoundaryServiceImplement implements AuthorizationBound TimiException.requiredTrue(resolver == null || resolver.canUseOwnerScope(operatorUserId, ownerType, ownerId), "归属范围不在可管理范围内"); } + private void checkOwnerDefinition(ModuleCode moduleCode, AuthOwnerType ownerType, String ownerId) { + if (ownerType == AuthOwnerType.MODULE) { + TimiException.requiredTrue(moduleCode.name().equals(ownerId), "模块级归属 ID 必须等于模块代码"); + } + } + private void checkCorePermission(String operatorUserId, CorePermissionCode permissionCode) { TimiException.requiredTrue(hasPermission(operatorUserId, ModuleCode.CORE, permissionCode.getValue()), "权限不足:%s".formatted(permissionCode.getValue())); } diff --git a/src/main/java/com/imyeyu/api/modules/user/service/implement/RoleServiceImplement.java b/src/main/java/com/imyeyu/api/modules/user/service/implement/RoleServiceImplement.java index 99a5850..f425eab 100644 --- a/src/main/java/com/imyeyu/api/modules/user/service/implement/RoleServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/user/service/implement/RoleServiceImplement.java @@ -355,6 +355,25 @@ public class RoleServiceImplement extends AbstractEntityService im .toList(); } + @Override + public List listCreateGrantableParentRole(ModuleCode moduleCode) { + String loginUserId = userLoginService.getRequireLoginUserId(); + if (authorizationScopeService.isSystemAdmin(loginUserId)) { + return mapper.selectAll(); + } + Set directOperatorRoleIdSet = userRoleMapper.selectByUserIdList(List.of(loginUserId)) + .stream() + .map(UserRole::getRoleId) + .collect(Collectors.toSet()); + if (TimiJava.isEmpty(directOperatorRoleIdSet)) { + return List.of(); + } + return mapper.selectByIdList(directOperatorRoleIdSet) + .stream() + .filter(role -> role.getModuleCode() == moduleCode) + .toList(); + } + @Transactional(TimiServerDBConfig.ROLLBACKER) @Override public void updateRoleParent(Role role) { diff --git a/src/main/resources/db/migration/timiserver/V66__migrate_gao_legacy_consumption_events.sql b/src/main/resources/db/migration/timiserver/V66__migrate_gao_legacy_consumption_events.sql index 4321c74..91f0a51 100644 --- a/src/main/resources/db/migration/timiserver/V66__migrate_gao_legacy_consumption_events.sql +++ b/src/main/resources/db/migration/timiserver/V66__migrate_gao_legacy_consumption_events.sql @@ -1,6 +1,6 @@ ALTER TABLE `gao_point_ledger` - ADD COLUMN `sales_order_id` VARCHAR(36) NULL COMMENT '关联销售订单 ID' AFTER `trigger_source_id`, - ADD KEY `idx_gao_point_ledger_sales_order` (`sales_order_id`); + ADD COLUMN IF NOT EXISTS `sales_order_id` VARCHAR(36) NULL COMMENT '关联销售订单 ID' AFTER `trigger_source_id`, + ADD KEY IF NOT EXISTS `idx_gao_point_ledger_sales_order` (`sales_order_id`); -- 将旧规则 sourceEventId 指向的登记事件标记为退役消费事件 UPDATE `gao_event` e @@ -41,4 +41,4 @@ WHERE e.`biz_type` = 'LEGACY_CONSUMPTION' AND r.`number_value` IS NOT NULL AND r.`number_value` > 0 AND r.`deleted_at` IS NULL -ON DUPLICATE KEY UPDATE `id` = `id`; +ON DUPLICATE KEY UPDATE `id` = `gao_customer_consumption_record`.`id`; diff --git a/src/main/resources/db/migration/timiserver/V68__snapshot_gao_fund_record_order_unpaid_amount.sql b/src/main/resources/db/migration/timiserver/V68__snapshot_gao_fund_record_order_unpaid_amount.sql new file mode 100644 index 0000000..374ccf4 --- /dev/null +++ b/src/main/resources/db/migration/timiserver/V68__snapshot_gao_fund_record_order_unpaid_amount.sql @@ -0,0 +1,2 @@ +ALTER TABLE `gao_fund_record` + ADD COLUMN IF NOT EXISTS `sales_order_unpaid_amount_snapshot` BIGINT NULL COMMENT '流水发生时销售订单剩余待收款金额快照,单位分' AFTER `amount`; diff --git a/src/main/resources/mapper/timi-server/gao/GaoCustomerPackageMapper.xml b/src/main/resources/mapper/timi-server/gao/GaoCustomerPackageMapper.xml index ce6f6b6..035f9c4 100644 --- a/src/main/resources/mapper/timi-server/gao/GaoCustomerPackageMapper.xml +++ b/src/main/resources/mapper/timi-server/gao/GaoCustomerPackageMapper.xml @@ -26,8 +26,9 @@ diff --git a/src/main/resources/mapper/timi-server/gao/GaoFundRecordMapper.xml b/src/main/resources/mapper/timi-server/gao/GaoFundRecordMapper.xml index d97f06f..fa509cd 100644 --- a/src/main/resources/mapper/timi-server/gao/GaoFundRecordMapper.xml +++ b/src/main/resources/mapper/timi-server/gao/GaoFundRecordMapper.xml @@ -11,6 +11,13 @@ WHERE `id` = #{id} AND `deleted_at` IS NULL FOR UPDATE + f.`deleted_at` IS NULL @@ -41,7 +57,16 @@ SELECT COUNT(1) FROM `gao_fund_record` f WHERE diff --git a/src/main/resources/mapper/timi-server/gao/GaoOrderFulfillmentItemMapper.xml b/src/main/resources/mapper/timi-server/gao/GaoOrderFulfillmentItemMapper.xml index 2a1bd6f..0d4854b 100644 --- a/src/main/resources/mapper/timi-server/gao/GaoOrderFulfillmentItemMapper.xml +++ b/src/main/resources/mapper/timi-server/gao/GaoOrderFulfillmentItemMapper.xml @@ -6,6 +6,14 @@ WHERE `fulfillment_id` = #{fulfillmentId} ORDER BY `created_at` + + +