XuqmGroup-Server/demo-service/src/main/java/com/xuqm/demo/service/DemoAuthService.java
XuqmGroup 624554a173 fix: group history endpoint, user-only group list, reset-password, change-password path
- im-service: add GET /messages/group-history/{groupId} for group message history
- im-service: add findGroupHistory query to ImMessageRepository
- im-service: listGroups now filters by user membership (JSON_CONTAINS on member_ids)
- im-service: add groupHistory/listUserGroups/addGroupMember/removeGroupMember methods
- demo-service: add POST /auth/reset-password (unauthenticated forgot-password flow)
- demo-service: rename /user/reset-password → /user/change-password to match client

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 11:57:46 +08:00

134 行
5.4 KiB
Java

package com.xuqm.demo.service;
import com.fasterxml.jackson.databind.JsonNode;
import com.xuqm.common.exception.BusinessException;
import com.xuqm.common.security.JwtUtil;
import com.xuqm.demo.entity.DemoUserEntity;
import com.xuqm.demo.repository.DemoUserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.time.Instant;
import java.util.Map;
import java.util.UUID;
@Service
public class DemoAuthService {
private static final Logger log = LoggerFactory.getLogger(DemoAuthService.class);
private final DemoUserRepository userRepository;
private final JwtUtil jwtUtil;
private final PasswordEncoder passwordEncoder;
private final RestTemplate restTemplate;
@Value("${demo.im-service-url:http://xuqm-im-service:8082}")
private String imServiceUrl;
public DemoAuthService(DemoUserRepository userRepository,
JwtUtil jwtUtil,
PasswordEncoder passwordEncoder,
RestTemplate restTemplate) {
this.userRepository = userRepository;
this.jwtUtil = jwtUtil;
this.passwordEncoder = passwordEncoder;
this.restTemplate = restTemplate;
}
public record AuthResult(String demoToken, String imToken, UserProfile profile) {}
public record UserProfile(String appId, String userId, String nickname, String avatar, String gender) {}
@Transactional
public AuthResult register(String appId, String userId, String password, String nickname) {
if (userRepository.existsByAppIdAndUserId(appId, userId)) {
throw new BusinessException(409, "User already exists: " + userId);
}
DemoUserEntity user = new DemoUserEntity();
user.setId(UUID.randomUUID().toString());
user.setAppId(appId);
user.setUserId(userId);
user.setPasswordHash(passwordEncoder.encode(password));
user.setNickname(nickname != null ? nickname : userId);
user.setGender(DemoUserEntity.Gender.UNKNOWN);
user.setCreatedAt(Instant.now());
userRepository.save(user);
String demoToken = generateDemoToken(appId, userId);
String imToken = callImServiceLogin(appId, userId, user.getNickname());
return new AuthResult(demoToken, imToken, toProfile(user));
}
@Transactional(readOnly = true)
public AuthResult login(String appId, String userId, String password) {
DemoUserEntity user = userRepository.findByAppIdAndUserId(appId, userId)
.orElseThrow(() -> new BusinessException(401, "Invalid credentials"));
if (!passwordEncoder.matches(password, user.getPasswordHash())) {
throw new BusinessException(401, "Invalid credentials");
}
String demoToken = generateDemoToken(appId, userId);
String imToken = callImServiceLogin(appId, userId, user.getNickname());
return new AuthResult(demoToken, imToken, toProfile(user));
}
@Transactional
public void resetPassword(String appId, String userId, String newPassword) {
DemoUserEntity user = userRepository.findByAppIdAndUserId(appId, userId)
.orElseThrow(() -> new BusinessException(404, "User not found: " + userId));
user.setPasswordHash(passwordEncoder.encode(newPassword));
userRepository.save(user);
}
private String generateDemoToken(String appId, String userId) {
return jwtUtil.generate(userId, Map.of("appId", appId, "role", "USER"));
}
/**
* Calls im-service to ensure the IM account exists and obtain an IM token.
* GET {imServiceUrl}/api/im/auth/login?appId={appId}&userId={userId}&nickname={nickname}
* Response: {"code":200,"data":{"token":"..."}}
*/
private String callImServiceLogin(String appId, String userId, String nickname) {
String url = UriComponentsBuilder.fromHttpUrl(imServiceUrl)
.path("/api/im/auth/login")
.queryParam("appId", appId)
.queryParam("userId", userId)
.queryParam("nickname", nickname != null ? nickname : userId)
.toUriString();
try {
JsonNode response = restTemplate.getForObject(url, JsonNode.class);
if (response != null && response.path("code").asInt() == 200) {
return response.path("data").path("token").asText();
}
log.warn("im-service login returned unexpected response for appId={} userId={}: {}", appId, userId, response);
return null;
} catch (RestClientException e) {
log.error("Failed to call im-service login for appId={} userId={}: {}", appId, userId, e.getMessage());
return null;
}
}
private UserProfile toProfile(DemoUserEntity user) {
return new UserProfile(
user.getAppId(),
user.getUserId(),
user.getNickname(),
user.getAvatar(),
user.getGender() != null ? user.getGender().name() : DemoUserEntity.Gender.UNKNOWN.name()
);
}
}