Explorar o código

Merge remote-tracking branch 'yudao/feature/mall_product' into feature/mall_product

puhui999 hai 1 ano
pai
achega
3976011af0
Modificáronse 36 ficheiros con 314 adicións e 201 borrados
  1. 2 2
      sql/mysql/pay_wallet.sql
  2. 1 1
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/BrokerageApiImpl.java
  3. 0 3
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/vo/BrokerageUserRespVO.java
  4. 12 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageRecordController.java
  5. 16 14
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageUserController.java
  6. 19 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/record/AppBrokerageProductPriceRespVO.java
  7. 1 1
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserChildSummaryRespVO.java
  8. 1 1
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserMySummaryRespVO.java
  9. 1 1
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankByPriceRespVO.java
  10. 3 0
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserRespVO.java
  11. 1 1
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/brokerage/record/BrokerageRecordConvert.java
  12. 2 2
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/brokerage/user/BrokerageUserDO.java
  13. 1 1
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/user/BrokerageUserService.java
  14. 2 2
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/user/BrokerageUserServiceImpl.java
  15. 11 1
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http
  16. 8 0
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginRespVO.java
  17. 1 1
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java
  18. 17 13
      yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java
  19. 2 2
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/wallet/vo/wallet/AppPayWalletRespVO.java
  20. 4 0
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/wallet/PayWalletTransactionConvert.java
  21. 2 4
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/wallet/PayWalletDO.java
  22. 18 42
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/PayWalletMapper.java
  23. 6 7
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/wallet/WalletPayClient.java
  24. 36 65
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletServiceImpl.java
  25. 7 3
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletTransactionService.java
  26. 16 3
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletTransactionServiceImpl.java
  27. 50 0
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/bo/CreateWalletTransactionBO.java
  28. 8 5
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApi.java
  29. 27 0
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserRespDTO.java
  30. 5 4
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApiImpl.java
  31. 4 3
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
  32. 8 5
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserService.java
  33. 5 3
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java
  34. 3 2
      yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java
  35. 10 7
      yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImplTest.java
  36. 4 2
      yudao-server/src/main/resources/application-local.yaml

+ 2 - 2
sql/mysql/pay_wallet.sql

@@ -8,8 +8,8 @@ CREATE TABLE `pay_wallet`
     `user_id`        bigint   NOT NULL COMMENT '用户编号',
     `user_type`      tinyint  NOT NULL DEFAULT 0 COMMENT '用户类型',
     `balance`        int      NOT NULL DEFAULT 0 COMMENT '余额,单位分',
-    `total_expense`  bigint      NOT NULL DEFAULT 0 COMMENT '累计支出,单位分',
-    `total_recharge` bigint      NOT NULL DEFAULT 0 COMMENT '累计充值,单位分',
+    `total_expense`  int      NOT NULL DEFAULT 0 COMMENT '累计支出,单位分',
+    `total_recharge` int      NOT NULL DEFAULT 0 COMMENT '累计充值,单位分',
     `creator`        varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
     `create_time`    datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
     `updater`        varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',

+ 1 - 1
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/BrokerageApiImpl.java

@@ -27,7 +27,7 @@ public class BrokerageApiImpl implements BrokerageApi {
 
     @Override
     public boolean bindUser(Long userId, Long bindUserId, Boolean isNewUser) {
-        return brokerageUserService.bindUser(userId, bindUserId, isNewUser);
+        return brokerageUserService.bindBrokerageUser(userId, bindUserId, isNewUser);
     }
 
 }

+ 0 - 3
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/user/vo/BrokerageUserRespVO.java

