This commit is contained in:
2025-11-03 17:03:57 +08:00
commit 7a04b85667
16804 changed files with 2492292 additions and 0 deletions

View File

@@ -0,0 +1,488 @@
# 实施计划
- [x] 1. 初始化项目结构
- 创建后端.NET 9.0解决方案包含API、Core、Infrastructure和Application四个项目层
- 创建前端Vite+Vue 3项目配置路由、状态管理和UI组件库
- 配置MySQL数据库连接和Entity Framework Core
- 设置开发环境配置文件appsettings.json和.env
- _需求: 1.1, 6.1_
- [x] 2. 实现数据库层和核心实体
- [x] 2.1 创建实体类和DbContext
- 实现Category、Product、PerformanceScore、Specification、DataSource和RankingHistory实体类
- 配置AppDbContext和实体关系映射
- 配置数据库索引Name, Model, CategoryId, PerformanceScore字段
- _需求: 7.1, 7.2_
- [x] 2.2 创建和执行数据库迁移
- 生成初始数据库迁移文件
- 执行迁移创建数据库表结构
- 添加种子数据初始产品类别手机CPU、手机GPU、电脑CPU、电脑GPU
- _需求: 1.1, 1.2, 7.1_
- [ ] 2.3 编写实体验证单元测试
- 测试实体属性验证规则
- 测试实体关系配置
- _需求: 7.1_
- [x] 3. 实现Repository层数据访问
- [ ] 3.1 创建Repository接口和实现
- 实现ICategoryRepository和CategoryRepository
- 实现IProductRepository和ProductRepository包含分页、搜索和筛选方法
- 实现通用Repository基类
- _需求: 2.2, 4.2, 7.2_
- [ ] 3.2 实现产品查询方法
- 实现按类别获取产品列表(支持分页和排序)
- 实现产品搜索方法(按名称、型号、品牌)
- 实现产品筛选方法(按性能分数区间、发布年份)
- _需求: 2.1, 4.2, 4.3, 4.4_
- [ ] 3.3 编写Repository层单元测试
- 使用InMemory数据库测试CRUD操作
- 测试查询和筛选逻辑
- _需求: 7.2_
- [x] 4. 实现Service层业务逻辑
- [x] 4.1 创建CategoryService
- 实现获取所有类别方法
- 实现获取单个类别详情方法
- 使用AutoMapper配置DTO映射
- _需求: 1.3, 1.4, 6.1_
- [x] 4.2 创建ProductService
- 实现获取产品排名列表方法(支持分页、排序)
- 实现获取产品详情方法(包含规格参数和性能分数)
- 实现产品搜索方法
- 实现产品筛选方法
- _需求: 2.1, 2.2, 2.3, 2.4, 4.2, 4.3, 4.4_
- [x] 4.3 创建ComparisonService
- 实现产品对比方法接受2-4个产品ID
- 实现对比数据处理逻辑(标识最优和最差指标)
- 生成对比矩阵数据结构
- _需求: 5.1, 5.2, 5.3, 5.4_
- [ ] 4.4 编写Service层单元测试
- 测试业务逻辑正确性
- 使用Mock测试Repository依赖
- _需求: 2.1, 4.2, 5.1_
- [x] 5. 实现API Controllers
- [x] 5.1 创建CategoriesController
- 实现GET /api/categories端点
- 实现GET /api/categories/{id}端点
- 配置响应格式和状态码
- _需求: 6.1, 6.6_
- [x] 5.2 创建ProductsController
- 实现GET /api/products端点支持查询参数categoryId, page, pageSize, sortBy, order
- 实现GET /api/products/{id}端点
- 实现GET /api/products/search端点支持查询参数q, categoryId, manufacturer, minScore, maxScore
- 配置FluentValidation验证查询参数
- _需求: 2.1, 2.2, 2.3, 4.2, 4.4, 6.2, 6.3, 6.4, 6.6_
- [x] 5.3 创建ComparisonController
- 实现POST /api/comparison端点
- 验证请求体productIds数组长度2-4
- 配置响应格式
- _需求: 5.1, 5.2, 6.5, 6.6_
- [x] 5.4 配置全局异常处理中间件
- 实现GlobalExceptionMiddleware
- 处理ValidationException、NotFoundException和通用Exception
- 返回标准化错误响应
- _需求: 6.7_
- [ ] 5.5 编写API集成测试
- 使用WebApplicationFactory测试API端点
- 测试各种查询参数组合
- 测试错误处理
- _需求: 6.1, 6.2, 6.3, 6.4, 6.5_
- [x] 6. 实现数据采集服务
- [x] 6.1 创建DataCollectionService基础结构
- 实现IDataCollectionService接口
- 配置HttpClientFactory
- 实现数据验证和转换逻辑
- _需求: 3.1, 3.2, 3.3, 3.4_
- [x] 6.2 实现GeekBench数据采集
- 实现CollectFromGeekBenchAsync方法
- 解析GeekBench API响应或爬取排行榜页面
- 提取CPU性能数据Single-Core和Multi-Core分数
- 更新或插入Product和PerformanceScore数据
- _需求: 3.1, 3.4_
- [x] 6.3 实现GPU数据采集
- 实现CollectGpuDataAsync方法
- 集成3DMark或GFXBench数据源
- 提取GPU性能数据
- _需求: 3.2, 3.4_
- [x] 6.4 实现AnTuTu数据采集
- 实现CollectFromAnTuTuAsync方法
- 爬取AnTuTu排行榜数据
- 提取移动设备综合性能数据
- _需求: 3.3, 3.4_
- [x] 6.5 实现排名计算服务
- 实现RecalculateRankingsAsync方法
- 按类别和性能分数计算排名
- 更新Product的CurrentRank字段
- 记录RankingHistory历史数据
- _需求: 2.5, 7.3_
- [x] 6.6 配置错误处理和重试机制
- 实现API调用失败重试逻辑最多3次
- 记录详细错误日志使用Serilog
- 实现告警通知机制
- _需求: 3.6_
- [x] 6.7 编写数据采集服务测试
- 使用Mock测试外部API调用
- 测试数据验证逻辑
- 测试排名计算准确性
- _需求: 3.4, 3.6_
- [ ] 7. 配置后台任务调度
- [x] 7.1 集成Hangfire
- 安装Hangfire NuGet包
- 配置Hangfire使用MySQL存储
- 配置Hangfire Dashboard
- _需求: 3.5_
- [x] 7.2 创建DataSyncJob
- 实现定时数据同步任务
- 配置每24小时执行一次
- 调用DataCollectionService的各个采集方法
- 记录任务执行结果
- _需求: 3.5_
- [x] 7.3 配置数据库备份任务
- 创建DatabaseBackupJob
- 配置每周执行一次
- 实现MySQL数据库备份逻辑
- _需求: 7.5_
- [x] 8. 实现前端基础架构
- [x] 8.1 配置Vue Router
- 创建路由配置文件
- 定义路由:首页、类别排名、产品详情、产品对比
- 配置路由懒加载
- _需求: 2.1, 8.1_
- [x] 8.2 配置Pinia状态管理
- 创建categoryStore管理产品类别状态
- 创建productStore管理产品列表和详情状态
- 创建comparisonStore管理对比产品状态
- _需求: 2.1, 4.1, 5.1_
- [x] 8.3 配置Axios和API服务层
- 创建Axios实例配置baseURL和拦截器
- 实现请求拦截器添加通用headers
- 实现响应拦截器(统一错误处理)
- 创建categoryService、productService和comparisonService
- _需求: 6.1, 6.2, 6.3, 6.4, 6.5_
- [ ] 8.4 集成Element Plus和TailwindCSS
- 安装和配置Element Plus组件库
- 配置TailwindCSS
- 创建全局样式文件
- _需求: 8.1, 8.2_
- [x] 9. 实现前端布局组件
- [x] 9.1 创建Header组件
- 实现顶部导航栏
- 添加Logo和应用名称
- 添加产品类别导航菜单
- 实现响应式布局
- _需求: 1.4, 8.1, 8.2_
- [x] 9.2 创建Footer组件
- 显示数据来源说明
- 显示最后更新时间
- 添加数据源链接
- _需求: 10.1, 10.2, 10.4_
- [x] 9.3 创建主布局组件
- 组合Header和Footer
- 配置router-view
- 实现响应式容器
- _需求: 8.1, 8.2_
- [x] 10. 实现首页和类别选择
- [x] 10.1 创建Home视图
- 显示产品类别卡片网格
- 调用categoryStore获取类别列表
- 实现类别卡片点击跳转
- 显示每个类别的产品数量
- _需求: 1.1, 1.4_
- [x] 10.2 实现响应式布局
- 桌面端4列网格
- 平板端2列网格
- 手机端1列网格
- _需求: 8.1, 8.2, 8.3_
- [x] 11. 实现产品排名列表页面
- [x] 11.1 创建CategoryRanking视图
- 根据路由参数获取categoryId
- 调用productStore获取产品列表
- 显示产品排名列表
- 实现分页组件
- _需求: 2.1, 2.5_
- [x] 11.2 创建ProductCard组件
- 显示产品缩略图、名称、型号
- 显示性能分数和排名
- 显示制造商和发布日期
- 实现点击跳转到详情页
- _需求: 2.2_
- [x] 11.3 创建ProductFilter组件
- 实现品牌筛选下拉框
- 实现性能分数区间滑块
- 实现发布年份筛选
- 实现筛选条件重置按钮
- _需求: 4.3, 4.5_
- [x] 11.4 创建ProductSearch组件
- 实现搜索输入框
- 实现搜索建议(防抖)
- 实现搜索结果高亮
- _需求: 4.1, 4.2_
- [x] 11.5 实现排序功能
- 添加排序下拉框(按性能分数、发布日期、价格)
- 实现升序/降序切换
- 调用API重新获取排序后的数据
- _需求: 2.4_
- [x] 11.6 实现产品对比选择
- 添加产品选择复选框
- 显示已选产品数量最多4个
- 添加"对比"按钮,跳转到对比页面
- _需求: 5.1_
- [x] 12. 实现产品详情页面
- [x] 12.1 创建ProductDetail视图
- 根据路由参数获取productId
- 调用productStore获取产品详情
- 显示产品基本信息(名称、型号、制造商、发布日期)
- 显示性能分数和当前排名
- _需求: 2.2, 2.3_
- [x] 12.2 显示规格参数
- 以表格形式展示Specifications
- 分组显示(如:处理器规格、图形规格)
- _需求: 2.2_
- [x] 12.3 显示性能测试分数
- 以卡片形式展示各项Benchmark分数
- 使用进度条可视化分数
- _需求: 2.2_
- [x] 12.4 显示数据来源信息
- 显示数据来源名称和链接
- 显示最后更新时间
- _需求: 10.2, 10.3, 10.4_
- [ ] 12.5 集成ECharts图表
- 安装vue-echarts
- 创建性能历史趋势折线图
- 创建性能指标雷达图
- 实现图表响应式
- _需求: 9.1, 9.3_
- [ ] 13. 实现产品对比页面
- [ ] 13.1 创建ProductComparison视图
- 从路由查询参数获取productIds
- 调用comparisonStore获取对比数据
- 显示对比产品的基本信息
- _需求: 5.1, 5.2_
- [ ] 13.2 创建ComparisonTable组件
- 以表格形式展示产品参数对比
- 高亮显示最优值(绿色)和最差值(红色)
- 支持横向滚动(移动端)
- _需求: 5.2, 5.4_
- [ ] 13.3 创建ComparisonChart组件
- 使用ECharts创建柱状图对比性能分数
- 使用雷达图对比多维度性能指标
- 实现图表交互(悬停显示详细数据)
- _需求: 5.3, 9.2_
- [ ] 14. 实现前端错误处理和加载状态
- [ ] 14.1 创建全局错误处理
- 在Axios拦截器中处理网络错误
- 显示Toast错误提示使用Element Plus
- 处理401/403/404/500等HTTP错误
- _需求: 8.4_
- [ ] 14.2 实现加载状态
- 在各个视图中添加Loading组件
- 使用Skeleton屏幕优化加载体验
- 实现数据加载失败重试按钮
- _需求: 8.4_
- [ ] 14.3 实现空状态处理
- 创建EmptyState组件
- 在无数据时显示友好提示
- 提供返回首页或重新加载按钮
- _需求: 8.4_
- [x] 15. 实现性能优化
- [x] 15.1 配置前端代码分割
- 配置Vite的代码分割策略
- 实现路由级别的懒加载
- 分离第三方库vendor chunk
- _需求: 8.4_
- [x] 15.2 实现图片优化
- 配置图片懒加载使用Intersection Observer
- 使用WebP格式图片带降级方案
- 实现图片占位符
- _需求: 8.4_
- [x] 15.3 配置后端缓存
- 安装StackExchange.Redis NuGet包
- 配置Redis连接
- 实现排名列表缓存5分钟过期
- 实现产品详情缓存15分钟过期
- _需求: 2.1, 2.3_
- [ ] 15.4 实现API响应压缩
- 配置Response Compression中间件
- 启用Gzip压缩
- _需求: 6.6_
- [ ] 15.5 配置数据库查询优化
- 使用AsNoTracking优化只读查询
- 实现Include预加载关联数据
- 避免N+1查询问题
- _需求: 7.2_
- [x] 16. 配置CORS和安全设置
- [x] 16.1 配置CORS策略
- 在Program.cs中配置CORS
- 允许前端域名访问
- 配置允许的HTTP方法和Headers
- _需求: 6.6_
- [ ] 16.2 配置API限流
- 安装AspNetCoreRateLimit NuGet包
- 配置IP限流规则每分钟最多100请求
- 配置端点级别限流
- _需求: 6.6_
- [ ] 16.3 配置输入验证
- 使用FluentValidation验证所有API输入
- 防止SQL注入使用参数化查询
- 清理和转义用户输入
- _需求: 6.7_
- [ ] 17. 配置日志和监控
- [ ] 17.1 配置Serilog
- 安装Serilog NuGet包
- 配置日志输出到文件和控制台
- 配置结构化日志格式
- 配置日志级别Development: Debug, Production: Information
- _需求: 3.6_
- [ ] 17.2 实现关键操作日志
- 记录数据采集任务执行日志
- 记录API错误日志
- 记录数据库操作异常日志
- _需求: 3.6_
- [ ] 17.3 配置Hangfire Dashboard监控
- 启用Hangfire Dashboard
- 配置Dashboard访问权限
- 监控后台任务执行状态
- _需求: 3.5_
- [ ] 18. 编写部署文档和配置
- [ ] 18.1 创建Docker配置
- 编写后端Dockerfile
- 编写前端Dockerfile
- 创建docker-compose.yml包含API、前端、MySQL、Redis
- _需求: 1.1_
- [ ] 18.2 创建环境配置文件
- 创建appsettings.Production.json
- 创建前端.env.production
- 配置生产环境数据库连接字符串
- 配置API密钥和外部服务配置
- _需求: 3.1, 3.2, 3.3_
- [ ] 18.3 编写部署文档
- 编写README.md项目介绍、技术栈、功能特性
- 编写DEPLOYMENT.md部署步骤、环境要求、配置说明
- 编写API文档使用Swagger/OpenAPI
- _需求: 1.1, 6.1, 6.2, 6.3, 6.4, 6.5_

