代码初始化

This commit is contained in:
2026-05-07 15:54:43 +08:00
parent a078ca46e5
commit 3522f1011f
274 changed files with 15704 additions and 0 deletions
@@ -0,0 +1,30 @@
# 开发环境:本机 Mongo / Redis / 跨服,日志落项目相对目录
server:
port: 8080
spring:
data:
mongodb:
uri: mongodb://huangzj:huangzj@43.155.238.153:27017/test?authSource=admin
redis:
host: 43.155.238.153
port: 6379
password: huangzj
username: default
game:
client-tcp:
port: 9091
cross:
host: 127.0.0.1
port: 9200
server-id: zone-1
token: ""
heartbeat-interval-ms: 15000
app:
logging:
dir: logs/ServerModule
mongo:
collection: test
@@ -0,0 +1,28 @@
# 部署环境(生产/预发):请通过环境变量注入连接信息与密钥,勿把真实密码写进仓库
server:
port: ${SERVER_HTTP_PORT:8080}
spring:
data:
mongodb:
uri: ${MONGODB_URI:mongodb://127.0.0.1:27017/demo_comm}
redis:
host: ${REDIS_HOST:127.0.0.1}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:}
username: ${REDIS_USERNAME:}
game:
client-tcp:
port: ${GAME_CLIENT_TCP_PORT:9091}
cross:
host: ${CROSS_HOST:127.0.0.1}
port: ${CROSS_TCP_PORT:9200}
server-id: ${ZONE_SERVER_ID:zone-1}
token: ${CROSS_LINK_TOKEN:}
heartbeat-interval-ms: ${CROSS_HEARTBEAT_INTERVAL_MS:15000}
app:
logging:
dir: ${APP_LOG_DIR:/var/log/comm-framework/server}
@@ -0,0 +1,17 @@
# 公共入口:按 spring.profiles.active 叠加 application-{profile}.yml
# 开发默认:未设置环境变量 SPRING_PROFILES_ACTIVE 时使用 dev
spring:
application:
name: server-module
profiles:
active: ${SPRING_PROFILES_ACTIVE:dev}
# 与运行环境无关的业务默认(可被各 profile 覆盖)
app:
mongo:
collection: demo_payload
save:
interval-ms: 15000
module:
player-id: 900001
tick-interval-ms: 8000
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 目录由本模块 application.yml 的 app.logging.dir 指定,与 CrossModule 互不干扰 -->
<springProperty name="LOG_DIR" scope="context" source="app.logging.dir" defaultValue="logs/ServerModule"/>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<!-- INFO、WARN:不含 ERROR,便于日常浏览 -->
<appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_DIR}/info.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>DENY</onMatch>
<onMismatch>NEUTRAL</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/info.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>14</maxHistory>
<totalSizeCap>512MB</totalSizeCap>
</rollingPolicy>
</appender>
<!-- ERROR 单独落盘,告警/排障只盯这个文件即可 -->
<appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_DIR}/error.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>512MB</totalSizeCap>
</rollingPolicy>
</appender>
<!-- DEBUG 仅进控制台(若把 root 调到 DEBUG);需要落盘时在 application.yml 里单独配 logger + appender -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE_INFO"/>
<appender-ref ref="FILE_ERROR"/>
</root>
</configuration>
@@ -0,0 +1,174 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>通信探测</title>
<style>
body { font-family: sans-serif; margin: 1rem; }
button { margin-right: 0.5rem; margin-bottom: 0.5rem; }
pre { background: #f4f4f4; padding: 0.75rem; white-space: pre-wrap; }
</style>
</head>
<body>
<p>业务 TCP4 字节大端长度 + <code>CmdRpcEnvelope</code>)端口:<code id="tcpPort"></code>
三条路径示例请运行 <code>com.huangzj.server.demo.TcpPathsDemo</code>(需先起 CrossModule 再起 ServerModule)。</p>
<button type="button" id="btnHttp">验证 HTTP</button>
<button type="button" id="btnMongoNow">Mongo 立即保存</button>
<button type="button" id="btnMongoLater">Mongo 定时保存</button>
<button type="button" id="btnMongoRead">Mongo 读最新</button>
<button type="button" id="btnRedisSet">Redis 写入</button>
<button type="button" id="btnRedisGet">Redis 读取</button>
<button type="button" id="btnEventPub">事件发布</button>
<button type="button" id="btnEventRead">事件状态</button>
<button type="button" id="btnModInfo">模块注册与定时配置</button>
<button type="button" id="btnModTick">手动 tick 全部 GeneralModule</button>
<button type="button" id="btnModSave">全部模块 saveData</button>
<pre id="log"></pre>
<script>
const logEl = document.getElementById('log');
let playerId = 900001;
function log(msg) {
logEl.textContent += msg + '\n';
}
async function refreshEndpoints() {
try {
const tcp = await fetch('/api/config/client-tcp');
const tj = await tcp.json();
document.getElementById('tcpPort').textContent = String(tj.port);
const mi = await fetch('/api/demo/module/info');
const mj = await mi.json();
playerId = mj.playerId;
} catch (e) {
log('加载配置失败 ' + e);
}
}
document.getElementById('btnHttp').onclick = async () => {
try {
const r = await fetch('/api/ping');
const j = await r.json();
log('[HTTP] ' + JSON.stringify(j));
} catch (e) {
log('[HTTP] 失败: ' + e);
}
};
document.getElementById('btnMongoNow').onclick = async () => {
try {
await refreshEndpoints();
const body = { playerId: playerId, content: 'immediate-' + Date.now() };
const r = await fetch('/api/demo/mongo/save-immediate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
log('[Mongo立即] ' + await r.text());
} catch (e) {
log('[Mongo立即] ' + e);
}
};
document.getElementById('btnMongoLater').onclick = async () => {
try {
await refreshEndpoints();
const body = { playerId: playerId, content: 'scheduled-' + Date.now() };
const r = await fetch('/api/demo/mongo/save-scheduled', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
log('[Mongo定时标记] ' + r.status);
} catch (e) {
log('[Mongo定时] ' + e);
}
};
document.getElementById('btnMongoRead').onclick = async () => {
try {
await refreshEndpoints();
const r = await fetch('/api/demo/mongo/latest?playerId=' + encodeURIComponent(playerId));
log('[Mongo读] ' + await r.text());
} catch (e) {
log('[Mongo读] ' + e);
}
};
document.getElementById('btnRedisSet').onclick = async () => {
try {
const r = await fetch('/api/demo/redis/set', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ key: 'demoKey', value: 'demoVal-' + Date.now() })
});
log('[Redis写] ' + r.status);
} catch (e) {
log('[Redis写] ' + e);
}
};
document.getElementById('btnRedisGet').onclick = async () => {
try {
const r = await fetch('/api/demo/redis/get?key=demoKey');
log('[Redis读] ' + await r.text());
} catch (e) {
log('[Redis读] ' + e);
}
};
document.getElementById('btnEventPub').onclick = async () => {
try {
const r = await fetch('/api/demo/event/publish', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ topic: 'demo', body: 'hello-' + Date.now() })
});
log('[事件发布] ' + r.status);
} catch (e) {
log('[事件发布] ' + e);
}
};
document.getElementById('btnEventRead').onclick = async () => {
try {
const r = await fetch('/api/demo/event/last');
log('[事件状态] ' + await r.text());
} catch (e) {
log('[事件状态] ' + e);
}
};
document.getElementById('btnModInfo').onclick = async () => {
try {
const r = await fetch('/api/demo/module/info');
log('[模块] ' + await r.text());
} catch (e) {
log('[模块] ' + e);
}
};
document.getElementById('btnModTick').onclick = async () => {
try {
const r = await fetch('/api/demo/module/tick', { method: 'POST' });
log('[模块tick] ' + await r.text());
} catch (e) {
log('[模块tick] ' + e);
}
};
document.getElementById('btnModSave').onclick = async () => {
try {
const r = await fetch('/api/demo/module/save', { method: 'POST' });
log('[模块save] ' + r.status);
} catch (e) {
log('[模块save] ' + e);
}
};
refreshEndpoints();
</script>
</body>
</html>