6-30代码优化.md 13.5 KB

6-30 代码优化

本文档说明 2026-06-30 对美国版接口的两项优化:

  1. Company Admin 在其绑定 Company 下新建 Region 时,自动将新 Region 绑定到当前账号(写入 userlocation)。
  2. Product / Label Type 分页列表增加 locationName(绑定门店名称)出参。
  3. App Preview / Print Log / Label ReportlabelId 统一为 yyyyMMdd-n;Print Log 与 Label Report 按门店展示全部用户打印记录。

一、需求背景

项目 说明
角色 Company Admin(库内 RoleCode = CompanyAdminRoleName = Company Admin
场景 在 Account Management → Region 页签,于已绑定 Company 下新增 Region
期望 新建 Region 后,当前账号应默认绑定该 Region,无需再手动维护成员门店范围
关联接口 GET/POST /api/app/group

为何需要写 userlocation

系统成员与数据范围以 userlocation(用户 ↔ 门店) 为主键路径:

  • Region 列表GET /api/app/group):Company Admin 已按 fl_group.PartnerId 展开可见该公司下全部 Region(见 6-18代码优化.md 第十五节)。
  • 门店列表 / auth-scope / 打印范围 等:仍按 userlocation 反推 location.Partner + location.GroupName 判断 Region 可见性。

因此仅写入 fl_group 不足以让新 Region 在「按门店绑定推导」的链路中立即可用;需在新建 Region 时追加该 Region 下门店的 userlocation 绑定。


二、行为说明

1. 触发条件(同时满足)

