欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 时评 > 二刷苍穹外卖 day02

二刷苍穹外卖 day02

2025/6/13 5:38:35 来源:https://blog.csdn.net/qq_34832548/article/details/148595317  浏览:    关键词:二刷苍穹外卖 day02

新增员工

DTO

将前端传递的参数列表通过对应的实体类接收
当前端提交的数据和实体类中对应的属性差别较大时,使用DTO来封装数据
![[Pasted image 20250611183830.png]]

@Data
public class EmployeeDTO implements Serializable {private Long id;private String username;private String name;private String phone;private String sex;private String idNumber;}

记得要implements Serializable,实现接口后,对象就具备了可序列化的能力

controller

/**  * 新增员工  */  
@PostMapping  
@ApiOperation("新增员工")  
public Result save(@RequestBody EmployeeDTO employeeDTO)  
{  log.info("新增员工:{}",employeeDTO);  employeeService.save(employeeDTO);  return Result.success();  
}

Result定义了后端的返回结果格式

Result

package com.sky.result;  import lombok.Data;  import java.io.Serializable;  /**  * 后端统一返回结果  * @param <T>  */  
@Data  
public class Result<T> implements Serializable {  private Integer code; //编码:1成功,0和其它数字为失败  private String msg; //错误信息  private T data; //数据  public static <T> Result<T> success() {  Result<T> result = new Result<T>();  result.code = 1;  return result;  }  public static <T> Result<T> success(T object) {  Result<T> result = new Result<T>();  result.data = object;  result.code = 1;  return result;  }  public static <T> Result<T> error(String msg) {  Result result = new Result();  result.msg = msg;  result.code = 0;  return result;  }  }

Result是一个通用的后端统一返回结果封装类,用于向前端返回统一格式的响应数据

泛型参数表示返回的数据类型,可以是任何对象
属性:
code 相应状态码,1表示成功,0或其他表示失败
msg:描述错误信息,成功则为空
data:封装返回给前端的具体数据,类型为泛型T

方法:
success()

只返回code=1
success(T object):静态方法,返回包含具体数据的成功响应(code=1,data=object)
error(String msg):返回错误信息和失败状态码

public static <T> Result<T>
是一个Java泛型方法的声明,前一个表明了泛型方法的类型参数声明部分
Result表示该方法返回一个Result类型的对象
static表示这是一个类方法,可通过类名调用

@Service

@Override  
public void save(EmployeeDTO employeeDTO) {  Employee employee = new Employee();  BeanUtils.copyProperties(employeeDTO,employee);  employee.setStatus(StatusConstant.ENABLE);  employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes());  employee.setCreateTime(LocalDateTime.now());  employee.setUpdateTime(LocalDateTime.now());  employee.setCreateUser(10L);  employee.setCreateUser(10L);employeeMapper.insert(employee);  
}

注意一下BeanUtils.copyProperties()方法,以后用的很多
用于将一个 JavaBean 对象(源对象)的属性值复制到另一个 JavaBean 对象(目标对象)中。这里它将 EmployeeDTO 对象 employeeDTO 的属性值复制到 Employee 对象 employee 中。通常,源对象和目标对象需有相同或兼容的属性名及类型,这样才能正确复制属性值,比如 EmployeeDTO 中的 name 属性若与 Employee 中的 name 属性类型匹配,就会将 employeeDTOname 值复制到 employeename 属性中 。

mapper

@Insert("insert into employee (name, username, password, phone, sex, id_number, create_time, update_time, create_user, update_user,status) " +  "values " +  "(#{name},#{username},#{password},#{phone},#{sex},#{idNumber},#{createTime},#{updateTime},#{createUser},#{updateUser},#{status})")  
void insert(Employee employee);

上面的是的属性是数据库属性,下面的是后端
因为在application.yml中已经开启了驼峰命名,所以二者可以对应

mybatis:configuration:#开启驼峰命名map-underscore-to-camel-case: true

问题一 重名

使用全局异常处理器来解决问题

@ExceptionHandler
public Result exceptionHandler(SQLIntegrityConstraintViolationException ex)  
{  //Duplicate entry 'zhangsan' for key 'employee.idx_username'String message = ex.getMessage();  if(message.contains("Duplicate entry"))  {  String[] split = message.split(" ");  String username=split[2];  String msg=username+MessageConstant.ALREADY_EXISTS;  return Result.error(msg);  }  else  {  return Result.error(MessageConstant.UNKNOWN_ERROR);  }  
}

