完成乐优商城用户中心基本功能
用户中心主要用于管理用户的信息,包含用户的注册/登陆、用户的个人信息管理、用户收货地址管理、用户收藏管理、我的订单等。
搭建用户中心微服务 ly-user:父工程,包含2个子工程:
ly-user-interface:实体及接口
ly-user-service:业务和服务
创建父工程 创建Module
GroupId:com.leyou.service
ArtifactId:ly-user
ly-user-interface 创建Module
GroupId:com.leyou.service
ArtifactId:ly-user-interface
ly-user-service 创建Module
GroupId:com.leyou.service
ArtifactId:ly-user-service
pom.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 <?xml version="1.0" encoding="UTF-8" ?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <parent> <artifactId>ly-user</artifactId> <groupId>com.leyou.service</groupId> <version>1.0.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>ly-user-service</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!-- mybatis启动器 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <!-- 通用Mapper启动器 --> <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper-spring-boot-starter</artifactId> </dependency> <!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.leyou.service</groupId> <artifactId>ly-user-interface </artifactId > <version>${leyou.latest.version}</version> </dependency> </dependencies> </project>
启动类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.leyou;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;import tk.mybatis.spring.annotation.MapperScan;@SpringBootApplication @EnableDiscoveryClient @MapperScan ("com.leyou.user.mapper" )public class UserApplication { public static void main (String[] args) { SpringApplication.run(UserApplication.class, args); } }
配置application.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 server: port: 8006 spring: application: name: user-service datasource: url: jdbc:mysql://localhost:3306/leyou username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver hikari: maximum-pool-size: 30 minimum-idle: 10 eureka: client: service-url: defaultZone: http://127.0.0.1:9999/eureka instance: lease-renewal-interval-in-seconds: 5 lease-expiration-duration-in-seconds: 15 mybatis: type-aliases-package: com.leyou.user.pojo
添加用户微服务路由 在ly-gateway
工程配置文件中新增user路由配置:
1 2 3 zuul: routes: user-service: /user/**
接口开发 准备工作 在开发接口之前,先将基本的工程架构搭建起来,包括mapper
,service
,controller
,常量类
以及实体类
。
实体类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.leyou.pojo;import com.fasterxml.jackson.annotation.JsonIgnore;import lombok.Data;import javax.persistence.GeneratedValue;import javax.persistence.GenerationType;import javax.persistence.Id;import javax.persistence.Table;import java.util.Date;@Data @Table (name = "tb_user" )public class User { @Id @GeneratedValue (strategy = GenerationType.IDENTITY) private Long id; private String username; @JsonIgnore private String password; private String phone; private Date created; @JsonIgnore private String salt; }
mapper 1 2 3 4 5 6 7 package com.leyou.user.mapper;import com.leyou.pojo.User;import tk.mybatis.mapper.common.Mapper;public interface UserMapper extends Mapper <User > {}
service 1 2 3 4 5 6 7 8 9 10 11 12 package com.leyou.user.service;import com.leyou.user.mapper.UserMapper;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Service public class UserService { @Autowired private UserMapper userMapper; }
controller 1 2 3 4 5 6 7 8 9 10 11 12 13 package com.leyou.user.controller;import com.leyou.user.service.UserService;import org.springframework.beans.factory.annotation.Autowired;@RestController @RequestMapping public class UserController { @Autowired private UserService userService; }
UserConstants 1 2 3 4 5 6 7 package com.leyou.util;public final class UserConstants {}
数据验证功能 接口说明 实现用户数据的校验,主要包括对:手机号、用户名的唯一性校验。
接口路径 1 GET /check/{data}/{type}
参数说明
参数
说明
是否必须
数据类型
默认值
data
要校验的数据
是
String
无
type
要校验的数据类型:1,用户名;2,手机;
否
Integer
1
返回结果 返回布尔类型结果:
状态码:
200:校验成功
400:参数有误
500:服务器内部异常
后端代码 controller 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @GetMapping ("/check/{data}/{type}" )public ResponseEntity<Boolean> checkData (@PathVariable("data" ) String data, @PathVariable (value = "type" ) String type) { Boolean result = userService.checkUserData(data, type); if (result == null ) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).build(); } return ResponseEntity.ok(result); }
service 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public Boolean checkUserData (String data, String type) { User param = new User(); switch (type) { case USER_DATA_USERNAME: param.setUsername(data); break ; case USER_DATA_PHONE: param.setPhone(data); break ; default : throw new LyException(LyExceptionEnum.NOT_SUPPORT_DATA_TYPE); } return userMapper.select(param).size() == 0 ; }
UserConstants 1 2 public static final String USER_DATA_USERNAME = "1" ;public static final String USER_DATA_PHONE = "2" ;
验证码短信发送 接口说明 根据用户输入的手机号,生成随机验证码,长度为6位,纯数字。发送短信验证码到该手机号。
接口路径
参数说明
参数
说明
是否必须
数据类型
默认值
phone
手机号码
是
String
无
返回结果 无
状态码:
204:请求已接受
400:参数有误
500:服务器内部错误
后端代码 controller 1 2 3 4 5 6 7 8 9 10 11 @PostMapping ("/code" )public ResponseEntity<Void> sendVerifyCode (String phone) { userService.sendVerifyCode(phone); return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); }
service 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 private static final String REDIS_PREFIX_SMS = "sms:verify:code:phone:" ; @Autowired private StringRedisTemplate redisTemplate; @Autowired private AmqpTemplate amqpTemplate; public void sendVerifyCode (String phone) { String verifyCode = NumberUtils.generateCode(6 ); try { Map<String, String> msg = new HashMap<>(); msg.put("phoneNumber" , phone); msg.put("code" , verifyCode); amqpTemplate.convertAndSend(LeyouConstants.EXCHANGE_SMS, LeyouConstants.ROUTING_KEY_VERIFY_CODE_SMS, msg); redisTemplate.opsForValue().set(REDIS_PREFIX_SMS + phone, verifyCode, 5 , TimeUnit.MINUTES); } catch (Exception e) { log.error("发送验证码失败, phone = [{}] verifyCode = [{}]" , phone, verifyCode, e); throw new LyException(LyExceptionEnum.SEND_VERIFY_CODE_FAILURE); } }
注意:需要引入rabbitmq
以及redis
的依赖,并配置其相关参数:
依赖
1 2 3 4 5 6 7 8 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-redis</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-amqp</artifactId > </dependency >
application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 spring: redis: host: 192.168 .136 .103 port: 6379 rabbitmq: host: 192.168 .136 .103 username: leyou password: leyou virtual-host: /leyou template: retry: enabled: true initial-interval: 10000 ms max-interval: 60000 ms multiplier: 2 publisher-confirms: true
用户注册 接口说明 实现用户注册功能,需要对用户密码进行加密存储,使用MD5加密,加密过程中使用随机生成的salt为盐。
接口路径
参数说明
参数
说明
是否必须
数据类型
默认值
username
用户名。格式为4-30位字母,数字,下划线
是
String
无
password
用户密码。格式为4-30位字母,数字,下划线
是
String
无
phone
手机号码
是
String
无
code
短信验证码
s是
String
无
返回结果 无
状态码:
201:注册成功
400:参数有误,注册失败
500:服务器内部异常,注册失败
后端代码 controller 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @PostMapping ("/register" )public ResponseEntity<Void> register (@Valid User user, BindingResult bindingResult, @RequestParam("code" ) String code) { if (bindingResult.hasErrors()) { bindingResult.getAllErrors().forEach(error -> { throw new LyException(LyExceptionEnum.valueOf(error.getDefaultMessage())); }); } Boolean username_unique = checkData(user.getUsername(), UserConstants.USER_DATA_USERNAME).getBody(); Boolean phone_unique = checkData(user.getUsername(), UserConstants.USER_DATA_PHONE).getBody(); if (username_unique == null || !username_unique) { throw new LyException(LyExceptionEnum.USERNAME_EXISTED); } if (phone_unique == null || !phone_unique) { throw new LyException(LyExceptionEnum.PHONE_EXISTED); } userService.register(user, code); return ResponseEntity.status(HttpStatus.CREATED).build(); }
service 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public void register (User user, String code) { user.setCreated(new Date()); String key = REDIS_PREFIX_SMS + user.getPhone(); String veriyCode = redisTemplate.opsForValue().get(key); if (!code.equals(veriyCode)) { throw new LyException(LyExceptionEnum.VERIFY_CODE_NOT_EQUALS); } String salt = UUID.randomUUID().toString().replace("-" ,"" ); user.setSalt(salt); String password = DigestUtils.md5Hex(user.getPassword()) + salt; user.setPassword(password); if (userMapper.insert(user) != 1 ) { throw new LyException(LyExceptionEnum.REGISTER_FAILURE); } redisTemplate.delete(key); }
服务端数据校验 使用hibernate-validator
,我的另一篇文章中有介绍到,可以参考:
文章链接:使用springmvc开发restful api
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package com.leyou.pojo;import com.fasterxml.jackson.annotation.JsonIgnore;import lombok.Data;import org.hibernate.validator.constraints.Length;import javax.persistence.GeneratedValue;import javax.persistence.GenerationType;import javax.persistence.Id;import javax.persistence.Table;import javax.validation.constraints.Pattern;import java.util.Date;@Data @Table (name = "tb_user" )public class User { @Id @GeneratedValue (strategy = GenerationType.IDENTITY) private Long id; @Length (min = 4 , max = 30 , message = "用户名只能在4~30位之间" ) private String username; @JsonIgnore @Length (min = 4 , max = 30 , message = "用户名只能在4~30位之间" ) private String password; @Pattern (regexp = "^1[35678]\\d{9}$" , message = "手机号格式不正确" ) private String phone; private Date created; @JsonIgnore private String salt; }
按用户名和密码查询 接口说明 查询功能,根据参数中的用户名和密码查询指定用户。
接口路径
参数说明
参数
说明
是否必须
数据类型
默认值
username
用户名,格式为4~30位字母、数字、下划线
是
String
无
password
用户密码,格式为4~30位字母、数字、下划线
是
String
无
返回结果 用户的json格式数据
1 2 3 4 5 6 { "id" : 6572312 , "username" :"test" , "phone" :"13688886666" , "created" : 1342432424 }
状态码:
200:查询成功
400:用户名或密码错误
500:服务器内部异常,注册失败
后端代码 controller 1 2 3 4 5 6 7 8 9 10 11 12 @GetMapping ("/query" )public ResponseEntity<User> queryUserByUsernameAndPassword (@RequestParam("username" ) String username, @RequestParam ("password" ) String password) { return ResponseEntity.ok(userService.queryUserByUsernameAndPassword(username, password)); }
service 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public User queryUserByUsernameAndPassword (String username, String password) { User param = new User(); param.setUsername(username); User user = userMapper.selectOne(param); if (user == null ) { throw new LyException(LyExceptionEnum.INVALID_USERNAME_OR_PASSWORD); } if (!(DigestUtils.md5Hex(password) + user.getSalt()).equals(user.getPassword())) { throw new LyException(LyExceptionEnum.INVALID_USERNAME_OR_PASSWORD); } return user; }