diff --git a/backend/services/knowledge-graph-service/src/main/java/com/datamate/knowledgegraph/application/GraphRelationService.java b/backend/services/knowledge-graph-service/src/main/java/com/datamate/knowledgegraph/application/GraphRelationService.java
new file mode 100644
index 0000000..891665b
--- /dev/null
+++ b/backend/services/knowledge-graph-service/src/main/java/com/datamate/knowledgegraph/application/GraphRelationService.java
@@ -0,0 +1,167 @@
+package com.datamate.knowledgegraph.application;
+
+import com.datamate.common.infrastructure.exception.BusinessException;
+import com.datamate.common.infrastructure.exception.SystemErrorCode;
+import com.datamate.common.interfaces.PagedResponse;
+import com.datamate.knowledgegraph.domain.model.RelationDetail;
+import com.datamate.knowledgegraph.domain.repository.GraphEntityRepository;
+import com.datamate.knowledgegraph.domain.repository.GraphRelationRepository;
+import com.datamate.knowledgegraph.infrastructure.exception.KnowledgeGraphErrorCode;
+import com.datamate.knowledgegraph.interfaces.dto.CreateRelationRequest;
+import com.datamate.knowledgegraph.interfaces.dto.RelationVO;
+import com.datamate.knowledgegraph.interfaces.dto.UpdateRelationRequest;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * 知识图谱关系业务服务。
+ *
+ * 信任边界说明:本服务仅通过内网被 API Gateway / Java 后端调用,
+ * 网关层已完成用户身份认证与权限校验,服务层不再重复鉴权,
+ * 仅校验 graphId 格式(防 Cypher 注入)与数据完整性约束。
+ */
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class GraphRelationService {
+
+ /** 分页偏移量上限,防止深翻页导致 Neo4j 性能退化。 */
+ private static final long MAX_SKIP = 100_000L;
+
+ private static final Pattern UUID_PATTERN = Pattern.compile(
+ "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
+ );
+
+ private final GraphRelationRepository relationRepository;
+ private final GraphEntityRepository entityRepository;
+
+ @Transactional
+ public RelationVO createRelation(String graphId, CreateRelationRequest request) {
+ validateGraphId(graphId);
+
+ // 校验源实体存在
+ entityRepository.findByIdAndGraphId(request.getSourceEntityId(), graphId)
+ .orElseThrow(() -> BusinessException.of(
+ KnowledgeGraphErrorCode.ENTITY_NOT_FOUND, "源实体不存在"));
+
+ // 校验目标实体存在
+ entityRepository.findByIdAndGraphId(request.getTargetEntityId(), graphId)
+ .orElseThrow(() -> BusinessException.of(
+ KnowledgeGraphErrorCode.ENTITY_NOT_FOUND, "目标实体不存在"));
+
+ RelationDetail detail = relationRepository.create(
+ graphId,
+ request.getSourceEntityId(),
+ request.getTargetEntityId(),
+ request.getRelationType(),
+ request.getProperties(),
+ request.getWeight(),
+ request.getSourceId(),
+ request.getConfidence()
+ ).orElseThrow(() -> BusinessException.of(
+ KnowledgeGraphErrorCode.INVALID_RELATION, "关系创建失败"));
+
+ log.info("Relation created: id={}, graphId={}, type={}, source={} -> target={}",
+ detail.getId(), graphId, request.getRelationType(),
+ request.getSourceEntityId(), request.getTargetEntityId());
+ return toVO(detail);
+ }
+
+ public RelationVO getRelation(String graphId, String relationId) {
+ validateGraphId(graphId);
+ RelationDetail detail = relationRepository.findByIdAndGraphId(relationId, graphId)
+ .orElseThrow(() -> BusinessException.of(KnowledgeGraphErrorCode.RELATION_NOT_FOUND));
+ return toVO(detail);
+ }
+
+ public PagedResponse listRelations(String graphId, String type, int page, int size) {
+ validateGraphId(graphId);
+
+ int safePage = Math.max(0, page);
+ int safeSize = Math.max(1, Math.min(size, 200));
+ long skip = (long) safePage * safeSize;
+ if (skip > MAX_SKIP) {
+ throw BusinessException.of(SystemErrorCode.INVALID_PARAMETER, "分页偏移量过大");
+ }
+
+ List details = relationRepository.findByGraphId(graphId, type, skip, safeSize);
+ long total = relationRepository.countByGraphId(graphId, type);
+ long totalPages = safeSize > 0 ? (total + safeSize - 1) / safeSize : 0;
+
+ List content = details.stream().map(GraphRelationService::toVO).toList();
+ return PagedResponse.of(content, safePage, total, totalPages);
+ }
+
+ @Transactional
+ public RelationVO updateRelation(String graphId, String relationId, UpdateRelationRequest request) {
+ validateGraphId(graphId);
+
+ // 确认关系存在
+ relationRepository.findByIdAndGraphId(relationId, graphId)
+ .orElseThrow(() -> BusinessException.of(KnowledgeGraphErrorCode.RELATION_NOT_FOUND));
+
+ RelationDetail detail = relationRepository.update(
+ relationId, graphId,
+ request.getRelationType(),
+ request.getProperties(),
+ request.getWeight(),
+ request.getConfidence()
+ ).orElseThrow(() -> BusinessException.of(KnowledgeGraphErrorCode.RELATION_NOT_FOUND));
+
+ log.info("Relation updated: id={}, graphId={}", relationId, graphId);
+ return toVO(detail);
+ }
+
+ @Transactional
+ public void deleteRelation(String graphId, String relationId) {
+ validateGraphId(graphId);
+
+ // 确认关系存在
+ relationRepository.findByIdAndGraphId(relationId, graphId)
+ .orElseThrow(() -> BusinessException.of(KnowledgeGraphErrorCode.RELATION_NOT_FOUND));
+
+ long deleted = relationRepository.deleteByIdAndGraphId(relationId, graphId);
+ if (deleted <= 0) {
+ throw BusinessException.of(KnowledgeGraphErrorCode.RELATION_NOT_FOUND);
+ }
+ log.info("Relation deleted: id={}, graphId={}", relationId, graphId);
+ }
+
+ // -----------------------------------------------------------------------
+ // 领域对象 → 视图对象 转换
+ // -----------------------------------------------------------------------
+
+ private static RelationVO toVO(RelationDetail detail) {
+ return RelationVO.builder()
+ .id(detail.getId())
+ .sourceEntityId(detail.getSourceEntityId())
+ .sourceEntityName(detail.getSourceEntityName())
+ .sourceEntityType(detail.getSourceEntityType())
+ .targetEntityId(detail.getTargetEntityId())
+ .targetEntityName(detail.getTargetEntityName())
+ .targetEntityType(detail.getTargetEntityType())
+ .relationType(detail.getRelationType())
+ .properties(detail.getProperties())
+ .weight(detail.getWeight())
+ .confidence(detail.getConfidence())
+ .sourceId(detail.getSourceId())
+ .graphId(detail.getGraphId())
+ .createdAt(detail.getCreatedAt())
+ .build();
+ }
+
+ /**
+ * 校验 graphId 格式(UUID)。
+ * 防止恶意构造的 graphId 注入 Cypher 查询。
+ */
+ private void validateGraphId(String graphId) {
+ if (graphId == null || !UUID_PATTERN.matcher(graphId).matches()) {
+ throw BusinessException.of(SystemErrorCode.INVALID_PARAMETER, "graphId 格式无效");
+ }
+ }
+}
diff --git a/backend/services/knowledge-graph-service/src/main/java/com/datamate/knowledgegraph/domain/model/RelationDetail.java b/backend/services/knowledge-graph-service/src/main/java/com/datamate/knowledgegraph/domain/model/RelationDetail.java
new file mode 100644
index 0000000..71abff1
--- /dev/null
+++ b/backend/services/knowledge-graph-service/src/main/java/com/datamate/knowledgegraph/domain/model/RelationDetail.java
@@ -0,0 +1,54 @@
+package com.datamate.knowledgegraph.domain.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 关系及其端点实体摘要,用于仓储层查询返回。
+ *
+ * 由于 {@link GraphRelation} 使用 {@code @RelationshipProperties} 且仅持有
+ * 目标节点引用,无法完整表达 Cypher 查询返回的"源节点 + 关系 + 目标节点"结构,
+ * 因此使用该领域对象作为仓储层的返回类型。
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class RelationDetail {
+
+ private String id;
+
+ private String sourceEntityId;
+
+ private String sourceEntityName;
+
+ private String sourceEntityType;
+
+ private String targetEntityId;
+
+ private String targetEntityName;
+
+ private String targetEntityType;
+
+ private String relationType;
+
+ @Builder.Default
+ private Map properties = new HashMap<>();
+
+ private Double weight;
+
+ private Double confidence;
+
+ /** 来源数据集/知识库的 ID */
+ private String sourceId;
+
+ private String graphId;
+
+ private LocalDateTime createdAt;
+}
diff --git a/backend/services/knowledge-graph-service/src/main/java/com/datamate/knowledgegraph/domain/repository/GraphRelationRepository.java b/backend/services/knowledge-graph-service/src/main/java/com/datamate/knowledgegraph/domain/repository/GraphRelationRepository.java
new file mode 100644
index 0000000..7e894d3
--- /dev/null
+++ b/backend/services/knowledge-graph-service/src/main/java/com/datamate/knowledgegraph/domain/repository/GraphRelationRepository.java
@@ -0,0 +1,328 @@
+package com.datamate.knowledgegraph.domain.repository;
+
+import com.datamate.knowledgegraph.domain.model.RelationDetail;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.neo4j.driver.Value;
+import org.neo4j.driver.types.MapAccessor;
+import org.springframework.data.neo4j.core.Neo4jClient;
+import org.springframework.stereotype.Repository;
+
+import java.time.LocalDateTime;
+import java.util.*;
+
+/**
+ * 知识图谱关系仓储。
+ *
+ * 由于 {@code GraphRelation} 使用 {@code @RelationshipProperties},
+ * 无法通过 {@code Neo4jRepository} 直接管理,
+ * 因此使用 {@code Neo4jClient} 执行 Cypher 查询实现 CRUD。
+ *
+ * Neo4j 中使用统一的 {@code RELATED_TO} 关系类型,
+ * 语义类型通过 {@code relation_type} 属性区分。
+ * 扩展属性(properties)序列化为 JSON 字符串存储在 {@code properties_json} 属性中。
+ */
+@Repository
+@Slf4j
+@RequiredArgsConstructor
+public class GraphRelationRepository {
+
+ private static final String REL_TYPE = "RELATED_TO";
+ private static final TypeReference