cqp 2 месяцев назад
Родитель
Сommit
57c3e99de5
3 измененных файлов с 260 добавлено и 0 удалено
  1. 26 0
      app/Http/Controllers/Api/DeviceWorkController.php
  2. 232 0
      app/Service/DeviceWorkService.php
  3. 2 0
      routes/api.php

+ 26 - 0
app/Http/Controllers/Api/DeviceWorkController.php

@@ -153,4 +153,30 @@ class DeviceWorkController extends BaseController
             return $this->json_return(201,$data);
             return $this->json_return(201,$data);
         }
         }
     }
     }
+
+    public function dailyDwOrderPreview(Request $request)
+    {
+        $service = new DeviceWorkService();
+        $user = $request->userData;
+        list($status,$data) = $service->dailyDwOrderPreview($request->all(),$user);
+
+        if($status){
+            return $this->json_return(200,'',$data);
+        }else{
+            return $this->json_return(201,$data);
+        }
+    }
+
+    public function dailyDwOrderSave(Request $request)
+    {
+        $service = new DeviceWorkService();
+        $user = $request->userData;
+        list($status,$data) = $service->dailyDwOrderSave($request->all(),$user);
+
+        if($status){
+            return $this->json_return(200,'',$data);
+        }else{
+            return $this->json_return(201,$data);
+        }
+    }
 }
 }

+ 232 - 0
app/Service/DeviceWorkService.php

@@ -1009,4 +1009,236 @@ class DeviceWorkService extends Service
         }
         }
         return [true, ''];
         return [true, ''];
     }
     }
