# test_scenario 实体 > 所属模块:[数据模型概览](../概览.md) --- ## 1. 表定义 **表名**:`test_scenario` **描述**:存储测试场景,通过外键关联各维度表,test_purpose 直接存储 --- ## 2. 字段定义 | 列名 | 类型 | 约束 | 说明 | |------|------|------|------| | id | UUID | PK, NOT NULL | 主键,自动生成 | | tx_panel_id | UUID | FK, NOT NULL | 关联TX面板维度 | | tx_hardware_id | UUID | FK, NOT NULL | 关联TX硬件版本维度 | | tx_software_id | UUID | FK, NOT NULL | 关联TX软件版本维度 | | rx_type_id | UUID | FK, NOT NULL | 关联RX类型维度 | | test_purpose | VARCHAR(200) | NULL | 测试目的(直接存储) | | test_date | DATE | NOT NULL | 测试日期 | | test_sequence | INT | NOT NULL, DEFAULT 1 | 测试次数序号 | | qfod_count | INT | NOT NULL, DEFAULT 0 | 包含的 Qfod 记录数 | | ploss_count | INT | NOT NULL, DEFAULT 0 | 包含的 Ploss 记录数 | | is_deleted | BOOLEAN | NOT NULL, DEFAULT FALSE | 软删除标记 | | created_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | 入库时间 | --- ## 3. 外键 | 外键 | 引用表 | 说明 | |------|--------|------| | tx_panel_id | tx_panel.id | 关联TX面板维度(必需) | | tx_hardware_id | tx_hardware.id | 关联TX硬件版本维度(必需) | | tx_software_id | tx_software.id | 关联TX软件版本维度(必需) | | rx_type_id | rx_type.id | 关联RX类型维度(必需) | --- ## 4. 索引 | 索引名 | 字段 | 用途 | |--------|------|------| | idx_scenario_tx_panel | tx_panel_id | 按面板类型筛选 | | idx_scenario_tx_hardware | tx_hardware_id | 按硬件版本筛选 | | idx_scenario_tx_software | tx_software_id | 按软件版本筛选 | | idx_scenario_rx_type | rx_type_id | 按RX类型筛选 | | idx_scenario_test_date | test_date | 按测试日期查询 | --- ## 5. 唯一约束 | 约束名 | 字段组合 | 说明 | |--------|----------|------| | uq_scenario | (tx_panel_id, tx_hardware_id, tx_software_id, rx_type_id, test_purpose, test_date, test_sequence) | 相同属性组合不重复创建 | --- ## 6. EF Core 模型 ```csharp // Models/TestScenario.cs using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace WCTDataMiner.Models; [Table("test_scenario")] [Index(nameof(TxPanelId), Name = "idx_scenario_tx_panel")] [Index(nameof(TxHardwareId), Name = "idx_scenario_tx_hardware")] [Index(nameof(TxSoftwareId), Name = "idx_scenario_tx_software")] [Index(nameof(RxTypeId), Name = "idx_scenario_rx_type")] [Index(nameof(TestDate), Name = "idx_scenario_test_date")] public class TestScenario { [Key] [Column("id")] public Guid Id { get; set; } = Guid.NewGuid(); [Required] [Column("tx_panel_id")] public Guid TxPanelId { get; set; } [Required] [Column("tx_hardware_id")] public Guid TxHardwareId { get; set; } [Required] [Column("tx_software_id")] public Guid TxSoftwareId { get; set; } [Required] [Column("rx_type_id")] public Guid RxTypeId { get; set; } [MaxLength(200)] [Column("test_purpose")] public string? TestPurpose { get; set; } [Required] [Column("test_date")] public DateOnly TestDate { get; set; } [Required] [Column("test_sequence")] public int TestSequence { get; set; } = 1; [Required] [Column("qfod_count")] public int QfodCount { get; set; } = 0; [Required] [Column("ploss_count")] public int PlossCount { get; set; } = 0; [Required] [Column("is_deleted")] public bool IsDeleted { get; set; } = false; [Required] [Column("created_at")] public DateTime CreatedAt { get; set; } = DateTime.UtcNow; // Navigation Properties - 维度表 [ForeignKey(nameof(TxPanelId))] public TxPanel TxPanel { get; set; } = null!; [ForeignKey(nameof(TxHardwareId))] public TxHardware TxHardware { get; set; } = null!; [ForeignKey(nameof(TxSoftwareId))] public TxSoftware TxSoftware { get; set; } = null!; [ForeignKey(nameof(RxTypeId))] public RxType RxType { get; set; } = null!; // Navigation Properties - 数据记录 public ICollection QfodRecords { get; set; } = []; public ICollection PlossRecords { get; set; } = []; } ``` --- ## 7. 场景创建流程 ### 从文件名解析 ```csharp // Services/ScenarioService.cs public async Task GetOrCreateFromFileNameAsync(string fileName) { // 1. 解析文件名 var parts = ParseFileName(fileName); // 格式: TX面板-TX硬件-TX软件-RX类型-测试目的-年月日-测试次数.log // 2. 获取或创建各维度 var txPanel = await txPanelService.GetOrCreateAsync(parts.TxPanel); var txHardware = await txHardwareService.GetOrCreateAsync(parts.TxHardware); var txSoftware = await txSoftwareService.GetOrCreateAsync(parts.TxSoftware); var rxType = await rxTypeService.GetOrCreateAsync(parts.RxType); // 3. 检查场景是否已存在 var existing = await context.TestScenarios .FirstOrDefaultAsync(s => s.TxPanelId == txPanel.Id && s.TxHardwareId == txHardware.Id && s.TxSoftwareId == txSoftware.Id && s.RxTypeId == rxType.Id && s.TestPurpose == parts.TestPurpose && s.TestDate == parts.TestDate && s.TestSequence == parts.TestSequence); if (existing != null) return existing; // 4. 创建新场景 var scenario = new TestScenario { TxPanelId = txPanel.Id, TxHardwareId = txHardware.Id, TxSoftwareId = txSoftware.Id, RxTypeId = rxType.Id, TestPurpose = parts.TestPurpose, TestDate = parts.TestDate, TestSequence = parts.TestSequence }; context.TestScenarios.Add(scenario); await context.SaveChangesAsync(); return scenario; } ``` ### 手动创建 ```csharp public async Task CreateAsync(CreateScenarioRequest request) { // 1. 验证维度是否存在 var txPanel = await context.TxPanels.FindAsync(request.TxPanelId) ?? throw new NotFoundException("TX面板不存在"); var txHardware = await context.TxHardwares.FindAsync(request.TxHardwareId) ?? throw new NotFoundException("TX硬件版本不存在"); // ... 其他维度验证 // 2. 创建场景 var scenario = new TestScenario { TxPanelId = request.TxPanelId, TxHardwareId = request.TxHardwareId, TxSoftwareId = request.TxSoftwareId, RxTypeId = request.RxTypeId, TestPurpose = request.TestPurpose, TestDate = request.TestDate, TestSequence = request.TestSequence }; context.TestScenarios.Add(scenario); await context.SaveChangesAsync(); return scenario; } ```