AI零代码应用平台项目初始化

项目简介

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

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

image-20250814233045559

百度-秒哒

网址:https://console.bce.baidu.com/miaoda/design

image-20250814233146327

美团-NoCode

网址:https://nocode.cn/

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

img

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

img

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

好~~ 那么新项目就叫 兔子应用生成

项目初始化

环境准备

1)安装的 JDK 版本必须 >= 17,推荐使用 21 版本!

2)MyS؜QL 数据库最好安‎装 8.x 版本,⁢或者 5.7 版本‎。

新建项目

image-20250814233754269

选择 Sprin؜g Boot 3.5.x 版本,‎必须添加的依赖包括 Spring⁢ Web、MySQL、Lombo‎k,Spring Boot De‎vTools 可以按需选取:

image-20250814233925696

当然,后续通过修改 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>

整合依赖

Hutool工具库

不多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-openapi
springdoc:
group-configs:
- group: 'default'
packages-to-scan: com.fantasy.rabbitaicodemother.controller
# knife4j
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";
}
}

img

这里发现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 {

/**
* 条件成立则抛异常
*
* @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层都要返回的通用响应类

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 {

/**
* 成功
*
* @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);
}
}

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

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

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 {

/**
* id
*/
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("/**")
// 允许发送 Cookie
.allowCredentials(true)
// 放行哪些域名(必须用 patterns,否则 * 会和 allowCredentials 冲突)
.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");
}
}

image-20250814235040066