Commit 3d37542f authored by Hsinli's avatar Hsinli

Merge branch 'master' of t-git.51gjj.com:fangbin/51business

parents b93f9b76 beb64607
Pipeline #8796 passed with stage
in 12 seconds
...@@ -9,13 +9,14 @@ class ProductController extends Controller { ...@@ -9,13 +9,14 @@ class ProductController extends Controller {
const { ctx } = this; const { ctx } = this;
const type = ctx.params.type || 'credit'; const type = ctx.params.type || 'credit';
const keys = ctx.query.keys ? eval(ctx.query.keys) : []; const keys = ctx.query.keys ? ctx.query.keys.split(',') : [];
const results = await ctx.service.gjj.product.getRecommendOptions(type, keys); const results = await ctx.service.gjj.product.getRecommendOptions(type, keys);
let suit_options; let suit_options;
let credit_loans = []; let credit_loans = [];
let common_loans = []; let common_loans = [];
let common_credits = []; let common_credits = [];
if (type == 'loan') { let credit_cards = [];
if (type === 'loan') {
suit_options = await ctx.service.gjj.product.getLoanChannelSuitOptions(); suit_options = await ctx.service.gjj.product.getLoanChannelSuitOptions();
if (suit_options) { if (suit_options) {
results.unshift(suit_options); results.unshift(suit_options);
...@@ -23,10 +24,11 @@ class ProductController extends Controller { ...@@ -23,10 +24,11 @@ class ProductController extends Controller {
credit_loans = await ctx.service.gjj.product.getAllProductsByType(1); credit_loans = await ctx.service.gjj.product.getAllProductsByType(1);
common_loans = await ctx.service.gjj.product.getAllProductsByType(4); common_loans = await ctx.service.gjj.product.getAllProductsByType(4);
} else { } else {
credit_cards = await ctx.service.gjj.product.getAllProductsByType(2);
common_credits = await ctx.service.gjj.product.getAllProductsByType(3); common_credits = await ctx.service.gjj.product.getAllProductsByType(3);
} }
ctx.success({ results: results, credit_loans: credit_loans, common_loans: common_loans, common_credits: common_credits }); ctx.success({ results, credit_loans, common_loans, common_credits, credit_cards });
} }
} }
module.exports = ProductController; module.exports = ProductController;
...@@ -39,6 +39,36 @@ module.exports = { ...@@ -39,6 +39,36 @@ module.exports = {
} }
}, },
isEmpty(data) {
const type = typeof (data);
let ret = false;
switch (type) {
case 'undefined':
ret = true;
break;
case 'string':
ret = !(data.length > 0);
break;
case 'number':
ret = !(data > 0);
break;
case 'boolean':
ret = false;
break;
case 'object':
// 判断是否为数组
if (data instanceof Array) {
ret = !(data.length > 0);
} else {
ret = JSON.stringify(data) === '{}' || !data;
}
break;
default:
break;
}
return ret;
},
login(params) { login(params) {
const { cookies, session } = this; const { cookies, session } = this;
const moment = require('moment'); const moment = require('moment');
......
...@@ -4,6 +4,6 @@ module.exports = app => { ...@@ -4,6 +4,6 @@ module.exports = app => {
const router = app.router.namespace(app.config.projectRootPath + '/product'); const router = app.router.namespace(app.config.projectRootPath + '/product');
router.get('/recommend/channel/options/:type', 'gjj.product.getOptionProducts');// router.get('third_options', '/recommend/channel/options/:type', 'gjj.product.getOptionProducts');//
}; };
'use strict'; 'use strict';
const Service = require('egg').Service; const Service = require('egg').Service;
const R = require('ramda') const R = require('ramda');
class ProductService extends Service { class ProductService extends Service {
//通过 type=loan/credit 频道 option_ids 筛选项数组 all_product_ids // 通过 type=loan/credit 频道 option_ids 筛选项数组 all_product_ids
// async getProductsByOptions(option_ids, all_product_ids = []) { // async getProductsByOptions(option_ids, all_product_ids = []) {
// const { ctx } = this; // const { ctx } = this;
...@@ -25,34 +25,34 @@ class ProductService extends Service { ...@@ -25,34 +25,34 @@ class ProductService extends Service {
// return product_ids; // return product_ids;
// } // }
//获取筛选项列表 带 product_id // 获取筛选项列表 带 product_id
//return : [{"id":3,"title":"银行分类","description":"信用卡银行分类","_children":[{"id":12,"title":"招商银行","quality":["1"],"normal":["1"]},{"id":16,"title":"广发银行","quality":[],"normal":[]}]},{"id":4,"title":"主题分类","description":"信用卡主题分类","_children":[]},{"id":5,"title":"信用卡筛选项","description":"信用卡筛选项","_children":[]}] // return : [{"id":3,"title":"银行分类","description":"信用卡银行分类","_children":[{"id":12,"title":"招商银行","quality":["1"],"normal":["1"]},{"id":16,"title":"广发银行","quality":[],"normal":[]}]},{"id":4,"title":"主题分类","description":"信用卡主题分类","_children":[]},{"id":5,"title":"信用卡筛选项","description":"信用卡筛选项","_children":[]}]
async getRecommendOptions(type = 'credit', keys = []) { async getRecommendOptions(type = 'credit', keys = []) {
const { ctx } = this; const { ctx } = this;
// const type = 'credit'; // const type = 'credit';
// keys = ['loan_organization', 'loan_filter', 'credit_bank', 'credit_theme', 'credit_filter']; // keys = ['loan_organization', 'loan_filter', 'credit_bank', 'credit_theme', 'credit_filter'];
// console.info(keys); // console.info(keys);
let classifies = await ctx.blockModel.RecommendChannelClassify.all({ where: { type: type, valid: 1, key: { $in: keys } } }); let classifies = await ctx.blockModel.RecommendChannelClassify.all({ where: { type, valid: 1, key: { $in: keys } } });
// return classifies; // return classifies;
classifies = R.project(['id', 'key', 'title', 'description'])(classifies); classifies = R.project([ 'id', 'key', 'title', 'description' ])(classifies);
const classify_ids = R.pluck('id', classifies); const classify_ids = R.pluck('id', classifies);
let options = await ctx.blockModel.RecommendChannelOption.all({ where: { classify_id: { $in: classify_ids }, status: 'online', valid: 1 }, order: [['order', 'asc']] }); let options = await ctx.blockModel.RecommendChannelOption.all({ where: { classify_id: { $in: classify_ids }, status: 'online', valid: 1 }, order: [[ 'order', 'asc' ]] });
options = R.project(['id', 'title', 'classify_id'])(options); options = R.project([ 'id', 'title', 'classify_id' ])(options);
const option_ids = R.pluck('id', options); const option_ids = R.pluck('id', options);
let option_products = await ctx.blockModel.RecommendChannelProduct.all({ where: { option_id: { $in: option_ids }, valid: 1 }, order: [['order', 'asc']] }); let option_products = await ctx.blockModel.RecommendChannelProduct.all({ where: { option_id: { $in: option_ids }, valid: 1 }, order: [[ 'order', 'asc' ]] });
option_products = R.project(['option_id', 'product_id', 'type'])(option_products); option_products = R.project([ 'option_id', 'product_id', 'type' ])(option_products);
let ret = []; const ret = [];
for (let i in classifies) { for (const i in classifies) {
let classify = classifies[i]; const classify = classifies[i];
const classify_id = classify.id; const classify_id = classify.id;
let temp_options = options.filter(option => parseInt(option.classify_id) === parseInt(classify_id)); let temp_options = options.filter(option => parseInt(option.classify_id) === parseInt(classify_id));
temp_options = R.project(['id', 'title'])(temp_options); temp_options = R.project([ 'id', 'title' ])(temp_options);
for (let j in temp_options) { for (const j in temp_options) {
let temp_option = temp_options[j]; const temp_option = temp_options[j];
const temp_option_id = temp_option.id; const temp_option_id = temp_option.id;
let temp_option_products = option_products.filter(v => parseInt(v.option_id) === parseInt(temp_option_id)); let temp_option_products = option_products.filter(v => parseInt(v.option_id) === parseInt(temp_option_id));
temp_option_products = R.groupBy(v => { return v.type === 'quality' ? 'quality' : 'normal' })(temp_option_products); temp_option_products = R.groupBy(v => { return v.type === 'quality' ? 'quality' : 'normal'; })(temp_option_products);
const quality_product_ids = R.pluck('product_id', temp_option_products.quality ? temp_option_products.quality : []); const quality_product_ids = R.pluck('product_id', temp_option_products.quality ? temp_option_products.quality : []);
const normal_product_ids = R.pluck('product_id', temp_option_products.normal ? temp_option_products.normal : []); const normal_product_ids = R.pluck('product_id', temp_option_products.normal ? temp_option_products.normal : []);
temp_options[j].quality = quality_product_ids; temp_options[j].quality = quality_product_ids;
...@@ -82,13 +82,13 @@ class ProductService extends Service { ...@@ -82,13 +82,13 @@ class ProductService extends Service {
title: '全部', title: '全部',
description: '全部', description: '全部',
_children: [], _children: [],
} };
let children = [{ id: -2, title: '全部', quality: credit_loans_ids, normal: common_loans_ids, }]; const children = [{ id: -2, title: '全部', quality: credit_loans_ids, normal: common_loans_ids }];
if (user_id && app_user_id) { if (user_id && app_user_id) {
children.push({ children.push({
id: -3, id: -3,
title: '最适合您的', title: '最适合您的',
quality: [7, 8, 29],//TODO quality: [ 7, 8, 29 ], // TODO
normal: [], normal: [],
}); });
...@@ -111,25 +111,283 @@ class ProductService extends Service { ...@@ -111,25 +111,283 @@ class ProductService extends Service {
return default_options; return default_options;
} }
//1 授信贷;2 信用卡;3 普通信用卡;4 普通贷款 // 1 授信贷;2 信用卡;3 普通信用卡;4 普通贷款
async getAllProductsByType(type, elements = ['business_id', 'logo', 'url', 'name', 'alias', 'rate', 'rate_title', 'min_amount', 'max_amount','feature','abridge_desc','recommend_sorter']) { async getAllProductsByType(type) {
const { ctx } = this; const { ctx } = this;
const url = this.config.NODE_BASE_URL + '/cassandra-server/bu_basic/list?type=' + type; const url = this.config.NODE_BASE_URL + '/cassandra-server/bu_basic/list?type=' + type;
let products_results = await ctx.helper.send_request(url, {}, { method: 'GET' }); const products_results = await ctx.helper.send_request(url, {}, { method: 'GET' });
ctx.logger.info(JSON.stringify({ url: url, products_results: products_results })); //ctx.logger.info(JSON.stringify({ url, products_results }));
if (type !== 1) {
console.log(products_results.data.ret);
}
let products = []; let products = [];
if (products_results.status !== 200 || !products_results.data || !products_results.data.ret) { if (products_results.status !== 200 || !products_results.data || !products_results.data.ret) {
return products; return products;
} }
products = products_results.data.ret; products = products_results.data.ret;
if (elements && elements.length !== 0) {
products = R.project(elements)(products);
}
return products; return products;
} }
// 最适合您的(推荐)贷款
async getSuitLoans() {
const ret = await this.getBusinessRecommendList();
return ret;
}
// 获取用户贷款分组
async getBusinessClassList() {
const { ctx } = this;
const userId = '58f1af6b-9018-4d69-b839-a26aaa368445';
const appUserId = '9CEB848A-2F87-4C5F-A50A-D65882C04CA6';
const url = ctx.app.config.NODE_BASE_URL + '/cassandra-server/loan_list/bu_class/list';
const param = { uid: userId, app_uid: appUserId };
const resp = await ctx.helper.send_request(url, param, { method: 'GET' });
if (resp.status !== 200) {
ctx.failed('[bu_class]内部接口错误');
}
return resp.data.ret;
}
// 获取用户贷款通过率
async getBusinessPassRate(businessList) {
const { ctx } = this;
const userId = '58f1af6b-9018-4d69-b839-a26aaa368445';
const appUserId = '9CEB848A-2F87-4C5F-A50A-D65882C04CA6';
const url = ctx.app.config.NODE_BASE_URL + '/cassandra-server/loan_list/recommend_sort/list';
const param = { uid: userId, app_uid: appUserId, business_arr: businessList };
// 判断列表是否为空
if (businessList.length === 0) {
return [];
}
const resp = await ctx.helper.send_request(url, param, { method: 'POST' });
if (resp.status !== 201) {
ctx.failed('[recommend_sort]内部接口错误');
}
return resp.data.ret;
}
// 获取普通贷款
async getCommonBusinessList() {
const { ctx } = this;
const url = ctx.app.config.NODE_BASE_URL + '/cassandra-server/bu_basic/list';
const param = { type: 3 };
const resp = await ctx.helper.send_request(url, param, { method: 'GET' });
if (resp.status !== 200) {
ctx.failed('[bu_basic]内部接口错误');
}
return resp.data.ret;
}
// 获取推荐列表,按照ABC分组顺序推荐
async getBusinessRecommendList() {
const { ctx, service } = this;
const userId = ctx.userId;
const R = require('ramda');
const limit = 10;
const recommendSort = function(a, b) {
return a.recommend_sorter - b.recommend_sorter;
};
let ret = [];
// 如果未登录,则推荐普通贷款
if (ctx.isEmpty(userId)) {
const commonLoanList = await this.getCommonBusinessList();
ret = this.formatLoanList(R.take(limit)(R.sort(recommendSort)(commonLoanList)));
} else {
// 先获取分组
const classList = await this.getBusinessClassList();
let result = [];
// 先取A
if (classList.hasOwnProperty('class_A') && !ctx.isEmpty(classList.class_A)) {
result = R.take(limit)(R.sort(recommendSort)(classList.class_A));
result = R.map(function(e) {
e.class = 'A';
e.rule = 2;
return e;
})(result);
}
// 再取B
if (classList.hasOwnProperty('class_B') && !ctx.isEmpty(classList.class_B)) {
let classB = R.take(limit - result.length)(R.sort(recommendSort)(classList.class_B));
classB = R.map(function(e) {
e.class = 'B';
e.rule = 2;
return e;
})(classB);
result = R.insertAll(result.length, classB)(result);
}
// 如果数量不够,再取C
if (result.length < limit) {
// 判断C组是否有值
if (classList.hasOwnProperty('class_C') && !ctx.isEmpty(classList.class_C)) {
// 获取开关状态,判断是自动推荐or手动推荐orABTest
const rule = 1;
// 是否需要计算模型
let isNeedCalc = false;
let userInfo = {};
switch (rule) {
case 1:
isNeedCalc = true;
break;
case 2:
isNeedCalc = false;
classList.class_C = R.map(function(e) {
e.class = 'C';
e.rule = rule;
e.hasPassRate = 0;
e.needCalc = 0;
return e;
})(classList.class_C);
break;
case 3:
// 获取不到手机号则默认使用手动推荐
isNeedCalc = false;
// 获取用户手机号,根据手机号末尾判断是否自动推荐
userInfo = await service.user.get_user(ctx.appUserId);
if (!ctx.isEmpty(userInfo) && !ctx.isEmpty(userInfo.user) && !ctx.isEmpty(userInfo.user.phone) && (Number(userInfo.user.phone) % 2 === 0)) {
isNeedCalc = true;
} else {
classList.class_C = R.map(function(e) {
e.class = 'C';
e.rule = rule;
e.hasPassRate = 0;
e.needCalc = 0;
return e;
})(classList.class_C);
}
break;
default:
break;
}
if (isNeedCalc) {
const businessArr = R.pluck('business_id')(classList.class_C);
const passRateList = await this.getBusinessPassRate(businessArr);
if (!ctx.isEmpty(passRateList)) {
classList.class_C = R.map(function(e) {
e.class = 'C';
e.rule = rule;
e.hasPassRate = 0;
e.needCalc = 1;
e.pass_rate = 0;
return e;
})(classList.class_C);
for (const v of passRateList) {
for (let i = 0; i < classList.class_C.length; i++) {
if (Number(classList.class_C[i].business_id) === Number(v.business_id)) {
classList.class_C[i].pass_rate = v.pass_rate;
classList.class_C[i].hasPassRate = 1;// 是否有通过率返回值
break;
}
}
}
// 根据通过率降序排序
const passRateSort = function(a, b) {
return b.pass_rate - a.pass_rate;
};
classList.class_C = R.sort(passRateSort)(classList.class_C);
} else {
// 模型计算参数不够,仍然采用手动推荐
classList.class_C = R.map(function(e) {
e.class = 'C';
e.rule = rule;
e.hasPassRate = 0;
e.needCalc = 1;
return e;
})(classList.class_C);
isNeedCalc = false;
}
}
if (!isNeedCalc) {
const tmp = R.sort(recommendSort)(classList.class_C);
classList.class_C = tmp;
}
}
}
result = R.insertAll(result.length, R.take(limit - result.length)(classList.class_C))(result);
ret = await this.formatLoanList(result);
}
return ret;
}
async formatLoanList(loanList) {
const ret = [];
for (const v of loanList) {
// 处理C组
let url = v.url;
if (v.hasOwnProperty('class')) {
if (url.includes('?')) {
url = url + '&class=' + v.class + '&rule=' + String(v.rule);
} else {
url = url + '?class=' + v.class + '&rule=' + String(v.rule);
}
if (v.class === 'C') {
url = url + '&hasPassRate=' + String(v.hasPassRate) + '&needCalc=' + String(v.needCalc) + '&passRate=' + String(v.pass_rate);
}
}
const tmp = {
type: v.type,
business_id: v.business_id,
name: v.name,
logo: v.logo,
alias: v.alias,
rate_title: v.rate_title,
rate: v.rate,
min_amount: v.min_amount,
max_amount: v.max_amount,
sort: v.recommend_sorter,
state: v.state,
url,
business_notice: v.business_notice,
feature: v.feature,
abridge_desc: v.abridge_desc,
};
ret.push(tmp);
}
return ret;
}
async formatProducts(products) {
const ret = [];
for (const v of products) {
const rate = v.rate.includes('元') ? v.rate : v.rate + '%';
const channel_rate = v.channel_rate.includes('元') ? v.channel_rate : v.channel_rate + '%';
let url = v.url;
if (Number(v.type) === 4) {
url = url.includes('?') ? url + '&from=51gjj_loan_channel' : url + '?from=51gjj_loan_channel';
}
url = this.config.PHP_URL + '/app/track/url?url=' + encodeURI(url) + '&source=51gjj&location=loan_channel';
const tmp = {
type: v.type,
business_id: v.business_id,
name: v.name,
logo: v.logo,
alias: v.alias,
rate_title: v.rate_title,
rate: v.rate,
min_amount: v.min_amount,
max_amount: v.max_amount,
sort: v.recommend_sorter,
state: v.state,
url,
business_notice: v.business_notice,
feature: v.feature,
abridge_desc: v.abridge_desc,
};
ret.push(tmp);
}
return ret;
}
} }
module.exports = ProductService; module.exports = ProductService;
...@@ -90,13 +90,13 @@ module.exports = appInfo => { ...@@ -90,13 +90,13 @@ module.exports = appInfo => {
}; };
config.PHP_URL = 'https://kaifa.jianbing.com'; config.PHP_URL = 'https://kaifa.jianbing.com';
config.NODE_URL = 'https://dev-nginx.jianbing.com/user_api/v1'; config.NODE_URL = 'https://uat-nginx.jianbing.com/user_api/v1';
config.NODE_BASE_URL = 'https://dev-nginx.jianbing.com'; config.NODE_BASE_URL = 'https://uat-nginx.jianbing.com';
config.HOUSE_SERVICE_API = 'https://uat-nginx.jianbing.com/house-service'; config.HOUSE_SERVICE_API = 'https://uat-nginx.jianbing.com/house-service';
config.CDN_BASE_URL = 'https://r.51gjj.com/image/'; config.CDN_BASE_URL = 'https://r.51gjj.com/image/';
config.USER_CENTER_API_URI = 'https://dev-nginx.jianbing.com/usercenter-service'; config.USER_CENTER_API_URI = 'https://uat-nginx.jianbing.com/usercenter-service';
return config; return config;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment