Files
StopShopping/StopShopping.Services/Implementions/RequestService.cs
2026-03-30 11:07:30 +08:00

191 lines
6.8 KiB
C#

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<ApiResponse> 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<ApiResponse<PagedResult<Request>>> SearchAsync(RequestSearchParams model)
{
return await DoSearchAsync(model, null);
}
public async Task<ApiResponse<PagedResult<Request>>> RequestOrderSearchAsync(RequestSearchWithStatusParams model)
{
return await DoSearchAsync(model, UserRoles.Buyer);
}
public async Task<ApiResponse<PagedResult<Request>>> ReplyOrderSearchAsync(RequestSearchWithStatusParams model)
{
return await DoSearchAsync(model, UserRoles.Seller);
}
public async Task<ApiResponse> 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<ApiResponse<PagedResult<Request>>> DoSearchAsync<T>(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<PagedResult<Request>>(paged);
}
}