# 条件
1 当前用户角色为 Company AdminTeamMemberRoleHelper.IsCompanyAdminUserAsync
2 非平台管理员(admin 不自动改 userlocation
3 新建 Region 的 partnerId 属于当前账号已绑定 Company(由 userlocation 反推 fl_partner.Id
4 接口已通过 EnsurePartnerIdAllowedForRegionMutationAsync 权限校验

2. 绑定规则

步骤 动作
1 POST /api/app/group 成功插入 fl_group
2 partnerIdfl_partner.PartnerName,与新建 groupName 匹配 location 表:Partner = PartnerNameGroupName = groupName
3 将匹配到的门店 Id 追加写入 userlocation不删除已有绑定;已绑门店幂等跳过)

3. 空 Region(尚无门店)

新建 Region 时若该公司下尚无同名 GroupName 的门店,则本次不会新增 userlocation 行(无门店可绑)。

补充:Company Admin 随后通过 POST /api/app/location 在同一 Company / Region 下新建首家门店时,后端会自动将该门店追加绑定到当前账号(与 Region 新建逻辑共用 LocationScopeBindingHelper)。

4. 不受影响

角色 / 场景 说明
平台 admin 不写入 userlocation
Regional Admin / Location Manager 等 不自动扩绑;仍按 Team Member 维护范围
PUT /api/app/group 编辑 Region 不触发自动绑定
在Snapshot 已有绑定 只追加缺失门店,不覆盖、不删除

三、接口对接

1. Region 分页列表

项目 内容
方法 GET
路径 /api/app/group

Query 参数

参数 类型 必填 说明
SkipCount int 页码,从 1 起
MaxResultCount int 每页条数
Sorting string CreationTime desc
Keyword string Region 名 / Company 名模糊
PartnerId string 按 Company(fl_partner.Id)筛选
State bool 启用状态

Company Admin 数据范围

  • 返回其绑定 Company(userlocationfl_partner.Id)下的全部 Region。
  • 新建 Region 后无需重新登录即可在列表中看到(列表本身不依赖新 Region 是否已写 userlocation)。

curl 示例

curl -s -G "http://localhost:19001/api/app/group" \
  -H "Authorization: <token>" \
  --data-urlencode "SkipCount=1" \
  --data-urlencode "MaxResultCount=10" \
  --data-urlencode "Sorting=CreationTime desc"

2. 新增 Region

项目 内容
方法 POST
路径 /api/app/group
Content-Type application/json

Body

字段 类型 必填 说明
groupName string Region 名称,对应 fl_group.GroupName / location.GroupName
partnerId string 所属 Company(fl_partner.Id
state bool 默认 true

Body 示例

{
  "groupName": "Subway Guangzhou",
  "partnerId": "3a218xxx-company-id",
  "state": true
}

成功响应GroupGetOutputDto(含 idgroupNamepartnerIdpartnerNamestatecreationTime)。

Company Admin 副作用(2026-06-30)

  • partnerId 为当前账号已绑定 Company,且存在 location.Partner + location.GroupName 与新建 Region 一致的门店,则这些门店写入 userlocation
  • 前端无需额外调用 Team Member 接口。

curl 示例

curl -s -X POST "http://localhost:19001/api/app/group" \
  -H "Authorization: <token>" \
  -H "Content-Type: application/json" \
  -d "{\"groupName\":\"Subway Guangzhou\",\"partnerId\":\"<fl_partner.Id>\",\"state\":true}"

3. 验证清单

# 步骤 期望
1 Company Admin 登录,确认 userlocation 已有该公司下至少 1 家门店 有绑定 Company
2 POST /api/app/group 新建 Region(partnerId = 该公司) 200,返回新 id
3 GET /api/app/group?SkipCount=1&MaxResultCount=10&Sorting=CreationTime desc 列表含新 Region
4 若该公司下已有同名 GroupName 的门店 查库 userlocation 新增对应 LocationId
5 GET /api/app/auth-scope/regions?partnerId=... 下拉含新 Region
6 空 Region 下 POST /api/app/location 创建首家门店 当前账号 userlocation 自动追加该门店

查库 SQL

-- 新建 Region 后:当前用户是否绑定该 Region 下门店
SELECT ul.*
FROM userlocation ul
INNER JOIN location loc ON loc.Id = ul.LocationId AND loc.IsDeleted = 0
INNER JOIN fl_partner p ON p.PartnerName = loc.Partner AND p.IsDeleted = 0
WHERE ul.IsDeleted = 0
  AND ul.UserId = '<UserId>'
  AND p.Id = '<partnerId>'
  AND loc.GroupName = '<groupName>';

四、代码改动

文件 改动
Helpers/LocationScopeBindingHelper.cs 新增 TryAutoBindCompanyAdminToNewRegionAsyncTryAutoBindCompanyAdminToNewLocationAsyncAppendUserLocationBindingsAsync
Services/GroupAppService.cs CreateAsync 插入 fl_group 后调用 Region 自动绑定
Services/LocationAppService.cs CreateAsync 插入门店后 Company Admin 自动追加 userlocation(空 Region 补门店)
IServices/IGroupAppService.cs 补充 CreateAsync XML 说明

六、Product / Label Type 列表增加 locationName

需求

接口 说明
GET /api/app/product 列表每行返回产品绑定的门店名称
GET /api/app/label-type 列表每行返回类型绑定的门店名称

/api/app/label 列表的 locationName 字段命名一致,前端可直接展示 Location 列,无需再单独拉 product-location 或全量门店目录反查。


1. Product 分页列表

项目 内容
方法 GET
路径 /api/app/product

Query 示例

SkipCount=1&MaxResultCount=10

新增出参字段(items[]

字段 类型 说明
locationName string 绑定门店名称,英文逗号拼接(location.LocationName,无名称时回退 LocationCode);无绑定时为
locationIds string[] 绑定门店 Id(fl_location_productlocation.Id

数据来源

fl_product
  └── fl_location_product (ProductId + LocationId)
        └── location (LocationName / LocationCode)

响应片段示例

{
  "totalCount": 2,
  "items": [
    {
      "id": "prod_001",
      "productName": "Chicken Wrap",
      "categoryName": "Sandwich",
      "noOfLabels": 3,
      "locationIds": ["3a220c4f-976e-9dcc-186b-ad2b90a9a4ef"],
      "locationName": "Subway Beijing Store 01"
    }
  ]
}

curl

curl -s -G "http://localhost:19001/api/app/product" \
  -H "Authorization: <token>" \
  --data-urlencode "SkipCount=1" \
  --data-urlencode "MaxResultCount=10"

2. Label Type 分页列表

项目 内容
方法 GET
路径 /api/app/label-type

Query 示例

SkipCount=1&MaxResultCount=10

新增出参字段(items[]

字段 类型 说明
locationName string 与既有 location 同值availabilityType=ALL 时为 All LocationsSPECIFIED 且无绑定时为
location string 保留兼容;内容与 locationName 一致
locationIds string[] 已有字段;fl_label_type_location 绑定的门店 Id

数据来源

fl_label_type
  └── fl_label_type_location (LabelTypeId + LocationId)
        └── location (LocationName / LocationCode)

展示规则

availabilityType locationName
ALL All Locations
SPECIFIED + 有绑定 门店名去重后英文逗号拼接
SPECIFIED + 无绑定

响应片段示例

{
  "totalCount": 1,
  "items": [
    {
      "id": "lt_price",
      "typeName": "Price Label",
      "availabilityType": "SPECIFIED",
      "locationIds": ["3a220c4f-976e-9dcc-186b-ad2b90a9a4ef"],
      "location": "Subway Beijing Store 01",
      "locationName": "Subway Beijing Store 01",
      "region": "Subway Beijing",
      "noOfLabels": 12
    }
  ]
}

curl

curl -s -G "http://localhost:19001/api/app/label-type" \
  -H "Authorization: <token>" \
  --data-urlencode "SkipCount=1" \
  --data-urlencode "MaxResultCount=10"

3. 代码改动

文件 改动
Dtos/Product/ProductGetListOutputDto.cs 新增 LocationNameLocationIds
Services/ProductAppService.cs GetListAsync 批量读 fl_location_product 并汇总门店名;Excel 导出复用 BuildProductLocationDisplayMapsAsync
Dtos/LabelType/LabelTypeGetListOutputDto.cs 新增 LocationName
Services/LabelTypeAppService.cs 列表映射 LocationName = Location

八、App Preview / Print Log / Label Report 修复

问题

# 现象 根因
1 preview 出参 labelId 为 GUID 误返回 fl_label.Id,未用当日序号
2 get-print-log-listlabelId 格式不对 误返回 fl_label_print_task.LabelId(标签主键)
3 Print Log 仅当前用户记录 restrictToCreator=true(非 Partner 角色)
4 Label Report 统计仅当前用户 同上

业务规则(统一)

Label ID = 某门店某自然日内打印任务递增序号:{yyyyMMdd}-{n}(如 20260707-120260707-2)。

范围 统计维度
同一 locationId 同一自然日(PrintedAt ?? CreationTime
排序 时间升序,再按任务 Id 升序
预览 不落库,返回「下一序号」= 已有任务数 + 1

Print Log / Label Report 数据范围:已绑定该门店的账号 → 查看该门店全部用户的打印任务(不按 CreatedBy 过滤)。

1. Preview

项目 内容
方法 POST
路径 /api/app/us-app-labeling/preview

出参 labelIdyyyyMMdd-n(下一序号);printLabelDisplayId:模板含 Label ID 控件时同步写入画布。

2. Print Log

项目 内容
方法 POST
路径 /api/app/us-app-labeling/get-print-log-list

入参

字段 说明
locationId 必填
printDateDay 可选,yyyy-MM-dd,按自然日筛选
skipCount / maxResultCount 分页(SkipCount 从 1 起)

出参 items[]

字段 说明
labelId 门店当日序号 yyyyMMdd-n
labelEntityId fl_label.Id(内部关联)
labelCode fl_label.LabelCode(重打/预览入参)
operatorName 实际打印人

3. Label Report

项目 内容
方法 POST
路径 /api/app/us-app-labeling/get-label-report

统计该 locationId全部用户startDateendDate 内的打印量(与 Print Log 同范围)。

代码改动

文件 改动
UsAppLabelingAppService.cs Preview labelId;Print Log 序号 + printDateDay + 全店范围;Report 全店范围;Reprint 同店可重打
UsAppPrintLogScopeHelper.cs 注释更新
PrintLogGetListInputVo.cs / IUsAppLabelingAppService.cs 接口说明

九、关联文档

  • Company Admin Region 列表范围:项目相关文档/6-18代码优化.md(第十五节)
  • Team Member Company Admin 范围规则:项目相关文档/6-18代码优化.md(第十二节)
  • group 列表 Token 范围(历史):项目相关文档/5-17接口优化.md(group Region 列表与 PDF 导出权限)
  • Product 门店绑定(历史):项目相关文档/5-17接口优化.md(product 列表筛选)
  • App Label ID 规则(历史):项目相关文档/6-11代码优化.md6-2代码优化.md