QualityLineWorkDetailService.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. namespace Admin.NET.Plugin.AiDOP.Production;
  2. /// <summary>
  3. /// 产线休息时间(QualityLineWorkDetail)
  4. /// 路由前缀:/api/Production/line-rest/...
  5. ///
  6. /// 【租户安全边界】租户一律经 <see cref="AidopTenantScope.ResolveOrThrow"/> 从认证后 JWT 解析,
  7. /// 无有效租户即拒绝,不读前端 tenantId、无默认回退(原实现:类级 <c>[AllowAnonymous]</c> + 宽松解析,
  8. /// 未认证方可携带 <c>?tenantId=&lt;任意租户&gt;</c> 读写该租户数据)。
  9. /// </summary>
  10. [ApiDescriptionSettings(Order = 268, Description = "产线休息时间")]
  11. [Route("api/Production")]
  12. [NonUnify]
  13. public class QualityLineWorkDetailService : IDynamicApiController, ITransient
  14. {
  15. private readonly ISqlSugarClient _db;
  16. private readonly UserManager _userManager;
  17. public QualityLineWorkDetailService(ISqlSugarClient db, UserManager userManager)
  18. {
  19. _db = db;
  20. _userManager = userManager;
  21. }
  22. [DisplayName("产线休息时间列表")]
  23. [HttpGet("line-rest/list")]
  24. public async Task<object> GetList([FromQuery] QualityLineRestListInput input)
  25. {
  26. var tenantId = AidopTenantScope.ResolveOrThrow(_userManager);
  27. var pars = new List<SugarParameter> { new("@TenantId", tenantId) };
  28. var where = "qd.IsActive = 1 AND qd.tenant_id = @TenantId";
  29. if (!string.IsNullOrWhiteSpace(input.ProdLine))
  30. {
  31. where += " AND qd.ProdLine = @ProdLine";
  32. pars.Add(new SugarParameter("@ProdLine", input.ProdLine.Trim()));
  33. }
  34. var orderClause = ResolveOrder(input.OrderBy, input.Sort);
  35. var offset = (input.Page - 1) * input.PageSize;
  36. var baseSql = $"""
  37. SELECT
  38. qd.ProdLine AS ProdLine,
  39. lm.line_describe AS LineDesc,
  40. qd.Line AS Line,
  41. qd.RestTimePoint AS RestTimePoint,
  42. qd.RestTime AS RestTime,
  43. qd.RecID AS Id,
  44. qd.Descr1 AS Descr1,
  45. qd.EndDateTime AS EndDateTime,
  46. qd.Ufld1 AS Remark,
  47. qd.`Domain` AS Domain,
  48. qd.Site AS Site
  49. FROM QualityLineWorkDetail qd
  50. LEFT JOIN LineMaster lm
  51. ON qd.`Domain` COLLATE utf8mb4_general_ci = lm.`Domain` COLLATE utf8mb4_general_ci
  52. AND qd.ProdLine COLLATE utf8mb4_general_ci = lm.`Line` COLLATE utf8mb4_general_ci
  53. AND lm.tenant_id = @TenantId
  54. WHERE {where}
  55. """;
  56. var total = await _db.Ado.GetIntAsync($"SELECT COUNT(*) FROM ({baseSql}) AS t", pars);
  57. var list = await _db.Ado.SqlQueryAsync<QualityLineRestListRow>(
  58. $"SELECT * FROM ({baseSql}) AS t ORDER BY {orderClause} LIMIT {input.PageSize} OFFSET {offset}", pars);
  59. return new { total, page = input.Page, pageSize = input.PageSize, list };
  60. }
  61. [DisplayName("产线休息时间详情")]
  62. [HttpGet("line-rest/{id:long}")]
  63. public async Task<object> GetDetail(long id)
  64. {
  65. var tenantId = AidopTenantScope.ResolveOrThrow(_userManager);
  66. var row = (await _db.Ado.SqlQueryAsync<QualityLineRestDetailRow>(
  67. """
  68. SELECT RecID AS Id, `Domain`, Site, ProdLine, WorkShift, Line, RestTimePoint, RestTime,
  69. Ufld1 AS Remark, Descr1, Descr2, Period, EndDateTime
  70. FROM QualityLineWorkDetail
  71. WHERE RecID = @Id AND tenant_id = @TenantId
  72. """,
  73. new List<SugarParameter> { new("@Id", id), new("@TenantId", tenantId) })).FirstOrDefault()
  74. ?? throw Oops.Oh("记录不存在");
  75. return row;
  76. }
  77. [DisplayName("保存产线休息时间")]
  78. [HttpPost("line-rest/save")]
  79. public async Task<object> Save([FromBody] QualityLineRestSaveInput input)
  80. {
  81. var account = Truncate8(_userManager.Account ?? "system");
  82. var now = DateTime.Now;
  83. var domain = Truncate(input.Domain.Trim(), 8);
  84. var site = Truncate((input.Site ?? input.Domain).Trim(), 8);
  85. var prodLine = Truncate(input.ProdLine.Trim(), 12);
  86. var restPoint = Truncate(input.RestTimePoint.Trim(), 30);
  87. var remark = string.IsNullOrWhiteSpace(input.Remark) ? null : Truncate(input.Remark.Trim(), 8);
  88. const string defaultWorkShift = "-";
  89. const string defaultEnd = "2999-12-31";
  90. var tenantId = AidopTenantScope.ResolveOrThrow(_userManager);
  91. // 同一产线 + 相同休息开始时间 重复校验(编辑时排除自身)
  92. var dupWhere = "ProdLine = @ProdLine AND RestTimePoint = @RestTimePoint AND IsActive = 1 AND tenant_id = @TenantId";
  93. var dupPars = new List<SugarParameter>
  94. {
  95. new("@ProdLine", prodLine),
  96. new("@RestTimePoint", restPoint),
  97. new("@TenantId", tenantId)
  98. };
  99. if (input.Id is not null and not 0)
  100. {
  101. dupWhere += " AND RecID <> @ExcludeId";
  102. dupPars.Add(new SugarParameter("@ExcludeId", input.Id.Value));
  103. }
  104. var dupCount = await _db.Ado.GetIntAsync(
  105. $"SELECT COUNT(*) FROM QualityLineWorkDetail WHERE {dupWhere}", dupPars);
  106. if (dupCount > 0)
  107. throw Oops.Oh("休息时段与已有配置重叠");
  108. if (input.Id is null or 0)
  109. {
  110. await _db.Ado.ExecuteCommandAsync(
  111. """
  112. INSERT INTO QualityLineWorkDetail (
  113. `Domain`, Site, ProdLine, WorkShift, Line, RestTimePoint, RestTime, Descr1, Descr2, Period, EndDateTime, Ufld1,
  114. IsActive, IsConfirm, BusinessID,
  115. CreateUser, CreateTime, UpdateUser, UpdateTime, tenant_id
  116. ) VALUES (
  117. @Domain, @Site, @ProdLine, @WorkShift, 0, @RestTimePoint, @RestTime, NULL, NULL, 0, @EndDateTime, @Ufld1,
  118. 1, 0, 0,
  119. @User, @Now, @User, @Now, @TenantId
  120. )
  121. """,
  122. new List<SugarParameter>
  123. {
  124. new("@Domain", domain),
  125. new("@Site", site),
  126. new("@ProdLine", prodLine),
  127. new("@WorkShift", defaultWorkShift),
  128. new("@RestTimePoint", restPoint),
  129. new("@RestTime", input.RestTime),
  130. new("@EndDateTime", defaultEnd),
  131. new("@Ufld1", (object?)remark ?? DBNull.Value),
  132. new("@User", account),
  133. new("@Now", now),
  134. new("@TenantId", tenantId)
  135. });
  136. var newId = await _db.Ado.GetIntAsync("SELECT LAST_INSERT_ID()");
  137. return new { id = (long)newId, message = "新增成功" };
  138. }
  139. var n = await _db.Ado.ExecuteCommandAsync(
  140. """
  141. UPDATE QualityLineWorkDetail
  142. SET `Domain` = @Domain,
  143. Site = @Site,
  144. ProdLine = @ProdLine,
  145. RestTimePoint = @RestTimePoint,
  146. RestTime = @RestTime,
  147. Ufld1 = @Ufld1,
  148. UpdateUser = @User,
  149. UpdateTime = @Now
  150. WHERE RecID = @Id AND tenant_id = @TenantId
  151. """,
  152. new List<SugarParameter>
  153. {
  154. new("@Id", input.Id!.Value),
  155. new("@Domain", domain),
  156. new("@Site", site),
  157. new("@ProdLine", prodLine),
  158. new("@RestTimePoint", restPoint),
  159. new("@RestTime", input.RestTime),
  160. new("@Ufld1", (object?)remark ?? DBNull.Value),
  161. new("@User", account),
  162. new("@Now", now),
  163. new("@TenantId", tenantId)
  164. });
  165. if (n == 0)
  166. throw Oops.Oh("记录不存在或未变更");
  167. return new { id = input.Id, message = "保存成功" };
  168. }
  169. [DisplayName("删除产线休息时间")]
  170. [HttpPost("line-rest/delete/{id:long}")]
  171. public async Task<object> Delete(long id)
  172. {
  173. var tenantId = AidopTenantScope.ResolveOrThrow(_userManager);
  174. var n = await _db.Ado.ExecuteCommandAsync(
  175. "UPDATE QualityLineWorkDetail SET IsActive = 0, UpdateTime = @Now WHERE RecID = @Id AND tenant_id = @TenantId",
  176. new List<SugarParameter> { new("@Id", id), new("@Now", DateTime.Now), new("@TenantId", tenantId) });
  177. if (n == 0)
  178. throw Oops.Oh("记录不存在");
  179. return new { message = "已删除" };
  180. }
  181. [DisplayName("生产线列表(含 Domain)")]
  182. [HttpGet("line-rest/lines")]
  183. public async Task<object> GetLines([FromQuery] string? domain)
  184. {
  185. var tenantId = AidopTenantScope.ResolveOrThrow(_userManager);
  186. var pars = new List<SugarParameter> { new("@TenantId", tenantId) };
  187. var where = "IsActive = 1 AND tenant_id = @TenantId";
  188. if (!string.IsNullOrWhiteSpace(domain))
  189. {
  190. where += " AND `Domain` = @Domain";
  191. pars.Add(new SugarParameter("@Domain", domain.Trim()));
  192. }
  193. var rows = await _db.Ado.SqlQueryAsync<LineWithDomainRow>(
  194. $"""
  195. SELECT `Line` AS Value, `Line` AS Label, `Domain` AS Extra
  196. FROM LineMaster
  197. WHERE {where}
  198. ORDER BY `Domain`, `Line`
  199. """,
  200. pars);
  201. return new { list = rows };
  202. }
  203. private static string Truncate(string s, int maxLen) =>
  204. s.Length <= maxLen ? s : s[..maxLen];
  205. private static string Truncate8(string s) => Truncate(s, 8);
  206. private static string ResolveOrder(string? orderBy, string? sort)
  207. {
  208. var desc = string.Equals(sort?.Trim(), "desc", StringComparison.OrdinalIgnoreCase);
  209. var dir = desc ? "DESC" : "ASC";
  210. var key = orderBy?.Trim().ToLowerInvariant();
  211. var col = key switch
  212. {
  213. "prodline" => "t.ProdLine",
  214. "resttimepoint" => "t.RestTimePoint",
  215. "remark" => "t.Remark",
  216. _ => "t.Id"
  217. };
  218. return $"{col} {dir}";
  219. }
  220. private sealed class QualityLineRestListRow
  221. {
  222. public string? ProdLine { get; set; }
  223. public string? LineDesc { get; set; }
  224. public int? Line { get; set; }
  225. public string? RestTimePoint { get; set; }
  226. public decimal? RestTime { get; set; }
  227. public long Id { get; set; }
  228. public string? Descr1 { get; set; }
  229. public string? EndDateTime { get; set; }
  230. public string? Remark { get; set; }
  231. public string? Domain { get; set; }
  232. public string? Site { get; set; }
  233. }
  234. private sealed class QualityLineRestDetailRow
  235. {
  236. public long Id { get; set; }
  237. public string? Domain { get; set; }
  238. public string? Site { get; set; }
  239. public string? ProdLine { get; set; }
  240. public string? WorkShift { get; set; }
  241. public int? Line { get; set; }
  242. public string? RestTimePoint { get; set; }
  243. public decimal? RestTime { get; set; }
  244. public string? Remark { get; set; }
  245. public string? Descr1 { get; set; }
  246. public string? Descr2 { get; set; }
  247. public string? Period { get; set; }
  248. public string? EndDateTime { get; set; }
  249. }
  250. private sealed class LineWithDomainRow
  251. {
  252. public string? Value { get; set; }
  253. public string? Label { get; set; }
  254. public string? Extra { get; set; }
  255. }
  256. }