Browse Source

新增版本更新提醒

master
许孟阳 2 weeks ago
parent
commit
75eaf4ab04
  1. 2
      .env.development
  2. 2
      .env.production
  3. 4
      java/application.properties
  4. 6
      java/pom.xml
  5. 6
      java/src/main/java/vip/xumy/idle/server/IdleServerApplocation.java
  6. 11
      java/src/main/java/vip/xumy/idle/server/conf/IdleServerInitializer.java
  7. 35
      java/src/main/java/vip/xumy/idle/server/ctrl/PublicController.java
  8. 25
      java/src/main/java/vip/xumy/idle/server/pojo/SocketMsg.java
  9. 96
      java/src/main/java/vip/xumy/idle/server/service/WebSocketService.java
  10. 4
      java/src/main/resources/logback.xml
  11. 2
      src/App.vue
  12. 2
      src/components/dialog.vue
  13. 2
      src/config/i18n/zh.ts
  14. 37
      src/tool/websocket.ts
  15. 2
      src/views/index.vue
  16. 79
      src/views/version.vue

2
.env.development

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
NODE_ENV="preview"
VUE_APP_BASE_URL="http://localhost"

2
.env.production

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
NODE_ENV="production"
VUE_APP_BASE_URL=""

4
java/application.properties

@ -1,7 +1,3 @@ @@ -1,7 +1,3 @@
# WEB服务端口配置
server.port=80
# WEB服务根路径配置
server.servlet.context-path=/
# 程序自身数据源配置
spring.datasource.url=jdbc:mysql://10.100.0.108:3306/idle_game?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
spring.datasource.username=root

6
java/pom.xml

@ -95,6 +95,12 @@ @@ -95,6 +95,12 @@
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${mybatis.pagehelper.version}</version>
</dependency>
<!-- WebSocket Server-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
</dependencies>

6
java/src/main/java/vip/xumy/idle/server/SimpleApplocation.java → java/src/main/java/vip/xumy/idle/server/IdleServerApplocation.java

@ -2,12 +2,12 @@ package vip.xumy.idle.server; @@ -2,12 +2,12 @@ package vip.xumy.idle.server;
import org.springframework.boot.SpringApplication;
import vip.xumy.idle.server.conf.SimpleInitializer;
import vip.xumy.idle.server.conf.IdleServerInitializer;
public class SimpleApplocation {
public class IdleServerApplocation {
public static void main(String[] args) {
Class<?>[] arr = new Class<?>[] { SimpleInitializer.class };
Class<?>[] arr = new Class<?>[] { IdleServerInitializer.class };
SpringApplication.run(arr, args);
}

11
java/src/main/java/vip/xumy/idle/server/conf/SimpleInitializer.java → java/src/main/java/vip/xumy/idle/server/conf/IdleServerInitializer.java

@ -4,15 +4,22 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -4,15 +4,22 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class)
@ComponentScan(value = "vip.xumy.idle.server, vip.xumy.core")
public class SimpleInitializer extends SpringBootServletInitializer {
public class IdleServerInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(SimpleInitializer.class);
return builder.sources(IdleServerInitializer.class);
}
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}

35
java/src/main/java/vip/xumy/idle/server/ctrl/PublicController.java

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
package vip.xumy.idle.server.ctrl;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import vip.xumy.core.pojo.com.BaseResponse;
import vip.xumy.idle.server.pojo.SocketMsg;
import vip.xumy.idle.server.service.WebSocketService;
/**
* Ownership belongs to the company
*
* @author:mengyxu
* @date:2025年4月21日
*/
@RestController
@RequestMapping("public")
public class PublicController {
@GetMapping("socket/num")
public BaseResponse getSocketNum() {
return new BaseResponse(WebSocketService.CLIENT_MAP.keySet().size());
}
@PostMapping("update/notify")
public BaseResponse updateNotify(@RequestBody String notify) {
WebSocketService.sendMessage(new SocketMsg<String>("update", notify));
return new BaseResponse();
}
}

25
java/src/main/java/vip/xumy/idle/server/pojo/SocketMsg.java

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
package vip.xumy.idle.server.pojo;
import lombok.Getter;
/** Ownership belongs to the company
*
* @author:mengyxu
* @date:2025年4月9日
*/
@Getter
public class SocketMsg<T> {
private String type;
private T data;
public SocketMsg(String type){
this.type = type;
}
public SocketMsg(String type, T data){
this.type = type;
this.data = data;
}
}

96
java/src/main/java/vip/xumy/idle/server/service/WebSocketService.java

@ -0,0 +1,96 @@ @@ -0,0 +1,96 @@
package vip.xumy.idle.server.service;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import lombok.Getter;
import lombok.extern.log4j.Log4j2;
import vip.xumy.core.exception.CoreException;
import vip.xumy.idle.server.pojo.SocketMsg;
/**
* Ownershid belongs to the company
*
* @author:mengyxu
* @date:2024年1月26日
*/
@Log4j2
@Component
@EnableScheduling
@ServerEndpoint(value = "/websocket/{id}")
public class WebSocketService {
public static final Map<String, WebSocketService> CLIENT_MAP = new HashMap<>();
private Session session;
@Getter
private String id;
@OnOpen
public void onOpen(Session session, @PathParam("id") String id) {
this.session = session;
this.id = id;
CLIENT_MAP.put(id, this);
log.debug(id + "连接websocket");
}
@OnClose
public void onClose() {
CLIENT_MAP.remove(id);
}
@OnMessage
public void onMessage(String message, Session session) {
}
@OnError
public void onError(Session session, Throwable error) {
log.error(id + "连接错误错误", error);
}
public static Collection<WebSocketService> clients() {
return CLIENT_MAP.values();
}
public void send(String message) {
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
throw new CoreException("下发消息失败", e);
}
}
public static void sendMessage(List<String> ids, SocketMsg<?> message) {
String json = JSON.toJSONString(message);
for (String id : ids) {
WebSocketService client = CLIENT_MAP.get(id);
if (client == null) {
continue;
}
client.send(json);
}
}
public static void sendMessage(SocketMsg<?> message) {
String json = JSON.toJSONString(message);
for (WebSocketService client : CLIENT_MAP.values()) {
client.send(json);
}
}
}

