初始化crush-level项目

This commit is contained in:
lijunming 2025-11-13 16:29:01 +08:00
commit b6c2171cc2
1640 changed files with 110880 additions and 0 deletions

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

67
.idea/compiler.xml Normal file
View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="sonic-bear-lib" />
<module name="sonic-pigeon-common" />
<module name="sonic-lion-server" />
<module name="sonic-pigeon-lib" />
<module name="sonic-bear-common" />
<module name="sonic-lion-lib" />
<module name="sonic-shark-server" />
<module name="sonic-lion-common" />
<module name="sonic-shark-lib" />
<module name="sonic-cow-lib" />
<module name="common-lib" />
<module name="sonic-frog-server" />
<module name="sonic-gateway" />
<module name="sonic-frog-common" />
<module name="sonic-pigeon-server" />
<module name="dao-support-lib" />
<module name="sonic-cow-server" />
<module name="sonic-shark-common" />
<module name="sonic-frog-lib" />
<module name="sonic-common-api" />
<module name="sonic-bear-server" />
<module name="sonic-cow-common" />
</profile>
</annotationProcessing>
</component>
<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
<module name="common-lib" options="-parameters" />
<module name="common-lib-box" options="-parameters" />
<module name="common-parent-pom" options="-parameters" />
<module name="dao-support-lib" options="-parameters" />
<module name="sonic-bear" options="-parameters" />
<module name="sonic-bear-common" options="-parameters" />
<module name="sonic-bear-lib" options="-parameters" />
<module name="sonic-bear-server" options="-parameters" />
<module name="sonic-cow" options="-parameters" />
<module name="sonic-cow-common" options="-parameters" />
<module name="sonic-cow-lib" options="-parameters" />
<module name="sonic-cow-server" options="-parameters" />
<module name="sonic-frog" options="-parameters" />
<module name="sonic-frog-common" options="-parameters" />
<module name="sonic-frog-lib" options="-parameters" />
<module name="sonic-frog-server" options="-parameters" />
<module name="sonic-gateway" options="-parameters" />
<module name="sonic-lion" options="-parameters" />
<module name="sonic-lion-common" options="-parameters" />
<module name="sonic-lion-lib" options="-parameters" />
<module name="sonic-lion-server" options="-parameters" />
<module name="sonic-pigeon" options="-parameters" />
<module name="sonic-pigeon-common" options="-parameters" />
<module name="sonic-pigeon-lib" options="-parameters" />
<module name="sonic-pigeon-server" options="-parameters" />
<module name="sonic-shark" options="-parameters" />
<module name="sonic-shark-common" options="-parameters" />
<module name="sonic-shark-lib" options="-parameters" />
<module name="sonic-shark-server" options="-parameters" />
</option>
</component>
</project>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

42
.idea/encodings.xml Normal file
View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/sonic-bear/common/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-bear/lib/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-bear/server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-bear/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-bear/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-common/common-lib-box/common-lib/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-common/common-lib-box/dao-support-lib/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-common/common-lib-box/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-common/common-lib-box/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-common/common-parent-pom/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-common/common-parent-pom/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-cow/common/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-cow/lib/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-cow/server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-cow/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-cow/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-frog/common/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-frog/lib/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-frog/server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-frog/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-frog/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-gateway/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-lion/common/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-lion/lib/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-lion/server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-lion/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-lion/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-pigeon/common/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-pigeon/lib/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-pigeon/server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-pigeon/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-pigeon/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-shark/common/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-shark/lib/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-shark/server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-shark/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/sonic-shark/src/main/resources" charset="UTF-8" />
</component>
</project>

25
.idea/jarRepositories.xml Normal file
View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://maven.aliyun.com/repository/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="aliyun" />
<option name="name" value="aliyun" />
<option name="url" value="https://maven.aliyun.com/repository/public" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</component>
</project>

25
.idea/misc.xml Normal file
View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/sonic-bear/common/pom.xml" />
<option value="$PROJECT_DIR$/sonic-bear/lib/pom.xml" />
<option value="$PROJECT_DIR$/sonic-bear/pom.xml" />
<option value="$PROJECT_DIR$/sonic-common/common-lib-box/pom.xml" />
<option value="$PROJECT_DIR$/sonic-common-api/pom.xml" />
<option value="$PROJECT_DIR$/sonic-cow/pom.xml" />
<option value="$PROJECT_DIR$/sonic-frog/pom.xml" />
<option value="$PROJECT_DIR$/sonic-gateway/pom.xml" />
<option value="$PROJECT_DIR$/sonic-lion/pom.xml" />
<option value="$PROJECT_DIR$/sonic-pigeon/pom.xml" />
<option value="$PROJECT_DIR$/sonic-shark/pom.xml" />
<option value="$PROJECT_DIR$/sonic-common/common-parent-pom/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8 (2)" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/crushlevel-server.iml" filepath="$PROJECT_DIR$/.idea/crushlevel-server.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

26
sonic-bear/.gitignore vendored Normal file
View File

@ -0,0 +1,26 @@
/**/rebel.xml
#target/
target/
# IDEA #
.idea/
*.iml
# Eclipse #
.settings/
.classpath
.project
# Log #
logs/
*.log%
#mac
.DS_Store
#svn
.svn
#reviewboardrc
.reviewboardrc
**/rest-client.private.env.json

54
sonic-bear/common/pom.xml Normal file
View File

@ -0,0 +1,54 @@
<?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">
<parent>
<artifactId>sonic-bear</artifactId>
<groupId>com.sonic.bear</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sonic-bear-common</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
<dependencies>
<dependency>
<groupId>com.sonic</groupId>
<artifactId>common-lib</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- TODO: 如果不需要mysql请移除以下mysql相关的包 -->
<!-- mysql相关的包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!-- mysql相关的包-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,106 @@
package com.sonic.bear.common;
import com.alibaba.fastjson.JSONObject;
import com.sonic.common.AppRuntime;
import com.sonic.common.log.RequestLogAspect;
import com.sonic.common.rpc.HttpClient;
import com.sonic.common.rpc.RpcClient;
import com.sonic.common.rpc.RpcClientImpl;
import com.sonic.common.stat.FrequencyAlertInterceptor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.task.TaskExecutorBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.stream.Collectors;
import static org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME;
/**
* @author coder
*/
@Slf4j
public class GlobalConfig {
@Value("${spring.application.name}")
private String appName;
@Value("${spring.application.id}")
private String appId;
@Bean
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor() {
return new ScheduledThreadPoolExecutor(1);
}
/**
* XXX Spring默认配置当有Executor的bean后不再装载TaskExecutor, 这里因为手动注册了
* {@link #scheduledThreadPoolExecutor}会导致spring不再自动注册TaskExecutor, 因此需要去掉ConditionalOnMissingBean的限制.
* 参考{@link TaskExecutionAutoConfiguration#applicationTaskExecutor}
*/
@Bean(name = {APPLICATION_TASK_EXECUTOR_BEAN_NAME,
AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME})
public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
return builder.build();
}
@Bean
public RequestLogAspect requestLogAspect() {
return new RequestLogAspect();
}
@Bean
AppRuntime appRuntime(ApplicationContext applicationContext) {
return AppRuntime.builder().appId(appId).appName(appName).applicationContext(applicationContext).build();
}
/**
* 用于配置接口访问频率超限告警, 接口频率配置参考application.yml中的web.frequency-alert.rules
* TODO 根据需要配置为输出到日志或者alertClient
*
* @return
*/
@Bean
public FrequencyAlertInterceptor frequencyAlertInterceptor(RuleConfig ruleConfig) {
return new FrequencyAlertInterceptor(ruleConfig.getAlertRuleMap(),
(request, frequencyAlert) -> log.warn("frequency alert, api frequency alert, url = {} alert = {}",
request.getRequestURI(), JSONObject.toJSONString(frequencyAlert)));
}
@Bean
@Primary
public RpcClient rpcClient() {
return new RpcClientImpl(HttpClient.Config.EMPTY);
}
@Data
@Component
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "web.frequency-alert")
public static class RuleConfig {
private List<String> rules;
public Map<String, String> getAlertRuleMap() {
if (rules == null) {
return Collections.emptyMap();
}
return rules.stream()
.map(e -> {
String[] split = e.split(":");
return new AbstractMap.SimpleEntry<>(split[0], split[1]);
}).collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
}
}
}

View File

@ -0,0 +1,19 @@
package com.sonic.bear.common;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
@EnableTransactionManagement
@MapperScan("com.sonic.**.dao")
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
paginationInterceptor.setDialectType("mysql");
return paginationInterceptor;
}
}

47
sonic-bear/lib/pom.xml Normal file
View File

@ -0,0 +1,47 @@
<?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">
<parent>
<artifactId>sonic-bear</artifactId>
<groupId>com.sonic.bear</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.sonic.bear</groupId>
<artifactId>sonic-bear-lib</artifactId>
<packaging>jar</packaging>
<version>1.1-SNAPSHOT</version>
<dependencies>
<!-- <dependency>-->
<!-- <groupId>com.sonic</groupId>-->
<!-- <artifactId>common-lib</artifactId>-->
<!-- <exclusions>-->
<!-- &lt;!&ndash;2.17.0之前的版本有远程代码执行漏洞,所以需要排除掉&ndash;&gt;-->
<!-- <exclusion>-->
<!-- <artifactId>log4j-api</artifactId>-->
<!-- <groupId>org.apache.logging.log4j</groupId>-->
<!-- </exclusion>-->
<!-- <exclusion>-->
<!-- <groupId>com.alibaba</groupId>-->
<!-- <artifactId>fastjson</artifactId>-->
<!-- </exclusion>-->
<!-- </exclusions>-->
<!-- </dependency>-->
<dependency>
<groupId>com.sonic</groupId>
<artifactId>common-lib</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,71 @@
package com.sonic.bear.lib.client;
import com.alibaba.fastjson.TypeReference;
import com.google.common.collect.ImmutableMap;
import com.sonic.bear.lib.input.ThirdUserLoginInput;
import com.sonic.common.AppRuntime;
import com.sonic.common.auth.domains.Session;
import com.sonic.common.rpc.Result;
import com.sonic.common.rpc.RpcClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* @author code
*/
@Slf4j
@Service
public class UserLoginClient {
private static final String URI_THIRD_USER_LOGIN = "/api/auth/third-user-login";
private static final String URI_LOGOUT = "/api/auth/logout";
private static final String URI_KICK_OUT = "/api/auth/kick-out";
private RpcClient rpcClient;
private String host;
public UserLoginClient(RpcClient rpcClient, AppRuntime appRuntime) {
this.rpcClient = rpcClient;
switch (appRuntime.getEnv()) {
case dev:
this.host = "http://localhost:8080";
break;
case local:
this.host = "http://localhost:8080";
break;
case test:
this.host = "http://test-bear-svc:8080";
break;
case product:
default:
this.host = "http://prod-bear-svc:8080";
}
}
/**
* 三方账号登录
* @param input
* @return
*/
public Session thirdLogin(ThirdUserLoginInput input) {
return rpcClient.post(host + URI_THIRD_USER_LOGIN, input, new TypeReference<Result<Session>>(){});
}
/**
* 退出登录
* @param token
*/
public void logout(String token) {
rpcClient.post(host.concat(URI_LOGOUT), ImmutableMap.of("token", token), new TypeReference<Result<Void>>(){});
}
/**
* 踢下线
* @param userId
*/
public void kickOut(Long userId) {
rpcClient.post(host.concat(URI_KICK_OUT), ImmutableMap.of("userId", userId), new TypeReference<Result<Void>>(){});
}
}

View File

@ -0,0 +1,103 @@
package com.sonic.bear.lib.client;
import com.alibaba.fastjson.TypeReference;
import com.google.common.collect.ImmutableMap;
import com.sonic.bear.lib.enums.OptTypeEnum;
import com.sonic.bear.lib.enums.UserTypeEnum;
import com.sonic.bear.lib.input.UserNicknameSyncPoolInput;
import com.sonic.bear.lib.output.NicknameOutput;
import com.sonic.common.AppRuntime;
import com.sonic.common.rpc.Result;
import com.sonic.common.rpc.RpcClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author code
*/
@Slf4j
@Service
public class UserNicknamePoolClient {
private static final String URI_USER_NICKNAME_EXIST_CHECK = "/api/user-nickname/exist-check";
private static final String URI_USER_NICKNAME_SYNC_POOL = "/api/user-nickname/sync-pool";
private static final String URI_USER_NICKNAME_BATCH_GET = "/api/user-nickname/batch-get";
private RpcClient rpcClient;
private String host;
public UserNicknamePoolClient(RpcClient rpcClient, AppRuntime appRuntime) {
this.rpcClient = rpcClient;
switch (appRuntime.getEnv()) {
case dev:
this.host = "http://localhost:8080";
break;
case local:
this.host = "http://localhost:8080";
break;
case test:
this.host = "http://test-bear-svc:8080";
break;
case product:
default:
this.host = "http://prod-bear-svc:8080";
}
}
/**
* 昵称重复性校验
* @param nickname
* @return
*/
public boolean userNicknameExistCheck(String nickname) {
return rpcClient.post(host + URI_USER_NICKNAME_EXIST_CHECK, ImmutableMap.of("nickname", nickname), new TypeReference<Result<Boolean>>(){});
}
/**
* 昵称重复性校验
* @param exUserId
* @param nickname
* @return
*/
public boolean userNicknameExistCheck(Long exUserId, String nickname) {
return rpcClient.post(host + URI_USER_NICKNAME_EXIST_CHECK, ImmutableMap.of("exUserId", exUserId, "nickname", nickname), new TypeReference<Result<Boolean>>(){});
}
/**
* 同步AI昵称到数据池
* @param nickname
* @return
*/
public void syncAiNickname(Long aiUserId, String nickname, OptTypeEnum optType) {
UserNicknameSyncPoolInput input = UserNicknameSyncPoolInput.builder()
.userId(aiUserId)
.nickname(nickname)
.userType(UserTypeEnum.AI.getCode())
.optType(optType)
.build();
rpcClient.post(host + URI_USER_NICKNAME_SYNC_POOL, input, new TypeReference<Result<Void>>(){});
}
/**
* 批量获取用户昵称
* @param userIdList
* @return
*/
public Map<Long, String> batchGetNickname(List<Long> userIdList) {
Map<String, Object> param = new HashMap<>(1);
param.put("userIdList", userIdList);
List<NicknameOutput> outputList = rpcClient.post(host + URI_USER_NICKNAME_BATCH_GET, param, new TypeReference<Result<List<NicknameOutput>>>(){});
return outputList.stream().collect(HashMap::new, (m, v) -> m.put(v.getUserId(), v.getNickname()), HashMap::putAll);
}
}

View File

@ -0,0 +1,82 @@
package com.sonic.bear.lib.client;
import com.alibaba.fastjson.TypeReference;
import com.sonic.bear.lib.input.UserIdInput;
import com.sonic.bear.lib.input.UserIdListInput;
import com.sonic.bear.lib.output.BaseUserInfoOutput;
import com.sonic.bear.lib.output.UserInfoListOutput;
import com.sonic.common.AppRuntime;
import com.sonic.common.rpc.Result;
import com.sonic.common.rpc.RpcClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author code
*/
@Slf4j
@Service
public class UserSearchClient {
private static final String URI_BATCH_GET_USER_INFO = "/api/user/batch-get-info";
private static final String URI_BASE_USER_INFO = "/api/user/base-user-info";
private RpcClient rpcClient;
private String host;
public UserSearchClient(RpcClient rpcClient, AppRuntime appRuntime) {
this.rpcClient = rpcClient;
switch (appRuntime.getEnv()) {
case dev:
this.host = "http://localhost:8080";
break;
case local:
this.host = "http://localhost:8080";
break;
case test:
this.host = "http://test-bear-svc:8080";
break;
case product:
default:
this.host = "http://prod-bear-svc:8080";
}
}
/**
* 批量获取用户基础信息
* @param userIdList
* @return
*/
public Map<Long, UserInfoListOutput> batchGetUserInfoToMap(List<Long> userIdList) {
List<UserInfoListOutput> list = batchGetUserInfo(userIdList);
return list.stream().collect(Collectors.toMap(UserInfoListOutput::getUserId, v -> v));
}
/**
* 批量获取用户基础信息
* @param userIdList
* @return
*/
public List<UserInfoListOutput> batchGetUserInfo(List<Long> userIdList) {
UserIdListInput input = UserIdListInput.builder().userIdList(userIdList).build();
return rpcClient.post(host + URI_BATCH_GET_USER_INFO, input, new TypeReference<Result<List<UserInfoListOutput>>>(){});
}
/**
* 获取用户自己的基础信息
* @param userId
* @return
*/
public BaseUserInfoOutput baseUserInfo(Long userId) {
UserIdInput input = UserIdInput.builder().userId(userId).build();
return rpcClient.post(host + URI_BASE_USER_INFO, input, new TypeReference<Result<BaseUserInfoOutput>>(){});
}
}

View File

@ -0,0 +1,55 @@
package com.sonic.bear.lib.client;
import com.alibaba.fastjson.TypeReference;
import com.google.common.collect.ImmutableMap;
import com.sonic.common.AppRuntime;
import com.sonic.common.auth.domains.Session;
import com.sonic.common.rpc.Result;
import com.sonic.common.rpc.RpcClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* @author code
*/
@Slf4j
@Service
public class UserSessionClient {
private static final String URI_TOUCH_SESSION = "/api/auth/touch-session";
private RpcClient rpcClient;
private String host;
public UserSessionClient(RpcClient rpcClient, AppRuntime appRuntime) {
this.rpcClient = rpcClient;
switch (appRuntime.getEnv()) {
case dev:
this.host = "http://dev-bear.svc.cloud";
break;
case local:
this.host = "http://localhost:8080";
break;
case test:
this.host = "http://test-bear-svc:8080";
break;
case product:
default:
this.host = "http://prod-bear-svc:8080";
}
}
/**
* 正常返回则表示该session处于活跃状态抛出异常表示该会话已过期
* <p/>
* LoadingCache.get如果缓存中不存在则调用load直接访问远程服务并放入本地缓存
* 如果 load 抛出异常get 会直接抛出 load 的异常
* <p>
*
* @param token 用户会话TOKEN
*/
public Session touchSession(final String token) {
return rpcClient.post(host + URI_TOUCH_SESSION, ImmutableMap.of("token", token), new TypeReference<Result<Session>>(){});
}
}

View File

@ -0,0 +1,80 @@
package com.sonic.bear.lib.client;
import com.alibaba.fastjson.TypeReference;
import com.sonic.bear.lib.input.CompleteUserInfoInput;
import com.sonic.bear.lib.input.EditUserInfoInput;
import com.sonic.bear.lib.input.UserIdInput;
import com.sonic.common.AppRuntime;
import com.sonic.common.rpc.Result;
import com.sonic.common.rpc.RpcClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* @author code
*/
@Slf4j
@Service
public class UserSetClient {
private static final String URI_COMPLETE_USER_INFO = "/api/user/complete-user-info";
private static final String URI_EDIT_USER_INFO = "/api/user/edit-user-info";
private static final String URI_DEL_ACCOUNT = "/api/user/del-account";
private RpcClient rpcClient;
private String host;
public UserSetClient(RpcClient rpcClient, AppRuntime appRuntime) {
this.rpcClient = rpcClient;
switch (appRuntime.getEnv()) {
case dev:
this.host = "http://localhost:8080";
break;
case local:
this.host = "http://localhost:8080";
break;
case test:
this.host = "http://test-bear-svc:8080";
break;
case product:
default:
this.host = "http://prod-bear-svc:8080";
}
}
/**
* 完善用户基础信息
* @param input
* @return
*/
public void completeUserInfo(CompleteUserInfoInput input) {
rpcClient.post(host + URI_COMPLETE_USER_INFO, input, new TypeReference<Result<Void>>(){});
}
/**
* 编辑用户基础信息
* @param input
* @return
*/
public void editUserInfo(EditUserInfoInput input) {
rpcClient.post(host + URI_EDIT_USER_INFO, input, new TypeReference<Result<Void>>(){});
}
/**
* 编辑用户基础信息
* @param userId
* @return
*/
public void delAccount(Long userId) {
UserIdInput input = UserIdInput.builder()
.userId(userId)
.build();
rpcClient.post(host + URI_DEL_ACCOUNT, input, new TypeReference<Result<Void>>(){});
}
}

View File

@ -0,0 +1,36 @@
package com.sonic.bear.lib.enums;
import lombok.Getter;
/**
* 账号类型
* @author code
*/
@Getter
public enum AccountTypeEnum {
/**
* 客户用户
*/
CUSTOMER(1),
/**
* 后台账号
*/
SYSTEM(2),
;
private final Integer code;
AccountTypeEnum(Integer code) {
this.code = code;
}
public static AccountTypeEnum from(Integer code) {
for (AccountTypeEnum status : AccountTypeEnum.values()) {
if (code.equals(status.code)) {
return status;
}
}
return null;
}
}

View File

@ -0,0 +1,7 @@
package com.sonic.bear.lib.enums;
public enum OptTypeEnum {
ADD,
DEL,
UPD
}

View File

@ -0,0 +1,13 @@
package com.sonic.bear.lib.enums;
/**
* 三方类型枚举
*/
public enum ThirdTypeEnum {
DISCORD,
GOOGLE,
APPLE,
;
}

View File

@ -0,0 +1,17 @@
package com.sonic.bear.lib.enums;
import lombok.Getter;
@Getter
public enum UserTypeEnum {
USER(1),
AI(2);
private Integer code;
UserTypeEnum(Integer code) {
this.code = code;
}
}

View File

@ -0,0 +1,33 @@
package com.sonic.bear.lib.input;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class CompleteUserInfoInput {
@ApiModelProperty("用户ID")
private Long userId;
@ApiModelProperty("headImage")
private String headImage;
@ApiModelProperty("昵称")
private String nickname;
@ApiModelProperty("性别")
private Integer sex;
@ApiModelProperty("生日")
private LocalDateTime birthday;
}

View File

@ -0,0 +1,30 @@
package com.sonic.bear.lib.input;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class EditUserInfoInput {
@ApiModelProperty("用户ID")
private Long userId;
@ApiModelProperty("headImage")
private String headImage;
@ApiModelProperty("昵称")
private String nickname;
@ApiModelProperty("生日")
private LocalDateTime birthday;
}

View File

@ -0,0 +1,47 @@
package com.sonic.bear.lib.input;
import com.sonic.bear.lib.enums.ThirdTypeEnum;
import com.sonic.common.auth.domains.AppClientEnum;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class ThirdUserLoginInput {
@ApiModelProperty("三方ID")
@NotBlank
private String openId;
@ApiModelProperty("三方邮箱")
private String email;
@ApiModelProperty("三方昵称")
private String nickname;
@ApiModelProperty("三方类型(DISCORD、GOOGLE、APPLE)")
@NotNull
private ThirdTypeEnum thirdType;
@ApiModelProperty("客户端标识(WEB、IOS、ANDROID、ADMIN)")
@NotBlank
private AppClientEnum clientCode;
@ApiModelProperty("设备ID")
private String deviceId;
@ApiModelProperty("IP地址")
private String ip;
@ApiModelProperty("UA")
private String userAgent;
}

View File

@ -0,0 +1,21 @@
package com.sonic.bear.lib.input;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotNull;
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class UserIdInput {
@ApiModelProperty("用户ID")
@NotNull
private Long userId;
}

View File

@ -0,0 +1,22 @@
package com.sonic.bear.lib.input;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotNull;
import java.util.List;
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class UserIdListInput {
@ApiModelProperty("用户ID列表")
@NotNull
private List<Long> userIdList;
}

View File

@ -0,0 +1,20 @@
package com.sonic.bear.lib.input;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class UserNicknameExistCheckInput {
@ApiModelProperty("昵称")
private String nickname;
@ApiModelProperty("需要排除的用户ID")
private Long exUserId;
}

View File

@ -0,0 +1,31 @@
package com.sonic.bear.lib.input;
import com.sonic.bear.lib.enums.OptTypeEnum;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotNull;
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class UserNicknameSyncPoolInput {
@NotNull
@ApiModelProperty("用户ID")
private Long userId;
@ApiModelProperty("昵称")
private String nickname;
@ApiModelProperty("用户类型")
private Integer userType;
@ApiModelProperty("操作类型")
private OptTypeEnum optType;
}

View File

@ -0,0 +1,51 @@
package com.sonic.bear.lib.output;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class BaseUserInfoOutput {
@ApiModelProperty("用户ID")
private Long userId;
@ApiModelProperty("headImage")
private String headImage;
@ApiModelProperty("昵称")
private String nickname;
@ApiModelProperty("性别")
private Integer sex;
@ApiModelProperty("ID编号")
private String idCard;
@ApiModelProperty("生日")
private LocalDateTime birthday;
@ApiModelProperty("账号类型")
private String thirdType;
@ApiModelProperty("账号昵称")
private String thirdNickname;
@ApiModelProperty("账号邮箱")
private String thirdEmail;
//=============== 开关相关字段 ===============
@ApiModelProperty("是否需要强制完善用户基础信息")
private Boolean cpUserInfo;
@ApiModelProperty("是否是会员")
private Boolean isMember;
}

View File

@ -0,0 +1,30 @@
package com.sonic.bear.lib.output;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Author code
* @Description 用户昵称
* @Date 2024/1/12 11:57
* @Version 1.0
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class NicknameOutput {
/**
* 用户ID
*/
private Long userId;
/**
* 昵称
*/
private String nickname;
}

View File

@ -0,0 +1,30 @@
package com.sonic.bear.lib.output;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class UserInfoListOutput {
@ApiModelProperty("用户ID")
private Long userId;
@ApiModelProperty("idCard")
private String idCard;
@ApiModelProperty("headImage")
private String headImage;
@ApiModelProperty("昵称")
private String nickname;
@ApiModelProperty("性别")
private Integer sex;
}

62
sonic-bear/pom.xml Normal file
View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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>com.sonic</groupId>
<artifactId>common-parent-pom</artifactId>
<version>1.0</version>
</parent>
<artifactId>sonic-bear</artifactId>
<groupId>com.sonic.bear</groupId>
<packaging>pom</packaging>
<version>1.0</version>
<properties>
<!-- 2nd dependencies -->
<common-lib.version>1.0.6</common-lib.version>
<dao-support-lib.version>1.0</dao-support-lib.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.sonic</groupId>
<artifactId>common-lib</artifactId>
<version>${common-lib.version}</version>
</dependency>
<dependency>
<groupId>com.sonic</groupId>
<artifactId>dao-support-lib</artifactId>
<version>${dao-support-lib.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<modules>
<module>common</module>
<module>server</module>
<module>lib</module>
</modules>
<!-- <distributionManagement>-->
<!-- <repository>-->
<!-- <id>releases</id>-->
<!-- <url>http://121.196.56.236:8081/repository/maven-releases/</url>-->
<!-- </repository>-->
<!-- <snapshotRepository>-->
<!-- <id>snapshots</id>-->
<!-- <url>http://121.196.56.236:8081/repository/maven-snapshots/</url>-->
<!-- </snapshotRepository>-->
<!-- </distributionManagement>-->
</project>

121
sonic-bear/server/pom.xml Normal file
View File

@ -0,0 +1,121 @@
<?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">
<parent>
<artifactId>sonic-bear</artifactId>
<groupId>com.sonic.bear</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sonic-bear-server</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.sonic.bear</groupId>
<artifactId>sonic-bear-common</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>com.sonic.sdk</groupId>
<artifactId>sonic-common-api</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>com.sonic</groupId>
<artifactId>dao-support-lib</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>com.sonic.bear</groupId>
<artifactId>sonic-bear-lib</artifactId>
<version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.sonic.pigeon</groupId>
<artifactId>sonic-pigeon-lib</artifactId>
<version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.sonic.lion</groupId>
<artifactId>sonic-lion-lib</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- Redis相关的包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>it.ozimov</groupId>
<artifactId>embedded-redis</artifactId>
</dependency>
<!-- Redis相关的包 -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.13.1</version>
</dependency>
<!-- rabbitmq -->
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
<!-- 创建新项目后在还没有和git绑定的情况下编译会报错需要将以下两个参数设为false -->
<configuration>
<failOnNoGitDirectory>false</failOnNoGitDirectory>
<failOnUnableToExtractRepoInfo>false</failOnUnableToExtractRepoInfo>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,175 @@
package com.baomidou.mybatisplus.core.toolkit;
import lombok.extern.slf4j.Slf4j;
import java.net.InetAddress;
/**
* 原始链接 https://segmentfault.com/a/1190000020835840
* 雪花算法分布式唯一ID生成器<br>
* 每个机器号最高支持每秒65535个序列, 当秒序列不足时启用备份机器号, 若备份机器也不足时借用备份机器下一秒可用序列<br>
* 53 bits 趋势自增ID结构如下:
* <p>
* |00000000|00011111|11111111|11111111|11111111|11111111|11111111|11111111|
* |-----------|##########32bit 秒级时间戳##########|-----|-----------------|
* |--------------------------------------5bit机器位|xxxxx|-----------------|
* |-----------------------------------------16bit自增序列|xxxxxxxx|xxxxxxxx|
*
* @author:
* @date: 2021-12-30
**/
@Slf4j
public class Sequence {
/**
* 初始偏移时间戳
*/
private final long OFFSET = 1546300800L;
/**
* 机器id (0~15 保留 16~31作为备份机器)
*/
private long WORKER_ID;
/**
* 机器id所占位数 (5bit, 支持最大机器数 2^5 = 32)
*/
private final long WORKER_ID_BITS = 5L;
/**
* 自增序列所占位数 (16bit, 支持最大每秒生成 2^16 = 65536)
*/
private final long SEQUENCE_ID_BITS = 16L;
/**
* 机器id偏移位数
*/
private final long WORKER_SHIFT_BITS = SEQUENCE_ID_BITS;
/**
* 自增序列偏移位数
*/
private final long OFFSET_SHIFT_BITS = SEQUENCE_ID_BITS + WORKER_ID_BITS;
/**
* 机器标识最大值 (2^5 / 2 - 1 = 15)
*/
private final long WORKER_ID_MAX = ((1 << WORKER_ID_BITS) - 1) >> 1;
/**
* 备份机器ID开始位置 (2^5 / 2 = 16)
*/
private final long BACK_WORKER_ID_BEGIN = (1 << WORKER_ID_BITS) >> 1;
/**
* 自增序列最大值 (2^16 - 1 = 65535)
*/
private final long SEQUENCE_MAX = (1 << SEQUENCE_ID_BITS) - 1;
/**
* 发生时间回拨时容忍的最大回拨时间 ()
*/
private final long BACK_TIME_MAX = 1L;
/**
* 上次生成ID的时间戳 ()
*/
private long lastTimestamp = 0L;
/**
* 当前秒内序列 (2^16)
*/
private long sequence = 0L;
/**
* 备份机器上次生成ID的时间戳 ()
*/
private long lastTimestampBak = 0L;
/**
* 备份机器当前秒内序列 (2^16)
*/
private long sequenceBak = 0L;
public static int WORK_ID = 0;
/**
* 使用ip第三位作为工作线程ID
*/
static {
try {
InetAddress inetAddress = InetAddress.getLocalHost();
String last = inetAddress.getHostAddress().split("\\.")[3];
WORK_ID = Integer.valueOf(last) % 15;
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 私有构造函数禁止外部访问
*/
public Sequence() {
WORKER_ID = WORK_ID;
}
public Sequence(long workerId, long datacenterId) {
WORKER_ID = WORK_ID;
}
/**
* 获取自增序列
*
* @return long
*/
public long nextId() {
return nextId(SystemClock.now() / 1000);
}
/**
* 主机器自增序列
*
* @param timestamp 当前Unix时间戳
* @return long
*/
private synchronized long nextId(long timestamp) {
// 时钟回拨检查
if (timestamp < lastTimestamp) {
// 发生时钟回拨
log.warn("时钟回拨, 启用备份机器ID: now: [{}] last: [{}]", timestamp, lastTimestamp);
return nextIdBackup(timestamp);
}
// 开始下一秒
if (timestamp != lastTimestamp) {
lastTimestamp = timestamp;
sequence = 0L;
}
if (0L == (++sequence & SEQUENCE_MAX)) {
// 秒内序列用尽
log.warn("秒内[{}]序列用尽, 启用备份机器ID序列", timestamp);
sequence--;
return nextIdBackup(timestamp);
}
return ((timestamp - OFFSET) << OFFSET_SHIFT_BITS) | (WORKER_ID << WORKER_SHIFT_BITS) | sequence;
}
/**
* 备份机器自增序列
*
* @param timestamp timestamp 当前Unix时间戳
* @return long
*/
private long nextIdBackup(long timestamp) {
if (timestamp < lastTimestampBak) {
if (lastTimestampBak - SystemClock.now() / 1000 <= BACK_TIME_MAX) {
timestamp = lastTimestampBak;
} else {
throw new RuntimeException(String.format("时钟回拨: now: [%d] last: [%d]", timestamp, lastTimestampBak));
}
}
if (timestamp != lastTimestampBak) {
lastTimestampBak = timestamp;
sequenceBak = 0L;
}
if (0L == (++sequenceBak & SEQUENCE_MAX)) {
// 秒内序列用尽
log.warn("秒内[{}]序列用尽, 备份机器ID借取下一秒序列", timestamp);
return nextIdBackup(timestamp + 1);
}
return ((timestamp - OFFSET) << OFFSET_SHIFT_BITS) | ((WORKER_ID ^ BACK_WORKER_ID_BEGIN) << WORKER_SHIFT_BITS) | sequenceBak;
}
}

View File

@ -0,0 +1,20 @@
package com.sonic.bear;
import com.sonic.sdk.api.annotation.EnableGatWayAuthScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* @author coder
*/
@EnableSwagger2
@ComponentScan(value = {"com.sonic"})
@SpringBootApplication
@EnableGatWayAuthScan(basePackages = "com.sonic.bear.controller")
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}

View File

@ -0,0 +1,48 @@
package com.sonic.bear.config;
import com.sonic.bear.common.GlobalConfig;
import com.sonic.bear.common.MybatisPlusConfig;
import com.sonic.common.AppRuntime;
import com.sonic.common.config.DefaultWebMvcConfig;
import com.sonic.common.exception.AbstractExceptionHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Profile;
import java.util.function.Consumer;
/**
* Server服务唯一的配置入口
* TODO: Import的各种Config按需使用例如如果不需要mysqlkafka消息redis等可以去掉对应的Config
*
* @author coder
*/
@Slf4j
@Configuration
@Import({GlobalConfig.class, DefaultWebMvcConfig.class, MybatisPlusConfig.class, RedisConfig.class,
EventConfig.class, SwaggerConfig.class})
public class Config {
/**
* XXX: ExceptionHandlerConfig 应该在 DefaultWebMvcConfig 前被初始. 否则 DefaultWebMvcConfig exceptionHandlerHook 会生效
*/
static class ExceptionHandlerConfig {
@Bean
@Profile({"!unittest && !local"})
AbstractExceptionHandler.ExceptionHandlerHook exceptionHandlerHook(AppRuntime appRuntime) {
return new AbstractExceptionHandler.ExceptionHandlerHook() {
@Override
public String getAppId() {
return appRuntime.getAppId();
}
@Override
public Consumer<AbstractExceptionHandler.ExceptionContext> getExceptionConsumer() {
return context -> log.error("未捕获内部异常, logText: {}", context.dumpRequest(), context.getException());
}
};
}
}
}

View File

@ -0,0 +1,147 @@
package com.sonic.bear.config;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.ImmutableMap;
import com.rabbitmq.client.Channel;
import com.sonic.common.AppRuntime;
import com.sonic.common.event.*;
import com.sonic.common.utils.LogUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.annotation.RabbitListeners;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.task.TaskExecutor;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.Payload;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
* @author coder
*/
@Slf4j
public class EventConfig {
/** TODO: 定义 Event.BuildInScene */
public final static String DEFAULT_SCENE = Event.BuildInScene.SONIC.getCode();
/** TODO: DEFAULT_SCENE + "_" + appName */
public final static String DEFAULT_MODULE = DEFAULT_SCENE + "_" + "sonic";
@Value("${mq.exchange}")
private String mqExchange;
@Value("${mq.default.queue}")
private String defaultQueue;
@Value("${mq.default.routing-key}")
private String defaultRoutingKey;
@Value("${mq.user-created.queue}")
private String userCreatedQueue;
@Value("${mq.user-created.routing-key}")
private String userCreatedRoutingKey;
@Configuration
@Profile("!unittest")
static class Listener {
@Autowired
EventConsumer eventConsumer;
@RabbitListeners({
@RabbitListener(queues = {"${mq.default.queue}"}, concurrency = "2"),
@RabbitListener(queues = {"${mq.user-created.queue}"}, concurrency = "2"),
})
public void process(@Payload Message message, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag, Channel channel) {
try {
LogUtils.setTraceId();
byte[] msgBody = message.getBody();
String contentEncoding = message.getMessageProperties().getContentEncoding();
String bodyStr = new String(msgBody, Charset.forName(ObjectUtils.defaultIfNull(contentEncoding, "UTF-8")));
MessageProperties pro = message.getMessageProperties();
if (log.isDebugEnabled()) {
log.info("receive msg routingKey:{}->consumerQueue:{}->redelivered:{}->body:{}",
pro.getReceivedRoutingKey(), pro.getConsumerQueue(), pro.getRedelivered(), bodyStr);
}
eventConsumer.onEvent(bodyStr, ImmutableMap.of("record", JSON.toJSONString(message)));
//消息确认multiple是否批量.true:将一次性ack所有小于deliveryTag的消息
channel.basicAck(deliveryTag, false);
} catch (Exception e) {
log.error("===> mq handler error, message: {}", JSON.toJSONString(message), e);
//消息拒绝//requeue=true,表示将消息重新放入到队列中false表示直接从队列中删除此时和basicAck(long deliveryTag, false)的效果一样
try {
channel.basicReject(deliveryTag,true);
} catch (IOException ioException) {
log.error("channel.basicReject error. message: {}, deliveryTag: {}", JSON.toJSONString(message), deliveryTag, ioException);
}
} finally {
LogUtils.removeTraceId();
}
}
}
@Bean
EventProducer eventProducer(AppRuntime appRuntime, RabbitTemplate rabbitTemplate, TaskExecutor taskExecutor) {
BiConsumer<Event, RabbitmqEventProducer.RabbitmqMessageMeta> callback = (event, meta) -> {};
return new RabbitmqEventProducer(rabbitTemplate, DEFAULT_MODULE, DEFAULT_SCENE, appRuntime.getAppId(),
RabbitmqEventProducer.RabbitmqMessageMeta.build(mqExchange, defaultRoutingKey), taskExecutor, callback);
}
@Bean
EventConsumer eventConsumer(AppRuntime appRuntime, EventHandlerRepository eventHandlerRepository) {
Consumer<EventConsumer.EventWrapper> callback = (eventWrapper) -> {};
return new DefaultEventConsumer(appRuntime.getAppName(), eventHandlerRepository, callback);
}
@Bean
EventHandlerRepository eventHandlerRepository() {
return new EventHandlerRepository((ex, logText) -> log.error("MQ,handle error {}", logText, ex));
}
@Bean
public DirectExchange messageServerExchange(){
return new DirectExchange(mqExchange);
}
@Bean
RabbitmqEventProducer.RabbitmqMessageMeta userCreatedMeta() {
return RabbitmqEventProducer.RabbitmqMessageMeta.build(mqExchange, userCreatedRoutingKey);
}
@Bean
public Binding bindingDefaultQueueExchange(DirectExchange exchange, Queue defaultQueue) {
return bindingExchange(exchange, defaultQueue, defaultRoutingKey);
}
@Bean
public Binding bindingUserCreatedQueueExchange(DirectExchange exchange, Queue userCreatedQueue) {
return bindingExchange(exchange, userCreatedQueue, userCreatedRoutingKey);
}
@Bean
public Queue defaultQueue() {
return new Queue(defaultQueue,true);
}
@Bean
public Queue userCreatedQueue() {
return new Queue(userCreatedQueue,true);
}
public Binding bindingExchange(DirectExchange exchange, Queue queueMessage, String routingKey) {
return BindingBuilder.bind(queueMessage).to(exchange).with(routingKey);
}
}

View File

@ -0,0 +1,39 @@
package com.sonic.bear.config;
import com.sonic.common.utils.RedisLock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.concurrent.TimeUnit;
/**
* 有关Redis的配置
*
* TODO: 如果不需要Redis, 可以删除该文件并且在{@link Config}@Import的注解中移除对RedisConfig的引用
*/
@Slf4j
public class RedisConfig {
/**
* redisWrapper用于分布式锁RedisLock
*
* @param redisTemplate
* @return
*/
@Bean
RedisLock.RedisWrapper redisWrapper(RedisTemplate redisTemplate) {
return new RedisLock.RedisWrapper() {
@Override
public void delete(String key) {
redisTemplate.delete(key);
}
@Override
public boolean lock(String key, String value, long expireMills) {
return redisTemplate.opsForValue().setIfAbsent(key, value, expireMills, TimeUnit.MILLISECONDS);
}
};
}
}

View File

@ -0,0 +1,52 @@
package com.sonic.bear.config;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RedissonConfig {
/**
* 配置 RedissonClient支持单机和集群模式
*/
@Bean
public RedissonClient redissonClient(RedisProperties redisProperties) {
Config config = new Config();
// 设置 Netty 线程数使用默认值 0Redisson 默认使用 CPU 核心数的两倍
config.setNettyThreads(0);
// 判断是否配置了集群节点
if (redisProperties.getCluster() != null && redisProperties.getCluster().getNodes() != null
&& !redisProperties.getCluster().getNodes().isEmpty()) {
// 集群模式配置
config.useClusterServers()
.setScanInterval(2000) // 集群状态扫描间隔单位毫秒
.addNodeAddress(redisProperties.getCluster().getNodes().stream()
.map(node -> "redis://" + node)
.toArray(String[]::new))
.setPassword(StringUtils.isNotBlank(redisProperties.getPassword())
? redisProperties.getPassword() : null);
} else {
// 单机模式配置
config.useSingleServer()
.setAddress("redis://" + redisProperties.getHost() + ":" + redisProperties.getPort())
.setPassword(StringUtils.isNotBlank(redisProperties.getPassword())
? redisProperties.getPassword() : null)
.setDatabase(redisProperties.getDatabase());
}
// 如果启用了 SSL设置 SSL 相关配置
if (redisProperties.isSsl()) {
config.useSingleServer().setSslEnableEndpointIdentification(false); // 禁用主机名验证
// 集群模式下Redisson 会自动应用 SSL 配置
}
return Redisson.create(config);
}
}

View File

@ -0,0 +1,24 @@
package com.sonic.bear.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory){
return new RestTemplate(factory);
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory(){
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setReadTimeout(5000);//单位为ms
factory.setConnectTimeout(5000);//单位为ms
return factory;
}
}

View File

@ -0,0 +1,43 @@
package com.sonic.bear.config;
import com.sonic.common.rpc.ApiResultCode;
import lombok.Getter;
/**
* @author coder
*/
@Getter
public enum ResultCode implements ApiResultCode {
/**
* TODO: 可以在此处扩展服务自身需要用到的错误码信息
*/
BIZ_ERROR1("0000", "业务异常1"),
DEMO_CREATED_FAIL("0001", "新增Demo实体失败");
private final String errorCode;
private final String errorMsg;
ResultCode(String errorCode, String errorMsg) {
this.errorCode = getAppId() + errorCode;
this.errorMsg = errorMsg;
}
/**
* 得到 message 的值.
*
* @return 属性 message 的值.
*/
@Override
public String getErrorMsg() {
return errorMsg + "(" + getAppId() + ")";
}
/**
* @return 当前服务的AppId
*/
@Override
public String getAppId() {
return "1001";
}
}

View File

@ -0,0 +1,110 @@
package com.sonic.bear.config;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
/**
* swagger配置
* @author code
*/
@Slf4j
@EnableSwagger2
public class SwaggerConfig {
private static final String SPLIT = ",";
@Value("${swagger.enabled:false}")
private Boolean swaggerEnabled;
@Value("${swagger.base.package}")
private String basePackageValue;
private final String DEFAULT_BASE_PACKAGE = "com.sonic.bear.controller";
public static Predicate<RequestHandler> basePackage(final String basePackage) {
return input -> declaringClass(input).transform(handlerPackage(basePackage)).or(true);
}
private static Optional<? extends Class<?>> declaringClass(RequestHandler input) {
return Optional.fromNullable(input.declaringClass());
}
private static Function<Class<?>, Boolean> handlerPackage(final String basePackage) {
return input -> {
// 循环判断匹配
for (String strPackage : basePackage.split(SPLIT)) {
boolean isMatch = input.getPackage().getName().startsWith(strPackage);
if (isMatch) {
return true;
}
}
return false;
};
}
@Bean
public Docket createRestApi() {
basePackageValue = null == basePackageValue || ("").equals(basePackageValue) ? DEFAULT_BASE_PACKAGE : basePackageValue;
log.info("===> swagger base package : ", basePackageValue);
//在配置好的配置类中增加此段代码即可
ParameterBuilder ticketPar = new ParameterBuilder();
List<Parameter> pars = new ArrayList<Parameter>();
ticketPar.name("_tk_")
//name表示名称description表示描述
.description("token")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(false)
.defaultValue("")
.build();//required表示是否必填defaultvalue表示默认值
//添加完此处一定要把下边的带***的也加上否则不生效
pars.add(ticketPar.build());
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(swaggerEnabled)
.select()
.apis(basePackage(basePackageValue))
.paths(PathSelectors.any())
.build()
//************把消息头添加
.globalOperationParameters(pars);
}
/**
* TODO: 更改文案配置
* @return
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("sonic-bear")
.description("sonic-bear API")
.version("1.0")
.contact(new Contact("sonic-bear", "", "admin.sonic-bear.com"))
.build();
}
}

View File

@ -0,0 +1,66 @@
package com.sonic.bear.controller.api;
import com.sonic.bear.domain.input.KickOutInput;
import com.sonic.bear.domain.input.LogoutInput;
import com.sonic.bear.domain.input.ThirdUserLoginInput;
import com.sonic.bear.service.UserService;
import com.sonic.bear.service.UserSessionService;
import com.sonic.common.auth.domains.Session;
import com.sonic.common.rpc.Result;
import com.sonic.sdk.api.annotation.InternalRpc;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
/**
* API-账号登录管理
* @author coder
*/
@InternalRpc(path = "/api/**")
@Validated
@RestController
public class UserLoginApi {
@Autowired
private UserService userService;
@Autowired
private UserSessionService userSessionService;
@ApiOperation(value = "三方账号登录", tags = {"API-账号登录"})
@PostMapping("/api/auth/third-user-login")
public Result<Session> thirdUserLogin(@RequestBody @Valid ThirdUserLoginInput input) {
//执行账号验证操作
Long userId = userService.thirdLoginOrRegister(input.getThirdType(), input.getOpenId(), input.getEmail(), input.getNickname());
//执行登录操作
UserSessionService.CreateSessionReq req = UserSessionService.CreateSessionReq.builder()
.userId(userId)
.clientCode(input.getClientCode())
.loginType(input.getThirdType().name())
.deviceId(input.getDeviceId())
.ip(input.getIp())
.userAgent(input.getUserAgent())
.build();
Session session = userSessionService.createSession(req);
return Result.success(session);
}
@ApiOperation(value = "退出登录", tags = {"API-账号登录"})
@PostMapping("/api/auth/logout")
public Result<Void> logout(@RequestBody @Valid LogoutInput input) {
userSessionService.logout(input);
return Result.success();
}
@ApiOperation(value = "踢下线", tags = {"API-账号登录"})
@PostMapping("/api/auth/kick-out")
public Result<Void> kickOut(@RequestBody @Valid KickOutInput input) {
userSessionService.kickOut(input);
return Result.success();
}
}

View File

@ -0,0 +1,49 @@
package com.sonic.bear.controller.api;
import com.sonic.bear.domain.input.IdInput;
import com.sonic.bear.domain.output.NicknameOutput;
import com.sonic.bear.lib.input.UserNicknameExistCheckInput;
import com.sonic.bear.lib.input.UserNicknameSyncPoolInput;
import com.sonic.bear.service.UserNicknamePoolService;
import com.sonic.common.rpc.Result;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.List;
/**
* API-用户昵称
* @author coder
*/
@Validated
@RestController
public class UserNicknamePoolApi {
@Autowired
private UserNicknamePoolService userNicknamePoolService;
@ApiOperation(value = "同步昵称数据到数据池", tags = {"API-用户昵称"})
@PostMapping("/api/user-nickname/sync-pool")
public Result<Void> syncPool(@RequestBody @Valid UserNicknameSyncPoolInput input) {
userNicknamePoolService.syncPool(input.getUserId(), input.getUserType(), input.getNickname(), input.getOptType().name());
return Result.success();
}
@ApiOperation(value = "批量查询昵称", tags = {"API-用户昵称"})
@PostMapping("/api/user-nickname/batch-get")
public Result<List<NicknameOutput>> batchGetNickname(@RequestBody @Valid IdInput input) {
return Result.success(userNicknamePoolService.batchGetNickname(input.getUserIdList()));
}
@ApiOperation(value = "查询用户昵称是否存在", tags = {"API-用户昵称"})
@PostMapping("/api/user-nickname/exist-check")
public Result<Boolean> existCheck(@RequestBody @Valid UserNicknameExistCheckInput input) {
return Result.success(userNicknamePoolService.existCheck(input.getExUserId(), input.getNickname()));
}
}

View File

@ -0,0 +1,43 @@
package com.sonic.bear.controller.api;
import com.sonic.bear.lib.input.UserIdInput;
import com.sonic.bear.lib.input.UserIdListInput;
import com.sonic.bear.lib.output.BaseUserInfoOutput;
import com.sonic.bear.lib.output.UserInfoListOutput;
import com.sonic.bear.service.UserSearchService;
import com.sonic.common.rpc.Result;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.List;
/**
* API-用户查询
* @author coder
*/
@Validated
@RestController
public class UserSearchApi {
@Autowired
private UserSearchService userSearchService;
@ApiOperation(value = "批量获取用户基础信息", tags = {"API-用户查询"})
@PostMapping("/api/user/batch-get-info")
public Result<List<UserInfoListOutput>> batchGetUserInfo(@RequestBody @Valid UserIdListInput input) {
return Result.success(userSearchService.batchGetUserInfo(input.getUserIdList()));
}
@ApiOperation(value = "获取用户自己的基础信息", tags = {"API-用户查询"})
@PostMapping("/api/user/base-user-info")
public Result<BaseUserInfoOutput> baseUserInfo(@RequestBody @Valid UserIdInput input) {
return Result.success(userSearchService.baseUserInfo(input.getUserId()));
}
}

View File

@ -0,0 +1,35 @@
package com.sonic.bear.controller.api;
import com.sonic.bear.domain.input.TouchSessionInput;
import com.sonic.bear.service.UserSessionService;
import com.sonic.common.auth.domains.Session;
import com.sonic.common.log.IgnoreRequestLog;
import com.sonic.common.rpc.Result;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
/**
* API-Session获取
* @author coder
*/
@Validated
@RestController
public class UserSessionApi {
@Autowired
private UserSessionService userSessionService;
@ApiOperation(value = "token验证", tags = {"API-Session获取"})
@PostMapping("/api/auth/touch-session")
@IgnoreRequestLog(types = IgnoreRequestLog.IgnoreType.SILENT)
public Result<Session> touchSession(@RequestBody @Valid TouchSessionInput input) {
return Result.success(userSessionService.touchSession(input.getToken()));
}
}

View File

@ -0,0 +1,51 @@
package com.sonic.bear.controller.api;
import com.sonic.bear.lib.input.CompleteUserInfoInput;
import com.sonic.bear.lib.input.EditUserInfoInput;
import com.sonic.bear.lib.input.UserIdInput;
import com.sonic.bear.service.UserSetService;
import com.sonic.common.rpc.Result;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
/**
* API-用户设置
* @author coder
*/
@Validated
@RestController
public class UserSetApi {
@Autowired
private UserSetService userSetService;
@ApiOperation(value = "补全用户基础信息", tags = {"API-用户设置"})
@PostMapping("/api/user/complete-user-info")
public Result<Void> completeUserInfo(@RequestBody @Valid CompleteUserInfoInput input) {
userSetService.completeUserInfo(input);
return Result.success();
}
@ApiOperation(value = "编辑用户基础信息", tags = {"API-用户设置"})
@PostMapping("/api/user/edit-user-info")
public Result<Void> editUserInfo(@RequestBody @Valid EditUserInfoInput input) {
userSetService.editUserInfo(input);
return Result.success();
}
@ApiOperation(value = "删除账号", tags = {"API-用户设置"})
@PostMapping("/api/user/del-account")
public Result<Void> delAccount(@RequestBody @Valid UserIdInput input) {
userSetService.delAccount(input.getUserId());
return Result.success();
}
}

View File

@ -0,0 +1,74 @@
package com.sonic.bear.controller.mock;
import com.sonic.common.AppRuntime;
import com.sonic.common.auth.domains.Session;
import com.sonic.common.rpc.Result;
import com.sonic.common.utils.RedisLock;
import com.sonic.sdk.api.annotation.IgnoreAuth;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
/**
* @author: code
* @date: 2025/06/01
* @Description:
* @version: 1.0.0
*/
@Slf4j
@RestController
public class MockController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private RedisLock.RedisWrapper redisWrapper;
@Autowired
private AppRuntime appRuntime;
@Autowired
private RedisTemplate redisTemplate;
@IgnoreAuth
@ApiOperation(value = "redis连接测试", tags = "lion-冒烟")
@PostMapping("/mock/redis/test/set-get")
public Result<String> testSetGet(Session session) {
String key = "aaa:111";
stringRedisTemplate.opsForValue().set(key, "上海", 3, TimeUnit.MINUTES);
return Result.success(stringRedisTemplate.opsForValue().get(key));
}
@IgnoreAuth
@ApiOperation(value = "redis连接测试", tags = "lion-冒烟")
@PostMapping("/mock/redis/test/exp")
public Result<String> exp(Session session) {
String key = "aaa:111";
return Result.success(stringRedisTemplate.getExpire(key));
}
@IgnoreAuth
@ApiOperation(value = "redis加锁测试", tags = "lion-冒烟")
@PostMapping("/mock/redis/test/lock-v1")
public Result<String> lockV1(Session session) {
String key = "aaa:333";
//加锁避免定时任务并发重复处理
RedisLock redisLock = new RedisLock(key, redisWrapper);
redisLock.tryAcquireRun(10 * 60 * 1000, () -> {
System.out.println("=====> redis加锁测试 通过");
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return true;
});
return Result.success("V1 成功");
}
}

View File

@ -0,0 +1,23 @@
package com.sonic.bear.controller.probe;
import com.sonic.common.auth.IgnoreAuth;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 探测接口
*/
@RestController
@Slf4j
public class ProbeController {
@IgnoreAuth
@ApiOperation(value = "检测服务存活状态", tags = {"探针"})
@PostMapping("/probe/check")
public void probeCheck() {
}
}

View File

@ -0,0 +1,10 @@
package com.sonic.bear.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sonic.bear.domain.entity.AppClient;
/**
* @author coder
*/
public interface AppClientDao extends BaseMapper<AppClient> {
}

View File

@ -0,0 +1,11 @@
package com.sonic.bear.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sonic.bear.domain.entity.UserAccessLog;
/**
* @author coder
*/
public interface UserAccessLogDao extends BaseMapper<UserAccessLog> {
}

View File

@ -0,0 +1,11 @@
package com.sonic.bear.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sonic.bear.domain.entity.UserCommonlyUsedLog;
/**
* @author coder
*/
public interface UserCommonlyUsedLogDao extends BaseMapper<UserCommonlyUsedLog> {
}

View File

@ -0,0 +1,9 @@
package com.sonic.bear.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sonic.bear.domain.entity.User;
public interface UserDao extends BaseMapper<User> {
}

View File

@ -0,0 +1,9 @@
package com.sonic.bear.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sonic.bear.domain.entity.UserNicknamePool;
public interface UserNicknamePoolDao extends BaseMapper<UserNicknamePool> {
}

View File

@ -0,0 +1,19 @@
package com.sonic.bear.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sonic.bear.domain.entity.UserSession;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
/**
* @author coder
*/
public interface UserSessionDao extends BaseMapper<UserSession> {
@Update("update user_session set status = 'EXPIRED' where user_id = #{userId} AND client_code= #{clientCode} AND status= 'ENABLED' ")
int expiredSession(@Param("userId") Long userId, @Param("clientCode") String clientCode);
@Update("update user_session set status = 'EXPIRED' where user_id = #{userId} AND status= 'ENABLED'")
int expiredSessionV2(@Param("userId") Long userId);
}

View File

@ -0,0 +1,9 @@
package com.sonic.bear.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sonic.bear.domain.entity.UserThird;
public interface UserThirdDao extends BaseMapper<UserThird> {
}

View File

@ -0,0 +1,51 @@
package com.sonic.bear.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.sonic.bear.domain.typehandler.AccountTypeSetTypeHandler;
import com.sonic.bear.enums.AccountType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.Set;
/**
* @author coder
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName(value = "app_client", autoResultMap = true)
public class AppClient {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/** 编码 */
private String code;
/** 描述 */
@TableField("`desc`")
private String desc;
/** 允许登陆的账号类型 */
@TableField(typeHandler = AccountTypeSetTypeHandler.class)
private Set<AccountType> allowAccountTypes;
/** session过期时间,单位为分钟 */
private Integer sessionAlive;
/** session存活时间, session 在未过期的情况,最大可以续存时间,单位为分钟 */
private Integer sessionExpired;
/** 创建时间 */
private LocalDateTime createTime;
/** 更新时间 */
private LocalDateTime editTime;
public boolean allowUser(AccountType accountType) {
if (this.getAllowAccountTypes().isEmpty()) {
return false;
}
return this.getAllowAccountTypes().stream().anyMatch(type -> type == accountType);
}
}

