- 店铺维度报表按期初数据+系统主体+爬虫新主体生成主体期间,一行一期间,修复流程历史重复行 - 变更日当天归新主体、老主体失效显示变更日前一天;关店和其他非正常状态两列显示 - - 新增店铺维度报表逻辑配置页(R1-R8)、主体商标数据逻辑配置页和预警页面 - 爬虫店铺主体按税号归一,首次抓到的新主体写入期间变更表并自动生成新期间 - 店铺基础信息、商标展示按爬虫优先;新增爬虫数据质量检查与预警处理
190 lines
6.8 KiB
PHP
190 lines
6.8 KiB
PHP
<?php
|
|
|
|
namespace Modules\EcommerceFinance\Repositories;
|
|
|
|
use Illuminate\Database\ConnectionInterface;
|
|
use Illuminate\Support\Carbon;
|
|
use Modules\EcommerceFinance\Repositories\GenericBusinessMirrorRepository;
|
|
|
|
class DataWarningMirrorRepository
|
|
{
|
|
private readonly ConnectionInterface $connection;
|
|
|
|
public function __construct(GenericBusinessMirrorRepository $mirror)
|
|
{
|
|
$this->connection = $mirror->connection();
|
|
}
|
|
|
|
public function ready(): bool
|
|
{
|
|
return $this->connection->getSchemaBuilder()->hasTable('business_data_warnings');
|
|
}
|
|
|
|
/**
|
|
* 生成或更新一条预警。同 warning_key 重复触发时只更新风险描述和影响,
|
|
* 保留首次触发时间、责任人指派和处理留痕。
|
|
*
|
|
* @param array<string,mixed> $warning
|
|
*/
|
|
public function upsert(array $warning): void
|
|
{
|
|
$key = (string) $warning['warning_key'];
|
|
$existing = $this->connection->table('business_data_warnings')->where('warning_key', $key)->first();
|
|
|
|
if ($existing !== null) {
|
|
$this->connection->table('business_data_warnings')->where('warning_key', $key)->update([
|
|
'warning_type' => $warning['warning_type'],
|
|
'risk_level' => $warning['risk_level'],
|
|
'related_object' => $warning['related_object'] ?? null,
|
|
'risk_description' => $warning['risk_description'] ?? null,
|
|
'evidence' => $warning['evidence'] ?? null,
|
|
'impact' => $warning['impact'] ?? null,
|
|
'trigger_rule' => $warning['trigger_rule'] ?? null,
|
|
'updated_at' => now(),
|
|
]);
|
|
|
|
return;
|
|
}
|
|
|
|
$this->connection->table('business_data_warnings')->insert([
|
|
'warning_key' => $key,
|
|
'warning_number' => $this->nextWarningNumber(),
|
|
'warning_type' => $warning['warning_type'],
|
|
'risk_level' => $warning['risk_level'],
|
|
'related_object' => $warning['related_object'] ?? null,
|
|
'risk_description' => $warning['risk_description'] ?? null,
|
|
'evidence' => $warning['evidence'] ?? null,
|
|
'impact' => $warning['impact'] ?? null,
|
|
'trigger_rule' => $warning['trigger_rule'] ?? null,
|
|
'triggered_at' => now(),
|
|
'responsible_person' => $warning['responsible_person'] ?? null,
|
|
'due_at' => $warning['due_at'] ?? null,
|
|
'review_status' => 'pending',
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 预警页面数据。按触发时间倒序,等待复审的排在前面。
|
|
*
|
|
* @param array<string,string> $filters
|
|
* @return array{data: array<int,array<string,mixed>>, total: int, pending: int}
|
|
*/
|
|
public function paginate(int $page, int $perPage, array $filters = []): array
|
|
{
|
|
if (! $this->ready()) {
|
|
return ['data' => [], 'total' => 0, 'pending' => 0];
|
|
}
|
|
|
|
$query = $this->connection->table('business_data_warnings');
|
|
|
|
foreach (['warning_type', 'risk_level', 'review_status'] as $exact) {
|
|
$value = trim((string) ($filters[$exact] ?? ''));
|
|
if ($value !== '') {
|
|
$query->where($exact, '=', $value);
|
|
}
|
|
}
|
|
|
|
foreach (['warning_number', 'related_object', 'responsible_person'] as $text) {
|
|
$value = trim((string) ($filters[$text] ?? ''));
|
|
if ($value !== '') {
|
|
$query->where($text, 'like', '%' . addcslashes($value, '%_\\') . '%');
|
|
}
|
|
}
|
|
|
|
$total = (clone $query)->count();
|
|
$pending = (clone $query)->where('review_status', '=', 'pending')->count();
|
|
|
|
$rows = $query
|
|
->orderByRaw("case when review_status = 'pending' then 0 else 1 end")
|
|
->orderByDesc('triggered_at')
|
|
->orderByDesc('id')
|
|
->forPage(max($page, 1), $perPage)
|
|
->get()
|
|
->map(static function (object $row): array {
|
|
$row = (array) $row;
|
|
$row['triggered_at'] = $row['triggered_at'] !== null
|
|
? Carbon::parse($row['triggered_at'])->format('Y-m-d H:i')
|
|
: null;
|
|
$row['reviewed_at'] = $row['reviewed_at'] !== null
|
|
? Carbon::parse($row['reviewed_at'])->format('Y-m-d H:i')
|
|
: null;
|
|
$row['due_at'] = $row['due_at'] !== null
|
|
? Carbon::parse($row['due_at'])->format('Y-m-d')
|
|
: null;
|
|
$row['review_status_label'] = match ((string) $row['review_status']) {
|
|
'pending' => '待复审',
|
|
'confirmed' => '已确认',
|
|
'rejected' => '已驳回',
|
|
default => (string) $row['review_status'],
|
|
};
|
|
|
|
return $row;
|
|
})
|
|
->all();
|
|
|
|
return ['data' => $rows, 'total' => $total, 'pending' => $pending];
|
|
}
|
|
|
|
/** @param array<int,array<string,mixed>> $warnings */
|
|
public function upsertMany(array $warnings): int
|
|
{
|
|
$count = 0;
|
|
foreach ($warnings as $warning) {
|
|
$this->upsert($warning);
|
|
$count++;
|
|
}
|
|
|
|
return $count;
|
|
}
|
|
|
|
public function find(int $id): ?array
|
|
{
|
|
if (! $this->ready()) {
|
|
return null;
|
|
}
|
|
|
|
$row = $this->connection->table('business_data_warnings')->where('id', $id)->first();
|
|
|
|
return $row === null ? null : (array) $row;
|
|
}
|
|
|
|
/**
|
|
* 人工复审:确认时保持系统按爬虫生成的结果,驳回时记录依据由后续
|
|
* 期间合并任务改回流程数据。
|
|
*/
|
|
public function review(int $id, string $status, string $reviewer, ?string $note, ?string $responsiblePerson = null): bool
|
|
{
|
|
if (! $this->ready()) {
|
|
return false;
|
|
}
|
|
|
|
$update = [
|
|
'review_status' => $status,
|
|
'reviewer' => $reviewer,
|
|
'reviewed_at' => now(),
|
|
'review_note' => $note,
|
|
'updated_at' => now(),
|
|
];
|
|
if ($responsiblePerson !== null && trim($responsiblePerson) !== '') {
|
|
$update['responsible_person'] = trim($responsiblePerson);
|
|
}
|
|
|
|
return $this->connection->table('business_data_warnings')->where('id', $id)->update($update) > 0;
|
|
}
|
|
|
|
private function nextWarningNumber(): string
|
|
{
|
|
$date = now()->format('Ymd');
|
|
$prefix = 'WARN-' . $date . '-';
|
|
$latest = $this->connection->table('business_data_warnings')
|
|
->where('warning_number', 'like', $prefix . '%')
|
|
->orderByDesc('warning_number')
|
|
->value('warning_number');
|
|
$sequence = $latest === null ? 0 : (int) substr((string) $latest, -3);
|
|
|
|
return $prefix . str_pad((string) ($sequence + 1), 3, '0', STR_PAD_LEFT);
|
|
}
|
|
}
|