微服务(四)—— 用户模块(backend-user)

上一篇:微服务(三)—— MyBatis逆向工程模块(backend-generator).

一、创建项目

首先创建一个SpringBoot和Spring Cloud项目,这里需要考虑SpringBoot和Spring Cloud的版本问题
在这里插入图片描述
这是官网给的Spring Cloud对应的Spring Boot版本
在这里插入图片描述
在BackendManageSystem模块new 一个 module
在这里插入图片描述
选择Spring Initializr,点击Next
选择版本
在这里插入图片描述
选择Spring Boot版本,Idea里都是最新版,我使用的是2.2.2版本,创建后修改Maven依赖即可;Lombok用于加构造方法注解;
然后是Web,JDBC、Mybatis相关的依赖,因为用户系统是需要往注册中心注册的,注册中心使用的是Spring Cloud的Eureka组件,所以需要Eureka Discover Client依赖;
在这里插入图片描述
创建项目名称
在这里插入图片描述

二、项目结构

1.目录结构

在这里插入图片描述
controller用来接收前端的请求,下面看UserController的具体细节

package com.aiun.user.controller;
/**
 * 用户模块控制层
 * @author lenovo
 */
@Api(tags = "用户系统相关接口")
@RestController
@RequestMapping("/user/")
public class UserController {
    @Autowired
    private IUserService iUserService;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 用户登录
     * @param username 用户名
     * @param password 密码
     * @return 返回登录信息
     */
    @PostMapping("login")
    @ApiOperation(value = "用户登录", notes = "用户登录")
    public ServerResponse<User> login(String username, String password) {
        ServerResponse<User> response = iUserService.login(username, password);
        return response;
    }

    /**
     * 用户注册
     * @param user 用户信息
     * @return 返回注册信息
     */
    @PostMapping("register")
    @ApiOperation(value = "用户注册")
    public ServerResponse<String> register(User user) {
        return iUserService.register(user);
    }

    /**
     * 退出登录
     * @param request 请求
     * @return 返回退出状态
     */
    @PostMapping("logout")
    @ApiOperation(value = "用户登录退出")
    public ServerResponse<String> logout(HttpServletRequest request) {
        String key = request.getHeader(UserConst.AUTHORITY);
        ValueOperations<String, String> operations = redisTemplate.opsForValue();
        String value = operations.get(key);
        User user = JsonUtils.jsonStr2Object(value, User.class);
        if (key.equals(user.getUsername())) {
            redisTemplate.delete(key);
            return ServerResponse.createBySuccessMessage("退出成功");
        }
        return ServerResponse.createBySuccessMessage("当前用户未登录");
    }

    /**
     * 验证信息
     * @param str 校验的数据
     * @param type 类型(userName, email)
     * @return 返回校验结果
     */
    @PostMapping("check_valid")
    @ApiOperation(value = "验证信息")
    public ServerResponse<String> checkValid(String str, String type){
        return iUserService.checkValid(str, type);
    }

    /**
     * 获取用户信息
     * @param request 请求
     * @return 返回用户信息
     */
    @PostMapping("get_information")
    @ApiOperation(value = "获取用户信息")
    public ServerResponse<User> getInformation(HttpServletRequest request) {
        ServerResponse hasLogin = loginHasExpired(request);
        if (hasLogin.isSuccess()) {
            User user = (User) hasLogin.getData();
            return iUserService.getInformation(user.getId());
        }

        return hasLogin;
    }

    /**
     * 判断用户登录是否过期
     */
    private ServerResponse<User> loginHasExpired(HttpServletRequest request) {
        String key = request.getHeader(UserConst.AUTHORITY);
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        String value = valueOperations.get(key);
        if (StringUtils.isEmpty(value)) {
            return ServerResponse.createByErrorMessage(ResponseCode.NEED_LOGIN.getCode(), ResponseCode.NEED_LOGIN.getDesc());
        }
        User user = JsonUtils.jsonStr2Object(value, User.class);
        if (!key.equals(user.getUsername())) {
            return ServerResponse.createByErrorMessage(ResponseCode.NEED_LOGIN.getCode(), ResponseCode.NEED_LOGIN.getDesc());
        }
        valueOperations.set(key, value, 1, TimeUnit.HOURS);
        return ServerResponse.createBySuccess(user);
    }
}

这里涉及到接口文档描述Swagger和微服务单点登录问题,看后面介绍。
微服务(十二)—— Swagger.
微服务(十)—— 单点登录.
controller层调用service层接口,下面看service层的实现类UserServiceImpl

package com.aiun.user.service.impl;

/**
 * 用户模块实现类
 * @author lenovo
 */
@Service("iUserService")
public class UserServiceImpl implements IUserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Override
    public ServerResponse<User> login(String userName, String password) {
        int resultCount = userMapper.checkUsername(userName);
        if (resultCount <= 0) {
            return ServerResponse.createByErrorMessage("用户名不存在");
        }
        //密码登录并MD5加密
        String md5password = MD5Utils.MD5EncodeUtf8(password);
        User user = userMapper.selectLogin(userName, md5password);
        if (user == null) {
            return ServerResponse.createByErrorMessage("密码错误");
        }
        String key = userName;
        String token = JsonUtils.object2JsonStr(user);
        ValueOperations<String, String>valueOperations = redisTemplate.opsForValue();
        //用户登录成功后,将该用户对象作为token值保存到redis里,并且设置过期时间为1小时
        valueOperations.set(key, token, 1, TimeUnit.HOURS);
        return ServerResponse.createBySuccess("登录成功", user);
    }

    @Override
    public ServerResponse<String> register(User user) {
        ServerResponse validResponse = this.checkValid(user.getUsername(), UserConst.USERNAME);
        if (!validResponse.isSuccess()) {
            return validResponse;
        }
        validResponse = this.checkValid(user.getEmail(), UserConst.EMAIL);
        if (!validResponse.isSuccess()) {
            return validResponse;
        }

        // 指定用户角色
        user.setRole(UserConst.Role.ROLE_CUSTOMER);
        // MD5 加密
        user.setPassword(MD5Utils.MD5EncodeUtf8(user.getPassword()));

        int resultCount = userMapper.insert(user);
        if(resultCount == 0) {
            return ServerResponse.createByErrorMessage("注册失败");
        }

        return ServerResponse.createBySuccessMessage("注册成功");
    }

    @Override
    public ServerResponse<User> getInformation(int userId) {
        User user = userMapper.selectByPrimaryKey(userId);
        if(user == null) {
            return ServerResponse.createByErrorMessage("当前用户不存在");
        }
        user.setPassword(StringUtils.EMPTY);

        return ServerResponse.createBySuccess(user);
    }
    //.......
}

用户接口层IUserService

package com.aiun.user.service;

/**
 * 用户接口
 * @author lenovo
 */
public interface IUserService {
    /**
     * 用户登录
     * @param userName
     * @param password
     * @return
     */
    ServerResponse<User> login(String userName, String password);
    /**
     * 注册
     * @param user
     * @return
     */
    ServerResponse<String> register(User user);

    /**
     * 用户名或邮箱的校验
     * @param str
     * @param type
     * @return
     */
    ServerResponse<String> checkValid(String str, String type);

    /**
     * 获取用户信息
     * @param userId
     * @return
     */
    ServerResponse<User> getInformation(int userId);

    /**
     * 更改密码
     * @param oldPassword
     * @param newPassword
     * @param user
     * @return
     */
    ServerResponse<String> resetPassword(String oldPassword, String newPassword, User user);
}

Server层的实现类调用DAO层的Mapper层,UserMapper如下:

package com.aiun.user.mapper;

@Mapper
@Repository
public interface UserMapper {
    /**
     * 根据主键删除用户
     * @param id 主键
     * @return 影响的记录行数
     */
    int deleteByPrimaryKey(Integer id);

    /**
     * 插入用户
     * @param record 用户信息
     * @return 影响的记录行数
     */
    int insert(@Param("record") User record);

    /**
     * 有选择的插入用户
     * @param record 用户信息
     * @return 影响的记录行数
     */
    int insertSelective(@Param("record") User record);

    /**
     * 通过主键查询用户
     * @param id 主键
     * @return 用户信息
     */
    User selectByPrimaryKey(Integer id);

    /**
     * 通过主键有选择的更新用户
     * @param record 用户信息
     * @return 影响的记录行数
     */
    int updateByPrimaryKeySelective(@Param("record") User record);

    /**
     * 通过主键更新用户
     * @param record 用户信息
     * @return 影响的记录行数
     */
    int updateByPrimaryKey(@Param("record") User record);

    /**
     * 登录
     * @param username 用户名
     * @param password 密码
     * @return 返回用户信息
     */
    User selectLogin(@Param("username") String username, @Param("password") String password);

    /**
     * 检查用户名
     * @param username 用户名
     * @return 影响的记录行数
     */
    int checkUsername(String username);

    /**
     * 检查email
     * @param email 邮箱
     * @return 影响的记录行数
     */
    int checkEmail(String email);

    /**
     * 检查密码
     * @param password
     * @param userId
     * @return
     */
    int checkPassword(@Param("password") String password, @Param("userId") Integer userId);
}

注:这个Mapper层的接口除了要写Spring的@Repository注解,还需要写@Mapper注解,这个注解是告诉SpringBoot这是DAO层的Mapper映射接口,与这个接口对应的是mapper.xml文件,注意是SQL语句的编写,如下UserMapper.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.aiun.user.mapper.UserMapper" >
  <resultMap id="BaseResultMap" type="com.aiun.user.pojo.User" >
    <constructor >
      <idArg column="id" jdbcType="INTEGER" javaType="java.lang.Integer" />
      <arg column="username" jdbcType="VARCHAR" javaType="java.lang.String" />
      <arg column="password" jdbcType="VARCHAR" javaType="java.lang.String" />
      <arg column="email" jdbcType="VARCHAR" javaType="java.lang.String" />
      <arg column="phone" jdbcType="VARCHAR" javaType="java.lang.String" />
      <arg column="question" jdbcType="VARCHAR" javaType="java.lang.String" />
      <arg column="answer" jdbcType="VARCHAR" javaType="java.lang.String" />
      <arg column="role" jdbcType="INTEGER" javaType="java.lang.Integer" />
      <arg column="create_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />
      <arg column="update_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />
    </constructor>
  </resultMap>
  <sql id="Base_Column_List" >
    id, username, password, email, phone, question, answer, role, create_time, update_time
  </sql>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select
    <include refid="Base_Column_List" />
    from trade_user
    where id = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
    delete from trade_user
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="com.aiun.user.pojo.User" >
    insert into trade_user (id, username, password,
      email, phone, question,
      answer, role, create_time,
      update_time)
    values (#{record.id,jdbcType=INTEGER}, #{record.username,jdbcType=VARCHAR}, #{record.password,jdbcType=VARCHAR},
      #{record.email,jdbcType=VARCHAR}, #{record.phone,jdbcType=VARCHAR}, #{record.question,jdbcType=VARCHAR},
      #{record.answer,jdbcType=VARCHAR}, #{record.role,jdbcType=INTEGER}, now(),
      now())
  </insert>
  <insert id="insertSelective" parameterType="com.aiun.user.pojo.User" >
    insert into trade_user
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        id,
      </if>
      <if test="username != null" >
        username,
      </if>
      <if test="password != null" >
        password,
      </if>
      <if test="email != null" >
        email,
      </if>
      <if test="phone != null" >
        phone,
      </if>
      <if test="question != null" >
        question,
      </if>
      <if test="answer != null" >
        answer,
      </if>
      <if test="role != null" >
        role,
      </if>
      <if test="createTime != null" >
        create_time,
      </if>
      <if test="updateTime != null" >
        update_time,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        #{record.id,jdbcType=INTEGER},
      </if>
      <if test="username != null" >
        #{record.username,jdbcType=VARCHAR},
      </if>
      <if test="password != null" >
        #{record.password,jdbcType=VARCHAR},
      </if>
      <if test="email != null" >
        #{record.email,jdbcType=VARCHAR},
      </if>
      <if test="phone != null" >
        #{record.phone,jdbcType=VARCHAR},
      </if>
      <if test="question != null" >
        #{record.question,jdbcType=VARCHAR},
      </if>
      <if test="answer != null" >
        #{record.answer,jdbcType=VARCHAR},
      </if>
      <if test="role != null" >
        #{record.role,jdbcType=INTEGER},
      </if>
      <if test="createTime != null" >
        #{record.createTime,jdbcType=TIMESTAMP},
      </if>
      <if test="updateTime != null" >
        now(),
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.aiun.user.pojo.User" >
    update trade_user
    <set >
      <if test="record.username != null" >
        username = #{record.username,jdbcType=VARCHAR},
      </if>
      <if test="record.password != null" >
        password = #{record.password,jdbcType=VARCHAR},
      </if>
      <if test="record.email != null" >
        email = #{record.email,jdbcType=VARCHAR},
      </if>
      <if test="record.phone != null" >
        phone = #{record.phone,jdbcType=VARCHAR},
      </if>
      <if test="record.question != null" >
        question = #{record.question,jdbcType=VARCHAR},
      </if>
      <if test="record.answer != null" >
        answer = #{record.answer,jdbcType=VARCHAR},
      </if>
      <if test="record.role != null" >
        role = #{record.role,jdbcType=INTEGER},
      </if>
      <if test="record.createTime != null" >
        create_time = #{record.createTime,jdbcType=TIMESTAMP},
      </if>
      <if test="record.updateTime != null" >
        update_time = now(),
      </if>
    </set>
    where id = #{record.id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.aiun.user.pojo.User" >
    update trade_user
    set username = #{record.username,jdbcType=VARCHAR},
      password = #{record.password,jdbcType=VARCHAR},
      email = #{record.email,jdbcType=VARCHAR},
      phone = #{record.phone,jdbcType=VARCHAR},
      question = #{record.question,jdbcType=VARCHAR},
      answer = #{record.answer,jdbcType=VARCHAR},
      role = #{record.role,jdbcType=INTEGER},
      create_time = #{record.createTime,jdbcType=TIMESTAMP},
      update_time = now()
    where id = #{id,jdbcType=INTEGER}
  </update>
  <select id="checkUsername" resultType="int" parameterType="string">
    select count(1) from trade_user
    where username = #{username}
  </select>
  <select id="selectLogin" resultMap="BaseResultMap" parameterType="map">
    select
    <include refid="Base_Column_List" />
    from trade_user
    where username = #{username}
    and password = #{password}
  </select>
  <select id="checkEmail" resultType="int" parameterType="string">
    select count(1) from trade_user
    where email = #{email}
  </select>
  <select id="checkPassword" resultType="int" parameterType="map">
    select count(1) from trade_user
    where id = #{userId}
    and password = #{password}
  </select>
</mapper>

对于返回User对象的我用BaseResultMap进行映射和封装。
数据库建表语句:

