fix : sync issu

This commit is contained in:
SajjadMahmoody 2026-04-19 11:29:57 +03:30
parent d657c48600
commit 95592aa510
6 changed files with 63 additions and 22 deletions

View File

@ -59,7 +59,7 @@ public function getTitle(): string
public function getPendingCount(): int public function getPendingCount(): int
{ {
return SyncLog::changesSince((int) OsurgInitial::val('our_sync_cursor', 0))->count(); return SyncLog::changesSince(OsurgInitial::val('our_sync_cursor') ?: null)->count();
} }
public function getLastSyncTime(): string public function getLastSyncTime(): string

View File

@ -25,11 +25,10 @@ public function export(Request $request): JsonResponse
return response()->json(['error' => 'Unauthorized'], 401); return response()->json(['error' => 'Unauthorized'], 401);
} }
$since = (int) $request->query('since', 0); $since = $request->query('since') ?: null;
return response()->json([ return response()->json([
'last_sync_id' => SyncLog::lastSyncId(), 'changes' => SyncLog::changesSince($since),
'changes' => SyncLog::changesSince($since),
]); ]);
} }

View File

@ -32,12 +32,22 @@ public static function lastSyncId(): int
return (int) static::where('action', 'synced')->max('id') ?: 0; return (int) static::where('action', 'synced')->max('id') ?: 0;
} }
public static function changesSince(int $since): \Illuminate\Database\Eloquent\Collection public static function changesSince(?string $since): \Illuminate\Database\Eloquent\Collection
{ {
return static::where('id', '>', $since) $query = static::whereIn('action', ['created', 'updated', 'deleted'])
->whereIn('action', ['created', 'updated', 'deleted']) ->orderBy('datetime')
->orderBy('id') ->orderBy('id');
->get();
if ($since !== null && $since !== '') {
$query->where('datetime', '>', $since);
}
return $query->get();
}
protected function serializeDate(\DateTimeInterface $date): string
{
return $date->format('Y-m-d H:i:s');
} }
public static function markSynced(string $ip): static public static function markSynced(string $ip): static

View File

