Helius
2021-11-26 4017fe347792c7e28695c455a40874f0c647cc9b
add fish hit websocket
12 files modified
11 files added
631 ■■■■■ changed files
src/main/java/com/xcong/excoin/ExcoinApplication.java 4 ●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/common/contants/AppContants.java 3 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/common/initialization/InitJob.java 62 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/common/system/dao/DataDictionaryCustomDao.java 14 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/common/system/entity/DataDictionaryCustom.java 22 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/configurations/RabbitMqConfig.java 22 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/configurations/WebSocketConfig.java 26 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/configurations/interceptor/WebCommonInterceptor.java 34 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/configurations/security/WebSecurityConfig.java 1 ●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/fish/controller/MemberCannonController.java 1 ●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/fish/dao/MemberAccountGoldDao.java 2 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/fish/vo/OwnCannonVo.java 12 ●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/rabbit/consumer/FishHitConsumer.java 46 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/rabbit/producer/FishHitProducer.java 37 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/websocket/fish/HitFishWebSocket.java 119 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/websocket/fish/model/MsgModel.java 34 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/websocket/handler/FishHitWebSocketHandler.java 118 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/websocket/handler/FishHitWebSocketHandshakeInterceptor.java 41 ●●●●● patch | view | raw | blame | history
src/main/resources/application-test.yml 1 ●●●● patch | view | raw | blame | history
src/main/resources/application.yml 7 ●●●●● patch | view | raw | blame | history
src/main/resources/mapper/common/DataDictionaryCustomMapper.xml 13 ●●●●● patch | view | raw | blame | history
src/main/resources/mapper/fish/CannonOwnRecordDao.xml 9 ●●●●● patch | view | raw | blame | history
src/main/resources/mapper/fish/MemberAccountGoldDao.xml 3 ●●●●● patch | view | raw | blame | history
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) {
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";
}
src/main/java/com/xcong/excoin/common/initialization/InitJob.java
New file
@@ -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);
            }
        }
    }
}
src/main/java/com/xcong/excoin/common/system/dao/DataDictionaryCustomDao.java
New file
@@ -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);
}
src/main/java/com/xcong/excoin/common/system/entity/DataDictionaryCustom.java
New file
@@ -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;
}
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);
    }
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);
    }
}
src/main/java/com/xcong/excoin/configurations/interceptor/WebCommonInterceptor.java
New file
@@ -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));
    }
}
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());
    }
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);
    }
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);
}
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;
}
src/main/java/com/xcong/excoin/rabbit/consumer/FishHitConsumer.java
New file
@@ -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);
        }
    }
}
src/main/java/com/xcong/excoin/rabbit/producer/FishHitProducer.java
New file
@@ -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) {
    }
}
src/main/java/com/xcong/excoin/websocket/fish/HitFishWebSocket.java
New file
@@ -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);
    }
}
src/main/java/com/xcong/excoin/websocket/fish/model/MsgModel.java
New file
@@ -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;
    }
}
src/main/java/com/xcong/excoin/websocket/handler/FishHitWebSocketHandler.java
New file
@@ -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);
    }
}
src/main/java/com/xcong/excoin/websocket/handler/FishHitWebSocketHandshakeInterceptor.java
New file
@@ -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;
    }
}
src/main/resources/application-test.yml
@@ -101,6 +101,7 @@
  loop-job: false
  rabbit-consumer: false
  block-job: true
  fish-hit: false
aliyun:
  oss:
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:
src/main/resources/mapper/common/DataDictionaryCustomMapper.xml
New file
@@ -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>
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">
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>