一、项目概述

1、酷鲨引流平台概述

  • 用户管理功能:包括登录、注册、密码找回和短信验证等功能,以确保用户信息的安全和准确性。
  • 商品展示功能:提供商品展示、商城排行榜、分类等功能,帮助用户更轻松地找到自己需要的商品。
  • 首页:包括轮播图、搜索、推荐展示和分列列表等功能,以提高用户的体验和方便性。
  • 二维码、扫码、动态内容、模板等功能,帮助用户更方便地分享和推广商品。

2、项目功能概述

功能概述

二、开发环境

1、基础开发环境

  • 操作系统:Windows/Linux/MacOS
  • 开发语言:Java 8
  • 开发框架:Spring Boot + SSM(Spring+SpringMVC+MyBatis)
  • 数据库:MySQL
  • 服务器:Spring Boot 内嵌Tomcat
  • 集成开发工具:IntelliJ IDEA

2、初始化项目数据库和配置数据库

#应用服务器(tomcat8080)
#相当于properties文件中server.port=8080
server:
  port: 8080

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/coolsharkhub?useSSL=false&serverTimeZone=Asia/Shanghai
    username: root
    password: root

#加载配置文件
mybatis:
  mapper-locations: classpath:mappers/*.xml

3、Spring Boot Profile与开发环境

使用Profile管理配置

Profile的作用就是在不同的环境中,为软件提供不同的配置选项,以确保软件在不同的环境中能够正常运行。Profile的作用包括以下几个方面:

  • 管理环境配置:Profile可以用来管理环境的配置选项,例如数据库配置、端口号、日志等级等,以适应不同环境的要求。
  • 管理资源文件:Profile可以用来管理不同环境的资源文件,例如配置文件、模板文件、静态资源文件等,以确保软件在不同环境下的资源使用正确无误。
  • 管理依赖:Profile可以用来管理软件的依赖,例如数据库驱动、第三方库等,以确保软件在不同环境下的依赖正确无误。
  • 管理日志:Profile可以用来管理软件的日志输出,例如调试信息、错误信息、警告信息等,以方便在不同环境下进行调试和故障排查。
  • 管理安全性:Profile可以用来管理软件的安全配置,例如HTTPS证书、安全认证等,以确保软件在不同环境下的安全性得到保障。

在application.yml文件中添加后会自动查找相应文件

pro

application.yml主文件:

#应用服务器(tomcat8080)
#相当于properties文件中server.port=8080
server:
  port: 8080

#加载当前的开发环境
spring:
  profiles:
    active: dev

#加载配置文件
mybatis:
  mapper-locations: classpath:mappers/*.xml

application-dev.yml文件:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/coolsharkhub?useSSL=false&serverTimeZone=Asia/Shanghai
    username: root
    password: root

4、日志Log

在application-dev.yml文件中配置日志:

logging:
  level:
    cn:
      highedu:
        coolsharkhub:
          mapper: debug

Spring boot使用日志分三步:获取Logger---》记录日志---》配置日志输出

获取Logger
@SpringBootTest
class HelloMapperTest {
    //获取日志对象
    private static final Logger logger = LoggerFactory.getLogger(HelloMapperTest.class);

    @Autowired
    HelloMapper mapper;

    @Test
    void helloWorld() {
        String helloWorld = mapper.helloWorld();
        //以info的级别输出日志
        logger.info(helloWorld);
        //支持占位符操作
        String username = "Tom";
        //输出:Tom对控制台说Hello World
        logger.info("{}对控制台说{}",username,helloWorld);
    }
}
记录日志
logging:
  level:
    cn:
      highedu:
        coolsharkhub:
          mapper: debug
配置日志输出

Spring Boot 日志级别

  • TRACE:记录应用程序的最详细信息,通常用于诊断和调试目的
  • DEBUG:记录应用程序中有用的调试信息
  • INFO:记录应用程序正常运行的关键事件和操作
  • WARN:记录可能指示潜在问题的异常或非预期事件
  • ERROR:记录错误和异常事件
  • FATAL:记录导致应用程序无法继续运行的严重错误

三、功能实现

1、登录功能

登录

(1)创建实体类

/**
 * @document: 用户实体类 对应数据库表user
 * @Author:SmallG
 * @CreateTime:2023/9/6+14:15
 */

public class User {
    private Long id;
    private String username;
    private String password;
    private String role;
    private LocalDateTime created; //创建时间
    private LocalDateTime updated; //修改时间
    
    省略...
}

(2)创建DTO

/**
 * @document: 用户登录数据传输对象
 * 用于用户登录信息的传递
 * @Author:SmallG
 * @CreateTime:2023/9/6+14:19
 */

public class UserLoginDTO {
    private String username;
    private String password;

