spring boot3单模块项目工程搭建-上(个人开发模板)

news2024/12/24 10:18:52

 

⛰️个人主页:     蒾酒

🔥系列专栏:《spring boot实战》

🌊山高路远,行路漫漫,终有归途


目录

写在前面

上文衔接

常规目录创建

common目录

exception.handle目录

result.handle目录

controller目录

service目录

mapper目录

entity目录

test目录

写在最后


写在前面

本文介绍了springboot开发后端服务,单模块项目工程搭建。单模块搭建出完会出多模块项目搭建。坚持看完相信对你有帮助。

同时欢迎订阅springboot系列专栏,持续分享spring boot的使用经验。

上文衔接

本文衔接上文,可以看一下:

新版idea(2023)创建spring boot3项目_新版idea2023创建springboot3-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_62262918/article/details/135785412?spm=1001.2014.3001.5501

上文我们已经通过spring官网下载了一个模板,本文继续搭建一个前后端分离架构中后端接口服务单模块工程

常规目录创建

如图:

我们一个一个来讲解吧

common目录

此目录用于存放全局会用到的一些静态常量类、枚举类、业务异常类、工具类、自定义注解、切面类、DTO、VO、配置类等都可以放在该目录下

exception.handle目录

存放全局异常处理类。

感兴趣可以看看

Spring Boot3自定义异常及全局异常捕获_springboot 自定义异常获取-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_62262918/article/details/136110267?spm=1001.2014.3001.5501

result.handle目录

存放全局返回格式统一处理类。

感兴趣可以看看

Spring Boot3统一结果封装_spring boot结果集封装-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_62262918/article/details/136075039?spm=1001.2014.3001.5501

controller目录

