Commit 3976011af0d9090160a739e72bfd0b2895e8ec6b

Authored by puhui999
2 parents 57f0ea04 40e1a879

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

Showing 36 changed files with 314 additions and 201 deletions
sql/mysql/pay_wallet.sql
... ... @@ -8,8 +8,8 @@ CREATE TABLE `pay_wallet`
8 8 `user_id` bigint NOT NULL COMMENT '用户编号',
9 9 `user_type` tinyint NOT NULL DEFAULT 0 COMMENT '用户类型',
10 10 `balance` int NOT NULL DEFAULT 0 COMMENT '余额,单位分',
11   - `total_expense` bigint NOT NULL DEFAULT 0 COMMENT '累计支出,单位分',
12   - `total_recharge` bigint NOT NULL DEFAULT 0 COMMENT '累计充值,单位分',
  11 + `total_expense` int NOT NULL DEFAULT 0 COMMENT '累计支出,单位分',
  12 + `total_recharge` int NOT NULL DEFAULT 0 COMMENT '累计充值,单位分',
13 13 `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
14 14 `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
15 15 `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
... ...
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 {
27 27  
28 28 @Override
29 29 public boolean bindUser(Long userId, Long bindUserId, Boolean isNewUser) {
30   - return brokerageUserService.bindUser(userId, bindUserId, isNewUser);
  30 + return brokerageUserService.bindBrokerageUser(userId, bindUserId, isNewUser);
31 31 }
32 32  
33 33 }
... ...
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 {
19 19 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
20 20 private LocalDateTime createTime;
21 21  
22   -
23 22 // ========== 用户信息 ==========
24 23  
25 24 @Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png")
... ... @@ -27,7 +26,6 @@ public class BrokerageUserRespVO extends BrokerageUserBaseVO {
27 26 @Schema(description = "用户昵称", example = "李四")
28 27 private String nickname;
29 28  
30   -
31 29 // ========== 推广信息 ==========
32 30  
33 31 @Schema(description = "推广用户数量(一级)", example = "20019")
... ... @@ -37,7 +35,6 @@ public class BrokerageUserRespVO extends BrokerageUserBaseVO {
37 35 @Schema(description = "推广订单金额", example = "20019")
38 36 private Integer brokerageOrderPrice;
39 37  
40   -
41 38 // ========== 提现信息 ==========
42 39  
43 40 @Schema(description = "已提现金额", example = "20019")
... ...
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;
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 5 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
  6 +import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageProductPriceRespVO;
6 7 import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageRecordPageReqVO;
7 8 import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageRecordRespVO;
8 9 import io.swagger.v3.oas.annotations.Operation;
... ... @@ -11,6 +12,7 @@ import lombok.extern.slf4j.Slf4j;
11 12 import org.springframework.validation.annotation.Validated;
12 13 import org.springframework.web.bind.annotation.GetMapping;
13 14 import org.springframework.web.bind.annotation.RequestMapping;
  15 +import org.springframework.web.bind.annotation.RequestParam;
14 16 import org.springframework.web.bind.annotation.RestController;
15 17  
16 18 import javax.validation.Valid;
... ... @@ -40,4 +42,14 @@ public class AppBrokerageRecordController {
40 42 return success(new PageResult<>(asList(vo1, vo2), 10L));
41 43 }
42 44  
  45 + @GetMapping("/get-product-brokerage-price")
  46 + @Operation(summary = "获得商品的分销金额")
  47 + public CommonResult<AppBrokerageProductPriceRespVO> getProductBrokeragePrice(@RequestParam("spuId") Long spuId) {
  48 + AppBrokerageProductPriceRespVO respVO = new AppBrokerageProductPriceRespVO();
  49 + respVO.setEnabled(true); // TODO @疯狂:需要开启分销 + 人允许分销
  50 + respVO.setBrokerageMinPrice(1);
  51 + respVO.setBrokerageMaxPrice(2);
  52 + return success(respVO);
  53 + }
  54 +
43 55 }
... ...
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 {
37 37 @PreAuthenticated
38 38 public CommonResult<AppBrokerageUserRespVO> getBrokerageUser() {
39 39 AppBrokerageUserRespVO respVO = new AppBrokerageUserRespVO()
  40 + .setBrokerageEnabled(true)
40 41 .setPrice(2000)
41 42 .setFrozenPrice(3000);
42 43 return success(respVO);
43 44 }
44 45  
  46 + @PutMapping("/bind")
  47 + @Operation(summary = "绑定推广员")
  48 + @PreAuthenticated
  49 + public CommonResult<Boolean> bindBrokerageUser(@Valid @RequestBody AppBrokerageUserBindReqVO reqVO) {
  50 + return success(brokerageUserService.bindBrokerageUser(getLoginUserId(), reqVO.getBindUserId(), false));
  51 + }
  52 +
45 53 // TODO 芋艿:临时 mock =>
46 54 @GetMapping("/get-summary")
47 55 @Operation(summary = "获得个人分销统计")
... ... @@ -49,7 +57,7 @@ public class AppBrokerageUserController {
49 57 public CommonResult<AppBrokerageUserMySummaryRespVO> getBrokerageUserSummary() {
50 58 AppBrokerageUserMySummaryRespVO respVO = new AppBrokerageUserMySummaryRespVO()
51 59 .setYesterdayPrice(1)
52   - .setPrice(2)
  60 + .setBrokeragePrice(2)
53 61 .setFrozenPrice(3)
54 62 .setWithdrawPrice(4)
55 63 .setFirstBrokerageUserCount(166)
... ... @@ -84,16 +92,16 @@ public class AppBrokerageUserController {
84 92 public CommonResult<PageResult<AppBrokerageUserRankByPriceRespVO>> getBrokerageUserChildSummaryPageByPrice(AppBrokerageUserRankPageReqVO pageReqVO) {
85 93 AppBrokerageUserRankByPriceRespVO vo1 = new AppBrokerageUserRankByPriceRespVO()
86 94 .setId(1L).setNickname("芋1**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
87   - .setPrice(10);
  95 + .setBrokeragePrice(10);
88 96 AppBrokerageUserRankByPriceRespVO vo2 = new AppBrokerageUserRankByPriceRespVO()
89 97 .setId(2L).setNickname("芋2**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
90   - .setPrice(6);
  98 + .setBrokeragePrice(6);
91 99 AppBrokerageUserRankByPriceRespVO vo3 = new AppBrokerageUserRankByPriceRespVO()
92 100 .setId(3L).setNickname("芋3**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
93   - .setPrice(4);
  101 + .setBrokeragePrice(4);
94 102 AppBrokerageUserRankByPriceRespVO vo4 = new AppBrokerageUserRankByPriceRespVO()
95 103 .setId(3L).setNickname("芋3**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
96   - .setPrice(4);
  104 + .setBrokeragePrice(4);
97 105 return success(new PageResult<>(asList(vo1, vo2, vo3, vo4), 10L));
98 106 }
99 107  
... ... @@ -105,11 +113,11 @@ public class AppBrokerageUserController {
105 113 AppBrokerageUserChildSummaryPageReqVO pageReqVO) {
106 114 AppBrokerageUserChildSummaryRespVO vo1 = new AppBrokerageUserChildSummaryRespVO()
107 115 .setId(1L).setNickname("芋1**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
108   - .setPrice(10).setPrice(20).setBrokerageOrderCount(30)
  116 + .setBrokeragePrice(10).setBrokeragePrice(20).setBrokerageOrderCount(30)
109 117 .setBrokerageTime(LocalDateTime.now());
110 118 AppBrokerageUserChildSummaryRespVO vo2 = new AppBrokerageUserChildSummaryRespVO()
111 119 .setId(1L).setNickname("芋2**艿").setAvatar("http://www.iocoder.cn/images/common/wechat_mp_2017_07_31_bak.jpg")
112   - .setPrice(20).setPrice(30).setBrokerageOrderCount(40)
  120 + .setBrokeragePrice(20).setBrokeragePrice(30).setBrokerageOrderCount(40)
113 121 .setBrokerageTime(LocalDateTime.now());
114 122 return success(new PageResult<>(asList(vo1, vo2), 10L));
115 123 }
... ... @@ -118,15 +126,9 @@ public class AppBrokerageUserController {
118 126 @GetMapping("/get-rank-by-price")
119 127 @Operation(summary = "获得分销用户排行(基于佣金)")
120 128 @Parameter(name = "times", description = "时间段", required = true)
121   - public CommonResult<Integer> getBrokerageUserRankByPrice(
  129 + public CommonResult<Integer> bindBrokerageUser(
122 130 @RequestParam("times") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) LocalDateTime[] times) {
123 131 return success(1);
124 132 }
125 133  
126   - @PutMapping("/bind-user")
127   - @Operation(summary = "绑定推广员")
128   - public CommonResult<Boolean> getBrokerageUserRankByPrice(@Valid AppBrokerageUserBindReqVO reqVO) {
129   - return success(brokerageUserService.bindUser(getLoginUserId(), reqVO.getBindUserId(), false));
130   - }
131   -
132 134 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/brokerage/vo/record/AppBrokerageProductPriceRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "用户 App - 商品的分销金额 Response VO")
  7 +@Data
  8 +public class AppBrokerageProductPriceRespVO {
  9 +
  10 + @Schema(description = "是否开启", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  11 + private Boolean enabled;
  12 +
  13 + @Schema(description = "分销最小金额,单位:分", example = "100")
  14 + private Integer brokerageMinPrice;
  15 +
  16 + @Schema(description = "分销最大金额,单位:分", example = "100")
  17 + private Integer brokerageMaxPrice;
  18 +
  19 +}
... ...
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 {
19 19 private String avatar;
20 20  
21 21 @Schema(description = "佣金金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
22   - private Integer price;
  22 + private Integer brokeragePrice;
23 23  
24 24 @Schema(description = "分销订单数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
25 25 private Integer brokerageOrderCount;
... ...
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 {
14 14 private Integer withdrawPrice;
15 15  
16 16 @Schema(description = "可用的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "2408")
17   - private Integer price;
  17 + private Integer brokeragePrice;
18 18  
19 19 @Schema(description = "冻结的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "234")
20 20 private Integer frozenPrice;
... ...
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 {
17 17 private String avatar;
18 18  
19 19 @Schema(description = "佣金金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
20   - private Integer price;
  20 + private Integer brokeragePrice;
21 21  
22 22 }
... ...
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;
7 7 @Data
8 8 public class AppBrokerageUserRespVO {
9 9  
  10 + @Schema(description = "是否有分销资格", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  11 + private Boolean brokerageEnabled;
  12 +
10 13 @Schema(description = "可用的佣金,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "2408")
11 14 private Integer price;
12 15  
... ...
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 {
41 41 : BrokerageRecordStatusEnum.SETTLEMENT.getStatus();
42 42 return new BrokerageRecordDO().setUserId(user.getId())
43 43 .setBizType(bizType.getType()).setBizId(bizId)
44   - .setPrice(brokeragePrice).setTotalPrice(user.getPrice())
  44 + .setPrice(brokeragePrice).setTotalPrice(user.getBrokeragePrice())
45 45 .setTitle(title)
46 46 .setDescription(StrUtil.format(bizType.getDescription(), String.valueOf(brokeragePrice / 100.0)))
47 47 .setStatus(status).setFrozenDays(brokerageFrozenDays).setUnfreezeTime(unfreezeTime);
... ...
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 {
43 43 private LocalDateTime bindUserTime;
44 44  
45 45 /**
46   - * 推广资格
  46 + * 是否有分销资格
47 47 */
48 48 private Boolean brokerageEnabled;
49 49 /**
... ... @@ -54,7 +54,7 @@ public class BrokerageUserDO extends BaseDO {
54 54 /**
55 55 * 可用佣金
56 56 */
57   - private Integer price;
  57 + private Integer brokeragePrice;
58 58 /**
59 59 * 冻结佣金
60 60 */
... ...
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 {
103 103 * @param isNewUser 是否为新用户
104 104 * @return 是否绑定
105 105 */
106   - boolean bindUser(Long userId, Long bindUserId, Boolean isNewUser);
  106 + boolean bindBrokerageUser(Long userId, Long bindUserId, Boolean isNewUser);
107 107  
108 108 }
... ...
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 {
135 135  
136 136 // TODO @疯狂:因为现在 user 会存在使用验证码直接注册,所以 isNewUser 不太好传递;我们是不是可以约定绑定的时间,createTime 在 30 秒内,就认为新用户;
137 137 @Override
138   - public boolean bindUser(Long userId, Long bindUserId, Boolean isNewUser) {
  138 + public boolean bindBrokerageUser(Long userId, Long bindUserId, Boolean isNewUser) {
139 139 // TODO @疯狂:userId 为空,搞到参数校验里哇;
140 140 if (userId == null) {
141 141 throw exception(0);
... ... @@ -146,7 +146,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
146 146 BrokerageUserDO brokerageUser = brokerageUserMapper.selectById(userId);
147 147 if (brokerageUser == null) { // 分销用户不存在的情况:1. 新注册;2. 旧数据;3. 分销功能关闭后又打开
148 148 isNewBrokerageUser = true;
149   - brokerageUser = new BrokerageUserDO().setId(userId).setBrokerageEnabled(false).setPrice(0).setFrozenPrice(0);
  149 + brokerageUser = new BrokerageUserDO().setId(userId).setBrokerageEnabled(false).setBrokeragePrice(0).setFrozenPrice(0);
150 150 }
151 151  
152 152 // 2.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}}
28 28 "code": 9999
29 29 }
30 30  
  31 +### 请求 /social-login 接口 => 成功
  32 +POST {{appApi}}/member/auth/social-login
  33 +Content-Type: application/json
  34 +tenant-id: {{appTenentId}}
  35 +
  36 +{
  37 + "type": 34,
  38 + "code": "0e1oc9000CTjFQ1oim200bhtb61oc90g",
  39 + "state": "default"
  40 +}
  41 +
31 42 ### 请求 /weixin-mini-app-login 接口 => 成功
32 43 POST {{appApi}}/member/auth/weixin-mini-app-login
33 44 Content-Type: application/json
... ... @@ -38,7 +49,6 @@ tenant-id: {{appTenentId}}
38 49 "loginCode": "001frTkl21JUf94VGxol2hSlff1frTkR"
39 50 }
40 51  
41   -
42 52 ### 请求 /logout 接口 => 成功
43 53 POST {{appApi}}/member/auth/logout
44 54 Content-Type: application/json
... ...
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 {
27 27 @Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED)
28 28 private LocalDateTime expiresTime;
29 29  
  30 + /**
  31 + * 仅社交登录、社交绑定时会返回
  32 + *
  33 + * 为什么需要返回?微信公众号、微信小程序支付需要传递 openid 给支付接口
  34 + */
  35 + @Schema(description = "社交用户 openid", example = "qq768")
  36 + private String openid;
  37 +
30 38 }
... ...
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 {
25 25 SmsCodeUseReqDTO convert(AppMemberUserResetPasswordReqVO reqVO, SmsSceneEnum scene, String usedIp);
26 26 SmsCodeUseReqDTO convert(AppAuthSmsLoginReqVO reqVO, Integer scene, String usedIp);
27 27  
28   - AppAuthLoginRespVO convert(OAuth2AccessTokenRespDTO bean);
  28 + AppAuthLoginRespVO convert(OAuth2AccessTokenRespDTO bean, String openid);
29 29  
30 30 SmsCodeValidateReqDTO convert(AppAuthSmsValidateReqVO bean);
31 31  
... ...
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;
20 20 import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
21 21 import cn.iocoder.yudao.module.system.api.social.SocialUserApi;
22 22 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
  23 +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
23 24 import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
24 25 import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
25 26 import cn.iocoder.yudao.module.system.enums.oauth2.OAuth2ClientConstants;
... ... @@ -65,13 +66,14 @@ public class MemberAuthServiceImpl implements MemberAuthService {
65 66 MemberUserDO user = login0(reqVO.getMobile(), reqVO.getPassword());
66 67  
67 68 // 如果 socialType 非空,说明需要绑定社交用户
  69 + String openid = null;
68 70 if (reqVO.getSocialType() != null) {
69   - socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
  71 + openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
70 72 reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState()));
71 73 }
72 74  
73 75 // 创建 Token 令牌,记录登录日志
74   - return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE);
  76 + return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE, openid);
75 77 }
76 78  
77 79 @Override
... ... @@ -86,32 +88,33 @@ public class MemberAuthServiceImpl implements MemberAuthService {
86 88 Assert.notNull(user, "获取用户失败,结果为空");
87 89  
88 90 // 如果 socialType 非空,说明需要绑定社交用户
  91 + String openid = null;
89 92 if (reqVO.getSocialType() != null) {
90   - socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
  93 + openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
91 94 reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState()));
92 95 }
93 96  
94 97 // 创建 Token 令牌,记录登录日志
95   - return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_SMS);
  98 + return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_SMS, openid);
96 99 }
97 100  
98 101 @Override
99 102 public AppAuthLoginRespVO socialLogin(AppAuthSocialLoginReqVO reqVO) {
100 103 // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
101   - Long userId = socialUserApi.getBindUserId(UserTypeEnum.MEMBER.getValue(), reqVO.getType(),
  104 + SocialUserRespDTO socialUser = socialUserApi.getSocialUser(UserTypeEnum.MEMBER.getValue(), reqVO.getType(),
102 105 reqVO.getCode(), reqVO.getState());
103   - if (userId == null) {
  106 + if (socialUser == null) {
104 107 throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
105 108 }
106 109  
107 110 // 自动登录
108   - MemberUserDO user = userService.getUser(userId);
  111 + MemberUserDO user = userService.getUser(socialUser.getUserId());
109 112 if (user == null) {
110 113 throw exception(USER_NOT_EXISTS);
111 114 }
112 115  
113 116 // 创建 Token 令牌,记录登录日志
114   - return createTokenAfterLoginSuccess(user, user.getMobile(), LoginLogTypeEnum.LOGIN_SOCIAL);
  117 + return createTokenAfterLoginSuccess(user, user.getMobile(), LoginLogTypeEnum.LOGIN_SOCIAL, socialUser.getOpenid());
115 118 }
116 119  
117 120 @Override
... ... @@ -129,14 +132,15 @@ public class MemberAuthServiceImpl implements MemberAuthService {
129 132 Assert.notNull(user, "获取用户失败,结果为空");
130 133  
131 134 // 绑定社交用户
132   - socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
  135 + String openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
133 136 SocialTypeEnum.WECHAT_MINI_APP.getType(), reqVO.getLoginCode(), ""));
134 137  
135 138 // 创建 Token 令牌,记录登录日志
136   - return createTokenAfterLoginSuccess(user, user.getMobile(), LoginLogTypeEnum.LOGIN_SOCIAL);
  139 + return createTokenAfterLoginSuccess(user, user.getMobile(), LoginLogTypeEnum.LOGIN_SOCIAL, openid);
137 140 }
138 141  
139   - private AppAuthLoginRespVO createTokenAfterLoginSuccess(MemberUserDO user, String mobile, LoginLogTypeEnum logType) {
  142 + private AppAuthLoginRespVO createTokenAfterLoginSuccess(MemberUserDO user, String mobile,
  143 + LoginLogTypeEnum logType, String openid) {
140 144 // 插入登陆日志
141 145 createLoginLog(user.getId(), mobile, logType, LoginResultEnum.SUCCESS);
142 146 // 创建 Token 令牌
... ... @@ -144,7 +148,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
144 148 .setUserId(user.getId()).setUserType(getUserType().getValue())
145 149 .setClientId(OAuth2ClientConstants.CLIENT_ID_DEFAULT));
146 150 // 构建返回结果
147   - return AuthConvert.INSTANCE.convert(accessTokenRespDTO);
  151 + return AuthConvert.INSTANCE.convert(accessTokenRespDTO, openid);
148 152 }
149 153  
150 154 @Override
... ... @@ -231,7 +235,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
231 235 public AppAuthLoginRespVO refreshToken(String refreshToken) {
232 236 OAuth2AccessTokenRespDTO accessTokenDO = oauth2TokenApi.refreshAccessToken(refreshToken,
233 237 OAuth2ClientConstants.CLIENT_ID_DEFAULT);
234   - return AuthConvert.INSTANCE.convert(accessTokenDO);
  238 + return AuthConvert.INSTANCE.convert(accessTokenDO, null);
235 239 }
236 240  
237 241 private void createLogoutLog(Long userId) {
... ...
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 {
11 11 private Integer balance;
12 12  
13 13 @Schema(description = "累计支出, 单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
14   - private Long totalExpense;
  14 + private Integer totalExpense;
15 15  
16 16 @Schema(description = "累计充值, 单位分", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000")
17   - private Long totalRecharge;
  17 + private Integer totalRecharge;
18 18  
19 19 }
... ...
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;
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionRespVO;
5 5 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
  6 +import cn.iocoder.yudao.module.pay.service.wallet.bo.CreateWalletTransactionBO;
6 7 import org.mapstruct.Mapper;
7 8 import org.mapstruct.factory.Mappers;
8 9  
... ... @@ -12,4 +13,7 @@ public interface PayWalletTransactionConvert {
12 13 PayWalletTransactionConvert INSTANCE = Mappers.getMapper(PayWalletTransactionConvert.class);
13 14  
14 15 PageResult<AppPayWalletTransactionRespVO> convertPage(PageResult<PayWalletTransactionDO> page);
  16 +
  17 + PayWalletTransactionDO convert(CreateWalletTransactionBO bean);
  18 +
15 19 }
... ...
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 {
37 37 */
38 38 private Integer userType;
39 39  
40   - // TODO @jason:三个都搞 integer?应该要统一哈
41   -
42 40 /**
43 41 * 余额,单位分
44 42 */
... ... @@ -47,10 +45,10 @@ public class PayWalletDO extends BaseDO {
47 45 /**
48 46 * 累计支出,单位分
49 47 */
50   - private Long totalExpense;
  48 + private Integer totalExpense;
51 49 /**
52 50 * 累计充值,单位分
53 51 */
54   - private Long totalRecharge;
  52 + private Integer totalRecharge;
55 53  
56 54 }
... ...
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;
3 3  
4 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 5 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
6   -import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
7   -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  6 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
8 7 import org.apache.ibatis.annotations.Mapper;
9 8  
10 9 @Mapper
... ... @@ -15,56 +14,33 @@ public interface PayWalletMapper extends BaseMapperX&lt;PayWalletDO&gt; {
15 14 PayWalletDO::getUserType, userType);
16 15 }
17 16  
18   - // TODO @jason:减少时,需要 update price -= ? where price >= ?,避免并发问题。现在基于 price 来过滤,虽然也能解决并发问题,但是冲突概率会高一点;可以看到 TradeBrokerageUserMapper 的做法;
19 17 /**
20   - * 当余额减少时候更新
  18 + * 当消费退款时候, 更新钱包
21 19 *
22   - * @param bizType 业务类型
23   - * @param balance 当前余额
24   - * @param totalRecharge 当前累计充值
25   - * @param totalExpense 当前累计支出
26   - * @param price 支出的金额
  20 + * @param price 消费金额
27 21 * @param id 钱包 id
28 22 */
29   - default int updateWhenDecBalance(PayWalletBizTypeEnum bizType, Integer balance, Long totalRecharge,
30   - Long totalExpense, Integer price, Long id) {
31   - // TODO @jason:这种偏判断的,最红放在 service 层;mapper 可以写多个方法;
32   - PayWalletDO updateDO = new PayWalletDO().setBalance(balance - price);
33   - if(bizType == PayWalletBizTypeEnum.PAYMENT){
34   - updateDO.setTotalExpense(totalExpense + price);
35   - }
36   - if (bizType == PayWalletBizTypeEnum.RECHARGE_REFUND) {
37   - updateDO.setTotalRecharge(totalRecharge - price);
38   - }
39   - return update(updateDO,
40   - new LambdaQueryWrapper<PayWalletDO>().eq(PayWalletDO::getId, id)
41   - .eq(PayWalletDO::getBalance, balance)
42   - .ge(PayWalletDO::getBalance, price));
  23 + default int updateWhenConsumptionRefund(Integer price, Long id){
  24 + LambdaUpdateWrapper<PayWalletDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<PayWalletDO>()
  25 + .setSql(" balance = balance + " + price
  26 + + ", total_expense = total_expense - " + price)
  27 + .eq(PayWalletDO::getId, id);
  28 + return update(null, lambdaUpdateWrapper);
43 29 }
44 30  
45   - // TODO @jason:类似上面的修改建议哈;
46 31 /**
47   - * 当余额增加时候更新
  32 + * 当消费时候, 更新钱包
48 33 *
49   - * @param bizType 业务类型
50   - * @param balance 当前余额
51   - * @param totalRecharge 当前累计充值
52   - * @param totalExpense 当前累计支出
53   - * @param price 金额
  34 + * @param price 消费金额
54 35 * @param id 钱包 id
55 36 */
56   - default int updateWhenIncBalance(PayWalletBizTypeEnum bizType, Integer balance, Long totalRecharge,
57   - Long totalExpense, Integer price, Long id) {
58   - PayWalletDO updateDO = new PayWalletDO().setBalance(balance + price);
59   - if (bizType == PayWalletBizTypeEnum.PAYMENT_REFUND) {
60   - updateDO.setTotalExpense(totalExpense - price);
61   - }
62   - if (bizType == PayWalletBizTypeEnum.RECHARGE) {
63   - updateDO.setTotalExpense(totalRecharge + price);
64   - }
65   - return update(updateDO,
66   - new LambdaQueryWrapper<PayWalletDO>().eq(PayWalletDO::getId, id)
67   - .eq(PayWalletDO::getBalance, balance));
  37 + default int updateWhenConsumption(Integer price, Long id){
  38 + LambdaUpdateWrapper<PayWalletDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<PayWalletDO>()
  39 + .setSql(" balance = balance - " + price
  40 + + ", total_expense = total_expense + " + price)
  41 + .eq(PayWalletDO::getId, id)
  42 + .ge(PayWalletDO::getBalance, price); // cas 逻辑
  43 + return update(null, lambdaUpdateWrapper);
68 44 }
69 45 }
70 46  
... ...
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&lt;NonePayClientConfig&gt; {
62 62 @Override
63 63 protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
64 64 try {
65   - // TODO @jason:直接 getLong 和 getInt 会不会更简洁哈
66   - String userId = MapUtil.getStr(reqDTO.getChannelExtras(), USER_ID_KEY);
67   - String userType = MapUtil.getStr(reqDTO.getChannelExtras(), USER_TYPE_KEY);
68   - Assert.notEmpty(userId, "用户 id 不能为空");
69   - Assert.notEmpty(userType, "用户类型不能为空");
70   - PayWalletTransactionDO transaction = wallService.orderPay(Long.valueOf(userId), Integer.valueOf(userType),
71   - reqDTO.getOutTradeNo(), reqDTO.getPrice());
  65 + Long userId = MapUtil.getLong(reqDTO.getChannelExtras(), USER_ID_KEY);
  66 + Integer userType = MapUtil.getInt(reqDTO.getChannelExtras(), USER_TYPE_KEY);
  67 + Assert.notNull(userId, "用户 id 不能为空");
  68 + Assert.notNull(userType, "用户类型不能为空");
  69 + PayWalletTransactionDO transaction = wallService.orderPay(userId, userType, reqDTO.getOutTradeNo(),
  70 + reqDTO.getPrice());
72 71 return PayOrderRespDTO.successOf(transaction.getNo(), transaction.getCreator(),
73 72 transaction.getCreateTime(),
74 73 reqDTO.getOutTradeNo(), transaction);
... ...
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;
6 6 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
7 7 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
8 8 import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletMapper;
9   -import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO;
10 9 import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
11 10 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
12 11 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
  12 +import cn.iocoder.yudao.module.pay.service.wallet.bo.CreateWalletTransactionBO;
13 13 import lombok.extern.slf4j.Slf4j;
14 14 import org.springframework.context.annotation.Lazy;
15 15 import org.springframework.stereotype.Service;
... ... @@ -18,7 +18,6 @@ import org.springframework.transaction.annotation.Transactional;
18 18 import javax.annotation.Resource;
19 19 import java.time.LocalDateTime;
20 20  
21   -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.TOO_MANY_REQUESTS;
22 21 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
23 22 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
24 23 import static cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum.PAYMENT;
... ... @@ -33,21 +32,9 @@ import static cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum.PAYM
33 32 @Slf4j
34 33 public class PayWalletServiceImpl implements PayWalletService {
35 34  
36   - /**
37   - * 余额支付的 no 前缀
38   - */
39   - private static final String WALLET_PAY_NO_PREFIX = "WP";
40   - /**
41   - * 余额退款的 no 前缀
42   - */
43   - private static final String WALLET_REFUND_NO_PREFIX = "WR";
44   -
45 35 @Resource
46 36 private PayWalletMapper walletMapper;
47 37 @Resource
48   - private PayNoRedisDAO noRedisDAO;
49   -
50   - @Resource
51 38 private PayWalletTransactionService walletTransactionService;
52 39 @Resource
53 40 @Lazy
... ... @@ -61,7 +48,7 @@ public class PayWalletServiceImpl implements PayWalletService {
61 48 PayWalletDO wallet = walletMapper.selectByUserIdAndType(userId, userType);
62 49 if (wallet == null) {
63 50 wallet = new PayWalletDO().setUserId(userId).setUserType(userType)
64   - .setBalance(0).setTotalExpense(0L).setTotalRecharge(0L);
  51 + .setBalance(0).setTotalExpense(0).setTotalRecharge(0);
65 52 wallet.setCreateTime(LocalDateTime.now());
66 53 walletMapper.insert(wallet);
67 54 }
... ... @@ -125,70 +112,54 @@ public class PayWalletServiceImpl implements PayWalletService {
125 112 @Override
126 113 public PayWalletTransactionDO reduceWalletBalance(Long userId, Integer userType,
127 114 Long bizId, PayWalletBizTypeEnum bizType, Integer price) {
128   - // 1.1 获取钱包
  115 + // 1. 获取钱包
129 116 PayWalletDO payWallet = getOrCreateWallet(userId, userType);
130   - // 1.2 判断余额是否足够
131   - int afterBalance = payWallet.getBalance() - price;
132   - if (afterBalance < 0) {
133   - throw exception(WALLET_BALANCE_NOT_ENOUGH);
134   - }
135 117  
136   - // TODO jason:建议基于 where price >= 来做哈;然后抛出 WALLET_BALANCE_NOT_ENOUGH
137 118 // 2.1 扣除余额
138   - int number = walletMapper.updateWhenDecBalance(bizType, payWallet.getBalance(),
139   - payWallet.getTotalRecharge(), payWallet.getTotalExpense(), price, payWallet.getId());
140   - if (number == 0) {
141   - throw exception(TOO_MANY_REQUESTS);
  119 + int updateCounts = 0 ;
  120 + switch (bizType) {
  121 + case PAYMENT: {
  122 + updateCounts = walletMapper.updateWhenConsumption(price, payWallet.getId());
  123 + break;
  124 + }
  125 + case RECHARGE_REFUND: {
  126 + // TODO
  127 + break;
  128 + }
  129 + }
  130 + if (updateCounts == 0) {
  131 + throw exception(WALLET_BALANCE_NOT_ENOUGH);
142 132 }
143   -
144 133 // 2.2 生成钱包流水
145   - // TODO @jason:walletNo 交给 payWalletTransactionService 自己生成哈;
146   - String walletNo = generateWalletNo(bizType);
147   - PayWalletTransactionDO walletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId())
148   - .setNo(walletNo).setPrice(-price).setBalance(afterBalance)
149   - .setBizId(String.valueOf(bizId)).setBizType(bizType.getType()).setTitle(bizType.getDescription());
150   - // TODO @jason:是不是可以 createWalletTransaction 搞个 bo 参数,然后 PayWalletTransactionDO 交回给 walletTransactionService 更好;然后把参数简化下
151   - walletTransactionService.createWalletTransaction(walletTransaction);
152   - return walletTransaction;
  134 + Integer afterBalance = payWallet.getBalance() - price;
  135 + CreateWalletTransactionBO bo = new CreateWalletTransactionBO().setWalletId(payWallet.getId())
  136 + .setPrice(-price).setBalance(afterBalance).setBizId(String.valueOf(bizId))
  137 + .setBizType(bizType.getType()).setTitle(bizType.getDescription());
  138 + return walletTransactionService.createWalletTransaction(bo);
153 139 }
154 140  
155 141 @Override
156 142 public PayWalletTransactionDO addWalletBalance(Long userId, Integer userType,
157 143 Long bizId, PayWalletBizTypeEnum bizType, Integer price) {
158   - // 1.1 获取钱包
  144 + // 1. 获取钱包
159 145 PayWalletDO payWallet = getOrCreateWallet(userId, userType);
160   -
161   - // 2.1 增加余额
162   - // TODO @jason:类似上面的思路哈;
163   - int number = walletMapper.updateWhenIncBalance(bizType, payWallet.getBalance(), payWallet.getTotalRecharge(),
164   - payWallet.getTotalExpense(), price, payWallet.getId());
165   - if (number == 0) {
166   - throw exception(TOO_MANY_REQUESTS);
167   - }
168   -
169   - // 2.2 生成钱包流水
170   - String walletNo = generateWalletNo(bizType);
171   - PayWalletTransactionDO newWalletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId())
172   - .setNo(walletNo).setPrice(price).setBalance(payWallet.getBalance()+price)
173   - .setBizId(String.valueOf(bizId)).setBizType(bizType.getType())
174   - .setTitle(bizType.getDescription());
175   - walletTransactionService.createWalletTransaction(newWalletTransaction);
176   - return newWalletTransaction;
177   - }
178   -
179   - private String generateWalletNo(PayWalletBizTypeEnum bizType) {
180   - // TODO @jason:对于余额来说,是不是直接 W+序号就行了,它其实不关注业务;;;不然就耦合啦
181   - String no = "";
182   - switch(bizType){
183   - case PAYMENT:
184   - no = noRedisDAO.generate(WALLET_PAY_NO_PREFIX);
  146 + switch (bizType) {
  147 + case PAYMENT_REFUND: {
  148 + // 更新退款
  149 + walletMapper.updateWhenConsumptionRefund(price, payWallet.getId());
185 150 break;
186   - case PAYMENT_REFUND:
187   - no = noRedisDAO.generate(WALLET_REFUND_NO_PREFIX);
  151 + }
  152 + case RECHARGE: {
  153 + //TODO
188 154 break;
189   - default :
  155 + }
190 156 }
191   - return no;
  157 +
  158 + // 2. 生成钱包流水
  159 + CreateWalletTransactionBO bo = new CreateWalletTransactionBO().setWalletId(payWallet.getId())
  160 + .setPrice(price).setBalance(payWallet.getBalance()+price).setBizId(String.valueOf(bizId))
  161 + .setBizType(bizType.getType()).setTitle(bizType.getDescription());
  162 + return walletTransactionService.createWalletTransaction(bo);
192 163 }
193 164  
194 165 }
... ...
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;
4 4 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionPageReqVO;
5 5 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
6 6 import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
  7 +import cn.iocoder.yudao.module.pay.service.wallet.bo.CreateWalletTransactionBO;
  8 +
  9 +import javax.validation.Valid;
7 10  
8 11 /**
9 12 * 钱包余额流水 Service 接口
... ... @@ -25,10 +28,10 @@ public interface PayWalletTransactionService {
25 28 /**
26 29 * 新增钱包余额流水
27 30 *
28   - * @param payWalletTransaction 余额流水
29   - * @return id
  31 + * @param bo 创建钱包流水 bo
  32 + * @return 新建的钱包 do
30 33 */
31   - Long createWalletTransaction(PayWalletTransactionDO payWalletTransaction);
  34 + PayWalletTransactionDO createWalletTransaction(@Valid CreateWalletTransactionBO bo);
32 35  
33 36 /**
34 37 * 根据 no,获取钱包余流水
... ... @@ -45,4 +48,5 @@ public interface PayWalletTransactionService {
45 48 * @return 钱包流水
46 49 */
47 50 PayWalletTransactionDO getWalletTransaction(String bizId, PayWalletBizTypeEnum type);
  51 +
48 52 }
... ...
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;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionPageReqVO;
  5 +import cn.iocoder.yudao.module.pay.convert.wallet.PayWalletTransactionConvert;
5 6 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
6 7 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
7 8 import cn.iocoder.yudao.module.pay.dal.mysql.wallet.PayWalletTransactionMapper;
  9 +import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO;
8 10 import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
  11 +import cn.iocoder.yudao.module.pay.service.wallet.bo.CreateWalletTransactionBO;
9 12 import lombok.extern.slf4j.Slf4j;
10 13 import org.springframework.stereotype.Service;
11 14  
... ... @@ -20,10 +23,17 @@ import javax.annotation.Resource;
20 23 @Slf4j
21 24 public class PayWalletTransactionServiceImpl implements PayWalletTransactionService {
22 25  
  26 + /**
  27 + * 钱包流水的 no 前缀
  28 + */
  29 + private static final String WALLET_NO_PREFIX = "W";
  30 +
23 31 @Resource
24 32 private PayWalletService payWalletService;
25 33 @Resource
26 34 private PayWalletTransactionMapper payWalletTransactionMapper;
  35 + @Resource
  36 + private PayNoRedisDAO noRedisDAO;
27 37  
28 38 @Override
29 39 public PageResult<PayWalletTransactionDO> getWalletTransactionPage(Long userId, Integer userType,
... ... @@ -33,9 +43,11 @@ public class PayWalletTransactionServiceImpl implements PayWalletTransactionServ
33 43 }
34 44  
35 45 @Override
36   - public Long createWalletTransaction(PayWalletTransactionDO payWalletTransaction) {
37   - payWalletTransactionMapper.insert(payWalletTransaction);
38   - return payWalletTransaction.getId();
  46 + public PayWalletTransactionDO createWalletTransaction(CreateWalletTransactionBO bo) {
  47 + PayWalletTransactionDO transaction = PayWalletTransactionConvert.INSTANCE.convert(bo)
  48 + .setNo(noRedisDAO.generate(WALLET_NO_PREFIX));
  49 + payWalletTransactionMapper.insert(transaction);
  50 + return transaction;
39 51 }
40 52  
41 53 @Override
... ... @@ -47,4 +59,5 @@ public class PayWalletTransactionServiceImpl implements PayWalletTransactionServ
47 59 public PayWalletTransactionDO getWalletTransaction(String bizId, PayWalletBizTypeEnum type) {
48 60 return payWalletTransactionMapper.selectByBiz(bizId, type.getType());
49 61 }
  62 +
50 63 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/bo/CreateWalletTransactionBO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.pay.service.wallet.bo;
  2 +
  3 +import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
  4 +import lombok.Data;
  5 +
  6 +/**
  7 + * 创建钱包流水 BO
  8 + *
  9 + * @author jason
  10 + */
  11 +@Data
  12 +public class CreateWalletTransactionBO {
  13 +
  14 + // TODO @jason:bo 的话,最好加个参数校验哈;
  15 +
  16 + /**
  17 + * 钱包编号
  18 + *
  19 + */
  20 + private Long walletId;
  21 +
  22 + /**
  23 + * 交易金额,单位分
  24 + *
  25 + * 正值表示余额增加,负值表示余额减少
  26 + */
  27 + private Integer price;
  28 +
  29 + /**
  30 + * 交易后余额,单位分
  31 + */
  32 + private Integer balance;
  33 +
  34 + /**
  35 + * 关联业务分类
  36 + *
  37 + * 枚举 {@link PayWalletBizTypeEnum#getType()}
  38 + */
  39 + private Integer bizType;
  40 +
  41 + /**
  42 + * 关联业务编号
  43 + */
  44 + private String bizId;
  45 +
  46 + /**
  47 + * 流水说明
  48 + */
  49 + private String title;
  50 +}
... ...
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;
2 2  
3 3 import cn.iocoder.yudao.framework.common.exception.ServiceException;
4 4 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
  5 +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
5 6 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO;
6 7 import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
7 8  
... ... @@ -27,8 +28,9 @@ public interface SocialUserApi {
27 28 * 绑定社交用户
28 29 *
29 30 * @param reqDTO 绑定信息
  31 + * @return 社交用户 openid
30 32 */
31   - void bindSocialUser(@Valid SocialUserBindReqDTO reqDTO);
  33 + String bindSocialUser(@Valid SocialUserBindReqDTO reqDTO);
32 34  
33 35 /**
34 36 * 取消绑定社交用户
... ... @@ -38,16 +40,17 @@ public interface SocialUserApi {
38 40 void unbindSocialUser(@Valid SocialUserUnbindReqDTO reqDTO);
39 41  
40 42 /**
41   - * 获得社交用户的绑定用户编号
42   - * 注意,返回的是 MemberUser 或者 AdminUser 的 id 编号!
  43 + * 获得社交用户
  44 + *
43 45 * 在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常
44 46 *
45 47 * @param userType 用户类型
46 48 * @param type 社交平台的类型
47 49 * @param code 授权码
48 50 * @param state state
49   - * @return 绑定用户编号
  51 + * @return 社交用户
50 52 */
51   - Long getBindUserId(Integer userType, Integer type, String code, String state);
  53 + SocialUserRespDTO getSocialUser(Integer userType, Integer type,
  54 + String code, String state);
52 55  
53 56 }
... ...
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserRespDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.api.social.dto;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Data;
  5 +import lombok.NoArgsConstructor;
  6 +
  7 +/**
  8 + * 社交用户 Response DTO
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +@Data
  13 +@NoArgsConstructor
  14 +@AllArgsConstructor
  15 +public class SocialUserRespDTO {
  16 +
  17 + /**
  18 + * 社交用户 openid
  19 + */
  20 + private String openid;
  21 +
  22 + /**
  23 + * 关联的用户编号
  24 + */
  25 + private Long userId;
  26 +
  27 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApiImpl.java
1 1 package cn.iocoder.yudao.module.system.api.social;
2 2  
3 3 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
  4 +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
4 5 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO;
5 6 import cn.iocoder.yudao.module.system.service.social.SocialUserService;
6 7 import org.springframework.stereotype.Service;
... ... @@ -26,8 +27,8 @@ public class SocialUserApiImpl implements SocialUserApi {
26 27 }
27 28  
28 29 @Override
29   - public void bindSocialUser(SocialUserBindReqDTO reqDTO) {
30   - socialUserService.bindSocialUser(reqDTO);
  30 + public String bindSocialUser(SocialUserBindReqDTO reqDTO) {
  31 + return socialUserService.bindSocialUser(reqDTO);
31 32 }
32 33  
33 34 @Override
... ... @@ -37,8 +38,8 @@ public class SocialUserApiImpl implements SocialUserApi {
37 38 }
38 39  
39 40 @Override
40   - public Long getBindUserId(Integer userType, Integer type, String code, String state) {
41   - return socialUserService.getBindUserId(userType, type, code, state);
  41 + public SocialUserRespDTO getSocialUser(Integer userType, Integer type, String code, String state) {
  42 + return socialUserService.getSocialUser(userType, type, code, state);
42 43 }
43 44  
44 45 }
... ...
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;
9 9 import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO;
10 10 import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
11 11 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
  12 +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
12 13 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*;
13 14 import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
14 15 import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
... ... @@ -155,14 +156,14 @@ public class AdminAuthServiceImpl implements AdminAuthService {
155 156 @Override
156 157 public AuthLoginRespVO socialLogin(AuthSocialLoginReqVO reqVO) {
157 158 // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
158   - Long userId = socialUserService.getBindUserId(UserTypeEnum.ADMIN.getValue(), reqVO.getType(),
  159 + SocialUserRespDTO socialUser = socialUserService.getSocialUser(UserTypeEnum.ADMIN.getValue(), reqVO.getType(),
159 160 reqVO.getCode(), reqVO.getState());
160   - if (userId == null) {
  161 + if (socialUser == null) {
161 162 throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
162 163 }
163 164  
164 165 // 获得用户
165   - AdminUserDO user = userService.getUser(userId);
  166 + AdminUserDO user = userService.getUser(socialUser.getUserId());
166 167 if (user == null) {
167 168 throw exception(USER_NOT_EXISTS);
168 169 }
... ...
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;
2 2  
3 3 import cn.iocoder.yudao.framework.common.exception.ServiceException;
4 4 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
  5 +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
5 6 import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
6 7 import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
7 8  
... ... @@ -50,8 +51,9 @@ public interface SocialUserService {
50 51 * 绑定社交用户
51 52 *
52 53 * @param reqDTO 绑定信息
  54 + * @return 社交用户 openid
53 55 */
54   - void bindSocialUser(@Valid SocialUserBindReqDTO reqDTO);
  56 + String bindSocialUser(@Valid SocialUserBindReqDTO reqDTO);
55 57  
56 58 /**
57 59 * 取消绑定社交用户
... ... @@ -64,15 +66,16 @@ public interface SocialUserService {
64 66 void unbindSocialUser(Long userId, Integer userType, Integer type, String openid);
65 67  
66 68 /**
67   - * 获得社交用户的绑定用户编号
68   - * 注意,返回的是 MemberUser 或者 AdminUser 的 id 编号!
  69 + * 获得社交用户
  70 + *
69 71 * 在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常
70 72 *
71 73 * @param userType 用户类型
72 74 * @param type 社交平台的类型
73 75 * @param code 授权码
74 76 * @param state state
75   - * @return 绑定用户编号
  77 + * @return 社交用户
76 78 */
77   - Long getBindUserId(Integer userType, Integer type, String code, String state);
  79 + SocialUserRespDTO getSocialUser(Integer userType, Integer type, String code, String state);
  80 +
78 81 }
... ...
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;
5 5 import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
6 6 import cn.iocoder.yudao.framework.social.core.YudaoAuthRequestFactory;
7 7 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
  8 +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
8 9 import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO;
9 10 import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
10 11 import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper;
... ... @@ -98,7 +99,7 @@ public class SocialUserServiceImpl implements SocialUserService {
98 99  
99 100 @Override
100 101 @Transactional
101   - public void bindSocialUser(SocialUserBindReqDTO reqDTO) {
  102 + public String bindSocialUser(SocialUserBindReqDTO reqDTO) {
102 103 // 获得社交用户
103 104 SocialUserDO socialUser = authSocialUser(reqDTO.getType(), reqDTO.getCode(), reqDTO.getState());
104 105 Assert.notNull(socialUser, "社交用户不能为空");
... ... @@ -115,6 +116,7 @@ public class SocialUserServiceImpl implements SocialUserService {
115 116 .userId(reqDTO.getUserId()).userType(reqDTO.getUserType())
116 117 .socialUserId(socialUser.getId()).socialType(socialUser.getType()).build();
117 118 socialUserBindMapper.insert(socialUserBind);
  119 + return socialUser.getOpenid();
118 120 }
119 121  
120 122 @Override
... ... @@ -130,7 +132,7 @@ public class SocialUserServiceImpl implements SocialUserService {
130 132 }
131 133  
132 134 @Override
133   - public Long getBindUserId(Integer userType, Integer type, String code, String state) {
  135 + public SocialUserRespDTO getSocialUser(Integer userType, Integer type, String code, String state) {
134 136 // 获得社交用户
135 137 SocialUserDO socialUser = authSocialUser(type, code, state);
136 138 Assert.notNull(socialUser, "社交用户不能为空");
... ... @@ -141,7 +143,7 @@ public class SocialUserServiceImpl implements SocialUserService {
141 143 if (socialUserBind == null) {
142 144 throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
143 145 }
144   - return socialUserBind.getUserId();
  146 + return new SocialUserRespDTO(socialUser.getOpenid(), socialUserBind.getUserId());
145 147 }
146 148  
147 149 /**
... ...
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;
6 6 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
7 7 import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
8 8 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
  9 +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
9 10 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*;
10 11 import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
11 12 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
... ... @@ -235,8 +236,8 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest {
235 236 AuthSocialLoginReqVO reqVO = randomPojo(AuthSocialLoginReqVO.class);
236 237 // mock 方法(绑定的用户编号)
237 238 Long userId = 1L;
238   - when(socialUserService.getBindUserId(eq(UserTypeEnum.ADMIN.getValue()), eq(reqVO.getType()),
239   - eq(reqVO.getCode()), eq(reqVO.getState()))).thenReturn(userId);
  239 + when(socialUserService.getSocialUser(eq(UserTypeEnum.ADMIN.getValue()), eq(reqVO.getType()),
  240 + eq(reqVO.getCode()), eq(reqVO.getState()))).thenReturn(new SocialUserRespDTO(randomString(), userId));
240 241 // mock(用户)
241 242 AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(userId));
242 243 when(userService.getUser(eq(userId))).thenReturn(user);
... ...
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;
4 4 import cn.iocoder.yudao.framework.social.core.YudaoAuthRequestFactory;
5 5 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
6 6 import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
  7 +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
7 8 import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO;
8 9 import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
9 10 import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper;
... ... @@ -195,10 +196,11 @@ public class SocialUserServiceImplTest extends BaseDbUnitTest {
195 196 .setSocialType(SocialTypeEnum.GITEE.getType()).setSocialUserId(socialUser.getId()));
196 197  
197 198 // 调用
198   - socialUserService.bindSocialUser(reqDTO);
  199 + String openid = socialUserService.bindSocialUser(reqDTO);
199 200 // 断言
200 201 List<SocialUserBindDO> socialUserBinds = socialUserBindMapper.selectList();
201 202 assertEquals(1, socialUserBinds.size());
  203 + assertEquals(socialUser.getOpenid(), openid);
202 204 }
203 205  
204 206 @Test
... ... @@ -232,25 +234,26 @@ public class SocialUserServiceImplTest extends BaseDbUnitTest {
232 234 }
233 235  
234 236 @Test
235   - public void testGetBindUserId() {
  237 + public void testGetSocialUser() {
236 238 // 准备参数
237 239 Integer userType = UserTypeEnum.ADMIN.getValue();
238 240 Integer type = SocialTypeEnum.GITEE.getType();
239 241 String code = "tudou";
240 242 String state = "yuanma";
241 243 // mock 社交用户
242   - SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(type).setCode(code).setState(state);
243   - socialUserMapper.insert(socialUser);
  244 + SocialUserDO socialUserDO = randomPojo(SocialUserDO.class).setType(type).setCode(code).setState(state);
  245 + socialUserMapper.insert(socialUserDO);
244 246 // mock 社交用户的绑定
245 247 Long userId = randomLong();
246 248 SocialUserBindDO socialUserBind = randomPojo(SocialUserBindDO.class).setUserType(userType).setUserId(userId)
247   - .setSocialType(type).setSocialUserId(socialUser.getId());
  249 + .setSocialType(type).setSocialUserId(socialUserDO.getId());
248 250 socialUserBindMapper.insert(socialUserBind);
249 251  
250 252 // 调用
251   - Long result = socialUserService.getBindUserId(userType, type, code, state);
  253 + SocialUserRespDTO socialUser = socialUserService.getSocialUser(userType, type, code, state);
252 254 // 断言
253   - assertEquals(userId, result);
  255 + assertEquals(userId, socialUser.getUserId());
  256 + assertEquals(socialUserDO.getOpenid(), socialUser.getOpenid());
254 257 }
255 258  
256 259 }
... ...
yudao-server/src/main/resources/application-local.yaml
... ... @@ -173,8 +173,10 @@ wx:
173 173 key-prefix: wx # Redis Key 的前缀
174 174 http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台
175 175 miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档
176   - appid: wx62056c0d5e8db250
177   - secret: 333ae72f41552af1e998fe1f54e1584a
  176 +# appid: wx62056c0d5e8db250
  177 +# secret: 333ae72f41552af1e998fe1f54e1584a
  178 + appid: wx63c280fe3248a3e7 # wenhualian的接口测试号
  179 + secret: 6f270509224a7ae1296bbf1c8cb97aed
178 180 config-storage:
179 181 type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取
180 182 key-prefix: wa # Redis Key 的前缀
... ...