nili
1 year ago
44 changed files with 1460 additions and 0 deletions
@ -0,0 +1,5 @@ |
|||
/.idea/ |
|||
/pid |
|||
**/target |
|||
/logs/ |
|||
*.log |
@ -0,0 +1,9 @@ |
|||
#!/bin/bash |
|||
mvn clean package -U -DskipTests > /dev/null |
|||
|
|||
if [ $? -eq 0 ]; then |
|||
echo "success" |
|||
else |
|||
echo "fail" |
|||
fi |
|||
|
@ -0,0 +1,50 @@ |
|||
<?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>game</artifactId> |
|||
<groupId>awesome.group</groupId> |
|||
<version>1.0-SNAPSHOT</version> |
|||
</parent> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
|
|||
<artifactId>game-dao</artifactId> |
|||
|
|||
<properties> |
|||
<maven.compiler.source>17</maven.compiler.source> |
|||
<maven.compiler.target>17</maven.compiler.target> |
|||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
|||
</properties> |
|||
|
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>com.baomidou</groupId> |
|||
<artifactId>mybatis-plus-boot-starter</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>mysql</groupId> |
|||
<artifactId>mysql-connector-java</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-jdbc</artifactId> |
|||
<!--不用tomcat的数据库连接池--> |
|||
<exclusions> |
|||
<exclusion> |
|||
<groupId>org.apache.tomcat</groupId> |
|||
<artifactId>tomcat-jdbc</artifactId> |
|||
</exclusion> |
|||
</exclusions> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-validation</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.projectlombok</groupId> |
|||
<artifactId>lombok</artifactId> |
|||
</dependency> |
|||
|
|||
</dependencies> |
|||
</project> |
@ -0,0 +1,11 @@ |
|||
package awesome.group.game.dao.bean; |
|||
|
|||
import lombok.Data; |
|||
import org.springframework.context.annotation.Bean; |
|||
|
|||
@Data |
|||
public class GameApp { |
|||
private String name; |
|||
private String appId; |
|||
private String appSecret; |
|||
} |
@ -0,0 +1,8 @@ |
|||
package awesome.group.game.dao.bean; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class User { |
|||
private Integer id; |
|||
} |
@ -0,0 +1,21 @@ |
|||
package awesome.group.game.dao.bean; |
|||
|
|||
import com.baomidou.mybatisplus.annotation.IdType; |
|||
import com.baomidou.mybatisplus.annotation.TableId; |
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class WeGameUser { |
|||
@TableId(value = "id", type = IdType.AUTO) |
|||
private Integer id; |
|||
private String appId; |
|||
private String openId; |
|||
private String nickname; |
|||
private Integer gender; |
|||
private String province; |
|||
private String city; |
|||
private String country; |
|||
private String avatarUrl; |
|||
private String unionId; |
|||
private String sessionKey; |
|||
} |
@ -0,0 +1,14 @@ |
|||
package awesome.group.game.dao.bean; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class WxUserInfo { |
|||
public String avatarUrl; |
|||
public String nickName; |
|||
public Integer gender; //0: 未知;1:男性;2:女性
|
|||
public String city; |
|||
public String province; |
|||
public String country; |
|||
public String language; |
|||
} |
@ -0,0 +1,69 @@ |
|||
package awesome.group.game.dao.config; |
|||
|
|||
import com.baomidou.mybatisplus.annotation.DbType; |
|||
import com.baomidou.mybatisplus.core.MybatisConfiguration; |
|||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; |
|||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; |
|||
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; |
|||
import com.zaxxer.hikari.HikariDataSource; |
|||
import org.apache.ibatis.session.SqlSessionFactory; |
|||
import org.mybatis.spring.SqlSessionTemplate; |
|||
import org.mybatis.spring.annotation.MapperScan; |
|||
import org.springframework.beans.factory.annotation.Qualifier; |
|||
import org.springframework.boot.context.properties.ConfigurationProperties; |
|||
import org.springframework.boot.jdbc.DataSourceBuilder; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.context.annotation.Primary; |
|||
import org.springframework.jdbc.datasource.DataSourceTransactionManager; |
|||
|
|||
import javax.sql.DataSource; |
|||
|
|||
|
|||
@Configuration |
|||
@MapperScan(basePackages = BasicDataSourceConfig.BASE_PACKAGE,sqlSessionFactoryRef = BasicDataSourceConfig.SSF_NAME,sqlSessionTemplateRef = BasicDataSourceConfig.SST_NAME) |
|||
public class BasicDataSourceConfig { |
|||
|
|||
public static final String DS_NAME = "basicDataSource"; |
|||
|
|||
public static final String TM_NAME = "basicTransactionManager"; |
|||
|
|||
public static final String SSF_NAME = "basicSqlSessionFactory"; |
|||
|
|||
public static final String SST_NAME = "basicSqlSessionTemplate"; |
|||
|
|||
public static final String DS_CFG_PREFIX = "datasource.game"; |
|||
|
|||
public static final String BASE_PACKAGE = "awesome.group.game.dao.mapper"; |
|||
|
|||
@Bean(name = DS_NAME) |
|||
@ConfigurationProperties(DS_CFG_PREFIX) |
|||
public DataSource dataSource() { |
|||
return DataSourceBuilder.create().type(HikariDataSource.class).build(); |
|||
} |
|||
|
|||
@Primary |
|||
@Bean(name = TM_NAME) |
|||
public DataSourceTransactionManager transactionManager(@Qualifier(DS_NAME) DataSource dataSource) { |
|||
return new DataSourceTransactionManager(dataSource); |
|||
} |
|||
|
|||
@Bean(name = SSF_NAME) |
|||
public SqlSessionFactory sqlSessionFactory(@Qualifier(DS_NAME) DataSource dataSource, |
|||
@Qualifier(MybatisGeneralConfig.MBTS_CFG_NAME) MybatisConfiguration configuration) throws Exception { |
|||
MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean(); |
|||
|
|||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); |
|||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); |
|||
configuration.addInterceptor(interceptor); |
|||
|
|||
factoryBean.setConfiguration(configuration); |
|||
factoryBean.setDataSource(dataSource); |
|||
return factoryBean.getObject(); |
|||
} |
|||
|
|||
@Bean(name = SST_NAME) |
|||
public SqlSessionTemplate sqlSessionTemplate(@Qualifier(SSF_NAME) SqlSessionFactory sqlSessionFactory) { |
|||
return new SqlSessionTemplate(sqlSessionFactory); |
|||
} |
|||
} |
@ -0,0 +1,22 @@ |
|||
package awesome.group.game.dao.config; |
|||
|
|||
import com.baomidou.mybatisplus.core.MybatisConfiguration; |
|||
import org.springframework.beans.factory.config.ConfigurableBeanFactory; |
|||
import org.springframework.boot.context.properties.ConfigurationProperties; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.context.annotation.Scope; |
|||
|
|||
@Configuration |
|||
public class MybatisGeneralConfig { |
|||
|
|||
public static final String MBTS_CFG_NAME = "mybatisGeneralCfg"; |
|||
|
|||
@Bean(MBTS_CFG_NAME) |
|||
@ConfigurationProperties("mybatis-plus.configuration") |
|||
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) |
|||
public MybatisConfiguration configuration() { |
|||
return new MybatisConfiguration(); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,11 @@ |
|||
package awesome.group.game.dao.mapper; |
|||
|
|||
import awesome.group.game.dao.bean.GameApp; |
|||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
|||
import org.apache.ibatis.annotations.Param; |
|||
import org.apache.ibatis.annotations.Select; |
|||
|
|||
public interface GameAppMapper extends BaseMapper<GameApp> { |
|||
@Select("select * from game_app where app_id = #{appId}") |
|||
GameApp query(@Param("appId")String appId); |
|||
} |
@ -0,0 +1,7 @@ |
|||
package awesome.group.game.dao.mapper; |
|||
|
|||
import awesome.group.game.dao.bean.User; |
|||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
|||
|
|||
public interface UserMapper extends BaseMapper<User> { |
|||
} |
@ -0,0 +1,15 @@ |
|||
package awesome.group.game.dao.mapper; |
|||
|
|||
import awesome.group.game.dao.bean.WeGameUser; |
|||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
|||
import org.apache.ibatis.annotations.Param; |
|||
import org.apache.ibatis.annotations.Select; |
|||
import org.apache.ibatis.annotations.Update; |
|||
|
|||
public interface WeGameUserMapper extends BaseMapper<WeGameUser> { |
|||
@Select("select * from we_game_user where app_id = #{appId} and open_id = #{openId}") |
|||
WeGameUser query(@Param("appId")String appId, @Param("openId")String openId); |
|||
|
|||
@Update("update we_game_user set session_key = #{sessionKey} where id = #{id}") |
|||
int updateSessionKey(@Param("sessionKey")String sessionKey, @Param("id")Integer id); |
|||
} |
@ -0,0 +1,69 @@ |
|||
<?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>game</artifactId> |
|||
<groupId>awesome.group</groupId> |
|||
<version>1.0-SNAPSHOT</version> |
|||
</parent> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
|
|||
<artifactId>game-service</artifactId> |
|||
|
|||
<properties> |
|||
<maven.compiler.source>17</maven.compiler.source> |
|||
<maven.compiler.target>17</maven.compiler.target> |
|||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
|||
</properties> |
|||
|
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>awesome.group</groupId> |
|||
<artifactId>game-dao</artifactId> |
|||
<version>1.0-SNAPSHOT</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-web</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-aop</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework.security</groupId> |
|||
<artifactId>spring-security-crypto</artifactId> |
|||
<version>5.6.7</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-test</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>junit</groupId> |
|||
<artifactId>junit</artifactId> |
|||
<scope>test</scope> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>com.auth0</groupId> |
|||
<artifactId>java-jwt</artifactId> |
|||
<version>3.14.0</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.google.code.gson</groupId> |
|||
<artifactId>gson</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp --> |
|||
<dependency> |
|||
<groupId>com.squareup.okhttp3</groupId> |
|||
<artifactId>okhttp</artifactId> |
|||
</dependency> |
|||
|
|||
|
|||
</dependencies> |
|||
|
|||
|
|||
</project> |
@ -0,0 +1,16 @@ |
|||
package awesome.group.game.service; |
|||
|
|||
import awesome.group.game.dao.bean.User; |
|||
import awesome.group.game.dao.mapper.UserMapper; |
|||
import jakarta.annotation.Resource; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
@Service |
|||
public class UserService { |
|||
@Resource |
|||
private UserMapper userMapper; |
|||
|
|||
public User getUser(Integer uid) { |
|||
return userMapper.selectById(uid); |
|||
} |
|||
} |
@ -0,0 +1,85 @@ |
|||
package awesome.group.game.service; |
|||
|
|||
|
|||
import awesome.group.game.dao.bean.GameApp; |
|||
import awesome.group.game.dao.bean.WeGameUser; |
|||
import awesome.group.game.dao.bean.WxUserInfo; |
|||
import awesome.group.game.dao.mapper.GameAppMapper; |
|||
import awesome.group.game.dao.mapper.WeGameUserMapper; |
|||
import awesome.group.game.service.bo.Code2SessionBo; |
|||
import awesome.group.game.service.common.exception.PaganiException; |
|||
import awesome.group.game.service.common.exception.PaganiExceptionCode; |
|||
import awesome.group.game.service.util.EncryptUtil; |
|||
import awesome.group.game.service.util.HttpUtils; |
|||
import awesome.group.game.service.util.JwtUtils; |
|||
import com.google.gson.Gson; |
|||
import jakarta.annotation.Resource; |
|||
import org.springframework.beans.BeanUtils; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.util.StringUtils; |
|||
|
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
|
|||
@Service |
|||
public class WxService { |
|||
@Resource |
|||
private WeGameUserMapper weGameUserMapper; |
|||
|
|||
@Resource |
|||
private GameAppMapper gameAppMapper; |
|||
|
|||
public String login(String code, String appId) { |
|||
Code2SessionBo data = getSession(code, appId); |
|||
if (data == null || data.openid == null) { |
|||
throw new PaganiException(PaganiExceptionCode.GENERAL_ERROR, "登陆失败,非法code"); |
|||
} |
|||
WeGameUser u = weGameUserMapper.query(appId, data.openid); |
|||
if (u == null) { |
|||
u = new WeGameUser(); |
|||
u.setOpenId(data.openid); |
|||
u.setAppId(appId); |
|||
u.setUnionId(data.unionid); |
|||
u.setSessionKey(data.session_key); |
|||
weGameUserMapper.insert(u); |
|||
} else { |
|||
weGameUserMapper.updateSessionKey(data.session_key, u.getId()); |
|||
} |
|||
return JwtUtils.generatorToken(u.getId() + "", 2 * 86400); |
|||
} |
|||
|
|||
public void updateUserInfo(Integer userId, String rawData, String signature) { |
|||
if (!verify(userId, rawData, signature)) { |
|||
throw new PaganiException(PaganiExceptionCode.GENERAL_ERROR, "签名有误"); |
|||
} |
|||
Gson gson = new Gson(); |
|||
WxUserInfo info = gson.fromJson(rawData, WxUserInfo.class); |
|||
WeGameUser u = new WeGameUser(); |
|||
BeanUtils.copyProperties(info, u); |
|||
u.setId(userId); |
|||
u.setNickname(info.nickName); |
|||
u.setAvatarUrl(info.avatarUrl); |
|||
weGameUserMapper.updateById(u); |
|||
} |
|||
|
|||
|
|||
public boolean verify(Integer userId, String rawData, String signature) { |
|||
WeGameUser u = weGameUserMapper.selectById(userId); |
|||
if (!StringUtils.hasText(u.getSessionKey())) { |
|||
return false; |
|||
} |
|||
String str = rawData + u.getSessionKey(); |
|||
return signature.equals(EncryptUtil.sha1(str)); |
|||
} |
|||
|
|||
public Code2SessionBo getSession(String code, String appId) { |
|||
GameApp gameApp = gameAppMapper.query(appId); |
|||
String api = "https://api.weixin.qq.com/sns/jscode2session"; |
|||
Map<String, String> param = new HashMap<>(); |
|||
param.put("appid", gameApp.getAppId()); |
|||
param.put("secret", gameApp.getAppSecret()); |
|||
param.put("js_code", code); |
|||
param.put("grant_type", "authorization_code"); |
|||
return HttpUtils.get(api, param, null, Code2SessionBo.class); |
|||
} |
|||
} |
@ -0,0 +1,16 @@ |
|||
package awesome.group.game.service.bo; |
|||
|
|||
public class Code2SessionBo extends WxResponse{ |
|||
public String session_key; |
|||
public String openid; |
|||
public String unionid; |
|||
|
|||
public Code2SessionBo() { |
|||
} |
|||
|
|||
public Code2SessionBo(String session_key, String openid, String unionid) { |
|||
this.session_key = session_key; |
|||
this.openid = openid; |
|||
this.unionid = unionid; |
|||
} |
|||
} |
@ -0,0 +1,6 @@ |
|||
package awesome.group.game.service.bo; |
|||
|
|||
public class WxResponse { |
|||
public Integer errcode; |
|||
public String errmsg; |
|||
} |
@ -0,0 +1,19 @@ |
|||
package awesome.group.game.service.common.exception; |
|||
|
|||
public class PaganiException extends RuntimeException { |
|||
private int code; |
|||
|
|||
public PaganiException(int code, String msg, Throwable t) { |
|||
super(msg, t); |
|||
this.code = code; |
|||
} |
|||
|
|||
public PaganiException(int code, String msg) { |
|||
super(msg); |
|||
this.code = code; |
|||
} |
|||
|
|||
public int getCode() { |
|||
return code; |
|||
} |
|||
} |
@ -0,0 +1,8 @@ |
|||
package awesome.group.game.service.common.exception; |
|||
|
|||
public class PaganiExceptionCode { |
|||
public static final int LOG_ERROR = -10001; |
|||
public static final int GENERAL_ERROR = -99999; |
|||
|
|||
public static final int ILLEGAL_REQUEST = 10001; |
|||
} |
@ -0,0 +1,17 @@ |
|||
package awesome.group.game.service.common.log; |
|||
|
|||
public class Const { |
|||
public static final String LOGGER_API = "log.api"; |
|||
public static final String LOGGER_TRACE = "log.trace"; |
|||
|
|||
public static final String COST = "key_cost"; |
|||
public static final String ACTION = "key_action"; |
|||
public static final String STATUS = "key_status"; |
|||
public static final String JWT = "key_jwt"; |
|||
public static final String METHOD_NAME = "key_method_name"; |
|||
|
|||
|
|||
public static final String API_LOG_FORMAT = "{%s,%s,%s}"; |
|||
|
|||
public static final String STRING_DEFAULT = "null"; |
|||
} |
@ -0,0 +1,27 @@ |
|||
package awesome.group.game.service.common.log; |
|||
|
|||
|
|||
import awesome.group.game.service.common.exception.PaganiException; |
|||
import awesome.group.game.service.common.exception.PaganiExceptionCode; |
|||
import ch.qos.logback.classic.pattern.ClassicConverter; |
|||
import ch.qos.logback.classic.spi.ILoggingEvent; |
|||
|
|||
import java.net.InetAddress; |
|||
|
|||
public class IPLogConfig extends ClassicConverter { |
|||
|
|||
private static String msIp = null; |
|||
|
|||
static { |
|||
try { |
|||
msIp = InetAddress.getLocalHost().getHostAddress(); |
|||
} catch (Exception e) { |
|||
throw new PaganiException(PaganiExceptionCode.LOG_ERROR, "get local ip failed", e); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public String convert(ILoggingEvent event) { |
|||
return msIp; |
|||
} |
|||
} |
@ -0,0 +1,22 @@ |
|||
package awesome.group.game.service.common.log; |
|||
|
|||
public class L { |
|||
private static PaganiLogger msLogger = new PaganiLogger(); |
|||
|
|||
public static void api(int cost, Status status, String methodName, String jwtToken, String cookie, String param, String resp) { |
|||
msLogger.api(cost, status, methodName, jwtToken, cookie, param, resp); |
|||
} |
|||
|
|||
public static void api(int cost, Status status, String methodName, String jwtToken, String cookie, String param, String resp, Throwable t) { |
|||
msLogger.api(cost, status, methodName, jwtToken, cookie, param, resp, t); |
|||
} |
|||
|
|||
public static void trace(String action, String msg) { |
|||
msLogger.trace(action, msg); |
|||
} |
|||
|
|||
public static void trace(String action, String msg, Throwable t) { |
|||
msLogger.trace(action, msg, t); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,78 @@ |
|||
package awesome.group.game.service.common.log; |
|||
|
|||
import awesome.group.game.service.common.exception.PaganiException; |
|||
import awesome.group.game.service.common.exception.PaganiExceptionCode; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.slf4j.MDC; |
|||
|
|||
import java.util.Map; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
|
|||
public class PaganiLogger { |
|||
private Map<String, Logger> mLoggers = new ConcurrentHashMap<String, Logger>(); |
|||
|
|||
public void api(int cost, Status status, String methodName, String accessToken, String cookie, String param, String resp) { |
|||
checkCostStatus(cost, status); |
|||
String msg = String.format(Const.API_LOG_FORMAT, param, resp, cookie); |
|||
Logger logger = getLogger(Const.LOGGER_API); |
|||
MDC.put(Const.COST, String.valueOf(cost)); |
|||
MDC.put(Const.STATUS, String.valueOf(status.ordinal())); |
|||
MDC.put(Const.JWT, accessToken); |
|||
MDC.put(Const.METHOD_NAME, methodName); |
|||
logger.info(msg); |
|||
mdcClear(Const.COST, Const.STATUS, Const.JWT, Const.METHOD_NAME); |
|||
} |
|||
|
|||
public void api(int cost, Status status, String methodName, String accessToken, String cookie, String param, String resp, Throwable t) { |
|||
checkCostStatus(cost, status); |
|||
String msg = String.format(Const.API_LOG_FORMAT, param, resp, cookie); |
|||
Logger logger = getLogger(Const.LOGGER_API); |
|||
MDC.put(Const.COST, String.valueOf(cost)); |
|||
MDC.put(Const.STATUS, String.valueOf(status.ordinal())); |
|||
MDC.put(Const.JWT, accessToken); |
|||
MDC.put(Const.METHOD_NAME, methodName); |
|||
logger.error(msg, t); |
|||
mdcClear(Const.COST, Const.STATUS, Const.JWT, Const.METHOD_NAME); |
|||
} |
|||
|
|||
public void trace(String action, String msg) { |
|||
Logger logger = getLogger(Const.LOGGER_TRACE); |
|||
MDC.put(Const.ACTION, action); |
|||
logger.info(msg); |
|||
mdcClear(Const.ACTION); |
|||
} |
|||
|
|||
public void trace(String action, String msg, Throwable t) { |
|||
Logger logger = getLogger(Const.LOGGER_TRACE); |
|||
MDC.put(Const.ACTION, action); |
|||
logger.error(msg, t); |
|||
mdcClear(Const.ACTION); |
|||
} |
|||
|
|||
private void checkCostStatus(long cost, Status status) { |
|||
if (null == status || cost < 0) { |
|||
throw new PaganiException(PaganiExceptionCode.LOG_ERROR, |
|||
String.format("statistics param error cost = %d, status = %s", |
|||
cost, null == status ? "" : status.toString())); |
|||
} |
|||
} |
|||
|
|||
private void mdcClear(String... args) { |
|||
for (String key : args) { |
|||
MDC.remove(key); |
|||
} |
|||
} |
|||
|
|||
private Logger getLogger(String key) { |
|||
if (null == key) { |
|||
throw new PaganiException(PaganiExceptionCode.LOG_ERROR, "getLogger = null"); |
|||
} |
|||
Logger logger = mLoggers.get(key); |
|||
if (null == logger) { |
|||
logger = LoggerFactory.getLogger(key); |
|||
mLoggers.put(key, logger); |
|||
} |
|||
return logger; |
|||
} |
|||
} |
@ -0,0 +1,6 @@ |
|||
package awesome.group.game.service.common.log; |
|||
|
|||
public enum Status { |
|||
FAILED, |
|||
SUCCESS |
|||
} |
@ -0,0 +1,49 @@ |
|||
package awesome.group.game.service.common.response; |
|||
|
|||
public class R<T> { |
|||
public static final int CODE_SUCCESS = 0; |
|||
|
|||
private Integer code; |
|||
private String message = ""; |
|||
private T data; |
|||
|
|||
public R() { |
|||
} |
|||
|
|||
public R(T result) { |
|||
this.data = result; |
|||
this.code = CODE_SUCCESS; |
|||
this.message = "ok"; |
|||
} |
|||
|
|||
public R(Integer code, String message, T result) { |
|||
this.code = code; |
|||
this.message = message; |
|||
this.data = result; |
|||
} |
|||
|
|||
public Integer getCode() { |
|||
return code; |
|||
} |
|||
|
|||
public void setCode(Integer code) { |
|||
this.code = code; |
|||
} |
|||
|
|||
public String getMessage() { |
|||
return message; |
|||
} |
|||
|
|||
public void setMessage(String message) { |
|||
this.message = message; |
|||
} |
|||
|
|||
public T getData() { |
|||
return data; |
|||
} |
|||
|
|||
public void setData(T data) { |
|||
this.data = data; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,6 @@ |
|||
package awesome.group.game.service.constant; |
|||
|
|||
public class TokenConstants { |
|||
public static final String ACCESS_TOKEN_TYPE = "accessToken"; |
|||
public static final String REFRESH_TOKEN_TYPE = "refreshToken"; |
|||
} |
@ -0,0 +1,31 @@ |
|||
package awesome.group.game.service.util; |
|||
|
|||
import java.nio.charset.StandardCharsets; |
|||
import java.security.MessageDigest; |
|||
import java.security.NoSuchAlgorithmException; |
|||
import java.util.Formatter; |
|||
|
|||
public class EncryptUtil { |
|||
public static String sha1(String text) { |
|||
String sha1 = ""; |
|||
try { |
|||
MessageDigest crypt = MessageDigest.getInstance("SHA-1"); |
|||
crypt.reset(); |
|||
crypt.update(text.getBytes(StandardCharsets.UTF_8)); |
|||
sha1 = byteToHex(crypt.digest()); |
|||
} catch (NoSuchAlgorithmException e) { |
|||
//忽略
|
|||
} |
|||
return sha1; |
|||
} |
|||
|
|||
public static String byteToHex(final byte[] hash) { |
|||
Formatter formatter = new Formatter(); |
|||
for (byte b : hash) { |
|||
formatter.format("%02x", b); |
|||
} |
|||
String result = formatter.toString(); |
|||
formatter.close(); |
|||
return result; |
|||
} |
|||
} |
@ -0,0 +1,95 @@ |
|||
package awesome.group.game.service.util; |
|||
|
|||
|
|||
import awesome.group.game.service.bo.WxResponse; |
|||
import awesome.group.game.service.common.exception.PaganiException; |
|||
import awesome.group.game.service.common.exception.PaganiExceptionCode; |
|||
import awesome.group.game.service.common.log.L; |
|||
import com.google.gson.Gson; |
|||
import okhttp3.*; |
|||
import org.springframework.util.CollectionUtils; |
|||
|
|||
import java.io.IOException; |
|||
import java.lang.reflect.ParameterizedType; |
|||
import java.lang.reflect.Type; |
|||
import java.util.Map; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
public class HttpUtils { |
|||
private static final OkHttpClient httpClient = new OkHttpClient.Builder() |
|||
.connectTimeout(2, TimeUnit.SECONDS) |
|||
.readTimeout(5, TimeUnit.SECONDS) |
|||
.connectionPool(new ConnectionPool(8, 5, TimeUnit.MINUTES)).build(); |
|||
private static Gson gson = new Gson(); |
|||
private static final int WX_RESP_CODE_SUCCESS = 0; |
|||
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); |
|||
|
|||
|
|||
public static <T> T postForm(String url, Map<String, String> query, Map<String, String> form, |
|||
Map<String, String> headers, Class<T> dataType) { |
|||
FormBody.Builder bodyBuilder = new FormBody.Builder(); |
|||
for (Map.Entry<String, String> kv : form.entrySet()) { |
|||
bodyBuilder.add(kv.getKey(), kv.getValue()); |
|||
} |
|||
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder(); |
|||
for (Map.Entry<String, String> kv : query.entrySet()) { |
|||
urlBuilder.addQueryParameter(kv.getKey(), kv.getValue()); |
|||
} |
|||
Request.Builder requestBuilder = new Request.Builder().post(bodyBuilder.build()).url(urlBuilder.build()); |
|||
if (!CollectionUtils.isEmpty(headers)) { |
|||
headers.entrySet().stream().forEach(x -> { |
|||
requestBuilder.addHeader(x.getKey(), x.getValue()); |
|||
}); |
|||
} |
|||
Request request = requestBuilder.build(); |
|||
String msg = "url:" + url + ",header:" + gson.toJson(headers) + ", body:" + form + ", query:" + query; |
|||
return doHttpExecute(msg, request, dataType); |
|||
} |
|||
|
|||
public static <T> T postJson(String url, Object body, Map<String, String> headers, Class<T> dataType) { |
|||
String bodyStr = gson.toJson(body); |
|||
RequestBody requestBody = RequestBody.create(JSON, bodyStr); |
|||
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder(); |
|||
Request.Builder requestBuilder = new Request.Builder().post(requestBody).url(urlBuilder.build()); |
|||
|
|||
if (!CollectionUtils.isEmpty(headers)) { |
|||
headers.forEach(requestBuilder::addHeader); |
|||
} |
|||
|
|||
Request request = requestBuilder.build(); |
|||
|
|||
String msg = "url:" + url + ",header:" + gson.toJson(headers) + ", body:" + bodyStr; |
|||
return doHttpExecute(msg, request, dataType); |
|||
} |
|||
|
|||
public static <T> T get(String url, Map<String, String> queryStr, Map<String, String> headers, Class<T> dataType) { |
|||
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder(); |
|||
for (Map.Entry<String, String> kv : queryStr.entrySet()) { |
|||
urlBuilder.addQueryParameter(kv.getKey(), kv.getValue()); |
|||
} |
|||
Request.Builder requestBuilder = new Request.Builder().get().url(urlBuilder.build()); |
|||
if (!CollectionUtils.isEmpty(headers)) { |
|||
headers.forEach(requestBuilder::addHeader); |
|||
} |
|||
Request request = requestBuilder.build(); |
|||
String msg = "url:" + url + ",header:" + gson.toJson(headers) + ", query:" + gson.toJson(queryStr); |
|||
return doHttpExecute(msg, request, dataType); |
|||
} |
|||
|
|||
private static <T> T doHttpExecute(String msg, Request request, Class<T> dataType) { |
|||
try { |
|||
long now = System.currentTimeMillis(); |
|||
Response resp = httpClient.newCall(request).execute(); |
|||
if (!resp.isSuccessful() || resp.body() == null) { |
|||
L.trace("http_request_error", msg + ",http_code:" + resp.code()); |
|||
throw new PaganiException(PaganiExceptionCode.GENERAL_ERROR, "http_request_error"); |
|||
} |
|||
String res = resp.body().string(); |
|||
return gson.fromJson(res, dataType); |
|||
} catch (IOException ioe) { |
|||
L.trace("http_request_error", msg + ",msg:" + ioe.getMessage(), ioe); |
|||
throw new PaganiException(PaganiExceptionCode.GENERAL_ERROR, ioe.getMessage(), ioe); |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,38 @@ |
|||
package awesome.group.game.service.util; |
|||
|
|||
import com.auth0.jwt.JWT; |
|||
import com.auth0.jwt.JWTCreator; |
|||
import com.auth0.jwt.algorithms.Algorithm; |
|||
import com.auth0.jwt.interfaces.DecodedJWT; |
|||
|
|||
import java.time.LocalDateTime; |
|||
import java.time.ZoneId; |
|||
import java.util.Date; |
|||
|
|||
public class JwtUtils { |
|||
|
|||
//盐
|
|||
private static final String SIGN = "CPFmsb!@#$$"; |
|||
|
|||
private static final String JWT_KEY_USERID = "userId"; |
|||
|
|||
|
|||
public static String generatorToken(String userId, long expireSeconds) { |
|||
LocalDateTime localDateTimeNow = LocalDateTime.now(); |
|||
LocalDateTime localDateTimeExpire = localDateTimeNow.plusSeconds(expireSeconds); |
|||
Date expire = Date.from(localDateTimeExpire.atZone(ZoneId.systemDefault()).toInstant()); |
|||
JWTCreator.Builder builder = JWT.create(); |
|||
builder.withExpiresAt(expire); |
|||
builder.withClaim(JWT_KEY_USERID, userId); |
|||
|
|||
//生成token
|
|||
return builder.sign(Algorithm.HMAC256(SIGN)); |
|||
} |
|||
|
|||
//解析token
|
|||
public static Integer parseToken(String token) { |
|||
DecodedJWT verify = JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token); |
|||
String userId = verify.getClaim(JWT_KEY_USERID).asString(); |
|||
return Integer.parseInt(userId); |
|||
} |
|||
} |
@ -0,0 +1,51 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
|
|||
|
|||
<parent> |
|||
<artifactId>game</artifactId> |
|||
<groupId>awesome.group</groupId> |
|||
<version>1.0-SNAPSHOT</version> |
|||
</parent> |
|||
|
|||
<artifactId>game-web</artifactId> |
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>awesome.group</groupId> |
|||
<artifactId>game-dao</artifactId> |
|||
<version>1.0-SNAPSHOT</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>awesome.group</groupId> |
|||
<artifactId>game-service</artifactId> |
|||
<version>1.0-SNAPSHOT</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-web</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-aop</artifactId> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-test</artifactId> |
|||
<scope>test</scope> |
|||
</dependency> |
|||
|
|||
</dependencies> |
|||
|
|||
<build> |
|||
<plugins> |
|||
<plugin> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-maven-plugin</artifactId> |
|||
</plugin> |
|||
</plugins> |
|||
</build> |
|||
</project> |
@ -0,0 +1,18 @@ |
|||
package awesome.group; |
|||
|
|||
import org.springframework.boot.SpringApplication; |
|||
import org.springframework.boot.autoconfigure.SpringBootApplication; |
|||
import org.springframework.boot.context.ApplicationPidFileWriter; |
|||
import org.springframework.boot.web.servlet.ServletComponentScan; |
|||
import org.springframework.context.annotation.EnableAspectJAutoProxy; |
|||
|
|||
@SpringBootApplication |
|||
@ServletComponentScan |
|||
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) |
|||
public class Application { |
|||
public static void main(String[] args) { |
|||
SpringApplication application = new SpringApplication(Application.class); |
|||
application.addListeners(new ApplicationPidFileWriter()); |
|||
application.run(args); |
|||
} |
|||
} |
@ -0,0 +1,47 @@ |
|||
package awesome.group; |
|||
|
|||
import jakarta.servlet.http.HttpServletRequest; |
|||
import jakarta.servlet.http.HttpServletResponse; |
|||
|
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
|
|||
public class RequestContext { |
|||
|
|||
private static final String KEY_REQUEST = "request"; |
|||
|
|||
private static final String KEY_RESPONSE = "response"; |
|||
|
|||
private static final String KEY_UID = "uid"; |
|||
|
|||
private static final ThreadLocal<Map<String, Object>> THREAD_LOCAL = new ThreadLocal<>(); |
|||
|
|||
|
|||
public static void init(HttpServletRequest request, HttpServletResponse response, Integer userId) { |
|||
Map<String, Object> context = new HashMap<>(); |
|||
context.put(KEY_REQUEST, request); |
|||
context.put(KEY_RESPONSE, response); |
|||
context.put(KEY_UID, userId); |
|||
THREAD_LOCAL.set(context); |
|||
} |
|||
|
|||
public static HttpServletRequest getRequest() { |
|||
|
|||
return (HttpServletRequest) THREAD_LOCAL.get().get(KEY_REQUEST); |
|||
} |
|||
|
|||
public static HttpServletResponse getResponse() { |
|||
|
|||
return (HttpServletResponse) THREAD_LOCAL.get().get(KEY_RESPONSE); |
|||
} |
|||
|
|||
|
|||
public static Integer getUid() { |
|||
if (THREAD_LOCAL.get() == null) { |
|||
return null; |
|||
} |
|||
return (Integer) THREAD_LOCAL.get().get(KEY_UID); |
|||
} |
|||
|
|||
|
|||
} |
@ -0,0 +1,11 @@ |
|||
package awesome.group.aop; |
|||
|
|||
import java.lang.annotation.ElementType; |
|||
import java.lang.annotation.Retention; |
|||
import java.lang.annotation.RetentionPolicy; |
|||
import java.lang.annotation.Target; |
|||
|
|||
@Target(ElementType.METHOD) |
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
public @interface RestApi { |
|||
} |
@ -0,0 +1,77 @@ |
|||
package awesome.group.aop; |
|||
|
|||
import awesome.group.RequestContext; |
|||
|
|||
import awesome.group.game.service.common.exception.PaganiException; |
|||
import awesome.group.game.service.common.exception.PaganiExceptionCode; |
|||
import awesome.group.game.service.common.log.L; |
|||
import awesome.group.game.service.common.log.Status; |
|||
import awesome.group.game.service.common.response.R; |
|||
import com.fasterxml.jackson.core.JsonProcessingException; |
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import org.aspectj.lang.ProceedingJoinPoint; |
|||
import org.aspectj.lang.annotation.Around; |
|||
import org.aspectj.lang.annotation.Aspect; |
|||
import org.aspectj.lang.reflect.MethodSignature; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.core.annotation.Order; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
@Aspect |
|||
@Component |
|||
@Order(1) |
|||
public class RestApiAop { |
|||
|
|||
@Autowired |
|||
private ObjectMapper objectMapper; |
|||
|
|||
@Around("@annotation(RestApi)") |
|||
public Object advice(ProceedingJoinPoint joinPoint) { |
|||
MethodSignature signature = (MethodSignature) joinPoint.getSignature(); |
|||
String methodName = signature.getMethod().getName(); |
|||
Throwable exception = null; |
|||
long start = System.currentTimeMillis(); |
|||
Object[] params = joinPoint.getArgs(); |
|||
Object ret = null; |
|||
Status status = Status.SUCCESS; |
|||
try { |
|||
ret = joinPoint.proceed(); |
|||
} catch (Throwable e) { |
|||
exception = e; |
|||
if (e instanceof PaganiException) { |
|||
ret = new R<>(((PaganiException) e).getCode(), e.getMessage(), null); |
|||
} else { |
|||
status = Status.FAILED; |
|||
ret = new R<>(PaganiExceptionCode.GENERAL_ERROR, "服务器错误,请稍后重试", null); |
|||
} |
|||
} finally { |
|||
int cost = (int) (System.currentTimeMillis() - start); |
|||
String paramStr = ""; |
|||
if (params != null) { |
|||
try { |
|||
paramStr = objectMapper.writeValueAsString(params); |
|||
} catch (JsonProcessingException e) { |
|||
} |
|||
} |
|||
|
|||
String retStr = ""; |
|||
if (ret != null) { |
|||
try { |
|||
retStr = objectMapper.writeValueAsString(ret); |
|||
} catch (JsonProcessingException e) { |
|||
} |
|||
} |
|||
|
|||
String uid = RequestContext.getUid() + ""; |
|||
//TODO cookie看情况取不取
|
|||
String cookie = ""; |
|||
|
|||
if (exception == null) { |
|||
L.api(cost, status, methodName, uid, cookie, paramStr, retStr); |
|||
} else { |
|||
L.api(cost, status, methodName, uid, cookie, paramStr, retStr, exception); |
|||
} |
|||
} |
|||
return ret; |
|||
} |
|||
} |
@ -0,0 +1,47 @@ |
|||
package awesome.group.controller; |
|||
|
|||
import awesome.group.RequestContext; |
|||
import awesome.group.aop.RestApi; |
|||
import awesome.group.game.service.WxService; |
|||
import awesome.group.game.service.common.response.R; |
|||
import jakarta.annotation.Resource; |
|||
import org.springframework.web.bind.annotation.PostMapping; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RequestParam; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
|
|||
@RestController |
|||
@RequestMapping("/api/game/auth") |
|||
public class LoginController { |
|||
|
|||
@Resource |
|||
private WxService wxService; |
|||
|
|||
/** |
|||
* 微信登陆 |
|||
* 返回jwt token |
|||
* |
|||
* @param code 微信wx.login获取到的code |
|||
*/ |
|||
@PostMapping("/login") |
|||
@RestApi |
|||
public R<String> wxLogin(@RequestParam String code, @RequestParam String appId) { |
|||
return new R<>(R.CODE_SUCCESS, "", wxService.login(code, appId)); |
|||
} |
|||
|
|||
/** |
|||
* 更新用户信息 |
|||
* 必须先登陆,需在login调用30分钟内 |
|||
* |
|||
* @param rawData 微信wx.getUserInfo获取到到rawData |
|||
* @param signature 微信wx.getUserInfo获取到到signature |
|||
*/ |
|||
@PostMapping("/updateUserInfo") |
|||
@RestApi |
|||
public R<Void> updateUserInfo(@RequestParam String rawData, @RequestParam String signature) { |
|||
Integer userId = RequestContext.getUid(); |
|||
wxService.updateUserInfo(userId, rawData, signature); |
|||
return new R<>(R.CODE_SUCCESS, "", null); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,26 @@ |
|||
package awesome.group.interceptor; |
|||
|
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; |
|||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
|||
|
|||
@Configuration //配置类
|
|||
public class InterceptorConfig implements WebMvcConfigurer { |
|||
|
|||
|
|||
@Bean //手动注入 JwtInterceptor 对象
|
|||
public JwtInterceptor jwtInterceptor() { |
|||
return new JwtInterceptor(); |
|||
} |
|||
|
|||
@Override |
|||
public void addInterceptors(InterceptorRegistry registry) { |
|||
registry.addInterceptor(jwtInterceptor()) |
|||
//拦截的路径
|
|||
.addPathPatterns("/**") |
|||
//不拦截的路径
|
|||
.excludePathPatterns("/api/game/auth/login") |
|||
.excludePathPatterns("/api/game/user/demo"); |
|||
} |
|||
} |
@ -0,0 +1,45 @@ |
|||
package awesome.group.interceptor; |
|||
|
|||
import awesome.group.RequestContext; |
|||
import awesome.group.game.service.common.log.L; |
|||
import awesome.group.game.service.common.response.R; |
|||
import awesome.group.game.service.util.JwtUtils; |
|||
import com.auth0.jwt.exceptions.TokenExpiredException; |
|||
import com.google.gson.Gson; |
|||
import jakarta.servlet.http.HttpServletRequest; |
|||
import jakarta.servlet.http.HttpServletResponse; |
|||
import org.springframework.util.StringUtils; |
|||
import org.springframework.web.servlet.HandlerInterceptor; |
|||
|
|||
import java.io.PrintWriter; |
|||
import java.util.List; |
|||
|
|||
public class JwtInterceptor implements HandlerInterceptor { |
|||
|
|||
@Override |
|||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { |
|||
String token = request.getHeader("Authorization"); |
|||
List<String> openApi = List.of("/api/game/user/demo"); |
|||
try { |
|||
if (StringUtils.hasText(token)) { |
|||
Integer userId = JwtUtils.parseToken(token); |
|||
RequestContext.init(request, response, userId); |
|||
return true; |
|||
} |
|||
} catch (TokenExpiredException e) { |
|||
//忽略
|
|||
} catch (Exception e) { |
|||
L.trace("parseTokenError", "token:" + token, e); |
|||
} |
|||
String path = request.getRequestURI().substring(request.getContextPath().length()).replaceAll("[/]+$", ""); |
|||
for (String s : openApi) { |
|||
if (path.startsWith(s)) { |
|||
return true; |
|||
} |
|||
} |
|||
PrintWriter out = response.getWriter(); |
|||
Gson gson = new Gson(); |
|||
out.print(gson.toJson(new R<>(-88888, "not log in", null))); |
|||
return false; |
|||
} |
|||
} |
@ -0,0 +1,41 @@ |
|||
spring: |
|||
application: |
|||
name: game |
|||
config: |
|||
activate: |
|||
on-profile: default |
|||
aop: |
|||
auto: true |
|||
proxy-target-class: true |
|||
pid: |
|||
file: pid #pid文件名 |
|||
datasource: |
|||
game: |
|||
type: com.zaxxer.hikari.HikariDataSource |
|||
jdbcUrl: jdbc:mysql://localhost:3306/game?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&connectTimeout=300&socketTimeout=2000&serverTimezone=Hongkong&zeroDateTimeBehavior=convertToNull |
|||
username: root |
|||
password: |
|||
connectionTimeout: 300 |
|||
maximumPoolSize: 5 |
|||
minimumIdle: 1 |
|||
idleTimeout: 600000 #This property controls the maximum amount of time that a connection is allowed to sit idle in the pool |
|||
maxLifetime: 3600000 #This property controls the maximum lifetime of a connection in the pool |
|||
|
|||
mybatis-plus: |
|||
configuration: |
|||
map-underscore-to-camel-case: true #数据库字段下划线映射java对象属性用驼峰命名 |
|||
|
|||
server: |
|||
port: 9001 |
|||
tomcat: |
|||
connection-timeout: 60000 #Time in milliseconds that connectors will wait for another HTTP request before closing the connection. |
|||
accept-count: 100 # Maximum queue length for incoming connection requests when all possible request processing threads are in use. |
|||
max-connections: 256 # Maximum number of connections that the server will accept and process at any given time. |
|||
max-http-form-post-size: 20MB |
|||
threads: |
|||
max: 16 |
|||
min-spare: 4 |
|||
servlet: |
|||
encoding: |
|||
enabled: true |
|||
charset: utf-8 |
@ -0,0 +1,42 @@ |
|||
spring: |
|||
application: |
|||
name: game |
|||
config: |
|||
activate: |
|||
on-profile: test |
|||
aop: |
|||
auto: true |
|||
proxy-target-class: true |
|||
pid: |
|||
file: pid #pid文件名 |
|||
|
|||
datasource: |
|||
game: |
|||
type: com.zaxxer.hikari.HikariDataSource |
|||
jdbcUrl: jdbc:mysql://localhost:3306/game?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&connectTimeout=300&socketTimeout=2000&serverTimezone=Hongkong&zeroDateTimeBehavior=convertToNull |
|||
username: game |
|||
password: Vd12luSjmwAXuwDH |
|||
connectionTimeout: 300 |
|||
maximumPoolSize: 5 |
|||
minimumIdle: 1 |
|||
idleTimeout: 600000 #This property controls the maximum amount of time that a connection is allowed to sit idle in the pool |
|||
maxLifetime: 3600000 #This property controls the maximum lifetime of a connection in the pool |
|||
|
|||
mybatis-plus: |
|||
configuration: |
|||
map-underscore-to-camel-case: true #数据库字段下划线映射java对象属性用驼峰命名 |
|||
|
|||
server: |
|||
port: 8000 |
|||
tomcat: |
|||
connection-timeout: 60000 #Time in milliseconds that connectors will wait for another HTTP request before closing the connection. |
|||
accept-count: 100 # Maximum queue length for incoming connection requests when all possible request processing threads are in use. |
|||
max-connections: 256 # Maximum number of connections that the server will accept and process at any given time. |
|||
threads: |
|||
max: 16 |
|||
min-spare: 4 |
|||
servlet: |
|||
encoding: |
|||
enabled: true |
|||
charset: utf-8 |
|||
|
@ -0,0 +1,4 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<configuration> |
|||
<include resource="logconfig.xml"/> |
|||
</configuration> |
@ -0,0 +1,114 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<included> |
|||
<property name="LOG_HOME" value="logs"/> |
|||
|
|||
<conversionRule conversionWord="hostIp" converterClass="awesome.group.game.service.common.log.IPLogConfig"/> |
|||
|
|||
<springProperty scope="context" name="springAppName" source="spring.application.name"/> |
|||
|
|||
|
|||
<property name="ENCODER_PATTERN" |
|||
value="[%level][%d{yyyy-MM-dd HH:mm:ss}][%t][${springAppName:-}][%X{X-B3-TraceId:-}][%X{X-B3-SpanId:-}][%X{X-B3-ParentSpanId:-}][%hostIp][%logger{5}] - %msg%n"/> |
|||
|
|||
<!-- 文件日志:输出全部日志到文件 --> |
|||
<appender name="OUTPUT_FILE" |
|||
class="ch.qos.logback.core.rolling.RollingFileAppender"> |
|||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"> |
|||
<level>INFO</level> |
|||
</filter> |
|||
<file>${LOG_HOME}/biz.log</file> |
|||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
|||
<fileNamePattern>${LOG_HOME}/biz.log.%d{yyyyMMddHH}</fileNamePattern> |
|||
<maxHistory>72</maxHistory> |
|||
<cleanHistoryOnStart>true</cleanHistoryOnStart> |
|||
</rollingPolicy> |
|||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> |
|||
<pattern>${ENCODER_PATTERN}</pattern> |
|||
</encoder> |
|||
</appender> |
|||
<!-- 异步输出 --> |
|||
<appender name="ASYNC_OUTPUT_FILE" class="ch.qos.logback.classic.AsyncAppender"> |
|||
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 --> |
|||
<discardingThreshold>0</discardingThreshold> |
|||
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 --> |
|||
<queueSize>64</queueSize> |
|||
<!-- 添加附加的appender,最多只能添加一个 --> |
|||
<appender-ref ref="OUTPUT_FILE"/> |
|||
</appender> |
|||
|
|||
|
|||
<!--trace log pattern--> |
|||
<property name="TRACE_ENCODER_PATTERN" |
|||
value="[%level][%d{yyyy-MM-dd HH:mm:ss}][%t][${springAppName:-}][%X{X-B3-TraceId:-}][%X{X-B3-SpanId:-}][%X{X-B3-ParentSpanId:-}][%hostIp][%X{key_action:-}] - %msg%n"/> |
|||
<!--trace log 输出文件--> |
|||
<appender name="TRACE_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
|||
<file>${LOG_HOME}/trace.log</file> |
|||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
|||
<fileNamePattern>${LOG_HOME}/trace.log.%d{yyyyMMddHH}</fileNamePattern> |
|||
<maxHistory>72</maxHistory> |
|||
<cleanHistoryOnStart>true</cleanHistoryOnStart> |
|||
</rollingPolicy> |
|||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> |
|||
<Pattern>${TRACE_ENCODER_PATTERN}</Pattern> |
|||
</encoder> |
|||
</appender> |
|||
|
|||
<!-- 异步输出 --> |
|||
<appender name="ASYNC_TRACE_FILE" class="ch.qos.logback.classic.AsyncAppender"> |
|||
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 --> |
|||
<discardingThreshold>0</discardingThreshold> |
|||
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 --> |
|||
<queueSize>64</queueSize> |
|||
<!-- 添加附加的appender,最多只能添加一个 --> |
|||
<appender-ref ref="TRACE_FILE"/> |
|||
</appender> |
|||
|
|||
<!-- api日志--> |
|||
<property name="API_ENCODER_PATTERN" |
|||
value="[%level][%d{yyyy-MM-dd HH:mm:ss}][%t][${springAppName:-}][%X{key_method_name:-}][%X{X-B3-TraceId:-}][%X{X-B3-SpanId:-}][%X{X-B3-ParentSpanId:-}][%hostIp][%X{key_cost:-}][%X{key_status:-}][%X{key_jwt:-}] - %msg%n"/> |
|||
<appender name="API_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
|||
<file>${LOG_HOME}/api.log</file> |
|||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
|||
<fileNamePattern>${LOG_HOME}/api.log.%d{yyyyMMddHH}</fileNamePattern> |
|||
<maxHistory>24</maxHistory> |
|||
<cleanHistoryOnStart>true</cleanHistoryOnStart> |
|||
</rollingPolicy> |
|||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> |
|||
<Pattern>${API_ENCODER_PATTERN}</Pattern> |
|||
</encoder> |
|||
</appender> |
|||
|
|||
<!-- 异步输出 --> |
|||
<appender name="ASYNC_API_FILE" class="ch.qos.logback.classic.AsyncAppender"> |
|||
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 --> |
|||
<discardingThreshold>0</discardingThreshold> |
|||
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 --> |
|||
<queueSize>64</queueSize> |
|||
<!-- 添加附加的appender,最多只能添加一个 --> |
|||
<appender-ref ref="API_FILE"/> |
|||
</appender> |
|||
<!-- api日志--> |
|||
|
|||
|
|||
<logger name="log.trace" level="INFO" additivity="false"> |
|||
<appender-ref ref="ASYNC_TRACE_FILE"/> |
|||
</logger> |
|||
|
|||
<logger name="log.api" level="INFO" additivity="false"> |
|||
<appender-ref ref="ASYNC_API_FILE"/> |
|||
</logger> |
|||
|
|||
|
|||
<logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR"/> |
|||
<logger name="org.apache.catalina.util.LifecycleBase" level="ERROR"/> |
|||
<logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN"/> |
|||
<logger name="org.apache.sshd.common.util.SecurityUtils" level="WARN"/> |
|||
<logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN"/> |
|||
<logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="ERROR"/> |
|||
<logger name="org.hibernate.validator.internal.util.Version" level="WARN"/> |
|||
|
|||
<root additivity="false"> |
|||
<level value="INFO"/> |
|||
<appender-ref ref="ASYNC_OUTPUT_FILE"/> |
|||
</root> |
|||
</included> |
@ -0,0 +1,73 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
|
|||
<groupId>awesome.group</groupId> |
|||
<artifactId>game</artifactId> |
|||
<version>1.0-SNAPSHOT</version> |
|||
<modules> |
|||
<module>game-dao</module> |
|||
<module>game-service</module> |
|||
<module>game-web</module> |
|||
</modules> |
|||
<packaging>pom</packaging> |
|||
<properties> |
|||
<java.version>17</java.version> |
|||
<awesome.group.common.version>1.0-SNAPSHOT</awesome.group.common.version> |
|||
</properties> |
|||
|
|||
<parent> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-parent</artifactId> |
|||
<version>3.1.0</version> |
|||
</parent> |
|||
|
|||
<dependencyManagement> |
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>mysql</groupId> |
|||
<artifactId>mysql-connector-java</artifactId> |
|||
<version>8.0.16</version> |
|||
</dependency> |
|||
<!-- mybatis-plus 3.5.3 才支持 spring boot 3--> |
|||
<dependency> |
|||
<groupId>com.baomidou</groupId> |
|||
<artifactId>mybatis-plus-boot-starter</artifactId> |
|||
<version>3.5.3</version> |
|||
</dependency> |
|||
|
|||
<!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp --> |
|||
<dependency> |
|||
<groupId>com.squareup.okhttp3</groupId> |
|||
<artifactId>okhttp</artifactId> |
|||
<version>4.11.0</version> |
|||
</dependency> |
|||
|
|||
</dependencies> |
|||
</dependencyManagement> |
|||
|
|||
<build> |
|||
<plugins> |
|||
<!--源码打包--> |
|||
<plugin> |
|||
<groupId>org.apache.maven.plugins</groupId> |
|||
<artifactId>maven-source-plugin</artifactId> |
|||
<version>3.2.1</version> |
|||
<configuration> |
|||
<attach>true</attach> |
|||
</configuration> |
|||
<executions> |
|||
<execution> |
|||
<phase>compile</phase><!--指定绑定的声明周期阶段--> |
|||
<goals> |
|||
<goal>jar</goal> |
|||
</goals> |
|||
</execution> |
|||
</executions> |
|||
</plugin> |
|||
</plugins> |
|||
</build> |
|||
|
|||
</project> |
@ -0,0 +1,20 @@ |
|||
#!/bin/bash |
|||
|
|||
project=`cat env.conf | awk -F , '{print$1}'` |
|||
profile=`cat env.conf | awk -F , '{print$2}'` |
|||
|
|||
logdir=logs |
|||
if [ ! -d "$logdir" ];then |
|||
mkdir $logdir |
|||
fi |
|||
|
|||
targetdir=$project/target |
|||
jar=$project-1.0-SNAPSHOT.jar |
|||
|
|||
|
|||
|
|||
rm -rf jar |
|||
mkdir jar |
|||
cp $targetdir/$jar jar/ |
|||
|
|||
java -server -Xmx1G -Xms1G -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+UseCompressedOops -XX:+UseCompressedClassPointers -verbose:gc -Xlog:gc*:logs/gc.log:time,l,tg:filecount=7,filesize=16M -jar jar/$jar --spring.profiles.active=$profile > run.log 2>&1 & echo $! > pid |
@ -0,0 +1,14 @@ |
|||
#!/bin/bash |
|||
|
|||
# ======== 下面是原来的下线逻辑 =========== |
|||
if [ -f pid ]; then |
|||
if kill `cat pid` ; then |
|||
echo 'kill process succeed' |
|||
else |
|||
echo 'kill process fail' |
|||
exit 1 |
|||
fi |
|||
else |
|||
echo 'pid file not exists' |
|||
exit 1 |
|||
fi |
Loading…
Reference in new issue