harinsangma2-deconstruction-engine
v1.1.5
Published
A flexible data deconstruction engine for transforming upstream API data into internal data structures
Maintainers
Readme
Data Deconstruction Engine
一个灵活的数据解构引擎,用于将上游供应商API数据转换为项目内部数据结构。
🚀 特性
- 🔧 灵活配置: 支持配置文件和装饰器两种方式定义API接口和数据映射规则
- ⚡ 强大的数据转换: 支持多种数据转换器(日期、JSON、条件、表达式等)
- 🔗 处理器链: 支持前置和后置处理器,可自定义扩展
- 📝 类型安全: 完整的TypeScript类型定义,支持装饰器类型推导
- 🚄 高性能: 内置缓存、重试机制和并发控制
- 🛡️错误处理: 完善的错误处理和日志记录
- 🔌 可扩展: 支持自定义处理器和转换器
- 📦 独立部署: 可作为独立npm包在其他项目中使用
- 🎯 装饰器支持: 支持基于类和装饰器的优雅API定义方式
📦 安装
# 使用 npm
npm install harinsangma2-deconstruction-engine
# 使用 yarn
yarn add harinsangma2-deconstruction-engine
# 使用 pnpm
pnpm add harinsangma2-deconstruction-engine📋 系统要求
- Node.js >= 16.0.0
- TypeScript >= 4.5.0 (如果使用TypeScript)
🎯 快速开始
引擎支持三种主要的使用方式:配置文件方式、单供应商装饰器方式和多供应商装饰器方式。每种方式都有其适用场景和优势。
方式一:配置文件方式
配置文件方式提供了灵活的API定义和字段映射配置,适合复杂的业务场景和需要动态配置的项目。
import { DeconstructionEngine, DataType, TransformerType, LogLevel } from 'harinsangma2-deconstruction-engine';
import { WestCnAuthPreProcessor, WestCnErrorHandlerProcessor } from './processors';
// 创建引擎实例
const engine = new DeconstructionEngine({
enableDebug: true,
enableCache: true,
timeout: 30000,
logLevel: LogLevel.INFO
});
// 注册处理器
engine.registerPreProcessor(new WestCnAuthPreProcessor());
engine.registerPostProcessor(new WestCnErrorHandlerProcessor());
// 定义供应商配置
const supplierConfig = {
name: 'west_cn',
baseUrl: 'https://api.west.cn',
version: '2.0',
description: '西部数码API服务',
// 处理器专用配置
processorConfigs: {
'west-cn-auth': {
username: 'your_username',
password: 'your_password'
}
},
apis: {
getDomainPrice: {
name: 'getDomainPrice',
path: '/api/v2/info/',
method: 'POST',
description: '查询域名价格',
// 固定参数
params: {
act: 'getprice',
type: 'domain'
},
// 请求参数定义和验证
requestParams: {
value: {
type: DataType.STRING,
required: false,
description: '要查询的域名',
validation: {
pattern: '^[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$'
}
},
year: {
type: DataType.STRING,
required: false,
defaultValue: '1',
description: '购买年限'
}
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json'
},
// 字段映射配置
fieldMapping: {
'data.buyprice': {
mapping: 'price',
type: DataType.NUMBER,
defaultValue: 0,
description: '购买价格',
transformer: {
type: TransformerType.CUSTOM_EXPRESSION,
expression: 'parseFloat(value) || 0'
}
},
'data.renewprice': {
mapping: 'renewPrice',
type: DataType.NUMBER,
defaultValue: 0,
description: '续费价格',
transformer: {
type: TransformerType.CUSTOM_EXPRESSION,
expression: 'parseFloat(value) || 0'
}
},
'data.proid': {
mapping: 'productId',
type: DataType.STRING,
defaultValue: ''
},
'data.buyyear': {
mapping: 'buyYear',
type: DataType.NUMBER,
defaultValue: 1
}
},
preProcessors: ['request-validation', 'params', 'west-cn-auth'],
postProcessors: ['west-cn-error-handler', 'field-mapping', 'clean-field-mapping'],
timeout: 15000
}
}
};
// 注册供应商
engine.registerSupplier(supplierConfig);
// 执行API调用
const result = await engine.execute('west_cn', 'getDomainPrice', {
value: 'example.com',
year: '1'
});
if (result.success) {
console.log('转换后的数据:', result.data);
// 输出: { price: 68.0, renewPrice: 88.0, productId: 'domain_com', buyYear: 1 }
}方式二:单供应商装饰器方式
装饰器方式提供了类型安全的API定义,适合需要强类型检查的项目。
import { ApiDataClass, Field, Transform, Supplier } from 'harinsangma2-deconstruction-engine';
import { DataType, TransformerType } from 'harinsangma2-deconstruction-engine';
/**
* 西部数码域名价格查询装饰器类
*/
@Supplier('west_cn', {
version: '2.0.0',
description: '西部数码API供应商'
})
@ApiDataClass({
name: 'getDomainPrice',
path: '/api/v2/info/',
method: 'POST',
description: '查询域名价格',
strict: true,
// 固定参数
params: {
act: 'getprice',
type: 'domain'
},
// 请求参数定义
requestParams: {
value: {
type: DataType.STRING,
required: false,
description: '要查询的域名',
validation: {
pattern: '^[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$'
}
},
year: {
type: DataType.STRING,
required: false,
defaultValue: '1',
description: '购买年限',
validation: {
enumValues: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
}
}
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json'
},
preProcessors: ['request-validation', 'params', 'west-cn-auth'],
postProcessors: ['west-cn-error-handler', 'field-mapping', 'clean-field-mapping'],
timeout: 10000
})
export class WestCnDomainPriceMapping {
// 购买价格字段
@Field({
from: 'data.buyprice',
type: DataType.NUMBER,
defaultValue: 0,
description: '购买价格'
})
@Transform({
type: TransformerType.CUSTOM_EXPRESSION,
expression: 'parseFloat(value) || 0'
})
price: number = 0;
// 续费价格字段
@Field({
from: 'data.renewprice',
type: DataType.NUMBER,
defaultValue: 0,
description: '续费价格'
})
@Transform({
type: TransformerType.CUSTOM_EXPRESSION,
expression: 'parseFloat(value) || 0'
})
renewPrice: number = 0;
// 产品ID字段
@Field({
from: 'data.proid',
type: DataType.STRING,
defaultValue: '',
description: '产品ID'
})
productId: string = '';
// 购买年限字段
@Field({
from: 'data.buyyear',
type: DataType.NUMBER,
defaultValue: 1,
description: '购买年限'
})
@Transform({
type: TransformerType.CUSTOM_EXPRESSION,
expression: 'parseInt(value) || 1'
})
buyYear: number = 1;
/**
* 验证数据有效性
*/
isValid(): boolean {
return this.price >= 0 && this.renewPrice >= 0;
}
/**
* 获取格式化价格
*/
getFormattedPrices(): { [key: string]: string } {
return {
buy: `¥${this.price.toFixed(2)}`,
renew: `¥${this.renewPrice.toFixed(2)}`
};
}
}
// 使用装饰器类
const engine = new DeconstructionEngine();
const result = await engine.executeByClass(WestCnDomainPriceMapping, {
value: 'example.com',
year: '1'
}, 'west_cn');
if (result.success) {
const data = result.data as WestCnDomainPriceMapping;
console.log('域名价格:', data.getFormattedPrices());
console.log('是否有效:', data.isValid());
}方式三:多供应商装饰器方式
多供应商装饰器方式支持同一个业务逻辑对接多个不同的供应商API,提供故障转移和负载均衡能力。
多供应商域名价格查询示例
import { MultiSupplier, MultiField, ApiDataClass, Transform, Validate } from 'harinsangma2-deconstruction-engine';
import { DataType, TransformerType } from 'harinsangma2-deconstruction-engine';
/**
* 多供应商域名价格查询装饰器类
* 支持西部数码的多个节点,提供故障转移能力
*/
@MultiSupplier({
suppliers: {
west_cn_primary: {
name: 'getDomainPrice',
path: '/api/v2/info/',
method: 'POST',
description: '查询域名价格(主供应商)',
params: {
act: 'getprice',
type: 'domain'
},
requestParams: {
value: {
type: DataType.STRING,
required: true,
description: '要查询的域名',
validation: {
pattern: '^[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$'
}
},
year: {
type: DataType.STRING,
required: true,
defaultValue: '1',
description: '购买年限',
validation: {
enumValues: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
}
}
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json'
},
preProcessors: ['request-validation', 'params', 'west-cn-auth'],
postProcessors: ['clean-field-mapping', 'west-cn-error-handler'],
timeout: 10000
},
west_cn_backup: {
name: 'getDomainPrice',
path: '/api/v2/info/',
method: 'POST',
description: '查询域名价格(备用供应商)',
// ... 类似配置,timeout: 8000
},
west_cn_fast: {
name: 'getDomainPrice',
path: '/api/v2/info/',
method: 'POST',
description: '查询域名价格(快速供应商)',
// ... 类似配置,timeout: 5000
}
},
defaultSupplier: 'west_cn_primary',
selectionStrategy: 'failover',
enableFailover: true,
failoverOrder: ['west_cn_primary', 'west_cn_backup', 'west_cn_fast']
})
@ApiDataClass({
name: 'MultiSupplierDomainPrice',
description: '多供应商域名价格查询数据类'
})
export class MultiSupplierDomainPriceMapping {
// 购买价格 - 支持多供应商映射
@MultiField({
suppliers: {
west_cn_primary: {
from: 'data.buyprice',
type: DataType.NUMBER,
defaultValue: 0,
description: '购买价格(主供应商)'
},
west_cn_backup: {
from: 'data.buyprice',
type: DataType.NUMBER,
defaultValue: 0,
description: '购买价格(备用供应商)'
},
west_cn_fast: {
from: 'data.buyprice',
type: DataType.NUMBER,
defaultValue: 0,
description: '购买价格(快速供应商)'
}
}
})
@Transform({
type: TransformerType.CUSTOM_EXPRESSION,
expression: 'parseFloat(value) || 0'
})
@Validate({
min: 0,
message: '价格不能为负数'
})
price: number = 0;
// 续费价格
@MultiField({
suppliers: {
west_cn_primary: {
from: 'data.renewprice',
type: DataType.NUMBER,
defaultValue: 0,
description: '续费价格(主供应商)'
},
west_cn_backup: {
from: 'data.renewprice',
type: DataType.NUMBER,
defaultValue: 0,
description: '续费价格(备用供应商)'
},
west_cn_fast: {
from: 'data.renewprice',
type: DataType.NUMBER,
defaultValue: 0,
description: '续费价格(快速供应商)'
}
}
})
@Transform({
type: TransformerType.CUSTOM_EXPRESSION,
expression: 'parseFloat(value) || 0'
})
@Validate({
min: 0,
message: '续费价格不能为负数'
})
renewPrice: number = 0;
// 供应商标识字段
@MultiField({
suppliers: {
west_cn_primary: {
from: 'supplier_info.name',
type: DataType.STRING,
defaultValue: 'west_cn_primary',
description: '供应商名称(主供应商)'
},
west_cn_backup: {
from: 'supplier_info.name',
type: DataType.STRING,
defaultValue: 'west_cn_backup',
description: '供应商名称(备用供应商)'
},
west_cn_fast: {
from: 'supplier_info.name',
type: DataType.STRING,
defaultValue: 'west_cn_fast',
description: '供应商名称(快速供应商)'
}
}
})
supplierUsed: string = '';
/**
* 验证数据有效性
*/
isValid(): boolean {
return this.price > 0 && this.renewPrice > 0;
}
/**
* 获取格式化的价格信息
*/
getFormattedPrices(): { buy: string; renew: string } {
return {
buy: `¥${this.price.toFixed(2)}`,
renew: `¥${this.renewPrice.toFixed(2)}`
};
}
}
// 使用多供应商装饰器类
const engine = new DeconstructionEngine();
// 注册多个供应商
engine.registerSupplier({ name: 'west_cn_primary', baseUrl: 'https://api.west.cn', version: '2.0', apis: {} });
engine.registerSupplier({ name: 'west_cn_backup', baseUrl: 'https://backup.west.cn', version: '2.0', apis: {} });
engine.registerSupplier({ name: 'west_cn_fast', baseUrl: 'https://fast.west.cn', version: '2.0', apis: {} });
// 执行多供应商API调用
const result = await engine.executeByClass(MultiSupplierDomainPriceMapping, {
value: 'example.com',
year: '2'
}, 'west_cn_primary');
if (result.success) {
const data = result.data as MultiSupplierDomainPriceMapping;
console.log('域名价格:', data.getFormattedPrices());
console.log('使用供应商:', data.supplierUsed);
console.log('是否有效:', data.isValid());
}复杂多供应商服务器配置查询示例
对于更复杂的业务场景,比如服务器配置查询,涉及嵌套对象和数组:
/**
* 机房线路信息类
*/
export class RoomEbsInfo {
@MultiField({
suppliers: {
west_cn: { from: 'id', type: DataType.NUMBER, defaultValue: 0 }
}
})
id: number = 0;
@MultiField({
suppliers: {
west_cn: { from: 'name', type: DataType.STRING, defaultValue: '' }
}
})
name: string = '';
@MultiField({
suppliers: {
west_cn: { from: 'hcpu', type: DataType.NUMBER, defaultValue: 0 }
}
})
hcpu: number = 0;
}
/**
* 机房信息类
*/
export class RoomInfo {
@MultiField({
suppliers: {
west_cn: { from: 'roomid', type: DataType.NUMBER, defaultValue: 0 }
}
})
roomid: number = 0;
@MultiField({
suppliers: {
west_cn: { from: 'roomname', type: DataType.STRING, defaultValue: '' }
}
})
roomname: string = '';
@MultiField({
suppliers: {
west_cn: { from: 'roomremark', type: DataType.STRING, defaultValue: '' }
}
})
roomremark: string = '';
@MultiField({
suppliers: {
west_cn: { from: 'ebsid', type: DataType.ARRAY, defaultValue: [] }
}
})
ebsid: RoomEbsInfo[] = [];
@MultiField({
suppliers: {
west_cn: { from: 'ddos', type: DataType.ARRAY, defaultValue: [] }
}
})
ddos: number[] = [];
/**
* 是否支持DDoS防护
*/
hasDdosProtection(): boolean {
return this.ddos && this.ddos.length > 0;
}
}
/**
* 操作系统信息类
*/
export class OSInfo {
@MultiField({
suppliers: {
west_cn: { from: 'name', type: DataType.STRING, defaultValue: '' }
}
})
name: string = '';
@MultiField({
suppliers: {
west_cn: { from: 'system', type: DataType.STRING, defaultValue: '' }
}
})
system: string = '';
/**
* 是否为Windows系统
*/
isWindows(): boolean {
return this.system.toLowerCase().includes('windows');
}
/**
* 是否为Linux系统
*/
isLinux(): boolean {
return this.system.toLowerCase().includes('linux');
}
}
/**
* 多供应商服务器配置查询装饰器类
* 支持获取CPU、内存、机房、操作系统等配置选项
*/
@MultiSupplier({
suppliers: {
west_cn: {
name: 'getServerConfig',
path: '/api/v2/server/config/',
method: 'GET',
description: '获取服务器配置选项(西部数码)',
params: {
act: 'base'
},
headers: {
'Accept': 'application/json'
},
preProcessors: ['request-validation'],
postProcessors: ['clean-field-mapping', 'west-cn-error-handler'],
timeout: 10000
},
aliyun: {
name: 'getServerConfig',
path: '/ecs/DescribeInstanceTypes',
method: 'POST',
description: '获取服务器配置选项(阿里云)',
requestParams: {
RegionId: {
type: DataType.STRING,
required: true,
defaultValue: 'cn-hangzhou',
description: '地域ID'
}
},
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
preProcessors: ['request-validation', 'params'],
postProcessors: ['clean-field-mapping'],
timeout: 8000
},
tencent: {
name: 'getServerConfig',
path: '/cvm/DescribeInstanceTypeConfigs',
method: 'POST',
description: '获取服务器配置选项(腾讯云)',
requestParams: {
Region: {
type: DataType.STRING,
required: true,
defaultValue: 'ap-guangzhou',
description: '地域'
}
},
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
preProcessors: ['request-validation', 'params'],
postProcessors: ['clean-field-mapping'],
timeout: 8000
}
},
defaultSupplier: 'west_cn',
selectionStrategy: 'failover',
enableFailover: true,
failoverOrder: ['west_cn', 'aliyun', 'tencent']
})
@ApiDataClass({
name: 'MultiSupplierServerConfig',
description: '多供应商服务器配置选项查询数据类'
})
export class MultiSupplierServerConfigResponse {
// 响应状态码
@MultiField({
suppliers: {
west_cn: { from: 'result', type: DataType.NUMBER, defaultValue: 0 },
aliyun: { from: 'Code', type: DataType.STRING, defaultValue: 'Failed' },
tencent: { from: 'Response.Error.Code', type: DataType.STRING, defaultValue: 'Success' }
}
})
@Transform({
type: TransformerType.CUSTOM_EXPRESSION,
expression: 'typeof value === "string" ? (value === "Success" ? 200 : 500) : parseInt(value) || 0'
})
result: number = 0;
// CPU选项
@MultiField({
suppliers: {
west_cn: { from: 'data.cpu', type: DataType.ARRAY, defaultValue: [] },
aliyun: { from: 'CpuOptions', type: DataType.ARRAY, defaultValue: [] },
tencent: { from: 'Response.CpuOptions', type: DataType.ARRAY, defaultValue: [] }
}
})
cpuOptions: number[] = [];
// 内存选项
@MultiField({
suppliers: {
west_cn: { from: 'data.ram', type: DataType.ARRAY, defaultValue: [] },
aliyun: { from: 'MemoryOptions', type: DataType.ARRAY, defaultValue: [] },
tencent: { from: 'Response.MemoryOptions', type: DataType.ARRAY, defaultValue: [] }
}
})
memoryOptions: number[] = [];
// 机房信息 - 支持复杂对象数组
@MultiField({
suppliers: {
west_cn: { from: 'data.room', type: DataType.ARRAY, defaultValue: [] },
aliyun: { from: 'ZoneList', type: DataType.ARRAY, defaultValue: [] },
tencent: { from: 'Response.ZoneSet', type: DataType.ARRAY, defaultValue: [] }
},
itemType: () => RoomInfo // 指定数组元素的类型
})
rooms: RoomInfo[] = [];
// 操作系统选项 - 支持复杂对象数组
@MultiField({
suppliers: {
west_cn: { from: 'data.os', type: DataType.ARRAY, defaultValue: [] },
aliyun: { from: 'ImageList', type: DataType.ARRAY, defaultValue: [] },
tencent: { from: 'Response.ImageSet', type: DataType.ARRAY, defaultValue: [] }
},
itemType: () => OSInfo // 指定数组元素的类型
})
osOptions: OSInfo[] = [];
// 供应商标识
supplierUsed: string = '';
/**
* 检查响应是否成功
*/
isSuccess(): boolean {
return this.result === 200;
}
/**
* 获取支持DDoS防护的机房
*/
getDdosProtectedRooms(): RoomInfo[] {
return this.rooms.filter(room => room.hasDdosProtection());
}
/**
* 获取Windows操作系统选项
*/
getWindowsOS(): OSInfo[] {
return this.osOptions.filter(os => os.isWindows());
}
/**
* 获取Linux操作系统选项
*/
getLinuxOS(): OSInfo[] {
return this.osOptions.filter(os => os.isLinux());
}
/**
* 根据机房ID查找机房信息
*/
getRoomById(roomId: number): RoomInfo | null {
return this.rooms.find(room => room.roomid === roomId) || null;
}
/**
* 获取配置选项摘要
*/
getConfigSummary(): any {
return {
cpuOptions: this.cpuOptions.length,
memoryOptions: this.memoryOptions.length,
rooms: this.rooms.length,
osOptions: this.osOptions.length,
ddosProtectedRooms: this.getDdosProtectedRooms().length,
windowsOS: this.getWindowsOS().length,
linuxOS: this.getLinuxOS().length
};
}
}
// 使用复杂多供应商装饰器类
const result = await engine.executeByClass(MultiSupplierServerConfigResponse, {}, 'west_cn');
if (result.success) {
const data = result.data as MultiSupplierServerConfigResponse;
console.log('配置摘要:', data.getConfigSummary());
// 输出: { cpuOptions: 8, memoryOptions: 14, rooms: 12, osOptions: 20, ddosProtectedRooms: 3, windowsOS: 8, linuxOS: 12 }
console.log('支持DDoS的机房数量:', data.getDdosProtectedRooms().length);
console.log('Windows系统数量:', data.getWindowsOS().length);
console.log('Linux系统数量:', data.getLinuxOS().length);
// 查找特定机房
const room = data.getRoomById(1);
if (room) {
console.log(`机房信息: ${room.roomname} - ${room.roomremark}`);
console.log(`DDoS防护: ${room.hasDdosProtection() ? '支持' : '不支持'}`);
}
}三种方式对比
| 特性 | 配置文件方式 | 单供应商装饰器 | 多供应商装饰器 | |------|-------------|--------------|--------------| | 类型安全 | 运行时验证 | 编译时验证 | 编译时验证 | | 配置复杂度 | 较高 | 中等 | 高 | | IDE支持 | 基础 | 完整 | 完整 | | 学习成本 | 中等 | 低 | 高 | | 灵活性 | 高 | 中等 | 中等 | | 性能 | 良好 | 优秀 | 良好 | | 故障转移 | 不支持 | 不支持 | 支持 | | 供应商管理 | 手动 | 自动 | 自动 | | 适用场景 | 动态配置 | 单一API | 多供应商集成 |
选择建议
- 配置文件方式:适合需要动态配置、复杂业务逻辑或者需要在运行时修改API配置的场景
- 单供应商装饰器:适合单一供应商、需要强类型检查、开发效率优先的场景
- 多供应商装饰器:适合需要多供应商支持、故障转移、负载均衡的企业级应用场景
API可拆分配置示例
对于大型项目,可以将API配置拆分到不同的文件中:
// configs/base.config.ts
export const baseConfig = {
name: 'west_cn',
baseUrl: 'https://api.west.cn',
version: '2.0',
description: '西部数码API服务',
processorConfigs: {
'west-cn-auth': {
username: 'your_username',
password: 'your_password'
}
}
};
// configs/domain-apis.config.ts
export const domainApis = {
getDomainPrice: {
name: 'getDomainPrice',
path: '/api/v2/info/',
method: 'POST',
// ... 域名价格查询配置
},
getDomainInfo: {
name: 'getDomainInfo',
path: '/api/v2/domain/info/',
method: 'POST',
// ... 域名信息查询配置
}
};
// configs/server-apis.config.ts
export const serverApis = {
getServerConfig: {
name: 'getServerConfig',
path: '/api/v2/server/config/',
method: 'GET',
// ... 服务器配置查询配置
},
createServer: {
name: 'createServer',
path: '/api/v2/server/create/',
method: 'POST',
// ... 创建服务器配置
}
};
// 合并配置
import { baseConfig } from './configs/base.config';
import { domainApis } from './configs/domain-apis.config';
import { serverApis } from './configs/server-apis.config';
const engine = new DeconstructionEngine();
// 方式1: 手动合并
const fullConfig = {
...baseConfig,
apis: {
...domainApis,
...serverApis
}
};
engine.registerSupplier(fullConfig);
// 方式2: 使用引擎的合并功能 (如果支持)
engine.mergeAndRegisterSupplier(baseConfig, [domainApis, serverApis]);🏗️ 架构设计
核心组件
- DeconstructionEngine: 核心引擎,负责协调整个处理流程
- ProcessorFactory: 处理器工厂,管理前置和后置处理器
- DataTransformerFactory: 数据转换器工厂,处理各种数据转换
- HttpClient: HTTP客户端,处理网络请求和重试机制
处理流程
请求数据 → 前置处理器 → HTTP请求 → 后置处理器 → 返回结果
↓ ↓ ↓ ↓ ↓
验证参数 添加认证头 调用API 字段映射 数据清理
添加签名 参数处理 错误处理 数据转换 结果验证🔧 配置详解
供应商配置 (SupplierConfig)
interface SupplierConfig {
name: string; // 供应商名称
baseUrl: string; // 基础URL
version: string; // 版本号
description?: string; // 描述
// 处理器专用配置
processorConfigs?: Record<string, {
[key: string]: any; // 处理器特定配置参数
description?: string; // 配置描述
}>;
apis: Record<string, ApiConfig>; // API配置
errorCodeMapping?: Record<number, string>; // 错误码映射表
timeout?: number; // 超时时间
retries?: number; // 重试次数
}API配置 (ApiConfig)
interface ApiConfig {
name: string; // API名称
path: string; // API路径
method: HttpMethod; // HTTP方法
description?: string; // API描述
// 固定参数(每次请求都会包含)
params?: Record<string, any>;
// 请求参数定义和验证
requestParams?: Record<string, {
type: DataType; // 参数类型
required?: boolean; // 是否必填
defaultValue?: any; // 默认值
description?: string; // 参数描述
validation?: {
pattern?: string; // 正则验证
enumValues?: string[]; // 枚举值
};
}>;
headers?: Record<string, string>; // 请求头
timeout?: number; // 超时时间
retries?: number; // 重试次数
// 字段映射 - 对象格式
fieldMapping: Record<string, {
mapping: string; // 目标字段路径
type: DataType; // 数据类型
defaultValue?: any; // 默认值
description?: string; // 字段描述
transformer?: TransformerConfig; // 数据转换器
}>;
preProcessors?: string[]; // 前置处理器
postProcessors?: string[]; // 后置处理器
}字段映射格式
字段映射现在使用对象格式,键为源字段路径(支持点记号法),值为映射配置:
fieldMapping: {
'source.field.path': {
mapping: 'target.field.path', // 目标字段路径
type: DataType.STRING, // 数据类型
defaultValue: '', // 默认值
description: '字段描述', // 字段描述
transformer?: { // 可选的数据转换器
type: TransformerType.CUSTOM_EXPRESSION,
expression: 'parseFloat(value) || 0'
}
}
}📋 数据类型
DataType 枚举
enum DataType {
STRING = 'string',
NUMBER = 'number',
BOOLEAN = 'boolean',
OBJECT = 'object',
ARRAY = 'array',
DATE = 'date'
}🎨 数据转换器
支持的转换器类型
- 日期转换器 (DATE)
{
type: TransformerType.DATE,
inputFormat: 'YYYY-MM-DD',
outputFormat: 'ISO',
timezone: 'UTC'
}- JSON转换器 (JSON)
{
type: TransformerType.JSON,
mode: 'parse', // 或 'stringify'
prettify: true
}- 对象属性提取 (OBJECT_EXTRACT)
{
type: TransformerType.OBJECT_EXTRACT,
path: 'data.items[0].name',
fallbackPath: 'data.name'
}- 自定义表达式 (CUSTOM_EXPRESSION)
{
type: TransformerType.CUSTOM_EXPRESSION,
expression: 'parseFloat(value) || 0' // JavaScript表达式
}- 自定义表达式 (CUSTOM_EXPRESSION)
{
type: TransformerType.CUSTOM_EXPRESSION,
expression: 'value * 100 + Math.random()',
context: { multiplier: 100 }
}- 条件转换器 (CONDITIONAL)
{
type: TransformerType.CONDITIONAL,
conditions: [
{ condition: 'value === "active"', value: true },
{ condition: 'value === "inactive"', value: false }
],
defaultValue: null
}- 正则表达式转换器 (REGEX)
{
type: TransformerType.REGEX,
pattern: '\\d{4}-\\d{2}-\\d{2}',
replacement: '$1/$2/$3',
flags: 'g'
}- 数组映射转换器 (ARRAY_MAP)
{
type: TransformerType.ARRAY_MAP,
itemTransformer: {
type: TransformerType.OBJECT_EXTRACT,
path: 'name'
},
filterCondition: 'item.active === true'
}- 字符串转换器 (STRING_TRANSFORM)
{
type: TransformerType.STRING_TRANSFORM,
operation: 'toLowerCase' // 或其他操作
}- 数字转换器 (NUMBER_TRANSFORM)
{
type: TransformerType.NUMBER_TRANSFORM,
operation: 'multiply',
operand: 100,
precision: 2
}🏗️ 架构设计
核心组件
引擎核心 (Engine Core)
- 统一的API调用入口
- 供应商管理和路由
- 配置验证和缓存
供应商引擎 (Supplier Engine)
- 基于配置文件的API管理
- 动态字段映射
- 处理器链执行
装饰器引擎 (Decorator Engine)
- 基于装饰器的类型安全映射
- 编译时配置验证
- 自动类型推导
处理器系统 (Processor System)
- 前置处理器:请求预处理、认证、参数验证
- 后置处理器:响应转换、错误处理、数据清洗
数据转换器 (Data Transformers)
- 类型转换和验证
- 自定义表达式执行
- 复杂数据结构映射
处理流程
配置文件方式流程
用户请求 → 引擎路由 → 供应商引擎 → 前置处理器链 → HTTP请求 → 后置处理器链 → 字段映射 → 返回结果装饰器类方式流程
用户请求 → 引擎路由 → 装饰器引擎 → 配置解析 → 前置处理器链 → HTTP请求 → 后置处理器链 → 类实例化 → 返回结果两种方式对比
| 特性 | 配置文件方式 | 装饰器类方式 | |------|-------------|-------------| | 类型安全 | 运行时验证 | 编译时验证 | | 配置复杂度 | 较高 | 较低 | | IDE支持 | 基础 | 完整 | | 学习成本 | 中等 | 低 | | 灵活性 | 高 | 中等 | | 性能 | 良好 | 优秀 |
自定义前置处理器
import { createHash } from 'crypto';
import { BasePreProcessor, ProcessorContext, ProcessorResult } from 'harinsangma2-deconstruction-engine';
class WestCnAuthPreProcessor extends BasePreProcessor {
name = 'west-cn-auth';
order = 1;
async execute(context: ProcessorContext): Promise<ProcessorResult> {
try {
// 从配置中读取认证参数
const username = this.getConfigValue(context, 'username');
const password = this.getConfigValue(context, 'password');
// 验证必要参数
if (!username || !password) {
throw new Error('认证配置缺失:username 或 password 未配置');
}
// 生成时间戳(毫秒)- 西部数码API要求毫秒时间戳
const timestamp = Date.now();
// 按照西部数码API规范生成MD5 token
const tokenString = username + password + timestamp;
const token = createHash('md5').update(tokenString).digest('hex');
// 将认证参数添加到请求体数据中
const updatedData = {
...context.currentData,
username,
time: timestamp.toString(),
token
};
return this.createSuccessResult(updatedData);
} catch (error) {
return this.createErrorResult(error as Error);
}
}
validateConfig(context: ProcessorContext): boolean {
const username = this.getConfigValue(context, 'username');
const password = this.getConfigValue(context, 'password');
return !!(username && password);
}
}
// 注册处理器
engine.registerPreProcessor(new WestCnAuthPreProcessor());自定义后置处理器
import { IPostProcessor, ProcessorContext, ProcessorResult } from 'harinsangma2-deconstruction-engine';
/**
* 西部数码API错误处理器
* 检查API响应中的错误码,如果存在错误码则抛出异常
*/
class WestCnErrorHandlerProcessor implements IPostProcessor {
readonly name = 'west-cn-error-handler';
readonly order = 5; // 在field-mapping之后执行
async execute(context: ProcessorContext): Promise<ProcessorResult> {
try {
const { currentData, supplierConfig } = context;
// 检查是否有错误码映射表
if (!supplierConfig?.errorCodeMapping) {
return {
success: true,
data: currentData
};
}
// 检查响应数据中的错误码
const errorCode = currentData?.errorCode;
if (errorCode && errorCode !== 0) {
// 从错误码映射表中获取错误信息
const errorMessage = supplierConfig.errorCodeMapping?.[errorCode];
if (errorMessage) {
// 只有在错误码映射表中存在的错误码才抛出异常
const error = new Error(`西部数码API错误 [${errorCode}]: ${errorMessage}`);
(error as any).code = errorCode;
(error as any).type = 'WEST_CN_API_ERROR';
return {
success: false,
error,
metadata: {
errorCode,
errorMessage,
originalMessage: currentData?.message
}
};
}
// 未知错误码不抛出异常,正常返回数据
}
// 没有错误码或错误码为0,正常返回
return {
success: true,
data: currentData
};
} catch (error) {
return {
success: false,
error: error as Error
};
}
}
}
engine.registerPostProcessor(new WestCnErrorHandlerProcessor());📊 内置处理器
前置处理器
- request-validation: 请求参数验证
- params: 参数处理和合并
- west-cn-auth: 西部数码API认证(示例)
后置处理器
- field-mapping: 字段映射转换
- west-cn-error-handler: 西部数码错误处理(示例)
处理器配置
处理器可以通过 processorConfigs 进行配置:
processorConfigs: {
'processor-name': {
param1: 'value1',
param2: 'value2',
description: '处理器描述'
}
}⚙️ 高级配置
引擎选项
const engine = createEngine({
enableDebug: true, // 启用调试日志
enableMetrics: true, // 启用性能指标
enableCache: true, // 启用缓存
cacheTtl: 300000, // 缓存TTL(毫秒)
timeout: 30000, // 默认超时时间
retries: 3, // 默认重试次数
onError: (error, context) => {
console.error('Engine error:', error);
},
onSuccess: (result) => {
console.log('Execution success:', result.metadata);
}
});缓存配置
import { ICacheProvider } from 'harinsangma2-deconstruction-engine';
// 自定义缓存提供者
class RedisCache implements ICacheProvider {
async get(key: string): Promise<any> {
// Redis获取逻辑
}
async set(key: string, value: any, ttl?: number): Promise<void> {
// Redis设置逻辑
}
async delete(key: string): Promise<void> {
// Redis删除逻辑
}
async clear(): Promise<void> {
// Redis清空逻辑
}
}
const engine = createEngine({
enableCache: true,
cache: new RedisCache()
});🔍 监控和调试
执行结果
interface EngineResult {
success: boolean; // 执行是否成功
data?: any; // 转换后的数据
originalData?: any; // 原始响应数据
errors?: Array<{ // 错误信息
stage: string;
error: Error;
}>;
metadata: { // 执行元数据
processingTime: number; // 处理时间
timestamp: Date; // 执行时间戳
supplierName: string; // 供应商名称
apiName: string; // API名称
preProcessorsExecuted: string[]; // 执行的前置处理器
postProcessorsExecuted: string[]; // 执行的后置处理器
transformersApplied: string[]; // 应用的转换器
};
}错误处理
try {
const result = await engine.execute('supplier', 'api', data);
if (!result.success) {
result.errors?.forEach(error => {
console.error(`${error.stage}: ${error.error.message}`);
});
}
} catch (error) {
console.error('Unhandled error:', error);
}🧪 测试
# 运行所有测试
npm test
# 运行测试并生成覆盖率报告
npm run test:coverage
# 监听模式运行测试
npm run test:watch📝 示例项目
查看 examples/ 目录获取更多使用示例:
basic-usage.ts- 基础使用示例advanced-features.ts- 高级功能示例custom-processors.ts- 自定义处理器示例batch-processing.ts- 批量处理示例
🛠️ 开发
# 克隆项目
git clone <repository-url>
cd data-deconstruction-engine
# 安装依赖
npm install
# 构建项目
npm run build
# 开发模式(监听文件变化)
npm run build:watch
# 运行示例
npm run dev📋 API参考
DeconstructionEngine
方法
registerSupplier(config: SupplierConfig): void- 注册供应商配置getSupplier(name: string): SupplierConfig | null- 获取供应商配置getSuppliers(): SupplierConfig[]- 获取所有供应商execute(supplierName: string, apiName: string, requestData?: any, options?: Partial<EngineOptions>): Promise<EngineResult>- 执行API调用registerPreProcessor(processor: IPreProcessor): void- 注册前置处理器registerPostProcessor(processor: IPostProcessor): void- 注册后置处理器clearCache(): Promise<void>- 清空缓存
工具函数
createEngine(options?: EngineOptions): DeconstructionEngine- 创建引擎实例createEngineWithSuppliers(suppliers: SupplierConfig[], options?: EngineOptions): DeconstructionEngine- 创建并配置引擎
🤝 贡献
欢迎提交Issue和Pull Request!
📄 许可证
MIT License
📞 支持
如有问题或需要支持,请:
- 查看文档和示例
- 搜索现有Issues
- 创建新Issue描述问题
- 联系开发团队
Data Deconstruction Engine - 让API数据转换变得简单而强大! 🚀
🎯 泛型类型安全
引擎的 execute 方法支持泛型,让你可以指定返回数据的确切类型,获得完整的 TypeScript 类型安全:
// 定义返回数据类型
interface UserProfile {
id: number;
username: string;
email: string;
isActive: boolean;
}
interface ProductInfo {
productId: string;
name: string;
price: number;
inStock: boolean;
}
// 使用泛型获得类型安全
const userResult = await engine.execute<UserProfile>("user-api", "getProfile", { userId: 123 });
const productResult = await engine.execute<ProductInfo>("product-api", "getProduct", { id: "ABC123" });
if (userResult.success) {
// userResult.data 现在是 UserProfile | undefined,具有完整的类型提示
console.log(`用户: ${userResult.data?.username}`);
console.log(`邮箱: ${userResult.data?.email}`);
console.log(`状态: ${userResult.data?.isActive ? "激活" : "未激活"}`);
// TypeScript 会在编译时检查类型错误
// console.log(userResult.data?.unknownField); // 编译错误!
}
if (productResult.success) {
// productResult.data 现在是 ProductInfo | undefined
console.log(`产品: ${productResult.data?.name}`);
console.log(`价格: ¥${productResult.data?.price}`);
console.log(`库存: ${productResult.data?.inStock ? "有货" : "缺货"}`);
}
// 向后兼容:不指定泛型时为 any 类型
const legacyResult = await engine.execute("user-api", "getProfile", { userId: 123 });
console.log(legacyResult.data); // 类型为 any泛型的优势
- ✅ 编译时类型检查:TypeScript 会在编译时捕获类型错误
- ✅ 智能代码提示:IDE 提供完整的属性和方法提示
- ✅ 重构安全:重命名字段时,TypeScript 会自动更新所有引用
- ✅ 向后兼容:不指定泛型时保持原有的
any类型行为 - ✅ 复杂类型支持:支持嵌套对象、数组、联合类型等复杂类型定义
类型定义示例
// 简单类型
interface User {
id: number;
name: string;
}
// 嵌套类型
interface UserProfile {
user: User;
profile: {
firstName: string;
lastName: string;
avatar?: string;
};
permissions: string[];
}
// 分页响应类型
interface PaginatedResponse<T> {
items: T[];
total: number;
page: number;
hasMore: boolean;
}
// 使用复杂类型
const userListResult = await engine.execute<PaginatedResponse<User>>(
"user-api",
"getUsers",
{ page: 1, limit: 10 }
);
// 现在 userListResult.data?.items 是 User[] 类型
userListResult.data?.items.forEach(user => {
console.log(`用户 ${user.id}: ${user.name}`);
});