View File

@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MinimalAPI\MinimalAPI.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,97 @@
using AutoMapper;
using HardwarePerformance.Models;
using HardwarePerformance.Models.DTOs;
using HardwarePerformance.Services;
using Moq;
namespace HardwarePerformance.Tests.Services
{
public class CategoryServiceTests
{
private readonly Mock<ICategoryRepository> _mockCategoryRepository;
private readonly Mock<IMapper> _mockMapper;
private readonly CategoryService _categoryService;
public CategoryServiceTests()
{
_mockCategoryRepository = new Mock<ICategoryRepository>();
_mockMapper = new Mock<IMapper>();
_categoryService = new CategoryService(_mockCategoryRepository.Object, _mockMapper.Object);
}
[Fact]
public async Task GetAllCategoriesAsync_ReturnsAllCategories()
{
// Arrange
var categories = new List<Category>
{
new Category { Id = 1, Name = "手机CPU", Description = "手机处理器" },
new Category { Id = 2, Name = "电脑CPU", Description = "电脑处理器" }
};
var categoryDtos = new List<CategoryDto>
{
new CategoryDto { Id = 1, Name = "手机CPU", Description = "手机处理器" },
new CategoryDto { Id = 2, Name = "电脑CPU", Description = "电脑处理器" }
};
_mockCategoryRepository.Setup(repo => repo.GetAllAsync())
.ReturnsAsync(categories);
_mockMapper.Setup(m => m.Map<IEnumerable<CategoryDto>>(categories))
.Returns(categoryDtos);
// Act
var result = await _categoryService.GetAllCategoriesAsync();
// Assert
Assert.NotNull(result);
Assert.Equal(2, result.Count());
_mockCategoryRepository.Verify(repo => repo.GetAllAsync(), Times.Once);
_mockMapper.Verify(m => m.Map<IEnumerable<CategoryDto>>(categories), Times.Once);
}
[Fact]
public async Task GetCategoryByIdAsync_ExistingCategory_ReturnsCategoryDto()
{
// Arrange
var categoryId = 1;
var category = new Category { Id = categoryId, Name = "手机CPU", Description = "手机处理器" };
var categoryDto = new CategoryDto { Id = categoryId, Name = "手机CPU", Description = "手机处理器" };
_mockCategoryRepository.Setup(repo => repo.GetByIdAsync(categoryId))
.ReturnsAsync(category);
_mockMapper.Setup(m => m.Map<CategoryDto>(category))
.Returns(categoryDto);
// Act
var result = await _categoryService.GetCategoryByIdAsync(categoryId);
// Assert
Assert.NotNull(result);
Assert.Equal(categoryId, result.Id);
Assert.Equal("手机CPU", result.Name);
_mockCategoryRepository.Verify(repo => repo.GetByIdAsync(categoryId), Times.Once);
_mockMapper.Verify(m => m.Map<CategoryDto>(category), Times.Once);
}
[Fact]
public async Task GetCategoryByIdAsync_NonExistingCategory_ReturnsNull()
{
// Arrange
var categoryId = 999;
_mockCategoryRepository.Setup(repo => repo.GetByIdAsync(categoryId))
.ReturnsAsync((Category?)null);
// Act
var result = await _categoryService.GetCategoryByIdAsync(categoryId);
// Assert
Assert.Null(result);
_mockCategoryRepository.Verify(repo => repo.GetByIdAsync(categoryId), Times.Once);
_mockMapper.Verify(m => m.Map<CategoryDto>(It.IsAny<Category>()), Times.Never);
}
}
}

View File

@@ -0,0 +1,173 @@
using AutoMapper;
using HardwarePerformance.Models;
using HardwarePerformance.Models.DTOs;
using HardwarePerformance.Services;
using Moq;
namespace HardwarePerformance.Tests.Services
{
public class ComparisonServiceTests
{
private readonly Mock<IProductRepository> _mockProductRepository;
private readonly Mock<IMapper> _mockMapper;
private readonly ComparisonService _comparisonService;
public ComparisonServiceTests()
{
_mockProductRepository = new Mock<IProductRepository>();
_mockMapper = new Mock<IMapper>();
_comparisonService = new ComparisonService(_mockProductRepository.Object, _mockMapper.Object);
}
[Fact]
public async Task CompareProductsAsync_ValidProductIds_ReturnsComparisonData()
{
// Arrange
var productIds = new List<int> { 1, 2 };
var products = new List<Product>
{
new Product
{
Id = 1,
Name = "Intel Core i9",
Manufacturer = "Intel",
CategoryId = 1,
CurrentRank = 1,
ReleaseDate = DateTime.Now.AddMonths(-6),
Price = 500,
PerformanceScore = 4000,
PerformanceScores = new List<PerformanceScore>
{
new PerformanceScore { Id = 1, ProductId = 1, TestType = "Single-Core", Score = 1500 },
new PerformanceScore { Id = 2, ProductId = 1, TestType = "Multi-Core", Score = 8000 }
},
Specifications = new List<Specification>
{
new Specification { Id = 1, ProductId = 1, Name = "Cores", Value = "8" },
new Specification { Id = 2, ProductId = 1, Name = "Threads", Value = "16" }
}
},
new Product
{
Id = 2,
Name = "AMD Ryzen 9",
Manufacturer = "AMD",
CategoryId = 1,
CurrentRank = 2,
ReleaseDate = DateTime.Now.AddMonths(-4),
Price = 450,
PerformanceScore = 3800,
PerformanceScores = new List<PerformanceScore>
{
new PerformanceScore { Id = 3, ProductId = 2, TestType = "Single-Core", Score = 1400 },
new PerformanceScore { Id = 4, ProductId = 2, TestType = "Multi-Core", Score = 7500 }
},
Specifications = new List<Specification>
{
new Specification { Id = 3, ProductId = 2, Name = "Cores", Value = "8" },
new Specification { Id = 4, ProductId = 2, Name = "Threads", Value = "16" }
}
}
};
var productDtos = new List<ProductDto>
{
new ProductDto
{
Id = 1,
Name = "Intel Core i9",
Manufacturer = "Intel",
CategoryId = 1,
CurrentRank = 1,
ReleaseDate = DateTime.Now.AddMonths(-6),
Price = 500,
PerformanceScore = 4000,
PerformanceScores = new List<PerformanceScore>
{
new PerformanceScore { Id = 1, ProductId = 1, TestType = "Single-Core", Score = 1500 },
new PerformanceScore { Id = 2, ProductId = 1, TestType = "Multi-Core", Score = 8000 }
},
Specifications = new List<Specification>
{
new Specification { Id = 1, ProductId = 1, Name = "Cores", Value = "8" },
new Specification { Id = 2, ProductId = 1, Name = "Threads", Value = "16" }
}
},
new ProductDto
{
Id = 2,
Name = "AMD Ryzen 9",
Manufacturer = "AMD",
CategoryId = 1,
CurrentRank = 2,
ReleaseDate = DateTime.Now.AddMonths(-4),
Price = 450,
PerformanceScore = 3800,
PerformanceScores = new List<PerformanceScore>
{
new PerformanceScore { Id = 3, ProductId = 2, TestType = "Single-Core", Score = 1400 },
new PerformanceScore { Id = 4, ProductId = 2, TestType = "Multi-Core", Score = 7500 }
},
Specifications = new List<Specification>
{
new Specification { Id = 3, ProductId = 2, Name = "Cores", Value = "8" },
new Specification { Id = 4, ProductId = 2, Name = "Threads", Value = "16" }
}
}
};
_mockProductRepository.Setup(repo => repo.GetByIdsAsync(productIds))
.ReturnsAsync(products);
_mockMapper.Setup(m => m.Map<List<ProductDto>>(products))
.Returns(productDtos);
// Act
var result = await _comparisonService.CompareProductsAsync(productIds);
// Assert
Assert.NotNull(result);
Assert.Equal(2, result.Products.Count);
Assert.NotNull(result.ComparisonMatrix);
_mockProductRepository.Verify(repo => repo.GetByIdsAsync(productIds), Times.Once);
_mockMapper.Verify(m => m.Map<List<ProductDto>>(products), Times.Once);
}
[Fact]
public async Task CompareProductsAsync_EmptyProductIds_ThrowsArgumentException()
{
// Arrange
var productIds = new List<int>();
// Act & Assert
await Assert.ThrowsAsync<ArgumentException>(async () =>
await _comparisonService.CompareProductsAsync(productIds));
}
[Fact]
public async Task CompareProductsAsync_TooManyProductIds_ThrowsArgumentException()
{
// Arrange
var productIds = new List<int> { 1, 2, 3, 4, 5 };
// Act & Assert
await Assert.ThrowsAsync<ArgumentException>(async () =>
await _comparisonService.CompareProductsAsync(productIds));
}
[Fact]
public async Task CompareProductsAsync_NonExistingProductIds_ThrowsKeyNotFoundException()
{
// Arrange
var productIds = new List<int> { 1, 2 };
_mockProductRepository.Setup(repo => repo.GetByIdsAsync(productIds))
.ReturnsAsync(new List<Product>());
// Act & Assert
await Assert.ThrowsAsync<KeyNotFoundException>(async () =>
await _comparisonService.CompareProductsAsync(productIds));
}
}
}

View File

@@ -0,0 +1,184 @@
using AutoMapper;
using HardwarePerformance.Models;
using HardwarePerformance.Models.DTOs;
using HardwarePerformance.Services;
using Moq;
namespace HardwarePerformance.Tests.Services
{
public class ProductServiceTests
{
private readonly Mock<IProductRepository> _mockProductRepository;
private readonly Mock<IMapper> _mockMapper;
private readonly ProductService _productService;
public ProductServiceTests()
{
_mockProductRepository = new Mock<IProductRepository>();
_mockMapper = new Mock<IMapper>();
_productService = new ProductService(_mockProductRepository.Object, _mockMapper.Object);
}
[Fact]
public async Task GetProductsByCategoryAsync_ReturnsPagedProducts()
{
// Arrange
var categoryId = 1;
var page = 1;
var pageSize = 10;
var sortBy = "Name";
var sortOrder = "asc";
var products = new List<Product>
{
new Product { Id = 1, Name = "Intel Core i9", CategoryId = categoryId },
new Product { Id = 2, Name = "AMD Ryzen 9", CategoryId = categoryId }
};
var productDtos = new List<ProductListDto>
{
new ProductListDto { Id = 1, Name = "Intel Core i9" },
new ProductListDto { Id = 2, Name = "AMD Ryzen 9" }
};
var pagedResult = new PagedResultDto<ProductListDto>
{
Items = productDtos,
TotalCount = 2,
Page = page,
PageSize = pageSize
};
_mockProductRepository.Setup(repo => repo.GetByCategoryAsync(categoryId, page, pageSize, sortBy, sortOrder))
.ReturnsAsync(products);
_mockProductRepository.Setup(repo => repo.CountAsync(categoryId))
.ReturnsAsync(2);
_mockMapper.Setup(m => m.Map<IEnumerable<ProductListDto>>(products))
.Returns(productDtos);
// Act
var result = await _productService.GetProductsByCategoryAsync(categoryId, page, pageSize, sortBy, sortOrder);
// Assert
Assert.NotNull(result);
Assert.Equal(2, result.TotalCount);
Assert.Equal(2, result.Items.Count());
_mockProductRepository.Verify(repo => repo.GetByCategoryAsync(categoryId, page, pageSize, sortBy, sortOrder), Times.Once);
_mockProductRepository.Verify(repo => repo.CountAsync(categoryId), Times.Once);
_mockMapper.Verify(m => m.Map<IEnumerable<ProductListDto>>(products), Times.Once);
}
[Fact]
public async Task GetProductByIdAsync_ExistingProduct_ReturnsProductDto()
{
// Arrange
var productId = 1;
var product = new Product
{
Id = productId,
Name = "Intel Core i9",
CategoryId = 1,
PerformanceScores = new List<PerformanceScore>(),
Specifications = new List<Specification>()
};
var productDto = new ProductDto
{
Id = productId,
Name = "Intel Core i9",
CategoryId = 1,
PerformanceScores = new List<PerformanceScore>(),
Specifications = new List<Specification>()
};
_mockProductRepository.Setup(repo => repo.GetByIdAsync(productId))
.ReturnsAsync(product);
_mockMapper.Setup(m => m.Map<ProductDto>(product))
.Returns(productDto);
// Act
var result = await _productService.GetProductByIdAsync(productId);
// Assert
Assert.NotNull(result);
Assert.Equal(productId, result.Id);
Assert.Equal("Intel Core i9", result.Name);
_mockProductRepository.Verify(repo => repo.GetByIdAsync(productId), Times.Once);
_mockMapper.Verify(m => m.Map<ProductDto>(product), Times.Once);
}
[Fact]
public async Task GetProductByIdAsync_NonExistingProduct_ReturnsNull()
{
// Arrange
var productId = 999;
_mockProductRepository.Setup(repo => repo.GetByIdAsync(productId))
.ReturnsAsync((Product?)null);
// Act
var result = await _productService.GetProductByIdAsync(productId);
// Assert
Assert.Null(result);
_mockProductRepository.Verify(repo => repo.GetByIdAsync(productId), Times.Once);
_mockMapper.Verify(m => m.Map<ProductDto>(It.IsAny<Product>()), Times.Never);
}
[Fact]
public async Task SearchProductsAsync_ReturnsPagedSearchResults()
{
// Arrange
var query = "Intel";
var categoryId = 1;
var manufacturer = "Intel";
var minScore = 1000;
var maxScore = 5000;
var page = 1;
var pageSize = 10;
var products = new List<Product>
{
new Product { Id = 1, Name = "Intel Core i9", Manufacturer = "Intel", CategoryId = categoryId, PerformanceScore = 4000 },
new Product { Id = 2, Name = "Intel Core i7", Manufacturer = "Intel", CategoryId = categoryId, PerformanceScore = 3000 }
};
var productDtos = new List<ProductListDto>
{
new ProductListDto { Id = 1, Name = "Intel Core i9" },
new ProductListDto { Id = 2, Name = "Intel Core i7" }
};
var pagedResult = new PagedResultDto<ProductListDto>
{
Items = productDtos,
TotalCount = 2,
Page = page,
PageSize = pageSize
};
_mockProductRepository.Setup(repo => repo.SearchAsync(query, categoryId, manufacturer, minScore, maxScore, page, pageSize))
.ReturnsAsync(products);
_mockProductRepository.Setup(repo => repo.CountSearchResultsAsync(query, categoryId, manufacturer, minScore, maxScore))
.ReturnsAsync(2);
_mockMapper.Setup(m => m.Map<IEnumerable<ProductListDto>>(products))
.Returns(productDtos);
// Act
var result = await _productService.SearchProductsAsync(query, categoryId, manufacturer, minScore, maxScore, page, pageSize);
// Assert
Assert.NotNull(result);
Assert.Equal(2, result.TotalCount);
Assert.Equal(2, result.Items.Count());
_mockProductRepository.Verify(repo => repo.SearchAsync(query, categoryId, manufacturer, minScore, maxScore, page, pageSize), Times.Once);
_mockProductRepository.Verify(repo => repo.CountSearchResultsAsync(query, categoryId, manufacturer, minScore, maxScore), Times.Once);
_mockMapper.Verify(m => m.Map<IEnumerable<ProductListDto>>(products), Times.Once);
}
}
}

