新增统计功能
This commit is contained in:
@@ -0,0 +1,526 @@
|
||||
// Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛
|
||||
//
|
||||
// SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款:
|
||||
// 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
// 2.请不要删除和修改SimpleAdmin源码头部的版权声明。
|
||||
// 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin
|
||||
|
||||
using SimpleAdmin.Application.Entity;
|
||||
using SimpleAdmin.Application.Services.Statistics;
|
||||
|
||||
/// <summary>
|
||||
/// 统计分析 Service 实现
|
||||
/// </summary>
|
||||
public class StatisticsService : DbRepository<Prescription>, IStatisticsService
|
||||
{
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// 公开接口
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<TotalOverviewDto> GetTotalOverview()
|
||||
{
|
||||
// 1) 所有处方(按创建时间升序,FIFO 需要)
|
||||
var prescriptionInfos = await Context.Queryable<Prescription>()
|
||||
.LeftJoin<Person>((p, pe) => p.PersonId == pe.Id)
|
||||
.OrderBy((p, pe) => p.CreateTime)
|
||||
.Select((p, pe) => new
|
||||
{
|
||||
p.Id,
|
||||
p.PersonId,
|
||||
IsRelative = pe.EmployeeId != null
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
decimal staffCost = 0, familyCost = 0;
|
||||
int staffQty = 0, familyQty = 0;
|
||||
|
||||
if (prescriptionInfos.Any())
|
||||
{
|
||||
var allIds = prescriptionInfos.Select(x => x.Id).ToList();
|
||||
|
||||
var allHerbs = await Context.Queryable<PrescriptionHerb>()
|
||||
.Where(ph => allIds.Contains(ph.PrescriptionId))
|
||||
.ToListAsync();
|
||||
|
||||
var usageGroups = allHerbs
|
||||
.GroupBy(h => new { h.PrescriptionId, h.MedicinalId })
|
||||
.Select(g => new
|
||||
{
|
||||
g.Key.PrescriptionId,
|
||||
g.Key.MedicinalId,
|
||||
TotalUsedWeight = g.Sum(h => h.AmountG)
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var allMedicinalIds = usageGroups.Select(u => u.MedicinalId).Distinct().ToList();
|
||||
var fifoData = await GetFifoData(allMedicinalIds);
|
||||
var fifoOffsets = new Dictionary<long, decimal>();
|
||||
|
||||
var usageByPres = usageGroups
|
||||
.GroupBy(u => u.PrescriptionId)
|
||||
.ToDictionary(g => g.Key, g => g.ToList());
|
||||
|
||||
foreach (var pInfo in prescriptionInfos)
|
||||
{
|
||||
decimal cost = 0;
|
||||
if (usageByPres.TryGetValue(pInfo.Id, out var usages))
|
||||
foreach (var u in usages)
|
||||
cost += CalculateFifoCost(fifoData, fifoOffsets, u.MedicinalId, u.TotalUsedWeight);
|
||||
|
||||
cost = Math.Round(cost, 4, MidpointRounding.AwayFromZero);
|
||||
|
||||
if (pInfo.IsRelative)
|
||||
{
|
||||
familyCost += Math.Round(cost * 1.1m, 2, MidpointRounding.AwayFromZero);
|
||||
familyQty++;
|
||||
}
|
||||
else
|
||||
{
|
||||
staffCost += Math.Round(cost, 2, MidpointRounding.AwayFromZero);
|
||||
staffQty++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2) 药材采购总花费(历史所有入库总额)
|
||||
var materialCost = await Context.Queryable<MedicinalAddLog>()
|
||||
.SumAsync(l => l.TotalPrice);
|
||||
|
||||
// 3) 累计服务人数(不重复人员)
|
||||
var serviceCount = prescriptionInfos.Select(x => x.PersonId).Distinct().Count();
|
||||
|
||||
// 4) 剩余药材库存价值:每种药材当前库存 × 最新入库单价
|
||||
var medicinals = await Context.Queryable<Medicinal>()
|
||||
.Where(m => m.Number > 0)
|
||||
.Select(m => new { m.Id, m.Number })
|
||||
.ToListAsync();
|
||||
|
||||
decimal remainingValue = 0;
|
||||
if (medicinals.Any())
|
||||
{
|
||||
var medIds = medicinals.Select(m => m.Id).ToList();
|
||||
|
||||
// 取每种药材最新的一条入库记录单价
|
||||
var latestPrices = await Context.Queryable<MedicinalAddLog>()
|
||||
.Where(l => medIds.Contains(l.MedicinalId))
|
||||
.GroupBy(l => l.MedicinalId)
|
||||
.Select(l => new
|
||||
{
|
||||
MedicinalId = l.MedicinalId,
|
||||
LatestPrice = SqlFunc.AggregateMax(l.UnitPrice)
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
var priceDict = latestPrices.ToDictionary(x => x.MedicinalId, x => x.LatestPrice);
|
||||
|
||||
foreach (var med in medicinals)
|
||||
{
|
||||
if (priceDict.TryGetValue(med.Id, out var price))
|
||||
remainingValue += Math.Round(med.Number * price, 2, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
}
|
||||
|
||||
return new TotalOverviewDto
|
||||
{
|
||||
TotalCost = Math.Round(staffCost + familyCost, 2),
|
||||
TotalQty = staffQty + familyQty,
|
||||
StaffCost = Math.Round(staffCost, 2),
|
||||
FamilyCost = Math.Round(familyCost, 2),
|
||||
StaffQty = staffQty,
|
||||
FamilyQty = familyQty,
|
||||
MaterialCost = Math.Round(materialCost, 2),
|
||||
ServiceCount = serviceCount,
|
||||
RemainingMedicinalValue = Math.Round(remainingValue, 2),
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<YearlyStatisticsDto> GetYearlyStatistics(int year)
|
||||
{
|
||||
var currentYearData = await BuildYearData(year);
|
||||
var lastYearData = await BuildYearData(year - 1);
|
||||
|
||||
return new YearlyStatisticsDto
|
||||
{
|
||||
Year = year.ToString(),
|
||||
TotalCost = currentYearData.TotalCost,
|
||||
TotalQty = currentYearData.TotalQty,
|
||||
StaffCost = currentYearData.StaffCost,
|
||||
FamilyCost = currentYearData.FamilyCost,
|
||||
StaffQty = currentYearData.StaffQty,
|
||||
FamilyQty = currentYearData.FamilyQty,
|
||||
MaterialCost = currentYearData.MaterialCost,
|
||||
ServiceCount = currentYearData.ServiceCount,
|
||||
|
||||
TotalCostTrend = CalcTrend(lastYearData.TotalCost, currentYearData.TotalCost),
|
||||
TotalQtyTrend = CalcTrend(lastYearData.TotalQty, currentYearData.TotalQty),
|
||||
MaterialCostTrend = CalcTrend(lastYearData.MaterialCost, currentYearData.MaterialCost),
|
||||
ServiceCountTrend = CalcTrend(lastYearData.ServiceCount, currentYearData.ServiceCount),
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<List<MonthlyDetailDto>> GetMonthlyDetails(int year)
|
||||
{
|
||||
var start = new DateTime(year, 1, 1);
|
||||
var end = new DateTime(year, 12, 31, 23, 59, 59);
|
||||
|
||||
// 1) 当年所有处方(按时间升序,供 FIFO 用)
|
||||
var prescriptionInfos = await Context.Queryable<Prescription>()
|
||||
.LeftJoin<Person>((p, pe) => p.PersonId == pe.Id)
|
||||
.Where((p, pe) => p.VisitDate >= start && p.VisitDate <= end)
|
||||
.OrderBy((p, pe) => p.CreateTime)
|
||||
.Select((p, pe) => new
|
||||
{
|
||||
p.Id,
|
||||
p.VisitDate,
|
||||
p.CreateTime,
|
||||
IsRelative = pe.EmployeeId != null
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
if (!prescriptionInfos.Any())
|
||||
return BuildEmptyMonthList();
|
||||
|
||||
var allPrescriptionIds = prescriptionInfos.Select(x => x.Id).ToList();
|
||||
|
||||
// 2) 批量拉取处方药材明细
|
||||
var allHerbs = await Context.Queryable<PrescriptionHerb>()
|
||||
.Where(ph => allPrescriptionIds.Contains(ph.PrescriptionId))
|
||||
.ToListAsync();
|
||||
|
||||
// 3) 按 处方ID+药材ID 分组,得到每张处方消耗每种药材的总量
|
||||
var usageGroups = allHerbs
|
||||
.GroupBy(h => new { h.PrescriptionId, h.MedicinalId })
|
||||
.Select(g => new
|
||||
{
|
||||
g.Key.PrescriptionId,
|
||||
g.Key.MedicinalId,
|
||||
TotalUsedWeight = g.Sum(h => h.AmountG)
|
||||
})
|
||||
.ToList();
|
||||
|
||||
// 4) FIFO 入库数据
|
||||
var allMedicinalIds = usageGroups.Select(u => u.MedicinalId).Distinct().ToList();
|
||||
var fifoData = await GetFifoData(allMedicinalIds);
|
||||
|
||||
// 5) 需要在"所有历史处方"维度跑 FIFO,才能保证成本正确(与 PrescriptionService 逻辑一致)
|
||||
// 先把当年之前的"历史"也加进来跑偏移
|
||||
var historyIds = await Context.Queryable<Prescription>()
|
||||
.Where(p => p.VisitDate < start)
|
||||
.OrderBy(p => p.CreateTime)
|
||||
.Select(p => p.Id)
|
||||
.ToListAsync();
|
||||
|
||||
var fifoOffsets = new Dictionary<long, decimal>();
|
||||
|
||||
// 先消耗历史处方(建立偏移量),不需要计入结果
|
||||
if (historyIds.Any())
|
||||
{
|
||||
var historyHerbs = await Context.Queryable<PrescriptionHerb>()
|
||||
.Where(ph => historyIds.Contains(ph.PrescriptionId))
|
||||
.ToListAsync();
|
||||
|
||||
var historyUsage = historyHerbs
|
||||
.GroupBy(h => new { h.PrescriptionId, h.MedicinalId })
|
||||
.Select(g => new
|
||||
{
|
||||
g.Key.PrescriptionId,
|
||||
g.Key.MedicinalId,
|
||||
TotalUsedWeight = g.Sum(h => h.AmountG)
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var historyByPres = historyUsage
|
||||
.GroupBy(u => u.PrescriptionId)
|
||||
.ToDictionary(g => g.Key, g => g.ToList());
|
||||
|
||||
foreach (var hid in historyIds)
|
||||
{
|
||||
if (!historyByPres.TryGetValue(hid, out var usages)) continue;
|
||||
foreach (var u in usages)
|
||||
CalculateFifoCost(fifoData, fifoOffsets, u.MedicinalId, u.TotalUsedWeight);
|
||||
}
|
||||
}
|
||||
|
||||
// 6) 计算当年每张处方的成本,按月汇总
|
||||
var usageByPres = usageGroups
|
||||
.GroupBy(u => u.PrescriptionId)
|
||||
.ToDictionary(g => g.Key, g => g.ToList());
|
||||
|
||||
// 月度桶:1–12
|
||||
var monthlyStaffCost = new decimal[13];
|
||||
var monthlyFamilyCost = new decimal[13];
|
||||
var monthlyStaffQty = new int[13];
|
||||
var monthlyFamilyQty = new int[13];
|
||||
|
||||
var presInfoDict = prescriptionInfos.ToDictionary(x => x.Id);
|
||||
|
||||
foreach (var pInfo in prescriptionInfos)
|
||||
{
|
||||
int month = pInfo.VisitDate?.Month ?? pInfo.CreateTime?.Month ?? 0;
|
||||
if (month < 1 || month > 12) continue;
|
||||
|
||||
decimal cost = 0m;
|
||||
if (usageByPres.TryGetValue(pInfo.Id, out var usages))
|
||||
{
|
||||
foreach (var u in usages)
|
||||
cost += CalculateFifoCost(fifoData, fifoOffsets, u.MedicinalId, u.TotalUsedWeight);
|
||||
}
|
||||
cost = Math.Round(cost, 4, MidpointRounding.AwayFromZero);
|
||||
|
||||
if (pInfo.IsRelative)
|
||||
{
|
||||
cost = Math.Round(cost * 1.1m, 2, MidpointRounding.AwayFromZero);
|
||||
monthlyFamilyCost[month] += cost;
|
||||
monthlyFamilyQty[month]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
cost = Math.Round(cost, 2, MidpointRounding.AwayFromZero);
|
||||
monthlyStaffCost[month] += cost;
|
||||
monthlyStaffQty[month]++;
|
||||
}
|
||||
}
|
||||
|
||||
// 7) 药材花费:按入库记录的月份汇总当年花费
|
||||
var materialLogs = await Context.Queryable<MedicinalAddLog>()
|
||||
.Where(l => l.CreateTime >= start && l.CreateTime <= end)
|
||||
.Select(l => new { l.CreateTime, l.TotalPrice })
|
||||
.ToListAsync();
|
||||
|
||||
var monthlyMaterialCost = new decimal[13];
|
||||
foreach (var log in materialLogs)
|
||||
{
|
||||
int month = log.CreateTime?.Month ?? 0;
|
||||
if (month >= 1 && month <= 12)
|
||||
monthlyMaterialCost[month] += log.TotalPrice;
|
||||
}
|
||||
|
||||
// 8) 组装返回结果
|
||||
var result = new List<MonthlyDetailDto>(12);
|
||||
for (int m = 1; m <= 12; m++)
|
||||
{
|
||||
result.Add(new MonthlyDetailDto
|
||||
{
|
||||
Month = $"{m}月",
|
||||
StaffCost = Math.Round(monthlyStaffCost[m], 2),
|
||||
FamilyCost = Math.Round(monthlyFamilyCost[m], 2),
|
||||
StaffQty = monthlyStaffQty[m],
|
||||
FamilyQty = monthlyFamilyQty[m],
|
||||
MaterialCost = Math.Round(monthlyMaterialCost[m], 2),
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// 私有辅助方法
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 计算指定年份的聚合数据(用于年度统计与同比计算)
|
||||
/// </summary>
|
||||
private async Task<(decimal TotalCost, int TotalQty, decimal StaffCost, decimal FamilyCost,
|
||||
int StaffQty, int FamilyQty, decimal MaterialCost, int ServiceCount)> BuildYearData(int year)
|
||||
{
|
||||
var start = new DateTime(year, 1, 1);
|
||||
var end = new DateTime(year, 12, 31, 23, 59, 59);
|
||||
|
||||
// 当年处方(按时间升序,FIFO 需要)
|
||||
var prescriptionInfos = await Context.Queryable<Prescription>()
|
||||
.LeftJoin<Person>((p, pe) => p.PersonId == pe.Id)
|
||||
.Where((p, pe) => p.VisitDate >= start && p.VisitDate <= end)
|
||||
.OrderBy((p, pe) => p.CreateTime)
|
||||
.Select((p, pe) => new
|
||||
{
|
||||
p.Id,
|
||||
p.PersonId,
|
||||
IsRelative = pe.EmployeeId != null
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
if (!prescriptionInfos.Any())
|
||||
return (0, 0, 0, 0, 0, 0, 0, 0);
|
||||
|
||||
var allPrescriptionIds = prescriptionInfos.Select(x => x.Id).ToList();
|
||||
|
||||
var allHerbs = await Context.Queryable<PrescriptionHerb>()
|
||||
.Where(ph => allPrescriptionIds.Contains(ph.PrescriptionId))
|
||||
.ToListAsync();
|
||||
|
||||
var usageGroups = allHerbs
|
||||
.GroupBy(h => new { h.PrescriptionId, h.MedicinalId })
|
||||
.Select(g => new
|
||||
{
|
||||
g.Key.PrescriptionId,
|
||||
g.Key.MedicinalId,
|
||||
TotalUsedWeight = g.Sum(h => h.AmountG)
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var allMedicinalIds = usageGroups.Select(u => u.MedicinalId).Distinct().ToList();
|
||||
var fifoData = await GetFifoData(allMedicinalIds);
|
||||
|
||||
// 历史偏移量(当年之前的处方)
|
||||
var historyIds = await Context.Queryable<Prescription>()
|
||||
.Where(p => p.VisitDate < start)
|
||||
.OrderBy(p => p.CreateTime)
|
||||
.Select(p => p.Id)
|
||||
.ToListAsync();
|
||||
|
||||
var fifoOffsets = new Dictionary<long, decimal>();
|
||||
if (historyIds.Any())
|
||||
{
|
||||
var historyHerbs = await Context.Queryable<PrescriptionHerb>()
|
||||
.Where(ph => historyIds.Contains(ph.PrescriptionId))
|
||||
.ToListAsync();
|
||||
|
||||
var historyUsage = historyHerbs
|
||||
.GroupBy(h => new { h.PrescriptionId, h.MedicinalId })
|
||||
.Select(g => new
|
||||
{
|
||||
g.Key.PrescriptionId,
|
||||
g.Key.MedicinalId,
|
||||
TotalUsedWeight = g.Sum(h => h.AmountG)
|
||||
})
|
||||
.GroupBy(u => u.PrescriptionId)
|
||||
.ToDictionary(g => g.Key, g => g.ToList());
|
||||
|
||||
foreach (var hid in historyIds)
|
||||
{
|
||||
if (!historyUsage.TryGetValue(hid, out var usages)) continue;
|
||||
foreach (var u in usages)
|
||||
CalculateFifoCost(fifoData, fifoOffsets, u.MedicinalId, u.TotalUsedWeight);
|
||||
}
|
||||
}
|
||||
|
||||
// 按处方计算成本
|
||||
var usageByPres = usageGroups
|
||||
.GroupBy(u => u.PrescriptionId)
|
||||
.ToDictionary(g => g.Key, g => g.ToList());
|
||||
|
||||
decimal staffCost = 0, familyCost = 0;
|
||||
int staffQty = 0, familyQty = 0;
|
||||
|
||||
foreach (var pInfo in prescriptionInfos)
|
||||
{
|
||||
decimal cost = 0;
|
||||
if (usageByPres.TryGetValue(pInfo.Id, out var usages))
|
||||
foreach (var u in usages)
|
||||
cost += CalculateFifoCost(fifoData, fifoOffsets, u.MedicinalId, u.TotalUsedWeight);
|
||||
|
||||
cost = Math.Round(cost, 4, MidpointRounding.AwayFromZero);
|
||||
|
||||
if (pInfo.IsRelative)
|
||||
{
|
||||
familyCost += Math.Round(cost * 1.1m, 2, MidpointRounding.AwayFromZero);
|
||||
familyQty++;
|
||||
}
|
||||
else
|
||||
{
|
||||
staffCost += Math.Round(cost, 2, MidpointRounding.AwayFromZero);
|
||||
staffQty++;
|
||||
}
|
||||
}
|
||||
|
||||
// 药材花费:当年所有入库记录的总额
|
||||
var materialCost = await Context.Queryable<MedicinalAddLog>()
|
||||
.Where(l => l.CreateTime >= start && l.CreateTime <= end)
|
||||
.SumAsync(l => l.TotalPrice);
|
||||
|
||||
// 服务人数:当年有处方的不重复人员
|
||||
var serviceCount = prescriptionInfos.Select(x => x.PersonId).Distinct().Count();
|
||||
|
||||
return (
|
||||
Math.Round(staffCost + familyCost, 2),
|
||||
staffQty + familyQty,
|
||||
Math.Round(staffCost, 2),
|
||||
Math.Round(familyCost, 2),
|
||||
staffQty,
|
||||
familyQty,
|
||||
Math.Round(materialCost, 2),
|
||||
serviceCount
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取 FIFO 入库数据(按时间升序)
|
||||
/// </summary>
|
||||
private async Task<Dictionary<long, List<MedicinalFifoRecord>>> GetFifoData(List<long> medicinalIds)
|
||||
{
|
||||
if (!medicinalIds.Any())
|
||||
return new Dictionary<long, List<MedicinalFifoRecord>>();
|
||||
|
||||
var logs = await Context.Queryable<MedicinalAddLog>()
|
||||
.Where(l => medicinalIds.Contains(l.MedicinalId))
|
||||
.OrderBy(l => l.CreateTime)
|
||||
.Select(l => new MedicinalFifoRecord
|
||||
{
|
||||
MedicinalId = l.MedicinalId,
|
||||
AddWeight = l.AddWeight,
|
||||
UnitPrice = l.UnitPrice
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
return logs.GroupBy(l => l.MedicinalId)
|
||||
.ToDictionary(g => g.Key, g => g.ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按 FIFO 规则(含累计偏移)计算单次消耗成本,并推进偏移量
|
||||
/// </summary>
|
||||
private static decimal CalculateFifoCost(
|
||||
Dictionary<long, List<MedicinalFifoRecord>> fifoData,
|
||||
Dictionary<long, decimal> fifoOffsets,
|
||||
long medicinalId,
|
||||
decimal usedWeight)
|
||||
{
|
||||
if (usedWeight <= 0 || !fifoData.TryGetValue(medicinalId, out var records) || !records.Any())
|
||||
return 0;
|
||||
|
||||
decimal alreadyUsed = fifoOffsets.GetValueOrDefault(medicinalId, 0);
|
||||
decimal remainingToUse = usedWeight;
|
||||
decimal totalCost = 0;
|
||||
|
||||
foreach (var record in records)
|
||||
{
|
||||
if (remainingToUse <= 0) break;
|
||||
|
||||
if (alreadyUsed >= record.AddWeight)
|
||||
{
|
||||
alreadyUsed -= record.AddWeight;
|
||||
continue;
|
||||
}
|
||||
|
||||
decimal available = record.AddWeight - alreadyUsed;
|
||||
decimal toConsume = Math.Min(available, remainingToUse);
|
||||
|
||||
totalCost += toConsume * record.UnitPrice;
|
||||
remainingToUse -= toConsume;
|
||||
alreadyUsed = 0;
|
||||
}
|
||||
|
||||
fifoOffsets[medicinalId] = fifoOffsets.GetValueOrDefault(medicinalId, 0) + usedWeight;
|
||||
return Math.Round(totalCost, 4, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
|
||||
/// <summary>计算同比增涨率(%),基期为 0 时返回 0</summary>
|
||||
private static decimal CalcTrend<T>(T baseVal, T currentVal) where T : struct, IConvertible
|
||||
{
|
||||
decimal b = Convert.ToDecimal(baseVal);
|
||||
decimal c = Convert.ToDecimal(currentVal);
|
||||
if (b == 0) return 0;
|
||||
return Math.Round((c - b) / b * 100, 1, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
|
||||
/// <summary>返回全零的 12 个月明细列表</summary>
|
||||
private static List<MonthlyDetailDto> BuildEmptyMonthList()
|
||||
{
|
||||
var list = new List<MonthlyDetailDto>(12);
|
||||
for (int m = 1; m <= 12; m++)
|
||||
list.Add(new MonthlyDetailDto { Month = $"{m}月" });
|
||||
return list;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user