AdoS0ProductDesignCyclesController.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. using Admin.NET.Plugin.AiDOP.Dto.S0.Sales;
  2. using Admin.NET.Plugin.AiDOP.Entity.S0.Sales;
  3. using Admin.NET.Plugin.AiDOP.Infrastructure;
  4. namespace Admin.NET.Plugin.AiDOP.Controllers.S0.Sales;
  5. [ApiController]
  6. [Route("api/s0/sales/product-design-cycles")]
  7. [NonUnify]
  8. public class AdoS0ProductDesignCyclesController : ControllerBase
  9. {
  10. private readonly SqlSugarRepository<AdoS0ProductDesignCycle> _rep;
  11. private readonly SqlSugarRepository<AdoS0ItemMaster> _itemMasterRep;
  12. public AdoS0ProductDesignCyclesController(
  13. SqlSugarRepository<AdoS0ProductDesignCycle> rep,
  14. SqlSugarRepository<AdoS0ItemMaster> itemMasterRep)
  15. {
  16. _rep = rep;
  17. _itemMasterRep = itemMasterRep;
  18. }
  19. [HttpGet]
  20. public async Task<IActionResult> GetPagedAsync([FromQuery] AdoS0ProductDesignCycleQueryDto q)
  21. {
  22. if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!;
  23. (q.Page, q.PageSize) = PagingGuard.Normalize(q.Page, q.PageSize);
  24. var query = _rep.AsQueryable()
  25. .Where(x => x.TenantId == tenantId)
  26. .WhereIF(q.CompanyRefId.HasValue, x => x.CompanyRefId == q.CompanyRefId!.Value)
  27. .WhereIF(q.FactoryRefId.HasValue, x => x.FactoryRefId == q.FactoryRefId!.Value)
  28. .WhereIF(!string.IsNullOrWhiteSpace(q.DomainCode), x => x.DomainCode == q.DomainCode)
  29. .WhereIF(!string.IsNullOrWhiteSpace(q.ItemType), x => x.ItemType == q.ItemType)
  30. .WhereIF(!string.IsNullOrWhiteSpace(q.OwnerApplication), x => x.OwnerApplication == q.OwnerApplication)
  31. .WhereIF(q.IsActive.HasValue, x => x.IsActive == q.IsActive!.Value);
  32. var total = await query.CountAsync();
  33. var entities = await query
  34. .OrderByDescending(x => x.CreateTime)
  35. .Skip((q.Page - 1) * q.PageSize)
  36. .Take(q.PageSize)
  37. .ToListAsync();
  38. var list = await BuildRowsWithItemMasterMatchAsync(entities);
  39. return Ok(new { total, page = q.Page, pageSize = q.PageSize, list });
  40. }
  41. /// <summary>
  42. /// 在 PDC 实体基础上附带 ItemMaster 命中校验信息。
  43. /// 统计口径:
  44. /// - ItemMaster.ItemType = PDC.item_type;
  45. /// - PDC.domain_code 有值 → ItemMaster.Domain = domain_code;为 NULL → 不限 Domain;
  46. /// - OwnerApplication / Status 均不参与过滤。
  47. /// 实现:2 次查询(PDC 主列表上游已查 + ItemMaster 按 ItemType IN 一次性拉取相关列),
  48. /// 内存按 (ItemType, Domain) 分组计算 count / top3,避免 N+1。
  49. /// </summary>
  50. private async Task<List<AdoS0ProductDesignCycleRowDto>> BuildRowsWithItemMasterMatchAsync(
  51. List<AdoS0ProductDesignCycle> entities)
  52. {
  53. if (entities.Count == 0) return new List<AdoS0ProductDesignCycleRowDto>();
  54. var distinctItemTypes = entities
  55. .Select(e => e.ItemType)
  56. .Where(t => !string.IsNullOrWhiteSpace(t))
  57. .Distinct()
  58. .ToList();
  59. // 候选 ItemMaster 行(仅当前页相关 ItemType);空集合不发起查询。
  60. var candidates = distinctItemTypes.Count == 0
  61. ? new List<AdoS0ItemMaster>()
  62. : await _itemMasterRep.AsQueryable()
  63. .Where(x => distinctItemTypes.Contains(x.ItemType!))
  64. .Select(x => new AdoS0ItemMaster
  65. {
  66. Id = x.Id,
  67. ItemType = x.ItemType,
  68. DomainCode = x.DomainCode,
  69. ItemNum = x.ItemNum,
  70. Descr = x.Descr,
  71. })
  72. .ToListAsync();
  73. return entities.Select(e =>
  74. {
  75. var matched = candidates.Where(c =>
  76. c.ItemType == e.ItemType &&
  77. (string.IsNullOrWhiteSpace(e.DomainCode) || c.DomainCode == e.DomainCode))
  78. .ToList();
  79. var samples = matched
  80. .OrderBy(c => c.Id)
  81. .Take(3)
  82. .Select(c => string.IsNullOrWhiteSpace(c.Descr) ? c.ItemNum : $"{c.ItemNum} / {c.Descr}")
  83. .ToList();
  84. return new AdoS0ProductDesignCycleRowDto
  85. {
  86. Id = e.Id,
  87. CompanyRefId = e.CompanyRefId,
  88. FactoryRefId = e.FactoryRefId,
  89. DomainCode = e.DomainCode,
  90. ItemType = e.ItemType,
  91. OwnerApplication = e.OwnerApplication,
  92. StdHours = e.StdHours,
  93. IsActive = e.IsActive,
  94. CreateUser = e.CreateUser,
  95. CreateTime = e.CreateTime,
  96. UpdateUser = e.UpdateUser,
  97. UpdateTime = e.UpdateTime,
  98. ItemMasterMatchedCount = matched.Count,
  99. ItemMasterSampleItems = samples,
  100. };
  101. }).ToList();
  102. }
  103. [HttpGet("{id:long}")]
  104. public async Task<IActionResult> GetAsync(long id)
  105. {
  106. if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!;
  107. var item = await _rep.AsQueryable().Where(x => x.Id == id && x.TenantId == tenantId).FirstAsync();
  108. return item == null ? NotFound() : Ok(item);
  109. }
  110. [HttpPost]
  111. public async Task<IActionResult> CreateAsync([FromBody] AdoS0ProductDesignCycleUpsertDto dto)
  112. {
  113. if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!;
  114. if (await _rep.IsAnyAsync(x => x.TenantId == tenantId && x.FactoryRefId == dto.FactoryRefId && x.ItemType == dto.ItemType && x.OwnerApplication == dto.OwnerApplication))
  115. return AdoS0ApiErrors.Conflict(AdoS0ErrorCodes.DuplicateCode, "该工厂下此物料类型+属性组合已存在");
  116. var entity = new AdoS0ProductDesignCycle
  117. {
  118. TenantId = tenantId,
  119. CompanyRefId = dto.CompanyRefId,
  120. FactoryRefId = dto.FactoryRefId,
  121. DomainCode = dto.DomainCode,
  122. ItemType = dto.ItemType,
  123. OwnerApplication = dto.OwnerApplication,
  124. StdHours = dto.StdHours,
  125. IsActive = dto.IsActive,
  126. CreateUser = dto.CreateUser,
  127. CreateTime = DateTime.Now,
  128. };
  129. await _rep.AsInsertable(entity).ExecuteReturnEntityAsync();
  130. return Ok(entity);
  131. }
  132. [HttpPut("{id:long}")]
  133. public async Task<IActionResult> UpdateAsync(long id, [FromBody] AdoS0ProductDesignCycleUpsertDto dto)
  134. {
  135. if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!;
  136. var entity = await _rep.AsQueryable().Where(x => x.Id == id && x.TenantId == tenantId).FirstAsync();
  137. if (entity == null) return NotFound();
  138. if (await _rep.IsAnyAsync(x => x.TenantId == tenantId && x.Id != id && x.FactoryRefId == dto.FactoryRefId && x.ItemType == dto.ItemType && x.OwnerApplication == dto.OwnerApplication))
  139. return AdoS0ApiErrors.Conflict(AdoS0ErrorCodes.DuplicateCode, "该工厂下此物料类型+属性组合已存在");
  140. entity.CompanyRefId = dto.CompanyRefId;
  141. entity.FactoryRefId = dto.FactoryRefId;
  142. entity.DomainCode = dto.DomainCode;
  143. entity.ItemType = dto.ItemType;
  144. entity.OwnerApplication = dto.OwnerApplication;
  145. entity.StdHours = dto.StdHours;
  146. entity.IsActive = dto.IsActive;
  147. entity.UpdateUser = dto.UpdateUser;
  148. entity.UpdateTime = DateTime.Now;
  149. await _rep.AsUpdateable(entity).ExecuteCommandAsync();
  150. return Ok(entity);
  151. }
  152. [HttpPatch("{id:long}/toggle-enabled")]
  153. public async Task<IActionResult> ToggleActiveAsync(long id, [FromBody] AdoS0ProductDesignCycleToggleActiveDto dto)
  154. {
  155. if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!;
  156. var entity = await _rep.AsQueryable().Where(x => x.Id == id && x.TenantId == tenantId).FirstAsync();
  157. if (entity == null) return NotFound();
  158. entity.IsActive = dto.IsActive;
  159. entity.UpdateTime = DateTime.Now;
  160. await _rep.AsUpdateable(entity).ExecuteCommandAsync();
  161. return Ok(entity);
  162. }
  163. [HttpDelete("{id:long}")]
  164. public async Task<IActionResult> DeleteAsync(long id)
  165. {
  166. if (!AdoS0TenantScope.TryResolveRequired(out var tenantId, out var tenantError)) return tenantError!;
  167. var item = await _rep.AsQueryable().Where(x => x.Id == id && x.TenantId == tenantId).FirstAsync();
  168. if (item == null) return NotFound();
  169. await _rep.DeleteAsync(item);
  170. return Ok(new { message = "删除成功" });
  171. }
  172. }