一、构建聚合工程(Java-Maven)

        foodie-dev为父工程,其他为子模块。foodie-dev-api的pom.xml引入依赖-service,-service引入-mapper,-mapper引入-pojo,-pojo引入-common。

二、使用PDMan进行数据库建模,可以生成表图和表创建或版本更新等功能。

三、整合springboot

1.pom.xml:

这步难在版本,springboot,jdk,MySQL等等,有问题让AI去寻找协调的依赖即可

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.0</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <java.version>17</java.version>
    </properties>

    <dependencies>
        <!-- Spring Boot 基础 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- MyBatis-Plus(兼容 Spring Boot 3.x) -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.5</version>
            <exclusions>
                <exclusion>
                    <groupId>org.mybatis</groupId>
                    <artifactId>mybatis-spring</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- MySQL 8+ 驱动 -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>8.2.0</version>
        </dependency>

        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

2.application.yml:

# Web 访问端口号
server:
  port: 8088
  tomcat:
    uri-encoding: UTF-8
    max-http-header-size: 80KB

############################################################
#
# 配置数据源信息
#
############################################################
spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.64.128:3306/foodie-shop-dev?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&autoReconnect=true&allowPublicKeyRetrieval=true
    username: root
    password: root
    hikari:
      connection-timeout: 30000      # 连接超时时间(毫秒)
      minimum-idle: 5                # 最小连接数
      maximum-pool-size: 20          # 最大连接数
      auto-commit: true              # 自动提交
      idle-timeout: 600000           # 空闲连接最大存活时间(毫秒)
      pool-name: DateSourceHikariCP  # 连接池名字
      max-lifetime: 1800000          # 连接的最大生命周期(毫秒)
      connection-test-query: SELECT 1 # 连接测试查询

