Kirin преди 3 години
родител
ревизия
fd691bc322

+ 1 - 1
app/admin/controller/merchant/SystemStore.php

@@ -177,7 +177,7 @@ class SystemStore extends AuthController
             if (!$orderKey) JsonService::fail('生成订单错误!');
             if (StoreOrder::be(['order_id|unique' => $orderKey, 'uid' => $uid, 'is_del' => 0]))
                 JsonService::fail('订单已生成');
-            $order = StoreOrder::cacheKeyCreateOrder($uid, $orderKey, 0, 'offline', false, 0, '加盟店后台订单', 0, 0, 0, 0, 0, false, 1, 2, $date['real_name'], $date['phone'], $this->store_id, 0, 1);
+            $order = StoreOrder::cacheKeyCreateOrder($uid, $orderKey, 0, 'offline', false, 0, '加盟店后台订单', 0, 0, 0, 0, 0, 0, false, 1, 2, $date['real_name'], $date['phone'], $this->store_id, 0, 1);
             if ($order === false) JsonService::fail(StoreOrder::getErrorInfo('订单生成失败'));
             else JsonService::success('订单已生成');
         } else {

+ 505 - 0
app/admin/controller/ump/StoreExchange.php

@@ -0,0 +1,505 @@
+<?php
+
+namespace app\admin\controller\ump;
+
+use app\admin\controller\AuthController;
+use app\admin\model\store\{StoreDescription,
+    StoreProductAttr,
+    StoreProductAttrResult,
+    StoreProduct as ProductModel,
+    StoreProductAttrValue
+};
+use crmeb\traits\CurdControllerTrait;
+use think\Exception;
+use think\exception\ErrorException;
+use think\exception\ValidateException;
+use think\facade\Route as Url;
+use app\admin\model\system\{SystemAttachment, SystemGroupData, ShippingTemplates};
+use crmeb\services\{
+    FormBuilder as Form, UtilService as Util, JsonService as Json
+};
+use app\admin\model\store\StoreCategory;
+
+/**
+ * 限时秒杀  控制器
+ * Class StoreSeckill
+ * @package app\admin\controller\store
+ */
+class StoreExchange extends AuthController
+{
+
+    use CurdControllerTrait;
+
+    protected $bindModel = \app\admin\model\ump\StoreExchange::class;
+
+    /**
+     * 显示资源列表
+     *
+     * @return \think\Response
+     */
+    public function index()
+    {
+        $this->assign('countSeckill', \app\admin\model\ump\StoreExchange::getSeckillCount());
+        $this->assign('seckillId', \app\admin\model\ump\StoreExchange::getSeckillIdAll());
+        return $this->fetch();
+    }
+
+    public function save_excel()
+    {
+        $where = Util::getMore([
+            ['status', ''],
+            ['store_name', '']
+        ]);
+        \app\admin\model\ump\StoreExchange::SaveExcel($where);
+    }
+
+    /**
+     * 异步获取砍价数据
+     */
+    public function get_seckill_list()
+    {
+        $where = Util::getMore([
+            ['page', 1],
+            ['limit', 20],
+            ['status', ''],
+            ['store_name', '']
+        ]);
+        $seckillList = \app\admin\model\ump\StoreExchange::systemPage($where);
+        if (is_object($seckillList['list'])) $seckillList['list'] = $seckillList['list']->toArray();
+        $data = $seckillList['list']['data'];
+        return Json::successlayui(['count' => $seckillList['list']['total'], 'data' => $data]);
+    }
+
+    public function get_seckill_id()
+    {
+        return Json::successlayui(\app\admin\model\ump\StoreExchange::getSeckillIdAll());
+    }
+
+    /**
+     * 添加秒杀商品
+     * @return form-builder
+     */
+    public function create()
+    {
+        $f = array();
+        $f[] = Form::frameImageOne('product', '选择商品', Url::buildUrl('productList', array('fodder' => 'product')))->icon('plus')->width('100%')->height('500px');
+        $f[] = Form::hidden('product_id', '');
+        $f[] = Form::hidden('description', '');
+        $f[] = Form::input('title', '商品标题');
+        $f[] = Form::input('info', '活动简介')->type('textarea');
+        $f[] = Form::input('unit_name', '单位')->placeholder('个、位');
+        $f[] = Form::select('temp_id', '运费模板')->setOptions(function () {
+            $list = ShippingTemplates::getList(['page' => 1, 'limit' => 20]);
+            $menus = [];
+            foreach ($list['data'] as $menu) {
+                $menus[] = ['value' => $menu['id'], 'label' => $menu['name']];
+            }
+            return $menus;
+        })->filterable(1)->col(12);
+        $f[] = Form::frameImageOne('image', '商品主图片(305*305px)', Url::buildUrl('admin/widget.images/index', array('fodder' => 'image')))->icon('image')->width('100%')->height('500px');
+        $f[] = Form::frameImages('images', '商品轮播图(640*640px)', Url::buildUrl('admin/widget.images/index', array('fodder' => 'images')))->maxLength(5)->icon('images')->width('100%')->height('500px');
+        $f[] = Form::number('sort', '排序')->col(12);
+        $f[] = Form::number('num', '单次购买商品个数')->precision(0)->col(12);
+        $f[] = Form::number('give_integral', '赠送积分')->min(0)->precision(0)->col(12);
+        $f[] = Form::radio('is_hot', '热门推荐', 1)->options([['label' => '开启', 'value' => 1], ['label' => '关闭', 'value' => 0]])->col(12);
+        $form = Form::make_post_form('添加用户通知', $f, Url::buildUrl('save'));
+        $this->assign(compact('form'));
+        return $this->fetch('public/form-builder');
+    }
+
+    /**
+     * 保存秒杀商品
+     * @param int $id
+     */
+    public function save($id = 0)
+    {
+        $data = Util::postMore([
+            'title',
+            'product_id',
+            'info',
+            'unit_name',
+            ['image', ''],
+            ['images', []],
+            ['price', 0],
+            ['ot_price', 0],
+            ['cost', 0],
+            ['sales', 0],
+            ['stock', 0],
+            ['sort', 0],
+            ['give_integral', 0],
+            ['postage', 0],
+            ['is_postage', 0],
+            ['cost', 0],
+            ['is_hot', 0],
+            ['status', 0],
+            ['num', 0],
+            'temp_id',
+            ['weight', 0],
+            ['volume', 0],
+        ]);
+        $data['description'] = StoreDescription::getDescription($data['product_id']);
+        if (!$data['title']) return Json::fail('请输入商品标题');
+        if (!$data['unit_name']) return Json::fail('请输入商品单位');
+        if (!$data['product_id']) return Json::fail('商品ID不能为空');
+        if (!$data['image']) return Json::fail('请选择推荐图');
+        if (count($data['images']) < 1) return Json::fail('请选择轮播图');
+        $data['images'] = json_encode($data['images']);
+        if ($data['num'] < 1) return Json::fail('请输入单次购买个数');
+        if ($id) {
+            unset($data['description']);
+            $product = \app\admin\model\ump\StoreExchange::get($id);
+            if (!$product) return Json::fail('数据不存在!');
+            \app\admin\model\ump\StoreExchange::edit($data, $id);
+            return Json::successful('编辑成功!');
+        } else {
+            $data['add_time'] = time();
+            $res = \app\admin\model\ump\StoreExchange::create($data);
+            $description['product_id'] = $res['id'];
+            $description['description'] = htmlspecialchars_decode($data['description']);
+            $description['type'] = 5;
+            StoreDescription::create($description);
+            return Json::successful('添加成功!');
+        }
+
+    }
+
+    /**
+     * 显示编辑资源表单页.
+     *
+     * @param int $id
+     * @return \think\Response
+     */
+    public function edit($id)
+    {
+        if (!$id) return $this->failed('数据不存在');
+        $product = \app\admin\model\ump\StoreExchange::get($id);
+//        $time = StoreSeckillTime::getSeckillTime($id);
+        if (!$product) return Json::fail('数据不存在!');
+        $f = array();
+        $f[] = Form::input('product_id', '产品ID', $product->getData('product_id'))->disabled(true);
+        $f[] = Form::input('title', '商品标题', $product->getData('title'));
+        $f[] = Form::input('info', '活动简介', $product->getData('info'))->type('textarea');
+        $f[] = Form::input('unit_name', '单位', $product->getData('unit_name'))->placeholder('个、位');
+        $f[] = Form::select('temp_id', '运费模板', (string)$product->getData('temp_id'))->setOptions(function () {
+            $list = ShippingTemplates::getList(['page' => 1, 'limit' => 20]);
+            $menus = [];
+            foreach ($list['data'] as $menu) {
+                $menus[] = ['value' => $menu['id'], 'label' => $menu['name']];
+            }
+            return $menus;
+        })->filterable(1)->col(12);
+        $f[] = Form::frameImageOne('image', '商品主图片(305*305px)', Url::buildUrl('admin/widget.images/index', array('fodder' => 'image')), $product->getData('image'))->icon('image')->width('100%')->height('500px');
+        $f[] = Form::frameImages('images', '商品轮播图(640*640px)', Url::buildUrl('admin/widget.images/index', array('fodder' => 'images')), json_decode($product->getData('images')))->maxLength(5)->icon('images')->width('100%')->height('500px');
+        $f[] = Form::number('sort', '排序', $product->getData('sort'))->col(12);
+        $f[] = Form::hidden('stock', $product->getData('stock'));
+        $f[] = Form::hidden('price', $product->getData('price'));
+        $f[] = Form::hidden('ot_price', $product->getData('ot_price'));
+        $f[] = Form::hidden('sales', $product->getData('sales'));
+        $f[] = Form::number('num', '单次购买商品个数', $product->getData('num'))->precision(0)->col(12);
+        $f[] = Form::number('give_integral', '赠送积分', $product->getData('give_integral'))->min(0)->precision(0)->col(12);
+        $f[] = Form::radio('is_hot', '热门推荐', $product->getData('is_hot'))->options([['label' => '开启', 'value' => 1], ['label' => '关闭', 'value' => 0]])->col(12);
+        $f[] = Form::hidden('status', $product->getData('status'));
+        $form = Form::make_post_form('添加用户通知', $f, Url::buildUrl('save', compact('id')));
+        $this->assign(compact('form'));
+        return $this->fetch('public/form-builder');
+    }
+
+    /**
+     * 删除指定资源
+     *
+     * @param int $id
+     * @return \think\Response
+     */
+    public function delete($id)
+    {
+        if (!$id) return $this->failed('数据不存在');
+        $product = \app\admin\model\ump\StoreExchange::get($id);
+        if (!$product) return Json::fail('数据不存在!');
+        if ($product['is_del']) return Json::fail('已删除!');
+        $data['is_del'] = 1;
+        if (!\app\admin\model\ump\StoreExchange::edit($data, $id))
+            return Json::fail(\app\admin\model\ump\StoreExchange::getErrorInfo('删除失败,请稍候再试!'));
+        else
+            return Json::successful('删除成功!');
+    }
+
+    public function edit_content($id)
+    {
+        if (!$id) return $this->failed('数据不存在');
+        $seckill = \app\admin\model\ump\StoreExchange::get($id);
+        if (!$seckill) return Json::fail('数据不存在!');
+        $this->assign([
+            'content' => htmlspecialchars_decode(StoreDescription::getDescription($id, 5)),
+            'field' => 'description',
+            'action' => Url::buildUrl('change_field', ['id' => $id, 'field' => 'description'])
+        ]);
+        return $this->fetch('public/edit_content');
+    }
+
+    public function change_field($id)
+    {
+        if (!$id) return $this->failed('数据不存在');
+        $seckill = \app\admin\model\ump\StoreExchange::get($id);
+        if (!$seckill) return Json::fail('数据不存在!');
+        $data['description'] = request()->post('description');
+        StoreDescription::saveDescription($data['description'], $id, 5);
+        $res = \app\admin\model\ump\StoreExchange::edit($data, $id);
+        if ($res)
+            return Json::successful('添加成功');
+        else
+            return Json::fail('添加失败');
+    }
+
+    /**
+     * 属性页面
+     * @param $id
+     * @return mixed|void
+     */
+    public function attr($id)
+    {
+        if (!$id) return $this->failed('数据不存在!');
+        $result = StoreProductAttrResult::getResult($id, 5);
+        $image = \app\admin\model\ump\StoreExchange::where('id', $id)->value('image');
+        $this->assign(compact('id', 'result', 'image'));
+        return $this->fetch();
+    }
+
+    /**
+     * 秒杀属性选择页面
+     * @param $id
+     * @return string|void
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function attr_list($id)
+    {
+        if (!$id) return $this->failed('数据不存在!');
+        $seckillInfo = \app\admin\model\ump\StoreExchange::where('id', $id)->find();
+        $seckillResult = StoreProductAttrResult::where('product_id', $id)->where('type', 5)->value('result');
+        $productResult = StoreProductAttrResult::where('product_id', $seckillInfo['product_id'])->where('type', 0)->value('result');
+        if ($productResult) {
+            $attr = json_decode($productResult, true)['attr'];
+            $productAttr = $this->get_attr($attr, $seckillInfo['product_id'], 0);
+            $seckillAttr = $this->get_attr($attr, $id, 5);
+            foreach ($productAttr as $pk => $pv) {
+                foreach ($seckillAttr as $sv) {
+                    if ($pv['detail'] == $sv['detail']) {
+                        $productAttr[$pk] = $sv;
+                    }
+                }
+            }
+        } else {
+            if ($seckillResult) {
+                $attr = json_decode($seckillResult, true)['attr'];
+                $productAttr = $this->get_attr($attr, $id, 5);
+            } else {
+                $attr[0]['value'] = '默认';
+                $attr[0]['detailValue'] = '';
+                $attr[0]['attrHidden'] = '';
+                $attr[0]['detail'][0] = '默认';
+                $productAttr[0]['value1'] = '默认';
+                $productAttr[0]['detail'] = json_encode(['默认' => '默认']);
+                $productAttr[0]['pic'] = $seckillInfo['image'];
+                $productAttr[0]['price'] = $seckillInfo['price'];
+                $productAttr[0]['cost'] = $seckillInfo['cost'];
+                $productAttr[0]['ot_price'] = $seckillInfo['ot_price'];
+                $productAttr[0]['stock'] = $seckillInfo['stock'];
+                $productAttr[0]['quota'] = 0;
+                $productAttr[0]['bar_code'] = $seckillInfo['bar_code'];
+                $productAttr[0]['weight'] = 0;
+                $productAttr[0]['volume'] = 0;
+                $productAttr[0]['deposit'] = 0;
+                $productAttr[0]['brokerage'] = 0;
+                $productAttr[0]['brokerage_two'] = 0;
+                $productAttr[0]['check'] = 0;
+            }
+        }
+        $attrs['attr'] = $attr;
+        $attrs['value'] = $productAttr;
+        $this->assign('attr', $attrs);
+        $this->assign('id', $id);
+        return $this->fetch();
+    }
+
+    /**
+     * 秒杀属性保存页面
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function save_attr()
+    {
+        $data = Util::postMore([
+            ['attr', []],
+            ['ids', []],
+            ['id', 0],
+        ]);
+        if (!$data['id']) return Json::fail('数据不存在!');
+        if (!$data['ids']) return Json::fail('你没有选择任何规格!');
+        $productId = \app\admin\model\ump\StoreExchange::where('id', $data['id'])->value('product_id');
+        $attr = json_decode(StoreProductAttrResult::where('product_id', $productId)->where('type', 0)->value('result'), true)['attr'];
+        foreach ($data['attr'] as $k => $v) {
+            if (in_array($k, $data['ids'])) {
+                $v['detail'] = json_decode(htmlspecialchars_decode($v['detail']), true);
+                $detail[$k] = $v;
+            }
+        }
+        if (min(array_column($detail, 'quota')) == 0) return Json::fail('限购不能为0');
+        $price = min(array_column($detail, 'price'));
+        $otPrice = min(array_column($detail, 'ot_price'));
+        $quota = array_sum(array_column($detail, 'quota'));
+        $stock = array_sum(array_column($detail, 'stock'));
+        $deposit = min(array_column($detail, 'deposit'));
+        if (!$attr) {
+            $attr[0]['value'] = '默认';
+            $attr[0]['detailValue'] = '';
+            $attr[0]['attrHidden'] = '';
+            $attr[0]['detail'][0] = '默认';
+        }
+        StoreProductAttr::createProductAttr($attr, $detail, $data['id'], 5);
+        \app\admin\model\ump\StoreExchange::where('id', $data['id'])->update(['deposit' => $deposit, 'stock' => $stock, 'quota' => $quota, 'quota_show' => $quota, 'price' => $price, 'ot_price' => $otPrice]);
+        return Json::successful('修改成功!');
+    }
+
+    /**
+     * 生成属性
+     * @param int $id
+     */
+    public function is_format_attr($id = 0)
+    {
+        if (!$id) return Json::fail('商品不存在');
+        list($attr, $detail) = Util::postMore([
+            ['items', []],
+            ['attrs', []]
+        ], $this->request, true);
+        $product = \app\admin\model\ump\StoreExchange::get($id);
+        if (!$product) return Json::fail('商品不存在');
+        $attrFormat = attr_format($attr)[1];
+        if (count($detail)) {
+            foreach ($attrFormat as $k => $v) {
+                foreach ($detail as $kk => $vv) {
+                    if ($v['detail'] == $vv['detail']) {
+                        $attrFormat[$k]['price'] = $vv['price'];
+                        $attrFormat[$k]['sales'] = $vv['sales'];
+                        $attrFormat[$k]['pic'] = $vv['pic'];
+                        $attrFormat[$k]['check'] = false;
+                        break;
+                    } else {
+                        $attrFormat[$k]['price'] = '';
+                        $attrFormat[$k]['sales'] = '';
+                        $attrFormat[$k]['pic'] = $product['image'];
+                        $attrFormat[$k]['check'] = true;
+                    }
+                }
+            }
+        } else {
+            foreach ($attrFormat as $k => $v) {
+                $attrFormat[$k]['price'] = $product['price'];
+                $attrFormat[$k]['sales'] = $product['stock'];
+                $attrFormat[$k]['pic'] = $product['image'];
+                $attrFormat[$k]['check'] = false;
+            }
+        }
+        return Json::successful($attrFormat);
+    }
+
+    /**
+     * 添加 修改属性
+     * @param $id
+     */
+    public function set_attr($id)
+    {
+        if (!$id) return $this->failed('商品不存在!');
+        list($attr, $detail) = Util::postMore([
+            ['items', []],
+            ['attrs', []]
+        ], $this->request, true);
+        $res = StoreProductAttr::createProductAttr($attr, $detail, $id, 5);
+        if ($res)
+            return $this->successful('编辑属性成功!');
+        else
+            return $this->failed(StoreProductAttr::getErrorInfo());
+    }
+
+    /**
+     * 清除属性
+     * @param $id
+     */
+    public function clear_attr($id)
+    {
+        if (!$id) return $this->failed('商品不存在!');
+        if (false !== StoreProductAttr::clearProductAttr($id, 5) && false !== StoreProductAttrResult::clearResult($id))
+            return $this->successful('清空商品属性成功!');
+        else
+            return $this->failed(StoreProductAttr::getErrorInfo('清空商品属性失败!'));
+    }
+
+    /**
+     * 修改秒杀商品状态
+     * @param $status
+     * @param int $id
+     */
+    public function set_seckill_status($status, $id = 0)
+    {
+        if (!$id) return Json::fail('参数错误');
+        $res = StoreProductAttrValue::where('product_id', $id)->where('type', 5)->find();
+        if (!$res) return Json::fail('请先配置规格');
+        $res = \app\admin\model\ump\StoreExchange::edit(['status' => $status], $id);
+        if ($res) return Json::successful('修改成功');
+        else return Json::fail('修改失败');
+    }
+
+    /**
+     * 秒杀获取商品列表
+     * @return string
+     * @throws \Exception
+     */
+    public function productList()
+    {
+        $cate = StoreCategory::getTierList(null, 1);
+        $this->assign('cate', $cate);
+        return $this->fetch();
+    }
+
+    /**
+     * 获取秒杀商品规格
+     * @param $attr
+     * @param $id
+     * @param $type
+     * @return array
+     */
+    public function get_attr($attr, $id, $type)
+    {
+        $value = attr_format($attr)[1];
+        $valueNew = [];
+        $count = 0;
+        foreach ($value as $key => $item) {
+            $detail = $item['detail'];
+//            sort($item['detail'], SORT_STRING);
+            $suk = implode(',', $item['detail']);
+            $sukValue = StoreProductAttrValue::where('product_id', $id)->where('type', $type)->where('suk', $suk)->column('bar_code,cost,price,ot_price,stock,image as pic,weight,volume,deposit,brokerage,brokerage_two,quota', 'suk');
+            if (count($sukValue)) {
+                foreach (array_values($detail) as $k => $v) {
+                    $valueNew[$count]['value' . ($k + 1)] = $v;
+                }
+                $valueNew[$count]['detail'] = json_encode($detail);
+                $valueNew[$count]['pic'] = $sukValue[$suk]['pic'] ?? '';
+                $valueNew[$count]['price'] = $sukValue[$suk]['price'] ? floatval($sukValue[$suk]['price']) : 0;
+                $valueNew[$count]['cost'] = $sukValue[$suk]['cost'] ? floatval($sukValue[$suk]['cost']) : 0;
+                $valueNew[$count]['ot_price'] = isset($sukValue[$suk]['ot_price']) ? floatval($sukValue[$suk]['ot_price']) : 0;
+                $valueNew[$count]['stock'] = $sukValue[$suk]['stock'] ? intval($sukValue[$suk]['stock']) : 0;
+                $valueNew[$count]['quota'] = $sukValue[$suk]['quota'] ? intval($sukValue[$suk]['quota']) : 0;
+                $valueNew[$count]['bar_code'] = $sukValue[$suk]['bar_code'] ?? '';
+                $valueNew[$count]['weight'] = $sukValue[$suk]['weight'] ?? 0;
+                $valueNew[$count]['volume'] = $sukValue[$suk]['volume'] ?? 0;
+                $valueNew[$count]['deposit'] = $sukValue[$suk]['deposit'] ?? 0;
+                $valueNew[$count]['brokerage'] = $sukValue[$suk]['brokerage'] ?? 0;
+                $valueNew[$count]['brokerage_two'] = $sukValue[$suk]['brokerage_two'] ?? 0;
+                $valueNew[$count]['check'] = $type != 0 ? 1 : 0;
+                $count++;
+            }
+        }
+        return $valueNew;
+    }
+}

+ 13 - 3
app/admin/model/order/StoreOrder.php

@@ -57,11 +57,12 @@ class StoreOrder extends BaseModel
         $data['yt'] = self::statusByWhere(-2, new self())->where($where)->where(['is_system_del' => 0])->count();
         $data['del'] = self::statusByWhere(-4, new self())->where($where)->where(['is_system_del' => 0])->count();
         $data['write_off'] = self::statusByWhere(5, new self())->where($where)->where(['is_system_del' => 0])->count();
-        $data['general'] = self::where(['pink_id' => 0, 'integral_id' => 0, 'combination_id' => 0, 'seckill_id' => 0, 'bargain_id' => 0, 'is_system_del' => 0])->where($where)->count();
+        $data['general'] = self::where(['pink_id' => 0, 'exchange_id' => 0, 'integral_id' => 0, 'combination_id' => 0, 'seckill_id' => 0, 'bargain_id' => 0, 'is_system_del' => 0])->where($where)->count();
         $data['pink'] = self::where('pink_id|combination_id', '>', 0)->where('is_system_del', 0)->where($where)->count();
         $data['seckill'] = self::where('seckill_id', '>', 0)->where('is_system_del', 0)->where($where)->count();
         $data['bargain'] = self::where('bargain_id', '>', 0)->where('is_system_del', 0)->where($where)->count();
         $data['integral'] = self::where('integral_id', '>', 0)->where('is_system_del', 0)->where($where)->count();
+        $data['exchange'] = self::where('exchange_id', '>', 0)->where('is_system_del', 0)->where($where)->count();
         return $data;
     }
 