View File

@@ -0,0 +1,10 @@
namespace HardwarePerformance.Tests;
public class UnitTest1
{
[Fact]
public void Test1()
{
}
}

View File

@@ -0,0 +1,95 @@
{
"format": 1,
"restore": {
"C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj": {}
},
"projects": {
"C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj",
"projectName": "HardwarePerformance.Tests",
"projectPath": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj",
"packagesPath": "C:\\Users\\代\\.nuget\\packages\\",
"outputPath": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
],
"configFilePaths": [
"C:\\Users\\代\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
],
"originalTargetFrameworks": [
"net9.0"
],
"sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"https://api.nuget.org/v3/index.json": {},
"https://nuget.cdn.azure.cn/v3/index.json": {},
"https://packages.chinacloudapi.cn/v3/index.json": {},
"https://packages.microsoft.com/dotnet": {},
"https://www.nuget.org/api/v2/": {}
},
"frameworks": {
"net9.0": {
"targetAlias": "net9.0",
"projectReferences": {}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
},
"SdkAnalysisLevel": "9.0.300"
},
"frameworks": {
"net9.0": {
"targetAlias": "net9.0",
"dependencies": {
"Microsoft.NET.Test.Sdk": {
"target": "Package",
"version": "[17.12.0, )"
},
"coverlet.collector": {
"target": "Package",
"version": "[6.0.2, )"
},
"xunit": {
"target": "Package",
"version": "[2.9.2, )"
},
"xunit.runner.visualstudio": {
"target": "Package",
"version": "[2.8.2, )"
}
},
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"frameworkReferences": {
"Microsoft.NETCore.App": {
"privateAssets": "all"
}
},
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
}
}
}
}
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\代\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.0</NuGetToolVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<SourceRoot Include="C:\Users\代\.nuget\packages\" />
<SourceRoot Include="C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages\" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />

View File

@@ -0,0 +1,126 @@
{
"version": 3,
"targets": {
"net9.0": {}
},
"libraries": {},
"projectFileDependencyGroups": {
"net9.0": [
"Microsoft.NET.Test.Sdk >= 17.12.0",
"coverlet.collector >= 6.0.2",
"xunit >= 2.9.2",
"xunit.runner.visualstudio >= 2.8.2"
]
},
"packageFolders": {
"C:\\Users\\代\\.nuget\\packages\\": {},
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {}
},
"project": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj",
"projectName": "HardwarePerformance.Tests",
"projectPath": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj",
"packagesPath": "C:\\Users\\代\\.nuget\\packages\\",
"outputPath": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
],
"configFilePaths": [
"C:\\Users\\代\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
],
"originalTargetFrameworks": [
"net9.0"
],
"sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"https://api.nuget.org/v3/index.json": {},
"https://nuget.cdn.azure.cn/v3/index.json": {},
"https://packages.chinacloudapi.cn/v3/index.json": {},
"https://packages.microsoft.com/dotnet": {},
"https://www.nuget.org/api/v2/": {}
},
"frameworks": {
"net9.0": {
"targetAlias": "net9.0",
"projectReferences": {}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
},
"SdkAnalysisLevel": "9.0.300"
},
"frameworks": {
"net9.0": {
"targetAlias": "net9.0",
"dependencies": {
"Microsoft.NET.Test.Sdk": {
"target": "Package",
"version": "[17.12.0, )"
},
"coverlet.collector": {
"target": "Package",
"version": "[6.0.2, )"
},
"xunit": {
"target": "Package",
"version": "[2.9.2, )"
},
"xunit.runner.visualstudio": {
"target": "Package",
"version": "[2.8.2, )"
}
},
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"frameworkReferences": {
"Microsoft.NETCore.App": {
"privateAssets": "all"
}
},
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
}
}
},
"logs": [
{
"code": "NU1301",
"level": "Error",
"message": "未能从远程源“https://www.nuget.org/api/v2/FindPackagesById()?id='coverlet.collector'&semVerLevel=2.0.0”检索有关“coverlet.collector”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
"libraryId": "coverlet.collector"
},
{
"code": "NU1301",
"level": "Error",
"message": "未能从远程源“https://www.nuget.org/api/v2/FindPackagesById()?id='xunit'&semVerLevel=2.0.0”检索有关“xunit”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
"libraryId": "xunit"
},
{
"code": "NU1301",
"level": "Error",
"message": "未能从远程源“https://www.nuget.org/api/v2/FindPackagesById()?id='Microsoft.NET.Test.Sdk'&semVerLevel=2.0.0”检索有关“Microsoft.NET.Test.Sdk”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
"libraryId": "Microsoft.NET.Test.Sdk"
}
]
}

View File

@@ -0,0 +1,36 @@
{
"version": 2,
"dgSpecHash": "HWf5d7t+LFg=",
"success": false,
"projectFilePath": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj",
"expectedPackageFiles": [],
"logs": [
{
"code": "NU1301",
"level": "Error",
"message": "未能从远程源“https://www.nuget.org/api/v2/FindPackagesById()?id='coverlet.collector'&semVerLevel=2.0.0”检索有关“coverlet.collector”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
"projectPath": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj",
"filePath": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj",
"libraryId": "coverlet.collector",
"targetGraphs": []
},
{
"code": "NU1301",
"level": "Error",
"message": "未能从远程源“https://www.nuget.org/api/v2/FindPackagesById()?id='xunit'&semVerLevel=2.0.0”检索有关“xunit”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
"projectPath": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj",
"filePath": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj",
"libraryId": "xunit",
"targetGraphs": []
},
{
"code": "NU1301",
"level": "Error",
"message": "未能从远程源“https://www.nuget.org/api/v2/FindPackagesById()?id='Microsoft.NET.Test.Sdk'&semVerLevel=2.0.0”检索有关“Microsoft.NET.Test.Sdk”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
"projectPath": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj",
"filePath": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj",
"libraryId": "Microsoft.NET.Test.Sdk",
"targetGraphs": []
}
]
}

View File

@@ -0,0 +1,20 @@
using AutoMapper;
using HardwarePerformance.Models;
using HardwarePerformance.Models.DTOs;
namespace HardwarePerformance
{
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Category, CategoryDto>();
CreateMap<Product, ProductDto>()
.ForMember(dest => dest.PerformanceScores, opt => opt.Ignore())
.ForMember(dest => dest.Specifications, opt => opt.Ignore());
CreateMap<Product, ProductListDto>();
}
}
}

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,9 @@
namespace HardwarePerformance.Models.DTOs
{
public class CategoryDto
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
}
}

View File

@@ -0,0 +1,11 @@
namespace HardwarePerformance.Models.DTOs
{
public class PagedResultDto<T>
{
public List<T> Items { get; set; } = new();
public int Total { get; set; }
public int CurrentPage { get; set; }
public int PageSize { get; set; }
public int TotalPages { get; set; }
}
}

View File

@@ -0,0 +1,20 @@
using System.Collections.Generic;
namespace HardwarePerformance.Models.DTOs
{
public class ProductDto
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Model { get; set; } = string.Empty;
public string Manufacturer { get; set; } = string.Empty;
public int CategoryId { get; set; }
public int CurrentRank { get; set; }
public DateTime ReleaseDate { get; set; }
public decimal? Price { get; set; }
public int? PerformanceScore { get; set; }
public CategoryDto? Category { get; set; }
public List<PerformanceScore> PerformanceScores { get; set; } = new();
public List<Specification> Specifications { get; set; } = new();
}
}

View File

@@ -0,0 +1,15 @@
namespace HardwarePerformance.Models.DTOs
{
public class ProductListDto
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Model { get; set; } = string.Empty;
public string Manufacturer { get; set; } = string.Empty;
public int CategoryId { get; set; }
public int CurrentRank { get; set; }
public DateTime ReleaseDate { get; set; }
public decimal? Price { get; set; }
public int? PerformanceScore { get; set; }
}
}

184
MinimalAPI/Program.cs Normal file
View File

@@ -0,0 +1,184 @@
using System.IO.Compression;
using System.Text.Json.Serialization;
namespace MinimalAPI
{
// 定义数据模型
record Category(int Id, string Name, string Description);
record Product(int Id, string Name, string Model, string Manufacturer, int CategoryId, int CurrentRank, DateTime ReleaseDate, decimal? Price);
record ApiResponse<T>(bool Success, T? Data, string? Message = null);
record PagedResponse<T>(List<T> Items, int TotalCount, int PageNumber, int PageSize, int TotalPages);
record ComparisonRequest(List<int> ProductIds);
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// 配置响应压缩
builder.Services.AddResponseCompression(options =>
{
options.EnableForHttps = true;
options.Providers.Add<BrotliCompressionProvider>();
options.Providers.Add<GzipCompressionProvider>();
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[]
{
"application/json",
"application/javascript",
"text/css",
"text/html",
"text/plain",
"text/xml"
});
});
builder.Services.Configure<BrotliCompressionProviderOptions>(options =>
{
options.Level = CompressionLevel.Fastest;
});
builder.Services.Configure<GzipCompressionProviderOptions>(options =>
{
options.Level = CompressionLevel.Fastest;
});
// 配置JSON序列化选项
builder.Services.Configure<Microsoft.AspNetCore.Http.Json.JsonOptions>(options =>
{
options.SerializerOptions.PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase;
options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
// 配置CORS
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll", policy =>
{
policy.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
// 注册服务
builder.Services.AddScoped<ICategoryService, CategoryService>();
builder.Services.AddScoped<IProductService, ProductService>();
builder.Services.AddScoped<IComparisonService, ComparisonService>();
var app = builder.Build();
// 添加响应压缩中间件
app.UseResponseCompression();
// 使用CORS
app.UseCors("AllowAll");
// 模拟数据
var categories = new List<Category>
{
new(1, "手机CPU", "移动设备处理器"),
new(2, "手机GPU", "移动设备图形处理器"),
new(3, "电脑CPU", "桌面和笔记本处理器"),
new(4, "电脑GPU", "桌面和笔记本图形处理器")
};
var products = new List<Product>
{
new(1, "Apple A17 Pro", "A17 Pro", "Apple", 1, 1, new DateTime(2023, 9, 12), null),
new(2, "Snapdragon 8 Gen 3", "SM8650-AB", "Qualcomm", 1, 2, new DateTime(2023, 10, 24), null),
new(3, "Intel Core i9-13900K", "Core i9-13900K", "Intel", 3, 1, new DateTime(2022, 10, 20), 589.99m),
new(4, "AMD Ryzen 9 7950X", "Ryzen 9 7950X", "AMD", 3, 2, new DateTime(2022, 9, 27), 699.99m),
new(5, "NVIDIA GeForce RTX 4090", "RTX 4090", "NVIDIA", 4, 1, new DateTime(2022, 10, 12), 1599.99m),
new(6, "AMD Radeon RX 7900 XTX", "RX 7900 XTX", "AMD", 4, 2, new DateTime(2022, 12, 3), 999.99m)
};
// API端点
// 类别相关
app.MapGet("/api/categories", async (ICategoryService categoryService) =>
{
var categories = await categoryService.GetAllCategoriesAsync();
return Results.Ok(new ApiResponse<List<CategoryDto>>(true, categories));
});
app.MapGet("/api/categories/{id}", async (int id, ICategoryService categoryService) =>
{
var category = await categoryService.GetCategoryByIdAsync(id);
if (category == null)
{
return Results.NotFound(new ApiResponse<object>(false, null, "类别不存在"));
}
return Results.Ok(new ApiResponse<CategoryDto>(true, category));
});
// 产品API
app.MapGet("/api/products", async (int? categoryId, int page = 1, int pageSize = 20, string sortBy = "CurrentRank", string sortOrder = "asc", IProductService productService) =>
{
if (!categoryId.HasValue)
{
return Results.BadRequest(new ApiResponse<object>(false, null, "类别ID是必需的"));
}
var pagedResult = await productService.GetProductsByCategoryAsync(categoryId.Value, page, pageSize, sortBy, sortOrder);
return Results.Ok(new ApiResponse<PagedResultDto<ProductListDto>>(true, pagedResult));
});
app.MapGet("/api/products/{id}", async (int id, IProductService productService) =>
{
var product = await productService.GetProductByIdAsync(id);
if (product == null)
{
return Results.NotFound(new ApiResponse<object>(false, null, "产品不存在"));
}
return Results.Ok(new ApiResponse<ProductDto>(true, product));
});
app.MapGet("/api/products/search", async (string q, int? categoryId, string? manufacturer, decimal? minScore, decimal? maxScore, int page = 1, int pageSize = 20, IProductService productService) =>
{
if (string.IsNullOrWhiteSpace(q))
{
return Results.BadRequest(new ApiResponse<object>(false, null, "搜索关键词不能为空"));
}
var pagedResult = await productService.SearchProductsAsync(q, categoryId, manufacturer, minScore, maxScore, page, pageSize);
return Results.Ok(new ApiResponse<PagedResultDto<ProductListDto>>(true, pagedResult));
});
app.MapGet("/api/products/filter", async (int categoryId, string? manufacturer, decimal? minScore, decimal? maxScore, int? year, int page = 1, int pageSize = 20, IProductService productService) =>
{
var pagedResult = await productService.FilterProductsAsync(categoryId, manufacturer, minScore, maxScore, year, page, pageSize);
return Results.Ok(new ApiResponse<PagedResultDto<ProductListDto>>(true, pagedResult));
});
// 产品对比
app.MapPost("/api/comparison", async (ComparisonRequest request, IComparisonService comparisonService) =>
{
// 验证请求
if (request.ProductIds == null || request.ProductIds.Count < 2 || request.ProductIds.Count > 4)
{
return Results.BadRequest(new ApiResponse<object>(false, null, "产品ID数量必须在2到4之间"));
}
var comparisonData = await comparisonService.CompareProductsAsync(request.ProductIds);
if (comparisonData == null)
{
return Results.NotFound(new ApiResponse<object>(false, null, "无法获取产品对比数据"));
}
return Results.Ok(new ApiResponse<object>(true, comparisonData));
});
// 状态检查端点
app.MapGet("/api/status", () => new
{
Status = "Running",
Message = "API服务正常运行",
Timestamp = DateTime.Now,
Version = "1.0.0"
});
app.Run();
}
}
}

View File

@@ -0,0 +1,23 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5172",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7055;http://localhost:5172",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore;
using HardwarePerformance.Data;
using HardwarePerformance.Models;
namespace HardwarePerformance.Repositories
{
public class CategoryRepository : Repository<Category>, ICategoryRepository
{
public CategoryRepository(AppDbContext context) : base(context)
{
}
public async Task<Category?> GetByNameAsync(string name)
{
return await _dbSet.FirstOrDefaultAsync(c => c.Name == name);
}
public async Task<bool> ExistsAsync(int id)
{
return await _dbSet.AnyAsync(c => c.Id == id);
}
public async Task<bool> NameExistsAsync(string name)
{
return await _dbSet.AnyAsync(c => c.Name == name);
}
}
}

