first commit

This commit is contained in:
lzh
2025-12-12 11:45:17 +08:00
commit dcd409e5d0
49 changed files with 3642 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
server:
port: 8080
jt808:
port: 20048
logging:
level:
root: INFO
com.hua.transport.jt808: DEBUG

View File

@@ -0,0 +1,187 @@
<!DOCTYPE html>
<html>
<head>
<title>JT808 Server Dashboard</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body { background-color: #f8f9fa; }
.dashboard-card { transition: all 0.3s; }
.dashboard-card:hover { transform: translateY(-5px); shadow: 0 4px 8px rgba(0,0,0,0.1); }
.log-console {
background-color: #1e1e1e;
color: #00ff00;
font-family: 'Courier New', Courier, monospace;
height: 300px;
overflow-y: auto;
padding: 10px;
border-radius: 5px;
font-size: 0.9rem;
}
.log-entry { margin-bottom: 5px; border-bottom: 1px solid #333; padding-bottom: 2px; }
.log-time { color: #888; margin-right: 10px; }
</style>
</head>
<body>
<div id="app">
<nav class="navbar navbar-dark bg-dark">
<div class="container-fluid">
<span class="navbar-brand mb-0 h1">JT808 Transport Server</span>
<span class="text-light">
Status:
<span class="badge" :class="connected ? 'bg-success' : 'bg-danger'">
{{ connected ? 'Connected' : 'Disconnected' }}
</span>
</span>
</div>
</nav>
<div class="container mt-4">
<div class="row">
<!-- Device Simulation Card -->
<div class="col-md-5">
<div class="card dashboard-card mb-4">
<div class="card-header bg-primary text-white">
Device Simulation (Typed API)
</div>
<div class="card-body">
<form @submit.prevent="sendReport">
<div class="mb-3">
<label class="form-label">IMEI</label>
<input v-model="form.imei" class="form-control" placeholder="123456789012">
</div>
<div class="row">
<div class="col-6 mb-3">
<label class="form-label">Lat</label>
<input v-model.number="form.lat" type="number" step="0.000001" class="form-control">
</div>
<div class="col-6 mb-3">
<label class="form-label">Lon</label>
<input v-model.number="form.lon" type="number" step="0.000001" class="form-control">
</div>
</div>
<button type="submit" class="btn btn-primary w-100">Send Location</button>
</form>
</div>
</div>
<!-- Generic Upload Test -->
<div class="card dashboard-card mb-4">
<div class="card-header bg-info text-white">
Universal Upload Test (/upload)
</div>
<div class="card-body">
<div class="mb-3">
<label class="form-label">Arbitrary JSON Payload</label>
<textarea v-model="customJson" class="form-control" rows="3"></textarea>
</div>
<button @click="sendCustomJson" class="btn btn-info text-white w-100">Send to /upload</button>
</div>
</div>
</div>
<!-- Live Log Console -->
<div class="col-md-7">
<div class="card dashboard-card h-100">
<div class="card-header bg-dark text-white d-flex justify-content-between align-items-center">
<span>Live Data Stream (/api/v1/device/upload)</span>
<button @click="logs = []" class="btn btn-sm btn-outline-secondary">Clear</button>
</div>
<div class="card-body bg-dark p-0">
<div class="log-console" ref="console">
<div v-if="logs.length === 0" class="text-muted text-center mt-5">Waiting for data...</div>
<div v-for="(log, index) in logs" :key="index" class="log-entry">
<span class="log-time">[{{ log.time }}]</span>
<span>{{ log.data }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
const { createApp } = Vue
createApp({
data() {
return {
form: {
imei: '13800138000',
lat: 34.215432,
lon: 108.924231,
speed: 60.5
},
customJson: '{\n "sensor": "temp-01",\n "value": 25.5,\n "unit": "C"\n}',
logs: [],
connected: false,
eventSource: null
}
},
mounted() {
this.connectSSE();
},
methods: {
connectSSE() {
this.eventSource = new EventSource('/api/v1/device/logs/stream');
this.eventSource.onopen = () => {
this.connected = true;
this.addLog('System connected. Listening for /upload events...');
};
this.eventSource.onerror = () => {
this.connected = false;
this.eventSource.close();
// Reconnect after 3s
setTimeout(() => this.connectSSE(), 3000);
};
this.eventSource.addEventListener('api-log', (event) => {
const data = JSON.parse(event.data);
this.addLog(data);
});
},
addLog(data) {
const now = new Date().toLocaleTimeString();
this.logs.unshift({
time: now,
data: typeof data === 'string' ? data : JSON.stringify(data)
});
// Keep last 50 logs
if (this.logs.length > 50) this.logs.pop();
},
async sendReport() {
try {
await fetch('/api/v1/device/location', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(this.form)
});
// Note: /location endpoint doesn't broadcast to SSE in this demo, only /upload does
// But we could add it if needed.
} catch (e) {
alert('Error: ' + e.message);
}
},
async sendCustomJson() {
try {
const res = await fetch('/api/v1/device/upload', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: this.customJson
});
const result = await res.json();
if (result.code !== 200) alert('Error: ' + result.message);
} catch (e) {
alert('Error: ' + e.message);
}
}
}
}).mount('#app')
</script>
</body>
</html>