- 新增违规字典表 store_violation_rules 与推送日志表 store_violation_push_logs;统一表增加预警派生字段(迁移,全部可空带索引) - 预警计算:按平台+违规类型匹配字典(去括号后 相等/前缀/分词/宽松/关键词),匹配不到=不提醒;责任人取店铺账号镜像运营负责人并回退店铺档案;处理截止=申诉截止优先,否则级别天数(P0+1/P1+2/P2+3,当天23:59:59) - 页面:新增预警级别(红/橙/绿标签)、责任人、处理截止、是否超时、推送状态5列;级别/责任人(含我的待办)/超时筛选;默认视图(需提醒&未处理)与全部数据切换;行内 认领/完成/忽略(忽略必填原因) - 处理状态扩为 未处理/处理中/处理完成/已忽略,导入兼容旧值(已处理/无需处理) - 新增「预览统计」页面:平台Tab联动 + 4张KPI卡(今日新增/待处理/P0未处理/已超时)+ 需提醒未处理预览列表与认领 - 群推送先留接口:P0/P1 每天09:00起每5分钟即时推送(责任人、处理建议、去认领签名链接);P2每日10:00汇总(含超时未认领点名);防重与推送日志;新命令 push-instant/push-digest - 同步与导入自动计算派生字段;新增 store-violations:recalc-alerts 重算命令;生产 cron 已登记推送任务 - 测试:StoreViolation 系列 + 部署调度 + 导航 共42项/453断言通过
83 lines
5.0 KiB
PHP
83 lines
5.0 KiB
PHP
<?php
|
||
|
||
namespace Tests\Unit\Business;
|
||
|
||
use App\Business\StoreViolationAlertCalculator;
|
||
use Illuminate\Support\Carbon;
|
||
use Tests\TestCase;
|
||
|
||
/**
|
||
* 店铺违规信息预警:预警级别匹配、处理截止与超时判定。
|
||
*/
|
||
class StoreViolationAlertCalculatorTest extends TestCase
|
||
{
|
||
public function test_clean_violation_type_strips_parenthesized_disposition(): void
|
||
{
|
||
$this->assertSame('虚假交易', StoreViolationAlertCalculator::cleanViolationType('虚假交易(立即删除销量;)'));
|
||
$this->assertSame('AE', StoreViolationAlertCalculator::cleanViolationType('A(B(C)D)E(F)'));
|
||
$this->assertSame('订单存在揽收超时行为', StoreViolationAlertCalculator::cleanViolationType(' 订单存在揽收超时行为 '));
|
||
$this->assertSame('', StoreViolationAlertCalculator::cleanViolationType(null));
|
||
}
|
||
|
||
public function test_match_rule_uses_exact_prefix_parts_and_keywords_in_order(): void
|
||
{
|
||
$rules = [
|
||
['id' => 1, 'platform' => 'jd', 'violation_type' => '缺货/无货', 'match_keywords' => null,
|
||
'alert_level' => 'P1', 'needs_alert' => 1, 'suggested_action' => '联系客户补发', 'handling_hours' => null],
|
||
['id' => 2, 'platform' => 'jingxi', 'violation_type' => '订单延迟发货', 'match_keywords' => null,
|
||
'alert_level' => 'P0', 'needs_alert' => 1, 'suggested_action' => null, 'handling_hours' => null],
|
||
['id' => 3, 'platform' => 'taobao', 'violation_type' => null, 'match_keywords' => '虚假交易|危及交易安全',
|
||
'alert_level' => 'P2', 'needs_alert' => 1, 'suggested_action' => null, 'handling_hours' => null],
|
||
['id' => 4, 'platform' => 'douyin', 'violation_type' => '效果虚假', 'match_keywords' => null,
|
||
'alert_level' => null, 'needs_alert' => 0, 'suggested_action' => null, 'handling_hours' => null],
|
||
];
|
||
|
||
// 分词包含:规则"缺货/无货"命中"延迟发货/缺货无货"
|
||
$this->assertSame(1, StoreViolationAlertCalculator::matchRule($rules, 'jd', '延迟发货/缺货无货')['id']);
|
||
// 相等与前缀
|
||
$this->assertSame(2, StoreViolationAlertCalculator::matchRule($rules, 'jingxi', '订单延迟发货')['id']);
|
||
$this->assertSame(2, StoreViolationAlertCalculator::matchRule($rules, 'jingxi', '订单延迟发货(立即扣分;)')['id']);
|
||
// 关键词匹配(先去括号)
|
||
$this->assertSame(3, StoreViolationAlertCalculator::matchRule($rules, 'taobao', '虚假交易(立即删除销量&屏蔽评论内容;)')['id']);
|
||
// 命中但规则标记为不需要提醒
|
||
$this->assertSame(4, StoreViolationAlertCalculator::matchRule($rules, 'douyin', '效果虚假')['id']);
|
||
// 平台隔离与未命中
|
||
$this->assertNull(StoreViolationAlertCalculator::matchRule($rules, 'jd', '效果虚假'));
|
||
$this->assertNull(StoreViolationAlertCalculator::matchRule($rules, 'pinduoduo', '任意类型'));
|
||
$this->assertNull(StoreViolationAlertCalculator::matchRule($rules, 'jd', null));
|
||
}
|
||
|
||
public function test_deadline_prefers_appeal_date_then_hours_then_level_days(): void
|
||
{
|
||
// 申诉截止非空 → 当天 23:59:59
|
||
$this->assertSame(
|
||
'2026-10-11 23:59:59',
|
||
StoreViolationAlertCalculator::deadlineFor('2026-10-04', '2026-10-11', null, 'P1'),
|
||
);
|
||
// 字典时效(小时)
|
||
$this->assertSame(
|
||
'2026-10-04 06:00:00',
|
||
StoreViolationAlertCalculator::deadlineFor('2026-10-04', null, 6, 'P0'),
|
||
);
|
||
// 级别默认天数:P0+1 / P1+2 / P2+3 天的 23:59:59
|
||
$this->assertSame('2026-10-05 23:59:59', StoreViolationAlertCalculator::deadlineFor('2026-10-04', null, null, 'P0'));
|
||
$this->assertSame('2026-10-06 23:59:59', StoreViolationAlertCalculator::deadlineFor('2026-10-04', null, null, 'P1'));
|
||
$this->assertSame('2026-10-07 23:59:59', StoreViolationAlertCalculator::deadlineFor('2026-10-04', null, null, 'P2'));
|
||
// 无违规日期或无级别 → null
|
||
$this->assertNull(StoreViolationAlertCalculator::deadlineFor(null, null, null, 'P0'));
|
||
$this->assertNull(StoreViolationAlertCalculator::deadlineFor('2026-10-04', null, null, null));
|
||
}
|
||
|
||
public function test_is_overdue_respects_status_and_deadline(): void
|
||
{
|
||
$now = Carbon::parse('2026-10-05 10:00:00');
|
||
|
||
$this->assertTrue(StoreViolationAlertCalculator::isOverdue('未处理', '2026-10-04 23:59:59', $now));
|
||
$this->assertTrue(StoreViolationAlertCalculator::isOverdue('处理中', '2026-10-05 09:00:00', $now));
|
||
$this->assertFalse(StoreViolationAlertCalculator::isOverdue('处理完成', '2020-01-01 00:00:00', $now));
|
||
$this->assertFalse(StoreViolationAlertCalculator::isOverdue('已忽略', '2020-01-01 00:00:00', $now));
|
||
$this->assertFalse(StoreViolationAlertCalculator::isOverdue('未处理', null, $now));
|
||
$this->assertFalse(StoreViolationAlertCalculator::isOverdue('未处理', '2026-10-06 23:59:59', $now));
|
||
}
|
||
}
|