CaiWuXiTong/app/Console/Commands/SyncDingWorklogs.php

155 lines
6.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Console\Commands;
use App\Services\Auth\FinanceRoster;
use Illuminate\Console\Command;
use Modules\EcommerceFinance\Exceptions\DingTalkWorklogException;
use Modules\EcommerceFinance\Repositories\DingWorklogMirrorRepository;
use Modules\EcommerceFinance\Services\DingTalkWorklogClient;
use Throwable;
class SyncDingWorklogs extends Command
{
protected $signature = 'business-mirror:sync-ding-worklogs
{--days= : 回看天数,默认使用配置值}
{--template= : 日志模板名称,默认使用配置值}';
protected $description = '同步钉钉财务日报到日志统计镜像';
public function handle(
DingTalkWorklogClient $client,
DingWorklogMirrorRepository $repository,
FinanceRoster $financeRoster,
): int {
if (! (bool) config('dingtalk.worklog.enabled', true)) {
return self::SUCCESS;
}
if (! $repository->available()) {
$this->error('日志统计镜像表尚未建立,请先执行数据库迁移。');
return self::FAILURE;
}
if (! $client->isConfigured()) {
$repository->recordFailure('dingtalk_credentials_missing');
$this->error('钉钉应用凭据未配置。');
return self::FAILURE;
}
$days = max((int) ($this->option('days') ?: config('dingtalk.worklog.lookback_days', 35)), 1);
$templates = $this->templates();
$to = now();
$from = $to->copy()->subDays($days)->startOfDay();
try {
$now = now();
$pages = 0;
$reports = [];
foreach ($templates as $template) {
$result = $client->listReports($from, $to, $template);
$pages += (int) $result['pages'];
foreach ($result['reports'] as $report) {
if (! is_array($report)) {
continue;
}
$reportId = DingWorklogMirrorRepository::reportId($report);
$reports[$reportId] = [
'requested_template' => $template,
'report' => $report,
];
}
}
$financeOnlyTemplates = array_values(array_filter(array_map(
'trim',
(array) config('dingtalk.worklog.finance_only_templates', ['空白日志']),
)));
$financeMembers = null;
$rows = [];
foreach ($reports as $reportId => $entry) {
$report = $entry['report'];
$templateName = trim((string) ($report['template_name'] ?? $entry['requested_template']));
$creatorUserId = mb_substr(trim((string) ($report['creator_userid'] ?? $report['creator_id'] ?? '')), 0, 128);
if (in_array($templateName, $financeOnlyTemplates, true)) {
$financeMembers ??= array_fill_keys(
array_column($financeRoster->cachedFinanceMembers(), 'userid'),
true,
);
if (! isset($financeMembers[$creatorUserId])) {
continue;
}
}
$submittedRaw = $report['create_time']
?? $report['created_at']
?? $report['submit_time']
?? null;
if ($submittedRaw === null || $submittedRaw === '') {
continue;
}
try {
$submittedAt = DingWorklogMirrorRepository::submittedAt($submittedRaw);
} catch (Throwable) {
continue;
}
$statisticsDate = DingWorklogMirrorRepository::statisticsDate($submittedAt);
$rows[] = [
'report_id' => $reportId,
'template_name' => mb_substr($templateName, 0, 191),
'creator_userid' => $creatorUserId,
'creator_name' => mb_substr(trim((string) ($report['creator_name'] ?? $report['creator_nick'] ?? '')), 0, 191),
'submitted_at' => $submittedAt->toDateTimeString(),
'statistics_date' => $statisticsDate->toDateString(),
'remark' => trim((string) ($report['remark'] ?? '')),
'raw_payload' => json_encode($report, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
'synced_at' => $now,
'created_at' => $now,
'updated_at' => $now,
];
}
$upserted = $repository->upsertReports($rows);
$repository->recordSuccess($upserted, implode(' / ', $templates), $from, $to);
$this->info(sprintf(
'钉钉日志同步完成:读取 %d 页,写入/更新 %d 条。',
$pages,
$upserted,
));
return self::SUCCESS;
} catch (DingTalkWorklogException $exception) {
$repository->recordFailure($exception->errorCode);
report($exception);
$this->error($exception->getMessage());
return self::FAILURE;
} catch (Throwable $exception) {
$repository->recordFailure('dingtalk_worklog_sync_failed');
report($exception);
$this->error('钉钉日志同步失败,详细原因已记录到受保护日志。');
return self::FAILURE;
}
}
/** @return list<string> */
private function templates(): array
{
$configured = trim((string) $this->option('template'));
$templates = $configured !== ''
? preg_split('/[,,]/u', $configured, -1, PREG_SPLIT_NO_EMPTY)
: (array) config('dingtalk.worklog.template_names', [
config('dingtalk.worklog.template_name', '财务日报'),
]);
$templates = array_values(array_unique(array_filter(array_map(
static fn (mixed $template): string => trim((string) $template),
$templates,
))));
return $templates !== [] ? $templates : ['财务日报'];
}
}