|
|
@@ -620,6 +620,8 @@ class DeviceWorkService extends Service
|
|
|
$internalOverlap = [];
|
|
|
|
|
|
foreach ($data['details'] as $key => $value){
|
|
|
+ $line = $key + 1;
|
|
|
+ $t = "第" . $line . "行";
|
|
|
$empId = $value['device_id'] ?? 0;
|
|
|
if(empty($empId)) return [false, '设备不能为空'];
|
|
|
|
|
|
@@ -627,23 +629,29 @@ class DeviceWorkService extends Service
|
|
|
|
|
|
// 校验数字有效性
|
|
|
$res = $this->checkNumber($value['start_time_hour'], 0, 'non-negative');
|
|
|
- if(!$res['valid']) return [false, "设备{$empName}开始点:" . $res['error']];
|
|
|
- if($value['start_time_hour'] > 23) return [false, false, "设备{$empName}开始点不合法"];
|
|
|
+ if(!$res['valid']) return [false, $t . "设备{$empName}开始点:" . $res['error']];
|
|
|
+ if($value['start_time_hour'] > 23) return [false, false, $t . "设备{$empName}开始点不合法"];
|
|
|
$res = $this->checkNumber($value['start_time_min'], 0, 'non-negative');
|
|
|
- if(!$res['valid']) return [false, "设备{$empName}开始分:" . $res['error']];
|
|
|
- if($value['start_time_min'] > 60) return [false, false, "设备{$empName}开始点不合法"];
|
|
|
+ if(!$res['valid']) return [false, $t ."设备{$empName}开始分:" . $res['error']];
|
|
|
+ if($value['start_time_min'] > 60) return [false, false, $t . "设备{$empName}开始点不合法"];
|
|
|
$res = $this->checkNumber($value['end_time_hour'], 0, 'non-negative');
|
|
|
- if(!$res['valid']) return [false, "设备{$empName}结束点:" . $res['error']];
|
|
|
- if($value['end_time_hour'] > 24) return [false, false, "设备{$empName}结束点不合法"];
|
|
|
+ if(!$res['valid']) return [false, $t ."设备{$empName}结束点:" . $res['error']];
|
|
|
+ if($value['end_time_hour'] > 24) return [false, false, $t . "设备{$empName}结束点不合法"];
|
|
|
$res = $this->checkNumber($value['end_time_min'], 0, 'non-negative');
|
|
|
- if(!$res['valid']) return [false, "设备{$empName}结束分:" . $res['error']];
|
|
|
- if($value['end_time_min'] > 60) return [false, false, "设备{$empName}结束分不合法"];
|
|
|
+ if(!$res['valid']) return [false, $t ."设备{$empName}结束分:" . $res['error']];
|
|
|
+ if($value['end_time_min'] > 60) return [false, false, $t . "设备{$empName}结束分不合法"];
|
|
|
|
|
|
$currentStart = $value['start_time_hour'] * 60 + $value['start_time_min'];
|
|
|
$currentEnd = $value['end_time_hour'] * 60 + $value['end_time_min'];
|
|
|
|
|
|
if ($currentStart >= $currentEnd) {
|
|
|
- return [false, "设备{$empName}:开始时间必须早于结束时间"];
|
|
|
+ return [false, $t . "设备{$empName}:开始时间必须早于结束时间"];
|
|
|
+ }
|
|
|
+
|
|
|
+ // --- 新增:总分钟数校验 ---
|
|
|
+ $calculatedTotal = $currentEnd - $currentStart;
|
|
|
+ if (!isset($value['total_work_min']) || intval($value['total_work_min']) !== $calculatedTotal) {
|
|
|
+ return [false, $t . "设备{$empName}:工时计算有误,应为 {$calculatedTotal} 分钟"];
|
|
|
}
|
|
|
|
|
|
// --- 3. 内部重叠校验(防止一次提交多行重复) ---
|
|
|
@@ -1046,158 +1054,6 @@ class DeviceWorkService extends Service
|
|
|
]];
|
|
|
}
|
|
|
|
|
|
- private function calculateDailyDeviceAllocation1($monthStart, $topDepartId, $user)
|
|
|
- {
|
|
|
- // 加载月度设备明细
|
|
|
- $monthlyOrder = DB::table('monthly_dw_order_details as d')
|
|
|
- ->join('monthly_dw_order as m', 'm.id', '=', 'd.main_id')
|
|
|
- ->where('m.month', $monthStart)
|
|
|
- ->where('m.top_depart_id', $topDepartId)
|
|
|
- ->where('m.del_time', 0)
|
|
|
- ->where('d.del_time', 0)
|
|
|
- ->select('d.*')
|
|
|
- ->get();
|
|
|
-
|
|
|
- if ($monthlyOrder->isEmpty()) return ['status' => false, 'msg' => '未找到设备月度工时明细'];
|
|
|
-
|
|
|
- $usedDeviceIds = $monthlyOrder->pluck('device_id')->unique()->toArray();
|
|
|
-
|
|
|
- // 【优化点】按需查询设备名称
|
|
|
- $deviceMap = DB::table('device')
|
|
|
- ->whereIn('id', $usedDeviceIds)
|
|
|
- ->pluck('title', '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', RuleSetDetails::type_two) // 设备类型
|
|
|
- ->where('rd.top_depart_id', $topDepartId)
|
|
|
- ->where('r.del_time', 0)
|
|
|
- ->where('rd.del_time', 0)
|
|
|
- ->select('rd.*')
|
|
|
- ->get();
|
|
|
-
|
|
|
- // 【优化点】按需查询项目名称
|
|
|
- $usedItemIds = $ruleSet->pluck('item_id')->unique()->toArray();
|
|
|
- $itemMap = DB::table('item')
|
|
|
- ->whereIn('id', $usedItemIds)
|
|
|
- ->pluck('title', 'id')
|
|
|
- ->toArray();
|
|
|
-
|
|
|
- $ruleSetGrouped = $ruleSet->groupBy('data_id'); // 按 device_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('top_depart_id', $topDepartId)
|
|
|
- ->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 = $ruleSetGrouped->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' => $deviceMap[$deviceId] ?? '未知设备',
|
|
|
- '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];
|
|
|
- }
|
|
|
-
|
|
|
private function calculateDailyDeviceAllocation($monthStart, $topDepartId, $user)
|
|
|
{
|
|
|
// 加载月度设备明细
|