From ad491191ab50a259da548863d805c57d8a95d083 Mon Sep 17 00:00:00 2001
From: liujie <1793218484@qq.com>
Date: 星期二, 15 九月 2026 11:16:12 +0800
Subject: [PATCH] update

---
 backend/src/main/java/com/tourguide/modules/user/service/GuideAccountService.java         |   14 +------
 backend/src/test/java/com/tourguide/modules/user/service/GuideUserServiceProfileTest.java |   26 +++++++++----
 backend/src/main/java/com/tourguide/modules/user/mapper/GuideUserMapper.java              |   16 ++++++++
 backend/src/main/java/com/tourguide/modules/user/service/GuideUserService.java            |   17 ++++----
 backend/src/test/java/com/tourguide/modules/user/service/GuideAccountServiceTest.java     |   16 ++------
 mingdaojingjiang_uniapp/mingdaojingjiang_uniapp/pages/home/index.vue                      |   12 +++--
 6 files changed, 56 insertions(+), 45 deletions(-)

diff --git a/backend/src/main/java/com/tourguide/modules/user/mapper/GuideUserMapper.java b/backend/src/main/java/com/tourguide/modules/user/mapper/GuideUserMapper.java
index e2708bd..7950712 100644
--- a/backend/src/main/java/com/tourguide/modules/user/mapper/GuideUserMapper.java
+++ b/backend/src/main/java/com/tourguide/modules/user/mapper/GuideUserMapper.java
@@ -9,6 +9,22 @@
 import java.math.BigDecimal;
 
 public interface GuideUserMapper extends BaseMapper<GuideUser> {
+    @Update("UPDATE guide_user SET username = #{username}, mobile = NULL, certificate_no = NULL, " +
+            "id_card_no = NULL, id_card_front_url = NULL, id_card_back_url = NULL, status = 3, " +
+            "auto_disable_whitelisted = 0, auto_disabled = 0, updated_at = NOW() " +
+            "WHERE id = #{guideId} AND deleted = 0")
+    int cancelAccount(@Param("guideId") Long guideId, @Param("username") String username);
+
+    @Update("UPDATE guide_user SET username = #{username}, mobile = NULL, certificate_no = NULL, " +
+            "id_card_no = NULL, id_card_front_url = NULL, id_card_back_url = NULL, updated_at = NOW() " +
+            "WHERE id = #{guideId} AND status = 3 AND deleted = 0")
+    int anonymizeCancelledGuide(@Param("guideId") Long guideId, @Param("username") String username);
+
+    @Update("UPDATE guide_user SET id_card_no = NULL, id_card_front_url = NULL, " +
+            "id_card_back_url = NULL, updated_at = NOW() " +
+            "WHERE id_card_no = #{idCardNo} AND status = 3 AND deleted = 0")
+    int clearCancelledIdentityByIdCard(@Param("idCardNo") String idCardNo);
+
     @Select("SELECT * FROM guide_user WHERE id = #{guideId} AND deleted = 0 FOR UPDATE")
     GuideUser selectForUpdate(@Param("guideId") Long guideId);
 
diff --git a/backend/src/main/java/com/tourguide/modules/user/service/GuideAccountService.java b/backend/src/main/java/com/tourguide/modules/user/service/GuideAccountService.java
index 5271792..e6ef137 100644
--- a/backend/src/main/java/com/tourguide/modules/user/service/GuideAccountService.java
+++ b/backend/src/main/java/com/tourguide/modules/user/service/GuideAccountService.java
@@ -1,7 +1,6 @@
 package com.tourguide.modules.user.service;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.tourguide.common.exception.BusinessException;
 import com.tourguide.modules.order.domain.OrderStatus;
 import com.tourguide.modules.order.entity.TourOrder;
@@ -58,17 +57,8 @@
         if (idCardNo != null && !idCardNo.trim().isEmpty()) {
             guideApplicationMapper.clearIdentityByIdCard(idCardNo);
         }
-        guideUserMapper.update(null, new LambdaUpdateWrapper<GuideUser>()
-                .eq(GuideUser::getId, guideId)
-                .set(GuideUser::getUsername, "deleted_" + UUID.randomUUID().toString().replace("-", "").substring(0, 24))
-                .set(GuideUser::getMobile, null)
-                .set(GuideUser::getCertificateNo, null)
-                .set(GuideUser::getIdCardNo, null)
-                .set(GuideUser::getIdCardFrontUrl, null)
-                .set(GuideUser::getIdCardBackUrl, null)
-                .set(GuideUser::getStatus, GuideUserService.STATUS_CANCELLED)
-                .set(GuideUser::getAutoDisableWhitelisted, 0)
-                .set(GuideUser::getAutoDisabled, 0));
+        String deletedUsername = "deleted_" + UUID.randomUUID().toString().replace("-", "").substring(0, 24);
+        guideUserMapper.cancelAccount(guideId, deletedUsername);
         tokenSessionService.remove(payload);
     }
 }
