You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

400 lines
16 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace App\Http\Controllers;
use App\Models\Admin;
use App\Models\Visit;
use App\Models\VisitLog;
use App\Models\GateLog;
use App\Models\Upload;
use App\Helpers\ApiResponse;
use App\Helpers\ResponseCode;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Carbon\Carbon;
use Illuminate\Support\Facades\Validator;
/**
* @OA\Tag(
* name="门岗端接口",
* description="门岗端相关接口,无需鉴权"
* )
*/
class GateController extends Controller
{
use ApiResponse;
/**
* @OA\Get(
* path="/api/admin/gate/user-list",
* tags={"门岗端接口"},
* summary="门岗用户列表",
* description="",
* @OA\Response(
* response="200",
* description="暂无"
* )
* )
*/
public function uerList()
{
$admin = Admin::whereHas('role', function ($query) {
$query->where('name', 'like', '%门岗%');
})->get();
return $this->success($admin);
}
/**
* @OA\Get(
* path="/api/gate/visits",
* tags={"门岗端接口"},
* summary="拜访信息列表",
* description="获取门岗端拜访信息列表",
* @OA\Parameter(name="start_date", in="query", @OA\Schema(type="string"), required=false, description="查询日期格式Y-m-d开始日期"),
* @OA\Parameter(name="end_date", in="query", @OA\Schema(type="string"), required=false, description="查询日期格式Y-m-d结束日期"),
* @OA\Parameter(name="status", in="query", @OA\Schema(type="string"), required=false, description="状态筛选1通过(待进厂)3已进厂4已离厂"),
* @OA\Parameter(name="type", in="query", @OA\Schema(type="string"), required=false, description="类型筛选1访客2访客车辆3物流车辆"),
* @OA\Parameter(name="keyword", in="query", @OA\Schema(type="string"), required=false, description="关键词搜索(人牌,身份证,核销码)"),
* @OA\Parameter(name="page", in="query", @OA\Schema(type="integer"), required=false, description="页码默认1"),
* @OA\Parameter(name="page_size", in="query", @OA\Schema(type="integer"), required=false, description="每页数量默认20"),
* @OA\Response(
* response="200",
* description="成功获取拜访信息列表",
* @OA\JsonContent(
* @OA\Property(property="code", type="integer", example=200),
* @OA\Property(property="message", type="string", example="success"),
* @OA\Property(property="data", type="object",
* @OA\Property(property="current_page", type="integer"),
* @OA\Property(property="data", type="array", @OA\Items(type="object")),
* @OA\Property(property="total", type="integer")
* )
* )
* )
* )
*/
public function visits(Request $request)
{
try {
$startDate = $request->input('start_date', date('Y-m-d'));
$endDate = $request->input('end_date', date('Y-m-d'));
$status = $request->input('status');
$type = $request->input('type');
$keyword = $request->input('keyword');
$page = $request->input('page', 1);
$pageSize = $request->input('page_size', 20);
$query = Visit::with([
'visitArea',
'visitTime',
'acceptDepartment',
'acceptAdmin',
'visitAudits',
'acceptAdminSign'
])->whereBetween('date', [$startDate, $endDate]);
// 状态筛选
if ($status) {
$query->where('audit_status', $status);
} else {
// 默认显示待进厂和已进厂的记录
$query->whereIn('audit_status', [Visit::AUDIT_STATUS_APPROVED, Visit::AUDIT_STATUS_ENTERED, Visit::AUDIT_STATUS_LEFT]);
}
// 类型筛选
if ($type) {
$query->where('type', $type);
}
// 关键词搜索
if ($keyword) {
$query->where(function ($q) use ($keyword) {
$q->where('person_no', "{$keyword}")
->orWhere('idcard', "{$keyword}")
->orWhere('code', "{$keyword}");
});
}
$visits = $query->orderBy('created_at', 'desc')
->paginate($pageSize, ['*'], 'page', $page);
return $this->success($visits);
} catch (\Exception $e) {
Log::error('门岗端获取拜访列表失败:' . $e->getMessage());
return $this->fail([ResponseCode::ERROR_INSIDE, __('gate.get_visit_list_failed')]);
}
}
/**
* @OA\Post(
* path="/api/admin/gate/visits/detail",
* tags={"门岗端接口"},
* summary="拜访详情信息",
* description="获取指定拜访的详细信息",
* @OA\RequestBody(
* required=true,
* @OA\JsonContent(
* @OA\Property(property="id", type="integer", description="拜访ID")
* )
* ),
* @OA\Response(
* response="200",
* description="成功获取拜访详情",
* @OA\JsonContent(
* @OA\Property(property="code", type="integer", example=200),
* @OA\Property(property="message", type="string", example="success"),
* @OA\Property(property="data", type="object")
* )
* )
* )
*/
public function visitDetail(Request $request)
{
try {
$id = $request->input('id');
if (!$id) {
return $this->fail([ResponseCode::ERROR_PARAMETER, __('gate.visit_id_required')]);
}
$visit = Visit::with([
'visitArea',
'visitTime',
'acceptDepartment',
'acceptAdmin',
'acceptGoodsAdmin',
'accompany',
'visitAudits',
'logs',
'acceptAdminSign'
])->find($id);
if (!$visit) {
return $this->fail([ResponseCode::ERROR_BUSINESS, __('gate.visit_not_found')]);
}
return $this->success($visit);
} catch (\Exception $e) {
Log::error('门岗端获取拜访详情失败:' . $e->getMessage());
return $this->fail([ResponseCode::ERROR_INSIDE, __('gate.get_visit_detail_failed')]);
}
}
/**
* @OA\Post(
* path="/api/admin/gate/visits/update",
* tags={"门岗端接口"},
* summary="更新拜访信息",
* description="门岗端更新拜访信息按字段进行更新绑定ID卡、修改进/离厂状态、上传货车图片等)",
* @OA\RequestBody(
* required=true,
* @OA\JsonContent(
* @OA\Property(property="id", type="integer", description="拜访ID"),
* @OA\Property(property="person_no", type="string", description="人牌号(提供则更新并记录绑定日志)"),
* @OA\Property(property="car_no", type="string", description="停车牌号(提供则更新)"),
* @OA\Property(property="audit_status", type="integer", enum={3,4}, description="审核状态3进厂(需当前为已通过)4离厂(需当前为已进厂)"),
* @OA\Property(property="vehicle_images", type="array", description="货车图片ID数组仅物流车辆可提供需为有效上传ID", @OA\Items(type="integer")),
* @OA\Property(property="remark", type="string", description="备注信息(用于进/离厂日志可选附加)")
* )
* ),
* @OA\Response(
* response="200",
* description="更新成功",
* @OA\JsonContent(
* @OA\Property(property="code", type="integer", example=200),
* @OA\Property(property="message", type="string", example="操作成功"),
* @OA\Property(property="data", type="object")
* )
* )
* )
*/
public function updateVisit(Request $request)
{
try {
$id = $request->input('id');
if (!$id) {
return $this->fail([ResponseCode::ERROR_PARAMETER, __('gate.visit_id_required')]);
}
$visit = Visit::find($id);
if (!$visit) {
return $this->fail([ResponseCode::ERROR_BUSINESS, __('gate.visit_not_found')]);
}
$remark = $request->input('remark', '');
$personNo = $request->input('person_no');
$carNo = $request->input('car_no');
$vehicleImages = $request->input('vehicle_images'); // null 表示未尝试更新
$newAuditStatus = $request->input('audit_status'); // 可选3进厂4离厂
DB::beginTransaction();
$hasAnyChange = false;
// 绑定/更新人牌、车牌
if (!is_null($personNo) || !is_null($carNo)) {
if (!is_null($personNo)) {
$visit->person_no = $personNo;
}
if (!is_null($carNo)) {
$visit->car_no = $carNo;
}
$visit->save();
$hasAnyChange = true;
$this->createVisitLog(
$visit,
VisitLog::TYPE_ENTER,
__('gate.bind_card_log', [
'person_no' => $visit->person_no ?: '',
'car_no' => $visit->car_no ?: ''
])
);
}
// 审核状态变更:支持进厂(Approved -> Entered) 与 离厂(Entered -> Left)
if (!is_null($newAuditStatus)) {
if ((int) $newAuditStatus === Visit::AUDIT_STATUS_ENTERED) {
if ($visit->audit_status != Visit::AUDIT_STATUS_APPROVED) {
return $this->fail([ResponseCode::ERROR_BUSINESS, __('gate.only_approved_visits_can_enter')]);
}
$visit->audit_status = Visit::AUDIT_STATUS_ENTERED;
$visit->save();
$hasAnyChange = true;
$this->createVisitLog($visit, VisitLog::TYPE_ENTER, __('gate.enter_log') . ($remark ? '' . $remark : ''));
$this->createGateLog($visit, 'enter', $remark);
} elseif ((int) $newAuditStatus === Visit::AUDIT_STATUS_LEFT) {
if ($visit->audit_status != Visit::AUDIT_STATUS_ENTERED) {
return $this->fail([ResponseCode::ERROR_BUSINESS, __('gate.only_entered_visitors_can_leave')]);
}
$visit->audit_status = Visit::AUDIT_STATUS_LEFT;
$visit->save();
$hasAnyChange = true;
$this->createVisitLog($visit, VisitLog::TYPE_LEAVE, __('gate.leave_log') . ($remark ? '' . $remark : ''));
$this->createGateLog($visit, 'leave', $remark);
}
}
// 货车图片更新(仅物流车辆)
if (!is_null($vehicleImages)) {
if ($visit->type != Visit::TYPE_LOGISTICS_CAR) {
return $this->fail([ResponseCode::ERROR_BUSINESS, __('gate.only_logistics_cars_can_upload_images')]);
}
if (!is_array($vehicleImages) || empty($vehicleImages)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, __('gate.vehicle_images_required')]);
}
$visit->vehicle_images = $vehicleImages;
$visit->save();
$hasAnyChange = true;
$this->createVisitLog($visit, VisitLog::TYPE_ENTER, __('gate.upload_vehicle_images_log', ['count' => count($vehicleImages)]));
}
if (!$hasAnyChange) {
DB::rollBack();
return $this->fail([ResponseCode::ERROR_PARAMETER, __('gate.invalid_action_type')]);
}
DB::commit();
return $this->success($visit);
} catch (\Exception $e) {
DB::rollBack();
Log::error('门岗端更新拜访信息失败:' . $e->getMessage());
return $this->fail([ResponseCode::ERROR_INSIDE, __('gate.operation_failed') . '' . $e->getMessage()]);
}
}
/**
* 创建拜访日志
*/
private function createVisitLog($visit, $type, $remark = '')
{
VisitLog::log($visit->id, $type, $remark);
}
/**
* 创建门岗日志
*/
private function createGateLog($visit, $action, $remark = '')
{
$personNo = $visit->person_no ? [$visit->person_no] : [];
$carNo = $visit->car_no ? [$visit->car_no] : [];
GateLog::create([
'code' => $visit->code,
'remark' => $remark,
'person_no' => $personNo,
'car_no' => $carNo,
'created_at' => now(),
'updated_at' => now(),
]);
}
/**
* @OA\Get(
* path="/api/admin/gate/visits/use-code",
* tags={"门岗端接口"},
* summary="核销",
* description="",
* @OA\Parameter(name="admin_id", in="query", @OA\Schema(type="string"), required=false, description="管理员id"),
* @OA\Parameter(name="keyword", in="query", @OA\Schema(type="string"), required=false, description="关键词(人牌,身份证,核销码)"),
* @OA\Parameter(name="type", in="query", @OA\Schema(type="string"), required=false, description="1进厂2离厂"),
* @OA\Response(
* response="200",
* description="暂无"
* )
* )
*/
public function useCode()
{
$all = \request()->all();
$messages = [
'keyword.required' => __('gate.validation.keyword_required'),
'type.required' => __('gate.validation.type_required'),
'admin_id.required' => __('gate.validation.admin_id_required')
];
$validator = Validator::make($all, [
'keyword' => 'required',
'type' => 'required',
'admin_id' => 'required'
], $messages);
if ($validator->fails()) {
return $this->fail([ResponseCode::ERROR_PARAMETER, implode(',', $validator->errors()->all())]);
}
$code = \request('code');
$idcard = \request('idcard');
if (empty($idcard) && empty($code)) {
return $this->fail([ResponseCode::ERROR_PARAMETER, __('gate.validation.code_or_idcard_required')]);
}
$check = Visit::where(function ($query) use ($all) {
$query->where('person_no', "{$all['keyword']}")
->orWhere('idcard', "{$all['keyword']}")
->orWhere('code', "{$all['keyword']}");
})->first();
if (empty($check)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, __('gate.business.visit_not_found')]);
}
$remark = __('gate.operation.enter_factory');
if ($all['type'] == 2) {
$remark = __('gate.operation.leave_factory');
}
$gateLog = GateLog::add($all['admin_id'], $all['code'], $all['person_no'] ?? [], $all['car_no'] ?? [], $remark);
if ($all['type'] == 1) {
// 入场
Visit::where('code', $all['code'])->update(['audit_status' => 3, 'person_no' => $all['person_no'] ?? '', 'car_no' => $all['car_no'] ?? '']);
}
if ($all['type'] == 2) {
Visit::where('code', $all['code'])->update(['audit_status' => 4]);
}
return $this->success($gateLog);
}
}