苍穹外卖项目说明

苍穹外卖项目说明

1 资料

瑞吉 https://pan.baidu.com/s/1bxEy2bHiCYQtouifUppsTA&pwd=1234

苍穹 https://pan.baidu.com/s/1MNDzXyVlr3mtmLgBjcPJVw&pwd=6633

  • 本地位置
C:\Users\20881\IdeaProjects\CangQiongWaiMai\sky-take-out

2 前端

"D:\BaiduNetdiskDownload\java\CangQiongWaiMai\nginx-1.20.2\nginx.exe"

// 端口号 : 80

3 流程

4 DTO

  • 前端提交的数据和实体对象差异较大时 使用dto

5 常见问题和解决方法

操作逻辑异常

业务逻辑不允许的操作

  • 用户删除分类时, 如果分类下有菜品则不能删除, 使用异常来处理 , 对每个异常都要在全局异常处理器中设置对应的 handler吗?

  • 使用继承思想,抛出子异常, 在全局异常处理器中 用父异常接收并处理;

  • 父异常

    /**  
     * 业务异常  
     */  
    public class BaseException extends RuntimeException {  
    
        public BaseException() {  
        }  
    
        public BaseException(String msg) {  
            super(msg);  
        }  
    
    }
    
  • 子异常

    public class DeletionNotAllowedException extends BaseException {  
    
        public DeletionNotAllowedException(String msg) {  
            super(msg);  
        }  
    
    }
    
  • 全局异常处理

    @RestControllerAdvice  
    @Slf4j  
    public class GlobalExceptionHandler {  
    
        /**  
         * 捕获业务异常  
         * @param ex  
         * @return  
         */    
        @ExceptionHandler  
        public Result exceptionHandler(BaseException ex){  
            log.error("异常信息:{}", ex.getMessage());  
            return Result.error(ex.getMessage());  
        }
    

插入异常

表中unique字段如果重复就会有异常

```

2025-01-08 16:47:56.236 ERROR 4276 — [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DuplicateKeyException: ``` 用 GlobalExceptionHandler 接受异常, 返回 Result ;

拦截器token传递

使用 ThreadLocal 对线程设置变量;

日期显示问题

  • 日期显示问题:

公共字段

  • 这些字段自动填充即可, 如果在每个函数中都手动设置过于冗余且不易维护

  • 这些aop一般用在mapper层

  • 使用反射

    • 编写Log类
    • 实现aop方法
    • 添加log
  • 编写log类

      package com.sky.annotation;  
    
    import com.sky.enumeration.OperationType;  
    
    import java.lang.annotation.ElementType;  
    import java.lang.annotation.Retention;  
    import java.lang.annotation.RetentionPolicy;  
    import java.lang.annotation.Target;  
    
    @Target(ElementType.METHOD)  
    @Retention(RetentionPolicy.RUNTIME)  
    // @interface 表示注解  
    public @interface AutoFill {  
        //UPDATE INSERT  
        // 注解中会出现  @AutoFill(value = ) 等于的值为 OperationType中指定的  
        OperationType value();  
    }
    
  • 实现aop方法


@Slf4j  
@Component  
@Aspect  
public class AutoFillAspect {  
    /**  
     * 切入点  
     */  
    @Pointcut("@annotation(com.sky.annotation.AutoFill)")  
    public void pt(){};  


    @Before("@annotation(autoFill)" )  
    public void AutoFill(JoinPoint joinPoint,AutoFill autoFill) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {  
        log.info("开始执行公共字段填充");  
        //拦截mapper方法 update操作 只用 updateTime updateUser 赋值即可  


        // 获取方法的参数实体对象  
        OperationType operationType = autoFill.value();  
        log.info("获取到传入value:{}",operationType);  

        Object[] args = joinPoint.getArgs();  
        if(args.length==0||args==null){  
            return;  
        }  
        Object obj = args[0];  
        //公共属性赋值  
        LocalDateTime now = LocalDateTime.now();  
        Long currentId = BaseContext.getCurrentId();  
        if(operationType==OperationType.INSERT){  
            //四个公共字段赋值  
            Method setCreateTime = obj.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);  
            Method setCreateUser = obj.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);  
            Method setUpdateTime = obj.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);  
            Method setUpdateUser = obj.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);  

            setCreateTime.invoke(obj,now);  
            setCreateUser.invoke(obj,currentId);  
            setUpdateTime.invoke(obj,now);  
            setUpdateUser.invoke(obj,currentId);  
        }else{  
            // 主需要传 update字段  
            Method setUpdateTime = obj.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);  
            Method setUpdateUser = obj.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);  
            setUpdateTime.invoke(obj,now);  
            setUpdateUser.invoke(obj,currentId);  
        }  

    }  
}

两个表操作

  • 开启事务, 在服务实现类方法上添加 @Transactional

先删除后插入

  • 更新菜品口味, 口味可能删除了, 因此直接 update业务错误, 要先删后插
  • 多对多的关系表可以先删除后插入

少量数据

  • 店铺营业状态, 存表不合适
  • 使用 redis

冗余字段

只存 id 不存其他的字段, 回显时还要进行多表查询, 因此为了避免进行多表查询, 设置冗余字段.

sql语句返回

  • 可能是 null 可以额外加个判断

逻辑删除

  • 例如订单表, 用户删除不是真正的删除
  • 可以在订单表中添加字段表示是否被删除, 在逻辑上增加判断是否已经被删除

规范

配置

application.yml

sky:  
  jwt:  
    # 设置jwt签名加密时使用的秘钥  
    admin-secret-key: itcast  
    # 设置jwt过期时间  
    #TODO 设置正确的时间  
    admin-ttl: 720000000  
    # 设置前端传递过来的令牌名称  
    admin-token-name: token  


  alioss:  
    access-key-id: ${sky.alioss.access-key-id}  
    access-key-secret: ${sky.alioss.access-key-secret}  
    endpoint: ${sky.alioss.endpoint}  
    bucket-name: ${sky.alioss.bucket-name}

application-dev.yml

sky:  
  datasource:  
    driver-class-name: com.mysql.cj.jdbc.Driver  
    host: localhost  
    port: 3306  
    database: sky_take_out  
    username: root  
    password: 1234  
  alioss:  
    access-key-id:   
    access-key-secret:   
    endpoint: oss-cn-beijing.aliyuncs.com  
    bucket-name: hzl-demo

结构

common

  • java代码中的字符串类型全部提取为常量->constant
  • context 使用 ThreadLocal 一般使用类包装一下
  • 异常通过设置父类和子类和全局异常处理器分别处理
  • properties 自动加载
  • result
  • utils 第三方的集成工具: aliyunOss, Jwt

pojo

  • dto 是接收从 web 传过来的参数的实体
  • entity 是实际存在的实体
  • vo 是返回前端的实体

service

  • annotation 自定义注解
  • aspect 自定义aop切片
  • config WebMvcConfiguration.java 设置拦截器消息转换器
  • handler 全局异常处理器
  • interceptor 登录验证拦截器

名字

  • /admin/employee 路径-> controller.admin.employeeController
  • /admin/category/page ->controller.admin.categoryController
  • /admin/common/upload ->controller.admin.commonController