谷粒学院service-ucenter模块
模块介绍
主要实现前台的登录和注册功能,能够进行微信登录。
库表设计
详细设计
用户
登录
接口设计
接受参数:用户(手机号和密码)
请求类型:POST
请求体:JSON 格式的数据
返回值:token
流程
- 根据用户获得手机号和密码
- 对手机号和密码进行非空判断
- 查询手机号是否正确
- 把用户输入的密码进行MD5加密,然后和数据库中的密码进行比较
- 判断用户是否被禁用
- 登录成功,返回token字符串
代码
//controller
//登录
@PostMapping("login")
public R loginUser(@RequestBody UcenterMember member) {
//业务层的登录方法login返回一个token值
String token = memberService.login(member);
return R.ok().data("token", token);
}
//service
@Override
public String login(UcenterMember member) {
//获取手机号和密码
String mobile = member.getMobile();
String password = member.getPassword();
//手机号和密码非空判断
if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)) {
throw new GuliException(20001, "手机号、密码为空");
}
//判断手机号是否正确
QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();
wrapper.eq("mobile", mobile);
UcenterMember mobileMember = baseMapper.selectOne(wrapper);
//判断查询对象是否正确
if (mobileMember == null) { //数据表中没有这个手机号
throw new GuliException(20001, "没有这个手机号数据");
}
//判断密码 数据库中的密码都是加密的
//把用户输入的密码进行MD5加密,然后和数据库中的密码进行比较
if (!MD5.encrypt(password).equals(mobileMember.getPassword())) {
throw new GuliException(20001, "密码错误");
}
//判断用户是否禁用
if (mobileMember.getIsDisabled()) {
throw new GuliException(20001, "用户已禁用");
}
//登录成功,生成token字符串
String jwtToken = JwtUtils.getJwtToken(mobileMember.getId(), mobileMember.getNickname());
return jwtToken;
}
注册
接口设计
接受参数:注册对象
请求类型:POST
请求体:JSON 格式的数据
流程
- 获取注册信息
- 非空校验
- 校验验证码
- 查询数据库中是否存在相同的手机号码
- 添加注册信息到数据库
- 密码进行加密
- 头像设置为默认头像
代码
//注册
@PostMapping("register")
public R register(@RequestBody RegisterVo register){
memberService.register(register);
return R.ok();
}
@Override
public void register(RegisterVo registerVo) {
//获取注册信息,进行校验
String nickname = registerVo.getNickname();
String mobile = registerVo.getMobile();
String password = registerVo.getPassword();
String code = registerVo.getCode();
//校验参数
if(StringUtils.isEmpty(mobile) ||
StringUtils.isEmpty(mobile) ||
StringUtils.isEmpty(password) ||
StringUtils.isEmpty(code)) {
throw new GuliException(20001,"有参数为空");
}
//校验校验验证码
//从redis获取发送的验证码
String mobleCode = redisTemplate.opsForValue().get(mobile);
if(!code.equals(mobleCode)) {
throw new GuliException(20001,"验证码错误");
}
//查询数据库中是否存在相同的手机号码
Integer count = baseMapper.selectCount(new QueryWrapper<UcenterMember>().eq("mobile", mobile));
if(count.intValue() > 0) {
throw new GuliException(20001,"存在相同的手机号码");
}
//添加注册信息到数据库
UcenterMember member = new UcenterMember();
member.setNickname(nickname);
member.setMobile(registerVo.getMobile());
member.setPassword(MD5.encrypt(password));
member.setIsDisabled(false);
member.setAvatar("http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132");
baseMapper.insert(member);
}
根据token字符获得会员id
接口设计
接受参数:request请求
请求类型:GET
返回值:用户对象
流程
- 调用Jwt工具类方法,根据request对象获取头信息,返回用户id
- 查询数据库根据用户id获取用户信息
代码
//根据token字符获得会员id
@GetMapping("getMemberInfo")
public R getMemberInfo(HttpServletRequest request){
//调用Jwt工具类方法,根据request对象获取头信息,返回用户id
String memberIdByJwtToken = JwtUtils.getMemberIdByJwtToken(request);
//查询数据库根据用户id获取用户信息
UcenterMember member = memberService.getById(memberIdByJwtToken);
return R.ok().data("userInfo",member);
}
根据用户id获取用户信息(该方法供远程调用使用)
接口设计
接受参数:用户id
请求类型:POST
请求体:JSON 格式的数据
返回值:用户对象
流程
- 订单需要获取用户信息
- 把member对象里面的值复制给UcenterMemberOrder对象
代码
@PostMapping("getUserInfoOrder/{id}")
public UcenterMemberOrder getUserInfoOrder(@PathVariable String id) {
UcenterMember member = memberService.getById(id);
//把member对象里面的值复制给UcenterMemberOrder对象
UcenterMemberOrder ucenterMemberOrder = new UcenterMemberOrder();
BeanUtils.copyProperties(member, ucenterMemberOrder);
return ucenterMemberOrder;
}
查询某天注册人数(该方法供远程调用使用)
接口设计
接受参数:日期
请求类型:GET
返回值:int类型
流程
- 调用Jwt工具类方法,根据request对象获取头信息,返回用户id
- 查询数据库根据用户id获取用户信息
代码
//查询某天注册人数(该方法供远程调用使用)
@GetMapping("countRegister/{day}")
public R countRegister(@PathVariable String day) {
Integer count = memberService.countRegisterDay(day);
return R.ok().data("countRegister", count);
}
//查询某天注册人数(该方法供远程调用使用)
@Override
public Integer countRegisterDay(String day) {
return baseMapper.countRegisterDay(day);
}
<mapper namespace="com.atguigu.educenter.mapper.UcenterMemberMapper">
<!--查询某天注册人数(该方法供远程调用使用)-->
<select id="countRegisterDay" resultType="java.lang.Integer">
SELECT COUNT(*) FROM ucenter_member uc
WHERE DATE(uc.gmt_create)=#{day}
</select>
</mapper>
微信登录
@RestController 这个注解,方法返回字符串时就会直接将字符串写到浏览器中,会导致无法生成二维码,所以用@Controller注解。
接口设计
请求类型:GET
返回值:token
流程
-
生成微信二维码
- 声明一个地址,按照微信要求的格式,留有四个占位符。
- 对redirect_url进行URLEncode编码
- 给baseUrl中的占位符(%s)赋值
- 重定向到请求微信地址里面
- 获得code和state
-
获得返回的信息
-
用code请求微信给的固定地址
- 给地址拼接三个参数:id、秘钥、code值
- 用httpclient请求拼接好的地址,得到openid和access_token
- 使用json工具把accessTokenInfo字符串转换为map集合,根据map中的key获取对应的值
-
将用户的openid、昵称、头像加到数据库中
-
根据opendi判断数据库中是否已有该用户
-
如果没有,拿着access_token和openid请求微信给的固定地址
-
给地址拼接两个参数:access_token、openid
-
使用httpclient请求拼接好的地址
-
获取扫码人信息
-
将json字符串转换为map集合添加到数据库中
-
-
-
-
使用jwt根据member对象(对象中有用户信息)生成token字符串
-
返回到首页面(通过路径传递token字符串)
代码
@Autowired
private UcenterMemberService memberService;
//生成微信扫描二维码
@GetMapping("login")
public String getWxCode() {
//1.url中的%s就相当于问号(?),代表占位符
String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
"?appid=%s" +
"&redirect_uri=%s" +
"&response_type=code" +
"&scope=snsapi_login" +
"&state=%s" +
"#wechat_redirect";
//2.对redirect_url进行URLEncode编码
String redirect_url = ConstantWxUtils.WX_OPEN_REDIRECT_URL;
try {
redirect_url = URLEncoder.encode(redirect_url, "utf-8");
} catch(Exception e) {
}
//3.给baseUrl中的占位符(%s)赋值
String url = String.format(
baseUrl,
ConstantWxUtils.WX_OPEN_APP_ID,
redirect_url,
"atguigu" //老师说了state目前没啥用,只是给他传个值atguigu
); //方法的返回值就是完整的带有参数的url地址
//4.重定向到请求微信地址里面
return "redirect:" + url;
}
@GetMapping("callback")
public String callback(String code, String state) {
try {
//1.拿着code(这是一个临时票据,类似于验证码)请求微信给的固定地址
String baseAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" +
"?appid=%s" +
"&secret=%s" +
"&code=%s" +
"&grant_type=authorization_code";
//1.1给地址拼接三个参数:id、秘钥、code值
String accessTokenUrl = String.format(
baseAccessTokenUrl,
ConstantWxUtils.WX_OPEN_APP_ID,
ConstantWxUtils.WX_OPEN_APP_SECRET,
code);
//1.2使用httpclient请求拼接好的地址,得到openid和access_token
String accessTokenInfo = HttpClientUtils.get(accessTokenUrl);
//从json字符串中获取openid和access_token
//使用json工具把accessTokenInfo字符串转换为map集合,根据map中的key获取对应的值
Gson gson = new Gson();
HashMap mapAccessToken = gson.fromJson(accessTokenInfo, HashMap.class);
String access_token = (String)mapAccessToken.get("access_token");
String openid = (String)mapAccessToken.get("openid");
//4.微信扫码登录时注册的过程不再由用户完成,而是由我们后端直接实现
//所以我们此时需要将用户的openid、昵称、头像加到数据库中
//根据opendi判断数据库中是否已有该用户
UcenterMember member = memberService.getOpenIdMember(openid);
if (member == null) { //数据表中没有这个openid,进行添加
//2.拿着access_token和openid请求微信给的固定地址
String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
"?access_token=%s" +
"&openid=%s";
//2.1给地址拼接两个参数:access_token、openid
String userInfoUrl = String.format(baseUserInfoUrl, access_token, openid);
//2.2使用httpclient请求拼接好的地址
String userInfo = HttpClientUtils.get(userInfoUrl);
//输出userInfo看一下这个字符串长什么样,以便分析后面的业务逻辑
System.out.println(userInfo);
//3.获取扫码人信息
//3.1将json字符串转换为map集合
HashMap userInfoMap = gson.fromJson(userInfo, HashMap.class);
String nickname = (String) userInfoMap.get("nickname"); //用户昵称
String headimgurl = (String) userInfoMap.get("headimgurl"); //用户头像
member = new UcenterMember();
member.setOpenid(openid);
member.setNickname(nickname);
member.setAvatar(headimgurl);
memberService.save(member);
}
//使用jwt根据member对象(对象中有用户信息)生成token字符串
String jwtToken = JwtUtils.getJwtToken(member.getId(), member.getNickname());
//返回到首页面(通过路径传递token字符串)
return "redirect:http://localhost:3000?token=" + jwtToken;
} catch (Exception e) {
throw new GuliException(20001, "登录失败");
}
}
//根据openid判断数据表中是否已有这个用户
@Override
public UcenterMember getOpenIdMember(String openid) {
QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();
wrapper.eq("openid", openid);
return baseMapper.selectOne(wrapper);
}