6-11代码优化.md 12 KB

6-11 代码优化

本文档说明 2026-06-11 对美国版 App 标签预览/打印相关的两项纯后端改造(不改 Web / App 前端):

  1. POST /api/app/us-app-labeling/preview 出参 labelId 改为门店当日序号 yyyyMMdd-n
  2. 模板 Company 自动生成元素:预览/打印时按门店从 fl_partner 填充公司名及可选地址字段

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


一、Preview 出参 labelId 格式

背景

预览页「Label ID」原先返回 fl_label.Id(GUID,如 3a2192be-7b8f-e3e8-db9c-3a5e627b9222),与业务要求不符。

业务规则:Label ID = 某门店当日每次打印任务的递增序号,与 Print Log、管理端报表一致:

示例 含义
20260513-1 该门店 2026-05-13 当日第 1 次打印
20260513-2 同日第 2 次
20260514-1 次日重新从 1 计数

格式:{yyyyMMdd}-{n}n 从 1 递增,按 PrintedAt ?? CreationTime 所在自然日、同一 locationId 统计)。

接口说明

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

入参(Body:UsAppLabelPreviewInputVo

字段 类型 必填 说明
locationId string 门店 Id(location.Id
labelCode string 标签编码(fl_label.LabelCode
productId string 预览产品 Id
baseTime DateTime 基准时间;影响模板内日期/时间控件,也用于确定「哪一天的序号」;未传则用服务端当前时间
printInputJson object 打印时输入项

出参(UsAppLabelPreviewDto)变更

字段 变更前 变更后
labelId fl_label.Id(GUID) 门店当日下一个打印序号 yyyyMMdd-n

其余字段(locationIdlabelCodetemplatelabelLastEdited 等)不变。

labelId 计算规则

  1. baseTime ?? 当前服务器时间 的日期部分 yyyyMMdd
  2. 统计该 locationId 在当日内已有打印任务数(fl_label_print_task,时间取 PrintedAt ?? CreationTime)。
  3. labelId = {yyyyMMdd}-{已有任务数 + 1}(预览不落库,表示「若此刻点击 Print 将获得的序号」)。
  4. POST /api/app/us-app-labeling/get-print-log-list、管理端 GET /api/app/reports/print-log-list 使用同一 Helper:ReportsPrintLogDailyLabelIdHelper

注意labelId 不是 fl_label.LabelCode,也不是 fl_label.Id。标签主键如需内部关联,请使用打印任务创建后的 taskId 或 print-log 中的 labelEntityId

请求示例(labelId)

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-05-13T09:00:00"
  }'

响应片段(示例)

{
  "labelId": "20260513-3",
  "locationId": "550e8400-e29b-41d4-a716-446655440000",
  "labelCode": "LBL0001",
  "labelLastEdited": "2026-06-01T08:30:09",
  "template": { }
}

若该门店在 2026-05-13 已有 2 条打印记录,预览返回 20260513-3;当日首条预览为 20260513-1

涉及代码(labelId)

文件 说明
Helpers/ReportsPrintLogDailyLabelIdHelper.cs 新增 ResolveNextDailyLabelIdAsync(预览取下一序号)
Services/UsAppLabelingAppService.cs PreviewAsync 出参 LabelId 改用当日序号
Dtos/UsAppLabeling/UsAppLabelPreviewDto.cs labelId XML 注释
IServices/IUsAppLabelingAppService.cs 接口注释

验证步骤(labelId)

  1. 选定门店,确认当日已有 N 条 fl_label_print_task(可用 print-log 列表核对)。
  2. 调用 previewlabelId 应为 {今日yyyyMMdd}-{N+1}
  3. 执行 print 创建新任务后,print-log 中该任务 labelId 与预览时一致(同一时刻连续预览+打印)。
  4. 修改 baseTime 为历史日期,序号应按该日任务数计算,而非「今天」。

SQL 抽查

SELECT Id, LocationId, PrintedAt, CreationTime
FROM fl_label_print_task
WHERE LocationId = '{locationId}'
  AND IFNULL(PrintedAt, CreationTime) >= CURDATE()
  AND IFNULL(PrintedAt, CreationTime) < DATE_ADD(CURDATE(), INTERVAL 1 DAY)
ORDER BY IFNULL(PrintedAt, CreationTime), Id;

当日行数 + 1 应与 preview 返回的 -n 部分一致。


二、Company 自动生成元素(预览/打印填充)

背景

模板编辑器中的 Company 控件属于「按门店自动取数」类型。用户点击打印时,后端应:

  • 始终展示当前门店所属 Company 名称fl_partner.PartnerName
  • 仅当模板元素 config 中勾选 Address / City / State / Zip / Email 时,才追加对应行;未勾选或库中无值则不展示该字段

勾选状态不落独立表,保存在 fl_label_template_element.ConfigJson(通过 POST/PUT /api/app/label-templateelements[].config 写入)。公司主数据来自 fl_partner

本次不提供 Web 属性面板 UI;需通过 label-template 保存接口 或 SQL 维护 config

元素识别规则

满足以下任一条件即视为 Company 自动生成元素(PartnerCompanyDisplayHelper.IsCompanyAutoElement):

条件 说明
typeAdd = "auto_Company" 标准 Company 控件
valueSourceType = "AUTO_DB"typeAddauto_ 开头且含 Company 兼容命名

常见组合:elementType = "TEXT_STATIC"valueSourceType = "AUTO_DB"typeAdd = "auto_Company"

模板 config(勾选字段)

写入 elements[].config(JSON),支持两种等价写法(可混用):

方式 A:数组 companyIncludeFields

数组值 含义 数据来源(fl_partner
address 街道地址 Street
city 城市 City
state 州/省 StateCode
zip 邮编 ZipCode
email 邮箱 ContactEmail

方式 B:布尔开关

includeAddress / includeCity / includeState / includeZip / includeEmailtrue 表示勾选)

示例(保存模板时)

{
  "elementType": "TEXT_STATIC",
  "valueSourceType": "AUTO_DB",
  "typeAdd": "auto_Company",
  "name": "Company",
  "config": {
    "companyIncludeFields": ["address", "city", "state", "zip", "email"]
  }
}

或:

"config": {
  "includeAddress": true,
  "includeCity": true,
  "includeState": true,
  "includeZip": true,
  "includeEmail": false
}

展示文本格式

后端将解析结果写入 template.elements[].config.text(多行,\n 分隔):

行序 内容 规则
第 1 行 公司名 固定输出(有 PartnerName 时)
第 2 行 街道 仅勾选 addressStreet 非空
第 3 行 城市, 州, 邮编 勾选 city/state/zip 中任一项时,按「City, StateCode, ZipCode」逗号拼接(空段跳过)
第 4 行 邮箱 仅勾选 emailContactEmail 非空

渲染示例

Acme Foods Inc
123 Main Street
New York, NY, 10001
sales@acme.com

若仅勾选公司名(无任何 include),则 text 仅一行公司名。

门店 → Company 解析

  1. 入参 locationIdlocation.Id,Guid 字符串)
  2. location 表取 Partner 字段
  3. fl_partner 中按 Id = PartnerPartnerName = Partner 匹配(未删除)
  4. 匹配失败时不抛错,config.text 保持原样(不填充)

入参要求

场景 locationId
模板不含 Company 自动生成元素 可选(App preview 仍必填门店,与原有逻辑一致)
模板 Company 自动生成元素 LabelPreviewResolveInputVo.locationId 必填;缺失返回 400预览/打印需要 locationId 以填充 Company 信息

App 调用链UsAppLabelPreviewInputVo.locationId → 内部 LabelAppService.PreviewAsync(LabelPreviewResolveInputVo) 时须原样传入 locationId

受影响接口(均通过 LabelAppService.PreviewAsync 渲染模板):

方法 路径 说明
POST /api/app/us-app-labeling/preview App 预览
POST /api/app/us-app-labeling/print App 打印(落库前解析模板)
POST /api/app/label/preview(或管理端等价预览) Web 预览;含 Company 元素时 Body 须带 locationId

重打 reprint 使用历史任务 RenderTemplateJson 快照,不再重新解析 Company。

出参变更

无新增顶层字段;变更在 template.elements[] 内:

字段 变更
elements[].config.text Company 元素由后端按上文规则写入多行文本

数据库与脚本

说明
fl_partner.PartnerName 公司名(必有)
fl_partner.Street / City / StateCode / ZipCode / ContactEmail 可选展示字段
location.Partner 门店归属 Company 键(Id 或名称)
DDL scripts/fl_partner_add_address_columns.sql(缺地址列时执行)

模板适用范围(Company/Region/Location 三维 scope)见 项目相关文档/6-4代码优化.md,与本节「元素内 Company 自动填值」相互独立。

请求示例(含 Company 的 preview)

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"
  }'

