CaiWuXiTong/modules/EcommerceFinance/Services/GoodsProfitAfsLookupSchema.php
WuYongJie 920fbcb650 优化售后首屏索引、工单查询及翻页 (#452)
保留原始日期与金额,增加可重建镜像索引并通过相关回归和百万行验证。
2026-10-22 10:55:40 +08:00

76 lines
3.1 KiB
PHP

<?php
namespace Modules\EcommerceFinance\Services;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Support\Facades\Schema;
/** Derived lookup only: the upstream strings and financial values stay intact. */
class GoodsProfitAfsLookupSchema
{
public const TABLE = 'ops_jst_goods_profit_kpi_items_afs';
public const DATE_COLUMN = '_mirror_afs_order_time';
public const INDEXES = [
'idx_afs_order_time_id' => ['_mirror_afs_order_time', 'id'],
'idx_afs_shop_order_time_id' => ['undefined8', '_mirror_afs_order_time', 'id'],
];
public function ensure(ConnectionInterface $db): void
{
if (! in_array($db->getDriverName(), ['mysql', 'mariadb'], true)) {
return;
}
$schema = Schema::connection($db->getName());
if (! $schema->hasTable(self::TABLE)
|| ! $schema->hasColumns(self::TABLE, ['id', 'undefined7', 'undefined8'])) {
return;
}
$statements = [];
if (! $schema->hasColumn(self::TABLE, self::DATE_COLUMN)) {
$expression = $this->dateExpression();
// MariaDB requires the virtual column and its indexes in separate
// ALTER statements to allow concurrent writes with LOCK=NONE.
$statements[] = 'ADD COLUMN `' . self::DATE_COLUMN
. '` VARCHAR(19) CHARACTER SET ascii COLLATE ascii_bin GENERATED ALWAYS AS (' . $expression . ') VIRTUAL';
}
$indexes = [];
foreach (self::INDEXES as $name => $columns) {
if (! $schema->hasIndex(self::TABLE, $name)) {
$indexes[] = 'ADD INDEX `' . $name . '` (`' . implode('`, `', $columns) . '`)';
}
}
if ($indexes !== []) $statements[] = implode(', ', $indexes);
if ($statements !== []) {
// Fail rather than silently copying/locking this multi-million-row table.
$wait = (int) $db->selectOne('SELECT @@SESSION.lock_wait_timeout AS seconds')->seconds;
$db->statement('SET SESSION lock_wait_timeout = 5');
try {
foreach ($statements as $statement) {
$db->statement('ALTER TABLE `' . self::TABLE . '` ' . $statement . ', ALGORITHM=INPLACE, LOCK=NONE');
}
} finally {
$db->statement('SET SESSION lock_wait_timeout = ' . $wait);
}
}
}
public function dateExpression(): string
{
$raw = "REPLACE(TRIM(`undefined7`), '/', '-')";
$pattern = '^[0-9]{1,4}-(0?[1-9]|1[0-2])-(0?[1-9]|[12][0-9]|3[01])'
. '([[:space:]]+([01]?[0-9]|2[0-3])(:[0-5]?[0-9]){0,2}([.][0-9]+)?)?$';
// Guard malformed strings so a future bad source value cannot break an
// indexed-column update in strict mode. Fractional seconds were already
// ignored by the original format. CHAR preserves its calendar semantics
// (e.g. 2026-02-31), unlike coercing the result into a DATETIME column.
return "CASE WHEN {$raw} REGEXP '{$pattern}' THEN CAST(STR_TO_DATE("
. "SUBSTRING_INDEX({$raw}, '.', 1), '%Y-%m-%d %H:%i:%s') AS CHAR(19)) ELSE NULL END";
}
}