Files
urbanLifeline/.kiro/specs/urbanlifeline-to-pigx-migration/pigx-dify-architecture.md
2026-01-14 15:42:26 +08:00

21 KiB
Raw Blame History

pigx-dify 模块架构设计

1. 模块概述

1.1 定位

pigx-dify 是 pigx 平台的 AI 服务模块,专门用于集成 Dify AI 平台,提供智能体管理、知识库管理和 AI 对话功能。

1.2 核心功能

  • 智能体Agent管理
  • 知识库Knowledge管理
  • AI 对话Chat功能
  • Dify API 集成
  • 流式响应支持SSE

1.3 技术栈

  • Spring Boot 3.5.8
  • Spring Cloud 2025.0.0
  • MyBatis-Plus 3.5.14
  • MySQL 8.0
  • Dify API Client
  • SSE (Server-Sent Events)

2. 模块结构

2.1 Maven 项目结构

<!-- pigx-dify/pom.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.pig4cloud</groupId>
        <artifactId>pigx</artifactId>
        <version>6.4.0</version>
    </parent>

    <artifactId>pigx-dify</artifactId>
    <packaging>pom</packaging>
    <description>Dify AI integration module</description>

    <modules>
        <module>pigx-dify-api</module>
        <module>pigx-dify-biz</module>
    </modules>
</project>

2.2 pigx-dify-api 结构

<!-- pigx-dify-api/pom.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
    <parent>
        <groupId>com.pig4cloud</groupId>
        <artifactId>pigx-dify</artifactId>
        <version>6.4.0</version>
    </parent>

    <artifactId>pigx-dify-api</artifactId>
    <description>Dify API interfaces and entities</description>

    <dependencies>
        <dependency>
            <groupId>com.pig4cloud</groupId>
            <artifactId>pigx-common-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-annotation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
        </dependency>
    </dependencies>
</project>

2.3 pigx-dify-biz 结构

<!-- pigx-dify-biz/pom.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
    <parent>
        <groupId>com.pig4cloud</groupId>
        <artifactId>pigx-dify</artifactId>
        <version>6.4.0</version>
    </parent>

    <artifactId>pigx-dify-biz</artifactId>
    <description>Dify business implementation</description>

    <dependencies>
        <!-- pigx dependencies -->
        <dependency>
            <groupId>com.pig4cloud</groupId>
            <artifactId>pigx-dify-api</artifactId>
        </dependency>
        <dependency>
            <groupId>com.pig4cloud</groupId>
            <artifactId>pigx-common-security</artifactId>
        </dependency>
        <dependency>
            <groupId>com.pig4cloud</groupId>
            <artifactId>pigx-common-log</artifactId>
        </dependency>
        <dependency>
            <groupId>com.pig4cloud</groupId>
            <artifactId>pigx-common-mybatis</artifactId>
        </dependency>
        <dependency>
            <groupId>com.pig4cloud</groupId>
            <artifactId>pigx-common-swagger</artifactId>
        </dependency>

        <!-- Spring Boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>

        <!-- Spring Cloud -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

        <!-- HTTP Client for Dify API -->
        <dependency>
            <groupId>org.apache.httpcomponents.client5</groupId>
            <artifactId>httpclient5</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webflux</artifactId>
        </dependency>

        <!-- SSE Support -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

3. 包结构设计

3.1 pigx-dify-api 包结构

pigx-dify-api/
└── src/main/java/com/pig4cloud/pigx/dify/api/
    ├── entity/               # 实体类
    │   ├── TbAgent.java     # 智能体
    │   ├── TbChat.java      # 聊天会话
    │   ├── TbChatMessage.java # 聊天消息
    │   └── TbKnowledge.java # 知识库
    ├── dto/                  # 数据传输对象
    │   ├── AgentDTO.java
    │   ├── ChatDTO.java
    │   ├── ChatMessageDTO.java
    │   └── KnowledgeDTO.java
    ├── vo/                   # 视图对象
    │   ├── AgentVO.java
    │   ├── ChatVO.java
    │   └── KnowledgeVO.java
    ├── feign/               # Feign接口
    │   └── RemoteDifyService.java
    └── constant/            # 常量定义
        └── DifyConstant.java

3.2 pigx-dify-biz 包结构

pigx-dify-biz/
└── src/main/java/com/pig4cloud/pigx/dify/
    ├── DifyApplication.java          # 启动类
    ├── config/                       # 配置类
    │   ├── DifyConfig.java          # Dify配置
    │   ├── WebConfig.java           # Web配置
    │   └── AsyncConfig.java         # 异步配置
    ├── controller/                   # 控制器
    │   ├── AgentController.java     # 智能体管理
    │   ├── ChatController.java      # 对话管理
    │   └── KnowledgeController.java # 知识库管理
    ├── service/                      # 服务层
    │   ├── AgentService.java
    │   ├── ChatService.java
    │   ├── KnowledgeService.java
    │   └── impl/
    │       ├── AgentServiceImpl.java
    │       ├── ChatServiceImpl.java
    │       └── KnowledgeServiceImpl.java
    ├── mapper/                       # 数据访问层
    │   ├── AgentMapper.java
    │   ├── ChatMapper.java
    │   ├── ChatMessageMapper.java
    │   └── KnowledgeMapper.java
    ├── client/                       # 外部API客户端
    │   ├── DifyApiClient.java       # Dify API客户端
    │   ├── dto/                     # Dify API DTO
    │   │   ├── DifyRequest.java
    │   │   └── DifyResponse.java
    │   └── callback/
    │       └── StreamCallback.java  # 流式回调
    └── handler/                      # 处理器
        ├── SseHandler.java           # SSE处理
        └── GlobalExceptionHandler.java # 全局异常处理

4. 核心代码设计

4.1 启动类

package com.pig4cloud.pigx.dify;

import com.pig4cloud.pigx.common.feign.annotation.EnablePigxFeignClients;
import com.pig4cloud.pigx.common.security.annotation.EnablePigxResourceServer;
import com.pig4cloud.pigx.common.swagger.annotation.EnablePigxDoc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnablePigxDoc
@EnablePigxResourceServer
@EnablePigxFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class DifyApplication {
    public static void main(String[] args) {
        SpringApplication.run(DifyApplication.class, args);
    }
}

4.2 配置类

package com.pig4cloud.pigx.dify.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
@ConfigurationProperties(prefix = "dify")
public class DifyConfig {

    /**
     * Dify API基础URL
     */
    private String apiBaseUrl = "https://api.dify.ai/v1";

    /**
     * 默认API Key可被智能体配置覆盖
     */
    private String defaultApiKey;

    /**
     * 连接超时(毫秒)
     */
    private Integer connectTimeout = 10000;

    /**
     * 读取超时(毫秒)
     */
    private Integer readTimeout = 30000;

    /**
     * 流式响应超时(毫秒)
     */
    private Integer streamTimeout = 60000;

    /**
     * 是否启用调试日志
     */
    private Boolean debug = false;
}

4.3 实体设计

package com.pig4cloud.pigx.dify.api.entity;

import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.time.LocalDateTime;

@Data
@TableName("tb_agent")
@EqualsAndHashCode(callSuper = true)
public class TbAgent extends Model<TbAgent> {

    @TableId(type = IdType.ASSIGN_UUID)
    private String agentId;

    private String name;

    private String description;

    private String difyApiKey;

    private String difyAgentId;

    private String config;  // JSON配置

    private String icon;

    private Integer status;  // 0:禁用 1:启用

    @TableField(fill = FieldFill.INSERT)
    private Long tenantId;

    @TableField(fill = FieldFill.INSERT)
    private String createBy;

    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.UPDATE)
    private String updateBy;

    @TableField(fill = FieldFill.UPDATE)
    private LocalDateTime updateTime;

    @TableLogic
    private Integer delFlag;
}

4.4 Controller设计

package com.pig4cloud.pigx.dify.controller;

import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.security.annotation.Inner;
import com.pig4cloud.pigx.common.security.util.SecurityUtils;
import com.pig4cloud.pigx.dify.api.dto.ChatDTO;
import com.pig4cloud.pigx.dify.service.ChatService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

@RestController
@RequiredArgsConstructor
@RequestMapping("/chat")
@Tag(name = "对话管理")
public class ChatController {

    private final ChatService chatService;

    @Operation(summary = "创建对话")
    @PostMapping
    @PreAuthorize("@pms.hasPermission('dify_chat_add')")
    public R<ChatDTO> createChat(@RequestBody ChatDTO chatDTO) {
        chatDTO.setUserId(SecurityUtils.getUser().getId());
        chatDTO.setTenantId(SecurityUtils.getUser().getTenantId());
        return R.ok(chatService.createChat(chatDTO));
    }

    @Operation(summary = "流式对话")
    @PostMapping("/stream/{chatId}")
    @PreAuthorize("@pms.hasPermission('dify_chat_message')")
    public SseEmitter streamChat(@PathVariable String chatId,
                                  @RequestBody String message) {
        return chatService.streamChat(chatId, message, SecurityUtils.getUser());
    }

