Commit 3b3075584bd52466df71a0a1fdaaf9eef51c655c
1 parent
7ba2d701
统计接口实现
Showing
8 changed files
with
514 additions
and
1 deletions
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Dashboard/DashboardCategoryDistributionDto.cs
0 → 100644
| 1 | +namespace FoodLabeling.Application.Contracts.Dtos.Dashboard; | |
| 2 | + | |
| 3 | +/// <summary> | |
| 4 | +/// 分类分布项 | |
| 5 | +/// </summary> | |
| 6 | +public class DashboardCategoryDistributionDto | |
| 7 | +{ | |
| 8 | + /// <summary>分类Id</summary> | |
| 9 | + public string CategoryId { get; set; } = string.Empty; | |
| 10 | + | |
| 11 | + /// <summary>分类名称</summary> | |
| 12 | + public string CategoryName { get; set; } = string.Empty; | |
| 13 | + | |
| 14 | + /// <summary>数量</summary> | |
| 15 | + public int Count { get; set; } | |
| 16 | + | |
| 17 | + /// <summary>占比(百分比)</summary> | |
| 18 | + public decimal Ratio { get; set; } | |
| 19 | +} | ... | ... |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Dashboard/DashboardDailyTrendPointDto.cs
0 → 100644
| 1 | +namespace FoodLabeling.Application.Contracts.Dtos.Dashboard; | |
| 2 | + | |
| 3 | +/// <summary> | |
| 4 | +/// 日趋势点 | |
| 5 | +/// </summary> | |
| 6 | +public class DashboardDailyTrendPointDto | |
| 7 | +{ | |
| 8 | + /// <summary>日期(yyyy-MM-dd)</summary> | |
| 9 | + public string Date { get; set; } = string.Empty; | |
| 10 | + | |
| 11 | + /// <summary>值</summary> | |
| 12 | + public int Value { get; set; } | |
| 13 | +} | ... | ... |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Dashboard/DashboardMetricCardDto.cs
0 → 100644
| 1 | +namespace FoodLabeling.Application.Contracts.Dtos.Dashboard; | |
| 2 | + | |
| 3 | +/// <summary> | |
| 4 | +/// 仪表盘指标卡片 | |
| 5 | +/// </summary> | |
| 6 | +public class DashboardMetricCardDto | |
| 7 | +{ | |
| 8 | + /// <summary>指标唯一标识(如 labelsPrintedToday)</summary> | |
| 9 | + public string Key { get; set; } = string.Empty; | |
| 10 | + | |
| 11 | + /// <summary>指标标题</summary> | |
| 12 | + public string Title { get; set; } = string.Empty; | |
| 13 | + | |
| 14 | + /// <summary>当前值</summary> | |
| 15 | + public int Value { get; set; } | |
| 16 | + | |
| 17 | + /// <summary>对比周期值</summary> | |
| 18 | + public int PreviousValue { get; set; } | |
| 19 | + | |
| 20 | + /// <summary>增减值(Value - PreviousValue)</summary> | |
| 21 | + public int ChangeValue { get; set; } | |
| 22 | + | |
| 23 | + /// <summary>增减比例(百分比)</summary> | |
| 24 | + public decimal ChangeRate { get; set; } | |
| 25 | +} | ... | ... |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Dashboard/DashboardOverviewOutputDto.cs
0 → 100644
| 1 | +namespace FoodLabeling.Application.Contracts.Dtos.Dashboard; | |
| 2 | + | |
| 3 | +/// <summary> | |
| 4 | +/// 仪表盘总览输出 | |
| 5 | +/// </summary> | |
| 6 | +public class DashboardOverviewOutputDto | |
| 7 | +{ | |
| 8 | + /// <summary>今日打印标签</summary> | |
| 9 | + public DashboardMetricCardDto LabelsPrintedToday { get; set; } = new(); | |
| 10 | + | |
| 11 | + /// <summary>启用模板数</summary> | |
| 12 | + public DashboardMetricCardDto ActiveTemplates { get; set; } = new(); | |
| 13 | + | |
| 14 | + /// <summary>活跃用户数</summary> | |
| 15 | + public DashboardMetricCardDto ActiveUsers { get; set; } = new(); | |
| 16 | + | |
| 17 | + /// <summary>门店数</summary> | |
| 18 | + public DashboardMetricCardDto Locations { get; set; } = new(); | |
| 19 | + | |
| 20 | + /// <summary>人员数</summary> | |
| 21 | + public DashboardMetricCardDto People { get; set; } = new(); | |
| 22 | + | |
| 23 | + /// <summary>产品数</summary> | |
| 24 | + public DashboardMetricCardDto Products { get; set; } = new(); | |
| 25 | + | |
| 26 | + /// <summary>指标卡片</summary> | |
| 27 | + public List<DashboardMetricCardDto> MetricCards { get; set; } = new(); | |
| 28 | + | |
| 29 | + /// <summary>近7天打印趋势</summary> | |
| 30 | + public List<DashboardDailyTrendPointDto> WeeklyPrintVolume { get; set; } = new(); | |
| 31 | + | |
| 32 | + /// <summary>按分类分布</summary> | |
| 33 | + public List<DashboardCategoryDistributionDto> CategoryDistribution { get; set; } = new(); | |
| 34 | + | |
| 35 | + /// <summary>分类分布总数</summary> | |
| 36 | + public int CategoryDistributionTotal { get; set; } | |
| 37 | + | |
| 38 | + /// <summary>按分类分布(前端直观命名)</summary> | |
| 39 | + public List<DashboardCategoryDistributionDto> ByCategory { get; set; } = new(); | |
| 40 | + | |
| 41 | + /// <summary>按分类分布总数(前端直观命名)</summary> | |
| 42 | + public int ByCategoryTotal { get; set; } | |
| 43 | + | |
| 44 | + /// <summary>统计时间</summary> | |
| 45 | + public DateTime GeneratedAt { get; set; } | |
| 46 | +} | ... | ... |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IDashboardAppService.cs
0 → 100644
| 1 | +using FoodLabeling.Application.Contracts.Dtos.Dashboard; | |
| 2 | +using Volo.Abp.Application.Services; | |
| 3 | + | |
| 4 | +namespace FoodLabeling.Application.Contracts.IServices; | |
| 5 | + | |
| 6 | +/// <summary> | |
| 7 | +/// Dashboard 统计接口(美国版) | |
| 8 | +/// </summary> | |
| 9 | +public interface IDashboardAppService : IApplicationService | |
| 10 | +{ | |
| 11 | + /// <summary> | |
| 12 | + /// 获取 Dashboard 总览统计(卡片 + 周趋势 + 分类分布) | |
| 13 | + /// </summary> | |
| 14 | + Task<DashboardOverviewOutputDto> GetOverviewAsync(); | |
| 15 | +} | ... | ... |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DashboardAppService.cs
0 → 100644
| 1 | +using FoodLabeling.Application.Contracts.Dtos.Dashboard; | |
| 2 | +using FoodLabeling.Application.Contracts.IServices; | |
| 3 | +using FoodLabeling.Application.Services.DbModels; | |
| 4 | +using FoodLabeling.Domain.Entities; | |
| 5 | +using Volo.Abp.Application.Services; | |
| 6 | +using Yi.Framework.Rbac.Domain.Entities; | |
| 7 | +using Yi.Framework.SqlSugarCore.Abstractions; | |
| 8 | + | |
| 9 | +namespace FoodLabeling.Application.Services; | |
| 10 | + | |
| 11 | +/// <summary> | |
| 12 | +/// Dashboard 统计服务(美国版) | |
| 13 | +/// </summary> | |
| 14 | +public class DashboardAppService : ApplicationService, IDashboardAppService | |
| 15 | +{ | |
| 16 | + private readonly ISqlSugarDbContext _dbContext; | |
| 17 | + private readonly ISqlSugarRepository<LocationAggregateRoot, Guid> _locationRepository; | |
| 18 | + private readonly ISqlSugarRepository<UserAggregateRoot, Guid> _userRepository; | |
| 19 | + | |
| 20 | + public DashboardAppService( | |
| 21 | + ISqlSugarDbContext dbContext, | |
| 22 | + ISqlSugarRepository<LocationAggregateRoot, Guid> locationRepository, | |
| 23 | + ISqlSugarRepository<UserAggregateRoot, Guid> userRepository) | |
| 24 | + { | |
| 25 | + _dbContext = dbContext; | |
| 26 | + _locationRepository = locationRepository; | |
| 27 | + _userRepository = userRepository; | |
| 28 | + } | |
| 29 | + | |
| 30 | + /// <inheritdoc /> | |
| 31 | + public async Task<DashboardOverviewOutputDto> GetOverviewAsync() | |
| 32 | + { | |
| 33 | + var now = DateTime.Now; | |
| 34 | + var todayStart = now.Date; | |
| 35 | + var tomorrowStart = todayStart.AddDays(1); | |
| 36 | + var yesterdayStart = todayStart.AddDays(-1); | |
| 37 | + var weekStart = todayStart.AddDays(-6); | |
| 38 | + var prevWeekStart = todayStart.AddDays(-13); | |
| 39 | + | |
| 40 | + var printedToday = await _dbContext.SqlSugarClient.Queryable<FlLabelPrintTaskDbEntity>() | |
| 41 | + .CountAsync(x => x.CreationTime >= todayStart && x.CreationTime < tomorrowStart); | |
| 42 | + | |
| 43 | + var printedYesterday = await _dbContext.SqlSugarClient.Queryable<FlLabelPrintTaskDbEntity>() | |
| 44 | + .CountAsync(x => x.CreationTime >= yesterdayStart && x.CreationTime < todayStart); | |
| 45 | + | |
| 46 | + var activeTemplates = await _dbContext.SqlSugarClient.Queryable<FlLabelTemplateDbEntity>() | |
| 47 | + .CountAsync(x => !x.IsDeleted && x.State); | |
| 48 | + var activeTemplatesPrevWeek = await _dbContext.SqlSugarClient.Queryable<FlLabelTemplateDbEntity>() | |
| 49 | + .CountAsync(x => !x.IsDeleted && x.State && x.CreationTime < weekStart); | |
| 50 | + | |
| 51 | + var activeUsers = await _userRepository._DbQueryable | |
| 52 | + .CountAsync(x => !x.IsDeleted && x.State); | |
| 53 | + var activeUsersPrevWeek = await _userRepository._DbQueryable | |
| 54 | + .CountAsync(x => !x.IsDeleted && x.State && x.CreationTime < weekStart); | |
| 55 | + | |
| 56 | + var locations = await _locationRepository._DbQueryable | |
| 57 | + .CountAsync(x => !x.IsDeleted); | |
| 58 | + var locationsPrevWeek = await _locationRepository._DbQueryable | |
| 59 | + .CountAsync(x => !x.IsDeleted && x.CreationTime < weekStart); | |
| 60 | + | |
| 61 | + var people = await _userRepository._DbQueryable | |
| 62 | + .CountAsync(x => !x.IsDeleted); | |
| 63 | + var peoplePrevWeek = await _userRepository._DbQueryable | |
| 64 | + .CountAsync(x => !x.IsDeleted && x.CreationTime < weekStart); | |
| 65 | + | |
| 66 | + var products = await _dbContext.SqlSugarClient.Queryable<FlProductDbEntity>() | |
| 67 | + .CountAsync(x => !x.IsDeleted); | |
| 68 | + // fl_product 当前实体未映射 CreationTime,无法按时间切分对比,先回退为同口径总量对比 | |
| 69 | + var productsPrevWeek = products; | |
| 70 | + | |
| 71 | + var weeklyPrintRaw = await _dbContext.SqlSugarClient.Queryable<FlLabelPrintTaskDbEntity>() | |
| 72 | + .Where(x => x.CreationTime >= weekStart && x.CreationTime < tomorrowStart) | |
| 73 | + .Select(x => x.CreationTime) | |
| 74 | + .ToListAsync(); | |
| 75 | + | |
| 76 | + var weeklyDict = weeklyPrintRaw | |
| 77 | + .GroupBy(x => x.Date) | |
| 78 | + .ToDictionary(g => g.Key, g => g.Count()); | |
| 79 | + | |
| 80 | + var weeklyTrend = Enumerable.Range(0, 7) | |
| 81 | + .Select(i => | |
| 82 | + { | |
| 83 | + var d = weekStart.AddDays(i).Date; | |
| 84 | + return new DashboardDailyTrendPointDto | |
| 85 | + { | |
| 86 | + Date = d.ToString("yyyy-MM-dd"), | |
| 87 | + Value = weeklyDict.TryGetValue(d, out var v) ? v : 0 | |
| 88 | + }; | |
| 89 | + }) | |
| 90 | + .ToList(); | |
| 91 | + | |
| 92 | + var categories = await _dbContext.SqlSugarClient.Queryable<FlLabelCategoryDbEntity>() | |
| 93 | + .Where(x => !x.IsDeleted && x.State) | |
| 94 | + .ToListAsync(); | |
| 95 | + | |
| 96 | + var labelCategoryIds = categories.Select(x => x.Id).ToList(); | |
| 97 | + var labelRows = labelCategoryIds.Count == 0 | |
| 98 | + ? new List<string?>() | |
| 99 | + : await _dbContext.SqlSugarClient.Queryable<FlLabelDbEntity>() | |
| 100 | + .Where(x => !x.IsDeleted && x.LabelCategoryId != null && labelCategoryIds.Contains(x.LabelCategoryId)) | |
| 101 | + .Select(x => x.LabelCategoryId) | |
| 102 | + .ToListAsync(); | |
| 103 | + | |
| 104 | + var labelCountByCategory = labelRows | |
| 105 | + .Where(x => !string.IsNullOrWhiteSpace(x)) | |
| 106 | + .GroupBy(x => x!) | |
| 107 | + .ToDictionary(g => g.Key, g => g.Count()); | |
| 108 | + | |
| 109 | + var categoryDistributionTotal = labelCountByCategory.Values.Sum(); | |
| 110 | + var categoryDistribution = categories | |
| 111 | + .Select(c => | |
| 112 | + { | |
| 113 | + var count = labelCountByCategory.TryGetValue(c.Id, out var v) ? v : 0; | |
| 114 | + var ratio = categoryDistributionTotal == 0 | |
| 115 | + ? 0m | |
| 116 | + : Math.Round(count * 100m / categoryDistributionTotal, 2); | |
| 117 | + return new DashboardCategoryDistributionDto | |
| 118 | + { | |
| 119 | + CategoryId = c.Id, | |
| 120 | + CategoryName = c.CategoryName, | |
| 121 | + Count = count, | |
| 122 | + Ratio = ratio | |
| 123 | + }; | |
| 124 | + }) | |
| 125 | + .Where(x => x.Count > 0) | |
| 126 | + .OrderByDescending(x => x.Count) | |
| 127 | + .ThenBy(x => x.CategoryName) | |
| 128 | + .ToList(); | |
| 129 | + | |
| 130 | + var labelsPrintedTodayCard = BuildMetricCard("labelsPrintedToday", "Labels Printed Today", printedToday, printedYesterday); | |
| 131 | + var activeTemplatesCard = BuildMetricCard("activeTemplates", "Active Templates", activeTemplates, activeTemplatesPrevWeek); | |
| 132 | + var activeUsersCard = BuildMetricCard("activeUsers", "Active Users", activeUsers, activeUsersPrevWeek); | |
| 133 | + var locationsCard = BuildMetricCard("locations", "Locations", locations, locationsPrevWeek); | |
| 134 | + var peopleCard = BuildMetricCard("people", "People", people, peoplePrevWeek); | |
| 135 | + var productsCard = BuildMetricCard("products", "Products", products, productsPrevWeek); | |
| 136 | + | |
| 137 | + var output = new DashboardOverviewOutputDto | |
| 138 | + { | |
| 139 | + LabelsPrintedToday = labelsPrintedTodayCard, | |
| 140 | + ActiveTemplates = activeTemplatesCard, | |
| 141 | + ActiveUsers = activeUsersCard, | |
| 142 | + Locations = locationsCard, | |
| 143 | + People = peopleCard, | |
| 144 | + Products = productsCard, | |
| 145 | + MetricCards = new List<DashboardMetricCardDto> | |
| 146 | + { | |
| 147 | + labelsPrintedTodayCard, | |
| 148 | + activeTemplatesCard, | |
| 149 | + activeUsersCard, | |
| 150 | + locationsCard, | |
| 151 | + peopleCard, | |
| 152 | + productsCard | |
| 153 | + }, | |
| 154 | + WeeklyPrintVolume = weeklyTrend, | |
| 155 | + CategoryDistribution = categoryDistribution, | |
| 156 | + CategoryDistributionTotal = categoryDistributionTotal, | |
| 157 | + ByCategory = categoryDistribution, | |
| 158 | + ByCategoryTotal = categoryDistributionTotal, | |
| 159 | + GeneratedAt = now | |
| 160 | + }; | |
| 161 | + | |
| 162 | + return output; | |
| 163 | + } | |
| 164 | + | |
| 165 | + private static DashboardMetricCardDto BuildMetricCard(string key, string title, int value, int previousValue) | |
| 166 | + { | |
| 167 | + var changeValue = value - previousValue; | |
| 168 | + var changeRate = previousValue <= 0 | |
| 169 | + ? (value > 0 ? 100m : 0m) | |
| 170 | + : Math.Round(changeValue * 100m / previousValue, 2); | |
| 171 | + | |
| 172 | + return new DashboardMetricCardDto | |
| 173 | + { | |
| 174 | + Key = key, | |
| 175 | + Title = title, | |
| 176 | + Value = value, | |
| 177 | + PreviousValue = previousValue, | |
| 178 | + ChangeValue = changeValue, | |
| 179 | + ChangeRate = changeRate | |
| 180 | + }; | |
| 181 | + } | |
| 182 | +} | ... | ... |
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/UsAppAuthAppService.cs
| ... | ... | @@ -38,7 +38,6 @@ namespace FoodLabeling.Application.Services; |
| 38 | 38 | public class UsAppAuthAppService : ApplicationService, IUsAppAuthAppService |
| 39 | 39 | { |
| 40 | 40 | private readonly IAccountManager _accountManager; |
| 41 | - | |
| 42 | 41 | private readonly ISqlSugarRepository<UserAggregateRoot, Guid> _userRepository; |
| 43 | 42 | private readonly ISqlSugarDbContext _dbContext; |
| 44 | 43 | private readonly IHttpContextAccessor _httpContextAccessor; | ... | ... |
项目相关文档/Dashboard统计接口对接说明.md
0 → 100644
| 1 | +# Dashboard 统计接口对接说明 | |
| 2 | + | |
| 3 | +> 适用范围:美国版 Web 管理端 Dashboard 首页统计 | |
| 4 | +> | |
| 5 | +> 接口实现:`IDashboardAppService.GetOverviewAsync` / `DashboardAppService.GetOverviewAsync` | |
| 6 | + | |
| 7 | +--- | |
| 8 | + | |
| 9 | +## 1. 接口信息 | |
| 10 | + | |
| 11 | +- **方法**:`GET` | |
| 12 | +- **路径**:`/api/app/dashboard/overview` | |
| 13 | +- **鉴权**:需要登录(Bearer Token) | |
| 14 | +- **请求参数**:无 | |
| 15 | + | |
| 16 | +--- | |
| 17 | + | |
| 18 | +## 2. 返回结构(顶层) | |
| 19 | + | |
| 20 | +```json | |
| 21 | +{ | |
| 22 | + "labelsPrintedToday": {}, | |
| 23 | + "activeTemplates": {}, | |
| 24 | + "activeUsers": {}, | |
| 25 | + "locations": {}, | |
| 26 | + "people": {}, | |
| 27 | + "products": {}, | |
| 28 | + "weeklyPrintVolume": [], | |
| 29 | + "byCategory": [], | |
| 30 | + "byCategoryTotal": 0, | |
| 31 | + "generatedAt": "2026-04-22T10:00:00+08:00", | |
| 32 | + | |
| 33 | + "metricCards": [], | |
| 34 | + "categoryDistribution": [], | |
| 35 | + "categoryDistributionTotal": 0 | |
| 36 | +} | |
| 37 | +``` | |
| 38 | + | |
| 39 | +说明: | |
| 40 | +- `labelsPrintedToday/activeTemplates/...`、`byCategory/byCategoryTotal` 是**前端直观命名**(推荐使用)。 | |
| 41 | +- `metricCards`、`categoryDistribution`、`categoryDistributionTotal` 为**兼容字段**(与旧版返回一致)。 | |
| 42 | + | |
| 43 | +--- | |
| 44 | + | |
| 45 | +## 3. 字段说明 | |
| 46 | + | |
| 47 | +### 3.1 指标卡片对象(`DashboardMetricCardDto`) | |
| 48 | + | |
| 49 | +用于以下字段: | |
| 50 | +- `labelsPrintedToday` | |
| 51 | +- `activeTemplates` | |
| 52 | +- `activeUsers` | |
| 53 | +- `locations` | |
| 54 | +- `people` | |
| 55 | +- `products` | |
| 56 | +- `metricCards[]`(同结构) | |
| 57 | + | |
| 58 | +| 字段 | 类型 | 说明 | | |
| 59 | +|---|---|---| | |
| 60 | +| `key` | string | 指标标识(如 `labelsPrintedToday`) | | |
| 61 | +| `title` | string | 指标标题 | | |
| 62 | +| `value` | int | 当前值 | | |
| 63 | +| `previousValue` | int | 对比周期值 | | |
| 64 | +| `changeValue` | int | 增减值(`value - previousValue`) | | |
| 65 | +| `changeRate` | decimal | 增减比例(百分比,保留 2 位) | | |
| 66 | + | |
| 67 | +--- | |
| 68 | + | |
| 69 | +### 3.2 周趋势(`weeklyPrintVolume`) | |
| 70 | + | |
| 71 | +| 字段 | 类型 | 说明 | | |
| 72 | +|---|---|---| | |
| 73 | +| `date` | string | 日期,格式 `yyyy-MM-dd` | | |
| 74 | +| `value` | int | 当天打印量 | | |
| 75 | + | |
| 76 | +--- | |
| 77 | + | |
| 78 | +### 3.3 分类分布(`byCategory`) | |
| 79 | + | |
| 80 | +`byCategory` 与 `categoryDistribution` 结构一致。 | |
| 81 | + | |
| 82 | +| 字段 | 类型 | 说明 | | |
| 83 | +|---|---|---| | |
| 84 | +| `categoryId` | string | 分类 Id(`fl_label_category.Id`) | | |
| 85 | +| `categoryName` | string | 分类名称 | | |
| 86 | +| `count` | int | 该分类下标签数量 | | |
| 87 | +| `ratio` | decimal | 占比(百分比,保留 2 位) | | |
| 88 | + | |
| 89 | +--- | |
| 90 | + | |
| 91 | +## 4. 返回示例 | |
| 92 | + | |
| 93 | +```json | |
| 94 | +{ | |
| 95 | + "labelsPrintedToday": { | |
| 96 | + "key": "labelsPrintedToday", | |
| 97 | + "title": "Labels Printed Today", | |
| 98 | + "value": 342, | |
| 99 | + "previousValue": 305, | |
| 100 | + "changeValue": 37, | |
| 101 | + "changeRate": 12.13 | |
| 102 | + }, | |
| 103 | + "activeTemplates": { | |
| 104 | + "key": "activeTemplates", | |
| 105 | + "title": "Active Templates", | |
| 106 | + "value": 24, | |
| 107 | + "previousValue": 22, | |
| 108 | + "changeValue": 2, | |
| 109 | + "changeRate": 9.09 | |
| 110 | + }, | |
| 111 | + "activeUsers": { | |
| 112 | + "key": "activeUsers", | |
| 113 | + "title": "Active Users", | |
| 114 | + "value": 8, | |
| 115 | + "previousValue": 7, | |
| 116 | + "changeValue": 1, | |
| 117 | + "changeRate": 14.29 | |
| 118 | + }, | |
| 119 | + "locations": { | |
| 120 | + "key": "locations", | |
| 121 | + "title": "Locations", | |
| 122 | + "value": 12, | |
| 123 | + "previousValue": 11, | |
| 124 | + "changeValue": 1, | |
| 125 | + "changeRate": 9.09 | |
| 126 | + }, | |
| 127 | + "people": { | |
| 128 | + "key": "people", | |
| 129 | + "title": "People", | |
| 130 | + "value": 48, | |
| 131 | + "previousValue": 45, | |
| 132 | + "changeValue": 3, | |
| 133 | + "changeRate": 6.67 | |
| 134 | + }, | |
| 135 | + "products": { | |
| 136 | + "key": "products", | |
| 137 | + "title": "Products", | |
| 138 | + "value": 156, | |
| 139 | + "previousValue": 156, | |
| 140 | + "changeValue": 0, | |
| 141 | + "changeRate": 0 | |
| 142 | + }, | |
| 143 | + "weeklyPrintVolume": [ | |
| 144 | + { "date": "2026-04-16", "value": 142 }, | |
| 145 | + { "date": "2026-04-17", "value": 226 }, | |
| 146 | + { "date": "2026-04-18", "value": 185 }, | |
| 147 | + { "date": "2026-04-19", "value": 261 }, | |
| 148 | + { "date": "2026-04-20", "value": 192 }, | |
| 149 | + { "date": "2026-04-21", "value": 121 }, | |
| 150 | + { "date": "2026-04-22", "value": 342 } | |
| 151 | + ], | |
| 152 | + "byCategory": [ | |
| 153 | + { "categoryId": "CAT001", "categoryName": "Breakfast", "count": 420, "ratio": 42.00 }, | |
| 154 | + { "categoryId": "CAT002", "categoryName": "Lunch", "count": 350, "ratio": 35.00 }, | |
| 155 | + { "categoryId": "CAT003", "categoryName": "Dinner", "count": 230, "ratio": 23.00 } | |
| 156 | + ], | |
| 157 | + "byCategoryTotal": 1000, | |
| 158 | + "generatedAt": "2026-04-22T10:00:00+08:00", | |
| 159 | + "metricCards": [], | |
| 160 | + "categoryDistribution": [], | |
| 161 | + "categoryDistributionTotal": 1000 | |
| 162 | +} | |
| 163 | +``` | |
| 164 | + | |
| 165 | +--- | |
| 166 | + | |
| 167 | +## 5. 统计口径说明 | |
| 168 | + | |
| 169 | +### 5.1 Labels Printed Today | |
| 170 | +- 当前值:`fl_label_print_task` 在“今日 00:00~次日 00:00”的记录数。 | |
| 171 | +- 对比值:昨日同口径记录数。 | |
| 172 | + | |
| 173 | +### 5.2 Active Templates | |
| 174 | +- 当前值:`fl_label_template` 中 `IsDeleted = false AND State = true` 数量。 | |
| 175 | +- 对比值:同口径且 `CreationTime < 最近7天起始日` 的数量。 | |
| 176 | + | |
| 177 | +### 5.3 Active Users | |
| 178 | +- 当前值:`User` 表中 `IsDeleted = false AND State = true` 数量。 | |
| 179 | +- 对比值:同口径且 `CreationTime < 最近7天起始日` 的数量。 | |
| 180 | + | |
| 181 | +### 5.4 Locations | |
| 182 | +- 当前值:`location` 表中 `IsDeleted = false` 数量。 | |
| 183 | +- 对比值:同口径且 `CreationTime < 最近7天起始日` 的数量。 | |
| 184 | + | |
| 185 | +### 5.5 People | |
| 186 | +- 当前值:`User` 表中 `IsDeleted = false` 数量。 | |
| 187 | +- 对比值:同口径且 `CreationTime < 最近7天起始日` 的数量。 | |
| 188 | + | |
| 189 | +### 5.6 Products | |
| 190 | +- 当前值:`fl_product` 表中 `IsDeleted = false` 数量。 | |
| 191 | +- 对比值:当前版本由于 `FlProductDbEntity` 未映射 `CreationTime`,临时按同口径总量返回(即变化可能为 0)。 | |
| 192 | + | |
| 193 | +### 5.7 Weekly Print Volume | |
| 194 | +- 统计最近 7 天(含今天)每天 `fl_label_print_task` 数量。 | |
| 195 | +- 无数据日期补 0。 | |
| 196 | + | |
| 197 | +### 5.8 By Category | |
| 198 | +- 基于启用且未删除的 `fl_label_category` 作为分类集合。 | |
| 199 | +- 统计 `fl_label` 中未删除且 `LabelCategoryId` 命中的数量。 | |
| 200 | +- 占比按 `count / byCategoryTotal * 100` 计算,保留 2 位。 | |
| 201 | + | |
| 202 | +--- | |
| 203 | + | |
| 204 | +## 6. 前端接入建议 | |
| 205 | + | |
| 206 | +- 新页面优先使用: | |
| 207 | + - 指标:`labelsPrintedToday`、`activeTemplates`、`activeUsers`、`locations`、`people`、`products` | |
| 208 | + - 图表:`weeklyPrintVolume` | |
| 209 | + - 环图:`byCategory` + `byCategoryTotal` | |
| 210 | +- 旧逻辑仍可使用兼容字段: | |
| 211 | + - `metricCards` | |
| 212 | + - `categoryDistribution` | |
| 213 | + - `categoryDistributionTotal` | |
| 214 | + | ... | ... |