此目录用于存放控制器类(负责接收用户的请求、调用适当的业务逻辑处理请求,并将处理结果返回给用户的类

例如userController:

import com.mijiu.commom.aop.annotation.RepeatSubmit;
import com.mijiu.commom.model.dto.UserLoginDTO;
import com.mijiu.commom.model.dto.UserSmsLoginDTO;
import com.mijiu.commom.model.vo.UserLoginVO;
import com.mijiu.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;

import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;


/**
 * <p>
 * 用户表 前端控制器
 * </p>
 *
 * @author 蒾酒
 * @since 2024-02-03
 */
@RestController
@RequestMapping("/user")
@CrossOrigin(origins = "*")//允许所有来源的请求跨域
@Tag(name = "用户模块")
public class UserController {
    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping("/login")
    @RepeatSubmit(interval = 5000)
    @Operation(summary = "用户账密登录")
    public UserLoginVO login(@RequestBody @Validated UserLoginDTO userLoginDTO) {
        return userService.login(userLoginDTO);
    }

    @PostMapping("/login/sms")
    @Operation(summary = "用户短信验证登录")
    public UserLoginVO smsLogin(@RequestBody @Validated UserSmsLoginDTO userSmsLoginDTO) {
        return userService.smsLogin(userSmsLoginDTO);
    }

}

 上述代码中的@RepeatSubmit(interval = 5000)这个自定义注解用来防止重复提交此处用来防止重复登录,这个注解就是放在Common/annotation/目录下的。

这个防重复提交功能是基于自定义注解+AOP实现的,那对应的切面类就是放在Common/aop/目录下的。

通常控制层是不写任何业务逻辑的,它的作用主要把业务功能暴漏为接口,再者进行参数校验

spring boot3参数校验基本用法_springboot3使用校验类注解-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_62262918/article/details/136180252?spm=1001.2014.3001.5501就比用户控制器类包定义了两个接口,用户的账号密码登录和短信验证登录,那么它就要依赖下层的用户业务逻辑接口的实现类的对应实现方法。下面就介绍一下service目录

service目录

前面也提到过了service目录就是用来放各种业务功能规范接口和对应实现类的

例如UserService、UserServiceImpl:

import com.mijiu.commom.model.dto.UserLoginDTO;
import com.mijiu.commom.model.dto.UserSmsLoginDTO;
import com.mijiu.commom.model.vo.UserLoginVO;
import com.mijiu.entity.User;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * <p>
 * 用户表 服务类
 * </p>
 *
 * @author 蒾酒
 * @since 2024-02-03
 */
public interface UserService extends IService<User> {


    /**
     *
     * @param userLoginDTO 用户登录表单
     * @return 用户信息返回
     */
     UserLoginVO login(UserLoginDTO userLoginDTO);

    /**
     *
     * @param userSmsLoginDTO 用户手机号登录表单
     * @return 用户信息返回
     */
     UserLoginVO smsLogin(UserSmsLoginDTO userSmsLoginDTO);


}
import java.util.Map;
import java.util.Objects;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author 蒾酒
 * @since 2024-02-03
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final UserMapper userMapper;
    private final JwtUtils jwtUtils;
    private final StringRedisTemplate stringRedisTemplate;

    public UserServiceImpl(UserMapper userMapper, JwtUtils jwtUtils, StringRedisTemplate stringRedisTemplate) {
        this.userMapper = userMapper;
        this.jwtUtils = jwtUtils;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public UserLoginVO login(UserLoginDTO userLoginDTO) {
        // 获取验证码id
        String captchaId = userLoginDTO.getCaptchaId();
        // 获取用户提交验证码
        String userCaptcha = userLoginDTO.getCaptcha();
        // 获取缓存验证码
        String cacheCaptcha = stringRedisTemplate.opsForValue().get("login:captcha:" + captchaId);
        // 比较验证码是否正确
        if (cacheCaptcha == null || !cacheCaptcha.equalsIgnoreCase(userCaptcha)) {
            throw new CaptchaErrorException(ResultEnum.USER_CAPTCHA_ERROR);
        }
        // 判断用户是否存在
        User loginUser = new LambdaQueryChainWrapper<>(userMapper)
                .select(User::getId, User::getUserAccount, User::getPassword,
                        User::getUserName, User::getUserRole,
                        User::getAvatar, User::getStatus)
                .eq(User::getUserAccount, userLoginDTO.getUserAccount())
                .one();
        if (loginUser == null) {
            throw new AccountNotFoundException(ResultEnum.USER_NOT_EXIST);
        }
        log.info("loginUser: {}", loginUser);
        // 判断密码是否正确
        String md5Password = DigestUtils.md5DigestAsHex(userLoginDTO.getPassword().getBytes());
        if (!md5Password.equals(loginUser.getPassword())) {
            throw new PasswordErrorException(ResultEnum.USER_PASSWORD_ERROR);
        }
        // 判断用户状态是否正常
        if (!loginUser.getStatus()) {
            throw new AccountForbiddenException(ResultEnum.USER_ACCOUNT_FORBIDDEN);
        }
        // 生成token
        String token = jwtUtils.generateToken(Map.of("userId", loginUser.getId(),
                        "userRole", loginUser.getUserRole()),
                "user");
        //构建响应对象
        return UserLoginVO.builder()
                .userName(loginUser.getUserName())
                .avatar(loginUser.getAvatar())
                .token(token)
                .build();
    }

    @Override
    public UserLoginVO smsLogin(UserSmsLoginDTO userSmsLoginDTO) {
        // 校验验证码是否存在
        HashOperations<String, String, String> hashOps = stringRedisTemplate.opsForHash();
        String captcha = hashOps.get("login:sms:captcha:" + userSmsLoginDTO.getPhone(), "captcha");

        if (StringUtils.isEmpty(captcha)) {
            log.error("手机号 {} 的验证码不存在或已过期", userSmsLoginDTO.getPhone());
            throw new CaptchaErrorException(ResultEnum.USER_CAPTCHA_NOT_EXIST);
        }

        // 查询用户是否已注册
        User loginUser = new LambdaQueryChainWrapper<>(userMapper).eq(User::getPhone, userSmsLoginDTO.getPhone()).one();

        // 如果未注册则进行注册
        if (Objects.isNull(loginUser)) {
            loginUser = register(userSmsLoginDTO.getPhone());
        }

        // 校验验证码是否正确
        if (!userSmsLoginDTO.getCaptcha().equals(captcha)) {
            log.error("手机号 {} 的验证码错误", userSmsLoginDTO.getPhone());
            throw new CaptchaErrorException(ResultEnum.AUTH_CODE_ERROR);
        }
        //判断用户是否被禁用
        if (!loginUser.getStatus()) {
            throw new AccountForbiddenException(ResultEnum.USER_ACCOUNT_FORBIDDEN);
        }
        log.info("手机号 {} 用户登录成功", userSmsLoginDTO.getPhone());

        return UserLoginVO.builder()
                .token(jwtUtils.generateToken(Map.of("userId", loginUser.getId()), "user"))
                .userName(loginUser.getUserName())
                .build();
    }

    private User register(String phone) {
        User user = new User();
        user.setPhone(phone);
        user.setUserName(phone);
        user.setStatus(true);
        if (userMapper.insert(user) < 1) {
            log.error("手机号 {} 用户注册失败!", phone);
            throw new AccountRegisterFailException(ResultEnum.USER_REGISTER_FAIL);
        }
        log.info("手机号 {} 用户注册成功", phone);
        return user;
    }
}

感兴趣这两种登录功能专业的实现方法的可以看下:

spring boot3登录开发-3(1账密登录逻辑实现)_springboot3登录-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_62262918/article/details/136124858?spm=1001.2014.3001.5501spring boot3登录开发-2(2短信验证码接口实现)-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_62262918/article/details/136888851?spm=1001.2014.3001.5501回到正题控制层依赖业务逻辑层,业务逻辑层则依赖下层mapper(DAO)层---数据访问层,

下面继续介绍mapper目录

mapper目录

该层存放数据访问接口类通常只需要定义出接口具体的操作数据库的逻辑是借助ORM(对象关系映射)框架---mybatis/mybatis-plue/jpa等来快捷编写或者直接生成的。

例如UserMapper、UserMapper.xml:

import com.mijiu.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

/**
 * <p>
 * 用户表 Mapper 接口
 * </p>
 *
 * @author 蒾酒
 * @since 2024-02-03
 */
@Mapper
public interface UserMapper extends BaseMapper<User> {

}
<?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.mijiu.mapper.UserMapper">

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.mijiu.entity.User">
        <id column="id" property="id" />
        <result column="user_name" property="userName" />
        <result column="password" property="password" />
        <result column="user_account" property="userAccount" />
        <result column="user_role" property="userRole" />
        <result column="avatar" property="avatar" />
        <result column="create_time" property="createTime" />
        <result column="update_time" property="updateTime" />
        <result column="is_delete" property="isDelete" />
        <result column="gender" property="gender" />
    </resultMap>

</mapper>

因为我用的是mybatis-plus框架,不需要写mapper,框架本身提供的一组通用mapper也够用,

如果用的是mybatis的话就需要写数据访问接口了

@Mapper
public interface UserMapper extends BaseMapper<User> {

    //根据账号密码查询用户
    User selectUserByNameAndPassword(User user);
}
<?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.mijiu.mapper.UserMapper">

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.mijiu.entity.User">
        <id column="id" property="id" />
        <result column="user_name" property="userName" />
        <result column="password" property="password" />
        <result column="user_account" property="userAccount" />
        <result column="user_role" property="userRole" />
        <result column="avatar" property="avatar" />
        <result column="create_time" property="createTime" />
        <result column="update_time" property="updateTime" />
        <result column="is_delete" property="isDelete" />
        <result column="gender" property="gender" />
    </resultMap>

<!--    根据账号密码查询用户-->
    <select id="selectUserByNameAndPassword" resultMap="BaseResultMap">
        SELECT * FROM user WHERE user_account = #{userAccount} AND password = #{password}
    </select>
</mapper>

数据访问层依赖实体类层,去做属性映射接收sql执行返回数据集。下面继续介绍最后一层entity目录

entity目录

这个目录存放的Entity类通常与数据库表中的记录(Row)对应,它们之间存在一一对应的关系。

@Data
@TableName("user")
@ApiModel(value = "User对象", description = "用户表")
public class User implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    @ApiModelProperty("主键")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    @ApiModelProperty("用户昵称")
    @TableField("user_name")
    private String userName;

    @ApiModelProperty("密码")
    @TableField("password")
    private String password;

    @ApiModelProperty("账号")
    @TableField("user_account")
    private String userAccount;

    @ApiModelProperty("用户角色:user / admin")
    @TableField("user_role")
    private String userRole;

    @ApiModelProperty("头像")
    @TableField("avatar")
    private String avatar;

    @ApiModelProperty("创建时间")
    @TableField("create_time")
    private LocalDateTime createTime;

    @ApiModelProperty("更新时间")
    @TableField("update_time")
    private LocalDateTime updateTime;

    @ApiModelProperty("逻辑删除:1删除/0存在")
    @TableField("is_delete")
    private Boolean isDelete;

    @ApiModelProperty("性别")
    @TableField("gender")
    private Boolean gender;

    @ApiModelProperty("状态:1正常0禁用")
    @TableField("status")
    private Boolean status;


    @ApiModelProperty("手机号")
    @TableField("phone")
    private String phone;
}