############################################################
#
# MyBatis 配置
#
############################################################
mybatis:
  type-aliases-package: com.imooc.pojo         # 所有 POJO 类所在包路径
  mapper-locations: classpath:mapper/*.xml     # Mapper XML 文件路径

############################################################
#
# MyBatis-Plus 配置
#
############################################################
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true    # 开启驼峰命名转换
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  # 打印 SQL 日志
  global-config:
    db-config:
      id-type: auto                       # 主键自增
      logic-delete-field: deleted         # 逻辑删除字段名
      logic-delete-value: 1               # 逻辑删除值
      logic-not-delete-value: 0           # 逻辑未删除值

        mybatis-plus在mybatis上只增强不修改,所以我选择了MP,因此springboot用3,jdk17,MySQL8......此外,不论是mybatis还是MP都有相应的配置一键生成工具(另一个项目),可以自行去寻找。不过我直接用通义灵码的智能体插件,告诉它需要生成的位置,实体类的字段,需要生成的文件,也能快速生成一套基本的增删改查类(pojo,mapper,service,serviceImpl)。

        

引入lombok依赖,不用写实体类的get,set方法,只需要注解,很方便。

        数据源url配置根据自己MySQL下载位置修改,一般是localhost,我是下载到了虚拟机Ubuntu中。数据库名(foodie-shop-dev)要与自己创建的一致。

3.启动类

package com.imooc;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = {"com.imooc", "org.n3r.idworker"})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

三、api接口

1.遵循restful编写风格

2.可以使用PostMan去调试接口,很简单,自行学习  

swgger

        没使用教学教学视频中较老的swagger2,因为版本不兼容,所以改成了更新的openapi,UI增强使用的knife4j(能有更好看的界面)。生成api文档更方便前端人员去调用接口去请求。(文中未说明的,依赖都是引入到父工程的pom中)

1.pom.xml:

<!-- springdoc-openapi (替代 Swagger2,兼容 Spring Boot 3.x) -->
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>2.3.0</version>
        </dependency>

        <!-- knife4j 增强 UI (支持 OpenAPI 3) -->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
            <version>4.4.0</version>
        </dependency>

2.Swagger.java

位置:foodie-dev\foodie-dev-api\src\main\java\com\imooc\config\Swagger.java

package com.imooc.config;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

// http://localhost:8088/swagger-ui/index.html
// http://localhost:8088/doc.html
@Configuration
public class Swagger {

    @Bean
    public OpenAPI customOpenAPI() {
        return new OpenAPI()
                .info(new Info()
                        .title("饕餮吃货 电商平台接口 api")
                        .version("1.0.1")
                        .description("饕餮吃货 电商平台接口 api 文档")
                        .contact(new Contact()
                                .name("imooc")
                                .email("123@imooc.com")
                                .url("http://www.imooc.com")));
    }
}

3.application.yml

############################################################
#
# Knife4j 增强配置
#
############################################################
springdoc:
  api-docs:
    enabled: true
    path: /v3/api-docs
  swagger-ui:
    enabled: true
    path: /swagger-ui.html
  group-configs:
    - group: 'default'
      paths-to-match: '/**'
      packages-to-scan: com.imooc.controller

# Knife4j 增强 UI 配置
knife4j:
  enable: true
  setting:
    language: zh_cn
    swagger-model-name: 实体类列表

四、跨域解决

foodie-dev\foodie-dev-api\src\main\java\com\imooc\config\CorsConfig.java

package com.imooc.config;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class CorsConfig {
    public CorsConfig(){
    }

    @Bean
    public CorsFilter corsFilter(){
        // 1. 添加CORS配置信息
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOrigin("http://localhost:8080");

        // 是否发送cookie信息
        config.setAllowCredentials(true);

        // 允许的请求方式
        config.addAllowedMethod("*");

        // 允许的header
        config.addAllowedHeader("*");

        // 2. 为URL添加映射路径
        UrlBasedCorsConfigurationSource corsSource = new UrlBasedCorsConfigurationSource();
        corsSource.registerCorsConfiguration("/**", config);

        return new CorsFilter(corsSource);
    }
}

五、基本功能实现

1.判断用户名是否存在

1.UserServiceImpl:

主要用于前端页面的用户名校验,用户填写密码前就能校验出结果。

@Autowired
    private UserMapper userMapper;

    /**
     * 判断用户名是否存在
     * @param username
     */
    @Override
    public boolean queryUsernameIsExist(String username) {
        if (StringUtils.isBlank(username)) {
            return false;
        }

        LambdaQueryWrapper<Users> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Users::getUsername, username);

        Users user = this.baseMapper.selectOne(wrapper);
        return user != null;
    }

2.PassportController

**
     * 校验用户名是否存在
     *
     * @param username 用户名
     * @return 200-OK(用户名可用), 500-用户名已存在或为空
     */
    @Operation(summary = "用户名是否存在", description = "用户名是否存在")
    @GetMapping()
    public IMOCCJSONResult checkUsername(
            @Parameter(description = "用户名", required = true) @RequestParam String username) {
        // 1. 校验用户名是否为空
        if (StringUtils.isBlank(username)) {
            return IMOCCJSONResult.errorMsg("用户名不能为空");
        }

        // 2. 校验用户名是否存在
        boolean isExist = userService.queryUsernameIsExist(username);
        if (isExist) {
            return IMOCCJSONResult.errorMsg("用户名已存在,请换一个试试");
        }

        // 3. 用户名可用
        return IMOCCJSONResult.ok("用户名可用");
    }

2.注册功能

1.User.java

package com.imooc.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.io.Serializable;
import java.util.Date;

/**
 * 用户实体类
 */
@Data
@TableName("users")
public class Users implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 主键 ID
     */
    @TableId(value = "id", type = IdType.ASSIGN_UUID)
    private String id;

    /**
     * 用户名
     */
    private String username;

    /**
     * 密码
     */
    private String password;

    /**
     * 昵称
     */
    private String nickname;

    /**
     * 真实姓名
     */
    private String realname;

    /**
     * 头像
     */
    private String face;

    /**
     * 手机号
     */
    private String mobile;

    /**
     * 邮箱
     */
    private String email;

    /**
     * 性别 (1:男 0:女 2:保密)
     */
    private Integer sex;

    /**
     * 生日
     */
    private Date birthday;

    /**
     * 创建时间
     */
    private Date createdTime;

    /**
     * 更新时间
     */
    private Date updatedTime;
}

