基于Spring Boot的竞赛设备共享平台后端开发探析

2023-08-09 07:17都霓凯解佳慧蒋杰
中国设备工程 2023年14期
关键词:调用管理员订单

都霓凯,解佳慧,蒋杰

(1.四川大学,四川 成都 610000;2.四川核工业技师学院,四川 广元 628000)

每年都会有大量职业技术学校的老师和学生参加该比赛,展现其突出的实践能力。参加该比赛,前期需投入一系列竞赛相关器材。然而,许多学校由于经费限制,无法购置足够的竞赛器材,导致一部分学生参赛受限;参赛后,购置的竞赛器材难有用武之地而被迫闲置,造成极大的浪费。因此,有必要针对参赛机构建立一个竞赛设备共享平台,促成各个机构间闲置竞赛设备的流通,以实现资源的充分利用。本项目组给出一种基于Spring Boot与Vue的竞赛设备供需平台的设计与实现。项目完整源代码在Gitee已上传,并且成功在服务器上运行供内部成员使用。

1 项目概述

1.1 项目环境

为了方便代码实现和后期运营维护,本项目采取了前后端分离的工作模式,前端通过ajax调用后端api来完成整个系统的运作。前后端均使用IntelliJ IDEA开发。测试阶段,进行本地的前后端代码结合测试。开发完成后部署在Linux服务器上进行测试和发布。

表1 项目环境表

1.2 关键技术说明

(1)Tomcat。是一个轻量级的Web应用服务器,免费使用且开放了源代码。利用它可以响应HTML页面的响应请求。而Spring框架中内置了1个tomcat服务器,极大方便了使用者的开发。当在1台机器上配置好Apache 服务器,可利用它响应HTML(标准通用标记语言下的一个应用)页面的访问请求。实际上Tomcat是Apache 服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat时,它实际上作为一个与Apache 独立的进程单独运行的。

(2)Spring Data JPA。JPA即Java Persistence API,由Sun公司官方提出的Java持久化规范。Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套 JPA 应用框架,底层使用了 Hibernate的 JPA 技术实现,可使开发者用极简的代码即可实现对数据的访问和操作,它提供了包括增删改查等在内的常用功能,且易于扩展。以此项目后端架构为例,DAO层会对JpaRepository继承,具体规则可以查询Spring文档。

图1 Spring Data Jpa示例

(4)lombok。是一个快捷的Java插件。主要使用其中的@Data注解,在类外加上这个注解之后,可以自动生成get()/set()类内部成员变量的方法,即不需要显示地定义就可以调用,可节省上百行甚至上千行代码。

(5)hash算法。hash 算法(散列算法、摘要算法)即把任意长度的输入映射为固定长度的输出,比如,密码 Evanniubi 变成五位的输出 kchpl,这种算法不可逆,且存在信息损失,对于复杂密码来说,破解成本极高。加严是提高 hash 算法的安全性的一个常用手段。

(6)开箱即用。是Spring Boot框架中非常重要的一个概念,通过在开发过程中,使用约定省去了以往在开发中繁琐的配置工作,用注解完全省去了XML文件的配置工作。传统Spring IO平台饱受非议的一点就是大量的XML配置以及复杂的依赖管理,开发人员需要不断编写XML,而且在一些场景中甚至不需要编写繁琐的import语句。开箱即用策略使得Spring Boot甚至可以在140个字符内实现可运行的web应用,且没有代码的生成,极大得提升了产品的关注度。

2 竞赛设备共享平台设计

2.1 功能模块设计

由于账号性质不同,所以系统功能基本分为2个子类:用户类和管理员类。用户类下又需要设计增加、修改、查询、删除等功能,以及对自己账号的管理。但是竞赛项目的增删、修改,竞赛所需设备的增删、修改都需要管理员权限,且用户不能获取他人账号所有的信息。查询也分很多种,需要根据具体需求来设计具体功能。管理员类除了拥有普通用户的全部功能外,还可以对所有竞赛项目、竞赛所需设备、共享设备、订单以及所有账号进行管理。

2.2 平台后端设计

