You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CQRS架构下Web API生成QR Code并存储返回的实现方案咨询

解决方案:CQRS架构下单请求完成QR Code生成、存储与返回

核心思路

严格遵循CQRS"命令改状态、查询读状态"的原则,通过同一请求内先执行命令、再执行查询的方式完成需求,无需前端发起两次请求。命令负责生成QR Code并将加密数据存入数据库,查询负责读取刚生成的Base64结果,通过唯一ID关联两者。


步骤1:定义命令与命令处理器

命令负责生成QR Code、加密数据并写入数据库,不返回结果(符合CQRS命令规范)。

命令类

public class GenerateAndSaveQrCodeCommand : IRequest
{
    // 前端传入的原始数据
    public string ReceiverData { get; set; }
    // 预先生成的唯一标识,用于后续查询关联
    public Guid QrCodeId { get; set; } = Guid.NewGuid();
}

命令处理器

internal class GenerateAndSaveQrCodeCommandHandler : IRequestHandler<GenerateAndSaveQrCodeCommand>
{
    private readonly IQrCodeClient<string> _qrCodeGenerator;
    private readonly AppDbContext _dbContext;

    public GenerateAndSaveQrCodeCommandHandler(IQrCodeClient<string> qrCodeGenerator, AppDbContext dbContext)
    {
        _qrCodeGenerator = qrCodeGenerator;
        _dbContext = dbContext;
    }

    public async Task Handle(GenerateAndSaveQrCodeCommand request, CancellationToken cancellationToken)
    {
        // 1. 调用外部服务生成QR Code的Base64字符串
        var qrBase64 = await _qrCodeGenerator.GetQrCode(request.ReceiverData, cancellationToken);
        
        // 2. 对原始数据执行加密(替换为你的实际加密逻辑)
        var encryptedData = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(request.ReceiverData));
        
        // 3. 将加密数据与Base64存入数据库
        var qrRecord = new QrCodeRecord
        {
            Id = request.QrCodeId,
            EncryptedData = encryptedData,
            QrBase64 = qrBase64,
            CreatedAt = DateTime.UtcNow
        };
        
        await _dbContext.QrCodeRecords.AddAsync(qrRecord, cancellationToken);
        await _dbContext.SaveChangesAsync(cancellationToken);
    }
}

步骤2:定义查询与查询处理器

查询负责根据唯一ID读取刚生成的QR Code Base64结果。

查询类

public class GetQrCodeByIdQuery : IRequest<string>
{
    public Guid QrCodeId { get; set; }
}

查询处理器

internal class GetQrCodeByIdQueryHandler : IRequestHandler<GetQrCodeByIdQuery, string>
{
    private readonly AppDbContext _dbContext;

    public GetQrCodeByIdQueryHandler(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task<string> Handle(GetQrCodeByIdQuery request, CancellationToken cancellationToken)
    {
        var qrRecord = await _dbContext.QrCodeRecords
            .FirstOrDefaultAsync(r => r.Id == request.QrCodeId, cancellationToken);
        
        return qrRecord?.QrBase64 ?? throw new KeyNotFoundException("QR Code记录不存在");
    }
}

步骤3:修改API端点

在同一请求中顺序执行命令与查询,返回结果给前端。注意用POST方法(生成操作属于状态变更,不符合GET语义)。

app.MapPost("/generateQrCode", async ([FromBody] string receiverData, [FromServices] IMediator mediator) =>
{
    var qrCodeId = Guid.NewGuid();
    
    // 执行命令:生成并保存QR Code
    await mediator.Send(new GenerateAndSaveQrCodeCommand
    {
        ReceiverData = receiverData,
        QrCodeId = qrCodeId
    });
    
    // 执行查询:获取刚生成的QR Code Base64
    var qrBase64 = await mediator.Send(new GetQrCodeByIdQuery
    {
        QrCodeId = qrCodeId
    });
    
    return Results.Ok(new { QrCodeBase64 = qrBase64 });
});

关键说明

  1. 避免查询修改状态:你的原代码将数据库写入逻辑放在查询处理器中,违反了CQRS"查询只读"的原则,必须拆分到命令处理器。
  2. 命令与查询的关联:通过预先生成的QrCodeId确保查询能精准获取刚生成的记录,避免并发场景下的数据混乱。
  3. 一致性保障:命令执行完成后才触发查询,依赖数据库的事务提交保证数据已持久化;单实例场景下无需额外处理,分布式场景可引入缓存(如Redis)临时存储生成结果,提升查询可靠性。

内容的提问来源于stack exchange,提问作者philomelka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 05:02:00