牟新芬 3 years ago
parent
commit
04307753ef

+ 107 - 1
app/common.php

@@ -100,4 +100,110 @@ function getLastTime($time) {
     $lDay = intval(($nowTime - $time) / $day);
 
     return ($lDay + 1).'天未消费';
-}
+}
+
+/**
+ * 分级排序
+ * @param $data
+ * @param int $pid
+ * @param string $field
+ * @param string $pk
+ * @param string $html
+ * @param int $level
+ * @param bool $clear
+ * @return array
+ */
+function sort_list_tier($data, $pid = 0, $field = 'pid', $pk = 'id', $html = '|-----', $level = 1, $clear = true)
+{
+    static $list = [];
+    if ($clear) $list = [];
+    foreach ($data as $k => $res) {
+        if ($res[$field] == $pid) {
+            $res['html'] = str_repeat($html, $level);
+            $list[] = $res;
+            unset($data[$k]);
+            sort_list_tier($data, $res[$pk], $field, $pk, $html, $level + 1, false);
+        }
+    }
+    return $list;
+}
+
+/**
+ * 上传路径转化,默认路径
+ * @param $path
+ * @param int $type
+ * @param bool $force
+ * @return string
+ */
+function make_path($path, int $type = 2, bool $force = false)
+{
+    $path = DS . ltrim(rtrim($path));
+    switch ($type) {
+        case 1:
+            $path .= DS . date('Y');
+            break;
+        case 2:
+            $path .= DS . date('Y') . DS . date('m');
+            break;
+        case 3:
+            $path .= DS . date('Y') . DS . date('m') . DS . date('d');
+            break;
+    }
+    try {
+        if (is_dir(app()->getRootPath() . 'public' . DS . 'uploads' . $path) == true || mkdir(app()->getRootPath() . 'public' . DS . 'uploads' . $path, 0777, true) == true) {
+            return trim(str_replace(DS, '/', $path), '.');
+        } else return '';
+    } catch (\Exception $e) {
+        if ($force)
+            throw new \Exception($e->getMessage());
+        return '无法创建文件夹,请检查您的上传目录权限:' . app()->getRootPath() . 'public' . DS . 'uploads' . DS . 'attach' . DS;
+    }
+
+}
+
+/**
+ * 格式化属性
+ * @param $arr
+ * @return array
+ */
+function attr_format($arr)
+{
+    $data = [];
+    $res = [];
+    $count = count($arr);
+    if ($count > 1) {
+        for ($i = 0; $i < $count - 1; $i++) {
+            if ($i == 0) $data = $arr[$i]['detail'];
+            //替代变量1
+            $rep1 = [];
+            foreach ($data as $v) {
+                foreach ($arr[$i + 1]['detail'] as $g) {
+                    //替代变量2
+                    $rep2 = ($i != 0 ? '' : $arr[$i]['value'] . '_$_') . $v . '-$-' . $arr[$i + 1]['value'] . '_$_' . $g;
+                    $tmp[] = $rep2;
+                    if ($i == $count - 2) {
+                        foreach (explode('-$-', $rep2) as $k => $h) {
+                            //替代变量3
+                            $rep3 = explode('_$_', $h);
+                            //替代变量4
+                            $rep4['detail'][$rep3[0]] = isset($rep3[1]) ? $rep3[1] : '';
+                        }
+                        if($count == count($rep4['detail']))
+                            $res[] = $rep4;
+                    }
+                }
+            }
+            $data = isset($tmp) ? $tmp : [];
+        }
+    } else {
+        $dataArr = [];
+        foreach ($arr as $k => $v) {
+            foreach ($v['detail'] as $kk => $vv) {
+                $dataArr[$kk] = $v['value'] . '_' . $vv;
+                $res[$kk]['detail'][$v['value']] = $vv;
+            }
+        }
+        $data[] = implode('-', $dataArr);
+    }
+    return [$data, $res];
+}

+ 1 - 1
app/model/system/Attachment.php

@@ -1,5 +1,5 @@
 <?php
-namespace app\models\system;
+namespace app\model\system;
 
 use library\traits\ModelTrait;
 use library\basic\BaseModel;

+ 6 - 31
app/model/system/AttachmentCategory.php

@@ -1,13 +1,13 @@
 <?php
-namespace app\models\system;
+namespace app\model\system;
 
 use library\traits\ModelTrait;
 use library\basic\BaseModel;
 
 /**
  * 附件目录
- * Class SystemAttachmentCategory
- * @package app\models\system
+ * Class AttachmentCategory
+ * @package app\model\system
  */
 class AttachmentCategory extends BaseModel
 {
@@ -26,28 +26,6 @@ class AttachmentCategory extends BaseModel
 
     use ModelTrait;
 
-    /**
-     * 添加分类
-     * @param $name
-     * @param $att_size
-     * @param $att_type
-     * @param $att_dir
-     * @param string $satt_dir
-     * @param int $pid
-     * @return AttachmentCategory|\think\Model
-     */
-    public static function Add($name,$att_size,$att_type,$att_dir,$satt_dir='',$pid = 0)
-    {
-        $data['name'] = $name;
-        $data['att_dir'] = $att_dir;
-        $data['satt_dir'] = $satt_dir;
-        $data['att_size'] = $att_size;
-        $data['att_type'] = $att_type;
-        $data['time'] = time();
-        $data['pid'] = $pid;
-        return self::create($data);
-    }
-
     /**
      * 获取分类图
      * @return array
@@ -59,13 +37,11 @@ class AttachmentCategory extends BaseModel
         $model = new self;
         if($name) $model = $model->where('name','LIKE',"%$name%");
         if($mer_id) $model = $model->where('mer_id', 'in', [0, $mer_id]);
-//        $count=$model->where('pid',0)->count();
         $list=self::tidyMenuTier($model->select(),0);
         return compact('list');
     }
     public static function tidyMenuTier($menusList,$pid = 0,$navList = [])
     {
-
         foreach ($menusList as $k=>$menu){
             $menu = $menu->getData();
             $menu['title']=$menu['name'];
@@ -96,16 +72,15 @@ class AttachmentCategory extends BaseModel
 
     /**
      * 获取单条信息
-     * @param $att_id
+     * @param $id
      * @return mixed
      * @throws \think\db\exception\DataNotFoundException
      * @throws \think\db\exception\ModelNotFoundException
      * @throws \think\exception\DbException
      */
-    public static function getinfo($att_id){
+    public static function getinfo($id){
         $model = new self;
-        $where['att_id'] = $att_id;
-        return $model->where($where)->select()->toArray()[0];
+        return $model->where('id',$id)->find()->toArray();
     }
 
 }

+ 16 - 2
app/model/system/Category.php

@@ -127,10 +127,24 @@ class Category extends BaseModel
         return $child;
     }
 
+    /**
+     * 分级排序列表
+     * @param null $model
+     * @param int $type
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @throws DbException
+     */
+    public static function getTierList($model = null, $type = 0)
+    {
+        if ($model === null) $model = new self();
+        if (!$type) return sort_list_tier($model->order('sort desc,id desc')->where('pid', 0)->select()->toArray());
+        return sort_list_tier($model->order('sort desc,id desc')->select()->toArray());
+    }
 
 
 
 
 
-
-}
+}

+ 80 - 78
app/model/system/Product.php

@@ -1,107 +1,109 @@
 <?php
-declare (strict_types = 1);
 
 namespace app\model\system;
 
 use library\basic\BaseModel;
-use library\traits\JwtAuthModelTrait;
 use library\traits\ModelTrait;