后端小组在JetBrain官方网站上申请了学生认证,使用其公司提供的正版IntelliJ IDEA最终版进行开发。借助目前比较热门的Spring框架搭建项目。框架基本组成已经提供,所以只需要丰富src/main路径下的内容即可。

后端根据层次划分共有11层:(1)config包下进行web和shiro的配置,需要定义允许特定请求跨域的方法以及编写基于URL的过滤器;(2)entity包下将数据库各实体映射成类,通过@Entity注解表明这个类代表了数据库中的一个实体表,@Table(name = “”)注解中,name值需要和数据库中表名相同;(3)DAO包对JpaRepository类进行继承,实现数据持久化的操作,直接操作数据库,用于与数据库的直接交互,定义增删改查等操作。其中大部分基本函数已经写好,并且由于Jpa遵循着一套规范,所以想要扩展出我们需要的函数相对容易,只需按照规定的语法编写函数名称和返回值即可。具体规范可以参照Spring官方文档;(4)service包是对DAO包内函数的进一步封装,实现增删查改等基本功能,并按照需求实现特殊的功能,负责业务逻辑,跟功能相关的代码一般写在这里,编写、调用各种方法对 DAO 取得的数据进行操作;(5)controller包是最后一层封装,需要将函数与接口关联。所谓接口即前端调用的url,负责数据交互,即接收前端发送的数据,通过调用 Service 获得处理后的数据并返回。在实践中我们倾向于让 Controller 显得清凉一些,以方便代码的阅读者寻找分析功能的入口;(6)result包定义了controller包中,函数成功或者失败的返回数据类型,一般需要code,message,data 3个部分组成;(7)utils包实现需要使用的工具类。例如对字符串的处理方法;(8)error包配置错误处理;(9)exception包配置异常处理;(10)filter包实现过滤器方法,即拦截用户访问没有相应权限的网页,同时不允许用户未登录就进入平台各页面;(11)realm包为shiro进行用户认证的核心,这里定义获取认证与授权信息的方法。

后端中最基本的最重要的层次为DAO-servicecontroller,也是项目开发的主要工作内容。

2.3 数据库设计

本项目使用MySQL 8.0数据库,以学生资格认证申请了正版Navicat 16来可视化管理数据库。数据库中共有7个实体表:dept(所属单位)、user(用户)、order(订单)、share_eq(共享设备详情)、equipments(设备种类)、equip_comp(设备、竞赛关联表)、competition(竞赛项目)。其中除了主码约束之外,还有部分属性使用check约束取值。每个表之间的主码还有多对一外码约束。代码中可以通过 @ManyToOne与@JoinColumn()注解实现。由于competition与equipments是多对多关系,所以直接在数据库中建立实体表equip_comp来维护关联。

wmax denotes the size of the region where the boundary between the doped and undoped zones is moved back and forth under the effect of a periodic bias signal. The two basic expressions for this model are:

竞赛项目需要分成各等级,competition表的tag属性存储了这个信息。需求中还需要各竞赛设备由官方给出指导价格,由equipments的guiding体现。2个表中的desc_属性需要加下划线,也是因为desc是保留关键字。share_eq表中surplus表示剩余量,设备提供方申请入网设备时可能设备数量有多个。需求方预约设备时也不能无限制预约,每个订单都会让剩余量减1,余量为0则不能进行预约。删除订单后余量重新加1。禁用状态status则用来判断是否可以进行预约。被禁用的理由有多种,余量不足时也会将禁用状态设为false,即禁用。order表中的evaluate属性用于记录评分。分值只能由设备借用者在完成订单之后给出。

各表主码均为id。其中user表与dept表没有外码约束,user表的school属性应该从dept表的name中取值。但由于SQL的特性,外码只能参照表的主码作为约束,所以school与dept之间没有参照关系,而是由前端的选择框实现。share_eq表的eid属性参照equipments表中的id,uid属性参照user表中的id,均为多对一关系。order表,实际上数据库中表名为orde,原因是order是java保留关键字,使用会报错,之后论文中均直接称其为order表。order表的sid参照share_eq表的id,uid参照user表的id。

