Authored by 陈峰

Merge branch '20180917' into 'master'

20180917



See merge request !53
  1 +/**
  2 + * 优惠券管理controller
  3 + * @author: leo <y.huang@yoho.cn>
  4 + * @date: 08/08/2018
  5 + */
  6 +const guoChaoModel = require('../models/guochao');
  7 +
  8 +const guochaoController = {
  9 +
  10 + list: async function(req, res) {
  11 +
  12 + let list = await req.ctx(guoChaoModel).list();
  13 +
  14 + return res.json(list);
  15 +
  16 + },
  17 +
  18 + listPage(req, res) {
  19 + res.render('guochao/list', {
  20 + bodyClass: 'nav-md',
  21 + module: 'admin',
  22 + page: 'guochao'
  23 + });
  24 + },
  25 +
  26 + async updatePage(req, res) {
  27 + let result = await req.ctx(guoChaoModel).findById(req.query.id);
  28 +
  29 + if (result instanceof Array) {
  30 + result = result[0];
  31 + }
  32 + res.render('guochao/guochao-option', {
  33 + bodyClass: 'nav-md',
  34 + module: 'admin',
  35 + content: result,
  36 + page: 'guochao-option'
  37 + });
  38 + },
  39 +
  40 + update: async function(req, res) {
  41 + let result = await req.ctx(guoChaoModel).update(req.body);
  42 +
  43 + return res.json(result);
  44 + },
  45 +};
  46 +
  47 +module.exports = guochaoController;
  1 +/* eslint-disable array-callback-return */
  2 +const { actGuochaoShop } = require('../../../db');
  3 +
  4 +class GuochaoModel extends global.yoho.BaseModel {
  5 + constructor(ctx) {
  6 + super(ctx);
  7 + }
  8 +
  9 + list() {
  10 + return actGuochaoShop.findAll({order: [['sort', 'desc']]});
  11 + }
  12 +
  13 + update(obj) {
  14 + return actGuochaoShop.update({ url: obj.url, sort: obj.sort, place: obj.place }, {where: {id: obj.id}});
  15 + }
  16 +
  17 + findById(id) {
  18 + return actGuochaoShop.findById(id);
  19 + }
  20 +}
  21 +
  22 +module.exports = GuochaoModel;
@@ -10,11 +10,17 @@ const activity = require('./controllers/activity'); @@ -10,11 +10,17 @@ const activity = require('./controllers/activity');
10 const user = require('./controllers/user'); 10 const user = require('./controllers/user');
11 const coupon = require('./controllers/coupon'); 11 const coupon = require('./controllers/coupon');
12 const multipart = require('connect-multiparty'); 12 const multipart = require('connect-multiparty');
  13 +const guochao = require('./controllers/guochao');
13 const mutilpartMiddleware = multipart(); 14 const mutilpartMiddleware = multipart();
14 15
15 // 管理员[page] 16 // 管理员[page]
16 router.get('/login', admin.loginPage); 17 router.get('/login', admin.loginPage);
17 18
  19 +
  20 +// 国潮排行[page]
  21 +router.get('/guochao/list', guochao.listPage);
  22 +router.get('/guochao/update', guochao.updatePage);
  23 +
18 // 活动管理[page] 24 // 活动管理[page]
19 router.get('/activity/list', activity.activityListPage); 25 router.get('/activity/list', activity.activityListPage);
20 router.get('/activity/create', activity.createActivityPage); 26 router.get('/activity/create', activity.createActivityPage);
@@ -48,6 +54,10 @@ router.get('/coupon/update', coupon.couponOptionPage); @@ -48,6 +54,10 @@ router.get('/coupon/update', coupon.couponOptionPage);
48 router.post('/api/login', admin.login); 54 router.post('/api/login', admin.login);
49 router.post('/api/logout', admin.logout); 55 router.post('/api/logout', admin.logout);
50 56
  57 +// 国潮排行[page]
  58 +router.get('/api/guochao/list', guochao.list);
  59 +router.post('/api/guochao/update', guochao.update);
  60 +
