【免费Web系列】JavaWeb实战项目案例六
这是Web第一天的课程大家可以传送过去学习 http://t.csdnimg.cn/K547r
员工信息-删除&修改
前面我们已经实现了员工信息的条件分页查询以及新增操作。 关于员工管理的功能,还有两个需要实现:
-
删除员工
-
修改员工
除了员工管理的功能之外,我们还要再完成员工信息统计的功能开发。
首先我们先完成 "删除员工" 的功能开发,再完成 "修改员工" 的功能开发。再来完成员工信息统计的接口开发 。
综上所述,我们今天的课程内容包含以下四个部分:
-
删除员工
-
修改员工
-
异常处理
-
员工信息统计
1. 删除员工
3.3.1 需求
当我们勾选列表前面的复选框,然后点击 "批量删除" 按钮,就可以将这一批次的员工信息删除掉了。也可以只勾选一个复选框,仅删除一个员工信息。
问题:我们需要开发两个功能接口吗?一个删除单个员工,一个删除多个员工
答案:不需要。 只需要开发一个功能接口即可(删除多个员工包含只删除一个员工)
3.3.2 接口文档
删除员工
-
基本信息
请求路径:
/emps
请求方式:
DELETE
接口描述:该接口用于批量删除员工的数据信息
-
请求参数
参数格式:查询参数
参数说明:
参数名 类型 示例 是否必须 备注 ids 数组 array 1,2,3 必须 员工的id数组 请求参数样例:
/emps?ids=1,2,3
-
响应数据
参数格式:application/json
参数说明:
参数名 类型 是否必须 备注 code number 必须 响应码,1 代表成功,0 代表失败 msg string 非必须 提示信息 data object 非必须 返回的数据 响应数据样例:
{"code":1,"msg":"success","data":null }
3.3.3 思路分析
3.3.4 功能开发
3.3.4.1 Controller接收参数
在 EmpController
中增加如下方法 delete
,来执行批量删除员工的操作。
方式一:在Controller方法中通过数组来接收
多个参数,默认可以将其封装到一个数组中,需要保证前端传递的参数名 与 方法形参名称保持一致。
/**
* 批量删除员工
*/
@DeleteMapping
public Result delete(Integer[] ids){log.info("批量删除部门: ids={} ", Arrays.asList(ids));return Result.success();
}
方式二:在Controller方法中通过集合来接收
也可以将其封装到一个List<Integer> 集合中,如果要将其封装到一个集合中,需要在集合前面加上 @RequestParam
注解。
/**
* 批量删除员工
*/
@DeleteMapping
public Result delete(@RequestParam List<Integer> ids){log.info("批量删除部门: ids={} ", ids);empService.deleteByIds(ids);return Result.success();
}
两种方式,选择其中一种就可以,我们一般推荐选择集合,因为基于集合操作其中的元素会更加方便。
3.3.4.2 Service
1). 在接口中 EmpService
中定义接口方法 deleteByIds
/** * 批量删除员工 */ void deleteByIds(List<Integer> ids);
2). 在实现类 EmpServiceImpl
中实现接口方法 deleteByIds
在删除员工信息时,既需要删除 emp 表中的员工基本信息,还需要删除 emp_expr 表中员工的工作经历信息
@Transactional
@Override
public void deleteByIds(List<Integer> ids) {//1. 根据ID批量删除员工基本信息empMapper.deleteByIds(ids);
//2. 根据员工的ID批量删除员工的工作经历信息empExprMapper.deleteByEmpIds(ids);
}
由于删除员工信息,既要删除员工基本信息,又要删除工作经历信息,操作多次数据库的删除,所以需要进行事务控制。
3.3.4.3 Mapper
1). 在 EmpMapper
接口中增加 deleteByIds
方法实现批量删除员工基本信息
/** * 批量删除员工信息 */ void deleteByIds(List<Integer> ids);
2). 在 EmpMapper.xml
配置文件中, 配置对应的SQL语句
<!--批量删除员工信息-->
<delete id="deleteByIds">delete from emp where id in<foreach collection="ids" item="id" open="(" close=")" separator=",">#{id}</foreach>
</delete>
3). 在 EmpExprMapper
接口中增加 deleteByEmpIds
方法实现根据员工ID批量删除员工的工作经历信息
/**
* 根据员工的ID批量删除工作经历信息
*/
void deleteByEmpIds(List<Integer> empIds);
4). 在 EmpExprMapper.xml
配置文件中, 配置对应的SQL语句
<!--根据员工的ID批量删除工作经历信息-->
<delete id="deleteByEmpIds">delete from emp_expr where emp_id in<foreach collection="empIds" item="empId" open="(" close=")" separator=",">#{empId}</foreach>
</delete>
3.3.5 功能测试
功能开发完成后,重启项目工程,打开 Apifox,发起DELETE请求:
控制台SQL语句:
3.3.6 前后端联调
打开浏览器,测试后端功能接口:
2. 修改员工
需求:修改员工信息
在进行修改员工信息的时候,我们首先先要根据员工的ID查询员工的详细信息用于页面回显展示,然后用户修改员工数据之后,点击保存按钮,就可以将修改的数据提交到服务端,保存到数据库。 具体操作为:
-
根据ID查询员工信息
-
保存修改的员工信息
2.1 查询回显
2.1.1 接口文档
根据ID查询员工数据
-
基本信息
请求路径:/emps/{id} 请求方式:GET 接口描述:该接口用于根据主键ID查询员工的信息
-
请求参数
参数格式:路径参数
参数说明:
参数名 类型 是否必须 备注 id number 必须 员工ID 请求参数样例:
/emps/1
-
响应数据
参数格式:application/json
参数说明:
名称 类型 是否必须 备注 code number 必须 响应码, 1 成功 , 0 失败 msg string 非必须 提示信息 data object 必须 返回的数据 |- id number 非必须 id |- username string 非必须 用户名 |- name string 非必须 姓名 |- password string 非必须 密码 |- entryDate string 非必须 入职日期 |- gender number 非必须 性别 , 1 男 ; 2 女 |- image string 非必须 图像 |- job number 非必须 职位, 说明: 1 班主任,2 讲师, 3 学工主管, 4 教研主管, 5 咨询师 |- salary number 非必须 薪资 |- deptId number 非必须 部门id |- createTime string 非必须 创建时间 |- updateTime string 非必须 更新时间 |- exprList object[] 非必须 工作经历列表 |- id number 非必须 ID |- company string 非必须 所在公司 |- job string 非必须 职位 |- begin string 非必须 开始时间 |- end string 非必须 结束时间 |- empId number 非必须 员工ID 响应数据样例:
{"code": 1,"msg": "success","data": {"id": 2,"username": "zhangwuji","password": "123456","name": "张无忌","gender": 1,"image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2022-09-02-00-27-53B.jpg","job": 2,"salary": 8000,"entryDate": "2015-01-01","deptId": 2,"createTime": "2022-09-01T23:06:30","updateTime": "2022-09-02T00:29:04","exprList": [{"id": 1,"begin": "2012-07-01","end": "2019-03-03""company": "百度科技股份有限公司""job": "java开发","empId": 2},{"id": 2,"begin": "2019-3-15","end": "2023-03-01""company": "阿里巴巴科技股份有限公司""job": "架构师","empId": 2}]} }
2.1.2 实现思路
在查询回显时,既需要查询出员工的基本信息,又需要查询出该员工的工作经历信息。
我们可以先通过一条SQL语句,查询出指定员工的基本信息,及其员工的工作经历信息。SQL如下:
select e.*,ee.id ee_id,ee.begin ee_begin,ee.end ee_end,ee.company ee_company,ee.job ee_job
from emp e left join emp_expr ee on e.id = ee.emp_id where e.id = 39;
具体的实现思路如下:
2.1.3 代码实现
1). EmpController
添加 getInfo
用来根据ID查询员工数据,用于页面回显
/*** 查询回显*/
@GetMapping("/{id}")
public Result getInfo(@PathVariable Integer id){log.info("根据id查询员工的详细信息");Emp emp = empService.getInfo(id);return Result.success(emp);
}
2). EmpService
接口中增加 getInfo
方法
/*** 根据ID查询员工的详细信息*/
Emp getInfo(Integer id);
3). EmpServiceImpl
实现类中实现 getInfo
方法
@Override
public Emp getInfo(Integer id) {return empMapper.getById(id);
}
4). EmpMapper
接口中增加 getById
方法
/*** 根据ID查询员工详细信息*/
Emp getById(Integer id);
5). EmpMapper.xml
配置文件中定义对应的SQL
<!--自定义结果集ResultMap-->
<resultMap id="empResultMap" type="com.itheima.pojo.Emp"><id column="id" property="id" /><result column="username" property="username" /><result column="password" property="password" /><result column="name" property="name" /><result column="gender" property="gender" /><result column="phone" property="phone" /><result column="job" property="job" /><result column="salary" property="salary" /><result column="image" property="image" /><result column="entry_date" property="entryDate" /><result column="dept_id" property="deptId" /><result column="create_time" property="createTime" /><result column="update_time" property="updateTime" />
<!--封装exprList--><collection property="exprList" ofType="com.itheima.pojo.EmpExpr"><id column="ee_id" property="id"/><result column="ee_company" property="company"/><result column="ee_job" property="job"/><result column="ee_begin" property="begin"/><result column="ee_end" property="end"/><result column="ee_empid" property="empId"/></collection>
</resultMap>
<!--根据ID查询员工的详细信息-->
<select id="getById" resultMap="empResultMap">select e.*,ee.id ee_id,ee.emp_id ee_empid,ee.begin ee_begin,ee.end ee_end,ee.company ee_company,ee.job ee_jobfrom emp e left join emp_expr ee on e.id = ee.emp_idwhere e.id = #{id}
</select>
在这种一对多的查询中,我们要想成功的封装的结果,需要手动的基于 <resultMap>
来进行封装结果。
Mybatis中封装查询结果,什么时候用 resultType,什么时候用resultMap ?
如果查询返回的字段名与实体的属性名可以直接对应上,用resultType 。
如果查询返回的字段名与实体的属性名对应不上,或实体属性比较复杂,可以通过resultMap手动封装 。
2.1.4 Apifox测试
重新启动服务,基于Apifox进行接口测试。
2.1.5 前后端联调测试
打开浏览器,进行前后端联调测试。
2.2 修改员工
查询回显之后,就可以在页面上修改员工的信息了。
当用户修改完数据之后,点击保存按钮,就需要将数据提交到服务端,然后服务端需要将修改后的数据更新到数据库中 。
而此次更新的时候,既需要更新员工的基本信息; 又需要更新员工的工作经历信息 。
2.2.1 接口文档
-
基本信息
请求路径:/emps 请求方式:PUT 接口描述:该接口用于修改员工的数据信息
-
请求参数
参数格式:application/json
参数说明:
名称 类型 是否必须 备注 id number 必须 id username string 必须 用户名 name string 必须 姓名 gender number 必须 性别, 说明: 1 男, 2 女 image string 非必须 图像 deptId number 非必须 部门id entryDate string 非必须 入职日期 job number 非必须 职位, 说明: 1 班主任,2 讲师, 3 学工主管, 4 教研主管, 5 咨询师 salary number 非必须 薪资 exprList object[] 非必须 工作经历列表 |- id number 非必须 ID |- company string 非必须 所在公司 |- job string 非必须 职位 |- begin string 非必须 开始时间 |- end string 非必须 结束时间 |- empId number 非必须 员工ID 请求数据样例:
{"id": 2,"username": "zhangwuji","name": "张无忌","gender": 1,"image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2022-09-02-00-27-53B.jpg","job": 2,"salary": 8000,"entryDate": "2015-01-01","deptId": 2,"exprList": [{"id": 1,"begin": "2012-07-01","end": "2015-06-20""company": "中软国际股份有限公司""job": "java开发","empId": 2},{"id": 2,"begin": "2015-07-01","end": "2019-03-03""company": "百度科技股份有限公司""job": "java开发","empId": 2}]}
-
响应数据
参数格式:application/json
参数说明:
参数名 类型 是否必须 备注 code number 必须 响应码,1 代表成功,0 代表失败 msg string 非必须 提示信息 data object 非必须 返回的数据 响应数据样例:
{"code":1,"msg":"success","data":null }
2.2.2 实现思路
2.2.3 代码实现
1). EmpController
增加 update
方法接收请求参数,响应数据
/**
* 更新员工信息
*/
@PutMapping
public Result update(@RequestBody Emp emp){log.info("修改员工信息, {}", emp);empService.update(emp);return Result.success();
}
2). EmpService
接口增加 update
方法
/**
* 更新员工信息
* @param emp
*/
void update(Emp emp);
3). EmpServiceImpl
实现类实现 update
方法
@Transactional
@Override
public void update(Emp emp) {//1. 根据ID更新员工基本信息emp.setUpdateTime(LocalDateTime.now());empMapper.updateById(emp);
//2. 根据员工ID删除员工的工作经历信息 【删除老的】empExprMapper.deleteByEmpIds(Arrays.asList(emp.getId()));
//3. 新增员工的工作经历数据 【新增新的】Integer empId = emp.getId();List<EmpExpr> exprList = emp.getExprList();if(!CollectionUtils.isEmpty(exprList)){exprList.forEach(empExpr -> empExpr.setEmpId(empId));empExprMapper.insertBatch(exprList);}
}
4). EmpMapper
接口中增加 updateById
方法
/**
* 更新员工基本信息
*/
void updateById(Emp emp);
5). EmpMapper.xml
配置文件中定义对应的SQL语句,基于动态SQL更新员工信息
<!--根据ID更新员工信息-->
<update id="updateById">update emp<set><if test="username != null and username != ''">username = #{username},</if><if test="password != null and password != ''">password = #{password},</if><if test="name != null and name != ''">name = #{name},</if><if test="gender != null">gender = #{gender},</if><if test="phone != null and phone != ''">phone = #{phone},</if><if test="job != null">job = #{job},</if><if test="salary != null">salary = #{salary},</if><if test="image != null and image != ''">image = #{image},</if><if test="entryDate != null">entry_date = #{entryDate},</if><if test="deptId != null">dept_id = #{deptId},</if><if test="updateTime != null">update_time = #{updateTime},</if></set>where id = #{id}
</update>
2.2.4 Apifox测试
重新启动服务,打开 Apifox 进行接口测试。
2.2.5 前后端联调测试
点击保存之后,查看更新后的数据。
3. 异常处理
3.1 当前问题
当我们在修改部门数据的时候,如果输入一个在数据库表中已经存在的手机号,点击保存按钮之后,前端提示了错误信息,但是返回的结果并不是统一的响应结果,而是框架默认返回的错误结果 。
状态码为500,表示服务器端异常,我们打开idea,来看一下,服务器端出了什么问题。
上述错误信息的含义是,emp
员工表的phone
手机号字段的值重复了,因为在数据库表emp
中已经有了13309090001这个手机号了,我们之前设计这张表时,为phone
字段建议了唯一约束,所以该字段的值是不能重复的。
而当我们再将该员工的手机号也设置为 13309090001
,就违反了唯一约束,此时就会报错。
我们来看一下出现异常之后,最终服务端给前端响应回来的数据长什么样。
响应回来的数据是一个JSON格式的数据。但这种JSON格式的数据还是我们开发规范当中所提到的统一响应结果Result吗?显然并不是。由于返回的数据不符合开发规范,所以前端并不能解析出响应的JSON数据 。
接下来我们需要思考的是出现异常之后,当前案例项目的异常是怎么处理的?
-
答案:没有做任何的异常处理
当我们没有做任何的异常处理时,我们三层架构处理异常的方案:
-
Mapper接口在操作数据库的时候出错了,此时异常会往上抛(谁调用Mapper就抛给谁),会抛给service。
-
service 中也存在异常了,会抛给controller。
-
而在controller当中,我们也没有做任何的异常处理,所以最终异常会再往上抛。最终抛给框架之后,框架就会返回一个JSON格式的数据,里面封装的就是错误的信息,但是框架返回的JSON格式的数据并不符合我们的开发规范。
3.2 解决方案
那么在三层构架项目中,出现了异常,该如何处理?
-
方案一:在所有Controller的所有方法中进行try…catch处理
-
缺点:代码臃肿(不推荐)
-
-
方案二:全局异常处理器
-
好处:简单、优雅(推荐)
-
3.3 全局异常处理器
我们该怎么样定义全局异常处理器?
-
定义全局异常处理器非常简单,就是定义一个类,在类上加上一个注解@RestControllerAdvice,加上这个注解就代表我们定义了一个全局异常处理器。
-
在全局异常处理器当中,需要定义一个方法来捕获异常,在这个方法上需要加上注解@ExceptionHandler。通过@ExceptionHandler注解当中的value属性来指定我们要捕获的是哪一类型的异常。
@RestControllerAdvice
public class GlobalExceptionHandler {
//处理异常@ExceptionHandlerpublic Result ex(Exception e){//方法形参中指定能够处理的异常类型e.printStackTrace();//打印堆栈中的异常信息//捕获到异常之后,响应一个标准的Resultreturn Result.error("对不起,操作失败,请联系管理员");}
}
@RestControllerAdvice = @ControllerAdvice + @ResponseBody
处理异常的方法返回值会转换为json后再响应给前端
重新启动SpringBoot服务,打开浏览器,再来测试一下 修改员工 这个操作,我们依然设置已存在的 "13309090001" 这个手机号:
此时,我们可以看到,出现异常之后,异常已经被全局异常处理器捕获了。然后返回的错误信息,被前端程序正常解析,然后提示出了对应的错误提示信息。
以上就是全局异常处理器的使用,主要涉及到两个注解:
@RestControllerAdvice //表示当前类为全局异常处理器
@ExceptionHandler //指定可以捕获哪种类型的异常进行处理
3.4 如何抛出自定义异常信息
package com.itheima.exception;
public class BusinessException extends RuntimeException{public BusinessException() {super();}public BusinessException(String message) {super(message);}
}
package com.itheima.exception;
import com.itheima.pojo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/*** 全局异常处理器*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandlerpublic Result handleException(Exception e){log.error("程序运行出错啦 .... ", e);return Result.error("对不起, 系统访问异常, 请联系管理员 ~");}
@ExceptionHandlerpublic Result handleBussinessException(BusinessException e){log.error("程序运行出错啦 .... ", e);return Result.error(e.getMessage());}
}
4. 员工信息统计
员工管理的增删改查功能我们已开发完成,接下来,我们再来完成员工信息统计的接口开发。 对于这些图形报表的开发,其实呢,都是基于现成的一些图形报表的组件开发的,比如:Echarts、HighCharts等。
而报表的制作,主要是前端人员开发,引入对应的组件(比如:ECharts)即可。 服务端开发人员仅为其提供数据即可。
官网:Apache ECharts
4.1 职位统计
4.1.1 需求
对于这类的图形报表,服务端要做的,就是为其提供数据即可。 我们可以通过官方的示例,看到提供的数据其实就是X轴展示的信息,和对应的数据。
4.1.2 接口文档
1). 基本信息
请求路径:/report/empJobData
请求方式:GET
接口描述:统计各个职位的员工人数
2). 请求参数
无
3). 响应数据
参数格式:application/json
参数说明:
参数名 | 类型 | 是否必须 | 备注 |
---|---|---|---|
code | number | 必须 | 响应码,1 代表成功,0 代表失败 |
msg | string | 非必须 | 提示信息 |
data | object | 非必须 | 返回的数据 |
|- jobList | string[] | 必须 | 职位列表 |
|- dataList | number[] | 必须 | 人数列表 |
响应数据样例:
{"code": 1,"msg": "success","data": {"jobList": ["教研主管","学工主管","其他","班主任","咨询师","讲师"],"dataList": [1,1,2,6,8,13]}
}
为了封装上面需要给前端返回的数据,在pojo包下再创建一个实体类 JobOption
,封装给前端返回的结果:
/*** 员工职位人数统计*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class JobOption {private List jobList; //职位列表private List dataList; //人数列表
}
4.1.3 代码实现
1). 定义ReportController,并添加方法。
@Slf4j
@RequestMapping("/report")
@RestController
public class ReportController {
@Autowiredprivate ReportService reportService;
/*** 统计各个职位的员工人数*/@GetMapping("/empJobData")public Result getEmpJobData(){log.info("统计各个职位的员工人数");JobOption jobOption = reportService.getEmpJobData();return Result.success(jobOption);}
}
2). 定义ReportService接口,并添加接口方法。
public interface ReportService {/*** 统计各个职位的员工人数* @return*/JobOption getEmpJobData();
}
3). 定义ReportServiceImpl实现类,并实现方法
@Service
public class ReportServiceImpl implements ReportService {
@Autowiredprivate EmpMapper empMapper;@Overridepublic JobOption getEmpJobData() {List<Map<String,Object>> list = empMapper.countEmpJobData();List<Object> jobList = list.stream().map(dataMap -> dataMap.get("pos")).toList();List<Object> dataList = list.stream().map(dataMap -> dataMap.get("total")).toList();return new JobOption(jobList, dataList);}
}
4). 定义EmpMapper 接口
统计的是员工的信息,所以需要操作的是员工表。 所以代码我们就写在 EmpMapper
接口中即可。
/*** 统计各个职位的员工人数*/
@MapKey("pos")
List<Map<String,Object>> countEmpJobData();
如果查询的记录往Map中封装,可以通过@MapKey注解指定返回的map中的唯一标识是那个字段。【也可以不指定】
5). 定义EmpMapper.xml
<!-- 统计各个职位的员工人数 -->
<select id="countEmpJobData" resultType="java.util.Map">select(case job when 1 then '班主任' when 2 then '讲师' when 3 then '学工主管' when 4 then '教研主管' when 5 then '咨询师' else '其他' end) pos,count(*) totalfrom emp group by joborder by total
</select>
case流程控制函数:
语法一:case when cond1 then res1 [ when cond2 then res2 ] else res end ;
含义:如果 cond1 成立, 取 res1。 如果 cond2 成立,取 res2。 如果前面的条件都不成立,则取 res。
语法二(仅适用于等值匹配):case expr when val1 then res1 [ when val2 then res2 ] else res end ;
含义:如果 expr 的值为 val1 , 取 res1。 如果 expr 的值为 val2 ,取 res2。 如果前面的条件都不成立,则取 res。
4.1.4 Apifox测试
重新启动服务,打开Apifox进行测试。
4.1.5 联调测试
4.2 性别统计
4.2.1 需求
对于这类的图形报表,服务端要做的,就是为其提供数据即可。 我们可以通过官方的示例,看到提供的数据就是一个json格式的数据。
4.2.2 接口文档
1). 基本信息
请求路径:/report/empGenderData
请求方式:GET
接口描述:统计员工性别信息
2). 请求参数
无
3). 响应数据
参数格式:application/json
参数说明:
参数名 | 类型 | 是否必须 | 备注 |
---|---|---|---|
code | number | 必须 | 响应码,1 代表成功,0 代表失败 |
msg | string | 非必须 | 提示信息 |
data | object[] | 非必须 | 返回的数据 |
|- name | string | 非必须 | 性别 |
|- value | number | 非必须 | 人数 |
响应数据样例:
{"code": 1,"msg": "success","data": [{"name": "男性","value": 5},{"name": "女性","value": 6}]
}
4.2.3 代码实现
1). 在ReportController,添加方法。
/*** 统计员工性别信息*/
@GetMapping("/empGenderData")
public Result getEmpGenderData(){log.info("统计员工性别信息");List<Map> genderList = reportService.getEmpGenderData();return Result.success(genderList);
}
2). 在ReportService接口,添加接口方法。
/*** 统计员工性别信息*/
List<Map> getEmpGenderData();
3). 在ReportServiceImpl实现类,实现方法
@Override
public List<Map> getEmpGenderData() {return empMapper.countEmpGenderData();
}
4). 定义EmpMapper 接口
统计的是员工的信息,所以需要操作的是员工表。 所以代码我们就写在 EmpMapper
接口中即可。
/*** 统计员工性别信息*/
@MapKey("name")
List<Map> countEmpGenderData();
5). 定义EmpMapper.xml
<!-- 统计员工的性别信息 -->
<select id="countEmpGenderData" resultType="java.util.Map">selectif(gender = 1, '男', '女') as name,count(*) as valuefrom emp group by gender ;
</select>
if函数语法:
if(条件, 条件为true取值, 条件为false取值)
ifnull函数语法:
ifnull(expr, val1)
如果expr不为null,取自身,否则取val1
4.2.4 Apifox测试
4.2.5 联调测试
相关文章:

