企业信息化管理系统

EIMS - 助力企业数字化转型

企业信息化系统数据中台架构设计与实现

引言

数据中台是企业数字化转型的核心基础设施,它承担着数据采集、清洗、治理、存储、服务化的全链路职责。通过构建统一的数据中台,企业可以实现数据的资产化、服务化,支撑各业务系统的数据需求。

数据中台整体架构

数据中台的架构分层设计:

层次 组件 职责
数据采集层 Flume、Logstash、DataX 批量与实时数据采集
数据计算层 Spark、Flink、MapReduce 离线与实时数据处理
数据存储层 HDFS、HBase、ClickHouse 分布式数据存储
数据服务层 API网关、数据服务中间件 统一数据服务出口

数据采集与接入

多源异构数据的统一采集方案:

// 数据采集器核心实现
class DataCollector {
  constructor() {
    this.sources = new Map();
    this.channels = [];
  }

  // 注册数据源
  registerSource(config) {
    const source = {
      type: config.type,
      config: config,
      collector: this.createCollector(config),
      status: 'idle'
    };

    this.sources.set(config.name, source);
    return this;
  }

  // 创建对应类型的采集器
  createCollector(config) {
    switch (config.type) {
      case 'database':
        return new DatabaseCollector(config);
      case 'api':
        return new ApiCollector(config);
      case 'file':
        return new FileCollector(config);
      case 'mq':
        return new MessageQueueCollector(config);
      default:
        throw new Error(`Unknown source type: ${config.type}`);
    }
  }

  // 启动采集任务
  async start(collection) {
    const source = this.sources.get(collection.sourceName);
    if (!source) {
      throw new Error(`Source not found: ${collection.sourceName}`);
    }

    source.status = 'running';

    // 创建定时任务
    const task = new IntervalJob({
      interval: collection.interval || 60000,
      handler: async () => {
        try {
          const data = await source.collector.collect(collection.query);

          // 数据校验
          if (this.validateData(data)) {
            // 发送到数据通道
            await this.publishToChannel(source, data);
          }
        } catch (error) {
          console.error(`Collection failed:`, error);
          await this.handleError(source, error);
        }
      }
    });

    task.start();
    return task;
  }

  // 批量数据采集
  async batchCollect(sourceNames) {
    const results = [];

    for (const name of sourceNames) {
      const source = this.sources.get(name);
      if (source && source.status === 'running') {
        const data = await source.collector.collect();
        results.push({ source: name, data });
      }
    }

    return results;
  }

  // 数据校验
  validateData(data) {
    if (!data || !Array.isArray(data) || data.length === 0) {
      return false;
    }

    // 校验数据格式
    const requiredFields = this.commonFields || [];
    for (const item of data) {
      for (const field of requiredFields) {
        if (!(field in item)) {
          console.warn(`Missing required field: ${field}`);
          return false;
        }
      }
    }

    return true;
  }

  // 发送到数据通道
  async publishToChannel(source, data) {
    for (const channel of this.channels) {
      await channel.send({
        source: source.config.name,
        timestamp: Date.now(),
        data: data
      });
    }
  }
}

// 数据库采集器
class DatabaseCollector {
  constructor(config) {
    this.config = config;
    this.pool = null;
  }

  async connect() {
    this.pool = new ConnectionPool(this.config connection);
  }

  async collect(query) {
    if (!this.pool) {
      await this.connect();
    }

    // 支持增量同步
    if (query.increment) {
      const lastSync = await this.getLastSyncTime();
      query.where += ` AND create_time > '${lastSync}'`;
    }

    const result = await this.pool.query(query.sql || query.where);
    return result;
  }

  async getLastSyncTime() {
    // 从元数据表获取上次同步时间
    const meta = await this.pool.query(
      `SELECT MAX(create_time) as last_time FROM sync_metadata WHERE source = '${this.config.name}'`
    );
    return meta[0]?.last_time || '1970-01-01';
  }
}

数据治理体系

构建完善的数据治理体系,确保数据质量:

// 数据治理中心
class DataGovernance {
  constructor() {
    this.rules = new Map();
    this.qualityReports = [];
  }