共有4个位置使用了check约束。user表中对role属性进行约束check (`role`=`user``adm``provider`),只可以取三者中的一个。这样的语法在不同版本的MySQL中可能报错,有的需要将字符串使用双引号括起来,并使用OR关键词。在dept表中,check(`local`=`川东``川西``川南``川北``川中`),后期可以丰富部门的地址,具体到成都市的某区某街道。目前只能取4种值是为了演示根据地址查询共享设备的功能。competition表中,check(`tag`=`土木建筑类``装备制造类``交通运输类``电子与信息类``财经商贸类``旅游服务类`),即对竞赛进行分类,这样可以根据类别查询竞赛项目。最后是order表中check (status = 1 OR status = 2 OR status = 3),check (evaluate <= 5 AND evaluate>= 0),对status和evaluate进行约束,status为1表示交易开始,2表示正在交易,3代表交易完成。评分则是限制在0.0~5.0范围内,一位小数,在数据库中用numeric(2,1)表示。

3 竞赛设备共享平台实现

3.1 登录模块

管理员注册用户账号时,输入密码(明文),向后台发送请求,后台将密码加上随机生成的盐并 hash,再将 hash 后的值作为密码存入数据库,盐也作为单独的字段存起来,生成随机的用户名告知用户。之后用户登录时,输入用户名密码(明文),向后台发送请求[4],后台根据用户名查询出盐,和密码组合并 hash,将得到的值与数据库中存储的密码比对,若一致则通过验证。

3.2 用户模块

/user/myself接口用于获取个人信息(同时用于验证登录),UserUtils.getUser()获取当前用户的用户名之后通过userService.findByUsername()获取用户对象即可。

3.3 竞赛模块

所有用户均可以获取全部竞赛项目信息,/api/user/allProject调用competitionService.list()即可。

管理员可以添加竞赛项目。添加之前先判断该名称的竞赛是否存在,通过调用competitionService.haveName()判断。competitionService.haveName()则是调用了competitonDAO。existByName()。前端传的数据除了竞赛的信息,还需要提供所需设备的id列表,整个作为Param1对象,方便处理。Param1在utils包下进行了详细定义。competitionService.add()添加竞赛后,使用增强for循环一一在equip_comp表中添加竞赛项目id(cid)和竞赛设备id(eid)的关联。

管理员修改竞赛项目,同时修改与设备的关联,先把现有关联删除,之后重新添加。同样使用Param1数据类型,使用增强for循环。

管理员可以删除竞赛项目,通过/api/admin/deleteProject接口。首先需要判断请求体competition对象是否在数据库中存在,判断equipCompService.haveCid (competition.getId()) 的返回值是否为真即可。删除操作使用的是 competitionService.deleteById(competition.getId())。所以删除功能中,前端只需要提供正确的id即可,其余信息不需要提供。这个设计十分重要,在之后的各功能模块中也都有所体现。

3.4 设备模块

所有用户都可以查看所有竞赛所需要的设备,通过/api/user/allDevice接口,调用equipmentsService.list()。

管理员可以添加设备,判断请求体是否为空以后,调用equipmentsService.add(equipments.getName(),equipments.getDesc_(), equipments.getGuiding())。

管理员修改设备的接口,调用的 equipmentsService.edit(equipments)的参数表就简单很多了,直接将前端传来的equipments对象传入即可。在service层中,Competition c=get(competition.getId());得到competition对象之后,对其每个属性进行覆盖修改,最后competitionDAO.save(c);完成修改设备。

管理员删除设备时,如果该设备有相关联的竞赛,则无法进行删除,调用equipCompService.haveEid()实现判断equip_comp表中是否有该设备id的关联项。

除了通过id查找设备,名称模糊查找设备外,还可以通过竞赛查找设备。通过名称模糊查找设备的方法基本和通过名称模糊查找竞赛的方法一致。想要通过竞赛查找设备,必然需要通过存储关联项的表equip_comp。该功能在controller/equipCompController中实现。用户还可以查询自己共享了的设备的信息。根据id查询到该用户共享的设备列表,由于shareEq对象内部均含有equipments对象,所以将共享设备列表中共有的设备对象全部提取出来组成新列表。由于其中可能出现重复的设备,所以需要做去重处理。去重最简单的方法就是新建一个hashset,HashSet hashset = new HashSet(list);将列表中的元素全部导入,hashset则自动做去重,再将元素全部取出组成列表并返回。

