feat: add non-destructive EJ medicine sync

This commit is contained in:
2026-09-10 11:09:26 +08:00
parent 27fbef9321
commit 17e9e7b6b6
6 changed files with 1087 additions and 0 deletions
+32
View File
@@ -39,9 +39,13 @@ $assertTrue(
$bootstrapItemPath = dirname(__DIR__, 2) . '/app/common/service/pharmacy/EjMedicineBootstrapItem.php';
$bootstrapServicePath = dirname(__DIR__, 2) . '/app/common/service/pharmacy/EjMedicineBootstrapService.php';
$bootstrapCommandPath = dirname(__DIR__, 2) . '/app/command/EjPharmacyBootstrapMedicines.php';
$incrementalServicePath = dirname(__DIR__, 2) . '/app/common/service/pharmacy/EjMedicineIncrementalPushService.php';
$incrementalCommandPath = dirname(__DIR__, 2) . '/app/command/EjPharmacyPushMedicines.php';
$assertTrue(is_file($bootstrapItemPath), 'ZYT must provide a dedicated EJ medicine bootstrap item normalizer');
$assertTrue(is_file($bootstrapServicePath), 'ZYT must provide an atomic EJ medicine bootstrap service');
$assertTrue(is_file($bootstrapCommandPath), 'ZYT must provide the ej-pharmacy:bootstrap-medicines command');
$assertTrue(is_file($incrementalServicePath), 'ZYT must provide a non-destructive EJ medicine incremental push service');
$assertTrue(is_file($incrementalCommandPath), 'ZYT must provide the ej-pharmacy:push-medicines command');
$clientSource = (string) file_get_contents(dirname(__DIR__, 2) . '/app/common/service/pharmacy/EjPharmacyClient.php');
$configSource = (string) file_get_contents(dirname(__DIR__, 2) . '/config/ej_pharmacy.php');
@@ -62,6 +66,34 @@ $assertTrue(
&& str_contains((string) file_get_contents($bootstrapCommandPath), 'RESET_TEST_CATALOG'),
'bootstrap command must be registered and guard destructive replacement with the exact confirmation token'
);
$incrementalServiceSource = (string) file_get_contents($incrementalServicePath);
$incrementalCommandSource = (string) file_get_contents($incrementalCommandPath);
$assertTrue(
str_contains($consoleSource, "'ej-pharmacy:push-medicines'")
&& str_contains($incrementalCommandSource, 'INCREMENTAL_NO_DELETE')
&& str_contains($incrementalCommandSource, "addOption('apply'")
&& str_contains($incrementalCommandSource, 'dry-run'),
'incremental push command must be registered, dry-run by default, and require the non-delete confirmation token'
);
$assertTrue(
!str_contains($incrementalServiceSource, '->delete(')
&& str_contains($incrementalServiceSource, "'remote_delete_count' => 0")
&& str_contains($incrementalServiceSource, "'local_delete_count' => 0"),
'incremental medicine push must preserve EJ-only medicines and unrelated local projections'
);
$assertTrue(
str_contains($incrementalServiceSource, "private const DEFAULT_RUN_ID = 'zyt-incremental'")
&& str_contains($incrementalServiceSource, 'private static function acquireLock(')
&& str_contains($incrementalServiceSource, "->where('lock_token', '')")
&& str_contains($incrementalServiceSource, "'lock_expires_at'"),
'incremental medicine apply must use deterministic retry ids and share the EJ synchronization lease'
);
$assertTrue(
str_contains($incrementalServiceSource, "['source_system']")
&& str_contains($incrementalServiceSource, "['payload_hash']")
&& str_contains($incrementalServiceSource, '增量同步未改写该映射'),
'incremental medicine apply must validate response identity and never rewrite an active mapping to a different code'
);
$assertTrue(
str_contains($configSource, "'catalog_sync_enabled'")
&& str_contains($configSource, "env('EJ_PHARMACY_CATALOG_SYNC_ENABLED', false)"),