响应 template.elements 中 Company 元素片段示例:

{
  "elementType": "TEXT_STATIC",
  "typeAdd": "auto_Company",
  "valueSourceType": "AUTO_DB",
  "config": {
    "companyIncludeFields": ["address", "city", "state", "zip"],
    "text": "Acme Foods Inc\n123 Main Street\nNew York, NY, 10001"
  }
}

涉及代码(Company)

文件 说明
Helpers/PartnerCompanyDisplayHelper.cs 识别元素、解析 config、格式化文本、门店→Partner
Services/LabelAppService.cs PreviewAsync 填充 Company 的 config.text
Dtos/Label/LabelPreviewResolveInputVo.cs 新增 locationId
Services/UsAppLabelingAppService.cs preview/print 调用预览时传入 locationId

验证步骤(Company)

  1. 确认门店 location.Partner 能关联到有效 fl_partner 记录(地址列已执行 DDL)。
  2. 模板含 auto_Company 元素,config 勾选若干 include 字段;保存后查 fl_label_template_element.ConfigJson
  3. 调用 preview,Body 带正确 locationIdtemplate.elements 中 Company 的 config.text 行数与勾选一致。
  4. 去掉 locationId 或传空 → 返回 400(仅当模板含 Company 元素时)。
  5. 执行 print,检查 fl_label_print_task.RenderTemplateJson 内 Company text 与预览一致。

SQL 抽查

-- 门店归属 Company
SELECT l.Id, l.Partner, p.PartnerName, p.Street, p.City, p.StateCode, p.ZipCode, p.ContactEmail
FROM location l
LEFT JOIN fl_partner p ON (p.Id = l.Partner OR p.PartnerName = l.Partner) AND p.IsDeleted = 0
WHERE l.Id = '{locationId}' AND l.IsDeleted = 0;

-- 模板 Company 元素 config
SELECT e.Id, e.TypeAdd, e.ValueSourceType, e.ConfigJson
FROM fl_label_template_element e
JOIN fl_label_template t ON t.Id = e.TemplateId
WHERE t.TemplateCode = '{templateCode}' AND e.IsDeleted = 0
  AND e.TypeAdd = 'auto_Company';

关联文档

  • 同序号规则:项目相关文档/6-2代码优化.mdApp get-print-log-list 的 Label ID
  • 模板三维 scope:项目相关文档/6-4代码优化.md/api/app/label-template
  • App 预览页读取字段:labelId / LabelIdpreview.vue