IncomingInspectionApplicationService.cs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. using Admin.NET.Plugin.AiDOP.MaterialWarehouse.Dto;
  2. namespace Admin.NET.Plugin.AiDOP.MaterialWarehouse;
  3. /// <summary>
  4. /// S5 来料检验申请列表 服务(只读)。
  5. ///
  6. /// 数据源:主库 aidopdev 表 qms_qcp_inspecapplyn(贴源 QMS 申请主表)。
  7. /// 一期降级方案②:仅查询/展示表内可直接真实渲染的字段
  8. /// FBILLNO 单据编号 / FBILLTYPE 单据类型(原值) / FAPPLYTIME 申请时间 / FCOMMENT 备注。
  9. /// 不查询 FORGID / FBIZTYPE / FAPPLYUSER —— 名称解析源(bos_org / qcbd_biztype / bos_user)
  10. /// 当前不在 aidopdev,不做脑补 join、不显示裸 ID。
  11. ///
  12. /// 租户:qms_qcp_inspecapplyn 已补 tenant_id 列,按严格可信租户过滤(需认证,租户仅取认证后 JWT,忽略前端 tenantId);
  13. /// 无 domain / factory 列,不新增前端 factory 选择,亦不脑补工厂维度。
  14. ///
  15. /// 本接口只读:仅 SELECT,无新增/编辑/删除/任务拆分/状态回写。
  16. /// </summary>
  17. [ApiDescriptionSettings(Order = 305, Description = "来料检验申请")]
  18. [Route("api/IncomingInspection")]
  19. [NonUnify]
  20. public class IncomingInspectionApplicationService : IDynamicApiController, ITransient
  21. {
  22. /// <summary>主/系统租户哨兵:超管未选择目标租户时其 JWT TenantId 即此值,拒绝作为业务租户。</summary>
  23. private const long MainTenantId = 1300000000001L;
  24. private readonly ISqlSugarClient _db;
  25. private readonly UserManager _userManager;
  26. public IncomingInspectionApplicationService(ISqlSugarClient db, UserManager userManager)
  27. {
  28. _db = db;
  29. _userManager = userManager;
  30. }
  31. /// <summary>严格可信租户解析:只来自认证后 JWT;无 Token(&lt;=0) 或超管未选主租户 → 拒绝;不读前端 tenantId、无默认回退。</summary>
  32. private long ResolveTenantOrThrow()
  33. {
  34. var tid = _userManager.TenantId;
  35. if (tid <= 0)
  36. throw Oops.Oh("无法确定当前租户,请重新登录或选择目标租户");
  37. if (_userManager.SuperAdmin && tid == MainTenantId)
  38. throw Oops.Oh("超级管理员操作前必须选择目标租户");
  39. return tid;
  40. }
  41. /// <summary>
  42. /// 来料检验申请列表(只读分页查询)。
  43. /// </summary>
  44. [DisplayName("来料检验申请列表")]
  45. [HttpGet("application/list")]
  46. public async Task<object> GetList([FromQuery] IncomingInspectionApplicationListInput input)
  47. {
  48. var page = input.Page <= 0 ? 1 : input.Page;
  49. var pageSize = input.PageSize <= 0 ? 10 : input.PageSize;
  50. var offset = (page - 1) * pageSize;
  51. // 同库分租户:qms_qcp_inspecapplyn 已补 tenant_id 列,按严格可信租户(仅认证后 JWT,忽略前端 tenantId)过滤,杜绝匿名跨租户读。
  52. var tenantId = ResolveTenantOrThrow();
  53. var where = new List<string> { "m.tenant_id = @TenantId" };
  54. var pars = new List<SugarParameter> { new("@TenantId", tenantId) };
  55. if (!string.IsNullOrWhiteSpace(input.BillNo))
  56. {
  57. where.Add("m.FBILLNO LIKE @BillNo");
  58. pars.Add(new SugarParameter("@BillNo", $"%{input.BillNo.Trim()}%"));
  59. }
  60. if (!string.IsNullOrWhiteSpace(input.ApplyTimeStart))
  61. {
  62. where.Add("m.FAPPLYTIME >= @ApplyTimeStart");
  63. pars.Add(new SugarParameter("@ApplyTimeStart", $"{input.ApplyTimeStart.Trim()} 00:00:00"));
  64. }
  65. if (!string.IsNullOrWhiteSpace(input.ApplyTimeEnd))
  66. {
  67. where.Add("m.FAPPLYTIME <= @ApplyTimeEnd");
  68. pars.Add(new SugarParameter("@ApplyTimeEnd", $"{input.ApplyTimeEnd.Trim()} 23:59:59"));
  69. }
  70. var whereSql = string.Join(" AND ", where);
  71. var total = await _db.Ado.GetIntAsync(
  72. $"SELECT COUNT(1) FROM qms_qcp_inspecapplyn m WHERE {whereSql}", pars);
  73. var list = await _db.Ado.SqlQueryAsync<IncomingInspectionApplicationListRow>(
  74. $"""
  75. SELECT
  76. m.id AS Id,
  77. m.FBILLNO AS FBillNo,
  78. m.FBILLTYPE AS FBillType,
  79. m.FAPPLYTIME AS FApplyTime,
  80. m.FCOMMENT AS FComment
  81. FROM qms_qcp_inspecapplyn m
  82. WHERE {whereSql}
  83. ORDER BY {BuildOrderBy(input.SortField, input.SortOrder)}
  84. LIMIT {pageSize} OFFSET {offset}
  85. """,
  86. pars);
  87. return new { total, page, pageSize, list };
  88. }
  89. /// <summary>
  90. /// 排序白名单:仅允许按已展示列排序,杜绝 SQL 注入。
  91. /// </summary>
  92. private static string BuildOrderBy(string? sortField, string? sortOrder)
  93. {
  94. var column = sortField switch
  95. {
  96. "fBillNo" => "m.FBILLNO",
  97. "fApplyTime" => "m.FAPPLYTIME",
  98. _ => "m.FAPPLYTIME",
  99. };
  100. var direction = string.Equals(sortOrder, "asc", StringComparison.OrdinalIgnoreCase) ? "ASC" : "DESC";
  101. return $"{column} {direction}, m.id DESC";
  102. }
  103. }