Skip to content

导出中心

MineShop 的导出中心以插件形式提供,支持大数据量异步导出、多种格式输出、下载中心管理等功能。

🎯 系统架构

┌─────────────────────────────────────────────────────────────┐
│                      导出中心插件                             │
├─────────────────────────────────────────────────────────────┤
│  ExportService  │  DtoHydrator  │  Writer  │  下载中心       │
├─────────────────────────────────────────────────────────────┤
│                    异步队列 (export-queue)                    │
├─────────────────────────────────────────────────────────────┤
│  PhpSpreadsheet Writer  │  XlsWriter Writer  │  CSV Writer   │
└─────────────────────────────────────────────────────────────┘

✨ 核心特性

1. 注解驱动

使用 @ExportColumn 注解定义导出字段:

php
// OrderExportDto.php
class OrderExportDto
{
    #[ExportColumn(name: '订单号', width: 20)]
    public string $orderNo;

    #[ExportColumn(name: '会员昵称', width: 15)]
    public string $memberNickname;

    #[ExportColumn(name: '商品名称', width: 30)]
    public string $productName;

    #[ExportColumn(name: '订单金额', width: 12, format: 'money')]
    public int $totalAmount;

    #[ExportColumn(name: '订单状态', width: 10)]
    public string $statusText;

    #[ExportColumn(name: '下单时间', width: 20, format: 'datetime')]
    public ?string $createdAt;
}

2. 异步导出

php
// ExportService.php
public function export(string $dtoClass, iterable $dataSource, array $options = []): ExportTask
{
    // 1. 创建导出任务
    $task = $this->createTask($dtoClass, $options);
    
    // 2. 投递到异步队列
    $this->queueDriver->push(new ExportJob(
        taskId: $task->id,
        dtoClass: $dtoClass,
        dataSource: $dataSource,
        options: $options,
    ));
    
    return $task;
}

3. 分批处理

php
// AbstractExportWriter.php
protected function writeInBatches(iterable $data, int $batchSize = 1000): void
{
    $batch = [];
    $count = 0;
    
    foreach ($data as $row) {
        $batch[] = $row;
        $count++;
        
        if ($count >= $batchSize) {
            $this->writeBatch($batch);
            $batch = [];
            $count = 0;
            
            // 更新进度
            $this->updateProgress();
        }
    }
    
    // 写入剩余数据
    if (!empty($batch)) {
        $this->writeBatch($batch);
    }
}

4. 多种 Writer

php
// PhpSpreadsheetWriter - 兼容性好
class PhpSpreadsheetWriter extends AbstractExportWriter
{
    protected function createWorkbook(): void
    {
        $this->spreadsheet = new Spreadsheet();
        $this->sheet = $this->spreadsheet->getActiveSheet();
    }
}

// XlsWriterWriter - 高性能
class XlsWriterWriter extends AbstractExportWriter
{
    protected function createWorkbook(): void
    {
        $this->excel = new \Vtiful\Kernel\Excel(['path' => $this->tempPath]);
        $this->excel->fileName($this->filename);
    }
}

📦 插件配置

plugin.json

json
{
    "name": "since/export-center",
    "version": "1.0.0",
    "description": "导出中心插件",
    "namespace": "Plugin\\ExportCenter",
    "configProvider": "Plugin\\ExportCenter\\ConfigProvider"
}

ConfigProvider

php
// ConfigProvider.php
class ConfigProvider
{
    public function __invoke()
    {
        return [
            'async_queue' => [
                'export' => [
                    'driver' => RedisDriver::class,
                    'channel' => '{export-queue}',
                    'timeout' => 2,
                    'retry_seconds' => 10,
                    'handle_timeout' => 600,  // 10分钟超时
                    'processes' => 1,
                    'concurrent' => [
                        'limit' => 5,
                    ],
                ],
            ],
            'processes' => [
                ExportConsumerProcess::class,
            ],
        ];
    }
}

🔄 导出流程

┌──────────┐    创建任务    ┌──────────┐    队列消费    ┌──────────┐
│  请求导出 │ ───────────→ │  pending │ ───────────→ │ processing│
└──────────┘              └──────────┘              └──────────┘

                               ┌─────────────────────────┤
                               │                         │
                               ↓                         ↓
                          ┌──────────┐            ┌──────────┐
                          │ completed│            │  failed  │
                          │  已完成   │            │  失败    │
                          └──────────┘            └──────────┘

💻 使用示例

后台导出订单

php
// OrderController.php
public function export(ExportRequest $request): ResponseInterface
{
    $query = $this->queryService->buildExportQuery($request->all());
    
    $task = $this->exportService->export(
        dtoClass: OrderExportDto::class,
        dataSource: $query->cursor(),
        options: [
            'filename' => '订单导出_' . date('YmdHis'),
            'format' => 'xlsx',
        ]
    );
    
    return $this->success([
        'task_id' => $task->id,
        'message' => '导出任务已创建,请在下载中心查看',
    ]);
}

自定义 DTO

php
// ProductExportDto.php
class ProductExportDto
{
    #[ExportColumn(name: '商品ID', width: 10)]
    public int $id;

    #[ExportColumn(name: '商品名称', width: 30)]
    public string $name;

    #[ExportColumn(name: '分类', width: 15)]
    public string $categoryName;

    #[ExportColumn(name: '价格', width: 12, format: 'money')]
    public int $price;

    #[ExportColumn(name: '库存', width: 10)]
    public int $stock;

    #[ExportColumn(name: '状态', width: 10)]
    public string $statusText;
}

📊 下载中心

任务列表

接口方法说明
/admin/export/tasksGET导出任务列表
/admin/export/tasks/{id}GET任务详情
/admin/export/download/{id}GET下载文件
/admin/export/tasks/{id}DELETE删除任务

任务状态

状态说明
pending等待处理
processing处理中
completed已完成
failed失败
expired已过期

🔧 格式化器

php
// 内置格式化器
'money'    => fn($v) => number_format($v / 100, 2),
'datetime' => fn($v) => $v ? date('Y-m-d H:i:s', strtotime($v)) : '',
'date'     => fn($v) => $v ? date('Y-m-d', strtotime($v)) : '',
'boolean'  => fn($v) => $v ? '是' : '否',

⚠️ 注意事项

  1. 内存管理: 使用 cursor() 或 chunk() 避免内存溢出
  2. 超时设置: 大数据量导出需要增加超时时间
  3. 文件清理: 定期清理过期的导出文件
  4. 并发控制: 限制同时进行的导出任务数量

📚 相关文档

基于 Apache-2.0 许可发布 | 感谢 MineAdmin 提供的优秀基础框架