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/payments")] [Authorize] public class PaymentsController( AppDbContext db, DataScopeService scope, CurrentUserService currentUser, NumberGenerator numberGen) : ControllerBase { /// 付款记录列表 [HttpGet] public async Task>> List( string? status, int? farmerId, 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.PaymentRecords .Include(p => p.Farmer).Include(p => p.PurchaseOrder) .Include(p => p.PurchaserOrg).Include(p => p.Operator) .AsQueryable(); if (visible is not null) query = query.Where(p => p.PurchaserOrgId != null && visible.Contains(p.PurchaserOrgId.Value)); 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 (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.PayNo.Contains(keyword) || p.TradeNo.Contains(keyword) || (p.Farmer != null && p.Farmer.Name.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("stats")] public async Task Stats() { var visible = await scope.GetVisibleOrgIdsAsync(currentUser.Get()); var query = db.PaymentRecords.AsQueryable(); if (visible is not null) query = query.Where(p => p.PurchaserOrgId != null && visible.Contains(p.PurchaserOrgId.Value)); return Ok(new { pending = await query.CountAsync(p => p.Status == PayStatus.Pending), success = await query.CountAsync(p => p.Status == PayStatus.Success), failed = await query.CountAsync(p => p.Status == PayStatus.Failed), abnormal = await query.CountAsync(p => p.Status == PayStatus.Abnormal) }); } /// 农户应收/已付汇总(结算用) [HttpGet("farmer-summary")] public async Task FarmerSummary(int farmerId, int? orgId) { var visible = await scope.GetVisibleOrgIdsAsync(currentUser.Get()); var orderQuery = db.PurchaseOrders .Where(p => p.FarmerId == farmerId && p.Status == PurchaseStatus.Completed) .AsQueryable(); if (visible is not null) orderQuery = orderQuery.Where(p => visible.Contains(p.PurchaserOrgId)); if (orgId.HasValue) orderQuery = orderQuery.Where(p => p.PurchaserOrgId == orgId); var totalAmount = await orderQuery.SumAsync(p => (decimal?)p.Amount) ?? 0; var payQuery = db.PaymentRecords .Where(p => p.FarmerId == farmerId && p.Status == PayStatus.Success) .AsQueryable(); if (visible is not null) payQuery = payQuery.Where(p => p.PurchaserOrgId != null && visible.Contains(p.PurchaserOrgId.Value)); if (orgId.HasValue) payQuery = payQuery.Where(p => p.PurchaserOrgId == orgId); var paidAmount = await payQuery.SumAsync(p => (decimal?)p.Amount) ?? 0; // 未结算的收购单(可批量付款) var unpaidOrders = await db.PurchaseOrders .Where(p => p.FarmerId == farmerId && p.Status == PurchaseStatus.Completed) .Where(p => !db.PaymentRecords.Any(pay => pay.PurchaseOrderId == p.Id && pay.Status == PayStatus.Success)) .Include(p => p.Product) .Select(p => new { p.Id, p.OrderNo, p.Product!.Name, p.NetWeight, p.Amount, p.UnitPrice }) .ToListAsync(); return Ok(new { farmerId, totalAmount, paidAmount, unpaidAmount = Math.Round(totalAmount - paidAmount, 2), unpaidOrders }); } /// 发起支付(按收购单或对农户批量结算) [HttpPost] public async Task> Create(PaymentCreateRequest req) { if (!await db.Farmers.AnyAsync(f => f.Id == req.FarmerId)) return BadRequest(new { message = "农户不存在" }); if (!Enum.TryParse(req.Method, out var method)) return BadRequest(new { message = "支付方式不正确" }); // 按收购单付款:校验订单并防止重复支付 if (req.PurchaseOrderId.HasValue) { var order = await db.PurchaseOrders.FindAsync(req.PurchaseOrderId); if (order is null || order.FarmerId != req.FarmerId) return BadRequest(new { message = "收购单不存在或与农户不匹配" }); if (await db.PaymentRecords.AnyAsync(p => p.PurchaseOrderId == req.PurchaseOrderId && p.Status == PayStatus.Success)) return BadRequest(new { message = "该收购单已支付成功" }); } var cu = currentUser.Get()!; int? purchaserOrgId = req.PurchaseOrderId.HasValue ? (await db.PurchaseOrders.FindAsync(req.PurchaseOrderId))?.PurchaserOrgId : cu.OrgId; var payNo = await numberGen.NextAsync("ZF"); var payment = new PaymentRecord { PayNo = payNo, PurchaseOrderId = req.PurchaseOrderId, PurchaserOrgId = purchaserOrgId, FarmerId = req.FarmerId, Amount = req.Amount, Method = method, Status = PayStatus.Pending, OperatorId = cu.Id, Notes = req.Notes }; db.PaymentRecords.Add(payment); await db.SaveChangesAsync(); return Ok((await db.PaymentRecords .Include(p => p.Farmer).Include(p => p.PurchaseOrder) .Include(p => p.PurchaserOrg).Include(p => p.Operator) .FirstAsync(p => p.Id == payment.Id)).ToDto()); } /// 确认支付结果(模拟收单回调,默认支付成功,可选失败/异常) [HttpPut("{id:int}/confirm")] public async Task> Confirm(int id, PaymentConfirmRequest req) { var payment = await db.PaymentRecords.FindAsync(id); if (payment is null) return NotFound(); if (payment.Status != PayStatus.Pending) return BadRequest(new { message = "当前状态不可确认" }); // Status 缺省或非法时按成功处理;成功/失败/异常均可写入 var result = Enum.TryParse(req.Status, out var st) && st is PayStatus.Success or PayStatus.Failed or PayStatus.Abnormal ? st : PayStatus.Success; payment.Status = result; if (result == PayStatus.Success) { payment.PaidAt = DateTime.Now; payment.TradeNo = string.IsNullOrWhiteSpace(req.TradeNo) ? payment.TradeNo : req.TradeNo; if (string.IsNullOrWhiteSpace(payment.TradeNo)) payment.TradeNo = $"TRADE{DateTime.Now:yyyyMMddHHmmss}{Random.Shared.Next(100, 999)}"; } if (!string.IsNullOrWhiteSpace(req.Message)) payment.Notes = string.IsNullOrWhiteSpace(payment.Notes) ? req.Message : $"{payment.Notes}\n{req.Message}"; await db.SaveChangesAsync(); return Ok((await db.PaymentRecords .Include(p => p.Farmer).Include(p => p.PurchaseOrder) .Include(p => p.PurchaserOrg).Include(p => p.Operator) .FirstAsync(p => p.Id == payment.Id)).ToDto()); } /// 退款 [HttpPut("{id:int}/refund")] public async Task Refund(int id) { var payment = await db.PaymentRecords.FindAsync(id); if (payment is null) return NotFound(); if (payment.Status != PayStatus.Success) return BadRequest(new { message = "仅已支付成功的记录可退款" }); payment.Status = PayStatus.Refunded; await db.SaveChangesAsync(); return Ok(new { message = "退款成功" }); } }