修复业务镜像日期格式 (#59)

This commit is contained in:
WuYongJie 2026-08-15 03:16:16 +08:00
parent 9c179f6170
commit 955ddb5e26
2 changed files with 22 additions and 4 deletions

View File

@ -106,7 +106,6 @@ class BusinessMirrorSynchronizer
?Carbon $windowEnd,
int $limit,
): array {
$names = array_map(fn (array $column): string => (string) $column['column_name'], $columns);
$order = $this->orderColumns($dataset, $mode);
$where = [];
$bindings = [];
@ -129,7 +128,7 @@ class BusinessMirrorSynchronizer
}
$sql = 'SELECT TOP (' . $limit . ') '
. implode(', ', array_map(fn (string $name): string => $this->quote($name), $names))
. implode(', ', array_map(fn (array $column): string => $this->selectExpression($column), $columns))
. ' FROM ' . $this->quote($dataset->sourceSchema()) . '.' . $this->quote($dataset->sourceObject())
. ($where === [] ? '' : ' WHERE (' . implode(') AND (', $where) . ')')
. ' ORDER BY ' . implode(', ', array_map(fn (string $name): string => $this->quote($name), $order));
@ -137,6 +136,22 @@ class BusinessMirrorSynchronizer
return $this->queries->forSource($dataset->sourceKey())->select($sql, $bindings);
}
/** @param array<string,mixed> $column */
private function selectExpression(array $column): string
{
$name = (string) $column['column_name'];
$quoted = $this->quote($name);
// FreeTDS may return SQL Server date values using the connection locale
// (for example "Jan 1 2026"). Convert temporal columns at the source so
// every mirror receives deterministic ISO values that MariaDB accepts.
return match (strtolower((string) ($column['data_type'] ?? ''))) {
'date' => 'CONVERT(varchar(10), ' . $quoted . ', 23) AS ' . $quoted,
'datetime', 'datetime2', 'smalldatetime' => 'CONVERT(varchar(27), ' . $quoted . ', 126) AS ' . $quoted,
default => $quoted,
};
}
private function sqlServerDateTime(?Carbon $value): ?string
{
// ISO 8601 with millisecond precision is accepted consistently by SQL Server

View File

@ -69,6 +69,7 @@ class BusinessMirrorSynchronizerTest extends TestCase
$this->column('Id', 1, 'int', null),
$this->column('updateTime', 2, 'datetime2', null),
$this->column('displayName', 3, 'nvarchar', 120),
$this->column('businessDate', 4, 'date', null),
]);
$this->query = new FakeBusinessMirrorQuery();
@ -88,8 +89,8 @@ class BusinessMirrorSynchronizerTest extends TestCase
public function test_full_sync_materializes_complete_table_and_marks_state_ready(): void
{
$this->query->batches = [[
['Id' => 1, 'updateTime' => '2026-08-14 10:00:00.1234567', 'displayName' => '一号'],
['Id' => 2, 'updateTime' => '2026-08-14 11:00:00.0000000', 'displayName' => '二号'],
['Id' => 1, 'updateTime' => '2026-08-14 10:00:00.1234567', 'displayName' => '一号', 'businessDate' => '2026-08-14'],
['Id' => 2, 'updateTime' => '2026-08-14 11:00:00.0000000', 'displayName' => '二号', 'businessDate' => '2026-08-15'],
]];
$result = $this->synchronizer->sync('test.dbo.records', true, 10, 1);
@ -105,6 +106,8 @@ class BusinessMirrorSynchronizerTest extends TestCase
$this->assertNotNull($state->initial_completed_at);
$this->assertSame(2, (int) $state->mirror_row_count);
$this->assertStringContainsString('SELECT TOP (10)', $this->query->calls[0]['sql']);
$this->assertStringContainsString('CONVERT(varchar(27), [updateTime], 126) AS [updateTime]', $this->query->calls[0]['sql']);
$this->assertStringContainsString('CONVERT(varchar(10), [businessDate], 23) AS [businessDate]', $this->query->calls[0]['sql']);
$this->assertStringContainsString('ORDER BY [Id]', $this->query->calls[0]['sql']);
}