Commit 8e13891a authored by 刘松's avatar 刘松

complex things

parent 0d27eb51
......@@ -15,6 +15,7 @@ const sessions = require('./db/mongo/session');
const cron = require('./lib/cron');
const cron_gather = require('./lib/cron_gather');
const cron_auto = require('./lib/cron_auto');
const control = require('./lib/controller');
const options = {
useMongoClient: true
......@@ -23,7 +24,25 @@ const options = {
mongoose.connect(config.mongo, options);
console.log('MONGO CONNECT INFO: ', config.mongo);
global.Promise = mongoose.Promise = require('bluebird');
var expressWs = require('express-ws')(app);
app.ws('/echo', function(ws, req) {
console.dir('inini');
ws.on('message', function(msg) {
console.log(msg);
control.getDynamic(function(data){
try{
ws.send(data);
}catch(e){
console.dir(e);
}
});
});
ws.on('close', function(msg) {
console.log(msg);
//
});
});
app.use(logger('dev'));
app.use(cookieParser('f7f926ad3fc8fe90eb4784643bc4d747'));
app.use(session({secret: '5f1d65f27e370c36dfd845f6dc78b869'}));
......@@ -52,6 +71,8 @@ app.use(routes);
app.listen(config.port);
console.log(process.env.NODE_ENV + 'server started on port ' + config.port);
if(process.env.NODE_ENV === 'production'){
cron.start();
cron_gather.start();
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -14,7 +14,7 @@ class Agent extends React.Component{
constructor(props){
super(props);
console.dir(this.props);
this.state= { links :[],status:'pending',pagination:{current:1,pageSize:500,total:1},modalVisible:false,username:'channel'};
this.state= { links :[],status:'pending',pagination:{current:1,pageSize:500,total:1},modalVisible:false,username:'channel',target:'target'};
}
componentDidMount(){
let pagination = this.state.pagination;
......@@ -57,6 +57,7 @@ class Agent extends React.Component{
for(var k in values){
if(values[k]) values[k] = values[k].trim();
}
values['select'] = this.state.target;
api('POST', 'link',values).then((res) => {
if(res && res.result){
self.setModalVisible(false);
......@@ -66,6 +67,11 @@ class Agent extends React.Component{
}
});
}
handleTargetChange(e){
console.dir(e.target.value);
this.props.form.resetFields && this.props.form.resetFields();
this.setState({target:e.target.value})
}
exportData(){
//json2xlsx(data,{sheetName:"基础数据", filename : '基础数据'+moment().format('YYYYMMDD')+'.xlsx'});
......@@ -169,6 +175,15 @@ class Agent extends React.Component{
onCancel = { this.setModalVisible.bind(this,false) }
>
<Form onSubmit={this.handleSubmit.bind(this)} className="login-form">
<FormItem
label="推广目标"
>
<Radio.Group defaultValue="target" onChange={this.handleTargetChange.bind(this)}>
<Radio.Button value="target">二合一链接</Radio.Button>
<Radio.Button value="quan">优惠券链接</Radio.Button>
<Radio.Button value="good">商品链接</Radio.Button>
</Radio.Group>
</FormItem>
<FormItem>
{getFieldDecorator('name', {
rules: [{ required: true, message: '推广页名称不能为空' }],
......@@ -176,13 +191,15 @@ class Agent extends React.Component{
<Input prefix={<span style={{ fontSize: 13 }}>推广名称</span>} placeholder="例如:商品页1" />
)}
</FormItem>
<FormItem>
{getFieldDecorator('quan', {
rules: [{ required: true, message: '优惠券链接不能为空' }],
})(
<Input prefix={<span style={{ fontSize: 13 }}>优惠券链接</span>} placeholder="例如:https://taoquan.taobao.com/coupon/unify_apply.htm?sellerId=2194810505&activityId=5c3186407cd441d48ecd2460815793e6" />
)}
</FormItem>
{
this.state.target == 'good' ? <div></div> : <FormItem>
{getFieldDecorator('quan', {
rules: [{ required: true, message: '优惠券链接不能为空' }],
})(
<Input prefix={<span style={{ fontSize: 13 }}>优惠券链接</span>} placeholder="例如:https://taoquan.taobao.com/coupon/unify_apply.htm?sellerId=2194810505&activityId=5c3186407cd441d48ecd2460815793e6" />
)}
</FormItem>
}
<FormItem>
{getFieldDecorator('good', {
rules: [{ required: true, message: '商品连接不能为空' }],
......@@ -190,14 +207,23 @@ class Agent extends React.Component{
<Input prefix={<span style={{ fontSize: 13 }}>商品连接</span>} placeholder="例如:https://detail.tmall.com/item.htm?id=40663494639" />
)}
</FormItem>
<FormItem>
{
this.state.target == 'good' ? <div></div> : <FormItem>
{getFieldDecorator('pid', {
rules: [{ required: true, message: 'pid不能为空' }],
})(
<Input prefix={<span style={{ fontSize: 13 }}>PID</span>} placeholder="例如:mm_33320967_40070156_153504280" />
)}
</FormItem>
<FormItem
}
<FormItem>
{getFieldDecorator('title', {
rules: [{ required: false, message: '推广语不能为空' }],
})(
<Input prefix={<span style={{ fontSize: 13 }}>推广语</span>} placeholder="例如:推广语" />
)}
</FormItem>
{/* <FormItem
label="推广使用"
>
{getFieldDecorator('select')(
......@@ -206,7 +232,7 @@ class Agent extends React.Component{
<Radio value="target">二合一链接</Radio>
</RadioGroup>
)}
</FormItem>
</FormItem>*/}
<FormItem>
<Button type="primary" htmlType="submit" className="login-form-button">
一键生成
......
......@@ -5,19 +5,48 @@ import ReactEcharts from "echarts-for-react";
import json2xlsx from '../../js/json2xlsx';
import { Table, Icon, DatePicker, Alert, Row, Col, Spin, Button,Popover,Form,Input,Modal,InputNumber,message,Select} from 'antd';
import { Table, Icon, DatePicker, Alert, Row, Col, Spin, Button,Popover,Form,Input,Modal,InputNumber,message,Select,Tabs} from 'antd';
const FormItem = Form.Item;
const { RangePicker } = DatePicker;
const TabPane = Tabs.TabPane;
class Gather extends React.Component{
constructor(props){
super(props);
this.timeTicket = null;
//this._t = null;
this.count = 51;
this.dynamic = { sum:0,click:0 };
this.range = [moment().subtract(14,'days').format('YYYYMMDD'), moment().format('YYYYMMDD')];
this.state= { range:this.range,currentQd:'all',qds:[],qdgathers :[],status:'pending',pagination:{current:1,pageSize:20,total:100},username:'channel',tody:{ tkl_count:0,count:0,schedule_count:0} ,none_tkls:[],list:[],modalVisible:false,xAxis:[],series:[],loading:true};
this.state= { range:this.range,currentQd:'all',qds:[],qdgathers :[],status:'pending',pagination:{current:1,pageSize:20,total:100},username:'channel',tody:{ click_count:0,tkl_count:0,count:0,schedule_count:0} ,none_tkls:[],list:[],modalVisible:false,xAxis:[],series:[],loading:true,dynamicOption:this.getDynamicOption(),websocket:(("WebSocket" in window) || ('MozWebSocket' in window))};
}
fetchNewDate(date,_data0,_data1){
let axisData = (date).toLocaleTimeString().replace(/^\D*/,'');
let option = this.state.dynamicOption;
//option.title.text = 'Hello Echarts-for-react.' + new Date().getSeconds();
let data0 = option.series[0].data;
let data1 = option.series[1].data;
var sum = this.dynamic['sum'] != 0 ? _data0 - this.dynamic['sum'] : 0;
var click = this.dynamic['click'] != 0 ? _data1 - this.dynamic['click'] : 0;
// console.dir(this.dynamic);
this.dynamic['sum'] = _data0;
this.dynamic['click'] = _data1;
data0.shift();
data0.push(sum);
data1.shift();
data1.push(click);
option.xAxis[0].data.shift();
option.xAxis[0].data.push(axisData);
this.setState({dynamicOption: option});
}
componentDidMount(){
var self = this;
if (this.timeTicket) {
clearInterval(this.timeTicket);
}
//this.timeTicket = setInterval(this.fetchNewDate.bind(this), 1000);
//let data = { start:moment().add(-6,'days').format('YYYYMMDD'),end:moment().format('YYYYMMDD')};
if(document.cookie.match('username=[a-zA-Z0-9]+')[0] && document.cookie.match('username=[a-zA-Z0-9]+')[0].split('=')[1]){
this.setState({username:document.cookie.match('username=[a-zA-Z0-9]+')[0].split('=')[1]});
......@@ -29,12 +58,55 @@ class Gather extends React.Component{
}
api('GET', 'gathers/qd?skip=0&limit=20').then((res) => {
this.setState({qdgathers:res.result,status:'ready',none_tkls:res.none_tkls,list:res.list,pagination:{current:res.pagination.skip,pageSize:res.pagination.limit,total:res.pagination.total}});
this.handle(res.list,res.tkl_count)
this.handle(res.list,res.tkl_count,res.clickData);
});
if("WebSocket" in window || 'MozWebSocket' in window){
var url = 'ws://kouling-admin.v2.yoo.yunpro.cn/echo';
var ws = null;
if ('WebSocket' in window) {
ws = new WebSocket(url);
} else if ('MozWebSocket' in window) {
ws = new MozWebSocket(url);
}
ws.onopen = function()
{
// Web Socket 已连接上,使用 send() 方法发送数据
self.timeTicket = setInterval(function(){
ws.send("发送数据");
},5000);
console.dir("数据发送中...");
};
ws.onmessage = function (evt)
{
var data = evt.data.split(',');
if(data && data.length == 3){
var time = parseInt(data[2]);
var date = new Date();
date.setTime(time);
var sum = parseInt(data[0]) || 0;
var click_sum = parseInt(data[1]) || 0;
self.fetchNewDate(date,sum,click_sum);
}
};
ws.onclose = function()
{
// 关闭 websocket
console.dir("连接已关闭...");
};
ws.onerror = function(e)
{
// 关闭 websocket
console.dir(e);
console.dir("连接错误");
};
}
}
onChartReady(chart){
//console.dir('in onChartReady');
var self = this;
if(document.cookie.match('username=[a-zA-Z0-9]+')[0] && document.cookie.match('username=[a-zA-Z0-9]+')[0].split('=')[1]){
this.getCharts(this.range[0],this.range[1]);
......@@ -131,12 +203,140 @@ class Gather extends React.Component{
series : this.state.series
};
}
handle(list,tkl_count){
getDynamicOption(){
return {
title: {
text:'实时动态图',
},
tooltip: {
trigger: 'axis'
},
legend: {
data:['请求数', '点击数']
},
toolbox: {
show: true,
feature: {
dataView: {readOnly: false},
restore: {},
saveAsImage: {}
}
},
grid: {
top: 60,
left: 30,
right: 60,
bottom:30
},
dataZoom: {
show: false,
start: 0,
end: 100
},
visualMap: {
show: false,
min: 0,
color: ['#BE002F', '#F20C00', '#F00056', '#FF2D51', '#FF2121', '#FF4C00', '#FF7500',
'#FF8936', '#FFA400', '#F0C239', '#FFF143', '#FAFF72', '#C9DD22', '#AFDD22',
'#9ED900', '#00E500', '#0EB83A', '#0AA344', '#0C8918', '#057748', '#177CB0']
},
xAxis: [
{
type: 'category',
boundaryGap: true,
data: (function (){
let now = new Date();
let res = [];
let len = 50;
while (len--) {
res.unshift(now.toLocaleTimeString().replace(/^\D*/,''));
now = new Date(now - 2000);
}
return res;
})()
},
{
type: 'category',
boundaryGap: true,
data: (function (){
let res = [];
let len = 50;
while (len--) {
res.push(50 - len + 1);
}
return res;
})()
}
],
yAxis: [
{
type: 'value',
scale: true,
name: '点击数',
min: 0,
boundaryGap: [0.2, 0.2]
},{
type: 'value',
scale: true,
name: '请求数',
min: 0,
boundaryGap: [0.2, 0.2]
}
],
series: [
{
name:'请求数',
type:'bar',
xAxisIndex: 1,
yAxisIndex: 1,
itemStyle: {
normal: {
barBorderRadius: 4,
}
},
animationEasing: 'elasticOut',
animationDelay: function (idx) {
return idx * 10;
},
animationDelayUpdate: function (idx) {
return idx * 10;
},
data:(function (){
let res = [];
let len = 50;
while (len--) {
res.push(0);
}
return res;
})()
},
{
name:'点击数',
type:'line',
data:(function (){
let res = [];
let len = 50;
while (len--) {
res.push(0);
}
return res;
})()
}
]
}
}
handle(list,tkl_count,clickData){
var sum = 0;
var click_sum = 0;
for(var k in list){
sum += (list[k]['sum'] || 0);
}
this.setState({tody:{tkl_count:tkl_count,schedule_count:list.length,count:sum}});
for(var j in clickData){
click_sum += clickData[j]['sum'];
}
this.setState({tody:{tkl_count:tkl_count,schedule_count:list.length,count:sum,click_count:click_sum}});
}
......@@ -158,11 +358,8 @@ class Gather extends React.Component{
}
dc_timesChange(data,value){
console.dir(data);
console.dir(value);
var key = data['key'];
var qdgathers = this.state.qdgathers;
console.dir(qdgathers.length + '====');
qdgathers[key]['dc_times'] = value;
this.setState({qdgathers:qdgathers})
}
......@@ -414,9 +611,9 @@ class Gather extends React.Component{
</div>
</Col>
<Col span="6">
<div className = "box" style={{ marginLeft:'0px'}}>
<h3>投放计划</h3>
<span>{ this.state.tody.schedule_count }</span>
<div className = "box">
<h3>点击数</h3>
<span>{ this.state.tody.click_count }</span>
</div>
</Col>
{
......@@ -434,22 +631,36 @@ class Gather extends React.Component{
</div>
</Col>
}
<Col span="6">
<div className = "box">
<h3>淘口令数</h3>
<span>{ this.state.tody.tkl_count }</span>
<Col span="6">
<div className = "box" style={{ marginLeft:'0px'}}>
<h3>计划口令</h3>
<span>{ this.state.tody.schedule_count + ' / ' + this.state.tody.tkl_count }</span>
</div>
</Col>
</Row>
</div>
{ this.state.username == 'admin' ? <RangePicker onChange={ this.onChangeDate.bind(this) } value={ ranges } format={'YYYYMMDD'} style={{margin:'10px 0px'}}/> : <div></div> }
{ this.state.username == 'admin' ? <ReactEcharts
option={this.getOption()}
style={{height: '350px', width: '100%',margin:'20px 0px'}}
onChartReady={this.onChartReady.bind(this)}
loadingOption={this.getLoadingOption.bind(this)()}
showLoading={this.state.loading}
className='react_for_echarts' /> : <div></div>}
{this.state.username == 'admin' ? <Tabs
defaultActiveKey="1"
tabPosition={'left'}
style={{height: '406px', width: '100%',margin:'15px 0px'}}
>
<TabPane tab="每日走势" key="1" >
<RangePicker onChange={ this.onChangeDate.bind(this) } value={ ranges } format={'YYYYMMDD'} style={{margin:'10px 0px'}}/>
<ReactEcharts
option={this.getOption()}
style={{height: '350px', width: '100%',margin:'15px 0px'}}
onChartReady={this.onChartReady.bind(this)}
loadingOption={this.getLoadingOption.bind(this)()}
showLoading={this.state.loading}
className='react_for_echarts' />
</TabPane>
<TabPane tab="动态走势" key="2">
{this.state.websocket ? <ReactEcharts ref='echarts_react'
option={this.state.dynamicOption}
style={{height: 400,width: '100%',margin:'15px 0px'}} /> : <div>浏览器不支持</div>}
</TabPane>
</Tabs> : <div></div>}
<div style={{ overflow:"hidden"}}>
<Button type="primary" icon="export" onClick={ this.exportData.bind(this) } style={{ float:'right',margin:'10px 0px'}}>
导出数据
......
......@@ -9,7 +9,7 @@ const schema = mongoose.Schema({
},
quan: {
type: String,
required: true
required: false
},
good:{
type: String,
......@@ -17,7 +17,7 @@ const schema = mongoose.Schema({
},
pid:{
type: String,
required: true
required: false
},
title:{
type: String,
......
const mongoose = require('mongoose');
const {ObjectId} = mongoose.SchemaTypes;
const schema = mongoose.Schema({
info: {
type: String,
required: true
},
qd:{
type:ObjectId,
required: true,
ref:'tao-agent'
},
times: {
type:Number,
required: true
},
date:{
type:String,
required: true
},
}, {
timestamps: true
});
schema.index({qd: 1});
module.exports = mongoose.model('tao-log-click', schema);
......@@ -5,6 +5,7 @@ const moment = require('moment');
const Log = require('../db/mongo/tao-log');
const Kouling = require('../db/mongo/tao-kouling');
const Link = require('../db/mongo/tao-link');
const Click = require('../db/mongo/tao-log-click');
const Session = require('../db/mongo/session');
const Agent = require('../db/mongo/tao-agent');
const Schedule = require('../db/mongo/tao-schedule');
......@@ -105,7 +106,7 @@ exports.logs = async (req, res, next) => {
}
exports.getIps = async(req,res,next) => {
let now = moment().add(0,'hours').format('YYYYMMDD');
let now = moment().add(8,'hours').format('YYYYMMDD');
let { qd = 'all', date = now,limit = 100,skip = 0} = req.query;
if(!qd ) return res.status(400).send(not_full);
let sess = req.cookies['sess'];
......@@ -141,6 +142,8 @@ exports.getQdGatherData = async (req, res, next) => {
let options = {limit:parseInt(limit),skip:parseInt(skip)*limit,sort};
let sess = req.cookies['sess'];
var qs = {};
var clickData = {};
var date = moment().add(8,'hours').format('YYYYMMDD');
if(sess){
if(qd)
qs['qd'] = qd;
......@@ -148,7 +151,7 @@ exports.getQdGatherData = async (req, res, next) => {
var session_body = await Session.findById(sess);
if(session_body){
var user = session_body.toJSON().user;
var match_qs = {date:moment().format('YYYYMMDD')};
var match_qs = {date:date};
var kl_qs = {status:'use'};
//console.log(user);
var list = [];
......@@ -191,6 +194,23 @@ exports.getQdGatherData = async (req, res, next) => {
var schedule = await Schedule.findById(item._id).populate('qd','user');
list[i]['qd'] = schedule.toJSON().qd;
});
clickData = await Click.aggregate([
{
$match:{date:date}
},
{
$group:{
_id: "$qd",
sum:{ $sum:"$times" }
}
}
]);
clickData.forEach( async (item,i) => {
var qd = await Agent.findById(item._id).populate('qd','user');
//console.dir(qd.toJSON().user);
clickData[i]['_id'] = qd.toJSON().user;
});
none_tkls = await Nonetkl.find({date:match_qs['date']}).populate('qd','user').limit(1000);
}
console.dir(qs);
......@@ -198,7 +218,7 @@ exports.getQdGatherData = async (req, res, next) => {
let total = await GatherData.count(qs,null);
let tkl_count = await Kouling.count(kl_qs,null);
gatherDatas = gatherDatas.map(x => { var d = x.toJSON();d['date'] = dateFormat(d['date'],'YYYYMMDD'); return d;});
res.send({ status:'ok',result:gatherDatas,list:list,none_tkls:none_tkls,tkl_count:tkl_count,pagination:{total:total,skip:parseInt(skip)+1,limit:limit}});
res.send({ status:'ok',result:gatherDatas,clickData:clickData,list:list,none_tkls:none_tkls,tkl_count:tkl_count,pagination:{total:total,skip:parseInt(skip)+1,limit:limit}});
}
else{
res.status(400).send(not_login);
......@@ -209,6 +229,32 @@ exports.getQdGatherData = async (req, res, next) => {
}
exports.getDynamic = async(cb) => {
var date = moment().add(0,'hours').format('YYYYMMDD');
var list = await Log.aggregate([
{
$match:{date:date}
},
{
$group:{
_id:null,
sum:{ $sum:"$times" }
}
}]);
var clickData = await Click.aggregate([
{
$match:{date:date}
},
{
$group:{
_id:null,
sum:{ $sum:"$times" }
}
}]);
//console.dir({list:list,clickData});
cb && cb((list[0] && list[0].sum)+','+ (clickData[0]&&clickData[0].sum) +","+ moment().format('x'));
}
exports.putGatherData = async(req,res,next) => {
let { dc_times = 0,id} = req.body;
if(!id ) return res.status(400).send(not_full);
......@@ -320,24 +366,31 @@ exports.getLinks = async (req, res, next) => {
}
exports.createLink = async (req, res, next) => {
let {quan,pid,good,name,select = 'target'} = req.body;
if(!pid || !good || !quan || !name )
var {quan,pid,good,name,select = 'target'} = req.body;
console.dir((select == 'good' && !good) || (select == 'good' && (!pid || !good || !quan || !name)));
if((select == 'good' && !good) || (select != 'good' && (!pid || !good || !quan || !name)))
res.status(400).send(not_full);
else{
try {
let quanQuery = urlArgs(url.parse(quan).query);
let quanQuery = '';
if(select !== 'good' && quan) quanQuery = urlArgs(url.parse(quan).query);
let goodQuery = urlArgs(url.parse(good).query);
if(!pid || !goodQuery['id'])
console.dir(goodQuery);
if(!goodQuery['id'])
res.status(400).send(not_full);
else{
let goodInfo = await tao.getGood(goodQuery['id']);
let title = goodInfo['title'];
let goodInfo = await tao.getGood(goodQuery['id']);
let title = (req.body.title && req.body.title.length) ? req.body.title : goodInfo['title'];
let pic = goodInfo['pict_url'] || '';
let target = '';
console.dir(select);
if(select == 'target')
target = 'https://uland.taobao.com/coupon/edetail?activityId='+ quanQuery['activityId']+'&itemId='+goodQuery['id']+'&pid='+pid+'&dx=1&src=tkm_tkmwz';
else
if(select == 'good')
target = good;
if(select == 'quan')
target = quan;
console.dir(target);
tao.saveLink({name,quan,pid,good,title,pic,target},function(e,result){
if(e) throw e;
res.send({result:'ok',result:result})
......
......@@ -20,6 +20,7 @@
"es6-promise": "^4.0.5",
"eslint-plugin-react": "^7.1.0",
"express": "^4.14.1",
"express-ws": "^3.0.0",
"file-saver": "^1.3.3",
"jquery": "^3.2.1",
"js-cookie": "^2.1.4",
......
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