Spree 6.0 B2B 企业自助注册与激活机制:公司创建、审批门禁与定价访问控制的完整设计

发布时间:2026/9/14 8:20:54
Spree 6.0 B2B 企业自助注册与激活机制:公司创建、审批门禁与定价访问控制的完整设计 Spree 6.0 B2B 企业自助注册与激活机制公司创建、审批门禁与定价访问控制的完整设计【免费下载链接】spreeOpen Source eCommerce Platform for B2B, Marketplace, and Enterprise. REST API, TypeScript SDK, and production-ready Next.js storefront. Self-host it. Own your stack. No vendor lock-in. Zero platform fees.项目地址: https://gitcode.com/GitHub_Trending/sp/spreeSpree 6.0 的 B2B 能力公司组织树、目录与量价规则已经落地但 storefront 侧缺少两样东西企业无法自行注册商家也无法把展示价格、允许结算这件事与存在商业关系绑定起来。本篇技术文章基于设计文档 B2B Company Self-Registration Activation Mechanisms完整解读这套面向 Spree 6.0 的开源机制storefront 企业自助注册POST /api/v3/store/companies、第四种 storefront 访问姿态approval_required、随行于隐藏价格的机器可读pricing_access原因码以及贯穿目录可见性、定价与结算的单一扩展点Spree::Companies::ActivationPolicy。读完后你将理解每个机制的接口形态、与现有代码的衔接点以及它与 Enterprise 审批产品之间的清晰边界。1. 问题背景B2B 流程没有前门当前仓库中的 B2B 公司能力已经相当完整Spree::Company实现了带类型company/division的组织树深度上限MAX_DEPTH 5含子树递归 CTEsubtree_ofscope、税务锚点解析legal_entity、成员关系membership与邀请机制详见 spree/core/app/models/spree/company.rb。配套的买家目录解析链Catalog.for_context → for_company公司子树 → 客户组 → 渠道默认目录的备选链也已就位见 spree/core/app/models/spree/catalog.rb。但文档指出的缺口是真实存在的store API 的公司控制器只暴露了展示与更新。从 spree/api/app/controllers/spree/api/v3/store/companies_controller.rb 可以看到Spree::Api::V3::Store::CompaniesController只有update动作以及基类的展示没有任何创建公司的端点——一个企业买家无法在 storefront 上把自己这家公司登记进来更没有任何机制让商家把定价权限限定在已建立商业关系的范围内。这套方案的目标就是用可独立交付的开源机制补上这个前门同时为 Enterprise 的审批 onboarding 产品预留唯一的接入缝。2. 关键设计决策总览设计文档列出了七条未经讨论不得偏离的关键决策它们共同定义了 OSS 与 Enterprise 的分工注册属于 OSS公司出生即激活POST /api/v3/store/companies允许已认证客户包括已有的零售客户一次调用创建一个根公司kind 为company及自己的 membershipguest 则在 storefront 上先链式调用POST /store/customers完成同一流程。自由填写的注册信息落入metadata[registration]。速率限制与客户注册一致。Core 发布company.registered事件不发邮件。Spree::Companies::ActivationPolicy是唯一接缝一个依赖注册类Spree::Dependencies.company_activation_policy_class镜像已有的storefront_access_policy_class对外提供active?(company)与pricing_access_code(user:, store:)两个方法。OSS 默认实现对所有公司返回 active。凡是为可见性、定价或结算解析公司的位置都必须咨询它。OSS 的spree_companies表不加 status 列生命周期pending / review / approved / rejected / suspended、要求清单、审核队列落在 Enterprise 拥有的 application 记录上该记录引用公司OSS 表永不引用 Enterprise 模型OSS 行为全部经由 policy 流转。第四种 storefront 访问姿态approval_required与public | prices_hidden | login_required并列。没有policy-active 公司立场standing的访客与买家浏览目录时价格为 null 且结算被拒。prices_hidden保留其既有的仅针对 guest 的语义不做改动。在默认 policy 下它读作价格仅公司成员可见在 Enterprise policy 下则读作价格仅已批准公司可见。隐藏价格必须携带原因码序列化器今天把价格置 null 时没有任何解释。门控方计算pricing_access——取值为null可见|login_required|company_required| 注册 policy 提供的代码Enterprise 增加approval_pending——凡是把价格置 null 的载荷都带上它遵循 checkout-requirements 的code:惯例渠道序列化器同时暴露storefront_access。storefront 依据代码渲染等待审批/注册你的企业文案而不是去猜测裸 null 的含义。结算由 requirement 门控而 standing 本身绝不被门控新增 checkout requirement代码company_activation_required在approval_required渠道上拒绝没有 policy-active 购物车公司的客户自主完成下单——形状与既有guest_checkout_not_allowed相同。而自服务 standingstanding_for?、company_standing、Storefront::AccessPolicy从不咨询 policy非激活公司的成员仍保留公司页面、地址簿、成员与邀请——这正是 Enterprise 流程所需的审批前工作区。客户组成员身份永远不是激活机制客户组负责定价目录分配policy 负责激活。种子 Wholesale 组上的审批原语注释随本方案退役。3. 注册机制Companies::Register工作流文档设计的注册流程是一个标准 Spree 工作流签名模式与仓库中已有的客户创建工作流 spree/core/app/workflows/spree/customers/create.rb 完全一致# 计划中的 Spree::Companies::Register # hooks :validate, :after_register在一个事务内构建根节点 membership写入metadata[registration]时间戳发布company.registered事件。validatehook 是筛查点域名规则、反欺诈检查由宿主或 Enterprise 的 hook handler 实现。这与Customers::Create中validatehook 的定位registration policy 的否决缝客户已构建但尚未持久化时运行是同一模式参见该工作流 第 60-62 行 的注释。关键区分validatehook 不在CompanyInvitations::Accept上触发——加入既有公司不算创建公司而Customers::Create的 hooks 在两条路径上照旧运行。控制器为POST /store/companies已认证、速率受限。账户下的公司列表端点与公司序列化器保持现状除非 policy 追加代码。一条开放的守卫规则每个客户在每个 store 至多自注册一个根公司校验放在Register内——无需对公司名建唯一约束即可挡住重复申请。为什么一次调用建根公司 membership是安全的可以直接对照现有模型约束根节点必须是 kind 为company的法人实体root_must_be_legal_entity校验spree/core/app/models/spree/company.rb 第 290-294 行且父子节点必须在同一 storeparent_in_same_store。自注册创建的是无 parent 的根天然满足这两条。4. 激活策略唯一的商业立场判定点4.1 OSS 默认实现class Spree::Companies::ActivationPolicy # Whether the company may act commercially (catalogs, pricing, checkout). def active?(_company) true # The pricing_access code for a viewer lacking prices, or nil. def pricing_access_code(user:, store:) return login_required if user.blank? company_required unless user.company_standing(store: store).any? { |c| active?(c) } end end两个方法语义明确active?回答这家公司能否开展商业活动目录、定价、结算pricing_access_code回答一个看不到价格的观看者应该收到什么原因码。默认实现下未登录者得login_required登录但没有 standing 的买家得company_required有任一 active 公司的 standing 则返回nil价格可见。依赖注册镜像现有做法spree/core/lib/spree/core/dependencies.rb 中已有storefront_access_policy_class: Spree::Storefront::AccessPolicy这一条目第 15 行company_activation_policy_class将并列其中。Storefront::AccessPolicyspree/core/app/models/spree/storefront/access_policy.rb本身就是这种可替换策略对象模式的范例——store API 从不咨询 CanCanCan客户授权就是所有权扩展时替换类、覆盖入口方法并 fall through 到super。4.2 咨询点不新增解析路径方案刻意不引入新的公司解析路径而是在既有调用点上插入 policy 咨询调用点现有行为已验证于源码加入 policy 后Catalog.for_company返回公司及其祖先节点上分配的全部目录catalog.rb 第 99-116 行只统计 active 公司的分配Purchase::Company#sole_standing_companyCart 侧委托Spree::Company.sole_standing_forpurchase/company.rb 第 89-93 行只取 active 公司的 membershipProducts::ForContext的第二个sole_standing_company与 Cart 侧镜像既有约束要求两处保持镜像同上购物车company_id写入校验customer_has_standing_over_companystanding 不成立时报:invalidpurchase/company.rb 第 77-82 行区分出独立的:not_active错误approval_required姿态 checkout requirement—新增消费active?判定为什么接缝拿到的是节点而不是 membership因为子树语义一个被停用的父公司覆盖其全部分公司是 policy 实现自己的职责OSS 默认实现无此问题。理解这些调用点的现状有助于把握改造量级。sole_standing_for的实现非常克制一条至多取 2 行的 join 查询companies.one? ? companies.first : nil——持有 0 个或 2 个以上 membership 都解析为 nil拒绝猜测因为猜测会把一个企业的账单开给另一个企业spree/core/app/models/spree/company.rb 第 125-144 行。买家侧 standing 的展开则经由Customer#company_standingmembership 所在节点经subtree_of展开为整棵子树与standing_for?子树检查从不做单节点相等比较见 spree/core/app/models/concerns/spree/customer_methods.rb 第 135-148 行。已完成订单不受影响从源码看Spree::Order完结后回答为哪个公司购买直接读取落单的company_id戳resolved_company中return company if is_a?(Spree::Order) completed?purchase/company.rb 第 44-52 行不会被 policy 回溯改写——这与数月后才把人加入公司不能回头豁免一单当初合法计税的订单的注释意图一致。5.approval_required姿态接入现有门控体系5.1 现有三态门控的实现渠道级 storefront 门控目前由 spree/core/app/models/spree/channel/gating.rb 承担STOREFRONT_ACCESS %w[public prices_hidden login_required].freeze第 14 行定义三种姿态public商品与价格对所有人可见、prices_hidden可浏览guest 价格为 null、login_requiredguest 的目录读请求被拒。偏好:storefront_access在渠道上可空resolved_storefront_access依次取渠道偏好 → store 级偏好 →public兜底第 28-32 行与OrderRouting::HasStrategyPreference的回退模式镜像。API 侧由 spree/api/app/controllers/concerns/spree/api/v3/storefront_gating.rb 执行before_action对login_required渠道的未认证读请求渲染 401authentication_required错误码hide_prices?判定是否为无认证客户且渠道storefront_prices_hidden?结果经serializer_params.merge(hide_prices: ...)注入共享的价格序列化助手把 guest 的价格字段置 null。5.2 新姿态的接入点方案对门控的改动是三处增量Channel::Gating::STOREFRONT_ACCESS [approval_required]并同步更新 store 级回退校验现有校验storefront_access_must_be_valid会拒收白名单外的值gating.rb 第 55-63 行。hide_prices?变为姿态感知在现有 guest 分支之外approval_required渠道下无 policy-active 公司 standing 的观看者同样命中价格隐藏——而 guest-only 的prices_hidden语义原样保留。pricing_access代码随行由门控关注点计算搭乘serializer_params与hide_prices并列暴露在所有把价格置 null 的载荷上渠道序列化器同时暴露storefront_access。这个原因码随裸 null 一起下发的设计是 storefront 前端的关键参考实现Next.js storefront从此可以按company_required渲染注册你的企业、按 Enterprise 扩展提供的approval_pending渲染等待审批而不需要对price: null做任何猜测性解释。6. 结算门控checkout requirement 的形状approval_required渠道上拒绝客户自主完成下单的机制采用与既有 requirement 完全相同的形状。现有先例是 spree/core/app/models/spree/checkout/default_requirements.rb 第 44 行errors req(address, email, Spree.t(:guest_checkout_not_allowed), code: guest_checkout_not_allowed) if cart.guest_checkout_disallowed?新增的 requirement 以代码company_activation_required表达同一意图在approval_required渠道上购物车公司不存在或 policy 判定非 active 时客户自主完成被拒。注意方向性——standing 的自服务表面公司页面、地址簿、成员管理、邀请不经过此 requirementpolicy 只回答能否交易不回答能否使用工作区。7. OSS 与 Enterprise 的边界文档最锋利的决策是第 3 条OSS 表结构上不存在 status/approval 列。审批流程pending 申请、验证要求清单、审核队列、要求补充信息全部落在 Enterprise 引用的 application 记录上而 OSS 的所有行为都经由ActivationPolicy流转。这意味着在 OSS 下注册即激活approval_required姿态等价于价格与结算仅对公司成员开放的贸易店特性——本身就独立可用升级到 Enterprise 后商家只是替换 policy 类同一套姿态、原因码与 requirement 立即获得仅已批准公司可见的语义无需任何 schema 迁移从源码结构看该边界与现有扩展缝的哲学一致Storefront::AccessPolicy的文档注释明确写着替换storefront_access_policy_class即可扩展访问面例如 B2B 公司治理而行为否决审批要求、支出上限属于 checkout workflow 的validatehook 而非 policy——本方案把同一分工推广到了公司商业立场这一维度。文档同时划定了迁移路径五步全为增量且默认惰性default policy 下行为中性规格会显式断言这一点共享关注点测试电池对 Cart 与 Order 各跑一遍ActivationPolicy 依赖注册 各解析调用点的咨询approval_required姿态 pricing_access代码 checkout requirement 序列化器暴露Companies::RegisterPOST /store/companies SDK 类型 文档Storefront 参考实现注册页 代码驱动的价格标签退役 Wholesale 客户组种子中的审批注释。没有任何数据迁移——所有改动在商家选择姿态或注册 policy 之前都是惰性的。8. 对当前与后续代码的约束设计文档为后续开发立下的红线值得逐条记住任何新的为可见性、定价或结算解析公司的代码都必须咨询激活 policy且两处sole_standing_company实现Spree::Purchase::Company与Spree::Products::ForContext保持镜像绝不用审批门控自服务 standing新的价格隐藏表面必须携带pricing_access代码不允许裸 nullspree_companies不加 status/approval 列——生命周期状态属于 Enterprise application 记录OSS 只问 policy不要在此机制旁边搭建临时审批原语客户组成员身份、布尔标志——任何形状像审批的东西都等这套机制。9. 小结与延伸阅读这套设计的价值在于用最少的 OSS 表面积一个工作流、一个端点、一个姿态、一个原因码、一个 requirement、一个 policy 类换来了两条产品线OSS 得到企业自助注册 公司成员专属价这一完整闭环Enterprise 则拿到一个不碰 OSS schema 的审批 onboarding 落点。仓库内可对照深入的文件设计文档本体docs/plans/6.0-b2b-company-self-registration.md前置方案docs/plans/6.0-b2b-companies-and-catalogs.md公司树、membership、自服务、Catalog.for_context门控实现spree/core/app/models/spree/channel/gating.rb、spree/api/app/controllers/concerns/spree/api/v3/storefront_gating.rbstanding 解析链spree/core/app/models/spree/company.rb、spree/core/app/models/concerns/spree/customer_methods.rb、spree/core/app/models/concerns/spree/purchase/company.rb注册工作流先例spree/core/app/workflows/spree/customers/create.rb依赖注册spree/core/lib/spree/core/dependencies.rb需要说明的适用前提该文档当前状态为 Draft设计已于 2026-08-30 定稿、尚未实现目标 Spree 6.0因此文中的POST /store/companies、approval_required、pricing_access与ActivationPolicy均为计划中的 API 形态而本文引用的公司模型、目录解析、门控链与注册工作流等均为当前仓库中已验证的实现现状。【免费下载链接】spreeOpen Source eCommerce Platform for B2B, Marketplace, and Enterprise. REST API, TypeScript SDK, and production-ready Next.js storefront. Self-host it. Own your stack. No vendor lock-in. Zero platform fees.项目地址: https://gitcode.com/GitHub_Trending/sp/spree创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询