diff --git a/backend/src/main/java/com/tourguide/modules/user/service/GuideUserService.java b/backend/src/main/java/com/tourguide/modules/user/service/GuideUserService.java
index c68ba55..6fb2478 100644
--- a/backend/src/main/java/com/tourguide/modules/user/service/GuideUserService.java
+++ b/backend/src/main/java/com/tourguide/modules/user/service/GuideUserService.java
@@ -1,7 +1,6 @@
 package com.tourguide.modules.user.service;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.tourguide.common.api.PageResponse;
 import com.tourguide.common.exception.BusinessException;
@@ -333,25 +332,27 @@
     }
 
     private void anonymizeCancelledGuide(GuideUser user) {
-        userMapper.update(null, new LambdaUpdateWrapper<GuideUser>()
-                .eq(GuideUser::getId, user.getId())
-                .set(GuideUser::getUsername, "deleted_" + UUID.randomUUID().toString().replace("-", "").substring(0, 24))
-                .set(GuideUser::getMobile, null)
-                .set(GuideUser::getCertificateNo, null)
-                .set(GuideUser::getIdCardNo, null));
+        String deletedUsername = "deleted_" + UUID.randomUUID().toString().replace("-", "").substring(0, 24);
+        userMapper.anonymizeCancelledGuide(user.getId(), deletedUsername);
     }
 
     private void ensureUniqueGuideIdentity(GuideApplication application, GuideUser existingUser) {
+        if (StringUtils.hasText(application.getIdCardNo())) {
+            // 兼容旧注销数据:释放已注销账号占用的身份证唯一索引。
+            userMapper.clearCancelledIdentityByIdCard(application.getIdCardNo().trim().toUpperCase());
+        }
         if (StringUtils.hasText(application.getCertificateNo())
                 && userMapper.selectCount(new LambdaQueryWrapper<GuideUser>()
                 .eq(GuideUser::getCertificateNo, application.getCertificateNo())
+                .ne(GuideUser::getStatus, STATUS_CANCELLED)
                 .ne(existingUser != null && existingUser.getId() != null,
                         GuideUser::getId, existingUser == null ? null : existingUser.getId())) > 0) {
             throw new BusinessException(409, "该导游证号已存在导游账号");
         }
         if (StringUtils.hasText(application.getIdCardNo())
                 && userMapper.selectCount(new LambdaQueryWrapper<GuideUser>()
-                .eq(GuideUser::getIdCardNo, application.getIdCardNo())
+                .eq(GuideUser::getIdCardNo, application.getIdCardNo().trim().toUpperCase())
+                .ne(GuideUser::getStatus, STATUS_CANCELLED)
                 .ne(existingUser != null && existingUser.getId() != null,
                         GuideUser::getId, existingUser == null ? null : existingUser.getId())) > 0) {
             throw new BusinessException(409, "该身份证号已存在导游账号");
diff --git a/backend/src/test/java/com/tourguide/modules/user/service/GuideAccountServiceTest.java b/backend/src/test/java/com/tourguide/modules/user/service/GuideAccountServiceTest.java
index eff9dfa..63f8654 100644
--- a/backend/src/test/java/com/tourguide/modules/user/service/GuideAccountServiceTest.java
+++ b/backend/src/test/java/com/tourguide/modules/user/service/GuideAccountServiceTest.java
@@ -11,11 +11,11 @@
 import com.tourguide.security.TokenSessionService;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.mockito.ArgumentCaptor;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -50,7 +50,7 @@
                 .isInstanceOf(BusinessException.class)
                 .hasMessage("存在未完成订单,暂时无法注销账号");
 
-        verify(guideUserMapper, never()).updateById(any());
+        verify(guideUserMapper, never()).cancelAccount(any(), any());
         verify(tokenSessionService, never()).remove(payload);
     }
 
@@ -71,16 +71,8 @@
 
         accountService.cancel(8L, "guide-token");
 
-        ArgumentCaptor<GuideUser> userCaptor = ArgumentCaptor.forClass(GuideUser.class);
-        verify(guideUserMapper).updateById(userCaptor.capture());
-        assertThat(userCaptor.getValue().getStatus()).isEqualTo(GuideUserService.STATUS_CANCELLED);
-        assertThat(userCaptor.getValue().getAutoDisableWhitelisted()).isZero();
-        assertThat(userCaptor.getValue().getUsername()).startsWith("deleted_").hasSize(32);
-        assertThat(userCaptor.getValue().getMobile()).isNull();
-        assertThat(userCaptor.getValue().getCertificateNo()).isNull();
-        assertThat(userCaptor.getValue().getIdCardNo()).isNull();
-        assertThat(userCaptor.getValue().getIdCardFrontUrl()).isNull();
-        assertThat(userCaptor.getValue().getIdCardBackUrl()).isNull();
+        verify(guideUserMapper).cancelAccount(eq(8L), org.mockito.ArgumentMatchers.argThat(
+                username -> username.startsWith("deleted_") && username.length() == 32));
         verify(guideApplicationMapper).clearIdentityByIdCard("11010519491231002X");
         verify(tokenSessionService).remove(payload);
     }
