5-26 代码优化
本文档说明 2026-05-26 对美国版接口的变更。
/api/app/product-category:新增/编辑categoryCode取消必填(见 product-category-categoryCode)。/api/app/label-template:新增/编辑/列表/详情支持 Region、Location 多选数组;列表 Query 增加 Region/Location 筛选(见 label-template-regionlocation)。/api/app/rbac-role:修复accessPermissionsJSON 数组(如manage_labels)无法绑定菜单(见 rbac-role-accesspermissions)。/api/app/auth-scope:管理员(及按数据范围受限账号)登录后 Company → Region → Location 级联选店(见 auth-scope-登录选店)。/api/app/us-app-auth:App 管理员 Token 专用 Company / Region / 门店筛选 接口(见 us-app-auth-管理员选店)。
应用服务:ProductCategoryAppService、LabelTemplateAppService、RbacRoleAppService
命名约定(与 5-17 / 5-18 一致):UI Region = API regionIds / groupIds / groupId(fl_group.Id);UI Location = locationIds / locationId(location.Id)。
product-category categoryCode 可选
影响接口
| 方法 | 路径 |
|---|---|
| POST | /api/app/product-category |
| PUT | /api/app/product-category/{id} |
变更说明
| 项 | 变更前 | 变更后 |
|---|---|---|
| categoryCode | 必填;空则报「类别编码和名称不能为空」 | 可选;可不传、传 null 或 "" |
| categoryName | 必填 | 仍必填 |
| 落库 | — | 未填编码时 CategoryCode 存 空字符串 |
| 唯一性 | 编码或名称重复即报错 | 有编码:编码 或 名称重复报错;无编码:仅校验 名称 不重复 |
入参(节选)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| categoryCode | string | 否 | 类别编码 |
| categoryName | string | 是 | 类别名称 |
| regionIds / groupIds / locationIds | string[] | 否 | Region·Location 范围(规则见 5-17接口优化.md) |
请求示例(无编码)
POST /api/app/product-category
Content-Type: application/json
Authorization: Bearer {token}
{
"categoryName": "Beverages",
"buttonAppearance": "TEXT",
"state": true,
"availabilityType": "ALL",
"orderNum": 0
}
联调注意
| 现象 | 处理 |
|---|---|
| 仍报「类别编码和名称不能为空」 | 确认已部署含本变更的后端;仅需保证 categoryName 非空 |
| 无编码时名称重复 | 正常:仅按 categoryName 判重 |
Region/Location 多选、列表
region/location展示等完整说明见5-17接口优化.md→ product-category 章节。
label-template Region·Location 多选
应用服务:LabelTemplateAppService
存储表:fl_label_template_location(模板 ↔ 门店,无新表)
主表字段:fl_label_template.AppliedLocationType = ALL / SPECIFIED
变更说明
| 项 | 变更前 | 变更后 |
|---|---|---|
| 新增/编辑 Body | 仅 appliedLocation + appliedLocationIds |
增加 regionIds、groupIds、locationIds(与 appliedLocationIds 合并) |
| 列表 Query | 仅 locationId |
增加 groupId(Region);locationId 优先于 groupId(与 product-category 一致) |
| 列表出参 | 仅 locationText(单条展示) |
增加 region、location 展示 + regionIds、locationIds 数组 |
| 详情出参 | appliedLocationIds |
同上,并保留 appliedLocationIds(与 locationIds 一致,兼容编辑器) |
| 范围解析 | 仅显式门店 Id | Region 展开为门店后与门店 Id 取并集 落库 |
影响接口
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/app/label-template?SkipCount=1&MaxResultCount=10 |
列表支持 groupId/locationId 筛选;items[] 增加 region、location、regionIds、locationIds |
| GET | /api/app/label-template/{id} |
详情增加上述字段 |
| POST | /api/app/label-template |
Body 支持 Region/Location 多选 |
| PUT | /api/app/label-template/{id} |
同新增 |
路径参数 id 仍为模板编码 TemplateCode(与编辑器 JSON 的 id 一致)。
新增/编辑入参(Body:LabelTemplateCreateInputVo)
| 字段 | JSON 名 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
| TemplateCode | id |
string | 是 | 模板编码 |
| TemplateName | name |
string | 是 | 模板名称 |
| AppliedLocationType | appliedLocation |
string | 否 | ALL / SPECIFIED,默认 ALL |
| RegionIds | regionIds |
string[] | 否 | Region 多选(fl_group.Id) |
| GroupIds | groupIds |
string[] | 否 | 与 regionIds 等价,合并去重 |
| LocationIds | locationIds |
string[] | 否 | 门店多选(location.Id) |
| AppliedLocationIds | appliedLocationIds |
string[] | 否 | 兼容旧字段,与 locationIds 合并 |
| Elements | elements |
array | 否 | 模板组件,全量重建 |
| TemplateProductDefaults | templateProductDefaults |
array | 否 | 仅 编辑 时显式传入才重建 |
自动规则
| 入参 | 行为 |
|---|---|
regionIds / groupIds / locationIds / appliedLocationIds 任一有有效 Id |
appliedLocation 按 SPECIFIED 处理 |
仅传空数组 [] 且 appliedLocation 为 ALL |
不绑定门店(全部门店) |
appliedLocation: "SPECIFIED" 且合并后无有效门店 |
报错:指定适用区域或门店时,至少需要匹配到一个有效门店 |
appliedLocation 非法值 |
报错:适用门店范围不合法(ALL/SPECIFIED) |
合并规则:每个 regionIds 展开为该 Region 下全部门店,再与 locationIds、appliedLocationIds 取并集 → 写入 fl_label_template_location。
请求示例(Region + 门店多选)
POST /api/app/label-template
Content-Type: application/json
Authorization: Bearer {token}
{
"id": "TPL_TEST_001",
"name": "Price Tag 4x6",
"labelType": "PRICE",
"unit": "inch",
"width": 4,
"height": 6,
"appliedLocation": "SPECIFIED",
"regionIds": [
"fl_group_id_east",
"fl_group_id_west"
],
"locationIds": [
"11111111-1111-1111-1111-111111111111"
],
"showRuler": true,
"showGrid": true,
"state": true,
"elements": []
}
请求示例(全部门店,兼容旧版)
{
"id": "TPL_ALL",
"name": "Global Template",
"labelType": "PRICE",
"unit": "inch",
"width": 4,
"height": 6,
"appliedLocation": "ALL",
"appliedLocationIds": [],
"elements": []
}
列表(GET /api/app/label-template)
Query 参数
| 字段 | 类型 | 说明 |
|---|---|---|
| SkipCount / MaxResultCount | int | 分页(项目约定 SkipCount 从 1 起) |
| keyword | string | 模板名称/编码模糊 |
| groupId | string | 按 Region 筛选(fl_group.Id):命中 appliedLocation=ALL 的模板,或在 fl_label_template_location 中绑定了该 Region 下任一门门店的模板 |
| locationId | string | 按门店筛选(location.Id);优先于 groupId |
| labelType | string | 如 PRICE |
| state | bool | 启用状态 |
| sorting | string | 排序(可选) |
筛选规则(与 product-category / label-type 相同,内部 LocationScopeBindingHelper.ResolveScopedLocationIdsAsync)
| 入参 | 行为 |
|---|---|
均未传 groupId、locationId |
不过滤适用范围 |
仅 groupId |
解析该 Region 下全部门店 Id,再筛模板 |
仅 locationId |
按该门店 Id 筛模板 |
| 同时传 | 以 locationId 为准(忽略 groupId) |
| Region/门店无效或解析结果为空 | 仅返回 appliedLocation=ALL 的模板 |
命中条件(满足其一即可出现在列表):
fl_label_template.AppliedLocationType = 'ALL'SPECIFIED且fl_label_template_location中存在LocationId ∈解析得到的门店集合
请求示例
GET /api/app/label-template?SkipCount=1&MaxResultCount=10&groupId=fl_group_id_east HTTP/1.1
Authorization: Bearer {token}
GET /api/app/label-template?SkipCount=1&MaxResultCount=10&locationId=11111111-1111-1111-1111-111111111111 HTTP/1.1
Authorization: Bearer {token}
命名对照:UI Region → Query groupId;UI Location → Query locationId。
列表出参
items[] 新增/对齐字段
| 字段 | 类型 | 说明 |
|---|---|---|
| region | string | 适用 Region 展示文案 |
| location | string | 适用门店展示文案 |
| regionIds | string[] | Region Id 多选;ALL 时为 [] |
| locationIds | string[] | 门店 Id 多选;ALL 时为 [] |
| locationText | string | 兼容字段,与 location 相同 |
其它字段不变:id(= TemplateCode)、templateName、contentsCount、sizeText、versionNo、lastEdited 等。
列表响应示例片段
{
"pageIndex": 1,
"pageSize": 10,
"totalCount": 2,
"items": [
{
"id": "TPL_ALL",
"templateCode": "TPL_ALL",
"templateName": "Global Template",
"labelType": "PRICE",
"region": "All Regions",
"location": "All Locations",
"locationText": "All Locations",
"regionIds": [],
"locationIds": [],
"contentsCount": 5,
"sizeText": "4x6inch",
"versionNo": 1,
"lastEdited": "2026-05-26T10:00:00"
},
{
"id": "TPL_TEST_001",
"templateName": "Price Tag 4x6",
"region": "East Region, West Region",
"location": "UNCC store, Central Park Store",
"locationText": "UNCC store, Central Park Store",
"regionIds": ["fl_group_id_east", "fl_group_id_west"],
"locationIds": [
"11111111-1111-1111-1111-111111111111",
"22222222-2222-2222-2222-222222222222"
],
"contentsCount": 3,
"sizeText": "4x6inch",
"versionNo": 2,
"lastEdited": "2026-05-26T11:30:00"
}
]
}
详情出参(GET /api/app/label-template/{id})
在原有 elements、templateProductDefaults、appliedLocationType 等基础上增加:
| 字段 | 类型 | 说明 |
|---|---|---|
| region | string | 展示文案 |
| location | string | 展示文案 |
| regionIds | string[] | Region Id 多选 |
| groupIds | string[] | 与 regionIds 相同(兼容) |
| locationIds | string[] | 门店 Id 多选 |
| appliedLocationIds | string[] | 与 locationIds 一致(编辑器回显) |
展示规则
| appliedLocation | region | location | regionIds / locationIds |
|---|---|---|---|
| ALL | All Regions |
All Locations |
空数组 [] |
| SPECIFIED | 绑定门店 location.GroupName 去重后 , 拼接 |
门店名(优先 LocationName,否则 LocationCode)拼接 |
由绑定门店反推 / 直接为绑定 Id |
| SPECIFIED 无绑定 | 无 |
无 |
[] |
regionIds 由 locationIds 反查 fl_group 得到(与 product-category、label-type 一致)。
编辑说明
PUTBody 字段与POST相同;传regionIds/locationIds会 全量替换 模板适用门店(先删fl_label_template_location再插入)。elements仍为全量重建;templateProductDefaults仅当 Body 显式包含 该字段时才重建,避免普通保存误清空。- 编辑成功
versionNo+1。
联调注意
| 现象 | 处理 |
|---|---|
列表无 regionIds |
确认已部署含本变更的后端 |
传 groupId 列表仍很多 |
正常:appliedLocation=ALL 的模板始终可见 |
传 groupId 列表为空 |
检查 Region 是否存在、其下是否有门店;无效 Region 时仅剩 ALL 模板 |
| 传了 Region 仍显示 All Locations | 检查 Region Id 是否有效、是否能在库中展开到门店 |
仅 appliedLocationIds 不传 locationIds |
仍支持,与 locationIds 合并 |
前端编辑器仍传 appliedLocation: "ALL" |
管理端若需多选,须在 Body 增加 regionIds / locationIds(见 labelTemplateService.ts) |
| 指定范围但 0 门店 | 后端报错,需至少 1 个有效门店 |
与 product-category / label-type 的关系
逻辑与 5-17接口优化.md 中 product-category、label-type 的 Region·Location 绑定一致,差异仅为:
| 模块 | 范围字段名 | 关联表 |
|---|---|---|
| product-category | availabilityType |
fl_product_category_location |
| label-type | availabilityType |
fl_label_type_location |
| label-template | appliedLocation |
fl_label_template_location |
rbac-role accessPermissions 修复
应用服务:RbacRoleAppService
影响接口:POST / PUT /api/app/rbac-role/{id}、GET 列表/详情回显
问题与根因
| 现象 | 根因 |
|---|---|
保存报 accessPermissions 未匹配到任何菜单 |
前端提交 JSON 数组字符串(如 ["manage_labels",...]),旧逻辑按逗号拆分,解析结果带 [" 引号,无法匹配 |
传 manage_labels 等仍无菜单 |
表单权限码为 UI 编码(manage_labels),菜单侧为 menu.labels(由 Menu.Router 推导);二者未做映射 |
详情 accessPermissionCodes 为空 |
新增/编辑未写入 Role.AccessPermissionCodes(JSON 列),仅依赖 RoleMenu 反查 |
变更说明
| 项 | 变更后 |
|---|---|
| 入参解析 | accessPermissions 支持 JSON 数组字符串、逗号分隔、以及 Body 字段 accessPermissionCodes 数组 |
| 菜单绑定 | UI 权限码经 RoleAccessPermissionMenuMapping 映射到 Menu.Router,再写入 RoleMenu |
| 落库 | 同时将勾选的 UI 编码写入 Role.AccessPermissionCodes(JSON 数组),供 GET 回显 |
| PermissionCode 为空 | 仍可按 Router 推导 menu.xxx(建议执行 menu_backfill_permission_code.sql) |
UI 权限码 → 菜单 Router 映射(当前库)
| accessPermissions(UI) | 绑定菜单 Router |
|---|---|
manage_labels |
/labeling、/labels、/label-categories、/label-types、/label-templates |
manage_people |
/account-management |
edit_settings |
/menu-management、/multiple-options |
view_reports |
/reports |
manage_products |
(当前 Menu 表无 Products 路由,勾选不绑定菜单,不单独报错) |
approve_batches |
(当前无对应菜单路由,同上) |
至少 1 个 权限码能匹配到菜单即保存成功;若 全部 均无法匹配(例如只勾
manage_products且库中无对应菜单),仍返回业务错误。
请求示例(与前端一致)
PUT /api/app/rbac-role/3a1f077b-3665-63f2-5fea-0fd7e7044b88
Content-Type: application/json
Authorization: Bearer {token}
{
"roleName": "Partner Admin",
"roleCode": "admin",
"remark": "Admin",
"dataScope": 0,
"state": true,
"orderNum": 999,
"accessPermissions": "[\"manage_labels\",\"edit_settings\",\"view_reports\",\"manage_people\",\"manage_products\",\"approve_batches\"]"
}
也可使用逗号分隔(旧格式):
{
"accessPermissions": "manage_labels, view_reports, manage_people"
}
或同时传数组字段(与 accessPermissions 合并去重):
{
"accessPermissionCodes": ["manage_labels", "view_reports"]
}
入参优先级(与 5-18 一致)
| menuIds | accessPermissions / accessPermissionCodes | 行为 |
|---|---|---|
| 非空数组 | 任意 | 以 menuIds 为准 |
| 不传 | 非空 | 按 UI 权限码映射菜单并覆盖 RoleMenu |
| 不传 | "" 或空数组 |
清空 RoleMenu 与 AccessPermissionCodes |
[] |
不传 | 清空绑定 |
响应回显
| 字段 | 说明 |
|---|---|
accessPermissionCodes |
来自 Role.AccessPermissionCodes,如 ["manage_labels","view_reports"] |
accessPermissions |
已绑定菜单的 menu.xxx 汇总(逗号拼接,只读展示) |
menuIds |
已绑定菜单 Guid 列表(RoleMenu) |
数据库准备(推荐)
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/scripts/menu_backfill_permission_code.sql
联调注意
| 现象 | 处理 |
|---|---|
| 仍报未匹配到菜单 | 确认已部署本修复;检查 Menu 是否存在上表 Router |
| 只勾 Products/Batches 报错 | 当前库无对应菜单属预期;请同时勾选 Labels/Reports 等 |
同时传 menuIds: [] |
menuIds 优先,会清空绑定并忽略 accessPermissions |
更完整的 RBAC 说明见
5-18接口优化.md→ rbac-role 章节。
auth-scope 登录后 Company · Region · Location 级联选店
应用服务:AuthScopeAppService
适用场景:Web POST /api/app/account/login 或 App POST /api/app/us-app-auth/login 取得 Token 后,管理员无 userlocation 绑定时需先选工作门店;亦支持非管理员在数据范围内级联选择(须已绑定该门店)。
命名约定(与 5-17 一致):UI Company = partnerId(fl_partner.Id);UI Region = groupId(fl_group.Id);UI Location = locationId(location.Id,Guid 字符串)。
接口一览
| 步骤 | 方法 | 路径 | 说明 |
|---|---|---|---|
| 1 | GET | /api/app/auth-scope/companies |
可选公司列表 |
| 2 | GET | /api/app/auth-scope/regions?partnerId={partnerId} |
指定公司下 Region |
| 3 | GET | /api/app/auth-scope/locations?partnerId={partnerId}&groupId={groupId} |
指定公司+Region 下门店 |
| 4 | POST | /api/app/auth-scope/select-location |
确认当前工作门店 |
| — | GET | /api/app/auth-scope/current-scope |
查询已选工作门店(未选返回 null) |
鉴权:均需 Authorization: Bearer {token}。
数据范围
| 角色 | Company | Region | Location |
|---|---|---|---|
管理员(admin / 用户名 admin / 权限 *:*:*) |
全部未删除公司 | 该公司下全部 Region | 该 Region 下全部门店(location.Partner + location.GroupName 与 fl_group 一致) |
| 非管理员 | userlocation 绑定门店所属公司 |
绑定门店对应 Region | 上述 Region 内且符合 LocationRegionScopeHelper 的门店;选店时须已绑定该 locationId |
1)公司列表
GET /api/app/auth-scope/companies HTTP/1.1
Authorization: Bearer {token}
响应:AuthScopeCompanyOptionDto[]
[
{ "id": "fl_partner_id_1", "partnerName": "Acme Foods", "state": true }
]
2)Region 列表
GET /api/app/auth-scope/regions?partnerId=fl_partner_id_1 HTTP/1.1
Authorization: Bearer {token}
响应:AuthScopeRegionOptionDto[]
[
{ "id": "fl_group_id_east", "groupName": "East Region", "partnerId": "fl_partner_id_1", "state": true }
]
3)门店列表
GET /api/app/auth-scope/locations?partnerId=fl_partner_id_1&groupId=fl_group_id_east HTTP/1.1
Authorization: Bearer {token}
响应:AuthScopeLocationOptionDto[](含 fullAddress、groupName 等)
4)确认选店(与现有 App 逻辑对齐)
POST /api/app/auth-scope/select-location HTTP/1.1
Authorization: Bearer {token}
Content-Type: application/json
{
"partnerId": "fl_partner_id_1",
"groupId": "fl_group_id_east",
"locationId": "a2696b9e-2277-11f1-b4c6-00163e0c7c4f"
}
响应:AuthScopeSelectLocationOutputDto
| 字段 | 说明 |
|---|---|
| partnerId / partnerName | 所选公司 |
| groupId / groupName | 所选 Region |
| location | 与 UsAppBoundLocationDto 相同(id、locationCode、locationName、fullAddress、state) |
选店后的服务端行为(无需改前端即可对接 App):
| 能力 | 行为 |
|---|---|
| 工作范围缓存 | 写入分布式缓存(24h);退出 POST /api/app/auth-session/logout 时清除 |
GET /api/app/us-app-auth/my-locations |
管理员在缓存选店后,列表 合并 该门店(与 userlocation 并集) |
GET .../location-detail/{locationId} |
管理员可不依赖 userlocation 访问已选门店(UsAppPrintLogScopeHelper.EnsureUserCanAccessLocationAsync) |
| App 打印/报表 | 仍传 locationId;权限规则不变(见 5-18接口优化.md) |
5)当前工作范围
GET /api/app/auth-scope/current-scope HTTP/1.1
Authorization: Bearer {token}
未选店时响应体为 null(HTTP 200)。
联调注意
| 现象 | 处理 |
|---|---|
regions 为空 |
公司下无 fl_group 或当前账号无 Region 数据范围 |
locations 为空 |
门店 Partner / GroupName 未与 fl_partner、fl_group 对齐 |
| 选店报「门店与所选公司/区域不匹配」 | 检查 location.Partner、location.GroupName |
| 非管理员选店报未绑定 | 须在 Team Member 中为该账号绑定该门店 |
选店后 my-locations 仍为空 |
确认已调 select-location 且 Token 为管理员身份 |
Web 管理端报表等模块仍可按 Query 传
partnerId/groupId/locationId收窄;本组接口主要解决 登录后选工作门店 与 App 门店列表 一致性问题。
us-app-auth App 管理员级联选店
应用服务:UsAppAuthAppService
适用场景:App 使用 POST /api/app/us-app-auth/login 登录后,持 管理员 身份(admin 角色 / 用户名 admin / 权限 *:*:*)且 JWT 含 client_kind=us-app,按 Company → Region 筛选门店。
与 auth-scope 关系:查询逻辑共用 AuthScopeQueryHelper;App 侧路径统一在 us-app-auth 下,并 强制 App Token + 管理员,避免误用 Web Token。
接口一览
| 步骤 | 方法 | 路径 | 说明 |
|---|---|---|---|
| 0 | POST | /api/app/us-app-auth/login |
获取 App Token(须管理员账号) |
| 1 | GET | /api/app/us-app-auth/admin-scope-companies |
公司列表 → 取 id 作 partnerId |
| 2 | GET | /api/app/us-app-auth/admin-scope-regions?partnerId={partnerId} |
Region 列表 → 取 id 作 groupId |
| 3 | GET | /api/app/us-app-auth/admin-scope-locations?partnerId={partnerId}&groupId={groupId} |
按公司与 Region Id 筛选门店 |
| 4 | POST | /api/app/us-app-auth/select-admin-scope-location |
确认工作门店 |
| — | GET | /api/app/us-app-auth/my-locations |
选店后刷新绑定门店(含缓存门店) |
鉴权:步骤 1–4 须 Header Authorization: Bearer {App登录返回的token}。
前置条件
| 项 | 要求 |
|---|---|
| Token 来源 | 必须来自 /api/app/us-app-auth/login(非 Web /api/app/account/login) |
| JWT 声明 | client_kind = us-app |
| 角色 | 平台管理员(ReportsRoleHelper.IsAdminRole) |
| 违反时 | 请使用 App 登录令牌调用该接口 或 仅管理员可使用公司/区域/门店筛选接口 |
1)公司列表
GET /api/app/us-app-auth/admin-scope-companies HTTP/1.1
Authorization: Bearer {app_token}
响应:AuthScopeCompanyOptionDto[](与 auth-scope 相同)
[
{ "id": "fl_partner_id_1", "partnerName": "Acme Foods", "state": true }
]
2)Region 列表
GET /api/app/us-app-auth/admin-scope-regions?partnerId=fl_partner_id_1 HTTP/1.1
Authorization: Bearer {app_token}
响应:AuthScopeRegionOptionDto[]
[
{ "id": "fl_group_id_east", "groupName": "East Region", "partnerId": "fl_partner_id_1", "state": true }
]
3)门店列表(按 partnerId + groupId 筛选)
GET /api/app/us-app-auth/admin-scope-locations?partnerId=fl_partner_id_1&groupId=fl_group_id_east HTTP/1.1
Authorization: Bearer {app_token}
Query
| 参数 | 必填 | 说明 |
|---|---|---|
| partnerId | 是 | 公司 Id(fl_partner.Id) |
| groupId | 是 | Region Id(fl_group.Id) |
响应:AuthScopeLocationOptionDto[]
[
{
"id": "a2696b9e-2277-11f1-b4c6-00163e0c7c4f",
"locationCode": "LOC-1",
"locationName": "Downtown Kitchen",
"fullAddress": "123 Main St, New York, NY 10001",
"state": true,
"partnerId": "fl_partner_id_1",
"groupId": "fl_group_id_east",
"groupName": "East Region"
}
]
筛选规则:location.Partner 匹配该公司(Id 或名称),且 location.GroupName 与所选 fl_group.GroupName 一致。
4)确认选店
POST /api/app/us-app-auth/select-admin-scope-location HTTP/1.1
Authorization: Bearer {app_token}
Content-Type: application/json
{
"partnerId": "fl_partner_id_1",
"groupId": "fl_group_id_east",
"locationId": "a2696b9e-2277-11f1-b4c6-00163e0c7c4f"
}
响应:AuthScopeSelectLocationOutputDto(含 location 节点,结构同 UsAppBoundLocationDto)
推荐调用顺序(App)
POST /api/app/us-app-auth/login
→ GET admin-scope-companies
→ GET admin-scope-regions?partnerId=...
→ GET admin-scope-locations?partnerId=...&groupId=...
→ POST select-admin-scope-location
→ GET my-locations
→ 后续业务接口传 locationId(打印、报表等,规则不变)
联调注意
| 现象 | 处理 |
|---|---|
| 报「请使用 App 登录令牌」 | 勿用 Web account/login 的 Token;须重新 App 登录 |
| 报「仅管理员可使用」 | 换管理员账号或绑定 admin 角色 |
locations 为空 |
核对门店 Partner、GroupName 与 fl_partner、fl_group |
| 与 auth-scope 重复 | App 端 优先 使用本节前缀;Web 端用 auth-scope |
变更记录
| 日期 | 说明 |
|---|---|
| 2026-05-26 | us-app-auth:App 管理员 admin-scope-companies/regions/locations、select-admin-scope-location |
| 2026-05-26 | auth-scope:登录后 Company/Region/Location 级联选店;选店缓存;my-locations / 门店详情与管理员选店对齐 |
| 2026-05-26 | product-category:categoryCode 新增/编辑改为可选 |
| 2026-05-26 | label-template:新增/编辑/列表/详情支持 regionIds、locationIds 及 region、location 展示 |
| 2026-05-26 | label-template 列表 Query 增加 groupId(Region)、locationId(门店)筛选 |
| 2026-05-26 | rbac-role:支持 accessPermissions JSON 数组 + UI 权限码映射 Menu;落库 AccessPermissionCodes |