Commit d65c28d7f7e7db4a8c14538ba3cd30d3111bf6d3

Authored by YunaiV
1 parent 106c1ecf

✨ Member:社交登录逻辑调整,如果登录时无用户,则自动创建用户,保持和各 App 统一逻辑

Showing 16 changed files with 80 additions and 38 deletions
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/TerminalEnum.java
... ... @@ -15,6 +15,7 @@ import java.util.Arrays;
15 15 @Getter
16 16 public enum TerminalEnum implements IntArrayValuable {
17 17  
  18 + UNKNOWN(0, "未知"), // 目的:在无法解析到 terminal 时,使用它
18 19 WECHAT_MINI_PROGRAM(10, "微信小程序"),
19 20 WECHAT_WAP(11, "微信公众号"),
20 21 H5(20, "H5 网页"),
... ...
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/servlet/ServletUtils.java
... ... @@ -88,8 +88,6 @@ public class ServletUtils {
88 88 return JakartaServletUtil.getClientIP(request);
89 89 }
90 90  
91   - // TODO @疯狂:terminal 还是从 ServletUtils 里拿,更容易全局治理;
92   -
93 91 public static boolean isJsonRequest(ServletRequest request) {
94 92 return StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE);
95 93 }
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java
1 1 package cn.iocoder.yudao.framework.web.core.util;
2 2  
3 3 import cn.hutool.core.util.NumberUtil;
  4 +import cn.hutool.extra.servlet.ServletUtil;
  5 +import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
