作者 lixiang

框架修改

package org.jeecg.modules.airag.app.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
// @Bean(name = "mysqlDataSource")
// @Primary
// @ConfigurationProperties(prefix = "spring.datasource.mysql")
// public DataSource mysqlDataSource() {
// return DataSourceBuilder.create().build();
// }
//
// @Bean(name = "postgresDataSource")
// @ConfigurationProperties(prefix = "spring.datasource.postgres")
// public DataSource postgresDataSource() {
// return DataSourceBuilder.create().build();
// }
}
... ...
package org.jeecg.modules.airag.app.controller;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.sql.Connection; // PostgreSQL JDBC 连接
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import dev.langchain4j.store.embedding.EmbeddingStore; // LangChain4j 的 EmbeddingStore 接口
import dev.langchain4j.store.embedding.pgvector.PgVectorEmbeddingStore;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.output.Response;
import dev.langchain4j.store.embedding.EmbeddingMatch;
import dev.langchain4j.store.embedding.EmbeddingSearchRequest;
import dev.langchain4j.store.embedding.EmbeddingSearchResult;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.pgvector.PgVectorEmbeddingStore;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
//import org.jeecg.modules.airag.app.config.PostgreEmbeddingStore;
import org.jeecg.modules.airag.app.entity.Embeddings;
import org.jeecg.modules.airag.app.entity.Embeddings;
import org.jeecg.modules.airag.app.service.IEmbeddingsService;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.modules.airag.app.utils.AiModelUtils;
import org.jeecg.modules.airag.llm.entity.AiragModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: Embeddings
* @Author: jeecg-boot
* @Date: 2025-05-26
* @Version: V1.0
*/
@Tag(name="Embeddings")
@RestController
@RequestMapping("/embeddings/embeddings")
@Slf4j
public class EmbeddingsController {
@Autowired
private IEmbeddingsService embeddingsService;
@Autowired
private AiModelUtils aiModelUtils;
/**
* 分页列表查询
*
* @param Embeddings
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "Embeddings-分页列表查询")
@Operation(summary="Embeddings-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<Embeddings>> queryPageList(Embeddings Embeddings,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) throws NoSuchFieldException, IllegalAccessException, SQLException {
Response<Embedding> embedding = aiModelUtils.getEmbedding("1925730210204721154", "33333");
List<Embeddings> records = embeddingsService.findAll();
for (Embeddings record : records) {
System.out.println("record = " + record);
}
Page<Embeddings> page = new Page<Embeddings>(pageNo, pageSize);
page.setRecords(records);
return Result.OK(page);
}
/**
* 添加
*
* @param Embeddings
* @return
*/
@AutoLog(value = "Embeddings-添加")
@Operation(summary="Embeddings-添加")
@RequiresPermissions("Embeddings:Embeddings:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody Embeddings Embeddings) {
// embeddingsService.save(Embeddings);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param Embeddings
* @return
*/
@AutoLog(value = "Embeddings-编辑")
@Operation(summary="Embeddings-编辑")
@RequiresPermissions("Embeddings:Embeddings:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody Embeddings Embeddings) {
// embeddingsService.updateById(Embeddings);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "Embeddings-通过id删除")
@Operation(summary="Embeddings-通过id删除")
@RequiresPermissions("Embeddings:Embeddings:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
// embeddingsService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "Embeddings-批量删除")
@Operation(summary="Embeddings-批量删除")
@RequiresPermissions("Embeddings:Embeddings:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
// this.embeddingsService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "Embeddings-通过id查询")
@Operation(summary="Embeddings-通过id查询")
@GetMapping(value = "/queryById")
public Result<Embeddings> queryById(@RequestParam(name="id",required=true) String id) {
// Embeddings Embeddings = embeddingsService.getById(id);
// if(Embeddings==null) {
// return Result.error("未找到对应数据");
// }
return Result.OK();
}
/**
* 导出excel
*
* @param request
* @param Embeddings
*/
@RequiresPermissions("Embeddings:Embeddings:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, Embeddings Embeddings) {
return null;
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("Embeddings:Embeddings:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return null;
}
}
... ...
package org.jeecg.modules.airag.app.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Map;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Embeddings {
private String id;
private String text;
private float[] embedding;
private Map<String, Object> metadata;
private Double similarity;
}
\ No newline at end of file
... ...
package org.jeecg.modules.airag.app.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: test
* @Author: jeecg-boot
* @Date: 2025-05-26
* @Version: V1.0
*/
@Data
@TableName("test")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="test")
public class Test implements Serializable {
private static final long serialVersionUID = 1L;
/**id*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "id")
private Integer id;
/**name*/
@Excel(name = "name", width = 15)
@Schema(description = "name")
private String name;
}
... ...
package org.jeecg.modules.airag.app.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.jeecg.modules.airag.app.entity.Embeddings;
import org.jeecg.modules.airag.app.entity.Test;
/**
* @Description: test
* @Author: jeecg-boot
* @Date: 2025-05-26
* @Version: V1.0
*/
public interface EmbeddingsMapper {
}
... ...
package org.jeecg.modules.airag.app.mapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.pgvector.PGvector;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.dao.DataAccessException;
import org.jeecg.modules.airag.app.entity.Embeddings;
import org.springframework.stereotype.Component;
import java.sql.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@Component
@Slf4j
public class PgVectorMapper {
// PostgreSQL连接参数(实际项目中应从配置读取)
private static final String URL = "jdbc:postgresql://192.168.100.103:5432/postgres";
private static final String USER = "postgres";
private static final String PASSWORD = "postgres";
// 获取数据库连接
private Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USER, PASSWORD);
}
// 查询所有向量记录
public List<Embeddings> findAll() {
List<Embeddings> results = new ArrayList<>();
String sql = "SELECT * FROM embeddings";
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
results.add(mapRowToEmbeddings(rs));
}
} catch (SQLException e) {
log.error("查询所有向量记录失败", e);
throw new RuntimeException("查询向量数据时发生数据库错误", e);
}
return results;
}
// 根据ID查询单个向量记录
public Embeddings findById(Long id) {
String sql = "SELECT * FROM embeddings WHERE id = ?";
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setLong(1, id);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return mapRowToEmbeddings(rs);
}
}
} catch (SQLException e) {
log.error("根据ID查询向量记录失败, ID: {}", id, e);
throw new RuntimeException("根据ID查询向量时发生数据库错误", e);
}
return null;
}
// 插入新向量记录
public int insert(Embeddings record) {
String sql = "INSERT INTO embeddings (id, embedding, metadata) VALUES (?, ?, ?::jsonb)";
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, record.getId());
stmt.setObject(2, new PGvector(record.getEmbedding()));
stmt.setString(3, toJson(record.getMetadata()));
return stmt.executeUpdate();
} catch (SQLException e) {
log.error("插入向量记录失败: {}", record, e);
throw new RuntimeException("插入向量数据时发生数据库错误", e);
}
}
// 更新向量记录
public int update(Embeddings record) {
String sql = "UPDATE embeddings SET embedding = ?, metadata = ?::jsonb WHERE id = ?";
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setObject(1, new PGvector(record.getEmbedding()));
stmt.setString(2, toJson(record.getMetadata()));
stmt.setString(3, record.getId());
return stmt.executeUpdate();
} catch (SQLException e) {
log.error("更新向量记录失败: {}", record, e);
throw new RuntimeException("更新向量数据时发生数据库错误", e);
}
}
// 根据ID删除向量记录
public int deleteById(Long id) {
String sql = "DELETE FROM embeddings WHERE id = ?";
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setLong(1, id);
return stmt.executeUpdate();
} catch (SQLException e) {
log.error("删除向量记录失败, ID: {}", id, e);
throw new RuntimeException("删除向量数据时发生数据库错误", e);
}
}
// 向量相似度搜索
public List<Embeddings> similaritySearch(float[] vector, int limit) {
String sql = "SELECT * FROM embeddings ORDER BY embedding <-> ? LIMIT ?";
List<Embeddings> results = new ArrayList<>();
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setObject(1, new PGvector(vector));
stmt.setInt(2, limit);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
results.add(mapRowToEmbeddings(rs));
}
}
} catch (SQLException e) {
log.error("向量相似度搜索失败", e);
throw new RuntimeException("执行向量相似度搜索时发生数据库错误", e);
}
return results;
}
// 将ResultSet行映射为VectorRecord对象
private Embeddings mapRowToEmbeddings(ResultSet rs) throws SQLException {
Embeddings record = new Embeddings();
record.setId(rs.getString("embedding_id"));
record.setText(rs.getString("text"));
String metadataJson = rs.getString("metadata");
if (StringUtils.isNotBlank(metadataJson)) {
record.setMetadata(fromJson(metadataJson));
}
return record;
}
// 将Map转换为JSON字符串
private String toJson(Map<String, Object> map) {
try {
return new ObjectMapper().writeValueAsString(map);
} catch (JsonProcessingException e) {
log.error("元数据转换为JSON失败", e);
return "{}";
}
}
// 将JSON字符串转换为Map
private Map<String, Object> fromJson(String json) {
try {
return new ObjectMapper().readValue(json, new TypeReference<Map<String, Object>>() {});
} catch (JsonProcessingException e) {
log.error("JSON转换为元数据失败", e);
return Collections.emptyMap();
}
}
}
\ No newline at end of file
... ...
package org.jeecg.modules.airag.app.service;
//import org.jeecg.modules.demo.test.entity.Test;
import org.jeecg.modules.airag.app.entity.Test;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.airag.app.entity.Embeddings;
import java.util.List;
/**
* @Description: test
* @Author: jeecg-boot
* @Date: 2025-05-26
* @Version: V1.0
*/
public interface IEmbeddingsService {
List<Embeddings> findAll();
}
... ...
package org.jeecg.modules.airag.app.service.impl;
import org.jeecg.modules.airag.app.entity.Test;
import org.jeecg.modules.airag.app.entity.Embeddings;
import org.jeecg.modules.airag.app.mapper.EmbeddingsMapper;
import org.jeecg.modules.airag.app.mapper.PgVectorMapper;
import org.jeecg.modules.airag.app.service.IEmbeddingsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.util.List;
/**
* @Description: test
* @Author: jeecg-boot
* @Date: 2025-05-26
* @Version: V1.0
*/
@Service
public class IEmbeddingsServiceImpl implements IEmbeddingsService {
@Autowired
private PgVectorMapper pgVectorMapper;
@Override
public List<Embeddings> findAll() {
return pgVectorMapper.findAll();
}
}
... ...
package org.jeecg.modules.airag.app.utils;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.output.Response;
import org.jeecg.ai.factory.AiModelFactory;
import org.jeecg.ai.factory.AiModelOptions;
import org.jeecg.common.util.AssertUtils;
import org.jeecg.modules.airag.llm.consts.LLMConsts;
import org.jeecg.modules.airag.llm.entity.AiragModel;
import org.jeecg.modules.airag.llm.service.IAiragModelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import static org.jeecg.modules.airag.llm.handler.EmbeddingHandler.buildModelOptions;
@Component
public class AiModelUtils {
@Autowired
@Lazy
private IAiragModelService airagModelService;
private AiragModel getEmbedModelData(String modelId) {
AssertUtils.assertNotEmpty("向量模型不能为空", modelId);
AiragModel model = airagModelService.getById(modelId);
AssertUtils.assertNotEmpty("向量模型不存在", model);
AssertUtils.assertEquals("仅支持向量模型", LLMConsts.MODEL_TYPE_EMBED, model.getModelType());
return model;
}
/**
* 获取向量模型
* @param modelId 模型id
* @return 向量模型
*/
public EmbeddingModel getEmbeddingModel(String modelId) {
AiModelOptions modelOp = buildModelOptions(getEmbedModelData(modelId));
return AiModelFactory.createEmbeddingModel(modelOp);
}
/**
* 根据模型id选择向量模型,将文本向量化
* @param modelId 模型id
* text 文本
* @return Embedding
*/
public Response<Embedding> getEmbedding(String modelId, String text) {
return getEmbeddingModel(modelId).embed(text);
}
}
... ...
package org.jeecg.modules.airag.app.utils;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class PureJdbcVectorQuery {
// PostgreSQL连接参数
private static final String URL = "jdbc:postgresql://192.168.100.103:5432/postgres";
private static final String USER = "postgres";
private static final String PASSWORD = "postgres";
public static List<Map<String, Object>> queryVectors() {
List<Map<String, Object>> results = new ArrayList<>();
try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM embeddings");
ResultSet rs = stmt.executeQuery()) {
ResultSetMetaData metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount();
while (rs.next()) {
Map<String, Object> row = new HashMap<>();
for (int i = 1; i <= columnCount; i++) {
String columnName = metaData.getColumnName(i);
Object value = "embedding".equals(columnName)
? rs.getBytes(i) // 向量字段特殊处理
: rs.getObject(i);
row.put(columnName, value);
}
results.add(row);
}
} catch (SQLException e) {
e.printStackTrace();
}
return results;
}
public static void main(String[] args) {
List<Map<String, Object>> maps = queryVectors();
for (Map<String, Object> map : maps) {
System.out.println(map);
}
}
}
\ No newline at end of file
... ...
... ... @@ -6,6 +6,7 @@ import org.flywaydb.core.Flyway;
import org.flywaydb.core.api.FlywayException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
... ... @@ -26,6 +27,7 @@ import java.util.Map;
public class FlywayConfig {
@Autowired
// @Qualifier("mysqlDataSource")
private DataSource dataSource;
@Autowired
... ...
... ... @@ -26,7 +26,7 @@ management:
spring:
flyway:
# 是否启用flyway
enabled: true
enabled: false
# 是否关闭要清除已有库下的表功能,生产环境必须为true,否则会删库,非常重要!!!
clean-disabled: true
servlet:
... ... @@ -50,6 +50,8 @@ spring:
quartz:
job-store-type: jdbc
initialize-schema: embedded
jdbc:
initialize-schema: always
#定时任务启动开关,true-开 false-关
auto-startup: true
#延迟1秒启动定时任务
... ... @@ -119,6 +121,7 @@ spring:
web-stat-filter:
enabled: true
dynamic:
primary: master
druid: # 全局druid参数,绝大部分值和默认保持一致。(现已支持的参数如下,不清楚含义不要乱设置)
# 连接池的配置信息
# 初始化大小,最小,最大
... ... @@ -154,11 +157,11 @@ spring:
password: 1234
driver-class-name: com.mysql.cj.jdbc.Driver
# 多数据源配置
#multi-datasource1:
#url: jdbc:mysql://localhost:3306/jeecg-boot2?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
#username: root
#password: root
#driver-class-name: com.mysql.cj.jdbc.Driver
# pgvector:
# jdbc-url: jdbc:postgresql://192.168.100.103:5432/postgres
# username: postgres
# password: postgres
# driver-class-name: org.postgresql.Driver
#redis 配置
redis:
database: 0
... ... @@ -253,7 +256,7 @@ jeecg:
#大屏报表参数设置
jmreport:
#多租户模式,默认值为空(created:按照创建人隔离、tenant:按照租户隔离) (v1.6.2+ 新增)
saasMode:
saasMode:
# 平台上线安全配置(v1.6.2+ 新增)
firewall:
# 数据源安全 (开启后,不允许使用平台数据源、SQL解析加签并且不允许查询数据库)
... ...
... ... @@ -432,6 +432,12 @@
</exclusion>
</exclusions>
</dependency>
<!-- HikariCP连接池 -->
<!-- <dependency>-->
<!-- <groupId>com.zaxxer</groupId>-->
<!-- <artifactId>HikariCP</artifactId>-->
<!-- <version>5.0.1</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.jeecgframework.jimureport</groupId>
<artifactId>jimureport-nosql-starter</artifactId>
... ...