6-2 代码优化
本文档说明 2026-06-02 对美国版 /api/app/label 列表接口报错修复,以及 适用 Region 字段的接口约定。
标签列表接口报错修复
现象
GET http://flus-test.3ffoodsafety.com/api/app/label?SkipCount=1&MaxResultCount=10 返回 500,典型错误为 MySQL:
Unknown column 'AppliedRegionType' in 'field list'- 或
Table 'xxx.fl_label_region' doesn't exist
根因
后端已上线「适用 Region」逻辑,但测试库 尚未执行 DDL:
fl_label缺少列AppliedRegionType- 缺少表
fl_label_region
ORM 在列表/筛选 SQL 中引用上述结构即失败。
修复方式(代码 + 库)
| 层级 | 处理 |
|---|---|
| 代码 | 新增 LabelRegionSchemaHelper:启动后首次访问探测 information_schema,无列/无表时走 兼容路径(仅按 LocationId、门店反推 Region 展示),列表不再因缺列 500 |
| 实体 | FlLabelDbEntity.AppliedRegionType 标记 IsIgnore,读写经 Helper 的原始 SQL(列存在时) |
| 数据库 | 须在环境执行 DDL,才能持久化 ALL/多选 Region(见下节脚本) |
DDL 脚本(可重复执行):
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/scripts/fl_label_region_scope.sql
验证步骤
- 未执行 DDL:部署含本修复的后端 →
GET /api/app/label?SkipCount=1&MaxResultCount=10应 200,region可由门店反推或显示「无」。 - 执行 DDL 后:重启应用(或等待 schema 缓存失效;生产建议重启)→ 新增/编辑可落库
regionIds、AppliedRegionType=ALL。 - 库内确认:
SELECT COLUMN_NAME FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fl_label' AND COLUMN_NAME = 'AppliedRegionType';
SELECT TABLE_NAME FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'fl_label_region';
涉及文件(本次修复)
| 文件 | 说明 |
|---|---|
Helpers/LabelRegionSchemaHelper.cs |
探测 schema、批量读 AppliedRegionType、按列更新 |
Helpers/LabelRegionScopeHelper.cs |
列表筛选/App 校验按 schema 分支 |
Services/LabelAppService.cs |
列表不再 SELECT 缺列;创建/更新后 SetAppliedRegionTypeAsync |
Services/UsAppLabelingAppService.cs |
labeling-tree 按 schema 拼接条件 |
Services/DbModels/FlLabelDbEntity.cs |
AppliedRegionType ORM 忽略 |
scripts/fl_label_region_scope.sql |
幂等 DDL |
标签 /api/app/label 适用 Region(接口说明)
背景
标签除 locationId 外,支持显式配置适用 Region(fl_group.Id),模式与 label-type / label-multiple-option 一致:
- ALL:全选 Region
- SPECIFIED:
regionIds/groupIds单选或多选
存储
| 位置 | 字段 | 说明 |
|---|---|---|
fl_label |
AppliedRegionType |
ALL / SPECIFIED(需 DDL) |
fl_label_region |
LabelId + GroupId |
Region 多选明细 |
fl_label |
LocationId |
兼容 App;多 Region 未指定门店时取合并门店第一家 |
接口一览
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/api/app/label |
分页列表;GroupId 按 Region 筛选 |
GET |
/api/app/label/{id} |
详情(id = LabelCode) |
POST |
/api/app/label |
新增 |
PUT |
/api/app/label/{id} |
编辑 |
DELETE |
/api/app/label/{id} |
逻辑删除 |
测试环境列表示例:
http://flus-test.3ffoodsafety.com/api/app/label?SkipCount=1&MaxResultCount=10
鉴权:Authorization: {登录返回的 data.token}(已含 Bearer 前缀)
列表请求参数
| 参数 | 类型 | 说明 |
|---|---|---|
SkipCount |
int | 跳过条数(分页) |
MaxResultCount |
int | 每页条数 |
Keyword |
string | 模糊:标签名、类别、类型、模板、产品名 |
GroupId |
string | Region(fl_group.Id)筛选 |
LocationId |
string | 门店筛选 |
ProductId |
string | 产品筛选 |
LabelCategoryId |
string | 标签类别 |
LabelTypeId |
string | 标签类型 |
TemplateCode |
string | 模板编码 |
State |
bool | 启用状态 |
Sorting |
string | 排序(可选) |
请求示例:
GET /api/app/label?SkipCount=1&MaxResultCount=10 HTTP/1.1
Host: flus-test.3ffoodsafety.com
Authorization: Bearer {access_token}
列表响应(主要字段)
| 字段 | 说明 |
|---|---|
totalCount |
总条数 |
items |
行数组 |
items[].id |
标签 Code(LabelCode) |
items[].labelName |
名称 |
items[].region |
Region 展示名;ALL 为 All Regions |
items[].regionIds / groupIds |
Region Id 数组 |
items[].appliedRegionType |
ALL / SPECIFIED |
items[].locationName |
门店名 |
items[].products |
关联产品名(逗号拼接) |
items[].lastEdited |
最后编辑时间 |
新增 / 编辑 Body(JSON)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
appliedRegionType |
string | 否 | ALL / SPECIFIED;有 regionIds 时按 SPECIFIED |
regionIds |
string[] | 条件 | fl_group.Id,与 groupIds 合并去重 |
groupIds |
string[] | 否 | 同 regionIds |
locationId |
string | 条件 | 门店;须在 Region 内(SPECIFIED) |
labelName |
string | 是 | 标签名称 |
templateCode |
string | 是 | 模板编码 |
labelCategoryId |
string | 是 | 标签类别 Id |
labelTypeId |
string | 是 | 标签类型 Id |
productIds |
string[] | 是 | 至少 1 个 |
labelInfoJson |
object | 否 | 模板数据 |
state |
bool | 否 | 默认 true |
全选 Region:
{
"labelName": "Demo",
"templateCode": "TPL001",
"appliedRegionType": "ALL",
"regionIds": [],
"labelCategoryId": "...",
"labelTypeId": "...",
"productIds": ["..."]
}
单选 Region:
{
"appliedRegionType": "SPECIFIED",
"regionIds": ["{fl_group.Id}"],
"locationId": "{location.Id}",
"labelName": "...",
"templateCode": "...",
"labelCategoryId": "...",
"labelTypeId": "...",
"productIds": ["..."]
}
联调注意
| 现象 | 处理 |
|---|---|
| 列表仍 500 | 确认已部署含 LabelRegionSchemaHelper 的版本 |
regionIds 保存不上 |
执行 fl_label_region_scope.sql 并重启 |
| ALL 不生效 | 需 AppliedRegionType 列;执行 DDL |
未执行 DDL 时新增带 regionIds |
关联表不存在则仅更新 locationId,Region 关联不落库 |
与 6-1 文档关系
6-1代码优化.md:首版 Region 功能说明(含 my-menus、team-member 等其它优化)。- 本节(6-2):列表 500 根因与兼容修复 + 接口速查;以 6-2 为准处理 label 列表报错与 DDL 要求。
Reports Print Log 日期格式统一(print-log-list)
背景
GET /api/app/reports/print-log-list 返回的 printedAt 原为 DateTime JSON(ISO / 客户端 en-US 再格式化),与 expiryDateText(来自 PrintInputJson 原始模板文案,如 MM/dd/yyyy)展示顺序不一致。要求两列统一为 年月日在前,且 Expiration(expiryDateText)精确到分钟。
目标格式
| 字段 | 格式 | 示例 |
|---|---|---|
printedAt |
yyyy-MM-dd HH:mm |
2026-05-19 14:30 |
expiryDateText |
yyyy-MM-dd HH:mm(可解析时) |
2026-06-02 18:00 |
| 无数据 | 固定文案 | 无 |
仅日期、无时分的数据解析后时间为
00:00。
接口
| 项 | 说明 |
|---|---|
| 方法 | GET |
| 路径 | /api/app/reports/print-log-list |
| 鉴权 | Bearer Token |
| 测试示例 | http://flus-test.3ffoodsafety.com/api/app/reports/print-log-list?Sorting=PrintedAt+desc&StartDate=2026-05-04&EndDate=2026-06-02&SkipCount=1&MaxResultCount=10 |
请求参数(常用)
| 参数 | 说明 |
|---|---|
SkipCount / MaxResultCount |
分页 |
Sorting |
如 PrintedAt desc / PrintedAt asc |
StartDate / EndDate |
区间(yyyy-MM-dd,含起止日) |
PartnerId / GroupId / LocationId |
组织筛选 |
Keyword |
产品名等模糊 |
响应 items[](与本次相关)
| 字段 | 类型 | 说明 |
|---|---|---|
printedAt |
string | 打印时间,yyyy-MM-dd HH:mm |
expiryDateText |
string | 保质期(Expiration),同上;无法解析时保留原文或 无 |
taskId |
string | 任务 Id(重打) |
labelCode |
string | 门店当日序号 Label ID |
productName |
string | 产品名 |
templateText |
string | 模板展示 |
printedByName |
string | 打印人 |
locationText |
string | 门店 |
响应片段示例:
{
"totalCount": 42,
"items": [
{
"taskId": "…",
"labelCode": "20260519-1",
"printedAt": "2026-05-19 14:30",
"expiryDateText": "2026-06-02 18:00",
"productName": "Chicken Breast",
"printedByName": "John",
"locationText": "Store A (001)"
}
]
}
实现说明
| 组件 | 说明 |
|---|---|
ReportsDateTimeDisplayHelper |
FormatPrintedAt / FormatExpiryDisplay,统一 yyyy-MM-dd HH:mm |
ReportsPrintLogExpiryHelper.ExtractFormattedExpiryText |
从 PrintInputJson 解析并格式化 Expiration |
ReportsPrintLogListItemDto.PrintedAt |
由 DateTime 改为 string 出参 |
| PDF / Excel 导出 | 与列表相同格式 |
Web ReportsView.tsx |
直接展示接口字符串,不再 en-US 二次格式化 |
App get-print-log-list 的 Label ID(labelId)
背景
POST /api/app/us-app-labeling/get-print-log-list 原先 labelId 返回 fl_label.Id(或界面误用 labelCode / batchId),与业务要求的 门店当日打印序号 不一致。管理端 Print Log 已使用 ReportsPrintLogDailyLabelIdHelper(yyyyMMdd-n),App 需对齐。
规则
| 项 | 说明 |
|---|---|
| 范围 | 同一 门店 + 同一 自然日(按 PrintedAt,无则 CreationTime) |
| 排序 | 当日任务按打印时间升序,再按任务 Id 升序 |
| 格式 | {yyyyMMdd}-{n},如 20260513-1、20260513-2 |
| 非 | 不是 fl_label.LabelCode,也不是标签主键本身 |
接口
| 项 | 说明 |
|---|---|
| 方法 | POST |
| 路径 | /api/app/us-app-labeling/get-print-log-list |
| 鉴权 | App 登录 Bearer Token |
请求 Body 示例:
{
"locationId": "{门店Guid}",
"skipCount": 1,
"maxResultCount": 10
}
响应 items[](与 Label ID 相关)
| 字段 | 说明 |
|---|---|
labelId |
门店当日打印序号(yyyyMMdd-n),列表 Ref / Label ID 展示用 |
labelEntityId |
fl_label.Id(内部、重打关联) |
labelCode |
fl_label.LabelCode(重打/预览入参,勿与 labelId 混用) |
taskId |
fl_label_print_task.Id(重打主键) |
响应片段:
{
"totalCount": 3,
"items": [
{
"taskId": "…",
"labelId": "20260513-1",
"labelEntityId": "…",
"labelCode": "LBL_xxx",
"productName": "Chicken",
"printedAt": "2026-05-13T10:30:00"
},
{
"taskId": "…",
"labelId": "20260513-2",
"labelEntityId": "…",
"labelCode": "LBL_yyy"
}
]
}
验证步骤
- 同一门店当日连续打印多条 →
labelId为yyyyMMdd-1、yyyyMMdd-2… 递增。 - 跨日:次日首条从
yyyyMMdd-1重新计数(日期段为当日yyyyMMdd)。 - 跨门店:各门店各自从
-1计数(请求已限定locationId)。 - 重打仍用
taskId+labelCode,不依赖labelId序号。
涉及文件
| 文件 | 说明 |
|---|---|
Helpers/ReportsPrintLogDailyLabelIdHelper.cs |
序号计算(与 Web Print Log 共用) |
Services/UsAppLabelingAppService.cs |
GetPrintLogListAsync |
Contracts/Dtos/UsAppLabeling/PrintLogItemDto.cs |
labelId / labelEntityId / labelCode 注释 |
App print-log.vue |
Ref 列展示 labelId |
App types/usAppLabeling.ts |
类型补充 |
验证步骤
- 调用上述
print-log-list,检查printedAt、expiryDateText均为yyyy-MM-dd HH:mm或无。 - 对比改前:不应再出现
05/19/2026与2026-05-19T…混用。 - 导出 Excel/PDF 的 Printed at、Expiration 列与列表一致。
涉及文件
| 文件 | 说明 |
|---|---|
Helpers/ReportsDateTimeDisplayHelper.cs |
统一格式化 |
Helpers/ReportsPrintLogExpiryHelper.cs |
ExtractFormattedExpiryText |
Services/ReportsAppService.cs |
列表 / 导出映射 |
Contracts/Dtos/Reports/ReportsPrintLogListItemDto.cs |
printedAt 类型与注释 |
Web ReportsView.tsx |
列表展示 |
标签模板列表 Items 列(控件名称)
说明
标签模板列表需展示 Items 列:该模板画布上每个控件的名称,逗号拼接(如图:Barcode, Print Time, Print Date, …)。
接口路径:
GET /api/app/label-template(标签模板分页列表)。
若写成/api/app/location则为门店列表,不含模板 Items;请以本节路径为准。
接口
| 项 | 说明 |
|---|---|
| 方法 | GET |
| 路径 | /api/app/label-template |
| 鉴权 | Web 登录 Bearer Token |
| 示例 | http://flus-test.3ffoodsafety.com/api/app/label-template?SkipCount=1&MaxResultCount=500 |
请求参数(常用)
| 参数 | 说明 |
|---|---|
SkipCount / MaxResultCount |
分页 |
Keyword |
模板名/编码模糊 |
GroupId / LocationId |
Region / 门店范围筛选 |
LabelType |
标签类型 |
State |
启用状态 |
Sorting |
排序(可选) |
响应 items[](本次新增)
| 字段 | 类型 | 说明 |
|---|---|---|
items |
string | 控件名称逗号拼接(按 OrderNum 排序),无控件时为 无 |
itemNames |
string[] | 与 items 相同内容的数组 |
contentsCount |
int | 控件数量(Contents 列) |
templateName / templateCode |
string | 模板名 / 编码 |
locationText / region |
string | 适用范围展示 |
展示名解析顺序:
fl_label_template_element.ElementName(非空优先)TypeAdd中下划线后缀(如print_Print Time→Print Time)ElementType映射(如BARCODE→Barcode)
响应片段:
{
"totalCount": 12,
"items": [
{
"id": "TPL001",
"templateName": "G'n'G 2x6",
"contentsCount": 8,
"items": "Barcode, Print Time, Print Date, Text Field, Duration Date, Label Name, Employee",
"itemNames": ["Barcode", "Print Time", "Print Date", "Text Field", "Duration Date", "Label Name", "Employee"],
"sizeText": "2x6inch"
}
]
}
验证步骤
- 打开 Web Label Templates 列表,Items 列与接口
items一致。 - 编辑模板增删控件后刷新列表,
items/contentsCount同步更新。 - 无元素的模板:
items为无,contentsCount为 0。
涉及文件
| 文件 | 说明 |
|---|---|
Helpers/LabelTemplateListItemsHelper.cs |
批量汇总控件名 |
Services/LabelTemplateAppService.cs |
GetListAsync 出参 |
Contracts/Dtos/LabelTemplate/LabelTemplateGetListOutputDto.cs |
items / itemNames |
Web labelTemplateService.ts / LabelTemplatesView.tsx |
列表 Items 列展示 |