Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

System、Passport 与系统级配置

Systemoak-general-business 里最顶层的业务配置对象。很多新手刚看到它时,会把它当成一个普通的“系统信息表”;但在 Oak 里,它的作用更接近“整套通用业务逻辑的系统级根配置”。

密码规则、邮箱能力、地图服务、样式、最低版本约束、默认登录方式,这些看起来互不相关的能力,最终都要么直接挂在 system.config 上,要么通过 system 去找到对应的 passportapplicationplatform

主要对象

这一章最重要的两个实体是:

  • System:定义系统名称、描述、配置、样式、所属平台以及最低版本等系统级信息;
  • Passport:定义一个系统允许出现哪些登录方式,例如 smsemailloginNamewechatPublicForWebwechatMpForWeboauth 等。

其中 Passport 不是“用户已经启用的登录方式”,而是“系统层面允许提供哪些登录入口”。真正落实到某个应用上,还要看后面的 ApplicationPassport

组件

围绕这两个对象,oak-general-business 已经提供了几组常用组件:

  • src/components/system/detail
  • src/components/system/panel
  • src/components/system/passport
  • src/components/system/upsert
  • src/components/passport

此外,src/components/platform/detailplatform/panelplatform/systemplatform/upsert 这组组件虽然属于 Platform,但在实际管理后台里通常也是和 System 一起出现的。

组件适合放在哪里

结合 haina-busitaicang 的现有页面,最常见的放法其实很固定:

  • system/panel:放在系统详情页、系统配置页,例如 haina-busi/src/pages/business/system/web.pc.tsxtaicang/src/pages/console/system/panel/web.pc.tsx
  • system/upsert:放在系统创建页或系统基本信息编辑页,taicang 的系统控制台就是这种拆法
  • platform/panel:放在平台详情页,适合把平台级附加配置做成页签挂进去,haina-busi/src/pages/business/platform/web.pc.tsx 就传了自定义 tabs

system/panel / platform/panel 常用参数

这两个组件项目层最常传的参数其实很少,核心就是:

  • oakId:当前 systemIdplatformId
  • oakPath:当前页面下的数据节点路径
  • tabs:给公共面板追加项目自己的页签

从源码看,system/panel 自带的投影里已经包含:

  • name
  • config
  • description
  • oldestVersion
  • super
  • platformId
  • style
  • translation
  • translateState
  • translationError
  • domain$system

所以它并不是一个“空壳 tab 容器”,而是已经默认把系统基础资料、样式、域名等常用配置一起带上了。

system/panel / platform/panel 内置了哪些页签

这两个组件的另一个关键点,是它们其实已经内置了系统管理后台的大部分标准结构。

system/panel 默认包含:

  • detail
  • config
  • translation(内含翻译配置与翻译数据管理)
  • style
  • application-list
  • domain-list
  • smsTemplate-list
  • login
  • oauth-manage

platform/panel 默认包含:

  • detail
  • config
  • style
  • system-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

它最关键的参数也很少:

  • systemId
  • systemName
  • oakPath

这意味着它适合直接作为“系统登录配置”页签挂进 system/panel 或项目自己的系统详情页里,而不是让项目层手工再拼一次“系统级登录方式 + 应用级登录方式”。

从职责上看,这个组件解决的是两层配置连续操作的问题:

  • 先在系统层确认有哪些 passport
  • 再在应用层确认每个 application 实际启用哪些 passport

这两个动作如果拆成两张完全独立的页面,新手很容易不知道应该先配哪一层;而 system/passport 的组合页签正好把这个顺序固定下来了。

前端入口

系统级配置相关的前端入口主要不是专门的 system feature,而是 features.config

  • updateConfig(...)
  • updateStyle(...)

对应的后端 aspect 位于 src/aspects/config.ts

  • updateConfig
  • updateStyle

这也说明一个很典型的 Oak 设计习惯:有些对象本身不一定有独立 feature,但会通过更通用的 feature 来暴露操作入口。

后台规则

这一章最值得认真读的是 src/triggers/system.tssrc/triggers/passport.ts

System 的触发器会自动维护登录方式基础设施:

  • 新建 system 时,自动创建 smsloginName 类型的 passport
  • 更新 system.config.Emails 时,自动创建、启用、关闭或删除 email 类型的 passport
  • 删除 system 时,自动清理相关 passport

Passport 的触发器则负责把系统级登录方式和应用级登录方式解耦:

  • 禁用 passport 时,删除关联的 applicationPassport
  • 删除 passport 时,也同步删除关联的 applicationPassport

对应的 checker 在 src/checkers/system.ts 中,负责约束系统配置的合法性。

注入点

这一组能力的注入点有两个:

  • 后端通过 ogb0Triggersogb0Checkers 合并进入项目初始化;
  • 前端通过 createOgb0Features(...) 创建出的 features.config 暴露配置修改入口。

也就是说,System / Passport 不是一个“手工管理的静态配置区”,而是已经被接到 Oak 运行时里的。

项目中如何接入

System 这一章在项目里的接入,通常不是“写一个页面去查 system 表”这么简单,而是三层一起接:

  • 初始化阶段合并 oak-general-businesstriggers / checkers / aspects / watchers / common / routines
  • 前端创建并初始化 ogb0Features
  • 后台或管理台通过 System.configPassportApplicationPassport 配出系统级能力。

bm-smart 这类项目里,最小接入代码就是:

const ogb0Features = createOgb0Features(totalFeatures);
Object.assign(totalFeatures, ogb0Features);

await initializeOgb0Features(
  features,
  accessConfiguration,
  undefined,
  [Qiniu, S3, Aliyun]
);

这一层接好以后,后面用户、token、文件、微信、短信等能力才会按 system.config 真正工作。

真实项目里的页面包法

haina-busitaicang 都没有自己重写整套系统配置后台,而是用一个很薄的页面壳去包公共组件:

<SystemPanel
  oakId={systemId}
  oakPath={`${oakFullpath}.system`}
/>

这也是更推荐的项目层写法。系统配置页尽量只负责:

  • 从路由或父节点拿到 systemId
  • 传递稳定的 oakPath
  • 根据项目需要补一两个自定义页签

不要把 SystemPassportApplicationPassport 的增删改查又在项目里重做一遍。

开发注意事项

如果你准备扩展系统管理页,最稳妥的方式通常不是改公共组件内部,而是继续往 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>/indexwechatQrCodeExpireSeconds 是微信临时二维码有效期,单位秒,不配置时默认 2592000,超过微信上限也会按 2592000 处理。

2. 用组件管理登录方式,而不是自己拼表单

系统级登录方式和应用级登录方式,推荐直接复用这些现成组件:

  • src/components/passport/*
  • src/components/applicationPassport
  • src/components/config/upsert
  • src/components/config/application

这样后续 components/user/loginfeatures.token、微信登录组件拿到的就是同一套配置,不会出现“页面判断一套、后台校验另一套”的问题。

使用建议

对于一个新项目,最推荐的顺序是:

  1. 先创建并配置 System
  2. 再确认 system.config 中的 PasswordEmailsMapSecurity 等系统级规则;
  3. 然后检查系统级 Passport 是否已经被自动建立;
  4. 最后再去每个 Application 上配置具体启用哪些登录方式。

如果把这几个步骤反过来做,就很容易出现“界面上有登录组件,但后端并没有正确的 passport 和配置支撑”的情况。