diff --git a/cloudutil/src/main/java/com/ccsens/cloudutil/feign/TallFeignClient.java b/cloudutil/src/main/java/com/ccsens/cloudutil/feign/TallFeignClient.java index c06d0e17..11eaa6ca 100644 --- a/cloudutil/src/main/java/com/ccsens/cloudutil/feign/TallFeignClient.java +++ b/cloudutil/src/main/java/com/ccsens/cloudutil/feign/TallFeignClient.java @@ -18,6 +18,7 @@ import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.util.List; import java.util.Map; /** @@ -90,6 +91,24 @@ public interface TallFeignClient { */ @GetMapping("/users/member") JsonResponse getMemberByUserId(@RequestParam( name = "userId") Long userId,@RequestParam( name = "projectId") Long projectId); + + + /** + * 获取项目下的所有成员id + * @param projectId + * @return + */ + @GetMapping("/users/allMemberAll") + List getMemberIdListByProject(@RequestParam( name = "projectId")Long projectId); + + /** + * 通过任务id获得项目id(消息系统用) + * @param token + * @return + */ + @GetMapping("/users/claims") + String getUserId(@RequestParam( name = "token")String token); + } @Slf4j @@ -142,6 +161,16 @@ class TallFeignClientFallBack implements FallbackFactory { public JsonResponse getMemberByUserId(Long userId,Long projectId) { return JsonResponse.newInstance().fail(); } + + @Override + public List getMemberIdListByProject(Long projectId) { + return null; + } + + @Override + public String getUserId(String token) { + return null; + } }; } diff --git a/game/src/main/java/com/ccsens/game/GameApplication.java b/game/src/main/java/com/ccsens/game/GameApplication.java index 54ee7de2..ae24fe24 100644 --- a/game/src/main/java/com/ccsens/game/GameApplication.java +++ b/game/src/main/java/com/ccsens/game/GameApplication.java @@ -1,6 +1,9 @@ package com.ccsens.game; +import com.ccsens.game.netty.wsserver.NettyWsServer; import org.mybatis.spring.annotation.MapperScan; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletComponentScan; @@ -15,10 +18,17 @@ import org.springframework.scheduling.annotation.EnableAsync; @EnableCircuitBreaker @EnableFeignClients(basePackages = "com.ccsens.cloudutil.feign") @SpringBootApplication(scanBasePackages = "com.ccsens") -public class GameApplication { +public class GameApplication implements CommandLineRunner { + @Autowired + private NettyWsServer nettyWsServer; public static void main(String[] args) { - SpringApplication.run(GameApplication.class, args); + SpringApplication.run(GameApplication.class,args); } + @Override + public void run(String... args) throws Exception { + nettyWsServer.start(); + + } } diff --git a/game/src/main/java/com/ccsens/game/api/ScreenController.java b/game/src/main/java/com/ccsens/game/api/ScreenController.java index 594f67a4..268261c2 100644 --- a/game/src/main/java/com/ccsens/game/api/ScreenController.java +++ b/game/src/main/java/com/ccsens/game/api/ScreenController.java @@ -26,7 +26,7 @@ public class ScreenController { @Autowired private IScreenService screenService; -// @MustLogin + @MustLogin @ApiOperation(value = "获取大屏路径", notes = "") @RequestMapping(value = "url", method = RequestMethod.POST, produces = {"application/json;charset=UTF-8"}) public JsonResponse getScreenUrl(@ApiParam @Validated @RequestBody QueryDto params) throws Exception { @@ -44,6 +44,7 @@ public class ScreenController { return JsonResponse.newInstance().ok(gameInfoVo); } + @Login @ApiOperation(value = "获取游戏的状态", notes = "") @ApiImplicitParams({ }) @@ -64,6 +65,7 @@ public class ScreenController { return JsonResponse.newInstance().ok(startGame); } + @Login @ApiOperation(value = "再玩一次", notes = "") @ApiImplicitParams({ }) diff --git a/game/src/main/java/com/ccsens/game/bean/dto/ScreenDto.java b/game/src/main/java/com/ccsens/game/bean/dto/ScreenDto.java index 80065f05..94a86418 100644 --- a/game/src/main/java/com/ccsens/game/bean/dto/ScreenDto.java +++ b/game/src/main/java/com/ccsens/game/bean/dto/ScreenDto.java @@ -11,6 +11,8 @@ public class ScreenDto { @Data @ApiModel public static class MemberGame{ + @ApiModelProperty("项目id") + private Long projectId; @ApiModelProperty("要创建的小游戏的类型 例如:SQ 代表数钱小游戏") private String gameType; } diff --git a/game/src/main/java/com/ccsens/game/bean/dto/message/AckMessageDto.java b/game/src/main/java/com/ccsens/game/bean/dto/message/AckMessageDto.java new file mode 100644 index 00000000..a17ca984 --- /dev/null +++ b/game/src/main/java/com/ccsens/game/bean/dto/message/AckMessageDto.java @@ -0,0 +1,32 @@ +package com.ccsens.game.bean.dto.message; + +import com.ccsens.util.WebConstant; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +@Data +public class AckMessageDto extends BaseMessageDto { + @Setter + @Getter + public static class Data { + Long msgId; + } + + private Data data; + + public AckMessageDto(){ + setType(WebConstant.Message_Type.Ack.phase); + setEvent(WebConstant.Message_Ack_Event.Ack.phase); + setTime(System.currentTimeMillis()); + } + + public AckMessageDto(Long projectId, Long msgId){ + this(); + setProjectId(projectId); + + Data d = new Data(); + d.setMsgId(msgId); + setData(d); + } +} diff --git a/game/src/main/java/com/ccsens/game/bean/dto/message/AuthMessageDto.java b/game/src/main/java/com/ccsens/game/bean/dto/message/AuthMessageDto.java new file mode 100644 index 00000000..8e68d42b --- /dev/null +++ b/game/src/main/java/com/ccsens/game/bean/dto/message/AuthMessageDto.java @@ -0,0 +1,35 @@ +package com.ccsens.game.bean.dto.message; + +import com.ccsens.util.WebConstant; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +@Data +public class AuthMessageDto extends BaseMessageDto { + @Setter + @Getter + public static class Data{ + private String id; + private String userId; + private String channelId; + private String token; + } + + private Data data; + + public AuthMessageDto(){ + setType(WebConstant.Message_Type.Auth.phase); + setEvent(WebConstant.Message_Auth_Event.Auth.phase); + setTime(System.currentTimeMillis()); + } + + public AuthMessageDto(String userId, String channelId,String recordId){ + this(); + Data d = new Data(); + d.setUserId(userId); + d.setChannelId(channelId); + d.setId(recordId); + setData(d); + } +} diff --git a/game/src/main/java/com/ccsens/game/bean/dto/message/AuthMessageWithAnswerDto.java b/game/src/main/java/com/ccsens/game/bean/dto/message/AuthMessageWithAnswerDto.java new file mode 100644 index 00000000..386d7827 --- /dev/null +++ b/game/src/main/java/com/ccsens/game/bean/dto/message/AuthMessageWithAnswerDto.java @@ -0,0 +1,32 @@ +package com.ccsens.game.bean.dto.message; + +import com.ccsens.util.WebConstant; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +@Data +public class AuthMessageWithAnswerDto extends BaseMessageDto{ + @Setter + @Getter + public static class Data{ + private Boolean success; + private String phase; + } + + private Data data; + + public AuthMessageWithAnswerDto(){ + setType(WebConstant.Message_Type.Auth.phase); + setEvent(WebConstant.Message_Auth_Event.Answer.phase); + setTime(System.currentTimeMillis()); + } + + public AuthMessageWithAnswerDto(Boolean success, String phase){ + this(); + Data d = new Data(); + d.setSuccess(success); + d.setPhase(phase); + setData(d); + } +} diff --git a/game/src/main/java/com/ccsens/game/bean/dto/message/BaseMessageDto.java b/game/src/main/java/com/ccsens/game/bean/dto/message/BaseMessageDto.java new file mode 100644 index 00000000..c67ac7d7 --- /dev/null +++ b/game/src/main/java/com/ccsens/game/bean/dto/message/BaseMessageDto.java @@ -0,0 +1,35 @@ +package com.ccsens.game.bean.dto.message; + +import lombok.Data; + +import java.util.List; + +@Data +public class BaseMessageDto { + @Data + public static class MessageUser { + private Long id; + private Long userId; //本质上是authId //20190507 本质上是userId + private String nickname; + private String avatarUrl; + private boolean hasRead; + public MessageUser(){ + hasRead = false; + } + public MessageUser(Long id,Long userId,String nickname,String avatarUrl){ + this(); + this.id = id; + this.userId = userId; + this.nickname = nickname; + this.avatarUrl = avatarUrl; + } + } + + private Long time; + private String type; + private String event; + private Long projectId; + private MessageUser sender; + private List receivers; +// private Object data; +} diff --git a/game/src/main/java/com/ccsens/game/bean/dto/message/ChangeStatusMessageDto.java b/game/src/main/java/com/ccsens/game/bean/dto/message/ChangeStatusMessageDto.java new file mode 100644 index 00000000..d1a3cd0f --- /dev/null +++ b/game/src/main/java/com/ccsens/game/bean/dto/message/ChangeStatusMessageDto.java @@ -0,0 +1,38 @@ +package com.ccsens.game.bean.dto.message; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +@Data +public class ChangeStatusMessageDto { + + private PendingData pendingData; + private CompletedData completedData; + + @Data + @ApiModel("开始") + public static class PendingData{ + @ApiModelProperty("活动规则") + private Long startLocalTime ; + @ApiModelProperty("活动奖品") + private Long endLocalTime; + } + + @Data + @ApiModel("结束") + public static class CompletedData{ + @ApiModelProperty("总人数") + private Integer totalMembers ; + @ApiModelProperty("总次数") + private Integer totalTimes; + @ApiModelProperty("总分数") + private Integer totalScore; + @ApiModelProperty("平均每人多少分") + private Integer averageTimes; + @ApiModelProperty("平均次数超过百分之多少人") + private Integer over; + } +} diff --git a/game/src/main/java/com/ccsens/game/bean/dto/message/ChromeMessageDto.java b/game/src/main/java/com/ccsens/game/bean/dto/message/ChromeMessageDto.java new file mode 100644 index 00000000..377e759a --- /dev/null +++ b/game/src/main/java/com/ccsens/game/bean/dto/message/ChromeMessageDto.java @@ -0,0 +1,36 @@ +package com.ccsens.game.bean.dto.message; + +import com.ccsens.util.WebConstant; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +/** + * @author __zHangSan + */ +@Data +public class ChromeMessageDto extends BaseMessageDto{ + @Setter + @Getter + public static class Data{ + private Long projectId; + private Long recordId; + private String url; + } + private Data data; + + public ChromeMessageDto(){ + setType(WebConstant.Message_Type.Chrome.phase); + setTime(System.currentTimeMillis()); + } + + public ChromeMessageDto(String url,Long recordId, Long projectId){ + this(); + if(data == null){ + data = new Data(); + } + data.setUrl(url); + data.setRecordId(recordId); + data.setProjectId(projectId); + } +} diff --git a/game/src/main/java/com/ccsens/game/bean/dto/message/GameMessageCountIn.java b/game/src/main/java/com/ccsens/game/bean/dto/message/GameMessageCountIn.java new file mode 100644 index 00000000..9813b246 --- /dev/null +++ b/game/src/main/java/com/ccsens/game/bean/dto/message/GameMessageCountIn.java @@ -0,0 +1,15 @@ +package com.ccsens.game.bean.dto.message; + +import com.ccsens.util.WebConstant; +import lombok.Data; + +@Data +public class GameMessageCountIn extends BaseMessageDto{ + + public GameMessageCountIn(){ + setType(WebConstant.Message_Type.Count.phase); + setTime(System.currentTimeMillis()); + } + + +} diff --git a/game/src/main/java/com/ccsens/game/bean/dto/message/GameMessageCountOut.java b/game/src/main/java/com/ccsens/game/bean/dto/message/GameMessageCountOut.java new file mode 100644 index 00000000..a3deb69a --- /dev/null +++ b/game/src/main/java/com/ccsens/game/bean/dto/message/GameMessageCountOut.java @@ -0,0 +1,30 @@ +package com.ccsens.game.bean.dto.message; + +import com.ccsens.util.WebConstant; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +@Data +public class GameMessageCountOut extends BaseMessageDto { + @Setter + @Getter + public static class Data{ + private int totalTimes; + private int totalScore; + } + + private Data data; + + public GameMessageCountOut(){ + setType(WebConstant.Message_Type.Count.phase); + setTime(System.currentTimeMillis()); + } + + public GameMessageCountOut(int totalTimes, int totalScore){ + this(); + Data d = new Data(); + d.setTotalTimes(totalTimes); + d.setTotalScore(totalScore); + setData(d); + } +} diff --git a/game/src/main/java/com/ccsens/game/bean/dto/message/GameMessageWithChangeStatusIn.java b/game/src/main/java/com/ccsens/game/bean/dto/message/GameMessageWithChangeStatusIn.java new file mode 100644 index 00000000..ef6a4426 --- /dev/null +++ b/game/src/main/java/com/ccsens/game/bean/dto/message/GameMessageWithChangeStatusIn.java @@ -0,0 +1,31 @@ +package com.ccsens.game.bean.dto.message; + +import com.ccsens.util.WebConstant; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +@Data +public class GameMessageWithChangeStatusIn extends BaseMessageDto{ + @Setter + @Getter + public static class Data{ + private String recordId; + private int gameStatus; + } + + private Data data; + + public GameMessageWithChangeStatusIn(){ + setType(WebConstant.Message_Type.ChangeStatus.phase); + setTime(System.currentTimeMillis()); + } + + public GameMessageWithChangeStatusIn(String recordId, int gameStatus){ + this(); + Data d = new Data(); + d.setRecordId(recordId); + d.setGameStatus(gameStatus); + setData(d); + } +} diff --git a/game/src/main/java/com/ccsens/game/bean/dto/message/GameMessageWithChangeStatusOut.java b/game/src/main/java/com/ccsens/game/bean/dto/message/GameMessageWithChangeStatusOut.java new file mode 100644 index 00000000..2c5f7bd0 --- /dev/null +++ b/game/src/main/java/com/ccsens/game/bean/dto/message/GameMessageWithChangeStatusOut.java @@ -0,0 +1,34 @@ +package com.ccsens.game.bean.dto.message; + +import com.ccsens.util.WebConstant; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +@Data +public class GameMessageWithChangeStatusOut extends BaseMessageDto{ + private Long userId; + @Setter + @Getter + public static class Data{ + private Long recordId; + private int gameStatus; + private ChangeStatusMessageDto changeStatusMessageDto; + } + + private Data data; + + public GameMessageWithChangeStatusOut(){ + setType(WebConstant.Message_Type.ChangeStatus.phase); + setTime(System.currentTimeMillis()); + } + + public GameMessageWithChangeStatusOut(Long recordId,int gameStatus,ChangeStatusMessageDto changeStatusMessageDto){ + this(); + Data d = new Data(); + d.setRecordId(recordId); + d.setGameStatus(gameStatus); + d.setChangeStatusMessageDto(changeStatusMessageDto); + setData(d); + } +} diff --git a/game/src/main/java/com/ccsens/game/bean/dto/message/GameMessageWithGetUrlDto.java b/game/src/main/java/com/ccsens/game/bean/dto/message/GameMessageWithGetUrlDto.java new file mode 100644 index 00000000..6688cd46 --- /dev/null +++ b/game/src/main/java/com/ccsens/game/bean/dto/message/GameMessageWithGetUrlDto.java @@ -0,0 +1,32 @@ +package com.ccsens.game.bean.dto.message; + +import com.ccsens.util.WebConstant; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +@Data +public class GameMessageWithGetUrlDto extends BaseMessageDto { + @Setter + @Getter + public static class Data{ + private Long recordId; + private String url; + } + + private Data data; + + public GameMessageWithGetUrlDto(){ + setType(WebConstant.Message_Type.Game.phase); + setEvent(WebConstant.Message_Url_Event.Url.phase); + setTime(System.currentTimeMillis()); + } + + public GameMessageWithGetUrlDto(Long recordId, String url){ + this(); + Data d = new Data(); + d.setRecordId(recordId); + d.setUrl(url); + setData(d); + } +} diff --git a/game/src/main/java/com/ccsens/game/bean/dto/message/HeartMessageDto.java b/game/src/main/java/com/ccsens/game/bean/dto/message/HeartMessageDto.java new file mode 100644 index 00000000..bd19b18a --- /dev/null +++ b/game/src/main/java/com/ccsens/game/bean/dto/message/HeartMessageDto.java @@ -0,0 +1,23 @@ +package com.ccsens.game.bean.dto.message; + +import com.ccsens.util.WebConstant; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +@Data +public class HeartMessageDto extends BaseMessageDto{ + @Setter + @Getter + public static class Data{ + private int major; + private int minor; + } + private Data data; + + public HeartMessageDto(){ + setType(WebConstant.Message_Type.Heart.phase); + setEvent(WebConstant.Message_Heart_Event.Heart.phase); + setTime(System.currentTimeMillis()); + } +} diff --git a/game/src/main/java/com/ccsens/game/bean/dto/message/PPTCtlMessageDto.java b/game/src/main/java/com/ccsens/game/bean/dto/message/PPTCtlMessageDto.java new file mode 100644 index 00000000..3ba14032 --- /dev/null +++ b/game/src/main/java/com/ccsens/game/bean/dto/message/PPTCtlMessageDto.java @@ -0,0 +1,35 @@ +package com.ccsens.game.bean.dto.message; + +import com.ccsens.util.WebConstant; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +/** + * @author __zHangSan + */ +@Data +public class PPTCtlMessageDto extends BaseMessageDto{ + @Setter + @Getter + public static class Data{ + /** + * Supported Operation: up,down,begin,end + */ + private String oper; + } + private Data data; + + public PPTCtlMessageDto(){ + setType(WebConstant.Message_Type.PPTCtl.phase); + setTime(System.currentTimeMillis()); + } + + public PPTCtlMessageDto(String oper){ + this(); + if(data == null){ + data = new Data(); + } + data.setOper(oper); + } +} diff --git a/game/src/main/java/com/ccsens/game/bean/vo/ScreenVo.java b/game/src/main/java/com/ccsens/game/bean/vo/ScreenVo.java index 12103d93..8373b4df 100644 --- a/game/src/main/java/com/ccsens/game/bean/vo/ScreenVo.java +++ b/game/src/main/java/com/ccsens/game/bean/vo/ScreenVo.java @@ -13,9 +13,11 @@ public class ScreenVo { @Data @ApiModel public static class UrlVo{ + @ApiModelProperty("大屏的路径") + private Long id; @ApiModelProperty("大屏的路径") private String url; - @ApiModelProperty("游戏路径") + @ApiModelProperty("游戏规则") private List ruleList; } diff --git a/game/src/main/java/com/ccsens/game/config/SpringConfig.java b/game/src/main/java/com/ccsens/game/config/SpringConfig.java index 14504ef8..fc3f8ab3 100644 --- a/game/src/main/java/com/ccsens/game/config/SpringConfig.java +++ b/game/src/main/java/com/ccsens/game/config/SpringConfig.java @@ -76,7 +76,6 @@ public class SpringConfig implements WebMvcConfigurer { configurer.favorPathExtension(false); } - @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS"); diff --git a/game/src/main/java/com/ccsens/game/netty/ChannelManager.java b/game/src/main/java/com/ccsens/game/netty/ChannelManager.java new file mode 100644 index 00000000..9061ee91 --- /dev/null +++ b/game/src/main/java/com/ccsens/game/netty/ChannelManager.java @@ -0,0 +1,391 @@ +package com.ccsens.game.netty; + +import cn.hutool.core.collection.CollectionUtil; +import io.netty.channel.Channel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author wei + */ +public class ChannelManager { + private static Logger logger = LoggerFactory.getLogger(ChannelManager.class); + private static ThreadLocal threadLocal = new ThreadLocal<>(); + + /** + * UserId,WrappedChannel authed channels; + */ + private static Map> authedChannels; + /** + * Channel,WrapperedChannel + */ + private static Map rawChannels; + + static { + authedChannels = new ConcurrentHashMap<>(); + rawChannels = new ConcurrentHashMap<>(); + } + + /** + * 私有构造,不允许生成该类对象 + */ + private ChannelManager(){} + + /** + * 设置当前正在错误的channel + * @param channel netty ws/tcp连接 + */ + public static void setCurrentChannel(Channel channel){ + threadLocal.set(channel); + } + + /** + * 获取当前线程的channel对象 + * @return channel + */ + public static Channel getCurrentChannel(){ + return threadLocal.get(); + } + + /** + * 删除当前线程的channel对象 + */ + public static void removeCurrentChannel(){ + threadLocal.remove(); + } + + /** + * 添加一个新的连接(Channel) + * @param channel 新的连接 + * @param serverType 服务器类型 + */ + public static synchronized void addChannel(Channel channel,String serverType){ + logger.debug("Invoke addChannel({},{})",channel,serverType); + if(null != channel) { + rawChannels.put(channel, new WrapperedChannel(channel, serverType)); + logger.debug("Add a new channel: {},{}",channel.id().asLongText(),serverType); + }else{ + logger.error("channel is null"); + } + } + + /** + * 用户认证 + * @param channel 连接 + * @param userId 用户ID + * @param major 客户端主版本号 + * @param minor 客户端次版本号 + */ + public static synchronized void authChannel(Channel channel,String userId,Integer major,Integer minor){ + logger.debug("Invoke authedChannels({},{},{},{})",channel.id().asLongText(),userId,major,minor); + major = major != null ? major : 0; + minor = minor != null ? minor : 0; + + WrapperedChannel wrapperedChannel = rawChannels.get(channel); + if(wrapperedChannel != null){ + wrapperedChannel.whenAuthed(userId,major,minor); + Set authedWchannelSet = authedChannels.computeIfAbsent(userId, k -> new HashSet<>()); + authedWchannelSet.add(wrapperedChannel); + logger.debug("Authed channel {} with user {}", channel.id().asLongText(), userId); + }else{ + logger.error("Authed channel,but wrappedChannel is null."); + } + } + + /** + * 手机端用户游戏认证 + * @param channel 连接 + * @param userId 用户ID + * @param major 客户端主版本号 + * @param minor 客户端次版本号 + * @param minor 用户参加的游戏id + */ + public static synchronized void authChannel(Channel channel,String userId,Integer major,Integer minor,String recordId){ + logger.debug("Invoke authedChannels({},{},{},{})",channel.id().asLongText(),userId,major,minor); + major = major != null ? major : 0; + minor = minor != null ? minor : 0; + + WrapperedChannel wrapperedChannel = rawChannels.get(channel); + if(wrapperedChannel != null){ + wrapperedChannel.whenAuthed(userId,major,minor,recordId); + Set authedWchannelSet = authedChannels.computeIfAbsent(userId, k -> new HashSet<>()); + authedWchannelSet.add(wrapperedChannel); + logger.debug("Authed channel {} with user {}", channel.id().asLongText(), userId); + }else{ + logger.error("Authed channel,but wrappedChannel is null."); + } + } + + /** + * 移除一个连接(Channel) + * @param channel 要移除的连接 + */ + public static synchronized void removeChannel(Channel channel){ + logger.debug("Invoke removeChannel({})",channel.id().asLongText()); + WrapperedChannel wrapperedChannel = rawChannels.get(channel); + if(wrapperedChannel != null){ + //从rawChannels集合中删除 + rawChannels.remove(channel); + logger.debug("Remove a channel from rawChannels: {}",channel.id().asLongText()); + if(wrapperedChannel.isAuthed()){ + Set authedChannelSet = authedChannels.get(wrapperedChannel.getUserId()); + //从authedChannel集合的value(set)中删除 + if(CollectionUtil.isNotEmpty(authedChannelSet)){ + authedChannelSet.remove(wrapperedChannel); + logger.debug("Remove a channel from authedChannelSet: {}, {}",wrapperedChannel.getUserId(),channel.id().asLongText()); + } + //从authedChannel中删除,此处不用else,因为if中语句执行完毕之后,authedChannelSet也可能变成空集合 + if(CollectionUtil.isEmpty(authedChannelSet)){ + authedChannels.remove(wrapperedChannel.getUserId()); + logger.debug("Remove a user from authedChannels: {}",wrapperedChannel.getUserId()); + } + } + }else{ + logger.error("Remove channel,but wrappedChannel is null."); + } + + if(channel.isOpen() || channel.isActive()){ + channel.close(); + } + } + + /** + * 移除一个用户 + * @param userId 要移除的用户 + */ + public static synchronized void removeUser(String userId){ + logger.debug("Invoke remove user : {}",userId); + Set wChannelSet = authedChannels.get(userId); + if(CollectionUtil.isNotEmpty(wChannelSet)){ + for(WrapperedChannel wChannel : wChannelSet){ + //从rawChannel中依次删除 + rawChannels.remove(wChannel.getChannel()); + logger.debug("Remove a channel from rawChannels: {}",wChannel.getChannel().id().asLongText()); + } + } + //从authedChannel中删除 + authedChannels.remove(userId); + logger.debug("Remove a user from authedChannels: {}",userId); + } + + /** + * 添加版本号 + * 只能给已认证的请求添加版本号 + * @param channel 连接 + * @param major 主版本号 + * @param minor 次版本号 + */ + public static synchronized void versionChannel(Channel channel,int major,int minor){ + logger.debug("Invoke Version channel({},{},{}))",channel.id().asLongText(),major,minor); + WrapperedChannel wChannel = rawChannels.get(channel); + if(wChannel != null){ + wChannel.setVersion(major,minor); + logger.debug("Version Channel: {},{},{}",channel.id().asLongText(),major,minor); + }else{ + logger.error("Remove channel,but wrappedChannel is null."); + } + } + + /** + * 发送广播消息给所有已认证Channel + * @param message 消息 + */ + public static synchronized void broadCastAuthed(Object message) { + logger.debug("Invoke broadCastAuthed({})",message); + for (Map.Entry entry : rawChannels.entrySet()) { + WrapperedChannel wChannel = entry.getValue(); + if(wChannel.isAuthed()) { + wChannel.writeAndFlush(message); + logger.debug("Send Message {} to {},{}",message,wChannel.getUserId(),wChannel.getId()); + } + } + } + + /** + * 发送广播消息 + * @param message 消息 + */ + public static synchronized void broadCast(Object message) { + logger.debug("Invoke broadCast({})",message); + for (Map.Entry entry : rawChannels.entrySet()) { + entry.getValue().writeAndFlush(message); + logger.debug("Send Message {} to {}",message,entry.getValue().getId()); + } + } + + /** + * 发送消息给某个连接 + * @param channel 连接 + * @param message 消息 + */ + public static synchronized void sendTo(Channel channel,Object message){ + logger.debug("Invoke sendTo({},{})",channel.id().asLongText(),message); + WrapperedChannel wrapperedChannel = rawChannels.get(channel); + if(wrapperedChannel != null) { + wrapperedChannel.writeAndFlush(message); + logger.debug("Write message {} to Channel {}",message,channel); + }else{ + logger.error("can't find channel from rawChannels"); + } + } + + /** + * 发送消息给某个用户 + * @param userId 用户ID + * @param message 消息 + */ + public static synchronized boolean sendTo(String userId,Object message){ + logger.debug("Invoke sendTo({},{})",userId,message); + Set wChannelSet = authedChannels.get(userId); + if(CollectionUtil.isNotEmpty(wChannelSet)){ + for(WrapperedChannel wChannel:wChannelSet){ + wChannel.writeAndFlush(message); + logger.debug("Send message {} to channel {}",message,userId); + } + return true; + }else{ + return false; + } + } + + /** + * 刷新最后一次接收数据时间 每次接收数据需要调用该函数 + * @param channel 接收数据的连接 + */ + public static synchronized void flushReceiveTimestamp(Channel channel){ + logger.debug("Invoke flushReceiveTimestamp({})",channel.id().asLongText()); + WrapperedChannel wrapperedChannel = rawChannels.get(channel); + if(wrapperedChannel != null){ + wrapperedChannel.whenReceivedData(); + }else{ + logger.error("can find channel from rawChannels"); + } + } + + /** + * 关闭所有未认证的连接 + * @param unAuthedChannelsMaxAliveTimeInSeconds 未认证的最大时长(s) + * @throws Exception + * @deprecated 已过期,该方法在多线程并发下可能出现问题,建议使用messageService中的同名方法 + */ + @Deprecated + public static synchronized void closeUnAuthedChannels(Long unAuthedChannelsMaxAliveTimeInSeconds) throws Exception { + logger.debug("Inovke closeUnAuthedChannels({})",unAuthedChannelsMaxAliveTimeInSeconds); + Iterator> it = rawChannels.entrySet().iterator(); + while(it.hasNext()){ + Map.Entry entry = it.next(); + WrapperedChannel wrapperedChannel = entry.getValue(); + if((!wrapperedChannel.isAuthed()) && (wrapperedChannel.getConnectedSeconds() > unAuthedChannelsMaxAliveTimeInSeconds)) { + it.remove(); + //关闭连接 + wrapperedChannel.getChannel().close(); + logger.debug("Remove a unAuthed Channel {}, which has connected {}s,maxOutTime is {}s", wrapperedChannel.getId(),wrapperedChannel.getConnectedSeconds() , + unAuthedChannelsMaxAliveTimeInSeconds ); + } + } + } + + /** + * 判断Channel是否存在 + * @param channel 连接 + * @return 是否存在 + */ + public static synchronized boolean channelExist(Channel channel){ + return rawChannels.containsKey(channel); + } + + /** + * 判断Channel是否已经认证 + * @param channel 连接 + * @return 是否认证 + */ + public static synchronized boolean channelAuthed(Channel channel){ + return rawChannels.containsKey(channel) && rawChannels.get(channel).isAuthed(); + } + + /** + * 判断用户是否在线 + * @param userId 用户ID + * @return 是否认证 + */ + public static synchronized boolean isUserOnline(String userId){ + return authedChannels.containsKey(userId); + } + + /** + * 根据Channel获取对应的用户ID + * @param channel 连接 + * @return 用户ID + */ + public static synchronized String getUserIdByChannel(Channel channel) { + return rawChannels.containsKey(channel) ? rawChannels.get(channel).getUserId() : null; + } + + /** + * 获取所有在线用户 + * @return 所有用户的集合列表 + */ + public static synchronized Set getOnlineUsers(){ + return authedChannels.keySet(); + } + + /** + * 获取某种类型的所有在线用户的连接 + * @param type 客户端类型(ws/tcp modebus/tcp text) + * @return 所有在西安channel的集合列表 + */ + public static synchronized Set getOnlineChannels(String type){ + Set onLineChannels = CollectionUtil.newHashSet(); + for(Map.Entry entry: rawChannels.entrySet()){ + WrapperedChannel wrapperedChannel = entry.getValue(); + if(wrapperedChannel.isAuthed() && wrapperedChannel.getType().equals(type)){ + onLineChannels.add(entry.getKey()); + } + } + return onLineChannels; + } + + /** + * 获取一个rawChannel的副本 + * @return 副本 + */ + public static Map getCopyOfAllChannels() { + Map copyMap = new HashMap<>(rawChannels.size()); + copyMap.putAll(rawChannels); + return copyMap; + } + + /** + * 根据Channel获取WrapperedChannel + * @param channel channel + * @return 对应的wrapperedChannel + */ + public static WrapperedChannel getWrapperedChannelByChannel(Channel channel) { + return rawChannels.get(channel); + } + + public static synchronized void showAuthedChannels(){ + for(Map.Entry> entry : authedChannels.entrySet()){ + for (WrapperedChannel channel:entry.getValue()){ + logger.debug("{}-->{}",entry.getKey(),channel.toString()); + } + } + } + public static synchronized void showAllChannels(){ + for(Map.Entry entry : rawChannels.entrySet()){ + logger.debug(entry.getValue().toString()); + } + } + + public static Set getAllOnlineUsers() { + return authedChannels.keySet(); + } + + public static String getRecordIdByChannel(Channel channel) { + System.out.println(rawChannels.containsKey(channel) ? rawChannels.get(channel).getRecordId() : null); + return rawChannels.containsKey(channel) ? rawChannels.get(channel).getRecordId() : null; + } +} diff --git a/game/src/main/java/com/ccsens/game/netty/GameMessageConstant.java b/game/src/main/java/com/ccsens/game/netty/GameMessageConstant.java new file mode 100644 index 00000000..a144b049 --- /dev/null +++ b/game/src/main/java/com/ccsens/game/netty/GameMessageConstant.java @@ -0,0 +1,217 @@ +package com.ccsens.game.netty; + +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * @author wei + * 消息相关常量 + */ +public class GameMessageConstant { + public enum ClientMessageType{ + //客户端心跳 + Ping(0x00), + //客户端认证 + Auth(0x01), + //客户端收到消息ACK + Ack(0x02), + //客户端收到消息ACK + HasRead(0x03), + //客户端请求连接状态 + GetChannelStatus(0x04), + + //不可预期的错误 + UnExceptedError(0x21), + ClientIdleClosed(0x22), + ClientAuthTimeOut(0x23), + + //设置消息状态消息(disable撤销消息,适用于,消息撤回),针对消息本身(eg:消息撤回) + SetMsgSuccess(0x52), + SetMsgReverted(0x53), + SetMsgDeleted(0x54); + + public int value; + + ClientMessageType(int value){ + this.value = value; + } + + /** + * 从int到enum的转换函数 + * @param value 枚举int值 + * @return 对应的枚举,找不到则返回null + */ + public static ClientMessageType valueOf(int value) { + for(ClientMessageType type : values()){ + if(type.value == value){ + return type; + } + } + return null; + } + } + + public enum ServerMessageType{ + //客户端心跳 + Pong(0x00), + //客户端收到消息ACK + Ack(0x02), + //客户端请求连接状态 + ChannelStatus(0x03); + //撤销某个消息 + //DelMessage(0x04), + //客户端请求连接状态 + //QueueStatus(0x05); + + public int value; + + ServerMessageType(int value){ + this.value = value; + } + + /** + * 从int到enum的转换函数 + * @param value 枚举int值 + * @return 对应的枚举,找不到则返回null + */ + public static ServerMessageType valueOf(int value) { + for(ServerMessageType type : values()){ + if(type.value == value){ + return type; + } + } + return null; + } + } + + /** + * JsonFormat是Json Serialize相关配置 + */ + @JsonFormat(shape = JsonFormat.Shape.OBJECT) + public enum Error{ + //Ok + Ok(200,"Ok"), + + /** + * 通用消息错误 + */ + //消息无接收者错误 + MessageNoReceiversError(1001,"[用户]消息至少应该有一个接收者"), + //消息无数据错误 + MessageNoDataError(1002,"不允许消息内容为空"), + + /** + * server消息错误 + */ + //server消息无type错误 + MessageNoTypeError(1003,"发送至Server域的消息必须有type字段"), + //Ack参数错误,没有找到对应的msgId + AckParameterError(1103,"Ack参数错误,没有找到对应的msgId"), + //SetSuccess参数错误,没有找到对应的msgId + SetSuccessParameterError(1104,"SetSuccess参数错误,没有找到对应的msgId"), + //SetStatusParameterError + SetStatusParameterError(1105,"SetStatus参数错误,没有找到对应的msgId"), + //HasRead参数错误,没有找到对应的msgId + HasReadParameterError(1106,"HasRead参数错误,没有找到对应的msgId"), + + /** + * 身份认证错误 + */ + //无权限 + UnAuthed(1021, "未认证的用户"), + + /** + * 业务错误 + */ + //认证失败 + AuthFailed(1301,"认证失败"), + //空闲断开连接(连续Ns没有收到数据) + ChannelIdle(1302,"连接断开:连续N秒没有从收到客户端收到任何数据"), + //认证超时断开连接 + ChannelAuthTimeOut(1303,"连接断开:认证超时"), + //不可预期错误 + UnExpectedError(1304,"不可预期异常"); + + public int code; + public String text; + public String extra; + + Error(int code,String text) { + this.code = code; + this.text = text; + } + Error(int code,String text,String extra) { + this.code = code; + this.text = text; + this.extra = extra; + } + public Error joinExtra(String extra){ + this.extra = extra; + return this; + } + } + + + /** + * 域(User、Queue、Rest、Server) + */ + public enum DomainType{ + //Netty Client + User(1), + //Queue Client + Queue(2), + //Rest Client + Rest(3), + //系统 + Server(4); + + public int value; + + DomainType(int value){ + this.value = value; + } + + public static DomainType valueOf(int value) { + for(DomainType domainType : values()){ + if(domainType.value == value){ + return domainType; + } + } + return null; + } + } + + public enum Status{ + //未决状态(未完成) + Pending(0), + //发送成功(投递成功) + Succeed(1), + //发送失败(投递失败) + Failed(2), + //消息过期 + Expired(3), + //消息被撤回 + Reverted(4), + //消息被删除 + Deleted(5), + //消息达到重试次数上限 + SendTimesUpLimit(6), + + //临时完成状态,当ackIsSuccess==0时,一旦ack设置消息为此状态 + TempSucceed(10); + + public int value; + + Status(int value){ + this.value = value; + } + + public static Status valueOf(int value){ + for(Status status : values()){ + if(status.value == value){ + return status; + } + } + return null; + } + } +} diff --git a/game/src/main/java/com/ccsens/game/netty/WrapperedChannel.java b/game/src/main/java/com/ccsens/game/netty/WrapperedChannel.java new file mode 100644 index 00000000..289e4146 --- /dev/null +++ b/game/src/main/java/com/ccsens/game/netty/WrapperedChannel.java @@ -0,0 +1,210 @@ +package com.ccsens.game.netty; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; +import io.netty.channel.Channel; +import lombok.Getter; +import lombok.Setter; + +import java.net.InetSocketAddress; + +/** + * @author wei + */ +@Getter +@Setter +public class WrapperedChannel { + @Getter + @Setter + private static class ClientVersion{ + int major; + int minor; + public ClientVersion(int major,int minor){ + this.major = major; + this.minor = minor; + } + } + + /** + * Netty's Channel + */ + private Channel channel; + /** + * 用户加入的游戏id + */ + private String recordId; + /** + * userId + */ + private String userId; + /** + * 客户端版本号 + */ + private ClientVersion version; + + /** + * 客户端类型(哪种服务器) + */ + private String type; + /** + * 是否认证 + */ + private boolean authed; + /** + * 连接建立时间 + */ + private Long createdAtInSeconds; + /** + * 认证时间(s) + */ + private Long authedAtInSeconds; + /** + * 最后一次接收有效数据时间(包括心跳包) + */ + private Long lastDataReceivedInSeconds; + /** + * 最后一次发送有效数据时间(包括心跳包) + */ + private Long lastDataSendInSeconds; + /** + * 接收到数据条数(每接收到一条协议加1,包括心跳) + */ + private long dataReceivedCount; + /** + * 发送数据条数(每发送到一条协议加1,包括心跳) + */ + private long dataSendCount; + + public WrapperedChannel(Channel channel){ + this.channel = channel; + this.createdAtInSeconds = DateUtil.currentSeconds(); + } + + public WrapperedChannel(Channel channel,String type){ + this(channel); + this.type = type; + } + + public WrapperedChannel(Channel channel,String type,int major,int minor){ + this(channel,type); + this.version = new ClientVersion(major,minor); + } + + public String getVersion(){ + if(ObjectUtil.isNotNull(version)){ + return "v" + version.getMajor() + "." + version.getMinor(); + } + return null; + } + + public void setVersion(int major,int minor){ + if (ObjectUtil.isNull(version)) { + version = new ClientVersion(major, minor); + } else { + version.setMajor(major); + version.setMinor(minor); + } + } + + public String getRemoteAddr(){ + if(ObjectUtil.isNotNull(channel)){ + InetSocketAddress insocket = (InetSocketAddress) channel.remoteAddress(); + if(ObjectUtil.isNotNull(insocket)) { + return insocket.getAddress().getHostAddress(); + } + } + return null; + } + public Integer getRemotePort(){ + if(ObjectUtil.isNotNull(channel)){ + InetSocketAddress insocket = (InetSocketAddress) channel.remoteAddress(); + if(ObjectUtil.isNotNull(insocket)) { + return insocket.getPort(); + } + } + return null; + } + + public String getId(){ + if(ObjectUtil.isNotNull(channel)){ + return channel.id().asLongText(); + } + return null; + } + + public void whenReceivedData(){ + lastDataReceivedInSeconds = DateUtil.currentSeconds(); + dataReceivedCount++; + } + + public void whenSendData(){ + lastDataSendInSeconds = DateUtil.currentSeconds(); + dataSendCount++; + } + + public void whenAuthed(){ + authed = true; + authedAtInSeconds = DateUtil.currentSeconds(); + } + + public void whenAuthed(String userId,int major,int minor){ + whenAuthed(); + setVersion(major,minor); + this.userId = userId; + } + public void whenAuthed(String userId,int major,int minor,String recordId){ + whenAuthed(); + setVersion(major,minor); + this.userId = userId; + this.recordId = recordId; + } + + public void writeAndFlush(Object message){ + if(channel != null && channel.isActive()){ + channel.writeAndFlush(message); + whenSendData(); + } + } + + public Long getConnectedSeconds(){ + return createdAtInSeconds == null ? null : DateUtil.currentSeconds() - createdAtInSeconds; + } + + public Long getOnlineSeconds(){ + return authedAtInSeconds == null ? null : DateUtil.currentSeconds() - authedAtInSeconds ; + } + + @Override + public boolean equals(Object obj) { + if(ObjectUtil.isNull(obj)) { + return false; + } + if(this == obj) { + return true; + } + if(ObjectUtil.isNull(channel)) { + return false; + } + if(obj.getClass() == this.getClass()){ + WrapperedChannel other = (WrapperedChannel)obj; + return channel.equals(other.channel); + }else if(obj.getClass() == channel.getClass()){ + Channel other = (Channel)obj; + return channel.equals(other); + } + return false; + } + + @Override + public int hashCode() { + if(ObjectUtil.isNotNull(channel)){ + return channel.hashCode(); + } + return 0; + } + + @Override + public String toString() { + return String.format("id: %s, type: %s, authed: %b, userId: %s, version: %s",getId(),type,authed,userId,getVersion()); + } +} diff --git a/game/src/main/java/com/ccsens/game/netty/wsserver/NettyWsServer.java b/game/src/main/java/com/ccsens/game/netty/wsserver/NettyWsServer.java new file mode 100644 index 00000000..69632d3d --- /dev/null +++ b/game/src/main/java/com/ccsens/game/netty/wsserver/NettyWsServer.java @@ -0,0 +1,70 @@ +package com.ccsens.game.netty.wsserver; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.*; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpServerCodec; +import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; +import io.netty.handler.stream.ChunkedWriteHandler; +import io.netty.handler.timeout.IdleStateHandler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +/** + * @author wei + */ +@Component +public class NettyWsServer { + private static final String URI = "/game/v3.0/ws"; + private static final short PORT = 7070; + + @Autowired + private WebSocketHandler webSocketHandler; + + @Async + public void start() { + // Configure the server. + EventLoopGroup bossGroup = new NioEventLoopGroup(1); + EventLoopGroup workerGroup = new NioEventLoopGroup(); + try { + ServerBootstrap b = new ServerBootstrap(); + b.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .option(ChannelOption.SO_BACKLOG, 100) + .handler(new LoggingHandler(LogLevel.INFO)) + .childHandler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline p = ch.pipeline(); + p.addLast(new IdleStateHandler(40, + 0, 0, TimeUnit.SECONDS)); + p.addLast(new HttpServerCodec()); + p.addLast(new HttpObjectAggregator(64 * 1024)); + p.addLast(new ChunkedWriteHandler()); + p.addLast(new WebSocketServerProtocolHandler(URI)); + p.addLast(new WebSocketDecoder()); + p.addLast(new WebSocketEncoder()); + p.addLast(webSocketHandler); + } + }); + // Start the server. + ChannelFuture f = b.bind(PORT).sync(); + // Wait until the server socket is closed. + f.channel().closeFuture().sync(); + } catch(Exception e){ + e.printStackTrace(); + }finally { + // Shut down all event loops to terminate all threads. + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + } + } +} diff --git a/game/src/main/java/com/ccsens/game/netty/wsserver/WebSocketDecoder.java b/game/src/main/java/com/ccsens/game/netty/wsserver/WebSocketDecoder.java new file mode 100644 index 00000000..e343a2da --- /dev/null +++ b/game/src/main/java/com/ccsens/game/netty/wsserver/WebSocketDecoder.java @@ -0,0 +1,63 @@ +package com.ccsens.game.netty.wsserver; + +import com.ccsens.game.bean.dto.message.*; +import com.ccsens.util.JacksonUtil; +import com.ccsens.util.WebConstant; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageDecoder; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.List; + +/** + * @author wei + */ +public class WebSocketDecoder extends MessageToMessageDecoder { + private static Logger logger = LoggerFactory.getLogger(WebSocketDecoder.class); + +// @Override +// protected void decode(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame msg, List out) throws Exception { +// String text = msg.text(); +// logger.info("Websocket received: {}",text); +// +// try { +// +// out.add(JacksonUtil.jsonToBean(text, BaseMessageDto.class)); +// }catch(IOException e){ +// e.printStackTrace(); +// logger.error("Websocket Read Error: {}",text); +// throw e; +// } +// } +@Override +protected void decode(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame msg, List out) throws Exception { + String text = msg.text(); + BaseMessageDto baseMessage = JacksonUtil.jsonToBean(text,BaseMessageDto.class); + WebConstant.Message_Type type = WebConstant.Message_Type.phaseOf(baseMessage.getType()); + System.out.println(text); + switch (type){ + case Heart: { + out.add(JacksonUtil.jsonToBean(text, HeartMessageDto.class)); + break; + } + case Auth:{ + out.add(JacksonUtil.jsonToBean(text, AuthMessageDto.class)); + break; + }case Ack:{ + out.add(JacksonUtil.jsonToBean(text, AckMessageDto.class)); + break; + }case Count:{ + out.add(JacksonUtil.jsonToBean(text, GameMessageCountIn.class)); + break; + }case ChangeStatus:{ + out.add(JacksonUtil.jsonToBean(text, GameMessageWithChangeStatusOut.class)); + break; + } + default: + break; + } +} +} diff --git a/game/src/main/java/com/ccsens/game/netty/wsserver/WebSocketEncoder.java b/game/src/main/java/com/ccsens/game/netty/wsserver/WebSocketEncoder.java new file mode 100644 index 00000000..1430ce80 --- /dev/null +++ b/game/src/main/java/com/ccsens/game/netty/wsserver/WebSocketEncoder.java @@ -0,0 +1,26 @@ +package com.ccsens.game.netty.wsserver; + +import com.ccsens.game.bean.dto.message.BaseMessageDto; +import com.ccsens.util.JacksonUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author wei + */ +public class WebSocketEncoder extends MessageToByteEncoder { + private static Logger logger = LoggerFactory.getLogger(WebSocketEncoder.class); + + @Override + protected void encode(ChannelHandlerContext ctx, BaseMessageDto outMessageSet, ByteBuf out) throws Exception { + String msg = JacksonUtil.beanToJson(outMessageSet); + ctx.writeAndFlush(new TextWebSocketFrame(msg)); + + logger.info("Websocket send: {}",msg); + } + +} diff --git a/game/src/main/java/com/ccsens/game/netty/wsserver/WebSocketHandler.java b/game/src/main/java/com/ccsens/game/netty/wsserver/WebSocketHandler.java new file mode 100644 index 00000000..1b2fadff --- /dev/null +++ b/game/src/main/java/com/ccsens/game/netty/wsserver/WebSocketHandler.java @@ -0,0 +1,136 @@ +package com.ccsens.game.netty.wsserver; + + +import com.ccsens.game.bean.dto.message.*; +import com.ccsens.game.netty.ChannelManager; +import com.ccsens.game.service.IClientService; +import com.ccsens.game.service.IMessageService; +import com.ccsens.util.WebConstant; +import com.ccsens.util.bean.message.common.MessageConstant; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.timeout.IdleState; +import io.netty.handler.timeout.IdleStateEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author wei + */ +@ChannelHandler.Sharable +@Component +public class WebSocketHandler extends SimpleChannelInboundHandler { + private static final String TYPE = "netty_ws"; + + private static Logger logger = LoggerFactory.getLogger(WebSocketHandler.class); + + @Autowired + private IClientService clientService; + @Autowired + private IMessageService messageService; + + @Override + public void handlerAdded(ChannelHandlerContext ctx) throws Exception { + } + + @Override + public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + ChannelManager.addChannel(ctx.channel(), TYPE); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + ChannelManager.removeChannel(ctx.channel()); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + + cause.printStackTrace(); + ctx.close(); + logger.error("Channel closed. Ws get a exception: {}", cause.getMessage()); + } + + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + if (evt instanceof IdleStateEvent) { + IdleStateEvent idleStateEvent = (IdleStateEvent) evt; + if (idleStateEvent.state() == IdleState.READER_IDLE) { + ctx.channel().close(); + logger.error("Ws channel idle,closed."); + } + } else { + super.userEventTriggered(ctx, evt); + } + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, BaseMessageDto baseMessage) throws Exception { + MessageConstant.GameClientMessageType gameClientMessageType = MessageConstant.GameClientMessageType.valueOf(baseMessage.getType()); + + System.out.println(baseMessage); + try { + switch (gameClientMessageType) { + case Heart: { + HeartMessageDto message = (HeartMessageDto) baseMessage; + message.setTime(System.currentTimeMillis()); + if (message.getData() != null) { + ChannelManager.versionChannel( + ctx.channel(), message.getData().getMajor(), message.getData().getMinor()); + } + ChannelManager.sendTo(ctx.channel(), message); + break; + } + case Ack: { + AckMessageDto message = (AckMessageDto) baseMessage; + String userId = ChannelManager.getUserIdByChannel(ctx.channel()); + messageService.doAckMessageWithAck(userId, message); + break; + } + case Auth: + AuthMessageDto theMessage = (AuthMessageDto) baseMessage; + theMessage.getData().setChannelId(ctx.channel().id().asLongText()); + doAuthMessage(ctx, theMessage); + break; + case Count: + String userId = ChannelManager.getUserIdByChannel(ctx.channel()); + String recordId = ChannelManager.getRecordIdByChannel(ctx.channel()); + GameMessageCountOut gameMessageCountOut = clientService.clientAddTimes(userId,recordId); + ChannelManager.sendTo(ctx.channel(), gameMessageCountOut); + break; + case ChangeStatus: + GameMessageWithChangeStatusOut gameMessage = (GameMessageWithChangeStatusOut) baseMessage; + messageService.doChangeStatusMessageDto(gameMessage); + break; + default: + break; + } + } catch (Exception e) { + e.printStackTrace(); + logger.error("Websocket Process Message Failed: {},{}", e.getMessage(), baseMessage); + throw e; + } finally { + ChannelManager.removeCurrentChannel(); + } + } + + private void doAuthMessage(ChannelHandlerContext ctx, AuthMessageDto message) throws Exception { + WebConstant.Message_Auth_Event event = WebConstant.Message_Auth_Event.phaseOf(message.getEvent()); + switch (event) { + case Auth: + messageService.doAuthMessageWithAuth(ctx, message); + break; + default: + break; + } + } +} + diff --git a/game/src/main/java/com/ccsens/game/persist/dao/GameUserJoinDao.java b/game/src/main/java/com/ccsens/game/persist/dao/GameUserJoinDao.java index 60ca0fde..ea732bac 100644 --- a/game/src/main/java/com/ccsens/game/persist/dao/GameUserJoinDao.java +++ b/game/src/main/java/com/ccsens/game/persist/dao/GameUserJoinDao.java @@ -1,10 +1,13 @@ package com.ccsens.game.persist.dao; +import com.ccsens.game.bean.dto.message.ChangeStatusMessageDto; import com.ccsens.game.bean.vo.ClientVo; import com.ccsens.game.persist.mapper.GameUserJoinMapper; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface GameUserJoinDao extends GameUserJoinMapper { @@ -16,6 +19,21 @@ public interface GameUserJoinDao extends GameUserJoinMapper { */ int getRanking(@Param("userId") Long userId, @Param("recordId") Long recordId); - ClientVo.MemberInfo selectByRecordId(@Param("recordId") Long recordId); + List selectByRecordId(@Param("recordId") Long recordId); + + /** + * 获取游戏结束时的数据 + * @param recordId + * @return + */ + ChangeStatusMessageDto.CompletedData gameCount(@Param("recordId") Long recordId); + + /** + * 超过多少人 + * @param recordId + * @param score + * @return + */ + int overNum(@Param("recordId") Long recordId, @Param("score") Integer score); } diff --git a/game/src/main/java/com/ccsens/game/service/ClientService.java b/game/src/main/java/com/ccsens/game/service/ClientService.java index dfec6d2d..90728fc2 100644 --- a/game/src/main/java/com/ccsens/game/service/ClientService.java +++ b/game/src/main/java/com/ccsens/game/service/ClientService.java @@ -2,10 +2,12 @@ package com.ccsens.game.service; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.lang.Snowflake; +import cn.hutool.core.util.ObjectUtil; import com.alibaba.fastjson.JSON; import com.ccsens.cloudutil.bean.tall.vo.MemberVo; import com.ccsens.cloudutil.feign.TallFeignClient; import com.ccsens.game.bean.dto.ClientDto; +import com.ccsens.game.bean.dto.message.*; import com.ccsens.game.bean.po.GameRecord; import com.ccsens.game.bean.po.GameUserJoin; import com.ccsens.game.bean.po.GameUserJoinExample; @@ -13,6 +15,7 @@ import com.ccsens.game.bean.vo.ClientVo; import com.ccsens.game.persist.dao.GameRecordDao; import com.ccsens.game.persist.dao.GameUserJoinDao; import com.ccsens.game.util.GameConstant; +import com.ccsens.game.util.SendMsg; import com.ccsens.util.CodeEnum; import com.ccsens.util.JsonResponse; import com.ccsens.util.RedisUtil; @@ -27,8 +30,11 @@ import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.ccsens.util.bean.dto.QueryDto; +import java.util.ArrayList; import java.util.List; - +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; /** @@ -53,6 +59,8 @@ public class ClientService implements IClientService { private TallFeignClient tallFeignClient; @Autowired private DelayProducer delaySender; + @Autowired + private SendMsg sendMsg; @Override public ClientVo.Join join(ClientDto.Join join, Long userId) { @@ -117,19 +125,24 @@ public class ClientService implements IClientService { user.setAvatarurl(userJoin.getAvatarUrl()); user.setNickname(userJoin.getNickname()); redisUtil.zsSet(GameConstant.generateGameKey(gameRecord.getId()), JSON.toJSONString(user), 0, GameConstant.REDIS_TIME); + ScheduledExecutorService executor = Executors.newScheduledThreadPool(10); + List joins = new ArrayList<>(); + joins.add(userJoin); //4.根据状态延时发送消息 if (prepare) { //延时发送开始 long expiration = gameRecord.getStartTime() - System.currentTimeMillis(); - //TODO 发送内容未定 - delaySender.sendDelayMessage("开始啦", expiration > 0 ? expiration : 0); + sendMsg.sendMsg(executor, expiration, ()->{ + sendMsg.sendStatus(gameRecord, joins, GameConstant.GAME_PROCESSING); + }); } if (prepare || processing) { //延时发送结束 - long expiration = gameRecord.getEndTime() - System.currentTimeMillis(); - //TODO 发送内容未定 - delaySender.sendDelayMessage("结束了", expiration > 0 ? expiration : 0); + long expiration = gameRecord.getStartTime() - System.currentTimeMillis(); + sendMsg.sendMsg(executor, expiration, ()->{ + sendMsg.sendStatus(gameRecord, joins, GameConstant.GAME_PROCESSING); + }); } @@ -165,7 +178,11 @@ public class ClientService implements IClientService { completedData.setTimes(join.getTimes()); completedData.setScore(join.getScore()); completedData.setSort(gameUserJoinDao.getRanking(join.getUserId(), join.getUserId())); - // TODO 超过百分之几的用户 + int low = gameUserJoinDao.overNum(gameRecord.getId(), join.getScore()); + GameUserJoinExample joinExample = new GameUserJoinExample(); + joinExample.createCriteria().andRecordIdEqualTo(gameRecord.getId()); + long count = gameUserJoinDao.countByExample(joinExample); + completedData.setOver((int) (low/count * 100)); joinVo.setCompletedData(completedData); break; default: break; @@ -173,7 +190,6 @@ public class ClientService implements IClientService { log.info("参加游戏:{}", joinVo); return joinVo; } - /** * 设置状态和总人数 * @param gameRecord @@ -192,20 +208,55 @@ public class ClientService implements IClientService { joinVo.setCountMembers(count); return joinVo; } + + /** + * 查询总排名表 + * @param params + * @return + */ @Override public ClientVo.RankingAll getRankingAll(QueryDto params) { ClientDto.GatRanking gatRanking = params.getParam(); + ClientVo.RankingAll rankingAll = new ClientVo.RankingAll(); GameUserJoinExample userJoinExample = new GameUserJoinExample(); userJoinExample.createCriteria().andRecordIdEqualTo(gatRanking.getRecordId()); List userJoinList = gameUserJoinDao.selectByExample(userJoinExample); - + if(CollectionUtil.isNotEmpty(userJoinList)){ + rankingAll.setTotalMembers(userJoinList.size()); + }else { + rankingAll.setTotalMembers(0); + } PageHelper.startPage(gatRanking.getPageNum(), gatRanking.getPageSize()); - ClientVo.MemberInfo memberInfo = gameUserJoinDao.selectByRecordId(gatRanking.getRecordId()); + List memberInfo = gameUserJoinDao.selectByRecordId(gatRanking.getRecordId()); log.info("查询成员信息"); - PageInfo pageInfo =new PageInfo<>(); + PageInfo pageInfo =new PageInfo<>(memberInfo); + + rankingAll.setMemberInfoList(memberInfo); + rankingAll.setFirstPage(pageInfo.isIsFirstPage()); + rankingAll.setLastPage(pageInfo.isIsLastPage()); + + return rankingAll; + } - return null; + @Override + public GameMessageCountOut clientAddTimes(String userId,String recordId) { + GameMessageCountOut gameMessageCountOut = new GameMessageCountOut(); + + if(ObjectUtil.isNotNull(userId)){ + GameUserJoinExample gameUserJoinExample = new GameUserJoinExample(); + gameUserJoinExample.createCriteria().andUserIdEqualTo(Long.valueOf(userId)).andRecordIdEqualTo(Long.valueOf(recordId)); + List userJoinList = gameUserJoinDao.selectByExample(gameUserJoinExample); + if(CollectionUtil.isNotEmpty(userJoinList)){ + GameUserJoin userJoin = userJoinList.get(0); + userJoin.setTimes(userJoin.getTimes() + 1); + userJoin.setScore(userJoin.getScore() + 1); + gameUserJoinDao.updateByPrimaryKeySelective(userJoin); + + gameMessageCountOut = new GameMessageCountOut(userJoin.getTimes(),userJoin.getScore()); + } + } + return gameMessageCountOut; } } diff --git a/game/src/main/java/com/ccsens/game/service/IClientService.java b/game/src/main/java/com/ccsens/game/service/IClientService.java index 6a10060a..04818203 100644 --- a/game/src/main/java/com/ccsens/game/service/IClientService.java +++ b/game/src/main/java/com/ccsens/game/service/IClientService.java @@ -1,6 +1,8 @@ package com.ccsens.game.service; import com.ccsens.game.bean.dto.ClientDto; +import com.ccsens.game.bean.dto.message.BaseMessageDto; +import com.ccsens.game.bean.dto.message.GameMessageCountOut; import com.ccsens.game.bean.vo.ClientVo; import com.ccsens.util.bean.dto.QueryDto; @@ -20,4 +22,9 @@ public interface IClientService { ClientVo.Join join(ClientDto.Join join, Long userId); ClientVo.RankingAll getRankingAll(QueryDto params); + + /** + * 滑动时增加次数 + */ + GameMessageCountOut clientAddTimes(String userId, String recordId); } diff --git a/game/src/main/java/com/ccsens/game/service/IMessageService.java b/game/src/main/java/com/ccsens/game/service/IMessageService.java new file mode 100644 index 00000000..ff664027 --- /dev/null +++ b/game/src/main/java/com/ccsens/game/service/IMessageService.java @@ -0,0 +1,17 @@ +package com.ccsens.game.service; + +import com.ccsens.game.bean.dto.message.*; +import com.fasterxml.jackson.core.JsonProcessingException; +import io.netty.channel.ChannelHandlerContext; + +public interface IMessageService { + //获取路径后给每个人发送游戏路径消息 + void sendGameMessageWithGetUrl(ChromeMessageDto message) throws Exception; + + void doAckMessageWithAck(String userId, AckMessageDto message); + + void doAuthMessageWithAuth(ChannelHandlerContext ctx, AuthMessageDto message); + + void doChangeStatusMessageDto(GameMessageWithChangeStatusOut gameMessage); +} + diff --git a/game/src/main/java/com/ccsens/game/service/IScreenService.java b/game/src/main/java/com/ccsens/game/service/IScreenService.java index 95cfdc2b..068d803c 100644 --- a/game/src/main/java/com/ccsens/game/service/IScreenService.java +++ b/game/src/main/java/com/ccsens/game/service/IScreenService.java @@ -5,7 +5,7 @@ import com.ccsens.game.bean.vo.ScreenVo; import com.ccsens.util.bean.dto.QueryDto; public interface IScreenService { - ScreenVo.UrlVo getScreenUrl(QueryDto params); + ScreenVo.UrlVo getScreenUrl(QueryDto params) throws Exception; ScreenVo.GameInfoVo getGameInformation(QueryDto params); diff --git a/game/src/main/java/com/ccsens/game/service/MessageService.java b/game/src/main/java/com/ccsens/game/service/MessageService.java new file mode 100644 index 00000000..92979124 --- /dev/null +++ b/game/src/main/java/com/ccsens/game/service/MessageService.java @@ -0,0 +1,96 @@ +package com.ccsens.game.service; + +import cn.hutool.core.collection.CollectionUtil; +import com.ccsens.cloudutil.feign.TallFeignClient; +import com.ccsens.game.bean.dto.message.*; +import com.ccsens.game.bean.po.GameUserJoin; +import com.ccsens.game.bean.po.GameUserJoinExample; +import com.ccsens.game.netty.ChannelManager; +import com.ccsens.game.persist.dao.GameUserJoinDao; +import com.ccsens.util.JacksonUtil; +import com.ccsens.util.config.RabbitMQConfig; +import io.netty.channel.ChannelHandlerContext; +import org.springframework.amqp.core.AmqpTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +import static com.ccsens.game.netty.ChannelManager.sendTo; + + +@Service +public class MessageService implements IMessageService { + @Autowired + private AmqpTemplate rabbitTemplate; + @Autowired + private TallFeignClient tallFeignClient; + @Autowired + private GameUserJoinDao gameUserJoinDao; + + @Override + public void sendGameMessageWithGetUrl(ChromeMessageDto message) throws Exception { + System.out.println(JacksonUtil.beanToJson(message)); + rabbitTemplate.convertAndSend(RabbitMQConfig.RabbitMQ_QUEUE_NAME, + JacksonUtil.beanToJson(message)); + } + + @Override + public void doAckMessageWithAck(String userId, AckMessageDto message) { +// System.out.println(message); +// //1.存储到MongoDB(ack消息不需要存储,修改响应消息的发送成功标志即可) +//// Long userId = message.getSender().getUserId(); +// Long msgId = message.getData().getMsgId(); +// messageLogDao.updateMessageLogSendSuccess(userId,msgId,1); + } + + @Override + public void doAuthMessageWithAuth(ChannelHandlerContext ctx, AuthMessageDto message) { + System.out.println(message); + //ws连接认证 + String userId = message.getData().getUserId(); + if (userId == null) { + String token = message.getData().getToken(); + userId = tallFeignClient.getUserId(token); + } + String recordId = message.getData().getId(); + ChannelManager.authChannel(ctx.channel(), userId, 1, 1, recordId); + AuthMessageWithAnswerDto messageWithAnswerDto = new AuthMessageWithAnswerDto(true, "ok"); + sendTo(ctx.channel(), messageWithAnswerDto); + + +// if(userId == null){ +//// logger.warn(String.format("Auth failed: %s",userId,message.getData().getToken())); +//// ChannelManager.removeChannel(ctx.channel()); +// AuthMessageWithAnswerDto messageWithAnswerDto = new AuthMessageWithAnswerDto(false,"未找到对应的用户信息"); +// sendTo(ctx.channel(),messageWithAnswerDto); +// ctx.channel().close(); +// return; +// } +// String channelId = message.getData().getChannelId(); +// if(ChannelManager.authChannel(ctx.channel(),userId)){ //认证成功 + //查找所有该用户相关未收到的消息和未读消息依次发送给该用户 +// sendAllUnSendSuccessedMessagesByUserId(userId,0L); +// AuthMessageWithAnswerDto messageWithAnswerDto = new AuthMessageWithAnswerDto(true,"ok"); +// sendTo(ctx.channel(),messageWithAnswerDto); +// }else { +//// AuthMessageWithAnswerDto messageWithAnswerDto = new AuthMessageWithAnswerDto(false,"认证失败"); +//// sendTo(ctx.channel(),messageWithAnswerDto); +// } + } + + + @Override + public void doChangeStatusMessageDto(GameMessageWithChangeStatusOut gameMessage) { + Long recordId = gameMessage.getData().getRecordId(); + GameUserJoinExample gameUserJoinExample = new GameUserJoinExample(); + gameUserJoinExample.createCriteria().andRecordIdEqualTo(recordId); + List userJoinList = gameUserJoinDao.selectByExample(gameUserJoinExample); + if (CollectionUtil.isNotEmpty(userJoinList)) { + for(GameUserJoin userJoin : userJoinList){ + sendTo(userJoin.getUserId().toString(),gameMessage); + } + } + } +} diff --git a/game/src/main/java/com/ccsens/game/service/ScreenService.java b/game/src/main/java/com/ccsens/game/service/ScreenService.java index d7caa08e..f282163e 100644 --- a/game/src/main/java/com/ccsens/game/service/ScreenService.java +++ b/game/src/main/java/com/ccsens/game/service/ScreenService.java @@ -3,20 +3,24 @@ package com.ccsens.game.service; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.lang.Snowflake; import cn.hutool.core.util.ObjectUtil; -import com.alibaba.fastjson.JSONObject; +import com.ccsens.cloudutil.feign.TallFeignClient; import com.ccsens.game.bean.dto.ScreenDto; +import com.ccsens.game.bean.dto.message.BaseMessageDto; +import com.ccsens.game.bean.dto.message.ChangeStatusMessageDto; +import com.ccsens.game.bean.dto.message.ChromeMessageDto; +import com.ccsens.game.bean.dto.message.GameMessageWithChangeStatusOut; import com.ccsens.game.bean.po.*; import com.ccsens.game.bean.vo.ScreenVo; import com.ccsens.game.persist.dao.*; import com.ccsens.game.util.GameConstant; +import com.ccsens.game.util.SendMsg; import com.ccsens.util.CodeEnum; import com.ccsens.util.WebConstant; import com.ccsens.util.bean.dto.QueryDto; -import com.ccsens.util.config.RabbitMQConfig; import com.ccsens.util.exception.BaseException; -import com.ccsens.util.mq.DelayProducer; + +import com.netflix.discovery.converters.Auto; 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.Service; @@ -46,21 +50,23 @@ public class ScreenService implements IScreenService{ @Autowired private GameUserJoinDao gameUserJoinDao; @Autowired - private DelayProducer delayProducer; + private TallFeignClient tallFeignClient; @Autowired - private DelayProducer delayProducer2; + private IMessageService messageService; @Autowired private Snowflake snowflake; @Autowired private RabbitTemplate rabbitTemplate; + @Autowired + private SendMsg sendMsg; /** * 传入用户信息,返回游戏大屏路径 * @return */ @Override - public ScreenVo.UrlVo getScreenUrl(QueryDto params) { + public ScreenVo.UrlVo getScreenUrl(QueryDto params) throws Exception { ScreenDto.MemberGame memberGame = params.getParam(); //查找游戏 GameType gameType = null; @@ -96,15 +102,33 @@ public class ScreenService implements IScreenService{ GameRecord gameRecord = new GameRecord(); gameRecord.setId(snowflake.nextId()); gameRecord.setUserPayId(gameUserPay.getId()); - gameRecord.setUrl(WebConstant.TEST_URL_GAME + gameRecord.getId() + File.separator + gameType.getScreenUrl()); + gameRecord.setUrl(WebConstant.TEST_URL_GAME + gameType.getScreenUrl() + "?id="+gameRecord.getId()); gameRecord.setQrCodeUrl(WebConstant.TEST_URL_GAME + gameRecord.getId() + File.separator + gameType.getClientUrl()); gameRecordDao.insertSelective(gameRecord); //5、查询该游戏的规则 List ruleList = getGameActivityRule(gameType.getId()); //6、返回 ScreenVo.UrlVo urlVo = new ScreenVo.UrlVo(); + urlVo.setId(gameRecord.getId()); urlVo.setUrl(gameRecord.getUrl()); urlVo.setRuleList(ruleList); + + //给所有人发送消息发送消息 + ChromeMessageDto chromeMessageDto = new ChromeMessageDto(gameRecord.getUrl(),gameRecord.getId(),memberGame.getProjectId()); + BaseMessageDto.MessageUser messageUser = null; + List messageUserList = new ArrayList<>(); + //获取项目下所有成员 + List memberIdList = tallFeignClient.getMemberIdListByProject(memberGame.getProjectId()); + if(CollectionUtil.isNotEmpty(memberIdList)){ + for(Long memberId:memberIdList){ + messageUser = new BaseMessageDto.MessageUser(); + messageUser.setUserId(memberId); + messageUserList.add(messageUser); + } + } + chromeMessageDto.setReceivers(messageUserList); + messageService.sendGameMessageWithGetUrl(chromeMessageDto); + return urlVo; } @@ -261,7 +285,7 @@ public class ScreenService implements IScreenService{ if(ObjectUtil.isNull(gameUserPay)){ throw new BaseException(CodeEnum.NOT_GAME_TYPE); } - GameType gameType = gameTypeDao.selectByPrimaryKey(gameUserPay.getId()); + GameType gameType = gameTypeDao.selectByPrimaryKey(gameUserPay.getGameTypeId()); if(ObjectUtil.isNull(gameType)){ throw new BaseException(CodeEnum.NOT_GAME_TYPE); } @@ -276,6 +300,24 @@ public class ScreenService implements IScreenService{ gameUserPay.setUsedCount(gameUserPay.getUsedCount() + 1); gameUserPayDao.updateByPrimaryKeySelective(gameUserPay); + + if(gameUserPay.getUsedCount() >= gameUserPay.getTotalCount()){ + throw new BaseException(CodeEnum. GAME_NOT_TIMES); + } + if(gameRecord.getGameStatus() == 3){ + //添加一场新的游戏记录 + gameRecordNew = new GameRecord(); + gameRecordNew.setId(snowflake.nextId()); + gameRecordNew.setUserPayId(gameUserPay.getId()); + gameRecordNew.setUrl(WebConstant.TEST_URL_GAME + gameType.getScreenUrl() + "?id="+gameRecord.getId()); + gameRecordNew.setQrCodeUrl(WebConstant.TEST_URL_GAME + gameRecordNew.getId() + File.separator + gameType.getClientUrl()); + gameRecordDao.insertSelective(gameRecordNew); + //修改购买的游戏的使用次数 + gameUserPay.setUsedCount(gameUserPay.getUsedCount() + 1); + gameUserPayDao.updateByPrimaryKeySelective(gameUserPay); + }else { + throw new BaseException(CodeEnum.GAME_NO_END); + } return gameRecordNew.getUrl(); } @@ -355,6 +397,7 @@ public class ScreenService implements IScreenService{ } + @Override public ScreenVo.StartGame startGame(ScreenDto.Start start) { long current = System.currentTimeMillis(); @@ -390,45 +433,44 @@ public class ScreenService implements IScreenService{ // JSONObject json2 = new JSONObject(); // json2.put("name", "修改游戏结束状态"); // delayProducer2.sendDelayMessage(json2.toJSONString(), endSend > 0 ? endSend : 0); - ScheduledExecutorService executor = Executors.newScheduledThreadPool(20); - Runnable startRunble = new Runnable() { - @Override - public void run() { - //更新状态 - gameRecord.setGameStatus(GameConstant.GAME_PREPARATION); - gameRecordDao.updateByPrimaryKeySelective(gameRecord); - //发送消息 - } - }; - sendMsg(executor, endSend, startRunble); - Runnable endRunble = new Runnable() { - @Override - public void run() { - //更新状态 + ScheduledExecutorService executor = Executors.newScheduledThreadPool(10); + //更新开始状态 + sendMsg.sendMsg(executor, startSend, () -> { gameRecord.setGameStatus(GameConstant.GAME_PREPARATION); gameRecordDao.updateByPrimaryKeySelective(gameRecord); - //发送消息 - } - }; - sendMsg(executor, endSend, endRunble); + }); + //更新结束状态 + sendMsg.sendMsg(executor, endSend, () -> { + gameRecord.setGameStatus(GameConstant.GAME_COMPLETED); + gameRecordDao.updateByPrimaryKeySelective(gameRecord); + }); //查询游戏用户,通知游戏开始和结束 GameUserJoinExample joinExample = new GameUserJoinExample(); joinExample.createCriteria().andRecordIdEqualTo(gameRecord.getId()); -// List userJoins = gameUserJoinDao.selectByExample(joinExample); -// userJoins.forEach(join -> { -// // 游戏开始 -// long clientStartSend = gameRecord.getStartTime() - System.currentTimeMillis(); -// long clientEndSend = gameRecord.getEndTime() - System.currentTimeMillis(); -// log.info("{}:{}--{}", join.getUserId(), clientStartSend, clientEndSend); -// json.put("name", "开始通知客户开始"+ join.getUserId()); -// delayProducer.sendDelayMessage(json, clientStartSend > 0 ? clientStartSend : 0); -// json.put("name", "开始通知客户结束"+ join.getUserId()); -// delayProducer.sendDelayMessage(json , clientEndSend > 0 ? clientEndSend : 0); -// -// }); + List userJoins = gameUserJoinDao.selectByExample(joinExample); + + + + if (CollectionUtil.isNotEmpty(userJoins)) { + + new Thread(()->{ + + + long startTime = gameRecord.getStartTime() - System.currentTimeMillis(); + sendMsg.sendMsg(executor, startTime > 0 ? startTime : 0, ()->{ + sendMsg.sendStatus(gameRecord, userJoins, GameConstant.GAME_PROCESSING); + }); + long endTime = gameRecord.getEndTime() - System.currentTimeMillis(); + sendMsg.sendMsg(executor, endTime > 0 ? endTime : 0, ()->{ + sendMsg.sendStatus(gameRecord, userJoins, GameConstant.GAME_COMPLETED); + }); + + }).start(); + + } //返回大屏开始时间 ScreenVo.StartGame startGame = new ScreenVo.StartGame(); @@ -436,13 +478,5 @@ public class ScreenService implements IScreenService{ return startGame; } - /** - * 定时任务 - * @param executor - * @param delayTime - * @param runnable - */ - private void sendMsg(ScheduledExecutorService executor, long delayTime, Runnable runnable) { - executor.schedule(new Thread(runnable), delayTime, TimeUnit.MILLISECONDS); - } + } diff --git a/game/src/main/java/com/ccsens/game/util/SendMsg.java b/game/src/main/java/com/ccsens/game/util/SendMsg.java new file mode 100644 index 00000000..dddc50b5 --- /dev/null +++ b/game/src/main/java/com/ccsens/game/util/SendMsg.java @@ -0,0 +1,76 @@ +package com.ccsens.game.util; + +import cn.hutool.core.collection.CollectionUtil; +import com.ccsens.game.bean.dto.message.ChangeStatusMessageDto; +import com.ccsens.game.bean.dto.message.GameMessageWithChangeStatusOut; +import com.ccsens.game.bean.po.GameRecord; +import com.ccsens.game.bean.po.GameUserJoin; +import com.ccsens.game.persist.dao.GameUserJoinDao; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * @description: + * @author: wuHuiJuan + * @create: 2019/12/28 15:49 + */ +@Service +public class SendMsg { + @Autowired + private GameUserJoinDao gameUserJoinDao; + + public void sendStatus(GameRecord gameRecord, List userJoins, byte status) { + List outs = new ArrayList<>(); + userJoins.forEach(join -> { + outs.add(getMsg(gameRecord, join, status)); + }); + if (CollectionUtil.isNotEmpty(outs)) { + //TODO fasong + } + } + + public GameMessageWithChangeStatusOut getMsg(GameRecord gameRecord, GameUserJoin join, byte gameStatus) { + GameMessageWithChangeStatusOut out = new GameMessageWithChangeStatusOut(); + GameMessageWithChangeStatusOut.Data data = new GameMessageWithChangeStatusOut.Data(); + data.setRecordId(gameRecord.getId()); + data.setGameStatus(gameStatus); + out.setUserId(join.getUserId()); + ChangeStatusMessageDto dtos = new ChangeStatusMessageDto(); + //游戏结束返回值 + ChangeStatusMessageDto.CompletedData completedData = null; + if (gameStatus == GameConstant.GAME_COMPLETED) { + completedData = gameUserJoinDao.gameCount(gameRecord.getId()); + completedData.setTotalTimes(completedData.getTotalScore()); + if (completedData.getAverageTimes() == null) { + completedData.setOver(0); + } else { + int num = gameUserJoinDao.overNum(gameRecord.getId(), completedData.getAverageTimes()); + completedData.setOver(completedData.getTotalMembers() == null || completedData.getTotalMembers().longValue() == 0 ? 100 : num/completedData.getTotalMembers() * 100); + } + dtos.setCompletedData(completedData); + } else { + ChangeStatusMessageDto.PendingData pendingData = new ChangeStatusMessageDto.PendingData(); + pendingData.setStartLocalTime(join.getLocalStartTime()); + pendingData.setEndLocalTime(join.getLocalEndTime()); + dtos.setPendingData(pendingData); + } + out.setData(data); + return out; + } + + /** + * 定时任务 + * @param executor + * @param delayTime + * @param runnable + */ + public void sendMsg(ScheduledExecutorService executor, long delayTime, Runnable runnable) { + executor.schedule(new Thread(runnable), delayTime, TimeUnit.MILLISECONDS); + } +} diff --git a/game/src/main/resources/mapper_dao/GameUserJoinDao.xml b/game/src/main/resources/mapper_dao/GameUserJoinDao.xml index f0a74a42..c8a20d99 100644 --- a/game/src/main/resources/mapper_dao/GameUserJoinDao.xml +++ b/game/src/main/resources/mapper_dao/GameUserJoinDao.xml @@ -33,4 +33,12 @@ ) c WHERE c.user_id= #{userId,jdbcType=BIGINT} limit 1 + + + + \ No newline at end of file diff --git a/mt/src/main/resources/druid-test.yml b/mt/src/main/resources/druid-test.yml index 4a60e9d8..1a0aa389 100644 --- a/mt/src/main/resources/druid-test.yml +++ b/mt/src/main/resources/druid-test.yml @@ -27,7 +27,7 @@ spring: testOnReturn: false testWhileIdle: true timeBetweenEvictionRunsMillis: 60000 - url: jdbc:mysql://127.0.0.1/mt?useUnicode=true&characterEncoding=UTF-8 + url: jdbc:mysql://49.233.89.188/mt?useUnicode=true&characterEncoding=UTF-8 username: root validationQuery: SELECT 1 FROM DUAL env: CCSENS_GAME \ No newline at end of file diff --git a/pom.xml b/pom.xml index c9d15fa3..d24d0af3 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,13 @@ + + + io.netty + netty-all + 4.1.32.Final + + org.springframework.boot spring-boot-starter-data-redis diff --git a/tall/src/main/java/com/ccsens/tall/bean/dto/message/ChromeMessageDto.java b/tall/src/main/java/com/ccsens/tall/bean/dto/message/ChromeMessageDto.java new file mode 100644 index 00000000..52c3d278 --- /dev/null +++ b/tall/src/main/java/com/ccsens/tall/bean/dto/message/ChromeMessageDto.java @@ -0,0 +1,32 @@ +package com.ccsens.tall.bean.dto.message; + +import com.ccsens.util.WebConstant; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +/** + * @author __zHangSan + */ +@Data +public class ChromeMessageDto extends BaseMessageDto{ + @Setter + @Getter + public static class Data{ + private String url; + } + private Data data; + + public ChromeMessageDto(){ + setType(WebConstant.Message_Type.Chrome.phase); + setTime(System.currentTimeMillis()); + } + + public ChromeMessageDto(String url){ + this(); + if(data == null){ + data = new Data(); + } + data.setUrl(url); + } +} diff --git a/tall/src/main/java/com/ccsens/tall/bean/dto/message/PPTCtlMessageDto.java b/tall/src/main/java/com/ccsens/tall/bean/dto/message/PPTCtlMessageDto.java new file mode 100644 index 00000000..28161fbd --- /dev/null +++ b/tall/src/main/java/com/ccsens/tall/bean/dto/message/PPTCtlMessageDto.java @@ -0,0 +1,35 @@ +package com.ccsens.tall.bean.dto.message; + +import com.ccsens.util.WebConstant; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +/** + * @author __zHangSan + */ +@Data +public class PPTCtlMessageDto extends BaseMessageDto{ + @Setter + @Getter + public static class Data{ + /** + * Supported Operation: up,down,begin,end + */ + private String oper; + } + private Data data; + + public PPTCtlMessageDto(){ + setType(WebConstant.Message_Type.PPTCtl.phase); + setTime(System.currentTimeMillis()); + } + + public PPTCtlMessageDto(String oper){ + this(); + if(data == null){ + data = new Data(); + } + data.setOper(oper); + } +} diff --git a/tall/src/main/java/com/ccsens/tall/bean/dto/message/SyncMessageWithStartDto.java b/tall/src/main/java/com/ccsens/tall/bean/dto/message/SyncMessageWithStartDto.java index 39d07e30..3ac80c1f 100644 --- a/tall/src/main/java/com/ccsens/tall/bean/dto/message/SyncMessageWithStartDto.java +++ b/tall/src/main/java/com/ccsens/tall/bean/dto/message/SyncMessageWithStartDto.java @@ -17,6 +17,7 @@ public class SyncMessageWithStartDto extends BaseMessageDto { Long endTaskId; Long time; Long duration; //获取当前节点时长,为倒计时做准备 + String player; //参赛选手名字 } private Data data; @@ -28,7 +29,7 @@ public class SyncMessageWithStartDto extends BaseMessageDto { } public SyncMessageWithStartDto(Long projectId, MessageUser sender, List receivers, Long roleId, Long beginTaskId, - Long endTaskId, Long time, Long duration){ + Long endTaskId, Long time, Long duration,String player){ this(); setProjectId(projectId); setSender(sender); @@ -40,6 +41,7 @@ public class SyncMessageWithStartDto extends BaseMessageDto { d.setEndTaskId(endTaskId); d.setTime(time); d.setDuration(duration); + d.setPlayer(player); setData(d); } } diff --git a/tall/src/main/java/com/ccsens/tall/config/SpringConfig.java b/tall/src/main/java/com/ccsens/tall/config/SpringConfig.java index 72055bd7..ab3d2532 100644 --- a/tall/src/main/java/com/ccsens/tall/config/SpringConfig.java +++ b/tall/src/main/java/com/ccsens/tall/config/SpringConfig.java @@ -135,6 +135,7 @@ public class SpringConfig implements WebMvcConfigurer { .excludePathPatterns("/users/token") .excludePathPatterns("/users/claims") .excludePathPatterns("/users/member") + .excludePathPatterns("/users/allMemberAll") .addPathPatterns("/plugins/**") .addPathPatterns("/delivers/**") .addPathPatterns("/tasks/**") diff --git a/tall/src/main/java/com/ccsens/tall/persist/dao/ProTaskMemberDao.java b/tall/src/main/java/com/ccsens/tall/persist/dao/ProTaskMemberDao.java new file mode 100644 index 00000000..509b4e89 --- /dev/null +++ b/tall/src/main/java/com/ccsens/tall/persist/dao/ProTaskMemberDao.java @@ -0,0 +1,8 @@ +package com.ccsens.tall.persist.dao; + +import com.ccsens.tall.persist.mapper.ProTaskMemberMapper; +import org.springframework.stereotype.Repository; + +@Repository +public interface ProTaskMemberDao extends ProTaskMemberMapper{ +} diff --git a/tall/src/main/java/com/ccsens/tall/service/IMessageService.java b/tall/src/main/java/com/ccsens/tall/service/IMessageService.java index 78adaba9..9cba185e 100644 --- a/tall/src/main/java/com/ccsens/tall/service/IMessageService.java +++ b/tall/src/main/java/com/ccsens/tall/service/IMessageService.java @@ -8,7 +8,7 @@ import com.ccsens.util.bean.message.common.InMessage; public interface IMessageService { void sendDeliverMessageWithUpload(InMessage inMessage) throws Exception; //同步消息——Start - void sendSyncMessageWithStart(Long currentUserId, Long projectId, Long roleId, Long taskId, Long time, Long duration) throws Exception; + void sendSyncMessageWithStart(Long currentUserId, Long projectId, Long roleId, Long taskId, Long time, Long duration,String player) throws Exception; void sendDeliverMessageWithChecker(InMessage inMessage)throws Exception; diff --git a/tall/src/main/java/com/ccsens/tall/service/IProMemberService.java b/tall/src/main/java/com/ccsens/tall/service/IProMemberService.java index 749b8e77..acfac85f 100644 --- a/tall/src/main/java/com/ccsens/tall/service/IProMemberService.java +++ b/tall/src/main/java/com/ccsens/tall/service/IProMemberService.java @@ -25,4 +25,6 @@ public interface IProMemberService { List selectByRole(Long roleId)throws Exception; MemberVo.MemberInfo getMemberByUserIdAndProjectId(Long userId, Long projectId); + + List getMemberIdByProjectId(Long projectId); } diff --git a/tall/src/main/java/com/ccsens/tall/service/ITaskMemberService.java b/tall/src/main/java/com/ccsens/tall/service/ITaskMemberService.java index c4ede195..ef3c828f 100644 --- a/tall/src/main/java/com/ccsens/tall/service/ITaskMemberService.java +++ b/tall/src/main/java/com/ccsens/tall/service/ITaskMemberService.java @@ -1,7 +1,10 @@ package com.ccsens.tall.service; import com.ccsens.tall.bean.po.ProTaskMember; +import org.springframework.beans.factory.annotation.Autowired; public interface ITaskMemberService { + + void saveTaskMember(ProTaskMember proTaskMember); } diff --git a/tall/src/main/java/com/ccsens/tall/service/MessageService.java b/tall/src/main/java/com/ccsens/tall/service/MessageService.java index 51b04aa2..52233b12 100644 --- a/tall/src/main/java/com/ccsens/tall/service/MessageService.java +++ b/tall/src/main/java/com/ccsens/tall/service/MessageService.java @@ -47,7 +47,7 @@ public class MessageService implements IMessageService{ return messageUsers; } @Override - public void sendSyncMessageWithStart(Long currentUserId, Long projectId, Long roleId, Long taskId,Long time, Long duration) throws Exception { + public void sendSyncMessageWithStart(Long currentUserId, Long projectId, Long roleId, Long taskId,Long time, Long duration,String player) throws Exception { MemberVo.MemberInfo currentMemberVo = memberService.getProMemberByProjectIdAndUserId(projectId,currentUserId); if(ObjectUtil.isNotNull(currentMemberVo)) { //生成userMessage @@ -58,7 +58,7 @@ public class MessageService implements IMessageService{ List memberList = memberService.getAuthedMemberByProjectId(projectId); receivers = constructMessageUsers(memberList); - SyncMessageWithStartDto message = new SyncMessageWithStartDto(projectId, sender, receivers, roleId, taskId, null, time, duration); + SyncMessageWithStartDto message = new SyncMessageWithStartDto(projectId, sender, receivers, roleId, taskId, null, time, duration,player); //FixMe 发送到消息队列 System.out.println(message); rabbitTemplate.convertAndSend(RabbitMQConfig.RabbitMQ_QUEUE_NAME, diff --git a/tall/src/main/java/com/ccsens/tall/service/ProMemberService.java b/tall/src/main/java/com/ccsens/tall/service/ProMemberService.java index 6b79dea7..60e41b1b 100644 --- a/tall/src/main/java/com/ccsens/tall/service/ProMemberService.java +++ b/tall/src/main/java/com/ccsens/tall/service/ProMemberService.java @@ -208,4 +208,21 @@ public class ProMemberService implements IProMemberService { } return memberInfo; } + + @Override + public List getMemberIdByProjectId(Long projectId) { + List memberIdList = null; + ProMemberExample memberExample = new ProMemberExample(); + memberExample.createCriteria().andProjectIdEqualTo(projectId); + List proMemberList = proMemberDao.selectByExample(memberExample); + if(CollectionUtil.isNotEmpty(proMemberList)){ + memberIdList = new ArrayList<>(); + for(ProMember member : proMemberList){ + if(ObjectUtil.isNotNull(member.getUserId())){ + memberIdList.add(member.getUserId()); + } + } + } + return memberIdList; + } } diff --git a/tall/src/main/java/com/ccsens/tall/service/TaskSubTimeService.java b/tall/src/main/java/com/ccsens/tall/service/TaskSubTimeService.java index 5e05b334..32eb4a90 100644 --- a/tall/src/main/java/com/ccsens/tall/service/TaskSubTimeService.java +++ b/tall/src/main/java/com/ccsens/tall/service/TaskSubTimeService.java @@ -39,12 +39,16 @@ public class TaskSubTimeService implements ITaskSubTimeService { @Autowired private ProSubTimeMemberDao proSubTimeMemberDao; @Autowired + private TaskMemberDao taskMemberDao; + @Autowired private ProTaskDeliverPostLogDao proTaskDeliverPostLogDao; @Autowired private TaskDetailDao taskDetailDao; @Autowired private ProRoleDao proRoleDao; @Autowired + private ProMemberDao proMemberDao; + @Autowired private IProMemberService proMemberService; @Autowired private IProTaskDetailService taskDetailService; @@ -185,12 +189,31 @@ public class TaskSubTimeService implements ITaskSubTimeService { taskSubTime.setComplatedStatus(1); taskSubTimeDao.updateByPrimaryKeySelective(taskSubTime); + //查找任务的负责人名 + String player = null; + ProTaskDetail taskDetail = taskDetailDao.selectByPrimaryKey(taskSubTime.getTaskDetailId()); + if(ObjectUtil.isNotNull(taskDetail)){ + player = taskDetail.getDescription(); + } + +// if(ObjectUtil.isNotNull(taskDetail)){ +// if(taskDetail.getAllMember() == 0){ +// ProTaskMemberExample taskMemberExample = new ProTaskMemberExample(); +// taskMemberExample.createCriteria().andTaskDetailIdEqualTo(taskDetail.getId()); +// List taskMemberList = taskMemberDao.selectByExample(taskMemberExample); +// if(CollectionUtil.isNotEmpty(taskMemberList)){ +// ProMember member = proMemberDao.selectByPrimaryKey(taskMemberList.get(0).getMemberId()); +// player = member.getNickname(); +// } +// } +// } + //发送同步消息 SysProject project = sysProjectDao.selectByPrimaryKey(startTaskDto.getProjectId()); //已发布的项目才同步 if (ObjectUtil.isNotNull(project.getPublished()) && project.getPublished() == 1) { messageService.sendSyncMessageWithStart(currentUserId, startTaskDto.getProjectId(), startTaskDto.getRoleId(), taskSubTime.getTaskDetailId(), now, - taskSubTime.getEndTime() - taskSubTime.getBeginTime()); + taskSubTime.getEndTime() - taskSubTime.getBeginTime(),player); } //3.添加记录 proLogService.addNewProLog(now, taskSubTime.getId(), diff --git a/tall/src/main/java/com/ccsens/tall/web/UserController.java b/tall/src/main/java/com/ccsens/tall/web/UserController.java index 526f80ab..e3cc18b9 100644 --- a/tall/src/main/java/com/ccsens/tall/web/UserController.java +++ b/tall/src/main/java/com/ccsens/tall/web/UserController.java @@ -272,6 +272,16 @@ public class UserController { MemberVo.MemberInfo memberInfo = proMemberService.getMemberByUserIdAndProjectId(userId,projectId); return JsonResponse.newInstance().ok(memberInfo); } + + /** + * 获取项目下的所有成员ID + */ + @RequestMapping(value = "allMemberAll", method = RequestMethod.GET, produces = {"application/json;charset=UTF-8"}) + public List getMemberIdByProjectId(Long projectId) throws Exception { + + List memberIdInfo = proMemberService.getMemberIdByProjectId(projectId); + return memberIdInfo; + } } diff --git a/util/src/main/java/com/ccsens/util/CodeEnum.java b/util/src/main/java/com/ccsens/util/CodeEnum.java index d73a5722..3703e77e 100644 --- a/util/src/main/java/com/ccsens/util/CodeEnum.java +++ b/util/src/main/java/com/ccsens/util/CodeEnum.java @@ -77,10 +77,14 @@ public enum CodeEnum { NOT_MEMBER(61,"对不起,找不到对应的成员信息",true), NOT_GAME_RECORD(62,"对不起,找不到对应的游戏场次",true), NOT_JOIN_GAME(63,"您还未加入游戏,请参加游戏后再试",true), - GAME_PENDING(64, "游戏尚未开始,请等待主持人开始", true), - GAME_PREPARATION(65, "游戏已经开始倒计时了", true), - GAME_PROCESSING (66, "游戏已经在火热进行啦", true), - GAME_COMPLETED(67, "抱歉,您来晚了,游戏已结束", true) + + GAME_NO_END(64,"您的上一场游戏尚未结束,请勿重复开启",true), + GAME_NOT_TIMES(65,"游戏可玩次数不足,请充值后重试",true), + + GAME_PENDING(66, "游戏尚未开始,请等待主持人开始", true), + GAME_PREPARATION(67, "游戏已经开始倒计时了", true), + GAME_PROCESSING (68, "游戏已经在火热进行啦", true), + GAME_COMPLETED(69, "抱歉,您来晚了,游戏已结束", true) ; public CodeEnum addMsg(String msg){ diff --git a/util/src/main/java/com/ccsens/util/WebConstant.java b/util/src/main/java/com/ccsens/util/WebConstant.java index b98c27cf..26b74373 100644 --- a/util/src/main/java/com/ccsens/util/WebConstant.java +++ b/util/src/main/java/com/ccsens/util/WebConstant.java @@ -72,7 +72,7 @@ public class WebConstant { public static final String UPLOAD_PROJECT_WBS = UPLOAD_PATH_BASE + File.separator + "project"; public static final String URL_BASE = "https://api.ccsens.com/ptpro/uploads/"; public static final String TEST_URL = "https://test.tall.wiki/"; - public static final String TEST_URL_GAME = TEST_URL + "game/"; + public static final String TEST_URL_GAME = TEST_URL + "game-dev/"; public static final String TEST_URL_BASE = TEST_URL + "gateway/tall/v1.0/uploads/"; public static final Integer Expired_Verify_Code_In_Seconds = 120; @@ -358,7 +358,12 @@ public class WebConstant { ,BatchSetting(0x11,"BatchSetting") ,Admin(0x12,"Admin") ,Ring(0x13,"Ring") - ,Deliver(0x14,"Deliver"); + ,Deliver(0x14,"Deliver") + ,Game(0x15,"Game") + ,Chrome(0x15,"Chrome") + ,PPTCtl(0x15,"PPTCtl") + ,Count(0x16,"Count") + ,ChangeStatus(0x17,"ChangeStatus"); public int value; public String phase; Message_Type(int value,String thePhase){ @@ -380,6 +385,9 @@ public class WebConstant { case 0x12: return Admin; case 0x13: return Ring; case 0x14: return Deliver; + case 0x15: return Game; + case 0x16: return Count; + case 0x17: return ChangeStatus; default: return null; } } @@ -423,7 +431,16 @@ public class WebConstant { } if(phase.equalsIgnoreCase("Deliver")) { return Deliver; - } else { + } + if(phase.equalsIgnoreCase("Game")) { + return Game; + } + if(phase.equalsIgnoreCase("Count")) { + return Count; + } + if(phase.equalsIgnoreCase("ChangeStatus")) { + return Count; + }else { return null; } } @@ -581,7 +598,8 @@ public class WebConstant { } public enum Message_Auth_Event{ - Auth(0,"Auth"); + Auth(0,"Auth"), + Answer(1,"Answer"); public int value; public String phase; Message_Auth_Event(int value,String thePhase){ @@ -591,13 +609,17 @@ public class WebConstant { public static Message_Auth_Event valueOf(int value) { // 手写的从int到enum的转换函数 switch (value) { case 0: return Auth; + case 1: return Answer; default: return null; } } public static Message_Auth_Event phaseOf(String phase) { // 手写的从String到enum的转换函数 if(phase.equalsIgnoreCase("Auth")) { return Auth; - } else { + } + if(phase.equalsIgnoreCase("Answer")) { + return Answer; + }else { return null; } } @@ -797,6 +819,27 @@ public class WebConstant { } } + public enum Message_Url_Event{ + Url(0,"Url"); + public int value; + public String phase; + Message_Url_Event(int value,String thePhase){ + this.value = value; + this.phase = thePhase; + } + public static Message_Url_Event valueOf(int value) { // 手写的从int到enum的转换函数 + switch (value) { + case 0: return Url; + default: return null; + } + } + public static Message_Url_Event phaseOf(String phase) { // 手写的从String到enum的转换函数 + if(phase.equalsIgnoreCase("Url")) { + return Url; + } + return null; + } + } //wbs表时间类型==================================================================/ } diff --git a/util/src/main/java/com/ccsens/util/bean/message/common/MessageConstant.java b/util/src/main/java/com/ccsens/util/bean/message/common/MessageConstant.java index 06be4b6b..74de022a 100644 --- a/util/src/main/java/com/ccsens/util/bean/message/common/MessageConstant.java +++ b/util/src/main/java/com/ccsens/util/bean/message/common/MessageConstant.java @@ -214,4 +214,38 @@ public class MessageConstant { return null; } } + + public enum GameClientMessageType{ + //客户端心跳 + Heart(0x00), + //客户端认证 + Auth(0x01), + //客户端收到消息ACK + Ack(0x02), + //滑动 + Count(0x03), + //状态改变 + ChangeStatus(0x04) + ; + + public int value; + + GameClientMessageType(int value){ + this.value = value; + } + + /** + * 从int到enum的转换函数 + * @param value 枚举int值 + * @return 对应的枚举,找不到则返回null + */ + public static GameClientMessageType valueOf(int value) { + for(GameClientMessageType type : values()){ + if(type.value == value){ + return type; + } + } + return null; + } + } }