1. 前言
使用最新springboot 和图数据库Neo4j搭建最简AI开发框架(图数据库Neo4j的优点大家可以百度),实现后台代码快速开发。 整个后台框架争取做到最简,只引入必须的依赖;开发的模块代码也争取做到最少。
-
数据库用neo4j ;
-
缓存用redis;
-
大模型用的是阿里云百炼平台;
-
对象存储服务用阿里云/华为云的云存储;
-
开发工具使用:IntelliJ IDEA 社区版,下载地址:www.jetbrains.com/zh-cn/idea/…
2. 使用IDEA社区版创建maven项目
2.1 创建空项目
注意archetype选择如上图(...archetype-quickstart模式)。
Advanced Settings里面可以填写GroupId,对应的是pom.xml中的groupId标签。
创建完后,会生成如下极其简单的目录:
2.2 调整src目录
生成的src目录不完整,可以手动创建必要的目录,如下图:
-
common
存放公共的类,如常量、公共异常、工具类等。
-
config
存放系统的配置
-
controller
controller接口类和页面请求实体类。
-
domain
与数据库对应的实体类
-
repository
数据库操作类
-
service
service业务类。
2.3 编写启动类
启动类修改为:GutApplication.java ,放在com.yunei.gut目录下
package com.yunei.gut;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.validation.annotation.Validated;
@SpringBootApplication
@EnableAsync
public class GutApplication {
public static void main(String[] args) {
SpringApplication.run(GutApplication.class, args);
}
}
2.4 调整resources目录
按照环境的不同增加配置:
application.yml
定义公共的配置:启动的端口及运行环境定义。
server:
port: 8080
spring:
profiles:
active: dev
application-dev.yml
定义具体的配置,如neo4j的连接参数、redis的连接、华为obs的连接参数,这些参数开发环境和生产环境的配置可能不一样。
spring:
neo4j:
uri: bolt://neo4jip:7687
authentication:
username: neo4j
password: *****
connection:
# 连接池配置
max-connection-pool-size: 10 # 默认20
max-connection-lifetime: 3000m # 连接最大生存时间
pool:
idle-before-connection-test: 6s # 空闲连接检测间隔
validate-connection: false # 使用前验证连接有效性
# 驱动配置
driver:
config:
connection-timeout: 3000s # 连接超时时间
max-transaction-retry-time: 10s # 事务重试时间
connection-acquisition-timeout: 60s # 获取连接超时时间
data:
redis:
host: redisip
# host: 127.0.0.1
port: 7479 #端口
database: 0 # 使用的数据库编号
password: ***** #Redis密码
lettuce: #Lettuce客户端配置
pool: # 连接池配置
max-active: 40 # 最大活跃连接数
max-wait: -1 # 最大等待时间(-1表示无限等待)
max-idle: 40 # 最大空闲连接数
min-idle: 5 # 最小空闲连接数
huawei:
obs:
accessKey: HP**************QAA
secretAccessKey: LLZ****************REpckdoy
endpoint: obs.cn-south-1.myhuaweicloud.com
bucketName: gut
application-prod.yml配置文件和dev配置文件一样,修改对应的参数值即可。
日志文件logback
Logback 是 Java 生态中主流的日志框架,具有高性能、灵活配置等特点。Spring Boot 3 默认集成 Logback 作为日志系统,配置文件如下:
<configuration>
<property name="LOG_PATH" value="logs"/>
<property name="APP_NAME" value="gut"/>
<springProfile name="dev">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</springProfile>
<springProfile name="prod">
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/prod-${APP_NAME}.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/prod-${APP_NAME}-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
</encoder>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="FILE" />
<appender-ref ref="STDOUT" />
</root>
</springProfile>
</configuration>
2.5 构建pom.xml
-
SpringBoot 3.5.5
截止目前SpringBoot最新版本是3.5.5,这应该是3.5版本的最高版本了,后面估计就直接使用SpringBoot 4.0了。SpringBoot在3.5 这个版本上做了一些升级,有兴趣的可以百度了解一下。
pom.xml中增加:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.5.5</version> <relativePath/> </parent><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency>
jdk17配置
<java.version>17</java.version>
-
hutool 工具
Hutool是一个Java工具包类库,对文件、流、加密解密、转码、正则、线程、XML等JDK方法进行封装,组成各种Util工具类。官网地址:www.hutool.cn/
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.39</version> </dependency>javax.validation -
validation校验
validation的一系列注解可以帮我们完成参数校验,免去繁琐的串行校验
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> -
jedis
jedis主要用于redis的连接
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> -
neo4j集成
SpringBoot集成Neo4j非常方便,pom.xml中加入如下配置即可
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-neo4j</artifactId> </dependency> -
fastjson
Fastjson 是一个 Java 库,可以将 Java 对象转换为 JSON 格式,当然它也可以将 JSON 字符串转换为 Java 对象。Fastjson 可以操作任何 Java 对象,即使是一些预先存在的没有源码的对象。
<!-- JSON 解 析 工 具 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
- pom.xml的modelVersion
modelVersion 标签用于指定当前 pom.xml 文件所遵循的项目对象模型(Project Object Model,POM)的版本。这个版本号确保了 Maven 能够正确地解析和处理 pom.xml 文件中的其他元素和配置。
在大多数 Maven 项目中,modelVersion 的标准值通常为 4.0.0,这是自 Maven 2.0 以来一直使用的版本。尽管 Maven 可能会引入新的 POM 模型版本,但在实践中,4.0.0 仍然是广泛使用和推荐的版本。
<modelVersion>4.0.0</modelVersion>
最终的pom.xml文件见源代码。
写好pom.xml文件后,通过IDEA工具编译打包,maven会自动下载依赖的包。
3. IDEA安装百度的Comate
百度的文心快码可以辅助我们快速开发,推荐安装一下,在设置的插件里面,搜索baidu即可看到Baidu Comate ,点击 install 安装,安装后重启IDEA。
从IDEA市场下载速度比较慢,可以去百度官网下载插件后,从硬盘安装。
-
下载插件
-
从磁盘安装
在设置的插件里面,有一个从磁盘安装插件的选项,点击后选择下载的插件即可。
安装后,可以使用试试,如IDEA使用时控制台出现乱码,我们可以直接问:idea控制台输出乱码怎么处理 。回复如下:
根据第二个回复即可修复问题 ,我们还可通过文心快码生成代码,后续专门说明如何使用。
4. 安装lombok插件
lombok插件是必须安装的插件了,同理在插件市场搜索后安装即可。
5. 启动
编译通过后,打开启动类GutApplication,右键运行,启动不报错,则运行成功。
6. 第一个实体类User的增删改查
6.1 实体类User
首先在domain目录下创建一个User类,代码如下:
package com.yunei.gut.domain;
import com.yunei.gut.domain.base.BaseDomain;
import lombok.Data;
import org.springframework.data.neo4j.core.schema.Id;
import org.springframework.data.neo4j.core.schema.Node;
import org.springframework.data.neo4j.core.schema.Property;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Node("User")
@Data
public class User extends BaseDomain implements Serializable {
@Id
private String userId;
private String account;
private String phone;
private String openId;
private String unionId; // 微信唯一标识[3]()
private String nickname;
private String avatar;
private String bio;//个人签名、简介
private String status;//状态:Y-有效;N-停用;U-未激活
private String realName;
private String sex;
private LocalDate birthday;
private int grade;//年级 1-一年级,2-二年级...7-七年级;8-八年级...12-十二年级;-2-幼儿园小班;-1-幼儿园中班;0-幼儿园大班
private String password;
private LocalDateTime lastLoginTime;
private String email;
private String userType;//C-孩子;P-家长
private String userRight="N";//用户权限;N-普通用户;V-付费VIP用户;A-管理员;T-老师
private LocalDateTime vipBeginDate;//VIP开始时间
private LocalDateTime vipEndDate;//VIP结束时间
}
公共的属性放到了BaseDomain
package com.yunei.gut.domain.base;
import lombok.Data;
import org.springframework.data.neo4j.core.schema.Property;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
public class BaseDomain implements Serializable {
private LocalDateTime createdAt;//创建时间
private LocalDateTime updateAt;//更新时间
private String createBy;//创建人
private String updateBy;//更新人
private String delFlag="N";//Y-已经删除;N-正常;未删除
private String familyId;//家庭ID,做数据隔离
}
因数据库是使用Neo4j数据库,这里的注解就是和Neo4j相关的。
@Node注解
该注解用于标记一个Java类作为Neo4j图数据库中的一个节点。注解中的字符串参数(@Node("User")中的"User")通常用于指定节点的标签(Label),这有助于在Neo4j中组织和查询数据。
@Id注解
该注解用于标识该节点的主键。
@Data 注解
是lombok的注解,用于减少代码量。
实体类只使用如上3个注解即可。
6.2 UserRepository接口
UserRepository是一个接口,它扩展了Neo4jRepository接口。在Spring Data Neo4j中,Neo4jRepository`是一个泛型接口,提供了对Neo4j图数据库进行CRUD(创建、读取、更新、删除)操作的能力。UserRepository需增加注解:@Repository
具体代码如下:
package com.yunei.gut.repository;
import com.yunei.gut.domain.User;
import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.data.neo4j.repository.query.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface UserRepository extends Neo4jRepository<User, String> {
}
单表的查询、新增、修改可不用在UserRepository中写代码。
6.3 UserService类
该类没有接口,减少代码量。
-
@Service注解表明这是一个service 。
-
@RequiredArgsConstructor 注解
RequiredArgsConstructor注解是一个提高开发效率的工具,它自动为类生成包含必需参数的构造函数。必需参数指的是@NonNull注解标记的字段,或者是 final 字段。如下段代码中对应的就是userRepository 参数,系统会自动给改参数生成构造函数。- 使用这个注解可以减少样板代码,并且确保对象在创建时就初始化了所有必需的状态。
@Slf4j
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
/**
* 根据userId查询用户
* @param userId
* @return
*/
public User getUserById(String userId) {
User u = new User();
u.setUserId(userId);
Example<User> example = Example.of(u);
return userRepository.findOne(example).orElseThrow(() -> new ServiceException(BusErrorCodeConstants.USER_NOT_EXIST)
);
}
public User register(RegisterReq registerReq) {
User user = new User();
BeanUtil.copyProperties(registerReq,user);
user.setUserId("UR"+IdUtil.fastSimpleUUID());
user.setCreatedAt(LocalDateTime.now());
user.setPassword(PasswordUtil.encryptPassword(registerReq.getPassword()));
user.setStatus(Constant.STATUS_Y);
user.setUserType(UserTypeEnum.PARENT.getCode());
user.setAvatar(Constant.DEFAULT_AVATAR);
//创建家庭
CreateFamilyReq req = new CreateFamilyReq();
req.setFamilyName(registerReq.getNickname()+"的家庭");
Family family = familyService.createFamily(req,user.getUserId());
//更新用户的familyId
user.setFamilyId(family.getFamilyId());
user.setVipBeginDate(LocalDateTime.now());
user.setVipEndDate(LocalDateTime.now().plusMonths(2));//注册送2个月的VIP
userRepository.save(user);
return user;
}
public Page<User> getAllUser(int page, int size) {
Pageable pageable = PageRequest.of(page, size);
User user = new User();
user.setStatus(Constant.STATUS_Y);
user.setDelFlag(Constant.STATUS_N);
Example<User> example = Example.of(user);
return userRepository.findAll(example,pageable);
}
}
单表查询
- 单表查询可使用Example类构建查询条件(不建议)
-
Example类是 Spring Data JPA 提供的一个工具类,用于构建基于属性的查询条件。 -
Example<User> example = Example.of(u);这行代码创建了一个Example对象,该对象封装了一个User类型的实体对象u,用于后续构建查询条件。
-
使用CQL(建议)
在UserRepository中增加如下代码,使用@Query注解撰写CQL方法
@Query("MATCH (u:User) where u.account = $account return u") List<User> findByAccount(@Param("account") String account);
新增、修改、删除
UserRepository 继承 Neo4jRepository , Neo4jRepository 继承 CrudRepository ,基础的新增、修改、删除都已经实现。
*** 注:新增和修改都是使用save方法。**
分页
分页示例见上面的getAllUser方法,使用的是Pageable对象。
Pageable pageable = PageRequest.of(page, size); 首先根据传入的分页参数组织pageable对象,page代表当前页码,size代表当前页有多少条数据。
userRepository.findAll(example,pageable); findAll方法支持传入分页对象pageable。
6.4 UserController类
注解
Controller层相对就比较简单,如下段代码,核心是需加上4个注解:
-
@Slf4j
记录日志用
-
@RestController
该注解简化了RESTful Web服务的开发,它结合了控制器声明和响应体自动转换的功能。使用这个注解可以方便地将控制器方法的返回值转换成JSON或XML格式的数据,并返回给客户端。
-
@RequiredArgsConstructor
-
@RequestMapping("/api/users")
该注解是Spring MVC中用于将HTTP请求映射到处理器方法上的重要注解。它可以定义在类级别或方法级别,用于指定请求的路径、请求方法、请求参数等条件。通过使用 @RequestMapping 注解,您可以灵活地定义控制器中各个处理器方法的请求映射规则。
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
@GetMapping("/getUserById/{userId}")
public PageResult<User> getUserById(@PathVariable @Validated String userId) {
User user = userService.getUserById(userId);
return PageResult.success(user);
}
}
页面返回封装
Controller层最核心的是要封装返回给前端页面的对象,因此要封装一个PageResult对象,代码如下:
package com.yunei.gut.common;
import cn.hutool.core.lang.Assert;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.yunei.gut.common.exception.ErrorCode;
import com.yunei.gut.common.exception.GlobalErrorCodeConstants;
import lombok.Data;
import java.io.Serializable;
import java.util.Objects;
/**
* 通用返回
*
* @param <T> 数据泛型
*/
@Data
public class PageResult<T> implements Serializable {
/**
* 错误码
*
*/
private Integer code;
/**
* 返回数据
*/
private T data;
/**
* 错误提示,用户可阅读
*
*/
private String msg;
/**
* 将传入的 result 对象,转换成另外一个泛型结果的对象
*
* 因为 A 方法返回的 CommonResult 对象,不满足调用其的 B 方法的返回,所以需要进行转换。
*
* @param result 传入的 result 对象
* @param <T> 返回的泛型
* @return 新的 CommonResult 对象
*/
public static <T> PageResult<T> error(PageResult<?> result) {
return error(result.getCode(), result.getMsg());
}
public static <T> PageResult<T> error(Integer code, String message) {
Assert.notEquals(GlobalErrorCodeConstants.SUCCESS.code(), code, "code 必须是错误的!");
PageResult<T> result = new PageResult<>();
result.code = code;
result.msg = message;
return result;
}
public static <T> PageResult<T> error(ErrorCode errorCode) {
return error(errorCode.code(), errorCode.msg());
}
public static <T> PageResult<T> success(T data) {
PageResult<T> result = new PageResult<>();
result.code = GlobalErrorCodeConstants.SUCCESS.code();
result.data = data;
result.msg = "";
return result;
}
public static boolean isSuccess(Integer code) {
return Objects.equals(code, GlobalErrorCodeConstants.SUCCESS.code());
}
@JsonIgnore // 避免 jackson 序列化
public boolean isSuccess() {
return isSuccess(code);
}
@JsonIgnore // 避免 jackson 序列化
public boolean isError() {
return !isSuccess();
}
}
系统还在开发中,后续会分享如何打通阿里云的百炼大模型,有兴趣可以加入交流群: