SYSTEM:同步 jdk21 boot 最新代码

 INFRA:同步 jdk21 boot 最新代码
This commit is contained in:
YunaiV
2024-01-19 20:34:59 +08:00
parent dbf844592a
commit 8289a22f03
314 changed files with 1960 additions and 1189 deletions

View File

@@ -2,6 +2,9 @@ package cn.iocoder.yudao.module.member.controller.admin.address.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.time.LocalDateTime;
import java.util.*;
import jakarta.validation.constraints.*;
/**

View File

@@ -73,9 +73,8 @@ public class AppAuthController {
@PostMapping("/sms-login")
@Operation(summary = "使用手机 + 验证码登录")
public CommonResult<AppAuthLoginRespVO> smsLogin(@RequestBody @Valid AppAuthSmsLoginReqVO reqVO,
@RequestHeader Integer terminal) {
return success(authService.smsLogin(reqVO, terminal));
public CommonResult<AppAuthLoginRespVO> smsLogin(@RequestBody @Valid AppAuthSmsLoginReqVO reqVO) {
return success(authService.smsLogin(reqVO));
}
@PostMapping("/send-sms-code")
@@ -122,7 +121,7 @@ public class AppAuthController {
description = "参考 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html 文档")
public CommonResult<SocialWxJsapiSignatureRespDTO> createWeixinMpJsapiSignature(@RequestParam("url") String url) {
SocialWxJsapiSignatureRespDTO signature = socialClientApi.createWxMpJsapiSignature(
UserTypeEnum.MEMBER.getValue(), url).getCheckedData();
UserTypeEnum.MEMBER.getValue(), url);
return success(AuthConvert.INSTANCE.convert(signature));
}

View File

@@ -15,15 +15,16 @@ import jakarta.validation.constraints.NotEmpty;
@Builder
public class AppAuthWeixinMiniAppLoginReqVO {
@Schema(description = "手机 code,小程序通过 wx.getPhoneNumber 方法获得", requiredMode = Schema.RequiredMode.REQUIRED, example = "hello")
@Schema(description = "手机 code小程序通过 wx.getPhoneNumber 方法获得", requiredMode = Schema.RequiredMode.REQUIRED, example = "hello")
@NotEmpty(message = "手机 code 不能为空")
private String phoneCode;
@Schema(description = "登录 code,小程序通过 wx.login 方法获得", requiredMode = Schema.RequiredMode.REQUIRED, example = "word")
@Schema(description = "登录 code小程序通过 wx.login 方法获得", requiredMode = Schema.RequiredMode.REQUIRED, example = "word")
@NotEmpty(message = "登录 code 不能为空")
private String loginCode;
@Schema(description = "state", requiredMode = Schema.RequiredMode.REQUIRED, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62")
@NotEmpty(message = "state 不能为空")
private String state;
}

View File

@@ -3,7 +3,9 @@ package cn.iocoder.yudao.module.member.controller.app.point;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
import cn.iocoder.yudao.module.member.controller.app.point.vo.AppMemberPointRecordPageReqVO;
import cn.iocoder.yudao.module.member.controller.app.point.vo.AppMemberPointRecordRespVO;
import cn.iocoder.yudao.module.member.convert.point.MemberPointRecordConvert;
import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointRecordDO;
@@ -33,9 +35,10 @@ public class AppMemberPointRecordController {
@GetMapping("/page")
@Operation(summary = "获得用户积分记录分页")
@PreAuthenticated
public CommonResult<PageResult<AppMemberPointRecordRespVO>> getPointRecordPage(@Valid PageParam pageVO) {
PageResult<MemberPointRecordDO> pageResult = pointRecordService.getPointRecordPage(getLoginUserId(), pageVO);
return success(MemberPointRecordConvert.INSTANCE.convertPage02(pageResult));
public CommonResult<PageResult<AppMemberPointRecordRespVO>> getPointRecordPage(
@Valid AppMemberPointRecordPageReqVO pageReqVO) {
PageResult<MemberPointRecordDO> pageResult = pointRecordService.getPointRecordPage(getLoginUserId(), pageReqVO);
return success(BeanUtils.toBean(pageResult, AppMemberPointRecordRespVO.class));
}
}

View File

@@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.member.controller.app.point.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "用户 App - 用户积分记录分页 Request VO")
@Data
public class AppMemberPointRecordPageReqVO extends PageParam {
@Schema(description = "是否增加积分", example = "true")
private Boolean addStatus; // true - 增加false - 减少null - 不筛选
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "创建时间")
private LocalDateTime[] createTime;
}

View File

@@ -2,11 +2,16 @@ package cn.iocoder.yudao.module.member.controller.app.social;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserBindReqVO;
import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserRespVO;
import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO;
import cn.iocoder.yudao.module.member.convert.social.SocialUserConvert;
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.api.social.dto.SocialUserUnbindReqDTO;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.validation.annotation.Validated;
@@ -15,11 +20,12 @@ import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "用户 App - 社交用户")
@RestController
@RequestMapping("/system/social-user")
@RequestMapping("/member/social-user")
@Validated
public class AppSocialUserController {
@@ -28,17 +34,30 @@ public class AppSocialUserController {
@PostMapping("/bind")
@Operation(summary = "社交绑定,使用 code 授权码")
public CommonResult<Boolean> socialBind(@RequestBody @Valid AppSocialUserBindReqVO reqVO) {
socialUserApi.bindSocialUser(SocialUserConvert.INSTANCE.convert(getLoginUserId(), UserTypeEnum.MEMBER.getValue(), reqVO));
return CommonResult.success(true);
public CommonResult<String> socialBind(@RequestBody @Valid AppSocialUserBindReqVO reqVO) {
SocialUserBindReqDTO reqDTO = new SocialUserBindReqDTO(getLoginUserId(), UserTypeEnum.MEMBER.getValue(),
reqVO.getType(), reqVO.getCode(), reqVO.getState());
String openid = socialUserApi.bindSocialUser(reqDTO);
return success(openid);
}
@DeleteMapping("/unbind")
@Operation(summary = "取消社交绑定")
@PreAuthenticated
public CommonResult<Boolean> socialUnbind(@RequestBody AppSocialUserUnbindReqVO reqVO) {
socialUserApi.unbindSocialUser(SocialUserConvert.INSTANCE.convert(getLoginUserId(), UserTypeEnum.MEMBER.getValue(), reqVO));
return CommonResult.success(true);
SocialUserUnbindReqDTO reqDTO = new SocialUserUnbindReqDTO(getLoginUserId(), UserTypeEnum.MEMBER.getValue(),
reqVO.getType(), reqVO.getOpenid());
socialUserApi.unbindSocialUser(reqDTO);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得社交用户")
@Parameter(name = "type", description = "社交平台的类型,参见 SocialTypeEnum 枚举值", required = true, example = "10")
@PreAuthenticated
public CommonResult<AppSocialUserRespVO> getSocialUser(@RequestParam("type") Integer type) {
SocialUserRespDTO socialUser = socialUserApi.getSocialUserByUserId(UserTypeEnum.MEMBER.getValue(), getLoginUserId(), type);
return success(BeanUtils.toBean(socialUser, AppSocialUserRespVO.class));
}
}

View File

@@ -13,9 +13,6 @@ import jakarta.validation.constraints.NotNull;
@Schema(description = "用户 APP - 社交绑定 Request VO使用 code 授权码")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AppSocialUserBindReqVO {
@Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")

View File

@@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.member.controller.app.social.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "用户 APP - 社交用户 Response VO")
@Data
public class AppSocialUserRespVO {
@Schema(description = "社交用户的 openid", requiredMode = Schema.RequiredMode.REQUIRED, example = "IPRmJ0wvBptiPIlGEZiPewGwiEiE")
private String openid;
@Schema(description = "社交用户的昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
private String nickname;
@Schema(description = "社交用户的头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
private String avatar;
}

View File

@@ -13,9 +13,6 @@ import jakarta.validation.constraints.NotNull;
@Schema(description = "用户 APP - 取消社交绑定 Request VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AppSocialUserUnbindReqVO {
@Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")

View File

@@ -57,17 +57,25 @@ public class AppMemberUserController {
return success(true);
}
@PutMapping("/update-mobile-by-weixin")
@Operation(summary = "基于微信小程序的授权码,修改用户手机")
@PreAuthenticated
public CommonResult<Boolean> updateUserMobileByWeixin(@RequestBody @Valid AppMemberUserUpdateMobileByWeixinReqVO reqVO) {
userService.updateUserMobileByWeixin(getLoginUserId(), reqVO);
return success(true);
}
@PutMapping("/update-password")
@Operation(summary = "修改用户密码", description = "用户修改密码时使用")
@PreAuthenticated
public CommonResult<Boolean> updatePassword(@RequestBody @Valid AppMemberUserUpdatePasswordReqVO reqVO) {
public CommonResult<Boolean> updateUserPassword(@RequestBody @Valid AppMemberUserUpdatePasswordReqVO reqVO) {
userService.updateUserPassword(getLoginUserId(), reqVO);
return success(true);
}
@PutMapping("/reset-password")
@Operation(summary = "重置密码", description = "用户忘记密码时使用")
public CommonResult<Boolean> resetPassword(@RequestBody @Valid AppMemberUserResetPasswordReqVO reqVO) {
public CommonResult<Boolean> resetUserPassword(@RequestBody @Valid AppMemberUserResetPasswordReqVO reqVO) {
userService.resetUserPassword(reqVO);
return success(true);
}

View File

@@ -20,6 +20,9 @@ public class AppMemberUserInfoRespVO {
@Schema(description = "用户手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300")
private String mobile;
@Schema(description = "用户性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer sex;
@Schema(description = "积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private Integer point;

View File

@@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.member.controller.app.user.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
@Schema(description = "用户 APP - 基于微信小程序的授权码,修改手机 Request VO")
@Data
public class AppMemberUserUpdateMobileByWeixinReqVO {
@Schema(description = "手机 code小程序通过 wx.getPhoneNumber 方法获得",
requiredMode = Schema.RequiredMode.REQUIRED, example = "hello")
@NotEmpty(message = "手机 code 不能为空")
private String code;
}

View File

@@ -14,9 +14,6 @@ import jakarta.validation.constraints.Pattern;
@Schema(description = "用户 APP - 修改手机 Request VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AppMemberUserUpdateMobileReqVO {
@Schema(description = "手机验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@@ -25,14 +22,13 @@ public class AppMemberUserUpdateMobileReqVO {
@Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
private String code;
@Schema(description = "手机号",requiredMode = Schema.RequiredMode.REQUIRED,example = "15823654487")
@Schema(description = "手机号",requiredMode = Schema.RequiredMode.REQUIRED, example = "15823654487")
@NotBlank(message = "手机号不能为空")
@Length(min = 8, max = 11, message = "手机号码长度为 8-11 位")
@Mobile
private String mobile;
@Schema(description = "原手机验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotEmpty(message = "原手机验证码不能为空")
@Schema(description = "原手机验证码", example = "1024")
@Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
@Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
private String oldCode;

View File

@@ -1,5 +1,7 @@
package cn.iocoder.yudao.module.member.controller.app.user.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.system.enums.common.SexEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.hibernate.validator.constraints.URL;
@@ -15,4 +17,7 @@ public class AppMemberUserUpdateReqVO {
@URL(message = "头像必须是 URL 格式")
private String avatar;
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer sex;
}

View File

@@ -34,6 +34,4 @@ public interface MemberPointRecordConvert {
}
PageResult<MemberPointRecordRespVO> convertPage(PageResult<MemberPointRecordDO> pageResult);
PageResult<AppMemberPointRecordRespVO> convertPage02(PageResult<MemberPointRecordDO> pageResult);
}

View File

@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.module.member.controller.admin.signin.vo.record.MemberSignInRecordRespVO;
import cn.iocoder.yudao.module.member.controller.app.signin.vo.record.AppMemberSignInRecordRespVO;
import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInConfigDO;
@@ -47,10 +48,9 @@ public interface MemberSignInRecordConvert {
// 1. 计算是第几天签到
configs.sort(Comparator.comparing(MemberSignInConfigDO::getDay));
MemberSignInConfigDO lastConfig = CollUtil.getLast(configs); // 最大签到天数配置
// 1.2. 计算今天是第几天签到
// 1.2. 计算今天是第几天签到 (只有连续签到才加否则重置为 1)
int day = 1;
// TODO @puhui999要判断是不是昨天签到的是否是昨天的判断可以抽个方法到 util 里
if (lastRecord != null) {
if (lastRecord != null && DateUtils.isYesterday(lastRecord.getCreateTime())) {
day = lastRecord.getDay() + 1;
}
// 1.3 判断是否超出了最大签到配置

View File

@@ -1,19 +0,0 @@
package cn.iocoder.yudao.module.member.convert.social;
import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserBindReqVO;
import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface SocialUserConvert {
SocialUserConvert INSTANCE = Mappers.getMapper(SocialUserConvert.class);
SocialUserBindReqDTO convert(Long userId, Integer userType, AppSocialUserBindReqVO reqVO);
SocialUserUnbindReqDTO convert(Long userId, Integer userType, AppSocialUserUnbindReqVO reqVO);
}

View File

@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.member.controller.admin.point.vo.recrod.MemberPointRecordPageReqVO;
import cn.iocoder.yudao.module.member.controller.app.point.vo.AppMemberPointRecordPageReqVO;
import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointRecordDO;
import org.apache.ibatis.annotations.Mapper;
@@ -27,9 +28,14 @@ public interface MemberPointRecordMapper extends BaseMapperX<MemberPointRecordDO
.orderByDesc(MemberPointRecordDO::getId));
}
default PageResult<MemberPointRecordDO> selectPage(Long userId, PageParam pageVO) {
return selectPage(pageVO, new LambdaQueryWrapperX<MemberPointRecordDO>()
default PageResult<MemberPointRecordDO> selectPage(Long userId, AppMemberPointRecordPageReqVO pageReqVO) {
return selectPage(pageReqVO, new LambdaQueryWrapperX<MemberPointRecordDO>()
.eq(MemberPointRecordDO::getUserId, userId)
.betweenIfPresent(MemberPointRecordDO::getCreateTime, pageReqVO.getCreateTime())
.gt(Boolean.TRUE.equals(pageReqVO.getAddStatus()),
MemberPointRecordDO::getPoint, 0)
.lt(Boolean.FALSE.equals(pageReqVO.getAddStatus()),
MemberPointRecordDO::getPoint, 0)
.orderByDesc(MemberPointRecordDO::getId));
}

View File

@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.member.framework.web.config;
import cn.iocoder.yudao.framework.swagger.config.YudaoSwaggerAutoConfiguration;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* member 模块的 web 组件的 Configuration
*
* @author 芋道源码
*/
@Configuration(proxyBeanMethods = false)
public class MemberWebConfiguration {
/**
* member 模块的 API 分组
*/
@Bean
public GroupedOpenApi memberGroupedOpenApi() {
return YudaoSwaggerAutoConfiguration.buildGroupedOpenApi("member");
}
}

