Skip to content

系统架构总览

架构图

模块职责

platform-api

所有模块依赖的共享 Dubbo 接口定义:

  • BlockChainService - 区块链操作
  • DistributedStorageService - 存储操作
  • 公共 DTO 和响应类型

platform-backend

多模块后端服务(Dubbo Consumer):

子模块职责
backend-webREST 控制器、JWT 过滤器、限流、CORS
backend-service业务逻辑、Saga 编排、Outbox 发布
backend-daoMyBatis Plus 映射、实体、VO
backend-api内部 API 接口定义
backend-common工具类、常量、注解

platform-fisco

区块链集成服务(Dubbo Provider):

  • 智能合约交互(Storage.sol, Sharing.sol)
  • 多链适配器(本地 FISCO、BSN FISCO、Besu)
  • 证书管理

platform-storage

分布式存储服务(Dubbo Provider):

  • 多节点 S3 客户端管理
  • 故障域管理
  • 一致性哈希和再平衡
  • 文件加密/解密

核心业务流程

文件上传存证

文件下载流程

下载策略对比

策略适用场景特点
内存模式小文件 (< 50MB)全部分片加载到内存后解密,速度快
流式模式大文件 (≥ 50MB)使用 StreamSaver.js,边下载边写入,内存占用低
后端代理特殊场景后端代理下载,适用于无法直连 S3 的环境

密钥链解密:每个分片使用独立密钥加密,下载时按 chunkIndex 顺序匹配密钥进行解密。

文件分享流程

普通分享(链接分享)

好友分享

分享类型对比

类型访问控制有效期特点
公开分享无限制可设置任何人可通过链接访问
私密分享访问密码可设置需要密码才能访问
好友分享好友关系永久仅指定好友可见,支持已读状态

Saga 补偿流程

步骤正向操作补偿操作
PENDING初始化-
S3_UPLOADING存储分片清理已存储分片
S3_UPLOADED分片存储完成删除 S3 文件
CHAIN_STORING区块链存证标记链上记录删除
COMPLETED提交-

补偿策略:指数退避重试(初始 1s,最多 5 次),失败后进入人工处理队列。

Saga 状态机

FileSagaOrchestrator 管理完整的状态机:

事务性 Outbox 模式

RecordPlatform 使用 Outbox 模式实现到 RabbitMQ 的可靠事件发布。

工作原理

组件

组件职责
OutboxService在业务事务中追加事件
OutboxPublisher后台轮询和发布(2 秒间隔)
outbox_event带租户隔离的持久化事件存储

保证

  • 至少一次投递:事件在消息队列不可用时仍能存活
  • 事务一致性:事件在同一数据库事务中与业务数据一起创建
  • 租户感知轮询:每个租户的事件独立处理

配置

yaml
outbox:
  publisher:
    batch-size: 100
    poll-interval-ms: 2000
    max-retries: 5
  cleanup:
    sent-retention-days: 7
    failed-retention-days: 30
    cron: 0 0 3 * * ?

CQRS 架构

文件模块采用命令查询职责分离:

Virtual Thread 异步方法

查询服务使用 Java 21 Virtual Thread 提供异步方法:

  • getUserFilesListAsync()
  • getFileAddressAsync()
  • getFileDecryptInfoAsync()

文件版本链

文件版本链允许同一逻辑文件拥有多个历史版本,并通过链式结构追踪版本演进。详细设计请参阅 文件版本链 专题文档。

核心字段

