BatchService.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. using Model;
  2. using Newtonsoft.Json;
  3. using System;
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. using System.Data;
  7. using System.Data.SQLite;
  8. using System.Linq;
  9. using System.Security.Cryptography;
  10. using System.Text;
  11. using System.Threading.Tasks;
  12. public enum BatchOperationType
  13. {
  14. Create,
  15. Update,
  16. Delete,
  17. Finish
  18. }
  19. public class BatchService
  20. {
  21. public static bool CreateBatchLog(string batchID, User user, out string errorMessage)
  22. {
  23. errorMessage = null;
  24. if(string.IsNullOrEmpty(batchID))
  25. {
  26. errorMessage = "批次号不能为空";
  27. return false;
  28. }
  29. using (var conn = new SQLiteConnection(DatabaseHelper.ConnectionString))
  30. {
  31. conn.Open();
  32. // 检查是否存在未结束的批次
  33. string checkUnfinishedSql = @"
  34. SELECT COUNT(*)
  35. FROM BatchLogs
  36. WHERE EndTimestamp IS NULL";
  37. using (var cmd = new SQLiteCommand(checkUnfinishedSql, conn))
  38. {
  39. var count = (long)cmd.ExecuteScalar();
  40. if (count > 0)
  41. {
  42. errorMessage = "存在未结束的批次,不允许创建新批次。";
  43. return false;
  44. }
  45. }
  46. /*
  47. //查询最新一条批次是否已结束
  48. string checkLatestBatchSql = @"
  49. SELECT EndTimestamp
  50. FROM BatchLogs
  51. ORDER BY StartTimestamp DESC
  52. LIMIT 1";
  53. using (var cmd = new SQLiteCommand(checkLatestBatchSql, conn))
  54. {
  55. var result = cmd.ExecuteScalar();
  56. // 如果表为空(无任何批次),result 是 null → 允许创建
  57. if (result != null && result != DBNull.Value)
  58. {
  59. // 最新批次已有 EndTimestamp → 已结束,允许新建
  60. }
  61. else if (result == null)
  62. {
  63. // 表里没数据 → 允许创建第一个批次
  64. }
  65. else // result == DBNull.Value
  66. {
  67. // 最新批次的 EndTimestamp 为 NULL → 未结束,禁止新建
  68. errorMessage = "最新批次尚未结束,不允许创建新批次。";
  69. return false;
  70. }
  71. }
  72. */
  73. // 检查数据库中是否已存在这些 BatchID
  74. string checkSql = "SELECT BatchID FROM BatchLogs WHERE BatchID = @id";
  75. using (var checkCmd = new SQLiteCommand(checkSql, conn))
  76. {
  77. checkCmd.Parameters.AddWithValue($"@id", batchID);
  78. using (var reader = checkCmd.ExecuteReader())
  79. {
  80. /*
  81. var existingIds = new List<string>();
  82. while (reader.Read())
  83. {
  84. existingIds.Add(reader.GetString(0));
  85. }
  86. if (existingIds.Any())
  87. {
  88. errorMessage = $"批次号已存在,无法重复创建: {string.Join(", ", existingIds)}";
  89. return false;
  90. }
  91. */
  92. if(reader.Read())
  93. {
  94. errorMessage = $"批次号已存在,无法重复创建";
  95. return false;
  96. }
  97. }
  98. }
  99. // 2. 开始插入
  100. string insertSql = @"
  101. INSERT INTO BatchLogs (
  102. BatchID,
  103. StartTimestamp,
  104. StartUserId,
  105. StartUsername,
  106. EndTimestamp,
  107. EndUserId,
  108. EndUsername,
  109. RecipeName,
  110. Details
  111. ) VALUES (
  112. @BatchID,
  113. @StartTimestamp,
  114. @StartUserId,
  115. @StartUsername,
  116. @EndTimestamp,
  117. @EndUserId,
  118. @EndUsername,
  119. @RecipeName,
  120. @Details
  121. )";
  122. using (var tran = conn.BeginTransaction())
  123. {
  124. using (var cmd = new SQLiteCommand(insertSql, conn, tran))
  125. {
  126. // 添加参数占位符(只加一次)
  127. cmd.Parameters.Add("@BatchID", DbType.String);
  128. cmd.Parameters.Add("@StartTimestamp", DbType.DateTime);
  129. cmd.Parameters.Add("@StartUserId", DbType.Int32);
  130. cmd.Parameters.Add("@StartUsername", DbType.String);
  131. cmd.Parameters.Add("@EndTimestamp", DbType.DateTime);
  132. cmd.Parameters.Add("@EndUserId", DbType.Int32);
  133. cmd.Parameters.Add("@EndUsername", DbType.String);
  134. cmd.Parameters.Add("@RecipeName", DbType.String);
  135. cmd.Parameters.Add("@Details", DbType.String);
  136. // 更新参数值
  137. cmd.Parameters["@BatchID"].Value = batchID;
  138. cmd.Parameters["@StartTimestamp"].Value = DateTime.Now;
  139. cmd.Parameters["@StartUserId"].Value = user.Id;
  140. cmd.Parameters["@StartUsername"].Value = user.Username;
  141. cmd.Parameters["@EndTimestamp"].Value = DBNull.Value;
  142. cmd.Parameters["@EndUserId"].Value = DBNull.Value;
  143. cmd.Parameters["@EndUsername"].Value = DBNull.Value;
  144. cmd.Parameters["@RecipeName"].Value = DBNull.Value;
  145. cmd.Parameters["@Details"].Value = DBNull.Value;//JsonConvert.SerializeObject(batch)
  146. cmd.ExecuteNonQuery();
  147. }
  148. tran.Commit();
  149. }
  150. }
  151. return true;
  152. }
  153. public static bool DeleteBatchByBatchId(string batchId, out string errorMessage)
  154. {
  155. errorMessage = null;
  156. if (string.IsNullOrWhiteSpace(batchId))
  157. {
  158. errorMessage = "批次号不能为空。";
  159. return false;
  160. }
  161. using (var conn = new SQLiteConnection(DatabaseHelper.ConnectionString))
  162. {
  163. conn.Open();
  164. // 1. 检查批次是否存在
  165. string checkSql = "SELECT COUNT(1) FROM BatchLogs WHERE BatchID = @BatchID";
  166. using (var checkCmd = new SQLiteCommand(checkSql, conn))
  167. {
  168. checkCmd.Parameters.AddWithValue("@BatchID", batchId);
  169. int count = Convert.ToInt32(checkCmd.ExecuteScalar());
  170. if (count == 0)
  171. {
  172. errorMessage = $"批次号 '{batchId}' 不存在,无法删除。";
  173. return false;
  174. }
  175. }
  176. // 2. 执行删除
  177. string deleteSql = "DELETE FROM BatchLogs WHERE BatchID = @BatchID";
  178. using (var deleteCmd = new SQLiteCommand(deleteSql, conn))
  179. {
  180. deleteCmd.Parameters.AddWithValue("@BatchID", batchId);
  181. int rowsAffected = deleteCmd.ExecuteNonQuery();
  182. if (rowsAffected == 0)
  183. {
  184. errorMessage = "删除失败:记录在检查后被其他操作删除或修改。";
  185. return false;
  186. }
  187. }
  188. }
  189. return true;
  190. }
  191. public static bool GetLatestBatchID(out string batchID,out bool finished,out string errorMessage)
  192. {
  193. batchID = null;
  194. finished = false;
  195. errorMessage = null;
  196. using (var conn = new SQLiteConnection(DatabaseHelper.ConnectionString))
  197. {
  198. conn.Open();
  199. // 1. 查询最新的批次(按 StartTimestamp 倒序)
  200. string selectLatestSql = @"
  201. SELECT BatchID, EndTimestamp
  202. FROM BatchLogs
  203. ORDER BY StartTimestamp DESC
  204. LIMIT 1";
  205. object endTimestampValue;
  206. using (var cmd = new SQLiteCommand(selectLatestSql, conn))
  207. {
  208. using (var reader = cmd.ExecuteReader())
  209. {
  210. if (!reader.Read())
  211. {
  212. errorMessage = "没有批次记录";
  213. return false;
  214. }
  215. batchID = reader.GetString(0);
  216. endTimestampValue = reader["EndTimestamp"]; // 可能是 DBNull 或 DateTime
  217. }
  218. }
  219. // 2. 检查是否已结束
  220. if (endTimestampValue != DBNull.Value)
  221. {
  222. finished = true;//批次已结束
  223. }
  224. return true;
  225. }
  226. }
  227. public static bool FinishLatestBatch(Batch batch, User user, out string errorMessage)
  228. {
  229. errorMessage = null;
  230. using (var conn = new SQLiteConnection(DatabaseHelper.ConnectionString))
  231. {
  232. conn.Open();
  233. // 1. 查询最新的批次(按 StartTimestamp 倒序)
  234. string selectLatestSql = @"
  235. SELECT BatchID, EndTimestamp
  236. FROM BatchLogs
  237. ORDER BY StartTimestamp DESC
  238. LIMIT 1";
  239. string latestBatchId;
  240. object endTimestampValue;
  241. using (var cmd = new SQLiteCommand(selectLatestSql, conn))
  242. {
  243. using (var reader = cmd.ExecuteReader())
  244. {
  245. if (!reader.Read())
  246. {
  247. errorMessage = "没有批次记录,无需结束。";
  248. return false;
  249. }
  250. latestBatchId = reader.GetString(0);
  251. endTimestampValue = reader["EndTimestamp"]; // 可能是 DBNull 或 DateTime
  252. }
  253. }
  254. // 2. 检查是否已结束
  255. if (endTimestampValue != DBNull.Value)
  256. {
  257. errorMessage = $"最新批次({latestBatchId})已结束,不能重复结束。";
  258. return false;
  259. }
  260. // 3. 更新该批次为结束状态
  261. string updateSql = @"
  262. UPDATE BatchLogs
  263. SET
  264. EndTimestamp = @EndTimestamp,
  265. EndUserId = @EndUserId,
  266. EndUsername = @EndUsername,
  267. RecipeName = @RecipeName,
  268. Details=@Details
  269. WHERE BatchID = @BatchID";
  270. using (var cmd = new SQLiteCommand(updateSql, conn))
  271. {
  272. cmd.Parameters.AddWithValue("@EndTimestamp", DateTime.Now);
  273. cmd.Parameters.AddWithValue("@EndUserId", user.Id);
  274. cmd.Parameters.AddWithValue("@EndUsername", user.Username);
  275. cmd.Parameters.AddWithValue("@RecipeName", batch.RecipeName);
  276. cmd.Parameters.AddWithValue("@Details", JsonConvert.SerializeObject(batch));
  277. cmd.Parameters.AddWithValue("@BatchID", latestBatchId);
  278. int rowsAffected = cmd.ExecuteNonQuery();
  279. if (rowsAffected == 0)
  280. {
  281. errorMessage = "更新失败:批次记录在查询后被删除或修改。";
  282. return false;
  283. }
  284. batch.EndTime = DateTime.Now;
  285. }
  286. }
  287. return true;
  288. }
  289. /// <summary>
  290. /// 获得最近的批次记录,有可能是未完成,也可能是已经完成,通过Batch.Finished判断。
  291. /// </summary>
  292. /// <param name="errorMessage"></param>
  293. /// <returns></returns>
  294. public static Batch GetLatestBatch(out string errorMessage)
  295. {
  296. errorMessage = null;
  297. using (var conn = new SQLiteConnection(DatabaseHelper.ConnectionString))
  298. {
  299. conn.Open();
  300. string checkBatchCountSql = @"
  301. SELECT COUNT(*)
  302. FROM BatchLogs";
  303. using (var cmd = new SQLiteCommand(checkBatchCountSql, conn))
  304. {
  305. var count = (long)cmd.ExecuteScalar();
  306. if (count == 0)
  307. {
  308. errorMessage = "没有任何批次记录。";
  309. return null;
  310. }
  311. }
  312. string sql = @"
  313. SELECT
  314. BatchID,
  315. StartTimestamp,
  316. StartUserId,
  317. StartUsername,
  318. EndTimestamp,
  319. EndUserId,
  320. EndUsername,
  321. RecipeName,
  322. Details
  323. FROM BatchLogs
  324. ORDER BY StartTimestamp DESC
  325. LIMIT 1";
  326. using (var cmd = new SQLiteCommand(sql, conn))
  327. {
  328. using (var reader = cmd.ExecuteReader())
  329. {
  330. while (reader.Read())
  331. {
  332. // 从 Details 反序列化基础属性(如果 Details 存了完整 Batch)
  333. string detailsJson = reader["Details"] as string;
  334. Batch batch;
  335. if (!string.IsNullOrEmpty(detailsJson))
  336. {
  337. batch = JsonConvert.DeserializeObject<Batch>(detailsJson);
  338. }
  339. else
  340. {
  341. batch = new Batch(); // 或抛异常,看业务
  342. }
  343. batch.BatchID = reader["BatchID"] as string;
  344. batch.StartTime = (DateTime)reader["StartTimestamp"];
  345. batch.StartUsername = reader["StartUsername"] as string;
  346. if (reader["EndTimestamp"] != DBNull.Value)
  347. {
  348. batch.EndTime = (DateTime)reader["EndTimestamp"];
  349. batch.EndUsername = reader["EndUsername"] as string;
  350. batch.RecipeName = reader["RecipeName"] as string;
  351. batch.Finished = true;
  352. }
  353. else
  354. batch.Finished = false;
  355. return batch;
  356. }
  357. }
  358. }
  359. }
  360. return null;
  361. }
  362. public static List<Batch> GetFinishedBatches(DateTime startTime,DateTime endTime,string batchIdFilter = null,string recipeNameFilter = null)
  363. {
  364. var batches = new List<Batch>();
  365. if (startTime > endTime)
  366. throw new ArgumentException("开始时间不能晚于结束时间。");
  367. using (var conn = new SQLiteConnection(DatabaseHelper.ConnectionString))
  368. {
  369. conn.Open();
  370. // 构建动态 WHERE 条件
  371. var whereConditions = new List<string> { "EndTimestamp IS NOT NULL" };
  372. var parameters = new List<(string name, object value)>();
  373. // 时间范围(始终存在)
  374. whereConditions.Add("EndTimestamp BETWEEN @Start AND @End");
  375. parameters.Add(("@Start", startTime.ToString("yyyy-MM-dd HH:mm:ss")));
  376. parameters.Add(("@End", endTime.ToString("yyyy-MM-dd HH:mm:ss")));
  377. // 批次号模糊匹配(可选)
  378. if (!string.IsNullOrWhiteSpace(batchIdFilter))
  379. {
  380. whereConditions.Add("BatchID LIKE @BatchId");
  381. parameters.Add(("@BatchId", $"%{batchIdFilter}%"));
  382. }
  383. // RecipeName 模糊匹配(可选)
  384. if (!string.IsNullOrWhiteSpace(recipeNameFilter))
  385. {
  386. whereConditions.Add("RecipeName LIKE @RecipeName");
  387. parameters.Add(("@RecipeName", $"%{recipeNameFilter}%"));
  388. }
  389. string sql = $@"
  390. SELECT
  391. BatchID,
  392. StartTimestamp,
  393. StartUsername,
  394. EndTimestamp,
  395. EndUsername,
  396. RecipeName,
  397. Details
  398. FROM BatchLogs
  399. WHERE {string.Join(" AND ", whereConditions)}
  400. ORDER BY EndTimestamp DESC";
  401. using (var cmd = new SQLiteCommand(sql, conn))
  402. {
  403. // 添加所有参数
  404. foreach (var (name, value) in parameters)
  405. {
  406. cmd.Parameters.AddWithValue(name, value);
  407. }
  408. using (var reader = cmd.ExecuteReader())
  409. {
  410. while (reader.Read())
  411. {
  412. // 从 Details 反序列化基础属性(如果 Details 存了完整 Batch)
  413. string detailsJson = reader["Details"] as string;
  414. Batch batch;
  415. if (!string.IsNullOrEmpty(detailsJson))
  416. {
  417. batch = JsonConvert.DeserializeObject<Batch>(detailsJson);
  418. }
  419. else
  420. {
  421. batch = new Batch(); // 或抛异常,看业务
  422. }
  423. // 覆盖关键字段(确保与数据库一致)
  424. batch.BatchID = reader["BatchID"] as string;
  425. batch.StartTime = (DateTime)reader["StartTimestamp"];
  426. batch.StartUsername = reader["StartUsername"] as string;
  427. batch.EndTime = (DateTime)reader["EndTimestamp"];
  428. batch.EndUsername = reader["EndUsername"] as string;
  429. batch.RecipeName = reader["RecipeName"] as string;
  430. batch.Finished = true;
  431. batches.Add(batch);
  432. }
  433. }
  434. }
  435. }
  436. return batches;
  437. }
  438. public static List<Batch> GetFinishedBatches(DateTime startTime, DateTime endTime)
  439. {
  440. return GetFinishedBatches(startTime, endTime, null,null);
  441. }
  442. private bool CreateBatchLogs(IEnumerable<string> batcheIDs, User user, out string errorMessage)
  443. {
  444. errorMessage = null;
  445. // 1. 检查是否有重复 BatchID
  446. var batchIds = batcheIDs.Select(b => b).ToList();
  447. if (batchIds.Count != batchIds.Distinct().Count())
  448. {
  449. errorMessage = "输入的批次列表中包含重复的批次号。";
  450. return false;
  451. }
  452. using (var conn = new SQLiteConnection(DatabaseHelper.ConnectionString))
  453. {
  454. conn.Open();
  455. // 检查数据库中是否已存在这些 BatchID
  456. string checkSql = "SELECT BatchID FROM BatchLogs WHERE BatchID IN (" +
  457. string.Join(",", batchIds.Select((_, i) => $"@id{i}")) + ")";
  458. using (var checkCmd = new SQLiteCommand(checkSql, conn))
  459. {
  460. for (int i = 0; i < batchIds.Count; i++)
  461. {
  462. checkCmd.Parameters.AddWithValue($"@id{i}", batchIds[i]);
  463. }
  464. using (var reader = checkCmd.ExecuteReader())
  465. {
  466. var existingIds = new List<string>();
  467. while (reader.Read())
  468. {
  469. existingIds.Add(reader.GetString(0));
  470. }
  471. if (existingIds.Any())
  472. {
  473. errorMessage = $"以下批次号已存在,无法重复创建: {string.Join(", ", existingIds)}";
  474. return false;
  475. }
  476. }
  477. }
  478. // 2. 开始插入
  479. string insertSql = @"
  480. INSERT INTO BatchLogs (
  481. BatchID,
  482. StartTimestamp,
  483. StartUserId,
  484. StartUsername,
  485. EndTimestamp,
  486. EndUserId,
  487. EndUsername,
  488. RecipeName,
  489. Details
  490. ) VALUES (
  491. @BatchID,
  492. @StartTimestamp,
  493. @StartUserId,
  494. @StartUsername,
  495. @EndTimestamp,
  496. @EndUserId,
  497. @EndUsername,
  498. @RecipeName,
  499. @Details
  500. )";
  501. using (var tran = conn.BeginTransaction())
  502. {
  503. using (var cmd = new SQLiteCommand(insertSql, conn, tran))
  504. {
  505. // 添加参数占位符(只加一次)
  506. cmd.Parameters.Add("@BatchID", DbType.String);
  507. cmd.Parameters.Add("@StartTimestamp", DbType.DateTime);
  508. cmd.Parameters.Add("@StartUserId", DbType.Int32);
  509. cmd.Parameters.Add("@StartUsername", DbType.String);
  510. cmd.Parameters.Add("@EndTimestamp", DbType.DateTime);
  511. cmd.Parameters.Add("@EndUserId", DbType.Int32);
  512. cmd.Parameters.Add("@EndUsername", DbType.String);
  513. cmd.Parameters.Add("@RecipeName", DbType.String);
  514. cmd.Parameters.Add("@Details", DbType.String);
  515. foreach (var id in batchIds)
  516. {
  517. // 更新参数值
  518. cmd.Parameters["@BatchID"].Value = id;
  519. cmd.Parameters["@StartTimestamp"].Value = DateTime.Now;
  520. cmd.Parameters["@StartUserId"].Value = user.Id;
  521. cmd.Parameters["@StartUsername"].Value = user.Username;
  522. cmd.Parameters["@EndTimestamp"].Value = DBNull.Value;
  523. cmd.Parameters["@EndUserId"].Value = DBNull.Value;
  524. cmd.Parameters["@EndUsername"].Value = DBNull.Value;
  525. cmd.Parameters["@RecipeName"].Value = DBNull.Value;
  526. cmd.Parameters["@Details"].Value =DBNull.Value ;//JsonConvert.SerializeObject(batch)
  527. cmd.ExecuteNonQuery();
  528. }
  529. }
  530. tran.Commit();
  531. }
  532. }
  533. return true;
  534. }
  535. }