From 4017fe347792c7e28695c455a40874f0c647cc9b Mon Sep 17 00:00:00 2001 From: Helius <wangdoubleone@gmail.com> Date: Fri, 26 Nov 2021 16:46:45 +0800 Subject: [PATCH] add fish hit websocket --- src/main/java/com/xcong/excoin/modules/fish/dao/MemberAccountGoldDao.java | 2 src/main/java/com/xcong/excoin/configurations/WebSocketConfig.java | 26 ++ src/main/resources/mapper/fish/MemberAccountGoldDao.xml | 3 src/main/java/com/xcong/excoin/common/initialization/InitJob.java | 62 +++++ src/main/java/com/xcong/excoin/configurations/RabbitMqConfig.java | 22 ++ src/main/java/com/xcong/excoin/websocket/fish/HitFishWebSocket.java | 119 ++++++++++ src/main/java/com/xcong/excoin/configurations/interceptor/WebCommonInterceptor.java | 34 +++ src/main/java/com/xcong/excoin/modules/fish/controller/MemberCannonController.java | 1 src/main/resources/application-test.yml | 1 src/main/java/com/xcong/excoin/configurations/security/WebSecurityConfig.java | 1 src/main/java/com/xcong/excoin/rabbit/consumer/FishHitConsumer.java | 46 ++++ src/main/java/com/xcong/excoin/websocket/fish/model/MsgModel.java | 34 +++ src/main/java/com/xcong/excoin/websocket/handler/FishHitWebSocketHandler.java | 118 ++++++++++ src/main/java/com/xcong/excoin/common/contants/AppContants.java | 3 src/main/java/com/xcong/excoin/common/system/entity/DataDictionaryCustom.java | 22 ++ src/main/resources/mapper/fish/CannonOwnRecordDao.xml | 9 src/main/java/com/xcong/excoin/ExcoinApplication.java | 4 src/main/java/com/xcong/excoin/modules/fish/vo/OwnCannonVo.java | 12 src/main/java/com/xcong/excoin/websocket/handler/FishHitWebSocketHandshakeInterceptor.java | 41 +++ src/main/resources/mapper/common/DataDictionaryCustomMapper.xml | 13 + src/main/java/com/xcong/excoin/common/system/dao/DataDictionaryCustomDao.java | 14 + src/main/resources/application.yml | 7 src/main/java/com/xcong/excoin/rabbit/producer/FishHitProducer.java | 37 +++ 23 files changed, 614 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/xcong/excoin/ExcoinApplication.java b/src/main/java/com/xcong/excoin/ExcoinApplication.java index a2a7e2b..9987fca 100644 --- a/src/main/java/com/xcong/excoin/ExcoinApplication.java +++ b/src/main/java/com/xcong/excoin/ExcoinApplication.java @@ -4,6 +4,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.web.socket.config.annotation.EnableWebSocket; import springfox.documentation.swagger2.annotations.EnableSwagger2; /** @@ -11,8 +12,9 @@ */ @EnableScheduling @EnableSwagger2 +@EnableWebSocket @SpringBootApplication -@MapperScan("com.xcong.excoin.modules.*.dao") +@MapperScan({"com.xcong.excoin.modules.*.dao", "com.xcong.excoin.common.*.dao"}) public class ExcoinApplication { public static void main(String[] args) { diff --git a/src/main/java/com/xcong/excoin/common/contants/AppContants.java b/src/main/java/com/xcong/excoin/common/contants/AppContants.java index 7fde51c..69899c5 100644 --- a/src/main/java/com/xcong/excoin/common/contants/AppContants.java +++ b/src/main/java/com/xcong/excoin/common/contants/AppContants.java @@ -83,4 +83,7 @@ public static final BigDecimal DEFAULT_PRICE = BigDecimal.valueOf(1.28); + public static final String DICTIONARY_TYPE_FISH = "FISH_TYPE"; + + public static final String CANNON_TYPE = "CANNON_TYPE"; } diff --git a/src/main/java/com/xcong/excoin/common/initialization/InitJob.java b/src/main/java/com/xcong/excoin/common/initialization/InitJob.java new file mode 100644 index 0000000..9d696a8 --- /dev/null +++ b/src/main/java/com/xcong/excoin/common/initialization/InitJob.java @@ -0,0 +1,62 @@ +package com.xcong.excoin.common.initialization; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.xcong.excoin.common.contants.AppContants; +import com.xcong.excoin.common.system.dao.DataDictionaryCustomDao; +import com.xcong.excoin.common.system.entity.DataDictionaryCustom; +import com.xcong.excoin.modules.fish.dao.CannonSettingDao; +import com.xcong.excoin.modules.fish.entity.CannonSetting; +import com.xcong.excoin.utils.RedisUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author wzy + * @date 2021-11-26 + **/ +@Slf4j +@Component +public class InitJob { + + @Autowired + private RedisUtils redisUtils; + @Autowired + private DataDictionaryCustomDao dictionaryCustomDao; + @Autowired + private CannonSettingDao cannonSettingDao; + + @PostConstruct + public void init() { + log.info("####项目初始化数据####"); + + // 初始化鱼类数据到redis缓存 + List<DataDictionaryCustom> fishTypes = dictionaryCustomDao.selectDicByType(AppContants.DICTIONARY_TYPE_FISH); + if (CollUtil.isNotEmpty(fishTypes)) { + Map<String, Object> map = new HashMap<>(); + for (DataDictionaryCustom fishType : fishTypes) { + Integer cnt = StrUtil.isEmpty(fishType.getValue()) ? 0 : Integer.parseInt(fishType.getValue()); + map.put(fishType.getCode(), cnt); + + redisUtils.hmset(AppContants.DICTIONARY_TYPE_FISH, map); + } + } + + // 初始化大炮数据到redis缓存 + List<CannonSetting> settings = cannonSettingDao.selectByMap(null); + if (CollUtil.isNotEmpty(settings)) { + Map<String, Object> map = new HashMap<>(); + for (CannonSetting setting : settings) { + map.put(setting.getCode(), setting.getGoldConsume()); + + redisUtils.hmset(AppContants.CANNON_TYPE, map); + } + } + } +} diff --git a/src/main/java/com/xcong/excoin/common/system/dao/DataDictionaryCustomDao.java b/src/main/java/com/xcong/excoin/common/system/dao/DataDictionaryCustomDao.java new file mode 100644 index 0000000..c30bec4 --- /dev/null +++ b/src/main/java/com/xcong/excoin/common/system/dao/DataDictionaryCustomDao.java @@ -0,0 +1,14 @@ +package com.xcong.excoin.common.system.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.xcong.excoin.common.system.entity.DataDictionaryCustom; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface DataDictionaryCustomDao extends BaseMapper<DataDictionaryCustom> { + + List<DataDictionaryCustom> selectDicByType(String type); + + DataDictionaryCustom selectDicDataByTypeAndCode(@Param("type") String type, @Param("code") String code); +} diff --git a/src/main/java/com/xcong/excoin/common/system/entity/DataDictionaryCustom.java b/src/main/java/com/xcong/excoin/common/system/entity/DataDictionaryCustom.java new file mode 100644 index 0000000..a7f7c6f --- /dev/null +++ b/src/main/java/com/xcong/excoin/common/system/entity/DataDictionaryCustom.java @@ -0,0 +1,22 @@ +package com.xcong.excoin.common.system.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.xcong.excoin.common.system.base.BaseEntity; +import lombok.Data; + +/** + * @author wzy + * @date 2021-09-25 + **/ +@Data +@TableName("data_dictionary_custom") +public class DataDictionaryCustom extends BaseEntity { + + private String type; + + private String code; + + private String value; + + private String description; +} diff --git a/src/main/java/com/xcong/excoin/configurations/RabbitMqConfig.java b/src/main/java/com/xcong/excoin/configurations/RabbitMqConfig.java index 3cddbe2..ab4d61b 100644 --- a/src/main/java/com/xcong/excoin/configurations/RabbitMqConfig.java +++ b/src/main/java/com/xcong/excoin/configurations/RabbitMqConfig.java @@ -43,6 +43,12 @@ public static final String ROUTING_KEY_USDT_ADDRESS = "routing_key_usdt_address"; + public static final String EXCHANGE_FISH_HIT = "EXCHANGE_FISH_HIT"; + + public static final String QUEUE_FISH_HIT = "QUEUE_FISH_HIT"; + + public static final String ROUTING_KEY_FISH_HIT = "ROUTING_KEY_FISH_HIT"; + /** * 撮合交易 */ @@ -232,6 +238,22 @@ } @Bean + public DirectExchange fishHitExchange() { + return new DirectExchange(EXCHANGE_FISH_HIT); + } + + + @Bean + public Queue fishHitQueue() { + return new Queue(QUEUE_FISH_HIT, true); + } + + @Bean + public Binding fishHitbinding() { + return BindingBuilder.bind(fishHitQueue()).to(fishHitExchange()).with(ROUTING_KEY_FISH_HIT); + } + + @Bean public DirectExchange usdtAddressExchange() { return new DirectExchange(EXCHANGE_USDT_ADDRESS); } diff --git a/src/main/java/com/xcong/excoin/configurations/WebSocketConfig.java b/src/main/java/com/xcong/excoin/configurations/WebSocketConfig.java index 51711c3..3ba5508 100644 --- a/src/main/java/com/xcong/excoin/configurations/WebSocketConfig.java +++ b/src/main/java/com/xcong/excoin/configurations/WebSocketConfig.java @@ -1,11 +1,26 @@ package com.xcong.excoin.configurations; +import com.xcong.excoin.websocket.handler.FishHitWebSocketHandler; +import com.xcong.excoin.websocket.handler.FishHitWebSocketHandshakeInterceptor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; +import org.springframework.web.socket.config.annotation.EnableWebSocket; +import org.springframework.web.socket.config.annotation.WebSocketConfigurer; +import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; import org.springframework.web.socket.server.standard.ServerEndpointExporter; -//@Configuration -public class WebSocketConfig { +@Slf4j +@Configuration +public class WebSocketConfig implements WebSocketConfigurer { + + @Autowired + private FishHitWebSocketHandler fishHitWebSocketHandler; + @Autowired + private FishHitWebSocketHandshakeInterceptor fishHitWebSocketHandshakeInterceptor; + /** * 注入一个ServerEndpointExporter,该Bean会自动注册使用@ServerEndpoint注解申明的websocket endpoint */ @@ -13,4 +28,11 @@ public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } + + @Override + public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { + registry.addHandler(fishHitWebSocketHandler, "websocket/fish/hit") + .setAllowedOrigins("*") + .addInterceptors(fishHitWebSocketHandshakeInterceptor); + } } diff --git a/src/main/java/com/xcong/excoin/configurations/interceptor/WebCommonInterceptor.java b/src/main/java/com/xcong/excoin/configurations/interceptor/WebCommonInterceptor.java new file mode 100644 index 0000000..b636002 --- /dev/null +++ b/src/main/java/com/xcong/excoin/configurations/interceptor/WebCommonInterceptor.java @@ -0,0 +1,34 @@ +package com.xcong.excoin.configurations.interceptor; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@Slf4j +@Component +public class WebCommonInterceptor implements HandlerInterceptor { + + + private String getUrl(HttpServletRequest request) { + return request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getRequestURI(); + } + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + log.info("#Begin Request# - [{}]", getUrl(request)); + return true; + } + + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { + log.info("#End Request# - [{}]", getUrl(request)); + } +} diff --git a/src/main/java/com/xcong/excoin/configurations/security/WebSecurityConfig.java b/src/main/java/com/xcong/excoin/configurations/security/WebSecurityConfig.java index 004f851..751ca5b 100644 --- a/src/main/java/com/xcong/excoin/configurations/security/WebSecurityConfig.java +++ b/src/main/java/com/xcong/excoin/configurations/security/WebSecurityConfig.java @@ -56,6 +56,7 @@ .antMatchers("/api/orderCoin/deal/list").permitAll() .antMatchers("/api/helpCenter/**").permitAll() .antMatchers("/trade/**").permitAll() + .antMatchers("/websocket/**").permitAll() .anyRequest().authenticated() .and().apply(securityConfiguereAdapter()); } diff --git a/src/main/java/com/xcong/excoin/modules/fish/controller/MemberCannonController.java b/src/main/java/com/xcong/excoin/modules/fish/controller/MemberCannonController.java index a19ba8c..63fcbd0 100644 --- a/src/main/java/com/xcong/excoin/modules/fish/controller/MemberCannonController.java +++ b/src/main/java/com/xcong/excoin/modules/fish/controller/MemberCannonController.java @@ -54,7 +54,6 @@ @ApiOperation(value = "代币金币互转") @PostMapping(value = "/coinGoldExchange") public Result coinGoldExchange(@RequestBody @Valid CoinGoldExchangeDto coinGoldExchangeDto) { - log.info("请求coinGoldExchange方法"); return memberCannonService.coinGoldExchange(coinGoldExchangeDto); } diff --git a/src/main/java/com/xcong/excoin/modules/fish/dao/MemberAccountGoldDao.java b/src/main/java/com/xcong/excoin/modules/fish/dao/MemberAccountGoldDao.java index 5bc26ca..46a7e51 100644 --- a/src/main/java/com/xcong/excoin/modules/fish/dao/MemberAccountGoldDao.java +++ b/src/main/java/com/xcong/excoin/modules/fish/dao/MemberAccountGoldDao.java @@ -14,4 +14,6 @@ int updateTotalBalanceAndAvailableBalance(@Param("id") Long id, @Param("availableBalance") BigDecimal availableBalance, @Param("totalBalance") BigDecimal totalBalance, @Param("frozenBalance") BigDecimal frozenBalance); GoldAccountVo selectAccountGoldVoByMemberId(Long memberId); + + MemberAccountGold selectByMemberIdForLock(@Param("memberId") Long memberId); } diff --git a/src/main/java/com/xcong/excoin/modules/fish/vo/OwnCannonVo.java b/src/main/java/com/xcong/excoin/modules/fish/vo/OwnCannonVo.java index c4889c2..bea558f 100644 --- a/src/main/java/com/xcong/excoin/modules/fish/vo/OwnCannonVo.java +++ b/src/main/java/com/xcong/excoin/modules/fish/vo/OwnCannonVo.java @@ -10,21 +10,21 @@ public class OwnCannonVo { private Long id; - private Long memberId; @ApiModelProperty(value = "炮台图片")//炮台图片 private String cannonImg; @ApiModelProperty(value = "炮弹图片")//炮弹图片 private String bulletImg; - @ApiModelProperty(value = "炮台UUID")//炮台UUID - private String cannonUuid; @ApiModelProperty(value = "炮台名称")//炮台名称 - private String cannonName; + private String name; @ApiModelProperty(value = "炮台编码")//炮台编码 - private String cannonCode; + private String code; @ApiModelProperty(value = "炮台兑换价格")//炮台兑换价格 - private BigDecimal cannonPrice; + private BigDecimal exchangePrice; @ApiModelProperty(value = "每发炮弹消耗金币数")//消耗金币 private BigDecimal goldConsume; @ApiModelProperty(value = "1:主动购买 2:系统赠送")//1:主动购买 2:系统赠送 private Integer type; + + @ApiModelProperty(value = "是否拥有 1-是 2-否") + private int isHas; } diff --git a/src/main/java/com/xcong/excoin/rabbit/consumer/FishHitConsumer.java b/src/main/java/com/xcong/excoin/rabbit/consumer/FishHitConsumer.java new file mode 100644 index 0000000..2bab670 --- /dev/null +++ b/src/main/java/com/xcong/excoin/rabbit/consumer/FishHitConsumer.java @@ -0,0 +1,46 @@ +package com.xcong.excoin.rabbit.consumer; + +import com.alibaba.fastjson.JSONObject; +import com.xcong.excoin.configurations.RabbitMqConfig; +import com.xcong.excoin.modules.fish.dao.MemberAccountGoldDao; +import com.xcong.excoin.modules.fish.entity.MemberAccountGold; +import com.xcong.excoin.websocket.fish.model.MsgModel; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; + +/** + * @author wzy + * @date 2021-11-26 + **/ +@Slf4j +@Component +@ConditionalOnProperty(prefix = "app", name = "fish-hit", havingValue = "true") +public class FishHitConsumer { + + @Autowired + private MemberAccountGoldDao memberAccountGoldDao; + + @RabbitListener(queues = RabbitMqConfig.QUEUE_FISH_HIT) + public void fishHit(String content) { + log.info("收到打渔消息:{}", content); + MsgModel msg = JSONObject.parseObject(content, MsgModel.class); + + synchronized (this) { + MemberAccountGold accountGold = memberAccountGoldDao.selectByMemberIdForLock(msg.getMemberId()); + BigDecimal available = accountGold.getAvailableBalance(); + + BigDecimal gold = BigDecimal.valueOf(msg.getObtain()).subtract(msg.getConsume()); + + if (gold.compareTo(BigDecimal.ZERO) < 0 && gold.abs().compareTo(available) > 0) { + log.info("余额不足"); + return; + } + memberAccountGoldDao.updateTotalBalanceAndAvailableBalance(accountGold.getId(), gold, gold, BigDecimal.ZERO); + } + } +} diff --git a/src/main/java/com/xcong/excoin/rabbit/producer/FishHitProducer.java b/src/main/java/com/xcong/excoin/rabbit/producer/FishHitProducer.java new file mode 100644 index 0000000..2f44d43 --- /dev/null +++ b/src/main/java/com/xcong/excoin/rabbit/producer/FishHitProducer.java @@ -0,0 +1,37 @@ +package com.xcong.excoin.rabbit.producer; + +import cn.hutool.core.util.IdUtil; +import com.xcong.excoin.configurations.RabbitMqConfig; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.rabbit.connection.CorrelationData; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author wzy + * @date 2021-11-26 + **/ +@Slf4j +@Component +public class FishHitProducer implements RabbitTemplate.ConfirmCallback { + + private RabbitTemplate rabbitTemplate; + + @Autowired + public FishHitProducer(RabbitTemplate rabbitTemplate) { + this.rabbitTemplate = rabbitTemplate; + rabbitTemplate.setConfirmCallback(this); + } + + public void sendFishHitMsg(String content) { + log.info("打渔发送消息:{}", content); + CorrelationData correlationData = new CorrelationData(IdUtil.simpleUUID()); + rabbitTemplate.convertAndSend(RabbitMqConfig.EXCHANGE_FISH_HIT, RabbitMqConfig.ROUTING_KEY_FISH_HIT, content, correlationData); + } + + @Override + public void confirm(CorrelationData correlationData, boolean ack, String cause) { + + } +} diff --git a/src/main/java/com/xcong/excoin/websocket/fish/HitFishWebSocket.java b/src/main/java/com/xcong/excoin/websocket/fish/HitFishWebSocket.java new file mode 100644 index 0000000..f0d7342 --- /dev/null +++ b/src/main/java/com/xcong/excoin/websocket/fish/HitFishWebSocket.java @@ -0,0 +1,119 @@ +package com.xcong.excoin.websocket.fish; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; +import com.xcong.excoin.common.contants.AppContants; +import com.xcong.excoin.modules.member.dao.MemberDao; +import com.xcong.excoin.rabbit.producer.FishHitProducer; +import com.xcong.excoin.utils.RedisUtils; +import com.xcong.excoin.websocket.fish.model.MsgModel; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.websocket.*; +import javax.websocket.server.ServerEndpoint; +import java.math.BigDecimal; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author wzy + * @date 2021-11-26 + **/ +@Slf4j +@Component +//@ServerEndpoint(value = "/websocket/fish/hit") +public class HitFishWebSocket { + + @Autowired + private RedisUtils redisUtils; + @Autowired + private FishHitProducer fishHitProducer; + + private final Map<Long, Session> MEMBER_SESSIONS = new ConcurrentHashMap<>(); + private final Map<String, Long> SID_MID = new ConcurrentHashMap<>(); + + @OnOpen + public void onOpen(Session session) { + } + + @OnClose + public void onClose(Session session) {} + + /** + * + * @param message { type : "hit", fortId : 1, fishType : "fish1" } + * + * @param session + */ + @OnMessage + public void onMessage(String message, Session session) { + JSONObject reqParam = JSONObject.parseObject(message); + if (!reqParam.containsKey("hit") && !reqParam.containsKey("login")) { + session.getAsyncRemote().sendText("{code:-1}"); + return; + } + + // 登录逻辑 + if (reqParam.containsKey("login")) { + String token = reqParam.getString("token"); + if (StrUtil.isBlank(token)) { + session.getAsyncRemote().sendText("{ code:-1, msg: \"token error\" }"); + return; + } + + String redisStr = AppContants.APP_LOGIN_PREFIX + token; + String memberStr = redisUtils.getString(redisStr); + if (StrUtil.isBlank(memberStr)) { + session.getAsyncRemote().sendText("{ code:-1, msg: \"Unauthorized\"}"); + return; + } + + JSONObject memberObject = JSONObject.parseObject(memberStr); + Long memberId = memberObject.getLong("memberId"); + MEMBER_SESSIONS.put(memberId, session); + SID_MID.put(session.getId(), memberId); + session.getAsyncRemote().sendText("{ code:0, msg: \"login succes\" }"); + return; + } + + // 打中鱼奖励金币 + int fishGold = 0; + String fishType = reqParam.getString("fishType"); + + // 如果请求参数中包含鱼类型,且类型不为空,则说明打中 + if (reqParam.containsKey("fishType") && StrUtil.isNotBlank(fishType)) { + Object fishTypeObj = redisUtils.hget(AppContants.DICTIONARY_TYPE_FISH, fishType); + if (fishTypeObj == null) { + session.getAsyncRemote().sendText("{code : -1, msg : \"fish error\"}"); + return; + } + + fishGold = (int) fishTypeObj; + } + + String fortId = reqParam.getString("fortId"); + if (!reqParam.containsKey("fortId") || fortId == null) { + return; + } + + Object fortObj = redisUtils.hget(AppContants.CANNON_TYPE, fortId); + if (fortObj == null) { + session.getAsyncRemote().sendText("{code : -1, msg : \"fort error\"}"); + return; + } + + // 消耗金币 + BigDecimal consume = (BigDecimal) fortObj; + + MsgModel msg = new MsgModel(consume, fishGold, SID_MID.get(session.getId())); + // 发送打渔消息 + fishHitProducer.sendFishHitMsg(JSONObject.toJSONString(msg)); + } + + @OnError + public void onError(Session session, Throwable error) { + log.error("捕鱼异常", error); + } +} diff --git a/src/main/java/com/xcong/excoin/websocket/fish/model/MsgModel.java b/src/main/java/com/xcong/excoin/websocket/fish/model/MsgModel.java new file mode 100644 index 0000000..7282176 --- /dev/null +++ b/src/main/java/com/xcong/excoin/websocket/fish/model/MsgModel.java @@ -0,0 +1,34 @@ +package com.xcong.excoin.websocket.fish.model; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author wzy + * @date 2021-11-26 + **/ +@Data +public class MsgModel { + + /** + * 消耗金币 + */ + private BigDecimal consume; + + /** + * 获得金币 + */ + private int obtain; + + /** + * 用户ID + */ + private Long memberId; + + public MsgModel(BigDecimal consume, int obtain, Long memberId) { + this.consume = consume; + this.obtain = obtain; + this.memberId = memberId; + } +} diff --git a/src/main/java/com/xcong/excoin/websocket/handler/FishHitWebSocketHandler.java b/src/main/java/com/xcong/excoin/websocket/handler/FishHitWebSocketHandler.java new file mode 100644 index 0000000..767cc63 --- /dev/null +++ b/src/main/java/com/xcong/excoin/websocket/handler/FishHitWebSocketHandler.java @@ -0,0 +1,118 @@ +package com.xcong.excoin.websocket.handler; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; +import com.xcong.excoin.common.contants.AppContants; +import com.xcong.excoin.netty.handler.WebSocketServerHandler; +import com.xcong.excoin.rabbit.producer.FishHitProducer; +import com.xcong.excoin.utils.RedisUtils; +import com.xcong.excoin.websocket.fish.model.MsgModel; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.socket.*; +import org.springframework.web.socket.handler.TextWebSocketHandler; + +import javax.websocket.Session; +import java.math.BigDecimal; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author wzy + * @date 2021-11-26 + **/ +@Slf4j +@Component +public class FishHitWebSocketHandler extends TextWebSocketHandler { + + @Autowired + private RedisUtils redisUtils; + @Autowired + private FishHitProducer fishHitProducer; + + private final Map<Long, WebSocketSession> MEMBER_SESSIONS = new ConcurrentHashMap<>(); + private final Map<String, Long> SID_MID = new ConcurrentHashMap<>(); + + + @Override + public void afterConnectionEstablished(WebSocketSession session) throws Exception { + Object token = session.getAttributes().get("token"); + if (token != null) { + String redisStr = AppContants.APP_LOGIN_PREFIX + token; + String memberStr = redisUtils.getString(redisStr); + if (StrUtil.isBlank(memberStr)) { + session.sendMessage(new TextMessage("{ code:-1, msg: \"Unauthorized\"}")); + return; + } + + JSONObject memberObject = JSONObject.parseObject(memberStr); + Long memberId = memberObject.getLong("id"); + MEMBER_SESSIONS.put(memberId, session); + SID_MID.put(session.getId(), memberId); + session.sendMessage(new TextMessage("{ code:0, msg: \"login succes\" }")); + } + } + + /** + * + * @param content { type : "hit", fortId : 1, fishType : "fish1" } + * @param session + */ + @Override + public void handleTextMessage(WebSocketSession session, TextMessage content) throws Exception { + String message = content.getPayload(); + + JSONObject reqParam = JSONObject.parseObject(message); + String type = reqParam.getString("type"); + if (!("hit").equals(type)) { + session.sendMessage(new TextMessage("{code:-1}")); + return; + } + + // 打中鱼奖励金币 + int fishGold = 0; + String fishType = reqParam.getString("fishType"); + + // 如果请求参数中包含鱼类型,且类型不为空,则说明打中 + if (reqParam.containsKey("fishType") && StrUtil.isNotBlank(fishType)) { + Object fishTypeObj = redisUtils.hget(AppContants.DICTIONARY_TYPE_FISH, fishType); + if (fishTypeObj == null) { + session.sendMessage(new TextMessage("{code : -1, msg : \"fish error\"}")); + return; + } + + fishGold = (int) fishTypeObj; + } + + String fortId = reqParam.getString("fortId"); + if (!reqParam.containsKey("fortId") || fortId == null) { + return; + } + + Object fortObj = redisUtils.hget(AppContants.CANNON_TYPE, fortId); + if (fortObj == null) { + session.sendMessage(new TextMessage("{code : -1, msg : \"fort error\"}")); + return; + } + + // 消耗金币 + BigDecimal consume = (BigDecimal) fortObj; + + MsgModel msg = new MsgModel(consume, fishGold, SID_MID.get(session.getId())); + // 发送打渔消息 + fishHitProducer.sendFishHitMsg(JSONObject.toJSONString(msg)); + } + + @Override + public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { + } + + @Override + public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { + Long memberId = SID_MID.get(session.getId()); + + SID_MID.remove(session.getId()); + MEMBER_SESSIONS.remove(memberId); + } +} diff --git a/src/main/java/com/xcong/excoin/websocket/handler/FishHitWebSocketHandshakeInterceptor.java b/src/main/java/com/xcong/excoin/websocket/handler/FishHitWebSocketHandshakeInterceptor.java new file mode 100644 index 0000000..faeddc8 --- /dev/null +++ b/src/main/java/com/xcong/excoin/websocket/handler/FishHitWebSocketHandshakeInterceptor.java @@ -0,0 +1,41 @@ +package com.xcong.excoin.websocket.handler; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.socket.WebSocketHandler; +import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; + +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Map; + +/** + * @author wzy + * @date 2021-11-26 + **/ +@Slf4j +@Component +public class FishHitWebSocketHandshakeInterceptor extends HttpSessionHandshakeInterceptor { + + + @Override + public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { + String query = request.getURI().getQuery(); + if (StrUtil.isBlank(query)) { + return false; + } + + HashMap<String, String> paramMap = (HashMap<String, String>) HttpUtil.decodeParamMap(query, Charset.defaultCharset()); + String token = paramMap.get("token"); + if (StrUtil.isBlank(token)) { + return false; + } + + attributes.put("token", token); + return true; + } +} diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml index a7c46c7..df9b63b 100644 --- a/src/main/resources/application-test.yml +++ b/src/main/resources/application-test.yml @@ -101,6 +101,7 @@ loop-job: false rabbit-consumer: false block-job: true + fish-hit: false aliyun: oss: diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 1cb4126..be2fd02 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -48,13 +48,13 @@ ## redis配置 redis: ## Redis数据库索引(默认为0) - database: 0 + database: 5 ## Redis服务器地址 - host: 154.91.195.155 + host: 120.27.238.55 ## Redis服务器连接端口 port: 6379 ## Redis服务器连接密码(默认为空) - password: ann123!@# + password: xcong123 jedis: pool: ## 连接池最大连接数(使用负值表示没有限制) @@ -101,6 +101,7 @@ loop-job: false rabbit-consumer: false block-job: false + fish-hit: true aliyun: oss: diff --git a/src/main/resources/mapper/common/DataDictionaryCustomMapper.xml b/src/main/resources/mapper/common/DataDictionaryCustomMapper.xml new file mode 100644 index 0000000..f19484a --- /dev/null +++ b/src/main/resources/mapper/common/DataDictionaryCustomMapper.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.xcong.excoin.common.system.dao.DataDictionaryCustomDao"> + + <select id="selectDicByType" resultType="com.xcong.excoin.common.system.entity.DataDictionaryCustom"> + select * from data_dictionary_custom where type=#{type} + </select> + + <select id="selectDicDataByTypeAndCode" resultType="com.xcong.excoin.common.system.entity.DataDictionaryCustom"> + select * from data_dictionary_custom a + where a.type=#{type} and a.code=#{code} + </select> +</mapper> \ No newline at end of file diff --git a/src/main/resources/mapper/fish/CannonOwnRecordDao.xml b/src/main/resources/mapper/fish/CannonOwnRecordDao.xml index b461582..b3a9c32 100644 --- a/src/main/resources/mapper/fish/CannonOwnRecordDao.xml +++ b/src/main/resources/mapper/fish/CannonOwnRecordDao.xml @@ -7,10 +7,11 @@ </select> <select id="selectCannonOwnRecordsByMemberId" resultType="com.xcong.excoin.modules.fish.vo.OwnCannonVo"> - select a.*,b.gold_consume goldConsume,b.cannon_img cannonImg,b.bullet_img bulletImg - from cannon_own_record a - left join cannon_setting b on a.cannon_code = b.code - where a.member_id = #{memberId} + select + a.*, + case when b.id is null then 0 else 1 end isHas + from cannon_setting a + left join cannon_own_record b on a.code=b.cannon_code and b.member_id=#{memberId} </select> <select id="selectCannonOwnRecordsByIdAndMemberId" resultType="com.xcong.excoin.modules.fish.entity.CannonOwnRecord"> diff --git a/src/main/resources/mapper/fish/MemberAccountGoldDao.xml b/src/main/resources/mapper/fish/MemberAccountGoldDao.xml index e9ceec9..73b4b31 100644 --- a/src/main/resources/mapper/fish/MemberAccountGoldDao.xml +++ b/src/main/resources/mapper/fish/MemberAccountGoldDao.xml @@ -32,4 +32,7 @@ select a.* from member_account_gold a where a.member_id = #{memberId} </select> + <select id="selectByMemberIdForLock" resultType="com.xcong.excoin.modules.fish.entity.MemberAccountGold"> + select a.* from member_account_gold a where a.member_id = #{memberId} for update + </select> </mapper> \ No newline at end of file -- Gitblit v1.9.1