4
java/src/main/resources/logback.xml

@ -57,12 +57,12 @@ @@ -57,12 +57,12 @@
</filter>
</appender>
<logger name="com.sprt" level="DEBUG" additivity="false">
<logger name="vip.xumy" level="DEBUG" additivity="false">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
<appender-ref ref="ERROR" />
</logger>
<logger name="com.sprt.core" level="INFO" additivity="false">
<logger name="vip.xumy.core" level="INFO" additivity="false">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
<appender-ref ref="ERROR" />

2
src/App.vue

@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
color: #FFF;
width: 100%;
height: 100%;
overflow: hidden;

2
src/components/dialog.vue

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
<template>
<teleport to="#app">
<div class="dialog" :draggable="true" v-show="show" @contextmenu.prevent="console.log('禁用浏览器默认右键功能')">
<div class="dialog" v-show="show" @contextmenu.prevent="console.log('禁用浏览器默认右键功能')">
<div class="title" v-if="showHeader">
<span>{{ title }}</span>
<i class="close" @click="show = false; emit('close')"></i>

2
src/config/i18n/zh.ts

@ -174,4 +174,6 @@ export default class Zh { @@ -174,4 +174,6 @@ export default class Zh {
music = ['播放或禁音背景音乐(M)'];
illustrated = ['装备图鉴(I)'];
update = ['版本更新通知', '检测到有服务器有新版本,是否刷新刷新页面更新版本?', '刷新页面', '暂时不管'];
}

37
src/tool/websocket.ts

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
let websocket: WebSocket;
const msgHandlersMap = {};
let lastUrl;
export const openWebSocket = (url) => {
if (websocket && websocket.readyState === WebSocket.OPEN) {
if (lastUrl == url) {
return;
}
websocket.close();
}
websocket = new WebSocket(url);
lastUrl = url;
websocket.onopen = () => {
console.log('websocket已连接');
};
websocket.onmessage = (msg) => {
const event = JSON.parse(msg.data);
const handlers = msgHandlersMap[event.type];
if (!handlers) {
return;
}
Object.values(handlers).forEach((handler: any) => {
handler(event.data);
});
};
websocket.onclose = () => {
console.log('websocket已断开');
openWebSocket(url);
};
};
export const registerHandler = (type, key, handler) => {
const handlers = msgHandlersMap[type] || {};
handlers[key] = handler;
msgHandlersMap[type] = handlers;
console.log(msgHandlersMap);
};

2
src/views/index.vue

@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
<Menu />
</div>
<EquipTips />
<Version />
</template>
<script lang="ts" setup>
@ -15,6 +16,7 @@ import { useI18n } from "vue3-i18n"; @@ -15,6 +16,7 @@ import { useI18n } from "vue3-i18n";
import Message from "./message";
import Menu from "./menu.vue";
import { EquipTips } from "@/components";
import Version from "./version.vue";
const { t } = useI18n();
const { state, commit, dispatch } = useStore();

79
src/views/version.vue

@ -0,0 +1,79 @@ @@ -0,0 +1,79 @@
<template>
<Dialog :title="t('update.0')" v-model="showUpdate" top="5rem" left="5rem">
<div class="nitofy">
<div class="confirm">{{ t('update.1') }}</div>
<div class="msg" v-if="message">{{ message }}</div>
<div class="btns">
<button class="button" @click="refresh">{{ t('update.2') }}</button>
<button class="button" @click="showUpdate = false">{{ t('update.3') }}</button>
</div>
</div>
</Dialog>
</template>
<script lang="ts" setup>
import { useStore } from "vuex";
import { reactive, onMounted, ref } from "vue";
import { useI18n } from "vue3-i18n";
import { openWebSocket, registerHandler } from "@/tool/websocket";
import Dialog from "@/components/dialog.vue";
const { t } = useI18n();
const { state, commit, dispatch } = useStore();
const showUpdate = ref(false);
const message = ref('');
const uuid = () => {
var s: any = [];
var hexDigits = "0123456789abcdef";
for (var i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = "4"; // UUID
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); //
s[8] = s[13] = s[18] = s[23] = "-";
var uuid = s.join("");
return uuid;
}
let url = 'ws://' + window.location.host + ':' + window.location.port;
if (process.env.VUE_APP_BASE_URL) {
url = 'ws://' + process.env.VUE_APP_BASE_URL.substring(7);
}
url += '/websocket/' + uuid();
openWebSocket(url);
const refresh = () => {
dispatch('saveGame');
window.location.reload();
}
const onUpdate = (msg) => {
message.value = msg;
showUpdate.value = true;
}
registerHandler('update', 'version', onUpdate)
onMounted(() => { });
</script>
<style lang="scss" scoped>
.nitofy {
width: 25rem;
padding: 0.5rem;
}
.confirm {
width: 100%;
font-size: 1.2rem;
text-align: start;
}
.msg {
color: #d1cfcf;
width: 100%;
white-space: normal;
word-break: break-all;
overflow: hidden;
padding: 1rem;
}
</style>
Loading…
Cancel
Save