test目录

主要用来放mapper层、service层的测试用例类

例如UserMapperTest:

@SpringBootTest
public class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    @MockBean
    private BaseMapper<User> baseMapper;

    @Test
    public void testSelectUserByNameAndPassword() {
        // 创建一个模拟的User对象,用于作为参数传入方法中
        User user = new User();
        user.setUserName("test");
        user.setPassword("password");

        // 创建一个模拟的查询结果
        User expectedResult = new User();
        expectedResult.setId(1L);
        expectedResult.setUserName("test");
        expectedResult.setPassword("password");

        // 模拟BaseMapper的行为,当调用其selectOne方法时,返回模拟的结果
        when(baseMapper.selectOne(new QueryWrapper<User>().eq("username", "test").eq("password", "password")))
                .thenReturn(expectedResult);

        // 调用被测试的方法
        User result = userMapper.selectUserByNameAndPassword(user);

        // 断言结果是否符合预期
        assertEquals(expectedResult, result);
    }
}

写在最后

spring boot3单模块项目工程搭建-上(个人开发模板)。任何问题评论区或私信讨论,欢迎指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1613348.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

探索NDWI:归一化水体指数的意义与应用

随着遥感技术的不断发展&#xff0c;NDWI&#xff08;Normalized Difference Water Index&#xff0c;归一化水体指数&#xff09;作为一种重要的植被指数&#xff0c;被广泛应用于水资源管理、湿地监测和环境保护等领域。本文将介绍NDWI的意义、计算方法以及在不同领域的应用。…

