AI智能体--无码--第一期后端初始化

142 阅读5分钟

一、项目简介

大厂都在卷AI编程的赛道,无论是网页还是AI应用生成器,这是当下的风口。

像百度的秒哒,美团的noCode都可以做到零代码编程

百度-秒哒

网址:console.bce.baidu.com/miaoda/desi…

美团-NoCode

网址:nocode.cn/

我自己亲身实践了一下百度的秒哒,确实爽的飞起~~~~

我用秒哒生成了一个个人网站,如果生成的样式和自己的想法有所不同,可以继续修改prompt来改进自己的项目。

鱼总马上也开始了这种零代码的开发,手撕一个属于自己的零代码开发平台!!

好~~ 那么新项目就叫 无码

二、项目初始化

后端项目初始化

1)这次项目不同于以前,这次使用了java的新版jdk--21

新建项目的时候要设置好jdk的版本,因为要利用21的新特性

并且这里的Server URL要用图中的



这里springboot也要主要版本,推荐使用3.5.3

当然了,在调试代码的过程中一定要有重要的Lombok依赖

<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.36</version>
  <optional>true</optional>
</dependency>

这里的Lombok要记录一下版本号,springboot3中的Lombok就没有版本了,需要手动设置一下。

整合依赖

1.Hutool工具库

不多bb,Hutool yyds!!

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.38</version>
</dependency>

2.Knife4j接口文档

http://localhost:8123/api/doc.html

1)利用Swagger进行测试

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
    <version>4.4.0</version>
</dependency>

2)这里需要在配置文件中加入配置

springdoc:
  group-configs:
   - group: 'default'
     paths-to-match: '/**'
     packages-to-scan: com.langdang.langaiwuma.controller
    # knife4j
knife4j:
      enable: true
      setting:
        language: zh_cn

继续测试一下我们的swagger~~~


@RestController
@RequestMapping("/health")
public class HealthController {
    /**
     * 健康检查接口
     */
    @GetMapping("/")
    public String healthCheck(){
        return "ok";
    }
}

这里发现swagger已经成功有了接口,说明可以正常使用了~~

那么接下来就需要继续写一些通用的代码

样板代码

这时候再来一个自定义错误码,用于统一返回错误信息

package com.langdang.langaiwuma.exception;

import lombok.Getter;

@Getter
public enum ErrorCode {

    SUCCESS(0, "ok"),
    PARAMS_ERROR(40000, "请求参数错误"),
    NOT_LOGIN_ERROR(40100, "未登录"),
    NO_AUTH_ERROR(40101, "无权限"),
    NOT_FOUND_ERROR(40400, "请求数据不存在"),
    FORBIDDEN_ERROR(40300, "禁止访问"),
    SYSTEM_ERROR(50000, "系统内部异常"),
    OPERATION_ERROR(50001, "操作失败");

    /**
     * 状态码
     */
    private final int code;

    /**
     * 信息
     */
    private final String message;

    ErrorCode(int code, String message) {
        this.code = code;
        this.message = message;
    }

}

有了自定义的错误码,自然也要生成自定义异常

package com.langdang.langaiwuma.exception;

import com.langdang.langaiwuma.exception.ErrorCode;
import lombok.Getter;

@Getter
public class BusinessException extends RuntimeException {

    /**
     * 错误码
     */
    private final int code;

    public BusinessException(int code, String message) {
        super(message);
        this.code = code;
    }

    public BusinessException(ErrorCode errorCode) {
        super(errorCode.getMessage());
        this.code = errorCode.getCode();
    }

    public BusinessException(ErrorCode errorCode, String message) {
        super(message);
        this.code = errorCode.getCode();
    }
}

那么,问题来了,什么时候抛这个自定义异常呢?难道还要用if来判断吗,那也太麻烦了。。。

不如直接生成一个专门抛异常的类

package com.langdang.langaiwuma.exception;

public class ThrowUtils {

    /**
     * 条件成立则抛异常
     *
     * @param condition        条件
     * @param runtimeException 异常
     */
    public static void throwIf(boolean condition, RuntimeException runtimeException) {
        if (condition) {
            throw runtimeException;
        }
    }

    /**
     * 条件成立则抛异常
     *
     * @param condition 条件
     * @param errorCode 错误码
     */
    public static void throwIf(boolean condition, ErrorCode errorCode) {
        throwIf(condition, new BusinessException(errorCode));
    }

    /**
     * 条件成立则抛异常
     *
     * @param condition 条件
     * @param errorCode 错误码
     * @param message   错误信息
     */
    public static void throwIf(boolean condition, ErrorCode errorCode, String message) {
        throwIf(condition, new BusinessException(errorCode, message));
    }
}

