using AgriculturalPlatform.Api.Data; using AgriculturalPlatform.Api.Dtos; using AgriculturalPlatform.Api.Models; using AgriculturalPlatform.Api.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace AgriculturalPlatform.Api.Controllers; [ApiController] [Route("api/purchases")] [Authorize] public class PurchasesController( AppDbContext db, DataScopeService scope, CurrentUserService currentUser, NumberGenerator numberGen) : ControllerBase { /// 收购单列表(分页 + 多条件 + 数据权限) [HttpGet] public async Task>> List( string? status, int? farmerId, int? productId, int? orgId, DateTime? from, DateTime? to, [FromQuery] int page = 1, [FromQuery] int pageSize = 20, [FromQuery] string? keyword = null) { var visible = await scope.GetVisibleOrgIdsAsync(currentUser.Get()); var query = db.PurchaseOrders .Include(p => p.Farmer).Include(p => p.Product) .Include(p => p.PurchaserOrg).Include(p => p.Operator) .AsQueryable(); if (visible is not null) query = query.Where(p => visible.Contains(p.PurchaserOrgId)); if (!string.IsNullOrWhiteSpace(status) && Enum.TryParse(status, out var ps)) query = query.Where(p => p.Status == ps); if (farmerId.HasValue) query = query.Where(p => p.FarmerId == farmerId); if (productId.HasValue) query = query.Where(p => p.ProductId == productId); if (orgId.HasValue) query = query.Where(p => p.PurchaserOrgId == orgId); if (from.HasValue) query = query.Where(p => p.CreatedAt >= from); if (to.HasValue) query = query.Where(p => p.CreatedAt < to.Value.Date.AddDays(1)); if (!string.IsNullOrWhiteSpace(keyword)) query = query.Where(p => p.OrderNo.Contains(keyword) || (p.Farmer != null && p.Farmer.Name.Contains(keyword)) || (p.Farmer != null && p.Farmer.Phone.Contains(keyword))); var total = await query.CountAsync(); var items = await query.OrderByDescending(p => p.CreatedAt) .Skip((page - 1) * pageSize).Take(pageSize) .ToListAsync(); return Ok(new PagedResult(items.ToDtos(), total)); } /// 进行中的过磅单(触摸屏/大屏实时展示) [HttpGet("weighing")] public async Task Weighing() { var visible = await scope.GetVisibleOrgIdsAsync(currentUser.Get()); var query = db.PurchaseOrders .Include(p => p.Farmer).Include(p => p.Product) .Where(p => p.Status == PurchaseStatus.Weighing) .AsQueryable(); if (visible is not null) query = query.Where(p => visible.Contains(p.PurchaserOrgId)); var items = await query.OrderByDescending(p => p.WeighInAt).Take(20).ToListAsync(); return Ok(items.ToDtos()); } /// 收购单详情 [HttpGet("{id:int}")] public async Task> Get(int id) { var order = await db.PurchaseOrders .Include(p => p.Farmer).Include(p => p.Product) .Include(p => p.PurchaserOrg).Include(p => p.Operator) .FirstOrDefaultAsync(p => p.Id == id); if (order is null) return NotFound(); return Ok(order.ToDto()); } /// 新建收购单(第一次过磅:记录毛重) [HttpPost] public async Task> Create(PurchaseCreateRequest req) { if (!await db.Farmers.AnyAsync(f => f.Id == req.FarmerId)) return BadRequest(new { message = "农户不存在" }); if (!await db.Products.AnyAsync(p => p.Id == req.ProductId)) return BadRequest(new { message = "品种不存在" }); if (!await db.Organizations.AnyAsync(o => o.Id == req.PurchaserOrgId)) return BadRequest(new { message = "收购方组织不存在" }); var cu = currentUser.Get()!; var orderNo = await numberGen.NextAsync("CG"); var order = new PurchaseOrder { OrderNo = orderNo, FarmerId = req.FarmerId, ProductId = req.ProductId, PurchaserOrgId = req.PurchaserOrgId, Grade = req.Grade, Unit = req.Unit, UnitPrice = req.UnitPrice, GrossWeight = req.GrossWeight, WeighCount = 1, Status = PurchaseStatus.Weighing, OperatorId = cu.Id, Notes = req.Notes }; db.PurchaseOrders.Add(order); await db.SaveChangesAsync(); return Ok((await db.PurchaseOrders .Include(p => p.Farmer).Include(p => p.Product) .Include(p => p.PurchaserOrg).Include(p => p.Operator) .FirstAsync(p => p.Id == order.Id)).ToDto()); } /// 回皮完成(第二次过磅:记录皮重,计算净重与金额) [HttpPut("{id:int}/tare")] public async Task> CompleteTare(int id, PurchaseTareRequest req) { var order = await db.PurchaseOrders.FindAsync(id); if (order is null) return NotFound(); if (order.Status != PurchaseStatus.Weighing) return BadRequest(new { message = "该收购单当前状态不可回皮" }); if (req.TareWeight >= order.GrossWeight) return BadRequest(new { message = "皮重不能大于等于毛重" }); order.TareWeight = req.TareWeight; order.NetWeight = Math.Round(order.GrossWeight - req.TareWeight, 2); order.Amount = Math.Round(order.NetWeight * order.UnitPrice, 2); order.WeighCount = 2; order.Status = PurchaseStatus.Completed; order.WeighOutAt = DateTime.Now; order.UpdatedAt = DateTime.Now; await db.SaveChangesAsync(); return Ok((await db.PurchaseOrders .Include(p => p.Farmer).Include(p => p.Product) .Include(p => p.PurchaserOrg).Include(p => p.Operator) .FirstAsync(p => p.Id == order.Id)).ToDto()); } /// 修改单价/等级(完成前可调整) [HttpPut("{id:int}/price")] public async Task> UpdatePrice(int id, [FromBody] decimal unitPrice, string? grade = null) { var order = await db.PurchaseOrders.FindAsync(id); if (order is null) return NotFound(); if (order.Status == PurchaseStatus.Cancelled) return BadRequest(new { message = "作废单据不可修改" }); order.UnitPrice = unitPrice; if (!string.IsNullOrWhiteSpace(grade)) order.Grade = grade; order.Amount = Math.Round(order.NetWeight * order.UnitPrice, 2); order.UpdatedAt = DateTime.Now; await db.SaveChangesAsync(); return Ok((await db.PurchaseOrders .Include(p => p.Farmer).Include(p => p.Product) .Include(p => p.PurchaserOrg).Include(p => p.Operator) .FirstAsync(p => p.Id == order.Id)).ToDto()); } /// 作废收购单 [HttpPut("{id:int}/cancel")] public async Task Cancel(int id) { var order = await db.PurchaseOrders.FindAsync(id); if (order is null) return NotFound(); if (order.Status == PurchaseStatus.Cancelled) return BadRequest(new { message = "该单据已作废" }); if (await db.PaymentRecords.AnyAsync(p => p.PurchaseOrderId == id && p.Status == PayStatus.Success)) return BadRequest(new { message = "该单据已付款,请先退款后再作废" }); order.Status = PurchaseStatus.Cancelled; order.UpdatedAt = DateTime.Now; await db.SaveChangesAsync(); return Ok(new { message = "作废成功" }); } }