6-25代码优化.md
8.28 KB
6-25 代码优化
本文档说明 2026-06-25 对美国版标签模板(Label Template)接口的 contents 字段改造:新建/编辑时持久化模板内各控件的 elementName,列表与详情接口返回该字段,供前端 Contents 列展示。
一、背景与目标
| 项目 | 说明 |
|---|---|
| 原行为 | 列表 Contents 列依赖实时查询 fl_label_template_element 汇总控件名(items / itemNames) |
| 新行为 | 新增 contents 字段,落库 fl_label_template.Contents;保存时写入,列表/详情直接返回 |
| 控件名来源 | 编辑器右侧 Element name(对应 elements[].elementName),按 orderNum 排序、去重后逗号拼接 |
| 兼容 | 未传 contents 时后端按 elements 自动推导;库内 Contents 为空时列表回退 element 汇总 |
示例:模板含控件 labelname、text → contents = "labelname, text"
二、数据库迁移
脚本路径:美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/scripts/fl_label_template_contents.sql
| 步骤 | 内容 |
|---|---|
| 1 | fl_label_template 增加列 Contents varchar(2000) NULL |
| 2 | 从 fl_label_template_element.ElementName 回填已有模板 |
执行前请备份数据库。
二点五、列表 500:Unknown column 'Contents'
| 项目 | 说明 |
|---|---|
| 现象 | GET /api/app/label-template?SkipCount=1&MaxResultCount=10 报 Unknown column 'Contents' in 'field list' |
| 原因 | 代码已支持 contents,但库表尚未执行 fl_label_template_contents.sql,ORM 主查询 SELECT 了不存在的列 |
| 修复 | Contents 不参与 SqlSugar 列投影(实体 IsIgnore);读写经 LabelTemplateScopeSchemaHelper 探测列是否存在 |
| 未迁移时 | 列表/详情 contents 回退为 fl_label_template_element 汇总(items / itemNames 逻辑),接口可正常 200 |
| 已迁移时 | 保存写入 fl_label_template.Contents;列表优先读库内 contents,空时再回退 element 汇总 |
仍需执行迁移:若要在保存后持久化 contents(不依赖每次查 element 表),请执行 fl_label_template_contents.sql 并重启服务。
三、contents 字段规范
入参(POST / PUT)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
contents |
string |
否 | 控件名称逗号拼接,如 "labelname, text" |
elements |
array |
是(保存时) | 全量重建;未传 contents 时由后端从 elements[].elementName 推导 |
推导规则(LabelTemplateContentsHelper.ResolveForSave)
- 入参
contents非空 → 原样 trim 后落库 - 否则按
elements的orderNum升序、id次序排序 - 取非空
elementName,大小写不敏感去重 - 用
", "拼接(英文逗号 + 空格)
出参(GET 列表 / 详情)
| 字段 | 类型 | 说明 |
|---|---|---|
contents |
string |
持久化控件名列表;空时详情回退 elements 推导 |
contentsCount |
number |
仅列表:elements 数量 |
items |
string |
兼容:与 contents 同值(列表) |
itemNames |
string[] |
兼容:展示名数组(仍来自 element 表汇总,可能与 contents 文案略有差异) |
四、接口说明
1. 模板列表
| 项目 | 内容 |
|---|---|
| 方法 | GET |
| 路径 | /api/app/label-template |
| 鉴权 | Bearer Token |
Query / data 参数(节选)
| 参数 | 说明 |
|---|---|
SkipCount |
页码,从 1 起 |
MaxResultCount |
每页条数 |
Keyword |
模板名称/编码模糊搜索 |
PartnerId / GroupId / LocationId |
适用范围筛选 |
State |
启用状态 |
出参 items[] 相关字段
| 字段 | 示例 | 说明 |
|---|---|---|
id |
tpl_c23gov_mqri4g5a |
即 templateCode |
templateName |
Unnamed template |
模板名称 |
contents |
labelname, text |
Contents 列展示 |
contentsCount |
2 |
控件数量 |
items |
labelname, text |
兼容字段 |
sizeText |
6.00x4.00cm |
尺寸展示 |
location |
All Locations |
适用门店 |
curl 示例
curl -s -G "http://localhost:5000/api/app/label-template" \
-H "Authorization: Bearer <TOKEN>" \
--data-urlencode "SkipCount=1" \
--data-urlencode "MaxResultCount=10"
2. 模板详情
| 项目 | 内容 |
|---|---|
| 方法 | GET |
| 路径 | /api/app/label-template/{id} |
| 路径参数 | id = templateCode,如 tpl_c23gov_mqri4g5a |
出参新增
| 字段 | 说明 |
|---|---|
contents |
持久化控件名;库内为空时由当前 elements 推导 |
elements |
控件明细(含 elementName) |
curl 示例
curl -s "http://localhost:5000/api/app/label-template/tpl_c23gov_mqri4g5a" \
-H "Authorization: Bearer <TOKEN>"
3. 新建模板
| 项目 | 内容 |
|---|---|
| 方法 | POST |
| 路径 | /api/app/label-template |
| Content-Type | application/json |
Body 节选
{
"id": "tpl_new_example",
"name": "Unnamed template",
"unit": "cm",
"width": 6,
"height": 4,
"printOrientation": "vertical",
"contents": "labelname, text",
"elements": [
{
"id": "el_1",
"elementName": "labelname",
"type": "TEXT_PRODUCT",
"orderNum": 1,
"x": 0.5,
"y": 0.5,
"width": 2,
"height": 0.8
},
{
"id": "el_2",
"elementName": "text",
"type": "TEXT_STATIC",
"orderNum": 2,
"x": 0.5,
"y": 1.5,
"width": 3,
"height": 1
}
]
}
4. 编辑模板
| 项目 | 内容 |
|---|---|
| 方法 | PUT |
| 路径 | /api/app/label-template/{id} |
| 说明 | 与 POST 相同 Body 结构;elements 全量重建;contents 随保存更新 |
curl 示例
curl -s -X PUT "http://localhost:5000/api/app/label-template/tpl_c23gov_mqri4g5a" \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d "{\"id\":\"tpl_c23gov_mqri4g5a\",\"name\":\"Unnamed template\",\"unit\":\"cm\",\"width\":6,\"height\":4,\"printOrientation\":\"vertical\",\"contents\":\"labelname, text\",\"elements\":[...]}"
五、前端改动
| 文件 | 改动 |
|---|---|
LabelTemplateEditor/index.tsx |
保存时提交 contents: buildTemplateContentsFromElements(elements) |
labelTemplateService.ts |
列表归一化优先读取 contents |
LabelTemplatesView.tsx |
Contents 列优先展示 contents / contentsText |
types/labelTemplate.ts |
新增 buildTemplateContentsFromElements |
六、后端改动摘要
| 文件 | 改动 |
|---|---|
FlLabelTemplateDbEntity.cs |
Contents 为内存字段(IsIgnore),避免未迁移时 ORM SELECT/INSERT 报错 |
LabelTemplateScopeSchemaHelper.cs |
探测 HasContentsColumn;GetContentsMapAsync / SetContentsAsync |
LabelTemplateCreateInputVo.cs |
入参 contents |
LabelTemplateGetListOutputDto.cs / LabelTemplateGetOutputDto.cs |
出参 contents |
LabelTemplateContentsHelper.cs |
推导与保存解析 |
LabelTemplateAppService.cs |
Create/Update 条件落库;List/Get 条件读取 + element 回退 |
LabelTemplateQueryHelper.cs |
列表投影不含 Contents(与 scope/border 列策略一致) |
七、部署与验证
- 重新编译并重启
Yi.Abp.Web(未迁移 Contents 列时列表亦应 200,contents 来自 element 汇总) - (推荐)执行
fl_label_template_contents.sql,使保存后的contents持久化到主表 - 编辑模板
tpl_c23gov_mqri4g5a保存后,GET 列表应返回"contents": "labelname, text"(与编辑器 Element name 一致) - 模板列表 Contents 列应显示相同文案
八、注意事项
elementName为必填(保存校验「组件名字不能为空」),与contents中各项一一对应contents存的是 elementName 原文(如labelname),不是面板展示标签(如Label Name)itemNames仍保留旧汇总逻辑,新 UI 请以contents为准- 未执行 SQL 迁移:接口可用,但
contents不落主表,每次列表从 element 表实时汇总;执行迁移后保存才会写入fl_label_template.Contents