From 1621766f686beb2d3b819bd7280bcf5d3b0306b4 Mon Sep 17 00:00:00 2001 From: Andy Hong Date: Tue, 25 Aug 2026 22:54:52 +0900 Subject: [PATCH] fix: allow phone registration reassignment --- .../ssu/unithon/call/application/CallService.java | 14 +++++++++----- .../ssu/unithon/call/domain/PhoneRegistration.java | 5 +++++ .../ssu/unithon/call/api/CallControllerTests.java | 11 +++++++++-- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/ssu/unithon/call/application/CallService.java b/src/main/java/com/ssu/unithon/call/application/CallService.java index 9a08b8f..cea293e 100644 --- a/src/main/java/com/ssu/unithon/call/application/CallService.java +++ b/src/main/java/com/ssu/unithon/call/application/CallService.java @@ -70,16 +70,20 @@ public PhoneRegistration registerPhone(String phoneNumber, String participantKey String normalized = normalizePhone(phoneNumber); String capabilityHash = hash(participantKey); PhoneRegistration existing = phones.findById(normalized).orElse(null); - if (existing != null && !existing.matches(capabilityHash)) { - throw conflict("이미 다른 기기에 등록된 전화번호입니다."); - } phones.findByCapabilityHash(capabilityHash) .filter(registration -> !registration.getPhoneNumber().equals(normalized)) - .ifPresent(registration -> phones.delete(registration)); + .ifPresent(registration -> { + phones.delete(registration); + phones.flush(); + }); PhoneRegistration registration = existing != null ? existing : new PhoneRegistration(normalized, capabilityHash, clock.instant()); - registration.seen(clock.instant()); + if (existing != null && !existing.matches(capabilityHash)) { + registration.reassign(capabilityHash, clock.instant()); + } else { + registration.seen(clock.instant()); + } return phones.save(registration); } diff --git a/src/main/java/com/ssu/unithon/call/domain/PhoneRegistration.java b/src/main/java/com/ssu/unithon/call/domain/PhoneRegistration.java index 80083a1..8d66060 100644 --- a/src/main/java/com/ssu/unithon/call/domain/PhoneRegistration.java +++ b/src/main/java/com/ssu/unithon/call/domain/PhoneRegistration.java @@ -30,5 +30,10 @@ public PhoneRegistration(String phoneNumber, String capabilityHash, Instant now) public boolean matches(String hash) { return capabilityHash.equals(hash); } + public void reassign(String hash, Instant now) { + capabilityHash = hash; + lastSeenAt = now; + } + public void seen(Instant now) { lastSeenAt = now; } } diff --git a/src/test/java/com/ssu/unithon/call/api/CallControllerTests.java b/src/test/java/com/ssu/unithon/call/api/CallControllerTests.java index aaf068d..435db90 100644 --- a/src/test/java/com/ssu/unithon/call/api/CallControllerTests.java +++ b/src/test/java/com/ssu/unithon/call/api/CallControllerTests.java @@ -297,19 +297,26 @@ void registered_phone_can_receive_and_accept_an_outgoing_call() throws Exception } @Test - void phone_registration_and_outgoing_permissions_are_enforced() throws Exception { + void phone_registration_moves_to_the_latest_device_and_outgoing_permissions_are_enforced() throws Exception { registerPhone("01011112222", "first-device"); mvc.perform(put("/api/calls/phone-registration") .header("X-Participant-Key", "other-device") .contentType(MediaType.APPLICATION_JSON) .content("{\"phoneNumber\":\"01011112222\"}")) - .andExpect(status().isConflict()); + .andExpect(status().isOk()); mvc.perform(post("/api/calls/outgoing") .header("X-Participant-Key", "first-device") .header("X-Creator-Key", "creator") .contentType(MediaType.APPLICATION_JSON) .content("{\"callerPhone\":\"01011112222\",\"calleePhone\":\"01099998888\",\"mode\":\"DEAF\"}")) + .andExpect(status().isForbidden()); + + mvc.perform(post("/api/calls/outgoing") + .header("X-Participant-Key", "other-device") + .header("X-Creator-Key", "creator") + .contentType(MediaType.APPLICATION_JSON) + .content("{\"callerPhone\":\"01011112222\",\"calleePhone\":\"01099998888\",\"mode\":\"DEAF\"}")) .andExpect(status().isNotFound()); }