View File

@@ -0,0 +1,11 @@
using HardwarePerformance.Models;
namespace HardwarePerformance.Repositories
{
public interface ICategoryRepository : IRepository<Category>
{
Task<Category?> GetByNameAsync(string name);
Task<bool> ExistsAsync(int id);
Task<bool> NameExistsAsync(string name);
}
}

View File

@@ -0,0 +1,18 @@
using HardwarePerformance.Models;
namespace HardwarePerformance.Repositories
{
public interface IProductRepository : IRepository<Product>
{
Task<IEnumerable<Product>> GetByCategoryAsync(int categoryId);
Task<IEnumerable<Product>> GetByCategoryAsync(int categoryId, int page, int pageSize, string sortBy = "CurrentRank", string sortOrder = "asc");
Task<IEnumerable<Product>> SearchAsync(string query);
Task<IEnumerable<Product>> SearchAsync(string query, int? categoryId = null, string? manufacturer = null, int? minScore = null, int? maxScore = null, int page = 1, int pageSize = 20);
Task<IEnumerable<Product>> FilterAsync(int? categoryId, string? manufacturer, int? minScore, int? maxScore, int? year);
Task<IEnumerable<Product>> FilterAsync(int? categoryId, string? manufacturer, int? minScore, int? maxScore, int? year, int page, int pageSize, string sortBy = "CurrentRank", string sortOrder = "asc");
Task<int> CountAsync(int? categoryId = null);
Task<int> CountSearchResultsAsync(string query, int? categoryId = null, string? manufacturer = null, int? minScore = null, int? maxScore = null);
Task<int> CountFilterResultsAsync(int? categoryId, string? manufacturer, int? minScore, int? maxScore, int? year);
Task<Category?> GetCategoryByProductIdAsync(int productId);
}
}

View File

@@ -0,0 +1,16 @@
using System.Linq.Expressions;
using HardwarePerformance.Models;
namespace HardwarePerformance.Repositories
{
public interface IRepository<T> where T : class
{
Task<T?> GetByIdAsync(int id);
Task<IEnumerable<T>> GetAllAsync();
Task<IEnumerable<T>> FindAsync(Expression<Func<T, bool>> predicate);
Task<T> AddAsync(T entity);
Task UpdateAsync(T entity);
Task DeleteAsync(T entity);
Task<int> CountAsync(Expression<Func<T, bool>>? predicate = null);
}
}

View File

@@ -0,0 +1,265 @@
using Microsoft.EntityFrameworkCore;
using HardwarePerformance.Data;
using HardwarePerformance.Models;
namespace HardwarePerformance.Repositories
{
public class ProductRepository : Repository<Product>, IProductRepository
{
public ProductRepository(AppDbContext context) : base(context)
{
}
public async Task<IEnumerable<Product>> GetByCategoryAsync(int categoryId)
{
return await _dbSet
.Where(p => p.CategoryId == categoryId)
.OrderBy(p => p.CurrentRank)
.ToListAsync();
}
public async Task<IEnumerable<Product>> GetByCategoryAsync(int categoryId, int page, int pageSize, string sortBy = "CurrentRank", string sortOrder = "asc")
{
var query = _dbSet.Where(p => p.CategoryId == categoryId);
// 排序
query = sortBy.ToLower() switch
{
"name" => sortOrder.ToLower() == "desc" ? query.OrderByDescending(p => p.Name) : query.OrderBy(p => p.Name),
"manufacturer" => sortOrder.ToLower() == "desc" ? query.OrderByDescending(p => p.Manufacturer) : query.OrderBy(p => p.Manufacturer),
"releasedate" => sortOrder.ToLower() == "desc" ? query.OrderByDescending(p => p.ReleaseDate) : query.OrderBy(p => p.ReleaseDate),
"price" => sortOrder.ToLower() == "desc" ? query.OrderByDescending(p => p.Price) : query.OrderBy(p => p.Price),
_ => sortOrder.ToLower() == "desc" ? query.OrderByDescending(p => p.CurrentRank) : query.OrderBy(p => p.CurrentRank)
};
return await query
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
}
public async Task<IEnumerable<Product>> SearchAsync(string query)
{
return await _dbSet
.Where(p =>
p.Name.Contains(query) ||
p.Model.Contains(query) ||
p.Manufacturer.Contains(query))
.ToListAsync();
}
public async Task<IEnumerable<Product>> SearchAsync(string query, int? categoryId = null, string? manufacturer = null, int? minScore = null, int? maxScore = null, int page = 1, int pageSize = 20)
{
var queryable = _dbSet.AsQueryable();
if (categoryId.HasValue)
{
queryable = queryable.Where(p => p.CategoryId == categoryId.Value);
}
if (!string.IsNullOrEmpty(manufacturer))
{
queryable = queryable.Where(p => p.Manufacturer == manufacturer);
}
if (minScore.HasValue)
{
// 使用排名计算性能分数100 - 排名)
queryable = queryable.Where(p => (100 - p.CurrentRank) >= minScore.Value);
}
if (maxScore.HasValue)
{
// 使用排名计算性能分数100 - 排名)
queryable = queryable.Where(p => (100 - p.CurrentRank) <= maxScore.Value);
}
if (!string.IsNullOrEmpty(query))
{
queryable = queryable.Where(p =>
p.Name.Contains(query) ||
p.Model.Contains(query) ||
p.Manufacturer.Contains(query));
}
return await queryable
.OrderBy(p => p.CurrentRank)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
}
public async Task<IEnumerable<Product>> FilterAsync(int? categoryId, string? manufacturer, int? minScore, int? maxScore, int? year)
{
var query = _dbSet.AsQueryable();
if (categoryId.HasValue)
{
query = query.Where(p => p.CategoryId == categoryId.Value);
}
if (!string.IsNullOrEmpty(manufacturer))
{
query = query.Where(p => p.Manufacturer == manufacturer);
}
if (minScore.HasValue)
{
// 使用排名计算性能分数100 - 排名)
query = query.Where(p => (100 - p.CurrentRank) >= minScore.Value);
}
if (maxScore.HasValue)
{
// 使用排名计算性能分数100 - 排名)
query = query.Where(p => (100 - p.CurrentRank) <= maxScore.Value);
}
if (year.HasValue)
{
query = query.Where(p => p.ReleaseDate.Year == year.Value);
}
return await query.ToListAsync();
}
public async Task<IEnumerable<Product>> FilterAsync(int? categoryId, string? manufacturer, int? minScore, int? maxScore, int? year, int page, int pageSize, string sortBy = "CurrentRank", string sortOrder = "asc")
{
var query = _dbSet.AsQueryable();
if (categoryId.HasValue)
{
query = query.Where(p => p.CategoryId == categoryId.Value);
}
if (!string.IsNullOrEmpty(manufacturer))
{
query = query.Where(p => p.Manufacturer == manufacturer);
}
if (minScore.HasValue)
{
// 使用排名计算性能分数100 - 排名)
query = query.Where(p => (100 - p.CurrentRank) >= minScore.Value);
}
if (maxScore.HasValue)
{
// 使用排名计算性能分数100 - 排名)
query = query.Where(p => (100 - p.CurrentRank) <= maxScore.Value);
}
if (year.HasValue)
{
query = query.Where(p => p.ReleaseDate.Year == year.Value);
}
// 排序
query = sortBy.ToLower() switch
{
"name" => sortOrder.ToLower() == "desc" ? query.OrderByDescending(p => p.Name) : query.OrderBy(p => p.Name),
"manufacturer" => sortOrder.ToLower() == "desc" ? query.OrderByDescending(p => p.Manufacturer) : query.OrderBy(p => p.Manufacturer),
"releasedate" => sortOrder.ToLower() == "desc" ? query.OrderByDescending(p => p.ReleaseDate) : query.OrderBy(p => p.ReleaseDate),
"price" => sortOrder.ToLower() == "desc" ? query.OrderByDescending(p => p.Price) : query.OrderBy(p => p.Price),
_ => sortOrder.ToLower() == "desc" ? query.OrderByDescending(p => p.CurrentRank) : query.OrderBy(p => p.CurrentRank)
};
return await query
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
}
public async Task<int> CountAsync(int? categoryId = null)
{
var query = _dbSet.AsQueryable();
if (categoryId.HasValue)
{
query = query.Where(p => p.CategoryId == categoryId.Value);
}
return await query.CountAsync();
}
public async Task<int> CountSearchResultsAsync(string query, int? categoryId = null, string? manufacturer = null, int? minScore = null, int? maxScore = null)
{
var queryable = _dbSet.AsQueryable();
if (categoryId.HasValue)
{
queryable = queryable.Where(p => p.CategoryId == categoryId.Value);
}
if (!string.IsNullOrEmpty(manufacturer))
{
queryable = queryable.Where(p => p.Manufacturer == manufacturer);
}
if (minScore.HasValue)
{
// 使用排名计算性能分数100 - 排名)
queryable = queryable.Where(p => (100 - p.CurrentRank) >= minScore.Value);
}
if (maxScore.HasValue)
{
// 使用排名计算性能分数100 - 排名)
queryable = queryable.Where(p => (100 - p.CurrentRank) <= maxScore.Value);
}
if (!string.IsNullOrEmpty(query))
{
queryable = queryable.Where(p =>
p.Name.Contains(query) ||
p.Model.Contains(query) ||
p.Manufacturer.Contains(query));
}
return await queryable.CountAsync();
}
public async Task<int> CountFilterResultsAsync(int? categoryId, string? manufacturer, int? minScore, int? maxScore, int? year)
{
var query = _dbSet.AsQueryable();
if (categoryId.HasValue)
{
query = query.Where(p => p.CategoryId == categoryId.Value);
}
if (!string.IsNullOrEmpty(manufacturer))
{
query = query.Where(p => p.Manufacturer == manufacturer);
}
if (minScore.HasValue)
{
// 使用排名计算性能分数100 - 排名)
query = query.Where(p => (100 - p.CurrentRank) >= minScore.Value);
}
if (maxScore.HasValue)
{
// 使用排名计算性能分数100 - 排名)
query = query.Where(p => (100 - p.CurrentRank) <= maxScore.Value);
}
if (year.HasValue)
{
query = query.Where(p => p.ReleaseDate.Year == year.Value);
}
return await query.CountAsync();
}
public async Task<Category?> GetCategoryByProductIdAsync(int productId)
{
var product = await _dbSet
.Include(p => p.Category)
.FirstOrDefaultAsync(p => p.Id == productId);
return product?.Category;
}
}
}

View File

@@ -0,0 +1,60 @@
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using HardwarePerformance.Data;
using HardwarePerformance.Models;
namespace HardwarePerformance.Repositories
{
public class Repository<T> : IRepository<T> where T : class
{
protected readonly AppDbContext _context;
protected readonly DbSet<T> _dbSet;
public Repository(AppDbContext context)
{
_context = context;
_dbSet = context.Set<T>();
}
public async Task<T?> GetByIdAsync(int id)
{
return await _dbSet.FindAsync(id);
}
public async Task<IEnumerable<T>> GetAllAsync()
{
return await _dbSet.ToListAsync();
}
public async Task<IEnumerable<T>> FindAsync(Expression<Func<T, bool>> predicate)
{
return await _dbSet.Where(predicate).ToListAsync();
}
public async Task<T> AddAsync(T entity)
{
await _dbSet.AddAsync(entity);
await _context.SaveChangesAsync();
return entity;
}
public async Task UpdateAsync(T entity)
{
_dbSet.Update(entity);
await _context.SaveChangesAsync();
}
public async Task DeleteAsync(T entity)
{
_dbSet.Remove(entity);
await _context.SaveChangesAsync();
}
public async Task<int> CountAsync(Expression<Func<T, bool>>? predicate = null)
{
if (predicate == null)
return await _dbSet.CountAsync();
return await _dbSet.CountAsync(predicate);
}
}
}

View File

@@ -0,0 +1,30 @@
using AutoMapper;
using HardwarePerformance.Models.DTOs;
using HardwarePerformance.Repositories;
namespace HardwarePerformance.Services
{
public class CategoryService : ICategoryService
{
private readonly ICategoryRepository _categoryRepository;
private readonly IMapper _mapper;
public CategoryService(ICategoryRepository categoryRepository, IMapper mapper)
{
_categoryRepository = categoryRepository;
_mapper = mapper;
}
public async Task<IEnumerable<CategoryDto>> GetAllCategoriesAsync()
{
var categories = await _categoryRepository.GetAllAsync();
return _mapper.Map<IEnumerable<CategoryDto>>(categories);
}
public async Task<CategoryDto?> GetCategoryByIdAsync(int id)
{
var category = await _categoryRepository.GetByIdAsync(id);
return category != null ? _mapper.Map<CategoryDto>(category) : null;
}
}
}

View File

