diff --git a/6-18代码优化.md b/6-18代码优化.md deleted file mode 100644 index bb85b74..0000000 --- a/6-18代码优化.md +++ /dev/null @@ -1,458 +0,0 @@ -# 6-18 代码优化 - -本文档说明 **2026-06-18** 对 **`GET /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`。 - ---- - -## 二、根因 - -服务端日志已明确报错: - -```text -Unknown column 'AppliedPartnerType' in 'field list' -``` - -对应 SQL: - -```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.AppliedRegionType` 的 `IsIgnore` 行为不一致),导致未执行 `fl_label_template_scope.sql` 的库直接 500。 - -**MCP 查库(测试库)**: - -- `fl_label_template` **无** `AppliedPartnerType` / `AppliedRegionType` 列 -- **无** `fl_label_template_partner` / `fl_label_template_region` 表 -- **有** `AppliedLocationType` 与 `fl_label_template_location` - -### 2.2 次因:列表 SQL 其它隐患(一并修复) - -| # | 问题 | 后果 | -|---|------|------| -| 1 | 默认排序曾用 `LastModificationTime ?? CreationTime` | SqlSugar 翻译失败 → 500 | -| 2 | `Sorting` 直接 `OrderBy(input.Sorting)` 拼 SQL | 非法字段 / 注入风险 | -| 3 | 空 `templateIds` 仍 `Contains` 查询 | 可能生成 `IN ()` | -| 4 | 无效 partner/group 筛选未安全降级 | 未捕获异常 | - ---- - -## 三、修复说明 - -### 1. 从 ORM 实体移除不存在列 + 强制列投影(核心) - -| 改动 | 说明 | -|------|------| -| `FlLabelTemplateDbEntity` | **删除** `AppliedPartnerType` / `AppliedRegionType` 属性 | -| **`LabelTemplateQueryHelper.ProjectListColumns`** | 列表/详情/重复校验等只读查询 **显式 Select** 真实列,SQL 不再出现 scope 列 | -| `LabelTemplateScopeSchemaHelper` | 探测列/表;已迁移时用 raw SQL 写入 scope 列 | -| `LabelTemplateAppService` | `GetListAsync` 在排序后调用 `ProjectListColumns` | - -> **重要**:若 Yi-SQL 日志仍出现 `AppliedPartnerType`,说明进程加载的是**旧 DLL**(`FoodLabeling.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/desc`、`TemplateCode asc/desc`、`CreationTime asc/desc`、`LastModificationTime asc/desc`;其它值忽略并走默认排序 | - -筛选解析顺序:**LocationId → GroupId → PartnerId**;均未传则不按门店范围收窄(仍返回全部未删除模板,除非前端传了无效 Id 则返回空列表)。 - -### 出参(`PagedResultWithPageDto`) - -| 字段 | 说明 | -|------|------| -| `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 - -```bash -curl -X POST "http://flus-test.3ffoodsafety.com/api/oauth/Login" \ - -H "Content-Type: application/x-www-form-urlencoded" \ - -d "userName=admin&password=123456" -``` - -### 列表(第一页,默认排序) - -```bash -curl -G "http://flus-test.3ffoodsafety.com/api/app/label-template" \ - -H "Authorization: Bearer {token}" \ - --data-urlencode "SkipCount=1" \ - --data-urlencode "MaxResultCount=10" -``` - -### 带 Company 筛选 - -```bash -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}" -``` - -### 指定排序 - -```bash -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" -``` - -### 响应片段(示例) - -```json -{ - "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" - } - ] -} -``` - ---- - -## 六、验证步骤 - -0. **重新编译并重启**(必做): - ```bash - cd "美国版/Food Labeling Management Code/Yi.Abp.Net8/src/Yi.Abp.Web" - dotnet build - ``` - 停止正在运行的 Web 进程后重新启动;确认 Yi-SQL 中 **不再出现** `AppliedPartnerType`。 - -1. **部署**包含 6-17 + 6-18 的后端并重启。 -2. 调用 `GET /api/app/label-template?SkipCount=1&MaxResultCount=10` → 应 **200**,`totalCount >= 0`。 -3. Web **Label Templates** 列表可加载,不再出现红色 **Failed to load label templates**。 -4. 传无效 `PartnerId` / `GroupId` → **200** 且 `items=[]`(非 500)。 -5. 不传 `Sorting` → 按最近编辑时间降序;传 `Sorting=TemplateName asc` → 按名称升序。 -6. 分页:第 2 页 `SkipCount=2`,`totalCount` 与 UI 一致。 - -### SQL 抽查(修复后 ORM 应生成的列) - -```sql -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 是否已迁移 - -```sql -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.PreviewAsync` 传 **`locationId`** | 模板含 Company 自动生成元素时报 **「预览/打印需要 locationId 以填充 Company 信息」** | -| 3 | 出参 **`labelId`** 仍返回 `fl_label.Id`(GUID) | 与 Print Log 当日序号 `yyyyMMdd-n` 不一致 | - -### 修复说明 - -| 改动 | 说明 | -|------|------| -| `LabelAppService.PreviewAsync` | 模板头查询改用 `LabelTemplateQueryHelper.ProjectListColumns` | -| `UsAppLabelingAppService.PreviewAsync` | 传入 `LocationId`;`labelId` 改用 `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` | 标签最近编辑时间 | -| 其它 | `typeName`、`productName`、`templateProductDefaultValues` 等 | - -#### `labelId` 规则 - -与 `6-11`、`get-print-log-list` 一致:`{baseTime 日期 yyyyMMdd}-{当日已有打印任务数 + 1}`。 - -#### Company 自动元素 - -模板含 Company 自动生成控件时,后端根据 **`locationId`** 查 `fl_partner` 填充 `config.text`(详见 `6-11` 第二节)。 - -### 请求示例 - -```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-06-18T09:00:00" - }' -``` - -### 响应片段(示例) - -```json -{ - "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. 响应 `labelId` 为 `yyyyMMdd-n`,非 GUID。 -4. 模板含 Company 元素时,`template.elements` 中对应 `config.text` 为门店所属公司名。 -5. 当日已有 N 条打印任务时,preview 返回 `-{N+1}`。 - -### 涉及代码(preview) - -| 文件 | 说明 | -|------|------| -| `Services/UsAppLabelingAppService.cs` | `PreviewAsync` / `PrintAsync` 传 `LocationId`、当日 `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 歧义 | -| 未传日期 | **`printDate` 与 `printDateDay` 均未传时不按日过滤**(返回该门店全部打印记录,兼容 App 未传参) | -| `labelId` | 列表出参改为当日序号 `yyyyMMdd-n`;`labelEntityId` 为 `fl_label.Id` | - -### 请求示例(有数据的日期) - -```bash -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` | `ResolvePrintLogFilterCalendarDay`、`ApplyPrintTaskCalendarDayFilter` | -| `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` diff --git a/美国版/Food Labeling Management App UniApp/src/App.vue b/美国版/Food Labeling Management App UniApp/src/App.vue index 99d3ed3..21c0101 100644 --- a/美国版/Food Labeling Management App UniApp/src/App.vue +++ b/美国版/Food Labeling Management App UniApp/src/App.vue @@ -3,11 +3,13 @@ import { onLaunch, onShow, onHide } from "@dcloudio/uni-app"; import { initOfflineSqlite } from "./utils/sqliteSync"; import { syncNowAndRefreshCaches } from "./utils/offlineSyncManager"; import { isLoggedIn } from "./utils/authSession"; +import { preloadAllLabelEditorFonts } from "./utils/labelEditorFonts"; let networkSyncInFlight = false; onLaunch(() => { void initOfflineSqlite(); + void preloadAllLabelEditorFonts(); uni.onNetworkStatusChange((res) => { if (!res.isConnected || !isLoggedIn() || networkSyncInFlight) return; networkSyncInFlight = true; diff --git a/美国版/Food Labeling Management App UniApp/src/pages/labels/preview.vue b/美国版/Food Labeling Management App UniApp/src/pages/labels/preview.vue index d50274f..8d2f5a4 100644 --- a/美国版/Food Labeling Management App UniApp/src/pages/labels/preview.vue +++ b/美国版/Food Labeling Management App UniApp/src/pages/labels/preview.vue @@ -63,7 +63,41 @@ {{ freeFieldNameLabel(el) }} -