一、项目概述
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文件中添加后会自动查找相应文件
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);
}
}
测试效果:
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);
}
});
}
}