文章目录
在现代移动应用开发中,微信小程序已经成为了许多企业的重要选择。为了支持小程序的用户登录功能,我们需要与微信的认证服务进行集成。这篇文章将带大家深入解析两个关键的服务实现类:
CustomerInfoServiceImpl
和
CustomerServiceImpl
,它们是如何共同工作来实现微信小程序登录功能的。
一、CustomerInfoServiceImpl类详解
CustomerInfoServiceImpl
类主要负责处理用户信息的管理和登录日志的记录。它继承了ServiceImpl
,并实现了CustomerInfoService
接口。下面我们来详细了解它的核心逻辑。
package com.atguigu.daijia.customer.service.impl;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.atguigu.daijia.customer.mapper.CustomerInfoMapper;
import com.atguigu.daijia.customer.mapper.CustomerLoginLogMapper;
import com.atguigu.daijia.customer.service.CustomerInfoService;
import com.atguigu.daijia.model.entity.customer.CustomerInfo;
import com.atguigu.daijia.model.entity.customer.CustomerLoginLog;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.stereotype.Service;
@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
@RequiredArgsConstructor
public class CustomerInfoServiceImpl extends ServiceImpl<CustomerInfoMapper, CustomerInfo> implements
CustomerInfoService {
private final WxMaService wxMaService;
private final CustomerInfoMapper customerInfoMapper;
private final CustomerLoginLogMapper customerLoginLogMapper;
// 微信小程序登录接口
@Override
public Long login(String code) {
// 1.获取code值,使用微信工具包对象,获取微信唯一标识openid
String openid = null;
try {
WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);
openid = sessionInfo.getOpenid();
} catch (WxErrorException e) {
throw new RuntimeException(e);
}
// 2.根据openid查询数据库表,判断是否第一次登录
// 如果不存返回null,如果存在返回一条记录
LambdaQueryWrapper<CustomerInfo> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CustomerInfo::getWxOpenId, openid);
CustomerInfo customerInfo = customerInfoMapper.selectOne(wrapper);
// 3.如果是第一次登录,将用户信息插入数据库表
if (customerInfo == null) {
customerInfo = new CustomerInfo();
customerInfo.setNickname(String.valueOf(System.currentTimeMillis()));
customerInfo.setAvatarUrl("https://oss.aliyuncs.com/aliyun_id_photo_bucket/default_handsome.jpg");
customerInfo.setWxOpenId(openid);
customerInfoMapper.insert(customerInfo);
}
// 4.记录登录日志
CustomerLoginLog customerLoginLog = new CustomerLoginLog();
customerLoginLog.setCustomerId(customerInfo.getId());
customerLoginLog.setMsg("小程序登录");
customerLoginLogMapper.insert(customerLoginLog);
// 5.返回用户id
return customerInfo.getId();
}
}
-
获取微信用户的openid
在微信小程序中,
code
是通过调用wx.login
接口获取的,作为登录的凭证。我们通过wxMaService
来获取openid
,这是每个微信用户的唯一标识。WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code); String openid = sessionInfo.getOpenid();
-
检查用户是否是第一次登录
通过
openid
在数据库中查询用户信息。如果没有找到对应的用户信息,则说明这是用户的首次登录。在这种情况下,我们会创建一条新的用户记录。LambdaQueryWrapper<CustomerInfo> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(CustomerInfo::getWxOpenId, openid); CustomerInfo customerInfo = customerInfoMapper.selectOne(wrapper); if (customerInfo == null) { customerInfo = new CustomerInfo(); customerInfo.setNickname(String.valueOf(System.currentTimeMillis())); customerInfo.setAvatarUrl("https://oss.aliyuncs.com/aliyun_id_photo_bucket/default_handsome.jpg"); customerInfo.setWxOpenId(openid); customerInfoMapper.insert(customerInfo); }
-
记录登录日志
每次用户登录,我们都会记录一条登录日志,以便后续分析和追踪。
CustomerLoginLog customerLoginLog = new CustomerLoginLog(); customerLoginLog.setCustomerId(customerInfo.getId()); customerLoginLog.setMsg("小程序登录"); customerLoginLogMapper.insert(customerLoginLog);
-
返回用户ID
最后,我们将用户的
ID
返回,供后续使用。return customerInfo.getId();
二、CustomerServiceImpl类详解
CustomerServiceImpl
类则主要负责与其他微服务的交互以及处理登录后的Token管理。它通过调用远程的用户服务,获取用户的登录信息,并将登录状态存储在Redis中。
package com.atguigu.daijia.customer.service.impl;
import com.atguigu.daijia.common.constant.RedisConstant;
import com.atguigu.daijia.common.execption.GuiguException;
import com.atguigu.daijia.common.result.Result;
import com.atguigu.daijia.common.result.ResultCodeEnum;
import com.atguigu.daijia.customer.client.CustomerInfoFeignClient;
import com.atguigu.daijia.customer.service.CustomerService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
@RequiredArgsConstructor
public class CustomerServiceImpl implements CustomerService {
// 注入远程调用接口
private final CustomerInfoFeignClient client;
private final RedisTemplate redisTemplate;
@Override
public String login(String code) {
// 1.远程调用,返回用户id
Result<Long> loginResult = client.login(code);
// 2.判断如果返回失败,返回错误提示
Integer codeResult = loginResult.getCode();
if (codeResult != 200) {
throw new GuiguException(ResultCodeEnum.DATA_ERROR);
}
// 3.获取远程调用返回的用户id
Long customerId = loginResult.getData();
// 4.判断返回用户id是否为空,如果为空,返回错误提示
if (customerId == null) {
throw new GuiguException(ResultCodeEnum.DATA_ERROR);
}
// 5.生成token字符串
String token = UUID.randomUUID().toString().replaceAll("-", "");
// 6.把用户id放到Redis,设置过期时间
// key:token value:customerId
redisTemplate.opsForValue().set(RedisConstant.USER_LOGIN_KEY_PREFIX + token,
customerId.toString(),
RedisConstant.USER_LOGIN_KEY_TIMEOUT,
TimeUnit.SECONDS);
// 7.返回token字符串
return token;
}
}
-
远程调用获取用户ID
CustomerServiceImpl
通过Feign
客户端远程调用CustomerInfoService
的login
方法,获取用户ID。Result<Long> loginResult = client.login(code);
-
错误处理
在调用远程服务后,首先要检查返回的结果状态码,如果状态码不为200,说明请求失败,抛出自定义异常。
if (loginResult.getCode() != 200) { throw new GuiguException(ResultCodeEnum.DATA_ERROR); }
-
生成Token并存储到Redis
为了管理用户的会话状态,我们生成了一个唯一的
Token
,并将其与用户ID绑定存储到Redis中,这样可以在后续的请求中验证用户身份。String token = UUID.randomUUID().toString().replaceAll("-", ""); redisTemplate.opsForValue().set(RedisConstant.USER_LOGIN_KEY_PREFIX + token, customerId.toString(), RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS);
-
返回Token
最终,将生成的
Token
返回给前端,前端会在后续的请求中使用这个Token
来进行身份验证。return token;
三、总结
CustomerInfoServiceImpl
和CustomerServiceImpl
这两个类的协作,完整实现了微信小程序的用户登录功能。前者负责处理微信用户信息的管理,后者则负责会话状态的管理与安全。这种设计使得代码清晰且职责明确,便于维护和扩展。