AdoS0ProductDesignCyclesController.cs 7.4 KB

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