using Admin.NET.Plugin.AiDOP.Dto.S0.Warehouse; using Admin.NET.Plugin.AiDOP.Entity.S0.Sales; using Admin.NET.Plugin.AiDOP.Entity.S0.Supply; using Admin.NET.Plugin.AiDOP.Entity.S0.Warehouse; using Admin.NET.Plugin.AiDOP.Infrastructure; namespace Admin.NET.Plugin.AiDOP.Controllers.S0.Warehouse; /// /// S0 条码规则主数据(BarCodeNbr 语义)。 /// 多租户隔离:所有读写显式限定当前请求租户(AdoS0TenantScope.TryResolveRequired),不依赖全局 AOP。 /// [ApiController] [Route("api/s0/warehouse/barcode-rules")] [NonUnify] public class AdoS0BarcodeRulesController : ControllerBase { private readonly SqlSugarRepository _rep; private readonly SqlSugarRepository _suppRep; private readonly SqlSugarRepository _custRep; public AdoS0BarcodeRulesController( SqlSugarRepository rep, SqlSugarRepository suppRep, SqlSugarRepository custRep) { _rep = rep; _suppRep = suppRep; _custRep = custRep; } [HttpGet] public async Task GetPagedAsync([FromQuery] AdoS0BarcodeRuleQueryDto q) { if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!; (q.Page, q.PageSize) = PagingGuard.Normalize(q.Page, q.PageSize); var query = _rep.AsQueryable() .Where(x => x.TenantId == tenantId) .WhereIF(q.CompanyRefId.HasValue, x => x.CompanyRefId == q.CompanyRefId!.Value) .WhereIF(q.FactoryRefId.HasValue, x => x.FactoryRefId == q.FactoryRefId!.Value) .WhereIF(!string.IsNullOrWhiteSpace(q.DomainCode), x => x.DomainCode == q.DomainCode) .WhereIF(!string.IsNullOrWhiteSpace(q.Customer), x => x.Customer != null && x.Customer.Contains(q.Customer!)) .WhereIF(!string.IsNullOrWhiteSpace(q.Type), x => x.Type != null && x.Type.Contains(q.Type!)) .WhereIF(!string.IsNullOrWhiteSpace(q.Keyword), x => (x.Customer != null && x.Customer.Contains(q.Keyword!)) || (x.Type != null && x.Type.Contains(q.Keyword!))); var total = await query.CountAsync(); var list = await query .OrderByDescending(x => x.CreateTime) .Skip((q.Page - 1) * q.PageSize) .Take(q.PageSize) .ToListAsync(); await ApplyPartyNameAsync(list, tenantId); return Ok(new { total, page = q.Page, pageSize = q.PageSize, list }); } [HttpGet("{id:long}")] public async Task GetAsync(long id) { if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!; var item = await _rep.AsQueryable().Where(x => x.Id == id && x.TenantId == tenantId).FirstAsync(); if (item == null) return NotFound(); await ApplyPartyNameAsync(new List { item }, tenantId); return Ok(item); } [HttpPost] public async Task CreateAsync([FromBody] AdoS0BarcodeRuleUpsertDto dto) { if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!; // 唯一口径对齐数据库 uk_BarCodeNbr_tenant_domain_customer_type (tenant_id, Domain, Customer, Type) if (await _rep.IsAnyAsync(x => x.TenantId == tenantId && x.DomainCode == (dto.DomainCode ?? string.Empty) && x.Customer == dto.Customer && x.Type == dto.Type)) return AdoS0ApiErrors.Conflict(AdoS0ErrorCodes.BarcodeRuleCodeExists, "条码规则已存在"); var entity = MapToEntity(dto); entity.TenantId = tenantId; entity.CreateTime = DateTime.Now; await _rep.AsInsertable(entity).ExecuteReturnEntityAsync(); return Ok(entity); } [HttpPut("{id:long}")] public async Task UpdateAsync(long id, [FromBody] AdoS0BarcodeRuleUpsertDto dto) { if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!; var entity = await _rep.AsQueryable().Where(x => x.Id == id && x.TenantId == tenantId).FirstAsync(); if (entity == null) return NotFound(); if (await _rep.IsAnyAsync(x => x.TenantId == tenantId && x.Id != id && x.DomainCode == (dto.DomainCode ?? string.Empty) && x.Customer == dto.Customer && x.Type == dto.Type)) return AdoS0ApiErrors.Conflict(AdoS0ErrorCodes.BarcodeRuleCodeExists, "条码规则已存在"); // entity.TenantId 保持不变(== tenantId);不接受前端修改 tenant entity.CompanyRefId = dto.CompanyRefId; entity.FactoryRefId = dto.FactoryRefId; entity.DomainCode = dto.DomainCode ?? string.Empty; entity.Customer = dto.Customer; entity.Type = dto.Type; entity.WaterRules = dto.WaterRules; entity.WaterLen = dto.WaterLen; entity.Separator = dto.Separator; entity.FirmLength = dto.FirmLength; CopyFirmStrings(dto, entity); entity.UpdateUser = dto.UpdateUser; entity.UpdateTime = DateTime.Now; await _rep.AsUpdateable(entity).ExecuteCommandAsync(); return Ok(entity); } [HttpDelete("{id:long}")] public async Task DeleteAsync(long id) { if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!; var item = await _rep.AsQueryable().Where(x => x.Id == id && x.TenantId == tenantId).FirstAsync(); if (item == null) return NotFound(); await _rep.DeleteAsync(item); return Ok(new { message = "删除成功" }); } private static AdoS0BarCodeNbr MapToEntity(AdoS0BarcodeRuleUpsertDto dto) { var e = new AdoS0BarCodeNbr { CompanyRefId = dto.CompanyRefId, FactoryRefId = dto.FactoryRefId, DomainCode = dto.DomainCode ?? string.Empty, Customer = dto.Customer, Type = dto.Type, WaterRules = dto.WaterRules, WaterLen = dto.WaterLen, Separator = dto.Separator, FirmLength = dto.FirmLength, CreateUser = dto.CreateUser }; CopyFirmStrings(dto, e); return e; } private static void CopyFirmStrings(AdoS0BarcodeRuleUpsertDto dto, AdoS0BarCodeNbr e) { e.FirmString1 = dto.FirmString1; e.FirmStringLen1 = dto.FirmStringLen1; e.FirmString1Note = dto.FirmString1Note; e.FirmString2 = dto.FirmString2; e.FirmStringLen2 = dto.FirmStringLen2; e.FirmString2Note = dto.FirmString2Note; e.FirmString3 = dto.FirmString3; e.FirmStringLen3 = dto.FirmStringLen3; e.FirmString3Note = dto.FirmString3Note; e.FirmString4 = dto.FirmString4; e.FirmStringLen4 = dto.FirmStringLen4; e.FirmString4Note = dto.FirmString4Note; e.FirmString5 = dto.FirmString5; e.FirmStringLen5 = dto.FirmStringLen5; e.FirmString5Note = dto.FirmString5Note; e.FirmString6 = dto.FirmString6; e.FirmStringLen6 = dto.FirmStringLen6; e.FirmString6Note = dto.FirmString6Note; e.FirmString7 = dto.FirmString7; e.FirmStringLen7 = dto.FirmStringLen7; e.FirmString7Note = dto.FirmString7Note; e.FirmString8 = dto.FirmString8; e.FirmStringLen8 = dto.FirmStringLen8; e.FirmString8Note = dto.FirmString8Note; e.FirmString9 = dto.FirmString9; e.FirmStringLen9 = dto.FirmStringLen9; e.FirmString9Note = dto.FirmString9Note; e.FirmString10 = dto.FirmString10; e.FirmStringLen10 = dto.FirmStringLen10; e.FirmString10Note = dto.FirmString10Note; e.FirmString11 = dto.FirmString11; e.FirmStringLen11 = dto.FirmStringLen11; e.FirmString11Note = dto.FirmString11Note; e.FirmString12 = dto.FirmString12; e.FirmStringLen12 = dto.FirmStringLen12; e.FirmString12Note = dto.FirmString12Note; } /// /// 补显「客户/供应商名称」。 /// /// legacy 字段兼容:BarCodeNbr.Customer 的列描述即「客户/供应商编码」, /// 同一字段既可能存客户编码、也可能存供应商编码,本批不改 schema、不引入 OwnerType。 /// 因此按 CustMaster → SuppMaster 依次解析: /// 仅命中客户 → 客户简称;仅命中供应商 → 供应商简称; /// 两边都命中 → 客户优先(当前无 Type 层面的 owner 语义证据,不做静默猜测); /// 两边都不命中 → 留空,不造值。 /// /// 两次批量查询(各一次),按 code 建字典,无 N+1;均显式限定当前租户。 /// PartyName 为新语义字段;SuppName 保留旧值以兼容既有引用。 /// private async Task ApplyPartyNameAsync(List rules, long tenantId) { if (rules.Count == 0) return; var partyCodes = rules .Select(r => r.Customer) .Where(c => !string.IsNullOrWhiteSpace(c)) .Select(c => c!) .Distinct() .ToList(); if (partyCodes.Count == 0) return; // 客户关联限定当前租户 var custs = await _custRep.AsQueryable() .Where(c => c.TenantId == tenantId && partyCodes.Contains(c.Cust)) .ToListAsync(); // 供应商关联限定当前租户,避免同编码供应商跨租户补错简称 var supps = await _suppRep.AsQueryable() .Where(s => s.TenantId == tenantId && partyCodes.Contains(s.Supp)) .ToListAsync(); foreach (var rule in rules) { if (string.IsNullOrWhiteSpace(rule.Customer)) continue; var cust = custs.Find(c => string.Equals(c.Cust, rule.Customer, StringComparison.OrdinalIgnoreCase)); var supp = supps.Find(s => string.Equals(s.Supp, rule.Customer, StringComparison.OrdinalIgnoreCase)); rule.SuppName = supp?.SortName; rule.PartyName = cust?.SortName ?? supp?.SortName; } } }