FoodLabeling.Application Food Labeling 模块 - 应用层(美国版) 将标签/产品类别的按钮外观与展示字段按「JSON 字符串」落库;兼容历史单行 TEXT/COLOR/IMAGE。 未传按钮外观时的默认 JSON(与前端数组语义一致)。 规范化 / 产品类别同名字段:落库为合法 JSON 文本,不做整串 ToUpper(避免破坏 JSON)。 规范化 / 产品类别同名字段:已是 JSON 则原样落库;否则将整段文本序列化为 JSON 字符串(兼容历史单行色值/URL)。 Location 批量导入/导出 Excel(列名与 Web「Location Manager」表头对齐,兼容中英文表头别名) 导出表头顺序(与模板一致) 将门店列表写入 xlsx 内存流。 从上传的 Excel 解析为创建入参列表(行号从 2 起为数据行)。 与平台端约定:列表接口 Query 的 SkipCount 表示 SqlSugar 分页页码(从 1 起), 不是 0 基 offset。第一页应传 SkipCount=1 Product 批量导入/导出 Excel(列与「Product-Manager-批量导入模板」一致) 导出表头顺序(与模板一致) 导出数据行 导出数据行 将产品导出数据写入 xlsx 内存流(工作表名 Products)。 从上传的 Excel 解析为原始行(行号从 2 起为数据行)。 Reports — Print Log 全量导出 Excel(列与 Web Print Log 表头对齐) 导出表头(与 UI 一致) 将 Print Log 行写入 xlsx 内存流(工作表名 Print Log)。 Reports 模块角色判断(与 JWT 中角色声明一致) 是否为「可查看全部用户打印数据」的管理员: 标准 中含角色码 admin(普通账号绑定 RoleCode=admin 时走此路径); 内置超管:用户名 admin 时 JWT 使用自定义 claim Roles,不写多条 role,需单独识别; 超管权限 claim Permission*:*:* 时视为管理员。 Team Member 批量导入 Excel(列名与 Account Management 表格对齐,兼容常见别名) 从上传的 Excel 解析为创建入参列表(行号从 2 起为数据行)。 xlsx 流 最多数据行 角色名(忽略大小写、去空白)到角色 Id 未填 Password 列时使用 表头或解析错误 拆分门店单元格为「待解析」片段(后续由服务层解析为 Location Id)。 批量导入模板目录(服务器静态路径)等配置。 模板文件所在目录(Linux 示例:/www/wwwroot/FoodLabelingManagementUs/batchImportOfFiles Location Manager 导入模板文件名(与服务器上已上传文件名一致) Team Member 导入模板文件名(与服务器上已上传文件名一致) Product(Menu Management)导入模板文件名 Team Member 批量导入时,Excel 未填写「Password」列则使用的默认初始密码 单次导入最多处理的数据行数(不含表头) 上传 Excel 最大体积(字节),默认 10MB 单次「批量编辑」请求最多允许的条数(含空行过滤前的数组长度) 当前登录会话:菜单权限与退出 Dashboard 统计服务(美国版) 依据 PrintInputJson 中的保质期字段与「当前日期」比较得到 active/expired。 组织/分组(Account Management / Group,表 fl_group) 组织名称(Group Name) 所属合作伙伴 Id(fl_partner.Id) 是否启用(对应 UI Active) 按钮展示文案(为空则默认使用 CategoryName) 分类图/展示值:TEXT 可为图或空;COLOR 存色值(如 #409EFF);IMAGE 存图片 URL(与 ButtonAppearance 配合) 按钮外观:TEXT / COLOR / IMAGE(展示数据见 CategoryPhotoUrl) 门店可用范围:ALL / SPECIFIED 标签分类可用门店关联(对应表:fl_label_category_location) json 字段,直接保存为字符串(入参/出参自行序列化/反序列化) 标签打印数据明细(对应表:fl_label_print_data) 标签打印任务(对应表:fl_label_print_task) 默认值JSON(字符串保存) 门店 Support 联系方式(每个门店仅一条,对 App Support 页展示) 合作伙伴主数据(Account Management / Partner,表 fl_partner) 合作伙伴名称(公司名) 联系邮箱 电话 是否启用(对应 UI Active) 按钮展示文案(为空则默认使用 CategoryName) 分类图/展示值:TEXT 可为图或空;COLOR 存色值;IMAGE 存图片 URL(与 ButtonAppearance 配合) 按钮外观:TEXT / COLOR / IMAGE(展示数据见 CategoryPhotoUrl) 门店可用范围:ALL / SPECIFIED 产品类别可用门店关联(对应表:fl_product_category_location) menu 表映射(兼容数字/字符串类型的 Id、ParentId) rolemenu 表映射(兼容字符串类型 RoleId/MenuId) userlocation 表映射(成员-门店关联) Food Labeling 示例应用服务实现(美国版) 组织(Group)管理(fl_group) 标签管理(一个产品展示多个标签) 标签预览:不落库,只把 template elements 的 AUTO_DB/PRINT_INPUT 渲染进 config 标签模板管理(Label Templates) 门店管理服务(美国版) 全局 Support 联系方式(全门店共用;Web 可增改查,App JWT 仅可读) 合作伙伴管理(fl_partner) 上传类别图片(保存到 /www/wwwroot/FoodLabelingManagementUs/picture) 返回的 Url 可直接保存到 CategoryPhotoUrl。 产品管理(Products) 生成未删除数据中不重复的 PRD_ 前缀产品编码。 去重、校验门店 Id 格式与存在性。 按产品维度替换 fl_location_product:先删本产品全部关联,再按列表插入(每门店一行)。 产品模块:类别(Categories)服务,对外仅在 food-labeling-us 暴露 类别分页列表 类别详情 新增类别 编辑类别 删除类别(逻辑删除) 产品-门店关联管理(fl_location_product) 权限(Menu)管理(食品标签-美国版对外) 角色管理(食品标签-美国版对外) 角色-权限关联(食品标签-美国版对外) Reports(Print Log / Label Report) 成员(Team Member)服务,对外仅在 food-labeling-us 暴露 美国版 App 登录:邮箱 + 密码(与 AccountManager 相同盐值哈希)签发 JWT,并返回 userlocation 绑定门店 App 登录:签发 Token / RefreshToken,并返回当前账号绑定的门店列表 行为与系统 AccountService.PostLoginAsync 一致(含验证码、登录日志事件)。 门店数据来自 userlocationlocation 表。 邮箱、密码;若系统开启验证码则需传 Uuid、Code Token、RefreshToken 与绑定门店 登录成功 参数或验证码错误 服务器错误 获取当前登录用户已绑定的门店(切换门店时可重新拉取) 查询单个门店详情(Location 页):地址、门店电话、营业时间占位、店长(角色含 manager 的绑定用户) 仅当当前登录用户在 userlocation 中绑定该 locationId 时可查;否则返回业务异常。 店长:在同店绑定用户中,取 Role.RoleCodeRole.RoleName(忽略大小写)包含 manager 的第一条; 若无匹配则店长姓名与电话均为「无」。 OperatingHours:当前 location 表无营业时间字段,固定返回「无」。 门店主键(Guid 字符串) 与原型一致的展示字段 成功 未登录、门店标识无效、未绑定或门店不存在 服务器错误 按邮箱或用户名(邮箱形字符串写在 UserName 时)查找未删除且启用的用户;比较忽略大小写,Email 命中优先。 App Labeling:四级列表(标签分类 → 产品分类 → 产品 → 标签种类) 获取当前门店下四级嵌套数据 L1 标签分类 fl_label_category(含 buttonAppearance;COLOR/IMAGE 展示值在 categoryPhotoUrl);仅对当前门店可用:ALL 或 SPECIFIED 且在 fl_label_category_location; L2 产品分类 fl_product.CategoryId join fl_product_category(同上,展示值在 categoryPhotoUrl); L3 产品卡片:按「产品 + 标签模板」拆分(同一 productId、不同 fl_label.TemplateId 为多张卡);L4 为该卡下与门店、标签分类、该产品、该模板关联的标签实例(fl_label + fl_label_type)。 L2 仅包含对当前门店可用的类别:AvailabilityType=ALL,或 SPECIFIED 且在 fl_product_category_location 存在该门店记录; 未归类或分类行未关联到 fl_product_category 时仍归入「无」节点。 App 打印预览:按标签编码解析模板并返回顶部展示字段 + 预览模板结构 示例请求: ```json { "locationId": "LOC001", "labelCode": "LBL0001", "productId": "PROD001", "baseTime": "2026-03-26T10:30:00", "printInputJson": { "price": "12.99" } } ``` 预览入参 顶部字段 + 预览模板结构 成功 参数错误/数据不存在 服务器错误 App 打印:创建打印任务并落库打印明细(fl_label_print_task / fl_label_print_data) 打印入参 任务Id App 重新打印:根据历史任务Id重打(创建新任务与明细) App 打印日志:获取当前登录账号在当前门店打印的记录(分页,时间倒序) 仅返回满足: - CreatedBy == CurrentUser.Id - LocationId == input.LocationId 的打印任务记录(fl_label_print_task)。 示例请求: ```json { "locationId": "11111111-1111-1111-1111-111111111111", "skipCount": 1, "maxResultCount": 20 } ``` 参数说明: - locationId: 当前门店 Id(必填) - skipCount: 页码(从 1 开始,遵循本项目约定) - maxResultCount: 每页条数 分页查询入参 分页打印日志 成功 参数错误/未登录 服务器错误 将 App 入参中的 JsonElement(对象或 null)反序列化为 PreviewAsync 所需的扁平字典。