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() ); } }