From 700f9d0da314a4ea701fe9f68d305b67d72ec5fb Mon Sep 17 00:00:00 2001
From: longz <longzhaoz@qq.com>
Date: 星期三, 08 七月 2026 15:08:37 +0800
Subject: [PATCH] 各类修复fix

---
 backend/src/main/java/com/sinata/appointment/service/MiniVisitorApplicationService.java |   92 +++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 90 insertions(+), 2 deletions(-)

diff --git a/backend/src/main/java/com/sinata/appointment/service/MiniVisitorApplicationService.java b/backend/src/main/java/com/sinata/appointment/service/MiniVisitorApplicationService.java
index 70ad6c8..2f1e864 100644
--- a/backend/src/main/java/com/sinata/appointment/service/MiniVisitorApplicationService.java
+++ b/backend/src/main/java/com/sinata/appointment/service/MiniVisitorApplicationService.java
@@ -1,6 +1,7 @@
 package com.sinata.appointment.service;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.sinata.appointment.constants.AppointmentFieldCodes;
 import com.sinata.appointment.constants.BizErrorCode;
 import com.sinata.appointment.dto.MiniVisitorCreateRequest;
@@ -46,6 +47,7 @@
                         req.getIdCard(),
                         req.getGender(),
                         requiredCodes);
+        assertIdCardUniqueUnderUser(current.getId(), input.idCard(), null);
 
         MpVisitor row = new MpVisitor();
         row.setMpUserId(current.getId());
@@ -58,6 +60,7 @@
                         ? SensitiveDataUtil.encryptIdCard(input.idCard())
                         : "");
         row.setGender(input.gender() == null ? "" : input.gender());
+        row.setIsDefault(hasAnyActiveVisitor(current.getId()) ? 0 : 1);
         mpVisitorMapper.insert(row);
     }
 
@@ -74,6 +77,7 @@
                         req.getIdCard(),
                         req.getGender(),
                         requiredCodes);
+        assertIdCardUniqueUnderUser(current.getId(), input.idCard(), row.getId());
         row.setVisitorName(input.name() == null ? "" : input.name());
         row.setVisitorMobile(input.mobile() == null ? "" : input.mobile());
         row.setVisitorCompany(input.company() == null ? "" : input.company());
@@ -83,6 +87,31 @@
                         : "");
         row.setGender(input.gender() == null ? "" : input.gender());
         mpVisitorMapper.updateById(row);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public void setDefault(Long id, String uuid) {
+        MpUser current = miniAuthApplicationService.currentMpUser();
+        MpVisitor row = loadOwnedVisitor(id, uuid, current.getId());
+        mpVisitorMapper.update(
+                null,
+                new LambdaUpdateWrapper<MpVisitor>()
+                        .eq(MpVisitor::getMpUserId, current.getId())
+                        .eq(MpVisitor::getDelFlag, 0)
+                        .set(MpVisitor::getIsDefault, 0));
+        row.setIsDefault(1);
+        mpVisitorMapper.updateById(row);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public void delete(Long id, String uuid) {
+        MpUser current = miniAuthApplicationService.currentMpUser();
+        MpVisitor row = loadOwnedVisitor(id, uuid, current.getId());
+        boolean wasDefault = isDefault(row);
+        mpVisitorMapper.deleteById(row.getId());
+        if (wasDefault) {
+            resetEarliestVisitorAsDefault(current.getId());
+        }
     }
 
     public MiniVisitorDetailVo detail(Long id, String uuid) {
@@ -99,6 +128,7 @@
                         ? SensitiveDataUtil.decryptIdCard(row.getIdCardCipher())
                         : "");
         vo.setGender(row.getGender());
+        vo.setIsDefault(isDefault(row));
         return vo;
     }
 
@@ -110,6 +140,7 @@
                         new LambdaQueryWrapper<MpVisitor>()
                                 .eq(MpVisitor::getMpUserId, current.getId())
                                 .eq(MpVisitor::getDelFlag, 0)