@ExceptionHandler 是一个注解,该方法用于处理特定类型的异常。比如在上述代码中,标记的方法 exceptionHandler 专门处理 SQLIntegrityConstraintViolationException 类型的异常。当程序执行过程中抛出此类型异常时,就会执行被 @ExceptionHandler 标记的方法,根据异常信息进行相应处理,比如提取异常信息中的用户名,返回特定的错误提示给前端,告知用户用户名已存在等。

![[Pasted image 20250611202502.png]]

问题二:

新增员工时,我们需要获得当前登录员工的id
使用JWT 令牌并相应给前端

当前端指令请求时,它所携带的JWT令牌可以解析出对应员工的登录id;

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  //判断当前拦截到的是Controller的方法还是其他资源  if (!(handler instanceof HandlerMethod)) {  //当前拦截到的不是动态方法,直接放行  return true;  }  //1、从请求头中获取令牌  String token = request.getHeader(jwtProperties.getAdminTokenName());  //2、校验令牌  try {  log.info("jwt校验:{}", token);  Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);  Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());  log.info("当前员工id:", empId);  //3、通过,放行  return true;  } catch (Exception ex) {  //4、不通过,响应401状态码  response.setStatus(401);  return false;  }  
}

在拦截器中已经解析出了员工的id

Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);  Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());  

第一行调用JwtUtil工具类的parseJWT方法,使用配置文件中的管理员密钥对token令牌进行解析,将解析后的对象(包括id)存储在claim对象中
通过JwtClaimsContant.EMP_ID这个常量作为键,获取对应的用户ID,得到字符串之后再转换为Long类型

ThreadLocal

ThreadLocal是为每一个线程提供的一个单独的存储空间,具有线程隔离的效果,其他线程无法访问

常用方法

public void set(T value)设置
public T get()获取
public void remove()

解决流程

**![[Pasted image 20250611203416.png]]**

在初始工程中已经封装了对应类

public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();  
public static void  setCurrentId(Long id)  
{  threadLocal.set(id);  
}  
public static Long getCurrentId() {  return threadLocal.get();  
}  
public static void removeCurrentId(){  threadLocal.remove();  
}

在一个多线程的 Web 应用中,不同的请求线程可能需要各自独立的用户 ID 标识。可以在某个请求线程开始处理业务时,调用setCurrentId方法设置该线程的用户 ID,在处理业务过程中通过getCurrentId方法随时获取该线程的用户 ID,处理完成后调用removeCurrentId方法清理资源。

