Authored by htoooth

Merge branch 'master' into feature/yohood

@@ -16,11 +16,19 @@ const index = co(function* (channel) { @@ -16,11 +16,19 @@ const index = co(function* (channel) {
16 16
17 const removeBlack = (remoteIp) => { 17 const removeBlack = (remoteIp) => {
18 let key = `pc:limiter:${remoteIp}`, 18 let key = `pc:limiter:${remoteIp}`,
19 - key10m = `pc:limiter:10m:${remoteIp}`; 19 + key10m = `pc:limiter:10m:${remoteIp}`,
  20 + keyMax = `pc:limiter:max:${remoteIp}`,
  21 + key10mMax = `pc:limiter:10m:max:${remoteIp}`,
  22 + synchronizeKey = `pc:limiter:synchronize:${remoteIp}`,
  23 + spiderKey = `pc:limiter:spider:${remoteIp}`;
20 24
21 return Promise.all([ 25 return Promise.all([
22 cache.delAsync(key), 26 cache.delAsync(key),
23 - cache.delAsync(key10m) 27 + cache.delAsync(key10m),
  28 + cache.delAsync(keyMax),
  29 + cache.delAsync(key10mMax),
  30 + cache.delAsync(synchronizeKey),
  31 + cache.delAsync(spiderKey)
24 ]); 32 ]);
25 }; 33 };
26 34
@@ -125,7 +125,8 @@ const orderSubmitAsync = (uid, cartType, addressId, deliveryTime, deliveryWay, p @@ -125,7 +125,8 @@ const orderSubmitAsync = (uid, cartType, addressId, deliveryTime, deliveryWay, p
125 invoices_type: other.invoicesType, 125 invoices_type: other.invoicesType,
126 invoices_title: other.invoicesTitle, 126 invoices_title: other.invoicesTitle,
127 invoice_content: other.invoicesContent, 127 invoice_content: other.invoicesContent,
128 - receiverMobile: other.receiverMobile 128 + receiverMobile: other.receiverMobile,
  129 + buyerTaxNumber: other.taxNumber
129 }); 130 });
130 } 131 }
131 132
@@ -70,6 +70,15 @@ const submit = (uid, cartType, p, remoteIp) => { @@ -70,6 +70,15 @@ const submit = (uid, cartType, p, remoteIp) => {
70 p.addressId = crypto.decrypt('', `${p.addressId}`); 70 p.addressId = crypto.decrypt('', `${p.addressId}`);
71 } 71 }
72 72
  73 + // 5.8.1 发票优化需求
  74 + // 只接受电子发票(1 纸质 2 电子),发票内容为明细(id 12:明细)
  75 + if (p.invoicesType) {
  76 + Object.assign(p, {
  77 + invoicesType: 2,
  78 + invoicesContent: 12
  79 + });
  80 + }
  81 +