CREATE TABLE reade_user (
	id int(11) NOT NULL AUTO_INCREMENT COMMENT ‘用户表id’,
	username varchar(50) NOT NULL COMMENT ‘用户名’,
	password varchar(50) NOT NULL COMMENT ‘用户密码,MD5加密’,
	email varchar(50) DEFAULT NULL,
	phone varchar(20) DEFAULT NULL,
	question varchar(100) DEFAULT NULL COMMENT ‘找回密码问题’,
	answer varchar(100) DEFAULT NULL COMMENT ‘找回密码答案’,
	role int(4) NOT NULL COMMENT ‘角色0-管理员,1-普通用户’,
	create_time datetime NOT NULL COMMENT ‘创建时间’,
	update_time datetime NOT NULL COMMENT ‘最后一次更新时间’,
	PRIMARY KEY (id),
	UNIQUE KEY user_name_unique (username) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8;

2.pom.xml

首先引入父模块依赖

<parent>
    <groupId>com.aiun</groupId>
    <artifactId>BackendManageSystem</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent>

引入backend common 依赖

<dependency>
    <groupId>com.aiun</groupId>
    <artifactId>backend-common</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

由于我在父模块添加了Spring Cloud依赖,所以这里就不需要添加Spring Cloud依赖,父模块的pom.xml文件见微服务(一)—— 项目结构.
pom.xml文件

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.aiun</groupId>
        <artifactId>BackendManageSystem</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <groupId>com.aiun</groupId>
    <artifactId>backend-user</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>backend-user</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!--backend common 依赖-->
        <dependency>
            <groupId>com.aiun</groupId>
            <artifactId>backend-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--config client 依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-client</artifactId>
        </dependency>
        <!--spring boot test 依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--spring boot web 依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- mybatis 依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <!--mysql 依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--druid 连接池依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <!-- lombok 依赖 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- swagger 依赖-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <releases>
                <enabled>false</enabled>
            </releases>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
</project>

3.配置文件application.yml

# Spring
spring:
 # 服务应用名称
 application:
  name: backend-user
  # 数据源
 datasource:
  driver-class-name: com.mysql.cj.jdbc.Driver
  url: jdbc:mysql://localhost:3306/trade?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
  username: root
  password: root
  # 指定druid 连接池以及druid 连接池配置
  type: com.alibaba.druid.pool.DruidDataSource
  druid:
   initial-size: 1          # 初始连接数
   max-active: 20           # 最大连接数
   max-idle: 20             # 最大空闲
   min-idle: 1              # 最小空闲
   max-wait: 60000          # 最长等待时间

server:
 port: 8084                  # 项目访问端口
 servlet:
  context-path: /backend-user  # 项目访问路径

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
  instance:
    hostname: localhost

# MyBatis
mybatis:
 # 配置 MyBatis 数据返回类型别名(默认别名是类名)
 type-aliases-package: com.aiun.user.pojo
 # 配置 MyBatis Mapper 映射文件
 mapper-locations: classpath:/mappers/*.xml

# MyBatis SQL 打印(方法接口所在的包,不是 Mapper.xml 所在的包)
# logging:
 # level:
 # com.aiun.order.mappers: debug
 # pagehelper
 pagehelper:
   helperDialect: sqlite #postgresql
   reasonable: true
   supportMethodsArguments: true
   params: countSql
   count: countSql
   returnPageInfo: check

 # 记录日志
 logging:
   config: classpath:logback-spring.xml

该文件是SpringBoot的配置文件,与properties文件不同的是它是键: 值,并且冒号后面要有一个空格,包的子目录也是通过空格体现,而且一个文件可以配置几个模块,例如上面我配置了开发环境dev和测试环境test,通过—符号进行分割。
这个配置文件主要配置的是应用名称、访问路径、端口、数据库、数据库连接池、日志和其他框架的配置。

4.启动项目

打开SpringBoot的启动类UserApplication

package com.aiun.user;

/**
 * 用户模块启动类
 * @author lenovo
 */
@SpringBootApplication
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class, args);
    }
}

启动该项目即可,然后就可以通过访问http://localhost:8084/backend-user/user/访问项目了
我这里通过Google插件Talend API Test进行后端接口测试
在这里插入图片描述
成功!!!
到这里用户模块就完成了。
注:项目已上传GitHub:https://github.com/AiunCode/BackendManageSystem.

下一篇:微服务(五)—— 产品模块(backend-product).

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值