6-18代码优化.md 16.6 KB

6-18 代码优化

本文档说明 2026-06-18GET /api/app/label-template 列表接口的第二轮修复。

6-17 已完成 scope 库结构兼容(未迁移 fl_label_template_partner / fl_label_template_region 时仍可查询),但测试环境 Web Label Templates 页仍返回 500,Network 中:

GET /api/app/label-template?SkipCount=1&MaxResultCount=10

测试环境:http://flus-test.3ffoodsafety.com


一、现象

  • 页面提示:Failed to load label templates. Request failed.
  • 接口 HTTP 500,响应体 errors 为空,无具体异常文案。
  • 同页 partner / group / location 下拉接口可正常加载。

SkipCount=1 表示第 1 页(页码从 1 起),不是报错原因。详见 Helpers/PagedQueryConvention.cs


二、根因

服务端日志已明确报错:

Unknown column 'AppliedPartnerType' in 'field list'

对应 SQL:

SELECT ... `AppliedLocationType`,`AppliedPartnerType`,`AppliedRegionType`, ... 
FROM `fl_label_template`
WHERE NOT ( `IsDeleted`=1 )
ORDER BY IFNULL(`LastModificationTime`,`CreationTime`) DESC
LIMIT 0,10

2.1 主因:ORM 实体仍映射不存在列

6-17 曾在 FlLabelTemplateDbEntity 上对 AppliedPartnerType / AppliedRegionType 标记 [SugarColumn(IsIgnore = true)],但当前 SqlSugar 运行时仍会把这两列拼进 SELECT(与 fl_label.AppliedRegionTypeIsIgnore 行为不一致),导致未执行 fl_label_template_scope.sql 的库直接 500。

MCP 查库(测试库)

  • fl_label_template AppliedPartnerType / AppliedRegionType
  • fl_label_template_partner / fl_label_template_region
  • AppliedLocationTypefl_label_template_location

2.2 次因:列表 SQL 其它隐患(一并修复)

# 问题 后果
1 默认排序曾用 LastModificationTime ?? CreationTime SqlSugar 翻译失败 → 500
2 Sorting 直接 OrderBy(input.Sorting) 拼 SQL 非法字段 / 注入风险
3 templateIdsContains 查询 可能生成 IN ()
4 无效 partner/group 筛选未安全降级 未捕获异常

三、修复说明

1. 从 ORM 实体移除不存在列 + 强制列投影(核心)

改动 说明
FlLabelTemplateDbEntity 删除 AppliedPartnerType / AppliedRegionType 属性
LabelTemplateQueryHelper.ProjectListColumns 列表/详情/重复校验等只读查询 显式 Select 真实列,SQL 不再出现 scope 列
LabelTemplateScopeSchemaHelper 探测列/表;已迁移时用 raw SQL 写入 scope 列
LabelTemplateAppService GetListAsync 在排序后调用 ProjectListColumns

重要:若 Yi-SQL 日志仍出现 AppliedPartnerType,说明进程加载的是旧 DLLFoodLabeling.Application 未重新编译或未重启)。请先 dotnet build 通过后再完全停止并重启 Yi.Abp.Web

2. 安全排序

新增 ApplyLabelTemplateListSorting

  • 默认:ORDER BY IFNULL(LastModificationTime, CreationTime) DESC, TemplateCode ASC
  • Sorting 白名单字段 + asc/desc

3. 列表查询健壮性

改动 说明
input ??= new()pageSize 默认 10 入参/分页兜底
templateIds.Count > 0 再查 elements/items 避免空 IN ()
ResolveFilteredLocationIdsForListAsync 无效 partner/group 返回空列表
DeleteAsync 仅当 scope 关联表存在时才删除 partner/region 行

4. 与 6-17 的关系

6-17 用 IsIgnore 试图跳过列映射,实测无效;6-18 改为实体不含列 + raw SQL 按需写入,与 fl_label.AppliedRegionType 处理方式一致。


四、接口说明