    @Operation(summary = "获取对话历史")
    @GetMapping("/{chatId}/messages")
    @PreAuthorize("@pms.hasPermission('dify_chat_view')")
    public R<?> getChatMessages(@PathVariable String chatId) {
        return R.ok(chatService.getChatMessages(chatId));
    }
}

5. 数据库设计

5.1 数据表DDL

-- 智能体表
CREATE TABLE `tb_agent` (
  `agent_id` varchar(36) NOT NULL COMMENT '智能体ID',
  `name` varchar(100) NOT NULL COMMENT '名称',
  `description` varchar(500) DEFAULT NULL COMMENT '描述',
  `dify_api_key` varchar(255) DEFAULT NULL COMMENT 'Dify API Key',
  `dify_agent_id` varchar(100) DEFAULT NULL COMMENT 'Dify Agent ID',
  `config` text COMMENT '配置信息(JSON)',
  `icon` varchar(255) DEFAULT NULL COMMENT '图标',
  `status` tinyint DEFAULT '1' COMMENT '状态 0:禁用 1:启用',
  `tenant_id` bigint NOT NULL DEFAULT '1' COMMENT '租户ID',
  `create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `del_flag` char(1) DEFAULT '0' COMMENT '删除标记',
  PRIMARY KEY (`agent_id`),
  KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='智能体表';

-- 聊天会话表
CREATE TABLE `tb_chat` (
  `chat_id` varchar(36) NOT NULL COMMENT '会话ID',
  `agent_id` varchar(36) NOT NULL COMMENT '智能体ID',
  `user_id` bigint NOT NULL COMMENT '用户ID',
  `title` varchar(200) DEFAULT NULL COMMENT '会话标题',
  `conversation_id` varchar(100) DEFAULT NULL COMMENT 'Dify会话ID',
  `status` tinyint DEFAULT '1' COMMENT '状态 0:关闭 1:活跃',
  `tenant_id` bigint NOT NULL DEFAULT '1' COMMENT '租户ID',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `del_flag` char(1) DEFAULT '0' COMMENT '删除标记',
  PRIMARY KEY (`chat_id`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_agent_id` (`agent_id`),
  KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='聊天会话表';

-- 聊天消息表
CREATE TABLE `tb_chat_message` (
  `message_id` varchar(36) NOT NULL COMMENT '消息ID',
  `chat_id` varchar(36) NOT NULL COMMENT '会话ID',
  `content` text NOT NULL COMMENT '消息内容',
  `role` varchar(20) NOT NULL COMMENT '角色(user/ai/system)',
  `dify_message_id` varchar(100) DEFAULT NULL COMMENT 'Dify消息ID',
  `parent_message_id` varchar(36) DEFAULT NULL COMMENT '父消息ID',
  `metadata` text COMMENT '元数据(JSON)',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`message_id`),
  KEY `idx_chat_id` (`chat_id`),
  KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='聊天消息表';

-- 知识库表
CREATE TABLE `tb_knowledge` (
  `knowledge_id` varchar(36) NOT NULL COMMENT '知识库ID',
  `title` varchar(200) NOT NULL COMMENT '标题',
  `description` text COMMENT '描述',
  `dify_dataset_id` varchar(100) DEFAULT NULL COMMENT 'Dify数据集ID',
  `status` tinyint DEFAULT '1' COMMENT '状态 0:禁用 1:启用',
  `tenant_id` bigint NOT NULL DEFAULT '1' COMMENT '租户ID',
  `create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `del_flag` char(1) DEFAULT '0' COMMENT '删除标记',
  PRIMARY KEY (`knowledge_id`),
  KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识库表';

6. 配置文件

6.1 bootstrap.yml

server:
  port: 9500

spring:
  application:
    name: @project.artifactId@
  profiles:
    active: @profiles.active@
  cloud:
    nacos:
      discovery:
        server-addr: ${NACOS_HOST:pigx-register}:${NACOS_PORT:8848}
      config:
        server-addr: ${spring.cloud.nacos.discovery.server-addr}
        file-extension: yml
        shared-configs:
          - data-id: common.yml
            refresh: true
          - data-id: db.yml
            refresh: true

6.2 application.yml

# Dify配置
dify:
  api-base-url: ${DIFY_API_BASE_URL:https://api.dify.ai/v1}
  default-api-key: ${DIFY_DEFAULT_API_KEY:}
  connect-timeout: 10000
  read-timeout: 30000
  stream-timeout: 60000
  debug: false

# MyBatis-Plus配置
mybatis-plus:
  mapper-locations: classpath:/mapper/*.xml
  type-aliases-package: com.pig4cloud.pigx.dify.api.entity
  configuration:
    map-underscore-to-camel-case: true

# 安全配置
security:
  oauth2:
    resource:
      ignore-urls:
        - /actuator/**
        - /v3/api-docs/**

7. 服务注册

7.1 路由配置

在 pigx-gateway 中添加路由:

spring:
  cloud:
    gateway:
      routes:
        - id: pigx-dify
          uri: lb://pigx-dify
          predicates:
            - Path=/dify/**
          filters:
            - StripPrefix=1

7.2 Feign配置

package com.pig4cloud.pigx.dify.api.feign;

import com.pig4cloud.pigx.common.core.constant.ServiceNameConstants;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.dify.api.dto.ChatDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(contextId = "remoteDifyService",
            value = ServiceNameConstants.DIFY_SERVICE)
public interface RemoteDifyService {

    @GetMapping("/chat/{chatId}")
    R<ChatDTO> getChatInfo(@PathVariable("chatId") String chatId);
}

8. 部署配置

8.1 Docker配置

FROM pig4cloud/java:8-jre

RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

COPY target/pigx-dify-biz.jar /app.jar

ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/app.jar"]

8.2 K8s部署

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pigx-dify
  namespace: pigx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: pigx-dify
  template:
    metadata:
      labels:
        app: pigx-dify
    spec:
      containers:
      - name: pigx-dify
        image: pigx/pigx-dify:latest
        ports:
        - containerPort: 9500
        env:
        - name: NACOS_HOST
          value: "pigx-register"
        - name: DIFY_API_BASE_URL
          value: "https://api.dify.ai/v1"
        - name: DIFY_DEFAULT_API_KEY
          valueFrom:
            secretKeyRef:
              name: dify-secret
              key: api-key

9. 集成测试

9.1 单元测试

@SpringBootTest
class ChatServiceTest {

    @Autowired
    private ChatService chatService;

    @MockBean
    private DifyApiClient difyApiClient;

    @Test
    void testCreateChat() {
        // 测试创建对话
        ChatDTO chatDTO = new ChatDTO();
        chatDTO.setAgentId("test-agent");
        chatDTO.setUserId(1L);

        ChatDTO result = chatService.createChat(chatDTO);
        assertNotNull(result.getChatId());
    }
}

9.2 API测试

### 创建对话
POST http://localhost:9999/dify/chat
Authorization: Bearer {{token}}
Content-Type: application/json

{
  "agentId": "agent-001",
  "title": "测试对话"
}

### 发送消息(流式)
POST http://localhost:9999/dify/chat/stream/{{chatId}}
Authorization: Bearer {{token}}
Content-Type: text/plain

你好,请介绍一下自己

10. 监控告警

10.1 健康检查

@Component
public class DifyHealthIndicator implements HealthIndicator {

    @Autowired
    private DifyApiClient difyApiClient;

    @Override
    public Health health() {
        try {
            // 检查Dify API连通性
            boolean isHealthy = difyApiClient.checkHealth();
            if (isHealthy) {
                return Health.up()
                    .withDetail("dify", "Available")
                    .build();
            }
        } catch (Exception e) {
            return Health.down()
                .withDetail("dify", "Unavailable")
                .withException(e)
                .build();
        }
        return Health.down().build();
    }
}

10.2 日志配置

<!-- logback-spring.xml -->
<configuration>
    <logger name="com.pig4cloud.pigx.dify" level="INFO"/>
    <logger name="com.pig4cloud.pigx.dify.client" level="DEBUG"/>
</configuration>

11. 安全考虑

11.1 API Key管理

  • API Key 加密存储
  • 支持多租户隔离
  • 定期轮换机制

11.2 数据隔离

  • 租户级别数据隔离
  • 用户权限验证
  • 敏感信息脱敏

11.3 限流配置

@Configuration
public class RateLimitConfig {

    @Bean
    public RedisRateLimiter redisRateLimiter() {
        return new RedisRateLimiter(10, 20);  // 10 requests per second
    }
}

12. 迁移清单

  • 创建 Maven 模块结构
  • 迁移实体类和 Mapper
  • 迁移 Service 层业务逻辑
  • 迁移 Controller 层接口
  • 适配权限注解
  • 迁移 DifyApiClient
  • 配置服务注册和发现
  • 数据库表结构迁移
  • 前端页面迁移
  • 集成测试
  • 部署配置