Commit df4cc6667dd1ad8fd49483f89f74f8129ce81453

Authored by 芋道源码
Committed by Gitee
2 parents 0552f860 87cee462

!1134 同步商城的客服优化、门店自提优化

Merge pull request !1134 from 芋道源码/develop
Showing 84 changed files with 1168 additions and 315 deletions
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/history/ProductBrowseHistoryController.java
@@ -51,7 +51,8 @@ public class ProductBrowseHistoryController { @@ -51,7 +51,8 @@ public class ProductBrowseHistoryController {
51 convertSet(pageResult.getList(), ProductBrowseHistoryDO::getSpuId)); 51 convertSet(pageResult.getList(), ProductBrowseHistoryDO::getSpuId));
52 return success(BeanUtils.toBean(pageResult, ProductBrowseHistoryRespVO.class, 52 return success(BeanUtils.toBean(pageResult, ProductBrowseHistoryRespVO.class,
53 vo -> Optional.ofNullable(spuMap.get(vo.getSpuId())) 53 vo -> Optional.ofNullable(spuMap.get(vo.getSpuId()))
54 - .ifPresent(spu -> vo.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()).setPrice(spu.getPrice())))); 54 + .ifPresent(spu -> vo.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()).setPrice(spu.getPrice())
  55 + .setSalesCount(spu.getSalesCount()).setStock(spu.getStock()))));
55 } 56 }
56 57
57 } 58 }
58 \ No newline at end of file 59 \ No newline at end of file
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/history/vo/ProductBrowseHistoryRespVO.java
1 package cn.iocoder.yudao.module.product.controller.admin.history.vo; 1 package cn.iocoder.yudao.module.product.controller.admin.history.vo;
2 2
3 import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; 3 import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
4 -import com.alibaba.excel.annotation.ExcelProperty;  
5 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
6 import lombok.Data; 5 import lombok.Data;
7 6
8 -import java.time.LocalDateTime;  
9 -  
10 import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; 7 import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
11 8
12 @Schema(description = "管理后台 - 商品浏览记录 Response VO") 9 @Schema(description = "管理后台 - 商品浏览记录 Response VO")
@@ -31,4 +28,10 @@ public class ProductBrowseHistoryRespVO { @@ -31,4 +28,10 @@ public class ProductBrowseHistoryRespVO {
31 @Schema(description = "商品单价", example = "100") 28 @Schema(description = "商品单价", example = "100")
32 private Integer price; 29 private Integer price;
33 30
  31 + @Schema(description = "商品销量", example = "100")
  32 + private Integer salesCount;
  33 +
  34 + @Schema(description = "库存", example = "100")
  35 + private Integer stock;
  36 +
34 } 37 }
35 \ No newline at end of file 38 \ No newline at end of file
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/history/AppProductBrowseHistoryController.java
@@ -67,7 +67,8 @@ public class AppProductBrowseHistoryController { @@ -67,7 +67,8 @@ public class AppProductBrowseHistoryController {
67 Map<Long, ProductSpuDO> spuMap = convertMap(productSpuService.getSpuList(spuIds), ProductSpuDO::getId); 67 Map<Long, ProductSpuDO> spuMap = convertMap(productSpuService.getSpuList(spuIds), ProductSpuDO::getId);
68 return success(BeanUtils.toBean(pageResult, AppProductBrowseHistoryRespVO.class, 68 return success(BeanUtils.toBean(pageResult, AppProductBrowseHistoryRespVO.class,
69 vo -> Optional.ofNullable(spuMap.get(vo.getSpuId())) 69 vo -> Optional.ofNullable(spuMap.get(vo.getSpuId()))
70 - .ifPresent(spu -> vo.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()).setPrice(spu.getPrice())))); 70 + .ifPresent(spu -> vo.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()).setPrice(spu.getPrice())
  71 + .setSalesCount(spu.getSalesCount()).setStock(spu.getStock()))));
71 } 72 }
72 73
73 } 74 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/history/vo/AppProductBrowseHistoryRespVO.java
@@ -17,13 +17,19 @@ public class AppProductBrowseHistoryRespVO { @@ -17,13 +17,19 @@ public class AppProductBrowseHistoryRespVO {
17 17
18 // ========== 商品相关字段 ========== 18 // ========== 商品相关字段 ==========
19 19
20 - @Schema(description = "商品 SPU 名称", example = "赵六") 20 + @Schema(description = "商品 SPU 名称", requiredMode = REQUIRED, example = "赵六")
21 private String spuName; 21 private String spuName;
22 22
23 - @Schema(description = "商品封面图", example = "https://domain/pic.png") 23 + @Schema(description = "商品封面图", requiredMode = REQUIRED, example = "https://www.iocoder.cn/pic.png")
24 private String picUrl; 24 private String picUrl;
25 25
26 - @Schema(description = "商品单价", example = "100") 26 + @Schema(description = "商品单价", requiredMode = REQUIRED, example = "50")
27 private Integer price; 27 private Integer price;
28 28
  29 + @Schema(description = "商品销量", requiredMode = REQUIRED, example = "60")
  30 + private Integer salesCount;
  31 +
  32 + @Schema(description = "库存", requiredMode = REQUIRED, example = "80")
  33 + private Integer stock;
  34 +
29 } 35 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/KeFuConversationController.java
@@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.member.api.user.MemberUserApi; @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
6 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; 6 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
7 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationRespVO; 7 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationRespVO;
8 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationUpdatePinnedReqVO; 8 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationUpdatePinnedReqVO;
  9 +import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO;
9 import cn.iocoder.yudao.module.promotion.service.kefu.KeFuConversationService; 10 import cn.iocoder.yudao.module.promotion.service.kefu.KeFuConversationService;
10 import io.swagger.v3.oas.annotations.Operation; 11 import io.swagger.v3.oas.annotations.Operation;
11 import io.swagger.v3.oas.annotations.Parameter; 12 import io.swagger.v3.oas.annotations.Parameter;
@@ -34,6 +35,25 @@ public class KeFuConversationController { @@ -34,6 +35,25 @@ public class KeFuConversationController {
34 @Resource 35 @Resource
35 private MemberUserApi memberUserApi; 36 private MemberUserApi memberUserApi;
36 37
  38 + @GetMapping("/get")
  39 + @Operation(summary = "获得客服会话")
  40 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  41 + @PreAuthorize("@ss.hasPermission('promotion:kefu-conversation:query')")
  42 + public CommonResult<KeFuConversationRespVO> getConversation(@RequestParam("id") Long id) {
  43 + KeFuConversationDO conversation = conversationService.getConversation(id);
  44 + if (conversation == null) {
  45 + return success(null);
  46 + }
  47 +
  48 + // 拼接数据
  49 + KeFuConversationRespVO result = BeanUtils.toBean(conversation, KeFuConversationRespVO.class);
  50 + MemberUserRespDTO memberUser = memberUserApi.getUser(conversation.getUserId());
  51 + if (memberUser != null) {
  52 + result.setUserAvatar(memberUser.getAvatar()).setUserNickname(memberUser.getNickname());
  53 + }
  54 + return success(result);
  55 + }
  56 +
37 @PutMapping("/update-conversation-pinned") 57 @PutMapping("/update-conversation-pinned")
38 @Operation(summary = "置顶/取消置顶客服会话") 58 @Operation(summary = "置顶/取消置顶客服会话")
39 @PreAuthorize("@ss.hasPermission('promotion:kefu-conversation:update')") 59 @PreAuthorize("@ss.hasPermission('promotion:kefu-conversation:update')")
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/KeFuMessageController.java
@@ -2,9 +2,8 @@ package cn.iocoder.yudao.module.promotion.controller.admin.kefu; @@ -2,9 +2,8 @@ package cn.iocoder.yudao.module.promotion.controller.admin.kefu;
2 2
3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; 3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7 -import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessagePageReqVO; 6 +import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageListReqVO;
8 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageRespVO; 7 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageRespVO;
9 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageSendReqVO; 8 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageSendReqVO;
10 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO; 9 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;
@@ -20,6 +19,7 @@ import org.springframework.security.access.prepost.PreAuthorize; @@ -20,6 +19,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
20 import org.springframework.validation.annotation.Validated; 19 import org.springframework.validation.annotation.Validated;
21 import org.springframework.web.bind.annotation.*; 20 import org.springframework.web.bind.annotation.*;
22 21
  22 +import java.util.List;
23 import java.util.Map; 23 import java.util.Map;
24 24
25 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 25 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -56,19 +56,18 @@ public class KeFuMessageController { @@ -56,19 +56,18 @@ public class KeFuMessageController {
56 return success(true); 56 return success(true);
57 } 57 }
58 58
59 - @GetMapping("/page")  
60 - @Operation(summary = "获得客服消息分页") 59 + @GetMapping("/list")
  60 + @Operation(summary = "获得客服消息列表")
61 @PreAuthorize("@ss.hasPermission('promotion:kefu-message:query')") 61 @PreAuthorize("@ss.hasPermission('promotion:kefu-message:query')")
62 - public CommonResult<PageResult<KeFuMessageRespVO>> getKeFuMessagePage(@Valid KeFuMessagePageReqVO pageReqVO) { 62 + public CommonResult<List<KeFuMessageRespVO>> getKeFuMessageList(@Valid KeFuMessageListReqVO pageReqVO) {
63 // 获得数据 63 // 获得数据
64 - PageResult<KeFuMessageDO> pageResult = messageService.getKeFuMessagePage(pageReqVO); 64 + List<KeFuMessageDO> list = messageService.getKeFuMessageList(pageReqVO);
65 65
66 // 拼接数据 66 // 拼接数据
67 - PageResult<KeFuMessageRespVO> result = BeanUtils.toBean(pageResult, KeFuMessageRespVO.class);  
68 - Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(filterList(result.getList(), 67 + List<KeFuMessageRespVO> result = BeanUtils.toBean(list, KeFuMessageRespVO.class);
  68 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(filterList(result,
69 item -> UserTypeEnum.ADMIN.getValue().equals(item.getSenderType())), KeFuMessageRespVO::getSenderId)); 69 item -> UserTypeEnum.ADMIN.getValue().equals(item.getSenderType())), KeFuMessageRespVO::getSenderId));
70 - result.getList().forEach(item-> findAndThen(userMap, item.getSenderId(),  
71 - user -> item.setSenderAvatar(user.getAvatar()))); 70 + result.forEach(item -> findAndThen(userMap, item.getSenderId(), user -> item.setSenderAvatar(user.getAvatar())));
72 return success(result); 71 return success(result);
73 } 72 }
74 73
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/vo/message/KeFuMessageListReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.Max;
  5 +import jakarta.validation.constraints.Min;
  6 +import jakarta.validation.constraints.NotNull;
  7 +import lombok.Data;
  8 +import org.springframework.format.annotation.DateTimeFormat;
  9 +
  10 +import java.time.LocalDateTime;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  13 +
  14 +@Schema(description = "管理后台 - 客服消息列表 Request VO")
  15 +@Data
  16 +public class KeFuMessageListReqVO {
  17 +
  18 + private static final Integer LIMIT = 10;
  19 +
  20 + @Schema(description = "会话编号", example = "12580")
  21 + @NotNull(message = "会话编号不能为空")
  22 + private Long conversationId;
  23 +
  24 + @Schema(description = "发送时间", example = "2024-03-27 12:00:00")
  25 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  26 + private LocalDateTime createTime;
  27 +
  28 + @Schema(description = "每次查询条数,最大值为 100", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  29 + @NotNull(message = "每次查询条数不能为空")
  30 + @Min(value = 1, message = "每次查询条数最小值为 1")
  31 + @Max(value = 100, message = "每次查询最大值为 100")
  32 + private Integer limit = LIMIT;
  33 +
  34 +}
0 \ No newline at end of file 35 \ No newline at end of file
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/vo/message/KeFuMessagePageReqVO.java deleted
1 -package cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message;  
2 -  
3 -import lombok.*;  
4 -import io.swagger.v3.oas.annotations.media.Schema;  
5 -import cn.iocoder.yudao.framework.common.pojo.PageParam;  
6 -  
7 -@Schema(description = "管理后台 - 客服消息分页 Request VO")  
8 -@Data  
9 -@ToString(callSuper = true)  
10 -public class KeFuMessagePageReqVO extends PageParam {  
11 -  
12 - @Schema(description = "会话编号", example = "12580")  
13 - private Long conversationId;  
14 -  
15 -}  
16 \ No newline at end of file 0 \ No newline at end of file
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/vo/activity/PointActivityPageReqVO.java
@@ -5,11 +5,6 @@ import io.swagger.v3.oas.annotations.media.Schema; @@ -5,11 +5,6 @@ import io.swagger.v3.oas.annotations.media.Schema;
5 import lombok.Data; 5 import lombok.Data;
6 import lombok.EqualsAndHashCode; 6 import lombok.EqualsAndHashCode;
7 import lombok.ToString; 7 import lombok.ToString;
8 -import org.springframework.format.annotation.DateTimeFormat;  
9 -  
10 -import java.time.LocalDateTime;  
11 -  
12 -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;  
13 8
14 @Schema(description = "管理后台 - 积分商城活动分页 Request VO") 9 @Schema(description = "管理后台 - 积分商城活动分页 Request VO")
15 @Data 10 @Data
@@ -17,20 +12,7 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_ @@ -17,20 +12,7 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
17 @ToString(callSuper = true) 12 @ToString(callSuper = true)
18 public class PointActivityPageReqVO extends PageParam { 13 public class PointActivityPageReqVO extends PageParam {
19 14
20 - @Schema(description = "积分商城活动商品", example = "19509")  
21 - private Long spuId;  
22 -  
23 @Schema(description = "活动状态", example = "2") 15 @Schema(description = "活动状态", example = "2")
24 private Integer status; 16 private Integer status;
25 17
26 - @Schema(description = "备注", example = "你说的对")  
27 - private String remark;  
28 -  
29 - @Schema(description = "排序")  
30 - private Integer sort;  
31 -  
32 - @Schema(description = "创建时间")  
33 - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)  
34 - private LocalDateTime[] createTime;  
35 -  
36 } 18 }
37 \ No newline at end of file 19 \ No newline at end of file
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/kefu/AppKeFuMessageController.java
@@ -2,13 +2,14 @@ package cn.iocoder.yudao.module.promotion.controller.app.kefu; @@ -2,13 +2,14 @@ package cn.iocoder.yudao.module.promotion.controller.app.kefu;
2 2
3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; 3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageRespVO; 6 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageRespVO;
8 import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessagePageReqVO; 7 import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessagePageReqVO;
9 import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessageSendReqVO; 8 import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessageSendReqVO;
10 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO; 9 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;
11 import cn.iocoder.yudao.module.promotion.service.kefu.KeFuMessageService; 10 import cn.iocoder.yudao.module.promotion.service.kefu.KeFuMessageService;
  11 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  12 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
12 import io.swagger.v3.oas.annotations.Operation; 13 import io.swagger.v3.oas.annotations.Operation;
13 import io.swagger.v3.oas.annotations.Parameter; 14 import io.swagger.v3.oas.annotations.Parameter;
14 import io.swagger.v3.oas.annotations.tags.Tag; 15 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -17,7 +18,13 @@ import jakarta.validation.Valid; @@ -17,7 +18,13 @@ import jakarta.validation.Valid;
17 import org.springframework.validation.annotation.Validated; 18 import org.springframework.validation.annotation.Validated;
18 import org.springframework.web.bind.annotation.*; 19 import org.springframework.web.bind.annotation.*;
19 20
  21 +import java.util.List;
  22 +import java.util.Map;
  23 +
20 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 24 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  25 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  26 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
  27 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
21 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 28 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
22 29
23 @Tag(name = "用户 APP - 客服消息") 30 @Tag(name = "用户 APP - 客服消息")
@@ -29,6 +36,9 @@ public class AppKeFuMessageController { @@ -29,6 +36,9 @@ public class AppKeFuMessageController {
29 @Resource 36 @Resource
30 private KeFuMessageService kefuMessageService; 37 private KeFuMessageService kefuMessageService;
31 38
  39 + @Resource
  40 + private AdminUserApi adminUserApi;
  41 +
32 @PostMapping("/send") 42 @PostMapping("/send")
33 @Operation(summary = "发送客服消息") 43 @Operation(summary = "发送客服消息")
34 public CommonResult<Long> sendKefuMessage(@Valid @RequestBody AppKeFuMessageSendReqVO sendReqVO) { 44 public CommonResult<Long> sendKefuMessage(@Valid @RequestBody AppKeFuMessageSendReqVO sendReqVO) {
@@ -44,11 +54,17 @@ public class AppKeFuMessageController { @@ -44,11 +54,17 @@ public class AppKeFuMessageController {
44 return success(true); 54 return success(true);
45 } 55 }
46 56
47 - @GetMapping("/page")  
48 - @Operation(summary = "获得客服消息分页")  
49 - public CommonResult<PageResult<KeFuMessageRespVO>> getKefuMessagePage(@Valid AppKeFuMessagePageReqVO pageReqVO) {  
50 - PageResult<KeFuMessageDO> pageResult = kefuMessageService.getKeFuMessagePage(pageReqVO, getLoginUserId());  
51 - return success(BeanUtils.toBean(pageResult, KeFuMessageRespVO.class)); 57 + @GetMapping("/list")
  58 + @Operation(summary = "获得客服消息列表")
  59 + public CommonResult<List<KeFuMessageRespVO>> getKefuMessageList(@Valid AppKeFuMessagePageReqVO pageReqVO) {
  60 + List<KeFuMessageDO> list = kefuMessageService.getKeFuMessageList(pageReqVO, getLoginUserId());
  61 +
  62 + // 拼接数据
  63 + List<KeFuMessageRespVO> result = BeanUtils.toBean(list, KeFuMessageRespVO.class);
  64 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(filterList(result,
  65 + item -> UserTypeEnum.ADMIN.getValue().equals(item.getSenderType())), KeFuMessageRespVO::getSenderId));
  66 + result.forEach(item -> findAndThen(userMap, item.getSenderId(), user -> item.setSenderAvatar(user.getAvatar())));
  67 + return success(result);
52 } 68 }
53 69
54 } 70 }
55 \ No newline at end of file 71 \ No newline at end of file
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/kefu/vo/message/AppKeFuMessagePageReqVO.java
1 package cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message; 1 package cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message;
2 2
3 -import cn.iocoder.yudao.framework.common.pojo.PageParam;  
4 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.Max;
  5 +import jakarta.validation.constraints.Min;
  6 +import jakarta.validation.constraints.NotNull;
5 import lombok.Data; 7 import lombok.Data;
6 -import lombok.EqualsAndHashCode;  
7 -import lombok.ToString; 8 +import org.springframework.format.annotation.DateTimeFormat;
8 9
9 -@Schema(description = "用户 App - 客服消息分页 Request VO") 10 +import java.time.LocalDateTime;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  13 +
  14 +@Schema(description = "用户 App - 客服消息 Request VO")
10 @Data 15 @Data
11 -@ToString(callSuper = true)  
12 -public class AppKeFuMessagePageReqVO extends PageParam { 16 +public class AppKeFuMessagePageReqVO {
  17 +
  18 + private static final Integer LIMIT = 10;
13 19
14 @Schema(description = "会话编号", example = "12580") 20 @Schema(description = "会话编号", example = "12580")
15 private Long conversationId; 21 private Long conversationId;
16 22
  23 + @Schema(description = "发送时间", example = "2024-03-27 12:00:00")
  24 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  25 + private LocalDateTime createTime;
  26 +
  27 + @Schema(description = "每次查询条数,最大值为 100", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  28 + @NotNull(message = "每次查询条数不能为空")
  29 + @Min(value = 1, message = "每次查询条数最小值为 1")
  30 + @Max(value = 100, message = "每次查询最大值为 100")
  31 + private Integer limit = LIMIT;
  32 +
17 } 33 }
18 \ No newline at end of file 34 \ No newline at end of file
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/vo/AppPointActivityRespVO.java
@@ -4,8 +4,6 @@ import com.alibaba.excel.annotation.ExcelProperty; @@ -4,8 +4,6 @@ import com.alibaba.excel.annotation.ExcelProperty;
4 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
5 import lombok.Data; 5 import lombok.Data;
6 6
7 -import java.time.LocalDateTime;  
8 -  
9 @Schema(description = "用户 App - 积分商城活动 Response VO") 7 @Schema(description = "用户 App - 积分商城活动 Response VO")
10 @Data 8 @Data
11 public class AppPointActivityRespVO { 9 public class AppPointActivityRespVO {
@@ -30,20 +28,6 @@ public class AppPointActivityRespVO { @@ -30,20 +28,6 @@ public class AppPointActivityRespVO {
30 @ExcelProperty("积分商城活动总库存") 28 @ExcelProperty("积分商城活动总库存")
31 private Integer totalStock; 29 private Integer totalStock;
32 30
33 - // TODO @puhui999:只返回必要的字段,例如说 remark、sort、createTime 应该是不需要的呢。也可以看看别的也不需要哈。  
34 -  
35 - @Schema(description = "备注", example = "你说的对")  
36 - @ExcelProperty("备注")  
37 - private String remark;  
38 -  
39 - @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED)  
40 - @ExcelProperty("排序")  
41 - private Integer sort;  
42 -  
43 - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)  
44 - @ExcelProperty("创建时间")  
45 - private LocalDateTime createTime;  
46 -  
47 // ========== 商品字段 ========== 31 // ========== 商品字段 ==========
48 32
49 @Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 name 读取 33 @Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 name 读取
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/combination/CombinationActivityConvert.java
@@ -217,8 +217,7 @@ public interface CombinationActivityConvert { @@ -217,8 +217,7 @@ public interface CombinationActivityConvert {
217 List<CombinationRecordDO> createRecords = new ArrayList<>(count); 217 List<CombinationRecordDO> createRecords = new ArrayList<>(count);
218 for (int i = 0; i < count; i++) { 218 for (int i = 0; i < count; i++) {
219 // 基础信息和团长保持一致 219 // 基础信息和团长保持一致
220 - CombinationRecordDO newRecord = BeanUtils.toBean(headRecord, CombinationRecordDO.class)  
221 - .setId(null).setHeadId(headRecord.getHeadId()); 220 + CombinationRecordDO newRecord = convert5(headRecord).setHeadId(headRecord.getId());
222 // 虚拟信息 221 // 虚拟信息
223 newRecord.setCount(0) // 会单独更新下,在后续的 Service 逻辑里 222 newRecord.setCount(0) // 会单独更新下,在后续的 Service 逻辑里
224 .setUserId(0L).setNickname("").setAvatar("").setOrderId(0L); 223 .setUserId(0L).setNickname("").setAvatar("").setOrderId(0L);
@@ -226,5 +225,7 @@ public interface CombinationActivityConvert { @@ -226,5 +225,7 @@ public interface CombinationActivityConvert {
226 } 225 }
227 return createRecords; 226 return createRecords;
228 } 227 }
  228 + @Mapping(target = "id", ignore = true)
  229 + CombinationRecordDO convert5(CombinationRecordDO headRecord);
229 230
230 } 231 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/kefu/KeFuMessageMapper.java
1 package cn.iocoder.yudao.module.promotion.dal.mysql.kefu; 1 package cn.iocoder.yudao.module.promotion.dal.mysql.kefu;
2 2
3 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;  
6 -import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessagePageReqVO;  
7 -import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessagePageReqVO; 4 +import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
  5 +import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageListReqVO;
8 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO; 6 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;
9 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 7 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
10 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 8 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@@ -21,10 +19,20 @@ import java.util.List; @@ -21,10 +19,20 @@ import java.util.List;
21 @Mapper 19 @Mapper
22 public interface KeFuMessageMapper extends BaseMapperX<KeFuMessageDO> { 20 public interface KeFuMessageMapper extends BaseMapperX<KeFuMessageDO> {
23 21
24 - default PageResult<KeFuMessageDO> selectPage(KeFuMessagePageReqVO reqVO) {  
25 - return selectPage(reqVO, new LambdaQueryWrapperX<KeFuMessageDO>()  
26 - .eqIfPresent(KeFuMessageDO::getConversationId, reqVO.getConversationId())  
27 - .orderByDesc(KeFuMessageDO::getCreateTime)); 22 + /**
  23 + * 获得消息列表
  24 + * 1. 第一次查询时,不带时间,默认查询最新的十条消息
  25 + * 2. 第二次查询时,带时间,查询历史消息
  26 + *
  27 + * @param reqVO 列表请求
  28 + * @return 消息列表
  29 + */
  30 + default List<KeFuMessageDO> selectList(KeFuMessageListReqVO reqVO) {
  31 + return selectList(new QueryWrapperX<KeFuMessageDO>()
  32 + .eqIfPresent("conversation_id", reqVO.getConversationId())
  33 + .ltIfPresent("create_time", reqVO.getCreateTime())
  34 + .orderByDesc("create_time")
  35 + .limitN(reqVO.getLimit()));
28 } 36 }
29 37
30 default List<KeFuMessageDO> selectListByConversationIdAndUserTypeAndReadStatus(Long conversationId, Integer userType, 38 default List<KeFuMessageDO> selectListByConversationIdAndUserTypeAndReadStatus(Long conversationId, Integer userType,
@@ -40,10 +48,4 @@ public interface KeFuMessageMapper extends BaseMapperX&lt;KeFuMessageDO&gt; { @@ -40,10 +48,4 @@ public interface KeFuMessageMapper extends BaseMapperX&lt;KeFuMessageDO&gt; {
40 .in(KeFuMessageDO::getId, ids)); 48 .in(KeFuMessageDO::getId, ids));
41 } 49 }
42 50
43 - default PageResult<KeFuMessageDO> selectPage(AppKeFuMessagePageReqVO pageReqVO) {  
44 - return selectPage(pageReqVO, new LambdaQueryWrapperX<KeFuMessageDO>()  
45 - .eqIfPresent(KeFuMessageDO::getConversationId, pageReqVO.getConversationId())  
46 - .orderByDesc(KeFuMessageDO::getCreateTime));  
47 - }  
48 -  
49 } 51 }
50 \ No newline at end of file 52 \ No newline at end of file
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/point/PointActivityMapper.java
@@ -19,12 +19,8 @@ public interface PointActivityMapper extends BaseMapperX&lt;PointActivityDO&gt; { @@ -19,12 +19,8 @@ public interface PointActivityMapper extends BaseMapperX&lt;PointActivityDO&gt; {
19 19
20 default PageResult<PointActivityDO> selectPage(PointActivityPageReqVO reqVO) { 20 default PageResult<PointActivityDO> selectPage(PointActivityPageReqVO reqVO) {
21 return selectPage(reqVO, new LambdaQueryWrapperX<PointActivityDO>() 21 return selectPage(reqVO, new LambdaQueryWrapperX<PointActivityDO>()
22 - .eqIfPresent(PointActivityDO::getSpuId, reqVO.getSpuId())  
23 .eqIfPresent(PointActivityDO::getStatus, reqVO.getStatus()) 22 .eqIfPresent(PointActivityDO::getStatus, reqVO.getStatus())
24 - .eqIfPresent(PointActivityDO::getRemark, reqVO.getRemark())  
25 - .eqIfPresent(PointActivityDO::getSort, reqVO.getSort())  
26 - .betweenIfPresent(PointActivityDO::getCreateTime, reqVO.getCreateTime())  
27 - .orderByDesc(PointActivityDO::getId)); 23 + .orderByDesc(PointActivityDO::getSort));
28 } 24 }
29 25
30 /** 26 /**
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java
@@ -375,7 +375,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { @@ -375,7 +375,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
375 CombinationRecordDO updateRecord = new CombinationRecordDO().setId(item.getId()) 375 CombinationRecordDO updateRecord = new CombinationRecordDO().setId(item.getId())
376 .setStatus(status.getStatus()).setEndTime(now); 376 .setStatus(status.getStatus()).setEndTime(now);
377 if (CombinationRecordStatusEnum.isSuccess(status.getStatus())) { // 虚拟成团完事更改状态成功后还需要把参与人数修改为成团需要人数 377 if (CombinationRecordStatusEnum.isSuccess(status.getStatus())) { // 虚拟成团完事更改状态成功后还需要把参与人数修改为成团需要人数
378 - updateRecord.setUserCount(updateRecord.getUserSize()); 378 + updateRecord.setUserCount(records.size());
379 } 379 }
380 updateRecords.add(updateRecord); 380 updateRecords.add(updateRecord);
381 }); 381 });
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuConversationService.java
@@ -14,6 +14,14 @@ import java.util.List; @@ -14,6 +14,14 @@ import java.util.List;
14 public interface KeFuConversationService { 14 public interface KeFuConversationService {
15 15
16 /** 16 /**
  17 + * 获得客服会话
  18 + *
  19 + * @param id 编号
  20 + * @return 客服会话
  21 + */
  22 + KeFuConversationDO getConversation(Long id);
  23 +
  24 + /**
17 * 【管理员】删除客服会话 25 * 【管理员】删除客服会话
18 * 26 *
19 * @param id 编号 27 * @param id 编号
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuConversationServiceImpl.java
@@ -31,6 +31,11 @@ public class KeFuConversationServiceImpl implements KeFuConversationService { @@ -31,6 +31,11 @@ public class KeFuConversationServiceImpl implements KeFuConversationService {
31 private KeFuConversationMapper conversationMapper; 31 private KeFuConversationMapper conversationMapper;
32 32
33 @Override 33 @Override
  34 + public KeFuConversationDO getConversation(Long id) {
  35 + return conversationMapper.selectById(id);
  36 + }
  37 +
  38 + @Override
34 public void deleteKefuConversation(Long id) { 39 public void deleteKefuConversation(Long id) {
35 // 校验存在 40 // 校验存在
36 validateKefuConversationExists(id); 41 validateKefuConversationExists(id);
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuMessageService.java
1 package cn.iocoder.yudao.module.promotion.service.kefu; 1 package cn.iocoder.yudao.module.promotion.service.kefu;
2 2
3 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
4 -import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessagePageReqVO; 3 +import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageListReqVO;
5 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageSendReqVO; 4 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageSendReqVO;
6 import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessagePageReqVO; 5 import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessagePageReqVO;
7 import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessageSendReqVO; 6 import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessageSendReqVO;
8 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO; 7 import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuMessageDO;
9 import jakarta.validation.Valid; 8 import jakarta.validation.Valid;
10 9
  10 +import java.util.List;
  11 +
11 /** 12 /**
12 * 客服消息 Service 接口 13 * 客服消息 Service 接口
13 * 14 *
@@ -46,7 +47,7 @@ public interface KeFuMessageService { @@ -46,7 +47,7 @@ public interface KeFuMessageService {
46 * @param pageReqVO 分页查询 47 * @param pageReqVO 分页查询
47 * @return 客服消息分页 48 * @return 客服消息分页
48 */ 49 */
49 - PageResult<KeFuMessageDO> getKeFuMessagePage(KeFuMessagePageReqVO pageReqVO); 50 + List<KeFuMessageDO> getKeFuMessageList(KeFuMessageListReqVO pageReqVO);
50 51
51 /** 52 /**
52 * 【会员】获得客服消息分页 53 * 【会员】获得客服消息分页
@@ -55,6 +56,6 @@ public interface KeFuMessageService { @@ -55,6 +56,6 @@ public interface KeFuMessageService {
55 * @param userId 用户编号 56 * @param userId 用户编号
56 * @return 客服消息分页 57 * @return 客服消息分页
57 */ 58 */
58 - PageResult<KeFuMessageDO> getKeFuMessagePage(AppKeFuMessagePageReqVO pageReqVO, Long userId); 59 + List<KeFuMessageDO> getKeFuMessageList(AppKeFuMessagePageReqVO pageReqVO, Long userId);
59 60
60 } 61 }
61 \ No newline at end of file 62 \ No newline at end of file
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuMessageServiceImpl.java
@@ -2,14 +2,12 @@ package cn.iocoder.yudao.module.promotion.service.kefu; @@ -2,14 +2,12 @@ package cn.iocoder.yudao.module.promotion.service.kefu;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.ObjUtil; 4 import cn.hutool.core.util.ObjUtil;
5 -import cn.hutool.core.util.StrUtil;  
6 import cn.hutool.extra.spring.SpringUtil; 5 import cn.hutool.extra.spring.SpringUtil;
7 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; 6 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
8 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
9 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
10 import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi; 8 import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi;
11 import cn.iocoder.yudao.module.member.api.user.MemberUserApi; 9 import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
12 -import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessagePageReqVO; 10 +import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageListReqVO;
13 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageSendReqVO; 11 import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.message.KeFuMessageSendReqVO;
14 import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessagePageReqVO; 12 import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessagePageReqVO;
15 import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessageSendReqVO; 13 import cn.iocoder.yudao.module.promotion.controller.app.kefu.vo.message.AppKeFuMessageSendReqVO;
@@ -23,6 +21,7 @@ import org.springframework.stereotype.Service; @@ -23,6 +21,7 @@ import org.springframework.stereotype.Service;
23 import org.springframework.transaction.annotation.Transactional; 21 import org.springframework.transaction.annotation.Transactional;
24 import org.springframework.validation.annotation.Validated; 22 import org.springframework.validation.annotation.Validated;
25 23
  24 +import java.util.Collections;
26 import java.util.List; 25 import java.util.List;
27 26
28 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 27 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -113,9 +112,9 @@ public class KeFuMessageServiceImpl implements KeFuMessageService { @@ -113,9 +112,9 @@ public class KeFuMessageServiceImpl implements KeFuMessageService {
113 // 2.3 发送消息通知会员,管理员已读 -> 会员更新发送的消息状态 112 // 2.3 发送消息通知会员,管理员已读 -> 会员更新发送的消息状态
114 KeFuMessageDO keFuMessage = getFirst(filterList(messageList, message -> UserTypeEnum.MEMBER.getValue().equals(message.getSenderType()))); 113 KeFuMessageDO keFuMessage = getFirst(filterList(messageList, message -> UserTypeEnum.MEMBER.getValue().equals(message.getSenderType())));
115 assert keFuMessage != null; // 断言避免警告 114 assert keFuMessage != null; // 断言避免警告
116 - getSelf().sendAsyncMessageToMember(keFuMessage.getSenderId(), KEFU_MESSAGE_ADMIN_READ, StrUtil.EMPTY); 115 + getSelf().sendAsyncMessageToMember(keFuMessage.getSenderId(), KEFU_MESSAGE_ADMIN_READ, conversation.getId());
117 // 2.4 通知所有管理员消息已读 116 // 2.4 通知所有管理员消息已读
118 - getSelf().sendAsyncMessageToAdmin(KEFU_MESSAGE_ADMIN_READ, StrUtil.EMPTY); 117 + getSelf().sendAsyncMessageToAdmin(KEFU_MESSAGE_ADMIN_READ, conversation.getId());
119 } 118 }
120 119
121 private void validateReceiverExist(Long receiverId, Integer receiverType) { 120 private void validateReceiverExist(Long receiverId, Integer receiverType) {
@@ -138,20 +137,20 @@ public class KeFuMessageServiceImpl implements KeFuMessageService { @@ -138,20 +137,20 @@ public class KeFuMessageServiceImpl implements KeFuMessageService {
138 } 137 }
139 138
140 @Override 139 @Override
141 - public PageResult<KeFuMessageDO> getKeFuMessagePage(KeFuMessagePageReqVO pageReqVO) {  
142 - return keFuMessageMapper.selectPage(pageReqVO); 140 + public List<KeFuMessageDO> getKeFuMessageList(KeFuMessageListReqVO pageReqVO) {
  141 + return keFuMessageMapper.selectList(pageReqVO);
143 } 142 }
144 143
145 @Override 144 @Override
146 - public PageResult<KeFuMessageDO> getKeFuMessagePage(AppKeFuMessagePageReqVO pageReqVO, Long userId) { 145 + public List<KeFuMessageDO> getKeFuMessageList(AppKeFuMessagePageReqVO pageReqVO, Long userId) {
147 // 1. 获得客服会话 146 // 1. 获得客服会话
148 KeFuConversationDO conversation = conversationService.getConversationByUserId(userId); 147 KeFuConversationDO conversation = conversationService.getConversationByUserId(userId);
149 if (conversation == null) { 148 if (conversation == null) {
150 - return PageResult.empty(); 149 + return Collections.emptyList();
151 } 150 }
152 // 2. 设置会话编号 151 // 2. 设置会话编号
153 pageReqVO.setConversationId(conversation.getId()); 152 pageReqVO.setConversationId(conversation.getId());
154 - return keFuMessageMapper.selectPage(pageReqVO); 153 + return keFuMessageMapper.selectList(BeanUtils.toBean(pageReqVO, KeFuMessageListReqVO.class));
155 } 154 }
156 155
157 private KeFuMessageServiceImpl getSelf() { 156 private KeFuMessageServiceImpl getSelf() {
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
@@ -36,6 +36,7 @@ public interface ErrorCodeConstants { @@ -36,6 +36,7 @@ public interface ErrorCodeConstants {
36 ErrorCode ORDER_UPDATE_ADDRESS_FAIL_STATUS_NOT_DELIVERED = new ErrorCode(1_011_000_031, "交易订单修改收货地址失败,原因:订单不是【待发货】状态"); 36 ErrorCode ORDER_UPDATE_ADDRESS_FAIL_STATUS_NOT_DELIVERED = new ErrorCode(1_011_000_031, "交易订单修改收货地址失败,原因:订单不是【待发货】状态");
37 ErrorCode ORDER_CREATE_FAIL_EXIST_UNPAID = new ErrorCode(1_011_000_032, "交易订单创建失败,原因:存在未付款订单"); 37 ErrorCode ORDER_CREATE_FAIL_EXIST_UNPAID = new ErrorCode(1_011_000_032, "交易订单创建失败,原因:存在未付款订单");
38 ErrorCode ORDER_CANCEL_PAID_FAIL = new ErrorCode(1_011_000_033, "交易订单取消支付失败,原因:订单不是【{}】状态"); 38 ErrorCode ORDER_CANCEL_PAID_FAIL = new ErrorCode(1_011_000_033, "交易订单取消支付失败,原因:订单不是【{}】状态");
  39 + ErrorCode ORDER_PICK_UP_FAIL_NOT_VERIFY_USER = new ErrorCode(1_011_000_034, "交易订单自提失败,原因:你没有核销该门店订单的权限");
39 40
40 // ========== After Sale 模块 1-011-000-100 ========== 41 // ========== After Sale 模块 1-011-000-100 ==========
41 ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1_011_000_100, "售后单不存在"); 42 ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1_011_000_100, "售后单不存在");
@@ -80,6 +81,7 @@ public interface ErrorCodeConstants { @@ -80,6 +81,7 @@ public interface ErrorCodeConstants {
80 81
81 // ========== 物流 PICK_UP 模块 1-011-006-000 ========== 82 // ========== 物流 PICK_UP 模块 1-011-006-000 ==========
82 ErrorCode PICK_UP_STORE_NOT_EXISTS = new ErrorCode(1_011_006_000, "自提门店不存在"); 83 ErrorCode PICK_UP_STORE_NOT_EXISTS = new ErrorCode(1_011_006_000, "自提门店不存在");
  84 + ErrorCode PICK_UP_STORE_STAFF_NOT_EXISTS = new ErrorCode(1_011_006_000, "自提门店店员不存在");
83 85
84 // ========== 分销用户 模块 1-011-007-000 ========== 86 // ========== 分销用户 模块 1-011-007-000 ==========
85 ErrorCode BROKERAGE_USER_NOT_EXISTS = new ErrorCode(1_011_007_000, "分销用户不存在"); 87 ErrorCode BROKERAGE_USER_NOT_EXISTS = new ErrorCode(1_011_007_000, "分销用户不存在");
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/MessageTemplateConstants.java
@@ -11,9 +11,6 @@ public interface MessageTemplateConstants { @@ -11,9 +11,6 @@ public interface MessageTemplateConstants {
11 11
12 String SMS_ORDER_DELIVERY = "order_delivery"; // 短信模版编号 12 String SMS_ORDER_DELIVERY = "order_delivery"; // 短信模版编号
13 13
14 - String SMS_BROKERAGE_WITHDRAW_AUDIT_APPROVE = "brokerage_withdraw_audit_approve"; // 佣金提现(审核通过)  
15 - String SMS_BROKERAGE_WITHDRAW_AUDIT_REJECT = "brokerage_withdraw_audit_reject"; // 佣金提现(审核不通过)  
16 -  
17 // ======================= 小程序订阅消息模版 ======================= 14 // ======================= 小程序订阅消息模版 =======================
18 15
19 String WXA_ORDER_DELIVERY = "订单发货通知"; 16 String WXA_ORDER_DELIVERY = "订单发货通知";
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/brokerage/BrokerageWithdrawTypeEnum.java
@@ -17,8 +17,9 @@ public enum BrokerageWithdrawTypeEnum implements IntArrayValuable { @@ -17,8 +17,9 @@ public enum BrokerageWithdrawTypeEnum implements IntArrayValuable {
17 17
18 WALLET(1, "钱包"), 18 WALLET(1, "钱包"),
19 BANK(2, "银行卡"), 19 BANK(2, "银行卡"),
20 - WECHAT(3, "微信"), 20 + WECHAT(3, "微信"), // 手动打款
21 ALIPAY(4, "支付宝"), 21 ALIPAY(4, "支付宝"),
  22 + WECHAT_API(5, "微信零钱"), // 自动打款,通过微信转账 API
22 ; 23 ;
23 24
24 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BrokerageWithdrawTypeEnum::getType).toArray(); 25 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BrokerageWithdrawTypeEnum::getType).toArray();
@@ -37,4 +38,14 @@ public enum BrokerageWithdrawTypeEnum implements IntArrayValuable { @@ -37,4 +38,14 @@ public enum BrokerageWithdrawTypeEnum implements IntArrayValuable {
37 return ARRAYS; 38 return ARRAYS;
38 } 39 }
39 40
  41 + /**
  42 + * 是否通过支付平台的 API 打款
  43 + *
  44 + * @param type 类型
  45 + * @return 是否
  46 + */
  47 + public static boolean isApi(Integer type) {
  48 + return WECHAT_API.getType().equals(type);
  49 + }
  50 +
40 } 51 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/base/system/package-info.java 0 → 100644
  1 +/**
  2 + * 占位符,可忽略
  3 + */
  4 +package cn.iocoder.yudao.module.trade.controller.admin.base.system;
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/base/system/user/UserSimpleBaseVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.controller.admin.base.system.user;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "用户精简信息 VO")
  7 +@Data
  8 +public class UserSimpleBaseVO {
  9 +
  10 + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  11 + private Long id;
  12 +
  13 + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
  14 + private String nickname;
  15 +
  16 + @Schema(description = "用户头像", example = "https://www.iocoder.cn/1.png")
  17 + private String avatar;
  18 +
  19 +}
0 \ No newline at end of file 20 \ No newline at end of file
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/BrokerageRecordController.java
@@ -44,7 +44,7 @@ public class BrokerageRecordController { @@ -44,7 +44,7 @@ public class BrokerageRecordController {
44 @Operation(summary = "获得佣金记录") 44 @Operation(summary = "获得佣金记录")
45 @Parameter(name = "id", description = "编号", required = true, example = "1024") 45 @Parameter(name = "id", description = "编号", required = true, example = "1024")
46 @PreAuthorize("@ss.hasPermission('trade:brokerage-record:query')") 46 @PreAuthorize("@ss.hasPermission('trade:brokerage-record:query')")
47 - public CommonResult<BrokerageRecordRespVO> getBrokerageRecord(@RequestParam("id") Integer id) { 47 + public CommonResult<BrokerageRecordRespVO> getBrokerageRecord(@RequestParam("id") Long id) {
48 BrokerageRecordDO brokerageRecord = brokerageRecordService.getBrokerageRecord(id); 48 BrokerageRecordDO brokerageRecord = brokerageRecordService.getBrokerageRecord(id);
49 return success(BrokerageRecordConvert.INSTANCE.convert(brokerageRecord)); 49 return success(BrokerageRecordConvert.INSTANCE.convert(brokerageRecord));
50 } 50 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/BrokerageWithdrawController.java
@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.module.member.api.user.MemberUserApi; 5 import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
6 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; 6 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
  7 +import cn.iocoder.yudao.module.pay.api.notify.dto.PayTransferNotifyReqDTO;
7 import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawRejectReqVO; 8 import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawRejectReqVO;
8 import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawPageReqVO; 9 import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawPageReqVO;
9 import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawRespVO; 10 import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawRespVO;
@@ -14,6 +15,8 @@ import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageWithdrawService; @@ -14,6 +15,8 @@ import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageWithdrawService;
14 import io.swagger.v3.oas.annotations.Operation; 15 import io.swagger.v3.oas.annotations.Operation;
15 import io.swagger.v3.oas.annotations.Parameter; 16 import io.swagger.v3.oas.annotations.Parameter;
16 import io.swagger.v3.oas.annotations.tags.Tag; 17 import io.swagger.v3.oas.annotations.tags.Tag;
  18 +import jakarta.annotation.security.PermitAll;
  19 +import lombok.extern.slf4j.Slf4j;
17 import org.springframework.security.access.prepost.PreAuthorize; 20 import org.springframework.security.access.prepost.PreAuthorize;
18 import org.springframework.validation.annotation.Validated; 21 import org.springframework.validation.annotation.Validated;
19 import org.springframework.web.bind.annotation.*; 22 import org.springframework.web.bind.annotation.*;
@@ -24,11 +27,13 @@ import java.util.Map; @@ -24,11 +27,13 @@ import java.util.Map;
24 27
25 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 28 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
26 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 29 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  30 +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
27 31
28 @Tag(name = "管理后台 - 佣金提现") 32 @Tag(name = "管理后台 - 佣金提现")
29 @RestController 33 @RestController
30 @RequestMapping("/trade/brokerage-withdraw") 34 @RequestMapping("/trade/brokerage-withdraw")
31 @Validated 35 @Validated
  36 +@Slf4j
32 public class BrokerageWithdrawController { 37 public class BrokerageWithdrawController {
33 38
34 @Resource 39 @Resource
@@ -40,8 +45,9 @@ public class BrokerageWithdrawController { @@ -40,8 +45,9 @@ public class BrokerageWithdrawController {
40 @PutMapping("/approve") 45 @PutMapping("/approve")
41 @Operation(summary = "通过申请") 46 @Operation(summary = "通过申请")
42 @PreAuthorize("@ss.hasPermission('trade:brokerage-withdraw:audit')") 47 @PreAuthorize("@ss.hasPermission('trade:brokerage-withdraw:audit')")
43 - public CommonResult<Boolean> approveBrokerageWithdraw(@RequestParam("id") Integer id) {  
44 - brokerageWithdrawService.auditBrokerageWithdraw(id, BrokerageWithdrawStatusEnum.AUDIT_SUCCESS, ""); 48 + public CommonResult<Boolean> approveBrokerageWithdraw(@RequestParam("id") Long id) {
  49 + brokerageWithdrawService.auditBrokerageWithdraw(id,
  50 + BrokerageWithdrawStatusEnum.AUDIT_SUCCESS, "", getClientIP());
45 return success(true); 51 return success(true);
46 } 52 }
47 53
@@ -49,7 +55,8 @@ public class BrokerageWithdrawController { @@ -49,7 +55,8 @@ public class BrokerageWithdrawController {
49 @Operation(summary = "驳回申请") 55 @Operation(summary = "驳回申请")
50 @PreAuthorize("@ss.hasPermission('trade:brokerage-withdraw:audit')") 56 @PreAuthorize("@ss.hasPermission('trade:brokerage-withdraw:audit')")
51 public CommonResult<Boolean> rejectBrokerageWithdraw(@Valid @RequestBody BrokerageWithdrawRejectReqVO reqVO) { 57 public CommonResult<Boolean> rejectBrokerageWithdraw(@Valid @RequestBody BrokerageWithdrawRejectReqVO reqVO) {
52 - brokerageWithdrawService.auditBrokerageWithdraw(reqVO.getId(), BrokerageWithdrawStatusEnum.AUDIT_FAIL, reqVO.getAuditReason()); 58 + brokerageWithdrawService.auditBrokerageWithdraw(reqVO.getId(),
  59 + BrokerageWithdrawStatusEnum.AUDIT_FAIL, reqVO.getAuditReason(), getClientIP());
53 return success(true); 60 return success(true);
54 } 61 }
55 62
@@ -57,7 +64,7 @@ public class BrokerageWithdrawController { @@ -57,7 +64,7 @@ public class BrokerageWithdrawController {
57 @Operation(summary = "获得佣金提现") 64 @Operation(summary = "获得佣金提现")
58 @Parameter(name = "id", description = "编号", required = true, example = "1024") 65 @Parameter(name = "id", description = "编号", required = true, example = "1024")
59 @PreAuthorize("@ss.hasPermission('trade:brokerage-withdraw:query')") 66 @PreAuthorize("@ss.hasPermission('trade:brokerage-withdraw:query')")
60 - public CommonResult<BrokerageWithdrawRespVO> getBrokerageWithdraw(@RequestParam("id") Integer id) { 67 + public CommonResult<BrokerageWithdrawRespVO> getBrokerageWithdraw(@RequestParam("id") Long id) {
61 BrokerageWithdrawDO brokerageWithdraw = brokerageWithdrawService.getBrokerageWithdraw(id); 68 BrokerageWithdrawDO brokerageWithdraw = brokerageWithdrawService.getBrokerageWithdraw(id);
62 return success(BrokerageWithdrawConvert.INSTANCE.convert(brokerageWithdraw)); 69 return success(BrokerageWithdrawConvert.INSTANCE.convert(brokerageWithdraw));
63 } 70 }
@@ -75,4 +82,15 @@ public class BrokerageWithdrawController { @@ -75,4 +82,15 @@ public class BrokerageWithdrawController {
75 return success(BrokerageWithdrawConvert.INSTANCE.convertPage(pageResult, userMap)); 82 return success(BrokerageWithdrawConvert.INSTANCE.convertPage(pageResult, userMap));
76 } 83 }
77 84
  85 + // TODO @luchi:update-transferred,url 改成这个。和 update-paid 、update-refunded 保持一致
  86 + @PostMapping("/update-transfer")
  87 + @Operation(summary = "更新转账订单为转账成功") // 由 pay-module 支付服务,进行回调,可见 PayNotifyJob
  88 + @PermitAll // 无需登录,安全由 PayDemoOrderService 内部校验实现
  89 + public CommonResult<Boolean> updateBrokerageWithdrawTransferred(@RequestBody PayTransferNotifyReqDTO notifyReqDTO) {
  90 + log.info("[updateAfterRefund][notifyReqDTO({})]", notifyReqDTO);
  91 + brokerageWithdrawService.updateBrokerageWithdrawTransferred(
  92 + Long.parseLong(notifyReqDTO.getMerchantTransferId()), notifyReqDTO.getPayTransferId());
  93 + return success(true);
  94 + }
  95 +
78 } 96 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/vo/record/BrokerageRecordRespVO.java
@@ -14,7 +14,7 @@ import java.time.LocalDateTime; @@ -14,7 +14,7 @@ import java.time.LocalDateTime;
14 public class BrokerageRecordRespVO extends BrokerageRecordBaseVO { 14 public class BrokerageRecordRespVO extends BrokerageRecordBaseVO {
15 15
16 @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28896") 16 @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "28896")
17 - private Integer id; 17 + private Long id;
18 18
19 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) 19 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
20 private LocalDateTime createTime; 20 private LocalDateTime createTime;
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawRejectReqVO.java
@@ -14,7 +14,7 @@ public class BrokerageWithdrawRejectReqVO { @@ -14,7 +14,7 @@ public class BrokerageWithdrawRejectReqVO {
14 14
15 @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "7161") 15 @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "7161")
16 @NotNull(message = "编号不能为空") 16 @NotNull(message = "编号不能为空")
17 - private Integer id; 17 + private Long id;
18 18
19 @Schema(description = "审核驳回原因", example = "不对") 19 @Schema(description = "审核驳回原因", example = "不对")
20 @NotEmpty(message = "审核驳回原因不能为空") 20 @NotEmpty(message = "审核驳回原因不能为空")
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/vo/withdraw/BrokerageWithdrawRespVO.java
@@ -14,7 +14,7 @@ import java.time.LocalDateTime; @@ -14,7 +14,7 @@ import java.time.LocalDateTime;
14 public class BrokerageWithdrawRespVO extends BrokerageWithdrawBaseVO { 14 public class BrokerageWithdrawRespVO extends BrokerageWithdrawBaseVO {
15 15
16 @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "7161") 16 @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "7161")
17 - private Integer id; 17 + private Long id;
18 18
19 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) 19 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
20 private LocalDateTime createTime; 20 private LocalDateTime createTime;
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryPickUpStoreController.java
1 package cn.iocoder.yudao.module.trade.controller.admin.delivery; 1 package cn.iocoder.yudao.module.trade.controller.admin.delivery;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  7 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  8 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  9 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  10 +import cn.iocoder.yudao.module.trade.controller.admin.base.system.user.UserSimpleBaseVO;
6 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup.*; 11 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup.*;
7 import cn.iocoder.yudao.module.trade.convert.delivery.DeliveryPickUpStoreConvert; 12 import cn.iocoder.yudao.module.trade.convert.delivery.DeliveryPickUpStoreConvert;
8 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO; 13 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO;
@@ -10,12 +15,12 @@ import cn.iocoder.yudao.module.trade.service.delivery.DeliveryPickUpStoreService @@ -10,12 +15,12 @@ import cn.iocoder.yudao.module.trade.service.delivery.DeliveryPickUpStoreService
10 import io.swagger.v3.oas.annotations.Operation; 15 import io.swagger.v3.oas.annotations.Operation;
11 import io.swagger.v3.oas.annotations.Parameter; 16 import io.swagger.v3.oas.annotations.Parameter;
12 import io.swagger.v3.oas.annotations.tags.Tag; 17 import io.swagger.v3.oas.annotations.tags.Tag;
  18 +import jakarta.annotation.Resource;
  19 +import jakarta.validation.Valid;
13 import org.springframework.security.access.prepost.PreAuthorize; 20 import org.springframework.security.access.prepost.PreAuthorize;
14 import org.springframework.validation.annotation.Validated; 21 import org.springframework.validation.annotation.Validated;
15 import org.springframework.web.bind.annotation.*; 22 import org.springframework.web.bind.annotation.*;
16 23
17 -import jakarta.annotation.Resource;  
18 -import jakarta.validation.Valid;  
19 import java.util.Collection; 24 import java.util.Collection;
20 import java.util.List; 25 import java.util.List;
21 26
@@ -30,6 +35,9 @@ public class DeliveryPickUpStoreController { @@ -30,6 +35,9 @@ public class DeliveryPickUpStoreController {
30 @Resource 35 @Resource
31 private DeliveryPickUpStoreService deliveryPickUpStoreService; 36 private DeliveryPickUpStoreService deliveryPickUpStoreService;
32 37
  38 + @Resource
  39 + private AdminUserApi adminUserApi;
  40 +
33 @PostMapping("/create") 41 @PostMapping("/create")
34 @Operation(summary = "创建自提门店") 42 @Operation(summary = "创建自提门店")
35 @PreAuthorize("@ss.hasPermission('trade:delivery:pick-up-store:create')") 43 @PreAuthorize("@ss.hasPermission('trade:delivery:pick-up-store:create')")
@@ -60,10 +68,16 @@ public class DeliveryPickUpStoreController { @@ -60,10 +68,16 @@ public class DeliveryPickUpStoreController {
60 @PreAuthorize("@ss.hasPermission('trade:delivery:pick-up-store:query')") 68 @PreAuthorize("@ss.hasPermission('trade:delivery:pick-up-store:query')")
61 public CommonResult<DeliveryPickUpStoreRespVO> getDeliveryPickUpStore(@RequestParam("id") Long id) { 69 public CommonResult<DeliveryPickUpStoreRespVO> getDeliveryPickUpStore(@RequestParam("id") Long id) {
62 DeliveryPickUpStoreDO deliveryPickUpStore = deliveryPickUpStoreService.getDeliveryPickUpStore(id); 70 DeliveryPickUpStoreDO deliveryPickUpStore = deliveryPickUpStoreService.getDeliveryPickUpStore(id);
63 - return success(DeliveryPickUpStoreConvert.INSTANCE.convert(deliveryPickUpStore)); 71 + if (deliveryPickUpStore == null) {
  72 + return success(null);
  73 + }
  74 + List<AdminUserRespDTO> verifyUsers = CollUtil.isNotEmpty(deliveryPickUpStore.getVerifyUserIds()) ?
  75 + adminUserApi.getUserList(deliveryPickUpStore.getVerifyUserIds()) : null;
  76 + return success(BeanUtils.toBean(deliveryPickUpStore, DeliveryPickUpStoreRespVO.class)
  77 + .setVerifyUsers(BeanUtils.toBean(verifyUsers, UserSimpleBaseVO.class)));
64 } 78 }
65 79
66 - @GetMapping("/list-all-simple") 80 + @GetMapping("/simple-list")
67 @Operation(summary = "获得自提门店精简信息列表") 81 @Operation(summary = "获得自提门店精简信息列表")
68 public CommonResult<List<DeliveryPickUpStoreSimpleRespVO>> getSimpleDeliveryPickUpStoreList() { 82 public CommonResult<List<DeliveryPickUpStoreSimpleRespVO>> getSimpleDeliveryPickUpStoreList() {
69 List<DeliveryPickUpStoreDO> list = deliveryPickUpStoreService.getDeliveryPickUpStoreListByStatus( 83 List<DeliveryPickUpStoreDO> list = deliveryPickUpStoreService.getDeliveryPickUpStoreListByStatus(
@@ -88,4 +102,12 @@ public class DeliveryPickUpStoreController { @@ -88,4 +102,12 @@ public class DeliveryPickUpStoreController {
88 return success(DeliveryPickUpStoreConvert.INSTANCE.convertPage(pageResult)); 102 return success(DeliveryPickUpStoreConvert.INSTANCE.convertPage(pageResult));
89 } 103 }
90 104
  105 + @PostMapping("/bind")
  106 + @Operation(summary = "绑定自提店员")
  107 + @PreAuthorize("@ss.hasPermission('trade:delivery:pick-up-store:create')")
  108 + public CommonResult<Boolean> bindDeliveryPickUpStore(@Valid @RequestBody DeliveryPickUpBindReqVO bindReqVO) {
  109 + deliveryPickUpStoreService.bindDeliveryPickUpStore(bindReqVO);
  110 + return success(true);
  111 + }
  112 +
91 } 113 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpBindReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import lombok.Data;
  7 +import lombok.ToString;
  8 +
  9 +import java.util.List;
  10 +
  11 +@Schema(description = "管理后台 - 自提门店绑定核销人 Request VO")
  12 +@Data
  13 +@ToString(callSuper = true)
  14 +public class DeliveryPickUpBindReqVO {
  15 +
  16 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23128")
  17 + @NotNull(message = "编号不能为空")
  18 + private Long id;
  19 +
  20 + @Schema(description = "绑定用户编号组数", requiredMode = Schema.RequiredMode.REQUIRED, example = "23128")
  21 + @NotEmpty(message = "绑定用户编号组数不能未空")
  22 + private List<Long> verifyUserIds;
  23 +
  24 +}
0 \ No newline at end of file 25 \ No newline at end of file
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreRespVO.java
1 package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup; 1 package cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup;
2 2
  3 +import cn.iocoder.yudao.module.trade.controller.admin.base.system.user.UserSimpleBaseVO;
3 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.Data; 5 import lombok.Data;
5 import lombok.EqualsAndHashCode; 6 import lombok.EqualsAndHashCode;
6 import lombok.ToString; 7 import lombok.ToString;
7 8
8 import java.time.LocalDateTime; 9 import java.time.LocalDateTime;
  10 +import java.util.List;
9 11
10 @Schema(description = "管理后台 - 自提门店 Response VO") 12 @Schema(description = "管理后台 - 自提门店 Response VO")
11 @Data 13 @Data
@@ -19,4 +21,7 @@ public class DeliveryPickUpStoreRespVO extends DeliveryPickUpStoreBaseVO { @@ -19,4 +21,7 @@ public class DeliveryPickUpStoreRespVO extends DeliveryPickUpStoreBaseVO {
19 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) 21 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
20 private LocalDateTime createTime; 22 private LocalDateTime createTime;
21 23
  24 + @Schema(description = "核销用户数组", requiredMode = Schema.RequiredMode.REQUIRED)
  25 + private List<UserSimpleBaseVO> verifyUsers;
  26 +
22 } 27 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreSimpleRespVO.java
@@ -5,6 +5,8 @@ import lombok.AllArgsConstructor; @@ -5,6 +5,8 @@ import lombok.AllArgsConstructor;
5 import lombok.Data; 5 import lombok.Data;
6 import lombok.NoArgsConstructor; 6 import lombok.NoArgsConstructor;
7 7
  8 +import java.util.List;
  9 +
8 @Schema(description = "管理后台 - 自提门店精简信息 Response VO") 10 @Schema(description = "管理后台 - 自提门店精简信息 Response VO")
9 @Data 11 @Data
10 @NoArgsConstructor 12 @NoArgsConstructor
@@ -29,4 +31,7 @@ public class DeliveryPickUpStoreSimpleRespVO { @@ -29,4 +31,7 @@ public class DeliveryPickUpStoreSimpleRespVO {
29 @Schema(description = "门店详细地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "复旦大学路 188 号") 31 @Schema(description = "门店详细地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "复旦大学路 188 号")
30 private String detailAddress; 32 private String detailAddress;
31 33
  34 + @Schema(description = "绑定用户编号组数", requiredMode = Schema.RequiredMode.REQUIRED, example = "23128")
  35 + private List<Long> verifyUserIds;
  36 +
32 } 37 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/TradeOrderController.java
@@ -10,19 +10,18 @@ import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert; @@ -10,19 +10,18 @@ import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
10 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 10 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
11 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; 11 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
12 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderLogDO; 12 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderLogDO;
13 -import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleService;  
14 import cn.iocoder.yudao.module.trade.service.order.TradeOrderLogService; 13 import cn.iocoder.yudao.module.trade.service.order.TradeOrderLogService;
15 import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService; 14 import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
16 import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService; 15 import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
17 import io.swagger.v3.oas.annotations.Operation; 16 import io.swagger.v3.oas.annotations.Operation;
18 import io.swagger.v3.oas.annotations.Parameter; 17 import io.swagger.v3.oas.annotations.Parameter;
19 import io.swagger.v3.oas.annotations.tags.Tag; 18 import io.swagger.v3.oas.annotations.tags.Tag;
  19 +import jakarta.annotation.Resource;
20 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
21 import org.springframework.security.access.prepost.PreAuthorize; 21 import org.springframework.security.access.prepost.PreAuthorize;
22 import org.springframework.validation.annotation.Validated; 22 import org.springframework.validation.annotation.Validated;
23 import org.springframework.web.bind.annotation.*; 23 import org.springframework.web.bind.annotation.*;
24 24
25 -import jakarta.annotation.Resource;  
26 import java.util.List; 25 import java.util.List;
27 import java.util.Map; 26 import java.util.Map;
28 import java.util.Objects; 27 import java.util.Objects;
@@ -31,6 +30,7 @@ import java.util.Set; @@ -31,6 +30,7 @@ import java.util.Set;
31 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 30 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
32 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 31 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
33 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 32 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  33 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
34 34
35 @Tag(name = "管理后台 - 交易订单") 35 @Tag(name = "管理后台 - 交易订单")
36 @RestController 36 @RestController
@@ -144,7 +144,7 @@ public class TradeOrderController { @@ -144,7 +144,7 @@ public class TradeOrderController {
144 @Parameter(name = "id", description = "交易订单编号") 144 @Parameter(name = "id", description = "交易订单编号")
145 @PreAuthorize("@ss.hasPermission('trade:order:pick-up')") 145 @PreAuthorize("@ss.hasPermission('trade:order:pick-up')")
146 public CommonResult<Boolean> pickUpOrderById(@RequestParam("id") Long id) { 146 public CommonResult<Boolean> pickUpOrderById(@RequestParam("id") Long id) {
147 - tradeOrderUpdateService.pickUpOrderByAdmin(id); 147 + tradeOrderUpdateService.pickUpOrderByAdmin(getLoginUserId(), id);
148 return success(true); 148 return success(true);
149 } 149 }
150 150
@@ -153,7 +153,7 @@ public class TradeOrderController { @@ -153,7 +153,7 @@ public class TradeOrderController {
153 @Parameter(name = "pickUpVerifyCode", description = "自提核销码") 153 @Parameter(name = "pickUpVerifyCode", description = "自提核销码")
154 @PreAuthorize("@ss.hasPermission('trade:order:pick-up')") 154 @PreAuthorize("@ss.hasPermission('trade:order:pick-up')")
155 public CommonResult<Boolean> pickUpOrderByVerifyCode(@RequestParam("pickUpVerifyCode") String pickUpVerifyCode) { 155 public CommonResult<Boolean> pickUpOrderByVerifyCode(@RequestParam("pickUpVerifyCode") String pickUpVerifyCode) {
156 - tradeOrderUpdateService.pickUpOrderByAdmin(pickUpVerifyCode); 156 + tradeOrderUpdateService.pickUpOrderByAdmin(getLoginUserId(), pickUpVerifyCode);
157 return success(true); 157 return success(true);
158 } 158 }
159 159
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/delivery/DeliveryPickUpStoreConvert.java
@@ -26,8 +26,6 @@ public interface DeliveryPickUpStoreConvert { @@ -26,8 +26,6 @@ public interface DeliveryPickUpStoreConvert {
26 26
27 DeliveryPickUpStoreDO convert(DeliveryPickUpStoreUpdateReqVO bean); 27 DeliveryPickUpStoreDO convert(DeliveryPickUpStoreUpdateReqVO bean);
28 28
29 - DeliveryPickUpStoreRespVO convert(DeliveryPickUpStoreDO bean);  
30 -  
31 List<DeliveryPickUpStoreRespVO> convertList(List<DeliveryPickUpStoreDO> list); 29 List<DeliveryPickUpStoreRespVO> convertList(List<DeliveryPickUpStoreDO> list);
32 30
33 PageResult<DeliveryPickUpStoreRespVO> convertPage(PageResult<DeliveryPickUpStoreDO> page); 31 PageResult<DeliveryPickUpStoreRespVO> convertPage(PageResult<DeliveryPickUpStoreDO> page);
@@ -43,14 +41,13 @@ public interface DeliveryPickUpStoreConvert { @@ -43,14 +41,13 @@ public interface DeliveryPickUpStoreConvert {
43 41
44 default List<AppDeliveryPickUpStoreRespVO> convertList(List<DeliveryPickUpStoreDO> list, 42 default List<AppDeliveryPickUpStoreRespVO> convertList(List<DeliveryPickUpStoreDO> list,
45 Double latitude, Double longitude) { 43 Double latitude, Double longitude) {
46 - List<AppDeliveryPickUpStoreRespVO> voList = CollectionUtils.convertList(list, store -> { 44 + return CollectionUtils.convertList(list, store -> {
47 AppDeliveryPickUpStoreRespVO storeVO = convert03(store); 45 AppDeliveryPickUpStoreRespVO storeVO = convert03(store);
48 if (latitude != null && longitude != null) { 46 if (latitude != null && longitude != null) {
49 storeVO.setDistance(NumberUtils.getDistance(latitude, longitude, storeVO.getLatitude(), storeVO.getLongitude())); 47 storeVO.setDistance(NumberUtils.getDistance(latitude, longitude, storeVO.getLatitude(), storeVO.getLongitude()));
50 } 48 }
51 return storeVO; 49 return storeVO;
52 }); 50 });
53 - return voList;  
54 } 51 }
55 @Mapping(source = "areaId", target = "areaName", qualifiedByName = "convertAreaIdToAreaName") 52 @Mapping(source = "areaId", target = "areaName", qualifiedByName = "convertAreaIdToAreaName")
56 AppDeliveryPickUpStoreRespVO convert03(DeliveryPickUpStoreDO bean); 53 AppDeliveryPickUpStoreRespVO convert03(DeliveryPickUpStoreDO bean);
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/delivery/DeliveryPickUpStoreDO.java
@@ -2,19 +2,23 @@ package cn.iocoder.yudao.module.trade.dal.dataobject.delivery; @@ -2,19 +2,23 @@ package cn.iocoder.yudao.module.trade.dal.dataobject.delivery;
2 2
3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
  6 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
5 import com.baomidou.mybatisplus.annotation.KeySequence; 7 import com.baomidou.mybatisplus.annotation.KeySequence;
  8 +import com.baomidou.mybatisplus.annotation.TableField;
6 import com.baomidou.mybatisplus.annotation.TableId; 9 import com.baomidou.mybatisplus.annotation.TableId;
7 import com.baomidou.mybatisplus.annotation.TableName; 10 import com.baomidou.mybatisplus.annotation.TableName;
8 import lombok.Data; 11 import lombok.Data;
9 12
10 import java.time.LocalTime; 13 import java.time.LocalTime;
  14 +import java.util.List;
11 15
12 /** 16 /**
13 * 自提门店 DO 17 * 自提门店 DO
14 * 18 *
15 * @author jason 19 * @author jason
16 */ 20 */
17 -@TableName(value ="trade_delivery_pick_up_store") 21 +@TableName(value ="trade_delivery_pick_up_store", autoResultMap = true)
18 @KeySequence("trade_delivery_pick_up_store_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 22 @KeySequence("trade_delivery_pick_up_store_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
19 @Data 23 @Data
20 public class DeliveryPickUpStoreDO extends BaseDO { 24 public class DeliveryPickUpStoreDO extends BaseDO {
@@ -75,6 +79,16 @@ public class DeliveryPickUpStoreDO extends BaseDO { @@ -75,6 +79,16 @@ public class DeliveryPickUpStoreDO extends BaseDO {
75 private Double longitude; 79 private Double longitude;
76 80
77 /** 81 /**
  82 + * 核销员工用户编号数组
  83 + *
  84 + * 订单自提核销时,只有对应门店的店员才能核销
  85 + *
  86 + * 关联 {@link AdminUserRespDTO#getId()} 管理员编号
  87 + */
  88 + @TableField(typeHandler = LongListTypeHandler.class)
  89 + private List<Long> verifyUserIds;
  90 +
  91 + /**
78 * 门店状态 92 * 门店状态
79 * 93 *
80 * 枚举 {@link CommonStatusEnum} 94 * 枚举 {@link CommonStatusEnum}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/delivery/DeliveryPickUpStoreStaffDO.java deleted
1 -package cn.iocoder.yudao.module.trade.dal.dataobject.delivery;  
2 -  
3 -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;  
4 -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;  
5 -import com.baomidou.mybatisplus.annotation.KeySequence;  
6 -import com.baomidou.mybatisplus.annotation.TableId;  
7 -import com.baomidou.mybatisplus.annotation.TableName;  
8 -import lombok.Data;  
9 -  
10 -// TODO @芋艿:后续再详细 review 一轮  
11 -// TODO @芋艿:可能改成 DeliveryPickUpStoreUserDO  
12 -/**  
13 - * 自提门店店员 DO  
14 - *  
15 - * @author jason  
16 - */  
17 -@TableName(value ="trade_delivery_pick_up_store_staff")  
18 -@KeySequence("trade_delivery_pick_up_store_staff_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。  
19 -@Data  
20 -public class DeliveryPickUpStoreStaffDO extends BaseDO {  
21 -  
22 - /**  
23 - * 编号,自增  
24 - */  
25 - @TableId  
26 - private Long id;  
27 -  
28 - /**  
29 - * 自提门店编号  
30 - *  
31 - * 关联 {@link DeliveryPickUpStoreDO#getId()}  
32 - */  
33 - private Long storeId;  
34 -  
35 - /**  
36 - * 管理员用户id  
37 - *  
38 - * 关联 {AdminUserDO#getId()}  
39 - */  
40 - private Long adminUserId;  
41 -  
42 - /**  
43 - * 状态  
44 - *  
45 - * 枚举 {@link CommonStatusEnum}  
46 - */  
47 - private Integer status;  
48 -  
49 -}  
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/brokerage/BrokerageWithdrawMapper.java
@@ -35,7 +35,7 @@ public interface BrokerageWithdrawMapper extends BaseMapperX&lt;BrokerageWithdrawDO @@ -35,7 +35,7 @@ public interface BrokerageWithdrawMapper extends BaseMapperX&lt;BrokerageWithdrawDO
35 .orderByAsc(BrokerageWithdrawDO::getStatus).orderByDesc(BrokerageWithdrawDO::getId)); 35 .orderByAsc(BrokerageWithdrawDO::getStatus).orderByDesc(BrokerageWithdrawDO::getId));
36 } 36 }
37 37
38 - default int updateByIdAndStatus(Integer id, Integer status, BrokerageWithdrawDO updateObj) { 38 + default int updateByIdAndStatus(Long id, Integer status, BrokerageWithdrawDO updateObj) {
39 return update(updateObj, new LambdaUpdateWrapper<BrokerageWithdrawDO>() 39 return update(updateObj, new LambdaUpdateWrapper<BrokerageWithdrawDO>()
40 .eq(BrokerageWithdrawDO::getId, id) 40 .eq(BrokerageWithdrawDO::getId, id)
41 .eq(BrokerageWithdrawDO::getStatus, status)); 41 .eq(BrokerageWithdrawDO::getStatus, status));
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/delivery/DeliveryPickUpStoreStaffMapper.java deleted
1 -package cn.iocoder.yudao.module.trade.dal.mysql.delivery;  
2 -  
3 -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;  
4 -import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryPickUpStoreStaffDO;  
5 -import org.apache.ibatis.annotations.Mapper;  
6 -  
7 -@Mapper  
8 -public interface DeliveryPickUpStoreStaffMapper extends BaseMapperX<DeliveryPickUpStoreStaffDO> {  
9 -  
10 -}  
11 -  
12 -  
13 -  
14 -  
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageRecordService.java
@@ -32,7 +32,7 @@ public interface BrokerageRecordService { @@ -32,7 +32,7 @@ public interface BrokerageRecordService {
32 * @param id 编号 32 * @param id 编号
33 * @return 佣金记录 33 * @return 佣金记录
34 */ 34 */
35 - BrokerageRecordDO getBrokerageRecord(Integer id); 35 + BrokerageRecordDO getBrokerageRecord(Long id);
36 36
37 /** 37 /**
38 * 获得佣金记录分页 38 * 获得佣金记录分页
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageRecordServiceImpl.java
@@ -64,7 +64,7 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService { @@ -64,7 +64,7 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService {
64 private ProductSkuApi productSkuApi; 64 private ProductSkuApi productSkuApi;
65 65
66 @Override 66 @Override
67 - public BrokerageRecordDO getBrokerageRecord(Integer id) { 67 + public BrokerageRecordDO getBrokerageRecord(Long id) {
68 return brokerageRecordMapper.selectById(id); 68 return brokerageRecordMapper.selectById(id);
69 } 69 }
70 70
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawService.java
@@ -27,8 +27,9 @@ public interface BrokerageWithdrawService { @@ -27,8 +27,9 @@ public interface BrokerageWithdrawService {
27 * @param id 佣金编号 27 * @param id 佣金编号
28 * @param status 审核状态 28 * @param status 审核状态
29 * @param auditReason 驳回原因 29 * @param auditReason 驳回原因
  30 + * @param userIp 操作 IP
30 */ 31 */
31 - void auditBrokerageWithdraw(Integer id, BrokerageWithdrawStatusEnum status, String auditReason); 32 + void auditBrokerageWithdraw(Long id, BrokerageWithdrawStatusEnum status, String auditReason, String userIp);
32 33
33 /** 34 /**
34 * 获得佣金提现 35 * 获得佣金提现
@@ -36,7 +37,7 @@ public interface BrokerageWithdrawService { @@ -36,7 +37,7 @@ public interface BrokerageWithdrawService {
36 * @param id 编号 37 * @param id 编号
37 * @return 佣金提现 38 * @return 佣金提现
38 */ 39 */
39 - BrokerageWithdrawDO getBrokerageWithdraw(Integer id); 40 + BrokerageWithdrawDO getBrokerageWithdraw(Long id);
40 41
41 /** 42 /**
42 * 获得佣金提现分页 43 * 获得佣金提现分页
@@ -56,6 +57,16 @@ public interface BrokerageWithdrawService { @@ -56,6 +57,16 @@ public interface BrokerageWithdrawService {
56 Long createBrokerageWithdraw(Long userId, AppBrokerageWithdrawCreateReqVO createReqVO); 57 Long createBrokerageWithdraw(Long userId, AppBrokerageWithdrawCreateReqVO createReqVO);
57 58
58 /** 59 /**
  60 + * 【API】更新佣金提现的转账结果
  61 + *
  62 + * 目前用于支付回调,标记提现转账结果
  63 + *
  64 + * @param id 提现编号
  65 + * @param payTransferId 转账订单编号
  66 + */
  67 + void updateBrokerageWithdrawTransferred(Long id, Long payTransferId);
  68 +
  69 + /**
59 * 按照 userId,汇总每个用户的提现 70 * 按照 userId,汇总每个用户的提现
60 * 71 *
61 * @param userIds 用户编号 72 * @param userIds 用户编号
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawServiceImpl.java
1 package cn.iocoder.yudao.module.trade.service.brokerage; 1 package cn.iocoder.yudao.module.trade.service.brokerage;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 -import cn.hutool.core.date.LocalDateTimeUtil;  
5 -import cn.hutool.core.map.MapUtil;  
6 import cn.hutool.core.util.ObjectUtil; 4 import cn.hutool.core.util.ObjectUtil;
  5 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; 7 import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
  8 +import cn.iocoder.yudao.module.pay.api.transfer.PayTransferApi;
  9 +import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO;
  10 +import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferRespDTO;
  11 +import cn.iocoder.yudao.module.pay.api.wallet.PayWalletApi;
  12 +import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletAddBalanceReqDTO;
  13 +import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum;
  14 +import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferTypeEnum;
  15 +import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum;
9 import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi; 16 import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
10 -import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO; 17 +import cn.iocoder.yudao.module.system.api.social.SocialUserApi;
  18 +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
  19 +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
11 import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawPageReqVO; 20 import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawPageReqVO;
12 import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawCreateReqVO; 21 import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawCreateReqVO;
13 import cn.iocoder.yudao.module.trade.convert.brokerage.BrokerageWithdrawConvert; 22 import cn.iocoder.yudao.module.trade.convert.brokerage.BrokerageWithdrawConvert;
14 import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawDO; 23 import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawDO;
15 import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO; 24 import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO;
16 import cn.iocoder.yudao.module.trade.dal.mysql.brokerage.BrokerageWithdrawMapper; 25 import cn.iocoder.yudao.module.trade.dal.mysql.brokerage.BrokerageWithdrawMapper;
17 -import cn.iocoder.yudao.module.trade.enums.MessageTemplateConstants;  
18 import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; 26 import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
19 import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum; 27 import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawStatusEnum;
20 import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum; 28 import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageWithdrawTypeEnum;
  29 +import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
21 import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO; 30 import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageWithdrawSummaryRespBO;
22 import cn.iocoder.yudao.module.trade.service.config.TradeConfigService; 31 import cn.iocoder.yudao.module.trade.service.config.TradeConfigService;
  32 +import jakarta.annotation.Resource;
  33 +import jakarta.validation.Validator;
  34 +import lombok.extern.slf4j.Slf4j;
23 import org.springframework.stereotype.Service; 35 import org.springframework.stereotype.Service;
24 import org.springframework.transaction.annotation.Transactional; 36 import org.springframework.transaction.annotation.Transactional;
25 import org.springframework.validation.annotation.Validated; 37 import org.springframework.validation.annotation.Validated;
26 38
27 -import jakarta.annotation.Resource;  
28 -import jakarta.validation.Validator;  
29 import java.time.LocalDateTime; 39 import java.time.LocalDateTime;
30 import java.util.Collection; 40 import java.util.Collection;
31 import java.util.Collections; 41 import java.util.Collections;
32 import java.util.List; 42 import java.util.List;
33 -import java.util.Map;  
34 43
35 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 44 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  45 +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
36 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*; 46 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
37 47
38 /** 48 /**
@@ -42,6 +52,7 @@ import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*; @@ -42,6 +52,7 @@ import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
42 */ 52 */
43 @Service 53 @Service
44 @Validated 54 @Validated
  55 +@Slf4j
45 public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { 56 public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
46 57
47 @Resource 58 @Resource
@@ -54,13 +65,22 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { @@ -54,13 +65,22 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
54 65
55 @Resource 66 @Resource
56 private NotifyMessageSendApi notifyMessageSendApi; 67 private NotifyMessageSendApi notifyMessageSendApi;
  68 + @Resource
  69 + private PayTransferApi payTransferApi;
  70 + @Resource
  71 + private SocialUserApi socialUserApi;
  72 + @Resource
  73 + private PayWalletApi payWalletApi;
57 74
58 @Resource 75 @Resource
59 private Validator validator; 76 private Validator validator;
60 77
  78 + @Resource
  79 + private TradeOrderProperties tradeOrderProperties;
  80 +
61 @Override 81 @Override
62 @Transactional(rollbackFor = Exception.class) 82 @Transactional(rollbackFor = Exception.class)
63 - public void auditBrokerageWithdraw(Integer id, BrokerageWithdrawStatusEnum status, String auditReason) { 83 + public void auditBrokerageWithdraw(Long id, BrokerageWithdrawStatusEnum status, String auditReason, String userIp) {
64 // 1.1 校验存在 84 // 1.1 校验存在
65 BrokerageWithdrawDO withdraw = validateBrokerageWithdrawExists(id); 85 BrokerageWithdrawDO withdraw = validateBrokerageWithdrawExists(id);
66 // 1.2 校验状态为审核中 86 // 1.2 校验状态为审核中
@@ -68,41 +88,67 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { @@ -68,41 +88,67 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
68 throw exception(BROKERAGE_WITHDRAW_STATUS_NOT_AUDITING); 88 throw exception(BROKERAGE_WITHDRAW_STATUS_NOT_AUDITING);
69 } 89 }
70 90
71 - // 2. 更新 91 + // 2. 更新状态
72 int rows = brokerageWithdrawMapper.updateByIdAndStatus(id, BrokerageWithdrawStatusEnum.AUDITING.getStatus(), 92 int rows = brokerageWithdrawMapper.updateByIdAndStatus(id, BrokerageWithdrawStatusEnum.AUDITING.getStatus(),
73 new BrokerageWithdrawDO().setStatus(status.getStatus()).setAuditReason(auditReason).setAuditTime(LocalDateTime.now())); 93 new BrokerageWithdrawDO().setStatus(status.getStatus()).setAuditReason(auditReason).setAuditTime(LocalDateTime.now()));
74 if (rows == 0) { 94 if (rows == 0) {
75 throw exception(BROKERAGE_WITHDRAW_STATUS_NOT_AUDITING); 95 throw exception(BROKERAGE_WITHDRAW_STATUS_NOT_AUDITING);
76 } 96 }
77 97
78 - String templateCode; 98 + // 3.1 审批通过的后续处理
79 if (BrokerageWithdrawStatusEnum.AUDIT_SUCCESS.equals(status)) { 99 if (BrokerageWithdrawStatusEnum.AUDIT_SUCCESS.equals(status)) {
80 - templateCode = MessageTemplateConstants.SMS_BROKERAGE_WITHDRAW_AUDIT_APPROVE;  
81 - // 3.1 通过时佣金转余额  
82 - if (BrokerageWithdrawTypeEnum.WALLET.getType().equals(withdraw.getType())) {  
83 - // todo 疯狂:  
84 - }  
85 - // TODO 疯狂:调用转账接口 100 + auditBrokerageWithdrawSuccess(withdraw);
  101 + // 3.2 审批不通过的后续处理
86 } else if (BrokerageWithdrawStatusEnum.AUDIT_FAIL.equals(status)) { 102 } else if (BrokerageWithdrawStatusEnum.AUDIT_FAIL.equals(status)) {
87 - templateCode = MessageTemplateConstants.SMS_BROKERAGE_WITHDRAW_AUDIT_REJECT;  
88 - // 3.2 驳回时需要退还用户佣金  
89 brokerageRecordService.addBrokerage(withdraw.getUserId(), BrokerageRecordBizTypeEnum.WITHDRAW_REJECT, 103 brokerageRecordService.addBrokerage(withdraw.getUserId(), BrokerageRecordBizTypeEnum.WITHDRAW_REJECT,
90 String.valueOf(withdraw.getId()), withdraw.getPrice(), BrokerageRecordBizTypeEnum.WITHDRAW_REJECT.getTitle()); 104 String.valueOf(withdraw.getId()), withdraw.getPrice(), BrokerageRecordBizTypeEnum.WITHDRAW_REJECT.getTitle());
91 } else { 105 } else {
92 throw new IllegalArgumentException("不支持的提现状态:" + status); 106 throw new IllegalArgumentException("不支持的提现状态:" + status);
93 } 107 }
  108 + }
94 109
95 - // 4. 通知用户  
96 - Map<String, Object> templateParams = MapUtil.<String, Object>builder()  
97 - .put("createTime", LocalDateTimeUtil.formatNormal(withdraw.getCreateTime()))  
98 - .put("price", MoneyUtils.fenToYuanStr(withdraw.getPrice()))  
99 - .put("reason", auditReason)  
100 - .build();  
101 - notifyMessageSendApi.sendSingleMessageToMember(new NotifySendSingleToUserReqDTO()  
102 - .setUserId(withdraw.getUserId()).setTemplateCode(templateCode).setTemplateParams(templateParams)); 110 + private void auditBrokerageWithdrawSuccess(BrokerageWithdrawDO withdraw) {
  111 + // 1.1 钱包
  112 + if (BrokerageWithdrawTypeEnum.WALLET.getType().equals(withdraw.getType())) {
  113 + payWalletApi.addWalletBalance(new PayWalletAddBalanceReqDTO()
  114 + .setUserId(withdraw.getUserId()).setUserType(UserTypeEnum.MEMBER.getValue())
  115 + .setBizType(PayWalletBizTypeEnum.BROKERAGE_WITHDRAW.getType()).setBizId(withdraw.getId().toString())
  116 + .setPrice(withdraw.getPrice()));
  117 + // 1.2 微信 API
  118 + } else if (BrokerageWithdrawTypeEnum.WECHAT_API.getType().equals(withdraw.getType())) {
  119 + // TODO @luchi:这里,要加个转账单号的记录;另外,调用 API 转账,是立马成功,还是有延迟的哈?
  120 + Long payTransferId = createPayTransfer(withdraw);
  121 + // 1.3 剩余类型,都是手动打款,所以不处理
  122 + } else {
  123 + // TODO 可优化:未来可以考虑,接入支付宝、银联等 API 转账,实现自动打款
  124 + log.info("[auditBrokerageWithdrawSuccess][withdraw({}) 类型({}) 手动打款,无需处理]", withdraw.getId(), withdraw.getType());
  125 + }
  126 +
  127 + // 2. 非支付 API,则直接体现成功
  128 + if (!BrokerageWithdrawTypeEnum.isApi(withdraw.getType())) {
  129 + brokerageWithdrawMapper.updateByIdAndStatus(withdraw.getId(), BrokerageWithdrawStatusEnum.AUDIT_SUCCESS.getStatus(),
  130 + new BrokerageWithdrawDO().setStatus(BrokerageWithdrawStatusEnum.WITHDRAW_SUCCESS.getStatus()));
  131 + }
103 } 132 }
104 133
105 - private BrokerageWithdrawDO validateBrokerageWithdrawExists(Integer id) { 134 + private Long createPayTransfer(BrokerageWithdrawDO withdraw) {
  135 + // 1.1 获取微信 openid
  136 + SocialUserRespDTO socialUser = socialUserApi.getSocialUserByUserId(
  137 + UserTypeEnum.MEMBER.getValue(), withdraw.getUserId(), SocialTypeEnum.WECHAT_MINI_APP.getType());
  138 + // TODO @luchi:这里,需要校验非空。如果空的话,要有业务异常哈;
  139 + // 1.2 构建请求
  140 + PayTransferCreateReqDTO payTransferCreateReqDTO = new PayTransferCreateReqDTO()
  141 + .setAppKey(tradeOrderProperties.getPayAppKey())
  142 + .setChannelCode("wx_lite").setType(PayTransferTypeEnum.WX_BALANCE.getType())
  143 + .setMerchantTransferId(withdraw.getId().toString())
  144 + .setPrice(withdraw.getPrice())
  145 + .setSubject("佣金提现")
  146 + .setOpenid(socialUser.getOpenid()).setUserIp(getClientIP());
  147 + // 2. 发起请求
  148 + return payTransferApi.createTransfer(payTransferCreateReqDTO);
  149 + }
  150 +
  151 + private BrokerageWithdrawDO validateBrokerageWithdrawExists(Long id) {
106 BrokerageWithdrawDO withdraw = brokerageWithdrawMapper.selectById(id); 152 BrokerageWithdrawDO withdraw = brokerageWithdrawMapper.selectById(id);
107 if (withdraw == null) { 153 if (withdraw == null) {
108 throw exception(BROKERAGE_WITHDRAW_NOT_EXISTS); 154 throw exception(BROKERAGE_WITHDRAW_NOT_EXISTS);
@@ -111,7 +157,7 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { @@ -111,7 +157,7 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
111 } 157 }
112 158
113 @Override 159 @Override
114 - public BrokerageWithdrawDO getBrokerageWithdraw(Integer id) { 160 + public BrokerageWithdrawDO getBrokerageWithdraw(Long id) {
115 return brokerageWithdrawMapper.selectById(id); 161 return brokerageWithdrawMapper.selectById(id);
116 } 162 }
117 163
@@ -141,15 +187,6 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { @@ -141,15 +187,6 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
141 return withdraw.getId(); 187 return withdraw.getId();
142 } 188 }
143 189
144 - @Override  
145 - public List<BrokerageWithdrawSummaryRespBO> getWithdrawSummaryListByUserId(Collection<Long> userIds,  
146 - BrokerageWithdrawStatusEnum status) {  
147 - if (CollUtil.isEmpty(userIds)) {  
148 - return Collections.emptyList();  
149 - }  
150 - return brokerageWithdrawMapper.selectCountAndSumPriceByUserIdAndStatus(userIds, status.getStatus());  
151 - }  
152 -  
153 /** 190 /**
154 * 计算提现手续费 191 * 计算提现手续费
155 * 192 *
@@ -157,7 +194,7 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { @@ -157,7 +194,7 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
157 * @param percent 手续费百分比 194 * @param percent 手续费百分比
158 * @return 提现手续费 195 * @return 提现手续费
159 */ 196 */
160 - Integer calculateFeePrice(Integer withdrawPrice, Integer percent) { 197 + private Integer calculateFeePrice(Integer withdrawPrice, Integer percent) {
161 Integer feePrice = 0; 198 Integer feePrice = 0;
162 if (percent != null && percent > 0) { 199 if (percent != null && percent > 0) {
163 feePrice = MoneyUtils.calculateRatePrice(withdrawPrice, Double.valueOf(percent)); 200 feePrice = MoneyUtils.calculateRatePrice(withdrawPrice, Double.valueOf(percent));
@@ -171,11 +208,42 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { @@ -171,11 +208,42 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
171 * @param withdrawPrice 提现金额 208 * @param withdrawPrice 提现金额
172 * @return 分销配置 209 * @return 分销配置
173 */ 210 */
174 - TradeConfigDO validateWithdrawPrice(Integer withdrawPrice) { 211 + private TradeConfigDO validateWithdrawPrice(Integer withdrawPrice) {
175 TradeConfigDO tradeConfig = tradeConfigService.getTradeConfig(); 212 TradeConfigDO tradeConfig = tradeConfigService.getTradeConfig();
176 if (tradeConfig.getBrokerageWithdrawMinPrice() != null && withdrawPrice < tradeConfig.getBrokerageWithdrawMinPrice()) { 213 if (tradeConfig.getBrokerageWithdrawMinPrice() != null && withdrawPrice < tradeConfig.getBrokerageWithdrawMinPrice()) {
177 throw exception(BROKERAGE_WITHDRAW_MIN_PRICE, MoneyUtils.fenToYuanStr(tradeConfig.getBrokerageWithdrawMinPrice())); 214 throw exception(BROKERAGE_WITHDRAW_MIN_PRICE, MoneyUtils.fenToYuanStr(tradeConfig.getBrokerageWithdrawMinPrice()));
178 } 215 }
179 return tradeConfig; 216 return tradeConfig;
180 } 217 }
  218 +
  219 + @Override
  220 + @Transactional(rollbackFor = Exception.class)
  221 + public void updateBrokerageWithdrawTransferred(Long id, Long payTransferId) {
  222 + BrokerageWithdrawDO withdraw = validateBrokerageWithdrawExists(id);
  223 + PayTransferRespDTO transfer = payTransferApi.getTransfer(payTransferId);
  224 + // TODO @luchi:建议参考支付那,即使成功的情况下,也要各种校验;金额是否匹配、转账单号是否匹配、是否重复调用;
  225 + if (PayTransferStatusEnum.isSuccess(transfer.getStatus())) {
  226 + withdraw.setStatus(BrokerageWithdrawStatusEnum.WITHDRAW_SUCCESS.getStatus());
  227 + // TODO @luchi:发送站内信
  228 + } else if (PayTransferStatusEnum.isPendingStatus(transfer.getStatus())) {
  229 + // TODO @luchi:这里,是不是不用更新哈?
  230 + withdraw.setStatus(BrokerageWithdrawStatusEnum.AUDIT_SUCCESS.getStatus());
  231 + } else {
  232 + withdraw.setStatus(BrokerageWithdrawStatusEnum.WITHDRAW_FAIL.getStatus());
  233 + // 3.2 驳回时需要退还用户佣金
  234 + brokerageRecordService.addBrokerage(withdraw.getUserId(), BrokerageRecordBizTypeEnum.WITHDRAW_REJECT,
  235 + String.valueOf(withdraw.getId()), withdraw.getPrice(), BrokerageRecordBizTypeEnum.WITHDRAW_REJECT.getTitle());
  236 + }
  237 + brokerageWithdrawMapper.updateById(withdraw);
  238 + }
  239 +
  240 + @Override
  241 + public List<BrokerageWithdrawSummaryRespBO> getWithdrawSummaryListByUserId(Collection<Long> userIds,
  242 + BrokerageWithdrawStatusEnum status) {
  243 + if (CollUtil.isEmpty(userIds)) {
  244 + return Collections.emptyList();
  245 + }
  246 + return brokerageWithdrawMapper.selectCountAndSumPriceByUserIdAndStatus(userIds, status.getStatus());
  247 + }
  248 +
181 } 249 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryPickUpStoreService.java
1 package cn.iocoder.yudao.module.trade.service.delivery; 1 package cn.iocoder.yudao.module.trade.service.delivery;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 -import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStoreCreateReqVO;  
5 -import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStorePageReqVO;  
6 -import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStoreUpdateReqVO; 4 +import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup.*;
7 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO; 5 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO;
8 6
9 import jakarta.validation.Valid; 7 import jakarta.validation.Valid;
@@ -56,6 +54,14 @@ public interface DeliveryPickUpStoreService { @@ -56,6 +54,14 @@ public interface DeliveryPickUpStoreService {
56 List<DeliveryPickUpStoreDO> getDeliveryPickUpStoreList(Collection<Long> ids); 54 List<DeliveryPickUpStoreDO> getDeliveryPickUpStoreList(Collection<Long> ids);
57 55
58 /** 56 /**
  57 + * 获得指定状态的自提门店列表
  58 + *
  59 + * @param status 状态
  60 + * @return 自提门店列表
  61 + */
  62 + List<DeliveryPickUpStoreDO> getDeliveryPickUpStoreListByStatus(Integer status);
  63 +
  64 + /**
59 * 获得自提门店分页 65 * 获得自提门店分页
60 * 66 *
61 * @param pageReqVO 分页查询 67 * @param pageReqVO 分页查询
@@ -64,10 +70,10 @@ public interface DeliveryPickUpStoreService { @@ -64,10 +70,10 @@ public interface DeliveryPickUpStoreService {
64 PageResult<DeliveryPickUpStoreDO> getDeliveryPickUpStorePage(DeliveryPickUpStorePageReqVO pageReqVO); 70 PageResult<DeliveryPickUpStoreDO> getDeliveryPickUpStorePage(DeliveryPickUpStorePageReqVO pageReqVO);
65 71
66 /** 72 /**
67 - * 获得指定状态的自提门店列表 73 + * 绑定自提店员
68 * 74 *
69 - * @param status 状态  
70 - * @return 自提门店列表 75 + * @param bindReqVO 绑定数据
71 */ 76 */
72 - List<DeliveryPickUpStoreDO> getDeliveryPickUpStoreListByStatus(Integer status); 77 + void bindDeliveryPickUpStore(DeliveryPickUpBindReqVO bindReqVO);
  78 +
73 } 79 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/delivery/DeliveryPickUpStoreServiceImpl.java
1 package cn.iocoder.yudao.module.trade.service.delivery; 1 package cn.iocoder.yudao.module.trade.service.delivery;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  6 +import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpBindReqVO;
4 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStoreCreateReqVO; 7 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStoreCreateReqVO;
5 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStorePageReqVO; 8 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStorePageReqVO;
6 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStoreUpdateReqVO; 9 import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.pickup.DeliveryPickUpStoreUpdateReqVO;
7 import cn.iocoder.yudao.module.trade.convert.delivery.DeliveryPickUpStoreConvert; 10 import cn.iocoder.yudao.module.trade.convert.delivery.DeliveryPickUpStoreConvert;
8 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO; 11 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO;
9 import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryPickUpStoreMapper; 12 import cn.iocoder.yudao.module.trade.dal.mysql.delivery.DeliveryPickUpStoreMapper;
  13 +import jakarta.annotation.Resource;
10 import org.springframework.stereotype.Service; 14 import org.springframework.stereotype.Service;
11 import org.springframework.validation.annotation.Validated; 15 import org.springframework.validation.annotation.Validated;
12 16
13 -import jakarta.annotation.Resource;  
14 import java.util.Collection; 17 import java.util.Collection;
15 import java.util.List; 18 import java.util.List;
16 19
@@ -29,6 +32,9 @@ public class DeliveryPickUpStoreServiceImpl implements DeliveryPickUpStoreServic @@ -29,6 +32,9 @@ public class DeliveryPickUpStoreServiceImpl implements DeliveryPickUpStoreServic
29 @Resource 32 @Resource
30 private DeliveryPickUpStoreMapper deliveryPickUpStoreMapper; 33 private DeliveryPickUpStoreMapper deliveryPickUpStoreMapper;
31 34
  35 + @Resource
  36 + private AdminUserApi adminUserApi;
  37 +
32 @Override 38 @Override
33 public Long createDeliveryPickUpStore(DeliveryPickUpStoreCreateReqVO createReqVO) { 39 public Long createDeliveryPickUpStore(DeliveryPickUpStoreCreateReqVO createReqVO) {
34 // 插入 40 // 插入
@@ -56,7 +62,8 @@ public class DeliveryPickUpStoreServiceImpl implements DeliveryPickUpStoreServic @@ -56,7 +62,8 @@ public class DeliveryPickUpStoreServiceImpl implements DeliveryPickUpStoreServic
56 } 62 }
57 63
58 private void validateDeliveryPickUpStoreExists(Long id) { 64 private void validateDeliveryPickUpStoreExists(Long id) {
59 - if (deliveryPickUpStoreMapper.selectById(id) == null) { 65 + DeliveryPickUpStoreDO deliveryPickUpStore = deliveryPickUpStoreMapper.selectById(id);
  66 + if (deliveryPickUpStore == null) {
60 throw exception(PICK_UP_STORE_NOT_EXISTS); 67 throw exception(PICK_UP_STORE_NOT_EXISTS);
61 } 68 }
62 } 69 }
@@ -72,13 +79,25 @@ public class DeliveryPickUpStoreServiceImpl implements DeliveryPickUpStoreServic @@ -72,13 +79,25 @@ public class DeliveryPickUpStoreServiceImpl implements DeliveryPickUpStoreServic
72 } 79 }
73 80
74 @Override 81 @Override
  82 + public List<DeliveryPickUpStoreDO> getDeliveryPickUpStoreListByStatus(Integer status) {
  83 + return deliveryPickUpStoreMapper.selectListByStatus(status);
  84 + }
  85 +
  86 + @Override
75 public PageResult<DeliveryPickUpStoreDO> getDeliveryPickUpStorePage(DeliveryPickUpStorePageReqVO pageReqVO) { 87 public PageResult<DeliveryPickUpStoreDO> getDeliveryPickUpStorePage(DeliveryPickUpStorePageReqVO pageReqVO) {
76 return deliveryPickUpStoreMapper.selectPage(pageReqVO); 88 return deliveryPickUpStoreMapper.selectPage(pageReqVO);
77 } 89 }
78 90
79 @Override 91 @Override
80 - public List<DeliveryPickUpStoreDO> getDeliveryPickUpStoreListByStatus(Integer status) {  
81 - return deliveryPickUpStoreMapper.selectListByStatus(status); 92 + public void bindDeliveryPickUpStore(DeliveryPickUpBindReqVO bindReqVO) {
  93 + // 1.1 校验门店存在
  94 + validateDeliveryPickUpStoreExists(bindReqVO.getId());
  95 + // 1.2 校验用户存在
  96 + adminUserApi.validateUserList(bindReqVO.getVerifyUserIds());
  97 +
  98 + // 2. 更新
  99 + DeliveryPickUpStoreDO updateObj = BeanUtils.toBean(bindReqVO, DeliveryPickUpStoreDO.class);
  100 + deliveryPickUpStoreMapper.updateById(updateObj);
82 } 101 }
83 102
84 } 103 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateService.java
@@ -129,16 +129,18 @@ public interface TradeOrderUpdateService { @@ -129,16 +129,18 @@ public interface TradeOrderUpdateService {
129 /** 129 /**
130 * 【管理员】核销订单 130 * 【管理员】核销订单
131 * 131 *
  132 + * @param userId 管理员编号
132 * @param id 订单编号 133 * @param id 订单编号
133 */ 134 */
134 - void pickUpOrderByAdmin(Long id); 135 + void pickUpOrderByAdmin(Long userId, Long id);
135 136
136 /** 137 /**
137 * 【管理员】核销订单 138 * 【管理员】核销订单
138 * 139 *
  140 + * @param userId 管理员编号
139 * @param pickUpVerifyCode 自提核销码 141 * @param pickUpVerifyCode 自提核销码
140 */ 142 */
141 - void pickUpOrderByAdmin(String pickUpVerifyCode); 143 + void pickUpOrderByAdmin(Long userId, String pickUpVerifyCode);
142 144
143 /** 145 /**
144 * 【管理员】根据自提核销码,查询订单 146 * 【管理员】根据自提核销码,查询订单
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
@@ -36,6 +36,7 @@ import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderI @@ -36,6 +36,7 @@ import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderI
36 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert; 36 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
37 import cn.iocoder.yudao.module.trade.dal.dataobject.cart.CartDO; 37 import cn.iocoder.yudao.module.trade.dal.dataobject.cart.CartDO;
38 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO; 38 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
  39 +import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO;
39 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 40 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
40 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; 41 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
41 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper; 42 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
@@ -48,6 +49,7 @@ import cn.iocoder.yudao.module.trade.framework.order.core.annotations.TradeOrder @@ -48,6 +49,7 @@ import cn.iocoder.yudao.module.trade.framework.order.core.annotations.TradeOrder
48 import cn.iocoder.yudao.module.trade.framework.order.core.utils.TradeOrderLogUtils; 49 import cn.iocoder.yudao.module.trade.framework.order.core.utils.TradeOrderLogUtils;
49 import cn.iocoder.yudao.module.trade.service.cart.CartService; 50 import cn.iocoder.yudao.module.trade.service.cart.CartService;
50 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; 51 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
  52 +import cn.iocoder.yudao.module.trade.service.delivery.DeliveryPickUpStoreService;
51 import cn.iocoder.yudao.module.trade.service.message.TradeMessageService; 53 import cn.iocoder.yudao.module.trade.service.message.TradeMessageService;
52 import cn.iocoder.yudao.module.trade.service.message.bo.TradeOrderMessageWhenDeliveryOrderReqBO; 54 import cn.iocoder.yudao.module.trade.service.message.bo.TradeOrderMessageWhenDeliveryOrderReqBO;
53 import cn.iocoder.yudao.module.trade.service.order.handler.TradeOrderHandler; 55 import cn.iocoder.yudao.module.trade.service.order.handler.TradeOrderHandler;
@@ -104,6 +106,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -104,6 +106,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
104 private DeliveryExpressService deliveryExpressService; 106 private DeliveryExpressService deliveryExpressService;
105 @Resource 107 @Resource
106 private TradeMessageService tradeMessageService; 108 private TradeMessageService tradeMessageService;
  109 + @Resource
  110 + private DeliveryPickUpStoreService pickUpStoreService;
107 111
108 @Resource 112 @Resource
109 private PayOrderApi payOrderApi; 113 private PayOrderApi payOrderApi;
@@ -718,14 +722,14 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -718,14 +722,14 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
718 722
719 @Override 723 @Override
720 @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_PICK_UP_RECEIVE) 724 @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_PICK_UP_RECEIVE)
721 - public void pickUpOrderByAdmin(Long id) {  
722 - getSelf().pickUpOrder(tradeOrderMapper.selectById(id)); 725 + public void pickUpOrderByAdmin(Long userId, Long id) {
  726 + getSelf().pickUpOrder(userId, tradeOrderMapper.selectById(id));
723 } 727 }
724 728
725 @Override 729 @Override
726 @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_PICK_UP_RECEIVE) 730 @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_PICK_UP_RECEIVE)
727 - public void pickUpOrderByAdmin(String pickUpVerifyCode) {  
728 - getSelf().pickUpOrder(tradeOrderMapper.selectOneByPickUpVerifyCode(pickUpVerifyCode)); 731 + public void pickUpOrderByAdmin(Long userId, String pickUpVerifyCode) {
  732 + getSelf().pickUpOrder(userId, tradeOrderMapper.selectOneByPickUpVerifyCode(pickUpVerifyCode));
729 } 733 }
730 734
731 @Override 735 @Override
@@ -734,13 +738,19 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -734,13 +738,19 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
734 } 738 }
735 739
736 @Transactional(rollbackFor = Exception.class) 740 @Transactional(rollbackFor = Exception.class)
737 - public void pickUpOrder(TradeOrderDO order) { 741 + public void pickUpOrder(Long userId, TradeOrderDO order) {
738 if (order == null) { 742 if (order == null) {
739 throw exception(ORDER_NOT_FOUND); 743 throw exception(ORDER_NOT_FOUND);
740 } 744 }
741 if (ObjUtil.notEqual(DeliveryTypeEnum.PICK_UP.getType(), order.getDeliveryType())) { 745 if (ObjUtil.notEqual(DeliveryTypeEnum.PICK_UP.getType(), order.getDeliveryType())) {
742 throw exception(ORDER_RECEIVE_FAIL_DELIVERY_TYPE_NOT_PICK_UP); 746 throw exception(ORDER_RECEIVE_FAIL_DELIVERY_TYPE_NOT_PICK_UP);
743 } 747 }
  748 + DeliveryPickUpStoreDO deliveryPickUpStore = pickUpStoreService.getDeliveryPickUpStore(order.getPickUpStoreId());
  749 + if (deliveryPickUpStore == null
  750 + || !CollUtil.contains(deliveryPickUpStore.getVerifyUserIds(), userId)) {
  751 + throw exception(ORDER_PICK_UP_FAIL_NOT_VERIFY_USER);
  752 + }
  753 +
744 receiveOrder0(order); 754 receiveOrder0(order);
745 } 755 }
746 756
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawServiceImplTest.java
@@ -7,14 +7,13 @@ import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.Brok @@ -7,14 +7,13 @@ import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.Brok
7 import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawDO; 7 import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageWithdrawDO;
8 import cn.iocoder.yudao.module.trade.dal.mysql.brokerage.BrokerageWithdrawMapper; 8 import cn.iocoder.yudao.module.trade.dal.mysql.brokerage.BrokerageWithdrawMapper;
9 import cn.iocoder.yudao.module.trade.service.config.TradeConfigService; 9 import cn.iocoder.yudao.module.trade.service.config.TradeConfigService;
  10 +import jakarta.annotation.Resource;
  11 +import jakarta.validation.Validator;
10 import org.junit.jupiter.api.Disabled; 12 import org.junit.jupiter.api.Disabled;
11 import org.junit.jupiter.api.Test; 13 import org.junit.jupiter.api.Test;
12 import org.springframework.boot.test.mock.mockito.MockBean; 14 import org.springframework.boot.test.mock.mockito.MockBean;
13 import org.springframework.context.annotation.Import; 15 import org.springframework.context.annotation.Import;
14 16
15 -import jakarta.annotation.Resource;  
16 -import jakarta.validation.Validator;  
17 -  
18 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; 17 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
19 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; 18 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
20 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; 19 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
@@ -104,15 +103,15 @@ public class BrokerageWithdrawServiceImplTest extends BaseDbUnitTest { @@ -104,15 +103,15 @@ public class BrokerageWithdrawServiceImplTest extends BaseDbUnitTest {
104 103
105 @Test 104 @Test
106 public void testCalculateFeePrice() { 105 public void testCalculateFeePrice() {
107 - Integer withdrawPrice = 100;  
108 - // 测试手续费比例未设置  
109 - Integer percent = null;  
110 - assertEquals(brokerageWithdrawService.calculateFeePrice(withdrawPrice, percent), 0);  
111 - // 测试手续费给为0  
112 - percent = 0;  
113 - assertEquals(brokerageWithdrawService.calculateFeePrice(withdrawPrice, percent), 0);  
114 - // 测试手续费  
115 - percent = 1;  
116 - assertEquals(brokerageWithdrawService.calculateFeePrice(withdrawPrice, percent), 1); 106 + //Integer withdrawPrice = 100;
  107 + //// 测试手续费比例未设置
  108 + //Integer percent = null;
  109 + //assertEquals(brokerageWithdrawService.calculateFeePrice(withdrawPrice, percent), 0);
  110 + //// 测试手续费给为0
  111 + //percent = 0;
  112 + //assertEquals(brokerageWithdrawService.calculateFeePrice(withdrawPrice, percent), 0);
  113 + //// 测试手续费
  114 + //percent = 1;
  115 + //assertEquals(brokerageWithdrawService.calculateFeePrice(withdrawPrice, percent), 1);
117 } 116 }
118 } 117 }
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/notify/dto/PayTransferNotifyReqDTO.java
@@ -13,6 +13,7 @@ import jakarta.validation.constraints.NotNull; @@ -13,6 +13,7 @@ import jakarta.validation.constraints.NotNull;
13 @Data 13 @Data
14 public class PayTransferNotifyReqDTO { 14 public class PayTransferNotifyReqDTO {
15 15
  16 + // TODO 芋艿:要不要改成 orderId 待定;
16 /** 17 /**
17 * 商户转账单号 18 * 商户转账单号
18 */ 19 */
@@ -24,4 +25,5 @@ public class PayTransferNotifyReqDTO { @@ -24,4 +25,5 @@ public class PayTransferNotifyReqDTO {
24 */ 25 */
25 @NotNull(message = "转账订单编号不能为空") 26 @NotNull(message = "转账订单编号不能为空")
26 private Long payTransferId; 27 private Long payTransferId;
  28 +
27 } 29 }
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/transfer/PayTransferApi.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.pay.api.transfer; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.pay.api.transfer;
2 2
3 import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO; 3 import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO;
4 4
  5 +import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferRespDTO;
5 import jakarta.validation.Valid; 6 import jakarta.validation.Valid;
6 7
7 /** 8 /**
@@ -19,4 +20,12 @@ public interface PayTransferApi { @@ -19,4 +20,12 @@ public interface PayTransferApi {
19 */ 20 */
20 Long createTransfer(@Valid PayTransferCreateReqDTO reqDTO); 21 Long createTransfer(@Valid PayTransferCreateReqDTO reqDTO);
21 22
  23 + /**
  24 + * 获得转账单
  25 + *
  26 + * @param id 转账单编号
  27 + * @return 转账单
  28 + */
  29 + PayTransferRespDTO getTransfer(Long id);
  30 +
22 } 31 }
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/transfer/dto/PayTransferRespDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pay.api.transfer.dto;
  2 +
  3 +import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum;
  4 +import lombok.Data;
  5 +
  6 +@Data
  7 +public class PayTransferRespDTO {
  8 +
  9 + /**
  10 + * 编号
  11 + */
  12 + private Long id;
  13 +
  14 + /**
  15 + * 转账单号
  16 + */
  17 + private String no;
  18 +
  19 + /**
  20 + * 转账金额,单位:分
  21 + */
  22 + private Integer price;
  23 +
  24 + /**
  25 + * 转账状态
  26 + *
  27 + * 枚举 {@link PayTransferStatusEnum}
  28 + */
  29 + private Integer status;
  30 +
  31 +}
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/wallet/PayWalletApi.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pay.api.wallet;
  2 +
  3 +import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletAddBalanceReqDTO;
  4 +
  5 +/**
  6 + * 钱包 API 接口
  7 + *
  8 + * @author liurulin
  9 + */
  10 +public interface PayWalletApi {
  11 +
  12 + /**
  13 + * 添加钱包余额
  14 + *
  15 + * @param reqDTO 增加余额请求
  16 + */
  17 + void addWalletBalance(PayWalletAddBalanceReqDTO reqDTO);
  18 +
  19 +}
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/wallet/dto/PayWalletAddBalanceReqDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pay.api.wallet.dto;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
  4 +import jakarta.validation.constraints.NotNull;
  5 +import lombok.Data;
  6 +
  7 +/**
  8 + * 钱包余额增加 Request DTO
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +@Data
  13 +public class PayWalletAddBalanceReqDTO {
  14 +
  15 + /**
  16 + * 用户编号
  17 + *
  18 + * 关联 MemberUserDO 的 id 属性,或者 AdminUserDO 的 id 属性
  19 + */
  20 + @NotNull(message = "用户编号不能为空")
  21 + private Long userId;
  22 + /**
  23 + * 用户类型
  24 + *
  25 + * 关联 {@link UserTypeEnum}
  26 + */
  27 + @NotNull(message = "用户类型不能为空")
  28 + private Integer userType;
  29 +
  30 + /**
  31 + * 关联业务分类
  32 + */
  33 + @NotNull(message = "关联业务分类不能为空")
  34 + private Integer bizType;
  35 + /**
  36 + * 关联业务编号
  37 + */
  38 + @NotNull(message = "关联业务编号不能为空")
  39 + private String bizId;
  40 +
  41 + /**
  42 + * 交易金额,单位分
  43 + *
  44 + * 正值表示余额增加,负值表示余额减少
  45 + */
  46 + @NotNull(message = "交易金额不能为空")
  47 + private Integer price;
  48 +
  49 +}
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/wallet/PayWalletBizTypeEnum.java
@@ -19,7 +19,8 @@ public enum PayWalletBizTypeEnum implements IntArrayValuable { @@ -19,7 +19,8 @@ public enum PayWalletBizTypeEnum implements IntArrayValuable {
19 RECHARGE_REFUND(2, "充值退款"), 19 RECHARGE_REFUND(2, "充值退款"),
20 PAYMENT(3, "支付"), 20 PAYMENT(3, "支付"),
21 PAYMENT_REFUND(4, "支付退款"), 21 PAYMENT_REFUND(4, "支付退款"),
22 - UPDATE_BALANCE(5, "更新余额"); 22 + UPDATE_BALANCE(5, "更新余额"),
  23 + BROKERAGE_WITHDRAW(6, "分佣提现");
23 24
24 /** 25 /**
25 * 业务分类 26 * 业务分类
@@ -36,4 +37,9 @@ public enum PayWalletBizTypeEnum implements IntArrayValuable { @@ -36,4 +37,9 @@ public enum PayWalletBizTypeEnum implements IntArrayValuable {
36 public int[] array() { 37 public int[] array() {
37 return ARRAYS; 38 return ARRAYS;
38 } 39 }
  40 +
  41 + public static PayWalletBizTypeEnum valueOf(Integer type) {
  42 + return Arrays.stream(values()).filter(item -> item.getType().equals(type)).findFirst().orElse(null);
  43 + }
  44 +
39 } 45 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/transfer/PayTransferApiImpl.java
1 package cn.iocoder.yudao.module.pay.api.transfer; 1 package cn.iocoder.yudao.module.pay.api.transfer;
2 2
  3 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
3 import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO; 4 import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO;
  5 +import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferRespDTO;
  6 +import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO;
4 import cn.iocoder.yudao.module.pay.service.transfer.PayTransferService; 7 import cn.iocoder.yudao.module.pay.service.transfer.PayTransferService;
  8 +import jakarta.annotation.Resource;
5 import org.springframework.stereotype.Service; 9 import org.springframework.stereotype.Service;
6 import org.springframework.validation.annotation.Validated; 10 import org.springframework.validation.annotation.Validated;
7 11
8 -import jakarta.annotation.Resource;  
9 -  
10 /** 12 /**
11 * 转账单 API 实现类 13 * 转账单 API 实现类
12 * 14 *
@@ -24,4 +26,10 @@ public class PayTransferApiImpl implements PayTransferApi { @@ -24,4 +26,10 @@ public class PayTransferApiImpl implements PayTransferApi {
24 return payTransferService.createTransfer(reqDTO); 26 return payTransferService.createTransfer(reqDTO);
25 } 27 }
26 28
  29 + @Override
  30 + public PayTransferRespDTO getTransfer(Long id) {
  31 + PayTransferDO transfer = payTransferService.getTransfer(id);
  32 + return BeanUtils.toBean(transfer, PayTransferRespDTO.class);
  33 + }
  34 +
27 } 35 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/wallet/PayWalletApiImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pay.api.wallet;
  2 +
  3 +import cn.hutool.core.lang.Assert;
  4 +import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletAddBalanceReqDTO;
  5 +import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
  6 +import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum;
  7 +import cn.iocoder.yudao.module.pay.service.wallet.PayWalletService;
  8 +import jakarta.annotation.Resource;
  9 +import org.springframework.stereotype.Service;
  10 +
  11 +/**
  12 + * 钱包 API 实现类
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +@Service
  17 +public class PayWalletApiImpl implements PayWalletApi {
  18 +
  19 + @Resource
  20 + private PayWalletService payWalletService;
  21 +
  22 + @Override
  23 + public void addWalletBalance(PayWalletAddBalanceReqDTO reqDTO) {
  24 + // 创建或获取钱包
  25 + PayWalletDO wallet = payWalletService.getOrCreateWallet(reqDTO.getUserId(), reqDTO.getUserType());
  26 + Assert.notNull(wallet, "钱包({}/{})不存在", reqDTO.getUserId(), reqDTO.getUserType());
  27 +
  28 + // 增加余额
  29 + PayWalletBizTypeEnum bizType = PayWalletBizTypeEnum.valueOf(reqDTO.getBizType());
  30 + payWalletService.addWalletBalance(wallet.getId(), reqDTO.getBizId(), bizType, reqDTO.getPrice());
  31 + }
  32 +
  33 +}
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/app/vo/PayAppBaseVO.java
@@ -40,4 +40,8 @@ public class PayAppBaseVO { @@ -40,4 +40,8 @@ public class PayAppBaseVO {
40 @URL(message = "退款结果的回调地址必须为 URL 格式") 40 @URL(message = "退款结果的回调地址必须为 URL 格式")
41 private String refundNotifyUrl; 41 private String refundNotifyUrl;
42 42
  43 + @Schema(description = "转账结果的回调地址", example = "http://127.0.0.1:48080/transfer-callback")
  44 + @URL(message = "转账结果的回调地址必须为 URL 格式")
  45 + private String transferNotifyUrl;
  46 +
43 } 47 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java
@@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.pay.core.client.PayClient; 6 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
7 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; 7 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
8 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; 8 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
  9 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
9 import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskDetailRespVO; 10 import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskDetailRespVO;
10 import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO; 11 import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO;
11 import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskRespVO; 12 import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskRespVO;
@@ -18,6 +19,7 @@ import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; @@ -18,6 +19,7 @@ import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
18 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; 19 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
19 import cn.iocoder.yudao.module.pay.service.order.PayOrderService; 20 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
20 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; 21 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
  22 +import cn.iocoder.yudao.module.pay.service.transfer.PayTransferService;
21 import io.swagger.v3.oas.annotations.Operation; 23 import io.swagger.v3.oas.annotations.Operation;
22 import io.swagger.v3.oas.annotations.Parameter; 24 import io.swagger.v3.oas.annotations.Parameter;
23 import io.swagger.v3.oas.annotations.tags.Tag; 25 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -49,6 +51,8 @@ public class PayNotifyController { @@ -49,6 +51,8 @@ public class PayNotifyController {
49 @Resource 51 @Resource
50 private PayRefundService refundService; 52 private PayRefundService refundService;
51 @Resource 53 @Resource
  54 + private PayTransferService payTransferService;
  55 + @Resource
52 private PayNotifyService notifyService; 56 private PayNotifyService notifyService;
53 @Resource 57 @Resource
54 private PayAppService appService; 58 private PayAppService appService;
@@ -65,7 +69,7 @@ public class PayNotifyController { @@ -65,7 +69,7 @@ public class PayNotifyController {
65 // 1. 校验支付渠道是否存在 69 // 1. 校验支付渠道是否存在
66 PayClient payClient = channelService.getPayClient(channelId); 70 PayClient payClient = channelService.getPayClient(channelId);
67 if (payClient == null) { 71 if (payClient == null) {
68 - log.error("[notifyCallback][渠道编号({}) 找不到对应的支付客户端]", channelId); 72 + log.error("[notifyOrder][渠道编号({}) 找不到对应的支付客户端]", channelId);
69 throw exception(CHANNEL_NOT_FOUND); 73 throw exception(CHANNEL_NOT_FOUND);
70 } 74 }
71 75
@@ -79,13 +83,13 @@ public class PayNotifyController { @@ -79,13 +83,13 @@ public class PayNotifyController {
79 @Operation(summary = "支付渠道的统一【退款】回调") 83 @Operation(summary = "支付渠道的统一【退款】回调")
80 @PermitAll 84 @PermitAll
81 public String notifyRefund(@PathVariable("channelId") Long channelId, 85 public String notifyRefund(@PathVariable("channelId") Long channelId,
82 - @RequestParam(required = false) Map<String, String> params,  
83 - @RequestBody(required = false) String body) { 86 + @RequestParam(required = false) Map<String, String> params,
  87 + @RequestBody(required = false) String body) {
84 log.info("[notifyRefund][channelId({}) 回调数据({}/{})]", channelId, params, body); 88 log.info("[notifyRefund][channelId({}) 回调数据({}/{})]", channelId, params, body);
85 // 1. 校验支付渠道是否存在 89 // 1. 校验支付渠道是否存在
86 PayClient payClient = channelService.getPayClient(channelId); 90 PayClient payClient = channelService.getPayClient(channelId);
87 if (payClient == null) { 91 if (payClient == null) {
88 - log.error("[notifyCallback][渠道编号({}) 找不到对应的支付客户端]", channelId); 92 + log.error("[notifyRefund][渠道编号({}) 找不到对应的支付客户端]", channelId);
89 throw exception(CHANNEL_NOT_FOUND); 93 throw exception(CHANNEL_NOT_FOUND);
90 } 94 }
91 95
@@ -95,6 +99,26 @@ public class PayNotifyController { @@ -95,6 +99,26 @@ public class PayNotifyController {
95 return "success"; 99 return "success";
96 } 100 }
97 101
  102 + @PostMapping(value = "/transfer/{channelId}")
  103 + @Operation(summary = "支付渠道的统一【转账】回调")
  104 + @PermitAll
  105 + public String notifyTransfer(@PathVariable("channelId") Long channelId,
  106 + @RequestParam(required = false) Map<String, String> params,
  107 + @RequestBody(required = false) String body) {
  108 + log.info("[notifyTransfer][channelId({}) 回调数据({}/{})]", channelId, params, body);
  109 + // 1. 校验支付渠道是否存在
  110 + PayClient payClient = channelService.getPayClient(channelId);
  111 + if (payClient == null) {
  112 + log.error("[notifyTransfer][渠道编号({}) 找不到对应的支付客户端]", channelId);
  113 + throw exception(CHANNEL_NOT_FOUND);
  114 + }
  115 +
  116 + // 2. 解析通知数据
  117 + PayTransferRespDTO notify = payClient.parseTransferNotify(params, body);
  118 + payTransferService.notifyTransfer(channelId, notify);
  119 + return "success";
  120 + }
  121 +
98 @GetMapping("/get-detail") 122 @GetMapping("/get-detail")
99 @Operation(summary = "获得回调通知的明细") 123 @Operation(summary = "获得回调通知的明细")
100 @Parameter(name = "id", description = "编号", required = true, example = "1024") 124 @Parameter(name = "id", description = "编号", required = true, example = "1024")
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/transfer/PayTransferConvert.java
@@ -24,7 +24,8 @@ public interface PayTransferConvert { @@ -24,7 +24,8 @@ public interface PayTransferConvert {
24 24
25 PayTransferCreateReqDTO convert(PayDemoTransferCreateReqVO vo); 25 PayTransferCreateReqDTO convert(PayDemoTransferCreateReqVO vo);
26 26
27 - PayTransferRespVO convert(PayTransferDO bean); 27 + PayTransferRespVO convert(PayTransferDO bean);
28 28
29 PageResult<PayTransferPageItemRespVO> convertPage(PageResult<PayTransferDO> pageResult); 29 PageResult<PayTransferPageItemRespVO> convertPage(PageResult<PayTransferDO> pageResult);
  30 +
30 } 31 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/transfer/PayTransferMapper.java
@@ -23,10 +23,6 @@ public interface PayTransferMapper extends BaseMapperX&lt;PayTransferDO&gt; { @@ -23,10 +23,6 @@ public interface PayTransferMapper extends BaseMapperX&lt;PayTransferDO&gt; {
23 PayTransferDO::getMerchantTransferId, merchantTransferId); 23 PayTransferDO::getMerchantTransferId, merchantTransferId);
24 } 24 }
25 25
26 - default PayTransferDO selectByNo(String no){  
27 - return selectOne(PayTransferDO::getNo, no);  
28 - }  
29 -  
30 default PageResult<PayTransferDO> selectPage(PayTransferPageReqVO reqVO) { 26 default PageResult<PayTransferDO> selectPage(PayTransferPageReqVO reqVO) {
31 return selectPage(reqVO, new LambdaQueryWrapperX<PayTransferDO>() 27 return selectPage(reqVO, new LambdaQueryWrapperX<PayTransferDO>()
32 .eqIfPresent(PayTransferDO::getNo, reqVO.getNo()) 28 .eqIfPresent(PayTransferDO::getNo, reqVO.getNo())
@@ -41,9 +37,15 @@ public interface PayTransferMapper extends BaseMapperX&lt;PayTransferDO&gt; { @@ -41,9 +37,15 @@ public interface PayTransferMapper extends BaseMapperX&lt;PayTransferDO&gt; {
41 .orderByDesc(PayTransferDO::getId)); 37 .orderByDesc(PayTransferDO::getId));
42 } 38 }
43 39
44 - default List<PayTransferDO> selectListByStatus(Integer status){ 40 + default List<PayTransferDO> selectListByStatus(Integer status) {
45 return selectList(PayTransferDO::getStatus, status); 41 return selectList(PayTransferDO::getStatus, status);
46 } 42 }
  43 +
  44 + default PayTransferDO selectByAppIdAndNo(Long appId, String no) {
  45 + return selectOne(PayTransferDO::getAppId, appId,
  46 + PayTransferDO::getNo, no);
  47 + }
  48 +
47 } 49 }
48 50
49 51
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/PayWalletMapper.java
@@ -69,6 +69,19 @@ public interface PayWalletMapper extends BaseMapperX&lt;PayWalletDO&gt; { @@ -69,6 +69,19 @@ public interface PayWalletMapper extends BaseMapperX&lt;PayWalletDO&gt; {
69 } 69 }
70 70
71 /** 71 /**
  72 + * 增加余额的时候,更新钱包
  73 + *
  74 + * @param id 钱包 id
  75 + * @param price 钱包金额
  76 + */
  77 + default void updateWhenAdd(Long id, Integer price) {
  78 + LambdaUpdateWrapper<PayWalletDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<PayWalletDO>()
  79 + .setSql(" balance = balance + " + price)
  80 + .eq(PayWalletDO::getId, id);
  81 + update(null, lambdaUpdateWrapper);
  82 + }
  83 +
  84 + /**
72 * 冻结钱包部分余额 85 * 冻结钱包部分余额
73 * 86 *
74 * @param id 钱包 id 87 * @param id 钱包 id
@@ -114,7 +127,6 @@ public interface PayWalletMapper extends BaseMapperX&lt;PayWalletDO&gt; { @@ -114,7 +127,6 @@ public interface PayWalletMapper extends BaseMapperX&lt;PayWalletDO&gt; {
114 return update(null, lambdaUpdateWrapper); 127 return update(null, lambdaUpdateWrapper);
115 } 128 }
116 129
117 -  
118 } 130 }
119 131
120 132
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/config/PayProperties.java
@@ -40,6 +40,15 @@ public class PayProperties { @@ -40,6 +40,15 @@ public class PayProperties {
40 private String refundNotifyUrl; 40 private String refundNotifyUrl;
41 41
42 /** 42 /**
  43 + * 转账回调地址
  44 + *
  45 + * 实际上,对应的 PayNotifyController 的 notifyTransfer 方法的 URL
  46 + *
  47 + * 回调顺序:支付渠道(支付宝支付、微信支付) => yudao-module-pay 的 transferNotifyUrl 地址 => 业务的 PayAppDO.transferNotifyUrl 地址
  48 + */
  49 + private String transferNotifyUrl;
  50 +
  51 + /**
43 * 支付订单 no 的前缀 52 * 支付订单 no 的前缀
44 */ 53 */
45 @NotEmpty(message = "支付订单 no 的前缀不能为空") 54 @NotEmpty(message = "支付订单 no 的前缀不能为空")
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/core/WalletPayClient.java
@@ -178,6 +178,11 @@ public class WalletPayClient extends AbstractPayClient&lt;NonePayClientConfig&gt; { @@ -178,6 +178,11 @@ public class WalletPayClient extends AbstractPayClient&lt;NonePayClientConfig&gt; {
178 } 178 }
179 179
180 @Override 180 @Override
  181 + protected PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body) throws Throwable {
  182 + throw new UnsupportedOperationException("未实现");
  183 + }
  184 +
  185 + @Override
181 public PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) { 186 public PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
182 throw new UnsupportedOperationException("待实现"); 187 throw new UnsupportedOperationException("待实现");
183 } 188 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferService.java
1 package cn.iocoder.yudao.module.pay.service.transfer; 1 package cn.iocoder.yudao.module.pay.service.transfer;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
4 import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO; 5 import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO;
5 import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferCreateReqVO; 6 import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferCreateReqVO;
6 import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferPageReqVO; 7 import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferPageReqVO;
@@ -54,4 +55,13 @@ public interface PayTransferService { @@ -54,4 +55,13 @@ public interface PayTransferService {
54 * @return 同步到状态的转账数量,包括转账成功、转账失败、转账中的 55 * @return 同步到状态的转账数量,包括转账成功、转账失败、转账中的
55 */ 56 */
56 int syncTransfer(); 57 int syncTransfer();
  58 +
  59 + /**
  60 + * 渠道的转账通知
  61 + *
  62 + * @param channelId 渠道编号
  63 + * @param notify 通知
  64 + */
  65 + void notifyTransfer(Long channelId, PayTransferRespDTO notify);
  66 +
57 } 67 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferServiceImpl.java
@@ -21,6 +21,7 @@ import cn.iocoder.yudao.module.pay.dal.mysql.transfer.PayTransferMapper; @@ -21,6 +21,7 @@ import cn.iocoder.yudao.module.pay.dal.mysql.transfer.PayTransferMapper;
21 import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO; 21 import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO;
22 import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; 22 import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum;
23 import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum; 23 import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum;
  24 +import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties;
24 import cn.iocoder.yudao.module.pay.service.app.PayAppService; 25 import cn.iocoder.yudao.module.pay.service.app.PayAppService;
25 import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; 26 import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
26 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; 27 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
@@ -51,6 +52,9 @@ public class PayTransferServiceImpl implements PayTransferService { @@ -51,6 +52,9 @@ public class PayTransferServiceImpl implements PayTransferService {
51 private static final String TRANSFER_NO_PREFIX = "T"; 52 private static final String TRANSFER_NO_PREFIX = "T";
52 53
53 @Resource 54 @Resource
  55 + private PayProperties payProperties;
  56 +
  57 + @Resource
54 private PayTransferMapper transferMapper; 58 private PayTransferMapper transferMapper;
55 @Resource 59 @Resource
56 private PayAppService appService; 60 private PayAppService appService;
@@ -96,13 +100,15 @@ public class PayTransferServiceImpl implements PayTransferService { @@ -96,13 +100,15 @@ public class PayTransferServiceImpl implements PayTransferService {
96 transfer = INSTANCE.convert(reqDTO) 100 transfer = INSTANCE.convert(reqDTO)
97 .setChannelId(channel.getId()) 101 .setChannelId(channel.getId())
98 .setNo(no).setStatus(WAITING.getStatus()) 102 .setNo(no).setStatus(WAITING.getStatus())
99 - .setNotifyUrl(payApp.getTransferNotifyUrl()); 103 + .setNotifyUrl(payApp.getTransferNotifyUrl())
  104 + .setAppId(channel.getAppId());
100 transferMapper.insert(transfer); 105 transferMapper.insert(transfer);
101 } 106 }
102 try { 107 try {
103 // 3. 调用三方渠道发起转账 108 // 3. 调用三方渠道发起转账
104 PayTransferUnifiedReqDTO transferUnifiedReq = INSTANCE.convert2(transfer) 109 PayTransferUnifiedReqDTO transferUnifiedReq = INSTANCE.convert2(transfer)
105 .setOutTransferNo(transfer.getNo()); 110 .setOutTransferNo(transfer.getNo());
  111 + transferUnifiedReq.setNotifyUrl(genChannelTransferNotifyUrl(channel));
106 PayTransferRespDTO unifiedTransferResp = client.unifiedTransfer(transferUnifiedReq); 112 PayTransferRespDTO unifiedTransferResp = client.unifiedTransfer(transferUnifiedReq);
107 // 4. 通知转账结果 113 // 4. 通知转账结果
108 getSelf().notifyTransfer(channel, unifiedTransferResp); 114 getSelf().notifyTransfer(channel, unifiedTransferResp);
@@ -116,6 +122,16 @@ public class PayTransferServiceImpl implements PayTransferService { @@ -116,6 +122,16 @@ public class PayTransferServiceImpl implements PayTransferService {
116 return transfer.getId(); 122 return transfer.getId();
117 } 123 }
118 124
  125 + /**
  126 + * 根据支付渠道的编码,生成支付渠道的回调地址
  127 + *
  128 + * @param channel 支付渠道
  129 + * @return 支付渠道的回调地址 配置地址 + "/" + channel id
  130 + */
  131 + private String genChannelTransferNotifyUrl(PayChannelDO channel) {
  132 + return payProperties.getTransferNotifyUrl() + "/" + channel.getId();
  133 + }
  134 +
119 private PayTransferDO validateTransferCanCreate(PayTransferCreateReqDTO dto, Long appId) { 135 private PayTransferDO validateTransferCanCreate(PayTransferCreateReqDTO dto, Long appId) {
120 PayTransferDO transfer = transferMapper.selectByAppIdAndMerchantTransferId(appId, dto.getMerchantTransferId()); 136 PayTransferDO transfer = transferMapper.selectByAppIdAndMerchantTransferId(appId, dto.getMerchantTransferId());
121 if (transfer != null) { 137 if (transfer != null) {
@@ -154,7 +170,7 @@ public class PayTransferServiceImpl implements PayTransferService { @@ -154,7 +170,7 @@ public class PayTransferServiceImpl implements PayTransferService {
154 170
155 private void notifyTransferInProgress(PayChannelDO channel, PayTransferRespDTO notify) { 171 private void notifyTransferInProgress(PayChannelDO channel, PayTransferRespDTO notify) {
156 // 1.校验 172 // 1.校验
157 - PayTransferDO transfer = transferMapper.selectByNo(notify.getOutTransferNo()); 173 + PayTransferDO transfer = transferMapper.selectByAppIdAndNo(channel.getAppId(), notify.getOutTransferNo());
158 if (transfer == null) { 174 if (transfer == null) {
159 throw exception(PAY_TRANSFER_NOT_FOUND); 175 throw exception(PAY_TRANSFER_NOT_FOUND);
160 } 176 }
@@ -172,16 +188,12 @@ public class PayTransferServiceImpl implements PayTransferService { @@ -172,16 +188,12 @@ public class PayTransferServiceImpl implements PayTransferService {
172 throw exception(PAY_TRANSFER_STATUS_IS_NOT_WAITING); 188 throw exception(PAY_TRANSFER_STATUS_IS_NOT_WAITING);
173 } 189 }
174 log.info("[notifyTransferInProgress][transfer({}) 更新为转账进行中状态]", transfer.getId()); 190 log.info("[notifyTransferInProgress][transfer({}) 更新为转账进行中状态]", transfer.getId());
175 -  
176 - // 3. 插入转账通知记录  
177 - notifyService.createPayNotifyTask(PayNotifyTypeEnum.TRANSFER.getType(),  
178 - transfer.getId());  
179 } 191 }
180 192
181 193
182 private void notifyTransferSuccess(PayChannelDO channel, PayTransferRespDTO notify) { 194 private void notifyTransferSuccess(PayChannelDO channel, PayTransferRespDTO notify) {
183 // 1.校验 195 // 1.校验
184 - PayTransferDO transfer = transferMapper.selectByNo(notify.getOutTransferNo()); 196 + PayTransferDO transfer = transferMapper.selectByAppIdAndNo(channel.getAppId(), notify.getOutTransferNo());
185 if (transfer == null) { 197 if (transfer == null) {
186 throw exception(PAY_TRANSFER_NOT_FOUND); 198 throw exception(PAY_TRANSFER_NOT_FOUND);
187 } 199 }
@@ -210,7 +222,7 @@ public class PayTransferServiceImpl implements PayTransferService { @@ -210,7 +222,7 @@ public class PayTransferServiceImpl implements PayTransferService {
210 222
211 private void notifyTransferClosed(PayChannelDO channel, PayTransferRespDTO notify) { 223 private void notifyTransferClosed(PayChannelDO channel, PayTransferRespDTO notify) {
212 // 1.校验 224 // 1.校验
213 - PayTransferDO transfer = transferMapper.selectByNo(notify.getOutTransferNo()); 225 + PayTransferDO transfer = transferMapper.selectByAppIdAndNo(channel.getAppId(), notify.getOutTransferNo());
214 if (transfer == null) { 226 if (transfer == null) {
215 throw exception(PAY_TRANSFER_NOT_FOUND); 227 throw exception(PAY_TRANSFER_NOT_FOUND);
216 } 228 }
@@ -283,7 +295,7 @@ public class PayTransferServiceImpl implements PayTransferService { @@ -283,7 +295,7 @@ public class PayTransferServiceImpl implements PayTransferService {
283 } 295 }
284 } 296 }
285 297
286 - private void notifyTransfer(Long channelId, PayTransferRespDTO notify) { 298 + public void notifyTransfer(Long channelId, PayTransferRespDTO notify) {
287 // 校验渠道是否有效 299 // 校验渠道是否有效
288 PayChannelDO channel = channelService.validPayChannel(channelId); 300 PayChannelDO channel = channelService.validPayChannel(channelId);
289 // 通知转账结果给对应的业务 301 // 通知转账结果给对应的业务
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletServiceImpl.java
@@ -187,7 +187,7 @@ public class PayWalletServiceImpl implements PayWalletService { @@ -187,7 +187,7 @@ public class PayWalletServiceImpl implements PayWalletService {
187 187
188 // 2. 加锁,更新钱包余额(目的:避免钱包流水的并发更新时,余额变化不连贯) 188 // 2. 加锁,更新钱包余额(目的:避免钱包流水的并发更新时,余额变化不连贯)
189 return lockRedisDAO.lock(walletId, UPDATE_TIMEOUT_MILLIS, () -> { 189 return lockRedisDAO.lock(walletId, UPDATE_TIMEOUT_MILLIS, () -> {
190 - // 2. 更新钱包金额 190 + // 3. 更新钱包金额
191 switch (bizType) { 191 switch (bizType) {
192 case PAYMENT_REFUND: { // 退款更新 192 case PAYMENT_REFUND: { // 退款更新
193 walletMapper.updateWhenConsumptionRefund(payWallet.getId(), price); 193 walletMapper.updateWhenConsumptionRefund(payWallet.getId(), price);
@@ -198,15 +198,15 @@ public class PayWalletServiceImpl implements PayWalletService { @@ -198,15 +198,15 @@ public class PayWalletServiceImpl implements PayWalletService {
198 break; 198 break;
199 } 199 }
200 case UPDATE_BALANCE: // 更新余额 200 case UPDATE_BALANCE: // 更新余额
201 - walletMapper.updateWhenRecharge(payWallet.getId(), price); 201 + case BROKERAGE_WITHDRAW: // 分佣提现
  202 + walletMapper.updateWhenAdd(payWallet.getId(), price);
202 break; 203 break;
203 default: { 204 default: {
204 - // TODO 其它类型待实现  
205 - throw new UnsupportedOperationException("待实现"); 205 + throw new UnsupportedOperationException("待实现:" + bizType);
206 } 206 }
207 } 207 }
208 208
209 - // 3. 生成钱包流水 209 + // 4. 生成钱包流水
210 WalletTransactionCreateReqBO transactionCreateReqBO = new WalletTransactionCreateReqBO() 210 WalletTransactionCreateReqBO transactionCreateReqBO = new WalletTransactionCreateReqBO()
211 .setWalletId(payWallet.getId()).setPrice(price).setBalance(payWallet.getBalance() + price) 211 .setWalletId(payWallet.getId()).setPrice(price).setBalance(payWallet.getBalance() + price)
212 .setBizId(bizId).setBizType(bizType.getType()).setTitle(bizType.getDescription()); 212 .setBizId(bizId).setBizType(bizType.getType()).setTitle(bizType.getDescription());
yudao-module-pay/yudao-spring-boot-starter-biz-pay/pom.xml
@@ -71,6 +71,7 @@ @@ -71,6 +71,7 @@
71 <artifactId>yudao-spring-boot-starter-test</artifactId> 71 <artifactId>yudao-spring-boot-starter-test</artifactId>
72 <scope>test</scope> 72 <scope>test</scope>
73 </dependency> 73 </dependency>
  74 +
74 </dependencies> 75 </dependencies>
75 76
76 </project> 77 </project>
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java
@@ -79,6 +79,8 @@ public interface PayClient { @@ -79,6 +79,8 @@ public interface PayClient {
79 */ 79 */
80 PayRefundRespDTO getRefund(String outTradeNo, String outRefundNo); 80 PayRefundRespDTO getRefund(String outTradeNo, String outRefundNo);
81 81
  82 + // ============ 转账相关 ==========
  83 +
82 /** 84 /**
83 * 调用渠道,进行转账 85 * 调用渠道,进行转账
84 * 86 *
@@ -95,4 +97,14 @@ public interface PayClient { @@ -95,4 +97,14 @@ public interface PayClient {
95 * @return 转账信息 97 * @return 转账信息
96 */ 98 */
97 PayTransferRespDTO getTransfer(String outTradeNo, PayTransferTypeEnum type); 99 PayTransferRespDTO getTransfer(String outTradeNo, PayTransferTypeEnum type);
  100 +
  101 + /**
  102 + * 解析 transfer 回调数据
  103 + *
  104 + * @param params HTTP 回调接口 content type 为 application/x-www-form-urlencoded 的所有参数
  105 + * @param body HTTP 回调接口的 request body
  106 + * @return 转账信息
  107 + */
  108 + PayTransferRespDTO parseTransferNotify(Map<String, String> params, String body);
  109 +
98 } 110 }
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/PayTransferUnifiedReqDTO.java
@@ -9,6 +9,8 @@ import jakarta.validation.constraints.Min; @@ -9,6 +9,8 @@ import jakarta.validation.constraints.Min;
9 import jakarta.validation.constraints.NotBlank; 9 import jakarta.validation.constraints.NotBlank;
10 import jakarta.validation.constraints.NotEmpty; 10 import jakarta.validation.constraints.NotEmpty;
11 import jakarta.validation.constraints.NotNull; 11 import jakarta.validation.constraints.NotNull;
  12 +import org.hibernate.validator.constraints.URL;
  13 +
12 import java.util.Map; 14 import java.util.Map;
13 15
14 import static cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum.*; 16 import static cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum.*;
@@ -75,4 +77,12 @@ public class PayTransferUnifiedReqDTO { @@ -75,4 +77,12 @@ public class PayTransferUnifiedReqDTO {
75 * 支付渠道的额外参数 77 * 支付渠道的额外参数
76 */ 78 */
77 private Map<String, String> channelExtras; 79 private Map<String, String> channelExtras;
  80 +
  81 + /**
  82 + * 转账结果的 notify 回调地址
  83 + */
  84 + @NotEmpty(message = "转账结果的回调地址不能为空")
  85 + @URL(message = "转账结果的 notify 回调地址必须是 URL 格式")
  86 + private String notifyUrl;
  87 +
78 } 88 }
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/WxPayTransferPartnerNotifyV3Result.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.pay.core.client.dto.transfer;
  2 +
  3 +import com.github.binarywang.wxpay.bean.notify.OriginNotifyResponse;
  4 +import com.github.binarywang.wxpay.bean.notify.WxPayBaseNotifyV3Result;
  5 +import com.google.gson.annotations.SerializedName;
  6 +import lombok.Data;
  7 +import lombok.NoArgsConstructor;
  8 +
  9 +import java.io.Serializable;
  10 +
  11 +// TODO @luchi:这个可以复用 wxjava 里的类么?
  12 +@NoArgsConstructor
  13 +public class WxPayTransferPartnerNotifyV3Result implements Serializable, WxPayBaseNotifyV3Result<WxPayTransferPartnerNotifyV3Result.TransferNotifyResult> {
  14 +
  15 + private static final long serialVersionUID = -1L;
  16 +
  17 + /**
  18 + * 源数据
  19 + */
  20 + private OriginNotifyResponse rawData;
  21 +
  22 + /**
  23 + * 解密后的数据
  24 + */
  25 + private TransferNotifyResult result;
  26 +
  27 + @Override
  28 + public void setRawData(OriginNotifyResponse rawData) {
  29 + this.rawData = rawData;
  30 + }
  31 +
  32 + @Override
  33 + public void setResult(TransferNotifyResult data) {
  34 + this.result = data;
  35 + }
  36 +
  37 + public TransferNotifyResult getResult() {
  38 + return result;
  39 + }
  40 +
  41 + public OriginNotifyResponse getRawData() {
  42 + return rawData;
  43 + }
  44 +
  45 + @Data
  46 + @NoArgsConstructor
  47 + public static class TransferNotifyResult implements Serializable {
  48 + private static final long serialVersionUID = 1L;
  49 +
  50 + /*********************** 公共字段 ********************
  51 +
  52 + /**
  53 + * 商家批次单号
  54 + */
  55 + @SerializedName(value = "out_batch_no")
  56 + protected String outBatchNo;
  57 +
  58 + /**
  59 + * 微信批次单号
  60 + */
  61 + @SerializedName(value = "batch_id")
  62 + protected String batchId;
  63 +
  64 + /**
  65 + * 批次状态
  66 + */
  67 + @SerializedName(value = "batch_status")
  68 + protected String batchStatus;
  69 +
  70 + /**
  71 + * 批次总笔数
  72 + */
  73 + @SerializedName(value = "total_num")
  74 + protected Integer totalNum;
  75 +
  76 + /**
  77 + * 批次总金额
  78 + */
  79 + @SerializedName(value = "total_amount")
  80 + protected Integer totalAmount;
  81 +
  82 + /**
  83 + * 批次更新时间
  84 + */
  85 + @SerializedName(value = "update_time")
  86 + private String updateTime;
  87 +
  88 + /*********************** FINISHED ********************
  89 +
  90 + /**
  91 + * 转账成功金额
  92 + */
  93 + @SerializedName(value = "success_amount")
  94 + protected Integer successAmount;
  95 +
  96 + /**
  97 + * 转账成功笔数
  98 + */
  99 + @SerializedName(value = "success_num")
  100 + protected Integer successNum;
  101 +
  102 + /**
  103 + * 转账失败金额
  104 + */
  105 + @SerializedName(value = "fail_amount")
  106 + protected Integer failAmount;
  107 +
  108 + /**
  109 + * 转账失败笔数
  110 + */
  111 + @SerializedName(value = "fail_num")
  112 + protected Integer failNum;
  113 +
  114 + /*********************** CLOSED ********************
  115 +
  116 + /**
  117 + * 商户号
  118 + */
  119 + @SerializedName(value = "mchid")
  120 + protected String mchId;
  121 +
  122 + /**
  123 + * 批次关闭原因
  124 + */
  125 + @SerializedName(value = "close_reason")
  126 + protected String closeReason;
  127 +
  128 + }
  129 +}
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java
@@ -220,6 +220,22 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen @@ -220,6 +220,22 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen
220 } 220 }
221 221
222 @Override 222 @Override
  223 + public final PayTransferRespDTO parseTransferNotify(Map<String, String> params, String body) {
  224 + try {
  225 + return doParseTransferNotify(params, body);
  226 + } catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可
  227 + throw ex;
  228 + } catch (Throwable ex) {
  229 + log.error("[doParseTransferNotify][客户端({}) params({}) body({}) 解析失败]",
  230 + getId(), params, body, ex);
  231 + throw buildPayException(ex);
  232 + }
  233 + }
  234 +
  235 + protected abstract PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body)
  236 + throws Throwable;
  237 +
  238 + @Override
223 public final PayTransferRespDTO getTransfer(String outTradeNo, PayTransferTypeEnum type) { 239 public final PayTransferRespDTO getTransfer(String outTradeNo, PayTransferTypeEnum type) {
224 try { 240 try {
225 return doGetTransfer(outTradeNo, type); 241 return doGetTransfer(outTradeNo, type);
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java
@@ -325,6 +325,12 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa @@ -325,6 +325,12 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
325 } 325 }
326 } 326 }
327 327
  328 + // TODO @chihuo:这里是不是也要实现,支付宝的。
  329 + @Override
  330 + protected PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body) throws Throwable {
  331 + throw new UnsupportedOperationException("未实现");
  332 + }
  333 +
328 // ========== 各种工具方法 ========== 334 // ========== 各种工具方法 ==========
329 335
330 protected String formatAmount(Integer amount) { 336 protected String formatAmount(Integer amount) {
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
2 2
3 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; 3 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
  5 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
5 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; 6 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
6 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; 7 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
7 import com.alipay.api.AlipayApiException; 8 import com.alipay.api.AlipayApiException;
@@ -56,4 +57,5 @@ public class AlipayAppPayClient extends AbstractAlipayPayClient { @@ -56,4 +57,5 @@ public class AlipayAppPayClient extends AbstractAlipayPayClient {
56 return PayOrderRespDTO.waitingOf(displayMode, response.getBody(), 57 return PayOrderRespDTO.waitingOf(displayMode, response.getBody(),
57 reqDTO.getOutTradeNo(), response); 58 reqDTO.getOutTradeNo(), response);
58 } 59 }
  60 +
59 } 61 }
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java
@@ -5,6 +5,7 @@ import cn.hutool.core.map.MapUtil; @@ -5,6 +5,7 @@ import cn.hutool.core.map.MapUtil;
5 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
6 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; 6 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
7 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 7 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
  8 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
8 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; 9 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
9 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; 10 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
10 import com.alipay.api.AlipayApiException; 11 import com.alipay.api.AlipayApiException;
@@ -82,4 +83,5 @@ public class AlipayBarPayClient extends AbstractAlipayPayClient { @@ -82,4 +83,5 @@ public class AlipayBarPayClient extends AbstractAlipayPayClient {
82 return PayOrderRespDTO.waitingOf(displayMode, "", 83 return PayOrderRespDTO.waitingOf(displayMode, "",
83 reqDTO.getOutTradeNo(), response); 84 reqDTO.getOutTradeNo(), response);
84 } 85 }
  86 +
85 } 87 }
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java
@@ -4,6 +4,7 @@ import cn.hutool.core.util.ObjectUtil; @@ -4,6 +4,7 @@ import cn.hutool.core.util.ObjectUtil;
4 import cn.hutool.http.Method; 4 import cn.hutool.http.Method;
5 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; 5 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
6 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 6 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
  7 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
7 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; 8 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
8 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; 9 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
9 import com.alipay.api.AlipayApiException; 10 import com.alipay.api.AlipayApiException;
@@ -66,4 +67,5 @@ public class AlipayPcPayClient extends AbstractAlipayPayClient { @@ -66,4 +67,5 @@ public class AlipayPcPayClient extends AbstractAlipayPayClient {
66 return PayOrderRespDTO.waitingOf(displayMode, response.getBody(), 67 return PayOrderRespDTO.waitingOf(displayMode, response.getBody(),
67 reqDTO.getOutTradeNo(), response); 68 reqDTO.getOutTradeNo(), response);
68 } 69 }
  70 +
69 } 71 }
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/mock/MockPayClient.java
@@ -58,6 +58,11 @@ public class MockPayClient extends AbstractPayClient&lt;NonePayClientConfig&gt; { @@ -58,6 +58,11 @@ public class MockPayClient extends AbstractPayClient&lt;NonePayClientConfig&gt; {
58 } 58 }
59 59
60 @Override 60 @Override
  61 + protected PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body) throws Throwable {
  62 + throw new UnsupportedOperationException("未实现");
  63 + }
  64 +
  65 + @Override
61 protected PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body) { 66 protected PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body) {
62 throw new UnsupportedOperationException("模拟支付无退款回调"); 67 throw new UnsupportedOperationException("模拟支付无退款回调");
63 } 68 }
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
@@ -14,6 +14,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; @@ -14,6 +14,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
14 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; 14 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
15 import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO; 15 import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
16 import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO; 16 import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
  17 +import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.WxPayTransferPartnerNotifyV3Result;
17 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; 18 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
18 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; 19 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
19 import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum; 20 import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
@@ -23,6 +24,10 @@ import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult; @@ -23,6 +24,10 @@ import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
23 import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result; 24 import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
24 import com.github.binarywang.wxpay.bean.request.*; 25 import com.github.binarywang.wxpay.bean.request.*;
25 import com.github.binarywang.wxpay.bean.result.*; 26 import com.github.binarywang.wxpay.bean.result.*;
  27 +import com.github.binarywang.wxpay.bean.transfer.QueryTransferBatchesRequest;
  28 +import com.github.binarywang.wxpay.bean.transfer.QueryTransferBatchesResult;
  29 +import com.github.binarywang.wxpay.bean.transfer.TransferBatchesRequest;
  30 +import com.github.binarywang.wxpay.bean.transfer.TransferBatchesResult;
26 import com.github.binarywang.wxpay.config.WxPayConfig; 31 import com.github.binarywang.wxpay.config.WxPayConfig;
27 import com.github.binarywang.wxpay.exception.WxPayException; 32 import com.github.binarywang.wxpay.exception.WxPayException;
28 import com.github.binarywang.wxpay.service.WxPayService; 33 import com.github.binarywang.wxpay.service.WxPayService;
@@ -31,6 +36,8 @@ import lombok.extern.slf4j.Slf4j; @@ -31,6 +36,8 @@ import lombok.extern.slf4j.Slf4j;
31 36
32 import java.time.LocalDateTime; 37 import java.time.LocalDateTime;
33 import java.time.ZoneId; 38 import java.time.ZoneId;
  39 +import java.util.Collections;
  40 +import java.util.List;
34 import java.util.Map; 41 import java.util.Map;
35 import java.util.Objects; 42 import java.util.Objects;
36 43
@@ -350,6 +357,33 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC @@ -350,6 +357,33 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
350 } 357 }
351 358
352 @Override 359 @Override
  360 + public PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body) throws WxPayException {
  361 + switch (config.getApiVersion()) {
  362 + case API_VERSION_V3:
  363 + return parseTransferNotifyV3(body);
  364 + case API_VERSION_V2:
  365 + throw new UnsupportedOperationException("V2 版本暂不支持,建议使用 V3 版本");
  366 + default:
  367 + throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
  368 + }
  369 + }
  370 +
  371 + private PayTransferRespDTO parseTransferNotifyV3(String body) throws WxPayException {
  372 + // 1. 解析回调
  373 + // TODO @luchi:这个可以复用 wxjava 里的类么?
  374 + WxPayTransferPartnerNotifyV3Result response = client.baseParseOrderNotifyV3Result(body, null, WxPayTransferPartnerNotifyV3Result.class, WxPayTransferPartnerNotifyV3Result.TransferNotifyResult.class);
  375 + WxPayTransferPartnerNotifyV3Result.TransferNotifyResult result = response.getResult();
  376 + // 2. 构建结果
  377 + if (Objects.equals("FINISHED", result.getBatchStatus())) {
  378 + if (result.getFailNum() <= 0) {
  379 + return PayTransferRespDTO.successOf(result.getBatchId(), parseDateV3(result.getUpdateTime()),
  380 + result.getOutBatchNo(), response);
  381 + }
  382 + }
  383 + return PayTransferRespDTO.closedOf(result.getBatchStatus(), result.getCloseReason(), result.getOutBatchNo(), response);
  384 + }
  385 +
  386 + @Override
353 protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) throws WxPayException { 387 protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) throws WxPayException {
354 try { 388 try {
355 switch (config.getApiVersion()) { 389 switch (config.getApiVersion()) {
@@ -427,13 +461,54 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC @@ -427,13 +461,54 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
427 } 461 }
428 462
429 @Override 463 @Override
430 - protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {  
431 - throw new UnsupportedOperationException("待实现"); 464 + protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) throws WxPayException {
  465 + // 1. 构建 TransferBatchesRequest 请求
  466 + List<TransferBatchesRequest.TransferDetail> transferDetailList = Collections.singletonList(
  467 + TransferBatchesRequest.TransferDetail.newBuilder()
  468 + .outDetailNo(reqDTO.getOutTransferNo())
  469 + .transferAmount(reqDTO.getPrice())
  470 + .transferRemark(reqDTO.getSubject())
  471 + .openid(reqDTO.getOpenid())
  472 + .build());
  473 + // TODO @luchi:能不能我们搞个 TransferBatchesRequestX extends TransferBatchesRequest,这样更简洁一点。
  474 + TransferBatchesRequest transferBatches = TransferBatchesRequest.newBuilder()
  475 + .appid(this.config.getAppId())
  476 + .outBatchNo(reqDTO.getOutTransferNo())
  477 + .batchName(reqDTO.getSubject())
  478 + .batchRemark(reqDTO.getSubject())
  479 + .totalAmount(reqDTO.getPrice())
  480 + .totalNum(transferDetailList.size())
  481 + .transferDetailList(transferDetailList).build()
  482 + .setNotifyUrl(reqDTO.getNotifyUrl());
  483 + // 2.1 执行请求
  484 + TransferBatchesResult transferBatchesResult = client.getTransferService().transferBatches(transferBatches);
  485 + // 2.2 创建返回结果
  486 + return PayTransferRespDTO.dealingOf(transferBatchesResult.getBatchId(), reqDTO.getOutTransferNo(), transferBatchesResult);
432 } 487 }
433 488
434 @Override 489 @Override
435 - protected PayTransferRespDTO doGetTransfer(String outTradeNo, PayTransferTypeEnum type) {  
436 - throw new UnsupportedOperationException("待实现"); 490 + protected PayTransferRespDTO doGetTransfer(String outTradeNo, PayTransferTypeEnum type) throws WxPayException {
  491 + QueryTransferBatchesRequest request = QueryTransferBatchesRequest.newBuilder()
  492 + .outBatchNo(outTradeNo).needQueryDetail(true).offset(0).limit(20).detailStatus("ALL")
  493 + .build();
  494 + QueryTransferBatchesResult response = client.getTransferService().transferBatchesOutBatchNo(request);
  495 + QueryTransferBatchesResult.TransferBatch transferBatch = response.getTransferBatch();
  496 + if (Objects.equals("FINISHED", transferBatch.getBatchStatus())) {
  497 + // 明细中全部成功则成功,任一失败则失败
  498 + if (response.getTransferDetailList().stream().allMatch(detail -> Objects.equals("SUCCESS", detail.getDetailStatus()))) {
  499 + return PayTransferRespDTO.successOf(transferBatch.getBatchId(), parseDateV3(transferBatch.getUpdateTime()),
  500 + transferBatch.getOutBatchNo(), response);
  501 + }
  502 + if (response.getTransferDetailList().stream().anyMatch(detail -> Objects.equals("FAIL", detail.getDetailStatus()))) {
  503 + return PayTransferRespDTO.closedOf(transferBatch.getBatchStatus(), transferBatch.getCloseReason(),
  504 + transferBatch.getOutBatchNo(), response);
  505 + }
  506 + }
  507 + if (Objects.equals("CLOSED", transferBatch.getBatchStatus())) {
  508 + return PayTransferRespDTO.closedOf(transferBatch.getBatchStatus(), transferBatch.getCloseReason(),
  509 + transferBatch.getOutBatchNo(), response);
  510 + }
  511 + return PayTransferRespDTO.dealingOf(transferBatch.getBatchId(), transferBatch.getOutBatchNo(), response);
437 } 512 }
438 513
439 // ========== 各种工具方法 ========== 514 // ========== 各种工具方法 ==========
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBatchesRequest.java 0 → 100644
  1 +package com.github.binarywang.wxpay.bean.transfer;
  2 +
  3 +import com.github.binarywang.wxpay.v3.SpecEncrypt;
  4 +import com.google.gson.annotations.SerializedName;
  5 +import lombok.AllArgsConstructor;
  6 +import lombok.Builder;
  7 +import lombok.Data;
  8 +import lombok.NoArgsConstructor;
  9 +
  10 +import java.io.Serializable;
  11 +import java.util.List;
  12 +
  13 +/**
  14 + * 发起商家转账API参数
  15 + *
  16 + * @author zhongjun
  17 + * created on 2022/6/17
  18 + **/
  19 +@Data
  20 +@Builder(builderMethodName = "newBuilder")
  21 +@NoArgsConstructor
  22 +@AllArgsConstructor
  23 +public class TransferBatchesRequest implements Serializable {
  24 + private static final long serialVersionUID = -2175582517588397426L;
  25 +
  26 + /**
  27 + * 直连商户的appid
  28 + */
  29 + @SerializedName("appid")
  30 + private String appid;
  31 +
  32 + /**
  33 + * 商家批次单号
  34 + */
  35 + @SerializedName("out_batch_no")
  36 + private String outBatchNo;
  37 +
  38 + /**
  39 + * 批次名称
  40 + */
  41 + @SerializedName("batch_name")
  42 + private String batchName;
  43 +
  44 + /**
  45 + * 批次备注
  46 + */
  47 + @SerializedName("batch_remark")
  48 + private String batchRemark;
  49 +
  50 + /**
  51 + * 转账总金额
  52 + */
  53 + @SerializedName("total_amount")
  54 + private Integer totalAmount;
  55 +
  56 + /**
  57 + * 转账总笔数
  58 + */
  59 + @SerializedName("total_num")
  60 + private Integer totalNum;
  61 +
  62 + /**
  63 + * 转账明细列表
  64 + */
  65 + @SpecEncrypt
  66 + @SerializedName("transfer_detail_list")
  67 + private List<TransferDetail> transferDetailList;
  68 +
  69 + /**
  70 + * 转账场景ID
  71 + */
  72 + @SerializedName("transfer_scene_id")
  73 + private String transferSceneId;
  74 +
  75 + /**
  76 + * 通知地址 说明:异步接收微信支付结果通知的回调地址,通知url必须为公网可访问的url,必须为https,不能携带参数。
  77 + */
  78 + @SerializedName("notify_url")
  79 + private String notifyUrl;
  80 +
  81 + @Data
  82 + @Builder(builderMethodName = "newBuilder")
  83 + @AllArgsConstructor
  84 + @NoArgsConstructor
  85 + public static class TransferDetail {
  86 +
  87 + /**
  88 + * 商家明细单号
  89 + */
  90 + @SerializedName("out_detail_no")
  91 + private String outDetailNo;
  92 +
  93 + /**
  94 + * 转账金额
  95 + */
  96 + @SerializedName("transfer_amount")
  97 + private Integer transferAmount;
  98 +
  99 + /**
  100 + * 转账备注
  101 + */
  102 + @SerializedName("transfer_remark")
  103 + private String transferRemark;
  104 +
  105 + /**
  106 + * 用户在直连商户应用下的用户标示
  107 + */
  108 + @SerializedName("openid")
  109 + private String openid;
  110 +
  111 + /**
  112 + * 收款用户姓名
  113 + */
  114 + @SpecEncrypt
  115 + @SerializedName("user_name")
  116 + private String userName;
  117 + }
  118 +}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserPageReqVO.java
@@ -35,4 +35,7 @@ public class UserPageReqVO extends PageParam { @@ -35,4 +35,7 @@ public class UserPageReqVO extends PageParam {
35 @Schema(description = "部门编号,同时筛选子部门", example = "1024") 35 @Schema(description = "部门编号,同时筛选子部门", example = "1024")
36 private Long deptId; 36 private Long deptId;
37 37
  38 + @Schema(description = "角色编号", example = "1024")
  39 + private Long roleId;
  40 +
38 } 41 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java
@@ -25,13 +25,14 @@ public interface AdminUserMapper extends BaseMapperX&lt;AdminUserDO&gt; { @@ -25,13 +25,14 @@ public interface AdminUserMapper extends BaseMapperX&lt;AdminUserDO&gt; {
25 return selectOne(AdminUserDO::getMobile, mobile); 25 return selectOne(AdminUserDO::getMobile, mobile);
26 } 26 }
27 27
28 - default PageResult<AdminUserDO> selectPage(UserPageReqVO reqVO, Collection<Long> deptIds) { 28 + default PageResult<AdminUserDO> selectPage(UserPageReqVO reqVO, Collection<Long> deptIds, Collection<Long> userIds) {
29 return selectPage(reqVO, new LambdaQueryWrapperX<AdminUserDO>() 29 return selectPage(reqVO, new LambdaQueryWrapperX<AdminUserDO>()
30 .likeIfPresent(AdminUserDO::getUsername, reqVO.getUsername()) 30 .likeIfPresent(AdminUserDO::getUsername, reqVO.getUsername())
31 .likeIfPresent(AdminUserDO::getMobile, reqVO.getMobile()) 31 .likeIfPresent(AdminUserDO::getMobile, reqVO.getMobile())
32 .eqIfPresent(AdminUserDO::getStatus, reqVO.getStatus()) 32 .eqIfPresent(AdminUserDO::getStatus, reqVO.getStatus())
33 .betweenIfPresent(AdminUserDO::getCreateTime, reqVO.getCreateTime()) 33 .betweenIfPresent(AdminUserDO::getCreateTime, reqVO.getCreateTime())
34 .inIfPresent(AdminUserDO::getDeptId, deptIds) 34 .inIfPresent(AdminUserDO::getDeptId, deptIds)
  35 + .inIfPresent(AdminUserDO::getId, userIds)
35 .orderByDesc(AdminUserDO::getId)); 36 .orderByDesc(AdminUserDO::getId));
36 } 37 }
37 38
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
@@ -46,8 +46,7 @@ import java.time.LocalDateTime; @@ -46,8 +46,7 @@ import java.time.LocalDateTime;
46 import java.util.*; 46 import java.util.*;
47 47
48 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 48 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
49 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;  
50 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 49 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
51 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; 50 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
52 import static cn.iocoder.yudao.module.system.enums.LogRecordConstants.*; 51 import static cn.iocoder.yudao.module.system.enums.LogRecordConstants.*;
53 52
@@ -272,7 +271,12 @@ public class AdminUserServiceImpl implements AdminUserService { @@ -272,7 +271,12 @@ public class AdminUserServiceImpl implements AdminUserService {
272 271
273 @Override 272 @Override
274 public PageResult<AdminUserDO> getUserPage(UserPageReqVO reqVO) { 273 public PageResult<AdminUserDO> getUserPage(UserPageReqVO reqVO) {
275 - return userMapper.selectPage(reqVO, getDeptCondition(reqVO.getDeptId())); 274 + // 如果有角色编号,查询角色对应的用户编号
  275 + Set<Long> userIds = reqVO.getRoleId() != null ?
  276 + permissionService.getUserRoleIdListByRoleId(singleton(reqVO.getRoleId())) : null;
  277 +
  278 + // 分页查询
  279 + return userMapper.selectPage(reqVO, getDeptCondition(reqVO.getDeptId()), userIds);
276 } 280 }
277 281
278 @Override 282 @Override
yudao-server/src/main/resources/application-dev.yaml
@@ -165,7 +165,8 @@ yudao: @@ -165,7 +165,8 @@ yudao:
165 pay: 165 pay:
166 order-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/order # 支付渠道的【支付】回调地址 166 order-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/order # 支付渠道的【支付】回调地址
167 refund-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/refund # 支付渠道的【退款】回调地址 167 refund-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/refund # 支付渠道的【退款】回调地址
168 - demo: true # 开启演示模式 168 + transfer-notify-url: https://yunai.natapp1.cc/admin-api/pay/notify/transfer # 支付渠道的【转账】回调地址
  169 + demo: false # 开启演示模式
169 tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc 170 tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc
170 171
171 justauth: 172 justauth:
yudao-server/src/main/resources/application-local.yaml
@@ -219,6 +219,7 @@ yudao: @@ -219,6 +219,7 @@ yudao:
219 pay: 219 pay:
220 order-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/order # 支付渠道的【支付】回调地址 220 order-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/order # 支付渠道的【支付】回调地址
221 refund-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/refund # 支付渠道的【退款】回调地址 221 refund-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/refund # 支付渠道的【退款】回调地址
  222 + transfer-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/transfer # 支付渠道的【转账】回调地址
222 access-log: # 访问日志的配置项 223 access-log: # 访问日志的配置项
223 enable: false 224 enable: false
224 demo: false # 关闭演示模式 225 demo: false # 关闭演示模式