Skip to content

概述

门店租赁权限是平台按门店业务能力授权的机制,授权主体是当前请求的 store_id,不是员工 RBAC 角色、员工 ID、URL、路由别名或菜单名。门店端和店员入口的安全边界由后端执行,前端按钮显隐不承担授权职责。

平台端仍由平台鉴权体系控制,不使用门店 capability;门店端和店员入口必须取得有效门店上下文,并在业务写入前依次完成:

text
解析当前门店 → 校验目标业务对象归属门店 → 校验显式 capability key → 执行业务动作

配置三态与 Admin 三模式

授权配置以 store_id 为粒度,授权记录表为 eb_system_store_lease_authcontent 保存 capability key 数组。三态语义如下:

状态数据表示12 项能力结果
未配置没有该门店记录全开,兼容历史门店
全部关闭有记录且 content=[]全关
自定义白名单有记录且 content 为非空数组仅数组中的已知 key 可用

Admin 配置界面提供三个互斥模式:

  1. 继承默认/全开:删除该门店授权记录;不能用空数组表示此模式。
  2. 全部关闭/全关:保存该门店记录,content=[]
  3. 自定义白名单:保存一个或多个固定 capability key;只能从下表 12 项能力中选择。

配置接口为平台端接口:

用途方法路径
租赁权限列表GET/adminapi/lease_auth_list
租赁权限详情GET/adminapi/lease_auth_detail/:id
保存租赁权限POST/adminapi/save_lease_auth/:id

详情响应显式包含 configuredmodecapabilities。保存请求提交门店 ID、模式和 key 数组,服务端重新校验门店及 key:inherit 删除记录,disabled 保存空数组,custom 必须保存非空数组。未知 key、无效门店、模式与数组不一致或重复 key 均应拒绝。

固定能力与 capability key

以下是本版本唯一支持的 12 项能力。名称和英文 key 是后端契约,必须逐字使用:

业务能力capability key典型写入口范围
订单资料调整lease.order_profile.edit收货信息、联系人、商品规格等资料写入
租赁金额调整lease.amount.edit租赁金额写入
风控与身份核验lease.risk_identity.verify风控刷新、身份核验等写入或触发
租赁订单审核lease.order.audit租赁订单审核
订单取消处理lease.order.cancel取消、同意或拒绝取消
分期账单管理lease.installment.manage分期账单生成、修改和备注
租金收退管理lease.rent_money.manage正式租金收款、退款及相关状态变更
合同与存证lease.contract_evidence.manage普通合同、公证、聚证及合同存证
归还与货损lease.return_damage.manage归还确认、货损记录和赔付资料
押金与赔付lease.deposit_compensation.manage押金收退、赔付处理和芝麻押金
买断lease.buyout.manage买断金额和买断流程
完结lease.complete租赁订单完结

入口必须通过后端预先定义的显式 key 绑定,禁止运行时从 URL、方法名或菜单名推导。storeapiapi 店员入口使用同一授权服务、同一 key 映射和同一对象归属校验;修改路由名称不会改变授权语义。

门店、平台与业务对象边界

门店或店员请求必须取得大于 0 的有效 store_id。不能用 store_id=0 推断平台身份,也不能用员工 RBAC 放行租赁能力。目标订单、合同、账单或其他业务对象必须属于当前 store_id;跨门店对象在能力判断前拒绝,并且不发生业务写入。

平台请求由明确的平台身份进入,跳过门店 capability,继续使用平台既有鉴权。门店前端按钮保持现状,未授权请求由后端统一拒绝,错误消息为“该门店未开通此租赁能力”。

列表、详情、只读统计、只读报告和只读下载地址等纯查询入口不绑定 capability,但仍必须执行既有的门店数据隔离。纯查询可用于查看数据,不代表获得对应写入能力。

能力边界

以下能力不属于门店 capability 白名单,不能通过增加任何 key 放行:

  • 监管锁;
  • 违约售后;
  • 租赁宝/蚂蚁链;
  • 旧代扣风控;
  • 司法动作;
  • 代客续租。

司法动作只保留平台专属入口并由平台身份校验。普通合同、公证和聚证保留,统一归入 lease.contract_evidence.manage。正式租金收退保留,withholdPaywithholdRefund 归入 lease.rent_money.manage;这不等同于重新开放租赁宝/蚂蚁链、旧代扣风控或其他禁用边界。

不恢复无门店路由残留接口,不新增代客续租路由,也不通过门店白名单绕过平台专属能力。

缓存与安全失败

授权缓存 key 必须包含门店 ID,当前实现使用以下形式:

text
store_lease_auth_state_{store_id}

缓存值必须同时保存配置存在性和能力数组,最小结构为:

json
{
  "configured": true,
  "capabilities": ["lease.order.audit"]
}

configured=false 表示无记录全开;configured=true 且数组为空表示全关。因此不能只缓存裸数组。缓存未命中时回源数据库并惰性回填;配置保存或删除成功后必须删除对应门店缓存。

缓存畸形、反序列化失败、缺少 configuredcapabilities,以及数据库配置含未知 key 时,必须安全失败:受控写操作拒绝,不得退化为默认全开,也不得发生业务写入。未知 capability key 同样不会获得授权。

数据库唯一索引与迁移

授权表按 store_id 一门店一记录。执行迁移前先在目标环境确认实际表名、现有索引并检查重复数据;不要默默删除未经确认的数据。唯一索引 SQL 如下:

sql
ALTER TABLE `eb_system_store_lease_auth`
  ADD UNIQUE KEY `uk_system_store_lease_auth_store_id` (`store_id`);

默认只允许在测试环境执行数据库变更。执行前备份授权表,确认重复 store_id 查询为空;执行后用 SHOW INDEX 确认唯一索引,并用同一 store_id 的重复插入验证数据库拒绝冲突。正式/生产环境不得在未明确确认环境的情况下执行 ALTERINSERTUPDATEDELETE 或其他变更语句。

当前代码和测试涉及的主要路径:

  • app/dao/store/SystemStoreLeaseAuthDao.php:固定 key、显式入口映射和纯查询清单;
  • app/services/store/StoreLeaseCapabilityServices.php:门店身份、对象归属和能力拦截;
  • app/services/store/SystemStoreLeaseAuthServices.php:三态配置、保存、缓存和安全失败;
  • app/controller/admin/v1/store/SystemStore.php:Admin 配置接口;
  • route/admin.php:Admin 租赁权限路由;
  • db_sql/zyl.sql:唯一索引迁移 SQL。

配置、接口与测试验证示例

以下 curl 仅为请求形状示意,不包含真实 token、域名或敏感配置。平台后台请求应使用实际部署环境的安全地址和合法登录凭证:

bash
curl -H 'Authori-zation: Bearer <ADMIN_TOKEN>' \
  'https://<ADMIN_HOST>/adminapi/lease_auth_detail/<STORE_ID>'

curl -X POST \
  -H 'Authori-zation: Bearer <ADMIN_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{"mode":"disabled","content":[]}' \
  'https://<ADMIN_HOST>/adminapi/save_lease_auth/<STORE_ID>'

# 自定义白名单
curl -X POST \
  -H 'Authori-zation: Bearer <ADMIN_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{"mode":"custom","content":["lease.order.audit"]}' \
  'https://<ADMIN_HOST>/adminapi/save_lease_auth/<STORE_ID>'

# 继承默认/全开
curl -X POST \
  -H 'Authori-zation: Bearer <ADMIN_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{"mode":"inherit","content":[]}' \
  'https://<ADMIN_HOST>/adminapi/save_lease_auth/<STORE_ID>'

后端验证至少应覆盖:

  1. 无记录时 12 项能力均允许,空数组记录时 12 项能力均拒绝,非空数组只允许白名单 key;
  2. storeapiapi 同一门店配置结果一致,纯查询不因缺少 capability 被拦截;
  3. 缺少有效 store_id、跨门店对象、未知 key、畸形缓存和未授权写操作均拒绝,且无业务副作用;
  4. 12 项写入口分别覆盖允许、拒绝和非本店对象拒绝;
  5. 普通合同公证聚证、正式租金收退和平台司法入口保持规定边界,监管锁、违约售后、租赁宝/蚂蚁链、旧代扣风控及代客续租不被放行。

可单独运行的相关测试示例:

bash
vendor/bin/phpunit -c tests/phpunit.xml \
  tests/Unit/Services/StoreLeaseCapabilityServicesTest.php \
  tests/Unit/Services/StoreLeaseWriteEntryMatrixTest.php \
  tests/Unit/Services/SystemStoreLeaseAuthCapabilitiesTest.php \
  tests/Unit/Services/SystemStoreLeaseAuthServicesTest.php

常见问题

Q1:为什么无记录是全开,而空数组是全关?

这是兼容历史门店和表达明确关闭的三态协议。configured 必须随缓存保存,否则两种状态都会表现为同一个空数组。

Q2:员工有 RBAC 菜单权限,为什么仍然被拒绝?

员工 RBAC 只负责既有登录和菜单权限,不是租赁业务能力来源。还必须确认当前门店、目标对象归属以及门店 capability 配置。

Q3:查询接口需要配置能力吗?

列表、详情、只读统计、报告和下载地址等纯查询不纳管 capability,但仍受门店数据隔离约束;写入、审核、收退和其他有副作用的动作需要显式 key。

Q4:缓存异常时能否按无配置门店放行?

不能。缓存结构异常、未知 key 或反序列化失败必须安全失败,拒绝受控写操作,避免故障扩大为全开。

Q5:怎样新增一项租赁能力?

新增能力必须经过规格和代码评审,按以下顺序执行:

  1. 明确业务边界,确认不是监管锁、违约售后、租赁宝/蚂蚁链、旧代扣风控、司法动作或代客续租;
  2. SystemStoreLeaseAuthDao 固定清单中增加中文名称和稳定英文 key,并为每个写入口建立显式映射;
  3. 明确 storeapiapi 入口及目标对象归属校验,纯查询不要加入能力映射;
  4. 更新 Admin 固定选项、服务端模式校验、缓存/配置读取和对应测试;
  5. 为允许、拒绝、跨店拒绝、未知 key、缓存异常和无业务副作用补充测试,再同步规格、数据库/发布说明和本二开文档。

不得从 URL 或方法名自动生成 key,不得仅靠前端按钮控制,也不得用新增白名单绕过既定禁用边界。

相关文档

承信租多门店租赁商城系统官方文档