store-manage/app/Admin/Services/EmployeeSignService.php

167 lines
5.5 KiB
PHP

<?php
namespace App\Admin\Services;
use App\Admin\Filters\EmployeeSignFilter;
use App\Enums\SignStatus;
use App\Enums\SignTime;
use App\Enums\SignType;
use App\Models\Employee;
use App\Models\EmployeeRest;
use App\Models\EmployeeSign;
use App\Models\EmployeeSignLog;
use Carbon\Carbon;
class EmployeeSignService extends BaseService
{
protected array $withRelationships = ['employee', 'store'];
protected string $modelName = EmployeeSign::class;
protected string $modelFilterName = EmployeeSignFilter::class;
/**
* 整理昨天的打卡流水, 生成对应的打卡记录
*/
public function signResult()
{
$date = now()->subDay();
$start = $date->copy()->startOfDay();
$end = $date->copy()->endOfDay();
// 打卡日志
$list = EmployeeSignLog::whereBetween('time', [$start, $end])->get();
// 休息的员工
$restEmployeeIds = EmployeeRest::whereBetWeen('date', [$start, $end])->pluck('employee_id');
// 需要打卡的员工
$employees = Employee::where('store_id', '>', 0)->whereNotIn('id', $restEmployeeIds)->get();
foreach ($employees as $employee) {
$logs = $list->where('employee_id', $employee->id);
// 状态: 两个打卡=正常, 一次打卡 = 缺卡, 两次未打=旷工
$status = 0;
// 外勤打卡-事由
$remarks = null;
// 上班打卡
$firstTime = null;
if ($item = $logs->where('sign_time', SignTime::Morning)->sortBy('time')->first()) {
$firstTime = $item->time;
$status++;
if ($item->sign_type == SignType::Outside) {
$remarks = $item->remarks;
}
}
// 下班打卡
$lastTime = null;
if ($item = $logs->where('sign_time', SignTime::Afternoon)->sortByDesc('time')->first()) {
$lastTime = $item->time;
$status++;
if ($item->sign_type == SignType::Outside) {
$remarks = $item->remarks;
}
}
// 打卡类型
$type = SignType::None;
if ($status > 0) {
$type = $logs->where('sign_type', SignType::Outside)->count() > 0 ? SignType::Outside : SignType::Normal;
}
$attributes = [
'sign_type' => $type,
'first_time' => $firstTime,
'last_time' => $lastTime,
'sign_status' => match ($status) {
0 => SignStatus::Absent,
1 => SignStatus::Lose,
2 => SignStatus::Normal,
},
'remarks' => $remarks,
];
$employee->signs()->updateOrCreate([
'date' => $date,
'store_id' => $employee->store_id,
], $attributes);
}
}
/**
* 打卡
*
* @param Employee $user 用户
* @param SignTime $time 上班/下班 打卡
* @param mixed $date 打卡时间
* @param array $options {type: 正常/外勤 打卡, remarks: 备注, position: 位置}
* @return boolean
*/
public function signDay(Employee $user, SignTime $time, $date = '', array $options = [])
{
$date = $date ?: now();
$log = EmployeeSignLog::create([
'store_id' => $user->store_id,
'employee_id' => $user->id,
'sign_time' => $time,
'time' => $date,
'sign_type' => data_get($options, 'type'),
'remarks' => data_get($options, 'remarks'),
'position' => data_get($options, 'position'),
'is_repair' => data_get($options, 'is_repair') ? 1 : 0,
'outside_remarks' => data_get($options, 'outside_remarks'),
'repair_id' => data_get($options, 'repair_id'),
]);
// 更新打卡情况
$sign = EmployeeSign::firstOrCreate([
'date' => $date->format('Y-m-d'),
'store_id' => $user->store_id,
'employee_id' => $user->id,
]);
$sign->sign_type = $log->sign_type;
if ($time == SignTime::Morning) {
$sign->first_time = $log->time;
} else if ($time == SignTime::Afternoon) {
$sign->last_time = $log->time;
}
$sign->sign_status = SignStatus::Lose;
if ($sign->first_time && $sign->last_time) {
$sign->sign_status = SignStatus::Normal;
}
$sign->remarks = $log->remarks;
$sign->save();
return true;
}
/**
* 判断员工某天是否休息
*/
public function hasRest(Employee $user, $date = '')
{
$date = $date ?: now();
return EmployeeRest::where('employee_id', $user->id)->where('date', $date)->exists();
}
/**
* 计算两点之间的距离(千米)
*/
public function haversineDistance($lat1, $lon1, $lat2, $lon2)
{
// 地球半径(千米)
$R = 6371;
// 将角度转换为弧度
$lat1 = deg2rad($lat1);
$lon1 = deg2rad($lon1);
$lat2 = deg2rad($lat2);
$lon2 = deg2rad($lon2);
// 经纬度差值
$dlon = $lon2 - $lon1;
$dlat = $lat2 - $lat1;
// Calculate the Haversine formula
$a = pow(sin($dlat/2), 2) + cos($lat1) * cos($lat2) * pow(sin($dlon/2), 2);
$c = 2 * atan2(sqrt($a), sqrt(1-$a));
$distance = $R * $c;
return floor($distance * 1000) / 1000;
}
}