3.5 共享模块

管理员可以删除任意共享设备,但是用户只能删除自己的共享设备。查询本用户的共享设备时,只需提取前端传来用户对象中的uid,再调用shareEqService.listByUser()即可。

上传图片的功能,分为首次上传图片和修改共享设备图片。首次上传图片调用api/user/sharecovers接口,以MultipartFile类型传输数据,请求参数的value为file。存储路径为/share/workspace/img,是Linux系统路径格式,因为项目部署在Linux服务器上。修改共享设备图片除了接受MultipartFile数据外,还要提供路径变量,调用api/user/sharecovers/{id}接口。需要先从数据库中获得该共享设备的图片url,判断本地磁盘上是否存在该文件,如果存在则删除旧图片,将新图片url存入数据库之后返回url。

3.6 订单模块

添加订单时,需判断共享设备是否有余量,如果有则余量减一再添加订单。默认状态为1,评分为0.0。相应的,删除订单时余量重新加一。

通过调用/api/user/borrow接口,用户可以查找自己的借用订单。调用/api/user/lend,用户查找自己的借出订单。以上2个功能均需要先验证当前登录用户是否和请求体用户对象相同。

3.7 访问拦截模块

完成了用户登录认证之后,还需要考虑完善的访问拦截。如果用户登录了一次,关闭浏览器后,sessionId就会消失,再次发送请求,shiro就会认为用户已经变更。但有时我们需要保持登录状态,不然每次都要重新登录,所以shiro提供了rememberMe机制。rememberMe机制不是单纯地设置cookie存活时间,而是又单独保存了一种新的状态。之所以这样设计,也是出于安全性考虑,把“记住我”的状态与实际登录状态做出区分,这样,就可以控制用户在访问不太敏感的页面时无需重新登录,而访问类似于购物车、订单之类的页面时必须重新登录。除了rememberMe机制外,还需要一个基于URL的路径过滤器,URLPathMatchingFilter。filterChainDefinitionMap.put(“/api/admin/**”,“url”);表示所有以/api/admin/为前缀的接口都需要有管理员权限才能访问。filterChainDefinitionMap.put(“/api/user/password”,“authc”);表示这个接口需要登录才能访问。

4 测试和部署

本项目采取了多种测试方法结合。首先后端开发过程中,通过学生资格认证申请正版Postman软件进行测试后端接口,测试结果全部通过。在Postman上需要提供测试的接口url,以及正确输入请求体格式、类型、数据内容。部分接口只接受单个参数,设置param即可。还有部分接口需要传输Map之类的特殊类型,则需要选择content-type为application/json,然后写明原始json格式的数据进行测试。之后在IDEA同时启动前端和后端做结合测试,本地计算机作为服务器,浏览器访问http://localhost:8080/index,测试前端按钮和后端接口的交互。最后项目在服务器端部署之后,也持续不断进行测试,发现问题后及时修改优化,主要是Linux系统文件路径问题。后端jar包版本已经迭代13次。目前已经能稳定运行,使用浏览器访问ip地址即可使用。

服务器使用的是华为云Linux服务器,通过学生认证申请的正版Xshell软件远程连接服务器,使用命令行操作文件系统。首先sudo apt-get install lrzsz安装rz,sz,之后选择文件传输中的ZModem传输本地文件至远程服务器。在此之前,后端内容需要通过mevan打包成jar文件包,上传至服务器之后,以nohup java -jar项目名.jar &命令运行jar包,即可在关闭服务器连接之后进程仍然运行。

最终用浏览器访问服务器的ip地址即可看到界面,开始使用本竞赛设备共享平台。

猜你喜欢
调用管理员订单
春节期间“订单蔬菜”走俏
我是小小午餐管理员
新产品订单纷至沓来
我是图书管理员
我是图书管理员
可疑的管理员
核电项目物项调用管理的应用研究
LabWindows/CVI下基于ActiveX技术的Excel调用
“最确切”的幸福观感——我们的致富订单
基于系统调用的恶意软件检测技术研究