【免费Web系列】JavaWeb实战项目案例六
这是Web第一天的课程大家可以传送过去学习 http://t.csdnimg.cn/K547r 员工信息-删除&修改 前面我们已经实现了员工信息的条件分页查询以及新增操作。 关于员工管理的功能,还有两个需要实现: 删除员工 修改员工 除了员工管理的功能之外&#x…...

git分布式版本控制系统(四)
目前世界上最先进的分布式版本控制系统 官方网址:https://git-scm.com 学习目标: 1 了解 git 前世今生 2 掌握 git 基础概念、基础操作 3 各种 git 问题处理 4 互联网常用 gitflow(工作流程规范) 5 git 代码提交规范 6 git 分支管理及命名规范 常见问…...

【React篇】简述React-Router 的实现原理及工作方式
React Router 路由的基础实现原理分为两种,如果是切换 Hash 的方式,那么依靠浏览器 Hash 变化即可;如果是切换网址中的 Path,就要用到 HTML5 History API 中的 pushState、replaceState 等。在使用这个方式时,还需要在…...

Django里多app
在 Django 里的某一个项目,里面得包含很多 App (功能),那么如何在该项目里管理这么多App呢? 先说明下背景:未先创建 apps 文件夹来存各个app文件夹,直接在项目文件目录里创建各个app。为了便于管理,得将各…...

