🚀 快速安装
复制以下命令并运行,立即安装此 Skill:
npx skills add https://github.com/wshobson/agents --skill dotnet-backend-patterns
💡 提示:需要 Node.js 和 NPM
.NET 后端开发模式
掌握用于构建生产级 API、MCP 服务器和企业级后端的 C#/.NET 模式,遵循现代最佳实践 (2024/2025)。
何时使用此技能
- 开发新的 .NET Web API 或 MCP 服务器
- 评审 C# 代码的质量和性能
- 使用依赖注入设计服务架构
- 使用 Redis 实现缓存策略
- 编写单元测试和集成测试
- 使用 EF Core 或 Dapper 优化数据库访问
- 使用 IOptions 模式配置应用程序
- 处理错误并实现弹性模式
核心概念
1. 项目结构 (整洁架构)
src/
├── Domain/ # 核心业务逻辑 (无外部依赖)
│ ├── Entities/ # 实体
│ ├── Interfaces/ # 接口
│ ├── Exceptions/ # 异常
│ └── ValueObjects/ # 值对象
├── Application/ # 用例、DTO、验证
│ ├── Services/ # 服务
│ ├── DTOs/ # 数据传输对象
│ ├── Validators/ # 验证器
│ └── Interfaces/ # 接口
├── Infrastructure/ # 外部实现
│ ├── Data/ # EF Core, Dapper 仓储
│ ├── Caching/ # Redis, 内存缓存
│ ├── External/ # HTTP 客户端,第三方 API
│ └── DependencyInjection/ # 服务注册
└── Api/ # 入口点
├── Controllers/ # 或 Minimal API 端点
├── Middleware/ # 中间件
├── Filters/ # 过滤器
└── Program.cs
2. 依赖注入模式
// 按生命周期注册服务
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddApplicationServices(
this IServiceCollection services,
IConfiguration configuration)
{
// Scoped: 每个 HTTP 请求一个实例
services.AddScoped<IProductService, ProductService>();
services.AddScoped<IOrderService, OrderService>();
// Singleton: 应用程序生命周期内一个实例
services.AddSingleton<ICacheService, RedisCacheService>();
services.AddSingleton<IConnectionMultiplexer>(_ =>
ConnectionMultiplexer.Connect(configuration["Redis:Connection"]!));
// Transient: 每次请求都创建新实例
services.AddTransient<IValidator<CreateOrderRequest>, CreateOrderValidator>();
// Options 模式用于配置
services.Configure<CatalogOptions>(configuration.GetSection("Catalog"));
services.Configure<RedisOptions>(configuration.GetSection("Redis"));
// 用于条件创建的工厂模式
services.AddScoped<IPriceCalculator>(sp =>
{
var options = sp.GetRequiredService<IOptions<PricingOptions>>().Value;
return options.UseNewEngine
? sp.GetRequiredService<NewPriceCalculator>()
: sp.GetRequiredService<LegacyPriceCalculator>();
});
// 键控服务 (.NET 8+)
services.AddKeyedScoped<IPaymentProcessor, StripeProcessor>("stripe");
services.AddKeyedScoped<IPaymentProcessor, PayPalProcessor>("paypal");
return services;
}
}
// 使用键控服务
public class CheckoutService
{
public CheckoutService(
[FromKeyedServices("stripe")] IPaymentProcessor stripeProcessor)
{
_processor = stripeProcessor;
}
}
3. 异步/等待模式
// ✅ 正确:一路异步到底
public async Task<Product> GetProductAsync(string id, CancellationToken ct = default)
{
return await _repository.GetByIdAsync(id, ct);
}
// ✅ 正确:使用 WhenAll 并行执行
public async Task<(Stock, Price)> GetStockAndPriceAsync(
string productId,
CancellationToken ct = default)
{
var stockTask = _stockService.GetAsync(productId, ct);
var priceTask = _priceService.GetAsync(productId, ct);
await Task.WhenAll(stockTask, priceTask);
return (await stockTask, await priceTask);
}
// ✅ 正确:在库中使用 ConfigureAwait
public async Task<T> LibraryMethodAsync<T>(CancellationToken ct = default)
{
var result = await _httpClient.GetAsync(url, ct).ConfigureAwait(false);
return await result.Content.ReadFromJsonAsync<T>(ct).ConfigureAwait(false);
}
// ✅ 正确:对带有缓存的代码热路径使用 ValueTask
public ValueTask<Product?> GetCachedProductAsync(string id)
{
if (_cache.TryGetValue(id, out Product? product))
return ValueTask.FromResult(product);
return new ValueTask<Product?>(GetFromDatabaseAsync(id));
}
// ❌ 错误:阻塞异步代码 (有死锁风险)
var result = GetProductAsync(id).Result; // 永远不要这样做
var result2 = GetProductAsync(id).GetAwaiter().GetResult(); // 同样不好
// ❌ 错误:async void (事件处理程序除外)
public async void ProcessOrder() { } // 异常会被丢失
// ❌ 错误:对已经是异步的代码使用不必要的 Task.Run
await Task.Run(async () => await GetDataAsync()); // 浪费线程
4. 使用 IOptions 进行配置
// 配置类
public class CatalogOptions
{
public const string SectionName = "Catalog";
public int DefaultPageSize { get; set; } = 50;
public int MaxPageSize { get; set; } = 200;
public TimeSpan CacheDuration { get; set; } = TimeSpan.FromMinutes(15);
public bool EnableEnrichment { get; set; } = true;
}
public class RedisOptions
{
public const string SectionName = "Redis";
public string Connection { get; set; } = "localhost:6379";
public string KeyPrefix { get; set; } = "mcp:";
public int Database { get; set; } = 0;
}
// appsettings.json
{
"Catalog": {
"DefaultPageSize": 50,
"MaxPageSize": 200,
"CacheDuration": "00:15:00",
"EnableEnrichment": true
},
"Redis": {
"Connection": "localhost:6379",
"KeyPrefix": "mcp:",
"Database": 0
}
}
// 注册
services.Configure<CatalogOptions>(configuration.GetSection(CatalogOptions.SectionName));
services.Configure<RedisOptions>(configuration.GetSection(RedisOptions.SectionName));
// 配合 IOptions 使用 (单例,启动时读取一次)
public class CatalogService
{
private readonly CatalogOptions _options;
public CatalogService(IOptions<CatalogOptions> options)
{
_options = options.Value;
}
}
// 配合 IOptionsSnapshot 使用 (作用域,每个请求重新读取)
public class DynamicService
{
private readonly CatalogOptions _options;
public DynamicService(IOptionsSnapshot<CatalogOptions> options)
{
_options = options.Value; // 每个请求的新值
}
}
// 配合 IOptionsMonitor 使用 (单例,变更时通知)
public class MonitoredService
{
private CatalogOptions _options;
public MonitoredService(IOptionsMonitor<CatalogOptions> monitor)
{
_options = monitor.CurrentValue;
monitor.OnChange(newOptions => _options = newOptions);
}
}
5. Result 模式 (避免将异常用于流程控制)
// 泛型 Result 类型
public class Result<T>
{
public bool IsSuccess { get; }
public T? Value { get; }
public string? Error { get; }
public string? ErrorCode { get; }
private Result(bool isSuccess, T? value, string? error, string? errorCode)
{
IsSuccess = isSuccess;
Value = value;
Error = error;
ErrorCode = errorCode;
}
public static Result<T> Success(T value) => new(true, value, null, null);
public static Result<T> Failure(string error, string? code = null) => new(false, default, error, code);
public Result<TNew> Map<TNew>(Func<T, TNew> mapper) =>
IsSuccess ? Result<TNew>.Success(mapper(Value!)) : Result<TNew>.Failure(Error!, ErrorCode);
public async Task<Result<TNew>> MapAsync<TNew>(Func<T, Task<TNew>> mapper) =>
IsSuccess ? Result<TNew>.Success(await mapper(Value!)) : Result<TNew>.Failure(Error!, ErrorCode);
}
// 在服务中使用
public async Task<Result<Order>> CreateOrderAsync(CreateOrderRequest request, CancellationToken ct)
{
// 验证
var validation = await _validator.ValidateAsync(request, ct);
if (!validation.IsValid)
return Result<Order>.Failure(
validation.Errors.First().ErrorMessage,
"VALIDATION_ERROR");
// 业务规则检查
var stock = await _stockService.CheckAsync(request.ProductId, request.Quantity, ct);
if (!stock.IsAvailable)
return Result<Order>.Failure(
$"库存不足:可用 {stock.Available},请求 {request.Quantity}",
"INSUFFICIENT_STOCK");
// 创建订单
var order = await _repository.CreateAsync(request.ToEntity(), ct);
return Result<Order>.Success(order);
}
// 在控制器/端点中使用
app.MapPost("/orders", async (
CreateOrderRequest request,
IOrderService orderService,
CancellationToken ct) =>
{
var result = await orderService.CreateOrderAsync(request, ct);
return result.IsSuccess
? Results.Created($"/orders/{result.Value!.Id}", result.Value)
: Results.BadRequest(new { error = result.Error, code = result.ErrorCode });
});
数据访问模式
Entity Framework Core
// DbContext 配置
public class AppDbContext : DbContext
{
public DbSet<Product> Products => Set<Product>();
public DbSet<Order> Orders => Set<Order>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 从程序集应用所有配置
modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
// 全局查询过滤器
modelBuilder.Entity<Product>().HasQueryFilter(p => !p.IsDeleted);
}
}
// 实体配置
public class ProductConfiguration : IEntityTypeConfiguration<Product>
{
public void Configure(EntityTypeBuilder<Product> builder)
{
builder.ToTable("Products");
builder.HasKey(p => p.Id);
builder.Property(p => p.Id).HasMaxLength(40);
builder.Property(p => p.Name).HasMaxLength(200).IsRequired();
builder.Property(p => p.Price).HasPrecision(18, 2);
builder.HasIndex(p => p.Sku).IsUnique();
builder.HasIndex(p => new { p.CategoryId, p.Name });
builder.HasMany(p => p.OrderItems)
.WithOne(oi => oi.Product)
.HasForeignKey(oi => oi.ProductId);
}
}
// 使用 EF Core 的仓储
public class ProductRepository : IProductRepository
{
private readonly AppDbContext _context;
public async Task<Product?> GetByIdAsync(string id, CancellationToken ct = default)
{
return await _context.Products
.AsNoTracking()
.FirstOrDefaultAsync(p => p.Id == id, ct);
}
public async Task<IReadOnlyList<Product>> SearchAsync(
ProductSearchCriteria criteria,
CancellationToken ct = default)
{
var query = _context.Products.AsNoTracking();
if (!string.IsNullOrWhiteSpace(criteria.SearchTerm))
query = query.Where(p => EF.Functions.Like(p.Name, $"%{criteria.SearchTerm}%"));
if (criteria.CategoryId.HasValue)
query = query.Where(p => p.CategoryId == criteria.CategoryId);
if (criteria.MinPrice.HasValue)
query = query.Where(p => p.Price >= criteria.MinPrice);
if (criteria.MaxPrice.HasValue)
query = query.Where(p => p.Price <= criteria.MaxPrice);
return await query
.OrderBy(p => p.Name)
.Skip((criteria.Page - 1) * criteria.PageSize)
.Take(criteria.PageSize)
.ToListAsync(ct);
}
}
用于性能的 Dapper
public class DapperProductRepository : IProductRepository
{
private readonly IDbConnection _connection;
public async Task<Product?> GetByIdAsync(string id, CancellationToken ct = default)
{
const string sql = """
SELECT Id, Name, Sku, Price, CategoryId, Stock, CreatedAt
FROM Products
WHERE Id = @Id AND IsDeleted = 0
""";
return await _connection.QueryFirstOrDefaultAsync<Product>(
new CommandDefinition(sql, new { Id = id }, cancellationToken: ct));
}
public async Task<IReadOnlyList<Product>> SearchAsync(
ProductSearchCriteria criteria,
CancellationToken ct = default)
{
var sql = new StringBuilder("""
SELECT Id, Name, Sku, Price, CategoryId, Stock, CreatedAt
FROM Products
WHERE IsDeleted = 0
""");
var parameters = new DynamicParameters();
if (!string.IsNullOrWhiteSpace(criteria.SearchTerm))
{
sql.Append(" AND Name LIKE @SearchTerm");
parameters.Add("SearchTerm", $"%{criteria.SearchTerm}%");
}
if (criteria.CategoryId.HasValue)
{
sql.Append(" AND CategoryId = @CategoryId");
parameters.Add("CategoryId", criteria.CategoryId);
}
if (criteria.MinPrice.HasValue)
{
sql.Append(" AND Price >= @MinPrice");
parameters.Add("MinPrice", criteria.MinPrice);
}
if (criteria.MaxPrice.HasValue)
{
sql.Append(" AND Price <= @MaxPrice");
parameters.Add("MaxPrice", criteria.MaxPrice);
}
sql.Append(" ORDER BY Name OFFSET @Offset ROWS FETCH NEXT @PageSize ROWS ONLY");
parameters.Add("Offset", (criteria.Page - 1) * criteria.PageSize);
parameters.Add("PageSize", criteria.PageSize);
var results = await _connection.QueryAsync<Product>(
new CommandDefinition(sql.ToString(), parameters, cancellationToken: ct));
return results.ToList();
}
// 关联数据的多映射
public async Task<Order?> GetOrderWithItemsAsync(int orderId, CancellationToken ct = default)
{
const string sql = """
SELECT o.*, oi.*, p.*
FROM Orders o
LEFT JOIN OrderItems oi ON o.Id = oi.OrderId
LEFT JOIN Products p ON oi.ProductId = p.Id
WHERE o.Id = @OrderId
""";
var orderDictionary = new Dictionary<int, Order>();
await _connection.QueryAsync<Order, OrderItem, Product, Order>(
new CommandDefinition(sql, new { OrderId = orderId }, cancellationToken: ct),
(order, item, product) =>
{
if (!orderDictionary.TryGetValue(order.Id, out var existingOrder))
{
existingOrder = order;
existingOrder.Items = new List<OrderItem>();
orderDictionary.Add(order.Id, existingOrder);
}
if (item != null)
{
item.Product = product;
existingOrder.Items.Add(item);
}
return existingOrder;
},
splitOn: "Id,Id");
return orderDictionary.Values.FirstOrDefault();
}
}
缓存模式
基于 Redis 的多级缓存
public class CachedProductService : IProductService
{
private readonly IProductRepository _repository;
private readonly IMemoryCache _memoryCache;
private readonly IDistributedCache _distributedCache;
private readonly ILogger<CachedProductService> _logger;
private static readonly TimeSpan MemoryCacheDuration = TimeSpan.FromMinutes(1);
private static readonly TimeSpan DistributedCacheDuration = TimeSpan.FromMinutes(15);
public async Task<Product?> GetByIdAsync(string id, CancellationToken ct = default)
{
var cacheKey = $"product:{id}";
// L1: 内存缓存 (进程内,最快)
if (_memoryCache.TryGetValue(cacheKey, out Product? cached))
{
_logger.LogDebug("L1 缓存命中 {CacheKey}", cacheKey);
return cached;
}
// L2: 分布式缓存 (Redis)
var distributed = await _distributedCache.GetStringAsync(cacheKey, ct);
if (distributed != null)
{
_logger.LogDebug("L2 缓存命中 {CacheKey}", cacheKey);
var product = JsonSerializer.Deserialize<Product>(distributed);
// 填充 L1
_memoryCache.Set(cacheKey, product, MemoryCacheDuration);
return product;
}
// L3: 数据库
_logger.LogDebug("缓存未命中 {CacheKey},从数据库获取", cacheKey);
var fromDb = await _repository.GetByIdAsync(id, ct);
if (fromDb != null)
{
var serialized = JsonSerializer.Serialize(fromDb);
// 填充两个缓存
await _distributedCache.SetStringAsync(
cacheKey,
serialized,
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = DistributedCacheDuration
},
ct);
_memoryCache.Set(cacheKey, fromDb, MemoryCacheDuration);
}
return fromDb;
}
public async Task InvalidateAsync(string id, CancellationToken ct = default)
{
var cacheKey = $"product:{id}";
_memoryCache.Remove(cacheKey);
await _distributedCache.RemoveAsync(cacheKey, ct);
_logger.LogInformation("已使缓存失效 {CacheKey}", cacheKey);
}
}
// 过期期间重用陈旧数据模式
public class StaleWhileRevalidateCache<T>
{
private readonly IDistributedCache _cache;
private readonly TimeSpan _freshDuration;
private readonly TimeSpan _staleDuration;
public async Task<T?> GetOrCreateAsync(
string key,
Func<CancellationToken, Task<T>> factory,
CancellationToken ct = default)
{
var cached = await _cache.GetStringAsync(key, ct);
if (cached != null)
{
var entry = JsonSerializer.Deserialize<CacheEntry<T>>(cached)!;
if (entry.IsStale && !entry.IsExpired)
{
// 立即返回陈旧数据,后台刷新
_ = Task.Run(async () =>
{
var fresh = await factory(CancellationToken.None);
await SetAsync(key, fresh, CancellationToken.None);
});
}
if (!entry.IsExpired)
return entry.Value;
}
// 缓存未命中或已过期
var value = await factory(ct);
await SetAsync(key, value, ct);
return value;
}
private record CacheEntry<TValue>(TValue Value, DateTime CreatedAt)
{
public bool IsStale => DateTime.UtcNow - CreatedAt > _freshDuration;
public bool IsExpired => DateTime.UtcNow - CreatedAt > _staleDuration;
}
}
测试模式
使用 xUnit 和 Moq 的单元测试
public class OrderServiceTests
{
private readonly Mock<IOrderRepository> _mockRepository;
private readonly Mock<IStockService> _mockStockService;
private readonly Mock<IValidator<CreateOrderRequest>> _mockValidator;
private readonly OrderService _sut; // 被测系统
public OrderServiceTests()
{
_mockRepository = new Mock<IOrderRepository>();
_mockStockService = new Mock<IStockService>();
_mockValidator = new Mock<IValidator<CreateOrderRequest>>();
// 默认:验证通过
_mockValidator
.Setup(v => v.ValidateAsync(It.IsAny<CreateOrderRequest>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new ValidationResult());
_sut = new OrderService(
_mockRepository.Object,
_mockStockService.Object,
_mockValidator.Object);
}
[Fact]
public async Task CreateOrderAsync_WithValidRequest_ReturnsSuccess()
{
// 准备
var request = new CreateOrderRequest
{
ProductId = "PROD-001",
Quantity = 5,
CustomerOrderCode = "ORD-2024-001"
};
_mockStockService
.Setup(s => s.CheckAsync("PROD-001", 5, It.IsAny<CancellationToken>()))
.ReturnsAsync(new StockResult { IsAvailable = true, Available = 10 });
_mockRepository
.Setup(r => r.CreateAsync(It.IsAny<Order>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new Order { Id = 1, CustomerOrderCode = "ORD-2024-001" });
// 执行
var result = await _sut.CreateOrderAsync(request);
// 断言
Assert.True(result.IsSuccess);
Assert.NotNull(result.Value);
Assert.Equal(1, result.Value.Id);
_mockRepository.Verify(
r => r.CreateAsync(It.Is<Order>(o => o.CustomerOrderCode == "ORD-2024-001"),
It.IsAny<CancellationToken>()),
Times.Once);
}
[Fact]
public async Task CreateOrderAsync_WithInsufficientStock_ReturnsFailure()
{
// 准备
var request = new CreateOrderRequest { ProductId = "PROD-001", Quantity = 100 };
_mockStockService
.Setup(s => s.CheckAsync(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new StockResult { IsAvailable = false, Available = 5 });
// 执行
var result = await _sut.CreateOrderAsync(request);
// 断言
Assert.False(result.IsSuccess);
Assert.Equal("INSUFFICIENT_STOCK", result.ErrorCode);
Assert.Contains("可用 5", result.Error);
_mockRepository.Verify(
r => r.CreateAsync(It.IsAny<Order>(), It.IsAny<CancellationToken>()),
Times.Never);
}
[Theory]
[InlineData(0)]
[InlineData(-1)]
[InlineData(-100)]
public async Task CreateOrderAsync_WithInvalidQuantity_ReturnsValidationError(int quantity)
{
// 准备
var request = new CreateOrderRequest { ProductId = "PROD-001", Quantity = quantity };
_mockValidator
.Setup(v => v.ValidateAsync(request, It.IsAny<CancellationToken>()))
.ReturnsAsync(new ValidationResult(new[]
{
new ValidationFailure("Quantity", "数量必须大于 0")
}));
// 执行
var result = await _sut.CreateOrderAsync(request);
// 断言
Assert.False(result.IsSuccess);
Assert.Equal("VALIDATION_ERROR", result.ErrorCode);
}
}
使用 WebApplicationFactory 的集成测试
public class ProductsApiTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly WebApplicationFactory<Program> _factory;
private readonly HttpClient _client;
public ProductsApiTests(WebApplicationFactory<Program> factory)
{
_factory = factory.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
// 用内存数据库替换真实数据库
services.RemoveAll<DbContextOptions<AppDbContext>>();
services.AddDbContext<AppDbContext>(options =>
options.UseInMemoryDatabase("TestDb"));
// 用内存缓存替换 Redis
services.RemoveAll<IDistributedCache>();
services.AddDistributedMemoryCache();
});
});
_client = _factory.CreateClient();
}
[Fact]
public async Task GetProduct_WithValidId_ReturnsProduct()
{
// 准备
using var scope = _factory.Services.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
context.Products.Add(new Product
{
Id = "TEST-001",
Name = "测试产品",
Price = 99.99m
});
await context.SaveChangesAsync();
// 执行
var response = await _client.GetAsync("/api/products/TEST-001");
// 断言
response.EnsureSuccessStatusCode();
var product = await response.Content.ReadFromJsonAsync<Product>();
Assert.Equal("测试产品", product!.Name);
}
[Fact]
public async Task GetProduct_WithInvalidId_Returns404()
{
// 执行
var response = await _client.GetAsync("/api/products/NONEXISTENT");
// 断言
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
}
最佳实践
应该做的
- 一路使用 async/await 贯穿整个调用堆栈
- 通过构造函数注入依赖
- 使用 IOptions 进行类型化配置
- 返回 Result 类型而不是为业务逻辑抛出异常
- 在所有异步方法中使用 CancellationToken
- 对于读取量大、性能关键的查询,优先使用 Dapper
- 对于带有变更跟踪的复杂领域模型,使用 EF Core
- 积极使用缓存并采用适当的失效策略
- 为业务逻辑编写单元测试,为 API 编写集成测试
- 对 DTO 和不可变数据使用 record 类型
应避免的
- 不要使用
.Result或.Wait()阻塞异步代码 - 不要使用 async void,事件处理程序除外
- 不要捕获通用的 Exception 而不重新抛出或记录
- 不要硬编码配置值
- 不要在 API 中直接暴露 EF 实体 (应使用 DTO)
- 不要忘记为只读查询使用
AsNoTracking() - 不要忽略 CancellationToken 参数
- 不要手动创建
new HttpClient()(应使用 IHttpClientFactory) - 不要不必要地混合同步和异步代码
- 不要跳过 API 边界的验证
常见陷阱
- N+1 查询问题:使用
.Include()或显式连接 - 内存泄漏:释放 IDisposable 资源,使用
using - 死锁:不要混合使用同步和异步代码,在库中使用 ConfigureAwait(false)
- 过度获取数据:只选择需要的列,使用投影
- 缺少索引:检查查询计划,为常用过滤器添加索引
- 超时问题:为 HTTP 客户端配置适当的超时时间
- 缓存雪崩:在填充缓存时使用分布式锁
📄 原始文档
完整文档(英文):
https://skills.sh/wshobson/agents/dotnet-backend-patterns
💡 提示:点击上方链接查看 skills.sh 原始英文文档,方便对照翻译。
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

评论(0)