mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-11-01 02:38:43 +08:00 
			
		
		
		
	增加短信验证码的功能
This commit is contained in:
		| @@ -6,3 +6,12 @@ Content-Type: application/json | |||||||
|   "mobile": "15601691300", |   "mobile": "15601691300", | ||||||
|   "password": "admin123" |   "password": "admin123" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | ### 请求 /send-sms-code 接口 => 成功 | ||||||
|  | POST {{userServerUrl}}/send-sms-code | ||||||
|  | Content-Type: application/json | ||||||
|  |  | ||||||
|  | { | ||||||
|  |   "mobile": "15601691300", | ||||||
|  |   "scene": 1 | ||||||
|  | } | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ package cn.iocoder.yudao.userserver.modules.system.controller.auth; | |||||||
| import cn.iocoder.yudao.framework.common.pojo.CommonResult; | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||||||
| import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.*; | import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.*; | ||||||
| import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService; | import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService; | ||||||
|  | import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; | ||||||
| import io.swagger.annotations.Api; | import io.swagger.annotations.Api; | ||||||
| import io.swagger.annotations.ApiImplicitParam; | import io.swagger.annotations.ApiImplicitParam; | ||||||
| import io.swagger.annotations.ApiImplicitParams; | import io.swagger.annotations.ApiImplicitParams; | ||||||
| @@ -27,6 +28,8 @@ public class SysAuthController { | |||||||
|  |  | ||||||
|     @Resource |     @Resource | ||||||
|     private SysAuthService authService; |     private SysAuthService authService; | ||||||
|  |     @Resource | ||||||
|  |     private SysSmsCodeService smsCodeService; | ||||||
|  |  | ||||||
|     @PostMapping("/login") |     @PostMapping("/login") | ||||||
|     @ApiOperation("使用手机 + 密码登录") |     @ApiOperation("使用手机 + 密码登录") | ||||||
| @@ -45,10 +48,8 @@ public class SysAuthController { | |||||||
|     @PostMapping("/send-sms-code") |     @PostMapping("/send-sms-code") | ||||||
|     @ApiOperation("发送手机验证码") |     @ApiOperation("发送手机验证码") | ||||||
|     public CommonResult<Boolean> sendSmsCode(@RequestBody @Valid MbrAuthSendSmsReqVO reqVO) { |     public CommonResult<Boolean> sendSmsCode(@RequestBody @Valid MbrAuthSendSmsReqVO reqVO) { | ||||||
| //        passportManager.sendSmsCode(sendSmsCodeDTO, HttpUtil.getIp(request)); |         smsCodeService.sendSmsCode(reqVO.getMobile(), reqVO.getScene(), getClientIP()); | ||||||
| //        // 返回成功 |         return success(true); | ||||||
| //        return success(true); |  | ||||||
|         return null; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @PostMapping("/reset-password") |     @PostMapping("/reset-password") | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo; | package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo; | ||||||
|  |  | ||||||
|  | import cn.iocoder.yudao.framework.common.validation.InEnum; | ||||||
| import cn.iocoder.yudao.framework.common.validation.Mobile; | import cn.iocoder.yudao.framework.common.validation.Mobile; | ||||||
|  | import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; | ||||||
| import io.swagger.annotations.ApiModel; | import io.swagger.annotations.ApiModel; | ||||||
| import io.swagger.annotations.ApiModelProperty; | import io.swagger.annotations.ApiModelProperty; | ||||||
| import lombok.Data; | import lombok.Data; | ||||||
| @@ -19,6 +21,7 @@ public class MbrAuthSendSmsReqVO { | |||||||
|  |  | ||||||
|     @ApiModelProperty(value = "发送场景", example = "1", notes = "对应 MbrSmsSceneEnum 枚举") |     @ApiModelProperty(value = "发送场景", example = "1", notes = "对应 MbrSmsSceneEnum 枚举") | ||||||
|     @NotNull(message = "发送场景不能为空") |     @NotNull(message = "发送场景不能为空") | ||||||
|  |     @InEnum(SysSmsSceneEnum.class) | ||||||
|     private Integer scene; |     private Integer scene; | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1 @@ | |||||||
|  | package cn.iocoder.yudao.userserver.modules.system.dal.dataobject; | ||||||
| @@ -1,9 +1,8 @@ | |||||||
| package cn.iocoder.yudao.userserver.modules.member.dal.mysql.sms; | package cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms; | ||||||
| 
 | 
 | ||||||
| import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | ||||||
| import com.baomidou.mybatisplus.annotation.TableName; | import com.baomidou.mybatisplus.annotation.TableName; | ||||||
| import lombok.Data; | import lombok.*; | ||||||
| import lombok.EqualsAndHashCode; |  | ||||||
| import lombok.experimental.Accessors; | import lombok.experimental.Accessors; | ||||||
| 
 | 
 | ||||||
| import java.util.Date; | import java.util.Date; | ||||||
| @@ -15,11 +14,13 @@ import java.util.Date; | |||||||
|  * |  * | ||||||
|  * @author 芋道源码 |  * @author 芋道源码 | ||||||
|  */ |  */ | ||||||
| @TableName("mbr_sms_code") | @TableName("sys_sms_code") | ||||||
| @Data | @Data | ||||||
| @EqualsAndHashCode(callSuper = true) | @EqualsAndHashCode(callSuper = true) | ||||||
| @Accessors(chain = true) | @Builder | ||||||
| public class MbrSmsCodeDO extends BaseDO { | @NoArgsConstructor | ||||||
|  | @AllArgsConstructor | ||||||
|  | public class SysSmsCodeDO extends BaseDO { | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 编号 |      * 编号 | ||||||
| @@ -36,7 +37,7 @@ public class MbrSmsCodeDO extends BaseDO { | |||||||
|     /** |     /** | ||||||
|      * 发送场景 |      * 发送场景 | ||||||
|      * |      * | ||||||
|      * 枚举 {@link MbrSmsCodeDO} |      * 枚举 {@link SysSmsCodeDO} | ||||||
|      */ |      */ | ||||||
|     private Integer scene; |     private Integer scene; | ||||||
|     /** |     /** | ||||||
| @@ -0,0 +1 @@ | |||||||
|  | package cn.iocoder.yudao.userserver.modules.system.dal.mysql; | ||||||
| @@ -0,0 +1,26 @@ | |||||||
|  | package cn.iocoder.yudao.userserver.modules.system.dal.mysql.sms; | ||||||
|  |  | ||||||
|  | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||||||
|  | import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; | ||||||
|  | import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO; | ||||||
|  | import org.apache.ibatis.annotations.Mapper; | ||||||
|  |  | ||||||
|  | @Mapper | ||||||
|  | public interface SysSmsCodeMapper extends BaseMapperX<SysSmsCodeDO> { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获得手机号的最后一个手机验证码 | ||||||
|  |      * | ||||||
|  |      * @param mobile 手机号 | ||||||
|  |      * @param scene 发送场景,选填 | ||||||
|  |      * @return 手机验证码 | ||||||
|  |      */ | ||||||
|  |     default SysSmsCodeDO selectLastByMobile(String mobile, Integer scene) { | ||||||
|  |         return selectOne(new QueryWrapperX<SysSmsCodeDO>() | ||||||
|  |                 .eq("mobile", mobile) | ||||||
|  |                 .eqIfPresent("scene", scene) | ||||||
|  |                 .orderByDesc("id") | ||||||
|  |                 .last("LIMIT 1")); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1 @@ | |||||||
|  | package cn.iocoder.yudao.userserver.modules.system.dal.redis; | ||||||
| @@ -14,4 +14,11 @@ public interface SysErrorCodeConstants { | |||||||
|     ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1005000001, "登录失败,账号被禁用"); |     ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1005000001, "登录失败,账号被禁用"); | ||||||
|     ErrorCode AUTH_LOGIN_FAIL_UNKNOWN = new ErrorCode(1005000002, "登录失败"); // 登录失败的兜底,未知原因 |     ErrorCode AUTH_LOGIN_FAIL_UNKNOWN = new ErrorCode(1005000002, "登录失败"); // 登录失败的兜底,未知原因 | ||||||
|  |  | ||||||
|  |     // ========== SMS CODE 模块 1005001000 ========== | ||||||
|  |     ErrorCode USER_SMS_CODE_NOT_FOUND = new ErrorCode(1005001000, "验证码不存在"); | ||||||
|  |     ErrorCode USER_SMS_CODE_EXPIRED = new ErrorCode(1005001001, "验证码已过期"); | ||||||
|  |     ErrorCode USER_SMS_CODE_USED = new ErrorCode(1005001002, "验证码已使用"); | ||||||
|  |     ErrorCode USER_SMS_CODE_NOT_CORRECT = new ErrorCode(1005001003, "验证码不正确"); | ||||||
|  |     ErrorCode USER_SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1005001004, "超过每日短信发送数量"); | ||||||
|  |     ErrorCode USER_SMS_CODE_SEND_TOO_FAST = new ErrorCode(1005001005, "短信发送过于频率"); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| package cn.iocoder.yudao.userserver.modules.member.enums.sms; | package cn.iocoder.yudao.userserver.modules.system.enums.sms; | ||||||
| 
 | 
 | ||||||
| import cn.iocoder.yudao.framework.common.core.IntArrayValuable; | import cn.iocoder.yudao.framework.common.core.IntArrayValuable; | ||||||
| import lombok.AllArgsConstructor; | import lombok.AllArgsConstructor; | ||||||
| @@ -13,13 +13,13 @@ import java.util.Arrays; | |||||||
|  */ |  */ | ||||||
| @Getter | @Getter | ||||||
| @AllArgsConstructor | @AllArgsConstructor | ||||||
| public enum MbrSmsSceneEnum implements IntArrayValuable { | public enum SysSmsSceneEnum implements IntArrayValuable { | ||||||
| 
 | 
 | ||||||
|     LOGIN_BY_SMS(1, "手机号登陆"), |     LOGIN_BY_SMS(1, "手机号登陆"), | ||||||
|     CHANGE_MOBILE_BY_SMS(2, "更换手机号"), |     CHANGE_MOBILE_BY_SMS(2, "更换手机号"), | ||||||
|             ; |             ; | ||||||
| 
 | 
 | ||||||
|     public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(MbrSmsSceneEnum::getScene).toArray(); |     public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SysSmsSceneEnum::getScene).toArray(); | ||||||
| 
 | 
 | ||||||
|     private final Integer scene; |     private final Integer scene; | ||||||
|     private final String name; |     private final String name; | ||||||
| @@ -0,0 +1,6 @@ | |||||||
|  | /** | ||||||
|  |  * 属于 system 模块的 framework 封装 | ||||||
|  |  * | ||||||
|  |  * @author 芋道源码 | ||||||
|  |  */ | ||||||
|  | package cn.iocoder.yudao.userserver.modules.system.framework; | ||||||
| @@ -0,0 +1,9 @@ | |||||||
|  | package cn.iocoder.yudao.userserver.modules.system.framework.sms; | ||||||
|  |  | ||||||
|  | import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||||||
|  | import org.springframework.context.annotation.Configuration; | ||||||
|  |  | ||||||
|  | @Configuration | ||||||
|  | @EnableConfigurationProperties(SmsCodeProperties.class) | ||||||
|  | public class SmsCodeConfiguration { | ||||||
|  | } | ||||||
| @@ -0,0 +1,43 @@ | |||||||
|  | package cn.iocoder.yudao.userserver.modules.system.framework.sms; | ||||||
|  |  | ||||||
|  | import lombok.Data; | ||||||
|  | import org.springframework.boot.context.properties.ConfigurationProperties; | ||||||
|  | import org.springframework.validation.annotation.Validated; | ||||||
|  |  | ||||||
|  | import javax.validation.constraints.NotEmpty; | ||||||
|  | import javax.validation.constraints.NotNull; | ||||||
|  | import java.time.Duration; | ||||||
|  | import java.util.Collection; | ||||||
|  |  | ||||||
|  | @ConfigurationProperties(prefix = "yudao.sms-code") | ||||||
|  | @Validated | ||||||
|  | @Data | ||||||
|  | public class SmsCodeProperties { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 过期时间 | ||||||
|  |      */ | ||||||
|  |     @NotNull(message = "过期时间不能为空") | ||||||
|  |     private Duration expireTimes; | ||||||
|  |     /** | ||||||
|  |      * 短信发送频率 | ||||||
|  |      */ | ||||||
|  |     @NotNull(message = "短信发送频率不能为空") | ||||||
|  |     private Duration sendFrequency; | ||||||
|  |     /** | ||||||
|  |      * 每日发送最大数量 | ||||||
|  |      */ | ||||||
|  |     @NotNull(message = "每日发送最大数量不能为空") | ||||||
|  |     private Integer sendMaximumQuantityPerDay; | ||||||
|  |     /** | ||||||
|  |      * 验证码最小值 | ||||||
|  |      */ | ||||||
|  |     @NotNull(message = "验证码最小值不能为空") | ||||||
|  |     private Integer beginCode; | ||||||
|  |     /** | ||||||
|  |      * 验证码最大值 | ||||||
|  |      */ | ||||||
|  |     @NotNull(message = "验证码最大值不能为空") | ||||||
|  |     private Integer endCode; | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,35 @@ | |||||||
|  | package cn.iocoder.yudao.userserver.modules.system.service.sms; | ||||||
|  |  | ||||||
|  | import cn.iocoder.yudao.framework.common.exception.ServiceException; | ||||||
|  | import cn.iocoder.yudao.framework.common.validation.Mobile; | ||||||
|  | import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 短信验证码 Service 接口 | ||||||
|  |  * | ||||||
|  |  * @author 芋道源码 | ||||||
|  |  */ | ||||||
|  | public interface SysSmsCodeService { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 创建短信验证码,并进行发送 | ||||||
|  |      * | ||||||
|  |      * @param mobile 手机号 | ||||||
|  |      * @param scene 发送场景 {@link SysSmsSceneEnum} | ||||||
|  |      * @param createIp 发送 IP | ||||||
|  |      */ | ||||||
|  |     void sendSmsCode(@Mobile String mobile, Integer scene, String createIp); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 验证短信验证码,并进行使用 | ||||||
|  |      * 如果正确,则将验证码标记成已使用 | ||||||
|  |      * 如果错误,则抛出 {@link ServiceException} 异常 | ||||||
|  |      * | ||||||
|  |      * @param mobile 手机号 | ||||||
|  |      * @param scene 发送场景 | ||||||
|  |      * @param code 验证码 | ||||||
|  |      * @param usedIp 使用 IP | ||||||
|  |      */ | ||||||
|  |     void useSmsCode(@Mobile String mobile, Integer scene, String code, String usedIp); | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,87 @@ | |||||||
|  | package cn.iocoder.yudao.userserver.modules.system.service.sms.impl; | ||||||
|  |  | ||||||
|  | import cn.iocoder.yudao.userserver.modules.system.dal.dataobject.sms.SysSmsCodeDO; | ||||||
|  | import cn.iocoder.yudao.userserver.modules.system.dal.mysql.sms.SysSmsCodeMapper; | ||||||
|  | import cn.iocoder.yudao.userserver.modules.system.framework.sms.SmsCodeProperties; | ||||||
|  | import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; | ||||||
|  | import org.springframework.stereotype.Service; | ||||||
|  | import org.springframework.validation.annotation.Validated; | ||||||
|  |  | ||||||
|  | import javax.annotation.Resource; | ||||||
|  | import java.util.Date; | ||||||
|  |  | ||||||
|  | import static cn.hutool.core.util.RandomUtil.randomInt; | ||||||
|  | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||||||
|  | import static cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConstants.*; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 短信验证码 Service 实现类 | ||||||
|  |  * | ||||||
|  |  * @author 芋道源码 | ||||||
|  |  */ | ||||||
|  | @Service | ||||||
|  | @Validated | ||||||
|  | public class SysSmsCodeServiceImpl implements SysSmsCodeService { | ||||||
|  |  | ||||||
|  |     @Resource | ||||||
|  |     private SmsCodeProperties smsCodeProperties; | ||||||
|  |  | ||||||
|  |     @Resource | ||||||
|  |     private SysSmsCodeMapper smsCodeMapper; | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void sendSmsCode(String mobile, Integer scene, String createIp) { | ||||||
|  |         // 创建验证码 | ||||||
|  |         String code = this.createSmsCode(mobile, scene, createIp); | ||||||
|  |         // 发送验证码 | ||||||
|  |         // TODO 芋艿:重要,发送短信验证码 | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private String createSmsCode(String mobile, Integer scene, String ip) { | ||||||
|  |         // 校验是否可以发送验证码,不用筛选场景 | ||||||
|  |         SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, null); | ||||||
|  |         if (lastSmsCode != null) { | ||||||
|  |             if (lastSmsCode.getTodayIndex() >= smsCodeProperties.getSendMaximumQuantityPerDay()) { // 超过当天发送的上限。 | ||||||
|  |                 throw exception(USER_SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY); | ||||||
|  |             } | ||||||
|  |             if (System.currentTimeMillis() - lastSmsCode.getCreateTime().getTime() | ||||||
|  |                     < smsCodeProperties.getSendFrequency().toMillis()) { // 发送过于频繁 | ||||||
|  |                 throw exception(USER_SMS_CODE_SEND_TOO_FAST); | ||||||
|  |             } | ||||||
|  |             // TODO 芋艿:提升,每个 IP 每天可发送数量 | ||||||
|  |             // TODO 芋艿:提升,每个 IP 每小时可发送数量 | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 创建验证码记录 | ||||||
|  |         String code = String.valueOf(randomInt(smsCodeProperties.getBeginCode(), smsCodeProperties.getEndCode() + 1)); | ||||||
|  |         SysSmsCodeDO newSmsCode = SysSmsCodeDO.builder().mobile(mobile).code(code) | ||||||
|  |                 .scene(scene).todayIndex(lastSmsCode != null ? lastSmsCode.getTodayIndex() + 1 : 1) | ||||||
|  |                 .createIp(ip).used(false).build(); | ||||||
|  |         smsCodeMapper.insert(newSmsCode); | ||||||
|  |         return code; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void useSmsCode(String mobile, Integer scene, String code, String usedIp) { | ||||||
|  |         // 校验验证码 | ||||||
|  |         SysSmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, scene); | ||||||
|  |         if (lastSmsCode == null) { // 若验证码不存在,抛出异常 | ||||||
|  |             throw exception(USER_SMS_CODE_NOT_FOUND); | ||||||
|  |         } | ||||||
|  |         if (System.currentTimeMillis() - lastSmsCode.getCreateTime().getTime() | ||||||
|  |                 >= smsCodeProperties.getExpireTimes().toMillis()) { // 验证码已过期 | ||||||
|  |             throw exception(USER_SMS_CODE_EXPIRED); | ||||||
|  |         } | ||||||
|  |         if (lastSmsCode.getUsed()) { // 验证码已使用 | ||||||
|  |             throw exception(USER_SMS_CODE_USED); | ||||||
|  |         } | ||||||
|  |         if (!lastSmsCode.getCode().equals(code)) { | ||||||
|  |             throw exception(USER_SMS_CODE_NOT_CORRECT); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // 使用验证码 | ||||||
|  |         smsCodeMapper.updateById(SysSmsCodeDO.builder().id(lastSmsCode.getId()) | ||||||
|  |                 .used(true).usedTime(new Date()).usedIp(usedIp).build()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -58,5 +58,13 @@ yudao: | |||||||
|     db-schemas: ${spring.datasource.dynamic.datasource.master.name} |     db-schemas: ${spring.datasource.dynamic.datasource.master.name} | ||||||
|   error-code: # 错误码相关配置项 |   error-code: # 错误码相关配置项 | ||||||
|     constants-class-list: |     constants-class-list: | ||||||
|  |       - cn.iocoder.yudao.userserver.modules.member.enums.MbrErrorCodeConstants | ||||||
|  |       - cn.iocoder.yudao.userserver.modules.system.enums.SysErrorCodeConstants | ||||||
|  |   sms-code: # 短信验证码相关的配置项 | ||||||
|  |     expire-times: 10m | ||||||
|  |     send-frequency: 1m | ||||||
|  |     send-maximum-quantity-per-day: 10 | ||||||
|  |     begin-code: 9999 # 这里配置 9999 的原因是,测试方便。 | ||||||
|  |     end-code: 9999 # 这里配置 9999 的原因是,测试方便。 | ||||||
|  |  | ||||||
| debug: false | debug: false | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								更新日志.md
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								更新日志.md
									
									
									
									
									
								
							| @@ -10,13 +10,14 @@ | |||||||
| ## [v1.1.1] 待定 | ## [v1.1.1] 待定 | ||||||
|  |  | ||||||
| * 支付 | * 支付 | ||||||
|  | * 用户前台的社交登陆 | ||||||
|  |  | ||||||
| ## [v1.1.0] 待定 | ## [v1.1.0] 进行中 | ||||||
|  |  | ||||||
| * 新增管理后台的企业微信、钉钉等社交登录 | * 新增管理后台的企业微信、钉钉等社交登录 | ||||||
| * 新增用户前台(例如说,用户使用的小程序)的后端项目 `yudao-user-server` | * 新增用户前台(例如说,用户使用的小程序)的后端项目 `yudao-user-server` | ||||||
| * 新增公共服务 `yudao-core-service` 项目,通过 Jar 包的方式,提供 `yudao-user-server` 和 `yudao-admin-server` 的共享逻辑的复用。 | * 新增公共服务 `yudao-core-service` 项目,通过 Jar 包的方式,提供 `yudao-user-server` 和 `yudao-admin-server` 的共享逻辑的复用。 | ||||||
| * 新增用户前台的手机登录、验证码登录、TODO  | * 新增用户前台的手机登录、验证码登录  | ||||||
| * 修复管理后台的用户头像上传 404 的问题 | * 修复管理后台的用户头像上传 404 的问题 | ||||||
|  |  | ||||||
| ## [v1.0.0] 2021.05.03 | ## [v1.0.0] 2021.05.03 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV