Authored by 邱骏

关键词批量导入,TDK批量导入

@@ -5,11 +5,14 @@ @@ -5,11 +5,14 @@
5 */ 5 */
6 6
7 const _ = require('lodash'); 7 const _ = require('lodash');
  8 +const fs = require('fs');
  9 +const Req = require('request-promise');
8 const moment = require('moment'); 10 const moment = require('moment');
9 const Router = require('koa-router'); 11 const Router = require('koa-router');
10 const r = new Router(); 12 const r = new Router();
11 const Mysql = require('../../../lib/mysql-promise'); 13 const Mysql = require('../../../lib/mysql-promise');
12 const pager = require('../utils/pager'); 14 const pager = require('../utils/pager');
  15 +const xlsx = require('xlsx');
13 16
14 const config = require('../../../config/config'); 17 const config = require('../../../config/config');
15 const singleBrandKeyPre = config.singleBrandKeyPre; 18 const singleBrandKeyPre = config.singleBrandKeyPre;
@@ -175,7 +178,59 @@ r.post('/save', async (ctx) => { @@ -175,7 +178,59 @@ r.post('/save', async (ctx) => {
175 178
176 result.code = 200; 179 result.code = 200;
177 return ctx.response.body = result; 180 return ctx.response.body = result;
178 -}) 181 +});
  182 +
  183 +r.post('/upload', async(ctx) => {
  184 + if (ctx.request.body._files) {
  185 + let file = ctx.request.body._files.up_excel;
  186 + const workbook = xlsx.readFile(file.path);
  187 + const sheetNames = workbook.Props.SheetNames;
  188 + const worksheet = workbook.Sheets[sheetNames[0]];
  189 +
  190 + let json_data = xlsx.utils.sheet_to_json(worksheet);
  191 + console.log(json_data);
  192 + let post_data = [];
  193 + _.each(json_data, (obj, index) => {
  194 + if (obj.keyword) {
  195 + post_data.push({
  196 + id: index,
  197 + keyword: obj.keyword,
  198 + describe: obj.describe || ''
  199 + })
  200 + }
  201 + });
  202 + console.log(post_data);
  203 + if (post_data.length > 0) {
  204 + return Req({
  205 + method: 'POST',
  206 + uri: 'http://spiderwebhook.yoho.cn/importApiHot', // 'http://172.16.6.84:9100/importApiHot',
  207 + body: {
  208 + keywords: post_data
  209 + },
  210 + json: true,
  211 + timeout: 5000
  212 + }).then(res => {
  213 + // console.log('res:', res);
  214 + return ctx.response.body = res;
  215 + }).catch(err => {
  216 + // console.log(err);
  217 + return ctx.response.body = {
  218 + code: 301,
  219 + message: '接口数据处理错误'
  220 + }
  221 + });
  222 +
  223 + } else {
  224 + return ctx.response.body = {
  225 + code: 400,
  226 + data: post_data,
  227 + message: 'excel无数据或数据格式不正确!'
  228 + }
  229 + }
  230 +
  231 +
  232 + }
  233 +});
179 234
180 module.exports = r; 235 module.exports = r;
181 236
@@ -6,6 +6,7 @@ const md5 = require('md5'); @@ -6,6 +6,7 @@ const md5 = require('md5');
6 const moment = require('moment'); 6 const moment = require('moment');
7 const pager = require('../utils/pager'); 7 const pager = require('../utils/pager');
8 const seoModel = require('../models/seoModel'); 8 const seoModel = require('../models/seoModel');
  9 +const xlsx = require('xlsx');
9 10
10 let r = new Router(); 11 let r = new Router();
11 12
@@ -149,7 +150,6 @@ const tdk = { @@ -149,7 +150,6 @@ const tdk = {
149 150
150 ctx.response.body = result; 151 ctx.response.body = result;
151 }, 152 },
152 -  
153 edit: async(ctx, next) => { 153 edit: async(ctx, next) => {
154 let result = {code: 500, message: '非法参数'}; 154 let result = {code: 500, message: '非法参数'};
155 155
@@ -217,6 +217,75 @@ const tdk = { @@ -217,6 +217,75 @@ const tdk = {
217 } 217 }
218 218
219 ctx.response.body = result; 219 ctx.response.body = result;
  220 + },
  221 + upload: async(ctx, next) => {
  222 + // console.log(ctx.request.body);
  223 + let result = {code: 500, message: '非法参数'};
  224 + if (ctx.request.body._files) {
  225 + let file = ctx.request.body._files.up_excel;
  226 + const workbook = xlsx.readFile(file.path);
  227 + // console.log('workbook=',workbook);
  228 + const sheetNames = workbook.Props.SheetNames;
  229 + const worksheet = workbook.Sheets[sheetNames[0]];
  230 +
  231 + let json_data = xlsx.utils.sheet_to_json(worksheet);
  232 +
  233 + let post_data = [];
  234 +
  235 + // 处理EXCEL数据,并插入redis
  236 + let createRedisData = async function (ctx, data, index) {
  237 + if (index < data.length) {
  238 + let obj = data[index];
  239 + let type = obj.type || '';
  240 + let key = obj.key || '';
  241 + let title = obj.title || '';
  242 + let keywords = obj.keywords || '';
  243 + let description = obj.description || '';
  244 + // console.log(obj, index);
  245 +
  246 + if (_.find(TYPE_LIST, ['type', type]) && key) {
  247 + // console.log('type: ', type, key);
  248 + let hashKey = key;
  249 + if (type === 'url') {
  250 + key = _.replace(key, /http[s]?:\/\//, '');
  251 + hashKey = md5(key);
  252 + }
  253 +
  254 + //先删除掉相同值的字段
  255 + let exist = await ctx.redis.multi([['lrem', `tdk:${type}:links`, 1, hashKey]]).execAsync();
  256 +
  257 + //插入List, 插入数据
  258 + let res = await ctx.redis.multi([
  259 + ['lpushx', `tdk:${type}:links`, hashKey],
  260 + ['hmset', `tdk:${type}:${hashKey}`, 'key', key, 'title', title, 'keywords', keywords,
  261 + 'description', description, 'modify_time', Date.parse(new Date()) / 1000]
  262 + ]).execAsync();
  263 +
  264 + if (res[1]) {
  265 + if (!res[0]) {
  266 + ctx.redis.lpush(`tdk:${type}:links`, hashKey);
  267 + }
  268 + }
  269 +
  270 + return createRedisData(ctx, data, index + 1);
  271 +
  272 + } else {
  273 + return createRedisData(ctx, data, index + 1);
  274 + }
  275 + } else {
  276 + result = {
  277 + code: 200,
  278 + data: data,
  279 + message: `导入完成${index}条`
  280 + };
  281 + console.log(result);
  282 + ctx.response.body = result;
  283 + }
  284 +
  285 + };
  286 +
  287 + return createRedisData(ctx, json_data, 0);
  288 + }
220 } 289 }
221 }; 290 };
222 291
@@ -720,6 +789,7 @@ r.get('/tdk', tdk.index); @@ -720,6 +789,7 @@ r.get('/tdk', tdk.index);
720 r.post('/tdk/add', tdk.add); 789 r.post('/tdk/add', tdk.add);
721 r.post('/tdk/edit', tdk.edit); 790 r.post('/tdk/edit', tdk.edit);
722 r.post('/tdk/delete', tdk.delete); 791 r.post('/tdk/delete', tdk.delete);
  792 +r.post('/tdk/upload', tdk.upload);
723 793
724 // 词根管理 794 // 词根管理
725 r.get('/rootwords', rootWords.index); 795 r.get('/rootwords', rootWords.index);
@@ -128,7 +128,15 @@ @@ -128,7 +128,15 @@
128 <label style="margin-right:20px;"><input type="checkbox" id="check-all" style="margin-right:5px;">全选</label> 128 <label style="margin-right:20px;"><input type="checkbox" id="check-all" style="margin-right:5px;">全选</label>
129 <a data-toggle="modal" href="#pop" class="btn btn-default" style="margin-right:10px;">增加</a> 129 <a data-toggle="modal" href="#pop" class="btn btn-default" style="margin-right:10px;">增加</a>
130 <button class="btn btn-default delete-all" type="submit">删除</button> 130 <button class="btn btn-default delete-all" type="submit">删除</button>
131 - 131 + <form id="uploadForm" enctype="multipart/form-data" style="display: inline-block;">
  132 + <div href="javascript:void(0)" class="btn btn-default btn-upload-excel" style="margin-left:10px">
  133 + <input id="up_excel" name="up_excel"
  134 + accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
  135 + type="file" style="position: absolute; opacity: 0; width: 100px;">
  136 + 上传EXCEL
  137 + </div>
  138 + </form>
  139 + <span>(excel必要字段type,key,title, keywords,description)</span>
132 <div class="input-append pull-right"> 140 <div class="input-append pull-right">
133 <form id="query-form" action="/seo/tdk" class="query-form" method="get"> 141 <form id="query-form" action="/seo/tdk" class="query-form" method="get">
134 <div class="btn-group"> 142 <div class="btn-group">
@@ -408,5 +416,24 @@ @@ -408,5 +416,24 @@
408 416
409 delTdk([data]); 417 delTdk([data]);
410 }); 418 });
  419 +
  420 + $('#up_excel').on('change', function(e) { // EXCEL上传
  421 + let $form = $('#uploadForm')[0];
  422 + let formData = new FormData($form);
  423 + $.ajax({
  424 + url: '/seo/tdk/upload', // web/actions/seo.js => tdk.upload
  425 + data: formData,
  426 + method: 'POST',
  427 + cache: false,
  428 + processData: false,
  429 + contentType: false,
  430 + success: function(res) {
  431 + if(res.code === 200) {
  432 + alert('上传完成!');
  433 + document.location.reload();
  434 + }
  435 + }
  436 + });
  437 + });
411 }); 438 });
412 </script> 439 </script>
@@ -14,7 +14,7 @@ const defaults = { @@ -14,7 +14,7 @@ const defaults = {
14 }, 14 },
15 redis: { 15 redis: {
16 connect: { 16 connect: {
17 - host: '127.0.0.1', 17 + host: '192.168.102.49', //'127.0.0.1',
18 port: '6379', 18 port: '6379',
19 retry_strategy: options => { 19 retry_strategy: options => {
20 if (options.error && options.error.code === 'ECONNREFUSED') { 20 if (options.error && options.error.code === 'ECONNREFUSED') {
@@ -73,7 +73,8 @@ @@ -73,7 +73,8 @@
73 "ssh2": "^0.5.4", 73 "ssh2": "^0.5.4",
74 "superagent": "^3.6.0", 74 "superagent": "^3.6.0",
75 "tar": "^2.2.1", 75 "tar": "^2.2.1",
76 - "utility": "^1.8.0" 76 + "utility": "^1.8.0",
  77 + "xlsx": "^0.13.0"
77 }, 78 },
78 "devDependencies": { 79 "devDependencies": {
79 "ada": "^1.1.0", 80 "ada": "^1.1.0",
@@ -361,13 +361,55 @@ class HotKeywords extends React.Component { @@ -361,13 +361,55 @@ class HotKeywords extends React.Component {
361 record.callbackFn = this.showOptionModal.bind(this); 361 record.callbackFn = this.showOptionModal.bind(this);
362 362
363 return record.id; 363 return record.id;
364 - } 364 + };
  365 + const props = {
  366 + name: 'up_excel',
  367 + action: '/hot-keywords/upload', // web/actions/hot-keywords.js
  368 + accept: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  369 + headers: {
  370 + authorization: 'authorization-text',
  371 + },
  372 + showUploadList: false, // 不显示文件列表
  373 + onChange(info) {
  374 + message.destroy();
  375 + message.info(`正在上传中...${info.file.percent || 0}%`, 2);
  376 + if(info.file.status === 'done') {
  377 + // 因为不知道如何清除调fileList,所以只能以最后一个上传的文件状态为当前状态
  378 + let fileLength = info.fileList.length - 1 || 0;
  379 +
  380 + // fileList中的response是服务端返回的数据
  381 + if (info.fileList[fileLength] && info.fileList[fileLength].response && info.fileList[fileLength].response.code) {
  382 + console.log(info.fileList[fileLength].response);
  383 + if (info.fileList[fileLength].response.code === 400) {
  384 + let msg = info.fileList[fileLength].response.message;
  385 + console.log(msg);
  386 + message.error(`上传失败! ${msg}`, 5);
  387 + } else if (info.fileList[fileLength].response.code === 200) {
  388 + message.success(`${info.file.name} 文件上传成功!`);
  389 + document.location.reload();
  390 + }
  391 +
  392 + }
  393 + console.log(info);
  394 + }
  395 + if(info.file.status === 'error') {
  396 + message.error(`${info.file.name} 文件上传失败!`);
  397 + }
  398 + console.log(info);
  399 +
  400 + }
  401 + };
365 402
366 return ( 403 return (
367 <div> 404 <div>
368 <div style={{ paddingBottom: 10 }}> 405 <div style={{ paddingBottom: 10 }}>
369 <Button type="primary" onClick={this.deleteTableRow} disabled={!hasSelected} loading={loading}>删除</Button> 406 <Button type="primary" onClick={this.deleteTableRow} disabled={!hasSelected} loading={loading}>删除</Button>
370 <Button type="primary" style={{ marginLeft: 10 }} onClick={this.addTableRow}>添加</Button> 407 <Button type="primary" style={{ marginLeft: 10 }} onClick={this.addTableRow}>添加</Button>
  408 + <Upload {...props} style={{marginLeft: 10}}>
  409 + <Button type="primary">
  410 + <Icon type="upload" />上传Excel
  411 + </Button>
  412 + </Upload>
371 {this.optionModal(showModal)} 413 {this.optionModal(showModal)}
372 </div> 414 </div>
373 415