基于 FISCO BCOS 区块链和 S3兼容存储 分布式存储的企业级文件存证平台。
业务特性
- 区块链存证:文件元数据上链,保证不可篡改和可追溯
- 分布式存储:S3兼容存储 双副本冗余,任一副本可读
- 分片上传:大文件断点续传,支持 AES-GCM / ChaCha20-Poly1305 可配置加密
- 文件分享:生成带访问次数限制的分享码
- 协作通知:站内消息、公告广播、工单系统、会话管理
- SSE 实时推送:文件状态变更、消息通知实时送达
平台能力
- 分布式事务:Saga + Outbox 模式保证跨服务数据一致性
- 弹性容错:Resilience4j 熔断、重试、限流
- 权限控制:RBAC 细粒度权限、资源所有权校验、分级限流
- 安全机制:JWT 认证(HMAC512)、ID 混淆、CORS 白名单
- 多租户隔离:数据库/Redis/S3兼容存储 路径租户隔离
- 审计追踪:完整的操作日志和审计记录
- CQRS 架构:FileQueryService 读写分离,Virtual Thread 异步查询
- 多级缓存:Caffeine 本地缓存 + Redis 分布式缓存
Infrastructure
┌────────┐ ┌───────┐ ┌──────────┐ ┌───────┐ ┌────────────┐
│ Nacos │ │ MySQL │ │ RabbitMQ │ │ Redis │ │ S3存储集群 │
│ :8848 │ │ :3306 │ │ :5672 │ │ :6379 │ │ :9000 │
└────┬───┘ └───┬───┘ └────┬─────┘ └───┬───┘ └─────┬──────┘
│ │ │ │ │
└──────────┴───────────┴────────────┴────────────┘
│
┌──────────────────────────┴──────────────────────────┐
│ platform-api │
│ FiscoExternalService, StorageExternalService │
└──────────────────────────┬──────────────────────────┘
│ implements
┌─────────────────────┼─────────────────────┐
│ │ │
▼ │ ▼
┌─────────────────┐ │ ┌─────────────────┐
│ platform-fisco │ │ │ platform-storage│
│ Dubbo Provider │ │ │ Dubbo Provider │
│ Port 8091 │ │ │ Port 8092 │
└────────┬────────┘ │ └────────┬────────┘
│ │ │
│ Dubbo RPC ▼ Dubbo RPC │
│ ┌─────────────────┐ │
└───────────►│ platform-backend│◄───────────┘
│ Dubbo Consumer │
│ REST API :8000 │
└────────┬────────┘
│
┌────────▼────────┐
│ FISCO BCOS Node │
│ Peer :20200 │
└─────────────────┘
Client Backend S3兼容存储 Blockchain
│ │ │ │
│── 1. start upload ──►│ │ │
│◄─── upload session ──│ │ │
│ │ │ │
│── 2. upload chunk ──►│ │ │
│ │── AES-GCM encrypt ─│ │
│ │── store chunk ────►│ │
│ │◄── chunk stored ───│ │
│◄─── chunk ack ───────│ │ │
│ │ │ │
│── 3. complete ──────►│ │ │
│ │─────── Saga Transaction ───────────────►│
│ │ │── store metadata ─►│
│ │ │◄── tx hash ────────│
│ │◄── Outbox publish ─│ │
│◄─── file record ─────│ │ │
│ │ │ │
│◄══ 4. SSE: status ═══│ │ │
关键步骤说明
- start: 创建上传会话,返回 uploadId 和分片参数
- chunk: 分片上传,每片使用配置的算法(AES-GCM/ChaCha20)加密后双写 S3兼容存储
- complete: 触发 Saga 事务,合并分片、计算哈希、上链存证
- SSE: 实时推送上传进度和存证结果
| 步骤 | 正向操作 | 补偿操作 |
|---|---|---|
| PENDING | 初始化 | - |
| MINIO_UPLOADING | S3兼容存储 存储分片 | 清理已存储分片 |
| MINIO_UPLOADED | 分片存储完成 | 删除 S3兼容存储 文件 |
| CHAIN_STORING | 区块链存证 | 标记链上记录删除 |
| COMPLETED | 提交成功 | - |
补偿策略:指数退避重试(初始 1s,最大 5 次),失败后进入人工处理队列。
SagaCompensationHelper 使用 REQUIRES_NEW 事务传播级别,确保每个补偿步骤独立提交:
问题背景:
- 外部调用(如 MinIO 删除)成功后,若后续事务回滚,状态会不一致
- 重试时无法判断哪些步骤已完成
解决方案:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void persistPayloadInNewTransaction(FileSaga saga, String payloadJson) {
// 补偿步骤完成后立即在独立事务中持久化状态
sagaMapper.updatePayloadById(saga.getId(), payloadJson);
}优势:
- 每个补偿步骤完成后立即提交(无依赖于外层事务)
- 重试时根据持久化的步骤状态跳过已完成操作
- 即使系统崩溃也能恢复到一致状态
| 分类 | 技术 | 版本 |
|---|---|---|
| 语言/框架 | Java, Spring Boot | 21, 3.2.11 |
| 微服务 | Apache Dubbo (Triple), Nacos | 3.3.3 |
| 区块链 | FISCO BCOS, Solidity | 3.8.0, ^0.8.11 |
| 区块链扩展 | Web3j (Besu 支持) | 4.9.8 |
| 存储 | S3兼容存储, MySQL, Redis | 8.5.9, 8.0+, 6.0+ |
| S3 客户端 | AWS SDK v2 | 2.29.30 |
| ORM | MyBatis Plus, Druid | 3.5.9, 1.2.23 |
| 消息队列 | RabbitMQ | 3.8+ |
| 弹性设计 | Resilience4j | 2.2.0 |
| 分布式锁 | Redisson | 3.35.0 |
| 数据库迁移 | Flyway | 9.22.3 |
| 监控 | Micrometer, Prometheus | - |
| 服务 | 端口 | 用途 |
|---|---|---|
| Nacos | 8848 | 服务发现和配置中心 |
| MySQL | 3306 | 数据库 |
| Redis | 6379 | 缓存和分布式锁 |
| RabbitMQ | 5672 | 消息队列 |
| S3兼容存储 | 9000 | 对象存储(建议双节点) |
| FISCO BCOS | 20200 | 区块链节点 |
cp .env.example .env
vim .env # 填入实际配置# 1. 安装共享接口
mvn -f platform-api/pom.xml clean install
# 2. 构建各模块
mvn -f platform-backend/pom.xml clean package -DskipTests
mvn -f platform-fisco/pom.xml clean package -DskipTests
mvn -f platform-storage/pom.xml clean package -DskipTests# 使用启动脚本(推荐)
./scripts/start.sh all
# 或手动启动(按顺序)
source .env
java -jar platform-storage/target/platform-storage-0.0.1-SNAPSHOT.jar --spring.profiles.active=local
java -jar platform-fisco/target/platform-fisco-0.0.1-SNAPSHOT.jar --spring.profiles.active=local
java -jar platform-backend/backend-web/target/backend-web-0.0.1-SNAPSHOT.jar --spring.profiles.active=localProfile: local, dev, prod
| 端点 | 地址 |
|---|---|
| Swagger UI | http://localhost:8000/record-platform/swagger-ui.html |
| 健康检查 | http://localhost:8000/record-platform/actuator/health |
| Prometheus | http://localhost:8000/record-platform/actuator/prometheus |
生产环境 Swagger 和 Druid 监控已禁用
RecordPlatform/
├── platform-api/ # Dubbo 接口定义
│ └── external/ # BlockChainService, DistributedStorageService
│
├── platform-backend/ # 后端主服务 (Dubbo Consumer)
│ ├── backend-web/ # REST 控制器、过滤器、配置
│ │ ├── controller/ # API 端点
│ │ ├── aspect/ # AOP 切面 (权限、限流)
│ │ ├── security/ # 自定义 SpEL 表达式
│ │ └── resources/db/migration/ # Flyway 迁移脚本
│ ├── backend-service/ # 业务逻辑、Saga、Outbox、权限服务
│ ├── backend-dao/ # MyBatis Plus Mapper、实体
│ └── backend-common/ # 工具类、常量、注解、分布式锁
│
├── platform-fisco/ # 区块链服务 (Dubbo Provider)
│ ├── contract/ # Solidity 智能合约
│ └── adapter/ # 多链适配器 (FISCO/BSN/Besu)
│
├── platform-storage/ # 存储服务 (Dubbo Provider)
│
├── platform-frontend/ # Svelte 5 + SvelteKit 前端 (独立部署)
│
└── scripts/ # 启动脚本
- 算法:HMAC512(升级自 HMAC256)
- 签发:登录成功后签发 Token,包含 issuer/audience 声明
- 校验:启动时验证密钥强度(长度 ≥32、熵值 ≥128 bits、弱密钥检测)
- 黑名单:退出登录后 Token 加入 Redis 黑名单
- IP 限流:单 IP 5 分钟内最多 10 次失败尝试
- 账号锁定:同账号连续失败 5 次锁定 15 分钟
- 验证码:登录失败 3 次后强制验证码
角色定义
| 角色 | 说明 | 默认权限 |
|---|---|---|
user |
普通用户 | 文件读写删除分享、工单创建、消息收发 |
admin |
管理员 | 所有权限 |
monitor |
监控员 | 只读权限 + 审计日志查看 |
权限码格式
module:action
示例:file:read, file:admin, ticket:write, system:audit
使用方式
// 权限码检查
@PreAuthorize("hasPerm('file:admin')")
// 多权限(任一满足)
@PreAuthorize("hasAnyPerm('file:read', 'file:admin')")
// 角色检查
@PreAuthorize("isAdmin()")
@PreAuthorize("isAdminOrMonitor()")
// 资源所有权检查
@PreAuthorize("isOwner(#file.uid)")
@PreAuthorize("isOwner(#userId) or hasPerm('file:admin')")资源所有权注解
@RequireOwnership(
resourceIdParam = "id", // 参数名
ownerIdField = "uid", // 实体中所有者字段
resourceClass = File.class, // 实体类
adminBypass = true // 管理员跳过校验
)
public Result<File> getFile(@PathVariable Long id) { ... }支持按角色设置不同的限流阈值:
@RateLimit(
limit = 10, // 普通用户:10次/分钟
period = 60, // 时间窗口(秒)
adminLimit = 100, // 管理员:100次/分钟
type = LimitType.USER // 限流维度:USER/IP/API
)限流维度:
USER:按用户 ID 限流(默认)IP:按 IP 地址限流API:全局限流(所有请求共享配额)
基于 Redis Lua 脚本的滑动窗口限流,支持多实例部署:
两种模式:
| 模式 | 方法 | 说明 |
|---|---|---|
| 带封禁 | tryAcquireWithBlock() |
超限后触发封禁,封禁期间直接拒绝 |
| 简单限流 | tryAcquire() |
仅计数限流,无封禁机制 |
返回值:
ALLOWED:允许访问RATE_LIMITED:窗口期内超限(在窗口期内超过限制)BLOCKED:已在封禁列表中
实现原理:
- 使用
INCR原子递增计数器 - 首次访问时设置过期时间(窗口期)
- 超过阈值后设置封禁 key
- 所有操作在单个 Lua 脚本中原子执行
容错策略:Redis 故障时自动放行,避免服务不可用。
外部 API 使用加密 ID,内部使用雪花 ID。采用无状态 AES 加密,无需 Redis 缓存:
外部请求 → AES 解密 → 内部 Snowflake ID → 数据库查询
技术实现
- 加密算法:AES-256-CTR + HMAC-SHA256(SIV 风格)
- 输出格式:前缀 + Base62 编码(约 40 字符)
E前缀:实体 ID(文件、记录等)U前缀:用户 ID
- 密钥派生:HKDF 从
JWT_KEY派生独立的加密密钥和 MAC 密钥 - 安全特性:16 字节 SIV + 10 字节 HMAC 完整性校验,防篡改
数据结构
明文 (16 bytes): [version:1][type:1][id:8][padding:6]
密文 (42 bytes): [SIV:16][AES-CTR(plaintext):16][HMAC:10]
输出: prefix + Base62(密文) ≈ 40 chars
生产环境自动将 HTTP 请求 301 重定向到 HTTPS:
配置项:
server:
port: 443
ssl:
enabled: true
key-store: classpath:keystore.p12
key-store-type: PKCS12
security:
require-ssl: true # 启用 HTTPS 强制
http-redirect-port: 80 # HTTP 监听端口实现方式:
- 额外创建 HTTP Connector 监听 80 端口
- 配置 Security Constraint 强制 CONFIDENTIAL 传输
- 所有 HTTP 请求自动 301 重定向到 HTTPS
- 仅在
prodProfile 且security.require-ssl=true时激活
分片文件加密支持两种算法,可通过配置切换:
| 算法 | 特点 | 适用场景 |
|---|---|---|
| AES-256-GCM | 有 AES-NI 硬件加速时极快(1000+ MB/s) | 物理服务器、有 AES-NI 的 x86 主机 |
| ChaCha20-Poly1305 | 软件实现稳定,抗侧信道攻击 | 容器环境、ARM 设备、混合云 |
配置项
file:
encryption:
# aes-gcm, chacha20, auto
algorithm: chacha20 # 默认 ChaCha20(安全默认值)
benchmark-on-startup: false # auto 模式时启动时运行基准测试分片文件格式 (v2.0+)
[Header: 4B] [IV/Nonce: 12B] [加密数据] [认证标签] [--HASH--\n] [hash] [--NEXT_KEY--\n] [key]
- Header: 魔数
RP(0x52 0x50) + 版本号 (0x01) + 算法标识 (0x01=AES-GCM, 0x02=ChaCha20) - 重要:v2.0 不再支持无头部的旧格式,所有加密文件必须包含版本头
由于 EventSource 不支持自定义 Header,SSE 连接使用 URL token 参数认证:
流程:
POST /api/v1/auth/sse-token获取短期令牌(需 JWT)GET /api/v1/sse/connect?token=<token>建立 SSE 连接
特性:
- 有效期 30 秒
- 一次性使用
- 与主 JWT 绑定用户身份
文件模块采用 CQRS(Command Query Responsibility Segregation)架构,将读写操作分离到独立服务:
┌─────────────────────────────────────────────────────────────────────────┐
│ CQRS 架构 │
├─────────────────────────────────────────────────────────────────────────┤
│ Command Side (FileService) Query Side (FileQueryService) │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ storeFile │ │ getUserFilesList │ ← Caffeine │
│ │ deleteFile │ │ getFileAddress │ │
│ │ generateSharingCode │ │ getTransactionByHash│ ← Caffeine │
│ │ cancelShare │ │ getFileDecryptInfo │ ← Caffeine │
│ │ saveShareFile │ │ getUserShares │ │
│ └─────────────────────┘ └─────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ MySQL (主库写入) │ │ Caffeine + Redis │ │
│ └─────────────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
Query 服务提供异步方法,使用 Java 21 Virtual Thread 执行:
| 方法 | 说明 |
|---|---|
getUserFilesListAsync() |
异步获取用户文件列表 |
getFileAddressAsync() |
异步获取文件分片地址 |
getFileDecryptInfoAsync() |
异步获取解密信息 |
配置启用:
spring:
threads:
virtual:
enabled: true # Java 21 Virtual Threads| 缓存名称 | TTL | 说明 |
|---|---|---|
userFiles |
5min | 用户文件列表 |
fileDecryptInfo |
10min | 文件解密信息 |
sharedFiles |
5min | 分享文件列表 |
transaction |
1h | 区块链交易记录(不可变) |
resilience4j:
circuitbreaker:
instances:
blockChainService:
sliding-window-size: 50
failure-rate-threshold: 50
wait-duration-in-open-state: 30s
storageService:
slow-call-duration-threshold: 8s
slow-call-rate-threshold: 80
retry:
instances:
storageService:
max-attempts: 3
wait-duration: 1s
exponential-backoff-multiplier: 2- 存储服务熔断时,Saga 暂停新事务,已有事务进入补偿队列
- 区块链服务熔断时,文件存储正常,存证延迟执行
| 层级 | 隔离方式 |
|---|---|
| 数据库 | tenant_id 字段,MyBatis 自动注入 |
| Redis | Key 前缀 tenant:{tenantId}: |
| S3兼容存储 | 路径 /{tenantId}/{userId}/ |
| Dubbo | Context 透传 TenantContext |
JWT Token 中的 tenantId 字段,由 JwtAuthenticationFilter 解析后写入 TenantContext。
@TenantScope 注解用于声明式控制方法执行时的租户隔离行为:
| 属性 | 默认值 | 说明 |
|---|---|---|
ignoreIsolation |
false | 忽略租户隔离,跨租户查询 |
tenantId |
-1 | 切换到指定租户执行 |
使用示例
// 定时任务跨租户查询
@TenantScope(ignoreIsolation = true)
@Scheduled(cron = "0 0 3 * * ?")
public void cleanupDeletedFiles() { ... }
// 健康检查跨租户统计
@TenantScope(ignoreIsolation = true)
public Health health() {
long totalPending = mapper.countByStatus("PENDING");
}
// 切换到指定租户执行
@TenantScope(tenantId = 1)
public void migrateDataForTenant() { ... }程序化租户切换
// 按租户循环处理
for (Long tenantId : tenantIds) {
TenantContext.callWithTenant(tenantId, () -> {
return processForTenant(tenantId);
});
}
// 临时跨租户查询
TenantContext.runWithoutIsolation(() -> {
// 跳过租户过滤的查询
});Flyway 脚本位于 platform-backend/backend-web/src/main/resources/db/migration/,Schema 定义位于 platform-backend/db/schema/:
| 版本 | 说明 |
|---|---|
| V1.0.0 | 核心实体 (account, file, image_store) |
| V1.0.1 | Saga + Outbox 表 |
| V1.0.2 | 操作日志表 |
| V1.0.3 | 审计日志表 |
| V1.0.4 | 多租户支持 |
| V1.1.0 | 消息服务表 (message, announcement, ticket, conversation) |
| V1.1.1 | 性能优化复合索引 (文件、日志、工单查询) |
| 06_permission | 权限控制表 (sys_permission, sys_role_permission, account_role_audit) |
启动时自动执行,或手动初始化:
CREATE DATABASE RecordPlatform CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;| 任务 | Cron / 间隔 | 说明 |
|---|---|---|
| 文件清理 | 0 0 3 * * ? |
清理软删除超过 30 天的文件 |
| Outbox 清理 | 0 0 3 * * ? |
清理已发送/失败事件 |
| Saga 补偿 | 30s 间隔 | 处理待补偿的 Saga 事务 |
| Outbox 发布 | 2s 间隔 | 发布待发送的 Outbox 事件 |
# 文件清理
file:
cleanup:
retention-days: 30 # 保留天数
batch-size: 100 # 单次处理数量
cron: 0 0 3 * * ?
# Outbox 清理
outbox:
cleanup:
sent-retention-days: 7 # 已发送保留 7 天
failed-retention-days: 30 # 失败保留 30 天
cron: 0 0 3 * * ?
# Saga 补偿
saga:
compensation:
max-retries: 5
batch-size: 50
poll-interval-ms: 30000所有定时任务按租户分别执行,确保:
- 单租户失败不影响其他租户
- 使用分布式锁防止多实例重复处理
- 自动恢复租户上下文
主要配置项(完整列表见 .env.example):
| 分类 | 变量 | 说明 |
|---|---|---|
| 数据库 | DB_HOST, DB_PORT, DB_NAME, DB_USERNAME, DB_PASSWORD |
MySQL 连接 |
| Redis | REDIS_HOST, REDIS_PORT, REDIS_PASSWORD |
Redis 连接 |
| Nacos | NACOS_HOST, NACOS_USERNAME, NACOS_PASSWORD |
配置中心 |
| S3兼容存储 | MINIO_ENDPOINT, MINIO_ACCESS_KEY, MINIO_SECRET_KEY |
对象存储 |
| RabbitMQ | RABBITMQ_ADDRESSES, RABBITMQ_USERNAME, RABBITMQ_PASSWORD |
消息队列 |
| 安全 | JWT_KEY |
JWT 签名 + ID 加密密钥派生(至少 32 字符) |
| 区块链 | FISCO_PEER_ADDRESS, FISCO_STORAGE_CONTRACT, FISCO_SHARING_CONTRACT |
合约配置 |
| 区块链 | BLOCKCHAIN_ACTIVE |
激活的区块链类型 (local-fisco/bsn-fisco/bsn-besu) |
| SSL | SERVER_SSL_KEY_STORE, SERVER_SSL_KEY_STORE_PASSWORD |
HTTPS 证书配置(生产环境) |
| SSL | SECURITY_REQUIRE_SSL, SECURITY_HTTP_REDIRECT_PORT |
HTTPS 强制和重定向端口 |
注:
ID_SECURITY_KEY在 v2.0 后不再需要,ID 加密密钥从JWT_KEY自动派生
| 端点 | 说明 |
|---|---|
/actuator/health |
健康状态 (DB/Redis/RabbitMQ/S3兼容存储/Saga/Outbox/Encryption) |
/actuator/prometheus |
Prometheus 指标 |
/actuator/circuitbreakers |
熔断器状态 |
/actuator/retries |
重试统计 |
saga_total{status=started|completed|compensated|failed}: Saga 状态计数saga_duration{phase=execution|compensation}: Saga 执行/补偿耗时saga_running: 运行中 Saga 数量saga_pending_compensation: 待补偿 Saga 数量outbox_events_total{status=published|failed}: Outbox 事件发布计数outbox_publish_latency: Outbox 发布延迟outbox_pending: 待发送 Outbox 事件数outbox_exhausted: 超过最大重试次数的 Outbox 事件数
/actuator/health 端点包含加密策略状态:
{
"encryption": {
"status": "UP",
"details": {
"algorithm": "ChaCha20-Poly1305",
"selectionReason": "explicitly configured",
"ivSize": 12,
"tagBitLength": 128,
"likelyHasAesNi": true
}
}
}# Outbox 健康阈值
outbox:
health:
pending-threshold: 500 # 待发送 >500 → DEGRADED
failed-threshold: 20 # 失败 >20 → DOWN
# Saga 健康阈值
saga:
health:
running-threshold: 100 # 运行中 >100 → DEGRADED
failed-threshold: 10 # 失败 >10 → DOWN
pending-compensation-threshold: 50 # 待补偿 >50 → DEGRADED健康检查使用 @TenantScope(ignoreIsolation = true) 进行跨租户统计,返回全局健康状态。
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /ask-code |
获取验证码 |
| POST | /register |
用户注册 |
| POST | /reset-password |
重置密码 |
| POST | /sse-token |
获取 SSE 连接专用短期令牌 (30s 有效期) |
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /info |
获取用户信息 |
| POST | /modify-email |
修改邮箱 |
| POST | /change-password |
修改密码 |
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /list |
文件列表 |
| GET | /page |
分页查询 |
| GET | /{id} |
根据 ID 获取文件详情 |
| GET | /address |
获取预签名下载地址 |
| GET | /download |
下载文件 |
| GET | /decryptInfo |
获取文件解密信息(客户端解密用) |
| GET | /getTransaction |
获取链上交易记录 |
| DELETE | /deleteByHash |
按哈希批量删除 |
| DELETE | /deleteById |
按 ID 批量删除(管理员) |
| POST | /share |
分享文件 |
| GET | /shares |
获取我的分享列表(分页) |
| GET | /getSharingFiles |
获取分享文件列表 |
| POST | /saveShareFile |
保存分享文件 |
| DELETE | /share/{shareCode} |
取消分享 |
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /upload/start |
开始上传 |
| POST | /upload/chunk |
上传分片 |
| POST | /upload/complete |
完成上传 |
| POST | /upload/pause |
暂停上传 |
| POST | /upload/resume |
恢复上传 |
| POST | /upload/cancel |
取消上传 |
| GET | /upload/check |
检查上传状态 |
| GET | /upload/progress |
获取上传进度 |
公告 /api/v1/announcements
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | / |
公告列表(分页) |
| GET | /{id} |
公告详情 |
| GET | /unread-count |
未读公告数 |
| POST | /{id}/read |
标记已读 |
| POST | /read-all |
标记全部已读 |
| POST | / |
发布公告(管理员) |
| PUT | /{id} |
编辑公告(管理员) |
| DELETE | /{id} |
删除公告(管理员) |
| GET | /admin/list |
管理员公告列表 |
私信 /api/v1/messages
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | / |
发送私信 |
| POST | /to/{receiverId} |
发送私信(指定接收者) |
| GET | /unread-count |
未读私信数 |
会话 /api/v1/conversations
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | / |
会话列表(分页) |
| GET | /{id} |
会话详情及消息 |
| GET | /unread-count |
未读会话数 |
| POST | /{id}/read |
标记会话已读 |
| DELETE | /{id} |
删除会话 |
工单 /api/v1/tickets
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | / |
我的工单列表 |
| GET | /{id} |
工单详情 |
| POST | / |
创建工单 |
| POST | /{id}/reply |
回复工单 |
| POST | /{id}/close |
关闭工单 |
| POST | /{id}/confirm |
确认完成 |
| GET | /pending-count |
待处理工单数 |
| GET | /admin/list |
管理员工单列表 |
| PUT | /admin/{id}/assign |
分配处理人 |
| PUT | /admin/{id}/status |
更新状态 |
SSE /api/v1/sse
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /connect |
建立 SSE 连接 |
| DELETE | /disconnect |
断开连接 |
| GET | /status |
获取连接状态 |
SSE 事件类型:NEW_MESSAGE, NEW_ANNOUNCEMENT, TICKET_UPDATE, TICKET_REPLY, HEARTBEAT, CONNECTED
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /overview |
审计概览 |
| GET | /logs/page |
审计日志 |
| POST | /logs/export |
导出日志 |
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /list |
权限列表(分页) |
| GET | /modules |
获取所有模块名 |
| POST | / |
创建权限定义 |
| PUT | /{id} |
更新权限定义 |
| DELETE | /{id} |
删除权限定义 |
| GET | /roles/{role} |
获取角色权限列表 |
| POST | /roles/{role}/grant |
为角色授予权限 |
| DELETE | /roles/{role}/revoke |
撤销角色权限 |
详细 API 文档请访问 Swagger UI。
| 合约 | 功能 |
|---|---|
| Storage.sol | 文件元数据存储、查询、删除 |
| Sharing.sol | 文件分享、取消分享、访问控制、分享码管理 |
合约地址通过环境变量配置:FISCO_STORAGE_CONTRACT, FISCO_SHARING_CONTRACT
| 方法 | 参数 | 说明 |
|---|---|---|
shareFiles |
uploader, fileHashes[], expireMinutes | 创建分享,返回 6 位分享码 |
getSharedFiles |
shareCode | 获取分享文件(校验有效期) |
cancelShare |
shareCode | 取消分享(设置 isValid=false) |
getUserShareCodes |
uploader | 获取用户所有分享码列表 |
getShareInfo |
shareCode | 获取分享详情(不校验有效性) |
| 事件 | 参数 | 触发时机 |
|---|---|---|
FileShared |
shareCode, uploader, fileHashes[], expireTime | 创建分享时 |
ShareCancelled |
shareCode, uploader | 取消分享时 |
支持多种区块链网络,通过环境变量切换:
| 链类型 | 配置值 | 说明 |
|---|---|---|
| Local FISCO | local-fisco |
本地 FISCO BCOS 节点 (默认) |
| BSN FISCO | bsn-fisco |
区块链服务网络 FISCO |
| Hyperledger Besu | bsn-besu |
Hyperledger Besu (EVM 兼容) |
配置:
blockchain:
active: ${BLOCKCHAIN_ACTIVE:local-fisco}
bsn-fisco:
node-id: <bsn-node-id>
peers: ["<peer-address>"]
bsn-besu:
rpc-url: https://<besu-rpc>
chain-id: <chain-id>Q: 启动报 Nacos 连接失败
A: 确认 Nacos 已启动,检查 NACOS_HOST 配置。
Q: S3兼容存储 上传失败
A: 检查 S3兼容存储 集群健康:/actuator/health 中 minio 状态。双节点至少一个在线即可写入。
Q: Saga 事务卡在 PENDING
A: 检查 file_saga 表状态,查看 outbox_event 是否有积压。可通过 /actuator/health 查看 Saga/Outbox 健康指标。
Q: 区块链存证延迟
A: 检查 FISCO 节点连接和熔断器状态:/actuator/circuitbreakers。
Q: 解密旧版本文件失败 A: v2.0 不再支持无版本头的旧格式文件。如需迁移旧数据,需重新加密或使用数据迁移工具。
Q: 如何选择加密算法
A: 如果服务器有 AES-NI 指令集支持,推荐 aes-gcm;容器/ARM 环境推荐 chacha20。可设置 algorithm: auto + benchmark-on-startup: true 让系统自动选择。
当前版本:v7.0
| 阶段 | 状态 | 完成内容 |
|---|---|---|
| P0 | ✅ | Bug 修复、CORS 安全加固、大文件上传超时优化 |
| P1 | ✅ | HTTPS 强制、分布式流控改造、定时任务分布式锁、Saga 补偿原子化 |
| P2 | ✅ | SkyWalking Agent 部署、健康检查指标补全、结构化日志标准化 |
| P3 | ✅ | 存储路径租户隔离、Redis Key 租户隔离、Dubbo 租户传播 |
| P4 | ✅ | 多链适配器架构、智能合约优化、数据库索引优化 |
| P5 | 🔄 | CQRS/虚拟线程(✅)、前端核心功能开发 |
系统成熟度:8.5/10 - 多链架构就绪,CQRS 读写分离完成,进入前端开发阶段
详细演进规划参见 EVOLUTION_PLAN.md
Apache License 2.0