1396 lines
64 KiB
PHP
1396 lines
64 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers;
|
||
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Illuminate\Support\Facades\Session;
|
||
use Illuminate\Validation\Rule;
|
||
use Illuminate\Support\Facades\Validator;
|
||
use Illuminate\Validation\ValidationException;
|
||
use Illuminate\Support\Facades\Redirect;
|
||
use Illuminate\Support\Facades\Artisan;
|
||
use Exception;
|
||
use function redirect;
|
||
|
||
class RegularContractCreateController extends Controller
|
||
{
|
||
// 新規作成画面表示
|
||
public function show()
|
||
{
|
||
$user_id = session('user_id');
|
||
if (!$user_id) {
|
||
return redirect('/login');
|
||
}
|
||
$user = DB::table('user')->where('user_id', $user_id)->first();
|
||
|
||
// 市町村名(park→city JOINで重複排除)
|
||
$cities = DB::table('park')
|
||
->join('city', 'park.city_id', '=', 'city.city_id')
|
||
->select('city.city_id', 'city.city_name')
|
||
->distinct()
|
||
->get();
|
||
|
||
// city_idごとの更新可能期間情報を取得
|
||
$city_grace_periods = DB::table('city')
|
||
->select('city_id', 'update_grace_period_start_date', 'update_grace_period_start_time', 'update_grace_period_end_date', 'update_grace_period_end_time')
|
||
->get()
|
||
->keyBy('city_id');
|
||
|
||
// 駅名(stationテーブルのstation_neighbor_station全件)
|
||
$stations = DB::table('station')
|
||
->select('station_neighbor_station')
|
||
->distinct()
|
||
->get();
|
||
|
||
// 駐輪場名(parkテーブルのpark_name全件)
|
||
$parks = DB::table('park')
|
||
->select('park_id', 'park_name')
|
||
->distinct()
|
||
->get();
|
||
|
||
// テーブル表示用データ(park/city/station JOIN, park_id昇順, 10件ずつページング)
|
||
$page = request()->input('page', 1);
|
||
$perPage = 10;
|
||
$city_id = request()->input('city_id');
|
||
$station_name = request()->input('station_neighbor_station');
|
||
$park_id = request()->input('park_id');
|
||
|
||
$query = DB::table('park')
|
||
->join('city', 'park.city_id', '=', 'city.city_id')
|
||
->leftJoin('station', 'park.park_id', '=', 'station.park_id')
|
||
->select(
|
||
'park.park_id',
|
||
'park.park_name',
|
||
'park.park_ruby',
|
||
'city.city_name',
|
||
'city.city_id',
|
||
'station.station_neighbor_station',
|
||
'station.station_name_ruby'
|
||
);
|
||
|
||
if ($city_id) {
|
||
$query->where('city.city_id', $city_id);
|
||
}
|
||
if ($station_name) {
|
||
$query->where('station.station_neighbor_station', $station_name);
|
||
}
|
||
if ($park_id) {
|
||
$query->where('park.park_id', $park_id);
|
||
}
|
||
|
||
// 並び替えパラメータ取得
|
||
$sort = request()->input('sort', 'park_id');
|
||
$order = request()->input('order', 'asc');
|
||
$sortable = [
|
||
'park_ruby' => 'park.park_ruby',
|
||
'city_id' => 'city.city_id',
|
||
'station_name_ruby' => 'station.station_name_ruby',
|
||
'park_id' => 'park.park_id',
|
||
];
|
||
if (isset($sortable[$sort])) {
|
||
$query->orderBy($sortable[$sort], $order);
|
||
} else {
|
||
$query->orderBy('park.park_id', 'asc');
|
||
}
|
||
|
||
$total = $query->count();
|
||
$parks_table = $query->skip(($page - 1) * $perPage)->take($perPage)->get();
|
||
|
||
// zoneテーブルデータを取得(psectionテーブルとJOINしてpsection_subjectも取得)
|
||
$zones = DB::table('zone')
|
||
->leftJoin('psection', 'zone.psection_id', '=', 'psection.psection_id')
|
||
->select('zone.zone_id', 'zone.park_id', 'zone.psection_id', 'zone.zone_number', 'zone.zone_tolerance', 'psection.psection_subject')
|
||
->get()
|
||
->groupBy('park_id');
|
||
|
||
// 空き予約マスタデータを取得
|
||
$reserve = DB::table('reserve')
|
||
->select('reserve_id', 'park_id', 'psection_id')
|
||
->where('valid_flag', 1)
|
||
->get()
|
||
->groupBy('park_id');
|
||
|
||
\Log::info('新規定期契約-駐輪場選択画面にアクセス', [
|
||
'user_id' => $user_id,
|
||
]);
|
||
|
||
return view('regular_contract.create', [
|
||
'active_menu' => 'SWC-8-1', // この画面のID
|
||
'user_name' => $user ? $user->user_name : '', // ユーザー名(ヘッダー用)
|
||
'cities' => $cities,
|
||
'stations' => $stations,
|
||
'parks' => $parks,
|
||
'parks_table' => $parks_table,
|
||
'parks_table_total' => $total,
|
||
'parks_table_page' => $page,
|
||
'parks_table_perPage' => $perPage,
|
||
'zones' => $zones,
|
||
'city_grace_periods' => $city_grace_periods,
|
||
'reserve' => $reserve,
|
||
]);
|
||
}
|
||
|
||
public function regulationCheck(Request $request)
|
||
{
|
||
// GETパラメータを取得
|
||
$parkId = $request->query('park_id');
|
||
$psectionId = $request->query('psection_id');
|
||
$ptypeId = $request->query('ptype_id');
|
||
|
||
// 必要なDB処理やロジック
|
||
$park_regulation = DB::table('park')->where('park_id', $parkId)->value('parking_regulations_flag');
|
||
|
||
if ($park_regulation == 1) {
|
||
// 駐輪規定画面へ
|
||
return redirect()->route('regular_contract.regulation', [
|
||
'park_id' => $parkId,
|
||
'psection_id' => $psectionId,
|
||
'ptype_id' => $ptypeId,
|
||
]);
|
||
} else {
|
||
// 契約情報入力画面へ
|
||
return redirect()->route('regular_contract.input', [
|
||
'park_id' => $parkId,
|
||
'psection_id' => $psectionId,
|
||
'ptype_id' => $ptypeId,
|
||
]);
|
||
}
|
||
}
|
||
|
||
public function showRegulation(Request $request)
|
||
{
|
||
$user_id = session('user_id');
|
||
if (!$user_id) {
|
||
return redirect('/login');
|
||
}
|
||
$user_name = DB::table('user')->where('user_id', $user_id)->value('user_name');
|
||
|
||
// 必要なパラメータ取得
|
||
$parkId = $request->query('park_id');
|
||
$psectionId = $request->query('psection_id');
|
||
$ptypeId = $request->query('ptype_id');
|
||
|
||
$park_name = DB::table('park')->where('park_id', $parkId)->value('park_name');
|
||
$psection_subject = DB::table('psection')->where('psection_id', $psectionId)->value('psection_subject');
|
||
$ptype_subject = DB::table('ptype')->where('ptype_id', $ptypeId)->value('ptype_subject');
|
||
$regulations_text = DB::table('parking_regulations')
|
||
->where('park_id', $parkId)
|
||
->where('psection_id', $psectionId)
|
||
->where('ptype_id', $ptypeId)
|
||
->value('regulations_text');
|
||
|
||
\Log::info('駐輪規定確認画面にアクセス', [
|
||
'user_id' => $user_id,
|
||
]);
|
||
|
||
return view('regular_contract.regulation', [
|
||
'park_id' => $parkId,
|
||
'park_name' => $park_name,
|
||
'psection_id' => $psectionId,
|
||
'psection_subject' => $psection_subject,
|
||
'ptype_id' => $ptypeId,
|
||
'ptype_subject' => $ptype_subject,
|
||
'regulations_text' => $regulations_text,
|
||
'active_menu' => 'SWC-8-1', // この画面のID
|
||
'user_name' => $user_name, // ユーザー名(ヘッダー用)
|
||
]);
|
||
}
|
||
|
||
public function insertRegulation(Request $request)
|
||
{
|
||
$user_id = session('user_id');
|
||
if (!$user_id) {
|
||
return redirect('/login');
|
||
}
|
||
$park_id = $request->input('park_id');
|
||
$psection_id = $request->input('psection_id');
|
||
$ptype_id = $request->input('ptype_id');
|
||
|
||
DB::table('parking_regulations_read')->insert([
|
||
'park_id' => $park_id,
|
||
'psection_id' => $psection_id,
|
||
'ptype_id' => $ptype_id,
|
||
'user_id' => $user_id,
|
||
'created_at' => now(),
|
||
]);
|
||
|
||
// 契約入力画面へリダイレクト
|
||
return redirect()->route('regular_contract.input', [
|
||
'park_id' => $park_id,
|
||
'psection_id' => $psection_id,
|
||
'ptype_id' => $ptype_id,
|
||
]);
|
||
}
|
||
|
||
// 契約入力画面表示
|
||
public function showContractForm(Request $request)
|
||
{
|
||
$user_id = session('user_id');
|
||
if (!$user_id) {
|
||
return redirect('/login');
|
||
}
|
||
$user = DB::table('user')->where('user_id', $user_id)->first();
|
||
$park_id = $request->query('park_id');
|
||
$park = DB::table('park')->where('park_id', $park_id)->first();
|
||
$psection_id = $request->query('psection_id');
|
||
$ptype_id = $request->query('ptype_id');
|
||
$city_id = DB::table('park')->where('park_id', $park_id)->value('city_id');
|
||
$terms_text = DB::table('terms')->where('city_id', $city_id)->value('terms_text');
|
||
// 利用者区分をusertypeテーブルから取得
|
||
$user_category = '';
|
||
if (isset($user->user_categoryid)) {
|
||
$usertype = DB::table('usertype')
|
||
->where('user_categoryid', $user->user_categoryid)
|
||
->first();
|
||
if ($usertype && isset($usertype->usertype_subject1)) {
|
||
$user_category = $usertype->usertype_subject1;
|
||
}
|
||
}
|
||
$user->user_homephone = explode('-', $user->user_homephone ?? '');
|
||
$user->user_mobile = explode('-', $user->user_mobile ?? '');
|
||
$user->user_regident_zip_1 = substr($user->user_regident_zip ?? '', 0, 3);
|
||
$user->user_regident_zip_2 = substr($user->user_regident_zip ?? '', 3, 4);
|
||
$user->user_relate_zip_1 = substr($user->user_relate_zip ?? '', 0, 3);
|
||
$user->user_relate_zip_2 = substr($user->user_relate_zip ?? '', 3, 4);
|
||
|
||
\Log::info('新規定期契約-契約情報入力画面にアクセス', [
|
||
'user_id' => $user_id,
|
||
]);
|
||
|
||
session()->forget('show_terms_modal');
|
||
|
||
return view('regular_contract.input', [
|
||
'park' => $park,
|
||
'psection_id' => $psection_id,
|
||
'ptype_id' => $ptype_id,
|
||
'terms_text' => $terms_text,
|
||
'user' => $user,
|
||
'user_name' => $user->user_name, // ユーザー名(ヘッダー用)
|
||
'active_menu' => 'SWC-8-1', // この画面のID
|
||
'user_category' => $user_category,
|
||
'show_terms_modal' => true,
|
||
]);
|
||
}
|
||
public function inputCheck(Request $request)
|
||
{
|
||
$user_id = session('user_id');
|
||
if (!$user_id) {
|
||
return redirect('/login');
|
||
}
|
||
$user = DB::table('user')->where('user_id', $user_id)->first();
|
||
|
||
$park_id = $request->input('park_id');
|
||
$park = DB::table('park')->where('park_id', $park_id)->first();
|
||
|
||
// バリデーションルール
|
||
$rules = [
|
||
'user_phonetic' => ['required', 'regex:/^[ァ-ヶー \s]+$/u'],
|
||
'user_regident_zip_1' => 'required|digits:3',
|
||
'user_regident_zip_2' => 'required|digits:4',
|
||
'user_regident_pre' => [
|
||
'required',
|
||
Rule::in([
|
||
'北海道',
|
||
'青森県',
|
||
'岩手県',
|
||
'宮城県',
|
||
'秋田県',
|
||
'山形県',
|
||
'福島県',
|
||
'茨城県',
|
||
'栃木県',
|
||
'群馬県',
|
||
'埼玉県',
|
||
'千葉県',
|
||
'東京都',
|
||
'神奈川県',
|
||
'新潟県',
|
||
'富山県',
|
||
'石川県',
|
||
'福井県',
|
||
'山梨県',
|
||
'長野県',
|
||
'岐阜県',
|
||
'静岡県',
|
||
'愛知県',
|
||
'三重県',
|
||
'滋賀県',
|
||
'京都府',
|
||
'大阪府',
|
||
'兵庫県',
|
||
'奈良県',
|
||
'和歌山県',
|
||
'鳥取県',
|
||
'島根県',
|
||
'岡山県',
|
||
'広島県',
|
||
'山口県',
|
||
'徳島県',
|
||
'香川県',
|
||
'愛媛県',
|
||
'高知県',
|
||
'福岡県',
|
||
'佐賀県',
|
||
'長崎県',
|
||
'熊本県',
|
||
'大分県',
|
||
'宮崎県',
|
||
'鹿児島県',
|
||
'沖縄県'
|
||
]),
|
||
],
|
||
'user_regident_city' => ['required', 'string', 'max:20', 'regex:/^(?:(?![\xF0-\xF7][\x80-\xBF]{3}).)*$/'],
|
||
'user_regident_add' => ['required', 'string', 'max:50', 'regex:/^(?:(?![\xF0-\xF7][\x80-\xBF]{3}).)*$/'],
|
||
'user_homephone.*' => 'nullable|digits_between:1,5',
|
||
'user_mobile.*' => 'nullable|digits_between:1,5',
|
||
'user_submail' => 'nullable|email|different:user_primemail|max:80',
|
||
'user_category' => ['required', Rule::in(['一般', '学生'])],
|
||
'contract_reduction' => ['required', Rule::in(['はい', 'いいえ'])],
|
||
'user_relate_zip_1' => 'nullable|digits:3',
|
||
'user_relate_zip_2' => 'nullable|digits:4',
|
||
'user_relate_pre' => [
|
||
'nullable',
|
||
Rule::in([
|
||
'北海道',
|
||
'青森県',
|
||
'岩手県',
|
||
'宮城県',
|
||
'秋田県',
|
||
'山形県',
|
||
'福島県',
|
||
'茨城県',
|
||
'栃木県',
|
||
'群馬県',
|
||
'埼玉県',
|
||
'千葉県',
|
||
'東京都',
|
||
'神奈川県',
|
||
'新潟県',
|
||
'富山県',
|
||
'石川県',
|
||
'福井県',
|
||
'山梨県',
|
||
'長野県',
|
||
'岐阜県',
|
||
'静岡県',
|
||
'愛知県',
|
||
'三重県',
|
||
'滋賀県',
|
||
'京都府',
|
||
'大阪府',
|
||
'兵庫県',
|
||
'奈良県',
|
||
'和歌山県',
|
||
'鳥取県',
|
||
'島根県',
|
||
'岡山県',
|
||
'広島県',
|
||
'山口県',
|
||
'徳島県',
|
||
'香川県',
|
||
'愛媛県',
|
||
'高知県',
|
||
'福岡県',
|
||
'佐賀県',
|
||
'長崎県',
|
||
'熊本県',
|
||
'大分県',
|
||
'宮崎県',
|
||
'鹿児島県',
|
||
'沖縄県'
|
||
]),
|
||
],
|
||
'user_relate_city' => ['nullable', 'string', 'max:20', 'regex:/^(?:(?![\xF0-\xF7][\x80-\xBF]{3}).)*$/'],
|
||
'user_relate_add' => ['nullable', 'string', 'max:50', 'regex:/^(?:(?![\xF0-\xF7][\x80-\xBF]{3}).)*$/'],
|
||
];
|
||
// 性別欄が表示されている場合のみ必須
|
||
if ((int)$park->gender_display_flag === 1) {
|
||
$rules['user_gender'] = ['required', Rule::in(['男性', '女性'])];
|
||
}
|
||
// 生年月日欄が表示されている場合のみ必須
|
||
if ((int)$park->bd_display_flag === 1) {
|
||
$rules['user_birthdate'] = ['required', 'date'];
|
||
}
|
||
// 防犯登録番号欄が表示されている場合のみ必須
|
||
if ((int)$park->securityreg_display_flag === 1) {
|
||
$rules['user_securitynum'] = ['required', 'max:50', 'regex:/^[a-zA-Z0-9]+$/'];
|
||
}
|
||
// 利用者区分ごとの追加バリデーション
|
||
if ($request->input('user_category') === '学生') {
|
||
$rules['user_school'] = ['required', 'string', 'max:50', 'regex:/^(?:(?![\xF0-\xF7][\x80-\xBF]{3}).)*$/'];
|
||
$rules['user_graduate'] = ['required', 'date'];
|
||
} else {
|
||
$rules['user_workplace'] = ['nullable', 'string', 'max:50', 'regex:/^(?:(?![\xF0-\xF7][\x80-\xBF]{3}).)*$/'];
|
||
}
|
||
$messages = [
|
||
'user_phonetic.required' => 'フリガナが入力されていません。',
|
||
'user_phonetic.regex' => 'フリガナはカタカナでご入力ください。',
|
||
'user_gender.required' => '性別が入力されていません。',
|
||
'user_gender.in' => '性別は「男性」または「女性」を選択してください。',
|
||
'user_regident_zip_1.required' => '居住所の郵便番号(前半3桁)が入力されていません。',
|
||
'user_regident_zip_2.required' => '居住所の郵便番号(後半4桁)が入力されていません。',
|
||
'user_regident_zip_1.digits' => '居住所の郵便番号(前半3桁)は3桁の数字で入力してください。',
|
||
'user_regident_zip_2.digits' => '居住所の郵便番号(後半4桁)は4桁の数字で入力してください。',
|
||
'user_regident_pre.required' => '居住所の都道府県が選択されていません。',
|
||
'user_regident_pre.in' => '都道府県は選択肢から選んでください。',
|
||
'user_regident_city.required' => '居住所の市区町村が入力されていません。',
|
||
'user_regident_city.max' => '居住所の市区町村は20文字以内で入力してください。',
|
||
'user_regident_city.regex' => '居住所の市区町村に絵文字などの特殊文字は使用できません。',
|
||
'user_regident_add.required' => '居住所の住所が入力されていません。',
|
||
'user_regident_add.max' => '居住所の住所は50文字以内で入力してください。',
|
||
'user_regident_add.regex' => '居住所の住所に絵文字などの特殊文字は使用できません。',
|
||
'user_birthdate.required' => '生年月日が入力されていません。',
|
||
'user_birthdate.date' => '生年月日は正しい日付で入力してください。',
|
||
'user_homephone.*.digits_between' => '自宅電話番号はそれぞれ1~5桁の数字で入力してください。',
|
||
'user_mobile.*.digits_between' => '携帯電話番号はそれぞれ1~5桁の数字で入力してください。',
|
||
'user_submail.email' => '予備メールアドレスは正しい形式で入力してください。',
|
||
'user_submail.max' => '予備メールアドレスは80文字以内で入力してください。',
|
||
'user_submail.different' => 'メールアドレスと予備メールアドレスに同じアドレスを入力できません。',
|
||
'user_category.required' => '利用者区分は必須です。',
|
||
'user_category.in' => '利用者区分の値が不正です。',
|
||
'contract_reduction.required' => '減免が選択されていません。',
|
||
'contract_reduction.in' => '減免の値が不正です。',
|
||
'user_workplace.max' => '勤務先は50文字以内で入力してください。',
|
||
'user_workplace.regex' => '勤務先に絵文字などの特殊文字は使用できません。',
|
||
'user_school.required' => '学校名が入力されていません。',
|
||
'user_school.max' => '学校名は50文字以内で入力してください。',
|
||
'user_school.regex' => '学校名に絵文字などの特殊文字は使用できません。',
|
||
'user_graduate.required' => '卒業年月日が入力されていません。',
|
||
'user_graduate.date' => '卒業年月日は正しい日付で入力してください。',
|
||
'user_relate_zip_1.digits' => '住所の郵便番号(前半3桁)は3桁の数字で入力してください。',
|
||
'user_relate_zip_2.digits' => '住所の郵便番号(後半4桁)は4桁の数字で入力してください。',
|
||
'user_relate_pre.in' => '住所の都道府県は選択肢から選んでください。',
|
||
'user_relate_city.max' => '住所の市区町村は20文字以内で入力してください。',
|
||
'user_relate_city.regex' => '住所の市区町村に絵文字などの特殊文字は使用できません。',
|
||
'user_relate_add.max' => '住所は50文字以内で入力してください。',
|
||
'user_relate_add.regex' => '住所に絵文字などの特殊文字は使用できません。',
|
||
'user_securitynum.required' => '防犯登録番号が入力されていません。',
|
||
'user_securitynum.max' => '防犯登録番号は50文字以内で入力してください。',
|
||
'user_securitynum.regex' => '防犯登録番号は英数字のみで入力してください。',
|
||
];
|
||
try {
|
||
$validated = $request->validate($rules, $messages);
|
||
} catch (ValidationException $e) {
|
||
return Redirect()->back()
|
||
->withErrors($e->validator)
|
||
->withInput($request->except('_token') + ['show_terms_modal' => false]);
|
||
}
|
||
|
||
if (empty(implode('', $request->input('user_homephone', []))) && empty(implode('', $request->input('user_mobile', [])))) {
|
||
return redirect()->back()
|
||
->withErrors(['user_homephone' => '自宅電話番号または携帯電話番号のいずれかは必須です'])
|
||
->withInput($request->except('_token') + ['show_terms_modal' => false]);
|
||
}
|
||
|
||
$city = DB::table('city')->where('city_id', $park->city_id)->first();
|
||
$matched = (mb_strpos($request->user_regident_city, $city->city_name) !== false);
|
||
if ($matched === true) {
|
||
$ward_residents = 1;
|
||
DB::table('user')
|
||
->where('user_id', $user->user_id)
|
||
->update(['ward_residents' => $ward_residents]);
|
||
} else {
|
||
$contract_allowable_city_name = DB::table('contract_allowable_city')->where('city_id', $city->city_id)->value('contract_allowable_city_name');
|
||
$matched_allowable = (mb_strpos($request->user_regident_city, $contract_allowable_city_name) !== false);
|
||
if ($matched_allowable) {
|
||
$ward_residents = 0;
|
||
DB::table('user')
|
||
->where('user_id', $user->user_id)
|
||
->update(['ward_residents' => $ward_residents]);
|
||
} else {
|
||
return redirect()->back()->withErrors(['契約対象地域にお住まいでないためお申込みできません'])->withInput()->with(['show_terms_modal' => false]);
|
||
}
|
||
}
|
||
if ($ward_residents == 1) {
|
||
$usertype_subject2 = '区民';
|
||
} else {
|
||
$usertype_subject2 = '区民外';
|
||
}
|
||
|
||
$user_categoryid = DB::table('usertype')
|
||
->where('usertype_subject1', $request->user_category)
|
||
->where('usertype_subject2', $usertype_subject2)
|
||
->value('user_categoryid');
|
||
|
||
$updateData = [
|
||
'user_categoryid' => $user_categoryid,
|
||
'user_phonetic' => $request->user_phonetic,
|
||
'user_mobile' => implode('-', $request->input('user_mobile', [])),
|
||
'user_homephone' => implode('-', $request->input('user_homephone', [])),
|
||
'user_submail' => $request->filled('user_submail') ? $request->user_submail : null,
|
||
'user_regident_zip' => $request->user_regident_zip_1 . $request->user_regident_zip_2,
|
||
'user_regident_pre' => $request->user_regident_pre,
|
||
'user_regident_city' => $request->user_regident_city,
|
||
'user_regident_add' => $request->user_regident_add,
|
||
'user_relate_zip' => ($request->filled('user_relate_zip_1') && $request->filled('user_relate_zip_2')) ? ($request->user_relate_zip_1 . $request->user_relate_zip_2) : null,
|
||
'user_relate_pre' => $request->filled('user_relate_pre') ? $request->user_relate_pre : null,
|
||
'user_relate_city' => $request->filled('user_relate_city') ? $request->user_relate_city : null,
|
||
'user_relate_add' => $request->filled('user_relate_add') ? $request->user_relate_add : null,
|
||
'ward_residents' => $ward_residents,
|
||
'user_workplace' => $request->user_category === '一般' ? $request->user_workplace : null,
|
||
'user_school' => $request->user_category === '学生' ? $request->user_school : null,
|
||
'user_graduate' => $request->user_category === '学生' ? $request->user_graduate : null,
|
||
'updated_at' => now()
|
||
];
|
||
|
||
// 性別欄が表示されている場合のみ追加
|
||
if (!empty($park->gender_display_flag) && $park->gender_display_flag == 1) {
|
||
$updateData['user_gender'] = $request->user_gender;
|
||
}
|
||
|
||
// 生年月日欄が表示されている場合のみ追加
|
||
if (!empty($park->bd_display_flag) && $park->bd_display_flag == 1) {
|
||
$updateData['user_birthdate'] = $request->user_birthdate;
|
||
$updateData['user_age'] = $request->user_age;
|
||
}
|
||
|
||
DB::table('user')
|
||
->where('user_id', $user->user_id)
|
||
->update($updateData);
|
||
|
||
$zone_id = DB::table('zone')
|
||
->where('park_id', $request->park_id)
|
||
->where('ptype_id', $request->ptype_id)
|
||
->where('psection_id', $request->psection_id)
|
||
->orderBy('zone_sort', 'asc')
|
||
->value('zone_id');
|
||
|
||
$contract_id = DB::table('regular_contract')->insertGetId([
|
||
'created_at' => now(),
|
||
'updated_at' => now(),
|
||
'user_id' => $user->user_id,
|
||
'user_categoryid' => $user_categoryid,
|
||
'park_id' => $park->park_id,
|
||
'contract_created_at' => now(),
|
||
'contract_reduction' => $ward_residents,
|
||
'update_flag' => 2,
|
||
'contract_cancel_flag' => 0,
|
||
'psection_id' => $request->psection_id,
|
||
'ptype_id' => $request->ptype_id,
|
||
'zone_id' => $zone_id
|
||
]);
|
||
|
||
$contractUpdateData = [
|
||
'contract_qr_id' => DB::raw("TO_BASE64(AES_ENCRYPT($contract_id, 'LJLASR4FAS34SAADFA72ASDFALLSDRGT'))")
|
||
];
|
||
// 防犯登録番号が表示されている場合のみ追加
|
||
if (!empty($park->securityreg_display_flag) && $park->securityreg_display_flag == 1) {
|
||
$contractUpdateData['user_securitynum'] = $request->user_securitynum;
|
||
}
|
||
|
||
DB::table('regular_contract')
|
||
->where('contract_id', $contract_id)
|
||
->update($contractUpdateData);
|
||
|
||
return redirect()->route('regular_contract.upload_identity_create', [
|
||
'contract_id' => $contract_id,
|
||
]);
|
||
}
|
||
|
||
public function showUploadIdentityCreate(Request $request)
|
||
{
|
||
$user_id = session('user_id');
|
||
if (!$user_id) {
|
||
return redirect('/login');
|
||
}
|
||
$user_name = DB::table('user')->where('user_id', $user_id)->value('user_name');
|
||
$contract = DB::table('regular_contract')->where('contract_id', $request->contract_id)->first();
|
||
|
||
\Log::info('新規定期契約-本人確認書類アップロード画面にアクセス', [
|
||
'user_id' => $user_id,
|
||
]);
|
||
|
||
return view('regular_contract.upload_identity_create', [
|
||
'contract_id' => $request->contract_id,
|
||
'park_id' => $contract->park_id,
|
||
'psection_id' => $contract->psection_id,
|
||
'ptype_id' => $contract->ptype_id,
|
||
'user_name' => $user_name,
|
||
'active_menu' => 'SWC-8-1'
|
||
]);
|
||
}
|
||
public function confirmUploadIdentity(Request $request, $contract_id)
|
||
{
|
||
$user_id = session('user_id');
|
||
if (!$user_id) {
|
||
return redirect('/login');
|
||
}
|
||
|
||
$validator = Validator::make($request->all(), [
|
||
'idcard_type' => 'required',
|
||
'user_idcard' => 'required|file|mimes:jpg,jpeg,png,pdf',
|
||
], [
|
||
'idcard_type.required' => '本人確認書類の種類を選択してください。',
|
||
'user_idcard.required' => '本人確認書類のおもて画像をアップロードしてください。',
|
||
'user_idcard.file' => '本人確認書類のおもて画像はファイルで指定してください。',
|
||
'user_idcard.mimes' => 'アップロードできるファイル形式はjpg、jpeg、png、pdfのみです。',
|
||
]);
|
||
if ($validator->fails()) {
|
||
return redirect()->route('regular_contract.upload_identity_create', [
|
||
'contract_id' => $contract_id,
|
||
])
|
||
->withErrors($validator)
|
||
->withInput();
|
||
}
|
||
|
||
// おもて画像保存(Laravel Storageを使用)
|
||
$front = $request->file('user_idcard');
|
||
$filename_front = uniqid('photo1_') . '.' . $front->getClientOriginalExtension();
|
||
$front->storeAs('photo', $filename_front, 'public');
|
||
|
||
// userテーブルに保存(チェック済フラグはSHJ-1処理後に設定)
|
||
$updateData = [
|
||
'photo_filename1' => $filename_front,
|
||
'user_idcard' => $request->idcard_type,
|
||
'updated_at' => now(),
|
||
];
|
||
|
||
// ウラ画像がある場合保存し更新項目に追加
|
||
if ($request->hasFile('user_idcard2')) {
|
||
$back = $request->file('user_idcard2');
|
||
$filename_back = uniqid('photo2_') . '.' . $back->getClientOriginalExtension();
|
||
$back->storeAs('photo', $filename_back, 'public');
|
||
$updateData['photo_filename2'] = $filename_back;
|
||
}
|
||
DB::table('user')->where('user_id', $user_id)->update($updateData);
|
||
|
||
// SHJ-1 本人確認自動処理を実行
|
||
$user = DB::table('user')->where('user_id', $user_id)->first();
|
||
$park = DB::table('park')->where('park_id', $request->park_id)->first();
|
||
$psection = DB::table('psection')->where('psection_id', $request->psection_id)->first();
|
||
$usertype = DB::table('usertype')->where('user_categoryid', $user->user_categoryid)->first();
|
||
|
||
|
||
// user_idからuser_seqを取得してSHJ-1に渡す
|
||
$user_seq = $user->user_seq;
|
||
$park_id = $request->park_id;
|
||
|
||
\Log::info('SHJ-1バッチ処理開始', [
|
||
'user_id' => $user_id,
|
||
'user_seq' => $user_seq,
|
||
'park_id' => $park_id,
|
||
'contract_id' => $contract_id
|
||
]);
|
||
|
||
try {
|
||
// SHJ-1 コマンドを同期実行
|
||
$exitCode = Artisan::call('shj:one', [
|
||
'user_id' => $user_seq,
|
||
'park_id' => $park_id
|
||
]);
|
||
|
||
\Log::info('SHJ-1バッチ処理完了', [
|
||
'exit_code' => $exitCode,
|
||
'user_seq' => $user_seq,
|
||
'park_id' => $park_id
|
||
]);
|
||
|
||
// 処理結果に基づいて遷移先を決定
|
||
if ($exitCode === 0) {
|
||
// 成功の場合
|
||
return redirect("/regular-contract/upload_identity_success?contract_id={$contract_id}");
|
||
// return view('regular_contract.create_confirm', [
|
||
// 'contract_id' => $request->contract_id,
|
||
// 'user' => $user,
|
||
// 'park' => $park,
|
||
// 'psection' => $psection,
|
||
// 'usertype' => $usertype,
|
||
// 'user_name' => $user->user_name,
|
||
// 'active_menu' => 'SWC-8-1'
|
||
// ]);
|
||
} else {
|
||
// 失敗の場合 または、学生証 と その他 の場合
|
||
return redirect("/regular-contract/upload_identity_fail?contract_id={$contract_id}");
|
||
|
||
// return view('regular_contract.create_confirm', [
|
||
// 'contract_id' => $request->contract_id,
|
||
// 'user' => $user,
|
||
// 'park' => $park,
|
||
// 'psection' => $psection,
|
||
// 'usertype' => $usertype,
|
||
// 'user_name' => $user->user_name,
|
||
// 'active_menu' => 'SWC-8-1'
|
||
// ]);
|
||
|
||
}
|
||
|
||
} catch (\Exception $e) {
|
||
\Log::error('SHJ-1バッチ処理でエラー発生', [
|
||
'error' => $e->getMessage(),
|
||
'user_seq' => $user_seq,
|
||
'park_id' => $park_id
|
||
]);
|
||
|
||
return redirect("/regular-contract/upload_identity_fail?contract_id={$contract_id}");
|
||
}
|
||
}
|
||
|
||
public function createConfirmNext($contract_id)
|
||
{
|
||
$user_id = session('user_id');
|
||
if (!$user_id) {
|
||
return redirect('/login');
|
||
}
|
||
$user = DB::table('user')->where('user_id', $user_id)->first();
|
||
|
||
// 本人確認自動処理結果を取得
|
||
if ($user && $user->user_idcard_chk_flag == 2) {
|
||
// 本人確認OKの場合は利用期間選択画面へ
|
||
// 必要な各マスタ情報を取得
|
||
$contract = DB::table('regular_contract')->where('contract_id', $contract_id)->first();
|
||
$park = DB::table('park')->where('park_id', $contract->park_id)->first();
|
||
$city = DB::table('city')->where('city_id', $park->city_id)->first();
|
||
$regular_type = DB::table('regular_type')->where('city_id', $city->city_id)->first();
|
||
$usertype = DB::table('usertype')->where('user_categoryid', $contract->user_categoryid)->first();
|
||
|
||
// 2重化しているマスタのため現在のテーブル名を取得
|
||
$master_setting = DB::table('setting')->value('web_master');
|
||
$tableName = 'price' . $master_setting;
|
||
|
||
// 利用者区分に応じた逆利用フラグを取得
|
||
$inverse_use_flag_column = ($usertype->usertype_subject1 == '一般') ? 'inverse_use_flag1' : 'inverse_use_flag2';
|
||
$inverse_use_flag = $park->$inverse_use_flag_column;
|
||
|
||
if ($inverse_use_flag == 0) {
|
||
// regident_cityまたはrelate_cityが一致するか
|
||
$is_same = (
|
||
strpos($user->user_regident_city, $city->city_name) !== false ||
|
||
strpos($user->user_relate_city, $city->city_name) !== false
|
||
);
|
||
} else {
|
||
// regident_cityのみ一致するか
|
||
$is_same = (strpos($user->user_regident_city, $city->city_name) !== false);
|
||
}
|
||
|
||
$target_subject2 = $is_same ? '区民' : '区民外';
|
||
$user_categoryid = DB::table('usertype')
|
||
->where('usertype_subject1', $usertype->usertype_subject1)
|
||
->where('usertype_subject2', $target_subject2)
|
||
->where('usertype_subject3', $usertype->usertype_subject3)
|
||
->value('user_categoryid');
|
||
|
||
// 駐輪場所マスタから料金を取得
|
||
$prices = DB::table($tableName)
|
||
->where('park_id', $contract->park_id)
|
||
->where('psection_id', $contract->psection_id)
|
||
->where('ptype_id', $contract->ptype_id)
|
||
->where('user_categoryid', $user_categoryid)
|
||
->get();
|
||
|
||
\Log::info('利用期間選択画面にアクセス', [
|
||
'user_id' => $user_id,
|
||
]);
|
||
|
||
// 利用期間選択画面へ遷移
|
||
return view('regular_contract.create_select_period', [
|
||
'active_menu' => 'SWC-4-1', // マイページメニューの選択状態用
|
||
'user_name' => $user->user_name, // ユーザー名(ヘッダー用)
|
||
'contract_id' => $contract_id,
|
||
'regular_type' => $regular_type,
|
||
'prices' => $prices,
|
||
]);
|
||
} else {
|
||
// NGの場合は本人確認書類確認中画面へ
|
||
\Log::info('本人確認書類確認中画面にアクセス', [
|
||
'user_id' => $user_id,
|
||
]);
|
||
return view('regular_contract.create_idcard_checking', [
|
||
'active_menu' => 'SWC-8-1', // マイページメニューの選択状態用
|
||
'user_name' => $user->user_name, // ユーザー名(ヘッダー用)
|
||
]);
|
||
}
|
||
}
|
||
|
||
public function selectPeriod(Request $request)
|
||
{
|
||
$user_id = session('user_id');
|
||
if (!$user_id) {
|
||
return redirect('/login');
|
||
}
|
||
|
||
// 期間選択チェック
|
||
$request->validate([
|
||
'month' => 'required',
|
||
], [
|
||
'month.required' => '契約期間が選択されていません。',
|
||
]);
|
||
|
||
$contract_id = $request->input('contract_id');
|
||
$month = $request->input('month');
|
||
$price = $request->input('price_' . $month);
|
||
|
||
$today = now();
|
||
$day = $today->day;
|
||
if ($day <= 19) {
|
||
// 今月の1日
|
||
$contract_periods = $today->copy()->startOfMonth()->format('Y-m-d');
|
||
} else {
|
||
// 翌月の1日
|
||
$contract_periods = $today->copy()->addMonth()->startOfMonth()->format('Y-m-d');
|
||
}
|
||
$contract_periode = Carbon::parse($contract_periods)->addMonths($month - 1)->endOfMonth()->format('Y-m-d');
|
||
// 契約更新
|
||
DB::table('regular_contract')->where('contract_id', $contract_id)->update([
|
||
'enable_months' => $month,
|
||
'billing_amount' => $price,
|
||
'contract_periods' => $contract_periods,
|
||
'contract_periode' => $contract_periode,
|
||
'updated_at' => now(),
|
||
]);
|
||
|
||
// 完了後はウェルネット決済画面(仮)へリダイレクト
|
||
return redirect()->route('wellnet.payment');
|
||
}
|
||
|
||
/**
|
||
* SHJ-1本人確認処理成功ページ表示!!!!テスト後に削除して!!!!viewも!!!!!
|
||
*/
|
||
public function showUploadIdentitySuccess(Request $request)
|
||
{
|
||
$contractId = $request->get('contract_id');
|
||
$userId = Session::get('user_id');
|
||
|
||
// 詳細情報を取得
|
||
$debugInfo = $this->getShjDebugInfo($userId, $contractId);
|
||
|
||
return view('regular_contract.upload_identity_success', compact('debugInfo', 'contractId'));
|
||
}
|
||
|
||
/**
|
||
* SHJ-1本人確認処理失敗ページ表示!!!!テスト後に削除して!!!!viewも!!!!!
|
||
*/
|
||
public function showUploadIdentityFail(Request $request)
|
||
{
|
||
$contractId = $request->get('contract_id');
|
||
$userId = Session::get('user_id');
|
||
|
||
// 詳細情報を取得
|
||
$debugInfo = $this->getShjDebugInfo($userId, $contractId);
|
||
|
||
return view('regular_contract.upload_identity_fail', compact('debugInfo', 'contractId'));
|
||
}
|
||
|
||
/**
|
||
* SHJ-1デバッグ情報取得!!!!テスト後に削除して!!!!
|
||
*/
|
||
private function getShjDebugInfo($userId, $contractId)
|
||
{
|
||
try {
|
||
// ユーザー情報取得
|
||
$user = DB::table('user')->where('user_id', $userId)->first();
|
||
|
||
// 契約情報取得
|
||
$contract = DB::table('regular_contract')->where('user_id', $user->user_id ?? 0)->first();
|
||
|
||
// 駐輪場情報取得
|
||
$park = null;
|
||
if ($contract) {
|
||
$park = DB::table('park')->where('park_id', $contract->park_id)->first();
|
||
}
|
||
|
||
// 最新のSHJ-1バッチログ取得
|
||
$batchLog = DB::table('batch_log')
|
||
->where('process_name', 'SHJ-1本人確認自動処理')
|
||
->orderBy('created_at', 'desc')
|
||
->first();
|
||
|
||
// 最新のログファイルから詳細ログを取得
|
||
$logContent = $this->getRecentShjLogs();
|
||
|
||
// ログからAPI結果情報を解析
|
||
$apiResults = $this->parseApiResultsFromLogs($logContent);
|
||
|
||
return [
|
||
'user' => $user,
|
||
'contract' => $contract,
|
||
'park' => $park,
|
||
'batch_log' => $batchLog,
|
||
'detailed_logs' => $logContent,
|
||
'recent_logs' => $logContent, // 为调试页面提供日志内容访问
|
||
'timestamp' => now()->format('Y-m-d H:i:s'),
|
||
// API結果情報
|
||
'ocr_text_length' => $apiResults['ocr_text_length'],
|
||
'ocr_text_preview' => $apiResults['ocr_text_preview'],
|
||
'ocr_full_text' => $apiResults['ocr_full_text'],
|
||
'ocr_threshold' => $apiResults['ocr_threshold'],
|
||
'name_similarity' => $apiResults['name_similarity'],
|
||
'address_similarity' => $apiResults['address_similarity'],
|
||
'name_passed' => $apiResults['name_passed'],
|
||
'address_passed' => $apiResults['address_passed'],
|
||
'matched_address_type' => $apiResults['matched_address_type'] ?? null, // 新增:匹配成功的地址类型
|
||
'name_match_attempts' => $apiResults['name_match_attempts'],
|
||
'address_match_attempts' => $apiResults['address_match_attempts'],
|
||
'best_name_match' => $apiResults['best_name_match'],
|
||
'best_address_match' => $apiResults['best_address_match'],
|
||
'name_match_details' => $apiResults['name_match_details'],
|
||
'address_match_details' => $apiResults['address_match_details'],
|
||
'ocr_debug_info' => $apiResults['ocr_debug_info'] ?? null,
|
||
// 移除重复分析逻辑,只显示SHJ-1的结果
|
||
'calculated_distance' => $apiResults['calculated_distance'],
|
||
'distance_text' => $apiResults['distance_text'] ?? null,
|
||
'distance_limit' => $apiResults['distance_limit'] ?? null,
|
||
'distance_start_address' => $apiResults['distance_start_address'] ?? null,
|
||
'distance_end_address' => $apiResults['distance_end_address'] ?? null,
|
||
'distance_passed' => $apiResults['distance_passed'],
|
||
'maps_api_status' => $apiResults['maps_api_status'],
|
||
'maps_api_error' => $apiResults['maps_api_error']
|
||
];
|
||
|
||
} catch (Exception $e) {
|
||
\Log::error('Debug info error: ' . $e->getMessage());
|
||
return [
|
||
'error' => 'デバッグ情報の取得に失敗しました: ' . $e->getMessage(),
|
||
'timestamp' => now()->format('Y-m-d H:i:s')
|
||
];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 最新のSHJ-1関連ログを取得!!!!テスト後に削除して!!!!
|
||
*/
|
||
private function getRecentShjLogs()
|
||
{
|
||
try {
|
||
$logPath = storage_path('logs/laravel.log');
|
||
if (!file_exists($logPath)) {
|
||
return '日志文件未找到';
|
||
}
|
||
|
||
$logs = file_get_contents($logPath);
|
||
$lines = explode("\n", $logs);
|
||
|
||
// 最新の1000行からSHJ-1関連ログを抽出(さらに範囲拡大)
|
||
$recentLines = array_slice($lines, -1000);
|
||
$shjLogs = [];
|
||
|
||
foreach ($recentLines as $line) {
|
||
if (strpos($line, 'SHJ-1') !== false ||
|
||
strpos($line, 'GoogleVision') !== false ||
|
||
strpos($line, 'Google Maps') !== false ||
|
||
strpos($line, 'バッチ処理') !== false) {
|
||
$shjLogs[] = $line;
|
||
}
|
||
}
|
||
|
||
return implode("\n", array_slice($shjLogs, -200)); // 最新200行(さらに拡大)
|
||
|
||
} catch (Exception $e) {
|
||
return 'ログ取得エラー: ' . $e->getMessage();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* ログからAPI結果情報を解析!!!!テスト後に削除して!!!!
|
||
*/
|
||
private function parseApiResultsFromLogs($logContent)
|
||
{
|
||
$results = [
|
||
'ocr_text_length' => 'N/A',
|
||
'ocr_text_preview' => 'N/A',
|
||
'ocr_threshold' => 'N/A',
|
||
'name_similarity' => 'N/A',
|
||
'address_similarity' => 'N/A',
|
||
'name_passed' => false,
|
||
'address_passed' => false,
|
||
'calculated_distance' => 'N/A',
|
||
'distance_passed' => false,
|
||
'maps_api_status' => '成功',
|
||
'maps_api_error' => 'なし',
|
||
// 新增详细OCR信息
|
||
'ocr_full_text' => 'N/A',
|
||
'name_match_attempts' => [],
|
||
'address_match_attempts' => [],
|
||
'best_name_match' => 'N/A',
|
||
'best_address_match' => 'N/A',
|
||
'match_details' => 'N/A'
|
||
];
|
||
|
||
try {
|
||
// OCR処理完了ログから文字数とプレビューを抽取(清理格式)
|
||
if (preg_match('/GoogleVision OCR処理完了.*"text_length":(\d+).*"text_preview":"([^"]*)"/', $logContent, $matches)) {
|
||
$results['ocr_text_length'] = $matches[1];
|
||
// 清理OCR文本,移除可能的日志污染
|
||
$cleanText = $matches[2];
|
||
// 移除可能混入的日志时间戳和标记
|
||
$cleanText = preg_replace('/\[\d{4}-\d{2}-\d{2}.*?\].*?local\.INFO.*?$/', '', $cleanText);
|
||
// 移除末尾的不完整JSON片段
|
||
$cleanText = preg_replace('/\s*\{?\s*$/', '', $cleanText);
|
||
// 清理换行和多余空格
|
||
$cleanText = trim($cleanText);
|
||
$results['ocr_text_preview'] = $cleanText;
|
||
}
|
||
|
||
// 尝试从GoogleVision日志提取完整的OCR文本
|
||
if (preg_match('/GoogleVision OCR処理完了.*"text_full":"([^"]*)"/', $logContent, $matches)) {
|
||
$fullText = $matches[1];
|
||
// 解码JSON转义字符
|
||
$fullText = str_replace('\n', "\n", $fullText);
|
||
$fullText = str_replace('\r', "\r", $fullText);
|
||
$fullText = str_replace('\"', '"', $fullText);
|
||
$fullText = str_replace('\\\\', '\\', $fullText);
|
||
$results['ocr_full_text'] = $fullText;
|
||
}
|
||
|
||
// 尝试从SHJ-1日志提取完整的OCR文本(多种模式匹配)
|
||
$patterns = [
|
||
'/SHJ-1 完全OCR認識結果.*"ocr_full_text":"([^"]*)"/', // 原始日文版本
|
||
'/螳悟・OCR隱崎ュ倡オ先棡.*"ocr_full_text":"([^"]*)"/', // 日文编码版本
|
||
'/OCR認識結果.*"ocr_full_text":"([^"]*)"/', // 简化匹配
|
||
'/"ocr_full_text":"([^"]*)"/', // 最宽泛匹配
|
||
];
|
||
|
||
foreach ($patterns as $pattern) {
|
||
if (empty($results['ocr_full_text']) && preg_match($pattern, $logContent, $matches)) {
|
||
$encodedText = $matches[1];
|
||
// Base64解码
|
||
$fullText = base64_decode($encodedText);
|
||
if ($fullText !== false && strlen($fullText) > 10) {
|
||
$results['ocr_full_text'] = $fullText;
|
||
|
||
// 简化OCR信息显示 - 只显示基本信息
|
||
$results['ocr_debug_info'] = [
|
||
'decoded_length' => strlen($fullText),
|
||
'preview' => substr($fullText, 0, 200)
|
||
];
|
||
break; // 找到后停止尝试其他模式
|
||
}
|
||
}
|
||
}
|
||
|
||
// 尝试从OCR认识内容详细日志提取(备选方案,适配日文编码)
|
||
if (empty($results['ocr_full_text']) && preg_match('/OCR隱崎ュ伜・螳ケ隧ウ邏ー.*"raw_text":"OCR_TEXT_START>([^<]*)<OCR_TEXT_END"/', $logContent, $matches)) {
|
||
$results['ocr_full_text'] = $matches[1];
|
||
}
|
||
|
||
// 提取氏名匹配详细信息(适配日文编码)
|
||
$nameMatchDetails = [];
|
||
if (preg_match_all('/豌丞錐繝槭ャ繝√Φ繧ー隧ウ邏ー.*"target_name":"([^"]*)".*"similarity_score":([^,}]*).*"ocr_contains_name":"([^"]*)"/', $logContent, $nameMatches, PREG_SET_ORDER)) {
|
||
foreach ($nameMatches as $match) {
|
||
$nameMatchDetails[] = [
|
||
'target' => $match[1],
|
||
'score' => round(floatval($match[2]), 2),
|
||
'found_in_ocr' => $match[3]
|
||
];
|
||
}
|
||
}
|
||
$results['name_match_details'] = $nameMatchDetails;
|
||
|
||
// 提取住所匹配详细信息(适配日文编码)
|
||
$addressMatchDetails = [];
|
||
if (preg_match_all('/菴乗園繝槭ャ繝√Φ繧ー隧ウ邏ー.*"target_address":"([^"]*)".*"address_type":"([^"]*)".*"similarity_score":([^,}]*).*"ocr_contains_address":"([^"]*)"/', $logContent, $addressMatches, PREG_SET_ORDER)) {
|
||
foreach ($addressMatches as $match) {
|
||
$addressMatchDetails[] = [
|
||
'target' => $match[1],
|
||
'type' => $match[2],
|
||
'score' => round(floatval($match[3]), 2),
|
||
'found_in_ocr' => $match[4]
|
||
];
|
||
}
|
||
}
|
||
$results['address_match_details'] = $addressMatchDetails;
|
||
|
||
// 从新的SHJ-1日志格式中提取信息
|
||
|
||
// 提取OCR抽出结果
|
||
if (preg_match('/SHJ-1 OCR抽出成功.*"extracted_name":"([^"]*)".*"extracted_address":"([^"]*)".*"ocr_value":"([^"]*)"/', $logContent, $matches)) {
|
||
$results['extracted_name'] = $matches[1];
|
||
$results['extracted_address'] = $matches[2];
|
||
$results['extracted_ocr_value'] = $matches[3];
|
||
}
|
||
|
||
// 提取居住住所照合结果
|
||
if (preg_match('/SHJ-1 居住住所照合.*"resident_address":"([^"]*)".*"similarity":([^,}]*)/', $logContent, $matches)) {
|
||
$results['resident_address'] = $matches[1];
|
||
$results['resident_similarity'] = round(floatval($matches[2]), 2);
|
||
}
|
||
|
||
// 提取関連住所照合结果
|
||
if (preg_match('/SHJ-1 関連住所照合.*"related_address":"([^"]*)".*"similarity":([^,}]*)/', $logContent, $matches)) {
|
||
$results['related_address'] = $matches[1];
|
||
$results['related_similarity'] = round(floatval($matches[2]), 2);
|
||
}
|
||
|
||
// 提取最終OCR結果
|
||
if (preg_match('/SHJ-1 OCR照合(成功|失敗)/', $logContent, $matches)) {
|
||
$results['final_ocr_result'] = $matches[1];
|
||
$results['address_passed'] = ($matches[1] === '成功');
|
||
$results['name_passed'] = ($matches[1] === '成功'); // 新SHJ-1逻辑中,成功表示整体成功
|
||
}
|
||
|
||
// 如果OCR文本为空或太短,提供说明
|
||
if (empty($results['ocr_text_preview']) || strlen($results['ocr_text_preview']) < 5) {
|
||
$results['ocr_text_preview'] = '(OCR認識内容が短いか、表示できない文字が含まれています)';
|
||
}
|
||
|
||
// 表面画像処理完了の詳細結果を抽取
|
||
if (preg_match('/SHJ-1 表面画像処理完了.*"front_result":\{"name_matches":\[([^\]]*)\],"address_matches":\[([^\]]*)\]\}/', $logContent, $matches)) {
|
||
$nameMatches = explode(',', $matches[1]);
|
||
$addressMatches = explode(',', $matches[2]);
|
||
|
||
$results['name_match_attempts'] = array_map(function($val) {
|
||
return round(floatval($val), 2);
|
||
}, $nameMatches);
|
||
|
||
$results['address_match_attempts'] = array_map(function($val) {
|
||
return round(floatval($val), 2);
|
||
}, $addressMatches);
|
||
}
|
||
|
||
// OCR類似度計算結果の詳細情報を抽取
|
||
if (preg_match('/SHJ-1 OCR類似度計算結果.*"best_name_match":([^,}]*).*"best_address_match":([^,}]*).*"all_name_matches":\[([^\]]*)\].*"all_address_matches":\[([^\]]*)\]/', $logContent, $matches)) {
|
||
$results['best_name_match'] = round(floatval($matches[1]), 2);
|
||
$results['best_address_match'] = round(floatval($matches[2]), 2);
|
||
|
||
$allNameMatches = explode(',', $matches[3]);
|
||
$allAddressMatches = explode(',', $matches[4]);
|
||
|
||
$results['name_match_attempts'] = array_map(function($val) {
|
||
return round(floatval($val), 2);
|
||
}, $allNameMatches);
|
||
|
||
$results['address_match_attempts'] = array_map(function($val) {
|
||
return round(floatval($val), 2);
|
||
}, $allAddressMatches);
|
||
}
|
||
|
||
// OCR閾値チェックログから類似度情報を抽出(新しい順序匹配対応)
|
||
if (preg_match('/SHJ-1 OCR閾値チェック.*"threshold":"?([^",}]*)"?.*"name_match":([^,}]*).*"address_match":([^,}]*).*"name_passed":([^,}]*).*"address_passed":([^,}]*).*"matched_address_type":"?([^",}]*)"?/', $logContent, $matches)) {
|
||
$results['ocr_threshold'] = $matches[1];
|
||
$results['name_similarity'] = round(floatval($matches[2]), 2);
|
||
$results['address_similarity'] = round(floatval($matches[3]), 2);
|
||
$results['name_passed'] = $matches[4] === 'true';
|
||
$results['address_passed'] = $matches[5] === 'true';
|
||
$results['matched_address_type'] = $matches[6] ?: null;
|
||
}
|
||
|
||
// Google Maps API エラーチェック
|
||
if (strpos($logContent, 'Google Maps distance calculation error') !== false) {
|
||
$results['maps_api_status'] = 'エラー';
|
||
if (preg_match('/Google Maps distance calculation error.*"error":"([^"]*)"/', $logContent, $matches)) {
|
||
$results['maps_api_error'] = $matches[1];
|
||
}
|
||
} else if (strpos($logContent, 'Distance calculation failed') !== false) {
|
||
$results['maps_api_status'] = 'アドレス未発見';
|
||
$results['maps_api_error'] = 'NOT_FOUND';
|
||
}
|
||
|
||
// 距離計算結果を抽取(最新の詳細ログから)
|
||
if (preg_match_all('/SHJ-1 距離計算完了.*"calculated_distance_meters":(\d+).*"distance_text":"([^"]*)".*"limit_meters":"?([^",}]*)"?.*"within_limit":([^,}]*)/', $logContent, $allMatches, PREG_SET_ORDER)) {
|
||
// 最後の(最新の)マッチを使用
|
||
$matches = end($allMatches);
|
||
$results['calculated_distance'] = $matches[1]; // 距離メートル
|
||
$results['distance_text'] = $matches[2]; // Google Mapsテキスト
|
||
$results['distance_limit'] = $matches[3]; // 制限値
|
||
$results['distance_passed'] = $matches[4] === 'true';
|
||
} else {
|
||
// ログから具体的な結果が取得できない場合はデフォルト値を設定
|
||
// ※重要:API成功≠距離制限内ではないため、明示的にfalseにする
|
||
$results['distance_passed'] = false;
|
||
if (strpos($logContent, 'Distance check error') === false &&
|
||
strpos($logContent, 'Google Maps distance calculation error') === false) {
|
||
$results['calculated_distance'] = '計算成功(制限値詳細はログで確認)';
|
||
$results['maps_api_status'] = '成功(但制限確認要)';
|
||
} else {
|
||
$results['calculated_distance'] = '計算失敗';
|
||
$results['maps_api_status'] = 'エラー';
|
||
}
|
||
}
|
||
|
||
// 距離計算開始ログから起点・終点住所を抽取
|
||
if (preg_match('/SHJ-1 距離計算開始.*"user_address":"([^"]*)".*"park_address":"([^"]*)"/', $logContent, $matches)) {
|
||
$results['distance_start_address'] = $matches[1];
|
||
$results['distance_end_address'] = $matches[2];
|
||
}
|
||
|
||
} catch (Exception $e) {
|
||
\Log::error('API結果解析エラー: ' . $e->getMessage());
|
||
}
|
||
|
||
return $results;
|
||
}
|
||
|
||
// 不再需要的分析方法已移除 - 只显示SHJ-1的结果
|
||
|
||
/**
|
||
* 废弃的方法(已不再使用)
|
||
*/
|
||
private function manualOcrAnalysis($logContent, $user = null)
|
||
{
|
||
$analysis = [
|
||
'found_base64' => false,
|
||
'decoded_success' => false,
|
||
'decoded_text' => '',
|
||
'contains_yamada' => false,
|
||
'contains_taro' => false,
|
||
'contains_tokyo' => false,
|
||
'contains_osaka' => false,
|
||
'full_analysis' => 'Analysis failed',
|
||
'corrected_matching' => null
|
||
];
|
||
|
||
try {
|
||
// 分割日志内容为行数组,按时间倒序搜索最新的OCR结果
|
||
$logLines = explode("\n", $logContent);
|
||
$logLines = array_reverse($logLines); // 从最新的开始搜索
|
||
|
||
// 查找最新的Base64编码OCR结果
|
||
$patterns = [
|
||
'/SHJ-1 完全OCR認識結果.*"ocr_full_text":"([^"]*)"/',
|
||
'/SHJ-1.*OCR.*結果.*"ocr_full_text":"([^"]*)"/',
|
||
'/"ocr_full_text":"([^"]*)"/'
|
||
];
|
||
|
||
foreach ($logLines as $line) {
|
||
foreach ($patterns as $pattern) {
|
||
if (preg_match($pattern, $line, $matches)) {
|
||
$analysis['found_base64'] = true;
|
||
$base64Text = $matches[1];
|
||
|
||
// Base64解码
|
||
$decodedText = base64_decode($base64Text);
|
||
if ($decodedText !== false && strlen($decodedText) > 10) {
|
||
$analysis['decoded_success'] = true;
|
||
$analysis['decoded_text'] = $decodedText;
|
||
|
||
// 内容分析
|
||
$analysis['contains_yamada'] = strpos($decodedText, '山田') !== false;
|
||
$analysis['contains_taro'] = strpos($decodedText, '太郎') !== false;
|
||
$analysis['contains_tokyo'] = strpos($decodedText, '東京') !== false;
|
||
$analysis['contains_osaka'] = strpos($decodedText, '大阪') !== false;
|
||
|
||
// ユーザー提案:空白と改行を除去して比較
|
||
$analysis['corrected_matching'] = $this->performCorrectedMatching($decodedText, $user);
|
||
|
||
// 詳細分析(実際のユーザーデータから期待値を取得)
|
||
if ($user) {
|
||
$expectedName = $user->user_name ?? "";
|
||
$expectedAddress = ($user->user_regident_pre ?? '') .
|
||
($user->user_regident_city ?? '') .
|
||
($user->user_regident_add ?? '') ?: "";
|
||
} else {
|
||
$expectedName = "";
|
||
$expectedAddress = "";
|
||
}
|
||
|
||
$analysis['full_analysis'] =
|
||
"OCR認識テキスト長: " . strlen($decodedText) . "文字\n" .
|
||
"期待氏名: '$expectedName'\n" .
|
||
"期待住所: '$expectedAddress'\n" .
|
||
"山田を含む: " . ($analysis['contains_yamada'] ? 'YES' : 'NO') . "\n" .
|
||
"太郎を含む: " . ($analysis['contains_taro'] ? 'YES' : 'NO') . "\n" .
|
||
"東京を含む: " . ($analysis['contains_tokyo'] ? 'YES' : 'NO') . "\n" .
|
||
"大阪を含む: " . ($analysis['contains_osaka'] ? 'YES' : 'NO') . "\n" .
|
||
"認識完全内容: " . substr($decodedText, 0, 200) . "...";
|
||
|
||
// 成功解码最新OCR结果后立即返回
|
||
return $analysis;
|
||
}
|
||
// 解码失败时,继续搜索其他条目
|
||
}
|
||
}
|
||
}
|
||
|
||
} catch (Exception $e) {
|
||
$analysis['full_analysis'] = 'OCR分析エラー: ' . $e->getMessage();
|
||
}
|
||
|
||
return $analysis;
|
||
}
|
||
|
||
/**
|
||
* 修正マッチングアルゴリズム実行(空白・改行除去)
|
||
*/
|
||
private function performCorrectedMatching($ocrText, $user = null)
|
||
{
|
||
// ユーザー情報から期待値を取得、デフォルトはテスト用
|
||
if ($user) {
|
||
$expectedName = $user->user_name ?? "";
|
||
$expectedAddress = ($user->user_regident_pre ?? '') .
|
||
($user->user_regident_city ?? '') .
|
||
($user->user_regident_add ?? '') ?: "";
|
||
} else {
|
||
$expectedName = "";
|
||
$expectedAddress = "";
|
||
}
|
||
|
||
// 統一的テキスト正規化関数
|
||
$normalize = function($text) {
|
||
// 全空白文字と改行を除去(全角スペース含む)
|
||
$text = preg_replace('/[\s\x{3000}]+/u', '', $text); // \x{3000}は全角スペース
|
||
// 一般的な空白文字を明示的に除去
|
||
$text = str_replace([' ', ' ', "\t", "\n", "\r"], '', $text);
|
||
// 全角→半角変換
|
||
$text = mb_convert_kana($text, 'rnask', 'UTF-8');
|
||
// 住所統一
|
||
$text = str_replace(['東京市', '東京府'], '東京都', $text);
|
||
$text = str_replace(['の'], '', $text);
|
||
// 数字統一
|
||
$text = str_replace(['1','2','3','4','5','6','7','8','9','0'],
|
||
['1','2','3','4','5','6','7','8','9','0'], $text);
|
||
return $text;
|
||
};
|
||
|
||
// 正規化処理
|
||
$normalizedOcr = $normalize($ocrText);
|
||
$normalizedExpectedName = $normalize($expectedName);
|
||
$normalizedExpectedAddr = $normalize($expectedAddress);
|
||
|
||
// 使用"住所"分割OCR文本
|
||
$addressKeyword = '住所';
|
||
$ocrParts = explode($addressKeyword, $normalizedOcr, 2);
|
||
|
||
$personalInfoSection = $ocrParts[0] ?? ''; // "住所"前の個人情報欄
|
||
$addressSection = isset($ocrParts[1]) ? $addressKeyword . $ocrParts[1] : ''; // "住所"後の住所欄
|
||
|
||
// 分区マッチング計算
|
||
$nameMatch = $this->calculateSimpleMatch($normalizedExpectedName, $personalInfoSection);
|
||
$addrMatch = $this->calculateSimpleMatch($normalizedExpectedAddr, $addressSection);
|
||
|
||
return [
|
||
'original_ocr' => substr($ocrText, 0, 100) . '...',
|
||
'normalized_ocr' => substr($normalizedOcr, 0, 100) . '...',
|
||
'normalized_expected_name' => $normalizedExpectedName,
|
||
'normalized_expected_addr' => $normalizedExpectedAddr,
|
||
'personal_info_section' => substr($personalInfoSection, 0, 80) . '...',
|
||
'address_section' => substr($addressSection, 0, 80) . '...',
|
||
'name_match_score' => $nameMatch,
|
||
'addr_match_score' => $addrMatch,
|
||
'name_passed' => $nameMatch >= 70,
|
||
'addr_passed' => $addrMatch >= 70,
|
||
'overall_result' => ($nameMatch >= 70 && $addrMatch >= 70) ? 'PASS' : 'FAIL'
|
||
];
|
||
}
|
||
|
||
/**
|
||
* シンプルマッチング計算
|
||
*/
|
||
private function calculateSimpleMatch($expected, $haystack)
|
||
{
|
||
if (empty($expected)) return 0;
|
||
|
||
// 1. 完全包含チェック
|
||
if (strpos($haystack, $expected) !== false) {
|
||
return 100;
|
||
}
|
||
|
||
// 2. 文字包含率
|
||
$expectedChars = mb_str_split($expected, 1, 'UTF-8');
|
||
$foundChars = 0;
|
||
|
||
foreach ($expectedChars as $char) {
|
||
if (mb_strpos($haystack, $char, 0, 'UTF-8') !== false) {
|
||
$foundChars++;
|
||
}
|
||
}
|
||
|
||
$charRate = ($foundChars / count($expectedChars)) * 100;
|
||
|
||
// 3. 類似度計算
|
||
similar_text($expected, $haystack, $similarRate);
|
||
|
||
// 最高スコアを返す
|
||
return max($charRate, $similarRate);
|
||
}
|
||
}
|