记录第一次SpringBoot开发的失败经历
一个小功能的失败的开发经历
一、开发前的想法
刚开始的时候,乌蒙地插传分的时候每次都要一步步输入Bot的指令,我就想了以下,刚好最近几天入门了Java,想着搞个神秘的玩意儿,来自己构造一个简单的controller来自动生成Bot指令。
以下是我的开发失败经过
二、前端和前端渲染问题
因为有过一些 Python 的 Flask 框架的基础,所以我知道一个东西叫模板渲染,这个可以保证后端代码的简洁,所以找ai生成了如下代码:
index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
| <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" lang="zh-CN">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> <title>音游传分工具 | 支持歌曲名/版本</title> <link rel="stylesheet" th:href="@{/css/style.css}"> </head>
<body> <div class="card"> <div class="header"> <h1> 🎵 舞萌·传分器 <span>扩展版</span> </h1> <p>选择「传分」填写成绩 → 提交后端 → 获取指令</p> </div>
<div class="mode-section"> <div class="mode-label"> <span>📌 模式选择:</span> <select id="modeSelect"> <option value="transmit">✨ 传分</option> <option value="buried">🕳️ 下埋</option> </select> <span class="badge-hint" style="background:#eef2ff;">传分包含9项参数</span> </div> </div>
<div id="scoreFormContainer" class="form-container"> <div class="form-grid"> <div class="input-group"> <label>🎼 乐曲ID <span class="badge-hint">可选,空则传None</span></label> <input type="text" id="musicId" placeholder="例: 11624 (可不填)" autocomplete="off"> </div>
<div class="input-group"> <label>📝 歌曲名字 <span class="badge-hint">可选,空则传None</span></label> <input type="text" id="songName" placeholder="例: 夜に駆ける (可不填)" autocomplete="off"> </div>
<div class="input-group"> <label>🎚️ 歌曲版本</label> <select id="songVersion"> <option value="标准谱">标准谱</option> <option value="DX谱">DX谱</option> </select> </div>
<div class="input-group"> <label>📊 等级 <span class="badge-hint">0绿 1黄 2红 3紫 4白</span></label> <select id="levelSelect"> <option value="0">0 - 🟢 绿谱 (BASIC)</option> <option value="1">1 - 🟡 黄谱 (ADVANCED)</option> <option value="2">2 - 🔴 红谱 (EXPERT)</option> <option value="3">3 - 🟣 紫谱 (MASTER)</option> <option value="4">4 - ⚪ 白谱 (RE:MASTER)</option> </select> <div class="small-note">※ 0→绿 / 1→黄 / 2→红 / 3→紫 / 4→白</div> </div>
<div class="input-group"> <label>🔄 游玩次数</label> <input type="number" id="playCount" placeholder="例: 3" min="0" step="1" value="1"> </div>
<div class="input-group"> <label>🏆 成绩分数</label> <input type="number" id="score" placeholder="例: 1010000" value="1010000" step="1"> <div class="small-note">理论值 1010000</div> </div>
<div class="input-group"> <label>⚡ 连击数 (Combo)</label> <input type="number" id="combo" placeholder="最大连击" min="0" value="1250" step="1"> </div>
<div class="input-group"> <label>🎯 同步分数 <span class="badge-hint">0无 → 5sync</span></label> <select id="syncSelect"> <option value="0">0 - 无 (None)</option> <option value="1">1 - fs</option> <option value="2">2 - fs+</option> <option value="3">3 - fdx</option> <option value="4">4 - fdx+</option> <option value="5">5 - sync</option> </select> <div class="small-note">0:无 / 1:fs / 2:fs+ / 3:fdx / 4:fdx+ / 5:sync</div> </div>
<div class="input-group"> <label>✨ DX分</label> <input type="number" id="dxScore" placeholder="DX 达成率分数" value="2845" step="any"> <div class="small-note">例如 2870</div> </div> </div> </div>
<div class="action-area"> <button class="submit-btn" id="submitDataBtn">📡 提交并生成指令</button> </div>
<div class="result-area"> <div class="result-header">📟 后端返回指令</div> <pre id="resultOutput" class="result-content">等待提交...</pre> </div> </div>
<div id="toastMessage" class="toast-msg"></div>
<script th:src="@{/js/script.js}" defer></script> </body>
</html>
|
style.css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
| * { margin: 0; padding: 0; box-sizing: border-box; font-family: system-ui, 'Segoe UI', 'Poppins', 'Noto Sans', sans-serif; }
body { background: linear-gradient(145deg, #f0f4fa 0%, #d9e2ef 100%); min-height: 100vh; display: flex; justify-content: center; align-items: center; padding: 2rem 1.5rem; }
.card { max-width: 960px; width: 100%; background: rgba(255, 255, 255, 0.96); backdrop-filter: blur(2px); border-radius: 2rem; box-shadow: 0 20px 35px -12px rgba(0, 0, 0, 0.2), 0 1px 3px rgba(0, 0, 0, 0.05); overflow: hidden; transition: all 0.2s ease; }
.header { background: #1a2c3e; padding: 1.5rem 2rem; color: white; }
.header h1 { font-size: 1.8rem; font-weight: 600; letter-spacing: -0.3px; display: flex; align-items: center; gap: 0.5rem; }
.header h1 span { background: #ffb347; font-size: 0.85rem; padding: 0.2rem 0.7rem; border-radius: 40px; color: #1a2c3e; font-weight: 500; }
.header p { font-size: 0.85rem; opacity: 0.8; margin-top: 0.5rem; }
.mode-section { padding: 1.8rem 2rem 0.8rem 2rem; border-bottom: 1px solid #e9edf2; background: #ffffff; }
.mode-label { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; font-weight: 600; color: #1e2f3f; }
.mode-label span { font-size: 1rem; }
select { background-color: #f8fafc; border: 1px solid #cbd5e1; border-radius: 60px; padding: 0.6rem 1.8rem 0.6rem 1.2rem; font-size: 1rem; font-weight: 500; color: #0f172a; cursor: pointer; transition: all 0.2s; appearance: none; background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23334155' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'></polyline></svg>"); background-repeat: no-repeat; background-position: right 1rem center; background-size: 1rem; }
select:focus { outline: none; border-color: #ff8c42; box-shadow: 0 0 0 3px rgba(255, 140, 66, 0.2); }
.form-container { padding: 1.8rem 2rem 2rem 2rem; transition: all 0.2s ease; }
.form-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 1.5rem 2rem; }
.input-group { display: flex; flex-direction: column; gap: 0.5rem; }
.input-group label { font-weight: 600; font-size: 0.9rem; color: #1e2f3f; display: flex; align-items: center; flex-wrap: wrap; gap: 0.5rem; }
.badge-hint { background: #eef2ff; font-size: 0.7rem; font-weight: normal; padding: 0.2rem 0.6rem; border-radius: 30px; color: #2c3e66; letter-spacing: 0.3px; }
input, select { background: #ffffff; border: 1px solid #cfdee9; border-radius: 1rem; padding: 0.7rem 1rem; font-size: 0.95rem; transition: all 0.2s; color: #0a1c2a; }
input:focus, select:focus { outline: none; border-color: #ff8c42; box-shadow: 0 0 0 3px rgba(255, 140, 66, 0.15); }
input::placeholder { color: #9aaebf; font-weight: 400; font-size: 0.85rem; }
.action-area { padding: 0rem 2rem 1rem 2rem; display: flex; justify-content: flex-end; border-top: 1px solid #eef2f8; margin-top: 0.2rem; }
.submit-btn { background: #1e2f3f; border: none; padding: 0.75rem 1.8rem; border-radius: 2rem; font-weight: 600; font-size: 0.9rem; color: white; cursor: pointer; transition: all 0.2s; display: inline-flex; align-items: center; gap: 0.5rem; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05); }
.submit-btn:hover { background: #ff8c42; transform: translateY(-2px); box-shadow: 0 8px 18px rgba(255, 140, 66, 0.25); }
.result-area { margin: 0 2rem 2rem 2rem; background: #f8fafc; border-radius: 1.2rem; border: 1px solid #e2e8f0; overflow: hidden; }
.result-header { background: #eef2ff; padding: 0.7rem 1.2rem; font-weight: 600; color: #1e2f3f; border-bottom: 1px solid #e2e8f0; font-size: 0.85rem; }
.result-content { background: #ffffff; padding: 1rem 1.2rem; font-family: 'SF Mono', 'Fira Code', monospace; font-size: 0.9rem; white-space: pre-wrap; word-wrap: break-word; color: #0f172a; min-height: 100px; max-height: 200px; overflow-y: auto; margin: 0; }
.toast-msg { position: fixed; bottom: 25px; left: 50%; transform: translateX(-50%) scale(0.9); background: #1e293b; color: #f1f5f9; padding: 0.7rem 1.6rem; border-radius: 60px; font-size: 0.85rem; font-weight: 500; box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2); opacity: 0; transition: opacity 0.2s, transform 0.2s; pointer-events: none; z-index: 1000; backdrop-filter: blur(4px); white-space: nowrap; }
.toast-msg.show { opacity: 1; transform: translateX(-50%) scale(1); }
.hidden-form { display: none; }
.small-note { font-size: 0.7rem; color: #6c86a3; margin-top: 0.2rem; }
@media (max-width: 680px) { .card { border-radius: 1.5rem; }
.form-container { padding: 1.5rem; }
.mode-section, .action-area, .result-area { padding-left: 1.5rem; padding-right: 1.5rem; margin-left: 0; margin-right: 0; }
.form-grid { grid-template-columns: 1fr; gap: 1.2rem; }
.toast-msg { white-space: normal; text-align: center; max-width: 85%; font-size: 0.75rem; padding: 0.6rem 1.2rem; } }
|
script.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
| (function () { const modeSelect = document.getElementById('modeSelect'); const formContainer = document.getElementById('scoreFormContainer'); const submitBtn = document.getElementById('submitDataBtn'); const toastEl = document.getElementById('toastMessage'); const resultOutput = document.getElementById('resultOutput');
const API_URL = '/api/score/submit';
function showMessage(msg, isError = false) { toastEl.textContent = msg; toastEl.classList.add('show'); if (isError) { toastEl.style.backgroundColor = '#b91c1c'; toastEl.style.color = '#fff5f0'; } else { toastEl.style.backgroundColor = '#1e293b'; toastEl.style.color = '#f1f5f9'; } setTimeout(() => { toastEl.classList.remove('show'); setTimeout(() => { toastEl.style.backgroundColor = '#1e293b'; toastEl.style.color = '#f1f5f9'; }, 200); }, 2500); }
function toggleFormByMode(mode) { if (mode === 'transmit') { formContainer.classList.remove('hidden-form'); formContainer.style.display = ''; } else if (mode === 'buried') { formContainer.classList.add('hidden-form'); formContainer.style.display = 'none'; } }
function initMode() { const currentMode = modeSelect.value; if (currentMode === 'buried') { formContainer.classList.add('hidden-form'); formContainer.style.display = 'none'; } else { formContainer.classList.remove('hidden-form'); formContainer.style.display = ''; } }
modeSelect.addEventListener('change', (e) => { toggleFormByMode(e.target.value); resultOutput.textContent = '等待提交...'; });
function collectTransmitData() { const mode = modeSelect.value; if (mode !== 'transmit') { return { success: false, reason: '当前为「下埋」模式,无传分数据。' }; } if (formContainer.classList.contains('hidden-form') || getComputedStyle(formContainer).display === 'none') { return { success: false, reason: '传分表单未激活,请重新选择「传分」模式' }; }
let musicId = document.getElementById('musicId').value.trim(); if (musicId === "") musicId = "None";
let songName = document.getElementById('songName').value.trim(); if (songName === "") songName = "None";
const songVersion = document.getElementById('songVersion').value;
const level = parseInt(document.getElementById('levelSelect').value, 10); const playCount = parseInt(document.getElementById('playCount').value, 10); if (isNaN(playCount) || playCount < 0) { return { success: false, reason: '游玩次数必须是有效数字且≥0' }; }
const score = parseInt(document.getElementById('score').value, 10); if (isNaN(score) || score < 0) { return { success: false, reason: '成绩分数必须是有效整数' }; }
const combo = parseInt(document.getElementById('combo').value, 10); if (isNaN(combo) || combo < 0) { return { success: false, reason: '连击数必须是非负整数' }; }
const sync = parseInt(document.getElementById('syncSelect').value, 10); const dxScoreRaw = document.getElementById('dxScore').value; let dxScore = dxScoreRaw.trim() === "" ? null : parseFloat(dxScoreRaw); if (dxScoreRaw.trim() !== "" && isNaN(dxScore)) { return { success: false, reason: 'DX分格式错误,请输入数字' }; }
return { success: true, data: { musicId: musicId, songName: songName, songVersion: songVersion, level: level, playCount: playCount, score: score, combo: combo, sync: sync, dxScore: dxScore } }; }
async function sendToBackend(payload) { try { const response = await fetch(API_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify(payload) });
if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP ${response.status}: ${errorText}`); }
const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { const jsonResult = await response.json(); return { success: true, data: jsonResult }; } else { const textResult = await response.text(); return { success: true, data: textResult }; } } catch (error) { console.error('POST 请求失败:', error); return { success: false, error: error.message }; } }
function renderCommandFromBackend(responseData) { if (typeof responseData === 'string') { resultOutput.textContent = responseData; return; } if (responseData && typeof responseData === 'object') { if (responseData.command && typeof responseData.command === 'string') { resultOutput.textContent = responseData.command; return; } resultOutput.textContent = JSON.stringify(responseData, null, 2); return; } resultOutput.textContent = String(responseData); }
async function handleSubmit() { const currentMode = modeSelect.value;
if (currentMode === 'buried') { showMessage('🕳️ 下埋模式:未发送请求,可自行扩展', false); resultOutput.textContent = '下埋模式,无后端请求。'; return; }
const collectResult = collectTransmitData(); if (!collectResult.success) { showMessage(`❌ ${collectResult.reason}`, true); return; }
const payload = collectResult.data; resultOutput.textContent = '⏳ 正在向后端请求指令...';
const response = await sendToBackend(payload); if (response.success) { showMessage('✅ 后端处理成功,指令已显示', false); renderCommandFromBackend(response.data); } else { showMessage(`❌ 请求失败: ${response.error}`, true); resultOutput.textContent = `错误:${response.error}\n请检查后端接口是否可用。`; } }
submitBtn.addEventListener('click', handleSubmit); initMode(); })();
|
模板渲染则是使用 thymeleaf 框架,maven 的pom.xml中加入这个就可以了:
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
|
前置就解决了。
三、小知识点
在写这个的时候,我发现,导入了thymeleaf之后,创建一个controller的java文件之后,如果采用@Controller这个注解的话,直接return "index;"的时候,就算是把index.html给返回了,如果是@RestController的话,之后返回index这一个字符串给浏览器。
这个控制器的后端代码比较简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.g01den.demo01.controller;
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping;
@Controller public class divingFishController { @GetMapping("/divingFish") public String divingFishGet() { return "index"; } }
|
四、关于歌曲别名的api和相关解析的代码
之后就是api了,看前端js代码可以知道,那里通过js请求了后端中中构造command的api,然后获得返回值之后渲染到输出框里。
那么关于这里,找可以通过歌曲别名搜索歌曲信息的api就是难点,本来我确实找到了一个,就是这个https://oss.lista233.cn/alias.json,本来确实开发完成了,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
| package com.g01den.demo01.controller;
import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; 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 java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Map;
@RestController @RequestMapping("/api/score") public class divingFishApiDemo {
public String aliaGetID(String alias, String version) throws IOException, JSONException { String id = ""; int tmp = -1; String aliasUrl = "https://oss.lista233.cn/alias.json";
HttpURLConnection conn = null; BufferedReader reader = null; StringBuilder response = new StringBuilder();
try { URL url = new URL(aliasUrl); conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET"); conn.setConnectTimeout(15000); conn.setReadTimeout(15000);
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"); conn.setRequestProperty("Accept", "application/json, text/plain, */*"); conn.setRequestProperty("Connection", "keep-alive");
try (InputStreamReader isr = new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8); BufferedReader br = new BufferedReader(isr)) { String line; while ((line = br.readLine()) != null) { response.append(line); } }
JSONObject jsonResponse = new JSONObject(response.toString()) ;
if (!jsonResponse.has("content")) { throw new JSONException("JSON数据格式错误:缺少 content 字段"); }
JSONArray aliasData = jsonResponse.getJSONArray("content");
for (int i = 0; i < aliasData.length(); i++) { JSONObject item = aliasData.getJSONObject(i);
if (!item.has("SongID") || !item.has("Alias")) { continue; }
String songId = item.getString("SongID"); JSONArray aliases = item.getJSONArray("Alias");
for (int j = 0; j < aliases.length(); j++) { if (alias.equals(aliases.getString(j))) { try { tmp = Integer.parseInt(songId); } catch (NumberFormatException e) { continue; }
if (version.equals("DX谱")) { if (tmp >= 10000) { id = songId; } } else if (version.equals("标准谱")) { if (tmp < 10000) { id = songId; } } } } } } finally { if (conn != null) { conn.disconnect(); } } return id; }
@PostMapping("/submit") public String submitScore(@RequestBody Map<String, Object> payload) throws JSONException, IOException { String musicId = payload.getOrDefault("musicId", "0").toString(); String songName = payload.getOrDefault("songName", "").toString(); String songVersion = payload.getOrDefault("songVersion", "").toString(); int level = ((Number) payload.getOrDefault("level", 0)).intValue(); int playCount = ((Number) payload.getOrDefault("playCount", 0)).intValue(); int score = ((Number) payload.getOrDefault("score", 0)).intValue(); int combo = ((Number) payload.getOrDefault("combo", 0)).intValue(); int sync = ((Number) payload.getOrDefault("sync", 0)).intValue(); Object dxScoreObj = payload.get("dxScore"); String dxScore = (dxScoreObj == null) ? "0" : dxScoreObj.toString();
if (musicId.equals("None") && !songName.equals("None")) { musicId = aliaGetID(songName, songVersion); if (musicId == null || musicId.isEmpty()) { return "{\"command\": \"未找到对应歌曲,请检查别名或版本\"}"; } } else if (musicId.equals("None") && songName.equals("None")) { return "{\"command\": \"ID和别名不能为空\"}"; } String command = String.format("传分 %s %d %d %d %d %d %s", musicId, level, playCount, score, combo, sync, dxScore); return "{\"command\": \"" + command + "\"}"; } }
|
五、部署到Windows服务器上:
通过maven打包了jar包之后,写三个bat文件:
start.bat
1 2 3 4 5 6 7 8 9 10
| @echo off set APP_NAME=demo01-0.0.1-SNAPSHOT set LOG_FILE=app.log
echo Starting %APP_NAME%...
REM 后台运行应用,并将控制台输出重定向到日志文件 start /b javaw -jar %APP_NAME%.jar > %LOG_FILE% 2>&1
echo Application started. Check %LOG_FILE% for details.
|
stop.bat
1 2 3 4 5 6 7
| @echo off echo Stopping Java applications...
REM 查找并终止所有 javaw.exe 进程 taskkill /f /im javaw.exe
echo All Java processes have been stopped.
|
restart.bat
1 2 3 4
| @echo off call stop.bat timeout /t 3 > nul call start.bat
|
之后安装jdk之后运行对应bat代码即可。
六、部署之后服务器上DNS解析不了
本来感觉成功了,本地都跑起来了,并且正常运行了,结果上了香港那边的服务器之后发现DNS解析不了,这个不难解决,通过clash之类的搞一个大陆的代理即可。
七、第二天直接本地都无法访问api了
第二天,本来想安装个clash去换个代理的,结果就出现了,本地测试的时候突然别名的那个api无法使用了,直接无法访问了,这就很尴尬,本来就是第一个SpringBoot小功能,结果炸了,用不了了,我一直哭。
八、🐧我感觉我被世界针对了🐧(开始发癫)
我操了老铁,我感觉我被世界针对了,你搓码子好不容易搓出来,本地能跑起来,都部署到服务器了,结果上了服务器之后,因为不知道什么原因,DNS解析不上,布什戈门?我本地都能调用api,结果服务器因为DNS解析不上调用不了api。这我忍了,给程序上个proxy就好了,但是,最让人气愤的来了,第二天本来想着修这个bug的,结果本地用python和java写的测试程序都访问不了这个api了,但是浏览器能访问,本以为加上user-agent能解决,结果来来回回换了好几次都解决不了。我真求你了,每次写的程序本来能跑,上了远程就跑不了了,然后过不了多久,本地也跑不了了。老天爷,我不想活了,你下叼啊,操死我。
咕咕嘎嘎!!!