using Admin.NET.Plugin.AiDOP.Dto.S0.Sales; using Admin.NET.Plugin.AiDOP.Entity.S0.Sales; using Admin.NET.Plugin.AiDOP.Infrastructure; namespace Admin.NET.Plugin.AiDOP.Controllers.S0.Sales; [ApiController] [Route("api/s0/sales/product-design-cycles")] [AllowAnonymous] [NonUnify] public class AdoS0ProductDesignCyclesController : ControllerBase { private readonly SqlSugarRepository _rep; private readonly SqlSugarRepository _itemMasterRep; public AdoS0ProductDesignCyclesController( SqlSugarRepository rep, SqlSugarRepository itemMasterRep) { _rep = rep; _itemMasterRep = itemMasterRep; } [HttpGet] public async Task GetPagedAsync([FromQuery] AdoS0ProductDesignCycleQueryDto q) { (q.Page, q.PageSize) = PagingGuard.Normalize(q.Page, q.PageSize); var query = _rep.AsQueryable() .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.ItemType), x => x.ItemType == q.ItemType) .WhereIF(!string.IsNullOrWhiteSpace(q.OwnerApplication), x => x.OwnerApplication == q.OwnerApplication) .WhereIF(q.IsActive.HasValue, x => x.IsActive == q.IsActive!.Value); var total = await query.CountAsync(); var entities = await query .OrderByDescending(x => x.CreateTime) .Skip((q.Page - 1) * q.PageSize) .Take(q.PageSize) .ToListAsync(); var list = await BuildRowsWithItemMasterMatchAsync(entities); return Ok(new { total, page = q.Page, pageSize = q.PageSize, list }); } /// /// 在 PDC 实体基础上附带 ItemMaster 命中校验信息。 /// 统计口径: /// - ItemMaster.ItemType = PDC.item_type; /// - PDC.domain_code 有值 → ItemMaster.Domain = domain_code;为 NULL → 不限 Domain; /// - OwnerApplication / Status 均不参与过滤。 /// 实现:2 次查询(PDC 主列表上游已查 + ItemMaster 按 ItemType IN 一次性拉取相关列), /// 内存按 (ItemType, Domain) 分组计算 count / top3,避免 N+1。 /// private async Task> BuildRowsWithItemMasterMatchAsync( List entities) { if (entities.Count == 0) return new List(); var distinctItemTypes = entities .Select(e => e.ItemType) .Where(t => !string.IsNullOrWhiteSpace(t)) .Distinct() .ToList(); // 候选 ItemMaster 行(仅当前页相关 ItemType);空集合不发起查询。 var candidates = distinctItemTypes.Count == 0 ? new List() : await _itemMasterRep.AsQueryable() .Where(x => distinctItemTypes.Contains(x.ItemType!)) .Select(x => new AdoS0ItemMaster { Id = x.Id, ItemType = x.ItemType, DomainCode = x.DomainCode, ItemNum = x.ItemNum, Descr = x.Descr, }) .ToListAsync(); return entities.Select(e => { var matched = candidates.Where(c => c.ItemType == e.ItemType && (string.IsNullOrWhiteSpace(e.DomainCode) || c.DomainCode == e.DomainCode)) .ToList(); var samples = matched .OrderBy(c => c.Id) .Take(3) .Select(c => string.IsNullOrWhiteSpace(c.Descr) ? c.ItemNum : $"{c.ItemNum} / {c.Descr}") .ToList(); return new AdoS0ProductDesignCycleRowDto { Id = e.Id, CompanyRefId = e.CompanyRefId, FactoryRefId = e.FactoryRefId, DomainCode = e.DomainCode, ItemType = e.ItemType, OwnerApplication = e.OwnerApplication, StdHours = e.StdHours, IsActive = e.IsActive, CreateUser = e.CreateUser, CreateTime = e.CreateTime, UpdateUser = e.UpdateUser, UpdateTime = e.UpdateTime, ItemMasterMatchedCount = matched.Count, ItemMasterSampleItems = samples, }; }).ToList(); } [HttpGet("{id:long}")] public async Task GetAsync(long id) { var item = await _rep.GetByIdAsync(id); return item == null ? NotFound() : Ok(item); } [HttpPost] public async Task CreateAsync([FromBody] AdoS0ProductDesignCycleUpsertDto dto) { if (await _rep.IsAnyAsync(x => x.FactoryRefId == dto.FactoryRefId && x.ItemType == dto.ItemType && x.OwnerApplication == dto.OwnerApplication)) return AdoS0ApiErrors.Conflict(AdoS0ErrorCodes.DuplicateCode, "该工厂下此物料类型+属性组合已存在"); var entity = new AdoS0ProductDesignCycle { CompanyRefId = dto.CompanyRefId, FactoryRefId = dto.FactoryRefId, DomainCode = dto.DomainCode, ItemType = dto.ItemType, OwnerApplication = dto.OwnerApplication, StdHours = dto.StdHours, IsActive = dto.IsActive, CreateUser = dto.CreateUser, CreateTime = DateTime.Now, }; await _rep.AsInsertable(entity).ExecuteReturnEntityAsync(); return Ok(entity); } [HttpPut("{id:long}")] public async Task UpdateAsync(long id, [FromBody] AdoS0ProductDesignCycleUpsertDto dto) { var entity = await _rep.GetByIdAsync(id); if (entity == null) return NotFound(); if (await _rep.IsAnyAsync(x => x.Id != id && x.FactoryRefId == dto.FactoryRefId && x.ItemType == dto.ItemType && x.OwnerApplication == dto.OwnerApplication)) return AdoS0ApiErrors.Conflict(AdoS0ErrorCodes.DuplicateCode, "该工厂下此物料类型+属性组合已存在"); entity.CompanyRefId = dto.CompanyRefId; entity.FactoryRefId = dto.FactoryRefId; entity.DomainCode = dto.DomainCode; entity.ItemType = dto.ItemType; entity.OwnerApplication = dto.OwnerApplication; entity.StdHours = dto.StdHours; entity.IsActive = dto.IsActive; entity.UpdateUser = dto.UpdateUser; entity.UpdateTime = DateTime.Now; await _rep.AsUpdateable(entity).ExecuteCommandAsync(); return Ok(entity); } [HttpPatch("{id:long}/toggle-enabled")] public async Task ToggleActiveAsync(long id, [FromBody] AdoS0ProductDesignCycleToggleActiveDto dto) { var entity = await _rep.GetByIdAsync(id); if (entity == null) return NotFound(); entity.IsActive = dto.IsActive; entity.UpdateTime = DateTime.Now; await _rep.AsUpdateable(entity).ExecuteCommandAsync(); return Ok(entity); } [HttpDelete("{id:long}")] public async Task DeleteAsync(long id) { var item = await _rep.GetByIdAsync(id); if (item == null) return NotFound(); await _rep.DeleteAsync(item); return Ok(new { message = "删除成功" }); } }