  // 注册数据质量规则
  registerRule(rule) {
    this.rules.set(rule.id, rule);
    return this;
  }

  // 数据质量检查
  async checkQuality(dataset) {
    const results = {
      dataset: dataset.name,
      total: dataset.data.length,
      issues: []
    };

    // 执行各项规则检查
    for (const [id, rule] of this.rules) {
      const ruleResult = await rule.check(dataset.data);

      if (!ruleResult.passed) {
        results.issues.push({
          ruleId: id,
          ruleName: rule.name,
          failedCount: ruleResult.failedCount,
          samples: ruleResult.samples
        });
      }
    }

    results.score = this.calculateScore(results);
    results.status = results.score >= 80 ? 'pass' : 'fail';

    // 记录质量报告
    this.qualityReports.push({
      ...results,
      timestamp: Date.now()
    });

    return results;
  }

  // 计算质量得分
  calculateScore(results) {
    if (results.total === 0) return 0;

    const baseScore = 100;
    const totalIssues = results.issues.reduce((sum, issue) => {
      return sum + issue.failedCount;
    }, 0);

    return Math.max(0, Math.round(
      baseScore - (totalIssues / results.total) * 100
    ));
  }

  // 异常数据处理
  async handleAnomalies(issues) {
    for (const issue of issues) {
      const rule = this.rules.get(issue.ruleId);

      switch (rule.action) {
        case 'flag':
          await this.flagData(issue.samples);
          break;
        case 'quarantine':
          await this.quarantineData(issue.samples);
          break;
        case 'auto_fix':
          await rule.autoFix(issue.samples);
          break;
        case 'alert':
          await this.sendAlert(issue);
          break;
      }
    }
  }

  // 数据标准化
  async normalize(dataset) {
    const normalized = [];

    for (const item of dataset.data) {
      const normalizedItem = {};

      for (const [field, config] of Object.entries(dataset.schema)) {
        let value = item[field];

        // 类型转换
        if (config.type === 'number') {
          value = parseFloat(value) || 0;
        } else if (config.type === 'date') {
          value = this.parseDate(value);
        }

        // 值域映射
        if (config.mapping) {
          value = config.mapping[value] || value;
        }

        // 脱敏处理
        if (config.mask) {
          value = this.maskValue(value, config.mask);
        }

        normalizedItem[field] = value;
      }

      normalized.push(normalizedItem);
    }

    return { ...dataset, data: normalized };
  }

  // 数据脱敏
  maskValue(value, maskType) {
    if (!value) return value;

    switch (maskType) {
      case 'phone':
        return value.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
      case 'idCard':
        return value.replace(/(\d{4})\d{10}(\d{4})/, '$1**********$2');
      case 'email':
        return value.replace(/(\w{2})[\w]*(@[\w.]+)/, '$1***$2');
      default:
        return value;
    }
  }
}

// 常用数据质量规则
class QualityRules {
  // 唯一性检查
  static uniqueness(field) {
    return {
      id: `unique_${field}`,
      name: `${field}字段唯一性检查`,
      async check(data) {
        const values = data.map(item => item[field]);
        const duplicates = values.filter((v, i) => values.indexOf(v) !== i);

        return {
          passed: duplicates.length === 0,
          failedCount: duplicates.length,
          samples: duplicates.slice(0, 10)
        };
      }
    };
  }

  // 完整性检查
  static completeness(field, required = true) {
    return {
      id: `complete_${field}`,
      name: `${field}字段完整性检查`,
      async check(data) {
        const missing = data.filter(item =>
          required && (item[field] === null || item[field] === undefined || item[field] === '')
        );

        return {
          passed: missing.length === 0,
          failedCount: missing.length,
          samples: missing.slice(0, 10)
        };
      }
    };
  }

  // 有效性检查
  static validity(field, validator) {
    return {
      id: `valid_${field}`,
      name: `${field}字段有效性检查`,
      async check(data) {
        const invalid = data.filter(item => !validator(item[field]));

        return {
          passed: invalid.length === 0,
          failedCount: invalid.length,
          samples: invalid.slice(0, 10)
        };
      }
    };
  }