      省略...
}

(3)创建用户持久层接口

/**
 * @document: 用户持久层接口,用于操作数据库表user
 * SQL语句在resource/mappers/UserMapper.xml
 * @Author:SmallG
 * @CreateTime:2023/9/6+14:23
 */

@Mapper //持久层声明
public interface UserMapper {

    /**
     * 查询保证用户名就行了,至于密码对不对是业务层的问题
     * @param username
     * @return
     */
    User selectUserByUsername(String username);
}

(4)创建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="cn.highedu.coolsharkhub.mapper.UserMapper">
    <resultMap id="UserMap" type="cn.highedu.coolsharkhub.pojo.entity.User">
        <id column="id" property="id"></id>
        <result column="username" property="username"></result>
        <result column="password" property="password"></result>
        <result column="role" property="role"></result>
        <result column="created" property="created"></result>
        <result column="updated" property="updated"></result>
    </resultMap>
    <select id="selectUserByUsername" resultMap="UserMap">
        select id, username, password, role, created, updated
        from user
        where username = #{username}
    </select>

</mapper>

测试

@SpringBootTest
class UserMapperTest {
    private static final Logger logger = LoggerFactory.getLogger(UserMapper.class);

    @Autowired
    UserMapper mapper;

    @Test
    void selectUserByUsername() {
        String username = "admin";
        UserLoginDTO userLoginDTO = mapper.selectUserByUsername(username);
        logger.info("登录用户:{}", userLoginDTO);
    }
}

(5)业务层

先创建与页面交互的VO

/**
 * @document: 用户视图对象
 * 封装数据库表user中的部分数据
 * 用于和view层的数据交互
 * @Author:SmallG
 * @CreateTime:2023/9/6+15:17
 */

public class UserVO {
    private Long id;
    private String username;
    private String password;
    private String role;

    省略...
}

创建service层接口:

/**
 * @document: 用户业务层接口
 * @Author:SmallG
 * @CreateTime:2023/9/6+15:22
 */
public interface UserService {
    /**
     * 用户登录
     * 如果登录成功返回用户视图对象
     * 如果登录失败 抛出异常
     * @param username 用户名 不能为空
     * @param password 密码 不能为空
     * @return 返回用户视图对象
     */
    UserVO login(String username,String password);
}

接口实现类:

@Service //业务层声明 告诉spring这是一个业务类
public class UserServiceImpl implements UserService {
    //获取日志对象
    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
    //注入持久层对象
    @Autowired
    private UserMapper userMapper;

    @Override
    public UserVO login(String username, String password) {
        if (username == null || username.trim().isEmpty()) {
            logger.warn("用户名不能为空"); //输出日志信息 级别是警告
            throw new RuntimeException("用户名不能为空"); //抛出异常
        }
        if (password == null || password.trim().isEmpty()) {
            logger.warn("密码不能为空");
            throw new RuntimeException("密码不能为空");
        }
        User user = userMapper.selectUserByUsername(username);
        if (user==null){
            logger.warn("用户名不存在");
            throw new RuntimeException("用户名不存在");
        }
        if (!user.getPassword().equals(password)){
            logger.warn("密码错误");
            throw new RuntimeException("密码错误");
        }
        //创建用户视图对象
        UserVO userVO = new UserVO(user.getId(),user.getUsername(),user.getPassword(),user.getRole());
        //返回用户视图对象
        return userVO;
    }
}

测试方法:

@SpringBootTest
class UserServiceImplTest {
    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);

    @Autowired
    UserService userService;

    @Test
    void login() {
        String username = "admin";
        String password = "123456";
        UserVO userVO = userService.login(username, password);
        logger.info("userVO:{}",userVO);
    }
}

效果展示:

业务层效果

(6)重构自定义异常

在上述业务层代码中,所有的异常类型都是RuntimeException,这样的做法并不好,因为RuntimeException是Java中的基本异常类型,它无法传达具体的业务意外情况。这样做会给调用者造成困扰,因为他们无法根据异常类型区分不同的业务异常情况。

(7)编写usercontroller类

@RestController //控制层声明
public class UserController {

    private static final Logger logger = LoggerFactory.getLogger(UserController.class);

    //注入UserService
    @Autowired
    UserService userService;

    /**
     * 如果登录成功返回用户登录结果
     *
     * @param userLoginDTO 用户登录信息数据传输对象 封装用户名和密码
     * @return
     * @RequestBody注解表示接收来自请求体中的json数据
     */
    //@RequestMapping(value = "/user/login",method = RequestMethod.POST)
    @PostMapping("/user/login") //专门用于处理post请求
    public String login(@RequestBody UserLoginDTO userLoginDTO) {
        logger.info("用户登录信息userLoginDTO:{}", userLoginDTO);
        //调用登录业务
        try {
            UserVO userVO = userService.login(userLoginDTO.getUsername(), userLoginDTO.getPassword());
            return "登录成功";
        } catch (BlankParameterException e) {
            //通过日志打印异常信息
            logger.warn("登录失败:{}", e.getMessage());
            return e.getMessage();
        } catch (UserNotFoundException e) {
            logger.warn("登录失败:{}", e.getMessage());
            return e.getMessage();
        }
    }

}