@@ -0,0 +1,271 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MinimalAPI.Data;
using MinimalAPI.Models;
using MinimalAPI.Models.DTOs;
namespace MinimalAPI.Services
{
public class ComparisonService : IComparisonService
{
private readonly AppDbContext _context;
private readonly IMapper _mapper;
public ComparisonService(AppDbContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
public async Task<object> CompareProductsAsync(List<int> productIds)
{
// 验证输入
if (productIds == null || productIds.Count < 2 || productIds.Count > 4)
{
return null;
}
// 获取产品
var products = await _context.Products
.Include(p => p.Category)
.Where(p => productIds.Contains(p.Id))
.ToListAsync();
if (products.Count != productIds.Count)
{
return null;
}
// 检查是否所有产品属于同一类别
var categoryIds = products.Select(p => p.CategoryId).Distinct().ToList();
if (categoryIds.Count > 1)
{
return null;
}
// 获取性能分数
var productIdsList = products.Select(p => p.Id).ToList();
var performanceScores = await _context.PerformanceScores
.Where(ps => productIdsList.Contains(ps.ProductId))
.ToListAsync();
// 获取规格参数
var specifications = await _context.Specifications
.Where(s => productIdsList.Contains(s.ProductId))
.ToListAsync();
// 创建产品DTO列表
var productDtos = products.Select(p => {
var dto = _mapper.Map<ProductDto>(p);
dto.PerformanceScores = performanceScores
.Where(ps => ps.ProductId == p.Id)
.ToList();
dto.Specifications = specifications
.Where(s => s.ProductId == p.Id)
.ToList();
return dto;
}).ToList();
// 生成对比矩阵
var comparisonMatrix = GenerateComparisonMatrix(productDtos);
return new
{
Products = productDtos,
Comparison = comparisonMatrix
};
}
private List<Dictionary<string, object>> GenerateComparisonMatrix(List<ProductDto> products)
{
var matrix = new List<Dictionary<string, object>>();
// 添加基本信息行
matrix.Add(new Dictionary<string, object> { ["指标"] = "产品名称", ["类型"] = "基本信息" });
foreach (var product in products)
{
matrix[0][$"{product.Name}"] = product.Name;
}
// 添加制造商行
matrix.Add(new Dictionary<string, object> { ["指标"] = "制造商", ["类型"] = "基本信息" });
foreach (var product in products)
{
matrix[1][$"{product.Name}"] = product.Manufacturer;
}
// 添加型号行
matrix.Add(new Dictionary<string, object> { ["指标"] = "型号", ["类型"] = "基本信息" });
foreach (var product in products)
{
matrix[2][$"{product.Name}"] = product.Model;
}
// 添加排名行
matrix.Add(new Dictionary<string, object> { ["指标"] = "当前排名", ["类型"] = "性能指标" });
foreach (var product in products)
{
matrix[3][$"{product.Name}"] = product.CurrentRank;
}
// 添加发布日期行
matrix.Add(new Dictionary<string, object> { ["指标"] = "发布日期", ["类型"] = "基本信息" });
foreach (var product in products)
{
matrix[4][$"{product.Name}"] = product.ReleaseDate.ToString("yyyy-MM-dd");
}
// 添加价格行
matrix.Add(new Dictionary<string, object> { ["指标"] = "价格", ["类型"] = "基本信息" });
foreach (var product in products)
{
matrix[5][$"{product.Name}"] = product.Price?.ToString("C") ?? "N/A";
}
// 添加性能分数行
var benchmarkTypes = products
.SelectMany(p => p.PerformanceScores)
.Select(ps => ps.BenchmarkType)
.Distinct()
.ToList();
foreach (var benchmarkType in benchmarkTypes)
{
var rowIndex = matrix.Count;
matrix.Add(new Dictionary<string, object> { ["指标"] = benchmarkType, ["类型"] = "性能指标" });
foreach (var product in products)
{
var score = product.PerformanceScores
.FirstOrDefault(ps => ps.BenchmarkType == benchmarkType)?.Score;
matrix[rowIndex][$"{product.Name}"] = score?.ToString() ?? "N/A";
}
}
// 添加规格参数行
var specNames = products
.SelectMany(p => p.Specifications)
.Select(s => s.Name)
.Distinct()
.ToList();
foreach (var specName in specNames)
{
var rowIndex = matrix.Count;
matrix.Add(new Dictionary<string, object> { ["指标"] = specName, ["类型"] = "规格参数" });
foreach (var product in products)
{
var specValue = product.Specifications
.FirstOrDefault(s => s.Name == specName)?.Value;
matrix[rowIndex][$"{product.Name}"] = specValue ?? "N/A";
}
}
// 标记最优和最差值
MarkBestAndWorstValues(matrix, products);
return matrix;
}
private void MarkBestAndWorstValues(List<Dictionary<string, object>> matrix, List<ProductDto> products)
{
// 对于排名,越小越好
var rankRow = matrix.FirstOrDefault(m => m["指标"].ToString() == "当前排名");
if (rankRow != null)
{
var ranks = products.Select(p => p.CurrentRank).ToList();
var minRank = ranks.Min();
var maxRank = ranks.Max();
foreach (var product in products)
{
var rank = product.CurrentRank;
if (rank == minRank)
{
rankRow[$"{product.Name}_isBest"] = true;
}
else if (rank == maxRank)
{
rankRow[$"{product.Name}_isWorst"] = true;
}
}
}
// 对于价格,越小越好
var priceRow = matrix.FirstOrDefault(m => m["指标"].ToString() == "价格");
if (priceRow != null)
{
var prices = products.Where(p => p.Price.HasValue).Select(p => p.Price!.Value).ToList();
if (prices.Any())
{
var minPrice = prices.Min();
var maxPrice = prices.Max();
foreach (var product in products)
{
if (product.Price.HasValue)
{
if (product.Price == minPrice)
{
priceRow[$"{product.Name}_isBest"] = true;
}
else if (product.Price == maxPrice)
{
priceRow[$"{product.Name}_isWorst"] = true;
}
}
}
}
}
// 对于性能分数,越大越好
var performanceRows = matrix.Where(m => m["类型"].ToString() == "性能指标" &&
m["指标"].ToString() != "当前排名").ToList();
foreach (var row in performanceRows)
{
var benchmarkType = row["指标"].ToString();
var scores = new List<decimal?>();
foreach (var product in products)
{
var scoreStr = row[$"{product.Name}"]?.ToString();
if (decimal.TryParse(scoreStr, out var score))
{
scores.Add(score);
}
else
{
scores.Add(null);
}
}
var validScores = scores.Where(s => s.HasValue).Select(s => s!.Value).ToList();
if (validScores.Any())
{
var maxScore = validScores.Max();
var minScore = validScores.Min();
for (int i = 0; i < products.Count; i++)
{
if (scores[i].HasValue)
{
if (scores[i] == maxScore)
{
row[$"{products[i].Name}_isBest"] = true;
}
else if (scores[i] == minScore)
{
row[$"{products[i].Name}_isWorst"] = true;
}
}
}
}
}
}
}
}

View File

@@ -0,0 +1,10 @@
using HardwarePerformance.Models.DTOs;
namespace HardwarePerformance.Services
{
public interface ICategoryService
{
Task<IEnumerable<CategoryDto>> GetAllCategoriesAsync();
Task<CategoryDto?> GetCategoryByIdAsync(int id);
}
}

View File

@@ -0,0 +1,10 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MinimalAPI.Services
{
public interface IComparisonService
{
Task<object> CompareProductsAsync(List<int> productIds);
}
}

View File

@@ -0,0 +1,12 @@
using HardwarePerformance.Models.DTOs;
namespace HardwarePerformance.Services
{
public interface IProductService
{
Task<PagedResultDto<ProductListDto>> GetProductsByCategoryAsync(int categoryId, int page = 1, int pageSize = 20, string sortBy = "CurrentRank", string order = "asc");
Task<ProductDto?> GetProductByIdAsync(int id);
Task<PagedResultDto<ProductListDto>> SearchProductsAsync(string query, int? categoryId = null, string? manufacturer = null, int? minScore = null, int? maxScore = null, int page = 1, int pageSize = 20);
Task<PagedResultDto<ProductListDto>> FilterProductsAsync(int categoryId, string? manufacturer = null, int? minScore = null, int? maxScore = null, int? releaseYear = null, int page = 1, int pageSize = 20, string sortBy = "CurrentRank", string order = "asc");
}
}

View File

@@ -0,0 +1,91 @@
using AutoMapper;
using HardwarePerformance.Models.DTOs;
using HardwarePerformance.Repositories;
namespace HardwarePerformance.Services
{
public class ProductService : IProductService
{
private readonly IProductRepository _productRepository;
private readonly IMapper _mapper;
public ProductService(IProductRepository productRepository, IMapper mapper)
{
_productRepository = productRepository;
_mapper = mapper;
}
public async Task<PagedResultDto<ProductListDto>> GetProductsByCategoryAsync(int categoryId, int page = 1, int pageSize = 20, string sortBy = "CurrentRank", string order = "asc")
{
var products = await _productRepository.GetByCategoryAsync(categoryId, page, pageSize, sortBy, order);
var totalCount = await _productRepository.CountAsync(categoryId);
var productDtos = _mapper.Map<IEnumerable<ProductListDto>>(products);
return new PagedResultDto<ProductListDto>
{
Items = productDtos.ToList(),
Total = totalCount,
CurrentPage = page,
PageSize = pageSize,
TotalPages = (int)Math.Ceiling((double)totalCount / pageSize)
};
}
public async Task<ProductDto?> GetProductByIdAsync(int id)
{
var product = await _productRepository.GetByIdAsync(id);
if (product == null) return null;
var productDto = _mapper.Map<ProductDto>(product);
// 获取类别信息
var category = await _productRepository.GetCategoryByProductIdAsync(id);
if (category != null)
{
productDto.Category = new CategoryDto
{
Id = category.Id,
Name = category.Name,
Description = category.Description
};
}
return productDto;
}
public async Task<PagedResultDto<ProductListDto>> SearchProductsAsync(string query, int? categoryId = null, string? manufacturer = null, int? minScore = null, int? maxScore = null, int page = 1, int pageSize = 20)
{
var products = await _productRepository.SearchAsync(query, categoryId, manufacturer, minScore, maxScore, page, pageSize);
var totalCount = await _productRepository.CountSearchResultsAsync(query, categoryId, manufacturer, minScore, maxScore);
var productDtos = _mapper.Map<IEnumerable<ProductListDto>>(products);
return new PagedResultDto<ProductListDto>
{
Items = productDtos.ToList(),
Total = totalCount,
CurrentPage = page,
PageSize = pageSize,
TotalPages = (int)Math.Ceiling((double)totalCount / pageSize)
};
}
public async Task<PagedResultDto<ProductListDto>> FilterProductsAsync(int categoryId, string? manufacturer = null, int? minScore = null, int? maxScore = null, int? releaseYear = null, int page = 1, int pageSize = 20, string sortBy = "CurrentRank", string order = "asc")
{
var products = await _productRepository.FilterAsync(categoryId, manufacturer, minScore, maxScore, releaseYear, page, pageSize, sortBy, order);
var totalCount = await _productRepository.CountFilterResultsAsync(categoryId, manufacturer, minScore, maxScore, releaseYear);
var productDtos = _mapper.Map<IEnumerable<ProductListDto>>(products);
return new PagedResultDto<ProductListDto>
{
Items = productDtos.ToList(),
Total = totalCount,
CurrentPage = page,
PageSize = pageSize,
TotalPages = (int)Math.Ceiling((double)totalCount / pageSize)
};
}
}
}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@@ -0,0 +1,23 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v9.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v9.0": {
"MinimalAPI/1.0.0": {
"runtime": {
"MinimalAPI.dll": {}
}
}
}
},
"libraries": {
"MinimalAPI/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,19 @@
{
"runtimeOptions": {
"tfm": "net9.0",
"frameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "9.0.0"
},
{
"name": "Microsoft.AspNetCore.App",
"version": "9.0.0"
}
],
"configProperties": {
"System.GC.Server": true,
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
}
}
}

View File

@@ -0,0 +1 @@
{"Version":1,"ManifestType":"Build","Endpoints":[]}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v9.0", FrameworkDisplayName = ".NET 9.0")]

View File

@@ -0,0 +1,22 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("MinimalAPI")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
[assembly: System.Reflection.AssemblyProductAttribute("MinimalAPI")]
[assembly: System.Reflection.AssemblyTitleAttribute("MinimalAPI")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// 由 MSBuild WriteCodeFragment 类生成。

View File

@@ -0,0 +1 @@
d440d87b19ec785d73e3e23306ab9680f7c4890517e977a1c254363c4f9347af

View File

@@ -0,0 +1,21 @@
is_global = true
build_property.TargetFramework = net9.0
build_property.TargetPlatformMinVersion =
build_property.UsingMicrosoftNETSdkWeb = true
build_property.ProjectTypeGuids =
build_property.InvariantGlobalization =
build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = MinimalAPI
build_property.RootNamespace = MinimalAPI
build_property.ProjectDir = C:\work\电脑硬件-01\MinimalAPI\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.RazorLangVersion = 9.0
build_property.SupportLocalizedComponentNames =
build_property.GenerateRazorMetadataSourceChecksumAttributes =
build_property.MSBuildProjectDirectory = C:\work\电脑硬件-01\MinimalAPI
build_property._RazorSourceGeneratorDebug =
build_property.EffectiveAnalysisLevelStyle = 9.0
build_property.EnableCodeStyleSeverity =

View File

@@ -0,0 +1,17 @@
// <auto-generated/>
global using global::Microsoft.AspNetCore.Builder;
global using global::Microsoft.AspNetCore.Hosting;
global using global::Microsoft.AspNetCore.Http;
global using global::Microsoft.AspNetCore.Routing;
global using global::Microsoft.Extensions.Configuration;
global using global::Microsoft.Extensions.DependencyInjection;
global using global::Microsoft.Extensions.Hosting;
global using global::Microsoft.Extensions.Logging;
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Net.Http.Json;
global using global::System.Threading;
global using global::System.Threading.Tasks;

Binary file not shown.

View File

@@ -0,0 +1 @@
05c277f9ef6d1c96b7c3a0662bb97967ee23e63c93d8aa1cd4aec40fc1c9febe

View File

@@ -0,0 +1,27 @@
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\rpswa.dswa.cache.json
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\MinimalAPI.GeneratedMSBuildEditorConfig.editorconfig
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\MinimalAPI.AssemblyInfoInputs.cache
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\MinimalAPI.AssemblyInfo.cs
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\MinimalAPI.csproj.CoreCompileInputs.cache
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\MinimalAPI.MvcApplicationPartsAssemblyInfo.cache
C:\work\电脑硬件-01\MinimalAPI\bin\Debug\net9.0\appsettings.Development.json
C:\work\电脑硬件-01\MinimalAPI\bin\Debug\net9.0\appsettings.json
C:\work\电脑硬件-01\MinimalAPI\bin\Debug\net9.0\MinimalAPI.staticwebassets.endpoints.json
C:\work\电脑硬件-01\MinimalAPI\bin\Debug\net9.0\MinimalAPI.exe
C:\work\电脑硬件-01\MinimalAPI\bin\Debug\net9.0\MinimalAPI.deps.json
C:\work\电脑硬件-01\MinimalAPI\bin\Debug\net9.0\MinimalAPI.runtimeconfig.json
C:\work\电脑硬件-01\MinimalAPI\bin\Debug\net9.0\MinimalAPI.dll
C:\work\电脑硬件-01\MinimalAPI\bin\Debug\net9.0\MinimalAPI.pdb
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\rjimswa.dswa.cache.json
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\rjsmrazor.dswa.cache.json
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\rjsmcshtml.dswa.cache.json
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\scopedcss\bundle\MinimalAPI.styles.css
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\staticwebassets.build.json
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\staticwebassets.build.json.cache
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\staticwebassets.development.json
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\staticwebassets.build.endpoints.json
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\MinimalAPI.dll
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\refint\MinimalAPI.dll
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\MinimalAPI.pdb
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\MinimalAPI.genruntimeconfig.cache
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\ref\MinimalAPI.dll

Binary file not shown.

View File

@@ -0,0 +1 @@
2cc508c4c984e00de0cee28ea5f00a982100be8dad563e9011ff5619c65193f1

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
{"GlobalPropertiesHash":"UvHx1BUBnfpFc6gxIpp/8mxp/Pr5ugj++Q4ag8rCqAY=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["vICfSWyJTl7Llwoo5pZeHmuInfb\u002BPDuiEXf67e03aVY=","HGCAjAUzY86/cTTfeLk8LuqAnP1lIVdvBKQP\u002BJ23JAc=","pXPB2q0SLSiqgQ6QWxaK7/r8KBQLxQCCriy2hCwae1A=","eZ0avPpvDZzkkkg3Y40\u002B9/FcOts1TlMSAvHEIAFbU8Q="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -0,0 +1 @@
{"GlobalPropertiesHash":"N05q42TiGbCpQThCMeppx1cqHYMYPr+MKOVECH+LM5o=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["vICfSWyJTl7Llwoo5pZeHmuInfb\u002BPDuiEXf67e03aVY=","HGCAjAUzY86/cTTfeLk8LuqAnP1lIVdvBKQP\u002BJ23JAc=","pXPB2q0SLSiqgQ6QWxaK7/r8KBQLxQCCriy2hCwae1A=","eZ0avPpvDZzkkkg3Y40\u002B9/FcOts1TlMSAvHEIAFbU8Q="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -0,0 +1 @@
{"GlobalPropertiesHash":"Qscza4zuOZL6qExaAaOGJZbKXeiSjSngMQ6k1ALDEr8=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["vICfSWyJTl7Llwoo5pZeHmuInfb\u002BPDuiEXf67e03aVY=","HGCAjAUzY86/cTTfeLk8LuqAnP1lIVdvBKQP\u002BJ23JAc="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -0,0 +1 @@
{"Version":1,"ManifestType":"Build","Endpoints":[]}

View File

@@ -0,0 +1 @@
{"Version":1,"Hash":"JkTLtN583la2vWjETk7JpCURqkzL9J5tTWuu+nxCKow=","Source":"MinimalAPI","BasePath":"_content/MinimalAPI","Mode":"Default","ManifestType":"Build","ReferencedProjectsConfiguration":[],"DiscoveryPatterns":[],"Assets":[],"Endpoints":[]}

View File

@@ -0,0 +1 @@
JkTLtN583la2vWjETk7JpCURqkzL9J5tTWuu+nxCKow=

View File

@@ -0,0 +1,86 @@
{
"format": 1,
"restore": {
"C:\\work\\电脑硬件-01\\MinimalAPI\\MinimalAPI.csproj": {}
},
"projects": {
"C:\\work\\电脑硬件-01\\MinimalAPI\\MinimalAPI.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "C:\\work\\电脑硬件-01\\MinimalAPI\\MinimalAPI.csproj",
"projectName": "MinimalAPI",
"projectPath": "C:\\work\\电脑硬件-01\\MinimalAPI\\MinimalAPI.csproj",
"packagesPath": "C:\\Users\\代\\.nuget\\packages\\",
"outputPath": "C:\\work\\电脑硬件-01\\MinimalAPI\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
],
"configFilePaths": [
"C:\\Users\\代\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
],
"originalTargetFrameworks": [
"net9.0"
],
"sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"https://api.nuget.org/v3/index.json": {},
"https://nuget.cdn.azure.cn/v3/index.json": {},
"https://packages.chinacloudapi.cn/v3/index.json": {},
"https://packages.microsoft.com/dotnet": {},
"https://www.nuget.org/api/v2/": {}
},
"frameworks": {
"net9.0": {
"targetAlias": "net9.0",
"projectReferences": {}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
},
"SdkAnalysisLevel": "9.0.300"
},
"frameworks": {
"net9.0": {
"targetAlias": "net9.0",
"dependencies": {
"Microsoft.AspNetCore.ResponseCompression": {
"target": "Package",
"version": "[2.2.0, )"
}
},
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"frameworkReferences": {
"Microsoft.AspNetCore.App": {
"privateAssets": "none"
},
"Microsoft.NETCore.App": {
"privateAssets": "all"
}
},
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
}
}
}
}
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\代\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.0</NuGetToolVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<SourceRoot Include="C:\Users\代\.nuget\packages\" />
<SourceRoot Include="C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages\" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />

View File

@@ -0,0 +1,102 @@
{
"version": 3,
"targets": {
"net9.0": {}
},
"libraries": {},
"projectFileDependencyGroups": {
"net9.0": [
"Microsoft.AspNetCore.ResponseCompression >= 2.2.0"
]
},
"packageFolders": {
"C:\\Users\\代\\.nuget\\packages\\": {},
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {}
},
"project": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "C:\\work\\电脑硬件-01\\MinimalAPI\\MinimalAPI.csproj",
"projectName": "MinimalAPI",
"projectPath": "C:\\work\\电脑硬件-01\\MinimalAPI\\MinimalAPI.csproj",
"packagesPath": "C:\\Users\\代\\.nuget\\packages\\",
"outputPath": "C:\\work\\电脑硬件-01\\MinimalAPI\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
],
"configFilePaths": [
"C:\\Users\\代\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
],
"originalTargetFrameworks": [
"net9.0"
],
"sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"https://api.nuget.org/v3/index.json": {},
"https://nuget.cdn.azure.cn/v3/index.json": {},
"https://packages.chinacloudapi.cn/v3/index.json": {},
"https://packages.microsoft.com/dotnet": {},
"https://www.nuget.org/api/v2/": {}
},
"frameworks": {
"net9.0": {
"targetAlias": "net9.0",
"projectReferences": {}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
},
"SdkAnalysisLevel": "9.0.300"
},
"frameworks": {
"net9.0": {
"targetAlias": "net9.0",
"dependencies": {
"Microsoft.AspNetCore.ResponseCompression": {
"target": "Package",
"version": "[2.2.0, )"
}
},
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"frameworkReferences": {
"Microsoft.AspNetCore.App": {
"privateAssets": "none"
},
"Microsoft.NETCore.App": {
"privateAssets": "all"
}
},
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
}
}
},
"logs": [
{
"code": "NU1301",
"level": "Error",
"message": "未能从远程源“https://www.nuget.org/api/v2/FindPackagesById()?id='Microsoft.AspNetCore.ResponseCompression'&semVerLevel=2.0.0”检索有关“Microsoft.AspNetCore.ResponseCompression”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
"libraryId": "Microsoft.AspNetCore.ResponseCompression"
}
]
}