2.UserServiceImpl

/**
 * 用户服务实现类
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, Users> implements UserService {

    @Autowired
    public Sid sid;

    private static final String USER_FACE = "https://img.zcool.cn/community/01a2f05e8d07e45f4ab05c1f60b4df.jpg";

    @Autowired
    private UserMapper userMapper;
  
    /**
     * 创建用户
     * @param userBo
     */
    @Override
    public Users createUser(UserBo userBo) {
        String userId = sid.nextShort();
        
        Users user = new Users();

        user.setId(userId);
        user.setUsername(userBo.getUsername());
        try {
            user.setPassword(MD5Utils.getMD5Str(userBo.getPassword()));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        user.setNickname(userBo.getUsername());
        user.setFace(USER_FACE);
        user.setBirthday(DateUtil.stringToDate("1900-01-01"));
        user.setSex(Sex.secret.type);
        user.setCreatedTime(new Date());
        user.setUpdatedTime(new Date());

        userMapper.insert(user);
        return user;
    }

        这段涉及很多其他的知识点:

1.createUser(UserBo userBo),用户注册时,输入包含用户名,密码,确认密码,用于前端传入,而后端不需要对确认密码进行其他操作,因此自定义UserBo用于参数传入,且不用写过长的参数,保持优雅。

package com.imooc.pojo.bo;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

@Schema(description = "用户对象 BO")
@Data
public class UserBo {
    @Schema(description = "用户名", example = "imooc", required = true)
    private String username;

    @Schema(description = "密码", example = "123456", required = true)
    private String password;

    @Schema(description = "确认密码", example = "123456", required = false)
    private String confirmPassword;
}

2.sid.nextShort(),使用雪花算法,来生成全局唯一id,支持分布式

3.MD5Utils.getMD5Str(userBo.getPassword()),用到了MD5加密,我们把设置的密码加密后存到数据库中显示,但实际用户登录还是用ta原先设置好的那串。

<!-- Apache 工具类 -->
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.11</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
        </dependency>

4.DateUtil.stringToDate("1900-01-01"),将字符串转换成日期。

5.user.setSex(Sex.secret.type),用枚举类设置性别,一目了然,而非数字。

        

3.PassportController

package com.imooc.controller;

@Tag(name = "注册登录", description = "用于用户注册登录相关接口")
@RestController
@RequestMapping("/passport")
public class PassportController {

    @Autowired
    private UserService userService;

    /**
     * 用户注册
     *
     * @param userBo 用户名
     * @return 200-OK(用户名可用), 500-用户名已存在或为空
     */
    @Operation(summary = "用户注册", description = "用户注册")
    @PostMapping("/regist")
    public IMOCCJSONResult regist(@RequestBody UserBo userBo,
                                  HttpServletRequest request,
                                  HttpServletResponse response) throws Exception {
        String username = userBo.getUsername();
        String password = userBo.getPassword();
        String confirmPassword = userBo.getConfirmPassword();
        // 0. 判断用户和密码是否为空
        if (StringUtils.isBlank(username) ||
                StringUtils.isBlank(password) ||
                StringUtils.isBlank(confirmPassword)) {
            return IMOCCJSONResult.errorMsg("用户名或密码不能为空");
        }

        // 1. 校验用户名是否存在
        boolean isExist = userService.queryUsernameIsExist(username);
        if (isExist) {
            return IMOCCJSONResult.errorMsg("用户名已存在,请换一个试试");
        }

        // 2.密码长度不能少于 6 位
        if (password.length() < 6) {
            return IMOCCJSONResult.errorMsg("密码长度不能少于 6 位");
        }
        // 3.判断两次密码是否一致
        if (!password.equals(confirmPassword)) {
            return IMOCCJSONResult.errorMsg("两次密码不一致");
        }
        // 4. 实现注册
        Users userResult = userService.createUser(userBo);

        userResult = setNullProperty(userResult);

        CookieUtils.setCookie(request, response, "user",
                JsonUtils.objectToJson(userResult), true);
                
        return IMOCCJSONResult.ok();
    }

        这有一个自定义响应数据结构类IMOCCJSONResult

package com.imooc.utils;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;

/**
 * 自定义响应数据结构
 * 本类可提供给 H5/ios/安卓/公众号/小程序 使用
 * 前端接受此类数据 (jsonobject) 后,可自行根据业务去实现相关功能
 * 
 * 200: 表示成功
 * 500: 表示错误,错误信息在 msg 字段中
 * 501: bean 验证错误,不管多少个错误都以 map 形式返回
 * 502: 拦截器拦截到用户 token 出错
 * 555: 异常抛出信息
 * 556: 用户 qg 校验异常
 */
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class IMOCCJSONResult {
    
    /**
     * 响应状态码
     */
    private Integer status;
    
    /**
     * 响应消息
     */
    private String msg;
    
    /**
     * 响应数据
     */
    private Object data;
    
    /**
     * 响应时间戳
     */
    private Long timestamp;
    
    public IMOCCJSONResult() {
        this.timestamp = System.currentTimeMillis();
    }
    
    /**
     * 成功响应(不带数据)
     */
    public static IMOCCJSONResult build(Integer status, String msg) {
        IMOCCJSONResult result = new IMOCCJSONResult();
        result.setStatus(status);
        result.setMsg(msg);
        return result;
    }
    
    /**
     * 成功响应(带数据)
     */
    public static IMOCCJSONResult build(Integer status, String msg, Object data) {
        IMOCCJSONResult result = new IMOCCJSONResult();
        result.setStatus(status);
        result.setMsg(msg);
        result.setData(data);
        return result;
    }
    
    /**
     * 成功响应(200)
     */
    public static IMOCCJSONResult ok() {
        return build(200, "OK");
    }
    
    /**
     * 成功响应(200,带数据)
     */
    public static IMOCCJSONResult ok(Object data) {
        return build(200, "OK", data);
    }
    
    /**
     * 错误响应(500)
     */
    public static IMOCCJSONResult errorMsg(String msg) {
        return build(500, msg);
    }
    
    /**
     * 错误响应(500,带数据)
     */
    public static IMOCCJSONResult errorMsg(String msg, Object data) {
        return build(500, msg, data);
    }
}

3.登录功能

1.UserServiceImpl

/**
     * 检索用户名和密码是否匹配,用于登录
     * @param username
     * @param password
     */
    @Override
    public Users queryUserForLogin(String username, String password) {
        LambdaQueryWrapper<Users> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Users::getUsername, username);
        wrapper.eq(Users::getPassword, password);

        Users result = userMapper .selectOne(wrapper);
        return result;
    }

2.PassportController

/**
     * 用户登录
     *
     * @param userBo 用户输入
     */
    @Operation(summary = "用户登录", description = "用户登录")
    @PostMapping("/login")
    public IMOCCJSONResult login(@RequestBody UserBo userBo,
                                 HttpServletRequest request,
                                 HttpServletResponse response) throws Exception {
        String username = userBo.getUsername();
        String password = userBo.getPassword();

        // 0. 判断用户和密码是否为空
        if (StringUtils.isBlank(username) ||
                StringUtils.isBlank(password)) {
            return IMOCCJSONResult.errorMsg("用户名或密码不能为空");
        }

        // 1. 实现登录
        Users userResult = userService.queryUserForLogin(username, MD5Utils.getMD5Str(password));
        if (userResult == null) {
            return IMOCCJSONResult.errorMsg("用户名或密码不正确");
        }

        // 数据敏感信息隐藏
        userResult = setNullProperty(userResult);

        // 保存会话
        CookieUtils.setCookie(request, response, "user",
                JsonUtils.objectToJson(userResult), true);

        return IMOCCJSONResult.ok(userResult);
    }

    private Users setNullProperty(Users userResult) {
        userResult.setPassword(null);
        userResult.setMobile(null);
        userResult.setEmail(null);
        userResult.setCreatedTime(null);
        userResult.setUpdatedTime(null);
        return userResult;
    }

        注册和登录后都需要把敏感信息设置为空,然后设置cookie用于身份(令牌)识别。这部分涉及一个JsonUtils.objectToJson(userResult),在com/imooc/utils下。

        <!-- Jakarta Servlet API (兼容 Spring Boot 3.x) -->
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>6.0.0</version>
            <scope>provided</scope>
        </dependency>

4.登出

/**
     * 用户退出登录
     * @param userId 用户ID
     */
    @Operation(summary = "用户退出登录", description = "用户退出登录")
    @PostMapping("/logout")
    public IMOCCJSONResult logout(@RequestParam String userId,
                                  HttpServletRequest request,
                                  HttpServletResponse  response) {

        // 清除用户相关信息的 cookie
        CookieUtils.deleteCookie(request, response, "user");

        // TODO 用户退出登录,需要清空购物车
        // TODO 分布式会话中需要清除用户数据
        return IMOCCJSONResult.ok();
    }

六、日志记录

1.依赖

 <!-- Spring Boot 基础 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <!--引入日志依赖 抽象层 与 实现层-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.21</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.21</version>
        </dependency>

2.log4j.properties

log4j.rootLogger=DEBUG,stdout,file 
log4j.additivity.org.apache=true 

log4j.appender.stdout=org.apache.log4j.ConsoleAppender 
log4j.appender.stdout.threshold=INFO 
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 
log4j.appender.stdout.layout.ConversionPattern=%-5p %c{1}:%L - %m%n

log4j.appender.file=org.apache.log4j.DailyRollingFileAppender 
log4j.appender.file.layout=org.apache.log4j.PatternLayout 
log4j.appender.file.DatePattern='.'yyyy-MM-dd-HH-mm 
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n 
log4j.appender.file.Threshold=INFO 
log4j.appender.file.append=true 
log4j.appender.file.File=/workspaces/logs/foodie-api/imooc.log 

3.ServiceLogAspect.java

foodie-dev\foodie-dev-api\src\main\java\com\imooc\aspect\ServiceLogAspect.java

package com.imooc.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class ServiceLogAspect {

    private static final Logger log = LoggerFactory.getLogger(ServiceLogAspect.class);
    /**
     * A0P通知:
     * 1.前置通知:在方法调用之前执行
     * 2.后置通知:在方法正常调用之后执行
     * 3.⭐环绕通知:在方法调用之前和之后,都分别可以执行的通知
     * 4.异常通知:如果在方法调用过程中发生异常,则通知
     * 5.最终通知:在方法调用之后执行
     */

    /**
     * 切面表达式:execution代表所要执行的表达式主体
     * 第一处 * 代表方法返回(所有)类型
     * 第二处 包名代表aop监控的类所在的包
     * 第三处 ..代表该包以及其子包下的所有类
     * 第四处 *代表(所有)类名
     * 第五处 *代表类中的方法名,(..)表示方法中的任何参数
     */

    @Around("execution(* com.imooc.service.impl..*.*(..))")
    public Object recordTimeLog(ProceedingJoinPoint joinPoint) throws Throwable {

        log.info("开始执行:{}.{}", 
                joinPoint.getTarget().getClass().getSimpleName(),
                joinPoint.getSignature().getName());

        // 记录开始时间
        long begin = System.currentTimeMillis();

        // 执行目标 service
        Object result = joinPoint.proceed();

        // 记录结束时间
        long end = System.currentTimeMillis();
        long takeTime = end - begin;

        if (takeTime > 3000) {
            log.error("执行结束,耗时{}毫秒", takeTime);
        } else if (takeTime > 2000) {
            log.warn("执行结束,耗时{}毫秒", takeTime);
        } else {
            log.info("执行结束,耗时{}毫秒", takeTime);
        }

        return result;
    }
}

更多推荐