1. 使用SpringMVC的方式开发用户信息
model层即之前配置的数据库对象的映射的组成 因为包括密码才是一个完整的model 再但是返回给前端的model不需要也不应该包括所有信息 所以目录结构长这个样子
UserController.java 即控制层
@Controller("user")//用来被spring扫描到
@RequestMapping("/user")//在url上通过/user的方式访问到
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/get")
@ResponseBody
public CommonReturnType getUser(@RequestParam(name="id")Integer id){
//接收一个入参 调用service服务获取对应id的用户对象并返回给前端
UserModel userModel = userService.getUserById(id);
//将核心领域模型用户对象转化为可供UI使用的viewobject
UserVO userVO = convertFromModel(userModel);
return CommonReturnType.create(userVO);
}
private UserVO convertFromModel(UserModel userModel){
if (userModel == null){
return null;
}
UserVO userVO = new UserVO();
BeanUtils.copyProperties(userModel,userVO);
return userVO;
}
}
UserServiceImpl.java 服务层
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDOMapper userDOMapper;
@Autowired
private UserPasswordDOMapper userPasswordDOMapper;
@Override
public UserModel getUserById(Integer id) {
//调用userdommapper获取到对应的用户dataobject
UserDO userDO = userDOMapper.selectByPrimaryKey(id);
if (userDO == null){
return null;
}
//业务逻辑要求通过user的用户名获取密码
UserPasswordDO userPasswordDO = userPasswordDOMapper.selectByUserId(userDO.getId());
return convertFromDataObject(userDO,userPasswordDO);
}
private UserModel convertFromDataObject(UserDO userDO, UserPasswordDO userPasswordDO){
if (userDO == null){
return null;
}
UserModel userModel = new UserModel();
BeanUtils.copyProperties(userDO, userModel);
if (userPasswordDO == null){
return userModel;
}
userModel.setEncrptPassword(userPasswordDO.getEncrptPassword());
return userModel;
}
}
2. 定义通用的返回对象
刚开始的代码是返回一个model类型给前端 那么前端每次都要解析json对象对应的model类型 出错的时候没有json对象就无法解析 所以需要给程序返回一个有意义的错误信息 现在定义通用的返回对象
public class CommonReturnType {
//表明对应请求的返回处理结果"success" or "fail"
private String status;
//若status=success 则data内返回前端需要的json数据
//若status=fail 则打他内使用通用的错误码格式
private Object data;
//定义一个通用的常见方法
public static CommonReturnType create(Object res){
return CommonReturnType.create(res,"success");
}
public static CommonReturnType create(Object res,String status){
CommonReturnType type = new CommonReturnType();
type.setStatus(status);
type.setData(res);
return type;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
定义了一个CommonReturnType 用对应的status和object类型的data的方式返回所有json序列化对象,供前端解析使用,摒弃了使用httpstatuscode和tomcat内置的error页的方式处理 并且定义了一个BusinessException的方式 去统一管理我们想要的错误码 在BaseController里面定义了通用的handlerException方法 解决未被controller层吸收的exception 并且使用errCode和errMsg统一的配置方式 吃掉了所有内部所有不可预知的异常
3. 实现用户的第一个行为 手机验证码注册
//Spring默认bean的装配是单例的 如何满足多个用户的同时访问?
//答:本质是一个process 里面实现了Threadlocal修饰的map 让每个线程都能够处理自己的request
//https://www.cnblogs.com/xrq730/p/4854820.html
@Autowired
private HttpServletRequest httpServletRequest;
@RequestMapping("/getotp")
@ResponseBody
public CommonReturnType getOtp(@RequestParam(name="telphone")String telphone){
//需要按照一定的规则生成OTP验证码
Random random = new Random();
int randomInt = random.nextInt(99999);
randomInt+=10000;
String otpCode = String.valueOf(randomInt);
//将OTP验证码同对应用户的手机号关联(这个需求最好使用Redis实现) 还没讲到分布式 先用httpsession
httpServletRequest.getSession().setAttribute(telphone,otpCode);
//将OTP验证码通过短信通道发送给用户 省略
System.out.println("telphone = "+ telphone+"&otpCode = "+otpCode);
return CommonReturnType.create(null);
}
4. 注册的功能实现
在UserDommapper里面指定用户表的keyProperty字段和useGeneratedKeys字段 否则会出错
<insert id="insertSelective" parameterType="com.miaoshaproject.dataobject.UserDO"
keyProperty="id" useGeneratedKeys="true">
volidator相关不表
遇到的一个问题自增id不是从最大的开始
Controller里的逻辑
//用户注册接口
@RequestMapping(value = "register")
@ResponseBody
public CommonReturnType register(@RequestParam(name = "telphone")String telphone,
@RequestParam(name = "otpCode")String otpCode,
@RequestParam(name = "name")String name,
@RequestParam(name = "gender")Integer gender,
@RequestParam(name = "age")Integer age,
@RequestParam(name = "password")String password) throws BusinessException, UnsupportedEncodingException, NoSuchAlgorithmException {
//验证手机号和对应的otpcode相符合(otpcode放在httpServletRequest)
String inSessionOtpCode = (String)this.httpServletRequest.getSession().getAttribute(telphone);//获取otpcode
if (!StringUtils.equals(otpCode, inSessionOtpCode)){ //与前端传上来的otpCode进行对比
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "短信验证码不符合");
}
//用户的注册流程
UserModel userModel = new UserModel();
userModel.setName(name);
userModel.setGender(new Byte(String.valueOf(gender.intValue())));
userModel.setAge(age);
userModel.setTelphone(telphone);
userModel.setRegisterMode("byphone");
userModel.setEncrptPassword(this.EncodeByMD5(password));//将密码加密存入数据库
//这里是Controller 调用了service的方法
userService.register(userModel);
return CommonReturnType.create(null);//只需要返回成功的状态码 而不需要再把这个用户的相关信息返回
}
public String EncodeByMD5(String str) throws UnsupportedEncodingException, NoSuchAlgorithmException {
//java9里的BASE64Encoder取消了
//https://blog.csdn.net/Cha0DD/article/details/87794268
// 确定计算方法
MessageDigest md5 = MessageDigest.getInstance("MD5");
Encoder base64Encoder = Base64.getEncoder();
//加密字符串
String newstr = base64Encoder.encodeToString(md5.digest(str.getBytes("utf-8")));
return newstr;
}
Service里的逻辑
@Override
public void register(UserModel userModel) throws BusinessException {
if (userModel == null){
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
}
ValidationResult result = validator.validate(userModel);
if (result.isHasErrors()){
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, result.getErrMsg());
}
//注册用户
//实现model -> dataobject 方法
UserDO userDO = convertFromModel(userModel);
//手机号重复注册发生异常
try {
userDOMapper.insertSelective(userDO);
}catch (DuplicateKeyException ex){
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "手机号已注册");
}
//id是自增字段 所以插入后再取出来赋值给userModel
userModel.setId(userDO.getId());
UserPasswordDO userPasswordDO = convertPasswordFromModel(userModel);
userPasswordDOMapper.insertSelective(userPasswordDO);
}
5. 登录逻辑的实现
Controller
//用户登录接口
@RequestMapping(value = "login", method = {RequestMethod.POST})
@ResponseBody
public CommonReturnType login(@RequestParam(name = "telphone")String telphone,
@RequestParam(name = "password")String password) throws BusinessException, UnsupportedEncodingException, NoSuchAlgorithmException {
//入参校验(手机号密码不能为空)
if (org.apache.commons.lang3.StringUtils.isEmpty(telphone)
|| org.apache.commons.lang3.StringUtils.isEmpty(password)){
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "不能为空");
}
//用户登录服务,校验用户登录是否合法。将手机号和加密后的密码传到Service层
UserModel userModel = userService.validateLogin(telphone, this.EncodeByMD5(password));
//将登陆凭证加入到用户登录成功的session中
//如果用户的会话标识中有IS_LOGIN,则表示登录成功
this.httpServletRequest.getSession().setAttribute("IS_LOGIN", true);//key:IS_LOGIN
//将userModel放到对应的session里
this.httpServletRequest.getSession().setAttribute("LOGIN_USER", userModel);
//返回前端一个正确的信息(通用对象)
return CommonReturnType.create(null);//success
}
Service
@Override
public UserModel validateLogin(String telphone, String encryptPassword) throws BusinessException {
//通过用户手机号,获取用户信息。(添加sql语句)
UserDO userDO = userDOMapper.selectByTelphone(telphone);
if (userDO == null){
throw new BusinessException(EmBusinessError.USER_LOGIN_FAIL);
}
//再通过用户信息(id),到用户密码表中 拿到用户密码信息
UserPasswordDO userPasswordDO = userPasswordDOMapper.selectByUserId(userDO.getId());
UserModel userModel = convertFromDataObject(userDO, userPasswordDO); //组装成UserModel
//比对用户信息内加密的密码是否和传输进来的密码匹配
if (!StringUtils.equals(encryptPassword, userModel.getEncryptPassword())){
throw new BusinessException(EmBusinessError.USER_LOGIN_FAIL);
}else {
return userModel;//如果用户登录成功,则将model返回controller层
}
}