System、Passport 与系统级配置
System 是 oak-general-business 里最顶层的业务配置对象。很多新手刚看到它时,会把它当成一个普通的“系统信息表”;但在 Oak 里,它的作用更接近“整套通用业务逻辑的系统级根配置”。
密码规则、邮箱能力、地图服务、样式、最低版本约束、默认登录方式,这些看起来互不相关的能力,最终都要么直接挂在 system.config 上,要么通过 system 去找到对应的 passport、application 和 platform。
主要对象
这一章最重要的两个实体是:
System:定义系统名称、描述、配置、样式、所属平台以及最低版本等系统级信息;Passport:定义一个系统允许出现哪些登录方式,例如sms、email、loginName、wechatPublicForWeb、wechatMpForWeb、oauth等。
其中 Passport 不是“用户已经启用的登录方式”,而是“系统层面允许提供哪些登录入口”。真正落实到某个应用上,还要看后面的 ApplicationPassport。
组件
围绕这两个对象,oak-general-business 已经提供了几组常用组件:
src/components/system/detailsrc/components/system/panelsrc/components/system/passportsrc/components/system/upsertsrc/components/passport
此外,src/components/platform/detail、platform/panel、platform/system、platform/upsert 这组组件虽然属于 Platform,但在实际管理后台里通常也是和 System 一起出现的。
组件适合放在哪里
结合 haina-busi 和 taicang 的现有页面,最常见的放法其实很固定:
system/panel:放在系统详情页、系统配置页,例如haina-busi/src/pages/business/system/web.pc.tsx、taicang/src/pages/console/system/panel/web.pc.tsxsystem/upsert:放在系统创建页或系统基本信息编辑页,taicang的系统控制台就是这种拆法platform/panel:放在平台详情页,适合把平台级附加配置做成页签挂进去,haina-busi/src/pages/business/platform/web.pc.tsx就传了自定义tabs
system/panel / platform/panel 常用参数
这两个组件项目层最常传的参数其实很少,核心就是:
oakId:当前systemId或platformIdoakPath:当前页面下的数据节点路径tabs:给公共面板追加项目自己的页签
从源码看,system/panel 自带的投影里已经包含:
nameconfigdescriptionoldestVersionsuperplatformIdstyletranslationtranslateStatetranslationErrordomain$system
所以它并不是一个“空壳 tab 容器”,而是已经默认把系统基础资料、样式、域名等常用配置一起带上了。
system/panel / platform/panel 内置了哪些页签
这两个组件的另一个关键点,是它们其实已经内置了系统管理后台的大部分标准结构。
system/panel 默认包含:
detailconfigtranslation(内含翻译配置与翻译数据管理)styleapplication-listdomain-listsmsTemplate-listloginoauth-manage
platform/panel 默认包含:
detailconfigstylesystem-list
所以项目层如果只是要做常规系统后台,通常根本不需要重写页面骨架;真正需要自定义的,往往只是再追加一两个业务 tab。
haina-busi 的平台页就是一个很典型的例子:
<PlatformPanel
oakId={platformId}
oakPath={`${oakFullpath}-PlatformPanel`}
tabs={[
{
label: '演示账号设置',
key: 'demo-account-config',
children: <DemoAccountConfig oakId={platformId} oakPath={`${oakFullpath}-DemoAccountConfig`} />,
},
]}
/>
system/passport 是一个组合组件
很多人第一次看到“登录配置”时,会分别去找 passport 列表和 applicationPassport 列表。但从 oak-general-business/src/components/system/passport/web.pc.tsx 看,公共包其实已经把这两块封成了一个组合组件:
- 第一个 tab 是
PassportList - 第二个 tab 是
ApplicationPassport
它最关键的参数也很少:
systemIdsystemNameoakPath
这意味着它适合直接作为“系统登录配置”页签挂进 system/panel 或项目自己的系统详情页里,而不是让项目层手工再拼一次“系统级登录方式 + 应用级登录方式”。
从职责上看,这个组件解决的是两层配置连续操作的问题:
- 先在系统层确认有哪些
passport - 再在应用层确认每个
application实际启用哪些passport
这两个动作如果拆成两张完全独立的页面,新手很容易不知道应该先配哪一层;而 system/passport 的组合页签正好把这个顺序固定下来了。
前端入口
系统级配置相关的前端入口主要不是专门的 system feature,而是 features.config:
updateConfig(...)updateStyle(...)
对应的后端 aspect 位于 src/aspects/config.ts:
updateConfigupdateStyle
这也说明一个很典型的 Oak 设计习惯:有些对象本身不一定有独立 feature,但会通过更通用的 feature 来暴露操作入口。
后台规则
这一章最值得认真读的是 src/triggers/system.ts 和 src/triggers/passport.ts。
System 的触发器会自动维护登录方式基础设施:
- 新建
system时,自动创建sms和loginName类型的passport; - 更新
system.config.Emails时,自动创建、启用、关闭或删除email类型的passport; - 删除
system时,自动清理相关passport。
Passport 的触发器则负责把系统级登录方式和应用级登录方式解耦:
- 禁用
passport时,删除关联的applicationPassport; - 删除
passport时,也同步删除关联的applicationPassport。
对应的 checker 在 src/checkers/system.ts 中,负责约束系统配置的合法性。
注入点
这一组能力的注入点有两个:
- 后端通过
ogb0Triggers、ogb0Checkers合并进入项目初始化; - 前端通过
createOgb0Features(...)创建出的features.config暴露配置修改入口。
也就是说,System / Passport 不是一个“手工管理的静态配置区”,而是已经被接到 Oak 运行时里的。
项目中如何接入
System 这一章在项目里的接入,通常不是“写一个页面去查 system 表”这么简单,而是三层一起接:
- 初始化阶段合并
oak-general-business的triggers / checkers / aspects / watchers / common / routines; - 前端创建并初始化
ogb0Features; - 后台或管理台通过
System.config、Passport、ApplicationPassport配出系统级能力。
在 bm-smart 这类项目里,最小接入代码就是:
const ogb0Features = createOgb0Features(totalFeatures);
Object.assign(totalFeatures, ogb0Features);
await initializeOgb0Features(
features,
accessConfiguration,
undefined,
[Qiniu, S3, Aliyun]
);
这一层接好以后,后面用户、token、文件、微信、短信等能力才会按 system.config 真正工作。
真实项目里的页面包法
haina-busi 和 taicang 都没有自己重写整套系统配置后台,而是用一个很薄的页面壳去包公共组件:
<SystemPanel
oakId={systemId}
oakPath={`${oakFullpath}.system`}
/>
这也是更推荐的项目层写法。系统配置页尽量只负责:
- 从路由或父节点拿到
systemId - 传递稳定的
oakPath - 根据项目需要补一两个自定义页签
不要把 System、Passport、ApplicationPassport 的增删改查又在项目里重做一遍。
开发注意事项
如果你准备扩展系统管理页,最稳妥的方式通常不是改公共组件内部,而是继续往 tabs 里挂项目自己的块。因为:
system/panel已经依赖了固定的系统投影platform/panel也是同样的设计- 项目层只补自定义 tab,最不容易和公共包后续升级冲突
使用示例
1. 在管理台更新系统配置
oak-general-business/src/types/Config.ts 已经把 System.config 的结构定义得很清楚了,所以项目里最推荐直接通过 features.config.updateConfig(...) 去更新:
await this.features.config.updateConfig('system', systemId, {
App: {
scanPage: 'wechatQrCode/scan',
wechatQrCodeExpireSeconds: 2592000,
tokenRefreshTime: 60 * 60 * 1000,
tokenExpireTime: 7 * 24 * 60 * 60 * 1000,
needManualVerification: true,
},
Password: {
min: 8,
max: 24,
verify: true,
regexs: ['^(?=.*[A-Z])(?=.*\\d).+$'],
},
Sms: {
defaultOrigin: 'tencent',
},
Map: {
amaps: [{ key: 'your-amap-key', type: 'web' }],
},
});
这里的 scanPage 是微信扫码后的中转页逻辑路径,默认 wechatQrCode/scan;小程序侧会自动拼成 pages/<scanPage>/index。wechatQrCodeExpireSeconds 是微信临时二维码有效期,单位秒,不配置时默认 2592000,超过微信上限也会按 2592000 处理。
2. 用组件管理登录方式,而不是自己拼表单
系统级登录方式和应用级登录方式,推荐直接复用这些现成组件:
src/components/passport/*src/components/applicationPassportsrc/components/config/upsertsrc/components/config/application
这样后续 components/user/login、features.token、微信登录组件拿到的就是同一套配置,不会出现“页面判断一套、后台校验另一套”的问题。
使用建议
对于一个新项目,最推荐的顺序是:
- 先创建并配置
System; - 再确认
system.config中的Password、Emails、Map、Security等系统级规则; - 然后检查系统级
Passport是否已经被自动建立; - 最后再去每个
Application上配置具体启用哪些登录方式。
如果把这几个步骤反过来做,就很容易出现“界面上有登录组件,但后端并没有正确的 passport 和配置支撑”的情况。