浏览代码

小高薪

cqp 2 月之前
父节点
当前提交
569616e6a6
共有 2 个文件被更改,包括 137 次插入51 次删除
  1. 74 29
      app/Service/DeviceWorkService.php
  2. 63 22
      app/Service/PersonWorkService.php

+ 74 - 29
app/Service/DeviceWorkService.php

@@ -1176,52 +1176,97 @@ class DeviceWorkService extends Service
     public function dailyDwOrderSave($data, $user)
     {
         $list = $data['list'] ?? [];
-        if (empty($list)) return [false, '没有可保存的数据'];
+        if (empty($list)) return [false, '提交数据不能为空'];
 
         $topDepartId = $user['top_depart_id'];
+        $month = $data['month'];
         $now = time();
 
+        // 1. 预加载设备名称映射,用于友好报错
+        $deviceIds = collect($list)->pluck('device_id')->unique()->toArray();
+        $deviceMap = DB::table('device')
+            ->whereIn('id', $deviceIds)
+            ->pluck('title', 'id')
+            ->toArray();
+
+        // --- 2. 重新分组:在分组的同时,利用原始索引 index 记录行号 ---
+        $groupedByOrder = [];
+        foreach ($list as $index => $item) {
+            $item['_line'] = $index + 1; // 记录原始行号(从1开始)
+            $groupKey = $item['order_time'] . '_' . $item['item_id'];
+            $groupedByOrder[$groupKey][] = $item;
+        }
+
+        // 冲突校验器容器:记录 [设备ID][日期] 下已占用的时间段
+        $deviceTimeline = [];
+
         DB::beginTransaction();
         try {
-            // 清理旧数据
-            $monthStart = $this->changeDateToDate($data['month']);
+            // A. 清理该月份旧数据
+            $monthStart = $this->changeDateToDate($month);
             $monthEnd = strtotime('+1 month', $monthStart) - 1;
 
             $oldOrderIds = DB::table('daily_dw_order')
                 ->where('top_depart_id', $topDepartId)
-                ->where('order_time', '>=', $monthStart)
-                ->where('order_time', '<=', $monthEnd)
-                ->where('is_create', 0)
-                ->where('del_time', 0)->pluck('id');
+                ->whereBetween('order_time', [$monthStart, $monthEnd])
+                ->where('del_time', 0)
+                ->pluck('id');
 
             if ($oldOrderIds->isNotEmpty()) {
                 DB::table('daily_dw_order')->whereIn('id', $oldOrderIds)->update(['del_time' => $now]);
                 DB::table('daily_dw_order_details')->whereIn('main_id', $oldOrderIds)->update(['del_time' => $now]);
             }
 
-            // 按 temp_main_id 分组批量插入
-            $grouped = collect($list)->groupBy('temp_main_id');
-
-            foreach ($grouped as $tempMainId => $details) {
-                $first = $details->first();
+            // B. 遍历重组后的分组,生成单据
+            foreach ($groupedByOrder as $details) {
+                $first = $details[0];
+                $orderTime = $first['order_timestamp'];
+                $itemId = $first['item_id'];
 
+                // 写入设备日工时主表
                 $mainId = DB::table('daily_dw_order')->insertGetId([
-                    'code' => '',
-                    'item_id' => $first['item_id'],
-                    'order_time' => $first['order_timestamp'],
+                    'code'          => '',
+                    'item_id'       => $itemId,
+                    'order_time'    => $orderTime,
                     'top_depart_id' => $topDepartId,
-                    'is_create' => 1,
-                    'crt_id' => $user['id'],
-                    'crt_time' => $now,
-                    'upd_time' => $now,
-                    'del_time' => 0
+                    'is_create'     => 1,
+                    'crt_id'        => $user['id'],
+                    'crt_time'      => $now,
                 ]);
 
-                $insertData = [];
+                $insertDetails = [];
                 foreach ($details as $d) {
-                    $insertData[] = [
+                    $rowNum = $d['_line'];
+                    $devId = $d['device_id'];
+                    $devName = $deviceMap[$devId] ?? "设备(ID:{$devId})";
+
+                    // 时间转分钟数进行冲突校验
+                    $s = (int)$d['start_hour'] * 60 + (int)$d['start_min'];
+                    $e = (int)$d['end_hour'] * 60 + (int)$d['end_min'];
+
+                    // --- 核心校验:精准提示行号 ---
+
+                    // 校验1:逻辑合法性
+                    if ($e <= $s) {
+                        return [false, "第 {$rowNum} 行:设备[{$devName}]在[{$d['order_time']}]的结束时间必须晚于开始时间"];
+                    }
+
+                    // 校验2:跨单据冲突校验
+                    if (isset($deviceTimeline[$devId][$orderTime])) {
+                        foreach ($deviceTimeline[$devId][$orderTime] as $exist) {
+                            // 碰撞算法: (开始A < 结束B) 且 (结束A > 开始B)
+                            if ($s < $exist['e'] && $e > $exist['s']) {
+                                return [false, "第 {$rowNum} 行:设备[{$devName}]在[{$d['order_time']}]的时间段({$d['start_time']}-{$d['end_time']})与其他项目存在冲突,请检查!"];
+                            }
+                        }
+                    }
+
+                    // 记录当前时间段用于后续校验
+                    $deviceTimeline[$devId][$orderTime][] = ['s' => $s, 'e' => $e];
+
+                    $insertDetails[] = [
                         'main_id'         => $mainId,
-                        'device_id'       => $d['device_id'],
+                        'device_id'       => $devId,
                         'top_depart_id'   => $topDepartId,
                         'start_time_hour' => $d['start_hour'],
                         'start_time_min'  => $d['start_min'],
@@ -1229,17 +1274,17 @@ class DeviceWorkService extends Service
                         'end_time_min'    => $d['end_min'],
                         'total_work_min'  => $d['total_work_min'],
                         'crt_time'        => $now,
-                        'upd_time'        => $now,
-                        'del_time'        => 0
                     ];
                 }
-                DB::table('daily_dw_order_details')->insert($insertData);
 
-                // 回填单号
+                // 执行明细批量插入
+                DB::table('daily_dw_order_details')->insert($insertDetails);
+
+                // C. 回填单号
                 $code = $this->generateBillNo([
                     'top_depart_id' => $topDepartId,
                     'type' => DailyDwOrder::Order_type,
-                    'period' => date("Ym", $first['order_timestamp'])
+                    'period' => date("Ym", $orderTime)
                 ]);
                 DB::table('daily_dw_order')->where('id', $mainId)->update(['code' => $code]);
             }
@@ -1248,7 +1293,7 @@ class DeviceWorkService extends Service
             return [true, ''];
         } catch (\Exception $e) {
             DB::rollBack();
-            return [false, '保存失败: ' . $e->getMessage()];
+            return [false, "保存失败:" . $e->getMessage()];
         }
     }
 }

+ 63 - 22
app/Service/PersonWorkService.php

@@ -1235,19 +1235,36 @@ class PersonWorkService extends Service
         if (empty($list)) return [false, '没有可保存的数据'];
 
         $topDepartId = $user['top_depart_id'];
+        $month = $data['month'];
         $now = time();
 
+        // 1. 预加载员工姓名,用于精准报错
+        $empIds = collect($list)->pluck('employee_id')->unique()->toArray();
+        $empMap = DB::table('employee')
+            ->whereIn('id', $empIds)
+            ->pluck('title', 'id')
+            ->toArray();
+
+        // --- 2. 重新分组:在分组的同时,利用原始索引记录行号 ---
+        $groupedByOrder = [];
+        foreach ($list as $index => $item) {
+            $item['_line'] = $index + 1; // 记录原始行号(从1开始)
+            $groupKey = $item['order_time'] . '_' . $item['item_id'];
+            $groupedByOrder[$groupKey][] = $item;
+        }
+
+        // 3. 跨单据冲突校验器:记录 [员工ID][日期] 的时间轴
+        $empTimeline = [];
+
         DB::beginTransaction();
         try {
-            // 1. 清理旧数据 (严格按月份和部门清理,防止重复)
-            $monthStart = $this->changeDateToDate($data['month']);
+            // A. 清理该月份旧数据 (is_create = 1 表示自动生成的)
+            $monthStart = $this->changeDateToDate($month);
             $monthEnd = strtotime('+1 month', $monthStart) - 1;
 
             $oldOrderIds = DB::table('daily_pw_order')
                 ->where('top_depart_id', $topDepartId)
-                ->where('order_time', '>=', $monthStart)
-                ->where('order_time', '<=', $monthEnd)
-                ->where('is_create', 0)
+                ->whereBetween('order_time', [$monthStart, $monthEnd])
                 ->where('del_time', 0)
                 ->pluck('id');
 
@@ -1256,31 +1273,55 @@ class PersonWorkService extends Service
                 DB::table('daily_pw_order_details')->whereIn('main_id', $oldOrderIds)->update(['del_time' => $now]);
             }
 
-            // 2. 按 temp_main_id 分组写入
-            $grouped = collect($list)->groupBy('temp_main_id');
-
-            foreach ($grouped as $tempMainId => $details) {
-                $first = $details->first();
+            // B. 遍历重组后的分组写入
+            foreach ($groupedByOrder as $details) {
+                $first = $details[0];
+                $orderTime = $first['order_timestamp'];
+                $itemId = $first['item_id'];
 
                 // 写入主表
                 $mainId = DB::table('daily_pw_order')->insertGetId([
-                    'code' => '',
-                    'item_id' => $first['item_id'],
-                    'order_time' => $first['order_timestamp'],
+                    'code'          => '',
+                    'item_id'       => $itemId,
+                    'order_time'    => $orderTime,
                     'top_depart_id' => $topDepartId,
-                    'is_create' => 1,
-                    'crt_id' => $user['id'],
-                    'crt_time' => $now,
+                    'is_create'     => 1,
+                    'crt_id'        => $user['id'],
+                    'crt_time'      => $now,
                 ]);
 
-                // 构造批量插入的明细数组
                 $insertDetails = [];
                 foreach ($details as $d) {
+                    $rowNum = $d['_line']; // 获取原始行号
+                    $empId = $d['employee_id'];
+                    $empName = $empMap[$empId] ?? "未知人员(ID:{$empId})";
+
+                    // 计算分钟数
+                    $s = (int)$d['start_hour'] * 60 + (int)$d['start_min'];
+                    $e = (int)$d['end_hour'] * 60 + (int)$d['end_min'];
+
+                    // --- 校验逻辑:精准提示行号 ---
+
+                    // 校验1:逻辑合法性
+                    if ($e <= $s) {
+                        return [false, "第 {$rowNum} 行:人员[{$empName}]在[{$d['order_time']}]的时间段无效:结束时间应晚于开始时间"];
+                    }
+
+                    // 校验2:跨单据时间重叠校验
+                    if (isset($empTimeline[$empId][$orderTime])) {
+                        foreach ($empTimeline[$empId][$orderTime] as $exist) {
+                            // 碰撞判断: Max(S1, S2) < Min(E1, E2) 则重叠
+                            if ($s < $exist['e'] && $e > $exist['s']) {
+                                return [false, "第 {$rowNum} 行:人员[{$empName}]在[{$d['order_time']}]存在时间重叠({$d['start_time']}-{$d['end_time']}),请检查!"];
+                            }
+                        }
+                    }
+                    $empTimeline[$empId][$orderTime][] = ['s' => $s, 'e' => $e];
+
                     $insertDetails[] = [
                         'main_id'         => $mainId,
-                        'employee_id'     => $d['employee_id'],
+                        'employee_id'     => $empId,
                         'top_depart_id'   => $topDepartId,
-                        // 直接使用预览时生成的字段,效率更高
                         'start_time_hour' => $d['start_hour'],
                         'start_time_min'  => $d['start_min'],
                         'end_time_hour'   => $d['end_hour'],
@@ -1293,11 +1334,11 @@ class PersonWorkService extends Service
                 // 批量写入明细
                 DB::table('daily_pw_order_details')->insert($insertDetails);
 
-                // 3. 生成并回填单号
+                // C. 回填单号
                 $code = $this->generateBillNo([
                     'top_depart_id' => $topDepartId,
                     'type' => DailyPwOrder::Order_type,
-                    'period' => date("Ym", $first['order_timestamp'])
+                    'period' => date("Ym", $orderTime)
                 ]);
                 DB::table('daily_pw_order')->where('id', $mainId)->update(['code' => $code]);
             }
@@ -1306,7 +1347,7 @@ class PersonWorkService extends Service
             return [true, ''];
         } catch (\Exception $e) {
             DB::rollBack();
-            return [false, '保存失败: ' . $e->getMessage() . ' (Line: ' . $e->getLine() . ')'];
+            return [false, "保存失败:" . $e->getMessage()];
         }
     }
 }