View File

@ -0,0 +1,106 @@
package com.sonic.bear.domain.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* 用户实体类
*
* @author coder
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("user")
public class User {
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 用户ID至少15位
*/
@TableField("user_id")
private Long userId;
/**
* ID编号
*/
@TableField("id_card")
private String idCard;
/**
* 头像
*/
@TableField("head_image")
private String headImage;
/**
* 昵称
*/
@TableField("nickname")
private String nickname;
/**
* 生日
*/
@TableField("birthday")
private LocalDateTime birthday;
/**
* 性别0 1 2 未知
*/
@TableField("sex")
private Integer sex;
/**
* 账户类型1 客户2 后台
*/
@TableField("account_type")
private Integer accountType;
/**
* 账户状态0 正常1 禁用2 锁定
*/
@TableField("account_status")
private Integer accountStatus;
/**
* 最后访问时间
*/
@TableField("last_access_time")
private LocalDateTime lastAccessTime;
/**
* 是否删除0 1
*/
@TableField("is_delete")
private Boolean isDelete;
/**
* 创建时间
*/
@TableField("create_time")
private LocalDateTime createTime;
/**
* 编辑人
*/
@TableField("edit_id")
private Long editId;
/**
* 编辑时间
*/
@TableField(value = "edit_time", update = "NOW()")
private LocalDateTime editTime;
}