diff --git a/backend/src/test/java/com/tourguide/modules/user/service/GuideUserServiceProfileTest.java b/backend/src/test/java/com/tourguide/modules/user/service/GuideUserServiceProfileTest.java
index a142289..e494fef 100644
--- a/backend/src/test/java/com/tourguide/modules/user/service/GuideUserServiceProfileTest.java
+++ b/backend/src/test/java/com/tourguide/modules/user/service/GuideUserServiceProfileTest.java
@@ -24,6 +24,7 @@
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -114,14 +115,9 @@
 
         service.createPendingFromApplication(application);
 
-        ArgumentCaptor<GuideUser> updateCaptor = ArgumentCaptor.forClass(GuideUser.class);
-        verify(userMapper).updateById(updateCaptor.capture());
-        GuideUser anonymizedGuide = updateCaptor.getValue();
-        assertThat(anonymizedGuide.getStatus()).isEqualTo(GuideUserService.STATUS_CANCELLED);
-        assertThat(anonymizedGuide.getUsername()).startsWith("deleted_").hasSize(32);
-        assertThat(anonymizedGuide.getMobile()).isNull();
-        assertThat(anonymizedGuide.getCertificateNo()).isNull();
-        assertThat(anonymizedGuide.getIdCardNo()).isNull();
+        verify(userMapper).anonymizeCancelledGuide(eq(12L), org.mockito.ArgumentMatchers.argThat(
+                username -> username.startsWith("deleted_") && username.length() == 32));
+        verify(userMapper).clearCancelledIdentityByIdCard("11010519491231002X");
 
         ArgumentCaptor<GuideUser> insertCaptor = ArgumentCaptor.forClass(GuideUser.class);
         verify(userMapper).insert(insertCaptor.capture());
@@ -134,6 +130,20 @@
     }
 
     @Test
+    void shouldReleaseIdentityFromStaleCancelledGuideBeforeNewRegistration() {
+        GuideApplication application = new GuideApplication();
+        application.setMobile("13900139000");
+        application.setRealName("新导游");
+        application.setIdCardNo("11010519491231002X");
+        when(userMapper.selectOne(any())).thenReturn(null);
+
+        service.createPendingFromApplication(application);
+
+        verify(userMapper).clearCancelledIdentityByIdCard("11010519491231002X");
+        verify(userMapper).insert(any(GuideUser.class));
+    }
+
+    @Test
     void shouldTreatOneYearOrLessAsZeroWorkYears() {
         GuideApplication application = new GuideApplication();
         application.setMobile("13800138000");
diff --git a/mingdaojingjiang_uniapp/mingdaojingjiang_uniapp/pages/home/index.vue b/mingdaojingjiang_uniapp/mingdaojingjiang_uniapp/pages/home/index.vue
index 1b7fdd5..cddde80 100644
--- a/mingdaojingjiang_uniapp/mingdaojingjiang_uniapp/pages/home/index.vue
+++ b/mingdaojingjiang_uniapp/mingdaojingjiang_uniapp/pages/home/index.vue
@@ -174,12 +174,12 @@
                   <text class="payment-service">{{ normalizedSelectedServiceTitle }}</text>
                   <text class="payment-desc">{{ selectedServiceDuration }}</text>
                 </view>
-                <text class="payment-unit-price">¥ {{ serviceUnitPrice }}/人</text>
+                <text class="payment-unit-price">¥ {{ serviceOriginalUnitPrice }}/人</text>
               </view>
               <view class="payment-row">
                 <text class="payment-label">人数</text>
                 <text class="payment-value">{{ selectedServiceQuantity }}人</text>
-                <text class="payment-price">¥{{ serviceSubtotalDisplay }}</text>
+                <text class="payment-price">¥{{ serviceOriginalSubtotalDisplay }}</text>
               </view>
               <view class="payment-row">
                 <text class="payment-label">平台折扣</text>
@@ -350,7 +350,9 @@
   return Number.isFinite(amount) ? amount.toFixed(2) : '0.00'
 }
 
-const serviceUnitPrice = computed(() => formatMoney(selectedService.value.price))
+const serviceOriginalUnitPrice = computed(() => formatMoney(
+  selectedService.value.originalPrice ?? selectedService.value.price
+))
 
 const serviceOriginalSubtotal = computed(() => Number(
   selectedService.value.originalPrice ?? selectedService.value.price ?? 0
@@ -363,10 +365,10 @@
 ))
 
 const payableAmount = computed(() => Number(
-  Math.max(0, serviceOriginalSubtotal.value - platformDiscount.value).toFixed(2)
+  Math.max(0, serviceSubtotal.value).toFixed(2)
 ))
 
-const serviceSubtotalDisplay = computed(() => formatMoney(serviceSubtotal.value))
+const serviceOriginalSubtotalDisplay = computed(() => formatMoney(serviceOriginalSubtotal.value))
 const platformDiscountDisplay = computed(() => formatMoney(platformDiscount.value))
 const payableAmountDisplay = computed(() => formatMoney(payableAmount.value))
 

--
Gitblit v1.7.1