ShopCalendarWorkCtrService.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. namespace Admin.NET.Plugin.AiDOP.Production;
  2. /// <summary>
  3. /// 产线工作日历(ShopCalendarWorkCtr)
  4. /// 路由前缀:/api/Production/shop-calendar/...
  5. /// </summary>
  6. [ApiDescriptionSettings(Order = 267, Description = "产线工作日历")]
  7. [Route("api/Production")]
  8. [AllowAnonymous]
  9. [NonUnify]
  10. public class ShopCalendarWorkCtrService : IDynamicApiController, ITransient
  11. {
  12. private readonly ISqlSugarClient _db;
  13. private readonly UserManager _userManager;
  14. public ShopCalendarWorkCtrService(ISqlSugarClient db, UserManager userManager)
  15. {
  16. _db = db;
  17. _userManager = userManager;
  18. }
  19. [DisplayName("产线工作日历列表")]
  20. [HttpGet("shop-calendar/list")]
  21. public async Task<object> GetList([FromQuery] ShopCalendarWorkCtrListInput input)
  22. {
  23. var pars = new List<SugarParameter>();
  24. var where = "sc.IsActive = 1";
  25. if (!string.IsNullOrWhiteSpace(input.ProdLine))
  26. {
  27. where += " AND sc.ProdLine = @ProdLine";
  28. pars.Add(new SugarParameter("@ProdLine", input.ProdLine.Trim()));
  29. }
  30. var orderClause = ResolveOrder(input.OrderBy, input.Sort);
  31. var offset = (input.Page - 1) * input.PageSize;
  32. var baseSql = $"""
  33. SELECT
  34. sc.ProdLine,
  35. sc.Ufld1 AS Descr,
  36. CASE sc.WeekDay
  37. WHEN 0 THEN '星期天'
  38. WHEN 1 THEN '星期一'
  39. WHEN 2 THEN '星期二'
  40. WHEN 3 THEN '星期三'
  41. WHEN 4 THEN '星期四'
  42. WHEN 5 THEN '星期五'
  43. WHEN 6 THEN '星期六'
  44. ELSE CAST(sc.WeekDay AS CHAR)
  45. END AS WeekDayName,
  46. REPLACE(CAST(CAST(sc.ShiftsStart1 AS DECIMAL(13,2)) AS CHAR), '.', ':') AS ShiftsStart1,
  47. sc.ShiftsHours1 AS ShiftsHours1,
  48. REPLACE(CAST(CAST(IFNULL(sc.ShiftsStart2,0) AS DECIMAL(13,2)) AS CHAR), '.', ':') AS ShiftsStart2,
  49. sc.ShiftsHours2 AS ShiftsHours2,
  50. sc.RecID AS Id,
  51. CASE WHEN sc.WeekDay = 0 THEN 7 ELSE sc.WeekDay END AS number,
  52. sc.WeekDay AS WeekDay,
  53. sc.`Domain` AS Domain,
  54. sc.Site AS Site,
  55. sc.WorkCtr AS WorkCtr
  56. FROM ShopCalendarWorkCtr sc
  57. WHERE {where}
  58. """;
  59. var total = await _db.Ado.GetIntAsync($"SELECT COUNT(*) FROM ({baseSql}) AS t", pars);
  60. var list = await _db.Ado.SqlQueryAsync<ShopCalendarListRow>(
  61. $"SELECT * FROM ({baseSql}) AS t ORDER BY {orderClause} LIMIT {input.PageSize} OFFSET {offset}", pars);
  62. return new { total, page = input.Page, pageSize = input.PageSize, list };
  63. }
  64. [DisplayName("生产线下拉(LineMaster)")]
  65. [HttpGet("shop-calendar/lines")]
  66. public async Task<object> GetLines([FromQuery] string? domain)
  67. {
  68. var pars = new List<SugarParameter>();
  69. var where = "IsActive = 1";
  70. if (!string.IsNullOrWhiteSpace(domain))
  71. {
  72. where += " AND `Domain` = @Domain";
  73. pars.Add(new SugarParameter("@Domain", domain.Trim()));
  74. }
  75. var rows = await _db.Ado.SqlQueryAsync<LineKvRow>(
  76. $"SELECT DISTINCT `Line` AS Value, `Line` AS Label FROM LineMaster WHERE {where} ORDER BY `Line`", pars);
  77. return new { list = rows };
  78. }
  79. [DisplayName("产线工作日历详情")]
  80. [HttpGet("shop-calendar/{id:long}")]
  81. public async Task<object> GetDetail(long id)
  82. {
  83. var row = (await _db.Ado.SqlQueryAsync<ShopCalendarDetailRow>(
  84. """
  85. SELECT RecID AS Id, ProdLine, WeekDay, ShiftsStart1, ShiftsHours1, ShiftsStart2, ShiftsHours2,
  86. ShiftsStart3, ShiftsHours3, ShiftsStart4, ShiftsHours4,
  87. Ufld1 AS Descr, `Domain`, Site, WorkCtr, TeamType, AllowOverTime, IsWorkDay
  88. FROM ShopCalendarWorkCtr
  89. WHERE RecID = @Id
  90. """,
  91. new List<SugarParameter> { new("@Id", id) })).FirstOrDefault()
  92. ?? throw Oops.Oh("记录不存在");
  93. row.ShiftsStart1Text = DecimalHoursToTimeString(row.ShiftsStart1);
  94. row.ShiftsStart2Text = DecimalHoursToTimeString(row.ShiftsStart2);
  95. return row;
  96. }
  97. /// <summary>保存 POST /api/Production/shop-calendar/save</summary>
  98. [DisplayName("保存产线工作日历")]
  99. [HttpPost("shop-calendar/save")]
  100. public async Task<object> Save([FromBody] ShopCalendarWorkCtrSaveInput input)
  101. {
  102. var account = Truncate(_userManager.Account ?? "system", 8);
  103. var now = DateTime.Now;
  104. var domain = Truncate(input.Domain.Trim(), 8);
  105. var site = Truncate(input.Site.Trim(), 8);
  106. var prodLine = Truncate(input.ProdLine.Trim(), 8);
  107. var workCtr = string.IsNullOrWhiteSpace(input.WorkCtr) ? null : Truncate(input.WorkCtr.Trim(), 8);
  108. var ufld1 = string.IsNullOrWhiteSpace(input.Ufld1) ? null : Truncate(input.Ufld1.Trim(), 8);
  109. var s1 = NormalizeShiftDecimal(ParseTimeToDecimal(input.ShiftsStart1));
  110. var s2 = NormalizeShiftDecimal(ParseTimeToDecimal(input.ShiftsStart2));
  111. var h1 = input.ShiftsHours1 ?? 0m;
  112. var h2 = input.ShiftsHours2 ?? 0m;
  113. if (input.Id is null or 0)
  114. {
  115. var dup = await _db.Ado.GetIntAsync(
  116. """
  117. SELECT COUNT(*) FROM ShopCalendarWorkCtr
  118. WHERE IsActive = 1 AND ProdLine = @ProdLine AND WeekDay = @WeekDay
  119. """,
  120. new List<SugarParameter>
  121. {
  122. new("@ProdLine", prodLine),
  123. new("@WeekDay", input.WeekDay)
  124. });
  125. if (dup > 0)
  126. throw Oops.Oh("该生产线与星期组合已存在");
  127. await _db.Ado.ExecuteCommandAsync(
  128. """
  129. INSERT INTO ShopCalendarWorkCtr (
  130. `Domain`, `Site`, `ProdLine`, `WeekDay`, `WorkCtr`, `Ufld1`,
  131. ShiftsStart1, ShiftsHours1, ShiftsStart2, ShiftsHours2,
  132. ShiftsStart3, ShiftsHours3, ShiftsStart4, ShiftsHours4,
  133. AllowOverTime, IsChanged, IsWorkDay,
  134. IsActive, IsConfirm, BusinessID,
  135. CreateUser, CreateTime, UpdateUser, UpdateTime
  136. ) VALUES (
  137. @Domain, @Site, @ProdLine, @WeekDay, @WorkCtr, @Ufld1,
  138. @S1, @H1, @S2, @H2,
  139. 0, 0, 0, 0,
  140. 0, 0, 1,
  141. 1, 0, 0,
  142. @User, @Now, @User, @Now
  143. )
  144. """,
  145. new List<SugarParameter>
  146. {
  147. new("@Domain", domain),
  148. new("@Site", site),
  149. new("@ProdLine", prodLine),
  150. new("@WeekDay", input.WeekDay),
  151. new("@WorkCtr", (object?)workCtr ?? DBNull.Value),
  152. new("@Ufld1", (object?)ufld1 ?? DBNull.Value),
  153. new("@S1", s1),
  154. new("@H1", h1),
  155. new("@S2", s2),
  156. new("@H2", h2),
  157. new("@User", account),
  158. new("@Now", now)
  159. });
  160. var newId = await _db.Ado.GetIntAsync("SELECT LAST_INSERT_ID()");
  161. return new { id = (long)newId, message = "新增成功" };
  162. }
  163. var n = await _db.Ado.ExecuteCommandAsync(
  164. """
  165. UPDATE ShopCalendarWorkCtr
  166. SET `Domain` = @Domain,
  167. `Site` = @Site,
  168. ProdLine = @ProdLine,
  169. WeekDay = @WeekDay,
  170. WorkCtr = @WorkCtr,
  171. Ufld1 = @Ufld1,
  172. ShiftsStart1 = @S1,
  173. ShiftsHours1 = @H1,
  174. ShiftsStart2 = @S2,
  175. ShiftsHours2 = @H2,
  176. UpdateUser = @User,
  177. UpdateTime = @Now
  178. WHERE RecID = @Id
  179. """,
  180. new List<SugarParameter>
  181. {
  182. new("@Id", input.Id!.Value),
  183. new("@Domain", domain),
  184. new("@Site", site),
  185. new("@ProdLine", prodLine),
  186. new("@WeekDay", input.WeekDay),
  187. new("@WorkCtr", (object?)workCtr ?? DBNull.Value),
  188. new("@Ufld1", (object?)ufld1 ?? DBNull.Value),
  189. new("@S1", s1),
  190. new("@H1", h1),
  191. new("@S2", s2),
  192. new("@H2", h2),
  193. new("@User", account),
  194. new("@Now", now)
  195. });
  196. if (n == 0)
  197. throw Oops.Oh("记录不存在或未变更");
  198. return new { id = input.Id, message = "保存成功" };
  199. }
  200. [DisplayName("删除产线工作日历")]
  201. [HttpPost("shop-calendar/delete/{id:long}")]
  202. public async Task<object> Delete(long id)
  203. {
  204. var n = await _db.Ado.ExecuteCommandAsync(
  205. "UPDATE ShopCalendarWorkCtr SET IsActive = 0, UpdateTime = @Now WHERE RecID = @Id",
  206. new List<SugarParameter> { new("@Id", id), new("@Now", DateTime.Now) });
  207. if (n == 0)
  208. throw Oops.Oh("记录不存在");
  209. return new { message = "已删除" };
  210. }
  211. private static string Truncate(string s, int maxLen)
  212. {
  213. if (string.IsNullOrEmpty(s)) return s;
  214. return s.Length <= maxLen ? s : s[..maxLen];
  215. }
  216. private static string ResolveOrder(string? orderBy, string? sort)
  217. {
  218. var desc = string.Equals(sort?.Trim(), "desc", StringComparison.OrdinalIgnoreCase);
  219. var dir = desc ? "DESC" : "ASC";
  220. var key = orderBy?.Trim().ToLowerInvariant();
  221. var col = key switch
  222. {
  223. "prodline" => "t.ProdLine",
  224. "weekdayname" => "t.number",
  225. _ => "t.number"
  226. };
  227. return $"{col} {dir}, t.Id";
  228. }
  229. private static decimal? ParseTimeToDecimal(string? s)
  230. {
  231. if (string.IsNullOrWhiteSpace(s)) return null;
  232. var p = s.Trim().Split(':');
  233. if (p.Length < 2 || !int.TryParse(p[0], out var h) || !int.TryParse(p[1], out var m))
  234. return null;
  235. return h + m / 60m;
  236. }
  237. private static decimal NormalizeShiftDecimal(decimal? value)
  238. {
  239. if (value is null) return 0m;
  240. // DB 列是 decimal(10,5),避免循环小数导致超精度写入报错。
  241. return Math.Round(value.Value, 5, MidpointRounding.AwayFromZero);
  242. }
  243. private static string? DecimalHoursToTimeString(decimal? d)
  244. {
  245. if (d is null || d.Value == 0) return null;
  246. var total = (double)d.Value;
  247. var h = (int)Math.Floor(total);
  248. var m = (int)Math.Round((total - h) * 60);
  249. if (m >= 60) { h++; m = 0; }
  250. return $"{h:D2}:{m:D2}";
  251. }
  252. private sealed class ShopCalendarListRow
  253. {
  254. public string? ProdLine { get; set; }
  255. public string? Descr { get; set; }
  256. public string? WeekDayName { get; set; }
  257. public string? ShiftsStart1 { get; set; }
  258. public decimal? ShiftsHours1 { get; set; }
  259. public string? ShiftsStart2 { get; set; }
  260. public decimal? ShiftsHours2 { get; set; }
  261. public long Id { get; set; }
  262. public int number { get; set; }
  263. public int WeekDay { get; set; }
  264. public string? Domain { get; set; }
  265. public string? Site { get; set; }
  266. public string? WorkCtr { get; set; }
  267. }
  268. private sealed class LineKvRow
  269. {
  270. public string? Value { get; set; }
  271. public string? Label { get; set; }
  272. }
  273. private sealed class ShopCalendarDetailRow
  274. {
  275. public long Id { get; set; }
  276. public string? ProdLine { get; set; }
  277. public int WeekDay { get; set; }
  278. public decimal? ShiftsStart1 { get; set; }
  279. public decimal? ShiftsHours1 { get; set; }
  280. public decimal? ShiftsStart2 { get; set; }
  281. public decimal? ShiftsHours2 { get; set; }
  282. public decimal? ShiftsStart3 { get; set; }
  283. public decimal? ShiftsHours3 { get; set; }
  284. public decimal? ShiftsStart4 { get; set; }
  285. public decimal? ShiftsHours4 { get; set; }
  286. public string? Descr { get; set; }
  287. public string? Domain { get; set; }
  288. public string? Site { get; set; }
  289. public string? WorkCtr { get; set; }
  290. public string? TeamType { get; set; }
  291. public decimal? AllowOverTime { get; set; }
  292. public bool? IsWorkDay { get; set; }
  293. public string? ShiftsStart1Text { get; set; }
  294. public string? ShiftsStart2Text { get; set; }
  295. }
  296. }