字段说明
version_group_id同一逻辑文件所有版本共享的分组 ID
parent_version_id父版本 ID(首个版本为 null
is_latest标记当前链中最新版本(每个 group 仅一条为 true)

版本 API

  • GET /api/v1/files/{id}/versions — 查询版本链列表
  • POST /api/v1/files/{id}/versions — 将现有文件指定为新版本的父版本(上传时传入 targetFileId

多租户

隔离策略

层级隔离方式
数据库tenant_id 字段,MyBatis 自动注入
RedisKey 前缀 tenant:{tenantId}:
S3 存储路径 /{tenantId}/{userId}/
DubboContext 透传 TenantContext

租户上下文控制

@TenantScope 注解用于声明式租户隔离:

java
// 跨租户查询(定时任务)
@TenantScope(ignoreIsolation = true)
@Scheduled(cron = "0 0 3 * * ?")
public void cleanupDeletedFiles() { ... }

// 切换到指定租户
@TenantScope(tenantId = 1)
public void migrateDataForTenant() { ... }

实时通知(SSE)

服务器推送事件(Server-Sent Events)为连接的客户端提供实时更新。

多连接架构

系统支持同一用户的多个同时连接:

连接配置

参数默认值说明
每用户最大连接数5超出时关闭最旧连接
心跳间隔30 秒保活信号
连接超时30 分钟无活动后自动关闭
重连延迟(基础)2 秒客户端指数退避重连基础间隔
重连延迟(上限)30 秒客户端指数退避重连上限
最大重连次数5达到上限后转为手动重连

事件类型

事件载荷说明
connected{ connectionId }连接确认
notification{ title, content }通用通知
message-received{ conversationId, preview }会话新消息
file-record-success{ fileName, fileHash, status }文件存证成功通知
file-record-failed{ fileName, status, reason }文件存证失败通知
announcement-published{ id, title }系统公告
ticket-updated{ ticketId, status }工单状态变更
badge-update{ unreadMessages, tickets }UI 徽章数量更新
friend-request{ requesterName, ... }新好友请求
friend-accepted{ friendName, ... }好友请求被接受
friend-share{ sharerName, fileCount, ... }好友文件分享
audit-alert{ type, message, details, severity }审计异常告警(管理员/监控员)
integrity-alert{ alertType, fileHash, details }存储完整性异常告警(管理员)

SSE 认证握手

SSE 连接采用短期一次性令牌:

  1. 登录态下调用 POST /api/v1/auth/tokens/sse 获取短期令牌(需常规 JWT)
  2. 使用 GET /api/v1/sse/connect?token={sseToken}&connectionId={optional} 建立连接

GET /api/v1/sse/connect 为公开端点,但依赖短期令牌完成认证;不是匿名开放连接。

监控容量口径

系统监控新增容量聚合端点:

  • GET /api/v1/system/storage-capacity

返回结构包含:

  • 集群维度:totalCapacityBytesusedCapacityBytesavailableCapacityBytes
  • 数据质量:degradedsource
  • 细粒度明细:nodes[]domains[]

GET /api/v1/system/statstotalStorage 已优先使用该容量聚合结果;当 Dubbo 调用失败时,回退到 totalFiles * 1MB 的估算逻辑,并输出固定日志标记 MONITOR_STORAGE_CAPACITY_FALLBACK

下载策略(前端)

前端按文件大小和浏览器能力动态选择下载策略:

  • 小文件:内存下载(in-memory)
  • 大文件:优先流式下载(streaming)
  • 超大文件或浏览器能力不足:后端代理或阻断并提示

默认阈值(platform-frontend/src/lib/utils/fileSize.ts):

阈值常量默认值
LARGE_FILE_WARNING_THRESHOLD500MB
STREAMING_RECOMMENDED_THRESHOLD1GB
MAX_SAFE_INMEMORY_SIZE2GB
MAX_DOWNLOADABLE_SIZE100GB

配额治理

平台支持按用户和租户维度的存储配额管控,提供两种执行模式:

  • SHADOW(默认):配额违规仅记录和告警,不阻止上传。在正式执行前用于观察影响。
  • ENFORCE:超出配额的上传会被拒绝,返回 50013 QUOTA_EXCEEDED

灰度策略:

属性说明
quota.enforcement-mode全局模式:SHADOWENFORCE
quota.rollout.strategyTENANT_WHITELIST — 仅白名单内租户使用 ENFORCE
quota.rollout.enforce-tenant-whitelist逗号分隔的 ENFORCE 模式租户 ID 列表
quota.rollout.force-shadow强制所有租户使用 SHADOW 模式

对账:定时任务(quota.reconcile.cron,默认每 30 分钟)重新计算使用量快照,修正缓存与实际使用量之间的偏差。

API 端点:

  • GET /api/v1/files/quota — 查询当前用户配额状态
  • POST /api/v1/admin/quota/rollout/audits — 写入灰度审计记录(管理员)
  • GET /api/v1/admin/quota/rollout/audits — 查询灰度审计记录(管理员)

批量下载

前端支持多选文件批量下载:

  1. 选择:用户从文件列表中选择多个文件
  2. 并行下载:文件按可配置的并发数同时下载
  3. 自动重试:下载失败的文件自动重试
  4. 指标上报:完成后,前端通过 POST /api/v1/files/download-batches/report 上报批量下载质量指标

上报载荷包含总文件数、成功/失败数、重试次数、总耗时、错误类型分布等,用于后端质量可观测。

关键词搜索模式

文件查询(GET /api/v1/files)支持 keywordMode 参数控制 keyword 的匹配方式:

模式文件名匹配文件哈希匹配适用场景
FUZZY(默认)LIKE %keyword%LIKE %keyword%通用搜索
PREFIXLIKE keyword%精确匹配索引加速查询
EXACT_HASH不搜索仅精确匹配哈希直查
AUTO取决于关键词取决于关键词智能检测

AUTO 模式会检查关键词:如果匹配十六进制哈希模式(/^[0-9a-fA-F]{32,128}$/),则解析为 EXACT_HASH;否则解析为 PREFIX

前端 Leader 选举

对于多标签页场景,前端使用 BroadcastChannel 进行 Leader 选举:

  • Leader 标签页:维护单一 SSE 连接
  • Follower 标签页:通过 BroadcastChannel 接收事件
  • 故障转移:Leader 标签页关闭时自动选举新 Leader

这可以防止同一浏览器建立多个 SSE 连接,减少服务器负载。

Released under the Apache 2.0 License.