AI零代码应用平台项目初始化
项目简介
大厂都在卷AI编程的赛道,无论是网页还是AI应用生成器,这是当下的风口。
像百度的秒哒,美团的noCode都可以做到零代码编程

百度-秒哒
网址:https://console.bce.baidu.com/miaoda/design

美团-NoCode
网址:https://nocode.cn/
我自己亲身实践了一下百度的秒哒,确实爽的飞起~~~~

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

我马上也开始了这种零代码的开发,手撕一个属于自己的零代码开发平台!!
好~~ 那么新项目就叫 兔子应用生成
项目初始化
环境准备
1)安装的 JDK 版本必须 >= 17,推荐使用 21 版本!
2)MySQL 数据库最好安装 8.x 版本,或者 5.7 版本。
新建项目

选择 Spring Boot 3.5.x 版本,必须添加的依赖包括 Spring Web、MySQL、Lombok,Spring Boot DevTools 可以按需选取:

当然,后续通过修改 Maven 配置添加依赖也是可以的。
💡 小提示,如果 Lombok 依赖报错的话,可以手动指定 Lombok 的版本,修改 pom.xml 的代码:
1 2 3 4 5 6
| <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.36</version> <optional>true</optional> </dependency>
|
整合依赖
不多bb,Hutool yyds!!
参考官方文档引入:https://doc.hutool.cn/pages/index/#%F0%9F%8D%8Amaven
1 2 3 4 5
| <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.38</version> </dependency>
|
Knife4j接口文档
参考官方文档引入:https://doc.xiaominfo.com/docs/quick-start#spring-boot-3
1 2 3 4 5
| <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> <version>4.4.0</version> </dependency>
|
这里需要在配置文件中加入配置
1 2 3 4 5 6 7 8 9 10
| springdoc: group-configs: - group: 'default' packages-to-scan: com.fantasy.rabbitaicodemother.controller
knife4j: enable: true setting: language: zh_cn
|
继续测试一下我们的swagger~~~
1 2 3 4 5 6 7 8 9
| @RestController @RequestMapping("/health") public class HealthController {
@GetMapping("/") public String healthCheck() { return "ok"; } }
|

这里发现swagger已经成功有了接口,说明可以正常使用了~~
那么接下来就需要继续写一些通用的代码
样板代码
这时候再来一个自定义错误码,用于统一返回错误信息
所以在 exception 包下新建错误码枚举类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| @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; }
}
|
有了自定义的错误码,自然也要生成自定义异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @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来判断吗,那也太麻烦了
不如直接生成一个专门抛异常的类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public class ThrowUtils {
public static void throwIf(boolean condition, RuntimeException runtimeException) { if (condition) { throw runtimeException; } }
public static void throwIf(boolean condition, ErrorCode errorCode) { throwIf(condition, new BusinessException(errorCode)); }
public static void throwIf(boolean condition, ErrorCode errorCode, String message) { throwIf(condition, new BusinessException(errorCode, message)); } }
|
接下来就是每一个controller层都要返回的通用响应类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @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列了出来,比如我们直接就可以ResultUtils.sucess()就ok了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| public class ResultUtils {
public static <T> BaseResponse<T> success(T data) { return new BaseResponse<>(0, data, "ok"); }
public static BaseResponse<?> error(ErrorCode errorCode) { return new BaseResponse<>(errorCode); }
public static BaseResponse<?> error(int code, String message) { return new BaseResponse<>(code, null, message); }
public static BaseResponse<?> error(ErrorCode errorCode, String message) { return new BaseResponse<>(errorCode.getCode(), null, message); } }
|
好,接下来就是全局异常处理器了
有人问,全局异常处理器是干嘛用的?其实这个就是来增加代码的健壮性,所谓“健壮性”就是让程序在运行的时候遇到我们一般预示不到的异常时保证自己的程序不中断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @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来解决问题
在很多的情况,我们要用分页查询,分页查询有一些参数,比如:页号,页面大小,排序字段,顺序等等
这里统一写成一个封装类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Data public class PageRequest {
private int pageNum = 1;
private int pageSize = 10;
private String sortField;
private String sortOrder = "descend"; }
|
当然删除也是~~
1 2 3 4 5 6 7 8 9 10
| @Data public class DeleteRequest implements Serializable {
private Long id;
private static final long serialVersionUID = 1L; }
|
还有前后端分离开发时,前端和后端的域名或者端口号是不同的,这就产生了跨域的问题,那么其实我们可以在后端统一写一个解决跨域的类来解决问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Configuration public class CorsConfig implements WebMvcConfigurer {
@Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowCredentials(true) .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .exposedHeaders("*"); } }
|
最后测试一下
1 2 3 4 5 6 7 8 9
| @RestController @RequestMapping("/health") public class HealthController {
@GetMapping("/") public BaseResponse<String> healthCheck() { return ResultUtils.success( "ok"); } }
|
