主表字段
|
测试时间(年、月、日),测试时间(时、分、秒),测试结果(固定为三种:0:0:1;0:测试为OK,0;0;0:1:测试为Fail,T面(分Top(上)面/Bottom(下)面),测试点数,实际不良,设备型号,批次工单,机种名,条码,SMT组别,线别
|
|
子表字段
|
偏位,缺件,反向,翘起,浮高,立碑,翻转,错件,翘脚,虚焊,空焊,少锡,多锡,连锡,漏铜,拉尖,异物,溢胶,引脚偏位,生产线名称,机器名称,投入板数,OK板数,通过板数,合格率,不良板数,不良率,不良率(PPM),不良点数,实测点数,待测点数
|
|
执行目标
|
- 将 `MES.Service/service/QC/SPIAOI.txt`:1-7 中的 SPI/AOI 检测字段转化为一个可调用的 REST API,并补充数据库建表与持久化逻辑。
|
- 目标覆盖请求契约、数据校验、主从表建模、存储过程/服务保存流程以及异常与日志处理。
|
|
数据结构拆解
|
- `主表字段` → 记录单次 SPI/AOI 检测的头信息。建议字段/类型:
|
测试日期(`TestDate`/DATE)、测试时间(`TestTime`/VARCHAR2(8 CHAR))、测试结果(`TestResult`/VARCHAR2(12 CHAR),值域:`0:0:1;0`、`0;0;0:1`、`Fail`)、面别(`Surface`/CHAR(1),值域 `T`/`B`)、测试点数(`TotalPoints`/NUMBER(6))、实际不良点数(`ActualDefects`/NUMBER(6))、设备型号(`EquipmentModel`/VARCHAR2(64 CHAR))、批次工单(`WorkOrder`/VARCHAR2(64 CHAR))、机种名(`ProductModel`/VARCHAR2(64 CHAR))、条码(`BoardBarcode`/VARCHAR2(128 CHAR))、SMT 组别(`SmtGroup`/VARCHAR2(32 CHAR))、线别(`LineName`/VARCHAR2(32 CHAR))。
|
- `子表字段` → 记录单次检测的缺陷统计。建议字段/类型:
|
偏位(`OffsetCount`/NUMBER(6))、缺件(`MissingCount`/NUMBER(6))、反向(`ReverseCount`/NUMBER(6))、翘起(`LiftedCount`/NUMBER(6))、浮高(`FloatHighCount`/NUMBER(6))、立碑(`TombstoneCount`/NUMBER(6))、翻转(`FlipCount`/NUMBER(6))、错件(`WrongPartCount`/NUMBER(6))、翘脚(`LeadLiftCount`/NUMBER(6))、虚焊(`ColdJointCount`/NUMBER(6))、空焊(`NoSolderCount`/NUMBER(6))、少锡(`InsufficientSolderCount`/NUMBER(6))、多锡(`ExcessSolderCount`/NUMBER(6))、连锡(`BridgeCount`/NUMBER(6))、漏铜(`CopperExposureCount`/NUMBER(6))、拉尖(`SpikeCount`/NUMBER(6))、异物(`ForeignMatterCount`/NUMBER(6))、溢胶(`GlueOverflowCount`/NUMBER(6))、引脚偏位(`PinOffsetCount`/NUMBER(6))、生产线名称(`LineDisplayName`/VARCHAR2(64 CHAR))、机器名称(`MachineName`/VARCHAR2(64 CHAR))、投入板数(`InputBoards`/NUMBER(6))、OK板数(`OkBoards`/NUMBER(6))、通过板数(`PassBoards`/NUMBER(6))、合格率(`PassRate`/NUMBER(5,2))、不良板数(`DefectBoards`/NUMBER(6))、不良率(`DefectRate`/NUMBER(5,2))、不良率PPM(`DefectPpm`/NUMBER(9))、不良点数(`DefectPoints`/NUMBER(6))、实测点数(`MeasuredPoints`/NUMBER(6))、待测点数(`PendingPoints`/NUMBER(6))。所有“率”字段以百分比存储,入库前转为数值型。
|
|
数据库设计
|
- 目标库为 Oracle 11g(11.2),本项目使用 `SqlSugar` 并通过 `[SugarColumn(OracleSequenceName = "...")]` 自动取号,因此只需声明主键和序列,无需触发器。示例 DDL:
|
```
|
-- 主表:记录每块电路板的 SPI/AOI 批次检测摘要数据
|
-- SPI/AOI 主表,记录单板批次检测摘要
|
CREATE TABLE MES_SPI_AOI_HEADER (
|
ID NUMBER(19) NOT NULL,
|
TEST_DATE DATE NOT NULL,
|
TEST_TIME VARCHAR2(8 CHAR) NOT NULL,
|
TEST_RESULT VARCHAR2(12 CHAR) NOT NULL,
|
SURFACE CHAR(1) NOT NULL CHECK (SURFACE IN ('T','B')),
|
TOTAL_POINTS NUMBER(6),
|
ACTUAL_DEFECTS NUMBER(6),
|
EQUIPMENT_MODEL VARCHAR2(64 CHAR),
|
WORK_ORDER VARCHAR2(64 CHAR),
|
PRODUCT_MODEL VARCHAR2(64 CHAR),
|
BOARD_BARCODE VARCHAR2(128 CHAR) NOT NULL,
|
SMT_GROUP VARCHAR2(32 CHAR),
|
LINE_NAME VARCHAR2(32 CHAR),
|
CREATED_AT TIMESTAMP DEFAULT SYSTIMESTAMP,
|
UPDATED_AT TIMESTAMP DEFAULT SYSTIMESTAMP
|
);
|
ALTER TABLE MES_SPI_AOI_HEADER
|
ADD CONSTRAINT PK_SPI_AOI_HEADER PRIMARY KEY (ID);
|
CREATE UNIQUE INDEX IDX_SPI_AOI_BARCODE ON MES_SPI_AOI_HEADER (BOARD_BARCODE);
|
|
COMMENT ON TABLE MES_SPI_AOI_HEADER IS 'SPI/AOI 主表,记录单板检测摘要数据';
|
COMMENT ON COLUMN MES_SPI_AOI_HEADER.ID IS '主键ID,由序列 SEQ_SPI_AOI_HEADER 生成';
|
COMMENT ON COLUMN MES_SPI_AOI_HEADER.TEST_DATE IS '测试日期,沿用设备上传的年月日';
|
COMMENT ON COLUMN MES_SPI_AOI_HEADER.TEST_TIME IS '测试时间,沿用设备上传的 HH:mm:ss';
|
COMMENT ON COLUMN MES_SPI_AOI_HEADER.TEST_RESULT IS '测试结果原始字符串,例如 0:0:1;0、0;0;0:1、Fail';
|
COMMENT ON COLUMN MES_SPI_AOI_HEADER.SURFACE IS '检测面,T=Top 顶面,B=Bottom 底面';
|
COMMENT ON COLUMN MES_SPI_AOI_HEADER.TOTAL_POINTS IS '计划检测点数';
|
COMMENT ON COLUMN MES_SPI_AOI_HEADER.ACTUAL_DEFECTS IS '实际不良点数';
|
COMMENT ON COLUMN MES_SPI_AOI_HEADER.EQUIPMENT_MODEL IS '设备型号';
|
COMMENT ON COLUMN MES_SPI_AOI_HEADER.WORK_ORDER IS '对应工单或批次号';
|
COMMENT ON COLUMN MES_SPI_AOI_HEADER.PRODUCT_MODEL IS '机种名称';
|
COMMENT ON COLUMN MES_SPI_AOI_HEADER.BOARD_BARCODE IS '板件条码,整单唯一';
|
COMMENT ON COLUMN MES_SPI_AOI_HEADER.SMT_GROUP IS 'SMT 组别';
|
COMMENT ON COLUMN MES_SPI_AOI_HEADER.LINE_NAME IS '线别名称';
|
COMMENT ON COLUMN MES_SPI_AOI_HEADER.CREATED_AT IS '创建时间,默认写入数据库时间戳';
|
COMMENT ON COLUMN MES_SPI_AOI_HEADER.UPDATED_AT IS '更新时间,默认写入数据库时间戳';
|
|
CREATE SEQUENCE SEQ_SPI_AOI_HEADER START WITH 1 INCREMENT BY 1 NOCACHE;
|
|
-- SPI/AOI 明细表,拆分缺陷统计与产能指标
|
CREATE TABLE MES_SPI_AOI_DETAIL (
|
ID NUMBER(19) NOT NULL,
|
HEADER_ID NUMBER(19) NOT NULL,
|
OFFSET_COUNT NUMBER(6) DEFAULT 0,
|
MISSING_COUNT NUMBER(6) DEFAULT 0,
|
REVERSE_COUNT NUMBER(6) DEFAULT 0,
|
LIFTED_COUNT NUMBER(6) DEFAULT 0,
|
FLOAT_HIGH_COUNT NUMBER(6) DEFAULT 0,
|
TOMBSTONE_COUNT NUMBER(6) DEFAULT 0,
|
FLIP_COUNT NUMBER(6) DEFAULT 0,
|
WRONG_PART_COUNT NUMBER(6) DEFAULT 0,
|
LEAD_LIFT_COUNT NUMBER(6) DEFAULT 0,
|
COLD_JOINT_COUNT NUMBER(6) DEFAULT 0,
|
NO_SOLDER_COUNT NUMBER(6) DEFAULT 0,
|
INSUFFICIENT_SOLDER_COUNT NUMBER(6) DEFAULT 0,
|
EXCESS_SOLDER_COUNT NUMBER(6) DEFAULT 0,
|
BRIDGE_COUNT NUMBER(6) DEFAULT 0,
|
COPPER_EXPOSURE_COUNT NUMBER(6) DEFAULT 0,
|
SPIKE_COUNT NUMBER(6) DEFAULT 0,
|
FOREIGN_MATTER_COUNT NUMBER(6) DEFAULT 0,
|
GLUE_OVERFLOW_COUNT NUMBER(6) DEFAULT 0,
|
PIN_OFFSET_COUNT NUMBER(6) DEFAULT 0,
|
LINE_DISPLAY_NAME VARCHAR2(64 CHAR),
|
MACHINE_NAME VARCHAR2(64 CHAR),
|
INPUT_BOARDS NUMBER(6) DEFAULT 0,
|
OK_BOARDS NUMBER(6) DEFAULT 0,
|
PASS_BOARDS NUMBER(6) DEFAULT 0,
|
PASS_RATE NUMBER(5,2),
|
DEFECT_BOARDS NUMBER(6) DEFAULT 0,
|
DEFECT_RATE NUMBER(5,2),
|
DEFECT_PPM NUMBER(9),
|
DEFECT_POINTS NUMBER(6) DEFAULT 0,
|
MEASURED_POINTS NUMBER(6) DEFAULT 0,
|
PENDING_POINTS NUMBER(6) DEFAULT 0,
|
CREATED_AT TIMESTAMP DEFAULT SYSTIMESTAMP,
|
UPDATED_AT TIMESTAMP DEFAULT SYSTIMESTAMP
|
);
|
ALTER TABLE MES_SPI_AOI_DETAIL
|
ADD CONSTRAINT PK_SPI_AOI_DETAIL PRIMARY KEY (ID);
|
ALTER TABLE MES_SPI_AOI_DETAIL
|
ADD CONSTRAINT FK_SPI_AOI_DETAIL_HEADER FOREIGN KEY (HEADER_ID)
|
REFERENCES MES_SPI_AOI_HEADER(ID);
|
|
CREATE INDEX IDX_SPI_AOI_DETAIL_HEADER ON MES_SPI_AOI_DETAIL (HEADER_ID);
|
|
COMMENT ON TABLE MES_SPI_AOI_DETAIL IS 'SPI/AOI 明细表,记录缺陷分类统计与产线效率数据';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.ID IS '主键ID,由序列 SEQ_SPI_AOI_DETAIL 生成';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.HEADER_ID IS '主表外键,指向 MES_SPI_AOI_HEADER.ID';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.OFFSET_COUNT IS '偏位数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.MISSING_COUNT IS '缺件数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.REVERSE_COUNT IS '反向安装数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.LIFTED_COUNT IS '翘起数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.FLOAT_HIGH_COUNT IS '浮高数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.TOMBSTONE_COUNT IS '立碑数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.FLIP_COUNT IS '翻转数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.WRONG_PART_COUNT IS '错件数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.LEAD_LIFT_COUNT IS '翘脚数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.COLD_JOINT_COUNT IS '虚焊数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.NO_SOLDER_COUNT IS '空焊数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.INSUFFICIENT_SOLDER_COUNT IS '少锡数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.EXCESS_SOLDER_COUNT IS '多锡数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.BRIDGE_COUNT IS '连锡数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.COPPER_EXPOSURE_COUNT IS '漏铜数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.SPIKE_COUNT IS '拉尖数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.FOREIGN_MATTER_COUNT IS '异物残留数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.GLUE_OVERFLOW_COUNT IS '溢胶数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.PIN_OFFSET_COUNT IS '引脚偏位数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.LINE_DISPLAY_NAME IS '产线显示名称';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.MACHINE_NAME IS '检测机台名称';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.INPUT_BOARDS IS '投入板数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.OK_BOARDS IS 'OK 板数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.PASS_BOARDS IS '通过板数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.PASS_RATE IS '合格率(%)';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.DEFECT_BOARDS IS '不良板数量';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.DEFECT_RATE IS '不良率(%)';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.DEFECT_PPM IS '不良数(PPM)';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.DEFECT_POINTS IS '不良点数';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.MEASURED_POINTS IS '实测点数';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.PENDING_POINTS IS '待测点数';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.CREATED_AT IS '创建时间,默认写入数据库时间戳';
|
COMMENT ON COLUMN MES_SPI_AOI_DETAIL.UPDATED_AT IS '更新时间,默认写入数据库时间戳';
|
|
CREATE SEQUENCE SEQ_SPI_AOI_DETAIL START WITH 1 INCREMENT BY 1 NOCACHE;
|
|
测试结果说明
|
- `TestResult` 字段直接接收原始字符串,不做枚举校验
|
- 常见值参考:
|
- "0:0:1;0" (通常表示测试通过)
|
- "0;0;0:1" (通常表示测试失败)
|
- "Fail" (通常表示测试异常)
|
- 数据库存储原始值,由业务层根据需要解析
|
|
接口设计
|
- URL:`POST /api/QC/SpiAoi/Upload`(位于 `MESApplication.Controllers.QC` 命名空间,沿用 `ResponseResult` 返回格式)。
|
- 请求体结构:
|
```
|
{
|
"header": {
|
"testDate": "2025-10-10",
|
"testTime": "14:33:21",
|
"testResult": "0;0;0:1",
|
"surface": "T",
|
"totalPoints": 500,
|
"actualDefects": 3,
|
"equipmentModel": "SPI-9000",
|
"workOrder": "WO20251010-01",
|
"productModel": "MODEL-ABC",
|
"boardBarcode": "BC123456789",
|
"smtGroup": "A1",
|
"lineName": "SMT-01"
|
},
|
"details": [
|
{
|
"machineName": "AOI-01",
|
"lineDisplayName": "SMT-01",
|
"offsetCount": 1,
|
"missingCount": 0,
|
"reverseCount": 0,
|
"liftedCount": 0,
|
"floatHighCount": 0,
|
"tombstoneCount": 0,
|
"flipCount": 0,
|
"wrongPartCount": 0,
|
"leadLiftCount": 0,
|
"coldJointCount": 0,
|
"noSolderCount": 0,
|
"insufficientSolderCount": 1,
|
"excessSolderCount": 0,
|
"bridgeCount": 1,
|
"copperExposureCount": 0,
|
"spikeCount": 0,
|
"foreignMatterCount": 0,
|
"glueOverflowCount": 0,
|
"pinOffsetCount": 0,
|
"inputBoards": 120,
|
"okBoards": 117,
|
"passBoards": 117,
|
"passRate": 97.5,
|
"defectBoards": 3,
|
"defectRate": 2.5,
|
"defectPpm": 25000,
|
"defectPoints": 3,
|
"measuredPoints": 500,
|
"pendingPoints": 0
|
}
|
]
|
}
|
```
|
- 响应示例:
|
```
|
{
|
"status": 0,
|
"message": "OK",
|
"data": { "headerId": 12345 }
|
}
|
```
|
|
业务处理流程
|
- 入口控制器负责模型绑定与基本校验:
|
- 必填字段:testDate、testTime、testResult、boardBarcode、surface
|
- 枚举合法性:surface 必须为 "T" 或 "B"
|
- testResult:不做枚举校验,直接接收原始字符串
|
- 数值非负:所有计数字段必须 >= 0
|
- **details 不能为空或 null**
|
- 将请求委托给 `SpiAoiService`(位于 `MES.Service.service.QC`),该服务使用 `SqlSugarClient` 启动事务:
|
1. **条码唯一性检测**:若 `BOARD_BARCODE` 已存在,直接返回错误 `status=1, message="条码已存在,不允许重复上传"`(保持唯一索引约束)。
|
2. 插入头表记录,返回 `HEADER_ID`(实体通过 `[SugarColumn(IsPrimaryKey = true, OracleSequenceName = "SEQ_SPI_AOI_HEADER")]` 自动获取序列值)。
|
3. 遍历 `details`,为每条填充 `HEADER_ID` 并插入子表。
|
4. **衍生字段校验**(记录日志但不阻断):
|
- `passBoards <= inputBoards`
|
- `defectBoards = inputBoards - passBoards`
|
- `passRate` 与计算值 `(passBoards / inputBoards * 100)` 的偏差在 ±1.0 以内
|
- 若校验失败,记录警告日志但不回滚事务
|
5. 提交事务;异常则回滚并记录日志。
|
- 成功后返回 `headerId`;失败返回 `status=1` 与错误信息。
|
|
验证与错误处理
|
- **输入校验失败**:返回 `status=1`,`message` 指明字段及原因(如:"boardBarcode 不能为空"、"surface 必须为 T 或 B")。
|
- **条码重复**:返回 `status=1, message="条码 {boardBarcode} 已存在,不允许重复上传"`。
|
- **details 为空**:返回 `status=1, message="details 不能为空"`。
|
- **数据库异常**:捕获 `SqlSugar`/Oracle 异常,记录到现有日志(如 `LogUtil`),同时写入消息中心(若需 ERP 同步)或自定义错误队列。
|
|
实体定义示例
|
```csharp
|
[SugarTable("MES_SPI_AOI_HEADER")]
|
public class SpiAoiHeader
|
{
|
[SugarColumn(ColumnName = "ID", IsPrimaryKey = true, OracleSequenceName = "SEQ_SPI_AOI_HEADER")]
|
public long Id { get; set; }
|
|
[SugarColumn(ColumnName = "TEST_DATE")]
|
public DateTime TestDate { get; set; }
|
|
[SugarColumn(ColumnName = "TEST_TIME")]
|
public string TestTime { get; set; }
|
|
[SugarColumn(ColumnName = "TEST_RESULT")]
|
public string TestResult { get; set; }
|
|
[SugarColumn(ColumnName = "SURFACE")]
|
public string Surface { get; set; }
|
|
[SugarColumn(ColumnName = "TOTAL_POINTS")]
|
public int? TotalPoints { get; set; }
|
|
[SugarColumn(ColumnName = "ACTUAL_DEFECTS")]
|
public int? ActualDefects { get; set; }
|
|
[SugarColumn(ColumnName = "EQUIPMENT_MODEL")]
|
public string EquipmentModel { get; set; }
|
|
[SugarColumn(ColumnName = "WORK_ORDER")]
|
public string WorkOrder { get; set; }
|
|
[SugarColumn(ColumnName = "PRODUCT_MODEL")]
|
public string ProductModel { get; set; }
|
|
[SugarColumn(ColumnName = "BOARD_BARCODE")]
|
public string BoardBarcode { get; set; }
|
|
[SugarColumn(ColumnName = "SMT_GROUP")]
|
public string SmtGroup { get; set; }
|
|
[SugarColumn(ColumnName = "LINE_NAME")]
|
public string LineName { get; set; }
|
|
[SugarColumn(ColumnName = "CREATED_AT")]
|
public DateTime CreatedAt { get; set; }
|
|
[SugarColumn(ColumnName = "UPDATED_AT")]
|
public DateTime UpdatedAt { get; set; }
|
}
|
|
[SugarTable("MES_SPI_AOI_DETAIL")]
|
public class SpiAoiDetail
|
{
|
[SugarColumn(ColumnName = "ID", IsPrimaryKey = true, OracleSequenceName = "SEQ_SPI_AOI_DETAIL")]
|
public long Id { get; set; }
|
|
[SugarColumn(ColumnName = "HEADER_ID")]
|
public long HeaderId { get; set; }
|
|
[SugarColumn(ColumnName = "OFFSET_COUNT")]
|
public int OffsetCount { get; set; }
|
|
[SugarColumn(ColumnName = "MISSING_COUNT")]
|
public int MissingCount { get; set; }
|
|
[SugarColumn(ColumnName = "REVERSE_COUNT")]
|
public int ReverseCount { get; set; }
|
|
[SugarColumn(ColumnName = "LIFTED_COUNT")]
|
public int LiftedCount { get; set; }
|
|
[SugarColumn(ColumnName = "FLOAT_HIGH_COUNT")]
|
public int FloatHighCount { get; set; }
|
|
[SugarColumn(ColumnName = "TOMBSTONE_COUNT")]
|
public int TombstoneCount { get; set; }
|
|
[SugarColumn(ColumnName = "FLIP_COUNT")]
|
public int FlipCount { get; set; }
|
|
[SugarColumn(ColumnName = "WRONG_PART_COUNT")]
|
public int WrongPartCount { get; set; }
|
|
[SugarColumn(ColumnName = "LEAD_LIFT_COUNT")]
|
public int LeadLiftCount { get; set; }
|
|
[SugarColumn(ColumnName = "COLD_JOINT_COUNT")]
|
public int ColdJointCount { get; set; }
|
|
[SugarColumn(ColumnName = "NO_SOLDER_COUNT")]
|
public int NoSolderCount { get; set; }
|
|
[SugarColumn(ColumnName = "INSUFFICIENT_SOLDER_COUNT")]
|
public int InsufficientSolderCount { get; set; }
|
|
[SugarColumn(ColumnName = "EXCESS_SOLDER_COUNT")]
|
public int ExcessSolderCount { get; set; }
|
|
[SugarColumn(ColumnName = "BRIDGE_COUNT")]
|
public int BridgeCount { get; set; }
|
|
[SugarColumn(ColumnName = "COPPER_EXPOSURE_COUNT")]
|
public int CopperExposureCount { get; set; }
|
|
[SugarColumn(ColumnName = "SPIKE_COUNT")]
|
public int SpikeCount { get; set; }
|
|
[SugarColumn(ColumnName = "FOREIGN_MATTER_COUNT")]
|
public int ForeignMatterCount { get; set; }
|
|
[SugarColumn(ColumnName = "GLUE_OVERFLOW_COUNT")]
|
public int GlueOverflowCount { get; set; }
|
|
[SugarColumn(ColumnName = "PIN_OFFSET_COUNT")]
|
public int PinOffsetCount { get; set; }
|
|
[SugarColumn(ColumnName = "LINE_DISPLAY_NAME")]
|
public string LineDisplayName { get; set; }
|
|
[SugarColumn(ColumnName = "MACHINE_NAME")]
|
public string MachineName { get; set; }
|
|
[SugarColumn(ColumnName = "INPUT_BOARDS")]
|
public int InputBoards { get; set; }
|
|
[SugarColumn(ColumnName = "OK_BOARDS")]
|
public int OkBoards { get; set; }
|
|
[SugarColumn(ColumnName = "PASS_BOARDS")]
|
public int PassBoards { get; set; }
|
|
[SugarColumn(ColumnName = "PASS_RATE")]
|
public decimal? PassRate { get; set; }
|
|
[SugarColumn(ColumnName = "DEFECT_BOARDS")]
|
public int DefectBoards { get; set; }
|
|
[SugarColumn(ColumnName = "DEFECT_RATE")]
|
public decimal? DefectRate { get; set; }
|
|
[SugarColumn(ColumnName = "DEFECT_PPM")]
|
public int? DefectPpm { get; set; }
|
|
[SugarColumn(ColumnName = "DEFECT_POINTS")]
|
public int DefectPoints { get; set; }
|
|
[SugarColumn(ColumnName = "MEASURED_POINTS")]
|
public int MeasuredPoints { get; set; }
|
|
[SugarColumn(ColumnName = "PENDING_POINTS")]
|
public int PendingPoints { get; set; }
|
|
[SugarColumn(ColumnName = "CREATED_AT")]
|
public DateTime CreatedAt { get; set; }
|
|
[SugarColumn(ColumnName = "UPDATED_AT")]
|
public DateTime UpdatedAt { get; set; }
|
}
|
```
|
|
实现步骤建议
|
1. **创建数据库表**:执行上述 DDL,创建 `MES_SPI_AOI_HEADER`、`MES_SPI_AOI_DETAIL` 表及序列。
|
2. **定义实体模型**:在 `MES.Service.Modes` 下创建 `SpiAoiHeader.cs` 和 `SpiAoiDetail.cs`(参考上述实体定义示例)。
|
3. **创建 DTO**:
|
- `SpiAoiHeaderDto.cs`(用于请求体 header 部分)
|
- `SpiAoiDetailDto.cs`(用于请求体 details 数组元素)
|
- `SpiAoiUploadRequest.cs`(包含 Header 和 Details 列表)
|
- `SpiAoiUploadResponse.cs`(包含 headerId)
|
4. **实现服务层**:
|
- 在 `MES.Service.service.QC` 下创建 `SpiAoiService.cs`
|
- 实现条码唯一性检查、事务插入、数据校验等业务逻辑
|
5. **实现控制器**:
|
- 在 `MESApplication.Controllers.QC` 下创建 `SpiAoiController.cs`
|
- 添加 `[HttpPost("Upload")]` 方法,调用服务层并返回 `ResponseResult`
|
6. **添加单元测试**(可选):验证成功插入、非法枚举、条码重复、details 为空、事务回滚等场景。
|
|
注意事项
|
- 所有字符串字段需检查长度是否超过数据库定义(如 `BOARD_BARCODE` 最大 128 字符)。
|
- `TestDate` 和 `TestTime` 分开存储,前端需分别传入日期和时间字符串。
|
- `TestResult` 不做枚举校验,接收任意字符串值(最大 12 字符),原样存储到数据库。
|
- 对于 `CREATED_AT` 和 `UPDATED_AT`,建议在代码中显式赋值 `DateTime.Now`,而非依赖数据库默认值。
|
- Oracle 11g 需确保 SqlSugar 版本兼容(推荐 5.x 或更高版本)。
|
- 条码唯一索引 `IDX_SPI_AOI_BARCODE` 会自动阻止重复插入,但建议在代码中提前检查并返回友好错误信息。
|