price-day-task.js 5.25 KB
const {ProductRelationModel} = require('../../models');
const _ = require('lodash');
const dayjs = require('dayjs');
const {sendMessage} = require('../../libs/influx-report');
const lockup = require('node-lockup');
const {mysqlPool} = require('../../libs/mysql');
const {logger} = require('../../libs/logger');
const spiderSj = require('./spider/shenjian');
const spiderDu = require('./spider/du');
const spiderUfo = require('./spider/ufo');
const {getSpecialSizeProducts, getSize} = require('../../utils');

const mergePrice = (prices, scales) => {
  let mp = 0;

  prices.forEach((price, index) => {
    mp += price * scales[index];
  });
  return Math.round(mp);
};

/**
 * @param {*} groupObj  {du: 123, ufo: 100, sj: 112}
 */
const calcPrice = (groupObj) => {
  const {ufo, sj, du} = groupObj;

  if (ufo && sj && du) {
    return mergePrice([ufo, sj, du], [0.5, 0.2, 0.3]);
  } else if (ufo && sj) {
    return mergePrice([ufo, sj], [0.7, 0.3]);
  } else if (ufo && du) {
    return mergePrice([ufo, du], [0.7, 0.3]);
  } else if (sj && du) {
    return mergePrice([sj, du], [0.5, 0.5]);
  } else {
    const onlyKey = Object.keys(groupObj).find(key => groupObj[key]);

    if (onlyKey) {
      return groupObj[onlyKey];
    }
  }
  return 0;
};

/**
 * @param {*} skuGroupObj {du: [{size: "37", price: 1999}], ufo: [{size: "37", price: 1999}] ...}
 * return {'37': {du: 123, ufo: 100, sj: 112}}
 */
const mergeSkus = (skuGroupObj) => {
  const mergeGroups = {};

  _.each(skuGroupObj, (skus, key) => {
    if (skus) {
      _.each(skus, sku => {
        if (!mergeGroups[sku.size]) {
          mergeGroups[sku.size] = {};
        }
        mergeGroups[sku.size][key] = sku.price;
      });
    }
  });
  return mergeGroups;
};

const insertData = async({product, sku, time}) => {
  const result = await mysqlPool.insert('INSERT INTO `price_trend_day` (`product_id`, `size_id`, `skn_price`, `sku_price`, `create_time`) VALUES (:productId, :sizeId, :sknPrice, :skuPrice, :createTime)', {
    productId: product.productId,
    sizeId: sku.sizeId,
    sknPrice: product.price,
    skuPrice: sku.price,
    createTime: time
  });


  if (result) {
    logger.info(`[price-day-task] dayTask, insert success productId: ${product.productId}, sizeId: ${sku.sizeId}, price: ${sku.price}, sknPrice: ${product.price}, time: ${time}`);
    return true;
  } else {
    logger.error(`[price-day-task] dayTask, insert fail productId: ${product.productId}, sizeId: ${sku.sizeId}, price: ${sku.price}, sknPrice: ${product.price}, time: ${time}`);
    return false;
  }
};

const report = async(product, time) => {
  const insertLockTask = lockup(insertData);

  const results = await Promise.all(_.map(product.skus, sku => insertLockTask({product, sku, time})));

  return results.filter(r => r).length;
};

const task = async({product, now, sizeRelation, isSpecialSize}) => {
  try {
    const results = await Promise.all([
      spiderUfo(product.third.ufo),
      spiderSj(product.third.sj, product.productCode, sizeRelation),
      spiderDu(product.third.du),
    ]);
    const skusGroups = mergeSkus({
      ufo: _.get(results[0], 'skus', []),
      sj: _.get(results[1], 'skus', []),
      du: _.get(results[2], 'skus', []),
    });
    const skus = _.map(Object.keys(skusGroups), size => {
      const find = getSize(size, sizeRelation, isSpecialSize);

      if (isSpecialSize) {
        console.log(`=================>price-day-task yhId: ${product.productId},  size: ${size} => ${find.relationSize || size}, ${find.sizeId}`);
      }
      if (find) {
        const price = calcPrice(skusGroups[size]);

        logger.info(`[price-day-task] skusGroups: ${product.productId}, ${size} ${JSON.stringify(skusGroups[size])}, price: ${price}`);

        if (price > 0) {
          return {
            size,
            price,
            sizeId: _.get(find, 'sizeId', 0)
          };
        }
      }
    }).filter(sku => sku);

    return report({
      productId: product.productId,
      price: Math.round(_.sumBy(skus, 'price') / skus.length) || 0,
      skus,
    }, now);
  } catch (error) {
    logger.error(`[price-day-task] dayTask, ${product.productId}, ${error}`);
  }
};


module.exports = async() => {
  const now = dayjs().startOf('hour').unix();

  const resultExists = await mysqlPool.query('SELECT 1 AS Num FROM `price_trend_day` WHERE `create_time` = :createTime  limit 1', {
    createTime: now
  });

  if (resultExists.length > 0) {
    logger.info(`[price-day-task] dayTask, exists createtime price: ${now}`);
    return;
  }

  const specialSizeProducts = await getSpecialSizeProducts();
  const sizeData = await mysqlPool.query('select `size_name`,min(`id`) as id from `size` where `size_name` <> \'-\' group by `size_name`');
  const sizeRelation = {};

  sizeData.forEach(size => {
    sizeRelation[_.trim(size.size_name)] = {
      sizeId: size.id
    };
  });

  const lockTask = lockup(task);
  const products = await ProductRelationModel.findAll();
  let insertTotal = 0;

  products.forEach(async(product, inx) => {
    const isSpecialSize = Boolean(specialSizeProducts[product.productId]);
    const inserted = await lockTask({product, now, sizeRelation, isSpecialSize});

    insertTotal += inserted || 0;
    if (inx >= products.length - 1) {
      sendMessage(insertTotal, new Date().getTime() * 1000000, 'price-day-task-inserted');
    }
  });
};