73 return ensureApi.orderSubmitAsync(uid, cartType, p.addressId, p.deliveryTime, 82 return ensureApi.orderSubmitAsync(uid, cartType, p.addressId, p.deliveryTime,
74 p.deliveryWay, p.paymentType, p.paymentId, p.printPrice, p, remoteIp).then(result => { 83 p.deliveryWay, p.paymentType, p.paymentId, p.printPrice, p, remoteIp).then(result => {
75 if (result.code === 200) { 84 if (result.code === 200) {
@@ -77,21 +77,22 @@ @@ -77,21 +77,22 @@
77 <script id="invoice-chose-tpl" type="text/html"> 77 <script id="invoice-chose-tpl" type="text/html">
78 <div class="invoice-close"><i class="iconfont">&#xe60d;</i></div> 78 <div class="invoice-close"><i class="iconfont">&#xe60d;</i></div>
79 <p class="invoice-header">发票信息</p> 79 <p class="invoice-header">发票信息</p>
80 - <ul class="invoice-type">  
81 - <li class="el-invoice focus">电子发票</li>  
82 - <li class="pa-invoice">纸质发票</li>  
83 - </ul>  
84 <div class="invoice-content el-content"> 80 <div class="invoice-content el-content">
  81 + <p class="invoice-type-text">
  82 + <span class="row-title">发&nbsp;票&nbsp;类&nbsp;型:</span>
  83 + 电子发票
  84 + </p>
  85 +
85 <p class="el-tip"> 86 <p class="el-tip">
86 ※ 电子发票是税务局认可的有效凭证,其法律效力、基本用途及使用规定同纸质发票,如需纸质发票可自行下载打印。<br> 87 ※ 电子发票是税务局认可的有效凭证,其法律效力、基本用途及使用规定同纸质发票,如需纸质发票可自行下载打印。<br>
87 - <a href="/help/detail?id=33&contId=139" target="_blank">什么是电子发票?</a> 88 + <a href="/help/detail?id=33&contId=139" target="_blank">查看发票须知</a>
88 </p> 89 </p>
89 90
90 <ul> 91 <ul>
91 <li class="invoice-title invoice-row"> 92 <li class="invoice-title invoice-row">
92 <span class="row-title"> 93 <span class="row-title">
93 <em>*</em> 94 <em>*</em>
94 -票抬头 95 +&nbsp;票&nbsp;抬&nbsp;头:
95 </span> 96 </span>
96 <div class="row-content"> 97 <div class="row-content">
97 <span class="radio-wrap"> 98 <span class="radio-wrap">
@@ -100,36 +101,41 @@ @@ -100,36 +101,41 @@
100 <span class="radio-wrap"> 101 <span class="radio-wrap">
101 <label class="rbt-2 radio-btn" data-id="2"></label> 单位 102 <label class="rbt-2 radio-btn" data-id="2"></label> 单位
102 </span> 103 </span>
103 -  
104 - <div class="company-row hide"> 104 + </div>
  105 + </li>
  106 + <li class="invoice-title-name invoice-row company-row hide">
  107 + <span class="row-title">
  108 + <em>*</em>
  109 + 单&nbsp;位&nbsp;名&nbsp;称:
  110 + </span>
  111 + <div class="row-content">
105 <input id="company-name" class="company-name" type="text" placeholder="请填写单位名称"> 112 <input id="company-name" class="company-name" type="text" placeholder="请填写单位名称">
106 - <span class="input-tip invoice-title-tip red hide"> 113 + <span class="input-tip company-name-tip red hide">
107 <span class="iconfont">&#xe629;</span> 114 <span class="iconfont">&#xe629;</span>
108 - 请填写发票抬头 115 + <em>请填写发票抬头</em>
109 </span> 116 </span>
110 </div> 117 </div>
111 - </div>  
112 </li> 118 </li>
113 - <li class="invoice-goods-type invoice-row"> 119 + <li class="invoice-tax-num invoice-row company-row hide">
114 <span class="row-title"> 120 <span class="row-title">
115 <em>*</em> 121 <em>*</em>
116 - 发票内容 122 + 税&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;号
117 </span> 123 </span>
118 <div class="row-content"> 124 <div class="row-content">
119 - {{#each invoices.invoiceContentList}}  
120 - <span class="radio-wrap">  
121 - <label class="radio-btn rbc-{{invoices_type_id}}{{#if @first}} on{{/if}}" data-id="{{invoices_type_id}}" data-name="{{invoices_type_name}}"></label> {{invoices_type_name}} 125 + <input id="company-tax-num" class="company-tax-num" type="text" placeholder="请输入正确的纳税人识别号">
  126 + <span class="input-tip company-tax-tip red hide">
  127 + <span class="iconfont">&#xe629;</span>
  128 + <em>请填写纳税人识别号</em>
122 </span> 129 </span>
123 - {{/each}}  
124 </div> 130 </div>
125 </li> 131 </li>
126 <li class="receiver invoice-row" data-full="{{receiverMobile}}" data-hide="{{hideReceiverMobile}}"> 132 <li class="receiver invoice-row" data-full="{{receiverMobile}}" data-hide="{{hideReceiverMobile}}">
127 <span class="row-title"> 133 <span class="row-title">
128 <em>*</em> 134 <em>*</em>
129 -机号码: 135 +&nbsp;机&nbsp;号&nbsp;码:
130 </span> 136 </span>
131 <div class="row-content"> 137 <div class="row-content">
132 - <input id="receiver-phone" class="receiver-phone" type="text"> 138 + <input id="receiver-phone" class="receiver-phone" type="text" placeholder="可通过手机号码在发票服务平台查询">
133 <span class="input-tip receiver-tip red hide"> 139 <span class="input-tip receiver-tip red hide">
134 <span class="iconfont">&#xe629;</span> 140 <span class="iconfont">&#xe629;</span>
135 <em></em> 141 <em></em>
@@ -295,7 +295,7 @@ module.exports = class extends global.yoho.BaseModel { @@ -295,7 +295,7 @@ module.exports = class extends global.yoho.BaseModel {
295 ht = `${params.keywords}`; 295 ht = `${params.keywords}`;
296 } else { 296 } else {
297 nav = _this._getNav(params.id, menuData, params.url); 297 nav = _this._getNav(params.id, menuData, params.url);
298 - ht = _.last(nav).name; 298 + ht = _.get(_.last(nav), 'name', '');
299 } 299 }
300 300
301 return { 301 return {
@@ -135,7 +135,6 @@ @@ -135,7 +135,6 @@
135 {{/if}} 135 {{/if}}
136 </div> 136 </div>
137 <p>发票抬头:{{title}}</p> 137 <p>发票抬头:{{title}}</p>
138 - <p>发票内容:{{contentValue}}</p>  
139 {{^}} 138 {{^}}
140 <p>暂不需要发票 139 <p>暂不需要发票
141 {{/if}} 140 {{/if}}
@@ -42,11 +42,14 @@ const index = (req, res, next) => { @@ -42,11 +42,14 @@ const index = (req, res, next) => {
42 return res.render('search/no-result', resData); 42 return res.render('search/no-result', resData);
43 } 43 }
44 44
  45 + if (queryKey) {
45 Object.assign(resData, { 46 Object.assign(resData, {
46 title: `${queryKey}价格_图片_品牌_怎么样-YOHO!BUY有货`, 47 title: `${queryKey}价格_图片_品牌_怎么样-YOHO!BUY有货`,
47 keywords: `${queryKey},${queryKey}价格,${queryKey}图片,${queryKey}怎么样,${queryKey}品牌,YOHO!BUY有货`, 48 keywords: `${queryKey},${queryKey}价格,${queryKey}图片,${queryKey}怎么样,${queryKey}品牌,YOHO!BUY有货`,
48 description: `YOHO!BUY有货网yohobuy.com是国内专业的${queryKey}网上潮流购物商城,为您找到${_.get(resData, 'search.totalCount', 0)}${queryKey}、产品的详细参数,实时报价,价格行情,图片、评价、品牌等信息。买${queryKey},就上YOHO!BUY有货` // eslint-disable-line 49 description: `YOHO!BUY有货网yohobuy.com是国内专业的${queryKey}网上潮流购物商城,为您找到${_.get(resData, 'search.totalCount', 0)}${queryKey}、产品的详细参数,实时报价,价格行情,图片、评价、品牌等信息。买${queryKey},就上YOHO!BUY有货` // eslint-disable-line
49 }); 50 });
  51 + }
  52 +
50 res.render('search/index', resData); 53 res.render('search/index', resData);
51 54
52 }); 55 });
@@ -11,7 +11,7 @@ module.exports = (url, width, height) => { @@ -11,7 +11,7 @@ module.exports = (url, width, height) => {
11 <a href="javascript:;" class="video-close-btn"></a> 11 <a href="javascript:;" class="video-close-btn"></a>
12 <object id="video_0" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" 12 <object id="video_0" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab"
13 classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"> 13 classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000">
14 - <param value="http://rescdn.yohoboys.com/res/new/boys/swf/util/VideoPlayerNew.swf?id=video_0&amp;url=${url}&amp;language=1&amp;sharepic=http://img01.yohoboys.com/contentimg/2016/06/12/14/0183fabea5332c5902cdd1d0d7fb63df04.jpg?imageMogr2/thumbnail/738x424|watermark/1/image/aHR0cDovL3Jlc2Nkbi55b2hvYm95cy5jb20vcmVzL25ldy9ib3lzL2ltYWdlcy9iYW5uZXJwbGF5LnBuZw==/dissolve/100/gravity/Center/dx/10/dy/10&amp;title1=Meet...Cody Sanderson &amp;shareurl=http://www.yohoboys.com/channel/detail/index/id/10657/time/1478052894&amp;coverpic=http://img01.yohoboys.com/contentimg/2016/06/12/14/0183fabea5332c5902cdd1d0d7fb63df04.jpg" 14 + <param value="//cdn.yoho.cn/tool/VideoPlayerNew.swf?id=video_0&amp;url=${url}&amp;language=1&amp;sharepic=http://img01.yohoboys.com/contentimg/2016/06/12/14/0183fabea5332c5902cdd1d0d7fb63df04.jpg?imageMogr2/thumbnail/738x424|watermark/1/image/aHR0cDovL3Jlc2Nkbi55b2hvYm95cy5jb20vcmVzL25ldy9ib3lzL2ltYWdlcy9iYW5uZXJwbGF5LnBuZw==/dissolve/100/gravity/Center/dx/10/dy/10&amp;title1=Meet...Cody Sanderson &amp;shareurl=http://www.yohoboys.com/channel/detail/index/id/10657/time/1478052894&amp;coverpic=http://img01.yohoboys.com/contentimg/2016/06/12/14/0183fabea5332c5902cdd1d0d7fb63df04.jpg"
15 name="movie"> 15 name="movie">
16 <param value="high" name="quality"> 16 <param value="high" name="quality">
17 <param value="#ffffff" name="bgcolor"> 17 <param value="#ffffff" name="bgcolor">
@@ -21,7 +21,7 @@ module.exports = (url, width, height) => { @@ -21,7 +21,7 @@ module.exports = (url, width, height) => {
21 <embed style="width: ${width}px;height: ${height}px;" id="flash" align="middle" pluginspage="http://www.macromedia.com/go/getflashplayer" 21 <embed style="width: ${width}px;height: ${height}px;" id="flash" align="middle" pluginspage="http://www.macromedia.com/go/getflashplayer"
22 type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" loop="false" 22 type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" loop="false"
23 play="true" bgcolor="#ffffff" quality="high" wmode="transparent" name="vMessage" 23 play="true" bgcolor="#ffffff" quality="high" wmode="transparent" name="vMessage"
24 - src="http://rescdn.yohoboys.com/res/new/boys/swf/util/VideoPlayerNew.swf?id=video_0&amp;url=${url}&amp;language=1&amp;sharepic=http://img01.yohoboys.com/contentimg/2016/06/12/14/0183fabea5332c5902cdd1d0d7fb63df04.jpg?imageMogr2/thumbnail/738x424|watermark/1/image/aHR0cDovL3Jlc2Nkbi55b2hvYm95cy5jb20vcmVzL25ldy9ib3lzL2ltYWdlcy9iYW5uZXJwbGF5LnBuZw==/dissolve/100/gravity/Center/dx/10/dy/10&amp;title1=Meet...Cody Sanderson &amp;shareurl=http://www.yohoboys.com/channel/detail/index/id/10657/time/1478052894&amp;coverpic=${url}?vframe/jpg/offset/0"> 24 + src="//cdn.yoho.cn/tool/VideoPlayerNew.swf?id=video_0&amp;url=${url}&amp;language=1&amp;sharepic=http://img01.yohoboys.com/contentimg/2016/06/12/14/0183fabea5332c5902cdd1d0d7fb63df04.jpg?imageMogr2/thumbnail/738x424|watermark/1/image/aHR0cDovL3Jlc2Nkbi55b2hvYm95cy5jb20vcmVzL25ldy9ib3lzL2ltYWdlcy9iYW5uZXJwbGF5LnBuZw==/dissolve/100/gravity/Center/dx/10/dy/10&amp;title1=Meet...Cody Sanderson &amp;shareurl=http://www.yohoboys.com/channel/detail/index/id/10657/time/1478052894&amp;coverpic=${url}?vframe/jpg/offset/0">
25 </object> 25 </object>
26 </div> `; 26 </div> `;
27 }; 27 };
@@ -64,7 +64,7 @@ const setSearchData = (result, params, channel) => { @@ -64,7 +64,7 @@ const setSearchData = (result, params, channel) => {
64 _.get(finalResult, 'search.leftContent.sort', {})); 64 _.get(finalResult, 'search.leftContent.sort', {}));
65 65
66 filters.checkedConditions.conditions = _.concat(filters.checkedConditions.conditions, 66 filters.checkedConditions.conditions = _.concat(filters.checkedConditions.conditions,
67 - finalResult.search.leftContent.checked); 67 + _.get(finalResult, 'search.leftContent.checked', []));
68 68
69 Object.assign(finalResult.search, 69 Object.assign(finalResult.search,
70 searchHandler.handlePathNavData({total: result[2].data.total }, params, 'search', channel), 70 searchHandler.handlePathNavData({total: result[2].data.total }, params, 'search', channel),
@@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
7 7
8 'use strict'; 8 'use strict';
9 const aes = require('./aes-pwd'); 9 const aes = require('./aes-pwd');
  10 +const helpers = global.yoho.helpers;
10 const common = require('../../../config/common'); 11 const common = require('../../../config/common');
11 const clientApi = require('../models/client-api'); 12 const clientApi = require('../models/client-api');
12 const clientService = require('../models/client-service'); 13 const clientService = require('../models/client-service');
@@ -16,10 +17,11 @@ const clientService = require('../models/client-service'); @@ -16,10 +17,11 @@ const clientService = require('../models/client-service');
16 */ 17 */
17 const index = (req, res, next) => { 18 const index = (req, res, next) => {
18 let type = 2; 19 let type = 2;
  20 + let uid = req.user.uid;
19 let reg = /MSIE\s?[987]\.0/i; 21 let reg = /MSIE\s?[987]\.0/i;
20 let userAgent = req.headers['user-agent']; 22 let userAgent = req.headers['user-agent'];
21 let unSupport = reg.test(userAgent); 23 let unSupport = reg.test(userAgent);
22 - let encryptedUid = aes.encryptionUid(req.user.uid); 24 + let encryptedUid = aes.encryptionUid(uid);
23 let domains = common.domains; 25 let domains = common.domains;
24 let imCs = domains.imCs; 26 let imCs = domains.imCs;
25 let imSocket = domains.imSocket; 27 let imSocket = domains.imSocket;
@@ -36,8 +38,11 @@ const index = (req, res, next) => { @@ -36,8 +38,11 @@ const index = (req, res, next) => {
36 layout: false 38 layout: false
37 }); 39 });
38 } else { 40 } else {
39 - clientService.getClientData(type, encryptedUid) 41 + clientService.getClientData(uid, type, encryptedUid)
40 .then(result => { 42 .then(result => {
  43 + if (result.code === 401) {
  44 + return res.redirect(helpers.urlFormat('/signin.html'));
  45 + }
41 res.render('client', Object.assign(data, result)); 46 res.render('client', Object.assign(data, result));
42 }).catch(next); 47 }).catch(next);
43 } 48 }
@@ -58,10 +63,11 @@ const domains = (req, res) => { @@ -58,10 +63,11 @@ const domains = (req, res) => {
58 * @param next 63 * @param next
59 */ 64 */
60 const history = (req, res, next) => { 65 const history = (req, res, next) => {
61 - const encId = aes.encryptionUid(req.user.uid); 66 + const uid = req.user.uid;
  67 + const encId = aes.encryptionUid(uid);
62 const endTime = req.body.endTime; 68 const endTime = req.body.endTime;
63 69
64 - clientApi.getMsgHistory(encId, endTime) 70 + clientApi.getMsgHistory(uid, encId, endTime)
65 .then(result => { 71 .then(result => {
66 res.json(result); 72 res.json(result);
67 }).catch(next); 73 }).catch(next);
@@ -74,7 +80,10 @@ const history = (req, res, next) => { @@ -74,7 +80,10 @@ const history = (req, res, next) => {
74 * @param next 80 * @param next
75 */ 81 */
76 const saveEval = (req, res, next) => { 82 const saveEval = (req, res, next) => {
77 - const params = {}; 83 + const uid = req.user.uid;
  84 + const params = {
  85 + uid
  86 + };
78 87
79 params.stars = req.body.stars; 88 params.stars = req.body.stars;
80 params.promoter = req.body.promoter; 89 params.promoter = req.body.promoter;
@@ -102,9 +111,12 @@ const saveEval = (req, res, next) => { @@ -102,9 +111,12 @@ const saveEval = (req, res, next) => {
102 * @param next 111 * @param next
103 */ 112 */
104 const queryReason = (req, res, next) => { 113 const queryReason = (req, res, next) => {
  114 + const uid = req.user.uid;
105 const type = req.body.type; 115 const type = req.body.type;
  116 + const encryptedUid = aes.encryptionUid(uid);
  117 +
106 118
107 - clientApi.queryReason(type) 119 + clientApi.queryReason(uid, encryptedUid, type)
108 .then(result => { 120 .then(result => {
109 res.json(result); 121 res.json(result);
110 }).catch(next); 122 }).catch(next);
@@ -117,11 +129,12 @@ const queryReason = (req, res, next) => { @@ -117,11 +129,12 @@ const queryReason = (req, res, next) => {
117 * @param next 129 * @param next
118 */ 130 */
119 const saveMessage = (req, res, next) => { 131 const saveMessage = (req, res, next) => {
  132 + const uid = req.user.uid;
120 const content = req.body.content; 133 const content = req.body.content;
121 const encId = req.body.encryptedUid; 134 const encId = req.body.encryptedUid;
122 const cvId = req.body.conversationId; 135 const cvId = req.body.conversationId;
123 136
124 - clientApi.saveMessage(content, encId, cvId) 137 + clientApi.saveMessage(content, encId, cvId, uid)
125 .then(result => { 138 .then(result => {
126 res.json(result); 139 res.json(result);
127 }).catch(next); 140 }).catch(next);
@@ -32,8 +32,9 @@ let urls = { @@ -32,8 +32,9 @@ let urls = {
32 * @param { string } encryptedUid 用户ID 32 * @param { string } encryptedUid 用户ID
33 * @return { Object } 最近10条订单 33 * @return { Object } 最近10条订单
34 */ 34 */
35 -const getLastTenOrders = (encryptedUid) => { 35 +const getLastTenOrders = (uid, encryptedUid) => {
36 return api.post(urls.lastTen, { 36 return api.post(urls.lastTen, {
  37 + uid,
37 encryptedUid 38 encryptedUid
38 }); 39 });
39 }; 40 };
@@ -43,8 +44,11 @@ const getLastTenOrders = (encryptedUid) => { @@ -43,8 +44,11 @@ const getLastTenOrders = (encryptedUid) => {
43 * @function getQas 44 * @function getQas
44 * @return { Object } 问答列表 45 * @return { Object } 问答列表
45 */ 46 */
46 -const getQas = () => {  
47 - return api.post(urls.qas, {}); 47 +const getQas = (uid, encryptedUid) => {
  48 + return api.post(urls.qas, {
  49 + uid,
  50 + encryptedUid
  51 + });
48 }; 52 };
49 53
50 /** 54 /**
@@ -65,8 +69,10 @@ const getCsSetting = (type) => { @@ -65,8 +69,10 @@ const getCsSetting = (type) => {
65 * @param { string } endTime 截止时间 69 * @param { string } endTime 截止时间
66 * @return { Array } 历史聊天记录 70 * @return { Array } 历史聊天记录
67 */ 71 */
68 -const getMsgHistory = (encryptedUid, endTime) => { 72 +const getMsgHistory = (uid, encryptedUid, endTime) => {
  73 +
69 let params = { 74 let params = {
  75 + uid,
70 encryptedUid 76 encryptedUid
71 }; 77 };
72 78
@@ -91,9 +97,11 @@ const saveEval = (params) => { @@ -91,9 +97,11 @@ const saveEval = (params) => {
91 * @param cvId 97 * @param cvId
92 * @returns {*} 98 * @returns {*}
93 */ 99 */
94 -const queryReason = (type) => { 100 +const queryReason = (uid, encryptedUid, type) => {
95 const params = { 101 const params = {
96 - type 102 + uid,
  103 + type,
  104 + encryptedUid
97 }; 105 };
98 106
99 return api.post(urls.evalReason, params); 107 return api.post(urls.evalReason, params);
@@ -106,8 +114,9 @@ const queryReason = (type) => { @@ -106,8 +114,9 @@ const queryReason = (type) => {
106 * @param cvId 会话ID 114 * @param cvId 会话ID
107 * @returns {*} 115 * @returns {*}
108 */ 116 */
109 -const saveMessage = (content, encId, cvId) => { 117 +const saveMessage = (content, encId, cvId, uid) => {
110 const params = { 118 const params = {
  119 + uid,
111 content, 120 content,
112 encryptedUid: encId, 121 encryptedUid: encId,
113 conversationId: cvId 122 conversationId: cvId
@@ -17,7 +17,7 @@ const clientAPI = require('./client-api'); @@ -17,7 +17,7 @@ const clientAPI = require('./client-api');
17 * @param { string } encryptedUid 17 * @param { string } encryptedUid
18 * @return { Object } 客服设置 18 * @return { Object } 客服设置
19 */ 19 */
20 -const getClientData = (type, encryptedUid) => { 20 +const getClientData = (uid, type, encryptedUid) => {
21 const logoSize = '136x40'; 21 const logoSize = '136x40';
22 const qcSize = '135x135'; 22 const qcSize = '135x135';
23 const advSize = '160x335'; 23 const advSize = '160x335';
@@ -25,9 +25,9 @@ const getClientData = (type, encryptedUid) => { @@ -25,9 +25,9 @@ const getClientData = (type, encryptedUid) => {
25 25
26 let apiMethod = [ 26 let apiMethod = [
27 clientAPI.getCsSetting(type), 27 clientAPI.getCsSetting(type),
28 - clientAPI.getMsgHistory(encryptedUid),  
29 - clientAPI.getLastTenOrders(encryptedUid),  
30 - clientAPI.getQas() 28 + clientAPI.getMsgHistory(uid, encryptedUid),
  29 + clientAPI.getLastTenOrders(uid, encryptedUid),
  30 + clientAPI.getQas(uid, encryptedUid)
31 ]; 31 ];
32 32
33 return Promise.all(apiMethod) 33 return Promise.all(apiMethod)
@@ -38,6 +38,14 @@ const getClientData = (type, encryptedUid) => { @@ -38,6 +38,14 @@ const getClientData = (type, encryptedUid) => {
38 let records = []; 38 let records = [];
39 let hasHistory = false; 39 let hasHistory = false;
40 40
  41 + if (res[1].code === 401 ||
  42 + res[2].code === 401 ||
  43 + res[3].code === 401) {
  44 + return {
  45 + code: 401
  46 + };
  47 + }
  48 +
41 if (res[0] && res[0].code === 200) { 49 if (res[0] && res[0].code === 200) {
42 if (res[0].data.config) { 50 if (res[0].data.config) {
43 csSetting = res[0].data.config; 51 csSetting = res[0].data.config;
@@ -5,6 +5,7 @@ const logger = global.yoho.logger; @@ -5,6 +5,7 @@ const logger = global.yoho.logger;
5 const ip = require('./rules/ip-list'); 5 const ip = require('./rules/ip-list');
6 const userAgent = require('./rules/useragent'); 6 const userAgent = require('./rules/useragent');
7 const qpsLimiter = require('./rules/qps-limit'); 7 const qpsLimiter = require('./rules/qps-limit');
  8 +const asynchronous = require('./rules/asynchronous');
8 9
9 // const fakerLimiter = require('./rules/faker-limit'); 10 // const fakerLimiter = require('./rules/faker-limit');
10 const captchaPolicy = require('./policies/captcha'); 11 const captchaPolicy = require('./policies/captcha');
@@ -53,7 +54,8 @@ module.exports = (req, res, next) => { @@ -53,7 +54,8 @@ module.exports = (req, res, next) => {
53 Promise.all([ 54 Promise.all([
54 limiter(userAgent, captchaPolicy, context), 55 limiter(userAgent, captchaPolicy, context),
55 limiter(ip, captchaPolicy, context), 56 limiter(ip, captchaPolicy, context),
56 - limiter(qpsLimiter, captchaPolicy, context) 57 + limiter(qpsLimiter, captchaPolicy, context),
  58 + limiter(asynchronous, captchaPolicy, context)
57 59
58 // limiter(fakerLimiter, reporterPolicy, context) 60 // limiter(fakerLimiter, reporterPolicy, context)
59 ]).then((results) => { 61 ]).then((results) => {
  1 +'use strict';
  2 +
  3 +const cache = global.yoho.cache.master;
  4 +const _ = require('lodash');
  5 +const logger = global.yoho.logger;
  6 +
  7 +
  8 +const ASYNCHRONOUSPAGES = {
  9 + '/product/index/isFavoriteShop': 1,
  10 + '/common/suggestfeedback': 1,
  11 + '/product/detail/hotarea': 1,
  12 + '/common/getbanner': 1,
  13 + '/passport/cert/headerTip': 1
  14 +};
  15 +
  16 +function isNormalSpider(userAgent) {
  17 + let normalReg = /(spider)|(bot.html)/i;
  18 +
  19 + if (normalReg.test(userAgent)) {
  20 + return true;
  21 + } else {
  22 + return false;
  23 + }
  24 +
  25 +}
  26 +
  27 +module.exports = (limiter, policy) => {
  28 + const ua = limiter.req.header('User-Agent');
  29 + const synchronizeKey = `pc:limiter:synchronize:${limiter.remoteIp}`; // 同步
  30 + const asynchronousKey = `pc:limiter:asynchronous:${limiter.remoteIp}`; // 异步
  31 + const spiderKey = `pc:limiter:spider:${limiter.remoteIp}`; // 异步
  32 +
  33 + // 正常蜘蛛直接过
  34 + if (isNormalSpider(ua)) {
  35 + return Promise.resolve(true);
  36 + }
  37 +
  38 +
  39 + const req = limiter.req,
  40 + res = limiter.res;
  41 +
  42 + res.on('render', function() {
  43 + cache.incrAsync(synchronizeKey, 1).catch(e=>console.log(e)); // eslint-disable-line
  44 + });
  45 +
  46 + return cache.getMultiAsync([synchronizeKey, asynchronousKey, spiderKey]).then((results) => {
  47 + logger.debug(results);
  48 +
  49 + if (results[spiderKey]) {
  50 + return Promise.resolve(policy);
  51 + }
  52 +
  53 + // 默认数据设置
  54 + if (!results[synchronizeKey] && !_.isNumber(results[synchronizeKey])) {
  55 + cache.setAsync(synchronizeKey, 1, 600);
  56 + }
  57 +
  58 + // 默认数据设置
  59 + if (ASYNCHRONOUSPAGES[req.path] > 0) {
  60 + cache.setAsync(asynchronousKey, 1, 600);
  61 + cache.setAsync(synchronizeKey, 1, 600);
  62 + }
  63 +
  64 + if (results[synchronizeKey] > 10 && !results[asynchronousKey]) {
  65 + cache.setAsync(spiderKey, 1, 60 * 60 * 24);
  66 + return Promise.resolve(policy);
  67 + }
  68 +
  69 + return Promise.resolve(true);
  70 +
  71 + });
  72 +};
1 { 1 {
2 "name": "yohobuy-node", 2 "name": "yohobuy-node",
3 - "version": "5.8.4", 3 + "version": "5.8.7",
4 "private": true, 4 "private": true,
5 "description": "A New Yohobuy Project With Express", 5 "description": "A New Yohobuy Project With Express",
6 "repository": { 6 "repository": {
@@ -23,9 +23,18 @@ function validateInvoice($el, info) { @@ -23,9 +23,18 @@ function validateInvoice($el, info) {
23 // 发票抬头 23 // 发票抬头
24 if (!info.titleName) { 24 if (!info.titleName) {
25 pass = false; 25 pass = false;
26 - $('.invoice-title-tip', $el).removeClass('hide'); 26 + $('.company-name-tip', $el).removeClass('hide');
27 } else { 27 } else {
28 - $('.invoice-title-tip', $el).addClass('hide'); 28 + $('.company-name-tip', $el).addClass('hide');
  29 + }
  30 +
  31 + if (info.titleId === 2) {
  32 + if (!info.taxNumber) {
  33 + pass = false;
  34 + $('.company-tax-tip', $el).removeClass('hide');
  35 + } else {
  36 + $('.company-tax-tip', $el).addClass('hide');
  37 + }
29 } 38 }
30 39
31 // 收票人手机号 40 // 收票人手机号
@@ -38,7 +47,7 @@ function validateInvoice($el, info) { @@ -38,7 +47,7 @@ function validateInvoice($el, info) {
38 } else if (info.receiver === defaultReceiver.hide) { 47 } else if (info.receiver === defaultReceiver.hide) {
39 $receiverTip.addClass('hide'); 48 $receiverTip.addClass('hide');
40 } else if (!/^[0-9]{11}$/.test(info.receiver)) { 49 } else if (!/^[0-9]{11}$/.test(info.receiver)) {
41 - $receiverTip.removeClass('hide').find('em').html('手机号码不正确'); 50 + $receiverTip.removeClass('hide').find('em').html('请输入正确手机号');
42 pass = false; 51 pass = false;
43 } else { 52 } else {
44 $receiverTip.addClass('hide'); 53 $receiverTip.addClass('hide');
@@ -49,29 +58,9 @@ function validateInvoice($el, info) { @@ -49,29 +58,9 @@ function validateInvoice($el, info) {
49 } 58 }
50 59
51 function bindInvoiceEvent($el) { 60 function bindInvoiceEvent($el) {
52 - var $invoiceTypeWrap = $('.invoice-type', $el),  
53 - $titleWrap = $('.invoice-title', $el),  
54 - $goodsTypeWrap = $('.invoice-goods-type', $el),  
55 - $receiver = $('.receiver', $el), 61 + var $titleWrap = $('.invoice-title', $el),
56 $companyRow = $('.company-row', $el); 62 $companyRow = $('.company-row', $el);
57 63
58 - $invoiceTypeWrap.on('click', 'li', function() {  
59 - var $this = $(this);  
60 -  
61 - if ($this.hasClass('focus')) {  
62 - return;  
63 - }  
64 -  
65 - if ($this.hasClass('el-invoice')) {  
66 - $receiver.removeClass('hide');  
67 - } else {  
68 - $receiver.addClass('hide');  
69 - }  
70 -  
71 - $this.siblings('.focus').removeClass('focus');  
72 - $this.addClass('focus');  
73 - });  
74 -  
75 $titleWrap.on('click', '.radio-btn', function() { 64 $titleWrap.on('click', '.radio-btn', function() {
76 var $this = $(this), 65 var $this = $(this),
77 id = $this.data('id'); 66 id = $this.data('id');
@@ -90,17 +79,6 @@ function bindInvoiceEvent($el) { @@ -90,17 +79,6 @@ function bindInvoiceEvent($el) {
90 $this.addClass('on'); 79 $this.addClass('on');
91 }); 80 });
92 81
93 - $goodsTypeWrap.on('click', '.radio-btn', function() {  
94 - var $this = $(this);  
95 -  
96 - if ($this.hasClass('on')) {  
97 - return;  
98 - }  
99 -  
100 - $goodsTypeWrap.find('.on').removeClass('on');  
101 - $this.addClass('on');  
102 - });  
103 -  
104 $el.on('click', '.invoice-close', function() { 82 $el.on('click', '.invoice-close', function() {
105 $('.btn-close', $el).trigger('click'); 83 $('.btn-close', $el).trigger('click');
106 }); 84 });
@@ -109,48 +87,35 @@ function bindInvoiceEvent($el) { @@ -109,48 +87,35 @@ function bindInvoiceEvent($el) {
109 function bindInvoiceInfo($el, info) { 87 function bindInvoiceInfo($el, info) {
110 info = info || {}; 88 info = info || {};
111 89
112 - // 发票类型  
113 - if (info.invocesType === 1) {  
114 - $('.pa-invoice', $el).trigger('click');  
115 - }  
116 -  
117 if (info.titleId) { 90 if (info.titleId) {
118 $('.rbt-' + info.titleId).trigger('click'); 91 $('.rbt-' + info.titleId).trigger('click');
119 92
120 - if (info.titleId === 2 && info.titleName) {  
121 - $('#company-name', $el).val(info.titleName); 93 + if (info.titleId === 2) {
  94 + $('#company-name', $el).val(info.titleName || '');
  95 + $('#company-tax-num', $el).val(info.taxNumber || '');
122 } 96 }
123 } 97 }
124 98
125 - if (info.contentId) {  
126 - $('.rbc-' + info.contentId, $el).trigger('click');  
127 - }  
128 -  
129 $('#receiver-phone', $el).val(info.receiver || defaultReceiver.hide || ''); 99 $('#receiver-phone', $el).val(info.receiver || defaultReceiver.hide || '');
130 } 100 }
131 101
132 function packInvoiceInfo($el) { 102 function packInvoiceInfo($el) {
133 - var $goodsType = $('.invoice-goods-type .on', $el);  
134 - var resData = {}, 103 + var resData = { // 5.8.1需求,只支持电子发票(type: 2),发票内容只能开明细(id:12)
  104 + invocesType: 2,
  105 + contentId: 12,
  106 + contentName: '明细'
  107 + },
135 receiver = $('#receiver-phone', $el).val(); 108 receiver = $('#receiver-phone', $el).val();
136 109
137 - if ($('.pa-invoice', $el).hasClass('focus')) {  
138 - resData.invocesType = 1;  
139 - } else {  
140 - resData.invocesType = 2;  
141 - }  
142 -  
143 resData.titleId = $('.invoice-title .on', $el).data('id') || 1; 110 resData.titleId = $('.invoice-title .on', $el).data('id') || 1;
144 111
145 if (resData.titleId * 1 === 1) { 112 if (resData.titleId * 1 === 1) {
146 resData.titleName = '个人'; 113 resData.titleName = '个人';
147 } else { 114 } else {
148 resData.titleName = $('#company-name', $el).val(); 115 resData.titleName = $('#company-name', $el).val();
  116 + resData.taxNumber = $('#company-tax-num', $el).val();
149 } 117 }
150 118
151 - resData.contentId = $goodsType.data('id');  
152 - resData.contentName = $goodsType.data('name');  
153 -  
154 if (receiver) { 119 if (receiver) {
155 resData.receiver = receiver; 120 resData.receiver = receiver;
156 } 121 }
@@ -173,8 +138,7 @@ function setShowInvoiceInfo() { @@ -173,8 +138,7 @@ function setShowInvoiceInfo() {
173 _h += '电子发票'; 138 _h += '电子发票';
174 } 139 }
175 140
176 - _h += '&nbsp;&nbsp;&nbsp;&nbsp;' + invoiceInfo.titleName +  
177 - '&nbsp;&nbsp;&nbsp;&nbsp;' + invoiceInfo.contentName; 141 + _h += '&nbsp;&nbsp;&nbsp;&nbsp;' + invoiceInfo.titleName;
178 142
179 $dom.removeClass('hide').find('span').html(_h); 143 $dom.removeClass('hide').find('span').html(_h);
180 } 144 }
@@ -186,7 +150,7 @@ function invoiceEditDialog(baseInfo) { @@ -186,7 +150,7 @@ function invoiceEditDialog(baseInfo) {
186 btns: [ 150 btns: [
187 { 151 {
188 id: 'save-invoice', 152 id: 'save-invoice',
189 - name: '保存发票信息', 153 + name: '提交',
190 btnClass: ['save-invoice'], 154 btnClass: ['save-invoice'],
191 cb: function() { 155 cb: function() {
192 var info = packInvoiceInfo(invoice.$el); 156 var info = packInvoiceInfo(invoice.$el);
@@ -258,6 +222,7 @@ exports.getInvoice = function() { @@ -258,6 +222,7 @@ exports.getInvoice = function() {
258 invoicesType: invoiceInfo.invocesType, 222 invoicesType: invoiceInfo.invocesType,
259 invoicesTitle: invoiceInfo.titleName, 223 invoicesTitle: invoiceInfo.titleName,
260 invoicesContent: invoiceInfo.contentId, 224 invoicesContent: invoiceInfo.contentId,
  225 + taxNumber: invoiceInfo.taxNumber || '',
261 receiver: invoiceInfo.receiver === defaultReceiver.hide ? defaultReceiver.full : invoiceInfo.receiver 226 receiver: invoiceInfo.receiver === defaultReceiver.hide ? defaultReceiver.full : invoiceInfo.receiver
262 }; 227 };
263 }; 228 };
@@ -237,6 +237,7 @@ function _loadPage() { @@ -237,6 +237,7 @@ function _loadPage() {
237 237
238 // 接入人工客服需要评价 238 // 接入人工客服需要评价
239 processInfo.manual = true; 239 processInfo.manual = true;
  240 + processInfo.savedEval = false;
240 241
241 // 显示评价&隐藏人工 242 // 显示评价&隐藏人工
242 edit.setIcons({ 243 edit.setIcons({
@@ -607,6 +608,12 @@ function _loadPage() { @@ -607,6 +608,12 @@ function _loadPage() {
607 608
608 serviceApi.history(data) 609 serviceApi.history(data)
609 .done(function(res) { 610 .done(function(res) {
  611 + if (res && res.code === 401) {
  612 + window.onbeforeunload = null;
  613 + return location.href = '//www.yohobuy.com/signin.html?refer=' + // eslint-disable-line
  614 + encodeURIComponent(location.href);
  615 + }
  616 +
610 if (res && res.code === 200) { 617 if (res && res.code === 200) {
611 if (processInfo.hasMore) { 618 if (processInfo.hasMore) {
612 msgList = res.data.records || []; 619 msgList = res.data.records || [];
@@ -690,7 +697,13 @@ function _loadPage() { @@ -690,7 +697,13 @@ function _loadPage() {
690 encryptedUid: encryptedUid, 697 encryptedUid: encryptedUid,
691 conversationId: socketConfCM.conversationId 698 conversationId: socketConfCM.conversationId
692 }) 699 })
693 - .done(function() {}) 700 + .done(function(res) {
  701 + if (res && res.code === 401) {
  702 + window.onbeforeunload = null;
  703 + return location.href = '//www.yohobuy.com/signin.html?refer=' + // eslint-disable-line
  704 + encodeURIComponent(location.href);
  705 + }
  706 + })
694 .always(function() { 707 .always(function() {
695 lMsg.modal('hide'); 708 lMsg.modal('hide');
696 }); 709 });
@@ -36,6 +36,12 @@ var _fetchReason = (function() { @@ -36,6 +36,12 @@ var _fetchReason = (function() {
36 type: YOHO_CS 36 type: YOHO_CS
37 }) 37 })
38 .done(function(res) { 38 .done(function(res) {
  39 + if (res && res.code === 401) {
  40 + window.onbeforeunload = null;
  41 + return location.href = '//www.yohobuy.com/signin.html?refer=' + // eslint-disable-line
  42 + encodeURIComponent(location.href);
  43 + }
  44 +
39 if (res.code === 200) { 45 if (res.code === 200) {
40 cache = res.data; 46 cache = res.data;
41 render(cache); 47 render(cache);
@@ -131,6 +137,12 @@ function _evalSubmit() { @@ -131,6 +137,12 @@ function _evalSubmit() {
131 $btnEval.hide(); 137 $btnEval.hide();
132 processSign.savedEval = true; 138 processSign.savedEval = true;
133 139
  140 + if (res && res.code === 401) {
  141 + window.onbeforeunload = null;
  142 + return location.href = '//www.yohobuy.com/signin.html?refer=' + // eslint-disable-line
  143 + encodeURIComponent(location.href);
  144 + }
  145 +
134 if (res && res.code === 200) { 146 if (res && res.code === 200) {
135 send.completeEval(); 147 send.completeEval();
136 self.close(); 148 self.close();
@@ -771,6 +771,15 @@ @@ -771,6 +771,15 @@
771 padding-right: 20px; 771 padding-right: 20px;
772 font-size: 14px; 772 font-size: 14px;
773 773
  774 + > span {
  775 + display: inline-block;
  776 + max-width: 300px;
  777 + overflow: hidden;
  778 + text-overflow: ellipsis;
  779 + white-space: nowrap;
  780 + vertical-align: middle;
  781 + }
  782 +
774 > label { 783 > label {
775 width: 80px; 784 width: 80px;
776 height: 24px; 785 height: 24px;
@@ -1593,10 +1602,12 @@ @@ -1593,10 +1602,12 @@
1593 .ensure-invoice-dialog { 1602 .ensure-invoice-dialog {
1594 $red: #d0021b; 1603 $red: #d0021b;
1595 1604
1596 - width: 440px; 1605 + width: 690px;
  1606 + padding: 20px 58px;
1597 font-size: 14px; 1607 font-size: 14px;
1598 color: #444; 1608 color: #444;
1599 background-color: #fff; 1609 background-color: #fff;
  1610 + box-sizing: border-box;
1600 1611
1601 > .close { 1612 > .close {
1602 display: none; 1613 display: none;
@@ -1639,31 +1650,17 @@ @@ -1639,31 +1650,17 @@
1639 } 1650 }
1640 1651
1641 .invoice-header { 1652 .invoice-header {
1642 - font-size: 18px;  
1643 - padding-bottom: 10px; 1653 + font-size: 14px;
  1654 + padding: 24px 0 20px;
1644 border-bottom: 1px solid #e8e8e8; 1655 border-bottom: 1px solid #e8e8e8;
1645 } 1656 }
1646 1657
1647 - .invoice-type {  
1648 - padding: 20px 0;  
1649 -  
1650 - > li {  
1651 - width: 98px;  
1652 - height: 28px;  
1653 - line-height: 28px;  
1654 - text-align: center;  
1655 - font-size: 14px;  
1656 - margin-right: 17px;  
1657 - border: 1px solid #505050;  
1658 - color: #505050;  
1659 - display: inline-block;  
1660 - cursor: pointer; 1658 + .invoice-content {
  1659 + font-weight: 300;
1661 } 1660 }
1662 1661
1663 - .focus {  
1664 - border-color: #d0021b;  
1665 - color: #d0021b;  
1666 - } 1662 + .row-title {
  1663 + font-weight: normal;
1667 } 1664 }
1668 1665
1669 .el-tip { 1666 .el-tip {
@@ -1672,15 +1669,24 @@ @@ -1672,15 +1669,24 @@
1672 line-height: 1.5; 1669 line-height: 1.5;
1673 1670
1674 > a { 1671 > a {
1675 - color: $red;  
1676 display: inline-block; 1672 display: inline-block;
1677 padding-top: 12px; 1673 padding-top: 12px;
  1674 + font-weight: 500;
  1675 + }
  1676 + }
  1677 +
  1678 + .invoice-type-text {
  1679 + line-height: 20px;
  1680 + padding: 20px 10px;
  1681 +
  1682 + .row-title {
  1683 + margin-right: 10px;
1678 } 1684 }
1679 } 1685 }
1680 1686
1681 .invoice-row { 1687 .invoice-row {
1682 padding-top: 16px; 1688 padding-top: 16px;
1683 - padding-left: 84px; 1689 + padding-left: 92px;
1684 overflow: hidden; 1690 overflow: hidden;
1685 1691
1686 .row-content { 1692 .row-content {
@@ -1690,7 +1696,7 @@ @@ -1690,7 +1696,7 @@
1690 .row-title { 1696 .row-title {
1691 line-height: 30px; 1697 line-height: 30px;
1692 position: absolute; 1698 position: absolute;
1693 - margin-left: -84px; 1699 + margin-left: -92px;
1694 1700
1695 > em { 1701 > em {
1696 color: $red; 1702 color: $red;
@@ -1707,9 +1713,11 @@ @@ -1707,9 +1713,11 @@
1707 } 1713 }
1708 1714
1709 input { 1715 input {
1710 - width: 194px;  
1711 - height: 26px; 1716 + width: 220px;
  1717 + height: 30px;
1712 padding: 0 10px; 1718 padding: 0 10px;
  1719 + background: #f5f5f5;
  1720 + border: 1px solid #e0e0e0;
1713 } 1721 }
1714 1722
1715 .red { 1723 .red {
@@ -1722,15 +1730,17 @@ @@ -1722,15 +1730,17 @@
1722 padding-bottom: 10px; 1730 padding-bottom: 10px;
1723 1731
1724 .btn { 1732 .btn {
1725 - width: 130px;  
1726 - height: 28px;  
1727 - line-height: 28px; 1733 + width: 140px;
  1734 + height: 40px;
  1735 + line-height: 38px;
  1736 + box-sizing: border-box;
1728 } 1737 }
1729 1738
1730 .save-invoice { 1739 .save-invoice {
1731 background-color: #000; 1740 background-color: #000;
1732 color: #fff; 1741 color: #fff;
1733 font-weight: 300; 1742 font-weight: 300;
  1743 + margin-right: 30px;
1734 } 1744 }
1735 } 1745 }
1736 } 1746 }
1 User-Agent: * 1 User-Agent: *
2 -Disallow: /*?*  
3 Disallow: /passport/ 2 Disallow: /passport/
4 Disallow: /help 3 Disallow: /help
5 Disallow: signin.html 4 Disallow: signin.html