清理代码;新增远程共育室公开接口

This commit is contained in:
BBIT-Kai
2026-05-25 14:52:45 +08:00
parent 1c107473ae
commit f32df8cde0
8 changed files with 99 additions and 1426 deletions
@@ -5,6 +5,7 @@ import ink.snowflake.server.controller.User
import ink.snowflake.server.controller.chat import ink.snowflake.server.controller.chat
import ink.snowflake.server.utils.plugins.configureSockets import ink.snowflake.server.utils.plugins.configureSockets
import ink.snowflake.server.controller.ImageAnalytics import ink.snowflake.server.controller.ImageAnalytics
import ink.snowflake.server.controller.Public
import ink.snowflake.server.controller.RemoteDebug import ink.snowflake.server.controller.RemoteDebug
import ink.snowflake.server.controller.Traceability import ink.snowflake.server.controller.Traceability
import ink.snowflake.server.controller.VideoAnalytics import ink.snowflake.server.controller.VideoAnalytics
@@ -18,6 +19,7 @@ import ink.snowflake.server.utils.plugins.configureSerialization
import ink.snowflake.server.utils.plugins.configureStaticPath import ink.snowflake.server.utils.plugins.configureStaticPath
import ink.snowflake.server.utils.plugins.configureStatusPages import ink.snowflake.server.utils.plugins.configureStatusPages
import ink.snowflake.server.utils.plugins.configureTemplating import ink.snowflake.server.utils.plugins.configureTemplating
import io.ktor.http.CacheControl
import io.ktor.server.application.* import io.ktor.server.application.*
import io.ktor.server.tomcat.jakarta.* import io.ktor.server.tomcat.jakarta.*
@@ -76,4 +78,6 @@ fun Application.module() {
// 业务-图片分析 // 业务-图片分析
ImageAnalytics() ImageAnalytics()
Traceability(appConfig) Traceability(appConfig)
// 业务-公开接口
Public()
} }
@@ -0,0 +1,37 @@
package ink.snowflake.server.controller
import ink.snowflake.server.SERVER_PATH_FRP
import ink.snowflake.server.model.response.BaseResponse
import io.ktor.server.application.Application
import io.ktor.server.response.respond
import io.ktor.server.routing.get
import io.ktor.server.routing.route
import io.ktor.server.routing.routing
fun Application.Public() {
routing {
route("/silk-remote") {
get("/connectLocalDevice") {
val port = call.parameters["port"]
if (port != null) {
runAdbCommand("disconnect")
runAdbCommand("connect ${SERVER_PATH_FRP}:$port")
val url =
"https://ai.ronsunny.cn:8090/remote#!action=stream&udid=s3.ronsunny.cn%3ATTT&player=mse&ws=wss%3A%2F%2Fai.ronsunny.cn%3A8090%2Fremote%3Faction%3Dproxy-adb%26remote%3Dtcp%253A8886%26udid%3Ds3.ronsunny.cn%253ATTT"
.replace(
"TTT",
port
)
call.respond(BaseResponse(data = url))
} else {
call.respond(BaseResponse(status = false, message = "IP或端口无效", data = null))
}
}
get("/disConnectAll") {
val result = runAdbCommand("disconnect")
call.respond(BaseResponse(data = result))
}
}
}
}
@@ -78,7 +78,8 @@ fun Application.RemoteDebug() {
val name = call.parameters["name"] val name = call.parameters["name"]
val response: HttpResponse = client.get("http://${SERVER_PATH_FRP}:65534/api/proxy/tcp") val response: HttpResponse = client.get("http://${SERVER_PATH_FRP}:65534/api/proxy/tcp")
val responseBody: String = response.bodyAsText() val responseBody: String = response.bodyAsText()
val devicesInfoRequest: DevicesInfoRequest = Gson().fromJson(responseBody, DevicesInfoRequest::class.java) val devicesInfoRequest: DevicesInfoRequest =
Gson().fromJson(responseBody, DevicesInfoRequest::class.java)
val onlineDevices = devicesInfoRequest.proxies.stream() val onlineDevices = devicesInfoRequest.proxies.stream()
.filter { it.status == "online" && it.conf != null && it.conf.remotePort >= 10000 && it.conf.remotePort <= 20000 } .filter { it.status == "online" && it.conf != null && it.conf.remotePort >= 10000 && it.conf.remotePort <= 20000 }
val devices: MutableList<DeviceItemResponse> = mutableListOf() val devices: MutableList<DeviceItemResponse> = mutableListOf()
+1 -1
View File
@@ -101,7 +101,7 @@
| https | **8093** | 8883 | 8883 | ce_emqx | EMQX | MQTT TCP TLS 端口 | | https | **8093** | 8883 | 8883 | ce_emqx | EMQX | MQTT TCP TLS 端口 |
| | | 8083 | 8083 | ce_emqx | EMQX | MQTT WS 端口 | | | | 8083 | 8083 | ce_emqx | EMQX | MQTT WS 端口 |
| | | 8084 | 8084 | ce_emqx | EMQX | MQTT WS TLS 端口 | | | | 8084 | 8084 | ce_emqx | EMQX | MQTT WS TLS 端口 |
| | | | | | | | | | | 3000 | | ce_gitea | Gitea | 管理页面:bbit:12345678 |
| | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | |
-20
View File
@@ -1,20 +0,0 @@
# 灵活溯源系统 MVP
这是一个纯前端的 H5 演示版,直接打开 `index.html` 就能看效果。
## 已包含内容
- 管理员端:节点库、字段编辑、模板创建、公共资料块复用
- 业务员端:基于模板新建批次、逐节点填报、二维码预览
- 消费者端:时间轴溯源展示、企业信息、县域情况、有机证书
## 使用方式
1. 直接双击打开 `index.html`
2. 或者用任意静态文件服务打开 `trace-demo` 目录
## 说明
- 演示数据保存在浏览器 `localStorage`
- 点击“重置演示数据”可以恢复默认内容
- 当前二维码是本地样式模拟,后续可替换成真实二维码
-1016
View File
File diff suppressed because it is too large Load Diff
-16
View File
@@ -1,16 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>灵活溯源系统 MVP</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<div id="app"></div>
<script src="./app.js"></script>
</body>
</html>
-317
View File
@@ -1,317 +0,0 @@
:root {
--bg: #f4f6f9;
--panel: #ffffff;
--panel-soft: #f9fafc;
--line: #e6ebf2;
--text: #172033;
--text-soft: #5f6b85;
--brand: #1958d6;
--brand-soft: #edf3ff;
--success: #0f8c62;
--danger: #c84242;
--radius-lg: 20px;
--radius-md: 14px;
--radius-sm: 10px;
--shadow: 0 12px 40px rgba(18, 30, 67, 0.08);
}
* { box-sizing: border-box; }
html, body { margin: 0; min-height: 100%; font-family: "Noto Sans SC", sans-serif; background: var(--bg); color: var(--text); }
button, input, select, textarea { font: inherit; }
button { cursor: pointer; }
a { color: var(--brand); text-decoration: none; }
.system-shell { min-height: 100vh; display: grid; grid-template-columns: 220px minmax(0, 1fr); }
.main-nav {
display: flex;
flex-direction: column;
gap: 10px;
padding: 24px 18px;
background: linear-gradient(180deg, #14213f 0%, #1e305d 100%);
color: #fff;
}
.brand { font-size: 22px; font-weight: 800; margin-bottom: 14px; }
.nav-btn {
border: 1px solid transparent;
background: rgba(255, 255, 255, 0.08);
color: #eef3ff;
border-radius: 14px;
padding: 12px 14px;
text-align: left;
}
.nav-btn.active { background: #fff; color: var(--brand); }
.nav-btn.danger { margin-top: auto; color: #ffd2d2; border-color: rgba(255,255,255,0.1); }
.main-panel { padding: 22px; }
.content-shell { display: grid; gap: 18px; }
.content-shell.two-col, .content-shell.operator-layout { grid-template-columns: 320px minmax(0, 1fr); }
.content-shell.single-col { grid-template-columns: 1fr; }
.left-pane, .right-pane, .editor-card, .stats-card {
background: var(--panel);
border: 1px solid var(--line);
border-radius: var(--radius-lg);
box-shadow: var(--shadow);
}
.left-pane, .right-pane { padding: 18px; }
.right-pane { display: grid; gap: 18px; align-content: start; }
.editor-card { padding: 18px; }
.pane-head {
display: flex;
justify-content: space-between;
align-items: center;
gap: 16px;
margin-bottom: 16px;
}
.pane-head h2, .timeline-head h3 { margin: 0; font-size: 20px; }
.pane-actions, .editor-actions, .template-toolbar, .query-bar, .inline-select, .sub-tabs { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.muted-line { margin: 6px 0 0; color: var(--text-soft); font-size: 13px; }
.scroll-list { display: grid; gap: 12px; max-height: calc(100vh - 150px); overflow: auto; padding-right: 4px; }
.list-card {
width: 100%;
border: 1px solid var(--line);
background: var(--panel-soft);
border-radius: var(--radius-md);
padding: 14px;
display: grid;
gap: 6px;
text-align: left;
color: var(--text);
}
.list-card.active { border-color: var(--brand); background: var(--brand-soft); }
.list-card span { color: var(--text-soft); font-size: 13px; }
.primary-btn, .ghost-btn, .sub-tab {
border-radius: 12px;
padding: 10px 14px;
border: 1px solid transparent;
}
.primary-btn { background: var(--brand); color: #fff; }
.ghost-btn, .sub-tab { background: var(--panel-soft); color: var(--text); border-color: var(--line); }
.danger-btn { color: var(--danger); }
.sub-tab.active { background: var(--brand-soft); color: var(--brand); border-color: #cadeff; }
.form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.form-grid.compact { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.field { display: flex; flex-direction: column; gap: 8px; }
.field.full { grid-column: 1 / -1; }
.field label { font-size: 13px; font-weight: 700; color: var(--text-soft); }
.field input, .field select, .field textarea {
width: 100%;
border: 1px solid #d6deea;
border-radius: 12px;
background: #fff;
color: var(--text);
padding: 11px 12px;
min-height: 44px;
}
.field textarea { min-height: 110px; resize: vertical; }
.field input:disabled, .field select:disabled, .field textarea:disabled { background: #f3f5f8; color: #79859e; }
.switch { position: relative; width: 54px; height: 32px; }
.switch input { opacity: 0; width: 0; height: 0; }
.switch span {
position: absolute;
inset: 0;
background: #cdd6e4;
border-radius: 999px;
transition: 0.2s ease;
}
.switch span::before {
content: "";
position: absolute;
width: 22px;
height: 22px;
left: 5px;
top: 5px;
border-radius: 50%;
background: #fff;
transition: 0.2s ease;
box-shadow: 0 4px 12px rgba(23, 32, 51, 0.18);
}
.switch input:checked + span { background: var(--brand); }
.switch input:checked + span::before { transform: translateX(22px); }
.template-toolbar { margin: 16px 0; }
.inline-select select { min-width: 180px; }
.node-strip {
display: flex;
gap: 10px;
flex-wrap: wrap;
padding: 6px 0 2px;
}
.node-pill {
display: grid;
gap: 4px;
min-width: 160px;
padding: 14px;
border: 1px solid var(--line);
background: var(--panel-soft);
border-radius: 16px;
text-align: left;
}
.node-pill.active { border-color: var(--brand); background: var(--brand-soft); }
.pill-order { color: var(--brand); font-size: 12px; font-weight: 800; }
.pill-name { font-weight: 700; }
.pill-tag { color: var(--text-soft); font-size: 12px; }
.readonly-tip {
margin-bottom: 16px;
padding: 12px 14px;
border-radius: 12px;
background: #fff6e9;
color: #8d5a16;
border: 1px solid #f2ddbb;
}
.field-list { display: grid; gap: 12px; margin-top: 16px; }
.field-card {
border: 1px solid var(--line);
border-radius: 16px;
background: var(--panel-soft);
padding: 14px;
}
.progress-strip {
display: flex;
gap: 12px;
overflow: auto;
padding-bottom: 4px;
}
.progress-step {
min-width: 132px;
border: 1px solid var(--line);
background: var(--panel-soft);
border-radius: 16px;
padding: 12px;
text-align: left;
display: grid;
gap: 6px;
}
.progress-step.active { border-color: var(--brand); background: var(--brand-soft); }
.progress-step.done .step-index { background: var(--success); }
.step-index {
width: 26px;
height: 26px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
background: var(--brand);
color: #fff;
font-size: 12px;
font-weight: 800;
}
.step-name { font-size: 13px; font-weight: 700; }
.chips { display: flex; flex-wrap: wrap; gap: 10px; }
.chip-check {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 10px;
border-radius: 999px;
background: var(--panel-soft);
border: 1px solid var(--line);
}
.consumer-tabs { display: flex; gap: 10px; margin-bottom: 16px; }
.consumer-shell {
background: linear-gradient(180deg, #fefefe 0%, #f5f7fb 100%);
border: 1px solid var(--line);
border-radius: 22px;
padding: 22px;
}
.consumer-topbar { margin-bottom: 18px; }
.consumer-topbar h2 { margin: 0; }
.consumer-topbar p { margin: 8px 0 0; color: var(--text-soft); }
.timeline-v2 { display: grid; gap: 0; }
.timeline-row { display: grid; grid-template-columns: 48px minmax(0, 1fr); gap: 14px; }
.timeline-rail {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
}
.timeline-dot {
width: 16px;
height: 16px;
border-radius: 50%;
background: #c7d2e7;
border: 4px solid #eef3ff;
z-index: 1;
margin-top: 18px;
}
.timeline-dot.active { background: var(--brand); }
.timeline-line {
width: 2px;
flex: 1;
background: linear-gradient(180deg, #bdd0ff 0%, #e0e8f6 100%);
margin-top: 8px;
}
.timeline-body {
margin-bottom: 18px;
padding: 18px;
border-radius: 18px;
border: 1px solid var(--line);
background: #fff;
}
.timeline-head span { display: block; margin-top: 8px; color: var(--text-soft); font-size: 13px; }
.timeline-grid, .materials-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
margin-top: 16px;
}
.kv-card {
padding: 14px;
border-radius: 14px;
background: var(--panel-soft);
border: 1px solid var(--line);
display: grid;
gap: 6px;
}
.kv-card span { color: var(--text-soft); font-size: 12px; }
.kv-card strong { font-size: 14px; word-break: break-word; }
.material-card {
border: 1px solid var(--line);
background: #fff;
border-radius: 18px;
padding: 18px;
}
.stats-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; }
.stats-card {
padding: 26px;
display: grid;
gap: 14px;
}
.stats-card span { color: var(--text-soft); }
.stats-card strong { font-size: 40px; line-height: 1; }
.empty-panel {
min-height: 180px;
border: 1px dashed #cfd7e4;
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-soft);
background: var(--panel-soft);
}
@media (max-width: 1080px) {
.system-shell, .content-shell.two-col, .content-shell.operator-layout { grid-template-columns: 1fr; }
.main-nav { flex-direction: row; flex-wrap: wrap; align-items: center; }
.nav-btn.danger { margin-top: 0; margin-left: auto; }
}
@media (max-width: 760px) {
.main-panel { padding: 14px; }
.form-grid, .form-grid.compact, .timeline-grid, .materials-grid, .stats-grid { grid-template-columns: 1fr; }
.timeline-row { grid-template-columns: 28px minmax(0, 1fr); gap: 10px; }
.node-pill, .progress-step { min-width: 120px; }
}