diff --git a/backend/api-gateway/src/main/java/com/deepdata/gateway/infrastructure/casdoor/CasdoorUserService.java b/backend/api-gateway/src/main/java/com/deepdata/gateway/infrastructure/casdoor/CasdoorUserService.java index 4e61640..f0fb29d 100644 --- a/backend/api-gateway/src/main/java/com/deepdata/gateway/infrastructure/casdoor/CasdoorUserService.java +++ b/backend/api-gateway/src/main/java/com/deepdata/gateway/infrastructure/casdoor/CasdoorUserService.java @@ -84,6 +84,11 @@ public class CasdoorUserService { // 并发首登:另一请求已建号 → 复用,避免唯一键冲突 log.info("casdoor provision race on [{}], reusing existing user", username); created = findByUsername(username); + if (created == null) { + // 冲突来自 email 唯一键(用户名不冲突)→ 显式报错,避免 NPE + throw new CasdoorProvisionException( + "unique-key conflict but user not found (likely email collision): " + username); + } } log.info("casdoor auto-provisioned local user [{}] role={} groups={}", username, created.getRole(), created.getUserGroups()); @@ -111,7 +116,9 @@ public class CasdoorUserService { private User createUser(IdTokenClaims claims, String username, LocalDateTime now) { User user = new User(); user.setUsername(username); - user.setEmail(claims.email()); + // email 列有唯一约束:空串会互相冲突,空值统一落 NULL(NULL 不参与唯一约束) + String email = claims.email(); + user.setEmail(email == null || email.isBlank() ? null : email.trim()); user.setFullName(claims.name()); // 本地密码不可用:随机占位(登录统一走 Casdoor,本地 BCrypt 仅迁移期兜底) user.setPasswordHash(new BCryptPasswordEncoder(10).encode(UUID.randomUUID().toString()));