Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

171 строка
6.1KB

  1. using CartWise.Application.Interfaces;
  2. using CartWise.Application.Results;
  3. using CartWise.Domain.Entities;
  4. using CartWise.Domain.Enums;
  5. using Microsoft.EntityFrameworkCore;
  6. namespace CartWise.Application.Services;
  7. public class ShoppingListService : IShoppingListService
  8. {
  9. private readonly IApplicationDbContext _db;
  10. private readonly TimeProvider _timeProvider;
  11. public ShoppingListService(IApplicationDbContext db, TimeProvider timeProvider)
  12. {
  13. _db = db;
  14. _timeProvider = timeProvider;
  15. }
  16. public async Task<ShoppingList?> GetActiveListAsync(Guid householdId, CancellationToken cancellationToken = default)
  17. {
  18. return await _db.ShoppingLists
  19. .Include(l => l.Items)
  20. .AsNoTracking()
  21. .Where(l => l.HouseholdId == householdId && l.Status == ShoppingListStatus.Active)
  22. .FirstOrDefaultAsync(cancellationToken);
  23. }
  24. public async Task<Result<ShoppingList>> CreateListAsync(Guid householdId, string name, string createdByUserId, CancellationToken cancellationToken = default)
  25. {
  26. var hasActiveList = await _db.ShoppingLists
  27. .AsNoTracking()
  28. .AnyAsync(l => l.HouseholdId == householdId && l.Status == ShoppingListStatus.Active, cancellationToken);
  29. if (hasActiveList)
  30. {
  31. return Result<ShoppingList>.Failure("This household already has an active shopping list.");
  32. }
  33. ShoppingList list;
  34. try
  35. {
  36. list = new ShoppingList(householdId, name, createdByUserId, _timeProvider.GetUtcNow().UtcDateTime);
  37. }
  38. catch (ArgumentException ex)
  39. {
  40. return Result<ShoppingList>.Failure(ex.Message);
  41. }
  42. _db.ShoppingLists.Add(list);
  43. await _db.SaveChangesAsync(cancellationToken);
  44. return Result<ShoppingList>.Success(list);
  45. }
  46. public async Task<Result<ShoppingListItem>> AddItemAsync(
  47. Guid shoppingListId,
  48. string displayName,
  49. decimal? quantity,
  50. string? unit,
  51. string addedByUserId,
  52. CancellationToken cancellationToken = default)
  53. {
  54. var list = await _db.ShoppingLists
  55. .Include(l => l.Items)
  56. .FirstOrDefaultAsync(l => l.ShoppingListId == shoppingListId, cancellationToken);
  57. if (list is null)
  58. {
  59. return Result<ShoppingListItem>.Failure("Shopping list not found.");
  60. }
  61. ShoppingListItem item;
  62. try
  63. {
  64. item = list.AddItem(displayName, quantity, unit, addedByUserId, _timeProvider.GetUtcNow().UtcDateTime);
  65. }
  66. catch (Exception ex) when (ex is ArgumentException or InvalidOperationException)
  67. {
  68. return Result<ShoppingListItem>.Failure(ex.Message);
  69. }
  70. // EF Core can't reliably infer Added state for a new child discovered only via a mutated
  71. // navigation collection on an already-tracked parent, since ShoppingListItemId is a
  72. // client-generated Guid rather than a store-generated key. Mark it explicitly.
  73. _db.ShoppingListItems.Add(item);
  74. await _db.SaveChangesAsync(cancellationToken);
  75. return Result<ShoppingListItem>.Success(item);
  76. }
  77. public async Task<Result<ShoppingListItem>> TogglePurchasedAsync(Guid householdId, Guid itemId, Guid expectedRowVersion, CancellationToken cancellationToken = default)
  78. {
  79. var item = await FindItemForHouseholdAsync(householdId, itemId, cancellationToken);
  80. if (item is null)
  81. {
  82. return Result<ShoppingListItem>.Failure("Item not found.");
  83. }
  84. _db.Entry(item).Property(i => i.RowVersion).OriginalValue = expectedRowVersion;
  85. item.TogglePurchased(_timeProvider.GetUtcNow().UtcDateTime);
  86. try
  87. {
  88. await _db.SaveChangesAsync(cancellationToken);
  89. }
  90. catch (DbUpdateConcurrencyException)
  91. {
  92. return Result<ShoppingListItem>.Failure("This item was changed by someone else. Please refresh and try again.");
  93. }
  94. return Result<ShoppingListItem>.Success(item);
  95. }
  96. public async Task<Result<ShoppingListItem>> ToggleSkippedAsync(Guid householdId, Guid itemId, Guid expectedRowVersion, CancellationToken cancellationToken = default)
  97. {
  98. var item = await FindItemForHouseholdAsync(householdId, itemId, cancellationToken);
  99. if (item is null)
  100. {
  101. return Result<ShoppingListItem>.Failure("Item not found.");
  102. }
  103. _db.Entry(item).Property(i => i.RowVersion).OriginalValue = expectedRowVersion;
  104. item.ToggleSkipped();
  105. try
  106. {
  107. await _db.SaveChangesAsync(cancellationToken);
  108. }
  109. catch (DbUpdateConcurrencyException)
  110. {
  111. return Result<ShoppingListItem>.Failure("This item was changed by someone else. Please refresh and try again.");
  112. }
  113. return Result<ShoppingListItem>.Success(item);
  114. }
  115. public async Task<Result> DeleteItemAsync(Guid householdId, Guid itemId, Guid expectedRowVersion, CancellationToken cancellationToken = default)
  116. {
  117. var item = await FindItemForHouseholdAsync(householdId, itemId, cancellationToken);
  118. if (item is null)
  119. {
  120. return Result.Failure("Item not found.");
  121. }
  122. _db.Entry(item).Property(i => i.RowVersion).OriginalValue = expectedRowVersion;
  123. _db.ShoppingListItems.Remove(item);
  124. try
  125. {
  126. await _db.SaveChangesAsync(cancellationToken);
  127. }
  128. catch (DbUpdateConcurrencyException)
  129. {
  130. return Result.Failure("This item was changed by someone else. Please refresh and try again.");
  131. }
  132. return Result.Success();
  133. }
  134. private Task<ShoppingListItem?> FindItemForHouseholdAsync(Guid householdId, Guid itemId, CancellationToken cancellationToken)
  135. {
  136. return (
  137. from item in _db.ShoppingListItems
  138. join list in _db.ShoppingLists on item.ShoppingListId equals list.ShoppingListId
  139. where item.ShoppingListItemId == itemId && list.HouseholdId == householdId
  140. select item
  141. ).FirstOrDefaultAsync(cancellationToken);
  142. }
  143. }

Powered by TurnKey Linux.