|
|
@@ -1009,4 +1009,236 @@ class DeviceWorkService extends Service
|
|
|
}
|
|
|
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()];
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|