View File

@@ -0,0 +1,18 @@
{
"version": 2,
"dgSpecHash": "ionCG+ayp8g=",
"success": false,
"projectFilePath": "C:\\work\\电脑硬件-01\\MinimalAPI\\MinimalAPI.csproj",
"expectedPackageFiles": [],
"logs": [
{
"code": "NU1301",
"level": "Error",
"message": "未能从远程源“https://www.nuget.org/api/v2/FindPackagesById()?id='Microsoft.AspNetCore.ResponseCompression'&semVerLevel=2.0.0”检索有关“Microsoft.AspNetCore.ResponseCompression”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
"projectPath": "C:\\work\\电脑硬件-01\\MinimalAPI\\MinimalAPI.csproj",
"filePath": "C:\\work\\电脑硬件-01\\MinimalAPI\\MinimalAPI.csproj",
"libraryId": "Microsoft.AspNetCore.ResponseCompression",
"targetGraphs": []
}
]
}

41
SimpleAPI/Program.cs Normal file
View File

@@ -0,0 +1,41 @@
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseHttpsRedirection();
var summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
app.MapGet("/weatherforecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast");
app.Run();
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

View File

@@ -0,0 +1,23 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:5256",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://localhost:7149;http://localhost:5256",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.8" />
</ItemGroup>
</Project>

6
SimpleAPI/SimpleAPI.http Normal file
View File

@@ -0,0 +1,6 @@
@SimpleAPI_HostAddress = http://localhost:5256
GET {{SimpleAPI_HostAddress}}/weatherforecast/
Accept: application/json
###

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@@ -0,0 +1,86 @@
{
"format": 1,
"restore": {
"C:\\work\\电脑硬件-01\\SimpleAPI\\SimpleAPI.csproj": {}
},
"projects": {
"C:\\work\\电脑硬件-01\\SimpleAPI\\SimpleAPI.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "C:\\work\\电脑硬件-01\\SimpleAPI\\SimpleAPI.csproj",
"projectName": "SimpleAPI",
"projectPath": "C:\\work\\电脑硬件-01\\SimpleAPI\\SimpleAPI.csproj",
"packagesPath": "C:\\Users\\代\\.nuget\\packages\\",
"outputPath": "C:\\work\\电脑硬件-01\\SimpleAPI\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
],
"configFilePaths": [
"C:\\Users\\代\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
],
"originalTargetFrameworks": [
"net9.0"
],
"sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"https://api.nuget.org/v3/index.json": {},
"https://nuget.cdn.azure.cn/v3/index.json": {},
"https://packages.chinacloudapi.cn/v3/index.json": {},
"https://packages.microsoft.com/dotnet": {},
"https://www.nuget.org/api/v2/": {}
},
"frameworks": {
"net9.0": {
"targetAlias": "net9.0",
"projectReferences": {}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
},
"SdkAnalysisLevel": "9.0.300"
},
"frameworks": {
"net9.0": {
"targetAlias": "net9.0",
"dependencies": {
"Microsoft.AspNetCore.OpenApi": {
"target": "Package",
"version": "[9.0.8, )"
}
},
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"frameworkReferences": {
"Microsoft.AspNetCore.App": {
"privateAssets": "none"
},
"Microsoft.NETCore.App": {
"privateAssets": "all"
}
},
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
}
}
}
}
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\代\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.0</NuGetToolVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<SourceRoot Include="C:\Users\代\.nuget\packages\" />
<SourceRoot Include="C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages\" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />

View File

@@ -0,0 +1,108 @@
{
"version": 3,
"targets": {
"net9.0": {}
},
"libraries": {},
"projectFileDependencyGroups": {
"net9.0": [
"Microsoft.AspNetCore.OpenApi >= 9.0.8"
]
},
"packageFolders": {
"C:\\Users\\代\\.nuget\\packages\\": {},
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {}
},
"project": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "C:\\work\\电脑硬件-01\\SimpleAPI\\SimpleAPI.csproj",
"projectName": "SimpleAPI",
"projectPath": "C:\\work\\电脑硬件-01\\SimpleAPI\\SimpleAPI.csproj",
"packagesPath": "C:\\Users\\代\\.nuget\\packages\\",
"outputPath": "C:\\work\\电脑硬件-01\\SimpleAPI\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
],
"configFilePaths": [
"C:\\Users\\代\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
],
"originalTargetFrameworks": [
"net9.0"
],
"sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"https://api.nuget.org/v3/index.json": {},
"https://nuget.cdn.azure.cn/v3/index.json": {},
"https://packages.chinacloudapi.cn/v3/index.json": {},
"https://packages.microsoft.com/dotnet": {},
"https://www.nuget.org/api/v2/": {}
},
"frameworks": {
"net9.0": {
"targetAlias": "net9.0",
"projectReferences": {}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
},
"SdkAnalysisLevel": "9.0.300"
},
"frameworks": {
"net9.0": {
"targetAlias": "net9.0",
"dependencies": {
"Microsoft.AspNetCore.OpenApi": {
"target": "Package",
"version": "[9.0.8, )"
}
},
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"frameworkReferences": {
"Microsoft.AspNetCore.App": {
"privateAssets": "none"
},
"Microsoft.NETCore.App": {
"privateAssets": "all"
}
},
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
}
}
},
"logs": [
{
"code": "NU1301",
"level": "Error",
"message": "未能从远程源“https://www.nuget.org/api/v2/FindPackagesById()?id='Microsoft.AspNetCore.OpenApi'&semVerLevel=2.0.0”检索有关“Microsoft.AspNetCore.OpenApi”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
"libraryId": "Microsoft.AspNetCore.OpenApi"
},
{
"code": "NU1301",
"level": "Error",
"message": "未能从远程源“https://packages.microsoft.com/dotnet/FindPackagesById()?id='Microsoft.AspNetCore.OpenApi'&semVerLevel=2.0.0”检索有关“Microsoft.AspNetCore.OpenApi”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
"libraryId": "Microsoft.AspNetCore.OpenApi"
}
]
}

View File

@@ -0,0 +1,27 @@
{
"version": 2,
"dgSpecHash": "qHQETFOfcmo=",
"success": false,
"projectFilePath": "C:\\work\\电脑硬件-01\\SimpleAPI\\SimpleAPI.csproj",
"expectedPackageFiles": [],
"logs": [
{
"code": "NU1301",
"level": "Error",
"message": "未能从远程源“https://www.nuget.org/api/v2/FindPackagesById()?id='Microsoft.AspNetCore.OpenApi'&semVerLevel=2.0.0”检索有关“Microsoft.AspNetCore.OpenApi”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
"projectPath": "C:\\work\\电脑硬件-01\\SimpleAPI\\SimpleAPI.csproj",
"filePath": "C:\\work\\电脑硬件-01\\SimpleAPI\\SimpleAPI.csproj",
"libraryId": "Microsoft.AspNetCore.OpenApi",
"targetGraphs": []
},
{
"code": "NU1301",
"level": "Error",
"message": "未能从远程源“https://packages.microsoft.com/dotnet/FindPackagesById()?id='Microsoft.AspNetCore.OpenApi'&semVerLevel=2.0.0”检索有关“Microsoft.AspNetCore.OpenApi”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
"projectPath": "C:\\work\\电脑硬件-01\\SimpleAPI\\SimpleAPI.csproj",
"filePath": "C:\\work\\电脑硬件-01\\SimpleAPI\\SimpleAPI.csproj",
"libraryId": "Microsoft.AspNetCore.OpenApi",
"targetGraphs": []
}
]
}

471
TestAPI/Program.cs Normal file
View File