View File

@ -0,0 +1,37 @@
package com.sonic.bear.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* @author coder
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@TableName(value = "user_access_log", autoResultMap = true)
public class UserAccessLog {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/** 账号 Id */
private Long userId;
/**客户端类型*/
private String clientCode;
/** 访问时间 */
private LocalDateTime accessTime;
}

View File

@ -0,0 +1,61 @@
package com.sonic.bear.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* @Author code
* @Description 用户常用日志
* @Date 2024/1/12 11:57
* @Version 1.0
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@TableName(value = "user_commonly_used_log", autoResultMap = true)
public class UserCommonlyUsedLog {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 用户ID
*/
private Long userId;
/**
* 数据类型1 IP地址2 设备好
*/
private Integer dataType;
/**
* 数据值
*/
private String dataValue;
/**
* 使用次数
*/
private Integer useCount;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime editTime;
}

View File

@ -0,0 +1,35 @@
package com.sonic.bear.domain.entity;
import com.sonic.bear.enums.AccountType;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class UserInfoBo {
/**
* 用户ID
*/
private Long userId;
/**
* 账号类型
*/
private Integer accountType;
/**
* 账号状态
*/
private Integer accountStatus;
/**
* 账号注册时间
*/
private LocalDateTime createTime;
public AccountType getAccountTypeEnum() {
return AccountType.from(this.accountType);
}
}

View File

