|
@@ -284,8 +284,9 @@ public class HarShopService {
|
|
|
// 挑店时却比不上的静默失配。
|
|
// 挑店时却比不上的静默失配。
|
|
|
HarRegionCatalog.City city = requireKnownCity(dto.getProvinceCode(), dto.getCityCode());
|
|
HarRegionCatalog.City city = requireKnownCity(dto.getProvinceCode(), dto.getCityCode());
|
|
|
// 企业为空 ⇒ 按**租户**寻址(去企业化,2026-09-24,见 db/har-tenant-level.sql)。
|
|
// 企业为空 ⇒ 按**租户**寻址(去企业化,2026-09-24,见 db/har-tenant-level.sql)。
|
|
|
- // 分流规则与 HarOpenController#requireMerchant、HarOrderService#create
|
|
|
|
|
- // 是同一套 —— 三处必须同时改。
|
|
|
|
|
|
|
+ // 企业与租户的分流规则与 HarOpenController#requireMerchant、HarOrderService#create
|
|
|
|
|
+ // 是同一套 —— 三处必须同时改。★ 例外只有一条:下方 ① 在本文件另有**超管腿**
|
|
|
|
|
+ // (2026-10-09 门店改由超管创建),另外两处没有 —— 改分流前先看它。
|
|
|
String ownerEnterpriseId = StrUtil.isNotBlank(enterpriseId)
|
|
String ownerEnterpriseId = StrUtil.isNotBlank(enterpriseId)
|
|
|
? enterpriseId : dto.getEnterpriseId();
|
|
? enterpriseId : dto.getEnterpriseId();
|
|
|
if (getByThirdShopId(ownerEnterpriseId, dto.getThirdShopId()) != null) {
|
|
if (getByThirdShopId(ownerEnterpriseId, dto.getThirdShopId()) != null) {
|
|
@@ -293,14 +294,25 @@ public class HarShopService {
|
|
|
}
|
|
}
|
|
|
// 三条分支,次序就是优先级:
|
|
// 三条分支,次序就是优先级:
|
|
|
// ① 显式 merchant_code —— 一个租户可以有多条配置(2026-09-24),
|
|
// ① 显式 merchant_code —— 一个租户可以有多条配置(2026-09-24),
|
|
|
- // 门店铺在上游是挂在商户下的,调用方指定才是准的;
|
|
|
|
|
|
|
+ // 门店铺在上游是挂在商户下的,调用方指定才是准的。
|
|
|
|
|
+ // 2026-10-09 起管理端建店由**超管**操作 ⇒ 超管腿按编号**全局**解析
|
|
|
|
|
+ // (超管没有归属租户,带租户谓词只会把它挡死);租户调用与开放 API
|
|
|
|
|
+ // 仍是带租户谓词的那条,语义不变。门店归属两条腿都取自商户行(见下方
|
|
|
|
|
+ // entity.setTenantId);
|
|
|
// ② 企业级映射;③ 租户级(此时只有一条才成立,多条由 requireByTenant 报 400 拦下)。
|
|
// ② 企业级映射;③ 租户级(此时只有一条才成立,多条由 requireByTenant 报 400 拦下)。
|
|
|
HarMerchantEntity merchant;
|
|
HarMerchantEntity merchant;
|
|
|
if (StrUtil.isNotBlank(dto.getMerchantCode())) {
|
|
if (StrUtil.isNotBlank(dto.getMerchantCode())) {
|
|
|
- merchant = merchantService.requireByTenantAndCode(
|
|
|
|
|
- getCurrentTenantId(), dto.getMerchantCode());
|
|
|
|
|
|
|
+ merchant = isSuperuser()
|
|
|
|
|
+ ? merchantService.requireByCode(dto.getMerchantCode())
|
|
|
|
|
+ : merchantService.requireByTenantAndCode(
|
|
|
|
|
+ getCurrentTenantId(), dto.getMerchantCode());
|
|
|
} else if (StrUtil.isNotBlank(ownerEnterpriseId)) {
|
|
} else if (StrUtil.isNotBlank(ownerEnterpriseId)) {
|
|
|
merchant = merchantService.requireByEnterpriseId(ownerEnterpriseId);
|
|
merchant = merchantService.requireByEnterpriseId(ownerEnterpriseId);
|
|
|
|
|
+ } else if (isSuperuser()) {
|
|
|
|
|
+ // 超管没有「本租户配置」可回退(tenantId 可以是 null)⇒ 不指定就拒绝。
|
|
|
|
|
+ // 回退到超管自己的租户会挑出一条商户并**静默**把门店挂错租户 —— 宁可报错。
|
|
|
|
|
+ throw new BusinessException(400,
|
|
|
|
|
+ "请指定商户编号 merchant_code:门店归属取自所选商户的租户");
|
|
|
} else {
|
|
} else {
|
|
|
merchant = merchantService.requireByTenant(getCurrentTenantId());
|
|
merchant = merchantService.requireByTenant(getCurrentTenantId());
|
|
|
}
|
|
}
|
|
@@ -314,6 +326,12 @@ public class HarShopService {
|
|
|
|
|
|
|
|
// 1. 先落 PENDING —— 无论后续发生什么都留下记录
|
|
// 1. 先落 PENDING —— 无论后续发生什么都留下记录
|
|
|
HarShopEntity entity = new HarShopEntity();
|
|
HarShopEntity entity = new HarShopEntity();
|
|
|
|
|
+ // 归属取自**商户行**(用户口径:「选的是哪个商户就用商户表的租户id」)。
|
|
|
|
|
+ // 租户调用/开放 API 下它就是当前租户(解析带着租户谓词,与 AutoFill 会填的值一致);
|
|
|
|
|
+ // 超管腿下这是**唯一**正确的来源 —— 超管 bypass 了租户拦截器,AutoFill 只会拿
|
|
|
|
|
+ // 超管自己的 tenantId(可以是 null)。AutoFill 只在值为 null 时兜底、不会覆盖这里
|
|
|
|
|
+ // (见 AutoFillMetaObjectHandler#insertFill),所以显式 set 是有效且必要的。
|
|
|
|
|
+ entity.setTenantId(merchant.getTenantId());
|
|
|
// 空 ⇒ NULL,不是空串:租户级门店找的正是 enterprise_id IS NULL
|
|
// 空 ⇒ NULL,不是空串:租户级门店找的正是 enterprise_id IS NULL
|
|
|
// (见 getByThirdShopId 与 uk_har_shop_tenant_third_shop)。
|
|
// (见 getByThirdShopId 与 uk_har_shop_tenant_third_shop)。
|
|
|
// 口径同 LsyStrings.blankToNull(isBlank ⇒ null,非空白原样,不 trim)。
|
|
// 口径同 LsyStrings.blankToNull(isBlank ⇒ null,非空白原样,不 trim)。
|
|
@@ -424,4 +442,16 @@ public class HarShopService {
|
|
|
}
|
|
}
|
|
|
return null;
|
|
return null;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 当前身份是不是超管 —— 分岔判据必须与 {@code TenantInnerInterceptor#ignoreTable}
|
|
|
|
|
+ * 用的是**同一个开关**({@code LoginUser#getIsSuperuser},同
|
|
|
|
|
+ * {@code HarMerchantController#requireMerchantForCurrentUser} 的论证)。
|
|
|
|
|
+ * 换成别的判据(例如「tenantId 是不是 null」)会出现「一边以为放行、另一边仍在过滤」。
|
|
|
|
|
+ */
|
|
|
|
|
+ private boolean isSuperuser() {
|
|
|
|
|
+ var auth = SecurityContextHolder.getContext().getAuthentication();
|
|
|
|
|
+ return auth != null && auth.getPrincipal() instanceof LoginUser user
|
|
|
|
|
+ && Boolean.TRUE.equals(user.getIsSuperuser());
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|