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