@@ -126,6 +127,9 @@ class StoreOrder extends BaseModel
             } elseif ($item['integral_id']) {
                 $item['pink_name'] = '[积分订单]';
                 $item['color'] = '#9fc5e9';
+            } elseif ($item['exchange_id']) {
+                $item['pink_name'] = '[积分订单]';
+                $item['color'] = '#9fe9c5';
             } else {
                 if ($item['shipping_type'] == 1) {
                     $item['pink_name'] = '[普通订单]';
@@ -500,7 +504,7 @@ HTML;
         if (isset($where['is_del']) && $where['is_del'] != '' && $where['is_del'] != -1) $model = $model->where($aler . 'is_del', $where['is_del']);
         if (isset($where['combination_id'])) {
             if ($where['combination_id'] == '普通订单') {
-                $model = $model->where($aler . 'combination_id', 0)->where($aler . 'seckill_id', 0)->where($aler . 'bargain_id', 0);
+                $model = $model->where($aler . 'combination_id', 0)->where($aler . 'integral_id', 0)->where($aler . 'exchange_id', 0)->where($aler . 'seckill_id', 0)->where($aler . 'bargain_id', 0);
             }
             if ($where['combination_id'] == '拼团订单') {
                 $model = $model->where($aler . 'combination_id', ">", 0)->where($aler . 'pink_id', ">", 0);
@@ -514,6 +518,9 @@ HTML;
             if ($where['combination_id'] == '积分订单') {
                 $model = $model->where($aler . 'integral_id', ">", 0);
             }
+            if ($where['combination_id'] == '兑换券订单') {
+                $model = $model->where($aler . 'exchange_id', ">", 0);
+            }
         }
         if (isset($where['pay_type'])) {
             switch ($where['pay_type']) {
@@ -531,7 +538,7 @@ HTML;
         if (isset($where['type'])) {
             switch ($where['type']) {
                 case 1:
-                    $model = $model->where($aler . 'integral_id', 0)->where($aler . 'combination_id', 0)->where($aler . 'seckill_id', 0)->where($aler . 'bargain_id', 0);
+                    $model = $model->where($aler . 'exchange_id', 0)->where($aler . 'integral_id', 0)->where($aler . 'combination_id', 0)->where($aler . 'seckill_id', 0)->where($aler . 'bargain_id', 0);
                     break;
                 case 2:
 //                    $model = $model->where($aler.'combination_id',">",0)->where($aler.'pink_id',">",0);
@@ -546,6 +553,9 @@ HTML;
                 case 5:
                     $model = $model->where($aler . 'integral_id', ">", 0);
                     break;
+                case 6:
+                    $model = $model->where($aler . 'exchange_id', ">", 0);
+                    break;
             }
         }
 

+ 360 - 0
app/admin/model/ump/StoreExchange.php

@@ -0,0 +1,360 @@
+<?php
+/**
+ * @author: xaboy<365615158@qq.com>
+ * @day: 2017/11/11
+ */
+
+namespace app\admin\model\ump;
+
+use app\admin\model\order\StoreOrder;
+use app\admin\model\store\StoreProductRelation;
+use app\admin\model\system\SystemGroupData;
+use crmeb\traits\ModelTrait;
+use crmeb\basic\BaseModel;
+use app\admin\model\store\StoreProduct;
+use crmeb\services\PHPExcelService;
+
+/**
+ * Class StoreSeckill
+ * @package app\admin\model\store
+ */
+class StoreExchange extends BaseModel
+{
+    /**
+     * 数据表主键
+     * @var string
+     */
+    protected $pk = 'id';
+
+    /**
+     * 模型名称
+     * @var string
+     */
+    protected $name = 'store_exchange';
+
+    use ModelTrait;
+
+    public function getDescriptionAttr($value)
+    {
+        return htmlspecialchars_decode($value);
+    }
+
+    /**
+     * 秒杀产品过滤条件
+     * @param $model
+     * @param $type
+     * @return mixed
+     */
+    public static function setWhereType($model, $type)
+    {
+        switch ($type) {
+            case 1:
+                $data = ['status' => 0, 'is_del' => 0];
+                break;
+            case 2:
+                $data = ['status' => 1, 'is_del' => 0];
+                break;
+            case 3:
+                $data = ['status' => 1, 'is_del' => 0, 'stock' => 0];
+                break;
+            case 4:
+                $data = ['status' => 1, 'is_del' => 0, 'stock' => ['elt', 1]];
+                break;
+            case 5:
+                $data = ['is_del' => 1];
+                break;
+        }
+        if (isset($data)) $model = $model->where($data);
+        return $model;
+    }
+
+    /**
+     * 秒杀产品数量 图标展示
+     * @param $type
+     * @param $data
+     * @return array
+     */
+    public static function getChatrdata($type, $data)
+    {
+        $legdata = ['销量', '数量', '点赞', '收藏'];
+        $model = self::setWhereType(self::order('id desc'), $type);
+        $list = self::getModelTime(compact('data'), $model)
+            ->field('FROM_UNIXTIME(add_time,"%Y-%c-%d") as un_time,count(id) as count,sum(sales) as sales')
+            ->group('un_time')
+            ->distinct(true)
+            ->select()
+            ->each(function ($item) use ($data) {
+                $item['collect'] = self::getModelTime(compact('data'), new StoreProductRelation)->where('type', 'collect')->count();
+                $item['like'] = self::getModelTime(compact('data'), new StoreProductRelation())->where('type', 'like')->count();
+            })->toArray();
+        $chatrList = [];
+        $datetime = [];
+        $data_item = [];
+        $itemList = [0 => [], 1 => [], 2 => [], 3 => []];
+        foreach ($list as $item) {
+            $itemList[0][] = $item['sales'];
+            $itemList[1][] = $item['count'];
+            $itemList[2][] = $item['like'];
+            $itemList[3][] = $item['collect'];
+            array_push($datetime, $item['un_time']);
+        }
+        foreach ($legdata as $key => $leg) {
+            $data_item['name'] = $leg;
+            $data_item['type'] = 'line';
+            $data_item['data'] = $itemList[$key];
+            $chatrList[] = $data_item;
+            unset($data_item);
+        }
+        unset($leg);
+        $badge = self::getbadge(compact('data'), $type);
+        $count = self::setWhereType(self::getModelTime(compact('data'), new self()), $type)->count();
+        return compact('datetime', 'chatrList', 'legdata', 'badge', 'count');
+
+    }
+
+    /**
+     * 秒杀产品数量
+     * @param $where
+     * @param $type
+     * @return array
+     */
+    public static function getbadge($where, $type)
+    {
+        $StoreOrderModel = new StoreOrder();
+        $replenishment_num = sys_config('replenishment_num');
+        $replenishment_num = $replenishment_num > 0 ? $replenishment_num : 20;
+        $stock1 = self::getModelTime($where, new self())->where('stock', '<', $replenishment_num)->column('stock', 'id');
+        $sum_stock = self::where('stock', '<', $replenishment_num)->column('stock', 'id');
+        $stk = [];
+        foreach ($stock1 as $item) {
+            $stk[] = $replenishment_num - $item;
+        }
+        $lack = array_sum($stk);
+        $sum = [];
+        foreach ($sum_stock as $val) {
+            $sum[] = $replenishment_num - $val;
+        }
+        return [
+            [
+                'name' => '商品种类',
+                'field' => '件',
+                'count' => self::setWhereType(new self(), $type)->where('add_time', '<', mktime(0, 0, 0, date('m'), date('d'), date('Y')))->count(),
+                'content' => '商品种类总数',
+                'background_color' => 'layui-bg-blue',
+                'sum' => self::count(),
+                'class' => 'fa fa fa-ioxhost',
+            ],
+            [
+                'name' => '新增商品',
+                'field' => '件',
+                'count' => self::setWhereType(self::getModelTime($where, new self), $type)->sum('stock'),
+                'content' => '新增商品总数',
+                'background_color' => 'layui-bg-cyan',
+                'sum' => self::where('status', 1)->sum('stock'),
+                'class' => 'fa fa-line-chart',
+            ],
+            [
+                'name' => '缺货商品',
+                'field' => '件',
+                'count' => $lack,
+                'content' => '总商品数量',
+                'background_color' => 'layui-bg-orange',
+                'sum' => array_sum($sum),
+                'class' => 'fa fa-cube',
+            ],
+        ];
+    }
+
+    /**
+     * 销量排行 top 10
+     * layui-bg-red 红 layui-bg-orange 黄 layui-bg-green 绿 layui-bg-blue 蓝 layui-bg-cyan 黑
+     */
+    public static function getMaxList($where)
+    {
+        $classs = ['layui-bg-red', 'layui-bg-orange', 'layui-bg-green', 'layui-bg-blue', 'layui-bg-cyan'];
+        $model = StoreOrder::alias('a')->join('store_seckill b', 'b.id=a.seckill_id')->where('a.paid', 1);
+        $list = self::getModelTime($where, $model, 'a.add_time')->group('a.seckill_id')->order('p_count desc')->limit(10)
+            ->field(['count(a.seckill_id) as p_count', 'b.title as store_name', 'sum(b.price) as sum_price'])->select();
+        if (count($list)) $list = $list->toArray();
+        $maxList = [];
+        $sum_count = 0;
+        $sum_price = 0;
+        foreach ($list as $item) {
+            $sum_count += $item['p_count'];
+            $sum_price = bcadd($sum_price, $item['sum_price'], 2);
+        }
+        unset($item);
+        foreach ($list as $key => &$item) {
+            $item['w'] = bcdiv($item['p_count'], $sum_count, 2) * 100;
+            $item['class'] = isset($classs[$key]) ? $classs[$key] : (isset($classs[$key - count($classs)]) ? $classs[$key - count($classs)] : '');
+            $item['store_name'] = self::getSubstrUTf8($item['store_name']);
+        }
+        $maxList['sum_count'] = $sum_count;
+        $maxList['sum_price'] = $sum_price;
+        $maxList['list'] = $list;
+        return $maxList;
+    }
+
+    /**
+     * 获取秒杀利润
+     * @param $where
+     * @return array
+     */
+    public static function ProfityTop10($where)
+    {
+        $classs = ['layui-bg-red', 'layui-bg-orange', 'layui-bg-green', 'layui-bg-blue', 'layui-bg-cyan'];
+        $model = StoreOrder::alias('a')->join('store_seckill b', 'b.id = a.seckill_id')->where('a.paid', 1);
+        $list = self::getModelTime($where, $model, 'a.add_time')->group('a.seckill_id')->order('profity desc')->limit(10)
+            ->field(['count(a.seckill_id) as p_count', 'b.title as store_name', 'sum(b.price) as sum_price', '(b.price-b.cost) as profity'])
+            ->select();
+        if (count($list)) $list = $list->toArray();
+        $maxList = [];
+        $sum_count = 0;
+        $sum_price = 0;
+        foreach ($list as $item) {
+            $sum_count += $item['p_count'];
+            $sum_price = bcadd($sum_price, $item['sum_price'], 2);
+        }
+        foreach ($list as $key => &$item) {
+            $item['w'] = bcdiv($item['sum_price'], $sum_price, 2) * 100;
+            $item['class'] = isset($classs[$key]) ? $classs[$key] : (isset($classs[$key - count($classs)]) ? $classs[$key - count($classs)] : '');
+            $item['store_name'] = self::getSubstrUTf8($item['store_name'], 30);
+        }
+        $maxList['sum_count'] = $sum_count;
+        $maxList['sum_price'] = $sum_price;
+        $maxList['list'] = $list;
+        return $maxList;
+    }
+
+    /**
+     * 获取秒杀缺货
+     * @param $where
+     * @return array
+     */
+    public static function getLackList($where)
+    {
+        $replenishment_num = sys_config('replenishment_num');
+        $replenishment_num = $replenishment_num > 0 ? $replenishment_num : 20;
+        $list = self::where('stock', '<', $replenishment_num)->field(['id', 'title as store_name', 'stock', 'price'])->page((int)$where['page'], (int)$where['limit'])->order('stock asc')->select();
+        if (count($list)) $list = $list->toArray();
+        $count = self::where('stock', '<', $replenishment_num)->count();
+        return ['count' => $count, 'data' => $list];
+    }
+
+    /**
+     * 秒杀产品评价
+     * @param array $where
+     * @return array
+     */
+    public static function getNegativeList($where = array())
+    {
+        $replenishment_num = 3;
+        return [];
+    }
+
+    /**
+     * 秒杀产品退货
+     * @param array $where
+     * @return mixed
+     */
+    public static function getBargainRefundList($where = array())
+    {
+        $model = StoreOrder::alias('a')->join('store_seckill b', 'b.id=a.seckill_id');
+        $list = self::getModelTime($where, $model, 'a.add_time')->where('a.refund_status', '<>', 0)->group('a.seckill_id')->order('count desc')->page((int)$where['page'], (int)$where['limit'])
+            ->field(['count(a.seckill_id) as count', 'b.title as store_name', 'sum(b.price) as sum_price'])->select();
+        if (count($list)) $list = $list->toArray();
+        return $list;
+    }
+
+    /**
+     * @param $where
+     * @return array
+     */
+    public static function systemPage($where)
+    {
+        $model = new self;
+        $model = $model->alias('s');
+//        $model = $model->join('StoreProduct p','p.id=s.product_id');
+        if ($where['status'] != '') $model = $model->where('s.status', $where['status']);
+        if ($where['store_name'] != '') $model = $model->where('s.title|s.id', 'LIKE', "%$where[store_name]%");
+        $model = $model->page(bcmul($where['page'], $where['limit'], 0), $where['limit']);
+        $model = $model->order('s.id desc');
+        $model = $model->where('s.is_del', 0);
+        return self::page($model, function ($item) {
+            $item['store_name'] = StoreProduct::where('id', $item['product_id'])->value('store_name');
+            if ($item['status']) {
+                $item['start_name'] = '开启';
+            } else $item['start_name'] = '关闭';
+
+        }, $where, $where['limit']);
+    }
+
+    public static function SaveExcel($where)
+    {
+        $model = new self;
+        if ($where['status'] != '') $model = $model->where('status', $where['status']);
+        if ($where['store_name'] != '') $model = $model->where('title|id', 'LIKE', "%$where[store_name]%");
+        $list = $model->order('id desc')->where('is_del', 0)->select();
+        count($list) && $list = $list->toArray();
+        $excel = [];
+        foreach ($list as $item) {
+            $item['store_name'] = StoreProduct::where('id', $item['product_id'])->value('store_name');
+            $excel[] = [
+                $item['id'],
+                $item['title'],
+                $item['info'],
+                $item['ot_price'],
+                $item['price'],
+                $item['stock'],
+                $item['status'] ? '开启' : '关闭',
+            ];
+        }
+        PHPExcelService::setExcelHeader(['编号', '活动标题', '活动简介', '原价', '价格', '库存', '状态'])
+            ->setExcelTile('兑换券产品导出', ' ', ' 生成时间:' . date('Y-m-d H:i:s', time()))
+            ->setExcelContent($excel)
+            ->ExcelSave();
+    }
+
+    /**
+     * 获取秒杀产品id
+     * @return array
+     */
+    public static function getSeckillIdAll()
+    {
+        return self::where('is_del', 0)->column('id', 'id');
+    }
+
+    /**
+     * 获取秒杀的所有产品
+     * @return int|string
+     */
+    public static function getSeckillCount()
+    {
+        return self::where('is_del', 0)->count();
+    }
+
+    /**
+     * TODO 获取某个字段值
+     * @param $id
+     * @param string $field
+     * @return mixed
+     */
+    public static function getSeckillField($id, $field = 'title')
+    {
+        return self::where('id', $id)->value($field);
+    }
+
+    /**
+     * 判断产品当前时间段是否有秒杀活动
+     * @param $product_id
+     * @param $time_id
+     * @return array|null|\think\Model
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public static function checkSeckill($product_id, $time_id)
+    {
+        return self::where('product_id', $product_id)->where('time_id', $time_id)->where('is_del', 0)->find();
+    }
+}

+ 1 - 0
app/admin/view/order/store_order/index.php

@@ -777,6 +777,7 @@
                     {name: '拼团订单', value: 2, count: orderCount.pink},
                     {name: '秒杀订单', value: 3, count: orderCount.seckill},
                     {name: '积分订单', value: 5, count: orderCount.integral},
+                    {name: '兑换券订单', value: 6, count: orderCount.exchange},
                     // {name: '砍价订单', value: 4, count: orderCount.bargain},
                 ],
                 orderStatus: [

+ 371 - 0
app/admin/view/ump/store_exchange/attr.php

@@ -0,0 +1,371 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+    {include file="public/head"}
+    <title>{$title|default=''}</title>
+    <style>
+        .demo-upload{
+            display: block;
+            height: 33px;
+            text-align: center;
+            border: 1px solid transparent;
+            border-radius: 4px;
+            overflow: hidden;
+            background: #fff;
+            position: relative;
+            box-shadow: 0 1px 1px rgba(0,0,0,.2);
+            margin-right: 4px;
+        }
+        .demo-upload img{
+            width: 100%;
+            height: 100%;
+            display: block;
+        }
+        .demo-upload-cover{
+            display: none;
+            position: absolute;
+            top: 0;
+            bottom: 0;
+            left: 0;
+            right: 0;
+            background: rgba(0,0,0,.6);
+        }
+        .demo-upload:hover .demo-upload-cover{
+            display: block;
+        }
+        .demo-upload-cover i{
+            color: #fff;
+            font-size: 20px;
+            cursor: pointer;
+            margin: 0 2px;
+        }
+        .check{color: #f00}
+    </style>
+</head>
+<body>
+<div id="store-attr" class="mp-form" v-cloak="">
+    <i-Form :label-width="80" style="width: 100%" v-show="hidden == false">
+        <Form-Item>
+            <Row>
+                <i-Col span="5">
+                    <i-Button type="dashed" long @click="hiddenBool" icon="plus-round">添加新规则</i-Button>
+                </i-Col>
+            </Row>
+        </Form-Item>
+    </i-Form>
+    <i-Form :label-width="80" style="width: 100%" v-show="hidden == true">
+        <Form-Item
+                :label="'规则名称:'">
+            <Row>
+                <i-Col style="position: relative;margin-right: 6px"  span="5"
+                       v-for="(item, index) in items"
+                       :key="index">
+                    <i-Input type="text" v-model="item.value" placeholder="设置名称"></i-Input>
+                    <i-Button style="position: absolute;top:0;right:0;margin-top:1px;border: none;font-size: 8px;line-height: 1.8" type="ghost" @click="handleRemove(index)" v-show="item.attrHidden == true"><Icon type="close-round" /></i-Button>
+                    <i-Button style="position: absolute;top:0;right:0;margin-top:1px;border: none;font-size: 8px;line-height: 1.8" type="ghost" @click="attrHiddenBool(item)" v-show="item.attrHidden == false"><Icon type="checkmark-round"></Icon></i-Button>
+                </i-Col>
+                <i-Col span="5">
+                    <i-Button type="dashed" long @click="handleAdd" icon="plus-round">添加新规则</i-Button>
+                </i-Col>
+            </Row>
+        </Form-Item>
+        <Form-Item v-show="item.attrHidden == true"
+                   v-for="(item, index) in items"
+                   :key="index"
+                   :label="''+item.value+':'" >
+            <Row>
+                <i-Col span="3"
+                       v-for="(attr,k) in item.detail"
+                       :key="attr"
+                       :name="attr">
+                    <Tag type="border" closable color="blue" @on-close="attrRemove(item,k)">{{ attr }}</Tag>
+                </i-Col>
+                <i-Col span="5">
+                    <i-Input type="text" v-model="item.detailValue" placeholder="设置属性"></i-Input>
+                </i-Col>
+                <i-Col span="5">
+                    <i-Button type="primary" style="margin-left: 6px" @click="attrAdd(item)">添加</i-Button>
+                </i-Col>
+            </Row>
+        </Form-Item>
+        <Form-Item v-show="hidden == true" style="width: 100%;">
+            <Row style="margin: 0 88px 0 20px">
+                <i-Col span="24">
+                    <i-Button type="primary" long @click="addGoods(true)">生成</i-Button>
+                </i-Col>
+            </Row>
+        </Form-Item>
+
+        <template v-if="items[0].value!='' && items[0].detail.length>0 && attrs.length">
+            <template v-for="(attr,index) in attrs">
+                <Form-Item>
+                    <Row>
+                        <template v-for="(item,index) in attr.detail">
+                            <i-Col span="3" style="margin-right: 3px">
+                                {{index}}:{{item}}
+                            </i-Col>
+                        </template>
+                        <i-Col span="5" style="margin-right: 3px">
+                            <span :class="attr.check ? 'check':''">金额:</span>&nbsp;&nbsp;<i-Input placeholder="请输入金额" v-model="attr.price" style="width: 68%"
+                                        :number="true"></i-Input>
+                        </i-Col>
+                        <i-Col span="5" style="margin-right: 3px">
+                            <span :class="attr.check ? 'check':''">库存:</span>&nbsp;&nbsp;<i-Input placeholder="请输入库存" v-model="attr.sales" style="width: 68%"
+                                        :number="true"></i-Input>
+                        </i-Col>
+                        <i-Col span="2" offset="1" style="margin-right: 3px">
+                            <div class="demo-upload">
+                                <img :src="attr.pic">
+                                <div class="demo-upload-cover">
+                                    <Icon type="ios-eye-outline" @click.native="openPic(attr.pic)" ></Icon>
+                                    <Upload
+                                            :show-upload-list="false"
+                                            :on-success="uploadSuccess(attr)"
+                                            :on-error="uploadError"
+                                            :format="['jpg','jpeg','png']"
+                                            :max-size="2048"
+                                            accept="image/*"
+                                            :on-format-error="uploadFormatError"
+                                            action="{:Url('upload')}"
+                                            style="display: inline-block"
+                                            :goods="attr"
+                                    >
+                                        <Icon type="ios-cloud-upload-outline"></Icon>
+                                    </Upload>
+
+                                </div>
+                            </div>
+                        </i-Col>
+                        <i-Col span="2" style="margin-right: 3px">
+                            <i-Button type="ghost" @click="removeGoods(index)">删除</i-Button>
+                        </i-Col>
+                    </Row>
+                </Form-Item>
+            </template>
+            <Form-Item>
+                <Row>
+                    <!--                    <i-Col span="10">-->
+                    <!--                        <i-Button type="dashed" long @click="addGoods" icon="plus-round">添加新商品</i-Button>-->
+                    <!--                    </i-Col>-->
+                    <i-Col span="2" offset="2">
+                        <i-Button type="primary" @click="submit">提交</i-Button>
+                    </i-Col>
+                    <i-Col span="2" offset="1">
+                        <i-Button type="error" @click="clear">清空所有属性</i-Button>
+                    </i-Col>
+                </Row>
+            </Form-Item>
+        </template>
+    </i-Form>
+    <Spin fix v-show="submiting == true">保存中...</Spin>
+</div>
+<script>
+    var _vm ;
+    mpFrame.start(function(Vue){
+        new Vue({
+            data () {
+                return {
+                    hidden:false,
+                    submiting :false,
+                    items: <?php echo $result && isset($result['attr']) && !empty($result['attr']) ? json_encode($result['attr']) : 'false'; ?> || [
+                    {
+                        value: '',
+                        detailValue:'',
+                        attrHidden:false,
+                        detail:[]
+                    }
+                ],
+                    attrs:<?php echo $result && isset($result['value']) && !empty($result['value']) ? json_encode($result['value']) : '[]'; ?>
+            }
+            },
+            watch:{
+                items:{
+                    handler:function(){
+//                        this.attrs = [];
+                    },
+                    deep:true
+                }
+            },
+            methods: {
+                attrHiddenBool(item){
+                    if(item.value == ''){
+                        $eb.message('error','请填写规则名称');
+                    }else{
+                        item.attrHidden = true;
+                    }
+                },
+                hiddenBool(){
+                    this.hidden = true;
+                },
+                handleAdd () {
+                    if(!this.checkAttr())return ;
+                    this.items.push({
+                        value: '',
+                        detailValue:'',
+                        attrHidden:false,
+                        detail:[]
+                    });
+                },
+                checkAttr(){
+                    var bool = true;
+                    this.items.map(function(item){
+                        if(!bool) return;
+                        if(!item.value){
+                            $eb.message('error','请填写规则名称');
+                            bool = false;
+                        }else if(!item.detail.length){
+                            $eb.message('error','请设置规则属性');
+                            bool = false;
+                        }
+                    });
+                    return bool;
+                },
+                attrAdd (item) {
+                    if(!item.detailValue) return false;
+                    item.detail.push(item.detailValue);
+                    item.detailValue = '';
+                },
+                handleRemove (index) {
+                    if(this.items.length > 1)
+                        this.items.splice(index,1);
+                    else
+                        $eb.message('error','请设置至少一个规则');
+                },
+                attrRemove(item,k){
+                    if(1==item.detail.length){
+                        $eb.message('error','请设置至少一个属性');
+                        return false;
+                    }
+                    item.detail.splice(k,1);
+                },
+                removeGoods(index){
+                    this.attrs.splice(index,1);
+                },
+                checkGoods(){
+                    var bool = true;
+                    this.attrs.map(function(attr){
+                        if(!bool) return ;
+                        if(!Object.keys(attr.detail).length){
+                            $eb.message('error','请选择至少一个属性');
+                            bool = false;
+                        }else if(attr.price != parseFloat(attr.price) || attr.price < 0){
+                            $eb.message('error','请输入正确的商品价格');
+                            bool = false;
+                        }else if(attr.sales != parseInt(attr.sales) || attr.sales < 0){
+                            $eb.message('error','请输入正确的商品库存');
+                            bool = false;
+                        }
+                    });
+                    return bool;
+                },
+                addGoods(type){
+                    var that = this;
+                    if(this.attrs.length){
+                        if(!this.checkGoods())return ;
+                    }
+                    $eb.axios.post("{:Url('is_format_attr',array('id'=>$id))}",{items:this.items,attrs:this.attrs}).then(function(res){
+                        if(res.data.code == 200){
+                            that.attrs = res.data.data
+                        }else{
+                            $eb.message('error',res.data.msg);
+                        }
+                    }).catch(function(err){
+                        if(res.data.code == 200){
+                            that.attrs = res.data.data
+                        }else{
+                            $eb.message('error',res.data.msg);
+                        }
+                    })
+//                    if(type === true){
+//                        this.attrs = [{
+//                            detail:{},
+//                            price:'',
+//                            sales:'',
+//                            pic:'{$image}'
+//                        }];
+//                    }else{
+//                        this.attrs.push({
+//                            detail:{},
+//                            price:'',
+//                            sales:'',
+//                            pic:'{$image}'
+//                        });
+//                    }
+                },
+                openPic(src){
+                    $eb.openImage(src);
+                },
+                uploadSuccess(data){
+                    return function(response, file, fileList){
+                        if(response.code == 200){
+                            data.pic = response.data.url;
+                        }else{
+                            $eb.message('error',response.data.msg || '图片上传失败!');
+                        }
+                    }
+                },
+                uploadError(error, file, fileList){
+                    $eb.message('error',error);
+                },
+                uploadFormatError(file, fileList){
+                    $eb.message('error','图片格式错误');
+                },
+                submit(){
+                    var that = this;
+                    that.submiting = true;
+                    if(!this.checkAttr() || !this.checkGoods()) return ;
+                    for(let attr in that.attrs){
+                        that.attrs[attr].check = false;
+                    }
+                    $eb.axios.post("{:Url('set_attr',array('id'=>$id))}",{items:this.items,attrs:this.attrs}).then(function(res){
+                        that.submiting = false;
+                        if(res.status == 200 && res.data.code == 200){
+                            $eb.message('success',res.data.msg || '编辑成功!');
+                            $eb.closeModalFrame(window.name);
+                        }else{
+                            $eb.message('error',res.data.msg || '请求失败!');
+                        }
+                    }).catch(function(err){
+                        $eb.message('error',err);
+                    })
+                },
+                clear(){
+                    var that = this;
+                    requirejs(['sweetalert'], function (swel) {
+                        swel({
+                            title: "您确定要清空商品属性吗",
+                            text: "删除后将无法恢复,请谨慎操作!",
+                            type: "warning",
+                            showCancelButton: true,
+                            confirmButtonColor: "#DD6B55",
+                            confirmButtonText: "是的,我要清空!",
+                            cancelButtonText: "让我再考虑一下…",
+                            closeOnConfirm: false,
+                            closeOnCancel: false
+                        }).then(function () {
+                            $eb.axios.post("{:Url('clear_attr',array('id'=>$id))}", {
+                                items: that.items,
+                                attrs: that.attrs
+                            }).then(function (res) {
+                                if (res.status == 200 && res.data.code == 200) {
+                                    $eb.message('success', res.data.msg || '清空成功!');
+                                    window.location.reload();
+                                } else {
+                                    $eb.message('error', res.data.msg || '清空失败!');
+                                }
+                            }).catch(function (err) {
+                                $eb.message('error', err);
+                            })
+                        }).catch(console.log);
+                    });
+                }
+            },
+            mounted (){
+                _vm = this;
+                var resultAdmin = <?php echo $result && isset($result['attr']) && !empty($result['attr']) ? json_encode($result['attr']) : 'false'; ?>;
+                if(resultAdmin) this.hidden = true;
+            }
+        }).$mount(document.getElementById('store-attr'));
+    });
+</script>
+</body>

+ 122 - 0
app/admin/view/ump/store_exchange/attr_list.php

@@ -0,0 +1,122 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
+    {include file="public/head"}
+    <script src="/static/plug/jquery-1.4.1.min.js"></script>
+    <link rel="stylesheet" href="/static/plug/layui/css/layui.css">
+    <script src="/static/plug/layui/layui.js"></script>
+    <style>
+        .layui-form-checkbox{
+            margin-top: 0!important;
+            margin-right: 0;
+        }
+        .layui-form-checkbox i{
+            border-left: 1px solid #d2d2d2;
+        }
+    </style>
+</head>
+<body>
+<form class="layui-form" action="" style="padding: 30px 50px 0px 0px;">
+    <div class="layui-form-item">
+        <label class="layui-form-label">规格选择</label>
+        <div class="layui-input-block">
+            <table class="layui-table attrTab">
+                <thead>
+                <tr>
+                    {volist name="attr.attr" id="vo"}
+                    <th>{$vo.value}</th>
+                    {/volist}
+                    <th>图片</th>
+                    <th>售价</th>
+                    <th>成本价</th>
+                    <th>原价</th>
+                    <th>库存</th>
+                    <th>限量</th>
+                    <th>商品编号</th>
+                    <th>重量</th>
+                    <th>体积</th>
+                    <th>押金</th>
+                    <th>选择</th>
+                </tr>
+                </thead>
+                <tbody>
+                {volist name="attr.value" id="vo" key="k"}
+                <tr>
+                    {volist name="$vo" id="item"}
+                    {if condition="$key eq 'pic'"}
+                    <td id="cl{$k}"><input type="hidden" name="attr[{$k}][{$key}]" class="layui-input" value="{$item}"><img src="{$item}" width="50px" onclick="createFrame('选择图片','{:Url('widget.images/index',['fodder'=>'image'])}',{w:900,h:550},'cl{$k}')"></td>
+                    {elseif condition="$key eq 'detail'"/}
+                    <input type="hidden" name="attr[{$k}][{$key}]" class="layui-input" value="{$item}">
+                    {elseif condition="$key eq 'check'"/}
+                    <td><input type="checkbox" name="ids[]" value="{$k}" {if condition="$item eq 1"}checked{/if}></td>
+                    {elseif condition="($key neq 'brokerage') AND ($key neq 'brokerage_two')"/}
+                    {if condition="($key neq 'price') AND ($key neq 'quota')"}
+                    <td style="text-align: center"><span>{$item}</span><input type="hidden" name="attr[{$k}][{$key}]" class="layui-input" value="{$item}"></td>
+                    {else/}
+                    <td><input type="number" name="attr[{$k}][{$key}]" class="layui-input max" value="{$item}" min="0" max="{if condition="$key eq 'quota'"}{$vo['stock']}{else/}{$vo['ot_price']}{/if}" oninput="checknum(this)"></td>
+                    {/if}
+                    {/if}
+                    {/volist}
+                </tr>
+                {/volist}
+                </tbody>
+            </table>
+        </div>
+    </div>
+    <div class="layui-form-item">
+        <div class="layui-input-block">
+            <input type="hidden" name="id" value="{$id}">
+            <button type="button" class="layui-btn" lay-submit lay-filter="formDemo">立即提交</button>
+        </div>
+    </div>
+</form>
+<script src="{__ADMIN_PATH}js/layuiList.js"></script>
+<script>
+    var cl = '';
+    layui.use('form', function () {
+        var form = layui.form;
+        form.on('submit(formDemo)', function (data) {
+            layList.basePost('save_attr', data.field, function (res) {
+                parent.layer.close(parent.layer.getFrameIndex(window.name));
+                parent.layer.msg(res.msg, {icon:1,time:2000});
+            }, function (res) {
+                parent.layer.msg(res.msg, {icon:1,time:2000});
+            });
+        });
+    });
+    function createFrame(title,src,opt,k){
+        cl = k;
+        opt === undefined && (opt = {});
+        var h = parent.document.body.clientHeight - 100;
+        return layer.open({
+            type: 2,
+            title:title,
+            area: [(opt.w || 700)+'px', (opt.h || h)+'px'],
+            fixed: false, //不固定
+            maxmin: true,
+            moveOut:false,//true  可以拖出窗外  false 只能在窗内拖
+            anim:5,//出场动画 isOutAnim bool 关闭动画
+            offset:'auto',//['100px','100px'],//'auto',//初始位置  ['100px','100px'] t[ 上 左]
+            shade:0,//遮罩
+            resize:true,//是否允许拉伸
+            content: src,//内容
+            move:'.layui-layer-title'
+        });
+    }
+    function changeIMG(index,pic){
+        $('#'+cl).children('input').val(pic);
+        $('#'+cl).children('img').attr('src',pic);
+    }
+    function checknum(e){
+        if(parseInt(e.value)>parseInt(e.max)){
+            $(e).val(e.max);
+        }
+        if(parseInt(e.value)<0){
+            $(e).val(0);
+        }
+    }
+</script>
+</body>
+</html>

+ 218 - 0
app/admin/view/ump/store_exchange/index.php

@@ -0,0 +1,218 @@
+{extend name="public/container"}
+{block name="head_top"}
+<script type="text/javascript" src="{__PLUG_PATH}jquery.downCount.js"></script>
+{/block}
+{block name="content"}
+<div class="layui-fluid">
+    <div class="layui-row layui-col-space15"  id="app">
+        <div class="layui-col-md12">
+            <div class="layui-card">
+                <div class="layui-card-header">兑换券商品搜索</div>
+                <div class="layui-card-body">
+                    <div class="alert alert-success alert-dismissable">
+                        <button aria-hidden="true" data-dismiss="alert" class="close" type="button">×</button>
+                        目前拥有{$countSeckill}个兑换券商品
+                    </div>
+                    <form class="layui-form">
+                        <div class="layui-form-item">
+                            <div class="layui-inline">
+                                <label class="layui-form-label">搜  索:</label>
+                                <div class="layui-input-inline">
+                                    <input type="text" name="store_name" lay-verify="store_name" style="width: 100%" autocomplete="off" placeholder="请输入商品名称,关键字,编号" class="layui-input">
+                                </div>
+                            </div>
+                            <div class="layui-inline">
+                                <label class="layui-form-label">兑换券状态:</label>
+                                <div class="layui-input-inline">
+                                    <select name="status" lay-verify="status">
+                                        <option value="">全部</option>
+                                        <option value="1">开启</option>
+                                        <option value="0">关闭</option>
+                                    </select>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="layui-form-item">
+                            <label class="layui-form-label">
+                                <button class="layui-btn layui-btn-sm" lay-submit="" lay-filter="search" style="font-size:14px;line-height: 9px;">
+                                    <i class="layui-icon layui-icon-search layuiadmin-button-btn"></i>搜索</button>
+                                <button lay-submit="export" lay-filter="export" class="layui-btn layui-btn-primary layui-btn-sm">
+                                    <i class="layui-icon layui-icon-delete layuiadmin-button-btn" ></i> Excel导出</button>
+                            </label>
+                        </div>
+                    </form>
+                </div>
+            </div>
+        </div>
+        <div class="layui-col-md12">
+            <div class="layui-card">
+                <div class="layui-card-header">兑换券商品列表</div>
+                <div class="layui-card-body">
+                    <div class="layui-btn-container">
+                        <a class="layui-btn layui-btn-sm" onclick="$eb.createModalFrame(this.innerText,'{:Url('create')}',{h:700,w:1100});">添加兑换券商品</a>
+                    </div>
+                    <table class="layui-hide" id="seckillList" lay-filter="seckillList"></table>
+                    <script type="text/html" id="status">
+                        <input type='checkbox' name='status' lay-skin='switch' value="{{d.id}}" lay-filter='status' lay-text='开启|关闭'  {{ d.status == 1 ? 'checked' : '' }}>
+                    </script>
+                    <script type="text/html" id="statusCn">
+                        {{ d.status == 1 ? d.start_name : '关闭' }}
+                    </script>
+                    <script type="text/html" id="barDemo">
+                        <button type="button" class="layui-btn layui-btn-xs" onclick="$eb.createModalFrame('{{d.title}}-设置规格','{:Url('attr_list')}?id={{d.id}}',{h:1000,w:1400});"><i class="layui-icon layui-icon-util"></i>规格</button>
+
+                        <button type="button" class="layui-btn layui-btn-xs" onclick="dropdown(this)">操作<span class="caret"></span></button>
+                        <ul class="layui-nav-child layui-anim layui-anim-upbit">
+                            <li>
+                                <a href="javascript:void(0);" onclick="$eb.createModalFrame('{{d.title}}-编辑','{:Url('edit')}?id={{d.id}}')"><i class="layui-icon layui-icon-edit"></i> 编辑活动</a>
+                            </li>
+                            <li>
+                                <a href="javascript:void(0);" onclick="$eb.createModalFrame('{{d.title}}-编辑内容','{:Url('edit_content')}?id={{d.id}}')"><i class="layui-icon layui-icon-edit"></i>编辑内容</a>
+                            </li>
+                            <li>
+                                <a href="javascript:void(0);" class="delstor" lay-event='delstor'><i class="layui-icon layui-icon-delete"></i> 删除</a>
+                            </li>
+                        </ul>
+                    </script>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+{/block}
+{block name="script"}
+<script src="{__ADMIN_PATH}js/layuiList.js"></script>
+<script src="{__FRAME_PATH}js/content.min.js?v=1.0.0"></script>
+<script>
+    layList.form.render();
+    layList.tableList('seckillList',"{:Url('get_seckill_list')}",function () {
+        return [
+            {field: 'id', title: 'ID', sort: true,width:'6%',event:'id'},
+            {field: 'image', title: '商品图片', width: '10%',templet: '<p><img src="{{d.image}}" alt="{{d.title}}" class="open_image" data-image="{{d.image}}"></p>'},
+            {field: 'title', title: '活动标题'},
+            {field: 'info', title: '活动简介',width:'20%'},
+            {field: 'ot_price', title: '原价',width:'6%'},
+            {field: 'price', title: '兑换券价格',width:'6%'},
+            {field: 'quota_show', title: '限量',width:'6%'},
+            {field: 'quota', title: '限量剩余',width:'6%'},
+            {field: 'start_name', title: '秒杀状态',width:'6%',toolbar:"#statusCn"},
+            {field: 'status', title: '状态',width:'6%',toolbar:"#status"},
+            {field: 'right', title: '操作',width:'10%', align: 'center', toolbar: '#barDemo'}
+        ]
+    });
+    layList.tool(function (event,data,obj) {
+        switch (event) {
+            case 'delstor':
+                var url=layList.U({c:'ump.store_exchange',a:'delete',q:{id:data.id}});
+                $eb.$swal('delete',function(){
+                    $eb.axios.get(url).then(function(res){
+                        if(res.status == 200 && res.data.code == 200) {
+                            $eb.$swal('success',res.data.msg);
+                            obj.del();
+                        }else
+                            return Promise.reject(res.data.msg || '删除失败')
+                    }).catch(function(err){
+                        $eb.$swal('error',err);
+                    });
+                })
+                break;
+        }
+    })
+    $(document).click(function (e) {
+        $('.layui-nav-child').hide();
+    })
+    function dropdown(that){
+        var oEvent = arguments.callee.caller.arguments[0] || event;
+        oEvent.stopPropagation();
+        var offset = $(that).offset();
+        var top=offset.top-$(window).scrollTop();
+        var index = $(that).parents('tr').data('index');
+        $('.layui-nav-child').each(function (key) {
+            if (key != index) {
+                $(this).hide();
+            }
+        })
+        if($(document).height() < top+$(that).next('ul').height()){
+            $(that).next('ul').css({
+                'padding': 10,
+                'top': - ($(that).parent('td').height() / 2 + $(that).height() + $(that).next('ul').height()/2),
+                'min-width': 'inherit',
+                'position': 'absolute'
+            }).toggle();
+        }else{
+            $(that).next('ul').css({
+                'padding': 10,
+                'top':$(that).parent('td').height() / 2 + $(that).height(),
+                'min-width': 'inherit',
+                'position': 'absolute'
+            }).toggle();
+        }
+    }
+    layList.search('search',function(where){
+        layList.reload(where);
+        setTime();
+    });
+    layList.search('export',function(where){
+        location.href=layList.U({c:'ump.store_exchange',a:'save_excel',q:{status:where.status,store_name:where.store_name}});
+    })
+    layList.switch('status',function (odj,value,name) {
+        if (odj.elem.checked == true) {
+            layList.baseGet(layList.Url({
+                c: 'ump.store_exchange',
+                a: 'set_seckill_status',
+                p: {status: 1, id: value}
+            }), function (res) {
+                layList.msg(res.msg);
+            }, function () {
+                odj.elem.checked = false;
+                layui.form.render();
+                layer.open({
+                    type: 1
+                    ,offset: 'auto'
+                    ,id: 'layerDemoauto' //防止重复弹出
+                    ,content: '<div style="padding: 20px 100px;">请先配置规格</div>'
+                    ,btn: '设置规格'
+                    ,btnAlign: 'c' //按钮居中
+                    ,shade: 0 //不显示遮罩
+                    ,yes: function(){
+                        layer.closeAll();
+                        $eb.createModalFrame('设置规格','{:Url('attr_list')}?id='+value+'',{h:1000,w:1400});
+                    }
+                });
+            });
+        } else {
+            layList.baseGet(layList.Url({
+                c: 'ump.store_exchange',
+                a: 'set_seckill_status',
+                p: {status: 0, id: value}
+            }), function (res) {
+                layList.msg(res.msg);
+            });
+        }
+    })
+    $('.js-group-btn').on('click',function(){
+        $('.js-group-btn').css({zIndex:1});
+        $(this).css({zIndex:2});
+    });
+    $('#delstor').on('click',function(){
+        window.t = $(this);
+        var _this = $(this),url =_this.data('url');
+        $eb.$swal('delete',function(){
+            $eb.axios.get(url).then(function(res){
+                console.log(res);
+                if(res.status == 200 && res.data.code == 200) {
+                    $eb.$swal('success',res.data.msg);
+                    _this.parents('tr').remove();
+                }else
+                    return Promise.reject(res.data.msg || '删除失败')
+            }).catch(function(err){
+                $eb.$swal('error',err);
+            });
+        })
+    });
+    $(document).on('click',".open_image",function (e) {
+        var image = $(this).data('image');
+        $eb.openImage(image);
+    });
+</script>
+{/block}

+ 104 - 0
app/admin/view/ump/store_exchange/product_list.php

@@ -0,0 +1,104 @@
+{extend name="public/container"}
+{block name="content"}
+<style type="text/css">
+    .form-add{position: fixed;left: 0;bottom: 0;width:100%;}
+    .form-add .sub-btn{border-radius: 0;width: 100%;padding: 6px 0;font-size: 14px;outline: none;border: none;color: #fff;background-color: #2d8cf0;}
+</style>
+<div class="layui-fluid">
+    <div class="layui-row layui-col-space15"  id="app">
+        <div class="layui-col-md12">
+            <div class="layui-card">
+                <div class="layui-card-header">搜索条件</div>
+                <div class="layui-card-body">
+                    <form class="layui-form layui-form-pane" action="">
+                        <div class="layui-inline">
+                            <label class="layui-form-label">所有分类</label>
+                            <div class="layui-input-block">
+                                <select name="cate_id">
+                                    <option value=" ">全部</option>
+                                    {volist name='cate' id='vo'}
+                                    <option value="{$vo.id}">{$vo.html}{$vo.cate_name}</option>
+                                    {/volist}
+                                </select>
+                            </div>
+                        </div>
+                        <div class="layui-inline">
+                            <label class="layui-form-label">商品名称</label>
+                            <div class="layui-input-block">
+                                <input type="text" name="store_name" class="layui-input" placeholder="请输入商品名称,关键字,编号">
+                            </div>
+                        </div>
+                        <div class="layui-inline">
+                            <div class="layui-input-inline">
+                                <button class="layui-btn layui-btn-sm layui-btn-normal" lay-submit="search" lay-filter="search">
+                                    <i class="layui-icon layui-icon-search"></i>搜索</button>
+                            </div>
+                        </div>
+                    </form>
+                </div>
+            </div>
+        </div>
+        <div class="layui-col-md12">
+            <div class="layui-card">
+                <div class="layui-card-body">
+                    <table class="layui-hide" id="List" lay-filter="List"></table>
+                    <!--图片-->
+                    <script type="text/html" id="image">
+                        <img style="cursor: pointer" lay-event="open_image" src="{{d.image}}">
+                    </script>
+                    <!--操作-->
+                    <script type="text/html" id="act">
+                        <button type="button" class="layui-btn layui-btn-normal layui-btn-sm select" lay-event='select'>选择</button>
+                    </script>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<script src="{__ADMIN_PATH}js/layuiList.js"></script>
+{/block}
+{block name='script'}
+<script>
+    var parentinputname = '{$Request.param.fodder}';
+    layList.form.render();
+    //加载列表
+    layList.tableList('List',"{:Url('store.store_product/product_ist',['type'=>1])}",function (){
+        return [
+            {field: 'id', title: 'ID', sort: true,event:'id',width:'8%'},
+            {field: 'image', title: '商品图片',templet:'#image',width:'12%'},
+            {field: 'store_name', title: '商品名称',templet:'#store_name',width:'40%'},
+            {field: 'right', title: '操作',align:'center',toolbar:'#act'}
+        ]
+    });
+    //点击事件绑定
+    layList.tool(function (event,data) {
+        switch (event) {
+            case 'select':
+                parent.$f.changeField('product',data.image);
+                parent.$f.changeField('product_id',data.id);
+                parent.$f.changeField('title',data.store_name);
+                parent.$f.changeField('store_name',data.store_name);
+                parent.$f.changeField('info',data.store_info);
+                parent.$f.changeField('unit_name',data.unit_name);
+                parent.$f.changeField('temp_id',data.temp_id.toString());
+                parent.$f.changeField('image',data.image);
+                parent.$f.changeField('images',eval('('+data.slider_image+')'));
+                parent.$f.changeField('price',data.price);
+                parent.$f.changeField('ot_price',data.ot_price);
+                parent.$f.changeField('cost',data.cost);
+                parent.$f.changeField('stock',data.stock);
+                parent.$f.changeField('sales',data.sales);
+                parent.$f.changeField('sort',data.sort);
+                parent.$f.changeField('num',1);
+                parent.$f.changeField('give_integral',data.give_integral);
+                parent.$f.changeField('description',data.description);
+                parent.$f.closeModal(parentinputname);
+                break;
+        }
+    })
+    //查询
+    layList.search('search',function(where){
+        layList.reload(where);
+    });
+</script>
+{/block}

+ 10 - 7
app/api/controller/order/StoreOrderController.php

@@ -71,19 +71,22 @@ class StoreOrderController
         $combination_id = 0;
         $bargain_id = 0;
         $integral_id = 0;
+        $exchange_id = 0;
         if (count($cartIdA) == 1) {
             $seckill_id = StoreCart::where('id', $cartId)->value('seckill_id');
             $combination_id = StoreCart::where('id', $cartId)->value('combination_id');
             $bargain_id = StoreCart::where('id', $cartId)->value('bargain_id');
             $integral_id = StoreCart::where('id', $cartId)->value('integral_id');
+            $exchange_id = StoreCart::where('id', $cartId)->value('exchange_id');
         }
-        $data['deduction'] = $seckill_id || $combination_id || $bargain_id || $integral_id;
+        $data['deduction'] = $seckill_id || $combination_id || $bargain_id || $integral_id || $exchange_id;
         $data['usableCoupon'] = $usableCoupon;
         $data['addressInfo'] = UserAddress::getUserDefaultAddress($uid);
         $data['seckill_id'] = $seckill_id;
         $data['combination_id'] = $combination_id;
         $data['bargain_id'] = $bargain_id;
         $data['integral_id'] = $integral_id;
+        $data['exchange_id'] = $exchange_id;
         $data['cartInfo'] = $cartInfo;
         $data['priceGroup'] = $priceGroup;
         $data['orderKey'] = StoreOrder::cacheOrderInfo($uid, $cartInfo, $priceGroup, $other);
@@ -122,8 +125,8 @@ class StoreOrderController
         $uid = $request->uid();
         if (StoreOrder::be(['order_id|unique' => $key, 'uid' => $uid, 'is_del' => 0]))
             return app('json')->status('extend_order', '订单已生成', ['orderId' => $key, 'key' => $key]);
-        list($addressId, $couponId, $payType, $useIntegral, $mark, $combinationId, $pinkId, $seckill_id, $integral_id, $formId, $bargainId, $shipping_type) = UtilService::postMore([
-            'addressId', 'couponId', ['payType', 'yue'], ['useIntegral', 0], 'mark', ['combinationId', 0], ['pinkId', 0], ['seckill_id', 0], ['integral_id', 0], ['formId', ''], ['bargainId', ''],
+        list($addressId, $couponId, $payType, $useIntegral, $mark, $combinationId, $pinkId, $seckill_id, $integral_id, $exchange_id, $formId, $bargainId, $shipping_type) = UtilService::postMore([
+            'addressId', 'couponId', ['payType', 'yue'], ['useIntegral', 0], 'mark', ['combinationId', 0], ['pinkId', 0], ['seckill_id', 0], ['integral_id', 0], ['exchange_id', 0], ['formId', ''], ['bargainId', ''],
             ['shipping_type', 1],
         ], $request, true);
         $payType = strtolower($payType);
@@ -142,7 +145,7 @@ class StoreOrderController
             if (StoreOrder::getIsOrderPink($pinkId, $request->uid()))
                 return app('json')->status('ORDER_EXIST', '订单生成失败,你已经参加该团了,请先支付订单', ['orderId' => StoreOrder::getStoreIdPink($pinkId, $request->uid())]);
         }
-        $priceGroup = StoreOrder::cacheKeyCreateOrder($request->uid(), $key, $addressId, $payType, (int)$useIntegral, $couponId, $mark, $combinationId, $pinkId, $seckill_id, $bargainId, $integral_id, true, 0, $shipping_type, '', '', $request->store_id());
+        $priceGroup = StoreOrder::cacheKeyCreateOrder($request->uid(), $key, $addressId, $payType, (int)$useIntegral, $couponId, $mark, $combinationId, $pinkId, $seckill_id, $bargainId, $integral_id, $exchange_id, true, 0, $shipping_type, '', '', $request->store_id());
         if ($priceGroup)
             return app('json')->status('NONE', 'ok', $priceGroup);
         else
@@ -165,8 +168,8 @@ class StoreOrderController
         $uid = $request->uid();
         if (StoreOrder::be(['order_id|unique' => $key, 'uid' => $uid, 'is_del' => 0]))
             return app('json')->status('extend_order', '订单已生成', ['orderId' => $key, 'key' => $key]);
-        list($addressId, $couponId, $payType, $useIntegral, $mark, $combinationId, $pinkId, $seckill_id, $integral_id, $formId, $bargainId, $from, $shipping_type, $real_name, $phone, $time_area, $storeId, $pointId, $free_check) = UtilService::postMore([
-            'addressId', 'couponId', 'payType', ['useIntegral', 0], 'mark', ['combinationId', 0], ['pinkId', 0], ['seckill_id', 0], ['integral_id', 0], ['formId', ''], ['bargainId', ''], ['from', 'weixin'],
+        list($addressId, $couponId, $payType, $useIntegral, $mark, $combinationId, $pinkId, $seckill_id, $integral_id, $exchange_id, $formId, $bargainId, $from, $shipping_type, $real_name, $phone, $time_area, $storeId, $pointId, $free_check) = UtilService::postMore([
+            'addressId', 'couponId', 'payType', ['useIntegral', 0], 'mark', ['combinationId', 0], ['pinkId', 0], ['seckill_id', 0], ['integral_id', 0], ['exchange_id', 0], ['formId', ''], ['bargainId', ''], ['from', 'weixin'],
             ['shipping_type', 1], ['real_name', ''], ['phone', ''], ['time_area', ''], ['store_id', 0], ['point_id', 0], ['free_check', 0]
         ], $request, true);
         $payType = strtolower($payType);
@@ -190,7 +193,7 @@ class StoreOrderController
             $isChannel = 0;
         elseif ($from == 'weixinh5')
             $isChannel = 2;
-        $order = StoreOrder::cacheKeyCreateOrder($request->uid(), $key, $addressId, $payType, (int)$useIntegral, $couponId, $mark, $combinationId, $pinkId, $seckill_id, $bargainId, $integral_id, false, $isChannel, $shipping_type, $real_name, $phone, $storeId, $pointId, 0, $free_check, $time_area);
+        $order = StoreOrder::cacheKeyCreateOrder($request->uid(), $key, $addressId, $payType, (int)$useIntegral, $couponId, $mark, $combinationId, $pinkId, $seckill_id, $bargainId, $integral_id, $exchange_id, false, $isChannel, $shipping_type, $real_name, $phone, $storeId, $pointId, 0, $free_check, $time_area);
         if ($order === false) return app('json')->fail(StoreOrder::getErrorInfo('订单生成失败'));
         $orderId = $order['order_id'];
         $info = compact('orderId', 'key');

+ 23 - 6
app/models/store/StoreCart.php

@@ -42,7 +42,7 @@ class StoreCart extends BaseModel
         return time();
     }
 
-    public static function setCart($uid, $product_id, $cart_num = 1, $product_attr_unique = '', $type = 'product', $is_new = 0, $combination_id = 0, $seckill_id = 0, $bargain_id = 0, $integral_id = 0)
+    public static function setCart($uid, $product_id, $cart_num = 1, $product_attr_unique = '', $type = 'product', $is_new = 0, $combination_id = 0, $seckill_id = 0, $bargain_id = 0, $integral_id = 0, $exchange_id = 0)
     {
         if ($cart_num < 1) $cart_num = 1;
         if ($seckill_id) {
@@ -94,6 +94,19 @@ class StoreCart extends BaseModel
             $product_stock = StoreProductAttrValue::where('product_id', $StoreIntegralInfo['product_id'])->where('suk', $res['suk'])->where('type', 0)->value('stock');
             if ($product_stock < $cart_num)
                 return self::setErrorInfo('该产品库存不足' . $cart_num);
+        } elseif ($exchange_id) {
+            $StoreIntegralInfo = StoreExchange::getValidProduct($exchange_id);
+            if (!$StoreIntegralInfo)
+                return self::setErrorInfo('该产品已下架或删除');
+            $userbuycount = StoreOrder::where('uid', $uid)->where('paid', 1)->where('exchange_id', $exchange_id)->count();
+            if ($StoreIntegralInfo['num'] <= $userbuycount || $StoreIntegralInfo['num'] < $cart_num)
+                return self::setErrorInfo('每人限购' . $StoreIntegralInfo['num'] . '件');
+            $res = StoreProductAttrValue::where('product_id', $exchange_id)->where('unique', $product_attr_unique)->where('type', 5)->field('suk,quota')->find();
+            if ($cart_num > $res['quota'])
+                return self::setErrorInfo('该产品库存不足' . $cart_num);
+            $product_stock = StoreProductAttrValue::where('product_id', $StoreIntegralInfo['product_id'])->where('suk', $res['suk'])->where('type', 0)->value('stock');
+            if ($product_stock < $cart_num)
+                return self::setErrorInfo('该产品库存不足' . $cart_num);
         } else {
             if (!StoreProduct::isValidProduct($product_id))
                 return self::setErrorInfo('该产品已下架或删除');
@@ -102,7 +115,7 @@ class StoreCart extends BaseModel
             if (StoreProduct::getProductStock($product_id, $product_attr_unique) < $cart_num)
                 return self::setErrorInfo('该产品库存不足' . $cart_num);
         }
-        if ($cart = self::where('type', $type)->where('uid', $uid)->where('product_id', $product_id)->where('product_attr_unique', $product_attr_unique)->where('is_new', $is_new)->where('is_pay', 0)->where('is_del', 0)->where('combination_id', $combination_id)->where('bargain_id', $bargain_id)->where('seckill_id', $seckill_id)->where('integral_id', $integral_id)->find()) {
+        if ($cart = self::where('type', $type)->where('uid', $uid)->where('product_id', $product_id)->where('product_attr_unique', $product_attr_unique)->where('is_new', $is_new)->where('is_pay', 0)->where('is_del', 0)->where('combination_id', $combination_id)->where('bargain_id', $bargain_id)->where('seckill_id', $seckill_id)->where('exchange_id', $exchange_id)->where('integral_id', $integral_id)->find()) {
             if ($is_new)
                 $cart->cart_num = $cart_num;
             else
@@ -112,7 +125,7 @@ class StoreCart extends BaseModel
             return $cart;
         } else {
             $add_time = time();
-            return self::create(compact('uid', 'product_id', 'cart_num', 'product_attr_unique', 'is_new', 'type', 'combination_id', 'add_time', 'bargain_id', 'seckill_id', 'integral_id'));
+            return self::create(compact('uid', 'product_id', 'cart_num', 'product_attr_unique', 'is_new', 'type', 'combination_id', 'add_time', 'bargain_id', 'seckill_id', 'integral_id', 'exchange_id'));
         }
     }
 
@@ -174,6 +187,7 @@ class StoreCart extends BaseModel
         $bargainInfoField = 'id,image,min_price as price,price as ot_price,postage,give_integral,sales,stock,title as store_name,unit_name,status as is_show,is_del,is_postage,cost,temp_id,weight,volume';
         $combinationInfoField = 'id,image,price,postage,sales,stock,title as store_name,is_show,is_del,is_postage,cost,temp_id,weight,volume';
         $IntegralInfoField = 'id,image,price,integral,ot_price,postage,give_integral,sales,stock,title as store_name,unit_name,is_show,is_del,is_postage,cost,temp_id,weight,volume';
+        $ExchangeInfoField = 'id,image,price,integral,ot_price,postage,give_integral,sales,stock,title as store_name,unit_name,is_show,is_del,is_postage,cost,temp_id,weight,volume';
         $model = new self();
         $valid = $invalid = [];
         $model = $model->where('uid', $uid)->where('type', 'product')->where('is_pay', 0)
@@ -197,6 +211,9 @@ class StoreCart extends BaseModel
             } elseif ($cart['integral_id']) {
                 $product = StoreIntegral::field($IntegralInfoField)
                     ->find($cart['integral_id'])->toArray();
+            } elseif ($cart['exchange_id']) {
+                $product = StoreExchange::field($ExchangeInfoField)
+                    ->find($cart['exchange_id'])->toArray();
             } else {
                 $product = StoreProduct::field($productInfoField)
                     ->find($cart['product_id'])->toArray();
@@ -215,7 +232,7 @@ class StoreCart extends BaseModel
             } else if ($cart['seckill_id'] && ($product['start_time'] > $now || $product['stop_time'] < $now - 86400)) {
                 $invalid[] = $product;
                 //商品属性不对应
-            } else if (!StoreProductAttr::issetProductUnique($cart['product_id'], $cart['product_attr_unique']) && !$cart['combination_id'] && !$cart['seckill_id'] && !$cart['bargain_id'] && !$cart['integral_id']) {
+            } else if (!StoreProductAttr::issetProductUnique($cart['product_id'], $cart['product_attr_unique']) && !$cart['combination_id'] && !$cart['seckill_id'] && !$cart['bargain_id'] && !$cart['integral_id'] && !$cart['exchange_id']) {
                 $invalid[] = $cart;
                 //正常商品
             } else {
@@ -252,7 +269,7 @@ class StoreCart extends BaseModel
                             if ($stock_info && $stock_info['price'] > 0) $attrInfo['price'] = $stock_info['price'];
                         }
                         $cart['productInfo']['attrInfo'] = $attrInfo;
-                        if ($cart['combination_id'] || $cart['seckill_id'] || $cart['bargain_id'] || $cart['integral_id']) {
+                        if ($cart['combination_id'] || $cart['seckill_id'] || $cart['bargain_id'] || $cart['integral_id'] || $cart['exchange_id']) {
                             $cart['truePrice'] = $attrInfo['price'];
                             $cart['vip_truePrice'] = 0;
                             $cart['integral'] = $attrInfo['integral'];
@@ -268,7 +285,7 @@ class StoreCart extends BaseModel
                         $valid[] = $cart;
                     }
                 } else {
-                    if ($cart['combination_id'] || $cart['seckill_id'] || $cart['bargain_id'] || $cart['integral_id']) {
+                    if ($cart['combination_id'] || $cart['seckill_id'] || $cart['bargain_id'] || $cart['integral_id'] || $cart['exchange_id']) {
                         $cart['truePrice'] = $cart['productInfo']['price'];
                         $cart['integral'] = $cart['productInfo']['integral'] ?? 0;
                         $cart['vip_truePrice'] = 0;

+ 205 - 0
app/models/store/StoreExchange.php

@@ -0,0 +1,205 @@
+<?php
+/**
+ *
+ * @author: xaboy<365615158@qq.com>
+ * @day: 2017/12/18
+ */
+
+namespace app\models\store;
+
+use crmeb\basic\BaseModel;
+use crmeb\services\GroupDataService;
+use app\admin\model\store\StoreProductAttrValue;
+use app\models\store\StoreProduct;
+
+/**
+ * TODO 秒杀产品Model
+ * Class StoreSeckill
+ * @package app\models\store
+ */
+class StoreExchange extends BaseModel
+{
+    /**
+     * 数据表主键
+     * @var string
+     */
+    protected $pk = 'id';
+
+    /**
+     * 模型名称
+     * @var string
+     */
+    protected $name = 'store_exchange';
+
+    protected function getImagesAttr($value)
+    {
+        return json_decode($value, true) ?: [];
+    }
+
+    public function getDescriptionAttr($value)
+    {
+        return htmlspecialchars_decode($value);
+    }
+
+    public static function getSeckillCount()
+    {
+        return self::where('is_del', 0)->where('status', 1)->count();
+    }
+
+    /**
+     * 获取秒杀列表
+     * @param $time
+     * @param int $page
+     * @param int $limit
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public static function seckillList($page = 0, $limit = 20)
+    {
+//        var_dump($page);
+//        var_dump($limit);
+        if ($page) $list = self::alias('n')->join('store_product c', 'c.id=n.product_id')->where('c.is_show', 1)->where('c.is_del', 0)->where('n.is_del', 0)->where('n.status', 1)->field('n.*')->order('n.sort desc')->page($page, $limit)->select();
+        else $list = self::alias('n')->join('store_product c', 'c.id=n.product_id')->where('c.is_show', 1)->where('c.is_del', 0)->where('n.is_del', 0)->where('n.status', 1)->field('n.*')->order('sort desc')->select();
+        if ($list) return $list->hidden(['cost', 'add_time', 'is_del'])->toArray();
+        return [];
+    }
+
+    /**
+     * 获取所有秒杀产品
+     * @param string $field
+     * @return array
+     */
+    public static function getListAll($offset = 0, $limit = 10, $field = 'id,product_id,image,title,price,integral,ot_price,start_time,stop_time,stock,sales')
+    {
+        $model = self::where('is_del', 0)->where('status', 1)->where('stock', '>', 0)->field($field)
+           ->order('sort DESC,add_time DESC');
+        $model = $model->limit($offset, $limit);
+        $list = $model->select();
+        if ($list) return $list->toArray();
+        else return [];
+    }
+
+    /**
+     * 获取热门推荐的秒杀产品
+     * @param int $limit
+     * @param string $field
+     * @return array
+     */
+    public static function getHotList($limit = 0, $field = 'id,product_id,image,title,price,ot_price,start_time,stop_time,stock,integral')
+    {
+        $model = self::where('is_hot', 1)->where('is_del', 0)->where('status', 1)->where('stock', '>', 0)->field($field)
+            ->order('sort DESC,add_time DESC');
+        if ($limit) $model->limit($limit);
+        $list = $model->select();
+        if ($list) return $list->toArray();
+        else return [];
+    }
+
+    /**
+     * 获取一条秒杀产品
+     * @param $id
+     * @param string $field
+     * @return array|false|\PDOStatement|string|\think\Model
+     */
+    public static function getValidProduct($id, $field = '*')
+    {
+        $info = self::alias('n')->join('store_product c', 'c.id=n.product_id')->where('n.id', $id)->where('c.is_show', 1)->where('c.is_del', 0)->where('n.is_del', 0)->where('n.status', 1)->field('n.*,SUM(c.sales+c.ficti) as total')->find();
+        if ($info['id']) {
+            return $info;
+        } else {
+            return [];
+        }
+    }
+
+    /**
+     * 获取秒杀是否有开启
+     * @return bool
+     */
+    public static function getSeckillContStatus()
+    {
+        $count = self::where('is_del', 0)->where('status', 1)->count();
+        return $count ? true : false;
+    }
+
+    public static function initFailSeckill()
+    {
+        self::where('is_hot', 1)->where('is_del', 0)->where('status', '<>', 1)->update(['status' => '-1']);
+    }
+
+    public static function idBySimilaritySeckill($id, $limit = 4, $field = '*')
+    {
+        $list = [];
+        $productId = self::where('id', $id)->value('product_id');
+        if ($productId) {
+            $list = array_merge($list, self::where('product_id', $productId)->where('id', '<>', $id)
+                ->where('is_del', 0)->where('status', 1)->where('stock', '>', 0)
+                ->field($field)
+                ->order('sort DESC,add_time DESC')->limit($limit)->select()->toArray());
+        }
+        $limit = $limit - count($list);
+        if ($limit) {
+            $list = array_merge($list, self::getHotList($limit, $field));
+        }
+
+        return $list;
+    }
+
+    /** 获取秒杀产品库存
+     * @param $id
+     * @return mixed
+     */
+    public static function getProductStock($id)
+    {
+        return self::where('id', $id)->value('stock');
+    }
+
+    /**
+     * 获取字段值
+     * @param $id
+     * @param string $field
+     * @return mixed
+     */
+    public static function getProductField($id, $field = 'title')
+    {
+        return self::where('id', $id)->value($field);
+    }
+
+    /**
+     * 修改秒杀库存
+     * @param int $num
+     * @param int $seckillId
+     * @return bool
+     */
+    public static function decIntegralStock($num = 0, $seckillId = 0, $unique = '')
+    {
+        $product_id = self::where('id', $seckillId)->value('product_id');
+        if ($unique) {
+            $res = false !== StoreProductAttrValue::decProductAttrStock($seckillId, $unique, $num, 5);
+            $res = $res && self::where('id', $seckillId)->dec('stock', $num)->inc('sales', $num)->update();
+            $sku = StoreProductAttrValue::where('product_id', $seckillId)->where('unique', $unique)->where('type', 5)->value('suk');
+            $res = $res && StoreProductAttrValue::where('product_id', $product_id)->where('suk', $sku)->where('type', 0)->dec('stock', $num)->inc('sales', $num)->update();
+        } else {
+            $res = false !== self::where('id', $seckillId)->dec('stock', $num)->inc('sales', $num)->update();
+        }
+        $res = $res && StoreProduct::where('id', $product_id)->dec('stock', $num)->inc('sales', $num)->update();
+        return $res;
+    }
+
+    /**
+     * 增加库存较少销量
+     * @param int $num
+     * @param int $seckillId
+     * @return bool
+     */
+    public static function incSeckillStock($num = 0, $seckillId = 0)
+    {
+        $seckill = self::where('id', $seckillId)->field(['stock', 'sales'])->find();
+        if (!$seckill) return true;
+        if ($seckill->sales > 0) $seckill->sales = bcsub($seckill->sales, $num, 0);
+        if ($seckill->sales < 0) $seckill->sales = 0;
+        $seckill->stock = bcadd($seckill->stock, $num, 0);
+        return $seckill->save();
+    }
+}

+ 7 - 2
app/models/store/StoreOrder.php

@@ -280,7 +280,7 @@ class StoreOrder extends BaseModel
      * @throws \think\exception\DbException
      */
 
-    public static function cacheKeyCreateOrder($uid, $key, $addressId, $payType, $useIntegral = false, $couponId = 0, $mark = '', $combinationId = 0, $pinkId = 0, $seckill_id = 0, $bargain_id = 0, $integral_id = 0, $test = false, $isChannel = 0, $shipping_type = 1, $real_name = '', $phone = '', $storeId = 0, $pointId = 0, $store_order = 0, $free_check = 0, $time_area = '')
+    public static function cacheKeyCreateOrder($uid, $key, $addressId, $payType, $useIntegral = false, $couponId = 0, $mark = '', $combinationId = 0, $pinkId = 0, $seckill_id = 0, $bargain_id = 0, $integral_id = 0, $exchange_id = 0, $test = false, $isChannel = 0, $shipping_type = 1, $real_name = '', $phone = '', $storeId = 0, $pointId = 0, $store_order = 0, $free_check = 0, $time_area = '')
     {
         self::beginTrans();
         try {
@@ -344,10 +344,11 @@ class StoreOrder extends BaseModel
                 if (!$bargain_id) $bargain_id = $cart['bargain_id'];
                 if (!$combinationId) $combinationId = $cart['combination_id'];
                 if (!$integral_id) $integral_id = $cart['integral_id'];
+                if (!$exchange_id) $exchange_id = $cart['exchange_id'];
                 $cartInfoGainIntegral = isset($cart['productInfo']['give_integral']) ? bcmul($cart['cart_num'], $cart['productInfo']['give_integral'], 2) : 0;
                 $gainIntegral = bcadd($gainIntegral, $cartInfoGainIntegral, 2);
             }
-            $deduction = $seckill_id || $bargain_id || $combinationId || $integral_id;
+            $deduction = $seckill_id || $bargain_id || $combinationId || $integral_id || $exchange_id;
             if ($deduction) {
                 $couponId = 0;
                 $useIntegral = false;
@@ -471,6 +472,7 @@ class StoreOrder extends BaseModel
                 'seckill_id' => $seckill_id,
                 'bargain_id' => $bargain_id,
                 'integral_id' => $integral_id,
+                'exchange_id' => $exchange_id,
                 'cost' => $priceGroup['costPrice'],
                 'is_channel' => $isChannel,
                 'add_time' => time(),
@@ -511,6 +513,7 @@ class StoreOrder extends BaseModel
                 else if ($seckill_id) $res5 = $res5 && StoreSeckill::decSeckillStock($cart['cart_num'], $seckill_id, isset($cart['productInfo']['attrInfo']) ? $cart['productInfo']['attrInfo']['unique'] : '');
                 else if ($bargain_id) $res5 = $res5 && StoreBargain::decBargainStock($cart['cart_num'], $bargain_id, isset($cart['productInfo']['attrInfo']) ? $cart['productInfo']['attrInfo']['unique'] : '');
                 else if ($integral_id) $res5 = $res5 && StoreIntegral::decIntegralStock($cart['cart_num'], $integral_id, isset($cart['productInfo']['attrInfo']) ? $cart['productInfo']['attrInfo']['unique'] : '');
+                else if ($exchange_id) $res5 = $res5 && StoreExchange::decIntegralStock($cart['cart_num'], $exchange_id, isset($cart['productInfo']['attrInfo']) ? $cart['productInfo']['attrInfo']['unique'] : '');
                 else $res5 = $res5 && StoreProduct::decProductStock($cart['cart_num'], $cart['productInfo']['id'], isset($cart['productInfo']['attrInfo']) ? $cart['productInfo']['attrInfo']['unique'] : '');
             }
 //保存购物车商品信息
@@ -570,6 +573,7 @@ class StoreOrder extends BaseModel
         $seckill_id = $order['seckill_id'];
         $bargain_id = $order['bargain_id'];
         $integral_id = $order['integral_id'];
+        $exchange_id = $order['exchange_id'];
         $res5 = true;
         $cartInfo = StoreOrderCartInfo::where('cart_id', 'in', $order['cart_id'])->select();
         foreach ($cartInfo as $cart) {
@@ -578,6 +582,7 @@ class StoreOrder extends BaseModel
             else if ($seckill_id) $res5 = $res5 && StoreSeckill::incSeckillStock($cart['cart_info']['cart_num'], $seckill_id);
             else if ($bargain_id) $res5 = $res5 && StoreBargain::incBargainStock($cart['cart_info']['cart_num'], $bargain_id);
             else if ($integral_id) $res5 = $res5 && StoreIntegral::incSeckillStock($cart['cart_info']['cart_num'], $integral_id);
+            else if ($exchange_id) $res5 = $res5 && StoreExchange::incSeckillStock($cart['cart_info']['cart_num'], $exchange_id);
             else $res5 = $res5 && StoreProduct::incProductStock($cart['cart_info']['cart_num'], $cart['cart_info']['productInfo']['id'], isset($cart['cart_info']['productInfo']['attrInfo']) ? $cart['cart_info']['productInfo']['attrInfo']['unique'] : '');
         }
         return $res5;