做为一个现代化电商平台,什么最重要呢?of course 是用户,广大用户群体是支持咱们可持续发展的基石,顾客是上帝
, 虽然在当今上帝已经不被重视了,特别是不少的平台对于老用户就是巴不得赶忙Out...可是用户量是一切的基础,那咱们就开始建立咱们的上帝吧!前端
数据库的部分,我在这里就很少讲了,你们须要的话能够直接去传送门 抓取脚本expensive-shop.sql
.java
参考上节内容:传送门git
首先,咱们先来分析一下要注册一个用户,咱们系统都须要作哪些动做?
github
FrontEnd validsql
前端校验是为了下降咱们服务器端压力而作的一部分校验,这部分校验能够拦截大多数的错误请求。shell
Backend valid数据库
后端校验是为了防止某些不法小伙伴绕开前端从而直接访问咱们的api形成数据请求服务器错误,或者前端小伙伴程序有bug...不管是哪种可能性,都有可能形成严重的后果。后端
email & mobile invalidapi
由于本人没有追求email / 短信发送服务器,因此这一步就pass,小伙伴们能够自行研究哈。
create user
校验经过后,就能够进行建立用户的动做了。
接下来,咱们就能够来实际编码实现业务了,咱们使用最基本的分层架构,在以前咱们已经经过Mybatis Generator
工具生成了基本的pojo
,mapper
,对于简单的操做咱们只须要再编写service
和controller
层就能够完成咱们的开发工做了。
在mscx-shop-service
中建立com.liferunner.service.IUserService
接口,包含2个方法findUserByUserName
和createUser
,以下:
public interface IUserService { /** * 根据用户名查询用户是否存在 * * @param username * @return */ Users findUserByUserName(String username); /** * 建立用户 * * @param userRequestDTO 用户请求dto * @return 当前用户 */ Users createUser(UserRequestDTO userRequestDTO) throws Exception; }
接着,咱们须要具体实现这个接口类,以下:
@Service @Slf4j public class UserServiceImpl implements IUserService { private final String FACE_IMG = "https://avatars1.githubusercontent.com/u/4083152?s=88&v=4"; // 构造器注入 private final UsersMapper usersMapper; private final Sid sid; @Autowired public UserServiceImpl(UsersMapper usersMapper, Sid sid) { this.usersMapper = usersMapper; this.sid = sid; } @Override public Users findUserByUserName(String username) { // 构建查询条件 Example example = new Example(Users.class); val condition = example.createCriteria() .andEqualTo("username", username); return this.usersMapper.selectOneByExample(example); } @Transactional(propagation = Propagation.REQUIRED) @Override public Users createUser(UserRequestDTO userRequestDTO) throws Exception { log.info("======begin create user : {}=======", userRequestDTO); val user = Users.builder() .id(sid.next()) //生成分布式id .username(userRequestDTO.getUsername()) .password(MD5GeneratorTools.getMD5Str(userRequestDTO.getPassword())) .birthday(DateUtils.parseDate("1970-01-01", "yyyy-MM-dd")) .nickname(userRequestDTO.getUsername()) .face(this.FACE_IMG) .sex(SexEnum.secret.type) .createdTime(new Date()) .updatedTime(new Date()) .build(); this.usersMapper.insertSelective(user); log.info("======end create user : {}=======", userRequestDTO); return user; } }
这里有几处地方有必要说明一下:
tk.mybatis.mapper.entity.Example
经过使用Example来构建mybatis的查询参数,若是有多个查询条件,能够经过example.createCriteria().addxxx
逐一添加。@Transactional(propagation = Propagation.REQUIRED)
,开启事务,选择事务传播级别为REQUIRED
,表示必需要有一个事务存在,若是调用者不存在事务,那本方法就本身开启一个新的事物,若是调用方自己存在一个活跃的事务,那本方法就加入到它里面(同生共死)。org.n3r.idworker.Sid
, 这个是一个开源的 分布式ID生成器组件,传送门, 后期有机会的话,会专门写一个id生成器文章。MD5GeneratorTools
是用来对数据进行MD5加密的工具类,你们能够在源码中下载。也能够直接使用java.security.MessageDigest
直接加密实现,总之密码不能明文存储就好了。SexEnum
这个是一个表述性别类型的枚举,在咱们编码的规范中,尽可能要求不要出现Magic number
,就是开发界常说的魔术数字(即1,2,300...)private final static Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
,这是由于咱们在开始的时候,咱们引入了lombok
依赖,不记得的同窗能够参考传送门。在这里依赖中,它继承了不少的日志组件,咱们只须要使用一个注解lombok.extern.slf4j.Slf4j
来开启日志,使用log.info..
就能够了。UserRequestDTO
又是个什么鬼?在咱们开发的过程当中,极可能会有大批量的参数须要传递,这时咱们若是使用xxx#(String aa,Integer bb,Boolean cc...)
会让咱们烦不胜数,并且看着也不美观,这时候咱们就能够选择建立一个新对象来帮助咱们传递数据,那么也就是咱们的UserRequestDTO
对象,所谓的DTO
就是Data Transfer Object
的首字母缩写,顾名思义,它是用来传递数据对象用的。一样在mscx-shop-api
中,建立com.liferunner.api.controller.UserController
,实现用户建立。
@RestController @RequestMapping(name = "/users") @Slf4j @Api(tags="用户管理") public class UserController { @Autowired private IUserService userService; @ApiOperation("校验是否重名") @GetMapping("/validateUsername") public JsonResponse validateUsername(@RequestParam String username) { // 判断用户名是否非法 if (StringUtils.isBlank(username)) return JsonResponse.errorMsg("用户名不能为空!"); if (null != userService.findUserByUserName(username)) return JsonResponse.errorMsg("用户名已存在!"); // 用户名可用 return JsonResponse.ok(); } @ApiOperation("建立用户") @PostMapping("/create") public JsonResponse createUser(@RequestBody UserRequestDTO userRequestDTO) { try { if (StringUtils.isBlank(userRequestDTO.getUsername())) return JsonResponse.errorMsg("用户名不能为空"); if (null != this.userService.findUserByUserName(userRequestDTO.getUsername())) { return JsonResponse.errorMsg("用户名已存在!"); } if (StringUtils.isBlank(userRequestDTO.getPassword()) || StringUtils.isBlank(userRequestDTO.getConfimPassword()) || userRequestDTO.getPassword().length() < 8) { return JsonResponse.errorMsg("密码为空或长度小于8位"); } if (!userRequestDTO.getPassword().equals(userRequestDTO.getConfimPassword())) return JsonResponse.errorMsg("两次密码不一致!"); val user = this.userService.createUser(userRequestDTO); if (null != user) return JsonResponse.ok(user); } catch (Exception e) { log.error("建立用户失败,{}", userRequestDTO); } return JsonResponse.errorMsg("建立用户失败"); } }
JsonResponse
对象是为了方便返回给客户端一个统一的格式而封装的数据对象。@Data @NoArgsConstructor @AllArgsConstructor public class JsonResponse { // 定义jackson对象 private static final ObjectMapper MAPPER = new ObjectMapper(); // 响应业务状态 private Integer status; // 响应消息 private String message; // 响应中的数据 private Object data; public static JsonResponse build(Integer status, String msg, Object data) { return new JsonResponse(status, msg, data); } public static JsonResponse ok(Object data) { return new JsonResponse(data); } public static JsonResponse ok() { return new JsonResponse(null); } public static JsonResponse errorMsg(String msg) { return new JsonResponse(500, msg, null); } public static JsonResponse errorMap(Object data) { return new JsonResponse(501, "error", data); } public static JsonResponse errorTokenMsg(String msg) { return new JsonResponse(502, msg, null); } public static JsonResponse errorException(String msg) { return new JsonResponse(555, msg, null); } public static JsonResponse errorUserQQ(String msg) { return new JsonResponse(556, msg, null); } public JsonResponse(Object data) { this.status = 200; this.message = "OK"; this.data = data; } public Boolean isOK() { return this.status == 200; } }
JsonResponse
@Api(tags="用户管理")
,@ApiOperation("建立用户")
,这个是Swagger 的注解,咱们会在下一节和你们详细探讨,以及如何生成off-line docs
。在咱们每次修改完成以后,都尽量的mvn clean install
一次,由于咱们隶属不一样的project,若是不从新安装一次,偶尔遇到的问题会让人怀疑人生的。
... [INFO] expensive-shop ..................................... SUCCESS [ 1.220 s] [INFO] mscx-shop-common ................................... SUCCESS [ 9.440 s] [INFO] mscx-shop-pojo ..................................... SUCCESS [ 2.020 s] [INFO] mscx-shop-mapper ................................... SUCCESS [ 1.564 s] [INFO] mscx-shop-service .................................. SUCCESS [ 1.366 s] [INFO] mscx-shop-api ...................................... SUCCESS [ 4.614 s] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 20.739 s [INFO] Finished at: 2019-11-06T14:53:55+08:00 [INFO] ------------------------------------------------------------------------
当看到上述运行结果以后,就能够启动咱们的应用就行测试啦~
测试API的方式有不少种,好比curl localhost:8080/validateUsername
,在好比使用超级流行的Postman
也是彻底ok的,我这里用的是以前在第一篇中和你们所说的一个插件Restful Toolkit(能够实现和postman同样的简单效果,同时还能帮助咱们生成一部分测试信息)
,当咱们应用启动以后,效果以下图,
咱们能够看到,插件帮咱们生成了几个测试方法,好比咱们点击validateUsername
,下方就会生成当前方法是一个包含username
参数的GET
方法,demoData
是插件默认给咱们生成的测试数据。能够随意修改。
点击Send:
能够看到请求成功了,而且返回咱们自定义的JSON格式数据。
接着咱们继续测试用户注册接口,请求以下:
能够看到,当咱们选择create
方法时,插件自动帮咱们设置请求类型为POST
,而且RequestBody
的默认值也帮助咱们生成了,我只修改了默认的username
和password
值,confimPassword
的默认值我没有变更,那按照咱们的校验逻辑,它应该返回的是return JsonResponse.errorMsg("两次密码不一致!");
这一行,点击Send:
修改confimPassword
为12345678
,点击Send:
能够看到,建立用户成功,而且将当前建立的用户返回到了咱们请求客户端。那么咱们继续重复点击建立,会怎么样呢?继续Send:
能够看到,咱们的验证重复用户也已经生效啦。
下一节咱们将学习如何使用Swagger自动生成API接口文档给前端,以及若是没有外部网络的状况下,或者须要和第三方平台对接的时候,咱们如何生成离线文档
给到第三方。 gogogo!