feat: 新增消息Outbox机制、雪花ID配置优化及多项功能完善
1. 新增数据库唯一约束和Message_Outbox表脚本 2. 新增雪花ID、Hangfire存储、MQ重试等配置实体 3. 重构各项目雪花ID生成逻辑,改为从配置读取WorkerId 4. 优化积分服务分页查询、用户背包更新逻辑 5. 新增JWT令牌Redis过期刷新逻辑 6. 完善RabbitMQ死信队列消息头信息 7. 新增可靠MQ消息发布服务和Outbox派发后台服务 8. 替换原有RabbitMQ直接发送为Outbox可靠发布 9. 优化签到服务逻辑,新增重复签到校验和补签卡扣减逻辑 10. 修复自动铺码消费逻辑,新增点阵页预占和释放机制
This commit is contained in:
@ -74,6 +74,14 @@ public class CheckInService(
|
||||
|
||||
await checkInRecordRepository.UseTranAsync(async () =>
|
||||
{
|
||||
var duplicate = await checkInRecordRepository.Context.Queryable<CheckInRecord>()
|
||||
.Where(r => r.UserId == userId && !r.IsDeleted && r.CheckInDate >= today && r.CheckInDate < today.AddDays(1))
|
||||
.AnyAsync();
|
||||
if (duplicate)
|
||||
{
|
||||
throw new BusinessException("今日已签到,请明天再来", ResultCode.CONFLICT);
|
||||
}
|
||||
|
||||
// 6a. 创建签到记录
|
||||
var checkInRecord = new CheckInRecord
|
||||
{
|
||||
@ -94,10 +102,16 @@ public class CheckInService(
|
||||
var newGrowthBalance = user.GrowthPoints + growthReward;
|
||||
|
||||
await checkInRecordRepository.Context.Updateable<Users>()
|
||||
.SetColumns(u => u.GrowthPoints == newGrowthBalance)
|
||||
.SetColumns(u => u.GrowthPoints == u.GrowthPoints + growthReward)
|
||||
.SetColumns(u => u.UpdatedAt == DateTime.Now)
|
||||
.Where(u => u.Id == userId && !u.IsDeleted)
|
||||
.ExecuteCommandAsync();
|
||||
|
||||
newGrowthBalance = await checkInRecordRepository.Context.Queryable<Users>()
|
||||
.Where(u => u.Id == userId && !u.IsDeleted)
|
||||
.Select(u => u.GrowthPoints)
|
||||
.FirstAsync();
|
||||
|
||||
// 6c. 通过积分服务增加积分
|
||||
var pointsResult = await pointsService.AddPointsInTranAsync(new AddPointsInput
|
||||
{
|
||||
@ -271,6 +285,14 @@ public class CheckInService(
|
||||
|
||||
await checkInRecordRepository.UseTranAsync(async () =>
|
||||
{
|
||||
var duplicate = await checkInRecordRepository.Context.Queryable<CheckInRecord>()
|
||||
.Where(r => r.UserId == userId && !r.IsDeleted && r.CheckInDate >= targetDate && r.CheckInDate < targetDate.AddDays(1))
|
||||
.AnyAsync();
|
||||
if (duplicate)
|
||||
{
|
||||
throw new BusinessException($"{targetDate:yyyy-MM-dd} 已签到,无需补签", ResultCode.CONFLICT);
|
||||
}
|
||||
|
||||
// 创建补签记录
|
||||
var checkInRecord = new CheckInRecord
|
||||
{
|
||||
@ -290,14 +312,17 @@ public class CheckInService(
|
||||
var recordId = await checkInRecordRepository.Insertable(checkInRecord).ExecuteReturnIdentityAsync();
|
||||
checkInRecord.Id = recordId;
|
||||
|
||||
// 更新用户成长值
|
||||
var newGrowthBalance = user.GrowthPoints + growthReward;
|
||||
|
||||
await checkInRecordRepository.Context.Updateable<Users>()
|
||||
.SetColumns(u => u.GrowthPoints == newGrowthBalance)
|
||||
.SetColumns(u => u.GrowthPoints == u.GrowthPoints + growthReward)
|
||||
.SetColumns(u => u.UpdatedAt == DateTime.Now)
|
||||
.Where(u => u.Id == userId && !u.IsDeleted)
|
||||
.ExecuteCommandAsync();
|
||||
|
||||
var newGrowthBalance = await checkInRecordRepository.Context.Queryable<Users>()
|
||||
.Where(u => u.Id == userId && !u.IsDeleted)
|
||||
.Select(u => u.GrowthPoints)
|
||||
.FirstAsync();
|
||||
|
||||
// 通过积分服务增加积分
|
||||
var pointsResult = await pointsService.AddPointsInTranAsync(new AddPointsInput
|
||||
{
|
||||
@ -311,19 +336,20 @@ public class CheckInService(
|
||||
// 扣减补签卡
|
||||
if (makeUpCard.Quantity <= 1)
|
||||
{
|
||||
var cardAffectedRows = await checkInRecordRepository.Context.Updateable<UserBag>()
|
||||
.SetColumns(b => b.Quantity == b.Quantity - 1)
|
||||
.SetColumns(b => b.UpdatedAt == DateTime.Now)
|
||||
.Where(b => b.Id == makeUpCard.Id && b.UserId == userId && b.Quantity > 0 && b.Status == (int)UserBagStatusEnum.Available && !b.IsDeleted)
|
||||
.ExecuteCommandAsync();
|
||||
if (cardAffectedRows <= 0)
|
||||
{
|
||||
throw new BusinessException("补签卡不足,无法补签", ResultCode.CONFLICT);
|
||||
}
|
||||
|
||||
await checkInRecordRepository.Context.Updateable<UserBag>()
|
||||
.SetColumns(b => b.Status == (int)UserBagStatusEnum.Expired)
|
||||
.SetColumns(b => b.Quantity == 0)
|
||||
.SetColumns(b => b.UpdatedAt == DateTime.Now)
|
||||
.Where(b => b.Id == makeUpCard.Id)
|
||||
.ExecuteCommandAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
await checkInRecordRepository.Context.Updateable<UserBag>()
|
||||
.SetColumns(b => b.Quantity == makeUpCard.Quantity - 1)
|
||||
.SetColumns(b => b.UpdatedAt == DateTime.Now)
|
||||
.Where(b => b.Id == makeUpCard.Id)
|
||||
.Where(b => b.Id == makeUpCard.Id && b.Quantity <= 0 && !b.IsDeleted)
|
||||
.ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@ using QYZH.InteractiveMagazine.IService;
|
||||
using QYZH.InteractiveMagazine.Models.Common;
|
||||
using QYZH.InteractiveMagazine.Models.Dto;
|
||||
using QYZH.InteractiveMagazine.Models.Dto.Journal;
|
||||
using QYZH.InteractiveMagazine.Models.Dto.RabbitMQ;
|
||||
using QYZH.InteractiveMagazine.Models.Entity;
|
||||
using QYZH.InteractiveMagazine.Models.Enum;
|
||||
using QYZH.InteractiveMagazine.Repository;
|
||||
@ -24,7 +25,7 @@ public class JournalPageService(BaseRepository<Journal> JournalRepository,
|
||||
OssService ossService,
|
||||
IHttpClientFactory httpClientFactory,
|
||||
IConfiguration configuration,
|
||||
IRabbitMQService rabbitMqService,
|
||||
IMessagePublishService messagePublishService,
|
||||
ILogger<AiBasePromptService> logger,
|
||||
BaseRepository<JournalPage> JournalPageRepository,
|
||||
BaseRepository<JournalPageTask> JournalPageTaskRepository,
|
||||
@ -135,18 +136,29 @@ public class JournalPageService(BaseRepository<Journal> JournalRepository,
|
||||
var isPdfExists = ossService.DoesObjectExist(uploadPdfKey);
|
||||
BusinessException.ThrowIf(!isPdfExists, "获取期刊上传的PDF文件失败,请检查PDF文件是否上传成功", ResultCode.GLOBAL_ERROR);
|
||||
|
||||
journalEntity.Status = (int)JournalStatusEnum.Codeing;
|
||||
|
||||
await JournalRepository.Updateable(journalEntity).UpdateColumns(x => new { x.Status, x.UpdatedAt }).ExecuteCommandAsync();
|
||||
|
||||
var data = new JournalPagePrintDto
|
||||
{
|
||||
JournalId = JournalId,
|
||||
JournalPdfUrl = uploadPdfUrl,
|
||||
PageNum = [.. journalPageList.Select(x => x.PageNum)]
|
||||
};
|
||||
var msRes = await rabbitMqService.SendAsync(new RabbitMQSendParam { Exchange = "ex.journal", Queue = "mq.journal.dotcode.auto", RoutingKey = "rk.journal.dotcode.auto", Data = data });//发生消息
|
||||
return msRes;
|
||||
|
||||
return await UseTranAsync(async () =>
|
||||
{
|
||||
journalEntity.Status = (int)JournalStatusEnum.Codeing;
|
||||
journalEntity.UpdatedAt = DateTime.Now;
|
||||
await JournalRepository.Updateable(journalEntity).UpdateColumns(x => new { x.Status, x.UpdatedAt }).ExecuteCommandAsync();
|
||||
await messagePublishService.PublishAsync(new MessagePublishInput<JournalPagePrintDto>
|
||||
{
|
||||
Exchange = "ex.journal",
|
||||
Queue = "mq.journal.dotcode.auto",
|
||||
RoutingKey = "rk.journal.dotcode.auto",
|
||||
Data = data,
|
||||
BusinessType = "JournalDotCode",
|
||||
BusinessId = JournalId
|
||||
});
|
||||
return true;
|
||||
});
|
||||
}
|
||||
/// <summary>
|
||||
/// 回调接口-自动铺码, 书页铺码后回调接口,更新书页的点阵码
|
||||
|
||||
@ -9,10 +9,12 @@ using QYZH.InteractiveMagazine.Models.Common;
|
||||
using QYZH.InteractiveMagazine.Models.Dto;
|
||||
using QYZH.InteractiveMagazine.Models.Dto.DotMatrix;
|
||||
using QYZH.InteractiveMagazine.Models.Dto.Journal;
|
||||
using QYZH.InteractiveMagazine.Models.Dto.RabbitMQ;
|
||||
using QYZH.InteractiveMagazine.Models.Entity;
|
||||
using QYZH.InteractiveMagazine.Models.Enum;
|
||||
using QYZH.InteractiveMagazine.Repository;
|
||||
using SqlSugar;
|
||||
using System.Text.Json;
|
||||
using Yitter.IdGenerator;
|
||||
|
||||
namespace QYZH.InteractiveMagazine.Service;
|
||||
@ -24,7 +26,7 @@ public class JournalService(BaseRepository<JournalPage> JournalPageRepository,
|
||||
BaseRepository<DotFile> dotFileRepository,
|
||||
BaseRepository<DotFileDetail> dotFileDetailRepository,
|
||||
OssService ossService,
|
||||
IRabbitMQService rabbitMqService,
|
||||
IMessagePublishService messagePublishService,
|
||||
ILogger<JournalService> logger) : BaseRepository<Journal>, IJournalService
|
||||
{
|
||||
private const string JournalExchange = "ex.journal";
|
||||
@ -375,23 +377,27 @@ public class JournalService(BaseRepository<JournalPage> JournalPageRepository,
|
||||
x => x.Key,
|
||||
x => string.Join(',', x.OrderBy(q => ParseTaskNo(q.No)).Select(q => q.Id)));
|
||||
|
||||
var bookMessageSent = await rabbitMqService.SendAsync(new RabbitMQSendParam
|
||||
var publishMessages = new List<MessagePublishInput<object>>
|
||||
{
|
||||
Exchange = JournalExchange,
|
||||
Queue = PublishBookQueue,
|
||||
RoutingKey = PublishBookRoutingKey,
|
||||
Data = new JournalPublishBookMessage
|
||||
new()
|
||||
{
|
||||
BookId = id,
|
||||
StartTime = book.StartTime.Value,
|
||||
EndTime = book.EndTime.Value
|
||||
Exchange = JournalExchange,
|
||||
Queue = PublishBookQueue,
|
||||
RoutingKey = PublishBookRoutingKey,
|
||||
Data = new JournalPublishBookMessage
|
||||
{
|
||||
BookId = id,
|
||||
StartTime = book.StartTime.Value,
|
||||
EndTime = book.EndTime.Value
|
||||
},
|
||||
BusinessType = "JournalPublish",
|
||||
BusinessId = id
|
||||
}
|
||||
});
|
||||
BusinessException.ThrowIf(!bookMessageSent, "发布书籍消息发送失败", ResultCode.GLOBAL_ERROR);
|
||||
};
|
||||
|
||||
foreach (var page in pages)
|
||||
{
|
||||
var pageMessageSent = await rabbitMqService.SendAsync(new RabbitMQSendParam
|
||||
publishMessages.Add(new MessagePublishInput<object>
|
||||
{
|
||||
Exchange = JournalExchange,
|
||||
Queue = PublishBookPageQueue,
|
||||
@ -404,16 +410,21 @@ public class JournalService(BaseRepository<JournalPage> JournalPageRepository,
|
||||
Layout = page.Layout,
|
||||
Url = DomainHelper.OssFullUrl(page.Url),
|
||||
QuestionNo = questionIdsByPageId.GetValueOrDefault(page.Id) ?? string.Empty
|
||||
}
|
||||
},
|
||||
BusinessType = "JournalPublish",
|
||||
BusinessId = id
|
||||
});
|
||||
BusinessException.ThrowIf(!pageMessageSent, $"发布书页消息发送失败,PageId: {page.Id}", ResultCode.GLOBAL_ERROR);
|
||||
}
|
||||
|
||||
return await base.Updateable()
|
||||
.SetColumns(s => s.Status, JournalStatusEnum.Published)
|
||||
.SetColumns(s => s.UpdatedAt, DateTime.Now)
|
||||
.Where(w => w.Id == id)
|
||||
.ExecuteCommandAsync() > 0;
|
||||
return await UseTranAsync(async () =>
|
||||
{
|
||||
await messagePublishService.PublishBatchAsync(publishMessages);
|
||||
return await base.Updateable()
|
||||
.SetColumns(s => s.Status, JournalStatusEnum.Published)
|
||||
.SetColumns(s => s.UpdatedAt, DateTime.Now)
|
||||
.Where(w => w.Id == id)
|
||||
.ExecuteCommandAsync() > 0;
|
||||
});
|
||||
|
||||
static (int First, int Second, int Third, int Fourth) ParseTaskNo(string? no)
|
||||
{
|
||||
|
||||
112
QYZH.InteractiveMagazine.Service/MessagePublishService.cs
Normal file
112
QYZH.InteractiveMagazine.Service/MessagePublishService.cs
Normal file
@ -0,0 +1,112 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using QYZH.InteractiveMagazine.Infrastructure.RabbitMQ;
|
||||
using QYZH.InteractiveMagazine.IService;
|
||||
using QYZH.InteractiveMagazine.Models.Common;
|
||||
using QYZH.InteractiveMagazine.Models.Dto;
|
||||
using QYZH.InteractiveMagazine.Models.Dto.RabbitMQ;
|
||||
using QYZH.InteractiveMagazine.Models.Entity;
|
||||
using QYZH.InteractiveMagazine.Models.Enum;
|
||||
using QYZH.InteractiveMagazine.Repository;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace QYZH.InteractiveMagazine.Service;
|
||||
|
||||
/// <summary>
|
||||
/// MQ消息发布服务。
|
||||
/// </summary>
|
||||
public class MessagePublishService(
|
||||
IRabbitMQService rabbitMQService,
|
||||
ILogger<MessagePublishService> logger) : BaseRepository<MessageOutbox>, IMessagePublishService
|
||||
{
|
||||
/// <summary>
|
||||
/// 可靠发布消息,默认写入Outbox。
|
||||
/// </summary>
|
||||
public async Task<MessagePublishResult> PublishAsync<T>(MessagePublishInput<T> input, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ValidateInput(input);
|
||||
var message = BuildOutboxMessage(input);
|
||||
await Context.Insertable(message).ExecuteCommandAsync(cancellationToken);
|
||||
return new MessagePublishResult
|
||||
{
|
||||
Success = true,
|
||||
OutboxIds = [message.Id],
|
||||
Message = "消息已写入Outbox"
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量可靠发布消息,默认写入Outbox。
|
||||
/// </summary>
|
||||
public async Task<MessagePublishResult> PublishBatchAsync<T>(IEnumerable<MessagePublishInput<T>> inputs, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var inputList = inputs.ToList();
|
||||
BusinessException.ThrowIf(inputList.Count == 0, "消息发布列表不能为空", ResultCode.BAD_REQUEST);
|
||||
inputList.ForEach(ValidateInput);
|
||||
|
||||
var messages = inputList.Select(BuildOutboxMessage).ToList();
|
||||
await Context.Insertable(messages).ExecuteCommandAsync(cancellationToken);
|
||||
return new MessagePublishResult
|
||||
{
|
||||
Success = true,
|
||||
OutboxIds = messages.Select(x => x.Id).ToList(),
|
||||
Message = "消息已批量写入Outbox"
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 直接发布消息,不写Outbox。
|
||||
/// </summary>
|
||||
public async Task<MessagePublishResult> PublishDirectAsync<T>(MessagePublishInput<T> input, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ValidateInput(input);
|
||||
var sent = await rabbitMQService.SendAsync(new RabbitMQSendParam
|
||||
{
|
||||
Exchange = input.Exchange,
|
||||
Queue = input.Queue,
|
||||
RoutingKey = input.RoutingKey,
|
||||
Data = input.Data!
|
||||
}, cancellationToken);
|
||||
|
||||
if (!sent)
|
||||
{
|
||||
logger.LogError("MQ直接发布失败,Exchange: {Exchange}, Queue: {Queue}, RoutingKey: {RoutingKey}, BusinessType: {BusinessType}, BusinessId: {BusinessId}",
|
||||
input.Exchange, input.Queue, input.RoutingKey, input.BusinessType, input.BusinessId);
|
||||
throw new BusinessException("MQ消息发送失败", ResultCode.GLOBAL_ERROR);
|
||||
}
|
||||
|
||||
return new MessagePublishResult
|
||||
{
|
||||
Success = true,
|
||||
Message = "消息已直接发送"
|
||||
};
|
||||
}
|
||||
|
||||
private static MessageOutbox BuildOutboxMessage<T>(MessagePublishInput<T> input)
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
return new MessageOutbox
|
||||
{
|
||||
Exchange = input.Exchange,
|
||||
Queue = input.Queue,
|
||||
RoutingKey = input.RoutingKey,
|
||||
Payload = JsonSerializer.Serialize(input.Data),
|
||||
BusinessType = input.BusinessType,
|
||||
BusinessId = input.BusinessId,
|
||||
Status = (int)MessageOutboxStatusEnum.Pending,
|
||||
CreatedBy = "System",
|
||||
CreatedAt = now,
|
||||
UpdatedBy = "System",
|
||||
UpdatedAt = now
|
||||
};
|
||||
}
|
||||
|
||||
private static void ValidateInput<T>(MessagePublishInput<T> input)
|
||||
{
|
||||
BusinessException.ThrowIf(string.IsNullOrWhiteSpace(input.Exchange), "MQ交换机不能为空", ResultCode.BAD_REQUEST);
|
||||
BusinessException.ThrowIf(string.IsNullOrWhiteSpace(input.Queue), "MQ队列不能为空", ResultCode.BAD_REQUEST);
|
||||
BusinessException.ThrowIf(string.IsNullOrWhiteSpace(input.RoutingKey), "MQ路由键不能为空", ResultCode.BAD_REQUEST);
|
||||
BusinessException.ThrowIf(string.IsNullOrWhiteSpace(input.BusinessType), "MQ业务类型不能为空", ResultCode.BAD_REQUEST);
|
||||
BusinessException.ThrowIf(input.BusinessId <= 0, "MQ业务ID无效", ResultCode.BAD_REQUEST);
|
||||
BusinessException.ThrowIf(input.Data == null, "MQ消息数据不能为空", ResultCode.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@ using QYZH.InteractiveMagazine.Models.Dto.Points;
|
||||
using QYZH.InteractiveMagazine.Models.Entity;
|
||||
using QYZH.InteractiveMagazine.Models.Enum;
|
||||
using QYZH.InteractiveMagazine.Repository;
|
||||
using SqlSugar;
|
||||
|
||||
namespace QYZH.InteractiveMagazine.Service;
|
||||
|
||||
@ -265,7 +266,7 @@ public class PointsService(
|
||||
.WhereIF(!string.IsNullOrEmpty(input.Status), r => r.Status.ToString() == input.Status)
|
||||
.OrderByDescending(r => r.CreatedAt);
|
||||
|
||||
var total = 0;
|
||||
RefAsync<int> total = 0;
|
||||
var records = await query
|
||||
.Select(r => new PointsRecordOutput
|
||||
{
|
||||
|
||||
@ -5,6 +5,8 @@ using QYZH.InteractiveMagazine.Infrastructure.RabbitMQ;
|
||||
using QYZH.InteractiveMagazine.IService;
|
||||
using QYZH.InteractiveMagazine.Models.Common;
|
||||
using QYZH.InteractiveMagazine.Models.Dto;
|
||||
using QYZH.InteractiveMagazine.Models.Dto.Journal;
|
||||
using QYZH.InteractiveMagazine.Models.Dto.RabbitMQ;
|
||||
using QYZH.InteractiveMagazine.Models.Entity;
|
||||
using QYZH.InteractiveMagazine.Models.Enum;
|
||||
using QYZH.InteractiveMagazine.Repository;
|
||||
@ -21,7 +23,7 @@ public class UserJournalService(
|
||||
BaseRepository<Users> usersRepository,
|
||||
BaseRepository<Journal> journalRepository,
|
||||
ILogger<UserJournalService> logger,
|
||||
IRabbitMQService rabbitMqService,
|
||||
IMessagePublishService messagePublishService,
|
||||
OssService ossService,
|
||||
IPetService petService)
|
||||
: BaseRepository<UserJournal>, IUserJournalService
|
||||
@ -214,7 +216,7 @@ public class UserJournalService(
|
||||
}
|
||||
|
||||
var recordIds = records.Select(r => r.Id).ToList();
|
||||
var messageSent = await rabbitMqService.SendAsync(new RabbitMQSendParam
|
||||
var messageSent = (await messagePublishService.PublishAsync(new MessagePublishInput<GenerateUserJournalQrCodeMessage>
|
||||
{
|
||||
Exchange = JournalExchange,
|
||||
Queue = QrCodeGenerateQueue,
|
||||
@ -223,8 +225,10 @@ public class UserJournalService(
|
||||
{
|
||||
RecordIds = recordIds,
|
||||
OperatorName = operatorName
|
||||
}
|
||||
});
|
||||
},
|
||||
BusinessType = "UserJournalQrCodeGenerate",
|
||||
BusinessId = input.JournalId
|
||||
})).Success;
|
||||
|
||||
if (!messageSent)
|
||||
{
|
||||
@ -457,7 +461,7 @@ public class UserJournalService(
|
||||
{
|
||||
try
|
||||
{
|
||||
var messageSent = await rabbitMqService.SendAsync(new RabbitMQSendParam
|
||||
var messageSent = (await messagePublishService.PublishAsync(new MessagePublishInput<BindJournalMessage>
|
||||
{
|
||||
Exchange = JournalExchange,
|
||||
Queue = BindJournalQueue,
|
||||
@ -469,8 +473,10 @@ public class UserJournalService(
|
||||
StartTime = journal.StartTime,
|
||||
EndTime = journal.EndTime,
|
||||
UploadDomain = user.UploadDomain
|
||||
}
|
||||
});
|
||||
},
|
||||
BusinessType = "UserJournalBind",
|
||||
BusinessId = user.Id
|
||||
})).Success;
|
||||
|
||||
if (!messageSent)
|
||||
{
|
||||
|
||||
@ -253,9 +253,9 @@ public class WxMallService(
|
||||
{
|
||||
// 已有同类物品,累加数量
|
||||
await exchangeRecordRepository.Context.Updateable<UserBag>()
|
||||
.SetColumns(b => b.Quantity == existingBag.Quantity + input.Quantity)
|
||||
.SetColumns(b => b.Quantity == b.Quantity + input.Quantity)
|
||||
.SetColumns(b => b.UpdatedAt == DateTime.Now)
|
||||
.Where(b => b.Id == existingBag.Id)
|
||||
.Where(b => b.Id == existingBag.Id && !b.IsDeleted && b.Status == (int)UserBagStatusEnum.Available)
|
||||
.ExecuteCommandAsync();
|
||||
}
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user