项目实战记录 -【用户中心项目】用户中心后端开发

后端技术栈:

Spring + SpringMVC + SpringBoot + MyBatis + MyBatis Plus + MySQL 

整理来说比较简单,也没有用到缓存,后面有需要可以随时添加

根据前端页面可以看出,需要登录,注册,以及一个查询页中的查改接口,除了业务相关还需要定义全局统一异常处理,统一返回类以及自定义异常来统一整体后端项目的规范,入参与回参的结构可以根据前后端讨论后的结构进行编写。

模型创建

模型这里是用了一个国产的建模工具PDManer创建的,官网链接【https://www.pdmaas.cn/home】,打开软件后可以选择新创建一个项目,如果已经有项目也可以选择打开

创建好后可以在模型中的数据表区域选择创建新的模型

也可以通过数据库进行选择,点击上方导航栏中的数据库

在弹出的数据库连接配置中添加数据库连接

添加数据源连接后在导航栏中选择从数据库导入,然后选择我们需要导入的表点击导入就完成了

对于模型由于是单表操作,所以就不细讲字段了,除了人员个人信息字段外还添加了用户状态(根据用户状态判断用户是否被封禁从而限制登录或部分功能),用户角色(判断用户是否为管理员,从而区分用户能看到的目录)以及通用字段

is_delete   是否删除,默认值为 0
create_time 创建时间
create_by   创建人
update_time 修改时间
update_by   修改人

模型创建好后选择数据表最右边的程序代码

这里会提供生成多种语言的代码,我们选择我们需要的MybatisPlus

这里看到代码生成器给出了几乎所有的代码,并且完成了所有的增删改查接口的实现,在路径及变量中可以对命名空间进行调整以及生成的文件保存位置,调整好后可以点击生成生成所有文件也可以需要哪部分直接复制哪部分的代码,我认为这是一个非常好用的工具,这个工具也是之前上班时同事分享的,非常感谢之前的同事,让通用代码的编写变的更高效

环境与项目搭建

由于在前端篇已经讲过了环境变量的配置,所以这里不再重复讲解环境变量的配置,这里同时给出菜鸟教程的配置【Java 开发环境配置 | 菜鸟教程 (runoob.com)】,maven环境这里使用的是3.6.3,也可以使用3.5.3的版本,如果在引入依赖比较慢的话可以修改maven-conf-settings.xml文件,将文件中的源改为国内镜像,这里可以参考csdn上的配置教程【Maven配置教程_maven环境变量配置教程-CSDN博客】,我这里使用的是我服务器上的数据库,如果你没有服务器可以在本地安装一个本地的mysql数据库,如果你是在本地安装可以参考csdn教程【MySQL 数据库安装(详细教程)【2024 版】_mysql数据库安装-CSDN博客】,请注意,由于每个人的电脑系统版本与系统环境都有差异所以可能会存在一些坑,根据每个人不同的报错进行搜索大概率可以解决,如遇到版本问题需要重新安装mysql数据库的,记得要把之前安装的卸载删除干净(包含但不限于残留文件,注册列表,相关服务等),不然可能会有影响

环境搭建好后就可以进行项目搭建了,项目搭建我使用了idea自带的项目创建工具,在File-New-Project

选择Spring Initializr根据提示编辑项目名,存放位置,项目语言,什么类型的项目,项目模块标识符,包名以及jdk的版本,这里需要主要,新版本的idea已经不再支持选择java8了,所以这里我使用了阿里的服务地址,将上方的Server URL替换为【https://start.aliyun.com】,再次打开java选项就有java8了

填写好后点击下一步,这里可以选择需要引入的依赖,相当于是一个大型的supermarket,要什么就勾选上,根据我们上述的技术栈引入相应的依赖即可,选择好后点击创建即可得到一个已经帮忙添加好依赖的空项目