@ -51,8 +51,8 @@ public function sync(): array
$report = []; $report = [];
$baseUrl = "http://{$this->peerIp}:{$this->peerPort}"; $baseUrl = "http://{$this->peerIp}:{$this->peerPort}";
$peerCursor = (int) \App\Models\OsurgInitial::val('sync_peer_cursor', 0); $peerCursor = \App\Models\OsurgInitial::val('sync_peer_cursor') ?: null;
$ourLastSync = (int) \App\Models\OsurgInitial::val('our_sync_cursor', 0); $ourLastSync = \App\Models\OsurgInitial::val('our_sync_cursor') ?: null;
try { try {
$pullResponse = Http::timeout(30) $pullResponse = Http::timeout(30)
@ -65,14 +65,27 @@ public function sync(): array
if (! empty($peerChanges)) { if (! empty($peerChanges)) {
app()->instance('sync.applying', true); app()->instance('sync.applying', true);
DB::transaction(function () use ($peerChanges, &$report) { $pullReport = [];
DB::transaction(function () use ($peerChanges, &$pullReport) {
foreach ($peerChanges as $change) { foreach ($peerChanges as $change) {
$this->applyChange($change, 'دریافت', $report); $this->applyChange($change, 'دریافت', $pullReport);
} }
}); });
foreach ($pullReport as $line) {
$report[] = $line;
}
$maxPeerChangeId = max(array_column($peerChanges, 'id')); $pullHasErrors = ! empty(array_filter($pullReport, fn ($l) => str_contains((string) $l, '✗')));
$this->savePeerCursor(max($peerCursor, $maxPeerChangeId));
if (! $pullHasErrors) {
$datetimes = array_filter(array_column($peerChanges, 'datetime'));
$maxPeerDatetime = \Carbon\Carbon::parse(max($datetimes))->format('Y-m-d H:i:s');
if ($peerCursor === null || $maxPeerDatetime > $peerCursor) {
$this->savePeerCursor($maxPeerDatetime);
}
} else {
$report[] = 'هشدار: peer_cursor پیش نرفت — برخی تغییرات دریافتی اعمال نشد و دفعه بعد retry می‌شه.';
}
if ($this->isFtpConfigured()) { if ($this->isFtpConfigured()) {
$this->transferFilesViaFtp($peerChanges, 's2c', $report); $this->transferFilesViaFtp($peerChanges, 's2c', $report);
@ -98,14 +111,22 @@ public function sync(): array
->post("{$baseUrl}/api/sync/apply", ['changes' => $ourChanges]); ->post("{$baseUrl}/api/sync/apply", ['changes' => $ourChanges]);
if ($pushResponse->successful()) { if ($pushResponse->successful()) {
foreach ($pushResponse->json('report', []) as $line) { $pushReport = $pushResponse->json('report', []);
foreach ($pushReport as $line) {
$report[] = 'ارسال: ' . $line; $report[] = 'ارسال: ' . $line;
} }
$report[] = count($ourChanges) . ' تغییر به سیستم مقابل ارسال شد.'; $report[] = count($ourChanges) . ' تغییر به سیستم مقابل ارسال شد.';
if (! empty($ourChanges)) { $hasErrors = ! empty(array_filter($pushReport, fn ($l) => str_contains((string) $l, '✗')));
$maxOurId = max(array_column($ourChanges, 'id'));
$this->saveOurCursor($maxOurId); if (! empty($ourChanges) && ! $hasErrors) {
$ourDatetimes = array_filter(array_column($ourChanges, 'datetime'));
$maxOurDatetime = \Carbon\Carbon::parse(max($ourDatetimes))->format('Y-m-d H:i:s');
if ($ourLastSync === null || $maxOurDatetime > $ourLastSync) {
$this->saveOurCursor($maxOurDatetime);
}
} elseif ($hasErrors) {
$report[] = 'هشدار: cursor پیش نرفت — برخی تغییرات روی سیستم مقابل اعمال نشد و دفعه بعد retry می‌شه.';
} }
if ($this->isFtpConfigured()) { if ($this->isFtpConfigured()) {
@ -431,7 +452,7 @@ private function applyChange(array $change, string $prefix, array &$report): voi
} }
} }
private function saveOurCursor(int $cursor): void private function saveOurCursor(string $cursor): void
{ {
\App\Models\OsurgInitial::updateOrCreate( \App\Models\OsurgInitial::updateOrCreate(
['init_parameter' => 'our_sync_cursor'], ['init_parameter' => 'our_sync_cursor'],
@ -440,7 +461,7 @@ private function saveOurCursor(int $cursor): void
cache()->forget('osurg_initial.our_sync_cursor'); cache()->forget('osurg_initial.our_sync_cursor');
} }
private function savePeerCursor(int $cursor): void private function savePeerCursor(string $cursor): void
{ {
\App\Models\OsurgInitial::updateOrCreate( \App\Models\OsurgInitial::updateOrCreate(
['init_parameter' => 'sync_peer_cursor'], ['init_parameter' => 'sync_peer_cursor'],

View File

@ -19,6 +19,7 @@ public function run(): void
TreatmentSeeder::class, TreatmentSeeder::class,
SurgeryCenterSeeder::class, SurgeryCenterSeeder::class,
MedicationSeeder::class, MedicationSeeder::class,
LabTestSeeder::class,
OsurgInitialSeeder::class, OsurgInitialSeeder::class,
RolePermissionSeeder::class, RolePermissionSeeder::class,
]); ]);

View File

@ -82,7 +82,17 @@ public function run(): void
], ],
[ [
'init_parameter' => 'ftp_remote_path', 'init_parameter' => 'ftp_remote_path',
'init_value' => 'C:/wamp64/www/matab-panel/storage/app/public', 'init_value' => '/',
'attachment' => null,
],
[
'init_parameter' => 'sync_peer_cursor',
'init_value' => null,
'attachment' => null,
],
[
'init_parameter' => 'our_sync_cursor',
'init_value' => null,
'attachment' => null, 'attachment' => null,
], ],
]; ];