Blame view

项目相关文档/6-11代码优化.md 12 KB
49755ef0   李曜臣   6-12代码优化
1
2
  # 6-11 代码优化
  
14afbc16   李曜臣   2026-6-22
3
4
5
6
7
8
  本文档说明 **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`
49755ef0   李曜臣   6-12代码优化
9
10
11
  
  ---
  
14afbc16   李曜臣   2026-6-22
12
13
14
  ## 一、Preview 出参 `labelId` 格式
  
  ### 背景
49755ef0   李曜臣   6-12代码优化
15
16
17
18
19
20
21
22
23
24
25
26
27
  
  预览页「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` 统计)。
  
14afbc16   李曜臣   2026-6-22
28
  ### 接口说明
49755ef0   李曜臣   6-12代码优化
29
30
31
32
33
34
35
  
  | 项目 | 内容 |
  |------|------|
  | 方法 | `POST` |
  | 路径 | `/api/app/us-app-labeling/preview` |
  | 鉴权 | Bearer Token |
  
14afbc16   李曜臣   2026-6-22
36
  #### 入参(Body:`UsAppLabelPreviewInputVo`)
49755ef0   李曜臣   6-12代码优化
37
38
39
40
41
42
43
44
45
  
  | 字段 | 类型 | 必填 | 说明 |
  |------|------|------|------|
  | `locationId` | string | 是 | 门店 Id(`location.Id`) |
  | `labelCode` | string | 是 | 标签编码(`fl_label.LabelCode`) |
  | `productId` | string | 否 | 预览产品 Id |
  | `baseTime` | DateTime | 否 | 基准时间;影响模板内日期/时间控件,也用于确定「哪一天的序号」;未传则用服务端当前时间 |
  | `printInputJson` | object | 否 | 打印时输入项 |
  
14afbc16   李曜臣   2026-6-22
46
  #### 出参(`UsAppLabelPreviewDto`)变更
49755ef0   李曜臣   6-12代码优化
47
48
49
50
51
52
53
  
  | 字段 | 变更前 | 变更后 |
  |------|--------|--------|
  | **`labelId`** | `fl_label.Id`(GUID) | 门店当日**下一个**打印序号 `yyyyMMdd-n` |
  
  其余字段(`locationId`、`labelCode`、`template`、`labelLastEdited` 等)不变。
  
14afbc16   李曜臣   2026-6-22
54
  #### `labelId` 计算规则
