5-17 接口优化
本文档说明 2026-05-17 对美国版对外接口的优化,包括:
/api/app/rbac-role:accessPermissions、menuIds、orderNum等(见 rbac-role 角色管理)。/api/app/partner、/api/app/group:列表与 export-pdf 按登录 Token 数据范围过滤(见 partner / group 章节)。/api/app/location:门店新增 / 编辑 / 列表增加operatingHours(经营时间),见 location 经营时间。/api/app/product:产品列表 / 导出增加 公司 / 组织 / 门店 筛选,见 product 列表筛选。/api/app/product-category:列表出参增加region、location展示字段,见 product-category 列表。/api/app/team-member:成员新增 / 编辑 / 详情 / 列表增加 Company、Region 绑定字段,见 team-member 成员。/api/app/label:标签列表 Region 筛选;新增/编辑 Company、Region 绑定,见 label。/api/app/label-category:见 label-category。
单条角色菜单的独立维护仍可使用 /api/app/rbac-role-menu(见 关联接口)。
目录
| 章节 | 内容 |
|---|---|
| 变更摘要 | 本次优化点一览 |
| 公共约定 | 基址、鉴权、响应包装 |
| 数据模型 | 入参 / 出参字段 |
| accessPermissions 规则 | 读写语义(重要) |
| 1 角色分页列表 | GET /api/app/rbac-role |
| 2 角色详情 | GET /api/app/rbac-role/{id} |
| 3 新增角色 | POST /api/app/rbac-role |
| 4 编辑角色 | PUT /api/app/rbac-role/{id} |
| 5 删除角色 | DELETE /api/app/rbac-role |
| 与 /api/app/role 的差异 | 平台内置 role 接口对比 |
| partner 合作伙伴列表权限 | GET /api/app/partner 按 Token 过滤 |
| partner 新增与编辑地址字段 | POST / PUT 增加 street 等 |
| group Region PDF 导出权限 | GET /api/app/group/export-pdf |
| location 经营时间 | operatingHours 字段 |
| product 列表筛选 | partnerId / groupId / locationId |
| product-category 列表 | region / location 出参 |
| team-member 成员 | Company / Region 绑定 |
| label | 列表筛选 + 新增/编辑 Company·Region |
| label-category | Region/Location 绑定与筛选 |
| 附录 curl 示例 | 登录与调用示例 |
rbac-role 角色管理
变更摘要
| 项 | 说明 |
|---|---|
| accessPermissions 出参 | 列表、详情、新增返回、编辑返回均包含 accessPermissions:由该角色已绑定菜单的 PermissionCode 去重后,按字母序用 ,(英文逗号+空格)拼接。 |
| accessPermissions 入参 | 新增 / 编辑 body 可传 accessPermissions(英文逗号分隔的 PermissionCode),用于绑定角色菜单;逻辑见 accessPermissions 规则。 |
| menuIds 入参 | 新增 / 编辑 body 可传 menuIds(菜单 Guid 数组);与 accessPermissions 同时传时 以 menuIds 为准。 |
| orderNum | 类型为 int?:新增不传或 null 时默认为 0;编辑不传或 null 时 保留原排序号。 |
| 事务 | 新增、编辑在写入角色后会同步角色-菜单绑定(若入参指定了 menuIds 或 accessPermissions),与删除角色同属可回滚单元。 |
公共约定
- 宿主:美国版后端
Yi.Abp.Web;本地示例:http://localhost:19001。 - 路由前缀:
api/app。 - Swagger 分组:「食品标签-美国版接口」。
- 应用服务:
RbacRoleAppService(模块food-labeling-us)。 - 鉴权:与其它业务接口相同,请求头:
Authorization: {登录返回的 data.token}(含Bearer前缀)。 - Content-Type:JSON 接口使用
application/json;字段名一般为 camelCase(如accessPermissions、menuIds)。
统一响应包装(与其它 api/app 接口一致)示例:
{
"statusCode": 200,
"succeeded": true,
"data": { },
"errors": null,
"extras": null,
"timestamp": 1710000000000
}
失败时 succeeded 为 false,errors 或 error 中为业务提示(如「角色名称或编码已存在」)。
数据模型
查询入参 RbacRoleGetListInputVo
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| skipCount | int | 是 | 跳过条数(分页;与项目其它列表约定一致) |
| maxResultCount | int | 是 | 每页条数 |
| sorting | string | 否 | 排序,如 orderNum desc |
| roleName | string | 否 | 角色名称,模糊 |
| roleCode | string | 否 | 角色编码,模糊 |
| state | bool | 否 | 启用状态 |
新增 / 编辑入参 RbacRoleCreateInputVo(编辑 RbacRoleUpdateInputVo 与其相同)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| roleName | string | 是 | 角色名称 |
| roleCode | string | 是 | 角色编码,唯一(与名称不可与未删角色重复) |
| remark | string | 否 | 备注 |
| dataScope | int | 否 | 数据范围,默认 0(与内置 RBAC DataScope 枚举一致) |
| state | bool | 否 | 是否启用,默认 true |
| orderNum | int? | 否 | 排序号;见 变更摘要 |
| menuIds | Guid[] | 否 | 要绑定的菜单 Id;见 accessPermissions 规则 |
| accessPermissions | string | 否 | PermissionCode 列表(英文逗号分隔);见 accessPermissions 规则 |
列表项 / 详情基础 RbacRoleGetListOutputDto
| 字段 | 类型 | 说明 |
|---|---|---|
| id | Guid | 角色主键 |
| roleName | string | 角色名称 |
| roleCode | string | 角色编码 |
| remark | string? | 备注 |
| dataScope | int | 数据范围 |
| state | bool | 是否启用 |
| orderNum | int | 排序号 |
| accessPermissions | string | 已绑定菜单的 PermissionCode 汇总;无绑定时为空字符串 "" |
详情扩展 RbacRoleGetOutputDto
继承列表字段,并增加:
| 字段 | 类型 | 说明 |
|---|---|---|
| menuIds | string[] | 已分配菜单 Id(字符串形式的 Guid) |
分页列表包装 PagedResultWithPageDto<T>
| 字段 | 类型 | 说明 |
|---|---|---|
| pageIndex | int | 当前页码(从 1 开始) |
| pageSize | int | 每页条数 |
| totalCount | int | 总记录数 |
| totalPages | int | 总页数 |
| items | T[] | 当前页数据 |
accessPermissions 规则
出参(只读汇总)
- 来源:表
RoleMenu+Menu,取每条绑定菜单的PermissionCode(非空)。 - 格式:去重后按字母序拼接,例如:
"product.view, product.edit, report.print"。 - 与
GET /api/app/role列表/详情中的accessPermissions含义一致,便于 People / Account 页与平台角色展示对齐。
入参(写入时绑定菜单)
| 场景 | menuIds | accessPermissions | 行为 |
|---|---|---|---|
| 新增 | 传数组 | 不传 | 按 menuIds 绑定菜单(仅保留未删除且存在的菜单) |
| 新增 | 不传 | 传非空字符串 | 按 PermissionCode 解析菜单并绑定 |
| 新增 | 不传 | 传 "" |
不绑定任何菜单 |
| 新增 | 传数组 | 同时传 | 以 menuIds 为准,忽略 accessPermissions |
| 编辑 | 传数组 | 任意 | 覆盖式更新为该 menuIds 集合 |
| 编辑 | 不传 | 传非空字符串 | 按 PermissionCode 覆盖绑定 |
| 编辑 | 不传 | 传 "" |
清空该角色全部菜单绑定 |
| 编辑 | 不传 | 不传 / null | 不修改已有菜单绑定,仅更新角色基础字段 |
说明:
accessPermissions中的 Code 必须在Menu.PermissionCode中存在且菜单未删除,否则该 Code 被忽略(不会报错,但不会绑定对应菜单)。- 绑定方式为 覆盖式:每次按入参指定的菜单集合替换原
RoleMenu关系(与rbac-role-menu/set一致)。
1 角色分页列表
| 项目 | 说明 |
|---|---|
| HTTP | GET |
| 路径 | /api/app/rbac-role |
| Query | skipCount、maxResultCount、sorting、roleName、roleCode、state |
| 默认排序 | 未传 sorting 时按 orderNum 降序 |
响应 data:PagedResultWithPageDto<RbacRoleGetListOutputDto>,每项含 accessPermissions。
2 角色详情
| 项目 | 说明 |
|---|---|
| HTTP | GET |
| 路径 | /api/app/rbac-role/{id} |
| 路径参数 | id:角色 Guid |
响应 data:RbacRoleGetOutputDto(含 menuIds、accessPermissions)。
3 新增角色
| 项目 | 说明 |
|---|---|
| HTTP | POST |
| 路径 | /api/app/rbac-role |
| Body | RbacRoleCreateInputVo |
请求示例(仅基础字段)
{
"roleName": "Store Manager",
"roleCode": "store_manager",
"remark": "门店管理员",
"dataScope": 0,
"state": true,
"orderNum": 10
}
请求示例(同时用 menuIds 绑定权限)
{
"roleName": "Store Manager",
"roleCode": "store_manager",
"state": true,
"menuIds": [
"33333333-3333-3333-3333-333333333301",
"33333333-3333-3333-3333-333333333302"
]
}
请求示例(用 accessPermissions 绑定权限)
{
"roleName": "Viewer",
"roleCode": "viewer",
"state": true,
"accessPermissions": "product.view, report.print"
}
响应 data:RbacRoleGetOutputDto(含写入后的 menuIds、accessPermissions)。
常见错误
| 提示 | 原因 |
|---|---|
| 角色名称不能为空 | roleName 为空 |
| 角色编码不能为空 | roleCode 为空 |
| 角色名称或编码已存在 | 与未删除角色重复 |
4 编辑角色
| 项目 | 说明 |
|---|---|
| HTTP | PUT |
| 路径 | /api/app/rbac-role/{id} |
| 路径参数 | id:角色 Guid |
| Body | RbacRoleUpdateInputVo(字段同新增) |
请求示例(只改名称,不动菜单)
{
"roleName": "Store Manager (Updated)",
"roleCode": "store_manager",
"state": true
}
请求示例(清空全部菜单权限)
{
"roleName": "Store Manager",
"roleCode": "store_manager",
"state": true,
"accessPermissions": ""
}
响应 data:RbacRoleGetOutputDto。
5 删除角色
| 项目 | 说明 |
|---|---|
| HTTP | DELETE |
| 路径 | /api/app/rbac-role |
| Body | Guid[],要删除的角色 Id 列表(可批量) |
删除时会同步清理该角色的 RoleMenu、RoleDept、UserRole 关联,角色表为 软删除。
与 /api/app/role 的差异
| 对比项 | /api/app/rbac-role(美国版对外) |
/api/app/role(内置 RBAC) |
|---|---|---|
| 用途 | People / 美国版 Web 角色管理 | 平台权限体系内置角色 |
| accessPermissions 出参 | 有(本次优化) | 有 |
| 新增/编辑传 menuIds | 支持 | RoleCreateInputVo 支持 menuIds |
| 新增/编辑传 accessPermissions | 支持(按 Code 解析) | 入参无此字段,仅出参汇总 |
| 详情 menuIds | string[] |
结构以 Swagger 为准 |
| orderNum 可选 | int?,编辑可省略保留原值 |
以内置 DTO 为准 |
前端 People → Roles 页应优先调用 /api/app/rbac-role;若仍调用 /api/app/role,字段命名需单独对齐。
关联接口
| 接口 | 说明 |
|---|---|
POST /api/app/rbac-role-menu/set |
单独为角色设置菜单(body:roleId + menuIds),覆盖式 |
GET /api/app/rbac-role-menu/menu-ids?roleId={guid} |
查询角色已绑定的 menuId 列表 |
DELETE /api/app/rbac-role-menu |
按 roleId + menuIds 移除部分绑定 |
若在新增/编辑角色时已传 menuIds 或 accessPermissions,一般无需再调 set;仅在「只改菜单、不改角色字段」时使用 rbac-role-menu。
附录 curl 示例
以下 BASE、TOKEN 请替换为实际环境。
BASE=http://localhost:19001
TOKEN="Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
登录(获取 Token)
curl -s -X POST "%BASE%/api/app/account/login" \
-H "Content-Type: application/json" \
-d "{\"userName\":\"admin@example.com\",\"password\":\"your_password\"}"
从返回的 data.token 填入 TOKEN。
分页列表
curl -s -G "%BASE%/api/app/rbac-role" \
-H "Authorization: %TOKEN%" \
--data-urlencode "skipCount=0" \
--data-urlencode "maxResultCount=20" \
--data-urlencode "sorting=orderNum desc"
详情
curl -s "%BASE%/api/app/rbac-role/{roleId}" \
-H "Authorization: %TOKEN%"
新增(含 accessPermissions)
curl -s -X POST "%BASE%/api/app/rbac-role" \
-H "Authorization: %TOKEN%" \
-H "Content-Type: application/json" \
-d "{\"roleName\":\"Test Role\",\"roleCode\":\"test_role\",\"state\":true,\"accessPermissions\":\"product.view\"}"
编辑(不传权限字段,保留原菜单)
curl -s -X PUT "%BASE%/api/app/rbac-role/{roleId}" \
-H "Authorization: %TOKEN%" \
-H "Content-Type: application/json" \
-d "{\"roleName\":\"Test Role Updated\",\"roleCode\":\"test_role\",\"state\":true}"
删除
curl -s -X DELETE "%BASE%/api/app/rbac-role" \
-H "Authorization: %TOKEN%" \
-H "Content-Type: application/json" \
-d "[\"{roleId}\"]"
partner 合作伙伴列表权限
应用服务:PartnerAppService(fl_partner,Account Management「Company」页签)。
变更说明
| 项 | 说明 |
|---|---|
| 影响接口 | GET /api/app/partner(分页列表)、GET /api/app/partner/export-pdf(PDF 导出,筛选与列表一致) |
| 管理员 | 可查看 全部 合作伙伴(公司) |
| 其它角色 | 仅可查看与当前登录用户在 userlocation 中 已绑定门店 相关联的公司 |
| 实现类 | PartnerScopeHelper(与 Reports 模块 ReportsRoleHelper.IsAdminRole 判定一致) |
详情、单条新增/编辑/删除 不做 本次范围限制(仍按原逻辑);若需详情也校验归属,可另行扩展。
管理员判定(与 Reports 一致)
满足 任一 条件视为管理员,不做 公司范围过滤:
| 条件 | 说明 |
|---|---|
| 用户名 | JWT 中用户名为 admin(内置超管) |
| 角色码 | Roles claim 或 ICurrentUser.Roles 中含 admin |
| 权限码 | Permission claim 为 *:*:* |
非管理员数据范围算法
- 取当前用户 Id(
CurrentUser.Id)→ 查userlocation(IsDeleted = 0)得到locationId列表。 - 若无绑定门店 → 列表 / 导出 0 条。
- 查
location表上述门店的Partner字段(存的是 公司名称,与新建门店时下拉所选partnerName一致)。 - 在
fl_partner中匹配:PartnerName等于上述值 或Id等于上述值(兼容历史若location.Partner误存 Id)。 - 列表 / 导出仅返回匹配到的
fl_partner.Id记录。
User ──userlocation──► Location ──Partner(公司名)──► fl_partner
1 合作伙伴分页列表
| 项目 | 说明 |
|---|---|
| HTTP | GET |
| 路径 | /api/app/partner |
| Query 示例 | SkipCount=1&MaxResultCount=10&Sorting=CreationTime desc |
| 其它筛选 | keyword(名称/邮箱/电话模糊)、state(启用状态) |
说明:
SkipCount在本项目中为 页码(从 1 起),非 SQL offset;与PagedQueryConvention一致。Sorting支持:CreationTime asc/desc、PartnerName asc/desc、State asc/desc;未识别时默认CreationTime desc。- Keyword / State 在数据范围过滤之后生效(先按权限收窄公司集合,再筛选)。
响应 data:PagedResultWithPageDto<PartnerGetListOutputDto>(id、partnerName、contactEmail、phoneNumber、state、creationTime)。
2 PDF 导出(同范围)
| 项目 | 说明 |
|---|---|
| HTTP | GET |
| 路径 | /api/app/partner/export-pdf |
| Query 示例 | Sorting=CreationTime desc(另可传 keyword、state) |
| 分页 | 忽略 SkipCount / MaxResultCount,导出符合筛选的全量公司 |
| 上限 | 5000 条,超出返回业务错误 |
| 数据范围 | 与列表相同:BuildPartnerListQueryAsync → PartnerScopeHelper |
规则:管理员导出全部公司;非管理员仅导出其绑定门店所属公司(算法见上)。同一 Token、同一 Query 筛选下,导出行集 = 列表全量结果。
curl 示例(partner 导出 PDF)
curl -s -G "%BASE%/api/app/partner/export-pdf" \
-H "Authorization: %TOKEN%" \
--data-urlencode "Sorting=CreationTime desc" \
-o companies.pdf
curl 示例(partner 列表)
curl -s -G "%BASE%/api/app/partner" \
-H "Authorization: %TOKEN%" \
--data-urlencode "SkipCount=1" \
--data-urlencode "MaxResultCount=10" \
--data-urlencode "Sorting=CreationTime desc"
group Region 列表与 PDF 导出权限
应用服务:GroupAppService(fl_group,Account Management「Region」页签)。
变更说明
| 项 | 说明 |
|---|---|
| 影响接口 | GET /api/app/group(分页列表)、GET /api/app/group/export-pdf(PDF 导出) |
| 管理员 | 可查看 / 导出 全部 Region(组织) |
| 其它角色 | 仅可查看 / 导出与 userlocation 绑定门店 对应的 Region(见下方算法) |
| 实现 | BuildGroupJoinedQueryAsync + PartnerScopeHelper.ResolveGroupScopeAsync / ApplyGroupScope |
与 partner 列表 的区别:partner 按「绑定门店所属 公司」过滤;group 按「绑定门店上的 公司 + 组织名」精确匹配 fl_group,不会列出该公司下其它未绑定门店的 Region。
管理员判定
与 partner 章节 相同(ReportsRoleHelper.IsAdminRole)。
非管理员数据范围算法
- 取当前用户 Id → 查
userlocation得到绑定的locationId。 - 若无绑定门店 → 列表 / 导出 0 条。
- 读取这些门店的
location.Partner(公司名称,与门店保存时一致)与location.GroupName(Region 名称)。 - 将
Partner解析为fl_partner.Id(按PartnerName或Id匹配,与 partner 列表相同)。 - 在
fl_group中查找同时满足:
fl_group.PartnerId= 上一步的公司 Idfl_group.GroupName= 该门店的GroupName(trim 后全等)
- 列表 / 导出仅包含上述匹配到的
fl_group.Id。
User ──userlocation──► Location(Partner + GroupName) ──匹配──► fl_group(PartnerId + GroupName)
注意:若门店未填 GroupName 或 Partner,该门店不会贡献任何可见 Region。
1 组织分页列表
| 项目 | 说明 |
|---|---|
| HTTP | GET |
| 路径 | /api/app/group |
| Query 示例 | SkipCount=1&MaxResultCount=10&Sorting=CreationTime desc |
| 其它筛选 | keyword、partnerId、state |
说明:
SkipCount为 页码(从 1 起)。Sorting支持:CreationTime、GroupName、State、PartnerName的 asc/desc。- 先按 Token 数据范围收窄,再应用
keyword/partnerId/state。
响应 data:PagedResultWithPageDto<GroupGetListOutputDto>。
curl 示例(group 列表)
curl -s -G "%BASE%/api/app/group" \
-H "Authorization: %TOKEN%" \
--data-urlencode "SkipCount=1" \
--data-urlencode "MaxResultCount=10" \
--data-urlencode "Sorting=CreationTime desc"
2 PDF 导出(同范围)
| 项目 | 说明 |
|---|---|
| HTTP | GET |
| 路径 | /api/app/group/export-pdf |
| Query 示例 | Sorting=CreationTime desc(另可传 keyword、partnerId、state) |
| 分页 | 忽略分页参数,全量导出(上限 5000 条) |
| 数据范围 | 与 GET /api/app/group 列表一致(ResolveGroupScopeAsync) |
curl 示例(group 导出 PDF)
curl -s -G "%BASE%/api/app/group/export-pdf" \
-H "Authorization: %TOKEN%" \
--data-urlencode "Sorting=CreationTime desc" \
-o regions.pdf
联调注意(group)
| 现象 | 可能原因 |
|---|---|
| 非管理员 Region 列表为空 | 账号未绑定门店;或门店 Partner / GroupName 为空 |
| 少看到 Region | 仅显示绑定门店对应的那条 fl_group,同公司其它 Region 不会出现 |
| 有门店仍无 Region | location.GroupName 与 fl_group.GroupName 不一致(大小写/空格);或 location.Partner 与 fl_partner 对不上 |
partner 新增与编辑地址字段
库表:fl_partner 新增列 Street、City、StateCode、Country、ZipCode。已有库执行脚本:
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/scripts/fl_partner_add_address_columns.sql
命名说明(与门店 location 对齐):
| 业务含义 | JSON 字段名 | 类型 | 说明 |
|---|---|---|---|
| 街道 | street |
string? | 可空 |
| 城市 | city |
string? | 可空 |
| 州/省代码 | stateCode |
string? | 如 NY;口语里的「state(州)」用此字段 |
| 国家 | country |
string? | 可空 |
| 邮编 | zipCode |
string? | 可空 |
| 是否启用 | state |
boolean | Active / Inactive,与地址无关 |
同一 body 中
state(boolean) 表示启用状态,stateCode(string) 表示美国州缩写,请勿混用。
3 新增合作伙伴
| 项目 | 说明 |
|---|---|
| HTTP | POST |
| 路径 | /api/app/partner |
| Body | PartnerCreateInputVo |
请求示例
{
"partnerName": "Global Foods Inc.",
"contactEmail": "admin@globalfoods.com",
"phoneNumber": "+1 (555) 100-2000",
"street": "123 Main St",
"city": "New York",
"stateCode": "NY",
"country": "USA",
"zipCode": "10001",
"state": true
}
响应 data:PartnerGetOutputDto(含上述地址字段及 lastModificationTime)。
4 编辑合作伙伴
| 项目 | 说明 |
|---|---|
| HTTP | PUT |
| 路径 | /api/app/partner/{id} |
| Body | PartnerUpdateInputVo(字段与新增相同) |
未传的地址字段会按空字符串处理为 清空(null 落库);传则覆盖。
列表 / 详情出参 同步返回:street、city、stateCode、country、zipCode(PartnerGetListOutputDto / PartnerGetOutputDto)。
列表 keyword 模糊搜索已包含地址五字段。
curl 示例(新增)
curl -s -X POST "%BASE%/api/app/partner" \
-H "Authorization: %TOKEN%" \
-H "Content-Type: application/json" \
-d "{\"partnerName\":\"Global Foods Inc.\",\"street\":\"123 Main St\",\"city\":\"New York\",\"stateCode\":\"NY\",\"country\":\"USA\",\"zipCode\":\"10001\",\"state\":true}"
联调注意
| 现象 | 可能原因 |
|---|---|
| 非管理员列表为空 | 账号在 Team Member 未绑定任何门店;或门店 Partner 字段为空 / 与 fl_partner 名称不一致 |
| 管理员仍看不到某公司 | 检查是否被误判为非管理员(角色码非 admin) |
| 绑定门店后仍看不到公司 | 确认门店保存时 partner 为该公司 名称(与 fl_partner.partnerName 一致) |
| 新增/编辑报列不存在 | 未执行 fl_partner_add_address_columns.sql |
location 门店经营时间 operatingHours
库表:location 新增列 OperatingHours(varchar(512),可空,自由文本)。
脚本路径(在目标库执行一次):
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/scripts/fl_location_add_operating_hours_column.sql
ALTER TABLE `location`
ADD COLUMN `OperatingHours` varchar(512) DEFAULT NULL COMMENT '经营时间(自由文本)' AFTER `Longitude`;
字段说明
| 业务含义 | JSON 字段名 | DB 列名 | 类型 | 说明 |
|---|---|---|---|---|
| 经营时间 | operatingHours |
OperatingHours |
string? | 可空;示例:Mon–Fri 9:00 AM – 6:00 PM |
影响接口
| 接口 | 说明 |
|---|---|
GET /api/app/location |
列表 items[] 出参含 operatingHours |
POST /api/app/location |
新增 body 可传 operatingHours |
PUT /api/app/location/{id} |
编辑 body 可传 operatingHours(覆盖;传空字符串可清空) |
PUT /api/app/location/bulk-update |
批量编辑继承 LocationUpdateInputVo,同行支持 |
App GET /api/app/us-app-auth/location-detail |
operatingHours 读库;为空时展示 无 |
列表 keyword 模糊搜索已包含 operatingHours。
1 门店分页列表
| 项目 | 说明 |
|---|---|
| HTTP | GET |
| 路径 | /api/app/location |
| Query 示例 | SkipCount=1&MaxResultCount=10 |
响应 items[] 新增字段:operatingHours(string?)。
curl 示例(列表)
curl -s -G "%BASE%/api/app/location" \
-H "Authorization: %TOKEN%" \
--data-urlencode "SkipCount=1" \
--data-urlencode "MaxResultCount=10"
2 新增门店
| 项目 | 说明 |
|---|---|
| HTTP | POST |
| 路径 | /api/app/location |
| Body | LocationCreateInputVo |
请求示例
{
"partner": "Global Foods Inc.",
"groupName": "East Region",
"locationCode": "LOC-001",
"locationName": "Downtown Store",
"street": "123 Main St",
"city": "New York",
"stateCode": "NY",
"country": "USA",
"zipCode": "10001",
"phone": "+1 (555) 100-2000",
"email": "store@example.com",
"operatingHours": "Mon–Fri 9:00 AM – 6:00 PM; Sat 10:00 AM – 4:00 PM",
"state": true
}
响应 data:LocationGetListOutputDto(含 operatingHours)。
3 编辑门店
| 项目 | 说明 |
|---|---|
| HTTP | PUT |
| 路径 | /api/app/location/{id} |
| Body | LocationUpdateInputVo(含 operatingHours) |
curl 示例(编辑经营时间)
curl -s -X PUT "%BASE%/api/app/location/{id}" \
-H "Authorization: %TOKEN%" \
-H "Content-Type: application/json" \
-d "{\"locationName\":\"Downtown Store\",\"operatingHours\":\"Mon–Fri 9:00 AM – 6:00 PM\",\"state\":true}"
联调注意(location)
| 现象 | 可能原因 |
|---|---|
| 新增/编辑报列不存在 | 未执行 fl_location_add_operating_hours_column.sql |
| App 详情仍显示「无」 | 门店未填写 operatingHours 或仅空格 |
product 产品列表按公司/组织/门店筛选
应用服务:ProductAppService(fl_product + fl_location_product + location)。
变更说明
| 项 | 说明 |
|---|---|
| 影响接口 | GET /api/app/product(分页列表)、GET /api/app/product/export-products-excel(Excel 导出) |
| 新增 Query | partnerId、groupId、locationId(均可选) |
| 筛选逻辑 | 仅返回在 fl_location_product 中关联到「匹配门店」的产品;与 Menu Management 页 All Companies / All Regions / All Locations 下拉一致 |
Query 参数
| 字段 | 类型 | 说明 |
|---|---|---|
| skipCount | int | 页码(从 1 起,与项目其它列表一致) |
| maxResultCount | int | 每页条数 |
| sorting | string | 可选排序 |
| keyword | string | 模糊:ProductCode / ProductName / CategoryName |
| state | bool | 启用状态 |
| partnerId | string | 公司:fl_partner.Id;匹配 location.Partner = 该公司名称的门店 |
| groupId | string | 组织/Region:fl_group.Id;匹配该 Region 下 Partner + GroupName 的门店 |
| locationId | string | 门店:location.Id(Guid 字符串);最优先,传则忽略 partnerId / groupId |
筛选优先级与算法
locationId 有值 → 仅该门店(须存在且未删除)
否则 groupId 有值 → location.Partner + location.GroupName 与 fl_group 对应公司名、组织名一致
否则 partnerId 有值 → location.Partner = fl_partner.PartnerName
否则 → 不按门店收窄(与改前一致)
匹配到的 location Id 集合 → fl_location_product.LocationId → 产品 fl_product.Id
| 场景 | 结果 |
|---|---|
| 三者均未传 | 全部未删除产品(仍受 keyword / state 约束) |
| 传了 partnerId 但公司不存在 | 0 条 |
| 传了 groupId 但 Region 不存在 | 0 条 |
| 传了 locationId 但门店不存在 / 非 Guid | 0 条 |
| 产品未绑定任何门店 | 在传了任一公司/组织/门店筛选时 不会出现 |
与 Reports 打印日志列表的 partnerId / groupId / locationId 解析规则一致。
1 产品分页列表
| 项目 | 说明 |
|---|---|
| HTTP | GET |
| 路径 | /api/app/product |
| Query 示例 | SkipCount=1&MaxResultCount=10 |
curl 示例(按门店筛选)
curl -s -G "%BASE%/api/app/product" \
-H "Authorization: %TOKEN%" \
--data-urlencode "SkipCount=1" \
--data-urlencode "MaxResultCount=10" \
--data-urlencode "locationId=11111111-1111-1111-1111-111111111111"
curl 示例(按公司 + 关键字)
curl -s -G "%BASE%/api/app/product" \
-H "Authorization: %TOKEN%" \
--data-urlencode "SkipCount=1" \
--data-urlencode "MaxResultCount=10" \
--data-urlencode "partnerId=你的fl_partner主键" \
--data-urlencode "Keyword=tuna"
2 Excel 导出(同筛选)
GET /api/app/product/export-products-excel 支持与列表相同的 partnerId、groupId、locationId、keyword、state。
联调注意(product)
| 现象 | 可能原因 |
|---|---|
| 选了公司后列表为空 | 产品未在 fl_location_product 绑定该公司下任一门店 |
| Region 筛选无数据 | 门店 Partner / GroupName 与 fl_group 不一致 |
| 与前端下拉 Id 对不上 | 公司/组织传 主键 Id(fl_partner.Id、fl_group.Id),门店传 location.Id |
product-category / product Region·Location 绑定与筛选
应用服务:ProductCategoryAppService、ProductAppService。
命名约定:UI Region = API groupIds / groupId(fl_group.Id);落库门店冗余字段为 location.GroupName。UI Location = locationIds / locationId(location.Id)。
变更说明(product-category)
| 项 | 说明 |
|---|---|
列表 GET /api/app/product-category |
出参 region、location;Query 增加 groupId、locationId 筛选 |
新增/编辑 POST / PUT |
Body 增加 groupIds、locationIds(与原有 availabilityType 配合) |
详情 GET /{id} |
返回 groupIds、locationIds |
| 存储 | 仍写入 fl_product_category_location(按合并后的门店 Id,无新表) |
变更说明(product)
| 项 | 说明 |
|---|---|
| 新增/编辑 | Body 增加 groupIds、locationIds,合并写入 fl_location_product |
| 详情 | 返回 groupIds(由关联门店反推)、locationIds |
出参字段
| 字段 | 类型 | 说明 |
|---|---|---|
| region | string | 适用 Region 汇总 |
| location | string | 适用门店汇总 |
其它列表字段不变(categoryCode、categoryName、availabilityType、orderNum 等)。
展示规则
| availabilityType | region | location |
|---|---|---|
| ALL(或未配置为 SPECIFIED) | All Regions |
All Locations |
| SPECIFIED | 绑定门店在 location.GroupName 上的去重值,按字母序 , 拼接 |
|
| SPECIFIED 但未绑定门店 | 无 |
无 |
| SPECIFIED 已绑定但门店无 GroupName | 无 |
仍有门店名时显示门店名 |
门店名优先 location.LocationName,为空则用 LocationCode。
数据来源:fl_product_category_location → location(与详情中的 locationIds 一致,列表侧转为可读文案)。
新增/编辑 product-category(Region / Location 多选数组)
| 项目 | 说明 |
|---|---|
| 新增 | POST /api/app/product-category |
| 编辑 | PUT /api/app/product-category/{id} |
| Body | ProductCategoryCreateInputVo / ProductCategoryUpdateInputVo |
| 字段 | 类型 | 说明 |
|---|---|---|
| availabilityType | string | ALL / SPECIFIED;见下方自动规则 |
| regionIds | string[] | Region 多选(fl_group.Id);推荐字段名 |
| groupIds | string[] | 与 regionIds 等价,会合并去重(兼容旧字段名) |
| locationIds | string[] | 门店 多选(location.Id) |
自动规则
| 入参 | 行为 |
|---|---|
regionIds / locationIds 任一有值 |
availabilityType 按 SPECIFIED 处理 |
仅传 regionIds: []、locationIds: [] 且 availabilityType 为 ALL |
清空指定门店,范围改回 全部 |
availabilityType: "ALL" 且未传上述数组 |
不绑定门店(全部门店可用) |
合并规则:每个 regionIds 展开为该 Region 下全部门店,再与 locationIds 取并集 → 写入 fl_product_category_location;至少 1 个有效门店。
请求示例(多选 Region + 多选门店)
{
"categoryCode": "CAT-01",
"categoryName": "Sandwich",
"regionIds": [
"fl_group_id_east",
"fl_group_id_west"
],
"locationIds": [
"11111111-1111-1111-1111-111111111111",
"22222222-2222-2222-2222-222222222222"
],
"state": true,
"orderNum": 10
}
详情 / 列表回显
| 接口 | 多选 Id 字段 |
|---|---|
GET /api/app/product-category/{id} |
regionIds、locationIds(groupIds 与 regionIds 相同) |
GET /api/app/product-category 列表 items[] |
regionIds、locationIds + 展示字段 region、location |
1 类别分页列表
| 项目 | 说明 |
|---|---|
| HTTP | GET |
| 路径 | /api/app/product-category |
| Query 示例 | SkipCount=1&MaxResultCount=10&Sorting=OrderNum desc |
新增 Query 筛选
| 字段 | 说明 |
|---|---|
| groupId | 按 Region(fl_group.Id)筛选;含 availabilityType=ALL 的分类 |
| locationId | 按门店筛选(优先于 groupId) |
响应 items[] 示例片段
{
"id": "...",
"categoryName": "Sandwich",
"availabilityType": "SPECIFIED",
"region": "East Region, West Region",
"location": "UNCC store, Central Park Store",
"regionIds": ["fl_group_id_east", "fl_group_id_west"],
"locationIds": ["11111111-1111-1111-1111-111111111111"],
"orderNum": 10
}
curl 示例(按 Region 筛选)
curl -s -G "%BASE%/api/app/product-category" \
-H "Authorization: %TOKEN%" \
--data-urlencode "SkipCount=1" \
--data-urlencode "MaxResultCount=10" \
--data-urlencode "Sorting=OrderNum desc" \
--data-urlencode "groupId=你的fl_group主键"
2 产品新增/编辑(Company + Region + Location)
| 项目 | 说明 |
|---|---|
| 新增 | POST /api/app/product |
| 编辑 | PUT /api/app/product/{id} |
| 详情 | GET /api/app/product/{id} 返回 partnerId、partnerIds、groupIds、locationIds |
| 字段 | UI | 类型 | 说明 |
|---|---|---|---|
| partnerId | Company | string? | fl_partner.Id;展开该公司下全部门店参与合并 |
| groupIds | Region | string[]? | fl_group.Id;展开各 Region 下全部门店 |
| locationIds | Location | string[]? | location.Id;显式指定门店 |
合并规则(去重后写入 fl_location_product):
partnerId → 公司下全部门店
groupIds → 各 Region 下全部门店(Partner + GroupName 匹配 location)
locationIds → 指定门店
三者取并集
传 locationIds: [] 且其它范围字段为空/不传时,可清空门店关联;仅传 partnerId 或 groupIds 时须至少解析出 1 个有效门店。
请求示例
{
"productName": "Tuna Sub",
"categoryId": "分类Id",
"partnerId": "fl_partner主键",
"groupIds": ["fl_group_id_east"],
"locationIds": ["11111111-1111-1111-1111-111111111111"],
"state": true
}
详情出参示例
{
"id": "...",
"productName": "Tuna Sub",
"partnerId": "fl_partner主键",
"partnerIds": ["fl_partner主键"],
"groupIds": ["fl_group_id_east"],
"locationIds": ["11111111-1111-1111-1111-111111111111"]
}
列表筛选仍用 Query:
partnerId(Company)、groupId(Region)、locationId(Location),见 product 列表筛选。
联调注意(product-category / product)
| 现象 | 可能原因 |
|---|---|
| region 为「无」 | SPECIFIED 但门店未填 GroupName |
| location 为「无」 | SPECIFIED 但未保存绑定 / 关联表无数据 |
| 仍为 All Regions | availabilityType 不是 SPECIFIED |
| 保存报「至少需要匹配到一个有效门店」 | groupIds 下无门店,且 locationIds 为空或无效 |
| Region 选了但门店未全带上 | 正常:groupIds 会展开该区域下全部门店写入关联表 |
team-member 成员 Company·Region 绑定
应用服务:TeamMemberAppService。
命名约定(与 product / product-category 一致):
| UI | API 字段 | 存储 |
|---|---|---|
| Company | partnerId / partnerIds |
fl_partner.Id;门店通过 location.Partner(公司名称)关联 |
| Region | regionIds / groupIds |
fl_group.Id;门店冗余字段 location.GroupName |
| Location | locationIds |
location.Id → 写入 userlocation |
变更说明
| 项 | 说明 |
|---|---|
新增 POST /api/app/team-member |
Body 增加 partnerId、partnerIds、regionIds、groupIds;locationIds 可与前三者合并 |
编辑 PUT /api/app/team-member/{id} |
同上;批量编辑 update-team-members-bulk 的 items[] 与单条 PUT 字段一致 |
详情 GET /api/app/team-member/{id} |
返回 partnerIds、regionIds、groupIds(与 regionIds 相同)、locationIds |
列表 GET /api/app/team-member |
items[] 增加 partnerIds、regionIds(由已绑定门店反推) |
| 存储 | 仍写入 userlocation(合并后的门店 Id 列表,无新表) |
新增 / 编辑入参(节选)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| fullName | string | 是 | 姓名 |
| userName | string | 是(新增) | 登录名 |
| password | string | 是(新增)/ 可空(编辑) | 密码 |
| string | 否 | 邮箱 | |
| phone | long? | 否 | 手机 |
| roleId | Guid? | 否 | RBAC 角色 |
| partnerId | string | 否 | 单个 Company(fl_partner.Id) |
| partnerIds | string[] | 否 | Company 多选;与 partnerId 合并 |
| regionIds | string[] | 否 | Region 多选(fl_group.Id) |
| groupIds | string[] | 否 | 与 regionIds 相同,合并去重 |
| locationIds | string[] | 否 | 显式门店;与 Company/Region 展开结果 取并集 |
| state | bool | 否 | 启用,默认 true |
合并规则:partnerId/partnerIds → 该公司下全部门店;regionIds/groupIds → 该区域下全部门店;再并入 locationIds。保存前须 至少 1 个有效门店,否则返回:成员必须至少分配一个门店(公司/区域/门店至少选一项)。
详情 / 列表出参(节选)
| 字段 | 类型 | 说明 |
|---|---|---|
| partnerIds | string[] | 由 userlocation 反推的 Company Id |
| regionIds | string[] | 由绑定门店反推的 Region Id |
| groupIds | string[] | 与 regionIds 相同 |
| locationIds | string[] | 实际绑定的门店 Id |
| assignedLocations | object[] | 门店展示(id、name 等,原有字段) |
请求示例(新增)
POST /api/app/team-member
Content-Type: application/json
Authorization: {token}
{
"fullName": "Jane Doe",
"userName": "jane.doe",
"password": "ChangeMe123!",
"email": "jane@example.com",
"roleId": "00000000-0000-0000-0000-000000000001",
"partnerId": "fl_partner主键",
"groupIds": ["fl_group_id_east"],
"locationIds": [],
"state": true
}
列表示例
GET /api/app/team-member?SkipCount=1&MaxResultCount=10
Authorization: {token}
联调注意(team-member)
| 现象 | 可能原因 |
|---|---|
| 保存报「至少分配一个门店」 | Company/Region/门店均未选,或所选范围下无有效门店 |
详情 partnerIds 为空 |
绑定门店的 location.Partner 未匹配到 fl_partner |
详情 regionIds 为空 |
门店未填 GroupName 或无法匹配 fl_group |
| 批量导入仍只认门店列 | Excel 导入逻辑未改,仍按 LocationIds 解析;Web 表单用新字段 |
批量导入 / PDF 导出说明见
项目相关文档/批量导入导出接口说明.md。
label 标签
应用服务:LabelAppService。
命名约定:
| UI | API | 存储 |
|---|---|---|
| Company | partnerId / partnerIds |
解析范围;落库为 fl_label.LocationId(单门店) |
| Region | regionIds / groupIds / groupId(列表筛选) |
fl_group.Id |
| Location | locationId / locationIds |
location.Id |
标签表仅保存 一个 LocationId;Company/Region 用于选定或校验门店,与 product 多门店关联不同。
变更说明(列表)
| 项 | 说明 |
|---|---|
列表 GET /api/app/label |
Query 增加 groupId(Region 筛选);与 locationId 配合 |
Query 参数(节选)
| 字段 | 类型 | 说明 |
|---|---|---|
| skipCount | int | 页码(从 1 起) |
| maxResultCount | int | 每页条数 |
| keyword | string | 模糊:标签名、分类、类型、模板、产品名 |
| productId | string | 按关联产品筛选 |
| labelCategoryId | string | 标签分类 Id |
| labelTypeId | string | 标签类型 Id |
| templateCode | string | 模板编码 |
| state | bool | 启用状态 |
| groupId | string | Region:fl_group.Id;返回 fl_label.LocationId 属于该区域下门店的标签 |
| locationId | string | 门店:location.Id;优先于 groupId |
筛选优先级
locationId 有值 → 仅该门店(须存在且未删除)
否则 groupId 有值 → 该区域下全部门店(Partner + GroupName 与 fl_group 一致)
否则 → 不按门店/Region 收窄
| 场景 | 结果 |
|---|---|
| 均未传 | 与改前一致(仍受 keyword / productId 等约束) |
| 传了 groupId 但 Region 不存在 | 0 条 |
| 传了 locationId 但门店无效 | 0 条 |
| 标签未填 LocationId | 在传了 groupId / locationId 时 不会出现 |
与 product-category 列表的 groupId / locationId 解析规则一致(LocationScopeBindingHelper.ResolveScopedLocationIdsAsync)。
1 标签分页列表
| 项目 | 说明 |
|---|---|
| HTTP | GET |
| 路径 | /api/app/label |
| Query 示例 | SkipCount=1&MaxResultCount=10 |
curl 示例(按 Region 筛选)
curl -s -G "%BASE%/api/app/label" \
-H "Authorization: %TOKEN%" \
--data-urlencode "SkipCount=1" \
--data-urlencode "MaxResultCount=10" \
--data-urlencode "groupId=你的fl_group主键"
curl 示例(按门店筛选,优先于 groupId)
curl -s -G "%BASE%/api/app/label" \
-H "Authorization: %TOKEN%" \
--data-urlencode "SkipCount=1" \
--data-urlencode "MaxResultCount=10" \
--data-urlencode "locationId=11111111-1111-1111-1111-111111111111"
变更说明(新增 / 编辑)
| 项 | 说明 |
|---|---|
新增 POST /api/app/label |
Body 增加 partnerId、partnerIds、regionIds、groupIds、locationIds |
编辑 PUT /api/app/label/{id} |
同上(id = LabelCode) |
详情 GET /api/app/label/{id} |
返回 partnerId、partnerIds、regionIds、groupIds、locationId |
新增/编辑入参(节选)
| 字段 | 类型 | 说明 |
|---|---|---|
| labelName | string | 标签名称 |
| templateCode | string | 模板编码 |
| labelCategoryId / labelTypeId | string | 分类、类型 |
| productIds | string[] | 关联产品(至少 1 个) |
| partnerId | string | Company(fl_partner.Id) |
| partnerIds | string[] | Company 多选 |
| regionIds | string[] | Region 多选(fl_group.Id) |
| groupIds | string[] | 与 regionIds 合并 |
| locationId | string | 所属门店;优先直接落库 |
| locationIds | string[] | 门店候选;未传 locationId 且合并后 唯一 时自动采用 |
解析规则
传 locationId → 校验在 Company/Region 展开范围内(有范围时)→ 写入 fl_label.LocationId
未传 locationId → 合并 partnerId/partnerIds + regionIds/groupIds + locationIds
→ 0 个:报错
→ 1 个:自动作为所属门店
→ 多个:报错,要求显式传 locationId
请求示例
{
"labelName": "Tuna Label",
"templateCode": "TMP-01",
"partnerId": "fl_partner主键",
"groupIds": ["fl_group_id_east"],
"locationId": "11111111-1111-1111-1111-111111111111",
"labelCategoryId": "分类Id",
"labelTypeId": "类型Id",
"productIds": ["产品Id"],
"state": true
}
详情出参示例
{
"id": "LBL_xxx",
"labelName": "Tuna Label",
"locationId": "11111111-1111-1111-1111-111111111111",
"partnerId": "fl_partner主键",
"partnerIds": ["fl_partner主键"],
"regionIds": ["fl_group_id_east"],
"groupIds": ["fl_group_id_east"]
}
联调注意(label)
| 现象 | 可能原因 |
|---|---|
| 选了 Region 后列表为空 | 标签 LocationId 未落在该区域门店,或门店 Partner/GroupName 与 fl_group 不一致 |
| 与前端下拉 Id 对不上 | Region 传 fl_group.Id,门店传 location.Id |
| 保存报「对应多个门店」 | 仅选了 Company/Region 且展开多于 1 家门店,须再选 locationId |
| 保存报「须指定门店」 | 未传 locationId 且 Company/Region 未解析出门店 |
label-category 标签分类 Region·Location
应用服务:LabelCategoryAppService。
命名约定:UI Region = API regionIds / groupIds / groupId(fl_group.Id);UI Location = locationIds / locationId(location.Id)。存储表 fl_label_category_location(与 product-category 模式一致)。
变更说明
| 项 | 说明 |
|---|---|
新增 POST /api/app/label-category |
Body 增加 regionIds、groupIds、locationIds(多选数组) |
编辑 PUT /api/app/label-category/{id} |
同上 |
详情 GET /{id} |
返回 regionIds、groupIds(与 regionIds 相同)、locationIds |
列表 GET /api/app/label-category |
出参 region、location、noOfLabels、lastEdited(与下属标签同步);Query groupId、locationId 筛选 |
| 存储 | availabilityType=SPECIFIED 时写入 fl_label_category_location(Region 展开为门店后取并集) |
新增/编辑入参(节选)
| 字段 | 类型 | 说明 |
|---|---|---|
| availabilityType | string | ALL / SPECIFIED;传了 regionIds/locationIds 时自动按 SPECIFIED |
| regionIds | string[] | Region 多选(fl_group.Id) |
| groupIds | string[] | 与 regionIds 合并去重 |
| locationIds | string[] | 门店多选(location.Id) |
合并规则:每个 regionIds 展开为该 Region 下全部门店,再与 locationIds 取并集 → 写入关联表;SPECIFIED 时至少 1 个有效门店。
请求示例
{
"categoryCode": "LC-01",
"categoryName": "Sandwich Labels",
"availabilityType": "SPECIFIED",
"regionIds": ["fl_group_id_east"],
"locationIds": ["11111111-1111-1111-1111-111111111111"],
"state": true,
"orderNum": 10
}
列表 Query 筛选
| 字段 | 说明 |
|---|---|
| groupId | 按 Region 筛选;含 availabilityType=ALL 的分类 |
| locationId | 按门店筛选;优先于 groupId |
curl -s -G "%BASE%/api/app/label-category" \
-H "Authorization: %TOKEN%" \
--data-urlencode "SkipCount=1" \
--data-urlencode "MaxResultCount=10" \
--data-urlencode "groupId=你的fl_group主键"
列表出参(节选)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | string | 分类主键 |
| categoryCode / categoryName | string | 编码、名称 |
| state / orderNum | bool / int | 状态、排序 |
| region | string | 列表列 Region:ALL → All Regions;SPECIFIED → 绑定门店 GroupName 去重 , 拼接 |
| location | string | 列表列 Location:ALL → All Locations;SPECIFIED → 门店名拼接 |
| regionIds | string[] | Region Id(ALL 时 []) |
| locationIds | string[] | 门店 Id(ALL 时 []) |
| noOfLabels | long | 列表列 No. of Label:该分类下未删除标签数(fl_label.LabelCategoryId) |
| creationTime | datetime | 分类创建时间 |
| lastEdited | datetime | 列表列 Last Edited:max(分类 LastModificationTime/CreationTime, 下属标签最近编辑时间);标签增删改会回写分类 LastModificationTime |
响应 items[] 示例
{
"id": "cat_001",
"categoryName": "Sandwich Labels",
"availabilityType": "SPECIFIED",
"region": "East Region",
"location": "UNCC store, Central Park Store",
"regionIds": ["fl_group_id_east"],
"locationIds": ["11111111-1111-1111-1111-111111111111"],
"noOfLabels": 12,
"creationTime": "2026-05-10T08:00:00",
"lastEdited": "2026-05-17T14:30:00",
"orderNum": 10
}
前端 Last Edited 列请绑定
lastEdited,勿用creationTime(仅为兼容保留)。
联调注意(label-category)
| 现象 | 可能原因 |
|---|---|
| region 为「无」 | SPECIFIED 但门店未填 GroupName |
| 保存报「至少需要匹配到一个有效门店」 | Region 下无门店且 locationIds 为空 |
| 列表筛 Region 仍看到 ALL 分类 | 设计如此:ALL 对任意 Region/门店均可见 |
| Last Edited 不随标签变化 | 未拉最新列表;应读 lastEdited 而非 creationTime |
| noOfLabels 为 0 | 该分类下尚无标签,或标签 LabelCategoryId 未指向本分类 |
逻辑与 product-category 对齐,仅表名为
fl_label_category/fl_label_category_location。
修订记录
| 日期 | 说明 |
|---|---|
| 2026-05-17 | 初版:rbac-role accessPermissions / menuIds / orderNum |
| 2026-05-17 | 补充:partner 列表与 export-pdf 按 Token 数据范围过滤 |
| 2026-05-17 | 补充:partner 新增/编辑/列表/详情 地址字段 street、city、stateCode、country、zipCode |
| 2026-05-17 | 补充:group 列表/export-pdf 按绑定门店 Partner+GroupName 过滤 Region |
| 2026-05-17 | 补充:location 新增/编辑/列表 operatingHours(经营时间) |
| 2026-05-17 | 补充:product 列表/export 增加 partnerId、groupId、locationId 筛选 |
| 2026-05-17 | 补充:product-category 列表出参 region、location 展示适用区域与门店 |
| 2026-05-17 | 补充:product-category/product 新增编辑 groupIds+locationIds;分类列表 groupId/locationId 筛选 |
| 2026-05-17 | 补充:product 新增/编辑/详情 增加 partnerId(Company)与 groupIds(Region)绑定 |
| 2026-05-17 | 补充:product-category 新增/编辑/列表 regionIds、locationIds 多选数组 |
| 2026-05-17 | 补充:team-member 新增/编辑/详情/列表 partnerId、partnerIds、regionIds、groupIds 与 locationIds 合并绑定 |
| 2026-05-17 | 补充:label 列表 GET 增加 groupId(Region)筛选,locationId 优先 |
| 2026-05-17 | 补充:label 新增/编辑/详情 partnerId、partnerIds、regionIds、groupIds 与 locationId 解析绑定 |
| 2026-05-17 | 补充:label-category 新增/编辑 regionIds+locationIds 多选;列表 groupId/locationId 筛选与 region/location 展示 |
| 2026-05-17 | 补充:label-category 列表 noOfLabels、lastEdited(含下属标签同步)、region/location 列出参说明 |