Files
WCTDataMiner/docs/data-model/entities/测试场景.md
ssss 28cb2533a7 docs: 完善数据模型设计
- 重命名 log_file 为 test_scenario(测试场景)
- 拆分维度表:tx_panel、tx_hardware、tx_software、rx_type
- 采用星型模型设计,消除数据冗余
- 为所有表添加软删除字段 is_deleted
- 更新关系说明,包含全局查询过滤器配置
- 更新 ER 图和查询示例

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 15:53:30 +08:00

6.7 KiB
Raw Blame History

test_scenario 实体

所属模块:数据模型概览


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 模型

// 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<QfodRecord> QfodRecords { get; set; } = [];
    public ICollection<PlossRecord> PlossRecords { get; set; } = [];
}

7. 场景创建流程

从文件名解析

// Services/ScenarioService.cs
public async Task<TestScenario> 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;
}

手动创建

public async Task<TestScenario> 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;
}