Compare commits
1 Commits
main
...
fujinkang_
| Author | SHA1 | Date |
|---|---|---|
|
|
c8819e93d2 | 2 months ago |
Binary file not shown.
@ -1,34 +0,0 @@
|
||||
# 个人周总结-第12周
|
||||
|
||||
## 姓名和起止时间
|
||||
|
||||
**姓 名:** 符晋康
|
||||
|
||||
**团队名称:** 2班-老师指定的组队
|
||||
|
||||
**开始时间:** 2025-12-08
|
||||
|
||||
**结束时间:** 2025-12-15
|
||||
|
||||
## 本周任务完成情况
|
||||
|
||||
| 序号 | 总结内容 | | 情况说明 |
|
||||
|----|-----------|-----|---------------------------|
|
||||
| 1 | 实现后端接口的实现 | 完成 | 按照α计划和开发进度完成了α版本的接口实现 |
|
||||
| 2 | 持续学习后端知识 | 完成 | 根据开发需要继续学习了gstore图数据库相关知识 |
|
||||
| 3 | 集成测试 | 未完成 | 前后端依然没有集成成功 |
|
||||
|
||||
## 小结
|
||||
|
||||
1. **持续学习:** 根据后续开发的需要 持续学习必需的后端知识;
|
||||
2. **代码测试:** 小组代码整合后重新测试接口功能。
|
||||
3. **补充设计:** 学习gstore图数据库相关知识 为后续数据库优化做准备。
|
||||
|
||||
---
|
||||
|
||||
## 【注】
|
||||
|
||||
1. 在小结一栏中写出希望得到如何的帮助,如讲座等;
|
||||
2. 请将个人计划和总结提前发给负责人;
|
||||
3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交;
|
||||
4. 所有组员都需提交个人周计划、周总结文档,按时上传至代码托管平台;
|
||||
@ -1,42 +0,0 @@
|
||||
# 个人周总结-第12周
|
||||
|
||||
## 姓名和起止时间
|
||||
|
||||
**姓 名:** 李梦源
|
||||
|
||||
**团队名称:** 2班-老师指定的组队
|
||||
|
||||
**开始时间:** 2025-12-08
|
||||
|
||||
**结束时间:** 2025-12-15
|
||||
|
||||
## 本周任务完成情况
|
||||
|
||||
|
||||
| 序号 | 计划内容 | 是否完成 | 情况说明 |
|
||||
|------|---------------------|------------|--------------------|
|
||||
| 1 | 整理项目材料,准备验收 | 完成 | 汇总文档与演示内容,顺利通过阶段性验收 |
|
||||
| 2 | 优化前端功能模块 | 完成 | 进一步提升了代码质量与交互体验 |
|
||||
| 3 | 配合后端完成接口联调 | 部分完成 | 对接数据部分接口,确保前后端通信稳定、数据准确 |
|
||||
| 4 | 系统测试与问题修复 | 完成 | 针对测试过程中出现的几个问题进行排查、修复与性能调优 |
|
||||
|
||||
|
||||
## 小结
|
||||
|
||||
1. **学习需求:** 学习了有关数据库和前后端对接的一部分知识;
|
||||
2. **前端功能优化**:持续改进前端模块,增强用户体验与界面交互流畅度;
|
||||
3. **测试修复与质量保障**:开展系统测试,及时修复发现问题,优化整体性能;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 【注】
|
||||
|
||||
1. 在小结一栏中写出希望得到如何的帮助,如讲座等;
|
||||
2. 请将个人计划和总结提前发给负责人;
|
||||
3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交;
|
||||
4. 所有组员都需提交个人周计划、周总结文档,按时上传至代码托管平台;
|
||||
@ -1,43 +0,0 @@
|
||||
# 个人周计划-第14周
|
||||
|
||||
## 姓名和起止时间
|
||||
|
||||
**姓 名:** 李梦源
|
||||
|
||||
**团队名称:** 2班-老师指定的组队
|
||||
|
||||
**开始时间:** 2025-12-22
|
||||
|
||||
**结束时间:** 2025-12-29
|
||||
|
||||
## 本周任务计划安排
|
||||
|
||||
| 序号 | 计划内容 | 执行人 | 情况说明 |
|
||||
|----------|---------------------|----------------|--------------------|
|
||||
| 1 | β版本前端功能开发 | 个人 | 完成文物浏览界面优化、AI助手界面改进等前端功能细化调整 |
|
||||
| 2 | 问题修复 | 个人 |针对测试过程中出现的几个问题进行排查、修复与性能调优 |
|
||||
| 3 | 学习前端知识 | 个人 | 持续学习前端以及数据库基础相关的知识,增强对代码的理解与修改能力 |
|
||||
| 4 | 前端优化 | 个人 | 持续改进前端模块,增强用户体验与界面交互流畅度;|
|
||||
|
||||
|
||||
|
||||
## 小结
|
||||
1. **细节优化:** 本周重点聚焦β版本前端功能微调,细化界面布局与交互细节,保障界面美观度与使用流畅性;
|
||||
|
||||
2. **问题排查:** 精准排查并修复前端细节问题,进一步提升用户使用体验;
|
||||
|
||||
3. **功能适配:** 按需求完成功能界面的微调与适配工作,确保与项目整体风格统一协调;
|
||||
|
||||
4. **联调配合:** 紧密配合后端团队推进接口联调,针对性优化适配细节,保障数据交互稳定准确;
|
||||
|
||||
5. **代码规整:** 注重代码细节优化与规整,提升代码可读性与可维护性,为后续迭代奠定基础;
|
||||
|
||||
6. **能力提升:** 在细节优化与问题排查过程中,深化对前端适配与交互的理解,提升精准解决问题的能力。
|
||||
---
|
||||
|
||||
## 【注】
|
||||
|
||||
1. 在小结一栏中写出希望得到如何的帮助,如讲座等;
|
||||
2. 请将个人计划和总结提前发给负责人;
|
||||
3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交;
|
||||
4. 所有组员都需提交个人周计划、周总结文档,按时上传至代码托管平台;
|
||||
@ -1,43 +0,0 @@
|
||||
# 个人周总结-第14周
|
||||
|
||||
## 姓名和起止时间
|
||||
|
||||
**姓 名:** 李梦源
|
||||
|
||||
**团队名称:** 2班-老师指定的组队
|
||||
|
||||
**开始时间:** 2025-12-22
|
||||
|
||||
**结束时间:** 2025-12-29
|
||||
|
||||
## 本周任务完成情况
|
||||
|
||||
| 序号 | 计划内容 | 是否完成 | 情况说明 |
|
||||
|----------|---------------------|----------------|--------------------|
|
||||
| 1 | β版本前端功能开发 | 完成 | 完成智能搜索、AI助手界面等前端功能细化调整 |
|
||||
| 2 | 问题修复 | 完成 |针对测试过程中出现的几个问题进行排查、修复与性能调优 |
|
||||
| 3 | 学习前端知识 | 完成 | 持续学习前端以及数据库基础相关的知识,增强对代码的理解与修改能力 |
|
||||
| 4 | 前端优化 | 完成 | 持续改进前端模块,增强用户体验与界面交互流畅度;|
|
||||
|
||||
|
||||
|
||||
## 小结
|
||||
1. **细节优化:** 本周重点聚焦β版本前端功能微调,细化界面布局与交互细节,保障界面美观度与使用流畅性;
|
||||
|
||||
2. **代码规整:** 注重代码细节优化与规整,提升代码可读性与可维护性,为后续迭代奠定基础;
|
||||
|
||||
3. **功能适配:** 按需求完成功能界面的微调与适配工作,确保与项目整体风格统一协调;
|
||||
|
||||
4. **联调配合:** 紧密配合后端团队推进接口联调,针对性优化适配细节,保障数据交互稳定准确;
|
||||
|
||||
5. **问题排查:** 精准排查并修复多个前端细节如表、按钮对齐等问题,进一步提升用户使用体验;
|
||||
|
||||
6. **能力提升:** 在细节优化与问题排查过程中,深化对前端适配与交互的理解,提升精准解决问题的能力。
|
||||
---
|
||||
|
||||
## 【注】
|
||||
|
||||
1. 在小结一栏中写出希望得到如何的帮助,如讲座等;
|
||||
2. 请将个人计划和总结提前发给负责人;
|
||||
3. 周任务总结与计划是项目小组评分考核的重要依据,将直接记入平时成绩,请各位同学按要求认真填写并按时提交;
|
||||
4. 所有组员都需提交个人周计划、周总结文档,按时上传至代码托管平台;
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "ArtifactLLM",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
wrapperVersion=3.3.4
|
||||
distributionType=only-script
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip
|
||||
@ -0,0 +1,3 @@
|
||||
wrapperVersion=3.3.4
|
||||
distributionType=only-script
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip
|
||||
@ -0,0 +1,13 @@
|
||||
package cn.edu.hnu.artifactai;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class ArtifactAiApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ArtifactAiApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,113 +0,0 @@
|
||||
package cn.edu.hnu.artifactai.client;
|
||||
|
||||
import cn.edu.hnu.artifactai.config.DeepSeekProperties;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.*;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class DeepSeekClient {
|
||||
|
||||
private final DeepSeekProperties deepSeekProperties;
|
||||
private final OkHttpClient httpClient;
|
||||
|
||||
public DeepSeekClient(DeepSeekProperties deepSeekProperties) {
|
||||
this.deepSeekProperties = deepSeekProperties;
|
||||
this.httpClient = new OkHttpClient.Builder()
|
||||
.connectTimeout(60, TimeUnit.SECONDS)
|
||||
.readTimeout(120, TimeUnit.SECONDS)
|
||||
.writeTimeout(60, TimeUnit.SECONDS)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple chat completion
|
||||
* @param prompt User input
|
||||
* @return AI response content
|
||||
*/
|
||||
public String chat(String prompt) {
|
||||
return chat(prompt, deepSeekProperties.getModel());
|
||||
}
|
||||
|
||||
public String chat(String prompt, String model) {
|
||||
JSONObject requestBody = new JSONObject();
|
||||
requestBody.put("model", model);
|
||||
|
||||
JSONObject message = new JSONObject();
|
||||
message.put("role", "user");
|
||||
message.put("content", prompt);
|
||||
|
||||
requestBody.put("messages", Collections.singletonList(message));
|
||||
requestBody.put("temperature", deepSeekProperties.getTemperature());
|
||||
requestBody.put("max_tokens", deepSeekProperties.getMaxTokens());
|
||||
requestBody.put("stream", false);
|
||||
|
||||
RequestBody body = RequestBody.create(
|
||||
requestBody.toJSONString(),
|
||||
MediaType.parse("application/json; charset=utf-8")
|
||||
);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(deepSeekProperties.getBaseUrl() + "/chat/completions")
|
||||
.addHeader("Authorization", "Bearer " + deepSeekProperties.getApiKey())
|
||||
.addHeader("Content-Type", "application/json")
|
||||
.post(body)
|
||||
.build();
|
||||
|
||||
try (Response response = httpClient.newCall(request).execute()) {
|
||||
if (!response.isSuccessful()) {
|
||||
String errorBody = response.body() != null ? response.body().string() : "Unknown error";
|
||||
log.error("DeepSeek API error: code={}, body={}", response.code(), errorBody);
|
||||
|
||||
String userMessage = "AI 服务调用失败,请稍后重试";
|
||||
try {
|
||||
JSONObject errorJson = JSON.parseObject(errorBody);
|
||||
JSONObject errorObj = errorJson.getJSONObject("error");
|
||||
if (errorObj != null) {
|
||||
String msg = errorObj.getString("message");
|
||||
String code = errorObj.getString("code");
|
||||
if (msg != null && msg.toLowerCase().contains("insufficient balance")) {
|
||||
userMessage = "AI 服务调用失败";
|
||||
} else if (msg != null && !msg.isEmpty()) {
|
||||
userMessage = "AI 服务调用失败:" + msg;
|
||||
} else if (code != null && !code.isEmpty()) {
|
||||
userMessage = "AI 服务调用失败:" + code;
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
throw new RuntimeException(userMessage);
|
||||
}
|
||||
|
||||
if (response.body() == null) {
|
||||
throw new RuntimeException("DeepSeek API returned empty body");
|
||||
}
|
||||
|
||||
String responseStr = response.body().string();
|
||||
JSONObject jsonResponse = JSON.parseObject(responseStr);
|
||||
|
||||
JSONArray choices = jsonResponse.getJSONArray("choices");
|
||||
if (choices != null && !choices.isEmpty()) {
|
||||
JSONObject choice = choices.getJSONObject(0);
|
||||
JSONObject messageObj = choice.getJSONObject("message");
|
||||
return messageObj.getString("content");
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
log.error("DeepSeek network error", e);
|
||||
throw new RuntimeException("DeepSeek network error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
package cn.edu.hnu.artifactai.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "ai.deepseek")
|
||||
public class DeepSeekProperties {
|
||||
/**
|
||||
* API Key
|
||||
*/
|
||||
private String apiKey = "sk-6f015483467b42c899233139feacfe11";
|
||||
|
||||
/**
|
||||
* Base URL (e.g., https://api.deepseek.com)
|
||||
*/
|
||||
private String baseUrl = "https://api.deepseek.com";
|
||||
|
||||
/**
|
||||
* Model name (e.g., deepseek-chat)
|
||||
*/
|
||||
private String model = "deepseek-reasoner";
|
||||
|
||||
/**
|
||||
* Max tokens
|
||||
*/
|
||||
private Integer maxTokens = 2048;
|
||||
|
||||
/**
|
||||
* Temperature
|
||||
*/
|
||||
private Double temperature = 0.7;
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
package cn.edu.hnu.artifactai.controller;
|
||||
|
||||
import cn.edu.hnu.artifactai.service.IAiChatService;
|
||||
import cn.edu.hnu.artifactcommon.result.Result;
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/ai")
|
||||
public class AiController {
|
||||
|
||||
@Autowired
|
||||
private IAiChatService aiChatService;
|
||||
|
||||
@PostMapping("/chat")
|
||||
public Result<ChatResponse> chat(@RequestBody ChatRequest request) {
|
||||
String response = aiChatService.chat(request.getPrompt(), request.getSessionId());
|
||||
return Result.success(new ChatResponse(response, request.getSessionId()));
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ChatRequest {
|
||||
private String prompt;
|
||||
private String sessionId;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ChatResponse {
|
||||
private String content;
|
||||
private String sessionId;
|
||||
|
||||
public ChatResponse(String content, String sessionId) {
|
||||
this.content = content;
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
package cn.edu.hnu.artifactai.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("ai_chat_history")
|
||||
public class AiChatHistory implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private String sessionId;
|
||||
|
||||
private Long userId;
|
||||
|
||||
private String userInput;
|
||||
|
||||
private String aiResponse;
|
||||
|
||||
private String model;
|
||||
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
package cn.edu.hnu.artifactai.mapper;
|
||||
|
||||
import cn.edu.hnu.artifactai.entity.AiChatHistory;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface AiChatHistoryMapper extends BaseMapper<AiChatHistory> {
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
package cn.edu.hnu.artifactai.service;
|
||||
|
||||
import cn.edu.hnu.artifactai.entity.AiChatHistory;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
public interface IAiChatService extends IService<AiChatHistory> {
|
||||
|
||||
/**
|
||||
* Chat with AI
|
||||
* @param prompt User input
|
||||
* @param sessionId Session ID (optional)
|
||||
* @return AI response
|
||||
*/
|
||||
String chat(String prompt, String sessionId);
|
||||
}
|
||||
@ -1,58 +0,0 @@
|
||||
package cn.edu.hnu.artifactai.service.impl;
|
||||
|
||||
import cn.edu.hnu.artifactai.client.DeepSeekClient;
|
||||
import cn.edu.hnu.artifactai.config.DeepSeekProperties;
|
||||
import cn.edu.hnu.artifactai.entity.AiChatHistory;
|
||||
import cn.edu.hnu.artifactai.mapper.AiChatHistoryMapper;
|
||||
import cn.edu.hnu.artifactai.service.IAiChatService;
|
||||
import cn.edu.hnu.artifactcommon.context.UserContext;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class AiChatServiceImpl extends ServiceImpl<AiChatHistoryMapper, AiChatHistory> implements IAiChatService {
|
||||
|
||||
@Autowired
|
||||
private DeepSeekClient deepSeekClient;
|
||||
|
||||
@Autowired
|
||||
private DeepSeekProperties deepSeekProperties;
|
||||
|
||||
@Override
|
||||
public String chat(String prompt, String sessionId) {
|
||||
// 1. Check/Generate Session ID
|
||||
if (sessionId == null || sessionId.isEmpty()) {
|
||||
sessionId = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
// 2. Call AI
|
||||
// In a real app, you might want to fetch previous context for this session
|
||||
// For now, we just do a single-turn chat with the model
|
||||
String response = deepSeekClient.chat(prompt);
|
||||
|
||||
// 3. Save History
|
||||
AiChatHistory history = new AiChatHistory();
|
||||
history.setSessionId(sessionId);
|
||||
history.setUserInput(prompt);
|
||||
history.setAiResponse(response);
|
||||
history.setModel(deepSeekProperties.getModel());
|
||||
history.setCreateTime(LocalDateTime.now());
|
||||
|
||||
try {
|
||||
Long userId = UserContext.getCurrentUserId();
|
||||
if (userId != null) {
|
||||
history.setUserId(userId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Ignore if user context is not available
|
||||
}
|
||||
|
||||
this.save(history);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
wrapperVersion=3.3.4
|
||||
distributionType=only-script
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip
|
||||
@ -1,8 +0,0 @@
|
||||
package cn.edu.hnu;
|
||||
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
|
||||
@SpringBootConfiguration
|
||||
public class RootBootConfiguration {
|
||||
}
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
package cn.edu.hnu.artifactcommon;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class ArtifactCommonApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ArtifactCommonApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package cn.edu.hnu.artifactcommon;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Result {
|
||||
private Boolean success;
|
||||
private String errorMsg;
|
||||
private Object data;
|
||||
private Long total;
|
||||
|
||||
public static Result ok(){
|
||||
return new Result(true, null, null, null);
|
||||
}
|
||||
public static Result ok(Object data){
|
||||
return new Result(true, null, data, null);
|
||||
}
|
||||
public static Result ok(List<?> data, Long total){
|
||||
return new Result(true, null, data, total);
|
||||
}
|
||||
public static Result fail(String errorMsg){
|
||||
return new Result(false, errorMsg, null, null);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
wrapperVersion=3.3.4
|
||||
distributionType=only-script
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip
|
||||
@ -0,0 +1,13 @@
|
||||
package cn.edu.hnu.artifactknowledge;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class ArtifactKnowledgeApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ArtifactKnowledgeApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
package cn.edu.hnu.artifactknowledge.connector;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.neo4j.core.Neo4jClient;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class Neo4jConnector {
|
||||
|
||||
@Autowired
|
||||
private Neo4jClient neo4jClient;
|
||||
|
||||
public List<Map<String, Object>> query(String cypher) {
|
||||
return query(cypher, Collections.emptyMap());
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> query(String cypher, Map<String, Object> params) {
|
||||
if (params == null || params.isEmpty()) {
|
||||
return (List<Map<String, Object>>) neo4jClient.query(cypher).fetch().all();
|
||||
}
|
||||
return (List<Map<String, Object>>) neo4jClient.query(cypher).bindAll(params).fetch().all();
|
||||
}
|
||||
|
||||
public void execute(String cypher, Map<String, Object> params) {
|
||||
if (params == null || params.isEmpty()) {
|
||||
neo4jClient.query(cypher).run();
|
||||
} else {
|
||||
neo4jClient.query(cypher).bindAll(params).run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,76 +0,0 @@
|
||||
package cn.edu.hnu.artifactknowledge.connector;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
@Component
|
||||
public class Neo4jHttpConnector {
|
||||
|
||||
@Value("${neo4j.http.base-url:http://localhost:7474}")
|
||||
private String baseUrl;
|
||||
|
||||
@Value("${neo4j.http.commit-path:/db/neo4j/tx/commit}")
|
||||
private String commitPath;
|
||||
|
||||
@Value("${spring.neo4j.authentication.username}")
|
||||
private String username;
|
||||
|
||||
@Value("${spring.neo4j.authentication.password}")
|
||||
private String password;
|
||||
|
||||
private final RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
public List<Map<String, Object>> query(String cypher, Map<String, Object> params) {
|
||||
String url = baseUrl + commitPath;
|
||||
Map<String, Object> payload = new HashMap<>();
|
||||
Map<String, Object> stmt = new HashMap<>();
|
||||
stmt.put("statement", cypher);
|
||||
stmt.put("parameters", params == null ? Map.of() : params);
|
||||
payload.put("statements", List.of(stmt));
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
String auth = username + ":" + password;
|
||||
String encoded = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
|
||||
headers.set("Authorization", "Basic " + encoded);
|
||||
|
||||
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(payload, headers);
|
||||
Map<?, ?> resp = restTemplate.postForObject(url, entity, Map.class);
|
||||
if (resp == null) {
|
||||
return List.of();
|
||||
}
|
||||
List<?> results = (List<?>) resp.get("results");
|
||||
if (results == null || results.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
Map<?, ?> first = (Map<?, ?>) results.get(0);
|
||||
List<?> columns = (List<?>) first.get("columns");
|
||||
List<?> data = (List<?>) first.get("data");
|
||||
List<Map<String, Object>> out = new ArrayList<>();
|
||||
if (columns == null || data == null) {
|
||||
return out;
|
||||
}
|
||||
for (Object d : data) {
|
||||
Map<?, ?> rowObj = (Map<?, ?>) d;
|
||||
List<?> row = (List<?>) rowObj.get("row");
|
||||
Map<String, Object> m = new HashMap<>();
|
||||
for (int i = 0; i < columns.size() && i < row.size(); i++) {
|
||||
String col = String.valueOf(columns.get(i));
|
||||
m.put(col, row.get(i));
|
||||
}
|
||||
out.add(m);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
@ -1,63 +0,0 @@
|
||||
package cn.edu.hnu.artifactknowledge.controller;
|
||||
|
||||
import cn.edu.hnu.artifactcommon.result.Result;
|
||||
import cn.edu.hnu.artifactknowledge.dto.GraphQueryDTO;
|
||||
import cn.edu.hnu.artifactknowledge.service.IKnowledgeGraphService;
|
||||
import cn.edu.hnu.artifactknowledge.service.IKnowledgeGraphHttpService;
|
||||
import cn.edu.hnu.artifactknowledge.vo.GraphVO;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping({"/knowledge/gstore", "/knowledge/neo4j", "/api/knowledge/neo4j"})
|
||||
public class Neo4jController {
|
||||
|
||||
@Autowired
|
||||
private IKnowledgeGraphService knowledgeGraphService;
|
||||
|
||||
@Autowired
|
||||
private IKnowledgeGraphHttpService knowledgeGraphHttpService;
|
||||
|
||||
@PostMapping("/query")
|
||||
public cn.edu.hnu.artifactcommon.result.Result<String> query(@RequestBody GraphQueryDTO queryDTO) {
|
||||
String cypher = queryDTO.getCypher();
|
||||
if (cypher == null || cypher.isBlank()) {
|
||||
cypher = queryDTO.getSparql();
|
||||
}
|
||||
if (cypher == null || cypher.isBlank()) {
|
||||
return cn.edu.hnu.artifactcommon.result.Result.error(400, "cypher 不能为空");
|
||||
}
|
||||
return cn.edu.hnu.artifactcommon.result.Result.success(knowledgeGraphService.query(cypher));
|
||||
}
|
||||
|
||||
@GetMapping("/test")
|
||||
public cn.edu.hnu.artifactcommon.result.Result<String> test() {
|
||||
String cypher = "MATCH (n) RETURN n LIMIT 1";
|
||||
return cn.edu.hnu.artifactcommon.result.Result.success(knowledgeGraphService.query(cypher));
|
||||
}
|
||||
|
||||
@GetMapping("/graph")
|
||||
public cn.edu.hnu.artifactcommon.result.Result<GraphVO> getGraph(@RequestParam String relicName) {
|
||||
return cn.edu.hnu.artifactcommon.result.Result.success(knowledgeGraphService.getRelicGraph(relicName));
|
||||
}
|
||||
|
||||
@GetMapping("/httpGraph")
|
||||
public cn.edu.hnu.artifactcommon.result.Result<GraphVO> getHttpGraph(@RequestParam String relicName) {
|
||||
return cn.edu.hnu.artifactcommon.result.Result.success(knowledgeGraphHttpService.getRelicGraph(relicName));
|
||||
}
|
||||
|
||||
@PostMapping("/import")
|
||||
public cn.edu.hnu.artifactcommon.result.Result<String> importArtifacts(@RequestParam String path) {
|
||||
try {
|
||||
knowledgeGraphService.importArtifacts(path);
|
||||
return cn.edu.hnu.artifactcommon.result.Result.success("Import started/completed successfully.");
|
||||
} catch (Exception e) {
|
||||
return cn.edu.hnu.artifactcommon.result.Result.error(500, "Import failed: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
package cn.edu.hnu.artifactknowledge.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class GraphQueryDTO {
|
||||
private String cypher;
|
||||
private String sparql;
|
||||
}
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
package cn.edu.hnu.artifactknowledge.service;
|
||||
|
||||
import cn.edu.hnu.artifactknowledge.vo.GraphVO;
|
||||
|
||||
public interface IKnowledgeGraphHttpService {
|
||||
GraphVO getRelicGraph(String relicName);
|
||||
}
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
package cn.edu.hnu.artifactknowledge.service;
|
||||
|
||||
import cn.edu.hnu.artifactknowledge.vo.GraphVO;
|
||||
|
||||
public interface IKnowledgeGraphService {
|
||||
String query(String cypher);
|
||||
|
||||
GraphVO getRelicGraph(String relicName);
|
||||
|
||||
void importArtifacts(String directoryPath);
|
||||
}
|
||||
|
||||
@ -1,99 +0,0 @@
|
||||
package cn.edu.hnu.artifactknowledge.service.impl;
|
||||
|
||||
import cn.edu.hnu.artifactknowledge.connector.Neo4jHttpConnector;
|
||||
import cn.edu.hnu.artifactknowledge.service.IKnowledgeGraphHttpService;
|
||||
import cn.edu.hnu.artifactknowledge.vo.GraphLink;
|
||||
import cn.edu.hnu.artifactknowledge.vo.GraphNode;
|
||||
import cn.edu.hnu.artifactknowledge.vo.GraphVO;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
public class KnowledgeGraphHttpServiceImpl implements IKnowledgeGraphHttpService {
|
||||
|
||||
@Autowired
|
||||
private Neo4jHttpConnector httpConnector;
|
||||
|
||||
@Override
|
||||
public GraphVO getRelicGraph(String relicName) {
|
||||
String cypher = ""
|
||||
+ "MATCH (a:Artifact) "
|
||||
+ "WHERE toString(coalesce(a.name,'')) = $relicName "
|
||||
+ "MATCH (a)-[r]-(n) "
|
||||
+ "WHERE type(r) <> 'HAS_IMAGE' "
|
||||
+ "AND NOT (n:Artifact AND toString(coalesce(n.name,'')) <> $relicName) "
|
||||
+ "RETURN elementId(a) AS nId, elementId(n) AS mId, labels(a) AS nLabels, labels(n) AS mLabels, "
|
||||
+ "a AS nProps, n AS mProps, type(r) AS rType, elementId(a) AS rStart, elementId(n) AS rEnd "
|
||||
+ "LIMIT 200";
|
||||
|
||||
List<Map<String, Object>> rows = httpConnector.query(cypher, Map.of("relicName", relicName));
|
||||
Map<String, GraphNode> nodeMap = new HashMap<>();
|
||||
List<GraphLink> links = new ArrayList<>();
|
||||
Set<String> linkSet = new HashSet<>();
|
||||
|
||||
for (Map<String, Object> row : rows) {
|
||||
String nId = String.valueOf(row.get("nId"));
|
||||
String mId = String.valueOf(row.get("mId"));
|
||||
Map<String, Object> nProps = castMap(row.get("nProps"));
|
||||
Map<String, Object> mProps = castMap(row.get("mProps"));
|
||||
String rType = String.valueOf(row.get("rType"));
|
||||
String rStart = String.valueOf(row.get("rStart"));
|
||||
String rEnd = String.valueOf(row.get("rEnd"));
|
||||
|
||||
GraphNode nNode = toGraphNode(nId, nProps, relicName);
|
||||
GraphNode mNode = toGraphNode(mId, mProps, relicName);
|
||||
nodeMap.putIfAbsent(nNode.getId(), nNode);
|
||||
nodeMap.putIfAbsent(mNode.getId(), mNode);
|
||||
|
||||
String linkKey = rStart + "|" + rEnd + "|" + rType;
|
||||
if (!linkSet.contains(linkKey)) {
|
||||
GraphLink link = new GraphLink();
|
||||
link.setSource(rStart);
|
||||
link.setTarget(rEnd);
|
||||
link.setValue(rType);
|
||||
links.add(link);
|
||||
linkSet.add(linkKey);
|
||||
}
|
||||
}
|
||||
return new GraphVO(new ArrayList<>(nodeMap.values()), links);
|
||||
}
|
||||
|
||||
private Map<String, Object> castMap(Object v) {
|
||||
if (v instanceof Map) {
|
||||
Map<?, ?> in = (Map<?, ?>) v;
|
||||
Map<String, Object> out = new HashMap<>();
|
||||
for (Map.Entry<?, ?> e : in.entrySet()) {
|
||||
out.put(String.valueOf(e.getKey()), e.getValue());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
private GraphNode toGraphNode(String id, Map<String, Object> props, String relicName) {
|
||||
String name = extractName(props);
|
||||
boolean isMain = false;
|
||||
if (relicName != null && !relicName.isBlank() && name != null) {
|
||||
isMain = name.equals(relicName);
|
||||
}
|
||||
GraphNode node = new GraphNode();
|
||||
node.setId(id);
|
||||
node.setName(name);
|
||||
node.setCategory(isMain ? 0 : 1);
|
||||
node.setSymbolSize(isMain ? 80.0 : 60.0);
|
||||
node.setValue(name);
|
||||
return node;
|
||||
}
|
||||
|
||||
private String extractName(Map<String, Object> props) {
|
||||
for (String key : List.of("name", "relicName", "label", "title")) {
|
||||
Object v = props.get(key);
|
||||
if (v != null) {
|
||||
return String.valueOf(v);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -1,304 +0,0 @@
|
||||
package cn.edu.hnu.artifactknowledge.service.impl;
|
||||
|
||||
import cn.edu.hnu.artifactknowledge.connector.Neo4jConnector;
|
||||
import cn.edu.hnu.artifactknowledge.service.IKnowledgeGraphService;
|
||||
import cn.edu.hnu.artifactknowledge.vo.GraphLink;
|
||||
import cn.edu.hnu.artifactknowledge.vo.GraphNode;
|
||||
import cn.edu.hnu.artifactknowledge.vo.GraphVO;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.neo4j.driver.Value;
|
||||
import org.neo4j.driver.types.Node;
|
||||
import org.neo4j.driver.types.Relationship;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class KnowledgeGraphServiceImpl implements IKnowledgeGraphService {
|
||||
|
||||
@Autowired
|
||||
private Neo4jConnector neo4jConnector;
|
||||
|
||||
@Override
|
||||
public void importArtifacts(String directoryPath) {
|
||||
log.info("Starting artifact import from: {}", directoryPath);
|
||||
try (Stream<Path> paths = Files.walk(Paths.get(directoryPath))) {
|
||||
paths.filter(Files::isRegularFile)
|
||||
.filter(p -> p.toString().endsWith(".json"))
|
||||
.forEach(this::processJsonFile);
|
||||
} catch (IOException e) {
|
||||
log.error("Error reading files from directory: {}", directoryPath, e);
|
||||
throw new RuntimeException("Import failed", e);
|
||||
}
|
||||
log.info("Import completed.");
|
||||
}
|
||||
|
||||
private void processJsonFile(Path filePath) {
|
||||
try {
|
||||
String content = Files.readString(filePath);
|
||||
JSONObject json = JSON.parseObject(content);
|
||||
importArtifactData(json);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process file: {}", filePath, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void importArtifactData(JSONObject json) {
|
||||
String uuid = json.getString("uuid");
|
||||
JSONObject info = json.getJSONObject("artifact_info");
|
||||
if (uuid == null || info == null) return;
|
||||
|
||||
String name = info.getString("文物名");
|
||||
String relicNo = info.getString("文物号");
|
||||
String category = info.getString("分类");
|
||||
String era = info.getString("年代");
|
||||
String detailUrl = json.getString("detail_url");
|
||||
|
||||
// 1. Merge Artifact Node
|
||||
String createArtifactCypher = """
|
||||
MERGE (a:Artifact {uuid: $uuid})
|
||||
SET a.name = $name,
|
||||
a.relicNo = $relicNo,
|
||||
a.detailUrl = $detailUrl
|
||||
""";
|
||||
Map<String, Object> artifactParams = new HashMap<>();
|
||||
artifactParams.put("uuid", uuid);
|
||||
artifactParams.put("name", name);
|
||||
artifactParams.put("relicNo", relicNo);
|
||||
artifactParams.put("detailUrl", detailUrl);
|
||||
neo4jConnector.execute(createArtifactCypher, artifactParams);
|
||||
|
||||
// 2. Merge Category Node and Relationship
|
||||
if (category != null && !category.isBlank()) {
|
||||
String categoryCypher = """
|
||||
MATCH (a:Artifact {uuid: $uuid})
|
||||
MERGE (c:Category {name: $category})
|
||||
MERGE (a)-[:BELONGS_TO]->(c)
|
||||
""";
|
||||
neo4jConnector.execute(categoryCypher, Map.of("uuid", uuid, "category", category));
|
||||
}
|
||||
|
||||
// 3. Merge Era Node and Relationship
|
||||
if (era != null && !era.isBlank()) {
|
||||
String eraCypher = """
|
||||
MATCH (a:Artifact {uuid: $uuid})
|
||||
MERGE (e:Era {name: $era})
|
||||
MERGE (a)-[:FROM_ERA]->(e)
|
||||
""";
|
||||
neo4jConnector.execute(eraCypher, Map.of("uuid", uuid, "era", era));
|
||||
}
|
||||
|
||||
// 4. Handle Colors
|
||||
JSONArray colors = info.getJSONArray("颜色");
|
||||
if (colors != null) {
|
||||
for (int i = 0; i < colors.size(); i++) {
|
||||
JSONObject color = colors.getJSONObject(i);
|
||||
String code = color.getString("code");
|
||||
String background = color.getString("background");
|
||||
if (code != null) {
|
||||
String colorCypher = """
|
||||
MATCH (a:Artifact {uuid: $uuid})
|
||||
MERGE (col:Color {code: $code})
|
||||
SET col.background = $background
|
||||
MERGE (a)-[:HAS_COLOR]->(col)
|
||||
""";
|
||||
neo4jConnector.execute(colorCypher, Map.of("uuid", uuid, "code", code, "background", background != null ? background : ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Handle Same Category Artifacts (Recommendations)
|
||||
JSONArray sameCategory = json.getJSONArray("same_category_artifacts");
|
||||
if (sameCategory != null) {
|
||||
for (int i = 0; i < sameCategory.size(); i++) {
|
||||
JSONObject other = sameCategory.getJSONObject(i);
|
||||
String otherUuid = other.getString("uuid");
|
||||
String otherName = other.getString("name");
|
||||
|
||||
if (otherUuid != null) {
|
||||
String relationCypher = """
|
||||
MATCH (a:Artifact {uuid: $uuid})
|
||||
MERGE (o:Artifact {uuid: $otherUuid})
|
||||
ON CREATE SET o.name = $otherName
|
||||
MERGE (a)-[:RELATED_TO]->(o)
|
||||
""";
|
||||
neo4jConnector.execute(relationCypher, Map.of("uuid", uuid, "otherUuid", otherUuid, "otherName", otherName != null ? otherName : ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String query(String cypher) {
|
||||
List<Map<String, Object>> rows = neo4jConnector.query(cypher);
|
||||
List<Map<String, Object>> normalized = new ArrayList<>(rows.size());
|
||||
for (Map<String, Object> row : rows) {
|
||||
Map<String, Object> out = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, Object> entry : row.entrySet()) {
|
||||
out.put(entry.getKey(), normalizeValue(entry.getValue()));
|
||||
}
|
||||
normalized.add(out);
|
||||
}
|
||||
return JSONArray.toJSONString(normalized);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphVO getRelicGraph(String relicName) {
|
||||
String cypher = """
|
||||
MATCH (a:Artifact)
|
||||
WHERE toString(coalesce(a.name,'')) = $relicName
|
||||
WITH collect(a)[0] AS a
|
||||
OPTIONAL MATCH (a)-[r1:FROM_ERA]->(e:Era)
|
||||
RETURN a AS n, r1 AS r, e AS m
|
||||
UNION
|
||||
MATCH (a:Artifact)
|
||||
WHERE toString(coalesce(a.name,'')) = $relicName
|
||||
WITH collect(a)[0] AS a
|
||||
OPTIONAL MATCH (a)-[r2:BELONGS_TO]->(c:Category)
|
||||
RETURN a AS n, r2 AS r, c AS m
|
||||
""";
|
||||
|
||||
List<Map<String, Object>> rows = neo4jConnector.query(cypher, Map.of("relicName", relicName));
|
||||
|
||||
Map<String, GraphNode> nodeMap = new HashMap<>();
|
||||
List<GraphLink> links = new ArrayList<>();
|
||||
|
||||
for (Map<String, Object> row : rows) {
|
||||
Object nObj = row.get("n");
|
||||
Object mObj = row.get("m");
|
||||
Object rObj = row.get("r");
|
||||
if (!(nObj instanceof Node) || !(mObj instanceof Node) || !(rObj instanceof Relationship)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Node n = (Node) nObj;
|
||||
Node m = (Node) mObj;
|
||||
Relationship r = (Relationship) rObj;
|
||||
|
||||
GraphNode nNode = toGraphNode(n, relicName);
|
||||
GraphNode mNode = toGraphNode(m, relicName);
|
||||
|
||||
nodeMap.putIfAbsent(nNode.getId(), nNode);
|
||||
nodeMap.putIfAbsent(mNode.getId(), mNode);
|
||||
|
||||
GraphLink link = new GraphLink();
|
||||
link.setSource(nNode.getId());
|
||||
link.setTarget(mNode.getId());
|
||||
link.setValue(r.type());
|
||||
links.add(link);
|
||||
}
|
||||
|
||||
return new GraphVO(new ArrayList<>(nodeMap.values()), links);
|
||||
}
|
||||
|
||||
private GraphNode toGraphNode(Node node, String relicName) {
|
||||
String id = node.elementId();
|
||||
String name = extractName(node);
|
||||
|
||||
boolean isMain = false;
|
||||
if (relicName != null && !relicName.isBlank()) {
|
||||
for (String key : List.of("name", "relicName", "label", "title")) {
|
||||
if (node.containsKey(key) && node.get(key) != null) {
|
||||
String v = String.valueOf(node.get(key).asObject());
|
||||
if (v.equals(relicName)) {
|
||||
isMain = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GraphNode graphNode = new GraphNode();
|
||||
graphNode.setId(id);
|
||||
graphNode.setName(name);
|
||||
graphNode.setCategory(isMain ? 0 : 1);
|
||||
graphNode.setSymbolSize(isMain ? 20.0 : 10.0);
|
||||
graphNode.setValue(name);
|
||||
return graphNode;
|
||||
}
|
||||
|
||||
private String extractName(Node node) {
|
||||
for (String key : List.of("name", "label", "title")) {
|
||||
if (node.containsKey(key) && node.get(key) != null) {
|
||||
Value v = node.get(key);
|
||||
if (!v.isNull()) {
|
||||
return String.valueOf(v.asObject());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!node.labels().iterator().hasNext()) {
|
||||
return node.elementId();
|
||||
}
|
||||
return node.labels().iterator().next() + ":" + node.elementId();
|
||||
}
|
||||
|
||||
private Object normalizeValue(Object value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value instanceof Node) {
|
||||
Node node = (Node) value;
|
||||
Map<String, Object> out = new LinkedHashMap<>();
|
||||
out.put("elementId", node.elementId());
|
||||
List<String> labels = new ArrayList<>();
|
||||
node.labels().forEach(labels::add);
|
||||
out.put("labels", labels);
|
||||
out.put("properties", node.asMap());
|
||||
return out;
|
||||
}
|
||||
if (value instanceof Relationship) {
|
||||
Relationship rel = (Relationship) value;
|
||||
Map<String, Object> out = new LinkedHashMap<>();
|
||||
out.put("elementId", rel.elementId());
|
||||
out.put("type", rel.type());
|
||||
out.put("startNodeId", rel.startNodeId());
|
||||
out.put("endNodeId", rel.endNodeId());
|
||||
out.put("properties", rel.asMap());
|
||||
return out;
|
||||
}
|
||||
if (value instanceof Map) {
|
||||
Map<?, ?> map = (Map<?, ?>) value;
|
||||
Map<String, Object> out = new LinkedHashMap<>();
|
||||
for (Map.Entry<?, ?> entry : map.entrySet()) {
|
||||
out.put(String.valueOf(entry.getKey()), normalizeValue(entry.getValue()));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
if (value instanceof Iterable) {
|
||||
List<Object> out = new ArrayList<>();
|
||||
for (Object v : (Iterable<?>) value) {
|
||||
out.add(normalizeValue(v));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
if (value instanceof Object[]) {
|
||||
List<Object> out = new ArrayList<>();
|
||||
for (Object v : (Object[]) value) {
|
||||
out.add(normalizeValue(v));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
if (value instanceof Value) {
|
||||
Value v = (Value) value;
|
||||
if (v.isNull()) {
|
||||
return null;
|
||||
}
|
||||
return normalizeValue(v.asObject());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
package cn.edu.hnu.artifactknowledge.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class GraphLink {
|
||||
private String source;
|
||||
private String target;
|
||||
private String value; // The relationship name
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
package cn.edu.hnu.artifactknowledge.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class GraphNode {
|
||||
private String id;
|
||||
private String name;
|
||||
private int category; // 0: main node, 1: property, 2: value (simplified)
|
||||
private double symbolSize;
|
||||
private String value;
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
package cn.edu.hnu.artifactknowledge.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class GraphVO {
|
||||
private List<GraphNode> nodes;
|
||||
private List<GraphLink> links;
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
server:
|
||||
port: 8083
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: artifact-knowledge
|
||||
neo4j:
|
||||
uri: bolt://localhost:7687
|
||||
authentication:
|
||||
username: neo4j
|
||||
password: artifactllm
|
||||
neo4j:
|
||||
http:
|
||||
base-url: http://localhost:7474
|
||||
commit-path: /db/neo4j/tx/commit
|
||||
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue