| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- using Admin.NET.Plugin.AiDOP.MaterialWarehouse.Dto;
- namespace Admin.NET.Plugin.AiDOP.MaterialWarehouse;
- /// <summary>
- /// S5 盘点结果查询 只读 list 服务。
- ///
- /// 数据源:aidopdev.MissedPrint(盘点标签 IsTag=1),忠实复刻证据 sql.txt:
- /// LEFT JOIN ItemMaster(名称/规格/单位),过滤 m.Domain=@Domain AND m.IsTag=1。
- /// 盘点口径由 SQL 内定义(非脑补):
- /// 账面(标签数量)=Qty;实盘数量=CompQty;差异数量=CompQty-Qty;
- /// 盘点状态=CompQty≠Qty?'有差异':'正常';状态=Status='Q'?'冻结':'';实际库位=FirmString2;实际货架=FirmString4。
- /// Domain 按当前租户经 SourceDomainTenantResolver 解析(不再全局取第一条、无 8010 回落)。排序 UpdateTime DESC, RecID DESC。
- ///
- /// 本服务仅 SELECT:无新增/编辑/删除/库存事务/状态流转。
- /// MissedPrint 的 tenant_id 列存在但实测全为 NULL,无法做租户行级过滤,仅能按 Domain 公司域收敛(既有数据模型约束)。
- /// 类级已去 [AllowAnonymous]:原先该接口可被**完全未认证**调用(实测匿名 GET 返回 200),已收敛为需认证。
- /// </summary>
- [ApiDescriptionSettings(Order = 304, Description = "盘点结果查询")]
- [Route("api/S5StocktakeResult")]
- [NonUnify]
- public class StocktakeResultService : IDynamicApiController, ITransient
- {
- /// <summary>主/系统租户哨兵:超管未选择目标租户时其 JWT TenantId 即此值,拒绝作为业务租户。</summary>
- private const long MainTenantId = 1300000000001L;
- /// <summary>贴源源码:与 S5 其余库存页保持一致,供 Domain 解析使用。</summary>
- private const string SourceCode = "DOPDEMORQ_SQLSERVER";
- private readonly ISqlSugarClient _db;
- private readonly UserManager _userManager;
- private readonly SourceDomainTenantResolver _domainTenant;
- public StocktakeResultService(
- ISqlSugarClient db, UserManager userManager, SourceDomainTenantResolver domainTenant)
- {
- _db = db;
- _userManager = userManager;
- _domainTenant = domainTenant;
- }
- /// <summary>严格可信租户解析:只来自认证后 JWT;无 Token 或超管未选目标租户 → 拒绝,无默认回退。</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>
- /// 按当前租户解析公司域,统一走 <see cref="SourceDomainTenantResolver"/>。
- /// <para>
- /// 原实现是 <c>SELECT Domain FROM GeneralizedCodeMaster WHERE FldName='SystemConfig'
- /// AND Val='CompanyCode' ORDER BY RecID LIMIT 1</c> —— **无 tenant 条件**,取全库第一条并硬回落 "8010",
- /// 与 resolver 修复前的缺陷同源。本项目的 Domain 解析只允许有一个正式实现。
- /// </para>
- /// </summary>
- private async Task<string> ResolveDomainOrThrowAsync(long tenantId, CancellationToken ct = default)
- {
- try
- {
- return await _domainTenant.ResolveDomainAsync(SourceCode, tenantId, ct);
- }
- catch (Exception ex)
- {
- throw Oops.Oh($"无法解析当前租户 Domain 映射:{ex.Message}");
- }
- }
- /// <summary>
- /// 盘点结果查询列表(只读分页查询)。
- /// </summary>
- [DisplayName("盘点结果查询列表")]
- [HttpGet("list")]
- public async Task<object> GetList([FromQuery] StocktakeResultListInput input)
- {
- var page = input.Page <= 0 ? 1 : input.Page;
- var pageSize = input.PageSize <= 0 ? 10 : input.PageSize;
- var offset = (page - 1) * pageSize;
- var domain = await ResolveDomainOrThrowAsync(ResolveTenantOrThrow());
- var where = new List<string> { "m.Domain = @Domain", "m.IsTag = 1" };
- var pars = new List<SugarParameter> { new("@Domain", domain) };
- if (!string.IsNullOrWhiteSpace(input.MaterialCode))
- {
- where.Add("m.ItemNum LIKE @MaterialCode");
- pars.Add(new SugarParameter("@MaterialCode", $"%{input.MaterialCode.Trim()}%"));
- }
- if (!string.IsNullOrWhiteSpace(input.InvStatus))
- {
- var s = input.InvStatus.Trim();
- if (s == "有差异")
- where.Add("m.CompQty <> m.Qty");
- else if (s == "正常")
- where.Add("m.CompQty = m.Qty");
- }
- var whereSql = string.Join(" AND ", where);
- var total = await _db.Ado.GetIntAsync($"SELECT COUNT(1) FROM MissedPrint m WHERE {whereSql}", pars);
- var list = await _db.Ado.SqlQueryAsync<StocktakeResultListRow>(
- $"""
- SELECT
- m.ItemNum AS MaterialCode,
- i.Descr AS MaterialName,
- i.Descr1 AS Spec,
- m.Location AS Location,
- m.Shelf AS Shelf,
- m.FirmString2 AS ActLocation,
- m.FirmString4 AS ActShelf,
- m.LotSerial AS BatchNo,
- i.UM AS Unit,
- m.Qty AS TagQty,
- m.CompQty AS CountQty,
- (m.CompQty - m.Qty) AS QtyDiff,
- (CASE WHEN m.Status = 'Q' THEN '冻结' ELSE '' END) AS Status,
- (CASE WHEN m.CompQty <> m.Qty THEN '有差异' ELSE '正常' END) AS InvStatus,
- m.UpdateUser AS CountUser,
- m.UpdateTime AS CountTime
- FROM MissedPrint m
- LEFT JOIN ItemMaster i ON m.Domain = i.Domain AND m.ItemNum = i.ItemNum
- WHERE {whereSql}
- ORDER BY m.UpdateTime DESC, m.RecID DESC
- LIMIT {pageSize} OFFSET {offset}
- """,
- pars);
- return new { total, page, pageSize, list };
- }
- }
|