+                                .orderByDesc(MpVisitor::getIsDefault)
                                 .orderByDesc(MpVisitor::getCreateTime)
                                 .orderByDesc(MpVisitor::getId));
         return rows.stream().map(row -> toListVo(row, requiredCodes)).collect(Collectors.toList());
@@ -128,8 +159,36 @@
             vo.setIdCardMasked(null);
         }
         vo.setVisitorCompany(row.getVisitorCompany());
+        vo.setIsDefault(isDefault(row));
         vo.setMissingFieldCodes(resolveMissingFieldCodes(row, requiredCodes));
         return vo;
+    }
+
+    private boolean isDefault(MpVisitor row) {
+        return row != null && row.getIsDefault() != null && row.getIsDefault() == 1;
+    }
+
+    private boolean hasAnyActiveVisitor(Long mpUserId) {
+        return mpVisitorMapper.selectCount(
+                        new LambdaQueryWrapper<MpVisitor>()
+                                .eq(MpVisitor::getMpUserId, mpUserId)
+                                .eq(MpVisitor::getDelFlag, 0))
+                > 0;
+    }
+
+    private void resetEarliestVisitorAsDefault(Long mpUserId) {
+        MpVisitor next =
+                mpVisitorMapper.selectOne(
+                        new LambdaQueryWrapper<MpVisitor>()
+                                .eq(MpVisitor::getMpUserId, mpUserId)
+                                .eq(MpVisitor::getDelFlag, 0)
+                                .orderByAsc(MpVisitor::getCreateTime)
+                                .orderByAsc(MpVisitor::getId)
+                                .last("limit 1"));
+        if (next != null) {
+            next.setIsDefault(1);
+            mpVisitorMapper.updateById(next);
+        }
     }
 
     private List<String> resolveMissingFieldCodes(MpVisitor row, Set<String> requiredCodes) {
@@ -221,8 +280,8 @@
         if (requiredCodes.contains(AppointmentFieldCodes.MOBILE) && !StringUtils.hasText(mobile)) {
             throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "来访人电话不能为空");
         }
-        if (StringUtils.hasText(mobile) && !mobile.matches("^\\d{11}$")) {
-            throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "来访人电话须为11位数字");
+        if (StringUtils.hasText(mobile) && !mobile.matches("^1[3-9]\\d{9}$")) {
+            throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "来访人电话格式不正确");
         }
 
         if (requiredCodes.contains(AppointmentFieldCodes.COMPANY) && !StringUtils.hasText(company)) {
@@ -245,6 +304,35 @@
         return new VisitorNormalizedInput(name, mobile, company, idCardPlain, genderText);
     }
 
+    private void assertIdCardUniqueUnderUser(Long mpUserId, String idCardPlain, Long excludeVisitorId) {
+        String normalizedIdCard = normalizeIdCard(idCardPlain);
+        if (!StringUtils.hasText(normalizedIdCard) || mpUserId == null) {
+            return;
+        }
+        List<MpVisitor> rows =
+                mpVisitorMapper.selectList(
+                        new LambdaQueryWrapper<MpVisitor>()
+                                .eq(MpVisitor::getMpUserId, mpUserId)
+                                .eq(MpVisitor::getDelFlag, 0));
+        for (MpVisitor row : rows) {
+            if (excludeVisitorId != null && excludeVisitorId.equals(row.getId())) {
+                continue;
+            }
+            if (!StringUtils.hasText(row.getIdCardCipher())) {
+                continue;
+            }
+            String existing = normalizeIdCard(SensitiveDataUtil.decryptIdCard(row.getIdCardCipher()));
+            if (normalizedIdCard.equals(existing)) {
+                throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "同一账号下身份证号码不可重复");
+            }
+        }
+    }
+
+    private String normalizeIdCard(String idCard) {
+        String value = trimToNull(idCard);
+        return value == null ? null : value.toUpperCase();
+    }
+
     private record VisitorNormalizedInput(
             String name, String mobile, String company, String idCard, String gender) {}
 }

--
Gitblit v1.7.1