Merge remote-tracking branch 'yudao/feature/mall_product' into feature/mall_product
This commit is contained in:
commit
3976011af0
@ -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 '更新者',
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -17,6 +17,6 @@ public class AppBrokerageUserRankByPriceRespVO {
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "佣金金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Integer price;
|
||||
private Integer brokeragePrice;
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
/**
|
||||
* 冻结佣金
|
||||
*/
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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 校验能否绑定
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 @jason:walletNo 交给 payWalletTransactionService 自己生成哈;
|
||||
String walletNo = generateWalletNo(bizType);
|
||||
PayWalletTransactionDO walletTransaction = new PayWalletTransactionDO().setWalletId(payWallet.getId())
|
||||
.setNo(walletNo).setPrice(-price).setBalance(afterBalance)
|
||||
.setBizId(String.valueOf(bizId)).setBizType(bizType.getType()).setTitle(bizType.getDescription());
|
||||
// TODO @jason:是不是可以 createWalletTransaction 搞个 bo 参数,然后 PayWalletTransactionDO 交回给 walletTransactionService 更好;然后把参数简化下
|
||||
walletTransactionService.createWalletTransaction(walletTransaction);
|
||||
return walletTransaction;
|
||||
Integer afterBalance = payWallet.getBalance() - price;
|
||||
CreateWalletTransactionBO bo = new CreateWalletTransactionBO().setWalletId(payWallet.getId())
|
||||
.setPrice(-price).setBalance(afterBalance).setBizId(String.valueOf(bizId))
|
||||
.setBizType(bizType.getType()).setTitle(bizType.getDescription());
|
||||
return walletTransactionService.createWalletTransaction(bo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PayWalletTransactionDO addWalletBalance(Long userId, Integer userType,
|
||||
Long bizId, PayWalletBizTypeEnum bizType, Integer price) {
|
||||
// 1.1 获取钱包
|
||||
// 1. 获取钱包
|
||||
PayWalletDO payWallet = getOrCreateWallet(userId, userType);
|
||||
|
||||
// 2.1 增加余额
|
||||
// TODO @jason:类似上面的思路哈;
|
||||
int number = walletMapper.updateWhenIncBalance(bizType, payWallet.getBalance(), payWallet.getTotalRecharge(),
|
||||
payWallet.getTotalExpense(), price, payWallet.getId());
|
||||
if (number == 0) {
|
||||
throw exception(TOO_MANY_REQUESTS);
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,50 @@
|
||||
package cn.iocoder.yudao.module.pay.service.wallet.bo;
|
||||
|
||||
import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 创建钱包流水 BO
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Data
|
||||
public class CreateWalletTransactionBO {
|
||||
|
||||
// TODO @jason:bo 的话,最好加个参数校验哈;
|
||||
|
||||
/**
|
||||
* 钱包编号
|
||||
*
|
||||
*/
|
||||
private Long walletId;
|
||||
|
||||
/**
|
||||
* 交易金额,单位分
|
||||
*
|
||||
* 正值表示余额增加,负值表示余额减少
|
||||
*/
|
||||
private Integer price;
|
||||
|
||||
/**
|
||||
* 交易后余额,单位分
|
||||
*/
|
||||
private Integer balance;
|
||||
|
||||
/**
|
||||
* 关联业务分类
|
||||
*
|
||||
* 枚举 {@link PayWalletBizTypeEnum#getType()}
|
||||
*/
|
||||
private Integer bizType;
|
||||
|
||||
/**
|
||||
* 关联业务编号
|
||||
*/
|
||||
private String bizId;
|
||||
|
||||
/**
|
||||
* 流水说明
|
||||
*/
|
||||
private String title;
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.service.social;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
|
||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
|
||||
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
||||
|
||||
@ -50,8 +51,9 @@ public interface SocialUserService {
|
||||
* 绑定社交用户
|
||||
*
|
||||
* @param reqDTO 绑定信息
|
||||
* @return 社交用户 openid
|
||||
*/
|
||||
void bindSocialUser(@Valid SocialUserBindReqDTO reqDTO);
|
||||
String bindSocialUser(@Valid SocialUserBindReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 取消绑定社交用户
|
||||
@ -64,15 +66,16 @@ public interface SocialUserService {
|
||||
void unbindSocialUser(Long userId, Integer userType, Integer type, String openid);
|
||||
|
||||
/**
|
||||
* 获得社交用户的绑定用户编号
|
||||
* 注意,返回的是 MemberUser 或者 AdminUser 的 id 编号!
|
||||
* 获得社交用户
|
||||
*
|
||||
* 在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常
|
||||
*
|
||||
* @param userType 用户类型
|
||||
* @param type 社交平台的类型
|
||||
* @param code 授权码
|
||||
* @param state state
|
||||
* @return 绑定用户编号
|
||||
* @return 社交用户
|
||||
*/
|
||||
Long getBindUserId(Integer userType, Integer type, String code, String state);
|
||||
SocialUserRespDTO getSocialUser(Integer userType, Integer type, String code, String state);
|
||||
|
||||
}
|
||||
|
@ -5,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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 的前缀
|
||||
|
Loading…
Reference in New Issue
Block a user