using Microsoft.EntityFrameworkCore; using StopShopping.EF; using StopShopping.Services.Extensions; using StopShopping.Services.Models; using StopShopping.Services.Models.Req; using StopShopping.Services.Models.Resp; namespace StopShopping.Services.Implementions; public class RequestService : IRequestService { public RequestService( StopShoppingContext dbContext, IClaimsService claimsService, ISerialNoGenerator serialNoGenerator) { _dbContext = dbContext; _claimService = claimsService; _serialNoGenerator = serialNoGenerator; } private readonly StopShoppingContext _dbContext; private readonly IClaimsService _claimService; private readonly ISerialNoGenerator _serialNoGenerator; public async Task PublishRequestAsync(CreateRequestParams model) { var serialNo = _serialNoGenerator.GenerateRequestNo(); var userId = _claimService.GetCurrentUserId()!.Value; EF.Models.Request req = new() { CategoryId = model.CategoryId, Deadline = DateOnly.Parse(model.Deadline), Description = model.Description, Name = model.Name, PublisherId = userId, SerialNo = serialNo, Status = (short)RequestStatus.Publish, }; await _dbContext.Requests.AddAsync(req); await _dbContext.SaveChangesAsync(); return ApiResponse.Succed(); } public async Task>> SearchAsync(RequestSearchParams model) { return await DoSearchAsync(model, null); } public async Task>> RequestOrderSearchAsync(RequestSearchWithStatusParams model) { return await DoSearchAsync(model, UserRoles.Buyer); } public async Task>> ReplyOrderSearchAsync(RequestSearchWithStatusParams model) { return await DoSearchAsync(model, UserRoles.Seller); } public async Task DeleteRequestAsync(RequestIdParams model) { var userId = _claimService.GetCurrentUserId()!.Value; var request = await _dbContext.Requests .Include(r => r.Replies) .Where(r => r.Id == model.RequestId && !r.Deleted) .FirstOrDefaultAsync(); if (null == request) return ApiResponse.Failed("此需求已不存在,请刷新重试"); var status = (RequestStatus)request.Status; if (status.CanDelete()) return ApiResponse.Failed("此需求状态已改变,请刷新重试"); request.Deleted = true; request.Status = (short)RequestStatus.Completed; foreach (var reply in request.Replies) { reply.Rejected = true; } await _dbContext.SaveChangesAsync(); return ApiResponse.Succed(); } private async Task>> DoSearchAsync(T model, UserRoles? userRoles) where T : RequestSearchParams, new() { var qry = _dbContext.Requests .AsNoTracking() .Where(q => !q.Deleted); if (model is RequestSearchWithStatusParams statusParams) { if (statusParams.Status != RequestStatus.All) qry = qry.Where(q => q.Status == (short)statusParams.Status); } else { qry = qry.Where(q => q.Status == (short)RequestStatus.Publish || q.Status == (short)RequestStatus.Replied); } if (model.CategoryId > 0) { string categoryPath = $"/{model.CategoryId.Value}/"; qry = qry.Where(q => _dbContext.Categories .Where(c => c.Path.StartsWith(categoryPath) && !c.Deleted) .Select(c => c.Id) .Contains(q.CategoryId)); } if (!string.IsNullOrEmpty(model.Keyword)) { qry = qry.Where(q => q.Name.Contains(model.Keyword) || (q.Description != null && q.Description.Contains(model.Keyword)) || q.SerialNo.Contains(model.Keyword)); } if (userRoles.HasValue) { var userId = _claimService.GetCurrentUserId()!.Value; qry = userRoles.Value switch { UserRoles.Seller => qry.Where(q => q.Replies.Any(r => r.UserId == userId)), UserRoles.Buyer => qry.Where(q => q.PublisherId == userId), _ => qry }; } var firstOrderBy = model.OrderBys!.First(); var included = qry .Include(r => r.Publisher).AsNoTracking() .Include(r => r.Category).AsNoTracking() .Select(r => new { ReplyAmount = r.Replies.Count, r }); var ordered = firstOrderBy switch { RequestOrderBys.PublishTime => included.OrderBy(q => q.r.PublishTime), RequestOrderBys.PublishTimeDesc => included.OrderByDescending(q => q.r.PublishTime), RequestOrderBys.CategoryId => included.OrderBy(q => q.r.CategoryId), RequestOrderBys.CategoryIdDesc => included.OrderByDescending(q => q.r.CategoryId), RequestOrderBys.ReplyAmount => included.OrderBy(q => q.ReplyAmount), RequestOrderBys.ReplyAmountDesc => included.OrderByDescending(q => q.ReplyAmount), _ => included.OrderBy(q => q.r.PublishTime) }; foreach (var orderBy in model.OrderBys!.Skip(1)) { ordered = orderBy switch { RequestOrderBys.PublishTime => ordered!.ThenBy(q => q.r.PublishTime), RequestOrderBys.PublishTimeDesc => ordered!.ThenByDescending(q => q.r.PublishTime), RequestOrderBys.CategoryId => ordered!.ThenBy(q => q.r.CategoryId), RequestOrderBys.CategoryIdDesc => ordered!.ThenByDescending(q => q.r.CategoryId), RequestOrderBys.ReplyAmount => ordered!.ThenBy(q => q.ReplyAmount), RequestOrderBys.ReplyAmountDesc => ordered!.ThenByDescending(q => q.ReplyAmount), _ => ordered!.ThenBy(q => q.r.PublishTime) }; } var paged = await ordered.Select(r => new Request { CategoryId = r.r.Category.Id, CategoryName = r.r.Category.Name, Deadline = r.r.Deadline.ToFormatted(), Description = r.r.Description, Id = r.r.Id, Name = r.r.Name, Publisher = r.r.Publisher.NickName, PublishTime = r.r.PublishTime.ToFormatted(), ReplyAmount = r.ReplyAmount, SerialNo = r.r.SerialNo, Status = (RequestStatus)r.r.Status, }).ToAsyncEnumerable().ToPagedAsync(model.PageIndex, model.PageSize); return new ApiResponse>(paged); } }