@zelostech/partition
v0.2.8
Published
前端分区包,用于管理和处理多分区环境下的请求路由和配置管理。
Readme
@zelos/partition
前端分区包,用于管理和处理多分区环境下的请求路由和配置管理。
功能特点
- 自动管理多分区环境的请求路由
- 支持基于车辆的分区动态路由
- 提供分区信息的缓存机制
- 事件驱动的状态通知
- 提供 React Hooks 简化状态管理
安装
npm install @zelos/partition --save
yarn add @zelos/partition
pnpm add @zelos/partition快速开始
import { PartitionService } from '@zelos/partition';
import Axios from 'axios';
import zauth from '@zelos/oauth';
const baseURL = AdminConfig.getDerivedAppUrl('gateway');
const request = zauth
.extend(Axios, {
client_id: AdminConfig.authClientId,
client_secret: AdminConfig.authClientSecret,
redirect_uri: `${window.location.protocol}//${window.location.host}`,
host: AdminConfig.getDerivedAppDomain('auth'),
})
.create({
baseURL,
timeout: 1000 * 60,
});
// 初始化时注入`分区`和`车辆分区`的请求拦截器
PartitionService.getInstance({
// 可选,默认分区
defaultPartition: 'default-partition',
// 必填,请求客户端
requestClient: request,
}).initialize();API 文档
PartitionService
静态方法
getInstance(config?: PartitionConfig): PartitionService- 获取 PartitionService 的单例实例
- 首次调用时必须提供配置
destroy(): void- 销毁当前实例,清除所有缓存和拦截器
实例方法
initialize(): PartitionService- 初始化分区服务
- 安装请求拦截器,使其生效
- 注意: 此方法现在是同步的并返回
this
isInitialized(): boolean- 检查分区服务是否已初始化
getPartitionEnabledAsync(): Promise<boolean>- 异步获取当前分区是否启用
getPartitionEnabled(): boolean | null- 同步获取当前分区是否启用
getPartitionsAsync(): Promise<Partition[]>- 异步获取所有可用分区列表(包括默认分区)
- 如果首次获取失败,会尝试从本地存储加载
getPartitions(): Partition[]- 同步获取已加载的分区列表
- 如果分区尚未加载,返回空数组
setCurrentPartition(partitionCode: string | null | undefined): void- 设置当前活动分区
- 会触发
partitionChanged和fullPartitionChanged事件
setCurrentPartitionByVehicle(vehicleName: string): Promise<string | null | undefined>- 根据车辆名称异步获取并设置当前分区
- 会触发
partitionChanged和fullPartitionChanged事件 - 返回设置的分区代码或
null/undefined
getCurrentPartition(): string | null | undefined- 同步获取当前活动分区的代码
getCurrentFullPartition(): Partition | null | undefined- 同步获取当前活动分区的完整信息对象
getCurrentFullPartitionAsync(): Promise<Partition | null>- 异步获取当前活动分区的完整信息对象
getPartitionByVehicle(vehicleName: string, cancelToken?: CancelToken): Promise<Partition | null>- 根据车辆名称异步获取对应的分区信息(不设置当前分区)
- 内部使用缓存,可通过
clearVehiclePartitionCache清除
clearVehiclePartitionCache(vehicleName?: string): void- 清除车辆分区缓存
- 如果不提供车辆名称,则清除所有缓存
getConfig(): PartitionConfig- 获取当前实例的配置信息
initPartitions(defaultFirstPartition?: boolean): Promise<Partition[]>- 手动触发分区列表的初始化加载
defaultFirstPartition: 是否将返回列表中的第一个分区设为默认当前分区
getCurrentError(): unknown | null | undefined- 获取服务初始化或运行过程中捕获的最后一个错误
事件
服务会触发以下事件,可以使用 on, once, off 方法监听:
| 事件名称 | 回调参数 | 描述 | | --- | --- | --- | | initialized | 无 | 服务初始化完成时触发 | | error | (error: unknown) | 发生错误时触发 | | enabledChanged | (enabled: boolean) | 分区是否启用状态变更时触发 | | partitionsLoaded | (partitions: Partition[]) | 分区列表加载完成时触发 | | partitionChanged | (partitionCode: string | null | undefined) | 当前分区代码变更时触发 | | fullPartitionChanged | (fullPartition: Partition | null | undefined) | 当前完整分区信息对象变更时触发 |
示例:
partitionService.on('partitionChanged', (partitionCode) => {
console.log('当前分区代码已更改为:', partitionCode);
});
partitionService.on('fullPartitionChanged', (fullPartition) => {
console.log('当前完整分区信息已更改为:', fullPartition);
});类型定义
interface PartitionConfig {
defaultPartition?: string | null | undefined; // 默认分区代码,可以是 null 或 undefined
requestClient: AxiosInstance; // 必须提供 Axios 实例
skipPartitionRules?: Array<(config: InternalAxiosRequestConfig) => boolean>; // 可选的跳过规则
}
interface Partition {
code: string;
name: string;
gatewayHost: string;
mqttHost: string;
}
// 事件接口
interface PartitionServiceEvents {
initialized: () => void;
enabledChanged: (enabled: boolean) => void;
error: (error: unknown) => void;
partitionsLoaded: (partitions: Partition[]) => void;
partitionChanged: (partitionCode: string | null | undefined) => void;
fullPartitionChanged: (fullPartition: Partition | null | undefined) => void;
}React Hooks
为了方便在 React 应用中使用分区状态,包提供了以下 Hooks:
usePartitionEnabled(): boolean | null- 获取当前分区是否启用。
- 当分区启用状态变化时,组件会自动重新渲染。
useCurrentPartition(): [string | null | undefined, (partition: string) => void]- 获取当前分区代码,并提供一个设置当前分区的函数。
- 当分区变化时,组件会自动重新渲染。
useCurrentFullPartition(): Partition | null | undefined- 获取当前完整的 Partition 对象。
- 当分区变化时,组件会自动重新渲染。
usePartitions(): Partition[]- 获取当前已加载的所有分区列表。
- 当分区列表加载完成时,组件会自动重新渲染。
useCurrentError(): unknown | null | undefined- 获取
PartitionService实例当前的错误状态。 - 当
error事件触发时,组件会自动重新渲染。
- 获取
示例:
import { useCurrentPartition, usePartitions } from '@zelos/partition';
function PartitionSelector() {
const [currentPartition, setCurrentPartition] = useCurrentPartition();
const partitions = usePartitions();
const handleChange = (event) => {
setCurrentPartition(event.target.value);
};
return (
<select value={currentPartition ?? ''} onChange={handleChange}>
{partitions.map((p) => (
<option key={p.code} value={p.code}>
{p.name}
</option>
))}
</select>
);
}注意事项
- 使用前必须先调用
initialize()方法,该方法是同步的。 - 服务使用单例模式,多次调用
getInstance()返回相同实例。 - 请求拦截器会自动处理分区路由,无需手动设置 Header (除非需要单次请求覆盖)。
- 车辆分区信息会被缓存,如需刷新请调用
clearVehiclePartitionCache()。 - React Hooks 依赖于
PartitionService的实例,请确保在使用 Hooks 前已调用getInstance和initialize。
示例
单请求根据车辆分区切换
优先级最高,会自动路由到对应分区
// 发送请求时在 headers 中包含 X-Partition-Vehicle 头,服务会自动路由到对应分区
const response = await axios.get('/api/endpoint', {
headers: {
'X-Partition-Vehicle': 'ZL00004',
},
});单请求分区切换
// 发送请求时在 headers 中包含 X-Partition-Code 头,服务会自动路由到对应分区
const response = await axios.get('/api/endpoint', {
headers: {
'X-Partition-Code': 'partition-2',
},
});单请求跳过分区切换
// 发送请求时在 headers 中包含 X-Partition-Skip 头,服务会跳过分区路由
const response = await axios.get('/api/endpoint', {
headers: {
'X-Partition-Skip': 'true',
},
});全局分区切换
// 切换当前分区
partitionService.setCurrentPartition('partition-2');
// 根据车辆切换全局分区
partitionService.setCurrentPartitionByVehicle('ZL00004');切换完成,所有的请求(除掉要跳过的),都会根据当前分区进行路由
自定义跳过分区规则
// 初始化 PartitionService 时可以这样配置
PartitionService.getInstance({
requestClient: axios,
skipPartitionRules: [
// 跳过所有 GET 请求
(config) => config.method === 'get',
// 跳过特定 URL
(config) => config.url?.startsWith('/api/public'),
// 跳过带有特定 header 的请求
(config) => Boolean(config.headers?.['skip-partition']),
],
}).initialize();websocket 分区切换
import { useCurrentFullPartition } from '@zelos/partition';
import { useMemo, useEffect } from 'react';
function useWebSocket(url: string) {
const partition = useCurrentFullPartition();
const partitionUrl = useMemo(() => {
if (!partition?.gatewayHost) {
return url;
}
if (!url) {
return null;
}
const urlObj = new URL(url);
urlObj.host = partition.gatewayHost;
return urlObj.toString();
}, [partition?.gatewayHost, url]);
useEffect(() => {
if (!partitionUrl) {
return;
}
const ws = new WebSocket(partitionUrl);
// ....
return () => {
ws.close();
};
}, [partitionUrl]);
}特别提示
该包请求使用的是传进来的 requestClient 实例,如果对 response 有特殊处理,可能会造成报错。所以特殊处理 response 时,请根据 X-Partition-Request 跳过处理逻辑,不要破坏原有 response 结构。例如:
import { PARTITION_REQUEST_HEADER } from '@zelos/partition';
request.interceptors.response.use(function (response) {
const { headers = {} } = response.config;
// 如果请求头中包含 X-Partition-Request 头,则跳过处理逻辑
if (headers[PARTITION_REQUEST_HEADER] === 'true') {
return response;
}
// 否则按照原有逻辑处理
// ....
});