# 6-17 代码优化 本文档说明 **2026-06-17** 对美国版的两项后端改造: 1. **`GET /api/app/label-template`** 列表 500 修复(scope 库结构兼容) 2. **`POST /api/app/us-app-labeling/get-print-log-list`** 增加 **按自然日** 的打印时间筛选 测试环境:`http://flus-test.3ffoodsafety.com` --- ## 一、label-template 列表修复 Web 管理端 **Label Templates** 页加载失败,Network 中: `GET /api/app/label-template?SkipCount=1&MaxResultCount=10` 返回错误,页面提示 **Failed to load label templates**。 ### 根因 6-4 改造引入 **Company / Region / Location** 三维适用范围后,代码会: 1. 查询 `fl_label_template.AppliedPartnerType` / `AppliedRegionType` 2. 关联 `fl_label_template_partner` / `fl_label_template_region` 测试库若**尚未执行**迁移脚本 `fl_label_template_scope.sql`,上述列/表不存在,SqlSugar 生成 SQL 时报 **Unknown column / Table doesn't exist**,接口 500。 > `SkipCount=1` 表示**第 1 页**(页码从 1 起),不是报错原因。 --- ### 修复说明 #### 1. 库结构兼容(未迁移仍可列表) | 改动 | 说明 | |------|------| | `LabelTemplateScopeSchemaHelper` | 启动后首次请求检测 partner/region 关联表是否存在 | | `FlLabelTemplateDbEntity` | `AppliedPartnerType` / `AppliedRegionType` 标记 `IsIgnore`,避免 SELECT 不存在列 | | `LabelTemplateScopeHelper` | 未迁移时列表/详情 **仅 Location 维度** 过滤与展示;Company/Region 显示 `All Companies` / `All Regions` | | 已迁移库 | 行为与 6-4 一致,按关联表判断 ALL/SPECIFIED | #### 2. 列表筛选补全 `LabelTemplateGetListInputVo` 增加 **`partnerId`**(Company),与 `groupId`、`locationId` 按「门店优先」解析(`ResolveFilteredLocationIdsForListAsync`)。 #### 3. Items 列 `LabelTemplateListItemsHelper` 补全 `ElementKey` 查询字段,控件名称排序稳定。 --- ### 接口说明(label-template) | 项目 | 内容 | |------|------| | 方法 | `GET` | | 路径 | `/api/app/label-template` | | 鉴权 | Bearer Token | ### 入参(Query) | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `SkipCount` | int | 否 | **页码,从 1 起**;第一页传 `1`(与全平台列表约定一致) | | `MaxResultCount` | int | 否 | 每页条数,默认 10 | | `Keyword` | string | 否 | 模板名称 / 编码模糊搜索 | | `PartnerId` | string | 否 | 按 Company(`fl_partner.Id`)筛选 | | `GroupId` | string | 否 | 按 Region(`fl_group.Id`)筛选 | | `LocationId` | string | 否 | 按门店(`location.Id`)筛选;**优先于** Partner/Region | | `LabelType` | string | 否 | 如 `PRICE` / `NUTRITION` | | `State` | bool | 否 | 启用状态 | | `Sorting` | string | 否 | 排序字段 | 筛选解析顺序:**LocationId → GroupId → PartnerId**;均未传则不按门店范围收窄(仍受登录账号数据权限约束)。 ### 出参(`PagedResultWithPageDto`) | 字段 | 说明 | |------|------| | `pageIndex` | 当前页码 | | `pageSize` | 每页条数 | | `totalCount` | 总条数 | | `totalPages` | 总页数 | | `items[]` | 模板列表 | **`items[]` 主要字段** | 字段 | 说明 | |------|------| | `id` / `templateCode` | 模板编码(前端主键) | | `templateName` | 模板名称 | | `company` / `region` / `location` | 三维适用范围展示文案 | | `partnerIds` / `regionIds` / `locationIds` | 对应 Id 数组(ALL 时为空) | | `items` / `itemNames` | 模板内控件名称(逗号拼接 / 数组) | | `contentsCount` | 控件数量 | | `sizeText` | 如 `4x2inch` | | `lastEdited` | 最近编辑时间 | --- ### 请求示例(label-template) ```bash curl -G "http://flus-test.3ffoodsafety.com/api/app/label-template" \ -H "Authorization: Bearer {token}" \ --data-urlencode "SkipCount=1" \ --data-urlencode "MaxResultCount=10" ``` 带筛选: ```bash curl -G "http://flus-test.3ffoodsafety.com/api/app/label-template" \ -H "Authorization: Bearer {token}" \ --data-urlencode "SkipCount=1" \ --data-urlencode "MaxResultCount=10" \ --data-urlencode "PartnerId=1234567890123456789" \ --data-urlencode "Keyword=Price" ``` ### 响应片段(示例) ```json { "pageIndex": 1, "pageSize": 10, "totalCount": 2, "totalPages": 1, "items": [ { "id": "TPL-PRICE-01", "templateCode": "TPL-PRICE-01", "templateName": "Standard Price Label", "company": "All Companies", "region": "All Regions", "location": "All Locations", "items": "Label Name, Price, Barcode", "itemNames": ["Label Name", "Price", "Barcode"], "contentsCount": 3, "sizeText": "2x2inch", "lastEdited": "2026-06-04T10:00:00" } ] } ``` --- ## 数据库迁移(启用完整三维 scope) 未执行前:列表可正常返回,但 **无法** 保存 Company/Region 多选明细。 **脚本(可重复执行)**: `美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/scripts/fl_label_template_scope.sql` | 对象 | 说明 | |------|------| | `fl_label_template.AppliedPartnerType` | Company:ALL / SPECIFIED | | `fl_label_template.AppliedRegionType` | Region:ALL / SPECIFIED | | `fl_label_template_partner` | Company 多选明细 | | `fl_label_template_region` | Region 多选明细 | 执行后重启应用,新建/编辑模板即可写入 Company/Region 范围。 ### SQL 检查是否已迁移 ```sql SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fl_label_template' AND COLUMN_NAME IN ('AppliedPartnerType', 'AppliedRegionType'); SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME IN ('fl_label_template_partner', 'fl_label_template_region'); ``` --- ### 涉及代码(label-template) | 文件 | 说明 | |------|------| | `Helpers/LabelTemplateScopeSchemaHelper.cs` | 检测 scope 表是否已迁移 | | `Helpers/LabelTemplateScopeHelper.cs` | 兼容未迁移库的过滤/展示/保存 | | `Helpers/LabelTemplateListItemsHelper.cs` | Items 列 ElementKey | | `Services/DbModels/FlLabelTemplateDbEntity.cs` | Partner/Region 列 IsIgnore | | `Services/LabelTemplateAppService.cs` | 列表筛选支持 `partnerId` | | `Dtos/LabelTemplate/LabelTemplateGetListInputVo.cs` | 新增 `PartnerId` | | `IServices/ILabelTemplateAppService.cs` | 接口注释 | ### 验证步骤(label-template) 1. **未迁移库**:仅执行后端部署,调用 `GET /api/app/label-template?SkipCount=1&MaxResultCount=10` 应 **200**,Web 列表可加载。 2. **已迁移库**:列表 `company`/`region` 展示与 6-4 一致;指定 Company 保存后再查列表 Id 数组正确。 3. 分页:第 1 页 `SkipCount=1`,第 2 页 `SkipCount=2`,`totalCount` 与 UI 分页一致。 4. 筛选:传 `LocationId` 后仅返回该门店可见模板 + 全范围模板。 --- ## 二、App Print Log 按日筛选(get-print-log-list) ### 背景 App **Print Log** 页增加 **Date** 选择器(及 **Today** 快捷按钮),需按所选自然日只展示该门店当日的打印记录;无记录时提示如 `No print records for 06/16/2026`。 改造前接口仅按门店 + 权限分页,**无日期条件**,无法与 UI 日期筛选对齐。 ### 接口说明 | 项目 | 内容 | |------|------| | 方法 | `POST` | | 路径 | `/api/app/us-app-labeling/get-print-log-list` | | 鉴权 | App Bearer Token | ### 入参(Body:`PrintLogGetListInputVo`) | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | `locationId` | string | 是 | 当前门店 Id(`location.Id`) | | **`printDate`** | DateTime | 否 | **打印日期(自然日)**;按 `PrintedAt ?? CreationTime` 落在该日的任务筛选 | | `skipCount` | int | 否 | 页码,从 **1** 起 | | `maxResultCount` | int | 否 | 每页条数 | #### `printDate` 规则 1. **未传** → 默认 **服务器当天**(与 App 默认选中 Today 一致)。 2. **传入任意时刻** → 仅取 **日期部分**(`Date`),区间为 `[当天 00:00:00, 次日 00:00:00)`。 3. 有效时间字段:`fl_label_print_task.PrintedAt` 优先,为空则用 `CreationTime`(与 Label ID 序号、`6-2` / `6-11` 一致)。 4. 权限不变:管理员 / Partner 看门店全部;其它角色仅本人(`CreatedBy`)。 ### 出参 仍为 `PagedResultWithPageDto`;`items[]` 字段不变。`totalCount` 为**该日**符合条件的总数。 | 字段 | 说明 | |------|------| | `labelId` | 门店当日打印序号 `yyyyMMdd-n`(与 preview / 报表一致) | | `printedAt` | 打印时间 | | 其它 | 与改造前一致 | ### 请求示例 **查询当天(显式传 printDate)** ```bash curl -X POST "http://flus-test.3ffoodsafety.com/api/app/us-app-labeling/get-print-log-list" \ -H "Authorization: Bearer {token}" \ -H "Content-Type: application/json" \ -d '{ "locationId": "550e8400-e29b-41d4-a716-446655440000", "printDate": "2026-06-16T00:00:00", "skipCount": 1, "maxResultCount": 20 }' ``` **Today 按钮(不传 printDate,后端默认当天)** ```json { "locationId": "550e8400-e29b-41d4-a716-446655440000", "skipCount": 1, "maxResultCount": 20 } ``` ### 响应片段(示例) ```json { "pageIndex": 1, "pageSize": 20, "totalCount": 2, "totalPages": 1, "items": [ { "taskId": "1234567890123456789", "labelId": "20260616-1", "productName": "Organic Milk", "printedAt": "2026-06-16T09:15:00", "operatorName": "Alice", "locationName": "Ordos Airport" }, { "taskId": "1234567890123456788", "labelId": "20260616-2", "printedAt": "2026-06-16T14:30:00" } ] } ``` 若 `totalCount = 0`,App 可展示 `No print records for MM/DD/YYYY`。 ### 涉及代码(get-print-log-list) | 文件 | 说明 | |------|------| | `Dtos/UsAppLabeling/PrintLogGetListInputVo.cs` | 新增 `PrintDate` | | `Services/UsAppLabelingAppService.cs` | `GetPrintLogListAsync` 增加日期 Where | | `Helpers/ReportsPrintLogDailyLabelIdHelper.cs` | 新增 `ResolvePrintLogFilterDayRange` | | `IServices/IUsAppLabelingAppService.cs` | 接口注释 | ### 验证步骤(get-print-log-list) 1. 门店在 `2026-06-16` 有 N 条打印任务,传 `printDate=2026-06-16` → `totalCount=N`,且每条 `printedAt` 落在该日。 2. 传 `printDate=2026-06-15` 且无记录 → `totalCount=0`,`items=[]`。 3. 不传 `printDate` → 等价于查询**服务器当天**。 4. 同一 `printDate` 下 `labelId` 序号与 `6-2` 规则一致(`-1`、`-2` … 按当日时间升序)。 #### SQL 抽查 ```sql SELECT Id, LocationId, PrintedAt, CreationTime FROM fl_label_print_task WHERE LocationId = '{locationId}' AND IFNULL(PrintedAt, CreationTime) >= '2026-06-16' AND IFNULL(PrintedAt, CreationTime) < '2026-06-17' ORDER BY IFNULL(PrintedAt, CreationTime), Id; ``` 行数应与接口 `totalCount`(该日、该门店、权限范围内)一致。 --- ## 关联文档 - 三维 scope 业务规则:`项目相关文档/6-4代码优化.md` - App Print Log Label ID:`项目相关文档/6-2代码优化.md` - Preview 当日序号:`项目相关文档/6-11代码优化.md` - 分页约定(SkipCount = 页码):`Helpers/PagedQueryConvention.cs`