Compare commits

...

2 Commits

Author SHA1 Message Date
DESKTOP-4QOCBEF\SunHouse ea22ebf3bc no message 2 months ago
DESKTOP-4QOCBEF\SunHouse 87c9d48617 feat:全系統整合(無變換車道) 2 months ago
  1. 1
      .gitignore
  2. 485
      app/Class/Entry.php
  3. 215
      app/Class/ItlEntry.php
  4. 25
      app/Class/LineNotify.php
  5. 23
      app/Class/LogWriter.php
  6. 620
      app/Class/NewEntry.php
  7. 102
      app/Class/StaticData.php
  8. 39
      app/Console/Commands/EntryInterval.php
  9. 155
      app/Console/Kernel.php
  10. 52
      app/Events/ExampleEvent.php
  11. 38
      app/Exports/ArrayExport.php
  12. 147
      app/Exports/ArrayExportH.php
  13. 40
      app/Http/Controllers/Auth/ConfirmPasswordController.php
  14. 22
      app/Http/Controllers/Auth/ForgotPasswordController.php
  15. 59
      app/Http/Controllers/Auth/LoginController.php
  16. 73
      app/Http/Controllers/Auth/RegisterController.php
  17. 30
      app/Http/Controllers/Auth/ResetPasswordController.php
  18. 42
      app/Http/Controllers/Auth/VerificationController.php
  19. 72
      app/Http/Controllers/AuthController.php
  20. 28
      app/Http/Controllers/HomeController.php
  21. 126
      app/Http/Controllers/RoleController.php
  22. 141
      app/Http/Controllers/System/FileIndexController.php
  23. 1044
      app/Http/Controllers/System/IntervalController.php
  24. 425
      app/Http/Controllers/System/IntervalEquipmentController.php
  25. 200
      app/Http/Controllers/System/LogController.php
  26. 343
      app/Http/Controllers/System/Monitor/PingIpController.php
  27. 987
      app/Http/Controllers/System/MultisysController.php
  28. 264
      app/Http/Controllers/System/MultisysEquipmentController.php
  29. 897
      app/Http/Controllers/System/OverSpeedController.php
  30. 258
      app/Http/Controllers/System/OverSpeedRedEquipmentController.php
  31. 179
      app/Http/Controllers/System/Permissions/RoleController.php
  32. 255
      app/Http/Controllers/System/Permissions/UserController.php
  33. 811
      app/Http/Controllers/System/RedController.php
  34. 516
      app/Http/Controllers/System/StatisticsController.php
  35. 324
      app/Http/Controllers/System/SystemController.php
  36. 300
      app/Http/Controllers/System/TimeLogController.php
  37. 702
      app/Http/Controllers/System/ViolationParkingController.php
  38. 258
      app/Http/Controllers/System/ViolationParkingEquipmentController.php
  39. 131
      app/Http/Controllers/UserController.php
  40. 8
      app/Http/Kernel.php
  41. 30
      app/Http/Middleware/CheckUserApiStatus.php
  42. 29
      app/Http/Middleware/CheckUserStatus.php
  43. 41
      app/Http/Middleware/PermissionMiddleware.php
  44. 46
      app/Listeners/SuccessfulLogin.php
  45. 26
      app/Models/AssetOwnership.php
  46. 75
      app/Models/Clientlog.php
  47. 67
      app/Models/Clientlogsecond.php
  48. 25
      app/Models/CustodyUnit.php
  49. 62
      app/Models/EquipmentView.php
  50. 27
      app/Models/ExportFiles.php
  51. 166
      app/Models/Interval.php
  52. 21
      app/Models/IntervalEntry.php
  53. 99
      app/Models/IntervalEquipment.php
  54. 58
      app/Models/Intervaldis.php
  55. 70
      app/Models/Multisys.php
  56. 21
      app/Models/MultisysEntry.php
  57. 122
      app/Models/MultisysEquipment.php
  58. 69
      app/Models/OverSpeedRed.php
  59. 19
      app/Models/OverSpeedRedEntry.php
  60. 19
      app/Models/OverSpeedRedEntry2.php
  61. 131
      app/Models/OverSpeedRedEquipment.php
  62. 15
      app/Models/Permission.php
  63. 13
      app/Models/PingIp.php
  64. 16
      app/Models/Role.php
  65. 39
      app/Models/Serverlog.php
  66. 20
      app/Models/StatisticView.php
  67. 53
      app/Models/User.php
  68. 48
      app/Models/UserLog.php
  69. 33
      app/Models/ViolationLaw.php
  70. 66
      app/Models/ViolationParking.php
  71. 102
      app/Models/ViolationParkingEquipment.php
  72. 3
      app/Providers/EventServiceProvider.php
  73. 60
      app/Providers/RouteServiceProvider.php
  74. 69
      app/Repositories/ViolationParkingRepository.php
  75. 76
      app/Services/ViolationParkingDataService.php
  76. 108
      app/Services/ViolationParkingService.php
  77. 11
      composer.json
  78. 1528
      composer.lock
  79. 18
      config/app.php
  80. 5
      config/auth.php
  81. 4
      config/database.php
  82. 364
      config/excel.php
  83. 4
      config/hashing.php
  84. 21
      config/image.php
  85. 8
      config/logging.php
  86. 19
      config/mail.php
  87. 161
      config/permission.php
  88. 22
      config/sanctum.php
  89. 13
      config/session.php
  90. 8
      database/factories/UserFactory.php
  91. 32
      database/migrations/2014_10_12_100000_create_password_resets_table.php
  92. 145
      database/migrations/2023_10_11_035920_create_permission_tables.php
  93. 31
      database/migrations/2023_10_11_062424_add_api_token.php
  94. 52
      database/migrations/2023_10_16_062701_add_user_detail.php
  95. 34
      database/migrations/2023_10_16_081301_create_user_log_table.php
  96. 31
      database/migrations/2023_10_27_005420_add_renew_password_at_to_users_table.php
  97. 31
      database/migrations/2023_10_27_010709_create_sessions_table.php
  98. 33
      database/migrations/2023_11_07_055939_create_violation_law_table.php
  99. 44
      database/migrations/2023_11_07_061949_create_violationparking_table.php
  100. 41
      database/migrations/2023_11_07_064603_create_violationparking_equipment_table.php
  101. Some files were not shown because too many files have changed in this diff Show More

1
.gitignore vendored

@ -17,3 +17,4 @@ yarn-error.log @@ -17,3 +17,4 @@ yarn-error.log
/.fleet
/.idea
/.vscode
public/amcharts_4.10.38/

485
app/Class/Entry.php

@ -0,0 +1,485 @@ @@ -0,0 +1,485 @@
<?php
namespace App\Class;
use App\Models\Multisys;
use App\Models\MultisysEntry;
use App\Models\OverSpeedRed;
use App\Models\OverSpeedRedEntry;
use App\Models\OverSpeedRedEntry2;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Intervention\Image\Laravel\Facades\Image;
// 入案專用 class
class Entry
{
#region 路口多功能科技執法入案
public static function multisys_entry($days = 7)
{
$records = Multisys::query();
$records->where('processcheck', 1);
$records->where('postcheck', 0);
// $records->where('datatime', '>', Carbon::now('Asia/Taipei')->subDays($days));
$records->where('datatime', '>', '2024-06-01');
$data = $records->select('id as multisys_id', 'picture as photo')->get()->toArray();
$values = [];
foreach ($data as $row) {
unset($row['law_type']);
$values[] = '(\'' . implode('\',\'', $row) . '\')';
}
// dd($values);
if (count($values) > 0) {
DB::transaction(function () use ($values, $records) {
// 在這裡執行需要交易的資料庫操作,例如新增、修改、刪除等等
DB::insert('INSERT IGNORE INTO multisys_entry (multisys_id, photo) VALUES' . implode(',', $values));
$records->update(['postcheck' => 1]);
});
}
// DB::table('multisys_entry')->insertOrIgnore($data);
Log::notice('路口多功能入案-資料收集 近' . $days . '天,共' . count($values) . '筆');
}
// 取得要處理的資料
public static function get_multisys_entry_data($days = 1)
{
$ids = MultisysEntry::where('status', 0)->where('created_at', '>', Carbon::now('Asia/Taipei')->subDays($days))->pluck('multisys_id');
$data = Multisys::whereIn('id', $ids)->get();
// dd($data);
$values = [];
foreach ($data as $row) {
$datetime = $row->datatime;
$date = Carbon::parse($datetime)->toDateString();
$twYear = Carbon::parse($date)->format('Y') - 1911; // 取得民國年,即西元年減去1911
$twDate = $twYear . Carbon::parse($date)->format('md'); // 將民國年與日期合併
$time = Carbon::parse($datetime)->format('His');
$row_data = [
'datatime' => $row->datatime,
'SN' => $row->id,
'ViolationDate' => $twDate,
'ViolationTime' => $time,
'UnitId' => User::where('account', $row->jsoncheck)->first()->leader,
'PoliceName' => User::where('account', $row->jsoncheck)->first()->name,
'LicensePlate' => $row->carnumber,
'VehicleType' => $row->cartype,
'RuleId' => $row->violationcode,
'Road' => $row->location,
];
array_push($values, $row_data);
}
return $values;
}
// 本機建檔備存
public static function multisys_entry_local($data = [])
{
if (count($data) > 0) {
foreach ($data as $row_data) {
$datetime = $row_data['datatime'];
unset($row_data['datatime']);
// 存放檔案的目錄路徑
$directory = 'entry/twosage/' . Carbon::now('Asia/Taipei')->format('Ymd');
// 如果目錄不存在,則建立該目錄
Storage::makeDirectory($directory);
// 將資料轉換為 JSON 格式
$jsonData = json_encode($row_data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
// 將 JSON 資料寫入檔案
$row_date = Carbon::parse($datetime)->format('Ymd_His');
$fileName = 'Ts_' . $row_date . '_' . $row_data['SN'] . '.json';
Storage::put($directory . "/$fileName", $jsonData);
}
}
Log::notice('路口多功能入案-資料組合備存,共' . count($data) . '筆');
}
// 送出入案,紀錄回傳no
public static function multisys_entry_post($data = [])
{
if (count($data) > 0) {
foreach ($data as $row_data) {
unset($row_data['datatime']);
try {
$client = new Client(['base_uri' => 'http://trafficfine.typd.gov.tw:88']);
$response = $client->request('POST', '/X/sunhouse', ['form_params' => $row_data, 'connect_timeout' => 10]);
// Here the code for successful request
if ($response->getStatusCode() == "200") {
$resp = $response->getBody();
$no = json_decode($resp, true)['No'];
if(json_decode($resp, true)['Message'] == '新增完成'){
MultisysEntry::where('multisys_id', $row_data['SN'])->first()->update(['no' => $no, 'response' => $resp, 'status' => 1]);
}else{
MultisysEntry::where('multisys_id', $row_data['SN'])->first()->update(['no' => $no, 'response' => $resp, 'status' => 3]);
}
} else {
$resp = $response->getBody();
$no = json_decode($resp, true)['No'];
MultisysEntry::where('multisys_id', $row_data['SN'])->first()->update(['no' => $no, 'response' => $resp, 'status' => 3]);
}
} catch (\Exception $e) {
// There was another exception.
Log::error('multisys_id: ' . $row_data['SN']);
Log::error($e->getMessage());
}
}
}
Log::notice('路口多功能入案-資料送往入案系統,共' . count($data) . '筆');
}
// 送出佐證圖片
public static function multisys_entry_post_img()
{
$re_arr = [];
$data = MultisysEntry::where('status', 1)
->where('created_at', '>', Carbon::now('Asia/Taipei')->subDays(3))
// ->whereIn('multisys_id', $re_arr)
->get();
if (count($data) > 0) {
foreach ($data as $row_data) {
try {
// 使用 intervention/image 將檔案壓縮至500K以下
// 將圖片轉換為 base64 格式 不儲存
// Log::notice($row_data->photo);
$img = Image::read(public_path('ParsingFiles/' . str_replace('*', '/', $row_data->photo)));
// if ($width > $height) then width = 720, height = 720 * $height / $width
// if ($height > $width) then height = 480, width = 480 * $width / $height
$width = $img->width();
$height = $img->height();
if ($width > $height) {
$img->resize(2560, 2560 * $height / $width);
} else {
$img->resize(1440 * $width / $height, 1440);
}
$base64 = base64_encode($img->encode());
$post_data = [
'data' => $base64
];
// Log::notice($post_data);
$client = new Client(['base_uri' => 'http://trafficfine.typd.gov.tw:88']);
$response = $client->request('POST', '/' . $row_data->no, ['form_params' => $post_data, 'connect_timeout' => 10]);
// Here the code for successful request
if ($response->getStatusCode() == "200") {
$msg = json_decode($response->getBody(), true)['Message'];
if ($msg == '上傳成功') {
$row_data->status = 2;
$row_data->save();
Log::notice($row_data->no . "-" . $msg);
} else {
$row_data->status = 4;
$row_data->save();
Log::error($row_data->no . "-" . $msg);
}
} else {
$row_data->status = 3;
$row_data->save();
}
} catch (\Exception $e) {
// There was another exception.
Log::error('multisys_id: ' . $row_data->id);
Log::error($e->getMessage());
}
}
}
Log::notice('路口多功能入案-佐證資料送往入案系統,共' . count($data) . '筆');
}
#endregion
#region 闖紅燈超速入案
public static function overspeedred_entry($days = 7)
{
$records = OverSpeedRed::query();
$records->where('processcheck', 1);
$records->where('postcheck', 0);
// $records->where('datatime', '>', Carbon::now('Asia/Taipei')->subDays($days));
$records->where('datatime', '>', '2024-06-01');
$data = $records->select('id as overspeedred_id', 'picture as photo', 'violationtype', 'datatime')->get()->toArray();
// 嚴重超速
$records2 = OverSpeedRed::query();
$records2->where('processcheck', 1);
$records2->where('postcheck', 0);
// $records->where('datatime', '>', Carbon::now('Asia/Taipei')->subDays($days));
$records2->where('datatime', '>', '2024-06-01');
$records2->whereIn('violationcode', ['4310240', '4310241', '4310242']);
$data2 = $records2->select('id as overspeedred_id', 'picture as photo', 'violationtype', 'datatime')->get()->toArray();
$values = [];
foreach ($data as $row) {
// Log::notice($row);
unset($row['law_type']);
$violationtype = $row['violationtype'];
$time = str_replace('-', '', explode(' ', $row['datatime'])[0]);
$photoName = explode('*', $row['photo']);
$photoName = end($photoName);
$photoPath = "merge/$violationtype/$time/$photoName";
$row['photo'] = $photoPath;
unset($row['violationtype']);
unset($row['datatime']);
$values[] = '(\'' . implode('\',\'', $row) . '\')';
}
// 嚴重超速資料彙整
$values2 = [];
foreach ($data2 as $row) {
// Log::notice($row);
unset($row['law_type']);
$violationtype = $row['violationtype'];
$time = str_replace('-', '', explode(' ', $row['datatime'])[0]);
$photoName = explode('*', $row['photo']);
$photoName = end($photoName);
$photoPath = "merge/$violationtype/$time/$photoName";
$row['photo'] = $photoPath;
unset($row['violationtype']);
unset($row['datatime']);
$values2[] = '(\'' . implode('\',\'', $row) . '\')';
}
if (count($values) > 0) {
DB::transaction(function () use ($values, $values2, $records) {
// 在這裡執行需要交易的資料庫操作,例如新增、修改、刪除等等
DB::insert('INSERT IGNORE INTO overspeedred_entry (overspeedred_id, photo) VALUES' . implode(',', $values));
if (count($values2) > 0) {
DB::insert('INSERT IGNORE INTO overspeedred_entry2 (overspeedred_id, photo) VALUES' . implode(',', $values2));
}
$records->update(['postcheck' => 1]);
});
}
// DB::table('overspeedred_entry')->insertOrIgnore($data);
Log::notice('闖紅燈超速入案-資料收集 近' . $days . '天,共' . count($values) . '筆');
}
// 取得要處理的資料
public static function get_overspeedred_entry_data($days = 1)
{
$ids = OverSpeedRedEntry::where('status', 0)->where('created_at', '>', Carbon::now('Asia/Taipei')->subDays($days))->pluck('overspeedred_id');
$data = OverSpeedRed::whereIn('id', $ids)->get();
// dd($data);
$values = [];
foreach ($data as $row) {
$datetime = $row->datatime;
$date = Carbon::parse($datetime)->toDateString();
$twYear = Carbon::parse($date)->format('Y') - 1911; // 取得民國年,即西元年減去1911
$twDate = $twYear . Carbon::parse($date)->format('md'); // 將民國年與日期合併
$time = Carbon::parse($datetime)->format('His');
$row_data = [
'datatime' => $row->datatime,
'SN' => $row->id,
'ViolationDate' => $twDate,
'ViolationTime' => $time,
'UnitId' => User::where('account', $row->jsoncheck)->first()->leader,
'PoliceName' => User::where('account', $row->jsoncheck)->first()->name,
'LicensePlate' => $row->carnumber,
'VehicleType' => $row->cartype,
'RuleId' => $row->violationcode,
'Road' => $row->location,
];
if ($row->violationtype == '超速') {
$row_data['Speed'] = intval(str_replace('km/h', '', $row->speed));
$row_data['LimitSpeed'] = intval(str_replace('km/h', '', $row->limitspeed));
}
array_push($values, $row_data);
}
return $values;
}
// 本機建檔備存
public static function overspeedred_entry_local($data = [])
{
if (count($data) > 0) {
foreach ($data as $row_data) {
$datetime = $row_data['datatime'];
unset($row_data['datatime']);
// 存放檔案的目錄路徑
$directory = 'entry/overspeedred/' . Carbon::now('Asia/Taipei')->format('Ymd');
// 如果目錄不存在,則建立該目錄
Storage::makeDirectory($directory);
// 將資料轉換為 JSON 格式
$jsonData = json_encode($row_data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
// 將 JSON 資料寫入檔案
$row_date = Carbon::parse($datetime)->format('Ymd_His');
$fileName = 'OvO_' . $row_date . '_' . $row_data['SN'] . '.json';
Storage::put($directory . "/$fileName", $jsonData);
}
}
Log::notice('闖紅燈超速入案-資料組合備存,共' . count($data) . '筆');
}
// 送出入案,紀錄回傳no
public static function overspeedred_entry_post($data = [])
{
if (count($data) > 0) {
foreach ($data as $row_data) {
unset($row_data['datatime']);
try {
$client = new Client(['base_uri' => 'http://trafficfine.typd.gov.tw:88']);
$response = $client->request('POST', '/X/sunhouse', ['form_params' => $row_data, 'connect_timeout' => 10]);
// Here the code for successful request
if ($response->getStatusCode() == "200") {
$resp = $response->getBody();
$no = json_decode($resp, true)['No'];
if(json_decode($resp, true)['Message'] == '新增完成'){
OverSpeedRedEntry::where('overspeedred_id', $row_data['SN'])->first()->update(['no' => $no, 'response' => $resp, 'status' => 1]);
}
else{
OverSpeedRedEntry::where('overspeedred_id', $row_data['SN'])->first()->update(['no' => $no, 'response' => $resp, 'status' => 3]);
}
} else {
$resp = $response->getBody();
$no = json_decode($resp, true)['No'];
OverSpeedRedEntry::where('overspeedred_id', $row_data['SN'])->first()->update(['no' => $no, 'response' => $resp, 'status' => 3]);
}
} catch (\Exception $e) {
// There was another exception.
Log::error('overspeedred_id: ' . $row_data['SN']);
Log::error($e->getMessage());
}
// 如果 $row->violationcode 是 4310240 4310241 4310242 才執行的條件
if (in_array($row_data['RuleId'], ['4310240', '4310241', '4310242'])) {
$row_data['RuleId'] = '4340068';
try {
$client = new Client(['base_uri' => 'http://trafficfine.typd.gov.tw:88']);
$response = $client->request('POST', '/X/sunhouse', ['form_params' => $row_data, 'connect_timeout' => 10]);
// Here the code for successful request
if ($response->getStatusCode() == "200") {
$resp = $response->getBody();
$no = json_decode($resp, true)['No'];
if(json_decode($resp, true)['Message'] == '新增完成'){
OverSpeedRedEntry2::where('overspeedred_id', $row_data['SN'])->first()->update(['no' => $no, 'response' => $resp, 'status' => 1]);
}
else{
OverSpeedRedEntry2::where('overspeedred_id', $row_data['SN'])->first()->update(['no' => $no, 'response' => $resp, 'status' => 3]);
}
} else {
$resp = $response->getBody();
$no = json_decode($resp, true)['No'];
OverSpeedRedEntry2::where('overspeedred_id', $row_data['SN'])->first()->update(['no' => $no, 'response' => $resp, 'status' => 3]);
}
} catch (\Exception $e) {
// There was another exception.
Log::error('處車主 overspeedred_id: ' . $row_data['SN']);
Log::error($e->getMessage());
}
}
}
}
Log::notice('闖紅燈超速入案-資料送往入案系統,共' . count($data) . '筆');
}
// 送出佐證圖片
public static function overspeedred_entry_post_img()
{
$re_arr = [];
$data = OverSpeedRedEntry::where('status', 1)
->where('created_at', '>', Carbon::now('Asia/Taipei')->subDays(20))
// ->whereIn('overspeedred_id', $re_arr)
->get();
if (count($data) > 0) {
foreach ($data as $row_data) {
try {
// 使用 intervention/image 將檔案壓縮至500K以下
// 將圖片轉換為 base64 格式 不儲存
$img = Image::read(public_path(str_replace('*', '/', $row_data->photo)));
// if ($width > $height) then width = 720, height = 720 * $height / $width
// if ($height > $width) then height = 480, width = 480 * $width / $height
$width = $img->width();
$height = $img->height();
if ($width > $height) {
$img->resize(2560, 2560 * $height / $width);
} else {
$img->resize(1440 * $width / $height, 1440);
}
$base64 = base64_encode($img->encode());
$post_data = [
'data' => $base64
];
$client = new Client(['base_uri' => 'http://trafficfine.typd.gov.tw:88']);
$response = $client->request('POST', '/' . $row_data->no, ['form_params' => $post_data, 'connect_timeout' => 10]);
// Here the code for successful request
if ($response->getStatusCode() == "200") {
$msg = json_decode($response->getBody(), true)['Message'];
if ($msg == '上傳成功') {
$row_data->status = 2;
$row_data->save();
Log::notice($row_data->no . "-" . $msg);
} else {
$row_data->status = 4;
$row_data->save();
Log::error($row_data->no . "-" . $msg);
}
} else {
$row_data->status = 3;
$row_data->save();
}
} catch (\Exception $e) {
// There was another exception.
Log::error('overspeedred_id: ' . $row_data->id);
Log::error($e->getMessage());
}
}
}
$data2 = OverSpeedRedEntry2::where('status', 1)
->where('created_at', '>', Carbon::now('Asia/Taipei')->subDays(20))
// ->whereIn('overspeedred_id', $re_arr)
->get();
if (count($data2) > 0) {
foreach ($data2 as $row_data) {
try {
// 使用 intervention/image 將檔案壓縮至500K以下
// 將圖片轉換為 base64 格式 不儲存
$img = Image::read(public_path(str_replace('*', '/', $row_data->photo)));
// if ($width > $height) then width = 720, height = 720 * $height / $width
// if ($height > $width) then height = 480, width = 480 * $width / $height
$width = $img->width();
$height = $img->height();
if ($width > $height) {
$img->resize(2560, 2560 * $height / $width);
} else {
$img->resize(1440 * $width / $height, 1440);
}
$base64 = base64_encode($img->encode());
$post_data = [
'data' => $base64
];
$client = new Client(['base_uri' => 'http://trafficfine.typd.gov.tw:88']);
$response = $client->request('POST', '/' . $row_data->no, ['form_params' => $post_data, 'connect_timeout' => 10]);
// Here the code for successful request
if ($response->getStatusCode() == "200") {
$msg = json_decode($response->getBody(), true)['Message'];
if ($msg == '上傳成功') {
$row_data->status = 2;
$row_data->save();
Log::notice($row_data->no . "-" . $msg);
} else {
$row_data->status = 4;
$row_data->save();
Log::error($row_data->no . "-" . $msg);
}
} else {
$row_data->status = 3;
$row_data->save();
}
} catch (\Exception $e) {
// There was another exception.
Log::error('處車主 overspeedred_id: ' . $row_data->id);
Log::error($e->getMessage());
}
}
}
Log::notice('闖紅燈超速入案-佐證資料送往入案系統,共' . count($data) . '筆');
}
#endregion
}

215
app/Class/ItlEntry.php

@ -0,0 +1,215 @@ @@ -0,0 +1,215 @@
<?php
namespace App\Class;
use App\Models\Interval;
use App\Models\IntervalEntry;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\Laravel\Facades\Image;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
// 入案專用 class
class ItlEntry
{
#region 區間入案
public static function interval_entry($days = 7)
{
$records = Interval::query();
$records->where('processcheck', 1);
$records->where('postcheck', 0);
$records->whereNotNull('violationcode');
$records->whereNotNull('jsoncheck');
// $records->where('starttime', '>', Carbon::now('Asia/Taipei')->subDays($days));
$records->where(DB::Raw('date(start_time)'), '>=', '2025-01-01');
$data = $records->select('id as interval_id', 'merge_picture as photo')->get()->toArray();
// dd($data);
$values = [];
foreach ($data as $row) {
unset($row['law_type']);
$values[] = '(\'' . implode('\',\'', $row) . '\')';
}
// dd($values);
if (count($values) > 0) {
DB::transaction(function () use ($values, $records) {
// 在這裡執行需要交易的資料庫操作,例如新增、修改、刪除等等
DB::insert('INSERT IGNORE INTO interval_entry (interval_id, photo) VALUES' . implode(',', $values));
$records->update(['postcheck' => 1]);
});
}
Log::channel('entry')->notice('區間入案-資料收集 近' . $days . '天,共' . count($values) . '筆');
}
// 取得要處理的資料
public static function get_interval_entry_data($days = 1, $status = [0])
{
$ids = IntervalEntry::whereIn('status', $status)->where('created_at', '>', Carbon::now('Asia/Taipei')->subDays($days))->pluck('interval_id');
$data = Interval::whereIn('id', $ids)->get();
// Log::channel('entry')->notice($ids);
// Log::channel('entry')->notice($data);
$values = [];
foreach ($data as $row) {
$s_time = $row->start_time;
$s_date = Carbon::parse($s_time)->toDateString();
$s_twYear = Carbon::parse($s_date)->format('Y') - 1911; // 取得民國年,即西元年減去1911
$s_twDate = $s_twYear . Carbon::parse($s_date)->format('md'); // 將民國年與日期合併
$s_time = Carbon::parse($s_time)->format('His');
$e_time = $row->end_time;
$e_date = Carbon::parse($e_time)->toDateString();
$e_twYear = Carbon::parse($e_date)->format('Y') - 1911; // 取得民國年,即西元年減去1911
$e_twDate = $e_twYear . Carbon::parse($e_date)->format('md'); // 將民國年與日期合併
$e_time = Carbon::parse($e_time)->format('His');
$cartype = $row->cartype;
if ($row->cartype == '98') {
$cartype = 1;
} else if ($row->cartype == '99') {
$cartype = 3;
} else{
$cartype = $row->cartype;
}
$row_data = [
'datatime' => $row->start_time,
'SN' => $row->id,
'ViolationDate' => $e_twDate,
'ViolationTime' => $e_time,
'UnitID' => User::where('account', $row->jsoncheck)->first()->leader,
'PoliceName' => User::where('account', $row->jsoncheck)->first()->name,
'LicensePlate' => $row->carnumber,
'VehicleType' => $cartype,
'RuleId' => $row->violationcode,
'Road' => $row->location,
'Speed' => intval(floor($row->speed)),
'LimitSpeed' => intval(floor($row->limit_speed)),
'Distance' => $row->distance,
'Duration' => $row->diff,
'InDate' => $s_twDate,
'InTime' => $s_time,
'OutDate' => $e_twDate,
'OutTime' => $e_time,
];
array_push($values, $row_data);
}
return $values;
}
// 本機建檔備存
public static function interval_entry_local($data = [])
{
if (count($data) > 0) {
foreach ($data as $row_data) {
$datetime = $row_data['datatime'];
unset($row_data['datatime']);
// 存放檔案的目錄路徑
$directory = 'entry/interval/' . Carbon::now('Asia/Taipei')->format('Ymd');
// 如果目錄不存在,則建立該目錄
Storage::makeDirectory($directory);
// 將資料轉換為 JSON 格式
$jsonData = json_encode($row_data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
// 將 JSON 資料寫入檔案
$row_date = Carbon::parse($datetime)->format('Ymd_His');
$fileName = 'ITL_' . $row_date . '_' . $row_data['SN'] . '.json';
Storage::put($directory . "/$fileName", $jsonData);
}
}
Log::channel('entry')->notice('區間入案-資料組合備存,共' . count($data) . '筆');
}
// 送出入案,紀錄回傳no
public static function interval_entry_post($data = [])
{
if (count($data) > 0) {
foreach ($data as $row_data) {
$interval_id = $row_data['SN'];
$row_data['SN'] = 'ITL'.$row_data['SN'];
unset($row_data['datatime']);
try {
// Log::channel('entry')->notice($row_data);
$client = new Client(['base_uri' => 'http://trafficfine.typd.gov.tw:88']);
$response = $client->request('POST', '/X/sunhouse', ['form_params' => $row_data, 'connect_timeout' => 10]);
// Here the code for successful request
if ($response->getStatusCode() == "200") {
$resp = $response->getBody();
$no = json_decode($resp, true)['No'];
if(json_decode($resp, true)['Message'] == '新增完成'){
IntervalEntry::where('interval_id', $interval_id)->first()->update(['no' => $no, 'response' => $resp, 'status' => 1]);
}else{
IntervalEntry::where('interval_id', $interval_id)->first()->update(['no' => $no, 'response' => $resp, 'status' => 3]);
}
} else {
$resp = $response->getBody();
$no = json_decode($resp, true)['No'];
IntervalEntry::where('interval_id', $interval_id)->first()->update(['no' => $no, 'response' => $resp, 'status' => 3]);
}
} catch (\Exception $e) {
// There was another exception.
Log::error('interval_id: ' . $interval_id);
Log::error($e->getMessage());
}
}
}
Log::channel('entry')->notice('區間入案-資料送往入案系統,共' . count($data) . '筆');
}
// 送出佐證圖片
public static function interval_entry_post_img()
{
$re_arr = [];
$data = IntervalEntry::where('status', 1)
->where('created_at', '>', Carbon::now('Asia/Taipei')->subDays(10))
// ->whereIn('interval_id', $re_arr)
->get();
if (count($data) > 0) {
foreach ($data as $row_data) {
try {
// 使用 intervention/image 將檔案壓縮至500K以下
// 將圖片轉換為 base64 格式 不儲存
// Log::channel('entry')->notice($row_data->photo);
$img = Image::read(public_path('ParsingFiles/interval/' . str_replace('*', '/', $row_data->photo)));
// if ($width > $height) then width = 720, height = 720 * $height / $width
// if ($height > $width) then height = 480, width = 480 * $width / $height
$width = $img->width();
$height = $img->height();
if ($width > $height) {
$img->resize(1920, 1920 * $height / $width);
}
$base64 = base64_encode($img->encode());
$post_data = [
'data' => $base64
];
// Log::channel('entry')->notice($post_data);
$client = new Client(['base_uri' => 'http://trafficfine.typd.gov.tw:88']);
$response = $client->request('POST', '/' . $row_data->no, ['form_params' => $post_data, 'connect_timeout' => 10]);
// Here the code for successful request
if ($response->getStatusCode() == "200") {
$msg = json_decode($response->getBody(), true)['Message'];
if ($msg == '上傳成功') {
$row_data->status = 2;
$row_data->save();
Log::channel('entry')->notice($row_data->no . "-" . $msg);
} else {
$row_data->status = 4;
$row_data->save();
Log::error($row_data->no . "-" . $msg);
}
} else {
$row_data->status = 3;
$row_data->save();
}
} catch (\Exception $e) {
// There was another exception.
Log::error('interval_id(postimg): ' . $row_data->id);
Log::error($e->getMessage());
}
}
}
Log::channel('entry')->notice('區間入案-佐證資料送往入案系統,共' . count($data) . '筆');
}
#endregion
}

25
app/Class/LineNotify.php

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
<?php
namespace App\Class;
class LineNotify
{
public static function send($message)
{
$token = 'VAClbvaOmpmnIcmDrVVVkDLS51BWaZ6JQ7udePUSY8J';
$header = array(
'Content-Type: multipart/form-data',
'Authorization: Bearer ' . $token
);
$message = array(
'message' => $message
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_URL, 'https://notify-api.line.me/api/notify');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $message);
$result = curl_exec($ch);
curl_close($ch);
}
}

23
app/Class/LogWriter.php

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
<?php
namespace App\Class;
use App\Models\UserLog;
use Illuminate\Support\Str;
class LogWriter
{
public static function writeLog($data,$guard = null)
{
$log = new UserLog();
$log->user_id = auth($guard)->user()->id;
$log->user_name = auth($guard)->user()->name;
$log->action = $data['action'];
$log->action_detail = $data['action_detail'];
$log->ip = $data['ip'];
$log->remark = $data['remark'] ?? null;
$log->carnumber = $data['carnumber'] ?? null;
$log->location = $data['location'] ?? null;
$log->save();
}
}

620
app/Class/NewEntry.php

@ -0,0 +1,620 @@ @@ -0,0 +1,620 @@
<?php
namespace App\Classes;
use App\TwoStage;
use App\TwostageEntry;
use App\UpdateCarNumber;
use App\ViolationParkingEntry;
use App\IntervalDataBack;
use App\IntervalEntry;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\Facades\Image;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
// 入案專用 class
class NewEntry
{
#region 路口多功能科技執法入案
public static function twostage_entry($days = 7)
{
$records = TwoStage::query();
$records->where('processcheck', 1);
$records->where('postcheck', 0);
$records->whereNotNull('violationcode');
// $records->where('datatime', '>', Carbon::now('Asia/Taipei')->subDays($days));
$records->where('datatime', '>', '2025-01-15');
$data = $records->select('id as twostage_id', 'picture as photo')->get()->toArray();
$values = [];
foreach ($data as $row) {
$values[] = '(\'' . implode('\',\'', $row) . '\')';
}
// dd($values);
if (count($values) > 0) {
DB::transaction(function () use ($values, $records) {
// 在這裡執行需要交易的資料庫操作,例如新增、修改、刪除等等
DB::insert('INSERT IGNORE INTO twostage_entry (twostage_id, photo) VALUES' . implode(',', $values));
$records->update(['postcheck' => 1]);
});
}
// DB::table('twostage_entry')->insertOrIgnore($data);
Log::notice('路口多功能入案-資料收集 近' . $days . '天,共' . count($values) . '筆');
}
// 取得要處理的資料
public static function get_twostage_entry_data($days = 1, $status = [0])
{
$ids = TwostageEntry::whereIn('status', $status)
->where('created_at', '>', Carbon::now('Asia/Taipei')->subDays($days))
// ->where('created_at', '<', Carbon::now('Asia/Taipei')->subDays(2))
->pluck('twostage_id');
$data = Twostage::whereIn('id', $ids)->get();
// dd($data);
$values = [];
foreach ($data as $row) {
$datetime = $row->datatime;
$date = Carbon::parse($datetime)->toDateString();
$twYear = Carbon::parse($date)->format('Y') - 1911; // 取得民國年,即西元年減去1911
$twDate = $twYear . Carbon::parse($date)->format('md'); // 將民國年與日期合併
$time = Carbon::parse($datetime)->format('His');
$row_data = [
'datatime' => $row->datatime,
'SN' => $row->id,
'ViolationDate' => $twDate,
'ViolationTime' => $time,
// 'UnitId' => DB::table('operator')->where('account', $row->jsoncheck)->first()->leader,
'UnitId' => 'A000',
'PoliceName' => DB::table('operator')->where('account', $row->jsoncheck)->first()->name,
'LicensePlate' => $row->carnumber,
'VehicleType' => $row->cartype,
'RuleId' => $row->violationcode,
'Road' => $row->location,
];
// if (isset($row->violationmsg)) {
// $row_data['PunishFACT'] = $row->violationmsg;
// }
array_push($values, $row_data);
}
return $values;
}
// 本機建檔備存
public static function twostage_entry_local($data = [])
{
if (count($data) > 0) {
foreach ($data as $row_data) {
$datetime = $row_data['datatime'];
unset($row_data['datatime']);
// unset($row_data['photo']);
// 存放檔案的目錄路徑
$directory = 'entry/twosage/' . Carbon::now('Asia/Taipei')->format('Ymd');
// 如果目錄不存在,則建立該目錄
Storage::makeDirectory($directory);
// 將資料轉換為 JSON 格式
$jsonData = json_encode($row_data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
// 將 JSON 資料寫入檔案
$row_date = Carbon::parse($datetime)->format('Ymd_His');
$fileName = 'Ts_' . $row_date . '_' . $row_data['SN'] . '.json';
Storage::put($directory . "/$fileName", $jsonData);
}
}
Log::notice('路口多功能入案-資料組合備存,共' . count($data) . '筆');
}
// 送出入案,紀錄回傳no
public static function twostage_entry_post($data = [])
{
set_time_limit(0);
if (count($data) > 0) {
foreach ($data as $row_data) {
unset($row_data['datatime']);
$id = $row_data['SN'];
$row_data['SN'] = 'TS' . $row_data['SN'];
Log::notice($row_data);
try {
// $client = new Client(['base_uri' => 'http://220.128.232.102:5000']);
$response = $client->request('POST', '/HX/sunhouse', ['form_params' => $row_data, 'connect_timeout' => 10]);
// Here the code for successful request
if ($response->getStatusCode() == "200") {
$resp = $response->getBody();
$no = json_decode($resp, true)['No'];
if(json_decode($resp, true)['Message'] == '新增完成'){
TwostageEntry::where('twostage_id', $id)->first()->update(['no' => $no, 'response' => $resp, 'status' => 1]);
}else{
TwostageEntry::where('twostage_id', $id)->first()->update(['no' => $no, 'response' => $resp, 'status' => 3]);
}
} else {
$resp = $response->getBody();
$no = json_decode($resp, true)['No'];
TwostageEntry::where('twostage_id', $id)->first()->update(['no' => $no, 'response' => $resp, 'status' => 3]);
}
} catch (\Exception $e) {
// There was another exception.
Log::error('twostage_id: ' . $id);
Log::error($e->getMessage());
}
}
}
Log::notice('路口多功能入案-資料送往入案系統,共' . count($data) . '筆');
}
public static function twostage_entry_post_img()
{
$re_arr = [];
$data = TwostageEntry::where('status', 1)
->where('created_at', '>', Carbon::now('Asia/Taipei')->subDays(3))
// ->whereIn('multisys_id', $re_arr)
->get();
if (count($data) > 0) {
foreach ($data as $row_data) {
try {
// 取得圖片檔案路徑
$filePath = storage_path('app/ParsingFiles/' . str_replace('*', '/', $row_data['photo']));
// 使用 Intervention/Image 套件進行壓縮
$image = Image::make($filePath);
// 確認圖片寬度是否超過 1920
if ($image->getWidth() > 1920) {
// 計算等比例縮放後的寬度和高度
$width = 1920;
$height = round($image->getHeight() * $width / $image->getWidth());
// 縮放圖片
$image->resize($width, $height, function ($constraint) {
$constraint->aspectRatio();
});
}
// 壓縮圖片
$image->encode('jpg', 40);
// 將圖片轉換為 base64 編碼
$base64 = base64_encode($image->encoded);
$post_data = [
'data' => $base64
];
Log::notice($post_data);
// $client = new Client(['base_uri' => 'http://220.128.232.102:5000']);
$response = $client->request('POST', '/' . $row_data->no, ['form_params' => $post_data, 'connect_timeout' => 10]);
// Here the code for successful request
if ($response->getStatusCode() == "200") {
$msg = json_decode($response->getBody(), true)['Message'];
if ($msg == '上傳成功') {
$row_data->status = 2;
$row_data->save();
Log::notice($row_data->no . "-" . $msg);
} else {
$row_data->status = 4;
$row_data->save();
Log::error($row_data->no . "-" . $msg);
}
} else {
$row_data->status = 3;
$row_data->save();
}
} catch (\Exception $e) {
// There was another exception.
Log::error('twostage_id: ' . $row_data->id);
Log::error($e->getMessage());
}
}
}
Log::notice('路口多功能入案-佐證資料送往入案系統,共' . count($data) . '筆');
}
#endregion
#region 違規停車入案
public static function violationparking_entry($days = 7)
{
$records = UpdateCarNumber::query();
$records->where('processcheck', 1);
$records->where('postcheck', 0);
$records->whereNotNull('violationcode');
// $records->where('time', '>', Carbon::now('Asia/Taipei')->subDays($days));
$records->where('time', '>', '2025-01-15');
$data = $records->select('id as violationparking_id', 'picture as photo')->get()->toArray();
$values = [];
foreach ($data as $row) {
$values[] = '(\'' . implode('\',\'', $row) . '\')';
}
// dd($values);
if (count($values) > 0) {
DB::transaction(function () use ($values, $records) {
// 在這裡執行需要交易的資料庫操作,例如新增、修改、刪除等等
DB::insert('INSERT IGNORE INTO violationparking_entry (violationparking_id, photo) VALUES' . implode(',', $values));
$records->update(['postcheck' => 1]);
});
}
Log::notice('違停入案-資料收集 近' . $days . '天,共' . count($values) . '筆');
}
// 取得要處理的資料
public static function get_violationparking_entry_data($days = 1, $status = [0])
{
$ids = ViolationParkingEntry::whereIn('status', $status)->where('created_at', '>', Carbon::now('Asia/Taipei')->subDays($days))->pluck('violationparking_id');
$data = UpdateCarNumber::whereIn('id', $ids)->get();
// dd($data);
$values = [];
foreach ($data as $row) {
$datetime = $row->datatime;
$date = Carbon::parse($datetime)->toDateString();
$twYear = Carbon::parse($date)->format('Y') - 1911; // 取得民國年,即西元年減去1911
$twDate = $twYear . Carbon::parse($date)->format('md'); // 將民國年與日期合併
$time = Carbon::parse($datetime)->format('His');
$row_data = [
'datatime' => $row->time,
'SN' => $row->id,
'ViolationDate' => $twDate,
'ViolationTime' => $time,
// 'UnitId' => DB::table('operator')->where('account', $row->jsoncheck)->first()->leader,
'UnitId' => 'A000',
'PoliceName' => DB::table('operator')->where('account', $row->jsoncheck)->first()->name,
'LicensePlate' => $row->carnumber,
'VehicleType' => $row->cartype,
'RuleId' => $row->violationcode,
'Road' => $row->deviceplace,
];
array_push($values, $row_data);
}
return $values;
}
// 組合資料並建檔備存
public static function violationparking_entry_local($data = [])
{
if (count($data) > 0) {
foreach ($data as $row_data) {
$datetime = $row_data['datatime'];
unset($row_data['datatime']);
// 存放檔案的目錄路徑
$directory = 'entry/violationparking/' . Carbon::now('Asia/Taipei')->format('Ymd');
// 如果目錄不存在,則建立該目錄
Storage::makeDirectory($directory);
// 將資料轉換為 JSON 格式
$jsonData = json_encode($row_data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
// 將 JSON 資料寫入檔案
$row_date = Carbon::parse($datetime)->format('Ymd_His');
$fileName = 'Vpk_' . $row_date . '_' . $row_data['SN'] . '.json';
Storage::put($directory . "/$fileName", $jsonData);
}
}
Log::notice('違停入案-資料組合備存,共' . count($data) . '筆');
}
public static function violationparking_entry_post($data = [])
{
set_time_limit(0);
if (count($data) > 0) {
foreach ($data as $row_data) {
unset($row_data['datatime']);
$id = $row_data['SN'];
$row_data['SN'] = 'TS' . $row_data['SN'];
Log::notice($row_data);
try {
// $client = new Client(['base_uri' => 'http://220.128.232.102:5000']);
$response = $client->request('POST', '/HX/sunhouse', ['form_params' => $row_data, 'connect_timeout' => 10]);
// Here the code for successful request
if ($response->getStatusCode() == "200") {
$resp = $response->getBody();
$no = json_decode($resp, true)['No'];
if(json_decode($resp, true)['Message'] == '新增完成'){
ViolationParkingEntry::where('violationparking_id', $id)->first()->update(['no' => $no, 'response' => $resp, 'status' => 1]);
}else{
ViolationParkingEntry::where('violationparking_id', $id)->first()->update(['no' => $no, 'response' => $resp, 'status' => 3]);
}
} else {
$resp = $response->getBody();
$no = json_decode($resp, true)['No'];
ViolationParkingEntry::where('violationparking_id', $id)->first()->update(['no' => $no, 'response' => $resp, 'status' => 3]);
}
} catch (\Exception $e) {
// There was another exception.
Log::error('violationparking_id: ' . $id);
Log::error($e->getMessage());
}
}
}
Log::notice('違停入案-資料送往入案系統,共' . count($data) . '筆');
}
public static function violationparking_entry_post_img()
{
$re_arr = [];
$data = ViolationParkingEntry::where('status', 1)
->where('created_at', '>', Carbon::now('Asia/Taipei')->subDays(3))
// ->whereIn('multisys_id', $re_arr)
->get();
if (count($data) > 0) {
foreach ($data as $row_data) {
try {
// 取得圖片檔案路徑
$filePath = storage_path('app/ParsingFiles/' . str_replace('*', '/', $row_data['photo']));
// 使用 Intervention/Image 套件進行壓縮
$image = Image::make($filePath);
// 確認圖片寬度是否超過 1920
if ($image->getWidth() > 1920) {
// 計算等比例縮放後的寬度和高度
$width = 1920;
$height = round($image->getHeight() * $width / $image->getWidth());
// 縮放圖片
$image->resize($width, $height, function ($constraint) {
$constraint->aspectRatio();
});
}
// 壓縮圖片
$image->encode('jpg', 40);
// 將圖片轉換為 base64 編碼
$base64 = base64_encode($image->encoded);
$post_data = [
'data' => $base64
];
Log::notice($post_data);
// $client = new Client(['base_uri' => 'http://220.128.232.102:5000']);
$response = $client->request('POST', '/' . $row_data->no, ['form_params' => $post_data, 'connect_timeout' => 10]);
// Here the code for successful request
if ($response->getStatusCode() == "200") {
$msg = json_decode($response->getBody(), true)['Message'];
if ($msg == '上傳成功') {
$row_data->status = 2;
$row_data->save();
Log::notice($row_data->no . "-" . $msg);
} else {
$row_data->status = 4;
$row_data->save();
Log::error($row_data->no . "-" . $msg);
}
} else {
$row_data->status = 3;
$row_data->save();
}
} catch (\Exception $e) {
// There was another exception.
Log::error('violationparking_id: ' . $row_data->id);
Log::error($e->getMessage());
}
}
}
Log::notice('違停入案-佐證資料送往入案系統,共' . count($data) . '筆');
}
#endregion
#region 區間入案
public static function interval_entry($days = 7)
{
$records = IntervalDataBack::query();
// $records->where('datacheck', 1);
$records->where('postcheck', 0);
$records->whereNotNull('viocode');
$records->whereNotNull('jsoncheck');
// $records->where('starttime', '>', Carbon::now('Asia/Taipei')->subDays($days));
$records->where('starttime', '>', '2025-01-15');
$data = $records->select('id as intervaldataback_id', 'startpicture', 'endpicture')->get()->toArray();
// dd($data);
$values = [];
foreach ($data as $row) {
$row['photo'] = str_replace('-merge.jpg', '~', str_replace('Merge', 'Interval', $row['startpicture'])) . str_replace('-merge.jpg', '-interval.png', explode('*', $row['endpicture'])[4]);
unset($row['startpicture']);
unset($row['endpicture']);
$values[] = '(\'' . implode('\',\'', $row) . '\')';
}
// dd($values);
if (count($values) > 0) {
DB::transaction(function () use ($values, $records) {
// 在這裡執行需要交易的資料庫操作,例如新增、修改、刪除等等
DB::insert('INSERT IGNORE INTO interval_entry (intervaldataback_id, photo) VALUES' . implode(',', $values));
$records->update(['postcheck' => 1]);
});
}
Log::notice('區間入案-資料收集 近' . $days . '天,共' . count($values) . '筆');
}
public static function get_interval_entry_data($days = 1, $status = [0])
{
$ids = IntervalEntry::whereIn('status', $status)->where('created_at', '>', Carbon::now('Asia/Taipei')->subDays($days))->pluck('intervaldataback_id');
$data = IntervalDataBack::whereIn('id', $ids)->get();
// dd($data);
$values = [];
foreach ($data as $row) {
$s_time = $row->start_time;
$s_date = Carbon::parse($s_time)->toDateString();
$s_twYear = Carbon::parse($s_date)->format('Y') - 1911; // 取得民國年,即西元年減去1911
$s_twDate = $s_twYear . Carbon::parse($s_date)->format('md'); // 將民國年與日期合併
$s_time = Carbon::parse($s_time)->format('His');
$e_time = $row->end_time;
$e_date = Carbon::parse($e_time)->toDateString();
$e_twYear = Carbon::parse($e_date)->format('Y') - 1911; // 取得民國年,即西元年減去1911
$e_twDate = $e_twYear . Carbon::parse($e_date)->format('md'); // 將民國年與日期合併
$e_time = Carbon::parse($e_time)->format('His');
$cartype = $row->cartype;
if ($row->carkind == '汽車' || $row->carkind == '大型車') {
$cartype = 1;
} else if ($row->carkind == '機車' || $row->carkind == '大型重型機車') {
$cartype = 3;
} else {
continue;
}
$row_data = [
'datatime' => $row->start_time,
'SN' => $row->id,
'ViolationDate' => $e_twDate,
'ViolationTime' => $e_time,
'UnitID' => 'A000',
'PoliceName' => DB::table('operator')->where('account', $row->jsoncheck)->first()->name ?? '',
'LicensePlate' => $row->carnumber,
'VehicleType' => $cartype,
'RuleId' => $row->violationcode,
'Road' => $row->location,
'Speed' => intval(floor($row->speed)),
'LimitSpeed' => intval(floor($row->limit_speed)),
'Distance' => $row->distance,
'Duration' => $row->diff,
'InDate' => $s_twDate,
'InTime' => $s_time,
'OutDate' => $e_twDate,
'OutTime' => $e_time,
];
array_push($values, $row_data);
}
return $values;
}
// 組合資料並建檔備存
public static function interval_entry_local($data = [])
{
if (count($data) > 0) {
foreach ($data as $row_data) {
$datetime = $row_data['datatime'];
unset($row_data['datatime']);
// 存放檔案的目錄路徑
$directory = 'entry/interval/' . Carbon::now('Asia/Taipei')->format('Ymd');
// 如果目錄不存在,則建立該目錄
Storage::makeDirectory($directory);
// 將資料轉換為 JSON 格式
$jsonData = json_encode($row_data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
// 將 JSON 資料寫入檔案
$row_date = Carbon::parse($datetime)->format('Ymd_His');
$fileName = 'ITL_' . $row_date . '_' . $row_data['SN'] . '.json';
Storage::put($directory . "/$fileName", $jsonData);
}
}
Log::notice('區間入案-資料組合備存,共' . count($data) . '筆');
}
public static function interval_entry_post($data = [])
{
set_time_limit(0);
if (count($data) > 0) {
foreach ($data as $row_data) {
unset($row_data['datatime']);
$id = $row_data['SN'];
$row_data['SN'] = 'ITL' . $row_data['SN'];
Log::notice($row_data);
try {
// $client = new Client(['base_uri' => 'http://220.128.232.102:8000']);
$response = $client->request('POST', '/HX/sunhouse', ['form_params' => $row_data, 'connect_timeout' => 10]);
// Here the code for successful request
if ($response->getStatusCode() == "200") {
$resp = $response->getBody();
try {
$res = json_decode($resp, true);
if ($res['code'] == "200") {
IntervalEntry::where('intervaldataback_id', $id)->first()->update(['response' => $resp, 'status' => 1]);
} else {
IntervalEntry::where('intervaldataback_id', $id)->first()->update(['response' => $resp, 'status' => 4]);
Log::error('intervaldataback_id: ' . $id . "雖然response->getStatusCode() == 200,但不成功");
}
} catch (\Throwable $th) {
IntervalEntry::where('intervaldataback_id', $id)->first()->update(['response' => $resp, 'status' => 5]);
Log::error('intervaldataback_id: ' . $id);
Log::error($th);
}
} else {
$resp = $response->getBody();
IntervalEntry::where('intervaldataback_id', $id)->first()->update(['response' => $resp, 'status' => 6]);
Log::error('intervaldataback_id: ' . $id . "response->getStatusCode() != 200");
}
// if ($response->getStatusCode() == "200") {
// $resp = $response->getBody();
// IntervalEntry::where('intervaldataback_id', $id)->first()->update(['response' => $resp, 'status' => 1]);
// } else {
// $resp = $response->getBody();
// IntervalEntry::where('intervaldataback_id', $id)->first()->update(['response' => $resp, 'status' => 3]);
// }
} catch (\Exception $e) {
// There was another exception.
Log::error('intervaldataback_id: ' . $id);
Log::error($e->getMessage());
}
// break;
}
}
Log::notice('區間入案-資料送往入案系統,共' . count($data) . '筆');
}
// 送出佐證圖片
public static function interval_entry_post_img()
{
$re_arr = [];
$data = IntervalEntry::where('status', 1)
->where('created_at', '>', Carbon::now('Asia/Taipei')->subDays(10))
// ->whereIn('interval_id', $re_arr)
->get();
if (count($data) > 0) {
foreach ($data as $row_data) {
try {
// 使用 intervention/image 將檔案壓縮至500K以下
// 將圖片轉換為 base64 格式 不儲存
$image = Image::make(public_path('ParsingFiles/interval/' . str_replace('*', '/', $row_data->photo)));
// 確認圖片寬度是否超過 1920
if ($image->getWidth() > 1920) {
// 計算等比例縮放後的寬度和高度
$width = 1920;
$height = round($image->getHeight() * $width / $image->getWidth());
// 縮放圖片
$image->resize($width, $height, function ($constraint) {
$constraint->aspectRatio();
});
}
// 壓縮圖片
$image->encode('jpg', 40);
// 將圖片轉換為 base64 編碼
$base64 = base64_encode($image->encoded);
$post_data = [
'data' => $base64
];
// Log::channel('entry')->notice($post_data);
$client = new Client(['base_uri' => 'http://220.128.232.102:8000']);
$response = $client->request('POST', '/' . $row_data->no, ['form_params' => $post_data, 'connect_timeout' => 10]);
// Here the code for successful request
if ($response->getStatusCode() == "200") {
$msg = json_decode($response->getBody(), true)['Message'];
if ($msg == '上傳成功') {
$row_data->status = 2;
$row_data->save();
Log::channel('entry')->notice($row_data->no . "-" . $msg);
} else {
$row_data->status = 4;
$row_data->save();
Log::error($row_data->no . "-" . $msg);
}
} else {
$row_data->status = 3;
$row_data->save();
}
} catch (\Exception $e) {
// There was another exception.
Log::error('intervaldataback_id(postimg): ' . $row_data->id);
Log::error($e->getMessage());
}
}
}
Log::channel('entry')->notice('區間入案-佐證資料送往入案系統,共' . count($data) . '筆');
}
#endregion
}

102
app/Class/StaticData.php

@ -0,0 +1,102 @@ @@ -0,0 +1,102 @@
<?php
namespace App\Class;
class StaticData
{
// 單位 unit
public static $unit = [
'警察局' => '警察局',
'交通警察大隊' => '交通警察大隊',
'執法組' => '執法組',
'交通分隊' => '交通分隊',
'廠商維運' => '廠商維運',
];
// 不舉發理由(通用)
public static $reason = [
'車牌模糊' => '車牌模糊',
'車牌無法辨識' => '車牌無法辨識',
'車種無法識別' => '車種無法識別',
'沒有車牌' => '沒有車牌',
'照片採證不足' => '照片採證不足',
'警車執勤' => '警車執勤',
'重複舉發' => '重複舉發',
'違規事實不明確' => '違規事實不明確',
'執行公務' => '執行公務',
];
// 不舉發理由(違規停車)
public static $reason_vpk = [
'黃線20點以後可以停車' => '黃線20點以後可以停車',
'卸貨車格停車' => '卸貨車格停車',
'未停車滿3分鐘' => '未停車滿3分鐘',
'車種停放吻合(計乘車停放計乘車格,自用車停放在自用車格)' => '車種停放吻合(計乘車停放計乘車格,自用車停放在自用車格)',
'公務車輛於執行任務時,其臨時停車及停車地點得不受前二條之限制' => '公務車輛於執行任務時,其臨時停車及停車地點得不受前二條之限制',
];
// 不舉發理由(路口多功能)
public static $reason_ms = [
'1.車牌模糊(遮蔽、過暗、過度曝光)無法辨識。' => '1.車牌模糊(遮蔽、過暗、過度曝光)無法辨識。',
'2.多車同時進入照相範圍無法辨識違規車道。' => '2.多車同時進入照相範圍無法辨識違規車道。',
'3.雷達感應速度偵測異常。' => '3.雷達感應速度偵測異常。',
'4.車輛廠牌顏色與車牌登記不符。' => '4.車輛廠牌顏色與車牌登記不符。',
'5.交通號誌燈異常無法辨識違規樣態。' => '5.交通號誌燈異常無法辨識違規樣態。',
'6.重複照片。' => '6.重複照片。',
'7.相片異常。' => '7.相片異常。',
'8.檔案逾期。' => '8.檔案逾期。',
'9.公告尚未完成。' => '9.公告尚未完成。',
'10.無車牌。' => '10.無車牌。',
'11.工程、警用、救護重輛。' => '11.工程、警用、救護重輛。',
'12.主線道未開放。' => '12.主線道未開放。',
'13.非取締車種。' => '13.非取締車種。',
'14.緊急避難(禮讓救護車)。' => '14.緊急避難(禮讓救護車)。',
'15.不宜舉發' => '15.不宜舉發',
'16.違規類型判別錯誤' => '16.違規類型判別錯誤',
'17.違規車輛判別錯誤' => '17.違規車輛判別錯誤',
];
// 不舉發理由(區間)
public static $reason_itl = [
];
// 不舉發理由(超速)
public static $reason_osr = [
'1.車牌模糊(遮蔽、過暗、過度曝光)無法辨識。' => '1.車牌模糊(遮蔽、過暗、過度曝光)無法辨識。',
'2.多車同時進入照相範圍無法辨識違規車道。' => '2.多車同時進入照相範圍無法辨識違規車道。',
'3.雷達感應速度偵測異常。' => '3.雷達感應速度偵測異常。',
'4.車輛廠牌顏色與車牌登記不符。' => '4.車輛廠牌顏色與車牌登記不符。',
'5.交通號誌燈異常無法辨識違規樣態。' => '5.交通號誌燈異常無法辨識違規樣態。',
'6.重複照片。' => '6.重複照片。',
'7.相片異常。' => '7.相片異常。',
'8.檔案逾期。' => '8.檔案逾期。',
'9.公告尚未完成。' => '9.公告尚未完成。',
'10.無車牌。' => '10.無車牌。',
'11.工程、警用、救護重輛。' => '11.工程、警用、救護重輛。',
'12.主線道未開放。' => '12.主線道未開放。',
'13.非取締車種。' => '13.非取締車種。',
'14.緊急避難(禮讓救護車)。' => '14.緊急避難(禮讓救護車)。',
'15.不宜舉發' => '15.不宜舉發',
'16.違規類型判別錯誤' => '16.違規類型判別錯誤',
'17.違規車輛判別錯誤' => '17.違規車輛判別錯誤',
];
// 不舉發理由(闖紅)
public static $reason_red = [
'1.車牌模糊(遮蔽、過暗、過度曝光)無法辨識。' => '1.車牌模糊(遮蔽、過暗、過度曝光)無法辨識。',
'2.多車同時進入照相範圍無法辨識違規車道。' => '2.多車同時進入照相範圍無法辨識違規車道。',
'3.雷達感應速度偵測異常。' => '3.雷達感應速度偵測異常。',
'4.車輛廠牌顏色與車牌登記不符。' => '4.車輛廠牌顏色與車牌登記不符。',
'5.交通號誌燈異常無法辨識違規樣態。' => '5.交通號誌燈異常無法辨識違規樣態。',
'6.重複照片。' => '6.重複照片。',
'7.相片異常。' => '7.相片異常。',
'8.檔案逾期。' => '8.檔案逾期。',
'9.公告尚未完成。' => '9.公告尚未完成。',
'10.無車牌。' => '10.無車牌。',
'11.工程、警用、救護重輛。' => '11.工程、警用、救護重輛。',
'12.主線道未開放。' => '12.主線道未開放。',
'13.非取締車種。' => '13.非取締車種。',
'14.緊急避難(禮讓救護車)。' => '14.緊急避難(禮讓救護車)。',
'15.不宜舉發' => '15.不宜舉發',
'16.違規類型判別錯誤' => '16.違規類型判別錯誤'
];
}

39
app/Console/Commands/EntryInterval.php

@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
<?php
namespace App\Console\Commands;
use App\Classes\NewEntry;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
class EntryInterval extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'entry:interval';
/**
* The console command description.
*
* @var string
*/
protected $description = '區間入案';
/**
* Execute the console command.
*/
public function handle()
{
NewEntry::interval_entry(14);
$itl_data = NewEntry::get_interval_entry_data(10);
// Log::channel('entry')->notice($itl_data);
if(count($itl_data)> 0){
NewEntry::interval_entry_local($itl_data);
NewEntry::interval_entry_post($itl_data);
}
NewEntry::interval_entry_post_img();
}
}

155
app/Console/Kernel.php

@ -2,8 +2,22 @@ @@ -2,8 +2,22 @@
namespace App\Console;
use App\Class\Entry;
use App\Class\ItlEntry;
use App\Class\LineNotify;
use App\Models\ExportFiles;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Illuminate\Support\Str;
use App\Exports\ArrayExport;
use App\Models\Clientlog;
use App\Models\Clientlogsecond;
use App\Models\Serverlog;
use App\Models\UserLog;
use Carbon\Carbon;
use DateInterval;
use DateTime;
use Maatwebsite\Excel\Facades\Excel;
class Kernel extends ConsoleKernel
{
@ -12,16 +26,155 @@ class Kernel extends ConsoleKernel @@ -12,16 +26,155 @@ class Kernel extends ConsoleKernel
*/
protected function schedule(Schedule $schedule): void
{
ini_set('memory_limit', '2048M');
// $schedule->command('inspire')->hourly();
// schedule function everyday 15:00 output
$schedule->call(function () {
// Entry::multisys_entry(14);
// Entry::overspeedred_entry(14);
// $ts_data = Entry::get_multisys_entry_data(1);
// if (count($ts_data) > 0) {
// Entry::multisys_entry_local($ts_data);
// Entry::multisys_entry_post($ts_data);
// }
// $ovo_data = Entry::get_overspeedred_entry_data(1);
// if (count($ovo_data) > 0) {
// Entry::overspeedred_entry_local($ovo_data);
// Entry::overspeedred_entry_post($ovo_data);
// }
// Entry::multisys_entry_post_img();
// Entry::overspeedred_entry_post_img();
});
$schedule->call(function () {
$data = [];
$message = [];
// message 增加 系統發信時間
$message[] = "系統發信時間: " . date('Y-m-d H:i:s');
$message[] = "校時異常 or 斷線!!!請檢查設備!!!";
$server_log = Serverlog::orderBy('this_time', 'desc')->first();
$nowTime = new DateTime();
$server_time = DateTime::createFromFormat('Y-m-d H:i:s.u', Carbon::parse($server_log->this_time)->format('Y-m-d H:i:s.u'));
// 如果Server log 的 diffsecond > 200 毫秒 或 this_time 與 現在的 的時間 差距大於或小於 |2.5分鐘|
if ($server_log-> diffsecond > 200 || abs(strtotime($server_log->this_time) - strtotime($nowTime->format('Y-m-d H:i:s'))) > 150) {
$message[] = "Server : " . $server_time . " " . $server_log->diffsecond . "ms";
}
// 取得 clientLog 的最新時間紀錄
$distinct_name = Clientlog::cachedDistinctName();
// dd($distinct_name);
foreach ($distinct_name as $name) {
$client_log = Clientlog::where('name', $name->name)->orderBy('this_time', 'desc')->first();
if ($client_log) {
$data[] = $client_log->toArray();
}
}
$distinct_name = Clientlogsecond::cachedDistinctName();
foreach ($distinct_name as $name) {
$client_log = Clientlogsecond::where('name', $name->name)->orderBy('this_time', 'desc')->first();
if ($client_log) {
$data[] = $client_log->toArray();
}
}
foreach ($data as $key => $value) {
// 如果 $date->thistime 與 $server_log->this_time 的時間 差距大於 150秒
$thisTime = DateTime::createFromFormat('Y-m-d H:i:s.u', $value['this_time']);
if ($thisTime !== false && abs(strtotime($value['this_time']) - strtotime($server_log->this_time)) > 150) {
$message[] = "大於2.5分鐘: " . $value['name'] . " " . $value['this_time'] . " ";
}
//如果diffsecond > 200毫秒 (絕對值 diffsecond*1000 > 200)
else if (abs($value['diffsecond']) > 200) {
$message[] = "大於200毫秒: " . $value['name'] . " " . $value['this_time'] . " " . $value['diffsecond'] . "ms";
}
}
// dd($message);
// if (count($message) > 2) {
// $this->send("\n" . implode("\n", $message));
// }
})->everyMinute();
$schedule->call(function () {
$this->exportLog();
Entry::multisys_entry(14);
Entry::overspeedred_entry(14);
ItlEntry::interval_entry(14);
})
// ->everyMinute();
->timezone('Asia/Taipei')->dailyAt('00:00');
$schedule
->call(function () {
// 步驟2 -撈出所有的建檔本地端儲存
$ts_data = Entry::get_multisys_entry_data(1);
if (count($ts_data) > 0) {
Entry::multisys_entry_local($ts_data);
Entry::multisys_entry_post($ts_data);
}
$ovo_data = Entry::get_overspeedred_entry_data(1);
if (count($ovo_data) > 0) {
Entry::overspeedred_entry_local($ovo_data);
Entry::overspeedred_entry_post($ovo_data);
}
$itl_data = ItlEntry::get_interval_entry_data(10);
// Log::channel('entry')->notice($itl_data);
if(count($itl_data)> 0){
ItlEntry::interval_entry_local($itl_data);
ItlEntry::interval_entry_post($itl_data);
}
// dd(count($ts_data),count($vpk_data));
})
->everyMinute();
// ->timezone('Asia/Taipei')->dailyAt('17:24');
$schedule
->call(function () {
// 步驟3-送出圖片
Entry::multisys_entry_post_img();
Entry::overspeedred_entry_post_img();
ItlEntry::interval_entry_post_img();
})
->everyMinute();
// ->timezone('Asia/Taipei')->dailyAt('00:30');
}
protected function exportLog()
{
$data_arr = UserLog::OrderBy('id', 'desc')->get()->toArray();
$columns = ['user_name', 'ip', 'action_detail', 'created_at', 'remark'];
$columnTitle = [
['使用者名稱', 'IP位址', '動作', '時間', '備註']
];
$data = array_map(function ($row) use ($columns) {
return array_merge(array_flip($columns), array_intersect_key($row, array_flip($columns)));
}, $data_arr);
$fileName = 'userlog-' . Str::random(10) . '.xlsx';
ExportFiles::create([
'name' => $fileName,
'path' => 'public/exports_log',
'type' => 'xlsx',
'status' => '1',
'remark' => '操作紀錄匯出(自動排程)',
'user_id' => 1,
]);
Excel::store(new ArrayExport($data, $columnTitle), 'public/exports_log/' . $fileName, 'local', \Maatwebsite\Excel\Excel::XLSX);
}
/**
* Register the commands for the application.
*/
protected function commands(): void
{
$this->load(__DIR__.'/Commands');
$this->load(__DIR__ . '/Commands');
require base_path('routes/console.php');
}
protected function send($message)
{
LineNotify::send($message);
}
}

52
app/Events/ExampleEvent.php

@ -0,0 +1,52 @@ @@ -0,0 +1,52 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class ExampleEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*/
public $message;
public $token;
public function __construct($message, $token = null)
{
$this->message = $message;
$this->token = $token ?? null;
}
/**
* Get the channels the event should broadcast on.
*
* @return array<int, \Illuminate\Broadcasting\Channel>
*/
public function broadcastOn()
{
// return new Channel('test-channel');
if ($this->token) {
return new PrivateChannel('test-private.' . $this->token);
}
return [
// 'test-event',
new Channel('test-channel'),
new PrivateChannel('test-private'),
];
}
// public function broadcastWith()
// {
// return [
// 'data' => 'key'
// ];
// }
}

38
app/Exports/ArrayExport.php

@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
<?php
namespace App\Exports;
use Maatwebsite\Excel\Concerns\FromArray;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithCustomStartCell;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class ArrayExport implements FromArray, WithHeadings, WithCustomStartCell
{
protected $data;
protected $headings;
protected $startCell;
public function __construct(array $data, array $headings = null, string $startCell = 'A1')
{
$this->data = $data;
$this->headings = $headings;
$this->startCell = $startCell;
}
public function array(): array
{
return $this->data;
}
public function headings(): array
{
// 定義標題列
return $this->headings ?? array_keys($this->data[0]);
}
public function startCell(): string
{
return $this->startCell ?? 'A1';
}
}

147
app/Exports/ArrayExportH.php

@ -0,0 +1,147 @@ @@ -0,0 +1,147 @@
<?php
namespace App\Exports;
use Maatwebsite\Excel\Concerns\FromArray;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithCustomStartCell;
use Maatwebsite\Excel\Events\AfterSheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Concerns\FromCollection;
use Illuminate\Support\Collection;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
class ArrayExportH implements FromArray, WithHeadings, WithCustomStartCell, WithEvents
{
protected $data = [];
protected $headings = null;
protected $startCell = 'A1';
protected $maxWidthThreshold = 50; // 設定相對的最大值
protected $printOrientation = 1; // 預設橫式列印 1 = 橫式列印, 0 = 直式列印
protected $showPageNumber = 0; // 是否顯示頁碼 1 = 顯示, 0 = 不顯示
protected $showBorder = 0; // 顯示框線
public function __construct(array $data, array $headings = null, array $option = [])
{
$this->data = $data;
$this->headings = $headings;
$this->startCell = $option['startCell'] ?? 'A1';
$this->maxWidthThreshold = $option['maxWidth'] ?? 50;
$this->printOrientation = $option['printDef'] ?? 1;
$this->showPageNumber = $option['showPN'] ?? 1;
$this->showBorder = $option['showBorder'] ?? 1;
}
public function array(): array
{
return $this->data;
}
public function headings(): array
{
// 定義標題列
return $this->headings ?? array_keys($this->data[0]);
}
public function startCell(): string
{
return $this->startCell ?? 'A1';
}
public function registerEvents(): array
{
return [
AfterSheet::class => function (AfterSheet $event) {
// 在這裡設定欄位寬度
$sheet = $event->sheet;
// 設定頁尾顯示
if ($this->showPageNumber) {
// $sheet->getDelegate()->getHeaderFooter()->setOddHeader('&L &D &T &R 第 &P 頁,共 &N 頁');
$sheet->getDelegate()->getHeaderFooter()->setOddFooter('&R 第 &P 頁,共 &N 頁');
}
// 設定列印邊界為窄邊界
$sheet->getDelegate()->getPageMargins()->setTop(0.75);
$sheet->getDelegate()->getPageMargins()->setBottom(0.75);
$sheet->getDelegate()->getPageMargins()->setLeft(0.25);
$sheet->getDelegate()->getPageMargins()->setRight(0.25);
// 設定橫式列印
if ($this->printOrientation)
$sheet->getDelegate()->getPageSetup()->setOrientation(PageSetup::ORIENTATION_LANDSCAPE);
// 最高的行和列
$highestRow = $sheet->getDelegate()->getHighestRow();
$highestColumn = $sheet->getDelegate()->getHighestColumn();
$sheet->getDelegate()->mergeCells('A1:' . $highestColumn . '1'); // 合併 A1:C1 儲存格
$sheet->getDelegate()->mergeCells('A2:' . $highestColumn . '2'); // 合併 A1:C1 儲存格
// $sheet->getDelegate()->mergeCells('A3:C3'); // 合併 A1:C1 儲存格
// $sheet->getDelegate()->mergeCells('D1:G2'); // 合併 A1:C1 儲存格
// 自動設定框線
if ($this->showBorder) {
$range = 'A1:' . $highestColumn . $highestRow;
$sheet->getDelegate()->getStyle($range)->getBorders()->getAllBorders()->setBorderStyle('thin');
}
// 設定資料內容置中
$range = 'A3:' . $highestColumn . $highestRow;
$sheet->getDelegate()->getStyle($range)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
$sheet->getDelegate()->getStyle($range)->getAlignment()->setVertical(Alignment::VERTICAL_CENTER);
// 測量欄位內容寬度並設定寬度
$maxWidths = [];
foreach ($sheet->getColumnIterator() as $column) {
$columnLetter = $column->getColumnIndex();
$maxWidth = 0;
foreach ($column->getCellIterator() as $cell) {
$width = strlen((string)$cell->getValue()) * 1.2; // 設定寬度倍率
if ($width > $maxWidth) {
$maxWidth = $width;
}
}
$maxWidths[$columnLetter] = $maxWidth;
}
foreach ($maxWidths as $columnLetter => $width) {
$sheet->getColumnDimension($columnLetter)->setWidth($width > $this->maxWidthThreshold ? $this->maxWidthThreshold : $width);
}
},
];
}
protected function wrapTextIfNeeded($value)
{
// 檢查是否需要換行顯示
return strlen($value) > $this->maxWidthThreshold ? wordwrap($value, $this->maxWidthThreshold, "\n", true) : $value;
}
// public function drawings()
// {
// $drawings = [];
// foreach ($this->data as $row) {
// if (isset($row[1]) && !empty($row[1])) {
// $imagePath = public_path($row[1]); // 圖片路徑
// if (file_exists($imagePath)) {
// $drawing = new Drawing();
// $drawing->setName('Image');
// $drawing->setDescription('Image');
// $drawing->setPath($imagePath);
// $drawing->setHeight(100);
// $drawing->setCoordinates('A' . ($this->key() + 2)); // 從第二行開始
// $drawings[] = $drawing;
// }
// }
// }
// return $drawings;
// }
}

40
app/Http/Controllers/Auth/ConfirmPasswordController.php

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\ConfirmsPasswords;
class ConfirmPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Confirm Password Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password confirmations and
| uses a simple trait to include the behavior. You're free to explore
| this trait and override any functions that require customization.
|
*/
use ConfirmsPasswords;
/**
* Where to redirect users when the intended url fails.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
}

22
app/Http/Controllers/Auth/ForgotPasswordController.php

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
class ForgotPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset emails and
| includes a trait which assists in sending these notifications from
| your application to your users. Feel free to explore this trait.
|
*/
use SendsPasswordResetEmails;
}

59
app/Http/Controllers/Auth/LoginController.php

@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
protected $maxAttempts = 3;
protected $decayMinutes = 10;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
//複寫登入驗證 可使用name登入
protected function credentials(Request $request)
{
// dd($this->account());
$field = filter_var($request->get($this->username()), FILTER_VALIDATE_EMAIL)
? $this->username()
: 'account';//欄位名稱
return [
$field => $request->get($this->username()),
'password' => $request->password,
];
}
}

73
app/Http/Controllers/Auth/RegisterController.php

@ -0,0 +1,73 @@ @@ -0,0 +1,73 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\Models\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
class RegisterController extends Controller
{
/*
|--------------------------------------------------------------------------
| Register Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users as well as their
| validation and creation. By default this controller uses a trait to
| provide this functionality without requiring any additional code.
|
*/
use RegistersUsers;
/**
* Where to redirect users after registration.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
]);
}
/**
* Create a new user instance after a valid registration.
*
* @param array $data
* @return \App\Models\User
*/
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
}
}

30
app/Http/Controllers/Auth/ResetPasswordController.php

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\ResetsPasswords;
class ResetPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;
/**
* Where to redirect users after resetting their password.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
}

42
app/Http/Controllers/Auth/VerificationController.php

@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\VerifiesEmails;
class VerificationController extends Controller
{
/*
|--------------------------------------------------------------------------
| Email Verification Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling email verification for any
| user that recently registered with the application. Emails may also
| be re-sent if the user didn't receive the original email message.
|
*/
use VerifiesEmails;
/**
* Where to redirect users after verification.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware('signed')->only('verify');
$this->middleware('throttle:6,1')->only('verify', 'resend');
}
}

72
app/Http/Controllers/AuthController.php

@ -0,0 +1,72 @@ @@ -0,0 +1,72 @@
<?php
namespace App\Http\Controllers;
use App\Class\LogWriter;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
class AuthController extends Controller
{
public function resetPassword()
{
return view('auth.resetPassword');
}
public function updatePassword(Request $request)
{
if ($request->password == "Aa@123456789") {
return redirect()->back()->with('message', '請勿使用預設密碼');
}
if($request->password != $request->password_confirmation){
return redirect()->back()->with('message', '密碼不一致');
}
$pattern = "/^(?![A-Za-z0-9]+$)(?![a-z0-9\W]+$)(?![A-Za-z\W]+$)(?![A-Z0-9\W]+$)[a-zA-Z0-9\W]{12,}$/";
$pregRs = preg_match($pattern, $request->password);
if ($pregRs == 0) {
return redirect()->back()->with('message', '密碼錯誤或強度不足,請混合使用 12 個字元以上的英文字母、數字和符號。');
}
DB::beginTransaction();
try {
$user = User::find(auth()->user()->id);
$exists = DB::table('past_passwords')
->where('user_id', $user->id)
->orderBy('created_at', 'desc')
// ->where('password', Hash::check($request->password, $user->password))
->limit(3)->pluck('password');
// dd($exists);
if(isset($exists)){
foreach ($exists as $exist) {
if (Hash::check($request->password, $exist)) {
return redirect()->back()->with('message', '密碼不可與過去相同');
}
}
}
$user->update([
'password' => Hash::make($request->password)
]);
DB::table('past_passwords')->insert([
'user_id'=>$user->id,
'password'=>$user->password,
'created_at'=>Carbon::now('Asia/Taipei')
]);
$logData = [
'action' => 'update',
'action_detail' => '變更密碼',
'ip' => request()->ip(),
'remark' => "使用者:$user->name 變更密碼",
];
LogWriter::writeLog($logData, 'web');
DB::commit();
auth()->logout();
return redirect()->route('login');
} catch (\Throwable $th) {
DB::rollback();
return redirect()->back()->withErrors(['error' => '密碼更新失敗']);
}
}
}

28
app/Http/Controllers/HomeController.php

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class HomeController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the application dashboard.
*
* @return \Illuminate\Contracts\Support\Renderable
*/
public function index()
{
return view('home');
}
}

126
app/Http/Controllers/RoleController.php

@ -0,0 +1,126 @@ @@ -0,0 +1,126 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
use DB;
use Illuminate\View\View;
use Illuminate\Http\RedirectResponse;
class RoleController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
function __construct()
{
$this->middleware('permission:role-list|role-create|role-edit|role-delete', ['only' => ['index', 'store']]);
$this->middleware('permission:role-create', ['only' => ['create', 'store']]);
$this->middleware('permission:role-edit', ['only' => ['edit', 'update']]);
$this->middleware('permission:role-delete', ['only' => ['destroy']]);
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request): View
{
$roles = Role::orderBy('id', 'DESC')->paginate(5);
return view('roles.index', compact('roles'))
->with('i', ($request->input('page', 1) - 1) * 5);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create(): View
{
$permission = Permission::get();
return view('roles.create', compact('permission'));
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request): RedirectResponse
{
$this->validate($request, [
'name' => 'required|unique:roles,name',
'permission' => 'required',
]);
$role = Role::create(['name' => $request->input('name')]);
$role->syncPermissions($request->input('permission'));
return redirect()->route('roles.index')
->with('success', 'Role created successfully');
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id): View
{
$role = Role::find($id);
$rolePermissions = Permission::join("role_has_permissions", "role_has_permissions.permission_id", "=", "permissions.id")
->where("role_has_permissions.role_id", $id)
->get();
return view('roles.show', compact('role', 'rolePermissions'));
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id): View
{
$role = Role::find($id);
$permission = Permission::get();
$rolePermissions = DB::table("role_has_permissions")->where("role_has_permissions.role_id", $id)
->pluck('role_has_permissions.permission_id', 'role_has_permissions.permission_id')
->all();
return view('roles.edit', compact('role', 'permission', 'rolePermissions'));
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id): RedirectResponse
{
$this->validate($request, [
'name' => 'required',
'permission' => 'required',
]);
$role = Role::find($id);
$role->name = $request->input('name');
$role->save();
$role->syncPermissions($request->input('permission'));
return redirect()->route('roles.index')
->with('success', 'Role updated successfully');
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id): RedirectResponse
{
DB::table("roles")->where('id', $id)->delete();
return redirect()->route('roles.index')
->with('success', 'Role deleted successfully');
}
}

141
app/Http/Controllers/System/FileIndexController.php

@ -0,0 +1,141 @@ @@ -0,0 +1,141 @@
<?php
namespace App\Http\Controllers\system;
use App\Http\Controllers\Controller;
use App\Models\ExportFiles;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Http\Request;
use PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Exp;
class FileIndexController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
return view('filesystem.index');
}
//api_index
public function api_index(Request $request)
{
// 從資料庫取得檔案索引表 ExportFiles
$user_id = auth('api')->user()->id;
$files = ExportFiles::where('user_id', $user_id)->get();
[$draw, $totalRecords, $totalRecordswithFilter, $data_arr] = $this->getData($request);
$response = array(
"draw" => intval($draw),
"iTotalRecords" => $totalRecords,
"iTotalDisplayRecords" => $totalRecordswithFilter,
"aaData" => $data_arr
);
// dd($response);
echo json_encode($response);
exit;
}
// get data
#region 取得資料
function getData($request)
{
$user_id = auth('api')->user()->id;
#region Request 搜尋值
if (isset($request->searchByFromdate))
$searchByFromdate = $request->searchByFromdate;
if (isset($request->searchByTodate))
$searchByTodate = $request->searchByTodate;
#endregion
#region DataTable 搜尋屬性
if (!isset($request->export)) {
$draw = $request->get('draw');
$start = $request->get("start");
$rowperpage = $request->get("length"); // Rows display per page
$columnIndex_arr = $request->get('order');
$columnName_arr = $request->get('columns');
$order_arr = $request->get('order');
$search_arr = $request->get('search');
$columnIndex = $columnIndex_arr[0]['column']; // Column index
$columnName = $columnName_arr[$columnIndex]['data']; // Column name
$columnSortOrder = $order_arr[0]['dir']; // asc or desc
$searchValue = $search_arr['value']; // Search value
} else {
$draw = 0;
}
#endregion
// Fetch records
$records = ExportFiles::query();
$records->where('user_id', $user_id);
$totalRecords = $records->count();
if (isset($columnName))
$records->orderBy($columnName, $columnSortOrder);
if (isset($searchValue)) {
$records->where(function ($query) use ($searchValue) {
$query->where('remark', 'like', '%' . $searchValue . '%');
});
}
//時間限制
if (isset($searchByFromdate))
$records->where('created_at', ">", $searchByFromdate . ' 00:00:00');
if (isset($searchByTodate))
$records->where('created_at', "<", $searchByTodate . ' 23:59:59');
$totalRecordswithFilter = $records->count();
#region DataTable 分頁(報表不分頁)
if (!isset($request->export)) {
if (isset($start))
$records->skip($start);
if (isset($rowperpage))
$records->take($rowperpage);
}
#endregion
$records = $records->get();
#region 資料處理
$data_arr = array();
$sno = 1 + ($request->get('start') ?? 0);
foreach ($records as $record) {
$data_arr[] = array(
"id" => $sno++, // $record->id,
"display_name" => $record->remark,
"user" => User::find($record->user_id)->name ?? '',
"created_at" => Carbon::parse($record->created_at)->format('Y-m-d H:i:s'),
"name" => "$record->name",
);
}
#endregion
return [$draw, $totalRecords, $totalRecordswithFilter, $data_arr];
}
#endregion
// 下載檔案 透過檔案名稱
public function downloadFile(Request $request, $path)
{
$file = ExportFiles::where('name', $path)->first();
$path = storage_path('app/' . $file->path . '/' . $file->name);
// 檔案名稱
$name = $file->remark;
// 副檔名
$ext = pathinfo($path, PATHINFO_EXTENSION);
return response()->download($path, $name . '.' . $ext);
}
}

1044
app/Http/Controllers/System/IntervalController.php

File diff suppressed because it is too large Load Diff

425
app/Http/Controllers/System/IntervalEquipmentController.php

@ -0,0 +1,425 @@ @@ -0,0 +1,425 @@
<?php
namespace App\Http\Controllers\System;
use App\Class\LogWriter;
use App\Models\IntervalEquipment;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\AssetOwnership;
use App\Models\CustodyUnit;
use App\Models\Intervaldis;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class IntervalEquipmentController extends Controller
{
public function __construct()
{
$this->middleware('permission:itl-device-setting');
}
public function page()
{
// ['custodyunit' => $custodyunit, 'assetownership' => $assetownership]
$custodyunit = CustodyUnit::all();
$assetownership = AssetOwnership::all();
return view('system.interval.equipment')
->with('custodyunit', $custodyunit)
->with('assetownership', $assetownership);
}
public function road()
{
// ['custodyunit' => $custodyunit, 'assetownership' => $assetownership]
$custodyunit = CustodyUnit::all();
$assetownership = AssetOwnership::all();
$location = IntervalEquipment::select('id', 'serialnumber', 'location')->distinct()->get();
// dd($location);
return view('system.interval.equ_dis')
->with('location', $location)
->with('custodyunit', $custodyunit)
->with('assetownership', $assetownership);
}
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
#region DataTable 搜尋屬性
$draw = $request->get('draw');
$start = $request->get("start");
$rowperpage = $request->get("length"); // Rows display per page
$columnIndex_arr = $request->get('order');
$columnName_arr = $request->get('columns');
$order_arr = $request->get('order');
$search_arr = $request->get('search');
$columnIndex = $columnIndex_arr[0]['column']; // Column index
$columnName = $columnName_arr[$columnIndex]['data']; // Column name
$columnSortOrder = $order_arr[0]['dir']; // asc or desc
$searchValue = $search_arr['value']; // Search value
#endregion
// Role with permissions
$records = IntervalEquipment::query();
if (isset($request->dis)) {
$records = Intervaldis::query();
}
$totalRecords = $records->count();
if (isset($columnName))
$records->orderBy($columnName, $columnSortOrder);
if (isset($searchValue)) {
$records->where(function ($query) use ($searchValue) {
// $query->where('outlet_id', 'like', '%' . $searchValue . '%')
// ->orwhere('name', 'like', '%' . $searchValue . '%')
// ->orwhere('serialnumber', 'like', '%' . $searchValue . '%')
// ->orwhere('creator_id', 'like', '%' . $searchValue . '%');
});
}
$totalRecordswithFilter = $records->count();
if (isset($start))
$records->skip($start);
if (isset($rowperpage))
$records->take($rowperpage);
$data_arr = $records->get();
$response = array(
"draw" => intval($draw),
"iTotalRecords" => $totalRecords,
"iTotalDisplayRecords" => $totalRecordswithFilter,
"aaData" => $data_arr
);
return response()->json($response);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$device = new IntervalEquipment();
// 如果缺少必要欄位
DB::beginTransaction();
try {
$device->serialnumber = $request->serialnumber;
$device->videonumber = $request->videonumber;
$device->brand = $request->brand;
$device->model = $request->model;
$device->custodyunit_id = $request->custodyunit_id;
$device->assetownership_id = $request->assetownership_id;
$device->buydate = $request->buydate;
$device->activatedate = $request->activatedate;
$device->locationid = $request->locationid;
$device->location = $request->location;
$device->comment = $request->comment;
$device->precinct = $request->precinct;
$device->station = $request->station;
$device->save();
$logData = [
'action' => 'create',
'action_detail' => '新增設備',
'ip' => request()->ip(),
'remark' => "新增設備:{$device->serialnumber}",
];
logWriter::writeLog($logData, 'api');
DB::commit();
return response()->json(['success' => true, 'message' => '新增成功']);
} catch (\Throwable $th) {
//throw $th;
Log::error($th);
DB::rollBack();
return response()->json(['success' => false, 'message' => '新增失敗']);
}
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
$device = IntervalEquipment::find($id);
return response()->json(['success' => true, 'data' => $device]);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id)
{
$device = IntervalEquipment::find($id);
if (!$request->serialnumber || !$request->location || !$request->precinct || !$request->station) {
return response()->json(['success' => false, 'message' => '缺少必要欄位']);
}
DB::beginTransaction();
try {
$device->serialnumber = $request->serialnumber;
$device->videonumber = $request->videonumber;
$device->brand = $request->brand;
$device->model = $request->model;
$device->custodyunit_id = $request->custodyunit_id ?? null;
$device->assetownership_id = $request->assetownership_id ?? null;
$device->buydate = $request->buydate;
$device->activatedate = $request->activatedate;
$device->locationid = $request->locationid;
$device->location = $request->location;
$device->comment = $request->comment;
$device->precinct = $request->precinct;
$device->station = $request->station;
$device->save();
$logData = [
'action' => 'update',
'action_detail' => '更新設備',
'ip' => request()->ip(),
'remark' => "更新設備:{$device->serialnumber}",
];
logWriter::writeLog($logData, 'api');
DB::commit();
return response()->json(['success' => true, 'message' => '更新成功']);
} catch (\Throwable $th) {
//throw $th;
Log::error($th);
DB::rollBack();
return response()->json(['success' => false, 'message' => '更新失敗']);
}
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
$device = IntervalEquipment::find($id);
try {
$device->delete();
return response()->json(['success' => true, 'message' => '刪除成功']);
} catch (\Throwable $th) {
//throw $th;
Log::error($th);
return response()->json(['success' => false, 'message' => '刪除失敗']);
}
}
/**
* 取得保管單位資料
*/
public function indexUnit(Request $request)
{
if ($request->type == "custodyunit") {
$unit = CustodyUnit::all();
return response()->json(['success' => true, 'data' => $unit]);
} else if ($request->type == "assetownership") {
$unit = AssetOwnership::all();
return response()->json(['success' => true, 'data' => $unit]);
}
return response()->json(['success' => false, 'message' => '取得失敗']);
}
/**
* 新增單位資料
*/
public function storeUnit(Request $request)
{
// custodyunit_id
//assetownership_id)
DB::beginTransaction();
try {
if ($request->type == "custodyunit") {
$unit = new CustodyUnit();
$unit->name = $request->name;
$unit->save();
$logData = [
'action' => 'create',
'action_detail' => '新增保管單位',
'ip' => request()->ip(),
'remark' => "新增保管單位:{$request->name}",
];
LogWriter::writeLog($logData, 'api');
DB::commit();
return response()->json(['success' => true, 'message' => '保管單位-新增成功']);
} else if ($request->type == "assetownership") {
$unit = new AssetOwnership();
$unit->name = $request->name;
$unit->save();
$logData = [
'action' => 'create',
'action_detail' => '新增財產所屬單位',
'ip' => request()->ip(),
'remark' => "新增財產所屬單位:{$request->name}",
];
LogWriter::writeLog($logData, 'api');
DB::commit();
return response()->json(['success' => true, 'message' => '財產所屬單位-新增成功']);
}
} catch (\Throwable $th) {
//throw $th;
Log::error($th);
DB::rollBack();
}
return response()->json(['success' => false, 'message' => '新增失敗']);
}
/**
* Store a newly created resource in storage.
*/
public function storeRoad(Request $request)
{
$road = new Intervaldis();
// 如果缺少必要欄位
if (
!$request->start_num || !$request->end_num || !$request->outlaw_speed || !$request->location
|| !$request->distance || !$request->limit_speed || !$request->location_id
|| !$request->start_cert || !$request->end_cert
) {
return response()->json(['error' => false, 'message' => '缺少必要欄位']);
}
// 起始機號與結束機號不可相同
if ($request->start_num == $request->end_num) {
return response()->json(['error' => false, 'message' => '起始機號與結束機號不可相同']);
}
// 起始日期不可大於結束日期
if ($request->start_cert > $request->end_cert) {
return response()->json(['error' => false, 'message' => '起始日期不可大於結束日期']);
}
DB::beginTransaction();
try {
$road->start_serialnumber = $request->start_num;
$road->end_serialnumber = $request->end_num;
$road->distance = $request->distance;
$road->limit_speed = $request->limit_speed;
$road->outlaw_speed = $request->outlaw_speed;
$road->limit_time = round(3600 * $request->distance / $request->limit_speed / 1000, 2);
$road->speed_alert = $request->speed_alert;
$road->count_alert = $request->count_alert;
$road->location = $request->location;
$road->location_id = $request->location_id;
$road->certificatenumber = $request->certificatenumber;
$road->start_cert = $request->start_cert;
$road->end_cert = $request->end_cert;
// dd($road);
$road->save();
$logData = [
'action' => 'create',
'action_detail' => '新增路段',
'ip' => request()->ip(),
'remark' => "新增路段:{$road->serialnumber}",
];
logWriter::writeLog($logData, 'api');
DB::commit();
return response()->json(['success' => true, 'message' => '新增成功']);
} catch (\Throwable $th) {
//throw $th;
Log::error($th);
DB::rollBack();
return response()->json(['error' => false, 'message' => '新增失敗']);
}
}
/**
* Display the specified resource.
*/
public function showRoad(string $id)
{
$device = Intervaldis::find($id);
return response()->json(['success' => true, 'data' => $device]);
}
/**
* Update the specified resource in storage.
*/
public function updateRoad(Request $request, string $id)
{
$road = Intervaldis::find($id);
if (
!$request->start_num || !$request->end_num || !$request->outlaw_speed || !$request->location
|| !$request->distance || !$request->limit_speed || !$request->location_id
|| !$request->start_cert || !$request->end_cert
) {
return response()->json(['error' => false, 'message' => '缺少必要欄位']);
}
// 起始機號與結束機號不可相同
if ($request->start_num == $request->end_num) {
return response()->json(['error' => false, 'message' => '起始機號與結束機號不可相同']);
}
// 起始日期不可大於結束日期
if ($request->start_cert > $request->end_cert) {
return response()->json(['error' => false, 'message' => '起始日期不可大於結束日期']);
}
DB::beginTransaction();
try {
$road->start_serialnumber = $request->start_num;
$road->end_serialnumber = $request->end_num;
$road->distance = $request->distance;
$road->limit_speed = $request->limit_speed;
$road->outlaw_speed = $request->outlaw_speed;
$road->limit_time = round(3600 * $request->distance / $request->limit_speed / 1000, 2);
$road->speed_alert = $request->speed_alert;
$road->count_alert = $request->count_alert;
$road->location = $request->location;
$road->location_id = $request->location_id;
$road->certificatenumber = $request->certificatenumber;
$road->start_cert = $request->start_cert;
$road->end_cert = $request->end_cert;
$road->save();
$logData = [
'action' => 'update',
'action_detail' => '更新路段',
'ip' => request()->ip(),
'remark' => "更新路段:{$road->location}",
];
logWriter::writeLog($logData, 'api');
DB::commit();
return response()->json(['success' => true, 'message' => '更新成功']);
} catch (\Throwable $th) {
//throw $th;
Log::error($th);
DB::rollBack();
return response()->json(['error' => false, 'message' => '更新失敗']);
}
}
/**
* Remove the specified resource from storage.
*/
public function destroyRoad(string $id)
{
$device = Intervaldis::find($id);
try {
$device->delete();
return response()->json(['success' => true, 'message' => '刪除成功']);
} catch (\Throwable $th) {
//throw $th;
Log::error($th);
return response()->json(['error' => false, 'message' => '刪除失敗']);
}
}
}

200
app/Http/Controllers/System/LogController.php

@ -0,0 +1,200 @@ @@ -0,0 +1,200 @@
<?php
namespace App\Http\Controllers\System;
use App\Exports\ArrayExport;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\ExportFiles;
use App\Models\UserLog;
use Carbon\Carbon;
use Maatwebsite\Excel\Facades\Excel;
use Illuminate\Support\Str;
class LogController extends Controller
{
public function __construct()
{
}
// index
public function index(Request $request)
{
$actionDetails = UserLog::cachedDistinctActionDetail();
$users = UserLog::cachedDistinctUser();
return view('system.setting.log')
->with('actionDetails', $actionDetails)
->with('users', $users);
}
// get data
#region 取得資料
function getData($request)
{
#region Request 搜尋值
if (isset($request->searchByFromdate))
$searchByFromdate = $request->searchByFromdate;
if (isset($request->searchByTodate))
$searchByTodate = $request->searchByTodate;
#endregion
#region DataTable 搜尋屬性
if (!isset($request->export)) {
$draw = $request->get('draw');
$start = $request->get("start");
$rowperpage = $request->get("length"); // Rows display per page
$columnIndex_arr = $request->get('order');
$columnName_arr = $request->get('columns');
$order_arr = $request->get('order');
$search_arr = $request->get('search');
$columnIndex = $columnIndex_arr[0]['column']; // Column index
$columnName = $columnName_arr[$columnIndex]['data']; // Column name
$columnSortOrder = $order_arr[0]['dir']; // asc or desc
$searchValue = $search_arr['value']; // Search value
} else {
$draw = 0;
}
#endregion
// Fetch records
$records = UserLog::query();
$records->whereNotIn('user_id', [1]);
if (isset($columnName))
$records->orderBy($columnName, $columnSortOrder);
if (isset($searchValue)) {
$records->where(function ($query) use ($searchValue) {
$query->where('user_name', 'like', '%' . $searchValue . '%')
->orwhere('action_detail', 'like', '%' . $searchValue . '%')
->orwhere('creator_id', 'like', '%' . $searchValue . '%')
->orwhere('remark', 'like', '%' . $searchValue . '%');
// ->orwhere('outlet_id', 'like', '%' . $searchValue . '%')
// ->orwhere('created_at', 'like', '%' . $searchValue . '%')
});
}
//時間限制
if (isset($searchByFromdate))
$records->where('created_at', ">", $searchByFromdate . ' 00:00:00');
if (isset($searchByTodate))
$records->where('created_at', "<", $searchByTodate . ' 23:59:59');
if (isset($request->action_detail))
$records->where('action_detail', $request->action_detail);
if (isset($request->username))
$records->whereIn('user_name', $request->username);
$totalRecords = $records->count();
$totalRecordswithFilter = $records->count();
#region DataTable 分頁(報表不分頁)
if (!isset($request->export)) {
if (isset($start))
$records->skip($start);
if (isset($rowperpage))
$records->take($rowperpage);
}
#endregion
$records = $records->get();
#region 資料處理
$data_arr = array();
$sno = 1+($request->get('start') ?? 0);
foreach ($records as $record) {
//還原用id
//報表用的欄位,避免前端錯誤,給預設值
$count = $record->count ?? 0;
$period = $record->period ?? "";
$data_arr[] = array(
"id" => $sno++, // $record->id,
"user_name" => $record->user_name,
"action_detail" => $record->action_detail,
"ip" => $record->ip,
"remark" => $record->remark,
"created_at" => Carbon::parse($record->created_at)->format('Y-m-d H:i:s'),
"count" => $count,
"period" => $period,
"last_count" => $record->last_count ?? 0,
"carnumber" => $record->carnumber,
"location" => $record->location,
);
}
#endregion
return [$draw, $totalRecords, $totalRecordswithFilter, $data_arr];
}
#endregion
// get datatable
#region DataTable (AJAX刷新用 參數 processcheckStatus 0未審 1已審 2不舉發 "99清冊用")使用
public function getDataTable(Request $request)
{
[$draw, $totalRecords, $totalRecordswithFilter, $data_arr] = $this->getData($request);
$response = array(
"draw" => intval($draw),
"iTotalRecords" => $totalRecords,
"iTotalDisplayRecords" => $totalRecordswithFilter,
"aaData" => $data_arr
);
// dd($response);
echo json_encode($response);
exit;
}
#endregion
public function getLogDataExport(Request $request)
{
$remark = '操作紀錄清冊';
$columns = ['id', 'created_at', 'user_name', 'ip', 'action_detail', 'remark', 'carnumber', 'location'];
$columnTitle = [
["【表單名稱】:", $remark],
["【違規日期區間】:", $request->searchByFromdate, " ~ ", $request->searchByTodate],
['編號', '操作時間', '使用者名稱', 'ip', '動作', '操作說明', '車號', '地點']
];
if (isset($request->searchByFromdate) && isset($request->searchByTodate)) {
$remark = $remark . ' ' . $request->searchByFromdate . ' ~ ' . $request->searchByTodate;
}
[$draw, $totalRecords, $totalRecordswithFilter, $data_arr] = $this->getData($request);
$data = array_map(function ($row) use ($columns) {
$data = array_merge(array_flip($columns), array_intersect_key($row, array_flip($columns)));
return $data;
}, $data_arr);
$fileName = 'log-' . Str::random(10) . '.xlsx';
ExportFiles::create([
'name' => $fileName,
'path' => 'public/exports',
'type' => 'xlsx',
'status' => '1',
'remark' => "操作紀錄-$remark",
'user_id' => auth('api')->user()->id,
]);
Excel::store(new ArrayExport($data, $columnTitle), 'public/exports/' . $fileName, 'local', \Maatwebsite\Excel\Excel::XLSX);
return response()->json(['url' => route('ms-export', ['fileName' => $fileName])], 200);
}
public function Export(Request $request, $fileName)
{
$file = ExportFiles::where('name', $fileName)->first();
$filePath = storage_path('app/public/exports/' . $fileName);
if (file_exists($filePath)) {
return response()->download($filePath, $file->remark . '.' . $file->type);
} else {
return response()->json(['error' => '檔案不存在']);
}
}
}

343
app/Http/Controllers/System/Monitor/PingIpController.php

@ -0,0 +1,343 @@ @@ -0,0 +1,343 @@
<?php
namespace App\Http\Controllers\System\Monitor;
use App\Exports\ArrayExportH;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\ExportFiles;
use App\Models\PingIp;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Maatwebsite\Excel\Facades\Excel;
use Illuminate\Support\Str;
class PingIpController extends Controller
{
//
public $serialNumber = [
// '20K888',
// '20K999',
// '20K003',
// '20K001',
// '20K002',
// '20K003',
// '20K004',
'24H633',
'24H634',
'24H635',
'24H636',
];
public function index()
{
// $s=[
// [
// 's' => '20K001',
// 'ip' => '192.168.100.2'
// ],
// [
// 's' => '20K002',
// 'ip' => '192.168.110.2'
// ],
// [
// 's' => '20K003',
// 'ip' => '192.168.80.2'
// ],
// [
// 's' => '20K004',
// 'ip' => '192.168.90.2'
// ],
// ];
// $t = Carbon::parse('2022-09-30 00:00:00');
// $arr=[];
// for($i = 0; $i < 1440 ;$i++){
// $r = rand(150,600);
// if( $r > 550){
// $dt = $t->addSeconds(1);
// }
// else{
// $dt = $t;
// }
// array_push($arr,[
// 'ping_time'=>$dt->format('Y-m-d H:i:s').'.'.rand(150,600),
// 'ip'=>$s[0]['ip'],
// 'serialnumber'=>$s[0]['s'],
// 'time' => 1
// ]);
// if( $r > 550){
// $dt = $t->addSeconds(-1);
// }
// else{
// $dt = $t;
// }
// $t = $t->addMinutes(1);
// }
// // dd($arr);
// foreach($arr as $a){
// PingIp::create($a);
// }
// $t = Carbon::parse('2022-09-30 00:00:00');
// $arr=[];
// for($i = 0; $i < 1440 ;$i++){
// $r = rand(150,600);
// if( $r > 550){
// $dt = $t->addSeconds(1);
// }
// else{
// $dt = $t;
// }
// array_push($arr,[
// 'ping_time'=>$dt->format('Y-m-d H:i:s').'.'.rand(150,600),
// 'ip'=>$s[1]['ip'],
// 'serialnumber'=>$s[1]['s'],
// 'time' => 1
// ]);
// if( $r > 550){
// $dt = $t->addSeconds(-1);
// }
// else{
// $dt = $t;
// }
// $t = $t->addMinutes(1);
// }
// // dd($arr);
// foreach($arr as $a){
// PingIp::create($a);
// }
// $t = Carbon::parse('2022-09-30 00:00:00');
// $arr=[];
// for($i = 0; $i < 1440 ;$i++){
// $r = rand(150,600);
// if( $r > 550){
// $dt = $t->addSeconds(1);
// }
// else{
// $dt = $t;
// }
// array_push($arr,[
// 'ping_time'=>$dt->format('Y-m-d H:i:s').'.'.rand(150,600),
// 'ip'=>$s[2]['ip'],
// 'serialnumber'=>$s[2]['s'],
// 'time' => 1
// ]);
// if( $r > 550){
// $dt = $t->addSeconds(-1);
// }
// else{
// $dt = $t;
// }
// $t = $t->addMinutes(1);
// }
// // dd($arr);
// foreach($arr as $a){
// PingIp::create($a);
// }
// $t = Carbon::parse('2022-09-30 00:00:00');
// $arr=[];
// for($i = 0; $i < 1440 ;$i++){
// $r = rand(150,600);
// if( $r > 550){
// $dt = $t->addSeconds(1);
// }
// else{
// $dt = $t;
// }
// array_push($arr,[
// 'ping_time'=>$dt->format('Y-m-d H:i:s').'.'.rand(150,600),
// 'ip'=>$s[3]['ip'],
// 'serialnumber'=>$s[3]['s'],
// 'time' => 1
// ]);
// if( $r > 550){
// $dt = $t->addSeconds(-1);
// }
// else{
// $dt = $t;
// }
// $t = $t->addMinutes(1);
// }
// // dd($arr);
// foreach($arr as $a){
// PingIp::create($a);
// }
$serialNumber = $this->serialNumber;
return view('system.monitor.ping', compact('serialNumber'));
}
#region 輸出excel
public function ExportExcel(Request $request, $status)
{
// dd($request->all(),$processcheckStatus);
#region 設備限制
// $device = explode(',', auth()->user()->devicetype);
#endregion
[$draw, $totalRecords, $totalRecordswithFilter, $data_arr] = $this->getData($request, $status);
$columns = ['ping_time', 'serialnumber', 'ip', 'time'];
$columnTitle = [
["【表單名稱】:". "前端攝影機設備狀態"],
["【日期區間】:". $request->searchByFromdate. " ~ ". $request->searchByTodate],
['檢測時間', '設備編號', 'IP', '延遲(ms)']
];
// $data_arr = $data_arr->toArray();
$data = array_map(function ($row) use ($columns) {
$data = array_merge(array_flip($columns), array_intersect_key($row, array_flip($columns)));
return $data;
}, $data_arr);
$fileName = 'ping-' . Str::random(10) . '.xlsx';
try {
DB::beginTransaction();
ExportFiles::create([
'name' => $fileName,
'path' => 'public/exports/monitor/',
'type' => 'xlsx',
'status' => '1',
'remark' => "PingIP報表匯出",
'user_id' => auth('api')->user()->id,
]);
$option = [
'printDef' => $request->printDef ?? 0,
'showPN' => $request->showPN ?? 0,
'showBorder' => $request->showBorder ?? 0,
];
Excel::store(new ArrayExportH($data, $columnTitle, $option), 'public/exports/monitor/' . $fileName, 'local', \Maatwebsite\Excel\Excel::XLSX);
DB::commit();
} catch (\Throwable $th) {
Log::error("報表匯出失敗: " . $th->getMessage());
DB::rollBack();
}
return response()->json(['url' => route('system.filedownload', ['path' => $fileName])], 200);
}
#endregion
public function getDataTable(Request $request, $status)
{
[$draw, $totalRecords, $totalRecordswithFilter, $data_arr] = $this->getData($request, $status);
$response = array(
"draw" => intval($draw),
"iTotalRecords" => $totalRecords,
"iTotalDisplayRecords" => $totalRecordswithFilter,
"aaData" => $data_arr
);
echo json_encode($response);
exit;
}
#region 取得資料
function getData($request, $status)
{
// 審查狀態 0 未審 1 已審查 2 不舉發 99清冊
// $status = 2;
// dd($device, $request, $status);
# 日期搜尋值
if (isset($request->searchByFromdate))
$searchByFromdate = $request->searchByFromdate;
if (isset($request->searchByTodate))
$searchByTodate = $request->searchByTodate;
//地點搜尋
if (isset($request->location))
$location = $request->location;
#region DataTable 搜尋屬性
$draw = $request->get('draw');
$start = $request->get("start");
$rowperpage = $request->get("length"); // Rows display per page
$columnIndex_arr = $request->get('order');
$columnName_arr = $request->get('columns');
$order_arr = $request->get('order');
$search_arr = $request->get('search');
if ($status != 999){
$columnIndex = $columnIndex_arr[0]['column']; // Column index
$columnName = $columnName_arr[$columnIndex]['data']; // Column name
$columnSortOrder = $order_arr[0]['dir']; // asc or desc
$searchValue = $search_arr['value']; // Search value
}
#endregion
// Fetch records
if ($status == 99) {
$serialNumber = $this->serialNumber;
$records = [];
foreach ($serialNumber as $s) {
$p = PingIp::where("serialnumber", $s)->orderby('ping_time', 'desc')->first();
if (isset($p))
array_push($records, $p);
}
$totalRecords = count($records);
$totalRecordswithFilter = count($records);
} else {
$records = PingIp::query();
$totalRecords = $records->count();
if (isset($columnName))
$records->orderBy($columnName, $columnSortOrder);
// if (isset($searchValue))
// $records->where('carnumber', 'like', '%' . $searchValue . '%');
//已審未審
//時間限制
if (isset($searchByFromdate))
$records->where('ping_time', ">", $searchByFromdate . ' 00:00:00');
else
$records->where('ping_time', ">", Carbon::now()->subMonths(36));
if (isset($searchByTodate))
$records->where('ping_time', "<", $searchByTodate . ' 23:59:59');
//地點篩選
if (isset($location))
$records->where('serialnumber', $location);
$totalRecordswithFilter = $records->count();
$records->select('*');
if (isset($start))
$records->skip($start);
if (isset($rowperpage))
$records->take($rowperpage);
$records = $records->get();
}
$data_arr = array();
$sno = $start + 1;
// dd($records);
foreach ($records as $record) {
//還原用id
$id = $record->id;
//不舉發原因
$ip = $record->ip;
$symbol = $record->symbol;
$ping_time = $record->ping_time;
$serialnumber = $record->serialnumber;
$time = $record->time;
$ttl = $record->ttl;
$status = $record->status;
$data_arr[] = array(
"id" => $id,
"ip" => $ip,
"time" => $time,
"symbol" => $symbol,
"serialnumber" => $serialnumber,
"ping_time" => $ping_time,
"ttl" => $ttl,
"status" => $status,
// "searchByFromdate" => $searchByFromdate,
// "searchByTodate"=> $searchByTodate
);
}
return [$draw, $totalRecords, $totalRecordswithFilter, $data_arr];
}
#endregion
}

987
app/Http/Controllers/System/MultisysController.php

@ -0,0 +1,987 @@ @@ -0,0 +1,987 @@
<?php
namespace App\Http\Controllers\System;
use App\Class\LogWriter;
use App\Exports\ArrayExport;
use App\Http\Controllers\Controller;
use App\Models\ExportFiles;
use App\Models\Multisys;
use App\Models\MultisysEquipment;
use App\Models\ViolationLaw;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Maatwebsite\Excel\Facades\Excel;
use Illuminate\Support\Str;
use ZipArchive;
class MultisysController extends Controller
{
public $destPath_root;
public $clientDestPath_root;
public $unreportPath_root;
public $month;
function __construct()
{
$this->middleware('permission:ms-review|ms-reduction|ms-device-setting|ms-statistics|ms-analysis|ms-read', ['only' => ['Review']]);
$this->middleware('permission:ms-review|permission:ms-reduction', ['only' => ['update']]);
$this->middleware('permission:ms-device-setting', ['only' => ['DeviceSetting']]);
$this->middleware('permission:ms-statistics', ['only' => ['Statistics']]);
$this->middleware('permission:ms-analysis', ['only' => ['Analysis']]);
// OK path
$this->destPath_root = 'D:\\OK\\MS\\';
$this->clientDestPath_root = 'Y:\\';
// NOK path
$this->unreportPath_root = 'C:\\xampp\\SMMS\\storage\\app\\ParsingFiles\\NOK\\MS\\';
$this->month['start'] = Carbon::now()->subDays(8)->format('Y-m-d');
$this->month['current'] = Carbon::now()->subDays(1)->format('Y-m-d');
}
public function Review()
{
$device = explode(',', auth()->user()->device) ?? [1];
// dd($device);
$equipmentData = new MultisysEquipment();
$serialNumber = $equipmentData::whereIn('serialnumber', $device)->groupBy('serialnumber')->get(['serialnumber', 'location']);
$violationparking = new Multisys();
$locationtype = $violationparking::where('processcheck', 0)->whereIn('serialnumber', $device)->groupBy('location')->pluck('location');
// dd($data,$page_data);
$violationtype = MultiSys::select('violationtype', 'location', 'serialnumber')->groupby('serialnumber')->orderby('serialnumber', 'asc')->get();
$violationtype2 = [];
foreach ($violationtype as $tmp_vio) {
array_push($violationtype2, $tmp_vio->violationtype);
}
$violationtype2 = array_unique($violationtype2);
$vio_arr = [];
foreach (ViolationLaw::whereIn('type', ['未禮讓行人', '路口未淨空', '未依標誌標線號誌行駛', '闖紅燈', '紅燈越線', '紅燈右轉'])->where('sort','!=','-1')->get() as $key => $item) {
$vio_arr[$key]['violationcode'] = $item->violationcode;
$vio_arr[$key]['display_name'] = $item->display_name;
}
// dd($vio_arr);
return view('system.multisys.index')
->with('serialNumber', $serialNumber)
->with('locationtype', $locationtype)
->with('violationtype', $violationtype)
->with('violationtype2', $violationtype2)
->with('vio_arr', $vio_arr)
->with('month', $this->month);
}
#region 已審 View
public function Censored(Request $request)
{
$device = explode(',', auth()->user()->device) ?? [1];
$equipmentData = new MultisysEquipment();
$serialNumber = $equipmentData::whereIn('serialnumber', $device)->groupBy('serialnumber')->get(['serialnumber', 'location']);
$violationparking = new Multisys();
$locationtype = $violationparking::where('processcheck', 1)->whereIn('serialnumber', $device)->groupBy('location')->pluck('location');
$violationtype = MultiSys::select('violationtype', 'location', 'serialnumber')->groupby('serialnumber')->orderby('serialnumber', 'asc')->get();
$violationtype2 = [];
foreach ($violationtype as $tmp_vio) {
array_push($violationtype2, $tmp_vio->violationtype);
}
$violationtype2 = array_unique($violationtype2);
// dd($data,$page_data);
return view('system.multisys.manage')
->with('serialNumber', $serialNumber)
->with('locationtype', $locationtype)
->with('violationtype', $violationtype)
->with('violationtype2', $violationtype2)
->with('month', $this->month);
}
#endregion
#region 不舉發 View
public function Unreport(Request $request)
{
$device = explode(',', auth()->user()->device) ?? [1];
$equipmentData = new MultisysEquipment();
$serialNumber = $equipmentData::whereIn('serialnumber', $device)->groupBy('serialnumber')->get(['serialnumber', 'location']);
$violationparking = new Multisys();
$locationtype = $violationparking::where('processcheck', 2)->whereIn('serialnumber', $device)->groupBy('location')->pluck('location');
$unreportreason = $violationparking->where('processcheck', 2)->whereIn('serialnumber', $device)->groupBy('unreportreason')->pluck('unreportreason');
// dd($locationtype,$unreportreason);
return view('system.multisys.unreport')
->with('serialNumber', $serialNumber)
->with('locationtype', $locationtype)
->with('unreportreason', $unreportreason)
->with('month', $this->month);
}
#endregion
public function update(Request $request, $id)
{
$data = Multisys::find($id);
// 先確認 processcheck 狀態 0 修改 1 舉發 2 不舉發
// 確認使用者是否可以 review
DB::beginTransaction();
try {
// 如果使用者具 審查權限
if (auth('api')->user()->can('ms-review')) {
// 修改 processcheck = 0
if ($request->processcheck == 0) {
$this->updateIni($data->picture, $data->carnumber, "$request->carnumber", $data->violationtype, $request->violationtype);
$data->carnumber = $request->carnumber ?? $data->carnumber;
$data->violationtype = $request->violationtype ?? $data->violationtype;
$data->cartype = $request->cartype ?? $data->cartype;
$data->violationcode = $request->viocode ?? $data->violationcode;
$logData = [
'action' => 'edit',
'action_detail' => '編輯案件',
'ip' => request()->ip(),
'remark' => "編輯路口多功能案件: $data->carnumber ,案件日期: $data->datatime",
'carnumber' => $data->carnumber,
'location' => $data->location,
];
}
// 舉發 processcheck = 1
elseif ($request->processcheck == 1) {
if ($request->carnumber == null) {
return response()->json(['error' => '請輸入車牌號碼']);
}
if (isset($request->viocode)) {
if ($request->viocode == null)
return response()->json(['error' => '請選擇法條代碼']);
// 如果法條代碼不再資料表中
if (!ViolationLaw::where('violationcode', $request->viocode)->exists()) {
return response()->json(['error' => '此法條代碼不存在']);
}
} else {
return response()->json(['error' => '請選擇法條代碼']);
}
$this->updateIni($data->picture, $data->carnumber, "$request->carnumber", $data->violationtype, $request->violationtype);
$data->carnumber = $request->carnumber ?? $data->carnumber;
$data->violationtype = $request->violationtype ?? $data->violationtype;
$data->cartype = $request->cartype ?? $data->cartype;
$data->violationcode = $request->viocode ?? $data->violationcode;
$data->processcheck = 1;
$logData = [
'action' => 'review',
'action_detail' => '舉發案件',
'ip' => request()->ip(),
'remark' => "舉發路口多功能案件: $data->carnumber ,案件日期: $data->datatime",
'carnumber' => $data->carnumber,
'location' => $data->location,
];
$aaData['picture'] = $data->picture;
$aaData['picture2'] = $data->picture2;
$aaData['datatime'] = $data->datatime;
$this->saveFinishFile($aaData);
}
// 不舉發 processcheck = 2
elseif ($request->processcheck == 2) {
$data->carnumber = $request->carnumber ?? $data->carnumber;
$data->violationtype = $request->violationtype ?? $data->violationtype;
$data->cartype = $request->cartype ?? $data->cartype;
$data->unreportreason = $request->unreportreason ?? $data->unreportreason;
$data->processcheck = 2;
$logData = [
'action' => 'review',
'action_detail' => '不舉發案件',
'ip' => request()->ip(),
'remark' => "不舉發路口多功能案件: $data->carnumber ,案件日期: $data->datatime",
'carnumber' => $data->carnumber,
'location' => $data->location,
];
}
// 還原 processcheck = 3
elseif ($request->processcheck == 3) {
//路徑位置設定
$destPath_root = $this->destPath_root;
$clientDestPath_root = $this->clientDestPath_root;
$unreportPath_root = $this->unreportPath_root;
// $fileName = explode('*', $data->picture)[4]; // 抓取檔名(寫死的)
$fileName = explode('*', $data->picture)[3];
// $fileName2 = explode('*', $data->picture2)[3];
$time = str_replace('-', '', explode(' ', $data->datatime)[0]);
#region 已審照片 還原時刪除
$iniPath = $destPath_root . Auth::user()->account . "\\" . $time . '\\' . str_replace('jpg', 'ini', $fileName);
$photoPath = $destPath_root . Auth::user()->account . "\\" . $time . '\\' . $fileName;
// $photoPath2 = $destPath_root.Auth::user()->account."\\".$time.'\\'.$fileName2;
$clientDestIniPath = $clientDestPath_root . $time . '\\' . str_replace('jpg', 'ini', $fileName); // 第二路徑測試機用不到
$clientDestPhotoPath = $clientDestPath_root . $time . '\\' . $fileName;
// $clientDestPhotoPath2 = $clientDestPath_root.Auth::user()->account."\\".$time.'\\'.$fileName2;
if (file_exists($iniPath)) {
unlink($iniPath);
}
if (file_exists($photoPath)) {
unlink($photoPath);
}
// if (file_exists($photoPath2)) { unlink($photoPath2);}
if (file_exists($clientDestIniPath)) {
unlink($clientDestIniPath);
}
if (file_exists($clientDestPhotoPath)) {
unlink($clientDestPhotoPath);
}
// if (file_exists($clientDestPhotoPath2)) { unlink($clientDestPhotoPath2);}
#endregion
#region 不舉發合成圖片 還原時刪除
$unreportiniPath = $unreportPath_root . $time . '\\' . str_replace('jpg', 'ini', $fileName);
$unreportphotoPath = $unreportPath_root . $time . '\\' . $fileName;
if (file_exists($unreportiniPath)) {
unlink($unreportiniPath);
}
if (file_exists($unreportphotoPath)) {
unlink($unreportphotoPath);
}
#endregion
$data->violationcode = null;
$data->unreportreason = '';
$data->processcheck = 0;
$logData = [
'action' => 'reduction',
'action_detail' => '還原案件',
'ip' => request()->ip(),
'remark' => "還原路口多功能案件: $data->carnumber ,案件日期: $data->datatime",
'carnumber' => $data->carnumber,
'location' => $data->location,
];
}
}
// 紀錄審查者 帳號
$data->jsoncheck = auth('api')->user()->account;
// 資料儲存
$data->save();
// 紀錄 LOG
LogWriter::writeLog($logData, 'api');
DB::commit();
// all good
return response()->json(['success' => $logData['remark']]);
} catch (\Exception $e) {
DB::rollback();
return response()->json(['error' => '系統錯誤']);
// something went wrong
}
}
#region DataTable (AJAX刷新用 參數 processcheckStatus 0未審 1已審 2不舉發 "99清冊用")使用
public function getDataTable(Request $request)
{
// dd($request->all());
#region 設備限制
$device = explode(',', auth("api")->user()->device) ?? [1];
// dd($device);
#endregion
$processcheckStatus = $request->processcheckStatus;
// dd($processcheckStatus, $device);
[$draw, $totalRecords, $totalRecordswithFilter, $data_arr] = $this->getData($device, $request, $processcheckStatus);
$response = array(
"draw" => intval($draw),
"iTotalRecords" => $totalRecords,
"iTotalDisplayRecords" => $totalRecordswithFilter,
"aaData" => $data_arr
);
// dd($response);
echo json_encode($response);
exit;
}
#endregion
#region 取得資料
function getData($device, $request, $processcheckStatus = null)
{
// 審查狀態 0 未審 1 已審查 2 不舉發 99清冊
// $processcheckStatus = 2;
// dd($device, $request, $processcheckStatus);
#region Request 搜尋值
if (isset($request->searchByFromdate))
$searchByFromdate = $request->searchByFromdate;
if (isset($request->searchByTodate))
$searchByTodate = $request->searchByTodate;
//地點搜尋
if (isset($request->location))
$location = $request->location;
//車牌搜尋
if (isset($request->carnumber))
$carnumber = $request->carnumber;
//不舉發理由搜尋
if (isset($request->unreportreason))
$unreportreason = $request->unreportreason;
#endregion
#region DataTable 搜尋屬性
if (!isset($request->export)) {
$draw = $request->get('draw');
$start = $request->get("start");
$rowperpage = $request->get("length"); // Rows display per page
$columnIndex_arr = $request->get('order');
$columnName_arr = $request->get('columns');
$order_arr = $request->get('order');
$search_arr = $request->get('search');
$columnIndex = $columnIndex_arr[0]['column']; // Column index
$columnName = $columnName_arr[$columnIndex]['data']; // Column name
$columnSortOrder = $order_arr[0]['dir']; // asc or desc
$searchValue = $search_arr['value']; // Search value
} else {
$draw = 0;
}
#endregion
// Fetch records
$records = Multisys::query();
if (isset($columnName))
$records->orderBy($columnName, $columnSortOrder);
if (isset($searchValue))
$records->where('carnumber', 'like', '%' . $searchValue . '%');
//已審未審
//清冊用 99
$records->where('violationtype', '!=', '違規臨時停車');
if (isset($request->violationtype)) {
$records->whereIn('violationtype', $request->violationtype);
}
#region 審查狀態
if ($processcheckStatus == 99) {
$processcheckStatus = $request->processCheck;
if ($processcheckStatus == 99 || $processcheckStatus == null)
$records->whereIn('processcheck', [0, 1, 2]);
else {
$records->where('processcheck', $processcheckStatus);
}
} else {
$records->where('processcheck', $processcheckStatus);
}
#endregion
#region 常用搜尋條件
//設備限制
$records->whereIn('serialnumber', $device);
//時間限制
if (isset($searchByFromdate))
$records->where('datatime', ">", $searchByFromdate . ' 00:00:00');
if (isset($searchByTodate))
$records->where('datatime', "<", $searchByTodate . ' 23:59:59');
//地點篩選
if (isset($location))
$records->where('serialnumber', $location);
//車牌篩選
if (isset($carnumber))
$records->where('carnumber', 'like', '%' . $carnumber . '%');
//不舉發理由
if (isset($unreportreason))
$records->where('unreportreason', 'like', '%' . $unreportreason . '%');
#endregion
$totalRecords = $records->count();
$totalRecordswithFilter = $records->count();
#region 報表類型
//如果有報表類型(1,2,3,...),並需要特別處理欄位的報表
if (isset($request->statisticstype)) {
if ($request->statisticstype == 2)
$records->groupBy('serialnumber', 'cartype')->select('*', DB::raw('count(*) as count'));
else if ($request->statisticstype == 3) {
$currentYear = date('Y');
$lastYear = $currentYear - 1;
$groupbyPara1 = "CAST(DATE_FORMAT(datatime, '%H') AS INT)";
$records->groupBy(DB::raw($groupbyPara1), 'serialnumber', 'violationtype')->select('*', DB::raw('COUNT(*) as count, CONCAT(CAST(DATE_FORMAT(datatime,"%H") as INT),"-",CAST(DATE_FORMAT(DATE_ADD(datatime,INTERVAL +1 HOUR),"%H") as INT)) AS period'));
// $records->addSelect(DB::raw("(SELECT COUNT(*) FROM multisys AS sub_records WHERE DATE_FORMAT(sub_records.datatime,'%Y') = '$lastYear' AND sub_records.serialnumber = multisys.serialnumber AND sub_records.violationtype = multisys.violationtype) AS last_count"));
$records->orderBy(DB::raw($groupbyPara1), 'ASC');
} else if ($request->statisticstype == 4) {
if ($request->searchByYear)
$records->whereYear('datatime', $request->searchByYear);
$records->groupBy('serialnumber', 'cartype')->select('*', DB::raw('count(*) as count'));
// 去年同期資料 與上述條件相同
$lastYear = $request->searchByYear - 1;
// $records->addSelect(DB::raw("(SELECT COUNT(*) FROM multisys AS sub_records WHERE DATE_FORMAT(sub_records.datatime,'%Y') = '$lastYear' AND sub_records.serialnumber = multisys.serialnumber AND sub_records.cartype = multisys.cartype) AS last_count"));
// dd($records->toSql(), $records->getBindings());
} else if ($request->statisticstype == 5) {
}
} else {
$records->select('*');
}
#endregion
#region DataTable 分頁(報表不分頁)
if (!isset($request->export)) {
if (isset($start))
$records->skip($start);
if (isset($rowperpage))
$records->take($rowperpage);
}
#endregion
$records = $records->get();
#region 資料處理
$data_arr = array();
// $sno = $start + 1;
foreach ($records as $record) {
//還原用id
$id = $record->id;
//不舉發原因
$unreportreason = $record->unreportreason;
$datatime = $record->datatime;
$serialnumber = $record->serialnumber;
$location = $record->location;
$carnumber = $record->carnumber;
$picture = $record->picture;
$picture2 = $record->picture2;
$violationtype = $record->violationtype;
$violationcode = $record->violationcode;
$cartype = $record->cartype;
$unreportpicture = $record->unreportpicture;
$processcheck = $record->processcheck;
$postcheck = $record->postcheck ?? 0;
//報表用的欄位,避免前端錯誤,給預設值
$count = $record->count ?? 0;
$period = $record->period ?? "";
$cartypeTitle = [
'1' => "汽車",
'2' => "機車",
'3' => "重型機車",
'4' => "輕型機車",
'8' => "微型電動二輪車",
'砂石車' => "砂石車"
];
$processCheckTitle = [
'0' => "未審",
'1' => "已審查",
'2' => "不舉發"
];
if (!isset($cartype))
$cartype = 1;
if ($violationcode != null) {
$display_name = ViolationLaw::where('violationcode', $violationcode)->first()->display_name ?? '';
$violationdata = "[$violationcode]" . $display_name;
}
$data_arr[] = array(
"id" => $id,
"datatime" => $datatime,
"serialnumber" => $serialnumber,
"location" => $location,
"carnumber" => $carnumber,
"picture" => $picture,
"picture2" => $picture2,
//影片連結 (用照片的路徑取代成mp4 picture= _A picture2= _B)
"video" => str_replace('.jpg', '.mp4', $picture),
"violationtype" => $violationtype,
"violationdata" => $violationdata ?? '',
"lawType" => $record->lawType ?? '',
"cartype" => $cartype,
"carkind" => $cartypeTitle[$cartype],
"unreportreason" => $unreportreason ?? '',
"unreportpicture" => $unreportpicture ?? $picture,
"postcheck" => $postcheck,
// 清冊用
"processcheck" => $processCheckTitle[$processcheck],
// "searchByFromdate" => $searchByFromdate,
// "searchByTodate"=> $searchByTodate,
"count" => $count,
"period" => $period,
"last_count" => $record->last_count ?? 0
);
}
#endregion
#region 如果需要去年同期比較
$data_last_year = [];
if (isset($request->searchByFromdate) && isset($request->searchByTodate) && $processcheckStatus == 99) {
$searchByFromdate = $request->searchByFromdate;
$searchByTodate = $request->searchByTodate;
$searchByFromdate = Carbon::parse($searchByFromdate)->subYear()->format('Y-m-d');
$searchByTodate = Carbon::parse($searchByTodate)->subYear()->format('Y-m-d');
$records = Multisys::query();
$records->where('violationtype', '!=', '違規臨時停車');
$records->where('datatime', ">", $searchByFromdate . ' 00:00:00');
$records->where('datatime', "<", $searchByTodate . ' 23:59:59');
$records->whereIn('processcheck', [0, 1, 2]);
$records->whereIn('serialnumber', $device);
if ($request->statisticstype == 2)
$records->groupBy('serialnumber', 'cartype')->select('*', DB::raw('count(*) as count'));
if ($request->statisticstype == 3) {
$groupbyPara1 = "CAST(DATE_FORMAT(datatime, '%H') AS INT)";
$currentYear = date('Y');
$lastYear = $currentYear - 1;
$records->groupBy(DB::raw($groupbyPara1), 'serialnumber', 'violationtype')->select('*', DB::raw('COUNT(*) as count, CONCAT(CAST(DATE_FORMAT(datatime,"%H") as INT),"-",CAST(DATE_FORMAT(DATE_ADD(datatime,INTERVAL +1 HOUR),"%H") as INT)) AS period'));
$records->addSelect(DB::raw("(SELECT COUNT(*) FROM multisys AS sub_records WHERE DATE_FORMAT(sub_records.datatime,'%Y') = '$lastYear' AND sub_records.serialnumber = multisys.serialnumber AND sub_records.violationtype = multisys.violationtype) AS last_count"));
$records->orderBy(DB::raw($groupbyPara1), 'ASC');
}
$records = $records->get();
foreach ($records as $record) {
$data_last_year[] = array(
"datatime" => $record->datatime,
"serialnumber" => $record->serialnumber,
"location" => $record->location,
"carnumber" => $record->carnumber,
"picture" => $record->picture,
"picture2" => $record->picture2,
"violationtype" => $record->violationtype,
"cartype" => $record->cartype,
"unreportreason" => $record->unreportreason ?? '',
"unreportpicture" => $record->unreportpicture ?? $record->picture,
"processcheck" => $processCheckTitle[$record->processcheck],
"count" => $record->count ?? 0,
"period" => $record->period ?? "",
"last_count" => $record->last_count ?? 0
);
}
}
return [$draw, $totalRecords, $totalRecordswithFilter, $data_arr, $data_last_year];
}
#endregion
// 清冊用
public function Statistics()
{
$device = explode(',', auth()->user()->device) ?? [1];
// 違規地點 group by
$equipmentData = new MultisysEquipment();
$serialNumber = $equipmentData::whereIn('serialnumber', $device)->groupBy('serialnumber')->get(['serialnumber', 'location']);
// 違規類型 group by
$violationparking = new Multisys();
$violationtype = $violationparking::whereIn('serialnumber', $device)->groupBy('violationtype')->pluck('violationtype');
return view('system.multisys.statistics')
->with('serialNumber', $serialNumber)
->with('violationtype', $violationtype);
}
public function StatisticsYears()
{
$device = explode(',', auth()->user()->device) ?? [1];
// 違規地點 group by
$equipmentData = new MultisysEquipment();
$serialNumber = $equipmentData::whereIn('serialnumber', $device)->groupBy('serialnumber')->get(['serialnumber', 'location']);
// 違規類型 group by
$violationparking = new Multisys();
$violationtype = $violationparking::whereIn('serialnumber', $device)->groupBy('violationtype')->pluck('violationtype');
return view('system.multisys.statistics_years')
->with('serialNumber', $serialNumber)
->with('violationtype', $violationtype);
}
public function getStatisticsData(Request $request)
{
#region 設備限制
$device = explode(',', auth("api")->user()->device) ?? [1];
#endregion
$processcheckStatus = 99;
[$draw, $totalRecords, $totalRecordswithFilter, $data_arr, $data_arr2] = $this->getData($device, $request, $processcheckStatus);
$response = array(
"draw" => intval($draw),
"iTotalRecords" => $totalRecords,
"iTotalDisplayRecords" => $totalRecordswithFilter,
"aaData" => $data_arr
);
return $response;
}
public function getStatisticsDataExport(Request $request)
{
#region 設備限制
$device = explode(',', auth("api")->user()->device) ?? [1];
#endregion
$processcheckStatus = 99;
if ($request->statisticstype == 1) {
$remark = '違規件數清冊';
$columns = ['datatime', 'serialnumber', 'location', 'carnumber', 'violationtype', 'processcheck'];
$columnTitle = [
["【表單名稱】:", $remark],
["【違規日期區間】:", $request->searchByFromdate, " ~ ", $request->searchByTodate],
['違規時段', '設備編號', '違規地點', '車號', '違規類型', '審查狀態']
];
} elseif ($request->statisticstype == 2) {
$remark = '違規件數統計清冊';
$columns = ['serialnumber', 'location', 'violationtype', 'carkind', 'count'];
$columnTitle = [
["【表單名稱】:", $remark],
["【違規日期區間】:", $request->searchByFromdate, " ~ ", $request->searchByTodate],
['設備編號', '違規地點', '違規類型', '車種', '統計']
];
} elseif ($request->statisticstype == 3) {
$remark = '違規時段件數統計清冊';
$columns = ['serialnumber', 'location', 'period', 'violationtype', 'count'];
$columnTitle = [
["【表單名稱】:", $remark],
["【違規日期區間】:", $request->searchByFromdate, " ~ ", $request->searchByTodate],
['設備編號', '違規地點', '違規時段', '違規類型', '統計']
];
} elseif ($request->statisticstype == 3) {
$remark = '違規時段件數統計清冊';
$columns = ['serialnumber', 'location', 'period', 'violationtype', 'count'];
$columnTitle = [
["【表單名稱】:", $remark],
["【違規日期區間】:", $request->searchByFromdate, " ~ ", $request->searchByTodate],
['設備編號', '違規地點', '違規時段', '違規類型', '統計']
];
}
if (isset($request->searchByFromdate) && isset($request->searchByTodate)) {
}
[$draw, $totalRecords, $totalRecordswithFilter, $data_arr, $data_arr2] = $this->getData($device, $request, $processcheckStatus);
$data = array_map(function ($row) use ($columns) {
$data = array_merge(array_flip($columns), array_intersect_key($row, array_flip($columns)));
return $data;
}, $data_arr);
$fileName = 'ms-' . Str::random(10) . '.xlsx';
ExportFiles::create([
'name' => $fileName,
'path' => 'public/exports',
'type' => 'xlsx',
'status' => '1',
'remark' => "路口多功能-$remark",
'user_id' => auth('api')->user()->id,
]);
Excel::store(new ArrayExport($data, $columnTitle), 'public/exports/' . $fileName, 'local', \Maatwebsite\Excel\Excel::XLSX);
return response()->json(['url' => route('export', ['fileName' => $fileName])], 200);
}
public function Export(Request $request, $fileName)
{
$file = ExportFiles::where('name', $fileName)->first();
$filePath = storage_path('app/public/exports/' . $fileName);
if (file_exists($filePath)) {
return response()->download($filePath, $file->remark . '.' . $file->type);
} else {
return response()->json(['error' => '檔案不存在']);
}
}
// 數據分析 vpkAnalysis
public function Analysis()
{
$device = explode(',', auth()->user()->device) ?? [1];
// 違規地點 group by
$equipmentData = new MultisysEquipment();
$serialNumber = $equipmentData::whereIn('serialnumber', $device)->groupBy('serialnumber')->get(['serialnumber', 'location']);
// 違規類型 group by
$violationparking = new Multisys();
$violationtype = $violationparking::whereIn('serialnumber', $device)->groupBy('violationtype')->pluck('violationtype');
return view('system.multisys.analysis')
->with('serialNumber', $serialNumber)
->with('violationtype', $violationtype);
}
public function getAnalysisData(Request $request)
{
$device = explode(',', auth("api")->user()->device) ?? [1];
$records = Multisys::query();
$records->whereIn('serialnumber', $device)->whereIn('processcheck', [1, 3]);
// dd($request->all());
if (isset($request->search_fromdate))
$records->where('datatime', ">", $request->search_fromdate . ' 00:00:00');
if (isset($request->search_todate))
$records->where('datatime', "<", $request->search_todate . ' 23:59:59');
//地點搜尋
if (isset($request->location))
$records->where('serialnumber', $request->location);
if (isset($request->violation_type))
$records->where('violationtype', $request->violation_type);
$data = $records->get();
if (isset($request->chart_type)) {
if ($request->chart_type == '1') {
// 根據違規類型分類 並計算每一類的總數
$data = $data->groupBy('violationtype')->map(function ($item, $key) {
return [
'title' => $key,
'times' => $item->count()
];
})->toArray();
$data = array_values($data);
} elseif ($request->chart_type == '2') {
// 根據違規類型分類 並計算每一類的總數
$data = $data->groupBy('location')->map(function ($item, $key) {
return [
'title' => $key,
'times' => $item->count()
];
})->toArray();
$data = array_values($data);
} elseif ($request->chart_type == '3') {
$records2 = Multisys::query();
$records2->whereIn('serialnumber', $device)->whereIn('processcheck', [1, 3]);
// dd($request->all());
if (isset($request->search_fromdate))
$records2->where('datatime', ">", $request->search_fromdate . ' 00:00:00');
if (isset($request->search_todate))
$records2->where('datatime', "<", $request->search_todate . ' 23:59:59');
//地點搜尋
if (isset($request->location))
$records2->where('serialnumber', $request->location);
if (isset($request->violation_type))
$records2->where('violationtype', $request->violation_type);
$violationTypes = $records2->groupBy('violationtype')->select('violationtype')->get()->toArray();
$violationTypes = array_column($violationTypes, 'violationtype');
// violationTypes key value 互換
$violationTypesKey = array_flip($violationTypes);
// dd($violationTypes);
// 初始化結果陣列
$response = [];
for ($hour = 0; $hour <= 23; $hour++) {
// 補零 01, 02, 03, ..., 09
// $hour = str_pad($hour, 2, '0', STR_PAD_LEFT);
// 初始化結果陣列
$response[$hour] = [
'category' => $hour,
];
for ($i = 0; $i < count($violationTypes); $i++) {
$response[$hour][$i] = 0;
}
}
$data = $records->get();
$data = $data->groupBy(function ($item, $key) {
return intval(Carbon::parse($item->datatime)->format('H'));
})->map(function ($item, $key) use (&$response, $violationTypesKey) {
$item->groupBy('violationtype')->map(function ($item, $viotype) use ($key, &$response, $violationTypesKey) {
$response[$key][$violationTypesKey[$viotype]] = $item->count();
});
});
$data = $response;
// dd($data);
}
}
// dd($data);
return response()->json(['data' => $data, 'chart_type' => $request->chart_type, 'violationtype' => $violationTypes ?? ''], 200);
}
// statistics
// public function statistics()
// {
// return view('system.multisys.statistics');
// }
public function getStatisticsTest(Request $request)
{
$request->statisticstype;
// 讀取json file (storage/app/data/get-ms-statistics-1~3.json )
$json = file_get_contents(storage_path('app/data/get-ms-statistics-1.json'));
}
//getLocationByViolationtype
public function getLocationByViolationtype(Request $request)
{
$device = explode(',', auth()->user()->device) ?? [1];
$records = Multisys::query();
$records->whereIn('serialnumber', $device);
if (isset($request->violationtype))
$records->whereIn('violationtype', $request->violationtype);
$records->groupBy('serialnumber')->get(['serialnumber', 'location']);
$data = $records->get();
return response()->json(['data' => $data]);
}
#region 更新Ini
function updateIni($path, $oldCarNumber, $newCarNumber, $oldViolationType, $newViolationType)
{
// $oldCarNumber = explode('"', "$oldCarNumber")[1];
$iniPath = str_replace('jpg', 'ini', str_replace('*', '\\', storage_path('app' . '\\' . 'ParsingFiles' . '\\' . "$path")));
$iniFile = fopen("$iniPath", "r+");
$iniData = fread($iniFile, filesize("$iniPath"));
$iniData = iconv('Big5', 'UTF-8', "$iniData");
// if(strpos($iniData,'車號')){
// $iniData = str_replace("$oldCarNumber", "$newCarNumber", "$iniData");
// }else{
// $iniData = str_replace("\r\n操作者姓名", "\r\n車號="."$newCarNumber"."\r\n操作者姓名", "$iniData");
// }
$iniData = str_replace("$oldCarNumber", "$newCarNumber", "$iniData");
$iniData = str_replace("$oldViolationType", "$newViolationType", "$iniData");
$iniData = iconv('UTF-8', 'Big5', "$iniData");
$iniFile = fopen("$iniPath", "w+");
fwrite($iniFile, "$iniData");
fclose($iniFile);
}
function saveFinishFile($data)
{
//路徑位置設定
$destPath_root = $this->destPath_root;
$clientDestPath_root = $this->clientDestPath_root;
// $ftpPath_root = $this->ftpPath_root;
$path = $data['picture'];
// $path2 = $data['picture2'];
$time = str_replace('-', '', explode(' ', $data['datatime'])[0]);
$iniPath = str_replace('jpg', 'ini', str_replace('*', '\\', storage_path('app' . '\\' . 'ParsingFiles' . '\\' . "$path")));
$photoPath = str_replace('*', '\\', storage_path('app' . '\\' . 'ParsingFiles' . '\\' . "$path"));
// $photoPath2 = str_replace('*','\\', storage_path('app'.'\\'.'ParsingFiles'.'\\'."$path2"));
// $photoName = explode('*', $data['picture'])[4]; //原始有"時" 資料夾的層數
$photoName = explode('*', $data['picture'])[3];
// $photoName2 = explode('*', $data['picture2'])[3];
$destPath = $destPath_root . Auth::user()->account . "\\" . $time;
if (is_dir($destPath) === false) {
mkdir($destPath, 0777, true);
}
try {
copy($iniPath, $destPath . '\\' . str_replace('jpg', 'ini', $photoName));
copy($photoPath, $destPath . '\\' . $photoName);
// copy($photoPath2, $destPath.'\\'.$photoName2);
// 存入第二路徑供客戶操作資料
$clientDestPath = $clientDestPath_root . $time;
if (is_dir($clientDestPath) === false) {
mkdir($clientDestPath, 0777, true);
}
copy($iniPath, $clientDestPath . '\\' . str_replace('jpg', 'ini', $photoName));
copy($photoPath, $clientDestPath . '\\' . $photoName);
// copy($photoPath2, $clientDestPath.'\\'.$photoName2);
// 存入第FTP路徑供客戶操作資料
// $ftpPath = $ftpPath_root.$time;
// if( is_dir($ftpPath) === false ) { mkdir($ftpPath, 0777, true);}
// copy($iniPath, $ftpPath.'\\'.str_replace('jpg','ini', $photoName));
// copy($photoPath, $ftpPath.'\\'.$photoName);
} catch (\Throwable $th) {
//throw $th;
}
}
#endregion
#region 下載ZIP
function downloadZipPicture(Request $request)
{
set_time_limit(0);
ini_set('memory_limit', '2048M');
$device = explode(',', auth("api")->user()->device) ?? [1];
if (isset($request->searchByFromdate))
$searchByFromdate = $request->searchByFromdate;
if (isset($request->searchByTodate))
$searchByTodate = $request->searchByTodate;
if (isset($request->location))
$serialnumber = $request->location;
if (isset($request->carnumber))
$carnumber = $request->carnumber;
if (isset($request->processcheck))
$processcheck = $request->processcheck;
else $processcheck = 1;
$records = Multisys::query();
$records->where('location', 'not like', '%測試%');
$records->whereIn('serialnumber', $device);
$records->where('processcheck', $processcheck);
if (isset($searchByFromdate))
$records->where('datatime', ">", $searchByFromdate . ' 00:00:00');
else {
$records->where('datatime', ">", Carbon::now()->subMonths(2));
$searchByFromdate = '';
}
if (isset($searchByTodate))
$records->where('datatime', "<", $searchByTodate . ' 23:59:59');
else $searchByTodate = '';
if (isset($serialnumber))
$records->where('serialnumber', $request->location);
if (isset($carnumber))
$records->where('carnumber', 'like', '%' . $carnumber . '%');
$records = $records->pluck("picture");
for ($i = 0; $i < count($records); $i++) {
$records[$i] = storage_path("app\\ParsingFiles\\" . str_replace('*', '\\', $records[$i]));
}
$fileName = $this->downloadZip($records, $request->location, $searchByFromdate, $searchByTodate, $processcheck);
if (file_exists($fileName)) {
ExportFiles::create([
'name' => $fileName,
'path' => 'public',
'type' => 'zip',
'status' => '1',
'remark' => "路口多功能-下載ZIP",
'user_id' => auth('api')->user()->id,
]);
return $fileName;
} else return "沒有檔案";
}
public function downloadZip($files, $serialnumber, $searchByFromdate, $searchByTodate, $processcheck)
{
set_time_limit(0);
ini_set('memory_limit', '2048M');
try {
foreach (glob(public_path() . '\*.zip') as $file) {
unlink($file);
}
} catch (\Throwable $th) {
//throw $th;
}
$zip = new ZipArchive;
$rand = Str::random(5);
$fileName = "_$rand.zip";
if (isset($searchByTodate))
$fileName = str_replace('-', '', $searchByTodate) . $fileName;
if (isset($searchByFromdate))
$fileName = str_replace('-', '', $searchByFromdate) . "_" . $fileName;
if (isset($serialnumber))
$fileName = $serialnumber . "_" . $fileName;
if (isset($processcheck)) {
if ($processcheck == 1)
$fileName = "MS_OK_" . $fileName;
if ($processcheck == 2)
$fileName = "MS_X_" . $fileName;
}
if ($zip->open(public_path($fileName), ZipArchive::CREATE) === TRUE) {
foreach ($files as $file) {
try {
$name = explode("\\", $file);
$iniPath = str_replace('.jpg', '.ini', $file);
$iniName = explode("\\", $iniPath);
if (end($name) != '') {
$zip->addFile($file, end($name));
$zip->addFile($iniPath, end($iniName));
}
} catch (\Throwable $th) {
//throw $th;
Log::error($th);
}
}
$zip->close();
}
// response()->download(public_path($fileName))->deleteFileAfterSend(true);
return "$fileName";
}
#endregion
}

264
app/Http/Controllers/System/MultisysEquipmentController.php

@ -0,0 +1,264 @@ @@ -0,0 +1,264 @@
<?php
namespace App\Http\Controllers\System;
use App\Class\LogWriter;
use App\Models\MultisysEquipment;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\AssetOwnership;
use App\Models\CustodyUnit;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class MultisysEquipmentController extends Controller
{
public function __construct()
{
$this->middleware('permission:ms-device-setting');
}
public function page()
{
// ['custodyunit' => $custodyunit, 'assetownership' => $assetownership]
$custodyunit = CustodyUnit::all();
$assetownership = AssetOwnership::all();
return view('system.multisys.equipment')
->with('custodyunit', $custodyunit)
->with('assetownership', $assetownership);
}
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
#region DataTable 搜尋屬性
$draw = $request->get('draw');
$start = $request->get("start");
$rowperpage = $request->get("length"); // Rows display per page
$columnIndex_arr = $request->get('order');
$columnName_arr = $request->get('columns');
$order_arr = $request->get('order');
$search_arr = $request->get('search');
$columnIndex = $columnIndex_arr[0]['column']; // Column index
$columnName = $columnName_arr[$columnIndex]['data']; // Column name
$columnSortOrder = $order_arr[0]['dir']; // asc or desc
$searchValue = $search_arr['value']; // Search value
#endregion
// Role with permissions
$records = MultisysEquipment::query();
$totalRecords = $records->count();
if (isset($columnName))
$records->orderBy($columnName, $columnSortOrder);
if (isset($searchValue)) {
$records->where(function ($query) use ($searchValue) {
// $query->where('outlet_id', 'like', '%' . $searchValue . '%')
// ->orwhere('name', 'like', '%' . $searchValue . '%')
// ->orwhere('serialnumber', 'like', '%' . $searchValue . '%')
// ->orwhere('creator_id', 'like', '%' . $searchValue . '%');
});
}
$totalRecordswithFilter = $records->count();
if (isset($start))
$records->skip($start);
if (isset($rowperpage))
$records->take($rowperpage);
$data_arr = $records->get();
$response = array(
"draw" => intval($draw),
"iTotalRecords" => $totalRecords,
"iTotalDisplayRecords" => $totalRecordswithFilter,
"aaData" => $data_arr
);
return response()->json($response);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$device = new MultisysEquipment();
// 如果缺少必要欄位
DB::beginTransaction();
try {
$device->serialnumber = $request->serialnumber;
$device->brand = $request->brand;
$device->model = $request->model;
$device->custodyunit_id = $request->custodyunit_id;
$device->assetownership_id = $request->assetownership_id;
$device->buydate = $request->buydate;
$device->activatedate = $request->activatedate;
$device->locationid = $request->locationid;
$device->location = $request->location;
$device->comment = $request->comment;
$device->precinct = $request->precinct;
$device->station = $request->station;
$device->save();
$logData = [
'action' => 'create',
'action_detail' => '新增設備',
'ip' => request()->ip(),
'remark' => "新增設備:{$device->serialnumber}",
];
logWriter::writeLog($logData, 'api');
DB::commit();
return response()->json(['success' => true, 'message' => '新增成功']);
} catch (\Throwable $th) {
//throw $th;
Log::error($th);
DB::rollBack();
return response()->json(['success' => false, 'message' => '新增失敗']);
}
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
$device = MultisysEquipment::find($id);
return response()->json(['success' => true, 'data' => $device]);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id)
{
$device = MultisysEquipment::find($id);
if (!$request->serialnumber || !$request->location || !$request->precinct || !$request->station) {
return response()->json(['success' => false, 'message' => '缺少必要欄位']);
}
DB::beginTransaction();
try {
$device->serialnumber = $request->serialnumber;
$device->brand = $request->brand;
$device->model = $request->model;
$device->custodyunit_id = $request->custodyunit_id ?? null;
$device->assetownership_id = $request->assetownership_id ?? null;
$device->buydate = $request->buydate;
$device->activatedate = $request->activatedate;
$device->locationid = $request->locationid;
$device->location = $request->location;
$device->comment = $request->comment;
$device->precinct = $request->precinct;
$device->station = $request->station;
$device->save();
$logData = [
'action' => 'update',
'action_detail' => '更新設備',
'ip' => request()->ip(),
'remark' => "更新設備:{$device->serialnumber}",
];
logWriter::writeLog($logData, 'api');
DB::commit();
return response()->json(['success' => true, 'message' => '更新成功']);
} catch (\Throwable $th) {
//throw $th;
Log::error($th);
DB::rollBack();
return response()->json(['success' => false, 'message' => '更新失敗']);
}
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
$device = MultisysEquipment::find($id);
try {
$device->delete();
return response()->json(['success' => true, 'message' => '刪除成功']);
} catch (\Throwable $th) {
//throw $th;
Log::error($th);
return response()->json(['success' => false, 'message' => '刪除失敗']);
}
}
/**
* 取得保管單位資料
*/
public function indexUnit(Request $request)
{
if($request->type == "custodyunit"){
$unit = CustodyUnit::all();
return response()->json(['success' => true, 'data' => $unit]);
}else if ($request->type == "assetownership"){
$unit = AssetOwnership::all();
return response()->json(['success' => true, 'data' => $unit]);
}
return response()->json(['success' => false, 'message' => '取得失敗']);
}
/**
* 新增單位資料
*/
public function storeUnit(Request $request)
{
// custodyunit_id
//assetownership_id)
DB::beginTransaction();
try {
if($request->type == "custodyunit"){
$unit = new CustodyUnit();
$unit->name = $request->name;
$unit->save();
$logData = [
'action' => 'create',
'action_detail' => '新增保管單位',
'ip' => request()->ip(),
'remark' => "新增保管單位:{$request->name}",
];
LogWriter::writeLog($logData, 'api');
DB::commit();
return response()->json(['success' => true, 'message' => '保管單位-新增成功']);
}else if ($request->type == "assetownership"){
$unit = new AssetOwnership();
$unit->name = $request->name;
$unit->save();
$logData = [
'action' => 'create',
'action_detail' => '新增財產所屬單位',
'ip' => request()->ip(),
'remark' => "新增財產所屬單位:{$request->name}",
];
LogWriter::writeLog($logData, 'api');
DB::commit();
return response()->json(['success' => true, 'message' => '財產所屬單位-新增成功']);
}
} catch (\Throwable $th) {
//throw $th;
Log::error($th);
DB::rollBack();
}
return response()->json(['success' => false, 'message' => '新增失敗']);
}
}

897
app/Http/Controllers/System/OverSpeedController.php

@ -0,0 +1,897 @@ @@ -0,0 +1,897 @@
<?php
namespace App\Http\Controllers\System;
use App\Class\LogWriter;
use App\Exports\ArrayExport;
use App\Http\Controllers\Controller;
use App\Models\ExportFiles;
use App\Models\OverSpeedRed;
use App\Models\OverSpeedRedEquipment;
use App\Models\ViolationLaw;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Facades\Excel;
use Illuminate\Support\Str;
use Intervention\Image\ImageManager;
use Intervention\Image\Drivers\Gd\Driver;
use ZipArchive;
class OverSpeedController extends Controller
{
public $destPath_root;
public $clientDestPath_root;
public $unreportPath_root;
public $month;
function __construct()
{
$this->middleware('permission:overspeed-review|overspeed-reduction|overspeed-device-setting|overspeed-statistics|overspeed-analysis|overspeed-read', ['only' => ['Review']]);
$this->middleware('permission:overspeed-review|permission:overspeed-reduction', ['only' => ['update']]);
$this->middleware('permission:overspeed-device-setting', ['only' => ['DeviceSetting']]);
$this->middleware('permission:overspeed-statistics', ['only' => ['Statistics']]);
$this->middleware('permission:overspeed-analysis', ['only' => ['Analysis']]);
// OK path
$this->destPath_root = 'D:\\OK\\OverSpeed\\';
$this->clientDestPath_root = 'Y:\\';
// NOK path
$this->unreportPath_root = 'C:\\xampp\\SMMS\\storage\\app\\ParsingFiles\\NOK\\OverSpeed\\';
$this->month['start'] = Carbon::now()->subDays(8)->format('Y-m-d');
$this->month['current'] = Carbon::now()->subDays(1)->format('Y-m-d');
}
public function Review()
{
$device = explode(',', auth()->user()->device) ?? [1];
// dd($device);
$equipmentData = new OverSpeedRedEquipment();
$serialNumber = $equipmentData::whereIn('serialnumber', $device)->groupBy('serialnumber')->get(['serialnumber', 'location']);
$violationparking = new OverSpeedRed();
$locationtype = $violationparking::where('processcheck', 0)->whereIn('violationtype', ['超速'])->whereIn('serialnumber', $device)->groupBy('location')->pluck('location');
// dd($data,$page_data);
$vio_arr = [];
foreach (ViolationLaw::whereIn('type', ['超速'])->get() as $key => $item) {
$vio_arr[$key]['violationcode'] = $item->violationcode;
$vio_arr[$key]['display_name'] = $item->display_name;
}
// dd($vio_arr);
return view('system.overspeed.index')
->with('serialNumber', $serialNumber)
->with('locationtype', $locationtype)
->with('vio_arr', $vio_arr)
->with('month', $this->month);
}
#region 已審 View
public function Censored(Request $request)
{
$device = explode(',', auth()->user()->device) ?? [1];
$equipmentData = new OverSpeedRedEquipment();
$serialNumber = $equipmentData::whereIn('serialnumber', $device)->groupBy('serialnumber')->get(['serialnumber', 'location']);
$violationparking = new OverSpeedRed();
$locationtype = $violationparking::where('processcheck', 1)->whereIn('violationtype', ['超速'])->whereIn('serialnumber', $device)->groupBy('location')->pluck('location');
// dd($data,$page_data);
return view('system.overspeed.manage')
->with('serialNumber', $serialNumber)
->with('locationtype', $locationtype)
->with('month', $this->month);
}
#endregion
#region 不舉發 View
public function Unreport(Request $request)
{
$device = explode(',', auth()->user()->device) ?? [1];
$equipmentData = new OverSpeedRedEquipment();
$serialNumber = $equipmentData::whereIn('serialnumber', $device)->groupBy('serialnumber')->get(['serialnumber', 'location']);
$violationparking = new OverSpeedRed();
$locationtype = $violationparking::where('processcheck', 2)->whereIn('violationtype', ['超速'])->whereIn('serialnumber', $device)->groupBy('location')->pluck('location');
$unreportreason = $violationparking->where('processcheck', 2)->whereIn('violationtype', ['超速'])->whereIn('serialnumber', $device)->groupBy('unreportreason')->pluck('unreportreason');
// dd($locationtype,$unreportreason);
return view('system.overspeed.unreport')
->with('serialNumber', $serialNumber)
->with('locationtype', $locationtype)
->with('unreportreason', $unreportreason)
->with('month', $this->month);
}
#endregion
public function mergePic(Request $request)
{
$path = $request->path;
$image = $request->image;
// dd($path,$image);
$data = OverSpeedRed::where('picture', 'like', '%' . $path . '%')->first();
// dd($path,$image,$data);
if ($data) {
// 使用 Intervention Image 套件開啟圖片檔案
$manager = new ImageManager(new Driver());
$photoPath = str_replace('*', '\\', storage_path('app' . '\\' . 'ParsingFiles' . '\\' . "$data->picture"));
$originalImage = $manager->read($photoPath);
// $originalImage = Image::read($photoPath);
$cnum = "";
// 取得前端傳來的 base64 圖片資料
$croppedImage = $manager->read($request->image);
// $croppedImage = Image::read($request->image);
$croppedImage->resize($croppedImage->width() * 4, $croppedImage->height() * 4);
// 獲取圖片的寬度和高度
$originalWidth = $originalImage->width();
$originalHeight = $originalImage->height();
$croppedWidth = $croppedImage->width();
$croppedHeight = $croppedImage->height();
// dd($originalWidth, $originalHeight, $croppedWidth, $croppedHeight, $request->position);
// 將裁切後的圖片合成於原圖
// 如果 $request->position 為 1,則將圖片合成於左下角,2 為右下角,3 為左上角,4 為右上角
switch ($request->position) {
case 1:
$originalImage->place($croppedImage, 'bottom-left');
break;
case 2:
$originalImage->place($croppedImage, 'bottom-right');
break;
case 3:
$originalImage->place($croppedImage, 'top-left', 0, 210);
break;
case 4:
$originalImage->place($croppedImage, 'top-right', 0, 210);
break;
default:
$originalImage->place($croppedImage, 'bottom-left');
break;
}
// $originalImage->place($croppedImage, 'bottom-left', 0, 0);
// 儲存合成後的圖片檔案
$photoName = explode('*', $data['picture']);
$photoName = end($photoName);
$time = str_replace('-', '', explode(' ', $data['datatime'])[0]);
$violationtype = $data['violationtype'];
$photoPath = public_path("merge\\$violationtype\\$time\\$photoName");
if (is_dir(public_path("merge\\$violationtype\\$time")) === false) {
mkdir(public_path("merge\\$violationtype\\$time"), 0777, true);
}
$originalImage->save($photoPath);
// $originalImage->save(public_path('merge\\' . $path));
// 將合成後的圖片路徑存入資料庫
// $data->mergepic = $this->merge_root.$path;
// $data->save();
return response()->json(['path' => "/merge/$violationtype/$time/$path", 'carnubmer' => $cnum], 200);
}
return response()->json(['path' => 'error'], 400);
}
public function update(Request $request, $id)
{
$data = OverSpeedRed::find($id);
// 先確認 processcheck 狀態 0 修改 1 舉發 2 不舉發
// 確認使用者是否可以 review
DB::beginTransaction();
try {
// 如果使用者具 審查權限
if (auth('api')->user()->can('overspeed-review')) {
// 修改 processcheck = 0
if ($request->processcheck == 0) {
$data->carnumber = $request->carnumber ?? $data->carnumber;
$data->violationtype = $request->violationtype ?? $data->violationtype;
$data->cartype = $request->cartype ?? $data->cartype;
$data->violationcode = $request->viocode ?? $data->violationcode;
$logData = [
'action' => 'edit',
'action_detail' => '編輯案件',
'ip' => request()->ip(),
'remark' => "編輯闖紅/超速案件: $data->carnumber ,案件日期: $data->datatime",
];
}
// 舉發 processcheck = 1
elseif ($request->processcheck == 1) {
if ($request->carnumber == null) {
return response()->json(['error' => '請輸入車牌號碼']);
}
if (isset($request->viocode)) {
if ($request->viocode == null)
return response()->json(['error' => '請選擇法條代碼']);
// 如果法條代碼不再資料表中
if (!ViolationLaw::where('violationcode', $request->viocode)->exists()) {
return response()->json(['error' => '此法條代碼不存在']);
}
} else {
return response()->json(['error' => '請選擇法條代碼']);
}
$this->updateIni($data->picture, $data->carnumber, "$request->carnumber", $data->violationtype, $request->violationtype);
$data->carnumber = $request->carnumber ?? $data->carnumber;
$data->violationtype = $request->violationtype ?? $data->violationtype;
$data->cartype = $request->cartype ?? $data->cartype;
$data->violationcode = $request->viocode ?? $data->violationcode;
$data->processcheck = 1;
$logData = [
'action' => 'review',
'action_detail' => '舉發案件',
'ip' => request()->ip(),
'remark' => "舉發闖紅/超速案件: $data->carnumber ,案件日期: $data->datatime",
];
$pth = explode('*', $data->picture);
$photoPath = str_replace('*', '\\', storage_path('app' . '\\' . 'ParsingFiles' . '\\' . "$data->picture"));
$pth = end($pth);
$violationtype = $data->violationtype;
$time = str_replace('-', '', explode(' ', $data->datatime)[0]);
// public_path('merge\\'.$path)
$mgpath = public_path("merge\\$violationtype\\$time\\$pth");
if (!file_exists($mgpath)) {
if (is_dir(public_path("merge\\$violationtype\\$time")) === false) {
mkdir(public_path("merge\\$violationtype\\$time"), 0777, true);
}
copy($photoPath, $mgpath);
}
$aaData['picture'] = $data->picture;
$aaData['picture2'] = $data->picture2;
$aaData['datatime'] = $data->datatime;
$aaData['violationtype'] = $data->violationtype;
$this->saveFinishFile($aaData);
}
// 不舉發 processcheck = 2
elseif ($request->processcheck == 2) {
$data->carnumber = $request->carnumber ?? $data->carnumber;
$data->violationtype = $request->violationtype ?? $data->violationtype;
$data->cartype = $request->cartype ?? $data->cartype;
$data->unreportreason = $request->unreportreason ?? $data->unreportreason;
$data->processcheck = 2;
$logData = [
'action' => 'review',
'action_detail' => '不舉發案件',
'ip' => request()->ip(),
'remark' => "不舉發闖紅/超速案件: $data->carnumber ,案件日期: $data->datatime",
];
}
// 還原 processcheck = 3
elseif ($request->processcheck == 3) {
$data->violationcode = null;
$data->unreportreason = '';
$data->processcheck = 0;
$logData = [
'action' => 'reduction',
'action_detail' => '還原案件',
'ip' => request()->ip(),
'remark' => "還原闖紅/超速案件: $data->carnumber ,案件日期: $data->datatime",
];
}
}
// 紀錄審查者 帳號
$data->jsoncheck = auth('api')->user()->account;
// 資料儲存
$data->save();
// 紀錄 LOG
LogWriter::writeLog($logData, 'api');
DB::commit();
// all good
return response()->json(['success' => $logData['remark']]);
} catch (\Exception $e) {
DB::rollback();
return response()->json(['error' => '系統錯誤']);
// something went wrong
}
}
#region DataTable (AJAX刷新用 參數 processcheckStatus 0未審 1已審 2不舉發 "99清冊用")使用
public function getDataTable(Request $request)
{
// dd($request->all());
#region 設備限制
$device = explode(',', auth("api")->user()->device) ?? [1];
// dd($device);
#endregion
$processcheckStatus = $request->processcheckStatus;
// dd($processcheckStatus, $device);
[$draw, $totalRecords, $totalRecordswithFilter, $data_arr] = $this->getData($device, $request, $processcheckStatus);
$response = array(
"draw" => intval($draw),
"iTotalRecords" => $totalRecords,
"iTotalDisplayRecords" => $totalRecordswithFilter,
"aaData" => $data_arr
);
// dd($response);
echo json_encode($response);
exit;
}
#endregion
#region 取得資料
function getData($device, $request, $processcheckStatus = null)
{
// 審查狀態 0 未審 1 已審查 2 不舉發 99清冊
// $processcheckStatus = 2;
// dd($device, $request, $processcheckStatus);
#region Request 搜尋值
if (isset($request->searchByFromdate))
$searchByFromdate = $request->searchByFromdate;
if (isset($request->searchByTodate))
$searchByTodate = $request->searchByTodate;
//地點搜尋
if (isset($request->location))
$location = $request->location;
//車牌搜尋
if (isset($request->carnumber))
$carnumber = $request->carnumber;
//不舉發理由搜尋
if (isset($request->unreportreason))
$unreportreason = $request->unreportreason;
#endregion
#region DataTable 搜尋屬性
if (!isset($request->export)) {
$draw = $request->get('draw');
$start = $request->get("start");
$rowperpage = $request->get("length"); // Rows display per page
$columnIndex_arr = $request->get('order');
$columnName_arr = $request->get('columns');
$order_arr = $request->get('order');
$search_arr = $request->get('search');
$columnIndex = $columnIndex_arr[0]['column']; // Column index
$columnName = $columnName_arr[$columnIndex]['data']; // Column name
$columnSortOrder = $order_arr[0]['dir']; // asc or desc
$searchValue = $search_arr['value']; // Search value
} else {
$draw = 0;
}
#endregion
// Fetch records
$records = OverSpeedRed::query();
$records->whereIn('violationtype', ['超速']);
if (isset($columnName))
$records->orderBy($columnName, $columnSortOrder);
if (isset($searchValue))
$records->where('carnumber', 'like', '%' . $searchValue . '%');
//已審未審
//清冊用 99
$records->where('violationtype', '!=', '違規臨時停車');
if ($processcheckStatus == 99) {
$processcheckStatus = $request->processCheck;
if ($processcheckStatus == 99 || $processcheckStatus == null)
$records->whereIn('processcheck', [0, 1, 2]);
else {
$records->where('processcheck', $processcheckStatus);
}
} else {
$records->where('processcheck', $processcheckStatus);
}
//設備限制
$records->whereIn('serialnumber', $device);
//時間限制
if (isset($searchByFromdate))
$records->where('datatime', ">", $searchByFromdate . ' 00:00:00');
if (isset($searchByTodate))
$records->where('datatime', "<", $searchByTodate . ' 23:59:59');
//地點篩選
if (isset($location))
$records->where('serialnumber', $location);
//車牌篩選
if (isset($carnumber))
$records->where('carnumber', 'like', '%' . $carnumber . '%');
//不舉發理由
if (isset($unreportreason))
$records->where('unreportreason', 'like', '%' . $unreportreason . '%');
$totalRecords = $records->count();
$totalRecordswithFilter = $records->count();
//如果有報表類型(1,2,3,...),並需要特別處理欄位的報表
if (isset($request->statisticstype)) {
if ($request->statisticstype == 2)
$records->groupBy('serialnumber', 'cartype')->select('*', DB::raw('count(*) as count'));
if ($request->statisticstype == 3) {
$groupbyPara1 = "CAST(DATE_FORMAT(datatime, '%H') AS INT)";
$records->groupBy(DB::raw($groupbyPara1), 'serialnumber', 'violationtype')->select('*', DB::raw('COUNT(*) as count, CONCAT(CAST(DATE_FORMAT(datatime,"%H") as INT),"-",CAST(DATE_FORMAT(DATE_ADD(datatime,INTERVAL +1 HOUR),"%H") as INT)) AS period'));
$records->orderBy(DB::raw($groupbyPara1), 'ASC');
}
} else {
$records->select('*');
}
if (!isset($request->export)) {
if (isset($start))
$records->skip($start);
if (isset($rowperpage))
$records->take($rowperpage);
}
$records = $records->get();
$data_arr = array();
// $sno = $start + 1;
foreach ($records as $record) {
//還原用id
$id = $record->id;
//不舉發原因
$unreportreason = $record->unreportreason;
$datatime = $record->datatime;
$serialnumber = $record->serialnumber;
$location = $record->location;
$carnumber = $record->carnumber;
$picture = $record->picture;
$picture2 = $record->picture2;
$violationtype = $record->violationtype;
$violationcode = $record->violationcode;
$cartype = $record->cartype;
$unreportpicture = $record->unreportpicture;
$processcheck = $record->processcheck;
$postcheck = $record->postcheck ?? 0;
//報表用的欄位,避免前端錯誤,給預設值
$count = $record->count ?? 0;
$period = $record->period ?? "";
$cartypeTitle = [
'1' => "汽車",
'2' => "機車",
'3' => "重型機車",
'4' => "輕型機車",
'8' => "微型電動二輪車"
];
$processCheckTitle = [
'0' => "未審",
'1' => "已審查",
'2' => "不舉發"
];
if (!isset($cartype))
$cartype = 1;
// 如果車牌 = None 則 顯示 ""
if ($carnumber == "None")
$carnumber = "";
if ($violationcode == null) {
if ($record->speed != 0) {
// 去除 km/h
$speed = explode('km/h', $record->speed)[0];
$speed = (int) $speed;
// 去除 km/h
$limitspeed = explode('km/h', $record->limitspeed)[0];
$limitspeed = (int) $limitspeed;
// 超速
// 如果 speed > $record->limitspeed 20以內法條 4000005 20~40 4000006 40~60 4000007
if ($speed > $limitspeed) {
$violationcode = 4000005;
if ($speed > $limitspeed + 20) {
$violationcode = 4000006;
if ($speed > $limitspeed + 40) {
$violationcode = 4000007;
}
}
}
}
}
$pthar = explode('*', $record->picture);
$pth = $pthar[count($pthar) - 1];
$time = str_replace('-', '', explode(' ', $datatime)[0]);
// public_path('merge\\'.$path)
$mgpath = public_path("merge\\$violationtype\\$time\\$pth");
$mergepic = 0;
if (file_exists($mgpath)) {
$mergepic = "merge/$violationtype/$time/$pth";
}
if ($violationcode != null) {
$display_name = ViolationLaw::where('violationcode', $violationcode)->first()->display_name ?? '';
$violationdata = "[$violationcode]" . $display_name;
// $violationdata = "[$violationcode]" . ViolationLaw::where('violationcode', $violationcode)->first()->display_name;
}
$data_arr[] = array(
"id" => $id,
"datatime" => $datatime,
"serialnumber" => $serialnumber,
"location" => $location,
"carnumber" => $carnumber,
"picture" => $picture,
"picture2" => $picture2,
//影片連結 (用照片的路徑取代成mp4 picture= _A picture2= _B)
"video" => str_replace('.jpg', '.mp4', $picture),
"violationtype" => $violationtype,
"violationcode" => $violationcode ?? '',
"violationdata" => $violationdata ?? '',
"lawType" => $record->lawType ?? '',
"cartype" => $cartype,
"carkind" => $cartypeTitle[$cartype],
"speed" => $record->speed ?? '',
"unreportreason" => $unreportreason ?? '',
"unreportpicture" => $unreportpicture ?? $picture,
"mergepic" => $mergepic ?? '0',
"postcheck" => $postcheck,
// 清冊用
"processcheck" => $processCheckTitle[$processcheck],
// "searchByFromdate" => $searchByFromdate,
// "searchByTodate"=> $searchByTodate,
"count" => $count,
"period" => $period
);
}
return [$draw, $totalRecords, $totalRecordswithFilter, $data_arr];
}
#endregion
// 清冊用
public function Statistics()
{
$device = explode(',', auth()->user()->device) ?? [1];
// 違規地點 group by
$equipmentData = new OverSpeedRedEquipment();
$serialNumber = $equipmentData::whereIn('serialnumber', $device)->groupBy('serialnumber')->get(['serialnumber', 'location']);
// 違規類型 group by
$violationparking = new OverSpeedRed();
$violationtype = $violationparking::whereIn('serialnumber', $device)->whereIn('violationtype', ['超速'])->groupBy('violationtype')->pluck('violationtype');
return view('system.overspeed.statistics')
->with('serialNumber', $serialNumber)
->with('violationtype', $violationtype);
}
public function getStatisticsData(Request $request)
{
#region 設備限制
$device = explode(',', auth("api")->user()->device) ?? [1];
#endregion
$processcheckStatus = 99;
[$draw, $totalRecords, $totalRecordswithFilter, $data_arr] = $this->getData($device, $request, $processcheckStatus);
$response = array(
"draw" => intval($draw),
"iTotalRecords" => $totalRecords,
"iTotalDisplayRecords" => $totalRecordswithFilter,
"aaData" => $data_arr
);
return $response;
}
public function getStatisticsDataExport(Request $request)
{
#region 設備限制
$device = explode(',', auth("api")->user()->device) ?? [1];
#endregion
$processcheckStatus = 99;
if ($request->statisticstype == 1) {
$remark = '違規件數清冊';
$columns = ['datatime', 'serialnumber', 'location', 'carnumber', 'violationtype', 'processcheck'];
$columnTitle = [
["【表單名稱】:", $remark],
["【違規日期區間】:", $request->searchByFromdate, " ~ ", $request->searchByTodate],
['違規時段', '設備編號', '違規地點', '車號', '違規類型', '審查狀態']
];
} elseif ($request->statisticstype == 2) {
$remark = '違規件數統計清冊';
$columns = ['serialnumber', 'location', 'violationtype', 'carkind', 'count'];
$columnTitle = [
["【表單名稱】:", $remark],
["【違規日期區間】:", $request->searchByFromdate, " ~ ", $request->searchByTodate],
['設備編號', '違規地點', '違規類型', '車種', '統計']
];
} elseif ($request->statisticstype == 3) {
$remark = '違規時段件數統計清冊';
$columns = ['serialnumber', 'location', 'period', 'violationtype', 'count'];
$columnTitle = [
["【表單名稱】:", $remark],
["【違規日期區間】:", $request->searchByFromdate, " ~ ", $request->searchByTodate],
['設備編號', '違規地點', '違規時段', '違規類型', '統計']
];
}
[$draw, $totalRecords, $totalRecordswithFilter, $data_arr] = $this->getData($device, $request, $processcheckStatus);
$data = array_map(function ($row) use ($columns) {
return array_merge(array_flip($columns), array_intersect_key($row, array_flip($columns)));
}, $data_arr);
$fileName = 'overspeed-' . Str::random(10) . '.xlsx';
ExportFiles::create([
'name' => $fileName,
'path' => 'public/exports',
'type' => 'xlsx',
'status' => '1',
'remark' => "路口多功能-$remark",
'user_id' => auth('api')->user()->id,
]);
Excel::store(new ArrayExport($data, $columnTitle), 'public/exports/' . $fileName, 'local', \Maatwebsite\Excel\Excel::XLSX);
return response()->json(['url' => route('overspeed-export', ['fileName' => $fileName])], 200);
}
public function Export(Request $request, $fileName)
{
$file = ExportFiles::where('name', $fileName)->first();
$filePath = storage_path('app/public/exports/' . $fileName);
if (file_exists($filePath)) {
return response()->download($filePath, $file->remark . '.' . $file->type);
} else {
return response()->json(['error' => '檔案不存在']);
}
}
// 數據分析 vpkAnalysis
public function Analysis()
{
$device = explode(',', auth()->user()->device) ?? [1];
// 違規地點 group by
$equipmentData = new OverSpeedRedEquipment();
$serialNumber = $equipmentData::whereIn('serialnumber', $device)->groupBy('serialnumber')->get(['serialnumber', 'location']);
// 違規類型 group by
$violationparking = new OverSpeedRed();
$violationtype = $violationparking::whereIn('serialnumber', $device)->whereIn('violationtype', ['超速'])->groupBy('violationtype')->pluck('violationtype');
return view('system.overspeed.analysis')
->with('serialNumber', $serialNumber)
->with('violationtype', $violationtype);
}
public function getAnalysisData(Request $request)
{
$device = explode(',', auth("api")->user()->device) ?? [1];
$records = OverSpeedRed::query();
$records->whereIn('serialnumber', $device)->whereIn('violationtype', ['超速'])->whereIn('processcheck', [1, 3]);
// dd($request->all());
if (isset($request->search_fromdate))
$records->where('datatime', ">", $request->search_fromdate . ' 00:00:00');
if (isset($request->search_todate))
$records->where('datatime', "<", $request->search_todate . ' 23:59:59');
//地點搜尋
if (isset($request->location))
$records->where('serialnumber', $request->location);
if (isset($request->violation_type))
$records->where('violationtype', $request->violation_type);
$data = $records->get();
if (isset($request->chart_type)) {
if ($request->chart_type == '1') {
// 根據違規類型分類 並計算每一類的總數
$data = $data->groupBy('violationtype')->map(function ($item, $key) {
return [
'title' => $key,
'times' => $item->count()
];
})->toArray();
$data = array_values($data);
} elseif ($request->chart_type == '2') {
// 根據違規類型分類 並計算每一類的總數
$data = $data->groupBy('location')->map(function ($item, $key) {
return [
'title' => $key,
'times' => $item->count()
];
})->toArray();
$data = array_values($data);
} elseif ($request->chart_type == '3') {
$records2 = OverSpeedRed::query();
$records2->whereIn('serialnumber', $device)->whereIn('processcheck', [1, 3]);
// dd($request->all());
if (isset($request->search_fromdate))
$records2->where('datatime', ">", $request->search_fromdate . ' 00:00:00');
if (isset($request->search_todate))
$records2->where('datatime', "<", $request->search_todate . ' 23:59:59');
//地點搜尋
if (isset($request->location))
$records2->where('serialnumber', $request->location);
if (isset($request->violation_type))
$records2->where('violationtype', $request->violation_type);
$violationTypes = $records2->whereIn('violationtype', ['超速'])->groupBy('violationtype')->select('violationtype')->get()->toArray();
$violationTypes = array_column($violationTypes, 'violationtype');
// violationTypes key value 互換
$violationTypesKey = array_flip($violationTypes);
// dd($violationTypes);
// 初始化結果陣列
$response = [];
for ($hour = 0; $hour <= 23; $hour++) {
// 補零 01, 02, 03, ..., 09
// $hour = str_pad($hour, 2, '0', STR_PAD_LEFT);
// 初始化結果陣列
$response[$hour] = [
'category' => $hour,
];
for ($i = 0; $i < count($violationTypes); $i++) {
$response[$hour][$i] = 0;
}
}
$data = $records->get();
$data = $data->groupBy(function ($item, $key) {
return intval(Carbon::parse($item->datatime)->format('H'));
})->map(function ($item, $key) use (&$response, $violationTypesKey) {
$item->groupBy('violationtype')->map(function ($item, $viotype) use ($key, &$response, $violationTypesKey) {
$response[$key][$violationTypesKey[$viotype]] = $item->count();
});
});
$data = $response;
// dd($data);
}
}
// dd($data);
return response()->json(['data' => $data, 'chart_type' => $request->chart_type, 'violationtype' => $violationTypes ?? ''], 200);
}
public function getStatisticsTest(Request $request)
{
$request->statisticstype;
// 讀取json file (storage/app/data/get-overspeed-statistics-1~3.json )
$json = file_get_contents(storage_path('app/data/get-overspeed-statistics-1.json'));
}
#region 更新ini檔案
function updateIni($path, $oldCarNumber, $newCarNumber, $oldViolationType, $newViolationType)
{
// $oldCarNumber = explode('"', "$oldCarNumber")[1];
$iniPath = str_replace('jpg', 'ini', str_replace('*', '\\', storage_path('app' . '\\' . 'ParsingFiles' . '\\' . "$path")));
$iniFile = fopen("$iniPath", "r+");
$iniData = fread($iniFile, filesize("$iniPath"));
$iniData = iconv('Big5', 'UTF-8', "$iniData");
// if(strpos($iniData,'車號')){
// $iniData = str_replace("$oldCarNumber", "$newCarNumber", "$iniData");
// }else{
// $iniData = str_replace("\r\n操作者姓名", "\r\n車號="."$newCarNumber"."\r\n操作者姓名", "$iniData");
// }
$iniData = str_replace("$oldCarNumber", "$newCarNumber", "$iniData");
$iniData = str_replace("$oldViolationType", "$newViolationType", "$iniData");
$iniData = iconv('UTF-8', 'Big5', "$iniData");
$iniFile = fopen("$iniPath", "w+");
fwrite($iniFile, "$iniData");
fclose($iniFile);
}
function saveFinishFile($data)
{
//路徑位置設定
$destPath_root = $this->destPath_root;
$clientDestPath_root = $this->clientDestPath_root;
// $ftpPath_root = $this->ftpPath_root;
$violationtype = $data['violationtype'];
$path = $data['picture'];
// $path2 = $data['picture2'];
$time = str_replace('-', '', explode(' ', $data['datatime'])[0]);
$iniPath = str_replace('jpg', 'ini', str_replace('*', '\\', storage_path('app' . '\\' . 'ParsingFiles' . '\\' . "$path")));
$photoPath = str_replace('*', '\\', storage_path('app' . '\\' . 'ParsingFiles' . '\\' . "$path"));
// $photoPath2 = str_replace('*','\\', storage_path('app'.'\\'.'ParsingFiles'.'\\'."$path2"));
// $photoName = explode('*', $data['picture'])[4]; //原始有"時" 資料夾的層數
$photoName = explode('*', $data['picture']);
$photoName = end($photoName);
$photoPath = public_path("merge\\$violationtype\\$time\\$photoName");
// $photoName2 = explode('*', $data['picture2'])[3];
$destPath = $destPath_root . Auth::user()->account . "\\" . $time;
if (is_dir($destPath) === false) {
mkdir($destPath, 0777, true);
}
try {
copy($iniPath, $destPath . '\\' . str_replace('jpg', 'ini', $photoName));
copy($photoPath, $destPath . '\\' . $photoName);
// copy($photoPath2, $destPath.'\\'.$photoName2);
// 存入第二路徑供客戶操作資料
$clientDestPath = $clientDestPath_root . $time;
if (is_dir($clientDestPath) === false) {
mkdir($clientDestPath, 0777, true);
}
copy($iniPath, $clientDestPath . '\\' . str_replace('jpg', 'ini', $photoName));
copy($photoPath, $clientDestPath . '\\' . $photoName);
// copy($photoPath2, $clientDestPath.'\\'.$photoName2);
// 存入第FTP路徑供客戶操作資料
// $ftpPath = $ftpPath_root.$time;
// if( is_dir($ftpPath) === false ) { mkdir($ftpPath, 0777, true);}
// copy($iniPath, $ftpPath.'\\'.str_replace('jpg','ini', $photoName));
// copy($photoPath, $ftpPath.'\\'.$photoName);
} catch (\Throwable $th) {
//throw $th;
}
}
#endregion
#region 下載ZIP
function downloadZipPicture(Request $request)
{
set_time_limit(0);
ini_set('memory_limit', '2048M');
$device = explode(',', auth("api")->user()->device) ?? [1];
if (isset($request->searchByFromdate))
$searchByFromdate = $request->searchByFromdate;
if (isset($request->searchByTodate))
$searchByTodate = $request->searchByTodate;
if (isset($request->location))
$serialnumber = $request->location;
if (isset($request->carnumber))
$carnumber = $request->carnumber;
if (isset($request->processcheck))
$processcheck = $request->processcheck;
else $processcheck = 1;
$records = OverSpeedRed::query();
$records->where('location', 'not like', '%測試%');
$records->whereIn('serialnumber', $device)->whereIn('violationtype', ['超速']);
$records->where('processcheck', $processcheck);
if (isset($searchByFromdate))
$records->where('datatime', ">", $searchByFromdate . ' 00:00:00');
else {
$records->where('datatime', ">", Carbon::now()->subMonths(2));
$searchByFromdate = '';
}
if (isset($searchByTodate))
$records->where('datatime', "<", $searchByTodate . ' 23:59:59');
else $searchByTodate = '';
if (isset($serialnumber))
$records->where('serialnumber', $request->location);
if (isset($carnumber))
$records->where('carnumber', 'like', '%' . $carnumber . '%');
$records = $records->pluck("picture");
for ($i = 0; $i < count($records); $i++) {
$records[$i] = storage_path("app\\ParsingFiles\\" . str_replace('*', '\\', $records[$i]));
}
$fileName = $this->downloadZip($records, $request->location, $searchByFromdate, $searchByTodate, $processcheck);
if (file_exists($fileName)) {
ExportFiles::create([
'name' => $fileName,
'path' => 'public',
'type' => 'zip',
'status' => '1',
'remark' => "超速-下載ZIP",
'user_id' => auth('api')->user()->id,
]);
return $fileName;
} else return "沒有檔案";
}
public function downloadZip($files, $serialnumber, $searchByFromdate, $searchByTodate, $processcheck)
{
set_time_limit(0);
ini_set('memory_limit', '2048M');
foreach (glob(public_path() . '\*.zip') as $file) {
unlink($file);
}
$zip = new ZipArchive;
$rand = Str::random(5);
$fileName = "_$rand.zip";
if (isset($searchByTodate))
$fileName = str_replace('-', '', $searchByTodate) . $fileName;
if (isset($searchByFromdate))
$fileName = str_replace('-', '', $searchByFromdate) . "_" . $fileName;
if (isset($serialnumber))
$fileName = $serialnumber . "_" . $fileName;
if (isset($processcheck)) {
if ($processcheck == 1)
$fileName = "OSR_OK_" . $fileName;
if ($processcheck == 2)
$fileName = "OSR_X_" . $fileName;
}
if ($zip->open(public_path($fileName), ZipArchive::CREATE) === TRUE) {
foreach ($files as $file) {
$name = explode("\\", $file);
$iniPath = str_replace('.jpg', '.ini', $file);
$iniName = explode("\\", $iniPath);
if (end($name) != '') {
$zip->addFile($file, end($name));
$zip->addFile($iniPath, end($iniName));
}
}
$zip->close();
}
// response()->download(public_path($fileName))->deleteFileAfterSend(true);
return "$fileName";
}
#endregion
}

258
app/Http/Controllers/System/OverSpeedRedEquipmentController.php

@ -0,0 +1,258 @@ @@ -0,0 +1,258 @@
<?php
namespace App\Http\Controllers\System;
use App\Class\LogWriter;
use App\Models\OverSpeedRedEquipment;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\AssetOwnership;
use App\Models\CustodyUnit;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class OverSpeedRedEquipmentController extends Controller
{
public function __construct()
{
$this->middleware('permission:overspeed-device-setting|red-device-setting');
}
public function page()
{
// ['custodyunit' => $custodyunit, 'assetownership' => $assetownership]
$custodyunit = CustodyUnit::all();
$assetownership = AssetOwnership::all();
return view('system.overspeed.equipment')
->with('custodyunit', $custodyunit)
->with('assetownership', $assetownership);
}
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
#region DataTable 搜尋屬性
$draw = $request->get('draw');
$start = $request->get("start");
$rowperpage = $request->get("length"); // Rows display per page
$columnIndex_arr = $request->get('order');
$columnName_arr = $request->get('columns');
$order_arr = $request->get('order');
$search_arr = $request->get('search');
$columnIndex = $columnIndex_arr[0]['column']; // Column index
$columnName = $columnName_arr[$columnIndex]['data']; // Column name
$columnSortOrder = $order_arr[0]['dir']; // asc or desc
$searchValue = $search_arr['value']; // Search value
#endregion
// Role with permissions
$records = OverSpeedRedEquipment::query();
$totalRecords = $records->count();
if (isset($columnName))
$records->orderBy($columnName, $columnSortOrder);
if (isset($searchValue)) {
$records->where(function ($query) use ($searchValue) {
// $query->where('outlet_id', 'like', '%' . $searchValue . '%')
// ->orwhere('name', 'like', '%' . $searchValue . '%')
// ->orwhere('serialnumber', 'like', '%' . $searchValue . '%')
// ->orwhere('creator_id', 'like', '%' . $searchValue . '%');
});
}
$totalRecordswithFilter = $records->count();
if (isset($start))
$records->skip($start);
if (isset($rowperpage))
$records->take($rowperpage);
$data_arr = $records->get();
$response = array(
"draw" => intval($draw),
"iTotalRecords" => $totalRecords,
"iTotalDisplayRecords" => $totalRecordswithFilter,
"aaData" => $data_arr
);
return response()->json($response);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$device = new OverSpeedRedEquipment();
DB::beginTransaction();
try {
$device->serialnumber = $request->serialnumber;
$device->brand = $request->brand;
$device->model = $request->model;
$device->custodyunit_id = $request->custodyunit_id;
$device->assetownership_id = $request->assetownership_id;
$device->buydate = $request->buydate;
$device->activatedate = $request->activatedate;
$device->locationid = $request->locationid;
$device->location = $request->location;
$device->comment = $request->comment;
$device->precinct = $request->precinct;
$device->station = $request->station;
$device->save();
$logData = [
'action' => 'create',
'action_detail' => '新增設備',
'ip' => request()->ip(),
'remark' => "新增設備:{$device->serialnumber}",
];
logWriter::writeLog($logData, 'api');
DB::commit();
return response()->json(['success' => true, 'message' => '新增成功']);
} catch (\Throwable $th) {
//throw $th;
Log::error($th);
DB::rollBack();
return response()->json(['success' => false, 'message' => '新增失敗']);
}
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
$device = OverSpeedRedEquipment::find($id);
return response()->json(['success' => true, 'data' => $device]);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id)
{
$device = OverSpeedRedEquipment::find($id);
DB::beginTransaction();
try {
$device->serialnumber = $request->serialnumber;
$device->brand = $request->brand;
$device->model = $request->model;
$device->custodyunit_id = $request->custodyunit_id ?? null;
$device->assetownership_id = $request->assetownership_id ?? null;
$device->buydate = $request->buydate;
$device->activatedate = $request->activatedate;
$device->locationid = $request->locationid;
$device->location = $request->location;
$device->comment = $request->comment;
$device->precinct = $request->precinct;
$device->station = $request->station;
$device->save();
$logData = [
'action' => 'update',
'action_detail' => '更新設備',
'ip' => request()->ip(),
'remark' => "更新設備:{$device->serialnumber}",
];
logWriter::writeLog($logData, 'api');
DB::commit();
return response()->json(['success' => true, 'message' => '更新成功']);
} catch (\Throwable $th) {
//throw $th;
Log::error($th);
DB::rollBack();
return response()->json(['success' => false, 'message' => '更新失敗']);
}
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
$device = OverSpeedRedEquipment::find($id);
try {
$device->delete();
return response()->json(['success' => true, 'message' => '刪除成功']);
} catch (\Throwable $th) {
//throw $th;
Log::error($th);
return response()->json(['success' => false, 'message' => '刪除失敗']);
}
}
/**
* 取得保管單位資料
*/
public function indexUnit(Request $request)
{
if($request->type == "custodyunit"){
$unit = CustodyUnit::all();
return response()->json(['success' => true, 'data' => $unit]);
}else if ($request->type == "assetownership"){
$unit = AssetOwnership::all();
return response()->json(['success' => true, 'data' => $unit]);
}
return response()->json(['success' => false, 'message' => '取得失敗']);
}
/**
* 新增單位資料
*/
public function storeUnit(Request $request)
{
// custodyunit_id
//assetownership_id)
DB::beginTransaction();
try {
if($request->type == "custodyunit"){
$unit = new CustodyUnit();
$unit->name = $request->name;
$unit->save();
$logData = [
'action' => 'create',
'action_detail' => '新增保管單位',
'ip' => request()->ip(),
'remark' => "新增保管單位:{$request->name}",
];
LogWriter::writeLog($logData, 'api');
DB::commit();
return response()->json(['success' => true, 'message' => '保管單位-新增成功']);
}else if ($request->type == "assetownership"){
$unit = new AssetOwnership();
$unit->name = $request->name;
$unit->save();
$logData = [
'action' => 'create',
'action_detail' => '新增財產所屬單位',
'ip' => request()->ip(),
'remark' => "新增財產所屬單位:{$request->name}",
];
LogWriter::writeLog($logData, 'api');
DB::commit();
return response()->json(['success' => true, 'message' => '財產所屬單位-新增成功']);
}
} catch (\Throwable $th) {
//throw $th;
Log::error($th);
DB::rollBack();
}
return response()->json(['success' => false, 'message' => '新增失敗']);
}
}

179
app/Http/Controllers/System/Permissions/RoleController.php

@ -0,0 +1,179 @@ @@ -0,0 +1,179 @@
<?php
namespace App\Http\Controllers\System\Permissions;
use App\Class\LogWriter;
use App\Http\Controllers\Controller;
use App\Models\Permission;
use App\Models\Role;
use Illuminate\Http\Request;
class RoleController extends Controller
{
function __construct()
{
$this->middleware('permission:role-list|role-create|role-edit|role-delete', ['only' => ['RoleManager', 'show', 'index']]);
$this->middleware('permission:role-create', ['only' => ['storeRole']]);
$this->middleware('permission:role-edit', ['only' => ['updateRole']]);
$this->middleware('permission:role-delete', ['only' => ['deleteRole']]);
}
#region 管理群組(角色)管理
public function RoleManager()
{
$typeName = [
0 => "檢視管理群組",
1 => "違規停車-審查",
2 => "區間-審查",
3 => "路口-審查",
4 => "禁行車種-審查",
5 => "檢視使用者",
];
$permissions = Permission::query();
// .env 檔案中的權限類型SYSTEM_MULTISYS SYSTEM_OVERSPEEDRED SYSTEM_VIOLATIONPARKING
$types = [1, 6];
// dd($types);
if (env('SYSTEM_MULTISYS', false)) {
array_push($types, 4);
}
if (env('SYSTEM_INTERVAL', false)) {
array_push($types, 3);
}
if (env('SYSTEM_OVERSPEEDRED', false)) {
array_push($types, 7);
array_push($types, 8);
}
if (env('SYSTEM_VIOLATIONPARKING', false)) {
array_push($types, 2);
}
// dd($types);
$permissions = $permissions->whereIn('type', $types);
$permissions = $permissions->orderBy('type', 'asc')->orderBy('sort', 'asc')->get();
// 將權限根據 type 分類 並且排序
$permissions = $permissions->sortBy('type')->groupBy('type');
// dd(env('SYSTEM_MULTISYS', false), env('SYSTEM_OVERSPEEDRED', false), env('SYSTEM_VIOLATIONPARKING', false));
// dd($permissions->toArray());
return view('system.setting.RoleManager')->with('permissions', $permissions);
}
public function index(Request $request)
{
// 如果沒有登入
if (!auth('api')->check()) {
return response()->json(['message' => 'Unauthenticated.'], 401);
}
#region DataTable 搜尋屬性
$draw = $request->get('draw');
$start = $request->get("start");
$rowperpage = $request->get("length"); // Rows display per page
$columnIndex_arr = $request->get('order');
$columnName_arr = $request->get('columns');
$order_arr = $request->get('order');
$search_arr = $request->get('search');
$columnIndex = $columnIndex_arr[0]['column']; // Column index
$columnName = $columnName_arr[$columnIndex]['data']; // Column name
$columnSortOrder = $order_arr[0]['dir']; // asc or desc
$searchValue = $search_arr['value']; // Search value
#endregion
// Role with permissions
$records = Role::query();
$records = $records->with('permissions');
$records = $records->whereNotIn('id', [1]);
$totalRecords = $records->count();
if (isset($columnName))
$records->orderBy($columnName, $columnSortOrder);
if (isset($searchValue)) {
$records->where(function ($query) use ($searchValue) {
// $query->where('outlet_id', 'like', '%' . $searchValue . '%')
// ->orwhere('name', 'like', '%' . $searchValue . '%')
// ->orwhere('serialnumber', 'like', '%' . $searchValue . '%')
// ->orwhere('creator_id', 'like', '%' . $searchValue . '%');
});
}
$totalRecordswithFilter = $records->count();
$data_arr = $records->get();
$response = array(
"draw" => intval($draw),
"iTotalRecords" => $totalRecords,
"iTotalDisplayRecords" => $totalRecordswithFilter,
"aaData" => $data_arr
);
return response()->json($response);
}
// 新增管理群組
public function store(Request $request)
{
$data = $request->all();
$idx = Role::max('id') + 1;
$data['name'] = "role-$idx";
$data['guard_name'] = '*';
$role = Role::create($data);
$role->syncPermissions($request->permission);
$logData = [
'action' => 'create',
'action_detail' => '新增管理群組',
'ip' => request()->ip(),
'remark' => "新增管理群組:$role->display_name",
];
LogWriter::writeLog($logData, 'api');
return response()->json(['success' => '新增成功']);
}
public function show(Request $request, $id)
{
if (!auth('api')->check()) {
return response()->json(['message' => 'Unauthenticated.'], 401);
}
$role = Role::with('permissions')->find($id);
$permissions = Permission::orderBy('type', 'asc')->orderBy('sort', 'asc')->get();
return response()->json(['role' => $role, 'permissions' => $permissions]);
}
public function update(Request $request, $id)
{
if ($id == 1) {
return response()->json(['error' => '此群組不可編輯']);
}
$role = Role::find($id);
$role->update($request->all());
// sync permissions
$role->syncPermissions($request->permission);
$logData = [
'action' => 'edit',
'action_detail' => '編輯管理群組',
'ip' => request()->ip(),
'remark' => "編輯管理群組:$role->display_name",
];
LogWriter::writeLog($logData, 'api');
return response()->json(['success' => '編輯成功']);
}
// 刪除管理群組
public function destory(Request $request, $id)
{
if ($id == 1) {
return response()->json(['error' => '此群組不可刪除']);
}
$role = Role::find($id);
$role->delete();
$logData = [
'action' => 'delete',
'action_detail' => '刪除管理群組',
'ip' => request()->ip(),
'remark' => "刪除管理群組:$role->display_name",
];
LogWriter::writeLog($logData, 'api');
return response()->json(['success' => '刪除成功']);
}
#endregion
}

255
app/Http/Controllers/System/Permissions/UserController.php

@ -0,0 +1,255 @@ @@ -0,0 +1,255 @@
<?php
namespace App\Http\Controllers\System\Permissions;
use App\Class\LogWriter;
use App\Exports\ArrayExport;
use App\Http\Controllers\Controller;
use App\Models\ExportFiles;
use App\Models\MultisysEquipment;
use App\Models\OverSpeedRed;
use App\Models\OverSpeedRedEquipment;
use App\Models\Permission;
use App\Models\Role;
use App\Models\User;
use App\Models\IntervalEquipment;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use PhpParser\Node\Expr\AssignOp\Mul;
use Illuminate\Support\Str;
use Maatwebsite\Excel\Facades\Excel;
class UserController extends Controller
{
function __construct()
{
$this->middleware('permission:user-list|user-create|user-edit|user-delete', ['only' => ['UserManager', 'show', 'index']]);
$this->middleware('permission:user-create', ['only' => ['storeUser']]);
$this->middleware('permission:user-edit', ['only' => ['updateUser']]);
$this->middleware('permission:user-delete', ['only' => ['deleteUser']]);
}
#region 使用者管理
public function UserManager()
{
$permissions = Permission::all();
$roles = Role::where('id', '!=', 1)->get();
$equipments = [
// "violationparking" => ViolationParkingEquipment::all(),
"multisys" => MultisysEquipment::all(),
"overspeedred" => OverSpeedRedEquipment::all(),
"interval"=> IntervalEquipment::all(),
];
$equipments_title = [
"multisys" => "路口多功能系統",
"overspeedred" => "闖紅燈超速",
"interval" => "區間測速",
];
$allow = explode(",", auth()->user()->device);
// dd($roles->toArray());
return view('system.setting.UserManager')
->with('permissions', $permissions)
->with('roles', $roles)
->with('equipments_title', $equipments_title)
->with('equipments', $equipments)
->with('allow', $allow);
}
public function index(Request $request)
{
#region DataTable 搜尋屬性
$draw = $request->get('draw');
$start = $request->get("start");
$rowperpage = $request->get("length"); // Rows display per page
$columnIndex_arr = $request->get('order');
$columnName_arr = $request->get('columns');
$order_arr = $request->get('order');
$search_arr = $request->get('search');
$columnIndex = $columnIndex_arr[0]['column']; // Column index
$columnName = $columnName_arr[$columnIndex]['data']; // Column name
$columnSortOrder = $order_arr[0]['dir']; // asc or desc
$searchValue = $search_arr['value']; // Search value
#endregion
// Role with permissions
$records = User::query();
$records = $records->with('roles');
$records = $records->whereNotIn('id', [1]);
$totalRecords = $records->count();
if (isset($columnName))
$records->orderBy($columnName, $columnSortOrder);
if (isset($searchValue)) {
$records->where(function ($query) use ($searchValue) {
// $query->where('outlet_id', 'like', '%' . $searchValue . '%')
// ->orwhere('name', 'like', '%' . $searchValue . '%')
// ->orwhere('serialnumber', 'like', '%' . $searchValue . '%')
// ->orwhere('creator_id', 'like', '%' . $searchValue . '%');
});
}
$totalRecordswithFilter = $records->count();
$data_arr = $records->get();
$response = array(
"draw" => intval($draw),
"iTotalRecords" => $totalRecords,
"iTotalDisplayRecords" => $totalRecordswithFilter,
"aaData" => $data_arr
);
return response()->json($response);
}
// 新增使用者
public function store(Request $request)
{
if(isset($request->device)){
$device = [];
foreach ($request->device as $key => $value) {
// $device[] = explode("-", $value)[1];
$device[] = $value;
}
$request->merge(['device' => implode(",", $device)]);
}else{
$request->merge(['device' => ""]);
}
DB::beginTransaction();
try {
$data = $request->all();
$data['password'] = Hash::make("Aa@123456789");
$user = User::create($data);
$user->syncRoles($request->role);
$logData = [
'action' => 'create',
'action_detail' => '新增使用者',
'ip' => request()->ip(),
'remark' => "新增使用者:$user->name",
];
LogWriter::writeLog($logData, 'api');
DB::commit();
return response()->json(['success' => '新增成功']);
} catch (\Exception $e) {
DB::rollback();
Log::error("新增使用者錯誤訊息: " . $e->getMessage());
return response()->json(['error' => '新增失敗']);
}
}
public function show(Request $request, $id)
{
$user = User::with('roles')->find($id);
$role = Role::where('id', '!=', 1)->get();
return response()->json(['user' => $user, 'role' => $role]);
}
public function update(Request $request, $id)
{
if ($id == 1) {
return response()->json(['error' => '此使用者不可編輯']);
}
if (isset($request->password_reset)){
$user = User::find($id);
$user->update(['password'=> Hash::make("Aa@123456789")]);
$logData = [
'action' => 'edit',
'action_detail' => '使用者密碼重設',
'ip' => request()->ip(),
'remark' => "重設使用者密碼: $user->name",
];
LogWriter::writeLog($logData, 'api');
return response()->json(['success' => '重設成功']);
}
if(isset($request->device)){
$device = [];
foreach ($request->device as $key => $value) {
// $device[] = explode("-", $value)[1];
$device[] = $value;
}
$request->merge(['device' => implode(",", $device)]);
}else{
$request->merge(['device' => ""]);
}
DB::beginTransaction();
try {
$user = User::find($id);
$user->update($request->all());
$user->syncRoles($request->role);
$logData = [
'action' => 'edit',
'action_detail' => '編輯使用者',
'ip' => request()->ip(),
'remark' => "編輯使用者:$user->name",
];
LogWriter::writeLog($logData, 'api');
DB::commit();
return response()->json(['success' => '編輯成功']);
} catch (\Exception $e) {
DB::rollback();
Log::error("編輯使用者錯誤訊息: " . $e->getMessage());
return response()->json(['error' => '編輯失敗']);
}
}
// 刪除使用者
public function destory(Request $request, $id)
{
if ($id == 1) {
return response()->json(['error' => '此使用者不可刪除']);
}
DB::beginTransaction();
try {
$user = User::find($id);
$user->update(['deleted_at' => now()]);
$logData = [
'action' => 'delete',
'action_detail' => '刪除使用者',
'ip' => request()->ip(),
'remark' => "刪除使用者:$user->name",
];
LogWriter::writeLog($logData, 'api');
DB::commit();
return response()->json(['success' => '刪除成功']);
} catch (\Exception $e) {
DB::rollback();
return response()->json(['error' => '刪除失敗']);
}
}
public function exportUserData()
{
$data_arr = User::whereNotIn('id',[1])->OrderBy('id', 'asc')->get()->toArray();
$columns = ['id', 'station', 'unit', 'class', 'name', 'account', 'role_name', 'deleted_at'];
$columnTitle = [
['編號', '分局', '使用單位', '職稱', '姓名', '帳號', '使用者權限', '備註']
];
$data = array_map(function ($row) use ($columns) {
return array_merge(array_flip($columns), array_intersect_key($row, array_flip($columns)));
}, $data_arr);
// 修改data中編號id 從1開始
foreach ($data as $key => $value) {
$data[$key]['id'] = $key + 1;
}
$fileName = 'userData-' . Str::random(10) . '.xlsx';
ExportFiles::create([
'name' => $fileName,
'path' => 'public/exports_user',
'type' => 'xlsx',
'status' => '1',
'remark' => '使用者匯出',
'user_id' => 1,
]);
Excel::store(new ArrayExport($data, $columnTitle), 'public/exports_user/' . $fileName, 'local', \Maatwebsite\Excel\Excel::XLSX);
// 直接跳轉下載
return response()->json(['success' => '匯出成功', 'file' => 'storage/exports_user/'.$fileName]);
}
#endregion
}

811
app/Http/Controllers/System/RedController.php

@ -0,0 +1,811 @@ @@ -0,0 +1,811 @@
<?php
namespace App\Http\Controllers\System;
use App\Class\LogWriter;
use App\Exports\ArrayExport;
use App\Http\Controllers\Controller;
use App\Models\ExportFiles;
use App\Models\OverSpeedRed;
use App\Models\OverSpeedRedEquipment;
use App\Models\ViolationLaw;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Maatwebsite\Excel\Facades\Excel;
use Illuminate\Support\Str;
use ZipArchive;
class RedController extends Controller
{
public $destPath_root;
public $clientDestPath_root;
public $unreportPath_root;
public $month;
function __construct()
{
$this->middleware('permission:red-review|red-reduction|red-device-setting|red-statistics|red-analysis|red-read', ['only' => ['Review']]);
$this->middleware('permission:red-review|permission:red-reduction', ['only' => ['update']]);
$this->middleware('permission:red-device-setting', ['only' => ['DeviceSetting']]);
$this->middleware('permission:red-statistics', ['only' => ['Statistics']]);
$this->middleware('permission:red-analysis', ['only' => ['Analysis']]);
// OK path
$this->destPath_root = 'D:\\OK\\Red\\';
$this->clientDestPath_root = 'Y:\\';
// NOK path
$this->unreportPath_root = 'C:\\xampp\\SMMS\\storage\\app\\ParsingFiles\\NOK\\Red\\';
$this->month['start'] = Carbon::now()->subDays(8)->format('Y-m-d');
$this->month['current'] = Carbon::now()->subDays(1)->format('Y-m-d');
}
public function Review()
{
$device = explode(',', auth()->user()->device) ?? [1];
// dd($device);
$equipmentData = new OverSpeedRedEquipment();
$serialNumber = $equipmentData::whereIn('serialnumber', $device)->groupBy('serialnumber')->get(['serialnumber', 'location']);
$violationparking = new OverSpeedRed();
$locationtype = $violationparking::where('processcheck', 0)->whereIn('violationtype', ['闖紅燈'])->whereIn('serialnumber', $device)->groupBy('location')->pluck('location');
// dd($data,$page_data);
$vio_arr = [];
foreach (ViolationLaw::whereIn('type', ["闖紅燈"])->get() as $key => $item) {
$vio_arr[$key]['violationcode'] = $item->violationcode;
$vio_arr[$key]['display_name'] = $item->display_name;
}
// dd($vio_arr);
return view('system.red.index')
->with('serialNumber', $serialNumber)
->with('locationtype', $locationtype)
->with('vio_arr', $vio_arr)
->with('month', $this->month);
}
#region 已審 View
public function Censored(Request $request)
{
$device = explode(',', auth()->user()->device) ?? [1];
$equipmentData = new OverSpeedRedEquipment();
$serialNumber = $equipmentData::whereIn('serialnumber', $device)->groupBy('serialnumber')->get(['serialnumber', 'location']);
$violationparking = new OverSpeedRed();
$locationtype = $violationparking::where('processcheck', 1)->whereIn('violationtype', ['闖紅燈'])->whereIn('serialnumber', $device)->groupBy('location')->pluck('location');
// dd($data,$page_data);
return view('system.red.manage')
->with('serialNumber', $serialNumber)
->with('locationtype', $locationtype)
->with('month', $this->month);
}
#endregion
#region 不舉發 View
public function Unreport(Request $request)
{
$device = explode(',', auth()->user()->device) ?? [1];
$equipmentData = new OverSpeedRedEquipment();
$serialNumber = $equipmentData::whereIn('serialnumber', $device)->groupBy('serialnumber')->get(['serialnumber', 'location']);
$violationparking = new OverSpeedRed();
$locationtype = $violationparking::where('processcheck', 2)->whereIn('violationtype', ['闖紅燈'])->whereIn('serialnumber', $device)->groupBy('location')->pluck('location');
$unreportreason = $violationparking->where('processcheck', 2)->whereIn('violationtype', ['闖紅燈'])->whereIn('serialnumber', $device)->groupBy('unreportreason')->pluck('unreportreason');
// dd($locationtype,$unreportreason);
return view('system.red.unreport')
->with('serialNumber', $serialNumber)
->with('locationtype', $locationtype)
->with('unreportreason', $unreportreason)
->with('month', $this->month);
}
#endregion
public function update(Request $request, $id)
{
$data = OverSpeedRed::find($id);
// 先確認 processcheck 狀態 0 修改 1 舉發 2 不舉發
// 確認使用者是否可以 review
DB::beginTransaction();
try {
// 如果使用者具 審查權限
if (auth('api')->user()->can('red-review')) {
// 修改 processcheck = 0
if ($request->processcheck == 0) {
$data->carnumber = $request->carnumber ?? $data->carnumber;
$data->violationtype = $request->violationtype ?? $data->violationtype;
$data->cartype = $request->cartype ?? $data->cartype;
$data->violationcode = $request->viocode ?? $data->violationcode;
$logData = [
'action' => 'edit',
'action_detail' => '編輯案件',
'ip' => request()->ip(),
'remark' => "編輯違停案件: $data->carnumber ,案件日期: $data->datatime",
];
}
// 舉發 processcheck = 1
elseif ($request->processcheck == 1) {
if ($request->carnumber == null) {
return response()->json(['error' => '請輸入車牌號碼']);
}
if (isset($request->viocode)) {
if ($request->viocode == null)
return response()->json(['error' => '請選擇法條代碼']);
// 如果法條代碼不再資料表中
if (!ViolationLaw::where('violationcode', $request->viocode)->exists()) {
return response()->json(['error' => '此法條代碼不存在']);
}
} else {
return response()->json(['error' => '請選擇法條代碼']);
}
$this->updateIni($data->picture, $data->carnumber, "$request->carnumber", $data->violationtype, $request->violationtype);
$data->carnumber = $request->carnumber ?? $data->carnumber;
$data->violationtype = $request->violationtype ?? $data->violationtype;
$data->cartype = $request->cartype ?? $data->cartype;
$data->violationcode = $request->viocode ?? $data->violationcode;
$data->processcheck = 1;
$logData = [
'action' => 'review',
'action_detail' => '舉發案件',
'ip' => request()->ip(),
'remark' => "舉發違停案件: $data->carnumber ,案件日期: $data->datatime",
];
$pth = explode('*', $data->picture);
$photoPath = str_replace('*', '\\', storage_path('app' . '\\' . 'ParsingFiles' . '\\' . "$data->picture"));
$pth = end($pth);
$violationtype = $data->violationtype;
$time = str_replace('-', '', explode(' ', $data->datatime)[0]);
// public_path('merge\\'.$path)
$mgpath = public_path("merge\\$violationtype\\$time\\$pth");
if (!file_exists($mgpath)) {
if (is_dir(public_path("merge\\$violationtype\\$time")) === false) {
mkdir(public_path("merge\\$violationtype\\$time"), 0777, true);
}
copy($photoPath, $mgpath);
}
$aaData['picture'] = $data->picture;
$aaData['picture2'] = $data->picture2;
$aaData['datatime'] = $data->datatime;
$aaData['violationtype'] = $data->violationtype;
$this->saveFinishFile($aaData);
}
// 不舉發 processcheck = 2
elseif ($request->processcheck == 2) {
$data->carnumber = $request->carnumber ?? $data->carnumber;
$data->violationtype = $request->violationtype ?? $data->violationtype;
$data->cartype = $request->cartype ?? $data->cartype;
$data->unreportreason = $request->unreportreason ?? $data->unreportreason;
$data->processcheck = 2;
$logData = [
'action' => 'review',
'action_detail' => '不舉發案件',
'ip' => request()->ip(),
'remark' => "不舉發違停案件: $data->carnumber ,案件日期: $data->datatime",
];
}
// 還原 processcheck = 3
elseif ($request->processcheck == 3) {
$data->violationcode = null;
$data->unreportreason = '';
$data->processcheck = 0;
$logData = [
'action' => 'reduction',
'action_detail' => '還原案件',
'ip' => request()->ip(),
'remark' => "還原違停案件: $data->carnumber ,案件日期: $data->datatime",
];
}
}
// 紀錄審查者 帳號
$data->jsoncheck = auth('api')->user()->account;
// 資料儲存
$data->save();
// 紀錄 LOG
LogWriter::writeLog($logData, 'api');
DB::commit();
// all good
return response()->json(['success' => $logData['remark']]);
} catch (\Exception $e) {
DB::rollback();
return response()->json(['error' => '系統錯誤']);
// something went wrong
}
}
#region DataTable (AJAX刷新用 參數 processcheckStatus 0未審 1已審 2不舉發 "99清冊用")使用
public function getDataTable(Request $request)
{
// dd($request->all());
#region 設備限制
$device = explode(',', auth("api")->user()->device) ?? [1];
// dd($device);
#endregion
$processcheckStatus = $request->processcheckStatus;
// dd($processcheckStatus, $device);
[$draw, $totalRecords, $totalRecordswithFilter, $data_arr] = $this->getData($device, $request, $processcheckStatus);
$response = array(
"draw" => intval($draw),
"iTotalRecords" => $totalRecords,
"iTotalDisplayRecords" => $totalRecordswithFilter,
"aaData" => $data_arr
);
// dd($response);
echo json_encode($response);
exit;
}
#endregion
#region 取得資料
function getData($device, $request, $processcheckStatus = null)
{
// 審查狀態 0 未審 1 已審查 2 不舉發 99清冊
// $processcheckStatus = 2;
// dd($device, $request, $processcheckStatus);
#region Request 搜尋值
if (isset($request->searchByFromdate))
$searchByFromdate = $request->searchByFromdate;
if (isset($request->searchByTodate))
$searchByTodate = $request->searchByTodate;
//地點搜尋
if (isset($request->location))
$location = $request->location;
//車牌搜尋
if (isset($request->carnumber))
$carnumber = $request->carnumber;
//不舉發理由搜尋
if (isset($request->unreportreason))
$unreportreason = $request->unreportreason;
#endregion
#region DataTable 搜尋屬性
if (!isset($request->export)) {
$draw = $request->get('draw');
$start = $request->get("start");
$rowperpage = $request->get("length"); // Rows display per page
$columnIndex_arr = $request->get('order');
$columnName_arr = $request->get('columns');
$order_arr = $request->get('order');
$search_arr = $request->get('search');
$columnIndex = $columnIndex_arr[0]['column']; // Column index
$columnName = $columnName_arr[$columnIndex]['data']; // Column name
$columnSortOrder = $order_arr[0]['dir']; // asc or desc
$searchValue = $search_arr['value']; // Search value
} else {
$draw = 0;
}
#endregion
// Fetch records
$records = OverSpeedRed::query();
$records->whereIn('violationtype', ['闖紅燈']);
if (isset($columnName))
$records->orderBy($columnName, $columnSortOrder);
if (isset($searchValue))
$records->where('carnumber', 'like', '%' . $searchValue . '%');
//已審未審
//清冊用 99
$records->where('violationtype', '!=', '違規臨時停車');
if ($processcheckStatus == 99) {
$processcheckStatus = $request->processCheck;
if ($processcheckStatus == 99 || $processcheckStatus == null)
$records->whereIn('processcheck', [0, 1, 2]);
else {
$records->where('processcheck', $processcheckStatus);
}
} else {
$records->where('processcheck', $processcheckStatus);
}
//設備限制
$records->whereIn('serialnumber', $device);
//時間限制
if (isset($searchByFromdate))
$records->where('datatime', ">", $searchByFromdate . ' 00:00:00');
if (isset($searchByTodate))
$records->where('datatime', "<", $searchByTodate . ' 23:59:59');
//地點篩選
if (isset($location))
$records->where('serialnumber', $location);
//車牌篩選
if (isset($carnumber))
$records->where('carnumber', 'like', '%' . $carnumber . '%');
//不舉發理由
if (isset($unreportreason))
$records->where('unreportreason', 'like', '%' . $unreportreason . '%');
$totalRecords = $records->count();
$totalRecordswithFilter = $records->count();
//如果有報表類型(1,2,3,...),並需要特別處理欄位的報表
if (isset($request->statisticstype)) {
if ($request->statisticstype == 2)
$records->groupBy('serialnumber', 'cartype')->select('*', DB::raw('count(*) as count'));
if ($request->statisticstype == 3) {
$groupbyPara1 = "CAST(DATE_FORMAT(datatime, '%H') AS INT)";
$records->groupBy(DB::raw($groupbyPara1), 'serialnumber', 'violationtype')->select('*', DB::raw('COUNT(*) as count, CONCAT(CAST(DATE_FORMAT(datatime,"%H") as INT),"-",CAST(DATE_FORMAT(DATE_ADD(datatime,INTERVAL +1 HOUR),"%H") as INT)) AS period'));
$records->orderBy(DB::raw($groupbyPara1), 'ASC');
}
} else {
$records->select('*');
}
if (!isset($request->export)) {
if (isset($start))
$records->skip($start);
if (isset($rowperpage))
$records->take($rowperpage);
}
$records = $records->get();
$data_arr = array();
// $sno = $start + 1;
foreach ($records as $record) {
//還原用id
$id = $record->id;
//不舉發原因
$unreportreason = $record->unreportreason;
$datatime = $record->datatime;
$serialnumber = $record->serialnumber;
$location = $record->location;
$carnumber = $record->carnumber;
$picture = $record->picture;
$picture2 = $record->picture2;
$violationtype = $record->violationtype;
$cartype = $record->cartype;
$unreportpicture = $record->unreportpicture;
$processcheck = $record->processcheck;
$postcheck = $record->postcheck ?? 0;
//報表用的欄位,避免前端錯誤,給預設值
$count = $record->count ?? 0;
$period = $record->period ?? "";
$cartypeTitle = [
'1' => "汽車",
'2' => "機車",
'3' => "重型機車",
'4' => "輕型機車",
'8' => "微型電動二輪車"
];
$processCheckTitle = [
'0' => "未審",
'1' => "已審查",
'2' => "不舉發"
];
if (!isset($cartype))
$cartype = 1;
// 如果車牌 = None 則 顯示 ""
if ($carnumber == "None")
$carnumber = "";
$pthar = explode('*', $record->picture);
$pth = $pthar[count($pthar) - 1];
$time = str_replace('-', '', explode(' ', $datatime)[0]);
// public_path('merge\\'.$path)
$mgpath = public_path("merge\\$violationtype\\$time\\$pth");
$mergepic = 0;
if (file_exists($mgpath)) {
$mergepic = "merge/$violationtype/$time/$pth";
}
$data_arr[] = array(
"id" => $id,
"datatime" => $datatime,
"serialnumber" => $serialnumber,
"location" => $location,
"carnumber" => $carnumber,
"picture" => $picture,
"picture2" => $picture2,
//影片連結 (用照片的路徑取代成mp4 picture= _A picture2= _B)
"video" => str_replace('.jpg', '.mp4', $picture),
"violationtype" => $violationtype,
"cartype" => $cartype,
"carkind" => $cartypeTitle[$cartype],
"unreportreason" => $unreportreason ?? '',
"unreportpicture" => $unreportpicture ?? $picture,
"mergepic" => $mergepic ?? '0',
"postcheck" => $postcheck,
// 清冊用
"processcheck" => $processCheckTitle[$processcheck],
// "searchByFromdate" => $searchByFromdate,
// "searchByTodate"=> $searchByTodate,
"count" => $count,
"period" => $period
);
}
return [$draw, $totalRecords, $totalRecordswithFilter, $data_arr];
}
#endregion
// 清冊用
public function Statistics()
{
$device = explode(',', auth()->user()->device) ?? [1];
// 違規地點 group by
$equipmentData = new OverSpeedRedEquipment();
$serialNumber = $equipmentData::whereIn('serialnumber', $device)->groupBy('serialnumber')->get(['serialnumber', 'location']);
// 違規類型 group by
$violationparking = new OverSpeedRed();
$violationtype = $violationparking::whereIn('serialnumber', $device)->whereIn('violationtype', ['闖紅燈'])->groupBy('violationtype')->pluck('violationtype');
return view('system.red.statistics')
->with('serialNumber', $serialNumber)
->with('violationtype', $violationtype);
}
public function getStatisticsData(Request $request)
{
#region 設備限制
$device = explode(',', auth("api")->user()->device) ?? [1];
#endregion
$processcheckStatus = 99;
[$draw, $totalRecords, $totalRecordswithFilter, $data_arr] = $this->getData($device, $request, $processcheckStatus);
$response = array(
"draw" => intval($draw),
"iTotalRecords" => $totalRecords,
"iTotalDisplayRecords" => $totalRecordswithFilter,
"aaData" => $data_arr
);
return $response;
}
public function getStatisticsDataExport(Request $request)
{
#region 設備限制
$device = explode(',', auth("api")->user()->device) ?? [1];
#endregion
$processcheckStatus = 99;
if ($request->statisticstype == 1) {
$remark = '違規件數清冊';
$columns = ['datatime', 'serialnumber', 'location', 'carnumber', 'violationtype', 'processcheck'];
$columnTitle = [
["【表單名稱】:", $remark],
["【違規日期區間】:", $request->searchByFromdate, " ~ ", $request->searchByTodate],
['違規時段', '設備編號', '違規地點', '車號', '違規類型', '審查狀態']
];
} elseif ($request->statisticstype == 2) {
$remark = '違規件數統計清冊';
$columns = ['serialnumber', 'location', 'violationtype', 'carkind', 'count'];
$columnTitle = [
["【表單名稱】:", $remark],
["【違規日期區間】:", $request->searchByFromdate, " ~ ", $request->searchByTodate],
['設備編號', '違規地點', '違規類型', '車種', '統計']
];
} elseif ($request->statisticstype == 3) {
$remark = '違規時段件數統計清冊';
$columns = ['serialnumber', 'location', 'period', 'violationtype', 'count'];
$columnTitle = [
["【表單名稱】:", $remark],
["【違規日期區間】:", $request->searchByFromdate, " ~ ", $request->searchByTodate],
['設備編號', '違規地點', '違規時段', '違規類型', '統計']
];
}
[$draw, $totalRecords, $totalRecordswithFilter, $data_arr] = $this->getData($device, $request, $processcheckStatus);
$data = array_map(function ($row) use ($columns) {
return array_merge(array_flip($columns), array_intersect_key($row, array_flip($columns)));
}, $data_arr);
$fileName = 'red-' . Str::random(10) . '.xlsx';
ExportFiles::create([
'name' => $fileName,
'path' => 'public/exports',
'type' => 'xlsx',
'status' => '1',
'remark' => "路口多功能-$remark",
'user_id' => auth('api')->user()->id,
]);
Excel::store(new ArrayExport($data, $columnTitle), 'public/exports/' . $fileName, 'local', \Maatwebsite\Excel\Excel::XLSX);
return response()->json(['url' => route('red-export', ['fileName' => $fileName])], 200);
}
public function Export(Request $request, $fileName)
{
$file = ExportFiles::where('name', $fileName)->first();
$filePath = storage_path('app/public/exports/' . $fileName);
if (file_exists($filePath)) {
return response()->download($filePath, $file->remark . '.' . $file->type);
} else {
return response()->json(['error' => '檔案不存在']);
}
}
// 數據分析 vpkAnalysis
public function Analysis()
{
$device = explode(',', auth()->user()->device) ?? [1];
// 違規地點 group by
$equipmentData = new OverSpeedRedEquipment();
$serialNumber = $equipmentData::whereIn('serialnumber', $device)->groupBy('serialnumber')->get(['serialnumber', 'location']);
// 違規類型 group by
$violationparking = new OverSpeedRed();
$violationtype = $violationparking::whereIn('serialnumber', $device)->whereIn('violationtype', ['闖紅燈'])->groupBy('violationtype')->pluck('violationtype');
return view('system.red.analysis')
->with('serialNumber', $serialNumber)
->with('violationtype', $violationtype);
}
public function getAnalysisData(Request $request)
{
$device = explode(',', auth("api")->user()->device) ?? [1];
$records = OverSpeedRed::query();
$records->whereIn('serialnumber', $device)->whereIn('violationtype', ['闖紅燈'])->whereIn('processcheck', [1, 3]);
// dd($request->all());
if (isset($request->search_fromdate))
$records->where('datatime', ">", $request->search_fromdate . ' 00:00:00');
if (isset($request->search_todate))
$records->where('datatime', "<", $request->search_todate . ' 23:59:59');
//地點搜尋
if (isset($request->location))
$records->where('serialnumber', $request->location);
if (isset($request->violation_type))
$records->where('violationtype', $request->violation_type);
$data = $records->get();
if (isset($request->chart_type)) {
if ($request->chart_type == '1') {
// 根據違規類型分類 並計算每一類的總數
$data = $data->groupBy('violationtype')->map(function ($item, $key) {
return [
'title' => $key,
'times' => $item->count()
];
})->toArray();
$data = array_values($data);
} elseif ($request->chart_type == '2') {
// 根據違規類型分類 並計算每一類的總數
$data = $data->groupBy('location')->map(function ($item, $key) {
return [
'title' => $key,
'times' => $item->count()
];
})->toArray();
$data = array_values($data);
} elseif ($request->chart_type == '3') {
$records2 = OverSpeedRed::query();
$records2->whereIn('serialnumber', $device)->whereIn('processcheck', [1, 3]);
// dd($request->all());
if (isset($request->search_fromdate))
$records2->where('datatime', ">", $request->search_fromdate . ' 00:00:00');
if (isset($request->search_todate))
$records2->where('datatime', "<", $request->search_todate . ' 23:59:59');
//地點搜尋
if (isset($request->location))
$records2->where('serialnumber', $request->location);
if (isset($request->violation_type))
$records2->where('violationtype', $request->violation_type);
$violationTypes = $records2->whereIn('violationtype', ['闖紅燈'])->groupBy('violationtype')->select('violationtype')->get()->toArray();
$violationTypes = array_column($violationTypes, 'violationtype');
// violationTypes key value 互換
$violationTypesKey = array_flip($violationTypes);
// dd($violationTypes);
// 初始化結果陣列
$response = [];
for ($hour = 0; $hour <= 23; $hour++) {
// 補零 01, 02, 03, ..., 09
// $hour = str_pad($hour, 2, '0', STR_PAD_LEFT);
// 初始化結果陣列
$response[$hour] = [
'category' => $hour,
];
for ($i = 0; $i < count($violationTypes); $i++) {
$response[$hour][$i] = 0;
}
}
$data = $records->get();
$data = $data->groupBy(function ($item, $key) {
return intval(Carbon::parse($item->datatime)->format('H'));
})->map(function ($item, $key) use (&$response, $violationTypesKey) {
$item->groupBy('violationtype')->map(function ($item, $viotype) use ($key, &$response, $violationTypesKey) {
$response[$key][$violationTypesKey[$viotype]] = $item->count();
});
});
$data = $response;
// dd($data);
}
}
// dd($data);
return response()->json(['data' => $data, 'chart_type' => $request->chart_type, 'violationtype' => $violationTypes ?? ''], 200);
}
// statistics
// public function statistics()
// {
// return view('system.red.statistics');
// }
public function getStatisticsTest(Request $request)
{
$request->statisticstype;
// 讀取json file (storage/app/data/get-red-statistics-1~3.json )
$json = file_get_contents(storage_path('app/data/get-red-statistics-1.json'));
}
#region 更新ini檔案
function updateIni($path, $oldCarNumber, $newCarNumber, $oldViolationType, $newViolationType)
{
// $oldCarNumber = explode('"', "$oldCarNumber")[1];
$iniPath = str_replace('jpg', 'ini', str_replace('*', '\\', storage_path('app' . '\\' . 'ParsingFiles' . '\\' . "$path")));
$iniFile = fopen("$iniPath", "r+");
$iniData = fread($iniFile, filesize("$iniPath"));
$iniData = iconv('Big5', 'UTF-8', "$iniData");
// if(strpos($iniData,'車號')){
// $iniData = str_replace("$oldCarNumber", "$newCarNumber", "$iniData");
// }else{
// $iniData = str_replace("\r\n操作者姓名", "\r\n車號="."$newCarNumber"."\r\n操作者姓名", "$iniData");
// }
$iniData = str_replace("$oldCarNumber", "$newCarNumber", "$iniData");
$iniData = str_replace("$oldViolationType", "$newViolationType", "$iniData");
$iniData = iconv('UTF-8', 'Big5', "$iniData");
$iniFile = fopen("$iniPath", "w+");
fwrite($iniFile, "$iniData");
fclose($iniFile);
}
function saveFinishFile($data)
{
//路徑位置設定
$destPath_root = $this->destPath_root;
$clientDestPath_root = $this->clientDestPath_root;
// $ftpPath_root = $this->ftpPath_root;
$violationtype = $data['violationtype'];
$path = $data['picture'];
// $path2 = $data['picture2'];
$time = str_replace('-', '', explode(' ', $data['datatime'])[0]);
$iniPath = str_replace('jpg', 'ini', str_replace('*', '\\', storage_path('app' . '\\' . 'ParsingFiles' . '\\' . "$path")));
$photoPath = str_replace('*', '\\', storage_path('app' . '\\' . 'ParsingFiles' . '\\' . "$path"));
// $photoPath2 = str_replace('*','\\', storage_path('app'.'\\'.'ParsingFiles'.'\\'."$path2"));
// $photoName = explode('*', $data['picture'])[4]; //原始有"時" 資料夾的層數
$photoName = explode('*', $data['picture']);
$photoName = end($photoName);
$photoPath = public_path("merge\\$violationtype\\$time\\$photoName");
// $photoName2 = explode('*', $data['picture2'])[3];
$destPath = $destPath_root . Auth::user()->account . "\\" . $time;
if (is_dir($destPath) === false) {
mkdir($destPath, 0777, true);
}
try {
copy($iniPath, $destPath . '\\' . str_replace('jpg', 'ini', $photoName));
copy($photoPath, $destPath . '\\' . $photoName);
// copy($photoPath2, $destPath.'\\'.$photoName2);
// 存入第二路徑供客戶操作資料
$clientDestPath = $clientDestPath_root . $time;
if (is_dir($clientDestPath) === false) {
mkdir($clientDestPath, 0777, true);
}
copy($iniPath, $clientDestPath . '\\' . str_replace('jpg', 'ini', $photoName));
copy($photoPath, $clientDestPath . '\\' . $photoName);
// copy($photoPath2, $clientDestPath.'\\'.$photoName2);
// 存入第FTP路徑供客戶操作資料
// $ftpPath = $ftpPath_root.$time;
// if( is_dir($ftpPath) === false ) { mkdir($ftpPath, 0777, true);}
// copy($iniPath, $ftpPath.'\\'.str_replace('jpg','ini', $photoName));
// copy($photoPath, $ftpPath.'\\'.$photoName);
} catch (\Throwable $th) {
//throw $th;
}
}
#endregion
#region 下載ZIP
function downloadZipPicture(Request $request)
{
set_time_limit(0);
ini_set('memory_limit', '2048M');
$device = explode(',', auth("api")->user()->device) ?? [1];
if (isset($request->searchByFromdate))
$searchByFromdate = $request->searchByFromdate;
if (isset($request->searchByTodate))
$searchByTodate = $request->searchByTodate;
if (isset($request->location))
$serialnumber = $request->location;
if (isset($request->carnumber))
$carnumber = $request->carnumber;
if (isset($request->processcheck))
$processcheck = $request->processcheck;
else $processcheck = 1;
$records = OverSpeedRed::query();
$records->where('location', 'not like', '%測試%');
$records->whereIn('serialnumber', $device)->whereIn('violationtype', ['闖紅燈']);
$records->where('processcheck', $processcheck);
if (isset($searchByFromdate))
$records->where('datatime', ">", $searchByFromdate . ' 00:00:00');
else {
$records->where('datatime', ">", Carbon::now()->subMonths(2));
$searchByFromdate = '';
}
if (isset($searchByTodate))
$records->where('datatime', "<", $searchByTodate . ' 23:59:59');
else $searchByTodate = '';
if (isset($serialnumber))
$records->where('serialnumber', $request->location);
if (isset($carnumber))
$records->where('carnumber', 'like', '%' . $carnumber . '%');
$records = $records->pluck("picture");
for ($i = 0; $i < count($records); $i++) {
$records[$i] = storage_path("app\\ParsingFiles\\" . str_replace('*', '\\', $records[$i]));
}
$fileName = $this->downloadZip($records, $request->location, $searchByFromdate, $searchByTodate, $processcheck);
if (file_exists($fileName)) {
ExportFiles::create([
'name' => $fileName,
'path' => 'public',
'type' => 'zip',
'status' => '1',
'remark' => "闖紅燈-下載ZIP",
'user_id' => auth('api')->user()->id,
]);
return $fileName;
} else return "沒有檔案";
}
public function downloadZip($files, $serialnumber, $searchByFromdate, $searchByTodate, $processcheck)
{
set_time_limit(0);
ini_set('memory_limit', '2048M');
foreach (glob(public_path() . '\*.zip') as $file) {
unlink($file);
}
$zip = new ZipArchive;
$rand = Str::random(5);
$fileName = "_$rand.zip";
if (isset($searchByTodate))
$fileName = str_replace('-', '', $searchByTodate) . $fileName;
if (isset($searchByFromdate))
$fileName = str_replace('-', '', $searchByFromdate) . "_" . $fileName;
if (isset($serialnumber))
$fileName = $serialnumber . "_" . $fileName;
if (isset($processcheck)) {
if ($processcheck == 1)
$fileName = "OSR_OK_" . $fileName;
if ($processcheck == 2)
$fileName = "OSR_X_" . $fileName;
}
if ($zip->open(public_path($fileName), ZipArchive::CREATE) === TRUE) {
foreach ($files as $file) {
$name = explode("\\", $file);
$iniPath = str_replace('.jpg', '.ini', $file);
$iniName = explode("\\", $iniPath);
if (end($name) != '') {
try {
$zip->addFile($file, end($name));
$zip->addFile($iniPath, end($iniName));
} catch (\Throwable $th) {
Log::error('Error: ' . $th);
}
}
}
$zip->close();
}
// response()->download(public_path($fileName))->deleteFileAfterSend(true);
return "$fileName";
}
#endregion
}

516
app/Http/Controllers/System/StatisticsController.php

@ -0,0 +1,516 @@ @@ -0,0 +1,516 @@
<?php
namespace App\Http\Controllers\system;
use App\Exports\ArrayExportH;
use App\Http\Controllers\Controller;
use App\Models\ExportFiles;
use App\Models\StatisticView;
use App\Models\EquipmentView;
use App\Models\MultisysEquipment;
use App\Models\OverSpeedRedEquipment;
use App\Models\User;
use App\Models\ViolationLaw;
use App\Models\ViolationParkingEquipment;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Maatwebsite\Excel\Facades\Excel;
class StatisticsController extends Controller
{
public function index(Request $request, $type)
{
$station = [];
$precinct = [];
$location = [];
$station = EquipmentView::select('station')->distinct()->pluck('station')->toArray();
$precinct = EquipmentView::select('precinct')->distinct()->pluck('precinct')->toArray();
$location = EquipmentView::select('location')->distinct()->pluck('location')->toArray();
$station = array_filter($station, function ($value) {
return $value != null;
});
$precinct = array_filter($precinct, function ($value) {
return $value != null;
});
$location = array_filter($location, function ($value) {
return $value != null;
});
// dd($station, $precinct, $location);
if ($type == '1') {
$violationTypes = ViolationLaw::getTypeCodes();
return view('system.statistics_1')
->with('violationTypes', $violationTypes)
->with('station', $station)
->with('precinct', $precinct)
->with('location', $location);
} elseif ($type == '2') {
$titles = ['未驗證', '已驗證舉發', '已驗證不舉發', '未處理比例'];
return view('system.statistics_2')
->with('titles', $titles)
->with('station', $station)
->with('precinct', $precinct)
->with('location', $location);
} elseif ($type == '3') {
$titles = ['時間區間1已舉發件數', '時間區間2已舉發件數', '增減'];
return view('system.statistics_3')
->with('titles', $titles)
->with('station', $station)
->with('precinct', $precinct)
->with('location', $location);
} elseif ($type == '4') {
$titles = ['已驗證舉發', '已驗證不舉發', '總件數'];
return view('system.statistics_4')
->with('titles', $titles);
} else {
// 404
return view('errors.404');
}
}
public function getStatistics(Request $request)
{
$draw = $request->get('draw');
// Fetch records
// $violationTypes = ['闖紅燈', '超速', '違停'];
if ($request->group_at == '分局') {
[$data_arr, $totalRecords] = self::getViolationSummary($request, '分局');
} else if ($request->group_at == '使用單位') {
[$data_arr, $totalRecords] = self::getViolationSummary($request, '使用單位');
} else if ($request->group_at == 'user') {
[$data_arr, $totalRecords] = self::getViolationSummary($request, 'user');
} else {
[$data_arr, $totalRecords] = self::getViolationSummary($request, 'location');
}
$response = array(
"draw" => intval($draw),
"iTotalRecords" => $totalRecords ?? 0,
"iTotalDisplayRecords" => $totalRecords ?? 0,
"aaData" => $data_arr ?? []
);
return $response;
}
public static function getViolationSummary($request, $group_at)
{
// dd($request->all());
if (isset($request->searchByFromdate))
$searchByFromdate = $request->searchByFromdate;
if (isset($request->searchByTodate))
$searchByTodate = $request->searchByTodate;
if (isset($request->searchByFromdate2))
$searchByFromdate2 = $request->searchByFromdate2;
if (isset($request->searchByTodate2))
$searchByTodate2 = $request->searchByTodate2;
if (isset($request->export))
$export = true;
else
$export = false;
#region DataTable 搜尋屬性
if (!isset($request->export)) {
$draw = $request->get('draw');
$start = $request->get("start");
$rowperpage = $request->get("length"); // Rows display per page
$columnIndex_arr = $request->get('order');
$columnName_arr = $request->get('columns');
$order_arr = $request->get('order');
$search_arr = $request->get('search');
$columnIndex = $columnIndex_arr[0]['column']; // Column index
$columnName = $columnName_arr[$columnIndex]['data']; // Column name
$columnSortOrder = $order_arr[0]['dir']; // asc or desc
$searchValue = $search_arr['value']; // Search value
} else {
$draw = 0;
}
#endregion
#region Query 舉發類別分析表
if ($request->reportType == '1') {
if ($group_at == '分局') {
$groups = EquipmentView::getTypePrecinct($request->s1 ?? []);
$groupLocationExpression = '';
$allSerialNumbers = [];
foreach ($groups as $group => $serialnumbers) {
$allSerialNumbers = array_merge($allSerialNumbers, $serialnumbers);
$serialnumber = implode("','", $serialnumbers);
if ($group == null || $group == '')
continue;
$groupLocationExpression .= " WHEN serialnumber IN ('$serialnumber') THEN '$group'";
}
$groupLocationExpression = 'CASE' . $groupLocationExpression . ' ELSE NULL END AS group_location';
$query = StatisticView::selectRaw($groupLocationExpression);
$query->whereIn('serialnumber', $allSerialNumbers);
} else if ($group_at == '使用單位') {
$groups = EquipmentView::getTypeStation($request->s2 ?? []);
// dd($groups);
$groupLocationExpression = '';
$allSerialNumbers = [];
foreach ($groups as $group => $serialnumbers) {
$allSerialNumbers = array_merge($allSerialNumbers, $serialnumbers);
$serialnumber = implode("','", $serialnumbers);
if ($group == null || $group == '')
continue;
$groupLocationExpression .= " WHEN serialnumber IN ('$serialnumber') THEN '$group'";
}
$groupLocationExpression = 'CASE' . $groupLocationExpression . ' ELSE NULL END AS group_location';
$query = StatisticView::selectRaw($groupLocationExpression);
$query->whereIn('serialnumber', $allSerialNumbers);
} else {
if (isset($request->s3) && $request->s3 != [])
$allSerialNumbers = EquipmentView::whereIn('location', $request->s3 ?? [])->pluck('serialnumber')->toArray();
else
$allSerialNumbers = EquipmentView::pluck('serialnumber')->toArray();
$groupLocationExpression = 'location AS group_location';
$query = StatisticView::selectRaw($groupLocationExpression);
$query->whereIn('serialnumber', $allSerialNumbers);
}
if (isset($searchByFromdate) && $searchByFromdate != '') {
$starttime = Carbon::parse($searchByFromdate)->format('Y-m-d');
$q1 = "AND datatime >= '$starttime 00:00:00'";
} else {
$q1 = '';
}
if (isset($searchByTodate) && $searchByTodate != '') {
$endtime = Carbon::parse($searchByTodate)->format('Y-m-d');
$q2 = "AND datatime <= '$endtime 23:59:59'";
} else {
$q2 = '';
}
$typeCodes = ViolationLaw::getTypeCodes();
foreach ($typeCodes as $type => $codes) {
// where in violationcode in (codes) and violationtype = type and processcheck = 1
// 替每個code 加上引號 並用逗號隔開 包含頭尾
$codes = implode("','", $codes);
$query->selectRaw("SUM(CASE WHEN violationcode IN ('$codes') AND processcheck = 1 $q1 $q2 THEN 1 ELSE 0 END) AS '$type'");
}
$query->selectRaw("SUM(CASE WHEN processcheck = 1 $q1 $q2 THEN 1 ELSE 0 END) AS total_cases");
}
// 審核統計分析表
else if ($request->reportType == '2') {
if ($group_at == '分局') {
$groups = EquipmentView::getTypePrecinct($request->s1 ?? []);
$groupLocationExpression = '';
$allSerialNumbers = [];
foreach ($groups as $group => $serialnumbers) {
$allSerialNumbers = array_merge($allSerialNumbers, $serialnumbers);
$serialnumber = implode("','", $serialnumbers);
if ($group == null || $group == '')
continue;
$groupLocationExpression .= " WHEN serialnumber IN ('$serialnumber') THEN '$group'";
}
$groupLocationExpression = 'CASE' . $groupLocationExpression . ' ELSE NULL END AS group_location';
$query = StatisticView::selectRaw($groupLocationExpression);
$query->whereIn('serialnumber', $allSerialNumbers);
} else if ($group_at == '使用單位') {
$groups = EquipmentView::getTypeStation($request->s2 ?? []);
$groupLocationExpression = '';
$allSerialNumbers = [];
foreach ($groups as $group => $serialnumbers) {
$allSerialNumbers = array_merge($allSerialNumbers, $serialnumbers);
$serialnumber = implode("','", $serialnumbers);
if ($group == null || $group == '')
continue;
$groupLocationExpression .= " WHEN serialnumber IN ('$serialnumber') THEN '$group'";
}
$groupLocationExpression = 'CASE' . $groupLocationExpression . ' ELSE NULL END AS group_location';
$query = StatisticView::selectRaw($groupLocationExpression);
$query->whereIn('serialnumber', $allSerialNumbers);
} else {
if (isset($request->s3) && $request->s3 != [])
$allSerialNumbers = EquipmentView::whereIn('location', $request->s3 ?? [])->pluck('serialnumber')->toArray();
else
$allSerialNumbers = EquipmentView::pluck('serialnumber')->toArray();
$groupLocationExpression = 'location AS group_location';
$query = StatisticView::selectRaw($groupLocationExpression);
$query->whereIn('serialnumber', $allSerialNumbers);
}
if (isset($searchByFromdate) && $searchByFromdate != '') {
$starttime = Carbon::parse($searchByFromdate)->format('Y-m-d');
$q1 = "AND datatime >= '$starttime 00:00:00'";
} else {
$q1 = '';
}
if (isset($searchByTodate) && $searchByTodate != '') {
$endtime = Carbon::parse($searchByTodate)->format('Y-m-d');
$q2 = "AND datatime <= '$endtime 23:59:59'";
} else {
$q2 = '';
}
$query->selectRaw("SUM(CASE WHEN processcheck = 0 $q1 $q2 THEN 1 ELSE 0 END) AS '未驗證'");
$query->selectRaw("SUM(CASE WHEN processcheck = 1 $q1 $q2 THEN 1 ELSE 0 END) AS '已驗證舉發'");
$query->selectRaw("SUM(CASE WHEN processcheck = 2 $q1 $q2 THEN 1 ELSE 0 END) AS '已驗證不舉發'");
// 未舉發比例 % = 未舉發 / (已舉發1 + 不舉發2 + 未舉發0) 直接顯示為 (比例數字) + %符號 保留兩位小數
$query->selectRaw("CONCAT(ROUND(SUM(CASE WHEN processcheck = 0 $q1 $q2 THEN 1 ELSE 0 END) / (SUM(CASE WHEN processcheck = 1 $q1 $q2 THEN 1 ELSE 0 END) + SUM(CASE WHEN processcheck = 2 $q1 $q2 THEN 1 ELSE 0 END) + SUM(CASE WHEN processcheck = 0 $q1 $q2 THEN 1 ELSE 0 END) ) * 100, 2), '%') AS '未處理比例'");
$query->selectRaw("SUM(CASE WHEN processcheck IN ('0', '1', '2') $q1 $q2 THEN 1 ELSE 0 END) AS total_cases");
} else if ($request->reportType == '3') {
if ($group_at == '分局') {
$groups = EquipmentView::getTypePrecinct($request->s1 ?? []);
$groupLocationExpression = '';
$allSerialNumbers = [];
foreach ($groups as $group => $serialnumbers) {
$allSerialNumbers = array_merge($allSerialNumbers, $serialnumbers);
$serialnumber = implode("','", $serialnumbers);
if ($group == null || $group == '')
continue;
$groupLocationExpression .= " WHEN serialnumber IN ('$serialnumber') THEN '$group'";
}
$groupLocationExpression = 'CASE' . $groupLocationExpression . ' ELSE NULL END AS group_location';
$query = StatisticView::selectRaw($groupLocationExpression);
$query->whereIn('serialnumber', $allSerialNumbers);
} else if ($group_at == '使用單位') {
$groups = EquipmentView::getTypeStation($request->s2 ?? []);
$groupLocationExpression = '';
$allSerialNumbers = [];
foreach ($groups as $group => $serialnumbers) {
$allSerialNumbers = array_merge($allSerialNumbers, $serialnumbers);
$serialnumber = implode("','", $serialnumbers);
if ($group == null || $group == '')
continue;
$groupLocationExpression .= " WHEN serialnumber IN ('$serialnumber') THEN '$group'";
}
$groupLocationExpression = 'CASE' . $groupLocationExpression . ' ELSE NULL END AS group_location';
$query = StatisticView::selectRaw($groupLocationExpression);
$query->whereIn('serialnumber', $allSerialNumbers);
} else {
if (isset($request->s3) && $request->s3 != [])
$allSerialNumbers = EquipmentView::whereIn('location', $request->s3 ?? [])->pluck('serialnumber')->toArray();
else
$allSerialNumbers = EquipmentView::pluck('serialnumber')->toArray();
$groupLocationExpression = 'location AS group_location';
$query = StatisticView::selectRaw($groupLocationExpression);
$query->whereIn('serialnumber', $allSerialNumbers);
}
if (isset($searchByFromdate) && $searchByFromdate != '' && isset($searchByTodate) && $searchByTodate != '') {
$starttime = Carbon::parse($searchByFromdate)->format('Y-m-d');
$endtime = Carbon::parse($searchByTodate)->format('Y-m-d');
$q1 = "AND datatime >= '$starttime 00:00:00'";
$q2 = "AND datatime <= '$endtime 23:59:59'";
} else {
$q1 = '';
$q2 = '';
}
if (isset($searchByFromdate2) && $searchByFromdate2 != '' && isset($searchByTodate2) && $searchByTodate2 != '') {
$starttime2 = Carbon::parse($searchByFromdate2)->format('Y-m-d');
$endtime2 = Carbon::parse($searchByTodate2)->format('Y-m-d');
$q3 = "AND datatime >= '$starttime2 00:00:00'";
$q4 = "AND datatime <= '$endtime2 23:59:59'";
} else {
$q3 = '';
$q4 = '';
}
$query->selectRaw("SUM(CASE WHEN processcheck = 1 $q1 $q2 THEN 1 ELSE 0 END) AS '時間區間1已舉發件數'");
$query->selectRaw("SUM(CASE WHEN processcheck = 1 $q3 $q4 THEN 1 ELSE 0 END) AS '時間區間2已舉發件數'");
// 時間區間2 - 時間區間1
$query->selectRaw("SUM(CASE WHEN processcheck = 1 $q3 $q4 THEN 1 ELSE 0 END) - SUM(CASE WHEN processcheck = 1 $q1 $q2 THEN 1 ELSE 0 END) AS '增減'");
}
// 審核統計分析表
else if ($request->reportType == '4') {
// distinct user account
$users = User::select('account')->distinct()->pluck('account')->toArray();
$users = array_filter($users, function ($value) {
return $value != null;
});
// StatisticView 的jsoncheck是 user account 依據 user account 分組統計 processcheck = 1 的數量 跟 processcheck = 2 的數量 還有總數
$query = StatisticView::selectRaw("jsoncheck");
// 增加一條join 用來取得 user account 關聯的 name
$query->leftJoin('users', 'users.account', '=', 'jsoncheck');
$query->selectRaw("users.name AS 'group_location'");
$query->selectRaw("SUM(CASE WHEN processcheck = 1 THEN 1 ELSE 0 END) AS '已驗證舉發'");
$query->selectRaw("SUM(CASE WHEN processcheck = 2 THEN 1 ELSE 0 END) AS '已驗證不舉發'");
$query->selectRaw("SUM(CASE WHEN processcheck IN ('1', '2') THEN 1 ELSE 0 END) AS 總件數");
// jsoncheck AS group_location is not null
$query->whereNotNull('jsoncheck');
$query->where('jsoncheck', '!=', 'admin');
$query->groupBy('group_location');
}
#endregion
// 所有 processcheck = 1 的案件
$query->groupBy('group_location');
if (isset($columnName))
$query->orderBy($columnName, $columnSortOrder);
$data = $query->get();
$count = count($data);
if (!$export) {
if (isset($start))
$query->skip($start);
if (isset($rowperpage))
$query->take($rowperpage);
}
$data = $query->get();
// 如果data[i] 底下的 group_location 是 null 就不顯示'
$data = $data->filter(function ($value, $key) {
return $value->group_location != null;
});
return [$data, $count];
}
public function getStatisticsExport(Request $request)
{
#region Request 搜尋值
if (isset($request->searchByFromdate))
$searchByFromdate = $request->searchByFromdate;
else
$searchByFromdate = '';
if (isset($request->searchByTodate))
$searchByTodate = $request->searchByTodate;
else
$searchByTodate = '';
#endregion
#region DataTable 搜尋屬性
if (!isset($request->export)) {
$draw = $request->get('draw');
$start = $request->get("start");
$rowperpage = $request->get("length"); // Rows display per page
$columnIndex_arr = $request->get('order');
$columnName_arr = $request->get('columns');
$order_arr = $request->get('order');
$search_arr = $request->get('search');
$columnIndex = $columnIndex_arr[0]['column']; // Column index
$columnName = $columnName_arr[$columnIndex]['data']; // Column name
$columnSortOrder = $order_arr[0]['dir']; // asc or desc
$searchValue = $search_arr['value']; // Search value
} else {
$draw = 0;
}
if ($request->group_at == '分局') {
$title = '分局';
[$data_arr, $totalRecords] = self::getViolationSummary($request, '分局');
} else if ($request->group_at == '使用單位') {
$title = '使用單位';
[$data_arr, $totalRecords] = self::getViolationSummary($request, '使用單位');
} else if ($request->group_at == 'user') {
$title = '員警姓名';
[$data_arr, $totalRecords] = self::getViolationSummary($request, 'user');
} else {
$title = '路口';
[$data_arr, $totalRecords] = self::getViolationSummary($request, 'location');
}
if ($request->reportType == '1') {
$titlef = '舉發類別分析表';
} else if ($request->reportType == '2') {
$titlef = '審核統計分析表';
} else if ($request->reportType == '3') {
$titlef = '審核統計分析表';
} else if ($request->reportType == '4') {
$titlef = '個別案件審核統計表';
}
$remark = '報表-' . $titlef . '-' . $title;
$columns = ['group_location', 'total_cases'];
if ($request->reportType == '1') {
$typeCodes = ViolationLaw::getTypeCodes();
foreach ($typeCodes as $type => $codes) {
if ($type == 99 || $type == 3 || $type == 1) {
continue;
}
$columns[] = $type;
}
} else if ($request->reportType == '2') {
$columns = ['group_location', 'total_cases', '未驗證', '已驗證舉發', '已驗證不舉發', '未處理比例'];
} else if ($request->reportType == '3') {
$columns = ['group_location', '時間區間1已舉發件數', '時間區間2已舉發件數', '增減'];
} else if ($request->reportType == '4') {
$columns = ['group_location', '已驗證舉發', '已驗證不舉發', '總件數'];
}
if (isset($request->searchByFromdate) && isset($request->searchByTodate)) {
$remark = $remark . ' ' . $request->searchByFromdate . ' ~ ' . $request->searchByTodate;
}
$data_arr = $data_arr->toArray();
$data = array_map(function ($row) use ($columns) {
$data = array_merge(array_flip($columns), array_intersect_key($row, array_flip($columns)));
return $data;
}, $data_arr);
if ($request->reportType == '1' || $request->reportType == '2') {
$column2 = [$title, '總案件數'];
} else {
$column2 = [$title];
}
foreach ($columns as $column) {
if ($column == 'group_location' || $column == 'total_cases') {
continue;
}
$column2[] = $column;
}
if (isset($request->searchByFromdate) && isset($request->searchByTodate)) {
$timeRange = $request->searchByFromdate . ' ~ ' . $request->searchByTodate;
} else {
$timeRange = '';
}
$columnTitle = [
["【表單名稱】: " . $remark],
["【查詢日期區間】: " . $timeRange],
$column2
];
$fileName = 'statistics-' . Str::random(10) . '.xlsx';
try {
DB::beginTransaction();
ExportFiles::create([
'name' => $fileName,
'path' => 'public/exports',
'type' => 'xlsx',
'status' => '1',
'remark' => "$remark",
'user_id' => auth('api')->user()->id,
]);
$option = [
'printDef' => $request->printDef ?? 0,
'showPN' => $request->showPN ?? 0,
'showBorder' => $request->showBorder ?? 0,
];
Excel::store(new ArrayExportH($data, $columnTitle, $option), 'public/exports/' . $fileName, 'local', \Maatwebsite\Excel\Excel::XLSX);
DB::commit();
} catch (\Throwable $th) {
Log::error("報表匯出失敗: " . $th->getMessage());
DB::rollBack();
}
return response()->json(['url' => route('export', ['fileName' => $fileName])], 200);
}
}

324
app/Http/Controllers/System/SystemController.php

@ -0,0 +1,324 @@ @@ -0,0 +1,324 @@
<?php
namespace App\Http\Controllers\System;
use App\Http\Controllers\Controller;
use App\Models\ExportFiles;
use App\Models\Multisys;
use App\Models\MultisysEntry;
use App\Models\MultisysEquipment;
use App\Models\OverSpeedRed;
use App\Models\OverSpeedRedEquipment;
use App\Models\ViolationParking;
use App\Models\ViolationParkingEquipment;
use Carbon\Carbon;
use GuzzleHttp\Client;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use ZipArchive;
use Illuminate\Support\Str;
class SystemController extends Controller
{
public function __construct()
{
}
public function getDashboardData(Request $request)
{
$device = explode(',', auth("api")->user()->device) ?? [1];
if ($request->has('location')) {
$location = $request->input('location');
}
if ($request->has('year')) {
$year = $request->input('year');
} else {
$year = Carbon::now('Asia/Taipei')->year;
}
if ($request->has('month')) {
$month = $request->input('month');
} else {
$month = Carbon::now('Asia/Taipei')->month;
}
#region 首頁儀錶板 - 總違規數 - 路口多功能
$data = Multisys::query();
$data->whereYear('datatime', $year);
if ($month != 0)
$data->whereMonth('datatime', $month);
if (isset($location))
$data->where('serialnumber', $location);
$data->whereIn('serialnumber', $device);
$data->groupBy('violationtype');
$data->select('violationtype', DB::raw('COUNT(*) as count'));
$data = $data->get();
$data = $data->toArray();
#endregion
#region 首頁儀錶板 - 總違規數 - 違規停車
$data2 = ViolationParking::query();
$data2->whereYear('datatime', $year);
if ($month != 0)
$data2->whereMonth('datatime', $month);
if (isset($location))
$data2->where('serialnumber', $location);
$data2->whereIn('serialnumber', $device);
$data2->groupBy('violationtype');
$data2->select('violationtype', DB::raw('COUNT(*) as count'));
$data2 = $data2->get();
$data2 = $data2->toArray();
#endregion
#region 首頁儀錶板 - 總違規數 - 路口多功能
$data3 = OverSpeedRed::query();
$data3->whereYear('datatime', $year);
if ($month != 0)
$data3->whereMonth('datatime', $month);
if (isset($location))
$data3->where('serialnumber', $location);
$data3->whereIn('serialnumber', $device);
$data3->groupBy('violationtype');
$data3->select('violationtype', DB::raw('COUNT(*) as count'));
$data3 = $data3->get();
$data3 = $data3->toArray();
#endregion
// $data = ViolationParking::groupBy('violationtype')->select('violationtype', DB::raw('COUNT(*) as count'))->get()->toArray();
// $data2 = Multisys::groupBy('violationtype')->select('violationtype', DB::raw('COUNT(*) as count'))->get()->toArray();
# merge array
$data = array_merge($data, $data2);
$data = array_merge($data, $data3);
$response = [];
foreach ($data as $key => $value) {
$response[] = [
'col' => 6,
'eventType' => $value['violationtype'],
'todayCount' => $value['count'],
'lastUpdate' => Carbon::now('Asia/Taipei')->toDateString(),
];
}
return response()->json(['data' => $response], 200);
}
public function getDashboardPie(Request $request)
{
$device = explode(',', auth("api")->user()->device) ?? [1];
if ($request->has('location')) {
$location = $request->input('location');
}
if ($request->has('year')) {
$year = $request->input('year');
} else {
$year = Carbon::now('Asia/Taipei')->year;
}
if ($request->has('month')) {
$month = $request->input('month');
} else {
$month = Carbon::now('Asia/Taipei')->month;
}
#region 首頁儀錶板 - 違規類型統計
$multisysQuery = Multisys::query();
$multisysQuery->whereYear('datatime', $year);
if ($month != 0)
$multisysQuery->whereMonth('datatime', $month);
if (isset($location))
$multisysQuery->where('serialnumber', $location);
$multisysQuery->whereIn('serialnumber', $device);
$multisysQuery->groupBy('violationtype');
$multisysQuery->select('violationtype', DB::raw('COUNT(*) as count'));
$data = $multisysQuery->get();
$data = $data->toArray();
$violationparkingQuery = ViolationParking::query();
$violationparkingQuery->whereYear('datatime', $year);
if ($month != 0)
$violationparkingQuery->whereMonth('datatime', $month);
if (isset($location))
$violationparkingQuery->where('serialnumber', $location);
$violationparkingQuery->whereIn('serialnumber', $device);
$violationparkingQuery->groupBy('violationtype');
$violationparkingQuery->select('violationtype', DB::raw('COUNT(*) as count'));
$data2 = $violationparkingQuery->get();
$data2 = $data2->toArray();
$overspeedredQuery = OverSpeedRed::query();
$overspeedredQuery->whereYear('datatime', $year);
if ($month != 0)
$overspeedredQuery->whereMonth('datatime', $month);
if (isset($location))
$overspeedredQuery->where('serialnumber', $location);
$overspeedredQuery->whereIn('serialnumber', $device);
$overspeedredQuery->groupBy('violationtype');
$overspeedredQuery->select('violationtype', DB::raw('COUNT(*) as count'));
$data3 = $overspeedredQuery->get();
$data3 = $data3->toArray();
# merge array
$data = array_merge($data, $data2);
$data = array_merge($data, $data3);
$response = [];
foreach ($data as $key => $value) {
$response[] = [
'violationtype' => $value['violationtype'],
'count' => $value['count'],
];
}
#endregion
return response()->json(['data' => $response], 200);
}
public function getDashboardBar(Request $request)
{
$device = explode(',', auth("api")->user()->device) ?? [1];
if ($request->has('location')) {
$location = $request->input('location');
}
if ($request->has('year')) {
$year = $request->input('year');
} else {
$year = Carbon::now('Asia/Taipei')->year;
}
if ($request->has('month')) {
$month = $request->input('month');
} else {
$month = Carbon::now('Asia/Taipei')->month;
}
#region 首頁儀錶板 - 違規類型統計
// 取得所有違規類型 存陣列
$violationTypes = Multisys::whereIn('serialnumber', $device)->groupBy('violationtype')->select('violationtype')->get()->toArray();
$violationTypes2 = ViolationParking::whereIn('serialnumber', $device)->groupBy('violationtype')->select('violationtype')->get()->toArray();
$violationTypes3 = OverSpeedRed::whereIn('serialnumber', $device)->groupBy('violationtype')->select('violationtype')->get()->toArray();
$violationTypes = array_merge($violationTypes, $violationTypes2);
$violationTypes = array_merge($violationTypes, $violationTypes3);
$violationTypes = array_column($violationTypes, 'violationtype');
// violationTypes key value 互換
$violationTypesKey = array_flip($violationTypes);
// dd($violationTypes);
// 初始化結果陣列
$response = [];
for ($hour = 0; $hour <= 23; $hour++) {
// 補零 01, 02, 03, ..., 09
// $hour = str_pad($hour, 2, '0', STR_PAD_LEFT);
// 初始化結果陣列
$response[$hour] = [
'category' => $hour,
];
for ($i = 0; $i < count($violationTypes); $i++) {
$response[$hour][$i] = 0;
}
}
$records = Multisys::query();
if ($year != 0)
$records->whereYear('datatime', $year);
if ($month != 0)
$records->whereMonth('datatime', $month);
if (isset($location))
$records->where('serialnumber', $location);
$records->whereIn('serialnumber', $device);
$data = $records->get();
$data = $data->groupBy(function ($item, $key) {
return Carbon::parse($item->datatime)->format('H');
})->map(function ($item, $key) use (&$response, $violationTypesKey) {
$item->groupBy('violationtype')->map(function ($item, $viotype) use ($key, &$response, $violationTypesKey) {
$response[intval($key)][$violationTypesKey[$viotype]] = $item->count();
});
});
$records = ViolationParking::query();
if ($year != 0)
$records->whereYear('datatime', $year);
if ($month != 0)
$records->whereMonth('datatime', $month);
if (isset($location))
$records->where('serialnumber', $location);
$records->whereIn('serialnumber', $device);
$data = $records->get();
$data = $data->groupBy(function ($item, $key) {
return Carbon::parse($item->datatime)->format('H');
})->map(function ($item, $key) use (&$response, $violationTypesKey) {
$item->groupBy('violationtype')->map(function ($item, $viotype) use ($key, &$response, $violationTypesKey) {
$response[intval($key)][$violationTypesKey[$viotype]] = $item->count();
});
});
#endregion
return response()->json(['data' => $response, 'violationtype' => $violationTypes], 200);
}
public function index()
{
// 取得所有的機號 地點 從設備表
$device = explode(',', auth()->user()->device) ?? [1];
$devices = [];
// array merge MultisysEquipment::select('serialnumber', 'location')->get();
// violationparking
$devices = array_merge($devices, MultisysEquipment::select('serialnumber', 'location')->whereIn('serialnumber', $device)->get()->toArray());
$devices = array_merge($devices, OverSpeedRedEquipment::select('serialnumber', 'location')->whereIn('serialnumber', $device)->get()->toArray());
$devices = array_merge($devices, ViolationParkingEquipment::select('serialnumber', 'location')->whereIn('serialnumber', $device)->get()->toArray());
return view('system.dashboard', compact('devices'));
}
public function getViolationImage($path)
{
$filePath = str_replace('*', '/', $path);
// dd($filePath);
if (Storage::disk('local')->exists($filePath)) {
$content = Storage::get($filePath);
return response($content)->header("Content-Type", "image");
} else {
return abort(404);
}
}
function getViolationVideo($path)
{
ini_set('memory_limit', '512M'); // 擴充記憶體,否則 Load 影片會爆掉
$filePath = str_replace('*', '/', $path);
// return view('video_faster')->with('path',$filePath);
//return response()->view('video_test')->with('path',$filePath);
if (Storage::disk('local')->exists($filePath)) {
$content = Storage::get($filePath);
return response($content)->header("Content-Type", "video/mp4"); //video/mp4
} else {
return abort(404);
}
}
public function Setting()
{
return view('system.setting.setting');
}
public function Export(Request $request, $fileName)
{
$file = ExportFiles::where('name', $fileName)->first();
$filePath = storage_path('app/public/exports/' . $fileName);
if (file_exists($filePath)) {
return response()->download($filePath, $file->remark . '.' . $file->type);
} else {
return response()->json(['error' => '檔案不存在']);
}
}
public function downloadZip(){
$zip = new ZipArchive;
$rand = Str::random(5);
$fileName = "_$rand.zip";
if ($zip->open(public_path($fileName), ZipArchive::CREATE) === TRUE) {
$zip->addFile(public_path().'/favicon.ico', "favicon.ico");
$zip->close();
}
return response()->download(public_path($fileName));
}
}

300
app/Http/Controllers/System/TimeLogController.php

@ -0,0 +1,300 @@ @@ -0,0 +1,300 @@
<?php
namespace App\Http\Controllers\System;
use App\Class\LogWriter;
use App\Exports\ArrayExport;
use App\Http\Controllers\Controller;
use App\Models\Clientlog;
use App\Models\Clientlogsecond;
use App\Models\Serverlog;
use Carbon\Carbon;
use Illuminate\Http\Request;
use App\Models\ExportFiles;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Maatwebsite\Excel\Facades\Excel;
use Illuminate\Support\Str;
class TimeLogController extends Controller
{
#region view
public function Timelog()
{
$equipment = Clientlog::cachedDistinctName();
return view('system.interval.timelog', compact('equipment'));
}
public function TimelogSecond()
{
$equipment = Clientlogsecond::cachedDistinctName();
return view('system.interval.timelogsecond', compact('equipment'));
}
#endregion
#region api
// 強制校時
#region forceTime
public function ForceTime(Request $request)
{
DB::beginTransaction();
try {
$client = Clientlog::cachedDistinctThisIP();
$txtstring = "";
for ($i = 0; $i < count($client); $i++) {
$txtstring .= $client[$i]->this_ip . "\n";
}
Storage::put(
"\\Timelog\\Flag\\" . "check.txt",
$txtstring
);
$logData = [
'action' => 'forceTime',
'action_detail' => '強制校時',
'ip' => request()->ip(),
'remark' => "強制校時",
];
LogWriter::writeLog($logData, 'api');
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
return response()->json(['message' => 'error'], 500);
}
return response()->json(['message' => 'success'], 200);
}
#endregion
// 取得最新的時間紀錄
#region getLatestTimeLog
public function getLatestTimeLog(Request $request)
{
$data = [];
#region DataTable 搜尋屬性
if (!isset($request->export)) {
$draw = $request->get('draw');
} else {
$draw = 0;
}
#endregion
if ($request->type == 'client') {
// 取得 serverLog 的最新時間紀錄
$server_log = Serverlog::orderBy('this_time', 'desc')->first();
if ($server_log) {
$data[] = $server_log->toArray();
}
// 取得 clientLog 的最新時間紀錄
$distinct_name = Clientlog::cachedDistinctName();
// dd($distinct_name);
foreach ($distinct_name as $name) {
$client_log = Clientlog::where('name', $name->name)->orderBy('this_time', 'desc')->first();
if ($client_log) {
$data[] = $client_log->toArray();
}
}
}
if ($request->type == 'clientsecond') {
// 取得 serverLog 的最新時間紀錄
$server_log = Serverlog::orderBy('this_time', 'desc')->first();
if ($server_log) {
$data[] = $server_log->toArray();
}
// 取得 clientLog 的最新時間紀錄
$distinct_name = Clientlogsecond::cachedDistinctName();
foreach ($distinct_name as $name) {
$client_log = Clientlogsecond::where('name', $name->name)->orderBy('this_time', 'desc')->first();
if ($client_log) {
$data[] = $client_log->toArray();
}
}
}
$totalRecords = count($data);
$totalRecordswithFilter = count($data);
$response = array(
"draw" => intval($draw),
"iTotalRecords" => $totalRecords,
"iTotalDisplayRecords" => $totalRecordswithFilter,
"aaData" => $data
);
echo json_encode($response);
exit;
}
#endregion
// 取得歷史時間紀錄
#region getServerLog
public function getLogHistory(Request $request)
{
// ini set memory limit
ini_set('memory_limit', '1024M');
// ini set time limit
set_time_limit(0);
#region Request 搜尋值
if (isset($request->searchByFromdate))
$searchByFromdate = $request->searchByFromdate;
if (isset($request->searchByTodate))
$searchByTodate = $request->searchByTodate;
#endregion
#region DataTable 搜尋屬性
if (!isset($request->export)) {
$draw = $request->get('draw');
$start = $request->get("start");
$rowperpage = $request->get("length"); // Rows display per page
$columnIndex_arr = $request->get('order');
$columnName_arr = $request->get('columns');
$order_arr = $request->get('order');
$search_arr = $request->get('search');
$columnIndex = $columnIndex_arr[0]['column']; // Column index
$columnName = $columnName_arr[$columnIndex]['data']; // Column name
$columnSortOrder = $order_arr[0]['dir']; // asc or desc
$searchValue = $search_arr['value']; // Search value
} else {
$draw = 0;
}
#endregion
// Fetch records
if ($request->type == 'client') {
$records = Clientlog::query();
}
if ($request->type == 'clientsecond') {
$records = Clientlogsecond::query();
}
if ($request->type == 'server') {
$records = Serverlog::query();
}
if (isset($columnName))
$records->orderBy($columnName, $columnSortOrder);
if (isset($searchValue)) {
$records->where(function ($query) use ($searchValue) {
$query->where('name', 'like', '%' . $searchValue . '%')
->orwhere('to_ip', 'like', '%' . $searchValue . '%')
->orwhere('this_ip', 'like', '%' . $searchValue . '%')
->orwhere('this_time', 'like', '%' . $searchValue . '%')
->orwhere('diffsecond', 'like', '%' . $searchValue . '%')
->orwhere('to_time', 'like', '%' . $searchValue . '%');
});
}
if (isset($request->name)){
$records->where('name', $request->name);
}
//時間限制
if (isset($searchByFromdate))
$records->where('this_time', ">", $searchByFromdate . ' 00:00:00');
if (isset($searchByTodate))
$records->where('this_time', "<", $searchByTodate . ' 23:59:59');
$totalRecords = $records->count();
$totalRecordswithFilter = $records->count();
#region DataTable 分頁(報表不分頁)
if (!isset($request->export)) {
if (isset($start))
$records->skip($start);
if (isset($rowperpage))
$records->take($rowperpage);
}
#endregion
$records = $records->get();
#region 資料處理
$data_arr = array();
$sno = 1 + ($request->get('start') ?? 0);
foreach ($records as $record) {
//還原用id
//報表用的欄位,避免前端錯誤,給預設值
$count = $record->count ?? 0;
$period = $record->period ?? "";
$data_arr[] = array(
// 校時對象時間 校時對象IP before_time本機校時前(本機校時後 - 時間誤差 毫秒三位數) 本機校時後 時間誤差(ms) 本機IP 本機名稱
// "sno" => $sno++,
"to_time" => $record->to_time,
"to_ip" => $record->to_ip,
"before_time" => $record->before_time,
"diffsecond" => $record->diffsecond,
"this_time" => $record->this_time,
"this_ip" => $record->this_ip,
"name" => $record->name,
"pushed" => $record->pushed ?? "",
"force_time" => $record->force_time ?? "0",
);
}
if (isset($request->export)) {
if ($request->type == 'client')
$remark = '設備端校時資料';
if ($request->type == 'clientsecond')
$remark = '設備端計時資料';
if ($request->type == 'server')
$remark = '伺服器端校時資料';
// if ($request->type != 'server')
// $columns = ['to_time', 'to_ip', 'before_time', 'this_time', 'diffsecond', 'this_ip', 'name', 'pushed'];
// else
$columns = ['to_time', 'to_ip', 'before_time', 'this_time', 'diffsecond', 'this_ip', 'name'];
$columnTitle = [
["【表單名稱】:", $remark],
["【查詢日期區間】:", $request->searchByFromdate, " ~ ", $request->searchByTodate],
['校時對象時間', '校時對象IP', '本機校時前', '本機校時後', '時間誤差(ms)', '本機IP', '本機名稱']
];
if (isset($request->searchByFromdate) && isset($request->searchByTodate)) {
$remark = $remark . ' ' . $request->searchByFromdate . ' ~ ' . $request->searchByTodate;
}
$data = array_map(function ($row) use ($columns) {
// 如果欄位diffsecond為0 顯示0
$data = array_merge(array_flip($columns), array_intersect_key($row, array_flip($columns)));
$data['diffsecond'] = $data['diffsecond'] == 0 ? "0" : $data['diffsecond'];
return $data;
}, $data_arr);
$fileName = $request->type . 'log-' . Str::random(10) . '.xlsx';
ExportFiles::create([
'name' => $fileName,
'path' => 'public/exports',
'type' => 'xlsx',
'status' => '1',
'remark' => "$remark",
'user_id' => auth('api')->user()->id,
]);
Excel::store(new ArrayExport($data, $columnTitle), 'public/exports/' . $fileName, 'local', \Maatwebsite\Excel\Excel::XLSX);
return response()->json(['url' => route('system.filedownload', ['path' => $fileName])], 200);
}
$response = array(
"draw" => intval($draw),
"iTotalRecords" => $totalRecords,
"iTotalDisplayRecords" => $totalRecordswithFilter,
"aaData" => $data_arr
);
echo json_encode($response);
exit;
}
#endregion
#endregion
}

702
app/Http/Controllers/System/ViolationParkingController.php

@ -0,0 +1,702 @@ @@ -0,0 +1,702 @@
<?php
namespace App\Http\Controllers\System;
use App\Class\LogWriter;
use App\Http\Controllers\Controller;
use App\Models\ViolationLaw;
use App\Models\ViolationParking;
use App\Models\ViolationParkingEquipment;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Exports\ArrayExport;
use App\Models\ExportFiles;
use App\Services\ViolationParkingService;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Maatwebsite\Excel\Facades\Excel;
use Illuminate\Support\Str;
use ZipArchive;
class ViolationParkingController extends Controller
{
public $destPath_root;
public $clientDestPath_root;
public $unreportPath_root;
public $month;
function __construct()
{
$this->middleware('permission:vpk-review|vpk-reduction|vpk-device-setting|vpk-statistics|vpk-analysis|vpk-read', ['only' => ['VpkReview']]);
$this->middleware('permission:vpk-review|permission:vpk-reduction', ['only' => ['vpkCensored', 'update']]);
$this->middleware('permission:vpk-device-setting', ['only' => ['VpkDeviceSetting']]);
$this->middleware('permission:vpk-statistics', ['only' => ['VpkStatistics']]);
$this->middleware('permission:vpk-analysis', ['only' => ['VpkAnalysis']]);
// OK path
$this->destPath_root = 'E:\\OK\\VPK\\';
$this->clientDestPath_root = 'E:\\';
// NOK path
$this->unreportPath_root = 'C:\\xampp\\SMMS\\storage\\app\\ParsingFiles\\NOK\\VPK\\';
$this->month['start'] = Carbon::now()->subDays(8)->format('Y-m-d');
$this->month['current'] = Carbon::now()->subDays(1)->format('Y-m-d');
}
public function VpkReview()
{
$device = explode(',', auth()->user()->device) ?? [1];
// dd($device);
$equipmentData = new ViolationParkingEquipment();
$serialNumber = $equipmentData::whereIn('serialnumber', $device)->groupBy('serialnumber')->get(['serialnumber', 'location']);
$violationparking = new ViolationParking();
$locationtype = $violationparking::where('processcheck', 0)->whereIn('serialnumber', $device)->groupBy('location')->pluck('location');
// dd($data,$page_data);
$vio_arr = [];
foreach (ViolationLaw::whereIn('type', ['違規停車'])->get() as $key => $item) {
$vio_arr[$key]['violationcode'] = $item->violationcode;
$vio_arr[$key]['display_name'] = $item->display_name;
}
// dd($vio_arr);
return view('system.violationParking.index')
->with('serialNumber', $serialNumber)
->with('locationtype', $locationtype)
->with('vio_arr', $vio_arr)
->with('month', $this->month);
}
#region 已審 View
public function vpkCensored(Request $request)
{
$device = explode(',', auth()->user()->device) ?? [1];
$equipmentData = new ViolationParkingEquipment();
$serialNumber = $equipmentData::whereIn('serialnumber', $device)->groupBy('serialnumber')->get(['serialnumber', 'location']);
$violationparking = new ViolationParking();
$locationtype = $violationparking::where('processcheck', 1)->whereIn('serialnumber', $device)->groupBy('location')->pluck('location');
// dd($data,$page_data);
return view('system.violationParking.manage')
->with('serialNumber', $serialNumber)
->with('locationtype', $locationtype)
->with('month', $this->month);
}
#endregion
#region 不舉發 View
public function vpkUnreport(Request $request)
{
$device = explode(',', auth()->user()->device) ?? [1];
$equipmentData = new ViolationParkingEquipment();
$serialNumber = $equipmentData::whereIn('serialnumber', $device)->groupBy('serialnumber')->get(['serialnumber', 'location']);
$violationparking = new ViolationParking();
$locationtype = $violationparking::where('processcheck', 2)->whereIn('serialnumber', $device)->groupBy('location')->pluck('location');
$unreportreason = $violationparking->where('processcheck', 2)->whereIn('serialnumber', $device)->groupBy('unreportreason')->pluck('unreportreason');
// dd($locationtype,$unreportreason);
return view('system.violationParking.unreport')
->with('serialNumber', $serialNumber)
->with('locationtype', $locationtype)
->with('unreportreason', $unreportreason)
->with('month', $this->month);
}
#endregion
public function update(Request $request, $id)
{
if (!auth('api')->user()->can('vpk-review')) {
return response()->json(['error' => '無權限審查此案件'], 403);
}
$vpk = ViolationParking::findOrFail($id);
$result = app(ViolationParkingService::class)->review($vpk, $request->all(), auth('api')->user());
return response()->json($result);
}
#region DataTable (AJAX刷新用 參數 processcheckStatus 0未審 1已審 2不舉發 "99清冊用")使用
public function getDataTable(Request $request)
{
// dd($request->all());
#region 設備限制
$device = explode(',', auth("api")->user()->device) ?? [1];
// dd($device);
#endregion
$processcheckStatus = $request->processcheckStatus;
// dd($processcheckStatus, $device);
[$draw, $totalRecords, $totalRecordswithFilter, $data_arr] = $this->getData($device, $request, $processcheckStatus);
$response = array(
"draw" => intval($draw),
"iTotalRecords" => $totalRecords,
"iTotalDisplayRecords" => $totalRecordswithFilter,
"aaData" => $data_arr
);
// dd($response);
echo json_encode($response);
exit;
}
#endregion
#region 取得資料
function getData($device, $request, $processcheckStatus = null)
{
// 審查狀態 0 未審 1 已審查 2 不舉發 99清冊
// $processcheckStatus = 2;
// dd($device, $request, $processcheckStatus);
#region Request 搜尋值
if (isset($request->searchByFromdate))
$searchByFromdate = $request->searchByFromdate;
if (isset($request->searchByTodate))
$searchByTodate = $request->searchByTodate;
//地點搜尋
if (isset($request->location))
$location = $request->location;
//車牌搜尋
if (isset($request->carnumber))
$carnumber = $request->carnumber;
//不舉發理由搜尋
if (isset($request->unreportreason))
$unreportreason = $request->unreportreason;
#endregion
#region DataTable 搜尋屬性
if (!isset($request->export)) {
$draw = $request->get('draw');
$start = $request->get("start");
$rowperpage = $request->get("length"); // Rows display per page
$columnIndex_arr = $request->get('order');
$columnName_arr = $request->get('columns');
$order_arr = $request->get('order');
$search_arr = $request->get('search');
$columnIndex = $columnIndex_arr[0]['column']; // Column index
$columnName = $columnName_arr[$columnIndex]['data']; // Column name
$columnSortOrder = $order_arr[0]['dir']; // asc or desc
$searchValue = $search_arr['value']; // Search value
} else {
$draw = 0;
}
#endregion
// Fetch records
$records = ViolationParking::query();
if (isset($columnName))
$records->orderBy($columnName, $columnSortOrder);
if (isset($searchValue))
$records->where('carnumber', 'like', '%' . $searchValue . '%');
//已審未審
//清冊用 99
$records->where('violationtype', '!=', '違規臨時停車');
if ($processcheckStatus == 99) {
$processcheckStatus = $request->processCheck;
if ($processcheckStatus == 99 || $processcheckStatus == null)
$records->whereIn('processcheck', [0, 1, 2]);
else {
$records->where('processcheck', $processcheckStatus);
}
} else {
$records->where('processcheck', $processcheckStatus);
}
//設備限制
$records->whereIn('serialnumber', $device);
//時間限制
if (isset($searchByFromdate))
$records->where('datatime', ">", $searchByFromdate . ' 00:00:00');
if (isset($searchByTodate))
$records->where('datatime', "<", $searchByTodate . ' 23:59:59');
//地點篩選
if (isset($location))
$records->where('serialnumber', $location);
//車牌篩選
if (isset($carnumber))
$records->where('carnumber', 'like', '%' . $carnumber . '%');
//不舉發理由
if (isset($unreportreason))
$records->where('unreportreason', 'like', '%' . $unreportreason . '%');
$totalRecords = $records->count();
$totalRecordswithFilter = $records->count();
//如果有報表類型(1,2,3,...),並需要特別處理欄位的報表
if (isset($request->statisticstype)) {
if ($request->statisticstype == 2){
$records->groupBy('serialnumber', 'cartype')->select('*', DB::raw('count(*) as count'));
$totalRecords = $records->get()->count();
$totalRecordswithFilter = $records->get()->count();
}
if ($request->statisticstype == 3) {
$groupbyPara1 = "CAST(DATE_FORMAT(datatime, '%H') AS SIGNED)";
$records->groupBy(DB::raw($groupbyPara1), 'serialnumber', 'violationtype')->select('*', DB::raw('COUNT(*) as count, CONCAT(CAST(DATE_FORMAT(datatime,"%H") as SIGNED),"-",CAST(DATE_FORMAT(DATE_ADD(datatime,INTERVAL +1 HOUR),"%H") as SIGNED)) AS period'));
$records->orderBy(DB::raw($groupbyPara1), 'ASC');
$totalRecords = $records->get()->count();
$totalRecordswithFilter = $records->get()->count();
}
} else {
$records->select('*');
}
if (!isset($request->export)) {
if (isset($start))
$records->skip($start);
if (isset($rowperpage))
$records->take($rowperpage);
}
$records = $records->get();
$data_arr = array();
// $sno = $start + 1;
foreach ($records as $record) {
//還原用id
$id = $record->id;
//不舉發原因
$unreportreason = $record->unreportreason;
$datatime = $record->datatime;
$serialnumber = $record->serialnumber;
$location = $record->location;
$carnumber = $record->carnumber;
$picture = $record->picture;
$picture2 = $record->picture2;
$violationtype = $record->violationtype;
$cartype = $record->cartype;
$unreportpicture = $record->unreportpicture;
$processcheck = $record->processcheck;
$postcheck = $record->postcheck ?? 0;
//報表用的欄位,避免前端錯誤,給預設值
$count = $record->count ?? 0;
$period = $record->period ?? "";
$cartypeTitle = [
'1' => "汽車",
'2' => "機車",
'3' => "重型機車",
'4' => "輕型機車"
];
$processCheckTitle = [
'0' => "未審",
'1' => "已審查",
'2' => "不舉發"
];
if (!isset($cartype))
$cartype = 1;
$data_arr[] = array(
"id" => $id,
"datatime" => $datatime,
"serialnumber" => $serialnumber,
"location" => $location,
"carnumber" => $carnumber,
"picture" => $picture,
"picture2" => $picture2,
//影片連結 (用照片的路徑取代成mp4 picture= _A picture2= _B)
"video" => str_replace('.jpg', '.mp4', $picture2),
"violationtype" => $violationtype,
"cartype" => $cartype,
"carkind" => $cartypeTitle[$cartype],
"unreportreason" => $unreportreason ?? "",
"unreportpicture" => $unreportpicture ?? $picture,
"postcheck" => $postcheck,
// 清冊用
"processcheck" => $processCheckTitle[$processcheck],
// "searchByFromdate" => $searchByFromdate,
// "searchByTodate"=> $searchByTodate,
"count" => $count,
"period" => $period
);
}
return [$draw, $totalRecords, $totalRecordswithFilter, $data_arr];
}
#endregion
// 清冊用
public function vpkStatistics()
{
$device = explode(',', auth()->user()->device) ?? [1];
// 違規地點 group by
$equipmentData = new ViolationParkingEquipment();
$serialNumber = $equipmentData::whereIn('serialnumber', $device)->groupBy('serialnumber')->get(['serialnumber', 'location']);
// 違規類型 group by
$violationparking = new ViolationParking();
$violationtype = $violationparking::whereIn('serialnumber', $device)->groupBy('violationtype')->pluck('violationtype');
return view('system.violationParking.statistics')
->with('serialNumber', $serialNumber)
->with('violationtype', $violationtype);
}
public function getStatisticsData(Request $request)
{
#region 設備限制
$device = explode(',', auth("api")->user()->device) ?? [1];
#endregion
$processcheckStatus = 99;
[$draw, $totalRecords, $totalRecordswithFilter, $data_arr] = $this->getData($device, $request, $processcheckStatus);
$response = array(
"draw" => intval($draw),
"iTotalRecords" => $totalRecords,
"iTotalDisplayRecords" => $totalRecordswithFilter,
"aaData" => $data_arr
);
return $response;
}
public function getStatisticsDataExport(Request $request)
{
set_time_limit(0);
ini_set('memory_limit', '2048M');
#region 設備限制
// $device = ViolationParkingEquipment::all()->pluck('serialnumber')->toArray();
$device = explode(',', auth("api")->user()->device) ?? [1];
#endregion
$processcheckStatus = 99;
if ($request->statisticstype == 1) {
$columns = ['datatime', 'serialnumber', 'location', 'carnumber', 'violationtype', 'processcheck'];
$columnTitle = [
["【表單名稱】:", "違規件數清冊"],
["【違規日期區間】:", $request->searchByFromdate, " ~ ", $request->searchByTodate],
['違規時段', '設備編號', '違規地點', '車號', '違規類型', '審查狀態']
];
} elseif ($request->statisticstype == 2) {
$columns = ['serialnumber', 'location', 'violationtype', 'carkind', 'count'];
$columnTitle = [
["【表單名稱】:", "違規件數統計清冊"],
["【違規日期區間】:", $request->searchByFromdate, " ~ ", $request->searchByTodate],
['設備編號', '違規地點', '違規類型', '車種', '統計']
];
} elseif ($request->statisticstype == 3) {
$columns = ['serialnumber', 'location', 'period', 'violationtype', 'count'];
$columnTitle = [
["【表單名稱】:", "違規時段件數統計清冊"],
["【違規日期區間】:", $request->searchByFromdate, " ~ ", $request->searchByTodate],
['設備編號', '違規地點', '違規時段', '違規類型', '統計']
];
}
[$draw, $totalRecords, $totalRecordswithFilter, $data_arr] = $this->getData($device, $request, $processcheckStatus);
$data = array_map(function ($row) use ($columns) {
return array_merge(array_flip($columns), array_intersect_key($row, array_flip($columns)));
}, $data_arr);
$fileName = 'vpk-' . Str::random(10) . '.xlsx';
ExportFiles::create([
'name' => $fileName,
'path' => 'public/exports',
'type' => 'xlsx',
'status' => '1',
'remark' => '違規件數清冊',
'user_id' => auth('api')->user()->id,
]);
Excel::store(new ArrayExport($data, $columnTitle), 'public/exports/' . $fileName, 'local', \Maatwebsite\Excel\Excel::XLSX);
return response()->json(['url' => route('vpk-export', ['fileName' => $fileName])], 200);
}
public function vpkExport(Request $request, $fileName)
{
$file = ExportFiles::where('name', $fileName)->first();
$filePath = storage_path('app/public/exports/' . $fileName);
if (file_exists($filePath)) {
return response()->download($filePath, $file->remark . '.' . $file->type);
} else {
return response()->json(['error' => '檔案不存在']);
}
}
// 數據分析 vpkAnalysis
public function vpkAnalysis()
{
$device = explode(',', auth()->user()->device) ?? [1];
// 違規地點 group by
$equipmentData = new ViolationParkingEquipment();
$serialNumber = $equipmentData::whereIn('serialnumber', $device)->groupBy('serialnumber')->get(['serialnumber', 'location']);
// 違規類型 group by
$violationparking = new ViolationParking();
$violationtype = $violationparking::whereIn('serialnumber', $device)->groupBy('violationtype')->pluck('violationtype');
return view('system.violationParking.analysis')
->with('serialNumber', $serialNumber)
->with('violationtype', $violationtype);
}
public function getAnalysisData(Request $request)
{
// memory
ini_set('memory_limit', '2048M');
// timeout
set_time_limit(0);
// $device = ViolationParkingEquipment::all()->pluck('serialnumber')->toArray();
$device = explode(',', auth("api")->user()->device) ?? [1];
$records = ViolationParking::query();
$records->whereIn('serialnumber', $device)->whereIn('processcheck', [1, 3]);
// dd($request->all());
if (isset($request->search_fromdate))
$records->where('datatime', ">", $request->search_fromdate . ' 00:00:00');
else //近七天
$records->where('datatime', ">", '2020-01-01 00:00:00');
if (isset($request->search_todate))
$records->where('datatime', "<", $request->search_todate . ' 23:59:59');
else
$records->where('datatime', "<", Carbon::now()->format('Y-m-d') . ' 23:59:59');
//地點搜尋
if (isset($request->location))
$records->where('serialnumber', $request->location);
if (isset($request->violation_type))
$records->where('violationtype', $request->violation_type);
$data = $records->get();
if (isset($request->chart_type)) {
if ($request->chart_type == '1') {
// 根據違規類型分類 並計算每一類的總數
$data = $data->groupBy('violationtype')->map(function ($item, $key) {
return [
'title' => $key,
'times' => $item->count()
];
})->toArray();
$data = array_values($data);
} elseif ($request->chart_type == '2') {
// 根據違規類型分類 並計算每一類的總數
$data = $data->groupBy('location')->map(function ($item, $key) {
return [
'title' => $key,
'times' => $item->count()
];
})->toArray();
$data = array_values($data);
} elseif ($request->chart_type == '3') {
$records2 = ViolationParking::query();
$records2->whereIn('serialnumber', $device)->whereIn('processcheck', [1, 3]);
// dd($request->all());
if (isset($request->search_fromdate))
$records2->where('datatime', ">", $request->search_fromdate . ' 00:00:00');
else //近七天
$records2->where('datatime', ">", '2020-01-01 00:00:00');
if (isset($request->search_todate))
$records2->where('datatime', "<", $request->search_todate . ' 23:59:59');
else
$records2->where('datatime', "<", Carbon::now()->format('Y-m-d') . ' 23:59:59');
//地點搜尋
if (isset($request->location))
$records2->where('serialnumber', $request->location);
if (isset($request->violation_type))
$records2->where('violationtype', $request->violation_type);
$violationTypes = $records2->groupBy('violationtype')->select('violationtype')->get()->toArray();
$violationTypes = array_column($violationTypes, 'violationtype');
// violationTypes key value 互換
$violationTypesKey = array_flip($violationTypes);
// dd($violationTypes);
// 初始化結果陣列
$response = [];
for ($hour = 0; $hour <= 23; $hour++) {
// 補零 01, 02, 03, ..., 09
// $hour = str_pad($hour, 2, '0', STR_PAD_LEFT);
// 初始化結果陣列
$response[$hour] = [
'category' => $hour,
];
for ($i = 0; $i < count($violationTypes); $i++) {
$response[$hour][$i] = 0;
}
}
$data = $records->get();
$data = $data->groupBy(function ($item, $key) {
return intval(Carbon::parse($item->datatime)->format('H'));
})->map(function ($item, $key) use (&$response, $violationTypesKey) {
$item->groupBy('violationtype')->map(function ($item, $viotype) use ($key, &$response, $violationTypesKey) {
$response[$key][$violationTypesKey[$viotype]] = $item->count();
});
});
$data = $response;
}
}
// dd($data);
return response()->json(['data' => $data, 'chart_type' => $request->chart_type, 'violationtype' => $violationTypes ?? ''], 200);
}
#region 更新Ini
function updateIni($path, $oldCarNumber, $newCarNumber, $oldViolationType, $newViolationType)
{
// $oldCarNumber = explode('"', "$oldCarNumber")[1];
$iniPath = str_replace('jpg', 'ini', str_replace('*', '\\', storage_path('app' . '\\' . 'ParsingFiles' . '\\' . "$path")));
$iniFile = fopen("$iniPath", "r+");
$iniData = fread($iniFile, filesize("$iniPath"));
$iniData = iconv('Big5', 'UTF-8', "$iniData");
// if(strpos($iniData,'車號')){
// $iniData = str_replace("$oldCarNumber", "$newCarNumber", "$iniData");
// }else{
// $iniData = str_replace("\r\n操作者姓名", "\r\n車號="."$newCarNumber"."\r\n操作者姓名", "$iniData");
// }
$iniData = str_replace("$oldCarNumber", "$newCarNumber", "$iniData");
$iniData = str_replace("$oldViolationType", "$newViolationType", "$iniData");
$iniData = iconv('UTF-8', 'Big5', "$iniData");
$iniFile = fopen("$iniPath", "w+");
fwrite($iniFile, "$iniData");
fclose($iniFile);
}
function saveFinishFile($data)
{
//路徑位置設定
$destPath_root = $this->destPath_root;
$clientDestPath_root = $this->clientDestPath_root;
// $ftpPath_root = $this->ftpPath_root;
$locPath = "[" . $data['serialnumber'] . "]" . $data['location'];
$path = $data['picture'];
// $path2 = $data['picture2'];
$time = str_replace('-', '', explode(' ', $data['datatime'])[0]);
$iniPath = str_replace('jpg', 'ini', str_replace('*', '\\', storage_path('app' . '\\' . 'ParsingFiles' . '\\' . "$path")));
$photoPath = str_replace('*', '\\', storage_path('app' . '\\' . 'ParsingFiles' . '\\' . "$path"));
// $photoPath2 = str_replace('*','\\', storage_path('app'.'\\'.'ParsingFiles'.'\\'."$path2"));
// $photoName = explode('*', $data['picture'])[4]; //原始有"時" 資料夾的層數
$photoName = explode('*', $data['picture'])[3];
// $photoName2 = explode('*', $data['picture2'])[3];
$destPath = $destPath_root . Auth::user()->account . "\\" . $time . "\\" . $locPath;
$clientDestPath = $clientDestPath_root . Auth::user()->account . "\\" . $time . "\\" . $locPath;
if (is_dir($destPath) === false) {
mkdir($destPath, 0777, true);
}
if (is_dir($clientDestPath) === false) {
mkdir($clientDestPath, 0777, true);
}
try {
copy($iniPath, $destPath . '\\' . str_replace('jpg', 'ini', $photoName));
copy($photoPath, $destPath . '\\' . $photoName);
// copy($photoPath2, $destPath.'\\'.$photoName2);
// 存入第二路徑供客戶操作資料
copy($iniPath, $clientDestPath . '\\' . str_replace('jpg', 'ini', $photoName));
copy($photoPath, $clientDestPath . '\\' . $photoName);
// copy($photoPath2, $clientDestPath.'\\'.$photoName2);
// 存入第FTP路徑供客戶操作資料
// $ftpPath = $ftpPath_root.$time;
// if( is_dir($ftpPath) === false ) { mkdir($ftpPath, 0777, true);}
// copy($iniPath, $ftpPath.'\\'.str_replace('jpg','ini', $photoName));
// copy($photoPath, $ftpPath.'\\'.$photoName);
} catch (\Throwable $th) {
//throw $th;
}
}
#endregion
#region 下載ZIP
function downloadZipPicture(Request $request)
{
set_time_limit(0);
ini_set('memory_limit', '2048M');
$device = explode(',', auth("api")->user()->device) ?? [1];
if (isset($request->searchByFromdate))
$searchByFromdate = $request->searchByFromdate;
if (isset($request->searchByTodate))
$searchByTodate = $request->searchByTodate;
if (isset($request->location))
$serialnumber = $request->location;
if (isset($request->carnumber))
$carnumber = $request->carnumber;
if (isset($request->processcheck))
$processcheck = $request->processcheck;
else $processcheck = 1;
$records = ViolationParking::query();
$records->where('location', 'not like', '%測試%');
$records->whereIn('serialnumber', $device);
$records->where('processcheck', $processcheck);
if (isset($searchByFromdate))
$records->where('datatime', ">", $searchByFromdate . ' 00:00:00');
else {
$records->where('datatime', ">", Carbon::now()->subMonths(2));
$searchByFromdate = '';
}
if (isset($searchByTodate))
$records->where('datatime', "<", $searchByTodate . ' 23:59:59');
else $searchByTodate = '';
if (isset($serialnumber))
$records->where('serialnumber', $request->location);
if (isset($carnumber))
$records->where('carnumber', 'like', '%' . $carnumber . '%');
$records = $records->pluck("picture");
for ($i = 0; $i < count($records); $i++) {
$records[$i] = storage_path("app\\ParsingFiles\\" . str_replace('*', '\\', $records[$i]));
}
$fileName = $this->downloadZip($records, $request->location, $searchByFromdate, $searchByTodate, $processcheck);
if (file_exists($fileName)) {
ExportFiles::create([
'name' => $fileName,
'path' => 'public',
'type' => 'zip',
'status' => '1',
'remark' => "違規停車-下載ZIP",
'user_id' => auth('api')->user()->id,
]);
return $fileName;
} else return "沒有檔案";
}
public function downloadZip($files, $serialnumber, $searchByFromdate, $searchByTodate, $processcheck)
{
set_time_limit(0);
ini_set('memory_limit', '2048M');
try {
foreach (glob(public_path() . '\*.zip') as $file) {
unlink($file);
}
} catch (\Throwable $th) {
//throw $th;
}
$zip = new ZipArchive;
$rand = Str::random(5);
$fileName = "_$rand.zip";
if (isset($searchByTodate))
$fileName = str_replace('-', '', $searchByTodate) . $fileName;
if (isset($searchByFromdate))
$fileName = str_replace('-', '', $searchByFromdate) . "_" . $fileName;
if (isset($serialnumber))
$fileName = $serialnumber . "_" . $fileName;
if (isset($processcheck)) {
if ($processcheck == 1)
$fileName = "VPK_OK_" . $fileName;
if ($processcheck == 2)
$fileName = "VPK_X_" . $fileName;
}
if ($zip->open(public_path($fileName), ZipArchive::CREATE) === TRUE) {
foreach ($files as $file) {
try {
$name = explode("\\", $file);
$iniPath = str_replace('.jpg', '.ini', $file);
$iniName = explode("\\", $iniPath);
if (end($name) != '') {
$zip->addFile($file, end($name));
$zip->addFile($iniPath, end($iniName));
}
} catch (\Throwable $th) {
//throw $th;
Log::error($th);
}
}
$zip->close();
}
// response()->download(public_path($fileName))->deleteFileAfterSend(true);
return "$fileName";
}
#endregion
}

258
app/Http/Controllers/System/ViolationParkingEquipmentController.php

@ -0,0 +1,258 @@ @@ -0,0 +1,258 @@
<?php
namespace App\Http\Controllers\System;
use App\Class\LogWriter;
use App\Models\ViolationParkingEquipment;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\AssetOwnership;
use App\Models\CustodyUnit;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class ViolationParkingEquipmentController extends Controller
{
public function __construct()
{
$this->middleware('permission:vpk-device-setting');
}
public function page()
{
// ['custodyunit' => $custodyunit, 'assetownership' => $assetownership]
$custodyunit = CustodyUnit::all();
$assetownership = AssetOwnership::all();
return view('system.violationparking.equipment')
->with('custodyunit', $custodyunit)
->with('assetownership', $assetownership);
}
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
#region DataTable 搜尋屬性
$draw = $request->get('draw');
$start = $request->get("start");
$rowperpage = $request->get("length"); // Rows display per page
$columnIndex_arr = $request->get('order');
$columnName_arr = $request->get('columns');
$order_arr = $request->get('order');
$search_arr = $request->get('search');
$columnIndex = $columnIndex_arr[0]['column']; // Column index
$columnName = $columnName_arr[$columnIndex]['data']; // Column name
$columnSortOrder = $order_arr[0]['dir']; // asc or desc
$searchValue = $search_arr['value']; // Search value
#endregion
// Role with permissions
$records = ViolationParkingEquipment::query();
$totalRecords = $records->count();
if (isset($columnName))
$records->orderBy($columnName, $columnSortOrder);
if (isset($searchValue)) {
$records->where(function ($query) use ($searchValue) {
// $query->where('outlet_id', 'like', '%' . $searchValue . '%')
// ->orwhere('name', 'like', '%' . $searchValue . '%')
// ->orwhere('serialnumber', 'like', '%' . $searchValue . '%')
// ->orwhere('creator_id', 'like', '%' . $searchValue . '%');
});
}
$totalRecordswithFilter = $records->count();
if (isset($start))
$records->skip($start);
if (isset($rowperpage))
$records->take($rowperpage);
$data_arr = $records->get();
$response = array(
"draw" => intval($draw),
"iTotalRecords" => $totalRecords,
"iTotalDisplayRecords" => $totalRecordswithFilter,
"aaData" => $data_arr
);
return response()->json($response);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$device = new ViolationParkingEquipment();
DB::beginTransaction();
try {
$device->serialnumber = $request->serialnumber;
$device->brand = $request->brand;
$device->model = $request->model;
$device->custodyunit_id = $request->custodyunit_id;
$device->assetownership_id = $request->assetownership_id;
$device->buydate = $request->buydate;
$device->activatedate = $request->activatedate;
$device->locationid = $request->locationid;
$device->location = $request->location;
$device->comment = $request->comment;
$device->precinct = $request->precinct;
$device->station = $request->station;
$device->save();
$logData = [
'action' => 'create',
'action_detail' => '新增設備',
'ip' => request()->ip(),
'remark' => "新增設備:{$device->serialnumber}",
];
logWriter::writeLog($logData, 'api');
DB::commit();
return response()->json(['success' => true, 'message' => '新增成功']);
} catch (\Throwable $th) {
//throw $th;
Log::error($th);
DB::rollBack();
return response()->json(['success' => false, 'message' => '新增失敗']);
}
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
$device = ViolationParkingEquipment::find($id);
return response()->json(['success' => true, 'data' => $device]);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id)
{
$device = ViolationParkingEquipment::find($id);
DB::beginTransaction();
try {
$device->serialnumber = $request->serialnumber;
$device->brand = $request->brand;
$device->model = $request->model;
$device->custodyunit_id = $request->custodyunit_id ?? null;
$device->assetownership_id = $request->assetownership_id ?? null;
$device->buydate = $request->buydate;
$device->activatedate = $request->activatedate;
$device->locationid = $request->locationid;
$device->location = $request->location;
$device->comment = $request->comment;
$device->precinct = $request->precinct;
$device->station = $request->station;
$device->save();
$logData = [
'action' => 'update',
'action_detail' => '更新設備',
'ip' => request()->ip(),
'remark' => "更新設備:{$device->serialnumber}",
];
logWriter::writeLog($logData, 'api');
DB::commit();
return response()->json(['success' => true, 'message' => '更新成功']);
} catch (\Throwable $th) {
//throw $th;
Log::error($th);
DB::rollBack();
return response()->json(['success' => false, 'message' => '更新失敗']);
}
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
$device = ViolationParkingEquipment::find($id);
try {
$device->delete();
return response()->json(['success' => true, 'message' => '刪除成功']);
} catch (\Throwable $th) {
//throw $th;
Log::error($th);
return response()->json(['success' => false, 'message' => '刪除失敗']);
}
}
/**
* 取得保管單位資料
*/
public function indexUnit(Request $request)
{
if($request->type == "custodyunit"){
$unit = CustodyUnit::all();
return response()->json(['success' => true, 'data' => $unit]);
}else if ($request->type == "assetownership"){
$unit = AssetOwnership::all();
return response()->json(['success' => true, 'data' => $unit]);
}
return response()->json(['success' => false, 'message' => '取得失敗']);
}
/**
* 新增單位資料
*/
public function storeUnit(Request $request)
{
// custodyunit_id
//assetownership_id)
DB::beginTransaction();
try {
if($request->type == "custodyunit"){
$unit = new CustodyUnit();
$unit->name = $request->name;
$unit->save();
$logData = [
'action' => 'create',
'action_detail' => '新增保管單位',
'ip' => request()->ip(),
'remark' => "新增保管單位:{$request->name}",
];
LogWriter::writeLog($logData, 'api');
DB::commit();
return response()->json(['success' => true, 'message' => '保管單位-新增成功']);
}else if ($request->type == "assetownership"){
$unit = new AssetOwnership();
$unit->name = $request->name;
$unit->save();
$logData = [
'action' => 'create',
'action_detail' => '新增財產所屬單位',
'ip' => request()->ip(),
'remark' => "新增財產所屬單位:{$request->name}",
];
LogWriter::writeLog($logData, 'api');
DB::commit();
return response()->json(['success' => true, 'message' => '財產所屬單位-新增成功']);
}
} catch (\Throwable $th) {
//throw $th;
Log::error($th);
DB::rollBack();
}
return response()->json(['success' => false, 'message' => '新增失敗']);
}
}

131
app/Http/Controllers/UserController.php

@ -0,0 +1,131 @@ @@ -0,0 +1,131 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\User;
use Spatie\Permission\Models\Role;
use DB;
use Hash;
use Illuminate\Support\Arr;
use Illuminate\View\View;
use Illuminate\Http\RedirectResponse;
class UserController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request): View
{
$data = User::latest()->paginate(5);
return view('users.index', compact('data'))
->with('i', ($request->input('page', 1) - 1) * 5);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create(): View
{
$roles = Role::pluck('name', 'name')->all();
return view('users.create', compact('roles'));
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request): RedirectResponse
{
$this->validate($request, [
'name' => 'required',
'email' => 'required|email|unique:users,email',
'password' => 'required|same:confirm-password',
'roles' => 'required'
]);
$input = $request->all();
$input['password'] = Hash::make($input['password']);
$user = User::create($input);
$user->assignRole($request->input('roles'));
return redirect()->route('users.index')
->with('success', 'User created successfully');
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id): View
{
$user = User::find($id);
return view('users.show', compact('user'));
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id): View
{
$user = User::find($id);
$roles = Role::pluck('name', 'name')->all();
$userRole = $user->roles->pluck('name', 'name')->all();
return view('users.edit', compact('user', 'roles', 'userRole'));
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id): RedirectResponse
{
$this->validate($request, [
'name' => 'required',
'email' => 'required|email|unique:users,email,' . $id,
'password' => 'same:confirm-password',
'roles' => 'required'
]);
$input = $request->all();
if (!empty($input['password'])) {
$input['password'] = Hash::make($input['password']);
} else {
$input = Arr::except($input, array('password'));
}
$user = User::find($id);
$user->update($input);
DB::table('model_has_roles')->where('model_id', $id)->delete();
$user->assignRole($request->input('roles'));
return redirect()->route('users.index')
->with('success', 'User updated successfully');
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id): RedirectResponse
{
User::find($id)->delete();
return redirect()->route('users.index')
->with('success', 'User deleted successfully');
}
}

8
app/Http/Kernel.php

@ -36,12 +36,14 @@ class Kernel extends HttpKernel @@ -36,12 +36,14 @@ class Kernel extends HttpKernel
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\CheckUserStatus::class,
],
'api' => [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\ThrottleRequests::class . ':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\CheckUserAPiStatus::class,
],
];
@ -64,5 +66,9 @@ class Kernel extends HttpKernel @@ -64,5 +66,9 @@ class Kernel extends HttpKernel
'signed' => \App\Http\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
// 'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
'permission' => \App\Http\Middleware\PermissionMiddleware::class,
'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
];
}

30
app/Http/Middleware/CheckUserApiStatus.php

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
<?php
namespace App\Http\Middleware;
use App\Models\User;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class CheckUserAPiStatus
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if (auth('api')->check()) {
$user_id = auth('api')->user()->id;
$user = User::find($user_id);
if ($user->status == 1) {
// 403 page
return response()->json(['error' => '此帳號已被停用']);
}
}
return $next($request);
}
}

29
app/Http/Middleware/CheckUserStatus.php

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
<?php
namespace App\Http\Middleware;
use App\Models\User;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class CheckUserStatus
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if (auth()->check()) {
$user_id = auth()->user()->id;
$user = User::find($user_id);
if ($user->status == 1) {
// 403 page
return abort(403, '此帳號已被停用');
}
}
return $next($request);
}
}

41
app/Http/Middleware/PermissionMiddleware.php

@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Spatie\Permission\Exceptions\UnauthorizedException;
use Symfony\Component\HttpFoundation\Response;
class PermissionMiddleware
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle($request, Closure $next, $permission, $guard = null)
{
// dd(auth('api')->user()->can("user-list"), auth('api')->user());
$authGuard = app('auth')->guard($guard);
if (auth()->guest()) {
// return to login page with unauthorized message session
// return response()->json(['message' => 'Unauthenticated.'], 401);
return redirect()->route('login')->with('error', '請先登入');
// throw UnauthorizedException::notLoggedIn();
}
$permissions = is_array($permission)
? $permission
: explode('|', $permission);
foreach ($permissions as $permission) {
// dd($authGuard->user(), $permission, $authGuard->user()->can($permission));
if ($authGuard->user()->can($permission)) {
return $next($request);
}
}
return redirect()->route('system.dashboard')->with('error', '您無權限訪問此頁面');
// throw UnauthorizedException::forPermissions($permissions);
}
}

46
app/Listeners/SuccessfulLogin.php

@ -0,0 +1,46 @@ @@ -0,0 +1,46 @@
<?php
namespace App\Listeners;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
class SuccessfulLogin
{
/**
* Create the event listener.
*/
public function __construct()
{
//
}
/**
* Handle the event.
*/
public function handle(object $event): void
{
$user = Auth::user();
$user->api_token = Str::random(80);
$user->save();
try {
//code...
$user->userLog()->create([
'user_id' => $user->id,
'user_name' => $user->name,
'action' => 'login',
'action_detail' => '登入',
'ip' => request()->ip(),
// 'mac' => request()->mac(),
'remark' => '登入成功',
]);
} catch (\Throwable $th) {
Log::error($th);
//throw $th;
}
}
}

26
app/Models/AssetOwnership.php

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class AssetOwnership extends Model
{
use HasFactory;
protected $table = 'asset_ownership';
protected $fillable = [
'name',
];
public $timestamps = false;
// 有關聯到違規停車設備
public function violationparkingequipment()
{
return $this->hasMany(ViolationParkingEquipment::class, 'assetownership_id', 'id');
}
}

75
app/Models/Clientlog.php

@ -0,0 +1,75 @@ @@ -0,0 +1,75 @@
<?php
namespace App\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
class Clientlog extends Model
{
use HasFactory;
protected $table = 'clientlog';
protected $fillable = [
'to_ip',
'to_time',
'this_ip',
'this_time',
'diffsecond',
'name',
'pushed',
];
// append before_time
protected $appends = ['before_time','force_time', 'timeout'];
public function getBeforeTimeAttribute()
{
return substr(Carbon::parse($this->this_time)->subMilliseconds($this->diffsecond)->format('Y-m-d H:i:s.u'), 0, -3);
}
public function getForceTimeAttribute()
{
$forceTime = UserLog::where('action', 'forceTime')->pluck('created_at')->toArray();
// 將資料轉換為 Y-m-d H:i 格式
$forceTime = array_map(function ($item) {
return Carbon::parse($item)->format('Y-m-d H:i');
}, $forceTime);
// 將this_time 轉換為 Y-m-d H:i 格式 並比對是否存在於 forceTime 陣列中
$thisTime = Carbon::parse($this->this_time)->format('Y-m-d H:i');
// 如果存在於陣列中 則回傳 1 代表強制校時 不存在則回傳 0
return in_array($thisTime, $forceTime) ? 1 : 0;
}
public function getTimeoutAttribute()
{
// this_time 與 serverlog 最新一筆,差超過150秒則回傳1 代表超時
$serverLog = Serverlog::orderBy('this_time', 'desc')->first();
if (!$serverLog) {
return 1;
}
return Carbon::parse($this->this_time)->diffInSeconds($serverLog->this_time) > 150 ? 1 : 0;
// return Carbon::parse($this->this_time)->diffInSeconds() > 150 ? 1 : 0;
}
// 修改現有的diffsecond格式
public function getDiffsecondAttribute($value)
{
return floatval($value) * 1000;
}
public static function cachedDistinctName()
{
return Cache::remember('client_log_distinct_name', 3600, function () {
return self::select('name')->distinct()->get();
});
}
public static function cachedDistinctThisIP()
{
return Cache::remember('client_log_distinct_thisip', 3600, function () {
return self::select('this_ip')->distinct()->get();
});
}
}

67
app/Models/Clientlogsecond.php

@ -0,0 +1,67 @@ @@ -0,0 +1,67 @@
<?php
namespace App\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
class Clientlogsecond extends Model
{
use HasFactory;
protected $table = 'clientlogsecond';
protected $fillable = [
'to_ip',
'to_time',
'this_ip',
'this_time',
'diffsecond',
'name',
'pushed',
];
protected $appends = ['before_time','force_time', 'timeout'];
public function getBeforeTimeAttribute()
{
return substr(Carbon::parse($this->this_time)->subMilliseconds($this->diffsecond)->format('Y-m-d H:i:s.u'), 0, -3);
}
public function getForceTimeAttribute()
{
$forceTime = UserLog::where('action', 'forceTime')->pluck('created_at')->toArray();
// 將資料轉換為 Y-m-d H:i 格式
$forceTime = array_map(function ($item) {
return Carbon::parse($item)->format('Y-m-d H:i');
}, $forceTime);
// 將this_time 轉換為 Y-m-d H:i 格式 並比對是否存在於 forceTime 陣列中
$thisTime = Carbon::parse($this->this_time)->format('Y-m-d H:i');
// 如果存在於陣列中 則回傳 1 代表強制校時 不存在則回傳 0
return in_array($thisTime, $forceTime) ? 1 : 0;
}
public function getTimeoutAttribute()
{
// this_time 與 serverlog 最新一筆,差超過150秒則回傳1 代表超時
$serverLog = Serverlog::orderBy('this_time', 'desc')->first();
if (!$serverLog) {
return 1;
}
return Carbon::parse($this->this_time)->diffInSeconds($serverLog->this_time) > 150 ? 1 : 0;
// return Carbon::parse($this->this_time)->diffInSeconds() > 150 ? 1 : 0;
}
// 修改現有的diffsecond格式
public function getDiffsecondAttribute($value)
{
return floatval($value) * 1000;
}
public static function cachedDistinctName()
{
return Cache::remember('client_log2_distinct_name', 3600, function () {
return self::select('name')->distinct()->get();
});
}
}

25
app/Models/CustodyUnit.php

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class CustodyUnit extends Model
{
use HasFactory;
protected $table = 'custody_unit';
protected $fillable = [
'name',
];
public $timestamps = false;
// 有關聯到違規停車設備
public function violationparkingequipment()
{
return $this->hasMany(ViolationParkingEquipment::class, 'custodyunit_id', 'id');
}
}

62
app/Models/EquipmentView.php

@ -0,0 +1,62 @@ @@ -0,0 +1,62 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class EquipmentView extends Model
{
use HasFactory;
protected $table = 'equipment_view';
protected $fillable = [
'serialnumber',
'location',
'precinct',
'station',
];
// 有關聯到
public static function getTypeStation($options = [])
{
// 获取所有的类型
$types = self::select('station')->distinct()->pluck('station')->toArray();
// 构建一个关联数组来存储每个类型对应的代码
$typeCodes = [];
foreach ($types as $type) {
if ($options != []) {
if (in_array($type, $options)) {
$codes = self::where('station', $type)->pluck('serialnumber')->toArray();
$typeCodes[$type] = $codes;
}
} else {
$codes = self::where('station', $type)->pluck('serialnumber')->toArray();
$typeCodes[$type] = $codes;
}
}
return $typeCodes;
}
public static function getTypePrecinct($options = [])
{
// 获取所有的类型
$types = self::select('precinct')->distinct()->pluck('precinct')->toArray();
// 构建一个关联数组来存储每个类型对应的代码
$typeCodes = [];
foreach ($types as $type) {
// $type in $options
if ($options != []) {
if (in_array($type, $options)) {
$codes = self::where('precinct', $type)->pluck('serialnumber')->toArray();
$typeCodes[$type] = $codes;
}
} else {
$codes = self::where('precinct', $type)->pluck('serialnumber')->toArray();
$typeCodes[$type] = $codes;
}
}
return $typeCodes;
}
}

27
app/Models/ExportFiles.php

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ExportFiles extends Model
{
use HasFactory;
protected $table = 'export_files';
protected $fillable = [
'name',
'path',
'type',
'status',
'remark',
'user_id',
];
// protected $casts = [
// 'created_at' => 'datetime:Y-m-d H:i:s',
// ];
}

166
app/Models/Interval.php

@ -0,0 +1,166 @@ @@ -0,0 +1,166 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Interval extends Model
{
use HasFactory;
protected $table = 'interval_data';
protected $fillable = [
'is_outlaw',
'start_id',
'end_id',
'start_time',
'end_time',
'start_location',
'end_location',
'start_serialnumber',
'end_serialnumber',
'start_picture',
'end_picture',
'start_video',
'end_video',
'carnumber',
'cartype',
'start_path',
'end_path',
'distance',
'limit_speed',
'alert_speed',
'outlaw_speed',
'limit_time',
'cert',
'diff',
'speed',
'location',
'mergedone',
'intervalnumber',
'faildata',
'failreason',
'violationcode',
'processcheck',
'postcheck',
'jsoncheck',
'unreportreason',
'unreportpicture',
'unreportmergedone',
'merge_picture'
];
protected $hidden = [
'created_at',
'updated_at',
'deleted_at'
];
protected $casts = [
'is_outlaw' => 'boolean',
'distance' => 'float',
'limit_speed' => 'float',
'alert_speed' => 'float',
'outlaw_speed' => 'float',
'limit_time' => 'float',
'diff' => 'float',
'mergedone' => 'boolean',
'faildata' => 'boolean',
'processcheck' => 'integer',
'postcheck' => 'integer',
'unreportmergedone' => 'integer'
];
// append fields
protected $appends = [
'law_type',
];
public function violationlaw()
{
return $this->hasOne(ViolationLaw::class, 'violationcode', 'violationcode');
}
public function getLawTypeAttribute()
{
// 取得陣列
$violationtype = "區間超速";
// $violationtype = [];
if ($violationtype == "未依標誌標線號誌" || $violationtype == "未依標誌標線行駛" || $violationtype == "機車未依規定兩段式左轉") {
$violationtype = ["未依標誌標線號誌行駛"];
} else if ($violationtype == "闖紅燈") {
$violationtype = ["闖紅燈", "紅燈右轉", "紅燈越線"];
} else if ($violationtype == "未禮讓行人") {
$violationtype = ["未禮讓行人"];
} else if ($violationtype == "違規停車") {
$violationtype = ["違規停車"];
} else if ($violationtype == "未保持路口淨空") {
$violationtype = ["未保持路口淨空"];
} else if ($violationtype == "超速") {
$violationtype = ["超速"];
} else {
$violationtype = [$violationtype];
}
$except_arr =[
'3310146',
'3310148',
'4340069'
];
$types = ViolationLaw::whereIn('type', $violationtype)->whereNotIn('violationcode',$except_arr)->get();
$lawData = [];
foreach ($types as $type) {
$lawData["$type->violationcode"] = "[$type->violationcode] $type->display_name";
}
$customLaws = ViolationLaw::whereIn('violationcode', ['72000041', '72000051', '72000061'])->get();
foreach ($customLaws as $type) {
$lawData["$type->violationcode"] = "[$type->violationcode] $type->display_name";
}
return $lawData;
}
// public function getMergePictureAttribute($value)
// {
// // replace * with /
// return 'ParsingFiles/Interval/' . str_replace('*', '/', $value);
// }
// public function getStartPictureAttribute($value)
// {
// return $value ? asset('storage/' . $value) : null;
// }
// public function getEndPictureAttribute($value)
// {
// return $value ? asset('storage/' . $value) : null;
// }
// public function getStartVideoAttribute($value)
// {
// return $value ? asset('storage/' . $value) : null;
// }
// public function getEndVideoAttribute($value)
// {
// return $value ? asset('storage/' . $value) : null;
// }
// public function getUnreportPictureAttribute($value)
// {
// return $value ? asset('storage/' . $value) : null;
// }
// public function getMergePictureAttribute($value)
// {
// return $value ? asset('storage/' . $value) : null;
// }
// public function getCreatedAtAttribute($value)
// {
// return $value ? date('Y-m-d H:i:s', strtotime($value)) : null;
// }
// public function getUpdatedAtAttribute($value)
// {
// return $value ? date('Y-m-d H:i:s', strtotime($value)) : null;
// }
// public function getDeletedAtAttribute($value)
// {
// return $value ? date('Y-m-d H:i:s', strtotime($value)) : null;
// }
}

21
app/Models/IntervalEntry.php

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class IntervalEntry extends Model
{
use HasFactory;
protected $table = 'interval_entry';
protected $fillable = [
'interval_id',
'no',
'photo',
'response',
'status',
];
}

99
app/Models/IntervalEquipment.php

@ -0,0 +1,99 @@ @@ -0,0 +1,99 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class IntervalEquipment extends Model
{
use HasFactory;
protected $table = 'interval_equipment';
protected $dates = ['deleted_at'];
protected $fillable = [
'serialnumber',
'brand',
'model',
'videonumber',
'custodyunit_id',
'assetownership_id',
'buydate',
'activatedate',
'locationid',
'location',
'comment',
'map',
'precinct',
'station',
];
// append fields
protected $appends = [
'custodyunit_name',
'assetownership_name',
];
// custome fields
public function getCustodyunitNameAttribute()
{
$name = CustodyUnit::find($this->custodyunit_id)->name ?? '無';
return $name;
}
public function getAssetownershipNameAttribute()
{
$name = AssetOwnership::find($this->assetownership_id)->name ?? '無';
return $name;
}
// 有關聯到設備保管單位
public function custodyunit()
{
return $this->belongsTo(CustodyUnit::class, 'custodyunit_id', 'id');
}
// 有關聯到設備財產權屬
public function assetownership()
{
return $this->belongsTo(AssetOwnership::class, 'assetownership_id', 'id');
}
// 有關聯到
public static function getTypeStation($options = [])
{
$types = self::select('station')->distinct()->pluck('station')->toArray();
$typeCodes = [];
foreach ($types as $type) {
if ($options != []){
if (in_array($type, $options)) {
$codes = self::where('station', $type)->pluck('serialnumber')->toArray();
$typeCodes[$type] = $codes;
}
}else{
$codes = self::where('station', $type)->pluck('serialnumber')->toArray();
$typeCodes[$type] = $codes;
}
}
return $typeCodes;
}
public static function getTypePrecinct($options = [])
{
$types = self::select('precinct')->distinct()->pluck('precinct')->toArray();
$typeCodes = [];
foreach ($types as $type) {
// $type in $options
if ($options != []){
if (in_array($type, $options)) {
$codes = self::where('precinct', $type)->pluck('serialnumber')->toArray();
$typeCodes[$type] = $codes;
}
}else{
$codes = self::where('precinct', $type)->pluck('serialnumber')->toArray();
$typeCodes[$type] = $codes;
}
}
return $typeCodes;
}
}

58
app/Models/Intervaldis.php

@ -0,0 +1,58 @@ @@ -0,0 +1,58 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Intervaldis extends Model
{
use HasFactory;
protected $table = 'intervaldis';
protected $fillable = [
'start_serialnumber',
'end_serialnumber',
'distance',
'limit_speed',
'outlaw_speed',
'limit_time',
'speed_alert',
'count_alert',
'location',
'location_id',
'certificatenumber',
'crosspathban',
'carkindban',
'start_cert',
'end_cert',
];
protected $hidden = [
'created_at',
'updated_at',
'deleted_at'
];
protected $appends = ['start_num', 'end_num'];
public function getStartNumAttribute()
{
return IntervalEquipment::find($this->start_serialnumber)->serialnumber;
}
public function getEndNumAttribute()
{
return IntervalEquipment::find($this->end_serialnumber)->serialnumber;
}
public function start()
{
return $this->belongsTo(IntervalEquipment::class, 'start_serialnumber', 'serialnumber');
}
public function end()
{
return $this->belongsTo(IntervalEquipment::class, 'end_serialnumber', 'serialnumber');
}
}

70
app/Models/Multisys.php

@ -0,0 +1,70 @@ @@ -0,0 +1,70 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Multisys extends Model
{
use HasFactory;
protected $table = 'multisys';
protected $fillable = [
'picture',
'picture2',
'datatime',
'cartype',
'carnumber',
'serialnumber',
'location',
'limitspeed',
'speed',
'sid',
'violationtype',
'violationcode',
'processcheck',
'postcheck',
'jsoncheck',
'unreportreason',
'unreportpicture',
'unreportmergedone',
];
// append fields
protected $appends = [
'law_type',
];
public function violationlaw()
{
return $this->hasOne(ViolationLaw::class, 'violationcode', 'violationcode');
}
public function getLawTypeAttribute()
{
// 取得陣列
$violationtype = $this->violationtype;
// $violationtype = [];
if ($violationtype == "未依標誌標線號誌" || $violationtype == "未依標誌標線行駛" || $violationtype == "機車未依規定兩段式左轉") {
$violationtype = ["未依標誌標線號誌行駛"];
} else if ($violationtype == "闖紅燈") {
$violationtype = ["闖紅燈", "紅燈右轉", "紅燈越線"];
} else if ($violationtype == "未禮讓行人") {
$violationtype = ["未禮讓行人"];
} else if ($violationtype == "違規停車") {
$violationtype = ["違規停車"];
} else if ($violationtype == "未保持路口淨空") {
$violationtype = ["未保持路口淨空"];
} else if ($violationtype == "超速") {
$violationtype = ["超速"];
} else {
$violationtype = [$violationtype];
}
$types = ViolationLaw::whereIn('type', $violationtype)->get();
$lawData = [];
foreach ($types as $type) {
$lawData["$type->violationcode"] = "[$type->violationcode] $type->display_name";
}
return $lawData;
}
}

21
app/Models/MultisysEntry.php

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class MultisysEntry extends Model
{
use HasFactory;
protected $table = 'multisys_entry';
protected $fillable = [
'multisys_id',
'no',
'photo',
'response',
'status',
];
}

122
app/Models/MultisysEquipment.php

@ -0,0 +1,122 @@ @@ -0,0 +1,122 @@
<?php
namespace App\Models;
use COM;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class MultisysEquipment extends Model
{
use HasFactory, SoftDeletes;
protected $table = 'multisys_equipment';
protected $dates = ['deleted_at'];
protected $fillable = [
'serialnumber',
'brand',
'model',
'custodyunit_id',
'assetownership_id',
'buydate',
'activatedate',
'locationid',
'location',
'comment',
'map',
'precinct',
'station',
];
// append fields
protected $appends = [
'custodyunit_name',
'assetownership_name',
'short_name',
];
// custome fields
public function getShortNameAttribute()
{
$match = [
'桃園區國際路二段與大興西路三段路口' => '國際路二段與大興西三段',
'桃園區復興路與民生路口' => '復興路與民生路',
'桃園區三民路二段與春日路口' => '三民二段與春日路',
'桃園區中山路與上海路口' => '中山路與上海路',
'桃園區三民路二段與民生路口' => '三民路二段與民生路',
'桃園區春日路與民光東路口' => '春日路與民光東路',
'桃園區三民路三段與永安路口' => '三民路三段與永安路',
'蘆竹區中正路與南山路一段路口' => '蘆竹中正路與南山路一段',
'八德區中華路與永豐路口' => '八德中華路與永豐路',
'八德區建德路與豐德路口' => '八德建德路與豐德路',
];
$name = $match[$this->location] ?? $this->location;
return $name;
}
public function getCustodyunitNameAttribute()
{
$name = CustodyUnit::find($this->custodyunit_id)->name ?? '無';
return $name;
}
public function getAssetownershipNameAttribute()
{
$name = AssetOwnership::find($this->assetownership_id)->name ?? '無';
return $name;
}
// 有關聯到設備保管單位
public function custodyunit()
{
return $this->belongsTo(CustodyUnit::class, 'custodyunit_id', 'id');
}
// 有關聯到設備財產權屬
public function assetownership()
{
return $this->belongsTo(AssetOwnership::class, 'assetownership_id', 'id');
}
// 有關聯到
public static function getTypeStation($options = [])
{
// 获取所有的类型
$types = self::select('station')->distinct()->pluck('station')->toArray();
// 构建一个关联数组来存储每个类型对应的代码
$typeCodes = [];
foreach ($types as $type) {
if ($options != []){
if (in_array($type, $options)) {
$codes = self::where('station', $type)->pluck('serialnumber')->toArray();
$typeCodes[$type] = $codes;
}
}else{
$codes = self::where('station', $type)->pluck('serialnumber')->toArray();
$typeCodes[$type] = $codes;
}
}
return $typeCodes;
}
public static function getTypePrecinct($options = [])
{
// 获取所有的类型
$types = self::select('precinct')->distinct()->pluck('precinct')->toArray();
// 构建一个关联数组来存储每个类型对应的代码
$typeCodes = [];
foreach ($types as $type) {
// $type in $options
if ($options != []){
if (in_array($type, $options)) {
$codes = self::where('precinct', $type)->pluck('serialnumber')->toArray();
$typeCodes[$type] = $codes;
}
}else{
$codes = self::where('precinct', $type)->pluck('serialnumber')->toArray();
$typeCodes[$type] = $codes;
}
}
return $typeCodes;
}
}

69
app/Models/OverSpeedRed.php

@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class OverSpeedRed extends Model
{
use HasFactory;
protected $table = 'overspeedred';
protected $primaryKey = 'id';
protected $fillable = [
'picture',
'picture2',
'serialnumber',
'location',
'limitspeed',
'violationtype',
'violationcode',
'cartype',
'sid',
'datatime',
'speed',
'carnumber',
'processcheck',
'jsoncheck',
'unreportreason',
'unreportpicture',
'unreportmergedone'
];
protected $appends = [
'law_type',
];
public function violationlaw()
{
return $this->hasOne(ViolationLaw::class, 'violationcode', 'violationcode');
}
public function getLawTypeAttribute()
{
// 取得陣列
$violationtype = $this->violationtype;
// $violationtype = [];
if ($violationtype == "未依標誌標線號誌" || $violationtype == "未依標誌標線行駛") {
$violationtype = ["未依標誌標線號誌行駛"];
} else if ($violationtype == "闖紅燈") {
$violationtype = ["闖紅燈", "紅燈右轉", "紅燈越線"];
} else if ($violationtype == "未禮讓行人") {
$violationtype = ["未禮讓行人"];
} else if ($violationtype == "違規停車") {
$violationtype = ["違規停車"];
} else if ($violationtype == "未保持路口淨空") {
$violationtype = ["未保持路口淨空"];
} else if ($violationtype == "超速") {
$violationtype = ["超速"];
} else {
$violationtype = [$violationtype];
}
$types = ViolationLaw::whereIn('type', $violationtype)->get();
$lawData = [];
foreach ($types as $type) {
$lawData["$type->violationcode"] = "[$type->violationcode] $type->display_name";
}
return $lawData;
}
}

19
app/Models/OverSpeedRedEntry.php

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class OverSpeedRedEntry extends Model
{
use HasFactory;
protected $table = 'overspeedred_entry';
protected $fillable = [
'overspeedred_id',
'no',
'photo',
'response',
'status',
];
}

19
app/Models/OverSpeedRedEntry2.php

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class OverSpeedRedEntry2 extends Model
{
use HasFactory;
protected $table = 'overspeedred_entry2';
protected $fillable = [
'overspeedred_id',
'no',
'photo',
'response',
'status',
];
}

131
app/Models/OverSpeedRedEquipment.php

@ -0,0 +1,131 @@ @@ -0,0 +1,131 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class OverSpeedRedEquipment extends Model
{
use HasFactory;
use HasFactory, SoftDeletes;
protected $table = 'overspeedred_equipment';
protected $dates = ['deleted_at'];
protected $fillable = [
'serialnumber',
'brand',
'model',
'custodyunit_id',
'assetownership_id',
'buydate',
'activatedate',
'locationid',
'location',
'comment',
'precinct',
'station',
];
// append fields
protected $appends = [
'custodyunit_name',
'assetownership_name',
'short_name',
];
// custome fields
public function getShortNameAttribute()
{
$match = [
'桃園區國際路二段與大興西路三段路口' => '國際路二段與大興西三段',
'桃園區復興路與民生路口' => '復興路與民生路',
'桃園區三民路二段與春日路口' => '三民二段與春日路',
'桃園區中山路與上海路口' => '中山路與上海路',
'桃園區三民路二段與民生路口' => '三民路二段與民生路',
'桃園區春日路與民光東路口' => '春日路與民光東路',
'桃園區三民路三段與永安路口' => '三民路三段與永安路',
'蘆竹區中正路與南山路一段路口' => '蘆竹中正路與南山路一段',
'八德區中華路與永豐路口' => '八德中華路與永豐路',
'八德區建德路與豐德路口' => '八德建德路與豐德路',
'大溪區台七乙線9K+100' => '台七乙線9K+100處',
'桃園區大興西路三段與正光路口(往交流道)' =>'大興西與正光路',
'龍潭區台3線50.8K處(往龍潭)' =>'龍潭台3線50.8K',
'龜山區萬壽路二段472號前' =>'龜山萬壽路472號前',
'中壢區民權路五段65號前' =>'民權路65號前',
'楊梅區台1線50K+536處' =>'楊梅台1線50K+536',
'大溪區仁和路2段517巷' =>'大溪仁和路2段517巷',
'觀音區濱海路廣興段845號前' =>'觀音濱海路與廣興段',
'龍潭區中正路三林段330號' =>'龍潭中正路三林段',
'桃園區中山路輿上海路口' =>'中山路與上海路口',
];
$name = $match[$this->location] ?? $this->location;
return $name;
}
public function getCustodyunitNameAttribute()
{
$name = CustodyUnit::find($this->custodyunit_id)->name ?? '無';
return $name;
}
public function getAssetownershipNameAttribute()
{
$name = AssetOwnership::find($this->assetownership_id)->name ?? '無';
return $name;
}
// 有關聯到設備保管單位
public function custodyunit()
{
return $this->belongsTo(CustodyUnit::class, 'custodyunit_id', 'id');
}
// 有關聯到設備財產權屬
public function assetownership()
{
return $this->belongsTo(AssetOwnership::class, 'assetownership_id', 'id');
}
// 有關聯到
public static function getTypeStation($options = [])
{
// 获取所有的类型
$types = self::select('station')->distinct()->pluck('station')->toArray();
// 构建一个关联数组来存储每个类型对应的代码
$typeCodes = [];
foreach ($types as $type) {
if ($options != []){
if (in_array($type, $options)) {
$codes = self::where('station', $type)->pluck('serialnumber')->toArray();
$typeCodes[$type] = $codes;
}
}else{
$codes = self::where('station', $type)->pluck('serialnumber')->toArray();
$typeCodes[$type] = $codes;
}
}
return $typeCodes;
}
public static function getTypePrecinct($options = [])
{
// 获取所有的类型
$types = self::select('precinct')->distinct()->pluck('precinct')->toArray();
// 构建一个关联数组来存储每个类型对应的代码
$typeCodes = [];
foreach ($types as $type) {
// $type in $options
if ($options != []){
if (in_array($type, $options)) {
$codes = self::where('precinct', $type)->pluck('serialnumber')->toArray();
$typeCodes[$type] = $codes;
}
}else{
$codes = self::where('precinct', $type)->pluck('serialnumber')->toArray();
$typeCodes[$type] = $codes;
}
}
return $typeCodes;
}
}

15
app/Models/Permission.php

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Permission extends \Spatie\Permission\Models\Permission
{
protected $guard = [];
protected $guard_name = '*';
// guard_name = '*' 代表所有 guard 都可以使用這個 model
// 這樣就不用在每次使用這個 model 時都要加上 guard_name
}

13
app/Models/PingIp.php

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class PingIp extends Model
{
//
// protected $connection = 'mysql_ping';
protected $table = "ping_ip";
protected $fillable = ["ip", "ping_time", "time", "symbol", "serialnumber", "ttl", "status"];
}

16
app/Models/Role.php

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Role extends \Spatie\Permission\Models\Role
{
protected $guard = [];
protected $guard_name = '*';
// guard_name = '*' 代表所有 guard 都可以使用這個 model
// 這樣就不用在每次使用這個 model 時都要加上 guard_name
// 例如: Role::where('name', 'Admin')->first();
}

39
app/Models/Serverlog.php

@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
<?php
namespace App\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Serverlog extends Model
{
use HasFactory;
protected $table = 'serverlog';
protected $fillable = [
'to_ip',
'to_time',
'this_ip',
'this_time',
'diffsecond',
'name',
];
protected $appends = ['before_time', 'timeout'];
public function getBeforeTimeAttribute()
{
return substr(Carbon::parse($this->this_time)->subMilliseconds($this->diffsecond)->format('Y-m-d H:i:s.u'), 0, -3);
}
public function getTimeoutAttribute()
{
// this_time 與現在差超過150秒則回傳1 代表超時
return Carbon::parse($this->this_time)->diffInSeconds() > 150 ? 1 : 0;
}
// 修改現有的diffsecond格式
public function getDiffsecondAttribute($value)
{
return floatval($value) * 1000;
}
}

20
app/Models/StatisticView.php

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class StatisticView extends Model
{
use HasFactory;
protected $table = 'statistic_view';
protected $fillable = [
'datatime',
'serialnumber',
'location',
'violationcode',
'processcheck',
'jsoncheck',
];
}

53
app/Models/User.php

@ -4,14 +4,25 @@ @@ -4,14 +4,25 @@
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
use HasApiTokens, HasFactory, Notifiable, HasRoles;
// soft delete
use SoftDeletes;
/**
* The attributes that should be mutated to dates.
*
* @var array<int, string>
*/
protected $dates = ['deleted_at'];
protected $guard = [];
protected $guard_name = '*';
/**
* The attributes that are mass assignable.
*
@ -21,6 +32,16 @@ class User extends Authenticatable @@ -21,6 +32,16 @@ class User extends Authenticatable
'name',
'email',
'password',
'api_token',
'account',
'status',
'class',
'leader',
'exp_op',
'unit',
'device',
'deleted_at',
];
/**
@ -31,6 +52,7 @@ class User extends Authenticatable @@ -31,6 +52,7 @@ class User extends Authenticatable
protected $hidden = [
'password',
'remember_token',
'api_token'
];
/**
@ -42,4 +64,31 @@ class User extends Authenticatable @@ -42,4 +64,31 @@ class User extends Authenticatable
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
// append attribute
protected $appends = ['role_name','station'];
public function userLog()
{
return $this->hasMany(UserLog::class);
}
// get role name attribute
public function getRoleNameAttribute()
{
return $this->roles->pluck('display_name')->first();
}
// get station attribute
public function getStationAttribute()
{
return '交通警察大隊';
}
// get status attribute
// public function getStatusAttribute($value)
// {
// return $value == 0 ? '啟用' : '停用';
// }
}

48
app/Models/UserLog.php

@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
class UserLog extends Model
{
use HasFactory;
protected $table = 'user_log';
protected $fillable = [
'user_id',
'user_name',
'action',
'action_detail',
'ip',
'mac',
'remark',
'carnumber',
'location',
];
// protected $casts = [
// 'created_at' => 'datetime:Y-m-d H:i:s',
// ];
public static function cachedDistinctActionDetail()
{
return Cache::remember('user_log_distinct_action_detail', 3600, function () {
return self::select('action_detail')->distinct()->get();
});
}
//cachedDistinctUser
public static function cachedDistinctUser()
{
return Cache::remember('user_log_distinct_user', 3600, function () {
return self::select('user_name')->distinct()->get();
});
}
public function user()
{
return $this->belongsTo(User::class);
}
}

33
app/Models/ViolationLaw.php

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ViolationLaw extends Model
{
use HasFactory;
protected $table = 'violation_law';
protected $fillable = [
'code',
'display_name',
'type',
'sort',
];
public static function getTypeCodes()
{
$types = self::select('type')->distinct()->pluck('type')->toArray();
$typeCodes = [];
foreach ($types as $type) {
if ($type == 99 || $type== 3 || $type == 1) {
continue;
}
$codes = self::where('type', $type)->pluck('violationcode')->toArray();
$typeCodes[$type] = $codes;
}
return $typeCodes;
}
}

66
app/Models/ViolationParking.php

@ -0,0 +1,66 @@ @@ -0,0 +1,66 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ViolationParking extends Model
{
use HasFactory;
protected $table = 'violationparking';
protected $fillable = [
'picture',
'picture2',
'datatime',
'cartype',
'carnumber',
'serialnumber',
'location',
'violationtype',
'violationcode',
'processcheck',
'postcheck',
'jsoncheck',
'unreportreason',
'unreportpicture',
'unreportmergedone',
];
protected $appends = [
'law_type',
];
public function violationlaw()
{
return $this->hasOne(ViolationLaw::class, 'violationcode', 'violationcode');
}
public function getLawTypeAttribute()
{
// 取得陣列
$violationtype = $this->violationtype;
// $violationtype = [];
if ($violationtype == "未依標誌標線號誌" || $violationtype == "未依標誌標線行駛") {
$violationtype = ["未依標誌標線號誌行駛"];
} else if ($violationtype == "闖紅燈") {
$violationtype = ["闖紅燈", "紅燈右轉", "紅燈越線"];
} else if ($violationtype == "未禮讓行人") {
$violationtype = ["未禮讓行人"];
} else if ($violationtype == "違規停車") {
$violationtype = ["違規停車"];
} else if ($violationtype == "未保持路口淨空") {
$violationtype = ["未保持路口淨空"];
} else if ($violationtype == "超速") {
$violationtype = ["超速"];
} else {
$violationtype = [$violationtype];
}
$types = ViolationLaw::whereIn('type', $violationtype)->get();
$lawData = [];
foreach ($types as $type) {
$lawData["$type->violationcode"] = "[$type->violationcode] $type->display_name";
}
return $lawData;
}
}

102
app/Models/ViolationParkingEquipment.php

@ -0,0 +1,102 @@ @@ -0,0 +1,102 @@
<?php
namespace App\Models;
use COM;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class ViolationParkingEquipment extends Model
{
use HasFactory, SoftDeletes;
protected $table = 'violationparking_equipment';
protected $dates = ['deleted_at'];
protected $fillable = [
'serialnumber',
'brand',
'model',
'custodyunit_id',
'assetownership_id',
'buydate',
'activatedate',
'locationid',
'location',
'comment',
'precinct',
'station',
];
// append fields
protected $appends = [
'custodyunit_name',
'assetownership_name',
];
// custome fields
public function getCustodyunitNameAttribute()
{
$name = CustodyUnit::find($this->custodyunit_id)->name ?? '無';
return $name;
}
public function getAssetownershipNameAttribute()
{
$name = AssetOwnership::find($this->assetownership_id)->name ?? '無';
return $name;
}
// 有關聯到設備保管單位
public function custodyunit()
{
return $this->belongsTo(CustodyUnit::class, 'custodyunit_id', 'id');
}
// 有關聯到設備財產權屬
public function assetownership()
{
return $this->belongsTo(AssetOwnership::class, 'assetownership_id', 'id');
}
// 有關聯到
public static function getTypeStation($options = [])
{
// 获取所有的类型
$types = self::select('station')->distinct()->pluck('station')->toArray();
// 构建一个关联数组来存储每个类型对应的代码
$typeCodes = [];
foreach ($types as $type) {
if ($options != []){
if (in_array($type, $options)) {
$codes = self::where('station', $type)->pluck('serialnumber')->toArray();
$typeCodes[$type] = $codes;
}
}else{
$codes = self::where('station', $type)->pluck('serialnumber')->toArray();
$typeCodes[$type] = $codes;
}
}
return $typeCodes;
}
public static function getTypePrecinct($options = [])
{
// 获取所有的类型
$types = self::select('precinct')->distinct()->pluck('precinct')->toArray();
// 构建一个关联数组来存储每个类型对应的代码
$typeCodes = [];
foreach ($types as $type) {
// $type in $options
if ($options != []){
if (in_array($type, $options)) {
$codes = self::where('precinct', $type)->pluck('serialnumber')->toArray();
$typeCodes[$type] = $codes;
}
}else{
$codes = self::where('precinct', $type)->pluck('serialnumber')->toArray();
$typeCodes[$type] = $codes;
}
}
return $typeCodes;
}
}

3
app/Providers/EventServiceProvider.php

@ -18,6 +18,9 @@ class EventServiceProvider extends ServiceProvider @@ -18,6 +18,9 @@ class EventServiceProvider extends ServiceProvider
Registered::class => [
SendEmailVerificationNotification::class,
],
'Illuminate\Auth\Events\Login' => [
'App\Listeners\SuccessfulLogin',
],
];
/**

60
app/Providers/RouteServiceProvider.php

@ -17,7 +17,7 @@ class RouteServiceProvider extends ServiceProvider @@ -17,7 +17,7 @@ class RouteServiceProvider extends ServiceProvider
*
* @var string
*/
public const HOME = '/home';
public const HOME = '/';
/**
* Define your route model bindings, pattern filters, and other route configuration.
@ -29,12 +29,70 @@ public function boot(): void @@ -29,12 +29,70 @@ public function boot(): void
});
$this->routes(function () {
#region default
Route::middleware('api')
->prefix('api')
->group(base_path('routes/api.php'));
Route::middleware('web')
->group(base_path('routes/web.php'));
#endregion
#region violationparking
// if .env SYSTEM_VIOLATIONPARKING is set to true
// then load the violationparking routes
if (config('app.vpk', false)) {
Route::middleware('web')
->group(base_path('routes/system/violationparking/web.php'));
Route::middleware('api')
->prefix('api')
->group(base_path('routes/system/violationparking/api.php'));
}
#endregion
#region multisys
if (config('app.ms', false)) {
Route::middleware('web')
->group(base_path('routes/system/multisys/web.php'));
Route::middleware('api')
->prefix('api')
->group(base_path('routes/system/multisys/api.php'));
}
#endregion
#region overspeed
if (config('app.osr', false)) {
Route::middleware('web')
->group(base_path('routes/system/overspeed/web.php'));
Route::middleware('api')
->prefix('api')
->group(base_path('routes/system/overspeed/api.php'));
#endregion
#region overspeed
Route::middleware('web')
->group(base_path('routes/system/red/web.php'));
Route::middleware('api')
->prefix('api')
->group(base_path('routes/system/red/api.php'));
}
#endregion
#region interval
if (config('app.itl', false)) {
Route::middleware('web')
->group(base_path('routes/system/interval/web.php'));
Route::middleware('api')
->prefix('api')
->group(base_path('routes/system/interval/api.php'));
}
#endregion
});
}
}

69
app/Repositories/ViolationParkingRepository.php

@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
<?php
namespace App\Repositories;
use App\Models\ViolationParking;
use Illuminate\Support\Facades\DB;
class ViolationParkingRepository
{
public function buildQuery(array $filters)
{
$query = ViolationParking::query();
if (!empty($filters['device'])) {
$query->whereIn('serialnumber', $filters['device']);
}
if (!empty($filters['searchByFromdate'])) {
$query->where('datatime', '>=', $filters['searchByFromdate'] . ' 00:00:00');
}
if (!empty($filters['searchByTodate'])) {
$query->where('datatime', '<=', $filters['searchByTodate'] . ' 23:59:59');
}
if (!empty($filters['location'])) {
$query->where('serialnumber', $filters['location']);
}
if (!empty($filters['carnumber'])) {
$query->where('carnumber', 'like', '%' . $filters['carnumber'] . '%');
}
if (!empty($filters['unreportreason'])) {
$query->where('unreportreason', 'like', '%' . $filters['unreportreason'] . '%');
}
if (!empty($filters['processcheckStatus'])) {
$status = $filters['processcheckStatus'];
if ($status == 99) {
$query->whereIn('processcheck', [0, 1, 2]);
} else {
$query->where('processcheck', $status);
}
}
// 排除違規臨時停車
$query->where('violationtype', '!=', '違規臨時停車');
if (!empty($filters['statisticstype'])) {
switch ($filters['statisticstype']) {
case 2:
$query->groupBy('serialnumber', 'cartype')
->select('*', DB::raw('count(*) as count'));
break;
case 3:
$query->groupBy(DB::raw("CAST(DATE_FORMAT(datatime, '%H') AS SIGNED)"), 'serialnumber', 'violationtype')
->select('*', DB::raw("COUNT(*) as count, CONCAT(CAST(DATE_FORMAT(datatime,'%H') as SIGNED),'-',CAST(DATE_FORMAT(DATE_ADD(datatime,INTERVAL +1 HOUR),'%H')) as SIGNED) AS period"))
->orderBy(DB::raw("CAST(DATE_FORMAT(datatime, '%H') AS SIGNED)"), 'ASC');
break;
default:
$query->select('*');
break;
}
}
return $query;
}
}

76
app/Services/ViolationParkingDataService.php

@ -0,0 +1,76 @@ @@ -0,0 +1,76 @@
<?php
namespace App\Services;
use App\Repositories\ViolationParkingRepository;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
class ViolationParkingDataService
{
protected $repository;
public function __construct(ViolationParkingRepository $repository)
{
$this->repository = $repository;
}
public function getDataTable(array $filters): array
{
$query = $this->repository->buildQuery($filters);
$draw = intval($filters['draw'] ?? 0);
$start = $filters['start'] ?? 0;
$length = $filters['length'] ?? 10;
$totalRecords = $query->count();
if (!isset($filters['export'])) {
$query->skip($start)->take($length);
}
$records = $query->get();
$data_arr = [];
foreach ($records as $record) {
$cartypeTitle = [
'1' => '汽車',
'2' => '機車',
'3' => '重型機車',
'4' => '輕型機車',
];
$processCheckTitle = [
'0' => '未審',
'1' => '已審查',
'2' => '不舉發',
];
$data_arr[] = [
'id' => $record->id,
'datatime' => $record->datatime,
'serialnumber' => $record->serialnumber,
'location' => $record->location,
'carnumber' => $record->carnumber,
'picture' => $record->picture,
'picture2' => $record->picture2,
'video' => str_replace('.jpg', '.mp4', $record->picture2),
'violationtype' => $record->violationtype,
'cartype' => $record->cartype,
'carkind' => $cartypeTitle[$record->cartype] ?? '',
'unreportreason' => $record->unreportreason ?? '',
'unreportpicture' => $record->unreportpicture ?? $record->picture,
'postcheck' => $record->postcheck ?? 0,
'processcheck' => $processCheckTitle[$record->processcheck] ?? '',
'count' => $record->count ?? 0,
'period' => $record->period ?? '',
];
}
return [
'draw' => $draw,
'iTotalRecords' => $totalRecords,
'iTotalDisplayRecords' => $totalRecords,
'aaData' => $data_arr,
];
}
}

108
app/Services/ViolationParkingService.php

@ -0,0 +1,108 @@ @@ -0,0 +1,108 @@
<?php
namespace App\Services;
use App\Models\ViolationParking;
use Illuminate\Support\Facades\DB;
use App\Class\LogWriter;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
class ViolationParkingService
{
public function review(ViolationParking $vpk, array $data, $user): array
{
DB::beginTransaction();
try {
$logData = [];
switch ($data['processcheck']) {
case 0:
// 編輯案件
$vpk->carnumber = $data['carnumber'] ?? $vpk->carnumber;
$vpk->violationtype = $data['violationtype'] ?? $vpk->violationtype;
$vpk->cartype = $data['cartype'] ?? $vpk->cartype;
$vpk->violationcode = $data['viocode'] ?? $vpk->violationcode;
$logData = [
'action' => 'edit',
'action_detail' => '編輯案件',
'ip' => request()->ip(),
'remark' => "編輯違停案件: {$vpk->carnumber} ,案件日期: {$vpk->datatime}",
];
break;
case 1:
// 舉發
$carrepeat = ViolationParking::where('carnumber', $data['carnumber'] ?? $vpk->carnumber)
->where('id', '<>', $vpk->id)
->where('processcheck', 1)
->whereBetween('datatime', [
Carbon::parse($vpk->datatime)->subDay()->toDateTimeString(),
Carbon::parse($vpk->datatime)->addDay()->toDateTimeString()
])->get();
if ($carrepeat->count() > 0) {
$vpk->processcheck = 2;
$vpk->unreportreason = '該車牌24小時內已被舉發過!!';
} else {
$vpk->processcheck = 1;
}
$vpk->carnumber = $data['carnumber'] ?? $vpk->carnumber;
$vpk->violationtype = $data['violationtype'] ?? $vpk->violationtype;
$vpk->cartype = $data['cartype'] ?? $vpk->cartype;
$vpk->violationcode = $data['viocode'] ?? $vpk->violationcode;
$logData = [
'action' => 'review',
'action_detail' => $vpk->processcheck === 1 ? '舉發案件' : '不舉發案件',
'ip' => request()->ip(),
'remark' => "舉發違停案件: {$vpk->carnumber} ,案件日期: {$vpk->datatime}",
];
break;
case 2:
// 不舉發
$vpk->carnumber = $data['carnumber'] ?? $vpk->carnumber;
$vpk->violationtype = $data['violationtype'] ?? $vpk->violationtype;
$vpk->cartype = $data['cartype'] ?? $vpk->cartype;
$vpk->unreportreason = $data['unreportreason'] ?? $vpk->unreportreason;
$vpk->processcheck = 2;
$logData = [
'action' => 'review',
'action_detail' => '不舉發案件',
'ip' => request()->ip(),
'remark' => "不舉發違停案件: {$vpk->carnumber} ,案件日期: {$vpk->datatime}",
];
break;
case 3:
// 還原
$vpk->violationcode = null;
$vpk->unreportreason = '';
$vpk->processcheck = 0;
$logData = [
'action' => 'reduction',
'action_detail' => '還原案件',
'ip' => request()->ip(),
'remark' => "還原違停案件: {$vpk->carnumber} ,案件日期: {$vpk->datatime}",
];
break;
}
$vpk->jsoncheck = $user->account;
$vpk->save();
LogWriter::writeLog($logData, 'api');
DB::commit();
return ['success' => $logData['remark']];
} catch (\Exception $e) {
DB::rollBack();
return ['error' => '系統錯誤'];
}
}
}

11
composer.json

@ -7,9 +7,16 @@ @@ -7,9 +7,16 @@
"require": {
"php": "^8.1",
"guzzlehttp/guzzle": "^7.2",
"intervention/image": "^3.5",
"intervention/image-laravel": "^1.2",
"laravel/framework": "^10.10",
"laravel/sanctum": "^3.3",
"laravel/tinker": "^2.8"
"laravel/sanctum": "^3.2",
"laravel/tinker": "^2.8",
"laravel/ui": "^4.2",
"laravelcollective/html": "^6.4",
"maatwebsite/excel": "^3.1",
"predis/predis": "^2.2",
"spatie/laravel-permission": "^5.11"
},
"require-dev": {
"fakerphp/faker": "^1.9.1",

1528
composer.lock generated

File diff suppressed because it is too large Load Diff

18
config/app.php

@ -5,6 +5,12 @@ @@ -5,6 +5,12 @@
return [
'ms' => env('SYSTEM_MULTISYS', false),
'osr' => env('SYSTEM_OVERSPEEDRED', false),
'vpk' => env('SYSTEM_VIOLATIONPARKING', false),
'itl' => env('SYSTEM_INTERVAL', false),
/*
|--------------------------------------------------------------------------
| Application Name
@ -70,7 +76,7 @@ @@ -70,7 +76,7 @@
|
*/
'timezone' => 'UTC',
'timezone' => 'Asia/Taipei',
/*
|--------------------------------------------------------------------------
@ -83,7 +89,7 @@ @@ -83,7 +89,7 @@
|
*/
'locale' => 'en',
'locale' => 'zh_TW',
/*
|--------------------------------------------------------------------------
@ -96,7 +102,7 @@ @@ -96,7 +102,7 @@
|
*/
'fallback_locale' => 'en',
'fallback_locale' => 'zh_TW',
/*
|--------------------------------------------------------------------------
@ -109,7 +115,7 @@ @@ -109,7 +115,7 @@
|
*/
'faker_locale' => 'en_US',
'faker_locale' => 'zh_TW',
/*
|--------------------------------------------------------------------------
@ -141,7 +147,7 @@ @@ -141,7 +147,7 @@
'maintenance' => [
'driver' => 'file',
// 'store' => 'redis',
// 'store' => 'redis',
],
/*
@ -165,7 +171,7 @@ @@ -165,7 +171,7 @@
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
])->toArray(),

5
config/auth.php

@ -40,6 +40,11 @@ @@ -40,6 +40,11 @@
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
// 'hash' => true,
],
],
/*

4
config/database.php

@ -56,7 +56,7 @@ @@ -56,7 +56,7 @@
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'strict' => false,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
@ -125,7 +125,7 @@ @@ -125,7 +125,7 @@
'options' => [
'cluster' => env('REDIS_CLUSTER', 'redis'),
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
// 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
],
'default' => [

364
config/excel.php

@ -0,0 +1,364 @@ @@ -0,0 +1,364 @@
<?php
use Maatwebsite\Excel\Excel;
return [
'exports' => [
/*
|--------------------------------------------------------------------------
| Chunk size
|--------------------------------------------------------------------------
|
| When using FromQuery, the query is automatically chunked.
| Here you can specify how big the chunk should be.
|
*/
'chunk_size' => 1000,
/*
|--------------------------------------------------------------------------
| Pre-calculate formulas during export
|--------------------------------------------------------------------------
*/
'pre_calculate_formulas' => false,
/*
|--------------------------------------------------------------------------
| Enable strict null comparison
|--------------------------------------------------------------------------
|
| When enabling strict null comparison empty cells ('') will
| be added to the sheet.
*/
'strict_null_comparison' => false,
/*
|--------------------------------------------------------------------------
| CSV Settings
|--------------------------------------------------------------------------
|
| Configure e.g. delimiter, enclosure and line ending for CSV exports.
|
*/
'csv' => [
'delimiter' => ',',
'enclosure' => '"',
'line_ending' => PHP_EOL,
'use_bom' => false,
'include_separator_line' => false,
'excel_compatibility' => false,
'output_encoding' => '',
'test_auto_detect' => true,
],
/*
|--------------------------------------------------------------------------
| Worksheet properties
|--------------------------------------------------------------------------
|
| Configure e.g. default title, creator, subject,...
|
*/
'properties' => [
'creator' => '',
'lastModifiedBy' => '',
'title' => '',
'description' => '',
'subject' => '',
'keywords' => '',
'category' => '',
'manager' => '',
'company' => '',
],
],
'imports' => [
/*
|--------------------------------------------------------------------------
| Read Only
|--------------------------------------------------------------------------
|
| When dealing with imports, you might only be interested in the
| data that the sheet exists. By default we ignore all styles,
| however if you want to do some logic based on style data
| you can enable it by setting read_only to false.
|
*/
'read_only' => true,
/*
|--------------------------------------------------------------------------
| Ignore Empty
|--------------------------------------------------------------------------
|
| When dealing with imports, you might be interested in ignoring
| rows that have null values or empty strings. By default rows
| containing empty strings or empty values are not ignored but can be
| ignored by enabling the setting ignore_empty to true.
|
*/
'ignore_empty' => false,
/*
|--------------------------------------------------------------------------
| Heading Row Formatter
|--------------------------------------------------------------------------
|
| Configure the heading row formatter.
| Available options: none|slug|custom
|
*/
'heading_row' => [
'formatter' => 'slug',
],
/*
|--------------------------------------------------------------------------
| CSV Settings
|--------------------------------------------------------------------------
|
| Configure e.g. delimiter, enclosure and line ending for CSV imports.
|
*/
'csv' => [
'delimiter' => null,
'enclosure' => '"',
'escape_character' => '\\',
'contiguous' => false,
'input_encoding' => 'UTF-8',
],
/*
|--------------------------------------------------------------------------
| Worksheet properties
|--------------------------------------------------------------------------
|
| Configure e.g. default title, creator, subject,...
|
*/
'properties' => [
'creator' => '',
'lastModifiedBy' => '',
'title' => '',
'description' => '',
'subject' => '',
'keywords' => '',
'category' => '',
'manager' => '',
'company' => '',
],
],
/*
|--------------------------------------------------------------------------
| Extension detector
|--------------------------------------------------------------------------
|
| Configure here which writer/reader type should be used when the package
| needs to guess the correct type based on the extension alone.
|
*/
'extension_detector' => [
'xlsx' => Excel::XLSX,
'xlsm' => Excel::XLSX,
'xltx' => Excel::XLSX,
'xltm' => Excel::XLSX,
'xls' => Excel::XLS,
'xlt' => Excel::XLS,
'ods' => Excel::ODS,
'ots' => Excel::ODS,
'slk' => Excel::SLK,
'xml' => Excel::XML,
'gnumeric' => Excel::GNUMERIC,
'htm' => Excel::HTML,
'html' => Excel::HTML,
'csv' => Excel::CSV,
'tsv' => Excel::TSV,
/*
|--------------------------------------------------------------------------
| PDF Extension
|--------------------------------------------------------------------------
|
| Configure here which Pdf driver should be used by default.
| Available options: Excel::MPDF | Excel::TCPDF | Excel::DOMPDF
|
*/
'pdf' => Excel::DOMPDF,
],
/*
|--------------------------------------------------------------------------
| Value Binder
|--------------------------------------------------------------------------
|
| PhpSpreadsheet offers a way to hook into the process of a value being
| written to a cell. In there some assumptions are made on how the
| value should be formatted. If you want to change those defaults,
| you can implement your own default value binder.
|
| Possible value binders:
|
| [x] Maatwebsite\Excel\DefaultValueBinder::class
| [x] PhpOffice\PhpSpreadsheet\Cell\StringValueBinder::class
| [x] PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder::class
|
*/
'value_binder' => [
'default' => Maatwebsite\Excel\DefaultValueBinder::class,
],
'cache' => [
/*
|--------------------------------------------------------------------------
| Default cell caching driver
|--------------------------------------------------------------------------
|
| By default PhpSpreadsheet keeps all cell values in memory, however when
| dealing with large files, this might result into memory issues. If you
| want to mitigate that, you can configure a cell caching driver here.
| When using the illuminate driver, it will store each value in the
| cache store. This can slow down the process, because it needs to
| store each value. You can use the "batch" store if you want to
| only persist to the store when the memory limit is reached.
|
| Drivers: memory|illuminate|batch
|
*/
'driver' => 'memory',
/*
|--------------------------------------------------------------------------
| Batch memory caching
|--------------------------------------------------------------------------
|
| When dealing with the "batch" caching driver, it will only
| persist to the store when the memory limit is reached.
| Here you can tweak the memory limit to your liking.
|
*/
'batch' => [
'memory_limit' => 60000,
],
/*
|--------------------------------------------------------------------------
| Illuminate cache
|--------------------------------------------------------------------------
|
| When using the "illuminate" caching driver, it will automatically use
| your default cache store. However if you prefer to have the cell
| cache on a separate store, you can configure the store name here.
| You can use any store defined in your cache config. When leaving
| at "null" it will use the default store.
|
*/
'illuminate' => [
'store' => null,
],
/*
|--------------------------------------------------------------------------
| Cache Time-to-live (TTL)
|--------------------------------------------------------------------------
|
| The TTL of items written to cache. If you want to keep the items cached
| indefinitely, set this to null. Otherwise, set a number of seconds,
| a \DateInterval, or a callable.
|
| Allowable types: callable|\DateInterval|int|null
|
*/
'default_ttl' => 10800,
],
/*
|--------------------------------------------------------------------------
| Transaction Handler
|--------------------------------------------------------------------------
|
| By default the import is wrapped in a transaction. This is useful
| for when an import may fail and you want to retry it. With the
| transactions, the previous import gets rolled-back.
|
| You can disable the transaction handler by setting this to null.
| Or you can choose a custom made transaction handler here.
|
| Supported handlers: null|db
|
*/
'transactions' => [
'handler' => 'db',
'db' => [
'connection' => null,
],
],
'temporary_files' => [
/*
|--------------------------------------------------------------------------
| Local Temporary Path
|--------------------------------------------------------------------------
|
| When exporting and importing files, we use a temporary file, before
| storing reading or downloading. Here you can customize that path.
| permissions is an array with the permission flags for the directory (dir)
| and the create file (file).
|
*/
'local_path' => storage_path('framework/cache/laravel-excel'),
/*
|--------------------------------------------------------------------------
| Local Temporary Path Permissions
|--------------------------------------------------------------------------
|
| Permissions is an array with the permission flags for the directory (dir)
| and the create file (file).
| If omitted the default permissions of the filesystem will be used.
|
*/
'local_permissions' => [
// 'dir' => 0755,
// 'file' => 0644,
],
/*
|--------------------------------------------------------------------------
| Remote Temporary Disk
|--------------------------------------------------------------------------
|
| When dealing with a multi server setup with queues in which you
| cannot rely on having a shared local temporary path, you might
| want to store the temporary file on a shared disk. During the
| queue executing, we'll retrieve the temporary file from that
| location instead. When left to null, it will always use
| the local path. This setting only has effect when using
| in conjunction with queued imports and exports.
|
*/
'remote_disk' => null,
'remote_prefix' => null,
/*
|--------------------------------------------------------------------------
| Force Resync
|--------------------------------------------------------------------------
|
| When dealing with a multi server setup as above, it's possible
| for the clean up that occurs after entire queue has been run to only
| cleanup the server that the last AfterImportJob runs on. The rest of the server
| would still have the local temporary file stored on it. In this case your
| local storage limits can be exceeded and future imports won't be processed.
| To mitigate this you can set this config value to be true, so that after every
| queued chunk is processed the local temporary file is deleted on the server that
| processed it.
|
*/
'force_resync_remote' => null,
],
];

4
config/hashing.php

@ -29,8 +29,7 @@ @@ -29,8 +29,7 @@
*/
'bcrypt' => [
'rounds' => env('BCRYPT_ROUNDS', 12),
'verify' => true,
'rounds' => env('BCRYPT_ROUNDS', 10),
],
/*
@ -48,7 +47,6 @@ @@ -48,7 +47,6 @@
'memory' => 65536,
'threads' => 1,
'time' => 4,
'verify' => true,
],
];

21
config/image.php

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Image Driver
|--------------------------------------------------------------------------
|
| Intervention Image supports “GD Library” and “Imagick” to process images
| internally. Depending on your PHP setup, you can choose one of them.
|
| Included options:
| - \Intervention\Image\Drivers\Gd\Driver::class
| - \Intervention\Image\Drivers\Imagick\Driver::class
|
*/
'driver' => \Intervention\Image\Drivers\Gd\Driver::class
];

8
config/logging.php

@ -126,6 +126,14 @@ @@ -126,6 +126,14 @@
'emergency' => [
'path' => storage_path('logs/laravel.log'),
],
'entry' => [
'driver' => 'daily',
'path' => storage_path('logs/entry/entry.log'),
'level' => 'debug',
'days' => 14,
'replace_placeholders' => true,
],
],
];

19
config/mail.php

@ -29,7 +29,7 @@ @@ -29,7 +29,7 @@
| mailers below. You are free to add additional mailers as required.
|
| Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2",
| "postmark", "log", "array", "failover", "roundrobin"
| "postmark", "log", "array", "failover"
|
*/
@ -50,16 +50,15 @@ @@ -50,16 +50,15 @@
'transport' => 'ses',
],
'postmark' => [
'transport' => 'postmark',
// 'message_stream_id' => null,
'mailgun' => [
'transport' => 'mailgun',
// 'client' => [
// 'timeout' => 5,
// ],
],
'mailgun' => [
'transport' => 'mailgun',
'postmark' => [
'transport' => 'postmark',
// 'client' => [
// 'timeout' => 5,
// ],
@ -86,14 +85,6 @@ @@ -86,14 +85,6 @@
'log',
],
],
'roundrobin' => [
'transport' => 'roundrobin',
'mailers' => [
'ses',
'postmark',
],
],
],
/*

161
config/permission.php

@ -0,0 +1,161 @@ @@ -0,0 +1,161 @@
<?php
return [
'models' => [
/*
* When using the "HasPermissions" trait from this package, we need to know which
* Eloquent model should be used to retrieve your permissions. Of course, it
* is often just the "Permission" model but you may use whatever you like.
*
* The model you want to use as a Permission model needs to implement the
* `Spatie\Permission\Contracts\Permission` contract.
*/
'permission' => Spatie\Permission\Models\Permission::class,
/*
* When using the "HasRoles" trait from this package, we need to know which
* Eloquent model should be used to retrieve your roles. Of course, it
* is often just the "Role" model but you may use whatever you like.
*
* The model you want to use as a Role model needs to implement the
* `Spatie\Permission\Contracts\Role` contract.
*/
'role' => Spatie\Permission\Models\Role::class,
],
'table_names' => [
/*
* When using the "HasRoles" trait from this package, we need to know which
* table should be used to retrieve your roles. We have chosen a basic
* default value but you may easily change it to any table you like.
*/
'roles' => 'roles',
/*
* When using the "HasPermissions" trait from this package, we need to know which
* table should be used to retrieve your permissions. We have chosen a basic
* default value but you may easily change it to any table you like.
*/
'permissions' => 'permissions',
/*
* When using the "HasPermissions" trait from this package, we need to know which
* table should be used to retrieve your models permissions. We have chosen a
* basic default value but you may easily change it to any table you like.
*/
'model_has_permissions' => 'model_has_permissions',
/*
* When using the "HasRoles" trait from this package, we need to know which
* table should be used to retrieve your models roles. We have chosen a
* basic default value but you may easily change it to any table you like.
*/
'model_has_roles' => 'model_has_roles',
/*
* When using the "HasRoles" trait from this package, we need to know which
* table should be used to retrieve your roles permissions. We have chosen a
* basic default value but you may easily change it to any table you like.
*/
'role_has_permissions' => 'role_has_permissions',
],
'column_names' => [
/*
* Change this if you want to name the related pivots other than defaults
*/
'role_pivot_key' => null, //default 'role_id',
'permission_pivot_key' => null, //default 'permission_id',
/*
* Change this if you want to name the related model primary key other than
* `model_id`.
*
* For example, this would be nice if your primary keys are all UUIDs. In
* that case, name this `model_uuid`.
*/
'model_morph_key' => 'model_id',
/*
* Change this if you want to use the teams feature and your related model's
* foreign key is other than `team_id`.
*/
'team_foreign_key' => 'team_id',
],
/*
* When set to true, the method for checking permissions will be registered on the gate.
* Set this to false, if you want to implement custom logic for checking permissions.
*/
'register_permission_check_method' => true,
/*
* When set to true the package implements teams using the 'team_foreign_key'. If you want
* the migrations to register the 'team_foreign_key', you must set this to true
* before doing the migration. If you already did the migration then you must make a new
* migration to also add 'team_foreign_key' to 'roles', 'model_has_roles', and
* 'model_has_permissions'(view the latest version of package's migration file)
*/
'teams' => false,
/*
* When set to true, the required permission names are added to the exception
* message. This could be considered an information leak in some contexts, so
* the default setting is false here for optimum safety.
*/
'display_permission_in_exception' => false,
/*
* When set to true, the required role names are added to the exception
* message. This could be considered an information leak in some contexts, so
* the default setting is false here for optimum safety.
*/
'display_role_in_exception' => false,
/*
* By default wildcard permission lookups are disabled.
*/
'enable_wildcard_permission' => false,
'cache' => [
/*
* By default all permissions are cached for 24 hours to speed up performance.
* When permissions or roles are updated the cache is flushed automatically.
*/
'expiration_time' => \DateInterval::createFromDateString('24 hours'),
/*
* The cache key used to store all permissions.
*/
'key' => 'spatie.permission.cache',
/*
* You may optionally indicate a specific cache driver to use for permission and
* role caching using any of the `store` drivers listed in the cache.php config
* file. Using 'default' here means to use the `default` set in cache.php.
*/
'store' => 'default',
],
];

22
config/sanctum.php

@ -41,28 +41,13 @@ @@ -41,28 +41,13 @@
|--------------------------------------------------------------------------
|
| This value controls the number of minutes until an issued token will be
| considered expired. This will override any values set in the token's
| "expires_at" attribute, but first-party sessions are not affected.
| considered expired. If this value is null, personal access tokens do
| not expire. This won't tweak the lifetime of first-party sessions.
|
*/
'expiration' => null,
/*
|--------------------------------------------------------------------------
| Token Prefix
|--------------------------------------------------------------------------
|
| Sanctum can prefix new tokens in order to take advantage of numerous
| security scanning initiatives maintained by open source platforms
| that notify developers if they commit tokens into repositories.
|
| See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning
|
*/
'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''),
/*
|--------------------------------------------------------------------------
| Sanctum Middleware
@ -75,9 +60,8 @@ @@ -75,9 +60,8 @@
*/
'middleware' => [
'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class,
'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class,
'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class,
'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class,
],
];

13
config/session.php

@ -198,17 +198,4 @@ @@ -198,17 +198,4 @@
'same_site' => 'lax',
/*
|--------------------------------------------------------------------------
| Partitioned Cookies
|--------------------------------------------------------------------------
|
| Setting this value to true will tie the cookie to the top-level site for
| a cross-site context. Partitioned cookies are accepted by the browser
| when flagged "secure" and the Same-Site attribute is set to "none".
|
*/
'partitioned' => false,
];

8
database/factories/UserFactory.php

@ -3,7 +3,6 @@ @@ -3,7 +3,6 @@
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
/**
@ -11,11 +10,6 @@ @@ -11,11 +10,6 @@
*/
class UserFactory extends Factory
{
/**
* The current password being used by the factory.
*/
protected static ?string $password;
/**
* Define the model's default state.
*
@ -27,7 +21,7 @@ public function definition(): array @@ -27,7 +21,7 @@ public function definition(): array
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
];
}

32
database/migrations/2014_10_12_100000_create_password_resets_table.php

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('password_resets', function (Blueprint $table) {
$table->string('email')->index();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('password_resets');
}
};

145
database/migrations/2023_10_11_035920_create_permission_tables.php

@ -0,0 +1,145 @@ @@ -0,0 +1,145 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Spatie\Permission\PermissionRegistrar;
class CreatePermissionTables extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$tableNames = config('permission.table_names');
$columnNames = config('permission.column_names');
$teams = config('permission.teams');
if (empty($tableNames)) {
throw new \Exception('Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.');
}
if ($teams && empty($columnNames['team_foreign_key'] ?? null)) {
throw new \Exception('Error: team_foreign_key on config/permission.php not loaded. Run [php artisan config:clear] and try again.');
}
Schema::create($tableNames['permissions'], function (Blueprint $table) {
$table->bigIncrements('id'); // permission id
$table->string('display_name')->nullable();
$table->string('type')->nullable();
$table->integer('sort')->nullable()->default(0);
$table->string('name')->nullable(); // For MySQL 8.0 use string('name', 125);
$table->string('guard_name'); // For MySQL 8.0 use string('guard_name', 125);
$table->timestamps();
$table->unique(['name', 'guard_name']);
});
Schema::create($tableNames['roles'], function (Blueprint $table) use ($teams, $columnNames) {
$table->bigIncrements('id'); // role id
if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing
$table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable();
$table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index');
}
$table->string('display_name')->nullable();
$table->string('name')->nullable(); // For MySQL 8.0 use string('name', 125);
$table->string('guard_name'); // For MySQL 8.0 use string('guard_name', 125);
$table->timestamps();
if ($teams || config('permission.testing')) {
$table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']);
} else {
$table->unique(['name', 'guard_name']);
}
});
Schema::create($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames, $columnNames, $teams) {
$table->unsignedBigInteger(PermissionRegistrar::$pivotPermission);
$table->string('model_type');
$table->unsignedBigInteger($columnNames['model_morph_key']);
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index');
$table->foreign(PermissionRegistrar::$pivotPermission)
->references('id') // permission id
->on($tableNames['permissions'])
->onDelete('cascade');
if ($teams) {
$table->unsignedBigInteger($columnNames['team_foreign_key']);
$table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index');
$table->primary([$columnNames['team_foreign_key'], PermissionRegistrar::$pivotPermission, $columnNames['model_morph_key'], 'model_type'],
'model_has_permissions_permission_model_type_primary');
} else {
$table->primary([PermissionRegistrar::$pivotPermission, $columnNames['model_morph_key'], 'model_type'],
'model_has_permissions_permission_model_type_primary');
}
});
Schema::create($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames, $columnNames, $teams) {
$table->unsignedBigInteger(PermissionRegistrar::$pivotRole);
$table->string('model_type');
$table->unsignedBigInteger($columnNames['model_morph_key']);
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index');
$table->foreign(PermissionRegistrar::$pivotRole)
->references('id') // role id
->on($tableNames['roles'])
->onDelete('cascade');
if ($teams) {
$table->unsignedBigInteger($columnNames['team_foreign_key']);
$table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index');
$table->primary([$columnNames['team_foreign_key'], PermissionRegistrar::$pivotRole, $columnNames['model_morph_key'], 'model_type'],
'model_has_roles_role_model_type_primary');
} else {
$table->primary([PermissionRegistrar::$pivotRole, $columnNames['model_morph_key'], 'model_type'],
'model_has_roles_role_model_type_primary');
}
});
Schema::create($tableNames['role_has_permissions'], function (Blueprint $table) use ($tableNames) {
$table->unsignedBigInteger(PermissionRegistrar::$pivotPermission);
$table->unsignedBigInteger(PermissionRegistrar::$pivotRole);
$table->foreign(PermissionRegistrar::$pivotPermission)
->references('id') // permission id
->on($tableNames['permissions'])
->onDelete('cascade');
$table->foreign(PermissionRegistrar::$pivotRole)
->references('id') // role id
->on($tableNames['roles'])
->onDelete('cascade');
$table->primary([PermissionRegistrar::$pivotPermission, PermissionRegistrar::$pivotRole], 'role_has_permissions_permission_id_role_id_primary');
});
app('cache')
->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null)
->forget(config('permission.cache.key'));
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
$tableNames = config('permission.table_names');
if (empty($tableNames)) {
throw new \Exception('Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.');
}
Schema::drop($tableNames['role_has_permissions']);
Schema::drop($tableNames['model_has_roles']);
Schema::drop($tableNames['model_has_permissions']);
Schema::drop($tableNames['roles']);
Schema::drop($tableNames['permissions']);
}
}

31
database/migrations/2023_10_11_062424_add_api_token.php

@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('api_token', 80)->after('password')
->unique()
->nullable()
->default(null);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
//
});
}
};

52
database/migrations/2023_10_16_062701_add_user_detail.php

@ -0,0 +1,52 @@ @@ -0,0 +1,52 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('account', 80)->after('id')
->unique()
->nullable()
->comment('帳號');
$table->integer('status')->after('account')
->nullable()
->default(0)
->comment('狀態: 0 啟用/ 1 停用');
$table->string('class', 255)->after('status')
->nullable()
->comment('職級');
$table->string('leader', 255)->after('class')
->nullable()
->comment('單位代碼');
$table->string('exp_op', 255)->after('leader')
->nullable()
->comment('員警代碼');
$table->string('unit', 255)->after('exp_op')
->nullable()
->comment('單位: 第一中隊、第三中隊、交通隊 ...');
$table->string('device', 255)->after('unit')
->nullable()
->comment('限制設備設定');
$table->timestamp('deleted_at')->after('updated_at')
->nullable()
->comment('刪除時間');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
//
}
};

34
database/migrations/2023_10_16_081301_create_user_log_table.php

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('user_log', function (Blueprint $table) {
$table->id();
$table->string('user_id', 80)->nullable()->comment('使用者ID');
$table->string('user_name', 80)->nullable()->comment('使用者名稱');
$table->string('action', 80)->nullable()->comment('動作');
$table->string('action_detail', 80)->nullable()->comment('動作細節');
$table->string('ip', 80)->nullable()->comment('IP');
$table->string('mac', 80)->nullable()->comment('MAC');
$table->longText('remark')->nullable()->comment('備註');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('user_log');
}
};

31
database/migrations/2023_10_27_005420_add_renew_password_at_to_users_table.php

@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->date('renew_password_at')->after('device')
->nullable()
->default(null)
->comment('上次修改密碼時間');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
//
});
}
};

31
database/migrations/2023_10_27_010709_create_sessions_table.php

@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->primary();
$table->foreignId('user_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->longText('payload');
$table->integer('last_activity')->index();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('sessions');
}
};

33
database/migrations/2023_11_07_055939_create_violation_law_table.php

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('violation_law', function (Blueprint $table) {
$table->id();
$table->string('violationcode')->nullable()->comment('違規法條代碼');
$table->string('display_name')->nullable()->comment('違規法條說明');
$table->string('type')->nullable()->comment('違規法條系統分類');
$table->integer('sort')->nullable()->default(0)->comment('排序');
$table->unique(['violationcode', 'display_name']);
$table->index('violationcode');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('violation_law');
}
};

44
database/migrations/2023_11_07_061949_create_violationparking_table.php

@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('violationparking', function (Blueprint $table) {
$table->id();
$table->string('picture', 255)->nullable()->comment('違規停車照片A');
$table->string('picture2', 255)->nullable()->comment('違規停車照片B');
$table->dateTime('datatime')->nullable()->comment('違規停車時間');
$table->string('cartype', 255)->nullable()->comment('車種');
$table->string('carnumber', 255)->nullable()->comment('違規停車車牌');
$table->string('serialnumber', 255)->nullable()->comment('設備編號');
$table->string('location', 255)->nullable()->comment('設備位置');
$table->string('violationtype', 255)->nullable()->comment('違規類型');
$table->string('violationcode', 255)->nullable()->comment('違規法條代碼');
$table->integer('processcheck')->nullable()->default(0)->comment('審查狀態 0:未審查 1:已審查 2:不舉發');
$table->integer('postcheck')->nullable()->default(0)->comment('入案狀態 0:未入案 1:已入案');
$table->string('jsoncheck', 255)->nullable()->comment('審查人');
$table->string('unreportreason', 255)->nullable()->comment('不舉發原因');
$table->string('unreportpicture', 255)->nullable()->comment('不舉發照片路徑');
$table->integer('unreportmergedone')->nullable()->default(0)->comment('不舉發照片合成狀態 0:未合併 1:已合併');
$table->unique(['carnumber', 'datatime']);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('violationparking');
}
};

41
database/migrations/2023_11_07_064603_create_violationparking_equipment_table.php

@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('violationparking_equipment', function (Blueprint $table) {
$table->id();
$table->string('serialnumber', 255)->nullable()->comment('機號');
$table->string('brand', 255)->nullable()->comment('廠牌');
$table->string('model', 255)->nullable()->comment('型號');
//
$table->unsignedBigInteger('custodyunit_id')->nullable()->comment('保管單位(關聯)');
$table->unsignedBigInteger('assetownership_id')->nullable()->comment('財產所屬單位(關聯)');
$table->date('buydate')->nullable()->comment('購買日期');
$table->date('activatedate')->nullable()->comment('啟用日期');
$table->string('locationid', 255)->nullable()->comment('設備位置編號');
$table->string('location', 255)->nullable()->comment('設備位置');
//
$table->longText('comment')->nullable()->comment('備註');
$table->dateTime('deleted_at')->nullable()->comment('是否刪除');
$table->unique(['serialnumber']);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('violationparking_equipment');
}
};

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save