index.js 7.24 KB
'use strict';

const ROOT_PATH = global.ROOT_PATH;
const _ = require('lodash');
const fs = require('fs');
const moment = require('moment');
const md5 = require('yoho-md5');
const helpers = global.yoho.helpers;
const goodsHbs = require(`${ROOT_PATH}/hbs/partials/seo/index.hbs`);
const util = require(`${ROOT_PATH}/libs/util`);
const redis = require(`${ROOT_PATH}/libs/redis`);
const baiduBrand = require('./baidu-brand');
const SIZE = 2500;
const GOODS_URL = 'http://spiderwebhook.yoho.cn/dist/goods-xml';

class SeoIndexModel extends global.yoho.BaseModel {
    constructor(ctx) {
        super(ctx);
    }

    writerGoodsXml(params) {
        params.start = parseInt(params.start || 1, 10);

        let limit = 100; // max 100
        // limt接口最大支持100,查询${SIZE}条数据返回。则page转换成如下公式
        let page = (params.start - 1) * (SIZE / limit) + 1;
        let fileName = `goods-${params.start}.xml`;

        return this.searchGoodsHandle(Object.assign({}, params, {page: page, limit: limit}), []).then(result => {
            if (result.length <= 0) {
                return {code: 400, data: '', message: 'data is empty'};
            }

            const fWrite = fs.createWriteStream(`${ROOT_PATH}/public/dist/goods-xml/${fileName}`);

            fWrite.write(goodsHbs({products: result}));// max 9.5M
            fWrite.end();

            result = [];

            console.log(`${GOODS_URL}/${fileName} over...`);

            return {code: 200, data: `${GOODS_URL}/${fileName}`};
        });
    }

    searchGoodsHandle(params, products) {

        if (products.length >= SIZE) {
            return Promise.resolve(products);
        }

        return util.sleep(2000).then(() => {
            return this.searchList(params, products);
        }).then(rdata => {
            let data = _.get(rdata, 'data', {});
            let productLists = _.get(rdata, 'data.product_list', []);

            _.each(productLists, item => {
                let images = [];

                if (!baiduBrand[item.small_sort_id]) {
                    console.error(`goods-xml => s_sort_id: ${item.small_sort_id}, s_sort_name: ${item.small_sort_name}, baidu cate is null`);// eslint-disable-line
                    return true;
                }

                _.each(item.goods_list.slice(0, 5), goods => {
                    images.push({
                        contentUrl: `https:${helpers.image(goods.images_url, 900, 1200)}`,
                        height: 1200,
                        width: 900,
                        description: `${item.product_name}-${goods.color_name}`,
                        name: goods.color_name
                    });
                });

                products.push({
                    loc: `${GOODS_URL}/goods-${params.start}.xml`,
                    lastmod: moment.unix(item.edit_time).format('YYYY-MM-DD'),
                    changefreq: 'daily',
                    priority: 1.0,
                    fromSrc: 'YOHO!BUY有货',
                    images: images,
                    name: item.product_name,
                    brand: item.brand_name,
                    offers: {
                        price: item.sales_price,
                        administrativeAreaLv2: '全国',
                        wapUrl: `https://m.yohobuy.com/product/${item.product_skn}.html`,
                        url: `https://www.yohobuy.com/product/${item.product_skn}.html`,
                        eligibleTransactionVolume: item.sales_num,
                        seller_name: item.shop_name,
                        ratingValue: '', // 总评分
                        favorableRating: '', // 好评率
                        onShelfTime: '', // 上架时间
                        offShelfTime: '', // 下架时间
                        type: '自营', // 商品属性,是否自营
                        isOverseas: item.is_global === 'N' ? 1 : 0,
                        discount: '', // 优惠信息
                        isFreightFree: 1,
                        commentCount: item.product_skn % 50, // 评论总数
                        deliveryPlace: {
                            administrativeAreaLv1: '江苏' // 发货地
                        },
                        stockSpecification: {// 分地域库存信息
                            AdministrativeAreaLv2: '南京', // 区域
                            volume: item.storage_num, // 库存
                            status: (item.storage_num <= 0 ? 'outstock' : 'instock')
                        },
                        shelfSpecification: {// 分地域上下架具体信息
                            AdministrativeAreaLv2: '南京',
                            status: 1
                        },
                        purchaseMethod: item.tags && item.tags['is_presell'] ? '付费预约' : '直接购买', // eslint-disable-line
                        coupon: [] // 优惠券
                    },
                    structuredCategory: {
                        categoryLv1: baiduBrand[item.small_sort_id].categoryLv1 || '',
                        categoryLv2: baiduBrand[item.small_sort_id].categoryLv2 || '',
                        categoryLv3: baiduBrand[item.small_sort_id].categoryLv3 || '',
                        categoryLv4: baiduBrand[item.small_sort_id].categoryLv4 || ''
                    },
                    exifData: {
                        name: '',
                        value: '',
                        propertyID: '',
                        unitText: '',
                        unitCode: ''
                    },
                    id: item.product_skn
                });
            });

            rdata = [];
            productLists = [];

            if (data.page >= data.page_total) {
                return products;
            }

            return this.searchGoodsHandle(Object.assign({}, params, {page: ++params.page}), products);
        });
    }

    searchList(params) {
        params = _.assign({
            limit: 100,
            method: 'app.search.li',
            status: 1,
            stocknumber: 1,
            attribute_not: 2,
            udid: 'seo_format_data'
        }, params);

        return this.get({
            data: params,
            param: {
                cache: 86400
            }
        }).catch(e => {
            console.log(e.message);
            return {code: 400, data: {}, message: e.message};
        });
    }

    autoGoodsXml(params) {
        return util.sleep(1000).then(() => {
            return this.writerGoodsXml(params);
        }).then(rdata => {

            if (rdata.code !== 200) {
                global.IS_GOODS_XML_RUN = false;
                return {};
            }

            return this.autoGoodsXml(Object.assign(params, {start: ++params.start}));
        });
    }

    setTask(params) {
        return redis.hsetAsync('global:yoho:seo:task', md5(params.url), JSON.stringify({
            url: params.url,
            time: params.time || 1000
        }));
    }

    delTask(url) {
        return redis.hdelAsync('global:yoho:seo:task', md5(url));
    }

    getGoodsPage() {
        return this.searchList({page: 1, limit: 1}).then(rdata => {
            let total = _.get(rdata, 'data.total', 0);

            return Math.ceil(total / SIZE);
        });
    }
}

module.exports = SeoIndexModel;