Too many changes to show.

To preserve performance only 39 of 39+ files are displayed.

... ... @@ -2,5 +2,9 @@
"extends": "yoho",
"parserOptions": {
"sourceType": "module"
},
"rules": {
"max-len": [0, 50, 4]
}
}
... ...
# Created by https://www.gitignore.io/api/node,webstorm,netbeans,sublimetext,vim
### Node ###
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules
jspm_packages
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
### WebStorm ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
.idea/
# User-specific stuff:
.idea/workspace.xml
.idea/tasks.xml
.idea/dictionaries
.idea/vcs.xml
.idea/jsLibraryMappings.xml
# Sensitive or high-churn files:
.idea/dataSources.ids
.idea/dataSources.xml
.idea/dataSources.local.xml
.idea/sqlDataSources.xml
.idea/dynamic.xml
.idea/uiDesigner.xml
# Gradle:
.idea/gradle.xml
.idea/libraries
# Mongo Explorer plugin:
.idea/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### WebStorm Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
### NetBeans ###
nbproject/private/
build/
nbbuild/
dist/
nbdist/
nbactions.xml
.nb-gradle/
### SublimeText ###
# cache files for sublime text
*.tmlanguage.cache
*.tmPreferences.cache
*.stTheme.cache
# workspace files are user-specific
*.sublime-workspace
# project files should be checked into the repository, unless a significant
# proportion of contributors will probably not be using SublimeText
# *.sublime-project
# sftp configuration file
sftp-config.json
### Vim ###
# swap
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
# session
Session.vim
# temporary
.netrwhist
*~
# auto-generated tag files
tags
### VS Code ###
.vscode/
### YOHO ###
dist
public/css/*
public/bundle/*
.eslintcache
*.log.*
nbproject/*
.DS_Store
# Created by https://www.gitignore.io/api/node,webstorm,netbeans,sublimetext,vim
### Node ###
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules
jspm_packages
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
### WebStorm ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
.idea/
# User-specific stuff:
.idea/workspace.xml
.idea/tasks.xml
.idea/dictionaries
.idea/vcs.xml
.idea/jsLibraryMappings.xml
# Sensitive or high-churn files:
.idea/dataSources.ids
.idea/dataSources.xml
.idea/dataSources.local.xml
.idea/sqlDataSources.xml
.idea/dynamic.xml
.idea/uiDesigner.xml
# Gradle:
.idea/gradle.xml
.idea/libraries
# Mongo Explorer plugin:
.idea/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### WebStorm Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
### NetBeans ###
nbproject/private/
build/
nbbuild/
dist/
nbdist/
nbactions.xml
.nb-gradle/
### SublimeText ###
# cache files for sublime text
*.tmlanguage.cache
*.tmPreferences.cache
*.stTheme.cache
# workspace files are user-specific
*.sublime-workspace
# project files should be checked into the repository, unless a significant
# proportion of contributors will probably not be using SublimeText
# *.sublime-project
# sftp configuration file
sftp-config.json
### Vim ###
# swap
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
# session
Session.vim
# temporary
.netrwhist
*~
# auto-generated tag files
tags
### VS Code ###
.vscode/
### YOHO ###
dist
public/css/*
public/bundle/*
.eslintcache
*.log.*
nbproject/*
.DS_Store
.devhost
... ...
**/css/**/*.css
**/dist/**/*.css
**/scss/activity/_aslider.css
... ...
... ... @@ -8,9 +8,9 @@
const config = require('./config/common');
// use one apm
if (config.useOneapm) {
require('oneapm');
}
// if (config.useOneapm) {
// require('oneapm');
// }
const express = require('express');
const path = require('path');
... ... @@ -86,13 +86,17 @@ try {
const setYohoData = require('./doraemon/middleware/set-yoho-data');
const errorHanlder = require('./doraemon/middleware/error-handler');
const setPageInfo = require('./doraemon/middleware/set-pageinfo');
const devtools = require('./doraemon/middleware/devtools');
// YOHO 前置中间件
app.use(setYohoData());
app.use(user());
app.use(setPageInfo());
if (app.locals.devEnv) {
app.use(devtools());
}
require('./dispatch')(app);
app.all('*', errorHanlder.notFound()); // 404
... ...
... ... @@ -61,7 +61,8 @@ exports.getCoupon = (req, res, next) => {
// 获取信息成功
couponData.ordercode = ordercode;
}
console.log(couponData);
// console.log(couponData);
res.json({
result: couponData
});
... ... @@ -94,7 +95,8 @@ exports.verify = (req, res, next) => {
// 获取信息成功
couponData.ordercode = ordercode;
}
console.log(couponData);
// console.log(couponData);
res.json({
result: couponData
});
... ...
/* eslint no-unused-vars: ["error", {"args": "none"}] */
'use strict';
const model = require('../models/live');
const status = {
wait: 0,
living: 1,
end: 2
};
const liveModel = require('../models/live');
exports.index = (req, res, next) => {
liveModel.getAllList().then(result => {
res.render('live/entry', {
title: '直播列表',
module: 'activity',
page: 'entry',
best: result[0],
living: result[1],
pre: result[2],
record: result[3],
content: result[4],
isApp: req.yoho.isApp
});
}).catch(next);
};
exports.main = (req, res, next) => {
const isReplay = /^\/live\/replay\//.test(req.path);
const id = req.params.id;
res.locals.liveRoom = id;
res.locals.module = 'activity';
res.locals.page = 'live-play';
model.fetchInfo(id, isReplay)
.then(result => {
if (!result.data) {
return next();
}
res.render('live/play', result.data);
})
.catch(next);
};
exports.barrage = (req, res, next) => {
const type = req.query.type;
model.getBarrageHost(type)
.then(result => {
if (result.code !== 200) {
next(result.message);
return;
}
res.json(result);
})
.catch(next);
};
exports.replayBarrage = (req, res, next) => {
const id = req.query.id;
const startTime = req.query.startTime;
const timeInterval = req.query.timeInterval;
model.getReplyBarrage(id, startTime, timeInterval)
.then(result => {
res.json(result);
})
.catch(next);
};
... ...
'use strict';
const serviceApi = global.yoho.ServiceAPI;
const api = global.yoho.API;
const helpers = global.yoho.helpers;
const crypto = global.yoho.crypto;
const queryString = require('querystring');
const Promise = require('bluebird');
const co = Promise.coroutine;
const headerModel = require('../../../doraemon/models/header'); // 头部model
const _ = require('lodash');
const getResource = code => {
return serviceApi.get('operations/api/v5/resource/get', {
content_code: code,
platform: 'iphone'
});
};
const vip = (limit) => {
return api.get('', {
method: 'app.student.vip',
limit: limit || 60
});
};
const verifiedStudentTotal = () => {
return api.get('', {
method: 'app.student.verifiedStudentTotal'
});
};
const getProvince = () => {
return api.get('', {
method: 'app.studentMarket.getAddressList'
}, {
cache: true
});
};
const getSchool = code => {
return api.get('', {
method: 'app.studentMarket.getSchoolInfoList',
areaCode: code
});
};
const getEducationLevelList = () => {
return api.get('', {
method: 'app.studentMarket.getEducationLevelList'
});
};
const userAcquireStatus = (uid, couponIds) => {
return api.get('', {
method: 'app.coupons.userAcquireStatus',
uid: uid,
couponIds: couponIds
});
};
const verifyStudent = (uid, collegename, educationdegree, enrollmentyear) => {
return api.get('', {
method: 'app.student.verifyStudent',
uid: uid,
client_type: 'h5',
college_name: collegename,
enrollment_year: enrollmentyear,
education_degree: educationdegree
});
};
const verifyIdentity = (uid, certno, name, pageurl) => {
return api.get('', {
method: 'app.student.verifyIdentity',
uid: uid,
client_type: 'h5',
cert_no: certno,
name: name,
page_url: pageurl
});
};
const getUser = (uid) => {
if (!uid) {
return Promise.resolve({
code: 200,
data: {}
});
}
return api.get('', {
method: 'app.passport.profile',
uid: uid
}, {
cache: true
});
};
/* 获取用户或者环境相关数据*/
const getPlatForm = (req) => {
let userAgent = req.get('User-Agent');
let yoho = {};
let uids = req.get('User-Agent').match(/uid=([^;]+)/i);
let versions = req.get('User-Agent').match(/app_version=([^;]+)/i);
let arrs = [];
let isNewVersion = false;
const isProduction = process.env.NODE_ENV === 'production';
// console.log(req.get('User-Agent'));
// console.log(req.query.uid);
yoho.isiOS = /\(i[^;]+;( U;)? CPU.+Mac OS X/i.test(userAgent);
yoho.isAndroid = /Android/i.test(userAgent);
yoho.isApp = /YohoBuy/i.test(req.get('User-Agent')) || (req.query.app_version && req.query.client_type);
yoho.app_version = versions && versions.length === 2 ? versions[1] : '';
yoho.app_version = yoho.app_version || req.query.app_version || '';
if (yoho.app_version) {
yoho.app_version = _.toString(yoho.app_version);
arrs = yoho.app_version.split('.');
if (arrs.length > 2) {
if (arrs[0] && +arrs[0] < 4) {
isNewVersion = false;
} else if (arrs[1] && +arrs[1] < 9) {
isNewVersion = false;
} else if (arrs[2] && +arrs[2] <= 0) {
isNewVersion = false;
} else {
isNewVersion = true;
}
}
}
yoho.isSupportStudent = !yoho.isApp || isNewVersion;
yoho.http = 'http:';
if (isProduction) {
yoho.http = 'https:';
}
yoho.uid = uids && uids.length === 2 ? uids[1] : ''; // 8041246
yoho.uid = req.user.uid || yoho.uid || req.query.uid || '';
yoho.isLogin = yoho.uid ? true : false;
return co(function*() {
let data = yield getUser(yoho.uid);
yoho.isStudent = data.data && data.data.vip_info && data.data.vip_info.is_student ? true : false;
// yoho.isStudent = false;
// console.log(yoho);
return yoho;
})();
};
exports.index = (req, res, next) => {
let code = 'a83b7d55324fb65f96c1f85a3387ebd8';
let uid = req.__USER__.uid;
let options;
let noLoginUrl = helpers.urlFormat('/activity/student/register') + '?openby:yohobuy={"action":"go.weblogin","params":{"jumpurl":{"url":"' + req.__USER__.http + '//m.yohobuy.com/activity/student"}}}';
Promise.all([getResource(code), vip()]).then(datas => {
let coupons,
activities,
banner,
icons,
link,
// url,
// param,
couponids = [];
(datas[0].data || []).forEach((item) => {
switch (item.template_name) {
case 'getCoupon':
coupons = item;
break;
case 'image_list':
activities = item;
break;
case 'focus':
banner = item;
break;
case 'recommend_content_five':
icons = item;
break;
case 'link':
link = item;
break;
default:
// other = item;
break;
}
});
if (coupons && coupons.data) {
coupons.link = link && link.data ? link.data[0].url + (req.__USER__.isApp ? '&app_version=' + req.__USER__.app_version : '') : '';
coupons.data = (coupons.data || []).map((item) => {
// let url = item.image.url;
couponids.push(item.couponID);
// if (!req.__USER__.isLogin) {
// url = 'http://m.yohobuy.com/activity/student/register?openby:yohobuy={"action":"go.weblogin","params":{"jumpurl":{"url":"http://m.yohobuy.com/activity/student"}}}';
// }
//
// item.image.noLoginUrl=url;
return item;
});
}
return userAcquireStatus(uid, couponids.join(',')).then((cous) => {
coupons.data = (coupons.data || []).map((item)=>{
item.status = 1;
(cous.data || []).forEach((it) => {
if (+it.couponId === +item.couponID) {
item.hasNum = it.hasNum;
item.status = it.status;
}
});
item.couponID = crypto.encryption('yoho9646abcdefgh', item.couponID);
return item;
});
datas[1].data = datas[1].data || {};
datas[1].data.product_list = (datas[1].data.product_list || []).map(function(value) {
value.goodsId = value.goods_list[0].goods_id;
value.url = helpers.urlFormat(`/product/pro_${value.product_id}_${value.goodsId}/${value.cn_alphabet}.html`) + `?openby:yohobuy={"action":"go.productDetail","params":{"product_skn":${value.product_skn}}}`;
return value;
});
options = {
isApp: req.__USER__.isApp,
goods: datas[1].data.product_list,
banner: banner,
icons: icons,
coupons: coupons,
activities: activities,
isStudent: req.__USER__.isStudent,
isSupportStudent: req.__USER__.isSupportStudent,
isLogin: req.__USER__.isLogin,
title: '有货学生专享优惠',
http: req.__USER__.http,
uid: req.__USER__.uid,
app_version: req.__USER__.app_version,
isAppNoLogin: req.__USER__.isApp && !req.__USER__.isLogin,
noLoginUrl: noLoginUrl
};
if (!req.__USER__.isApp) {
options.pageHeader = headerModel.setNav({
navTitle: options.title,
navBtn: true
});
}
options.loginUrl = '//m.yohobuy.com/activity/student/register';
if (options.isApp) {
if (options.isLogin) {
if (options.isStudent) {
options.loginUrl = false;
} else {
options.loginUrl = options.loginUrl + '?openby:yohobuy={"action":"go.h5","params":{"islogin":"N","url":"' + req.__USER__.http + '//m.yohobuy.com/activity/student/register"}}';
}
} else {
// no login
options.loginUrl = options.loginUrl + '?openby:yohobuy={"action":"go.weblogin","params":{"jumpurl":{"url":"' + req.__USER__.http + '//m.yohobuy.com/activity/student/register","antarget":"1"}}}';
}
} else {
if (options.isLogin) {
if (options.isStudent) {
options.loginUrl = false;
}
}
}
// console.log(options);
res.render('student', options);
});
}).catch(next);
};
exports.province = (req, res, next) => {
getProvince().then((result) => {
res.json(result);
}).catch(next);
};
exports.register = (req, res, next) => {
let years = [],
refer;
for (let i = 0; i < 8; i++) {
years.push((new Date()).getFullYear() - i);
}
Promise.all([verifiedStudentTotal(), getEducationLevelList()]).then((arr) => {
if (req.__USER__.isStudent) {
refer = '/activity/student?uid=' + req.__USER__.uid;// 所有认证过的,都跳转学生首页
res.redirect(helpers.urlFormat(refer));
} else {
res.render('register', {
title: '认证信息填写',
isApp: req.__USER__.isApp,
count: arr[0].data,
educations: arr[1].data,
educationsStr: JSON.stringify(arr[1].data),
years: years,
yearsStr: JSON.stringify(years),
module: 'activity',
page: 'register',
http: req.__USER__.http
});
}
}).catch(next);
};
exports.school = (req, res, next) => {
let code = req.query.code;
getSchool(code).then((result) => {
res.json(result);
}).catch(next);
};
exports.verifyidentity = (req, res, next) => {
// let uid=req.user.id;
let params = req.body;
let url = 'http://m.yohobuy.com/activity/student/verify?' +
queryString.stringify({
college_name: params.college_name,
education_degree: params.education_degree,
enrollment_year: params.enrollment_year
}) + '&';
let uid = req.__USER__.uid;
verifyIdentity(uid, params.cert_no, params.name, url).then((result) => {
res.json(result);
}).catch(next);
};
exports.verifystudent = (req, res, next) => {
let params = req.query;
let uid = req.__USER__.uid;
Promise.all([verifiedStudentTotal(), vip(), verifyStudent(uid, params.college_name, params.education_degree, params.enrollment_year, params.token)])
.then((datas) => {
let isverify = false,
prompt = '您的学校信息未通过审核';
if (datas[2].code === 200) {
if (datas[2].data.isStudent === 1) {
isverify = true;
prompt = datas[2].data.prompt;
}
} else {
if (datas[2].code === 500) {
prompt = '芝麻信用认证失败,稍后重试!';
} else {
prompt = datas[2].message;
}
}
datas[1].data.product_list = datas[1].data.product_list.map(function(value) {
value.goodsId = value.goods_list[0].goods_id;
value.url = helpers.urlFormat(`/product/pro_${value.product_id}_${value.goodsId}/${value.cn_alphabet}.html`) + `?openby:yohobuy={"action":"go.productDetail","params":{"product_skn":${value.product_skn}}}`;
return value;
});
return getUser(uid).then((user) => {
res.render('verify', {
isApp: req.__USER__.isApp,
count: datas[0].data,
goods: datas[1].data.product_list,
isverify: isverify,
prompt: prompt,
isLogin: user.data && user.data.vip_info && user.data.vip_info.is_student ? true : false,
title: '学生身份认证',
http: req.__USER__.http,
uid: req.__USER__.uid,
app_version: req.__USER__.app_version
});
}).catch(next);
}).catch(next);
};
exports.detail = (req, res) => {
let type = req.params.type,
options;
if (type === 'renzhen') {
options = {
title: '认证协议',
isRenZhen: true
};
} else {
options = {
title: '更多学生权益',
isQuanYi: true
};
}
options.isApp = req.__USER__.isApp;
res.render('detail', options);
};
exports.isLogin = (req, res, next) => {
// let refer = req.cookies.refer;
let url = req.get('referer') || '/activity/student/register';
getPlatForm(req).then((yoho)=>{
if (yoho.uid) {
req.__USER__ = yoho;
next();
return;
}
// refer = decodeURI(req.cookies.refer)||req.get("refer");
//
// if (refer) {
// refer = decodeURI(req.cookies.refer)||req.get("refer");
// } else {
// refer = '/activity/student/register';
// }
if (req.path === '/student/register' && !yoho.isStudent) {
url = '/activity/student/register';
}
res.redirect(helpers.urlFormat('/signin.html', {
refer: url
}));
}).catch(next);
};
exports.getUser = (req, res, next)=>{
getPlatForm(req).then((yoho)=>{
req.__USER__ = yoho;
next();
}).catch(next);
};
... ...
... ... @@ -322,23 +322,23 @@ const getWxUserInfo = (params) => {
gzip: true,
timeout: 3000
})
.then((result) => {
if (_.isEmpty(result.openid)) {
return false;
}
url2 = url2 + '&access_token=' + result.access_token +
.then((result) => {
if (_.isEmpty(result.openid)) {
return false;
}
url2 = url2 + '&access_token=' + result.access_token +
'&openid=' + result.openid;
return api._requestFromAPI({
url: url2,
qs: {},
json: true,
gzip: true,
timeout: 3000
})
.then((result2) => {
return result2;
return api._requestFromAPI({
url: url2,
qs: {},
json: true,
gzip: true,
timeout: 3000
})
.then((result2) => {
return result2;
});
});
});
};
module.exports = {
... ...
'use strict';
const moment = require('moment');
const service = global.yoho.ServiceAPI;
const liveAPI = global.yoho.LiveAPI;
const contentCodeConfig = require('../../../config/content-code');
const resourcesProcess = require(`${global.utils}/resources-process`);
const helpers = global.yoho.helpers;
const _formatTime = (timestamp, b) => {
let date = b ? 'M.D ' : 'M月D日';
let time = 'HH:mm';
let startTime = moment(timestamp);
let now = moment();
let diff = moment.duration(startTime.clone().startOf('day') - now.startOf('day')).days();
switch (diff) {
case 0:
date = '[今天]';
break;
case 1:
date = '[明日]';
break;
default:
null;
}
return startTime.format(`${date}${time}`);
};
/**
* 根据 时长(秒) 返回 时长格式化后的 字符串 HH:mm:ss
*/
const _getHumanDuration = (duration) => {
duration = moment.duration(duration, 's');
let durationH = duration.hours();
let durationM = duration.minutes();
let durationS = duration.seconds();
duration = [durationH, durationM, durationS].map((item) => {
if (item < 10) {
return `0${item}`;
} else {
return String(item);
}
});
return `${duration[0]}:${duration[1]}:${duration[2]}`;
};
// 获取顶部bannel
let _getBannerData = () => {
return service.get('operations/api/v5/resource/get', {
content_code: contentCodeConfig.live.index,
platform: 'iphone'
}, {
code: 200,
cache: true
}).then((result) => {
return result && result.data ? resourcesProcess(result.data) : [];
});
};
// 获取精选视频
const _getBestList = () => {
return liveAPI.get('v1/living/best', {}, {
code: 200,
cache: false
}).then(result => {
let list = result && result.data || [];
if (result && result.data && result.data.length !== 2) {
result.data = [];
}
for (let item of list) {
switch (item.living) {
case 0:
item.pre_living = true;
break;
case 1:
default:
item.now_living = true;
break;
case 2:
// 直播结束不显示
result.data = [];
break;
}
// 格式化时间
item.starting_time = _formatTime(item.starting_time * 1000, true);
}
return result && result.data || [];
});
};
// 获取直播中所有视频
const _getLivingList = () => {
return liveAPI.get('v1/living/listing', {}, {
code: 200,
cache: false
}).then(result => {
return result && result.data || [];
});
};
// 获取直播预告列表
const _getPrelivingList = () => {
return liveAPI.get('v1/living/starting', {}, {
code: 200,
cache: false
}).then(result => {
let list = result && result.data || [];
for (let item of list) {
item.starting_time = _formatTime(item.starting_time * 1000);
}
return result && result.data || [];
});
};
// 获取回看列表
const _getRecordList = () => {
return liveAPI.get('v1/living/replaying', {}, {
code: 200,
cache: false
}).then(result => {
return result && result.data || [];
});
};
// 返回所有数据
const getAllList = () => {
return Promise.all([_getBestList(), _getLivingList(), _getPrelivingList(), _getRecordList(), _getBannerData()]);
};
// 获取 回放视屏 信息
const fetchReplayInfo = (videoID) => {
let url = 'v1/living/detail';
let data = { video_id: videoID };
let options = { cache: true };
return liveAPI.get(url, data, options)
.then(result => {
if (result && result.data) {
let d = result.data;
d.background = helpers.image(d.background, 640, 968);
d.pic = helpers.image(d.pic, 640, 968);
d.master_pic = helpers.image(d.master_pic, 180, 180);
d.humanTime = _formatTime(data.live_start_time * 1000);
d.video_src = d.url;
// 自定义数据
d.duration = '00:00:00'; // 回看时长 前端JS根据video获取
d.living = 3; // 重播 状态
d.canPlay = true;
d.atEnd = false;
d.isReplay = true;
}
return result || {};
});
};
// 获取 直播视屏 信息
const fetchLiveInfo = (roomID) => {
let url = 'v1/living/detail';
let data = { room_id: roomID };
return liveAPI.get(url, data)
.then(result => {
if (result && result.data) {
let d = result.data;
d.background_pic = helpers.image(d.background_pic, 640, 968);
d.pic = helpers.image(d.pic, 640, 968);
d.master_pic = helpers.image(d.master_pic, 180, 180);
d.humanTime = _formatTime(d.starting_time * 1000); // 预告 开始时间
d.video_src = d.hls_downstream_address;
// 自定义数据
d.duration = _getHumanDuration(d.live_last_time);
d.canPlay = d.living === 1;
d.notBegin = d.living === 0;
d.atEnd = d.living === 2;
}
return result || {};
});
};
const fetchInfo = (id, isReplay) => {
if (isReplay) {
return fetchReplayInfo(id);
} else {
return fetchLiveInfo(id);
}
};
// 获取 直播 弹幕 host
const getBarrageHost = (type) => {
return liveAPI.get('v1/system/gethosts', { type });
};
const getReplyBarrage = (videoID, startTime, timeInterval) => {
const url = 'v1/living/getreplaybarrage';
const data = {
startTime,
timeInterval,
video_id: videoID
};
const options = { cache: true };
return liveAPI.get(url, data, options);
};
// 处理直播时间
module.exports = {
getAllList,
fetchInfo,
getBarrageHost,
getReplyBarrage
};
... ...
... ... @@ -11,6 +11,8 @@ const cRoot = './controllers';
const coupon = require(`${cRoot}/coupon`);
const wechat = require(`${cRoot}/wechat`);
const student = require(`${cRoot}/student`);
const live = require(`${cRoot}/live`);
const invite = require(`${cRoot}/invite`);
// routers
... ... @@ -23,6 +25,28 @@ router.get('/coupon/verify', coupon.verify);
router.get('/wechat/share', wechat.wechatShare);
router.get('/student', student.getUser, student.index);
router.get('/student/register', student.isLogin, student.register);
router.get('/student/province', student.province);
router.get('/student/school', student.school);
router.post('/student/join', student.isLogin, student.verifyidentity);
router.get('/student/verify', student.isLogin, student.verifystudent);
router.get('/student/detail/:type', student.getUser, student.detail);
// router.get('/student/getCoupons',student.getCoupons)
router.get('/live', live.index);
router.get('/live/barrage', live.barrage);
router.get('/live/replay/barrage', live.replayBarrage);
router.get('/live/replay/:id', live.main);
router.get('/live/:id', live.main);
router.get('/invite', invite.index);
router.get('/invite/index', invite.index);
... ... @@ -37,4 +61,5 @@ router.get('/invite/getwxinfo', invite.getwxinfo);
router.get('/invite/shareover', invite.shareover);
router.get('/invite/over', invite.over);
module.exports = router;
... ...
{{#unless isApp}}
<header class="yoho-header">
<a href="javascript:history.go(-1);" class="iconfont nav-back close"></a>
<p class="nav-title">{{title}}</p>
</header>
{{/unless}}
{{#isQuanYi}}
<div class="mobile-container">
<div class="mobile-wrap yoho-page student">
<section class='s-text'>
<h6>权益1:新品立享9折</h6>
<p>1、学生购买原价新品时,可立即享受9折优惠,与VIP折扣不可同时享受。</p>
<h6>权益2:每1元返1个有货币</h6>
<p>1、学生购买商品时,以商品的实际成交金额计算,每1元返1个有货币;</p>
<p>2、返有货币时间:确认收货7日后,系统将自动将对应数量的有货币返至购买账户;</p>
<p>3、有货币有效期:获得当日至次年12月31日,逾期自动作废;</p>
<p>4、查看有货币:登录后,点击“个人中心”在“我的有货币”中可以查看有货币余额及明细。</p>
<h6>权益3:免单抽奖</h6>
<p>1、每月将在累计购物金额最高的学校中,抽取3名幸运学生用户,获得免单资格;</p>
<p>2、免单用户名单将在每月第1个工作日公布在有货微信公众号上,可关注【有货YOHOBUY】;</p>
<p>3、免单用户将在中奖次日获得与实付金额等额的现金券,使用时间:中奖当月。</p>
<h6>权益4:学生专享活动</h6>
<p>1、每月不定期的开展学生专享活动,可打开有货APP推送,及时查收学生专享活动通知。</p>
<h6></h6>
</section>
</div>
</div>
{{/isQuanYi}}
{{#isRenZhen}}
<div class="mobile-container">
<div class="mobile-wrap yoho-page student">
<section class='s-text'>
<h6>认证协议</h6>
<p>1、全日制大学及硕士博士研究生</p>
<p>2、学校在可选范围内,有部分学校可能暂未收录,后期会尽快添加</p>
<p>3、每个学号只能认证一个有货账户</p>
</section>
</div>
</div>
{{/isRenZhen}}
... ...
... ... @@ -77,4 +77,4 @@
<div class="share-tag"><img src="//static.yohobuy.com/m/v1/img/invite/yd_share.png"></div>
</div>
</div><!--/invite-page-->
\ No newline at end of file
</div><!--/invite-page-->
... ...
<div class="yoho-live yoho-page">
{{! 导航条}}
{{#unless isApp}}
<div class="home-header clearfix yoho-header">
<a href="javascript:history.go(-1);" class="iconfont nav-back buriedpoint" data-bp-id="page_header_back_0"></a>
<p class="nav-title">直播列表</p>
</div>
{{/unless}}
{{#content}}
{{! 头部banner}}
{{#if focus}}
{{> resources/banner-top}}
{{/if}}
{{/content}}
{{#if content}}
{{#if best}}
<div class="head_margin"></div>
{{/if}}
{{/if}}
{{! 精选房间}}
{{#if best}}
<div class="liverec">
{{#best}}
<div class="liverec_child">
<a href='http://m.yohobuy.com/activity/live/{{room_id}}?openby:yohobuy={"action":"go.videolive", "params":{"type":"{{living}}","room":"{{room_id}}","bgpic":"{{pic}}"}}'>
<img class="liverec_pic" src="{{image pic 320 320}}" alt="直播预览">
{{#if now_living}}
<p class="living">直播</p>
{{else if pre_living}}
<p class="pre-living">预告 {{starting_time}}</p>
{{/if}}
</a>
<div class="liverec_info">
<img class="liverec_head" src="{{image master_pic 120 120}}" alt="头像">
<div class="liverec_pannel">
<p class="liverec_name clearfix">
<span class="name-name">{{master_name}}</span>
<span class="name-tag">{{master_meta}}</span>
</p>
<p class="liverec_tag">{{title}}</p>
</div>
</div>
</div>
{{/best}}
</div>
{{/if}}
{{! 直播中房间}}
{{#if living}}
<h2 class="living_title">直播中</h2>
{{/if}}
{{#living}}
<div class="liveliving">
<header>
<img class="main-head" src="{{image master_pic 120 120}}" alt="头像">
<div class="header-info">
<p class="main-name">{{master_name}}</p>
<p class="main-tag">{{master_meta}}</p>
</div>
</header>
<section>
<a href='http://m.yohobuy.com/activity/live/{{room_id}}?openby:yohobuy={"action":"go.videolive", "params":{"type":"1","room":"{{room_id}}","bgpic":"{{pic}}"}}'>
<img class="main-bg" src="{{image pic 640 640}}" alt="正在直播">
<p class="main-living">直播</p>
<p class="main-intro">{{title}}</p>
<div class="main-people">
<span class="people-icon"></span>
<p class="people-sum">{{audience_num}}人观看</p>
</div>
</a>
</section>
</div>
{{/living}}
{{! 直播预告列表}}
{{#if pre}}
<div class="live-list">
<h2 class="title">直播预告</h2>
<ul class="list">
{{#pre}}
<li class="pre-list">
<a href='http://m.yohobuy.com/activity/live/{{room_id}}?openby:yohobuy={"action":"go.videolive", "params":{"type":"0","room":"{{room_id}}","bgpic":"{{pic}}"}}'>
<img class="pre-pic" src="{{image pic 150 150}}" alt="直播预览图">
<p class="pre-icon">预告</p>
<p class="pre-time">{{starting_time}}</p>
<div class="pre-pannel">
<p class="pre-title text-overflow">{{title}}</p>
<p class="pre-cast">主播:{{master_name}}</p>
</div>
</a>
</li>
{{/pre}}
</ul>
</div>
{{/if}}
{{! 精彩回看}}
{{#if record}}
<h2 class="living_title">精彩回看</h2>
{{/if}}
{{#record}}
<div class="liveliving">
<header>
<img class="main-head" src="{{image master_pic 120 120}}" alt="头像">
<div class="header-info">
<p class="main-name">{{master_name}}</p>
<p class="main-tag">{{master_meta}}</p>
</div>
</header>
<section>
<a href='http://m.yohobuy.com/activity/live/replay/{{video_id}}?openby:yohobuy={"action":"go.videoreplay", "params":{"videoid":"{{video_id}}","bgpic":"{{pic}}"}}'>
<div class="record-icon"></div>
<img class="main-bg" src="{{image pic 640 640}}" alt="精彩回放">
<p class="main-living">回放</p>
<p class="main-intro">{{title}}</p>
<div class="main-people">
<span class="eye-icon"></span>
<p class="people-sum">{{audience_num}}人看过</p>
</div>
</a>
</section>
</div>
{{/record}}
</div>
... ...
{{! 直播 播放页 }}
<div class="live-wrapper">
{{#canPlay}}
<div class="live-main">
<!-- 视频部分start-->
<!--http://live-hls-pili.1iptv.com/meipai-live/57651bb975b6255acc01444c.m3u8-->
<section id="live_container" class="live-video-main" style="background-image: url('{{pic}}');">
<div id="video_container" class="video_player" data-video="{{video_src}}">
</div>
<div class="live-loading-container">
<div class="live-video-loading">
<div class="img"></div>
<p>加载中</p>
</div>
<div class="live-loading-cover" style="background-image: url('{{pic}}');"></div>
</div>
<div id="live_touch_layer"></div>
<!--弹幕-->
<div class="live-chat-pannel">
<ul id="live_chat_ul">
</ul>
</div>
<!--点赞-->
<div class="live-like-pannel">
<div id="live_like_pannel" class="animate_pannel"></div>
<div class="like-main"></div>
<span id="like_num">0</span>
</div>
<!--播放按钮-->
<div class="live-video-play-button">
<a href="javascript:void(0)">
<div class="img"></div>
</a>
</div>
<!--直播状态-->
<div class="live-status">
<div class="overflow-hidden">
<div class="img"></div>
<div class="live-time">
{{#if isReplay}}
<span>Yoho!Buy回看</span>
{{else}}
<span>Yoho!Buy直播</span>&nbsp;<span id="live_time"></span>
{{/if}}
</div>
<div class="live-num">
{{#if isRelay}}
<span>{{audience_num}}人观看</span>
{{else}}
<span></span>
{{/if}}
</div>
</div>
<div class="title hide" id="live-watermark">
{{#if watemark }}
<span>{{watermark}}</span>
{{/if}}
</div>
</div>
<a href="javascript:;" class="live-btn-share">
<i class="iconfont">&#xe600</i>
</a>
<a href="javascript: history.back();" class="live-btn-close">
<i class="iconfont">&#xe623</i>
</a>
</section>
</div>
{{/canPlay}}
{{!直播已结束}}
<div id="live-state-end" class="live-state is-no-start {{#atEnd}}show{{/atEnd}}">
<div class="live-state-inner" style="background-image: url('{{background_pic}}');">
<div class="live-state__txt">直播已结束</div>
<ul class="live-state-info">
<li class="audience text-center">
<span class="val">{{audience_num}}</span>
<br>
<span class="label">总观看人数</span>
</li>
<li class="duration">
<div class="inner pull-right">
<span class="val">{{duration}}</span>
<br>
<span class="label">直播时长</span>
</div>
</li>
<li class="favorite">
<div class="inner pull-left">
<span class="val">{{like_num}}</span>
<br>
<span class="label">点赞数</span>
</div>
</li>
</ul>
<a href="javascript: history.back();" class="live-btn-close">
<i class="iconfont">&#xe623</i>
</a>
</div>
</div>
{{!直播未开始}}
{{#notBegin}}
<div class="live-state is-no-start show">
<div class="live-state-inner" style="background-image: url('{{background_pic}}');">
<div class="live-state__txt">直播未开始</div>
<div class="live-state-info text-center">
<img src="{{master_pic}}" alt="" class="avatar">
<span class="name text-overflow">{{master_name}}</span><br>
<h5 class="title">直播主题: {{title}}</h5>
<p class="begin-time">开始时间: {{humanTime}}</p>
</div>
<a href="javascript: history.back();" class="live-btn-close">
<i class="iconfont">&#xe623</i>
</a>
</div>
</div>
{{/notBegin}}
{{! footer}}
<div class="float-layer" id="float-layer-app">
<div class="float-layer-left">
<span class="yoho-icon iconfont">&#xe60d;</span>
<p>新用户送惊喜礼包</p>
</div>
<a href="javascript:void(0);" id="float-layer-close" >
<i class="close-icon iconfont">&#xe623;</i>
<div class="circle-rightbottom"></div>
</a>
<a href="http://a.app.qq.com/o/simple.jsp?pkgname=com.yoho" id="float-layer-btn">
立即下载
</a>
</div>
</div>
<script>
var live_start_time = {{live_start_time}};//该直播开始时间
var live_type = {{living}};//是否是直播 0直播未开始,1直播中,2直播结束,3重播
var live_room = {{liveRoom}};//房间id,资讯id
var replay_total_likes = {{like_num}};//重播总计点赞数,取的是直播最终点赞数
var replay_user_nums = {{audience_num}};//重播观看人数,取的是直播时最终观看人数
var site_url = '';
var site_domain = 'http://api.live.yoho.cn/';
// share data
var shareTitle = '{{share_title}}';
var shareContent = '{{share_content}}';
var sharePic = '{{pic}}'
</script>
\ No newline at end of file
... ...
{{! 直播 播放页: 状态页}}
\ No newline at end of file
... ...
{{#unless isApp}}
<header class="yoho-header">
<a href="javascript:history.go(-1);" class="iconfont nav-back"></a>
<p class="nav-title">认证信息填写</p>
</header>
{{/unless}}
<div class="mobile-container">
<div class="mobile-wrap yoho-page student">
<div style=" position: fixed;background: #f0f0f0;height: 100%;width: 100%;">
<section class="s-feild s-marbot">
全国已有<span class="s-red">{{count}}</span>位学生完成认证
</section>
<div class="s-form">
<section class="s-feild">
<label>真实姓名</label><input type="text" name="tb_name" id="tb_name" placeholder="请输入您的真实姓名" >
</section>
<section class="s-feild">
<label>身份证号</label><input type="text" id="tb_cert_no" name="tb_cert_no" placeholder="请输入您身份证号码" maxlength="18">
</section>
<section class="s-feild" data-aslider-in="province|fade">
<label>学校省份</label><input type="text" id="s-province-tb" name="s-province-tb" readonly="readonly" placeholder="请选择省份"><span class="s-select iconfont" >&#xe604;</span>
</section>
<section class="s-feild" data-aslider-in="school|fade" aslider-isShow="false">
<label>学校名称</label><input type="text" id="s-school-tb" name="s-school-tb" readonly="readonly" placeholder="请选择您的所在学校"><span id='s-toast-school' class="s-select iconfont">&#xe604;</span>
</section>
<section class="s-feild">
<a {{#if isApp}} href='//m.yohobuy.com/activity/student/register?openby:yohobuy={"action":"go.picker","type":"1","params":{"title":"选择学历","list":{{educationsStr}} }}'{{else}} data-aslider-in="education|fade" href="javascript:void(0)"{{/if}}><label>当前学历</label><input type="text" id="s-education-tb" name="s-education-tb" readonly="readonly" placeholder="请选择您的学历"><span class="s-select iconfont">&#xe604;</span></a>
</section>
<section class="s-feild">
<a {{#if isApp}} href='//m.yohobuy.com/activity/student/register?openby:yohobuy={"action":"go.picker","type":"2","params":{"title":"选择入学时间","list":{{yearsStr}} }}' {{else}} data-aslider-in="year|fade" href="javascript:void(0)" {{/if}}><label>入学年份</label><input type="text" id="s-year-tb" name="s-year-tb" readonly="readonly" placeholder="请选择您的入学年份"><span class="s-select iconfont">&#xe604;</span></a>
</section>
</div>
<div class="s-footer">
<section class="s-shenming">
<input type="checkbox" id="checkbox" class="regular-checkbox" style="visibility: hidden;">
<label class="checkbox icon-s-checked iconfont" for="checkbox">
</label>
同意<a class="s-blue" href='//m.yohobuy.com/activity/student/detail/renzhen?openby:yohobuy={"action":"go.h5","params":{"islogin":"N","url":"{{http}}//m.yohobuy.com/activity/student/detail/renzhen"}}'>Yoho!Buy有货学生认证协议</a>
</section>
<a class="s-submit" href="javascript:void(0);">去支付宝完成认证</a>
<p class="s-sub-tip">与蚂蚁金服旗下的芝麻信用(支付宝)合作认证</p>
</div>
</div>
</div>
</div>
<article class="aslider" data-aslider="province">
<section class="asilder_wrapper close">
<header class="s-layout-title">
<a href="javascript:void(0);" class="iconfont close">&#xe623;</a>
</header>
<div class="slider">
<div class="mobile-container">
<div class="mobile-wrap yoho-page student">
<section class='s-search'>
<span class="iconfont s-empty">&#xe60f;</span>
<div class='s-seach-tip'><span class="iconfont search-icon empty">&#xe60f;</span>搜索省份</div>
<input id='s-search-provinces' class='s-input-search'>
<span class="iconfont s-clear">&#xe623;</span>
</section>
<div class='s-items' id='s-provinces'>
</div>
</div>
</div>
</div>
<div class='s-group-zimu'></div>
</section>
</article>
<article class="aslider" data-aslider="school">
<section class="asilder_wrapper close">
<header class="s-layout-title">
<a href="javascript:void(0);" class="iconfont close">&#xe623;</a>
</header>
<div class="slider">
<div class="mobile-container">
<div class="mobile-wrap yoho-page student">
<section class='s-search'>
<span class="iconfont s-empty">&#xe60f;</span>
<div class='s-seach-tip'><span class="iconfont search-icon empty">&#xe60f;</span>搜索学校</div>
<input id='s-search-school' class='s-input-search'>
<span class="iconfont s-clear">&#xe623;</span>
</section>
<div class='s-items' id='s-school'>
</div>
</div>
</div>
</div>
<div class='s-group-zimu'></div>
</section>
</article>
<article class="aslider" data-aslider="education">
<section class="asilder_wrapper close">
<header class="s-layout-title">
<a href="javascript:void(0);" class="iconfont close">&#xe623;</a>
</header>
<div class="slider">
<div class="mobile-container">
<div class="mobile-wrap yoho-page student">
<div class='s-items' id='s-education'>
{{#each educations}}
<div class='s-item close'>
{{this}}
</div>
{{/each}}
</div>
</div>
</div>
</div>
</section>
</article>
<article class="aslider" data-aslider="year">
<section class="asilder_wrapper close">
<header class="s-layout-title">
<a href="javascript:void(0);" class="iconfont close">&#xe623;</a>
</header>
<div class="slider">
<div class="mobile-container">
<div class="mobile-wrap yoho-page student">
<div class='s-items' id='s-year'>
{{#each years}}
<div class='s-item close'>
{{this}}
</div>
{{/each}}
</div>
</div>
</div>
</div>
</section>
</article>
... ...
<div class="mobile-container">
<div class="mobile-wrap yoho-page student">
{{! 头部banner}}
{{#banner}}
{{> resources/banner-top}}
{{/banner}}
{{#icons}}
<section class='s-section clearfix' data-template-id="{{template_id}}">
<h1>学生权益<a class='more s-quan' href='//m.yohobuy.com/activity/student/detail/quanyi?openby:yohobuy={"action":"go.h5","params":{"islogin":"N","url":"{{../http}}//m.yohobuy.com/activity/student/detail/quanyi"}}'>更多权益</a></h1>
<div class='s-content'>
{{#each data.list}}
<div class='s-section'>
<a href="javascript:void(0)">
<img src='{{image src 320 149}}' title="{{title}}">
</a>
</div>
{{/each}}
</div>
</section>
{{/icons}}
{{#coupons}}
<section class='s-section clearfix' data-template-id="{{template_id}}">
<h1>领券中心
<a class='more iconfont' href="{{link}}">&#xe618;</a>
</h1>
<div class='s-coupon-contain'>
<style type="text/css">
.no-bg{
color: #fff!important;
background-image: none!important;
}
</style>
{{#each data}}
<div class="coupon-floor" coupon-id="{{couponID}}">
<div class="floor-main" style="background-image: url({{image image.src 0 0}});">
<a href='{{image.url}}' class="main-left"></a>
{{#isEqual status 1}}
{{#if @root.isAppNoLogin}}
<a href='{{@root.noLoginUrl}}' class="main-right-use" >
<span class="on-receive on-lingqu no-bg">
<p>点击</p>
<p>领取</p>
</span>
</a>
{{else}}
<div class="main-right-use" href='{{../image.url}}'>
<span class="on-receive on-lingqu no-bg">
<p>点击</p>
<p>领取</p>
</span>
</div>
{{/if}}
{{/isEqual}}
{{#isEqual status 2}}
<a href='{{../image.url}}' class="main-right-use" >
<span class="zero"></span>
</a>
{{/isEqual}}
{{#isEqual status 3}}
<a href='{{../image.url}}' class="main-right-use" >
<span class="received"></span>
</a>
{{/isEqual}}
{{#isEqual status 4}}
<a href='{{../image.url}}' class="main-right-use" >
<span class="on-receive no-bg" >
<p>已经</p>
<p>过期</p>
</span>
</a>
{{/isEqual}}
</div>
</div>
{{/each}}
</div>
</section>
{{/coupons}}
{{#activities}}
<section class='s-section clearfix' data-template-id="{{template_id}}">
<h1>学生专属活动</h1>
{{#each data.list}}
<a class='s-activity' href="{{url}}">
<img src='{{image src 750 234}}' title='{{title}}'>
</a>
{{/each}}
</section>
{{/activities}}
<section class='s-section clearfix'>
<h1>学生专享商品<a class="more iconfont" href="//search.m.yohobuy.com/?students=1&title=学生专享商品&uid={{uid}}{{#isApp}}&app_version={{@root.app_version}}{{/isApp}}?openby:yohobuy={'action':'go.h5','params':{'islogin':'N','url':'{{@root.http}}//search.m.yohobuy.com/','param':{'students':'1','title':'学生专享商品','uid':'{{uid}}'}}}">&#xe618;</a></h1>
<div class='goods-list clearfix'>
{{#each goods}}
<div class="good-info">
<div class="tag-container clearfix">
{{# tags}}
{{# isNew}}
<p class="good-tag new-tag">NEW</p>
{{/ isNew}}
{{# isAdvance}}
<p class="good-tag renew-tag">再到着</p>
{{/ isAdvance}}
{{# isDiscount}}
<p class="good-tag sale-tag">SALE</p>
{{/ isDiscount}}
{{# isYohoood}}
<p class="good-tag running-man-tag">跑男同款</p>
{{/ isYohoood}}
{{# isLimited}}
<p class="good-tag limit-tag">限量商品</p>
{{/ isLimited}}
{{/ tags}}
</div>
<div class="good-detail-img">
<a class="good-thumb" href="{{url}}">
<img class="lazy" data-original="{{image default_images 235 314}}">
</a>
</div>
<div class="good-detail-text">
<div class="name">
<a href="{{url}}">{{product_name}}</a>
</div>
<div class="price">
<span class="sale-price">¥{{round student_price}} <i class='s-biaoqian'></i></span>
<p class="s-price-block">
<span class="market-price">¥{{round sales_price}}</span>
</p>
</div>
</div>
</div>
{{/each}}
</div>
<a class='s-more' href="//search.m.yohobuy.com/?students=1&title=学生专享商品&uid={{uid}}{{#isApp}}&app_version={{@root.app_version}}{{/isApp}}?openby:yohobuy={'action':'go.h5','params':{'islogin':'N','url':'{{@root.http}}//search.m.yohobuy.com/','param':{'students':'1','title':'学生专享商品','uid':'{{uid}}'}}}">查看更多</a>
</section>
{{#loginUrl}}
<div class='s-layout'>
{{#if @root.isSupportStudent}}
快来认证吧,认证通过即可享受专属优惠!
{{else}}
请升级最新APP版本,完成认证
{{/if}}
{{#if @root.isSupportStudent}}
<a class='s-renzhen' href='{{.}}'>立即认证</a>
{{/if}}
</div>
<div class="s-replace"></div>
{{/loginUrl}}
</div>
</div>
... ...
{{#unless isApp}}
<header class="yoho-header">
<a href="javascript:history.go(-1);" class="iconfont nav-back"></a>
<p class="nav-title">学生身份认证</p>
</header>
{{/unless}}
<div class="mobile-container">
<div class="mobile-wrap yoho-page student">
{{#if isverify}}
<section class='s-section clearfix'>
<div class='s-verify-img s-verify-success'></div>
<p class='s-verify-title'>认证成功!{{./prompt}}</p>
<p class='s-verify-txt'>您是第<i class='red'>{{count}}</i>位认证的学生</p>
</section>
{{else}}
<section class='s-section clearfix'>
<div class='s-verify-img s-verify-fail'></div>
<p class='s-verify-title'>太遗憾了,{{./prompt}}</p>
<p class='s-verify-txt'>您可以<a class='red' href='//m.yohobuy.com/activity/student/register'>重新验证 ></a></p>
</section>
{{/if}}
<section class='s-section clearfix'>
<h1>学生专享商品<a class="more iconfont" href="//search.m.yohobuy.com/?students=1&title=学生专享商品&uid={{uid}}{{#isApp}}&app_version={{@root.app_version}}{{/isApp}}?openby:yohobuy={'action':'go.h5','params':{'islogin':'N','url':'{{@root.http}}//search.m.yohobuy.com/','param':{'students':'1','title':'学生专享商品','uid':'{{uid}}'}}}">&#xe618;</a></h1>
<div class='goods-list clearfix'>
{{#each goods}}
<div class="good-info">
<div class="tag-container clearfix">
{{# tags}}
{{# isNew}}
<p class="good-tag new-tag">NEW</p>
{{/ isNew}}
{{# isAdvance}}
<p class="good-tag renew-tag">再到着</p>
{{/ isAdvance}}
{{# isDiscount}}
<p class="good-tag sale-tag">SALE</p>
{{/ isDiscount}}
{{# isYohoood}}
<p class="good-tag running-man-tag">跑男同款</p>
{{/ isYohoood}}
{{# isLimited}}
<p class="good-tag limit-tag">限量商品</p>
{{/ isLimited}}
{{/ tags}}
</div>
<div class="good-detail-img">
<a class="good-thumb" href="{{url}}">
<img class="lazy" data-original="{{image default_images 235 314}}">
</a>
</div>
<div class="good-detail-text">
<div class="name">
<a href="{{url}}">{{product_name}}</a>
</div>
<div class="price">
<span class="sale-price">¥{{round student_price}} <i class='s-biaoqian'></i></span>
<p class="s-price-block">
<span class="market-price">¥{{round sales_price}}</span>
</p>
</div>
</div>
</div>
{{/each}}
</div>
<a class='s-more' href="//search.m.yohobuy.com/?students=1&title=学生专享商品{{#isApp}}&app_version={{@root.app_version}}{{/isApp}}">查看更多</a>
</section>
</div>
</div>
... ...
/**
*
* @author: jiangfeng<jeff.jiang@yoho.cn>
* @date: 16/8/22
*/
'use strict';
const http = require('http');
const logger = global.yoho.logger;
const env = process.env.NODE_ENV || 'development';
const hotfix = {
v1(req, res) {
let clientType = req.body.client_type || '';
let version = req.body.app_version || '';
let apiFile = `http://cdn.yoho.cn/app-hotfix2/${env}/yohobuy/`;
if (clientType.toLowerCase() === 'ios' && version) {
apiFile += `ios/${version}/api.json?_=` + (new Date()).getTime();
} else if (clientType.toLowerCase() === 'android' && version) {
apiFile += `android/${version}/api.json?_=` + (new Date()).getTime();
} else {
return res.json({
code: 400,
message: 'client_type or app_version error',
data: {}
});
}
http.get(apiFile, response => {
res.setHeader('Content-Type', 'application/json');
if (response.statusCode === 200) {
response.pipe(res);
} else {
return res.json({
code: 400,
message: 'client_type or app_version error',
data: {}
});
}
}).on('error', err => {
logger.error('hot fix v1 error:', err);
return res.json({
code: 400,
message: 'read api file fail',
data: {}
});
});
}
};
module.exports = hotfix;
... ...
/**
* some common api app
* @author: jiangfeng<jeff.jiang@yoho.cn>
* @date: 2016/08/22
*/
var express = require('express');
var app = express();
// set view engin
app.on('mount', function(parent) {
delete parent.locals.settings; // 不继承父 App 的设置
Object.assign(app.locals, parent.locals);
});
// router
app.use(require('./router'));
module.exports = app;
... ...
/**
* router of sub app coupon
* @author: lixia.zhang<lixia.zhang@yoho.cn>
* @date: 2016/05/31
*/
'use strict';
const router = require('express').Router(); // eslint-disable-line
const cRoot = './controllers';
const hotfix = require(`${cRoot}/hotfix`);
// routers
router.post('/hf/v1', hotfix.v1);
module.exports = router;
... ...
... ... @@ -60,4 +60,12 @@
{{#if seckill}}
{{> resources/seckill}}
{{/if}}
{{! 标题楼层}}
{{#if titleFloor}}
{{> resources/title-floor}}
{{/if}}
{{! 直播楼层}}
{{#if livePicture}}
{{> resources/live-picture}}
{{/if}}
{{/content}}
... ...
# 代码规范说明文档
开发前请务必仔细阅读,遵守规范,保持团队代码风格统一
## 文件命名
* 中划线分隔小写单词
* Ex: `your-file`
## 缩进
* 统一4个Space
* 建议将编辑器Tab映射成4个Space
## 注释
* 为每个你创建的JS文件添加注释
```
/**
* 对文件实现功能的描述
* @date: 2016-11-11
* @author: name<emial@yoho.cn>
*/
```
* 为重要的函数添加注释
```
/**
* 对函数功能的说明
* @params name paramType 参数描述
* @return name returnType 返回值描述
*/
```
* 为重要的代码、逻辑复杂的代码或者有特殊处理的代码添加注释
```
// Your comments for the code
```
* 减少不必要的注释
类似于:**进入循环****循环结束**等垃圾话的注释请谨慎添加,大家都是程序员,不用你注释也能知道的
## html
* 见名知意,不要有1,2,3这种名字出现
* class、id等属性命名为中划线分隔小写单词
* html中请不要出现不必要的嵌套以及不要将标签滥用,比如使用`a`标签作为不跳转的按钮的标签
* 属性按顺序出现:`id -> class -> name -> data-* -> src,for,type,href -> title,alt -> aria-*,role`
## js
[Link](doc/code-norm/js.md)
## css
# 代码规范说明文档
开发前请务必仔细阅读,遵守规范,保持团队代码风格统一
## 文件命名
* 中划线分隔小写单词
* Ex: `your-file`
## 缩进
* 统一4个Space
* 建议将编辑器Tab映射成4个Space
## 注释
* 为每个你创建的JS文件添加注释
```
/**
* 对文件实现功能的描述
* @date: 2016-11-11
* @author: name<emial@yoho.cn>
*/
```
* 为重要的函数添加注释
```
/**
* 对函数功能的说明
* @params name paramType 参数描述
* @return name returnType 返回值描述
*/
```
* 为重要的代码、逻辑复杂的代码或者有特殊处理的代码添加注释
```
// Your comments for the code
```
* 减少不必要的注释
类似于:**进入循环****循环结束**等垃圾话的注释请谨慎添加,大家都是程序员,不用你注释也能知道的
## html
* 见名知意,不要有1,2,3这种名字出现
* class、id等属性命名为中划线分隔小写单词
* html中请不要出现不必要的嵌套以及不要将标签滥用,比如使用`a`标签作为不跳转的按钮的标签
* 属性按顺序出现:`id -> class -> name -> data-* -> src,for,type,href -> title,alt -> aria-*,role`
## js
[Link](doc/code-norm/js.md)
## css
[Link](doc/code-norm/css.md)
\ No newline at end of file
... ...
# css代码规范
## 选择器
* 使用class进行样式匹配,而不是id和标签
* 尽量避免使用属性选择器
* 尽可能精确的元素定位
## 规则细节
.ele-header, /*规则1:每个选择器声明总是使用新的一行*/
.ele-body,
.ele-footer { /*规则2:'{' 前需要添加1个空格*/
line-height: 1.2; /*规则3:样式声明以;结束,每个样式声明独占一行*/
font-weight: normal; /*规则4:属性声明的:后添加1个空格*/
margin: 0; /*规则5:属性值为0时不添加单位*/
background-color: #f3d; /*规则6:十六进制小写和缩写*/
}
/*规则7:没组选择器声明之间适用一空行间隔*/
.ele2 {
font-family: "open sans", arial, sans-serif; /*规则8:使用" ",而不是' '*/
padding: 0 1em 2em; /*规则9:适当缩写但不滥用*/
border-top: 1px;
background-color: rgba(0,0,0,.5); /*规则10:颜色值rgba等中不需要增加空格,并且去除浮点数前面不必要的0*/
}
## 声明顺序(不做强制要求,尽量实现)
这是一个选择器内书写CSS属性顺序的大致轮廓,作为最佳实践,我们应该遵循以下顺序:
* Position属性 (position,top,right,z-index...)
* Box Model属性 (display,float,width...)
* Typographic属性 (font,line-height,color,text-align...)
* Visual属性 (background,border,border-radius...)
因为Position属性可以是一个元素脱离正常的文本流并可以覆盖盒模型相关样式,所以Position排第一位。盒模型决定一个元素位置和大小紧跟其后。后面属性属于元素内部或不会对前两者产生影响的,排在后面。
完整属性顺序参考[Recsss](http://twitter.github.com/recess)
## Sass风格
* 控制嵌套层级,禁止超过5层,尽量控制在3层以内
# css代码规范
## 选择器
* 使用class进行样式匹配,而不是id和标签
* 尽量避免使用属性选择器
* 尽可能精确的元素定位
## 规则细节
.ele-header, /*规则1:每个选择器声明总是使用新的一行*/
.ele-body,
.ele-footer { /*规则2:'{' 前需要添加1个空格*/
line-height: 1.2; /*规则3:样式声明以;结束,每个样式声明独占一行*/
font-weight: normal; /*规则4:属性声明的:后添加1个空格*/
margin: 0; /*规则5:属性值为0时不添加单位*/
background-color: #f3d; /*规则6:十六进制小写和缩写*/
}
/*规则7:没组选择器声明之间适用一空行间隔*/
.ele2 {
font-family: "open sans", arial, sans-serif; /*规则8:使用" ",而不是' '*/
padding: 0 1em 2em; /*规则9:适当缩写但不滥用*/
border-top: 1px;
background-color: rgba(0,0,0,.5); /*规则10:颜色值rgba等中不需要增加空格,并且去除浮点数前面不必要的0*/
}
## 声明顺序(不做强制要求,尽量实现)
这是一个选择器内书写CSS属性顺序的大致轮廓,作为最佳实践,我们应该遵循以下顺序:
* Position属性 (position,top,right,z-index...)
* Box Model属性 (display,float,width...)
* Typographic属性 (font,line-height,color,text-align...)
* Visual属性 (background,border,border-radius...)
因为Position属性可以是一个元素脱离正常的文本流并可以覆盖盒模型相关样式,所以Position排第一位。盒模型决定一个元素位置和大小紧跟其后。后面属性属于元素内部或不会对前两者产生影响的,排在后面。
完整属性顺序参考[Recsss](http://twitter.github.com/recess)
## Sass风格
* 控制嵌套层级,禁止超过5层,尽量控制在3层以内
... ...
# JavaScript代码规范
## 行的长度
每行长度不应该超过**120**个字符,如果一行多余120个字符,应该在一个运算符后换行,下一行增加**2**级缩进,即8个空格)
```
doSomething(argument1, argument2, argument3, argument4,
argument5);
```
## 运算符间距
二元运算符前后必须使用一个空格保持表达式的整洁,操作符包括赋值运算符和逻辑运算符
```
var name = 'xuqi'; // GOOD
var name='xuqi'; // BAD
```
## 括号间距
当使用括号时,紧接左括号之后和紧接右括号之前不应该有空格。
```
doSomething(arg); // GODD
doSomething( arg ); // BAD
```
## 变量声明
* 所有变量在使用前应该先定义
* 变量定义应该放在函数开头
* 使用var,const,let表达式定义变量,每行定义一个
* 除了首行,所有行都应该多一层缩进使变量声明对齐
* 初始化的变量放在未初始化的变量之前
* 所有的变量命名必须使用英文单词
* 浮点变量必须指明实部(即便以0.开头)和小数点后一位
```
var name = 'xuqi',
age,
sex,
...;
const $ = require('yoho.jquery');
let i;
```
另外,晦涩的变量名最好给出注释,否则别人很难读懂接下来代码的意思。
## 函数声明
* 函数在使用前应该先定义
* 函数名和开始圆括号之间无空格(包括匿名函数的function关键字与圆括号之间)
* 开始圆括号和结束圆括号之间无空格
* 参数名之间应该在逗号之后保留一个空格
* 开始花括号应该同function关键字保持同一行,结束圆括号和开始花括号之间应该保留一个空格
* 函数体保持一级缩进
```
function doSomething(arg1, arg2) {
doThing1();
doThing2();
}
const method = function() {
doSomething();
};
```
另外,IIFE的标准格式也在这里指出:
```
(function(args) {
//
}(args)); //(args)位于外层括号内
```
## 对象直接量
* 起始左括号应该与表达式保持一行
* 每个属性的键名前保持一个缩进,第一个属性应该在左括号后另一起行
* 每个属性的键名不包含引号,其后跟一个冒号(前无空格,后有空格),然后是值
* 如果属性值为函数,函数体应该在属性名之下另起一行,并且其前后均应保留一个空行
* 一组相关属性的前后插入空行以提高代码的可读性
* 结束的右括号独占一行
```
var person = {
name: 'xuqi',
age: 25,
groupAttr1: xx1,
groupAttr2: xx2,
walk: function() {
//your walk fn
}
};
```
* 当对象字面量作为函数参数时,起始括号应该与函数名同行
```
doSomething({
//do something
});
```
## 命名
### 变量:
* 采用小驼峰命名格式
* 变量命名为名词(区别函数)
* 变量中不使用_
### 函数:
* 采用小驼峰命名格式
* 函数命名为动词(区别变量)
* 函数名中不使用_
### 构造函数:
* 采用大驼峰命名格式
* 命名应该是名词
### 私有成员:
* 一个对象中不希望外部访问的以下划线开头(约定)
## 等号运算符
使用`===`和`!==`,禁止使用`==`和`!=`
## undefined
禁止使用`name === undefined`判断一个变量是否定义。应该使用`typeof(name) === 'undefined'`;
## 常用语句规范
### if语句
```
if (condition) {
doSomething();
} else if (condition1) {
doSomething2();
} else {
soOtherThing();
}
```
### for语句
```
// GOOD
var i;
for (i = 0; i < len; i++) {
doSomething();
}
for (i in collection) {
if (collection.hasOwnProperty(i)) {
doSomething();
}
}
// BAD
for (var i = 0; i < len; i++) {
doSomething();
}
for (i in collection) {
doSomething();
}
```
### while,do语句
```
while (condition) {
doSomething();
}
do {
doSomething();
} while (condition)
```
### switch语句
* 每一个case保持一个缩进
* 每一组语句都应该以break,return等结尾,或者用一行注释表示跳过(falling through)
* 无default的情况也要注释特别说明
```
switch (val) {
case 1:
//nothing
case 2:
doSomething();
break;
default:
doDefault();
}
```
### try语句
```
try {
doSomething();
} catch (err) {
doSomething2();
} finally {
doSomething3();
}
```
## 模块化规范
* 模块开头require加载所有依赖模块
```
var $ = require('yoho.jquery'),
flip = require('../plugin/flip'); //普通文件
require('../plguin/login');
```
# JavaScript代码规范
## 行的长度
每行长度不应该超过**120**个字符,如果一行多余120个字符,应该在一个运算符后换行,下一行增加**2**级缩进,即8个空格)
```
doSomething(argument1, argument2, argument3, argument4,
argument5);
```
## 运算符间距
二元运算符前后必须使用一个空格保持表达式的整洁,操作符包括赋值运算符和逻辑运算符
```
var name = 'xuqi'; // GOOD
var name='xuqi'; // BAD
```
## 括号间距
当使用括号时,紧接左括号之后和紧接右括号之前不应该有空格。
```
doSomething(arg); // GODD
doSomething( arg ); // BAD
```
## 变量声明
* 所有变量在使用前应该先定义
* 变量定义应该放在函数开头
* 使用var,const,let表达式定义变量,每行定义一个
* 除了首行,所有行都应该多一层缩进使变量声明对齐
* 初始化的变量放在未初始化的变量之前
* 所有的变量命名必须使用英文单词
* 浮点变量必须指明实部(即便以0.开头)和小数点后一位
```
var name = 'xuqi',
age,
sex,
...;
const $ = require('yoho.jquery');
let i;
```
另外,晦涩的变量名最好给出注释,否则别人很难读懂接下来代码的意思。
## 函数声明
* 函数在使用前应该先定义
* 函数名和开始圆括号之间无空格(包括匿名函数的function关键字与圆括号之间)
* 开始圆括号和结束圆括号之间无空格
* 参数名之间应该在逗号之后保留一个空格
* 开始花括号应该同function关键字保持同一行,结束圆括号和开始花括号之间应该保留一个空格
* 函数体保持一级缩进
```
function doSomething(arg1, arg2) {
doThing1();
doThing2();
}
const method = function() {
doSomething();
};
```
另外,IIFE的标准格式也在这里指出:
```
(function(args) {
//
}(args)); //(args)位于外层括号内
```
## 对象直接量
* 起始左括号应该与表达式保持一行
* 每个属性的键名前保持一个缩进,第一个属性应该在左括号后另一起行
* 每个属性的键名不包含引号,其后跟一个冒号(前无空格,后有空格),然后是值
* 如果属性值为函数,函数体应该在属性名之下另起一行,并且其前后均应保留一个空行
* 一组相关属性的前后插入空行以提高代码的可读性
* 结束的右括号独占一行
```
var person = {
name: 'xuqi',
age: 25,
groupAttr1: xx1,
groupAttr2: xx2,
walk: function() {
//your walk fn
}
};
```
* 当对象字面量作为函数参数时,起始括号应该与函数名同行
```
doSomething({
//do something
});
```
## 命名
### 变量:
* 采用小驼峰命名格式
* 变量命名为名词(区别函数)
* 变量中不使用_
### 函数:
* 采用小驼峰命名格式
* 函数命名为动词(区别变量)
* 函数名中不使用_
### 构造函数:
* 采用大驼峰命名格式
* 命名应该是名词
### 私有成员:
* 一个对象中不希望外部访问的以下划线开头(约定)
## 等号运算符
使用`===`和`!==`,禁止使用`==`和`!=`
## undefined
禁止使用`name === undefined`判断一个变量是否定义。应该使用`typeof(name) === 'undefined'`;
## 常用语句规范
### if语句
```
if (condition) {
doSomething();
} else if (condition1) {
doSomething2();
} else {
soOtherThing();
}
```
### for语句
```
// GOOD
var i;
for (i = 0; i < len; i++) {
doSomething();
}
for (i in collection) {
if (collection.hasOwnProperty(i)) {
doSomething();
}
}
// BAD
for (var i = 0; i < len; i++) {
doSomething();
}
for (i in collection) {
doSomething();
}
```
### while,do语句
```
while (condition) {
doSomething();
}
do {
doSomething();
} while (condition)
```
### switch语句
* 每一个case保持一个缩进
* 每一组语句都应该以break,return等结尾,或者用一行注释表示跳过(falling through)
* 无default的情况也要注释特别说明
```
switch (val) {
case 1:
//nothing
case 2:
doSomething();
break;
default:
doDefault();
}
```
### try语句
```
try {
doSomething();
} catch (err) {
doSomething2();
} finally {
doSomething3();
}
```
## 模块化规范
* 模块开头require加载所有依赖模块
```
var $ = require('yoho.jquery'),
flip = require('../plugin/flip'); //普通文件
require('../plguin/login');
```
* 模块抛出接口应该给予注释说明功能和使用方法
\ No newline at end of file
... ...
/**
* 潮流优选
* @author: xiaoxiao<xiaoxiao.hao@yoho.cn>
* @date: 2016/08/04
*/
'use strict';
const mRoot = '../models';
const plusstarModel = require(`${mRoot}/plusstar`);
const headerModel = require('../../../doraemon/models/header'); // 头部model
let channels = {
boys: 1,
girl: 2,
kids: 3,
lifestyle: 4
};
/**
* 潮流优选首页
*/
exports.index = (req, res, next) => {
let isApp = req.query.app_version || req.query.appVersion || false;
let parameter = {};
let title = '潮流优选';
let gender = req.query.gender || req.cookies._Channel && channels[req.cookies._Channel] || 1;
if (isApp === false) {
parameter = {
pageHeader: headerModel.setNav({
navTitle: title
}),
pageFooter: true
};
}
plusstarModel.getAllChannels({gender: gender, app_type: 0}).then(result => {
res.render('plusstar/index', Object.assign({
page: 'plusstar-index',
result: result,
isApp: isApp,
title: title
}, parameter));
}).catch(next);
};
/**
* 潮流优选首页-资源位
*/
exports.resourcesTemplate = (req, res, next) => {
let code = req.query.code || '';
let isApp = req.query.app_version || req.query.appVersion || false;
plusstarModel.getResources({
content_code: code
}, {
isApp: isApp
}).then(result => {
res.render('plusstar/resources-template', {
layout: false,
result: result,
title: '潮流优选'
});
}).catch(next);
};
... ...
'use strict';
const api = global.yoho.API;
const serviceAPI = global.yoho.ServiceAPI;
const _ = require('lodash');
const productProcess = require('../../../utils/product-process');
/**
* 获取潮流优选tab
* @return {[array]}
*/
const getAllChannels = (params) => {
let gender = params.gender - 1 || 0;
return api.get('', {
method: 'app.blk.getAllChannels',
app_type: params.app_type
}).then(result => {
let data = {channel: []};
if (result.code !== 200) {
return data;
}
if (gender === 3 && result.data.length === 3) {
// 1:男,2:女,3:童装,4:创意生活
// 如果为3,说明童装没有配置,只配置了创意生活。所以要减一
gender = 2;
}
_.forEach(result.data, (res, index) => {
data.channel.push({
id: res.channel_id,
mame: res.channel_name,
code: res.content_code,
focus: index === gender ? true : false
});
});
return data;
});
};
/**
* 通过skn查询商品详情
* @param {[string || array]} productSkn 商品skn
* @return {[array]}
*/
const getProductBatch = (productSkn, options) => {
productSkn = _.isArray(productSkn) ? productSkn : [productSkn];
return api.get('', {
method: 'h5.product.batch',
productSkn: productSkn.join(',')
}).then(result => {
return result && result.data ? productProcess.processProductList(result.data.product_list, options) : [];
});
};
/**
* 获取资源位数据
* @param {[string]} content_code
* @return {[array]}
*/
const getResources = (params, options) => {
params = params || {};
if (options.isApp) {
params.platform = 'iphone';
}
return serviceAPI.get('operations/api/v5/resource/get', params).then(result => {
let data = {
goods: {},
recommend: {}
};
let list = {};
let productSkns = [];
if (result.code !== 200) {
return data;
}
_.forEach(result.data, (res) => {
list = {};
switch (res.template_name) {
case 'focus':
list = {
data: res.data
};
if (res.focus_type * 1 === 1) {
data.focus1 = [];
data.focus1.push(list);
} else {
data.focus2 = list;
}
break;
case 'title_image' :
if (typeof data[res.template_name] === 'undefined') {
data[res.template_name] = [];
}
list = {
title: res.data.title,
moreUrl: res.data.more_url,
moreName: res.data.more_name,
image: res.data.image
};
data.title_image.push(list);
break;
case 'titleFloor':
list = {
name: res.data.title.name,
moreUrl: res.data.title.more_url,
moreName: res.data.title.more_name
};
if (res.data.title.name === '热门商品') {
data.goods.title = list;
} else if (res.data.title.name === '热门品类') {
data.recommend.title = list;
}
break;
case 'recommend_content_five':
data.recommend.data = res.data.list;
break;
case 'goods':
_.forEach(res.data, (skn) => {
productSkns.push(skn.id);
});
data[res.template_name].productSkns = productSkns;
break;
default:
break;
}
});
if (_.isEmpty(data.goods.productSkns)) {
return data;
}
return getProductBatch(data.goods.productSkns, options).then(res => {
data.goods.data = res;
return data;
});
});
};
module.exports = {
getAllChannels,
getResources,
getProductBatch
};
... ...
... ... @@ -57,7 +57,7 @@ const _processIndexData = (dataList) => {
if (list.ads) {
_.forEach(list.ads.data, (data) => {
formatData.ads.push({
src: data.src + '/q/80',
src: data.src.replace('imageView/', 'imageView2/'),
url: data.url
});
});
... ... @@ -65,7 +65,7 @@ const _processIndexData = (dataList) => {
// 首页明星文章数据处理
if (list.list) {
_.forEach(list.list, (data) => {
_.forEach(list.list, (data, index) => {
const avatar = {
tags: []
};
... ... @@ -74,20 +74,29 @@ const _processIndexData = (dataList) => {
avatar.isSwiper = true;
}
_.forEach(data.ext.tags, (tags) => {
_.forEach(data.ext.tags, (tags, i) => {
if (i >= 1) {
return;
}
avatar.tags.push({
avatarUrl: `/guang/star/detail?tag=${tags.tagName}&openby:yohobuy{"action":"go.h5","params":{"id":"","share":"","shareparam":{},"islogin":"N","type":0,"updateflag":"N","url":"http://m.yohobuy.com/guang/star/detail","param":{"tag":"${tags.tagName}"}}}`, // eslint-disable-line
cover: tags.cover ? (tags.cover + '?imageView2/2/w/104/h/104/q/80') : tags.cover,
cover: tags.cover ? (tags.cover + '?imageView2/2/w/104/h/104') : tags.cover,
tagName: tags.tagName
});
});
if (formatData.articles.length > 30) {
return;
}
formatData.articles.push(_.merge({
noLazy: index < 2,
id: data.id,
url: data.url,
title: data.title,
articeTxt: data.intro,
src: data.src + '/q/80',
src: data.src,
publishTime: helpers.dateFormat('MM月DD日 hh:mm', data.publishTime),
viewsNum: data.viewsNum
}, avatar));
... ... @@ -97,12 +106,13 @@ const _processIndexData = (dataList) => {
// 首页明星头像数据处理
if (list.tags) {
_.forEach(list.tags, (data) => {
_.forEach(list.tags, (data, index) => {
let url = `/guang/star/detail?tag=${data.tagName}&openby:yohobuy{"action":"go.h5","params":{"id":"","share":"","shareparam":{},"islogin":"N","type":0,"updateflag":"N","url":"http://m.yohobuy.com/guang/star/detail","param":{"tag":"${data.tagName}"}}}`; // eslint-disable-line
formatData.starAvatar.push({
noLazy: index < 6,
url: url,
cover: data.cover ? (data.cover + '?imageView2/2/w/180/h/180/q/80') : data.cover
cover: data.cover ? (data.cover + '?imageView2/2/w/180/h/180') : data.cover
});
});
}
... ... @@ -136,7 +146,7 @@ const _processGuangData = (list, flag) => {
// data.src += '/q/80';
if (key < 4) {
if (key < 2) {
data.islazy = true;
}
... ... @@ -203,7 +213,7 @@ const getSpecialData = () => {
return;
}
item.src += '/q/80';
// item.src += '/q/80';
if (key < 4) {
item.islazy = true;
... ...
... ... @@ -9,6 +9,7 @@
const express = require('express');
const cRoot = './controllers';
const star = require(cRoot + '/star');
const plusstar = require(cRoot + '/plusstar');
const router = express.Router(); // eslint-disable-line
... ... @@ -22,4 +23,7 @@ router.get('/star/collocation/list', star.collocationList); // 星潮教室星
router.post('/star/setFavorite', star.setFavorite); // 收藏文章
router.get('/plusstar', plusstar.index); // 潮流优选
router.get('/plusstar/resources-template', plusstar.resourcesTemplate); // 潮流优选首页-资源位
module.exports = router;
... ...
<div class='plusstar-page'>
{{#if result.channel}}
<div class="tab-nav">
<ul>
{{#each result.channel}}
<li class='{{#if focus}} focus {{/if}}' data-code='{{code}}'>
<span>{{mame}}</span>
</li>
{{/each}}
</ul>
</div>
{{/if}}
<!--/tab-nav-->
{{#if isApp}}
<div class='empty-height'></div>
{{/if}}
<div class="plusstar-resources">
<!--资源位数据模板-->
</div><!--/plusstar-resources-->
</div><!--/plusstar-page-->
\ No newline at end of file
... ...
<div class="resources">
<!--banner-->
{{#each result.focus1}}
{{> resources/banner-top}}
{{/each}}
{{#each result.title_image}}
<div class="header-title">
{{title}}
<a class="more" href="{{moreUrl}}">{{moreName}}</a>
</div>
<div class="title-image">
<a class="image" href="{{image.url}}">
<img class="lazy" data-original="{{image image.src 750 364}}">
</a>
</div>
{{/each}}
{{#if result.focus2.data}}
<div class="focus-left-right">
{{#each result.focus2.data}}
<a href="{{url}}" title="{{title}}">
<img class="lazy" data-original="{{image src 250 250}}">
</a>
{{/each}}
</div>
{{/if}}
<!--/focus-left-right-->
{{#if result.recommend.title}}
<div class="header-title">
{{result.recommend.title.name}}
<a class="more" href="{{result.recommend.title.moreUrl}}">
{{result.recommend.title.moreName}}
</a>
</div>
{{/if}}
{{#if result.recommend.data}}
<div class="recommend-content-five">
{{#each result.recommend.data}}
<a href="{{url}}" title="{{title}}">
<img class="lazy" data-original="{{image src 375 375}}">
</a>
{{/each}}
</div>
{{/if}}
{{#if result.goods.title}}
<div class="header-title">
{{result.goods.title.name}}
<a class="more" href="{{result.goods.title.moreUrl}}">
{{result.goods.title.moreName}}
</a>
</div>
<div class="goods clearfix">
<!--商品--->
{{#each result.goods.data}}
{{> common/goods}}
{{/each}}
</div><!--/goods-->
{{/if}}
</div><!--/resources-->
... ...
... ... @@ -11,18 +11,47 @@
<ul class="clearfix swiper-wrapper">
{{# starAvatar}}
<li class="swiper-slide">
<a href='{{url}}' style="background-image: url({{image cover 180 180}})" class="star"></a>
<a href='{{url}}'>
{{#if noLazy}}
<img class="star" src="{{cover}}">
{{else}}
<img class="star swiper-lazy" data-src="{{cover}}">
{{/if}}
</a>
</li>
{{/ starAvatar}}
</ul>
</div>
</div>
<ul class="star-info clearfix">
{{#each articles}}
<li data-id="{{id}}">
<div class="star-avatar">
{{#if isSwiper}}
<div class="avatar-wrap avatar-num-{{tags.length}}">
{{# tags}}
<a href="{{avatarUrl}}">
{{#if ../noLazy}}
<img src="{{image cover 100 100}}" class="rank-avatar" ></img>
{{else}}
<img data-original="{{image cover 100 100}}" class="rank-avatar lazy" ></img>
{{/if}}
<p class="name">{{tagName}}</p>
</a>
{{/ tags}}
{{# tags}}
{{#if @first}}
<a href="{{avatarUrl}}">
<img data-original="{{image cover 100 100}}" class="rank-avatar lazy" ></img>
<p class="name">{{tagName}}</p>
</a>
{{/if}}
{{/ tags}}
</div>
{{!-- {{#if isSwiper}}
<div class="article-avatar-swiper">
<ul class="clearfix swiper-wrapper">
{{#each tags}}
... ... @@ -42,7 +71,7 @@
<p class="name">{{tagName}}</p>
</a>
{{/ tags}}
{{/if}}
{{/if}} --}}
</div>
<a class="star-article" href='{{url}}'>
<i class="article-arrow"></i>
... ... @@ -50,7 +79,11 @@
<div class="artice-cont">
<p>{{articeTxt}}</p>
<div class="artice-imgs-area">
{{#if noLazy}}
<img src="{{image src 266 266}}" />
{{else}}
<img data-original="{{image src 266 266}}" class="lazy" />
{{/if}}
{{!-- <ul class="artice-imgs">
{{#each articeImg}}
... ...
/**
* 分期付款
* @author: wsl<shuiling.wang@yoho.cn>
* @date: 2016/08/01
*/
'use strict';
const installmentModel = require('../models/installment');
const _ = require('lodash');
const helpers = global.yoho.helpers;
// 判断是否已经获取到了开通的状态值
const _reviewStatus = (uid, status, next) => {
let jumpUrl = helpers.appUrlFormat('/product/new', 'go.new');
if (status === '1') {
return {
review: {
url: jumpUrl
}
};
} else if (status === '2') {
return Promise.all([installmentModel.getSearchIntallment({
page: 1
}), installmentModel.getQueryCreditInfo(uid)]).then((result) => { //eslint-disable-line
return {
success: {
price: result[1].currCreditLimit,
installmentOnly: {
title: '分期专享',
goods: result[0]
},
url: jumpUrl
}
};
}).catch(next);
} else if (status === '3') {
return {
error: {
url: jumpUrl
}
};
}
};
// 还款列表公共处理块
const _repaymentList = (req, res, next, title, params) => {
params = _.assign({
uid: req.cookies.installmentUid || 1
}, params);
installmentModel.getQueryAmtList(params).then((result) => {
res.render('installment/repayment-list', {
module: 'home',
page: 'repayment-list',
isInstallmentPage: true,
title: title,
data: result
});
}).catch(next);
};
// 开通分期首页
const index = (req, res, next) => {
let uid = req.query.uid;
Promise.all([installmentModel.getStauts(uid), installmentModel.getSearchIntallment({
page: 1
})]).then((result) => {
// status:0 未申请 1审核中 2已开通 3 审核未通过
let openStatus = result[0];
let installmentOnly = {
title: '分期专享',
goods: result[1]
};
if (openStatus === '0') {
return installmentModel.getResources().then(data => {
if (data[0] && data[0].data[0]) {
data[0].data[0].url = 'javascript:void(0)'; //eslint-disable-line
}
return {
bannerTop: data,
notOpen: true,
installmentOnly: installmentOnly
};
});
} else if (openStatus === '2') {
return Promise.all([installmentModel.getQueryCreditInfo(uid), installmentModel.getQueryAmtInfo(uid)]).then((data) => { //eslint-disable-line
let params = _.assign({
isOverdue: false,
installmentOnly: installmentOnly
}, data[0], data[1]);
// status: 1 正常 2 逾期 3 不可用 4 未开通
if (data[0].status === '2') {
params.replayStatus = '逾期';
params.isOverdue = true;
} else if (data[0].status === '3') {
params.replayStatus = '不可用';
}
return params;
});
} else if (openStatus === '1' || openStatus === '3') {
res.redirect('/home/installment/review?status=' + openStatus);
}
}).then((result) => {
res.render('installment/open-index', _.assign({
module: 'home',
page: 'installment',
isInstallmentPage: true,
title: '有货分期'
}, result));
}).catch(next);
};
// ajax 请求分期专享商品
const getInstallmentGoods = (req, res, next) => {
let params = req.query || {};
installmentModel.getSearchIntallment(params).then((result) => {
if (result) {
res.render('installment/goods-template', {
layout: false,
goods: result
});
} else {
res.json();
}
}).catch(next);
};
// 开通结果显示
const review = (req, res, next) => {
let openStatus = req.query.status || false;
let uid = req.query.uid;
let data = {
module: 'home',
page: 'installment',
title: '有货分期'
};
if (openStatus !== '2') {
res.render('installment/open-result', _.assign(data, _reviewStatus(uid, openStatus)));
} else {
_reviewStatus(uid, openStatus, next).then((params) => {
res.render('installment/open-result', _.assign(data, params));
});
}
};
// 逾期未还款列表
const overdueList = (req, res, next) => {
_repaymentList(req, res, next, '逾期未还金额', {
queryDays: -1
});
};
// 7日待还款列表
const sevenDayList = (req, res, next) => {
_repaymentList(req, res, next, '近7日待还款', {
queryDays: 7
});
};
// 本月待还款列表
const monthRepayList = (req, res, next) => {
_repaymentList(req, res, next, '本月待还金额', {
queryDays: 30
});
};
// 待还总金额列表
const totalRepayList = (req, res, next) => {
_repaymentList(req, res, next, '待还总金额', {
queryDays: 0
});
};
// 还款记录页面渲染
const repayRecordPage = (req, res) => {
res.render('installment/repay-record', {
module: 'home',
page: 'repay-record',
isInstallmentPage: true,
title: '还款记录'
});
};
// ajax 请求还款记录
const getRepayRecord = (req, res, next) => {
let params = _.assign({
uid: req.cookies.installmentUid || 1,
pageNo: req.query.page || 1
});
installmentModel.getQueryRePayList(params).then((result) => {
if (result) {
res.render('installment/record-template', {
layout: false,
recordData: result
});
} else {
res.json();
}
}).catch(next);
};
// 账号管理
const account = (req, res, next) => {
let uid = req.cookies.installmentUid || 512579468;
installmentModel.getBankCards(uid).then((result) => {
res.render('installment/account', {
accountList: result,
title: '账号管理',
isInstallmentPage: true,
repaymentList: result
});
}).catch(next);
};
const startingService = (req, res) => {
res.render('installment/starting-service', {
module: 'home',
page: 'installment.starting-service',
title: '开通有货分期',
navTitle: '开通有货分期',
navBtn: false
});
};
/**
* 获取真实IP
*
* @param req
* @returns {*|string}
*/
function getRealIP(req) {
var realIP = req.headers['x-real-ip'];
var forwardedFor = req.headers['x-forwarded-for'] || '';
return realIP || forwardedFor.split(',')[0] || req.connection.remoteAddress;
}
const activateService = (req, res, next) => {
installmentModel.activateService({
uid: req.cookies.installmentUid || 532892,
userName: req.body.userName,
identityCardNo: req.body.identityCardNo,
cardNo: req.body.cardNo,
mobile: req.body.mobile,
snsCheckCode: req.body.snsCheckCode,
bankCode: req.body.bankCode,
bankName: req.body.bankName,
udid: req.cookies.udid || 0,
from_client_type: req.cookies.clientType,
ip: getRealIP(req)
}).then((result)=> {
res.json(result);
}).catch(next);
};
const getBankInfo = (req, res, next) => {
installmentModel.getBankInfo({
cardNo: req.query.cardNo,
uid: req.cookies.installmentUid || 512579468 // TODO: fix uid
}).then((result)=> {
res.json(result);
}).catch(next);
};
const verifyCode = (req, res, next) => {
installmentModel.sendVerifyCode(req.cookies.installmentUid || 1, req.query.mobile).then((result)=> {
res.json(result);
}).catch(next);
};
const orderIndex = (req, res) => {
res.render('installment/order', {
module: 'home',
page: 'installment.order',
title: '我的分期订单',
navBtn: false,
isInstallmentPage: true
});
};
const orderList = (req, res, next) => {
const params = {
uid: req.cookies.installmentUid || 8041876, // TODO: fix me
type: req.query.type || 1,
page: req.query.page || 1,
limit: req.query.limit || 10
};
installmentModel.getInstallmentOrders(params).then((result)=> {
res.render('installment/order-list', {
title: '我的分期订单',
orders: ((()=> {
// 处理数据,订单只包含第一条物品纪录
var list = result.data ? result.data.orderList : [];
if (list) {
list.forEach((item)=> {
item.orderGoods = [
item.orderGoods[0]
];
});
}
return list;
})()),
layout: false,
isInstallmentPage: true
});
}).catch(next);
};
const orderDetail = (req, res, next) => {
const params = {
uid: req.cookies.installmentUid || 8041876, // TODO: fix me
orderCode: req.params.id
};
installmentModel.getInstallmentOrderDetail(params).then((result)=> {
res.render('installment/order-detail', {
module: 'home',
page: 'installment.order-detail',
title: '分期详情',
order: (()=> {
let refundStatusCount = 0, completeStatusCount = 0, listCount = 0;
if (result && result.data && result.data.orderGoods) {
result.data.orderGoods = [
result.data.orderGoods[0]
];
}
if (result && result.data && result.data.packageList) {
listCount = result.data.packageList.length;
result.data.packageList.forEach((item)=> {
if (item.status === 2) {
completeStatusCount++;
} else if (item.status === 4 || item.status === 5) {
refundStatusCount++;
}
});
}
return Object.assign({
status: (()=> {
if (refundStatusCount === listCount) {
return 5;
} else if (completeStatusCount === listCount) {
return 2;
}
})()
}, result.data);
})(),
navBtn: false,
currAmtCount: 0,
currFeeCount: 0,
isCurrFee: true,
helpers: {
isPaymentComplete: function(status, options) {
if (status === 2 || status === 4 || status === 5) { // 已结清/已取消
return options.fn(this);
}
return options.inverse(this);
},
isPaymentIncomplete: function(status, options) {
if (status !== 2 && status !== 4 && status !== 5) { // 其他状态
return options.fn(this);
}
return options.inverse(this);
},
isRefundedAll: function(status, options) {
if (status === 5) { // 已退款
return options.fn(this);
}
return options.inverse(this);
},
isRepaymentAllCompleted: function(status, options) {
if (status === 2) { // 已结清
return options.fn(this);
}
return options.inverse(this);
},
greaterThanZero: function(value, options) {
if (value && parseFloat(value) > 0) {
return options.fn(this);
}
return options.inverse(this);
}
}
});
}).catch(next);
};
// 还款详情
const repayDetail = (req, res, next) => {
let params = {
uid: req.cookies.installmentUid || 512579468,
rePayNo: req.query.id || '',
pageNo: 1
};
installmentModel.getQueryRePayList(params).then((result) => {
res.render('installment/repay-detail', _.assign({
title: '还款详情',
isInstallmentPage: true,
isOne: true
}, result[0]));
}).catch(next);
};
// 帮助静态页面
const help = (req, res) => {
res.render('installment/help', {
title: '分期支付帮助中心'
});
};
// 协议静态页面
const agreement = (req, res) => {
res.render('installment/agreement', {
title: '用户服务协议'
});
};
// 计算金额
const totalAmount = (req, res, next) => {
installmentModel.totalAmount(req.query.prices).then((result) => {
res.json(result);
}).catch(next);
};
// 检查验证码
const checkVerifyCode = (req, res, next) => {
installmentModel.checkVerifyCode(req.cookies.installmentUid, req.query.mobile, req.query.code).then((result) => {
res.json(result);
}).catch(next);
};
module.exports = {
index,
review,
overdueList,
sevenDayList,
monthRepayList,
totalRepayList,
repayRecordPage,
getRepayRecord,
startingService,
activateService,
verifyCode,
getBankInfo,
account,
orderIndex,
orderList,
orderDetail,
repayDetail,
help,
agreement,
totalAmount,
checkVerifyCode,
getInstallmentGoods
};
... ...
/**
* sub app coupon
* @author: lixia.zhang<lixia.zhang@yoho.cn>
* @date: 2016/05/31
* sub app home
* @author: Bi Kai<kai.bi@yoho.cn>
* @date: 2016/05/09
*/
var express = require('express'),
... ... @@ -17,12 +17,13 @@ app.on('mount', function(parent) {
delete parent.locals.settings; // 不继承父 App 的设置
Object.assign(app.locals, parent.locals);
});
app.set('views', path.join(__dirname, 'views/action'));
app.engine('.hbs', hbs({
extname: '.hbs',
defaultLayout: 'layout',
layoutsDir: doraemon,
partialsDir: ['./views/partial', `${doraemon}/partial`],
partialsDir: [path.join(__dirname, 'views/partial'), `${doraemon}/partial`],
helpers: global.yoho.helpers
}));
... ...
/**
* 分期付款
* @author: wsl<shuiling.wang@yoho.cn>
* @date: 2016/08/01
*/
'use strict';
const utils = '../../../utils';
const productProcess = require(`${utils}/product-process`);
const resourcesProcess = require(`${utils}/resources-process`);
const _ = require('lodash');
const helpers = global.yoho.helpers;
const api = global.yoho.API;
const serviceAPI = global.yoho.ServiceAPI;
const logger = global.yoho.logger;
const camelCase = global.yoho.camelCase;
const API_TIMEOUT = 10000;
// 处理还款列表数据
const _processAmtList = (listData, queryDays) => {
let overduecount = 0;
let formartData = {
currAmtCount: 0,
currFeeCount: 0
};
if (listData !== 'NULL') {
let list = listData.amtList;
_.forEach(list, (data, key) => {
// 第一条选中
if (key === 0) {
data.isChecked = true;
}
// 组装分期的显示格式
if (data.terms === 1) {
data.stage = data.terms;
} else {
data.stage = data.currTerm + '/' + data.terms;
}
// 判断是否逾期
if (data.unExpireDays < 0) {
data.isOverdue = true;
overduecount++;
}
data.day = Math.abs(data.unExpireDays);
data.url = `/home/installment/order/${data.billNo}`;
data.key = key;
});
if (overduecount === 0) {
if (queryDays === 0) { // 待还总金额
list[0].isChecked = true;
formartData.currAmtCount = (+list[0].currAmt);
formartData.currFeeCount = (+list[0].currFee);
if (list.length === 1) {
formartData.isAllChecked = true;
}
} else if (queryDays === 7 || queryDays === 30) { // 近7日待还金额和本月待还金额
_.forEach(list, (data) => {
data.isChecked = true;
formartData.currAmtCount += (+data.currAmt);
formartData.currFeeCount += (+data.currFee);
});
formartData.isAllChecked = true;
}
} else { // 只要有逾期的记录,走这里
_.forEach(list, (data) => {
if (data.unExpireDays < 0) {
data.isChecked = true;
formartData.currAmtCount += (+data.currAmt);
formartData.currFeeCount += (+data.currFee);
}
});
if (overduecount === list.length) {
formartData.isAllChecked = true;
}
}
// 逾期未还款金额
if (queryDays === -1) {
formartData.isAllChecked = true;
}
if (formartData.currFeeCount > 0) {
formartData.isCurrFee = true;
} else {
formartData.isCurrFee = false;
}
return _.assign({
repaymentList: list
}, formartData);
} else {
return {
isNoResult: true
};
}
};
// 处理还款记录数据
const _processRepayList = (list) => {
if (list !== 'NULL') {
_.forEach(list.rePayList, (data) => {
if (data.status === 1) {
data.repayStatus = '已还清';
data.txt = '还款成功!';
data.isTwo = true;
data.isThree = true;
delete data.desc;
} else if (data.status === 2) {
data.repayStatus = '还款失败';
data.txt = '还款失败';
if (data.desc || data.desc !== '') {
data.txt += `,${data.desc}`;
}
data.isTwo = true;
data.isThree = true;
} else {
data.repayStatus = '还款中';
data.txt = '预计1-2分钟内还款成功,<br>请到还款记录查看还款结果';
delete data.desc;
}
data.url = `/home/installment/repay/detail?id=${data.prePayNo}`;
});
return list.rePayList;
} else {
return false;
}
};
// 银行卡号处理
const _processBankCards = (list) => {
list = list || [];
_.forEach(list, (data) => {
// let newCardNo = data.cardNo.split('');
// let cardNo = newCardNo.length;
// data.cardNo = '';
//
//
// for (let i = 1; i < cardNo; i++) {
// if (i < (cardNo - 4)) {
// data.cardNo += '*';
// } else {
// data.cardNo += newCardNo[i - 1];
// }
// }
// 正则替换卡号,保留后4位
data.cardNo = data.cardNo.replace(/(\d+)(\d{4})$/, function(a, b, c) {
return b.replace(/\d/g, '*') + c;
});
});
return list;
};
/**
* 获取资源位数据
* @return {[array]}
*/
const getResources = () => {
return serviceAPI.get('/operations/api/v5/resource/get', {
content_code: '0876085ff46bed27f1a1eb6ee8b68987'
}, {
cache: true
}).then((result) => {
if (result && result.code === 200) {
return resourcesProcess(result.data);
} else {
logger.error('get resources data return code is not 200');
return [];
}
});
};
const response = require('./response.json');
const _DEBUG = false;// true;
// 获取分期开通状态
const getStauts = (uid) => {
return api.get('', {
method: 'user.instalment.getStatus',
uid: uid
}, {
timeout: API_TIMEOUT
}).then((result) => {
if (result && result.code === 200) {
return result.data.status;
} else {
logger.error('get installment open status return code is not 200');
return Promise.reject(result);
}
});
};
// 获取用户可用额度信息
const getQueryCreditInfo = (uid) => {
return api.get('', {
method: 'user.instalment.queryCreditLimit',
uid: uid
}, {
timeout: API_TIMEOUT
}).then((result) => {
if (result && result.code === 200) {
return result.data;
} else {
logger.error('get user installment usable price info return code is not 200');
return Promise.reject(result);
}
});
};
// 获取用户待还款金额
const getQueryAmtInfo = (uid) => {
return api.get('', {
method: 'app.order.queryAmtInfo',
uid: uid
}, {
timeout: API_TIMEOUT
}).then((result) => {
if (result && result.code === 200) {
result.data.dayAmt = result.data.s_7daysAmt;
return result.data;
} else {
logger.error('get user installment repay info return code is not 200');
return Promise.reject(result);
}
});
};
// 获取用户待还列表信息 queryDays -1:逾期待还;0:全部待还;7:七日待还;30:本月待还
const getQueryAmtList = (params) => {
return api.get('', _.assign({
method: 'app.order.queryAmtList',
pageSize: '20'
}, params), {
timeout: API_TIMEOUT
}).then((result) => {
if (result && result.code === 200) {
return _processAmtList(result.data, params.queryDays);
} else {
logger.error('get queryAmtList data return code is not 200');
return Promise.reject(result);
}
});
};
// 分期专享推荐商品
const getSearchIntallment = (params) => {
return api.post('', {
method: 'app.search.instalment',
limit: '50',
order: 's_t_desc',
page: params.page
}, {
cache: true,
timeout: API_TIMEOUT
}).then((result) => {
if (result && result.code === 200) {
let goods = productProcess.processProductList(result.data.product_list);
_.forEach(goods, (data) => {
data.url = helpers.appUrlFormat(data.url, 'go.productDetail', {
product_skn: data.productSkn
});
});
return goods;
} else {
logger.error('get instalment promote goods return code is not 200');
return Promise.reject(result);
}
});
};
/**
* 获取短信验证码
*
* @param uid 用户ID
* @param mobile 手机号码
*/
const sendVerifyCode = (uid, mobile) => {
return api.get('', {
method: 'user.instalment.getSnsCheckCode',
uid: uid,
mobile: mobile,
codeType: 1 // 授信
}, {
timeout: API_TIMEOUT
});
};
/**
* 开通服务
*
* @param 参数
* uid 用户id
* userName 姓名
* identityCardNo 身份证号码
* cardNo 银行卡号码
* mobile 手机号码
* snsCheckCode 验证码
* @returns {*}
*/
const activateService = (params) => {
return api.get('', Object.assign({
method: 'user.instalment.activate',
debug: 'XYZ' // TODO: remove this
}, params), {
timeout: 30000
});
};
/**
* 获取银行信息
*
* @param cardNo
* @param uid
* @returns {*}
*/
const getBankInfo = (params) => {
return api.get('', {
method: 'user.instalment.getBankInfoByCardNo',
cardNo: params.cardNo,
uid: params.uid
}, {
timeout: API_TIMEOUT
});
};
/**
* 分期订单列表
*
* @params 参数
* uid
* type 订单类型 1:全部订单,2:还款中,3:已还清
* page 页数
* limit
* @returns {*}
*/
const getInstallmentOrders = (params) => {
const method = 'app.SpaceOrders.getInstallment';
if (!_DEBUG) {
return api.get('', {
method: method,
uid: params.uid,
type: params.type || 1,
page: params.page || 1,
limit: params.limit || 10,
debug: 'XYZ'
}, {
timeout: API_TIMEOUT
}).then((result) => {
return Promise.resolve(camelCase(result));
});
} else {
return Promise.resolve(camelCase(response[method]));
}
};
// 还款记录查询
const getQueryRePayList = (params) => {
return api.get('', _.assign({
method: 'app.order.queryRePayList',
pageSize: '20'
}, params), {
timeout: API_TIMEOUT
}).then((result) => {
if (result && result.code === 200) {
return _processRepayList(result.data);
} else {
logger.error('get queryRePayList data return code is not 200');
return {};
}
});
};
// 账号管理
const getBankCards = (uid) => {
return api.get('', {
method: 'user.instalment.getBankCards',
uid: uid
}, {
timeout: API_TIMEOUT
}).then((result) => {
if (result && result.code === 200) {
return _processBankCards(result.data);
} else {
logger.error('get getBankCards data return code is not 200');
return '';
}
});
};
/**
* 获取订单号
*
* @param 参数
* orderCode 订单号
*
* @returns {*}
*/
const getInstallmentOrderDetail = (params) => {
const method = 'app.SpaceOrders.installDetail';
return api.get('', {
method: method,
uid: params.uid,
order_code: params.orderCode,
debug: 'XYZ'
}, {
timeout: API_TIMEOUT
}).then((result)=> {
return Promise.resolve(camelCase(result));
});
};
/**
* 计算总金额
*
* @param params 金额
*
* @returns {*|Promise.<TResult>}
*/
const totalAmount = (params) => {
const method = 'app.order.calPrice';
return api.get('', {
method: method,
prices: params,
debug: 'XYZ'
}, {
timeout: API_TIMEOUT
}).then((result)=> {
return Promise.resolve(camelCase(result));
});
};
const checkVerifyCode = (uid, mobile, code) => {
const method = 'user.instalment.validateSnsCheckCode';
return api.get('', {
uid: uid,
method: method,
mobile: mobile,
snsCheckCode: code,
debug: 'XYZ'
}, {
timeout: API_TIMEOUT
}).then((result)=> {
return Promise.resolve(camelCase(result));
});
};
module.exports = {
getStauts,
getQueryCreditInfo,
getQueryAmtInfo,
getSearchIntallment,
sendVerifyCode,
activateService,
getResources,
getQueryAmtList,
getQueryRePayList,
getBankCards,
getBankInfo,
getInstallmentOrders,
getInstallmentOrderDetail,
totalAmount,
checkVerifyCode
};
... ...
{
"app.SpaceOrders.getInstallment": {
"code": 200,
"data": {
"order_list": [
{
"order_code": "1519273880",
"uid": "5772257",
"create_time": "2016/08/01",
"amount": "¥100.00",
"buy_total": 1,
"install_status": "已还清",
"order_goods": [
{
"buy_number": 1,
"goods_image": "http://img10.static.yhbimg.com/goodsimg/2014/06/19/02/0118c2721e4f2219f69539dd389f4bacac.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80",
"goods_price": "¥1.00",
"goods_amount": "¥1.00",
"product_id": 84230,
"product_name": "004 满版龙纹迷彩七分休闲裤",
"cn_alphabet": "004QiFenBuKu004PA095",
"product_skn": 51055461
}
]
},
{
"order_code": "1519273880",
"uid": "5772257",
"create_time": "2016/08/01",
"amount": "¥100.00",
"buy_total": 1,
"install_status": "已还清",
"order_goods": [
{
"buy_number": 1,
"goods_image": "http://img10.static.yhbimg.com/goodsimg/2014/06/19/02/0118c2721e4f2219f69539dd389f4bacac.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80",
"goods_price": "¥1.00",
"goods_amount": "¥1.00",
"product_id": 84230,
"product_name": "004 满版龙纹迷彩七分休闲裤",
"cn_alphabet": "004QiFenBuKu004PA095",
"product_skn": 51055461
}
]
},
{
"order_code": "1519273880",
"uid": "5772257",
"create_time": "2016/08/01",
"amount": "¥100.00",
"buy_total": 1,
"install_status": "已还清",
"order_goods": [
{
"buy_number": 1,
"goods_image": "http://img10.static.yhbimg.com/goodsimg/2014/06/19/02/0118c2721e4f2219f69539dd389f4bacac.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80",
"goods_price": "¥1.00",
"goods_amount": "¥1.00",
"product_id": 84230,
"product_name": "004 满版龙纹迷彩七分休闲裤",
"cn_alphabet": "004QiFenBuKu004PA095",
"product_skn": 51055461
}
]
},
{
"order_code": "1519273880",
"uid": "5772257",
"create_time": "2016/08/01",
"amount": "¥100.00",
"buy_total": 1,
"install_status": "已还清",
"order_goods": [
{
"buy_number": 1,
"goods_image": "http://img10.static.yhbimg.com/goodsimg/2014/06/19/02/0118c2721e4f2219f69539dd389f4bacac.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80",
"goods_price": "¥1.00",
"goods_amount": "¥1.00",
"product_id": 84230,
"product_name": "004 满版龙纹迷彩七分休闲裤",
"cn_alphabet": "004QiFenBuKu004PA095",
"product_skn": 51055461
}
]
},{
"order_code": "1519273880",
"uid": "5772257",
"create_time": "2016/08/01",
"amount": "¥100.00",
"buy_total": 1,
"install_status": "已还清",
"order_goods": [
{
"buy_number": 1,
"goods_image": "http://img10.static.yhbimg.com/goodsimg/2014/06/19/02/0118c2721e4f2219f69539dd389f4bacac.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80",
"goods_price": "¥1.00",
"goods_amount": "¥1.00",
"product_id": 84230,
"product_name": "004 满版龙纹迷彩七分休闲裤",
"cn_alphabet": "004QiFenBuKu004PA095",
"product_skn": 51055461
}
]
},{
"order_code": "1519273880",
"uid": "5772257",
"create_time": "2016/08/01",
"amount": "¥100.00",
"buy_total": 1,
"install_status": "已还清",
"order_goods": [
{
"buy_number": 1,
"goods_image": "http://img10.static.yhbimg.com/goodsimg/2014/06/19/02/0118c2721e4f2219f69539dd389f4bacac.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80",
"goods_price": "¥1.00",
"goods_amount": "¥1.00",
"product_id": 84230,
"product_name": "004 满版龙纹迷彩七分休闲裤",
"cn_alphabet": "004QiFenBuKu004PA095",
"product_skn": 51055461
}
]
},{
"order_code": "1519273880",
"uid": "5772257",
"create_time": "2016/08/01",
"amount": "¥100.00",
"buy_total": 1,
"install_status": "已还清",
"order_goods": [
{
"buy_number": 1,
"goods_image": "http://img10.static.yhbimg.com/goodsimg/2014/06/19/02/0118c2721e4f2219f69539dd389f4bacac.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80",
"goods_price": "¥1.00",
"goods_amount": "¥1.00",
"product_id": 84230,
"product_name": "004 满版龙纹迷彩七分休闲裤",
"cn_alphabet": "004QiFenBuKu004PA095",
"product_skn": 51055461
}
]
},{
"order_code": "1519273880",
"uid": "5772257",
"create_time": "2016/08/01",
"amount": "¥100.00",
"buy_total": 1,
"install_status": "已还清",
"order_goods": [
{
"buy_number": 1,
"goods_image": "http://img10.static.yhbimg.com/goodsimg/2014/06/19/02/0118c2721e4f2219f69539dd389f4bacac.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80",
"goods_price": "¥1.00",
"goods_amount": "¥1.00",
"product_id": 84230,
"product_name": "004 满版龙纹迷彩七分休闲裤",
"cn_alphabet": "004QiFenBuKu004PA095",
"product_skn": 51055461
}
]
},{
"order_code": "1519273880",
"uid": "5772257",
"create_time": "2016/08/01",
"amount": "¥100.00",
"buy_total": 1,
"install_status": "已还清",
"order_goods": [
{
"buy_number": 1,
"goods_image": "http://img10.static.yhbimg.com/goodsimg/2014/06/19/02/0118c2721e4f2219f69539dd389f4bacac.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80",
"goods_price": "¥1.00",
"goods_amount": "¥1.00",
"product_id": 84230,
"product_name": "004 满版龙纹迷彩七分休闲裤",
"cn_alphabet": "004QiFenBuKu004PA095",
"product_skn": 51055461
}
]
},{
"order_code": "1519273880",
"uid": "5772257",
"create_time": "2016/08/01",
"amount": "¥100.00",
"buy_total": 1,
"install_status": "已还清",
"order_goods": [
{
"buy_number": 1,
"goods_image": "http://img10.static.yhbimg.com/goodsimg/2014/06/19/02/0118c2721e4f2219f69539dd389f4bacac.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80",
"goods_price": "¥1.00",
"goods_amount": "¥1.00",
"product_id": 84230,
"product_name": "004 满版龙纹迷彩七分休闲裤",
"cn_alphabet": "004QiFenBuKu004PA095",
"product_skn": 51055461
}
]
}
],
"page": 1,
"page_total": 27,
"total": 265
},
"message": "ok"
},
"app.SpaceOrders.installDetail": {
"alg": "SALT_MD5",
"code": 200,
"data": {
"amount": "¥690.00",
"order_code": "1519816409",
"create_time": "2016-08-01",
"avg_fee_amt":"¥0.00/期",
"avg_principal_amt":"¥599.00/期",
"total_amt":"¥2120.00",
"order_goods": [
{
"buy_number": "2",
"goods_amount": "¥690.00",
"goods_image": "http://img13.static.yhbimg.com/goodsimg/2014/11/27/09/0222a041d7f9c17479a684e9e7e4efeac2.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80",
"goods_price": "¥345.00",
"product_name": "5 PREVIEW 人物趣味印花圆领T恤衫 (通用款)"
}
],
"package_list": [
{
"curr_amt": "¥599.00",
"curr_principal_amt": "¥590.00",
"curr_fee_amt": "¥5.00",
"curr_dealy_fee_amt": "¥4.00",
"desc":"已还款",
"curr_date":"【1/3期】",
"sort_id":"1"
},
{
"curr_amt": "¥599.00",
"curr_principal_amt": "¥590.00",
"curr_fee_amt": "¥5.00",
"curr_dealy_fee_amt": "¥4.00",
"desc":"逾期2天",
"curr_date":"【1/3期】",
"sort_id":"1"
}
]
},
"md5": "93833c6c94b5da93af49f4591a166147",
"message": "OK"
}
}
... ...
/**
* router of sub app product
* @author: weiqingting<qingting.wei@yoho.cn>
* @date: 2016/05/06
* router of sub app home
* @author: Bi Kai<kai.bi@yoho.cn>
* @date: 2016/05/09
*/
'use strict';
const express = require('express');
const router = express.Router(); // eslint-disable-line
const cRoot = './controllers';
const installment = require(cRoot + '/installment');
const router = express.Router(); // eslint-disable-line
const personalController = require(`${cRoot}/qrcode`);
// 查看二维码
router.get('/QRcode/:id', personalController.QRcode);
// 分期付款
router.get('/installment/index', installment.index);// 开通分期首页
router.get('/installment/review', installment.review); // 开通分期首页
router.get('/installment/starting-service', installment.startingService); // 分期付款开通
router.get('/installment/starting-service/verify-code', installment.verifyCode);
router.get('/installment/starting-service/check-verify-code', installment.checkVerifyCode);
router.get('/installment/get-goods', installment.getInstallmentGoods); // ajax请求分期专享商品数据
router.get('/installment/repay/overdue', installment.overdueList); // 逾期未还款列表
router.get('/installment/repay/7daylist', installment.sevenDayList); // 7日待还款列表
router.get('/installment/repay/month', installment.monthRepayList); // 本月待还款列表
router.get('/installment/repay/total', installment.totalRepayList); // 待还总金额列表
router.get('/installment/repay/record', installment.repayRecordPage); // 还款记录
router.get('/installment/repay/get-record', installment.getRepayRecord); // ajax请求还款记录
router.get('/installment/repay/detail', installment.repayDetail); // 还款详情
router.get('/installment/account', installment.account); // 账户管理
router.get('/installment/bank-info', installment.getBankInfo);
router.post('/installment/activate-service', installment.activateService);
router.get('/installment/order', installment.orderIndex);
router.get('/installment/order.html', installment.orderList);
router.get('/installment/order/:id', installment.orderDetail);
router.get('/installment/total-amount.json', installment.totalAmount);
router.get('/installment/help', installment.help);// 帮助静态页面
router.get('/installment/agreement', installment.agreement);// 服务协议\服务条款静态页面
module.exports = router;
... ...
<div class="account-page">
<ul class="account-list">
{{#accountList}}
<li>我的银行卡:<div class="list-right">{{cardNo}}</div></li>
{{/accountList}}
</ul>
</div>
\ No newline at end of file
... ...