@@ -0,0 +1,471 @@
using Microsoft.AspNetCore.ResponseCompression;
using System.IO.Compression;
using System.Text.Json;
using System.Linq;
var builder = WebApplication.CreateBuilder(args);
// 添加响应压缩服务
builder.Services.AddResponseCompression(options =>
{
options.EnableForHttps = true;
options.Providers.Add<BrotliCompressionProvider>();
options.Providers.Add<GzipCompressionProvider>();
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "application/json" });
});
// 配置压缩级别
builder.Services.Configure<BrotliCompressionProviderOptions>(options =>
{
options.Level = CompressionLevel.Fastest;
});
builder.Services.Configure<GzipCompressionProviderOptions>(options =>
{
options.Level = CompressionLevel.Fastest;
});
var app = builder.Build();
// 使用响应压缩中间件
app.UseResponseCompression();
// 添加一个返回大量数据的API端点以便测试压缩效果
app.MapGet("/api/data", () => new
{
Message = "这是一个用于测试响应压缩的API端点",
Data = Enumerable.Range(1, 1000).Select(i => new { Id = i, Name = $"Item {i}", Description = $"这是第{i}个项目的描述,包含更多文本内容以便测试压缩效果。" }).ToArray(),
Timestamp = DateTime.UtcNow
});
// 添加类别API端点供前端测试使用
app.MapGet("/api/categories", () => new[]
{
new { Id = 1, Name = "手机CPU", Description = "移动设备处理器性能排名", ProductCount = 25 },
new { Id = 2, Name = "手机GPU", Description = "移动设备图形处理器性能排名", ProductCount = 18 },
new { Id = 3, Name = "电脑CPU", Description = "桌面处理器性能排名", ProductCount = 32 },
new { Id = 4, Name = "电脑GPU", Description = "桌面显卡性能排名", ProductCount = 28 }
});
// 添加产品API端点供前端测试使用
app.MapGet("/api/products", (int? categoryId, int page = 1, int pageSize = 20, string sortBy = "PerformanceScore", string order = "desc") =>
{
// 模拟产品数据
var allProducts = new[]
{
new { Id = 1, Name = "Snapdragon 8 Gen 2", Model = "SM8550-AB", Manufacturer = "Qualcomm", ReleaseYear = 2022, PerformanceScore = 3879, CurrentRank = 1, ImageUrl = "/images/placeholder.svg", CategoryId = 1 },
new { Id = 2, Name = "A16 Bionic", Model = "A16 Bionic", Manufacturer = "Apple", ReleaseYear = 2022, PerformanceScore = 3756, CurrentRank = 2, ImageUrl = "/images/placeholder.svg", CategoryId = 1 },
new { Id = 3, Name = "Dimensity 9200", Model = "MT6985", Manufacturer = "MediaTek", ReleaseYear = 2022, PerformanceScore = 3512, CurrentRank = 3, ImageUrl = "/images/placeholder.svg", CategoryId = 1 },
new { Id = 4, Name = "Snapdragon 8+ Gen 1", Model = "SM8475", Manufacturer = "Qualcomm", ReleaseYear = 2022, PerformanceScore = 3341, CurrentRank = 4, ImageUrl = "/images/placeholder.svg", CategoryId = 1 },
new { Id = 5, Name = "A15 Bionic", Model = "A15 Bionic", Manufacturer = "Apple", ReleaseYear = 2021, PerformanceScore = 3215, CurrentRank = 5, ImageUrl = "/images/placeholder.svg", CategoryId = 1 },
new { Id = 6, Name = "Adreno 740", Model = "Adreno 740", Manufacturer = "Qualcomm", ReleaseYear = 2022, PerformanceScore = 14350, CurrentRank = 1, ImageUrl = "/images/placeholder.svg", CategoryId = 2 },
new { Id = 7, Name = "Apple A16 GPU", Model = "A16 GPU", Manufacturer = "Apple", ReleaseYear = 2022, PerformanceScore = 13980, CurrentRank = 2, ImageUrl = "/images/placeholder.svg", CategoryId = 2 },
new { Id = 8, Name = "Mali-G715 MC10", Model = "Mali-G715", Manufacturer = "ARM", ReleaseYear = 2022, PerformanceScore = 12150, CurrentRank = 3, ImageUrl = "/images/placeholder.svg", CategoryId = 2 },
new { Id = 9, Name = "Adreno 730", Model = "Adreno 730", Manufacturer = "Qualcomm", ReleaseYear = 2022, PerformanceScore = 11020, CurrentRank = 4, ImageUrl = "/images/placeholder.svg", CategoryId = 2 },
new { Id = 10, Name = "Apple A15 GPU", Model = "A15 GPU", Manufacturer = "Apple", ReleaseYear = 2021, PerformanceScore = 10560, CurrentRank = 5, ImageUrl = "/images/placeholder.svg", CategoryId = 2 },
new { Id = 11, Name = "Intel Core i9-13900K", Model = "i9-13900K", Manufacturer = "Intel", ReleaseYear = 2022, PerformanceScore = 3176, CurrentRank = 1, ImageUrl = "/images/placeholder.svg", CategoryId = 3 },
new { Id = 12, Name = "AMD Ryzen 9 7950X", Model = "Ryzen 9 7950X", Manufacturer = "AMD", ReleaseYear = 2022, PerformanceScore = 3095, CurrentRank = 2, ImageUrl = "/images/placeholder.svg", CategoryId = 3 },
new { Id = 13, Name = "Intel Core i7-13700K", Model = "i7-13700K", Manufacturer = "Intel", ReleaseYear = 2022, PerformanceScore = 2956, CurrentRank = 3, ImageUrl = "/images/placeholder.svg", CategoryId = 3 },
new { Id = 14, Name = "AMD Ryzen 9 5900X", Model = "Ryzen 9 5900X", Manufacturer = "AMD", ReleaseYear = 2020, PerformanceScore = 2835, CurrentRank = 4, ImageUrl = "/images/placeholder.svg", CategoryId = 3 },
new { Id = 15, Name = "Intel Core i5-13600K", Model = "i5-13600K", Manufacturer = "Intel", ReleaseYear = 2022, PerformanceScore = 2742, CurrentRank = 5, ImageUrl = "/images/placeholder.svg", CategoryId = 3 },
new { Id = 16, Name = "NVIDIA GeForce RTX 4090", Model = "RTX 4090", Manufacturer = "NVIDIA", ReleaseYear = 2022, PerformanceScore = 38928, CurrentRank = 1, ImageUrl = "/images/placeholder.svg", CategoryId = 4 },
new { Id = 17, Name = "NVIDIA GeForce RTX 4080", Model = "RTX 4080", Manufacturer = "NVIDIA", ReleaseYear = 2022, PerformanceScore = 32168, CurrentRank = 2, ImageUrl = "/images/placeholder.svg", CategoryId = 4 },
new { Id = 18, Name = "NVIDIA GeForce RTX 3090 Ti", Model = "RTX 3090 Ti", Manufacturer = "NVIDIA", ReleaseYear = 2022, PerformanceScore = 28595, CurrentRank = 3, ImageUrl = "/images/placeholder.svg", CategoryId = 4 },
new { Id = 19, Name = "AMD Radeon RX 7900 XTX", Model = "RX 7900 XTX", Manufacturer = "AMD", ReleaseYear = 2022, PerformanceScore = 27850, CurrentRank = 4, ImageUrl = "/images/placeholder.svg", CategoryId = 4 },
new { Id = 20, Name = "NVIDIA GeForce RTX 3090", Model = "RTX 3090", Manufacturer = "NVIDIA", ReleaseYear = 2020, PerformanceScore = 26420, CurrentRank = 5, ImageUrl = "/images/placeholder.svg", CategoryId = 4 }
};
// 根据类别ID筛选
var filteredProducts = categoryId.HasValue
? allProducts.Where(p => p.CategoryId == categoryId.Value).ToArray()
: allProducts;
// 排序
filteredProducts = sortBy.ToLower() switch
{
"name" => order.ToLower() == "desc"
? filteredProducts.OrderByDescending(p => p.Name).ToArray()
: filteredProducts.OrderBy(p => p.Name).ToArray(),
"manufacturer" => order.ToLower() == "desc"
? filteredProducts.OrderByDescending(p => p.Manufacturer).ToArray()
: filteredProducts.OrderBy(p => p.Manufacturer).ToArray(),
"releaseyear" => order.ToLower() == "desc"
? filteredProducts.OrderByDescending(p => p.ReleaseYear).ToArray()
: filteredProducts.OrderBy(p => p.ReleaseYear).ToArray(),
_ => order.ToLower() == "desc"
? filteredProducts.OrderByDescending(p => p.PerformanceScore).ToArray()
: filteredProducts.OrderBy(p => p.PerformanceScore).ToArray()
};
var totalItems = filteredProducts.Length;
var totalPages = (int)Math.Ceiling((double)totalItems / pageSize);
// 分页
var pagedProducts = filteredProducts
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToArray();
return new {
Items = pagedProducts,
Total = totalItems,
CurrentPage = page,
PageSize = pageSize,
TotalPages = totalPages
};
});
// 添加产品详情API端点
app.MapGet("/api/products/{id}", (int id) =>
{
// 模拟产品详情数据
var allProducts = new[]
{
new {
Id = 1,
Name = "Snapdragon 8 Gen 2",
Model = "SM8550-AB",
Manufacturer = "Qualcomm",
ReleaseYear = 2022,
PerformanceScore = 3879,
CurrentRank = 1,
ImageUrl = "/images/placeholder.svg",
CategoryId = 1,
Description = "高通骁龙8 Gen 2是2022年推出的旗舰移动处理器采用4nm工艺制程。",
Specifications = new[] {
new { Name = "工艺", Value = "4nm" },
new { Name = "核心数", Value = "8" },
new { Name = "主频", Value = "3.2GHz" },
new { Name = "GPU", Value = "Adreno 740" }
},
PerformanceScores = new[] {
new { Benchmark = "Geekbench 5 Single-Core", Score = 1524 },
new { Benchmark = "Geekbench 5 Multi-Core", Score = 5352 },
new { Benchmark = "AnTuTu 9", Score = 1314582 }
}
},
new {
Id = 2,
Name = "A16 Bionic",
Model = "A16 Bionic",
Manufacturer = "Apple",
ReleaseYear = 2022,
PerformanceScore = 3756,
CurrentRank = 2,
ImageUrl = "/images/placeholder.svg",
CategoryId = 1,
Description = "苹果A16 Bionic是2022年推出的移动处理器采用4nm工艺制程。",
Specifications = new[] {
new { Name = "工艺", Value = "4nm" },
new { Name = "核心数", Value = "6" },
new { Name = "主频", Value = "3.46GHz" },
new { Name = "GPU", Value = "Apple A16 GPU" }
},
PerformanceScores = new[] {
new { Benchmark = "Geekbench 5 Single-Core", Score = 1874 },
new { Benchmark = "Geekbench 5 Multi-Core", Score = 5372 },
new { Benchmark = "AnTuTu 9", Score = 1425876 }
}
},
new {
Id = 3,
Name = "Dimensity 9200",
Model = "MT6985",
Manufacturer = "MediaTek",
ReleaseYear = 2022,
PerformanceScore = 3512,
CurrentRank = 3,
ImageUrl = "/images/placeholder.svg",
CategoryId = 1,
Description = "联发科天玑9200是2022年推出的旗舰移动处理器采用4nm工艺制程。",
Specifications = new[] {
new { Name = "工艺", Value = "4nm" },
new { Name = "核心数", Value = "8" },
new { Name = "主频", Value = "3.05GHz" },
new { Name = "GPU", Value = "Mali-G715 MC10" }
},
PerformanceScores = new[] {
new { Benchmark = "Geekbench 5 Single-Core", Score = 1424 },
new { Benchmark = "Geekbench 5 Multi-Core", Score = 4485 },
new { Benchmark = "AnTuTu 9", Score = 1245876 }
}
},
new {
Id = 4,
Name = "Snapdragon 8+ Gen 1",
Model = "SM8475",
Manufacturer = "Qualcomm",
ReleaseYear = 2022,
PerformanceScore = 3341,
CurrentRank = 4,
ImageUrl = "/images/placeholder.svg",
CategoryId = 1,
Description = "高通骁龙8+ Gen 1是2022年推出的旗舰移动处理器采用4nm工艺制程。",
Specifications = new[] {
new { Name = "工艺", Value = "4nm" },
new { Name = "核心数", Value = "8" },
new { Name = "主频", Value = "3.2GHz" },
new { Name = "GPU", Value = "Adreno 730" }
},
PerformanceScores = new[] {
new { Benchmark = "Geekbench 5 Single-Core", Score = 1314 },
new { Benchmark = "Geekbench 5 Multi-Core", Score = 4185 },
new { Benchmark = "AnTuTu 9", Score = 1145876 }
}
}
};
var product = allProducts.FirstOrDefault(p => p.Id == id);
if (product == null)
{
return Results.NotFound(new { Message = "产品不存在" });
}
return Results.Ok(product);
});
// 添加产品搜索API端点
app.MapGet("/api/products/search", (string q, int? categoryId) =>
{
// 模拟产品数据
var allProducts = new[]
{
new { Id = 1, Name = "Snapdragon 8 Gen 2", Model = "SM8550-AB", Manufacturer = "Qualcomm", ReleaseYear = 2022, PerformanceScore = 3879, CurrentRank = 1, ImageUrl = "/images/placeholder.svg", CategoryId = 1 },
new { Id = 2, Name = "A16 Bionic", Model = "A16 Bionic", Manufacturer = "Apple", ReleaseYear = 2022, PerformanceScore = 3756, CurrentRank = 2, ImageUrl = "/images/placeholder.svg", CategoryId = 1 },
new { Id = 3, Name = "Dimensity 9200", Model = "MT6985", Manufacturer = "MediaTek", ReleaseYear = 2022, PerformanceScore = 3512, CurrentRank = 3, ImageUrl = "/images/placeholder.svg", CategoryId = 1 },
new { Id = 4, Name = "Snapdragon 8+ Gen 1", Model = "SM8475", Manufacturer = "Qualcomm", ReleaseYear = 2022, PerformanceScore = 3341, CurrentRank = 4, ImageUrl = "/images/placeholder.svg", CategoryId = 1 },
new { Id = 5, Name = "A15 Bionic", Model = "A15 Bionic", Manufacturer = "Apple", ReleaseYear = 2021, PerformanceScore = 3215, CurrentRank = 5, ImageUrl = "/images/placeholder.svg", CategoryId = 1 },
new { Id = 6, Name = "Adreno 740", Model = "Adreno 740", Manufacturer = "Qualcomm", ReleaseYear = 2022, PerformanceScore = 14350, CurrentRank = 1, ImageUrl = "/images/placeholder.svg", CategoryId = 2 },
new { Id = 7, Name = "Apple A16 GPU", Model = "A16 GPU", Manufacturer = "Apple", ReleaseYear = 2022, PerformanceScore = 13980, CurrentRank = 2, ImageUrl = "/images/placeholder.svg", CategoryId = 2 },
new { Id = 8, Name = "Mali-G715 MC10", Model = "Mali-G715", Manufacturer = "ARM", ReleaseYear = 2022, PerformanceScore = 12150, CurrentRank = 3, ImageUrl = "/images/placeholder.svg", CategoryId = 2 },
new { Id = 9, Name = "Adreno 730", Model = "Adreno 730", Manufacturer = "Qualcomm", ReleaseYear = 2022, PerformanceScore = 11020, CurrentRank = 4, ImageUrl = "/images/placeholder.svg", CategoryId = 2 },
new { Id = 10, Name = "Apple A15 GPU", Model = "A15 GPU", Manufacturer = "Apple", ReleaseYear = 2021, PerformanceScore = 10560, CurrentRank = 5, ImageUrl = "/images/placeholder.svg", CategoryId = 2 },
new { Id = 11, Name = "Intel Core i9-13900K", Model = "i9-13900K", Manufacturer = "Intel", ReleaseYear = 2022, PerformanceScore = 3176, CurrentRank = 1, ImageUrl = "/images/placeholder.svg", CategoryId = 3 },
new { Id = 12, Name = "AMD Ryzen 9 7950X", Model = "Ryzen 9 7950X", Manufacturer = "AMD", ReleaseYear = 2022, PerformanceScore = 3095, CurrentRank = 2, ImageUrl = "/images/placeholder.svg", CategoryId = 3 },
new { Id = 13, Name = "Intel Core i7-13700K", Model = "i7-13700K", Manufacturer = "Intel", ReleaseYear = 2022, PerformanceScore = 2956, CurrentRank = 3, ImageUrl = "/images/placeholder.svg", CategoryId = 3 },
new { Id = 14, Name = "AMD Ryzen 9 5900X", Model = "Ryzen 9 5900X", Manufacturer = "AMD", ReleaseYear = 2020, PerformanceScore = 2835, CurrentRank = 4, ImageUrl = "/images/placeholder.svg", CategoryId = 3 },
new { Id = 15, Name = "Intel Core i5-13600K", Model = "i5-13600K", Manufacturer = "Intel", ReleaseYear = 2022, PerformanceScore = 2742, CurrentRank = 5, ImageUrl = "/images/placeholder.svg", CategoryId = 3 },
new { Id = 16, Name = "NVIDIA GeForce RTX 4090", Model = "RTX 4090", Manufacturer = "NVIDIA", ReleaseYear = 2022, PerformanceScore = 38928, CurrentRank = 1, ImageUrl = "/images/placeholder.svg", CategoryId = 4 },
new { Id = 17, Name = "NVIDIA GeForce RTX 4080", Model = "RTX 4080", Manufacturer = "NVIDIA", ReleaseYear = 2022, PerformanceScore = 32168, CurrentRank = 2, ImageUrl = "/images/placeholder.svg", CategoryId = 4 },
new { Id = 18, Name = "NVIDIA GeForce RTX 3090 Ti", Model = "RTX 3090 Ti", Manufacturer = "NVIDIA", ReleaseYear = 2022, PerformanceScore = 28595, CurrentRank = 3, ImageUrl = "/images/placeholder.svg", CategoryId = 4 },
new { Id = 19, Name = "AMD Radeon RX 7900 XTX", Model = "RX 7900 XTX", Manufacturer = "AMD", ReleaseYear = 2022, PerformanceScore = 27850, CurrentRank = 4, ImageUrl = "/images/placeholder.svg", CategoryId = 4 },
new { Id = 20, Name = "NVIDIA GeForce RTX 3090", Model = "RTX 3090", Manufacturer = "NVIDIA", ReleaseYear = 2020, PerformanceScore = 26420, CurrentRank = 5, ImageUrl = "/images/placeholder.svg", CategoryId = 4 }
};
// 根据类别ID筛选
var filteredProducts = categoryId.HasValue
? allProducts.Where(p => p.CategoryId == categoryId.Value).ToArray()
: allProducts;
// 根据搜索关键词筛选
if (!string.IsNullOrWhiteSpace(q))
{
filteredProducts = filteredProducts.Where(p =>
p.Name.Contains(q, StringComparison.OrdinalIgnoreCase) ||
p.Model.Contains(q, StringComparison.OrdinalIgnoreCase) ||
p.Manufacturer.Contains(q, StringComparison.OrdinalIgnoreCase)
).ToArray();
}
return filteredProducts;
});
// 添加产品对比API端点
app.MapPost("/api/comparison", async (HttpRequest request) =>
{
try
{
// 读取请求体
var requestBody = await new StreamReader(request.Body).ReadToEndAsync();
// 尝试解析为JSON对象
dynamic jsonBody = JsonSerializer.Deserialize<Dictionary<string, object>>(requestBody);
// 提取productIds数组
if (jsonBody == null || !jsonBody.ContainsKey("productIds"))
{
return Results.BadRequest(new { Message = "请求体必须包含productIds字段" });
}
// 获取productIds数组
var productIdsJson = jsonBody["productIds"].ToString();
var productIds = JsonSerializer.Deserialize<int[]>(productIdsJson);
// 验证产品ID数量
if (productIds == null || productIds.Length < 2 || productIds.Length > 4)
{
return Results.BadRequest(new { Message = "产品对比需要2-4个产品ID" });
}
// 模拟产品详情数据
var allProducts = new[]
{
new {
Id = 1,
Name = "Snapdragon 8 Gen 2",
Model = "SM8550-AB",
Manufacturer = "Qualcomm",
ReleaseYear = 2022,
PerformanceScore = 3879,
CurrentRank = 1,
ImageUrl = "/images/placeholder.svg",
CategoryId = 1,
Description = "高通骁龙8 Gen 2是2022年推出的旗舰移动处理器采用4nm工艺制程。",
Specifications = new[] {
new { Name = "工艺", Value = "4nm" },
new { Name = "核心数", Value = "8" },
new { Name = "主频", Value = "3.2GHz" },
new { Name = "GPU", Value = "Adreno 740" }
},
PerformanceScores = new[] {
new { Benchmark = "Geekbench 5 Single-Core", Score = 1524 },
new { Benchmark = "Geekbench 5 Multi-Core", Score = 5352 },
new { Benchmark = "AnTuTu 9", Score = 1314582 }
}
},
new {
Id = 2,
Name = "A16 Bionic",
Model = "A16 Bionic",
Manufacturer = "Apple",
ReleaseYear = 2022,
PerformanceScore = 3756,
CurrentRank = 2,
ImageUrl = "/images/placeholder.svg",
CategoryId = 1,
Description = "苹果A16 Bionic是2022年推出的移动处理器采用4nm工艺制程。",
Specifications = new[] {
new { Name = "工艺", Value = "4nm" },
new { Name = "核心数", Value = "6" },
new { Name = "主频", Value = "3.46GHz" },
new { Name = "GPU", Value = "Apple A16 GPU" }
},
PerformanceScores = new[] {
new { Benchmark = "Geekbench 5 Single-Core", Score = 1874 },
new { Benchmark = "Geekbench 5 Multi-Core", Score = 5372 },
new { Benchmark = "AnTuTu 9", Score = 1425876 }
}
},
new {
Id = 3,
Name = "Dimensity 9200",
Model = "MT6985",
Manufacturer = "MediaTek",
ReleaseYear = 2022,
PerformanceScore = 3512,
CurrentRank = 3,
ImageUrl = "/images/placeholder.svg",
CategoryId = 1,
Description = "联发科天玑9200是2022年推出的旗舰移动处理器采用4nm工艺制程。",
Specifications = new[] {
new { Name = "工艺", Value = "4nm" },
new { Name = "核心数", Value = "8" },
new { Name = "主频", Value = "3.05GHz" },
new { Name = "GPU", Value = "Mali-G715 MC10" }
},
PerformanceScores = new[] {
new { Benchmark = "Geekbench 5 Single-Core", Score = 1424 },
new { Benchmark = "Geekbench 5 Multi-Core", Score = 4485 },
new { Benchmark = "AnTuTu 9", Score = 1245876 }
}
},
new {
Id = 4,
Name = "Snapdragon 8+ Gen 1",
Model = "SM8475",
Manufacturer = "Qualcomm",
ReleaseYear = 2022,
PerformanceScore = 3341,
CurrentRank = 4,
ImageUrl = "/images/placeholder.svg",
CategoryId = 1,
Description = "高通骁龙8+ Gen 1是2022年推出的旗舰移动处理器采用4nm工艺制程。",
Specifications = new[] {
new { Name = "工艺", Value = "4nm" },
new { Name = "核心数", Value = "8" },
new { Name = "主频", Value = "3.2GHz" },
new { Name = "GPU", Value = "Adreno 730" }
},
PerformanceScores = new[] {
new { Benchmark = "Geekbench 5 Single-Core", Score = 1314 },
new { Benchmark = "Geekbench 5 Multi-Core", Score = 4185 },
new { Benchmark = "AnTuTu 9", Score = 1145876 }
}
}
};
// 获取请求的产品
var selectedProducts = allProducts.Where(p => productIds.Contains(p.Id)).ToArray();
// 检查是否找到了所有请求的产品
if (selectedProducts.Length != productIds.Length)
{
return Results.NotFound(new { Message = "一个或多个产品不存在" });
}
// 创建对比数据
var comparisonData = new {
Products = selectedProducts,
ComparisonMatrix = new {
PerformanceScore = selectedProducts.Select(p => new {
ProductId = p.Id,
ProductName = p.Name,
Value = p.PerformanceScore,
IsBest = p.PerformanceScore == selectedProducts.Max(x => x.PerformanceScore),
IsWorst = p.PerformanceScore == selectedProducts.Min(x => x.PerformanceScore)
}).ToArray(),
ReleaseYear = selectedProducts.Select(p => new {
ProductId = p.Id,
ProductName = p.Name,
Value = p.ReleaseYear,
IsBest = p.ReleaseYear == selectedProducts.Max(x => x.ReleaseYear),
IsWorst = p.ReleaseYear == selectedProducts.Min(x => x.ReleaseYear)
}).ToArray()
},
PerformanceComparison = new {
Benchmarks = new[] {
new {
Name = "Geekbench 5 Single-Core",
Values = selectedProducts.Select(p => new {
ProductId = p.Id,
ProductName = p.Name,
Value = p.PerformanceScores.FirstOrDefault(s => s.Benchmark == "Geekbench 5 Single-Core")?.Score ?? 0,
IsBest = p.PerformanceScores.FirstOrDefault(s => s.Benchmark == "Geekbench 5 Single-Core")?.Score == selectedProducts.Max(x => x.PerformanceScores.FirstOrDefault(s => s.Benchmark == "Geekbench 5 Single-Core")?.Score ?? 0),
IsWorst = p.PerformanceScores.FirstOrDefault(s => s.Benchmark == "Geekbench 5 Single-Core")?.Score == selectedProducts.Min(x => x.PerformanceScores.FirstOrDefault(s => s.Benchmark == "Geekbench 5 Single-Core")?.Score ?? int.MaxValue)
}).ToArray()
},
new {
Name = "Geekbench 5 Multi-Core",
Values = selectedProducts.Select(p => new {
ProductId = p.Id,
ProductName = p.Name,
Value = p.PerformanceScores.FirstOrDefault(s => s.Benchmark == "Geekbench 5 Multi-Core")?.Score ?? 0,
IsBest = p.PerformanceScores.FirstOrDefault(s => s.Benchmark == "Geekbench 5 Multi-Core")?.Score == selectedProducts.Max(x => x.PerformanceScores.FirstOrDefault(s => s.Benchmark == "Geekbench 5 Multi-Core")?.Score ?? 0),
IsWorst = p.PerformanceScores.FirstOrDefault(s => s.Benchmark == "Geekbench 5 Multi-Core")?.Score == selectedProducts.Min(x => x.PerformanceScores.FirstOrDefault(s => s.Benchmark == "Geekbench 5 Multi-Core")?.Score ?? int.MaxValue)
}).ToArray()
},
new {
Name = "AnTuTu 9",
Values = selectedProducts.Select(p => new {
ProductId = p.Id,
ProductName = p.Name,
Value = p.PerformanceScores.FirstOrDefault(s => s.Benchmark == "AnTuTu 9")?.Score ?? 0,
IsBest = p.PerformanceScores.FirstOrDefault(s => s.Benchmark == "AnTuTu 9")?.Score == selectedProducts.Max(x => x.PerformanceScores.FirstOrDefault(s => s.Benchmark == "AnTuTu 9")?.Score ?? 0),
IsWorst = p.PerformanceScores.FirstOrDefault(s => s.Benchmark == "AnTuTu 9")?.Score == selectedProducts.Min(x => x.PerformanceScores.FirstOrDefault(s => s.Benchmark == "AnTuTu 9")?.Score ?? int.MaxValue)
}).ToArray()
}
}
}
};
return Results.Ok(comparisonData);
}
catch (Exception ex)
{
return Results.BadRequest(new { Message = $"处理请求时出错: {ex.Message}" });
}
});
app.MapGet("/", () => "Hello World!");
app.Run();

