npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

harinsangma2-deconstruction-engine

v1.1.5

Published

A flexible data deconstruction engine for transforming upstream API data into internal data structures

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]);

🏗️ 架构设计

核心组件

  1. DeconstructionEngine: 核心引擎,负责协调整个处理流程
  2. ProcessorFactory: 处理器工厂,管理前置和后置处理器
  3. DataTransformerFactory: 数据转换器工厂,处理各种数据转换
  4. 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'
}

🎨 数据转换器

支持的转换器类型

  1. 日期转换器 (DATE)
{
  type: TransformerType.DATE,
  inputFormat: 'YYYY-MM-DD',
  outputFormat: 'ISO',
  timezone: 'UTC'
}
  1. JSON转换器 (JSON)
{
  type: TransformerType.JSON,
  mode: 'parse',  // 或 'stringify'
  prettify: true
}
  1. 对象属性提取 (OBJECT_EXTRACT)
{
  type: TransformerType.OBJECT_EXTRACT,
  path: 'data.items[0].name',
  fallbackPath: 'data.name'
}
  1. 自定义表达式 (CUSTOM_EXPRESSION)
{
  type: TransformerType.CUSTOM_EXPRESSION,
  expression: 'parseFloat(value) || 0'  // JavaScript表达式
}
  1. 自定义表达式 (CUSTOM_EXPRESSION)
{
  type: TransformerType.CUSTOM_EXPRESSION,
  expression: 'value * 100 + Math.random()',
  context: { multiplier: 100 }
}
  1. 条件转换器 (CONDITIONAL)
{
  type: TransformerType.CONDITIONAL,
  conditions: [
    { condition: 'value === "active"', value: true },
    { condition: 'value === "inactive"', value: false }
  ],
  defaultValue: null
}
  1. 正则表达式转换器 (REGEX)
{
  type: TransformerType.REGEX,
  pattern: '\\d{4}-\\d{2}-\\d{2}',
  replacement: '$1/$2/$3',
  flags: 'g'
}
  1. 数组映射转换器 (ARRAY_MAP)
{
  type: TransformerType.ARRAY_MAP,
  itemTransformer: {
    type: TransformerType.OBJECT_EXTRACT,
    path: 'name'
  },
  filterCondition: 'item.active === true'
}
  1. 字符串转换器 (STRING_TRANSFORM)
{
  type: TransformerType.STRING_TRANSFORM,
  operation: 'toLowerCase' // 或其他操作
}
  1. 数字转换器 (NUMBER_TRANSFORM)
{
  type: TransformerType.NUMBER_TRANSFORM,
  operation: 'multiply',
  operand: 100,
  precision: 2
}

🏗️ 架构设计

核心组件

  1. 引擎核心 (Engine Core)

    • 统一的API调用入口
    • 供应商管理和路由
    • 配置验证和缓存
  2. 供应商引擎 (Supplier Engine)

    • 基于配置文件的API管理
    • 动态字段映射
    • 处理器链执行
  3. 装饰器引擎 (Decorator Engine)

    • 基于装饰器的类型安全映射
    • 编译时配置验证
    • 自动类型推导
  4. 处理器系统 (Processor System)

    • 前置处理器:请求预处理、认证、参数验证
    • 后置处理器:响应转换、错误处理、数据清洗
  5. 数据转换器 (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

📞 支持

如有问题或需要支持,请:

  1. 查看文档和示例
  2. 搜索现有Issues
  3. 创建新Issue描述问题
  4. 联系开发团队

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}`);
});