-use think\Model;
 
 /**
- * @mixin \think\Model
+ * TODO 商品Model
+ * Class Product
+ * @package app\model\system
  */
 class Product extends BaseModel
 {
-    use ModelTrait;
-    use JwtAuthModelTrait;
-    //仓库
-    private $warehouse;
-
-
-
-
     /**
-     * 获取列表数据
-     * @param $page
-     * @param $where
-     * @param $pageCount
-     * @param $desc
+     * 数据表主键
+     * @var string
      */
-    public function getList($page,$where = [],$pageCount = 20,$filed = '*',$desc = ''){
-        $data =  $this
-            ->field("p.*,(SELECT cate_name from table_category where p.cate_id = id) as cate_name")
-            ->alias("p")
-            ->when(!empty($where),function ($query) use($where){
-                if(!empty($where['name'])){
-                    $query->whereLike('p.title',"%{$where['name']}%");
-                }
-                if($where['artType']=='open'){
-                    $query->where('p.status',1);
-                }
-                if($where['artType']=='stop'){
-                    $query->where('p.status',0);
-                }
-            })
-            ->order($desc)
-            ->paginate(['list_rows'=>$pageCount,'page'=>$page])
-            ->toArray();
-        foreach ($data['data'] as $k=>$v) {
-            $data['data'][$k]['warehouseAr'] = $this->getWarehouse($v['warehouse_ids']);
-            $data['data'][$k]['add_time'] = date('Y-m-d',$v['add_time']);
-        }
-        return [$data['total'],$data['data']];
-    }
-
+    protected $pk = 'id';
 
     /**
-     * 保存产品数据
-     * @param $post
-     * @return bool|int|string
+     * 模型名称
+     * @var string
      */
-    public function saveProduct($post){
-        if(!empty($post['id'])) {
-            $this->where('id',$post['id'])->save($post);
-            return true;
-        } else {
-            unset($post['id']);
-            $post['add_time'] = time();
-            $bool = $this->insert($post);
-            return $bool;
-        }
-    }
+    protected $name = 'product';
 
+    use  ModelTrait;
 
     /**
-     * 删除数据
-     * @param $id
+     * 获取商品列表
+     * @param $where array
+     * @return array
+     *
      */
-    public function delProduct($id) {
-        $this->where('id',$id)->delete();
-        return true;
+    public static function ProductList($where)
+    {
+        $model = (new Product)->getWhere($where)->field([
+            '*',
+            '(SELECT SUM(stock) FROM table_product_attr_value WHERE product_id = table_product.id AND type = 0) as stock',
+            '(SELECT SUM(sales) FROM table_product_attr_value WHERE product_id = table_product.id AND type = 0) as sales'
+        ]);
+        $model = $model->page((int)$where['page'], (int)$where['limit']);
+        $data = ($data = $model->select()) && count($data) ? $data->toArray() : [];
+        foreach ($data as &$item) {
+            $cateName = Category::alias('c')->join('Category b', 'b.id = c.pid')->where('c.id', $item['cate_id'])->column('c.cate_name as two,b.cate_name as one', 'c.id');
+            $item['cate_name'] = [];
+            foreach ($cateName as $k => $v) {
+                $item['cate_name'][] = $v['one'] . '/' . $v['two'];
+            }
+            $item['cate_name'] = is_array($item['cate_name']) ? implode(',', $item['cate_name']) : '';
+            $item['stock_attr'] = $item['stock'] > 0 ? true : false;//库存
+        }
+        unset($item);
+        $count = (new Product)->getWhere($where)->count();
+        return ['count' => $count, 'list' => $data];
     }
 
-
     /**
-     * 获取仓库数据
-     * @param $ids
-     * @return array
-     * @throws \think\db\exception\DataNotFoundException
-     * @throws \think\db\exception\DbException
-     * @throws \think\db\exception\ModelNotFoundException
+     * 查询条件
+     * @param $model
+     * @return object
      */
-    private function getWarehouse($ids) {
-        $idAr = explode(',',$ids);
-        $rAr = [];
-        if(empty($this->warehouse))
-            $this->warehouse = (new Warehouse)->select()->toArray();
-        foreach ($this->warehouse as $v) {
-            if(in_array($v['id'],$idAr)) {
-                $rAr[] = $v;
+    public function getWhere($where = [])
+    {
+        $model = new self();
+        if (!empty($where)) {
+            $type = $where['type'] ?? 0;
+            switch ((int)$type) {
+                case 1:
+                    $model = $model->where(['is_show' => 1, 'is_del' => 0]);
+                    break;
+                case 2:
+                    $model = $model->where(['is_show' => 0, 'is_del' => 0]);
+                    break;
+                case 3:
+                    $model = $model->where(['is_del' => 0]);
+                    break;
+                case 4:
+                    $model = $model->where(['is_del' => 0])->whereIn('id', function ($query) {
+                        $query->name('product_attr_value')->where('stock', 0)->where('type', 0)->field('product_id')->select();
+                    })->where(function ($query) {
+                        $query->whereOr('stock', 0);
+                    });
+                    break;
+                case 5:
+                    $model = $model->where(['is_show' => 1, 'is_del' => 0])->where('stock', '<=', 3)->where('stock', '>', 0);
+                    break;
+                case 6:
+                    $model = $model->where(['is_del' => 1]);
+                    break;
+                case 7:
+                    $model = $model->where(['is_one' => 1, 'is_del' => 0]);
+                    break;
+            };
+            if (isset($where['mer_id']) && $where['mer_id'] != '') {
+                $model = $model->where('mer_id', $where['mer_id']);
+            }
+            if (isset($where['store_name']) && $where['store_name'] != '') {
+                $model = $model->where('store_name|keyword|id', 'LIKE', "%$where[store_name]%");
             }
+            if (isset($where['cate_id']) && $where['cate_id'] != '') {
+                $model = $model->where('cate_id', $where['cate_id']);
+            }
+            $model = $model->order('sort desc,id desc');
         }
-        return $rAr;
+        return $model;
     }
 
-
 }

+ 219 - 0
app/model/system/ProductAttr.php

@@ -0,0 +1,219 @@
+<?php
+
+namespace app\model\system;
+
+use app\model\system\ProductAttrResult;
+use app\model\system\ProductAttrValue;
+use library\basic\BaseModel;
+use think\db\exception\DataNotFoundException;
+use think\db\exception\DbException;
+use think\db\exception\ModelNotFoundException;
+use think\facade\Db;
+use library\traits\ModelTrait;
+
+/**
+ * TODO  商品属性Model
+ * Class ProductAttr
+ * @package app\models\system
+ */
+class ProductAttr extends BaseModel
+{
+    /**
+     * 模型名称
+     * @var string
+     */
+    protected $name = 'product_attr';
+
+    use ModelTrait;
+
+    protected function getAttrValuesAttr($value)
+    {
+        return explode(',', $value);
+    }
+
+    public static function storeProductAttrValueDb()
+    {
+        return Db::name('ProductAttrValue');
+    }
+
+    protected function setAttrValuesAttr($value)
+    {
+        return is_array($value) ? implode(',', $value) : $value;
+    }
+
+    /**
+     * 获取商品属性数据
+     * @param $productId
+     * @param int $uid
+     * @param int $type
+     * @param int $type_id
+     * @return array
+     * @throws DataNotFoundException
+     * @throws DbException
+     * @throws ModelNotFoundException
+     */
+    public static function getProductAttrDetail($productId, $uid = 0, $type = 0, $type_id = 0)
+    {
+        $attrDetail = self::where('product_id', $productId)->where('type', $type_id)->order('attr_values asc')->select()->toArray() ?: [];
+        $_values = self::storeProductAttrValueDb()->where('product_id', $productId)->where('type', $type_id)->select();
+        $values = [];
+        foreach ($_values as $value) {
+            if ($type) {
+                if ($uid)
+                    $value['cart_num'] = StoreCart::where('product_attr_unique', $value['unique'])->where('is_pay', 0)->where('is_del', 0)->where('is_new', 0)->where('type', 'product')->where('product_id', $productId)->where('uid', $uid)->value('cart_num');
+                else
+                    $value['cart_num'] = 0;
+                if (is_null($value['cart_num'])) $value['cart_num'] = 0;
+            }
+            $values[$value['suk']] = $value;
+        }
+        foreach ($attrDetail as $k => $v) {
+            $attr = $v['attr_values'];
+//            unset($productAttr[$k]['attr_values']);
+            foreach ($attr as $kk => $vv) {
+                $attrDetail[$k]['attr_value'][$kk]['attr'] = $vv;
+                $attrDetail[$k]['attr_value'][$kk]['check'] = false;
+            }
+        }
+        return [$attrDetail, $values];
+    }
+
+    public static function uniqueByStock($unique)
+    {
+        return self::storeProductAttrValueDb()->where('unique', $unique)->value('stock') ?: 0;
+    }
+
+    public static function uniqueByAttrInfo($unique, $field = '*')
+    {
+        return self::storeProductAttrValueDb()->field($field)->where('unique', $unique)->find();
+    }
+
+    public static function uniqueByAttrInfoMore($unique, $field = '*')
+    {
+        return self::storeProductAttrValueDb()->field($field)->where('unique', 'in', $unique)->select()->toArray() ?: [];
+    }
+
+    public static function issetProductUnique($productId, $unique)
+    {
+        $res = self::where('product_id', $productId)->where('type', 0)->find();
+        if ($unique) {
+            return $res && self::storeProductAttrValueDb()->where('product_id', $productId)->where('unique', $unique)->where('type', 0)->count() > 0;
+        } else {
+            return !$res;
+        }
+    }
+
+    public static function createProductAttr($attrList, $valueList, $productId, $type = 0)
+    {
+        $result = ['attr' => $attrList, 'value' => $valueList];
+        $attrValueList = [];
+        $attrNameList = [];
+        foreach ($attrList as $index => $attr) {
+            if (!isset($attr['value'])) return self::setErrorInfo('请输入规则名称!');
+            $attr['value'] = trim($attr['value']);
+            if (!isset($attr['value'])) return self::setErrorInfo('请输入规则名称!!');
+            if (!isset($attr['detail']) || !count($attr['detail'])) return self::setErrorInfo('请输入属性名称!');
+            foreach ($attr['detail'] as $k => $attrValue) {
+                $attrValue = trim($attrValue);
+                if (empty($attrValue)) return self::setErrorInfo('请输入正确的属性');
+                $attr['detail'][$k] = $attrValue;
+                $attrValueList[] = $attrValue;
+                $attr['detail'][$k] = $attrValue;
+            }
+            $attrNameList[] = $attr['value'];
+            $attrList[$index] = $attr;
+        }
+        $attrCount = count($attrList);
+        foreach ($valueList as $index => $value) {
+            if (!isset($value['detail']) || count($value['detail']) != $attrCount) return self::setErrorInfo('请填写正确的商品信息');
+            if (!isset($value['price']) || !is_numeric($value['price']) || floatval($value['price']) != $value['price'])
+                return self::setErrorInfo('请填写正确的商品价格');
+            if (!isset($value['stock']) || !is_numeric($value['stock']) || intval($value['stock']) != $value['stock'])
+                return self::setErrorInfo('请填写正确的商品库存');
+            if (!isset($value['cost']) || !is_numeric($value['cost']) || floatval($value['cost']) != $value['cost'])
+                return self::setErrorInfo('请填写正确的商品成本价格');
+            if (!isset($value['pic']) || empty($value['pic']))
+                return self::setErrorInfo('请上传商品图片');
+            foreach ($value['detail'] as $attrName => $attrValue) {
+                $attrName = trim($attrName);
+                $attrValue = trim($attrValue);
+                if (!in_array($attrName, $attrNameList, true)) return self::setErrorInfo($attrName . '规则不存在');
+                if (!in_array($attrValue, $attrValueList, true)) return self::setErrorInfo($attrName . '属性不存在');
+                if (empty($attrName)) return self::setErrorInfo('请输入正确的属性');
+                $value['detail'][$attrName] = $attrValue;
+            }
+            $valueList[$index] = $value;
+        }
+        $attrGroup = [];
+        $valueGroup = [];
+        foreach ($attrList as $k => $value) {
+            $attrGroup[] = [
+                'product_id' => $productId,
+                'attr_name' => $value['value'],
+                'attr_values' => $value['detail'],
+                'type' => $type
+            ];
+        }
+        foreach ($valueList as $k => $value) {
+            sort($value['detail'], SORT_STRING);
+            $suk = implode(',', $value['detail']);
+            $valueGroup[$suk] = [
+                'product_id' => $productId,
+                'suk' => $suk,
+                'price' => $value['price'],
+                'cost' => $value['cost'],
+                'ot_price' => $value['ot_price'],
+                'stock' => $value['stock'],
+                'unique' => ProductAttrValue::where(['product_id' => $productId, 'suk' => $suk, 'type' => $type])->value('unique') ?: '',
+                'image' => $value['pic'],
+                'bar_code' => $value['bar_code'] ?? '',
+                'weight' => $value['weight'] ?? 0,
+                'volume' => $value['volume'] ?? 0,
+                'brokerage' => $value['brokerage'] ?? 0,
+                'brokerage_two' => $value['brokerage_two'] ?? 0,
+                'type' => $type,
+                'quota' => $value['quota'] ?? 0,
+                'quota_show' => $value['quota'] ?? 0,
+            ];
+        }
+        if (!count($attrGroup) || !count($valueGroup)) return self::setErrorInfo('请设置至少一个属性!');
+        $attrModel = new self;
+        $attrValueModel = new ProductAttrValue;
+        if (!self::clearProductAttr($productId, $type)) return false;
+        $res = false !== $attrModel->saveAll($attrGroup)
+            && false !== $attrValueModel->saveAll($valueGroup)
+            && false !== ProductAttrResult::setResult($result, $productId, $type);
+        if ($res)
+            return true;
+        else
+            return self::setErrorInfo('编辑商品属性失败!');
+    }
+
+    /**
+     * 获取商品属性
+     * @param $productId
+     * @return array|bool|null|\think\Model
+     * @throws DataNotFoundException
+     * @throws ModelNotFoundException
+     * @throws \think\exception\DbException
+     */
+    public static function getProductAttr($productId, $type = 0)
+    {
+        if (empty($productId) && $productId != 0) return self::setErrorInfo('商品不存在!');
+        $count = self::where('product_id', $productId)->where('type', $type)->count();
+        if (!$count) return self::setErrorInfo('商品不存在!');
+        return self::where('product_id', $productId)->where('type', $type)->select()->toArray();
+    }
+
+    public static function clearProductAttr($productId, $type = 0)
+    {
+        if (empty($productId) && $productId != 0) return self::setErrorInfo('商品不存在!');
+        $res = false !== self::where('product_id', $productId)->where('type', $type)->delete()
+            && false !== ProductAttrValue::clearProductAttrValue($productId, $type);
+        if (!$res)
+            return self::setErrorInfo('编辑属性失败,清除旧属性失败!');
+        else
+            return true;
+    }
+
+}

+ 52 - 0
app/model/system/ProductAttrResult.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace app\model\system;
+
+use library\basic\BaseModel;
+use library\traits\ModelTrait;
+
+class ProductAttrResult extends BaseModel
+{
+
+    /**
+     * 模型名称
+     * @var string
+     */
+    protected $name = 'product_attr_result';
+
+    use ModelTrait;
+
+    protected $insert = ['change_time'];
+
+    protected static function setChangeTimeAttr($value)
+    {
+        return time();
+    }
+
+    protected static function setResultAttr($value)
+    {
+        return is_array($value) ? json_encode($value) : $value;
+    }
+
+    public static function setResult($result, $product_id, $type = 0)
+    {
+        $result = self::setResultAttr($result);
+        $change_time = self::setChangeTimeAttr(0);
+        $count = self::where('product_id', $product_id)->where('type', $type)->count();
+        $res = true;
+        if ($count) $res = self::where('product_id', $product_id)->where('type', $type)->delete();
+        if ($res) return self::insert(compact('product_id', 'result', 'change_time', 'type'), true);
+        return $res;
+    }
+
+    public static function getResult($productId, int $type = 0)
+    {
+        return json_decode(self::where('product_id', $productId)->where('type', $type)->value('result'), true) ?: ['value' => []];
+    }
+
+    public static function clearResult($productId)
+    {
+        return self::where('product_id', $productId)->delete();
+    }
+
+}

+ 225 - 0
app/model/system/ProductAttrValue.php

@@ -0,0 +1,225 @@
+<?php
+
+namespace app\model\system;
+
+use library\basic\BaseModel;
+use library\services\SystemConfigService;
+use library\services\workerman\ChannelService;
+use library\traits\ModelTrait;
+
+class ProductAttrValue extends BaseModel
+{
+    /**
+     * 模型名称
+     * @var string
+     */
+    protected $name = 'product_attr_value';
+
+    use ModelTrait;
+
+    protected $insert = ['unique'];
+
+    protected function setSukAttr($value)
+    {
+        return is_array($value) ? implode(',', $value) : $value;
+    }
+
+    protected function setUniqueAttr($value, $data)
+    {
+
+        if (is_array($data['suk'])) $data['suk'] = $this->setSukAttr($data['suk']);
+        return self::uniqueId($data['product_id'] . $data['suk'] . uniqid(true));
+    }
+
+    /*
+     * 减少销量增加库存
+     * */
+    public static function incProductAttrStock($productId, $unique, $num)
+    {
+        $productAttr = self::where('unique', $unique)->where('product_id', $productId)->field('stock,sales')->find();
+        if (!$productAttr) return true;
+        if ($productAttr->sales > 0) $productAttr->sales = bcsub($productAttr->sales, $num, 0);
+        if ($productAttr->sales < 0) $productAttr->sales = 0;
+        $productAttr->stock = bcadd($productAttr->stock, $num, 0);
+        return $productAttr->save();
+    }
+
+    public static function decProductAttrStock($productId, $unique, $num, $type = 0)
+    {
+        if ($type == 0) {
+            $res = self::where('product_id', $productId)->where('unique', $unique)->where('type', $type)
+                ->dec('stock', $num)->inc('sales', $num)->update();
+        } else {
+            $res = self::where('product_id', $productId)->where('unique', $unique)->where('type', $type)
+                ->dec('stock', $num)->dec('quota', $num)->inc('sales', $num)->update();
+        }
+
+        if ($res) {
+            $stock = self::where('product_id', $productId)->where('unique', $unique)->where('type', $type)->value('stock');
+            $replenishment_num = sys_config('store_stock') ?? 0;//库存预警界限
+            if ($replenishment_num >= $stock) {
+                try {
+                    ChannelService::instance()->send('STORE_STOCK', ['id' => $productId]);
+                } catch (\Exception $e) {
+                }
+            }
+        }
+        return $res;
+    }
+
+    /**
+     * 获取属性参数
+     * @param $productId
+     * @return array|string
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @throws \think\exception\DbException
+     */
+    public static function getStoreProductAttrResult($productId)
+    {
+        $productAttr = ProductAttr::getProductAttr($productId);
+        if (!$productAttr) return [];
+        $attr = [];
+        foreach ($productAttr as $key => $value) {
+            $attr[$key]['value'] = $value['attr_name'];
+            $attr[$key]['detailValue'] = '';
+            $attr[$key]['attrHidden'] = true;
+            $attr[$key]['detail'] = $value['attr_values'];
+        }
+        $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 = self::where('product_id', $productId)->where('suk', $suk)->column('bar_code,cost,price,ot_price,stock,image as pic', 'suk');
+            if (!count($sukValue)) {
+                unset($value[$key]);
+            } else {
+                foreach (array_keys($detail) as $k => $title) {
+                    $header[$k]['title'] = $title;
+                    $header[$k]['align'] = 'center';
+                    $header[$k]['minWidth'] = 130;
+                }
+                foreach (array_values($detail) as $k => $v) {
+                    $valueNew[$count]['value' . ($k + 1)] = $v;
+                    $header[$k]['key'] = 'value' . ($k + 1);
+                }
+                $valueNew[$count]['detail'] = $detail;
+//                $valueNew[$count]['unit_name'] = $product['unit_name'];
+                $valueNew[$count]['pic'] = $sukValue[$suk]['pic'];
+                $valueNew[$count]['price'] = floatval($sukValue[$suk]['price']);
+                $valueNew[$count]['cost'] = floatval($sukValue[$suk]['cost']);
+                $valueNew[$count]['ot_price'] = floatval($sukValue[$suk]['ot_price']);
+                $valueNew[$count]['stock'] = intval($sukValue[$suk]['stock']);
+                $valueNew[$count]['bar_code'] = $sukValue[$suk]['bar_code'];
+//                $valueNew[$count]['check'] = false;
+                $count++;
+            }
+        }
+//        $header[] = ['title'=>'商品单位','key'=>'unit_name'];
+        $header[] = ['title' => '图片', 'slot' => 'pic', 'align' => 'center', 'minWidth' => 80];
+        $header[] = ['title' => '售价', 'slot' => 'price', 'align' => 'center', 'minWidth' => 120];
+        $header[] = ['title' => '成本价', 'slot' => 'cost', 'align' => 'center', 'minWidth' => 140];
+        $header[] = ['title' => '原价', 'slot' => 'ot_price', 'align' => 'center', 'minWidth' => 140];
+        $header[] = ['title' => '库存', 'slot' => 'stock', 'align' => 'center', 'minWidth' => 140];
+        $header[] = ['title' => '商品编号', 'slot' => 'bar_code', 'align' => 'center', 'minWidth' => 140];
+        $header[] = ['title' => '操作', 'slot' => 'action', 'align' => 'center', 'minWidth' => 70];
+        return ['attr' => $attr, 'value' => $valueNew, 'header' => $header];
+    }
+
+    public static function uniqueId($key)
+    {
+        return substr(md5($key), 12, 8);
+    }
+
+    public static function clearProductAttrValue($productId, $type = 0)
+    {
+        return self::where('product_id', $productId)->where('type', $type)->delete();
+    }
+
+    public static function activityRules($productId, $type = 0)
+    {
+        $productAttr = StoreProductAttr::getProductAttr($productId);
+        if (!$productAttr) return [];
+        $attr = [];
+        foreach ($productAttr as $key => $value) {
+            $attr[$key]['value'] = $value['attr_name'];
+            $attr[$key]['detailValue'] = '';
+            $attr[$key]['attrHidden'] = true;
+            $attr[$key]['detail'] = $value['attr_values'];
+        }
+        $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 = self::where('product_id', $productId)->where('suk', $suk)->where('type', 0)->column('bar_code,cost,price,ot_price,stock,image as pic,weight,volume,brokerage,brokerage_two,quota', 'suk');
+            if (!count($sukValue)) {
+                unset($value[$key]);
+            } else {
+                foreach (array_keys($detail) as $k => $title) {
+                    $header[$k]['title'] = $title;
+                    $header[$k]['align'] = 'center';
+                    $header[$k]['minWidth'] = 80;
+                }
+                foreach (array_values($detail) as $k => $v) {
+                    $valueNew[$count]['value' . ($k + 1)] = $v;
+                    $header[$k]['key'] = 'value' . ($k + 1);
+                }
+                $valueNew[$count]['detail'] = $detail;
+                $valueNew[$count]['pic'] = $sukValue[$suk]['pic'];
+                $valueNew[$count]['price'] = floatval($sukValue[$suk]['price']);
+                if ($type == 2) $valueNew[$count]['min_price'] = 0;
+                $valueNew[$count]['cost'] = floatval($sukValue[$suk]['cost']);
+                $valueNew[$count]['ot_price'] = floatval($sukValue[$suk]['ot_price']);
+                $valueNew[$count]['stock'] = intval($sukValue[$suk]['stock']);
+                $valueNew[$count]['quota'] = intval($sukValue[$suk]['quota']);
+                $valueNew[$count]['bar_code'] = $sukValue[$suk]['bar_code'];
+                if ($type == 5) {
+                    $valueNew[$count]['trade_price'] = 0;
+                    $valueNew[$count]['deposit'] = 0;
+                }
+                $valueNew[$count]['weight'] = $sukValue[$suk]['weight'] ? floatval($sukValue[$suk]['weight']) : 0;
+                $valueNew[$count]['volume'] = $sukValue[$suk]['volume'] ? floatval($sukValue[$suk]['volume']) : 0;
+                $valueNew[$count]['brokerage'] = $sukValue[$suk]['brokerage'] ? floatval($sukValue[$suk]['brokerage']) : 0;
+                $valueNew[$count]['brokerage_two'] = $sukValue[$suk]['brokerage_two'] ? floatval($sukValue[$suk]['brokerage_two']) : 0;
+                $count++;
+            }
+        }
+        $header[] = ['title' => '图片', 'slot' => 'pic', 'align' => 'center', 'minWidth' => 120];
+        if ($type == 1) {
+            $header[] = ['title' => '秒杀价', 'key' => 'price', 'type' => 1, 'align' => 'center', 'minWidth' => 80];
+        } elseif ($type == 2) {
+            $header[] = ['title' => '砍价起始金额', 'slot' => 'price', 'align' => 'center', 'minWidth' => 80];
+            $header[] = ['title' => '砍价最低价', 'slot' => 'min_price', 'align' => 'center', 'minWidth' => 80];
+        } elseif ($type == 3) {
+            $header[] = ['title' => '拼团价', 'key' => 'price', 'type' => 1, 'align' => 'center', 'minWidth' => 80];
+        } elseif ($type == 4) {
+            $header[] = ['title' => '助力价', 'key' => 'price', 'type' => 1, 'align' => 'center', 'minWidth' => 80];
+        } else {
+            $header[] = ['title' => '零售价', 'slot' => 'price', 'align' => 'center', 'minWidth' => 80];
+        }
+        if ($type == 5) {
+            $header[] = ['title' => '批发价', 'slot' => 'trade_price', 'align' => 'center', 'minWidth' => 80];
+            $header[] = ['title' => '定金','slot' => 'deposit', 'align' => 'center', 'minWidth' => 80];
+        } else {
+            $header[] = ['title' => '成本价', 'key' => 'cost', 'align' => 'center', 'minWidth' => 80];
+            $header[] = ['title' => '原价', 'key' => 'ot_price', 'align' => 'center', 'minWidth' => 80];
+        }
+        $header[] = ['title' => '库存', 'key' => 'stock', 'align' => 'center', 'minWidth' => 80];
+        if ($type == 2 || $type == 5) {
+            $header[] = ['title' => '限量', 'slot' => 'quota', 'align' => 'center', 'minWidth' => 80];
+        } else {
+            $header[] = ['title' => '限量', 'key' => 'quota', 'type' => 1, 'align' => 'center', 'minWidth' => 80];
+        }
+        $header[] = ['title' => '重量(KG)', 'key' => 'weight', 'align' => 'center', 'minWidth' => 80];
+        $header[] = ['title' => '体积(m³)', 'key' => 'volume', 'align' => 'center', 'minWidth' => 80];
+        $header[] = ['title' => '商品编号', 'key' => 'bar_code', 'align' => 'center', 'minWidth' => 80];
+        return ['attr' => $attr, 'value' => $valueNew, 'header' => $header];
+    }
+
+}

+ 66 - 0
app/model/system/ProductRule.php

@@ -0,0 +1,66 @@
+<?php
+
+namespace app\model\system;
+
+use library\basic\BaseModel;
+use library\traits\ModelTrait;
+
+/**
+ * 商品规则
+ * @mixin think\Model
+ */
+class ProductRule extends BaseModel
+{
+    /**
+     * 模型名称
+     * @var string
+     */
+    protected $name = 'product_rule';
+
+    use ModelTrait;
+
+    /**
+     * 列表
+     * @param $where
+     * @return array
+     */
+    public static function sysPage($where)
+    {
+        $model = new self;
+        if ($where['rule_name']) $model = $model->where('rule_name','LIKE','%'.$where['rule_name'].'%');
+        //if ($where['mer_id']) $model = $model->where('mer_id', $where['mer_id']);
+        $model=$model->order('id desc');
+        $count = $model->count();
+        $list = $model->page((int)$where['page'], (int)$where['limit'])
+            ->select()
+            ->each(function ($item) {
+                if ($item['rule_value']) {
+                    $specs = json_decode($item['rule_value'],true);
+                    if($specs){
+                        foreach ($specs as $key=>$value){
+                            $attr_name[] = $value['value'];
+                            $attr_value[] = implode(',',$value['detail']);
+                        }
+                    }else{
+                        $attr_name[] = '';
+                        $attr_value[] = '';
+                    }
+                    $item['attr_name'] = implode(',',$attr_name);
+                    $item['attr_value'] = $attr_value;
+                }
+            });
+        return compact('count', 'list');
+    }
+
+    /**
+     * 详情
+     * @param $id
+     * @return array
+     */
+    public static function sysInfo($id)
+    {
+        $info = self::get($id);
+        $info['spec'] =json_decode($info['rule_value'],true);
+        return compact('info');
+    }
+}

+ 35 - 43
app/system/controller/v1/Attachment.php

@@ -3,14 +3,9 @@
 namespace app\system\controller\v1;
 
 use app\BaseController;
-use think\facade\Route as Url;
-use library\services\UtilService;
-use app\models\system\{
-    Attachment as AttachmentModel, AttachmentCategory as Category
-};
-use think\Request;
-use library\basic\BaseUpload as Upload;
-use library\utils\Qiniu;
+use library\services\UtilService as Util;
+use app\model\system\Attachment as AttachmentModel;
+use library\services\upload\Upload as Upload;
 
 /**
  * 图片管理类
@@ -27,12 +22,12 @@ class Attachment extends BaseController
      */
     public function index()
     {
-        $where = UtilService::getMore([
+        $where = Util::getMore([
             ['page', 1],
             ['limit', 18],
             ['pid', 0]
         ]);
-        return $this->success(AttachmentModel::getImageList($where));
+        return app('json')->success(AttachmentModel::getImageList($where));
     }
 
     /**
@@ -47,11 +42,11 @@ class Attachment extends BaseController
         $ids = $request->post('ids');
         $ids = explode(',', $ids);
         if (empty($ids))
-            return $this->fail('请选择要删除的图片');
+            return app('json')->fail('请选择要删除的图片');
         foreach ($ids as $v) {
             self::deleteImg($v);
         }
-        return $this->success('删除成功');
+        return app('json')->success('删除成功');
     }
 
     /**
@@ -60,25 +55,32 @@ class Attachment extends BaseController
      */
     public function upload()
     {
-        [$pid, $file] = UtilService::getMore([
+        [$pid, $file] = Util::getMore([
             ['pid', 0],
             ['file', 'file']
         ], $this->request, true);
         try {
             $path = make_path('attach', 2, true);
-            $res = Upload::to($path)->validate()->move($file);
+            $config = [
+                'accessKey' => config('qiniu')['accessKeyId'],
+                'secretKey' => config('qiniu')['accessKeySecret'],
+                'uploadUrl' => 'imgs.boofly.cn',
+                'storageName' => config('qiniu')['bucket'],
+                'storageRegion' => 's3-cn-south-1.qiniucs.com',
+            ];
+            $upload = new Upload(2,$config);
+            $res = $upload->to($path)->validate()->move($file);
             if ($res === false) {
-                return $this->fail(Upload::getError());
+                return app('json')->fail($upload->getError());
             } else {
-                $img_url = Qiniu::updateFile('img', $path, $path);
-                $fileInfo = Upload::getUploadInfo();
+                $fileInfo = $upload->getUploadInfo();
                 if ($fileInfo) {
                     AttachmentModel::attachmentAdd($fileInfo['name'], $fileInfo['size'], $fileInfo['type'], $fileInfo['dir'], $fileInfo['thumb_path'], $pid, 2, $fileInfo['time'], 1, 0);
                 }
-                return $this->success('上传成功', ['src' => $res->filePath]);
+                return app('json')->success('上传成功', ['src' => $res->filePath]);
             }
         } catch (\Exception $e) {
-            return $this->fail($e->getMessage());
+            return app('json')->fail($e->getMessage());
         }
     }
 
@@ -90,31 +92,21 @@ class Attachment extends BaseController
         $attinfo = AttachmentModel::get($att_id);
         if ($attinfo) {
             try {
-                Upload::delete($attinfo['name']);
+                $config = [
+                    'accessKey' => config('qiniu')['accessKeyId'],
+                    'secretKey' => config('qiniu')['accessKeySecret'],
+                    'uploadUrl' => 'imgs.boofly.cn',
+                    'storageName' => config('qiniu')['bucket'],
+                    'storageRegion' => 's3-cn-south-1.qiniucs.com',
+                ];
+                $upload = new Upload(2,$config);
+                $upload->delete($attinfo['name']);
             } catch (\Throwable $e) {
             }
             AttachmentModel::where('att_id', $att_id)->delete();
         }
     }
 
-    /**
-     * 移动图片分类显示
-     */
-    public function move($images)
-    {
-        $formbuider = [];
-        $formbuider[] = Form::hidden('images', $images);
-        $formbuider[] = Form::select('pid', '选择分类')->setOptions(function () {
-            $list = Category::getCateList();
-            $options = [['value' => 0, 'label' => '所有分类']];
-            foreach ($list as $id => $cateName) {
-                $options[] = ['label' => $cateName['html'] . $cateName['name'], 'value' => $cateName['id']];
-            }
-            return $options;
-        })->filterable(1);
-        return $this->makePostForm('编辑分类', $formbuider, Url::buildUrl('/file/do_move'), 'PUT');
-    }
-
     /**
      * 移动图片分类操作
      */
@@ -124,13 +116,13 @@ class Attachment extends BaseController
             'pid',
             'images'
         ]);
-        if ($data['images'] == '') return $this->fail('请选择图片');
-        if (!$data['pid']) return $this->fail('请选择分类');
-        $res = SystemAttachmentModel::where('att_id', 'in', $data['images'])->update(['pid' => $data['pid']]);
+        if ($data['images'] == '') return app('json')->fail('请选择图片');
+        if (!$data['pid']) return app('json')->fail('请选择分类');
+        $res = AttachmentModel::where('att_id', 'in', $data['images'])->update(['pid' => $data['pid']]);
         if ($res)
-            return $this->success('移动成功');
+            return app('json')->success('移动成功');
         else
-            return $this->fail('移动失败!');
+            return app('json')->fail('移动失败!');
     }
 
 }

+ 29 - 79
app/system/controller/v1/AttachmentCategory.php

@@ -4,10 +4,9 @@ namespace app\system\controller\v1;
 
 use app\BaseController;
 use think\Request;
-use think\facade\Route as Url;
-use library\services\{UtilService as Util,FormBuilder as Form};
-use app\models\system\{Attachment as AttachmentModel,AttachmentCategory as Category};
-
+use library\services\UtilService as Util;
+use app\model\system\AttachmentCategory as Category;
+use app\model\system\Attachment as AttachmentModel;
 /**
  * 图片分类管理类
  * Class AttachmentCategory
@@ -22,94 +21,45 @@ class AttachmentCategory extends BaseController
      */
     public function index($name='')
     {
-        return $this->success(Category::getAll($name));
+        return app('json')->success(Category::getAll($name));
     }
 
-    /**
-     * 显示创建资源表单页.
-     *
-     * @return \think\Response
-     */
-    public function create()
-    {
-        $id=$this->request->param('id');
-        $id=$id ?? 0;
-        $formbuider = [];
-        $formbuider[] = Form::select('pid','上级分类',(string)$id)->setOptions(function () {
-            $list = Category::getCateList(0);
-            $options =  [['value'=>0,'label'=>'所有分类']];
-            foreach ($list as $id=>$cateName){
-                $options[] = ['label'=>$cateName['html'].$cateName['name'],'value'=>$cateName['id']];
-            }
-            return $options;
-        })->filterable(1);
-        $formbuider[] = Form::input('name','分类名称');
-        return $this->makePostForm('添加分类', $formbuider, Url::buildUrl('/file/category'), 'POST');
-    }
 
     /**
-     * 保存新建的资源
+     * 保存资源
      *
      * @param  \think\Request  $request
      * @return \think\Response
      */
     public function save(Request $request)
     {
-        $request = app('request');
-        $post = $request->post();
-        $data['pid'] = $post['pid'];
-        $data['name'] = $post['name'];
-        $data['mer_id'] = $this->merId;
-        if(empty($post['name'] ))
-            return $this->fail('分类名称不能为空!');
-        $res = Category::create($data);
-        if($res)
-            return $this->success('添加成功');
-        else
-            return $this->fail('添加失败!');
-    }
-
-    /**
-     * 显示编辑资源表单页.
-     *
-     * @param  int  $id
-     * @return \think\Response
-     */
-    public function edit($id)
-    {
-        $Category = Category::get($id);
-        if(!$Category) return $this->fail('数据不存在!');
-        $formbuider = [];
-        $formbuider[] = Form::hidden('id',$id);
-        $formbuider[] = Form::select('pid','上级分类',(string)$Category->getData('pid'))->setOptions(function ()use($id){
-            $list = Category::getCateList();
-            $options =  [['value'=>0,'label'=>'所有分类']];
-            foreach ($list as $id=>$cateName){
-                $options[] = ['label'=>$cateName['html'].$cateName['name'],'value'=>$cateName['id']];
-            }
-            return $options;
-        })->filterable(1);
-        $formbuider[] = Form::input('name','分类名称',$Category->getData('name'));
-        return $this->makePostForm('编辑分类', $formbuider, Url::buildUrl('/file/category/'.$id), 'PUT');
+        $data = Util::getMore([
+            ['id',0],
+            ['pid','0'],
+            ['name','','empty','分类名称不能为空!']
+        ]);
+        if(empty($data['id'])){
+            $res = Category::create($data);
+            if($res)
+                return app('json')->success("添加成功");
+            else
+                return app('json')->fail('添加失败!');
+        }else{
+            $res = Category::update($data);
+            if($res)
+                return app('json')->success("编辑成功");
+            else
+                return app('json')->fail('编辑失败!');
+        }
     }
 
-    /**
-     * 保存更新的资源
-     *
-     * @param  \think\Request  $request
-     * @param  int  $id
-     * @return \think\Response
-     */
-    public function update(Request $request, $id)
+    public function info(Request $request)
     {
         $data = Util::postMore([
-            'pid',
-            'name'
+            'id'
         ]);
-        if($data['pid'] == '') return $this->fail('请选择父类');
-        if(!$data['name']) return $this->fail('请输入分类名称');
-        Category::edit($data,$id);
-        return $this->success('分类编辑成功!');
+        $info = Category::getinfo($data['id']);
+        return app('json')->success($info);
     }
 
     /**
@@ -125,8 +75,8 @@ class AttachmentCategory extends BaseController
         $chdcount = AttachmentModel::where('pid',$id)->count();
         if($chdcount) return $this->fail('栏目内有图片不能删除');
         if(Category::del($id))
-            return $this->success('删除成功!');
+            return app('json')->success('删除成功!');
         else
-            return $this->fail('删除失败');
+            return app('json')->fail('删除失败');
     }
 }

+ 338 - 145
app/system/controller/v1/Product.php

@@ -1,182 +1,375 @@
 <?php
-declare (strict_types = 1);
+
 namespace app\system\controller\v1;
 
 use app\BaseController;
+use app\model\system\ProductRule;
+use app\model\system\ProductAttr;
+use app\model\system\ProductAttrValue;
+use app\model\system\ProductAttrResult;
 use app\model\system\Product as ProductModel;
-use app\model\system\SiteProduct;
-use app\Request;
+use app\model\system\Category;
 use library\services\UtilService;
 
-// +----------------------------------------------------------------------
-// | [ WE CAN DO IT MORE SIMPLE  ]
-// +----------------------------------------------------------------------
-// | Copyright (c) 2018-2020 rights reserved.
-// +----------------------------------------------------------------------
-// | Author: TABLE ME
-// +----------------------------------------------------------------------
-// | Date: 2020-09-06 21:53
-// +----------------------------------------------------------------------
+class Product extends BaseController
+{/**
+ * 显示资源列表头部
+ *
+ * @return \think\Response
+ */
+    public function type_header()
+    {
+        //出售中商品
+        $onsale = ProductModel::where('is_del', 0)->where('is_show', 1)->count();
+        //仓库中商品
+        $forsale = ProductModel::where('is_del', 0)->where('is_show', 0)->count();
+        //已经售馨产品
+        $outofstock = (new ProductModel)->getWhere(['type' => 4])->count();
+        //警戒库存
+        $policeforce = (new ProductModel)->getWhere(['type' => 5])->count();
+        //回收站
+        $recycle = ProductModel::where('is_del', 1)->count();
+        $list = [
+            ['type' => 1, 'name' => '出售中产品', 'count' => $onsale],
+            ['type' => 2, 'name' => '仓库中产品', 'count' => $forsale],
+            ['type' => 4, 'name' => '已经售馨产品', 'count' => $outofstock],
+            ['type' => 5, 'name' => '警戒库存', 'count' => $policeforce],
+            ['type' => 6, 'name' => '产品回收站', 'count' => $recycle],
+        ];
+        return app('json')->success(compact('list'));
+    }
 
-class Product extends  BaseController
-{
     /**
-     * 基本设置
+     * 显示资源列表
+     * @return mixed
      */
-    public function list(Request $request) {
-        $pageSize = 20;
-        $post =  UtilService::getMore([
-            ['page',1],
-            ['name',''],
-            ['artType','all']
-        ],$request);
-        list($pageCount,$data) = (new ProductModel)->getList($post['page'],$post,$pageSize,'id desc');
-        $result = UtilService::getParam(["id","price","title","commission","status","sales","count","img","add_time","wget","seq","warehouseAr",'cate_name'],$data);
-
-        $totalCount['count'] = (new ProductModel)->count();
-        $totalCount['downCount'] = (new ProductModel)->where('status',0)->count();
-        $totalCount['upCount'] = (new ProductModel)->where('status',1)->count();
-
-        return app('json')->success([
-            'list'      => $result,
-            'pageCount' => $pageCount,
-            'pageSize'  => $pageSize,
-            'page'      => $post['page'],
-            'totalCount'=>$totalCount
+    public function index()
+    {
+        $where = UtilService::getMore([
+            ['page', 1],
+            ['limit', 20],
+            ['store_name', ''],
+            ['cate_id', ''],
+            ['excel', 0],
+            ['type', 1]
         ]);
+//        if($this->merId){
+//            $where['mer_id'] = $this->merId;
+//        }
+        return app('json')->success(ProductModel::ProductList($where));
     }
 
     /**
-     * 保存数据
-     * @param Request $request
+     * 获取规则属性模板
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
      */
-    public function save(Request $request) {
-       $post =  UtilService::getMore([
-            ['cate_id','','empty','请选择商品分类'],
-            ['count','0'],
-            ['desc',''],
-            ['id','0'],
-            ['imgAr',[]],
-            ['is_host',0],
-            ['csno',''],
-            ['price','0'],
-            ['commission','0'],
-            ['is_new',0],
-            ['status',''],
-            ['title',''],
-            ['unit_name',''],
-            ['ver_bug_count',0],
-            ['wget',0],
-            ['warehouseAr',[]],
-           ['seq',0]
-        ],$request);
-       $post['img'] = join(',',$post['imgAr']);
-       $post['warehouse_ids'] = join(',',$post['warehouseAr']);
-        unset($post['imgAr']);
-        unset($post['warehouseAr']);
-        (new ProductModel())->saveProduct($post);
-        return app('json')->success("数据保存成功");
+    public function get_rule()
+    {
+        return app('json')->success(ProductRule::field(['rule_name', 'rule_value'])->select()->each(function ($item) {
+            $item['rule_value'] = json_decode($item['rule_value'], true);
+        })->toArray());
     }
 
-
     /**
-     * 获取产品基本信息
-     * @param Request $request
+     * 生成属性
+     * @param int $id
      */
-    public function info(Request $request) {
-        [$id] = UtilService::getMore([
-            ['id','','empty','参数错误']
-        ],$request,true);
-        $info = ProductModel::get(compact('id'))->toArray();
-        return app('json')->success($info);
+    public function is_format_attr($id)
+    {
+        $data = UtilService::getMore([
+            ['attrs', []],
+            ['items', []]
+        ]);
+        $attr = $data['attrs'];
+        $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']);
+            if ($id) {
+                $sukValue = ProductAttrValue::where('product_id', $id)->where('type', 0)->where('suk', $suk)->column('bar_code,cost,price,ot_price,stock,image as pic,weight,volume,brokerage,brokerage_two', 'suk');
+                if (!count($sukValue)) {
+                    $sukValue[$suk]['pic'] = '';
+                    $sukValue[$suk]['price'] = 0;
+                    $sukValue[$suk]['cost'] = 0;
+                    $sukValue[$suk]['ot_price'] = 0;
+                    $sukValue[$suk]['stock'] = 0;
+                    $sukValue[$suk]['bar_code'] = '';
+                    $sukValue[$suk]['weight'] = 0;
+                    $sukValue[$suk]['volume'] = 0;
+                    $sukValue[$suk]['brokerage'] = 0;
+                    $sukValue[$suk]['brokerage_two'] = 0;
+                }
+            } else {
+                $sukValue[$suk]['pic'] = '';
+                $sukValue[$suk]['price'] = 0;
+                $sukValue[$suk]['cost'] = 0;
+                $sukValue[$suk]['ot_price'] = 0;
+                $sukValue[$suk]['stock'] = 0;
+                $sukValue[$suk]['bar_code'] = '';
+                $sukValue[$suk]['weight'] = 0;
+                $sukValue[$suk]['volume'] = 0;
+                $sukValue[$suk]['brokerage'] = 0;
+                $sukValue[$suk]['brokerage_two'] = 0;
+            }
+            foreach (array_keys($detail) as $k => $title) {
+                $header[$k]['title'] = $title;
+                $header[$k]['align'] = 'center';
+                $header[$k]['minWidth'] = 120;
+            }
+            foreach (array_values($detail) as $k => $v) {
+                $valueNew[$count]['value' . ($k + 1)] = $v;
+                $header[$k]['key'] = 'value' . ($k + 1);
+            }
+            $valueNew[$count]['detail'] = $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]['bar_code'] = $sukValue[$suk]['bar_code'] ?? '';
+            $valueNew[$count]['weight'] = $sukValue[$suk]['weight'] ? floatval($sukValue[$suk]['weight']) : 0;
+            $valueNew[$count]['volume'] = $sukValue[$suk]['volume'] ? floatval($sukValue[$suk]['volume']) : 0;
+            $valueNew[$count]['brokerage'] = $sukValue[$suk]['brokerage'] ? floatval($sukValue[$suk]['brokerage']) : 0;
+            $valueNew[$count]['brokerage_two'] = $sukValue[$suk]['brokerage_two'] ? floatval($sukValue[$suk]['brokerage_two']) : 0;
+            $count++;
+        }
+        $header[] = ['title' => '图片', 'slot' => 'pic', 'align' => 'center', 'minWidth' => 80];
+        $header[] = ['title' => '售价', 'slot' => 'price', 'align' => 'center', 'minWidth' => 95];
+        $header[] = ['title' => '成本价', 'slot' => 'cost', 'align' => 'center', 'minWidth' => 95];
+        $header[] = ['title' => '原价', 'slot' => 'ot_price', 'align' => 'center', 'minWidth' => 95];
+        $header[] = ['title' => '库存', 'slot' => 'stock', 'align' => 'center', 'minWidth' => 95];
+        $header[] = ['title' => '商品编号', 'slot' => 'bar_code', 'align' => 'center', 'minWidth' => 120];
+        $header[] = ['title' => '重量(KG)', 'slot' => 'weight', 'align' => 'center', 'minWidth' => 95];
+        $header[] = ['title' => '体积(m³)', 'slot' => 'volume', 'align' => 'center', 'minWidth' => 95];
+        $header[] = ['title' => '操作', 'slot' => 'action', 'align' => 'center', 'minWidth' => 70];
+        $info = ['attr' => $attr, 'value' => $valueNew, 'header' => $header];
+        return app('json')->success(compact('info'));
     }
 
-
-
     /**
-     * 栏目删除
-     * @param Request $request
+     * 获取商品详细信息
+     * @param int $id
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
      */
-    public function del(Request $request) {
-        [$id] = UtilService::getMore([
-            ['id',0,'empty','参数错误']
-        ],$request,true);
-        $bool =   (new ProductModel())->delProduct($id);
-        return app('json')->success("栏目删除成功");
+    public function get_product_info($id = 0)
+    {
+        //$mer_id = $this->merId ?: '';
+        $list = Category::getTierList(null, 1);
+        $menus = [];
+        foreach ($list as $menu) {
+            $menus[] = ['value' => $menu['id'], 'label' => $menu['html'] . $menu['cate_name'], 'disabled' => $menu['pid'] == 0 ? 0 : 1];//'disabled'=>$menu['pid']== 0];
+        }
+        $data['cateList'] = $menus;
+        $data['productInfo'] = [];
+        if ($id) {
+            $productInfo = ProductModel::get($id);
+            if (!$productInfo) {
+                return app('json')->fail('修改的商品不存在');
+            }
+            $productInfo['postage'] = floatval($productInfo['postage']);
+            $productInfo['slider_image'] = is_string($productInfo['slider_image']) ? json_decode($productInfo['slider_image'], true) : [];
+            if ($productInfo['spec_type'] == 1) {
+                $result = ProductAttrResult::getResult($id);
+                foreach ($result['value'] as $k => $v) {
+                    $num = 1;
+                    foreach ($v['detail'] as $dv) {
+                        $result['value'][$k]['value' . $num] = $dv;
+                        $num++;
+                    }
+                }
+                $productInfo['items'] = $result['attr'];
+                $productInfo['attrs'] = $result['value'];
+                $productInfo['attr'] = ['pic' => '', 'price' => 0, 'cost' => 0, 'ot_price' => 0, 'stock' => 0, 'bar_code' => '', 'weight' => 0, 'volume' => 0, 'brokerage' => 0, 'brokerage_two' => 0];
+            } else {
+                $result = ProductAttrValue::where('product_id', $id)->where('type', 0)->find();
+                $productInfo['items'] = [];
+                $productInfo['attrs'] = [];
+                $productInfo['attr'] = [
+                    'pic' => $result['image'] ?? '',
+                    'price' => $result['price'] ? floatval($result['price']) : 0,
+                    'cost' => $result['cost'] ? floatval($result['cost']) : 0,
+                    'ot_price' => $result['ot_price'] ? floatval($result['ot_price']) : 0,
+                    'stock' => $result['stock'] ? floatval($result['stock']) : 0,
+                    'bar_code' => $result['bar_code'] ?? '',
+                    'weight' => $result['weight'] ? floatval($result['weight']) : 0,
+                    'volume' => $result['volume'] ? floatval($result['volume']) : 0,
+                    'brokerage' => $result['brokerage'] ? floatval($result['brokerage']) : 0,
+                    'brokerage_two' => $result['brokerage_two'] ? floatval($result['brokerage_two']) : 0
+                ];
+            }
+            $data['productInfo'] = $productInfo;
+        }
+        return app('json')->success($data);
     }
 
     /**
-     * 站点产品
-     * @param Request $request
+     * 保存新建或编辑
+     * @param $id
+     * @return mixed
+     * @throws \Exception
      */
-    public function site(Request $request) {
-        $pageSize = 50;
-        [$sassid,$page,$type] = UtilService::getMore([
-            ['sassid',0,'empty','参数错误'],
-            ['page',1],
-            ['type','']
-        ],$request,true);
-        $where = [];
-        if(!empty($type)) {
-            $where['type'] = $type;
-        }
-        $sitePro = new SiteProduct();
-        $sitePro->setSassid($sassid);
-        list($pageCount,$data) = $sitePro->getList($page,$where,$pageSize,'id desc');
-        $result = UtilService::getParam(["id","price","title","sales","count","img","commission",
-            ['is_host','is_host',function($var){
-                return $var ? true : false;
-            }],
-            ['is_new','is_new',function($var){
-                return $var ? true : false;
-            }],
-            ['status','status',function($var){
-                return $var ? true : false;
-            }],
-            "ver_bug_count"],$data);
-        return app('json')->success([
-            'list'      => $result,
-            'pageCount' => $pageCount,
-            'pageSize'  => $pageSize,
-            'page'      =>  $page
+    public function save($id)
+    {
+        $data = UtilService::getMore([
+            'store_name',
+            'cate_id',
+            'keyword',
+            'store_info',
+            ['unit_name', '件'],
+            ['postage', 0],
+            ['image', []],
+            ['slider_image', []],
+            ['video_link', ''],
+            ['spec_type', 0],
+            ['ficti', 100],
+            ['sales', 0],
+            ['sort', 0],
+            ['is_show', 0],
+            ['is_best', 0],
+            ['is_sub', 0],
+            ['description', ''],
+            ['items', []],
+            ['attrs', []]
         ]);
+        //$data['mer_id'] = $this->merId ?: '';
+        $detail = $data['attrs'];
+        $data['price'] = min(array_column($detail, 'price'));
+        $data['ot_price'] = min(array_column($detail, 'ot_price'));
+        $data['cost'] = min(array_column($detail, 'cost'));
+        $attr = $data['items'];
+        unset($data['items'], $data['video'], $data['attrs']);
+        if (!$data['cate_id']) return app('json')->fail('请选择商品分类');
+        if (!$data['store_name']) return app('json')->fail('请输入商品名称');
+        if (empty($data['image'])) return app('json')->fail('请上传商品图片');
+        if (count($data['slider_image']) < 1) return app('json')->fail('请上传商品轮播图');
+        $data['slider_image'] = json_encode($data['slider_image']);
+        $data['stock'] = array_sum(array_column($detail, 'stock'));
+        ProductModel::beginTrans();
+        foreach ($detail as &$item) {
+            if ($item['brokerage'] > $item['price']) {
+                return app('json')->fail('佣金不能大于商品售价');
+            }
+        }
+        if ($id) {
+            unset($data['sales']);
+            ProductModel::where('id',$id)->save($data);
+            if ($data['spec_type'] == 0) {
+                $attr = [
+                    [
+                        'value' => '规格',
+                        'detailValue' => '',
+                        'attrHidden' => '',
+                        'detail' => ['默认']
+                    ]
+                ];
+                $detail[0]['value1'] = '规格';
+                $detail[0]['detail'] = ['规格' => '默认'];
+            }
+            $attr_res = ProductAttr::createProductAttr($attr, $detail, $id);
+            if ($attr_res) {
+                ProductModel::commitTrans();
+                return app('json')->success('修改成功!');
+            } else {
+                ProductModel::rollbackTrans();
+                return app('json')->fail(ProductAttr::getErrorInfo());
+            }
+        } else {
+            $data['add_time'] = time();
+            $data['code_path'] = '';
+            $res = ProductModel::create($data);
+            if ($data['spec_type'] == 0) {
+                $attr = [
+                    [
+                        'value' => '规格',
+                        'detailValue' => '',
+                        'attrHidden' => '',
+                        'detail' => ['默认']
+                    ]
+                ];
+                $detail[0]['value1'] = '规格';
+                $detail[0]['detail'] = ['规格' => '默认'];
+            }
+            $attr_res = ProductAttr::createProductAttr($attr, $detail, $res['id']);
+            if ($attr_res) {
+                ProductModel::commitTrans();
+                return app('json')->success('添加商品成功!');
+            } else {
+                ProductModel::rollbackTrans();
+                return app('json')->fail(ProductAttr::getErrorInfo());
+            }
+        }
     }
 
     /**
-     * 站点产品配置
-     * @param Request $request
+     * 删除指定资源
+     *
+     * @param int $id
+     * @return \think\Response
      */
-    public function siteSave(Request $request) {
-       $post = UtilService::getMore([
-            ['id','','empty','参数错误'],
-            ['price','','empty','参数错误'],
-            ['ver_bug_count',''],
-            ['is_new',false],
-            ['is_host',false],
-           ['status',false],
-        ],$request);
-       $sitePro = (new SiteProduct)->where('id',$post['id'])->find();
-        if(empty($sitePro)) {
-            return app('json')->fail('找不到产品数据');
-        }
-        $pro = ProductModel::where('id',$sitePro['p_id'])->find();
-        if(empty($pro)) {
-            return app('json')->fail('找不到产品数据');
+    public function delete($id)
+    {
+        if (!$id) return app('json')->fail('数据不存在');
+        if (!ProductModel::be(['id' => $id])) return app('json')->fail('商品数据不存在');
+        if (ProductModel::be(['id' => $id, 'is_del' => 1])) {
+            $data['is_del'] = 0;
+            if (!ProductModel::where('id',$id)->save($data))
+                return app('json')->fail(ProductModel::getErrorInfo('恢复失败,请稍候再试!'));
+            else
+                return app('json')->success('成功恢复商品!');
+        } else {
+            $data['is_del'] = 1;
+            $data['is_show'] = 0;
+            if (!ProductModel::where('id',$id)->save($data))
+                return app('json')->fail(ProductModel::getErrorInfo('删除失败,请稍候再试!'));
+            else
+                return app('json')->success('成功移到回收站!');
         }
-        if($pro['commission'] > $post['price']) {
-            return app('json')->fail('价格不能低于,产品售价!');
+    }
+
+    /**
+     * 修改状态
+     * @param string $is_show
+     * @param string $id
+     * @return mixed
+     */
+    public function set_show($is_show = '', $id = '')
+    {
+        ($is_show == '' || $id == '') && app('json')->fail('缺少参数');
+        if (ProductModel::be(['id' => $id, 'is_del' => 1])) return app('json')->fail('商品已删除,不能上架');
+        $res = ProductModel::where(['id' => $id])->update(['is_show' => (int)$is_show]);
+        if ($res) {
+            return app('json')->success($is_show == 1 ? '上架成功' : '下架成功');
+        } else {
+            return app('json')->fail($is_show == 1 ? '上架失败' : '下架失败');
         }
+    }
 
-        (new SiteProduct)
-            ->where('id',$post['id'])
-            ->save([
-                'price'         =>  $post['price'],
-                'ver_bug_count' =>  $post['ver_bug_count'],
-                'is_new'        =>  $post['is_new'] ? 1 : 0,
-                'is_host'       =>  $post['is_host'] ? 1 : 0,
-                'status'        =>  $post['status'] ? 1 : 0,
-            ]);
-        return app('json')->success('设置成功');
+    /**
+     * 设置批量商品上架
+     * @return mixed
+     */
+    public function product_show()
+    {
+        $post = UtilService::getMore([
+            ['ids', []]
+        ]);
+        if (empty($post['ids'])) {
+            return app('json')->fail('请选择需要上架的商品');
+        } else {
+            $res = ProductModel::where('id', 'in', $post['ids'])->update(['is_show' => 1]);
+            if ($res !== false)
+                return app('json')->success('上架成功');
+            else
+                return app('json')->fail('上架失败');
+        }
     }
 
-}
+
+}

+ 95 - 0
app/system/controller/v1/ProductRule.php

@@ -0,0 +1,95 @@
+<?php
+
+namespace app\system\controller\v1;
+
+use app\BaseController;
+use app\model\system\ProductRule as ProductRuleModel;
+use library\services\UtilService;
+use think\Request;
+
+/**
+ * 规则管理
+ * Class ProductRule
+ * @package app\system\controller\v1
+ */
+class ProductRule extends BaseController
+{
+    /**
+     * 显示资源列表
+     *
+     * @return \think\Response
+     */
+    public function index()
+    {
+        $where = UtilService::getMore([
+            ['page', 1],
+            ['limit', 15],
+            ['rule_name','']
+        ]);
+//        if($this->merId){
+//            $where['mer_id'] = $this->merId;
+//        }
+        $list = ProductRuleModel::sysPage($where);
+        return app('json')->success($list);
+    }
+
+    /**
+     * 保存新建的资源
+     *
+     * @param \think\Request $request
+     * @return \think\Response
+     */
+    public function save(Request $request,$id)
+    {
+        $data = UtilService::getMore([
+            ['rule_name',''],
+            ['spec',[]]
+        ]);
+        //$data['mer_id'] = $this->merId ?: '';
+        if ($data['rule_name'] == '') return app('json')->fail('请输入规则名称');
+        if (!$data['spec']) return app('json')->fail('缺少规则值');
+        $data['rule_value'] = json_encode($data['spec']);
+        unset($data['spec']);
+        if ($id){
+            $rule = ProductRuleModel::get($id);
+            if (!$rule) return app('json')->fail('数据不存在');
+            ProductRuleModel::edit($data, $id);
+            return app('json')->success('编辑成功!');
+        }else{
+            ProductRuleModel::create($data);
+            return app('json')->success('规则添加成功!');
+        }
+    }
+
+    /**
+     * 显示指定的资源
+     *
+     * @param int $id
+     * @return \think\Response
+     */
+    public function read($id)
+    {
+        $info = ProductRuleModel::sysInfo($id);
+        return app('json')->success($info);
+    }
+
+    /**
+     * 删除指定资源
+     *
+     * @param int $id
+     * @return \think\Response
+     */
+    public function delete()
+    {
+        $data = UtilService::getMore([
+            ['ids','']
+        ]);
+        if ($data['ids']=='') return app('json')->fail('请至少选择一条数据');
+        $ids = strval($data['ids']);
+        $res = ProductRuleModel::whereIn('id',$ids)->delete();
+        if (!$res)
+            return app('json')->fail(ProductRuleModel::getErrorInfo('删除失败,请稍候再试!'));
+        else
+            return app('json')->success('删除成功!');
+    }
+}

+ 10 - 7
app/system/route/file.php

@@ -6,21 +6,24 @@ use think\facade\Route;
  * 附件相关路由
  */
 Route::group('file', function () {
+    //分类列表
+    Route::get('category', 'v1.AttachmentCategory/index');
+    //添加编辑分类
+    Route::post('category/create', 'v1.AttachmentCategory/save');
+    //分类详情
+    Route::post('category/info', 'v1.AttachmentCategory/info');
+    //删除分类
+    Route::rule('category/:id', 'v1.AttachmentCategory/delete');
     //附件列表
     Route::get('file', 'v1.Attachment/index');
     //删除图片和数据记录
     Route::post('file/delete', 'v1.Attachment/delete');
-    //移动图片分来表单
-    Route::get('file/move', 'v1.Attachment/move');
     //移动图片分类
-    Route::put('file/do_move', 'v1.Attachment/moveImageCate');
+    Route::rule('file/do_move', 'v1.Attachment/moveImageCate');
     //上传图片
-    Route::post('upload/[:upload_type]', 'v1.Attachment/upload');
-    //附件分类管理资源路由
-    Route::resource('category', 'v1.AttachmentCategory');
+    Route::rule('upload/[:upload_type]', 'v1.Attachment/upload');
 
 })->middleware([
     \app\system\middleware\AllowOriginMiddleware::class,
     \app\system\middleware\AdminAuthTokenMiddleware::class
-    // \app\adminapi\middleware\AdminCkeckRole::class
 ]);

+ 27 - 23
app/system/route/product.php

@@ -1,32 +1,36 @@
 <?php
-// +----------------------------------------------------------------------
-// | [ WE CAN DO IT MORE SIMPLE  ]
-// +----------------------------------------------------------------------
-// | Copyright (c) 2018-2020 rights reserved.
-// +----------------------------------------------------------------------
-// | Author: TABLE ME
-// +----------------------------------------------------------------------
-// | Date: 2020-09-06 21:52
-// +----------------------------------------------------------------------
+
 use think\facade\Route;
 
 Route::group('product', function () {
-    //获取商品列表
-    Route::rule('list', 'v1.product/list');
-    //商品删除数据
-    Route::post('del','v1.product/del');
-
-    Route::post('save','v1.product/save');
+    //规则列表
+    Route::get('rule', 'v1.ProductRule/index');
+    //规则 保存新建或编辑
+    Route::rule('rule/:id', 'v1.ProductRule/save');
+    //规则详情
+    Route::rule('ruleInfo/:id', 'v1.ProductRule/read');
+    //删除属性规则
+    Route::rule('rule/delete', 'v1.ProductRule/delete');
+    //获取规则属性模板
+    Route::rule('get_rule', 'v1.Product/get_rule');
+    //生成属性
+    Route::rule('generate_attr/:id', 'v1.Product/is_format_attr');
+    //商品列表头
+    Route::get('type_header', 'v1.Product/type_header');
+    //商品列表
+    Route::rule('list', 'v1.Product/index');
     //商品详情
-    Route::post('info','v1.product/info');
-    //站点图片
-    Route::post('site','v1.product/site');
-    //站点产品
-    Route::post('siteSave','v1.product/siteSave');
-
+    Route::rule('productInfo/:id', 'v1.Product/get_product_info');
+    //保存商品
+    Route::rule('product/:id', 'v1.Product/save');
+    //加入回收站
+    Route::rule('del/:id', 'v1.Product/delete');
+    //修改商品状态
+    Route::rule('set_show/:id/:is_show', 'v1.Product/set_show');
+    //设置批量商品上架
+    Route::rule('product_show', 'v1.Product/product_show');
 
 })->middleware([
     \app\system\middleware\AllowOriginMiddleware::class,
     \app\system\middleware\AdminAuthTokenMiddleware::class
-    // \app\adminapi\middleware\AdminCkeckRole::class
-]);
+]);

+ 3 - 3
config/qiniu.php

@@ -11,7 +11,7 @@
 
 return ['accessKeyId'       => 'K-P9_cqutGZOchFKMMaNPStTQLnTP-xer97Qe2ug',
         'accessKeySecret'   => 'R_WUyYNp7K1CI8w5yqqVnQ0pOAQfE_E7cQ4nkZI2',
-        'endpoint'          => 'https://imgs.lipinwawa.com/',
-        'bucket'            => 'lipmama',
+        'endpoint'          => 'https://imgs.boofly.cn/',
+        'bucket'            => 'live-product',
         'bingW'             => true //如果绑定域名请设置Ture,如果没有请设置flase
-];
+];

+ 22 - 0
config/upload.php

@@ -0,0 +1,22 @@
+<?php
+// +----------------------------------------------------------------------
+// | 上传配置
+// +----------------------------------------------------------------------
+
+return [
+    //默认上传模式
+    'default' => 'local',
+    //上传文件大小
+    'filesize' => 2097152,
+    //上传文件后缀类型
+    'fileExt' => ['jpg', 'jpeg', 'png', 'gif', 'pem', 'mp3', 'wma', 'wav', 'amr', 'mp4', 'key'],
+    //上传文件类型
+    'fileMime' => ['image/jpeg', 'image/gif', 'image/png', 'text/plain', 'audio/mpeg'],
+    //驱动模式
+    'stores' => [
+        //本地上传配置
+        'local' => [],
+        //七牛云上传配置
+        'qiniu' => [],
+    ]
+];

+ 209 - 0
library/basic/BaseManager.php

@@ -0,0 +1,209 @@
+<?php
+
+namespace library\basic;
+
+use think\facade\Config;
+use think\helper\Str;
+use think\Container;
+
+/**
+ * Class BaseManager
+ * @package crmeb\basic
+ */
+abstract class BaseManager
+{
+    /**
+     * 驱动的命名空间
+     * @var null
+     */
+    protected $namespace = null;
+
+    /**
+     * 配置
+     * @var null
+     */
+    protected $configFile = null;
+
+    /**
+     * 配置
+     * @var array
+     */
+    protected $config = [];
+
+    /**
+     * 驱动
+     * @var array
+     */
+    protected $drivers = [];
+
+    /**
+     * 驱动类型
+     * @var null
+     */
+    protected $name = null;
+
+    /**
+     * BaseManager constructor.
+     * @param string|array|int $name驱动名称
+     * @param array $config 配置
+     */
+    public function __construct($name = null, array $config = [])
+    {
+        $type = null;
+        if (is_array($name)) {
+            $config = $name;
+            $name = null;
+        }
+
+        if (is_int($name)) {
+            $type = $name;
+            $name = null;
+        }
+
+        if ($name)
+            $this->name = $name;
+        if ($type && is_null($this->name)) {
+            $this->setHandleType((int)$type - 1);
+        }
+        $this->config = $config;
+    }
+
+    /**
+     * 提取配置文件名
+     * @return $this
+     */
+    protected function getConfigFile()
+    {
+        if (is_null($this->configFile)) {
+            $this->configFile = strtolower((new \ReflectionClass($this))->getShortName());
+        }
+        return $this;
+    }
+
+    /**
+     * 设置文件句柄
+     * @param int $type
+     */
+    protected function setHandleType(int $type)
+    {
+        $this->getConfigFile();
+        $stores = array_keys(Config::get($this->configFile . '.stores', []));
+        $name = $stores[$type] ?? null;
+        if (!$name) {
+            throw new \RuntimeException($this->configFile . ' type is not used');
+        }
+        $this->name = $name;
+    }
+
+    /**
+     * 设置默认句柄
+     * @return mixed
+     */
+    abstract protected function getDefaultDriver();
+
+    /**
+     * 动态调用
+     * @param $method
+     * @param $arguments
+     */
+    public function __call($method, $arguments)
+    {
+        return $this->driver()->{$method}(...$arguments);
+    }
+
+    /**
+     * 获取驱动实例
+     * @param null|string $name
+     * @return mixed
+     */
+    protected function driver(string $name = null)
+    {
+        $name = $name ?: $this->name;
+        $name = $name ?: $this->getDefaultDriver();
+
+        if (is_null($name)) {
+            throw new \InvalidArgumentException(sprintf(
+                'Unable to resolve NULL driver for [%s].', static::class
+            ));
+        }
+
+        return $this->drivers[$name] = $this->getDriver($name);
+    }
+
+    /**
+     * 获取驱动实例
+     * @param string $name
+     * @return mixed
+     */
+    protected function getDriver(string $name)
+    {
+        return $this->drivers[$name] ?? $this->createDriver($name);
+    }
+
+    /**
+     * 获取驱动类型
+     * @param string $name
+     * @return mixed
+     */
+    protected function resolveType(string $name)
+    {
+        return $name;
+    }
+
+    /**
+     * 创建驱动
+     *
+     * @param string $name
+     * @return mixed
+     *
+     */
+    protected function createDriver(string $name)
+    {
+        $type = $this->resolveType($name);
+
+        $method = 'create' . Str::studly($type) . 'Driver';
+
+        if (method_exists($this, $method)) {
+            return $this->$method($name);
+        }
+
+        $class = $this->resolveClass($type);
+        $this->name = $type;
+        return $this->invokeClass($class);
+    }
+
+
+    /**
+     * 获取驱动类
+     * @param string $type
+     * @return string
+     */
+    protected function resolveClass(string $type): string
+    {
+        if ($this->namespace || false !== strpos($type, '\\')) {
+            $class = false !== strpos($type, '\\') ? $type : $this->namespace . Str::studly($type);
+            if (class_exists($class)) {
+                return $class;
+            }
+        }
+
+        throw new \InvalidArgumentException("Driver [$type] not supported.");
+    }
+
+    /**
+     * 实例化类
+     * @param $class
+     * @return mixed
+     */
+    protected function invokeClass($class)
+    {
+        if (!class_exists($class)) {
+            throw new \RuntimeException('class not exists: ' . $class);
+        }
+        $this->getConfigFile();
+        $handle = Container::getInstance()->invokeClass($class, [$this->name, $this->config, $this->configFile]);
+        $this->config = [];
+        return $handle;
+    }
+
+}

+ 25 - 0
library/exceptions/UploadException.php

@@ -0,0 +1,25 @@
+<?php
+
+
+namespace library\exceptions;
+
+
+use Throwable;
+
+/**
+ * Class AuthException
+ * @package library\exceptions
+ */
+class UploadException extends \RuntimeException
+{
+    public function __construct($message = "", $code = 0, Throwable $previous = null)
+    {
+        if (is_array($message)) {
+            $errInfo = $message;
+            $message = $errInfo[1] ?? '未知错误';
+            $code = $errInfo[0] ?? 400;
+        }
+
+        parent::__construct($message, $code, $previous);
+    }
+}

+ 0 - 16
library/services/FormBuilder.php

@@ -1,16 +0,0 @@
-<?php
-/**
- *
- * @author: xaboy<365615158@qq.com>
- * @day: 2017/11/23
- */
-
-namespace crmeb\services;
-
-use FormBuilder\Form;
-
-class FormBuilder extends Form
-{
-
-
-}

+ 34 - 0
library/services/upload/Upload.php

@@ -0,0 +1,34 @@
+<?php
+
+namespace library\services\upload;
+
+use library\basic\BaseManager;
+use think\facade\Config;
+
+/**
+ * Class Upload
+ * @package library\services\upload
+ * @mixin \library\services\upload\storage\Local
+ * @mixin \library\services\upload\storage\OSS
+ * @mixin \library\services\upload\storage\COS
+ * @mixin \library\services\upload\storage\Qiniu
+ */
+class Upload extends BaseManager
+{
+    /**
+     * 空间名
+     * @var string
+     */
+    protected $namespace = '\\library\\services\\upload\\storage\\';
+
+    /**
+     * 设置默认上传类型
+     * @return mixed
+     */
+    protected function getDefaultDriver()
+    {
+        return Config::get('upload.default', 'qiniu');
+    }
+
+
+}

+ 132 - 0
library/services/upload/storage/Local.php

@@ -0,0 +1,132 @@
+<?php
+
+namespace library\services\upload\storage;
+
+use library\basic\BaseUpload;
+use library\exceptions\UploadException;
+use think\exception\ValidateException;
+use think\facade\Config;
+use think\facade\Filesystem;
+use think\File;
+
+/**
+ * 本地上传
+ * Class Local
+ * @package library\services\upload\storage
+ */
+class Local extends BaseUpload
+{
+
+    /**
+     * 默认存放路径
+     * @var string
+     */
+    protected $defaultPath;
+
+    public function initialize(array $config)
+    {
+        parent::initialize($config);
+        $this->defaultPath = Config::get('filesystem.disks.' . Config::get('filesystem.default') . '.url');
+    }
+
+    protected function app()
+    {
+        // TODO: Implement app() method.
+    }
+
+    public function getTempKeys()
+    {
+        // TODO: Implement getTempKeys() method.
+    }
+
+    /**
+     * 生成上传文件目录
+     * @param $path
+     * @param null $root
+     * @return string
+     */
+    protected function uploadDir($path, $root = null)
+    {
+        if ($root === null) $root = app()->getRootPath() . 'public' . DS;
+        return str_replace('\\', '/', $root . 'uploads' . DS . $path);
+    }
+
+    /**
+     * 检查上传目录不存在则生成
+     * @param $dir
+     * @return bool
+     */
+    protected function validDir($dir)
+    {
+        return is_dir($dir) == true || mkdir($dir, 0777, true) == true;
+    }
+
+    /**
+     * 文件上传
+     * @param string $file
+     * @return array|bool|mixed|\StdClass
+     */
+    public function move(string $file = 'file')
+    {
+        $fileHandle = app()->request->file($file);
+        if (!$fileHandle) {
+            return $this->setError('Upload file does not exist');
+        }
+        if ($this->validate) {
+            try {
+                validate([$file => $this->validate])->check([$file => $fileHandle]);
+            } catch (ValidateException $e) {
+                return $this->setError($e->getMessage());
+            }
+        }
+        $fileName = Filesystem::putFile($this->path, $fileHandle);
+        if (!$fileName)
+            return $this->setError('Upload failure');
+        $filePath = Filesystem::path($fileName);
+        $this->fileInfo->uploadInfo = new File($filePath);
+        $this->fileInfo->fileName = $this->fileInfo->uploadInfo->getFilename();
+        $this->fileInfo->filePath = $this->defaultPath . '/' . str_replace('\\', '/', $fileName);
+        return $this->fileInfo;
+    }
+
+    /**
+     * 文件流上传
+     * @param string $fileContent
+     * @param string|null $key
+     * @return array|bool|mixed|\StdClass
+     */
+    public function stream(string $fileContent, string $key = null)
+    {
+        if (!$key) {
+            $key = $this->saveFileName();
+        }
+        $dir = $this->uploadDir($this->path);
+        if (!$this->validDir($dir)) {
+            return $this->setError('Failed to generate upload directory, please check the permission!');
+        }
+        $fileName = $dir . '/' . $key;
+        file_put_contents($fileName, $fileContent);
+        $this->fileInfo->uploadInfo = new File($fileName);
+        $this->fileInfo->fileName = $key;
+        $this->fileInfo->filePath = $this->defaultPath . '/' . $this->path . '/' . $key;
+        return $this->fileInfo;
+    }
+
+    /**
+     * 删除文件
+     * @param string $filePath
+     * @return bool|mixed
+     */
+    public function delete(string $filePath)
+    {
+        if (file_exists($filePath)) {
+            try {
+                unlink($filePath);
+                return true;
+            } catch (UploadException $e) {
+                return $this->setError($e->getMessage());
+            }
+        }
+        return false;
+    }
+}

+ 183 - 0
library/services/upload/storage/Qiniu.php

@@ -0,0 +1,183 @@
+<?php
+
+namespace library\services\upload\storage;
+
+use library\basic\BaseUpload;
+use library\exceptions\UploadException;
+use Qiniu\Auth;
+use function Qiniu\base64_urlSafeEncode;
+use Qiniu\Storage\BucketManager;
+use Qiniu\Storage\UploadManager;
+use Qiniu\Config;
+use think\exception\ValidateException;
+
+
+/**
+ * TODO 七牛云上传
+ * Class Qiniu
+ */
+class Qiniu extends BaseUpload
+{
+    /**
+     * accessKey
+     * @var mixed
+     */
+    protected $accessKey;
+
+    /**
+     * secretKey
+     * @var mixed
+     */
+    protected $secretKey;
+
+    /**
+     * 句柄
+     * @var object
+     */
+    protected $handle;
+
+    /**
+     * 空间域名 Domain
+     * @var mixed
+     */
+    protected $uploadUrl;
+
+    /**
+     * 存储空间名称  公开空间
+     * @var mixed
+     */
+    protected $storageName;
+
+    /**
+     * COS使用  所属地域
+     * @var mixed|null
+     */
+    protected $storageRegion;
+
+    /**
+     * 初始化
+     * @param array $config
+     * @return mixed|void
+     */
+    public function initialize(array $config)
+    {
+        parent::initialize($config);
+        $this->accessKey = $config['accessKey'] ?? null;
+        $this->secretKey = $config['secretKey'] ?? null;
+        $this->uploadUrl = $this->checkUploadUrl($config['uploadUrl'] ?? '');
+        $this->storageName = $config['storageName'] ?? null;
+        $this->storageRegion = $config['storageRegion'] ?? null;
+    }
+
+    /**
+     * 实例化七牛云
+     * @return object|Auth
+     */
+    protected function app()
+    {
+        if (!$this->accessKey || !$this->secretKey) {
+            throw new UploadException('Please configure accessKey and secretKey');
+        }
+        $this->handle = new Auth($this->accessKey, $this->secretKey);
+        return $this->handle;
+    }
+
+    /**
+     * 上传文件
+     * @param string $file
+     * @return array|bool|mixed|\StdClass|string
+     */
+    public function move(string $file = 'file')
+    {
+        $fileHandle = app()->request->file($file);
+        if (!$fileHandle) {
+            return $this->setError('Upload file does not exist');
+        }
+        if ($this->validate) {
+            try {
+                validate([$file => $this->validate])->check([$file => $fileHandle]);
+            } catch (ValidateException $e) {
+                return $this->setError($e->getMessage());
+            }
+        }
+        $key = $this->saveFileName($fileHandle->getRealPath(), $fileHandle->getOriginalExtension());
+        $token = $this->app()->uploadToken($this->storageName);
+        try {
+            $uploadMgr = new UploadManager();
+            [$result, $error] = $uploadMgr->putFile($token, $key, $fileHandle->getRealPath());
+            if ($error !== null) {
+                return $this->setError($error->message());
+            }
+            $this->fileInfo->uploadInfo = $result;
+            $this->fileInfo->filePath = $this->uploadUrl . '/' . $key;
+            $this->fileInfo->fileName = $key;
+            return $this->fileInfo;
+        } catch (UploadException $e) {
+            return $this->setError($e->getMessage());
+        }
+    }
+
+    /**
+     * 文件流上传
+     * @param string $fileContent
+     * @param string|null $key
+     * @return array|bool|mixed|\StdClass
+     */
+    public function stream(string $fileContent, string $key = null)
+    {
+        $token = $this->app()->uploadToken($this->storageName);
+        if (!$key) {
+            $key = $this->saveFileName();
+        }
+        try {
+            $uploadMgr = new UploadManager();
+            [$result, $error] = $uploadMgr->put($token, $key, $fileContent);
+            if ($error !== null) {
+                return $this->setError($error->message());
+            }
+            $this->fileInfo->uploadInfo = $result;
+            $this->fileInfo->filePath = $this->uploadUrl . '/' . $key;
+            $this->fileInfo->fileName = $key;
+            return $this->fileInfo;
+        } catch (UploadException $e) {
+            return $this->setError($e->getMessage());
+        }
+    }
+
+    /**
+     * 获取上传配置信息
+     * @return array
+     */
+    public function getSystem()
+    {
+        $token = $this->app()->uploadToken($this->storageName);
+        $domain = $this->uploadUrl;
+        $key = $this->saveFileName();
+        return compact('token', 'domain', 'key');
+    }
+
+    /**
+     * TODO 删除资源
+     * @param $key
+     * @param $bucket
+     * @return mixed
+     */
+    public function delete(string $key)
+    {
+        $bucketManager = new BucketManager($this->app(), new Config());
+        return $bucketManager->delete($this->storageName, $key);
+    }
+
+    /**
+     * 获取七牛云上传密钥
+     * @return mixed|string
+     */
+    public function getTempKeys()
+    {
+        $token = $this->app()->uploadToken($this->storageName);
+        $domain = $this->uploadUrl;
+        $key = $this->saveFileName(NULL, 'mp4');
+        $type = 'QINIU';
+        return compact('token', 'domain', 'key', 'type');
+    }
+}

+ 23 - 1
library/traits/ModelTrait.php

@@ -156,4 +156,26 @@ trait ModelTrait
         return $model == null ? (new self()) : $model;
     }
 
-}
+    /**
+     * 修改一条数据
+     * @param $data
+     * @param $id
+     * @param $field
+     * @return bool $type 返回成功失败
+     */
+    public static function edit($data, $id, $field = null)
+    {
+        $model = new self;
+        if (!$field) $field = $model->getPk();
+//        return false !== $model->update($data,[$field=>$id]);
+//        return 0 < $model->update($data,[$field=>$id])->result;
+        $res = $model->update($data, [$field => $id]);
+        if (isset($res->result))
+            return 0 < $res->result;
+        else if (isset($res['data']['result']))
+            return 0 < $res['data']['result'];
+        else
+            return false !== $res;
+    }
+
+}