Authored by 郝肖肖

'news'

  1 +/**
  2 + * guang controller
  3 + * @author: lcy<chuanyang.liu@yoho.cn>
  4 + * @date: 2016/08/31
  5 + */
  6 +'use strict';
  7 +
  8 +const newsService = require('../models/news-service');
  9 +
  10 +/**
  11 + * 首页文章列表 类型列表
  12 + */
  13 +exports.index = (req, res, next) => {
  14 + let channel = req.yoho.channel;
  15 +
  16 + req.ctx(newsService).getIndexList(channel, req.query).then(result => {
  17 + if (result.msgs.length <= 0) {
  18 + res.set('Cache-Control', 'no-cache');
  19 + }
  20 +
  21 + return res.render('news-index', Object.assign({
  22 + title: '新闻 | ' + (res.locals.title || ''),
  23 + module: 'news',
  24 + page: 'index'
  25 + }, result));
  26 + }).catch(next);
  27 +};
  28 +
  29 +/**
  30 + * 文章详情
  31 + */
  32 +exports.detail = (req, res, next) => {
  33 + let channel = req.yoho.channel;
  34 + let id = req.params[0] || 0;
  35 + let cid = req.params[1] || 0;
  36 + let query = {
  37 + channel: channel,
  38 + id: id,
  39 + cid: cid,
  40 + };
  41 +
  42 + if (!id || !cid) {
  43 + return next();
  44 + }
  45 +
  46 + req.ctx(newsService).detail(channel, query).then(result => {
  47 + return res.render('news-detail', Object.assign({
  48 + module: 'news',
  49 + page: 'detail'
  50 + }, result));
  51 + }).catch(next);
  52 +};
  1 +/**
  2 + * news
  3 + * @author: xiaoxiao<xiaoxiao.hao@yoho.cn>
  4 + * @date: 2017/10/13
  5 + */
  6 +'use strict';
  7 +
  8 +var express = require('express'),
  9 + path = require('path'),
  10 + hbsEvent = require('../../config/hbsevent');
  11 +
  12 +var app = express();
  13 +
  14 +// set view engin
  15 +var doraemon = path.join(__dirname, '../../doraemon/views'); // parent view root
  16 +
  17 +app.disable('x-powered-by');
  18 +
  19 +app.on('mount', function(parent) {
  20 + delete parent.locals.settings; // 不继承父 App 的设置
  21 + Object.assign(app.locals, parent.locals);
  22 +});
  23 +
  24 +app.use(global.yoho.hbs({
  25 + extname: '.hbs',
  26 + defaultLayout: 'layout',
  27 + layoutsDir: doraemon,
  28 + partialsDir: [path.join(__dirname, 'views/partial')],
  29 + views: path.join(__dirname, 'views/action'),
  30 + helpers: global.yoho.helpers,
  31 + cb: hbsEvent.cb
  32 +}));
  33 +
  34 +// router
  35 +app.use(require('./router'));
  36 +
  37 +module.exports = app;
  38 +
  1 +'use strict';
  2 +
  3 +module.exports = class extends global.yoho.BaseModel {
  4 + constructor(ctx) {
  5 + super(ctx);
  6 + }
  7 +
  8 + getPolymerizationList(params) {
  9 + return this.get({
  10 + url: 'yohonow/api/page/getPolymerizationList',
  11 + data: params,
  12 + param: {
  13 + cache: true
  14 + },
  15 + api: global.yoho.YohoNowApi
  16 + });
  17 + }
  18 +
  19 + getContentDetail(params) {
  20 + return this.get({
  21 + url: 'yohoboyins/v5/channel/getContentDetail',
  22 + data: {parameters: JSON.stringify(params)},
  23 + param: {
  24 + cache: true
  25 + },
  26 + api: global.yoho.YohoNowApi
  27 + });
  28 + }
  29 +
  30 + /**
  31 + * 推荐文章
  32 + */
  33 + getRecoArticles(params) {
  34 + return this.get({
  35 + url: 'guang/api/v2/article/getArticleByViewsNum',
  36 + data: params,
  37 + param: {
  38 + cache: true
  39 + },
  40 + api: global.yoho.ServiceAPI
  41 + });
  42 + }
  43 +
  44 + /**
  45 + * 获取广告数据
  46 + * @param {String} channelType 传入频道页类型,值可以是: boys, girls, kids, lifestyle
  47 + * @return {Object}
  48 + */
  49 + getAds(params) {
  50 + if (params.isAdDegrade) {
  51 + return Promise.resolve({});
  52 + }
  53 +
  54 + return this.get({
  55 + url: 'operations/api/v5/resource/get',
  56 + data: params,
  57 + param: {
  58 + cache: true
  59 + },
  60 + api: global.yoho.ServiceAPI
  61 + });
  62 + }
  63 +};
  1 +'use strict';
  2 +
  3 +const _ = require('lodash');
  4 +const helpers = global.yoho.helpers;
  5 +const NewsAPi = require('./news-api');
  6 +const utils = require('./utils');
  7 +const moment = require('moment');
  8 +
  9 +// 逛 ads code
  10 +const ADS_CODE = {
  11 + boys: '41777aa7ac86347692c5aa0d394b2f59',
  12 + girls: '722253573823ebb994e313e71b0a4fb9',
  13 + lifestyle: '02568b6042510e4be739cc688dc7d6ae',
  14 + kids: '1ffdd6ea22c58af52ee6408cd353c2d5'
  15 +};
  16 +
  17 +module.exports = class extends global.yoho.BaseModel {
  18 + constructor(ctx) {
  19 + super(ctx);
  20 + }
  21 +
  22 + _formatArticle(rdata, params) {
  23 + let list = _.get(rdata, 'data.content', []);
  24 + let total = _.get(rdata, 'data.total', 0);
  25 + let lresult = {};
  26 + let width = 360;
  27 + let height = 240;
  28 +
  29 + let result = _.map(list, (articleData) => {
  30 + articleData.image += '?imageView2/{mode}/w/{width}/h/{height}';
  31 +
  32 + lresult = {
  33 + id: articleData.id,
  34 + classification: _.get(articleData, 'min_category_name', '') || _.get(articleData, 'category_name', ''),
  35 + url: helpers.urlFormat(`/news/${articleData.id}_${articleData.cid}.html`),
  36 + img: helpers.image(articleData.image, width, height, 1),
  37 + title: articleData.title,
  38 + pTime: articleData.update_time && moment(articleData.update_time * 1000).format('YYYY年MM月DD HH:mm'),
  39 + pView: articleData.views_num,
  40 + content: articleData.summary,
  41 + isVideo: articleData.videoUrl ? true : false
  42 + };
  43 +
  44 + return lresult;
  45 + });
  46 +
  47 + return {msgs: result, totalCount: total};
  48 + }
  49 +
  50 + getIndexList(channel, param) {
  51 + let newsAPi = new NewsAPi(this.ctx);
  52 + let params = {
  53 + type: 'wechat',
  54 + atype: param.atype || 'yohogroup',
  55 + limit: 20,
  56 + page: param.page || 1
  57 + };
  58 +
  59 + let apiMethod = [
  60 + newsAPi.getPolymerizationList(Object.assign({}, params, {id: param.atype || 'yohogroup'})),
  61 + ];
  62 +
  63 + return Promise.all(apiMethod).then(result => {
  64 + let responseData = {};
  65 +
  66 + // 列表数据
  67 + Object.assign(responseData, this._formatArticle(result[3], params));
  68 +
  69 + return responseData;
  70 + });
  71 + }
  72 +
  73 + _formatDetail(rdata) {
  74 + let contents = _.get(rdata, 'data.contents', {});
  75 + let header = {
  76 + title: contents.title,
  77 + summary: contents.summary,
  78 + tag: contents.tag,
  79 + time: contents.update_time && moment(contents.update_time * 1000).format('YYYY年MM月DD HH:mm'),
  80 + };
  81 + let content = utils.filterPhtml(contents.content, [
  82 + '阅读原文',
  83 + '点击这里',
  84 + '点这里'
  85 + ]);
  86 +
  87 + return {header: header, content: utils.filterAhtml(content)};
  88 + }
  89 +
  90 + detail(channel, param) {
  91 + let params = {
  92 + id: param.id,
  93 + cid: param.cid
  94 + };
  95 + let newsAPi = new NewsAPi(this.ctx);
  96 + let apiMethod = [
  97 + newsAPi.getContentDetail(params)
  98 + ];
  99 +
  100 + return Promise.all(apiMethod).then(result => {
  101 + let responseData = {};
  102 +
  103 + // 详情页数据
  104 + Object.assign(responseData, this._formatDetail(result[3], params));
  105 +
  106 + // seo
  107 + let title = _.get(responseData, 'header.title', '新闻详情页');
  108 + let keywords = [];
  109 + let tags = _.compact(_.get(responseData, 'header.tag', []).map(el => {
  110 + return el.tag_name;
  111 + }));
  112 +
  113 + keywords.push(title, tags.slice(0, 3).join('、'), 'YOHO!BUY有货');
  114 + Object.assign(responseData, {
  115 + title: `${title} | YOHO!BUY有货`,
  116 + keywords: keywords.join('、'),
  117 + description: _.get(responseData, 'header.summary', _.get(this.ctx, 'res.locals.description'))
  118 + });
  119 +
  120 + return responseData;
  121 + });
  122 + }
  123 +};
  1 +'use strict';
  2 +const _ = require('lodash');
  3 +const cheerio = require('cheerio');
  4 +
  5 +const util = {
  6 + // 过滤指定字符的p标签
  7 + filterPhtml: (html, filters) => {
  8 + if (!html) {
  9 + return html;
  10 + }
  11 +
  12 + let $ = cheerio.load(html, {decodeEntities: false});
  13 +
  14 + _.each($('p'), (item) => {
  15 + let ele = $(item);
  16 + let phtml = ele.html();
  17 +
  18 + _.each(filters, ft => {
  19 + if (phtml.indexOf(ft) >= 0) {
  20 + ele.remove();
  21 + }
  22 + });
  23 +
  24 + });
  25 +
  26 + return $.html();
  27 + },
  28 +
  29 + // 过滤 a标签连接和删除html标签中的script和link脚本
  30 + filterAhtml: (html) => {
  31 + if (!html) {
  32 + return html;
  33 + }
  34 +
  35 + let $ = cheerio.load(html, {decodeEntities: false});
  36 +
  37 + $('a').attr('href', 'javascript:void(0);').css({cursor: 'text'});// eslint-disable-line
  38 + $('script,link').remove();
  39 + return $.html();
  40 + }
  41 +};
  42 +
  43 +module.exports = util;
  1 +/**
  2 + * news
  3 + * @author: xiaoxiao<xiaoxiao.hao@yoho.cn>
  4 + * @date: 2017/10/13
  5 + */
  6 +
  7 +'use strict';
  8 +
  9 +const router = require('express').Router(); // eslint-disable-line
  10 +const cRoot = './controllers';
  11 +
  12 +const newsController = require(`${cRoot}/index`);
  13 +
  14 +router.get(['/', '/index/index'], newsController.index);
  15 +router.get(/\/([\d]+)_([\d]+).html/, newsController.detail);
  16 +
  17 +
  18 +// ajax
  19 +
  20 +
  21 +module.exports = router;
  1 +<div class="news-detail-page news-page yoho-page clearfix">
  2 + {{> common/path-nav}}
  3 + <div class="left-side detail-body" data-id="{{id}}">
  4 + {{# header}}
  5 + <h1 class="detail-title">{{title}}</h1>
  6 + <div class="article-info clearfix">
  7 + {{#if authorUrl}}
  8 + <div class="article-author">
  9 + <div class="author-avatar">
  10 + <a href="{{authorUrl}}" target="_blank">
  11 + <img src="{{image2 avatar}}">
  12 + </a>
  13 + </div>
  14 + </div>
  15 + <div class="author-info">
  16 + <a class="author-name" href="{{authorUrl}}">{{name}}</a>
  17 + </div>
  18 + {{/if}}
  19 + <div class="article-status clearfix">
  20 + {{#if time}}
  21 + <span class="article-time">
  22 + <i class="iconfont">&#xe625;</i>
  23 + {{time}}
  24 + </span>
  25 + {{/if}}
  26 + {{#if click}}
  27 + <span class="article-click">点击:<em>{{click}}</em></span>
  28 + {{/if}}
  29 + {{#if commentNum}}
  30 + <a href="#comment-info" id="article-comment" class="article-comment"><em class="comment-num">{{commentNum}}</em>条评论</a>
  31 + {{/if}}
  32 + </div>
  33 + </div>
  34 + {{/ header}}
  35 +
  36 + <div class="article-main">
  37 + {{{content}}}
  38 + </div>
  39 +
  40 + </div>
  41 + <div class="right-side detail-side">
  42 + {{> news-right-side}}
  43 + </div>
  44 +</div>
  1 +<div class="news-list-page news-page yoho-page" id="guangList">
  2 + {{# msgs}}
  3 + <div id="info-list" class="info-list-container">
  4 + {{# infos}}
  5 + <div class="info-list{{^show}} hide{{/show}}">
  6 + {{# info}}
  7 + {{> index/info}}
  8 + {{/ info}}
  9 + </div>
  10 + {{/ infos}}
  11 + </div>
  12 +
  13 + <div id="load-more-info" class="load-more-info">
  14 + <div class="loading status hide">
  15 + 正在加载...
  16 + </div>
  17 + <span class="no-more status">没有更多啦</span>
  18 + </div>
  19 + {{/ msgs}}
  20 +</div>
  21 +{{> footer-tab}}
  1 +<div class="guang-info" data-id="{{id}}">
  2 + {{# author}}
  3 + <a class="info-author clearfix"{{#if url}} href="{{url}}"{{/if}}>
  4 + <img class="lazy avatar" data-original="{{image2 avatar mode=2 q=60}}">
  5 + <span class="name">{{name}}</span>
  6 + {{#if minCategory}}
  7 + <span class="min-tag">#{{minCategory}}</span>
  8 + {{/if}}
  9 + </a>
  10 + {{/ author}}
  11 +
  12 + <div class="info-img">
  13 + {{#if showTags}}
  14 + <a href="javascript:;" class="info-match hide">
  15 + {{# isTip}}
  16 + 小贴士
  17 + <div class="info-tag tip"></div>
  18 + {{/ isTip}}
  19 + {{# isCollocation}}
  20 + 搭配
  21 + <div class="info-tag collocation"></div>
  22 + {{/ isCollocation}}
  23 + {{# isFashionMan}}
  24 + 潮人
  25 + <div class="info-tag fashion-man"></div>
  26 + {{/ isFashionMan}}
  27 + {{# isFashionGood}}
  28 + 潮品
  29 + <div class="info-tag fashion-good"></div>
  30 + {{/ isFashionGood}}
  31 + {{# isTopic}}
  32 + 话题
  33 + <div class="info-tag topic"></div>
  34 + {{/ isTopic}}
  35 + {{# isSpecialTopic}}
  36 + 专题
  37 + <div class="info-tag special-topic"></div>
  38 + {{/ isSpecialTopic}}
  39 + {{# isVideo}}
  40 + 视频
  41 + <div class="info-tag video"></div>
  42 + {{/ isVideo}}
  43 + {{# isShow}}
  44 + SHOW
  45 + <div class="info-tag show"></div>
  46 + {{/ isShow}}
  47 + </a>
  48 + {{/if}}
  49 + <a class="img-box"{{#unless isShow}} href="{{url}}"{{/unless}}>
  50 + {{#if @first}}
  51 + <img src="{{image2 img q=60}}" alt="{{alt}}">
  52 + {{^}}
  53 + <img class="lazy" data-original="{{image2 img q=60}}" alt="{{alt}}">
  54 + {{/if}}
  55 + {{#if isVideo}}
  56 + <div class="play"></div>
  57 + {{/if}}
  58 + </a>
  59 + </div>
  60 +
  61 + <div class="info-deps">
  62 + <a class="info-title-container"{{#unless isShow}} href="{{url}}"{{/unless}}>
  63 + <div class="info-title">{{#if top}}<span class="top-tag">置顶</span>{{/if}}{{title}}</div>
  64 + </a>
  65 + <p class="info-text">{{text}}</p>
  66 + {{> index/tvls}}
  67 + </div>
  68 +
  69 + {{#unless @root.isApp}}
  70 + {{#if productList}}
  71 + <div class="product-list-box">
  72 + <ul class="product-list">
  73 + {{#productList}}
  74 + {{> index/product-list}}
  75 + {{/productList}}
  76 + </ul>
  77 + </div>
  78 + {{/if}}
  79 + {{/unless}}
  80 +</div>
  1 +<li class="goods-box">
  2 + <div class="goods-img">
  3 + <a href="{{href}}">
  4 + <img class="lazy" data-original="{{image2 pic_url w=106 h=138}}" />
  5 + </a>
  6 + </div>
  7 + <div class="goods-info">
  8 + <a href="{{href}}">
  9 + <p class="title">{{product_name}}</p>
  10 + <p class="price">¥{{price}}</p>
  11 + </a>
  12 + </div>
  13 +</li>
  1 +<div class="time-view-like-share clearfix">
  2 + <i class="iconfont">&#xe603;</i>
  3 + {{publishTime}}&nbsp;&nbsp;&nbsp;&nbsp;
  4 + <i class="iconfont">&#xe602;</i>
  5 + <span class="page-view">{{pageView}}</span>
  6 + <div class="like-share-container">
  7 + {{#like}}
  8 + <i class="iconfont like-btn{{#isLiked}} like{{/isLiked}}">&#xe601;</i>
  9 + <span class="like-count">{{count}}</span>
  10 + {{/ like}}
  11 + {{# collect}}
  12 + <i class="iconfont collect-btn{{#isCollected}} collected{{/isCollected}}">&#xe605;</i>
  13 + {{/ collect}}
  14 + {{# share}}
  15 + <a href="{{.}}" class="iconfont share-btn">&#xe600;</a>
  16 + {{/ share}}
  17 + </div>
  18 +</div>
@@ -19,7 +19,8 @@ const domains = { @@ -19,7 +19,8 @@ const domains = {
19 imCs: 'http://im.yohobuy.com/api', 19 imCs: 'http://im.yohobuy.com/api',
20 platformApi: 'http://192.168.102.48:8088/', 20 platformApi: 'http://192.168.102.48:8088/',
21 store: 'http://192.168.102.47:8080/portal-gateway/wechat/', 21 store: 'http://192.168.102.47:8080/portal-gateway/wechat/',
22 - extstore: 'http://extstore-test1.yohops.com' 22 + extstore: 'http://extstore-test1.yohops.com',
  23 + yohoNowApi: 'http://yohonow-test.yohops.com:9999/',
23 }; 24 };
24 25
25 module.exports = { 26 module.exports = {
@@ -144,7 +145,8 @@ if (isProduction) { @@ -144,7 +145,8 @@ if (isProduction) {
144 imSocket: 'wss://imsocket.yohobuy.com:443', 145 imSocket: 'wss://imsocket.yohobuy.com:443',
145 imCs: 'https://imhttp.yohobuy.com/api', 146 imCs: 'https://imhttp.yohobuy.com/api',
146 platformApi: 'http://api.platform.yohoops.org', 147 platformApi: 'http://api.platform.yohoops.org',
147 - extstore: 'http://extstore.yohobuy.com' 148 + extstore: 'http://extstore.yohobuy.com',
  149 + yohoNowApi: 'http://new.yohoboys.com/',
148 }, 150 },
149 memcache: { 151 memcache: {
150 master: ['memcache1.yohoops.org:12111', 'memcache2.yohoops.org:12111', 'memcache3.yohoops.org:12111'], 152 master: ['memcache1.yohoops.org:12111', 'memcache2.yohoops.org:12111', 'memcache3.yohoops.org:12111'],
@@ -220,7 +222,8 @@ if (isProduction) { @@ -220,7 +222,8 @@ if (isProduction) {
220 imSocket: process.env.TEST_IM_SOCKET || 'ws://socket.yohobuy.com:10240', 222 imSocket: process.env.TEST_IM_SOCKET || 'ws://socket.yohobuy.com:10240',
221 imCs: process.env.TEST_IM_CS || 'http://im.yohobuy.com/api', 223 imCs: process.env.TEST_IM_CS || 'http://im.yohobuy.com/api',
222 platformApi: 'http://192.168.102.48:8088/', 224 platformApi: 'http://192.168.102.48:8088/',
223 - extstore: 'http://extstore-test1.yohops.com' 225 + extstore: 'http://extstore-test1.yohops.com',
  226 + yohoNowApi: process.env.YOHO_NOW_API || 'http://yohonow-test.yohops.com:9999/',
224 }, 227 },
225 memcache: { 228 memcache: {
226 master: ['127.0.0.1:12111'], 229 master: ['127.0.0.1:12111'],
@@ -16,6 +16,7 @@ module.exports = app => { @@ -16,6 +16,7 @@ module.exports = app => {
16 // 业务模块 16 // 业务模块
17 app.use('/product', require('./apps/product')); 17 app.use('/product', require('./apps/product'));
18 app.use('/guang', require('./apps/guang')); 18 app.use('/guang', require('./apps/guang'));
  19 + app.use('/news', require('./apps/news'));
19 app.use('/activity', require('./apps/activity')); 20 app.use('/activity', require('./apps/activity'));
20 app.use('/cart', require('./apps/cart')); 21 app.use('/cart', require('./apps/cart'));
21 22