商品利润镜像:超宽表非键列存 TEXT,并在首次全量完成前不对外使用 #427

Merged
renshen merged 1 commits from fix/renshen-afs-wide-strings into main 2026-10-21 14:08:23 +08:00
3 changed files with 21 additions and 0 deletions

View File

@ -408,6 +408,8 @@ return [
'watermark' => 'updatedAt',
'batch_size' => 2000,
'max_batches_per_run' => 4,
// 160 多个 nvarchar 列,整行按 VARCHAR 建表会超过 InnoDB 行长度上限。
'wide_strings' => true,
'indexes' => [
['undefined8', 'undefined7'],
['undefined2'],

View File

@ -30,12 +30,23 @@ class GoodsProfitAfsRepository
try {
return Schema::connection($this->connectionName())->hasTable(self::MAIN_TABLE)
&& Schema::connection($this->connectionName())->hasTable(self::SCHEMA_TABLE)
&& $this->initialSyncCompleted()
&& $this->connection()->table(self::MAIN_TABLE)->exists();
} catch (\Throwable) {
return false;
}
}
/** 首次全量同步完成前不对外使用,避免页面读到不完整的数据。 */
private function initialSyncCompleted(): bool
{
$state = $this->connection()->table('business_mirror_table_states')
->where('dataset_key', self::MAIN_DATASET)
->first(['initial_completed_at']);
return $state !== null && $state->initial_completed_at !== null;
}
/**
* 页面字段键 => 上游列名(undefinedN)。
*

View File

@ -139,6 +139,13 @@ class BusinessMirrorSchemaManager
$length = (int) ($column['max_length'] ?? 0);
$precision = min(max((int) ($column['numeric_precision'] ?? 20), 1), 65);
$scale = min(max((int) ($column['numeric_scale'] ?? 0), 0), min($precision, 30));
// 超宽表(例如商品利润 160 多个 nvarchar 列)即使每列只有 50 字符,
// 全部按 VARCHAR 建表也会超过 InnoDB 行长度上限;这类数据集把非键列存为 TEXT。
$wideStrings = (bool) app(BusinessMirrorDatasetRegistry::class)
->definitionValue($dataset->key(), 'wide_strings', false);
$requiredInline = in_array(strtolower($name), array_map('strtolower', $dataset->keyColumns()), true)
|| strcasecmp((string) $dataset->watermarkColumn(), $name) === 0
|| $this->isConfiguredIndexColumn($dataset, $name);
$definition = match ($type) {
'bit', 'boolean' => $table->boolean($name),
@ -160,6 +167,7 @@ class BusinessMirrorSchemaManager
// may opt a bounded legacy identifier into VARCHAR; every other
// wide string remains TEXT and is stored off-page.
'char', 'nchar', 'varchar', 'nvarchar', 'character', 'character varying' => $length > 0
&& ! ($wideStrings && ! $requiredInline)
&& ($length <= 64 || ($length <= 255 && $this->isConfiguredIndexColumn($dataset, $name)))
? $table->string($name, $length)
: $table->longText($name),