在拦截器中存储empid

 try {//.................Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());log.info("当前员工id:", empId);/将用户id存储到ThreadLocalBaseContext.setCurrentId(empId);//3、通过,放行return true;

在service中获取局部变量的值

    public void save(EmployeeDTO employeeDTO) {//.............................//设置当前记录创建人id和修改人idemployee.setCreateUser(BaseContext.getCurrentId());employee.setUpdateUser(BaseContext.getCurrentId());employeeMapper.insert(employee);}

员工分页查询

注意:
请求参数类型为Query,在路径后拼接/admin/employee/page?name=zhangsan

返回数据中records数组使用Employee实体类对属性进行了封装

Controller

  @GetMapping("/page")@ApiOperation("员工分页查询")public Result<PageResult> page(EmployeePageQueryDTO employeePageQueryDTO){log.info("员工分页查询,参数为:{}", employeePageQueryDTO);PageResult pageResult = employeeService.pageQuery(employeePageQueryDTO);//后续定义return Result.success(pageResult);}

方法返回值类型为Result<PageResult>Result是自定义的用于封装响应结果的类,PageResult则可能是包含分页数据的类。

PageResult是一个专用的数据载体
long total 总记录数
List records 当前页实际查询到的数据集合

Result
是泛型的具体类型实例,data字段的类型是PageResult

service

@Override  
public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {  PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());  Page<Employee>page=employeeMapper.pageQuery(employeePageQueryDTO);  long total=page.getTotal();  List<Employee> records=page.getResult();  return new PageResult(total,records);  
}

mappe层

<select id="pageQuery" resultType="com.sky.entity.Employee">  select * from employee    <where>  <if test="name !=null and name !=''">  and name like concat('%',${name},'%');        </if>  </where>  order by create_time desc  
</select>

查询名字中间包含name的,不需要limit,分页由pagehelper插件进行处理

操作时间字段问题

在WebMvcConfiguration中扩展SpringMVC消息转换器
Spring Boot 默认使用 MappingJackson2HttpMessageConverter 处理 JSON 序列化 / 反序列化,但 Jackson 对日期类型的默认序列化规则会导致格式异常

  • 若实体类中是 DateLocalDateTime 等日期类型,Jackson 默认可能将其序列化为 时间戳拆分的数组(如 [年, 月, 日, 时, 分, 秒],与前端期望的格式(如 2022524112024)不匹配。
  • 将自定义的 MappingJackson2HttpMessageConverter 放到转换器列表的第一位,确保优先使用(覆盖默认的 Jackson 转换器)。
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {  log.info("扩展消息转换器...");  //创建一个消息转换器对象  MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();  //需要为消息转换器设置一个对象转换器,对象转换器可以将Java对象序列化为json数据  converter.setObjectMapper(new JacksonObjectMapper());  //将自己的消息转化器加入容器内  converters.add(0, converter);  
}

启用禁用员工账号

Controller

@PostMapping("/status/{status}")  
@ApiOperation("启用禁用员工账号")  
public Result startOrStop(@PathVariable Integer status,@RequestParam("id") Long id  // 显式声明id来自请求参数
)  
{  log.info("启用禁用员工账号");  employeeService.startOrStop(status,id);  return Result.success();  
}

注意讲义中当前方法中 Long id 参数未显式声明参数来源。在 Spring MVC 中,未标注@PathVariable/@RequestBody等注解的参数默认会尝试从请求参数(@RequestParam)获取,但如果请求中未传递id参数,会直接抛出MissingServletRequestParameterException(400 错误)。建议修改

编辑员工

回显员工信息

controller

/**  * 根据id查询员工信息  */  
@GetMapping("/{id}")  
@ApiOperation("根据id查询员工信息")  
public Result<Employee> getById(@PathVariable Long id) {  Employee employee = employeeService.getById(id);  return Result.success(employee);  
}

service层

@Override  
public Employee getById(Long id) {  Employee employee = employeeMapper.getById(id);  employee.setPassword("****");  return employee;  
}

密码主动修改很细节

修改信息

service

注意这里不能用builder(),因为builder只能是创建对象的时候使用

public void update(EmployeeDTO employeeDTO) {  Employee employee = new Employee();  BeanUtils.copyProperties(employeeDTO, employee);  employee.setUpdateTime(LocalDateTime.now());  employee.setUpdateUser(BaseContext.getCurrentId());  employeeMapper.update(employee);  }

在上一个功能中已经实现了可泛用的mapper,这里不需要再写了

导入代码

CategoryMapper.xml文件中注意到一个点
select 语句中使用where if test时需要"and",表示&
在update语句中set里不需要写表示| ,有就满足

<select id="pageQuery" resultType="com.sky.entity.Category">  select * from category    <where>  <if test="name != null and name != ''">  and name like concat('%',#{name},'%')        </if>  <if test="type != null">  and type = #{type}        </if>  </where>  order by sort asc , create_time desc</select>  <update id="update" parameterType="Category">  update category    <set>  <if test="type != null">  type = #{type},        </if>  <if test="name != null">  name = #{name},        </if>  <if test="sort != null">  sort = #{sort},        </if>  <if test="status != null">  status = #{status},        </if>  <if test="updateTime != null">  update_time = #{updateTime},        </if>  <if test="updateUser != null">  update_user = #{updateUser}        </if>  </set>  where id = #{id}</update>

总结

1.DTO:

用来接受前端提交的数据以及不同层之间传递
当与实体类差别较大时使用

2.Result

通用后端统一返回结果封装类,向前端返回统一格式的响应数据

3.public static Result

是一个Java泛型方法的声明,前一个表明了泛型方法的类型参数声明部分
Result表示该方法返回一个Result类型的对象
static表示这是一个类方法,可通过类名调用

4.BeanUtils.copyProperties()

一般将DTO赋值给对应的对象

5.重名

ExceptionHandler注解,当程序抛出对应异常时,会执行被@ExceptionHandler标记的方法

6.JWT令牌

通过JwTUtil.parseJWT可以解析令牌

7.ThreadLocal

仅本线程可用存储空间
set get remove

8.分页查询

PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());
第一个参数employeePageQueryDTO.getPage()获取到的是要查询的页码,第二个参数employeePageQueryDTO.getPageSize()获取到的是每页显示的记录数

方法返回值类型为Result
PageResult是专门的数据载体,包括List records,即当前页查询到的数据集合
records

9.操作时间字段

更换自定义的转换器覆盖原有

10.启用禁用员工账号

未表明数据来源会默认从请求参数(@RequestParam)获取,即带有查询参数(Query Parameters)的 URL 链接?xx=xx

11.xml文件 if条件

在select时要保证全满足,即&,所以if 后要加"and"
在update时满足一个修改一个,即 |,所以If 后不用加

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词