Authored by htoooth

Merge branch 'master' into feature/channel-op

@@ -17,7 +17,7 @@ const getArea = (req, res, next) => { @@ -17,7 +17,7 @@ const getArea = (req, res, next) => {
17 }); 17 });
18 } 18 }
19 19
20 - address.getAreaListData(id).then(result => { 20 + req.ctx(address).getAreaListData(id).then(result => {
21 res.send(result); 21 res.send(result);
22 }).catch(next); 22 }).catch(next);
23 }; 23 };
@@ -32,7 +32,7 @@ const getList = (req, res, next) => { @@ -32,7 +32,7 @@ const getList = (req, res, next) => {
32 }); 32 });
33 } 33 }
34 34
35 - address.getAddressListData(uid).then(result => { 35 + req.ctx(address).getAddressListData(uid).then(result => {
36 res.send(result); 36 res.send(result);
37 }).catch(next); 37 }).catch(next);
38 }; 38 };
@@ -45,7 +45,7 @@ const delAddress = (req, res, next) => { @@ -45,7 +45,7 @@ const delAddress = (req, res, next) => {
45 return next(); 45 return next();
46 } 46 }
47 47
48 - address.delAddressById(uid, id).then(result => { 48 + req.ctx(address).delAddressById(uid, id).then(result => {
49 res.send(result); 49 res.send(result);
50 }).catch(next); 50 }).catch(next);
51 }; 51 };
@@ -58,7 +58,7 @@ const saveAddress = (req, res, next) => { @@ -58,7 +58,7 @@ const saveAddress = (req, res, next) => {
58 return next(); 58 return next();
59 } 59 }
60 60
61 - address.saveAddressData(uid, params).then(result => { 61 + req.ctx(address).saveAddressData(uid, params).then(result => {
62 res.send(result); 62 res.send(result);
63 }).catch(next); 63 }).catch(next);
64 }; 64 };
@@ -71,7 +71,7 @@ const setDefault = (req, res, next) => { @@ -71,7 +71,7 @@ const setDefault = (req, res, next) => {
71 return next(); 71 return next();
72 } 72 }
73 73
74 - address.setDefaultAddress(uid, id).then(result => { 74 + req.ctx(address).setDefaultAddress(uid, id).then(result => {
75 res.send(result); 75 res.send(result);
76 }).catch(next); 76 }).catch(next);
77 }; 77 };
@@ -17,7 +17,7 @@ const index = (req, res, next) => { @@ -17,7 +17,7 @@ const index = (req, res, next) => {
17 let uid = req.user.uid; 17 let uid = req.user.uid;
18 let params = req.query; 18 let params = req.query;
19 19
20 - easypay.getEasypayOrderData(params, uid).then(result => { 20 + req.ctx(easypay).getEasypayOrderData(params, uid).then(result => {
21 let header = headerModel.setSimpleHeaderData() || {}; 21 let header = headerModel.setSimpleHeaderData() || {};
22 22
23 result.stepper = stepper; 23 result.stepper = stepper;
@@ -39,7 +39,7 @@ const compute = (req, res, next) => { @@ -39,7 +39,7 @@ const compute = (req, res, next) => {
39 let uid = req.user.uid; 39 let uid = req.user.uid;
40 let params = req.body; 40 let params = req.body;
41 41
42 - easypay.getOrderComputeData(uid, 'ordinary', params).then(result => { 42 + req.ctx(easypay).getOrderComputeData(uid, 'ordinary', params).then(result => {
43 res.json(result); 43 res.json(result);
44 }).catch(next); 44 }).catch(next);
45 }; 45 };
@@ -53,7 +53,7 @@ const submit = (req, res, next) => { @@ -53,7 +53,7 @@ const submit = (req, res, next) => {
53 return next(); 53 return next();
54 } 54 }
55 55
56 - easypay.easypayOrderSubmit(uid, 'ordinary', params, remoteIp).then(result => { 56 + req.ctx(easypay).easypayOrderSubmit(uid, 'ordinary', params, remoteIp).then(result => {
57 res.json(result); 57 res.json(result);
58 }).catch(next); 58 }).catch(next);
59 }; 59 };
@@ -23,7 +23,7 @@ const stepper = [ @@ -23,7 +23,7 @@ const stepper = [
23 const index = (req, res, next) => { 23 const index = (req, res, next) => {
24 let cartType = req.query.type === '2' ? 'advance' : 'ordinary'; 24 let cartType = req.query.type === '2' ? 'advance' : 'ordinary';
25 25
26 - oeModel.index(req.user.uid, cartType).then(result => { 26 + req.ctx(oeModel).index(req.user.uid, cartType).then(result => {
27 let header = headerModel.setSimpleHeaderData() || {}; 27 let header = headerModel.setSimpleHeaderData() || {};
28 28
29 Object.assign(result, { 29 Object.assign(result, {
@@ -46,14 +46,14 @@ const index = (req, res, next) => { @@ -46,14 +46,14 @@ const index = (req, res, next) => {
46 46
47 // 获取优惠券列表 47 // 获取优惠券列表
48 const getCoupons = (req, res, next) => { 48 const getCoupons = (req, res, next) => {
49 - oeModel.getCoupons(req.user.uid).then(data => { 49 + req.ctx(oeModel).getCoupons(req.user.uid).then(data => {
50 res.send(data); 50 res.send(data);
51 }).catch(next); 51 }).catch(next);
52 }; 52 };
53 53
54 // 获取优惠券列表 54 // 获取优惠券列表
55 const convertCoupons = (req, res, next) => { 55 const convertCoupons = (req, res, next) => {
56 - oeModel.convertCoupons(req.user.uid, req.query.code).then(data => { 56 + req.ctx(oeModel).convertCoupons(req.user.uid, req.query.code).then(data => {
57 res.send(data); 57 res.send(data);
58 }).catch(next); 58 }).catch(next);
59 }; 59 };
@@ -64,11 +64,11 @@ const compute = (req, res, next) => { @@ -64,11 +64,11 @@ const compute = (req, res, next) => {
64 let cartType = params.cartType === '2' ? 'advance' : 'ordinary'; 64 let cartType = params.cartType === '2' ? 'advance' : 'ordinary';
65 65
66 if (params.sku) { // 快捷结算 66 if (params.sku) { // 快捷结算
67 - easypayModel.getOrderComputeData(req.user.uid, 'ordinary', params).then(result => { 67 + req.ctx(easypayModel).getOrderComputeData(req.user.uid, 'ordinary', params).then(result => {
68 res.json(result); 68 res.json(result);
69 }).catch(next); 69 }).catch(next);
70 } else { 70 } else {
71 - oeModel.compute(req.user.uid, cartType, params).then(data => { 71 + req.ctx(oeModel).compute(req.user.uid, cartType, params).then(data => {
72 res.send(data); 72 res.send(data);
73 }).catch(next); 73 }).catch(next);
74 } 74 }
@@ -153,11 +153,11 @@ const submit = (req, res, next) => { @@ -153,11 +153,11 @@ const submit = (req, res, next) => {
153 params.udid = req.cookies._yasvd || 'yoho_pc'; 153 params.udid = req.cookies._yasvd || 'yoho_pc';
154 154
155 if (params.sku) { // 快捷结算 155 if (params.sku) { // 快捷结算
156 - easypayModel.easypayOrderSubmit(uid, 'ordinary', params, remoteIp).then(result => { 156 + req.ctx(easypayModel).easypayOrderSubmit(uid, 'ordinary', params, remoteIp).then(result => {
157 res.json(result); 157 res.json(result);
158 }).catch(next); 158 }).catch(next);
159 } else { 159 } else {
160 - oeModel.submit(uid, cartType, params, remoteIp).then(data => { 160 + req.ctx(oeModel).submit(uid, cartType, params, remoteIp).then(data => {
161 if (data && data.code === 200 && unionKey) { 161 if (data && data.code === 200 && unionKey) {
162 data.data.unionKey = { 162 data.data.unionKey = {
163 client_id: clientId 163 client_id: clientId
@@ -21,7 +21,7 @@ const ticketEnsure = (req, res, next) => { @@ -21,7 +21,7 @@ const ticketEnsure = (req, res, next) => {
21 let buyNumber = req.query.buyNumber || 0; 21 let buyNumber = req.query.buyNumber || 0;
22 let skn = req.query.productSkn || 0; 22 let skn = req.query.productSkn || 0;
23 23
24 - ticketService.addTicket(uid, sku, buyNumber).then(result => { 24 + req.ctx(ticketService).addTicket(uid, sku, buyNumber).then(result => {
25 let header = headerModel.setSimpleHeaderData() || {}; 25 let header = headerModel.setSimpleHeaderData() || {};
26 26
27 result.stepper = stepper; 27 result.stepper = stepper;
@@ -51,7 +51,7 @@ const ticketSubmit = (req, res, next) => { @@ -51,7 +51,7 @@ const ticketSubmit = (req, res, next) => {
51 }); 51 });
52 } 52 }
53 53
54 - ticketService.submitTicket(uid, sku, count, mobile, yohoCoin).then(result => { 54 + req.ctx(ticketService).submitTicket(uid, sku, count, mobile, yohoCoin).then(result => {
55 return res.json(result); 55 return res.json(result);
56 }).catch(next); 56 }).catch(next);
57 }; 57 };
@@ -62,7 +62,7 @@ const ticketCompute = (req, res, next) => { @@ -62,7 +62,7 @@ const ticketCompute = (req, res, next) => {
62 let buyNumber = req.body.count || 0; 62 let buyNumber = req.body.count || 0;
63 let yohoCoin = req.body.coin || 0; 63 let yohoCoin = req.body.coin || 0;
64 64
65 - ticketService.addTicket(uid, sku, buyNumber, yohoCoin).then(result => { 65 + req.ctx(ticketService).addTicket(uid, sku, buyNumber, yohoCoin).then(result => {
66 if (_.isEmpty(result)) { 66 if (_.isEmpty(result)) {
67 return res.json({ 67 return res.json({
68 code: 401, 68 code: 401,
@@ -5,104 +5,122 @@ @@ -5,104 +5,122 @@
5 */ 5 */
6 'use strict'; 6 'use strict';
7 7
8 -const api = global.yoho.API; 8 +module.exports = class extends global.yoho.BaseModel {
  9 + constructor(ctx) {
  10 + super(ctx);
  11 + }
9 12
10 -/**  
11 - * 获取用户收货地址列表  
12 - * @param uid [number] uid  
13 - */  
14 -const getAddressListAsync = (uid) => api.get('', {  
15 - method: 'app.address.gethidden',  
16 - uid: uid  
17 -}, {code: 200}); 13 + /**
  14 + * 获取用户收货地址列表
  15 + * @param uid [number] uid
  16 + */
  17 + getAddressListAsync(uid) {
  18 + let options = {
  19 + method: 'app.address.gethidden',
  20 + uid: uid
  21 + };
18 22
19 -/**  
20 - * 省市区列表  
21 - * @param id [number] 省市区id  
22 - */  
23 -const getAreaListAsync = (id) => {  
24 - return api.get('', {  
25 - method: 'app.address.provinces',  
26 - id: id || 0  
27 - });  
28 -}; 23 + return this.get({data: options, param: {
  24 + code: 200
  25 + }});
  26 + }
29 27
30 -/**  
31 - * 地址删除  
32 - * @param uid [number] uid  
33 - * @param id [string] address id  
34 - */  
35 -const delAddressAsync = (uid, id) => api.get('', {  
36 - method: 'app.address.del',  
37 - uid: uid,  
38 - id: id  
39 -}); 28 + /**
  29 + * 省市区列表
  30 + * @param id [number] 省市区id
  31 + */
  32 + getAreaListAsync(id) {
  33 + let options = {
  34 + method: 'app.address.provinces',
  35 + id: id || 0
  36 + };
40 37
41 -/**  
42 - * 新增地址api  
43 - * @param uid [Number]  
44 - * @param consignee [String] 收货人  
45 - * @param areaCode [Number] 区号  
46 - * @param address [String] 地址  
47 - * @param mobile [String] 手机号  
48 - * @param phone [String] 电话号码  
49 - * @param zipCode [String] 邮编  
50 - * @param email [String] 邮箱  
51 - */  
52 -const addAddressAsync = (uid, consignee, areaCode, address, mobile, phone, zipCode, email) => api.get('', {  
53 - method: 'app.address.add',  
54 - uid: uid,  
55 - consignee: consignee,  
56 - area_code: areaCode,  
57 - address: address,  
58 - mobile: mobile,  
59 - phone: phone,  
60 - zip_code: zipCode,  
61 - email: email  
62 -}); 38 + return this.get({data: options});
  39 + }
63 40
64 -/**  
65 - * 更新地址地址api  
66 - * @param uid [Number]  
67 - * @param id [Number] 地址id  
68 - * @param consignee [String] 收货人  
69 - * @param areaCode [Number] 区号  
70 - * @param address [String] 地址  
71 - * @param mobile [String] 手机号  
72 - * @param phone [String] 电话号码  
73 - * @param zipCode [String] 邮编  
74 - * @param email [String] 邮箱  
75 - */  
76 -const updateAddressAsync = (uid, id, consignee, areaCode, address, mobile, phone, zipCode, email) => api.get('', {  
77 - method: 'app.address.update',  
78 - uid: uid,  
79 - id: id,  
80 - consignee: consignee,  
81 - area_code: areaCode,  
82 - address: address,  
83 - mobile: mobile,  
84 - phone: phone,  
85 - zip_code: zipCode,  
86 - email: email  
87 -}); 41 + /**
  42 + * 地址删除
  43 + * @param uid [number] uid
  44 + * @param id [string] address id
  45 + */
  46 + delAddressAsync(uid, id) {
  47 + let options = {
  48 + method: 'app.address.del',
  49 + uid: uid,
  50 + id: id
  51 + };
88 52
89 -/**  
90 - * 设置默认地址  
91 - * @param uid [Number]  
92 - * @param id [Number] 地址id  
93 - */  
94 -const setDefaultAddressAsync = (uid, id) => api.get('', {  
95 - method: 'app.address.setdefault',  
96 - uid: uid,  
97 - id: id  
98 -}); 53 + return this.get({data: options});
  54 + }
  55 +
  56 + /**
  57 + * 新增地址api
  58 + * @param uid [Number]
  59 + * @param consignee [String] 收货人
  60 + * @param areaCode [Number] 区号
  61 + * @param address [String] 地址
  62 + * @param mobile [String] 手机号
  63 + * @param phone [String] 电话号码
  64 + * @param zipCode [String] 邮编
  65 + * @param email [String] 邮箱
  66 + */
  67 + addAddressAsync(uid, consignee, areaCode, address, mobile, phone, zipCode, email) {
  68 + let options = {
  69 + method: 'app.address.add',
  70 + uid: uid,
  71 + consignee: consignee,
  72 + area_code: areaCode,
  73 + address: address,
  74 + mobile: mobile,
  75 + phone: phone,
  76 + zip_code: zipCode,
  77 + email: email
  78 + };
  79 +
  80 + return this.get({data: options});
  81 + }
  82 +
  83 + /**
  84 + * 更新地址地址api
  85 + * @param uid [Number]
  86 + * @param id [Number] 地址id
  87 + * @param consignee [String] 收货人
  88 + * @param areaCode [Number] 区号
  89 + * @param address [String] 地址
  90 + * @param mobile [String] 手机号
  91 + * @param phone [String] 电话号码
  92 + * @param zipCode [String] 邮编
  93 + * @param email [String] 邮箱
  94 + */
  95 + updateAddressAsync(uid, id, consignee, areaCode, address, mobile, phone, zipCode, email) {
  96 + let options = {
  97 + method: 'app.address.update',
  98 + uid: uid,
  99 + id: id,
  100 + consignee: consignee,
  101 + area_code: areaCode,
  102 + address: address,
  103 + mobile: mobile,
  104 + phone: phone,
  105 + zip_code: zipCode,
  106 + email: email
  107 + };
  108 +
  109 + return this.get({data: options});
  110 + }
99 111
  112 + /**
  113 + * 设置默认地址
  114 + * @param uid [Number]
  115 + * @param id [Number] 地址id
  116 + */
  117 + setDefaultAddressAsync(uid, id) {
  118 + let options = {
  119 + method: 'app.address.setdefault',
  120 + uid: uid,
  121 + id: id
  122 + };
100 123
101 -module.exports = {  
102 - getAddressListAsync,  
103 - getAreaListAsync,  
104 - delAddressAsync,  
105 - addAddressAsync,  
106 - updateAddressAsync,  
107 - setDefaultAddressAsync 124 + return this.get({data: options});
  125 + }
108 }; 126 };
@@ -10,89 +10,88 @@ const _ = require('lodash'); @@ -10,89 +10,88 @@ const _ = require('lodash');
10 const crypto = global.yoho.crypto; 10 const crypto = global.yoho.crypto;
11 11
12 const pinyin = require('../models/province-pinyin'); 12 const pinyin = require('../models/province-pinyin');
13 -const addressApi = require('../models/address-api'); 13 +const AddressApi = require('../models/address-api');
14 14
15 const worldSort = 'abcdefghijklmnopqrstuvwxyz'; 15 const worldSort = 'abcdefghijklmnopqrstuvwxyz';
16 16
17 -const getAreaListData = (id) => {  
18 - return addressApi.getAreaListAsync(id).then(result => {  
19 - let list = _.get(result, 'data', []); 17 +module.exports = class extends global.yoho.BaseModel {
  18 + constructor(ctx) {
  19 + super(ctx);
  20 + }
20 21
21 - if (id * 1 === 0 && list.length) {  
22 - _.forEach(list, value => {  
23 - value.initial = pinyin[value.caption] || 'z';  
24 - value.pySort = _.indexOf(worldSort, value.initial);  
25 - });  
26 - result.data = list;  
27 - }  
28 - return result;  
29 - });  
30 -}; 22 + getAreaListData(id) {
  23 + return new AddressApi(this.ctx).getAreaListAsync(id).then(result => {
  24 + let list = _.get(result, 'data', []);
31 25
32 -const getAddressListData = (uid) => {  
33 - return addressApi.getAddressListAsync(uid).then(result => {  
34 - if (result.code !== 200) { 26 + if (id * 1 === 0 && list.length) {
  27 + _.forEach(list, value => {
  28 + value.initial = pinyin[value.caption] || 'z';
  29 + value.pySort = _.indexOf(worldSort, value.initial);
  30 + });
  31 + result.data = list;
  32 + }
35 return result; 33 return result;
36 - }  
37 -  
38 - let d = result.data; 34 + });
  35 + }
39 36
40 - _.forEach(d, dd => {  
41 - if (dd.is_default === 'Y') {  
42 - dd.default = true; 37 + getAddressListData(uid) {
  38 + return new AddressApi(this.ctx).getAddressListAsync(uid).then(result => {
  39 + if (result.code !== 200) {
  40 + return result;
43 } 41 }
44 42
45 - // 地址加密  
46 - let id = dd.address_id; 43 + let d = result.data;
  44 +
  45 + _.forEach(d, dd => {
  46 + if (dd.is_default === 'Y') {
  47 + dd.default = true;
  48 + }
  49 +
  50 + // 地址加密
  51 + let id = dd.address_id;
  52 +
  53 + dd.id = crypto.encryption('', `${id}`);
  54 + delete dd.address_id;
  55 + delete dd.uid; // 删除uid,用户数据保密
  56 + });
47 57
48 - dd.id = crypto.encryption('', `${id}`);  
49 - delete dd.address_id;  
50 - delete dd.uid; // 删除uid,用户数据保密 58 + return result;
51 }); 59 });
  60 + }
52 61
53 - return result;  
54 - });  
55 -}; 62 + delAddressById(uid, id) {
  63 + id = crypto.decrypt('', `${id}`);
  64 + return new AddressApi(this.ctx).delAddressAsync(uid, id);
  65 + }
56 66
57 -const delAddressById = (uid, id) => {  
58 - id = crypto.decrypt('', `${id}`); 67 + saveAddressData(uid, info) {
  68 + let addressApiModel = new AddressApi(this.ctx);
59 69
60 - return addressApi.delAddressAsync(uid, id);  
61 -}; 70 + if (info.id) {
  71 + let id = crypto.decrypt('', `${info.id}`);
62 72
63 -const saveAddressData = (uid, info) => {  
64 - if (info.id) {  
65 - let id = crypto.decrypt('', `${info.id}`);  
66 -  
67 - return addressApi.updateAddressAsync(uid, id, info.consignee, info.areaCode, info.address,  
68 - info.mobile, info.phone, info.zipCode, info.email);  
69 - } else {  
70 - return addressApi.addAddressAsync(uid, info.consignee, info.areaCode, info.address, info.mobile,  
71 - info.phone, info.zipCode, info.email).then(result => {  
72 - if (result.code === 200) {  
73 - let d = result.data;  
74 -  
75 - d.id = crypto.encryption('', `${d.address_id}`);  
76 - delete d.address_id;  
77 - delete d.uid;  
78 - } 73 + return addressApiModel.updateAddressAsync(uid, id, info.consignee, info.areaCode, info.address,
  74 + info.mobile, info.phone, info.zipCode, info.email);
  75 + } else {
  76 + return addressApiModel.addAddressAsync(uid, info.consignee, info.areaCode, info.address, info.mobile,
  77 + info.phone, info.zipCode, info.email).then(result => {
  78 + if (result.code === 200) {
  79 + let d = result.data;
79 80
80 - return result;  
81 - });  
82 - } 81 + d.id = crypto.encryption('', `${d.address_id}`);
  82 + delete d.address_id;
  83 + delete d.uid;
  84 + }
83 85
84 -}; 86 + return result;
  87 + });
  88 + }
85 89
86 -const setDefaultAddress = (uid, id) => {  
87 - id = crypto.decrypt('', `${id}`); 90 + }
88 91
89 - return addressApi.setDefaultAddressAsync(uid, id);  
90 -}; 92 + setDefaultAddress(uid, id) {
  93 + id = crypto.decrypt('', `${id}`);
91 94
92 -module.exports = {  
93 - getAreaListData,  
94 - getAddressListData,  
95 - delAddressById,  
96 - saveAddressData,  
97 - setDefaultAddress 95 + return new AddressApi(this.ctx).setDefaultAddressAsync(uid, id);
  96 + }
98 }; 97 };
@@ -5,71 +5,158 @@ @@ -5,71 +5,158 @@
5 */ 5 */
6 'use strict'; 6 'use strict';
7 7
8 -const api = global.yoho.API;  
9 -  
10 -const getEasyPaymentAsync = (uid, goods, activity) => {  
11 - let param = {  
12 - method: 'app.Shopping.easyPayment',  
13 - uid: uid,  
14 - cart_type: 'ordinary',  
15 - product_sku_list: goods,  
16 - yoho_coin_mode: 0,  
17 - is_support_apple_pay: 'N'  
18 - };  
19 -  
20 - if (activity) {  
21 - param.activity_id = activity; 8 +module.exports = class extends global.yoho.BaseModel {
  9 + constructor(ctx) {
  10 + super(ctx);
22 } 11 }
23 12
24 - return api.get('', param, {code: 200});  
25 -}; 13 + getEasyPaymentAsync(uid, goods, activity) {
  14 + let param = {
  15 + method: 'app.Shopping.easyPayment',
  16 + uid: uid,
  17 + cart_type: 'ordinary',
  18 + product_sku_list: goods,
  19 + yoho_coin_mode: 0,
  20 + is_support_apple_pay: 'N'
  21 + };
  22 +
  23 + if (activity) {
  24 + param.activity_id = activity;
  25 + }
26 26
  27 + return this.get({data: param});
  28 + }
27 29
28 -/**  
29 - * 订单计算API  
30 - * @param uid [number] uid  
31 - * @param cartType [string] 购物车类型,ordinary表示普通, advance表示预售  
32 - * @param paymentType [number] 支付方式,1表示在线支付,2表示货到付款  
33 - * @param deliveryWay [number] 配送方式,1表示普通快递,2表示顺丰速运  
34 - * @param coin [number] 使用的有货币  
35 - * @param useRedEnvelops [number] 红包  
36 - * @param couponCode [string] 优惠券码  
37 - * @param promotionCode [string] 优惠码  
38 - * @param productSkuList [string] 限购商品 30 + /**
  31 + * 订单计算API
  32 + * @param uid [number] uid
  33 + * @param cartType [string] 购物车类型,ordinary表示普通, advance表示预售
  34 + * @param paymentType [number] 支付方式,1表示在线支付,2表示货到付款
  35 + * @param deliveryWay [number] 配送方式,1表示普通快递,2表示顺丰速运
  36 + * @param coin [number] 使用的有货币
  37 + * @param useRedEnvelops [number] 红包
  38 + * @param couponCode [string] 优惠券码
  39 + * @param promotionCode [string] 优惠码
  40 + * @param productSkuList [string] 限购商品
  41 + */
  42 + getEasypayComputeAsync(uid, cartType, paymentType, deliveryWay, other) {
  43 + let param = {
  44 + method: 'app.Shopping.easyCompute',
  45 + uid: uid,
  46 + cart_type: cartType || 'ordinary',
  47 + payment_type: paymentType,
  48 + delivery_way: deliveryWay
  49 + };
  50 +
  51 + // 其他可选参数
  52 + if (other) {
  53 + if (other.coin) {
  54 + Object.assign(param, {
  55 + use_yoho_coin: other.coin / 100
  56 + });
  57 + }
39 58
40 - */  
41 -const getEasypayComputeAsync = (uid, cartType, paymentType, deliveryWay, other) => {  
42 - let param = {  
43 - method: 'app.Shopping.easyCompute',  
44 - uid: uid,  
45 - cart_type: cartType || 'ordinary',  
46 - payment_type: paymentType,  
47 - delivery_way: deliveryWay  
48 - };  
49 -  
50 - // 其他可选参数  
51 - if (other) { 59 + if (other.useRedEnvelops) {
  60 + Object.assign(param, {
  61 + use_red_envelopes: other.useRedEnvelops
  62 + });
  63 + }
  64 +
  65 + if (other.couponCode) {
  66 + Object.assign(param, {
  67 + coupon_code: other.couponCode
  68 + });
  69 + }
  70 +
  71 + if (other.promotionCode) {
  72 + Object.assign(param, {
  73 + promotion_code: other.promotionCode
  74 + });
  75 + }
  76 +
  77 + if (other.productSkuList) {
  78 + Object.assign(param, {
  79 + product_sku_list: other.productSkuList
  80 + });
  81 +
  82 + if (other.bundle) {
  83 + Object.assign(param, {
  84 + activity_id: other.bundle
  85 + });
  86 + }
  87 + }
  88 + }
  89 +
  90 + return this.get({data: param});
  91 + }
  92 +
  93 + /**
  94 + * 订单提交API
  95 + * @param uid [number] uid
  96 + * @param cartType [String] 购物车类型,ordinary表示普通, advance表示预售
  97 + * @param addressId [String] 地址
  98 + * @param deliveryTime [number] 寄送时间类型
  99 + * @param deliveryWay [number] 配送方式,1表示普通快递,2表示顺丰速运
  100 + * @param invoicesType [number] 发票类型
  101 + * @param invoicesTitle [string] 发票抬头
  102 + * @param invoicesContent [int] 发票类型
  103 + * @param receiverMobile [string] 发票收人电话
  104 + * @param coin [number] 使用的有货币金额
  105 + * @param redEnvelopes [number] 使用的红包
  106 + * @param paymentId [number] 支付id
  107 + * @param paymentType [number] 支付类型
  108 + * @param remark [string] 备注
  109 + * @param couponCode [string] 优惠券码
  110 + * @param printPrice [string] 是否打印价格
  111 + */
  112 + easypayOrderSubmitAsync(uid, cartType, addressId, deliveryTime, deliveryWay, paymentType, paymentId, printPrice, other, remoteIp) { // eslint-disable-line
  113 + let param = {
  114 + method: 'app.Shopping.easySubmit',
  115 + uid: uid,
  116 + cart_type: cartType || 'ordinary',
  117 + address_id: addressId,
  118 + delivery_time: deliveryTime,
  119 + delivery_way: deliveryWay,
  120 + payment_type: paymentType,
  121 + payment_id: paymentId,
  122 + is_print_price: printPrice
  123 + };
  124 +
  125 + // 发票
  126 + if (other.invoicesType) {
  127 + Object.assign(param, {
  128 + invoices_type: other.invoicesType,
  129 + invoices_title: other.invoicesTitle,
  130 + invoice_content: other.invoicesContent,
  131 + receiverMobile: other.receiver
  132 + });
  133 + }
  134 +
  135 + // 有货币
52 if (other.coin) { 136 if (other.coin) {
53 Object.assign(param, { 137 Object.assign(param, {
54 - use_yoho_coin: other.coin / 100 138 + use_yoho_coin: other.coin / 100 // 有货币稀释
55 }); 139 });
56 } 140 }
57 141
58 - if (other.useRedEnvelops) { 142 + // 红包
  143 + if (other.redEnvelopes) {
59 Object.assign(param, { 144 Object.assign(param, {
60 - use_red_envelopes: other.useRedEnvelops 145 + use_red_envelopes: other.redEnvelopes
61 }); 146 });
62 } 147 }
63 148
64 - if (other.couponCode) { 149 + // 备注
  150 + if (other.remark) {
65 Object.assign(param, { 151 Object.assign(param, {
66 - coupon_code: other.couponCode 152 + remark: other.remark
67 }); 153 });
68 } 154 }
69 155
70 - if (other.promotionCode) { 156 + // 优惠券码
  157 + if (other.couponCode) {
71 Object.assign(param, { 158 Object.assign(param, {
72 - promotion_code: other.promotionCode 159 + coupon_code: other.couponCode
73 }); 160 });
74 } 161 }
75 162
@@ -85,106 +172,16 @@ const getEasypayComputeAsync = (uid, cartType, paymentType, deliveryWay, other) @@ -85,106 +172,16 @@ const getEasypayComputeAsync = (uid, cartType, paymentType, deliveryWay, other)
85 } 172 }
86 } 173 }
87 174
88 - }  
89 -  
90 - return api.get('', param);  
91 -};  
92 -  
93 -/**  
94 - * 订单提交API  
95 - * @param uid [number] uid  
96 - * @param cartType [String] 购物车类型,ordinary表示普通, advance表示预售  
97 - * @param addressId [String] 地址  
98 - * @param deliveryTime [number] 寄送时间类型  
99 - * @param deliveryWay [number] 配送方式,1表示普通快递,2表示顺丰速运  
100 - * @param invoicesType [number] 发票类型  
101 - * @param invoicesTitle [string] 发票抬头  
102 - * @param invoicesContent [int] 发票类型  
103 - * @param receiverMobile [string] 发票收人电话  
104 - * @param coin [number] 使用的有货币金额  
105 - * @param redEnvelopes [number] 使用的红包  
106 - * @param paymentId [number] 支付id  
107 - * @param paymentType [number] 支付类型  
108 - * @param remark [string] 备注  
109 - * @param couponCode [string] 优惠券码  
110 - * @param printPrice [string] 是否打印价格  
111 - */  
112 -const easypayOrderSubmitAsync = (uid, cartType, addressId, deliveryTime, deliveryWay, paymentType, paymentId, printPrice, other, remoteIp) => { // eslint-disable-line  
113 - let param = {  
114 - method: 'app.Shopping.easySubmit',  
115 - uid: uid,  
116 - cart_type: cartType || 'ordinary',  
117 - address_id: addressId,  
118 - delivery_time: deliveryTime,  
119 - delivery_way: deliveryWay,  
120 - payment_type: paymentType,  
121 - payment_id: paymentId,  
122 - is_print_price: printPrice  
123 - };  
124 -  
125 - // 发票  
126 - if (other.invoicesType) {  
127 - Object.assign(param, {  
128 - invoices_type: other.invoicesType,  
129 - invoices_title: other.invoicesTitle,  
130 - invoice_content: other.invoicesContent,  
131 - receiverMobile: other.receiver  
132 - });  
133 - }  
134 -  
135 - // 有货币  
136 - if (other.coin) {  
137 - Object.assign(param, {  
138 - use_yoho_coin: other.coin / 100 // 有货币稀释  
139 - });  
140 - }  
141 -  
142 - // 红包  
143 - if (other.redEnvelopes) {  
144 - Object.assign(param, {  
145 - use_red_envelopes: other.redEnvelopes  
146 - });  
147 - }  
148 -  
149 - // 备注  
150 - if (other.remark) {  
151 - Object.assign(param, {  
152 - remark: other.remark  
153 - });  
154 - }  
155 -  
156 - // 优惠券码  
157 - if (other.couponCode) {  
158 - Object.assign(param, {  
159 - coupon_code: other.couponCode  
160 - });  
161 - }  
162 -  
163 - if (other.productSkuList) {  
164 - Object.assign(param, {  
165 - product_sku_list: other.productSkuList  
166 - });  
167 -  
168 - if (other.bundle) { 175 + if (other.udid) {
169 Object.assign(param, { 176 Object.assign(param, {
170 - activity_id: other.bundle 177 + udid: other.udid
171 }); 178 });
172 } 179 }
173 - }  
174 180
175 - if (other.udid) {  
176 - Object.assign(param, {  
177 - udid: other.udid  
178 - }); 181 + return this.get({data: param, param: {
  182 + headers: {
  183 + 'X-Forwarded-For': remoteIp || ''
  184 + }
  185 + }});
179 } 186 }
180 -  
181 - return api.get('', param, {  
182 - headers: {'X-Forwarded-For': remoteIp || ''}  
183 - });  
184 -};  
185 -  
186 -module.exports = {  
187 - getEasyPaymentAsync,  
188 - getEasypayComputeAsync,  
189 - easypayOrderSubmitAsync  
190 }; 187 };
@@ -10,91 +10,101 @@ const _ = require('lodash'); @@ -10,91 +10,101 @@ const _ = require('lodash');
10 const helper = global.yoho.helpers; 10 const helper = global.yoho.helpers;
11 const crypto = global.yoho.crypto; 11 const crypto = global.yoho.crypto;
12 12
13 -const easypayApi = require('./easypay-api');  
14 -const addressApi = require('./address-api');  
15 -const ensureApi = require('./order-ensure-api');  
16 -  
17 -const ensureHandle = require('./order-ensure-handle');  
18 -  
19 -const _getLimitProductData = (params) => {  
20 - let resList = [];  
21 -  
22 - if (params.limitcode && params.sku && params.skn) {  
23 - resList.push({  
24 - type: 'limitcode',  
25 - buy_number: 1,  
26 - sku: params.sku,  
27 - skn: params.skn,  
28 - limitproductcode: params.limitcode  
29 - });  
30 - } else if (params.bundle && params.sku) {  
31 - resList = []; 13 +const EasypayApi = require('./easypay-api');
  14 +const AddressApi = require('./address-api');
  15 +const EnsureApi = require('./order-ensure-api');
32 16
33 - _.forEach(_.split(params.sku, ',', 10), val => {  
34 - resList.push({  
35 - type: 'bundle',  
36 - sku: parseInt(val, 10),  
37 - buy_number: 1  
38 - });  
39 - }); 17 +const EnsureHandle = require('./order-ensure-handle');
  18 +
  19 +module.exports = class extends global.yoho.BaseModel {
  20 + constructor(ctx) {
  21 + super(ctx);
40 } 22 }
41 23
42 - return resList.length ? JSON.stringify(resList) : false;  
43 -}; 24 + _getLimitProductData(params) {
  25 + let resList = [];
44 26
45 -// 获取结算信息  
46 -const getEasypayOrderData = (params, uid) => {  
47 - let resData = {};  
48 - let strInfo = _getLimitProductData(params);  
49 - let bundle = params.bundle ? parseInt(params.bundle, 10) : false;  
50 -  
51 - return Promise.all([  
52 - easypayApi.getEasyPaymentAsync(uid, strInfo, bundle),  
53 - addressApi.getAddressListAsync(uid),  
54 - ensureApi.getUserProfileAsync(uid)  
55 - ]).then(result => {  
56 - let data = _.get(result, '[0].data', false);  
57 - let address = _.get(result, '[1].data', []);  
58 - let receiver = _.get(result, '[2].data.mobile', '');  
59 -  
60 - if (data) {  
61 - Object.assign(resData, ensureHandle.handlePaymentInfo(data, address), {  
62 - receiverMobile: receiver,  
63 - hideReceiverMobile: _.replace(receiver, /(\d{3})\d{4}(\d{4,9})/, '$1****$2') 27 + if (params.limitcode && params.sku && params.skn) {
  28 + resList.push({
  29 + type: 'limitcode',
  30 + buy_number: 1,
  31 + sku: params.sku,
  32 + skn: params.skn,
  33 + limitproductcode: params.limitcode
  34 + });
  35 + } else if (params.bundle && params.sku) {
  36 + resList = [];
  37 +
  38 + _.forEach(_.split(params.sku, ',', 10), val => {
  39 + resList.push({
  40 + type: 'bundle',
  41 + sku: parseInt(val, 10),
  42 + buy_number: 1
  43 + });
64 }); 44 });
65 } 45 }
66 46
67 - return resData;  
68 - });  
69 -};  
70 -  
71 -// 价格计算  
72 -const getOrderComputeData = (uid, cartType, params) => {  
73 - params.productSkuList = _getLimitProductData(params); 47 + return resList.length ? JSON.stringify(resList) : false;
  48 + }
74 49
75 - return easypayApi.getEasypayComputeAsync(uid, cartType, params.paymentType, params.deliveryWay, params).then(result => { // eslint-disable-line  
76 - if (result.code === 200) {  
77 - if (_.has(result, 'data.last_order_amount')) {  
78 - result.data.last_order_amount = (result.data.last_order_amount).toFixed(2); 50 + // 获取结算信息
  51 + getEasypayOrderData(params, uid) {
  52 + let resData = {};
  53 + let strInfo = this._getLimitProductData(params);
  54 + let bundle = params.bundle ? parseInt(params.bundle, 10) : false;
  55 +
  56 + return Promise.all([
  57 + new EasypayApi(this.ctx).getEasyPaymentAsync(uid, strInfo, bundle),
  58 + new AddressApi(this.ctx).getAddressListAsync(uid),
  59 + new EnsureApi(this.ctx).getUserProfileAsync(uid)
  60 + ]).then(result => {
  61 + let data = _.get(result, '[0].data', false);
  62 + let address = _.get(result, '[1].data', []);
  63 + let receiver = _.get(result, '[2].data.mobile', '');
  64 +
  65 + if (data) {
  66 + Object.assign(resData, new EnsureHandle(this.ctx).handlePaymentInfo(data, address), {
  67 + receiverMobile: receiver,
  68 + hideReceiverMobile: _.replace(receiver, /(\d{3})\d{4}(\d{4,9})/, '$1****$2')
  69 + });
79 } 70 }
80 71
81 - if (_.has(result, 'data.promotion_formula_list')) {  
82 - ensureHandle.handleViewPrice(result.data.promotion_formula_list);  
83 - } 72 + return resData;
  73 + });
  74 + }
84 75
85 - result.data = Object.assign(result.data, ensureHandle.handleUseYohoCoin(result.data));  
86 - }  
87 - return result;  
88 - });  
89 -}; 76 + // 价格计算
  77 + getOrderComputeData(uid, cartType, params) {
  78 + params.productSkuList = this._getLimitProductData(params);
  79 +
  80 + return new EasypayApi(this.ctx).getEasypayComputeAsync(uid, cartType, params.paymentType, params.deliveryWay, params).then(result => { // eslint-disable-line
  81 + if (result.code === 200) {
  82 + let ensureHandleModel = new EnsureHandle(this.ctx);
  83 +
  84 + if (_.has(result, 'data.last_order_amount')) {
  85 + result.data.last_order_amount = (result.data.last_order_amount).toFixed(2);
  86 + }
  87 +
  88 + if (_.has(result, 'data.promotion_formula_list')) {
  89 + ensureHandleModel.handleViewPrice(result.data.promotion_formula_list);
  90 + }
  91 +
  92 + result.data = Object.assign(result.data, ensureHandleModel.handleUseYohoCoin(result.data));
  93 + }
  94 + return result;
  95 + });
  96 + }
90 97
91 -// 订单提交  
92 -const easypayOrderSubmit = (uid, cartType, params, remoteIp) => {  
93 - params.addressId = crypto.decrypt('', params.addressId);  
94 - params.productSkuList = _getLimitProductData(params); 98 + // 订单提交
  99 + easypayOrderSubmit(uid, cartType, params, remoteIp) {
  100 + params.addressId = crypto.decrypt('', params.addressId);
  101 + params.productSkuList = this._getLimitProductData(params);
95 102
96 - return easypayApi.easypayOrderSubmitAsync(uid, cartType, params.addressId, params.deliveryTime,  
97 - params.deliveryWay, params.paymentType, params.paymentId, params.printPrice, params, remoteIp).then(result => { 103 + return new EasypayApi(this.ctx).easypayOrderSubmitAsync(
  104 + uid, cartType, params.addressId, params.deliveryTime,
  105 + params.deliveryWay, params.paymentType, params.paymentId,
  106 + params.printPrice, params, remoteIp
  107 + ).then(result => {
98 if (result.code === 200) { 108 if (result.code === 200) {
99 let d = result.data; 109 let d = result.data;
100 110
@@ -103,12 +113,6 @@ const easypayOrderSubmit = (uid, cartType, params, remoteIp) => { @@ -103,12 +113,6 @@ const easypayOrderSubmit = (uid, cartType, params, remoteIp) => {
103 }); 113 });
104 } 114 }
105 return result; 115 return result;
106 - }  
107 - );  
108 -};  
109 -  
110 -module.exports = {  
111 - getEasypayOrderData,  
112 - getOrderComputeData,  
113 - easypayOrderSubmit 116 + });
  117 + }
114 }; 118 };
@@ -5,85 +5,185 @@ @@ -5,85 +5,185 @@
5 */ 5 */
6 'use strict'; 6 'use strict';
7 7
8 -const api = global.yoho.API; 8 +module.exports = class extends global.yoho.BaseModel {
  9 + constructor(ctx) {
  10 + super(ctx);
  11 + }
9 12
10 -/**  
11 - * 获取用户信息API  
12 - * @param uid [number] uid  
13 - */  
14 -const getUserProfileAsync = (uid) => api.get('', {  
15 - method: 'app.passport.profile',  
16 - uid: uid  
17 -}); 13 + /**
  14 + * 获取用户信息API
  15 + * @param uid [number] uid
  16 + */
  17 + getUserProfileAsync(uid) {
  18 + let options = {
  19 + method: 'app.passport.profile',
  20 + uid: uid
  21 + };
  22 +
  23 + return this.get({data: options});
  24 + }
18 25
19 -/**  
20 - * 购物车结算API  
21 - * @param uid [number] uid  
22 - * @param cartType [string] 购物车类型,ordinary表示普通, advance表示预售  
23 - * @param yohoCoinMode [number] 是否使用yoho币,1使用,0不使用  
24 - */  
25 -const getOrderPaymentAsync = (uid, cartType, yohoCoinMode) => api.get('', {  
26 - method: 'app.Shopping.payment',  
27 - cart_type: cartType,  
28 - yoho_coin_mode: yohoCoinMode,  
29 - uid: uid  
30 -}); 26 + /**
  27 + * 购物车结算API
  28 + * @param uid [number] uid
  29 + * @param cartType [string] 购物车类型,ordinary表示普通, advance表示预售
  30 + * @param yohoCoinMode [number] 是否使用yoho币,1使用,0不使用
  31 + */
  32 + getOrderPaymentAsync(uid, cartType, yohoCoinMode) {
  33 + let options = {
  34 + method: 'app.Shopping.payment',
  35 + cart_type: cartType,
  36 + yoho_coin_mode: yohoCoinMode,
  37 + uid: uid
  38 + };
  39 +
  40 + return this.get({data: options});
  41 + }
31 42
32 -/**  
33 - * 获取用户优惠券信息API  
34 - * @param uid [number] uid  
35 - */  
36 -const getUesrCouponAsync = (uid) => api.get('', {  
37 - method: 'app.Shopping.listCoupon',  
38 - uid: uid,  
39 - is_group_frees: 'Y'  
40 -}); 43 + /**
  44 + * 获取用户优惠券信息API
  45 + * @param uid [number] uid
  46 + */
  47 + getUesrCouponAsync(uid) {
  48 + let options = {
  49 + method: 'app.Shopping.listCoupon',
  50 + uid: uid,
  51 + is_group_frees: 'Y'
  52 + };
  53 +
  54 + return this.get({data: options});
  55 + }
41 56
42 -/**  
43 - * 优惠码兑换优惠券API  
44 - * @param uid [number] uid  
45 - * @param code [number] code  
46 - */  
47 -const getCouponByCodeAsync = (uid, code) => api.get('', {  
48 - method: 'app.Shopping.useCoupon',  
49 - uid: uid,  
50 - coupon_code: code  
51 -}); 57 + /**
  58 + * 优惠码兑换优惠券API
  59 + * @param uid [number] uid
  60 + * @param code [number] code
  61 + */
  62 + getCouponByCodeAsync(uid, code) {
  63 + let options = {
  64 + method: 'app.Shopping.useCoupon',
  65 + uid: uid,
  66 + coupon_code: code
  67 + };
  68 +
  69 + return this.get({data: options});
  70 + }
52 71
53 -/**  
54 - * 订单计算API  
55 - * @param uid [number] uid  
56 - * @param cartType [string] 购物车类型,ordinary表示普通, advance表示预售  
57 - * @param paymentType [number] 支付方式,1表示在线支付,2表示货到付款  
58 - * @param deliveryWay [number] 配送方式,1表示普通快递,2表示顺丰速运  
59 - * @param coin [number] 使用的有货币  
60 - * @param useRedEnvelops [number] 红包  
61 - * @param couponCode [string] 优惠券码  
62 - * @param promotionCode [string] 优惠码  
63 - */  
64 -const getOrderComputeAsync = (uid, cartType, paymentType, deliveryWay, other) => {  
65 - let param = {  
66 - method: 'app.Shopping.compute',  
67 - uid: uid,  
68 - cart_type: cartType,  
69 - payment_type: paymentType,  
70 - delivery_way: deliveryWay  
71 - };  
72 -  
73 - // 其他可选参数  
74 - if (other) { 72 + /**
  73 + * 订单计算API
  74 + * @param uid [number] uid
  75 + * @param cartType [string] 购物车类型,ordinary表示普通, advance表示预售
  76 + * @param paymentType [number] 支付方式,1表示在线支付,2表示货到付款
  77 + * @param deliveryWay [number] 配送方式,1表示普通快递,2表示顺丰速运
  78 + * @param coin [number] 使用的有货币
  79 + * @param useRedEnvelops [number] 红包
  80 + * @param couponCode [string] 优惠券码
  81 + * @param promotionCode [string] 优惠码
  82 + */
  83 + getOrderComputeAsync(uid, cartType, paymentType, deliveryWay, other) {
  84 + let param = {
  85 + method: 'app.Shopping.compute',
  86 + uid: uid,
  87 + cart_type: cartType,
  88 + payment_type: paymentType,
  89 + delivery_way: deliveryWay
  90 + };
  91 +
  92 + // 其他可选参数
  93 + if (other) {
  94 + if (other.coin) {
  95 + Object.assign(param, {
  96 + use_yoho_coin: other.coin
  97 + });
  98 + }
  99 +
  100 + if (other.redEnvelopes) {
  101 + Object.assign(param, {
  102 + use_red_envelopes: other.redEnvelopes
  103 + });
  104 + }
  105 +
  106 + if (other.couponCode) {
  107 + Object.assign(param, {
  108 + coupon_code: other.couponCode
  109 + });
  110 + } else if (other.promotionCode) {
  111 + Object.assign(param, {
  112 + promotion_code: other.promotionCode
  113 + });
  114 + }
  115 + }
  116 +
  117 + return this.get({data: param});
  118 + }
  119 +
  120 + /**
  121 + * 订单提交API
  122 + * @param uid [number] uid
  123 + * @param cartType [String] 购物车类型,ordinary表示普通, advance表示预售
  124 + * @param addressId [String] 地址
  125 + * @param deliveryTime [number] 寄送时间类型
  126 + * @param deliveryWay [number] 配送方式,1表示普通快递,2表示顺丰速运
  127 + * @param invoicesType [number] 发票类型
  128 + * @param invoicesTitle [string] 发票抬头
  129 + * @param invoicesContent [int] 发票类型
  130 + * @param receiverMobile [string] 发票收人电话
  131 + * @param coin [number] 使用的有货币金额
  132 + * @param redEnvelopes [number] 使用的红包
  133 + * @param paymentId [number] 支付id
  134 + * @param paymentType [number] 支付类型
  135 + * @param remark [string] 备注
  136 + * @param couponCode [string] 优惠券码
  137 + * @param printPrice [string] 是否打印价格
  138 + */
  139 + orderSubmitAsync(uid, cartType, addressId, deliveryTime, deliveryWay, paymentType, paymentId, printPrice, other, remoteIp) { // eslint-disable-line
  140 + let param = {
  141 + method: 'app.Shopping.submit',
  142 + uid: uid,
  143 + cart_type: cartType,
  144 + address_id: addressId,
  145 + delivery_time: deliveryTime,
  146 + delivery_way: deliveryWay,
  147 + payment_type: paymentType,
  148 + payment_id: paymentId,
  149 + is_print_price: printPrice,
  150 + is_continue_buy: other.continueBuy ? 'Y' : 'N'
  151 + };
  152 +
  153 + // 发票
  154 + if (other.invoicesType) {
  155 + Object.assign(param, {
  156 + invoices_type: other.invoicesType,
  157 + invoices_title: other.invoicesTitle,
  158 + invoice_content: other.invoicesContent,
  159 + receiverMobile: other.receiverMobile,
  160 + buyerTaxNumber: other.taxNumber,
  161 + invoice_payable_type: other.taxNumber ? 2 : 1
  162 + });
  163 + }
  164 +
  165 + // 有货币
75 if (other.coin) { 166 if (other.coin) {
76 Object.assign(param, { 167 Object.assign(param, {
77 - use_yoho_coin: other.coin 168 + use_yoho_coin: other.coin / 100 // 有货币稀释
78 }); 169 });
79 } 170 }
80 171
  172 + // 红包
81 if (other.redEnvelopes) { 173 if (other.redEnvelopes) {
82 Object.assign(param, { 174 Object.assign(param, {
83 use_red_envelopes: other.redEnvelopes 175 use_red_envelopes: other.redEnvelopes
84 }); 176 });
85 } 177 }
86 178
  179 + // 备注
  180 + if (other.remark) {
  181 + Object.assign(param, {
  182 + remark: other.remark
  183 + });
  184 + }
  185 +
  186 + // 优惠券码
87 if (other.couponCode) { 187 if (other.couponCode) {
88 Object.assign(param, { 188 Object.assign(param, {
89 coupon_code: other.couponCode 189 coupon_code: other.couponCode
@@ -93,120 +193,32 @@ const getOrderComputeAsync = (uid, cartType, paymentType, deliveryWay, other) => @@ -93,120 +193,32 @@ const getOrderComputeAsync = (uid, cartType, paymentType, deliveryWay, other) =>
93 promotion_code: other.promotionCode 193 promotion_code: other.promotionCode
94 }); 194 });
95 } 195 }
96 - }  
97 196
98 - return api.get('', param);  
99 -};  
100 -  
101 -/**  
102 - * 订单提交API  
103 - * @param uid [number] uid  
104 - * @param cartType [String] 购物车类型,ordinary表示普通, advance表示预售  
105 - * @param addressId [String] 地址  
106 - * @param deliveryTime [number] 寄送时间类型  
107 - * @param deliveryWay [number] 配送方式,1表示普通快递,2表示顺丰速运  
108 - * @param invoicesType [number] 发票类型  
109 - * @param invoicesTitle [string] 发票抬头  
110 - * @param invoicesContent [int] 发票类型  
111 - * @param receiverMobile [string] 发票收人电话  
112 - * @param coin [number] 使用的有货币金额  
113 - * @param redEnvelopes [number] 使用的红包  
114 - * @param paymentId [number] 支付id  
115 - * @param paymentType [number] 支付类型  
116 - * @param remark [string] 备注  
117 - * @param couponCode [string] 优惠券码  
118 - * @param printPrice [string] 是否打印价格  
119 - */  
120 -const orderSubmitAsync = (uid, cartType, addressId, deliveryTime, deliveryWay, paymentType, paymentId, printPrice, other, remoteIp) => { // eslint-disable-line  
121 - let param = {  
122 - method: 'app.Shopping.submit',  
123 - uid: uid,  
124 - cart_type: cartType,  
125 - address_id: addressId,  
126 - delivery_time: deliveryTime,  
127 - delivery_way: deliveryWay,  
128 - payment_type: paymentType,  
129 - payment_id: paymentId,  
130 - is_print_price: printPrice,  
131 - is_continue_buy: other.continueBuy ? 'Y' : 'N'  
132 - };  
133 -  
134 - // 发票  
135 - if (other.invoicesType) {  
136 - Object.assign(param, {  
137 - invoices_type: other.invoicesType,  
138 - invoices_title: other.invoicesTitle,  
139 - invoice_content: other.invoicesContent,  
140 - receiverMobile: other.receiverMobile,  
141 - buyerTaxNumber: other.taxNumber,  
142 - invoice_payable_type: other.taxNumber ? 2 : 1  
143 - });  
144 - }  
145 -  
146 - // 有货币  
147 - if (other.coin) {  
148 - Object.assign(param, {  
149 - use_yoho_coin: other.coin / 100 // 有货币稀释  
150 - });  
151 - }  
152 -  
153 - // 红包  
154 - if (other.redEnvelopes) {  
155 - Object.assign(param, {  
156 - use_red_envelopes: other.redEnvelopes  
157 - });  
158 - }  
159 -  
160 - // 备注  
161 - if (other.remark) {  
162 - Object.assign(param, {  
163 - remark: other.remark  
164 - });  
165 - }  
166 -  
167 - // 优惠券码  
168 - if (other.couponCode) {  
169 - Object.assign(param, {  
170 - coupon_code: other.couponCode  
171 - });  
172 - } else if (other.promotionCode) {  
173 - Object.assign(param, {  
174 - promotion_code: other.promotionCode  
175 - });  
176 - } 197 + if (other.continueBuy) {
  198 + Object.assign(param, {
  199 + is_continue_buy: 'Y'
  200 + });
  201 + }
177 202
178 - if (other.continueBuy) {  
179 - Object.assign(param, {  
180 - is_continue_buy: 'Y'  
181 - });  
182 - } 203 + if (other.udid) {
  204 + Object.assign(param, {
  205 + udid: other.udid
  206 + });
  207 + }
183 208
184 - if (other.udid) {  
185 - Object.assign(param, {  
186 - udid: other.udid  
187 - });  
188 - } 209 + // 友盟有关信息的传递
  210 + if (other.qhyUnion) {
  211 + Object.assign(param, {
  212 + qhy_union: other.qhyUnion
  213 + });
  214 + }
189 215
190 - // 友盟有关信息的传递  
191 - if (other.qhyUnion) {  
192 - Object.assign(param, {  
193 - qhy_union: other.qhyUnion  
194 - }); 216 + return this.get({data: param, param: {
  217 + headers: {
  218 + 'X-Forwarded-For': remoteIp || '',
  219 + 'User-Agent': other.userAgent || ''
  220 + }
  221 + }});
195 } 222 }
196 -  
197 - return api.get('', param, {  
198 - headers: {  
199 - 'X-Forwarded-For': remoteIp || '',  
200 - 'User-Agent': other.userAgent || ''  
201 - }  
202 - });  
203 }; 223 };
204 224
205 -module.exports = {  
206 - getUserProfileAsync,  
207 - getOrderPaymentAsync,  
208 - getUesrCouponAsync,  
209 - getCouponByCodeAsync,  
210 - getOrderComputeAsync,  
211 - orderSubmitAsync  
212 -};  
@@ -6,121 +6,119 @@ @@ -6,121 +6,119 @@
6 'use strict'; 6 'use strict';
7 7
8 const _ = require('lodash'); 8 const _ = require('lodash');
9 -  
10 const helper = global.yoho.helpers; 9 const helper = global.yoho.helpers;
11 const crypto = global.yoho.crypto; 10 const crypto = global.yoho.crypto;
12 11
13 -const handleViewPrice = list => {  
14 - if (list) {  
15 - _.forEach(list, val => {  
16 - if (val.promotion === '商品金额') {  
17 - val.promotion = '商品总价';  
18 - val.promotion_amount = `+ ${val.promotion_amount}`;  
19 - } else {  
20 - val.promotion_amount = _.replace(val.promotion_amount, '+', '+ ');  
21 - val.promotion_amount = _.replace(val.promotion_amount, '-', '- ');  
22 - }  
23 - }); 12 +module.exports = class extends global.yoho.BaseModel {
  13 + constructor(ctx) {
  14 + super(ctx);
24 } 15 }
25 -};  
26 -  
27 -const handleUseYohoCoin = (info) => {  
28 - let resData = {};  
29 - let limitCoin = _.get(info, 'yoho_coin_pay_rule.num_limit', 0);  
30 -  
31 - if (info) {  
32 - Object.assign(resData, {  
33 - yoho_coin: info.yoho_coin.toFixed(2),  
34 - use_yoho_coin: info.use_yoho_coin,  
35 - total_yoho_coin_num: info.total_yoho_coin_num,  
36 - yoho_coin_pay_rule: info.yoho_coin_pay_rule,  
37 - canUseCoinNum: _.round(info.yoho_coin * 100),  
38 - usedCoinNum: _.round(info.use_yoho_coin * 100)  
39 - });  
40 16
41 - if (!resData.canUseCoinNum) {  
42 - let coinErrorTip = ''; 17 + handleViewPrice(list) {
  18 + if (list) {
  19 + _.forEach(list, val => {
  20 + if (val.promotion === '商品金额') {
  21 + val.promotion = '商品总价';
  22 + val.promotion_amount = `+ ${val.promotion_amount}`;
  23 + } else {
  24 + val.promotion_amount = _.replace(val.promotion_amount, '+', '+ ');
  25 + val.promotion_amount = _.replace(val.promotion_amount, '-', '- ');
  26 + }
  27 + });
  28 + }
  29 + }
43 30
44 - if (info.total_yoho_coin_num > limitCoin) {  
45 - coinErrorTip = '抱歉,您的订单实付款不满足有货币使用条件';  
46 - } else {  
47 - coinErrorTip = `抱歉,您的有货币不足,有货币满${limitCoin}个方可使用`; 31 + handleUseYohoCoin(info) {
  32 + let resData = {};
  33 + let limitCoin = _.get(info, 'yoho_coin_pay_rule.num_limit', 0);
  34 +
  35 + if (info) {
  36 + Object.assign(resData, {
  37 + yoho_coin: info.yoho_coin.toFixed(2),
  38 + use_yoho_coin: info.use_yoho_coin,
  39 + total_yoho_coin_num: info.total_yoho_coin_num,
  40 + yoho_coin_pay_rule: info.yoho_coin_pay_rule,
  41 + canUseCoinNum: _.round(info.yoho_coin * 100),
  42 + usedCoinNum: _.round(info.use_yoho_coin * 100)
  43 + });
  44 +
  45 + if (!resData.canUseCoinNum) {
  46 + let coinErrorTip = '';
  47 +
  48 + if (info.total_yoho_coin_num > limitCoin) {
  49 + coinErrorTip = '抱歉,您的订单实付款不满足有货币使用条件';
  50 + } else {
  51 + coinErrorTip = `抱歉,您的有货币不足,有货币满${limitCoin}个方可使用`;
  52 + }
  53 +
  54 + resData.coinErrorTip = coinErrorTip;
48 } 55 }
49 -  
50 - resData.coinErrorTip = coinErrorTip;  
51 } 56 }
  57 +
  58 + return resData;
52 } 59 }
53 60
54 - return resData;  
55 -}; 61 + handlePaymentInfo(d, address) {
  62 + let resData = {};
  63 + let defAddrId = _.get(d, 'delivery_address.address_id', 0);
56 64
57 -const handlePaymentInfo = (d, address) => {  
58 - let resData = {};  
59 - let defAddrId = _.get(d, 'delivery_address.address_id', 0); 65 + _.forEach(address, dd => {
  66 + if (dd.is_default === 'Y') {
  67 + dd.default = true;
  68 + }
60 69
61 - _.forEach(address, dd => {  
62 - if (dd.is_default === 'Y') {  
63 - dd.default = true;  
64 - } 70 + // 地址加密
  71 + let id = dd.address_id;
65 72
66 - // 地址加密  
67 - let id = dd.address_id; 73 + dd.id = crypto.encryption('', `${id}`);
68 74
69 - dd.id = crypto.encryption('', `${id}`); 75 + // 设置默认选中地址
  76 + if (id === defAddrId) {
  77 + dd.selected = true;
  78 + }
70 79
71 - // 设置默认选中地址  
72 - if (id === defAddrId) {  
73 - dd.selected = true;  
74 - } 80 + // 删除uid,用户数据保密
  81 + _.unset(dd, 'address_id');
  82 + _.unset(dd, 'uid');
  83 + });
75 84
76 - // 删除uid,用户数据保密  
77 - _.unset(dd, 'address_id');  
78 - _.unset(dd, 'uid');  
79 - }); 85 + resData.deliveryAddress = address;
80 86
81 - resData.deliveryAddress = address; 87 + d.shopping_cart_data.hasCoin = _.round(d.yoho_coin * 100); // 有货币稀释
82 88
83 - d.shopping_cart_data.hasCoin = _.round(d.yoho_coin * 100); // 有货币稀释 89 + _.forEach(d.goods_list, g => {
  90 + g.last_vip_price = g.last_vip_price && Number(g.last_vip_price).toFixed(2) || '';
  91 + g.sales_price = g.sales_price && Number(g.sales_price).toFixed(2) || '';
84 92
85 - _.forEach(d.goods_list, g => {  
86 - g.last_vip_price = g.last_vip_price && Number(g.last_vip_price).toFixed(2) || '';  
87 - g.sales_price = g.sales_price && Number(g.sales_price).toFixed(2) || ''; 93 + // link to goods
  94 + g.linkToGoods = helper.urlFormat(`/product/pro_${g.product_id}_${g.goods_id}/${g.cn_alphabet}.html`,
  95 + '', 'item');
  96 + g.productPrice = g.sales_price;
  97 + g.isVipPrice = g.discount_tag === 'V';
  98 + g.isStuPrice = g.discount_tag === 'S';
88 99
89 - // link to goods  
90 - g.linkToGoods = helper.urlFormat(`/product/pro_${g.product_id}_${g.goods_id}/${g.cn_alphabet}.html`,  
91 - '', 'item');  
92 - g.productPrice = g.sales_price;  
93 - g.isVipPrice = g.discount_tag === 'V';  
94 - g.isStuPrice = g.discount_tag === 'S'; 100 + // 赠品、加价购
  101 + if (g.goods_type === 'gift' || g.goods_type === 'price_gift') {
  102 + g.productPrice = g.last_price;
  103 + }
  104 + });
  105 + resData.goodsList = d.goods_list;
95 106
96 - // 赠品、加价购  
97 - if (g.goods_type === 'gift' || g.goods_type === 'price_gift') {  
98 - g.productPrice = g.last_price; 107 + if (d.shopping_cart_data && d.shopping_cart_data.promotion_formula_list) {
  108 + this.handleViewPrice(d.shopping_cart_data.promotion_formula_list);
99 } 109 }
100 - });  
101 - resData.goodsList = d.goods_list;  
102 -  
103 - if (d.shopping_cart_data && d.shopping_cart_data.promotion_formula_list) {  
104 - handleViewPrice(d.shopping_cart_data.promotion_formula_list);  
105 - }  
106 -  
107 - Object.assign(resData, {  
108 - paymentWay: d.payment_way,  
109 - deliveryTime: d.delivery_time,  
110 - deliveryWay: d.delivery_way,  
111 - shoppingCartData: Object.assign(d.shopping_cart_data, handleUseYohoCoin(d), {  
112 - redEnvelopes: d.red_envelopes,  
113 - useRedEnvelopes: d.use_red_envelopes  
114 - }),  
115 - invoices: d.invoices  
116 - });  
117 -  
118 - return resData;  
119 -};  
120 110
  111 + Object.assign(resData, {
  112 + paymentWay: d.payment_way,
  113 + deliveryTime: d.delivery_time,
  114 + deliveryWay: d.delivery_way,
  115 + shoppingCartData: Object.assign(d.shopping_cart_data, this.handleUseYohoCoin(d), {
  116 + redEnvelopes: d.red_envelopes,
  117 + useRedEnvelopes: d.use_red_envelopes
  118 + }),
  119 + invoices: d.invoices
  120 + });
121 121
122 -module.exports = {  
123 - handleViewPrice,  
124 - handleUseYohoCoin,  
125 - handlePaymentInfo 122 + return resData;
  123 + }
126 }; 124 };
@@ -6,109 +6,110 @@ @@ -6,109 +6,110 @@
6 'use strict'; 6 'use strict';
7 7
8 const _ = require('lodash'); 8 const _ = require('lodash');
9 -  
10 -// const api = global.yoho.API;  
11 const helper = global.yoho.helpers; 9 const helper = global.yoho.helpers;
12 -  
13 const crypto = global.yoho.crypto; 10 const crypto = global.yoho.crypto;
14 11
15 -// const config = global.yoho.config;  
16 -  
17 -const ensureApi = require('./order-ensure-api');  
18 -const addressApi = require('./address-api'); 12 +const EnsureApi = require('./order-ensure-api');
  13 +const AddressApi = require('./address-api');
  14 +const EnsureHandle = require('./order-ensure-handle');
19 15
20 -const ensureHandle = require('./order-ensure-handle');  
21 -  
22 -const index = (uid, cartType) => {  
23 - let resData = {}; 16 +module.exports = class extends global.yoho.BaseModel {
  17 + constructor(ctx) {
  18 + super(ctx);
  19 + }
24 20
25 - return Promise.all([  
26 - ensureApi.getOrderPaymentAsync(uid, cartType, 0),  
27 - addressApi.getAddressListAsync(uid),  
28 - ensureApi.getUserProfileAsync(uid)  
29 - ]).then(result => {  
30 - let data = _.get(result, '[0].data', false);  
31 - let address = _.get(result, '[1].data', []);  
32 - let receiver = _.get(result, '[2].data.mobile', ''); 21 + index(uid, cartType) {
  22 + let resData = {};
  23 + let addressApiModel = new AddressApi(this.ctx);
  24 + let ensureApiModel = new EnsureApi(this.ctx);
  25 + let ensureHandleModel = new EnsureHandle(this.ctx);
  26 +
  27 + return Promise.all([
  28 + ensureApiModel.getOrderPaymentAsync(uid, cartType, 0),
  29 + addressApiModel.getAddressListAsync(uid),
  30 + ensureApiModel.getUserProfileAsync(uid)
  31 + ]).then(result => {
  32 + let data = _.get(result, '[0].data', false);
  33 + let address = _.get(result, '[1].data', []);
  34 + let receiver = _.get(result, '[2].data.mobile', '');
  35 +
  36 + if (data) {
  37 + Object.assign(resData, ensureHandleModel.handlePaymentInfo(data, address), {
  38 + receiverMobile: receiver,
  39 + hideReceiverMobile: _.replace(receiver, /(\d{3})\d{4}(\d{4,9})/, '$1****$2')
  40 + });
  41 + }
33 42
34 - if (data) {  
35 - Object.assign(resData, ensureHandle.handlePaymentInfo(data, address), {  
36 - receiverMobile: receiver,  
37 - hideReceiverMobile: _.replace(receiver, /(\d{3})\d{4}(\d{4,9})/, '$1****$2')  
38 - });  
39 - } 43 + return resData;
  44 + });
  45 + }
40 46
41 - return resData;  
42 - });  
43 -}; 47 + // 获取优惠券列表
  48 + getCoupons(uid) {
  49 + return new EnsureApi(this.ctx).getUesrCouponAsync(uid).then(result => {
  50 + if (_.isEmpty(_.get(result, 'data.usable_coupons', [])) &&
  51 + _.isEmpty(_.get(result, 'data.usable_frees_coupons', []))) {
  52 + _.set(result, 'data.emptyUsable', true);
  53 + }
  54 + return result;
  55 + });
  56 + }
44 57
45 -// 获取优惠券列表  
46 -const getCoupons = (uid) => ensureApi.getUesrCouponAsync(uid).then(result => {  
47 - if (_.isEmpty(_.get(result, 'data.usable_coupons', [])) &&  
48 - _.isEmpty(_.get(result, 'data.usable_frees_coupons', []))) {  
49 - _.set(result, 'data.emptyUsable', true); 58 + // 兑换优惠券
  59 + convertCoupons(uid, code) {
  60 + return new EnsureApi(this.ctx).getCouponByCodeAsync(uid, code);
50 } 61 }
51 62
52 - return result;  
53 -}); 63 + // 订单计算
  64 + compute(uid, cartType, pa) {
  65 + return new EnsureApi(this.ctx).getOrderComputeAsync(
  66 + uid, cartType, pa.paymentType, pa.deliveryWay, pa
  67 + ).then(result => {
  68 + if (result.code === 200) {
  69 + let ensureHandleModel = new EnsureHandle(this.ctx);
54 70
55 -// 兑换优惠券  
56 -const convertCoupons = (uid, code) => ensureApi.getCouponByCodeAsync(uid, code); 71 + if (_.has(result, 'data.last_order_amount')) {
  72 + result.data.last_order_amount = (result.data.last_order_amount).toFixed(2);
  73 + }
57 74
  75 + if (_.has(result, 'data.promotion_formula_list')) {
  76 + ensureHandleModel.handleViewPrice(result.data.promotion_formula_list);
  77 + }
58 78
59 -// 订单计算  
60 -const compute = (uid, cartType, pa) => {  
61 - return ensureApi.getOrderComputeAsync(uid, cartType, pa.paymentType, pa.deliveryWay, pa).then(result => {  
62 - if (result.code === 200) {  
63 - if (_.has(result, 'data.last_order_amount')) {  
64 - result.data.last_order_amount = (result.data.last_order_amount).toFixed(2); 79 + result.data = Object.assign(result.data, ensureHandleModel.handleUseYohoCoin(result.data));
65 } 80 }
66 81
67 - if (_.has(result, 'data.promotion_formula_list')) {  
68 - ensureHandle.handleViewPrice(result.data.promotion_formula_list);  
69 - } 82 + return result;
  83 + });
  84 + }
70 85
71 - result.data = Object.assign(result.data, ensureHandle.handleUseYohoCoin(result.data)); 86 + // 订单提交
  87 + submit(uid, cartType, p, remoteIp) {
  88 + if (p.addressId) {
  89 + p.addressId = crypto.decrypt('', `${p.addressId}`);
72 } 90 }
73 91
74 - return result;  
75 - });  
76 -};  
77 -  
78 -// 订单提交  
79 -const submit = (uid, cartType, p, remoteIp) => {  
80 - if (p.addressId) {  
81 - p.addressId = crypto.decrypt('', `${p.addressId}`);  
82 - } 92 + // 5.8.1 发票优化需求
  93 + // 只接受电子发票(1 纸质 2 电子),发票内容为明细(id 12:明细)
  94 + if (p.invoicesType) {
  95 + Object.assign(p, {
  96 + invoicesType: 2,
  97 + invoicesContent: 12
  98 + });
  99 + }
83 100
84 - // 5.8.1 发票优化需求  
85 - // 只接受电子发票(1 纸质 2 电子),发票内容为明细(id 12:明细)  
86 - if (p.invoicesType) {  
87 - Object.assign(p, {  
88 - invoicesType: 2,  
89 - invoicesContent: 12  
90 - });  
91 - } 101 + return new EnsureApi(this.ctx).orderSubmitAsync(uid, cartType, p.addressId, p.deliveryTime,
  102 + p.deliveryWay, p.paymentType, p.paymentId, p.printPrice, p, remoteIp).then(result => {
  103 + if (result.code === 200) {
  104 + let d = result.data;
92 105
93 - return ensureApi.orderSubmitAsync(uid, cartType, p.addressId, p.deliveryTime,  
94 - p.deliveryWay, p.paymentType, p.paymentId, p.printPrice, p, remoteIp).then(result => {  
95 - if (result.code === 200) {  
96 - let d = result.data; 106 + d.url = helper.urlFormat('/shopping/newpay', {
  107 + ordercode: d.order_code
  108 + });
  109 + }
97 110
98 - d.url = helper.urlFormat('/shopping/newpay', {  
99 - ordercode: d.order_code  
100 - }); 111 + return result;
101 } 112 }
102 -  
103 - return result;  
104 - }  
105 - );  
106 -};  
107 -  
108 -module.exports = {  
109 - index,  
110 - getCoupons,  
111 - convertCoupons,  
112 - compute,  
113 - submit 113 + );
  114 + }
114 }; 115 };
@@ -2,55 +2,54 @@ @@ -2,55 +2,54 @@
2 * Created by TaoHuang on 2017/6/22. 2 * Created by TaoHuang on 2017/6/22.
3 */ 3 */
4 4
5 -const api = global.yoho.API;  
6 -  
7 -/**  
8 - * 电子票下单  
9 - * @param uid 用户id  
10 - * @param sku 商品sku  
11 - * @param count 购买数量 1-4  
12 - * @param mobile 手机号码  
13 - * @param yohoCoin 有货币  
14 - */  
15 -function submit(uid, sku, count, mobile, yohoCoin) {  
16 - let params = {  
17 - method: 'app.shopping.submitTicket',  
18 - uid,  
19 - product_sku: sku,  
20 - buy_number: count,  
21 - mobile: mobile  
22 - };  
23 -  
24 - if (yohoCoin) {  
25 - params.use_yoho_coin = yohoCoin / 100; 5 +module.exports = class extends global.yoho.BaseModel {
  6 + constructor(ctx) {
  7 + super(ctx);
26 } 8 }
27 9
28 - return api.get('', params);  
29 -}  
30 -  
31 -/**  
32 - * 电子票添加和查询  
33 - * @param uid 用户 id  
34 - * @param sku 商品sku  
35 - * @param count 购买数量 1-4  
36 - * @param yohoCoin 有货币  
37 - */  
38 -function add(uid, sku, count, yohoCoin) {  
39 - let params = {  
40 - method: 'app.shopping.ticket',  
41 - uid,  
42 - product_sku: sku,  
43 - buy_number: count  
44 - };  
45 -  
46 - if (yohoCoin) {  
47 - params.use_yoho_coin = yohoCoin / 100; 10 + /**
  11 + * 电子票下单
  12 + * @param uid 用户id
  13 + * @param sku 商品sku
  14 + * @param count 购买数量 1-4
  15 + * @param mobile 手机号码
  16 + * @param yohoCoin 有货币
  17 + */
  18 + submit(uid, sku, count, mobile, yohoCoin) {
  19 + let params = {
  20 + method: 'app.shopping.submitTicket',
  21 + uid,
  22 + product_sku: sku,
  23 + buy_number: count,
  24 + mobile: mobile
  25 + };
  26 +
  27 + if (yohoCoin) {
  28 + params.use_yoho_coin = yohoCoin / 100;
  29 + }
  30 +
  31 + return this.get({data: params});
48 } 32 }
49 33
50 - return api.get('', params);  
51 -}  
52 -  
53 -module.exports = {  
54 - submit,  
55 - add 34 + /**
  35 + * 电子票添加和查询
  36 + * @param uid 用户 id
  37 + * @param sku 商品sku
  38 + * @param count 购买数量 1-4
  39 + * @param yohoCoin 有货币
  40 + */
  41 + add(uid, sku, count, yohoCoin) {
  42 + let params = {
  43 + method: 'app.shopping.ticket',
  44 + uid,
  45 + product_sku: sku,
  46 + buy_number: count
  47 + };
  48 +
  49 + if (yohoCoin) {
  50 + params.use_yoho_coin = yohoCoin / 100;
  51 + }
  52 +
  53 + return this.get({data: params});
  54 + }
56 }; 55 };
@@ -2,80 +2,83 @@ @@ -2,80 +2,83 @@
2 * Created by TaoHuang on 2017/6/22. 2 * Created by TaoHuang on 2017/6/22.
3 */ 3 */
4 4
5 -const api = require('./ticket-api');  
6 -const Promise = require('bluebird');  
7 -const co = Promise.coroutine; 5 +const TicketApi = require('./ticket-api');
8 const _ = require('lodash'); 6 const _ = require('lodash');
9 const helpers = global.yoho.helpers; 7 const helpers = global.yoho.helpers;
10 -const _handleUseYhoCoin = require('./order-ensure-handle').handleUseYohoCoin; 8 +const OrderEnsureHandle = require('./order-ensure-handle');
11 9
12 -function _handleGoodsList(list) {  
13 - return list.map((i) => {  
14 - i.linkToGoods = helpers.getUrlBySkc(i.product_skn);  
15 - i.productPrice = i.sales_price;  
16 - return i;  
17 - });  
18 -}  
19 -  
20 -function _handleAmount(info) {  
21 - return _.get(info, 'data.shopping_cart_data.last_order_amount', 0);  
22 -}  
23 -  
24 -const addTicket = co(function * (uid, sku, count, yohoCoin) {  
25 - let ticketInfo = yield api.add(uid, sku, count, yohoCoin);  
26 - let result = {}; 10 +module.exports = class extends global.yoho.BaseModel {
  11 + constructor(ctx) {
  12 + super(ctx);
  13 + }
27 14
28 - if (_.isEmpty(ticketInfo)) {  
29 - return {  
30 - last_order_amount: 0,  
31 - error: '人太多啦,稍后再试!'  
32 - }; 15 + _handleGoodsList(list) {
  16 + return list.map((i) => {
  17 + i.linkToGoods = helpers.getUrlBySkc(i.product_skn);
  18 + i.productPrice = i.sales_price;
  19 + return i;
  20 + });
33 } 21 }
34 22
35 - if (ticketInfo.code !== 200) {  
36 - return {  
37 - last_order_amount: 0,  
38 - error: ticketInfo.message  
39 - }; 23 + _handleAmount(info) {
  24 + return _.get(info, 'data.shopping_cart_data.last_order_amount', 0);
40 } 25 }
41 26
42 - result.virtualGood = true;  
43 - result.goodsList = _handleGoodsList(_.get(ticketInfo, 'data.goods_list', []));  
44 - result.last_order_amount = _handleAmount(ticketInfo);  
45 - Object.assign(result, _handleUseYhoCoin(_.get(ticketInfo, 'data.shopping_cart_data', {}))); 27 + addTicket(uid, sku, count, yohoCoin) {
  28 + return new TicketApi(this.ctx).add(uid, sku, count, yohoCoin).then(ticketInfo => {
  29 + let result = {};
46 30
47 - return result;  
48 -}); 31 + if (_.isEmpty(ticketInfo)) {
  32 + return {
  33 + last_order_amount: 0,
  34 + error: '人太多啦,稍后再试!'
  35 + };
  36 + }
49 37
50 -const submitTicket = co(function * (uid, sku, count, mobile, yohoCoin) {  
51 - let result = yield api.submit(uid, sku, count, mobile, yohoCoin); 38 + if (ticketInfo.code !== 200) {
  39 + return {
  40 + last_order_amount: 0,
  41 + error: ticketInfo.message
  42 + };
  43 + }
52 44
53 - if (_.isEmpty(result)) {  
54 - return {  
55 - code: 500,  
56 - message: '人太多啦,稍后再试!'  
57 - };  
58 - } 45 + result.virtualGood = true;
  46 + result.goodsList = this._handleGoodsList(_.get(ticketInfo, 'data.goods_list', []));
  47 + result.last_order_amount = this._handleAmount(ticketInfo);
59 48
60 - if (result.code !== 200) {  
61 - return {  
62 - code: result.code,  
63 - message: result.message  
64 - }; 49 + Object.assign(result, new OrderEnsureHandle(this.ctx).handleUseYohoCoin(
  50 + _.get(ticketInfo, 'data.shopping_cart_data', {}
  51 + )));
  52 +
  53 + return result;
  54 + });
65 } 55 }
66 56
67 - return {  
68 - code: 200,  
69 - message: '提交成功',  
70 - data: {  
71 - refer: helpers.urlFormat('/shopping/newpay', {  
72 - ordercode: result.data.order_code  
73 - })  
74 - }  
75 - };  
76 -}); 57 + submitTicket(uid, sku, count, mobile, yohoCoin) {
  58 + return new TicketApi(this.ctx).submit(uid, sku, count, mobile, yohoCoin).then(result => {
  59 + if (_.isEmpty(result)) {
  60 + return {
  61 + code: 500,
  62 + message: '人太多啦,稍后再试!'
  63 + };
  64 + }
  65 +
  66 + if (result.code !== 200) {
  67 + return {
  68 + code: result.code,
  69 + message: result.message
  70 + };
  71 + }
77 72
78 -module.exports = {  
79 - addTicket,  
80 - submitTicket 73 + return {
  74 + code: 200,
  75 + message: '提交成功',
  76 + data: {
  77 + refer: helpers.urlFormat('/shopping/newpay', {
  78 + ordercode: result.data.order_code
  79 + })
  80 + }
  81 + };
  82 + });
  83 + }
81 }; 84 };
@@ -12,7 +12,7 @@ const helpers = global.yoho.helpers; @@ -12,7 +12,7 @@ const helpers = global.yoho.helpers;
12 exports.QRcode = (req, res, next) => { 12 exports.QRcode = (req, res, next) => {
13 let id = req.query.orderCode || 0; 13 let id = req.query.orderCode || 0;
14 14
15 - QRcodeModel.getQRcodeData(id, req.user.uid).then((result)=>{ 15 + req.ctx(QRcodeModel).getQRcodeData(id, req.user.uid).then((result)=>{
16 if (!result) { 16 if (!result) {
17 return next(); 17 return next();
18 } 18 }
@@ -6,19 +6,26 @@ @@ -6,19 +6,26 @@
6 'use strict'; 6 'use strict';
7 7
8 const logger = global.yoho.logger; 8 const logger = global.yoho.logger;
9 -var api = global.yoho.API;  
10 9
11 -exports.getQRcodeData = (id, uid) => {  
12 - return api.get('', {  
13 - method: 'app.SpaceOrders.getQrByOrderCode',  
14 - order_code: id,  
15 - uid: uid  
16 - }).then(result => {  
17 - if (result && result.code === 200) {  
18 - return result.data;  
19 - } else {  
20 - logger.error(`查看二维码ID: ${id} 接口返回数据错误`);  
21 - return false;  
22 - }  
23 - }); 10 +module.exports = class extends global.yoho.BaseModel {
  11 + constructor(ctx) {
  12 + super(ctx);
  13 + }
  14 +
  15 + getQRcodeData(id, uid) {
  16 + let options = {
  17 + method: 'app.SpaceOrders.getQrByOrderCode',
  18 + order_code: id,
  19 + uid: uid
  20 + };
  21 +
  22 + return this.get({data: options}).then(result => {
  23 + if (result && result.code === 200) {
  24 + return result.data;
  25 + } else {
  26 + logger.error(`查看二维码ID: ${id} 接口返回数据错误`);
  27 + return false;
  28 + }
  29 + });
  30 + }
24 }; 31 };
@@ -183,8 +183,8 @@ exports.isFavoriteBrand = (req, res, next) => { @@ -183,8 +183,8 @@ exports.isFavoriteBrand = (req, res, next) => {
183 return next(); 183 return next();
184 } 184 }
185 185
186 - if (!req.body.brandId || !uid) {  
187 - return res.json({code: 400, message: '用户未登录或缺少参数'}); 186 + if (!req.body.brandId) {
  187 + return res.json({code: 400, message: '缺少参数'});
188 } 188 }
189 189
190 req.ctx(searchApi).isFavoriteBrand(uid, brandId).then(result => { 190 req.ctx(searchApi).isFavoriteBrand(uid, brandId).then(result => {
@@ -207,8 +207,8 @@ exports.shopCouponSync = (req, res, next) => { @@ -207,8 +207,8 @@ exports.shopCouponSync = (req, res, next) => {
207 return next(); 207 return next();
208 } 208 }
209 209
210 - if (!id || !uid) {  
211 - return res.json({code: 400, message: '用户未登录或缺少参数'}); 210 + if (!id) {
  211 + return res.json({code: 400, message: '缺少参数'});
212 } 212 }
213 213
214 req.ctx(list).getUserCoupunStatus(id, uid, 'shop').then(result => { 214 req.ctx(list).getUserCoupunStatus(id, uid, 'shop').then(result => {
@@ -225,7 +225,14 @@ const keyId = (req, res, next) => { @@ -225,7 +225,14 @@ const keyId = (req, res, next) => {
225 title: `${query}价格_图片_品牌_怎么样-YOHO!BUY有货`, 225 title: `${query}价格_图片_品牌_怎么样-YOHO!BUY有货`,
226 keywords: `${query},${query}价格,${query}图片,${query}怎么样,${query}品牌,YOHO!BUY有货`, 226 keywords: `${query},${query}价格,${query}图片,${query}怎么样,${query}品牌,YOHO!BUY有货`,
227 description: `YOHO!BUY有货网yohobuy.com是国内专业的${query}网上潮流购物商城,为您找到${_.get(result, 227 description: `YOHO!BUY有货网yohobuy.com是国内专业的${query}网上潮流购物商城,为您找到${_.get(result,
228 - 'search.totalCount', 0)}${query}、产品的详细参数,实时报价,价格行情,图片、评价、品牌等信息。买${query},就上YOHO!BUY有货` 228 + 'search.totalCount', 0)}${query}、产品的详细参数,实时报价,价格行情,图片、评价、品牌等信息。买${query},就上YOHO!BUY有货`,
  229 + pageFooterSeo: {
  230 + description: `YOHO!BUY有货网yohobuy.com是国内专业的<b>${query}</b>网上潮流购物商城,为您找到<b>${_.get(result,
  231 + 'search.totalCount', 0)}</b>条<b>${query}</b>、产品的详细参数,实时报价,价格行情,图片、评价、品牌等信息。买<b>` +
  232 + `${query}</b>,就上YOHO!BUY有货!`,
  233 + queryKey: query,
  234 + wapUrl: helpers.urlFormat(`/chanpin/${id}.html`, null, 'm')
  235 + }
229 }); 236 });
230 237
231 if (!_.get(result, 'search.goods') || !_.get(result, 'search.goods').length) { 238 if (!_.get(result, 'search.goods') || !_.get(result, 'search.goods').length) {
@@ -444,7 +444,15 @@ function getSearchKeywordDataById(id, params, channel) { @@ -444,7 +444,15 @@ function getSearchKeywordDataById(id, params, channel) {
444 }); 444 });
445 }); 445 });
446 446
447 - _.set(resData, 'search.leftContent.allSuggest.list', redisData.data); 447 + let rarr = _.chunk(redisData.data, 12);
  448 +
  449 + if (rarr.length) {
  450 + _.set(resData, 'search.leftContent.allSuggest.list', rarr.shift());
  451 + _.set(resData, 'recommendKeywordsInfo', {
  452 + recommendKeywordsTitle: '更多推荐',
  453 + recommendKeywords: _.concat(...rarr)
  454 + });
  455 + }
448 } 456 }
449 457
450 return resData; 458 return resData;
1 -<div class="product-list-page search-page product-page yoho-page"> 1 +<div class="product-list-page product-search-page product-page yoho-page">
2 {{# search}} 2 {{# search}}
3 3
4 {{> list/list}} 4 {{> list/list}}
@@ -65,17 +65,14 @@ @@ -65,17 +65,14 @@
65 {{> product/left-content}} 65 {{> product/left-content}}
66 </div> 66 </div>
67 <div class="list-right pull-right"> 67 <div class="list-right pull-right">
68 -  
69 {{#if_cond goods.length '<' 20 }} 68 {{#if_cond goods.length '<' 20 }}
70 -  
71 {{#if suggest}} 69 {{#if suggest}}
72 {{#unless changedQuery}} 70 {{#unless changedQuery}}
73 -  
74 - <div class="search-suggest-less">  
75 - <em>"{{name}}"</em>&nbsp;搜索结果太少了,试试{{#suggest}}&nbsp;"<a href="/?query={{.}}&is_rec=Y">{{.}}</a>"&nbsp;{{/suggest}}关键词搜索  
76 - </div> 71 + <div class="search-suggest-less">
  72 + <em>"{{name}}"</em>&nbsp;搜索结果太少了,试试{{#suggest}}&nbsp;"<a href="/?query={{.}}&is_rec=Y">{{.}}</a>"&nbsp;{{/suggest}}关键词搜索
  73 + </div>
77 {{/unless}} 74 {{/unless}}
78 - {{/if}} 75 + {{/if}}
79 {{/if_cond}} 76 {{/if_cond}}
80 77
81 {{#if changedQuery}} 78 {{#if changedQuery}}
@@ -108,14 +105,14 @@ @@ -108,14 +105,14 @@
108 105
109 {{> product/standard-content}} 106 {{> product/standard-content}}
110 107
111 - {{#if @root.search.isSearch}}  
112 -  
113 - {{#if_cond goods.length '<' 20 }} 108 + {{> product/recommend-keywords goodsInfo=@root.recommendKeywordsInfo}}
114 109
  110 + {{#if @root.search.isSearch}}
  111 + {{#if_cond goods.length '<' 20}}
115 <div> 112 <div>
116 - <textarea class="lazy-load-object-2" style="visibility: hidden;">  
117 - <script>getSearchRecommend();</script>  
118 - </textarea> 113 + <textarea class="lazy-load-object-2" style="visibility: hidden;">
  114 + <script>getSearchRecommend();</script>
  115 + </textarea>
119 </div> 116 </div>
120 117
121 <div class="bottom-tab-line"> 118 <div class="bottom-tab-line">
@@ -125,13 +122,17 @@ @@ -125,13 +122,17 @@
125 <div class="goods-slide"> 122 <div class="goods-slide">
126 <div data-role="recommend-slide" class="slide-panel recommend-slider"></div> 123 <div data-role="recommend-slide" class="slide-panel recommend-slider"></div>
127 </div> 124 </div>
128 -  
129 - {{/if_cond}}  
130 - 125 + {{/if_cond}}
131 {{/if}} 126 {{/if}}
132 127
133 {{> product/latest-walk}} 128 {{> product/latest-walk}}
134 129
  130 + {{# @root.pageFooterSeo}}
  131 + <div class="page-footer-seo">
  132 + <div>{{{description}}}</div>
  133 + <p class="wap-page-href">移动版:<a href="{{wapUrl}}">{{queryKey}}</a></p>
  134 + </div>
  135 + {{/ @root.pageFooterSeo}}
135 </div> 136 </div>
136 {{/ brandAbout}} 137 {{/ brandAbout}}
137 </div> 138 </div>
1 -{{#if goodsInfo.recommendKeywords}}  
2 - <div class="recommend-keywords">  
3 - <h3>相关推荐</h3>  
4 - <p>  
5 - {{# goodsInfo.recommendKeywords}}  
6 - <a href="{{url}}" title="{{keyword}}" target="_blank" class="keyword">{{keyword}}</a>  
7 - {{/ goodsInfo.recommendKeywords}}  
8 - </p>  
9 - </div>  
10 -{{/if}} 1 +{{# goodsInfo}}
  2 + {{#if recommendKeywords}}
  3 + <div class="recommend-keywords">
  4 + <h3>{{#if recommendKeywordsTitle}}{{recommendKeywordsTitle}}{{^}}相关推荐{{/if}}</h3>
  5 + <p>
  6 + {{# recommendKeywords}}
  7 + <a href="{{url}}" title="{{keyword}}" target="_blank" class="keyword">{{keyword}}</a>
  8 + {{/ recommendKeywords}}
  9 + </p>
  10 + </div>
  11 + {{/if}}
11 12
12 -{{#if goodsInfo.recommendNewProducts}}  
13 - <div class="recommend-keywords">  
14 - <h3>新品推荐</h3>  
15 - <p>  
16 - {{# goodsInfo.recommendNewProducts}}  
17 - <a href="{{url}}" title="{{name}}" target="_blank" class="common">{{name}}</a>  
18 - {{/ goodsInfo.recommendNewProducts}}  
19 - </p>  
20 - </div>  
21 -{{/if}} 13 + {{#if recommendNewProducts}}
  14 + <div class="recommend-keywords">
  15 + <h3>新品推荐</h3>
  16 + <p>
  17 + {{# recommendNewProducts}}
  18 + <a href="{{url}}" title="{{name}}" target="_blank" class="common">{{name}}</a>
  19 + {{/ recommendNewProducts}}
  20 + </p>
  21 + </div>
  22 + {{/if}}
22 23
23 -{{#if goodsInfo.recommendArticles}}  
24 - <div class="recommend-keywords">  
25 - <h3>文章推荐</h3>  
26 - <p>  
27 - {{# goodsInfo.recommendArticles}}  
28 - <a href="{{url}}" title="{{name}}" target="_blank" class="common">{{name}}</a>  
29 - {{/ goodsInfo.recommendArticles}}  
30 - </p>  
31 - </div>  
32 -{{/if}} 24 + {{#if recommendArticles}}
  25 + <div class="recommend-keywords">
  26 + <h3>文章推荐</h3>
  27 + <p>
  28 + {{# recommendArticles}}
  29 + <a href="{{url}}" title="{{name}}" target="_blank" class="common">{{name}}</a>
  30 + {{/ recommendArticles}}
  31 + </p>
  32 + </div>
  33 + {{/if}}
  34 +{{/ goodsInfo}}
@@ -168,7 +168,7 @@ module.exports = class extends global.yoho.BaseModel { @@ -168,7 +168,7 @@ module.exports = class extends global.yoho.BaseModel {
168 168
169 if (orderInfo.order_code) { 169 if (orderInfo.order_code) {
170 // 订单处理 170 // 订单处理
171 - if (orderInfo.attribute === 3) { 171 + if (+orderInfo.attribute === 3) {
172 orderInfo.deliveryTimes = '自动发货 - 在您支付成功后,系统将立即为您发放二维码,您可以在您的订单中查看。'; 172 orderInfo.deliveryTimes = '自动发货 - 在您支付成功后,系统将立即为您发放二维码,您可以在您的订单中查看。';
173 } else { 173 } else {
174 orderInfo.deliveryTimes = orderInfo.delivery_time || ''; 174 orderInfo.deliveryTimes = orderInfo.delivery_time || '';
@@ -17,12 +17,12 @@ module.exports = { @@ -17,12 +17,12 @@ module.exports = {
17 cookieDomain: '.yohobuy.com', 17 cookieDomain: '.yohobuy.com',
18 domains: { 18 domains: {
19 // test3 19 // test3
20 - // singleApi: 'http://api-test3.yohops.com:9999/',  
21 - // api: 'http://api-test3.yohops.com:9999/',  
22 - // service: 'http://service-test3.yohops.com:9999/',  
23 - // serviceNotify: 'http://service-test3.yohops.com:9999/',  
24 - // global: 'http://global-test-soa.yohops.com:9999/',  
25 - // platformApi: 'http://192.168.102.48:8088/', 20 + singleApi: 'http://api-test3.yohops.com:9999/',
  21 + api: 'http://api-test3.yohops.com:9999/',
  22 + service: 'http://service-test3.yohops.com:9999/',
  23 + serviceNotify: 'http://service-test3.yohops.com:9999/',
  24 + global: 'http://global-test-soa.yohops.com:9999/',
  25 + platformApi: 'http://192.168.102.48:8088/',
26 26
27 // test2 27 // test2
28 // singleApi: 'http://api-test2.yohops.com:9999/', 28 // singleApi: 'http://api-test2.yohops.com:9999/',
1 { 1 {
2 "name": "yohobuy-node", 2 "name": "yohobuy-node",
3 - "version": "6.0.18", 3 + "version": "6.0.20",
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": {
@@ -137,18 +137,18 @@ function syncCouponStatus() { @@ -137,18 +137,18 @@ function syncCouponStatus() {
137 for (i = 0; i < info.length; i++) { 137 for (i = 0; i < info.length; i++) {
138 asyncObj[info[i].coupon_id] = info[i]; 138 asyncObj[info[i].coupon_id] = info[i];
139 } 139 }
140 - }  
141 -  
142 - for (i in couponObj) {  
143 - if (couponObj.hasOwnProperty(i)) {  
144 - coup = asyncObj[i];  
145 140
146 - if (coup && (coup.status === 1 || coup.status === 3)) {  
147 - couponObj[i].status = coup.status;  
148 - coup.status === 3 ? setPicked(couponObj[i]) : false;  
149 - } else {  
150 - couponObj[i].status = 2; // 券不存在设置领取状态为已抢光  
151 - couponObj[i].dom.text('已抢光'); 141 + for (i in couponObj) {
  142 + if (couponObj.hasOwnProperty(i)) {
  143 + coup = asyncObj[i];
  144 +
  145 + if (coup && (coup.status === 1 || coup.status === 3)) {
  146 + couponObj[i].status = coup.status;
  147 + coup.status === 3 ? setPicked(couponObj[i]) : false;
  148 + } else {
  149 + couponObj[i].status = 2; // 券不存在设置领取状态为已抢光
  150 + couponObj[i].dom.text('已抢光');
  151 + }
152 } 152 }
153 } 153 }
154 } 154 }
@@ -17,6 +17,60 @@ @@ -17,6 +17,60 @@
17 font-weight: bold; 17 font-weight: bold;
18 } 18 }
19 } 19 }
  20 +
  21 + .recommend-keywords {
  22 + margin-top: 30px;
  23 + margin-bottom: 20px;
  24 + border: 1px #e0e0e0 solid;
  25 +
  26 + h3 {
  27 + height: 46px;
  28 + border-bottom: 1px #e0e0e0 solid;
  29 + line-height: 44px;
  30 + background: #f5f5f5;
  31 + text-align: center;
  32 + font-size: 15px;
  33 + }
  34 +
  35 + p {
  36 + padding: 10px;
  37 +
  38 + .keyword {
  39 + display: inline-block;
  40 + margin: 5px 15px;
  41 + font-size: 12px;
  42 + width: 150px;
  43 + white-space: nowrap;
  44 + overflow: hidden;
  45 + text-overflow: ellipsis;
  46 + }
  47 +
  48 + .common {
  49 + display: inline-block;
  50 + margin: 5px 15px;
  51 + font-size: 12px;
  52 + width: 190px;
  53 + white-space: nowrap;
  54 + overflow: hidden;
  55 + text-overflow: ellipsis;
  56 + }
  57 + }
  58 + }
  59 +
  60 + .page-footer-seo {
  61 + font-size: 13px;
  62 + margin: 30px 0;
  63 +
  64 + b {
  65 + font-weight: bold;
  66 + line-height: 2;
  67 + }
  68 +
  69 + .wap-page-href {
  70 + text-align: right;
  71 + font-weight: bold;
  72 + }
  73 + }
20 } 74 }
21 75
22 .min-screen .product-search-page { 76 .min-screen .product-search-page {