View File

@@ -0,0 +1,23 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5034",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7100;http://localhost:5034",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

9
TestAPI/TestAPI.csproj Normal file
View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

9
TestAPI/appsettings.json Normal file
View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@@ -0,0 +1,23 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v9.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v9.0": {
"TestAPI/1.0.0": {
"runtime": {
"TestAPI.dll": {}
}
}
}
},
"libraries": {
"TestAPI/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,19 @@
{
"runtimeOptions": {
"tfm": "net9.0",
"frameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "9.0.0"
},
{
"name": "Microsoft.AspNetCore.App",
"version": "9.0.0"
}
],
"configProperties": {
"System.GC.Server": true,
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
}
}
}

View File

@@ -0,0 +1 @@
{"Version":1,"ManifestType":"Build","Endpoints":[]}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v9.0", FrameworkDisplayName = ".NET 9.0")]

View File

@@ -0,0 +1,22 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("TestAPI")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
[assembly: System.Reflection.AssemblyProductAttribute("TestAPI")]
[assembly: System.Reflection.AssemblyTitleAttribute("TestAPI")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// 由 MSBuild WriteCodeFragment 类生成。

View File

@@ -0,0 +1 @@
ccdd8558c5bd69980236f6ef7435019aea79860a1fdbecb265564d3b99885b5a

View File

@@ -0,0 +1,21 @@
is_global = true
build_property.TargetFramework = net9.0
build_property.TargetPlatformMinVersion =
build_property.UsingMicrosoftNETSdkWeb = true
build_property.ProjectTypeGuids =
build_property.InvariantGlobalization =
build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = TestAPI
build_property.RootNamespace = TestAPI
build_property.ProjectDir = C:\work\电脑硬件-01\TestAPI\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.RazorLangVersion = 9.0
build_property.SupportLocalizedComponentNames =
build_property.GenerateRazorMetadataSourceChecksumAttributes =
build_property.MSBuildProjectDirectory = C:\work\电脑硬件-01\TestAPI
build_property._RazorSourceGeneratorDebug =
build_property.EffectiveAnalysisLevelStyle = 9.0
build_property.EnableCodeStyleSeverity =

View File

@@ -0,0 +1,17 @@
// <auto-generated/>
global using global::Microsoft.AspNetCore.Builder;
global using global::Microsoft.AspNetCore.Hosting;
global using global::Microsoft.AspNetCore.Http;
global using global::Microsoft.AspNetCore.Routing;
global using global::Microsoft.Extensions.Configuration;
global using global::Microsoft.Extensions.DependencyInjection;
global using global::Microsoft.Extensions.Hosting;
global using global::Microsoft.Extensions.Logging;
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Net.Http.Json;
global using global::System.Threading;
global using global::System.Threading.Tasks;

Binary file not shown.

View File

@@ -0,0 +1 @@
629dc1ba4c6ea1c70ecea85384c28652dcd9f627da004e854aea08234c082e60

View File

@@ -0,0 +1,27 @@
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\rpswa.dswa.cache.json
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\TestAPI.GeneratedMSBuildEditorConfig.editorconfig
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\TestAPI.AssemblyInfoInputs.cache
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\TestAPI.AssemblyInfo.cs
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\TestAPI.csproj.CoreCompileInputs.cache
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\TestAPI.MvcApplicationPartsAssemblyInfo.cache
C:\work\电脑硬件-01\TestAPI\bin\Debug\net9.0\appsettings.Development.json
C:\work\电脑硬件-01\TestAPI\bin\Debug\net9.0\appsettings.json
C:\work\电脑硬件-01\TestAPI\bin\Debug\net9.0\TestAPI.staticwebassets.endpoints.json
C:\work\电脑硬件-01\TestAPI\bin\Debug\net9.0\TestAPI.exe
C:\work\电脑硬件-01\TestAPI\bin\Debug\net9.0\TestAPI.deps.json
C:\work\电脑硬件-01\TestAPI\bin\Debug\net9.0\TestAPI.runtimeconfig.json
C:\work\电脑硬件-01\TestAPI\bin\Debug\net9.0\TestAPI.dll
C:\work\电脑硬件-01\TestAPI\bin\Debug\net9.0\TestAPI.pdb
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\rjimswa.dswa.cache.json
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\rjsmrazor.dswa.cache.json
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\rjsmcshtml.dswa.cache.json
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\scopedcss\bundle\TestAPI.styles.css
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\staticwebassets.build.json
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\staticwebassets.build.json.cache
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\staticwebassets.development.json
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\staticwebassets.build.endpoints.json
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\TestAPI.dll
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\refint\TestAPI.dll
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\TestAPI.pdb
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\TestAPI.genruntimeconfig.cache
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\ref\TestAPI.dll

Some files were not shown because too many files have changed in this diff Show More