测试类:

/**
 * @document: 测试用户控制器
 * RANDOM_PORT随机端口号 避免和8080冲突
 * @Author:SmallG
 * @CreateTime:2023/9/6+21:16
 */

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserControllerTest {

    private static final Logger logger = LoggerFactory.getLogger(UserControllerTest.class);

    //注入TestRestTemplate对象 spring mvc提供的可以用于发送请求的对象
    @Autowired
    TestRestTemplate testRestTemplate;

    @Test
    void login() {
        logger.info("用户登录");
        //声明请求地址
        String url = "/user/login";
        //创建用户登录数据传输对象
        UserLoginDTO userLoginDTO = new UserLoginDTO("admin", "123456");
        //发送请求 postForObject方法发送post请求
        //第一个参数是请求地址
        //第二个参数是传递包含请求数据的请求对象
        //第三个参数是返回值类型
        String result = testRestTemplate.postForObject(url, userLoginDTO, String.class);
        logger.info("登录结果:{}",result);
    }
}

测试效果:

usercontroller测试类效果

2、首页显示分类功能

将首页的分类做跳转

(1)创建轮播图表 并导入数据

-- 创建轮播图表 并导入数据
create table banner
(
    id        BIGINT(20) PRIMARY KEY AUTO_INCREMENT,
    image     VARCHAR(100),
    order_num int(11),
    created   datetime NOT NULL DEFAULT NOW(),
    updated   datetime NOT NULL DEFAULT NOW()
);

insert into banner (image, order_num)VALUES ('/imgs/b1.jpg',1);
insert into banner (image, order_num)VALUES ('/imgs/b2.jpg',2);
insert into banner (image, order_num)VALUES ('/imgs/b3.jpg',3);
insert into banner (image, order_num)VALUES ('/imgs/b4.jpg',4);

(2)商品类别

处理过程如下:

  • 前端页面通过 Ajax 发送 GET 请求到控制器,请求获取分类信息。
  • 控制器接收到请求后,调用业务层的方法获取分类信息。
  • 业务层从数据库中获取分类信息,使用 MyBatis 的 Mapper 接口进行查询操作。
  • Mapper 接口返回 Entity 类型的分类信息。
  • 业务层将 Entity 转换为 ViewObject(VO)类型,以符合前端页面的数据显示需求。
  • 业务层返回 VO 类型的分类信息给控制器。
  • 控制器将 VO 类型的分类信息通过响应体返回给前端页面,前端页面根据数据进行渲染。

商品类别实体类:

/**
 * @document: 对应数据库表category
 * @Author:SmallG
 * @CreateTime:2023/9/6+22:10
 */

public class Category {
    private Long id;
    private String name; //类别名
    private Boolean display; //是否显示
    private Integer orderNum; //显示顺序
    private LocalDateTime created; //创建时间
    private LocalDateTime updated;
    
    省略...
}

持久层:

@Mapper //持久层声明
public interface CategoryMapper {
    /**
     * 查询所有商品类别
     *
     * @return 所有商品类别
     */
    List<Category> selectAll();
}

配置文件:

<?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="cn.highedu.coolsharkhub.mapper.CategoryMapper">
    <resultMap id="CategoryMapper" type="cn.highedu.coolsharkhub.pojo.entity.Category">
        <id column="id" property="id"></id>
        <result column="name" property="name"></result>
        <result column="display" property="display"></result>
        <result column="order_num" property="orderNum"></result>
        <result column="created" property="created"></result>
        <result column="updated" property="updated"></result>
    </resultMap>

    <!--查询所有商品类别-->
    <select id="selectAll" resultMap="CategoryMap">
        select *
        from category
        where display = 1
        order by order_num
    </select>
</mapper>

测试类:

@SpringBootTest
class CategoryMapperTest {
    private static final Logger logger = LoggerFactory.getLogger(CategoryMapperTest.class);

    @Autowired
    CategoryMapper categoryMapper;

    @Test
    void selectAll() {
        List<Category> categories = categoryMapper.selectAll();
        categories.forEach(new Consumer<Category>() {
            @Override
            public void accept(Category category) {
                logger.info("category:{}", category);
            }
        });
    }
}
最后修改:2023 年 09 月 06 日
如果觉得我的文章对你有用,请随意赞赏