接下来就是每一个controller层都要返回的通用响应类

package com.langdang.langaiwuma.common;

import com.langdang.langaiwuma.exception.ErrorCode;
import lombok.Data;

import java.io.Serializable;

@Data
public class BaseResponse<T> implements Serializable {

    private int code;

    private T data;

    private String message;

    public BaseResponse(int code, T data, String message) {
        this.code = code;
        this.data = data;
        this.message = message;
    }

    public BaseResponse(int code, T data) {
        this(code, data, "");
    }

    public BaseResponse(ErrorCode errorCode) {
        this(errorCode.getCode(), null, errorCode.getMessage());
    }
}

但是如果用了这个通用响应类,那我每一次在controller层返回结果的时候,都要new一个对象,再把code,data,message都输入出来,这也太麻烦了。。。

所以,我们写一个工具类,其实就是把各种可能的结果都用static列了出来,比如我们直接就可以BaseResponse.sucess()就ok了

public class ResultUtils {

    /**
     * 成功
     *
     * @param data 数据
     * @param <T>  数据类型
     * @return 响应
     */
    public static <T> BaseResponse<T> success(T data) {
        return new BaseResponse<>(0, data, "ok");
    }

    /**
     * 失败
     *
     * @param errorCode 错误码
     * @return 响应
     */
    public static BaseResponse<?> error(ErrorCode errorCode) {
        return new BaseResponse<>(errorCode);
    }

    /**
     * 失败
     *
     * @param code    错误码
     * @param message 错误信息
     * @return 响应
     */
    public static BaseResponse<?> error(int code, String message) {
        return new BaseResponse<>(code, null, message);
    }

    /**
     * 失败
     *
     * @param errorCode 错误码
     * @return 响应
     */
    public static BaseResponse<?> error(ErrorCode errorCode, String message) {
        return new BaseResponse<>(errorCode.getCode(), null, message);
    }
}

好,接下来就是全局异常处理器

有人问,全局异常处理器是干嘛用的?其实这个就是来增加代码的健壮性, 所谓“健壮性"就是让程序在运行的时候遇到我们一般预示不到的异常时保证自己的程序不中断


@Hidden
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(BusinessException.class)
    public BaseResponse<?> businessExceptionHandler(BusinessException e) {
        log.error("BusinessException", e);
        return ResultUtils.error(e.getCode(), e.getMessage());
    }

    @ExceptionHandler(RuntimeException.class)
    public BaseResponse<?> runtimeExceptionHandler(RuntimeException e) {
        log.error("RuntimeException", e);
        return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "系统错误");
    }
}

让我们来读一下这个代码,这里的 @ExceptionHandler(BusinessException.class)意思是在系统运行的过程中,如果有了BusinessException,系统能继续走 return ResultUtils.error(e.getCode(), e.getMessage());

在最新版的springboot中,knife4j会发生不兼容的情况,这里采用了@Hidden来解决问题



在很多的情况,我们要用分页查询,分页查询有一些参数,比如:页号,页面大小,排序字段,顺序等等

这里统一写成一个封装类

@Data
public class PageRequest {

    /**
     * 当前页号
     */
    private int pageNum = 1;

    /**
     * 页面大小
     */
    private int pageSize = 10;

    /**
     * 排序字段
     */
    private String sortField;

    /**
     * 排序顺序(默认降序)
     */
    private String sortOrder = "descend";
}

当然删除也是~~

@Data
public class DeleteRequest implements Serializable {

    /**
     * id
     */
    private Long id;

    private static final long serialVersionUID = 1L;
}

还有前后端分离开发时,前端和后端的域名或者端口号是不同的,这就产生了跨域的问题,那么其实我们可以在后端统一写一个解决跨域的类来解决问题

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 覆盖所有请求
        registry.addMapping("/**")
                // 允许发送 Cookie
                .allowCredentials(true)
                // 放行哪些域名(必须用 patterns,否则 * 会和 allowCredentials 冲突)
                .allowedOriginPatterns("*")
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowedHeaders("*")
                .exposedHeaders("*");
    }
}

最后测试一下


@RestController
@RequestMapping("/health")
public class HealthController {
    /**
     * 健康检查接口
     */
    @GetMapping("/")
    public BaseResponse healthCheck(){
        return ResultUtils.success("ok");
    }
}

在这里踩了一个坑,@Getter注解和@SLF4J一直不好用,原来我只在一个lombok的依赖下设置了版本,在pom中有三个都需要设置~~~

原创作者:鱼皮 网站:编程导航www.codefather.cn/