202 lines
6.4 KiB
PHP
202 lines
6.4 KiB
PHP
<?php
|
|
|
|
namespace Modules\EcommerceFinance\Repositories;
|
|
|
|
use Carbon\CarbonInterface;
|
|
use Illuminate\Database\ConnectionInterface;
|
|
use Illuminate\Database\DatabaseManager;
|
|
use Illuminate\Support\Carbon;
|
|
use Illuminate\Support\Facades\Schema;
|
|
|
|
class DingAttendanceMirrorRepository
|
|
{
|
|
public const TABLE = 'ding_attendance_records';
|
|
|
|
public const STATE_KEY = 'dingtalk.attendance.finance_daily_records';
|
|
|
|
private readonly ConnectionInterface $connection;
|
|
|
|
public function __construct(DatabaseManager $database)
|
|
{
|
|
$this->connection = $database->connection(
|
|
(string) config('upstream_mirrors.connection', 'business_mirror')
|
|
);
|
|
}
|
|
|
|
public function available(): bool
|
|
{
|
|
$schema = Schema::connection($this->connection->getName());
|
|
|
|
return $schema->hasTable(self::TABLE)
|
|
&& $schema->hasTable('business_mirror_table_states');
|
|
}
|
|
|
|
public function ready(): bool
|
|
{
|
|
if (! $this->available()) {
|
|
return false;
|
|
}
|
|
|
|
return ($this->state()['status'] ?? '') === 'succeeded';
|
|
}
|
|
|
|
/** @return array<string,mixed>|null */
|
|
public function state(): ?array
|
|
{
|
|
if (! $this->available()) {
|
|
return null;
|
|
}
|
|
|
|
$row = $this->connection->table('business_mirror_table_states')
|
|
->where('dataset_key', self::STATE_KEY)
|
|
->first();
|
|
|
|
return $row === null ? null : (array) $row;
|
|
}
|
|
|
|
/** @param list<array<string,mixed>> $rows */
|
|
public function upsertRecords(array $rows): int
|
|
{
|
|
if ($rows === []) {
|
|
return 0;
|
|
}
|
|
|
|
$this->connection->table(self::TABLE)->upsert(
|
|
$rows,
|
|
['record_id'],
|
|
[
|
|
'user_id',
|
|
'user_name',
|
|
'work_date',
|
|
'check_type',
|
|
'time_result',
|
|
'approve_tag_name',
|
|
'approve_id',
|
|
'proc_inst_id',
|
|
'source_type',
|
|
'base_check_time',
|
|
'user_check_time',
|
|
'synced_at',
|
|
'updated_at',
|
|
],
|
|
);
|
|
|
|
return count($rows);
|
|
}
|
|
|
|
public function recordSuccess(int $upserted, CarbonInterface $from, CarbonInterface $to): void
|
|
{
|
|
$now = now();
|
|
$state = $this->state();
|
|
$values = [
|
|
'source_key' => 'dingtalk_attendance',
|
|
'source_object' => 'attendance/list',
|
|
'target_table' => self::TABLE,
|
|
'status' => 'succeeded',
|
|
'run_mode' => 'incremental',
|
|
'cursor' => json_encode([
|
|
'from' => $from->toDateTimeString(),
|
|
'to' => $to->toDateTimeString(),
|
|
], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR),
|
|
'high_watermark' => json_encode(
|
|
['work_date' => $to->toDateString()],
|
|
JSON_THROW_ON_ERROR,
|
|
),
|
|
'window_started_at' => $from,
|
|
'window_ends_at' => $to,
|
|
'initial_completed_at' => $state['initial_completed_at'] ?? $now,
|
|
'last_attempted_at' => $now,
|
|
'last_succeeded_at' => $now,
|
|
'last_upserted_rows' => $upserted,
|
|
'mirror_row_count' => $this->connection->table(self::TABLE)->count(),
|
|
'last_error_code' => null,
|
|
'updated_at' => $now,
|
|
];
|
|
|
|
$this->connection->table('business_mirror_table_states')->updateOrInsert(
|
|
['dataset_key' => self::STATE_KEY],
|
|
$values + ['created_at' => $state['created_at'] ?? $now],
|
|
);
|
|
}
|
|
|
|
public function recordFailure(string $errorCode): void
|
|
{
|
|
if (! $this->available()) {
|
|
return;
|
|
}
|
|
|
|
$now = now();
|
|
$state = $this->state();
|
|
$this->connection->table('business_mirror_table_states')->updateOrInsert(
|
|
['dataset_key' => self::STATE_KEY],
|
|
[
|
|
'source_key' => 'dingtalk_attendance',
|
|
'source_object' => 'attendance/list',
|
|
'target_table' => self::TABLE,
|
|
'status' => 'failed',
|
|
'run_mode' => 'incremental',
|
|
'last_attempted_at' => $now,
|
|
'last_error_code' => mb_substr($errorCode, 0, 120),
|
|
'updated_at' => $now,
|
|
'initial_completed_at' => $state['initial_completed_at'] ?? null,
|
|
'mirror_row_count' => $this->connection->table(self::TABLE)->count(),
|
|
'created_at' => $state['created_at'] ?? $now,
|
|
],
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 返回“已审批请假/休息”等不扣分的用户日期。
|
|
*
|
|
* @param list<string> $userIds
|
|
* @return array<string,array<string,true>>
|
|
*/
|
|
public function exemptDatesForUsers(array $userIds, string $from, string $to): array
|
|
{
|
|
$userIds = array_values(array_unique(array_filter(array_map('trim', $userIds))));
|
|
if ($userIds === []) {
|
|
return [];
|
|
}
|
|
|
|
$approvalTags = array_values(array_filter(array_map(
|
|
'trim',
|
|
(array) config('dingtalk.attendance.exempt_approval_tags', []),
|
|
)));
|
|
$timeResults = array_values(array_filter(array_map(
|
|
'trim',
|
|
(array) config('dingtalk.attendance.exempt_time_results', []),
|
|
)));
|
|
if ($approvalTags === [] && $timeResults === []) {
|
|
return [];
|
|
}
|
|
|
|
$query = $this->connection->table(self::TABLE)
|
|
->whereIn('user_id', $userIds)
|
|
->whereBetween('work_date', [$from, $to])
|
|
->where(function ($scope) use ($approvalTags, $timeResults): void {
|
|
if ($approvalTags !== []) {
|
|
$scope->whereIn('approve_tag_name', $approvalTags);
|
|
}
|
|
if ($timeResults !== []) {
|
|
if ($approvalTags === []) {
|
|
$scope->whereIn('time_result', $timeResults);
|
|
} else {
|
|
$scope->orWhereIn('time_result', $timeResults);
|
|
}
|
|
}
|
|
})
|
|
->get(['user_id', 'work_date']);
|
|
|
|
$dates = [];
|
|
foreach ($query as $row) {
|
|
$userId = trim((string) ($row->user_id ?? ''));
|
|
$date = Carbon::parse($row->work_date)->toDateString();
|
|
if ($userId !== '' && $date !== '') {
|
|
$dates[$userId][$date] = true;
|
|
}
|
|
}
|
|
|
|
return $dates;
|
|
}
|
|
}
|