这里我首先定义了一下配置文件,将通用的配置放到application.yml文件中,这里我配置了启动环境,循环依赖配置,MyBatis-Plus 配置说明mapper.xml文件存放位置,数据库配置,包括字段策略(在插入、更新、查询时如何处理字段的NULL或空值)和ID生成策略(这里使用assign_id,通常是基于雪花算法生成全局唯一ID),MyBatis的基础配置,包括驼峰命名到下划线的自动映射、是否启用缓存(这里禁用)、是否在字段为null时调用setter方法(这里启用),另外我这里引入了jasypt加密器进行数据库密码加密以及全局的接口前缀配置

为了实现多环境配置,复制application.yml分别创建application-dev.yml和application-prod.yml,在这两个配置文件中主要定义了端口以及数据库相关的配置

这里的数据库密码是使用jasypt加密后的,并未设置HikariCP连接池数据库连接的最大生存时间为500000毫秒(约5分钟),当连接在这个时间后仍然没有被使用,那么它将被关闭并从池中移除

在prod环境中额外添加了session超时时间

接下来创建项目结构,在resources文件夹下创建mapper和sql文件夹,用于放置mapper的xml文件和建表sql的执行脚本,这里建议在项目中存放对表结构的执行语句,以便在项目上线时同步生产环境

在自己的项目包下创建如下文件夹:

common:存放常量与通用配置等,这里我存放的是常量,枚举,自定义异常,统一异常处理配置,通用工具类,通用返回实体

config:存放的是webmvc的配置,其实这里没有用到,跨域配置其实是在nginx中配置的

controller:控制器,前端请求的接口

mapper:由于用到了mybatis plus所以这里继承BaseMapper后就无需在写sql,由上可知,我在

resources中也创建的mapper相关的文件夹,这里主要是为了之后的拓展考虑,为了编写复杂sql使用

model:这里存放的模型或者实体只用到了数据库映射,前端展示以及传入表单的实体,对于转换使用的实体并没有创建,这里做了部分实体的简化

mybatisplus:这个文件夹中主要存放了dao曾相关操作的一些全局配置文件,包括删除时执行的切面,mybatisplus的分页插件,自定义sql注入器,以及自动填充器(为通用字段做填充),但是这部分还有待优化

service:具体的接口实现,在控制层定义好的接口在这里进行实现

最后就是项目的启动器

创建好整体的项目结构后,这里就不再展开说明每一个接口具体的实现了,有些项目中涉及到的关键点会重点说一下

首先是Controller层跨域相关的设置,如果说在前端或nginx中没有设置跨域,可以把跨域相关的配置在后端进行设置,这里可以在Controller层添加CrossOrigin注解(跨域资源共享),在注解中添加origins然后写明需要跨域访问的地址,这里是一个数组以逗号分隔添加多个地址。

@CrossOrigin(origins = {"http://192.168.1.1:8081"})

CrossOrigin注解中还有其他的属性

methods:允许跨域请求的方法,如 GET, POST, PUT, DELETE 等。

allowedHeaders:在请求中允许的头信息字段。

exposedHeaders:在响应中暴露给客户端的头信息字段。

allowCredentials:是否允许发送Cookie信息。

maxAge:预检请求的结果的有效时间,以秒为单位。

如果你需要对多个端点应用相同的 CORS 策略,可以考虑在全局级别配置 CORS,比如通过实现 WebMvcConfigurer 接口的 addCorsMappings 方法。这样可以避免在每个控制器或方法上重复添加 @CrossOrigin 注解。

在实现方面,对于mapper层的注入使用的是目前spring推荐的构造方法注入的方式,在登录与注册功能上也做了后端的参数校验,这里是为了防止请求不是从前端发起而直接访问接口的请求。

在注册功能中,密码并不会名文存储,在登录校验中,也会根据加密后的密文进行比较,在登录成功后,会对用户数据脱敏并将脱敏后的用户登录态存放到session中在后续鉴权获取用户信息以及登出功能中使用。

在业务代码中,将使用到的常量存放到公共的常量类中以便统一管理与访问。

