第二天

  • 管理端发来的请求 统一用/admin 用户端发来的请求 统一用/user

  • 如何动态的获取当前登录的id来进行设置 添加员工有一个属性值是当前用户的id 就是当前是哪个管理员再添加员工 如果想要获取这个id 就要回到jwt令牌校验那里`//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);`
    

在jwt令牌校验的时候 已经获取到了empId了 但是问题就是如何将这个empId传递给service 因为具体的逻辑是在service中进行的开发 因此如果要完成这个功能 就要用到ThreadLocal ThreadLocal不是一个线程 而是线程的局部变量 ThreadLocal为每一个线程单独提供了一份存储空间 具有线程隔离的操作 只有在线程内才能获取到对应的值 线程外则不能访问 由于代码已经实现了ThreadLocal的实体类 在BaseContext中 因此在jwt校验的时候使用BaseContext.setCurrentId(empId)存储 然后在service中通过BaseContext.getCurrentId()拿到即可动态的设置值

  • 分页查询实现 要实现分页查询 就需要封装前端传来的对象PageDTO 其中有必须参数 页码和每页的大小 不必须参数姓名 然后前端返回对象用PageResult进行数据封装 其中有总记录数和具体员工对象值 即返回Result<PageResult>实现就是先要定义方法 然后在mapper.xml中实现动态sql查询 使用where标签 然后if判断如果name不为空或者空串 就加上name 并且用模糊查询like实现 然后再对查询的结果按照创建时间降序排序
    代码实现为
    <select id="pageQuery" resultType="com.sky.entity.Employee">
        select * from employee
        <where>
            <if test="name != null and name != ''">
                name like concat('%',#{name},'%')
            </if>
        </where>
        order by creat_time desc
    </select>

注意查完之后在service中是page对象返回 而page对象中有一堆属性 我们只要其中的记录数跟数据 因此需要再进行封装long total = page.getTotal(); List<Employee> records = page.getResult(); return new PageResult(total, records);

注意:返回的时间问题需要进一步的优化 其中一个就是在属性上标注@JsonFormat("yyyy-MM-dd HH:mm:ss")
第二种就是在WebMVCConfiguration中扩展springMVC消息转换器 统一对日期类型进行格式化处理

公共字段自动填充
前面在插入员工 修改员工 添加类别 修改类别 包括之后的添加菜品 修改菜品 都会进行修改时间和修改的id以及创建时间 创建的id 这些每一次进行修改和插入都要进行设置
比如这四句代码

        employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());

        employee.setCreateUser(BaseContext.getCurrentId());
        employee.setUpdateUser(BaseContext.getCurrentId());

因此 就可以设计一个功能 对这种公共字段来进行自动填充
可以设计aop切面来对切入insertupdate的方法 只要有这两种方法 就进行公共字段的填充 插入方法填充创建和修改四个公共字段 更改需要填充两个 如何监控这两种方法 可以再设计一个注解@AutoFill然后在插入和修改的方法上标注这个注解 切面方法就可以切入了
注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
    //数据库操作类型:UPDATE INSERT
    OperationType value();
}

上面代码的OperationType value();这个语句是规定注解中要有一个属性的值 就是OperationType枚举变量的值 然后就可以在修改和插入的方法上标注 @AutoFill(OperationType.UPDATE) void update(Employee employee);
因此切面就可以监控这个mapper类下的标注有@AutoFill的注解的方法 然后进行公共字段填充
因此aspect切面方法中的切入点就可以这样规定 @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)") public void autoFillPointCut(){}只有同时满足这两个条件时 开始切入 条件是前置切入 因为要在mapper方法执行之前填入公共字段的值 然后再执行修改和插入方法
首先在方法中要获取到被拦截方法上的操作类型 是修改还是插入

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);
        OperationType value = autoFill.value();

先拿到方法签名 然后根据签名拿到方法getMethod 然后再拿到注解getAnnotation中的Autofill类 AutoFill类中的值就是操作类型 是insert还是update
然后获取被拦截方法的参数 就是实体对象 employee或者菜品或者类别

        Object[] args = joinPoint.getArgs();
        if(args == null || args.length == 0){
            return;
        }
        Object entity = args[0];

先拿到所有参数 因为插入和修改的方法中的第一个参数都是实体对象 因此就是第一个参数就是实体类 比如void update(Employee employee);只有一个参数 就是实体类 即使有多个参数的话 默认把实体类放在前面
再定义准备赋值的数据
LocalDateTime now = LocalDateTime.now(); Long currentId = BaseContext.getCurrentId();
最后就是根据不同的操作类型 根据反射来进行赋值

if(value == OperationType.INSERT){
            //为四个属性赋值
            try {
                Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

                //通过反射为对象属性赋值
                setCreateTime.invoke(entity, now);
                setUpdateTime.invoke(entity, now);
                setCreateUser.invoke(entity, currentId);
                setUpdateUser.invoke(entity, currentId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }else if(value == OperationType.UPDATE){
            //为两个属性赋值
            try {
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

                //通过反射为对象属性赋值
                setUpdateTime.invoke(entity, now);
                setUpdateUser.invoke(entity, currentId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

如果是插入 就需要为四个属性赋值 如果是修改 就为两个属性赋值 先从实体类中getClass得到该对象 然后通过getDeclaredMethod得到方法 AutoFillConstant.SET_UPDATE_TIME'是方法名 用常量来表示了 本质的方法名是setCreateTime 去找到这个方法 然后再通过LocalDateTime.class来规定这个方法中的参数类型 如此一来 就设置好了setUpdateTime方法 然后通过反射invoke`来真正的调用这个对象的set方法来赋值 走到这里 就赋完值了 大体流程就是通过aop来监控标注了autofill注解的mapper方法 然后对插入和修改切入 先拿到方法签名 然后拿到注解中的属性就是操作类型 再拿到实体参数 再定义要填充的公共字段的值 然后根据这个实体类去拿到类 拿到方法 规定方法名 规定方法参数 去设置这个方法 最后调用invoke来真正的为这个对象赋值

posted @ 2025-05-30 23:20  big4mart  阅读(25)  评论(0)    收藏  举报