using Admin.NET.Plugin.AiDOP.Manufacturing.Dto; namespace Admin.NET.Plugin.AiDOP.Manufacturing; /// /// S6 过程检验单(IPQC)只读 list 服务。 /// /// 数据源:DOP 数据中台标准层 mdp_std_ipqc_inspection(头)。 /// 由 IpqcInspectionMdpSyncService 从归口后的 aidopdev.qms_gcjyd(过程检验单头)同步标准化而来。 /// /// 本服务仅 SELECT:无新增/编辑/删除/写入。 /// 当前上游尚未抽数,归口源表 qms_gcjyd 为空 → 标准层为空 → list 返回空数组(前端空态)。 /// 明细(qms_gcjydzb)与详情不在本批范围,留待 IPQC-INSPECTION-DETAIL-READONLY-1。 /// [ApiDescriptionSettings(Order = 306, Description = "过程检验单")] [Route("api/IpqcInspection")] [AllowAnonymous] [NonUnify] public class IpqcInspectionService : IDynamicApiController, ITransient { private readonly ISqlSugarClient _db; public IpqcInspectionService(ISqlSugarClient db) { _db = db; } /// /// 过程检验单列表(只读分页查询)。 /// [DisplayName("过程检验单列表")] [HttpGet("list")] public async Task GetList([FromQuery] IpqcInspectionListInput input) { var page = input.Page <= 0 ? 1 : input.Page; var pageSize = input.PageSize <= 0 ? 10 : input.PageSize; var offset = (page - 1) * pageSize; var where = new List { "1=1" }; var pars = new List(); if (input.TenantId is > 0) { where.Add("m.tenant_id = @TenantId"); pars.Add(new SugarParameter("@TenantId", input.TenantId)); } if (!string.IsNullOrWhiteSpace(input.BillNo)) { where.Add("m.bill_no LIKE @BillNo"); pars.Add(new SugarParameter("@BillNo", $"%{input.BillNo.Trim()}%")); } if (!string.IsNullOrWhiteSpace(input.ProductionBatchNo)) { where.Add("m.production_batch_no LIKE @ProductionBatchNo"); pars.Add(new SugarParameter("@ProductionBatchNo", $"%{input.ProductionBatchNo.Trim()}%")); } var whereSql = string.Join(" AND ", where); var total = await _db.Ado.GetIntAsync( $"SELECT COUNT(1) FROM mdp_std_ipqc_inspection m WHERE {whereSql}", pars); var list = await _db.Ado.SqlQueryAsync( $""" SELECT m.id AS Id, m.bill_no AS BillNo, m.product_model AS ProductModel, m.production_batch_no AS ProductionBatchNo, m.production_work_order AS ProductionWorkOrder, m.result_judgement AS ResultJudgement, m.attachment AS Attachment, m.remark AS Remark, m.inspector AS Inspector, m.process_code AS ProcessCode, m.process_name AS ProcessName, m.production_person AS ProductionPerson, m.sample_qty AS SampleQty, m.form_no AS FormNo, m.version_no AS VersionNo, m.effective_date AS EffectiveDate, m.material_code AS MaterialCode, m.material_name AS MaterialName, m.inspec_standard_version AS InspecStandardVersion, m.inspec_standard_code AS InspecStandardCode FROM mdp_std_ipqc_inspection m WHERE {whereSql} ORDER BY {BuildOrderBy(input.SortField, input.SortOrder)} LIMIT {pageSize} OFFSET {offset} """, pars); return new { total, page, pageSize, list }; } /// /// 过程检验单详情(只读:头 + 明细)。查不到返回 null。 /// [DisplayName("过程检验单详情")] [HttpGet("detail")] public async Task GetDetail([FromQuery] long id, [FromQuery] long? tenantId) { if (id <= 0) return null; var headPars = new List { new("@Id", id) }; var headWhere = "m.id = @Id"; if (tenantId is > 0) { headWhere += " AND m.tenant_id = @TenantId"; headPars.Add(new SugarParameter("@TenantId", tenantId)); } var head = await _db.Ado.SqlQuerySingleAsync( $""" SELECT m.id AS Id, m.bill_no AS BillNo, m.product_model AS ProductModel, m.production_batch_no AS ProductionBatchNo, m.production_work_order AS ProductionWorkOrder, m.result_judgement AS ResultJudgement, m.attachment AS Attachment, m.remark AS Remark, m.inspector AS Inspector, m.process_code AS ProcessCode, m.process_name AS ProcessName, m.production_person AS ProductionPerson, m.sample_qty AS SampleQty, m.form_no AS FormNo, m.version_no AS VersionNo, m.effective_date AS EffectiveDate, m.material_code AS MaterialCode, m.material_name AS MaterialName, m.inspec_standard_version AS InspecStandardVersion, m.inspec_standard_code AS InspecStandardCode, m.inspection_status AS InspectionStatus FROM mdp_std_ipqc_inspection m WHERE {headWhere} LIMIT 1 """, headPars); if (head == null || head.Id <= 0) return null; head.Lines = await _db.Ado.SqlQueryAsync( """ SELECT d.id AS Id, d.inspection_item AS InspectionItem, d.inspection_basis AS InspectionBasis, d.technical_standard AS TechnicalStandard, d.inspection_method AS InspectionMethod, d.upper_limit AS UpperLimit, d.lower_limit AS LowerLimit, d.process_code AS ProcessCode, d.process_name AS ProcessName, d.measurement_instrument AS MeasurementInstrument, d.inspector AS Inspector, d.inspection_time AS InspectionTime, d.sample_qty AS SampleQty, d.inspection_qty AS InspectionQty, d.unqualified_qty AS UnqualifiedQty, d.result_judgement AS ResultJudgement, d.non_numeric_ok AS NonNumericOk, d.non_numeric_ng AS NonNumericNg, d.remark AS Remark, d.attachment AS Attachment FROM mdp_std_ipqc_inspection_detail d WHERE d.std_head_id = @HeadId ORDER BY d.id ASC """, new List { new("@HeadId", head.Id) }); return head; } /// /// 排序白名单:仅允许按已展示列排序,杜绝 SQL 注入。 /// private static string BuildOrderBy(string? sortField, string? sortOrder) { var column = sortField switch { "billNo" => "m.bill_no", "effectiveDate" => "m.effective_date", _ => "m.effective_date", }; var direction = string.Equals(sortOrder, "asc", StringComparison.OrdinalIgnoreCase) ? "ASC" : "DESC"; return $"{column} {direction}, m.id DESC"; } }