feat(admin): 添加用户管理相关文件
添加用户管理视图、API和状态管理文件
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>io.destiny</groupId>
|
||||
<artifactId>everything-is-suitable-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>everything-is-suitable-client</artifactId>
|
||||
|
||||
<name>Everything Is Suitable Client</name>
|
||||
<description>Client for Everything Is Suitable API</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.destiny</groupId>
|
||||
<artifactId>everything-is-suitable-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-commons</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-crypto</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>dysmsapi20170525</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>tea-openapi</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>tea-util</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
package io.destiny.client.client;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import io.destiny.client.config.DouyinMiniProgramConfig;
|
||||
import io.destiny.client.dto.WechatAuthResponse;
|
||||
import io.destiny.client.core.exception.ClientUserAuthExceptionCode;
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.reactive.function.client.WebClientResponseException;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Component
|
||||
public class DouyinMiniProgramClient {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DouyinMiniProgramClient.class);
|
||||
|
||||
private static final String AUTH_URL = "https://developer.toutiao.com/api/apps/jscode2session";
|
||||
|
||||
private final WebClient webClient;
|
||||
|
||||
private final DouyinMiniProgramConfig config;
|
||||
|
||||
public DouyinMiniProgramClient(DouyinMiniProgramConfig config, WebClient webClient) {
|
||||
this.config = config;
|
||||
this.webClient = webClient != null ? webClient : WebClient.builder().build();
|
||||
}
|
||||
|
||||
public Mono<WechatAuthResponse> auth(String code) {
|
||||
logger.info("Douyin mini program auth request, code: {}", code);
|
||||
|
||||
return webClient.get()
|
||||
.uri(AUTH_URL, uriBuilder -> uriBuilder
|
||||
.queryParam("appid", config.getAppId())
|
||||
.queryParam("secret", config.getAppSecret())
|
||||
.queryParam("code", code)
|
||||
.queryParam("anonymous_code", "")
|
||||
.build())
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.retrieve()
|
||||
.bodyToMono(String.class)
|
||||
.flatMap(this::parseAuthResponse)
|
||||
.onErrorResume(WebClientResponseException.class, ex -> {
|
||||
logger.error("Douyin auth request failed, status: {}, body: {}", ex.getStatusCode(), ex.getResponseBodyAsString());
|
||||
return Mono.error(new ApplicationException(ClientUserAuthExceptionCode.CLIENT_DOUYIN_AUTH_FAILED));
|
||||
})
|
||||
.onErrorResume(Exception.class, ex -> {
|
||||
logger.error("Douyin auth request error", ex);
|
||||
return Mono.error(new ApplicationException(ClientUserAuthExceptionCode.CLIENT_DOUYIN_AUTH_FAILED));
|
||||
});
|
||||
}
|
||||
|
||||
private Mono<WechatAuthResponse> parseAuthResponse(String responseBody) {
|
||||
try {
|
||||
com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
JsonNode jsonNode = objectMapper.readTree(responseBody);
|
||||
|
||||
String errNo = jsonNode.has("err_no") ? jsonNode.get("err_no").asText() : null;
|
||||
|
||||
if (errNo != null && !"0".equals(errNo)) {
|
||||
String errMsg = jsonNode.has("err_tips") ? jsonNode.get("err_tips").asText() : "Unknown error";
|
||||
logger.error("Douyin auth failed, err_no: {}, err_tips: {}", errNo, errMsg);
|
||||
return Mono.error(new ApplicationException(ClientUserAuthExceptionCode.CLIENT_DOUYIN_AUTH_FAILED));
|
||||
}
|
||||
|
||||
JsonNode dataNode = jsonNode.get("data");
|
||||
if (dataNode == null) {
|
||||
logger.error("Douyin auth response missing data field");
|
||||
return Mono.error(new ApplicationException(ClientUserAuthExceptionCode.CLIENT_DOUYIN_AUTH_FAILED));
|
||||
}
|
||||
|
||||
WechatAuthResponse response = new WechatAuthResponse();
|
||||
response.setOpenId(dataNode.get("openid").asText());
|
||||
if (dataNode.has("unionid")) {
|
||||
response.setUnionId(dataNode.get("unionid").asText());
|
||||
}
|
||||
response.setSessionKey(dataNode.get("session_key").asText());
|
||||
|
||||
logger.info("Douyin auth success, openId: {}, unionId: {}", response.getOpenId(), response.getUnionId());
|
||||
return Mono.just(response);
|
||||
} catch (Exception e) {
|
||||
logger.error("Parse Douyin auth response failed", e);
|
||||
return Mono.error(new ApplicationException(ClientUserAuthExceptionCode.CLIENT_DOUYIN_AUTH_FAILED));
|
||||
}
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package io.destiny.client.client;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import io.destiny.client.config.WechatMiniProgramConfig;
|
||||
import io.destiny.client.dto.WechatAuthResponse;
|
||||
import io.destiny.client.core.exception.ClientUserAuthExceptionCode;
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.reactive.function.client.WebClientResponseException;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Component
|
||||
public class WechatMiniProgramClient {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(WechatMiniProgramClient.class);
|
||||
|
||||
private static final String AUTH_URL = "https://api.weixin.qq.com/sns/jscode2session";
|
||||
|
||||
private final WebClient webClient;
|
||||
|
||||
private final WechatMiniProgramConfig config;
|
||||
|
||||
public WechatMiniProgramClient(WechatMiniProgramConfig config, WebClient webClient) {
|
||||
this.config = config;
|
||||
this.webClient = webClient != null ? webClient : WebClient.builder().build();
|
||||
}
|
||||
|
||||
public Mono<WechatAuthResponse> auth(String code) {
|
||||
logger.info("WeChat mini program auth request, code: {}", code);
|
||||
|
||||
return webClient.get()
|
||||
.uri(AUTH_URL, uriBuilder -> uriBuilder
|
||||
.queryParam("appid", config.getAppId())
|
||||
.queryParam("secret", config.getAppSecret())
|
||||
.queryParam("js_code", code)
|
||||
.queryParam("grant_type", "authorization_code")
|
||||
.build())
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.retrieve()
|
||||
.bodyToMono(String.class)
|
||||
.flatMap(this::parseAuthResponse)
|
||||
.onErrorResume(WebClientResponseException.class, ex -> {
|
||||
logger.error("WeChat auth request failed, status: {}, body: {}", ex.getStatusCode(), ex.getResponseBodyAsString());
|
||||
return Mono.error(new ApplicationException(ClientUserAuthExceptionCode.CLIENT_WECHAT_AUTH_FAILED));
|
||||
})
|
||||
.onErrorResume(Exception.class, ex -> {
|
||||
logger.error("WeChat auth request error", ex);
|
||||
return Mono.error(new ApplicationException(ClientUserAuthExceptionCode.CLIENT_WECHAT_AUTH_FAILED));
|
||||
});
|
||||
}
|
||||
|
||||
private Mono<WechatAuthResponse> parseAuthResponse(String responseBody) {
|
||||
try {
|
||||
com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
JsonNode jsonNode = objectMapper.readTree(responseBody);
|
||||
|
||||
String errCode = jsonNode.has("errcode") ? jsonNode.get("errcode").asText() : null;
|
||||
|
||||
if (errCode != null && !"0".equals(errCode)) {
|
||||
String errMsg = jsonNode.has("errmsg") ? jsonNode.get("errmsg").asText() : "Unknown error";
|
||||
logger.error("WeChat auth failed, errcode: {}, errmsg: {}", errCode, errMsg);
|
||||
return Mono.error(new ApplicationException(ClientUserAuthExceptionCode.CLIENT_WECHAT_AUTH_FAILED));
|
||||
}
|
||||
|
||||
WechatAuthResponse response = new WechatAuthResponse();
|
||||
response.setOpenId(jsonNode.get("openid").asText());
|
||||
if (jsonNode.has("unionid")) {
|
||||
response.setUnionId(jsonNode.get("unionid").asText());
|
||||
}
|
||||
response.setSessionKey(jsonNode.get("session_key").asText());
|
||||
|
||||
logger.info("WeChat auth success, openId: {}, unionId: {}", response.getOpenId(), response.getUnionId());
|
||||
return Mono.just(response);
|
||||
} catch (Exception e) {
|
||||
logger.error("Parse WeChat auth response failed", e);
|
||||
return Mono.error(new ApplicationException(ClientUserAuthExceptionCode.CLIENT_WECHAT_AUTH_FAILED));
|
||||
}
|
||||
}
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
package io.destiny.client.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@ConfigurationProperties(prefix = "client.sms.aliyun")
|
||||
public class AliyunSmsConfig {
|
||||
|
||||
private String accessKeyId;
|
||||
|
||||
private String accessKeySecret;
|
||||
|
||||
private String endpoint;
|
||||
|
||||
private String signName;
|
||||
|
||||
private String templateCode;
|
||||
|
||||
private Integer expireMinutes = 10;
|
||||
|
||||
private Integer sendIntervalSeconds = 60;
|
||||
|
||||
private Integer maxSendCountPerDay = 10;
|
||||
|
||||
private Integer maxVerifyAttempts = 5;
|
||||
|
||||
private Integer lockMinutes = 10;
|
||||
|
||||
public String getAccessKeyId() {
|
||||
return accessKeyId;
|
||||
}
|
||||
|
||||
public void setAccessKeyId(String accessKeyId) {
|
||||
this.accessKeyId = accessKeyId;
|
||||
}
|
||||
|
||||
public String getAccessKeySecret() {
|
||||
return accessKeySecret;
|
||||
}
|
||||
|
||||
public void setAccessKeySecret(String accessKeySecret) {
|
||||
this.accessKeySecret = accessKeySecret;
|
||||
}
|
||||
|
||||
public String getEndpoint() {
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
public void setEndpoint(String endpoint) {
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
public String getSignName() {
|
||||
return signName;
|
||||
}
|
||||
|
||||
public void setSignName(String signName) {
|
||||
this.signName = signName;
|
||||
}
|
||||
|
||||
public String getTemplateCode() {
|
||||
return templateCode;
|
||||
}
|
||||
|
||||
public void setTemplateCode(String templateCode) {
|
||||
this.templateCode = templateCode;
|
||||
}
|
||||
|
||||
public Integer getExpireMinutes() {
|
||||
return expireMinutes;
|
||||
}
|
||||
|
||||
public void setExpireMinutes(Integer expireMinutes) {
|
||||
this.expireMinutes = expireMinutes;
|
||||
}
|
||||
|
||||
public Integer getSendIntervalSeconds() {
|
||||
return sendIntervalSeconds;
|
||||
}
|
||||
|
||||
public void setSendIntervalSeconds(Integer sendIntervalSeconds) {
|
||||
this.sendIntervalSeconds = sendIntervalSeconds;
|
||||
}
|
||||
|
||||
public Integer getMaxSendCountPerDay() {
|
||||
return maxSendCountPerDay;
|
||||
}
|
||||
|
||||
public void setMaxSendCountPerDay(Integer maxSendCountPerDay) {
|
||||
this.maxSendCountPerDay = maxSendCountPerDay;
|
||||
}
|
||||
|
||||
public Integer getMaxVerifyAttempts() {
|
||||
return maxVerifyAttempts;
|
||||
}
|
||||
|
||||
public void setMaxVerifyAttempts(Integer maxVerifyAttempts) {
|
||||
this.maxVerifyAttempts = maxVerifyAttempts;
|
||||
}
|
||||
|
||||
public Integer getLockMinutes() {
|
||||
return lockMinutes;
|
||||
}
|
||||
|
||||
public void setLockMinutes(Integer lockMinutes) {
|
||||
this.lockMinutes = lockMinutes;
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package io.destiny.client.config;
|
||||
|
||||
public class DouyinConfig {
|
||||
|
||||
private String appId;
|
||||
|
||||
private String appSecret;
|
||||
|
||||
public String getAppId() {
|
||||
return appId;
|
||||
}
|
||||
|
||||
public void setAppId(String appId) {
|
||||
this.appId = appId;
|
||||
}
|
||||
|
||||
public String getAppSecret() {
|
||||
return appSecret;
|
||||
}
|
||||
|
||||
public void setAppSecret(String appSecret) {
|
||||
this.appSecret = appSecret;
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package io.destiny.client.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@ConfigurationProperties(prefix = "client.douyin")
|
||||
public class DouyinMiniProgramConfig {
|
||||
|
||||
private String appId;
|
||||
|
||||
private String appSecret;
|
||||
|
||||
public String getAppId() {
|
||||
return appId;
|
||||
}
|
||||
|
||||
public void setAppId(String appId) {
|
||||
this.appId = appId;
|
||||
}
|
||||
|
||||
public String getAppSecret() {
|
||||
return appSecret;
|
||||
}
|
||||
|
||||
public void setAppSecret(String appSecret) {
|
||||
this.appSecret = appSecret;
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package io.destiny.client.config;
|
||||
|
||||
import com.aliyun.dysmsapi20170525.Client;
|
||||
import com.aliyun.teaopenapi.models.Config;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class SmsClientConfig {
|
||||
|
||||
@Bean
|
||||
public Client smsClient(AliyunSmsConfig smsConfig) throws Exception {
|
||||
Config config = new Config()
|
||||
.setAccessKeyId(smsConfig.getAccessKeyId())
|
||||
.setAccessKeySecret(smsConfig.getAccessKeySecret())
|
||||
.setEndpoint(smsConfig.getEndpoint());
|
||||
return new Client(config);
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package io.destiny.client.config;
|
||||
|
||||
public class SmsConfig {
|
||||
|
||||
private String accessKeyId;
|
||||
|
||||
private String accessKeySecret;
|
||||
|
||||
private String signName;
|
||||
|
||||
private String templateCode;
|
||||
|
||||
public String getAccessKeyId() {
|
||||
return accessKeyId;
|
||||
}
|
||||
|
||||
public void setAccessKeyId(String accessKeyId) {
|
||||
this.accessKeyId = accessKeyId;
|
||||
}
|
||||
|
||||
public String getAccessKeySecret() {
|
||||
return accessKeySecret;
|
||||
}
|
||||
|
||||
public void setAccessKeySecret(String accessKeySecret) {
|
||||
this.accessKeySecret = accessKeySecret;
|
||||
}
|
||||
|
||||
public String getSignName() {
|
||||
return signName;
|
||||
}
|
||||
|
||||
public void setSignName(String signName) {
|
||||
this.signName = signName;
|
||||
}
|
||||
|
||||
public String getTemplateCode() {
|
||||
return templateCode;
|
||||
}
|
||||
|
||||
public void setTemplateCode(String templateCode) {
|
||||
this.templateCode = templateCode;
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package io.destiny.client.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
@Configuration
|
||||
public class WebClientConfig {
|
||||
|
||||
@Bean
|
||||
public WebClient webClient() {
|
||||
return WebClient.builder().build();
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package io.destiny.client.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@ConfigurationProperties(prefix = "client.wechat")
|
||||
public class WechatMiniProgramConfig {
|
||||
|
||||
private String appId;
|
||||
|
||||
private String appSecret;
|
||||
|
||||
public String getAppId() {
|
||||
return appId;
|
||||
}
|
||||
|
||||
public void setAppId(String appId) {
|
||||
this.appId = appId;
|
||||
}
|
||||
|
||||
public String getAppSecret() {
|
||||
return appSecret;
|
||||
}
|
||||
|
||||
public void setAppSecret(String appSecret) {
|
||||
this.appSecret = appSecret;
|
||||
}
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
package io.destiny.client.core.domain;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import io.destiny.common.primitive.IpAddress;
|
||||
import io.destiny.common.utils.SnowflakeId;
|
||||
|
||||
/**
|
||||
* Client Log 客户端用户登录日志
|
||||
*
|
||||
* @author zhangxiang
|
||||
* @date 2023/12/15
|
||||
*/
|
||||
public class ClientLoginLog {
|
||||
|
||||
private Long id;
|
||||
|
||||
private Long clientUserId;
|
||||
|
||||
private IpAddress ipAddress;
|
||||
|
||||
private LocalDateTime loginTime;
|
||||
|
||||
private LocalDateTime logoutTime;
|
||||
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
private LocalDateTime deletedAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getClientUserId() {
|
||||
return clientUserId;
|
||||
}
|
||||
|
||||
public void setClientUserId(Long clientUserId) {
|
||||
this.clientUserId = clientUserId;
|
||||
}
|
||||
|
||||
public IpAddress getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(IpAddress ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
public LocalDateTime getLoginTime() {
|
||||
return loginTime;
|
||||
}
|
||||
|
||||
public void setLoginTime(LocalDateTime loginTime) {
|
||||
this.loginTime = loginTime;
|
||||
}
|
||||
|
||||
public LocalDateTime getLogoutTime() {
|
||||
return logoutTime;
|
||||
}
|
||||
|
||||
public void setLogoutTime(LocalDateTime logoutTime) {
|
||||
this.logoutTime = logoutTime;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getDeletedAt() {
|
||||
return deletedAt;
|
||||
}
|
||||
|
||||
public void setDeletedAt(LocalDateTime deletedAt) {
|
||||
this.deletedAt = deletedAt;
|
||||
}
|
||||
|
||||
public ClientLoginLog() {
|
||||
}
|
||||
|
||||
public ClientLoginLog(Long clientUserId, IpAddress ipAddress) {
|
||||
this.id = SnowflakeId.nextId();
|
||||
this.clientUserId = clientUserId;
|
||||
this.ipAddress = ipAddress;
|
||||
this.loginTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public void generateId() {
|
||||
this.id = SnowflakeId.nextId();
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
this.deletedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public boolean isLogoutTimeNull() {
|
||||
return this.logoutTime == null;
|
||||
}
|
||||
|
||||
}
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
package io.destiny.client.core.domain;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import io.destiny.common.primitive.EmailAddress;
|
||||
import io.destiny.common.primitive.PhoneNumber;
|
||||
import io.destiny.common.utils.SnowflakeId;
|
||||
|
||||
/**
|
||||
* Client User
|
||||
*
|
||||
* @author zhangxiang
|
||||
* @date 2023/12/15
|
||||
*/
|
||||
public class ClientUser {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
private EmailAddress email;
|
||||
|
||||
private PhoneNumber phone;
|
||||
|
||||
private String gender;
|
||||
|
||||
private LocalDateTime birthTime;
|
||||
|
||||
private String wechatOpenId;
|
||||
|
||||
private String wechatUnionId;
|
||||
|
||||
private String douyinOpenId;
|
||||
|
||||
private String douyinUnionId;
|
||||
|
||||
private Boolean phoneVerified;
|
||||
|
||||
private String nickname;
|
||||
|
||||
private String avatarUrl;
|
||||
|
||||
private String country;
|
||||
|
||||
private String province;
|
||||
|
||||
private String city;
|
||||
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
private LocalDateTime deletedAt;
|
||||
|
||||
private List<ClientLoginLog> loginLogs;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public EmailAddress getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(EmailAddress email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public PhoneNumber getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(PhoneNumber phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getGender() {
|
||||
return gender;
|
||||
}
|
||||
|
||||
public void setGender(String gender) {
|
||||
this.gender = gender;
|
||||
}
|
||||
|
||||
public LocalDateTime getBirthTime() {
|
||||
return birthTime;
|
||||
}
|
||||
|
||||
public void setBirthTime(LocalDateTime birthTime) {
|
||||
this.birthTime = birthTime;
|
||||
}
|
||||
|
||||
public String getWechatOpenId() {
|
||||
return wechatOpenId;
|
||||
}
|
||||
|
||||
public void setWechatOpenId(String wechatOpenId) {
|
||||
this.wechatOpenId = wechatOpenId;
|
||||
}
|
||||
|
||||
public String getWechatUnionId() {
|
||||
return wechatUnionId;
|
||||
}
|
||||
|
||||
public void setWechatUnionId(String wechatUnionId) {
|
||||
this.wechatUnionId = wechatUnionId;
|
||||
}
|
||||
|
||||
public String getDouyinOpenId() {
|
||||
return douyinOpenId;
|
||||
}
|
||||
|
||||
public void setDouyinOpenId(String douyinOpenId) {
|
||||
this.douyinOpenId = douyinOpenId;
|
||||
}
|
||||
|
||||
public String getDouyinUnionId() {
|
||||
return douyinUnionId;
|
||||
}
|
||||
|
||||
public void setDouyinUnionId(String douyinUnionId) {
|
||||
this.douyinUnionId = douyinUnionId;
|
||||
}
|
||||
|
||||
public Boolean getPhoneVerified() {
|
||||
return phoneVerified;
|
||||
}
|
||||
|
||||
public void setPhoneVerified(Boolean phoneVerified) {
|
||||
this.phoneVerified = phoneVerified;
|
||||
}
|
||||
|
||||
public String getNickname() {
|
||||
return nickname;
|
||||
}
|
||||
|
||||
public void setNickname(String nickname) {
|
||||
this.nickname = nickname;
|
||||
}
|
||||
|
||||
public String getAvatarUrl() {
|
||||
return avatarUrl;
|
||||
}
|
||||
|
||||
public void setAvatarUrl(String avatarUrl) {
|
||||
this.avatarUrl = avatarUrl;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getDeletedAt() {
|
||||
return deletedAt;
|
||||
}
|
||||
|
||||
public void setDeletedAt(LocalDateTime deletedAt) {
|
||||
this.deletedAt = deletedAt;
|
||||
}
|
||||
|
||||
public List<ClientLoginLog> getLoginLogs() {
|
||||
return loginLogs;
|
||||
}
|
||||
|
||||
public void setLoginLogs(List<ClientLoginLog> loginLogs) {
|
||||
this.loginLogs = loginLogs;
|
||||
}
|
||||
|
||||
public String getCountry() {
|
||||
return country;
|
||||
}
|
||||
|
||||
public void setCountry(String country) {
|
||||
this.country = country;
|
||||
}
|
||||
|
||||
public String getProvince() {
|
||||
return province;
|
||||
}
|
||||
|
||||
public void setProvince(String province) {
|
||||
this.province = province;
|
||||
}
|
||||
|
||||
public String getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
public void setCity(String city) {
|
||||
this.city = city;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成用户ID
|
||||
*/
|
||||
public void generateId() {
|
||||
this.id = SnowflakeId.nextId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户
|
||||
*/
|
||||
public void delete() {
|
||||
this.deletedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
package io.destiny.client.core.domain;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import io.destiny.common.utils.SnowflakeId;
|
||||
|
||||
public class SmsVerificationCode {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String phone;
|
||||
|
||||
private String code;
|
||||
|
||||
private String type;
|
||||
|
||||
private LocalDateTime expireTime;
|
||||
|
||||
private Boolean verified;
|
||||
|
||||
private Integer verifyCount;
|
||||
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
private LocalDateTime deletedAt;
|
||||
|
||||
public SmsVerificationCode() {
|
||||
}
|
||||
|
||||
public SmsVerificationCode(String phone, String code, String type, LocalDateTime expireTime) {
|
||||
this.phone = phone;
|
||||
this.code = code;
|
||||
this.type = type;
|
||||
this.expireTime = expireTime;
|
||||
this.createdAt = LocalDateTime.now();
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
this.verified = false;
|
||||
this.verifyCount = 0;
|
||||
this.id = SnowflakeId.nextId();
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public LocalDateTime getExpireTime() {
|
||||
return expireTime;
|
||||
}
|
||||
|
||||
public void setExpireTime(LocalDateTime expireTime) {
|
||||
this.expireTime = expireTime;
|
||||
}
|
||||
|
||||
public Boolean getVerified() {
|
||||
return verified;
|
||||
}
|
||||
|
||||
public void setVerified(Boolean verified) {
|
||||
this.verified = verified;
|
||||
}
|
||||
|
||||
public Integer getVerifyCount() {
|
||||
return verifyCount;
|
||||
}
|
||||
|
||||
public void setVerifyCount(Integer verifyCount) {
|
||||
this.verifyCount = verifyCount;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getDeletedAt() {
|
||||
return deletedAt;
|
||||
}
|
||||
|
||||
public void setDeletedAt(LocalDateTime deletedAt) {
|
||||
this.deletedAt = deletedAt;
|
||||
}
|
||||
|
||||
public void generateId() {
|
||||
this.id = SnowflakeId.nextId();
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
this.deletedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public void markAsVerified() {
|
||||
this.verified = true;
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public void incrementVerifyCount() {
|
||||
if (this.verifyCount == null) {
|
||||
this.verifyCount = 0;
|
||||
}
|
||||
this.verifyCount++;
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
return LocalDateTime.now().isAfter(this.expireTime);
|
||||
}
|
||||
|
||||
public boolean isVerified() {
|
||||
return Boolean.TRUE.equals(this.verified);
|
||||
}
|
||||
|
||||
public boolean isDeleted() {
|
||||
return this.deletedAt != null;
|
||||
}
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
package io.destiny.client.core.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import io.destiny.common.utils.SnowflakeId;
|
||||
|
||||
public class Subscription {
|
||||
|
||||
private Long id;
|
||||
|
||||
private Long clientUserId;
|
||||
|
||||
private String planType;
|
||||
|
||||
private LocalDate startDate;
|
||||
|
||||
private LocalDate endDate;
|
||||
|
||||
private String status;
|
||||
|
||||
private BigDecimal amount;
|
||||
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
private LocalDateTime deletedAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getClientUserId() {
|
||||
return clientUserId;
|
||||
}
|
||||
|
||||
public void setClientUserId(Long clientUserId) {
|
||||
this.clientUserId = clientUserId;
|
||||
}
|
||||
|
||||
public String getPlanType() {
|
||||
return planType;
|
||||
}
|
||||
|
||||
public void setPlanType(String planType) {
|
||||
this.planType = planType;
|
||||
}
|
||||
|
||||
public LocalDate getStartDate() {
|
||||
return startDate;
|
||||
}
|
||||
|
||||
public void setStartDate(LocalDate startDate) {
|
||||
this.startDate = startDate;
|
||||
}
|
||||
|
||||
public LocalDate getEndDate() {
|
||||
return endDate;
|
||||
}
|
||||
|
||||
public void setEndDate(LocalDate endDate) {
|
||||
this.endDate = endDate;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(BigDecimal amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getDeletedAt() {
|
||||
return deletedAt;
|
||||
}
|
||||
|
||||
public void setDeletedAt(LocalDateTime deletedAt) {
|
||||
this.deletedAt = deletedAt;
|
||||
}
|
||||
|
||||
public void generateId() {
|
||||
this.id = SnowflakeId.nextId();
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
this.deletedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return "ACTIVE".equals(status) && endDate != null && endDate.isAfter(LocalDate.now());
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
return endDate != null && endDate.isBefore(LocalDate.now());
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
package io.destiny.client.core.domain;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import io.destiny.common.utils.SnowflakeId;
|
||||
|
||||
public class UserBinding {
|
||||
|
||||
private Long id;
|
||||
|
||||
private Long userId;
|
||||
|
||||
private String platform;
|
||||
|
||||
private String platformOpenId;
|
||||
|
||||
private String platformUnionId;
|
||||
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
private LocalDateTime deletedAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getPlatform() {
|
||||
return platform;
|
||||
}
|
||||
|
||||
public void setPlatform(String platform) {
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
public String getPlatformOpenId() {
|
||||
return platformOpenId;
|
||||
}
|
||||
|
||||
public void setPlatformOpenId(String platformOpenId) {
|
||||
this.platformOpenId = platformOpenId;
|
||||
}
|
||||
|
||||
public String getPlatformUnionId() {
|
||||
return platformUnionId;
|
||||
}
|
||||
|
||||
public void setPlatformUnionId(String platformUnionId) {
|
||||
this.platformUnionId = platformUnionId;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getDeletedAt() {
|
||||
return deletedAt;
|
||||
}
|
||||
|
||||
public void setDeletedAt(LocalDateTime deletedAt) {
|
||||
this.deletedAt = deletedAt;
|
||||
}
|
||||
|
||||
public void generateId() {
|
||||
this.id = SnowflakeId.nextId();
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
this.deletedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public boolean isDeleted() {
|
||||
return this.deletedAt != null;
|
||||
}
|
||||
|
||||
public boolean isWechatPlatform() {
|
||||
return "wechat".equals(this.platform);
|
||||
}
|
||||
|
||||
public boolean isDouyinPlatform() {
|
||||
return "douyin".equals(this.platform);
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package io.destiny.client.core.domain.query;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import io.destiny.common.primitive.IpAddress;
|
||||
|
||||
public class ClientLoginLogQuery {
|
||||
|
||||
private Long clientUserId;
|
||||
|
||||
private IpAddress ipAddress;
|
||||
|
||||
private LocalDateTime loginTimeStart;
|
||||
|
||||
private LocalDateTime loginTimeEnd;
|
||||
|
||||
private LocalDateTime logoutTimeStart;
|
||||
|
||||
private LocalDateTime logoutTimeEnd;
|
||||
|
||||
public Long getClientUserId() {
|
||||
return clientUserId;
|
||||
}
|
||||
|
||||
public void setClientUserId(Long clientUserId) {
|
||||
this.clientUserId = clientUserId;
|
||||
}
|
||||
|
||||
public IpAddress getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(IpAddress ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
public LocalDateTime getLoginTimeStart() {
|
||||
return loginTimeStart;
|
||||
}
|
||||
|
||||
public void setLoginTimeStart(LocalDateTime loginTimeStart) {
|
||||
this.loginTimeStart = loginTimeStart;
|
||||
}
|
||||
|
||||
public LocalDateTime getLoginTimeEnd() {
|
||||
return loginTimeEnd;
|
||||
}
|
||||
|
||||
public void setLoginTimeEnd(LocalDateTime loginTimeEnd) {
|
||||
this.loginTimeEnd = loginTimeEnd;
|
||||
}
|
||||
|
||||
public LocalDateTime getLogoutTimeStart() {
|
||||
return logoutTimeStart;
|
||||
}
|
||||
|
||||
public void setLogoutTimeStart(LocalDateTime logoutTimeStart) {
|
||||
this.logoutTimeStart = logoutTimeStart;
|
||||
}
|
||||
|
||||
public LocalDateTime getLogoutTimeEnd() {
|
||||
return logoutTimeEnd;
|
||||
}
|
||||
|
||||
public void setLogoutTimeEnd(LocalDateTime logoutTimeEnd) {
|
||||
this.logoutTimeEnd = logoutTimeEnd;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package io.destiny.client.core.domain.query;
|
||||
|
||||
import io.destiny.common.primitive.IpAddress;
|
||||
|
||||
/**
|
||||
* 客户端用户登录
|
||||
*/
|
||||
public class ClientUserLogin {
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
private IpAddress ipAddress;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public IpAddress getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(IpAddress ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package io.destiny.client.core.domain.query;
|
||||
|
||||
import io.destiny.common.primitive.EmailAddress;
|
||||
import io.destiny.common.primitive.IpAddress;
|
||||
import io.destiny.common.primitive.PhoneNumber;
|
||||
|
||||
public class ClientUserQuery {
|
||||
|
||||
private PhoneNumber phone;
|
||||
|
||||
private String username;
|
||||
|
||||
private EmailAddress email;
|
||||
|
||||
private IpAddress ipAddress;
|
||||
|
||||
public PhoneNumber getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(PhoneNumber phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public EmailAddress getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(EmailAddress email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public IpAddress getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(IpAddress ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package io.destiny.client.core.enums;
|
||||
|
||||
import io.destiny.common.utils.EnumSupport;
|
||||
|
||||
public enum PlatformType implements EnumSupport {
|
||||
|
||||
WECHAT("wechat", "微信小程序"),
|
||||
|
||||
DOUYIN("douyin", "抖音小程序");
|
||||
|
||||
private final String code;
|
||||
|
||||
private final String displayName;
|
||||
|
||||
PlatformType(String code, String displayName) {
|
||||
this.code = code;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public static PlatformType fromCode(String code) {
|
||||
for (PlatformType type : values()) {
|
||||
if (type.code.equals(code)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown platform type: " + code);
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package io.destiny.client.core.enums;
|
||||
|
||||
import io.destiny.common.utils.EnumSupport;
|
||||
|
||||
public enum SmsCodeType implements EnumSupport {
|
||||
|
||||
LOGIN("login", "登录验证码", "SMS_123456789"),
|
||||
|
||||
REGISTER("register", "注册验证码", "SMS_123456790"),
|
||||
|
||||
BIND("bind", "绑定验证码", "SMS_123456791"),
|
||||
|
||||
RESET_PASSWORD("reset_password", "重置密码验证码", "SMS_123456792");
|
||||
|
||||
private final String code;
|
||||
|
||||
private final String displayName;
|
||||
|
||||
private final String templateCode;
|
||||
|
||||
SmsCodeType(String code, String displayName, String templateCode) {
|
||||
this.code = code;
|
||||
this.displayName = displayName;
|
||||
this.templateCode = templateCode;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public String getTemplateCode() {
|
||||
return templateCode;
|
||||
}
|
||||
|
||||
public static SmsCodeType fromCode(String code) {
|
||||
for (SmsCodeType type : values()) {
|
||||
if (type.code.equals(code)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown SMS code type: " + code);
|
||||
}
|
||||
}
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
package io.destiny.client.core.exception;
|
||||
|
||||
import io.destiny.common.exception.ExceptionCode;
|
||||
import io.destiny.common.utils.EnumSupport;
|
||||
|
||||
/**
|
||||
* 客户端用户认证异常码
|
||||
*/
|
||||
public enum ClientUserAuthExceptionCode implements ExceptionCode, EnumSupport {
|
||||
|
||||
/**
|
||||
* 用户名或密码错误
|
||||
*/
|
||||
CLIENT_INVALID_USERNAME_OR_PASSWORD("401", "CLIENT_AUTH_10001", "用户名或密码错误"),
|
||||
|
||||
/**
|
||||
* 平台标识无效
|
||||
*/
|
||||
CLIENT_INVALID_PLATFORM("400", "CLIENT_AUTH_10002", "平台标识无效"),
|
||||
|
||||
/**
|
||||
* 微信授权码无效
|
||||
*/
|
||||
CLIENT_INVALID_WECHAT_CODE("400", "CLIENT_AUTH_10003", "微信授权码无效"),
|
||||
|
||||
/**
|
||||
* 微信授权失败
|
||||
*/
|
||||
CLIENT_WECHAT_AUTH_FAILED("400", "CLIENT_AUTH_10004", "微信授权失败"),
|
||||
|
||||
/**
|
||||
* 抖音授权码无效
|
||||
*/
|
||||
CLIENT_INVALID_DOUYIN_CODE("400", "CLIENT_AUTH_10005", "抖音授权码无效"),
|
||||
|
||||
/**
|
||||
* 抖音授权失败
|
||||
*/
|
||||
CLIENT_DOUYIN_AUTH_FAILED("400", "CLIENT_AUTH_10006", "抖音授权失败"),
|
||||
|
||||
/**
|
||||
* 验证码已过期
|
||||
*/
|
||||
CLIENT_SMS_CODE_EXPIRED("400", "CLIENT_AUTH_10007", "验证码已过期"),
|
||||
|
||||
/**
|
||||
* 验证码错误
|
||||
*/
|
||||
CLIENT_SMS_CODE_INVALID("400", "CLIENT_AUTH_10008", "验证码错误"),
|
||||
|
||||
/**
|
||||
* 验证码已使用
|
||||
*/
|
||||
CLIENT_SMS_CODE_USED("400", "CLIENT_AUTH_10009", "验证码已使用"),
|
||||
|
||||
/**
|
||||
* 验证码验证次数过多
|
||||
*/
|
||||
CLIENT_SMS_CODE_EXCEEDED("400", "CLIENT_AUTH_10010", "验证码验证次数过多"),
|
||||
|
||||
/**
|
||||
* 验证码发送频率过高
|
||||
*/
|
||||
CLIENT_SMS_CODE_SEND_TOO_FAST("429", "CLIENT_AUTH_10011", "验证码发送频率过高"),
|
||||
|
||||
/**
|
||||
* 验证码发送次数超限
|
||||
*/
|
||||
CLIENT_SMS_CODE_SEND_EXCEEDED("429", "CLIENT_AUTH_10012", "验证码发送次数超限"),
|
||||
|
||||
/**
|
||||
* 用户不存在
|
||||
*/
|
||||
CLIENT_USER_NOT_FOUND("404", "CLIENT_AUTH_10013", "用户不存在"),
|
||||
|
||||
/**
|
||||
* 账号已绑定
|
||||
*/
|
||||
CLIENT_ACCOUNT_ALREADY_BOUND("400", "CLIENT_AUTH_10014", "账号已绑定"),
|
||||
|
||||
/**
|
||||
* 账号绑定失败
|
||||
*/
|
||||
CLIENT_ACCOUNT_BIND_FAILED("400", "CLIENT_AUTH_10015", "账号绑定失败"),
|
||||
|
||||
/**
|
||||
* 手机号未验证
|
||||
*/
|
||||
CLIENT_PHONE_NOT_VERIFIED("400", "CLIENT_AUTH_10016", "手机号未验证"),
|
||||
|
||||
/**
|
||||
* 自动注册失败
|
||||
*/
|
||||
CLIENT_AUTO_REGISTER_FAILED("400", "CLIENT_AUTH_10017", "自动注册失败"),
|
||||
|
||||
/**
|
||||
* 登录类型无效
|
||||
*/
|
||||
CLIENT_LOGIN_TYPE_INVALID("400", "CLIENT_AUTH_10018", "登录类型无效"),
|
||||
|
||||
/**
|
||||
* 登录失败
|
||||
*/
|
||||
CLIENT_LOGIN_FAILED("401", "CLIENT_AUTH_10019", "登录失败"),
|
||||
|
||||
/**
|
||||
* 账号已锁定
|
||||
*/
|
||||
CLIENT_ACCOUNT_LOCKED("403", "CLIENT_AUTH_10020", "账号已锁定");
|
||||
|
||||
/**
|
||||
* HTTP状态码
|
||||
*/
|
||||
private final String httpCode;
|
||||
|
||||
/**
|
||||
* 业务错误码
|
||||
*/
|
||||
private final String businessCode;
|
||||
|
||||
/**
|
||||
* 异常码显示名称
|
||||
*/
|
||||
private final String displayName;
|
||||
|
||||
ClientUserAuthExceptionCode(String httpCode, String businessCode, String displayName) {
|
||||
this.httpCode = httpCode;
|
||||
this.businessCode = businessCode;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHttpCode() {
|
||||
return httpCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return businessCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package io.destiny.client.core.exception;
|
||||
|
||||
import io.destiny.common.exception.ExceptionCode;
|
||||
import io.destiny.common.utils.EnumSupport;
|
||||
|
||||
/**
|
||||
* 客户端用户异常码
|
||||
*/
|
||||
public enum ClientUserExceptionCode implements ExceptionCode, EnumSupport {
|
||||
|
||||
/**
|
||||
* 客户端用户不存在
|
||||
*/
|
||||
CLIENT_USER_NOT_FOUND("500", "CLIENT_USER_20001", "客户端用户不存在");
|
||||
|
||||
private final String httpCode;
|
||||
|
||||
private final String businessCode;
|
||||
|
||||
private final String displayName;
|
||||
|
||||
ClientUserExceptionCode(String httpCode, String businessCode, String displayName) {
|
||||
this.httpCode = httpCode;
|
||||
this.businessCode = businessCode;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHttpCode() {
|
||||
return httpCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return businessCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
package io.destiny.client.core.exception;
|
||||
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import io.destiny.common.response.Result;
|
||||
import io.destiny.common.utils.HttpStatusUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Component
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
||||
|
||||
public Mono<ServerResponse> handleException(Throwable ex, ServerWebExchange exchange) {
|
||||
if (ex instanceof ApplicationException) {
|
||||
return handleApplicationException((ApplicationException) ex, exchange);
|
||||
} else if (ex instanceof IllegalArgumentException) {
|
||||
return handleIllegalArgumentException((IllegalArgumentException) ex, exchange);
|
||||
} else if (ex instanceof Exception) {
|
||||
return handleGenericException((Exception) ex, exchange);
|
||||
} else {
|
||||
return handleUnknownException(ex, exchange);
|
||||
}
|
||||
}
|
||||
|
||||
private Mono<ServerResponse> handleApplicationException(ApplicationException ex, ServerWebExchange exchange) {
|
||||
String httpCode = ex.getHttpCode();
|
||||
String businessCode = ex.getExceptionCode() != null ? ex.getExceptionCode().getCode() : "ERROR";
|
||||
String message = ex.getExceptionMessage() != null ? ex.getExceptionMessage() :
|
||||
(ex.getExceptionCode() != null ? ex.getExceptionCode().getDisplayName() : "系统异常");
|
||||
|
||||
HttpStatus status = HttpStatusUtils.getHttpStatusByCode(httpCode);
|
||||
|
||||
String path = exchange != null ? exchange.getRequest().getPath().value() : "unknown";
|
||||
logger.warn("ApplicationException - HTTP: {}, Business: {}, Message: {}, Path: {}",
|
||||
httpCode, businessCode, message, path);
|
||||
|
||||
Result<Void> result = Result.error(businessCode, message);
|
||||
|
||||
return ServerResponse.status(status)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(result);
|
||||
}
|
||||
|
||||
private Mono<ServerResponse> handleIllegalArgumentException(IllegalArgumentException ex,
|
||||
ServerWebExchange exchange) {
|
||||
String path = exchange != null ? exchange.getRequest().getPath().value() : "unknown";
|
||||
logger.warn("IllegalArgumentException - Message: {}, Path: {}",
|
||||
ex.getMessage(), path);
|
||||
|
||||
Result<Void> result = Result.error("INVALID_ARGUMENT", ex.getMessage());
|
||||
|
||||
return ServerResponse.badRequest()
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(result);
|
||||
}
|
||||
|
||||
private Mono<ServerResponse> handleGenericException(Exception ex, ServerWebExchange exchange) {
|
||||
String path = exchange != null ? exchange.getRequest().getPath().value() : "unknown";
|
||||
logger.error("Unexpected exception - Message: {}, Path: {}",
|
||||
ex.getMessage(), path, ex);
|
||||
|
||||
Result<Void> result = Result.error("INTERNAL_ERROR", "Internal server error");
|
||||
|
||||
return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(result);
|
||||
}
|
||||
|
||||
private Mono<ServerResponse> handleUnknownException(Throwable ex, ServerWebExchange exchange) {
|
||||
String path = exchange != null ? exchange.getRequest().getPath().value() : "unknown";
|
||||
logger.error("Unknown exception - Message: {}, Path: {}",
|
||||
ex.getMessage(), path, ex);
|
||||
|
||||
Result<Void> result = Result.error("UNKNOWN_ERROR", "Unknown error occurred");
|
||||
|
||||
return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(result);
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package io.destiny.client.core.repository;
|
||||
|
||||
import io.destiny.client.core.domain.ClientLoginLog;
|
||||
import io.destiny.client.core.domain.query.ClientLoginLogQuery;
|
||||
import io.destiny.common.request.PageQuery;
|
||||
import io.destiny.common.response.PageModel;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface IClientLoginLogRepository {
|
||||
|
||||
Mono<PageModel<ClientLoginLog>> findByQueryWithPagination(ClientLoginLogQuery query, PageQuery pageQuery);
|
||||
|
||||
Mono<ClientLoginLog> save(ClientLoginLog loginLog);
|
||||
|
||||
Flux<ClientLoginLog> findByClientUserId(Long clientUserId);
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package io.destiny.client.core.repository;
|
||||
|
||||
import io.destiny.client.core.domain.ClientUser;
|
||||
import io.destiny.client.core.domain.query.ClientUserQuery;
|
||||
import io.destiny.common.primitive.PhoneNumber;
|
||||
import io.destiny.common.request.PageQuery;
|
||||
import io.destiny.common.response.PageModel;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 客户端用户仓库接口
|
||||
*
|
||||
* @author zhangxiang
|
||||
* @date 2025-12-30
|
||||
*/
|
||||
public interface IClientUserRepository {
|
||||
|
||||
Mono<ClientUser> findByPhone(PhoneNumber phone);
|
||||
|
||||
Mono<ClientUser> findByWechatOpenId(String wechatOpenId);
|
||||
|
||||
Mono<ClientUser> findByDouyinOpenId(String douyinOpenId);
|
||||
|
||||
Mono<ClientUser> findByWechatUnionId(String wechatUnionId);
|
||||
|
||||
Mono<ClientUser> findByDouyinUnionId(String douyinUnionId);
|
||||
|
||||
Mono<ClientUser> save(ClientUser clientUser);
|
||||
|
||||
Mono<ClientUser> findById(Long id);
|
||||
|
||||
Mono<ClientUser> findByUsername(String username);
|
||||
|
||||
Flux<ClientUser> findByQuery(ClientUserQuery query);
|
||||
|
||||
Mono<PageModel<ClientUser>> findByQueryWithPagination(ClientUserQuery query, PageQuery pageQuery);
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package io.destiny.client.core.repository;
|
||||
|
||||
import io.destiny.client.core.domain.SmsVerificationCode;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public interface ISmsVerificationCodeRepository {
|
||||
|
||||
Mono<SmsVerificationCode> save(SmsVerificationCode smsVerificationCode);
|
||||
|
||||
Mono<SmsVerificationCode> findLatestByPhoneAndType(String phone, String type);
|
||||
|
||||
Mono<Long> countByPhoneAndCreatedAtAfter(String phone, LocalDateTime createdAt);
|
||||
|
||||
Mono<Integer> deleteExpiredBefore(LocalDateTime expireTime);
|
||||
|
||||
Mono<Void> markAsVerified(Long id);
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package io.destiny.client.core.repository;
|
||||
|
||||
import io.destiny.client.core.domain.Subscription;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface ISubscriptionRepository {
|
||||
|
||||
Flux<Subscription> findAll();
|
||||
|
||||
Flux<Subscription> findByStatus(String status);
|
||||
|
||||
Flux<Subscription> findByClientUserId(Long clientUserId);
|
||||
|
||||
Flux<Subscription> findByStartDateBetween(long startTimestamp, long endTimestamp);
|
||||
|
||||
Mono<Subscription> save(Subscription subscription);
|
||||
|
||||
Mono<Void> deleteById(Long id);
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package io.destiny.client.core.repository;
|
||||
|
||||
import io.destiny.client.core.domain.UserBinding;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface IUserBindingRepository {
|
||||
|
||||
Mono<UserBinding> save(UserBinding userBinding);
|
||||
|
||||
Mono<UserBinding> findByUserIdAndPlatform(Long userId, String platform);
|
||||
|
||||
Mono<UserBinding> findByPlatformAndOpenId(String platform, String platformOpenId);
|
||||
|
||||
Flux<UserBinding> findByUserId(Long userId);
|
||||
|
||||
Mono<UserBinding> findByPlatformAndUnionId(String platform, String platformUnionId);
|
||||
|
||||
Mono<Void> deleteByUserIdAndPlatform(Long userId, String platform);
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
package io.destiny.client.core.service;
|
||||
|
||||
import io.destiny.client.core.domain.ClientLoginLog;
|
||||
import io.destiny.client.core.domain.ClientUser;
|
||||
import io.destiny.client.core.domain.query.ClientUserLogin;
|
||||
import io.destiny.client.core.repository.IClientLoginLogRepository;
|
||||
import io.destiny.client.core.repository.IClientUserRepository;
|
||||
import io.destiny.client.dto.ClientLoginResponse;
|
||||
import io.destiny.client.security.JwtTokenProvider;
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static io.destiny.client.core.exception.ClientUserAuthExceptionCode.CLIENT_INVALID_USERNAME_OR_PASSWORD;
|
||||
|
||||
@Service
|
||||
public class ClientUserAuthService {
|
||||
|
||||
private final IClientUserRepository clientUserRepository;
|
||||
private final IClientLoginLogRepository clientLoginLogRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final JwtTokenProvider jwtTokenProvider;
|
||||
|
||||
public ClientUserAuthService(IClientUserRepository clientUserRepository,
|
||||
IClientLoginLogRepository clientLoginLogRepository, PasswordEncoder passwordEncoder,
|
||||
JwtTokenProvider jwtTokenProvider) {
|
||||
this.clientUserRepository = clientUserRepository;
|
||||
this.clientLoginLogRepository = clientLoginLogRepository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.jwtTokenProvider = jwtTokenProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册客户端用户
|
||||
*/
|
||||
public Mono<ClientUser> register(ClientUser clientUser) {
|
||||
return Mono.fromCallable(() -> passwordEncoder.encode(clientUser.getPassword()))
|
||||
.map(encodedPassword -> {
|
||||
ClientUser newUser = new ClientUser();
|
||||
newUser.setUsername(clientUser.getUsername());
|
||||
newUser.setPassword(encodedPassword);
|
||||
newUser.setEmail(clientUser.getEmail());
|
||||
newUser.setPhone(clientUser.getPhone());
|
||||
newUser.setGender(clientUser.getGender());
|
||||
newUser.setBirthTime(clientUser.getBirthTime());
|
||||
return newUser;
|
||||
})
|
||||
.flatMap(clientUserRepository::save);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录并记录登录日志
|
||||
*/
|
||||
public Mono<ClientLoginResponse> login(ClientUserLogin clientUserLogin) {
|
||||
return clientUserRepository.findByUsername(clientUserLogin.getUsername())
|
||||
.filter(user -> passwordEncoder.matches(clientUserLogin.getPassword(), user.getPassword()))
|
||||
.switchIfEmpty(Mono.error(new ApplicationException(CLIENT_INVALID_USERNAME_OR_PASSWORD)))
|
||||
.flatMap(user -> clientLoginLogRepository
|
||||
.save(new ClientLoginLog(user.getId(), clientUserLogin.getIpAddress()))
|
||||
.thenReturn(user))
|
||||
.map(user -> {
|
||||
String token = jwtTokenProvider.generateToken(user.getId(), user.getUsername());
|
||||
return ClientLoginResponse.from(token, user);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新登录令牌
|
||||
*/
|
||||
public Mono<ClientLoginResponse> refreshLoginToken(String oldToken) {
|
||||
return Mono.fromCallable(() -> jwtTokenProvider.getUserIdFromToken(oldToken))
|
||||
.switchIfEmpty(Mono.error(new ApplicationException(CLIENT_INVALID_USERNAME_OR_PASSWORD)))
|
||||
.flatMap(userId -> clientUserRepository.findById(userId))
|
||||
.switchIfEmpty(Mono.error(new ApplicationException(CLIENT_INVALID_USERNAME_OR_PASSWORD)))
|
||||
.map(user -> {
|
||||
String newToken = jwtTokenProvider.generateToken(user.getId(), user.getUsername());
|
||||
return ClientLoginResponse.from(newToken, user);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销登录
|
||||
*/
|
||||
public Mono<Void> logout(Long clientUserId) {
|
||||
return clientLoginLogRepository.findByClientUserId(clientUserId)
|
||||
.filter(ClientLoginLog::isLogoutTimeNull)
|
||||
.next()
|
||||
.flatMap(loginLog -> {
|
||||
loginLog.setLogoutTime(LocalDateTime.now());
|
||||
return clientLoginLogRepository.save(loginLog);
|
||||
})
|
||||
.then();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查登录状态
|
||||
*/
|
||||
public Mono<Boolean> checkLoginStatus(Long clientUserId) {
|
||||
return clientLoginLogRepository.findByClientUserId(clientUserId)
|
||||
.filter(ClientLoginLog::isLogoutTimeNull)
|
||||
.hasElements()
|
||||
.defaultIfEmpty(false);
|
||||
}
|
||||
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
package io.destiny.client.core.service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import io.destiny.client.core.domain.ClientUser;
|
||||
import io.destiny.client.core.domain.query.ClientUserQuery;
|
||||
import io.destiny.client.core.repository.IClientUserRepository;
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import io.destiny.common.request.PageQuery;
|
||||
import io.destiny.common.response.PageModel;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import static io.destiny.client.core.exception.ClientUserExceptionCode.CLIENT_USER_NOT_FOUND;
|
||||
|
||||
@Service
|
||||
public class ClientUserService {
|
||||
|
||||
private final IClientUserRepository clientUserRepository;
|
||||
|
||||
public ClientUserService(IClientUserRepository clientUserRepository) {
|
||||
this.clientUserRepository = clientUserRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存客户端用户
|
||||
*
|
||||
* @param clientUser 客户端用户
|
||||
* @return 保存后的客户端用户
|
||||
*/
|
||||
public Mono<ClientUser> save(ClientUser clientUser) {
|
||||
return clientUserRepository.save(clientUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新客户端用户
|
||||
*
|
||||
* @param clientUser 客户端用户
|
||||
* @return 更新后的客户端用户
|
||||
*/
|
||||
public Mono<ClientUser> update(ClientUser clientUser) {
|
||||
return clientUserRepository.findById(clientUser.getId())
|
||||
.switchIfEmpty(Mono.error(new ApplicationException(CLIENT_USER_NOT_FOUND)))
|
||||
.flatMap(existingClientUser -> {
|
||||
clientUser.setCreatedAt(existingClientUser.getCreatedAt());
|
||||
return clientUserRepository.save(clientUser);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除客户端用户
|
||||
*
|
||||
* @param id 客户端用户ID
|
||||
* @return 删除结果
|
||||
*/
|
||||
public Mono<Void> deleteById(Long id) {
|
||||
return clientUserRepository.findById(id)
|
||||
.switchIfEmpty(Mono.error(new ApplicationException(CLIENT_USER_NOT_FOUND)))
|
||||
.flatMap(existingClientUser -> {
|
||||
existingClientUser.delete();
|
||||
return clientUserRepository.save(existingClientUser);
|
||||
})
|
||||
.then();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据客户端用户ID查询客户端用户
|
||||
*
|
||||
* @param id 客户端用户ID
|
||||
* @return 客户端用户
|
||||
*/
|
||||
public Mono<ClientUser> findById(Long id) {
|
||||
return clientUserRepository.findById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询客户端用户
|
||||
*
|
||||
* @param pageRequest 分页请求
|
||||
* @return 客户端用户分页
|
||||
*/
|
||||
public Mono<PageModel<ClientUser>> findByPage(ClientUserQuery query, PageQuery pageRequest) {
|
||||
return clientUserRepository.findByQueryWithPagination(query, pageRequest);
|
||||
}
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
package io.destiny.client.core.service;
|
||||
|
||||
import com.aliyun.dysmsapi20170525.Client;
|
||||
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
|
||||
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import io.destiny.client.config.AliyunSmsConfig;
|
||||
import io.destiny.client.core.domain.SmsVerificationCode;
|
||||
import io.destiny.client.core.enums.SmsCodeType;
|
||||
import io.destiny.client.core.exception.ClientUserAuthExceptionCode;
|
||||
import io.destiny.client.core.repository.ISmsVerificationCodeRepository;
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Service
|
||||
public class SmsService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SmsService.class);
|
||||
|
||||
private static final int CODE_EXPIRE_MINUTES = 10;
|
||||
private static final int MAX_SEND_COUNT_PER_MINUTE = 1;
|
||||
private static final int MAX_SEND_COUNT_PER_DAY = 10;
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
private final AliyunSmsConfig smsConfig;
|
||||
private final ISmsVerificationCodeRepository smsVerificationCodeRepository;
|
||||
private final Client smsClient;
|
||||
|
||||
private final Cache<String, SendRecord> sendRecords;
|
||||
|
||||
public SmsService(AliyunSmsConfig smsConfig, ISmsVerificationCodeRepository smsVerificationCodeRepository,
|
||||
Client smsClient) {
|
||||
this.smsConfig = smsConfig;
|
||||
this.smsVerificationCodeRepository = smsVerificationCodeRepository;
|
||||
this.smsClient = smsClient;
|
||||
this.sendRecords = Caffeine.newBuilder()
|
||||
.expireAfterWrite(1, TimeUnit.DAYS)
|
||||
.recordStats()
|
||||
.build();
|
||||
}
|
||||
|
||||
public Mono<String> sendVerificationCode(String phone, SmsCodeType type) {
|
||||
if (phone == null || phone.isEmpty()) {
|
||||
return Mono.error(new ApplicationException(ClientUserAuthExceptionCode.CLIENT_SMS_CODE_INVALID));
|
||||
}
|
||||
if (type == null) {
|
||||
return Mono.error(new ApplicationException(ClientUserAuthExceptionCode.CLIENT_SMS_CODE_SEND_EXCEEDED));
|
||||
}
|
||||
return checkSendRateLimit(phone)
|
||||
.then(generateVerificationCode())
|
||||
.flatMap(code -> sendSms(phone, code, type.getTemplateCode())
|
||||
.flatMap(response -> saveVerificationCode(phone, code, type))
|
||||
.thenReturn(code));
|
||||
}
|
||||
|
||||
private Mono<Void> checkSendRateLimit(String phone) {
|
||||
if (phone == null || phone.isEmpty()) {
|
||||
return Mono.error(new ApplicationException(ClientUserAuthExceptionCode.CLIENT_SMS_CODE_INVALID));
|
||||
}
|
||||
|
||||
SendRecord record = sendRecords.getIfPresent(phone);
|
||||
|
||||
if (record != null) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
|
||||
if (record.getMinuteCount() >= MAX_SEND_COUNT_PER_MINUTE) {
|
||||
if (now.isBefore(record.getMinuteExpireTime())) {
|
||||
logger.warn("SMS send rate limit exceeded (1 minute) - Phone: {}", phone);
|
||||
return Mono
|
||||
.error(new ApplicationException(ClientUserAuthExceptionCode.CLIENT_SMS_CODE_SEND_TOO_FAST));
|
||||
} else {
|
||||
record.resetMinuteCount();
|
||||
}
|
||||
}
|
||||
|
||||
if (record.getDayCount() >= MAX_SEND_COUNT_PER_DAY) {
|
||||
if (now.isBefore(record.getDayExpireTime())) {
|
||||
logger.warn("SMS send rate limit exceeded (1 day) - Phone: {}", phone);
|
||||
return Mono
|
||||
.error(new ApplicationException(ClientUserAuthExceptionCode.CLIENT_SMS_CODE_SEND_EXCEEDED));
|
||||
} else {
|
||||
record.resetDayCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
private Mono<String> generateVerificationCode() {
|
||||
int code = 100000 + RANDOM.nextInt(900000);
|
||||
return Mono.just(String.valueOf(code));
|
||||
}
|
||||
|
||||
private Mono<SendSmsResponse> sendSms(String phone, String code, String templateCode) {
|
||||
return Mono.fromCallable(() -> {
|
||||
String signName = smsConfig.getSignName();
|
||||
if (signName == null) {
|
||||
throw new ApplicationException(ClientUserAuthExceptionCode.CLIENT_SMS_CODE_SEND_EXCEEDED);
|
||||
}
|
||||
|
||||
SendSmsRequest request = new SendSmsRequest()
|
||||
.setPhoneNumbers(phone)
|
||||
.setSignName(signName)
|
||||
.setTemplateCode(templateCode)
|
||||
.setTemplateParam("{\"code\":\"" + code + "\"}");
|
||||
|
||||
logger.info("Sending SMS - Phone: {}, Template: {}, Code: {}", phone, templateCode, code);
|
||||
|
||||
SendSmsResponse response = smsClient.sendSms(request);
|
||||
|
||||
if (!"OK".equals(response.getBody().getCode())) {
|
||||
logger.error("SMS send failed - Phone: {}, Code: {}, Message: {}",
|
||||
phone, response.getBody().getCode(), response.getBody().getMessage());
|
||||
throw new ApplicationException(ClientUserAuthExceptionCode.CLIENT_SMS_CODE_SEND_EXCEEDED);
|
||||
}
|
||||
|
||||
logger.info("SMS sent successfully - Phone: {}, RequestId: {}",
|
||||
phone, response.getBody().getRequestId());
|
||||
|
||||
return response;
|
||||
});
|
||||
}
|
||||
|
||||
private Mono<SmsVerificationCode> saveVerificationCode(String phone, String code, SmsCodeType type) {
|
||||
String typeCode = type != null ? type.getCode() : null;
|
||||
return smsVerificationCodeRepository.save(new SmsVerificationCode(
|
||||
phone,
|
||||
code,
|
||||
typeCode,
|
||||
LocalDateTime.now().plusMinutes(CODE_EXPIRE_MINUTES))).doOnSuccess(v -> updateSendRecord(phone));
|
||||
}
|
||||
|
||||
private void updateSendRecord(String phone) {
|
||||
if (phone == null || phone.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
SendRecord record = sendRecords.get(phone, key -> new SendRecord());
|
||||
record.increment();
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to update send record for phone: {}", phone, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class SendRecord {
|
||||
private int minuteCount = 0;
|
||||
private LocalDateTime minuteExpireTime;
|
||||
private int dayCount = 0;
|
||||
private LocalDateTime dayExpireTime;
|
||||
|
||||
public void increment() {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
minuteCount++;
|
||||
dayCount++;
|
||||
|
||||
if (minuteExpireTime == null) {
|
||||
minuteExpireTime = now.plusMinutes(1);
|
||||
}
|
||||
|
||||
if (dayExpireTime == null) {
|
||||
dayExpireTime = now.plusDays(1);
|
||||
}
|
||||
}
|
||||
|
||||
public void resetMinuteCount() {
|
||||
minuteCount = 0;
|
||||
minuteExpireTime = LocalDateTime.now().plusMinutes(1);
|
||||
}
|
||||
|
||||
public void resetDayCount() {
|
||||
dayCount = 0;
|
||||
dayExpireTime = LocalDateTime.now().plusDays(1);
|
||||
}
|
||||
|
||||
public int getMinuteCount() {
|
||||
return minuteCount;
|
||||
}
|
||||
|
||||
public int getDayCount() {
|
||||
return dayCount;
|
||||
}
|
||||
|
||||
public LocalDateTime getMinuteExpireTime() {
|
||||
return minuteExpireTime;
|
||||
}
|
||||
|
||||
public LocalDateTime getDayExpireTime() {
|
||||
return dayExpireTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package io.destiny.client.core.strategy;
|
||||
|
||||
import io.destiny.client.dto.LoginRequest;
|
||||
import io.destiny.client.dto.LoginResult;
|
||||
import io.destiny.client.core.service.ClientUserAuthService;
|
||||
import io.destiny.client.security.JwtTokenProvider;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public abstract class AbstractLoginStrategy implements LoginStrategy {
|
||||
|
||||
protected final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
protected final ClientUserAuthService clientUserAuthService;
|
||||
|
||||
protected final JwtTokenProvider jwtTokenProvider;
|
||||
|
||||
public AbstractLoginStrategy(ClientUserAuthService clientUserAuthService, JwtTokenProvider jwtTokenProvider) {
|
||||
this.clientUserAuthService = clientUserAuthService;
|
||||
this.jwtTokenProvider = jwtTokenProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<LoginResult> login(LoginRequest request) {
|
||||
return validateRequest(request)
|
||||
.flatMap(this::doLogin)
|
||||
.flatMap(this::generateToken)
|
||||
.doOnSuccess(result -> logger.info("Login successful, userId: {}, platform: {}", result.getUserId(), result.getPlatform()))
|
||||
.doOnError(error -> logger.error("Login failed, error: {}", error.getMessage()));
|
||||
}
|
||||
|
||||
protected abstract Mono<LoginRequest> validateRequest(LoginRequest request);
|
||||
|
||||
protected abstract Mono<LoginResult> doLogin(LoginRequest request);
|
||||
|
||||
protected Mono<LoginResult> generateToken(LoginResult result) {
|
||||
String token = jwtTokenProvider.generateToken(result.getUserId(), result.getPlatform());
|
||||
result.setToken(token);
|
||||
return Mono.just(result);
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
package io.destiny.client.core.strategy;
|
||||
|
||||
import io.destiny.client.client.DouyinMiniProgramClient;
|
||||
import io.destiny.client.core.domain.ClientUser;
|
||||
import io.destiny.client.core.domain.UserBinding;
|
||||
import io.destiny.client.core.repository.IClientUserRepository;
|
||||
import io.destiny.client.core.repository.IUserBindingRepository;
|
||||
import io.destiny.client.core.service.ClientUserAuthService;
|
||||
import io.destiny.client.dto.DouyinLoginRequest;
|
||||
import io.destiny.client.dto.LoginRequest;
|
||||
import io.destiny.client.dto.LoginResult;
|
||||
import io.destiny.client.security.JwtTokenProvider;
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import org.springframework.stereotype.Component;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import static io.destiny.client.core.exception.ClientUserAuthExceptionCode.*;
|
||||
|
||||
@Component
|
||||
public class DouyinLoginStrategy extends AbstractLoginStrategy {
|
||||
|
||||
private static final String LOGIN_TYPE = "douyin";
|
||||
|
||||
private final DouyinMiniProgramClient douyinClient;
|
||||
private final IClientUserRepository clientUserRepository;
|
||||
private final IUserBindingRepository userBindingRepository;
|
||||
|
||||
public DouyinLoginStrategy(ClientUserAuthService clientUserAuthService,
|
||||
JwtTokenProvider jwtTokenProvider,
|
||||
DouyinMiniProgramClient douyinClient,
|
||||
IClientUserRepository clientUserRepository,
|
||||
IUserBindingRepository userBindingRepository) {
|
||||
super(clientUserAuthService, jwtTokenProvider);
|
||||
this.douyinClient = douyinClient;
|
||||
this.clientUserRepository = clientUserRepository;
|
||||
this.userBindingRepository = userBindingRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLoginType() {
|
||||
return LOGIN_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<LoginRequest> validateRequest(LoginRequest request) {
|
||||
if (request.getData() instanceof DouyinLoginRequest) {
|
||||
DouyinLoginRequest douyinRequest = (DouyinLoginRequest) request.getData();
|
||||
if (douyinRequest.getCode() == null || douyinRequest.getCode().isEmpty()) {
|
||||
return Mono.error(new ApplicationException(CLIENT_INVALID_DOUYIN_CODE));
|
||||
}
|
||||
return Mono.just(request);
|
||||
}
|
||||
return Mono.error(new ApplicationException(CLIENT_INVALID_DOUYIN_CODE));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<LoginResult> doLogin(LoginRequest request) {
|
||||
DouyinLoginRequest douyinRequest = (DouyinLoginRequest) request.getData();
|
||||
|
||||
return douyinClient.auth(douyinRequest.getCode())
|
||||
.flatMap(authResponse -> {
|
||||
String openId = authResponse.getOpenId();
|
||||
String unionId = authResponse.getUnionId();
|
||||
|
||||
return clientUserRepository.findByDouyinOpenId(openId)
|
||||
.switchIfEmpty(Mono.defer(() -> {
|
||||
if (unionId != null && !unionId.isEmpty()) {
|
||||
return clientUserRepository.findByDouyinUnionId(unionId);
|
||||
}
|
||||
return Mono.empty();
|
||||
}))
|
||||
.switchIfEmpty(Mono.defer(() -> {
|
||||
if (unionId != null && !unionId.isEmpty()) {
|
||||
return userBindingRepository.findByPlatformAndUnionId("douyin", unionId)
|
||||
.flatMap(binding -> clientUserRepository.findById(binding.getUserId()));
|
||||
}
|
||||
return Mono.empty();
|
||||
}))
|
||||
.switchIfEmpty(Mono.defer(() -> createNewUser(openId, unionId)))
|
||||
.map(user -> buildLoginResult(user, "douyin"));
|
||||
});
|
||||
}
|
||||
|
||||
private Mono<ClientUser> createNewUser(String openId, String unionId) {
|
||||
ClientUser newUser = new ClientUser();
|
||||
newUser.generateId();
|
||||
newUser.setDouyinOpenId(openId);
|
||||
if (unionId != null && !unionId.isEmpty()) {
|
||||
newUser.setDouyinUnionId(unionId);
|
||||
}
|
||||
newUser.setPhoneVerified(false);
|
||||
|
||||
return clientUserRepository.save(newUser)
|
||||
.flatMap(user -> {
|
||||
UserBinding binding = new UserBinding();
|
||||
binding.generateId();
|
||||
binding.setUserId(user.getId());
|
||||
binding.setPlatform("douyin");
|
||||
binding.setPlatformOpenId(openId);
|
||||
if (unionId != null && !unionId.isEmpty()) {
|
||||
binding.setPlatformUnionId(unionId);
|
||||
}
|
||||
return userBindingRepository.save(binding).thenReturn(user);
|
||||
});
|
||||
}
|
||||
|
||||
private LoginResult buildLoginResult(ClientUser user, String platform) {
|
||||
LoginResult result = new LoginResult();
|
||||
result.setUserId(user.getId());
|
||||
result.setUsername(user.getUsername());
|
||||
result.setPhone(user.getPhone() != null ? user.getPhone().getValue() : null);
|
||||
result.setPlatform(platform);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package io.destiny.client.core.strategy;
|
||||
|
||||
import io.destiny.client.dto.LoginRequest;
|
||||
import io.destiny.client.dto.LoginResult;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface LoginStrategy {
|
||||
|
||||
Mono<LoginResult> login(LoginRequest request);
|
||||
|
||||
String getLoginType();
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package io.destiny.client.core.strategy;
|
||||
|
||||
import io.destiny.client.core.exception.ClientUserAuthExceptionCode;
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
public class LoginStrategyFactory {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(LoginStrategyFactory.class);
|
||||
|
||||
private final Map<String, LoginStrategy> strategyMap;
|
||||
private final List<LoginStrategy> strategies;
|
||||
|
||||
public LoginStrategyFactory(List<LoginStrategy> strategies) {
|
||||
this.strategyMap = new ConcurrentHashMap<>();
|
||||
this.strategies = strategies;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
this.strategyMap.putAll(strategies.stream()
|
||||
.collect(Collectors.toMap(
|
||||
strategy -> strategy.getLoginType().toLowerCase(),
|
||||
Function.identity()
|
||||
)));
|
||||
logger.info("Initialized LoginStrategyFactory with strategies: {}", strategyMap.keySet());
|
||||
}
|
||||
|
||||
public LoginStrategy getStrategy(String loginType) {
|
||||
if (loginType == null || loginType.isEmpty()) {
|
||||
logger.error("Login type is null or empty");
|
||||
throw new ApplicationException(ClientUserAuthExceptionCode.CLIENT_LOGIN_TYPE_INVALID);
|
||||
}
|
||||
|
||||
LoginStrategy strategy = strategyMap.get(loginType.toLowerCase());
|
||||
if (strategy == null) {
|
||||
logger.error("Unsupported login type: {}", loginType);
|
||||
throw new ApplicationException(ClientUserAuthExceptionCode.CLIENT_LOGIN_TYPE_INVALID);
|
||||
}
|
||||
|
||||
logger.debug("Retrieved strategy for login type: {}", loginType);
|
||||
return strategy;
|
||||
}
|
||||
|
||||
public boolean supports(String loginType) {
|
||||
return loginType != null && strategyMap.containsKey(loginType.toLowerCase());
|
||||
}
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
package io.destiny.client.core.strategy;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import io.destiny.client.core.exception.ClientUserAuthExceptionCode;
|
||||
import io.destiny.client.dto.LoginRequest;
|
||||
import io.destiny.client.dto.LoginResult;
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Component
|
||||
public class LoginStrategyProxy {
|
||||
private static final Logger logger = LoggerFactory.getLogger(LoginStrategyProxy.class);
|
||||
|
||||
private final LoginStrategyFactory strategyFactory;
|
||||
|
||||
private static final int MAX_LOGIN_ATTEMPTS = 5;
|
||||
private static final long LOCK_TIME_MS = 10 * 60 * 1000;
|
||||
private final Cache<String, LoginAttemptRecord> attemptRecords;
|
||||
|
||||
public LoginStrategyProxy(LoginStrategyFactory strategyFactory) {
|
||||
this.strategyFactory = strategyFactory;
|
||||
this.attemptRecords = Caffeine.newBuilder()
|
||||
.expireAfterWrite(10, TimeUnit.MINUTES)
|
||||
.recordStats()
|
||||
.build();
|
||||
}
|
||||
|
||||
public Mono<LoginResult> login(LoginRequest request) {
|
||||
String loginType = request.getLoginType();
|
||||
String clientKey = getClientKey(request);
|
||||
String ipAddress = request.getIpAddress() != null ? request.getIpAddress() : "unknown";
|
||||
|
||||
logger.info("Login request received - Type: {}, IP: {}, ClientKey: {}",
|
||||
loginType, ipAddress, clientKey);
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
return checkRateLimit(clientKey)
|
||||
.then(Mono.defer(() -> {
|
||||
LoginStrategy strategy = strategyFactory.getStrategy(loginType);
|
||||
return strategy.login(request);
|
||||
}))
|
||||
.doOnSuccess(result -> {
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
logger.info("Login successful - Type: {}, UserId: {}, Duration: {}ms",
|
||||
loginType, result.getUserId(), duration);
|
||||
clearAttemptRecord(clientKey);
|
||||
})
|
||||
.doOnError(error -> {
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
logger.error("Login failed - Type: {}, Error: {}, Duration: {}ms",
|
||||
loginType, error.getMessage(), duration);
|
||||
recordFailedAttempt(clientKey);
|
||||
})
|
||||
.onErrorResume(error -> {
|
||||
if (error instanceof ApplicationException) {
|
||||
return Mono.error(error);
|
||||
}
|
||||
logger.error("Unexpected error during login", error);
|
||||
return Mono.error(new ApplicationException(ClientUserAuthExceptionCode.CLIENT_LOGIN_FAILED));
|
||||
});
|
||||
}
|
||||
|
||||
private Mono<Void> checkRateLimit(String clientKey) {
|
||||
if (clientKey == null) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
LoginAttemptRecord record = attemptRecords.getIfPresent(clientKey);
|
||||
|
||||
if (record != null && record.isLocked()) {
|
||||
long remainingTime = (record.getLockUntil() - System.currentTimeMillis()) / 1000;
|
||||
logger.warn("Login attempt blocked - ClientKey: {}, Remaining lock time: {}s",
|
||||
clientKey, remainingTime);
|
||||
return Mono.error(new ApplicationException(ClientUserAuthExceptionCode.CLIENT_ACCOUNT_LOCKED));
|
||||
}
|
||||
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
private void recordFailedAttempt(String clientKey) {
|
||||
if (clientKey == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
LoginAttemptRecord record = attemptRecords.get(clientKey, key -> new LoginAttemptRecord());
|
||||
record.incrementAttemptCount();
|
||||
|
||||
if (record.getAttemptCount() >= MAX_LOGIN_ATTEMPTS) {
|
||||
record.lock();
|
||||
logger.warn("Account locked due to too many failed attempts - ClientKey: {}, Attempts: {}",
|
||||
clientKey, record.getAttemptCount());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to record failed attempt for clientKey: {}", clientKey, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void clearAttemptRecord(String clientKey) {
|
||||
if (clientKey != null) {
|
||||
attemptRecords.invalidate(clientKey);
|
||||
}
|
||||
}
|
||||
|
||||
private String getClientKey(LoginRequest request) {
|
||||
String loginType = request.getLoginType() != null ? request.getLoginType() : "unknown";
|
||||
String ipAddress = request.getIpAddress() != null ? request.getIpAddress() : "unknown";
|
||||
|
||||
if (request.getData() instanceof io.destiny.client.dto.WechatLoginRequest) {
|
||||
io.destiny.client.dto.WechatLoginRequest wechatRequest = (io.destiny.client.dto.WechatLoginRequest) request
|
||||
.getData();
|
||||
String code = wechatRequest.getCode() != null ? wechatRequest.getCode() : "unknown";
|
||||
return loginType + ":" + code + ":" + ipAddress;
|
||||
} else if (request.getData() instanceof io.destiny.client.dto.DouyinLoginRequest) {
|
||||
io.destiny.client.dto.DouyinLoginRequest douyinRequest = (io.destiny.client.dto.DouyinLoginRequest) request
|
||||
.getData();
|
||||
String code = douyinRequest.getCode() != null ? douyinRequest.getCode() : "unknown";
|
||||
return loginType + ":" + code + ":" + ipAddress;
|
||||
} else if (request.getData() instanceof io.destiny.client.dto.SmsLoginRequest) {
|
||||
io.destiny.client.dto.SmsLoginRequest smsRequest = (io.destiny.client.dto.SmsLoginRequest) request
|
||||
.getData();
|
||||
String phone = smsRequest.getPhone() != null ? smsRequest.getPhone() : "unknown";
|
||||
return loginType + ":" + phone + ":" + ipAddress;
|
||||
}
|
||||
|
||||
return loginType + ":" + ipAddress;
|
||||
}
|
||||
|
||||
private static class LoginAttemptRecord {
|
||||
private int attemptCount = 0;
|
||||
private volatile long lockUntil = 0;
|
||||
|
||||
public int getAttemptCount() {
|
||||
return attemptCount;
|
||||
}
|
||||
|
||||
public void incrementAttemptCount() {
|
||||
attemptCount++;
|
||||
}
|
||||
|
||||
public void lock() {
|
||||
lockUntil = System.currentTimeMillis() + LOCK_TIME_MS;
|
||||
}
|
||||
|
||||
public boolean isLocked() {
|
||||
return lockUntil > System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public long getLockUntil() {
|
||||
return lockUntil;
|
||||
}
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
package io.destiny.client.core.strategy;
|
||||
|
||||
import io.destiny.client.core.domain.ClientUser;
|
||||
import io.destiny.client.core.enums.SmsCodeType;
|
||||
import io.destiny.client.core.repository.IClientUserRepository;
|
||||
import io.destiny.client.core.repository.ISmsVerificationCodeRepository;
|
||||
import io.destiny.client.core.service.ClientUserAuthService;
|
||||
import io.destiny.client.dto.LoginRequest;
|
||||
import io.destiny.client.dto.LoginResult;
|
||||
import io.destiny.client.dto.SmsLoginRequest;
|
||||
import io.destiny.client.security.JwtTokenProvider;
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import org.springframework.stereotype.Component;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import static io.destiny.client.core.exception.ClientUserAuthExceptionCode.*;
|
||||
|
||||
@Component
|
||||
public class SmsLoginStrategy extends AbstractLoginStrategy {
|
||||
|
||||
private static final String LOGIN_TYPE = "sms";
|
||||
|
||||
private static final int MAX_VERIFY_COUNT = 5;
|
||||
|
||||
private final IClientUserRepository clientUserRepository;
|
||||
private final ISmsVerificationCodeRepository smsVerificationCodeRepository;
|
||||
|
||||
public SmsLoginStrategy(ClientUserAuthService clientUserAuthService,
|
||||
JwtTokenProvider jwtTokenProvider,
|
||||
IClientUserRepository clientUserRepository,
|
||||
ISmsVerificationCodeRepository smsVerificationCodeRepository) {
|
||||
super(clientUserAuthService, jwtTokenProvider);
|
||||
this.clientUserRepository = clientUserRepository;
|
||||
this.smsVerificationCodeRepository = smsVerificationCodeRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLoginType() {
|
||||
return LOGIN_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<LoginRequest> validateRequest(LoginRequest request) {
|
||||
if (request.getData() instanceof SmsLoginRequest) {
|
||||
SmsLoginRequest smsRequest = (SmsLoginRequest) request.getData();
|
||||
if (smsRequest.getPhone() == null || smsRequest.getPhone().isEmpty()) {
|
||||
return Mono.error(new ApplicationException(CLIENT_SMS_CODE_INVALID));
|
||||
}
|
||||
if (smsRequest.getCode() == null || smsRequest.getCode().isEmpty()) {
|
||||
return Mono.error(new ApplicationException(CLIENT_SMS_CODE_INVALID));
|
||||
}
|
||||
return Mono.just(request);
|
||||
}
|
||||
return Mono.error(new ApplicationException(CLIENT_SMS_CODE_INVALID));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<LoginResult> doLogin(LoginRequest request) {
|
||||
SmsLoginRequest smsRequest = (SmsLoginRequest) request.getData();
|
||||
|
||||
return smsVerificationCodeRepository
|
||||
.findLatestByPhoneAndType(smsRequest.getPhone(), SmsCodeType.LOGIN.getCode())
|
||||
.switchIfEmpty(Mono.error(new ApplicationException(CLIENT_SMS_CODE_INVALID)))
|
||||
.flatMap(verificationCode -> {
|
||||
if (verificationCode.isExpired()) {
|
||||
return Mono.error(new ApplicationException(CLIENT_SMS_CODE_EXPIRED));
|
||||
}
|
||||
if (verificationCode.isVerified()) {
|
||||
return Mono.error(new ApplicationException(CLIENT_SMS_CODE_USED));
|
||||
}
|
||||
if (verificationCode.getVerifyCount() != null
|
||||
&& verificationCode.getVerifyCount() >= MAX_VERIFY_COUNT) {
|
||||
return Mono.error(new ApplicationException(CLIENT_SMS_CODE_EXCEEDED));
|
||||
}
|
||||
if (!smsRequest.getCode().equals(verificationCode.getCode())) {
|
||||
verificationCode.incrementVerifyCount();
|
||||
return smsVerificationCodeRepository.save(verificationCode)
|
||||
.then(Mono.error(new ApplicationException(CLIENT_SMS_CODE_INVALID)));
|
||||
}
|
||||
verificationCode.markAsVerified();
|
||||
return smsVerificationCodeRepository.markAsVerified(verificationCode.getId())
|
||||
.thenReturn(verificationCode);
|
||||
})
|
||||
.flatMap(verificationCode -> clientUserRepository.findByPhone(
|
||||
io.destiny.common.primitive.PhoneNumber.of(smsRequest.getPhone())))
|
||||
.switchIfEmpty(Mono.defer(() -> createNewUser(smsRequest.getPhone())))
|
||||
.map(user -> buildLoginResult(user, "sms"));
|
||||
}
|
||||
|
||||
private Mono<ClientUser> createNewUser(String phone) {
|
||||
ClientUser newUser = new ClientUser();
|
||||
newUser.generateId();
|
||||
newUser.setPhone(io.destiny.common.primitive.PhoneNumber.of(phone));
|
||||
newUser.setPhoneVerified(true);
|
||||
newUser.setUsername(phone);
|
||||
|
||||
return clientUserRepository.save(newUser);
|
||||
}
|
||||
|
||||
private LoginResult buildLoginResult(ClientUser user, String platform) {
|
||||
LoginResult result = new LoginResult();
|
||||
result.setUserId(user.getId());
|
||||
result.setUsername(user.getUsername());
|
||||
result.setPhone(user.getPhone() != null ? user.getPhone().getValue() : null);
|
||||
result.setPlatform(platform);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
package io.destiny.client.core.strategy;
|
||||
|
||||
import io.destiny.client.core.domain.ClientLoginLog;
|
||||
import io.destiny.client.core.domain.ClientUser;
|
||||
import io.destiny.client.core.repository.IClientLoginLogRepository;
|
||||
import io.destiny.client.core.repository.IClientUserRepository;
|
||||
import io.destiny.client.core.service.ClientUserAuthService;
|
||||
import io.destiny.client.dto.LoginRequest;
|
||||
import io.destiny.client.dto.LoginResult;
|
||||
import io.destiny.client.security.JwtTokenProvider;
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import io.destiny.common.primitive.IpAddress;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static io.destiny.client.core.exception.ClientUserAuthExceptionCode.CLIENT_INVALID_USERNAME_OR_PASSWORD;
|
||||
|
||||
@Component
|
||||
public class UsernamePasswordLoginStrategy extends AbstractLoginStrategy {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(UsernamePasswordLoginStrategy.class);
|
||||
|
||||
private final IClientUserRepository clientUserRepository;
|
||||
private final IClientLoginLogRepository clientLoginLogRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
public UsernamePasswordLoginStrategy(ClientUserAuthService clientUserAuthService,
|
||||
JwtTokenProvider jwtTokenProvider,
|
||||
IClientUserRepository clientUserRepository,
|
||||
IClientLoginLogRepository clientLoginLogRepository,
|
||||
PasswordEncoder passwordEncoder) {
|
||||
super(clientUserAuthService, jwtTokenProvider);
|
||||
this.clientUserRepository = clientUserRepository;
|
||||
this.clientLoginLogRepository = clientLoginLogRepository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLoginType() {
|
||||
return "password";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<LoginRequest> validateRequest(LoginRequest request) {
|
||||
if (request.getData() instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> data = (Map<String, Object>) request.getData();
|
||||
String username = (String) data.get("username");
|
||||
String password = (String) data.get("password");
|
||||
|
||||
if (username == null || username.isEmpty()) {
|
||||
return Mono.error(new ApplicationException(CLIENT_INVALID_USERNAME_OR_PASSWORD));
|
||||
}
|
||||
if (password == null || password.isEmpty()) {
|
||||
return Mono.error(new ApplicationException(CLIENT_INVALID_USERNAME_OR_PASSWORD));
|
||||
}
|
||||
return Mono.just(request);
|
||||
}
|
||||
return Mono.error(new ApplicationException(CLIENT_INVALID_USERNAME_OR_PASSWORD));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<LoginResult> doLogin(LoginRequest request) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> data = (Map<String, Object>) request.getData();
|
||||
String username = (String) data.get("username");
|
||||
String password = (String) data.get("password");
|
||||
|
||||
logger.info("Executing username password login - Username: {}, IP: {}",
|
||||
username, request.getIpAddress());
|
||||
|
||||
return clientUserRepository.findByUsername(username)
|
||||
.switchIfEmpty(Mono.error(new ApplicationException(CLIENT_INVALID_USERNAME_OR_PASSWORD)))
|
||||
.filter(user -> passwordEncoder.matches(password, user.getPassword()))
|
||||
.switchIfEmpty(Mono.error(new ApplicationException(CLIENT_INVALID_USERNAME_OR_PASSWORD)))
|
||||
.flatMap(user -> recordLoginLog(user, request.getIpAddress())
|
||||
.thenReturn(user))
|
||||
.map(user -> {
|
||||
logger.info("Username password login successful - UserId: {}, Username: {}",
|
||||
user.getId(), username);
|
||||
LoginResult result = new LoginResult();
|
||||
result.setUserId(user.getId());
|
||||
result.setUsername(user.getUsername());
|
||||
result.setPlatform(getLoginType());
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
private Mono<Void> recordLoginLog(ClientUser user, String ipAddress) {
|
||||
ClientLoginLog loginLog = new ClientLoginLog(user.getId(), IpAddress.of(ipAddress));
|
||||
return clientLoginLogRepository.save(loginLog)
|
||||
.doOnSuccess(log -> logger.debug("Login log recorded - UserId: {}, IP: {}",
|
||||
user.getId(), ipAddress))
|
||||
.then();
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
package io.destiny.client.core.strategy;
|
||||
|
||||
import io.destiny.client.client.WechatMiniProgramClient;
|
||||
import io.destiny.client.core.domain.ClientUser;
|
||||
import io.destiny.client.core.domain.UserBinding;
|
||||
import io.destiny.client.core.repository.IClientUserRepository;
|
||||
import io.destiny.client.core.repository.IUserBindingRepository;
|
||||
import io.destiny.client.core.service.ClientUserAuthService;
|
||||
import io.destiny.client.dto.LoginRequest;
|
||||
import io.destiny.client.dto.LoginResult;
|
||||
import io.destiny.client.dto.WechatLoginRequest;
|
||||
import io.destiny.client.security.JwtTokenProvider;
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import org.springframework.stereotype.Component;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import static io.destiny.client.core.exception.ClientUserAuthExceptionCode.*;
|
||||
|
||||
@Component
|
||||
public class WechatLoginStrategy extends AbstractLoginStrategy {
|
||||
|
||||
private static final String LOGIN_TYPE = "wechat";
|
||||
|
||||
private final WechatMiniProgramClient wechatClient;
|
||||
private final IClientUserRepository clientUserRepository;
|
||||
private final IUserBindingRepository userBindingRepository;
|
||||
|
||||
public WechatLoginStrategy(ClientUserAuthService clientUserAuthService,
|
||||
JwtTokenProvider jwtTokenProvider,
|
||||
WechatMiniProgramClient wechatClient,
|
||||
IClientUserRepository clientUserRepository,
|
||||
IUserBindingRepository userBindingRepository) {
|
||||
super(clientUserAuthService, jwtTokenProvider);
|
||||
this.wechatClient = wechatClient;
|
||||
this.clientUserRepository = clientUserRepository;
|
||||
this.userBindingRepository = userBindingRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLoginType() {
|
||||
return LOGIN_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<LoginRequest> validateRequest(LoginRequest request) {
|
||||
if (request.getData() instanceof WechatLoginRequest) {
|
||||
WechatLoginRequest wechatRequest = (WechatLoginRequest) request.getData();
|
||||
if (wechatRequest.getCode() == null || wechatRequest.getCode().isEmpty()) {
|
||||
return Mono.error(new ApplicationException(CLIENT_INVALID_WECHAT_CODE));
|
||||
}
|
||||
return Mono.just(request);
|
||||
}
|
||||
return Mono.error(new ApplicationException(CLIENT_INVALID_WECHAT_CODE));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<LoginResult> doLogin(LoginRequest request) {
|
||||
WechatLoginRequest wechatRequest = (WechatLoginRequest) request.getData();
|
||||
|
||||
return wechatClient.auth(wechatRequest.getCode())
|
||||
.flatMap(authResponse -> {
|
||||
String openId = authResponse.getOpenId();
|
||||
String unionId = authResponse.getUnionId();
|
||||
|
||||
return clientUserRepository.findByWechatOpenId(openId)
|
||||
.switchIfEmpty(Mono.defer(() -> {
|
||||
if (unionId != null && !unionId.isEmpty()) {
|
||||
return clientUserRepository.findByWechatUnionId(unionId);
|
||||
}
|
||||
return Mono.empty();
|
||||
}))
|
||||
.switchIfEmpty(Mono.defer(() -> {
|
||||
if (unionId != null && !unionId.isEmpty()) {
|
||||
return userBindingRepository.findByPlatformAndUnionId("wechat", unionId)
|
||||
.flatMap(binding -> clientUserRepository.findById(binding.getUserId()));
|
||||
}
|
||||
return Mono.empty();
|
||||
}))
|
||||
.switchIfEmpty(Mono.defer(() -> createNewUser(openId, unionId)))
|
||||
.map(user -> buildLoginResult(user, "wechat"));
|
||||
});
|
||||
}
|
||||
|
||||
private Mono<ClientUser> createNewUser(String openId, String unionId) {
|
||||
ClientUser newUser = new ClientUser();
|
||||
newUser.generateId();
|
||||
newUser.setWechatOpenId(openId);
|
||||
if (unionId != null && !unionId.isEmpty()) {
|
||||
newUser.setWechatUnionId(unionId);
|
||||
}
|
||||
newUser.setPhoneVerified(false);
|
||||
|
||||
return clientUserRepository.save(newUser)
|
||||
.flatMap(user -> {
|
||||
UserBinding binding = new UserBinding();
|
||||
binding.generateId();
|
||||
binding.setUserId(user.getId());
|
||||
binding.setPlatform("wechat");
|
||||
binding.setPlatformOpenId(openId);
|
||||
if (unionId != null && !unionId.isEmpty()) {
|
||||
binding.setPlatformUnionId(unionId);
|
||||
}
|
||||
return userBindingRepository.save(binding).thenReturn(user);
|
||||
});
|
||||
}
|
||||
|
||||
private LoginResult buildLoginResult(ClientUser user, String platform) {
|
||||
LoginResult result = new LoginResult();
|
||||
result.setUserId(user.getId());
|
||||
result.setUsername(user.getUsername());
|
||||
result.setPhone(user.getPhone() != null ? user.getPhone().getValue() : null);
|
||||
result.setPlatform(platform);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package io.destiny.client.dto;
|
||||
|
||||
public class AccountBindRequest {
|
||||
|
||||
private String platform;
|
||||
|
||||
private String code;
|
||||
|
||||
public String getPlatform() {
|
||||
return platform;
|
||||
}
|
||||
|
||||
public void setPlatform(String platform) {
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package io.destiny.client.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.destiny.client.core.domain.ClientUser;
|
||||
|
||||
public class ClientLoginResponse {
|
||||
|
||||
@JsonProperty("token")
|
||||
private String token;
|
||||
|
||||
@JsonProperty("user")
|
||||
private ClientUserResponse user;
|
||||
|
||||
public ClientLoginResponse() {
|
||||
}
|
||||
|
||||
public ClientLoginResponse(String token, ClientUser user) {
|
||||
this.token = token;
|
||||
this.user = ClientUserResponse.from(user);
|
||||
}
|
||||
|
||||
public static ClientLoginResponse from(String token, ClientUser user) {
|
||||
return new ClientLoginResponse(token, user);
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public ClientUserResponse getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(ClientUserResponse user) {
|
||||
this.user = user;
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package io.destiny.client.dto;
|
||||
|
||||
import io.destiny.client.core.domain.query.ClientUserLogin;
|
||||
import io.destiny.common.primitive.IpAddress;
|
||||
|
||||
public class ClientUserLoginRequest {
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
private String ipAddress;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(String ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
public ClientUserLogin toDomain() {
|
||||
ClientUserLogin login = new ClientUserLogin();
|
||||
login.setUsername(this.username);
|
||||
login.setPassword(this.password);
|
||||
login.setIpAddress(IpAddress.of(this.ipAddress));
|
||||
return login;
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package io.destiny.client.dto;
|
||||
|
||||
import io.destiny.client.core.domain.ClientUser;
|
||||
|
||||
public class ClientUserRegisterRequest {
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
private String email;
|
||||
|
||||
private String phone;
|
||||
|
||||
private String gender;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getGender() {
|
||||
return gender;
|
||||
}
|
||||
|
||||
public void setGender(String gender) {
|
||||
this.gender = gender;
|
||||
}
|
||||
|
||||
public ClientUser toDomain() {
|
||||
ClientUser user = new ClientUser();
|
||||
user.setUsername(this.username);
|
||||
user.setPassword(this.password);
|
||||
user.setEmail(io.destiny.common.primitive.EmailAddress.of(this.email));
|
||||
user.setPhone(io.destiny.common.primitive.PhoneNumber.of(this.phone));
|
||||
user.setGender(this.gender);
|
||||
return user;
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
package io.destiny.client.dto;
|
||||
|
||||
import io.destiny.client.core.domain.ClientUser;
|
||||
|
||||
public class ClientUserResponse {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String username;
|
||||
|
||||
private String email;
|
||||
|
||||
private String phone;
|
||||
|
||||
private String gender;
|
||||
|
||||
private String token;
|
||||
|
||||
public static ClientUserResponse from(ClientUser user) {
|
||||
ClientUserResponse response = new ClientUserResponse();
|
||||
response.setId(user.getId());
|
||||
response.setUsername(user.getUsername());
|
||||
response.setEmail(user.getEmail() != null ? user.getEmail().getValue() : null);
|
||||
response.setPhone(user.getPhone() != null ? user.getPhone().getValue() : null);
|
||||
response.setGender(user.getGender());
|
||||
return response;
|
||||
}
|
||||
|
||||
public static ClientUserResponse from(ClientUser user, String token) {
|
||||
ClientUserResponse response = from(user);
|
||||
response.setToken(token);
|
||||
return response;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getGender() {
|
||||
return gender;
|
||||
}
|
||||
|
||||
public void setGender(String gender) {
|
||||
this.gender = gender;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package io.destiny.client.dto;
|
||||
|
||||
public class DouyinLoginRequest {
|
||||
|
||||
private String code;
|
||||
|
||||
private String platform;
|
||||
|
||||
private String ipAddress;
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getPlatform() {
|
||||
return platform;
|
||||
}
|
||||
|
||||
public void setPlatform(String platform) {
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(String ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package io.destiny.client.dto;
|
||||
|
||||
public class LoginRequest {
|
||||
|
||||
private String loginType;
|
||||
|
||||
private String ipAddress;
|
||||
|
||||
private Object data;
|
||||
|
||||
public String getLoginType() {
|
||||
return loginType;
|
||||
}
|
||||
|
||||
public void setLoginType(String loginType) {
|
||||
this.loginType = loginType;
|
||||
}
|
||||
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(String ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
public Object getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(Object data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package io.destiny.client.dto;
|
||||
|
||||
public class LoginResult {
|
||||
|
||||
private String token;
|
||||
|
||||
private Long userId;
|
||||
|
||||
private String username;
|
||||
|
||||
private String phone;
|
||||
|
||||
private String platform;
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getPlatform() {
|
||||
return platform;
|
||||
}
|
||||
|
||||
public void setPlatform(String platform) {
|
||||
this.platform = platform;
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package io.destiny.client.dto;
|
||||
|
||||
public class PasswordLoginRequest {
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
private String ipAddress;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(String ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package io.destiny.client.dto;
|
||||
|
||||
public class SmsCodeSendRequest {
|
||||
|
||||
private String phone;
|
||||
|
||||
private String type;
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package io.destiny.client.dto;
|
||||
|
||||
public class SmsLoginRequest {
|
||||
|
||||
private String phone;
|
||||
|
||||
private String code;
|
||||
|
||||
private String ipAddress;
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(String ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package io.destiny.client.dto;
|
||||
|
||||
public class WechatAuthResponse {
|
||||
|
||||
private String openId;
|
||||
|
||||
private String unionId;
|
||||
|
||||
private String sessionKey;
|
||||
|
||||
public String getOpenId() {
|
||||
return openId;
|
||||
}
|
||||
|
||||
public void setOpenId(String openId) {
|
||||
this.openId = openId;
|
||||
}
|
||||
|
||||
public String getUnionId() {
|
||||
return unionId;
|
||||
}
|
||||
|
||||
public void setUnionId(String unionId) {
|
||||
this.unionId = unionId;
|
||||
}
|
||||
|
||||
public String getSessionKey() {
|
||||
return sessionKey;
|
||||
}
|
||||
|
||||
public void setSessionKey(String sessionKey) {
|
||||
this.sessionKey = sessionKey;
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package io.destiny.client.dto;
|
||||
|
||||
public class WechatLoginRequest {
|
||||
|
||||
private String code;
|
||||
|
||||
private String platform;
|
||||
|
||||
private String ipAddress;
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getPlatform() {
|
||||
return platform;
|
||||
}
|
||||
|
||||
public void setPlatform(String platform) {
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(String ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
package io.destiny.client.security;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Component("clientJwtTokenProvider")
|
||||
public class JwtTokenProvider {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(JwtTokenProvider.class);
|
||||
|
||||
@Value("${jwt.secret:this-is-a-secure-jwt-secret-key-that-must-be-at-least-64-characters-long-for-hs512-algorithm}")
|
||||
private String secret;
|
||||
|
||||
@Value("${jwt.expiration:86400000}")
|
||||
private Long expiration;
|
||||
|
||||
private SecretKey getSigningKey() {
|
||||
byte[] keyBytes = secret.getBytes(StandardCharsets.UTF_8);
|
||||
return Keys.hmacShaKeyFor(keyBytes);
|
||||
}
|
||||
|
||||
public String generateToken(Long userId, String username) {
|
||||
Map<String, Object> claims = new HashMap<>();
|
||||
claims.put("userId", userId);
|
||||
claims.put("username", username);
|
||||
return createToken(claims, userId.toString());
|
||||
}
|
||||
|
||||
private String createToken(Map<String, Object> claims, String subject) {
|
||||
Date now = new Date();
|
||||
Date expiryDate = new Date(now.getTime() + expiration);
|
||||
|
||||
return Jwts.builder()
|
||||
.setClaims(claims)
|
||||
.setSubject(subject)
|
||||
.setIssuedAt(now)
|
||||
.setExpiration(expiryDate)
|
||||
.signWith(getSigningKey())
|
||||
.compact();
|
||||
}
|
||||
|
||||
public Long getUserIdFromToken(String token) {
|
||||
try {
|
||||
Claims claims = Jwts.parserBuilder()
|
||||
.setSigningKey(getSigningKey())
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
return claims.get("userId", Long.class);
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to get user id from token", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getUsernameFromToken(String token) {
|
||||
try {
|
||||
Claims claims = Jwts.parserBuilder()
|
||||
.setSigningKey(getSigningKey())
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
return claims.get("username", String.class);
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to get username from token", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean validateToken(String token) {
|
||||
try {
|
||||
Jwts.parserBuilder()
|
||||
.setSigningKey(getSigningKey())
|
||||
.build()
|
||||
.parseClaimsJws(token);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
logger.error("Invalid JWT token", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean isTokenExpired(String token) {
|
||||
try {
|
||||
Claims claims = Jwts.parserBuilder()
|
||||
.setSigningKey(getSigningKey())
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
Date expiration = claims.getExpiration();
|
||||
return expiration.before(new Date());
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to check token expiration", e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
package io.destiny.client.client;
|
||||
|
||||
import io.destiny.client.config.DouyinMiniProgramConfig;
|
||||
import io.destiny.client.core.exception.ClientUserAuthExceptionCode;
|
||||
import io.destiny.client.dto.WechatAuthResponse;
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.reactive.function.client.WebClientResponseException;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
class DouyinMiniProgramClientTest {
|
||||
|
||||
@Mock
|
||||
private WebClient webClient;
|
||||
|
||||
@Mock
|
||||
private WebClient.RequestHeadersUriSpec requestHeadersUriSpec;
|
||||
|
||||
@Mock
|
||||
private WebClient.RequestHeadersSpec requestHeadersSpec;
|
||||
|
||||
@Mock
|
||||
private WebClient.ResponseSpec responseSpec;
|
||||
|
||||
@Mock
|
||||
private DouyinMiniProgramConfig config;
|
||||
|
||||
private DouyinMiniProgramClient douyinMiniProgramClient;
|
||||
|
||||
private static final String TEST_CODE = "test_code_123";
|
||||
private static final String TEST_APP_ID = "test_app_id";
|
||||
private static final String TEST_APP_SECRET = "test_app_secret";
|
||||
private static final String TEST_OPEN_ID = "test_open_id";
|
||||
private static final String TEST_SESSION_KEY = "test_session_key";
|
||||
private static final String TEST_UNION_ID = "test_union_id";
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
when(config.getAppId()).thenReturn(TEST_APP_ID);
|
||||
when(config.getAppSecret()).thenReturn(TEST_APP_SECRET);
|
||||
douyinMiniProgramClient = new DouyinMiniProgramClient(config, webClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuth_Success() {
|
||||
String successResponse = String.format("{\"err_no\":0,\"err_tips\":\"success\",\"data\":{\"openid\":\"%s\",\"session_key\":\"%s\",\"unionid\":\"%s\"}}",
|
||||
TEST_OPEN_ID, TEST_SESSION_KEY, TEST_UNION_ID);
|
||||
|
||||
when(webClient.get()).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.uri(anyString(), any(Function.class))).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.accept(MediaType.APPLICATION_JSON)).thenReturn(requestHeadersSpec);
|
||||
when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
|
||||
when(responseSpec.bodyToMono(String.class)).thenReturn(Mono.just(successResponse));
|
||||
|
||||
Mono<WechatAuthResponse> result = douyinMiniProgramClient.auth(TEST_CODE);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNextMatches(response ->
|
||||
TEST_OPEN_ID.equals(response.getOpenId()) &&
|
||||
TEST_SESSION_KEY.equals(response.getSessionKey()) &&
|
||||
TEST_UNION_ID.equals(response.getUnionId()))
|
||||
.verifyComplete();
|
||||
|
||||
verify(webClient, times(1)).get();
|
||||
verify(requestHeadersUriSpec, times(1)).uri(anyString(), any(Function.class));
|
||||
verify(requestHeadersUriSpec, times(1)).accept(MediaType.APPLICATION_JSON);
|
||||
verify(requestHeadersSpec, times(1)).retrieve();
|
||||
verify(responseSpec, times(1)).bodyToMono(String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuth_WebClientResponseException() {
|
||||
WebClientResponseException exception = WebClientResponseException.create(
|
||||
400, "Bad Request", null, "{\"errcode\":40029,\"errmsg\":\"invalid code\"}".getBytes(), null);
|
||||
|
||||
when(webClient.get()).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.uri(anyString(), any(Function.class))).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.accept(MediaType.APPLICATION_JSON)).thenReturn(requestHeadersSpec);
|
||||
when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
|
||||
when(responseSpec.bodyToMono(String.class)).thenReturn(Mono.error(exception));
|
||||
|
||||
Mono<WechatAuthResponse> result = douyinMiniProgramClient.auth(TEST_CODE);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectErrorMatches(ex -> ex instanceof ApplicationException &&
|
||||
((ApplicationException) ex).getExceptionCode().equals(ClientUserAuthExceptionCode.CLIENT_DOUYIN_AUTH_FAILED))
|
||||
.verify();
|
||||
|
||||
verify(webClient, times(1)).get();
|
||||
verify(requestHeadersUriSpec, times(1)).uri(anyString(), any(Function.class));
|
||||
verify(requestHeadersUriSpec, times(1)).accept(MediaType.APPLICATION_JSON);
|
||||
verify(requestHeadersSpec, times(1)).retrieve();
|
||||
verify(responseSpec, times(1)).bodyToMono(String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuth_GeneralException() {
|
||||
RuntimeException exception = new RuntimeException("Network error");
|
||||
|
||||
when(webClient.get()).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.uri(anyString(), any(Function.class))).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.accept(MediaType.APPLICATION_JSON)).thenReturn(requestHeadersSpec);
|
||||
when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
|
||||
when(responseSpec.bodyToMono(String.class)).thenReturn(Mono.error(exception));
|
||||
|
||||
Mono<WechatAuthResponse> result = douyinMiniProgramClient.auth(TEST_CODE);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectErrorMatches(ex -> ex instanceof ApplicationException &&
|
||||
((ApplicationException) ex).getExceptionCode().equals(ClientUserAuthExceptionCode.CLIENT_DOUYIN_AUTH_FAILED))
|
||||
.verify();
|
||||
|
||||
verify(webClient, times(1)).get();
|
||||
verify(requestHeadersUriSpec, times(1)).uri(anyString(), any(Function.class));
|
||||
verify(requestHeadersUriSpec, times(1)).accept(MediaType.APPLICATION_JSON);
|
||||
verify(requestHeadersSpec, times(1)).retrieve();
|
||||
verify(responseSpec, times(1)).bodyToMono(String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuth_InvalidResponseFormat() {
|
||||
String invalidResponse = "invalid json";
|
||||
|
||||
when(webClient.get()).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.uri(anyString(), any(Function.class))).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.accept(MediaType.APPLICATION_JSON)).thenReturn(requestHeadersSpec);
|
||||
when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
|
||||
when(responseSpec.bodyToMono(String.class)).thenReturn(Mono.just(invalidResponse));
|
||||
|
||||
Mono<WechatAuthResponse> result = douyinMiniProgramClient.auth(TEST_CODE);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectErrorMatches(ex -> ex instanceof ApplicationException &&
|
||||
((ApplicationException) ex).getExceptionCode().equals(ClientUserAuthExceptionCode.CLIENT_DOUYIN_AUTH_FAILED))
|
||||
.verify();
|
||||
|
||||
verify(webClient, times(1)).get();
|
||||
verify(requestHeadersUriSpec, times(1)).uri(anyString(), any(Function.class));
|
||||
verify(requestHeadersUriSpec, times(1)).accept(MediaType.APPLICATION_JSON);
|
||||
verify(requestHeadersSpec, times(1)).retrieve();
|
||||
verify(responseSpec, times(1)).bodyToMono(String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuth_ResponseWithoutUnionId() {
|
||||
String responseWithoutUnionId = String.format("{\"err_no\":0,\"err_tips\":\"success\",\"data\":{\"openid\":\"%s\",\"session_key\":\"%s\"}}",
|
||||
TEST_OPEN_ID, TEST_SESSION_KEY);
|
||||
|
||||
when(webClient.get()).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.uri(anyString(), any(Function.class))).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.accept(MediaType.APPLICATION_JSON)).thenReturn(requestHeadersSpec);
|
||||
when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
|
||||
when(responseSpec.bodyToMono(String.class)).thenReturn(Mono.just(responseWithoutUnionId));
|
||||
|
||||
Mono<WechatAuthResponse> result = douyinMiniProgramClient.auth(TEST_CODE);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNextMatches(response ->
|
||||
TEST_OPEN_ID.equals(response.getOpenId()) &&
|
||||
TEST_SESSION_KEY.equals(response.getSessionKey()) &&
|
||||
response.getUnionId() == null)
|
||||
.verifyComplete();
|
||||
|
||||
verify(webClient, times(1)).get();
|
||||
verify(requestHeadersUriSpec, times(1)).uri(anyString(), any(Function.class));
|
||||
verify(requestHeadersUriSpec, times(1)).accept(MediaType.APPLICATION_JSON);
|
||||
verify(requestHeadersSpec, times(1)).retrieve();
|
||||
verify(responseSpec, times(1)).bodyToMono(String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuth_ApiError() {
|
||||
String errorResponse = "{\"err_no\":40029,\"err_tips\":\"invalid code\"}";
|
||||
|
||||
when(webClient.get()).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.uri(anyString(), any(Function.class))).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.accept(MediaType.APPLICATION_JSON)).thenReturn(requestHeadersSpec);
|
||||
when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
|
||||
when(responseSpec.bodyToMono(String.class)).thenReturn(Mono.just(errorResponse));
|
||||
|
||||
Mono<WechatAuthResponse> result = douyinMiniProgramClient.auth(TEST_CODE);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectErrorMatches(ex -> ex instanceof ApplicationException &&
|
||||
((ApplicationException) ex).getExceptionCode().equals(ClientUserAuthExceptionCode.CLIENT_DOUYIN_AUTH_FAILED))
|
||||
.verify();
|
||||
|
||||
verify(webClient, times(1)).get();
|
||||
verify(requestHeadersUriSpec, times(1)).uri(anyString(), any(Function.class));
|
||||
verify(requestHeadersUriSpec, times(1)).accept(MediaType.APPLICATION_JSON);
|
||||
verify(requestHeadersSpec, times(1)).retrieve();
|
||||
verify(responseSpec, times(1)).bodyToMono(String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuth_ResponseWithoutData() {
|
||||
String responseWithoutData = "{\"err_no\":0,\"err_tips\":\"success\"}";
|
||||
|
||||
when(webClient.get()).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.uri(anyString(), any(Function.class))).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.accept(MediaType.APPLICATION_JSON)).thenReturn(requestHeadersSpec);
|
||||
when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
|
||||
when(responseSpec.bodyToMono(String.class)).thenReturn(Mono.just(responseWithoutData));
|
||||
|
||||
Mono<WechatAuthResponse> result = douyinMiniProgramClient.auth(TEST_CODE);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectErrorMatches(ex -> ex instanceof ApplicationException &&
|
||||
((ApplicationException) ex).getExceptionCode().equals(ClientUserAuthExceptionCode.CLIENT_DOUYIN_AUTH_FAILED))
|
||||
.verify();
|
||||
|
||||
verify(webClient, times(1)).get();
|
||||
verify(requestHeadersUriSpec, times(1)).uri(anyString(), any(Function.class));
|
||||
verify(requestHeadersUriSpec, times(1)).accept(MediaType.APPLICATION_JSON);
|
||||
verify(requestHeadersSpec, times(1)).retrieve();
|
||||
verify(responseSpec, times(1)).bodyToMono(String.class);
|
||||
}
|
||||
}
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
package io.destiny.client.client;
|
||||
|
||||
import io.destiny.client.config.WechatMiniProgramConfig;
|
||||
import io.destiny.client.core.exception.ClientUserAuthExceptionCode;
|
||||
import io.destiny.client.dto.WechatAuthResponse;
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.reactive.function.client.WebClientResponseException;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
class WechatMiniProgramClientTest {
|
||||
|
||||
@Mock
|
||||
private WebClient webClient;
|
||||
|
||||
@Mock
|
||||
private WebClient.RequestHeadersUriSpec requestHeadersUriSpec;
|
||||
|
||||
@Mock
|
||||
private WebClient.RequestHeadersSpec requestHeadersSpec;
|
||||
|
||||
@Mock
|
||||
private WebClient.ResponseSpec responseSpec;
|
||||
|
||||
@Mock
|
||||
private WechatMiniProgramConfig config;
|
||||
|
||||
private WechatMiniProgramClient wechatMiniProgramClient;
|
||||
|
||||
private static final String TEST_CODE = "test_code_123";
|
||||
private static final String TEST_APP_ID = "test_app_id";
|
||||
private static final String TEST_APP_SECRET = "test_app_secret";
|
||||
private static final String TEST_OPEN_ID = "test_open_id";
|
||||
private static final String TEST_SESSION_KEY = "test_session_key";
|
||||
private static final String TEST_UNION_ID = "test_union_id";
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
when(config.getAppId()).thenReturn(TEST_APP_ID);
|
||||
when(config.getAppSecret()).thenReturn(TEST_APP_SECRET);
|
||||
wechatMiniProgramClient = new WechatMiniProgramClient(config, webClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuth_Success() {
|
||||
String successResponse = String.format("{\"openid\":\"%s\",\"session_key\":\"%s\",\"unionid\":\"%s\"}",
|
||||
TEST_OPEN_ID, TEST_SESSION_KEY, TEST_UNION_ID);
|
||||
|
||||
when(webClient.get()).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.uri(anyString(), any(Function.class))).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.accept(MediaType.APPLICATION_JSON)).thenReturn(requestHeadersSpec);
|
||||
when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
|
||||
when(responseSpec.bodyToMono(String.class)).thenReturn(Mono.just(successResponse));
|
||||
|
||||
Mono<WechatAuthResponse> result = wechatMiniProgramClient.auth(TEST_CODE);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNextMatches(response ->
|
||||
TEST_OPEN_ID.equals(response.getOpenId()) &&
|
||||
TEST_SESSION_KEY.equals(response.getSessionKey()) &&
|
||||
TEST_UNION_ID.equals(response.getUnionId()))
|
||||
.verifyComplete();
|
||||
|
||||
verify(webClient, times(1)).get();
|
||||
verify(requestHeadersUriSpec, times(1)).uri(anyString(), any(Function.class));
|
||||
verify(requestHeadersUriSpec, times(1)).accept(MediaType.APPLICATION_JSON);
|
||||
verify(requestHeadersSpec, times(1)).retrieve();
|
||||
verify(responseSpec, times(1)).bodyToMono(String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuth_WebClientResponseException() {
|
||||
WebClientResponseException exception = WebClientResponseException.create(
|
||||
400, "Bad Request", null, "{\"errcode\":40029,\"errmsg\":\"invalid code\"}".getBytes(), null);
|
||||
|
||||
when(webClient.get()).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.uri(anyString(), any(Function.class))).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.accept(MediaType.APPLICATION_JSON)).thenReturn(requestHeadersSpec);
|
||||
when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
|
||||
when(responseSpec.bodyToMono(String.class)).thenReturn(Mono.error(exception));
|
||||
|
||||
Mono<WechatAuthResponse> result = wechatMiniProgramClient.auth(TEST_CODE);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectErrorMatches(ex -> ex instanceof ApplicationException &&
|
||||
((ApplicationException) ex).getExceptionCode().equals(ClientUserAuthExceptionCode.CLIENT_WECHAT_AUTH_FAILED))
|
||||
.verify();
|
||||
|
||||
verify(webClient, times(1)).get();
|
||||
verify(requestHeadersUriSpec, times(1)).uri(anyString(), any(Function.class));
|
||||
verify(requestHeadersUriSpec, times(1)).accept(MediaType.APPLICATION_JSON);
|
||||
verify(requestHeadersSpec, times(1)).retrieve();
|
||||
verify(responseSpec, times(1)).bodyToMono(String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuth_GeneralException() {
|
||||
RuntimeException exception = new RuntimeException("Network error");
|
||||
|
||||
when(webClient.get()).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.uri(anyString(), any(Function.class))).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.accept(MediaType.APPLICATION_JSON)).thenReturn(requestHeadersSpec);
|
||||
when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
|
||||
when(responseSpec.bodyToMono(String.class)).thenReturn(Mono.error(exception));
|
||||
|
||||
Mono<WechatAuthResponse> result = wechatMiniProgramClient.auth(TEST_CODE);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectErrorMatches(ex -> ex instanceof ApplicationException &&
|
||||
((ApplicationException) ex).getExceptionCode().equals(ClientUserAuthExceptionCode.CLIENT_WECHAT_AUTH_FAILED))
|
||||
.verify();
|
||||
|
||||
verify(webClient, times(1)).get();
|
||||
verify(requestHeadersUriSpec, times(1)).uri(anyString(), any(Function.class));
|
||||
verify(requestHeadersUriSpec, times(1)).accept(MediaType.APPLICATION_JSON);
|
||||
verify(requestHeadersSpec, times(1)).retrieve();
|
||||
verify(responseSpec, times(1)).bodyToMono(String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuth_InvalidResponseFormat() {
|
||||
String invalidResponse = "invalid json";
|
||||
|
||||
when(webClient.get()).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.uri(anyString(), any(Function.class))).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.accept(MediaType.APPLICATION_JSON)).thenReturn(requestHeadersSpec);
|
||||
when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
|
||||
when(responseSpec.bodyToMono(String.class)).thenReturn(Mono.just(invalidResponse));
|
||||
|
||||
Mono<WechatAuthResponse> result = wechatMiniProgramClient.auth(TEST_CODE);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectErrorMatches(ex -> ex instanceof ApplicationException &&
|
||||
((ApplicationException) ex).getExceptionCode().equals(ClientUserAuthExceptionCode.CLIENT_WECHAT_AUTH_FAILED))
|
||||
.verify();
|
||||
|
||||
verify(webClient, times(1)).get();
|
||||
verify(requestHeadersUriSpec, times(1)).uri(anyString(), any(Function.class));
|
||||
verify(requestHeadersUriSpec, times(1)).accept(MediaType.APPLICATION_JSON);
|
||||
verify(requestHeadersSpec, times(1)).retrieve();
|
||||
verify(responseSpec, times(1)).bodyToMono(String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuth_ResponseWithoutUnionId() {
|
||||
String responseWithoutUnionId = String.format("{\"openid\":\"%s\",\"session_key\":\"%s\"}",
|
||||
TEST_OPEN_ID, TEST_SESSION_KEY);
|
||||
|
||||
when(webClient.get()).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.uri(anyString(), any(Function.class))).thenReturn(requestHeadersUriSpec);
|
||||
when(requestHeadersUriSpec.accept(MediaType.APPLICATION_JSON)).thenReturn(requestHeadersSpec);
|
||||
when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
|
||||
when(responseSpec.bodyToMono(String.class)).thenReturn(Mono.just(responseWithoutUnionId));
|
||||
|
||||
Mono<WechatAuthResponse> result = wechatMiniProgramClient.auth(TEST_CODE);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNextMatches(response ->
|
||||
TEST_OPEN_ID.equals(response.getOpenId()) &&
|
||||
TEST_SESSION_KEY.equals(response.getSessionKey()) &&
|
||||
response.getUnionId() == null)
|
||||
.verifyComplete();
|
||||
|
||||
verify(webClient, times(1)).get();
|
||||
verify(requestHeadersUriSpec, times(1)).uri(anyString(), any(Function.class));
|
||||
verify(requestHeadersUriSpec, times(1)).accept(MediaType.APPLICATION_JSON);
|
||||
verify(requestHeadersSpec, times(1)).retrieve();
|
||||
verify(responseSpec, times(1)).bodyToMono(String.class);
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package io.destiny.client.config;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class AliyunSmsConfigTest {
|
||||
|
||||
private AliyunSmsConfig aliyunSmsConfig;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
aliyunSmsConfig = new AliyunSmsConfig();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGettersAndSetters_Success() {
|
||||
aliyunSmsConfig.setAccessKeyId("testAccessKeyId");
|
||||
aliyunSmsConfig.setAccessKeySecret("testAccessKeySecret");
|
||||
aliyunSmsConfig.setEndpoint("testEndpoint");
|
||||
aliyunSmsConfig.setSignName("testSignName");
|
||||
aliyunSmsConfig.setTemplateCode("testTemplateCode");
|
||||
|
||||
assertEquals("testAccessKeyId", aliyunSmsConfig.getAccessKeyId());
|
||||
assertEquals("testAccessKeySecret", aliyunSmsConfig.getAccessKeySecret());
|
||||
assertEquals("testEndpoint", aliyunSmsConfig.getEndpoint());
|
||||
assertEquals("testSignName", aliyunSmsConfig.getSignName());
|
||||
assertEquals("testTemplateCode", aliyunSmsConfig.getTemplateCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefaultValues_Success() {
|
||||
assertEquals(10, aliyunSmsConfig.getExpireMinutes());
|
||||
assertEquals(60, aliyunSmsConfig.getSendIntervalSeconds());
|
||||
assertEquals(10, aliyunSmsConfig.getMaxSendCountPerDay());
|
||||
assertEquals(5, aliyunSmsConfig.getMaxVerifyAttempts());
|
||||
assertEquals(10, aliyunSmsConfig.getLockMinutes());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomValues_Success() {
|
||||
aliyunSmsConfig.setExpireMinutes(5);
|
||||
aliyunSmsConfig.setSendIntervalSeconds(30);
|
||||
aliyunSmsConfig.setMaxSendCountPerDay(20);
|
||||
aliyunSmsConfig.setMaxVerifyAttempts(3);
|
||||
aliyunSmsConfig.setLockMinutes(15);
|
||||
|
||||
assertEquals(5, aliyunSmsConfig.getExpireMinutes());
|
||||
assertEquals(30, aliyunSmsConfig.getSendIntervalSeconds());
|
||||
assertEquals(20, aliyunSmsConfig.getMaxSendCountPerDay());
|
||||
assertEquals(3, aliyunSmsConfig.getMaxVerifyAttempts());
|
||||
assertEquals(15, aliyunSmsConfig.getLockMinutes());
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package io.destiny.client.config;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class DouyinMiniProgramConfigTest {
|
||||
|
||||
private DouyinMiniProgramConfig douyinMiniProgramConfig;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
douyinMiniProgramConfig = new DouyinMiniProgramConfig();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGettersAndSetters_Success() {
|
||||
douyinMiniProgramConfig.setAppId("testAppId");
|
||||
douyinMiniProgramConfig.setAppSecret("testAppSecret");
|
||||
|
||||
assertEquals("testAppId", douyinMiniProgramConfig.getAppId());
|
||||
assertEquals("testAppSecret", douyinMiniProgramConfig.getAppSecret());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNullValues_Success() {
|
||||
assertNull(douyinMiniProgramConfig.getAppId());
|
||||
assertNull(douyinMiniProgramConfig.getAppSecret());
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package io.destiny.client.config;
|
||||
|
||||
import com.aliyun.dysmsapi20170525.Client;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SmsClientConfigTest {
|
||||
|
||||
private SmsClientConfig smsClientConfig;
|
||||
|
||||
@Mock
|
||||
private AliyunSmsConfig smsConfig;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
smsClientConfig = new SmsClientConfig();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSmsClientBean_Success() throws Exception {
|
||||
Client client = smsClientConfig.smsClient(smsConfig);
|
||||
|
||||
assertNotNull(client);
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package io.destiny.client.config;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class WechatMiniProgramConfigTest {
|
||||
|
||||
private WechatMiniProgramConfig wechatMiniProgramConfig;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
wechatMiniProgramConfig = new WechatMiniProgramConfig();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGettersAndSetters_Success() {
|
||||
wechatMiniProgramConfig.setAppId("testAppId");
|
||||
wechatMiniProgramConfig.setAppSecret("testAppSecret");
|
||||
|
||||
assertEquals("testAppId", wechatMiniProgramConfig.getAppId());
|
||||
assertEquals("testAppSecret", wechatMiniProgramConfig.getAppSecret());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNullValues_Success() {
|
||||
assertNull(wechatMiniProgramConfig.getAppId());
|
||||
assertNull(wechatMiniProgramConfig.getAppSecret());
|
||||
}
|
||||
}
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
package io.destiny.client.core.domain;
|
||||
|
||||
import io.destiny.common.primitive.IpAddress;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ClientLoginLogTest {
|
||||
|
||||
private static final Long TEST_ID = 12345L;
|
||||
private static final Long TEST_CLIENT_USER_ID = 67890L;
|
||||
private static final IpAddress TEST_IP_ADDRESS = IpAddress.of("192.168.1.1");
|
||||
|
||||
@Test
|
||||
void testConstructor_DefaultValues() {
|
||||
ClientLoginLog log = new ClientLoginLog();
|
||||
|
||||
assertNull(log.getId());
|
||||
assertNull(log.getClientUserId());
|
||||
assertNull(log.getIpAddress());
|
||||
assertNull(log.getLoginTime());
|
||||
assertNull(log.getLogoutTime());
|
||||
assertNull(log.getCreatedAt());
|
||||
assertNull(log.getUpdatedAt());
|
||||
assertNull(log.getDeletedAt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConstructor_WithParameters() {
|
||||
ClientLoginLog log = new ClientLoginLog(TEST_CLIENT_USER_ID, TEST_IP_ADDRESS);
|
||||
|
||||
assertNotNull(log.getId());
|
||||
assertTrue(log.getId() > 0);
|
||||
assertEquals(TEST_CLIENT_USER_ID, log.getClientUserId());
|
||||
assertEquals(TEST_IP_ADDRESS, log.getIpAddress());
|
||||
assertNotNull(log.getLoginTime());
|
||||
assertTrue(log.getLoginTime().isBefore(LocalDateTime.now().plusSeconds(1)));
|
||||
assertTrue(log.getLoginTime().isAfter(LocalDateTime.now().minusSeconds(1)));
|
||||
assertNull(log.getLogoutTime());
|
||||
assertNull(log.getCreatedAt());
|
||||
assertNull(log.getUpdatedAt());
|
||||
assertNull(log.getDeletedAt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGettersAndSetters() {
|
||||
ClientLoginLog log = new ClientLoginLog();
|
||||
|
||||
log.setId(TEST_ID);
|
||||
log.setClientUserId(TEST_CLIENT_USER_ID);
|
||||
log.setIpAddress(TEST_IP_ADDRESS);
|
||||
LocalDateTime testLoginTime = LocalDateTime.now().minusHours(1);
|
||||
LocalDateTime testLogoutTime = LocalDateTime.now().minusMinutes(30);
|
||||
LocalDateTime testCreatedAt = LocalDateTime.now().minusDays(1);
|
||||
LocalDateTime testUpdatedAt = LocalDateTime.now().minusHours(1);
|
||||
LocalDateTime testDeletedAt = LocalDateTime.now();
|
||||
log.setLoginTime(testLoginTime);
|
||||
log.setLogoutTime(testLogoutTime);
|
||||
log.setCreatedAt(testCreatedAt);
|
||||
log.setUpdatedAt(testUpdatedAt);
|
||||
log.setDeletedAt(testDeletedAt);
|
||||
|
||||
assertEquals(TEST_ID, log.getId());
|
||||
assertEquals(TEST_CLIENT_USER_ID, log.getClientUserId());
|
||||
assertEquals(TEST_IP_ADDRESS, log.getIpAddress());
|
||||
assertEquals(testLoginTime, log.getLoginTime());
|
||||
assertEquals(testLogoutTime, log.getLogoutTime());
|
||||
assertEquals(testCreatedAt, log.getCreatedAt());
|
||||
assertEquals(testUpdatedAt, log.getUpdatedAt());
|
||||
assertEquals(testDeletedAt, log.getDeletedAt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenerateId_SetsUniqueId() {
|
||||
ClientLoginLog log = new ClientLoginLog();
|
||||
log.generateId();
|
||||
|
||||
assertNotNull(log.getId());
|
||||
assertTrue(log.getId() > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenerateId_GeneratesDifferentIds() {
|
||||
ClientLoginLog log1 = new ClientLoginLog();
|
||||
ClientLoginLog log2 = new ClientLoginLog();
|
||||
|
||||
log1.generateId();
|
||||
log2.generateId();
|
||||
|
||||
assertNotNull(log1.getId());
|
||||
assertNotNull(log2.getId());
|
||||
assertNotEquals(log1.getId(), log2.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenerateId_OverwritesExistingId() {
|
||||
ClientLoginLog log = new ClientLoginLog();
|
||||
log.setId(TEST_ID);
|
||||
|
||||
log.generateId();
|
||||
|
||||
assertNotEquals(TEST_ID, log.getId());
|
||||
assertNotNull(log.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDelete_SetsDeletedAt() {
|
||||
ClientLoginLog log = new ClientLoginLog();
|
||||
assertNull(log.getDeletedAt());
|
||||
|
||||
log.delete();
|
||||
|
||||
assertNotNull(log.getDeletedAt());
|
||||
assertTrue(log.getDeletedAt().isBefore(LocalDateTime.now().plusSeconds(1)));
|
||||
assertTrue(log.getDeletedAt().isAfter(LocalDateTime.now().minusSeconds(1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDelete_CanBeCalledMultipleTimes() {
|
||||
ClientLoginLog log = new ClientLoginLog();
|
||||
log.delete();
|
||||
LocalDateTime firstDeletedAt = log.getDeletedAt();
|
||||
|
||||
log.delete();
|
||||
LocalDateTime secondDeletedAt = log.getDeletedAt();
|
||||
|
||||
assertNotNull(secondDeletedAt);
|
||||
assertNotEquals(firstDeletedAt, secondDeletedAt);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsLogoutTimeNull_WhenNull() {
|
||||
ClientLoginLog log = new ClientLoginLog();
|
||||
log.setLogoutTime(null);
|
||||
|
||||
assertTrue(log.isLogoutTimeNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsLogoutTimeNull_WhenNotNull() {
|
||||
ClientLoginLog log = new ClientLoginLog();
|
||||
LocalDateTime logoutTime = LocalDateTime.now();
|
||||
log.setLogoutTime(logoutTime);
|
||||
|
||||
assertFalse(log.isLogoutTimeNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConstructor_GeneratesId() {
|
||||
ClientLoginLog log1 = new ClientLoginLog(TEST_CLIENT_USER_ID, TEST_IP_ADDRESS);
|
||||
ClientLoginLog log2 = new ClientLoginLog(TEST_CLIENT_USER_ID, TEST_IP_ADDRESS);
|
||||
|
||||
assertNotNull(log1.getId());
|
||||
assertNotNull(log2.getId());
|
||||
assertNotEquals(log1.getId(), log2.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConstructor_SetsLoginTimeToNow() {
|
||||
LocalDateTime before = LocalDateTime.now();
|
||||
ClientLoginLog log = new ClientLoginLog(TEST_CLIENT_USER_ID, TEST_IP_ADDRESS);
|
||||
LocalDateTime after = LocalDateTime.now();
|
||||
|
||||
assertNotNull(log.getLoginTime());
|
||||
assertTrue(log.getLoginTime().isAfter(before.minusSeconds(1)));
|
||||
assertTrue(log.getLoginTime().isBefore(after.plusSeconds(1)));
|
||||
}
|
||||
}
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
package io.destiny.client.core.domain;
|
||||
|
||||
import io.destiny.common.primitive.PhoneNumber;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ClientUserTest {
|
||||
|
||||
private static final Long TEST_ID = 12345L;
|
||||
private static final String TEST_USERNAME = "testuser";
|
||||
private static final String TEST_PASSWORD = "password123";
|
||||
private static final String TEST_WECHAT_OPEN_ID = "wx_open_id_123";
|
||||
private static final String TEST_WECHAT_UNION_ID = "wx_union_id_456";
|
||||
private static final String TEST_DOUYIN_OPEN_ID = "dy_open_id_789";
|
||||
private static final String TEST_DOUYIN_UNION_ID = "dy_union_id_012";
|
||||
private static final String TEST_GENDER = "male";
|
||||
private static final String TEST_NICKNAME = "测试用户";
|
||||
private static final String TEST_AVATAR_URL = "https://example.com/avatar.jpg";
|
||||
|
||||
@Test
|
||||
void testConstructor_DefaultValues() {
|
||||
ClientUser user = new ClientUser();
|
||||
|
||||
assertNull(user.getId());
|
||||
assertNull(user.getUsername());
|
||||
assertNull(user.getPassword());
|
||||
assertNull(user.getEmail());
|
||||
assertNull(user.getPhone());
|
||||
assertNull(user.getGender());
|
||||
assertNull(user.getBirthTime());
|
||||
assertNull(user.getWechatOpenId());
|
||||
assertNull(user.getWechatUnionId());
|
||||
assertNull(user.getDouyinOpenId());
|
||||
assertNull(user.getDouyinUnionId());
|
||||
assertNull(user.getPhoneVerified());
|
||||
assertNull(user.getNickname());
|
||||
assertNull(user.getAvatarUrl());
|
||||
assertNull(user.getCreatedAt());
|
||||
assertNull(user.getUpdatedAt());
|
||||
assertNull(user.getDeletedAt());
|
||||
assertNull(user.getLoginLogs());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGettersAndSetters() {
|
||||
ClientUser user = new ClientUser();
|
||||
|
||||
user.setId(TEST_ID);
|
||||
user.setUsername(TEST_USERNAME);
|
||||
user.setPassword(TEST_PASSWORD);
|
||||
user.setGender(TEST_GENDER);
|
||||
user.setWechatOpenId(TEST_WECHAT_OPEN_ID);
|
||||
user.setWechatUnionId(TEST_WECHAT_UNION_ID);
|
||||
user.setDouyinOpenId(TEST_DOUYIN_OPEN_ID);
|
||||
user.setDouyinUnionId(TEST_DOUYIN_UNION_ID);
|
||||
user.setPhoneVerified(true);
|
||||
user.setNickname(TEST_NICKNAME);
|
||||
user.setAvatarUrl(TEST_AVATAR_URL);
|
||||
LocalDateTime testBirthTime = LocalDateTime.of(1990, 1, 1, 0, 0);
|
||||
user.setBirthTime(testBirthTime);
|
||||
LocalDateTime testCreatedAt = LocalDateTime.now().minusDays(1);
|
||||
LocalDateTime testUpdatedAt = LocalDateTime.now().minusHours(1);
|
||||
LocalDateTime testDeletedAt = LocalDateTime.now();
|
||||
user.setCreatedAt(testCreatedAt);
|
||||
user.setUpdatedAt(testUpdatedAt);
|
||||
user.setDeletedAt(testDeletedAt);
|
||||
|
||||
assertEquals(TEST_ID, user.getId());
|
||||
assertEquals(TEST_USERNAME, user.getUsername());
|
||||
assertEquals(TEST_PASSWORD, user.getPassword());
|
||||
assertEquals(TEST_GENDER, user.getGender());
|
||||
assertEquals(TEST_WECHAT_OPEN_ID, user.getWechatOpenId());
|
||||
assertEquals(TEST_WECHAT_UNION_ID, user.getWechatUnionId());
|
||||
assertEquals(TEST_DOUYIN_OPEN_ID, user.getDouyinOpenId());
|
||||
assertEquals(TEST_DOUYIN_UNION_ID, user.getDouyinUnionId());
|
||||
assertTrue(user.getPhoneVerified());
|
||||
assertEquals(TEST_NICKNAME, user.getNickname());
|
||||
assertEquals(TEST_AVATAR_URL, user.getAvatarUrl());
|
||||
assertEquals(testBirthTime, user.getBirthTime());
|
||||
assertEquals(testCreatedAt, user.getCreatedAt());
|
||||
assertEquals(testUpdatedAt, user.getUpdatedAt());
|
||||
assertEquals(testDeletedAt, user.getDeletedAt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetPhone_WithPhoneNumber() {
|
||||
ClientUser user = new ClientUser();
|
||||
PhoneNumber phoneNumber = PhoneNumber.of("13800138000");
|
||||
|
||||
user.setPhone(phoneNumber);
|
||||
|
||||
assertEquals(phoneNumber, user.getPhone());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetPhone_WithNull() {
|
||||
ClientUser user = new ClientUser();
|
||||
|
||||
user.setPhone(null);
|
||||
|
||||
assertNull(user.getPhone());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenerateId_SetsUniqueId() {
|
||||
ClientUser user = new ClientUser();
|
||||
user.generateId();
|
||||
|
||||
assertNotNull(user.getId());
|
||||
assertTrue(user.getId() > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenerateId_GeneratesDifferentIds() {
|
||||
ClientUser user1 = new ClientUser();
|
||||
ClientUser user2 = new ClientUser();
|
||||
|
||||
user1.generateId();
|
||||
user2.generateId();
|
||||
|
||||
assertNotNull(user1.getId());
|
||||
assertNotNull(user2.getId());
|
||||
assertNotEquals(user1.getId(), user2.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDelete_SetsDeletedAt() {
|
||||
ClientUser user = new ClientUser();
|
||||
assertNull(user.getDeletedAt());
|
||||
|
||||
user.delete();
|
||||
|
||||
assertNotNull(user.getDeletedAt());
|
||||
assertTrue(user.getDeletedAt().isBefore(LocalDateTime.now().plusSeconds(1)));
|
||||
assertTrue(user.getDeletedAt().isAfter(LocalDateTime.now().minusSeconds(1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDelete_CanBeCalledMultipleTimes() {
|
||||
ClientUser user = new ClientUser();
|
||||
user.delete();
|
||||
LocalDateTime firstDeletedAt = user.getDeletedAt();
|
||||
|
||||
user.delete();
|
||||
LocalDateTime secondDeletedAt = user.getDeletedAt();
|
||||
|
||||
assertNotNull(secondDeletedAt);
|
||||
assertNotEquals(firstDeletedAt, secondDeletedAt);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPhoneVerified_DefaultValue() {
|
||||
ClientUser user = new ClientUser();
|
||||
|
||||
assertNull(user.getPhoneVerified());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPhoneVerified_SetToTrue() {
|
||||
ClientUser user = new ClientUser();
|
||||
user.setPhoneVerified(true);
|
||||
|
||||
assertTrue(user.getPhoneVerified());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPhoneVerified_SetToFalse() {
|
||||
ClientUser user = new ClientUser();
|
||||
user.setPhoneVerified(false);
|
||||
|
||||
assertFalse(user.getPhoneVerified());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeletedAt_DefaultValue() {
|
||||
ClientUser user = new ClientUser();
|
||||
|
||||
assertNull(user.getDeletedAt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleted_AfterDelete() {
|
||||
ClientUser user = new ClientUser();
|
||||
user.delete();
|
||||
|
||||
assertNotNull(user.getDeletedAt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginLogs_DefaultValue() {
|
||||
ClientUser user = new ClientUser();
|
||||
|
||||
assertNull(user.getLoginLogs());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetLoginLogs_WithEmptyList() {
|
||||
ClientUser user = new ClientUser();
|
||||
user.setLoginLogs(List.of());
|
||||
|
||||
assertNotNull(user.getLoginLogs());
|
||||
assertTrue(user.getLoginLogs().isEmpty());
|
||||
}
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
package io.destiny.client.core.domain;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class SmsVerificationCodeTest {
|
||||
|
||||
private static final String TEST_PHONE = "13800138000";
|
||||
private static final String TEST_CODE = "123456";
|
||||
private static final String TEST_TYPE = "login";
|
||||
|
||||
@Test
|
||||
void testConstructor_ValidInput_Success() {
|
||||
LocalDateTime expireTime = LocalDateTime.now().plusMinutes(5);
|
||||
SmsVerificationCode code = new SmsVerificationCode(TEST_PHONE, TEST_CODE, TEST_TYPE, expireTime);
|
||||
|
||||
assertNotNull(code.getId());
|
||||
assertEquals(TEST_PHONE, code.getPhone());
|
||||
assertEquals(TEST_CODE, code.getCode());
|
||||
assertEquals(TEST_TYPE, code.getType());
|
||||
assertEquals(expireTime, code.getExpireTime());
|
||||
assertNotNull(code.getCreatedAt());
|
||||
assertNotNull(code.getUpdatedAt());
|
||||
assertFalse(code.isVerified());
|
||||
assertEquals(0, code.getVerifyCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConstructor_GeneratesUniqueId() {
|
||||
LocalDateTime expireTime = LocalDateTime.now().plusMinutes(5);
|
||||
SmsVerificationCode code1 = new SmsVerificationCode(TEST_PHONE, TEST_CODE, TEST_TYPE, expireTime);
|
||||
SmsVerificationCode code2 = new SmsVerificationCode(TEST_PHONE, TEST_CODE, TEST_TYPE, expireTime);
|
||||
|
||||
assertNotNull(code1.getId());
|
||||
assertNotNull(code2.getId());
|
||||
assertNotEquals(code1.getId(), code2.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMarkAsVerified_UpdatesStatusAndTimestamp() {
|
||||
LocalDateTime expireTime = LocalDateTime.now().plusMinutes(5);
|
||||
SmsVerificationCode code = new SmsVerificationCode(TEST_PHONE, TEST_CODE, TEST_TYPE, expireTime);
|
||||
LocalDateTime originalUpdatedAt = code.getUpdatedAt();
|
||||
|
||||
code.markAsVerified();
|
||||
|
||||
assertTrue(code.isVerified());
|
||||
assertTrue(code.getUpdatedAt().isAfter(originalUpdatedAt) || code.getUpdatedAt().isEqual(originalUpdatedAt));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIncrementVerifyCount_FromZero() {
|
||||
LocalDateTime expireTime = LocalDateTime.now().plusMinutes(5);
|
||||
SmsVerificationCode code = new SmsVerificationCode(TEST_PHONE, TEST_CODE, TEST_TYPE, expireTime);
|
||||
|
||||
code.incrementVerifyCount();
|
||||
|
||||
assertEquals(1, code.getVerifyCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIncrementVerifyCount_MultipleTimes() {
|
||||
LocalDateTime expireTime = LocalDateTime.now().plusMinutes(5);
|
||||
SmsVerificationCode code = new SmsVerificationCode(TEST_PHONE, TEST_CODE, TEST_TYPE, expireTime);
|
||||
|
||||
code.incrementVerifyCount();
|
||||
code.incrementVerifyCount();
|
||||
code.incrementVerifyCount();
|
||||
|
||||
assertEquals(3, code.getVerifyCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIncrementVerifyCount_UpdatesTimestamp() {
|
||||
LocalDateTime expireTime = LocalDateTime.now().plusMinutes(5);
|
||||
SmsVerificationCode code = new SmsVerificationCode(TEST_PHONE, TEST_CODE, TEST_TYPE, expireTime);
|
||||
LocalDateTime originalUpdatedAt = code.getUpdatedAt();
|
||||
|
||||
code.incrementVerifyCount();
|
||||
|
||||
assertTrue(code.getUpdatedAt().isAfter(originalUpdatedAt) || code.getUpdatedAt().isEqual(originalUpdatedAt));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsExpired_WithFutureExpireTime() {
|
||||
LocalDateTime expireTime = LocalDateTime.now().plusMinutes(5);
|
||||
SmsVerificationCode code = new SmsVerificationCode(TEST_PHONE, TEST_CODE, TEST_TYPE, expireTime);
|
||||
|
||||
assertFalse(code.isExpired());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsExpired_WithPastExpireTime() {
|
||||
LocalDateTime expireTime = LocalDateTime.now().minusMinutes(5);
|
||||
SmsVerificationCode code = new SmsVerificationCode(TEST_PHONE, TEST_CODE, TEST_TYPE, expireTime);
|
||||
|
||||
assertTrue(code.isExpired());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsExpired_WithCurrentTime() {
|
||||
LocalDateTime expireTime = LocalDateTime.now().plusSeconds(1);
|
||||
SmsVerificationCode code = new SmsVerificationCode(TEST_PHONE, TEST_CODE, TEST_TYPE, expireTime);
|
||||
|
||||
assertFalse(code.isExpired());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsVerified_DefaultValue() {
|
||||
LocalDateTime expireTime = LocalDateTime.now().plusMinutes(5);
|
||||
SmsVerificationCode code = new SmsVerificationCode(TEST_PHONE, TEST_CODE, TEST_TYPE, expireTime);
|
||||
|
||||
assertFalse(code.isVerified());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsVerified_AfterMarkAsVerified() {
|
||||
LocalDateTime expireTime = LocalDateTime.now().plusMinutes(5);
|
||||
SmsVerificationCode code = new SmsVerificationCode(TEST_PHONE, TEST_CODE, TEST_TYPE, expireTime);
|
||||
code.markAsVerified();
|
||||
|
||||
assertTrue(code.isVerified());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetVerifyCount_DefaultValue() {
|
||||
LocalDateTime expireTime = LocalDateTime.now().plusMinutes(5);
|
||||
SmsVerificationCode code = new SmsVerificationCode(TEST_PHONE, TEST_CODE, TEST_TYPE, expireTime);
|
||||
|
||||
assertEquals(0, code.getVerifyCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetVerifyCount_AfterIncrements() {
|
||||
LocalDateTime expireTime = LocalDateTime.now().plusMinutes(5);
|
||||
SmsVerificationCode code = new SmsVerificationCode(TEST_PHONE, TEST_CODE, TEST_TYPE, expireTime);
|
||||
code.incrementVerifyCount();
|
||||
code.incrementVerifyCount();
|
||||
|
||||
assertEquals(2, code.getVerifyCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGettersAndSetters() {
|
||||
LocalDateTime expireTime = LocalDateTime.now().plusMinutes(5);
|
||||
SmsVerificationCode code = new SmsVerificationCode(TEST_PHONE, TEST_CODE, TEST_TYPE, expireTime);
|
||||
|
||||
Long testId = 12345L;
|
||||
LocalDateTime testCreatedAt = LocalDateTime.now().minusHours(1);
|
||||
LocalDateTime testUpdatedAt = LocalDateTime.now().minusMinutes(30);
|
||||
|
||||
code.setId(testId);
|
||||
code.setCreatedAt(testCreatedAt);
|
||||
code.setUpdatedAt(testUpdatedAt);
|
||||
|
||||
assertEquals(testId, code.getId());
|
||||
assertEquals(testCreatedAt, code.getCreatedAt());
|
||||
assertEquals(testUpdatedAt, code.getUpdatedAt());
|
||||
}
|
||||
}
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
package io.destiny.client.core.domain;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class UserBindingTest {
|
||||
|
||||
private static final Long TEST_ID = 12345L;
|
||||
private static final Long TEST_USER_ID = 67890L;
|
||||
private static final String TEST_PLATFORM = "wechat";
|
||||
private static final String TEST_PLATFORM_OPEN_ID = "wx_open_id_123";
|
||||
private static final String TEST_PLATFORM_UNION_ID = "wx_union_id_456";
|
||||
|
||||
@Test
|
||||
void testConstructor_DefaultValues() {
|
||||
UserBinding binding = new UserBinding();
|
||||
|
||||
assertNull(binding.getId());
|
||||
assertNull(binding.getUserId());
|
||||
assertNull(binding.getPlatform());
|
||||
assertNull(binding.getPlatformOpenId());
|
||||
assertNull(binding.getPlatformUnionId());
|
||||
assertNull(binding.getCreatedAt());
|
||||
assertNull(binding.getUpdatedAt());
|
||||
assertNull(binding.getDeletedAt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGettersAndSetters() {
|
||||
UserBinding binding = new UserBinding();
|
||||
|
||||
binding.setId(TEST_ID);
|
||||
binding.setUserId(TEST_USER_ID);
|
||||
binding.setPlatform(TEST_PLATFORM);
|
||||
binding.setPlatformOpenId(TEST_PLATFORM_OPEN_ID);
|
||||
binding.setPlatformUnionId(TEST_PLATFORM_UNION_ID);
|
||||
LocalDateTime testCreatedAt = LocalDateTime.now().minusDays(1);
|
||||
LocalDateTime testUpdatedAt = LocalDateTime.now().minusHours(1);
|
||||
LocalDateTime testDeletedAt = LocalDateTime.now();
|
||||
binding.setCreatedAt(testCreatedAt);
|
||||
binding.setUpdatedAt(testUpdatedAt);
|
||||
binding.setDeletedAt(testDeletedAt);
|
||||
|
||||
assertEquals(TEST_ID, binding.getId());
|
||||
assertEquals(TEST_USER_ID, binding.getUserId());
|
||||
assertEquals(TEST_PLATFORM, binding.getPlatform());
|
||||
assertEquals(TEST_PLATFORM_OPEN_ID, binding.getPlatformOpenId());
|
||||
assertEquals(TEST_PLATFORM_UNION_ID, binding.getPlatformUnionId());
|
||||
assertEquals(testCreatedAt, binding.getCreatedAt());
|
||||
assertEquals(testUpdatedAt, binding.getUpdatedAt());
|
||||
assertEquals(testDeletedAt, binding.getDeletedAt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenerateId_SetsUniqueId() {
|
||||
UserBinding binding = new UserBinding();
|
||||
binding.generateId();
|
||||
|
||||
assertNotNull(binding.getId());
|
||||
assertTrue(binding.getId() > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenerateId_GeneratesDifferentIds() {
|
||||
UserBinding binding1 = new UserBinding();
|
||||
UserBinding binding2 = new UserBinding();
|
||||
|
||||
binding1.generateId();
|
||||
binding2.generateId();
|
||||
|
||||
assertNotNull(binding1.getId());
|
||||
assertNotNull(binding2.getId());
|
||||
assertNotEquals(binding1.getId(), binding2.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDelete_SetsDeletedAt() {
|
||||
UserBinding binding = new UserBinding();
|
||||
assertNull(binding.getDeletedAt());
|
||||
|
||||
binding.delete();
|
||||
|
||||
assertNotNull(binding.getDeletedAt());
|
||||
assertTrue(binding.getDeletedAt().isBefore(LocalDateTime.now().plusSeconds(1)));
|
||||
assertTrue(binding.getDeletedAt().isAfter(LocalDateTime.now().minusSeconds(1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDelete_CanBeCalledMultipleTimes() {
|
||||
UserBinding binding = new UserBinding();
|
||||
binding.delete();
|
||||
LocalDateTime firstDeletedAt = binding.getDeletedAt();
|
||||
|
||||
binding.delete();
|
||||
LocalDateTime secondDeletedAt = binding.getDeletedAt();
|
||||
|
||||
assertNotNull(secondDeletedAt);
|
||||
assertNotEquals(firstDeletedAt, secondDeletedAt);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsWechatPlatform_WhenWechat() {
|
||||
UserBinding binding = new UserBinding();
|
||||
binding.setPlatform("wechat");
|
||||
|
||||
assertTrue(binding.isWechatPlatform());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsWechatPlatform_WhenDouyin() {
|
||||
UserBinding binding = new UserBinding();
|
||||
binding.setPlatform("douyin");
|
||||
|
||||
assertFalse(binding.isWechatPlatform());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsWechatPlatform_WhenNull() {
|
||||
UserBinding binding = new UserBinding();
|
||||
binding.setPlatform(null);
|
||||
|
||||
assertFalse(binding.isWechatPlatform());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsDouyinPlatform_WhenDouyin() {
|
||||
UserBinding binding = new UserBinding();
|
||||
binding.setPlatform("douyin");
|
||||
|
||||
assertTrue(binding.isDouyinPlatform());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsDouyinPlatform_WhenWechat() {
|
||||
UserBinding binding = new UserBinding();
|
||||
binding.setPlatform("wechat");
|
||||
|
||||
assertFalse(binding.isDouyinPlatform());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsDouyinPlatform_WhenNull() {
|
||||
UserBinding binding = new UserBinding();
|
||||
binding.setPlatform(null);
|
||||
|
||||
assertFalse(binding.isDouyinPlatform());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsDeleted_WhenDeletedAtPresent() {
|
||||
UserBinding binding = new UserBinding();
|
||||
binding.setDeletedAt(LocalDateTime.now());
|
||||
|
||||
assertTrue(binding.isDeleted());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsDeleted_WhenDeletedAtNull() {
|
||||
UserBinding binding = new UserBinding();
|
||||
binding.setDeletedAt(null);
|
||||
|
||||
assertFalse(binding.isDeleted());
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package io.destiny.client.core.enums;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class PlatformTypeTest {
|
||||
|
||||
@Test
|
||||
void testGetCode() {
|
||||
assertEquals("wechat", PlatformType.WECHAT.getCode());
|
||||
assertEquals("douyin", PlatformType.DOUYIN.getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDisplayName() {
|
||||
assertEquals("微信小程序", PlatformType.WECHAT.getDisplayName());
|
||||
assertEquals("抖音小程序", PlatformType.DOUYIN.getDisplayName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFromCode() {
|
||||
assertEquals(PlatformType.WECHAT, PlatformType.fromCode("wechat"));
|
||||
assertEquals(PlatformType.DOUYIN, PlatformType.fromCode("douyin"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFromCodeInvalid() {
|
||||
IllegalArgumentException exception = assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> PlatformType.fromCode("invalid_code")
|
||||
);
|
||||
assertTrue(exception.getMessage().contains("Unknown platform type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValues() {
|
||||
PlatformType[] values = PlatformType.values();
|
||||
assertEquals(2, values.length);
|
||||
assertEquals(PlatformType.WECHAT, values[0]);
|
||||
assertEquals(PlatformType.DOUYIN, values[1]);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValueOf() {
|
||||
assertEquals(PlatformType.WECHAT, PlatformType.valueOf("WECHAT"));
|
||||
assertEquals(PlatformType.DOUYIN, PlatformType.valueOf("DOUYIN"));
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package io.destiny.client.core.enums;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class SmsCodeTypeTest {
|
||||
|
||||
@Test
|
||||
void testGetCode() {
|
||||
assertEquals("login", SmsCodeType.LOGIN.getCode());
|
||||
assertEquals("register", SmsCodeType.REGISTER.getCode());
|
||||
assertEquals("bind", SmsCodeType.BIND.getCode());
|
||||
assertEquals("reset_password", SmsCodeType.RESET_PASSWORD.getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDisplayName() {
|
||||
assertEquals("登录验证码", SmsCodeType.LOGIN.getDisplayName());
|
||||
assertEquals("注册验证码", SmsCodeType.REGISTER.getDisplayName());
|
||||
assertEquals("绑定验证码", SmsCodeType.BIND.getDisplayName());
|
||||
assertEquals("重置密码验证码", SmsCodeType.RESET_PASSWORD.getDisplayName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetTemplateCode() {
|
||||
assertEquals("SMS_123456789", SmsCodeType.LOGIN.getTemplateCode());
|
||||
assertEquals("SMS_123456790", SmsCodeType.REGISTER.getTemplateCode());
|
||||
assertEquals("SMS_123456791", SmsCodeType.BIND.getTemplateCode());
|
||||
assertEquals("SMS_123456792", SmsCodeType.RESET_PASSWORD.getTemplateCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFromCode() {
|
||||
assertEquals(SmsCodeType.LOGIN, SmsCodeType.fromCode("login"));
|
||||
assertEquals(SmsCodeType.REGISTER, SmsCodeType.fromCode("register"));
|
||||
assertEquals(SmsCodeType.BIND, SmsCodeType.fromCode("bind"));
|
||||
assertEquals(SmsCodeType.RESET_PASSWORD, SmsCodeType.fromCode("reset_password"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFromCodeInvalid() {
|
||||
IllegalArgumentException exception = assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> SmsCodeType.fromCode("invalid_code")
|
||||
);
|
||||
assertTrue(exception.getMessage().contains("Unknown SMS code type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValues() {
|
||||
SmsCodeType[] values = SmsCodeType.values();
|
||||
assertEquals(4, values.length);
|
||||
assertEquals(SmsCodeType.LOGIN, values[0]);
|
||||
assertEquals(SmsCodeType.REGISTER, values[1]);
|
||||
assertEquals(SmsCodeType.BIND, values[2]);
|
||||
assertEquals(SmsCodeType.RESET_PASSWORD, values[3]);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValueOf() {
|
||||
assertEquals(SmsCodeType.LOGIN, SmsCodeType.valueOf("LOGIN"));
|
||||
assertEquals(SmsCodeType.REGISTER, SmsCodeType.valueOf("REGISTER"));
|
||||
assertEquals(SmsCodeType.BIND, SmsCodeType.valueOf("BIND"));
|
||||
assertEquals(SmsCodeType.RESET_PASSWORD, SmsCodeType.valueOf("RESET_PASSWORD"));
|
||||
}
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
package io.destiny.client.core.exception;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ClientUserAuthExceptionCodeTest {
|
||||
|
||||
@Test
|
||||
void testGetCode() {
|
||||
assertEquals("CLIENT_AUTH_10001", ClientUserAuthExceptionCode.CLIENT_INVALID_USERNAME_OR_PASSWORD.getCode());
|
||||
assertEquals("CLIENT_AUTH_10002", ClientUserAuthExceptionCode.CLIENT_INVALID_PLATFORM.getCode());
|
||||
assertEquals("CLIENT_AUTH_10003", ClientUserAuthExceptionCode.CLIENT_INVALID_WECHAT_CODE.getCode());
|
||||
assertEquals("CLIENT_AUTH_10004", ClientUserAuthExceptionCode.CLIENT_WECHAT_AUTH_FAILED.getCode());
|
||||
assertEquals("CLIENT_AUTH_10005", ClientUserAuthExceptionCode.CLIENT_INVALID_DOUYIN_CODE.getCode());
|
||||
assertEquals("CLIENT_AUTH_10006", ClientUserAuthExceptionCode.CLIENT_DOUYIN_AUTH_FAILED.getCode());
|
||||
assertEquals("CLIENT_AUTH_10007", ClientUserAuthExceptionCode.CLIENT_SMS_CODE_EXPIRED.getCode());
|
||||
assertEquals("CLIENT_AUTH_10008", ClientUserAuthExceptionCode.CLIENT_SMS_CODE_INVALID.getCode());
|
||||
assertEquals("CLIENT_AUTH_10009", ClientUserAuthExceptionCode.CLIENT_SMS_CODE_USED.getCode());
|
||||
assertEquals("CLIENT_AUTH_10010", ClientUserAuthExceptionCode.CLIENT_SMS_CODE_EXCEEDED.getCode());
|
||||
assertEquals("CLIENT_AUTH_10011", ClientUserAuthExceptionCode.CLIENT_SMS_CODE_SEND_TOO_FAST.getCode());
|
||||
assertEquals("CLIENT_AUTH_10012", ClientUserAuthExceptionCode.CLIENT_SMS_CODE_SEND_EXCEEDED.getCode());
|
||||
assertEquals("CLIENT_AUTH_10013", ClientUserAuthExceptionCode.CLIENT_USER_NOT_FOUND.getCode());
|
||||
assertEquals("CLIENT_AUTH_10014", ClientUserAuthExceptionCode.CLIENT_ACCOUNT_ALREADY_BOUND.getCode());
|
||||
assertEquals("CLIENT_AUTH_10015", ClientUserAuthExceptionCode.CLIENT_ACCOUNT_BIND_FAILED.getCode());
|
||||
assertEquals("CLIENT_AUTH_10016", ClientUserAuthExceptionCode.CLIENT_PHONE_NOT_VERIFIED.getCode());
|
||||
assertEquals("CLIENT_AUTH_10017", ClientUserAuthExceptionCode.CLIENT_AUTO_REGISTER_FAILED.getCode());
|
||||
assertEquals("CLIENT_AUTH_10018", ClientUserAuthExceptionCode.CLIENT_LOGIN_TYPE_INVALID.getCode());
|
||||
assertEquals("CLIENT_AUTH_10019", ClientUserAuthExceptionCode.CLIENT_LOGIN_FAILED.getCode());
|
||||
assertEquals("CLIENT_AUTH_10020", ClientUserAuthExceptionCode.CLIENT_ACCOUNT_LOCKED.getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDisplayName() {
|
||||
assertEquals("用户名或密码错误", ClientUserAuthExceptionCode.CLIENT_INVALID_USERNAME_OR_PASSWORD.getDisplayName());
|
||||
assertEquals("平台标识无效", ClientUserAuthExceptionCode.CLIENT_INVALID_PLATFORM.getDisplayName());
|
||||
assertEquals("微信授权码无效", ClientUserAuthExceptionCode.CLIENT_INVALID_WECHAT_CODE.getDisplayName());
|
||||
assertEquals("微信授权失败", ClientUserAuthExceptionCode.CLIENT_WECHAT_AUTH_FAILED.getDisplayName());
|
||||
assertEquals("抖音授权码无效", ClientUserAuthExceptionCode.CLIENT_INVALID_DOUYIN_CODE.getDisplayName());
|
||||
assertEquals("抖音授权失败", ClientUserAuthExceptionCode.CLIENT_DOUYIN_AUTH_FAILED.getDisplayName());
|
||||
assertEquals("验证码已过期", ClientUserAuthExceptionCode.CLIENT_SMS_CODE_EXPIRED.getDisplayName());
|
||||
assertEquals("验证码错误", ClientUserAuthExceptionCode.CLIENT_SMS_CODE_INVALID.getDisplayName());
|
||||
assertEquals("验证码已使用", ClientUserAuthExceptionCode.CLIENT_SMS_CODE_USED.getDisplayName());
|
||||
assertEquals("验证码验证次数过多", ClientUserAuthExceptionCode.CLIENT_SMS_CODE_EXCEEDED.getDisplayName());
|
||||
assertEquals("验证码发送频率过高", ClientUserAuthExceptionCode.CLIENT_SMS_CODE_SEND_TOO_FAST.getDisplayName());
|
||||
assertEquals("验证码发送次数超限", ClientUserAuthExceptionCode.CLIENT_SMS_CODE_SEND_EXCEEDED.getDisplayName());
|
||||
assertEquals("用户不存在", ClientUserAuthExceptionCode.CLIENT_USER_NOT_FOUND.getDisplayName());
|
||||
assertEquals("账号已绑定", ClientUserAuthExceptionCode.CLIENT_ACCOUNT_ALREADY_BOUND.getDisplayName());
|
||||
assertEquals("账号绑定失败", ClientUserAuthExceptionCode.CLIENT_ACCOUNT_BIND_FAILED.getDisplayName());
|
||||
assertEquals("手机号未验证", ClientUserAuthExceptionCode.CLIENT_PHONE_NOT_VERIFIED.getDisplayName());
|
||||
assertEquals("自动注册失败", ClientUserAuthExceptionCode.CLIENT_AUTO_REGISTER_FAILED.getDisplayName());
|
||||
assertEquals("登录类型无效", ClientUserAuthExceptionCode.CLIENT_LOGIN_TYPE_INVALID.getDisplayName());
|
||||
assertEquals("登录失败", ClientUserAuthExceptionCode.CLIENT_LOGIN_FAILED.getDisplayName());
|
||||
assertEquals("账号已锁定", ClientUserAuthExceptionCode.CLIENT_ACCOUNT_LOCKED.getDisplayName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetHttpCode() {
|
||||
assertEquals("401", ClientUserAuthExceptionCode.CLIENT_INVALID_USERNAME_OR_PASSWORD.getHttpCode());
|
||||
assertEquals("400", ClientUserAuthExceptionCode.CLIENT_INVALID_PLATFORM.getHttpCode());
|
||||
assertEquals("400", ClientUserAuthExceptionCode.CLIENT_INVALID_WECHAT_CODE.getHttpCode());
|
||||
assertEquals("400", ClientUserAuthExceptionCode.CLIENT_WECHAT_AUTH_FAILED.getHttpCode());
|
||||
assertEquals("400", ClientUserAuthExceptionCode.CLIENT_INVALID_DOUYIN_CODE.getHttpCode());
|
||||
assertEquals("400", ClientUserAuthExceptionCode.CLIENT_DOUYIN_AUTH_FAILED.getHttpCode());
|
||||
assertEquals("400", ClientUserAuthExceptionCode.CLIENT_SMS_CODE_EXPIRED.getHttpCode());
|
||||
assertEquals("400", ClientUserAuthExceptionCode.CLIENT_SMS_CODE_INVALID.getHttpCode());
|
||||
assertEquals("400", ClientUserAuthExceptionCode.CLIENT_SMS_CODE_USED.getHttpCode());
|
||||
assertEquals("400", ClientUserAuthExceptionCode.CLIENT_SMS_CODE_EXCEEDED.getHttpCode());
|
||||
assertEquals("429", ClientUserAuthExceptionCode.CLIENT_SMS_CODE_SEND_TOO_FAST.getHttpCode());
|
||||
assertEquals("429", ClientUserAuthExceptionCode.CLIENT_SMS_CODE_SEND_EXCEEDED.getHttpCode());
|
||||
assertEquals("404", ClientUserAuthExceptionCode.CLIENT_USER_NOT_FOUND.getHttpCode());
|
||||
assertEquals("400", ClientUserAuthExceptionCode.CLIENT_ACCOUNT_ALREADY_BOUND.getHttpCode());
|
||||
assertEquals("400", ClientUserAuthExceptionCode.CLIENT_ACCOUNT_BIND_FAILED.getHttpCode());
|
||||
assertEquals("400", ClientUserAuthExceptionCode.CLIENT_PHONE_NOT_VERIFIED.getHttpCode());
|
||||
assertEquals("400", ClientUserAuthExceptionCode.CLIENT_AUTO_REGISTER_FAILED.getHttpCode());
|
||||
assertEquals("400", ClientUserAuthExceptionCode.CLIENT_LOGIN_TYPE_INVALID.getHttpCode());
|
||||
assertEquals("401", ClientUserAuthExceptionCode.CLIENT_LOGIN_FAILED.getHttpCode());
|
||||
assertEquals("403", ClientUserAuthExceptionCode.CLIENT_ACCOUNT_LOCKED.getHttpCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValues() {
|
||||
ClientUserAuthExceptionCode[] values = ClientUserAuthExceptionCode.values();
|
||||
assertEquals(20, values.length);
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_INVALID_USERNAME_OR_PASSWORD, values[0]);
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_INVALID_PLATFORM, values[1]);
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_INVALID_WECHAT_CODE, values[2]);
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_WECHAT_AUTH_FAILED, values[3]);
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_INVALID_DOUYIN_CODE, values[4]);
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_DOUYIN_AUTH_FAILED, values[5]);
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_SMS_CODE_EXPIRED, values[6]);
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_SMS_CODE_INVALID, values[7]);
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_SMS_CODE_USED, values[8]);
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_SMS_CODE_EXCEEDED, values[9]);
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_SMS_CODE_SEND_TOO_FAST, values[10]);
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_SMS_CODE_SEND_EXCEEDED, values[11]);
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_USER_NOT_FOUND, values[12]);
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_ACCOUNT_ALREADY_BOUND, values[13]);
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_ACCOUNT_BIND_FAILED, values[14]);
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_PHONE_NOT_VERIFIED, values[15]);
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_AUTO_REGISTER_FAILED, values[16]);
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_LOGIN_TYPE_INVALID, values[17]);
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_LOGIN_FAILED, values[18]);
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_ACCOUNT_LOCKED, values[19]);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValueOf() {
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_INVALID_USERNAME_OR_PASSWORD, ClientUserAuthExceptionCode.valueOf("CLIENT_INVALID_USERNAME_OR_PASSWORD"));
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_INVALID_PLATFORM, ClientUserAuthExceptionCode.valueOf("CLIENT_INVALID_PLATFORM"));
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_INVALID_WECHAT_CODE, ClientUserAuthExceptionCode.valueOf("CLIENT_INVALID_WECHAT_CODE"));
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_WECHAT_AUTH_FAILED, ClientUserAuthExceptionCode.valueOf("CLIENT_WECHAT_AUTH_FAILED"));
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_INVALID_DOUYIN_CODE, ClientUserAuthExceptionCode.valueOf("CLIENT_INVALID_DOUYIN_CODE"));
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_DOUYIN_AUTH_FAILED, ClientUserAuthExceptionCode.valueOf("CLIENT_DOUYIN_AUTH_FAILED"));
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_SMS_CODE_EXPIRED, ClientUserAuthExceptionCode.valueOf("CLIENT_SMS_CODE_EXPIRED"));
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_SMS_CODE_INVALID, ClientUserAuthExceptionCode.valueOf("CLIENT_SMS_CODE_INVALID"));
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_SMS_CODE_USED, ClientUserAuthExceptionCode.valueOf("CLIENT_SMS_CODE_USED"));
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_SMS_CODE_EXCEEDED, ClientUserAuthExceptionCode.valueOf("CLIENT_SMS_CODE_EXCEEDED"));
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_SMS_CODE_SEND_TOO_FAST, ClientUserAuthExceptionCode.valueOf("CLIENT_SMS_CODE_SEND_TOO_FAST"));
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_SMS_CODE_SEND_EXCEEDED, ClientUserAuthExceptionCode.valueOf("CLIENT_SMS_CODE_SEND_EXCEEDED"));
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_USER_NOT_FOUND, ClientUserAuthExceptionCode.valueOf("CLIENT_USER_NOT_FOUND"));
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_ACCOUNT_ALREADY_BOUND, ClientUserAuthExceptionCode.valueOf("CLIENT_ACCOUNT_ALREADY_BOUND"));
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_ACCOUNT_BIND_FAILED, ClientUserAuthExceptionCode.valueOf("CLIENT_ACCOUNT_BIND_FAILED"));
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_PHONE_NOT_VERIFIED, ClientUserAuthExceptionCode.valueOf("CLIENT_PHONE_NOT_VERIFIED"));
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_AUTO_REGISTER_FAILED, ClientUserAuthExceptionCode.valueOf("CLIENT_AUTO_REGISTER_FAILED"));
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_LOGIN_TYPE_INVALID, ClientUserAuthExceptionCode.valueOf("CLIENT_LOGIN_TYPE_INVALID"));
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_LOGIN_FAILED, ClientUserAuthExceptionCode.valueOf("CLIENT_LOGIN_FAILED"));
|
||||
assertEquals(ClientUserAuthExceptionCode.CLIENT_ACCOUNT_LOCKED, ClientUserAuthExceptionCode.valueOf("CLIENT_ACCOUNT_LOCKED"));
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package io.destiny.client.core.exception;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ClientUserExceptionCodeTest {
|
||||
|
||||
@Test
|
||||
void testGetCode() {
|
||||
assertEquals("CLIENT_USER_20001", ClientUserExceptionCode.CLIENT_USER_NOT_FOUND.getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDisplayName() {
|
||||
assertEquals("客户端用户不存在", ClientUserExceptionCode.CLIENT_USER_NOT_FOUND.getDisplayName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetHttpCode() {
|
||||
assertEquals("500", ClientUserExceptionCode.CLIENT_USER_NOT_FOUND.getHttpCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValues() {
|
||||
ClientUserExceptionCode[] values = ClientUserExceptionCode.values();
|
||||
assertEquals(1, values.length);
|
||||
assertEquals(ClientUserExceptionCode.CLIENT_USER_NOT_FOUND, values[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValueOf() {
|
||||
assertEquals(ClientUserExceptionCode.CLIENT_USER_NOT_FOUND, ClientUserExceptionCode.valueOf("CLIENT_USER_NOT_FOUND"));
|
||||
}
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
package io.destiny.client.core.exception;
|
||||
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import io.destiny.common.exception.ExceptionCode;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@DisplayName("GlobalExceptionHandler 测试")
|
||||
class GlobalExceptionHandlerTest {
|
||||
|
||||
private GlobalExceptionHandler globalExceptionHandler;
|
||||
|
||||
private ServerWebExchange exchange;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
globalExceptionHandler = new GlobalExceptionHandler();
|
||||
exchange = MockServerWebExchange.from(org.springframework.mock.http.server.reactive.MockServerHttpRequest
|
||||
.get("/api/test").accept(MediaType.APPLICATION_JSON));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("处理 ApplicationException - 包含 exceptionCode")
|
||||
void testHandleApplicationExceptionWithCode() {
|
||||
ApplicationException exception = new ApplicationException(
|
||||
ClientUserAuthExceptionCode.CLIENT_SMS_CODE_INVALID);
|
||||
|
||||
StepVerifier.create(globalExceptionHandler.handleException(exception, exchange))
|
||||
.assertNext(response -> {
|
||||
assertEquals(HttpStatus.BAD_REQUEST, response.statusCode());
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("处理 ApplicationException - exceptionCode 为 null,exceptionMessage 不为 null")
|
||||
void testHandleApplicationExceptionWithNullCodeAndMessage() {
|
||||
ApplicationException exception = new ApplicationException((ExceptionCode) null,
|
||||
new Object[] { "Custom error message" });
|
||||
|
||||
StepVerifier.create(globalExceptionHandler.handleException(exception, exchange))
|
||||
.assertNext(response -> {
|
||||
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.statusCode());
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("处理 ApplicationException - exceptionCode 不为 null,arguments 为 null")
|
||||
void testHandleApplicationExceptionWithCodeAndNullMessage() {
|
||||
ApplicationException exception = new ApplicationException(
|
||||
ClientUserAuthExceptionCode.CLIENT_SMS_CODE_INVALID,
|
||||
(Object[]) null);
|
||||
|
||||
StepVerifier.create(globalExceptionHandler.handleException(exception, exchange))
|
||||
.assertNext(response -> {
|
||||
assertEquals(HttpStatus.BAD_REQUEST, response.statusCode());
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("处理 ApplicationException - exceptionCode 和 arguments 都为 null")
|
||||
void testHandleApplicationExceptionWithNullCodeAndNullMessage() {
|
||||
ApplicationException exception = new ApplicationException((ExceptionCode) null, (Object[]) null);
|
||||
|
||||
StepVerifier.create(globalExceptionHandler.handleException(exception, exchange))
|
||||
.assertNext(response -> {
|
||||
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.statusCode());
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("处理 IllegalArgumentException")
|
||||
void testHandleIllegalArgumentException() {
|
||||
IllegalArgumentException exception = new IllegalArgumentException("Invalid argument");
|
||||
|
||||
StepVerifier.create(globalExceptionHandler.handleException(exception, exchange))
|
||||
.assertNext(response -> {
|
||||
assertEquals(HttpStatus.BAD_REQUEST, response.statusCode());
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("处理通用 Exception")
|
||||
void testHandleGenericException() {
|
||||
Exception exception = new Exception("Unexpected error");
|
||||
|
||||
StepVerifier.create(globalExceptionHandler.handleException(exception, exchange))
|
||||
.assertNext(response -> {
|
||||
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.statusCode());
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("处理未知 Throwable(非 Exception)")
|
||||
void testHandleUnknownException() {
|
||||
Throwable throwable = new Throwable("Unknown error");
|
||||
|
||||
StepVerifier.create(globalExceptionHandler.handleException(throwable, exchange))
|
||||
.assertNext(response -> {
|
||||
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.statusCode());
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("处理 RuntimeException(继承自 Exception)")
|
||||
void testHandleRuntimeException() {
|
||||
RuntimeException exception = new RuntimeException("Runtime error");
|
||||
|
||||
StepVerifier.create(globalExceptionHandler.handleException(exception, exchange))
|
||||
.assertNext(response -> {
|
||||
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.statusCode());
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("处理 NullPointerException(继承自 RuntimeException)")
|
||||
void testHandleNullPointerException() {
|
||||
NullPointerException exception = new NullPointerException("Null pointer");
|
||||
|
||||
StepVerifier.create(globalExceptionHandler.handleException(exception, exchange))
|
||||
.assertNext(response -> {
|
||||
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.statusCode());
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("处理 Error(非 Exception)")
|
||||
void testHandleError() {
|
||||
Error error = new Error("Fatal error");
|
||||
|
||||
StepVerifier.create(globalExceptionHandler.handleException(error, exchange))
|
||||
.assertNext(response -> {
|
||||
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.statusCode());
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
}
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
package io.destiny.client.core.service;
|
||||
|
||||
import io.destiny.client.core.domain.ClientLoginLog;
|
||||
import io.destiny.client.core.domain.ClientUser;
|
||||
import io.destiny.client.core.domain.query.ClientUserLogin;
|
||||
import io.destiny.client.core.repository.IClientLoginLogRepository;
|
||||
import io.destiny.client.core.repository.IClientUserRepository;
|
||||
import io.destiny.client.dto.ClientLoginResponse;
|
||||
import io.destiny.client.security.JwtTokenProvider;
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import io.destiny.common.primitive.EmailAddress;
|
||||
import io.destiny.common.primitive.IpAddress;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class ClientUserAuthServiceTest {
|
||||
|
||||
@Mock
|
||||
private IClientUserRepository clientUserRepository;
|
||||
|
||||
@Mock
|
||||
private IClientLoginLogRepository clientLoginLogRepository;
|
||||
|
||||
@Mock
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Mock
|
||||
private JwtTokenProvider jwtTokenProvider;
|
||||
|
||||
@InjectMocks
|
||||
private ClientUserAuthService clientUserAuthService;
|
||||
|
||||
private ClientUser testUser;
|
||||
private ClientUserLogin testLogin;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
testUser = new ClientUser();
|
||||
testUser.setId(1L);
|
||||
testUser.setUsername("testuser");
|
||||
testUser.setPassword("encodedPassword");
|
||||
testUser.setEmail(EmailAddress.of("test@example.com"));
|
||||
|
||||
testLogin = new ClientUserLogin();
|
||||
testLogin.setUsername("testuser");
|
||||
testLogin.setPassword("rawPassword");
|
||||
testLogin.setIpAddress(IpAddress.of("127.0.0.1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegister_Success() {
|
||||
ClientUser newUser = new ClientUser();
|
||||
newUser.setUsername("newuser");
|
||||
newUser.setPassword("rawPassword");
|
||||
newUser.setEmail(EmailAddress.of("new@example.com"));
|
||||
|
||||
when(passwordEncoder.encode(anyString())).thenReturn("encodedPassword");
|
||||
when(clientUserRepository.save(any(ClientUser.class))).thenReturn(Mono.just(testUser));
|
||||
|
||||
Mono<ClientUser> result = clientUserAuthService.register(newUser);
|
||||
|
||||
assertNotNull(result);
|
||||
ClientUser registeredUser = result.block();
|
||||
assertEquals("testuser", registeredUser.getUsername());
|
||||
assertEquals("encodedPassword", registeredUser.getPassword());
|
||||
|
||||
verify(passwordEncoder, times(1)).encode(anyString());
|
||||
verify(clientUserRepository, times(1)).save(any(ClientUser.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogin_Success() {
|
||||
when(clientUserRepository.findByUsername(anyString())).thenReturn(Mono.just(testUser));
|
||||
when(passwordEncoder.matches(anyString(), anyString())).thenReturn(true);
|
||||
when(clientLoginLogRepository.save(any(ClientLoginLog.class))).thenReturn(Mono.just(new ClientLoginLog()));
|
||||
when(jwtTokenProvider.generateToken(anyLong(), anyString())).thenReturn("test-token");
|
||||
|
||||
Mono<ClientLoginResponse> result = clientUserAuthService.login(testLogin);
|
||||
|
||||
assertNotNull(result);
|
||||
ClientLoginResponse response = result.block();
|
||||
assertNotNull(response);
|
||||
assertEquals("test-token", response.getToken());
|
||||
|
||||
verify(clientUserRepository, times(1)).findByUsername(anyString());
|
||||
verify(passwordEncoder, times(1)).matches(anyString(), anyString());
|
||||
verify(clientLoginLogRepository, times(1)).save(any(ClientLoginLog.class));
|
||||
verify(jwtTokenProvider, times(1)).generateToken(anyLong(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogin_InvalidPassword() {
|
||||
when(clientUserRepository.findByUsername(anyString())).thenReturn(Mono.just(testUser));
|
||||
when(passwordEncoder.matches(anyString(), anyString())).thenReturn(false);
|
||||
|
||||
Mono<ClientLoginResponse> result = clientUserAuthService.login(testLogin);
|
||||
|
||||
assertThrows(ApplicationException.class, () -> result.block());
|
||||
|
||||
verify(clientUserRepository, times(1)).findByUsername(anyString());
|
||||
verify(passwordEncoder, times(1)).matches(anyString(), anyString());
|
||||
verify(clientLoginLogRepository, never()).save(any(ClientLoginLog.class));
|
||||
verify(jwtTokenProvider, never()).generateToken(anyLong(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogin_UserNotFound() {
|
||||
when(clientUserRepository.findByUsername(anyString())).thenReturn(Mono.empty());
|
||||
|
||||
Mono<ClientLoginResponse> result = clientUserAuthService.login(testLogin);
|
||||
|
||||
assertThrows(ApplicationException.class, () -> result.block());
|
||||
|
||||
verify(clientUserRepository, times(1)).findByUsername(anyString());
|
||||
verify(passwordEncoder, never()).matches(anyString(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRefreshLoginToken_Success() {
|
||||
String oldToken = "old-test-token";
|
||||
when(jwtTokenProvider.getUserIdFromToken(anyString())).thenReturn(1L);
|
||||
when(clientUserRepository.findById(anyLong())).thenReturn(Mono.just(testUser));
|
||||
when(jwtTokenProvider.generateToken(anyLong(), anyString())).thenReturn("new-test-token");
|
||||
|
||||
Mono<ClientLoginResponse> result = clientUserAuthService.refreshLoginToken(oldToken);
|
||||
|
||||
assertNotNull(result);
|
||||
ClientLoginResponse response = result.block();
|
||||
assertNotNull(response);
|
||||
assertEquals("new-test-token", response.getToken());
|
||||
|
||||
verify(jwtTokenProvider, times(1)).getUserIdFromToken(anyString());
|
||||
verify(clientUserRepository, times(1)).findById(anyLong());
|
||||
verify(jwtTokenProvider, times(1)).generateToken(anyLong(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRefreshLoginToken_InvalidToken() {
|
||||
String oldToken = "invalid-token";
|
||||
when(jwtTokenProvider.getUserIdFromToken(anyString())).thenReturn(null);
|
||||
|
||||
Mono<ClientLoginResponse> result = clientUserAuthService.refreshLoginToken(oldToken);
|
||||
|
||||
assertThrows(ApplicationException.class, () -> result.block());
|
||||
|
||||
verify(jwtTokenProvider, times(1)).getUserIdFromToken(anyString());
|
||||
verify(clientUserRepository, never()).findById(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRefreshLoginToken_UserNotFound() {
|
||||
String oldToken = "old-test-token";
|
||||
when(jwtTokenProvider.getUserIdFromToken(anyString())).thenReturn(1L);
|
||||
when(clientUserRepository.findById(anyLong())).thenReturn(Mono.empty());
|
||||
|
||||
Mono<ClientLoginResponse> result = clientUserAuthService.refreshLoginToken(oldToken);
|
||||
|
||||
assertThrows(ApplicationException.class, () -> result.block());
|
||||
|
||||
verify(jwtTokenProvider, times(1)).getUserIdFromToken(anyString());
|
||||
verify(clientUserRepository, times(1)).findById(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogout_Success() {
|
||||
Long userId = 1L;
|
||||
ClientLoginLog loginLog = new ClientLoginLog();
|
||||
loginLog.setLogoutTime(null);
|
||||
|
||||
when(clientLoginLogRepository.findByClientUserId(anyLong())).thenReturn(Flux.just(loginLog));
|
||||
when(clientLoginLogRepository.save(any(ClientLoginLog.class))).thenReturn(Mono.just(loginLog));
|
||||
|
||||
Mono<Void> result = clientUserAuthService.logout(userId);
|
||||
|
||||
assertNotNull(result);
|
||||
result.block();
|
||||
|
||||
verify(clientLoginLogRepository, times(1)).findByClientUserId(anyLong());
|
||||
verify(clientLoginLogRepository, times(1)).save(any(ClientLoginLog.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogout_NoActiveSession() {
|
||||
Long userId = 1L;
|
||||
when(clientLoginLogRepository.findByClientUserId(anyLong())).thenReturn(Flux.empty());
|
||||
|
||||
Mono<Void> result = clientUserAuthService.logout(userId);
|
||||
|
||||
assertNotNull(result);
|
||||
result.block();
|
||||
|
||||
verify(clientLoginLogRepository, times(1)).findByClientUserId(anyLong());
|
||||
verify(clientLoginLogRepository, never()).save(any(ClientLoginLog.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCheckLoginStatus_Active() {
|
||||
Long userId = 1L;
|
||||
ClientLoginLog loginLog = new ClientLoginLog();
|
||||
loginLog.setLogoutTime(null);
|
||||
|
||||
when(clientLoginLogRepository.findByClientUserId(anyLong())).thenReturn(Flux.just(loginLog));
|
||||
|
||||
Mono<Boolean> result = clientUserAuthService.checkLoginStatus(userId);
|
||||
|
||||
assertNotNull(result);
|
||||
Boolean isActive = result.block();
|
||||
assertTrue(isActive);
|
||||
|
||||
verify(clientLoginLogRepository, times(1)).findByClientUserId(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCheckLoginStatus_Inactive() {
|
||||
Long userId = 1L;
|
||||
when(clientLoginLogRepository.findByClientUserId(anyLong())).thenReturn(Flux.empty());
|
||||
|
||||
Mono<Boolean> result = clientUserAuthService.checkLoginStatus(userId);
|
||||
|
||||
assertNotNull(result);
|
||||
Boolean isActive = result.block();
|
||||
assertFalse(isActive);
|
||||
|
||||
verify(clientLoginLogRepository, times(1)).findByClientUserId(anyLong());
|
||||
}
|
||||
}
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
package io.destiny.client.core.service;
|
||||
|
||||
import io.destiny.client.core.domain.ClientUser;
|
||||
import io.destiny.client.core.domain.query.ClientUserQuery;
|
||||
import io.destiny.client.core.repository.IClientUserRepository;
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import io.destiny.common.primitive.EmailAddress;
|
||||
import io.destiny.common.request.PageQuery;
|
||||
import io.destiny.common.response.PageModel;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class ClientUserServiceTest {
|
||||
|
||||
@Mock
|
||||
private IClientUserRepository clientUserRepository;
|
||||
|
||||
@InjectMocks
|
||||
private ClientUserService clientUserService;
|
||||
|
||||
private ClientUser testUser;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
testUser = new ClientUser();
|
||||
testUser.setId(1L);
|
||||
testUser.setUsername("testuser");
|
||||
testUser.setPassword("password");
|
||||
testUser.setEmail(EmailAddress.of("test@example.com"));
|
||||
testUser.setCreatedAt(LocalDateTime.now());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSave_Success() {
|
||||
when(clientUserRepository.save(any(ClientUser.class))).thenReturn(Mono.just(testUser));
|
||||
|
||||
Mono<ClientUser> result = clientUserService.save(testUser);
|
||||
|
||||
assertNotNull(result);
|
||||
ClientUser savedUser = result.block();
|
||||
assertEquals(1L, savedUser.getId());
|
||||
assertEquals("testuser", savedUser.getUsername());
|
||||
|
||||
verify(clientUserRepository, times(1)).save(any(ClientUser.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdate_Success() {
|
||||
ClientUser updateUser = new ClientUser();
|
||||
updateUser.setId(1L);
|
||||
updateUser.setUsername("updateduser");
|
||||
updateUser.setPassword("newpassword");
|
||||
|
||||
when(clientUserRepository.findById(anyLong())).thenReturn(Mono.just(testUser));
|
||||
when(clientUserRepository.save(any(ClientUser.class))).thenReturn(Mono.just(updateUser));
|
||||
|
||||
Mono<ClientUser> result = clientUserService.update(updateUser);
|
||||
|
||||
assertNotNull(result);
|
||||
ClientUser updatedUser = result.block();
|
||||
assertEquals("updateduser", updatedUser.getUsername());
|
||||
|
||||
verify(clientUserRepository, times(1)).findById(anyLong());
|
||||
verify(clientUserRepository, times(1)).save(any(ClientUser.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdate_UserNotFound() {
|
||||
ClientUser updateUser = new ClientUser();
|
||||
updateUser.setId(999L);
|
||||
updateUser.setUsername("updateduser");
|
||||
|
||||
when(clientUserRepository.findById(anyLong())).thenReturn(Mono.empty());
|
||||
|
||||
Mono<ClientUser> result = clientUserService.update(updateUser);
|
||||
|
||||
assertThrows(ApplicationException.class, () -> result.block());
|
||||
|
||||
verify(clientUserRepository, times(1)).findById(anyLong());
|
||||
verify(clientUserRepository, never()).save(any(ClientUser.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleteById_Success() {
|
||||
when(clientUserRepository.findById(anyLong())).thenReturn(Mono.just(testUser));
|
||||
when(clientUserRepository.save(any(ClientUser.class))).thenReturn(Mono.just(testUser));
|
||||
|
||||
Mono<Void> result = clientUserService.deleteById(1L);
|
||||
|
||||
assertNotNull(result);
|
||||
result.block();
|
||||
|
||||
assertNotNull(testUser.getDeletedAt());
|
||||
verify(clientUserRepository, times(1)).findById(anyLong());
|
||||
verify(clientUserRepository, times(1)).save(any(ClientUser.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleteById_UserNotFound() {
|
||||
when(clientUserRepository.findById(anyLong())).thenReturn(Mono.empty());
|
||||
|
||||
Mono<Void> result = clientUserService.deleteById(999L);
|
||||
|
||||
assertThrows(ApplicationException.class, () -> result.block());
|
||||
|
||||
verify(clientUserRepository, times(1)).findById(anyLong());
|
||||
verify(clientUserRepository, never()).save(any(ClientUser.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFindById_Success() {
|
||||
when(clientUserRepository.findById(anyLong())).thenReturn(Mono.just(testUser));
|
||||
|
||||
Mono<ClientUser> result = clientUserService.findById(1L);
|
||||
|
||||
assertNotNull(result);
|
||||
ClientUser foundUser = result.block();
|
||||
assertEquals(1L, foundUser.getId());
|
||||
assertEquals("testuser", foundUser.getUsername());
|
||||
|
||||
verify(clientUserRepository, times(1)).findById(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFindById_NotFound() {
|
||||
when(clientUserRepository.findById(anyLong())).thenReturn(Mono.empty());
|
||||
|
||||
Mono<ClientUser> result = clientUserService.findById(999L);
|
||||
|
||||
assertNotNull(result);
|
||||
ClientUser foundUser = result.block();
|
||||
assertNull(foundUser);
|
||||
|
||||
verify(clientUserRepository, times(1)).findById(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFindByPage_Success() {
|
||||
ClientUserQuery query = new ClientUserQuery();
|
||||
PageQuery pageQuery = new PageQuery(0, 10);
|
||||
|
||||
PageModel<ClientUser> pageModel = new PageModel<>(1L, Collections.singletonList(testUser));
|
||||
|
||||
when(clientUserRepository.findByQueryWithPagination(any(ClientUserQuery.class), any(PageQuery.class)))
|
||||
.thenReturn(Mono.just(pageModel));
|
||||
|
||||
Mono<PageModel<ClientUser>> result = clientUserService.findByPage(query, pageQuery);
|
||||
|
||||
assertNotNull(result);
|
||||
PageModel<ClientUser> resultPage = result.block();
|
||||
assertEquals(1, resultPage.getData().size());
|
||||
assertEquals(1L, resultPage.getCount());
|
||||
|
||||
verify(clientUserRepository, times(1)).findByQueryWithPagination(any(ClientUserQuery.class), any(PageQuery.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFindByPage_EmptyResult() {
|
||||
ClientUserQuery query = new ClientUserQuery();
|
||||
PageQuery pageQuery = new PageQuery(0, 10);
|
||||
|
||||
PageModel<ClientUser> pageModel = new PageModel<>(0L, Collections.emptyList());
|
||||
|
||||
when(clientUserRepository.findByQueryWithPagination(any(ClientUserQuery.class), any(PageQuery.class)))
|
||||
.thenReturn(Mono.just(pageModel));
|
||||
|
||||
Mono<PageModel<ClientUser>> result = clientUserService.findByPage(query, pageQuery);
|
||||
|
||||
assertNotNull(result);
|
||||
PageModel<ClientUser> resultPage = result.block();
|
||||
assertTrue(resultPage.getData().isEmpty());
|
||||
assertEquals(0L, resultPage.getCount());
|
||||
|
||||
verify(clientUserRepository, times(1)).findByQueryWithPagination(any(ClientUserQuery.class), any(PageQuery.class));
|
||||
}
|
||||
}
|
||||
+344
@@ -0,0 +1,344 @@
|
||||
package io.destiny.client.core.service;
|
||||
|
||||
import com.aliyun.dysmsapi20170525.Client;
|
||||
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
|
||||
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
|
||||
import com.aliyun.dysmsapi20170525.models.SendSmsResponseBody;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import io.destiny.client.config.AliyunSmsConfig;
|
||||
import io.destiny.client.core.domain.SmsVerificationCode;
|
||||
import io.destiny.client.core.enums.SmsCodeType;
|
||||
import io.destiny.client.core.exception.ClientUserAuthExceptionCode;
|
||||
import io.destiny.client.core.repository.ISmsVerificationCodeRepository;
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@DisplayName("SmsService 测试")
|
||||
class SmsServiceTest {
|
||||
|
||||
@Mock
|
||||
private AliyunSmsConfig smsConfig;
|
||||
|
||||
@Mock
|
||||
private ISmsVerificationCodeRepository smsVerificationCodeRepository;
|
||||
|
||||
@Mock
|
||||
private Client smsClient;
|
||||
|
||||
private SmsService smsService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
lenient().when(smsConfig.getSignName()).thenReturn("TestSign");
|
||||
smsService = new SmsService(smsConfig, smsVerificationCodeRepository, smsClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("发送验证码 - 手机号为 null")
|
||||
void testSendVerificationCodeWithNullPhone() {
|
||||
StepVerifier.create(smsService.sendVerificationCode(null, SmsCodeType.LOGIN))
|
||||
.expectErrorMatches(throwable -> throwable instanceof ApplicationException &&
|
||||
((ApplicationException) throwable)
|
||||
.getExceptionCode() == ClientUserAuthExceptionCode.CLIENT_SMS_CODE_INVALID)
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("发送验证码 - 手机号为空字符串")
|
||||
void testSendVerificationCodeWithEmptyPhone() {
|
||||
StepVerifier.create(smsService.sendVerificationCode("", SmsCodeType.LOGIN))
|
||||
.expectErrorMatches(throwable -> throwable instanceof ApplicationException &&
|
||||
((ApplicationException) throwable)
|
||||
.getExceptionCode() == ClientUserAuthExceptionCode.CLIENT_SMS_CODE_INVALID)
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("发送验证码 - SmsCodeType 为 null")
|
||||
void testSendVerificationCodeWithNullType() {
|
||||
StepVerifier.create(smsService.sendVerificationCode("13800138000", null))
|
||||
.expectErrorMatches(throwable -> throwable instanceof ApplicationException &&
|
||||
((ApplicationException) throwable)
|
||||
.getExceptionCode() == ClientUserAuthExceptionCode.CLIENT_SMS_CODE_SEND_EXCEEDED)
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("发送验证码 - 新手机号成功发送")
|
||||
void testSendVerificationCodeSuccess() throws Exception {
|
||||
String phone = "13800138000";
|
||||
SmsCodeType type = SmsCodeType.LOGIN;
|
||||
|
||||
SendSmsResponseBody responseBody = new SendSmsResponseBody();
|
||||
responseBody.setCode("OK");
|
||||
responseBody.setRequestId("test-request-id");
|
||||
|
||||
SendSmsResponse response = new SendSmsResponse();
|
||||
response.setBody(responseBody);
|
||||
|
||||
when(smsClient.sendSms(any(SendSmsRequest.class))).thenReturn(response);
|
||||
|
||||
SmsVerificationCode savedCode = new SmsVerificationCode();
|
||||
when(smsVerificationCodeRepository.save(any(SmsVerificationCode.class)))
|
||||
.thenReturn(Mono.just(savedCode));
|
||||
|
||||
StepVerifier.create(smsService.sendVerificationCode(phone, type))
|
||||
.assertNext(code -> {
|
||||
assertNotNull(code);
|
||||
assertEquals(6, code.length());
|
||||
assertTrue(code.chars().allMatch(Character::isDigit));
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
ArgumentCaptor<SmsVerificationCode> codeCaptor = ArgumentCaptor.forClass(SmsVerificationCode.class);
|
||||
verify(smsVerificationCodeRepository).save(codeCaptor.capture());
|
||||
|
||||
SmsVerificationCode capturedCode = codeCaptor.getValue();
|
||||
assertEquals(phone, capturedCode.getPhone());
|
||||
assertEquals(type.getCode(), capturedCode.getType());
|
||||
assertNotNull(capturedCode.getExpireTime());
|
||||
assertTrue(capturedCode.getExpireTime().isAfter(LocalDateTime.now()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("发送验证码 - 分钟速率限制已达到且未过期")
|
||||
void testSendVerificationCodeMinuteRateLimitExceeded() throws Exception {
|
||||
String phone = "13800138000";
|
||||
SmsCodeType type = SmsCodeType.LOGIN;
|
||||
|
||||
SendSmsResponseBody responseBody = new SendSmsResponseBody();
|
||||
responseBody.setCode("OK");
|
||||
responseBody.setRequestId("test-request-id");
|
||||
|
||||
SendSmsResponse response = new SendSmsResponse();
|
||||
response.setBody(responseBody);
|
||||
|
||||
when(smsClient.sendSms(any(SendSmsRequest.class))).thenReturn(response);
|
||||
when(smsVerificationCodeRepository.save(any(SmsVerificationCode.class)))
|
||||
.thenReturn(Mono.just(new SmsVerificationCode()));
|
||||
|
||||
StepVerifier.create(smsService.sendVerificationCode(phone, type))
|
||||
.assertNext(code -> assertNotNull(code))
|
||||
.verifyComplete();
|
||||
|
||||
StepVerifier.create(smsService.sendVerificationCode(phone, type))
|
||||
.expectErrorMatches(throwable -> throwable instanceof ApplicationException &&
|
||||
((ApplicationException) throwable)
|
||||
.getExceptionCode() == ClientUserAuthExceptionCode.CLIENT_SMS_CODE_SEND_TOO_FAST)
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("发送验证码 - 分钟速率限制已达到但已过期")
|
||||
void testSendVerificationCodeMinuteRateLimitExpired() throws Exception {
|
||||
String phone = "13800138000";
|
||||
SmsCodeType type = SmsCodeType.LOGIN;
|
||||
|
||||
SendSmsResponseBody responseBody = new SendSmsResponseBody();
|
||||
responseBody.setCode("OK");
|
||||
responseBody.setRequestId("test-request-id");
|
||||
|
||||
SendSmsResponse response = new SendSmsResponse();
|
||||
response.setBody(responseBody);
|
||||
|
||||
when(smsClient.sendSms(any(SendSmsRequest.class))).thenReturn(response);
|
||||
when(smsVerificationCodeRepository.save(any(SmsVerificationCode.class)))
|
||||
.thenReturn(Mono.just(new SmsVerificationCode()));
|
||||
|
||||
StepVerifier.create(smsService.sendVerificationCode(phone, type))
|
||||
.assertNext(code -> assertNotNull(code))
|
||||
.verifyComplete();
|
||||
|
||||
modifySendRecordExpireTime(phone, LocalDateTime.now().minusMinutes(1));
|
||||
|
||||
StepVerifier.create(smsService.sendVerificationCode(phone, type))
|
||||
.assertNext(code -> assertNotNull(code))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("发送验证码 - 日速率限制已达到且未过期")
|
||||
void testSendVerificationCodeDayRateLimitExceeded() throws Exception {
|
||||
String phone = "13800138000";
|
||||
SmsCodeType type = SmsCodeType.LOGIN;
|
||||
|
||||
SendSmsResponseBody responseBody = new SendSmsResponseBody();
|
||||
responseBody.setCode("OK");
|
||||
responseBody.setRequestId("test-request-id");
|
||||
|
||||
SendSmsResponse response = new SendSmsResponse();
|
||||
response.setBody(responseBody);
|
||||
|
||||
when(smsClient.sendSms(any(SendSmsRequest.class))).thenReturn(response);
|
||||
when(smsVerificationCodeRepository.save(any(SmsVerificationCode.class)))
|
||||
.thenReturn(Mono.just(new SmsVerificationCode()));
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
StepVerifier.create(smsService.sendVerificationCode(phone, type))
|
||||
.assertNext(code -> assertNotNull(code))
|
||||
.verifyComplete();
|
||||
modifySendRecordExpireTime(phone, LocalDateTime.now().minusMinutes(1));
|
||||
}
|
||||
|
||||
StepVerifier.create(smsService.sendVerificationCode(phone, type))
|
||||
.expectErrorMatches(throwable -> throwable instanceof ApplicationException &&
|
||||
((ApplicationException) throwable)
|
||||
.getExceptionCode() == ClientUserAuthExceptionCode.CLIENT_SMS_CODE_SEND_EXCEEDED)
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("发送验证码 - 日速率限制已达到但已过期")
|
||||
void testSendVerificationCodeDayRateLimitExpired() throws Exception {
|
||||
String phone = "13800138000";
|
||||
SmsCodeType type = SmsCodeType.LOGIN;
|
||||
|
||||
SendSmsResponseBody responseBody = new SendSmsResponseBody();
|
||||
responseBody.setCode("OK");
|
||||
responseBody.setRequestId("test-request-id");
|
||||
|
||||
SendSmsResponse response = new SendSmsResponse();
|
||||
response.setBody(responseBody);
|
||||
|
||||
when(smsClient.sendSms(any(SendSmsRequest.class))).thenReturn(response);
|
||||
when(smsVerificationCodeRepository.save(any(SmsVerificationCode.class)))
|
||||
.thenReturn(Mono.just(new SmsVerificationCode()));
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
StepVerifier.create(smsService.sendVerificationCode(phone, type))
|
||||
.assertNext(code -> assertNotNull(code))
|
||||
.verifyComplete();
|
||||
modifySendRecordExpireTime(phone, LocalDateTime.now().minusMinutes(1));
|
||||
}
|
||||
|
||||
modifySendRecordDayExpireTime(phone, LocalDateTime.now().minusDays(1));
|
||||
|
||||
StepVerifier.create(smsService.sendVerificationCode(phone, type))
|
||||
.assertNext(code -> assertNotNull(code))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("发送验证码 - 阿里云短信发送失败")
|
||||
void testSendVerificationCodeAliyunSendFailed() throws Exception {
|
||||
String phone = "13800138000";
|
||||
SmsCodeType type = SmsCodeType.LOGIN;
|
||||
|
||||
SendSmsResponseBody responseBody = new SendSmsResponseBody();
|
||||
responseBody.setCode("FAILED");
|
||||
responseBody.setMessage("Invalid phone number");
|
||||
|
||||
SendSmsResponse response = new SendSmsResponse();
|
||||
response.setBody(responseBody);
|
||||
|
||||
when(smsClient.sendSms(any(SendSmsRequest.class))).thenReturn(response);
|
||||
|
||||
StepVerifier.create(smsService.sendVerificationCode(phone, type))
|
||||
.expectErrorMatches(throwable -> throwable instanceof ApplicationException &&
|
||||
((ApplicationException) throwable)
|
||||
.getExceptionCode() == ClientUserAuthExceptionCode.CLIENT_SMS_CODE_SEND_EXCEEDED)
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("发送验证码 - 短信签名为 null")
|
||||
void testSendVerificationCodeWithNullSignName() throws Exception {
|
||||
String phone = "13800138000";
|
||||
SmsCodeType type = SmsCodeType.LOGIN;
|
||||
|
||||
when(smsConfig.getSignName()).thenReturn(null);
|
||||
|
||||
smsService = new SmsService(smsConfig, smsVerificationCodeRepository, smsClient);
|
||||
|
||||
StepVerifier.create(smsService.sendVerificationCode(phone, type))
|
||||
.expectErrorMatches(throwable -> throwable instanceof ApplicationException &&
|
||||
((ApplicationException) throwable)
|
||||
.getExceptionCode() == ClientUserAuthExceptionCode.CLIENT_SMS_CODE_SEND_EXCEEDED)
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("发送验证码 - 不同的验证码类型")
|
||||
void testSendVerificationCodeWithDifferentTypes() throws Exception {
|
||||
SendSmsResponseBody responseBody = new SendSmsResponseBody();
|
||||
responseBody.setCode("OK");
|
||||
responseBody.setRequestId("test-request-id");
|
||||
|
||||
SendSmsResponse response = new SendSmsResponse();
|
||||
response.setBody(responseBody);
|
||||
|
||||
when(smsClient.sendSms(any(SendSmsRequest.class))).thenReturn(response);
|
||||
|
||||
SmsVerificationCode savedCode = new SmsVerificationCode();
|
||||
when(smsVerificationCodeRepository.save(any(SmsVerificationCode.class)))
|
||||
.thenReturn(Mono.just(savedCode));
|
||||
|
||||
SmsCodeType[] types = { SmsCodeType.LOGIN, SmsCodeType.REGISTER, SmsCodeType.BIND,
|
||||
SmsCodeType.RESET_PASSWORD };
|
||||
|
||||
for (SmsCodeType type : types) {
|
||||
String phone = "1380013800" + type.getCode();
|
||||
StepVerifier.create(smsService.sendVerificationCode(phone, type))
|
||||
.assertNext(code -> assertNotNull(code))
|
||||
.verifyComplete();
|
||||
|
||||
ArgumentCaptor<SmsVerificationCode> codeCaptor = ArgumentCaptor
|
||||
.forClass(SmsVerificationCode.class);
|
||||
verify(smsVerificationCodeRepository, atLeastOnce()).save(codeCaptor.capture());
|
||||
|
||||
SmsVerificationCode capturedCode = codeCaptor.getValue();
|
||||
assertEquals(type.getCode(), capturedCode.getType());
|
||||
}
|
||||
}
|
||||
|
||||
private void modifySendRecordExpireTime(String phone, LocalDateTime newExpireTime) {
|
||||
try {
|
||||
Field cacheField = SmsService.class.getDeclaredField("sendRecords");
|
||||
cacheField.setAccessible(true);
|
||||
@SuppressWarnings("unchecked")
|
||||
Cache<String, Object> cache = (Cache<String, Object>) cacheField.get(smsService);
|
||||
Object sendRecord = cache.getIfPresent(phone);
|
||||
if (sendRecord != null) {
|
||||
Field minuteExpireTimeField = sendRecord.getClass()
|
||||
.getDeclaredField("minuteExpireTime");
|
||||
minuteExpireTimeField.setAccessible(true);
|
||||
minuteExpireTimeField.set(sendRecord, newExpireTime);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to modify send record expire time", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void modifySendRecordDayExpireTime(String phone, LocalDateTime newExpireTime) {
|
||||
try {
|
||||
Field cacheField = SmsService.class.getDeclaredField("sendRecords");
|
||||
cacheField.setAccessible(true);
|
||||
@SuppressWarnings("unchecked")
|
||||
Cache<String, Object> cache = (Cache<String, Object>) cacheField.get(smsService);
|
||||
Object sendRecord = cache.getIfPresent(phone);
|
||||
if (sendRecord != null) {
|
||||
Field dayExpireTimeField = sendRecord.getClass().getDeclaredField("dayExpireTime");
|
||||
dayExpireTimeField.setAccessible(true);
|
||||
dayExpireTimeField.set(sendRecord, newExpireTime);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to modify send record day expire time", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+324
@@ -0,0 +1,324 @@
|
||||
package io.destiny.client.core.strategy;
|
||||
|
||||
import io.destiny.client.client.DouyinMiniProgramClient;
|
||||
import io.destiny.client.core.domain.ClientUser;
|
||||
import io.destiny.client.core.domain.UserBinding;
|
||||
import io.destiny.client.core.repository.IClientUserRepository;
|
||||
import io.destiny.client.core.repository.IUserBindingRepository;
|
||||
import io.destiny.client.core.service.ClientUserAuthService;
|
||||
import io.destiny.client.dto.DouyinLoginRequest;
|
||||
import io.destiny.client.dto.LoginRequest;
|
||||
|
||||
import io.destiny.client.dto.WechatAuthResponse;
|
||||
import io.destiny.client.security.JwtTokenProvider;
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import static io.destiny.client.core.exception.ClientUserAuthExceptionCode.CLIENT_INVALID_DOUYIN_CODE;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class DouyinLoginStrategyTest {
|
||||
|
||||
@Mock
|
||||
private ClientUserAuthService clientUserAuthService;
|
||||
|
||||
@Mock
|
||||
private JwtTokenProvider jwtTokenProvider;
|
||||
|
||||
@Mock
|
||||
private DouyinMiniProgramClient douyinClient;
|
||||
|
||||
@Mock
|
||||
private IClientUserRepository clientUserRepository;
|
||||
|
||||
@Mock
|
||||
private IUserBindingRepository userBindingRepository;
|
||||
|
||||
@InjectMocks
|
||||
private DouyinLoginStrategy douyinLoginStrategy;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetLoginType() {
|
||||
assertEquals("douyin", douyinLoginStrategy.getLoginType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateRequestWithValidData() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
DouyinLoginRequest douyinRequest = new DouyinLoginRequest();
|
||||
douyinRequest.setCode("valid_code");
|
||||
request.setData(douyinRequest);
|
||||
|
||||
StepVerifier.create(douyinLoginStrategy.validateRequest(request))
|
||||
.expectNext(request)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateRequestWithNullCode() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
DouyinLoginRequest douyinRequest = new DouyinLoginRequest();
|
||||
douyinRequest.setCode(null);
|
||||
request.setData(douyinRequest);
|
||||
|
||||
StepVerifier.create(douyinLoginStrategy.validateRequest(request))
|
||||
.expectErrorMatches(error -> error instanceof ApplicationException &&
|
||||
((ApplicationException) error).getExceptionCode().equals(CLIENT_INVALID_DOUYIN_CODE))
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateRequestWithEmptyCode() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
DouyinLoginRequest douyinRequest = new DouyinLoginRequest();
|
||||
douyinRequest.setCode("");
|
||||
request.setData(douyinRequest);
|
||||
|
||||
StepVerifier.create(douyinLoginStrategy.validateRequest(request))
|
||||
.expectErrorMatches(error -> error instanceof ApplicationException &&
|
||||
((ApplicationException) error).getExceptionCode().equals(CLIENT_INVALID_DOUYIN_CODE))
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateRequestWithInvalidDataType() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setData("invalid_data");
|
||||
|
||||
StepVerifier.create(douyinLoginStrategy.validateRequest(request))
|
||||
.expectErrorMatches(error -> error instanceof ApplicationException &&
|
||||
((ApplicationException) error).getExceptionCode().equals(CLIENT_INVALID_DOUYIN_CODE))
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginSuccessWithExistingUserByOpenId() {
|
||||
when(jwtTokenProvider.generateToken(anyLong(), anyString())).thenReturn("test-token");
|
||||
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("douyin");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
DouyinLoginRequest douyinRequest = new DouyinLoginRequest();
|
||||
douyinRequest.setCode("valid_code");
|
||||
request.setData(douyinRequest);
|
||||
|
||||
WechatAuthResponse authResponse = new WechatAuthResponse();
|
||||
authResponse.setOpenId("open_id_123");
|
||||
authResponse.setUnionId("union_id_456");
|
||||
authResponse.setSessionKey("session_key");
|
||||
|
||||
ClientUser user = new ClientUser();
|
||||
user.setId(1L);
|
||||
user.setUsername("testuser");
|
||||
user.setDouyinOpenId("open_id_123");
|
||||
|
||||
when(douyinClient.auth("valid_code")).thenReturn(Mono.just(authResponse));
|
||||
when(clientUserRepository.findByDouyinOpenId("open_id_123")).thenReturn(Mono.just(user));
|
||||
|
||||
StepVerifier.create(douyinLoginStrategy.login(request))
|
||||
.expectNextMatches(result -> result.getUserId().equals(1L) &&
|
||||
result.getUsername().equals("testuser") &&
|
||||
result.getPlatform().equals("douyin") &&
|
||||
result.getToken().equals("test-token"))
|
||||
.verifyComplete();
|
||||
|
||||
verify(clientUserRepository, never()).findByDouyinUnionId(anyString());
|
||||
verify(userBindingRepository, never()).findByPlatformAndUnionId(anyString(), anyString());
|
||||
verify(clientUserRepository, never()).save(any(ClientUser.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginSuccessWithExistingUserByUnionId() {
|
||||
when(jwtTokenProvider.generateToken(anyLong(), anyString())).thenReturn("test-token");
|
||||
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("douyin");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
DouyinLoginRequest douyinRequest = new DouyinLoginRequest();
|
||||
douyinRequest.setCode("valid_code");
|
||||
request.setData(douyinRequest);
|
||||
|
||||
WechatAuthResponse authResponse = new WechatAuthResponse();
|
||||
authResponse.setOpenId("open_id_123");
|
||||
authResponse.setUnionId("union_id_456");
|
||||
authResponse.setSessionKey("session_key");
|
||||
|
||||
ClientUser user = new ClientUser();
|
||||
user.setId(1L);
|
||||
user.setUsername("testuser");
|
||||
user.setDouyinUnionId("union_id_456");
|
||||
|
||||
when(douyinClient.auth("valid_code")).thenReturn(Mono.just(authResponse));
|
||||
when(clientUserRepository.findByDouyinOpenId("open_id_123")).thenReturn(Mono.empty());
|
||||
when(clientUserRepository.findByDouyinUnionId("union_id_456")).thenReturn(Mono.just(user));
|
||||
|
||||
StepVerifier.create(douyinLoginStrategy.login(request))
|
||||
.expectNextMatches(result -> result.getUserId().equals(1L) &&
|
||||
result.getUsername().equals("testuser") &&
|
||||
result.getPlatform().equals("douyin") &&
|
||||
result.getToken().equals("test-token"))
|
||||
.verifyComplete();
|
||||
|
||||
verify(userBindingRepository, never()).findByPlatformAndUnionId(anyString(), anyString());
|
||||
verify(clientUserRepository, never()).save(any(ClientUser.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginSuccessWithExistingUserByBinding() {
|
||||
when(jwtTokenProvider.generateToken(anyLong(), anyString())).thenReturn("test-token");
|
||||
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("douyin");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
DouyinLoginRequest douyinRequest = new DouyinLoginRequest();
|
||||
douyinRequest.setCode("valid_code");
|
||||
request.setData(douyinRequest);
|
||||
|
||||
WechatAuthResponse authResponse = new WechatAuthResponse();
|
||||
authResponse.setOpenId("open_id_123");
|
||||
authResponse.setUnionId("union_id_456");
|
||||
authResponse.setSessionKey("session_key");
|
||||
|
||||
ClientUser user = new ClientUser();
|
||||
user.setId(1L);
|
||||
user.setUsername("testuser");
|
||||
|
||||
UserBinding binding = new UserBinding();
|
||||
binding.setUserId(1L);
|
||||
binding.setPlatform("douyin");
|
||||
binding.setPlatformUnionId("union_id_456");
|
||||
|
||||
when(douyinClient.auth("valid_code")).thenReturn(Mono.just(authResponse));
|
||||
when(clientUserRepository.findByDouyinOpenId("open_id_123")).thenReturn(Mono.empty());
|
||||
when(clientUserRepository.findByDouyinUnionId("union_id_456")).thenReturn(Mono.empty());
|
||||
when(userBindingRepository.findByPlatformAndUnionId("douyin", "union_id_456")).thenReturn(Mono.just(binding));
|
||||
when(clientUserRepository.findById(1L)).thenReturn(Mono.just(user));
|
||||
|
||||
StepVerifier.create(douyinLoginStrategy.login(request))
|
||||
.expectNextMatches(result -> result.getUserId().equals(1L) &&
|
||||
result.getUsername().equals("testuser") &&
|
||||
result.getPlatform().equals("douyin") &&
|
||||
result.getToken().equals("test-token"))
|
||||
.verifyComplete();
|
||||
|
||||
verify(clientUserRepository, never()).save(any(ClientUser.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginSuccessWithNewUser() {
|
||||
when(jwtTokenProvider.generateToken(anyLong(), anyString())).thenReturn("test-token");
|
||||
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("douyin");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
DouyinLoginRequest douyinRequest = new DouyinLoginRequest();
|
||||
douyinRequest.setCode("valid_code");
|
||||
request.setData(douyinRequest);
|
||||
|
||||
WechatAuthResponse authResponse = new WechatAuthResponse();
|
||||
authResponse.setOpenId("open_id_123");
|
||||
authResponse.setUnionId("union_id_456");
|
||||
authResponse.setSessionKey("session_key");
|
||||
|
||||
ClientUser newUser = new ClientUser();
|
||||
newUser.setId(1L);
|
||||
newUser.setDouyinOpenId("open_id_123");
|
||||
newUser.setDouyinUnionId("union_id_456");
|
||||
newUser.setPhoneVerified(false);
|
||||
|
||||
UserBinding binding = new UserBinding();
|
||||
binding.setId(1L);
|
||||
binding.setUserId(1L);
|
||||
binding.setPlatform("douyin");
|
||||
binding.setPlatformOpenId("open_id_123");
|
||||
binding.setPlatformUnionId("union_id_456");
|
||||
|
||||
when(douyinClient.auth("valid_code")).thenReturn(Mono.just(authResponse));
|
||||
when(clientUserRepository.findByDouyinOpenId("open_id_123")).thenReturn(Mono.empty());
|
||||
when(clientUserRepository.findByDouyinUnionId("union_id_456")).thenReturn(Mono.empty());
|
||||
when(userBindingRepository.findByPlatformAndUnionId("douyin", "union_id_456")).thenReturn(Mono.empty());
|
||||
when(clientUserRepository.save(any(ClientUser.class))).thenReturn(Mono.just(newUser));
|
||||
when(userBindingRepository.save(any(UserBinding.class))).thenReturn(Mono.just(binding));
|
||||
|
||||
StepVerifier.create(douyinLoginStrategy.login(request))
|
||||
.expectNextMatches(result -> result.getUserId().equals(1L) &&
|
||||
result.getPlatform().equals("douyin") &&
|
||||
result.getToken().equals("test-token"))
|
||||
.verifyComplete();
|
||||
|
||||
verify(clientUserRepository).save(any(ClientUser.class));
|
||||
verify(userBindingRepository).save(any(UserBinding.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginWithNewUserWithoutUnionId() {
|
||||
when(jwtTokenProvider.generateToken(anyLong(), anyString())).thenReturn("test-token");
|
||||
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("douyin");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
DouyinLoginRequest douyinRequest = new DouyinLoginRequest();
|
||||
douyinRequest.setCode("valid_code");
|
||||
request.setData(douyinRequest);
|
||||
|
||||
WechatAuthResponse authResponse = new WechatAuthResponse();
|
||||
authResponse.setOpenId("open_id_123");
|
||||
authResponse.setSessionKey("session_key");
|
||||
|
||||
ClientUser newUser = new ClientUser();
|
||||
newUser.setId(1L);
|
||||
newUser.setDouyinOpenId("open_id_123");
|
||||
newUser.setPhoneVerified(false);
|
||||
|
||||
UserBinding binding = new UserBinding();
|
||||
binding.setId(1L);
|
||||
binding.setUserId(1L);
|
||||
binding.setPlatform("douyin");
|
||||
binding.setPlatformOpenId("open_id_123");
|
||||
|
||||
when(douyinClient.auth("valid_code")).thenReturn(Mono.just(authResponse));
|
||||
when(clientUserRepository.findByDouyinOpenId("open_id_123")).thenReturn(Mono.empty());
|
||||
when(clientUserRepository.save(any(ClientUser.class))).thenReturn(Mono.just(newUser));
|
||||
when(userBindingRepository.save(any(UserBinding.class))).thenReturn(Mono.just(binding));
|
||||
|
||||
StepVerifier.create(douyinLoginStrategy.login(request))
|
||||
.expectNextMatches(result -> result.getUserId().equals(1L) &&
|
||||
result.getPlatform().equals("douyin") &&
|
||||
result.getToken().equals("test-token"))
|
||||
.verifyComplete();
|
||||
|
||||
verify(clientUserRepository, never()).findByDouyinUnionId(anyString());
|
||||
verify(userBindingRepository, never()).findByPlatformAndUnionId(anyString(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginWithAuthFailure() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("douyin");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
DouyinLoginRequest douyinRequest = new DouyinLoginRequest();
|
||||
douyinRequest.setCode("invalid_code");
|
||||
request.setData(douyinRequest);
|
||||
|
||||
when(douyinClient.auth("invalid_code")).thenReturn(Mono.error(new Exception("Auth failed")));
|
||||
|
||||
StepVerifier.create(douyinLoginStrategy.login(request))
|
||||
.expectError(Exception.class)
|
||||
.verify();
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package io.destiny.client.core.strategy;
|
||||
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class LoginStrategyFactoryTest {
|
||||
|
||||
@Mock
|
||||
private WechatLoginStrategy wechatLoginStrategy;
|
||||
|
||||
@Mock
|
||||
private DouyinLoginStrategy douyinLoginStrategy;
|
||||
|
||||
@Mock
|
||||
private SmsLoginStrategy smsLoginStrategy;
|
||||
|
||||
private LoginStrategyFactory factory;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
when(wechatLoginStrategy.getLoginType()).thenReturn("wechat");
|
||||
when(douyinLoginStrategy.getLoginType()).thenReturn("douyin");
|
||||
when(smsLoginStrategy.getLoginType()).thenReturn("sms");
|
||||
|
||||
factory = new LoginStrategyFactory(List.of(wechatLoginStrategy, douyinLoginStrategy, smsLoginStrategy));
|
||||
factory.init();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetStrategy_Wechat() {
|
||||
LoginStrategy strategy = factory.getStrategy("wechat");
|
||||
|
||||
assertNotNull(strategy);
|
||||
assertEquals(wechatLoginStrategy, strategy);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetStrategy_Douyin() {
|
||||
LoginStrategy strategy = factory.getStrategy("douyin");
|
||||
|
||||
assertNotNull(strategy);
|
||||
assertEquals(douyinLoginStrategy, strategy);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetStrategy_Sms() {
|
||||
LoginStrategy strategy = factory.getStrategy("sms");
|
||||
|
||||
assertNotNull(strategy);
|
||||
assertEquals(smsLoginStrategy, strategy);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetStrategy_InvalidType() {
|
||||
assertThrows(ApplicationException.class, () -> {
|
||||
factory.getStrategy("invalid");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetStrategy_NullType() {
|
||||
assertThrows(ApplicationException.class, () -> {
|
||||
factory.getStrategy(null);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetStrategy_EmptyType() {
|
||||
assertThrows(ApplicationException.class, () -> {
|
||||
factory.getStrategy("");
|
||||
});
|
||||
}
|
||||
}
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
package io.destiny.client.core.strategy;
|
||||
|
||||
import io.destiny.client.core.exception.ClientUserAuthExceptionCode;
|
||||
import io.destiny.client.dto.DouyinLoginRequest;
|
||||
import io.destiny.client.dto.LoginRequest;
|
||||
import io.destiny.client.dto.LoginResult;
|
||||
import io.destiny.client.dto.SmsLoginRequest;
|
||||
import io.destiny.client.dto.WechatLoginRequest;
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class LoginStrategyProxyTest {
|
||||
|
||||
@Mock
|
||||
private LoginStrategyFactory strategyFactory;
|
||||
|
||||
@Mock
|
||||
private LoginStrategy loginStrategy;
|
||||
|
||||
@InjectMocks
|
||||
private LoginStrategyProxy loginStrategyProxy;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
when(strategyFactory.getStrategy(any())).thenReturn(loginStrategy);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginSuccess() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("wechat");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
request.setData(new WechatLoginRequest());
|
||||
|
||||
LoginResult result = new LoginResult();
|
||||
result.setUserId(1L);
|
||||
result.setPlatform("wechat");
|
||||
|
||||
when(loginStrategy.login(any())).thenReturn(Mono.just(result));
|
||||
|
||||
StepVerifier.create(loginStrategyProxy.login(request))
|
||||
.expectNext(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(loginStrategy).login(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginWithNullIpAddress() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("wechat");
|
||||
request.setIpAddress(null);
|
||||
request.setData(new WechatLoginRequest());
|
||||
|
||||
LoginResult result = new LoginResult();
|
||||
result.setUserId(1L);
|
||||
result.setPlatform("wechat");
|
||||
|
||||
when(loginStrategy.login(any())).thenReturn(Mono.just(result));
|
||||
|
||||
StepVerifier.create(loginStrategyProxy.login(request))
|
||||
.expectNext(result)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginFailureWithApplicationException() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("wechat");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
request.setData(new WechatLoginRequest());
|
||||
|
||||
ApplicationException exception = new ApplicationException(ClientUserAuthExceptionCode.CLIENT_INVALID_USERNAME_OR_PASSWORD);
|
||||
when(loginStrategy.login(any())).thenReturn(Mono.error(exception));
|
||||
|
||||
StepVerifier.create(loginStrategyProxy.login(request))
|
||||
.expectError(ApplicationException.class)
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginFailureWithNonApplicationException() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("wechat");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
request.setData(new WechatLoginRequest());
|
||||
|
||||
RuntimeException exception = new RuntimeException("Unexpected error");
|
||||
when(loginStrategy.login(any())).thenReturn(Mono.error(exception));
|
||||
|
||||
StepVerifier.create(loginStrategyProxy.login(request))
|
||||
.expectErrorMatches(error -> error instanceof ApplicationException &&
|
||||
((ApplicationException) error).getExceptionCode().equals(ClientUserAuthExceptionCode.CLIENT_LOGIN_FAILED))
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAccountLockAfterMultipleFailedAttempts() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("wechat");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
request.setData(new WechatLoginRequest());
|
||||
|
||||
ApplicationException exception = new ApplicationException(ClientUserAuthExceptionCode.CLIENT_INVALID_USERNAME_OR_PASSWORD);
|
||||
when(loginStrategy.login(any())).thenReturn(Mono.error(exception));
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
StepVerifier.create(loginStrategyProxy.login(request))
|
||||
.expectError(ApplicationException.class)
|
||||
.verify();
|
||||
}
|
||||
|
||||
StepVerifier.create(loginStrategyProxy.login(request))
|
||||
.expectErrorMatches(error -> error instanceof ApplicationException &&
|
||||
((ApplicationException) error).getExceptionCode().equals(ClientUserAuthExceptionCode.CLIENT_ACCOUNT_LOCKED))
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginWithDouyinRequest() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("douyin");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
|
||||
DouyinLoginRequest douyinRequest = new DouyinLoginRequest();
|
||||
douyinRequest.setCode("douyin_code");
|
||||
request.setData(douyinRequest);
|
||||
|
||||
LoginResult result = new LoginResult();
|
||||
result.setUserId(1L);
|
||||
result.setPlatform("douyin");
|
||||
|
||||
when(loginStrategy.login(any())).thenReturn(Mono.just(result));
|
||||
|
||||
StepVerifier.create(loginStrategyProxy.login(request))
|
||||
.expectNext(result)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginWithDouyinRequestNullCode() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("douyin");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
|
||||
DouyinLoginRequest douyinRequest = new DouyinLoginRequest();
|
||||
douyinRequest.setCode(null);
|
||||
request.setData(douyinRequest);
|
||||
|
||||
LoginResult result = new LoginResult();
|
||||
result.setUserId(1L);
|
||||
result.setPlatform("douyin");
|
||||
|
||||
when(loginStrategy.login(any())).thenReturn(Mono.just(result));
|
||||
|
||||
StepVerifier.create(loginStrategyProxy.login(request))
|
||||
.expectNext(result)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginWithSmsRequest() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("sms");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
|
||||
SmsLoginRequest smsRequest = new SmsLoginRequest();
|
||||
smsRequest.setPhone("13800138000");
|
||||
smsRequest.setCode("123456");
|
||||
request.setData(smsRequest);
|
||||
|
||||
LoginResult result = new LoginResult();
|
||||
result.setUserId(1L);
|
||||
result.setPlatform("sms");
|
||||
|
||||
when(loginStrategy.login(any())).thenReturn(Mono.just(result));
|
||||
|
||||
StepVerifier.create(loginStrategyProxy.login(request))
|
||||
.expectNext(result)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginWithSmsRequestNullPhone() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("sms");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
|
||||
SmsLoginRequest smsRequest = new SmsLoginRequest();
|
||||
smsRequest.setPhone(null);
|
||||
smsRequest.setCode("123456");
|
||||
request.setData(smsRequest);
|
||||
|
||||
LoginResult result = new LoginResult();
|
||||
result.setUserId(1L);
|
||||
result.setPlatform("sms");
|
||||
|
||||
when(loginStrategy.login(any())).thenReturn(Mono.just(result));
|
||||
|
||||
StepVerifier.create(loginStrategyProxy.login(request))
|
||||
.expectNext(result)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginWithNullLoginType() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType(null);
|
||||
request.setIpAddress("192.168.1.1");
|
||||
request.setData(new WechatLoginRequest());
|
||||
|
||||
LoginResult result = new LoginResult();
|
||||
result.setUserId(1L);
|
||||
result.setPlatform("wechat");
|
||||
|
||||
when(loginStrategy.login(any())).thenReturn(Mono.just(result));
|
||||
|
||||
StepVerifier.create(loginStrategyProxy.login(request))
|
||||
.expectNext(result)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginWithNullData() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("password");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
request.setData(null);
|
||||
|
||||
LoginResult result = new LoginResult();
|
||||
result.setUserId(1L);
|
||||
result.setPlatform("password");
|
||||
|
||||
when(loginStrategy.login(any())).thenReturn(Mono.just(result));
|
||||
|
||||
StepVerifier.create(loginStrategyProxy.login(request))
|
||||
.expectNext(result)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClearAttemptRecordAfterSuccessfulLogin() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("wechat");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
request.setData(new WechatLoginRequest());
|
||||
|
||||
ApplicationException exception = new ApplicationException(ClientUserAuthExceptionCode.CLIENT_INVALID_USERNAME_OR_PASSWORD);
|
||||
when(loginStrategy.login(any())).thenReturn(Mono.error(exception));
|
||||
|
||||
StepVerifier.create(loginStrategyProxy.login(request))
|
||||
.expectError(ApplicationException.class)
|
||||
.verify();
|
||||
|
||||
LoginResult result = new LoginResult();
|
||||
result.setUserId(1L);
|
||||
result.setPlatform("wechat");
|
||||
when(loginStrategy.login(any())).thenReturn(Mono.just(result));
|
||||
|
||||
StepVerifier.create(loginStrategyProxy.login(request))
|
||||
.expectNext(result)
|
||||
.verifyComplete();
|
||||
}
|
||||
}
|
||||
+385
@@ -0,0 +1,385 @@
|
||||
package io.destiny.client.core.strategy;
|
||||
|
||||
import io.destiny.client.core.domain.ClientUser;
|
||||
import io.destiny.client.core.domain.SmsVerificationCode;
|
||||
import io.destiny.client.core.enums.SmsCodeType;
|
||||
import io.destiny.client.core.repository.IClientUserRepository;
|
||||
import io.destiny.client.core.repository.ISmsVerificationCodeRepository;
|
||||
import io.destiny.client.core.service.ClientUserAuthService;
|
||||
import io.destiny.client.dto.LoginRequest;
|
||||
import io.destiny.client.dto.LoginResult;
|
||||
import io.destiny.client.dto.SmsLoginRequest;
|
||||
import io.destiny.client.security.JwtTokenProvider;
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import io.destiny.common.primitive.PhoneNumber;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static io.destiny.client.core.exception.ClientUserAuthExceptionCode.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SmsLoginStrategyTest {
|
||||
|
||||
@Mock
|
||||
private ClientUserAuthService clientUserAuthService;
|
||||
|
||||
@Mock
|
||||
private JwtTokenProvider jwtTokenProvider;
|
||||
|
||||
@Mock
|
||||
private IClientUserRepository clientUserRepository;
|
||||
|
||||
@Mock
|
||||
private ISmsVerificationCodeRepository smsVerificationCodeRepository;
|
||||
|
||||
private SmsLoginStrategy strategy;
|
||||
|
||||
private static final String TEST_PHONE = "13800138000";
|
||||
private static final String TEST_CODE = "123456";
|
||||
private static final Long TEST_USER_ID = 12345L;
|
||||
private static final String TEST_TOKEN = "test_jwt_token";
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
strategy = new SmsLoginStrategy(clientUserAuthService, jwtTokenProvider, clientUserRepository, smsVerificationCodeRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetLoginType() {
|
||||
String result = strategy.getLoginType();
|
||||
|
||||
assert "sms".equals(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateRequest_Success() {
|
||||
SmsLoginRequest smsRequest = new SmsLoginRequest();
|
||||
smsRequest.setPhone(TEST_PHONE);
|
||||
smsRequest.setCode(TEST_CODE);
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setData(smsRequest);
|
||||
|
||||
Mono<LoginRequest> result = strategy.validateRequest(request);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNext(request)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateRequest_NullPhone() {
|
||||
SmsLoginRequest smsRequest = new SmsLoginRequest();
|
||||
smsRequest.setPhone(null);
|
||||
smsRequest.setCode(TEST_CODE);
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setData(smsRequest);
|
||||
|
||||
Mono<LoginRequest> result = strategy.validateRequest(request);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectErrorMatches(ex -> ex instanceof ApplicationException &&
|
||||
((ApplicationException) ex).getExceptionCode().equals(CLIENT_SMS_CODE_INVALID))
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateRequest_EmptyPhone() {
|
||||
SmsLoginRequest smsRequest = new SmsLoginRequest();
|
||||
smsRequest.setPhone("");
|
||||
smsRequest.setCode(TEST_CODE);
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setData(smsRequest);
|
||||
|
||||
Mono<LoginRequest> result = strategy.validateRequest(request);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectErrorMatches(ex -> ex instanceof ApplicationException &&
|
||||
((ApplicationException) ex).getExceptionCode().equals(CLIENT_SMS_CODE_INVALID))
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateRequest_NullCode() {
|
||||
SmsLoginRequest smsRequest = new SmsLoginRequest();
|
||||
smsRequest.setPhone(TEST_PHONE);
|
||||
smsRequest.setCode(null);
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setData(smsRequest);
|
||||
|
||||
Mono<LoginRequest> result = strategy.validateRequest(request);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectErrorMatches(ex -> ex instanceof ApplicationException &&
|
||||
((ApplicationException) ex).getExceptionCode().equals(CLIENT_SMS_CODE_INVALID))
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateRequest_EmptyCode() {
|
||||
SmsLoginRequest smsRequest = new SmsLoginRequest();
|
||||
smsRequest.setPhone(TEST_PHONE);
|
||||
smsRequest.setCode("");
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setData(smsRequest);
|
||||
|
||||
Mono<LoginRequest> result = strategy.validateRequest(request);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectErrorMatches(ex -> ex instanceof ApplicationException &&
|
||||
((ApplicationException) ex).getExceptionCode().equals(CLIENT_SMS_CODE_INVALID))
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateRequest_InvalidDataType() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setData(new Object());
|
||||
|
||||
Mono<LoginRequest> result = strategy.validateRequest(request);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectErrorMatches(ex -> ex instanceof ApplicationException &&
|
||||
((ApplicationException) ex).getExceptionCode().equals(CLIENT_SMS_CODE_INVALID))
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogin_ExistingUser_Success() {
|
||||
SmsLoginRequest smsRequest = new SmsLoginRequest();
|
||||
smsRequest.setPhone(TEST_PHONE);
|
||||
smsRequest.setCode(TEST_CODE);
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setData(smsRequest);
|
||||
|
||||
SmsVerificationCode verificationCode = new SmsVerificationCode();
|
||||
verificationCode.setCode(TEST_CODE);
|
||||
verificationCode.setExpireTime(LocalDateTime.now().plusMinutes(5));
|
||||
verificationCode.setVerified(false);
|
||||
verificationCode.setVerifyCount(0);
|
||||
|
||||
ClientUser existingUser = new ClientUser();
|
||||
existingUser.setId(TEST_USER_ID);
|
||||
existingUser.setPhone(PhoneNumber.of(TEST_PHONE));
|
||||
existingUser.setUsername(TEST_PHONE);
|
||||
|
||||
when(smsVerificationCodeRepository.findLatestByPhoneAndType(TEST_PHONE, SmsCodeType.LOGIN.getCode()))
|
||||
.thenReturn(Mono.just(verificationCode));
|
||||
when(smsVerificationCodeRepository.markAsVerified(any())).thenReturn(Mono.empty());
|
||||
when(clientUserRepository.findByPhone(PhoneNumber.of(TEST_PHONE))).thenReturn(Mono.just(existingUser));
|
||||
when(jwtTokenProvider.generateToken(TEST_USER_ID, "sms")).thenReturn(TEST_TOKEN);
|
||||
|
||||
Mono<LoginResult> result = strategy.login(request);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNextMatches(loginResult ->
|
||||
TEST_USER_ID.equals(loginResult.getUserId()) &&
|
||||
TEST_PHONE.equals(loginResult.getUsername()) &&
|
||||
TEST_PHONE.equals(loginResult.getPhone()) &&
|
||||
"sms".equals(loginResult.getPlatform()) &&
|
||||
TEST_TOKEN.equals(loginResult.getToken()))
|
||||
.verifyComplete();
|
||||
|
||||
verify(smsVerificationCodeRepository, times(1)).findLatestByPhoneAndType(TEST_PHONE, SmsCodeType.LOGIN.getCode());
|
||||
verify(smsVerificationCodeRepository, times(1)).markAsVerified(any());
|
||||
verify(clientUserRepository, times(1)).findByPhone(PhoneNumber.of(TEST_PHONE));
|
||||
verify(jwtTokenProvider, times(1)).generateToken(TEST_USER_ID, "sms");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogin_NewUser_Success() {
|
||||
SmsLoginRequest smsRequest = new SmsLoginRequest();
|
||||
smsRequest.setPhone(TEST_PHONE);
|
||||
smsRequest.setCode(TEST_CODE);
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setData(smsRequest);
|
||||
|
||||
SmsVerificationCode verificationCode = new SmsVerificationCode();
|
||||
verificationCode.setCode(TEST_CODE);
|
||||
verificationCode.setExpireTime(LocalDateTime.now().plusMinutes(5));
|
||||
verificationCode.setVerified(false);
|
||||
verificationCode.setVerifyCount(0);
|
||||
|
||||
ClientUser newUser = new ClientUser();
|
||||
newUser.setId(TEST_USER_ID);
|
||||
newUser.setPhone(PhoneNumber.of(TEST_PHONE));
|
||||
newUser.setPhoneVerified(true);
|
||||
newUser.setUsername(TEST_PHONE);
|
||||
|
||||
when(smsVerificationCodeRepository.findLatestByPhoneAndType(TEST_PHONE, SmsCodeType.LOGIN.getCode()))
|
||||
.thenReturn(Mono.just(verificationCode));
|
||||
when(smsVerificationCodeRepository.markAsVerified(any())).thenReturn(Mono.empty());
|
||||
when(clientUserRepository.findByPhone(PhoneNumber.of(TEST_PHONE))).thenReturn(Mono.empty());
|
||||
when(clientUserRepository.save(any(ClientUser.class))).thenReturn(Mono.just(newUser));
|
||||
when(jwtTokenProvider.generateToken(TEST_USER_ID, "sms")).thenReturn(TEST_TOKEN);
|
||||
|
||||
Mono<LoginResult> result = strategy.login(request);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNextMatches(loginResult ->
|
||||
TEST_USER_ID.equals(loginResult.getUserId()) &&
|
||||
TEST_PHONE.equals(loginResult.getUsername()) &&
|
||||
TEST_PHONE.equals(loginResult.getPhone()) &&
|
||||
"sms".equals(loginResult.getPlatform()) &&
|
||||
TEST_TOKEN.equals(loginResult.getToken()))
|
||||
.verifyComplete();
|
||||
|
||||
verify(smsVerificationCodeRepository, times(1)).findLatestByPhoneAndType(TEST_PHONE, SmsCodeType.LOGIN.getCode());
|
||||
verify(smsVerificationCodeRepository, times(1)).markAsVerified(any());
|
||||
verify(clientUserRepository, times(1)).findByPhone(PhoneNumber.of(TEST_PHONE));
|
||||
verify(clientUserRepository, times(1)).save(any(ClientUser.class));
|
||||
verify(jwtTokenProvider, times(1)).generateToken(TEST_USER_ID, "sms");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogin_CodeNotFound() {
|
||||
SmsLoginRequest smsRequest = new SmsLoginRequest();
|
||||
smsRequest.setPhone(TEST_PHONE);
|
||||
smsRequest.setCode(TEST_CODE);
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setData(smsRequest);
|
||||
|
||||
when(smsVerificationCodeRepository.findLatestByPhoneAndType(TEST_PHONE, SmsCodeType.LOGIN.getCode()))
|
||||
.thenReturn(Mono.empty());
|
||||
|
||||
Mono<LoginResult> result = strategy.login(request);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectErrorMatches(ex -> ex instanceof ApplicationException &&
|
||||
((ApplicationException) ex).getExceptionCode().equals(CLIENT_SMS_CODE_INVALID))
|
||||
.verify();
|
||||
|
||||
verify(smsVerificationCodeRepository, times(1)).findLatestByPhoneAndType(TEST_PHONE, SmsCodeType.LOGIN.getCode());
|
||||
verify(clientUserRepository, never()).findByPhone(any());
|
||||
verify(jwtTokenProvider, never()).generateToken(any(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogin_CodeExpired() {
|
||||
SmsLoginRequest smsRequest = new SmsLoginRequest();
|
||||
smsRequest.setPhone(TEST_PHONE);
|
||||
smsRequest.setCode(TEST_CODE);
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setData(smsRequest);
|
||||
|
||||
SmsVerificationCode verificationCode = new SmsVerificationCode();
|
||||
verificationCode.setCode(TEST_CODE);
|
||||
verificationCode.setExpireTime(LocalDateTime.now().minusMinutes(1));
|
||||
verificationCode.setVerified(false);
|
||||
verificationCode.setVerifyCount(0);
|
||||
|
||||
when(smsVerificationCodeRepository.findLatestByPhoneAndType(TEST_PHONE, SmsCodeType.LOGIN.getCode()))
|
||||
.thenReturn(Mono.just(verificationCode));
|
||||
|
||||
Mono<LoginResult> result = strategy.login(request);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectErrorMatches(ex -> ex instanceof ApplicationException &&
|
||||
((ApplicationException) ex).getExceptionCode().equals(CLIENT_SMS_CODE_EXPIRED))
|
||||
.verify();
|
||||
|
||||
verify(smsVerificationCodeRepository, times(1)).findLatestByPhoneAndType(TEST_PHONE, SmsCodeType.LOGIN.getCode());
|
||||
verify(clientUserRepository, never()).findByPhone(any());
|
||||
verify(jwtTokenProvider, never()).generateToken(any(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogin_CodeAlreadyUsed() {
|
||||
SmsLoginRequest smsRequest = new SmsLoginRequest();
|
||||
smsRequest.setPhone(TEST_PHONE);
|
||||
smsRequest.setCode(TEST_CODE);
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setData(smsRequest);
|
||||
|
||||
SmsVerificationCode verificationCode = new SmsVerificationCode();
|
||||
verificationCode.setCode(TEST_CODE);
|
||||
verificationCode.setExpireTime(LocalDateTime.now().plusMinutes(5));
|
||||
verificationCode.setVerified(true);
|
||||
verificationCode.setVerifyCount(0);
|
||||
|
||||
when(smsVerificationCodeRepository.findLatestByPhoneAndType(TEST_PHONE, SmsCodeType.LOGIN.getCode()))
|
||||
.thenReturn(Mono.just(verificationCode));
|
||||
|
||||
Mono<LoginResult> result = strategy.login(request);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectErrorMatches(ex -> ex instanceof ApplicationException &&
|
||||
((ApplicationException) ex).getExceptionCode().equals(CLIENT_SMS_CODE_USED))
|
||||
.verify();
|
||||
|
||||
verify(smsVerificationCodeRepository, times(1)).findLatestByPhoneAndType(TEST_PHONE, SmsCodeType.LOGIN.getCode());
|
||||
verify(clientUserRepository, never()).findByPhone(any());
|
||||
verify(jwtTokenProvider, never()).generateToken(any(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogin_VerifyCountExceeded() {
|
||||
SmsLoginRequest smsRequest = new SmsLoginRequest();
|
||||
smsRequest.setPhone(TEST_PHONE);
|
||||
smsRequest.setCode(TEST_CODE);
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setData(smsRequest);
|
||||
|
||||
SmsVerificationCode verificationCode = new SmsVerificationCode();
|
||||
verificationCode.setCode(TEST_CODE);
|
||||
verificationCode.setExpireTime(LocalDateTime.now().plusMinutes(5));
|
||||
verificationCode.setVerified(false);
|
||||
verificationCode.setVerifyCount(5);
|
||||
|
||||
when(smsVerificationCodeRepository.findLatestByPhoneAndType(TEST_PHONE, SmsCodeType.LOGIN.getCode()))
|
||||
.thenReturn(Mono.just(verificationCode));
|
||||
|
||||
Mono<LoginResult> result = strategy.login(request);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectErrorMatches(ex -> ex instanceof ApplicationException &&
|
||||
((ApplicationException) ex).getExceptionCode().equals(CLIENT_SMS_CODE_EXCEEDED))
|
||||
.verify();
|
||||
|
||||
verify(smsVerificationCodeRepository, times(1)).findLatestByPhoneAndType(TEST_PHONE, SmsCodeType.LOGIN.getCode());
|
||||
verify(clientUserRepository, never()).findByPhone(any());
|
||||
verify(jwtTokenProvider, never()).generateToken(any(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLogin_WrongCode() {
|
||||
SmsLoginRequest smsRequest = new SmsLoginRequest();
|
||||
smsRequest.setPhone(TEST_PHONE);
|
||||
smsRequest.setCode("999999");
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setData(smsRequest);
|
||||
|
||||
SmsVerificationCode verificationCode = new SmsVerificationCode();
|
||||
verificationCode.setCode(TEST_CODE);
|
||||
verificationCode.setExpireTime(LocalDateTime.now().plusMinutes(5));
|
||||
verificationCode.setVerified(false);
|
||||
verificationCode.setVerifyCount(0);
|
||||
|
||||
when(smsVerificationCodeRepository.findLatestByPhoneAndType(TEST_PHONE, SmsCodeType.LOGIN.getCode()))
|
||||
.thenReturn(Mono.just(verificationCode));
|
||||
when(smsVerificationCodeRepository.save(any(SmsVerificationCode.class))).thenReturn(Mono.just(verificationCode));
|
||||
|
||||
Mono<LoginResult> result = strategy.login(request);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectErrorMatches(ex -> ex instanceof ApplicationException &&
|
||||
((ApplicationException) ex).getExceptionCode().equals(CLIENT_SMS_CODE_INVALID))
|
||||
.verify();
|
||||
|
||||
verify(smsVerificationCodeRepository, times(1)).findLatestByPhoneAndType(TEST_PHONE, SmsCodeType.LOGIN.getCode());
|
||||
verify(smsVerificationCodeRepository, times(1)).save(any(SmsVerificationCode.class));
|
||||
verify(clientUserRepository, never()).findByPhone(any());
|
||||
verify(jwtTokenProvider, never()).generateToken(any(), anyString());
|
||||
}
|
||||
}
|
||||
+273
@@ -0,0 +1,273 @@
|
||||
package io.destiny.client.core.strategy;
|
||||
|
||||
import io.destiny.client.core.domain.ClientLoginLog;
|
||||
import io.destiny.client.core.domain.ClientUser;
|
||||
import io.destiny.client.core.repository.IClientLoginLogRepository;
|
||||
import io.destiny.client.core.repository.IClientUserRepository;
|
||||
import io.destiny.client.core.service.ClientUserAuthService;
|
||||
import io.destiny.client.dto.LoginRequest;
|
||||
import io.destiny.client.security.JwtTokenProvider;
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static io.destiny.client.core.exception.ClientUserAuthExceptionCode.CLIENT_INVALID_USERNAME_OR_PASSWORD;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class UsernamePasswordLoginStrategyTest {
|
||||
|
||||
@Mock
|
||||
private ClientUserAuthService clientUserAuthService;
|
||||
|
||||
@Mock
|
||||
private JwtTokenProvider jwtTokenProvider;
|
||||
|
||||
@Mock
|
||||
private IClientUserRepository clientUserRepository;
|
||||
|
||||
@Mock
|
||||
private IClientLoginLogRepository clientLoginLogRepository;
|
||||
|
||||
@Mock
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@InjectMocks
|
||||
private UsernamePasswordLoginStrategy usernamePasswordLoginStrategy;
|
||||
|
||||
@Test
|
||||
void testGetLoginType() {
|
||||
assertEquals("password", usernamePasswordLoginStrategy.getLoginType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginSuccess() {
|
||||
when(jwtTokenProvider.generateToken(anyLong(), anyString())).thenReturn("test-token");
|
||||
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("password");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("username", "testuser");
|
||||
data.put("password", "password123");
|
||||
request.setData(data);
|
||||
|
||||
ClientUser user = new ClientUser();
|
||||
user.setId(1L);
|
||||
user.setUsername("testuser");
|
||||
user.setPassword("encoded_password");
|
||||
|
||||
when(clientUserRepository.findByUsername("testuser")).thenReturn(Mono.just(user));
|
||||
when(passwordEncoder.matches("password123", "encoded_password")).thenReturn(true);
|
||||
when(clientLoginLogRepository.save(any(ClientLoginLog.class)))
|
||||
.thenReturn(Mono.just(new ClientLoginLog()));
|
||||
|
||||
StepVerifier.create(usernamePasswordLoginStrategy.login(request))
|
||||
.expectNextMatches(result -> result.getUserId().equals(1L) &&
|
||||
result.getUsername().equals("testuser") &&
|
||||
result.getPlatform().equals("password") &&
|
||||
result.getToken().equals("test-token"))
|
||||
.verifyComplete();
|
||||
|
||||
verify(clientUserRepository).findByUsername("testuser");
|
||||
verify(passwordEncoder).matches("password123", "encoded_password");
|
||||
verify(clientLoginLogRepository).save(any(ClientLoginLog.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginWithNullUsername() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("password");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("username", null);
|
||||
data.put("password", "password123");
|
||||
request.setData(data);
|
||||
|
||||
StepVerifier.create(usernamePasswordLoginStrategy.login(request))
|
||||
.expectErrorMatches(error -> error instanceof ApplicationException &&
|
||||
((ApplicationException) error).getExceptionCode()
|
||||
.equals(CLIENT_INVALID_USERNAME_OR_PASSWORD))
|
||||
.verify();
|
||||
|
||||
verify(clientUserRepository, never()).findByUsername(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginWithEmptyUsername() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("password");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("username", "");
|
||||
data.put("password", "password123");
|
||||
request.setData(data);
|
||||
|
||||
StepVerifier.create(usernamePasswordLoginStrategy.login(request))
|
||||
.expectErrorMatches(error -> error instanceof ApplicationException &&
|
||||
((ApplicationException) error).getExceptionCode()
|
||||
.equals(CLIENT_INVALID_USERNAME_OR_PASSWORD))
|
||||
.verify();
|
||||
|
||||
verify(clientUserRepository, never()).findByUsername(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginWithNullPassword() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("password");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("username", "testuser");
|
||||
data.put("password", null);
|
||||
request.setData(data);
|
||||
|
||||
StepVerifier.create(usernamePasswordLoginStrategy.login(request))
|
||||
.expectErrorMatches(error -> error instanceof ApplicationException &&
|
||||
((ApplicationException) error).getExceptionCode()
|
||||
.equals(CLIENT_INVALID_USERNAME_OR_PASSWORD))
|
||||
.verify();
|
||||
|
||||
verify(clientUserRepository, never()).findByUsername(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginWithEmptyPassword() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("password");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("username", "testuser");
|
||||
data.put("password", "");
|
||||
request.setData(data);
|
||||
|
||||
StepVerifier.create(usernamePasswordLoginStrategy.login(request))
|
||||
.expectErrorMatches(error -> error instanceof ApplicationException &&
|
||||
((ApplicationException) error).getExceptionCode()
|
||||
.equals(CLIENT_INVALID_USERNAME_OR_PASSWORD))
|
||||
.verify();
|
||||
|
||||
verify(clientUserRepository, never()).findByUsername(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginWithInvalidData() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("password");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
request.setData("invalid_data");
|
||||
|
||||
StepVerifier.create(usernamePasswordLoginStrategy.login(request))
|
||||
.expectErrorMatches(error -> error instanceof ApplicationException &&
|
||||
((ApplicationException) error).getExceptionCode()
|
||||
.equals(CLIENT_INVALID_USERNAME_OR_PASSWORD))
|
||||
.verify();
|
||||
|
||||
verify(clientUserRepository, never()).findByUsername(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginWithUserNotFound() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("password");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("username", "nonexistent");
|
||||
data.put("password", "password123");
|
||||
request.setData(data);
|
||||
|
||||
when(clientUserRepository.findByUsername("nonexistent")).thenReturn(Mono.empty());
|
||||
|
||||
StepVerifier.create(usernamePasswordLoginStrategy.login(request))
|
||||
.expectErrorMatches(error -> error instanceof ApplicationException &&
|
||||
((ApplicationException) error).getExceptionCode()
|
||||
.equals(CLIENT_INVALID_USERNAME_OR_PASSWORD))
|
||||
.verify();
|
||||
|
||||
verify(clientUserRepository).findByUsername("nonexistent");
|
||||
verify(passwordEncoder, never()).matches(anyString(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginWithWrongPassword() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("password");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("username", "testuser");
|
||||
data.put("password", "wrong_password");
|
||||
request.setData(data);
|
||||
|
||||
ClientUser user = new ClientUser();
|
||||
user.setId(1L);
|
||||
user.setUsername("testuser");
|
||||
user.setPassword("encoded_password");
|
||||
|
||||
when(clientUserRepository.findByUsername("testuser")).thenReturn(Mono.just(user));
|
||||
when(passwordEncoder.matches("wrong_password", "encoded_password")).thenReturn(false);
|
||||
|
||||
StepVerifier.create(usernamePasswordLoginStrategy.login(request))
|
||||
.expectErrorMatches(error -> error instanceof ApplicationException &&
|
||||
((ApplicationException) error).getExceptionCode()
|
||||
.equals(CLIENT_INVALID_USERNAME_OR_PASSWORD))
|
||||
.verify();
|
||||
|
||||
verify(clientUserRepository).findByUsername("testuser");
|
||||
verify(passwordEncoder).matches("wrong_password", "encoded_password");
|
||||
verify(clientLoginLogRepository, never()).save(any(ClientLoginLog.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginWithNullIpAddress() {
|
||||
when(jwtTokenProvider.generateToken(anyLong(), anyString())).thenReturn("test-token");
|
||||
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("password");
|
||||
request.setIpAddress(null);
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("username", "testuser");
|
||||
data.put("password", "password123");
|
||||
request.setData(data);
|
||||
|
||||
ClientUser user = new ClientUser();
|
||||
user.setId(1L);
|
||||
user.setUsername("testuser");
|
||||
user.setPassword("encoded_password");
|
||||
|
||||
when(clientUserRepository.findByUsername("testuser")).thenReturn(Mono.just(user));
|
||||
when(passwordEncoder.matches("password123", "encoded_password")).thenReturn(true);
|
||||
when(clientLoginLogRepository.save(any(ClientLoginLog.class)))
|
||||
.thenReturn(Mono.just(new ClientLoginLog()));
|
||||
|
||||
StepVerifier.create(usernamePasswordLoginStrategy.login(request))
|
||||
.expectNextMatches(result -> result.getUserId().equals(1L) &&
|
||||
result.getUsername().equals("testuser") &&
|
||||
result.getPlatform().equals("password"))
|
||||
.verifyComplete();
|
||||
|
||||
verify(clientUserRepository).findByUsername("testuser");
|
||||
verify(passwordEncoder).matches("password123", "encoded_password");
|
||||
verify(clientLoginLogRepository).save(any(ClientLoginLog.class));
|
||||
}
|
||||
}
|
||||
+328
@@ -0,0 +1,328 @@
|
||||
package io.destiny.client.core.strategy;
|
||||
|
||||
import io.destiny.client.client.WechatMiniProgramClient;
|
||||
import io.destiny.client.core.domain.ClientUser;
|
||||
import io.destiny.client.core.domain.UserBinding;
|
||||
import io.destiny.client.core.repository.IClientUserRepository;
|
||||
import io.destiny.client.core.repository.IUserBindingRepository;
|
||||
import io.destiny.client.core.service.ClientUserAuthService;
|
||||
import io.destiny.client.dto.LoginRequest;
|
||||
|
||||
import io.destiny.client.dto.WechatAuthResponse;
|
||||
import io.destiny.client.dto.WechatLoginRequest;
|
||||
import io.destiny.client.security.JwtTokenProvider;
|
||||
import io.destiny.common.exception.ApplicationException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import static io.destiny.client.core.exception.ClientUserAuthExceptionCode.CLIENT_INVALID_WECHAT_CODE;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class WechatLoginStrategyTest {
|
||||
|
||||
@Mock
|
||||
private ClientUserAuthService clientUserAuthService;
|
||||
|
||||
@Mock
|
||||
private JwtTokenProvider jwtTokenProvider;
|
||||
|
||||
@Mock
|
||||
private WechatMiniProgramClient wechatClient;
|
||||
|
||||
@Mock
|
||||
private IClientUserRepository clientUserRepository;
|
||||
|
||||
@Mock
|
||||
private IUserBindingRepository userBindingRepository;
|
||||
|
||||
@InjectMocks
|
||||
private WechatLoginStrategy wechatLoginStrategy;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetLoginType() {
|
||||
assertEquals("wechat", wechatLoginStrategy.getLoginType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateRequestWithValidData() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
WechatLoginRequest wechatRequest = new WechatLoginRequest();
|
||||
wechatRequest.setCode("valid_code");
|
||||
request.setData(wechatRequest);
|
||||
|
||||
StepVerifier.create(wechatLoginStrategy.validateRequest(request))
|
||||
.expectNext(request)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateRequestWithNullCode() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
WechatLoginRequest wechatRequest = new WechatLoginRequest();
|
||||
wechatRequest.setCode(null);
|
||||
request.setData(wechatRequest);
|
||||
|
||||
StepVerifier.create(wechatLoginStrategy.validateRequest(request))
|
||||
.expectErrorMatches(error -> error instanceof ApplicationException &&
|
||||
((ApplicationException) error).getExceptionCode()
|
||||
.equals(CLIENT_INVALID_WECHAT_CODE))
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateRequestWithEmptyCode() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
WechatLoginRequest wechatRequest = new WechatLoginRequest();
|
||||
wechatRequest.setCode("");
|
||||
request.setData(wechatRequest);
|
||||
|
||||
StepVerifier.create(wechatLoginStrategy.validateRequest(request))
|
||||
.expectErrorMatches(error -> error instanceof ApplicationException &&
|
||||
((ApplicationException) error).getExceptionCode()
|
||||
.equals(CLIENT_INVALID_WECHAT_CODE))
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateRequestWithInvalidDataType() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setData("invalid_data");
|
||||
|
||||
StepVerifier.create(wechatLoginStrategy.validateRequest(request))
|
||||
.expectErrorMatches(error -> error instanceof ApplicationException &&
|
||||
((ApplicationException) error).getExceptionCode()
|
||||
.equals(CLIENT_INVALID_WECHAT_CODE))
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginSuccessWithExistingUserByOpenId() {
|
||||
when(jwtTokenProvider.generateToken(anyLong(), anyString())).thenReturn("test-token");
|
||||
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("wechat");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
WechatLoginRequest wechatRequest = new WechatLoginRequest();
|
||||
wechatRequest.setCode("valid_code");
|
||||
request.setData(wechatRequest);
|
||||
|
||||
WechatAuthResponse authResponse = new WechatAuthResponse();
|
||||
authResponse.setOpenId("open_id_123");
|
||||
authResponse.setUnionId("union_id_456");
|
||||
authResponse.setSessionKey("session_key");
|
||||
|
||||
ClientUser user = new ClientUser();
|
||||
user.setId(1L);
|
||||
user.setUsername("testuser");
|
||||
user.setWechatOpenId("open_id_123");
|
||||
|
||||
when(wechatClient.auth("valid_code")).thenReturn(Mono.just(authResponse));
|
||||
when(clientUserRepository.findByWechatOpenId("open_id_123")).thenReturn(Mono.just(user));
|
||||
|
||||
StepVerifier.create(wechatLoginStrategy.login(request))
|
||||
.expectNextMatches(result -> result.getUserId().equals(1L) &&
|
||||
result.getUsername().equals("testuser") &&
|
||||
result.getPlatform().equals("wechat") &&
|
||||
result.getToken().equals("test-token"))
|
||||
.verifyComplete();
|
||||
|
||||
verify(clientUserRepository, never()).findByWechatUnionId(anyString());
|
||||
verify(userBindingRepository, never()).findByPlatformAndUnionId(anyString(), anyString());
|
||||
verify(clientUserRepository, never()).save(any(ClientUser.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginSuccessWithExistingUserByUnionId() {
|
||||
when(jwtTokenProvider.generateToken(anyLong(), anyString())).thenReturn("test-token");
|
||||
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("wechat");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
WechatLoginRequest wechatRequest = new WechatLoginRequest();
|
||||
wechatRequest.setCode("valid_code");
|
||||
request.setData(wechatRequest);
|
||||
|
||||
WechatAuthResponse authResponse = new WechatAuthResponse();
|
||||
authResponse.setOpenId("open_id_123");
|
||||
authResponse.setUnionId("union_id_456");
|
||||
authResponse.setSessionKey("session_key");
|
||||
|
||||
ClientUser user = new ClientUser();
|
||||
user.setId(1L);
|
||||
user.setUsername("testuser");
|
||||
user.setWechatUnionId("union_id_456");
|
||||
|
||||
when(wechatClient.auth("valid_code")).thenReturn(Mono.just(authResponse));
|
||||
when(clientUserRepository.findByWechatOpenId("open_id_123")).thenReturn(Mono.empty());
|
||||
when(clientUserRepository.findByWechatUnionId("union_id_456")).thenReturn(Mono.just(user));
|
||||
|
||||
StepVerifier.create(wechatLoginStrategy.login(request))
|
||||
.expectNextMatches(result -> result.getUserId().equals(1L) &&
|
||||
result.getUsername().equals("testuser") &&
|
||||
result.getPlatform().equals("wechat") &&
|
||||
result.getToken().equals("test-token"))
|
||||
.verifyComplete();
|
||||
|
||||
verify(userBindingRepository, never()).findByPlatformAndUnionId(anyString(), anyString());
|
||||
verify(clientUserRepository, never()).save(any(ClientUser.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginSuccessWithExistingUserByBinding() {
|
||||
when(jwtTokenProvider.generateToken(anyLong(), anyString())).thenReturn("test-token");
|
||||
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("wechat");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
WechatLoginRequest wechatRequest = new WechatLoginRequest();
|
||||
wechatRequest.setCode("valid_code");
|
||||
request.setData(wechatRequest);
|
||||
|
||||
WechatAuthResponse authResponse = new WechatAuthResponse();
|
||||
authResponse.setOpenId("open_id_123");
|
||||
authResponse.setUnionId("union_id_456");
|
||||
authResponse.setSessionKey("session_key");
|
||||
|
||||
ClientUser user = new ClientUser();
|
||||
user.setId(1L);
|
||||
user.setUsername("testuser");
|
||||
|
||||
UserBinding binding = new UserBinding();
|
||||
binding.setUserId(1L);
|
||||
binding.setPlatform("wechat");
|
||||
binding.setPlatformUnionId("union_id_456");
|
||||
|
||||
when(wechatClient.auth("valid_code")).thenReturn(Mono.just(authResponse));
|
||||
when(clientUserRepository.findByWechatOpenId("open_id_123")).thenReturn(Mono.empty());
|
||||
when(clientUserRepository.findByWechatUnionId("union_id_456")).thenReturn(Mono.empty());
|
||||
when(userBindingRepository.findByPlatformAndUnionId("wechat", "union_id_456"))
|
||||
.thenReturn(Mono.just(binding));
|
||||
when(clientUserRepository.findById(1L)).thenReturn(Mono.just(user));
|
||||
|
||||
StepVerifier.create(wechatLoginStrategy.login(request))
|
||||
.expectNextMatches(result -> result.getUserId().equals(1L) &&
|
||||
result.getUsername().equals("testuser") &&
|
||||
result.getPlatform().equals("wechat") &&
|
||||
result.getToken().equals("test-token"))
|
||||
.verifyComplete();
|
||||
|
||||
verify(clientUserRepository, never()).save(any(ClientUser.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginSuccessWithNewUser() {
|
||||
when(jwtTokenProvider.generateToken(anyLong(), anyString())).thenReturn("test-token");
|
||||
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("wechat");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
WechatLoginRequest wechatRequest = new WechatLoginRequest();
|
||||
wechatRequest.setCode("valid_code");
|
||||
request.setData(wechatRequest);
|
||||
|
||||
WechatAuthResponse authResponse = new WechatAuthResponse();
|
||||
authResponse.setOpenId("open_id_123");
|
||||
authResponse.setUnionId("union_id_456");
|
||||
authResponse.setSessionKey("session_key");
|
||||
|
||||
ClientUser newUser = new ClientUser();
|
||||
newUser.setId(1L);
|
||||
newUser.setWechatOpenId("open_id_123");
|
||||
newUser.setWechatUnionId("union_id_456");
|
||||
newUser.setPhoneVerified(false);
|
||||
|
||||
UserBinding binding = new UserBinding();
|
||||
binding.setId(1L);
|
||||
binding.setUserId(1L);
|
||||
binding.setPlatform("wechat");
|
||||
binding.setPlatformOpenId("open_id_123");
|
||||
binding.setPlatformUnionId("union_id_456");
|
||||
|
||||
when(wechatClient.auth("valid_code")).thenReturn(Mono.just(authResponse));
|
||||
when(clientUserRepository.findByWechatOpenId("open_id_123")).thenReturn(Mono.empty());
|
||||
when(clientUserRepository.findByWechatUnionId("union_id_456")).thenReturn(Mono.empty());
|
||||
when(userBindingRepository.findByPlatformAndUnionId("wechat", "union_id_456")).thenReturn(Mono.empty());
|
||||
when(clientUserRepository.save(any(ClientUser.class))).thenReturn(Mono.just(newUser));
|
||||
when(userBindingRepository.save(any(UserBinding.class))).thenReturn(Mono.just(binding));
|
||||
|
||||
StepVerifier.create(wechatLoginStrategy.login(request))
|
||||
.expectNextMatches(result -> result.getUserId().equals(1L) &&
|
||||
result.getPlatform().equals("wechat") &&
|
||||
result.getToken().equals("test-token"))
|
||||
.verifyComplete();
|
||||
|
||||
verify(clientUserRepository).save(any(ClientUser.class));
|
||||
verify(userBindingRepository).save(any(UserBinding.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginWithNewUserWithoutUnionId() {
|
||||
when(jwtTokenProvider.generateToken(anyLong(), anyString())).thenReturn("test-token");
|
||||
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("wechat");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
WechatLoginRequest wechatRequest = new WechatLoginRequest();
|
||||
wechatRequest.setCode("valid_code");
|
||||
request.setData(wechatRequest);
|
||||
|
||||
WechatAuthResponse authResponse = new WechatAuthResponse();
|
||||
authResponse.setOpenId("open_id_123");
|
||||
authResponse.setSessionKey("session_key");
|
||||
|
||||
ClientUser newUser = new ClientUser();
|
||||
newUser.setId(1L);
|
||||
newUser.setWechatOpenId("open_id_123");
|
||||
newUser.setPhoneVerified(false);
|
||||
|
||||
UserBinding binding = new UserBinding();
|
||||
binding.setId(1L);
|
||||
binding.setUserId(1L);
|
||||
binding.setPlatform("wechat");
|
||||
binding.setPlatformOpenId("open_id_123");
|
||||
|
||||
when(wechatClient.auth("valid_code")).thenReturn(Mono.just(authResponse));
|
||||
when(clientUserRepository.findByWechatOpenId("open_id_123")).thenReturn(Mono.empty());
|
||||
when(clientUserRepository.save(any(ClientUser.class))).thenReturn(Mono.just(newUser));
|
||||
when(userBindingRepository.save(any(UserBinding.class))).thenReturn(Mono.just(binding));
|
||||
|
||||
StepVerifier.create(wechatLoginStrategy.login(request))
|
||||
.expectNextMatches(result -> result.getUserId().equals(1L) &&
|
||||
result.getPlatform().equals("wechat") &&
|
||||
result.getToken().equals("test-token"))
|
||||
.verifyComplete();
|
||||
|
||||
verify(clientUserRepository, never()).findByWechatUnionId(anyString());
|
||||
verify(userBindingRepository, never()).findByPlatformAndUnionId(anyString(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginWithAuthFailure() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
request.setLoginType("wechat");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
WechatLoginRequest wechatRequest = new WechatLoginRequest();
|
||||
wechatRequest.setCode("invalid_code");
|
||||
request.setData(wechatRequest);
|
||||
|
||||
when(wechatClient.auth("invalid_code")).thenReturn(Mono.error(new Exception("Auth failed")));
|
||||
|
||||
StepVerifier.create(wechatLoginStrategy.login(request))
|
||||
.expectError(Exception.class)
|
||||
.verify();
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package io.destiny.client.dto;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class AccountBindRequestTest {
|
||||
|
||||
@Test
|
||||
void testGettersAndSetters() {
|
||||
AccountBindRequest request = new AccountBindRequest();
|
||||
|
||||
request.setPlatform("wechat");
|
||||
request.setCode("test_code_123");
|
||||
|
||||
assertEquals("wechat", request.getPlatform());
|
||||
assertEquals("test_code_123", request.getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefaultValues() {
|
||||
AccountBindRequest request = new AccountBindRequest();
|
||||
|
||||
assertNull(request.getPlatform());
|
||||
assertNull(request.getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetNullValues() {
|
||||
AccountBindRequest request = new AccountBindRequest();
|
||||
|
||||
request.setPlatform(null);
|
||||
request.setCode(null);
|
||||
|
||||
assertNull(request.getPlatform());
|
||||
assertNull(request.getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetEmptyValues() {
|
||||
AccountBindRequest request = new AccountBindRequest();
|
||||
|
||||
request.setPlatform("");
|
||||
request.setCode("");
|
||||
|
||||
assertEquals("", request.getPlatform());
|
||||
assertEquals("", request.getCode());
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package io.destiny.client.dto;
|
||||
|
||||
import io.destiny.client.core.domain.query.ClientUserLogin;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ClientUserLoginRequestTest {
|
||||
|
||||
@Test
|
||||
void testGettersAndSetters() {
|
||||
ClientUserLoginRequest request = new ClientUserLoginRequest();
|
||||
|
||||
request.setUsername("testuser");
|
||||
request.setPassword("password123");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
|
||||
assertEquals("testuser", request.getUsername());
|
||||
assertEquals("password123", request.getPassword());
|
||||
assertEquals("192.168.1.1", request.getIpAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefaultValues() {
|
||||
ClientUserLoginRequest request = new ClientUserLoginRequest();
|
||||
|
||||
assertNull(request.getUsername());
|
||||
assertNull(request.getPassword());
|
||||
assertNull(request.getIpAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetNullValues() {
|
||||
ClientUserLoginRequest request = new ClientUserLoginRequest();
|
||||
|
||||
request.setUsername(null);
|
||||
request.setPassword(null);
|
||||
request.setIpAddress(null);
|
||||
|
||||
assertNull(request.getUsername());
|
||||
assertNull(request.getPassword());
|
||||
assertNull(request.getIpAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetEmptyValues() {
|
||||
ClientUserLoginRequest request = new ClientUserLoginRequest();
|
||||
|
||||
request.setUsername("");
|
||||
request.setPassword("");
|
||||
request.setIpAddress("");
|
||||
|
||||
assertEquals("", request.getUsername());
|
||||
assertEquals("", request.getPassword());
|
||||
assertEquals("", request.getIpAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToDomain() {
|
||||
ClientUserLoginRequest request = new ClientUserLoginRequest();
|
||||
request.setUsername("testuser");
|
||||
request.setPassword("password123");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
|
||||
ClientUserLogin domain = request.toDomain();
|
||||
|
||||
assertNotNull(domain);
|
||||
assertEquals("testuser", domain.getUsername());
|
||||
assertEquals("password123", domain.getPassword());
|
||||
assertNotNull(domain.getIpAddress());
|
||||
assertEquals("192.168.1.1", domain.getIpAddress().getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToDomainWithNullValues() {
|
||||
ClientUserLoginRequest request = new ClientUserLoginRequest();
|
||||
|
||||
ClientUserLogin domain = request.toDomain();
|
||||
|
||||
assertNotNull(domain);
|
||||
assertNull(domain.getUsername());
|
||||
assertNull(domain.getPassword());
|
||||
assertNull(domain.getIpAddress());
|
||||
}
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
package io.destiny.client.dto;
|
||||
|
||||
import io.destiny.client.core.domain.ClientUser;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ClientUserRegisterRequestTest {
|
||||
|
||||
@Test
|
||||
void testGettersAndSetters() {
|
||||
ClientUserRegisterRequest request = new ClientUserRegisterRequest();
|
||||
|
||||
request.setUsername("testuser");
|
||||
request.setPassword("password123");
|
||||
request.setEmail("test@example.com");
|
||||
request.setPhone("13800138000");
|
||||
request.setGender("male");
|
||||
|
||||
assertEquals("testuser", request.getUsername());
|
||||
assertEquals("password123", request.getPassword());
|
||||
assertEquals("test@example.com", request.getEmail());
|
||||
assertEquals("13800138000", request.getPhone());
|
||||
assertEquals("male", request.getGender());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefaultValues() {
|
||||
ClientUserRegisterRequest request = new ClientUserRegisterRequest();
|
||||
|
||||
assertNull(request.getUsername());
|
||||
assertNull(request.getPassword());
|
||||
assertNull(request.getEmail());
|
||||
assertNull(request.getPhone());
|
||||
assertNull(request.getGender());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetNullValues() {
|
||||
ClientUserRegisterRequest request = new ClientUserRegisterRequest();
|
||||
|
||||
request.setUsername(null);
|
||||
request.setPassword(null);
|
||||
request.setEmail(null);
|
||||
request.setPhone(null);
|
||||
request.setGender(null);
|
||||
|
||||
assertNull(request.getUsername());
|
||||
assertNull(request.getPassword());
|
||||
assertNull(request.getEmail());
|
||||
assertNull(request.getPhone());
|
||||
assertNull(request.getGender());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetEmptyValues() {
|
||||
ClientUserRegisterRequest request = new ClientUserRegisterRequest();
|
||||
|
||||
request.setUsername("");
|
||||
request.setPassword("");
|
||||
request.setEmail("");
|
||||
request.setPhone("");
|
||||
request.setGender("");
|
||||
|
||||
assertEquals("", request.getUsername());
|
||||
assertEquals("", request.getPassword());
|
||||
assertEquals("", request.getEmail());
|
||||
assertEquals("", request.getPhone());
|
||||
assertEquals("", request.getGender());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToDomain() {
|
||||
ClientUserRegisterRequest request = new ClientUserRegisterRequest();
|
||||
request.setUsername("testuser");
|
||||
request.setPassword("password123");
|
||||
request.setEmail("test@example.com");
|
||||
request.setPhone("13800138000");
|
||||
request.setGender("male");
|
||||
|
||||
ClientUser domain = request.toDomain();
|
||||
|
||||
assertNotNull(domain);
|
||||
assertEquals("testuser", domain.getUsername());
|
||||
assertEquals("password123", domain.getPassword());
|
||||
assertNotNull(domain.getEmail());
|
||||
assertEquals("test@example.com", domain.getEmail().getValue());
|
||||
assertNotNull(domain.getPhone());
|
||||
assertEquals("13800138000", domain.getPhone().getValue());
|
||||
assertEquals("male", domain.getGender());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToDomainWithNullValues() {
|
||||
ClientUserRegisterRequest request = new ClientUserRegisterRequest();
|
||||
|
||||
ClientUser domain = request.toDomain();
|
||||
|
||||
assertNotNull(domain);
|
||||
assertNull(domain.getUsername());
|
||||
assertNull(domain.getPassword());
|
||||
assertNull(domain.getEmail());
|
||||
assertNull(domain.getPhone());
|
||||
assertNull(domain.getGender());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToDomainWithPartialValues() {
|
||||
ClientUserRegisterRequest request = new ClientUserRegisterRequest();
|
||||
request.setUsername("testuser");
|
||||
request.setPassword("password123");
|
||||
|
||||
ClientUser domain = request.toDomain();
|
||||
|
||||
assertNotNull(domain);
|
||||
assertEquals("testuser", domain.getUsername());
|
||||
assertEquals("password123", domain.getPassword());
|
||||
assertNull(domain.getEmail());
|
||||
assertNull(domain.getPhone());
|
||||
assertNull(domain.getGender());
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package io.destiny.client.dto;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class DouyinLoginRequestTest {
|
||||
|
||||
@Test
|
||||
void testGettersAndSetters() {
|
||||
DouyinLoginRequest request = new DouyinLoginRequest();
|
||||
|
||||
request.setCode("test_code_123");
|
||||
request.setPlatform("douyin");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
|
||||
assertEquals("test_code_123", request.getCode());
|
||||
assertEquals("douyin", request.getPlatform());
|
||||
assertEquals("192.168.1.1", request.getIpAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefaultValues() {
|
||||
DouyinLoginRequest request = new DouyinLoginRequest();
|
||||
|
||||
assertNull(request.getCode());
|
||||
assertNull(request.getPlatform());
|
||||
assertNull(request.getIpAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetNullValues() {
|
||||
DouyinLoginRequest request = new DouyinLoginRequest();
|
||||
|
||||
request.setCode(null);
|
||||
request.setPlatform(null);
|
||||
request.setIpAddress(null);
|
||||
|
||||
assertNull(request.getCode());
|
||||
assertNull(request.getPlatform());
|
||||
assertNull(request.getIpAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetEmptyValues() {
|
||||
DouyinLoginRequest request = new DouyinLoginRequest();
|
||||
|
||||
request.setCode("");
|
||||
request.setPlatform("");
|
||||
request.setIpAddress("");
|
||||
|
||||
assertEquals("", request.getCode());
|
||||
assertEquals("", request.getPlatform());
|
||||
assertEquals("", request.getIpAddress());
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package io.destiny.client.dto;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class LoginRequestTest {
|
||||
|
||||
@Test
|
||||
void testGettersAndSetters() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
|
||||
request.setLoginType("sms");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
request.setData(new SmsLoginRequest());
|
||||
|
||||
assertEquals("sms", request.getLoginType());
|
||||
assertEquals("192.168.1.1", request.getIpAddress());
|
||||
assertNotNull(request.getData());
|
||||
assertTrue(request.getData() instanceof SmsLoginRequest);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefaultValues() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
|
||||
assertNull(request.getLoginType());
|
||||
assertNull(request.getIpAddress());
|
||||
assertNull(request.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetNullValues() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
|
||||
request.setLoginType(null);
|
||||
request.setIpAddress(null);
|
||||
request.setData(null);
|
||||
|
||||
assertNull(request.getLoginType());
|
||||
assertNull(request.getIpAddress());
|
||||
assertNull(request.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetEmptyValues() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
|
||||
request.setLoginType("");
|
||||
request.setIpAddress("");
|
||||
|
||||
assertEquals("", request.getLoginType());
|
||||
assertEquals("", request.getIpAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetDifferentDataTypes() {
|
||||
LoginRequest request = new LoginRequest();
|
||||
|
||||
SmsLoginRequest smsRequest = new SmsLoginRequest();
|
||||
request.setData(smsRequest);
|
||||
assertEquals(smsRequest, request.getData());
|
||||
|
||||
PasswordLoginRequest passwordRequest = new PasswordLoginRequest();
|
||||
request.setData(passwordRequest);
|
||||
assertEquals(passwordRequest, request.getData());
|
||||
|
||||
WechatLoginRequest wechatRequest = new WechatLoginRequest();
|
||||
request.setData(wechatRequest);
|
||||
assertEquals(wechatRequest, request.getData());
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package io.destiny.client.dto;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class PasswordLoginRequestTest {
|
||||
|
||||
@Test
|
||||
void testGettersAndSetters() {
|
||||
PasswordLoginRequest request = new PasswordLoginRequest();
|
||||
|
||||
request.setUsername("testuser");
|
||||
request.setPassword("password123");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
|
||||
assertEquals("testuser", request.getUsername());
|
||||
assertEquals("password123", request.getPassword());
|
||||
assertEquals("192.168.1.1", request.getIpAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefaultValues() {
|
||||
PasswordLoginRequest request = new PasswordLoginRequest();
|
||||
|
||||
assertNull(request.getUsername());
|
||||
assertNull(request.getPassword());
|
||||
assertNull(request.getIpAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetNullValues() {
|
||||
PasswordLoginRequest request = new PasswordLoginRequest();
|
||||
|
||||
request.setUsername(null);
|
||||
request.setPassword(null);
|
||||
request.setIpAddress(null);
|
||||
|
||||
assertNull(request.getUsername());
|
||||
assertNull(request.getPassword());
|
||||
assertNull(request.getIpAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetEmptyValues() {
|
||||
PasswordLoginRequest request = new PasswordLoginRequest();
|
||||
|
||||
request.setUsername("");
|
||||
request.setPassword("");
|
||||
request.setIpAddress("");
|
||||
|
||||
assertEquals("", request.getUsername());
|
||||
assertEquals("", request.getPassword());
|
||||
assertEquals("", request.getIpAddress());
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package io.destiny.client.dto;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class SmsCodeSendRequestTest {
|
||||
|
||||
@Test
|
||||
void testGettersAndSetters() {
|
||||
SmsCodeSendRequest request = new SmsCodeSendRequest();
|
||||
|
||||
request.setPhone("13800138000");
|
||||
request.setType("login");
|
||||
|
||||
assertEquals("13800138000", request.getPhone());
|
||||
assertEquals("login", request.getType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefaultValues() {
|
||||
SmsCodeSendRequest request = new SmsCodeSendRequest();
|
||||
|
||||
assertNull(request.getPhone());
|
||||
assertNull(request.getType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetNullValues() {
|
||||
SmsCodeSendRequest request = new SmsCodeSendRequest();
|
||||
|
||||
request.setPhone(null);
|
||||
request.setType(null);
|
||||
|
||||
assertNull(request.getPhone());
|
||||
assertNull(request.getType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetEmptyValues() {
|
||||
SmsCodeSendRequest request = new SmsCodeSendRequest();
|
||||
|
||||
request.setPhone("");
|
||||
request.setType("");
|
||||
|
||||
assertEquals("", request.getPhone());
|
||||
assertEquals("", request.getType());
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package io.destiny.client.dto;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class SmsLoginRequestTest {
|
||||
|
||||
@Test
|
||||
void testGettersAndSetters() {
|
||||
SmsLoginRequest request = new SmsLoginRequest();
|
||||
|
||||
request.setPhone("13800138000");
|
||||
request.setCode("123456");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
|
||||
assertEquals("13800138000", request.getPhone());
|
||||
assertEquals("123456", request.getCode());
|
||||
assertEquals("192.168.1.1", request.getIpAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefaultValues() {
|
||||
SmsLoginRequest request = new SmsLoginRequest();
|
||||
|
||||
assertNull(request.getPhone());
|
||||
assertNull(request.getCode());
|
||||
assertNull(request.getIpAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetNullValues() {
|
||||
SmsLoginRequest request = new SmsLoginRequest();
|
||||
|
||||
request.setPhone(null);
|
||||
request.setCode(null);
|
||||
request.setIpAddress(null);
|
||||
|
||||
assertNull(request.getPhone());
|
||||
assertNull(request.getCode());
|
||||
assertNull(request.getIpAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetEmptyValues() {
|
||||
SmsLoginRequest request = new SmsLoginRequest();
|
||||
|
||||
request.setPhone("");
|
||||
request.setCode("");
|
||||
request.setIpAddress("");
|
||||
|
||||
assertEquals("", request.getPhone());
|
||||
assertEquals("", request.getCode());
|
||||
assertEquals("", request.getIpAddress());
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package io.destiny.client.dto;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class WechatLoginRequestTest {
|
||||
|
||||
@Test
|
||||
void testGettersAndSetters() {
|
||||
WechatLoginRequest request = new WechatLoginRequest();
|
||||
|
||||
request.setCode("test_code_123");
|
||||
request.setPlatform("wechat");
|
||||
request.setIpAddress("192.168.1.1");
|
||||
|
||||
assertEquals("test_code_123", request.getCode());
|
||||
assertEquals("wechat", request.getPlatform());
|
||||
assertEquals("192.168.1.1", request.getIpAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefaultValues() {
|
||||
WechatLoginRequest request = new WechatLoginRequest();
|
||||
|
||||
assertNull(request.getCode());
|
||||
assertNull(request.getPlatform());
|
||||
assertNull(request.getIpAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetNullValues() {
|
||||
WechatLoginRequest request = new WechatLoginRequest();
|
||||
|
||||
request.setCode(null);
|
||||
request.setPlatform(null);
|
||||
request.setIpAddress(null);
|
||||
|
||||
assertNull(request.getCode());
|
||||
assertNull(request.getPlatform());
|
||||
assertNull(request.getIpAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetEmptyValues() {
|
||||
WechatLoginRequest request = new WechatLoginRequest();
|
||||
|
||||
request.setCode("");
|
||||
request.setPlatform("");
|
||||
request.setIpAddress("");
|
||||
|
||||
assertEquals("", request.getCode());
|
||||
assertEquals("", request.getPlatform());
|
||||
assertEquals("", request.getIpAddress());
|
||||
}
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
package io.destiny.client.security;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class JwtTokenProviderTest {
|
||||
|
||||
@InjectMocks
|
||||
private JwtTokenProvider jwtTokenProvider;
|
||||
|
||||
private static final String SECRET_KEY = "your-secret-key-must-be-at-least-256-bits-long-for-hs256-algorithm";
|
||||
private static final Long EXPIRATION = 86400000L;
|
||||
private static final Long USER_ID = 123L;
|
||||
private static final String USERNAME = "testuser";
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
ReflectionTestUtils.setField(jwtTokenProvider, "secret", SECRET_KEY);
|
||||
ReflectionTestUtils.setField(jwtTokenProvider, "expiration", EXPIRATION);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenerateToken_Success() {
|
||||
String token = jwtTokenProvider.generateToken(USER_ID, USERNAME);
|
||||
assertNotNull(token);
|
||||
assertFalse(token.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetUserIdFromToken_Success() {
|
||||
String token = jwtTokenProvider.generateToken(USER_ID, USERNAME);
|
||||
Long userId = jwtTokenProvider.getUserIdFromToken(token);
|
||||
assertEquals(USER_ID, userId);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetUserIdFromToken_InvalidToken() {
|
||||
Long userId = jwtTokenProvider.getUserIdFromToken("invalid.token.here");
|
||||
assertNull(userId);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetUserIdFromToken_NullToken() {
|
||||
Long userId = jwtTokenProvider.getUserIdFromToken(null);
|
||||
assertNull(userId);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetUsernameFromToken_Success() {
|
||||
String token = jwtTokenProvider.generateToken(USER_ID, USERNAME);
|
||||
String username = jwtTokenProvider.getUsernameFromToken(token);
|
||||
assertEquals(USERNAME, username);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetUsernameFromToken_InvalidToken() {
|
||||
String username = jwtTokenProvider.getUsernameFromToken("invalid.token.here");
|
||||
assertNull(username);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetUsernameFromToken_NullToken() {
|
||||
String username = jwtTokenProvider.getUsernameFromToken(null);
|
||||
assertNull(username);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateToken_Success() {
|
||||
String token = jwtTokenProvider.generateToken(USER_ID, USERNAME);
|
||||
Boolean isValid = jwtTokenProvider.validateToken(token);
|
||||
assertTrue(isValid);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateToken_InvalidToken() {
|
||||
Boolean isValid = jwtTokenProvider.validateToken("invalid.token.here");
|
||||
assertFalse(isValid);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidateToken_NullToken() {
|
||||
Boolean isValid = jwtTokenProvider.validateToken(null);
|
||||
assertFalse(isValid);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsTokenExpired_False() {
|
||||
String token = jwtTokenProvider.generateToken(USER_ID, USERNAME);
|
||||
Boolean isExpired = jwtTokenProvider.isTokenExpired(token);
|
||||
assertFalse(isExpired);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsTokenExpired_InvalidToken() {
|
||||
Boolean isExpired = jwtTokenProvider.isTokenExpired("invalid.token.here");
|
||||
assertTrue(isExpired);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsTokenExpired_NullToken() {
|
||||
Boolean isExpired = jwtTokenProvider.isTokenExpired(null);
|
||||
assertTrue(isExpired);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenerateToken_DifferentUsers() {
|
||||
String token1 = jwtTokenProvider.generateToken(1L, "user1");
|
||||
String token2 = jwtTokenProvider.generateToken(2L, "user2");
|
||||
assertNotEquals(token1, token2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetUserIdFromToken_CorrectUser() {
|
||||
String token = jwtTokenProvider.generateToken(USER_ID, USERNAME);
|
||||
Long userId = jwtTokenProvider.getUserIdFromToken(token);
|
||||
assertEquals(USER_ID, userId);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user