AI知识库
开源知识库
知识库搭建组合
Fast + Ollama
RAGFlow + Ollama
深度解读RAGFlow的深度文档理解DeepDoc
Ollama + Dify.ai
AnythingLLM + Ollama
MaxKB + Ollama
LobeChat + Ollama
ChatNio + Ollama
运维知识库
02-新框架技术选型说明
07-性能与并发能力分析
05-开发规范文档
01-现有架构分析报告
06-实施计划与时间表
03-新框架详细设计文档
README
04-数据库迁移方案
00-项目总结与建议
文档管理详细设计
-
+
首页
03-新框架详细设计文档
# PandaWiki 新框架详细设计文档 ## 一、整体架构设计 ### 1.1 系统架构图 ``` ┌─────────────────────────────────────────────────────────────────────┐ │ 用户访问层 │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ Admin 管理控制台 │ │ Wiki 前台站点 │ │ │ │ Vue 3 + Vite │ │ Nuxt 3 (SSR) │ │ │ │ Element Plus │ │ Naive UI │ │ │ └──────────────────┘ └──────────────────┘ │ │ │ │ │ │ └────────────────┬───────────────────┘ │ │ │ HTTPS │ ├─────────────────────────────────────────────────────────────────────┤ │ 反向代理层 (Caddy) │ │ 端口: 2443 │ ├─────────────────────────────────────────────────────────────────────┤ │ API 网关层 │ │ ┌────────────────────────────────────────────────────┐ │ │ │ ASP.NET Core 9.0 Web API │ │ │ │ - Minimal APIs │ │ │ │ - RESTful API │ │ │ │ - Swagger/OpenAPI │ │ │ │ - JWT 认证 │ │ │ │ - 中间件管道 │ │ │ └────────────────────────────────────────────────────┘ │ ├─────────────────────────────────────────────────────────────────────┤ │ 应用服务层 (.NET 9) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ 主 API 服务 │ │ 消息消费者 │ │ 定时任务 │ │ │ │ (API) │ │ (Consumer) │ │ (Jobs) │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ │ 核心层: │ │ ┌────────────────────────────────────────────────┐ │ │ │ Application Layer (应用层) │ │ │ │ - Services / Commands / Queries / DTOs │ │ │ └────────────────────────────────────────────────┘ │ │ ┌────────────────────────────────────────────────┐ │ │ │ Domain Layer (领域层) │ │ │ │ - Entities / Value Objects / Domain Events │ │ │ └────────────────────────────────────────────────┘ │ │ ┌────────────────────────────────────────────────┐ │ │ │ Infrastructure Layer (基础设施层) │ │ │ │ - Repositories / EF Core / External Services │ │ │ └────────────────────────────────────────────────┘ │ ├─────────────────────────────────────────────────────────────────────┤ │ 数据与服务层 │ │ ┌────────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ 达梦数据库 │ │ Redis │ │ NATS │ │ MinIO │ │ │ │ (DM8) │ │ 缓存 │ │ 消息队列 │ │ 对象存储 │ │ │ └────────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ ┌────────────┐ ┌──────────┐ │ │ │ RAG │ │ Sentry │ │ │ │ 向量数据库 │ │ 监控 │ │ │ └────────────┘ └──────────┘ │ └─────────────────────────────────────────────────────────────────────┘ ``` ### 1.2 技术栈版本明细 | 组件 | 技术 | 版本 | 说明 | |------|------|------|------| | **后端运行时** | .NET | 9.0 LTS | 2024-2027 长期支持 | | **Web 框架** | ASP.NET Core | 9.0 | Minimal APIs + MVC | | **ORM** | Entity Framework Core | 9.0 | Code First + Migrations | | **主数据库** | 达梦数据库 | DM8 | PostgreSQL 兼容模式 | | **数据库驱动** | Npgsql / DmProvider | 8.x / 最新 | 优先使用 PG 兼容模式 | | **缓存** | StackExchange.Redis | 2.x | Redis 客户端 | | **消息队列** | NATS.Net | 2.x | NATS 客户端 | | **对象存储** | Minio | 6.x | S3 兼容客户端 | | **前端框架 (Admin)** | Vue | 3.5+ | Composition API | | **前端框架 (App)** | Nuxt | 3.x | SSR 支持 | | **UI 库 (Admin)** | Element Plus | 2.x | 企业级组件库 | | **UI 库 (App)** | Naive UI | 2.x | 轻量级组件库 | | **状态管理** | Pinia | 2.x | Vue 官方推荐 | | **构建工具** | Vite | 6.x | 快速构建 | --- ## 二、后端详细设计 ### 2.1 项目结构 ``` PandaWiki/ ├── src/ │ ├── PandaWiki.API/ # API 主项目 │ │ ├── Controllers/ # MVC 控制器 │ │ │ ├── V1/ # V1 版本 API │ │ │ │ ├── AuthController.cs │ │ │ │ ├── KnowledgeBaseController.cs │ │ │ │ ├── NodeController.cs │ │ │ │ ├── ConversationController.cs │ │ │ │ ├── AppController.cs │ │ │ │ ├── ModelController.cs │ │ │ │ ├── FileController.cs │ │ │ │ ├── CrawlerController.cs │ │ │ │ ├── StatController.cs │ │ │ │ └── UserController.cs │ │ │ └── Share/ # 公共分享 API │ │ │ ├── ShareNodeController.cs │ │ │ ├── ShareChatController.cs │ │ │ └── ShareAuthController.cs │ │ ├── Endpoints/ # Minimal APIs │ │ │ └── HealthEndpoints.cs │ │ ├── Middlewares/ # 中间件 │ │ │ ├── ExceptionHandlingMiddleware.cs │ │ │ ├── JwtAuthenticationMiddleware.cs │ │ │ ├── RequestLoggingMiddleware.cs │ │ │ └── RateLimitingMiddleware.cs │ │ ├── Filters/ # 过滤器 │ │ │ ├── AuthorizationFilter.cs │ │ │ └── ValidationFilter.cs │ │ ├── Extensions/ # 扩展方法 │ │ │ ├── ServiceCollectionExtensions.cs │ │ │ └── ApplicationBuilderExtensions.cs │ │ ├── appsettings.json # 配置文件 │ │ ├── appsettings.Development.json │ │ ├── appsettings.Production.json │ │ └── Program.cs # 启动文件 │ │ │ ├── PandaWiki.Application/ # 应用层 │ │ ├── Services/ # 应用服务 │ │ │ ├── Auth/ │ │ │ │ ├── AuthService.cs │ │ │ │ └── IAuthService.cs │ │ │ ├── KnowledgeBase/ │ │ │ │ ├── KnowledgeBaseService.cs │ │ │ │ └── IKnowledgeBaseService.cs │ │ │ ├── Node/ │ │ │ │ ├── NodeService.cs │ │ │ │ └── INodeService.cs │ │ │ ├── Conversation/ │ │ │ ├── App/ │ │ │ ├── Model/ │ │ │ ├── File/ │ │ │ ├── Crawler/ │ │ │ ├── Stat/ │ │ │ └── User/ │ │ ├── Commands/ # CQRS 命令 │ │ │ ├── CreateNodeCommand.cs │ │ │ ├── UpdateNodeCommand.cs │ │ │ └── ... │ │ ├── Queries/ # CQRS 查询 │ │ │ ├── GetNodeByIdQuery.cs │ │ │ ├── ListNodesQuery.cs │ │ │ └── ... │ │ ├── DTOs/ # 数据传输对象 │ │ │ ├── Request/ │ │ │ │ ├── CreateNodeRequest.cs │ │ │ │ ├── UpdateNodeRequest.cs │ │ │ │ └── ... │ │ │ └── Response/ │ │ │ ├── NodeResponse.cs │ │ │ ├── PagedResponse.cs │ │ │ └── ... │ │ ├── Mappings/ # AutoMapper 配置 │ │ │ └── MappingProfile.cs │ │ ├── Validators/ # FluentValidation 验证器 │ │ │ ├── CreateNodeValidator.cs │ │ │ └── ... │ │ └── Interfaces/ # 接口定义 │ │ └── IApplicationService.cs │ │ │ ├── PandaWiki.Domain/ # 领域层 │ │ ├── Entities/ # 实体 │ │ │ ├── User.cs │ │ │ ├── KnowledgeBase.cs │ │ │ ├── Node.cs │ │ │ ├── NodeVersion.cs │ │ │ ├── App.cs │ │ │ ├── Conversation.cs │ │ │ ├── ConversationMessage.cs │ │ │ ├── Model.cs │ │ │ ├── Comment.cs │ │ │ ├── Contribute.cs │ │ │ └── ... │ │ ├── ValueObjects/ # 值对象 │ │ │ ├── NodeContent.cs │ │ │ ├── AppSettings.cs │ │ │ └── ... │ │ ├── Enums/ # 枚举 │ │ │ ├── NodeType.cs │ │ │ ├── AppType.cs │ │ │ ├── UserRole.cs │ │ │ └── ... │ │ ├── Events/ # 领域事件 │ │ │ ├── NodeCreatedEvent.cs │ │ │ ├── NodeUpdatedEvent.cs │ │ │ └── ... │ │ ├── Exceptions/ # 领域异常 │ │ │ ├── NodeNotFoundException.cs │ │ │ └── ... │ │ └── Interfaces/ # 领域接口 │ │ ├── IRepository.cs │ │ ├── IUnitOfWork.cs │ │ └── ... │ │ │ ├── PandaWiki.Infrastructure/ # 基础设施层 │ │ ├── Persistence/ # 持久化 │ │ │ ├── PandaWikiDbContext.cs # EF Core 上下文 │ │ │ ├── Configurations/ # EF 配置 │ │ │ │ ├── UserConfiguration.cs │ │ │ │ ├── NodeConfiguration.cs │ │ │ │ └── ... │ │ │ ├── Repositories/ # 仓储实现 │ │ │ │ ├── UserRepository.cs │ │ │ │ ├── NodeRepository.cs │ │ │ │ └── ... │ │ │ ├── Migrations/ # 数据库迁移 │ │ │ │ └── (自动生成的迁移文件) │ │ │ └── UnitOfWork.cs │ │ ├── Cache/ # 缓存实现 │ │ │ ├── RedisCacheService.cs │ │ │ └── ICacheService.cs │ │ ├── MessageQueue/ # 消息队列 │ │ │ ├── NatsProducer.cs │ │ │ ├── NatsConsumer.cs │ │ │ └── IMessageQueueService.cs │ │ ├── ObjectStorage/ # 对象存储 │ │ │ ├── MinioService.cs │ │ │ └── IObjectStorageService.cs │ │ ├── ExternalServices/ # 外部服务 │ │ │ ├── RagService.cs # RAG 服务 │ │ │ ├── LlmService.cs # LLM 服务 │ │ │ ├── OAuth/ # OAuth 认证 │ │ │ │ ├── GitHubOAuthService.cs │ │ │ │ └── ... │ │ │ └── Bots/ # 聊天机器人 │ │ │ ├── DingTalkBotService.cs │ │ │ ├── FeishuBotService.cs │ │ │ └── WeComBotService.cs │ │ └── Identity/ # 身份认证 │ │ ├── JwtTokenService.cs │ │ └── ITokenService.cs │ │ │ ├── PandaWiki.Consumer/ # 消息消费者 │ │ ├── Handlers/ # 消息处理器 │ │ │ ├── RagIndexHandler.cs │ │ │ ├── StatAggregateHandler.cs │ │ │ └── ... │ │ └── Program.cs │ │ │ ├── PandaWiki.Jobs/ # 定时任务 │ │ ├── Jobs/ │ │ │ ├── StatAggregationJob.cs │ │ │ └── ... │ │ └── Program.cs │ │ │ └── PandaWiki.Shared/ # 共享库 │ ├── Constants/ # 常量 │ │ ├── AppConstants.cs │ │ └── ErrorCodes.cs │ ├── Extensions/ # 扩展方法 │ │ ├── StringExtensions.cs │ │ └── DateTimeExtensions.cs │ ├── Helpers/ # 辅助类 │ │ ├── PasswordHelper.cs │ │ └── JsonHelper.cs │ └── Models/ # 共享模型 │ └── Result.cs │ ├── tests/ # 测试项目 │ ├── PandaWiki.UnitTests/ │ ├── PandaWiki.IntegrationTests/ │ └── PandaWiki.E2ETests/ │ ├── docker/ # Docker 配置 │ ├── Dockerfile.api │ ├── Dockerfile.consumer │ └── docker-compose.yml │ ├── docs/ # 文档 │ ├── api/ │ └── development/ │ ├── PandaWiki.sln # 解决方案文件 └── README.md ``` ### 2.2 核心领域模型设计 #### 2.2.1 用户领域 (User Domain) ```csharp namespace PandaWiki.Domain.Entities; /// <summary> /// 用户实体 /// </summary> public class User : BaseEntity { /// <summary> /// 用户 ID (ULID) /// </summary> public string Id { get; set; } /// <summary> /// 账号 /// </summary> public string Account { get; set; } /// <summary> /// 密码哈希 /// </summary> public string PasswordHash { get; set; } /// <summary> /// 用户角色 /// </summary> public UserRole Role { get; set; } /// <summary> /// 创建时间 /// </summary> public DateTime CreatedAt { get; set; } /// <summary> /// 最后访问时间 /// </summary> public DateTime? LastAccess { get; set; } /// <summary> /// 用户-知识库关联 /// </summary> public ICollection<KnowledgeBaseUser> KnowledgeBaseUsers { get; set; } } /// <summary> /// 用户角色枚举 /// </summary> public enum UserRole { Admin = 0, // 管理员 User = 1 // 普通用户 } /// <summary> /// 知识库用户关联 /// </summary> public class KnowledgeBaseUser : BaseEntity { public string UserId { get; set; } public User User { get; set; } public string KnowledgeBaseId { get; set; } public KnowledgeBase KnowledgeBase { get; set; } /// <summary> /// 权限 /// </summary> public KbPermission Permission { get; set; } } /// <summary> /// 知识库权限枚举 /// </summary> public enum KbPermission { FullControl = 0, // 完全控制 DocManage = 1, // 文档管理 DataOperate = 2, // 数据操作 ReadOnly = 3 // 只读 } ``` #### 2.2.2 知识库领域 (KnowledgeBase Domain) ```csharp namespace PandaWiki.Domain.Entities; /// <summary> /// 知识库实体 /// </summary> public class KnowledgeBase : BaseEntity { public string Id { get; set; } /// <summary> /// 知识库名称 /// </summary> public string Name { get; set; } /// <summary> /// 访问设置 (JSON) /// </summary> public AccessSettings AccessSettings { get; set; } /// <summary> /// 数据集 ID (用于 RAG) /// </summary> public string? DatasetId { get; set; } public DateTime CreatedAt { get; set; } public DateTime UpdatedAt { get; set; } /// <summary> /// 关联的节点 /// </summary> public ICollection<Node> Nodes { get; set; } /// <summary> /// 关联的应用 /// </summary> public ICollection<App> Apps { get; set; } /// <summary> /// 用户关联 /// </summary> public ICollection<KnowledgeBaseUser> KnowledgeBaseUsers { get; set; } } /// <summary> /// 访问设置值对象 /// </summary> public class AccessSettings { public bool IsPublic { get; set; } public bool RequireLogin { get; set; } public string? WhitelistIps { get; set; } } ``` #### 2.2.3 节点领域 (Node Domain) ```csharp namespace PandaWiki.Domain.Entities; /// <summary> /// 节点(文档/文件夹)实体 /// </summary> public class Node : BaseEntity { public string Id { get; set; } /// <summary> /// 知识库 ID /// </summary> public string KbId { get; set; } public KnowledgeBase KnowledgeBase { get; set; } /// <summary> /// 文档 ID (同一文档的不同版本共享) /// </summary> public string DocId { get; set; } /// <summary> /// 节点类型 /// </summary> public NodeType Type { get; set; } /// <summary> /// 节点名称 /// </summary> public string Name { get; set; } /// <summary> /// 内容 (Markdown/HTML) /// </summary> public string? Content { get; set; } /// <summary> /// 元数据 (JSON) /// </summary> public NodeMeta? Meta { get; set; } /// <summary> /// 父节点 ID /// </summary> public string? ParentId { get; set; } public Node? Parent { get; set; } /// <summary> /// 位置(用于排序) /// </summary> public double Position { get; set; } public DateTime CreatedAt { get; set; } public DateTime UpdatedAt { get; set; } /// <summary> /// 子节点 /// </summary> public ICollection<Node> Children { get; set; } /// <summary> /// 版本历史 /// </summary> public ICollection<NodeVersion> Versions { get; set; } } /// <summary> /// 节点类型枚举 /// </summary> public enum NodeType { Folder = 0, // 文件夹 Document = 1 // 文档 } /// <summary> /// 节点元数据值对象 /// </summary> public class NodeMeta { public string? Icon { get; set; } public string? Cover { get; set; } public string? Description { get; set; } public Dictionary<string, string>? CustomFields { get; set; } } /// <summary> /// 节点版本实体 /// </summary> public class NodeVersion : BaseEntity { public string Id { get; set; } public string NodeId { get; set; } public Node Node { get; set; } /// <summary> /// 版本号 /// </summary> public int Version { get; set; } /// <summary> /// 版本内容 /// </summary> public string Content { get; set; } /// <summary> /// 创建者 ID /// </summary> public string? CreatedBy { get; set; } public DateTime CreatedAt { get; set; } } ``` #### 2.2.4 会话领域 (Conversation Domain) ```csharp namespace PandaWiki.Domain.Entities; /// <summary> /// 会话实体 /// </summary> public class Conversation : BaseEntity { public string Id { get; set; } /// <summary> /// 随机码(用于访问控制) /// </summary> public string? Nonce { get; set; } public string KbId { get; set; } public KnowledgeBase KnowledgeBase { get; set; } public string AppId { get; set; } public App App { get; set; } /// <summary> /// 会话主题 /// </summary> public string? Subject { get; set; } /// <summary> /// 访问 IP /// </summary> public string? RemoteIp { get; set; } public DateTime CreatedAt { get; set; } /// <summary> /// 消息列表 /// </summary> public ICollection<ConversationMessage> Messages { get; set; } } /// <summary> /// 会话消息实体 /// </summary> public class ConversationMessage : BaseEntity { public string Id { get; set; } public string ConversationId { get; set; } public Conversation Conversation { get; set; } public string AppId { get; set; } public App App { get; set; } /// <summary> /// 角色 (user/assistant/system) /// </summary> public string Role { get; set; } /// <summary> /// 消息内容 /// </summary> public string Content { get; set; } /// <summary> /// 模型提供商 /// </summary> public string? Provider { get; set; } /// <summary> /// 模型名称 /// </summary> public string? Model { get; set; } /// <summary> /// Token 使用量 /// </summary> public long PromptTokens { get; set; } public long CompletionTokens { get; set; } public long TotalTokens { get; set; } public string? RemoteIp { get; set; } public DateTime CreatedAt { get; set; } } ``` ### 2.3 应用服务层设计 #### 2.3.1 服务接口示例 ```csharp namespace PandaWiki.Application.Services.Node; /// <summary> /// 节点服务接口 /// </summary> public interface INodeService { /// <summary> /// 获取节点详情 /// </summary> Task<NodeResponse> GetNodeAsync(string id, CancellationToken cancellationToken = default); /// <summary> /// 获取知识库节点树 /// </summary> Task<List<NodeTreeResponse>> GetNodeTreeAsync(string kbId, CancellationToken cancellationToken = default); /// <summary> /// 创建节点 /// </summary> Task<NodeResponse> CreateNodeAsync(CreateNodeRequest request, CancellationToken cancellationToken = default); /// <summary> /// 更新节点 /// </summary> Task<NodeResponse> UpdateNodeAsync(string id, UpdateNodeRequest request, CancellationToken cancellationToken = default); /// <summary> /// 删除节点 /// </summary> Task DeleteNodeAsync(string id, CancellationToken cancellationToken = default); /// <summary> /// 移动节点 /// </summary> Task MoveNodeAsync(string id, MoveNodeRequest request, CancellationToken cancellationToken = default); /// <summary> /// 获取节点版本历史 /// </summary> Task<List<NodeVersionResponse>> GetNodeVersionsAsync(string id, CancellationToken cancellationToken = default); } ``` #### 2.3.2 服务实现示例 ```csharp namespace PandaWiki.Application.Services.Node; public class NodeService : INodeService { private readonly IUnitOfWork _unitOfWork; private readonly IMapper _mapper; private readonly ICacheService _cache; private readonly IMessageQueueService _messageQueue; private readonly ILogger<NodeService> _logger; public NodeService( IUnitOfWork unitOfWork, IMapper mapper, ICacheService cache, IMessageQueueService messageQueue, ILogger<NodeService> logger) { _unitOfWork = unitOfWork; _mapper = mapper; _cache = cache; _messageQueue = messageQueue; _logger = logger; } public async Task<NodeResponse> GetNodeAsync(string id, CancellationToken cancellationToken = default) { // 尝试从缓存获取 var cacheKey = $"node:{id}"; var cachedNode = await _cache.GetAsync<NodeResponse>(cacheKey, cancellationToken); if (cachedNode != null) { return cachedNode; } // 从数据库获取 var node = await _unitOfWork.NodeRepository.GetByIdAsync(id, cancellationToken); if (node == null) { throw new NodeNotFoundException(id); } var response = _mapper.Map<NodeResponse>(node); // 写入缓存 await _cache.SetAsync(cacheKey, response, TimeSpan.FromMinutes(10), cancellationToken); return response; } public async Task<NodeResponse> CreateNodeAsync(CreateNodeRequest request, CancellationToken cancellationToken = default) { // 验证 var validator = new CreateNodeValidator(); await validator.ValidateAndThrowAsync(request, cancellationToken); // 创建实体 var node = new Domain.Entities.Node { Id = Ulid.NewUlid().ToString(), DocId = Ulid.NewUlid().ToString(), KbId = request.KbId, Type = request.Type, Name = request.Name, Content = request.Content, ParentId = request.ParentId, Position = await CalculatePositionAsync(request.KbId, request.ParentId, cancellationToken), CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow }; // 保存 await _unitOfWork.NodeRepository.AddAsync(node, cancellationToken); await _unitOfWork.SaveChangesAsync(cancellationToken); // 发送 RAG 索引消息 if (node.Type == NodeType.Document && !string.IsNullOrEmpty(node.Content)) { await _messageQueue.PublishAsync("rag.index", new { NodeId = node.Id, KbId = node.KbId, Content = node.Content }, cancellationToken); } // 清除缓存 await _cache.RemoveAsync($"node_tree:{request.KbId}", cancellationToken); _logger.LogInformation("Node created: {NodeId}", node.Id); return _mapper.Map<NodeResponse>(node); } // ... 其他方法实现 } ``` ### 2.4 数据访问层设计 #### 2.4.1 DbContext 配置 ```csharp namespace PandaWiki.Infrastructure.Persistence; public class PandaWikiDbContext : DbContext { public PandaWikiDbContext(DbContextOptions<PandaWikiDbContext> options) : base(options) { } public DbSet<User> Users { get; set; } public DbSet<KnowledgeBase> KnowledgeBases { get; set; } public DbSet<Node> Nodes { get; set; } public DbSet<NodeVersion> NodeVersions { get; set; } public DbSet<App> Apps { get; set; } public DbSet<Conversation> Conversations { get; set; } public DbSet<ConversationMessage> ConversationMessages { get; set; } public DbSet<Model> Models { get; set; } public DbSet<Comment> Comments { get; set; } public DbSet<Contribute> Contributes { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // 应用所有配置 modelBuilder.ApplyConfigurationsFromAssembly(typeof(PandaWikiDbContext).Assembly); } } ``` #### 2.4.2 实体配置示例 ```csharp namespace PandaWiki.Infrastructure.Persistence.Configurations; public class NodeConfiguration : IEntityTypeConfiguration<Node> { public void Configure(EntityTypeBuilder<Node> builder) { builder.ToTable("nodes"); builder.HasKey(n => n.Id); builder.Property(n => n.Id) .HasMaxLength(26) .IsRequired(); builder.Property(n => n.KbId) .HasMaxLength(26) .IsRequired(); builder.Property(n => n.Name) .HasMaxLength(255) .IsRequired(); builder.Property(n => n.Content) .HasColumnType("text"); // JSON 列 builder.Property(n => n.Meta) .HasColumnType("jsonb") .HasConversion( v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null), v => JsonSerializer.Deserialize<NodeMeta>(v, (JsonSerializerOptions)null)); // 索引 builder.HasIndex(n => n.KbId).HasDatabaseName("idx_nodes_kb_id"); builder.HasIndex(n => n.DocId).HasDatabaseName("idx_nodes_doc_id"); builder.HasIndex(n => n.ParentId).HasDatabaseName("idx_nodes_parent_id"); // 关系 builder.HasOne(n => n.KnowledgeBase) .WithMany(kb => kb.Nodes) .HasForeignKey(n => n.KbId) .OnDelete(DeleteBehavior.Cascade); builder.HasOne(n => n.Parent) .WithMany(n => n.Children) .HasForeignKey(n => n.ParentId) .OnDelete(DeleteBehavior.Restrict); builder.HasMany(n => n.Versions) .WithOne(v => v.Node) .HasForeignKey(v => v.NodeId) .OnDelete(DeleteBehavior.Cascade); } } ``` #### 2.4.3 仓储实现 ```csharp namespace PandaWiki.Infrastructure.Persistence.Repositories; public class NodeRepository : INodeRepository { private readonly PandaWikiDbContext _context; public NodeRepository(PandaWikiDbContext context) { _context = context; } public async Task<Node?> GetByIdAsync(string id, CancellationToken cancellationToken = default) { return await _context.Nodes .Include(n => n.Versions) .FirstOrDefaultAsync(n => n.Id == id, cancellationToken); } public async Task<List<Node>> GetByKbIdAsync(string kbId, CancellationToken cancellationToken = default) { return await _context.Nodes .Where(n => n.KbId == kbId) .OrderBy(n => n.Position) .ToListAsync(cancellationToken); } public async Task AddAsync(Node node, CancellationToken cancellationToken = default) { await _context.Nodes.AddAsync(node, cancellationToken); } public void Update(Node node) { _context.Nodes.Update(node); } public void Delete(Node node) { _context.Nodes.Remove(node); } } ``` --- ## 三、前端详细设计 ### 3.1 Admin 控制台项目结构 (Vue 3) ``` admin/ ├── public/ # 静态资源 │ ├── favicon.ico │ └── ... ├── src/ │ ├── main.ts # 入口文件 │ ├── App.vue # 根组件 │ ├── router/ # 路由配置 │ │ ├── index.ts │ │ └── routes.ts │ ├── store/ # Pinia 状态管理 │ │ ├── index.ts │ │ ├── modules/ │ │ │ ├── user.ts │ │ │ ├── kb.ts │ │ │ ├── node.ts │ │ │ └── ... │ ├── views/ # 页面视图 │ │ ├── Login.vue │ │ ├── Document/ │ │ │ ├── Index.vue │ │ │ ├── Editor.vue │ │ │ └── History.vue │ │ ├── Stat/ │ │ │ └── Index.vue │ │ ├── Conversation/ │ │ │ └── Index.vue │ │ ├── Feedback/ │ │ │ └── Index.vue │ │ ├── Release/ │ │ │ └── Index.vue │ │ ├── Contribution/ │ │ │ └── Index.vue │ │ └── Setting/ │ │ └── Index.vue │ ├── components/ # 组件 │ │ ├── Layout/ │ │ │ ├── MainLayout.vue │ │ │ ├── Header.vue │ │ │ └── Sidebar.vue │ │ ├── Editor/ │ │ │ └── TiptapEditor.vue │ │ ├── NodeTree/ │ │ │ └── Tree.vue │ │ └── ... │ ├── composables/ # 组合式函数 │ │ ├── useAuth.ts │ │ ├── useKb.ts │ │ ├── useNode.ts │ │ └── ... │ ├── api/ # API 请求 │ │ ├── client.ts # Axios 实例 │ │ ├── auth.ts │ │ ├── kb.ts │ │ ├── node.ts │ │ └── ... │ ├── types/ # TypeScript 类型 │ │ ├── user.ts │ │ ├── kb.ts │ │ ├── node.ts │ │ └── ... │ ├── utils/ # 工具函数 │ │ ├── request.ts │ │ ├── storage.ts │ │ └── ... │ ├── styles/ # 样式文件 │ │ ├── index.scss │ │ ├── variables.scss │ │ └── ... │ └── assets/ # 资源文件 │ ├── images/ │ └── fonts/ ├── .env.development # 开发环境变量 ├── .env.production # 生产环境变量 ├── vite.config.ts # Vite 配置 ├── tsconfig.json # TypeScript 配置 └── package.json ``` ### 3.2 核心组件示例 #### 3.2.1 Pinia Store 示例 ```typescript // src/store/modules/node.ts import { defineStore } from 'pinia'; import { ref, computed } from 'vue'; import type { Node, NodeTree } from '@/types/node'; import * as nodeApi from '@/api/node'; export const useNodeStore = defineStore('node', () => { // State const currentNode = ref<Node | null>(null); const nodeTree = ref<NodeTree[]>([]); const loading = ref(false); // Getters const hasUnsavedChanges = computed(() => { // 检查是否有未保存的更改 return false; }); // Actions async function fetchNodeTree(kbId: string) { loading.value = true; try { const data = await nodeApi.getNodeTree(kbId); nodeTree.value = data; } finally { loading.value = false; } } async function fetchNode(id: string) { loading.value = true; try { const data = await nodeApi.getNode(id); currentNode.value = data; } finally { loading.value = false; } } async function createNode(request: CreateNodeRequest) { const data = await nodeApi.createNode(request); // 更新树 await fetchNodeTree(request.kbId); return data; } async function updateNode(id: string, request: UpdateNodeRequest) { const data = await nodeApi.updateNode(id, request); currentNode.value = data; return data; } return { currentNode, nodeTree, loading, hasUnsavedChanges, fetchNodeTree, fetchNode, createNode, updateNode }; }); ``` #### 3.2.2 API 请求示例 ```typescript // src/api/node.ts import { request } from './client'; import type { Node, NodeTree, CreateNodeRequest, UpdateNodeRequest } from '@/types/node'; export function getNodeTree(kbId: string): Promise<NodeTree[]> { return request.get(`/api/v1/nodes/tree`, { params: { kb_id: kbId } }); } export function getNode(id: string): Promise<Node> { return request.get(`/api/v1/nodes/${id}`); } export function createNode(data: CreateNodeRequest): Promise<Node> { return request.post('/api/v1/nodes', data); } export function updateNode(id: string, data: UpdateNodeRequest): Promise<Node> { return request.put(`/api/v1/nodes/${id}`, data); } export function deleteNode(id: string): Promise<void> { return request.delete(`/api/v1/nodes/${id}`); } ``` #### 3.2.3 组合式函数示例 ```typescript // src/composables/useNode.ts import { ref, computed } from 'vue'; import { useNodeStore } from '@/store/modules/node'; import { ElMessage } from 'element-plus'; export function useNode() { const store = useNodeStore(); const saving = ref(false); const currentNode = computed(() => store.currentNode); const nodeTree = computed(() => store.nodeTree); async function loadNode(id: string) { try { await store.fetchNode(id); } catch (error) { ElMessage.error('加载节点失败'); throw error; } } async function saveNode(id: string, content: string) { saving.value = true; try { await store.updateNode(id, { content }); ElMessage.success('保存成功'); } catch (error) { ElMessage.error('保存失败'); throw error; } finally { saving.value = false; } } return { currentNode, nodeTree, saving, loadNode, saveNode }; } ``` ### 3.3 App Wiki 站项目结构 (Nuxt 3) ``` app/ ├── pages/ # 页面(自动路由) │ ├── index.vue # 首页 │ ├── home.vue # 知识库首页 │ ├── node/ │ │ └── [id].vue # 文档详情 │ ├── chat/ │ │ └── [...id].vue # AI 对话 │ └── feedback.vue # 反馈 ├── components/ # 组件 │ ├── Header.vue │ ├── Footer.vue │ ├── Catalog.vue │ ├── Markdown.vue │ └── ... ├── composables/ # 组合式函数 │ ├── useNode.ts │ ├── useChat.ts │ └── ... ├── server/ # 服务端代码 │ ├── api/ # API 路由 │ │ └── ... │ └── middleware/ ├── public/ # 静态资源 ├── assets/ # 资源文件 ├── nuxt.config.ts # Nuxt 配置 ├── tsconfig.json └── package.json ``` --- ## 四、API 接口设计规范 ### 4.1 RESTful API 设计原则 **URL 规范**: ``` GET /api/v1/nodes # 列表 GET /api/v1/nodes/{id} # 详情 POST /api/v1/nodes # 创建 PUT /api/v1/nodes/{id} # 更新 DELETE /api/v1/nodes/{id} # 删除 ``` **响应格式**: ```json { "success": true, "data": {}, "message": "操作成功", "timestamp": "2025-10-30T12:00:00Z" } ``` **错误响应**: ```json { "success": false, "error": { "code": "NODE_NOT_FOUND", "message": "节点不存在", "details": {} }, "timestamp": "2025-10-30T12:00:00Z" } ``` ### 4.2 核心 API 端点 #### 4.2.1 认证 API ``` POST /api/v1/auth/login # 登录 POST /api/v1/auth/logout # 登出 POST /api/v1/auth/refresh # 刷新 Token GET /api/v1/auth/me # 获取当前用户信息 ``` #### 4.2.2 知识库 API ``` GET /api/v1/kbs # 获取知识库列表 POST /api/v1/kbs # 创建知识库 GET /api/v1/kbs/{id} # 获取知识库详情 PUT /api/v1/kbs/{id} # 更新知识库 DELETE /api/v1/kbs/{id} # 删除知识库 ``` #### 4.2.3 节点 API ``` GET /api/v1/nodes/tree # 获取节点树 GET /api/v1/nodes/{id} # 获取节点详情 POST /api/v1/nodes # 创建节点 PUT /api/v1/nodes/{id} # 更新节点 DELETE /api/v1/nodes/{id} # 删除节点 POST /api/v1/nodes/{id}/move # 移动节点 GET /api/v1/nodes/{id}/versions # 获取版本历史 ``` #### 4.2.4 会话 API ``` POST /api/v1/conversations # 创建会话 GET /api/v1/conversations/{id} # 获取会话详情 POST /api/v1/conversations/{id}/messages # 发送消息(流式) ``` --- ## 五、数据库迁移方案 ### 5.1 达梦数据库配置 **达梦 PostgreSQL 兼容模式配置**: ```ini # dm.ini 配置文件 COMPATIBLE_MODE = 2 # PostgreSQL 兼容模式 PORT_NUM = 5236 ``` ### 5.2 EF Core 迁移命令 ```bash # 添加迁移 dotnet ef migrations add InitialCreate --project src/PandaWiki.Infrastructure # 应用迁移 dotnet ef database update --project src/PandaWiki.Infrastructure # 生成 SQL 脚本 dotnet ef migrations script --project src/PandaWiki.Infrastructure -o migration.sql ``` --- ## 六、部署架构 ### 6.1 Docker Compose 配置 ```yaml version: '3.8' services: # 达梦数据库 dm-database: image: dm8:latest container_name: pandawiki-dm ports: - "5236:5236" environment: - INSTANCE_NAME=PANDAWIKI - SYSDBA_PWD=SYSDBA123456 volumes: - dm-data:/opt/dmdbms/data restart: always # Redis redis: image: redis:7-alpine container_name: pandawiki-redis ports: - "6379:6379" volumes: - redis-data:/data restart: always # NATS nats: image: nats:latest container_name: pandawiki-nats ports: - "4222:4222" restart: always # MinIO minio: image: minio/minio:latest container_name: pandawiki-minio ports: - "9000:9000" - "9001:9001" environment: - MINIO_ROOT_USER=admin - MINIO_ROOT_PASSWORD=admin123456 volumes: - minio-data:/data command: server /data --console-address ":9001" restart: always # API 服务 api: build: context: . dockerfile: docker/Dockerfile.api container_name: pandawiki-api ports: - "8000:8080" environment: - ASPNETCORE_ENVIRONMENT=Production - ConnectionStrings__DmConnection=Server=dm-database;Port=5236;User Id=SYSDBA;Password=SYSDBA123456;Database=PANDAWIKI - Redis__Addr=redis:6379 - NATS__Server=nats://nats:4222 - S3__Endpoint=minio:9000 depends_on: - dm-database - redis - nats - minio restart: always # 消息消费者 consumer: build: context: . dockerfile: docker/Dockerfile.consumer container_name: pandawiki-consumer environment: - ASPNETCORE_ENVIRONMENT=Production - ConnectionStrings__DmConnection=Server=dm-database;Port=5236;User Id=SYSDBA;Password=SYSDBA123456;Database=PANDAWIKI - NATS__Server=nats://nats:4222 depends_on: - dm-database - nats restart: always # Admin 前端 admin: build: context: ./web/admin dockerfile: Dockerfile container_name: pandawiki-admin ports: - "3000:80" restart: always # App 前端 app: build: context: ./web/app dockerfile: Dockerfile container_name: pandawiki-app ports: - "3010:3000" restart: always volumes: dm-data: redis-data: minio-data: ``` --- ## 七、总结 本文档详细设计了基于 **Vue 3 + .NET 9 + 达梦数据库** 的 PandaWiki 新框架,包括: 1. **整体架构**:清晰的分层架构,职责明确 2. **后端设计**:DDD 领域驱动设计,CQRS 模式 3. **前端设计**:Vue 3 Composition API,组合式函数 4. **数据库设计**:达梦 PostgreSQL 兼容模式,EF Core 迁移 5. **API 设计**:RESTful 规范,统一响应格式 6. **部署方案**:Docker Compose 容器化部署 接下来的工作: - 详细的开发规范文档 - 数据迁移脚本 - 单元测试和集成测试 - CI/CD 流程配置 --- **文档版本**:v1.0 **编写时间**:2025-10-30 **状态**:待审核
张猛
2025年11月3日 10:12
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码