|
@@ -14,15 +14,36 @@ namespace Admin.NET.Plugin.AiDOP.Manufacturing;
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
[ApiDescriptionSettings(Order = 306, Description = "过程检验单")]
|
|
[ApiDescriptionSettings(Order = 306, Description = "过程检验单")]
|
|
|
[Route("api/IpqcInspection")]
|
|
[Route("api/IpqcInspection")]
|
|
|
-[AllowAnonymous]
|
|
|
|
|
[NonUnify]
|
|
[NonUnify]
|
|
|
public class IpqcInspectionService : IDynamicApiController, ITransient
|
|
public class IpqcInspectionService : IDynamicApiController, ITransient
|
|
|
{
|
|
{
|
|
|
|
|
+ /// <summary>主/系统租户哨兵:超管未选择目标租户时其 JWT TenantId 即此值,拒绝作为业务租户。</summary>
|
|
|
|
|
+ private const long MainTenantId = 1300000000001L;
|
|
|
|
|
+
|
|
|
private readonly ISqlSugarClient _db;
|
|
private readonly ISqlSugarClient _db;
|
|
|
|
|
+ private readonly UserManager _userManager;
|
|
|
|
|
|
|
|
- public IpqcInspectionService(ISqlSugarClient db)
|
|
|
|
|
|
|
+ public IpqcInspectionService(ISqlSugarClient db, UserManager userManager)
|
|
|
{
|
|
{
|
|
|
_db = db;
|
|
_db = db;
|
|
|
|
|
+ _userManager = userManager;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// IPQC 只读查询的可信租户解析(严格语义,无任何默认回退、不读前端 tenantId):
|
|
|
|
|
+ /// - 普通租户用户:返回认证后的 JWT TenantId(>0);
|
|
|
|
|
+ /// - 无 Token(TenantId<=0):拒绝(正常已被认证层 401 拦截,此处为纵深防御);
|
|
|
|
|
+ /// - 超管未选择目标租户(TenantId==主租户):拒绝,必须先选择目标租户。
|
|
|
|
|
+ /// 前端传入的 tenantId 一律忽略,绝不作为切换租户的权威来源。
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private long ResolveTenantOrThrow()
|
|
|
|
|
+ {
|
|
|
|
|
+ var tid = _userManager.TenantId;
|
|
|
|
|
+ if (tid <= 0)
|
|
|
|
|
+ throw Oops.Oh("无法确定当前租户,请重新登录或选择目标租户");
|
|
|
|
|
+ if (_userManager.SuperAdmin && tid == MainTenantId)
|
|
|
|
|
+ throw Oops.Oh("超级管理员操作前必须选择目标租户");
|
|
|
|
|
+ return tid;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -39,9 +60,9 @@ public class IpqcInspectionService : IDynamicApiController, ITransient
|
|
|
var where = new List<string> { "1=1" };
|
|
var where = new List<string> { "1=1" };
|
|
|
var pars = new List<SugarParameter>();
|
|
var pars = new List<SugarParameter>();
|
|
|
|
|
|
|
|
- // 强制租户过滤:qms_gcjyd/qms_gcjydzb 全库共享无 tenant,标准层按源 tenant_id 落库,
|
|
|
|
|
- // 这里必须强制按当前租户过滤(显式参数 > JWT > query > 默认租户),杜绝跨租户可见。
|
|
|
|
|
- var tid = AidopTenantHelper.Resolve(App.HttpContext, input.TenantId);
|
|
|
|
|
|
|
+ // 强制租户过滤:qms_gcjyd/qms_gcjydzb 全库共享无 tenant,标准层按源 tenant_id 落库。
|
|
|
|
|
+ // 租户只来自认证后的可信上下文(严格 JWT),忽略前端传入的 input.TenantId,杜绝跨租户可见。
|
|
|
|
|
+ var tid = ResolveTenantOrThrow();
|
|
|
where.Add("m.tenant_id = @TenantId");
|
|
where.Add("m.tenant_id = @TenantId");
|
|
|
pars.Add(new SugarParameter("@TenantId", tid));
|
|
pars.Add(new SugarParameter("@TenantId", tid));
|
|
|
if (!string.IsNullOrWhiteSpace(input.BillNo))
|
|
if (!string.IsNullOrWhiteSpace(input.BillNo))
|
|
@@ -99,12 +120,12 @@ public class IpqcInspectionService : IDynamicApiController, ITransient
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
[DisplayName("过程检验单详情")]
|
|
[DisplayName("过程检验单详情")]
|
|
|
[HttpGet("detail")]
|
|
[HttpGet("detail")]
|
|
|
- public async Task<IpqcInspectionDetailDto?> GetDetail([FromQuery] long id, [FromQuery] long? tenantId)
|
|
|
|
|
|
|
+ public async Task<IpqcInspectionDetailDto?> GetDetail([FromQuery] long id)
|
|
|
{
|
|
{
|
|
|
if (id <= 0) return null;
|
|
if (id <= 0) return null;
|
|
|
|
|
|
|
|
- // 强制租户过滤(同 GetList):显式参数 > JWT > query > 默认租户,杜绝跨租户读取详情。
|
|
|
|
|
- var tid = AidopTenantHelper.Resolve(App.HttpContext, tenantId);
|
|
|
|
|
|
|
+ // 强制租户过滤(同 GetList):租户只来自认证后的可信上下文,忽略前端 tenantId,杜绝跨租户读取详情。
|
|
|
|
|
+ var tid = ResolveTenantOrThrow();
|
|
|
var headPars = new List<SugarParameter> { new("@Id", id), new("@TenantId", tid) };
|
|
var headPars = new List<SugarParameter> { new("@Id", id), new("@TenantId", tid) };
|
|
|
var headWhere = "m.id = @Id AND m.tenant_id = @TenantId";
|
|
var headWhere = "m.id = @Id AND m.tenant_id = @TenantId";
|
|
|
|
|
|