From 8e0c49ebdba651766bb1bc597822e6af2063c69b Mon Sep 17 00:00:00 2001
From: 李曜臣
Date: Tue, 21 Apr 2026 16:41:29 +0800
Subject: [PATCH] 产品标签类别优化; 菜单权限优化; 标签code设置不必填
---
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserBriefDto.cs | 17 +++++++++++++++++
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserMenuNodeDto.cs | 43 +++++++++++++++++++++++++++++++++++++++++++
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserMenuPermissionsOutputDto.cs | 15 +++++++++++++++
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelCreateInputVo.cs | 2 +-
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryCreateInputVo.cs | 23 +++++++++++++++++++++++
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryGetListOutputDto.cs | 6 ++++++
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryGetOutputDto.cs | 8 ++++++++
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateElementDto.cs | 2 +-
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryCreateInputVo.cs | 23 +++++++++++++++++++++++
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryGetListOutputDto.cs | 6 ++++++
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryGetOutputDto.cs | 9 +++++++++
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacMenu/RbacMenuGetListOutputDto.cs | 4 ++++
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelCategoryTreeNodeDto.cs | 3 +++
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelPrintInputVo.cs | 5 -----
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppProductCategoryNodeDto.cs | 12 ++++++++++++
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IAuthSessionAppService.cs | 33 +++++++++++++++++++++++++++++++++
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IUsAppLabelingAppService.cs | 1 -
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/AuthSessionAppService.cs | 213 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelCategoryDbEntity.cs | 18 ++++++++++++++++++
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelCategoryLocationDbEntity.cs | 22 ++++++++++++++++++++++
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelDbEntity.cs | 2 +-
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlProductCategoryDbEntity.cs | 18 ++++++++++++++++++
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlProductCategoryLocationDbEntity.cs | 22 ++++++++++++++++++++++
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelAppService.cs | 2 +-
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelCategoryAppService.cs | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/ProductCategoryAppService.cs | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/RbacMenuAppService.cs | 4 ++++
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/UsAppLabelingAppService.cs | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
项目相关文档/本次新增与优化接口汇总.md | 262 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
29 files changed, 1086 insertions(+), 21 deletions(-)
create mode 100644 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserBriefDto.cs
create mode 100644 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserMenuNodeDto.cs
create mode 100644 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserMenuPermissionsOutputDto.cs
create mode 100644 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IAuthSessionAppService.cs
create mode 100644 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/AuthSessionAppService.cs
create mode 100644 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelCategoryLocationDbEntity.cs
create mode 100644 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlProductCategoryLocationDbEntity.cs
create mode 100644 项目相关文档/本次新增与优化接口汇总.md
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserBriefDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserBriefDto.cs
new file mode 100644
index 0000000..4567f6d
--- /dev/null
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserBriefDto.cs
@@ -0,0 +1,17 @@
+namespace FoodLabeling.Application.Contracts.Dtos.AuthSession;
+
+///
+/// 当前登录用户简要信息(不含敏感字段)
+///
+public class CurrentUserBriefDto
+{
+ public Guid Id { get; set; }
+
+ public string UserName { get; set; } = string.Empty;
+
+ public string? Nick { get; set; }
+
+ public string? Email { get; set; }
+
+ public string? Icon { get; set; }
+}
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserMenuNodeDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserMenuNodeDto.cs
new file mode 100644
index 0000000..b7935f7
--- /dev/null
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserMenuNodeDto.cs
@@ -0,0 +1,43 @@
+namespace FoodLabeling.Application.Contracts.Dtos.AuthSession;
+
+///
+/// 当前用户可见菜单树节点(与权限分配一致)
+///
+public class CurrentUserMenuNodeDto
+{
+ public string Id { get; set; } = string.Empty;
+
+ public string ParentId { get; set; } = "0";
+
+ public string MenuName { get; set; } = string.Empty;
+
+ public string? RouterName { get; set; }
+
+ public string? Router { get; set; }
+
+ public string? PermissionCode { get; set; }
+
+ public int MenuType { get; set; }
+
+ public int MenuSource { get; set; }
+
+ public int OrderNum { get; set; }
+
+ public bool State { get; set; }
+
+ public string? MenuIcon { get; set; }
+
+ public string? Component { get; set; }
+
+ public bool IsLink { get; set; }
+
+ public bool IsCache { get; set; }
+
+ public bool IsShow { get; set; }
+
+ public string? Query { get; set; }
+
+ public string? Remark { get; set; }
+
+ public List Children { get; set; } = new();
+}
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserMenuPermissionsOutputDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserMenuPermissionsOutputDto.cs
new file mode 100644
index 0000000..5ea17eb
--- /dev/null
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/AuthSession/CurrentUserMenuPermissionsOutputDto.cs
@@ -0,0 +1,15 @@
+namespace FoodLabeling.Application.Contracts.Dtos.AuthSession;
+
+///
+/// 当前登录用户的菜单与权限码(用于前端动态路由/按钮权限)
+///
+public class CurrentUserMenuPermissionsOutputDto
+{
+ public CurrentUserBriefDto User { get; set; } = new();
+
+ public List RoleCodes { get; set; } = new();
+
+ public List PermissionCodes { get; set; } = new();
+
+ public List Menus { get; set; } = new();
+}
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelCreateInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelCreateInputVo.cs
index 5965aec..bd1107f 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelCreateInputVo.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/Label/LabelCreateInputVo.cs
@@ -2,7 +2,7 @@ namespace FoodLabeling.Application.Contracts.Dtos.Label;
public class LabelCreateInputVo
{
- public string LabelCode { get; set; } = string.Empty;
+ public string? LabelCode { get; set; }
public string LabelName { get; set; } = string.Empty;
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryCreateInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryCreateInputVo.cs
index 986bca2..efdd7fd 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryCreateInputVo.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryCreateInputVo.cs
@@ -6,10 +6,33 @@ public class LabelCategoryCreateInputVo
public string CategoryName { get; set; } = string.Empty;
+ ///
+ /// 按钮展示文案(为空则默认使用 CategoryName)
+ ///
+ public string? DisplayText { get; set; }
+
+ ///
+ /// COLOR 模式存色值、IMAGE 模式存图片 URL、TEXT 可为分类小图或空(与 buttonAppearance 配合)
+ ///
public string? CategoryPhotoUrl { get; set; }
public bool State { get; set; } = true;
+ ///
+ /// 按钮外观:TEXT / COLOR / IMAGE(展示值见 categoryPhotoUrl)
+ ///
+ public string ButtonAppearance { get; set; } = "TEXT";
+
+ ///
+ /// 门店可用范围:ALL / SPECIFIED
+ ///
+ public string AvailabilityType { get; set; } = "ALL";
+
+ ///
+ /// 指定门店 Id 列表(当 AvailabilityType=SPECIFIED 时必填)
+ ///
+ public List? LocationIds { get; set; }
+
public int OrderNum { get; set; }
}
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryGetListOutputDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryGetListOutputDto.cs
index db2aa7c..c8effdf 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryGetListOutputDto.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryGetListOutputDto.cs
@@ -8,10 +8,16 @@ public class LabelCategoryGetListOutputDto
public string CategoryName { get; set; } = string.Empty;
+ public string? DisplayText { get; set; }
+
public string? CategoryPhotoUrl { get; set; }
public bool State { get; set; }
+ public string ButtonAppearance { get; set; } = "TEXT";
+
+ public string AvailabilityType { get; set; } = "ALL";
+
public int OrderNum { get; set; }
public long NoOfLabels { get; set; }
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryGetOutputDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryGetOutputDto.cs
index 242b820..c4a740b 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryGetOutputDto.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelCategory/LabelCategoryGetOutputDto.cs
@@ -8,10 +8,18 @@ public class LabelCategoryGetOutputDto
public string CategoryName { get; set; } = string.Empty;
+ public string? DisplayText { get; set; }
+
public string? CategoryPhotoUrl { get; set; }
public bool State { get; set; }
+ public string ButtonAppearance { get; set; } = "TEXT";
+
+ public string AvailabilityType { get; set; } = "ALL";
+
+ public List LocationIds { get; set; } = new();
+
public int OrderNum { get; set; }
}
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateElementDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateElementDto.cs
index 5b6cf03..643a49d 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateElementDto.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LabelTemplate/LabelTemplateElementDto.cs
@@ -3,7 +3,7 @@ using System.Text.Json.Serialization;
namespace FoodLabeling.Application.Contracts.Dtos.LabelTemplate;
///
-/// 模板元素(对齐你给的 editor JSON:id/type/x/y/width/height/rotation/border/config)
+/// 模板元素(对齐 editor JSON:id/type/typeAdd/elementName/x/y/width/height/rotation/border/config 等)
///
public class LabelTemplateElementDto
{
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryCreateInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryCreateInputVo.cs
index 0dd9931..9f45ada 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryCreateInputVo.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryCreateInputVo.cs
@@ -9,10 +9,33 @@ public class ProductCategoryCreateInputVo
public string CategoryName { get; set; } = string.Empty;
+ ///
+ /// 按钮展示文案(为空则默认使用 CategoryName)
+ ///
+ public string? DisplayText { get; set; }
+
+ ///
+ /// COLOR 模式存色值、IMAGE 模式存图片 URL、TEXT 可为分类小图或空(与 buttonAppearance 配合)
+ ///
public string? CategoryPhotoUrl { get; set; }
+ ///
+ /// 按钮外观:TEXT / COLOR / IMAGE(展示值见 categoryPhotoUrl)
+ ///
+ public string ButtonAppearance { get; set; } = "TEXT";
+
public bool State { get; set; } = true;
+ ///
+ /// 门店可用范围:ALL / SPECIFIED
+ ///
+ public string AvailabilityType { get; set; } = "ALL";
+
+ ///
+ /// 指定门店 Id 列表(当 AvailabilityType=SPECIFIED 时必填)
+ ///
+ public List? LocationIds { get; set; }
+
public int OrderNum { get; set; } = 0;
}
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryGetListOutputDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryGetListOutputDto.cs
index ced7d81..51048ce 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryGetListOutputDto.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryGetListOutputDto.cs
@@ -11,10 +11,16 @@ public class ProductCategoryGetListOutputDto
public string CategoryName { get; set; } = string.Empty;
+ public string? DisplayText { get; set; }
+
public string? CategoryPhotoUrl { get; set; }
+ public string ButtonAppearance { get; set; } = "TEXT";
+
public bool State { get; set; }
+ public string AvailabilityType { get; set; } = "ALL";
+
public int OrderNum { get; set; }
public DateTime LastEdited { get; set; }
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryGetOutputDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryGetOutputDto.cs
index 3c673a5..2018bc2 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryGetOutputDto.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/ProductCategory/ProductCategoryGetOutputDto.cs
@@ -11,10 +11,19 @@ public class ProductCategoryGetOutputDto
public string CategoryName { get; set; } = string.Empty;
+ public string? DisplayText { get; set; }
+
+ /// COLOR 色值 / IMAGE 图片 URL / TEXT 可选图
public string? CategoryPhotoUrl { get; set; }
+ public string ButtonAppearance { get; set; } = "TEXT";
+
public bool State { get; set; }
+ public string AvailabilityType { get; set; } = "ALL";
+
+ public List LocationIds { get; set; } = new();
+
public int OrderNum { get; set; }
}
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacMenu/RbacMenuGetListOutputDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacMenu/RbacMenuGetListOutputDto.cs
index 4a59a70..9bebabf 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacMenu/RbacMenuGetListOutputDto.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/RbacMenu/RbacMenuGetListOutputDto.cs
@@ -11,6 +11,10 @@ public class RbacMenuGetListOutputDto
public string MenuName { get; set; } = string.Empty;
+ public string? RouterName { get; set; }
+
+ public string? Router { get; set; }
+
public string? PermissionCode { get; set; }
public int MenuType { get; set; }
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelCategoryTreeNodeDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelCategoryTreeNodeDto.cs
index 4e2ce5d..dc93a0b 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelCategoryTreeNodeDto.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelCategoryTreeNodeDto.cs
@@ -11,6 +11,9 @@ public class UsAppLabelCategoryTreeNodeDto
public string? CategoryPhotoUrl { get; set; }
+ /// 按钮外观:TEXT / COLOR / IMAGE(COLOR/IMAGE 的展示值在 categoryPhotoUrl)
+ public string ButtonAppearance { get; set; } = "TEXT";
+
public int OrderNum { get; set; }
public List ProductCategories { get; set; } = new();
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelPrintInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelPrintInputVo.cs
index 0977cdf..24d9679 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelPrintInputVo.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppLabelPrintInputVo.cs
@@ -49,11 +49,6 @@ public class UsAppLabelPrintInputVo
public JsonElement? PrintInputJson { get; set; }
///
- /// 客户端幂等请求 Id(可选);重复相同值时由服务端决定是否直接返回首次结果(见接口文档)。
- ///
- public string? ClientRequestId { get; set; }
-
- ///
/// 打印机Id(可选,若业务需要追踪)
///
public string? PrinterId { get; set; }
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppProductCategoryNodeDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppProductCategoryNodeDto.cs
index fd93dae..ddf43ea 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppProductCategoryNodeDto.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/UsAppLabeling/UsAppProductCategoryNodeDto.cs
@@ -14,6 +14,18 @@ public class UsAppProductCategoryNodeDto
/// 分类显示名;空为「无」
public string Name { get; set; } = string.Empty;
+ /// 按钮展示文案;为空时客户端可回退使用 Name
+ public string? DisplayText { get; set; }
+
+ /// 按钮外观:TEXT / COLOR / IMAGE(COLOR/IMAGE 的展示值在 categoryPhotoUrl)
+ public string ButtonAppearance { get; set; } = "TEXT";
+
+ /// 门店可用范围:ALL / SPECIFIED(本树已按当前门店过滤)
+ public string AvailabilityType { get; set; } = "ALL";
+
+ /// 排序号(来自 fl_product_category;未归类为较大值以排在后)
+ public int OrderNum { get; set; }
+
public int ItemCount { get; set; }
public List Products { get; set; } = new();
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IAuthSessionAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IAuthSessionAppService.cs
new file mode 100644
index 0000000..a033cd9
--- /dev/null
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IAuthSessionAppService.cs
@@ -0,0 +1,33 @@
+using FoodLabeling.Application.Contracts.Dtos.AuthSession;
+using Volo.Abp.Application.Services;
+
+namespace FoodLabeling.Application.Contracts.IServices;
+
+///
+/// 当前登录会话:菜单权限与退出(美国版 Web 管理端)
+///
+public interface IAuthSessionAppService : IApplicationService
+{
+ ///
+ /// 获取当前登录用户的角色编码、权限码与可见菜单树
+ ///
+ ///
+ /// 与框架 UserManager.GetInfoAsync 一致;用户名为 admin 时返回全部未删除菜单(与 AccountService.GetVue3Router 行为对齐)。
+ ///
+ /// 用户简要信息、权限码与菜单树
+ /// 成功
+ /// 未登录或令牌无效
+ /// 服务器错误
+ Task GetMyMenusAsync();
+
+ ///
+ /// 退出登录:清除服务端用户信息缓存(JWT 仍由前端丢弃)
+ ///
+ ///
+ /// 与框架 AccountService.PostLogout 一致;未登录时返回 false。
+ ///
+ /// 是否执行了缓存清理(已登录为 true)
+ /// 成功
+ /// 服务器错误
+ Task LogoutAsync();
+}
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IUsAppLabelingAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IUsAppLabelingAppService.cs
index ccde16d..781adcd 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IUsAppLabelingAppService.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/IUsAppLabelingAppService.cs
@@ -1,6 +1,5 @@
using FoodLabeling.Application.Contracts.Dtos.Common;
using FoodLabeling.Application.Contracts.Dtos.UsAppLabeling;
-using FoodLabeling.Application.Contracts.Dtos.Common;
using Volo.Abp.Application.Services;
namespace FoodLabeling.Application.Contracts.IServices;
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/AuthSessionAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/AuthSessionAppService.cs
new file mode 100644
index 0000000..7507726
--- /dev/null
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/AuthSessionAppService.cs
@@ -0,0 +1,213 @@
+using FoodLabeling.Application.Contracts.Dtos.AuthSession;
+using FoodLabeling.Application.Contracts.IServices;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Volo.Abp;
+using Volo.Abp.Application.Services;
+using Volo.Abp.Caching;
+using FoodLabeling.Application.Services.DbModels;
+using Yi.Framework.Rbac.Domain.Entities;
+using Yi.Framework.Rbac.Domain.Shared.Caches;
+using Yi.Framework.Rbac.Domain.Shared.Consts;
+using Yi.Framework.SqlSugarCore.Abstractions;
+
+namespace FoodLabeling.Application.Services;
+
+///
+/// 当前登录会话:菜单权限与退出
+///
+[Authorize]
+public class AuthSessionAppService : ApplicationService, IAuthSessionAppService
+{
+ private readonly IDistributedCache _userCache;
+ private readonly ISqlSugarDbContext _dbContext;
+ private readonly ISqlSugarRepository _userRepository;
+
+ public AuthSessionAppService(
+ ISqlSugarDbContext dbContext,
+ ISqlSugarRepository userRepository,
+ IDistributedCache userCache)
+ {
+ _dbContext = dbContext;
+ _userRepository = userRepository;
+ _userCache = userCache;
+ }
+
+ ///
+ public virtual async Task GetMyMenusAsync()
+ {
+ if (!CurrentUser.Id.HasValue)
+ {
+ throw new UserFriendlyException("用户未登录");
+ }
+
+ // 避免走 UserManager.GetInfoAsync -> UserRepository.GetUserAllInfoAsync 的导航加载
+ // 这里直接按 UserRole/RoleMenu/Menu 表关联查询当前用户可见菜单与权限码
+ var userId = CurrentUser.Id.Value;
+ var user = await _userRepository.GetByIdAsync(userId);
+ if (user is null || user.IsDeleted)
+ {
+ throw new UserFriendlyException("用户不存在");
+ }
+
+ List menus;
+ if (UserConst.Admin.Equals(user.UserName))
+ {
+ // MenuAggregateRoot(ParentId 为 Guid) 无法兼容 menu.ParentId=0/字符串:这里统一用 MenuDbEntity
+ menus = await _dbContext.SqlSugarClient.Queryable()
+ .Where(x => x.IsDeleted == false)
+ .ToListAsync();
+ }
+ else
+ {
+ var roleIds = await _dbContext.SqlSugarClient.Queryable()
+ .Where(x => x.UserId == userId)
+ .Select(x => x.RoleId)
+ .ToListAsync();
+
+ var roleIdStrs = roleIds.Select(x => x.ToString()).Distinct().ToList();
+ if (roleIdStrs.Count == 0)
+ {
+ menus = new List();
+ }
+ else
+ {
+ var menuIds = await _dbContext.SqlSugarClient.Queryable()
+ .Where(x => roleIdStrs.Contains(x.RoleId))
+ .Select(x => x.MenuId)
+ .Distinct()
+ .ToListAsync();
+
+ menus = menuIds.Count == 0
+ ? new List()
+ : await _dbContext.SqlSugarClient.Queryable()
+ .Where(x => x.IsDeleted == false && menuIds.Contains(x.Id))
+ .ToListAsync();
+ }
+ }
+
+ var menuNodes = menus
+ .Select(MapToNode)
+ .OrderByDescending(x => x.OrderNum)
+ .ThenBy(x => x.MenuName)
+ .ToList();
+
+ // 注意:查询 RoleAggregateRoot 会触发 YiRbacDbContext 的 IDataPermission 过滤,
+ // 其表达式包含 roleInfo.Select(...).Contains(...),在当前 SqlSugar 版本下会报“不支持 Select”。
+ // 这里直接使用 JWT 中的角色码(CurrentUser.Roles)返回,避免触发过滤器。
+ var roleCodes = CurrentUser.Roles?.ToList() ?? new List();
+
+ var permissionCodes = menuNodes
+ .Where(x => !string.IsNullOrWhiteSpace(x.PermissionCode))
+ .Select(x => x.PermissionCode!.Trim())
+ .Distinct()
+ .OrderBy(x => x)
+ .ToList();
+
+ return new CurrentUserMenuPermissionsOutputDto
+ {
+ User = new CurrentUserBriefDto
+ {
+ Id = user.Id,
+ UserName = user.UserName,
+ Nick = user.Nick,
+ Email = user.Email,
+ Icon = user.Icon
+ },
+ RoleCodes = roleCodes.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()).Distinct().OrderBy(x => x).ToList(),
+ PermissionCodes = permissionCodes,
+ Menus = BuildMenuTree(menuNodes)
+ };
+ }
+
+ ///
+ [HttpPost]
+ public virtual async Task LogoutAsync()
+ {
+ if (!CurrentUser.Id.HasValue)
+ {
+ return false;
+ }
+
+ await _userCache.RemoveAsync(new UserInfoCacheKey(CurrentUser.Id.Value));
+ return true;
+ }
+
+ private static List BuildMenuTree(List flat)
+ {
+ var nodes = flat
+ .GroupBy(x => x.Id)
+ .Select(g => g.First())
+ .ToList();
+ var byId = nodes.ToDictionary(n => n.Id, n => n);
+
+ foreach (var n in nodes)
+ {
+ n.Children = new List();
+ }
+
+ var roots = new List();
+ foreach (var n in nodes)
+ {
+ var pid = string.IsNullOrWhiteSpace(n.ParentId) ? "0" : n.ParentId.Trim();
+ if (pid == "0" || pid == "00000000-0000-0000-0000-000000000000")
+ {
+ roots.Add(n);
+ continue;
+ }
+
+ if (byId.TryGetValue(pid, out var parent))
+ {
+ parent.Children.Add(n);
+ }
+ else
+ {
+ roots.Add(n);
+ }
+ }
+
+ SortMenuTree(roots);
+ return roots;
+ }
+
+ private static CurrentUserMenuNodeDto MapToNode(MenuDbEntity m)
+ {
+ return new CurrentUserMenuNodeDto
+ {
+ Id = m.Id,
+ ParentId = string.IsNullOrWhiteSpace(m.ParentId) ? "0" : m.ParentId.Trim(),
+ MenuName = m.MenuName ?? string.Empty,
+ RouterName = m.RouterName,
+ Router = m.Router,
+ PermissionCode = m.PermissionCode,
+ MenuType = m.MenuType,
+ MenuSource = m.MenuSource,
+ OrderNum = m.OrderNum,
+ State = m.State,
+ MenuIcon = m.MenuIcon,
+ Component = m.Component,
+ IsLink = m.IsLink,
+ IsCache = m.IsCache,
+ IsShow = m.IsShow,
+ Query = m.Query,
+ Remark = m.Remark
+ };
+ }
+
+ private static void SortMenuTree(List level)
+ {
+ level.Sort((a, b) =>
+ {
+ var o = b.OrderNum.CompareTo(a.OrderNum);
+ return o != 0 ? o : string.Compare(a.MenuName, b.MenuName, StringComparison.Ordinal);
+ });
+
+ foreach (var n in level)
+ {
+ if (n.Children.Count > 0)
+ {
+ SortMenuTree(n.Children);
+ }
+ }
+ }
+}
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelCategoryDbEntity.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelCategoryDbEntity.cs
index d70b724..b89ab82 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelCategoryDbEntity.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelCategoryDbEntity.cs
@@ -24,10 +24,28 @@ public class FlLabelCategoryDbEntity
public string CategoryName { get; set; } = string.Empty;
+ ///
+ /// 按钮展示文案(为空则默认使用 CategoryName)
+ ///
+ public string? DisplayText { get; set; }
+
+ ///
+ /// 分类图/展示值:TEXT 可为图或空;COLOR 存色值(如 #409EFF);IMAGE 存图片 URL(与 ButtonAppearance 配合)
+ ///
public string? CategoryPhotoUrl { get; set; }
public int OrderNum { get; set; }
public bool State { get; set; }
+
+ ///
+ /// 按钮外观:TEXT / COLOR / IMAGE(展示数据见 CategoryPhotoUrl)
+ ///
+ public string ButtonAppearance { get; set; } = "TEXT";
+
+ ///
+ /// 门店可用范围:ALL / SPECIFIED
+ ///
+ public string AvailabilityType { get; set; } = "ALL";
}
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelCategoryLocationDbEntity.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelCategoryLocationDbEntity.cs
new file mode 100644
index 0000000..ae9b896
--- /dev/null
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelCategoryLocationDbEntity.cs
@@ -0,0 +1,22 @@
+using SqlSugar;
+
+namespace FoodLabeling.Application.Services.DbModels;
+
+///
+/// 标签分类可用门店关联(对应表:fl_label_category_location)
+///
+[SugarTable("fl_label_category_location")]
+public class FlLabelCategoryLocationDbEntity
+{
+ [SugarColumn(IsPrimaryKey = true)]
+ public string Id { get; set; } = string.Empty;
+
+ public string CategoryId { get; set; } = string.Empty;
+
+ public string LocationId { get; set; } = string.Empty;
+
+ public DateTime CreationTime { get; set; }
+
+ public string? CreatorId { get; set; }
+}
+
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelDbEntity.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelDbEntity.cs
index 3199335..23f6f20 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelDbEntity.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLabelDbEntity.cs
@@ -20,7 +20,7 @@ public class FlLabelDbEntity
public string ConcurrencyStamp { get; set; } = string.Empty;
- public string LabelCode { get; set; } = string.Empty;
+ public string? LabelCode { get; set; }
public string LabelName { get; set; } = string.Empty;
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlProductCategoryDbEntity.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlProductCategoryDbEntity.cs
index 1f51a75..2852756 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlProductCategoryDbEntity.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlProductCategoryDbEntity.cs
@@ -24,10 +24,28 @@ public class FlProductCategoryDbEntity
public string CategoryName { get; set; } = string.Empty;
+ ///
+ /// 按钮展示文案(为空则默认使用 CategoryName)
+ ///
+ public string? DisplayText { get; set; }
+
+ ///
+ /// 分类图/展示值:TEXT 可为图或空;COLOR 存色值;IMAGE 存图片 URL(与 ButtonAppearance 配合)
+ ///
public string? CategoryPhotoUrl { get; set; }
+ ///
+ /// 按钮外观:TEXT / COLOR / IMAGE(展示数据见 CategoryPhotoUrl)
+ ///
+ public string ButtonAppearance { get; set; } = "TEXT";
+
public bool State { get; set; }
+ ///
+ /// 门店可用范围:ALL / SPECIFIED
+ ///
+ public string AvailabilityType { get; set; } = "ALL";
+
public int OrderNum { get; set; }
}
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlProductCategoryLocationDbEntity.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlProductCategoryLocationDbEntity.cs
new file mode 100644
index 0000000..c6a6795
--- /dev/null
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlProductCategoryLocationDbEntity.cs
@@ -0,0 +1,22 @@
+using SqlSugar;
+
+namespace FoodLabeling.Application.Services.DbModels;
+
+///
+/// 产品类别可用门店关联(对应表:fl_product_category_location)
+///
+[SugarTable("fl_product_category_location")]
+public class FlProductCategoryLocationDbEntity
+{
+ [SugarColumn(IsPrimaryKey = true)]
+ public string Id { get; set; } = string.Empty;
+
+ public string CategoryId { get; set; } = string.Empty;
+
+ public string LocationId { get; set; } = string.Empty;
+
+ public DateTime CreationTime { get; set; }
+
+ public string? CreatorId { get; set; }
+}
+
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelAppService.cs
index f17c480..64fbea7 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelAppService.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelAppService.cs
@@ -261,7 +261,7 @@ public class LabelAppService : ApplicationService, ILabelAppService
return new LabelGetOutputDto
{
- Id = label.LabelCode,
+ Id = label.LabelCode ?? string.Empty,
LabelName = label.LabelName,
LocationId = label.LocationId ?? string.Empty,
LocationName = location?.LocationName ?? location?.LocationCode ?? "无",
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelCategoryAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelCategoryAppService.cs
index 52cab93..827d7cc 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelCategoryAppService.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LabelCategoryAppService.cs
@@ -30,12 +30,34 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
var query = _dbContext.SqlSugarClient.Queryable()
.Where(x => !x.IsDeleted)
.WhereIF(!string.IsNullOrWhiteSpace(keyword),
- x => x.CategoryCode.Contains(keyword!) || x.CategoryName.Contains(keyword!))
+ x => x.CategoryCode.Contains(keyword!) || x.CategoryName.Contains(keyword!) ||
+ (x.DisplayText != null && x.DisplayText.Contains(keyword!)))
.WhereIF(input.State != null, x => x.State == input.State);
+ // Sorting 仅允许白名单字段,避免 Unknown column/注入风险
if (!string.IsNullOrWhiteSpace(input.Sorting))
{
- query = query.OrderBy(input.Sorting);
+ var sorting = input.Sorting.Trim();
+ if (sorting.Equals("OrderNum desc", StringComparison.OrdinalIgnoreCase))
+ {
+ query = query.OrderByDescending(x => x.OrderNum);
+ }
+ else if (sorting.Equals("OrderNum asc", StringComparison.OrdinalIgnoreCase))
+ {
+ query = query.OrderBy(x => x.OrderNum);
+ }
+ else if (sorting.Equals("CreationTime desc", StringComparison.OrdinalIgnoreCase))
+ {
+ query = query.OrderByDescending(x => x.CreationTime);
+ }
+ else if (sorting.Equals("CreationTime asc", StringComparison.OrdinalIgnoreCase))
+ {
+ query = query.OrderBy(x => x.CreationTime);
+ }
+ else
+ {
+ query = query.OrderByDescending(x => x.OrderNum).OrderByDescending(x => x.CreationTime);
+ }
}
else
{
@@ -58,8 +80,11 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
Id = x.Id,
CategoryCode = x.CategoryCode,
CategoryName = x.CategoryName,
+ DisplayText = x.DisplayText,
CategoryPhotoUrl = x.CategoryPhotoUrl,
State = x.State,
+ ButtonAppearance = x.ButtonAppearance,
+ AvailabilityType = x.AvailabilityType,
OrderNum = x.OrderNum,
NoOfLabels = countMap.TryGetValue(x.Id, out var count) ? count : 0,
LastEdited = x.LastModificationTime ?? x.CreationTime
@@ -77,7 +102,17 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
throw new UserFriendlyException("标签分类不存在");
}
- return MapToGetOutput(entity);
+ var dto = MapToGetOutput(entity);
+ if (string.Equals(entity.AvailabilityType, "SPECIFIED", StringComparison.OrdinalIgnoreCase))
+ {
+ var locationIds = await _dbContext.SqlSugarClient.Queryable()
+ .Where(x => x.CategoryId == entity.Id)
+ .Select(x => x.LocationId)
+ .ToListAsync();
+ dto.LocationIds = locationIds?.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()).Distinct().ToList() ?? new();
+ }
+
+ return dto;
}
public async Task CreateAsync(LabelCategoryCreateInputVo input)
@@ -89,6 +124,13 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
throw new UserFriendlyException("分类编码和名称不能为空");
}
+ var displayText = input.DisplayText?.Trim();
+ var appearance = (input.ButtonAppearance ?? "TEXT").Trim().ToUpperInvariant();
+ var availabilityType = (input.AvailabilityType ?? "ALL").Trim().ToUpperInvariant();
+ ValidateButtonAppearance(appearance);
+ var locationIds = NormalizeLocationIds(input.LocationIds);
+ ValidateAvailabilityTypeAndLocations(availabilityType, locationIds);
+
var duplicated = await _dbContext.SqlSugarClient.Queryable()
.AnyAsync(x => !x.IsDeleted && (x.CategoryCode == code || x.CategoryName == name));
if (duplicated)
@@ -96,17 +138,23 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
throw new UserFriendlyException("分类编码或名称已存在");
}
+ var now = DateTime.Now;
+ var currentUserId = CurrentUser?.Id?.ToString();
var entity = new FlLabelCategoryDbEntity
{
Id = _guidGenerator.Create().ToString(),
CategoryCode = code,
CategoryName = name,
+ DisplayText = string.IsNullOrWhiteSpace(displayText) ? null : displayText,
CategoryPhotoUrl = input.CategoryPhotoUrl?.Trim(),
State = input.State,
+ ButtonAppearance = appearance,
+ AvailabilityType = availabilityType,
OrderNum = input.OrderNum
};
await _dbContext.SqlSugarClient.Insertable(entity).ExecuteCommandAsync();
+ await SaveCategoryLocationsAsync(entity.Id, availabilityType, locationIds, currentUserId, now);
return await GetAsync(entity.Id);
}
@@ -126,6 +174,13 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
throw new UserFriendlyException("分类编码和名称不能为空");
}
+ var displayText = input.DisplayText?.Trim();
+ var appearance = (input.ButtonAppearance ?? "TEXT").Trim().ToUpperInvariant();
+ var availabilityType = (input.AvailabilityType ?? "ALL").Trim().ToUpperInvariant();
+ ValidateButtonAppearance(appearance);
+ var locationIds = NormalizeLocationIds(input.LocationIds);
+ ValidateAvailabilityTypeAndLocations(availabilityType, locationIds);
+
var duplicated = await _dbContext.SqlSugarClient.Queryable()
.AnyAsync(x => !x.IsDeleted && x.Id != id && (x.CategoryCode == code || x.CategoryName == name));
if (duplicated)
@@ -135,13 +190,17 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
entity.CategoryCode = code;
entity.CategoryName = name;
+ entity.DisplayText = string.IsNullOrWhiteSpace(displayText) ? null : displayText;
entity.CategoryPhotoUrl = input.CategoryPhotoUrl?.Trim();
entity.State = input.State;
+ entity.ButtonAppearance = appearance;
+ entity.AvailabilityType = availabilityType;
entity.OrderNum = input.OrderNum;
entity.LastModificationTime = DateTime.Now;
entity.LastModifierId = CurrentUser?.Id?.ToString();
await _dbContext.SqlSugarClient.Updateable(entity).ExecuteCommandAsync();
+ await SaveCategoryLocationsAsync(entity.Id, availabilityType, locationIds, entity.LastModifierId, entity.LastModificationTime ?? DateTime.Now);
return await GetAsync(id);
}
@@ -174,12 +233,78 @@ public class LabelCategoryAppService : ApplicationService, ILabelCategoryAppServ
Id = x.Id,
CategoryCode = x.CategoryCode,
CategoryName = x.CategoryName,
+ DisplayText = x.DisplayText,
CategoryPhotoUrl = x.CategoryPhotoUrl,
State = x.State,
+ ButtonAppearance = x.ButtonAppearance,
+ AvailabilityType = x.AvailabilityType,
OrderNum = x.OrderNum
};
}
+ private static void ValidateAvailabilityTypeAndLocations(string availabilityType, List locationIds)
+ {
+ if (availabilityType != "ALL" && availabilityType != "SPECIFIED")
+ {
+ throw new UserFriendlyException("门店可用范围不合法(ALL/SPECIFIED)");
+ }
+
+ if (availabilityType == "SPECIFIED" && locationIds.Count == 0)
+ {
+ throw new UserFriendlyException("指定门店范围时必须至少选择一个门店");
+ }
+ }
+
+ private static List NormalizeLocationIds(List? locationIds)
+ {
+ return locationIds?
+ .Where(x => !string.IsNullOrWhiteSpace(x))
+ .Select(x => x.Trim())
+ .Distinct()
+ .ToList() ?? new();
+ }
+
+ private static void ValidateButtonAppearance(string appearance)
+ {
+ if (appearance != "TEXT" && appearance != "COLOR" && appearance != "IMAGE")
+ {
+ throw new UserFriendlyException("按钮外观不合法(TEXT/COLOR/IMAGE)");
+ }
+ }
+
+ private async Task SaveCategoryLocationsAsync(
+ string categoryId,
+ string availabilityType,
+ List locationIds,
+ string? currentUserId,
+ DateTime now)
+ {
+ await _dbContext.SqlSugarClient.Deleteable()
+ .Where(x => x.CategoryId == categoryId)
+ .ExecuteCommandAsync();
+
+ if (availabilityType != "SPECIFIED")
+ {
+ return;
+ }
+
+ if (locationIds.Count == 0)
+ {
+ return;
+ }
+
+ var rows = locationIds.Select(locId => new FlLabelCategoryLocationDbEntity
+ {
+ Id = _guidGenerator.Create().ToString(),
+ CategoryId = categoryId,
+ LocationId = locId,
+ CreationTime = now,
+ CreatorId = currentUserId
+ }).ToList();
+
+ await _dbContext.SqlSugarClient.Insertable(rows).ExecuteCommandAsync();
+ }
+
private static PagedResultWithPageDto BuildPagedResult(int skipCount, int maxResultCount, int total, List items)
{
var pageSize = maxResultCount <= 0 ? items.Count : maxResultCount;
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/ProductCategoryAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/ProductCategoryAppService.cs
index 3a6fdf6..9af1412 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/ProductCategoryAppService.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/ProductCategoryAppService.cs
@@ -35,7 +35,8 @@ public class ProductCategoryAppService : ApplicationService, IProductCategoryApp
var query = _dbContext.SqlSugarClient.Queryable()
.Where(x => !x.IsDeleted)
.WhereIF(!string.IsNullOrWhiteSpace(keyword),
- x => x.CategoryCode.Contains(keyword!) || x.CategoryName.Contains(keyword!))
+ x => x.CategoryCode.Contains(keyword!) || x.CategoryName.Contains(keyword!) ||
+ (x.DisplayText != null && x.DisplayText.Contains(keyword!)))
.WhereIF(input.State != null, x => x.State == input.State);
// Sorting 仅允许白名单字段,避免不同数据库列命名导致 Unknown column
@@ -77,8 +78,11 @@ public class ProductCategoryAppService : ApplicationService, IProductCategoryApp
Id = x.Id,
CategoryCode = x.CategoryCode,
CategoryName = x.CategoryName,
+ DisplayText = x.DisplayText,
CategoryPhotoUrl = x.CategoryPhotoUrl,
+ ButtonAppearance = x.ButtonAppearance,
State = x.State,
+ AvailabilityType = x.AvailabilityType,
OrderNum = x.OrderNum,
LastEdited = x.LastModificationTime ?? x.CreationTime
}).ToList();
@@ -98,7 +102,17 @@ public class ProductCategoryAppService : ApplicationService, IProductCategoryApp
throw new UserFriendlyException("类别不存在");
}
- return MapToGetOutput(entity);
+ var dto = MapToGetOutput(entity);
+ if (string.Equals(entity.AvailabilityType, "SPECIFIED", StringComparison.OrdinalIgnoreCase))
+ {
+ var locationIds = await _dbContext.SqlSugarClient.Queryable()
+ .Where(x => x.CategoryId == entity.Id)
+ .Select(x => x.LocationId)
+ .ToListAsync();
+ dto.LocationIds = locationIds?.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()).Distinct().ToList() ?? new();
+ }
+
+ return dto;
}
///
@@ -113,6 +127,13 @@ public class ProductCategoryAppService : ApplicationService, IProductCategoryApp
throw new UserFriendlyException("类别编码和名称不能为空");
}
+ var displayText = input.DisplayText?.Trim();
+ var appearance = (input.ButtonAppearance ?? "TEXT").Trim().ToUpperInvariant();
+ var availabilityType = (input.AvailabilityType ?? "ALL").Trim().ToUpperInvariant();
+ ValidateButtonAppearance(appearance);
+ var locationIds = NormalizeLocationIds(input.LocationIds);
+ ValidateAvailabilityTypeAndLocations(availabilityType, locationIds);
+
var duplicated = await _dbContext.SqlSugarClient.Queryable()
.AnyAsync(x => !x.IsDeleted && (x.CategoryCode == code || x.CategoryName == name));
if (duplicated)
@@ -133,12 +154,16 @@ public class ProductCategoryAppService : ApplicationService, IProductCategoryApp
ConcurrencyStamp = _guidGenerator.Create().ToString("N"),
CategoryCode = code,
CategoryName = name,
+ DisplayText = string.IsNullOrWhiteSpace(displayText) ? null : displayText,
CategoryPhotoUrl = input.CategoryPhotoUrl?.Trim(),
+ ButtonAppearance = appearance,
State = input.State,
+ AvailabilityType = availabilityType,
OrderNum = input.OrderNum
};
await _dbContext.SqlSugarClient.Insertable(entity).ExecuteCommandAsync();
+ await SaveCategoryLocationsAsync(entity.Id, availabilityType, locationIds, currentUserId, now);
return await GetAsync(entity.Id);
}
@@ -161,6 +186,13 @@ public class ProductCategoryAppService : ApplicationService, IProductCategoryApp
throw new UserFriendlyException("类别编码和名称不能为空");
}
+ var displayText = input.DisplayText?.Trim();
+ var appearance = (input.ButtonAppearance ?? "TEXT").Trim().ToUpperInvariant();
+ var availabilityType = (input.AvailabilityType ?? "ALL").Trim().ToUpperInvariant();
+ ValidateButtonAppearance(appearance);
+ var locationIds = NormalizeLocationIds(input.LocationIds);
+ ValidateAvailabilityTypeAndLocations(availabilityType, locationIds);
+
var duplicated = await _dbContext.SqlSugarClient.Queryable()
.AnyAsync(x => !x.IsDeleted && x.Id != id && (x.CategoryCode == code || x.CategoryName == name));
if (duplicated)
@@ -170,13 +202,17 @@ public class ProductCategoryAppService : ApplicationService, IProductCategoryApp
entity.CategoryCode = code;
entity.CategoryName = name;
+ entity.DisplayText = string.IsNullOrWhiteSpace(displayText) ? null : displayText;
entity.CategoryPhotoUrl = input.CategoryPhotoUrl?.Trim();
+ entity.ButtonAppearance = appearance;
entity.State = input.State;
+ entity.AvailabilityType = availabilityType;
entity.OrderNum = input.OrderNum;
entity.LastModificationTime = DateTime.Now;
entity.LastModifierId = CurrentUser?.Id?.ToString();
await _dbContext.SqlSugarClient.Updateable(entity).ExecuteCommandAsync();
+ await SaveCategoryLocationsAsync(entity.Id, availabilityType, locationIds, entity.LastModifierId, entity.LastModificationTime ?? DateTime.Now);
return await GetAsync(id);
}
@@ -213,12 +249,78 @@ public class ProductCategoryAppService : ApplicationService, IProductCategoryApp
Id = x.Id,
CategoryCode = x.CategoryCode,
CategoryName = x.CategoryName,
+ DisplayText = x.DisplayText,
CategoryPhotoUrl = x.CategoryPhotoUrl,
+ ButtonAppearance = x.ButtonAppearance,
State = x.State,
+ AvailabilityType = x.AvailabilityType,
OrderNum = x.OrderNum
};
}
+ private static void ValidateAvailabilityTypeAndLocations(string availabilityType, List locationIds)
+ {
+ if (availabilityType != "ALL" && availabilityType != "SPECIFIED")
+ {
+ throw new UserFriendlyException("门店可用范围不合法(ALL/SPECIFIED)");
+ }
+
+ if (availabilityType == "SPECIFIED" && locationIds.Count == 0)
+ {
+ throw new UserFriendlyException("指定门店范围时必须至少选择一个门店");
+ }
+ }
+
+ private static List NormalizeLocationIds(List? locationIds)
+ {
+ return locationIds?
+ .Where(x => !string.IsNullOrWhiteSpace(x))
+ .Select(x => x.Trim())
+ .Distinct()
+ .ToList() ?? new();
+ }
+
+ private static void ValidateButtonAppearance(string appearance)
+ {
+ if (appearance != "TEXT" && appearance != "COLOR" && appearance != "IMAGE")
+ {
+ throw new UserFriendlyException("按钮外观不合法(TEXT/COLOR/IMAGE)");
+ }
+ }
+
+ private async Task SaveCategoryLocationsAsync(
+ string categoryId,
+ string availabilityType,
+ List locationIds,
+ string? currentUserId,
+ DateTime now)
+ {
+ await _dbContext.SqlSugarClient.Deleteable()
+ .Where(x => x.CategoryId == categoryId)
+ .ExecuteCommandAsync();
+
+ if (availabilityType != "SPECIFIED")
+ {
+ return;
+ }
+
+ if (locationIds.Count == 0)
+ {
+ return;
+ }
+
+ var rows = locationIds.Select(locId => new FlProductCategoryLocationDbEntity
+ {
+ Id = _guidGenerator.Create().ToString(),
+ CategoryId = categoryId,
+ LocationId = locId,
+ CreationTime = now,
+ CreatorId = currentUserId
+ }).ToList();
+
+ await _dbContext.SqlSugarClient.Insertable(rows).ExecuteCommandAsync();
+ }
+
private static PagedResultWithPageDto BuildPagedResult(int skipCount, int maxResultCount, int total, List items)
{
var pageSize = maxResultCount <= 0 ? items.Count : maxResultCount;
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/RbacMenuAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/RbacMenuAppService.cs
index e23abcd..66868ba 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/RbacMenuAppService.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/RbacMenuAppService.cs
@@ -38,6 +38,8 @@ public class RbacMenuAppService : ApplicationService, IRbacMenuAppService
Id = x.Id,
ParentId = x.ParentId,
MenuName = x.MenuName ?? string.Empty,
+ RouterName = x.RouterName,
+ Router = x.Router,
PermissionCode = x.PermissionCode,
MenuType = x.MenuType,
MenuSource = x.MenuSource,
@@ -62,6 +64,8 @@ public class RbacMenuAppService : ApplicationService, IRbacMenuAppService
Id = entity.Id,
ParentId = entity.ParentId,
MenuName = entity.MenuName ?? string.Empty,
+ RouterName = entity.RouterName,
+ Router = entity.Router,
PermissionCode = entity.PermissionCode,
MenuType = entity.MenuType,
MenuSource = entity.MenuSource,
diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/UsAppLabelingAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/UsAppLabelingAppService.cs
index 1a89a0d..bb294a3 100644
--- a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/UsAppLabelingAppService.cs
+++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/UsAppLabelingAppService.cs
@@ -50,8 +50,11 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
/// 获取当前门店下四级嵌套数据
///
///
- /// L1 标签分类 fl_label_category;L2 产品分类 fl_product.CategoryId join fl_product_category;
+ /// L1 标签分类 fl_label_category(含 buttonAppearance;COLOR/IMAGE 展示值在 categoryPhotoUrl);仅对当前门店可用:ALL 或 SPECIFIED 且在 fl_label_category_location;
+ /// L2 产品分类 fl_product.CategoryId join fl_product_category(同上,展示值在 categoryPhotoUrl);
/// L3 产品;L4 与该门店、该标签分类、该产品关联的标签实例(fl_label + fl_label_type)。
+ /// L2 仅包含对当前门店可用的类别:AvailabilityType=ALL,或 SPECIFIED 且在 fl_product_category_location 存在该门店记录;
+ /// 未归类或分类行未关联到 fl_product_category 时仍归入「无」节点。
///
[Authorize]
public virtual async Task> GetLabelingTreeAsync(UsAppLabelingTreeInputVo input)
@@ -83,10 +86,15 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
LabelCategoryId = c.Id,
LabelCategoryName = c.CategoryName,
LabelCategoryPhotoUrl = c.CategoryPhotoUrl,
+ LabelCategoryButtonAppearance = c.ButtonAppearance,
LabelCategoryOrderNum = c.OrderNum,
ProductCategoryId = p.CategoryId,
ProductCategoryName = pc.CategoryName,
ProductCategoryPhotoUrl = pc.CategoryPhotoUrl,
+ ProductCategoryDisplayText = pc.DisplayText,
+ ProductCategoryButtonAppearance = pc.ButtonAppearance,
+ ProductCategoryAvailabilityType = pc.AvailabilityType,
+ ProductCategoryOrderNum = pc.OrderNum,
ProductId = p.Id,
ProductName = p.ProductName,
ProductCode = p.ProductCode,
@@ -112,17 +120,22 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
x.LabelCategoryId,
x.LabelCategoryName,
x.LabelCategoryPhotoUrl,
+ x.LabelCategoryButtonAppearance,
x.LabelCategoryOrderNum
}).OrderBy(g => g.Key.LabelCategoryOrderNum).ThenBy(g => g.Key.LabelCategoryName);
var result = new List();
foreach (var g1 in byL1)
{
+ var l1Appearance = string.IsNullOrWhiteSpace(g1.Key.LabelCategoryButtonAppearance)
+ ? "TEXT"
+ : g1.Key.LabelCategoryButtonAppearance.Trim().ToUpperInvariant();
var l1 = new UsAppLabelCategoryTreeNodeDto
{
Id = g1.Key.LabelCategoryId,
CategoryName = g1.Key.LabelCategoryName ?? string.Empty,
CategoryPhotoUrl = g1.Key.LabelCategoryPhotoUrl,
+ ButtonAppearance = l1Appearance,
OrderNum = g1.Key.LabelCategoryOrderNum,
ProductCategories = new List()
};
@@ -136,7 +149,11 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
{
CategoryId = (string?)null,
CategoryName = "无",
- CategoryPhotoUrl = (string?)null
+ CategoryPhotoUrl = (string?)null,
+ DisplayText = (string?)null,
+ ButtonAppearance = (string?)null,
+ AvailabilityType = (string?)null,
+ CategoryOrderNum = int.MaxValue
};
}
@@ -146,19 +163,34 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
{
CategoryId = (string?)categoryId,
CategoryName = categoryName,
- CategoryPhotoUrl = categoryPhotoUrl
+ CategoryPhotoUrl = categoryPhotoUrl,
+ DisplayText = NormalizeNullableUrl(x.ProductCategoryDisplayText),
+ ButtonAppearance = NormalizeNullableId(x.ProductCategoryButtonAppearance),
+ AvailabilityType = NormalizeNullableId(x.ProductCategoryAvailabilityType),
+ CategoryOrderNum = x.ProductCategoryOrderNum
};
})
- .OrderBy(g => g.Key.CategoryName);
+ .OrderBy(g => g.Key.CategoryOrderNum)
+ .ThenBy(g => g.Key.CategoryName);
foreach (var g2 in byL2)
{
var productsGrouped = g2.GroupBy(x => x.ProductId).OrderBy(pg => pg.First().ProductName);
+ var appearance = string.IsNullOrWhiteSpace(g2.Key.ButtonAppearance)
+ ? "TEXT"
+ : g2.Key.ButtonAppearance.Trim().ToUpperInvariant();
+ var availability = string.IsNullOrWhiteSpace(g2.Key.AvailabilityType)
+ ? "ALL"
+ : g2.Key.AvailabilityType.Trim().ToUpperInvariant();
var l2 = new UsAppProductCategoryNodeDto
{
CategoryId = g2.Key.CategoryId,
CategoryPhotoUrl = g2.Key.CategoryPhotoUrl,
Name = g2.Key.CategoryName,
+ DisplayText = g2.Key.DisplayText,
+ ButtonAppearance = appearance,
+ AvailabilityType = availability,
+ OrderNum = g2.Key.CategoryOrderNum == int.MaxValue ? 0 : g2.Key.CategoryOrderNum,
ItemCount = productsGrouped.Count(),
Products = new List()
};
@@ -424,7 +456,7 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
}
var previewProductId = await ResolvePreviewProductIdAsync(labelRow.Id, input.ProductId);
- var normalizedPrintInput = input.PrintInputJson?.ToDictionary(x => x.Key, x => (object?)x.Value);
+ var normalizedPrintInput = ParsePrintInputJsonToDictionary(input.PrintInputJson);
// 解析模板 elements(与预览一致的渲染数据)
var resolvedTemplate = await _labelAppService.PreviewAsync(new LabelPreviewResolveInputVo
@@ -852,14 +884,29 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
.Where((lp, l, p, c, t, tpl, pc) => l.LocationId == locationId)
.Where((lp, l, p, c, t, tpl, pc) => !l.IsDeleted && l.State)
.Where((lp, l, p, c, t, tpl, pc) => !p.IsDeleted && p.State)
- .Where((lp, l, p, c, t, tpl, pc) => !c.IsDeleted && c.State)
+ .Where((lp, l, p, c, t, tpl, pc) =>
+ !c.IsDeleted && c.State &&
+ (c.AvailabilityType == "ALL" ||
+ (c.AvailabilityType == "SPECIFIED" &&
+ SqlFunc.Subqueryable()
+ .Where(loc => loc.CategoryId == c.Id && loc.LocationId == locationId)
+ .Any())))
.Where((lp, l, p, c, t, tpl, pc) => !t.IsDeleted && t.State)
.Where((lp, l, p, c, t, tpl, pc) => !tpl.IsDeleted)
+ .Where((lp, l, p, c, t, tpl, pc) =>
+ pc.Id == null ||
+ (!pc.IsDeleted && pc.State &&
+ (pc.AvailabilityType == "ALL" ||
+ (pc.AvailabilityType == "SPECIFIED" &&
+ SqlFunc.Subqueryable()
+ .Where(loc => loc.CategoryId == pc.Id && loc.LocationId == locationId)
+ .Any()))))
.WhereIF(!string.IsNullOrWhiteSpace(filterCategoryId), (lp, l, p, c, t, tpl, pc) => l.LabelCategoryId == filterCategoryId)
.WhereIF(!string.IsNullOrWhiteSpace(keyword), (lp, l, p, c, t, tpl, pc) =>
(l.LabelName != null && l.LabelName.Contains(keyword!)) ||
(p.ProductName != null && p.ProductName.Contains(keyword!)) ||
(pc.CategoryName != null && pc.CategoryName.Contains(keyword!)) ||
+ (pc.DisplayText != null && pc.DisplayText.Contains(keyword!)) ||
(c.CategoryName != null && c.CategoryName.Contains(keyword!)) ||
(t.TypeName != null && t.TypeName.Contains(keyword!)) ||
(l.LabelCode != null && l.LabelCode.Contains(keyword!)));
@@ -875,6 +922,8 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
public string? LabelCategoryPhotoUrl { get; set; }
+ public string? LabelCategoryButtonAppearance { get; set; }
+
public int LabelCategoryOrderNum { get; set; }
public string? ProductCategoryId { get; set; }
@@ -883,6 +932,14 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
public string? ProductCategoryPhotoUrl { get; set; }
+ public string? ProductCategoryDisplayText { get; set; }
+
+ public string? ProductCategoryButtonAppearance { get; set; }
+
+ public string? ProductCategoryAvailabilityType { get; set; }
+
+ public int ProductCategoryOrderNum { get; set; }
+
public string ProductId { get; set; } = string.Empty;
public string? ProductName { get; set; }
@@ -908,6 +965,32 @@ public class UsAppLabelingAppService : ApplicationService, IUsAppLabelingAppServ
public string TemplateUnit { get; set; } = "inch";
}
+ ///
+ /// 将 App 入参中的 JsonElement(对象或 null)反序列化为 PreviewAsync 所需的扁平字典。
+ ///
+ private static Dictionary? ParsePrintInputJsonToDictionary(JsonElement? printInputJson)
+ {
+ if (printInputJson is null)
+ {
+ return null;
+ }
+
+ var je = printInputJson.Value;
+ if (je.ValueKind is JsonValueKind.Null or JsonValueKind.Undefined)
+ {
+ return null;
+ }
+
+ try
+ {
+ return JsonSerializer.Deserialize>(je.GetRawText());
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
private static string NormalizeCategoryName(string? categoryName)
{
var s = categoryName?.Trim();
diff --git a/项目相关文档/本次新增与优化接口汇总.md b/项目相关文档/本次新增与优化接口汇总.md
new file mode 100644
index 0000000..a2f79e6
--- /dev/null
+++ b/项目相关文档/本次新增与优化接口汇总.md
@@ -0,0 +1,262 @@
+# 本次接口变更汇总(标签 / 产品类别 / 模板组件 / App 树 / Web 会话)
+
+> 说明:本文只汇总本次迭代中相关内容:
+> - 标签模块 Label Categories:对齐“新增类别”原型图(`buttonAppearance` + `categoryPhotoUrl` / 展示文案 / 门店范围)
+> - 产品模块 Categories:对齐“新增产品类别”原型图(同上)
+> - 模板组件(`fl_label_template_element`):新增字段 `TypeAdd`(并补齐 `ElementName`)
+> - App `labeling-tree`:L1 标签分类返回 `buttonAppearance`
+> - Web 管理端 `auth-session`:**当前用户菜单与权限**、**退出登录**(食品标签-美国版模块)
+> - Web `rbac-menu` 列表/详情:补充返回 `routerName`、`router`
+>
+> 其余标签打印相关接口不在本文范围内。
+
+---
+
+## 1. 模板组件字段(`fl_label_template_element`)
+
+### 1.1 字段变更
+
+- 新增字段:`TypeAdd`(元素附加类型,如 `label_Duration`)
+- 新增字段:`ElementName`(元素名称,用于更稳定的显示/快照)
+
+### 1.2 接口影响范围
+
+- 模板新增/编辑:保存 `elements[].typeAdd` / `elements[].elementName`
+- 模板详情/预览:返回 `elements[].typeAdd` / `elements[].elementName`
+
+### 1.3 JSON 对齐(elements[])
+
+| 前端字段 | 后端字段 | 说明 |
+|---|---|---|
+| `type` | `ElementType` | 元素类型 |
+| `typeAdd` | `TypeAdd` | 元素附加类型 |
+| `elementName` | `ElementName` | 元素名称 |
+
+---
+
+## 2. 产品模块 Categories(Products → Categories)
+
+> 数据库侧你已完成:`fl_product_category` 新字段、`fl_product_category_location` 新表。
+
+### 2.1 表结构要点
+
+- `fl_product_category`(主表)关键字段:
+ - `DisplayText`:按钮展示文案(为空可回退 `CategoryName`)
+ - `ButtonAppearance`:`TEXT/COLOR/IMAGE`(前端按类型解析)
+ - `CategoryPhotoUrl`:与 `ButtonAppearance` 配合——`COLOR` 存颜色值、`IMAGE` 存图片 URL;`TEXT` 可空或不用
+ - **已删除列**(需在库上执行 `DROP COLUMN`):`ButtonTextColor`、`ButtonBgColor`、`ButtonImageUrl`、`ButtonStyleJson`
+ - `AvailabilityType`:`ALL/SPECIFIED`(门店可用范围)
+- `fl_product_category_location`(关联表):
+ - `(CategoryId, LocationId)` 唯一约束;用于 `AvailabilityType=SPECIFIED` 指定门店
+
+### 2.2 CRUD 接口(字段扩展)
+
+接口路径不变,仅扩展字段。
+
+#### 2.2.1 列表
+
+- **方法**:`GET`
+- **路径**:`/api/app/product-category`
+- **列表行新增返回**:
+ - `displayText`
+ - `buttonAppearance`
+ - `categoryPhotoUrl`
+ - `availabilityType`
+
+#### 2.2.2 详情
+
+- **方法**:`GET`
+- **路径**:`/api/app/product-category/{id}`
+- **新增返回字段**:
+ - `displayText`
+ - `buttonAppearance`、`categoryPhotoUrl`(COLOR/IMAGE 的展示数据统一在此字段)
+ - `availabilityType`
+ - `locationIds`(当 `availabilityType=SPECIFIED` 返回门店 Id 列表,否则为空数组)
+
+#### 2.2.3 新增
+
+- **方法**:`POST`
+- **路径**:`/api/app/product-category`
+- **新增入参字段**:
+ - `displayText`
+ - `buttonAppearance`、`categoryPhotoUrl`
+ - `availabilityType`
+ - `locationIds`(当 `availabilityType=SPECIFIED` 必填且至少 1 个)
+
+#### 2.2.4 编辑
+
+- **方法**:`PUT`
+- **路径**:`/api/app/product-category/{id}`
+- **入参同新增**
+
+#### 2.2.5 删除
+
+- **方法**:`DELETE`
+- **路径**:`/api/app/product-category/{id}`
+- **说明**:逻辑删除;若被产品引用会阻止删除(保持原行为)
+
+### 2.3 后端校验规则(本次新增)
+
+- `availabilityType` 仅允许 `ALL/SPECIFIED`
+ - `SPECIFIED` 时 `locationIds` 至少 1 个
+- `buttonAppearance` 仅允许 `TEXT/COLOR/IMAGE`(接口层校验枚举;`categoryPhotoUrl` 是否必填由业务/前端约定,`COLOR/IMAGE` 时应写入该字段)
+
+---
+
+## 3. 标签模块 Label Categories(Labels → Label Categories)
+
+> 数据库侧新增:`fl_label_category` 新字段、`fl_label_category_location` 新表。
+
+### 3.1 表结构要点
+
+- `fl_label_category`(主表)关键字段:
+ - `DisplayText`:按钮展示文案(为空可回退 `CategoryName`)
+ - `ButtonAppearance`:`TEXT/COLOR/IMAGE`(前端按类型解析)
+ - `CategoryPhotoUrl`:与 `ButtonAppearance` 配合——`COLOR` 存颜色值、`IMAGE` 存图片 URL;`TEXT` 可空或不用
+ - **已删除列**(需在库上执行 `DROP COLUMN`):`ButtonTextColor`、`ButtonBgColor`、`ButtonImageUrl`、`ButtonStyleJson`
+ - `AvailabilityType`:`ALL/SPECIFIED`(门店可用范围)
+- `fl_label_category_location`(关联表):
+ - `(CategoryId, LocationId)` 唯一约束;用于 `AvailabilityType=SPECIFIED` 指定门店(`LocationId` 对应 `location` 表主键)
+
+### 3.2 CRUD 接口(字段扩展)
+
+接口路径不变,仅扩展字段。
+
+#### 3.2.1 列表
+
+- **方法**:`GET`
+- **路径**:`/api/app/label-category`
+- **列表行新增返回**:
+ - `displayText`
+ - `buttonAppearance`
+ - `categoryPhotoUrl`
+ - `availabilityType`
+
+#### 3.2.2 详情
+
+- **方法**:`GET`
+- **路径**:`/api/app/label-category/{id}`
+- **新增返回字段**:
+ - `displayText`
+ - `buttonAppearance`、`categoryPhotoUrl`(COLOR/IMAGE 的展示数据统一在此字段)
+ - `availabilityType`
+ - `locationIds`(当 `availabilityType=SPECIFIED` 返回门店 Id 列表,否则为空数组)
+
+#### 3.2.3 新增
+
+- **方法**:`POST`
+- **路径**:`/api/app/label-category`
+- **新增入参字段**:
+ - `displayText`
+ - `buttonAppearance`、`categoryPhotoUrl`
+ - `availabilityType`
+ - `locationIds`(当 `availabilityType=SPECIFIED` 必填且至少 1 个)
+
+#### 3.2.4 编辑
+
+- **方法**:`PUT`
+- **路径**:`/api/app/label-category/{id}`
+- **入参同新增**
+
+#### 3.2.5 删除
+
+- **方法**:`DELETE`
+- **路径**:`/api/app/label-category/{id}`
+- **说明**:逻辑删除;若被标签引用会阻止删除(保持原行为)
+
+### 3.3 后端校验规则(本次新增)
+
+- `availabilityType` 仅允许 `ALL/SPECIFIED`
+ - `SPECIFIED` 时 `locationIds` 至少 1 个
+- `buttonAppearance` 仅允许 `TEXT/COLOR/IMAGE`(接口层校验枚举;`categoryPhotoUrl` 是否必填由业务/前端约定,`COLOR/IMAGE` 时应写入该字段)
+
+---
+
+## 4. App 端 `GET /api/app/us-app-labeling/labeling-tree`
+
+- **L1(标签分类)节点**:除原有 `categoryName`、`categoryPhotoUrl`、`orderNum` 等外,**返回 `buttonAppearance`**(缺省或空时后端按 `TEXT` 规范化为大写)。
+- **L2(产品分类)节点**:仅 `buttonAppearance` + `categoryPhotoUrl` 承载外观数据(已不再返回 `buttonTextColor`、`buttonBgColor`、`buttonImageUrl`、`buttonStyleJson`)。
+
+### 4.1 数据库迁移(两张主表)
+
+在确认历史数据已按需迁到 `CategoryPhotoUrl` 后,可执行(列不存在时需跳过或调整):
+
+```sql
+ALTER TABLE `fl_label_category`
+ DROP COLUMN `ButtonTextColor`,
+ DROP COLUMN `ButtonBgColor`,
+ DROP COLUMN `ButtonImageUrl`,
+ DROP COLUMN `ButtonStyleJson`;
+
+ALTER TABLE `fl_product_category`
+ DROP COLUMN `ButtonTextColor`,
+ DROP COLUMN `ButtonBgColor`,
+ DROP COLUMN `ButtonImageUrl`,
+ DROP COLUMN `ButtonStyleJson`;
+```
+
+---
+
+## 5. Web 管理端会话(`AuthSession` / 食品标签-美国版)
+
+> 实现:`IAuthSessionAppService` / `AuthSessionAppService`。需携带与后台一致的 **JWT**(`Authorization: Bearer {token}`)。具体 action 路径以部署环境 **Swagger / OpenAPI** 为准;下列为 ABP 常规约定(`RootPath = api/app`)。
+
+### 5.1 获取当前登录用户菜单与权限
+
+- **方法**:`GET`
+- **路径**(约定):`/api/app/auth-session/my-menus`
+- **鉴权**:需要登录
+- **用途**:前端动态路由、侧边栏、按钮级权限(`permissionCodes`)
+- **返回体**(`CurrentUserMenuPermissionsOutputDto`,JSON 字段名为 camelCase):
+
+| 字段 | 类型 | 说明 |
+|---|---|---|
+| `user` | object | 当前用户简要信息(无密码) |
+| `user.id` | guid | 用户 Id |
+| `user.userName` | string | 登录名 |
+| `user.nick` | string? | 昵称 |
+| `user.email` | string? | 邮箱 |
+| `user.icon` | string? | 头像 |
+| `roleCodes` | string[] | 角色编码列表(已排序) |
+| `permissionCodes` | string[] | 权限码列表(已排序;超级管理员常见为 `*:*:*`) |
+| `menus` | array | **菜单树**(根节点列表,子节点在 `children`) |
+
+**菜单树节点**(`CurrentUserMenuNodeDto`)主要字段:
+
+| 字段 | 说明 |
+|---|---|
+| `id` / `parentId` | 菜单 Id、父 Id(根父级多为 `"0"`) |
+| `menuName` | 菜单名称 |
+| `routerName` / `router` | 路由名、路径 |
+| `permissionCode` | 权限标识(按钮/接口控制用) |
+| `menuType` / `menuSource` | 枚举整型值(与 `Menu` 表一致) |
+| `orderNum` / `state` | 排序、是否启用 |
+| `menuIcon` / `component` / `isLink` / `isCache` / `isShow` / `query` / `remark` | 与菜单表一致 |
+| `children` | 子节点数组 |
+
+**业务说明**:
+
+- 数据来源与框架 `UserManager.GetInfoAsync` 一致:按用户角色合并菜单与权限码。
+- 用户名为 **`admin`** 时:与 `AccountService.GetVue3Router` 对齐,返回 **`Menu` 表中未逻辑删除** 的全部菜单再组树(`permissionCodes` 仍为超级管理员约定值)。
+
+### 5.2 退出登录
+
+- **方法**:`POST`
+- **路径**(约定):`/api/app/auth-session/logout`
+- **鉴权**:需要登录(未登录或无法解析用户时返回 `false`)
+- **请求体**:无
+- **返回**:`boolean`
+ - `true`:已清除服务端 **用户信息分布式缓存**(与 `AccountService.PostLogout` 一致)
+ - `false`:当前请求未识别到用户 Id(例如未登录)
+- **说明**:JWT 为无状态令牌,**前端仍需丢弃本地 Token**;退出接口主要清理服务端缓存侧用户信息。
+
+---
+
+## 6. 权限菜单 `rbac-menu` 列表/详情补充字段
+
+- **路径**:`GET /api/app/rbac-menu`(列表)、`GET /api/app/rbac-menu/{id}`(详情)
+- **新增返回**(与 `menu` 表字段一致,JSON 一般为 camelCase):
+ - `routerName`:路由名称
+ - `router`:路由路径
+- **说明**:树接口 `GET /api/app/rbac-menu/tree`(若已使用)本身已包含完整菜单字段,无需重复改动。
+
--
libgit2 0.21.4