  // 稳定性检查
  static consistency(field, expectedPattern) {
    return {
      id: `consistent_${field}`,
      name: `${field}字段一致性检查`,
      async check(data) {
        const outliers = data.filter(item => !expectedPattern.test(item[field]));

        return {
          passed: outliers.length === 0,
          failedCount: outliers.length,
          samples: outliers.slice(0, 10)
        };
      }
    };
  }
}

数据服务化

将数据资产封装为可复用的服务:

// 数据服务网关
class DataServiceGateway {
  constructor() {
    this.services = new Map();
    this.middleware = [];
  }

  // 注册数据服务
  registerService(config) {
    const service = {
      name: config.name,
      version: config.version || 'v1',
      handler: config.handler,
      cache: config.cache || { enabled: false, ttl: 300 },
      rateLimit: config.rateLimit || { limit: 100, window: 60 },
      auth: config.auth || false
    };

    const key = `${service.name}/${service.version}`;
    this.services.set(key, service);

    return this;
  }

  // 处理数据请求
  async handleRequest(ctx) {
    const { service, version, method } = this.parsePath(ctx.path);
    const key = `${service}/${version}`;
    const serviceConfig = this.services.get(key);

    if (!serviceConfig) {
      throw new Error(`Service not found: ${key}`);
    }

    // 权限校验
    if (serviceConfig.auth) {
      await this.verifyAuth(ctx);
    }

    // 限流检查
    await this.checkRateLimit(ctx, serviceConfig);

    // 缓存检查
    if (serviceConfig.cache.enabled) {
      const cached = await this.getFromCache(ctx);
      if (cached) {
        ctx.body = cached;
        return;
      }
    }

    // 执行服务
    const result = await serviceConfig.handler(ctx.query, ctx.body);

    // 缓存结果
    if (serviceConfig.cache.enabled) {
      await this.setCache(ctx, result, serviceConfig.cache.ttl);
    }

    ctx.body = result;
  }

  // 通用查询服务
  registerQueryService(config) {
    return this.registerService({
      name: config.name,
      version: config.version,
      handler: async (query) => {
        const builder = new QueryBuilder(config.dataset);

        // 构建查询条件
        if (query.filters) {
          for (const filter of query.filters) {
            builder.where(filter.field, filter.operator, filter.value);
          }
        }

        // 排序
        if (query.sort) {
          builder.orderBy(query.sort.field, query.sort.order);
        }

        // 分页
        if (query.page && query.pageSize) {
          builder.offset((query.page - 1) * query.pageSize);
          builder.limit(query.pageSize);
        }

        // 执行查询
        const data = await builder.execute();
        const total = await builder.count();

        return {
          data,
          pagination: {
            page: query.page || 1,
            pageSize: query.pageSize || 20,
            total
          }
        };
      },
      cache: { enabled: true, ttl: 60 },
      rateLimit: { limit: 200, window: 60 }
    });
  }

  // 聚合数据服务
  registerAggregationService(config) {
    return this.registerService({
      name: config.name,
      version: config.version,
      handler: async (query) => {
        const pipeline = [];

        // 过滤阶段
        if (query.filters) {
          pipeline.push({ $match: this.buildMatchQuery(query.filters) });
        }

        // 分组阶段
        if (query.groupBy) {
          pipeline.push({
            $group: {
              _id: this.buildGroupKey(query.groupBy),
              count: { $sum: 1 },
              ...this.buildAggregations(query.metrics)
            }
          });
        }

        // 排序阶段
        if (query.sort) {
          pipeline.push({ $sort: query.sort });
        }

        // 限制阶段
        if (query.limit) {
          pipeline.push({ $limit: query.limit });
        }

        return await config.dataset.aggregate(pipeline);
      },
      cache: { enabled: true, ttl: 300 },
      rateLimit: { limit: 50, window: 60 }
    });
  }
}

最佳实践建议

总结

数据中台建设是企业数字化转型的重要支撑:

构建数据中台需要技术、业务、管理多方协同,是一个持续优化的过程。

← 下一篇:企业信息化系统视频监控与智能分析