Prime1 - 信息收集和分析能力的试炼
主机发现 nmap扫描与分析 端口22、80 详细扫描;linux、ubuntu、 udp扫描 端口都是关闭的 脚本扫描 web渗透 打开只有一张图片;源码有图片和一个alt:hnp security不知道有啥用,先记录下来吧 继续web渗透思路走吧,目录…...

3.location的写法
location的写法 一、location的写法1、 精确匹配2、~ 以正则表达式匹配请求,区分大小写3、~* 以正则匹配请求,不区分大小写4、^~ 不以正则的方式匹配请求 二、stub_status模块显示工作状态三、url地址重写 rewrite模块1、语法2、针对项目结构有变化3、网…...

AndroidStudio设置允许APP获取定位权限
1. 在AndroidManifest.xml中声明权限 常用的定位权限有以下两种: <uses-permission android:name"android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name"android.permission.ACCESS_COARSE_LOCATION"/>2. …...

Spring Boot 统一数据返回格式
在 Spring Boot 项目中,统一的数据格式返回是一种良好的实践,它提高了代码的可维护性和一致性,并改善了客户端与服务端之间的通信。本文将介绍如何在 Spring Boot 中实现统一的数据格式返回。 1 为什么需要统一数据返回格式 ⽅便前端程序员更…...