@@ -19,7 +19,6 @@ public class BrokerageUserRespVO extends BrokerageUserBaseVO {
     @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
     private LocalDateTime createTime;
 
-
     // ========== 用户信息 ==========
 
     @Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png")
@@ -27,7 +26,6 @@ public class BrokerageUserRespVO extends BrokerageUserBaseVO {
     @Schema(description = "用户昵称", example = "李四")
     private String nickname;
 
-
     // ========== 推广信息 ==========
 
     @Schema(description = "推广用户数量(一级)", example = "20019")
@@ -37,7 +35,6 @@ public class BrokerageUserRespVO extends BrokerageUserBaseVO {
     @Schema(description = "推广订单金额", example = "20019")
     private Integer brokerageOrderPrice;
 
-
     // ========== 提现信息 ==========
 
     @Schema(description = "已提现金额", example = "20019")

+ 12 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageRecordController.java

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.trade.controller.app.brokerage;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
+import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageProductPriceRespVO;
 import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageRecordPageReqVO;
 import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageRecordRespVO;
 import io.swagger.v3.oas.annotations.Operation;
@@ -11,6 +12,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
 import javax.validation.Valid;
@@ -40,4 +42,14 @@ public class AppBrokerageRecordController {
         return success(new PageResult<>(asList(vo1, vo2), 10L));
     }
 
+    @GetMapping("/get-product-brokerage-price")
+    @Operation(summary = "获得商品的分销金额")
+    public CommonResult<AppBrokerageProductPriceRespVO> getProductBrokeragePrice(@RequestParam("spuId") Long spuId) {
+        AppBrokerageProductPriceRespVO respVO = new AppBrokerageProductPriceRespVO();
+        respVO.setEnabled(true); // TODO @疯狂:需要开启分销 + 人允许分销
+        respVO.setBrokerageMinPrice(1);
+        respVO.setBrokerageMaxPrice(2);
+        return success(respVO);
+    }
+
 }

+ 16 - 14
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/AppBrokerageUserController.java

@@ -37,11 +37,19 @@ public class AppBrokerageUserController {
     @PreAuthenticated
     public CommonResult<AppBrokerageUserRespVO> getBrokerageUser() {
         AppBrokerageUserRespVO respVO = new AppBrokerageUserRespVO()
+                .setBrokerageEnabled(true)
                 .setPrice(2000)
                 .setFrozenPrice(3000);
         return success(respVO);
     }
 
+    @PutMapping("/bind")
+    @Operation(summary = "绑定推广员")
+    @PreAuthenticated
+    public CommonResult<Boolean> bindBrokerageUser(@Valid @RequestBody AppBrokerageUserBindReqVO reqVO) {
+        return success(brokerageUserService.bindBrokerageUser(getLoginUserId(), reqVO.getBindUserId(), false));
+    }
+
     // TODO 芋艿:临时 mock =>
     @GetMapping("/get-summary")
     @Operation(summary = "获得个人分销统计")
@@ -49,7 +57,7 @@ public class AppBrokerageUserController {
     public CommonResult<AppBrokerageUserMySummaryRespVO> getBrokerageUserSummary() {
         AppBrokerageUserMySummaryRespVO respVO = new AppBrokerageUserMySummaryRespVO()
                 .setYesterdayPrice(1)
-                .setPrice(2)
+                .setBrokeragePrice(2)
                 .setFrozenPrice(3)
                 .setWithdrawPrice(4)
                 .setFirstBrokerageUserCount(166)
@@ -84,16 +92,16 @@ public class AppBrokerageUserController {
     public CommonResult<PageResult<AppBrokerageUserRankByPriceRespVO>> getBrokerageUserChildSummaryPageByPrice(AppBrokerageUserRankPageReqVO pageReqVO) {
         AppBrokerageUserRankByPriceRespVO vo1 = new AppBrokerageUserRankByPriceRespVO()
                 .setId(1L).setNickname("芋1**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
-                .setPrice(10);
+                .setBrokeragePrice(10);
         AppBrokerageUserRankByPriceRespVO vo2 = new AppBrokerageUserRankByPriceRespVO()
                 .setId(2L).setNickname("芋2**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
-                .setPrice(6);
+                .setBrokeragePrice(6);
         AppBrokerageUserRankByPriceRespVO vo3 = new AppBrokerageUserRankByPriceRespVO()
                 .setId(3L).setNickname("芋3**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
-                .setPrice(4);
+                .setBrokeragePrice(4);
         AppBrokerageUserRankByPriceRespVO vo4 = new AppBrokerageUserRankByPriceRespVO()
                 .setId(3L).setNickname("芋3**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
-                .setPrice(4);
+                .setBrokeragePrice(4);
         return success(new PageResult<>(asList(vo1, vo2, vo3, vo4), 10L));
     }
 
@@ -105,11 +113,11 @@ public class AppBrokerageUserController {
             AppBrokerageUserChildSummaryPageReqVO pageReqVO) {
         AppBrokerageUserChildSummaryRespVO vo1 = new AppBrokerageUserChildSummaryRespVO()
                 .setId(1L).setNickname("芋1**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
-                .setPrice(10).setPrice(20).setBrokerageOrderCount(30)
+                .setBrokeragePrice(10).setBrokeragePrice(20).setBrokerageOrderCount(30)
                 .setBrokerageTime(LocalDateTime.now());
         AppBrokerageUserChildSummaryRespVO vo2 = new AppBrokerageUserChildSummaryRespVO()
                 .setId(1L).setNickname("芋2**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
-                .setPrice(20).setPrice(30).setBrokerageOrderCount(40)
+                .setBrokeragePrice(20).setBrokeragePrice(30).setBrokerageOrderCount(40)
                 .setBrokerageTime(LocalDateTime.now());
         return success(new PageResult<>(asList(vo1, vo2), 10L));
     }
@@ -118,15 +126,9 @@ public class AppBrokerageUserController {
     @GetMapping("/get-rank-by-price")
     @Operation(summary = "获得分销用户排行(基于佣金)")
     @Parameter(name = "times", description = "时间段", required = true)
-    public CommonResult<Integer> getBrokerageUserRankByPrice(
+    public CommonResult<Integer> bindBrokerageUser(
             @RequestParam("times") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) LocalDateTime[] times) {
         return success(1);
     }
 
-    @PutMapping("/bind-user")
-    @Operation(summary = "绑定推广员")
-    public CommonResult<Boolean> getBrokerageUserRankByPrice(@Valid AppBrokerageUserBindReqVO reqVO) {
-        return success(brokerageUserService.bindUser(getLoginUserId(), reqVO.getBindUserId(), false));
-    }
-
 }

+ 19 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/record/AppBrokerageProductPriceRespVO.java

@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "用户 App - 商品的分销金额 Response VO")
+@Data
+public class AppBrokerageProductPriceRespVO {
+
+    @Schema(description = "是否开启", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private Boolean enabled;
+
+    @Schema(description = "分销最小金额,单位:分", example = "100")
+    private Integer brokerageMinPrice;
+
+    @Schema(description = "分销最大金额,单位:分", example = "100")
+    private Integer brokerageMaxPrice;
+
+}

+ 1 - 1
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserChildSummaryRespVO.java

@@ -19,7 +19,7 @@ public class AppBrokerageUserChildSummaryRespVO {
     private String avatar;
 
     @Schema(description = "佣金金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
-    private Integer price;
+    private Integer brokeragePrice;
 
     @Schema(description = "分销订单数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
     private Integer brokerageOrderCount;

+ 1 - 1
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserMySummaryRespVO.java

@@ -14,7 +14,7 @@ public class AppBrokerageUserMySummaryRespVO {
     private Integer withdrawPrice;
 
     @Schema(description = "可用的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "2408")
-    private Integer price;
+    private Integer brokeragePrice;
 
     @Schema(description = "冻结的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "234")
     private Integer frozenPrice;

+ 1 - 1
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserRankByPriceRespVO.java

@@ -17,6 +17,6 @@ public class AppBrokerageUserRankByPriceRespVO {
     private String avatar;
 
     @Schema(description = "佣金金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
-    private Integer price;
+    private Integer brokeragePrice;
 
 }

+ 3 - 0
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/user/AppBrokerageUserRespVO.java

@@ -7,6 +7,9 @@ import lombok.Data;
 @Data
 public class AppBrokerageUserRespVO {
 
+    @Schema(description = "是否有分销资格", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+    private Boolean brokerageEnabled;
+
     @Schema(description = "可用的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "2408")
     private Integer price;
 

+ 1 - 1
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/brokerage/record/BrokerageRecordConvert.java

@@ -41,7 +41,7 @@ public interface BrokerageRecordConvert {
                 : BrokerageRecordStatusEnum.SETTLEMENT.getStatus();
         return new BrokerageRecordDO().setUserId(user.getId())
                 .setBizType(bizType.getType()).setBizId(bizId)
-                .setPrice(brokeragePrice).setTotalPrice(user.getPrice())
+                .setPrice(brokeragePrice).setTotalPrice(user.getBrokeragePrice())
                 .setTitle(title)
                 .setDescription(StrUtil.format(bizType.getDescription(), String.valueOf(brokeragePrice / 100.0)))
                 .setStatus(status).setFrozenDays(brokerageFrozenDays).setUnfreezeTime(unfreezeTime);

+ 2 - 2
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/brokerage/user/BrokerageUserDO.java

@@ -43,7 +43,7 @@ public class BrokerageUserDO extends BaseDO {
     private LocalDateTime bindUserTime;
 
     /**
-     * 推广资格
+     * 是否有分销资格
      */
     private Boolean brokerageEnabled;
     /**
@@ -54,7 +54,7 @@ public class BrokerageUserDO extends BaseDO {
     /**
      * 可用佣金
      */
-    private Integer price;
+    private Integer brokeragePrice;
     /**
      * 冻结佣金
      */

+ 1 - 1
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/user/BrokerageUserService.java

@@ -103,6 +103,6 @@ public interface BrokerageUserService {
      * @param isNewUser  是否为新用户
      * @return 是否绑定
      */
-    boolean bindUser(Long userId, Long bindUserId, Boolean isNewUser);
+    boolean bindBrokerageUser(Long userId, Long bindUserId, Boolean isNewUser);
 
 }

+ 2 - 2
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/user/BrokerageUserServiceImpl.java

@@ -135,7 +135,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
 
     // TODO @疯狂:因为现在 user 会存在使用验证码直接注册,所以 isNewUser 不太好传递;我们是不是可以约定绑定的时间,createTime 在 30 秒内,就认为新用户;
     @Override
-    public boolean bindUser(Long userId, Long bindUserId, Boolean isNewUser) {
+    public boolean bindBrokerageUser(Long userId, Long bindUserId, Boolean isNewUser) {
         // TODO @疯狂:userId 为空,搞到参数校验里哇;
         if (userId == null) {
             throw exception(0);
@@ -146,7 +146,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
         BrokerageUserDO brokerageUser = brokerageUserMapper.selectById(userId);
         if (brokerageUser == null) { // 分销用户不存在的情况:1. 新注册;2. 旧数据;3. 分销功能关闭后又打开
             isNewBrokerageUser = true;
-            brokerageUser = new BrokerageUserDO().setId(userId).setBrokerageEnabled(false).setPrice(0).setFrozenPrice(0);
+            brokerageUser = new BrokerageUserDO().setId(userId).setBrokerageEnabled(false).setBrokeragePrice(0).setFrozenPrice(0);
         }
 
         // 2.1 校验能否绑定

+ 11 - 1
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http

@@ -28,6 +28,17 @@ tenant-id: {{appTenentId}}
   "code": 9999
 }
 
+### 请求 /social-login 接口 => 成功
+POST {{appApi}}/member/auth/social-login
+Content-Type: application/json
+tenant-id: {{appTenentId}}
+
+{
+  "type": 34,
+  "code": "0e1oc9000CTjFQ1oim200bhtb61oc90g",
+  "state": "default"
+}
+
 ### 请求 /weixin-mini-app-login 接口 => 成功
 POST {{appApi}}/member/auth/weixin-mini-app-login
 Content-Type: application/json
@@ -38,7 +49,6 @@ tenant-id: {{appTenentId}}
   "loginCode": "001frTkl21JUf94VGxol2hSlff1frTkR"
 }
 
-
 ### 请求 /logout 接口 => 成功
 POST {{appApi}}/member/auth/logout
 Content-Type: application/json

+ 8 - 0
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginRespVO.java

@@ -27,4 +27,12 @@ public class AppAuthLoginRespVO {
     @Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED)
     private LocalDateTime expiresTime;
 
+    /**
+     * 仅社交登录、社交绑定时会返回
+     *
+     * 为什么需要返回?微信公众号、微信小程序支付需要传递 openid 给支付接口
+     */
+    @Schema(description = "社交用户 openid", example = "qq768")
+    private String openid;
+
 }

+ 1 - 1
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java

@@ -25,7 +25,7 @@ public interface AuthConvert {
     SmsCodeUseReqDTO convert(AppMemberUserResetPasswordReqVO reqVO, SmsSceneEnum scene, String usedIp);
     SmsCodeUseReqDTO convert(AppAuthSmsLoginReqVO reqVO, Integer scene, String usedIp);
 
-    AppAuthLoginRespVO convert(OAuth2AccessTokenRespDTO bean);
+    AppAuthLoginRespVO convert(OAuth2AccessTokenRespDTO bean, String openid);
 
     SmsCodeValidateReqDTO convert(AppAuthSmsValidateReqVO bean);
 

+ 17 - 13
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java

@@ -20,6 +20,7 @@ import cn.iocoder.yudao.module.system.api.oauth2.dto.OAuth2AccessTokenRespDTO;
 import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
 import cn.iocoder.yudao.module.system.api.social.SocialUserApi;
 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
+import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
 import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
 import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
 import cn.iocoder.yudao.module.system.enums.oauth2.OAuth2ClientConstants;
@@ -65,13 +66,14 @@ public class MemberAuthServiceImpl implements MemberAuthService {
         MemberUserDO user = login0(reqVO.getMobile(), reqVO.getPassword());
 
         // 如果 socialType 非空,说明需要绑定社交用户
+        String openid = null;
         if (reqVO.getSocialType() != null) {
-            socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
+            openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
                     reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState()));
         }
 
         // 创建 Token 令牌,记录登录日志
-        return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE);
+        return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE, openid);
     }
 
     @Override
@@ -86,32 +88,33 @@ public class MemberAuthServiceImpl implements MemberAuthService {
         Assert.notNull(user, "获取用户失败,结果为空");
 
         // 如果 socialType 非空,说明需要绑定社交用户
+        String openid = null;
         if (reqVO.getSocialType() != null) {
-            socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
+            openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
                     reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState()));
         }
 
         // 创建 Token 令牌,记录登录日志
-        return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_SMS);
+        return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_SMS, openid);
     }
 
     @Override
     public AppAuthLoginRespVO socialLogin(AppAuthSocialLoginReqVO reqVO) {
         // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
-        Long userId = socialUserApi.getBindUserId(UserTypeEnum.MEMBER.getValue(), reqVO.getType(),
+        SocialUserRespDTO socialUser = socialUserApi.getSocialUser(UserTypeEnum.MEMBER.getValue(), reqVO.getType(),
                 reqVO.getCode(), reqVO.getState());
-        if (userId == null) {
+        if (socialUser == null) {
             throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
         }
 
         // 自动登录
-        MemberUserDO user = userService.getUser(userId);
+        MemberUserDO user = userService.getUser(socialUser.getUserId());
         if (user == null) {
             throw exception(USER_NOT_EXISTS);
         }
 
         // 创建 Token 令牌,记录登录日志
-        return createTokenAfterLoginSuccess(user, user.getMobile(), LoginLogTypeEnum.LOGIN_SOCIAL);
+        return createTokenAfterLoginSuccess(user, user.getMobile(), LoginLogTypeEnum.LOGIN_SOCIAL, socialUser.getOpenid());
     }
 
     @Override
@@ -129,14 +132,15 @@ public class MemberAuthServiceImpl implements MemberAuthService {
         Assert.notNull(user, "获取用户失败,结果为空");
 
         // 绑定社交用户
-        socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
+        String openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
                 SocialTypeEnum.WECHAT_MINI_APP.getType(), reqVO.getLoginCode(), ""));
 
         // 创建 Token 令牌,记录登录日志
-        return createTokenAfterLoginSuccess(user, user.getMobile(), LoginLogTypeEnum.LOGIN_SOCIAL);
+        return createTokenAfterLoginSuccess(user, user.getMobile(), LoginLogTypeEnum.LOGIN_SOCIAL, openid);
     }
 
-    private AppAuthLoginRespVO createTokenAfterLoginSuccess(MemberUserDO user, String mobile, LoginLogTypeEnum logType) {
+    private AppAuthLoginRespVO createTokenAfterLoginSuccess(MemberUserDO user, String mobile,
+                                                            LoginLogTypeEnum logType, String openid) {
         // 插入登陆日志
         createLoginLog(user.getId(), mobile, logType, LoginResultEnum.SUCCESS);
         // 创建 Token 令牌
@@ -144,7 +148,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
                 .setUserId(user.getId()).setUserType(getUserType().getValue())
                 .setClientId(OAuth2ClientConstants.CLIENT_ID_DEFAULT));
         // 构建返回结果
-        return AuthConvert.INSTANCE.convert(accessTokenRespDTO);
+        return AuthConvert.INSTANCE.convert(accessTokenRespDTO, openid);
     }
 
     @Override
@@ -231,7 +235,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
     public AppAuthLoginRespVO refreshToken(String refreshToken) {
         OAuth2AccessTokenRespDTO accessTokenDO = oauth2TokenApi.refreshAccessToken(refreshToken,
                 OAuth2ClientConstants.CLIENT_ID_DEFAULT);
-        return AuthConvert.INSTANCE.convert(accessTokenDO);
+        return AuthConvert.INSTANCE.convert(accessTokenDO, null);
     }
 
     private void createLogoutLog(Long userId) {

+ 2 - 2
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/wallet/vo/wallet/AppPayWalletRespVO.java

@@ -11,9 +11,9 @@ public class AppPayWalletRespVO {
     private Integer balance;
 
     @Schema(description = "累计支出, 单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
-    private Long totalExpense;
+    private Integer totalExpense;
 
     @Schema(description = "累计充值, 单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000")
-    private Long totalRecharge;
+    private Integer totalRecharge;
 
 }

+ 4 - 0
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/wallet/PayWalletTransactionConvert.java

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.pay.convert.wallet;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionRespVO;
 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
+import cn.iocoder.yudao.module.pay.service.wallet.bo.CreateWalletTransactionBO;
 import org.mapstruct.Mapper;
 import org.mapstruct.factory.Mappers;
 
@@ -12,4 +13,7 @@ public interface PayWalletTransactionConvert {
     PayWalletTransactionConvert INSTANCE = Mappers.getMapper(PayWalletTransactionConvert.class);
 
     PageResult<AppPayWalletTransactionRespVO> convertPage(PageResult<PayWalletTransactionDO> page);
+
+    PayWalletTransactionDO convert(CreateWalletTransactionBO bean);
+
 }

+ 2 - 4
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/wallet/PayWalletDO.java

@@ -37,8 +37,6 @@ public class PayWalletDO extends BaseDO {
      */
     private Integer userType;
 
-    // TODO @jason:三个都搞 integer?应该要统一哈
-
     /**
      * 余额,单位分
      */
@@ -47,10 +45,10 @@ public class PayWalletDO extends BaseDO {
     /**
      * 累计支出,单位分
      */
-    private Long totalExpense;
+    private Integer totalExpense;
     /**
      * 累计充值,单位分
      */
-    private Long totalRecharge;
+    private Integer totalRecharge;
 
 }

+ 18 - 42
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/PayWalletMapper.java

@@ -3,8 +3,7 @@ package cn.iocoder.yudao.module.pay.dal.mysql.wallet;
 
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
-import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import org.apache.ibatis.annotations.Mapper;
 
 @Mapper
@@ -15,56 +14,33 @@ public interface PayWalletMapper extends BaseMapperX<PayWalletDO> {
                 PayWalletDO::getUserType, userType);
     }
 
-    // TODO @jason:减少时,需要 update price -= ? where price >= ?,避免并发问题。现在基于 price 来过滤,虽然也能解决并发问题,但是冲突概率会高一点;可以看到 TradeBrokerageUserMapper 的做法;
     /**
-     * 当余额减少时候更新
+     * 当消费退款时候, 更新钱包
      *
-     * @param bizType 业务类型
-     * @param balance 当前余额
-     * @param totalRecharge 当前累计充值
-     * @param totalExpense 当前累计支出
-     * @param price 支出的金额
+     * @param price 消费金额
      * @param id 钱包 id
      */
-    default int updateWhenDecBalance(PayWalletBizTypeEnum bizType, Integer balance, Long totalRecharge,
-                                     Long totalExpense, Integer price, Long id) {
-        // TODO @jason:这种偏判断的,最红放在 service 层;mapper 可以写多个方法;
-        PayWalletDO updateDO = new PayWalletDO().setBalance(balance - price);
-        if(bizType == PayWalletBizTypeEnum.PAYMENT){
-            updateDO.setTotalExpense(totalExpense + price);
-        }
-        if (bizType == PayWalletBizTypeEnum.RECHARGE_REFUND) {
-            updateDO.setTotalRecharge(totalRecharge - price);
-        }
-        return update(updateDO,
-                new LambdaQueryWrapper<PayWalletDO>().eq(PayWalletDO::getId, id)
-                        .eq(PayWalletDO::getBalance, balance)
-                        .ge(PayWalletDO::getBalance, price));
+    default int updateWhenConsumptionRefund(Integer price, Long id){
+        LambdaUpdateWrapper<PayWalletDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<PayWalletDO>()
+                .setSql(" balance = balance + " + price
+                        + ", total_expense = total_expense - " + price)
+                .eq(PayWalletDO::getId, id);
+        return update(null, lambdaUpdateWrapper);
     }
 
-    // TODO @jason:类似上面的修改建议哈;
     /**
-     * 当余额增加时候更新
+     * 当消费时候, 更新钱包
      *
-     * @param bizType  业务类型
-     * @param balance  当前余额
-     * @param totalRecharge 当前累计充值
-     * @param totalExpense 当前累计支出
-     * @param price 金额
+     * @param price 消费金额
      * @param id 钱包 id
      */
-    default int updateWhenIncBalance(PayWalletBizTypeEnum bizType, Integer balance, Long totalRecharge,
-                                     Long totalExpense, Integer price, Long id) {
-        PayWalletDO updateDO = new PayWalletDO().setBalance(balance + price);
-        if (bizType == PayWalletBizTypeEnum.PAYMENT_REFUND) {
-            updateDO.setTotalExpense(totalExpense - price);
-        }
-        if (bizType == PayWalletBizTypeEnum.RECHARGE) {
-            updateDO.setTotalExpense(totalRecharge + price);
-        }
-        return update(updateDO,
-                new LambdaQueryWrapper<PayWalletDO>().eq(PayWalletDO::getId, id)
-                        .eq(PayWalletDO::getBalance, balance));
+    default int updateWhenConsumption(Integer price, Long id){
+        LambdaUpdateWrapper<PayWalletDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<PayWalletDO>()
+                .setSql(" balance = balance - " + price
+                        + ", total_expense = total_expense + " + price)
+                .eq(PayWalletDO::getId, id)
+                .ge(PayWalletDO::getBalance, price); // cas 逻辑
+        return update(null, lambdaUpdateWrapper);
     }
 }
 

+ 6 - 7
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/wallet/WalletPayClient.java

@@ -62,13 +62,12 @@ public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> {
     @Override
     protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
         try {
-            // TODO @jason:直接 getLong 和 getInt 会不会更简洁哈
-            String userId = MapUtil.getStr(reqDTO.getChannelExtras(), USER_ID_KEY);
-            String userType = MapUtil.getStr(reqDTO.getChannelExtras(), USER_TYPE_KEY);
-            Assert.notEmpty(userId, "用户 id 不能为空");
-            Assert.notEmpty(userType, "用户类型不能为空");
-            PayWalletTransactionDO transaction = wallService.orderPay(Long.valueOf(userId), Integer.valueOf(userType),
-                    reqDTO.getOutTradeNo(), reqDTO.getPrice());
+            Long userId = MapUtil.getLong(reqDTO.getChannelExtras(), USER_ID_KEY);
+            Integer userType = MapUtil.getInt(reqDTO.getChannelExtras(), USER_TYPE_KEY);
+            Assert.notNull(userId, "用户 id 不能为空");
+            Assert.notNull(userType, "用户类型不能为空");
+            PayWalletTransactionDO transaction = wallService.orderPay(userId, userType, reqDTO.getOutTradeNo(),
+                    reqDTO.getPrice());
             return PayOrderRespDTO.successOf(transaction.getNo(), transaction.getCreator(),
                     transaction.getCreateTime(),
                     reqDTO.getOutTradeNo(), transaction);

+ 36 - 65
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletServiceImpl.java

@@ -6,10 +6,10 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
 import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletMapper;
-import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO;
 import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
+import cn.iocoder.yudao.module.pay.service.wallet.bo.CreateWalletTransactionBO;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
@@ -18,7 +18,6 @@ import org.springframework.transaction.annotation.Transactional;
 import javax.annotation.Resource;
 import java.time.LocalDateTime;
 
-import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.TOO_MANY_REQUESTS;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
 import static cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum.PAYMENT;
@@ -33,20 +32,8 @@ import static cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum.PAYM
 @Slf4j
 public class PayWalletServiceImpl implements  PayWalletService {
 
-    /**
-     * 余额支付的 no 前缀
-     */
-    private static final String WALLET_PAY_NO_PREFIX = "WP";
-    /**
-     * 余额退款的 no 前缀
-     */
-    private static final String WALLET_REFUND_NO_PREFIX = "WR";
-
     @Resource
     private PayWalletMapper walletMapper;
-    @Resource
-    private PayNoRedisDAO noRedisDAO;
-
     @Resource
     private PayWalletTransactionService walletTransactionService;
     @Resource
@@ -61,7 +48,7 @@ public class PayWalletServiceImpl implements  PayWalletService {
         PayWalletDO wallet = walletMapper.selectByUserIdAndType(userId, userType);
         if (wallet == null) {
             wallet = new PayWalletDO().setUserId(userId).setUserType(userType)
-                    .setBalance(0).setTotalExpense(0L).setTotalRecharge(0L);
+                    .setBalance(0).setTotalExpense(0).setTotalRecharge(0);
             wallet.setCreateTime(LocalDateTime.now());
             walletMapper.insert(wallet);
         }
@@ -125,70 +112,54 @@ public class PayWalletServiceImpl implements  PayWalletService {
     @Override
     public PayWalletTransactionDO reduceWalletBalance(Long userId, Integer userType,
                                                       Long bizId, PayWalletBizTypeEnum bizType, Integer price) {
-        // 1.1 获取钱包
+        // 1. 获取钱包
         PayWalletDO payWallet = getOrCreateWallet(userId, userType);
-        // 1.2 判断余额是否足够
-        int afterBalance = payWallet.getBalance() - price;
-        if (afterBalance < 0) {
-            throw exception(WALLET_BALANCE_NOT_ENOUGH);
-        }
 
-        // TODO jason:建议基于 where price >= 来做哈;然后抛出 WALLET_BALANCE_NOT_ENOUGH
         // 2.1 扣除余额
-        int number = walletMapper.updateWhenDecBalance(bizType, payWallet.getBalance(),
-                payWallet.getTotalRecharge(), payWallet.getTotalExpense(), price, payWallet.getId());
-        if (number == 0) {
-            throw exception(TOO_MANY_REQUESTS);
+        int updateCounts = 0 ;
+        switch (bizType) {
+            case PAYMENT: {
+                updateCounts = walletMapper.updateWhenConsumption(price, payWallet.getId());
+                break;
+            }
+            case RECHARGE_REFUND: {
+                // TODO
+                break;
+            }
+        }
+        if (updateCounts == 0) {
+            throw exception(WALLET_BALANCE_NOT_ENOUGH);
         }
-
         // 2.2 生成钱包流水
-        // TODO @jason:walletNo 交给 payWalletTransactionService 自己生成哈;
-        String walletNo = generateWalletNo(bizType);
-        PayWalletTransactionDO walletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId())
-                .setNo(walletNo).setPrice(-price).setBalance(afterBalance)
-                .setBizId(String.valueOf(bizId)).setBizType(bizType.getType()).setTitle(bizType.getDescription());
-        // TODO @jason:是不是可以 createWalletTransaction 搞个 bo 参数,然后 PayWalletTransactionDO 交回给 walletTransactionService 更好;然后把参数简化下
-        walletTransactionService.createWalletTransaction(walletTransaction);
-        return walletTransaction;
+        Integer afterBalance = payWallet.getBalance() - price;
+        CreateWalletTransactionBO bo = new CreateWalletTransactionBO().setWalletId(payWallet.getId())
+                .setPrice(-price).setBalance(afterBalance).setBizId(String.valueOf(bizId))
+                .setBizType(bizType.getType()).setTitle(bizType.getDescription());
+        return walletTransactionService.createWalletTransaction(bo);
     }
 
     @Override
     public PayWalletTransactionDO addWalletBalance(Long userId, Integer userType,
                                                    Long bizId, PayWalletBizTypeEnum bizType, Integer price) {
-        // 1.1 获取钱包
+        // 1. 获取钱包
         PayWalletDO payWallet = getOrCreateWallet(userId, userType);
-
-        // 2.1 增加余额
-        // TODO @jason:类似上面的思路哈;
-        int number = walletMapper.updateWhenIncBalance(bizType, payWallet.getBalance(), payWallet.getTotalRecharge(),
-                payWallet.getTotalExpense(), price, payWallet.getId());
-        if (number == 0) {
-            throw exception(TOO_MANY_REQUESTS);
-        }
-
-        // 2.2 生成钱包流水
-        String walletNo = generateWalletNo(bizType);
-        PayWalletTransactionDO newWalletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId())
-                .setNo(walletNo).setPrice(price).setBalance(payWallet.getBalance()+price)
-                .setBizId(String.valueOf(bizId)).setBizType(bizType.getType())
-                .setTitle(bizType.getDescription());
-        walletTransactionService.createWalletTransaction(newWalletTransaction);
-        return newWalletTransaction;
-    }
-
-    private String generateWalletNo(PayWalletBizTypeEnum bizType) {
-        // TODO @jason:对于余额来说,是不是直接 W+序号就行了,它其实不关注业务;;;不然就耦合啦
-        String no = "";
-        switch(bizType){
-            case PAYMENT:
-                no = noRedisDAO.generate(WALLET_PAY_NO_PREFIX);
+        switch (bizType) {
+            case PAYMENT_REFUND: {
+                // 更新退款
+                walletMapper.updateWhenConsumptionRefund(price, payWallet.getId());
                 break;
-            case PAYMENT_REFUND:
-                no = noRedisDAO.generate(WALLET_REFUND_NO_PREFIX);
+            }
+            case RECHARGE: {
+                //TODO
                 break;
-            default :
+            }
         }
-        return no;
+
+        // 2. 生成钱包流水
+        CreateWalletTransactionBO bo = new CreateWalletTransactionBO().setWalletId(payWallet.getId())
+                .setPrice(price).setBalance(payWallet.getBalance()+price).setBizId(String.valueOf(bizId))
+                .setBizType(bizType.getType()).setTitle(bizType.getDescription());
+        return walletTransactionService.createWalletTransaction(bo);
     }
 
 }

+ 7 - 3
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletTransactionService.java

@@ -4,6 +4,9 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionPageReqVO;
 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
 import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
+import cn.iocoder.yudao.module.pay.service.wallet.bo.CreateWalletTransactionBO;
+
+import javax.validation.Valid;
 
 /**
  * 钱包余额流水 Service 接口
@@ -25,10 +28,10 @@ public interface PayWalletTransactionService {
     /**
      * 新增钱包余额流水
      *
-     * @param payWalletTransaction 余额流水
-     * @return id
+     * @param bo 创建钱包流水 bo
+     * @return 新建的钱包 do
      */
-    Long createWalletTransaction(PayWalletTransactionDO payWalletTransaction);
+    PayWalletTransactionDO createWalletTransaction(@Valid CreateWalletTransactionBO bo);
 
     /**
      * 根据 no,获取钱包余流水
@@ -45,4 +48,5 @@ public interface PayWalletTransactionService {
      * @return 钱包流水
      */
     PayWalletTransactionDO getWalletTransaction(String bizId, PayWalletBizTypeEnum type);
+    
 }

+ 16 - 3
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletTransactionServiceImpl.java

@@ -2,10 +2,13 @@ package cn.iocoder.yudao.module.pay.service.wallet;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionPageReqVO;
+import cn.iocoder.yudao.module.pay.convert.wallet.PayWalletTransactionConvert;
 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
 import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletTransactionMapper;
+import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO;
 import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
+import cn.iocoder.yudao.module.pay.service.wallet.bo.CreateWalletTransactionBO;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
@@ -20,10 +23,17 @@ import javax.annotation.Resource;
 @Slf4j
 public class PayWalletTransactionServiceImpl implements PayWalletTransactionService {
 
+    /**
+     * 钱包流水的 no 前缀
+     */
+    private static final String WALLET_NO_PREFIX = "W";
+
     @Resource
     private PayWalletService payWalletService;
     @Resource
     private PayWalletTransactionMapper payWalletTransactionMapper;
+    @Resource
+    private PayNoRedisDAO noRedisDAO;
 
     @Override
     public PageResult<PayWalletTransactionDO> getWalletTransactionPage(Long userId, Integer userType,
@@ -33,9 +43,11 @@ public class PayWalletTransactionServiceImpl implements PayWalletTransactionServ
     }
 
     @Override
-    public Long createWalletTransaction(PayWalletTransactionDO payWalletTransaction) {
-         payWalletTransactionMapper.insert(payWalletTransaction);
-         return payWalletTransaction.getId();
+    public PayWalletTransactionDO createWalletTransaction(CreateWalletTransactionBO bo) {
+        PayWalletTransactionDO transaction = PayWalletTransactionConvert.INSTANCE.convert(bo)
+                .setNo(noRedisDAO.generate(WALLET_NO_PREFIX));
+        payWalletTransactionMapper.insert(transaction);
+        return transaction;
     }
 
     @Override
@@ -47,4 +59,5 @@ public class PayWalletTransactionServiceImpl implements PayWalletTransactionServ
     public PayWalletTransactionDO getWalletTransaction(String bizId, PayWalletBizTypeEnum type) {
         return payWalletTransactionMapper.selectByBiz(bizId, type.getType());
     }
+
 }

+ 50 - 0
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/bo/CreateWalletTransactionBO.java

@@ -0,0 +1,50 @@
+package cn.iocoder.yudao.module.pay.service.wallet.bo;
+
+import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
+import lombok.Data;
+
+/**
+ * 创建钱包流水 BO
+ *
+ * @author jason
+ */
+@Data
+public class CreateWalletTransactionBO {
+
+    // TODO @jason:bo 的话,最好加个参数校验哈;
+
+    /**
+     * 钱包编号
+     *
+     */
+    private Long walletId;
+
+    /**
+     * 交易金额,单位分
+     *
+     * 正值表示余额增加,负值表示余额减少
+     */
+    private Integer price;
+
+    /**
+     * 交易后余额,单位分
+     */
+    private Integer balance;
+
+    /**
+     * 关联业务分类
+     *
+     * 枚举 {@link PayWalletBizTypeEnum#getType()}
+     */
+    private Integer bizType;
+
+    /**
+     * 关联业务编号
+     */
+    private String bizId;
+
+    /**
+     * 流水说明
+     */
+    private String title;
+}

+ 8 - 5
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApi.java

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.api.social;
 
 import cn.iocoder.yudao.framework.common.exception.ServiceException;
 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
+import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO;
 import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
 
@@ -27,8 +28,9 @@ public interface SocialUserApi {
      * 绑定社交用户
      *
      * @param reqDTO 绑定信息
+     * @return 社交用户 openid
      */
-    void bindSocialUser(@Valid SocialUserBindReqDTO reqDTO);
+    String bindSocialUser(@Valid SocialUserBindReqDTO reqDTO);
 
     /**
      * 取消绑定社交用户
@@ -38,16 +40,17 @@ public interface SocialUserApi {
     void unbindSocialUser(@Valid SocialUserUnbindReqDTO reqDTO);
 
     /**
-     * 获得社交用户的绑定用户编号
-     * 注意,返回的是 MemberUser 或者 AdminUser 的 id 编号!
+     * 获得社交用户
+     *
      * 在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常
      *
      * @param userType 用户类型
      * @param type 社交平台的类型
      * @param code 授权码
      * @param state state
-     * @return 绑定用户编号
+     * @return 社交用户
      */
-    Long getBindUserId(Integer userType, Integer type, String code, String state);
+    SocialUserRespDTO getSocialUser(Integer userType, Integer type,
+                                    String code, String state);
 
 }

+ 27 - 0
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserRespDTO.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.system.api.social.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 社交用户 Response DTO
+ *
+ * @author 芋道源码
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class SocialUserRespDTO {
+
+    /**
+     * 社交用户 openid
+     */
+    private String openid;
+
+    /**
+     * 关联的用户编号
+     */
+    private Long userId;
+
+}

+ 5 - 4
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApiImpl.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.system.api.social;
 
 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
+import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO;
 import cn.iocoder.yudao.module.system.service.social.SocialUserService;
 import org.springframework.stereotype.Service;
@@ -26,8 +27,8 @@ public class SocialUserApiImpl implements SocialUserApi {
     }
 
     @Override
-    public void bindSocialUser(SocialUserBindReqDTO reqDTO) {
-        socialUserService.bindSocialUser(reqDTO);
+    public String bindSocialUser(SocialUserBindReqDTO reqDTO) {
+        return socialUserService.bindSocialUser(reqDTO);
     }
 
     @Override
@@ -37,8 +38,8 @@ public class SocialUserApiImpl implements SocialUserApi {
     }
 
     @Override
-    public Long getBindUserId(Integer userType, Integer type, String code, String state) {
-       return socialUserService.getBindUserId(userType, type, code, state);
+    public SocialUserRespDTO getSocialUser(Integer userType, Integer type, String code, String state) {
+       return socialUserService.getSocialUser(userType, type, code, state);
     }
 
 }

+ 4 - 3
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java

@@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
 import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO;
 import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
+import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*;
 import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
 import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
@@ -155,14 +156,14 @@ public class AdminAuthServiceImpl implements AdminAuthService {
     @Override
     public AuthLoginRespVO socialLogin(AuthSocialLoginReqVO reqVO) {
         // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
-        Long userId = socialUserService.getBindUserId(UserTypeEnum.ADMIN.getValue(), reqVO.getType(),
+        SocialUserRespDTO socialUser = socialUserService.getSocialUser(UserTypeEnum.ADMIN.getValue(), reqVO.getType(),
                 reqVO.getCode(), reqVO.getState());
-        if (userId == null) {
+        if (socialUser == null) {
             throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
         }
 
         // 获得用户
-        AdminUserDO user = userService.getUser(userId);
+        AdminUserDO user = userService.getUser(socialUser.getUserId());
         if (user == null) {
             throw exception(USER_NOT_EXISTS);
         }

+ 8 - 5
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserService.java

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.service.social;
 
 import cn.iocoder.yudao.framework.common.exception.ServiceException;
 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
+import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
 import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
 import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
 
@@ -50,8 +51,9 @@ public interface SocialUserService {
      * 绑定社交用户
      *
      * @param reqDTO 绑定信息
+     * @return 社交用户 openid
      */
-    void bindSocialUser(@Valid SocialUserBindReqDTO reqDTO);
+    String bindSocialUser(@Valid SocialUserBindReqDTO reqDTO);
 
     /**
      * 取消绑定社交用户
@@ -64,15 +66,16 @@ public interface SocialUserService {
     void unbindSocialUser(Long userId, Integer userType, Integer type, String openid);
 
     /**
-     * 获得社交用户的绑定用户编号
-     * 注意,返回的是 MemberUser 或者 AdminUser 的 id 编号!
+     * 获得社交用户
+     *
      * 在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常
      *
      * @param userType 用户类型
      * @param type 社交平台的类型
      * @param code 授权码
      * @param state state
-     * @return 绑定用户编号
+     * @return 社交用户
      */
-    Long getBindUserId(Integer userType, Integer type, String code, String state);
+    SocialUserRespDTO getSocialUser(Integer userType, Integer type, String code, String state);
+
 }

+ 5 - 3
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java

@@ -5,6 +5,7 @@ import cn.hutool.core.lang.Assert;
 import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
 import cn.iocoder.yudao.framework.social.core.YudaoAuthRequestFactory;
 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
+import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
 import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
 import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper;
@@ -98,7 +99,7 @@ public class SocialUserServiceImpl implements SocialUserService {
 
     @Override
     @Transactional
-    public void bindSocialUser(SocialUserBindReqDTO reqDTO) {
+    public String bindSocialUser(SocialUserBindReqDTO reqDTO) {
         // 获得社交用户
         SocialUserDO socialUser = authSocialUser(reqDTO.getType(), reqDTO.getCode(), reqDTO.getState());
         Assert.notNull(socialUser, "社交用户不能为空");
@@ -115,6 +116,7 @@ public class SocialUserServiceImpl implements SocialUserService {
                 .userId(reqDTO.getUserId()).userType(reqDTO.getUserType())
                 .socialUserId(socialUser.getId()).socialType(socialUser.getType()).build();
         socialUserBindMapper.insert(socialUserBind);
+        return socialUser.getOpenid();
     }
 
     @Override
@@ -130,7 +132,7 @@ public class SocialUserServiceImpl implements SocialUserService {
     }
 
     @Override
-    public Long getBindUserId(Integer userType, Integer type, String code, String state) {
+    public SocialUserRespDTO getSocialUser(Integer userType, Integer type, String code, String state) {
         // 获得社交用户
         SocialUserDO socialUser = authSocialUser(type, code, state);
         Assert.notNull(socialUser, "社交用户不能为空");
@@ -141,7 +143,7 @@ public class SocialUserServiceImpl implements SocialUserService {
         if (socialUserBind == null) {
             throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
         }
-        return socialUserBind.getUserId();
+        return new SocialUserRespDTO(socialUser.getOpenid(), socialUserBind.getUserId());
     }
 
     /**

+ 3 - 2
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java

@@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
 import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
+import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*;
 import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
@@ -235,8 +236,8 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest {
         AuthSocialLoginReqVO reqVO = randomPojo(AuthSocialLoginReqVO.class);
         // mock 方法(绑定的用户编号)
         Long userId = 1L;
-        when(socialUserService.getBindUserId(eq(UserTypeEnum.ADMIN.getValue()), eq(reqVO.getType()),
-                eq(reqVO.getCode()), eq(reqVO.getState()))).thenReturn(userId);
+        when(socialUserService.getSocialUser(eq(UserTypeEnum.ADMIN.getValue()), eq(reqVO.getType()),
+                eq(reqVO.getCode()), eq(reqVO.getState()))).thenReturn(new SocialUserRespDTO(randomString(), userId));
         // mock(用户)
         AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(userId));
         when(userService.getUser(eq(userId))).thenReturn(user);

+ 10 - 7
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImplTest.java

@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import cn.iocoder.yudao.framework.social.core.YudaoAuthRequestFactory;
 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
+import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
 import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
 import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper;
@@ -195,10 +196,11 @@ public class SocialUserServiceImplTest extends BaseDbUnitTest {
                 .setSocialType(SocialTypeEnum.GITEE.getType()).setSocialUserId(socialUser.getId()));
 
         // 调用
-        socialUserService.bindSocialUser(reqDTO);
+        String openid = socialUserService.bindSocialUser(reqDTO);
         // 断言
         List<SocialUserBindDO> socialUserBinds = socialUserBindMapper.selectList();
         assertEquals(1, socialUserBinds.size());
+        assertEquals(socialUser.getOpenid(), openid);
     }
 
     @Test
@@ -232,25 +234,26 @@ public class SocialUserServiceImplTest extends BaseDbUnitTest {
     }
 
     @Test
-    public void testGetBindUserId() {
+    public void testGetSocialUser() {
         // 准备参数
         Integer userType = UserTypeEnum.ADMIN.getValue();
         Integer type = SocialTypeEnum.GITEE.getType();
         String code = "tudou";
         String state = "yuanma";
         // mock 社交用户
-        SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(type).setCode(code).setState(state);
-        socialUserMapper.insert(socialUser);
+        SocialUserDO socialUserDO = randomPojo(SocialUserDO.class).setType(type).setCode(code).setState(state);
+        socialUserMapper.insert(socialUserDO);
         // mock 社交用户的绑定
         Long userId = randomLong();
         SocialUserBindDO socialUserBind = randomPojo(SocialUserBindDO.class).setUserType(userType).setUserId(userId)
-                .setSocialType(type).setSocialUserId(socialUser.getId());
+                .setSocialType(type).setSocialUserId(socialUserDO.getId());
         socialUserBindMapper.insert(socialUserBind);
 
         // 调用
-        Long result = socialUserService.getBindUserId(userType, type, code, state);
+        SocialUserRespDTO socialUser = socialUserService.getSocialUser(userType, type, code, state);
         // 断言
-        assertEquals(userId, result);
+        assertEquals(userId, socialUser.getUserId());
+        assertEquals(socialUserDO.getOpenid(), socialUser.getOpenid());
     }
 
 }

+ 4 - 2
yudao-server/src/main/resources/application-local.yaml

@@ -173,8 +173,10 @@ wx:
       key-prefix: wx # Redis Key 的前缀
       http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台
   miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档
-    appid: wx62056c0d5e8db250
-    secret: 333ae72f41552af1e998fe1f54e1584a
+#    appid: wx62056c0d5e8db250
+#    secret: 333ae72f41552af1e998fe1f54e1584a
+    appid: wx63c280fe3248a3e7 # wenhualian的接口测试号
+    secret: 6f270509224a7ae1296bbf1c8cb97aed
     config-storage:
       type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
       key-prefix: wa # Redis Key 的前缀