在对数据层操作时,创建了用于删除方法的自动注入的切面类,切点是每一个实现层中前缀为delete的方法,声明一个环绕通知,在所有匹配的delete方法执行前后执行。环绕通知方法内部逻辑,通过 ProceedingJoinPoint 对象的getArgs()方法获取到被代理的目标(IService<Object> 类型)和方法的参数,获取到目标对象后对其校验,如果不是单一对象或为空则直接执行原方法。使用 MyBatis-Plus 的 UpdateWrapper 来构建更新条件,由于是逻辑删除,所以也是作为更新来操作,这里主要更新的是update_time,update_by,update_by_name字段,获取参数中的id,调用获取到对象的update() 方法来执行更新操作。最后通过ProceedingJoinPoint的proceed()方法执行代理方法。

通过实现MetaObjectHandler自定义自动填充器,重写MetaObjectHandler中的insertFill和updateFill对插入与更新操作填充通用字段createBy,createByName,createTime以及updateBy,updateByName,updateTime。定义好后其实他是不生效的,需要在对应的数据库操作实体的通用字段TableField注解上添加fill = FieldFill.操作的类型

    //插入操作时
    @TableField(value = "xxxxx" , fill = FieldFill.INSERT)
    //更新操作时
    @TableField(value = "xxxxx" , fill = FieldFill.UPDATE)
    //插入或更新操作时    
    @TableField(value = "xxxxx" , fill = FieldFill.INSERT_UPDATE)
    //根据源码可知还有一种默认,即不特别指定在插入或更新时自动填充。具体含义可能依赖于使用这个枚举的上下文        
    @TableField(value = "xxxxx" , fill = FieldFill.DEFAULT)

当处完业务逻辑获取结果后就可以做统一返回以及统一异常处理了。

这里首先来讲自定义异常,在common文件夹中创建exception文件夹,创建一个项目中使用的公共异常,继承RuntimeException添加一个构造方法,参数这里使用了两个(code和message)并添加get方法或注解。然后创建异常统一处理,创建handler文件夹,然后创建异常处理类并添加@ControllerAdvice(定义全局的控制器级别的切面)注解,在@ExceptionHandler注解中添加我们创建的异常类。最后创建一个方法用于处理我们的异常,这里我们对返回的结构进行统一封装处理,在common中创建一个统一返回的vo实体对象,为对象添加一个泛型,这个实体中的参数有code,message,data,其中添加的泛型就是data的数据类型并通过lombok添加get,set方法

最后在返回数据时我们可以定义一个统一的返回工具类,参数也是code,message,code,状态码可以定义一个枚举,将需要返回的不同状态码放进去。返回类型就是上面定义好的带泛型的返回实体,在这个工具类中,定义了success,error成功或者失败方法,参数可以根据业务实际情况来定,我这里只是用到了,参数是message或message和data的(因为我是在方法内部定给定了状态码,也可以通过外层业务代码中传入)。在返回的时候由于是统一异常就使用的是工具类中的error方法。

剩下的就是单独创建了一个加密类(用于用户注册密码加密)

至此后端所有的业务代码就已经完成了,然后就是后端的dockerfile文件,可以使用线上打包并部署的方式,但是我试了一下,我的小服务器带宽有限,并且服务器在国内,打包巨慢且容易卡住,我也没有再去设置镜像之类的,这里选择直接上传打包好的文件,在服务器上直接部署,第一次构建会比较慢,后面有缓存后就快了,首先是获取maven和jdk,然后创建xxxxx文件夹和log日志存放的文件夹,复制我们上传的文件到xxxxx文件夹并重命名app.jar,设置端口号为8081,最终以cmd命令行方式启动项目,并设置启动环境

FROM maven:3.5-jdk-8-alpine as builder

RUN mkdir /xxxxx
RUN mkdir -p /xxx/log/
COPY ./target/xxxxx-0.0.1-SNAPSHOT.jar /xxxxx/app.jar
EXPOSE 8081

CMD ["java","-jar","/xxxxx/app.jar","--spring.profiles.active=prod"]

至此后端的开发就完成了

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

*

162 次浏览