From aa67b3cb0fe26fab576f7710c978ff3594426cca Mon Sep 17 00:00:00 2001
From: longz <longzhaoz@qq.com>
Date: 星期四, 28 五月 2026 15:23:30 +0800
Subject: [PATCH] 添加微信公众号登录功能并优化系统配置

---
 backend/src/main/java/com/sinata/appointment/service/SysUserApplicationService.java |   78 ++++++++++++++++++++++++++++++++++++++-
 1 files changed, 76 insertions(+), 2 deletions(-)

diff --git a/backend/src/main/java/com/sinata/appointment/service/SysUserApplicationService.java b/backend/src/main/java/com/sinata/appointment/service/SysUserApplicationService.java
index b0d34d5..f7eb4de 100644
--- a/backend/src/main/java/com/sinata/appointment/service/SysUserApplicationService.java
+++ b/backend/src/main/java/com/sinata/appointment/service/SysUserApplicationService.java
@@ -6,6 +6,8 @@
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.sinata.appointment.constants.BizErrorCode;
 import com.sinata.appointment.dto.ResetPasswordRequest;
+import com.sinata.appointment.dto.CurrentUserPasswordUpdateRequest;
+import com.sinata.appointment.dto.CurrentUserUpdateRequest;
 import com.sinata.appointment.dto.SysUserCreateRequest;
 import com.sinata.appointment.dto.SysUserDetailVo;
 import com.sinata.appointment.dto.SysUserListVo;
@@ -35,6 +37,7 @@
 @Service
 @RequiredArgsConstructor
 public class SysUserApplicationService {
+    private static final String SUPER_ADMIN_USERNAME = "admin";
 
     private final SysUserMapper sysUserMapper;
     private final SysRoleMapper sysRoleMapper;
@@ -101,6 +104,42 @@
 
     public SysUserDetailVo getDetail(Long id, String uuid) {
         SysUser u = loadActiveWithUuid(id, uuid);
+        return toDetailVo(u);
+    }
+
+    public SysUserDetailVo getCurrentUserDetail(long currentUserId) {
+        SysUser u = loadActive(currentUserId);
+        return toDetailVo(u);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public SysUserDetailVo updateCurrentUser(long currentUserId, CurrentUserUpdateRequest req) {
+        SysUser u = loadActive(currentUserId);
+        u.setRealName(trimToNull(req.getRealName()));
+        u.setMobile(trimToNull(req.getMobile()));
+        u.setAvatarUrl(trimToNull(req.getAvatarUrl()));
+        sysUserMapper.updateById(u);
+        return toDetailVo(u);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public void updateCurrentPassword(long currentUserId, CurrentUserPasswordUpdateRequest req) {
+        SysUser u = loadActive(currentUserId);
+        if (!StringUtils.hasText(req.getOldPassword())) {
+            throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "原密码不能为空");
+        }
+        if (!bcrypt.matches(req.getOldPassword(), u.getPasswordHash())) {
+            throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "原密码不正确");
+        }
+        validateStrongPassword(req.getNewPassword());
+        if (!Objects.equals(req.getNewPassword(), req.getNewPasswordConfirm())) {
+            throw new BizException(BizErrorCode.PASSWORD_CONFIRM_MISMATCH, "两次输入的新密码不一致");
+        }
+        u.setPasswordHash(bcrypt.encode(req.getNewPassword()));
+        sysUserMapper.updateById(u);
+    }
+
+    private SysUserDetailVo toDetailVo(SysUser u) {
         SysUserDetailVo vo = new SysUserDetailVo();
         vo.setId(u.getId());
         vo.setUuid(u.getUuid());
@@ -109,7 +148,7 @@
         vo.setMobile(u.getMobile());
         vo.setAvatarUrl(u.getAvatarUrl());
         vo.setStatus(u.getStatus());
-        vo.setRoleIds(listRoleIds(id));
+        vo.setRoleIds(listRoleIds(u.getId()));
         return vo;
     }
 
@@ -123,6 +162,7 @@
         if (exists > 0) {
             throw new BizException(BizErrorCode.DUPLICATE_USERNAME, "登录账号已存在");
         }
+        validateStrongPassword(req.getPassword());
         validateRoleIds(req.getRoleIds());
 
         SysUser u = new SysUser();
@@ -157,6 +197,7 @@
 
     @Transactional(rollbackFor = Exception.class)
     public void resetPassword(Long id, ResetPasswordRequest req) {
+        validateStrongPassword(req.getNewPassword());
         if (!Objects.equals(req.getNewPassword(), req.getNewPasswordConfirm())) {
             throw new BizException(BizErrorCode.PASSWORD_CONFIRM_MISMATCH, "两次输入的新密码不一致");
         }
@@ -176,6 +217,9 @@
             throw new BizException(BizErrorCode.USER_SELF_FORBIDDEN, "不能禁用自己的账号");
         }
         SysUser u = loadActiveWithUuid(id, req.getUuid());
+        if (st == 0 && isSuperAdmin(u)) {
+            throw new BizException(BizErrorCode.AUTH_FORBIDDEN, "超级管理员账号不可禁用");
+        }
         u.setStatus(st);
         sysUserMapper.updateById(u);
         if (st == 0) {
@@ -189,6 +233,9 @@
             throw new BizException(BizErrorCode.USER_SELF_FORBIDDEN, "不能删除自己的账号");
         }
         SysUser u = loadActiveWithUuid(id, uuid);
+        if (isSuperAdmin(u)) {
+            throw new BizException(BizErrorCode.AUTH_FORBIDDEN, "超级管理员账号不可删除");
+        }
         sysUserRoleMapper.delete(
                 new LambdaQueryWrapper<SysUserRole>()
                         .eq(SysUserRole::getUserId, id)
@@ -205,11 +252,16 @@
     }
 
     private SysUser loadActiveWithUuid(Long id, String uuid) {
+        SysUser u = loadActive(id);
+        UuidAssert.match(u, uuid);
+        return u;
+    }
+
+    private SysUser loadActive(Long id) {
         SysUser u = sysUserMapper.selectById(id);
         if (u == null || u.getDelFlag() != null && u.getDelFlag() == 1) {
             throw new BizException(BizErrorCode.RESOURCE_VERIFY_FAILED, "资源不存在或无权访问");
         }
-        UuidAssert.match(u, uuid);
         return u;
     }
 
@@ -256,4 +308,26 @@
         String t = s.trim();
         return t.isEmpty() ? null : t;
     }
+
+    private static boolean isSuperAdmin(SysUser u) {
+        return u != null && SUPER_ADMIN_USERNAME.equalsIgnoreCase(u.getUsername());
+    }
+
+    private static void validateStrongPassword(String password) {
+        if (!StringUtils.hasText(password)) {
+            throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "密码不能为空");
+        }
+        String value = password.trim();
+        boolean ok =
+                value.length() >= 12
+                        && value.length() <= 64
+                        && value.matches(".*[a-z].*")
+                        && value.matches(".*[A-Z].*")
+                        && value.matches(".*[^A-Za-z0-9].*");
+        if (!ok) {
+            throw new BizException(
+                    BizErrorCode.AUTH_INVALID_CREDENTIALS,
+                    "请输入至少12位含大小写字母、特殊字符的密码");
+        }
+    }
 }

--
Gitblit v1.7.1