什么是0-day漏洞,怎么防护0-day漏洞攻击

随着信息技术的快速发展&#xff0c;网络安全问题日益凸显&#xff0c;其中0day漏洞攻击作为一种高级威胁手段&#xff0c;给企业和个人用户带来了极大的风险。下面德迅云安全就对0day漏洞攻击进行简单讲解下&#xff0c;并分享相应的一些安全措施&#xff0c;以期提高网络安全…

设计模式-创建型-抽象工厂模式-Abstract Factory

UML类图 工厂接口类 public interface ProductFactory {Phone phoneProduct();//生产手机Router routerProduct();//生产路由器 } 小米工厂实现类 public class XiaomiFactoryImpl implements ProductFactory {Overridepublic Phone phoneProduct() {return new XiaomiPhone…

【Interconnection Networks 互连网络】Flattened Butterfly 扁平蝶形拓扑

Flattened Butterfly 扁平蝶形拓扑 1. 传统蝶形网络 Butterfly Topology2. 扁平蝶形拓扑 Flattened Butterfly3.On-Chip Flattened Butterfly 扁平蝶形拓扑应用于片上网络 Flattened Butterfly 扁平蝶形拓扑 扁平蝶形拓扑是一种经济高效的拓扑&#xff0c;适用于高基数路由器…

向量数据库的崛起:如何改变数据存储与机器学习的未来

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

基于 Spring Boot 博客系统开发(一)

基于 Spring Boot 博客系统开发&#xff08;一&#xff09; 本系统是简易的个人博客系统开发&#xff0c;为了更加熟练地掌握SprIng Boot 框架及相关技术的使用。&#x1f913;&#x1f913;&#x1f913; 本系统开发所需的环境及相关软件 操作系统&#xff1a;Windows Java…

20240330-1-词嵌入模型w2v+tf-idf

Word2Vector 1.什么是词嵌入模型&#xff1f; 把词映射为实数域向量的技术也叫词嵌⼊ 2.介绍一下Word2Vec 谷歌2013年提出的Word2Vec是目前最常用的词嵌入模型之一。Word2Vec实际是一种浅层的神经网络模型&#xff0c;它有两种网络结构&#xff0c;分别是连续词袋&#xff…

iOS 全平台矢量动画库:体积小巧、功能丰富 | 开源日报 No.227

airbnb/lottie-ios Stars: 24k License: NOASSERTION lottie-ios 是一个用于在 iOS 平台上本地渲染 After Effects 矢量动画的库。 该项目主要功能、关键特性、核心优势包括&#xff1a; 跨平台支持&#xff1a;可在 iOS, macOS, tvOS, visionOS, Android 和 Web 上使用实时渲…

NewStarCTF 2023 web

目录 week1 泄漏的秘密 Begin of Upload Begin of HTTP ErrorFlask Begin of PHP R!C!E! EasyLogin week2 游戏高手 include 0。0 ez_sql Unserialize&#xff1f; Upload again! R!!C!!E!! week3 Include &#x1f350; medium_sql POP Gadget GenShin wee…

jvm调优的命令和工具总结与分析

目录 1 前置启动程序 2 Jmap查看内存信息 2.1 jmap ‐histo查看历史实例信息 2.2 jmap ‐heap查看堆信息&#xff1a; 2.3 jmap ‐dump导出堆内存信息 2 Jstack查找死锁 2.1 用jstack加进程id查找死锁 2.2 还可以用jvisualvm自动检测死锁 3 Jinfo查看参数配置值 3.1 …

Hadoop1X,Hadoop2X和hadoop3X有很大的区别么?

Hadoop的演进从Hadoop 1到Hadoop 3主要是为了提供更高的效率、更好的资源管理、更高的可靠性以及对更多数据处理方式的支持。下面是Hadoop 1, Hadoop 2, 和 Hadoop 3之间的主要区别和演进的原因&#xff1a; Hadoop 1 特点&#xff1a; 主要包括两大核心组件&#xff1a;HDFS&a…

kettle从入门到精通 第五十三课 ETL之kettle MQTT/RabbitMQ consumer实战

1、上一节课我们学习了MQTT producer 生产者步骤&#xff0c;MQTT consumer消费者步骤。该步骤可以从支持MRQTT协议的中间件获取数据&#xff0c;该步骤和kafka consumer 一样可以处理实时数据交互&#xff0c;如下图所示&#xff1a; 2、双击步骤打开MQTT consumer 配置窗口&a…

sso-oauth2单点登录功能笔记

场景&#xff1a;最近公司2个系统需要做单点登录&#xff0c;A系统作为服务器&#xff0c;认证方式是sso-oauth2方式&#xff0c;B系统作为客户端&#xff0c;token方式是ta-token&#xff0c;先来张sso-oauth2认证方式的图 前置准备工作 第一步&#xff1a;要确认谁是服务提…

Python 全栈安全(一)

原文&#xff1a;annas-archive.org/md5/712ab41a4ed6036d0e8214d788514d6b 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 前言 序言 多年前&#xff0c;我在亚马逊搜索了一本基于 Python 的应用程序安全书。我以为会有多本书可供选择。已经有了很多其他主题的 Pyt…

【Linux】MySQL的安装及配置(Ubuntu-18.04)

一、安装MySQL 分别安装MySQL服务器、MySQL客户端、C/C开发库 sudo apt-get install mysql-server sudo apt-get install mysql-client sudo apt-get install libmysqlclient-dev 二、配置MySQL 1.查看默认配置文件&#xff0c;此处的user和password为默认提供的&#xff0c;…

vulfocus靶场thinkphp命令执行cve-2018-1002015

thinkPHP 5.0.x版本和5.1.x版本中存在远程代码执行漏洞&#xff0c;该漏洞源于ThinkPHP在获取控制器名时未对用户提交的参数进行严格的过滤。远程攻击者可通过输入‘&#xff3c;’字符的方式调用任意方法利用该漏洞执行代码 开启靶场&#xff1a; 使用工具&#xff1a; think…

SpringBoot-无法从static上下文引用同非static方法

1.问题 说明&#xff1a;无法从static上下文引用同非static方法。 2.解决 说明&#xff1a;return后面的语句中&#xff0c;调用的是变量的方法&#xff0c;而不是类型的方法&#xff01;

ChatGPT研究论文提示词集合3-【数据收集】、【数据分析】和【解释与讨论】

点击下方▼▼▼▼链接直达AIPaperPass &#xff01; AIPaperPass - AI论文写作指导平台 目录 1.数据收集 2.数据分析 3.讨论与解释 4.书籍介绍 AIPaperPass智能论文写作平台 近期小编按照学术论文的流程&#xff0c;精心准备一套学术研究各个流程的提示词集合。总共14个步…

从零到一大屏开发过程记录

写在前面&#xff0c;博主是个在北京打拼的码农&#xff0c;凭借多年前端工作经验做过各类项目&#xff0c;最近心血来潮在这儿写点东西&#xff0c;欢迎大家多多指教。 对于文章中出现的任何错误请大家批评指出&#xff0c;一定及时修改。有任何想要讨论和学习的问题可联系我&…

Windows使用freeSSHd搭建sftp服务器

一、安装 1、运行freeSSHd.exe&#xff08;最好以管理员方式运行&#xff09; 2、选择安装位置 3、选择全部安装 4、是否创建开始启动栏快捷入口 5、是否创建桌面快捷方式 6、安装 7、安装完成&#xff0c;点击close 8、安装私钥 9、是否要安装为服务 10、全部安装完成 二、配…