@ -0,0 +1,56 @@
package com.sonic.bear.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* 用户昵称数据池实体类
*
* @author coder
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("user_nickname_pool")
public class UserNicknamePool {
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 用户ID普通用户IDAI用户ID
*/
@TableField("user_id")
private Long userId;
/**
* 用户类型1 USER2 AI
*/
@TableField("user_type")
private Integer userType;
/**
* 昵称
*/
@TableField("nickname")
private String nickname;
/**
* 创建时间
*/
@TableField("create_time")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,116 @@
package com.sonic.bear.domain.entity;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.annotation.JSONField;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
import com.google.common.base.Strings;
import com.sonic.bear.enums.AccountType;
import com.sonic.common.auth.domains.SessionStatusEnum;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* @author coder
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@TableName(value = "user_session", autoResultMap = true)
public class UserSession {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/** 登陆记号 */
private String token;
/** 账号 Id */
private Long userId;
/** 登陆的账号类型 */
@TableField(typeHandler = FastjsonTypeHandler.class)
private AccountType accountType;
/** 客户端编码 */
private String clientCode;
/***
* 登录类型账号密码三方手机号验证码
*/
private String loginType;
/** session 状态,默认为有效 */
private SessionStatusEnum status;
/**
* 客户端设备基础信息
*/
private String userAgent;
/** 扩展数据json字符串 */
private String extra;
/**
* 登录的设备ID
*/
private String deviceId;
/**
* 登录的IP地址
*/
private String ip;
/** session 创建时间 */
private LocalDateTime createTime;
/** session 最后一次访问时间 */
private LocalDateTime lastAccessTime;
/** session 过期/结束时间 */
private LocalDateTime expireTime;
@JSONField(serialize = false)
public boolean isEnabled() {
return status == SessionStatusEnum.ENABLED;
}
@JSONField(serialize = false)
public boolean isDisabled() {
return status != SessionStatusEnum.ENABLED;
}
@JSONField(serialize = false)
public boolean isExpired() {
return expireTime != null && LocalDateTime.now().isAfter(expireTime);
}
/**
* 是否超过最大存活期内
* @param sessionAlive 最大存活时间单位分钟 client 定义
* @return 是否在最大存活时间内
*/
@JSONField(serialize = false)
public boolean isOverMaxAlive(Integer sessionAlive) {
LocalDateTime maxAliveTime = createTime.plusMinutes(sessionAlive);
return LocalDateTime.now().isAfter(maxAliveTime);
}
/** 将extra转为对象 */
public Extra normalizedExtra() {
return JSON.parseObject(extra, Extra.class);
}
public static Extra parseExtraObject(String text) {
if (Strings.isNullOrEmpty(text)) {
return new Extra();
}
return JSON.parseObject(text, Extra.class);
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class Extra {
private String email;
}
}

View File

@ -0,0 +1,82 @@
package com.sonic.bear.domain.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* 用户三方信息实体类
*
* @author coder
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("user_third")
public class UserThird {
/**
* 主键ID
*/
@TableId(value = "id", type = IdType.NONE)
private Long id;
/**
* 用户ID
*/
@TableField("user_id")
private Long userId;
/**
* 三方类型DISCORDGOOGLEAPPLE详见ThirdTypeEnum
*/
@TableField("third_type")
private String thirdType;
/**
* 三方ID
*/
@TableField("open_id")
private String openId;
/**
* 昵称
*/
@TableField("nickname")
private String nickname;
/**
* 邮箱
*/
@TableField("email")
private String email;
/**
* 是否删除0 1
*/
@TableField("is_delete")
private Boolean isDelete;
/**
* 创建时间
*/
@TableField("create_time")
private LocalDateTime createTime;
/**
* 编辑人
*/
@TableField("edit_id")
private Long editId;
/**
* 编辑时间
*/
@TableField("edit_time")
private LocalDateTime editTime;
}

View File

@ -0,0 +1,24 @@
package com.sonic.bear.domain.input;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @Author code
* @Description TODO
* @Date 2022/11/14 20:19
* @Version 1.0
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class IdInput {
List<Long> userIdList;
}

View File

@ -0,0 +1,24 @@
package com.sonic.bear.domain.input;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @Author code
* @Description TODO
* @Date 2022/11/14 20:19
* @Version 1.0
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class InitExistCacheInput {
List<Long> idList;
}

View File

@ -0,0 +1,14 @@
package com.sonic.bear.domain.input;
import io.swagger.annotations.ApiParam;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Data
public class KickOutInput {
@ApiParam("用户ID")
@NotNull
private Long userId;
}

View File

@ -0,0 +1,15 @@
package com.sonic.bear.domain.input;
import io.swagger.annotations.ApiParam;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Data
public class LogoutInput {
@ApiParam("登录授权码")
@NotEmpty
private String token;
}

View File

@ -0,0 +1,40 @@
package com.sonic.bear.domain.input;
import com.sonic.bear.enums.ThirdTypeEnum;
import io.swagger.annotations.ApiParam;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@Data
public class ThirdUserLoginInput {
@ApiParam("三方ID")
@NotBlank
private String openId;
@ApiParam("三方邮箱")
private String email;
@ApiParam("三方昵称")
private String nickname;
@ApiParam("三方类型")
@NotNull
private ThirdTypeEnum thirdType;
@ApiParam("客户端标识")
@NotBlank
private String clientCode;
@ApiParam("设备ID")
private String deviceId;
@ApiParam("IP地址")
private String ip;
@ApiParam("UA")
private String userAgent;
}

View File

@ -0,0 +1,13 @@
package com.sonic.bear.domain.input;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
public class TouchSessionInput {
@NotBlank(message = "token not null")
private String token;
}

View File

@ -0,0 +1,20 @@
package com.sonic.bear.domain.input;
import lombok.Data;
import java.util.List;
/**
* @Author code
* @Description TODO
* @Date 2024/1/12 19:25
* @Version 1.0
*/
@Data
public class UserCommonlyUsedLogInput {
List<Long> userIdList;
List<String> dataValueList;
}

View File

@ -0,0 +1,30 @@
package com.sonic.bear.domain.output;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Author code
* @Description 用户昵称
* @Date 2024/1/12 11:57
* @Version 1.0
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class NicknameOutput {
/**
* 用户ID
*/
private Long userId;
/**
* 昵称
*/
private String nickname;
}

View File

@ -0,0 +1,35 @@
package com.sonic.bear.domain.output;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Author code
* @Description 用户常用日志
* @Date 2024/1/12 11:57
* @Version 1.0
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserCommonlyUsedLogOutput {
/**
* 用户ID
*/
private Long userId;
/**
* 数据类型1 IP地址2 设备好
*/
private Integer dataType;
/**
* 数据值
*/
private String dataValue;
}

View File

@ -0,0 +1,13 @@
package com.sonic.bear.domain.typehandler;
import com.sonic.daosupport.mysql.typehandler.BaseSetTypeHandler;
import com.sonic.bear.enums.AccountType;
/**
* fastjsonTypeHandler 解析 Set<XXX> 不会解析泛型
* @author coder
* @date 2020-04-15
*/
public class AccountTypeSetTypeHandler extends BaseSetTypeHandler<AccountType> {
}

View File

@ -0,0 +1,36 @@
package com.sonic.bear.enums;
import lombok.Getter;
/**
* 账号类型
* @author coder
*/
@Getter
public enum AccountType {
/**
* 客户用户
*/
CUSTOMER(1),
/**
* 后台账号
*/
SYSTEM(2),
;
private final Integer code;
AccountType(Integer code) {
this.code = code;
}
public static AccountType from(Integer code) {
for (AccountType status : AccountType.values()) {
if (code.equals(status.code)) {
return status;
}
}
return null;
}
}

View File

@ -0,0 +1,31 @@
package com.sonic.bear.enums;
import lombok.Getter;
/**
* 应用端编码数据来自 app_client.code
* @author coder
*/
@Getter
public enum AppClientType {
/**
* web
*/
WEB,
/**
* 管理后台
*/
ADMIN,
/**
* ios
*/
IOS,
/**
* android
*/
ANDROID,
;
}

View File

@ -0,0 +1,34 @@
package com.sonic.bear.enums;
import lombok.Getter;
/**
* 降级的业务类型
* @Author code
* @Date 2022/1/12
* @Version 1.0
*/
@Getter
public enum FallBackBizTypeEnum {
/** SSO服务、3分钟内3次 */
SSO(2, 360),
;
/**
* 超时次数
*/
private Integer timeOutCount;
/**
* 窗口时间单位
*/
private Integer timeSeconds;
FallBackBizTypeEnum(Integer timeOutCount, Integer timeSeconds) {
this.timeOutCount = timeOutCount;
this.timeSeconds = timeSeconds;
}
}

View File

@ -0,0 +1,13 @@
package com.sonic.bear.enums;
/**
* 三方类型枚举
*/
public enum ThirdTypeEnum {
DISCORD,
GOOGLE,
APPLE,
;
}

View File

@ -0,0 +1,92 @@
package com.sonic.bear.enums;
import com.sonic.common.exception.BizException;
import com.sonic.common.rpc.ApiResultCode;
import com.sonic.common.utils.MessageUtils;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
/**
* @description: 弹窗消息的定义
* @author: code
* @create: 2021-12-17 10:25
**/
@Getter
public enum ToastResultCode implements ApiResultCode {
/** 可以在此处扩展服务自身需要用到的错误码信息 */
BIZ_ERROR1("-1", "biz error"),
NO_LOGIN("0001", "User is not logged in"),
USER_NOT_EXIST("0002", "User does not exist"),
APP_CLIENT_NOT_EXIST("0003", "Login client does not exist"),
APP_CLIENT_NOT_ALLOW("0004", "Not authorized to log in to the client"),
SESSION_EXPIRED("0005", "Session is expired"),
SESSION_INVALID("0006", "Session is invalid"),
PASSWORD_INVALID("0007", "Invalid account or password"),
/** 第三方登陆授权失败 */
AUTH_FAIL("0008","auth.fail"),
USER_FROZEN("0009", "User is frozen"),
;
private final String errorCode;
private final String errorMsg;
ToastResultCode(String errorMsg) {
this.errorCode = "";
this.errorMsg = errorMsg;
}
ToastResultCode(String errorCode, String errorMsg) {
this.errorCode = getAppId() + errorCode;
this.errorMsg = errorMsg;
}
/**
* 得到 message 的值.
*
* @return 属性 message 的值.
*/
@Override
public String getErrorMsg() {
return errorMsg + "(" + getAppId() + ")";
}
/**
* @return 当前服务的AppId
*/
@Override
public String getAppId() {
return "1001";
}
/**
* 校验方法
*
* @param expect
*/
public void check(boolean expect) {
if (expect) {
String message = MessageUtils.get(this.name());
throw new BizException(this.getErrorCode(), this.name().equals(message) ? this.getErrorMsg() : message);
}
}
/**
* 校验方法
*
* @param expect
*/
public void check(boolean expect, String msg) {
if (expect) {
if(StringUtils.isNotBlank(msg)) {
throw new BizException(this.getErrorCode(), msg);
} else {
String message = MessageUtils.get(this.name());
throw new BizException(this.getErrorCode(), this.name().equals(message) ? this.getErrorMsg() : message);
}
}
}
}

View File

@ -0,0 +1,30 @@
package com.sonic.bear.event.inner;
import com.sonic.bear.config.EventConfig;
import com.sonic.common.event.Event;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 定义当前系统的消息
* @author coder
*/
@AllArgsConstructor
@Getter
public enum EventType {
/** 事件定义 */
DEMO_CREATED(EventConfig.DEFAULT_SCENE, EventConfig.DEFAULT_MODULE, "demo_created", "demo 创建"),
;
EventType(String scene, String module, String name, String desc) {
this.eventCode = Event.EventCode.builder()
.scene(scene)
.module(module)
.name(name)
.build();
this.desc = desc;
}
private String desc;
private Event.EventCode eventCode;
}

View File

@ -0,0 +1,33 @@
package com.sonic.bear.event.inner.handler;
import com.sonic.bear.event.inner.EventType;
import com.sonic.bear.event.inner.payload.DemoCreatedPayload;
import com.sonic.common.event.Event;
import com.sonic.common.event.EventConsumer;
import com.sonic.common.event.EventHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author coder
*/
@Slf4j
@Component
public class DemoCreatedThenHandler implements EventHandler, InitializingBean {
@Autowired
private EventConsumer eventConsumer;
@Override
public void onEvent(Event event) {
DemoCreatedPayload payload = event.normalizedData(DemoCreatedPayload.class);
// TODO:
}
@Override
public void afterPropertiesSet() {
eventConsumer.registerHandler(EventType.DEMO_CREATED.getEventCode(), this);
}
}

View File

@ -0,0 +1,18 @@
package com.sonic.bear.event.inner.payload;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author coder
* @date 2020-05-07
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DemoCreatedPayload {
private Long demoId;
}

View File

@ -0,0 +1,29 @@
package com.sonic.bear.event.outer;
import com.sonic.common.event.Event;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 定义不属于当前系统的消息
* @author coder
*/
@AllArgsConstructor
@Getter
public enum EventType {
/** 事件定义 */
USER_CREATED(Event.BuildInScene.BS.getCode(), "bs_user", "user_created", "用户创建"),
;
EventType(String scene, String module, String name, String desc) {
this.eventCode = Event.EventCode.builder()
.scene(scene)
.module(module)
.name(name)
.build();
this.desc = desc;
}
private String desc;
private Event.EventCode eventCode;
}

View File

@ -0,0 +1,78 @@
package com.sonic.bear.event.outer.handler;
import com.sonic.bear.event.outer.EventType;
import com.sonic.bear.event.outer.payload.UserCratedPayload;
import com.sonic.bear.lib.output.BaseUserInfoOutput;
import com.sonic.bear.service.CommonMessageService;
import com.sonic.bear.service.UserSearchService;
import com.sonic.common.event.Event;
import com.sonic.common.event.EventConsumer;
import com.sonic.common.event.EventHandler;
import com.sonic.lion.lib.client.PayClient;
import com.sonic.lion.lib.input.PlatformGiftInput;
import com.sonic.pigeon.lib.client.ImUserClient;
import com.sonic.pigeon.lib.enums.ImUserTypeEnum;
import com.sonic.pigeon.lib.input.CreateImUserInput;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* @author coder
*/
@Slf4j
@Component
public class UserCreatedThenHandler implements EventHandler, InitializingBean {
@Autowired
private EventConsumer eventConsumer;
@Autowired
private ImUserClient imUserClient;
@Autowired
private PayClient payClient;
@Autowired
private CommonMessageService commonMessageService;
@Autowired
private UserSearchService userSearchService;
@Override
public void onEvent(Event event) {
UserCratedPayload payload = event.normalizedData(UserCratedPayload.class);
log.info("UserCreatedThenHandler payload:{}", payload);
if (payload.getUserId() == null) {
return;
}
Long userId = payload.getUserId();
//获取用户信息
BaseUserInfoOutput baseUserInfo = userSearchService.baseUserInfo(userId);
//设置创建IM账号的入参对象
CreateImUserInput createImUserInput = new CreateImUserInput();
createImUserInput.setUserId(baseUserInfo.getUserId());
createImUserInput.setNickname(baseUserInfo.getNickname());
createImUserInput.setHeadImage(baseUserInfo.getHeadImage());
createImUserInput.setImUserType(ImUserTypeEnum.u);
//RPC 调用IM创建账号
imUserClient.createImUser(createImUserInput);
//RPC 初始化账号钱包
payClient.createAccountBuff(userId);
//用户注册发送欢迎系统通知
commonMessageService.userRegisterSendMessage(userId);
//赠送5个BUFF
PlatformGiftInput platformGiftInput = new PlatformGiftInput();
platformGiftInput.setPlatform("1");
platformGiftInput.setUid(userId);
platformGiftInput.setName(PlatformGiftInput.Type.NEW_USER_GIFT.getBizType().getDesc());
platformGiftInput.setCreateTime(LocalDateTime.now());
platformGiftInput.setAmount(500L);
platformGiftInput.setType(PlatformGiftInput.Type.NEW_USER_GIFT);
payClient.platformGift(platformGiftInput);
}
@Override
public void afterPropertiesSet() {
eventConsumer.registerHandler(EventType.USER_CREATED.getEventCode(), this);
}
}

View File

@ -0,0 +1,18 @@
package com.sonic.bear.event.outer.payload;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author coder
* @date 2020-05-07
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserCratedPayload {
private Long userId;
}

View File

@ -0,0 +1,11 @@
package com.sonic.bear.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.sonic.bear.domain.entity.AppClient;
import java.util.Optional;
public interface AppClientService extends IService<AppClient> {
Optional<AppClient> getByCode(String code);
}

View File

@ -0,0 +1,17 @@
package com.sonic.bear.service;
/**
* 公共发送系统通知
*/
public interface CommonMessageService {
/**
* 用户注册发送欢迎系统通知
*
* @param
*/
void userRegisterSendMessage(Long userId);
}

View File

@ -0,0 +1,17 @@
package com.sonic.bear.service;
/**
* 发送消息到im的mq
*/
public interface CommonSendMqService {
/**
* 用户创建成功后发送消息
*
* @param userId
*/
void userCreatedSendMq(Long userId);
}

View File

@ -0,0 +1,16 @@
package com.sonic.bear.service;
import com.sonic.common.auth.domains.Session;
import java.util.Optional;
public interface SessionCacheService {
Optional<Session> get(String token);
void cache(String token, Session session);
void localCache(String token, Session session);
void invalidate(String token);
}

View File

@ -0,0 +1,47 @@
package com.sonic.bear.service;
import java.util.Optional;
/**
* @Author code
* @Description 用户Token存在性验证缓存防止穿透
* @Date 2022/11/14 19:02
* @Version 1.0
*/
public interface SessionExistVerifyCacheService {
/**
* 获取缓存数据
* @param token
* @return
*/
Optional<String> get(String token);
/**
* 校验当前访问的token是否存在
* @param token
* @return
*/
boolean exist(String token);
/**
* 设置数据到内存缓存和redis缓存
* @param token
* @param expTime 过期时间单位 s
*/
void cache(String token, Long expTime);
/**
* 初始化数据
* @param token
* @param expTime
*/
void initCache(String token, Long expTime);
/**
* 清理掉缓存的存在的token数据
* @param token
*/
void clearCache(String token);
}

View File

@ -0,0 +1,12 @@
package com.sonic.bear.service;
import java.util.Optional;
public interface SessionKickOutCacheService {
Optional<String> get(String token);
void cache(String token);
void invalidate(String token);
}

View File

@ -0,0 +1,18 @@
package com.sonic.bear.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.sonic.bear.domain.entity.UserAccessLog;
/**
* @author coder
*/
public interface UserAccessLogService extends IService<UserAccessLog> {
/**
* 添加访问日志记录
* @param userId
* @param clientCode
*/
void addLog(Long userId, String clientCode);
}

View File

@ -0,0 +1,30 @@
package com.sonic.bear.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.sonic.bear.domain.entity.UserCommonlyUsedLog;
import java.util.List;
/**
* @author coder
*/
public interface UserCommonlyUsedLogService extends IService<UserCommonlyUsedLog> {
/**
* 添加数据
* @param userId
* @param dataType
* @param dataValue
*/
void addData(Long userId, Integer dataType, String dataValue);
/**
* 查询数据
* @param userIdList
* @param dataValueList
* @return
*/
List<UserCommonlyUsedLog> queryData(List<Long> userIdList, List<String> dataValueList);
}

View File

@ -0,0 +1,37 @@
package com.sonic.bear.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.sonic.bear.domain.entity.UserNicknamePool;
import com.sonic.bear.domain.output.NicknameOutput;
import java.util.List;
/**
* @author coder
*/
public interface UserNicknamePoolService extends IService<UserNicknamePool> {
/**
* 同步昵称池
* @param userId
* @param userType
* @param nickname
* @param optType
*/
void syncPool(Long userId, Integer userType, String nickname, String optType);
/**
* 批量获取昵称池
* @param userIdList
*/
List<NicknameOutput> batchGetNickname(List<Long> userIdList);
/**
* 校验昵称是否已经存在
* @param userId
* @param nickname
* @return
*/
boolean existCheck(Long userId, String nickname);
}

View File

@ -0,0 +1,27 @@
package com.sonic.bear.service;
import com.sonic.bear.lib.output.BaseUserInfoOutput;
import com.sonic.bear.lib.output.UserInfoListOutput;
import java.util.List;
/**
* @author coder
*/
public interface UserSearchService {
/**
* 批量获取用户基础信息
* @param userIdList
* @return
*/
List<UserInfoListOutput> batchGetUserInfo(List<Long> userIdList);
/**
* 获取用户自己的基础信息
* @param userId
* @return
*/
BaseUserInfoOutput baseUserInfo(Long userId);
}

View File

@ -0,0 +1,31 @@
package com.sonic.bear.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.sonic.bear.domain.entity.User;
import com.sonic.bear.domain.entity.UserInfoBo;
import com.sonic.bear.enums.ThirdTypeEnum;
/**
* @author coder
*/
public interface UserService extends IService<User> {
/**
* 三方登录或注册
* @param thirdType
* @param openId
* @param email
* @param nickname
* @return
*/
Long thirdLoginOrRegister(ThirdTypeEnum thirdType, String openId, String email, String nickname);
/**
* 查询用户信息
* @param userId
* @return
*/
UserInfoBo queryUserInfo(Long userId);
}

View File

@ -0,0 +1,80 @@
package com.sonic.bear.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.sonic.bear.domain.input.KickOutInput;
import com.sonic.bear.domain.input.LogoutInput;
import com.sonic.common.auth.domains.Session;
import com.sonic.bear.domain.entity.UserSession;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Optional;
/**
* @author coder
*/
public interface UserSessionService extends IService<UserSession> {
/**
* 创建一个 Session
* @param param
* @return
*/
Session createSession(CreateSessionReq param);
/**
* 验证token是否有效
* @param token session 令牌
* @return 有效的 session无效则抛出异常
*/
Session touchSession(String token);
/**
* 退出登录
* 1.更新session状态
* 2.更新loginToken状态
* 3.使session缓存过期
* @param input
*/
void logout(LogoutInput input);
/**
* 根据 token 查询 UserSession
* @param token
* @return
*/
Optional<UserSession> get(String token);
/**
* 踢下线
* @param input
*/
void kickOut(KickOutInput input);
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class CreateSessionReq {
@NotNull(message = "{YOU_HAVE_TO_ENTER_YOUR_ACCOUNT}")
private Long userId;
@NotBlank(message = "{YOU_HAVE_TO_SPECIFY_THE_LOGIN_CLIENT}")
private String clientCode;
private String deviceId;
private String ip;
/**
* 登录类型三方账号账号密码手机号验证码
*/
private String loginType;
/**
* 客户端设备基础信息
*/
private String userAgent;
}
}

View File

@ -0,0 +1,28 @@
package com.sonic.bear.service;
import com.sonic.bear.lib.input.CompleteUserInfoInput;
import com.sonic.bear.lib.input.EditUserInfoInput;
/**
* @author coder
*/
public interface UserSetService {
/**
* 补全用户信息
* @param input
*/
void completeUserInfo(CompleteUserInfoInput input);
/**
* 编辑用户信息
* @param input
*/
void editUserInfo(EditUserInfoInput input);
/**
* 删除账号
* @param userId
*/
void delAccount(Long userId);
}

View File

@ -0,0 +1,19 @@
package com.sonic.bear.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.sonic.bear.domain.entity.UserThird;
import com.sonic.bear.enums.ThirdTypeEnum;
/**
* @author coder
*/
public interface UserThirdService extends IService<UserThird> {
/**
* 根据三方类型获取基础数据
* @param thirdType
* @param openId
* @return
*/
UserThird queryByOpenId(ThirdTypeEnum thirdType, String openId);
}

Some files were not shown because too many files have changed in this diff Show More