项目开发中的问题(7.8)
1、SpringMVC 接收参数的方式
方式一:@RequestParam(接收普通键值对)
适用场景
前端通过 URL 查询参数(?key=value)或普通表单提交数据。这是最基础、最常用的方式。
| 维度 | 内容 |
|---|---|
| 前端请求示例 | GET /user/detail?userId=1001&token=abc123 |
| 后端注解 | @RequestParam("参数名") |
| 特点 | 参数名需与形参名一致(或通过注解指定映射),类型需相容。支持设置默认值和必填校验。 |
后端代码示例
1@GetMapping("/user/detail")
2public String getUser(@RequestParam("userId") Long id,
3 @RequestParam("token") String token) {
4 return "用户ID是:" + id + ",Token是:" + token;
5}
方式二:@PathVariable(接收路径参数)
适用场景
RESTful 风格接口,参数直接拼在 URL 路径中,通常用于标识某个具体资源(如 ID)。
| 维度 | 内容 |
|---|---|
| 前端请求示例 | GET /user/1001 |
| 后端注解 | @PathVariable("变量名") |
| 特点 | 参数嵌入在 URL 路径中,URL 更加美观且语义化强。 |
后端代码示例
1@GetMapping("/user/{userId}")
2public String getUserById(@PathVariable("userId") Long id) {
3 return "查询到的用户ID是:" + id;
4}
方式三:@RequestBody(接收 JSON 数据)
适用场景
前端发送 POST/PUT 请求,且 Content-Type 为 application/json,传递复杂结构化数据。
| 维度 | 内容 |
|---|---|
| 前端请求示例 | POST /user/add Body: { "username": "张三", "age": 25 } |
| 后端注解 | @RequestBody |
| 特点 | 将 JSON 字符串自动反序列化为 Java 对象或集合。一个方法只能使用一次该注解。 |
后端代码示例
1@PostMapping("/user/add")
2public String addUser(@RequestBody User user) {
3 return "成功添加用户:" + user.getUsername();
4}
方式四:直接接收(不加注解)
适用场景
这是最基础的方式,适用于简单类型、数组以及自定义对象(JavaBean)。SpringMVC 会自动根据名称匹配并转换类型。
| 维度 | 内容 |
|---|---|
| 前端请求示例 | GET /list?pageNum=1&pageSize=10 POST /delete?ids=1&ids=2 |
| 后端写法 | 方法参数直接写变量名,无需注解 |
| 特点 | 要求传递的参数名字与接收的形参名字一致,类型相容即可自动转换。 |
后端代码示例
1// 1. 简单类型 & 数组类型
2@GetMapping("/list")
3public String getList(int pageNum, int pageSize, Long[] ids) {
4 return "页码:" + pageNum;
5}
6
7// 2. 自定义 JavaBean (自动填充属性)
8@PostMapping("/save")
9public String save(User user) { // User类中需有对应的setter方法
10 return "保存用户:" + user.getUsername();
11}
注意事项
- 框架关系:SpringBoot 本质上是快速构建框架的 Spring 框架,SpringMVC 是 Spring 的一部分。
- 底层原理:SpringMVC 接收参数本质上是对原生 Servlet 方式的封装。
- 原生对比:上述所有方式最终都对应原生的
String 变量名 = request.getParameter("形参名")。
2、:key、:label、:value属性的含义
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="用户状态" clearable style="width: 240px">
<el-option
v-for="dict in dict.type.sys_normal_disable"
:label="dict.label"
:value="dict.value"
:key="dict.value" />
</el-select>
</el-form-item>
| 属性 | 对应的字典字段 | “正常”时的值 | “停用”时的值 | 作用通俗解释 |
|---|---|---|---|---|
:label |
dict.label |
"正常" | "停用" | 给用户看的(面子) |
:value |
dict.value |
"0" | "1" | 给程序存的(里子) |
:key |
dict.value |
"0" | "1" | 给 Vue 认人的(身份证) |
3、prop=‘’ ‘’作用
<!-- 表格数据部分 -->
<el-table v-loading="loading" :data="postList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="岗位编号" align="center" prop="postId" />
<el-table-column label="岗位编码" align="center" prop="postCode" />
<el-table-column label="岗位名称" align="center" prop="postName" />
<el-table-column label="岗位排序" align="center" prop="postSort" />
<el-table-column label="状态" align="center" prop="status">
<template slot-scope="scope">
<dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status"/>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['system:post:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['system:post:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 添加或修改岗位对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="岗位名称" prop="postName">
<el-input v-model="form.postName" placeholder="请输入岗位名称" />
</el-form-item>
<el-form-item label="岗位编码" prop="postCode">
<el-input v-model="form.postCode" placeholder="请输入编码名称" />
</el-form-item>
<el-form-item label="岗位顺序" prop="postSort">
<el-input-number v-model="form.postSort" controls-position="right" :min="0" />
</el-form-item>
<el-form-item label="岗位状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio
v-for="dict in dict.type.sys_normal_disable"
:key="dict.value"
:label="dict.value"
>{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
| 维度 | 表格 (el-table-column) |
表单 (el-form-item) |
|---|---|---|
| 核心作用 | 数据展示(从数据源取值显示) | 数据校验(关联 rules 验证规则) |
| 配合对象 | 配合 :data 属性 |
配合 :rules 属性和 :model 属性 |
| 能否省略 | 使用插槽自定义内容时可省略;纯文本展示时不可省略 | 不可省略(否则校验失效) |
| 取值方式 | 自动提取或 scope.row.字段名 |
必须与 v-model 绑定的字段名一致 |
- 含义:
prop是 Properties(属性) 的缩写。它是自定义组件的“对外接口”。 - 作用:专门用于父组件向子组件传递数据。它保证了数据的单向流动(父传子),防止子组件意外篡改父组件的数据,从而让代码更安全、组件更容易复用。
总结:prop 的本质是组件接收外部数据的接口;在表格里它负责“提取数据展示”,在表单里它负责“关联规则校验”。
4、日期控件中的type属性
<el-col :span="24">
<el-form-item label="到店时间" prop="actualArrivalTime">
<el-date-picker clearable
v-model="form.actualArrivalTime"
style="width:100%"
type="datetime" //原为date,项目中带时分秒需改为datetime
value-format="yyyy-MM-dd HH:mm:ss"
placeholder="请选择实际到店时间">
</el-date-picker>
</el-form-item>
</el-col>
(1)type="date":这是日期选择器。它的逻辑是“选中即完成”,当你点击日历上的某一天(例如 9月15日),下拉框会自动关闭并赋值,因此它不需要“此刻”和“确定”按钮。
(2)type="datetime":这是日期时间选择器。因为它既需要选日期,又需要在下方滑动选择具体的时、分、秒,所以必须有一个“确定”按钮来确认最终的时间点,同时也会有“此刻”按钮快速填充当前时间。
5、数据表格中的时间显示和新增编辑模态框中的时间显示
(1)数据表格:
<el-table-column label="预约时间" align="center" prop="appointmentTime" width="200">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.appointmentTime,'{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
(2)新增、编辑模态框:
注意格式为"yyyy-MM-dd HH:mm:ss"
<el-form-item label="预约时间" prop="appointmentTime">
<el-date-picker clearable
v-model="form.appointmentTime"
style="width: 380px;"
size="large"
type="datetime"
value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择预约时间">
</el-date-picker>
</el-form-item>
6、结算单页面列表时间显示问题
为什么新增数据后,列表中显示的只有年月日,时分秒为00:00:00?
因为前端通过新增添加数据,向后端传过去的时间是带时分秒的(日期控件中的type属性为"datetime")。

后端在Controller中BusStatement接请求,但实际上是在domain中BusStatement中的actualArrivalTime接。
在@JsonFormat(pattern = "yyyy-MM-dd")注解中,只定义了年月日,所以要在注解中加入 HH:mm:ss
/** 实际到店时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "实际到店时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date actualArrivalTime;
7、结算单页面高级搜索时间查询问题
问题:为什么搜索到店时间为当天的结算单数据却搜索不到?
例如:
搜索2026年7月15号的数据却搜索不到。

原因:
这是因为前端发的请求只带年月日,没带时分秒。Java中接收数据若没带时分秒,则默认为00:00:00。
也就是开始时间为2026-7-15 00:00:00,结束时间为2026-7-15 00:00:00。

如何解决?
只需将结束时间改为当天的23:59:59即可。
解决步骤:
(1)分析前端数据是如何发送的?
开始日期和结束日期用"-"分割,就可以把actualArrivalTime的值会变成一个数组。
例如:2023-10-01 到 2023-10-31,拆分后变为["2023-10-01", "2023-10-31"]。
高级搜索部分v-model,双向数据绑定了daterangeActualArrivalTime,那么在data中一定会有这个数据。
相当于先绑定到daterangeActualArrivalTime中,后续如果传递了时间,就按'-'分割开始时间和结束时间,后面再向params中添加开始时间和结束时间,供时间查询。
// 备注信息时间范围
daterangeActualArrivalTime: [],
<!-- 高级搜索部分 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="到店时间">
<el-date-picker
v-model="daterangeActualArrivalTime"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
actualArrivalTime: null,
isDelete: null
},
到店时间绑定了"daterangeActualArrivalTime",但是在queryParams中只有actualArrivalTime,它是如何封装的?
首先,点击搜索时会调用getList()方法。
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 查询结算单列表 */
getList() {
this.loading = true
// 相当于向 this.queryParams 中追加一个属性 params:{}
this.queryParams.params = {}
// 当 daterangeActualArrivalTime数组 != null && 长度 != 0
// 说明我们填写了高级搜索中的时间
if (null != this.daterangeActualArrivalTime && '' != this.daterangeActualArrivalTime) {
// 相当于向this.queryParams.params追加了开始时间和结束时间两个属性,
// 值为我们填写的 第一个(开始时间) 第二个(结束时间)
this.queryParams.params["beginActualArrivalTime"] = this.daterangeActualArrivalTime[0]
this.queryParams.params["endActualArrivalTime"] = this.daterangeActualArrivalTime[1]
}
listStatement(this.queryParams).then(response => {
this.statementList = response.rows
this.total = response.total
this.loading = false
})
},
相当于
queryParams: {
pageNum: 1,
pageSize: 10,
actualArrivalTime: null,
isDelete: null,
params:{
beginActualArrivalTime:this.daterangeActualArrivalTime[0],
endActualArrivalTime:this.daterangeActualArrivalTime[1]
}
}
(2)后端是如何接收的?
那为什么一定要追加一个params,接着往里添加两组数据?为什么一定叫params?
实际上,我们的实体类BusStatement继承了BaseEntity。
BaseEntity存在Map<String, Object>的请求参数params。
相当于把前端的这个 params:{beginActualArrivalTime:this.daterangeActualArrivalTime[0], endActualArrivalTime:this.daterangeActualArrivalTime[1] }传到后端的Map<String, Object> params中去了。
public class BaseEntity implements Serializable
{
private static final long serialVersionUID = 1L;
/** 搜索值 */
@JsonIgnore
private String searchValue;
/** 创建者 */
private String createBy;
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/** 更新者 */
private String updateBy;
/** 更新时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/** 备注 */
private String remark;
/** 请求参数 */
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Map<String, Object> params;
所以前端是强制性用这种方式给后端传值,如果前端传了时间,才封装params;如果没传,就不封装,避免查询时出问题。
(3)处理高级搜索时间问题
解决方法:结束时间 + 1天 - 1秒
/**
* 查询结算单列表
*
* @param busStatement 结算单
* @return 结算单
*/
@Override
public List<BusStatement> selectBusStatementList(BusStatement busStatement) throws ParseException {
// 处理高级搜索时间问题 结束时间 + 1天 - 1秒
// 1. 接收结束时间 刚进入页面,前端没传时间,则获取 endActualArrivalTime 为 null
String endActualArrivalTime = (String)busStatement.getParams().get("endActualArrivalTime");
if(endActualArrivalTime != null){
// 2. 转为 Date 类型
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date endDate = sdf.parse(endActualArrivalTime);
// 3. 创建 Calendar 类来更新时间
Calendar calendar = Calendar.getInstance();
calendar.setTime(endDate);
// + 1 天
calendar.add(Calendar.DAY_OF_YEAR,1);
// - 1 秒
calendar.add(Calendar.SECOND,-1);
// 转换后,再将其转换为 Date 类型
Date changeDate = calendar.getTime();
// 再将无时分秒的,转换为 23:59:59 这种格式带时分秒的字符串
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String endTime = sdf1.format(changeDate);
// 再设置回给结束时间
busStatement.getParams().put("endActualArrivalTime",endTime);
}
return busStatementMapper.selectBusStatementList(busStatement);
}
-
初始获取(String 类型)
endActualArrivalTime="2026-07-16"说明: 从 Map 中取出的原始字符串,只有日期。原来是Object类型,需强转为String类型。
-
转为 Date 对象(Date 类型)
endDate=Wed Jul 16 00:00:00 CST 2026说明: 代码执行
sdf.parse()。因为原字符串没写时间,系统自动补全为当天的 0点0分0秒。 -
日历运算后(Calendar -> Date 类型)
changeDate=Thu Jul 17 23:59:59 CST 2026说明: 代码先 +1天 (变成17号0点),再 -1秒 (变成16号最后一秒)。目的是包含 "16号" 这一整天的数据。
-
最终格式化(String 类型)
endTime="2026-07-16 23:59:59"说明: 代码执行
sdf1.format()。将复杂的 Date 对象重新变回字符串,此时带上了时分秒,可以直接传给数据库进行范围查询。
String (日期) →→ Date (对象) →→ Calendar (日历工具) →→ Date (对象) →→ String (时间戳)
封装成工具类:
package cn.wolfcode.business.utils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class DateUtil {
/**
* 更新时间
* @param date 需要更新的时间
* @param type 要算什么单位
* @param amount 要加多少
* @return 更新后的时间
*/
public static Date changeDate(Date date,Integer type,Integer amount){
if(date == null || type == null || amount == null){
return null;
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(type,amount);
// 转换回 Date 类型
return calendar.getTime();
}
/**
* String 转 Date
* @param str 需要转换的字符串
* @param regex 指定转换的格式
* @return 转换后的 Date 日期类型
*/
public static Date parseDate(String str,String regex){
if(str == null || regex == null){
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat(regex);
try {
return sdf.parse(str);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
/**
* 日期转换为字符串
* @param date 需要转换的日期
* @param regex 转换格式
* @return 转换后的字符串
*/
public static String parseString(Date date,String regex){
if(date == null || regex == null){
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat(regex);
return sdf.format(date);
}
}
封装后的实现类:
/**
* 查询结算单列表
*
* @param busStatement 结算单
* @return 结算单
*/
@Override
public List<BusStatement> selectBusStatementList(BusStatement busStatement) throws ParseException {
// 处理高级搜索时间问题 结束时间 + 1天 - 1秒
// 1. 接收结束时间 刚进入页面,前端没传时间,则获取 endActualArrivalTime 为 null
String endActualArrivalTime = (String)busStatement.getParams().get("endActualArrivalTime");
// 转换为 Date 类型
Date date = DateUtil.parseDate(endActualArrivalTime,"yyyy-MM-dd");
// 时间 + 1天 - 1秒
Date addDate = DateUtil.changeDate(date, Calendar.DAY_OF_YEAR,1);
Date subDate = DateUtil.changeDate(addDate, Calendar.SECOND,-1);
// 转回 String 类型
String endTime = DateUtil.parseString(subDate,"yyyy-MM-dd HH:mm:ss");
busStatement.getParams().put("endActualArrivalTime",endTime);
return busStatementMapper.selectBusStatementList(busStatement);
}
后端查询sql:
<select id="selectBusStatementList" parameterType="BusStatement" resultMap="BusStatementResult">
<include refid="selectBusStatementVo"/>
<where>
<if test="params.beginActualArrivalTime != null and params.beginActualArrivalTime != '' and params.endActualArrivalTime != null and params.endActualArrivalTime != ''"> and actual_arrival_time between #{params.beginActualArrivalTime} and #{params.endActualArrivalTime}</if>
<if test="isDelete != null "> and is_delete = #{isDelete}</if>
</where>
</select>
8、软删除问题
软删除在数据库层面本质是 Update 操作。但在 RESTful API 设计中,前端请求通常使用 DELETE 方法,部分场景下也会用 PATCH 或 POST,但绝不会用 PUT。
软删除带来的响应:
-
对于查询:我们必须添加 is_delete = 0;
- 设置默认只查询有效用户,若前端传了查询无效用户,则正常查询。
// 设置只查询有效用户 if(busStatement.getIsDelete() == null){ busStatement.setIsDelete(BusStatement.IS_NOT_DELETE); }<if test="isDelete != null "> and is_delete = #{isDelete}</if> -
对于新增:我们必须保证添加的用户默认 is_delete = 0
// 新录入的结算单 用户状态 --> 有效 busStatement.setIsDelete(BusStatement.IS_NOT_DELETE); -
对于编辑:我们必须保证 is_delete=0 的有效用户才可以进行编辑操作。
// 判断状态必须为有效用户才可以进行编辑操作 if(BusStatement.IS_DELETE.equals(oldObj.getIsDelete())){ throw new RuntimeException("无效用户禁止编辑"); } -
对于删除:必须是有效用户才能执行删除。
// 必须是有效用户才能执行删除 if(BusStatement.IS_DELETE.equals(oldObj.getIsDelete())){ throw new RuntimeException("无效用户禁止删除"); } // 最好修改方法名称为 updateIsDeleteById return busStatementMapper.updateIsDeleteById(id,BusStatement.IS_DELETE);<update id="updateIsDeleteById"> update bus_statement set is_delete = #{isDelete} where id = #{id} </update>
9、路由跳转
(1)明细按钮方法中:
/** 明细按钮操作 */
// statementId 形参 名字随意
handleGoToStatementItem(statementId){
// 编程式导航 通过写代码的方式让页面跳转
this.$router.push({
// 这里定义了一个跳转的目标地址。它通过字符串拼接,把 statementId 的值拼接到 URL 的末尾。
path:'/business/statementItem/index/' + statementId
})
}
(2)router下的index.js中:
{
// 父级路由路径
path: '/business/statementItem',
// 使用的布局组件
component: Layout,
// 设置为 true,表示该路由不会在侧边栏菜单中显示
hidden: true,
// 访问此路由所需的权限标识
permissions: ['business:statementItem:list'],
// 子路由配置
children: [
{
// 子路由路径,包含一个名为 statementId 的动态参数,且该参数必须是数字 (\\d+)
path: 'index/:statementId(\\d+)',
// 使用懒加载方式引入对应的页面组件
component: () => import('@/views/business/statementItem'),
// 路由的唯一名称,可用于编程式导航
name: 'statementItem',
// 路由元信息,用于存储额外数据
meta: {
// 页面标题,可用于浏览器标签或面包屑导航
title: '结算单明细',
// 当访问此隐藏页面时,让指定的菜单项保持高亮激活状态
activeMenu: '/business/statement'
}
}
]
},
10、添加或修改养修预约信息对话框中数据字典显示的问题
<el-form-item label="服务类型" prop="serviceType">
<el-select v-model="form.serviceType" placeholder="请选择服务类型" style="width: 100%">
<el-option v-for="dict in dict.type.appoint_serv_type"
:key="dict.value"
:label="dict.label"
:value="parseInt(dict.value)"></el-option>
</el-select>
</el-form-item>
(1)为什么要加parseInt()?
这是为了解决 Vue 和 Element UI 中非常经典的“类型匹配”问题。
- 字典数据的默认类型:在若依脚手架中,字典数据(
dict.type.appoint_serv_type)从后端接口返回时,它的value默认都是字符串类型(例如"1","2")。 - 表单数据的实际类型:由于后端 Java 实体类(如
BusAppointment)中,serviceType字段定义为Integer类型。因此,当点击“新增”或“编辑”时,后端返回给前端的form.serviceType是一个数字类型(例如1)。 - 回显失败的根源:Element UI 的
<el-select>组件在判断下拉框是否选中某一项时,底层使用的是 JavaScript 的严格相等(===)逻辑。如果不加parseInt,前端会拿数字1去和下拉框选项的字符串"1"进行匹配,由于1 === "1"的结果是false,导致下拉框无法正确回显,只能干巴巴地显示一串数字,而不是对应的文字标签。 - 加上
parseInt的效果:通过parseInt(dict.value)将字典的字符串"1"强制转换成了数字1。这样前端拿数字1去匹配数字1,1 === 1结果为true,下拉框就能完美回显了。
(2)“三等(===)”是什么意思?
“三等”指的是 JavaScript 中的严格相等运算符(===)。
它和普通的两个等号(==)最大的区别在于:它不仅比较“值”是否相等,还要求“数据类型”必须完全一致。
- 双等号(==):会进行隐式类型转换。例如
1 == "1",它会自动把字符串"1"转换成数字再进行比较,最终结果是true。 - 三等号(===):绝对严格,不进行任何转换。例如
1 === "1",因为一个是数字(Number),一个是字符串(String),类型不同,直接返回false。
11、MyBatis 动态 SQL 更新策略对比:是否使用 <if> 判断
(1)不带 <if>
- 传值时:正常更新为传入的值。
- 不传值时(null):一定会更新。数据库字段会被强制覆盖为
null,导致原有数据丢失。
actual_arrival_time = #{actualArrivalTime}
(2)带 <if>
- 传值时:正常更新为传入的值。
- 不传值时(null):不会更新。SQL 会忽略该字段,数据库保持原来的值不变。
<if test="actualArrivalTime != null">actual_arrival_time = #{actualArrivalTime}</if>
12、Vue Router 传参与接参
发件人:this.$router(负责跳转)
-
角色:全局路由实例,用于执行导航操作。
-
动作:通过
push将数据拼接到 URL 路径中。 -
代码示例:
1this.$router.push({ 2 path: '/business/statementItem/index/' + statementId 3}) -
结果:URL 变为
/.../index/10086,数据作为路径参数传递。
收件人:this.$route(负责接收)
-
角色:当前激活的路由信息对象,包含 URL、参数等状态。
-
动作:从
params中提取路径参数。 -
代码示例:
1data() { 2 return { 3 // 安全获取参数,避免 params 为空时报错 4 statementId: this.$route.params && this.$route.params.statementId 5 } 6}
13、数据库分析
-
所见即所得,页面上能看到的内容都需要添加字段。
-
是否存在关联表,需要存储关联表关键字段(id)。
-
考虑是否存在隐藏字段。
-
考虑是否存在小数位。该小数位应该使用什么表示(以金钱为主。decimal)
-
是否存在一些固定的值。考虑是否使用数据字典。
14、@JsonFormat 和 @DateTimeFormat
| 特性 | @JsonFormat | @DateTimeFormat |
|---|---|---|
| 作用方向 | 出参 (后端 -> 前端) 注:JSON入参也能管 | 入参 (前端 -> 后端) 专管表单/URL参数 |
| 所属框架 | Jackson (JSON 处理库) | Spring MVC (Web 数据绑定) |
| 核心功能 | 将 Java Date 对象序列化为 JSON 字符串时的格式控制 | 将 HTTP 请求中的日期字符串解析为 Java Date 对象时的格式控制 |
| 典型场景 | Controller 返回 JSON 数据给前端展示 | 接收 GET/POST 请求中的日期参数 |
| 时区支持 | 支持 (timezone 属性) |
不支持 |
- @JsonFormat:"管出门" —— 后端返回数据时使用(同时也负责 JSON 格式的进门)。
- @DateTimeFormat:"管进门" —— 前端提交表单或 URL 传参时使用。
15、BaseEntity 和 BaseController封装了哪些字段和方法?
(1)BaseEntity —— 实体层通用字段
所有业务实体类(如 SysUser, SysDept 等)通常继承此类,它封装了数据库表中几乎每个表都有的“审计字段”和“辅助字段”:
- searchValue:搜索值,用于前端模糊查询时传递关键词。
- createBy:创建者,记录数据创建人用户名。
- createTime:创建时间,记录数据首次入库时间。
- updateBy:更新者,记录最后修改人用户名。
- updateTime:更新时间,记录数据最后一次被修改的时间。
- remark:备注,用于存储额外说明信息。
- params:请求参数映射,用于接收前端传来的非实体字段参数(如分页、排序等),常配合 MyBatis 动态 SQL 使用。
这些字段通过 MyBatis-Plus 或自定义拦截器实现自动填充(如 @TableField(fill = FieldFill.INSERT)),无需手动赋值。
(2)BaseController —— 控制层通用方法
所有 Controller 类继承此类,它提供了 Web 层常用的工具方法,简化接口开发:
分页相关
- startPage():开启分页,自动从请求中提取页码和每页条数。
- startOrderBy():设置排序规则,支持前端传入排序字段。
- clearPage():清理分页线程变量,防止内存泄漏。
返回结果封装
- success() / error():返回标准成功/失败响应。
- toAjax(int rows):根据数据库操作影响行数自动返回成功或失败。
- getDataTable(List<?> list):将查询结果封装为带总数和列表的分页响应对象(
TableDataInfo)。
用户与权限获取
- getUsername():获取当前登录用户名。
- getUserId():获取当前登录用户 ID。
- getLoginUser():获取完整登录用户对象(含角色、权限等)。
其他实用方法
- initBinder():注册日期格式转换器,自动将前端字符串转为 Date 对象(替代部分
@DateTimeFormat场景)。 - getIpAddr():获取客户端 IP 地址。
- getBrowser():获取浏览器类型。
浙公网安备 33010602011771号