diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..934919e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+/.idea/
+/pid
+**/target
+/logs/
+*.log
\ No newline at end of file
diff --git a/build b/build
new file mode 100755
index 0000000..b207c4b
--- /dev/null
+++ b/build
@@ -0,0 +1,9 @@
+#!/bin/bash
+mvn clean package -U -DskipTests > /dev/null
+
+if [ $? -eq 0 ]; then
+ echo "success"
+else
+ echo "fail"
+fi
+
diff --git a/game-dao/pom.xml b/game-dao/pom.xml
new file mode 100644
index 0000000..a2227bf
--- /dev/null
+++ b/game-dao/pom.xml
@@ -0,0 +1,50 @@
+
+
+
+ game
+ awesome.group
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ game-dao
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+
+
+ mysql
+ mysql-connector-java
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+
+ org.apache.tomcat
+ tomcat-jdbc
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ org.projectlombok
+ lombok
+
+
+
+
\ No newline at end of file
diff --git a/game-dao/src/main/java/awesome/group/game/dao/bean/GameApp.java b/game-dao/src/main/java/awesome/group/game/dao/bean/GameApp.java
new file mode 100644
index 0000000..5446449
--- /dev/null
+++ b/game-dao/src/main/java/awesome/group/game/dao/bean/GameApp.java
@@ -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;
+}
diff --git a/game-dao/src/main/java/awesome/group/game/dao/bean/User.java b/game-dao/src/main/java/awesome/group/game/dao/bean/User.java
new file mode 100644
index 0000000..f1ae3c3
--- /dev/null
+++ b/game-dao/src/main/java/awesome/group/game/dao/bean/User.java
@@ -0,0 +1,8 @@
+package awesome.group.game.dao.bean;
+
+import lombok.Data;
+
+@Data
+public class User {
+ private Integer id;
+}
diff --git a/game-dao/src/main/java/awesome/group/game/dao/bean/WeGameUser.java b/game-dao/src/main/java/awesome/group/game/dao/bean/WeGameUser.java
new file mode 100644
index 0000000..7b34c03
--- /dev/null
+++ b/game-dao/src/main/java/awesome/group/game/dao/bean/WeGameUser.java
@@ -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;
+}
diff --git a/game-dao/src/main/java/awesome/group/game/dao/bean/WxUserInfo.java b/game-dao/src/main/java/awesome/group/game/dao/bean/WxUserInfo.java
new file mode 100644
index 0000000..cbf150a
--- /dev/null
+++ b/game-dao/src/main/java/awesome/group/game/dao/bean/WxUserInfo.java
@@ -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;
+}
diff --git a/game-dao/src/main/java/awesome/group/game/dao/config/BasicDataSourceConfig.java b/game-dao/src/main/java/awesome/group/game/dao/config/BasicDataSourceConfig.java
new file mode 100644
index 0000000..63fdf4e
--- /dev/null
+++ b/game-dao/src/main/java/awesome/group/game/dao/config/BasicDataSourceConfig.java
@@ -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);
+ }
+}
diff --git a/game-dao/src/main/java/awesome/group/game/dao/config/MybatisGeneralConfig.java b/game-dao/src/main/java/awesome/group/game/dao/config/MybatisGeneralConfig.java
new file mode 100644
index 0000000..ac5e351
--- /dev/null
+++ b/game-dao/src/main/java/awesome/group/game/dao/config/MybatisGeneralConfig.java
@@ -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();
+ }
+
+}
\ No newline at end of file
diff --git a/game-dao/src/main/java/awesome/group/game/dao/mapper/GameAppMapper.java b/game-dao/src/main/java/awesome/group/game/dao/mapper/GameAppMapper.java
new file mode 100644
index 0000000..8b30a95
--- /dev/null
+++ b/game-dao/src/main/java/awesome/group/game/dao/mapper/GameAppMapper.java
@@ -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 {
+ @Select("select * from game_app where app_id = #{appId}")
+ GameApp query(@Param("appId")String appId);
+}
diff --git a/game-dao/src/main/java/awesome/group/game/dao/mapper/UserMapper.java b/game-dao/src/main/java/awesome/group/game/dao/mapper/UserMapper.java
new file mode 100644
index 0000000..2a4838e
--- /dev/null
+++ b/game-dao/src/main/java/awesome/group/game/dao/mapper/UserMapper.java
@@ -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 {
+}
diff --git a/game-dao/src/main/java/awesome/group/game/dao/mapper/WeGameUserMapper.java b/game-dao/src/main/java/awesome/group/game/dao/mapper/WeGameUserMapper.java
new file mode 100644
index 0000000..f96c0ef
--- /dev/null
+++ b/game-dao/src/main/java/awesome/group/game/dao/mapper/WeGameUserMapper.java
@@ -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 {
+ @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);
+}
diff --git a/game-service/pom.xml b/game-service/pom.xml
new file mode 100644
index 0000000..34507b0
--- /dev/null
+++ b/game-service/pom.xml
@@ -0,0 +1,69 @@
+
+
+
+ game
+ awesome.group
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ game-service
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+ awesome.group
+ game-dao
+ 1.0-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+ org.springframework.security
+ spring-security-crypto
+ 5.6.7
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+
+
+ junit
+ junit
+ test
+
+
+ com.auth0
+ java-jwt
+ 3.14.0
+
+
+
+ com.google.code.gson
+ gson
+
+
+
+
+ com.squareup.okhttp3
+ okhttp
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/game-service/src/main/java/awesome/group/game/service/UserService.java b/game-service/src/main/java/awesome/group/game/service/UserService.java
new file mode 100644
index 0000000..d115ddb
--- /dev/null
+++ b/game-service/src/main/java/awesome/group/game/service/UserService.java
@@ -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);
+ }
+}
diff --git a/game-service/src/main/java/awesome/group/game/service/WxService.java b/game-service/src/main/java/awesome/group/game/service/WxService.java
new file mode 100644
index 0000000..26b3678
--- /dev/null
+++ b/game-service/src/main/java/awesome/group/game/service/WxService.java
@@ -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 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);
+ }
+}
diff --git a/game-service/src/main/java/awesome/group/game/service/bo/Code2SessionBo.java b/game-service/src/main/java/awesome/group/game/service/bo/Code2SessionBo.java
new file mode 100644
index 0000000..04f0cc1
--- /dev/null
+++ b/game-service/src/main/java/awesome/group/game/service/bo/Code2SessionBo.java
@@ -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;
+ }
+}
diff --git a/game-service/src/main/java/awesome/group/game/service/bo/WxResponse.java b/game-service/src/main/java/awesome/group/game/service/bo/WxResponse.java
new file mode 100644
index 0000000..7fdb5c6
--- /dev/null
+++ b/game-service/src/main/java/awesome/group/game/service/bo/WxResponse.java
@@ -0,0 +1,6 @@
+package awesome.group.game.service.bo;
+
+public class WxResponse {
+ public Integer errcode;
+ public String errmsg;
+}
diff --git a/game-service/src/main/java/awesome/group/game/service/common/exception/PaganiException.java b/game-service/src/main/java/awesome/group/game/service/common/exception/PaganiException.java
new file mode 100644
index 0000000..4562955
--- /dev/null
+++ b/game-service/src/main/java/awesome/group/game/service/common/exception/PaganiException.java
@@ -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;
+ }
+}
diff --git a/game-service/src/main/java/awesome/group/game/service/common/exception/PaganiExceptionCode.java b/game-service/src/main/java/awesome/group/game/service/common/exception/PaganiExceptionCode.java
new file mode 100644
index 0000000..8d2445a
--- /dev/null
+++ b/game-service/src/main/java/awesome/group/game/service/common/exception/PaganiExceptionCode.java
@@ -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;
+}
diff --git a/game-service/src/main/java/awesome/group/game/service/common/log/Const.java b/game-service/src/main/java/awesome/group/game/service/common/log/Const.java
new file mode 100644
index 0000000..47c87e6
--- /dev/null
+++ b/game-service/src/main/java/awesome/group/game/service/common/log/Const.java
@@ -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";
+}
diff --git a/game-service/src/main/java/awesome/group/game/service/common/log/IPLogConfig.java b/game-service/src/main/java/awesome/group/game/service/common/log/IPLogConfig.java
new file mode 100644
index 0000000..a9b02f7
--- /dev/null
+++ b/game-service/src/main/java/awesome/group/game/service/common/log/IPLogConfig.java
@@ -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;
+ }
+}
diff --git a/game-service/src/main/java/awesome/group/game/service/common/log/L.java b/game-service/src/main/java/awesome/group/game/service/common/log/L.java
new file mode 100644
index 0000000..63a9d7a
--- /dev/null
+++ b/game-service/src/main/java/awesome/group/game/service/common/log/L.java
@@ -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);
+ }
+
+}
diff --git a/game-service/src/main/java/awesome/group/game/service/common/log/PaganiLogger.java b/game-service/src/main/java/awesome/group/game/service/common/log/PaganiLogger.java
new file mode 100644
index 0000000..a7dd4bc
--- /dev/null
+++ b/game-service/src/main/java/awesome/group/game/service/common/log/PaganiLogger.java
@@ -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 mLoggers = new ConcurrentHashMap();
+
+ 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;
+ }
+}
diff --git a/game-service/src/main/java/awesome/group/game/service/common/log/Status.java b/game-service/src/main/java/awesome/group/game/service/common/log/Status.java
new file mode 100644
index 0000000..edd947e
--- /dev/null
+++ b/game-service/src/main/java/awesome/group/game/service/common/log/Status.java
@@ -0,0 +1,6 @@
+package awesome.group.game.service.common.log;
+
+public enum Status {
+ FAILED,
+ SUCCESS
+}
diff --git a/game-service/src/main/java/awesome/group/game/service/common/response/R.java b/game-service/src/main/java/awesome/group/game/service/common/response/R.java
new file mode 100644
index 0000000..0eabe00
--- /dev/null
+++ b/game-service/src/main/java/awesome/group/game/service/common/response/R.java
@@ -0,0 +1,49 @@
+package awesome.group.game.service.common.response;
+
+public class R {
+ 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;
+ }
+
+}
diff --git a/game-service/src/main/java/awesome/group/game/service/constant/TokenConstants.java b/game-service/src/main/java/awesome/group/game/service/constant/TokenConstants.java
new file mode 100644
index 0000000..d9f7362
--- /dev/null
+++ b/game-service/src/main/java/awesome/group/game/service/constant/TokenConstants.java
@@ -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";
+}
diff --git a/game-service/src/main/java/awesome/group/game/service/util/EncryptUtil.java b/game-service/src/main/java/awesome/group/game/service/util/EncryptUtil.java
new file mode 100644
index 0000000..8f9c857
--- /dev/null
+++ b/game-service/src/main/java/awesome/group/game/service/util/EncryptUtil.java
@@ -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;
+ }
+}
diff --git a/game-service/src/main/java/awesome/group/game/service/util/HttpUtils.java b/game-service/src/main/java/awesome/group/game/service/util/HttpUtils.java
new file mode 100644
index 0000000..d9488df
--- /dev/null
+++ b/game-service/src/main/java/awesome/group/game/service/util/HttpUtils.java
@@ -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 postForm(String url, Map query, Map form,
+ Map headers, Class dataType) {
+ FormBody.Builder bodyBuilder = new FormBody.Builder();
+ for (Map.Entry kv : form.entrySet()) {
+ bodyBuilder.add(kv.getKey(), kv.getValue());
+ }
+ HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
+ for (Map.Entry 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 postJson(String url, Object body, Map headers, Class 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 get(String url, Map queryStr, Map headers, Class dataType) {
+ HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
+ for (Map.Entry 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 doHttpExecute(String msg, Request request, Class 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);
+ }
+ }
+
+}
diff --git a/game-service/src/main/java/awesome/group/game/service/util/JwtUtils.java b/game-service/src/main/java/awesome/group/game/service/util/JwtUtils.java
new file mode 100644
index 0000000..81837c0
--- /dev/null
+++ b/game-service/src/main/java/awesome/group/game/service/util/JwtUtils.java
@@ -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);
+ }
+}
diff --git a/game-web/pom.xml b/game-web/pom.xml
new file mode 100644
index 0000000..287a7cc
--- /dev/null
+++ b/game-web/pom.xml
@@ -0,0 +1,51 @@
+
+
+ 4.0.0
+
+
+
+ game
+ awesome.group
+ 1.0-SNAPSHOT
+
+
+ game-web
+
+
+ awesome.group
+ game-dao
+ 1.0-SNAPSHOT
+
+
+ awesome.group
+ game-service
+ 1.0-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
\ No newline at end of file
diff --git a/game-web/src/main/java/awesome/group/Application.java b/game-web/src/main/java/awesome/group/Application.java
new file mode 100644
index 0000000..7910c10
--- /dev/null
+++ b/game-web/src/main/java/awesome/group/Application.java
@@ -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);
+ }
+}
diff --git a/game-web/src/main/java/awesome/group/RequestContext.java b/game-web/src/main/java/awesome/group/RequestContext.java
new file mode 100644
index 0000000..4972330
--- /dev/null
+++ b/game-web/src/main/java/awesome/group/RequestContext.java
@@ -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