+
+    public function dailyDwOrderPreview($data, $user)
+    {
+        $topDepartId = $user['top_depart_id'];
+        if (empty($data['month'])) return [false, '月份不能为空'];
+
+        $monthStart = $this->changeDateToDate($data['month']);
+
+        // 调用核心计算逻辑
+        $result = $this->calculateDailyDeviceAllocation($monthStart, $topDepartId, $user);
+
+        if (!$result['status']) return [false, $result['msg']];
+
+        return [true, [
+            'list' => $result['data'] // 返回给前端预览
+        ]];
+    }
+
+    private function calculateDailyDeviceAllocation($monthStart, $topDepartId, $user)
+    {
+        $monthEnd = strtotime('+1 month', $monthStart) - 1;
+
+        // --- 1. 基础数据预加载 ---
+        // 月度明细 & 设备名称
+        $monthlyOrder = DB::table('monthly_dw_order_details as d')
+            ->join('monthly_dw_order as m', 'm.id', '=', 'd.main_id')
+            ->leftJoin('device as dv', 'dv.id', '=', 'd.device_id') // 假设设备表是 device
+            ->where('m.month', $monthStart)->where('m.top_depart_id', $topDepartId)
+            ->where('m.del_time', 0)->where('d.del_time', 0)
+            ->select('d.*', 'dv.name as device_title')->get();
+
+        if ($monthlyOrder->isEmpty()) return ['status' => false, 'msg' => '未找到设备月度工时明细'];
+
+        // 项目名称映射
+        $itemMap = DB::table('items')->pluck('name', 'id')->toArray();
+
+        // 设备分配比例规则
+        $ruleSet = DB::table('rule_set_details as rd')
+            ->join('rule_set as r', 'r.id', '=', 'rd.main_id')
+            ->where('r.month', $monthStart)
+            ->where('rd.type', 2) // 假设 2 是设备规则
+            ->where('r.del_time', 0)->where('rd.del_time', 0)
+            ->select('rd.*')->get()->groupBy('data_id');
+
+        // 标准班次
+        $standardWorkRanges = DB::table('work_range_details')
+            ->where('top_depart_id', $topDepartId)->where('del_time', 0)->get();
+
+        $dayMaxAvail = (int)$standardWorkRanges->sum('total_work_min');
+
+        // 工作日历
+        $workDays = DB::table('calendar_details')
+            ->where('month', $monthStart)->where('is_work', 1)->where('del_time', 0)
+            ->orderBy('time', 'asc')->get();
+
+        if ($workDays->isEmpty()) return ['status' => false, 'msg' => '未配置工作日历'];
+
+        // --- 2. 阶段一:计算每天每台设备在每个项目下的整数分钟数 ---
+        $finalAlloc = [];
+        foreach ($monthlyOrder as $mDetail) {
+            $deviceId = $mDetail->device_id;
+            $deviceRules = $ruleSet->get($deviceId);
+            if (!$deviceRules) continue;
+
+            $remainingMin = (int)round((float)$mDetail->rd_total_hours * 60);
+            if ($remainingMin <= 0) continue;
+
+            foreach ($workDays as $dayInfo) {
+                if ($remainingMin <= 0) break;
+
+                $canAllocToday = min($remainingMin, $dayMaxAvail);
+                $allocatedInDay = 0;
+                $ruleCount = count($deviceRules);
+
+                foreach ($deviceRules as $index => $rule) {
+                    $rate = (float)$rule->rate / 100;
+                    // 最后一个项目拿差额,防止精度流失
+                    if ($index === $ruleCount - 1) {
+                        $projectMin = $canAllocToday - $allocatedInDay;
+                    } else {
+                        $projectMin = (int)round($canAllocToday * $rate);
+                    }
+
+                    if ($projectMin > 0) {
+                        $finalAlloc[$dayInfo->time][$rule->item_id][$deviceId] = $projectMin;
+                        $allocatedInDay += $projectMin;
+                    }
+                }
+                $remainingMin -= $canAllocToday;
+            }
+        }
+
+        // --- 3. 阶段二:打散到具体时段并生成预览行 ---
+        $previewList = [];
+        $dailyDevicePools = [];
+        $tempMainIdCounter = 1;
+
+        foreach ($finalAlloc as $dayTs => $projects) {
+            foreach ($projects as $itemId => $devices) {
+
+                $currentTempMainId = $tempMainIdCounter++;
+                $itemTitle = $itemMap[$itemId] ?? '未知项目';
+
+                foreach ($devices as $deviceId => $toAllocMin) {
+                    // 初始化该设备当天的标准班次池
+                    if (!isset($dailyDevicePools[$dayTs][$deviceId])) {
+                        $pool = [];
+                        foreach ($standardWorkRanges as $swr) {
+                            $pool[] = [
+                                's' => (int)($swr->start_time_hour * 60 + $swr->start_time_min),
+                                'e' => (int)($swr->end_time_hour * 60 + $swr->end_time_min)
+                            ];
+                        }
+                        $dailyDevicePools[$dayTs][$deviceId] = $pool;
+                    }
+
+                    $tempRem = (int)$toAllocMin;
+                    foreach ($dailyDevicePools[$dayTs][$deviceId] as &$p) {
+                        if ($tempRem <= 0) break;
+
+                        $pMax = $p['e'] - $p['s'];
+                        if ($pMax <= 0) continue;
+
+                        $take = min($tempRem, $pMax);
+                        $start = (int)$p['s'];
+                        $end = $start + $take;
+
+                        $previewList[] = [
+                            'temp_main_id'    => $currentTempMainId,
+                            'order_time'      => date('Y-m-d', $dayTs),
+                            'order_timestamp' => $dayTs,
+                            'item_id'         => $itemId,
+                            'item_title'      => $itemTitle,
+                            'device_id'       => $deviceId,
+                            'device_title'    => $monthlyOrder->where('device_id', $deviceId)->first()->device_title ?? '未知设备',
+                            'start_time'      => sprintf('%02d:%02d', floor($start / 60), $start % 60),
+                            'end_time'        => sprintf('%02d:%02d', floor($end / 60), $end % 60),
+                            'start_hour'      => (int)floor($start / 60),
+                            'start_min'       => (int)($start % 60),
+                            'end_hour'        => (int)floor($end / 60),
+                            'end_min'         => (int)($end % 60),
+                            'total_work_min'  => $take,
+                        ];
+
+                        $tempRem -= $take;
+                        $p['s'] = $end; // 时间指针向后推,确保不重叠
+                    }
+                }
+            }
+        }
+
+        return ['status' => true, 'data' => $previewList];
+    }
+
+    public function dailyDwOrderSave($data, $user)
+    {
+        $list = $data['list'] ?? [];
+        if (empty($list)) return [false, '没有可保存的数据'];
+
+        $topDepartId = $user['top_depart_id'];
+        $now = time();
+
+        DB::beginTransaction();
+        try {
+            // 清理旧数据
+            $monthStart = $this->changeDateToDate($data['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', 1)
+                ->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();
+
+                $mainId = DB::table('daily_dw_order')->insertGetId([
+                    'code' => '',
+                    'item_id' => $first['item_id'],
+                    'order_time' => $first['order_timestamp'],
+                    'top_depart_id' => $topDepartId,
+                    'is_create' => 1,
+                    'crt_id' => $user['id'],
+                    'crt_time' => $now,
+                    'upd_time' => $now,
+                    'del_time' => 0
+                ]);
+
+                $insertData = [];
+                foreach ($details as $d) {
+                    $insertData[] = [
+                        'main_id'         => $mainId,
+                        'device_id'       => $d['device_id'],
+                        'top_depart_id'   => $topDepartId,
+                        'start_time_hour' => $d['start_hour'],
+                        'start_time_min'  => $d['start_min'],
+                        'end_time_hour'   => $d['end_hour'],
+                        '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);
+
+                // 回填单号
+                $code = $this->generateBillNo([
+                    'top_depart_id' => $topDepartId,
+                    'type' => DailyDwOrder::Order_type,
+                    'period' => date("Ym", $first['order_timestamp'])
+                ]);
+                DB::table('daily_dw_order')->where('id', $mainId)->update(['code' => $code]);
+            }
+
+            DB::commit();
+            return [true, ''];
+        } catch (\Exception $e) {
+            DB::rollBack();
+            return [false, '保存失败: ' . $e->getMessage()];
+        }
+    }
 }
 }

+ 2 - 0
routes/api.php

@@ -145,6 +145,8 @@ Route::group(['middleware'=> ['checkLogin']],function ($route){
     $route->any('dailyDwOrderDetail', 'Api\DeviceWorkController@dailyDwOrderDetail');
     $route->any('dailyDwOrderDetail', 'Api\DeviceWorkController@dailyDwOrderDetail');
     //根据月工时生成
     //根据月工时生成
     $route->any('dailyDwOrderCreate', 'Api\DeviceWorkController@dailyDwOrderCreate');
     $route->any('dailyDwOrderCreate', 'Api\DeviceWorkController@dailyDwOrderCreate');
+    $route->any('dailyDwOrderPreview', 'Api\DeviceWorkController@dailyDwOrderPreview');
+    $route->any('dailyDwOrderSave', 'Api\DeviceWorkController@dailyDwOrderSave');
 
 
     //日历设置
     //日历设置
     $route->any('calendarList', 'Api\CalendarController@calendarList');
     $route->any('calendarList', 'Api\CalendarController@calendarList');