49755ef0   李曜臣   6-12代码优化
55
56
57
58
59
60
61
62
  
  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`。
  
14afbc16   李曜臣   2026-6-22
63
  ### 请求示例(labelId)
49755ef0   李曜臣   6-12代码优化
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
  
  ```bash
  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"
    }'
  ```
  
  ### 响应片段(示例)
  
  ```json
  {
    "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`
  
14afbc16   李曜臣   2026-6-22
91
  ### 涉及代码(labelId)
49755ef0   李曜臣   6-12代码优化
92
93
94
95
96
97
98
99
  
  | 文件 | 说明 |
  |------|------|
  | `Helpers/ReportsPrintLogDailyLabelIdHelper.cs` | 新增 `ResolveNextDailyLabelIdAsync`(预览取下一序号) |
  | `Services/UsAppLabelingAppService.cs` | `PreviewAsync` 出参 `LabelId` 改用当日序号 |
  | `Dtos/UsAppLabeling/UsAppLabelPreviewDto.cs` | `labelId` XML 注释 |
  | `IServices/IUsAppLabelingAppService.cs` | 接口注释 |
  
14afbc16   李曜臣   2026-6-22
100
  ### 验证步骤(labelId)
49755ef0   李曜臣   6-12代码优化
101
102
103
104
105
106
  
  1. 选定门店,确认当日已有 N 条 `fl_label_print_task`(可用 print-log 列表核对)。
  2. 调用 **preview**`labelId` 应为 `{今日yyyyMMdd}-{N+1}`
  3. 执行 **print** 创建新任务后,print-log 中该任务 `labelId` 与预览时一致(同一时刻连续预览+打印)。
  4. 修改 `baseTime` 为历史日期,序号应按该日任务数计算,而非「今天」。
  
14afbc16   李曜臣   2026-6-22
107
  #### SQL 抽查
49755ef0   李曜臣   6-12代码优化
108
109
110
111
112
113
114
115
116
117
118
119
120
121
  
  ```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` 部分一致。
  
  ---
  
14afbc16   李曜臣   2026-6-22
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
  ## 二、Company 自动生成元素(预览/打印填充)
  
  ### 背景
  
  模板编辑器中的 **Company** 控件属于「按门店自动取数」类型。用户点击打印时,后端应:
  
  - **始终**展示当前门店所属 **Company 名称**`fl_partner.PartnerName`
  - **仅当模板元素 config 中勾选** Address / City / State / Zip / Email 时,才追加对应行;未勾选或库中无值则不展示该字段
  
  勾选状态**不落独立表**,保存在 **`fl_label_template_element.ConfigJson`**(通过 `POST/PUT /api/app/label-template` 的 `elements[].config` 写入)。公司主数据来自 **`fl_partner`**
  
  > 本次**不提供** Web 属性面板 UI;需通过 **label-template 保存接口** 或 SQL 维护 `config`。
  
  ### 元素识别规则
  
  满足以下任一条件即视为 Company 自动生成元素(`PartnerCompanyDisplayHelper.IsCompanyAutoElement`):
  
  | 条件 | 说明 |
  |------|------|
  | `typeAdd = "auto_Company"` | 标准 Company 控件 |
  | `valueSourceType = "AUTO_DB"` 且 `typeAdd` 以 `auto_` 开头且含 `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` / `includeEmail`(`true` 表示勾选)
  
  **示例(保存模板时)**
  
  ```json
  {
    "elementType": "TEXT_STATIC",
    "valueSourceType": "AUTO_DB",
    "typeAdd": "auto_Company",
    "name": "Company",
    "config": {
      "companyIncludeFields": ["address", "city", "state", "zip", "email"]
    }
  }
  ```
  
  或:
  
  ```json
  "config": {
    "includeAddress": true,
    "includeCity": true,
    "includeState": true,
    "includeZip": true,
    "includeEmail": false
  }
  ```
  
  ### 展示文本格式
  
  后端将解析结果写入 **`template.elements[].config.text`**(多行,`\n` 分隔):
  
  | 行序 | 内容 | 规则 |
  |------|------|------|
  | 第 1 行 | 公司名 | **固定输出**(有 `PartnerName` 时) |
  | 第 2 行 | 街道 | 仅勾选 `address` 且 `Street` 非空 |
  | 第 3 行 | 城市, 州, 邮编 | 勾选 city/state/zip 中任一项时,按「City, StateCode, ZipCode」逗号拼接(空段跳过) |
  | 第 4 行 | 邮箱 | 仅勾选 `email` 且 `ContactEmail` 非空 |
  
  **渲染示例**
  
  ```
  Acme Foods Inc
  123 Main Street
  New York, NY, 10001
  sales@acme.com
  ```
  
  若仅勾选公司名(无任何 include),则 `text` 仅一行公司名。
  
  ### 门店 → Company 解析
  
  1. 入参 **`locationId`**`location.Id`,Guid 字符串)
  2.`location` 表取 **`Partner`** 字段
  3.**`fl_partner`** 中按 **`Id = Partner`****`PartnerName = 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)
  
  ```bash
  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 元素片段示例:
  
  ```json
  {
    "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 带正确 `locationId`:`template.elements` 中 Company 的 `config.text` 行数与勾选一致。
  4. 去掉 `locationId` 或传空 → 返回 400(仅当模板含 Company 元素时)。
  5. 执行 **print**,检查 `fl_label_print_task.RenderTemplateJson` 内 Company `text` 与预览一致。
  
  #### SQL 抽查
  
  ```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';
  ```
  
  ---
  
49755ef0   李曜臣   6-12代码优化
320
321
322
  ## 关联文档
  
  - 同序号规则:`项目相关文档/6-2代码优化.md` → **App `get-print-log-list` 的 Label ID**
14afbc16   李曜臣   2026-6-22
323
  - 模板三维 scope:`项目相关文档/6-4代码优化.md` → **`/api/app/label-template`**
49755ef0   李曜臣   6-12代码优化
324
  - App 预览页读取字段:`labelId` / `LabelId`(`preview.vue`