4 6 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
5 7 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  8 +import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
6 9 import cn.iocoder.yudao.framework.web.config.WebProperties;
7 10 import org.springframework.web.context.request.RequestAttributes;
8 11 import org.springframework.web.context.request.RequestContextHolder;
... ... @@ -25,6 +28,13 @@ public class WebFrameworkUtils {
25 28  
26 29 public static final String HEADER_TENANT_ID = "tenant-id";
27 30  
  31 + /**
  32 + * 终端的 Header
  33 + *
  34 + * @see cn.iocoder.yudao.framework.common.enums.TerminalEnum
  35 + */
  36 + public static final String HEADER_TERMINAL = "terminal";
  37 +
28 38 private static WebProperties properties;
29 39  
30 40 public WebFrameworkUtils(WebProperties webProperties) {
... ... @@ -107,6 +117,15 @@ public class WebFrameworkUtils {
107 117 return getLoginUserId(request);
108 118 }
109 119  
  120 + public static Integer getTerminal() {
  121 + HttpServletRequest request = getRequest();
  122 + if (request == null) {
  123 + return TerminalEnum.UNKNOWN.getTerminal();
  124 + }
  125 + String terminalValue = request.getHeader(HEADER_TERMINAL);
  126 + return NumberUtil.parseInt(terminalValue, TerminalEnum.UNKNOWN.getTerminal());
  127 + }
  128 +
110 129 public static void setCommonResult(ServletRequest request, CommonResult<?> result) {
111 130 request.setAttribute(REQUEST_ATTRIBUTE_COMMON_RESULT, result);
112 131 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java
... ... @@ -61,9 +61,8 @@ public class AppTradeOrderController {
61 61 @PostMapping("/create")
62 62 @Operation(summary = "创建订单")
63 63 @PreAuthenticated
64   - public CommonResult<AppTradeOrderCreateRespVO> createOrder(@Valid @RequestBody AppTradeOrderCreateReqVO createReqVO,
65   - @RequestHeader Integer terminal) {
66   - TradeOrderDO order = tradeOrderUpdateService.createOrder(getLoginUserId(), getClientIP(), createReqVO, terminal);
  64 + public CommonResult<AppTradeOrderCreateRespVO> createOrder(@Valid @RequestBody AppTradeOrderCreateReqVO createReqVO) {
  65 + TradeOrderDO order = tradeOrderUpdateService.createOrder(getLoginUserId(), createReqVO);
67 66 return success(new AppTradeOrderCreateRespVO().setId(order.getId()).setPayOrderId(order.getPayOrderId()));
68 67 }
69 68  
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java
... ... @@ -69,8 +69,7 @@ public interface TradeOrderConvert {
69 69 @Mapping(source = "calculateRespBO.price.vipPrice", target = "vipPrice"),
70 70 @Mapping(source = "calculateRespBO.price.payPrice", target = "payPrice")
71 71 })
72   - TradeOrderDO convert(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO,
73   - TradePriceCalculateRespBO calculateRespBO);
  72 + TradeOrderDO convert(Long userId, AppTradeOrderCreateReqVO createReqVO, TradePriceCalculateRespBO calculateRespBO);
74 73  
75 74 TradeOrderRespDTO convert(TradeOrderDO orderDO);
76 75  
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateService.java
... ... @@ -36,12 +36,10 @@ public interface TradeOrderUpdateService {
36 36 * 【会员】创建交易订单
37 37 *
38 38 * @param userId 登录用户
39   - * @param userIp 用户 IP 地址
40 39 * @param createReqVO 创建交易订单请求模型
41   - * @param terminal 终端 {@link TerminalEnum}
42 40 * @return 交易订单的
43 41 */
44   - TradeOrderDO createOrder(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO, Integer terminal);
  42 + TradeOrderDO createOrder(Long userId, AppTradeOrderCreateReqVO createReqVO);
45 43  
46 44 /**
47 45 * 更新交易订单已支付
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
... ... @@ -64,6 +64,8 @@ import java.util.Set;
64 64 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
65 65 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
66 66 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.minusTime;
  67 +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
  68 +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getTerminal;
67 69 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
68 70  
69 71 /**
... ... @@ -158,11 +160,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
158 160 @Override
159 161 @Transactional(rollbackFor = Exception.class)
160 162 @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CREATE)
161   - public TradeOrderDO createOrder(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO, Integer terminal) {
  163 + public TradeOrderDO createOrder(Long userId, AppTradeOrderCreateReqVO createReqVO) {
162 164 // 1.1 价格计算
163 165 TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, createReqVO);
164 166 // 1.2 构建订单
165   - TradeOrderDO order = buildTradeOrder(userId, userIp, createReqVO, calculateRespBO, terminal);
  167 + TradeOrderDO order = buildTradeOrder(userId, createReqVO, calculateRespBO);
166 168 List<TradeOrderItemDO> orderItems = buildTradeOrderItems(order, calculateRespBO);
167 169  
168 170 // 2. 订单创建前的逻辑
... ... @@ -178,15 +180,15 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
178 180 return order;
179 181 }
180 182  
181   - private TradeOrderDO buildTradeOrder(Long userId, String clientIp, AppTradeOrderCreateReqVO createReqVO,
182   - TradePriceCalculateRespBO calculateRespBO, Integer terminal) {
183   - TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(userId, clientIp, createReqVO, calculateRespBO);
  183 + private TradeOrderDO buildTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO,
  184 + TradePriceCalculateRespBO calculateRespBO) {
  185 + TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(userId, createReqVO, calculateRespBO);
184 186 order.setType(calculateRespBO.getType());
185 187 order.setNo(tradeNoRedisDAO.generate(TradeNoRedisDAO.TRADE_ORDER_NO_PREFIX));
186 188 order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
187 189 order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
188 190 order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum));
189   - order.setTerminal(terminal);
  191 + order.setUserIp(getClientIP()).setTerminal(getTerminal());
190 192 // 支付 + 退款信息
191 193 order.setAdjustPrice(0).setPayStatus(false);
192 194 order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()).setRefundPrice(0);
... ...
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java
... ... @@ -73,9 +73,8 @@ public class AppAuthController {
73 73  
74 74 @PostMapping("/sms-login")
75 75 @Operation(summary = "使用手机 + 验证码登录")
76   - public CommonResult<AppAuthLoginRespVO> smsLogin(@RequestBody @Valid AppAuthSmsLoginReqVO reqVO,
77   - @RequestHeader Integer terminal) {
78   - return success(authService.smsLogin(reqVO, terminal));
  76 + public CommonResult<AppAuthLoginRespVO> smsLogin(@RequestBody @Valid AppAuthSmsLoginReqVO reqVO) {
  77 + return success(authService.smsLogin(reqVO));
79 78 }
80 79  
81 80 @PostMapping("/send-sms-code")
... ...
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthService.java
1 1 package cn.iocoder.yudao.module.member.service.auth;
2 2  
3   -import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
4 3 import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
5 4  
6 5 import jakarta.validation.Valid;
... ... @@ -33,10 +32,9 @@ public interface MemberAuthService {
33 32 * 手机 + 验证码登陆
34 33 *
35 34 * @param reqVO 登陆信息
36   - * @param terminal 终端 {@link TerminalEnum}
37 35 * @return 登录结果
38 36 */
39   - AppAuthLoginRespVO smsLogin(@Valid AppAuthSmsLoginReqVO reqVO, Integer terminal);
  37 + AppAuthLoginRespVO smsLogin(@Valid AppAuthSmsLoginReqVO reqVO);
40 38  
41 39 /**
42 40 * 社交登录,使用 code 授权码
... ...
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java
... ... @@ -36,6 +36,7 @@ import java.util.Objects;
36 36  
37 37 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
38 38 import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
  39 +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getTerminal;
39 40 import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*;
40 41  
41 42 /**
... ... @@ -78,13 +79,13 @@ public class MemberAuthServiceImpl implements MemberAuthService {
78 79  
79 80 @Override
80 81 @Transactional
81   - public AppAuthLoginRespVO smsLogin(AppAuthSmsLoginReqVO reqVO, Integer terminal) {
  82 + public AppAuthLoginRespVO smsLogin(AppAuthSmsLoginReqVO reqVO) {
82 83 // 校验验证码
83 84 String userIp = getClientIP();
84 85 smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MEMBER_LOGIN.getScene(), userIp));
85 86  
86 87 // 获得获得注册用户
87   - MemberUserDO user = userService.createUserIfAbsent(reqVO.getMobile(), userIp, terminal);
  88 + MemberUserDO user = userService.createUserIfAbsent(reqVO.getMobile(), userIp, getTerminal());
88 89 Assert.notNull(user, "获取用户失败,结果为空");
89 90  
90 91 // 如果 socialType 非空,说明需要绑定社交用户
... ... @@ -107,10 +108,13 @@ public class MemberAuthServiceImpl implements MemberAuthService {
107 108 throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
108 109 }
109 110  
110   - // 自动登录
  111 + // 情况一:已绑定,自动登录
111 112 MemberUserDO user = userService.getUser(socialUser.getUserId());
112 113 if (user == null) {
113 114 throw exception(USER_NOT_EXISTS);
  115 + // 情况二:未绑定,注册登录
  116 + } else {
  117 + user = userService.createUser(user.getNickname(), user.getAvatar(), getClientIP(), getTerminal());
114 118 }
115 119  
116 120 // 创建 Token 令牌,记录登录日志
... ...
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java
... ... @@ -50,6 +50,18 @@ public interface MemberUserService {
50 50 MemberUserDO createUserIfAbsent(@Mobile String mobile, String registerIp, Integer terminal);
51 51  
52 52 /**
  53 + * 创建用户
  54 + * 目的:三方登录时,如果未绑定用户时,自动创建对应用户
  55 + *
  56 + * @param nickname 昵称
  57 + * @param avtar 头像
  58 + * @param registerIp 注册 IP
  59 + * @param terminal 终端 {@link TerminalEnum}
  60 + * @return 用户对象
  61 + */
  62 + MemberUserDO createUser(String nickname, String avtar, String registerIp, Integer terminal);
  63 +
  64 + /**
53 65 * 更新用户的最后登陆信息
54 66 *
55 67 * @param id 用户编号
... ...
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java
... ... @@ -81,10 +81,17 @@ public class MemberUserServiceImpl implements MemberUserService {
81 81 return user;
82 82 }
83 83 // 用户不存在,则进行创建
84   - return createUser(mobile, registerIp, terminal);
  84 + return createUser(mobile, null, null, registerIp, terminal);
85 85 }
86 86  
87   - private MemberUserDO createUser(String mobile, String registerIp, Integer terminal) {
  87 + @Override
  88 + @Transactional(rollbackFor = Exception.class)
  89 + public MemberUserDO createUser(String nickname, String avtar, String registerIp, Integer terminal) {
  90 + return createUser(null, nickname, avtar, registerIp, terminal);
  91 + }
  92 +
  93 + private MemberUserDO createUser(String mobile, String nickname, String avtar,
  94 + String registerIp, Integer terminal) {
88 95 // 生成密码
89 96 String password = IdUtil.fastSimpleUUID();
90 97 // 插入用户
... ... @@ -92,8 +99,8 @@ public class MemberUserServiceImpl implements MemberUserService {
92 99 user.setMobile(mobile);
93 100 user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
94 101 user.setPassword(encodePassword(password)); // 加密密码
95   - user.setRegisterIp(registerIp);
96   - user.setRegisterTerminal(terminal);
  102 + user.setRegisterIp(registerIp).setRegisterTerminal(terminal);
  103 + user.setNickname(nickname).setAvatar(avtar); // 基础信息
97 104 memberUserMapper.insert(user);
98 105  
99 106 // 发送 MQ 消息:用户创建
... ...
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/LoginLogCreateReqDTO.java
... ... @@ -36,9 +36,9 @@ public class LoginLogCreateReqDTO {
36 36 private Integer userType;
37 37 /**
38 38 * 用户账号
  39 + *
  40 + * 不再强制校验 username 非空,因为 Member 社交登录时,此时暂时没有 username(mobile)!
39 41 */
40   - @NotBlank(message = "用户账号不能为空")
41   - @Size(max = 30, message = "用户账号长度不能超过30个字符")
42 42 private String username;
43 43  
44 44 /**
... ...
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserRespDTO.java
... ... @@ -15,9 +15,17 @@ import lombok.NoArgsConstructor;
15 15 public class SocialUserRespDTO {
16 16  
17 17 /**
18   - * 社交用户 openid
  18 + * 社交用户的 openid
19 19 */
20 20 private String openid;
  21 + /**
  22 + * 社交用户的昵称
  23 + */
  24 + private String nickname;
  25 + /**
  26 + * 社交用户的头像
  27 + */
  28 + private String avatar;
21 29  
22 30 /**
23 31 * 关联的用户编号
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
... ... @@ -158,7 +158,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
158 158 // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
159 159 SocialUserRespDTO socialUser = socialUserService.getSocialUser(UserTypeEnum.ADMIN.getValue(), reqVO.getType(),
160 160 reqVO.getCode(), reqVO.getState());
161   - if (socialUser == null) {
  161 + if (socialUser == null || socialUser.getUserId() == null) {
162 162 throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
163 163 }
164 164  
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java
... ... @@ -99,13 +99,11 @@ public class SocialUserServiceImpl implements SocialUserService {
99 99 SocialUserDO socialUser = authSocialUser(socialType, userType, code, state);
100 100 Assert.notNull(socialUser, "社交用户不能为空");
101 101  
102   - // 如果未绑定的社交用户,则无法自动登录,进行报错
  102 + // 获得绑定用户
103 103 SocialUserBindDO socialUserBind = socialUserBindMapper.selectByUserTypeAndSocialUserId(userType,
104 104 socialUser.getId());
105   - if (socialUserBind == null) {
106   - throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
107   - }
108   - return new SocialUserRespDTO(socialUser.getOpenid(), socialUserBind.getUserId());
  105 + return new SocialUserRespDTO(socialUser.getOpenid(), socialUser.getNickname(), socialUser.getAvatar(),
  106 + socialUserBind != null ? socialUserBind.getUserId() : null);
109 107 }
110 108  
111 109 /**
... ...