error( '异常操作!', ['status' => false]); // } // $where['admin_username'] = $param['ad']; // $where['admin_pwd_salt'] = $param['salt']; // $admin = Db::name('admin')->where($where)->find(); $admin = Admin::get(1); if ($admin) { $this->success('正常!', ['status' => true]); } else { $this->error('失败!', ['status' => false]); } } //大屏地图车辆列表 public function screen_list() { $map['is_del'] = array('in', '1,2'); $field = 'id,vehicleNo,longitude,latitude,longitude_84,latitude_84,vec1,vehicle_type,is_waybill, is_violations,qr_color,positionTime,is_del,zai'; $vehicle_list = Vehicle::where($map)->field($field)->select(); //待优化运单查询 $w_field = 'tow_license,w_mission,waybill_loading,waybill_unloading,sale_product,cargocount,driver_tel'; $day_waybill_list = Waybill::whereTime('waybill_date', 'd')->field($w_field)->select(); $temp = array_column($day_waybill_list, 'tow_license'); if ($vehicle_list) { foreach ($vehicle_list as $k => $v) { $vehicle_list[$k]['waybill_status'] = false; $v_id = in_array($v['vehicleNo'], $temp); if ($v_id) { $vehicle_list[$k]['waybill_status'] = true; $waybill_info = Waybill::whereTime('waybill_date', 'd')->where('tow_license', $v['vehicleNo'])->field($w_field)->find(); $vehicle_list[$k]['waybill_info'] = $waybill_info; } } } //明宇停车场(count车辆总数;enter_count当日进入车辆数;当日出去车辆数) $mingyu['count'] = Vehicle::where($map)->where('perimeter_id', 76)->field('id')->count(); $mingyu['enter_count'] = LineLog::whereTime('create_time', 'd')->where(['is_del' => 1, 'perimeter_id' => 76, 'type' => 1])->count(); $mingyu['out_count'] = LineLog::whereTime('create_time', 'd')->where(['is_del' => 1, 'perimeter_id' => 76, 'type' => 2])->count(); $data['vehicle_list'] = $vehicle_list; $data['mingyu'] = $mingyu; $this->success('请求成功!', $data); } //大屏地图车辆列表 public function screen_list22() { $map['is_del'] = array('in', '1,2'); $field = 'id,vehicleNo,longitude,latitude,longitude_84,latitude_84,vec1,vehicle_type,is_waybill, is_violations,qr_color,positionTime,is_del,zai'; $vehicle_list = Vehicle::where($map)->field($field)->select(); if ($vehicle_list) { foreach ($vehicle_list as $k => $v) { $w_field = 'w_mission,waybill_loading,waybill_unloading,sale_product,cargocount,driver_tel'; $waybill_info = Db::name('waybill_c')->whereTime('waybill_date', 'd')->where('tow_license', $v['vehicleNo'])->field($w_field)->find(); $vehicle_list[$k]['waybill_status'] = false; if ($waybill_info) { $vehicle_list[$k]['waybill_status'] = true; $vehicle_list[$k]['waybill_info'] = $waybill_info; } } } //明宇停车场(count车辆总数;enter_count当日进入车辆数;当日出去车辆数) $mingyu['count'] = Vehicle::where($map)->where('perimeter_id', 76)->field('id')->count(); $mingyu['enter_count'] = LineLog::whereTime('create_time', 'd')->where(['is_del' => 1, 'perimeter_id' => 76, 'type' => 1])->count(); $mingyu['out_count'] = LineLog::whereTime('create_time', 'd')->where(['is_del' => 1, 'perimeter_id' => 76, 'type' => 2])->count(); $data['vehicle_list'] = $vehicle_list; $data['mingyu'] = $mingyu; $this->success('请求成功!', $data); } //大屏-总览 public function overview() { $map['is_del'] = 1; //园区基本情况 //一级企业 二级企业 三级企业 $info['enterprise_count'] = Perimeter::group('region_lv')->where($map)->field('region_lv,count(id) as num')->select(); //片区情况 - 统计每个片区的车辆数量(取前3个) $areaStats = $this->getAreaVehicleStatistics(); $info['area'] = []; // 取前3个片区的车辆数量,如果不足3个则补0 for ($i = 0; $i < 3; $i++) { $info['area'][] = isset($areaStats[$i]) ? $areaStats[$i]['value'] : 0; } //道路情况 - 统计道路上的车辆数量总和 $roadStats = $this->getRoadVehicleStatistics(); $info['road_count'] = 0; if ($roadStats) { foreach ($roadStats as $road) { $info['road_count'] += $road['count']; } } //卡口流量统计 $info['road_checkpoints'] = [ 'name' => ['卡1', '卡2', '卡2', '卡2', '卡2', '卡2'],//卡口名称 'drive_in' => [35, 32, 36, 47, 14, 60],//驶入 'drive_out' => [47, 20, 16, 52, 40, 18]//驶出 ]; //近30天报警统计 $info['alarm_count'] = []; // 计算30天前的日期 $startDate = date('Y-m-d 00:00:00', strtotime('-30 days')); $endDate = date('Y-m-d 23:59:59'); $event_type = EventType::select(); foreach ($event_type as $k => $v) { $info['alarm_count'][] = [ 'name' => $v['name'], 'value' => Alarm::where('type', $v['id']) ->where('create_time', '>=', $startDate) ->where('create_time', '<=', $endDate) ->count() ]; } //报警处理情况 - 统计近30天的报警处理情况 $alarmMap = [ 'is_del' => 1, ]; // 已处理:res_status = 1(已响应)或 feedback_status = 1(已反馈) $processed = Alarm::where($alarmMap) ->where('create_time', '>=', $startDate) ->where('create_time', '<=', $endDate) ->where(function($query) { $query->where('res_status', 1) ->whereOr('feedback_status', 1); }) ->count(); // 未处理:res_status = 0(未响应)且 feedback_status = 0(未反馈) $untreated = Alarm::where($alarmMap) ->where('create_time', '>=', $startDate) ->where('create_time', '<=', $endDate) ->where('res_status', 0) ->where('feedback_status', 0) ->count(); $he = $processed + $untreated; $info['alarm_handling'] = [ 'count' => array_sum(array_column($info['alarm_count'], 'value')), 'processed' => $processed, 'untreated' => $untreated, 'he' => $he, 'processed_ratio' => $he == 0 ? 0 : round(($processed / $he) * 100, 2), 'untreated_ratio' => $he == 0 ? 0 : round(($untreated / $he) * 100, 2), ]; //园区危化品车辆统计 - 在线/离线(根据定位时间判断,10分钟内更新为在线) $vehicleMap = ['is_del' => ['in', '1,2']]; // 计算10分钟前的时间戳 $onlineTimeThreshold = time() - 600; // 10分钟 = 600秒 // 查询所有车辆 $allVehicles = Vehicle::where($vehicleMap) ->field('id,vehicleNo,positionTime') ->select(); $online = 0; $offline = 0; if ($allVehicles) { foreach ($allVehicles as $vehicle) { if (empty($vehicle['positionTime'])) { $offline++; continue; } // 将定位时间转换为时间戳 $positionTimestamp = is_numeric($vehicle['positionTime']) ? $vehicle['positionTime'] : strtotime($vehicle['positionTime']); // 如果定位时间在10分钟内,则是在线 if ($positionTimestamp >= $onlineTimeThreshold) { $online++; } else { $offline++; } } } $he = $online + $offline; $info['vehicle'] = [ 'online' => $online, 'offline' => $offline, 'online_ratio' => $he == 0 ? 0 : round(($online / $he) * 100, 2), 'offline_ratio' => $he == 0 ? 0 : round(($offline / $he) * 100, 2) ]; //危化品车流量top5 - 按装货单位统计车流量(装货+卸货) $waybillMap = [ 'is_del' => 1, 'waybill_loading' => ['<>', ''], // 装货单位不为空 ]; // 统计每个装货单位的装货数量 $loadingStats = Db::name('waybill') ->where($waybillMap) ->where('w_mission', '装货') ->field('waybill_loading, COUNT(*) as count') ->group('waybill_loading') ->select(); // 统计每个装货单位的卸货数量 $unloadingStats = Db::name('waybill') ->where($waybillMap) ->where('w_mission', '卸货') ->field('waybill_loading, COUNT(*) as count') ->group('waybill_loading') ->select(); // 构建装货单位到数量的映射 $loadingMap = []; $unloadingMap = []; if ($loadingStats) { foreach ($loadingStats as $stat) { $loadingMap[$stat['waybill_loading']] = intval($stat['count']); } } if ($unloadingStats) { foreach ($unloadingStats as $stat) { $unloadingMap[$stat['waybill_loading']] = intval($stat['count']); } } // 合并所有装货单位,计算总车流量(装货+卸货) $allEnterprises = array_unique(array_merge(array_keys($loadingMap), array_keys($unloadingMap))); $trafficData = []; foreach ($allEnterprises as $enterprise) { $loadingCount = isset($loadingMap[$enterprise]) ? $loadingMap[$enterprise] : 0; $unloadingCount = isset($unloadingMap[$enterprise]) ? $unloadingMap[$enterprise] : 0; $totalCount = $loadingCount + $unloadingCount; $trafficData[] = [ 'waybill_loading' => $enterprise, 'loading' => $loadingCount, 'unloading' => $unloadingCount, 'total' => $totalCount ]; } // 按总车流量排序,取前5个 usort($trafficData, function($a, $b) { return $b['total'] - $a['total']; }); $top5 = array_slice($trafficData, 0, 5); // 构建返回数据 $nameList = []; $loadingList = []; $unloadingList = []; foreach ($top5 as $item) { $nameList[] = [ 'waybill_loading' => $item['waybill_loading'], 'count' => $item['total'] ]; $loadingList[] = $item['loading']; $unloadingList[] = $item['unloading']; } // 如果不足5个,用空数据补齐 while (count($nameList) < 5) { $nameList[] = ['waybill_loading' => '', 'count' => 0]; $loadingList[] = 0; $unloadingList[] = 0; } $info['traffic_volume'] = [ 'name' => $nameList, 'loading' => $loadingList, // 装货数量 'unloading' => $unloadingList // 卸货数量 ]; //全年运单统计 - 统计当前年份每个月的运单数量 $year = date('Y'); $startDate = "{$year}-01-01 00:00:00"; $endDate = "{$year}-12-31 23:59:59"; // 查询实际数据(只统计未删除的运单) $waybillMap = ['is_del' => 1]; $actualStats = Db::name('waybill') ->where($waybillMap) ->where('create_time', '>=', $startDate) ->where('create_time', '<=', $endDate) ->field("DATE_FORMAT(create_time, '%Y-%m') as month, COUNT(*) as count") ->group("DATE_FORMAT(create_time, '%Y-%m')") ->select(); $monthlyData = []; foreach ($actualStats as $stat) { $month = date('m', strtotime($stat['month'])); $monthlyData[$month] = [ 'month' => $month, 'year_month' => $stat['month'], 'count' => intval($stat['count']), ]; } // 构建完整的12个月数据(包含空月份) $fullYearStats = []; for ($i = 1; $i <= 12; $i++) { $month = str_pad($i, 2, '0', STR_PAD_LEFT); $yearMonth = "{$year}-{$month}"; if (isset($monthlyData[$month])) { // 有数据的月份 $fullYearStats[] = $monthlyData[$month]; } else { // 无数据的月份补零 $fullYearStats[] = [ 'month' => $month, 'year_month' => $yearMonth, 'count' => 0, ]; } } $info['year'] = $fullYearStats; $this->success('请求成功!', $info); } //大屏-车辆码 public function vehicle_code() { $map['is_del'] = array('in', '1,2'); //绿码车数量 $info['green_code_count'] = Vehicle::where($map)->where('qr_color', 1)->count(); //黄码车数量 $info['yellow_code_count'] = Vehicle::where($map)->where('qr_color', 2)->count(); //红码车数量 $info['red_code_count'] = Vehicle::where($map)->where('qr_color', 3)->count(); //报警列表 - 只显示当天的报警 $wh_police['is_del'] = 1; $wh_police['type'] = array('in', '1,2,3,4,5,6'); $field = 'license,name,trigger_type,create_time'; // 查询当天的报警 $police_list['count'] = Alarm::where($wh_police)->whereTime('create_time', 'd')->count(); $police_list['list'] = Alarm::where($wh_police)->whereTime('create_time', 'd')->field($field)->order('create_time desc')->select(); // 如果当天没有报警,显示最近7天的报警 if ($police_list['count'] == 0) { $police_list['count'] = Alarm::where($wh_police)->whereTime('create_time', '-7 days')->count(); $police_list['list'] = Alarm::where($wh_police)->whereTime('create_time', '-7 days')->field($field)->order('create_time desc')->limit(50)->select(); } $data['info'] = $info;//基础信息-左 $data['police_list'] = $police_list;//运单信息-右3 $this->success('请求成功!', $data); } //大屏-车辆动态 public function vehicle_trend() { $map['is_del'] = array('in', '1,2'); $park_info = Db::name('perimeter')->where('id', 1)->field('enter_vehicle,out_vehicle')->find(); // 园区围栏坐标(用于判断进出园) $max_path_json = '[{"lng":"107.01527","lat":"29.895601"},{"lng":"106.997876","lat":"29.89281"},{"lng":"106.991391","lat":"29.887939"},{"lng":"106.985853","lat":"29.880405"},{"lng":"106.981835","lat":"29.871026"},{"lng":"106.960543","lat":"29.849142"},{"lng":"106.950737","lat":"29.83564"},{"lng":"106.945862","lat":"29.824089"},{"lng":"106.943563","lat":"29.814148"},{"lng":"106.945054","lat":"29.799754"},{"lng":"106.942764","lat":"29.783607"},{"lng":"106.970332","lat":"29.766519"},{"lng":"106.983273","lat":"29.751322"},{"lng":"107.017612","lat":"29.737776"},{"lng":"107.050425","lat":"29.734115"},{"lng":"107.064458","lat":"29.737683"},{"lng":"107.064744","lat":"29.739318"},{"lng":"107.058942","lat":"29.748447"},{"lng":"107.055205","lat":"29.762378"},{"lng":"107.071581","lat":"29.77504"},{"lng":"107.086248","lat":"29.789923"},{"lng":"107.088111","lat":"29.796775"},{"lng":"107.084106","lat":"29.807387"},{"lng":"107.080959","lat":"29.81142"},{"lng":"107.068298","lat":"29.813474"},{"lng":"107.06422","lat":"29.813406"},{"lng":"107.060435","lat":"29.82112"},{"lng":"107.050628","lat":"29.827207"},{"lng":"107.04726","lat":"29.828623"},{"lng":"107.046543","lat":"29.829966"},{"lng":"107.04701","lat":"29.830878"},{"lng":"107.04712","lat":"29.834263"},{"lng":"107.044827","lat":"29.838826"},{"lng":"107.053757","lat":"29.842875"},{"lng":"107.056479","lat":"29.849047"},{"lng":"107.057124","lat":"29.85153"},{"lng":"107.056802","lat":"29.853468"},{"lng":"107.060415","lat":"29.86047"},{"lng":"107.061665","lat":"29.864841"},{"lng":"107.054919","lat":"29.881592"},{"lng":"107.052508","lat":"29.894119"},{"lng":"107.040451","lat":"29.898403"},{"lng":"107.028579","lat":"29.89536"},{"lng":"107.014946","lat":"29.895825"},{"lng":"106.997804","lat":"29.89278"}]'; $max_path_arr = json_decode($max_path_json, true); // 初始化变量 $enterByHour = array_fill(0, 24, 0); $outByHour = array_fill(0, 24, 0); // 先从 vehicle_park_line_log 表查询(如果已有数据) // 注意:vehicle_park_line_log 表的 is_del 字段可能不存在或值不同,所以不添加 is_del 条件 $info['enter_count'] = ParkLineLog::whereTime('create_time', 'd')->where('type', 1)->where('is_del', 1)->count(); $info['out_count'] = ParkLineLog::whereTime('create_time', 'd')->where('type', 2)->where('is_del', 1)->count(); // 如果 vehicle_park_line_log 表没有数据,从轨迹数据实时统计 if ($info['enter_count'] == 0 && $info['out_count'] == 0) { $enterCount = 0; $outCount = 0; // 查询当天的轨迹数据 $todayStart = date('Y-m-d 00:00:00'); $todayEnd = date('Y-m-d 23:59:59'); $vehicleLines = Db::name('vehicle_line') ->where('create_time', '>=', $todayStart) ->where('create_time', '<=', $todayEnd) ->where('is_del', 1) ->field('vehicleNo,line') ->select(); if (empty($vehicleLines)) { // 如果没有轨迹数据,返回0 $info['enter_count'] = 0; $info['out_count'] = 0; } else { foreach ($vehicleLines as $lineRecord) { $lineData = json_decode($lineRecord['line'], true); if (!is_array($lineData) || empty($lineData)) { continue; } // 遍历轨迹点,判断进出状态(允许同一辆车多次进出) foreach ($lineData as $o => $point) { if (empty($point['coordinate']) || empty($point['create_time'])) { continue; } // 解析坐标 $coords = explode(',', $point['coordinate']); if (count($coords) < 2) { continue; } $point_coord = [ 'lng' => floatval($coords[0]), 'lat' => floatval($coords[1]) ]; // 判断当前点是否在园区内 $vehicle_exist = $this->is_point_in_polygon($point_coord, $max_path_arr); // 判断上一个点是否在园区内 $up_exist = false; if ($o > 0 && isset($lineData[$o-1]['coordinate'])) { $up_coords = explode(',', $lineData[$o-1]['coordinate']); if (count($up_coords) >= 2) { $up_point = [ 'lng' => floatval($up_coords[0]), 'lat' => floatval($up_coords[1]) ]; $up_exist = $this->is_point_in_polygon($up_point, $max_path_arr); } } elseif ($o == 0) { // 第一个点,假设上一个点在园区外 $up_exist = false; } // 判断是否发生进出变化 if ($vehicle_exist && !$up_exist) { // 从园区外进入园区内 - 进园 $enterCount++; // 统计该小时 $pointTime = strtotime($point['create_time']); if ($pointTime !== false) { $hour = (int)date('H', $pointTime); if ($hour >= 0 && $hour < 24) { $enterByHour[$hour]++; } } } elseif (!$vehicle_exist && $up_exist) { // 从园区内离开到园区外 - 出园 $outCount++; // 统计该小时 $pointTime = strtotime($point['create_time']); if ($pointTime !== false) { $hour = (int)date('H', $pointTime); if ($hour >= 0 && $hour < 24) { $outByHour[$hour]++; } } } } } $info['enter_count'] = $enterCount; $info['out_count'] = $outCount; } } // 统计每小时进出园次数 $info['park'] = []; $time_arr = []; for ($x = 0; $x < 24; $x++) { $time_arr[$x]['s_time'] = date('Y-m-d ') . $x . ':00:00'; $time_arr[$x]['e_time'] = date('Y-m-d ') . $x . ':59:59'; $info['park']['time'][] = $x; } // 如果 vehicle_park_line_log 表有数据,从表查询 if ($info['enter_count'] > 0 || $info['out_count'] > 0) { foreach ($time_arr as $k => $v) { $enter = ParkLineLog::where('create_time', '>=', $v['s_time']) ->where('create_time', '<=', $v['e_time']) ->where('type', 1) ->where('is_del', 1) ->count(); $out = ParkLineLog::where('create_time', '>=', $v['s_time']) ->where('create_time', '<=', $v['e_time']) ->where('type', 2) ->where('is_del', 1) ->count(); $info['park']['enter'][] = $enter; $info['park']['out'][] = $out; } } else { // 如果表没有数据,使用从轨迹数据统计的结果 foreach ($time_arr as $k => $v) { $info['park']['enter'][] = isset($enterByHour[$k]) ? $enterByHour[$k] : 0; $info['park']['out'][] = isset($outByHour[$k]) ? $outByHour[$k] : 0; } } //片区车流统计 - 统计每个企业区域(region_type=6)内的车辆数量 $info['area'] = $this->getAreaVehicleStatistics(); //道路情况 - 统计每条道路上的真实车辆数量 $info['road_count'] = $this->getRoadVehicleStatistics(); // //园区停止车辆数 // $info['stop_count'] = Vehicle::where($map)->where('vec1', 0)->count(); // //园区行驶车辆数 // $info['driving_count'] = Vehicle::where($map)->where('vec1', '>', 0)->count(); // 园区内行驶停止车辆统计 - 使用轨迹数据统计每个小时的车辆数量 // 获取园区围栏坐标(使用与 vehicle_log 相同的围栏) $max_path_json = '[{"lng":"107.01527","lat":"29.895601"},{"lng":"106.997876","lat":"29.89281"},{"lng":"106.991391","lat":"29.887939"},{"lng":"106.985853","lat":"29.880405"},{"lng":"106.981835","lat":"29.871026"},{"lng":"106.960543","lat":"29.849142"},{"lng":"106.950737","lat":"29.83564"},{"lng":"106.945862","lat":"29.824089"},{"lng":"106.943563","lat":"29.814148"},{"lng":"106.945054","lat":"29.799754"},{"lng":"106.942764","lat":"29.783607"},{"lng":"106.970332","lat":"29.766519"},{"lng":"106.983273","lat":"29.751322"},{"lng":"107.017612","lat":"29.737776"},{"lng":"107.050425","lat":"29.734115"},{"lng":"107.064458","lat":"29.737683"},{"lng":"107.064744","lat":"29.739318"},{"lng":"107.058942","lat":"29.748447"},{"lng":"107.055205","lat":"29.762378"},{"lng":"107.071581","lat":"29.77504"},{"lng":"107.086248","lat":"29.789923"},{"lng":"107.088111","lat":"29.796775"},{"lng":"107.084106","lat":"29.807387"},{"lng":"107.080959","lat":"29.81142"},{"lng":"107.068298","lat":"29.813474"},{"lng":"107.06422","lat":"29.813406"},{"lng":"107.060435","lat":"29.82112"},{"lng":"107.050628","lat":"29.827207"},{"lng":"107.04726","lat":"29.828623"},{"lng":"107.046543","lat":"29.829966"},{"lng":"107.04701","lat":"29.830878"},{"lng":"107.04712","lat":"29.834263"},{"lng":"107.044827","lat":"29.838826"},{"lng":"107.053757","lat":"29.842875"},{"lng":"107.056479","lat":"29.849047"},{"lng":"107.057124","lat":"29.85153"},{"lng":"107.056802","lat":"29.853468"},{"lng":"107.060415","lat":"29.86047"},{"lng":"107.061665","lat":"29.864841"},{"lng":"107.054919","lat":"29.881592"},{"lng":"107.052508","lat":"29.894119"},{"lng":"107.040451","lat":"29.898403"},{"lng":"107.028579","lat":"29.89536"},{"lng":"107.014946","lat":"29.895825"},{"lng":"106.997804","lat":"29.89278"}]'; $max_path_arr = json_decode($max_path_json, true); // 获取当前时间 $currentTime = time(); $currentHour = (int)date('H', $currentTime); $todayDate = date('Y-m-d', $currentTime); // 初始化今天24小时的数据数组(00:00-23:59) $fullStats = []; $fullStats2 = []; for ($i = 0; $i < 24; $i++) { $hourTimeShort = sprintf('%02d:00', $i); $fullStats[] = [ 'hour_time' => $hourTimeShort, 'order_count' => 0 ]; $fullStats2[] = [ 'hour_time' => $hourTimeShort, 'order_count' => 0 ]; } // 查询当天的轨迹数据 $todayStart = date('Y-m-d 00:00:00'); $todayEnd = date('Y-m-d 23:59:59'); $vehicleLines = Db::name('vehicle_line') ->where('create_time', '>=', $todayStart) ->where('create_time', '<=', $todayEnd) ->where('is_del', 1) ->field('vehicleNo,line') ->select(); // 批量获取所有车辆的速度信息(用于旧数据备选,如果轨迹点没有保存速度) $allVehicleNos = []; foreach ($vehicleLines as $lineRecord) { $allVehicleNos[] = $lineRecord['vehicleNo']; } $allVehicleNos = array_unique($allVehicleNos); $vehicleSpeedMap = []; if (!empty($allVehicleNos)) { $vehicles = Db::name('vehicle') ->where('vehicleNo', 'in', $allVehicleNos) ->where($map) ->field('vehicleNo,vec1') ->select(); foreach ($vehicles as $v) { $vehicleSpeedMap[$v['vehicleNo']] = $v['vec1']; } } // 统计每个小时的车辆数量(使用集合去重,避免同一车辆在同一小时被重复计算) $hourVehicleMap = []; // [小时索引 => [vehicleNo => true]] $speedStats = ['has_speed_in_point' => 0, 'no_speed_in_point' => 0, 'driving_count' => 0, 'stop_count' => 0]; foreach ($vehicleLines as $lineRecord) { $lineData = json_decode($lineRecord['line'], true); if (!is_array($lineData) || empty($lineData)) { continue; } $vehicleNo = $lineRecord['vehicleNo']; $defaultSpeed = isset($vehicleSpeedMap[$vehicleNo]) ? $vehicleSpeedMap[$vehicleNo] : 0; // 遍历轨迹点,按小时统计 foreach ($lineData as $point) { if (empty($point['coordinate']) || empty($point['create_time'])) { continue; } // 解析坐标 $coords = explode(',', $point['coordinate']); if (count($coords) < 2) { continue; } $point_coord = [ 'lng' => floatval($coords[0]), 'lat' => floatval($coords[1]) ]; // 判断是否在园区内 if (!$this->is_point_in_polygon($point_coord, $max_path_arr)) { continue; } // 获取轨迹点的时间 $pointTime = strtotime($point['create_time']); if ($pointTime === false) { continue; } // 计算轨迹点属于哪个小时(今天的小时) $pointHour = (int)date('H', $pointTime); $pointDate = date('Y-m-d', $pointTime); // 只统计今天的数据 if ($pointDate !== $todayDate) { continue; } // 直接使用小时作为索引(0-23) $index = $pointHour; // 初始化小时数据 if (!isset($hourVehicleMap[$index])) { $hourVehicleMap[$index] = ['stop' => [], 'driving' => []]; } // 优先使用轨迹点保存的速度信息 // 如果轨迹点没有speed字段(旧数据),跳过该点,不进行统计 // 因为使用车辆当前速度来判断历史轨迹点是不准确的 if (!isset($point['speed'])) { // 旧数据没有speed字段,跳过统计 $speedStats['no_speed_in_point']++; continue; } $pointSpeed = floatval($point['speed']); $speedStats['has_speed_in_point']++; // 根据速度判断是停止还是行驶(速度大于0.1km/h视为行驶,避免浮点数精度问题) // 使用集合去重,同一车辆在同一小时只统计一次 if ($pointSpeed > 0.1) { $hourVehicleMap[$index]['driving'][$vehicleNo] = true; $speedStats['driving_count']++; } else { $hourVehicleMap[$index]['stop'][$vehicleNo] = true; $speedStats['stop_count']++; } } } // 调试信息(可以注释掉,或记录到日志) // \think\Log::info('车辆行驶停止统计调试信息: ' . json_encode($speedStats, JSON_UNESCAPED_UNICODE)); // 将统计结果填充到数组中(24小时) for ($i = 0; $i < 24; $i++) { if (isset($hourVehicleMap[$i])) { $fullStats[$i]['order_count'] = count($hourVehicleMap[$i]['stop']); $fullStats2[$i]['order_count'] = count($hourVehicleMap[$i]['driving']); } } // 只返回过去12小时的数据(用于前端显示) // 从当前小时往前推12小时 $resultStats = []; $resultStats2 = []; for ($i = 11; $i >= 0; $i--) { $targetTime = $currentTime - $i * 3600; $targetHour = (int)date('H', $targetTime); $hourTimeShort = date('H:00', $targetTime); $resultStats[] = [ 'hour_time' => $hourTimeShort, 'order_count' => isset($fullStats[$targetHour]) ? $fullStats[$targetHour]['order_count'] : 0 ]; $resultStats2[] = [ 'hour_time' => $hourTimeShort, 'order_count' => isset($fullStats2[$targetHour]) ? $fullStats2[$targetHour]['order_count'] : 0 ]; } $info['stop_count'] = $resultStats; $info['driving_count'] = $resultStats2; // 园区内车辆重载和空载 $info['heavy'] = []; $info['empty'] = []; $this->success('请求成功!', $info); } //大屏-运单 public function waybill_trend() { //运单周数量 // 获取当前日期(包含时间,用于精确查询) $endDate = date('Y-m-d 23:59:59'); // 计算7天前的日期(从当天0点开始) $startDate = date('Y-m-d 00:00:00', strtotime('-6 days')); // 查询实际数据(只统计未删除的运单) $map_waybill['is_del'] = 1; $actualStats = Db::name('waybill') ->where($map_waybill) ->where('create_time', '>=', $startDate) ->where('create_time', '<=', $endDate) ->field("DATE_FORMAT(create_time, '%Y-%m-%d') as date, COUNT(*) as count") ->group("DATE_FORMAT(create_time, '%Y-%m-%d')") ->select(); // 转换为以日期为键的数组方便查找 $week = ['周日','周一','周二','周三','周四','周五','周六']; $dailyData = []; foreach ($actualStats as $stat) { $weekdayNum = date('w', strtotime($stat['date'])); // 0-6表示周日到周六 $dailyData[$stat['date']] = [ 'date' => $stat['date'], 'weekday' => $week[$weekdayNum], // 转换为中文星期 'count' => intval($stat['count']), ]; } // 构建完整的7天数据(包含空日期) $fullWeekStats = []; for ($i = 6; $i >= 0; $i--) { $currentDate = date('Y-m-d', strtotime("-{$i} days")); $weekdayNum = date('w', strtotime($currentDate)); // 0-6表示周日到周六 $weekday = $week[$weekdayNum]; if (isset($dailyData[$currentDate])) { // 有数据的日期 $fullWeekStats[] = $dailyData[$currentDate]; } else { // 无数据的日期补零 $fullWeekStats[] = [ 'date' => $currentDate, 'weekday' => $weekday, 'count' => 0, ]; } } $info['waybill'] = $fullWeekStats; //企业货物前10 - 按装货单位统计货物总量 $map_enterprise['is_del'] = 1; $map_enterprise['waybill_loading'] = ['<>', '']; // 装货单位不为空 // 按装货单位分组,统计每个企业的货物总量(cargocount的SUM) $enterpriseList = Db::name('waybill') ->where($map_enterprise) ->field('waybill_loading, SUM(cargocount) as total_count') ->group('waybill_loading') ->order('total_count desc') ->limit(10) ->select(); $enterpriseNames = []; $enterpriseNums = []; if ($enterpriseList) { foreach ($enterpriseList as $item) { $enterpriseNames[] = $item['waybill_loading'] ? $item['waybill_loading'] : '未知企业'; $enterpriseNums[] = floatval($item['total_count']); // 货物总量(吨) } } // 如果数据不足10条,用空数据补齐(前端需要固定10条) $count = count($enterpriseNames); if ($count < 10) { for ($i = $count; $i < 10; $i++) { $enterpriseNames[] = ''; $enterpriseNums[] = 0; } } $info['enterprise'] = [ 'name' => $enterpriseNames, 'num' => $enterpriseNums ]; //当日片区货物输入 - 统计装货的运单 $info['in_area'] = $this->getAreaCargoStatistics('in'); //当日片区货物输出 - 统计卸货的运单 $info['out_area'] = $this->getAreaCargoStatistics('out'); $this->success('请求成功!', $info); } //大屏-未查验运单列表 public function uncheck_waybill_list() { $limit = input('limit', 20); // 默认20条 $map['is_del'] = 1; // 未删除 $map['check_status'] = 0; // 未查验状态 $field = 'id,waybill_order,tow_license,sale_product,waybill_loading,waybill_unloading,waybill_date,create_time'; $list = Waybill::where($map) ->field($field) ->order('create_time desc') ->limit($limit) ->select(); $result = []; if ($list) { foreach ($list as $k => $v) { // 转换为数组格式 $item = [ 'id' => $v['id'], 'waybill_order' => $v['waybill_order'] ? $v['waybill_order'] : '', 'tow_license' => $v['tow_license'] ? $v['tow_license'] : '', 'sale_product' => $v['sale_product'] ? $v['sale_product'] : '', 'waybill_loading' => $v['waybill_loading'] ? $v['waybill_loading'] : '', 'waybill_unloading' => $v['waybill_unloading'] ? $v['waybill_unloading'] : '', ]; // 格式化时间 if ($v['waybill_date']) { $item['waybill_date'] = date('Y-m-d H:i:s', strtotime($v['waybill_date'])); } else { $item['waybill_date'] = $v['create_time'] ? date('Y-m-d H:i:s', strtotime($v['create_time'])) : ''; } $result[] = $item; } } $this->success('请求成功!', $result); } //大屏-每日企业车辆进出记录 public function enterprise_vehicle($param) { if (empty($param['id'])) { $this->error('异常操作!'); } $data['vehicle_info'] = Db::name('perimeter')->where('id', $param['id'])->field('stop_vehicle,vehicle_count,enter_vehicle,out_vehicle')->find(); $this->error('请求成功!', $data); } //大屏-车辆报警列表 public function vehicle_alarm_list() { $page = input('page', 1); $limit = input('limit', 10); if (input('key')) { $map['name'] = array('like', "%" . input('key') . "%"); } //获取车牌号 - 支持通过车辆ID或车牌号查询 $map['is_del'] = array('neq', -1); $vehicleNo = input('vehicleNo'); if (is_numeric($vehicleNo)) { // 如果是数字,当作车辆ID处理 $map['license'] = Vehicle::where('id', $vehicleNo)->value('license'); } else { // 如果是字符串,当作车牌号处理 $map['license'] = $vehicleNo; } $list = Alarm::where($map)->order('create_time desc')->page($page)->limit($limit)->select(); $count = Alarm::where($map)->count(); $data['pages'] = ceil($count / $limit); $data['list'] = $list; $this->success('请求成功!', $data); } //大屏按钮-车辆详情 public function vehicle_detail() { $param = input(); if (empty($param['id'])) { $this->error('异常操作!'); } $field = 'vehicleNo,plateColor,ownerName,businessScopeName,transdCertificateCode,certificateEffdate,certificateExpdate,licenseIssueOrganCode'; // 支持通过车辆ID或车牌号查询 if (is_numeric($param['id'])) { // 如果是数字,当作车辆ID处理 $vehicle_info = Vehicle::where('id', $param['id'])->field($field)->find(); } else { // 如果是字符串,当作车牌号处理,先查询车辆ID $vehicleId = Vehicle::where('vehicleNo', $param['id'])->value('id'); if (empty($vehicleId)) { $this->error('未找到该车辆信息!'); } $vehicle_info = Vehicle::where('id', $vehicleId)->field($field)->find(); } $url = 'http://47.108.219.88:5000/api/httpserver/vehicleInfo?vehicleNo=' . rawurlencode($vehicle_info['vehicleNo']) . '&plateColor=' . $vehicle_info['plateColor']; $header = array(); $ch = curl_init(); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $output = curl_exec($ch); curl_close($ch); $json = json_decode($output, true); if (empty($json['body'])) { $list = [ 'vehicleNo' => '', 'ownerName' => '', 'licenseIssueOrganCode' => '', 'transCertificateCode' => '', 'transCertificateWord' => '', 'businessScopeName' => '', 'certificateEffdate' => '', 'certificateExpdate' => '', ]; } else { $list = $json['body']; } $vehicle_info = array_merge($vehicle_info, $list); $this->success('操作成功!', $vehicle_info); } //大屏按钮-车辆入园记录列表 public function vehicle_park_list() { // 支持通过车辆ID或车牌号查询 $vehicleNoInput = input('vehicleNo'); if (is_numeric($vehicleNoInput)) { // 如果是数字,当作车辆ID处理 $param['vehicleNo'] = Vehicle::where('id', $vehicleNoInput)->value('vehicleNo'); } else { // 如果是字符串,当作车牌号处理 $param['vehicleNo'] = $vehicleNoInput; } if (empty($param['vehicleNo'])) { $this->error('异常操作!'); } $page = input('page', 1); $limit = input('limit', 10); $map['is_del'] = array('neq', -1); $map['tractor_license'] = $param['vehicleNo']; $field = 'tractor_license,mission,status,create_time'; $list = Park::where($map)->order('create_time desc')->field($field)->page($page)->limit($limit)->select(); $count = Park::where($map)->count(); $data['pages'] = ceil($count / $limit); $data['list'] = $list; $this->success('请求成功!', $data); } //大屏按钮-车辆运单记录列表 public function vehicle_waybill_list() { // 支持通过车辆ID或车牌号查询 $vehicleNoInput = input('vehicleNo'); if (is_numeric($vehicleNoInput)) { // 如果是数字,当作车辆ID处理 $param['vehicleNo'] = Vehicle::where('id', $vehicleNoInput)->value('vehicleNo'); } else { // 如果是字符串,当作车牌号处理 $param['vehicleNo'] = $vehicleNoInput; } if (empty($param['vehicleNo'])) { $this->error('异常操作!'); } $page = input('page', 1); $limit = input('limit', 10); if (isset($param['mission']) && !empty($param['mission'])) { $map['w_mission'] = $param['mission']; } $map['is_del'] = array('neq', -1); $map['tow_license'] = $param['vehicleNo']; $list = Waybill::where($map)->order('create_time desc')->page($page)->limit($limit)->select(); $count = Waybill::where($map)->count(); if ($list) { foreach ($list as $k => &$v) { $v->check_name = ''; $v->check_status_record = ''; $record_list = EnterpriseCheck::where('waybill_id', $v['id'])->field('check_uid,check_status')->find(); if ($record_list) { $v->check_name = Perimeter::where('id', $record_list['check_uid'])->value('name'); $v->check_status_record = $record_list['check_status']; } } } $data['pages'] = ceil($count / $limit); $data['list'] = $list; $this->success('请求成功!', $data); } //大屏按钮-车辆行驶轨迹 public function vehicle_history() { $param = input(); // 支持通过车辆ID或车牌号查询 $vehicleNoInput = input('vehicleNo'); if (is_numeric($vehicleNoInput)) { // 如果是数字,当作车辆ID处理 $vehicleNo = Vehicle::where('id', $vehicleNoInput)->value('vehicleNo'); } else { // 如果是字符串,当作车牌号处理 $vehicleNo = $vehicleNoInput; } if (empty($vehicleNo)) { $this->error('异常操作!'); } // 处理日期参数:如果提供了 reservation(日期),使用该日期;否则使用今天 $reservation = input('reservation', ''); if ($reservation) { // 验证日期格式 $date = strtotime($reservation); if ($date === false) { $this->error('日期格式错误!'); } $dateStr = date('Y-m-d', $date); } else { $dateStr = date('Y-m-d'); } // 查询指定日期的轨迹数据 $table = 'vehicle_log'; $map['vehicleNo'] = $vehicleNo; // 添加日期过滤:查询指定日期的数据 // vehicle_log 表使用 positionTime 或 V_carettime 字段存储时间 $dateStart = $dateStr . ' 00:00:00'; $dateEnd = $dateStr . ' 23:59:59'; // 查询指定日期的轨迹数据 // 优先使用 positionTime 字段,如果为空则使用 V_carettime 字段 $list = Db::name($table) ->where($map) ->where(function($query) use ($dateStart, $dateEnd) { // 使用 positionTime 字段进行日期过滤(优先) $query->where('positionTime', '>=', $dateStart) ->where('positionTime', '<=', $dateEnd); }) ->whereOr(function($query) use ($map, $dateStart, $dateEnd) { // 如果 positionTime 为空或无效,使用 V_carettime 字段 $query->where($map) ->where(function($q) { $q->where('positionTime', '') ->whereOr('positionTime', 'null') ->whereOr('positionTime', null); }) ->where('V_carettime', '>=', $dateStart) ->where('V_carettime', '<=', $dateEnd); }) ->field('longitude,latitude,positionTime,V_carettime') ->order('id asc') ->select(); // 如果还是没有结果,尝试只使用 V_carettime 字段(兼容旧数据) if (empty($list)) { $list = Db::name($table) ->where($map) ->where('V_carettime', '>=', $dateStart) ->where('V_carettime', '<=', $dateEnd) ->field('longitude,latitude') ->order('id asc') ->select(); } // 如果仍然没有结果,尝试查询 vehicle_line 表(轨迹数据可能存储在这里) if (empty($list)) { // vehicle_line 表存储的是 JSON 格式的轨迹数据 $lineRecord = Db::name('vehicle_line') ->where('vehicleNo', $vehicleNo) ->where('create_time', '>=', $dateStart) ->where('create_time', '<=', $dateEnd) ->where('is_del', 1) ->field('line') ->order('id asc') ->select(); if ($lineRecord && count($lineRecord) > 0) { // 解析 JSON 格式的轨迹数据 $list = []; foreach ($lineRecord as $record) { $lineData = json_decode($record['line'], true); if (is_array($lineData) && count($lineData) > 0) { foreach ($lineData as $point) { if (isset($point['coordinate']) && !empty($point['coordinate'])) { $coords = explode(',', $point['coordinate']); if (count($coords) >= 2) { $list[] = [ 'longitude' => floatval($coords[0]), 'latitude' => floatval($coords[1]) ]; } } } } } } } $coordinate = ''; if ($list && count($list) > 0) { foreach ($list as $k => $v) { if (empty($v['longitude']) || empty($v['latitude'])) { continue; } $str = $this->getWgs84($v['longitude'] . ',' . $v['latitude']); $coordinate .= $str['lon'] . ',' . $str['lat'] . ';'; } if (!empty($coordinate)) { $coordinate = substr($coordinate, 0, strlen($coordinate) - 1); } } // 如果没有查询到数据,返回默认值 if (empty($coordinate)) { $coordinate = '106.964108,29.793287';//默认初始值 } $data['coordinate'] = $coordinate; $this->success('请求成功!', $data); } //车辆码 一小时请求一次 public function getHourData() { //园区当月车辆进出列表 $month_arr = []; $m = date('m'); $y = date('Y'); $month = Date::days_in_month(date($m), date($y)); for ($i = 0; $i < $month; $i++) { // 格式化日期,确保是两位数:2025-11-01 而不是 2025-11-1 $day = str_pad($i + 1, 2, '0', STR_PAD_LEFT); $month_arr[$i]['month'] = $y . '-' . $m . '-' . $day; $month_arr[$i]['green_count'] = 0; $month_arr[$i]['yellow_count'] = 0; $month_arr[$i]['red_count'] = 0; } // 方案1:优先使用 vehicle_park_line_log 表(如果表存在且有数据) // 方案2:如果 vehicle_park_line_log 表没有数据,则使用 vehicle 表统计每天首次出现的车辆 foreach ($month_arr as $k => $v) { $dateStr = $v['month']; $dateStart = $dateStr . ' 00:00:00'; $dateEnd = $dateStr . ' 23:59:59'; // 先尝试从 vehicle_park_line_log 表查询 $park_log = ParkLineLog::where('is_del', 1) ->where('type', 1) ->whereTime('create_time', 'between', [$dateStart, $dateEnd]) ->field('vehicleNo,create_time') ->select(); if ($park_log && count($park_log) > 0) { // 使用 vehicle_park_line_log 表的数据 foreach ($park_log as $a => $b) { $code = Vehicle::where('vehicleNo', $b['vehicleNo']) ->where('is_del', 'in', '1,2') ->value('qr_color'); if ($code == 1) { $month_arr[$k]['green_count'] += 1; } elseif ($code == 2) { $month_arr[$k]['yellow_count'] += 1; } elseif ($code == 3) { $month_arr[$k]['red_count'] += 1; } } } else { // 如果 vehicle_park_line_log 表没有数据,使用 vehicle 表统计 // 统计每天首次出现的车辆(按车牌号去重) $vehicles = Db::name('vehicle') ->where('is_del', 'in', '1,2') ->where('create_time', '>=', $dateStart) ->where('create_time', '<=', $dateEnd) ->field('vehicleNo, qr_color') ->group('vehicleNo') ->select(); if ($vehicles) { foreach ($vehicles as $vehicle) { $code = $vehicle['qr_color']; if ($code == 1) { $month_arr[$k]['green_count'] += 1; } elseif ($code == 2) { $month_arr[$k]['yellow_count'] += 1; } elseif ($code == 3) { $month_arr[$k]['red_count'] += 1; } } } } } $info['month_arr'] = $month_arr;//车辆码 一个小时自动请求 //当日园区进入车辆数 $park_same_day_count = ParkLineLog::whereTime('create_time', 'd')->where(['is_del' => 1, 'type' => 1])->count(); //当日入园上报数 $apply_count = Park::whereTime('create_time', 'd')->where('status', 1)->count(); //当日运单上报数 $waybill_count = Waybill::whereTime('create_time', 'd')->where('is_del', 1)->count(); $report_count = $apply_count + $waybill_count; $info['park_same_day_count'] = $park_same_day_count; $info['report_count'] = $report_count; //当日入园未上报数 $info['no_report_count'] = $park_same_day_count - $report_count; $this->success('请求成功!', $info); } public function getWgs84($par) { $result = array(); // 转换后的结果 $tokens = preg_split('/[\r\n]+/', $par); foreach ($tokens as $token) { if (false !== strpos($token, '=')) { list($key, $value) = explode('=', $token, 2); $result[$key] = $value; } else $result[] = $token; } foreach ($result as $k => $v) { if ($v) { $Varr = explode(',', $v); $res = $this->bd_decrypt($Varr[0], $Varr[1]); $arr = [ 'lon' => number_format($res['gg_lon'], 6), 'lat' => number_format($res['gg_lat'], 6), ]; } } return $arr; } //BD-09(百度) 坐标转换成 GCJ-02(火星,高德) 坐标 //@param bd_lon 百度经度 //@param bd_lat 百度纬度 public function bd_decrypt($bd_lon, $bd_lat) { $x_pi = 3.14159265358979324 * 3000.0 / 180.0; $x = $bd_lon - 0.0065; $y = $bd_lat - 0.006; $z = sqrt($x * $x + $y * $y) - 0.00002 * sin($y * $x_pi); $theta = atan2($y, $x) - 0.000003 * cos($x * $x_pi); $data['gg_lon'] = $z * cos($theta); $data['gg_lat'] = $z * sin($theta); return $data; } /** * 统计每个片区的车辆数量 * @return array */ private function getAreaVehicleStatistics() { // 查询所有企业区域(region_type=6)作为片区 $areas = Perimeter::where('region_type', 6) ->where('is_del', 1) ->where('info', '<>', '') ->field('id,name,info') ->select(); if (empty($areas)) { // 如果没有配置片区,返回空数组 return []; } // 查询所有车辆 $vehicles = Vehicle::where('is_del', 'in', '1,2') ->field('id,vehicleNo,longitude,latitude') ->select(); $areaStats = []; // 遍历每个片区 foreach ($areas as $area) { // 解析片区坐标 $polygon = $this->parsePerimeterCoords($area['info']); if (empty($polygon)) { continue; } $vehicleCount = 0; // 统计在该片区内的车辆数量 foreach ($vehicles as $vehicle) { if (empty($vehicle['longitude']) || empty($vehicle['latitude'])) { continue; } $point = [ 'lng' => floatval($vehicle['longitude']), 'lat' => floatval($vehicle['latitude']) ]; if ($this->is_point_in_polygon($point, $polygon)) { $vehicleCount++; } } $areaStats[] = [ 'name' => $area['name'], 'value' => $vehicleCount ]; } // 如果没有统计数据,返回空数组 return $areaStats; } /** * 统计每条道路上的车辆数量 * @return array */ private function getRoadVehicleStatistics() { // 查询所有道路(region_type=5) $roads = Perimeter::where('region_type', 5) ->where('is_del', 1) ->where('info', '<>', '') ->field('id,name,info') ->select(); if (empty($roads)) { // 如果没有配置道路,返回空数组 return []; } // 查询所有车辆 $vehicles = Vehicle::where('is_del', 'in', '1,2') ->field('id,vehicleNo,longitude,latitude') ->select(); $roadStats = []; // 遍历每条道路 foreach ($roads as $road) { // 解析道路坐标 $polygon = $this->parsePerimeterCoords($road['info']); if (empty($polygon)) { // 如果没有坐标信息,返回0 $roadStats[] = [ 'id' => $road['id'], 'name' => $road['name'], 'count' => 0 ]; continue; } $vehicleCount = 0; // 统计在该道路上的车辆数量 foreach ($vehicles as $vehicle) { if (empty($vehicle['longitude']) || empty($vehicle['latitude'])) { continue; } $point = [ 'lng' => floatval($vehicle['longitude']), 'lat' => floatval($vehicle['latitude']) ]; if ($this->is_point_in_polygon($point, $polygon)) { $vehicleCount++; } } $roadStats[] = [ 'id' => $road['id'], 'name' => $road['name'], 'count' => $vehicleCount ]; } // 如果没有统计数据,返回空数组 return $roadStats; } /** * 统计片区货物输入/输出 * @param string $type 'in' 表示货物输入(装货),'out' 表示货物输出(卸货) * @return array */ private function getAreaCargoStatistics($type = 'in') { // 查询所有企业区域(region_type=6)作为片区 $areas = Perimeter::where('region_type', 6) ->where('is_del', 1) ->field('id,name') ->select(); if (empty($areas)) { // 如果没有配置片区,返回空数组 return []; } // 获取当天的日期(使用waybill_date字段,表示提货日期) $todayDate = date('Y-m-d'); // 根据类型确定查询字段和条件 if ($type === 'in') { // 货物输入:统计装货的运单 $enterpriseField = 'waybill_loading'; $missionCondition = '装货'; } else { // 货物输出:统计卸货的运单 $enterpriseField = 'waybill_unloading'; $missionCondition = '卸货'; } // 查询当日的运单数据(按提货日期统计) $waybillMap = [ 'is_del' => 1, 'w_mission' => $missionCondition, $enterpriseField => ['<>', ''], // 装货/卸货单位不为空 ]; $waybills = Db::name('waybill') ->where($waybillMap) ->whereTime('waybill_date', 'd') // 使用waybill_date字段,统计当天的运单 ->field("{$enterpriseField} as enterprise_name, SUM(cargocount) as total_count") ->group($enterpriseField) ->select(); // 构建企业名称到货物数量的映射 $enterpriseCargoMap = []; if ($waybills) { foreach ($waybills as $waybill) { $enterpriseName = $waybill['enterprise_name']; $enterpriseCargoMap[$enterpriseName] = floatval($waybill['total_count']); } } // 统计每个片区的货物总量 $areaStats = []; foreach ($areas as $area) { $areaName = $area['name']; $totalCargo = 0; // 遍历运单数据,匹配装货/卸货单位与片区名称 // 这里使用名称匹配,如果装货/卸货单位包含片区名称,则计入该片区 foreach ($enterpriseCargoMap as $enterpriseName => $cargoCount) { // 如果企业名称与片区名称匹配(完全匹配或包含关系) if ($enterpriseName == $areaName || strpos($enterpriseName, $areaName) !== false || strpos($areaName, $enterpriseName) !== false) { $totalCargo += $cargoCount; } } $areaStats[] = [ 'name' => $areaName, 'value' => round($totalCargo, 2) // 保留2位小数 ]; } // 如果没有统计数据,至少返回片区名称,值为0 if (empty($areaStats)) { foreach ($areas as $area) { $areaStats[] = [ 'name' => $area['name'], 'value' => 0 ]; } } return $areaStats; } /** * 解析周界坐标字符串为多边形数组 * @param string $info 坐标字符串,格式:lng,lat;lng,lat;... * @return array */ private function parsePerimeterCoords($info) { if (empty($info)) { return []; } $polygon = []; $points = explode(';', $info); foreach ($points as $point) { $coords = explode(',', $point); if (count($coords) >= 2) { $polygon[] = [ 'lng' => floatval(trim($coords[0])), 'lat' => floatval(trim($coords[1])) ]; } } return $polygon; } /** * 判断一个坐标是否在一个多边形内 * @param array $point 点坐标 ['lng' => x, 'lat' => y] * @param array $pts 多边形坐标数组 * @return bool */ private function is_point_in_polygon($point, $pts) { $N = count($pts); if ($N < 3) { return false; } $boundOrVertex = true; $intersectCount = 0; $precision = 2e-10; $p1 = 0; $p2 = 0; $p = $point; $p1 = $pts[0]; for ($i = 1; $i <= $N; ++$i) { if ($p['lng'] == $p1['lng'] && $p['lat'] == $p1['lat']) { return $boundOrVertex; } $p2 = $pts[$i % $N]; if ($p['lat'] < min($p1['lat'], $p2['lat']) || $p['lat'] > max($p1['lat'], $p2['lat'])) { $p1 = $p2; continue; } if ($p['lat'] > min($p1['lat'], $p2['lat']) && $p['lat'] < max($p1['lat'], $p2['lat'])) { if($p['lng'] <= max($p1['lng'], $p2['lng'])){ if ($p1['lat'] == $p2['lat'] && $p['lng'] >= min($p1['lng'], $p2['lng'])) { return $boundOrVertex; } if ($p1['lng'] == $p2['lng']) { if ($p1['lng'] == $p['lng']) { return $boundOrVertex; } else { ++$intersectCount; } } else { $xinters = ($p['lat'] - $p1['lat']) * ($p2['lng'] - $p1['lng']) / ($p2['lat'] - $p1['lat']) + $p1['lng']; if (abs($p['lng'] - $xinters) < $precision) { return $boundOrVertex; } if ($p['lng'] < $xinters) { ++$intersectCount; } } } } else { if ($p['lat'] == $p2['lat'] && $p['lng'] <= $p2['lng']) { $p3 = $pts[($i+1) % $N]; if ($p['lat'] >= min($p1['lat'], $p3['lat']) && $p['lat'] <= max($p1['lat'], $p3['lat'])) { ++$intersectCount; } else { $intersectCount += 2; } } } $p1 = $p2; } if ($intersectCount % 2 == 0) { return false; } else { return true; } } }