Android 项目Gradle文件讲解(Groovy和Kotlin)
Android 项目Gradle文件讲解(Groovy和Kotlin) 前言正文一、Gradle的作用二、Gradle的种类① 工程build.gradle② 项目build.gradle③ settings.gradle④ gradle.properties⑤ gradle-wrapper.properties⑥ local.properties 三、Groovy和Kotlin的语言对比…...

python-最接近target的值
【问题描述】:给定一个数组,在数组中找到两个数,使它们的和最接近目标值的值但不超过目标值,然后返回它们的和。 【问题示例】:输入target15,array[1,3,5,11,7],输出14,31114。 完整代码如下: …...

转换张量形状:`nlc_to_nchw` 函数详解
在深度学习和计算机视觉领域,张量的形状转换是一个常见的操作。本文将详细讲解一个用于形状转换的函数 nlc_to_nchw,它能够将形状为 [N, L, C] 的张量转换为 [N, C, H, W] 的张量。 函数定义 def nlc_to_nchw(x, hw_shape):"""Convert …...

「架构」云上自动化运维及其应用
随着云计算的普及,自动化运维成为企业提升运营效率和降低成本的关键。本文通过分析一家中型企业实施云上自动化运维(CloudOps)的案例,探讨了自动化监控、配置管理和持续集成/持续部署(CI/CD)三个核心模块的实际应用。文章详细阐述了每个模块的技术选型、实施原因、优缺点…...

分布式和集群的区别
分布式系统(Distributed System)和集群(Cluster)是两个经常被提及的计算机科学概念,它们在提高系统性能和可靠性方面都扮演着重要角色,很多同学会觉得这俩个是同一种东西,但事实上它们之间有着本…...

最新h5st(4.7.2)参数分析与纯算法还原(含算法源码)
文章目录 1. 写在前面2. 加密分析3. 算法还原 【🏠作者主页】:吴秋霖 【💼作者介绍】:擅长爬虫与JS加密逆向分析!Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python…...

Spark大数据 掌握RDD的创建
在Apache Spark中,弹性分布式数据集(Resilient Distributed Dataset,简称RDD)是一个核心的数据结构,用于表示不可变、可分区、可并行操作的元素集合。理解并掌握RDD的创建是使用Spark进行大数据处理的关键步骤之一。 …...

Chrome谷歌浏览器如何打开不安全页面的禁止权限?
目录 一、背景二、如何打开不安全页面被禁止的权限?2.1 第一步,添加信任站点2.2 第二步,打开不安全页面的权限2.3 结果展示 一、背景 在开发过程中,由于测试环境没有配置 HTTPS 请求,所以谷歌浏览器的地址栏会有这样一…...

3D目标检测入门:探索OpenPCDet框架
前言 在自动驾驶和机器人视觉这两个飞速发展的领域中,3D目标检测技术扮演着核心角色。随着深度学习技术的突破性进展,3D目标检测算法的研究和应用正日益深入。OpenPCDet,这个由香港中文大学OpenMMLab实验室精心打造的开源工具箱,…...

JS异步编程
目录 概念定时器Promise对象概念 单线程模型指的是,JavaScript 只在一个线程上运行。也就是说,JavaScript 同时只能执行一个任务,其他任务都必须在后面排队等待。JavaScript 只在一个线程上运行,不代表 JavaScript 引擎只有一个线程。事实上,JavaScript 引擎有多个线程,…...

多元联合分布建模 Copula python实例
多元联合分布建模 Copula python实例 目录 库安装 实例可视化代码 库安装 pip install copulas 实例可视化代码 import numpy as np import pandas as pd from copulas.multivariate import GaussianMultivariate# Generate some example data np.random.seed(42) data = …...

单号日入50+,全自动挂机赚钱
大家好!今天我为大家精心挑选了一个极具潜力的副业项目——“游戏工作室自由之刃2:单号日入50,全自动挂机赚钱”。 传奇游戏,无疑是许多人心中那段青春时光的珍贵回忆。 即便是其手游版本,也依旧保持着极高的热度和人…...

LabVIEW老程序功能升级:重写还是改进?
概述:面对LabVIEW老程序的功能升级,开发者常常面临重写与改进之间的选择。本文从多个角度分析两种方法的利弊,并提供评估方法和解决思路。 重写(重新开发)的优势和劣势: 优势: 代码清晰度高&a…...

chrome谷歌浏览器开启Gemini Nano模型
前提 确保您的操作系统语言设置为英语(美国) 可能还需要将 Chrome 浏览器的语言更改为英语(美国)。 下载dev或Canary版本Chrome Chrome Canary Chrome Dev 注意:确认您的版本高于 127.0.6512.0。 其中一个Chrome版本…...

C语言王国——内存函数
目录 1 memcpy函数 1.1 函数表达式 1.2 函数模拟 2 memmove函数 2.1 函数的表达式 2.2 函数模拟 3 memset函数 3.1 函数的表达式 3.2 函数的运用 4 memcmp函数 4.1函数的表达式: 4.2 函数的运用 5 结论 接上回我们讲了C语言的字符和字符串函数&#…...

【计算机组成原理】1.1计算机的软硬件组成(记录学习计算机组成原理)
文章目录 1.早期的冯诺依曼机2.早期冯诺依曼机的基本运行框图3.早期冯诺依曼机的特点4.现代计算机的结构5. 小结 本次及以后有关于计算机组成原理的文章,旨在做学习时的记录和知识的分享。不论是应对期末考试,还是考研都是很有帮助的。希望大家多多支持更…...

Qt xml学习之calculator-qml
1.功能说明:制作简易计算器 2.使用技术:qml,scxml 3.项目效果: 4.qml部分: import Calculator 1.0 //需要引用对应类的队友版本 import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 1.4 import QtScxml…...

低代码开发系统是什么?它有那些部分组成?
低代码开发系统是什么?它有那些部分组成? 一、引言 在当今快速变化的商业环境中,企业对于快速响应市场需求、降低开发成本和提高开发效率的需求日益增强。低代码开发系统(Low-Code Development Platform)应运而生&am…...

2024年西安交通大学程序设计竞赛校赛
2024年西安交通大学程序设计竞赛校赛 文章目录 2024年西安交通大学程序设计竞赛校赛D瑟莉姆的宴会E: 雪中楼I: 命令行(待补)J:最后一块石头的重量(待补)K: 崩坏:星穹铁道(待补)M:生命游戏N: 圣诞树 D瑟莉姆的宴会 解题思路: …...

【学习Day5】操作系统
✍🏻记录学习过程中的输出,坚持每天学习一点点~ ❤️希望能给大家提供帮助~欢迎点赞👍🏻收藏⭐评论✍🏻指点🙏 学习编辑文章的时间不太够用,先放思维导图,后续复习完善细节。...

学习小记录——python函数的定义和调用
今日小好运,未来有好运。🎁💖🫔 分享个人学习的小小心意,一起来看看吧 函数的定义 函数通常来说就是带名字的代码块,用于完成具体的工作,需要使用的时候调用即可,这不仅提高代码的…...

RHEL7.9修改分区
系统RHEL7.9 他因为安装软件,需要修改分区 进入超级用户root,输入lsblk 查看分区,可见465.8G系统盘sda下有三个物理卷,其中sda3下/home有410.6G,需要这部分拆分出200G软件和100G的数据库分区 备份/home 目录下文件 c…...