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/reports")] [Authorize] public class ReportsController(AppDbContext db, DataScopeService scope, CurrentUserService currentUser) : ControllerBase { /// 收购汇总报表(维度:day=按日 / month=按月 / product=按品种 / farmer=按农户 / org=按收购方) [HttpGet("purchase-summary")] public async Task> PurchaseSummary( string dimension = "day", DateTime? from = null, DateTime? to = null) { var start = from ?? DateTime.Today.AddDays(-29); var end = (to ?? DateTime.Today).Date.AddDays(1); var visible = await scope.GetVisibleOrgIdsAsync(currentUser.Get()); var query = db.PurchaseOrders .Where(p => p.Status == PurchaseStatus.Completed && p.CreatedAt >= start && p.CreatedAt < end) .AsQueryable(); if (visible is not null) query = query.Where(p => visible.Contains(p.PurchaserOrgId)); var rows = dimension switch { "month" => (await query .GroupBy(p => new { p.CreatedAt.Year, p.CreatedAt.Month }) .Select(g => new { Year = g.Key.Year, Month = g.Key.Month, Amount = g.Sum(p => p.Amount), Weight = g.Sum(p => p.NetWeight), Cnt = g.Count() }) .OrderBy(g => g.Year).ThenBy(g => g.Month) .ToListAsync()) .Select(g => new { Key = $"{g.Year}-{g.Month:D2}", g.Amount, g.Weight, g.Cnt }) .ToList(), "product" => await query .GroupBy(p => p.Product!.Name) .Select(g => new { Key = g.Key, Amount = g.Sum(p => p.Amount), Weight = g.Sum(p => p.NetWeight), Cnt = g.Count() }) .OrderByDescending(g => g.Amount).ToListAsync(), "farmer" => await query .GroupBy(p => p.Farmer!.Name) .Select(g => new { Key = g.Key, Amount = g.Sum(p => p.Amount), Weight = g.Sum(p => p.NetWeight), Cnt = g.Count() }) .OrderByDescending(g => g.Amount).Take(20).ToListAsync(), "org" => await query .GroupBy(p => p.PurchaserOrg!.Name) .Select(g => new { Key = g.Key, Amount = g.Sum(p => p.Amount), Weight = g.Sum(p => p.NetWeight), Cnt = g.Count() }) .OrderByDescending(g => g.Amount).ToListAsync(), _ => (await query .GroupBy(p => new { p.CreatedAt.Year, p.CreatedAt.Month, p.CreatedAt.Day }) .Select(g => new { Year = g.Key.Year, Month = g.Key.Month, Day = g.Key.Day, Amount = g.Sum(p => p.Amount), Weight = g.Sum(p => p.NetWeight), Cnt = g.Count() }) .OrderBy(g => g.Year).ThenBy(g => g.Month).ThenBy(g => g.Day) .ToListAsync()) .Select(g => new { Key = $"{g.Year}-{g.Month:D2}-{g.Day:D2}", g.Amount, g.Weight, g.Cnt }) .ToList() }; return Ok(new PurchaseSummaryResult( dimension, start, end, rows.Sum(r => r.Amount), rows.Sum(r => r.Weight), rows.Sum(r => r.Cnt), rows.Select(r => new SummaryRow(r.Key, r.Key, r.Amount, r.Cnt)).ToArray())); } /// 付款统计报表 [HttpGet("payment-summary")] public async Task> PaymentSummary(DateTime? from = null, DateTime? to = null) { var start = from ?? DateTime.Today.AddDays(-29); var end = (to ?? DateTime.Today).Date.AddDays(1); 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)); var inRange = query.Where(p => p.CreatedAt >= start && p.CreatedAt < end); var totalPaid = await inRange.Where(p => p.Status == PayStatus.Success).SumAsync(p => (decimal?)p.Amount) ?? 0; var totalPending = await inRange.Where(p => p.Status == PayStatus.Pending).SumAsync(p => (decimal?)p.Amount) ?? 0; var payCount = await inRange.Where(p => p.Status == PayStatus.Success).CountAsync(); var byMethod = await inRange.GroupBy(p => p.Method.ToString()) .Select(g => new { Key = g.Key, Value = g.Sum(p => p.Amount), Cnt = g.Count() }) .ToListAsync(); var byStatus = await inRange.GroupBy(p => p.Status.ToString()) .Select(g => new { Key = g.Key, Value = g.Sum(p => p.Amount), Cnt = g.Count() }) .ToListAsync(); return Ok(new PaymentSummaryResult(start, end, totalPaid, totalPending, payCount, byMethod.Select(g => new SummaryRow(g.Key, g.Key, g.Value, g.Cnt)).ToArray(), byStatus.Select(g => new SummaryRow(g.Key, g.Key, g.Value, g.Cnt)).ToArray())); } /// 开票统计报表 [HttpGet("invoice-summary")] public async Task> InvoiceSummary(DateTime? from = null, DateTime? to = null) { var start = from ?? DateTime.Today.AddDays(-29); var end = (to ?? DateTime.Today).Date.AddDays(1); var visible = await scope.GetVisibleOrgIdsAsync(currentUser.Get()); var query = db.Invoices.Where(i => i.IssueDate >= start && i.IssueDate < end).AsQueryable(); if (visible is not null) query = query.Where(i => visible.Contains(i.PurchaserOrgId)); var issued = query.Where(i => i.Status == InvoiceStatus.Issued); var totalAmount = await issued.SumAsync(i => (decimal?)i.Amount) ?? 0; var totalTax = await issued.SumAsync(i => (decimal?)i.TaxAmount) ?? 0; var issueCount = await issued.CountAsync(); var byProduct = await issued.GroupBy(i => i.Product!.Name) .Select(g => new { Key = g.Key, Value = g.Sum(i => i.Amount), Cnt = g.Count() }) .OrderByDescending(g => g.Value).ToListAsync(); var byStatus = await query.GroupBy(i => i.Status.ToString()) .Select(g => new { Key = g.Key, Value = g.Sum(i => i.Amount), Cnt = g.Count() }) .ToListAsync(); return Ok(new InvoiceSummaryResult(start, end, totalAmount, totalTax, issueCount, byProduct.Select(g => new SummaryRow(g.Key, g.Key, g.Value, g.Cnt)).ToArray(), byStatus.Select(g => new SummaryRow(g.Key, g.Key, g.Value, g.Cnt)).ToArray())); } }