View File

@@ -0,0 +1,4 @@
/**
* member 模块的 web 配置
*/
package cn.iocoder.yudao.module.member.framework.web;

View File

@@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.member.service.auth;
import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
import jakarta.validation.Valid;
@@ -33,10 +32,9 @@ public interface MemberAuthService {
* 手机 + 验证码登陆
*
* @param reqVO 登陆信息
* @param terminal 终端 {@link TerminalEnum}
* @return 登录结果
*/
AppAuthLoginRespVO smsLogin(@Valid AppAuthSmsLoginReqVO reqVO, Integer terminal);
AppAuthLoginRespVO smsLogin(@Valid AppAuthSmsLoginReqVO reqVO);
/**
* 社交登录,使用 code 授权码

View File

@@ -36,6 +36,7 @@ import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getTerminal;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*;
/**
@@ -69,7 +70,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
String openid = null;
if (reqVO.getSocialType() != null) {
openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())).getCheckedData();
reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState()));
}
// 创建 Token 令牌,记录登录日志
@@ -78,20 +79,20 @@ public class MemberAuthServiceImpl implements MemberAuthService {
@Override
@Transactional
public AppAuthLoginRespVO smsLogin(AppAuthSmsLoginReqVO reqVO, Integer terminal) {
public AppAuthLoginRespVO smsLogin(AppAuthSmsLoginReqVO reqVO) {
// 校验验证码
String userIp = getClientIP();
smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MEMBER_LOGIN.getScene(), userIp));
// 获得获得注册用户
MemberUserDO user = userService.createUserIfAbsent(reqVO.getMobile(), userIp, terminal);
MemberUserDO user = userService.createUserIfAbsent(reqVO.getMobile(), userIp, getTerminal());
Assert.notNull(user, "获取用户失败,结果为空");
// 如果 socialType 非空,说明需要绑定社交用户
String openid = null;
if (reqVO.getSocialType() != null) {
openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())).getCheckedData();
reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState()));
}
// 创建 Token 令牌,记录登录日志
@@ -99,16 +100,25 @@ public class MemberAuthServiceImpl implements MemberAuthService {
}
@Override
@Transactional
public AppAuthLoginRespVO socialLogin(AppAuthSocialLoginReqVO reqVO) {
// 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
SocialUserRespDTO socialUser = socialUserApi.getSocialUser(UserTypeEnum.MEMBER.getValue(), reqVO.getType(),
reqVO.getCode(), reqVO.getState()).getCheckedData();
SocialUserRespDTO socialUser = socialUserApi.getSocialUserByCode(UserTypeEnum.MEMBER.getValue(), reqVO.getType(),
reqVO.getCode(), reqVO.getState());
if (socialUser == null) {
throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
throw exception(AUTH_SOCIAL_USER_NOT_FOUND);
}
// 自动登录
MemberUserDO user = userService.getUser(socialUser.getUserId());
// 情况一:已绑定,直接读取用户信息
MemberUserDO user;
if (socialUser.getUserId() != null) {
user = userService.getUser(socialUser.getUserId());
// 情况二:未绑定,注册用户 + 绑定用户
} else {
user = userService.createUser(socialUser.getNickname(), socialUser.getAvatar(), getClientIP(), getTerminal());
socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
reqVO.getType(), reqVO.getCode(), reqVO.getState()));
}
if (user == null) {
throw exception(USER_NOT_EXISTS);
}
@@ -121,7 +131,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
public AppAuthLoginRespVO weixinMiniAppLogin(AppAuthWeixinMiniAppLoginReqVO reqVO) {
// 获得对应的手机号信息
SocialWxPhoneNumberInfoRespDTO phoneNumberInfo = socialClientApi.getWxMaPhoneNumberInfo(
UserTypeEnum.MEMBER.getValue(), reqVO.getPhoneCode()).getCheckedData();
UserTypeEnum.MEMBER.getValue(), reqVO.getPhoneCode());
Assert.notNull(phoneNumberInfo, "获得手机信息失败,结果为空");
// 获得获得注册用户
@@ -131,7 +141,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
// 绑定社交用户
String openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
SocialTypeEnum.WECHAT_MINI_APP.getType(), reqVO.getLoginCode(), reqVO.getState())).getCheckedData();
SocialTypeEnum.WECHAT_MINI_APP.getType(), reqVO.getLoginCode(), reqVO.getState()));
// 创建 Token 令牌,记录登录日志
return createTokenAfterLoginSuccess(user, user.getMobile(), LoginLogTypeEnum.LOGIN_SOCIAL, openid);
@@ -144,14 +154,14 @@ public class MemberAuthServiceImpl implements MemberAuthService {
// 创建 Token 令牌
OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.createAccessToken(new OAuth2AccessTokenCreateReqDTO()
.setUserId(user.getId()).setUserType(getUserType().getValue())
.setClientId(OAuth2ClientConstants.CLIENT_ID_DEFAULT)).getCheckedData();
.setClientId(OAuth2ClientConstants.CLIENT_ID_DEFAULT));
// 构建返回结果
return AuthConvert.INSTANCE.convert(accessTokenRespDTO, openid);
}
@Override
public String getSocialAuthorizeUrl(Integer type, String redirectUri) {
return socialClientApi.getAuthorizeUrl(type, UserTypeEnum.MEMBER.getValue(), redirectUri).getCheckedData();
return socialClientApi.getAuthorizeUrl(type, UserTypeEnum.MEMBER.getValue(), redirectUri);
}
private MemberUserDO login0(String mobile, String password) {
@@ -195,7 +205,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
@Override
public void logout(String token) {
// 删除访问令牌
OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.removeAccessToken(token).getCheckedData();
OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.removeAccessToken(token);
if (accessTokenRespDTO == null) {
return;
}
@@ -214,11 +224,17 @@ public class MemberAuthServiceImpl implements MemberAuthService {
}
// 情况 2如果是重置密码场景需要校验手机号是存在的
if (Objects.equals(reqVO.getScene(), SmsSceneEnum.MEMBER_RESET_PASSWORD.getScene())) {
MemberUserDO user= userService.getUserByMobile(reqVO.getMobile());
MemberUserDO user = userService.getUserByMobile(reqVO.getMobile());
if (user == null) {
throw exception(USER_MOBILE_NOT_EXISTS);
}
}
// 情况 3如果是修改密码场景需要查询手机号无需前端传递
if (Objects.equals(reqVO.getScene(), SmsSceneEnum.MEMBER_UPDATE_PASSWORD.getScene())) {
MemberUserDO user = userService.getUser(userId);
// TODO 芋艿:后续 member user 手机非强绑定,这块需要做下调整;
reqVO.setMobile(user.getMobile());
}
// 执行发送
smsCodeApi.sendSmsCode(AuthConvert.INSTANCE.convert(reqVO).setCreateIp(getClientIP()));
@@ -232,7 +248,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
@Override
public AppAuthLoginRespVO refreshToken(String refreshToken) {
OAuth2AccessTokenRespDTO accessTokenDO = oauth2TokenApi.refreshAccessToken(refreshToken,
OAuth2ClientConstants.CLIENT_ID_DEFAULT).getCheckedData();
OAuth2ClientConstants.CLIENT_ID_DEFAULT);
return AuthConvert.INSTANCE.convert(accessTokenDO, null);
}

View File

@@ -64,7 +64,6 @@ public interface MemberGroupService {
*/
PageResult<MemberGroupDO> getGroupPage(MemberGroupPageReqVO pageReqVO);
/**
* 获得指定状态的用户分组列表
*
@@ -73,7 +72,6 @@ public interface MemberGroupService {
*/
List<MemberGroupDO> getGroupListByStatus(Integer status);
/**
* 获得开启状态的用户分组列表
*

View File

@@ -12,6 +12,8 @@ import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import jakarta.annotation.Resource;
import java.util.Collection;
import java.util.List;
/**
* 会员经验记录 Service 实现类

View File

@@ -1,8 +1,8 @@
package cn.iocoder.yudao.module.member.service.point;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.controller.admin.point.vo.recrod.MemberPointRecordPageReqVO;
import cn.iocoder.yudao.module.member.controller.app.point.vo.AppMemberPointRecordPageReqVO;
import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointRecordDO;
import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum;
@@ -25,10 +25,10 @@ public interface MemberPointRecordService {
* 【会员】获得积分记录分页
*
* @param userId 用户编号
* @param pageVO 分页查询
* @param pageReqVO 分页查询
* @return 签到记录分页
*/
PageResult<MemberPointRecordDO> getPointRecordPage(Long userId, PageParam pageVO);
PageResult<MemberPointRecordDO> getPointRecordPage(Long userId, AppMemberPointRecordPageReqVO pageReqVO);
/**
* 创建用户积分记录

View File

@@ -5,6 +5,7 @@ import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.controller.admin.point.vo.recrod.MemberPointRecordPageReqVO;
import cn.iocoder.yudao.module.member.controller.app.point.vo.AppMemberPointRecordPageReqVO;
import cn.iocoder.yudao.module.member.dal.dataobject.point.MemberPointRecordDO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import cn.iocoder.yudao.module.member.dal.mysql.point.MemberPointRecordMapper;
@@ -59,8 +60,8 @@ public class MemberPointRecordServiceImpl implements MemberPointRecordService {
}
@Override
public PageResult<MemberPointRecordDO> getPointRecordPage(Long userId, PageParam pageVO) {
return memberPointRecordMapper.selectPage(userId, pageVO);
public PageResult<MemberPointRecordDO> getPointRecordPage(Long userId, AppMemberPointRecordPageReqVO pageReqVO) {
return memberPointRecordMapper.selectPage(userId, pageReqVO);
}
@Override

View File

@@ -19,14 +19,12 @@ import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum;
import cn.iocoder.yudao.module.member.service.level.MemberLevelService;
import cn.iocoder.yudao.module.member.service.point.MemberPointRecordService;
import cn.iocoder.yudao.module.member.service.user.MemberUserService;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import jakarta.annotation.Resource;
import java.time.LocalDate;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
@@ -77,50 +75,15 @@ public class MemberSignInRecordServiceImpl implements MemberSignInRecordService
}
summary.setTodaySignIn(DateUtils.isToday(lastRecord.getCreateTime()));
// 4. 校验今天是否签到,没有签到则直接返回
// 4.1 校验今天是否签到,没有签到则直接返回
if (!summary.getTodaySignIn()) {
return summary;
}
// 4.1. 判断连续签到天数
// TODO @puhui999连续签到可以基于 lastRecord 的 day 和当前时间判断呀?按 day 统计连续签到天数可能不准确
// 1. day 只是记录第几天签到的有可能不连续,比如第一次签到是周一,第二次签到是周三这样 lastRecord 的 day 为 2 但是并不是连续的两天
// 2. day 超出签到规则的最大天数会重置到从第一天开始签到(我理解为开始下一轮,类似一周签到七天七天结束下周又从周一开始签到)
// 1. 回复周三签到day 要归 1 呀。连续签到哈;
List<MemberSignInRecordDO> signInRecords = signInRecordMapper.selectListByUserId(userId);
signInRecords.sort(Comparator.comparing(MemberSignInRecordDO::getCreateTime).reversed()); // 根据签到时间倒序
summary.setContinuousDay(calculateConsecutiveDays(signInRecords));
// 4.2 连续签到天数
summary.setContinuousDay(lastRecord.getDay());
return summary;
}
/**
* 计算连续签到天数
*
* @param signInRecords 签到记录列表
* @return int 连续签到天数
*/
public int calculateConsecutiveDays(List<MemberSignInRecordDO> signInRecords) {
int consecutiveDays = 1; // 初始连续天数为1
LocalDate previousDate = null;
for (MemberSignInRecordDO record : signInRecords) {
LocalDate currentDate = record.getCreateTime().toLocalDate();
if (previousDate != null) {
// 检查相邻两个日期是否连续
if (currentDate.minusDays(1).isEqual(previousDate)) {
consecutiveDays++;
} else {
// 如果日期不连续,停止遍历
break;
}
}
previousDate = currentDate;
}
return consecutiveDays;
}
@Override
public PageResult<MemberSignInRecordDO> getSignInRecordPage(MemberSignInRecordPageReqVO pageReqVO) {
// 根据用户昵称查询出用户ids

View File

@@ -5,10 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.validation.Mobile;
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO;
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserResetPasswordReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateMobileReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdatePasswordReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.*;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import jakarta.validation.Valid;
@@ -49,6 +46,18 @@ public interface MemberUserService {
*/
MemberUserDO createUserIfAbsent(@Mobile String mobile, String registerIp, Integer terminal);
/**
* 创建用户
* 目的:三方登录时,如果未绑定用户时,自动创建对应用户
*
* @param nickname 昵称
* @param avtar 头像
* @param registerIp 注册 IP
* @param terminal 终端 {@link TerminalEnum}
* @return 用户对象
*/
MemberUserDO createUser(String nickname, String avtar, String registerIp, Integer terminal);
/**
* 更新用户的最后登陆信息
*
@@ -82,13 +91,21 @@ public interface MemberUserService {
void updateUser(Long userId, AppMemberUserUpdateReqVO reqVO);
/**
* 【会员】修改手机
* 【会员】修改手机,基于手机验证码
*
* @param userId 用户编号
* @param reqVO 请求信息
*/
void updateUserMobile(Long userId, AppMemberUserUpdateMobileReqVO reqVO);
/**
* 【会员】修改手机,基于微信小程序的授权码
*
* @param userId 用户编号
* @param reqVO 请求信息
*/
void updateUserMobileByWeixin(Long userId, AppMemberUserUpdateMobileByWeixinReqVO reqVO);
/**
* 【会员】修改密码
*
@@ -169,4 +186,5 @@ public interface MemberUserService {
* @return 更新结果
*/
boolean updateUserPoint(Long userId, Integer point);
}

View File

@@ -2,17 +2,15 @@ package cn.iocoder.yudao.module.member.service.user;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.*;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO;
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserResetPasswordReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateMobileReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdatePasswordReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppMemberUserUpdateReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.*;
import cn.iocoder.yudao.module.member.convert.auth.AuthConvert;
import cn.iocoder.yudao.module.member.convert.user.MemberUserConvert;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
@@ -20,6 +18,8 @@ import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
import cn.iocoder.yudao.module.member.mq.producer.user.MemberUserProducer;
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
import cn.iocoder.yudao.module.system.api.social.dto.SocialWxPhoneNumberInfoRespDTO;
import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
@@ -55,6 +55,9 @@ public class MemberUserServiceImpl implements MemberUserService {
@Resource
private SmsCodeApi smsCodeApi;
@Resource
private SocialClientApi socialClientApi;
@Resource
private PasswordEncoder passwordEncoder;
@@ -80,10 +83,17 @@ public class MemberUserServiceImpl implements MemberUserService {
return user;
}
// 用户不存在,则进行创建
return createUser(mobile, registerIp, terminal);
return createUser(mobile, null, null, registerIp, terminal);
}
private MemberUserDO createUser(String mobile, String registerIp, Integer terminal) {
@Override
@Transactional(rollbackFor = Exception.class)
public MemberUserDO createUser(String nickname, String avtar, String registerIp, Integer terminal) {
return createUser(null, nickname, avtar, registerIp, terminal);
}
private MemberUserDO createUser(String mobile, String nickname, String avtar,
String registerIp, Integer terminal) {
// 生成密码
String password = IdUtil.fastSimpleUUID();
// 插入用户
@@ -91,8 +101,12 @@ public class MemberUserServiceImpl implements MemberUserService {
user.setMobile(mobile);
user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
user.setPassword(encodePassword(password)); // 加密密码
user.setRegisterIp(registerIp);
user.setRegisterTerminal(terminal);
user.setRegisterIp(registerIp).setRegisterTerminal(terminal);
user.setNickname(nickname).setAvatar(avtar); // 基础信息
if (StrUtil.isEmpty(nickname)) {
// 昵称为空时,随机一个名字,避免一些依赖 nickname 的逻辑报错,或者有点丑。例如说,短信发送有昵称时~
user.setNickname("用户" + RandomUtil.randomNumbers(6));
}
memberUserMapper.insert(user);
// 发送 MQ 消息:用户创建
@@ -128,29 +142,45 @@ public class MemberUserServiceImpl implements MemberUserService {
@Override
public void updateUser(Long userId, AppMemberUserUpdateReqVO reqVO) {
memberUserMapper.updateById(new MemberUserDO().setId(userId)
.setNickname(reqVO.getNickname()).setAvatar(reqVO.getAvatar()));
MemberUserDO updateObj = BeanUtils.toBean(reqVO, MemberUserDO.class).setId(userId);
memberUserMapper.updateById(updateObj);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateUserMobile(Long userId, AppMemberUserUpdateMobileReqVO reqVO) {
// 检测用户是否存在
// 1.1 检测用户是否存在
MemberUserDO user = validateUserExists(userId);
// 校验新手机是否已经被绑定
// 1.2 校验新手机是否已经被绑定
validateMobileUnique(null, reqVO.getMobile());
// 校验旧手机和旧验证码
smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(user.getMobile()).setCode(reqVO.getOldCode())
.setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP()));
// 使用新验证码
// 2.1 校验旧手机和旧验证码
// 补充说明:从安全性来说,老手机也校验 oldCode 验证码会更安全。但是由于 uni-app 商城界面暂时没做,所以这里不强制校验
if (StrUtil.isNotEmpty(reqVO.getOldCode())) {
smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(user.getMobile()).setCode(reqVO.getOldCode())
.setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP()));
}
// 2.2 使用新验证码
smsCodeApi.useSmsCode(new SmsCodeUseReqDTO().setMobile(reqVO.getMobile()).setCode(reqVO.getCode())
.setScene(SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene()).setUsedIp(getClientIP()));
// 更新用户手机
// 3. 更新用户手机
memberUserMapper.updateById(MemberUserDO.builder().id(userId).mobile(reqVO.getMobile()).build());
}
@Override
public void updateUserMobileByWeixin(Long userId, AppMemberUserUpdateMobileByWeixinReqVO reqVO) {
// 1.1 获得对应的手机号信息
SocialWxPhoneNumberInfoRespDTO phoneNumberInfo = socialClientApi.getWxMaPhoneNumberInfo(
UserTypeEnum.MEMBER.getValue(), reqVO.getCode());
Assert.notNull(phoneNumberInfo, "获得手机信息失败,结果为空");
// 1.2 校验新手机是否已经被绑定
validateMobileUnique(userId, phoneNumberInfo.getPhoneNumber());
// 2. 更新用户手机
memberUserMapper.updateById(MemberUserDO.builder().id(userId).mobile(phoneNumberInfo.getPhoneNumber()).build());
}
@Override
public void updateUserPassword(Long userId, AppMemberUserUpdatePasswordReqVO reqVO) {
// 检测用户是否存在