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

This commit is contained in:
puhui999 2023-09-10 22:54:01 +08:00
commit 3976011af0
36 changed files with 316 additions and 203 deletions

View File

@ -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 '更新者',

View File

@ -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);
}
}

View File

@ -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")

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

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

View File

@ -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;

View File

@ -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);

View File

@ -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;
/**
* 冻结佣金
*/

View File

@ -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);
}

View File

@ -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 校验能否绑定

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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) {

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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) {
// 2.1 扣除余额
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);
}
// 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);
}
// 2.2 生成钱包流水
// TODO @jasonwalletNo 交给 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);
switch (bizType) {
case PAYMENT_REFUND: {
// 更新退款
walletMapper.updateWhenConsumptionRefund(price, payWallet.getId());
break;
}
case RECHARGE: {
//TODO
break;
}
}
// 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);
break;
case PAYMENT_REFUND:
no = noRedisDAO.generate(WALLET_REFUND_NO_PREFIX);
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);
}
}

View File

@ -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);
}

View File

@ -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());
}
}

View File

@ -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 @jasonbo 的话最好加个参数校验哈
/**
* 钱包编号
*
*/
private Long walletId;
/**
* 交易金额单位分
*
* 正值表示余额增加负值表示余额减少
*/
private Integer price;
/**
* 交易后余额单位分
*/
private Integer balance;
/**
* 关联业务分类
*
* 枚举 {@link PayWalletBizTypeEnum#getType()}
*/
private Integer bizType;
/**
* 关联业务编号
*/
private String bizId;
/**
* 流水说明
*/
private String title;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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());
}
/**

View File

@ -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);

View File

@ -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());
}
}

View File

@ -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 的前缀