项目 内容
方法 GET
路径 /api/app/label-template
鉴权 Bearer Token(Authorization: {data.token}data.token 已含 Bearer 前缀)

入参(Query)

参数 类型 必填 说明
SkipCount int 页码,从 1 起;第一页传 1
MaxResultCount int 每页条数;<=0 时后端按 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 白名单:TemplateName asc/descTemplateCode asc/descCreationTime asc/descLastModificationTime asc/desc;其它值忽略并走默认排序

筛选解析顺序:LocationId → GroupId → PartnerId;均未传则不按门店范围收窄(仍返回全部未删除模板,除非前端传了无效 Id 则返回空列表)。

出参(PagedResultWithPageDto<LabelTemplateGetListOutputDto>

字段 说明
pageIndex 当前页码
pageSize 每页条数
totalCount 总条数
totalPages 总页数
items[] 模板列表

items[] 主要字段

字段 说明
id / templateCode 模板编码(前端主键)
templateName 模板名称
company / region / location 适用范围展示;未迁移 scope 库时 Company/Region 为 All Companies / All Regions
partnerIds / regionIds / locationIds 对应 Id 数组
items / itemNames 模板内控件名称
contentsCount 控件数量
sizeText 2x2inch
lastEdited 最近编辑时间(LastModificationTime ?? CreationTime

五、请求示例

登录获取 Token

curl -X POST "http://flus-test.3ffoodsafety.com/api/oauth/Login" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "userName=admin&password=123456"

列表(第一页,默认排序)

curl -G "http://flus-test.3ffoodsafety.com/api/app/label-template" \
  -H "Authorization: Bearer {token}" \
  --data-urlencode "SkipCount=1" \
  --data-urlencode "MaxResultCount=10"

带 Company 筛选

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={fl_partner.Id}"

指定排序

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 "Sorting=TemplateName asc"

响应片段(示例)

{
  "pageIndex": 1,
  "pageSize": 10,
  "totalCount": 3,
  "totalPages": 1,
  "items": [
    {
      "id": "tpl_n0b5h9_mpyssitm",
      "templateCode": "tpl_n0b5h9_mpyssitm",
      "templateName": "Retail Label w/Price Copy",
      "company": "All Companies",
      "region": "All Regions",
      "location": "Ordos Airport, Store B",
      "items": "Label Name, Price, Barcode",
      "contentsCount": 5,
      "sizeText": "2x2inch",
      "lastEdited": "2026-06-04T08:30:00"
    }
  ]
}

六、验证步骤

  1. 重新编译并重启(必做):

    cd "美国版/Food Labeling Management Code/Yi.Abp.Net8/src/Yi.Abp.Web"
    dotnet build
    

    停止正在运行的 Web 进程后重新启动;确认 Yi-SQL 中 不再出现 AppliedPartnerType

  2. 部署包含 6-17 + 6-18 的后端并重启。

  3. 调用 GET /api/app/label-template?SkipCount=1&MaxResultCount=10 → 应 200totalCount >= 0

  4. Web Label Templates 列表可加载,不再出现红色 Failed to load label templates

  5. 传无效 PartnerId / GroupId200items=[](非 500)。

  6. 不传 Sorting → 按最近编辑时间降序;传 Sorting=TemplateName asc → 按名称升序。

  7. 分页:第 2 页 SkipCount=2totalCount 与 UI 一致。

SQL 抽查(修复后 ORM 应生成的列)

SELECT Id, TemplateCode, TemplateName, AppliedLocationType,
       IFNULL(LastModificationTime, CreationTime) AS LastEdited
FROM fl_label_template
WHERE IsDeleted = 0
ORDER BY IFNULL(LastModificationTime, CreationTime) DESC, TemplateCode ASC
LIMIT 10;

不应再出现 AppliedPartnerType / AppliedRegionType

检查 scope 是否已迁移

SELECT COUNT(*) AS scope_table_cnt
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
  AND TABLE_NAME IN ('fl_label_template_partner', 'fl_label_template_region');

scope_table_cnt = 0 时行为与 6-17 一致:仅 Location 维度落库与展示。


七、涉及代码

文件 说明
Helpers/LabelTemplateQueryHelper.cs 新增 ProjectListColumns 强制 SQL 列白名单
Services/DbModels/FlLabelTemplateDbEntity.cs 移除 Partner/Region 列属性
Helpers/LabelTemplateScopeSchemaHelper.cs 列/表探测 + SetAppliedScopeTypesAsync raw SQL
Services/LabelTemplateAppService.cs 安全排序、Create/Update 写 scope 列、列表健壮性
Helpers/LabelTemplateScopeHelper.cs 未迁移库 scope 过滤/展示(6-17)
Helpers/LocationScopeBindingHelper.cs ResolveFilteredLocationIdsForListAsync
Helpers/LabelTemplateListItemsHelper.cs Items 列
Dtos/LabelTemplate/LabelTemplateGetListInputVo.cs PartnerId 筛选

八、数据库迁移(可选)

需完整 Company / Region 三维 scope 时,执行:

美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/scripts/fl_label_template_scope.sql

未执行前:列表可正常返回(6-17 + 6-18),但无法持久化 Company/Region 多选明细。


九、App 标签预览 POST /api/app/us-app-labeling/preview

现象

App 标签预览页调用 preview 失败(500 或 400),常与 label-template 列表 同源:加载模板头时 ORM 仍 SELECT 不存在的 AppliedPartnerType / AppliedRegionType

另有两项逻辑缺陷(与 6-11 文档不一致):

# 问题 后果
1 LabelAppService.PreviewAsync 全表查询 FlLabelTemplateDbEntity 未迁移 scope 列时 Unknown column → 500
2 UsAppLabelingAppService.PreviewAsync 未向 _labelAppService.PreviewAsynclocationId 模板含 Company 自动生成元素时报 「预览/打印需要 locationId 以填充 Company 信息」
3 出参 labelId 仍返回 fl_label.Id(GUID) 与 Print Log 当日序号 yyyyMMdd-n 不一致

修复说明

改动 说明
LabelAppService.PreviewAsync 模板头查询改用 LabelTemplateQueryHelper.ProjectListColumns
UsAppLabelingAppService.PreviewAsync 传入 LocationIdlabelId 改用 ReportsPrintLogDailyLabelIdHelper.ResolveNextDailyLabelIdAsync
UsAppLabelingAppService.PrintAsync 解析模板时同步传入 LocationId(打印与预览 Company 填充一致)
DashboardAppService 模板统计 Count 同样走 ProjectListColumns

接口说明

项目 内容
方法 POST
路径 /api/app/us-app-labeling/preview
鉴权 App Bearer Token

入参(Body:UsAppLabelPreviewInputVo

字段 类型 必填 说明
locationId string 当前门店 Id(location.Id
labelCode string 标签编码(fl_label.LabelCode
productId string 预览产品 Id;不传则取标签绑定第一个产品
baseTime DateTime 日期/时间控件基准;也用于计算当日 labelId 序号;未传为服务器当前时间
printInputJson object PRINT_INPUT 元素用户输入

出参(UsAppLabelPreviewDto

字段 说明
labelId 门店当日下一个打印序号 yyyyMMdd-n(预览不落库)
locationId / labelCode 回传入参
template 已解析 AUTO_DB / PRINT_INPUT 的模板结构(含 Company 自动填充)
labelLastEdited 标签最近编辑时间
其它 typeNameproductNametemplateProductDefaultValues

labelId 规则

6-11get-print-log-list 一致:{baseTime 日期 yyyyMMdd}-{当日已有打印任务数 + 1}

Company 自动元素

模板含 Company 自动生成控件时,后端根据 locationIdfl_partner 填充 config.text(详见 6-11 第二节)。

请求示例

curl -X POST "http://flus-test.3ffoodsafety.com/api/app/us-app-labeling/preview" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "locationId": "550e8400-e29b-41d4-a716-446655440000",
    "labelCode": "LBL0001",
    "productId": "PROD001",
    "baseTime": "2026-06-18T09:00:00"
  }'

响应片段(示例)

{
  "labelId": "20260618-3",
  "locationId": "550e8400-e29b-41d4-a716-446655440000",
  "labelCode": "LBL0001",
  "labelLastEdited": "2026-06-01T08:30:09",
  "template": {
    "id": "tpl_xxx",
    "width": 2,
    "height": 2,
    "unit": "inch",
    "elements": []
  }
}

验证步骤

  1. 停服 → dotnet build → 重启(同 label-template 一节)。
  2. App 进入标签预览页,Network 中 preview 应 200
  3. 响应 labelIdyyyyMMdd-n,非 GUID。
  4. 模板含 Company 元素时,template.elements 中对应 config.text 为门店所属公司名。
  5. 当日已有 N 条打印任务时,preview 返回 -{N+1}

涉及代码(preview)

文件 说明
Services/UsAppLabelingAppService.cs PreviewAsync / PrintAsyncLocationId、当日 labelId
Services/LabelAppService.cs PreviewAsync 模板头 ProjectListColumns
Helpers/LabelTemplateQueryHelper.cs 列投影
Helpers/ReportsPrintLogDailyLabelIdHelper.cs ResolveNextDailyLabelIdAsync
Helpers/PartnerCompanyDisplayHelper.cs Company 自动填充

十、App 打印日志 POST /api/app/us-app-labeling/get-print-log-list

现象

Postman 传 printDate: "2026-06-16" 返回 totalCount: 0,误以为接口异常。

根因(查库核对)

门店 3a218397-8dda-a378-e024-ef89bcef8d24 在测试库中:

自然日(DATE(IFNULL(PrintedAt, CreationTime)) 记录数
2026-06-01 7
2026-05-31 3
2026-06-16 0

接口按入参日期筛选时,该日无数据则返回空列表,行为正确。 文档/示例误用 2026-06-16,应改用 2026-06-01 验证。

另:PrintedAt 入库多为 null,筛选实际走 CreationTime

本轮修复

改动 说明
日期条件 改用 MySQL DATE(IFNULL(t.PrintedAt, t.CreationTime)) = 'yyyy-MM-dd',避免 DateTime 区间比较时区偏差
printDateDay 新增字符串入参(yyyy-MM-dd),优先于 printDate,避免 JSON 仅日期 UTC 歧义
未传日期 printDateprintDateDay 均未传时不按日过滤(返回该门店全部打印记录,兼容 App 未传参)
labelId 列表出参改为当日序号 yyyyMMdd-nlabelEntityIdfl_label.Id

请求示例(有数据的日期)

curl -X POST "http://192.168.1.4:19001/api/app/us-app-labeling/get-print-log-list" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "locationId": "3a218397-8dda-a378-e024-ef89bcef8d24",
    "skipCount": 1,
    "maxResultCount": 20,
    "printDateDay": "2026-06-01"
  }'

或使用 "printDate": "2026-06-01"(等价)。

权限说明

非 admin / 非 Partner 角色时,仅返回 CreatedBy = 当前用户 的记录;若 Token 用户不是打印人,即使日期正确也会空列表。

部署

修改在 FoodLabeling.Application,需 停服 → dotnet build → 重启 后 Postman 才生效。

涉及代码

文件 说明
Helpers/ReportsPrintLogDailyLabelIdHelper.cs ResolvePrintLogFilterCalendarDayApplyPrintTaskCalendarDayFilter
Dtos/UsAppLabeling/PrintLogGetListInputVo.cs PrintDateDay
Services/UsAppLabelingAppService.cs GetPrintLogListAsync

关联文档

  • App Preview labelId / Company:项目相关文档/6-11代码优化.md
  • 第一轮 scope 兼容:项目相关文档/6-17代码优化.md(第一节、第二节 get-print-log-list)
  • 三维 scope 业务规则:项目相关文档/6-4代码优化.md
  • 分页约定:Helpers/PagedQueryConvention.cs