51 // 活动管理[ajax] 61 // 活动管理[ajax]
52 router.post('/api/activity/list', activity.activityList); 62 router.post('/api/activity/list', activity.activityList);
53 router.post('/api/activity/create', activity.createActivity); 63 router.post('/api/activity/create', activity.createActivity);
  1 +<div class="right_col" role="main">
  2 + <div class="col-md-12 col-sm-12 col-xs-12">
  3 + <div class="x_panel">
  4 + {{# content}}
  5 + <div class="x_title">
  6 + <h2>修改品牌信息</h2>
  7 + <div class="clearfix"></div>
  8 + </div>
  9 + <form id="createForm" class="form-horizontal form-label-left">
  10 + <input class="form-imput" type="hidden" data-type="id" id="id" value="{{id}}">
  11 + <input id="place" type="hidden" value="{{place}}">
  12 + <div class="item form-group">
  13 + <label class="control-label col-md-3 col-sm-3 col-xs-12" for="actName">
  14 + 商铺id<span class="required">*</span>
  15 + </label>
  16 + <div class="col-md-6 col-sm-6 col-xs-12">
  17 + <input class="form-control col-md-7 col-xs-12"
  18 + data-type="shopId"
  19 + data-toggle="tooltip"
  20 + data-placement="bottom"
  21 + title="商铺id不能为空"
  22 + name="shopName"
  23 + type="text"
  24 + maxlength="50"
  25 + value="{{shop_id}}">
  26 + </div>
  27 + </div>
  28 + <div class="item form-group">
  29 + <label class="control-label col-md-3 col-sm-3 col-xs-12" for="actName">
  30 + 商家logo图片<span class="required">*</span>
  31 + </label>
  32 + <div class="col-md-6 col-sm-6 col-xs-12">
  33 + <img src="{{logo}}" class="col-md-6 product-thumb {{#unless logo}}hide{{/unless}}">
  34 + </div>
  35 + </div>
  36 + <div class="item form-group">
  37 + <label class="control-label col-md-3 col-sm-3 col-xs-12" for="actName">
  38 + 链接地址<span class="required">*</span>
  39 + </label>
  40 + <div class="col-md-6 col-sm-6 col-xs-12">
  41 + <input class="form-control col-md-7 col-xs-12 form-imput"
  42 + data-type="url"
  43 + data-toggle="tooltip"
  44 + data-placement="bottom"
  45 + title="链接地址不能为空"
  46 + name="shopName"
  47 + type="text"
  48 + maxlength="50"
  49 + value="{{url}}">
  50 + </div>
  51 + </div>
  52 + <div class="item form-group">
  53 + <label class="control-label col-md-3 col-sm-3 col-xs-12" for="actName">排序<span
  54 + class="required">*</span>
  55 + </label>
  56 + <div class="col-md-2 col-sm-2 col-xs-12">
  57 + <input id="actVoteLimit" class="form-control col-md-2 form-imput"
  58 + data-type="sort"
  59 + data-toggle="tooltip" data-placement="bottom" title="排序"
  60 + name="actVoteLimit"
  61 + type="text"
  62 + maxlength="10"
  63 + value="{{sort}}"><span style="color:#d44950">备注:排序权重数大的排在前面</span>
  64 + </div>
  65 + </div>
  66 + <div class="item form-group">
  67 + <label class="control-label col-md-3 col-sm-3 col-xs-12">
  68 + 位置
  69 + </label>
  70 + <div class="col-md-6 col-sm-6 col-xs-12">
  71 + <label class="control-label col-md-1">
  72 + <input type="radio" name="place" required="required"
  73 + class="form-contro col-md-7 col-xs-12" value="0">
  74 +
  75 + </label>
  76 + <label class="control-label col-md-1">
  77 + <input type="radio" name="place" required="required"
  78 + class="form-contro col-md-7 col-xs-12" value="1">
  79 +
  80 + </label>
  81 + </div>
  82 + </div>
  83 + <div class="item form-group">
  84 + <div class="col-md-2 col-md-offset-5 action-wrap">
  85 + <button type="button" class="btn btn-primary btn-save">保存</button>
  86 + </div>
  87 + </div>
  88 + </form>
  89 + <a id="upload-btn"></a>
  90 + {{/ content}}
  91 + </div>
  92 + </div>
  93 +</div>
  1 +<!-- page content -->
  2 +<div class="right_col" role="main">
  3 + <div class="">
  4 + <div class="row">
  5 + <div class="col-md-12 col-sm-12 col-xs-12">
  6 + <div class="x_panel">
  7 + <div class="x_title">
  8 + </div>
  9 + <div class="x_content">
  10 + <div class="table-responsive">
  11 + <table class="table table-bordered">
  12 + <thead>
  13 + <tr class="headings">
  14 + <th class="column-title">ID</th>
  15 + <th class="column-title">logo</th>
  16 + <th class="column-title">品牌名</th>
  17 + <th class="column-title">排序</th>
  18 + <th class="column-title">位置</th>
  19 + <th class="column-title">收藏数</th>
  20 + <th class="column-title">操作</th>
  21 + </tr>
  22 + </thead>
  23 + <tbody class="guochao-list">
  24 + </tbody>
  25 + </table>
  26 + </div>
  27 + <div class="table-pagination coupon-pagination pull-right"></div>
  28 + </div>
  29 + </div>
  30 + </div>
  31 + </div>
  32 + </div>
  33 +</div>
  34 +<!-- /page content -->
@@ -28,15 +28,18 @@ let createY100Data = async function(req, res, next, actId, data, index) { @@ -28,15 +28,18 @@ let createY100Data = async function(req, res, next, actId, data, index) {
28 let content = ''; 28 let content = '';
29 let createTime = moment().format('YYYY-MM-DD hh:mm:ss'); 29 let createTime = moment().format('YYYY-MM-DD hh:mm:ss');
30 30
  31 + item.skns_1 = (item.skns_1 || '').replace(/ /g, '').replace(/\r/g, '').replace(/\n/g, '');
  32 + item.skns_2 = (item.skns_2 || '').replace(/ /g, '').replace(/\r/g, '').replace(/\n/g, '');
  33 +
31 let skns = (item.skns_1 || '') + '||' + (item.skns_2 || ''); 34 let skns = (item.skns_1 || '') + '||' + (item.skns_2 || '');
32 - let career = item.career;  
33 - let interest = item.interest; 35 + let career = item.career || '';
  36 + let interest = item.interest || '';
34 let style = item.style; 37 let style = item.style;
35 - let tag = item.tag; 38 + let tag = item.tag || '';
36 let desc = item.desc || ''; 39 let desc = item.desc || '';
37 let top = item.top || 0; 40 let top = item.top || 0;
38 - let head_url = item.head_url;  
39 - let img_url = item.img_url; 41 + let head_url = item.head_url || '';
  42 + let img_url = item.img_url || '';
40 43
41 const articleId = await req.ctx(ActivityModel).createArticle({actId, name, content, createTime}); 44 const articleId = await req.ctx(ActivityModel).createArticle({actId, name, content, createTime});
42 const numRes = await req.ctx(ActivityModel).getY100ArticleNums(actId); 45 const numRes = await req.ctx(ActivityModel).getY100ArticleNums(actId);
  1 +/**
  2 + * 优惠券管理controller
  3 + * @author: leo <y.huang@yoho.cn>
  4 + * @date: 08/08/2018
  5 + */
  6 +
  7 +const GuochaoModel = require('../models/guochao');
  8 +const _ = require('lodash');
  9 +
  10 +const guochaoController = {
  11 +
  12 + async addFav(req, res, next) {
  13 + let param = req.body;
  14 + let sessionKey = param.sessionKey;
  15 +
  16 + // 调用接口传参时切勿使用toString获得字符串
  17 + let uid = {
  18 + toString: () => {
  19 + return _.parseInt(req.body.uid);
  20 + },
  21 + sessionKey,
  22 + };
  23 +
  24 + try {
  25 + let r = await req.ctx(GuochaoModel).addFavAsync(uid, param.id, 'shop');
  26 +
  27 + res.json(r);
  28 + } catch (e) {
  29 + next;
  30 + }
  31 +
  32 + },
  33 + list: async function(req, res, next) {
  34 + try {
  35 + let result = await req.ctx(GuochaoModel).list();
  36 +
  37 + res.json({
  38 + data: result,
  39 + code: 200,
  40 + result: true
  41 + });
  42 + } catch (e) {
  43 + next;
  44 + }
  45 + }
  46 +};
  47 +
  48 +module.exports = guochaoController;
  1 +/* eslint-disable array-callback-return */
  2 +const { actGuochaoShop } = require('../../../db');
  3 +
  4 +class Guochao extends global.yoho.BaseModel {
  5 + constructor(ctx) {
  6 + super(ctx);
  7 + }
  8 +
  9 + async addFavAsync(uid, id, type) {
  10 + console.log({uid, id, type});
  11 + try {
  12 + await this.get({ data: {
  13 + method: 'app.favorite.add',
  14 + id: id,
  15 + uid: uid,
  16 + type: type
  17 + }});
  18 +
  19 + let item = await actGuochaoShop.findOne({where: {shop_id: id}});
  20 + let result = await item.increment('collect_count');
  21 +
  22 + return Promise.resolve({code: 200, result: true, data: result});
  23 + } catch (e) {
  24 + console.log(e.toString());
  25 + return Promise.resolve({code: 202, result: false, errorMsg: e});
  26 + }
  27 + }
  28 +
  29 + async list() {
  30 + return actGuochaoShop.findAll({order: [['sort', 'desc']]});
  31 + }
  32 +}
  33 +
  34 +module.exports = Guochao;
@@ -11,7 +11,7 @@ const prize = require('./controllers/prize'); @@ -11,7 +11,7 @@ const prize = require('./controllers/prize');
11 const shoes = require('./controllers/shoes'); 11 const shoes = require('./controllers/shoes');
12 const yohood = require('./controllers/yohood'); 12 const yohood = require('./controllers/yohood');
13 const coupon = require('./controllers/coupon'); 13 const coupon = require('./controllers/coupon');
14 - 14 +const guochao = require('./controllers/guochao');
15 const excel = require('./controllers/excel'); 15 const excel = require('./controllers/excel');
16 const multipart = require('connect-multiparty'); 16 const multipart = require('connect-multiparty');
17 const mutilpartMiddleware = multipart(); 17 const mutilpartMiddleware = multipart();
@@ -52,8 +52,11 @@ router.get('/coupon/getCouponSendFlags', coupon.couponSendFlag); @@ -52,8 +52,11 @@ router.get('/coupon/getCouponSendFlags', coupon.couponSendFlag);
52 router.post('/coupon/couponGet', coupon.couponGet); 52 router.post('/coupon/couponGet', coupon.couponGet);
53 router.get('/coupon/couponUserOwner', coupon.couponUserOwner); 53 router.get('/coupon/couponUserOwner', coupon.couponUserOwner);
54 54
55 -//图片处理  
56 -router.get('/shoes/getBase64ImageData', shoes.getImageData); 55 +// guochao
  56 +router.post('/guochao/addFav', guochao.addFav);
  57 +router.get('/guochao/list', guochao.list);
57 58
  59 +// 图片处理git
  60 +router.get('/shoes/getBase64ImageData', shoes.getImageData);
58 61
59 module.exports = router; 62 module.exports = router;
@@ -146,8 +146,8 @@ class Y100Model extends global.yoho.BaseModel { @@ -146,8 +146,8 @@ class Y100Model extends global.yoho.BaseModel {
146 146
147 if (skns) { 147 if (skns) {
148 148
149 - const product_skns = skns.split('||')[0] || '';  
150 - const recommend_skns = skns.split('||')[1] || ''; 149 + const product_skns = skns.split('||')[0].replace(/\r/g, '').replace(/\n/g, '').replace(/ /g, '') || '';
  150 + const recommend_skns = skns.split('||')[1].replace(/\r/g, '').replace(/\n/g, '').replace(/ /g, '') || '';
151 151
152 const productsResult = await this.get({ 152 const productsResult = await this.get({
153 data: { 153 data: {
  1 +module.exports = {
  2 + test: {
  3 + username: 'root',
  4 + password: '123456',
  5 + database: 'yoho_activity_platform',
  6 + port: '3306',
  7 + host: '0.0.0.0',
  8 + dialect: 'mysql',
  9 + pool: {
  10 + max: 5,
  11 + min: 0,
  12 + acquire: 30000,
  13 + idle: 10000
  14 + },
  15 + seederStorage: 'sequelize'
  16 + },
  17 + production: {
  18 + username: 'yh_vpc_bak',
  19 + password: 'yoho@2Y$^YpNb7hp',
  20 + database: 'yoho_activity_platform',
  21 + port: '3307',
  22 + host: '10.67.2.144',
  23 + dialect: 'mysql',
  24 + pool: {
  25 + max: 5,
  26 + min: 0,
  27 + acquire: 30000,
  28 + idle: 10000
  29 + },
  30 + seederStorage: 'sequelize'
  31 + }
  32 +}[process.env.NODE_ENV || 'test'];
  1 +const fs = require('fs');
  2 +const path = require('path');
  3 +const Sequelize = require('sequelize');
  4 +const basename = path.basename(__filename);
  5 +const config = require('../config/database');
  6 +const logger = global.yoho.logger;
  7 +const db = {};
  8 +
  9 +const sequelize = new Sequelize(config.database, config.username, config.password, Object.assign(config, {
  10 + logging: (log) => {
  11 + logger.info(log);
  12 + }
  13 +}));
  14 +
  15 +fs.readdirSync(path.join(__dirname, './models'))
  16 + .filter(file => {
  17 + return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
  18 + })
  19 + .forEach(file => {
  20 + let model = sequelize.import(path.join(__dirname, './models', file));
  21 +
  22 + db[model.name] = model;
  23 + });
  24 +Object.keys(db).forEach(modelName => {
  25 + if (db[modelName].associate) {
  26 + db[modelName].associate(db);
  27 + }
  28 +});
  29 +
  30 +db.sequelize = sequelize;
  31 +
  32 +module.exports = db;
  1 +module.exports = {
  2 + timestamps: false,
  3 + paranoid: false,
  4 + underscored: true,
  5 + freezeTableName: true,
  6 +};
  1 +/* eslint new-cap: "off" */
  2 +const options = require('../model-opts');
  3 +
  4 +module.exports = function(sequelize, DataTypes) {
  5 + const actGuochaoShop = sequelize.define('actGuochaoShop', {
  6 + id: {
  7 + type: DataTypes.INTEGER(8),
  8 + allowNull: false,
  9 + primaryKey: true,
  10 + autoIncrement: true
  11 + },
  12 + logo: {
  13 + type: DataTypes.STRING(200),
  14 + allowNull: true
  15 + },
  16 + url: {
  17 + type: DataTypes.STRING(300),
  18 + allowNull: true,
  19 + },
  20 + sort: {
  21 + type: DataTypes.INTEGER(8),
  22 + allowNull: true,
  23 + defaultValue: '0'
  24 + },
  25 + place: {
  26 + type: DataTypes.INTEGER(2),
  27 + allowNull: true,
  28 + defaultValue: '0'
  29 + },
  30 + collect_count: {
  31 + type: DataTypes.INTEGER(8),
  32 + allowNull: true,
  33 + defaultValue: '0'
  34 + },
  35 + shop_id: {
  36 + type: DataTypes.INTEGER(10),
  37 + allowNull: true,
  38 + defaultValue: '0'
  39 + },
  40 + shop_name: {
  41 + type: DataTypes.STRING(50),
  42 + allowNull: true,
  43 + }
  44 + }, Object.assign(options, {tableName: 'act_guochao_shop'}));
  45 +
  46 + return actGuochaoShop;
  47 +};
@@ -68,6 +68,11 @@ @@ -68,6 +68,11 @@
68 <li><a href="/admin/coupon/list">券列表</a></li> 68 <li><a href="/admin/coupon/list">券列表</a></li>
69 </ul> 69 </ul>
70 </li> 70 </li>
  71 + <li><a><i class="fa fa-ticket"></i> 国潮排行 <span class="fa fa-chevron-down"></span></a>
  72 + <ul class="nav child_menu">
  73 + <li><a href="/admin/guochao/list">品牌列表</a></li>
  74 + </ul>
  75 + </li>
71 </ul> 76 </ul>
72 </div> 77 </div>
73 </div> 78 </div>
@@ -56,6 +56,8 @@ @@ -56,6 +56,8 @@
56 "memory-cache": "^0.1.6", 56 "memory-cache": "^0.1.6",
57 "moment": "^2.18.1", 57 "moment": "^2.18.1",
58 "mysql": "^2.13.0", 58 "mysql": "^2.13.0",
  59 + "sequelize": "^4.38.0",
  60 + "mysql2": "^1.6.1",
59 "nodemailer": "^4.4.1", 61 "nodemailer": "^4.4.1",
60 "nodemailer-smtp-transport": "^2.7.4", 62 "nodemailer-smtp-transport": "^2.7.4",
61 "passport": "^0.3.2", 63 "passport": "^0.3.2",
No preview for this file type
  1 +require('admin/coupon-option.page.css');
  2 +require('bootstrap-daterangepicker');
  3 +require('bootpag/lib/jquery.bootpag.min');
  4 +
  5 +function bindEditPageEvent() {
  6 +
  7 + function packInfo() {
  8 + let resData = {};
  9 + let error;
  10 +
  11 + $('.form-imput').each(function() {
  12 +
  13 + let $this = $(this);
  14 + let val = $this.val();
  15 + let data = $this.data();
  16 +
  17 + if (!val && !error) {
  18 + error = data.empty || `请填写${data.type}`;
  19 + }
  20 + resData[data.type] = val;
  21 + });
  22 +
  23 + if (error) {
  24 + alert(error);// eslint-disable line
  25 + return;
  26 + }
  27 +
  28 +
  29 + resData.place = $('input[name=place]:checked').val();
  30 + return resData;
  31 + }
  32 +
  33 + function radioInit() {
  34 + let place = parseInt($('#place').val());
  35 +
  36 + if (place) {
  37 + $($('input[name=place]')[1]).attr('checked', true);
  38 + } else {
  39 + $($('input[name=place]')[0]).attr('checked', true);
  40 + }
  41 + }
  42 +
  43 + radioInit();
  44 +
  45 +
  46 + $('.btn-save').on('click', function() {
  47 + let info = packInfo();
  48 +
  49 + if (!info || window.saveing) {
  50 + return;
  51 + }
  52 +
  53 + window.saveing = true;
  54 +
  55 + $.ajax({
  56 + method: 'post',
  57 + url: '/admin/api/guochao/update',
  58 + contentType: 'application/json',
  59 + data: JSON.stringify(info)
  60 + }).then(() => {
  61 + window.saveing = false;
  62 + alert('保存成功');
  63 + });
  64 + });
  65 +}
  66 +
  67 +(function() {
  68 + bindEditPageEvent();
  69 +}());
  1 +/* eslint-disable eqeqeq */
  2 +require('admin/user.page.css');
  3 +require('bootpag/lib/jquery.bootpag.min');
  4 +
  5 +const _ = require('lodash');
  6 +
  7 +function bind_table_pagination() {
  8 + const $ul = $('.guochao-list');
  9 + const fetchRender = () => {
  10 + $.ajax({
  11 + url: '/admin/api/guochao/list',
  12 + }).then(result => {
  13 + const list = result;
  14 +
  15 + let html = '';
  16 +
  17 + _.each(list, item => {
  18 + if (item.place) {
  19 + item.placeText = '下';
  20 + } else {
  21 + item.placeText = '上';
  22 + }
  23 +
  24 + html += `
  25 + <tr class="even pointer">
  26 + <td class="">${item.id}</td>
  27 + <td class=""><img src="${item.logo}" style="background: #000000" width="100" height="50"/></td>
  28 + <td class="">${item.shop_name}</td>
  29 + <td class="">${item.sort}</td>
  30 + <td class="">${item.placeText}</td>
  31 + <td class="">${item.collect_count}</td>
  32 + <td class="">
  33 + <a class="btn btn-info btn-update" href="/admin/guochao/update?id=${item.id}">修改</a>
  34 + </td>
  35 + </tr>`;
  36 + });
  37 + $ul.html(html);
  38 + });
  39 + };
  40 +
  41 + fetchRender();
  42 +
  43 +}
  44 +
  45 +(function() {
  46 + bind_table_pagination();
  47 +}());
  48 +
  49 +
  50 +
  51 +
  52 +
1 @import "user/user"; 1 @import "user/user";
  2 +
  3 +td {
  4 + word-break: break-all;
  5 +}