Subscription 订阅源
Subscription 是 oak-general-business 里相对轻量的一块能力。它提供的是“订阅源的结构化记录”,而不是一整套自动抓取和自动发布引擎。
从实体结构看,它更像一个被别的同步逻辑消费的配置对象:
- 订阅源属于哪个业务对象;
- 订阅源名称和描述是什么;
- 配置是什么;
- 当前已经同步到了哪个偏移位置。
主要对象
这一章的核心实体只有一个:
Subscription
当前它的 config 结构偏向微信订阅号场景,但它本身作为实体是比较中性的,因此本章也把它称为“订阅源”,而不再狭义地理解成某一个具体渠道。
组件
虽然实体很轻,但组件已经准备了基础管理能力:
src/components/subscription/detailsrc/components/subscription/listsrc/components/subscription/upsertsrc/components/subscription/config/upsert
这意味着你可以先把“订阅源管理后台”搭起来,再决定项目层要不要继续补同步逻辑。
常用组件与参数
这一组组件里,最值得直接按源码理解的是下面三块:
subscription/listsubscription/upsertsubscription/config/upsert
subscription/list 不是全局订阅源列表,它当前是按业务对象维度工作的。oak-general-business/src/components/subscription/list/index.ts 暴露的关键参数只有两个:
entityentityId
组件内部会把这两个参数直接变成固定 filter:
{
entityId: this.props.entityId,
entity: this.props.entity,
}
因此它更适合挂在“某个对象自己的订阅源列表”里,例如某个栏目、某个应用、某个业务主体下的订阅号配置,而不是做全系统订阅源总表。
从真实行为看,它还已经内置了几条常见管理动作:
- 详情跳
/subscription/detail - 更新跳
/subscription/upsert - 配置跳
/subscription/config/upsert - 删除后直接
removeItem(id)再execute()
subscription/upsert 则是最基础的订阅源新增/编辑页。它最关键的两个参数同样是:
entityentityId
并且源码里已经做了一个很实用的默认行为:如果当前是“创建”而不是“编辑”(也就是没有 oakId),组件会在 ready() 里自动把 entity/entityId 写进当前编辑数据。也就是说,项目层通常只要把业务对象上下文传进来,不必在页面里再手工 update({ entity, entityId }) 一遍。
subscription/config/upsert 更值得单独写清楚,因为它不是一套自定义表单,而是直接复用了 components/config/application:
isService={false}type="wechatPublic"entity="subscription"entityId={oakId}name={name}
这意味着当前公共包里 Subscription.config 虽然挂在 subscription 实体上,但实际配置界面完全按“微信公众号配置”来处理。结合 src/entities/Subscription.ts 的定义,这一章当前真正稳定支持的配置形状就是:
{
type: 'wechatPublic',
appId: string,
appSecret: string,
}
如果项目层未来要把 Subscription 扩展成别的订阅源类型,通常不能只改实体类型,还要一起补配置组件。
subscription/config/upsert 当前真实读取了哪些数据
这个配置组件虽然最终渲染的是 config/application,但它自己的数据读取范围其实很窄。从 src/components/subscription/config/upsert/index.ts 看,它只取:
idnameconfig
也就是说,进入“订阅源配置页”时,公共组件默认只关心:
- 当前订阅源是谁
- 展示名称是什么
- 当前渠道配置是什么
像:
descriptionoffsetentity/entityId
这些字段都不会在这里展示,也不会在这里直接编辑。它们仍然分别归:
subscription/upsert- 同步逻辑本身
这也是为什么项目层如果要做“订阅同步状态监控页”,通常不能只靠这一个配置组件。
subscription/detail 当前展示边界
subscription/detail 的 projection 里虽然带了 config、entity、entityId,但当前 web 端实现实际上只展示:
idnamedescription
也就是说,它现在更像一个“订阅源概览卡片”,而不是完整详情页。像 config、offset 这种更偏配置和同步状态的信息,项目层如果需要展示,通常要么直接走 config/upsert,要么再包一层自己的 detail 页面。
aspect / endpoint / feature
这一章需要明确说清楚:
- 当前没有专门的 frontend feature;
- 没有独立的 aspect;
- 也没有专门的 endpoint。
也就是说,Subscription 现在主要提供的是:
- 一个实体模型;
- 一组基础组件;
- 一个可以被项目层进一步扩展的配置入口。
后台规则与注入点
当前 oak-general-business 里没有为 subscription 单独提供 trigger、checker、watcher 或 routine。
这也正是它和前面那些“高度自动化”的模块最大的区别:这部分更多是在给项目层留扩展点,而不是强行内置一套默认流程。
因此它的注入点其实非常简单:
- 实体跟随
oak-general-business进入项目; - 组件可以直接复用;
- 其它业务逻辑由项目层自己补。
项目中如何接入
Subscription 在项目里的接法比较轻:
- 如果你只是要做订阅号配置后台,直接复用
subscription/list、detail、upsert、config/upsert - 如果你要把订阅号挂到某个业务对象上,就按
entity + entityId建立关联
它没有 feature 和 aspect,所以项目层更多是在页面和实体数据层使用它。
结合组件源码,更推荐按下面这种方式落:
- 列表页把
entity和entityId固定住,直接包subscription/list - 新增/编辑页继续把同样的
entity/entityId传给subscription/upsert - 配置页单独包
subscription/config/upsert
我这次也额外对 haina-busi、taicang 做了定点检索,目前没有找到它们直接复用这组公共 subscription/* 页面的现成包法。更合理的理解是:这套组件已经够搭基础后台,但最终“订阅谁、同步谁、入口放哪儿”通常仍由项目层自己薄包一层页面壳。
一个更稳的页面拆法
结合当前组件边界,更推荐项目里按下面方式拆:
- 对象详情页里挂
subscription/list - 新增/编辑页挂
subscription/upsert - 渠道配置页单独挂
subscription/config/upsert - 同步偏移量、最近同步时间、同步日志等监控信息,项目层自己再补一张页面
这样职责会比较清楚:
- 公共组件管“订阅源对象”
- 项目逻辑管“订阅同步过程”
使用示例
1. 创建一个挂在业务对象上的订阅源
根据 src/entities/Subscription.ts 的定义,项目层最小数据骨架通常是:
await this.features.cache.operate('subscription', {
id: generateNewId(),
action: 'create',
data: {
id: generateNewId(),
entity: 'articleMenu',
entityId: menuId,
name: '帮助中心订阅号',
description: '用于推送帮助中心更新',
config: {
type: 'wechatPublic',
appId: 'wx-app-id',
appSecret: 'wx-app-secret',
},
},
});
2. 管理台直接复用现成组件
如果你只是做后台,最省事的做法通常是直接接出这几个页面:
/subscription/detail/subscription/upsert/subscription/config/upsert
公共组件已经围绕这些路径和数据结构组织好了。
例如,一个最小的“某业务对象下的订阅源列表页”通常就可以这样包:
<SubscriptionList
oakPath="#Subscriptions"
entity="articleMenu"
entityId={menuId}
/>
而新增页继续把相同上下文带进去即可:
<SubscriptionUpsert
oakPath="#SubscriptionUpsert"
entity="articleMenu"
entityId={menuId}
/>
使用建议
如果你看到这里只有对象和组件,没有自动同步逻辑,不要觉得奇怪。
更合理的理解是:
Subscription负责定义“订阅了什么”和“同步到了哪里”,至于“怎么同步”,通常应该根据具体业务场景在项目层补充 aspect、trigger 或 timer。