cqpCow 1 年之前
父节点
当前提交
5fc20f34e5

+ 22 - 0
app/Http/Controllers/Api/ImportController.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace App\Http\Controllers\Api;
+
+use App\Service\ImportService;
+use Illuminate\Http\Request;
+
+class ImportController extends BaseController
+{
+    public function import(Request $request)
+    {
+        $service = new ImportService();
+        $userData = $request->userData->toArray();
+        list($status,$data) = $service->import($request->all(),$userData);
+
+        if($status){
+            return $this->json_return(200,'',$data);
+        }else{
+            return $this->json_return(201,$data);
+        }
+    }
+}

+ 331 - 0
app/Import/Import.php

@@ -0,0 +1,331 @@
+<?php
+
+
+namespace  App\Import;
+
+use App\Service\ImportService;
+use Illuminate\Support\Facades\DB;
+use Maatwebsite\Excel\Concerns\ToArray;
+use Maatwebsite\Excel\Concerns\WithMultipleSheets;
+use Maatwebsite\Excel\Facades\Excel;
+
+
+class Import implements WithMultipleSheets,ToArray {
+    private $msg = '';
+    public $crt_id = 0;
+    public $config = [];
+    public $mergedCells = [];
+    public $data = [];
+
+    public function sheets(): array
+    {
+        return [
+            0 => $this, // 指定要导入的 sheet 索引,这里假设是第一个 sheet
+        ];
+    }
+
+    public function array (array $array){
+        $this->handleData($array);
+    }
+
+    public function setConfig($config,$mergedCells,$data){
+        $this->config = $config;
+        $this->mergedCells = $mergedCells;
+        $this->data = $data;
+    }
+
+    public function getMsg(){
+        return $this->msg;
+    }
+
+    public function setMsg($msg){
+        $this->msg = $msg;
+    }
+
+    public function handleData (array $array) {
+        //表头处理
+        list($status,$msg) = $this->clearHead($array);
+        if(! $status) {
+            $this->setMsg($msg);
+            return ;
+        }
+
+        //表头
+        $table_head = $msg;
+
+        // 去除表头
+        unset($array[0]);
+        if(empty($array)) {
+            $this->setMsg('导入数据不能为空!');
+            return ;
+        }
+
+        //数据处理
+        list($status,$msg) = $this->checkRule($table_head,$array);
+        if(! $status) {
+            $this->setMsg($msg);
+            return ;
+        }
+
+        //写入数据
+        $time = time();
+        list($status,$msg) = $this->insertAllData($msg, $time);
+        if(! $status) {
+            $this->setMsg($msg);
+            return ;
+        }
+
+        //(特殊 需要额外写入的数据) 做对写入数据的更新
+        if(! empty($this->config['other_field_func'])){
+            $func = $this->config['other_field_func'] ?? "";
+            if(empty($func)) return;
+            (new ImportService)->$func($time);
+        }
+    }
+
+    public function clearHead($array){
+        $table_head = array_filter($array[0]);
+        if(empty($table_head)) return [false, '表头不能为空!'];
+
+        foreach ($table_head as $key => $value){
+            $head_tmp = trim($value);
+            if(empty($head_tmp)) {
+                unset($table_head[$key]);
+            }else{
+                $table_head[$key] = $head_tmp;
+            }
+        }
+        if(empty($table_head)) return [false, '表头不能为空!'];
+
+        return [true, $table_head];
+    }
+
+    public function insertAllData($data, $time){
+        $model_array = $this->config['table'];
+
+        //执行写入
+        try{
+            DB::beginTransaction();
+
+            foreach ($model_array as $model_name => $model_field){
+                if(! empty($model_field['need_param'])) {
+                    $model = "\App\Model\\" . $model_field['db_name'];
+                    $last_insert_id = $model::where('crt_time',$time)
+                        ->select($model_field['db_key'])
+                        ->get()->toArray();
+                    $last_insert_id = array_column($last_insert_id,$model_field['db_key']);
+                }
+                $tmp = [];$insert = [];
+                if(! empty($model_field['field'])){
+                    foreach ($model_field['field'] as $m){
+                        $tmp[$m] = "";
+                        if(strstr($m,'time')) $tmp[$m] = $time;
+                        if($m == "depart_id") $tmp[$m] = $this->data['depart_id'] ?? 0;
+                        if($m == "top_depart_id") $tmp[$m] = $this->data['top_depart_id'] ?? 0;
+                        if($m == "crt_id") $tmp[$m] = $this->data['user_id'] ?? 0;
+                    }
+                    foreach ($data as $value){
+                        $insert[] = array_intersect_key(array_merge($tmp, $value),$tmp);
+                    }
+                }elseif (! empty($model_field['field_array'])){
+                    foreach ($model_field['field_array'] as $km => $m){
+                        foreach ($m as $v){
+                            $tmp[$v] = "";
+                            if(strstr($v,'time')) $tmp[$v] = $time;
+                        }
+                        foreach ($data as $key => $value){
+                            foreach ($value[$km] as $vv){
+                                $vv[$model_field['need_param']] = $last_insert_id[$key];
+                                $insert[] = array_intersect_key(array_merge($tmp, $vv),$tmp);
+                            }
+                        }
+                    }
+                }
+
+                $model = "\App\Model\\" . $model_name;
+                $model::insert($insert);
+            }
+
+            DB::commit();
+        }catch (\Exception $e){
+            DB::rollBack();
+            return [false, $e->getMessage().$e->getLine()];
+        }
+
+        return [true, ''];
+    }
+
+    public function checkRule($head, $data){
+        //模板里的表头
+        foreach ($this->config['field'] as $key => $value){
+            if(! in_array($key, $head)) return [false, "缺少表头:" . $key];
+        }
+
+        //处理 =》 存在合并单元格数据
+        if(! empty($this->mergedCells)) $data = $this->clearMergeData($data);
+
+        //数据校验
+        list($status,$msg) = $this->checkData($head,$data);
+        return [$status,$msg];
+    }
+
+    function checkData($head,$data){
+        $return_data = [];
+
+        //数据处理
+        foreach ($this->config['field'] as $key => $value){
+            //找到表头在xlsx中的下标位置
+            $key2 = array_search($key, $head);
+            if ($key2 === false) return [false, '未找到表头在xlsx中的位置!'];
+            //获取表头对应列数据
+            $tmp_data = array_column($data,$key2);
+            //去除数据中两侧的空白字符或其他预定义字符
+            foreach ($tmp_data as $v_key => $v_data){
+                $tmp_data[$v_key] = trim($v_data);
+            }
+
+            //------ 根据规则 查找数据 =>做 替换和校验--------------//
+            //校验数据唯一
+            if (strpos($value['other_rule'], 'unique') !== false) {
+                $uniqueIndex = strpos($value['other_rule'], 'unique:');
+                $start = $uniqueIndex + strlen('unique:');
+                $end = strpos($value['other_rule'], '|', $start);
+                if ($end !== false) {
+                    $uniqueValue = substr($value['other_rule'], $start, $end - $start);
+                } else {
+                    $uniqueValue = substr($value['other_rule'], $start);
+                }
+                $model = "\App\Model\\" . $uniqueValue;
+                $bool = $model::whereIn($value['key'], array_unique($tmp_data))
+                    ->where('del_time',0)
+                    ->exists();
+                if($bool) return [false, $key . "在数据库中已存在"];
+            }
+
+            //替换数据
+            if(! empty($value['db_search'])){
+                $db = $value['db_search'];
+                $model = "\App\Model\\" . $db['db_name'];
+                $map = $model::whereIn($db['key'], array_unique($tmp_data))
+                    ->pluck($db['value'], $db['key'])
+                    ->toArray();
+            }
+            //------ 根据规则 查找数据 =>做 替换和校验--------------//
+
+
+            //校验&&组织
+            $tmp_clean = [];
+            foreach ($tmp_data as $v_key => $v_data){
+                if (strpos($value['other_rule'], 'require') !== false) {
+                    if(empty($v_data)) return [false, $key . '不能为空!'];
+                }
+                if (strpos($value['other_rule'], 'unique') !== false) {
+                    if(in_array($v_data,$tmp_clean)) return [false, $key . '不能重复!'];
+                    $tmp_clean[] = $v_data;
+                }
+                if (strpos($value['other_rule'], 'is_numeric') !== false && ! empty($v_data)) {
+                    if(! is_numeric($v_data))  return [false, $key . '请输入数字!'];
+                    $formattedNumber = number_format($v_data, 2, '.', '');
+                    if($formattedNumber != $v_data) return [false, $key . '请输入数字(请输入不超过两位小数)!'];
+                }
+                if(! empty($value['db_search'])) {
+                    if(empty($map[$v_data])) return [false, $key . '下所输入的内容在系统中不存在!'];
+                    $v_data = $map[$v_data];
+                }
+
+                if(! empty($value['multiple'])) {
+                    $field = $value['map'][$key] ?? "";
+                    $value['key_array'][$field] = $v_data;
+
+                    //子表数据 一对多
+                    $return_data[$v_key][$value['key']][] = $value['key_array'];
+                }else{
+                    //数据
+                    $return_data[$v_key][$value['key']] = $v_data;
+                }
+            }
+        }
+        unset($tmp_clean);
+
+        return [true, $return_data];
+    }
+
+    // 处理合并单元格数据
+    function clearMergeData($data){
+        //获取行列
+        $rows = count($data);
+        $cols = count($data[1]);
+
+        //非合并单元格并且为null的数据 替换成空字符串
+        for ($row = 1; $row < $rows; $row++) {
+            // 遍历每一列
+            for ($col = 0; $col < $cols; $col++) {
+                // 如果当前值为 null,并且不是合并单元格
+                if ($data[$row][$col] === null && ! $this->isMergedCell($this->mergedCells, $row, $col)) {
+                    $data[$row][$col] = '';
+                } else {
+                    $data[$row][$col] = $data[$row][$col];
+                }
+            }
+        }
+
+        //合并单元格数据替换为原来的数据
+        for ($row = 1; $row <= $rows; $row++) {
+            // 遍历每一列
+            for ($col = 0; $col < $cols; $col++) {
+                // 如果当前值为 null
+                if ($data[$row][$col] === null) {
+                    // 查找左方第一个非 null 值
+                    for ($i = $col - 1; $i >= 0; $i--) {
+                        if ($data[$row][$i] !== null) {
+                            $data[$row][$col] = $data[$row][$i];
+                            break;
+                        }
+                    }
+                    // 如果左方没有非 null 值,则查找上方第一个非 null 值
+                    if (!isset($newArray[$row][$col])) {
+                        for ($i = $row - 1; $i >= 1; $i--) {
+                            if ($data[$i][$col] !== null) {
+                                $data[$row][$col] = $data[$i][$col];
+                                break;
+                            }
+                        }
+                    }
+                } else {
+                    $data[$row][$col] = $data[$row][$col];
+                }
+            }
+        }
+
+        return $data;
+    }
+
+    // 判断一个单元格是否是合并单元格
+    function isMergedCell($mergeCells, $row, $col) {
+        foreach ($mergeCells as $mergeCell) {
+            [$start, $end] = explode(':', $mergeCell);
+            [$startCol, $startRow] = $this->coordinateToIndex($start);
+            [$endCol, $endRow] = $this->coordinateToIndex($end);
+
+            if ($col >= $startCol && $col <= $endCol && $row >= $startRow && $row <= $endRow) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    // 将 Excel 单元格坐标转换为索引
+    function coordinateToIndex($coordinate) {
+        preg_match('/([A-Z]+)(\d+)/', $coordinate, $matches);
+        [, $col, $row] = $matches;
+
+        $colIndex = 0;
+        $length = strlen($col);
+        for ($i = 0; $i < $length; $i++) {
+            $colIndex += (ord($col[$i]) - 65 + 1) * pow(26, $length - $i - 1);
+        }
+
+        return [$colIndex - 1, $row - 1];
+    }
+}

+ 118 - 0
app/Service/ImportService.php

@@ -0,0 +1,118 @@
+<?php
+
+namespace App\Service;
+
+use App\Import\Import;
+use App\Model\BasicType;
+use App\Model\Product;
+use App\Model\ProductCategory;
+use PhpOffice\PhpSpreadsheet\IOFactory;
+
+class ImportService extends Service
+{
+    public static $type = [
+        'product', //产品
+    ];
+
+    //导入入口
+    public function import($data,$user){
+        if(empty($data['type'])) return [false,'缺少导入类型,导入失败'];
+        if(! in_array($data['type'],self::$type)) return [false,'导入类型不存在,导入失败'];
+        if(empty($data['file'])) return [false,'导入文件不能为空'];
+
+        //导入的数据并且校验写入
+        list($status,$msg) = $this->importMain($data,$user);
+        if(! $status) return [false, $msg];
+
+        return [true, ''];
+    }
+
+    //主方法
+    public function importMain($data,$user){
+        //获取配置文件
+        $config = "excel." . $data['type'];
+        $config_array = config($config) ?? [];
+        if(empty($config_array)) return [false, '配置文件不存在'];
+
+        //(特殊 额外的表头数据)
+        $config_array = $this->getTableTitle($config_array,$user,$data);
+
+        //获取合并单元格范围
+        $uploadedFile = $_FILES['file']['tmp_name']; // 获取上传文件的临时路径
+        $spreadsheet = IOFactory::load($uploadedFile); // 加载上传的 Excel 文件
+        $worksheet = $spreadsheet->getActiveSheet(); // 获取第一个工作表
+        $mergedCells = $worksheet->getMergeCells(); // 获取单元格合并范围
+
+        // 需要导入的公用数据
+        $msg['user_id'] = $user['id'];
+        $msg['depart_id'] = $this->getDepart($user);
+        $msg['top_depart_id'] = $user['depart_map'][$msg['depart_id']] ?? 0;
+
+        //导入
+        $import = new Import();
+        $import->setConfig($config_array, $mergedCells,$msg);
+        \Maatwebsite\Excel\Facades\Excel::import($import,$data['file']);
+
+        //异常提示报错
+        if($import->getMsg()) return [false, $import->getMsg()];
+
+        return [true, ''];
+    }
+
+    //表头入口
+    public function getTableTitle($config_array,$user,$data){
+        if(! empty($config_array['dynamics_field'])){
+            $func = $config_array['dynamics_field']['func'];
+            return $this->$func($config_array,$user,$data);
+        }
+
+        return $config_array;
+    }
+
+    //产品导入的额外表头
+    public function productTitle($config_array,$user,$data){
+        $model = BasicType::TopClear($user,$data);
+        $result = $model->whereRaw('type = 22 And del_time = 0')->get()->toArray();
+        if(! empty($result)){
+            foreach ($result as $value){
+                $config_array['field'][$value['title']] = [
+                    "key" => $config_array['dynamics_field']['name'],
+                    "key_array" => [
+                        "basic_type_id" => $value['id'],
+                        "price" => 0,
+                    ],
+                    "rule" => "",
+                    "other_rule" => "is_numeric",
+                    "multiple" => true,
+                    "map" => [
+                        $value['title'] => "price",
+                    ],
+                ];
+            }
+        }
+
+        return $config_array;
+    }
+
+    //产品导入的额外数据
+    public function fillInsertProductData($time){
+        $last_insert_data = Product::where('crt_time',$time)
+            ->where('del_time',0)
+            ->select("id","product_category_id")
+            ->get()->toArray();
+        if(empty($last_insert_data)) return;
+
+        $list = ProductCategory::where('del_time',0)
+            ->select('id','parent_id')
+            ->get()->toArray();
+
+        foreach ($last_insert_data as $value){
+            $parentsId = $this->findParentIds($value['product_category_id'], $list);
+            array_unshift($parentsId, $value['product_category_id']);
+            $result = array_reverse($parentsId);
+            Product::where('id',$value['id'])->update([
+                'product_category' => json_encode($result)
+            ]);
+        }
+    }
+}

+ 1 - 1
app/Service/Wx/WxEmployeeService.php

@@ -111,7 +111,7 @@ class WxEmployeeService extends Service
 
     public function checkWxUser($userId){
         $res = Employee::where('id', $userId)
-            ->where('del_time',0)
+//            ->where('del_time',0)
             ->where('state',Employee::USE)->get()->first();
         if(empty($res)) return [false, '该账号无法登录,请联系管理员!'];
 

+ 80 - 0
config/excel/product.php

@@ -0,0 +1,80 @@
+<?php
+return [
+    "field" => [
+        '产品名称' => [
+            'key' =>'title',
+            'rule' =>'',
+            'other_rule' => 'require|unique:Product',
+        ],
+        '产品分类' => [
+            'key' =>'product_category_id',
+            'db_search' => [
+                'db_name' => 'ProductCategory',
+                'key' => 'title',
+                'value' => 'id',
+            ],
+            'other_rule' => 'require',
+        ],
+        '产品编码' => [
+            'key' =>'code',
+            'rule' => '',
+            'other_rule' => 'require|unique:Product',
+        ],
+        '产品SN编码' => [
+            'key' =>'sn_code',
+            'rule' => '',
+            'other_rule' => 'require|unique:Product',
+        ],
+        '规格' => [
+            'key' =>'size',
+            'rule' =>'',
+            'other_rule' => '',
+        ],
+        '单位' => [
+            'key' =>'unit',
+            'rule' =>'',
+            'other_rule' => '',
+            'db_search' => [
+                'db_name' => 'BasicType',
+                'key' => 'title',
+                'value' => 'id',
+            ],
+        ],
+        '条码' => [
+            'key' =>'bar_code',
+            'rule' =>'',
+            'other_rule' => '',
+        ],
+        '成本' => [
+            'key' =>'cost',
+            'rule' =>'',
+            'other_rule' => 'require|is_numeric',
+        ],
+        '零售价' => [
+            'key' =>'retail_price',
+            'rule' =>'',
+            'other_rule' => 'is_numeric',
+        ],
+    ],
+    //(特殊) 额外表头字段数组名 因为一般这个数据理论上是无限的  所以放在子表里 结构是数组
+    "dynamics_field" => [
+        "name" => "basic_type_22",
+        "func" => "productTitle",
+    ],
+    "other_field_func" => "fillInsertProductData",// (特殊)需要填充的其它字段 这里是为了前端组件渲染的数据
+    "table" => [
+        "Product" => [
+            "field" => ["title","product_category_id","code","size","unit","bar_code","cost","retail_price","crt_id","mark","sn_code","crt_time","depart_id","top_depart_id"],
+        ],
+        "ProductPriceDetail" => [
+            "field_array" => [
+                "basic_type_22" => [
+                    "product_id","basic_type_id","price","crt_time"
+                ],
+            ],
+            "need_param" => "product_id",
+            "db_name" => "Product",
+            "db_key" => "id",
+        ],
+    ],
+];

+ 2 - 0
routes/api.php

@@ -236,4 +236,6 @@ Route::group(['middleware'=> ['checkLogin']],function ($route){
     //移交 分配
     $route->any('fpMan','Api\DeleteController@fp');
     $route->any('yjMan','Api\DeleteController@yj');
+    //导入
+    $route->any('import','Api\ImportController@import');
 });

+ 24 - 1
routes/wx.php

@@ -31,11 +31,34 @@ Route::group(['middleware'=> ['checkWx']],function ($route){
     $route->any('purchaseOrderAdd', 'Api\PurchaseOrderController@purchaseOrderAdd');
     $route->any('purchaseOrderDel', 'Api\PurchaseOrderController@purchaseOrderDel');
 
+    //合同
+    $route->any('salesOrderGet', 'Api\SalesOrderController@salesOrderGet');
+    $route->any('salesOrderList', 'Api\SalesOrderController@salesOrderList');
+    $route->any('salesOrderEdit', 'Api\SalesOrderController@salesOrderEdit');
+    $route->any('salesOrderDetail', 'Api\SalesOrderController@salesOrderDetail');
+    $route->any('salesOrderAdd', 'Api\SalesOrderController@salesOrderAdd');
+    $route->any('salesOrderDel', 'Api\SalesOrderController@salesOrderDel');
+
+    $route->any('constructionGet', 'Api\ConstructionController@constructionGet');
+    $route->any('constructionList', 'Api\ConstructionController@constructionList');
+    $route->any('constructionEdit', 'Api\ConstructionController@constructionEdit');
+    $route->any('constructionDetail', 'Api\ConstructionController@constructionDetail');
+    $route->any('constructionAdd', 'Api\ConstructionController@constructionAdd');
+    $route->any('constructionDel', 'Api\ConstructionController@constructionDel');
+
     $route->any('employeeList', 'Api\EmployeeController@employeeList');
     $route->any('departList', 'Api\EmployeeController@departList');
     $route->any('storehouseList', 'Api\StorehouseController@storehouseList');
     $route->any('basicTypeList', 'Api\BasicTypeController@basicTypeList');
     $route->any('productList', 'Api\ProductController@productList');
-    $route->any('salesOrderList', 'Api\SalesOrderController@salesOrderList');
     $route->any('supplierList', 'Api\SupplierController@customerList');
+    $route->any('customerList', 'Api\CustomerController@customerList');
+
+    //退换货
+    $route->any('ReturnExchangeOrderList', 'Api\ReturnExchangeOrderController@ReturnExchangeOrderList');
+    $route->any('ReturnExchangeOrderEdit', 'Api\ReturnExchangeOrderController@ReturnExchangeOrderEdit');
+    $route->any('ReturnExchangeOrderDetail', 'Api\ReturnExchangeOrderController@ReturnExchangeOrderDetail');
+    $route->any('ReturnExchangeOrderAdd', 'Api\ReturnExchangeOrderController@ReturnExchangeOrderAdd');
+    $route->any('ReturnExchangeOrderDel', 'Api\ReturnExchangeOrderController@ReturnExchangeOrderDel');
+
 });