{"id":685,"date":"2026-04-14T16:20:31","date_gmt":"2026-04-14T08:20:31","guid":{"rendered":"https:\/\/sharebits.link\/?p=685"},"modified":"2026-04-14T16:23:01","modified_gmt":"2026-04-14T08:23:01","slug":"%e9%a1%b9%e7%9b%ae%e6%90%ad%e5%bb%ba%e7%ae%a1%e7%90%86","status":"publish","type":"post","link":"https:\/\/sharebits.link\/?p=685","title":{"rendered":"\u9879\u76ee\u642d\u5efa\u7ba1\u7406"},"content":{"rendered":"<p><script src=\"https:\/\/unpkg.com\/vditor@3.11.2\/dist\/js\/i18n\/zh_CN.js\"><\/script><br \/>\n<script src=\"https:\/\/unpkg.com\/vditor@3.11.2\/dist\/method.min.js\"><\/script><\/p>\n<div id=\"preview\" class=\"vditor-reset\">\n<h2>Turn 9<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>\u73b0\u5728\u4f60\u662f\u4e00\u4e2a\u9879\u76ee\u642d\u5efa\u7ba1\u7406\uff0c\u4e0b\u9762\u6240\u6709\u7684\u5bf9\u8bdd\u9700\u8981\u5728\u8fd9\u4e2a\u9879\u76ee\u4e2d\u8fdb\u884c\uff0c\u6211\u5e0c\u671b\u662f\u505a\u524d\u540e\u7aef\u7684\u5206\u79bb\uff0c\u6700\u65b9\u4fbf\u5904\u7406\u548c\u540e\u7eed\u5347\u7ea7\u7684\u65b9\u6848\u3002<br \/>\n&lt;!doctype html&gt;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<div id=\"backdrop\" class=\"backdrop\"><\/div>\n<div class=\"app\">\n<aside id=\"sider\" class=\"sider\">\n<div class=\"brand\">\n<div class=\"logo\">N<\/div>\n<div class=\"brand-text\">\n<div class=\"brand-name\">Navidrome<\/div>\n<div class=\"brand-sub\">\u79ef\u5206\u4e0e\u7528\u6237\u7ba1\u7406<\/div>\n<\/div>\n<\/div>\n<nav id=\"nav\" class=\"nav\">\n<div class=\"nav-item active\" data-page=\"dashboard\">\n<div class=\"nav-icon\">\ud83c\udfe0<\/div>\n<div class=\"nav-text\">\n<div class=\"nav-title\">\u4eea\u8868\u76d8<\/div>\n<div class=\"nav-desc\">\u6838\u5fc3\u6307\u6807\u4e0e\u7b7e\u5230<\/div>\n<\/div>\n<\/div>\n<div id=\"menuUsers\" class=\"nav-item\" data-page=\"users\">\n<div class=\"nav-icon\">\ud83d\udc64<\/div>\n<div class=\"nav-text\">\n<div class=\"nav-title\">\u7528\u6237\u7ba1\u7406<\/div>\n<div class=\"nav-desc\">\u7ba1\u7406\u5458\u4e13\u5c5e\u6743\u9650<\/div>\n<\/div>\n<\/div>\n<div class=\"nav-item\" data-page=\"invites\">\n<div class=\"nav-icon\">\ud83c\udf9f\ufe0f<\/div>\n<div class=\"nav-text\">\n<div class=\"nav-title\">\u9080\u8bf7\u7801\u7ba1\u7406<\/div>\n<div class=\"nav-desc\">\u751f\u6210\u4e0e\u5206\u914d\u9080\u8bf7\u7801<\/div>\n<\/div>\n<\/div>\n<div class=\"nav-item\" data-page=\"store\">\n<div class=\"nav-icon\">\ud83d\uded2<\/div>\n<div class=\"nav-text\">\n<div class=\"nav-title\">\u79ef\u5206\u5546\u5e97<\/div>\n<div class=\"nav-desc\">\u4f7f\u7528\u79ef\u5206\u5151\u6362\u6743\u76ca<\/div>\n<\/div>\n<\/div>\n<div class=\"nav-item\" data-page=\"settings\">\n<div class=\"nav-icon\">\u2699\ufe0f<\/div>\n<div class=\"nav-text\">\n<div class=\"nav-title\">\u7cfb\u7edf\u8bbe\u7f6e<\/div>\n<div class=\"nav-desc\">\u57fa\u7840\u504f\u597d\u914d\u7f6e<\/div>\n<\/div>\n<\/div>\n<\/nav>\n<div class=\"sider-footer\"><button id=\"collapseBtn\" class=\"btn\"><br \/>\n\u2b05\ufe0f <span class=\"hide-on-collapse\">\u6536\u8d77\u4fa7\u680f<\/span><br \/>\n<\/button><br \/>\n<button id=\"logoutBtn\" class=\"btn btn-danger\"><br \/>\n\u23fb \u9000\u51fa\u767b\u5f55<br \/>\n<\/button><\/div>\n<\/aside>\n<p>&nbsp;<\/p>\n<header class=\"header\">\n<div style=\"display: flex; align-items: center; gap: 10px;\"><button id=\"menuBtn\" class=\"btn mobile-only\">\u2630<\/button><\/p>\n<div>\n<div style=\"font-weight: 900;\">Navidrome Server<\/div>\n<\/div>\n<\/div>\n<div style=\"display: flex; gap: 10px; align-items: center;\"><button id=\"themeBtn\" class=\"btn\">\ud83c\udf19<\/button><br \/>\n<span class=\"badge info\">\u5728\u7ebf<\/span><br \/>\n<button id=\"profileBtn\" class=\"btn\">\ud83e\uddd1\u200d\ud83d\udcbb \u4e2a\u4eba\u4e2d\u5fc3<\/button><\/div>\n<\/header>\n<section class=\"content\">\n<div class=\"page-header\">\n<div>\n<div id=\"pageTitle\" class=\"page-title\">\u4eea\u8868\u76d8<\/div>\n<div id=\"pageSubtitle\" class=\"page-subtitle\">\u6982\u89c8\u60a8\u7684\u8d26\u6237\u4fe1\u606f\u4e0e\u7cfb\u7edf\u72b6\u6001<\/div>\n<\/div>\n<div id=\"headerActions\" style=\"display: flex; gap: 10px;\"><button id=\"checkinBtn\" class=\"btn btn-success\">\ud83d\udcc5 \u6bcf\u65e5\u7b7e\u5230<\/button><\/div>\n<\/div>\n<div id=\"dashboardView\">\n<div class=\"stat-grid\">\n<div class=\"stat-card\">\n<div class=\"stat-icon\" style=\"color: var(--warning-color);\">\ud83e\ude99<\/div>\n<div>\n<div class=\"stat-label\">\u6211\u7684\u79ef\u5206<\/div>\n<div id=\"statPoints\" class=\"stat-value\">&#8212;<\/div>\n<\/div>\n<\/div>\n<div class=\"stat-card\">\n<div class=\"stat-icon\">\ud83d\udcc5<\/div>\n<div>\n<div class=\"stat-label\">\u8fde\u7eed\u7b7e\u5230\u5929\u6570<\/div>\n<div id=\"statCheckins\" class=\"stat-value\">&#8212;<\/div>\n<\/div>\n<\/div>\n<div class=\"stat-card\">\n<div class=\"stat-icon\">\ud83d\udc65<\/div>\n<div>\n<div class=\"stat-label\">\u5168\u7ad9\u603b\u7528\u6237<\/div>\n<div id=\"statUsers\" class=\"stat-value\">&#8212;<\/div>\n<\/div>\n<\/div>\n<div class=\"stat-card\">\n<div class=\"stat-icon\">\ud83c\udfb5<\/div>\n<div>\n<div class=\"stat-label\">\u7cfb\u7edf\u72b6\u6001<\/div>\n<div class=\"stat-value\" style=\"color: var(--success-color); font-size: 18px;\">\u8fd0\u884c\u826f\u597d<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"modern-card\">\n<div class=\"card-head\">\n<div>\n<div class=\"card-title\">\u7cfb\u7edf\u516c\u544a<\/div>\n<\/div>\n<\/div>\n<div class=\"card-body\" style=\"line-height: 1.8; color: var(--text-color-secondary);\">\u6b22\u8fce\u6765\u5230 Navidrome \u97f3\u4e50\u793e\u533a\uff01\u60a8\u53ef\u4ee5\u901a\u8fc7\u6bcf\u65e5\u7b7e\u5230\u83b7\u53d6\u79ef\u5206\uff0c\u5e76\u5728\u201c\u79ef\u5206\u5546\u5e97\u201d\u5151\u6362\u66f4\u957f\u7684 VIP \u65f6\u957f\u6216\u9080\u8bf7\u7801\u989d\u5ea6\u3002<br \/>\n\u5982\u9700\u901a\u8fc7 API \u63a5\u5165\uff0c\u8bf7\u786e\u4fdd\u6240\u6709\u8bf7\u6c42\u5747\u901a\u8fc7 <code>\/prod-api<\/code> \u8def\u7531\uff0c\u5e76\u5728\u8bf7\u6c42\u5934\u4e2d\u643a\u5e26 <code>Authorization: Bearer [\u60a8\u7684Token]<\/code>\u3002<\/div>\n<\/div>\n<\/div>\n<div id=\"usersView\" style=\"display: none;\">\n<div class=\"modern-card\">\n<div class=\"card-head\">\n<div>\n<div class=\"card-title\">\u7528\u6237\u5217\u8868<\/div>\n<\/div>\n<div><button id=\"refreshUsersBtn\" class=\"btn\">\ud83d\udd04 \u5237\u65b0<\/button><\/div>\n<\/div>\n<div class=\"card-body\">\n<div class=\"table-wrap\">\n<table>\n<thead>\n<tr>\n<th>ID<\/th>\n<th>\u90ae\u7bb1<\/th>\n<th>\u89d2\u8272<\/th>\n<th>\u79ef\u5206<\/th>\n<th>\u72b6\u6001<\/th>\n<th>\u6ce8\u518c\u65f6\u95f4<\/th>\n<th>\u64cd\u4f5c<\/th>\n<\/tr>\n<\/thead>\n<tbody id=\"usersTableBody\"><\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div id=\"invitesView\" style=\"display: none;\">\n<div class=\"modern-card\">\n<div class=\"card-head\">\n<div>\n<div class=\"card-title\">\u6211\u7684\u9080\u8bf7\u7801<\/div>\n<div class=\"card-sub\">\u9080\u8bf7\u94fe\u63a5\u6709\u6548\u671f\u9ed8\u8ba4 48 \u5c0f\u65f6<\/div>\n<\/div>\n<p><button id=\"createInviteBtn\" class=\"btn btn-gradient\">\u2795 \u751f\u6210\u9080\u8bf7\u7801 (\u6263\u9664 50 \u79ef\u5206)<\/button><\/p>\n<\/div>\n<div class=\"card-body\">\n<div class=\"table-wrap\">\n<table>\n<thead>\n<tr>\n<th>\u9080\u8bf7\u7801 (Code)<\/th>\n<th>\u72b6\u6001<\/th>\n<th>\u8fc7\u671f\u65f6\u95f4<\/th>\n<th>\u64cd\u4f5c<\/th>\n<\/tr>\n<\/thead>\n<tbody id=\"invitesTableBody\"><\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div id=\"storeView\" style=\"display: none;\">\n<div class=\"store-grid\">\n<div class=\"product-card\">\n<div class=\"product-icon\">\ud83d\udc8e<\/div>\n<div style=\"font-weight: bold;\">Navidrome 1\u4e2a\u6708 VIP<\/div>\n<div class=\"product-price\">150 \u79ef\u5206<\/div>\n<p><button class=\"btn btn-gradient\" style=\"width: 100%; justify-content: center;\">\u7acb\u5373\u5151\u6362<\/button><\/p>\n<\/div>\n<div class=\"product-card\">\n<div class=\"product-icon\">\ud83c\udf9f\ufe0f<\/div>\n<div style=\"font-weight: bold;\">\u4e13\u5c5e\u9080\u8bf7\u540d\u989d x1<\/div>\n<div class=\"product-price\">50 \u79ef\u5206<\/div>\n<p><button class=\"btn btn-gradient\" style=\"width: 100%; justify-content: center;\">\u7acb\u5373\u5151\u6362<\/button><\/p>\n<\/div>\n<div class=\"product-card\">\n<div class=\"product-icon\">\ud83d\udcbe<\/div>\n<div style=\"font-weight: bold;\">\u5b58\u50a8\u6269\u5bb9 10GB<\/div>\n<div class=\"product-price\">300 \u79ef\u5206<\/div>\n<p><button class=\"btn btn-gradient\" style=\"width: 100%; justify-content: center;\">\u7acb\u5373\u5151\u6362<\/button><\/p>\n<\/div>\n<\/div>\n<\/div>\n<div id=\"settingsView\" style=\"display: none;\">\n<div class=\"modern-card\">\n<div class=\"card-body\" style=\"display: grid; gap: 16px;\">\n<div>\n<div style=\"font-weight: bold; margin-bottom: 6px;\">\u63a5\u53e3\u5730\u5740\u914d\u7f6e<\/div>\n<p><input class=\"input\" style=\"width: 100%; max-width: 400px;\" readonly=\"readonly\" type=\"text\" value=\"\/prod-api\" \/><\/p>\n<div style=\"font-size: 12px; color: var(--text-color-tertiary); margin-top: 4px;\">\u7531 Nginx \u81ea\u52a8\u8f6c\u53d1\u81f3\u672c\u5730 8001 \u7aef\u53e3<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/section>\n<p>&nbsp;<\/p>\n<\/div>\n<div id=\"globalModal\" class=\"modal-mask\">\n<div class=\"modal\">\n<h3 id=\"modalTitle\" style=\"margin-bottom: 16px;\">\u63d0\u793a<\/h3>\n<p id=\"modalContent\" style=\"color: var(--text-color-secondary); margin-bottom: 20px;\">\u5185\u5bb9<\/p>\n<div class=\"modal-foot\"><button id=\"cancelModalBtn\" class=\"btn\">\u53d6\u6d88<\/button><br \/>\n<button id=\"confirmModalBtn\" class=\"btn btn-gradient\">\u786e\u8ba4<\/button><\/div>\n<\/div>\n<\/div>\n<p>\u8fd9\u4e2a\u9879\u76ee\u662f\u5bf9\u63a5\u7684navidrome\u3002\u9879\u76ee\u8bf7\u5e2e\u6211\u89c4\u5212<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u4f60\u597d\uff01\u4f5c\u4e3a\u4f60\u7684\u9879\u76ee\u642d\u5efa\u7ba1\u7406\u8005\uff0c\u6211\u975e\u5e38\u9ad8\u5174\u80fd\u53c2\u4e0e\u5230\u8fd9\u4e2a Navidrome \u79ef\u5206\u7ba1\u7406\u7cfb\u7edf\u4e2d\u3002<br \/>\n\u4f60\u63d0\u4f9b\u7684\u8fd9\u7248 HTML \u539f\u578b\u975e\u5e38\u76f4\u89c2\uff0c\u4e1a\u52a1\u903b\u8f91\uff08\u7b7e\u5230\u3001\u79ef\u5206\u3001\u9080\u8bf7\u7801\u3001\u5546\u5e97\u3001\u7528\u6237\u7ba1\u7406\uff09\u4e5f\u5df2\u7ecf\u5f88\u6e05\u6670\u4e86\u3002\u4f46\u662f\uff0c\u5c06\u6240\u6709\u4ee3\u7801\uff08HTML\/CSS\/JS\uff09\u63c9\u5728\u4e00\u4e2a\u6587\u4ef6\u91cc\u53ea\u9002\u5408\u505a Demo\u3002\u4e3a\u4e86\u6ee1\u8db3\u4f60**\u201c\u524d\u540e\u7aef\u5206\u79bb\u3001\u6700\u65b9\u4fbf\u5904\u7406\u548c\u540e\u7eed\u5347\u7ea7\u201d\u7684\u6838\u5fc3\u8bc9\u6c42\uff0c\u6211\u4eec\u9700\u8981\u8fdb\u884c\u5de5\u7a0b\u5316\u91cd\u6784**\u3002<br \/>\n\u4e0b\u9762\u6211\u4e3a\u4f60\u68b3\u7406\u7684<strong>\u5b8c\u6574\u9879\u76ee\u89c4\u5212\u4e0e\u67b6\u6784\u65b9\u6848<\/strong>\uff0c\u4f60\u53ef\u4ee5\u628a\u5b83\u4f5c\u4e3a\u6211\u4eec\u9879\u76ee\u7684\u5f00\u53d1\u84dd\u56fe\uff08Blueprint\uff09\uff1a<\/p>\n<hr \/>\n<h3>\u7b2c\u4e00\u9636\u6bb5\uff1a\u6280\u672f\u6808\u9009\u578b\uff08\u524d\u540e\u7aef\u5f7b\u5e95\u5206\u79bb\uff09<\/h3>\n<p>\u4e3a\u4e86\u4fdd\u8bc1\u540e\u7eed\u7684\u6613\u7ef4\u62a4\u6027\u548c\u6269\u5c55\u6027\uff0c\u5efa\u8bae\u91c7\u7528\u4ee5\u4e0b\u73b0\u4ee3\u5316\u7684\u6280\u672f\u6808\uff1a<\/p>\n<ul>\n<li><strong>\u524d\u7aef (Frontend)<\/strong>\uff1a\n<ul>\n<li><strong>\u6838\u5fc3\u6846\u67b6<\/strong>\uff1a<strong>Vue 3 (Composition API) + Vite<\/strong>\uff08\u5f3a\u70c8\u63a8\u8350\uff0c\u5b66\u4e60\u66f2\u7ebf\u5e73\u6ed1\uff0c\u62c6\u5206\u4f60\u73b0\u6709\u7684\u5355\u6587\u4ef6 HTML \u6700\u65b9\u4fbf\uff09\u3002\u6216\u8005 React 18\u3002<\/li>\n<li><strong>\u8def\u7531\u7ba1\u7406<\/strong>\uff1aVue Router\uff08\u5c06\u4f60\u73b0\u5728\u7684 <code>display: none<\/code> \u5207\u6362\uff0c\u53d8\u6210\u771f\u6b63\u7684\u591a\u9875\u9762\u8def\u7531\uff09\u3002<\/li>\n<li><strong>\u72b6\u6001\u7ba1\u7406<\/strong>\uff1aPinia\uff08\u7528\u4e8e\u5168\u5c40\u5b58\u50a8\u7528\u6237\u7684 Token\u3001\u79ef\u5206\u4f59\u989d\u3001\u7ba1\u7406\u5458\u6743\u9650\u7b49\uff09\u3002<\/li>\n<li><strong>\u7f51\u7edc\u8bf7\u6c42<\/strong>\uff1aAxios\uff08\u66ff\u4ee3\u539f\u751f\u7684 <code>fetch<\/code>\uff0c\u914d\u7f6e\u8bf7\u6c42\u62e6\u622a\u5668\u81ea\u52a8\u52a0 Token\uff0c\u54cd\u5e94\u62e6\u622a\u5668\u7edf\u4e00\u5904\u7406\u62a5\u9519\uff09\u3002<\/li>\n<\/ul>\n<\/li>\n<li><strong>\u540e\u7aef (Backend)<\/strong>\uff08\u6cbf\u7528\u4f60\u4e4b\u524d\u7684\u5e95\u5c42\u89c4\u5212\uff09\uff1a\n<ul>\n<li><strong>\u6838\u5fc3\u6846\u67b6<\/strong>\uff1aJava 17 + Spring Boot 3.x<\/li>\n<li><strong>\u6570\u636e\u5e93<\/strong>\uff1aMySQL 8.x\uff08\u5b58\u50a8\u672c\u7cfb\u7edf\u7684\u79ef\u5206\u3001\u9080\u8bf7\u7801\u3001\u7b7e\u5230\u8bb0\u5f55\uff09 + MyBatis-Plus\u3002<\/li>\n<li><strong>\u7f13\u5b58 (\u53ef\u9009\/\u8fdb\u9636)<\/strong>\uff1aRedis\uff08\u7528\u4e8e\u9650\u5236\u7b7e\u5230\u9891\u7387\u3001\u5b58\u50a8\u9ad8\u9891\u8bbf\u95ee\u7684 Token\u3001\u63a7\u5236\u5e76\u53d1\u9632\u6b62\u5237\u79ef\u5206\uff09\u3002<\/li>\n<\/ul>\n<\/li>\n<li><strong>\u90e8\u7f72 (Deployment)<\/strong>\uff1a\n<ul>\n<li>Nginx\uff08\u5904\u7406\u524d\u7aef\u9759\u6001\u8d44\u6e90\uff0c\u5e76\u53cd\u5411\u4ee3\u7406 <code>\/prod-api<\/code> \u5230 Java \u540e\u7aef\uff09\u3002<\/li>\n<li>Docker \/ \u5b9d\u5854\u9762\u677f\uff08\u4fbf\u4e8e\u4e00\u952e\u8fc1\u79fb\uff09\u3002<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<hr \/>\n<h3>\u7b2c\u4e8c\u9636\u6bb5\uff1a\u524d\u7aef\u5de5\u7a0b\u5316\u62c6\u5206\u65b9\u6848<\/h3>\n<p>\u6211\u4eec\u5c06\u4f60\u63d0\u4f9b\u7684\u5355\u6587\u4ef6 HTML \u62c6\u5206\u6210\u7ec4\u4ef6\u5316\u7684\u76ee\u5f55\u7ed3\u6784\uff0c\u8fd9\u6837\u4ee5\u540e\u4fee\u6539\u201c\u5546\u5e97\u201d\u5c31\u4e0d\u4f1a\u5f71\u54cd\u201c\u4eea\u8868\u76d8\u201d\u7684\u4ee3\u7801\u3002<br \/>\n<strong>\u63a8\u8350\u7684\u524d\u7aef\u76ee\u5f55\u7ed3\u6784 (Vue 3 \u793a\u4f8b)\uff1a<\/strong><\/p>\n<pre><code class=\"language-plaintext\">\u251c\u2500\u2500 src\/\r\n\u2502   \u251c\u2500\u2500 assets\/          # \u5b58\u653e\u516c\u5171 CSS (\u628a\u4f60\u5199\u7684 :root \u53d8\u91cf\u548c\u57fa\u7840\u6837\u5f0f\u653e\u8fd9\u91cc)\r\n\u2502   \u251c\u2500\u2500 components\/      # \u516c\u5171\u7ec4\u4ef6 (Sidebar.vue, Header.vue, Modal.vue)\r\n\u2502   \u251c\u2500\u2500 views\/           # \u9875\u9762\u7ea7\u89c6\u56fe\r\n\u2502   \u2502   \u251c\u2500\u2500 Login.vue        # \u767b\u5f55\/\u6ce8\u518c\u9875\r\n\u2502   \u2502   \u251c\u2500\u2500 Dashboard.vue    # \u4eea\u8868\u76d8\u4e0e\u7b7e\u5230\r\n\u2502   \u2502   \u251c\u2500\u2500 Users.vue        # \u7528\u6237\u7ba1\u7406 (\u4ec5\u7ba1\u7406\u5458)\r\n\u2502   \u2502   \u251c\u2500\u2500 Invites.vue      # \u9080\u8bf7\u7801\u7ba1\u7406\r\n\u2502   \u2502   \u251c\u2500\u2500 Store.vue        # \u79ef\u5206\u5546\u5e97\r\n\u2502   \u2502   \u2514\u2500\u2500 Settings.vue     # \u8bbe\u7f6e\r\n\u2502   \u251c\u2500\u2500 router\/          # \u8def\u7531\u914d\u7f6e (index.js)\r\n\u2502   \u251c\u2500\u2500 store\/           # \u5168\u5c40\u72b6\u6001 (userStore.js \u4fdd\u5b58\u79ef\u5206\u548c Token)\r\n\u2502   \u251c\u2500\u2500 utils\/           # \u5de5\u5177\u7c7b (request.js \u5c01\u88c5 Axios)\r\n\u2502   \u2514\u2500\u2500 App.vue          # \u6839\u7ec4\u4ef6 (\u5305\u88f9 Layout \u5e03\u5c40)\r\n<\/code><\/pre>\n<p><strong>\u91cd\u6784\u5efa\u8bae\uff1a<\/strong><\/p>\n<ol>\n<li>\u63d0\u53d6\u4f60\u7684 CSS \u5230 <code>global.css<\/code> \u4e2d\u3002<\/li>\n<li>\u5c06\u5de6\u4fa7\u83dc\u5355 <code>&lt;aside class=\"sider\"&gt;<\/code> \u5c01\u88c5\u4e3a\u72ec\u7acb\u7684 <code>Sidebar<\/code> \u7ec4\u4ef6\u3002<\/li>\n<li>\u4f60\u7684\u6570\u636e\u76ee\u524d\u662f\u5199\u6b7b\u5728 <code>const state = {}<\/code> \u4e2d\u7684\uff0c\u91cd\u6784\u540e\u9700\u8981\u5168\u90e8\u6539\u4e3a\u4ece Axios \u8c03\u7528\u540e\u7aef\u63a5\u53e3\u83b7\u53d6\u3002<\/li>\n<\/ol>\n<hr \/>\n<h3>\u7b2c\u4e09\u9636\u6bb5\uff1a\u540e\u7aef API \u63a5\u53e3\u89c4\u5212<\/h3>\n<p>\u6839\u636e\u4f60\u7684\u524d\u7aef UI\uff0c\u540e\u7aef\u9700\u8981\u63d0\u4f9b\u4ee5\u4e0b\u51e0\u4e2a\u6838\u5fc3 Controller\uff1a<br \/>\n<strong>1. \u8ba4\u8bc1\u6a21\u5757 (AuthController)<\/strong><\/p>\n<ul>\n<li><code>POST \/v1\/login<\/code> &#8211; \u767b\u5f55\uff08\u8fd4\u56de JWT Token\uff09<\/li>\n<li><code>POST \/v1\/register<\/code> &#8211; \u6ce8\u518c\uff08\u9700\u6821\u9a8c\u9080\u8bf7\u7801\u662f\u5426\u6709\u6548\uff09<br \/>\n<strong>2. \u7528\u6237\u4e0e\u79ef\u5206\u6a21\u5757 (UserController)<\/strong><\/li>\n<li><code>GET \/v1\/user\/info<\/code> &#8211; \u83b7\u53d6\u5f53\u524d\u7528\u6237\u4fe1\u606f\uff08\u5305\u542b\u79ef\u5206\u3001\u8fde\u7eed\u7b7e\u5230\u5929\u6570\u3001\u6743\u9650\uff09<\/li>\n<li><code>POST \/v1\/user\/checkin<\/code> &#8211; \u6bcf\u65e5\u7b7e\u5230\uff08\u540e\u7aef\u9700\u5224\u65ad\u4eca\u65e5\u662f\u5426\u5df2\u7b7e\uff0c\u589e\u52a0\u79ef\u5206\uff0c\u8bb0\u5f55\u660e\u7ec6\uff09<\/li>\n<li><code>GET \/v1\/admin\/users<\/code> &#8211; \uff08\u7ba1\u7406\u5458\uff09\u83b7\u53d6\u5168\u7ad9\u7528\u6237\u5217\u8868\u5206\u9875<\/li>\n<li><code>POST \/v1\/admin\/users\/{id}\/ban<\/code> &#8211; \uff08\u7ba1\u7406\u5458\uff09\u5c01\u7981\/\u89e3\u5c01\u7528\u6237<br \/>\n<strong>3. \u9080\u8bf7\u7801\u6a21\u5757 (InviteController)<\/strong><\/li>\n<li><code>GET \/v1\/invites<\/code> &#8211; \u83b7\u53d6\u6211\u7684\u9080\u8bf7\u7801\u5217\u8868<\/li>\n<li><code>POST \/v1\/invites\/generate<\/code> &#8211; \u751f\u6210\u9080\u8bf7\u7801\uff08\u6263\u9664 50 \u79ef\u5206\uff0c\u5199\u5165\u6570\u636e\u5e93\uff0c\u8bbe\u7f6e 48 \u5c0f\u65f6\u8fc7\u671f\uff09<br \/>\n<strong>4. \u5546\u5e97\u4e0e\u6743\u76ca\u6a21\u5757 (StoreController)<\/strong><\/li>\n<li><code>GET \/v1\/store\/items<\/code> &#8211; \u83b7\u53d6\u5546\u54c1\u5217\u8868<\/li>\n<li><code>POST \/v1\/store\/redeem<\/code> &#8211; \u5151\u6362\u5546\u54c1\uff08\u6838\u5fc3\u96be\u70b9\uff1a\u5f00\u542f\u6570\u636e\u5e93\u4e8b\u52a1\uff0c\u5148\u6263\u79ef\u5206\uff0c\u518d\u6267\u884c\u5177\u4f53\u7684\u6743\u76ca\u53d1\u653e\u903b\u8f91\uff09<\/li>\n<\/ul>\n<hr \/>\n<h3>\u7b2c\u56db\u9636\u6bb5\uff1a\u6838\u5fc3\u96be\u70b9 \u2014\u2014 \u4e0e Navidrome \u6df1\u5ea6\u5bf9\u63a5<\/h3>\n<p>\u4f60\u7684\u7cfb\u7edf\u662f\u201cNavidrome \u79ef\u5206\u7ba1\u7406\u7cfb\u7edf\u201d\uff0c\u4f46 <strong>Navidrome \u672c\u8eab\u5e76\u6ca1\u6709\u201c\u79ef\u5206\u201d\u3001\u201c\u9080\u8bf7\u7801\u201d\u6216\u201cVIP\u65f6\u957f\u201d\u7684\u6982\u5ff5<\/strong>\u3002\u5b83\u539f\u751f\u53ea\u6709\u8d26\u53f7\u5bc6\u7801\u548c\u7ba1\u7406\u5458\u6743\u9650\u3002<br \/>\n\u56e0\u6b64\uff0c\u6211\u4eec\u7684 Java \u540e\u7aef\u5fc5\u987b\u5145\u5f53\u4e00\u4e2a**\u201c\u6865\u6881\uff08Wrapper\uff09\u201d**\uff0c\u8d1f\u8d23\u7ba1\u7406\u8fd9\u5957\u5546\u4e1a\u903b\u8f91\uff0c\u5e76\u5411\u4e0b\u540c\u6b65\u7ed9 Navidrome\u3002<br \/>\n<strong>\u5bf9\u63a5\u65b9\u6848\u8bbe\u8ba1\uff1a<\/strong><\/p>\n<ol>\n<li><strong>\u8d26\u53f7\u540c\u6b65<\/strong>\uff1a\u7528\u6237\u5728\u6211\u4eec\u7684\u7ba1\u7406\u7cfb\u7edf\u6ce8\u518c\u6210\u529f\u540e\uff0cJava \u540e\u7aef\u9664\u4e86\u628a\u6570\u636e\u5b58\u5165 MySQL\uff0c\u8fd8\u8981<strong>\u81ea\u52a8\u8c03\u7528 Navidrome \u7684 API<\/strong>\uff08\u6216\u8005\u76f4\u63a5\u8bfb\u5199 Navidrome \u7684 SQLite \u6570\u636e\u5e93\uff09\u53bb\u521b\u5efa\u4e00\u4e2a\u540c\u540d\u7684 Navidrome \u8d26\u53f7\u3002<\/li>\n<li><strong>\u5bc6\u7801\u4fee\u6539<\/strong>\uff1a\u7528\u6237\u5728\u7ba1\u7406\u53f0\u4fee\u6539\u5bc6\u7801\uff0cJava \u540e\u7aef\u9700\u8981\u540c\u6b65\u4fee\u6539 Navidrome \u91cc\u7684\u5bc6\u7801\u3002<\/li>\n<li><strong>VIP \u673a\u5236\u63a7\u5236 (\u91cd\u8981)<\/strong>\uff1a<\/li>\n<\/ol>\n<ul>\n<li>\u7531\u4e8e Navidrome \u539f\u751f\u4e0d\u652f\u6301 VIP \u5230\u671f\u81ea\u52a8\u5c01\u53f7\uff0c\u4f60\u9700\u8981\u5728 Java \u540e\u7aef\u8bbe\u8ba1\u4e00\u4e2a<strong>\u5b9a\u65f6\u4efb\u52a1\uff08Cron Job\uff09<\/strong>\u3002<\/li>\n<li>\u6bcf\u6b21\u7528\u6237\u5151\u6362\u201c1\u4e2a\u6708 VIP\u201d\uff0c\u5728 MySQL \u4e2d\u8bb0\u5f55\u5176\u5230\u671f\u65f6\u95f4\u3002<\/li>\n<li>\u6bcf\u5929\u51cc\u6668 0 \u70b9\uff0cJava \u5b9a\u65f6\u4efb\u52a1\u626b\u63cf MySQL\u3002\u5982\u679c\u53d1\u73b0\u6709\u7528\u6237 VIP \u8fc7\u671f\uff0c\u5c31\u901a\u8fc7\u8c03\u7528 Navidrome API \u5c06\u8be5\u8d26\u53f7<strong>\u6302\u8d77\uff08Suspend\/Disable\uff09<\/strong>\u3002<\/li>\n<li>\u5f53\u7528\u6237\u7eed\u8d39\u540e\uff0c\u518d\u5c06\u5176\u8d26\u53f7\u6062\u590d\u3002<\/li>\n<\/ul>\n<hr \/>\n<h3>\u7b2c\u4e94\u9636\u6bb5\uff1a\u5f00\u53d1\u6f14\u8fdb\u4e0e\u90e8\u7f72\u8def\u7ebf\u56fe<\/h3>\n<p>\u4f5c\u4e3a\u9879\u76ee\u7ba1\u7406\uff0c\u6211\u5efa\u8bae\u6211\u4eec\u5c06\u9879\u76ee\u5206\u4e3a\u4e09\u4e2a\u91cc\u7a0b\u7891\uff08Milestone\uff09\u6765\u63a8\u8fdb\uff1a<\/p>\n<ul>\n<li><strong>Milestone 1\uff1aMVP\uff08\u6700\u5c0f\u53ef\u884c\u6027\u4ea7\u54c1\uff09\u642d\u5efa<\/strong>\n<ul>\n<li>\u76ee\u6807\uff1a\u5b8c\u6210 Vue3 \u524d\u7aef\u5de5\u7a0b\u5316\u62c6\u5206\uff0c\u8dd1\u901a\u5047\u6570\u636e\uff1b\u5b8c\u6210 Java \u540e\u7aef\u57fa\u7840\u6846\u67b6\u642d\u5efa\uff0c\u63a5\u901a MySQL\u3002<\/li>\n<\/ul>\n<\/li>\n<li><strong>Milestone 2\uff1a\u6838\u5fc3\u4e1a\u52a1\u95ed\u73af<\/strong>\n<ul>\n<li>\u76ee\u6807\uff1a\u8054\u8c03\u767b\u5f55\/\u6ce8\u518c\u3001\u7b7e\u5230\u5f97\u79ef\u5206\u3001\u6d88\u8017\u79ef\u5206\u751f\u6210\u9080\u8bf7\u7801\u3001\u7ba1\u7406\u5458\u67e5\u770b\u5217\u8868\u8fd9\u56db\u5927\u6838\u5fc3\u63a5\u53e3\u3002<\/li>\n<\/ul>\n<\/li>\n<li><strong>Milestone 3\uff1aNavidrome \u6df1\u5ea6\u8026\u5408<\/strong>\n<ul>\n<li>\u76ee\u6807\uff1a\u6253\u901a\u5546\u5e97\u5151\u6362\u903b\u8f91\u3002\u7f16\u5199 Java \u5b9a\u65f6\u4efb\u52a1\u548c Navidrome API \u5bf9\u63a5\u670d\u52a1\uff0c\u5b9e\u73b0\u8d26\u53f7\u771f\u6b63\u7684\u81ea\u52a8\u5f00\u901a\u3001\u5c01\u7981\u548c\u6269\u5bb9\u3002<br \/>\n<strong>\u4e0b\u4e00\u6b65\u600e\u4e48\u505a\uff1f<\/strong><br \/>\n\u5982\u679c\u4f60\u8ba4\u53ef\u8fd9\u4e2a\u89c4\u5212\uff0c\u6211\u4eec\u53ef\u4ee5\u4ece <strong>Milestone 1<\/strong> \u5f00\u59cb\u3002<br \/>\n\u4f60\u662f\u5e0c\u671b\u6211\u5148\u5e2e\u4f60 <strong>\u628a\u8fd9\u7248 HTML \u62c6\u5199\u6210 Vue 3 \u7684\u6807\u51c6\u5de5\u7a0b\u4ee3\u7801<\/strong>\uff0c\u8fd8\u662f\u5148\u5e2e\u4f60 <strong>\u5b9a\u4e49\u540e\u7aef Java \u6570\u636e\u5e93\u7684\u8868\u7ed3\u6784 (SQL Schema)<\/strong> \u5462\uff1f<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h2>Turn 10<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>\u5f88\u597d\uff0c\u6211\u662f\u4ee3\u7801\u5c0f\u767d\uff0c\u8bf7\u8ba9\u6211\u4eec\u6765\u8fdb\u884c\u7b2c\u4e00\u90e8\uff0c\u6211\u7684\u670d\u52a1\u5668\u662fdebian\uff0c\u6211\u9700\u8981\u600e\u4e48\u90e8\u7f72\u6211\u7684\u670d\u52a1\u548c\u73af\u5883\uff0c\u6211\u4f1a\u5728mnt\u6587\u4ef6\u4e0b\u5f00\u59cb\u4efb\u52a1\u3002<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u6b22\u8fce\u6765\u5230 Milestone 1\uff01\u65e2\u7136\u4f60\u81ea\u8ba4\u662f\u201c\u4ee3\u7801\u5c0f\u767d\u201d\uff0c\u90a3\u4f5c\u4e3a\u4f60\u7684\u9879\u76ee\u7ba1\u7406\uff0c\u6211\u4f1a\u628a\u6bcf\u4e00\u6b65\u7684\u6307\u4ee4\u90fd\u5199\u5f97\u660e\u660e\u767d\u767d\uff0c\u4f60\u53ea\u9700\u8981\u50cf\u642d\u79ef\u6728\u4e00\u6837\u201c\u590d\u5236\u3001\u7c98\u8d34\u3001\u56de\u8f66\u201d\u5c31\u53ef\u4ee5\u4e86\u3002<br \/>\n\u8003\u8651\u5230\u4f60\u9009\u62e9\u5728 Debian \u7684 <code>\/mnt<\/code> \u76ee\u5f55\u4e0b\u8fdb\u884c\u90e8\u7f72\uff0c\u4e3a\u4e86\u8ba9\u540e\u7eed\u7684\u7ba1\u7406\u548c\u5347\u7ea7\u6700\u65b9\u4fbf\uff0c\u5e76\u4e14\u4e0d\u628a\u670d\u52a1\u5668\u7cfb\u7edf\u641e\u4e71\uff0c\u6211\u4eec\u91c7\u7528**\u201c\u5bbf\u4e3b\u673a\u7f16\u8bd1 + Docker \u5bb9\u5668\u5316\u8fd0\u884c\u6570\u636e\u5e93\u201d**\u7684\u73b0\u4ee3\u5316\u65b9\u6848\u3002<br \/>\n\u8bf7\u4f7f\u7528 SSH \u767b\u5f55\u5230\u4f60\u7684 Debian \u670d\u52a1\u5668\uff08\u5efa\u8bae\u4f7f\u7528 <code>root<\/code> \u7528\u6237\uff09\uff0c\u7136\u540e\u6309\u7167\u4ee5\u4e0b\u56db\u4e2a\u6b65\u9aa4\u65e0\u8111\u6267\u884c\uff1a<\/p>\n<h3>\u7b2c\u4e00\u6b65\uff1a\u7cfb\u7edf\u66f4\u65b0\u4e0e\u57fa\u7840\u5de5\u5177<\/h3>\n<p>\u5728\u5b89\u88c5\u4efb\u4f55\u65b0\u4e1c\u897f\u4e4b\u524d\uff0c\u6211\u4eec\u5148\u628a\u7cfb\u7edf\u81ea\u5e26\u7684\u8f6f\u4ef6\u66f4\u65b0\u5230\u6700\u65b0\uff0c\u5e76\u5b89\u88c5\u4e00\u4e9b\u5fc5\u5907\u7684\u4e0b\u8f7d\u5de5\u5177\u3002<br \/>\n\u8bf7\u5728\u7ec8\u7aef\u6267\u884c\uff1a<\/p>\n<pre><code class=\"language-bash\">apt update &amp;&amp; apt upgrade -y\r\napt install git curl wget vim unzip -y\r\n<\/code><\/pre>\n<h3>\u7b2c\u4e8c\u6b65\uff1a\u5b89\u88c5\u5f00\u53d1\u4e0e\u7f16\u8bd1\u73af\u5883<\/h3>\n<p>\u6211\u4eec\u9700\u8981 Java \u6765\u8dd1\u540e\u7aef\uff0c\u9700\u8981 Node.js \u6765\u7f16\u8bd1\u524d\u7aef\u3002<br \/>\n<strong>1. \u5b89\u88c5 Java \u73af\u5883 (JDK 17)<\/strong><br \/>\n\u7531\u4e8e\u540e\u7aef\u8981\u6c42 JDK 17\uff0cDebian \u5b89\u88c5\u8d77\u6765\u975e\u5e38\u7b80\u5355\uff1a<\/p>\n<pre><code class=\"language-bash\">apt install openjdk-17-jdk -y\r\n<\/code><\/pre>\n<p><strong>2. \u5b89\u88c5\u524d\u7aef\u73af\u5883 (Node.js 20)<\/strong><br \/>\nVue 3 \u9700\u8981\u6bd4\u8f83\u65b0\u7684 Node.js\uff0c\u6211\u4eec\u4f7f\u7528\u5b98\u65b9\u811a\u672c\u5b89\u88c5\u7248\u672c 20\uff1a<\/p>\n<pre><code class=\"language-bash\">curl -fsSL https:\/\/deb.nodesource.com\/setup_20.x | bash -\r\napt install -y nodejs\r\n<\/code><\/pre>\n<h3>\u7b2c\u4e09\u6b65\uff1a\u90e8\u7f72\u6570\u636e\u5e93 (MySQL 8.0)<\/h3>\n<p>\u4e3a\u4e86\u4e0d\u8ba9\u6570\u636e\u5e93\u5f04\u810f\u4f60\u7684\u7cfb\u7edf\u73af\u5883\uff0c\u6700\u5b8c\u7f8e\u7684\u65b9\u5f0f\u662f\u7528 Docker \u8dd1 MySQL\u3002\u8fd9\u4e0d\u4ec5\u65b9\u4fbf\u540e\u7eed\u5907\u4efd\uff0c\u8fd8\u80fd\u4e00\u952e\u91cd\u542f\u3002<br \/>\n<strong>1. \u4e00\u952e\u5b89\u88c5 Docker \u4e0e Docker Compose<\/strong><br \/>\n\u5982\u679c\u4f60\u7684\u670d\u52a1\u5668\u8fd8\u6ca1\u88c5 Docker\uff0c\u8fd0\u884c\u8fd9\u4e2a\u5b98\u65b9\u7684\u4e00\u952e\u5b89\u88c5\u811a\u672c\uff1a<\/p>\n<pre><code class=\"language-bash\">curl -fsSL https:\/\/get.docker.com | bash\r\nsystemctl enable --now docker\r\n<\/code><\/pre>\n<p><strong>2. \u4f7f\u7528 Compose \u542f\u52a8 MySQL<\/strong><br \/>\n\u6211\u4eec\u5728 <code>\/mnt<\/code> \u4e0b\u5efa\u7acb\u4e00\u4e2a\u4e13\u95e8\u653e\u57fa\u7840\u8bbe\u65bd\u7684\u6587\u4ef6\u5939\uff0c\u5e76\u542f\u52a8\u6570\u636e\u5e93\uff1a<\/p>\n<pre><code class=\"language-bash\">mkdir -p \/mnt\/navidrome-system\/infrastructure\r\ncd \/mnt\/navidrome-system\/infrastructure\r\n<\/code><\/pre>\n<p>\u4f7f\u7528 <code>vim docker-compose.yml<\/code> \u547d\u4ee4\u65b0\u5efa\u4e00\u4e2a\u6587\u4ef6\uff0c\u5e76\u628a\u4e0b\u9762\u7684\u5185\u5bb9\u7c98\u8d34\u8fdb\u53bb\uff08\u6ce8\u610f\uff1a\u8fd9\u91cc\u6211\u9ed8\u8ba4\u628a\u6570\u636e\u5e93\u5bc6\u7801\u8bbe\u7f6e\u6210\u4e86 <code>navidrome_pwd_123<\/code>\uff0c\u4f60\u53ef\u4ee5\u81ea\u5df1\u6539\uff09\uff1a<\/p>\n<pre><code class=\"language-yaml\">version: '3.8'\r\nservices:\r\n  mysql:\r\n    image: mysql:8.0\r\n    container_name: navidrome-mysql\r\n    restart: always\r\n    environment:\r\n      MYSQL_ROOT_PASSWORD: navidrome_pwd_123\r\n      MYSQL_DATABASE: nevidrome-server\r\n    ports:\r\n      - \"3306:3306\"\r\n    volumes:\r\n      - .\/mysql-data:\/var\/lib\/mysql\r\n    command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci\r\n<\/code><\/pre>\n<p>\u4fdd\u5b58\u9000\u51fa\u540e\uff0c\u8fd0\u884c\u4ee5\u4e0b\u547d\u4ee4\u542f\u52a8\u6570\u636e\u5e93\uff1a<\/p>\n<pre><code class=\"language-bash\">docker compose up -d\r\n<\/code><\/pre>\n<h3>\u7b2c\u56db\u6b65\uff1a\u521b\u5efa\u9879\u76ee\u9aa8\u67b6<\/h3>\n<p>\u6700\u540e\uff0c\u6211\u4eec\u5728 <code>\/mnt\/navidrome-system<\/code> \u4e0b\u628a\u524d\u540e\u7aef\u7684\u6587\u4ef6\u5939\u5efa\u597d\uff0c\u65b9\u4fbf\u4e00\u4f1a\u513f\u5199\u4ee3\u7801\u3002<\/p>\n<pre><code class=\"language-bash\">cd \/mnt\/navidrome-system\r\nmkdir frontend backend\r\n<\/code><\/pre>\n<hr \/>\n<h3>\ud83d\udfe2 \u68c0\u67e5\u4f5c\u4e1a\u65f6\u95f4<\/h3>\n<p>\u5230\u8fd9\u91cc\uff0c\u4f60\u7684\u670d\u52a1\u5668\u5c31\u5df2\u7ecf\u62e5\u6709\u4e86\u5b8c\u6574\u7684\u73b0\u4ee3\u5316\u524d\u540e\u7aef\u5206\u79bb\u5f00\u53d1\u73af\u5883\uff01\u4f60\u53ef\u4ee5\u8fd0\u884c\u4ee5\u4e0b\u4e09\u6761\u547d\u4ee4\u6765\u9a8c\u8bc1\u4e00\u4e0b\u662f\u5426\u90fd\u5b89\u88c5\u6210\u529f\u4e86\uff1a<\/p>\n<pre><code class=\"language-bash\">java -version\r\nnode -v\r\ndocker ps\r\n<\/code><\/pre>\n<p>\uff08\u5982\u679c\u5206\u522b\u5f39\u51fa\u4e86\u7248\u672c\u53f7\uff0c\u4e14\u770b\u5230 mysql \u6b63\u5728\u8fd0\u884c\uff0c\u5c31\u8bf4\u660e\u5b8c\u7f8e\u8fc7\u5173\uff01\uff09<br \/>\n\u73af\u5883\u642d\u5efa\u5b8c\u6bd5\u540e\uff0c\u6211\u4eec\u5c31\u6709\u4e86\u65bd\u5c55\u62f3\u811a\u7684\u821e\u53f0\u3002\u63a5\u4e0b\u6765\uff0c\u4f60\u662f\u60f3\u5148\u8fdb\u5165 <code>frontend<\/code> \u6587\u4ef6\u5939<strong>\u521d\u59cb\u5316 Vue 3 \u7684\u524d\u7aef\u9879\u76ee\u5e76\u690d\u5165\u4f60\u90a3\u5957 UI<\/strong>\uff0c\u8fd8\u662f\u5148\u8fdb\u5165 <code>backend<\/code> \u6587\u4ef6\u5939<strong>\u628a Java \u540e\u7aef\u7684\u9879\u76ee\u7ed3\u6784\u548c\u6570\u636e\u5e93\u8868\u5efa\u7acb\u8d77\u6765<\/strong>\uff1f<\/p>\n<h2>Turn 11<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>\u5f88\u597d\u5df2\u7ecf\u5f00\u59cb\u4e86\u3002mysql \u6211\u8bbe\u7f6e\u7684\u662f3535\u7aef\u53e3\uff0c\u540e\u9762\u522b\u5fd8\u8bb0\u4e86\u3002<br \/>\n\u73b0\u5728\u6839\u636e\u4f60\u89c9\u5f97\u5982\u4f55\u7a33\u56fa\u4e0b\u9762\u7684\u9879\u76ee\u6765\u5f00\u59cb<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u597d\u7684\uff0c\u660e\u767d\uff01MySQL \u7aef\u53e3\u6211\u4eec\u8bb0\u4e0b\u662f <strong>3535<\/strong>\u3002<br \/>\n\u65e2\u7136\u4f60\u5e0c\u671b\u4ee5\u6700\u7a33\u56fa\u3001\u6700\u65b9\u4fbf\u540e\u7eed\u6269\u5c55\u548c\u7ef4\u62a4\u7684\u65b9\u5f0f\u6765\u642d\u5efa\u8fd9\u4e2a\u9879\u76ee\uff0c\u6211\u4eec\u5c31\u4ece <strong>Milestone 1\uff1a\u524d\u540e\u7aef\u5206\u79bb\u7684\u57fa\u7840\u9aa8\u67b6\u642d\u5efa<\/strong> \u5f00\u59cb\u3002<br \/>\n\u4e3a\u4e86\u7a33\u56fa\uff0c\u6211\u4eec\u9996\u5148\u8981\u628a\u540e\u7aef\u7684 Java \u9879\u76ee\u7ed3\u6784\u642d\u5efa\u597d\uff0c\u5e76\u8ba9\u5b83\u80fd\u591f\u6210\u529f\u8fde\u63a5\u5230\u4f60\u7684 MySQL \u6570\u636e\u5e93\u3002\u8fd9\u662f\u6574\u4e2a\u7cfb\u7edf\u7684\u57fa\u77f3\u3002<br \/>\n\u63a5\u4e0b\u6765\uff0c\u6211\u4eec\u5c06\u5206\u4e24\u6b65\u8d70\uff1a\u5148\u5efa\u8868\uff08\u6570\u636e\u5e93 Schema\uff09\uff0c\u518d\u5efa\u540e\u7aef\u9879\u76ee\u9aa8\u67b6\u3002<\/p>\n<hr \/>\n<h3>\u7b2c\u4e00\u6b65\uff1a\u521d\u59cb\u5316\u6570\u636e\u5e93\u8868\u7ed3\u6784 (MySQL Schema)<\/h3>\n<p>\u6211\u4eec\u9700\u8981\u5728\u4f60\u7684 <code>nevidrome-server<\/code> \u6570\u636e\u5e93\u4e2d\u521b\u5efa\u51e0\u5f20\u6838\u5fc3\u8868\uff0c\u7528\u6765\u652f\u6491\u79ef\u5206\u3001\u7b7e\u5230\u3001\u9080\u8bf7\u7801\u7b49\u4e1a\u52a1\u3002<br \/>\n\u8bf7\u901a\u8fc7 SSH \u767b\u5f55\u4f60\u7684\u670d\u52a1\u5668\uff0c\u8fdb\u5165 MySQL \u5bb9\u5668\u5185\u90e8\u6267\u884c\u5efa\u8868\u64cd\u4f5c\uff1a<br \/>\n<strong>1. \u8fdb\u5165 MySQL \u5bb9\u5668\uff1a<\/strong><\/p>\n<pre><code class=\"language-bash\">docker exec -it navidrome-mysql mysql -u root -p\r\n<\/code><\/pre>\n<p><em>(\u63d0\u793a\uff1a\u8f93\u5165\u4f60\u521a\u624d\u8bbe\u7f6e\u7684\u5bc6\u7801 navidrome_pwd_123\uff0c\u8f93\u5165\u65f6\u5c4f\u5e55\u4e0d\u663e\u793a\u662f\u6b63\u5e38\u7684\uff0c\u6309\u56de\u8f66\u5373\u53ef\u3002)<\/em><br \/>\n<strong>2. \u521b\u5efa\u6570\u636e\u5e93\u5e76\u9009\u62e9\u5b83\uff1a<\/strong><\/p>\n<pre><code class=\"language-sql\">CREATE DATABASE IF NOT EXISTS `nevidrome-server` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\r\nUSE `nevidrome-server`;\r\n<\/code><\/pre>\n<p><strong>3. \u6267\u884c\u4ee5\u4e0b SQL \u5efa\u8868\u8bed\u53e5\uff08\u76f4\u63a5\u590d\u5236\u7c98\u8d34\u5e76\u56de\u8f66\uff09\uff1a<\/strong><\/p>\n<pre><code class=\"language-sql\">-- 1. \u7528\u6237\u8868 (User)\r\nCREATE TABLE `sys_user` (\r\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\r\n  `email` varchar(100) NOT NULL COMMENT '\u90ae\u7bb1\u8d26\u53f7',\r\n  `password` varchar(100) NOT NULL COMMENT '\u5bc6\u7801(\u52a0\u5bc6)',\r\n  `role` varchar(20) DEFAULT 'user' COMMENT '\u89d2\u8272: admin\/user',\r\n  `points` int(11) DEFAULT '0' COMMENT '\u5f53\u524d\u79ef\u5206',\r\n  `status` varchar(20) DEFAULT 'active' COMMENT '\u72b6\u6001: active\/disabled',\r\n  `navidrome_username` varchar(100) DEFAULT NULL COMMENT '\u5173\u8054\u7684Navidrome\u8d26\u53f7',\r\n  `created_at` datetime DEFAULT CURRENT_TIMESTAMP,\r\n  `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\r\n  PRIMARY KEY (`id`),\r\n  UNIQUE KEY `uk_email` (`email`)\r\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='\u7528\u6237\u8868';\r\n\r\n-- 2. \u7b7e\u5230\u8bb0\u5f55\u8868 (Checkin)\r\nCREATE TABLE `sys_checkin` (\r\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\r\n  `user_id` bigint(20) NOT NULL COMMENT '\u7528\u6237ID',\r\n  `checkin_date` date NOT NULL COMMENT '\u7b7e\u5230\u65e5\u671f(YYYY-MM-DD)',\r\n  `points_earned` int(11) NOT NULL COMMENT '\u83b7\u5f97\u79ef\u5206',\r\n  `created_at` datetime DEFAULT CURRENT_TIMESTAMP,\r\n  PRIMARY KEY (`id`),\r\n  UNIQUE KEY `uk_user_date` (`user_id`, `checkin_date`) -- \u4fdd\u8bc1\u6bcf\u5929\u53ea\u80fd\u7b7e\u5230\u4e00\u6b21\r\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='\u7b7e\u5230\u8bb0\u5f55\u8868';\r\n\r\n-- 3. \u9080\u8bf7\u7801\u8868 (Invite Code)\r\nCREATE TABLE `sys_invite_code` (\r\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\r\n  `code` varchar(50) NOT NULL COMMENT '\u9080\u8bf7\u7801',\r\n  `creator_id` bigint(20) NOT NULL COMMENT '\u751f\u6210\u6b64\u7801\u7684\u7528\u6237ID',\r\n  `used_by_id` bigint(20) DEFAULT NULL COMMENT '\u4f7f\u7528\u8005ID',\r\n  `status` varchar(20) DEFAULT 'valid' COMMENT '\u72b6\u6001: valid\/used\/expired',\r\n  `expire_time` datetime NOT NULL COMMENT '\u8fc7\u671f\u65f6\u95f4',\r\n  `created_at` datetime DEFAULT CURRENT_TIMESTAMP,\r\n  PRIMARY KEY (`id`),\r\n  UNIQUE KEY `uk_code` (`code`)\r\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='\u9080\u8bf7\u7801\u8868';\r\n\r\n-- 4. \u79ef\u5206\u53d8\u52a8\u660e\u7ec6\u8868 (Point Log - \u7528\u4e8e\u8ffd\u6eaf\u548c\u5ba1\u8ba1)\r\nCREATE TABLE `sys_point_log` (\r\n  `id` bigint(20) NOT NULL AUTO_INCREMENT,\r\n  `user_id` bigint(20) NOT NULL COMMENT '\u7528\u6237ID',\r\n  `action` varchar(50) NOT NULL COMMENT '\u53d8\u52a8\u7c7b\u578b(\u5982: checkin, buy_invite, redeem_vip)',\r\n  `points_change` int(11) NOT NULL COMMENT '\u53d8\u52a8\u6570\u91cf(\u6b63\u8d1f\u503c)',\r\n  `description` varchar(255) DEFAULT NULL COMMENT '\u53d8\u52a8\u63cf\u8ff0',\r\n  `created_at` datetime DEFAULT CURRENT_TIMESTAMP,\r\n  PRIMARY KEY (`id`)\r\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='\u79ef\u5206\u53d8\u52a8\u65e5\u5fd7\u8868';\r\n<\/code><\/pre>\n<p><strong>4. \u63d2\u5165\u4e00\u4e2a\u521d\u59cb\u7ba1\u7406\u5458\u8d26\u53f7\uff1a<\/strong><\/p>\n<pre><code class=\"language-sql\">INSERT INTO `sys_user` (`email`, `password`, `role`, `points`) VALUES ('admin@crabpt.vip', '\u8fd9\u91cc\u7a0d\u540e\u586b\u52a0\u5bc6\u5bc6\u7801', 'admin', 9999);\r\n<\/code><\/pre>\n<p><strong>5. \u9000\u51fa MySQL\uff1a<\/strong><\/p>\n<pre><code class=\"language-sql\">exit;\r\n<\/code><\/pre>\n<hr \/>\n<h3>\u7b2c\u4e8c\u6b65\uff1a\u642d\u5efa Spring Boot \u540e\u7aef\u9aa8\u67b6<\/h3>\n<p>\u4e3a\u4e86\u5feb\u901f\u642d\u5efa\uff0c\u6211\u4eec\u4f7f\u7528 Spring Initializr \u751f\u6210\u6807\u51c6\u7684\u540e\u7aef\u9879\u76ee\u7ed3\u6784\u3002<br \/>\n<strong>1. \u8fdb\u5165\u4f60\u521a\u624d\u521b\u5efa\u7684 backend \u76ee\u5f55\uff1a<\/strong><\/p>\n<pre><code class=\"language-bash\">cd \/mnt\/navidrome-system\/backend\r\n<\/code><\/pre>\n<p><strong>2. \u4f7f\u7528 curl \u547d\u4ee4\u76f4\u63a5\u4e0b\u8f7d\u521d\u59cb\u5316\u597d\u7684 Spring Boot \u57fa\u7840\u9879\u76ee\uff1a<\/strong><br \/>\n\uff08\u8fd9\u4f1a\u81ea\u52a8\u5305\u542b Web\u3001MySQL\u3001MyBatis-Plus \u548c Validation \u7684\u57fa\u7840\u4f9d\u8d56\uff09<\/p>\n<pre><code class=\"language-bash\">curl -G https:\/\/start.spring.io\/starter.zip \\\r\n  -d dependencies=web,mysql,lombok,validation \\\r\n  -d javaVersion=17 \\\r\n  -d bootVersion=3.2.4 \\\r\n  -d groupId=vip.crabpt.navidrome \\\r\n  -d artifactId=navidrome-server \\\r\n  -d name=navidrome-server \\\r\n  -d packageName=vip.crabpt.navidrome \\\r\n  -o navidrome-server.zip\r\n<\/code><\/pre>\n<p><strong>3. \u89e3\u538b\u5e76\u8fdb\u5165\u9879\u76ee\uff1a<\/strong><\/p>\n<pre><code class=\"language-bash\">unzip navidrome-server.zip\r\ncd navidrome-server\r\n<\/code><\/pre>\n<p><strong>4. \u914d\u7f6e application.yml (\u6700\u5173\u952e\u7684\u4e00\u6b65)<\/strong><br \/>\n\u6211\u4eec\u9700\u8981\u628a\u9ed8\u8ba4\u7684 <code>application.properties<\/code> \u5220\u6389\uff0c\u6362\u6210\u66f4\u6613\u8bfb\u7684 <code>application.yml<\/code>\uff0c\u5e76\u914d\u4e0a\u4f60\u7684\u6570\u636e\u5e93\u8fde\u63a5\uff08\u8bb0\u5f97\u4f60\u8bf4\u7684 <strong>3535<\/strong> \u7aef\u53e3\uff09\u3002<\/p>\n<pre><code class=\"language-bash\">rm src\/main\/resources\/application.properties\r\n<\/code><\/pre>\n<p>\u4f7f\u7528 vim \u521b\u5efa\u65b0\u914d\u7f6e\uff1a<\/p>\n<pre><code class=\"language-bash\">vim src\/main\/resources\/application.yml\r\n<\/code><\/pre>\n<p>\u7c98\u8d34\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/p>\n<pre><code class=\"language-yaml\">server:\r\n  port: 8001\r\n  servlet:\r\n    context-path: \/v1\r\n\r\nspring:\r\n  datasource:\r\n    driver-class-name: com.mysql.cj.jdbc.Driver\r\n    # \u6ce8\u610f\u8fd9\u91cc\u7684\u7aef\u53e3\u6539\u6210\u4e86 3535\r\n    url: jdbc:mysql:\/\/127.0.0.1:3535\/nevidrome-server?autoReconnect=true&amp;useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=Asia\/Shanghai\r\n    username: root\r\n    password: navidrome_pwd_123\r\n\r\nmybatis-plus:\r\n  mapper-locations: classpath*:\/mapper\/**\/*.xml\r\n  global-config:\r\n    db-config:\r\n      id-type: auto\r\n      logic-delete-field: deleted\r\n      logic-delete-value: 1\r\n      logic-not-delete-value: 0\r\n  configuration:\r\n    map-underscore-to-camel-case: true # \u5f00\u542f\u9a7c\u5cf0\u547d\u540d\u81ea\u52a8\u6620\u5c04\r\n    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # \u6253\u5370 SQL \u65e5\u5fd7\u65b9\u4fbf\u672c\u5730\u8c03\u8bd5\r\n<\/code><\/pre>\n<hr \/>\n<h3>\ud83d\udfe2 \u9636\u6bb5\u6027\u68c0\u67e5<\/h3>\n<p>\u5230\u8fd9\u91cc\uff0c\u4f60\u7684\u6570\u636e\u5e93\u8868\u5df2\u7ecf\u5efa\u597d\uff0cSpring Boot \u540e\u7aef\u7684\u57fa\u7840\u67b6\u5b50\u548c\u8fde\u63a5\u914d\u7f6e\u4e5f\u5f04\u597d\u4e86\u3002<br \/>\n\u4e3a\u4e86\u786e\u8ba4\u4e00\u5207\u987a\u5229\uff0c\u4f60\u53ef\u4ee5\u5728 <code>backend\/navidrome-server<\/code> \u76ee\u5f55\u4e0b\u5c1d\u8bd5\u8fd0\u884c\u4e00\u6b21\u7f16\u8bd1\u6253\u5305\uff1a<\/p>\n<pre><code class=\"language-bash\">.\/mvnw clean package -DskipTests\r\n<\/code><\/pre>\n<p><em>(\u5982\u679c\u662f\u7b2c\u4e00\u6b21\u8fd0\u884c\uff0c\u5b83\u4f1a\u4e0b\u8f7d\u4e00\u4e9b\u4f9d\u8d56\u5305\uff0c\u53ef\u80fd\u9700\u8981\u51e0\u5206\u949f\u3002)<\/em><br \/>\n\u5982\u679c\u6700\u540e\u663e\u793a <strong>BUILD SUCCESS<\/strong>\uff0c\u90a3\u4e48\u606d\u559c\u4f60\uff01\u540e\u7aef\u7684\u57fa\u7840\u5de5\u7a0b\u5316\u5df2\u7ecf\u975e\u5e38\u7a33\u56fa\u4e86\u3002<br \/>\n<strong>\u63a5\u4e0b\u6765\u7684\u89c4\u5212\u5efa\u8bae\uff1a<\/strong><br \/>\n\u4f60\u53ef\u4ee5\u51b3\u5b9a\u6211\u4eec\u4e0b\u4e00\u6b65\u7684\u8d70\u5411\uff1a<\/p>\n<ul>\n<li><strong>\u9009\u9879 A (\u7ee7\u7eed\u6df1\u8015\u540e\u7aef)<\/strong>\uff1a\u6211\u4eec\u5728 Java \u91cc\u628a MyBatis-Plus \u7684\u4ee3\u7801\u751f\u6210\u5668\u8dd1\u8d77\u6765\uff0c\u81ea\u52a8\u751f\u6210 <code>User<\/code>\u3001<code>Checkin<\/code> \u7b49\u5b9e\u4f53\u7c7b\u548c\u57fa\u7840\u7684\u589e\u5220\u6539\u67e5\u63a5\u53e3\uff08Controller\/Service\/Mapper\uff09\u3002<\/li>\n<li><strong>\u9009\u9879 B (\u8f6c\u6218\u524d\u7aef Vue3)<\/strong>\uff1a\u53bb <code>frontend<\/code> \u76ee\u5f55\u628a Vue3 + Vite \u7684\u73af\u5883\u642d\u8d77\u6765\uff0c\u5e76\u628a\u4f60\u521a\u624d\u7684 HTML \u62c6\u5206\u6210\u7ec4\u4ef6\u3002<br \/>\n\u4f60\u5e0c\u671b\u5148\u8fdb\u884c\u54ea\u4e00\u4e2a\uff1f<\/li>\n<\/ul>\n<h2>Turn 12<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<blockquote>\n<ol start=\"2\">\n<li>\u4f7f\u7528 curl \u547d\u4ee4\u76f4\u63a5\u4e0b\u8f7d\u521d\u59cb\u5316\u597d\u7684 Spring Boot \u57fa\u7840\u9879\u76ee\uff1a<br \/>\n\uff08\u8fd9\u4f1a\u81ea\u52a8\u5305\u542b Web\u3001MySQL\u3001MyBatis-Plus \u548c Validation \u7684\u57fa\u7840\u4f9d\u8d56\uff09Bashcurl -G https:\/\/start.spring.io\/starter.zip<br \/>\n-d dependencies=web,mysql,lombok,validation<br \/>\n-d javaVersion=17<br \/>\n-d bootVersion=3.2.4<br \/>\n-d groupId=vip.crabpt.navidrome<br \/>\n-d artifactId=navidrome-server<br \/>\n-d name=navidrome-server<br \/>\n-d packageName=vip.crabpt.navidrome<br \/>\n-o navidrome-server.zip<br \/>\n\u5728\u8fd9\u5757\uff1aroot@192-227-164-42-host:\/mnt\/navidrome-system\/backend# curl -G https:\/\/start.spring.io\/starter.zip<br \/>\n-d dependencies=web,mysql,lombok,validation<br \/>\n-d javaVersion=17<br \/>\n-d bootVersion=3.2.4<br \/>\n-d groupId=vip.crabpt.navidrome<br \/>\n-d artifactId=navidrome-server<br \/>\n-d name=navidrome-server<br \/>\n-d packageName=vip.crabpt.navidrome<br \/>\n-o navidrome-server.zip<br \/>\n% Total % Received % Xferd Average Speed Time Time Time Current<br \/>\nDload Upload Total Spent Left Speed<br \/>\n100 189 0 189 0 0 831 0 &#8211;:&#8211;:&#8211; &#8211;:&#8211;:&#8211; &#8211;:&#8211;:&#8211; 836<br \/>\nroot@192-227-164-42-host:\/mnt\/navidrome-system\/backend# unzip navidrome-server.zip<br \/>\nArchive: navidrome-server.zip<br \/>\nEnd-of-central-directory signature not found. Either this file is not<br \/>\na zipfile, or it constitutes one disk of a multi-part archive. In the<br \/>\nlatter case the central directory and zipfile comment will be found on<br \/>\nthe last disk(s) of this archive.<br \/>\nunzip: cannot find zipfile directory in one of navidrome-server.zip or<br \/>\nnavidrome-server.zip.zip, and cannot find navidrome-server.zip.ZIP, period.<\/li>\n<\/ol>\n<\/blockquote>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u9047\u5230\u8fd9\u79cd\u62a5\u9519\u4e0d\u7528\u614c\uff0c\u8fd9\u662f\u6211\u4eec\u5728\u7528\u547d\u4ee4\u884c\u4e0b\u8f7d\u65f6\u975e\u5e38\u5e38\u89c1\u7684\u4e00\u4e2a\u5c0f\u63d2\u66f2\u3002<br \/>\n\u4f60\u6ce8\u610f\u770b\u8fd9\u4e00\u884c\u6570\u636e\uff1a<code>100 189 0 189<\/code>\uff0c\u8fd9\u8bf4\u660e\u4e0b\u8f7d\u4e0b\u6765\u7684\u6587\u4ef6\u53ea\u6709 <strong>189 bytes<\/strong>\uff08\u6b63\u5e38\u7684\u4e00\u4e2a\u9879\u76ee\u5305\u81f3\u5c11\u6709\u51e0\u5341 KB\uff09\u3002\u8fd9\u5176\u5b9e\u662f\u56e0\u4e3a\u6211\u4eec\u586b\u5199\u7684 Spring Boot \u7248\u672c\u53f7 <code>3.2.4<\/code> \u592a\u5177\u4f53\u4e86\uff0c\u800c Spring \u5b98\u65b9\u7f51\u7ad9\uff08start.spring.io\uff09\u66f4\u65b0\u5f88\u5feb\uff0c\u65e7\u7684\u8865\u4e01\u7248\u672c\u4f1a\u88ab\u4e0b\u67b6\uff0c\u6240\u4ee5\u5b98\u65b9\u8fd4\u56de\u4e86\u4e00\u6bb5\u201c\u7248\u672c\u627e\u4e0d\u5230\u201d\u7684\u9519\u8bef\u63d0\u793a\uff0c\u7ed3\u679c\u88ab\u6211\u4eec\u5f3a\u884c\u5b58\u6210\u4e86 <code>.zip<\/code> \u6587\u4ef6\uff0c\u81ea\u7136\u5c31\u89e3\u538b\u5931\u8d25\u4e86\u3002<br \/>\n<strong>\u89e3\u51b3\u529e\u6cd5\u975e\u5e38\u7b80\u5355\uff1a\u6211\u4eec\u5220\u6389\u8fd9\u4e2a\u574f\u6389\u7684\u5305\uff0c\u7136\u540e\u5728\u547d\u4ee4\u4e2d\u53bb\u6389\u5177\u4f53\u7248\u672c\u53f7\uff08\u8ba9\u5b98\u65b9\u81ea\u52a8\u7ed9\u6211\u4eec\u5206\u914d\u6700\u65b0\u7684\u7a33\u5b9a 3.x \u7248\u672c\uff09\u91cd\u65b0\u4e0b\u8f7d\u3002<\/strong><br \/>\n\u8bf7\u4f9d\u6b21\u590d\u5236\u5e76\u6267\u884c\u4ee5\u4e0b\u547d\u4ee4\uff1a<\/p>\n<h3>1. \u5220\u6389\u521a\u624d\u90a3\u4e2a\u574f\u6389\u7684\u538b\u7f29\u5305\uff1a<\/h3>\n<pre><code class=\"language-bash\">rm navidrome-server.zip\r\n<\/code><\/pre>\n<h3>2. \u7528\u65b0\u7684\u547d\u4ee4\u91cd\u65b0\u4e0b\u8f7d\uff08\u53bb\u6389\u4e86 bootVersion\uff09\uff1a<\/h3>\n<pre><code class=\"language-bash\">curl -G https:\/\/start.spring.io\/starter.zip \\\r\n  -d dependencies=web,mysql,lombok,validation \\\r\n  -d javaVersion=17 \\\r\n  -d groupId=vip.crabpt.navidrome \\\r\n  -d artifactId=navidrome-server \\\r\n  -d name=navidrome-server \\\r\n  -d packageName=vip.crabpt.navidrome \\\r\n  -o navidrome-server.zip\r\n<\/code><\/pre>\n<h3>3. \u518d\u6b21\u5c1d\u8bd5\u89e3\u538b\u5e76\u8fdb\u5165\u76ee\u5f55\uff1a<\/h3>\n<pre><code class=\"language-bash\">unzip navidrome-server.zip\r\ncd navidrome-server\r\n<\/code><\/pre>\n<p>\u8fd9\u6b21\u89e3\u538b\u5e94\u8be5\u5c31\u4f1a\u5237\u5237\u5237\u5730\u51fa\u73b0\u4e00\u5806\u6587\u4ef6\u4e86\uff08\u6bd4\u5982 <code>pom.xml<\/code>, <code>src\/main\/java...<\/code> \u7b49\u7b49\uff09\u3002<br \/>\n\u89e3\u538b\u6210\u529f\u5e76\u8fdb\u5165 <code>navidrome-server<\/code> \u76ee\u5f55\u540e\uff0c\u4f60\u5c31\u53ef\u4ee5\u63a5\u7740\u521a\u624d\u7684\u6b65\u9aa4\uff0c\u628a <code>src\/main\/resources\/application.properties<\/code> \u5220\u6389\uff0c\u6362\u6210\u914d\u7f6e\u4e86\u4f60 <code>3535<\/code> \u7aef\u53e3\u7684 <code>application.yml<\/code> \u6587\u4ef6\u4e86\uff1a<\/p>\n<pre><code class=\"language-bash\"># \u5220\u9664\u65e7\u914d\u7f6e\r\nrm src\/main\/resources\/application.properties\r\n\r\n# \u65b0\u5efa\u5e76\u7f16\u8f91 yml \u914d\u7f6e\r\nvim src\/main\/resources\/application.yml\r\n<\/code><\/pre>\n<p><em>(yml \u7684\u5185\u5bb9\u4f60\u7ffb\u4e00\u4e0b\u4e0a\u4e00\u6761\u5bf9\u8bdd\u7684\u6a21\u677f\u76f4\u63a5\u7c98\u8d34\u8fdb\u53bb\u4fdd\u5b58\u5373\u53ef\u3002)<\/em><br \/>\n\u641e\u5b9a\u4e4b\u540e\u544a\u8bc9\u6211\uff0c\u6211\u4eec\u7ee7\u7eed\u5f80\u4e0b\u8d70\uff01<\/p>\n<h2>Turn 13<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<blockquote>\n<ol start=\"3\">\n<li>\u518d\u6b21\u5c1d\u8bd5\u89e3\u538b\u5e76\u8fdb\u5165\u76ee\u5f55\uff1aBashunzip navidrome-server.zip<br \/>\ncd navidrome-server<br \/>\nh\u55ef\u597d\uff0c\u4f46\u662f\u5728\u8fd9\u4e2a\u4f4d\u7f6e\uff0c\u6211\u5df2\u7ecf\u89e3\u538b\u4e86\uff0c\u4f46\u662f\u76f4\u63a5\u662f\u51fa\u73b0\u7684\u90a3\u4e9b\u76ee\u5f55<br \/>\nroot@192-227-164-42-host:\/mnt\/navidrome-system\/backend# unzip navidrome-server.zip<br \/>\nArchive: navidrome-server.zip<br \/>\ninflating: build.gradle<br \/>\ninflating: gradlew<br \/>\ncreating: gradle\/<br \/>\ncreating: gradle\/wrapper\/<br \/>\ninflating: gradle\/wrapper\/gradle-wrapper.properties<br \/>\ninflating: gradle\/wrapper\/gradle-wrapper.jar<br \/>\ninflating: gradlew.bat<br \/>\ninflating: .gitattributes<br \/>\ninflating: .gitignore<br \/>\ncreating: src\/<br \/>\ncreating: src\/main\/<br \/>\ncreating: src\/main\/java\/<br \/>\ncreating: src\/main\/java\/vip\/<br \/>\ncreating: src\/main\/java\/vip\/crabpt\/<br \/>\ncreating: src\/main\/java\/vip\/crabpt\/navidrome\/<br \/>\ninflating: src\/main\/java\/vip\/crabpt\/navidrome\/NavidromeServerApplication.java<br \/>\ncreating: src\/main\/resources\/<br \/>\ncreating: src\/main\/resources\/templates\/<br \/>\ninflating: src\/main\/resources\/application.properties<br \/>\ncreating: src\/main\/resources\/static\/<br \/>\ncreating: src\/test\/<br \/>\ncreating: src\/test\/java\/<br \/>\ncreating: src\/test\/java\/vip\/<br \/>\ncreating: src\/test\/java\/vip\/crabpt\/<br \/>\ncreating: src\/test\/java\/vip\/crabpt\/navidrome\/<br \/>\ninflating: src\/test\/java\/vip\/crabpt\/navidrome\/NavidromeServerApplicationTests.java<br \/>\ninflating: HELP.md<br \/>\ninflating: settings.gradle<br \/>\nroot@192-227-164-42-host:\/mnt\/navidrome-system\/backend# cd navidrome-server<br \/>\n-bash: cd: navidrome-server: No such file or directory<\/li>\n<\/ol>\n<\/blockquote>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u554a\uff0c\u6211\u660e\u767d\u4e86\uff01\u8fd9\u662f\u4e00\u4e2a\u975e\u5e38\u5178\u578b\u7684\u7ec6\u8282\u95ee\u9898\u3002<br \/>\n\u5f53\u4f60\u5728\u547d\u4ee4\u884c\u901a\u8fc7 <code>curl<\/code> \u4ece start.spring.io \u4e0b\u8f7d\u9879\u76ee\u65f6\uff0c\u5982\u679c\u4f60\u6ca1\u6709\u7279\u522b\u6307\u5b9a <code>type<\/code>\uff08\u6bd4\u5982 <code>type=maven-project<\/code> \u6216 <code>type=gradle-project<\/code>\uff0c\u6216\u8005\u50cf\u6211\u4eec\u6628\u5929\u6ca1\u6307\u5b9a\u5b83\u9ed8\u8ba4\u7ed9\u4e86 Gradle\uff09\uff0c\u5e76\u4e14\u6700\u5173\u952e\u7684\u662f\uff1a<strong>\u5b83\u89e3\u538b\u51fa\u6765\u7684\u65f6\u5019\uff0c\u6ca1\u6709\u81ea\u52a8\u5305\u88f9\u5728\u4e00\u4e2a\u540d\u4e3a navidrome-server \u7684\u603b\u6587\u4ef6\u5939\u91cc\uff0c\u800c\u662f\u76f4\u63a5\u628a\u91cc\u9762\u7684 src\u3001build.gradle \u7b49\u6587\u4ef6\u6492\u5728\u4e86\u5f53\u524d\u7684 backend \u76ee\u5f55\u4e0b\u3002<\/strong><br \/>\n\u6240\u4ee5\u4f60\u6267\u884c <code>cd navidrome-server<\/code> \u4f1a\u63d0\u793a\u627e\u4e0d\u5230\u6587\u4ef6\u5939\uff0c\u56e0\u4e3a\u4f60\u5df2\u7ecf\u5728\u8fd9\u4e2a\u201c\u9879\u76ee\u6839\u76ee\u5f55\u201d\uff08\u4e5f\u5c31\u662f <code>backend<\/code> \u76ee\u5f55\uff09\u91cc\u9762\u4e86\u3002<br \/>\n\u8fd9\u4f1a\u5bfc\u81f4\u6211\u4eec\u7684\u76ee\u5f55\u7ed3\u6784\u6709\u70b9\u4e71\u3002\u4e3a\u4e86\u4fdd\u6301\u9879\u76ee\u7684\u6574\u6d01\uff08\u6211\u4eec\u9700\u8981\u4e00\u4e2a\u5e72\u51c0\u7684 <code>backend<\/code> \u76ee\u5f55\u6765\u88c5\u6574\u4e2a Java \u9879\u76ee\uff09\uff0c\u8ba9\u6211\u4eec\u91cd\u65b0\u6574\u7406\u4e00\u4e0b\uff0c\u987a\u4fbf\u628a\u6784\u5efa\u5de5\u5177\u660e\u786e\u6307\u5b9a\u4e3a\u4f60\u5728\u90e8\u7f72\u6587\u6863\u91cc\u63d0\u5230\u7684 <strong>Maven<\/strong>\uff08\u8fd9\u5bf9\u4e8e\u4f60\u540e\u7eed\u76f4\u63a5\u7528 <code>mvn clean install<\/code> \u6253\u5305\u81f3\u5173\u91cd\u8981\uff09\u3002<\/p>\n<h3>\u91cd\u5efa\u7a33\u56fa\u7684\u540e\u7aef\u7ed3\u6784<\/h3>\n<p>\u8bf7\u4f9d\u6b21\u5728\u7ec8\u7aef\u6267\u884c\u4ee5\u4e0b\u547d\u4ee4\u3002\u6211\u4f1a\u5e26\u4f60\u628a\u4e71\u6389\u7684\u6587\u4ef6\u6e05\u7406\u6389\uff0c\u7136\u540e\u7528\u6700\u7a33\u59a5\u7684\u65b9\u5f0f\u91cd\u65b0\u521b\u5efa\uff1a<br \/>\n<strong>1. \u6e05\u7406\u5f53\u524d\u76ee\u5f55\uff08\u628a\u521a\u624d\u6492\u843d\u7684\u6587\u4ef6\u90fd\u5220\u6389\uff0c\u4fdd\u6301\u5e72\u51c0\uff09\uff1a<\/strong><\/p>\n<pre><code class=\"language-bash\"># \u786e\u4fdd\u4f60\u73b0\u5728\u5728 \/mnt\/navidrome-system\/backend \u76ee\u5f55\u4e0b\r\nrm -rf src gradle build.gradle gradlew gradlew.bat .gitattributes .gitignore HELP.md settings.gradle navidrome-server.zip\r\n<\/code><\/pre>\n<p><strong>2. \u521b\u5efa\u4e00\u4e2a\u771f\u6b63\u7684\u9879\u76ee\u6587\u4ef6\u5939\u5e76\u8fdb\u5165\uff1a<\/strong><\/p>\n<pre><code class=\"language-bash\">mkdir navidrome-server\r\ncd navidrome-server\r\n<\/code><\/pre>\n<p><strong>3. \u5728\u8fd9\u4e2a\u5e72\u51c0\u7684\u6587\u4ef6\u5939\u91cc\uff0c\u91cd\u65b0\u4e0b\u8f7d\u5e76\u89e3\u538b Maven \u9879\u76ee\uff1a<\/strong><br \/>\n\u8fd9\u6b21\u6211\u4eec\u52a0\u4e86 <code>type=maven-project<\/code> \u53c2\u6570\uff0c\u786e\u4fdd\u5b83\u751f\u6210\u7684\u662f\u4e00\u4e2a Maven \u9879\u76ee\uff08\u4f1a\u6709 <code>pom.xml<\/code> \u548c <code>mvnw<\/code>\uff09\u3002<\/p>\n<pre><code class=\"language-bash\">curl -G https:\/\/start.spring.io\/starter.zip \\\r\n  -d type=maven-project \\\r\n  -d dependencies=web,mysql,lombok,validation \\\r\n  -d javaVersion=17 \\\r\n  -d groupId=vip.crabpt.navidrome \\\r\n  -d artifactId=navidrome-server \\\r\n  -d name=navidrome-server \\\r\n  -d packageName=vip.crabpt.navidrome \\\r\n  -o navidrome-server.zip\r\n\r\n# \u539f\u5730\u89e3\u538b\r\nunzip navidrome-server.zip\r\n\r\n# \u5220\u6389\u7528\u5b8c\u7684\u538b\u7f29\u5305\r\nrm navidrome-server.zip\r\n<\/code><\/pre>\n<p>\u8fd9\u4e2a\u65f6\u5019\uff0c\u5982\u679c\u4f60\u8f93\u5165 <code>ls<\/code>\uff0c\u4f60\u5e94\u8be5\u80fd\u770b\u5230 <code>pom.xml<\/code>\u3001<code>src<\/code>\u3001<code>mvnw<\/code> \u7b49\u5178\u578b\u7684 Maven \u9879\u76ee\u7ed3\u6784\uff0c\u800c\u4e14\u5b83\u4eec\u90fd\u4e56\u4e56\u5730\u5f85\u5728 <code>\/mnt\/navidrome-system\/backend\/navidrome-server<\/code> \u8fd9\u4e2a\u76ee\u5f55\u4e0b\u4e86\u3002<\/p>\n<hr \/>\n<h3>\u63a5\u7eed\u914d\u7f6e\u5de5\u4f5c<\/h3>\n<p>\u73b0\u5728\u6211\u4eec\u7684\u76ee\u5f55\u7ed3\u6784\u5bf9\u4e86\uff0c\u6784\u5efa\u5de5\u5177\u4e5f\u5bf9\u9f50\u4e86 Maven\uff0c\u6211\u4eec\u6765\u628a\u914d\u7f6e\u6587\u4ef6\u7684\u6536\u5c3e\u5de5\u4f5c\u505a\u5b8c\uff1a<br \/>\n<strong>1. \u66ff\u6362\u914d\u7f6e\u6587\u4ef6<\/strong><br \/>\n\u628a\u9ed8\u8ba4\u7684 <code>.properties<\/code> \u5220\u6389\uff0c\u6362\u6210\u6211\u4eec\u4e60\u60ef\u7684 <code>.yml<\/code>\uff1a<\/p>\n<pre><code class=\"language-bash\">rm src\/main\/resources\/application.properties\r\nvim src\/main\/resources\/application.yml\r\n<\/code><\/pre>\n<p><strong>2. \u7c98\u8d34\u914d\u7f6e\uff08\u5305\u542b\u4f60\u7684 MySQL 3535 \u7aef\u53e3\uff09<\/strong><br \/>\n\u8fdb\u5165 vim \u540e\uff0c\u6309 <code>i<\/code> \u952e\u8fdb\u5165\u63d2\u5165\u6a21\u5f0f\uff0c\u7c98\u8d34\u4e0b\u9762\u7684\u5185\u5bb9\uff1a<\/p>\n<pre><code class=\"language-yaml\">server:\r\n  port: 8001\r\n  servlet:\r\n    context-path: \/v1\r\n\r\nspring:\r\n  datasource:\r\n    driver-class-name: com.mysql.cj.jdbc.Driver\r\n    url: jdbc:mysql:\/\/127.0.0.1:3535\/nevidrome-server?autoReconnect=true&amp;useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=Asia\/Shanghai\r\n    username: root\r\n    password: navidrome_pwd_123\r\n\r\nmybatis-plus:\r\n  mapper-locations: classpath*:\/mapper\/**\/*.xml\r\n  global-config:\r\n    db-config:\r\n      id-type: auto\r\n      logic-delete-field: deleted\r\n      logic-delete-value: 1\r\n      logic-not-delete-value: 0\r\n  configuration:\r\n    map-underscore-to-camel-case: true\r\n    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl\r\n<\/code><\/pre>\n<p>\u7c98\u8d34\u597d\u540e\uff0c\u6309 <code>Esc<\/code> \u952e\uff0c\u8f93\u5165 <code>:wq<\/code> \u7136\u540e\u56de\u8f66\u4fdd\u5b58\u3002<br \/>\n<strong>3. \u8865\u5145 MyBatis-Plus \u4f9d\u8d56<\/strong><br \/>\n\u7531\u4e8e start.spring.io \u9ed8\u8ba4\u4e0d\u63d0\u4f9b\u56fd\u4eba\u5e38\u7528\u7684 MyBatis-Plus\uff08\u53ea\u63d0\u4f9b\u539f\u751f\u7684 MyBatis\uff09\uff0c\u6211\u4eec\u9700\u8981\u5728 <code>pom.xml<\/code> \u91cc\u52a0\u4e00\u4e0b\u3002<br \/>\n\u6253\u5f00 <code>pom.xml<\/code>\uff1a<\/p>\n<pre><code class=\"language-bash\">vim pom.xml\r\n<\/code><\/pre>\n<p>\u627e\u5230 <code>&lt;dependencies&gt;<\/code> \u8fd9\u4e2a\u6807\u7b7e\uff0c\u5728\u5b83\u91cc\u9762\uff08\u968f\u4fbf\u627e\u4e24\u4e2a <code>&lt;dependency&gt;<\/code> \u4e4b\u95f4\uff09\u63d2\u5165\u8fd9\u5757\u4ee3\u7801\uff1a<\/p>\n<pre><code class=\"language-xml\">&lt;dependency&gt;\r\n            &lt;groupId&gt;com.baomidou&lt;\/groupId&gt;\r\n            &lt;artifactId&gt;mybatis-plus-spring-boot3-starter&lt;\/artifactId&gt;\r\n            &lt;version&gt;3.5.5&lt;\/version&gt;\r\n        &lt;\/dependency&gt;\r\n<\/code><\/pre>\n<p>\u6309 <code>Esc<\/code> \u952e\uff0c\u8f93\u5165 <code>:wq<\/code> \u4fdd\u5b58\u3002<\/p>\n<hr \/>\n<h3>\u6d4b\u8bd5\u9a8c\u8bc1<\/h3>\n<p>\u73b0\u5728\uff0c\u5728 <code>\/mnt\/navidrome-system\/backend\/navidrome-server<\/code> \u76ee\u5f55\u4e0b\uff0c\u8fd0\u884c Maven \u7f16\u8bd1\u6253\u5305\u547d\u4ee4\uff1a<\/p>\n<pre><code class=\"language-bash\">.\/mvnw clean package -DskipTests\r\n<\/code><\/pre>\n<p>\u8fd9\u6b21\u7b49\u5b83\u4e0b\u8f7d\u5b8c\u4f9d\u8d56\uff0c\u5e94\u8be5\u5c31\u80fd\u770b\u5230\u4e45\u8fdd\u7684 <code>[INFO] BUILD SUCCESS<\/code> \u4e86\uff01\u770b\u5230\u8fd9\u4e2a\uff0c\u6211\u4eec\u540e\u7aef\u7684\u9aa8\u67b6\u5c31 100% \u7a33\u56fa\u4e86\u3002\u6210\u529f\u540e\u53eb\u6211\u4e00\u58f0\uff0c\u6211\u4eec\u8fdb\u5165\u4e0b\u4e00\u6b65\uff01<\/p>\n<h2>Turn 14<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>[INFO] Replacing main artifact \/mnt\/navidrome-system\/backend\/navidrome-server\/target\/navidrome-server-0.0.1-SNAPSHOT.jar with repackaged archive, adding nested dependencies in BOOT-INF\/.<br \/>\n[INFO] The original artifact has been renamed to \/mnt\/navidrome-system\/backend\/navidrome-server\/target\/navidrome-server-0.0.1-SNAPSHOT.jar.original<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[INFO] BUILD SUCCESS<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[INFO] Total time: 45.648 s<br \/>\n[INFO] Finished at: 2026-04-14T01:35:44-04:00<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n\u5982\u679c\u8fd9\u6837\u6210\u529f\uff0c\u90a3\u6211\u4eec\u5f00\u59cb\u4e0b\u4e00\u6b65\u3002<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u592a\u68d2\u4e86\uff01\u770b\u5230 <code>[INFO] BUILD SUCCESS<\/code>\uff0c\u8bf4\u660e\u6211\u4eec\u7684\u540e\u7aef\u5730\u57fa\u5df2\u7ecf\u975e\u5e38\u624e\u5b9e\u4e86\u3002<br \/>\n\u63a5\u4e0b\u6765\uff0c\u6211\u4eec\u9700\u8981\u6839\u636e\u4f60\u7684\u89c4\u5212\uff0c\u5b8c\u6210 <strong>\u9009\u9879 A\uff1a\u6df1\u8015\u540e\u7aef<\/strong> \u6216\u8005 <strong>\u9009\u9879 B\uff1a\u8f6c\u6218\u524d\u7aef Vue3<\/strong>\u3002<br \/>\n\u65e2\u7136\u4f60\u63d0\u5230\u4f60\u662f\u201c\u4ee3\u7801\u5c0f\u767d\u201d\uff0c\u6211\u5efa\u8bae\u6211\u4eec\u5148\u8d81\u70ed\u6253\u94c1\uff0c\u628a<strong>\u540e\u7aef\uff08\u9009\u9879 A\uff09\u7684\u6838\u5fc3\u4ee3\u7801\u642d\u5efa\u5b8c\u6210\uff0c\u7279\u522b\u662f\u81ea\u52a8\u751f\u6210\u5b9e\u4f53\u7c7b\u548c\u57fa\u7840\u63a5\u53e3<\/strong>\u3002\u8fd9\u6837\u540e\u7aef\u5c31\u6709\u4e86\u4e00\u4e2a\u53ef\u4ee5\u968f\u65f6\u8c03\u7528\u7684\u57fa\u7840\u7ed3\u6784\uff0c\u4e4b\u540e\u6211\u4eec\u518d\u53bb\u5199\u524d\u7aef\u65f6\uff0c\u5c31\u80fd\u76f4\u63a5\u5bf9\u7740\u771f\u5b9e\u7684\u63a5\u53e3\u8bf7\u6c42\u6570\u636e\u4e86\u3002<\/p>\n<h3>\u4e0b\u4e00\u6b65\u8ba1\u5212\uff1a\u4f7f\u7528 MyBatis-Plus \u4ee3\u7801\u751f\u6210\u5668<\/h3>\n<p>MyBatis-Plus \u63d0\u4f9b\u4e86\u4e00\u4e2a\u975e\u5e38\u5f3a\u5927\u7684\u201c\u4ee3\u7801\u751f\u6210\u5668\uff08AutoGenerator\uff09\u201d\uff0c\u5b83\u53ef\u4ee5\u76f4\u63a5\u8bfb\u53d6\u6211\u4eec\u521a\u624d\u5728 MySQL \u91cc\u5efa\u597d\u7684\u8868\u7ed3\u6784\uff0c\u7136\u540e\u81ea\u52a8\u751f\u6210\u5bf9\u5e94\u7684 <code>Entity<\/code>\uff08\u5b9e\u4f53\u7c7b\uff09\u3001<code>Mapper<\/code>\uff08\u6570\u636e\u8bbf\u95ee\u5c42\uff09\u3001<code>Service<\/code>\uff08\u4e1a\u52a1\u903b\u8f91\u5c42\uff09\u548c <code>Controller<\/code>\uff08\u63a5\u53e3\u63a7\u5236\u5c42\uff09\u3002<br \/>\n\u8fd9\u80fd\u7701\u53bb\u6211\u4eec\u624b\u52a8\u7f16\u5199\u5927\u91cf\u91cd\u590d\u4ee3\u7801\u7684\u65f6\u95f4\uff01<br \/>\n\u8bf7\u6309\u7167\u4ee5\u4e0b\u6b65\u9aa4\u64cd\u4f5c\uff1a<br \/>\n<strong>1. \u6dfb\u52a0\u4ee3\u7801\u751f\u6210\u5668\u4f9d\u8d56<\/strong><br \/>\n\u6211\u4eec\u9700\u8981\u5728 <code>pom.xml<\/code> \u4e2d\u6dfb\u52a0\u4ee3\u7801\u751f\u6210\u5668\u548c\u6a21\u677f\u5f15\u64ce\uff08Velocity\uff09\u7684\u4f9d\u8d56\u3002<br \/>\n\u6253\u5f00 <code>pom.xml<\/code>\uff1a<\/p>\n<pre><code class=\"language-bash\">vim pom.xml\r\n<\/code><\/pre>\n<p>\u627e\u5230 <code>&lt;dependencies&gt;<\/code> \u6807\u7b7e\uff0c\u5728\u91cc\u9762\u63d2\u5165\u4ee5\u4e0b\u4ee3\u7801\uff1a<\/p>\n<pre><code class=\"language-xml\">&lt;dependency&gt;\r\n            &lt;groupId&gt;com.baomidou&lt;\/groupId&gt;\r\n            &lt;artifactId&gt;mybatis-plus-generator&lt;\/artifactId&gt;\r\n            &lt;version&gt;3.5.5&lt;\/version&gt;\r\n        &lt;\/dependency&gt;\r\n        &lt;dependency&gt;\r\n            &lt;groupId&gt;org.apache.velocity&lt;\/groupId&gt;\r\n            &lt;artifactId&gt;velocity-engine-core&lt;\/artifactId&gt;\r\n            &lt;version&gt;2.3&lt;\/version&gt;\r\n        &lt;\/dependency&gt;\r\n<\/code><\/pre>\n<p>\u4fdd\u5b58\u5e76\u9000\u51fa\uff08\u6309 <code>Esc<\/code>\uff0c\u8f93\u5165 <code>:wq<\/code>\uff0c\u56de\u8f66\uff09\u3002<br \/>\n<strong>2. \u521b\u5efa\u4ee3\u7801\u751f\u6210\u5668\u542f\u52a8\u7c7b<\/strong><br \/>\n\u6211\u4eec\u9700\u8981\u5199\u4e00\u5c0f\u6bb5 Java \u4ee3\u7801\u6765\u6267\u884c\u8fd9\u4e2a\u751f\u6210\u8fc7\u7a0b\u3002<br \/>\n\u5728 <code>\/mnt\/navidrome-system\/backend\/navidrome-server\/src\/test\/java\/vip\/crabpt\/navidrome\/<\/code> \u76ee\u5f55\u4e0b\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a <code>CodeGenerator.java<\/code> \u7684\u6587\u4ef6\uff1a<\/p>\n<pre><code class=\"language-bash\">vim src\/test\/java\/vip\/crabpt\/navidrome\/CodeGenerator.java\r\n<\/code><\/pre>\n<p>\u5c06\u4ee5\u4e0b\u4ee3\u7801\u7c98\u8d34\u8fdb\u53bb\uff08\u8fd9\u5df2\u7ecf\u662f\u914d\u7f6e\u597d\u7684\uff0c\u5b83\u4f1a\u8fde\u63a5\u4f60\u7684 <code>3535<\/code> \u7aef\u53e3\u7684\u6570\u636e\u5e93\uff0c\u5e76\u8bfb\u53d6\u4e4b\u524d\u521b\u5efa\u7684\u6240\u6709 <code>sys_<\/code> \u5f00\u5934\u7684\u8868\uff09\uff1a<\/p>\n<pre><code class=\"language-java\">package vip.crabpt.navidrome;\r\n\r\nimport com.baomidou.mybatisplus.generator.FastAutoGenerator;\r\nimport com.baomidou.mybatisplus.generator.config.OutputFile;\r\nimport com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;\r\n\r\nimport java.util.Collections;\r\n\r\npublic class CodeGenerator {\r\n    public static void main(String[] args) {\r\n        FastAutoGenerator.create(\"jdbc:mysql:\/\/127.0.0.1:3535\/nevidrome-server?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=Asia\/Shanghai\", \"root\", \"navidrome_pwd_123\")\r\n                .globalConfig(builder -&gt; {\r\n                    builder.author(\"crabpt\") \/\/ \u8bbe\u7f6e\u4f5c\u8005\r\n                            .enableSwagger() \/\/ \u5f00\u542f swagger \u6a21\u5f0f (\u53ef\u9009\uff0c\u5148\u7559\u7740)\r\n                            .outputDir(System.getProperty(\"user.dir\") + \"\/src\/main\/java\"); \/\/ \u6307\u5b9a\u8f93\u51fa\u76ee\u5f55\r\n                })\r\n                .packageConfig(builder -&gt; {\r\n                    builder.parent(\"vip.crabpt.navidrome\") \/\/ \u8bbe\u7f6e\u7236\u5305\u540d\r\n                            .pathInfo(Collections.singletonMap(OutputFile.xml, System.getProperty(\"user.dir\") + \"\/src\/main\/resources\/mapper\")); \/\/ \u8bbe\u7f6e mapperXml \u751f\u6210\u8def\u5f84\r\n                })\r\n                .strategyConfig(builder -&gt; {\r\n                    builder.addInclude(\"sys_user\", \"sys_checkin\", \"sys_invite_code\", \"sys_point_log\") \/\/ \u8bbe\u7f6e\u9700\u8981\u751f\u6210\u7684\u8868\u540d\r\n                            .addTablePrefix(\"sys_\") \/\/ \u8bbe\u7f6e\u8fc7\u6ee4\u8868\u524d\u7f00\uff0c\u8fd9\u6837\u751f\u6210\u7684\u7c7b\u540d\u5c31\u4e0d\u4f1a\u5e26 Sys\r\n                            \/\/ Entity \u7b56\u7565\u914d\u7f6e\r\n                            .entityBuilder()\r\n                            .enableLombok() \/\/ \u5f00\u542f Lombok\r\n                            \/\/ Controller \u7b56\u7565\u914d\u7f6e\r\n                            .controllerBuilder()\r\n                            .enableRestStyle(); \/\/ \u5f00\u542f @RestController\r\n                })\r\n                .templateEngine(new VelocityTemplateEngine())\r\n                .execute();\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>\u4fdd\u5b58\u5e76\u9000\u51fa\uff08\u6309 <code>Esc<\/code>\uff0c\u8f93\u5165 <code>:wq<\/code>\uff0c\u56de\u8f66\uff09\u3002<br \/>\n<strong>3. \u8fd0\u884c\u4ee3\u7801\u751f\u6210\u5668<\/strong><br \/>\n\u73b0\u5728\uff0c\u6211\u4eec\u901a\u8fc7 Maven \u6765\u7f16\u8bd1\u5e76\u8fd0\u884c\u8fd9\u4e2a\u6d4b\u8bd5\u7c7b\u3002\u5728 <code>\/mnt\/navidrome-system\/backend\/navidrome-server<\/code> \u76ee\u5f55\u4e0b\u6267\u884c\uff1a<\/p>\n<pre><code class=\"language-bash\">.\/mvnw test-compile exec:java -Dexec.mainClass=\"vip.crabpt.navidrome.CodeGenerator\" -Dexec.classpathScope=test\r\n<\/code><\/pre>\n<p><strong>\u89c1\u8bc1\u5947\u8ff9\u7684\u65f6\u523b\uff01<\/strong><br \/>\n\u5982\u679c\u4e00\u5207\u987a\u5229\uff0c\u4f60\u4f1a\u770b\u5230\u63a7\u5236\u53f0\u6253\u5370\u51fa\u4e00\u8fde\u4e32\u751f\u6210\u7684\u65e5\u5fd7\u3002\u5b8c\u6210\u540e\uff0c\u4f60\u53ef\u4ee5\u53bb\u67e5\u770b <code>src\/main\/java\/vip\/crabpt\/navidrome\/<\/code> \u76ee\u5f55\uff0c\u4f60\u4f1a\u53d1\u73b0\u591a\u4e86 <code>entity<\/code>, <code>mapper<\/code>, <code>service<\/code>, <code>controller<\/code> \u56db\u4e2a\u6587\u4ef6\u5939\uff0c\u91cc\u9762\u5df2\u7ecf\u81ea\u52a8\u586b\u6ee1\u4e86\u5bf9\u5e94\u7684\u6570\u636e\u8868\u7c7b\uff01<br \/>\n\u6267\u884c\u5b8c\u8fd9\u4e00\u6b65\u544a\u8bc9\u6211\u7ed3\u679c\uff0c\u5982\u679c\u6210\u529f\uff0c\u6211\u4eec\u7684\u540e\u7aef\u57fa\u7840\u67b6\u6784\u5efa\u8bbe\u5c31\u771f\u6b63\u5b8c\u6210\u4e86\uff0c\u53ef\u4ee5\u5f00\u59cb\u542f\u52a8\u6d4b\u8bd5\u4e86\uff01<\/p>\n<h2>Turn 15<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[INFO] BUILD SUCCESS<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[INFO] Total time: 19.331 s<br \/>\n[INFO] Finished at: 2026-04-14T01:39:43-04:00<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u592a\u68d2\u4e86\uff01\u518d\u6b21\u770b\u5230 <code>BUILD SUCCESS<\/code>\uff0c\u8fd9\u610f\u5473\u7740 <strong>MyBatis-Plus \u4ee3\u7801\u751f\u6210\u5668\u5df2\u7ecf\u5b8c\u7f8e\u5b8c\u6210\u4e86\u4efb\u52a1\uff01<\/strong><br \/>\n\u4f60\u53ef\u4ee5\u60f3\u8c61\u4e00\u4e0b\uff0c\u5982\u679c\u521a\u624d\u7eaf\u624b\u5de5\u6572\u4ee3\u7801\uff0c\u4f60\u9700\u8981\u624b\u5199\u51e0\u5341\u4e2a\u6587\u4ef6\u548c\u51e0\u5343\u884c\u4ee3\u7801\u6765\u5efa\u7acb\u6570\u636e\u5e93\u4e0e Java \u4e4b\u95f4\u7684\u6620\u5c04\u5173\u7cfb\u3002\u800c\u73b0\u5728\uff0c\u5982\u679c\u4f60\u901a\u8fc7 <code>ls src\/main\/java\/vip\/crabpt\/navidrome\/<\/code> \u67e5\u770b\u76ee\u5f55\uff0c\u4f60\u4f1a\u53d1\u73b0\u7cfb\u7edf\u5df2\u7ecf\u81ea\u52a8\u5e2e\u4f60\u5efa\u597d\u4e86 <code>entity<\/code>\uff08\u5b9e\u4f53\u7c7b\uff09\u3001<code>mapper<\/code>\uff08\u6570\u636e\u5e93\u64cd\u4f5c\u5c42\uff09\u3001<code>service<\/code>\uff08\u4e1a\u52a1\u903b\u8f91\u5c42\uff09\u548c <code>controller<\/code>\uff08\u63a5\u53e3\u63a7\u5236\u5c42\uff09\u56db\u4e2a\u6587\u4ef6\u5939\uff0c\u91cc\u9762\u5168\u662f\u4f60\u9700\u8981\u7684\u4ee3\u7801\u3002<br \/>\n\u81f3\u6b64\uff0c\u6211\u4eec\u7684 <strong>Milestone 1\uff08\u540e\u7aef\u57fa\u7840\u9aa8\u67b6\uff09<\/strong> \u5df2\u7ecf\u5927\u529f\u544a\u6210\uff01\u6574\u4e2a\u5730\u57fa\u975e\u5e38\u7262\u56fa\u3002<br \/>\n\u63a5\u4e0b\u6765\uff0c\u6211\u4eec\u6765\u5230\u4e86\u4e00\u4e2a\u5206\u5c94\u8def\u53e3\u3002\u4f5c\u4e3a\u4f60\u7684\u9879\u76ee\u7ba1\u7406\uff0c\u6211\u7ed9\u4f60\u63d0\u4f9b\u4e24\u4e2a\u4e0b\u4e00\u6b65\u7684\u63a8\u8fdb\u65b9\u5411\uff0c\u7531\u4f60\u6765\u51b3\u5b9a\uff1a<\/p>\n<h3>\ud83d\udee3\ufe0f \u8def\u7ebf A\uff1a\u8d81\u70ed\u6253\u94c1\uff0c\u5b8c\u5584\u540e\u7aef\u4e1a\u52a1\u903b\u8f91\uff08\u63a8\u8350\uff09<\/h3>\n<p>\u4ee3\u7801\u751f\u6210\u5668\u53ea\u5e2e\u6211\u4eec\u5199\u4e86\u201c\u589e\u5220\u6539\u67e5\u201d\u7684\u57fa\u7840\u52a8\u4f5c\uff0c\u4f46\u5b83\u4e0d\u61c2\u6211\u4eec\u7684\u201c\u4e1a\u52a1\u89c4\u5219\u201d\u3002<br \/>\n\u5982\u679c\u6211\u4eec\u9009\u8fd9\u6761\u8def\uff0c\u4e0b\u4e00\u6b65\u6211\u4eec\u5c06\u7f16\u5199\uff1a<\/p>\n<ol>\n<li><strong>\u8ba4\u8bc1\u903b\u8f91<\/strong>\uff1a\u5199\u4e00\u4e2a <code>\/login<\/code> \u63a5\u53e3\uff0c\u9a8c\u8bc1\u8d26\u53f7\u5bc6\u7801\uff0c\u5e76\u751f\u6210 JWT Token\u3002<\/li>\n<li><strong>\u7b7e\u5230\u903b\u8f91<\/strong>\uff1a\u5199\u4e00\u4e2a <code>\/checkin<\/code> \u63a5\u53e3\uff0c\u5224\u65ad\u7528\u6237\u4eca\u5929\u662f\u5426\u7b7e\u5230\u8fc7\uff0c\u5982\u679c\u6ca1\u6709\uff0c\u79ef\u5206 +10\u3002<br \/>\n\u8fd9\u6837\u540e\u7aef\u5c31\u5f7b\u5e95\u62e5\u6709\u4e86\u7075\u9b42\uff0c\u968f\u65f6\u53ef\u4ee5\u88ab\u8c03\u7528\u3002<\/li>\n<\/ol>\n<h3>\ud83d\udee3\ufe0f \u8def\u7ebf B\uff1a\u8f6c\u6218\u524d\u7aef\uff0c\u642d\u5efa Vue3 \u5de5\u7a0b<\/h3>\n<p>\u5982\u679c\u4f60\u89c9\u5f97\u770b\u540e\u7aef\u4ee3\u7801\u6709\u4e9b\u67af\u71e5\uff0c\u60f3\u5148\u770b\u770b\u754c\u9762\u7684\u6548\u679c\uff0c\u6211\u4eec\u53ef\u4ee5\u53bb <code>\/mnt\/navidrome-system\/frontend<\/code> \u76ee\u5f55\u3002<br \/>\n\u5982\u679c\u6211\u4eec\u9009\u8fd9\u6761\u8def\uff0c\u4e0b\u4e00\u6b65\u6211\u4eec\u5c06\uff1a<\/p>\n<ol>\n<li>\u4f7f\u7528 <code>Vite<\/code> \u4e00\u952e\u521d\u59cb\u5316\u4e00\u4e2a\u73b0\u4ee3\u5316\u7684 Vue 3 \u9879\u76ee\u3002<\/li>\n<li>\u628a\u4f60\u4e4b\u524d\u90a3\u5957\u6f02\u4eae\u7684 HTML\/CSS \u62c6\u5206\u6210\u4e00\u4e2a\u4e2a <code>.vue<\/code> \u7ec4\u4ef6\uff08\u6bd4\u5982\u4fa7\u8fb9\u680f\u7ec4\u4ef6\u3001\u9876\u90e8\u5bfc\u822a\u7ec4\u4ef6\uff09\u3002<\/li>\n<li>\u914d\u7f6e\u597d\u8def\u7531\uff08Router\uff09\uff0c\u8ba9\u9875\u9762\u771f\u6b63\u80fd\u6765\u56de\u8df3\u8f6c\u3002<br \/>\n<strong>\u4f60\u5e0c\u671b\u6211\u4eec\u5148\u8d70\u8def\u7ebf A\uff08\u6b7b\u78d5\u540e\u7aef\u903b\u8f91\uff09\uff0c\u8fd8\u662f\u8def\u7ebf B\uff08\u642d\u5efa\u524d\u7aef Vue3 \u9aa8\u67b6\uff09\uff1f<\/strong> \u544a\u8bc9\u6211\u4f60\u7684\u9009\u62e9\uff0c\u6211\u4f1a\u9a6c\u4e0a\u4e3a\u4f60\u751f\u6210\u4e0b\u4e00\u6b65\u7684\u201c\u50bb\u74dc\u5f0f\u201d\u64cd\u4f5c\u6307\u4ee4\uff01<\/li>\n<\/ol>\n<h2>Turn 16<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>\u8def\u7ebf A\uff08\u6b7b\u78d5\u540e\u7aef\u903b\u8f91\uff09<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u975e\u5e38\u597d\uff01\u6211\u4eec\u9009\u62e9\u8def\u7ebf A\uff1a\u6b7b\u78d5\u540e\u7aef\u903b\u8f91\u3002\u628a\u6838\u5fc3\u4e1a\u52a1\u903b\u8f91\u8dd1\u901a\uff0c\u7cfb\u7edf\u624d\u7b97\u662f\u771f\u6b63\u6d3b\u8fc7\u6765\u4e86\u3002<br \/>\n\u5728\u8fd9\u4e00\u6b65\uff0c\u6211\u4eec\u5148\u4e0d\u641e\u590d\u6742\u7684\u5b89\u5168\u6821\u9a8c\uff08\u6bd4\u5982 Spring Security \u6216 JWT\uff09\uff0c\u800c\u662f\u91c7\u7528\u4e00\u79cd**\u6700\u7b80\u5355\u3001\u6700\u6e05\u6670\u7684\u201c\u571f\u6cd5\u70bc\u94a2\u201d**\u65b9\u5f0f\uff0c\u8ba9\u4f60\u76f4\u63a5\u4f53\u9a8c\u5230\u63a5\u53e3\u662f\u600e\u4e48\u8c03\u7528\u7684\u3001\u6570\u636e\u662f\u600e\u4e48\u8fdb\u51fa\u6570\u636e\u5e93\u7684\u3002\u7b49\u6d41\u7a0b\u8dd1\u901a\u4e86\uff0c\u4ee5\u540e\u60f3\u52a0\u5b89\u5168\u6821\u9a8c\u968f\u65f6\u53ef\u4ee5\u52a0\u3002<br \/>\n\u6211\u4eec\u5c06\u5b8c\u6210\u4e24\u4e2a\u6838\u5fc3\u63a5\u53e3\uff1a<\/p>\n<ol>\n<li><strong>\/v1\/login (\u767b\u5f55\u63a5\u53e3)<\/strong>\uff1a\u9a8c\u8bc1\u8d26\u53f7\u5bc6\u7801\uff0c\u5982\u679c\u6b63\u786e\uff0c\u8fd4\u56de\u7528\u6237\u7684\u57fa\u7840\u4fe1\u606f\u3002<\/li>\n<li><strong>\/v1\/checkin (\u7b7e\u5230\u63a5\u53e3)<\/strong>\uff1a\u63a5\u6536\u4e00\u4e2a\u7528\u6237 ID\uff0c\u5224\u65ad\u4eca\u5929\u662f\u5426\u5df2\u7ecf\u7b7e\u5230\u3002\u5982\u679c\u6ca1\u6709\uff0c\u7ed9\u8be5\u7528\u6237\u589e\u52a0 10 \u79ef\u5206\uff0c\u5e76\u5728\u7b7e\u5230\u8868\u4e2d\u8bb0\u5f55\u3002<br \/>\n\u8bf7\u6309\u7167\u4ee5\u4e0b\u6b65\u9aa4\uff0c\u4f9d\u6b21\u521b\u5efa\u76f8\u5173\u7c7b\uff1a<\/li>\n<\/ol>\n<h3>1. \u521b\u5efa\u901a\u7528\u8fd4\u56de\u7ed3\u679c\u7c7b (Result)<\/h3>\n<p>\u6240\u6709\u7684\u63a5\u53e3\u6700\u597d\u8fd4\u56de\u7edf\u4e00\u7684\u683c\u5f0f\uff0c\u6bd4\u5982 <code>{ \"code\": 200, \"msg\": \"\u6210\u529f\", \"data\": ... }<\/code>\u3002<\/p>\n<pre><code class=\"language-bash\">vim src\/main\/java\/vip\/crabpt\/navidrome\/Result.java\r\n<\/code><\/pre>\n<p>\u8d34\u5165\u4ee5\u4e0b\u4ee3\u7801\uff1a<\/p>\n<pre><code class=\"language-java\">package vip.crabpt.navidrome;\r\n\r\nimport lombok.Data;\r\n\r\n@Data\r\npublic class Result&lt;T&gt; {\r\n    private Integer code;\r\n    private String msg;\r\n    private T data;\r\n\r\n    public static &lt;T&gt; Result&lt;T&gt; success(T data) {\r\n        Result&lt;T&gt; result = new Result&lt;&gt;();\r\n        result.setCode(200);\r\n        result.setMsg(\"\u6210\u529f\");\r\n        result.setData(data);\r\n        return result;\r\n    }\r\n\r\n    public static &lt;T&gt; Result&lt;T&gt; error(String msg) {\r\n        Result&lt;T&gt; result = new Result&lt;&gt;();\r\n        result.setCode(500);\r\n        result.setMsg(msg);\r\n        return result;\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>\u4fdd\u5b58\u9000\u51fa\uff08<code>:wq<\/code>\uff09\u3002<\/p>\n<h3>2. \u5b8c\u5584\u7528\u6237\u5b9e\u4f53\u7c7b (User)<\/h3>\n<p>\u6211\u4eec\u9700\u8981\u544a\u8bc9 MyBatis-Plus\uff0c\u5b9e\u4f53\u7c7b <code>User<\/code> \u5bf9\u5e94\u7684\u662f\u6570\u636e\u5e93\u91cc\u7684\u54ea\u5f20\u8868\uff0c\u4e3b\u952e\u662f\u4ec0\u4e48\u3002<\/p>\n<pre><code class=\"language-bash\">vim src\/main\/java\/vip\/crabpt\/navidrome\/entity\/User.java\r\n<\/code><\/pre>\n<p>\u4fee\u6539\u6210\u8fd9\u6837\uff08\u4e3b\u8981\u662f\u52a0\u4e0a <code>@TableName<\/code> \u548c <code>@TableId<\/code> \u7b49\u6ce8\u89e3\uff0c\u4f60\u53ef\u4ee5\u76f4\u63a5\u8986\u76d6\u539f\u6765\u7684\u5185\u5bb9\uff09\uff1a<\/p>\n<pre><code class=\"language-java\">package vip.crabpt.navidrome.entity;\r\n\r\nimport com.baomidou.mybatisplus.annotation.IdType;\r\nimport com.baomidou.mybatisplus.annotation.TableId;\r\nimport com.baomidou.mybatisplus.annotation.TableName;\r\nimport lombok.Data;\r\nimport java.time.LocalDateTime;\r\n\r\n@Data\r\n@TableName(\"sys_user\")\r\npublic class User {\r\n    @TableId(type = IdType.AUTO)\r\n    private Long id;\r\n    private String email;\r\n    private String password;\r\n    private String role;\r\n    private Integer points;\r\n    private String status;\r\n    private String navidromeUsername;\r\n    private LocalDateTime createdAt;\r\n    private LocalDateTime updatedAt;\r\n}\r\n<\/code><\/pre>\n<p>\u4fdd\u5b58\u9000\u51fa\u3002<\/p>\n<h3>3. \u5b8c\u5584\u7b7e\u5230\u5b9e\u4f53\u7c7b (Checkin)<\/h3>\n<p>\u540c\u6837\u5730\uff0c\u5904\u7406\u4e00\u4e0b <code>Checkin<\/code> \u5b9e\u4f53\u7c7b\u3002<\/p>\n<pre><code class=\"language-bash\">vim src\/main\/java\/vip\/crabpt\/navidrome\/entity\/Checkin.java\r\n<\/code><\/pre>\n<p>\u4fee\u6539\u6210\u8fd9\u6837\uff1a<\/p>\n<pre><code class=\"language-java\">package vip.crabpt.navidrome.entity;\r\n\r\nimport com.baomidou.mybatisplus.annotation.IdType;\r\nimport com.baomidou.mybatisplus.annotation.TableId;\r\nimport com.baomidou.mybatisplus.annotation.TableName;\r\nimport lombok.Data;\r\nimport java.time.LocalDate;\r\nimport java.time.LocalDateTime;\r\n\r\n@Data\r\n@TableName(\"sys_checkin\")\r\npublic class Checkin {\r\n    @TableId(type = IdType.AUTO)\r\n    private Long id;\r\n    private Long userId;\r\n    private LocalDate checkinDate;\r\n    private Integer pointsEarned;\r\n    private LocalDateTime createdAt;\r\n}\r\n<\/code><\/pre>\n<p>\u4fdd\u5b58\u9000\u51fa\u3002<\/p>\n<h3>4. \u7f16\u5199\u767b\u5f55\u903b\u8f91 (AuthController)<\/h3>\n<p>\u6211\u4eec\u65b0\u5efa\u4e00\u4e2a\u4e13\u95e8\u8d1f\u8d23\u8ba4\u8bc1\u7684 Controller\u3002<\/p>\n<pre><code class=\"language-bash\">vim src\/main\/java\/vip\/crabpt\/navidrome\/controller\/AuthController.java\r\n<\/code><\/pre>\n<p>\u8d34\u5165\u4ee5\u4e0b\u4ee3\u7801\uff1a<\/p>\n<pre><code class=\"language-java\">package vip.crabpt.navidrome.controller;\r\n\r\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.web.bind.annotation.*;\r\nimport vip.crabpt.navidrome.Result;\r\nimport vip.crabpt.navidrome.entity.User;\r\nimport vip.crabpt.navidrome.service.IUserService;\r\n\r\nimport java.util.Map;\r\n\r\n@RestController\r\npublic class AuthController {\r\n\r\n    @Autowired\r\n    private IUserService userService;\r\n\r\n    \/\/ \u7b80\u5355\u767b\u5f55\u63a5\u53e3\r\n    @PostMapping(\"\/login\")\r\n    public Result&lt;User&gt; login(@RequestBody Map&lt;String, String&gt; params) {\r\n        String email = params.get(\"email\");\r\n        String password = params.get(\"password\");\r\n\r\n        \/\/ \u53bb\u6570\u636e\u5e93\u6839\u636e\u90ae\u7bb1\u67e5\u7528\u6237\r\n        QueryWrapper&lt;User&gt; queryWrapper = new QueryWrapper&lt;&gt;();\r\n        queryWrapper.eq(\"email\", email);\r\n        User user = userService.getOne(queryWrapper);\r\n\r\n        if (user == null) {\r\n            return Result.error(\"\u7528\u6237\u4e0d\u5b58\u5728\");\r\n        }\r\n        \/\/ \u7b80\u5355\u660e\u6587\u5bc6\u7801\u6bd4\u5bf9\uff08\u540e\u671f\u518d\u6539\u52a0\u5bc6\uff09\r\n        if (!user.getPassword().equals(password)) {\r\n            return Result.error(\"\u5bc6\u7801\u9519\u8bef\");\r\n        }\r\n\r\n        \/\/ \u767b\u5f55\u6210\u529f\uff0c\u4e0d\u8fd4\u56de\u5bc6\u7801\r\n        user.setPassword(null);\r\n        return Result.success(user);\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>\u4fdd\u5b58\u9000\u51fa\u3002<\/p>\n<h3>5. \u7f16\u5199\u7b7e\u5230\u903b\u8f91 (UserController \u8ffd\u52a0)<\/h3>\n<p>\u6211\u4eec\u5c06\u7b7e\u5230\u63a5\u53e3\u653e\u5728 <code>UserController<\/code> \u91cc\u3002<\/p>\n<pre><code class=\"language-bash\">vim src\/main\/java\/vip\/crabpt\/navidrome\/controller\/UserController.java\r\n<\/code><\/pre>\n<p>\u6253\u5f00\u540e\uff0c\u628a\u91cc\u9762\u539f\u672c\u7684\u5185\u5bb9<strong>\u66ff\u6362<\/strong>\u6210\u4ee5\u4e0b\u4ee3\u7801\uff08\u4e3b\u8981\u662f\u5f15\u5165 <code>CheckinService<\/code> \u5e76\u589e\u52a0\u4e86 <code>\/checkin<\/code> \u65b9\u6cd5\uff09\uff1a<\/p>\n<pre><code class=\"language-java\">package vip.crabpt.navidrome.controller;\r\n\r\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.web.bind.annotation.*;\r\nimport vip.crabpt.navidrome.Result;\r\nimport vip.crabpt.navidrome.entity.Checkin;\r\nimport vip.crabpt.navidrome.entity.User;\r\nimport vip.crabpt.navidrome.service.ICheckinService;\r\nimport vip.crabpt.navidrome.service.IUserService;\r\n\r\nimport java.time.LocalDate;\r\nimport java.util.Map;\r\n\r\n@RestController\r\n@RequestMapping(\"\/user\")\r\npublic class UserController {\r\n\r\n    @Autowired\r\n    private IUserService userService;\r\n    @Autowired\r\n    private ICheckinService checkinService;\r\n\r\n    \/\/ \u7b7e\u5230\u63a5\u53e3\r\n    @PostMapping(\"\/checkin\")\r\n    public Result&lt;String&gt; checkin(@RequestBody Map&lt;String, Long&gt; params) {\r\n        Long userId = params.get(\"userId\");\r\n        if (userId == null) {\r\n            return Result.error(\"\u7f3a\u5c11 userId \u53c2\u6570\");\r\n        }\r\n\r\n        \/\/ 1. \u68c0\u67e5\u7528\u6237\u662f\u5426\u5b58\u5728\r\n        User user = userService.getById(userId);\r\n        if (user == null) {\r\n            return Result.error(\"\u7528\u6237\u4e0d\u5b58\u5728\");\r\n        }\r\n\r\n        \/\/ 2. \u68c0\u67e5\u4eca\u5929\u662f\u5426\u5df2\u7ecf\u7b7e\u5230\r\n        LocalDate today = LocalDate.now();\r\n        QueryWrapper&lt;Checkin&gt; queryWrapper = new QueryWrapper&lt;&gt;();\r\n        queryWrapper.eq(\"user_id\", userId).eq(\"checkin_date\", today);\r\n        long count = checkinService.count(queryWrapper);\r\n\r\n        if (count &gt; 0) {\r\n            return Result.error(\"\u60a8\u4eca\u5929\u5df2\u7ecf\u7b7e\u5230\u8fc7\u4e86\");\r\n        }\r\n\r\n        \/\/ 3. \u6267\u884c\u7b7e\u5230\u64cd\u4f5c\r\n        \/\/ 3.1 \u8bb0\u5f55\u7b7e\u5230\u8868\r\n        Checkin checkin = new Checkin();\r\n        checkin.setUserId(userId);\r\n        checkin.setCheckinDate(today);\r\n        checkin.setPointsEarned(10); \/\/ \u6bcf\u6b21\u7b7e\u5230\u7ed9 10 \u79ef\u5206\r\n        checkinService.save(checkin);\r\n\r\n        \/\/ 3.2 \u589e\u52a0\u7528\u6237\u79ef\u5206\r\n        user.setPoints(user.getPoints() + 10);\r\n        userService.updateById(user);\r\n\r\n        return Result.success(\"\u7b7e\u5230\u6210\u529f\uff0c\u83b7\u5f97 10 \u79ef\u5206\uff01\");\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>\u4fdd\u5b58\u9000\u51fa\u3002<\/p>\n<hr \/>\n<h3>\ud83d\udfe2 \u6d4b\u8bd5\u63a5\u53e3\u662f\u5426\u8dd1\u901a<\/h3>\n<p>\u4ee3\u7801\u5199\u5b8c\u4e86\uff01\u73b0\u5728\u6211\u4eec\u6765\u542f\u52a8\u9879\u76ee\u5e76\u6d4b\u8bd5\u4e00\u4e0b\u3002<br \/>\n<strong>1. \u542f\u52a8 Spring Boot \u670d\u52a1\uff08\u8ba9\u5b83\u5728\u540e\u53f0\u4e00\u76f4\u8dd1\u7740\uff09\uff1a<\/strong><\/p>\n<pre><code class=\"language-bash\">nohup .\/mvnw spring-boot:run &gt; app.log 2&gt;&amp;1 &amp;\r\n<\/code><\/pre>\n<p><em>(\u4f7f\u7528 nohup \u548c &amp; \u53ef\u4ee5\u8ba9\u7a0b\u5e8f\u5728\u4f60\u5173\u95ed\u7ec8\u7aef\u540e\u7ee7\u7eed\u8fd0\u884c\u3002)<\/em><br \/>\n\u7b49\u4e2a 10 \u79d2\u949f\u5de6\u53f3\uff0c\u4f60\u53ef\u4ee5\u7528 <code>tail -f app.log<\/code> \u770b\u4e00\u4e0b\u542f\u52a8\u65e5\u5fd7\uff0c\u770b\u5230 <code>Started NavidromeServerApplication<\/code> \u5c31\u8bf4\u660e\u542f\u52a8\u6210\u529f\u4e86\u3002\u6309 <code>Ctrl+C<\/code> \u9000\u51fa\u65e5\u5fd7\u67e5\u770b\u3002<br \/>\n<strong>2. \u6d4b\u8bd5\uff1a\u7528 curl \u6a21\u62df\u524d\u7aef\u53d1\u8bf7\u6c42\uff01<\/strong><\/p>\n<pre><code class=\"language-bash\">curl -X POST http:\/\/127.0.0.1:8001\/v1\/login \\\r\n-H \"Content-Type: application\/json\" \\\r\n-d '{\"email\":\"admin@crabpt.vip\", \"password\":\"wrongpassword\"}'\r\n<\/code><\/pre>\n<p>\u4f60\u53ef\u4ee5\u6328\u4e2a\u8bd5\u4e00\u4e0b\u8fd9\u51e0\u4e2a curl \u547d\u4ee4\uff0c\u5982\u679c\u4f60\u770b\u5230\u4e86\u6211\u4e0a\u9762\u5199\u7684\u201c\u9884\u671f\u8fd4\u56de\u201d\u7ed3\u679c\uff0c\u90a3\u5c31\u8bf4\u660e\u2014\u2014<strong>\u4f60\u7684\u540e\u7aef\u5df2\u7ecf\u5f7b\u5e95\u6d3b\u4e86\uff01\u4e1a\u52a1\u903b\u8f91\u5b8c\u5168\u6253\u901a\uff01<\/strong><br \/>\n\u6d4b\u8bd5\u5b8c\u4e4b\u540e\u544a\u8bc9\u6211\uff0c\u63a5\u4e0b\u6765\u6211\u4eec\u8981\u53bb\u6311\u6218\u751f\u6210\u9080\u8bf7\u7801\u7684\u903b\u8f91\uff0c\u8fd8\u662f\u4f60\u89c9\u5f97\u540e\u7aef\u5df2\u7ecf\u4f53\u9a8c\u591f\u4e86\uff0c\u60f3\u8f6c\u53bb\u642d\u524d\u7aef\uff08\u8def\u7ebf B\uff09\u770b\u770b\u6548\u679c\uff1f<\/p>\n<h2>Turn 17<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<blockquote><p>\ud83d\udfe2 \u6d4b\u8bd5\u63a5\u53e3\u662f\u5426\u8dd1\u901a\u4ee3\u7801\u5199\u5b8c\u4e86\uff01\u73b0\u5728\u6211\u4eec\u6765\u542f\u52a8\u9879\u76ee\u5e76\u6d4b\u8bd5\u4e00\u4e0b\u30021. \u542f\u52a8 Spring Boot \u670d\u52a1\uff08\u8ba9\u5b83\u5728\u540e\u53f0\u4e00\u76f4\u8dd1\u7740\uff09\uff1aBashnohup .\/mvnw spring-boot:run &gt; app.log 2&gt;&amp;1 &amp;<br \/>\n(\u4f7f\u7528 nohup \u548c &amp; \u53ef\u4ee5\u8ba9\u7a0b\u5e8f\u5728\u4f60\u5173\u95ed\u7ec8\u7aef\u540e\u7ee7\u7eed\u8fd0\u884c\u3002)\u7b49\u4e2a 10 \u79d2\u949f\u5de6\u53f3\uff0c\u4f60\u53ef\u4ee5\u7528 tail -f app.log \u770b\u4e00\u4e0b\u542f\u52a8\u65e5\u5fd7\uff0c\u770b\u5230 Started NavidromeServerApplication \u5c31\u8bf4\u660e\u542f\u52a8\u6210\u529f\u4e86\u3002\u6309 Ctrl+C \u9000\u51fa\u65e5\u5fd7\u67e5\u770b\u3002<br \/>\n\u8fd9\u4e00\u5757\uff1aroot@192-227-164-42-host:\/mnt\/navidrome-system\/backend\/navidrome-server# nohup .\/mvnw spring-boot:run &gt; app.log 2&gt;&amp;1 &amp;<br \/>\n[2] 1289120<br \/>\nroot@192-227-164-42-host:\/mnt\/navidrome-system\/backend\/navidrome-server# nohup .\/mvnw spring-boot:run &gt; app.log 2&gt;&amp;1 &amp;<br \/>\n[3] 1289358<br \/>\n[2] Exit 1 nohup .\/mvnw spring-boot:run &gt; app.log 2&gt;&amp;1<br \/>\nroot@192-227-164-42-host:\/mnt\/navidrome-system\/backend\/navidrome-server# ^C<br \/>\n[3]- Exit 1 nohup .\/mvnw spring-boot:run &gt; app.log 2&gt;&amp;1<br \/>\nroot@192-227-164-42-host:\/mnt\/navidrome-system\/backend\/navidrome-server# nohup .\/mvnw spring-boot:run &gt; app.log 2&gt;&amp;1 &amp;<br \/>\n[2] 1289812<\/p><\/blockquote>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u770b\u5230 <code>[Exit 1]<\/code>\uff0c\u8bf4\u660e\u6211\u4eec\u7684\u540e\u7aef\u7a0b\u5e8f\u5728\u542f\u52a8\u7684\u77ac\u95f4**\u5d29\u6e83\uff08Crash\uff09**\u4e86\u3002\u522b\u614c\uff0c\u5728\u5f00\u53d1\u8fc7\u7a0b\u4e2d\uff0c\u8fd9\u7b80\u76f4\u662f\u5bb6\u5e38\u4fbf\u996d\u3002<br \/>\n\u7531\u4e8e\u6211\u4eec\u4f7f\u7528\u4e86 <code>nohup ... &gt; app.log &amp;<\/code> \u628a\u7a0b\u5e8f\u653e\u5230\u4e86\u540e\u53f0\u8fd0\u884c\uff0c\u6240\u4ee5\u5b83\u5d29\u6e83\u7684\u539f\u56e0\u5168\u90fd\u88ab\u585e\u8fdb <code>app.log<\/code> \u8fd9\u4e2a\u65e5\u5fd7\u6587\u4ef6\u91cc\u4e86\uff0c\u7ec8\u7aef\u5c4f\u5e55\u4e0a\u81ea\u7136\u4ec0\u4e48\u90fd\u6ca1\u663e\u793a\u3002<br \/>\n\u6839\u636e\u6211\u7684\u7ecf\u9a8c\uff0c\u65e2\u7136\u4e4b\u524d <code>mvn clean package<\/code> \u7f16\u8bd1\u6210\u529f\u4e86\uff08\u8bf4\u660e\u4ee3\u7801\u6ca1\u6709\u8bed\u6cd5\u9519\u8bef\uff09\uff0c\u90a3\u542f\u52a8\u5d29\u6e83 <strong>99% \u7684\u6982\u7387\u662f\u56e0\u4e3a Spring Boot \u627e\u4e0d\u5230\u6570\u636e\u5e93\u7684 Mapper\uff08\u6620\u5c04\u5668\uff09<\/strong>\u3002\u6211\u4eec\u5728\u7528\u4ee3\u7801\u751f\u6210\u5668\u751f\u6210\u5b8c\u4ee3\u7801\u540e\uff0c\u5c11\u52a0\u4e86\u4e00\u4e2a\u5f00\u542f Mapper \u626b\u63cf\u7684\u6ce8\u89e3\u3002<br \/>\n\u6765\uff0c\u6211\u4eec\u7528\u4e24\u6b65\u628a\u8fd9\u4e2a Bug \u63ea\u51fa\u6765\u5e76\u89e3\u51b3\u6389\uff1a<\/p>\n<h3>\u7b2c\u4e00\u6b65\uff1a\u67e5\u770b\u786e\u5207\u7684\u62a5\u9519\u4fe1\u606f<\/h3>\n<p>\u5728\u7ec8\u7aef\u8f93\u5165\u4ee5\u4e0b\u547d\u4ee4\uff0c\u67e5\u770b\u65e5\u5fd7\u7684\u6700\u540e 50 \u884c\uff1a<\/p>\n<pre><code class=\"language-bash\">tail -n 50 app.log\r\n<\/code><\/pre>\n<p>\u5982\u679c\u4f60\u5728\u65e5\u5fd7\u91cc\u770b\u5230\u4e86\u7c7b\u4f3c <code>NoSuchBeanDefinitionException<\/code> \u6216\u8005 <code>A component required a bean of type '...mapper.UserMapper' that could not be found<\/code> \u7684\u5b57\u6837\uff0c\u90a3\u5c31\u5b8c\u5168\u5370\u8bc1\u4e86\u6211\u7684\u731c\u60f3\u3002<\/p>\n<h3>\u7b2c\u4e8c\u6b65\uff1a\u8865\u5145 @MapperScan \u6ce8\u89e3\uff08\u5927\u6982\u7387\u7684\u89e3\u51b3\u65b9\u6848\uff09<\/h3>\n<p>\u6211\u4eec\u9700\u8981\u544a\u8bc9 Spring Boot \u53bb\u54ea\u91cc\u627e\u521a\u624d\u81ea\u52a8\u751f\u6210\u7684\u6570\u636e\u5e93\u64cd\u4f5c\u4ee3\u7801\uff08Mapper\uff09\u3002<br \/>\n\u8bf7\u6253\u5f00 Spring Boot \u7684\u4e3b\u542f\u52a8\u7c7b\uff1a<\/p>\n<pre><code class=\"language-bash\">vim src\/main\/java\/vip\/crabpt\/navidrome\/NavidromeServerApplication.java\r\n<\/code><\/pre>\n<p>\u628a\u91cc\u9762\u7684\u5185\u5bb9\u4fee\u6539\u6210\u4e0b\u9762\u8fd9\u6837\uff08\u6ce8\u610f\u7b2c 4 \u884c\u548c\u7b2c 7 \u884c\uff0c\u6211\u4eec\u5f15\u5165\u4e86 <code>@MapperScan<\/code> \u5e76\u6307\u5b9a\u4e86\u5305\u540d\uff09\uff1a<\/p>\n<pre><code class=\"language-java\">package vip.crabpt.navidrome;\r\n\r\nimport org.mybatis.spring.annotation.MapperScan;\r\nimport org.springframework.boot.SpringApplication;\r\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\r\n\r\n@SpringBootApplication\r\n@MapperScan(\"vip.crabpt.navidrome.mapper\") \/\/ \u52a0\u4e0a\u8fd9\u4e00\u884c\uff0c\u544a\u8bc9 Spring \u53bb\u54ea\u91cc\u627e Mapper\r\npublic class NavidromeServerApplication {\r\n\r\n    public static void main(String[] args) {\r\n        SpringApplication.run(NavidromeServerApplication.class, args);\r\n    }\r\n\r\n}\r\n<\/code><\/pre>\n<p>\u4fee\u6539\u597d\u540e\uff0c\u6309 <code>Esc<\/code>\uff0c\u8f93\u5165 <code>:wq<\/code> \u4fdd\u5b58\u5e76\u9000\u51fa\u3002<\/p>\n<h3>\u7b2c\u4e09\u6b65\uff1a\u524d\u53f0\u542f\u52a8\uff08\u65b9\u4fbf\u6392\u9519\uff09<\/h3>\n<p>\u4e3a\u4e86\u65b9\u4fbf\u6211\u4eec\u7acb\u523b\u770b\u5230\u62a5\u9519\uff08\u800c\u4e0d\u662f\u53bb\u7ffb\u65e5\u5fd7\uff09\uff0c\u8fd9\u6b21\u6211\u4eec<strong>\u4e0d\u8981\u7528 nohup<\/strong>\uff0c\u76f4\u63a5\u5728\u524d\u53f0\u542f\u52a8\u5b83\u3002\u8fd9\u6837\u5982\u679c\u518d\u62a5\u9519\uff0c\u7ea2\u8272\u7684\u9519\u8bef\u4fe1\u606f\u4f1a\u76f4\u63a5\u7838\u5728\u4f60\u7684\u5c4f\u5e55\u4e0a\u3002<br \/>\n\u5728\u7ec8\u7aef\u6267\u884c\uff1a<\/p>\n<pre><code class=\"language-bash\">.\/mvnw spring-boot:run\r\n<\/code><\/pre>\n<ul>\n<li><strong>\u5982\u679c\u6210\u529f\uff1a<\/strong> \u65e5\u5fd7\u4f1a\u4e0d\u65ad\u5f80\u4e0b\u6eda\uff0c\u5e76\u5728\u6700\u540e\u505c\u7559\u5728\u7c7b\u4f3c <code>Started NavidromeServerApplication in X.XXX seconds<\/code> \u7684\u5b57\u6837\uff0c\u5e76\u4e14\u5149\u6807\u4f1a\u5361\u5728\u90a3\u91cc\u3002\u8fd9\u5c31\u8bf4\u660e\u6210\u529f\u4e86\uff01\u4f60\u53ef\u4ee5\u518d\u5f00\u4e00\u4e2a SSH \u7a97\u53e3\uff08\u4e0d\u8981\u5173\u6389\u5f53\u524d\u7a97\u53e3\uff09\uff0c\u53bb\u6267\u884c\u6211\u4eec\u4e4b\u524d\u7684 <code>curl<\/code> \u6d4b\u8bd5\u547d\u4ee4\u3002<\/li>\n<li><strong>\u5982\u679c\u5931\u8d25\uff1a<\/strong> \u5c4f\u5e55\u4e0a\u4f1a\u6253\u5370\u51fa\u4e00\u5927\u5806\u62a5\u9519\uff08Exception\uff09\u3002\u8bf7\u628a\u62a5\u9519\u4fe1\u606f\u6700\u4e0a\u9762\u6216\u8005\u6700\u4e0b\u9762\u7684\u51e0\u884c\u5173\u952e\u82f1\u6587\u590d\u5236\u53d1\u7ed9\u6211\uff0c\u6211\u6765\u5e2e\u4f60\u201c\u5bf9\u75c7\u4e0b\u836f\u201d\uff01<\/li>\n<\/ul>\n<h2>Turn 18<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<blockquote><p>\u7b2c\u4e00\u6b65\uff1a\u67e5\u770b\u786e\u5207\u7684\u62a5\u9519\u4fe1\u606f\u5728\u7ec8\u7aef\u8f93\u5165\u4ee5\u4e0b\u547d\u4ee4\uff0c\u67e5\u770b\u65e5\u5fd7\u7684\u6700\u540e 50 \u884c\uff1aBashtail -n 50 app.log<br \/>\n\u5982\u679c\u4f60\u5728\u65e5\u5fd7\u91cc\u770b\u5230\u4e86\u7c7b\u4f3c NoSuchBeanDefinitionException \u6216\u8005 A component required a bean of type &#8216;&#8230;mapper.UserMapper&#8217; that could not be found \u7684\u5b57\u6837\uff0c\u90a3\u5c31\u5b8c\u5168\u5370\u8bc1\u4e86\u6211\u7684\u731c\u60f3\u3002<br \/>\nroot@192-227-164-42-host:\/mnt\/navidrome-system\/backend\/navidrome-server# tail -n 50 app.log<br \/>\nsymbol: class ApiModelProperty<br \/>\nlocation: class vip.crabpt.navidrome.entity.PointLog<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/entity\/PointLog.java:[35,6] cannot find symbol<br \/>\nsymbol: class ApiModelProperty<br \/>\nlocation: class vip.crabpt.navidrome.entity.PointLog<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/entity\/PointLog.java:[38,6] cannot find symbol<br \/>\nsymbol: class ApiModelProperty<br \/>\nlocation: class vip.crabpt.navidrome.entity.PointLog<br \/>\n[INFO] 12 errors<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[INFO] BUILD FAILURE<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[INFO] Total time: 2.745 s<br \/>\n[INFO] Finished at: 2026-04-14T01:47:49-04:00<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.14.1:compile (default-compile) on project navidrome-server: Compilation failure: Compilation failure:<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/entity\/InviteCode.java:[8,30] package io.swagger.annotations does not exist<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/entity\/InviteCode.java:[9,30] package io.swagger.annotations does not exist<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/entity\/InviteCode.java:[24,2] cannot find symbol<br \/>\n[ERROR] symbol: class ApiModel<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/entity\/PointLog.java:[8,30] package io.swagger.annotations does not exist<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/entity\/PointLog.java:[9,30] package io.swagger.annotations does not exist<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/entity\/PointLog.java:[24,2] cannot find symbol<br \/>\n[ERROR] symbol: class ApiModel<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/entity\/InviteCode.java:[34,6] cannot find symbol<br \/>\n[ERROR] symbol: class ApiModelProperty<br \/>\n[ERROR] location: class vip.crabpt.navidrome.entity.InviteCode<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/entity\/InviteCode.java:[37,6] cannot find symbol<br \/>\n[ERROR] symbol: class ApiModelProperty<br \/>\n[ERROR] location: class vip.crabpt.navidrome.entity.InviteCode<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/entity\/InviteCode.java:[40,6] cannot find symbol<br \/>\n[ERROR] symbol: class ApiModelProperty<br \/>\n[ERROR] location: class vip.crabpt.navidrome.entity.InviteCode<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/entity\/PointLog.java:[32,6] cannot find symbol<br \/>\n[ERROR] symbol: class ApiModelProperty<br \/>\n[ERROR] location: class vip.crabpt.navidrome.entity.PointLog<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/entity\/PointLog.java:[35,6] cannot find symbol<br \/>\n[ERROR] symbol: class ApiModelProperty<br \/>\n[ERROR] location: class vip.crabpt.navidrome.entity.PointLog<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/entity\/PointLog.java:[38,6] cannot find symbol<br \/>\n[ERROR] symbol: class ApiModelProperty<br \/>\n[ERROR] location: class vip.crabpt.navidrome.entity.PointLog<br \/>\n[ERROR] -&gt; [Help 1]<br \/>\n[ERROR]<br \/>\n[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.<br \/>\n[ERROR] Re-run Maven using the -X switch to enable full debug logging.<br \/>\n[ERROR]<br \/>\n[ERROR] For more information about the errors and possible solutions, please read the following articles:<br \/>\n[ERROR] [Help 1] http:\/\/cwiki.apache.org\/confluence\/display\/MAVEN\/MojoFailureException<\/p><\/blockquote>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u539f\u6765\u5982\u6b64\uff01\u4f60\u505a\u5f97\u975e\u5e38\u68d2\uff0c\u628a\u62a5\u9519\u4fe1\u606f\u8d34\u51fa\u6765\uff0c\u8fd9\u7edd\u5bf9\u662f\u6807\u51c6\u5f00\u53d1\u8005\u7684\u597d\u4e60\u60ef\uff01<br \/>\n\u6211\u4e00\u773c\u5c31\u770b\u51fa\u4e86\u95ee\u9898\u6240\u5728\uff1a<br \/>\n\u62a5\u9519\u4fe1\u606f\u91cc\u5199\u7740 <code>package io.swagger.annotations does not exist<\/code> \u548c <code>cannot find symbol class ApiModelProperty<\/code>\u3002<br \/>\n<strong>\u539f\u56e0\u5206\u6790\uff1a<\/strong><br \/>\n\u8fd8\u8bb0\u5f97\u6211\u4eec\u4e4b\u524d\u5199\u7684\u90a3\u4e2a\u201c\u4ee3\u7801\u751f\u6210\u5668\u201d\u811a\u672c\u5417\uff1f\u91cc\u9762\u6709\u4e00\u884c <code>.enableSwagger()<\/code>\u3002\u8fd9\u4e2a\u914d\u7f6e\u4f1a\u8ba9\u751f\u6210\u7684\u4ee3\u7801\u81ea\u52a8\u5e26\u4e0a Swagger\uff08API\u63a5\u53e3\u6587\u6863\uff09\u7684\u6ce8\u89e3\u3002\u4f46\u662f\uff0c\u6211\u4eec\u5728 <code>pom.xml<\/code> \u91cc<strong>\u5e76\u6ca1\u6709\u5f15\u5165 Swagger \u7684\u4f9d\u8d56\u5305<\/strong>\uff01<br \/>\n\u4e4b\u524d\u6211\u4eec\u624b\u52a8\u91cd\u5199\u4e86 <code>User.java<\/code> \u548c <code>Checkin.java<\/code>\uff0c\u628a\u91cc\u9762\u7684 Swagger \u6ce8\u89e3\u5220\u6389\u4e86\uff0c\u6240\u4ee5\u5b83\u4eec\u6ca1\u4e8b\u3002\u4f46\u5269\u4e0b\u7684 <code>InviteCode.java<\/code> \u548c <code>PointLog.java<\/code> \u91cc\u9762\u8fd8\u6b8b\u7559\u7740\u8fd9\u4e9b\u6ce8\u89e3\uff0c\u5bfc\u81f4 Java \u7f16\u8bd1\u65f6\u8bf4\uff1a\u201c\u6211\u4e0d\u8ba4\u8bc6\u8fd9\u4e2a\u53eb Swagger \u7684\u4e1c\u897f\uff01\u201d\u3002<br \/>\n<strong>\u89e3\u51b3\u529e\u6cd5\u975e\u5e38\u7b80\u5355\uff1a<\/strong><br \/>\n\u6211\u4eec\u76f4\u63a5\u628a\u8fd9\u4e24\u4e2a\u6587\u4ef6\u4e5f\u201c\u6e05\u6d17\u201d\u4e00\u4e0b\uff0c\u53bb\u6389\u90a3\u4e9b\u591a\u4f59\u7684\u6ce8\u89e3\u5373\u53ef\u3002\u8bf7\u6309\u7167\u4e0b\u9762\u7684\u6307\u4ee4\uff0c\u8986\u76d6\u8fd9\u4e24\u4e2a\u6587\u4ef6\u7684\u5185\u5bb9\uff1a<\/p>\n<h3>1. \u6e05\u6d17 InviteCode \u5b9e\u4f53\u7c7b<\/h3>\n<pre><code class=\"language-bash\">vim src\/main\/java\/vip\/crabpt\/navidrome\/entity\/InviteCode.java\r\n<\/code><\/pre>\n<p>\u5c06\u91cc\u9762\u7684\u5185\u5bb9\u5168\u90e8<strong>\u66ff\u6362<\/strong>\u4e3a\u4ee5\u4e0b\u7eaf\u51c0\u7248\u4ee3\u7801\uff1a<\/p>\n<pre><code class=\"language-java\">package vip.crabpt.navidrome.entity;\r\n\r\nimport com.baomidou.mybatisplus.annotation.IdType;\r\nimport com.baomidou.mybatisplus.annotation.TableId;\r\nimport com.baomidou.mybatisplus.annotation.TableName;\r\nimport lombok.Data;\r\nimport java.time.LocalDateTime;\r\n\r\n@Data\r\n@TableName(\"sys_invite_code\")\r\npublic class InviteCode {\r\n    @TableId(type = IdType.AUTO)\r\n    private Long id;\r\n    private String code;\r\n    private Long creatorId;\r\n    private Long usedById;\r\n    private String status;\r\n    private LocalDateTime expireTime;\r\n    private LocalDateTime createdAt;\r\n}\r\n<\/code><\/pre>\n<p>\u4fdd\u5b58\u9000\u51fa\uff08\u6309 <code>Esc<\/code>\uff0c\u8f93\u5165 <code>:wq<\/code>\uff0c\u56de\u8f66\uff09\u3002<\/p>\n<h3>2. \u6e05\u6d17 PointLog \u5b9e\u4f53\u7c7b<\/h3>\n<pre><code class=\"language-bash\">vim src\/main\/java\/vip\/crabpt\/navidrome\/entity\/PointLog.java\r\n<\/code><\/pre>\n<p>\u5c06\u91cc\u9762\u7684\u5185\u5bb9\u5168\u90e8<strong>\u66ff\u6362<\/strong>\u4e3a\u4ee5\u4e0b\u7eaf\u51c0\u7248\u4ee3\u7801\uff1a<\/p>\n<pre><code class=\"language-java\">package vip.crabpt.navidrome.entity;\r\n\r\nimport com.baomidou.mybatisplus.annotation.IdType;\r\nimport com.baomidou.mybatisplus.annotation.TableId;\r\nimport com.baomidou.mybatisplus.annotation.TableName;\r\nimport lombok.Data;\r\nimport java.time.LocalDateTime;\r\n\r\n@Data\r\n@TableName(\"sys_point_log\")\r\npublic class PointLog {\r\n    @TableId(type = IdType.AUTO)\r\n    private Long id;\r\n    private Long userId;\r\n    private String action;\r\n    private Integer pointsChange;\r\n    private String description;\r\n    private LocalDateTime createdAt;\r\n}\r\n<\/code><\/pre>\n<p>\u4fdd\u5b58\u9000\u51fa\uff08\u6309 <code>Esc<\/code>\uff0c\u8f93\u5165 <code>:wq<\/code>\uff0c\u56de\u8f66\uff09\u3002<\/p>\n<hr \/>\n<h3>3. \u518d\u6b21\u524d\u53f0\u542f\u52a8\u6d4b\u8bd5\uff01<\/h3>\n<p>\u73b0\u5728\u6240\u6709\u7684 Swagger \u6bd2\u7624\u90fd\u88ab\u6e05\u7406\u5e72\u51c0\u4e86\uff01\u4e3a\u4e86\u65b9\u4fbf\u6211\u4eec\u7acb\u523b\u770b\u5230\u7ed3\u679c\uff0c\u8fd9\u6b21\u6211\u4eec\u5728<strong>\u524d\u53f0\u8fd0\u884c<\/strong>\uff1a<\/p>\n<pre><code class=\"language-bash\">.\/mvnw spring-boot:run\r\n<\/code><\/pre>\n<p>\u7b49\u5927\u6982\u5341\u51e0\u79d2\uff0c\u53ea\u8981\u4f60\u770b\u5230\u63a7\u5236\u53f0\u6700\u540e\u505c\u5728\u7c7b\u4f3c\uff1a<br \/>\n<code>Started NavidromeServerApplication in 3.123 seconds<\/code><br \/>\n\u5e76\u4e14<strong>\u5149\u6807\u5361\u5728\u90a3\u91cc\u4e0d\u518d\u8df3\u51fa ERROR<\/strong>\uff0c\u5c31\u8bf4\u660e\u9879\u76ee\u5b8c\u7f8e\u542f\u52a8\u4e86\uff01<br \/>\n\u8fd9\u65f6\u5019\u4e0d\u8981\u5173\u6389\u8fd9\u4e2a\u7ec8\u7aef\u7a97\u53e3\uff08\u6309 <code>Ctrl+C<\/code> \u4f1a\u505c\u6b62\u7a0b\u5e8f\uff09\uff0c\u4f60\u53ef\u4ee5<strong>\u65b0\u5f00\u4e00\u4e2a SSH \u7a97\u53e3<\/strong>\u8fde\u63a5\u4f60\u7684\u670d\u52a1\u5668\uff0c\u53bb\u8dd1\u90a3\u51e0\u4e2a <code>curl<\/code> \u547d\u4ee4\u6d4b\u8bd5\u767b\u5f55\u548c\u7b7e\u5230\u529f\u80fd\u5566\uff01<br \/>\n\u7b49\u4f60\u6d4b\u8bd5\u6210\u529f\uff0c\u6211\u4eec\u5c31\u4e58\u80dc\u8ffd\u51fb\uff0c\u628a\u751f\u6210\u9080\u8bf7\u7801\u7684\u903b\u8f91\u5199\u51fa\u6765\uff01<\/p>\n<h2>Turn 19<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<blockquote>\n<ol start=\"3\">\n<li>\u518d\u6b21\u524d\u53f0\u542f\u52a8\u6d4b\u8bd5\uff01\u73b0\u5728\u6240\u6709\u7684 Swagger \u6bd2\u7624\u90fd\u88ab\u6e05\u7406\u5e72\u51c0\u4e86\uff01\u4e3a\u4e86\u65b9\u4fbf\u6211\u4eec\u7acb\u523b\u770b\u5230\u7ed3\u679c\uff0c\u8fd9\u6b21\u6211\u4eec\u5728\u524d\u53f0\u8fd0\u884c\uff1aBash.\/mvnw spring-boot:run<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&lt; vip.crabpt.navidrome:navidrome-server &gt;&#8212;&#8212;&#8212;&#8212;&#8212;-<br \/>\n[INFO] Building navidrome-server 0.0.1-SNAPSHOT<br \/>\n[INFO] from pom.xml<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;[ jar ]&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[INFO]<br \/>\n[INFO] &gt;&gt;&gt; spring-boot:4.0.5:run (default-cli) &gt; test-compile @ navidrome-server &gt;&gt;&gt;<br \/>\n[INFO]<br \/>\n[INFO] &#8212; resources:3.3.1:resources (default-resources) @ navidrome-server &#8212;<br \/>\n[INFO] Copying 1 resource from src\/main\/resources to target\/classes<br \/>\n[INFO] Copying 4 resources from src\/main\/resources to target\/classes<br \/>\n[INFO]<br \/>\n[INFO] &#8212; compiler:3.14.1:compile (default-compile) @ navidrome-server &#8212;<br \/>\n[INFO] Recompiling the module because of changed source code.<br \/>\n[INFO] Compiling 23 source files with javac [debug parameters release 17] to target\/classes<br \/>\n[INFO]<br \/>\n[INFO] &#8212; resources:3.3.1:testResources (default-testResources) @ navidrome-server &#8212;<br \/>\n[INFO] skip non existing resourceDirectory \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/test\/resources<br \/>\n[INFO]<br \/>\n[INFO] &#8212; compiler:3.14.1:testCompile (default-testCompile) @ navidrome-server &#8212;<br \/>\n[INFO] Recompiling the module because of changed dependency.<br \/>\n[INFO] Compiling 2 source files with javac [debug parameters release 17] to target\/test-classes<br \/>\n[INFO]<br \/>\n[INFO] &lt;&lt;&lt; spring-boot:4.0.5:run (default-cli) &lt; test-compile @ navidrome-server &lt;&lt;&lt;<br \/>\n[INFO]<br \/>\n[INFO]<br \/>\n[INFO] &#8212; spring-boot:4.0.5:run (default-cli) @ navidrome-server &#8212;<br \/>\n[INFO] Attaching agents: []<br \/>\n. ____ _ __ _ _<br \/>\n\/\\ \/ <em><em><em>&#8216;<\/em> __ _ <em>(<\/em>)<\/em> __ __ _ \\ \\<br \/>\n( ( )_<\/em>_ | &#8216;_ | &#8216;<em>| | &#8216;<\/em> \/ <em>` | \\ \\<br \/>\n\\\/ <em><em><em>)| |<\/em>)| | | | | || (<\/em>| | ) ) ) )<br \/>\n&#8216; |<\/em><\/em><em><em>| .__|<\/em>| |<\/em>|<em>| |<\/em>_<em>, | \/ \/ \/ \/<br \/>\n=========|<\/em>|==============|<em><em><em>\/=\/<\/em>\/<\/em>\/<\/em>\/<br \/>\n:: Spring Boot :: (v4.0.5)<br \/>\n2026-04-14T01:51:40.363-04:00 INFO 1292319 &#8212; [ main] v.c.n.NavidromeServerApplication : Starting NavidromeServerApplication using Java 17.0.18 with PID 1292319 (\/mnt\/navidrome-system\/backend\/navidrome-server\/target\/classes started by root in \/mnt\/navidrome-system\/backend\/navidrome-server)<br \/>\n2026-04-14T01:51:40.366-04:00 INFO 1292319 &#8212; [ main] v.c.n.NavidromeServerApplication : No active profile set, falling back to 1 default profile: &#8220;default&#8221;<br \/>\n2026-04-14T01:51:41.124-04:00 INFO 1292319 &#8212; [ main] o.s.boot.tomcat.TomcatWebServer : Tomcat initialized with port 8001 (http)<br \/>\n2026-04-14T01:51:41.134-04:00 INFO 1292319 &#8212; [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]<br \/>\n2026-04-14T01:51:41.134-04:00 INFO 1292319 &#8212; [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat\/11.0.20]<br \/>\n2026-04-14T01:51:41.168-04:00 INFO 1292319 &#8212; [ main] b.w.c.s.WebApplicationContextInitializer : Root WebApplicationContext: initialization completed in 761 ms<br \/>\n2026-04-14T01:51:41.237-04:00 WARN 1292319 &#8212; [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization &#8211; cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name &#8216;authController&#8217;: Unsatisfied dependency expressed through field &#8216;userService&#8217;: Error creating bean with name &#8216;userServiceImpl&#8217;: Unsatisfied dependency expressed through field &#8216;baseMapper&#8217;: No qualifying bean of type &#8216;vip.crabpt.navidrome.mapper.UserMapper&#8217; available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}<br \/>\n2026-04-14T01:51:41.239-04:00 INFO 1292319 &#8212; [ main] o.apache.catalina.core.StandardService : Stopping service [Tomcat]<br \/>\n2026-04-14T01:51:41.250-04:00 INFO 1292319 &#8212; [ main] .s.b.a.l.ConditionEvaluationReportLogger :<br \/>\nError starting ApplicationContext. To display the condition evaluation report re-run your application with &#8216;debug&#8217; enabled.<br \/>\n2026-04-14T01:51:41.262-04:00 ERROR 1292319 &#8212; [ main] o.s.boot.SpringApplication : Application run failed<br \/>\norg.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name &#8216;authController&#8217;: Unsatisfied dependency expressed through field &#8216;userService&#8217;: Error creating bean with name &#8216;userServiceImpl&#8217;: Unsatisfied dependency expressed through field &#8216;baseMapper&#8217;: No qualifying bean of type &#8216;vip.crabpt.navidrome.mapper.UserMapper&#8217; available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}<br \/>\nat org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:767) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:748) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:146) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1446) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:602) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:525) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:333) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:371) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:331) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:196) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1218) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1184) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1121) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:994) ~[spring-context-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:621) ~[spring-context-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.boot.web.server.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143) ~[spring-boot-web-server-4.0.5.jar:4.0.5]<br \/>\nat org.springframework.boot.SpringApplication.refresh(SpringApplication.java:756) ~[spring-boot-4.0.5.jar:4.0.5]<br \/>\nat org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:445) ~[spring-boot-4.0.5.jar:4.0.5]<br \/>\nat org.springframework.boot.SpringApplication.run(SpringApplication.java:321) ~[spring-boot-4.0.5.jar:4.0.5]<br \/>\nat org.springframework.boot.SpringApplication.run(SpringApplication.java:1365) ~[spring-boot-4.0.5.jar:4.0.5]<br \/>\nat org.springframework.boot.SpringApplication.run(SpringApplication.java:1354) ~[spring-boot-4.0.5.jar:4.0.5]<br \/>\nat vip.crabpt.navidrome.NavidromeServerApplication.main(NavidromeServerApplication.java:10) ~[classes\/:na]<br \/>\nCaused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name &#8216;userServiceImpl&#8217;: Unsatisfied dependency expressed through field &#8216;baseMapper&#8217;: No qualifying bean of type &#8216;vip.crabpt.navidrome.mapper.UserMapper&#8217; available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}<br \/>\nat org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:767) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:748) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:146) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1446) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:602) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:525) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:333) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:371) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:331) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:201) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:229) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1762) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1651) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:764) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\n&#8230; 22 common frames omitted<br \/>\nCaused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type &#8216;vip.crabpt.navidrome.mapper.UserMapper&#8217; available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}<br \/>\nat org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:2297) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1725) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1651) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\nat org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:764) ~[spring-beans-7.0.6.jar:7.0.6]<br \/>\n&#8230; 36 common frames omitted<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[INFO] BUILD FAILURE<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[INFO] Total time: 5.126 s<br \/>\n[INFO] Finished at: 2026-04-14T01:51:41-04:00<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:4.0.5:run (default-cli) on project navidrome-server: Process terminated with exit code: 1 -&gt; [Help 1]<br \/>\n[ERROR]<br \/>\n[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.<br \/>\n[ERROR] Re-run Maven using the -X switch to enable full debug logging.<br \/>\n[ERROR]<br \/>\n[ERROR] For more information about the errors and possible solutions, please read the following articles:<br \/>\n[ERROR] [Help 1] http:\/\/cwiki.apache.org\/confluence\/display\/MAVEN\/MojoExecutionException<\/li>\n<\/ol>\n<\/blockquote>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u8fd9\u4e2a\u62a5\u9519\u975e\u5e38\u6709\u610f\u601d\uff0c\u4f60\u8fd9\u53ef\u662f\u8e29\u5230\u4e86\u4e00\u4e2a\u6781\u5176\u7f55\u89c1\u7684\u201c\u65f6\u7a7a\u7ea7\u201d\u5751\uff01<br \/>\n\u6211\u4ed4\u7ec6\u770b\u4e86\u4e00\u4e0b\u4f60\u53d1\u51fa\u6765\u7684\u542f\u52a8\u65e5\u5fd7\uff0c\u7834\u6848\u4e86\u3002\u8bf7\u6ce8\u610f\u65e5\u5fd7\u91cc\u7684\u8fd9\u4e00\u884c\uff1a<br \/>\n<code>:: Spring Boot :: (v4.0.5)<\/code><br \/>\n<strong>\u4e3a\u4ec0\u4e48\u4f1a\u62a5\u9519\uff1f<\/strong><br \/>\n\u6211\u4eec\u5728\u524d\u4e00\u6b65\u4e0b\u8f7d\u57fa\u7840\u9879\u76ee\u65f6\uff0c\u53bb\u6389\u4e86\u7248\u672c\u53f7\u9650\u5236\uff0c\u7ed3\u679c Spring \u5b98\u65b9\u628a\u6700\u65b0\u53d1\u5e03\u7684 <strong>Spring Boot 4.0.5<\/strong>\uff08\u4f34\u968f Spring 7.x\uff09\u5206\u914d\u7ed9\u4e86\u6211\u4eec\uff01<br \/>\n\u4f46\u662f\uff0c\u6211\u4eec\u624b\u52a8\u5728 <code>pom.xml<\/code> \u91cc\u5f15\u5165\u7684\u6570\u636e\u5e93\u6301\u4e45\u5c42\u6846\u67b6\u53eb <code>mybatis-plus-spring-boot3-starter<\/code>\uff0c\u987e\u540d\u601d\u4e49\uff0c\u5b83\u662f\u4e13\u95e8\u7ed9 <strong>Spring Boot 3.x<\/strong> \u8bbe\u8ba1\u7684\u3002<br \/>\n\u8fd9\u79cd\u201c\u8de8\u4ee3\u9645\u201d\u7684\u7248\u672c\u51b2\u7a81\uff0c\u5bfc\u81f4 Spring Boot 4 \u62d2\u7edd\u52a0\u8f7d MyBatis-Plus \u7684\u81ea\u52a8\u914d\u7f6e\uff0c\u6240\u4ee5\u5b83\u6b7b\u6d3b\u627e\u4e0d\u5230\u6570\u636e\u5e93\u7684 Mapper\uff08<code>No qualifying bean of type '...UserMapper' available<\/code>\uff09\u3002<br \/>\n<strong>\u89e3\u51b3\u529e\u6cd5\u6781\u5176\u7b80\u5355\uff1a<\/strong><br \/>\n\u6211\u4eec\u4e0d\u9700\u8981\u91cd\u65b0\u4e0b\u8f7d\u9879\u76ee\uff0c\u53ea\u9700\u8981\u5728 <code>pom.xml<\/code> \u91cc\u628a Spring Boot \u7684\u7248\u672c\u53f7\u201c\u964d\u7ea7\u201d\u56de\u7a33\u5982\u8001\u72d7\u7684 <code>3.2.4<\/code> \u5373\u53ef\u3002<br \/>\n\u8bf7\u6267\u884c\u4ee5\u4e0b\u4e24\u6b65\u64cd\u4f5c\uff1a<\/p>\n<h3>\u7b2c\u4e00\u6b65\uff1a\u4fee\u6539 pom.xml \u7248\u672c\u53f7<\/h3>\n<p>\u6253\u5f00\u914d\u7f6e\u6587\u4ef6\uff1a<\/p>\n<pre><code class=\"language-bash\">vim pom.xml\r\n<\/code><\/pre>\n<p>\u5728\u6587\u4ef6\u7684\u6700\u4e0a\u9762\uff08\u5927\u6982\u7b2c 10 \u5230 15 \u884c\u4e4b\u95f4\uff09\uff0c\u627e\u5230 <code>&lt;parent&gt;<\/code> \u6807\u7b7e\u3002\u628a\u5b83\u91cc\u9762\u7684 <code>&lt;version&gt;<\/code> \u4ece <code>4.0.5<\/code>\uff08\u6216\u5176\u4ed6 4.x \u7248\u672c\uff09\u6539\u6210 <code>3.2.4<\/code>\u3002\u4fee\u6539\u540e\u957f\u8fd9\u6837\uff1a<\/p>\n<pre><code class=\"language-xml\">&lt;parent&gt;\r\n        &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n        &lt;artifactId&gt;spring-boot-starter-parent&lt;\/artifactId&gt;\r\n        &lt;version&gt;3.2.4&lt;\/version&gt;  &lt;relativePath\/&gt; &lt;\/parent&gt;\r\n<\/code><\/pre>\n<p>\u6309 <code>Esc<\/code>\uff0c\u8f93\u5165 <code>:wq<\/code> \u4fdd\u5b58\u9000\u51fa\u3002<\/p>\n<h3>\u7b2c\u4e8c\u6b65\uff1a\u6e05\u7406\u65e7\u6587\u4ef6\u5e76\u91cd\u65b0\u542f\u52a8<\/h3>\n<p>\u56e0\u4e3a\u521a\u624d\u7528 4.x \u7248\u672c\u7f16\u8bd1\u8fc7\u4e00\u6b21\uff0c\u6211\u4eec\u9700\u8981\u52a0\u4e0a <code>clean<\/code> \u53c2\u6570\uff0c\u628a\u65e7\u7684\u7f16\u8bd1\u6b8b\u9ab8\u626b\u5e72\u51c0\uff0c\u7136\u540e\u91cd\u65b0\u542f\u52a8\uff1a<\/p>\n<pre><code class=\"language-bash\">.\/mvnw clean spring-boot:run\r\n<\/code><\/pre>\n<p>\u8fd9\u4e00\u6b21\uff0cMaven \u4f1a\u81ea\u52a8\u53bb\u62c9\u53d6\u9002\u914d <code>3.2.4<\/code> \u7684\u4f9d\u8d56\u5305\u3002\u7b49\u5b83\u8dd1\u5b8c\uff0c\u53ea\u8981\u5c4f\u5e55\u6700\u540e\u505c\u5728 <code>Started NavidromeServerApplication in ...<\/code> \u5e76\u4e14\u4e0d\u518d\u62a5\u9519\u9000\u51fa\uff0c\u5c31\u4ee3\u8868\u6210\u529f\u4e86\uff01<br \/>\n\u6210\u529f\u540e\uff0c\u4f60\u5c31\u53ef\u4ee5\u65b0\u5f00\u4e00\u4e2a SSH \u7a97\u53e3\uff0c\u53bb\u6d4b\u8bd5\u6211\u4eec\u7684 <code>curl<\/code> \u767b\u5f55\u548c\u7b7e\u5230\u63a5\u53e3\u5566\uff01\u5feb\u53bb\u8bd5\u8bd5\u5427\uff01<\/p>\n<h2>Turn 20<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>\u5148\u89e3\u51b3\u8fd9\u4e2a\u95ee\u9898\uff1a<br \/>\n[ERROR] [Help 1] http:\/\/cwiki.apache.org\/confluence\/display\/MAVEN\/MojoExecutionException<br \/>\nroot@192-227-164-42-host:\/mnt\/navidrome-system\/backend\/navidrome-server# .\/mvnw clean spring-boot:run<br \/>\n[INFO] Scanning for projects&#8230;<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/springframework\/boot\/spring-boot-starter-parent\/3.2.4\/spring-boot-starter-parent-3.2.4.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/springframework\/boot\/spring-boot-starter-parent\/3.2.4\/spring-boot-starter-parent-3.2.4.pom (13 kB at 49 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/springframework\/boot\/spring-boot-dependencies\/3.2.4\/spring-boot-dependencies-3.2.4.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/springframework\/boot\/spring-boot-dependencies\/3.2.4\/spring-boot-dependencies-3.2.4.pom (111 kB at 2.4 MB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/io\/dropwizard\/metrics\/metrics-bom\/4.2.25\/metrics-bom-4.2.25.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/io\/dropwizard\/metrics\/metrics-bom\/4.2.25\/metrics-bom-4.2.25.pom (8.2 kB at 328 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/io\/dropwizard\/metrics\/metrics-parent\/4.2.25\/metrics-parent-4.2.25.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/io\/dropwizard\/metrics\/metrics-parent\/4.2.25\/metrics-parent-4.2.25.pom (21 kB at 667 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/glassfish\/jaxb\/jaxb-bom\/4.0.5\/jaxb-bom-4.0.5.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/glassfish\/jaxb\/jaxb-bom\/4.0.5\/jaxb-bom-4.0.5.pom (12 kB at 416 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/groovy\/groovy-bom\/4.0.20\/groovy-bom-4.0.20.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/groovy\/groovy-bom\/4.0.20\/groovy-bom-4.0.20.pom (27 kB at 909 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/infinispan\/infinispan-bom\/14.0.27.Final\/infinispan-bom-14.0.27.Final.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/infinispan\/infinispan-bom\/14.0.27.Final\/infinispan-bom-14.0.27.Final.pom (25 kB at 909 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/infinispan\/infinispan-build-configuration-parent\/14.0.27.Final\/infinispan-build-configuration-parent-14.0.27.Final.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/infinispan\/infinispan-build-configuration-parent\/14.0.27.Final\/infinispan-build-configuration-parent-14.0.27.Final.pom (24 kB at 440 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/com\/fasterxml\/jackson\/jackson-bom\/2.15.4\/jackson-bom-2.15.4.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/com\/fasterxml\/jackson\/jackson-bom\/2.15.4\/jackson-bom-2.15.4.pom (18 kB at 782 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/glassfish\/jersey\/jersey-bom\/3.1.5\/jersey-bom-3.1.5.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/glassfish\/jersey\/jersey-bom\/3.1.5\/jersey-bom-3.1.5.pom (21 kB at 829 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/eclipse\/jetty\/ee10\/jetty-ee10-bom\/12.0.7\/jetty-ee10-bom-12.0.7.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/eclipse\/jetty\/ee10\/jetty-ee10-bom\/12.0.7\/jetty-ee10-bom-12.0.7.pom (9.2 kB at 319 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/eclipse\/jetty\/jetty-bom\/12.0.7\/jetty-bom-12.0.7.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/eclipse\/jetty\/jetty-bom\/12.0.7\/jetty-bom-12.0.7.pom (14 kB at 564 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/jetbrains\/kotlin\/kotlin-bom\/1.9.23\/kotlin-bom-1.9.23.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/jetbrains\/kotlin\/kotlin-bom\/1.9.23\/kotlin-bom-1.9.23.pom (9.1 kB at 304 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/jetbrains\/kotlinx\/kotlinx-serialization-bom\/1.6.3\/kotlinx-serialization-bom-1.6.3.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/jetbrains\/kotlinx\/kotlinx-serialization-bom\/1.6.3\/kotlinx-serialization-bom-1.6.3.pom (3.7 kB at 147 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/io\/micrometer\/micrometer-bom\/1.12.4\/micrometer-bom-1.12.4.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/io\/micrometer\/micrometer-bom\/1.12.4\/micrometer-bom-1.12.4.pom (7.9 kB at 293 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/io\/micrometer\/micrometer-tracing-bom\/1.2.4\/micrometer-tracing-bom-1.2.4.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/io\/micrometer\/micrometer-tracing-bom\/1.2.4\/micrometer-tracing-bom-1.2.4.pom (4.5 kB at 161 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/io\/netty\/netty-bom\/4.1.107.Final\/netty-bom-4.1.107.Final.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/io\/netty\/netty-bom\/4.1.107.Final\/netty-bom-4.1.107.Final.pom (14 kB at 528 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/com\/oracle\/database\/jdbc\/ojdbc-bom\/21.9.0.0\/ojdbc-bom-21.9.0.0.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/com\/oracle\/database\/jdbc\/ojdbc-bom\/21.9.0.0\/ojdbc-bom-21.9.0.0.pom (13 kB at 468 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/io\/projectreactor\/reactor-bom\/2023.0.4\/reactor-bom-2023.0.4.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/io\/projectreactor\/reactor-bom\/2023.0.4\/reactor-bom-2023.0.4.pom (4.8 kB at 159 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/springframework\/amqp\/spring-amqp-bom\/3.1.3\/spring-amqp-bom-3.1.3.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/springframework\/amqp\/spring-amqp-bom\/3.1.3\/spring-amqp-bom-3.1.3.pom (3.9 kB at 138 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/springframework\/batch\/spring-batch-bom\/5.1.1\/spring-batch-bom-5.1.1.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/springframework\/batch\/spring-batch-bom\/5.1.1\/spring-batch-bom-5.1.1.pom (3.2 kB at 120 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/springframework\/data\/spring-data-bom\/2023.1.4\/spring-data-bom-2023.1.4.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/springframework\/data\/spring-data-bom\/2023.1.4\/spring-data-bom-2023.1.4.pom (5.5 kB at 212 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/springframework\/spring-framework-bom\/6.1.5\/spring-framework-bom-6.1.5.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/springframework\/spring-framework-bom\/6.1.5\/spring-framework-bom-6.1.5.pom (5.8 kB at 232 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/springframework\/integration\/spring-integration-bom\/6.2.3\/spring-integration-bom-6.2.3.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/springframework\/integration\/spring-integration-bom\/6.2.3\/spring-integration-bom-6.2.3.pom (10 kB at 419 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/springframework\/pulsar\/spring-pulsar-bom\/1.0.4\/spring-pulsar-bom-1.0.4.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/springframework\/pulsar\/spring-pulsar-bom\/1.0.4\/spring-pulsar-bom-1.0.4.pom (2.7 kB at 98 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/springframework\/security\/spring-security-bom\/6.2.3\/spring-security-bom-6.2.3.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/springframework\/security\/spring-security-bom\/6.2.3\/spring-security-bom-6.2.3.pom (5.3 kB at 197 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/springframework\/session\/spring-session-bom\/3.2.2\/spring-session-bom-3.2.2.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/springframework\/session\/spring-session-bom\/3.2.2\/spring-session-bom-3.2.2.pom (2.9 kB at 116 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/springframework\/ws\/spring-ws-bom\/4.0.10\/spring-ws-bom-4.0.10.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/springframework\/ws\/spring-ws-bom\/4.0.10\/spring-ws-bom-4.0.10.pom (3.5 kB at 135 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/testcontainers\/testcontainers-bom\/1.19.7\/testcontainers-bom-1.19.7.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/testcontainers\/testcontainers-bom\/1.19.7\/testcontainers-bom-1.19.7.pom (10 kB at 417 kB\/s)<br \/>\n[ERROR] [ERROR] Some problems were encountered while processing the POMs:<br \/>\n[ERROR] &#8216;dependencies.dependency.version&#8217; for org.springframework.boot:spring-boot-starter-webmvc:jar is missing. @ line 56, column 15<br \/>\n[ERROR] &#8216;dependencies.dependency.version&#8217; for org.springframework.boot:spring-boot-starter-validation-test:jar is missing. @ line 71, column 15<br \/>\n[ERROR] &#8216;dependencies.dependency.version&#8217; for org.springframework.boot:spring-boot-starter-webmvc-test:jar is missing. @ line 76, column 15<br \/>\n@<br \/>\n[ERROR] The build could not read 1 project -&gt; [Help 1]<br \/>\n[ERROR]<br \/>\n[ERROR] The project vip.crabpt.navidrome:navidrome-server:0.0.1-SNAPSHOT (\/mnt\/navidrome-system\/backend\/navidrome-server\/pom.xml) has 3 errors<br \/>\n[ERROR] &#8216;dependencies.dependency.version&#8217; for org.springframework.boot:spring-boot-starter-webmvc:jar is missing. @ line 56, column 15<br \/>\n[ERROR] &#8216;dependencies.dependency.version&#8217; for org.springframework.boot:spring-boot-starter-validation-test:jar is missing. @ line 71, column 15<br \/>\n[ERROR] &#8216;dependencies.dependency.version&#8217; for org.springframework.boot:spring-boot-starter-webmvc-test:jar is missing. @ line 76, column 15<br \/>\n[ERROR]<br \/>\n[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.<br \/>\n[ERROR] Re-run Maven using the -X switch to enable full debug logging.<br \/>\n[ERROR]<br \/>\n[ERROR] For more information about the errors and possible solutions, please read the following articles:<br \/>\n[ERROR] [Help 1] http:\/\/cwiki.apache.org\/confluence\/display\/MAVEN\/ProjectBuildingException<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u8fd9\u4e2a\u95ee\u9898\u51fa\u73b0\u7684\u539f\u56e0\u975e\u5e38\u7ecf\u5178\uff0c\u5c5e\u4e8e\u524d\u9762\u964d\u7ea7\u7559\u4e0b\u7684\u201c\u5386\u53f2\u9057\u7559\u95ee\u9898\u201d\u3002<br \/>\n<strong>\u539f\u7406\u89e3\u6790\uff1a<\/strong><br \/>\nSpring Boot 4.x \u5b98\u65b9\u6084\u6084\u6539\u4e86\u4e00\u4e9b\u6838\u5fc3\u4f9d\u8d56\u5305\u7684\u540d\u5b57\uff08\u6bd4\u5982\u628a <code>spring-boot-starter-web<\/code> \u62c6\u5206\u6210\u4e86 <code>spring-boot-starter-webmvc<\/code> \u7b49\uff09\u3002<br \/>\n\u6211\u4eec\u521a\u624d\u53ea\u5728 <code>pom.xml<\/code> \u9876\u90e8\u628a\u7236\u7248\u672c\u6539\u56de\u4e86 <code>3.2.4<\/code>\uff0c\u4f46\u5e95\u4e0b\u7684 <code>&lt;dependencies&gt;<\/code> \u4f9d\u8d56\u5217\u8868\u91cc\uff0c\u8fd8\u6b8b\u7559\u7740 4.x \u4e13\u5c5e\u7684\u65b0\u540d\u5b57\u3002Spring Boot 3.2.4 \u7684\u5e93\u91cc\u6839\u672c\u4e0d\u8ba4\u8bc6\u8fd9\u4e9b\u65b0\u540d\u5b57\uff0c\u6240\u4ee5 Maven \u5c31\u61f5\u4e86\uff0c\u62a5\u9519\u8bf4\u201c\u627e\u4e0d\u5230\u7248\u672c\u53f7\u201d\u3002<br \/>\n<strong>\u7ec8\u6781\u89e3\u51b3\u529e\u6cd5\uff1a<\/strong><br \/>\n\u4e3a\u4e86\u9632\u6b62\u5728 <code>vim<\/code> \u91cc\u5220\u51cf\u4ee3\u7801\u51fa\u9519\uff0c\u6211\u7ed9\u4f60\u51c6\u5907\u4e86\u4e00\u4e2a<strong>\u4e00\u952e\u91cd\u5199<\/strong><code>pom.xml<\/code> \u7684\u6307\u4ee4\u3002\u8fd9\u4e2a\u914d\u7f6e\u4e0d\u4ec5\u9002\u914d 3.2.4\uff0c\u8fd8\u5305\u542b\u4e86\u6211\u4eec\u7528\u5230\u7684\u6240\u6709\u4f9d\u8d56\uff08Web\u3001MySQL\u3001MyBatis-Plus\u3001Lombok \u7b49\uff09\u3002<br \/>\n\u8bf7\u5728 <code>\/mnt\/navidrome-system\/backend\/navidrome-server<\/code> \u76ee\u5f55\u4e0b\uff0c\u76f4\u63a5\u590d\u5236\u7c98\u8d34\u8fd9\u4e00\u6574\u6bb5\u4ee3\u7801\u5e76\u56de\u8f66\uff08\u5b83\u4f1a\u76f4\u63a5\u628a\u65b0\u7684\u5185\u5bb9\u5199\u5165 <code>pom.xml<\/code>\uff09\uff1a<\/p>\n<pre><code class=\"language-bash\">cat &lt;&lt; 'EOF' &gt; pom.xml\r\n&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\r\n&lt;project xmlns=\"http:\/\/maven.apache.org\/POM\/4.0.0\" xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\"\r\n         xsi:schemaLocation=\"http:\/\/maven.apache.org\/POM\/4.0.0 https:\/\/maven.apache.org\/xsd\/maven-4.0.0.xsd\"&gt;\r\n    &lt;modelVersion&gt;4.0.0&lt;\/modelVersion&gt;\r\n    &lt;parent&gt;\r\n        &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n        &lt;artifactId&gt;spring-boot-starter-parent&lt;\/artifactId&gt;\r\n        &lt;version&gt;3.2.4&lt;\/version&gt;\r\n        &lt;relativePath\/&gt; &lt;\/parent&gt;\r\n    &lt;groupId&gt;vip.crabpt.navidrome&lt;\/groupId&gt;\r\n    &lt;artifactId&gt;navidrome-server&lt;\/artifactId&gt;\r\n    &lt;version&gt;0.0.1-SNAPSHOT&lt;\/version&gt;\r\n    &lt;name&gt;navidrome-server&lt;\/name&gt;\r\n    &lt;description&gt;Navidrome Server Backend&lt;\/description&gt;\r\n    &lt;properties&gt;\r\n        &lt;java.version&gt;17&lt;\/java.version&gt;\r\n    &lt;\/properties&gt;\r\n    &lt;dependencies&gt;\r\n        &lt;dependency&gt;\r\n            &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n            &lt;artifactId&gt;spring-boot-starter-web&lt;\/artifactId&gt;\r\n        &lt;\/dependency&gt;\r\n        &lt;dependency&gt;\r\n            &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n            &lt;artifactId&gt;spring-boot-starter-validation&lt;\/artifactId&gt;\r\n        &lt;\/dependency&gt;\r\n        &lt;dependency&gt;\r\n            &lt;groupId&gt;com.baomidou&lt;\/groupId&gt;\r\n            &lt;artifactId&gt;mybatis-plus-spring-boot3-starter&lt;\/artifactId&gt;\r\n            &lt;version&gt;3.5.5&lt;\/version&gt;\r\n        &lt;\/dependency&gt;\r\n        &lt;dependency&gt;\r\n            &lt;groupId&gt;org.apache.velocity&lt;\/groupId&gt;\r\n            &lt;artifactId&gt;velocity-engine-core&lt;\/artifactId&gt;\r\n            &lt;version&gt;2.3&lt;\/version&gt;\r\n        &lt;\/dependency&gt;\r\n        &lt;dependency&gt;\r\n            &lt;groupId&gt;com.baomidou&lt;\/groupId&gt;\r\n            &lt;artifactId&gt;mybatis-plus-generator&lt;\/artifactId&gt;\r\n            &lt;version&gt;3.5.5&lt;\/version&gt;\r\n        &lt;\/dependency&gt;\r\n        &lt;dependency&gt;\r\n            &lt;groupId&gt;com.mysql&lt;\/groupId&gt;\r\n            &lt;artifactId&gt;mysql-connector-j&lt;\/artifactId&gt;\r\n            &lt;scope&gt;runtime&lt;\/scope&gt;\r\n        &lt;\/dependency&gt;\r\n        &lt;dependency&gt;\r\n            &lt;groupId&gt;org.projectlombok&lt;\/groupId&gt;\r\n            &lt;artifactId&gt;lombok&lt;\/artifactId&gt;\r\n            &lt;optional&gt;true&lt;\/optional&gt;\r\n        &lt;\/dependency&gt;\r\n        &lt;dependency&gt;\r\n            &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n            &lt;artifactId&gt;spring-boot-starter-test&lt;\/artifactId&gt;\r\n            &lt;scope&gt;test&lt;\/scope&gt;\r\n        &lt;\/dependency&gt;\r\n    &lt;\/dependencies&gt;\r\n\r\n    &lt;build&gt;\r\n        &lt;plugins&gt;\r\n            &lt;plugin&gt;\r\n                &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n                &lt;artifactId&gt;spring-boot-maven-plugin&lt;\/artifactId&gt;\r\n            &lt;\/plugin&gt;\r\n        &lt;\/plugins&gt;\r\n    &lt;\/build&gt;\r\n&lt;\/project&gt;\r\nEOF\r\n<\/code><\/pre>\n<p>\u6267\u884c\u5b8c\u4e4b\u540e\uff0c\u6211\u4eec\u7684\u4f9d\u8d56\u6587\u4ef6\u5c31\u5f7b\u5e95\u201c\u7eaf\u51c0\u201d\u4e86\u3002<br \/>\n\u73b0\u5728\uff0c\u518d\u6765\u4e00\u6b21\u542f\u52a8\u547d\u4ee4\uff1a<\/p>\n<pre><code class=\"language-bash\">.\/mvnw clean spring-boot:run\r\n<\/code><\/pre>\n<p>\u8fd9\u6b21\u6ca1\u6709\u4e86 4.x \u7684\u5e72\u6270\uff0c\u5fc5\u5b9a\u80fd\u591f\u987a\u5229\u542f\u52a8\uff01\u7b49\u5230\u5c4f\u5e55\u6eda\u5230 <code>Started NavidromeServerApplication<\/code> \u505c\u4f4f\uff0c\u4f60\u5c31\u53ef\u4ee5\u53bb\u5f00\u65b0\u7a97\u53e3\u6d4b\u521a\u624d\u5199\u7684 <code>\/v1\/login<\/code> \u548c <code>\/v1\/user\/checkin<\/code> \u63a5\u53e3\u4e86\uff01\u6709\u4ec0\u4e48\u60c5\u51b5\u968f\u65f6\u544a\u8bc9\u6211\u3002<\/p>\n<h2>Turn 21<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[INFO] BUILD FAILURE<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[INFO] Total time: 15.542 s<br \/>\n[INFO] Finished at: 2026-04-14T01:58:19-04:00<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:3.2.4:run (default-cli) on project navidrome-server: Process terminated with exit code: 1 -&gt; [Help 1]<br \/>\n[ERROR]<br \/>\n[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.<br \/>\n[ERROR] Re-run Maven using the -X switch to enable full debug logging.<br \/>\n[ERROR]<br \/>\n[ERROR] For more information about the errors and possible solutions, please read the following articles:<br \/>\n[ERROR] [Help 1] http:\/\/cwiki.apache.org\/confluence\/display\/MAVEN\/MojoExecutionException<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u54ce\u5440\uff0c\u53c8\u88ab <code>Exit 1<\/code> \u62e6\u4f4f\u4e86\uff01\u4e0d\u8fc7\u4e0d\u8981\u614c\uff0c\u65e2\u7136\u80fd\u8d70\u5230 <code>15.542 s<\/code> \u624d\u5931\u8d25\uff0c\u8bf4\u660e Maven \u7684\u4f9d\u8d56\u5df2\u7ecf\u987a\u5229\u4e0b\u8f7d\u5b8c\u6bd5\uff0c\u4ee3\u7801\u4e5f\u6210\u529f\u7f16\u8bd1\u4e86\u3002<br \/>\n<strong>\u8fd9\u6b21\u5931\u8d25\u7684\u539f\u56e0\uff0c\u7edd\u5927\u591a\u6570\u60c5\u51b5\u662f\u56e0\u4e3a Spring Boot \u5728\u542f\u52a8\u7684\u6700\u540e\u5173\u5934\u9047\u5230\u4e86\u67d0\u4e2a\u5177\u4f53\u7684\u62a5\u9519\uff08\u6bd4\u5982\u7aef\u53e3\u88ab\u5360\u7528\u3001\u6570\u636e\u5e93\u8fde\u4e0d\u4e0a\u3001\u6216\u8005\u67d0\u4e2a\u7c7b\u627e\u4e0d\u5230\uff09\u3002<\/strong><br \/>\n\u4f46\u662f\uff0c\u4f60\u76ee\u524d\u53d1\u7ed9\u6211\u7684\u8fd9\u6bb5\u65e5\u5fd7\u53ea\u662f Maven \u7684\u201c\u6700\u7ec8\u5ba3\u5224\u4e66\u201d\uff08\u603b\u7ed3\u62a5\u544a\uff09\uff0c\u5b83\u628a\u771f\u6b63\u201c\u4f5c\u6848\u8fc7\u7a0b\u201d\uff08Spring Boot \u7684\u8be6\u7ec6\u62a5\u9519\uff09\u7ed9\u538b\u5728\u4e0a\u9762\u4e86\u3002<br \/>\n\u6211\u4eec\u9700\u8981\u63ea\u51fa\u771f\u6b63\u7684\u9519\u8bef\u4fe1\u606f\u3002\u8bf7\u6309\u7167\u4ee5\u4e0b\u4e24\u4e2a\u65b9\u5411\u6392\u67e5\uff1a<\/p>\n<h3>\u65b9\u6848\u4e00\uff1a\u5411\u4e0a\u6eda\u52a8\u5c4f\u5e55\uff0c\u627e\u5173\u952e\u7ebf\u7d22\uff08\u6700\u5feb\uff09<\/h3>\n<p>\u5728\u4f60\u521a\u624d\u6267\u884c\u547d\u4ee4\u7684\u7ec8\u7aef\u754c\u9762\uff0c<strong>\u7a0d\u5fae\u5f80\u4e0a\u6eda\u52a8\u4e00\u70b9\u70b9<\/strong>\uff08\u5c31\u5728 <code>[INFO] BUILD FAILURE<\/code> \u7684\u4e0a\u65b9\uff09\u3002<br \/>\n\u4f60\u5e94\u8be5\u80fd\u770b\u5230\u4e00\u4e9b\u5e26\u6709 <code>ERROR<\/code>\u3001<code>Exception<\/code> \u5b57\u6837\u7684\u65e5\u5fd7\uff0c\u6216\u8005\u6709\u4e00\u6bb5\u957f\u5f97\u5f88\u50cf\u4e0b\u9762\u8fd9\u6837\u7684\u63d0\u793a\uff1a<\/p>\n<pre><code class=\"language-plaintext\">***************************\r\nAPPLICATION FAILED TO START\r\n***************************\r\n\r\nDescription:\r\n... (\u8fd9\u91cc\u4f1a\u5199\u5177\u4f53\u7684\u9519\u8bef\u539f\u56e0)\r\n\r\nAction:\r\n... (\u8fd9\u91cc\u4f1a\u5199\u5efa\u8bae\u7684\u89e3\u51b3\u529e\u6cd5)\r\n<\/code><\/pre>\n<p><strong>\u8bf7\u628a\u7d27\u6328\u7740 BUILD FAILURE \u4e0a\u65b9\u7684\u90a3\u4e00\u6bb5\uff08\u5927\u6982 10-20 \u884c\uff09\u9519\u8bef\u4fe1\u606f\u590d\u5236\u53d1\u7ed9\u6211<\/strong>\uff0c\u6211\u4e00\u773c\u5c31\u80fd\u770b\u51fa\u95ee\u9898\u5728\u54ea\uff01<\/p>\n<hr \/>\n<h3>\u65b9\u6848\u4e8c\uff1a\u76f2\u731c\u6700\u6709\u53ef\u80fd\u7684\u539f\u56e0\uff08\u7aef\u53e3\u53f7\u51b2\u7a81\uff09<\/h3>\n<p>\u6839\u636e\u6211\u4eec\u4e4b\u524d\u7684\u64cd\u4f5c\u8bb0\u5f55\uff0c\u6709\u4e00\u4e2a\u975e\u5e38\u5e38\u89c1\u7684\u201c\u5751\u201d\uff1a<br \/>\n\u4e4b\u524d\u6211\u4eec\u6267\u884c\u8fc7\u51e0\u6b21 <code>nohup .\/mvnw spring-boot:run &amp;<\/code> \u5728\u540e\u53f0\u542f\u52a8\u3002\u867d\u7136\u5f53\u65f6\u62a5\u9519\u4e86\uff0c\u4f46\u5728\u67d0\u4e9b\u60c5\u51b5\u4e0b\uff0cJava \u8fdb\u7a0b\u53ef\u80fd\u5e76\u6ca1\u6709\u5f7b\u5e95\u6b7b\u6389\uff0c\u800c\u662f\u53d8\u6210\u4e86\u201c\u50f5\u5c38\u8fdb\u7a0b\u201d\uff0c<strong>\u6b7b\u6b7b\u9738\u5360\u7740 8001 \u7aef\u53e3<\/strong>\u3002<br \/>\n\u5f53\u6211\u4eec\u5728\u524d\u53f0\u518d\u6b21\u8fd0\u884c\u7a0b\u5e8f\u65f6\uff0c\u7a0b\u5e8f\u53d1\u73b0 <code>8001<\/code> \u7aef\u53e3\u88ab\u5360\u7528\u4e86\uff0c\u5c31\u4f1a\u76f4\u63a5 <code>Exit 1<\/code> \u5d29\u6e83\u3002<br \/>\n\u4f60\u53ef\u4ee5\u8fd0\u884c\u8fd9\u6761\u547d\u4ee4\uff0c\u770b\u770b\u662f\u4e0d\u662f\u6709\u4e2a\u6b8b\u7559\u7684 Java \u8fdb\u7a0b\u5728\u5077\u5077\u8fd0\u884c\uff1a<\/p>\n<pre><code class=\"language-bash\">ps -ef | grep java\r\n<\/code><\/pre>\n<p>\u5982\u679c\u4f60\u770b\u5230\u8f93\u51fa\u91cc\u6709\u957f\u957f\u7684 <code>java<\/code> \u6216\u8005 <code>spring-boot<\/code> \u76f8\u5173\u7684\u8fdb\u7a0b\uff08\u9664\u4e86 grep \u81ea\u8eab\uff09\uff0c\u4f60\u53ef\u4ee5\u76f4\u63a5\u796d\u51fa\u5927\u62db\uff0c\u628a\u6240\u6709\u6b8b\u4f59\u7684 Java \u8fdb\u7a0b\u5168\u90e8\u6740\u6389\uff0c\u817e\u51fa\u7aef\u53e3\uff1a<\/p>\n<pre><code class=\"language-bash\">killall -9 java\r\n<\/code><\/pre>\n<p>\u6740\u6389\u4e4b\u540e\uff0c\u518d\u91cd\u65b0\u8fd0\u884c\u4e00\u6b21\uff1a<\/p>\n<pre><code class=\"language-bash\">.\/mvnw clean spring-boot:run\r\n<\/code><\/pre>\n<p>\u5982\u679c\u8fd8\u662f\u62a5\u9519\uff0c<strong>\u4e00\u5b9a\u8bb0\u5f97\u628a\u4e0a\u65b9\u5c4f\u5e55\u7684\u62a5\u9519\u8be6\u60c5\uff08Exception\/Error \u90e8\u5206\uff09\u53d1\u7ed9\u6211\u54e6\uff01<\/strong> \u9a6c\u4e0a\u5c31\u80fd\u628a\u5b83\u62ff\u4e0b\uff01<\/p>\n<h2>Turn 22<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<blockquote><p>ps -ef | grep java<br \/>\nroot@192-227-164-42-host:\/mnt\/navidrome-system\/backend\/navidrome-server# ps -ef | grep java<br \/>\nroot 1011189 1010691 0 Apr13 ? 00:00:00 \/sbin\/tini &#8212; java -jar \/app\/bin\/reader.jar<br \/>\nroot 1012112 1011189 0 Apr13 ? 00:01:20 java -jar \/app\/bin\/reader.jar<br \/>\nroot 1297460 1215622 0 01:59 pts\/0 00:00:00 grep java<\/p><\/blockquote>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u4ece\u4f60\u8fd0\u884c <code>ps -ef | grep java<\/code> \u7684\u7ed3\u679c\u6765\u770b\uff0c<strong>\u76ee\u524d\u670d\u52a1\u5668\u4e0a\u5e76\u6ca1\u6709 8001 \u7aef\u53e3\u5bf9\u5e94\u7684\u50f5\u5c38 Java \u8fdb\u7a0b\u5728\u8fd0\u884c<\/strong>\u3002<\/p>\n<ul>\n<li><code>1011189<\/code> \u548c <code>1012112<\/code> \u662f <code>reader.jar<\/code>\uff0c\u8fd9\u5e94\u8be5\u662f\u4f60\u670d\u52a1\u5668\u4e0a\u8dd1\u7684\u5176\u4ed6\u670d\u52a1\uff08\u6bd4\u5982\u5c0f\u8bf4\u9605\u8bfb\u5668\u6216\u8005 RSS \u8ba2\u9605\u5668\uff09\uff0c\u4e0d\u5f71\u54cd\u6211\u4eec\u3002<\/li>\n<li><code>1297460<\/code> \u662f <code>grep java<\/code> \u672c\u8eab\u3002<br \/>\n\u65e2\u7136\u4e0d\u662f\u7aef\u53e3\u88ab\u201c\u524d\u671d\u9057\u8001\u201d\u9738\u5360\uff0c\u90a3\u5c31<strong>\u4e00\u5b9a\u8fd8\u662f\u4ee3\u7801\u6216\u8005\u914d\u7f6e\u4e0a\u6709\u62a5\u9519<\/strong>\u3002<br \/>\n\u4e3a\u4e86\u4e0d\u201c\u778e\u731c\u201d\uff0c\u6211\u4eec\u9700\u8981\u628a\u88ab\u541e\u6389\u7684\u771f\u5b9e\u9519\u8bef\u4fe1\u606f\u7ed9\u627e\u51fa\u6765\u3002<\/li>\n<\/ul>\n<h3>\u7ec8\u6781\u67e5\u9519\u65b9\u6848\uff08\u76f4\u63a5\u628a\u9519\u8bef\u62cd\u8138\u4e0a\uff09<\/h3>\n<p>\u65e2\u7136 <code>spring-boot:run<\/code> \u7ecf\u5e38\u5728\u6700\u540e\u5d29\u6e83\u4e14\u4e0d\u62a5\u9519\uff0c\u6211\u4eec\u76f4\u63a5\u4f7f\u7528 Java \u6700\u539f\u751f\u7684\u65b9\u5f0f\u8fd0\u884c\u7f16\u8bd1\u597d\u7684 <code>jar<\/code> \u5305\uff0c\u8fd9\u6837\u4efb\u4f55\u62a5\u9519\u90fd\u4f1a\u4e00\u5b57\u4e0d\u843d\u5730\u6253\u5370\u5728\u5c4f\u5e55\u4e0a\u3002<br \/>\n\u8bf7\u4f9d\u6b21\u6267\u884c\u4ee5\u4e0b\u4e24\u6761\u547d\u4ee4\uff1a<br \/>\n<strong>1. \u91cd\u65b0\u6253\u5305\u9879\u76ee\uff08\u8df3\u8fc7\u6d4b\u8bd5\uff09\uff1a<\/strong><\/p>\n<pre><code class=\"language-bash\">.\/mvnw clean package -DskipTests\r\n<\/code><\/pre>\n<p><em>(\u770b\u5230 BUILD SUCCESS \u540e\u8fdb\u884c\u4e0b\u4e00\u6b65)<\/em><br \/>\n<strong>2. \u76f4\u63a5\u8fd0\u884c\u6253\u5305\u597d\u7684 jar \u6587\u4ef6\uff1a<\/strong><\/p>\n<pre><code class=\"language-bash\">java -jar target\/navidrome-server-0.0.1-SNAPSHOT.jar\r\n<\/code><\/pre>\n<p>\u8fd9\u4e00\u6b21\uff0c\u8bf7\u7d27\u7d27\u76ef\u7740\u5c4f\u5e55\uff01<br \/>\n\u5f53\u65e5\u5fd7\u6eda\u52a8\u5230\u62a5\u9519\u5d29\u6e83\u7684\u65f6\u5019\uff0c<strong>\u8bf7\u7acb\u523b\u590d\u5236\u5012\u6570\u524d 15 \u884c\u5230 20 \u884c\u7684\u5185\u5bb9\uff08\u7279\u522b\u662f\u7d27\u6328\u7740 APPLICATION FAILED TO START \u6216\u8005\u542b\u6709 Exception:\u3001Error creating bean \u7684\u90e8\u5206\uff09\u53d1\u7ed9\u6211\u3002<\/strong><br \/>\n\u53ea\u8981\u770b\u5230\u8fd9\u4e2a\u62a5\u9519\uff0c\u6211\u5c31\u80fd\u7acb\u523b\u7ed9\u4f60\u89e3\u51b3\u529e\u6cd5\uff01<\/p>\n<h2>Turn 23<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>oot@192-227-164-42-host:\/mnt\/navidrome-system\/backend\/navidrome-server# .\/mvnw clean package -DskipTests<br \/>\n[INFO] Scanning for projects&#8230;<br \/>\n[INFO]<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&lt; vip.crabpt.navidrome:navidrome-server &gt;&#8212;&#8212;&#8212;&#8212;&#8212;-<br \/>\n[INFO] Building navidrome-server 0.0.1-SNAPSHOT<br \/>\n[INFO] from pom.xml<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;[ jar ]&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/plugins\/maven-surefire-plugin\/3.1.2\/maven-surefire-plugin-3.1.2.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/plugins\/maven-surefire-plugin\/3.1.2\/maven-surefire-plugin-3.1.2.pom (5.5 kB at 19 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire\/3.1.2\/surefire-3.1.2.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire\/3.1.2\/surefire-3.1.2.pom (22 kB at 736 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/plugins\/maven-surefire-plugin\/3.1.2\/maven-surefire-plugin-3.1.2.jar<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/plugins\/maven-surefire-plugin\/3.1.2\/maven-surefire-plugin-3.1.2.jar (43 kB at 1.2 MB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/plugins\/maven-jar-plugin\/3.3.0\/maven-jar-plugin-3.3.0.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/plugins\/maven-jar-plugin\/3.3.0\/maven-jar-plugin-3.3.0.pom (6.8 kB at 260 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/plugins\/maven-plugins\/37\/maven-plugins-37.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/plugins\/maven-plugins\/37\/maven-plugins-37.pom (9.9 kB at 330 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/plugins\/maven-jar-plugin\/3.3.0\/maven-jar-plugin-3.3.0.jar<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/plugins\/maven-jar-plugin\/3.3.0\/maven-jar-plugin-3.3.0.jar (27 kB at 1.0 MB\/s)<br \/>\n[INFO]<br \/>\n[INFO] &#8212; clean:3.3.2:clean (default-clean) @ navidrome-server &#8212;<br \/>\n[INFO] Deleting \/mnt\/navidrome-system\/backend\/navidrome-server\/target<br \/>\n[INFO]<br \/>\n[INFO] &#8212; resources:3.3.1:resources (default-resources) @ navidrome-server &#8212;<br \/>\n[INFO] Copying 1 resource from src\/main\/resources to target\/classes<br \/>\n[INFO] Copying 4 resources from src\/main\/resources to target\/classes<br \/>\n[INFO]<br \/>\n[INFO] &#8212; compiler:3.11.0:compile (default-compile) @ navidrome-server &#8212;<br \/>\n[INFO] Changes detected &#8211; recompiling the module! :source<br \/>\n[INFO] Compiling 23 source files with javac [debug release 17] to target\/classes<br \/>\n[INFO]<br \/>\n[INFO] &#8212; resources:3.3.1:testResources (default-testResources) @ navidrome-server &#8212;<br \/>\n[INFO] skip non existing resourceDirectory \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/test\/resources<br \/>\n[INFO]<br \/>\n[INFO] &#8212; compiler:3.11.0:testCompile (default-testCompile) @ navidrome-server &#8212;<br \/>\n[INFO] Changes detected &#8211; recompiling the module! :dependency<br \/>\n[INFO] Compiling 2 source files with javac [debug release 17] to target\/test-classes<br \/>\n[INFO]<br \/>\n[INFO] &#8212; surefire:3.1.2:test (default-test) @ navidrome-server &#8212;<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/maven-surefire-common\/3.1.2\/maven-surefire-common-3.1.2.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/maven-surefire-common\/3.1.2\/maven-surefire-common-3.1.2.pom (6.1 kB at 234 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-api\/3.1.2\/surefire-api-3.1.2.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-api\/3.1.2\/surefire-api-3.1.2.pom (3.5 kB at 141 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-logger-api\/3.1.2\/surefire-logger-api-3.1.2.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-logger-api\/3.1.2\/surefire-logger-api-3.1.2.pom (3.3 kB at 136 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-shared-utils\/3.1.2\/surefire-shared-utils-3.1.2.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-shared-utils\/3.1.2\/surefire-shared-utils-3.1.2.pom (4.1 kB at 162 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-extensions-api\/3.1.2\/surefire-extensions-api-3.1.2.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-extensions-api\/3.1.2\/surefire-extensions-api-3.1.2.pom (3.3 kB at 132 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-booter\/3.1.2\/surefire-booter-3.1.2.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-booter\/3.1.2\/surefire-booter-3.1.2.pom (4.5 kB at 194 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-extensions-spi\/3.1.2\/surefire-extensions-spi-3.1.2.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-extensions-spi\/3.1.2\/surefire-extensions-spi-3.1.2.pom (1.8 kB at 77 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/shared\/maven-common-artifact-filters\/3.1.1\/maven-common-artifact-filters-3.1.1.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/shared\/maven-common-artifact-filters\/3.1.1\/maven-common-artifact-filters-3.1.1.pom (5.8 kB at 242 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-artifact\/3.2.5\/maven-artifact-3.2.5.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-artifact\/3.2.5\/maven-artifact-3.2.5.pom (2.3 kB at 102 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven\/3.2.5\/maven-3.2.5.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven\/3.2.5\/maven-3.2.5.pom (22 kB at 973 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-parent\/25\/maven-parent-25.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-parent\/25\/maven-parent-25.pom (37 kB at 1.4 MB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/apache\/15\/apache-15.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/apache\/15\/apache-15.pom (15 kB at 609 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-core\/3.2.5\/maven-core-3.2.5.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-core\/3.2.5\/maven-core-3.2.5.pom (8.1 kB at 351 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-settings\/3.2.5\/maven-settings-3.2.5.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-settings\/3.2.5\/maven-settings-3.2.5.pom (2.2 kB at 103 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-settings-builder\/3.2.5\/maven-settings-builder-3.2.5.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-settings-builder\/3.2.5\/maven-settings-builder-3.2.5.pom (2.6 kB at 100 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus-interpolation\/1.21\/plexus-interpolation-1.21.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus-interpolation\/1.21\/plexus-interpolation-1.21.pom (1.5 kB at 67 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus-components\/1.3.1\/plexus-components-1.3.1.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus-components\/1.3.1\/plexus-components-1.3.1.pom (3.1 kB at 133 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus\/3.3.1\/plexus-3.3.1.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus\/3.3.1\/plexus-3.3.1.pom (20 kB at 786 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/sonatype\/spice\/spice-parent\/17\/spice-parent-17.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/sonatype\/spice\/spice-parent\/17\/spice-parent-17.pom (6.8 kB at 260 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/sonatype\/forge\/forge-parent\/10\/forge-parent-10.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/sonatype\/forge\/forge-parent\/10\/forge-parent-10.pom (14 kB at 502 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/sonatype\/plexus\/plexus-sec-dispatcher\/1.3\/plexus-sec-dispatcher-1.3.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/sonatype\/plexus\/plexus-sec-dispatcher\/1.3\/plexus-sec-dispatcher-1.3.pom (3.0 kB at 114 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/sonatype\/spice\/spice-parent\/12\/spice-parent-12.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/sonatype\/spice\/spice-parent\/12\/spice-parent-12.pom (6.8 kB at 296 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/sonatype\/forge\/forge-parent\/4\/forge-parent-4.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/sonatype\/forge\/forge-parent\/4\/forge-parent-4.pom (8.4 kB at 311 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/sonatype\/plexus\/plexus-cipher\/1.4\/plexus-cipher-1.4.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/sonatype\/plexus\/plexus-cipher\/1.4\/plexus-cipher-1.4.pom (2.1 kB at 83 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-repository-metadata\/3.2.5\/maven-repository-metadata-3.2.5.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-repository-metadata\/3.2.5\/maven-repository-metadata-3.2.5.pom (2.2 kB at 89 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-plugin-api\/3.2.5\/maven-plugin-api-3.2.5.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-plugin-api\/3.2.5\/maven-plugin-api-3.2.5.pom (3.0 kB at 108 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/eclipse\/sisu\/org.eclipse.sisu.plexus\/0.3.5\/org.eclipse.sisu.plexus-0.3.5.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/eclipse\/sisu\/org.eclipse.sisu.plexus\/0.3.5\/org.eclipse.sisu.plexus-0.3.5.pom (4.3 kB at 165 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/eclipse\/sisu\/sisu-plexus\/0.3.5\/sisu-plexus-0.3.5.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/eclipse\/sisu\/sisu-plexus\/0.3.5\/sisu-plexus-0.3.5.pom (14 kB at 508 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/javax\/annotation\/javax.annotation-api\/1.2\/javax.annotation-api-1.2.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/javax\/annotation\/javax.annotation-api\/1.2\/javax.annotation-api-1.2.pom (13 kB at 463 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/net\/java\/jvnet-parent\/3\/jvnet-parent-3.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/net\/java\/jvnet-parent\/3\/jvnet-parent-3.pom (4.8 kB at 200 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/javax\/enterprise\/cdi-api\/1.2\/cdi-api-1.2.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/javax\/enterprise\/cdi-api\/1.2\/cdi-api-1.2.pom (6.3 kB at 224 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/jboss\/weld\/weld-parent\/26\/weld-parent-26.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/jboss\/weld\/weld-parent\/26\/weld-parent-26.pom (32 kB at 1.1 MB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/eclipse\/sisu\/org.eclipse.sisu.inject\/0.3.5\/org.eclipse.sisu.inject-0.3.5.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/eclipse\/sisu\/org.eclipse.sisu.inject\/0.3.5\/org.eclipse.sisu.inject-0.3.5.pom (2.6 kB at 101 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/eclipse\/sisu\/sisu-inject\/0.3.5\/sisu-inject-0.3.5.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/eclipse\/sisu\/sisu-inject\/0.3.5\/sisu-inject-0.3.5.pom (14 kB at 554 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus-classworlds\/2.5.2\/plexus-classworlds-2.5.2.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus-classworlds\/2.5.2\/plexus-classworlds-2.5.2.pom (7.3 kB at 332 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-model-builder\/3.2.5\/maven-model-builder-3.2.5.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-model-builder\/3.2.5\/maven-model-builder-3.2.5.pom (3.0 kB at 136 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-aether-provider\/3.2.5\/maven-aether-provider-3.2.5.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-aether-provider\/3.2.5\/maven-aether-provider-3.2.5.pom (4.2 kB at 170 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/eclipse\/aether\/aether-spi\/1.0.0.v20140518\/aether-spi-1.0.0.v20140518.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/eclipse\/aether\/aether-spi\/1.0.0.v20140518\/aether-spi-1.0.0.v20140518.pom (2.1 kB at 89 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/eclipse\/aether\/aether-impl\/1.0.0.v20140518\/aether-impl-1.0.0.v20140518.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/eclipse\/aether\/aether-impl\/1.0.0.v20140518\/aether-impl-1.0.0.v20140518.pom (3.5 kB at 134 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/sonatype\/sisu\/sisu-guice\/3.2.3\/sisu-guice-3.2.3.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/sonatype\/sisu\/sisu-guice\/3.2.3\/sisu-guice-3.2.3.pom (11 kB at 377 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/sonatype\/sisu\/inject\/guice-parent\/3.2.3\/guice-parent-3.2.3.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/sonatype\/sisu\/inject\/guice-parent\/3.2.3\/guice-parent-3.2.3.pom (13 kB at 518 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/sonatype\/forge\/forge-parent\/38\/forge-parent-38.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/sonatype\/forge\/forge-parent\/38\/forge-parent-38.pom (19 kB at 721 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/aopalliance\/aopalliance\/1.0\/aopalliance-1.0.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/aopalliance\/aopalliance\/1.0\/aopalliance-1.0.pom (363 B at 15 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/com\/google\/guava\/guava\/16.0.1\/guava-16.0.1.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/com\/google\/guava\/guava\/16.0.1\/guava-16.0.1.pom (6.1 kB at 265 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/com\/google\/guava\/guava-parent\/16.0.1\/guava-parent-16.0.1.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/com\/google\/guava\/guava-parent\/16.0.1\/guava-parent-16.0.1.pom (7.3 kB at 319 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/commons-io\/commons-io\/2.12.0\/commons-io-2.12.0.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/commons-io\/commons-io\/2.12.0\/commons-io-2.12.0.pom (20 kB at 813 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/commons\/commons-parent\/57\/commons-parent-57.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/commons\/commons-parent\/57\/commons-parent-57.pom (83 kB at 2.9 MB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/maven-surefire-common\/3.1.2\/maven-surefire-common-3.1.2.jar<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/maven-surefire-common\/3.1.2\/maven-surefire-common-3.1.2.jar (306 kB at 7.7 MB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-api\/3.1.2\/surefire-api-3.1.2.jar<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-logger-api\/3.1.2\/surefire-logger-api-3.1.2.jar<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-extensions-api\/3.1.2\/surefire-extensions-api-3.1.2.jar<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-booter\/3.1.2\/surefire-booter-3.1.2.jar<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-extensions-spi\/3.1.2\/surefire-extensions-spi-3.1.2.jar<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-logger-api\/3.1.2\/surefire-logger-api-3.1.2.jar (14 kB at 566 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/shared\/maven-common-artifact-filters\/3.1.1\/maven-common-artifact-filters-3.1.1.jar<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/shared\/maven-common-artifact-filters\/3.1.1\/maven-common-artifact-filters-3.1.1.jar (61 kB at 1.3 MB\/s)<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-extensions-api\/3.1.2\/surefire-extensions-api-3.1.2.jar (26 kB at 517 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/commons-io\/commons-io\/2.12.0\/commons-io-2.12.0.jar<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-shared-utils\/3.1.2\/surefire-shared-utils-3.1.2.jar<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-booter\/3.1.2\/surefire-booter-3.1.2.jar (118 kB at 2.4 MB\/s)<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-extensions-spi\/3.1.2\/surefire-extensions-spi-3.1.2.jar (8.2 kB at 154 kB\/s)<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-api\/3.1.2\/surefire-api-3.1.2.jar (171 kB at 2.4 MB\/s)<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/commons-io\/commons-io\/2.12.0\/commons-io-2.12.0.jar (474 kB at 4.8 MB\/s)<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/surefire\/surefire-shared-utils\/3.1.2\/surefire-shared-utils-3.1.2.jar (2.3 MB at 12 MB\/s)<br \/>\n[INFO] Tests are skipped.<br \/>\n[INFO]<br \/>\n[INFO] &#8212; jar:3.3.0:jar (default-jar) @ navidrome-server &#8212;<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus-utils\/3.3.0\/plexus-utils-3.3.0.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus-utils\/3.3.0\/plexus-utils-3.3.0.pom (5.2 kB at 207 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-archiver\/3.6.0\/maven-archiver-3.6.0.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-archiver\/3.6.0\/maven-archiver-3.6.0.pom (3.9 kB at 151 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus-io\/3.4.0\/plexus-io-3.4.0.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus-io\/3.4.0\/plexus-io-3.4.0.pom (6.0 kB at 231 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus-archiver\/4.4.0\/plexus-archiver-4.4.0.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus-archiver\/4.4.0\/plexus-archiver-4.4.0.pom (6.3 kB at 216 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus-utils\/3.4.2\/plexus-utils-3.4.2.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus-utils\/3.4.2\/plexus-utils-3.4.2.pom (8.2 kB at 373 kB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus\/8\/plexus-8.pom<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus\/8\/plexus-8.pom (25 kB at 1.1 MB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-archiver\/3.6.0\/maven-archiver-3.6.0.jar<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/apache\/maven\/maven-archiver\/3.6.0\/maven-archiver-3.6.0.jar (26 kB at 1.2 MB\/s)<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus-io\/3.4.0\/plexus-io-3.4.0.jar<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus-archiver\/4.4.0\/plexus-archiver-4.4.0.jar<br \/>\nDownloading from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus-utils\/3.4.2\/plexus-utils-3.4.2.jar<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus-io\/3.4.0\/plexus-io-3.4.0.jar (79 kB at 3.1 MB\/s)<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus-archiver\/4.4.0\/plexus-archiver-4.4.0.jar (211 kB at 6.6 MB\/s)<br \/>\nDownloaded from central: https:\/\/repo.maven.apache.org\/maven2\/org\/codehaus\/plexus\/plexus-utils\/3.4.2\/plexus-utils-3.4.2.jar (267 kB at 7.6 MB\/s)<br \/>\n[INFO] Building jar: \/mnt\/navidrome-system\/backend\/navidrome-server\/target\/navidrome-server-0.0.1-SNAPSHOT.jar<br \/>\n[INFO]<br \/>\n[INFO] &#8212; spring-boot:3.2.4:repackage (repackage) @ navidrome-server &#8212;<br \/>\n[INFO] Replacing main artifact \/mnt\/navidrome-system\/backend\/navidrome-server\/target\/navidrome-server-0.0.1-SNAPSHOT.jar with repackaged archive, adding nested dependencies in BOOT-INF\/.<br \/>\n[INFO] The original artifact has been renamed to \/mnt\/navidrome-system\/backend\/navidrome-server\/target\/navidrome-server-0.0.1-SNAPSHOT.jar.original<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[INFO] BUILD SUCCESS<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[INFO] Total time: 6.839 s<br \/>\n[INFO] Finished at: 2026-04-14T02:01:54-04:00<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\nroot@192-227-164-42-host:\/mnt\/navidrome-system\/backend\/navidrome-server# java -jar target\/navidrome-server-0.0.1-SNAPSHOT.jar<br \/>\n. ____ _ __ _ _<br \/>\n\/\\ \/ <em><em><em>&#8216;<\/em> __ _ <em>(<\/em>)<\/em> __ __ _ \\ \\<br \/>\n( ( )_<\/em>_ | &#8216;_ | &#8216;<em>| | &#8216;<\/em> \/ <em>` | \\ \\<br \/>\n\\\/ <em><em><em>)| |<\/em>)| | | | | || (<\/em>| | ) ) ) )<br \/>\n&#8216; |<\/em><\/em><em><em>| .__|<\/em>| |<\/em>|<em>| |<\/em>_<em>, | \/ \/ \/ \/<br \/>\n=========|<\/em>|==============|<em><em><em>\/=\/<\/em>\/<\/em>\/<\/em>\/<br \/>\n:: Spring Boot :: (v3.2.4)<br \/>\n2026-04-14T02:02:05.102-04:00 INFO 1299074 &#8212; [ main] v.c.n.NavidromeServerApplication : Starting NavidromeServerApplication v0.0.1-SNAPSHOT using Java 17.0.18 with PID 1299074 (\/mnt\/navidrome-system\/backend\/navidrome-server\/target\/navidrome-server-0.0.1-SNAPSHOT.jar started by root in \/mnt\/navidrome-system\/backend\/navidrome-server)<br \/>\n2026-04-14T02:02:05.109-04:00 INFO 1299074 &#8212; [ main] v.c.n.NavidromeServerApplication : No active profile set, falling back to 1 default profile: &#8220;default&#8221;<br \/>\n2026-04-14T02:02:06.261-04:00 WARN 1299074 &#8212; [ main] o.m.s.mapper.ClassPathMapperScanner : No MyBatis mapper was found in &#8216;[vip.crabpt.navidrome]&#8217; package. Please check your configuration.<br \/>\n2026-04-14T02:02:06.718-04:00 INFO 1299074 &#8212; [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8001 (http)<br \/>\n2026-04-14T02:02:06.729-04:00 INFO 1299074 &#8212; [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]<br \/>\n2026-04-14T02:02:06.730-04:00 INFO 1299074 &#8212; [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat\/10.1.19]<br \/>\n2026-04-14T02:02:06.775-04:00 INFO 1299074 &#8212; [ main] o.a.c.c.C.[Tomcat].[localhost].[\/v1] : Initializing Spring embedded WebApplicationContext<br \/>\n2026-04-14T02:02:06.777-04:00 INFO 1299074 &#8212; [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1541 ms<br \/>\n2026-04-14T02:02:06.847-04:00 WARN 1299074 &#8212; [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization &#8211; cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name &#8216;userServiceImpl&#8217;: Unsatisfied dependency expressed through field &#8216;baseMapper&#8217;: No qualifying bean of type &#8216;vip.crabpt.navidrome.mapper.UserMapper&#8217; available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}<br \/>\n2026-04-14T02:02:06.850-04:00 INFO 1299074 &#8212; [ main] o.apache.catalina.core.StandardService : Stopping service [Tomcat]<br \/>\n2026-04-14T02:02:06.872-04:00 INFO 1299074 &#8212; [ main] .s.b.a.l.ConditionEvaluationReportLogger :<br \/>\nError starting ApplicationContext. To display the condition evaluation report re-run your application with &#8216;debug&#8217; enabled.<br \/>\n2026-04-14T02:02:06.896-04:00 ERROR 1299074 &#8212; [ main] o.s.boot.SpringApplication : Application run failed<br \/>\norg.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name &#8216;userServiceImpl&#8217;: Unsatisfied dependency expressed through field &#8216;baseMapper&#8217;: No qualifying bean of type &#8216;vip.crabpt.navidrome.mapper.UserMapper&#8217; available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}<br \/>\nat org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:787) ~[spring-beans-6.1.5.jar!\/:6.1.5]<br \/>\nat org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:767) ~[spring-beans-6.1.5.jar!\/:6.1.5]<br \/>\nat org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) ~[spring-beans-6.1.5.jar!\/:6.1.5]<br \/>\nat org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:508) ~[spring-beans-6.1.5.jar!\/:6.1.5]<br \/>\nat org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1419) ~[spring-beans-6.1.5.jar!\/:6.1.5]<br \/>\nat org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:599) ~[spring-beans-6.1.5.jar!\/:6.1.5]<br \/>\nat org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.5.jar!\/:6.1.5]<br \/>\nat org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.1.5.jar!\/:6.1.5]<br \/>\nat org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.5.jar!\/:6.1.5]<br \/>\nat org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-beans-6.1.5.jar!\/:6.1.5]<br \/>\nat org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.1.5.jar!\/:6.1.5]<br \/>\nat org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975) ~[spring-beans-6.1.5.jar!\/:6.1.5]<br \/>\nat org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:962) ~[spring-context-6.1.5.jar!\/:6.1.5]<br \/>\nat org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) ~[spring-context-6.1.5.jar!\/:6.1.5]<br \/>\nat org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.2.4.jar!\/:3.2.4]<br \/>\nat org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.2.4.jar!\/:3.2.4]<br \/>\nat org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.2.4.jar!\/:3.2.4]<br \/>\nat org.springframework.boot.SpringApplication.run(SpringApplication.java:334) ~[spring-boot-3.2.4.jar!\/:3.2.4]<br \/>\nat org.springframework.boot.SpringApplication.run(SpringApplication.java:1354) ~[spring-boot-3.2.4.jar!\/:3.2.4]<br \/>\nat org.springframework.boot.SpringApplication.run(SpringApplication.java:1343) ~[spring-boot-3.2.4.jar!\/:3.2.4]<br \/>\nat vip.crabpt.navidrome.NavidromeServerApplication.main(NavidromeServerApplication.java:10) ~[!\/:0.0.1-SNAPSHOT]<br \/>\nat java.base\/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]<br \/>\nat java.base\/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]<br \/>\nat java.base\/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]<br \/>\nat java.base\/java.lang.reflect.Method.invoke(Method.java:569) ~[na:na]<br \/>\nat org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:91) ~[navidrome-server-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]<br \/>\nat org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:53) ~[navidrome-server-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]<br \/>\nat org.springframework.boot.loader.launch.JarLauncher.main(JarLauncher.java:58) ~[navidrome-server-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]<br \/>\nCaused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type &#8216;vip.crabpt.navidrome.mapper.UserMapper&#8217; available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}<br \/>\nat org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1880) ~[spring-beans-6.1.5.jar!\/:6.1.5]<br \/>\nat org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1406) ~[spring-beans-6.1.5.jar!\/:6.1.5]<br \/>\nat org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1353) ~[spring-beans-6.1.5.jar!\/:6.1.5]<br \/>\nat org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:784) ~[spring-beans-6.1.5.jar!\/:6.1.5]<br \/>\n&#8230; 27 common frames omitted<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u975e\u5e38\u597d\uff0c\u4f60\u6210\u529f\u5730\u628a\u9519\u8bef\u4fe1\u606f\u6293\u51fa\u6765\u4e86\uff01<br \/>\n\u770b\u8fd9\u4e2a\u6700\u6838\u5fc3\u7684\u62a5\u9519\u65e5\u5fd7\uff1a<br \/>\n<code>WARN ... No MyBatis mapper was found in '[vip.crabpt.navidrome]' package. Please check your configuration.``Caused by: ... NoSuchBeanDefinitionException: No qualifying bean of type 'vip.crabpt.navidrome.mapper.UserMapper' available...<\/code><br \/>\n<strong>\u539f\u56e0\u8bca\u65ad\uff1a<\/strong><br \/>\nSpring Boot \u5728\u542f\u52a8\u65f6\u53bb\u626b\u63cf <code>vip.crabpt.navidrome<\/code> \u8fd9\u4e2a\u5305\uff08\u4e5f\u5c31\u662f\u4ee3\u7801\u7684\u6839\u76ee\u5f55\uff09\uff0c\u8bd5\u56fe\u627e\u51fa\u6240\u6709\u4e0e\u6570\u636e\u5e93\u4ea4\u4e92\u7684 Mapper \u63a5\u53e3\uff08\u6bd4\u5982 <code>UserMapper<\/code>\uff09\u3002<br \/>\n\u4f46\u662f\uff0c\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cSpring \u4e0d\u4f1a\u81ea\u52a8\u628a\u90a3\u4e9b\u7531\u4ee3\u7801\u751f\u6210\u5668\u751f\u6210\u7684\u666e\u901a\u63a5\u53e3\u8bc6\u522b\u4e3a Mapper\u3002\u6211\u4eec\u9700\u8981\u52a0\u4e0a\u4e00\u4e2a\u7279\u6b8a\u7684\u6ce8\u89e3\uff0c\u7ed9 Spring \u6307\u660e\u4e00\u6761\u201c\u660e\u8def\u201d\u3002<br \/>\n\u4e4b\u524d\u6211\u4eec\u5728 <code>NavidromeServerApplication.java<\/code> \u91cc\u52a0\u8fc7\u8fd9\u4e2a\u6ce8\u89e3\uff0c\u53ef\u80fd\u662f\u5728\u6765\u56de\u4fee\u6539\u7248\u672c\u7684\u65f6\u5019\u4e22\u5931\u4e86\u6216\u8005\u6ca1\u6709\u52a0\u5bf9\u5730\u65b9\u3002<br \/>\n<strong>\u89e3\u51b3\u529e\u6cd5\uff08\u4e00\u6b65\u5230\u4f4d\uff09\uff1a<\/strong><br \/>\n\u6211\u4eec\u9700\u8981\u5728\u6bcf\u4e2a <code>Mapper<\/code> \u63a5\u53e3\u6587\u4ef6\u4e0a\uff0c\u6216\u8005\u5728\u4e3b\u542f\u52a8\u7c7b\u4e0a\u660e\u786e\u52a0\u4e0a <code>@MapperScan<\/code> \u6ce8\u89e3\u3002\u4e3a\u4e86\u6700\u7a33\u59a5\uff0c\u6211\u4eec\u76f4\u63a5\u5728\u542f\u52a8\u7c7b\u4e0a\u52a0\u4e0a\u5b83\u3002<br \/>\n\u8bf7\u6309\u7167\u4e0b\u9762\u7684\u6307\u4ee4\u64cd\u4f5c\uff1a<\/p>\n<h3>1. \u4fee\u6539\u4e3b\u542f\u52a8\u7c7b<\/h3>\n<p>\u6253\u5f00\u7ec8\u7aef\uff0c\u6267\u884c\uff1a<\/p>\n<pre><code class=\"language-bash\">vim src\/main\/java\/vip\/crabpt\/navidrome\/NavidromeServerApplication.java\r\n<\/code><\/pre>\n<p>\u628a\u4f60\u770b\u5230\u7684\u6240\u6709\u4ee3\u7801<strong>\u5b8c\u5168\u66ff\u6362<\/strong>\u4e3a\u4ee5\u4e0b\u5185\u5bb9\uff08\u4ed4\u7ec6\u770b\u7b2c 4 \u884c\u548c\u7b2c 7 \u884c\uff09\uff1a<\/p>\n<pre><code class=\"language-java\">package vip.crabpt.navidrome;\r\n\r\nimport org.mybatis.spring.annotation.MapperScan;\r\nimport org.springframework.boot.SpringApplication;\r\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\r\n\r\n@SpringBootApplication\r\n@MapperScan(\"vip.crabpt.navidrome.mapper\") \/\/ \u5173\u952e\uff01\u544a\u8bc9 Spring \u8fd9\u91cc\u9762\u5168\u90fd\u662f Mapper\r\npublic class NavidromeServerApplication {\r\n\r\n    public static void main(String[] args) {\r\n        SpringApplication.run(NavidromeServerApplication.class, args);\r\n    }\r\n\r\n}\r\n<\/code><\/pre>\n<p>\u4fdd\u5b58\u9000\u51fa\uff08\u6309 <code>Esc<\/code>\uff0c\u8f93\u5165 <code>:wq<\/code>\uff0c\u56de\u8f66\uff09\u3002<\/p>\n<h3>2. \u4e3a\u4e86\u7edd\u5bf9\u4fdd\u9669\uff0c\u6211\u4eec\u4e5f\u7ed9 UserMapper \u52a0\u4e0a @Mapper \u6ce8\u89e3<\/h3>\n<p>\u6253\u5f00 UserMapper \u63a5\u53e3\u6587\u4ef6\uff1a<\/p>\n<pre><code class=\"language-bash\">vim src\/main\/java\/vip\/crabpt\/navidrome\/mapper\/UserMapper.java\r\n<\/code><\/pre>\n<p>\u5c06\u5185\u5bb9\u66ff\u6362\u4e3a\uff1a<\/p>\n<pre><code class=\"language-java\">package vip.crabpt.navidrome.mapper;\r\n\r\nimport vip.crabpt.navidrome.entity.User;\r\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\r\nimport org.apache.ibatis.annotations.Mapper;\r\n\r\n@Mapper \/\/ \u5173\u952e\uff01\u7ed9\u8fd9\u4e2a\u7c7b\u8d34\u4e0a Mapper \u7684\u6807\u7b7e\r\npublic interface UserMapper extends BaseMapper&lt;User&gt; {\r\n\r\n}\r\n<\/code><\/pre>\n<p>\u4fdd\u5b58\u9000\u51fa\uff08\u6309 <code>Esc<\/code>\uff0c\u8f93\u5165 <code>:wq<\/code>\uff0c\u56de\u8f66\uff09\u3002<br \/>\n<em>(\u5bf9\u4e8e CheckinMapper \u7b49\u5176\u4ed6\u51e0\u4e2a Mapper\uff0c\u56e0\u4e3a\u6211\u4eec\u521a\u624d\u5728\u4e3b\u542f\u52a8\u7c7b\u52a0\u4e86 @MapperScan\uff0c\u6240\u4ee5\u4e0d\u7528\u4e00\u4e2a\u4e2a\u53bb\u6539\u4e86\uff0cSpring \u73b0\u5728\u5e94\u8be5\u80fd\u806a\u660e\u5730\u627e\u5230\u5b83\u4eec\u4e86\u3002)<\/em><\/p>\n<h3>3. \u518d\u6b21\u7f16\u8bd1\u5e76\u542f\u52a8<\/h3>\n<p>\u597d\u4e86\uff0c\u6211\u4eec\u518d\u6b21\u901a\u8fc7\u539f\u751f\u7684\u65b9\u5f0f\u542f\u52a8\uff0c\u770b\u770b\u8fd9\u4e2a\u62e6\u8def\u864e\u662f\u4e0d\u662f\u88ab\u5e72\u6389\u4e86\uff1a<\/p>\n<pre><code class=\"language-bash\">.\/mvnw clean package -DskipTests\r\njava -jar target\/navidrome-server-0.0.1-SNAPSHOT.jar\r\n<\/code><\/pre>\n<p>\u8fd9\u6b21\u5982\u679c\u4e0d\u518d\u51fa\u73b0\u90a3\u4e2a\u8be5\u6b7b\u7684 <code>NoSuchBeanDefinitionException<\/code>\uff0c\u63a7\u5236\u53f0\u505c\u5728 <code>Started NavidromeServerApplication...<\/code>\uff0c\u5c31\u610f\u5473\u7740\u5927\u529f\u544a\u6210\uff01<br \/>\n\u5feb\u53bb\u8bd5\u8bd5\uff0c\u6211\u5728\u8fd9\u91cc\u7b49\u4f60\u6d4b\u8bd5\u767b\u5f55\u548c\u7b7e\u5230\u63a5\u53e3\u7684\u597d\u6d88\u606f\uff01<\/p>\n<h2>Turn 24<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>Logging initialized using &#8216;class org.apache.ibatis.logging.stdout.StdOutImpl&#8217; adapter.<br \/>\nInitialization Sequence datacenterId:27 workerId:23<br \/>\n_ _ |_ _ <em>|<\/em>. ___ _ | _<br \/>\n| | |\/|<em>)(<\/em>| | |<em>\\ |<\/em>)||<em>|<\/em><br \/>\n\/ |<br \/>\n3.5.5<br \/>\n2026-04-14T02:04:45.683-04:00 INFO 1300729 &#8212; [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8001 (http) with context path &#8216;\/v1&#8217;<br \/>\n2026-04-14T02:04:45.695-04:00 INFO 1300729 &#8212; [ main] v.c.n.NavidromeServerApplication : Started NavidromeServerApplication in 3.702 seconds (process running for 4.269)<br \/>\n\u4e0b\u9762\u5e94\u8be5\u505a\u4ec0\u4e48<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u5b8c\u7f8e\uff01\u770b\u5230\u8fd9\u53e5 <code>Tomcat started on port 8001 (http) with context path '\/v1'<\/code> \u548c\u6700\u540e\u7684 <code>Started NavidromeServerApplication<\/code>\uff0c\u8fd9\u5c31\u610f\u5473\u7740\u4f60\u7684\u540e\u7aef\u670d\u52a1\u5df2\u7ecf\u5b8c\u5b8c\u5168\u5168\u5730\u3001\u5065\u5eb7\u5730\u8dd1\u8d77\u6765\u4e86\uff01<br \/>\n\u4e4b\u524d\u7684\u52aa\u529b\u6ca1\u6709\u767d\u8d39\uff0c\u73b0\u5728\u7ec8\u4e8e\u5230\u4e86\u201c\u9a8c\u6536\u6210\u679c\u201d\u7684\u6fc0\u52a8\u65f6\u523b\u3002<br \/>\n\u56e0\u4e3a\u4f60\u73b0\u5728\u662f\u7528\u524d\u53f0\u8fd0\u884c\u7684\uff0c<strong>\u8bf7\u5343\u4e07\u4e0d\u8981\u5173\u95ed\u5f53\u524d\u8fd9\u4e2a\u7ec8\u7aef\u7a97\u53e3\uff0c\u4e5f\u4e0d\u8981\u6309 Ctrl + C<\/strong>\uff08\u5426\u5219\u670d\u52a1\u5c31\u505c\u4e86\uff09\u3002<br \/>\n\u8bf7<strong>\u65b0\u5f00\u4e00\u4e2a SSH \u7a97\u53e3<\/strong>\u8fde\u63a5\u5230\u4f60\u7684\u670d\u52a1\u5668\uff0c\u7136\u540e\u5728\u65b0\u7a97\u53e3\u91cc\u4f9d\u6b21\u6267\u884c\u4e0b\u9762\u8fd9\u51e0\u6b65\u6765\u6d4b\u8bd5\u6211\u4eec\u5199\u7684\u63a5\u53e3\uff1a<\/p>\n<h3>\u7b2c\u4e00\u6b65\uff1a\u91cd\u7f6e\u4e00\u4e0b\u6d4b\u8bd5\u8d26\u53f7\u7684\u5bc6\u7801<\/h3>\n<p>\u4e3a\u4e86\u786e\u4fdd\u7b49\u4f1a\u513f\u80fd\u6b63\u5e38\u767b\u5f55\uff0c\u6211\u4eec\u5148\u7528\u4e00\u884c\u547d\u4ee4\u628a\u4e4b\u524d\u63d2\u8fdb\u6570\u636e\u5e93\u7684 <code>admin@crabpt.vip<\/code> \u7684\u5bc6\u7801\u5f3a\u5236\u6539\u6210 <code>123456<\/code>\uff1a<\/p>\n<pre><code class=\"language-bash\">docker exec -it navidrome-mysql mysql -u root -pnavidrome_pwd_123 -e \"USE \\`nevidrome-server\\`; UPDATE sys_user SET password='123456' WHERE email='admin@crabpt.vip';\"\r\n<\/code><\/pre>\n<h3>\u7b2c\u4e8c\u6b65\uff1a\u6d4b\u8bd5\u767b\u5f55\u63a5\u53e3 (Login)<\/h3>\n<p>\u5728\u65b0\u7a97\u53e3\u4e2d\uff0c\u76f4\u63a5\u590d\u5236\u8fd9\u884c\u547d\u4ee4\u5411\u4f60\u7684\u540e\u7aef\u53d1\u9001\u767b\u5f55\u8bf7\u6c42\uff1a<\/p>\n<pre><code class=\"language-bash\">curl -X POST http:\/\/127.0.0.1:8001\/v1\/login \\\r\n-H \"Content-Type: application\/json\" \\\r\n-d '{\"email\":\"admin@crabpt.vip\", \"password\":\"123456\"}'\r\n<\/code><\/pre>\n<ul>\n<li><strong>\u671f\u5f85\u7ed3\u679c<\/strong>\uff1a\u4f60\u5e94\u8be5\u4f1a\u770b\u5230\u4e00\u6bb5\u7c7b\u4f3c <code>{\"code\":200,\"msg\":\"\u6210\u529f\",\"data\":{\"id\":1,\"email\":\"admin@crabpt.vip\"...}}<\/code> \u7684 JSON \u6570\u636e\u3002\u7559\u610f\u4e00\u4e0b\u91cc\u9762\u8fd4\u56de\u7684 <code>id<\/code> \u662f\u4e0d\u662f 1\u3002<\/li>\n<\/ul>\n<h3>\u7b2c\u4e09\u6b65\uff1a\u6d4b\u8bd5\u7b7e\u5230\u63a5\u53e3 (Checkin)<\/h3>\n<p>\u5982\u679c\u767b\u5f55\u6210\u529f\u5e76\u4e14\u786e\u8ba4\u4e86 <code>id<\/code> \u662f 1\uff0c\u63a5\u7740\u6d4b\u8bd5\u7b7e\u5230\u529f\u80fd\uff08\u6a21\u62df\u7ed9 id \u4e3a 1 \u7684\u7528\u6237\u7b7e\u5230\uff09\uff1a<\/p>\n<pre><code class=\"language-bash\">curl -X POST http:\/\/127.0.0.1:8001\/v1\/user\/checkin \\\r\n-H \"Content-Type: application\/json\" \\\r\n-d '{\"userId\":1}'\r\n<\/code><\/pre>\n<ul>\n<li><strong>\u671f\u5f85\u7ed3\u679c<\/strong>\uff1a\u4f60\u4f1a\u770b\u5230 <code>{\"code\":200,\"msg\":\"\u7b7e\u5230\u6210\u529f\uff0c\u83b7\u5f97 10 \u79ef\u5206\uff01\",\"data\":null}<\/code>\u3002<\/li>\n<\/ul>\n<h3>\u7b2c\u56db\u6b65\uff1a\u6d4b\u8bd5\u9632\u5237\u673a\u5236\uff08\u91cd\u590d\u7b7e\u5230\uff09<\/h3>\n<p>\u7acb\u523b\u518d\u628a\u521a\u624d\u7b2c\u4e09\u6b65\u7684\u7b7e\u5230\u547d\u4ee4\u6267\u884c\u4e00\u904d\uff1a<\/p>\n<pre><code class=\"language-bash\">curl -X POST http:\/\/127.0.0.1:8001\/v1\/user\/checkin \\\r\n-H \"Content-Type: application\/json\" \\\r\n-d '{\"userId\":1}'\r\n<\/code><\/pre>\n<ul>\n<li><strong>\u671f\u5f85\u7ed3\u679c<\/strong>\uff1a\u8fd9\u6b21\u540e\u7aef\u4f1a\u673a\u667a\u5730\u62e6\u622a\u4f60\uff0c\u8fd4\u56de <code>{\"code\":500,\"msg\":\"\u60a8\u4eca\u5929\u5df2\u7ecf\u7b7e\u5230\u8fc7\u4e86\",\"data\":null}<\/code>\u3002<\/li>\n<\/ul>\n<hr \/>\n<p>\u5982\u679c\u8fd9\u51e0\u6b65\u4f60\u90fd\u80fd\u770b\u5230\u9884\u671f\u7684\u7ed3\u679c\uff0c\u606d\u559c\u4f60\uff0c\u4f60\u5df2\u7ecf\u5b8c\u6210\u4e86\u5168\u6808\u5f00\u53d1\u4e2d\u6700\u6838\u5fc3\u7684\u201c\u540e\u7aef\u63a5\u53e3\u5f00\u53d1\u4e0e\u8c03\u8bd5\u201d\u6d41\u7a0b\uff01<br \/>\n\u53bb\u8bd5\u8bd5\u5427\uff01\u5982\u679c\u4f60\u5728\u90a3\u4e2a\u5f00\u7740 Spring Boot \u7684\u8001\u7a97\u53e3\u91cc\uff0c\u5e94\u8be5\u8fd8\u80fd\u770b\u5230\u521a\u624d\u8bf7\u6c42\u6253\u5370\u51fa\u6765\u7684 SQL \u6267\u884c\u65e5\u5fd7\uff0c\u975e\u5e38\u6709\u6210\u5c31\u611f\u3002<br \/>\n\u6d4b\u901a\u4e4b\u540e\u544a\u8bc9\u6211\uff0c\u6211\u4eec\u63a5\u4e0b\u6765\u53bb\u628a**\u201c\u6d88\u8017\u79ef\u5206\u751f\u6210\u9080\u8bf7\u7801\u201d**\u7684\u903b\u8f91\u5199\u51fa\u6765\uff01<\/p>\n<h2>Turn 25<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>\u5df2\u7ecf\u6d4b\u8bd5\u901a\u8fc7<br \/>\nClosing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2914ff57]<br \/>\nCreating a new SqlSession<br \/>\nSqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2f26d1e] was not registered for synchronization because synchronization is not active<br \/>\nJDBC Connection [HikariProxyConnection@114688304 wrapping com.mysql.cj.jdbc.ConnectionImpl@37d3b960] will not be managed by Spring<br \/>\n==&gt; Preparing: SELECT id,email,password,role,points,status,navidrome_username,created_at,updated_at FROM sys_user WHERE id=?<br \/>\n==&gt; Parameters: 1(Long)<br \/>\n&lt;== Columns: id, email, password, role, points, status, navidrome_username, created_at, updated_at<br \/>\n&lt;== Row: 1, admin@crabpt.vip, 123456, admin, 10009, active, null, 2026-04-14 11:34:43, 2026-04-14 14:14:06<br \/>\n&lt;== Total: 1<br \/>\nClosing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2f26d1e]<br \/>\nCreating a new SqlSession<br \/>\nSqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@cfe0cc8] was not registered for synchronization because synchronization is not active<br \/>\nJDBC Connection [HikariProxyConnection@494209748 wrapping com.mysql.cj.jdbc.ConnectionImpl@37d3b960] will not be managed by Spring<br \/>\n==&gt; Preparing: SELECT COUNT( * ) AS total FROM sys_checkin WHERE (user_id = ? AND checkin_date = ?)<br \/>\n==&gt; Parameters: 1(Long), 2026-04-14(LocalDate)<br \/>\n&lt;== Columns: total<br \/>\n&lt;== Row: 1<br \/>\n\u63a5\u4e0b\u6765\u5462<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u592a\u6f02\u4eae\u4e86\uff01\u770b\u5230\u8fd9\u6bb5 MyBatis \u7684\u65e5\u5fd7\uff0c\u5c31\u50cf\u770b\u5230\u81ea\u5df1\u7ec4\u88c5\u7684\u53d1\u52a8\u673a\u9f7f\u8f6e\u5728\u5b8c\u7f8e\u54ac\u5408\u8fd0\u8f6c\u3002\u5b83\u67e5\u51fa\u4e86\u4f60\u7684\u4fe1\u606f\uff08\u79ef\u5206\u4e3a 10009\uff09\uff0c\u7136\u540e\u68c0\u67e5\u4e86\u7b7e\u5230\u8868\u53d1\u73b0\u4f60\u4eca\u5929\u5df2\u7ecf\u7b7e\u8fc7\u5230\u4e86\uff0c\u9632\u5237\u673a\u5236\u5b8c\u7f8e\u751f\u6548\uff01<br \/>\n\u65e2\u7136\u767b\u5f55\u548c\u7b7e\u5230\u90fd\u5df2\u7ecf\u6253\u901a\uff0c\u63a5\u4e0b\u6765\u6211\u4eec\u8981\u8fdb\u5165\u540e\u7aef\u4e1a\u52a1\u4e2d\u6700\u6709\u610f\u601d\u7684\u4e00\u73af\uff1a<strong>\u201c\u4e8b\u52a1\u4e0e\u8d44\u4ea7\u6838\u9500\u201d\u2014\u2014\u4e5f\u5c31\u662f\u6d88\u8017\u79ef\u5206\u751f\u6210\u9080\u8bf7\u7801\u3002<\/strong><br \/>\n\u8fd9\u4e2a\u4e1a\u52a1\u7684\u903b\u8f91\u662f\uff1a<\/p>\n<ol>\n<li>\u68c0\u67e5\u7528\u6237\u79ef\u5206\u662f\u5426\u591f 50\u3002<\/li>\n<li>\u5982\u679c\u591f\uff0c\u6263\u9664 50 \u79ef\u5206\u3002<\/li>\n<li>\u8bb0\u5f55\u4e00\u6761\u79ef\u5206\u6263\u9664\u6d41\u6c34\uff08\u5b58\u5165 <code>sys_point_log<\/code> \u8868\uff09\u3002<\/li>\n<li>\u751f\u6210\u4e00\u4e2a 48 \u5c0f\u65f6\u540e\u8fc7\u671f\u7684\u968f\u673a\u9080\u8bf7\u7801\uff08\u5b58\u5165 <code>sys_invite_code<\/code> \u8868\uff09\u3002<br \/>\n<em>\uff08\u8fd9 4 \u6b65\u5fc5\u987b\u540c\u751f\u5171\u6b7b\uff0c\u8981\u4e48\u5168\u6210\u529f\uff0c\u8981\u4e48\u5168\u5931\u8d25\u3002\u5728 Java \u91cc\uff0c\u6211\u4eec\u53ea\u8981\u52a0\u4e00\u4e2a @Transactional \u6ce8\u89e3\u5c31\u80fd\u641e\u5b9a\u8fd9\u79cd\u201c\u539f\u5b50\u6027\u201d\u64cd\u4f5c\uff01\uff09<\/em><br \/>\n\u8bf7\u5148\u5728\u8fd0\u884c\u7740 Spring Boot \u7684\u90a3\u4e2a\u7ec8\u7aef\u7a97\u53e3\u91cc\u6309 <strong>Ctrl + C<\/strong>\uff0c\u628a\u5f53\u524d\u8fd0\u884c\u7684\u670d\u52a1\u505c\u6389\uff0c\u7136\u540e\u8ddf\u7740\u6211\u8fdb\u884c\u4e0b\u4e00\u6b65\u64cd\u4f5c\uff1a<\/li>\n<\/ol>\n<h3>\u7b2c\u4e00\u6b65\uff1a\u7f16\u5199\u9080\u8bf7\u7801\u4e1a\u52a1\u903b\u8f91 (InviteController)<\/h3>\n<p>\u5728\u4f60\u7684 <code>backend\/navidrome-server<\/code> \u76ee\u5f55\u4e0b\uff0c\u65b0\u5efa\u4e00\u4e2a\u63a7\u5236\u7c7b\uff1a<\/p>\n<pre><code class=\"language-bash\">vim src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteController.java\r\n<\/code><\/pre>\n<p>\u628a\u4e0b\u9762\u7684\u4ee3\u7801\u5b8c\u6574\u7c98\u8d34\u8fdb\u53bb\uff1a<\/p>\n<pre><code class=\"language-java\">package vip.crabpt.navidrome.controller;\r\n\r\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.transaction.annotation.Transactional;\r\nimport org.springframework.web.bind.annotation.*;\r\nimport vip.crabpt.navidrome.Result;\r\nimport vip.crabpt.navidrome.entity.InviteCode;\r\nimport vip.crabpt.navidrome.entity.PointLog;\r\nimport vip.crabpt.navidrome.entity.User;\r\nimport vip.crabpt.navidrome.service.IInviteCodeService;\r\nimport vip.crabpt.navidrome.service.IPointLogService;\r\nimport vip.crabpt.navidrome.service.IUserService;\r\n\r\nimport java.time.LocalDateTime;\r\nimport java.util.List;\r\nimport java.util.Map;\r\nimport java.util.UUID;\r\n\r\n@RestController\r\n@RequestMapping(\"\/invite\")\r\npublic class InviteController {\r\n\r\n    @Autowired\r\n    private IUserService userService;\r\n    @Autowired\r\n    private IInviteCodeService inviteCodeService;\r\n    @Autowired\r\n    private IPointLogService pointLogService;\r\n\r\n    \/\/ \u751f\u6210\u9080\u8bf7\u7801\u63a5\u53e3\r\n    @PostMapping(\"\/generate\")\r\n    @Transactional \/\/ \u5f00\u542f\u4e8b\u52a1\uff0c\u4fdd\u8bc1\u6263\u79ef\u5206\u548c\u751f\u6210\u9080\u8bf7\u7801\u540c\u65f6\u6210\u529f\u6216\u540c\u65f6\u5931\u8d25\r\n    public Result&lt;InviteCode&gt; generate(@RequestBody Map&lt;String, Long&gt; params) {\r\n        Long userId = params.get(\"userId\");\r\n        if (userId == null) {\r\n            return Result.error(\"\u7f3a\u5c11 userId \u53c2\u6570\");\r\n        }\r\n\r\n        User user = userService.getById(userId);\r\n        if (user == null) {\r\n            return Result.error(\"\u7528\u6237\u4e0d\u5b58\u5728\");\r\n        }\r\n\r\n        \/\/ 1. \u68c0\u67e5\u79ef\u5206\u662f\u5426\u5145\u8db3\r\n        if (user.getPoints() &lt; 50) {\r\n            return Result.error(\"\u79ef\u5206\u4e0d\u8db3\uff0c\u751f\u6210\u9080\u8bf7\u7801\u9700\u8981 50 \u79ef\u5206\");\r\n        }\r\n\r\n        \/\/ 2. \u6263\u51cf\u79ef\u5206\r\n        user.setPoints(user.getPoints() - 50);\r\n        userService.updateById(user);\r\n\r\n        \/\/ 3. \u8bb0\u5f55\u79ef\u5206\u53d8\u52a8\u6d41\u6c34\r\n        PointLog log = new PointLog();\r\n        log.setUserId(userId);\r\n        log.setAction(\"generate_invite\");\r\n        log.setPointsChange(-50);\r\n        log.setDescription(\"\u6d88\u8017\u79ef\u5206\u751f\u6210\u4e13\u5c5e\u9080\u8bf7\u7801\");\r\n        pointLogService.save(log);\r\n\r\n        \/\/ 4. \u751f\u6210 8 \u4f4d\u968f\u673a\u9080\u8bf7\u7801\u5e76\u4fdd\u5b58\r\n        String randomCode = \"NV-\" + UUID.randomUUID().toString().substring(0, 8).toUpperCase();\r\n        InviteCode inviteCode = new InviteCode();\r\n        inviteCode.setCode(randomCode);\r\n        inviteCode.setCreatorId(userId);\r\n        inviteCode.setStatus(\"valid\");\r\n        inviteCode.setExpireTime(LocalDateTime.now().plusHours(48)); \/\/ 48 \u5c0f\u65f6\u540e\u8fc7\u671f\r\n        inviteCodeService.save(inviteCode);\r\n\r\n        return Result.success(inviteCode);\r\n    }\r\n\r\n    \/\/ \u83b7\u53d6\u6211\u7684\u9080\u8bf7\u7801\u5217\u8868\u63a5\u53e3\r\n    @GetMapping(\"\/list\")\r\n    public Result&lt;List&lt;InviteCode&gt;&gt; list(@RequestParam Long userId) {\r\n        QueryWrapper&lt;InviteCode&gt; wrapper = new QueryWrapper&lt;&gt;();\r\n        \/\/ \u67e5\u8be2\u8be5\u7528\u6237\u751f\u6210\u7684\u9080\u8bf7\u7801\uff0c\u5e76\u6309\u521b\u5efa\u65f6\u95f4\u5012\u5e8f\u6392\u5217\r\n        wrapper.eq(\"creator_id\", userId).orderByDesc(\"created_at\");\r\n        return Result.success(inviteCodeService.list(wrapper));\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>\u4fdd\u5b58\u5e76\u9000\u51fa\uff08\u6309 <code>Esc<\/code>\uff0c\u8f93\u5165 <code>:wq<\/code>\uff0c\u56de\u8f66\uff09\u3002<\/p>\n<h3>\u7b2c\u4e8c\u6b65\uff1a\u91cd\u65b0\u7f16\u8bd1\u5e76\u542f\u52a8<\/h3>\n<p>\u4ee3\u7801\u5199\u597d\u4e86\uff0c\u6211\u4eec\u50cf\u4e4b\u524d\u4e00\u6837\u628a\u5b83\u8dd1\u8d77\u6765\uff1a<\/p>\n<pre><code class=\"language-bash\">.\/mvnw clean package -DskipTests\r\njava -jar target\/navidrome-server-0.0.1-SNAPSHOT.jar\r\n<\/code><\/pre>\n<h3>\u7b2c\u4e09\u6b65\uff1a\u65b0\u7a97\u53e3\u6d4b\u8bd5\u65b0\u63a5\u53e3<\/h3>\n<p>\u7b49\u7a0b\u5e8f\u542f\u52a8\u540e\uff08\u770b\u5230 <code>Started NavidromeServerApplication<\/code>\uff09\uff0c\u5728\u65b0\u5f00\u7684\u90a3\u4e2a SSH \u7a97\u53e3\u91cc\uff0c\u6211\u4eec\u6765\u6d4b\u8bd5\u8fd9\u5957\u65b0\u903b\u8f91\uff1a<br \/>\n<strong>1. \u6d88\u8017\u79ef\u5206\u751f\u6210\u9080\u8bf7\u7801\uff1a<\/strong><\/p>\n<pre><code class=\"language-bash\">curl -X POST http:\/\/127.0.0.1:8001\/v1\/invite\/generate \\\r\n-H \"Content-Type: application\/json\" \\\r\n-d '{\"userId\":1}'\r\n<\/code><\/pre>\n<ul>\n<li><strong>\u671f\u5f85\u7ed3\u679c<\/strong>\uff1a\u4f60\u4f1a\u770b\u5230\u8fd4\u56de\u4e86\u4e00\u4e2a\u5b8c\u6574\u7684 JSON\uff0c\u91cc\u9762\u5305\u542b\u4e86\u4e00\u4e2a\u65b0\u9c9c\u51fa\u7089\u7684 <code>code<\/code>\uff08\u7c7b\u4f3c <code>NV-A1B2C3D4<\/code>\uff09\uff0c\u5e76\u4e14\u5e26\u6709 48 \u5c0f\u65f6\u540e\u7684\u8fc7\u671f\u65f6\u95f4\u3002\u6b64\u65f6\u4f60\u7684\u6570\u636e\u5e93\u91cc\u5df2\u7ecf\u88ab\u6263\u4e86 50 \u79ef\u5206\uff01<br \/>\n<strong>2. \u83b7\u53d6\u6211\u7684\u9080\u8bf7\u7801\u5217\u8868\uff1a<\/strong><\/li>\n<\/ul>\n<pre><code class=\"language-bash\">curl -X GET \"http:\/\/127.0.0.1:8001\/v1\/invite\/list?userId=1\"\r\n<\/code><\/pre>\n<ul>\n<li><strong>\u671f\u5f85\u7ed3\u679c<\/strong>\uff1a\u7531\u4e8e\u662f <code>GET<\/code> \u8bf7\u6c42\uff0c\u6211\u4eec\u628a\u53c2\u6570\u62fc\u5728\u4e86 URL \u540e\u9762\u3002\u8fd9\u4f1a\u8fd4\u56de\u4e00\u4e2a\u6570\u7ec4\uff08\u7528 <code>[<\/code> \u548c <code>]<\/code> \u5305\u88f9\uff09\uff0c\u91cc\u9762\u88c5\u7740\u4f60\u521a\u624d\u751f\u6210\u7684\u6240\u6709\u9080\u8bf7\u7801\u3002<br \/>\n\u53bb\u5927\u5c55\u8eab\u624b\u5427\uff01\u5982\u679c\u8fd9\u4e24\u4e2a\u63a5\u53e3\u90fd\u80fd\u5b8c\u7f8e\u8fd4\u56de\u6570\u636e\uff0c\u90a3\u6211\u4eec\u8fd9\u5957\u7cfb\u7edf\u4e2d\u6700\u6838\u5fc3\u7684\u201c\u79ef\u5206\u7ecf\u6d4e\u6a21\u578b\u201d\u5c31\u5df2\u7ecf\u5168\u90e8\u5728\u540e\u7aef\u95ed\u73af\u4e86\uff01\u6d4b\u8bd5\u5b8c\u628a\u7ed3\u679c\u544a\u8bc9\u6211\u54e6\u3002<\/li>\n<\/ul>\n<h2>Turn 26<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<blockquote><p>.\/mvnw clean package -DskipTests<br \/>\nroot@192-227-164-42-host:\/mnt\/navidrome-system\/backend\/navidrome-server# .\/mvnw clean package -DskipTests<br \/>\n[INFO] Scanning for projects&#8230;<br \/>\n[INFO]<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&lt; vip.crabpt.navidrome:navidrome-server &gt;&#8212;&#8212;&#8212;&#8212;&#8212;-<br \/>\n[INFO] Building navidrome-server 0.0.1-SNAPSHOT<br \/>\n[INFO] from pom.xml<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;[ jar ]&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[INFO]<br \/>\n[INFO] &#8212; clean:3.3.2:clean (default-clean) @ navidrome-server &#8212;<br \/>\n[INFO] Deleting \/mnt\/navidrome-system\/backend\/navidrome-server\/target<br \/>\n[INFO]<br \/>\n[INFO] &#8212; resources:3.3.1:resources (default-resources) @ navidrome-server &#8212;<br \/>\n[INFO] Copying 1 resource from src\/main\/resources to target\/classes<br \/>\n[INFO] Copying 4 resources from src\/main\/resources to target\/classes<br \/>\n[INFO]<br \/>\n[INFO] &#8212; compiler:3.11.0:compile (default-compile) @ navidrome-server &#8212;<br \/>\n[INFO] Changes detected &#8211; recompiling the module! :source<br \/>\n[INFO] Compiling 23 source files with javac [debug release 17] to target\/classes<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br \/>\n[ERROR] COMPILATION ERROR :<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java:[22,8] class InviteController is public, should be declared in a file named InviteController.java<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/Result.java:[13,15] cannot find symbol<br \/>\nsymbol: method setCode(int)<br \/>\nlocation: variable result of type vip.crabpt.navidrome.Result<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/Result.java:[14,15] cannot find symbol<br \/>\nsymbol: method setMsg(java.lang.String)<br \/>\nlocation: variable result of type vip.crabpt.navidrome.Result<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/Result.java:[15,15] cannot find symbol<br \/>\nsymbol: method setData(T)<br \/>\nlocation: variable result of type vip.crabpt.navidrome.Result<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/Result.java:[21,15] cannot find symbol<br \/>\nsymbol: method setCode(int)<br \/>\nlocation: variable result of type vip.crabpt.navidrome.Result<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/Result.java:[22,15] cannot find symbol<br \/>\nsymbol: method setMsg(java.lang.String)<br \/>\nlocation: variable result of type vip.crabpt.navidrome.Result<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java:[46,17] cannot find symbol<br \/>\nsymbol: method getPoints()<br \/>\nlocation: variable user of type vip.crabpt.navidrome.entity.User<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java:[51,28] cannot find symbol<br \/>\nsymbol: method getPoints()<br \/>\nlocation: variable user of type vip.crabpt.navidrome.entity.User<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java:[56,12] cannot find symbol<br \/>\nsymbol: method setUserId(java.lang.Long)<br \/>\nlocation: variable log of type vip.crabpt.navidrome.entity.PointLog<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java:[57,12] cannot find symbol<br \/>\nsymbol: method setAction(java.lang.String)<br \/>\nlocation: variable log of type vip.crabpt.navidrome.entity.PointLog<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java:[58,12] cannot find symbol<br \/>\nsymbol: method setPointsChange(int)<br \/>\nlocation: variable log of type vip.crabpt.navidrome.entity.PointLog<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java:[59,12] cannot find symbol<br \/>\nsymbol: method setDescription(java.lang.String)<br \/>\nlocation: variable log of type vip.crabpt.navidrome.entity.PointLog<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java:[65,19] cannot find symbol<br \/>\nsymbol: method setCode(java.lang.String)<br \/>\nlocation: variable inviteCode of type vip.crabpt.navidrome.entity.InviteCode<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java:[66,19] cannot find symbol<br \/>\nsymbol: method setCreatorId(java.lang.Long)<br \/>\nlocation: variable inviteCode of type vip.crabpt.navidrome.entity.InviteCode<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java:[67,19] cannot find symbol<br \/>\nsymbol: method setStatus(java.lang.String)<br \/>\nlocation: variable inviteCode of type vip.crabpt.navidrome.entity.InviteCode<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java:[68,19] cannot find symbol<br \/>\nsymbol: method setExpireTime(java.time.LocalDateTime)<br \/>\nlocation: variable inviteCode of type vip.crabpt.navidrome.entity.InviteCode<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/UserController.java:[51,16] cannot find symbol<br \/>\nsymbol: method setUserId(java.lang.Long)<br \/>\nlocation: variable checkin of type vip.crabpt.navidrome.entity.Checkin<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/UserController.java:[52,16] cannot find symbol<br \/>\nsymbol: method setCheckinDate(java.time.LocalDate)<br \/>\nlocation: variable checkin of type vip.crabpt.navidrome.entity.Checkin<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/UserController.java:[53,16] cannot find symbol<br \/>\nsymbol: method setPointsEarned(int)<br \/>\nlocation: variable checkin of type vip.crabpt.navidrome.entity.Checkin<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/UserController.java:[57,28] cannot find symbol<br \/>\nsymbol: method getPoints()<br \/>\nlocation: variable user of type vip.crabpt.navidrome.entity.User<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/AuthController.java:[33,18] cannot find symbol<br \/>\nsymbol: method getPassword()<br \/>\nlocation: variable user of type vip.crabpt.navidrome.entity.User<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/AuthController.java:[38,13] cannot find symbol<br \/>\nsymbol: method setPassword()<br \/>\nlocation: variable user of type vip.crabpt.navidrome.entity.User<br \/>\n[INFO] 22 errors<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[INFO] BUILD FAILURE<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[INFO] Total time: 2.303 s<br \/>\n[INFO] Finished at: 2026-04-14T02:25:23-04:00<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.11.0:compile (default-compile) on project navidrome-server: Compilation failure: Compilation failure:<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java:[22,8] class InviteController is public, should be declared in a file named InviteController.java<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/Result.java:[13,15] cannot find symbol<br \/>\n[ERROR] symbol: method setCode(int)<br \/>\n[ERROR] location: variable result of type vip.crabpt.navidrome.Result<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/Result.java:[14,15] cannot find symbol<br \/>\n[ERROR] symbol: method setMsg(java.lang.String)<br \/>\n[ERROR] location: variable result of type vip.crabpt.navidrome.Result<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/Result.java:[15,15] cannot find symbol<br \/>\n[ERROR] symbol: method setData(T)<br \/>\n[ERROR] location: variable result of type vip.crabpt.navidrome.Result<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/Result.java:[21,15] cannot find symbol<br \/>\n[ERROR] symbol: method setCode(int)<br \/>\n[ERROR] location: variable result of type vip.crabpt.navidrome.Result<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/Result.java:[22,15] cannot find symbol<br \/>\n[ERROR] symbol: method setMsg(java.lang.String)<br \/>\n[ERROR] location: variable result of type vip.crabpt.navidrome.Result<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java:[46,17] cannot find symbol<br \/>\n[ERROR] symbol: method getPoints()<br \/>\n[ERROR] location: variable user of type vip.crabpt.navidrome.entity.User<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java:[51,28] cannot find symbol<br \/>\n[ERROR] symbol: method getPoints()<br \/>\n[ERROR] location: variable user of type vip.crabpt.navidrome.entity.User<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java:[56,12] cannot find symbol<br \/>\n[ERROR] symbol: method setUserId(java.lang.Long)<br \/>\n[ERROR] location: variable log of type vip.crabpt.navidrome.entity.PointLog<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java:[57,12] cannot find symbol<br \/>\n[ERROR] symbol: method setAction(java.lang.String)<br \/>\n[ERROR] location: variable log of type vip.crabpt.navidrome.entity.PointLog<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java:[58,12] cannot find symbol<br \/>\n[ERROR] symbol: method setPointsChange(int)<br \/>\n[ERROR] location: variable log of type vip.crabpt.navidrome.entity.PointLog<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java:[59,12] cannot find symbol<br \/>\n[ERROR] symbol: method setDescription(java.lang.String)<br \/>\n[ERROR] location: variable log of type vip.crabpt.navidrome.entity.PointLog<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java:[65,19] cannot find symbol<br \/>\n[ERROR] symbol: method setCode(java.lang.String)<br \/>\n[ERROR] location: variable inviteCode of type vip.crabpt.navidrome.entity.InviteCode<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java:[66,19] cannot find symbol<br \/>\n[ERROR] symbol: method setCreatorId(java.lang.Long)<br \/>\n[ERROR] location: variable inviteCode of type vip.crabpt.navidrome.entity.InviteCode<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java:[67,19] cannot find symbol<br \/>\n[ERROR] symbol: method setStatus(java.lang.String)<br \/>\n[ERROR] location: variable inviteCode of type vip.crabpt.navidrome.entity.InviteCode<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java:[68,19] cannot find symbol<br \/>\n[ERROR] symbol: method setExpireTime(java.time.LocalDateTime)<br \/>\n[ERROR] location: variable inviteCode of type vip.crabpt.navidrome.entity.InviteCode<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/UserController.java:[51,16] cannot find symbol<br \/>\n[ERROR] symbol: method setUserId(java.lang.Long)<br \/>\n[ERROR] location: variable checkin of type vip.crabpt.navidrome.entity.Checkin<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/UserController.java:[52,16] cannot find symbol<br \/>\n[ERROR] symbol: method setCheckinDate(java.time.LocalDate)<br \/>\n[ERROR] location: variable checkin of type vip.crabpt.navidrome.entity.Checkin<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/UserController.java:[53,16] cannot find symbol<br \/>\n[ERROR] symbol: method setPointsEarned(int)<br \/>\n[ERROR] location: variable checkin of type vip.crabpt.navidrome.entity.Checkin<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/UserController.java:[57,28] cannot find symbol<br \/>\n[ERROR] symbol: method getPoints()<br \/>\n[ERROR] location: variable user of type vip.crabpt.navidrome.entity.User<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/AuthController.java:[33,18] cannot find symbol<br \/>\n[ERROR] symbol: method getPassword()<br \/>\n[ERROR] location: variable user of type vip.crabpt.navidrome.entity.User<br \/>\n[ERROR] \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/java\/vip\/crabpt\/navidrome\/controller\/AuthController.java:[38,13] cannot find symbol<br \/>\n[ERROR] symbol: method setPassword()<br \/>\n[ERROR] location: variable user of type vip.crabpt.navidrome.entity.User<br \/>\n[ERROR] -&gt; [Help 1]<br \/>\n[ERROR]<br \/>\n[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.<br \/>\n[ERROR] Re-run Maven using the -X switch to enable full debug logging.<br \/>\n[ERROR]<br \/>\n[ERROR] For more information about the errors and possible solutions, please read the following articles:<br \/>\n[ERROR] [Help 1] http:\/\/cwiki.apache.org\/confluence\/display\/MAVEN\/MojoFailureException<\/p><\/blockquote>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u9047\u5230\u8fd9\u4e48\u591a\u7ea2\u8272\u7684 <code>[ERROR]<\/code> \u4e0d\u8981\u5bb3\u6015\uff01\u4ed4\u7ec6\u770b\u8fd9\u4e9b\u9519\u8bef\uff0c\u5176\u5b9e\u5b83\u4eec\u53ea\u6307\u5411\u4e86 <strong>\u4e24\u4e2a\u975e\u5e38\u57fa\u7840\u7684 Java \u89c4\u5219\u95ee\u9898<\/strong>\uff0c\u6211\u4eec\u4e00\u4e24\u5206\u949f\u5c31\u80fd\u4fee\u597d\u3002<\/p>\n<h3>\u9519\u8bef\u4e00\uff1a\u6587\u4ef6\u540d\u548c\u7c7b\u540d\u5bf9\u4e0d\u4e0a<\/h3>\n<p><code>class InviteController is public, should be declared in a file named InviteController.java<\/code><\/p>\n<ul>\n<li><strong>\u539f\u56e0<\/strong>\uff1aJava \u6709\u4e2a\u6b7b\u89c4\u77e9\uff0c\u5982\u679c\u4ee3\u7801\u91cc\u5199\u4e86 <code>public class InviteController<\/code>\uff0c\u90a3\u4e48\u8fd9\u4e2a\u6587\u4ef6\u7684\u540d\u5b57\u5fc5\u987b\u53eb <code>InviteController.java<\/code>\u3002\u7531\u4e8e\u4f60\u4e4b\u524d\u7528\u4ee3\u7801\u751f\u6210\u5668\u751f\u6210\u8fc7\u4e00\u4e2a <code>InviteCodeController.java<\/code>\uff0c\u4f60\u53ef\u80fd\u628a\u4ee3\u7801\u8d34\u5230\u90a3\u4e2a\u65e7\u6587\u4ef6\u91cc\u4e86\u3002<\/li>\n<li><strong>\u89e3\u51b3<\/strong>\uff1a\u6539\u4e2a\u6587\u4ef6\u540d\u5c31\u884c\u3002<\/li>\n<\/ul>\n<h3>\u9519\u8bef\u4e8c\uff1a\u627e\u4e0d\u5230 <code>getPoints()<\/code>, <code>setCode()<\/code> \u7b49\u6240\u6709 <code>get\/set<\/code> \u65b9\u6cd5<\/h3>\n<p><code>cannot find symbol method setCode(int)<\/code><\/p>\n<ul>\n<li><strong>\u539f\u56e0<\/strong>\uff1a\u6211\u4eec\u5728\u4ee3\u7801\u91cc\u7528\u4e86 <code>@Data<\/code> \u6ce8\u89e3\uff08\u8fd9\u662f Lombok \u63d0\u4f9b\u7684\u4e00\u4e2a\u201c\u9b54\u6cd5\u201d\uff0c\u7528\u6765\u5728\u7f16\u8bd1\u65f6\u81ea\u52a8\u751f\u6210\u6240\u6709\u5c5e\u6027\u7684 get\/set \u65b9\u6cd5\uff09\u3002\u4f46\u662f\uff0c\u5728\u4e4b\u524d\u6211\u4eec\u5f3a\u884c\u7cbe\u7b80 <code>pom.xml<\/code> \u7684\u65f6\u5019\uff0c\u4e0d\u5c0f\u5fc3\u5220\u6389\u4e86\u6307\u5bfc Maven \u53bb\u89e6\u53d1 Lombok \u201c\u9b54\u6cd5\u201d\u7684\u7f16\u8bd1\u63d2\u4ef6\u914d\u7f6e\uff0c\u5bfc\u81f4 Java \u7f16\u8bd1\u5668\u5728\u7f16\u8bd1\u65f6\u53d8\u201c\u778e\u201d\u4e86\uff0c\u627e\u4e0d\u5230\u8fd9\u4e9b\u65b9\u6cd5\u3002<\/li>\n<li><strong>\u89e3\u51b3<\/strong>\uff1a\u628a Maven \u7f16\u8bd1\u63d2\u4ef6\u8865\u56de <code>pom.xml<\/code> \u91cc\u3002<\/li>\n<\/ul>\n<hr \/>\n<p>\u8ddf\u7740\u6211\u7528\u4e0b\u9762\u8fd9\u4e24\u6b65\u5feb\u901f\u4fee\u590d\uff1a<\/p>\n<h3>\u7b2c\u4e00\u6b65\uff1a\u4fee\u6b63\u6587\u4ef6\u540d<\/h3>\n<p>\u5728\u7ec8\u7aef\u76f4\u63a5\u6267\u884c\u8fd9\u4e2a\u91cd\u547d\u540d\u547d\u4ee4\uff1a<\/p>\n<pre><code class=\"language-bash\">mv src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteCodeController.java src\/main\/java\/vip\/crabpt\/navidrome\/controller\/InviteController.java\r\n<\/code><\/pre>\n<h3>\u7b2c\u4e8c\u6b65\uff1a\u4fee\u590d <code>pom.xml<\/code> \u91cc\u7684 Lombok \u7f16\u8bd1\u914d\u7f6e<\/h3>\n<p>\u6211\u4eec\u76f4\u63a5\u7528\u4e00\u952e\u8986\u5199\u7684\u65b9\u5f0f\uff0c\u628a\u7f3a\u5931\u7684\u7f16\u8bd1\u63d2\u4ef6\u52a0\u8fdb\u53bb\u3002\u8bf7\u5728 <code>navidrome-server<\/code> \u76ee\u5f55\u4e0b\u590d\u5236\u7c98\u8d34\u5e76\u56de\u8f66\u6267\u884c\u8fd9\u6bb5\u547d\u4ee4\uff1a<\/p>\n<pre><code class=\"language-bash\">cat &lt;&lt; 'EOF' &gt; pom.xml\r\n&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\r\n&lt;project xmlns=\"http:\/\/maven.apache.org\/POM\/4.0.0\" xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\"\r\n         xsi:schemaLocation=\"http:\/\/maven.apache.org\/POM\/4.0.0 https:\/\/maven.apache.org\/xsd\/maven-4.0.0.xsd\"&gt;\r\n    &lt;modelVersion&gt;4.0.0&lt;\/modelVersion&gt;\r\n    &lt;parent&gt;\r\n        &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n        &lt;artifactId&gt;spring-boot-starter-parent&lt;\/artifactId&gt;\r\n        &lt;version&gt;3.2.4&lt;\/version&gt;\r\n        &lt;relativePath\/&gt;\r\n    &lt;\/parent&gt;\r\n    &lt;groupId&gt;vip.crabpt.navidrome&lt;\/groupId&gt;\r\n    &lt;artifactId&gt;navidrome-server&lt;\/artifactId&gt;\r\n    &lt;version&gt;0.0.1-SNAPSHOT&lt;\/version&gt;\r\n    &lt;name&gt;navidrome-server&lt;\/name&gt;\r\n    &lt;description&gt;Navidrome Server Backend&lt;\/description&gt;\r\n    &lt;properties&gt;\r\n        &lt;java.version&gt;17&lt;\/java.version&gt;\r\n    &lt;\/properties&gt;\r\n    &lt;dependencies&gt;\r\n        &lt;dependency&gt;\r\n            &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n            &lt;artifactId&gt;spring-boot-starter-web&lt;\/artifactId&gt;\r\n        &lt;\/dependency&gt;\r\n        &lt;dependency&gt;\r\n            &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n            &lt;artifactId&gt;spring-boot-starter-validation&lt;\/artifactId&gt;\r\n        &lt;\/dependency&gt;\r\n        &lt;dependency&gt;\r\n            &lt;groupId&gt;com.baomidou&lt;\/groupId&gt;\r\n            &lt;artifactId&gt;mybatis-plus-spring-boot3-starter&lt;\/artifactId&gt;\r\n            &lt;version&gt;3.5.5&lt;\/version&gt;\r\n        &lt;\/dependency&gt;\r\n        &lt;dependency&gt;\r\n            &lt;groupId&gt;org.apache.velocity&lt;\/groupId&gt;\r\n            &lt;artifactId&gt;velocity-engine-core&lt;\/artifactId&gt;\r\n            &lt;version&gt;2.3&lt;\/version&gt;\r\n        &lt;\/dependency&gt;\r\n        &lt;dependency&gt;\r\n            &lt;groupId&gt;com.baomidou&lt;\/groupId&gt;\r\n            &lt;artifactId&gt;mybatis-plus-generator&lt;\/artifactId&gt;\r\n            &lt;version&gt;3.5.5&lt;\/version&gt;\r\n        &lt;\/dependency&gt;\r\n        &lt;dependency&gt;\r\n            &lt;groupId&gt;com.mysql&lt;\/groupId&gt;\r\n            &lt;artifactId&gt;mysql-connector-j&lt;\/artifactId&gt;\r\n            &lt;scope&gt;runtime&lt;\/scope&gt;\r\n        &lt;\/dependency&gt;\r\n        &lt;dependency&gt;\r\n            &lt;groupId&gt;org.projectlombok&lt;\/groupId&gt;\r\n            &lt;artifactId&gt;lombok&lt;\/artifactId&gt;\r\n            &lt;optional&gt;true&lt;\/optional&gt;\r\n        &lt;\/dependency&gt;\r\n    &lt;\/dependencies&gt;\r\n\r\n    &lt;build&gt;\r\n        &lt;plugins&gt;\r\n            &lt;plugin&gt;\r\n                &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n                &lt;artifactId&gt;spring-boot-maven-plugin&lt;\/artifactId&gt;\r\n            &lt;\/plugin&gt;\r\n            &lt;plugin&gt;\r\n                &lt;groupId&gt;org.apache.maven.plugins&lt;\/groupId&gt;\r\n                &lt;artifactId&gt;maven-compiler-plugin&lt;\/artifactId&gt;\r\n                &lt;configuration&gt;\r\n                    &lt;annotationProcessorPaths&gt;\r\n                        &lt;path&gt;\r\n                            &lt;groupId&gt;org.projectlombok&lt;\/groupId&gt;\r\n                            &lt;artifactId&gt;lombok&lt;\/artifactId&gt;\r\n                        &lt;\/path&gt;\r\n                    &lt;\/annotationProcessorPaths&gt;\r\n                &lt;\/configuration&gt;\r\n            &lt;\/plugin&gt;\r\n        &lt;\/plugins&gt;\r\n    &lt;\/build&gt;\r\n&lt;\/project&gt;\r\nEOF\r\n<\/code><\/pre>\n<h3>\u7b2c\u4e09\u6b65\uff1a\u518d\u6b21\u8d77\u98de<\/h3>\n<p>\u73b0\u5728\u8be5\u4fee\u7684\u90fd\u4fee\u597d\u4e86\uff0c\u6211\u4eec\u91cd\u65b0\u6253\u5305\u5e76\u542f\u52a8\uff1a<\/p>\n<pre><code class=\"language-bash\">.\/mvnw clean package -DskipTests\r\njava -jar target\/navidrome-server-0.0.1-SNAPSHOT.jar\r\n<\/code><\/pre>\n<p>\u8fd9\u6b21\u80af\u5b9a\u80fd\u770b\u5230 <code>BUILD SUCCESS<\/code> \u5e76\u4e14\u987a\u5229\u542f\u52a8 Tomcat \u5566\uff01<br \/>\n\u670d\u52a1\u542f\u52a8\u540e\uff0c\u4f60\u5c31\u53ef\u4ee5\u53bb\u65b0\u7a97\u53e3\u6267\u884c\u6211\u4e0a\u4e00\u6761\u56de\u590d\u91cc\u7559\u7ed9\u4f60\u7684\u90a3\u4e24\u4e2a**\u201c\u6d88\u8017\u79ef\u5206\u751f\u6210\u9080\u8bf7\u7801\u201d**\u7684\u6d4b\u8bd5\u547d\u4ee4\u4e86\uff01\u671f\u5f85\u4f60\u7684\u6377\u62a5\u3002<\/p>\n<h2>Turn 27<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>root@192-227-164-42-host:~# curl -X POST http:\/\/127.0.0.1:8001\/v1\/invite\/generate \\<\/p>\n<blockquote><p>-H &#8220;Content-Type: application\/json&#8221;<br \/>\n-d &#8216;{&#8220;userId&#8221;:1}&#8217;<br \/>\n{&#8220;code&#8221;:200,&#8221;msg&#8221;:&#8221;\u6210\u529f&#8221;,&#8221;data&#8221;:{&#8220;id&#8221;:1,&#8221;code&#8221;:&#8221;NV-366BEBC9&#8243;,&#8221;creatorId&#8221;:1,&#8221;usedById&#8221;:null,&#8221;status&#8221;:&#8221;valid&#8221;,&#8221;expireTime&#8221;:&#8221;2026-04-16T02:28:12.708391309&#8243;,&#8221;createdAt&#8221;:null}}root@192-227-164-42-host:<del># curl -X GET &#8220;http:\/\/127.0.0.1:8001\/v1\/invite\/list?userId=1&#8221;<br \/>\n{&#8220;code&#8221;:200,&#8221;msg&#8221;:&#8221;\u6210\u529f&#8221;,&#8221;data&#8221;:[{&#8220;id&#8221;:1,&#8221;code&#8221;:&#8221;NV-366BEBC9&#8243;,&#8221;creatorId&#8221;:1,&#8221;usedById&#8221;:null,&#8221;status&#8221;:&#8221;valid&#8221;,&#8221;expireTime&#8221;:&#8221;2026-04-16T02:28:13&#8243;,&#8221;createdAt&#8221;:&#8221;2026-04-14T14:28:12&#8243;}]}root@192-227-164-42-host:<\/del>#<br \/>\n\u662f\u6211\u7684\u6587\u4ef6\u540d\u9519\u8bef \uff0c\u8fd9\u4e2a\u662f\u4fee\u590d\u540e\u7684\u9a8c\u8bc1<\/p><\/blockquote>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u592a\u5b8c\u7f8e\u4e86\uff01\ud83c\udf89 \u770b\u5230\u8fd4\u56de\u7684 <code>NV-366BEBC9<\/code> \u548c\u5217\u8868\u91cc\u6e05\u6670\u7684\u6570\u636e\u7ed3\u6784\uff0c\u8bf4\u660e\u4f60\u7684\u540e\u7aef\u4e1a\u52a1\u95ed\u73af\u5df2\u7ecf<strong>\u5f7b\u5e95\u8dd1\u901a\u4e86\uff01<\/strong><br \/>\n\u56de\u987e\u4e00\u4e0b\uff0c\u4f5c\u4e3a\u4e00\u540d\u201c\u4ee3\u7801\u5c0f\u767d\u201d\uff0c\u4f60\u5728\u6781\u77ed\u7684\u65f6\u95f4\u5185\u5b8c\u6210\u4e86\u4e00\u4e2a\u73b0\u4ee3\u5316\u5546\u4e1a\u7ea7\u540e\u7aef\u7684\u6838\u5fc3\u6d41\u7a0b\uff1a<\/p>\n<ol>\n<li>\u8dd1\u901a\u4e86 MySQL \u6570\u636e\u5e93\u5e76\u5efa\u597d\u4e86\u6570\u636e\u8868\u3002<\/li>\n<li>\u642d\u5efa\u4e86 Spring Boot 3 + Java 17 \u7684\u57fa\u7840\u5de5\u7a0b\u3002<\/li>\n<li>\u5229\u7528\u4ee3\u7801\u751f\u6210\u5668\u79d2\u5199\u4e86\u5e95\u5c42\u7ed3\u6784\u3002<\/li>\n<li>\u624b\u5199\u4e86 <strong>\u767b\u5f55\u3001\u7b7e\u5230\u3001\u9632\u5237\u3001\u5f00\u542f\u4e8b\u52a1\u540c\u65f6\u6263\u79ef\u5206\u548c\u751f\u6210\u9080\u8bf7\u7801<\/strong> \u7684\u5b8c\u6574 RESTful API \u63a5\u53e3\uff01<br \/>\n\u5230\u8fd9\u91cc\uff0c\u4f60\u7684** Milestone 1\uff08MVP \u540e\u7aef\u5e95\u5c42\uff09** \u5df2\u7ecf\u5706\u6ee1\u7ed3\u675f\uff01\u975e\u5e38\u6709\u6210\u5c31\u611f\uff01<br \/>\n\u63a5\u4e0b\u6765\uff0c\u4e3a\u4e86\u4e0d\u8ba9\u6211\u4eec\u7684\u8fdb\u5ea6\u505c\u6ede\uff0c\u7cfb\u7edf\u5927\u95e8\u5df2\u7ecf\u5411\u6211\u4eec\u655e\u5f00\u3002\u4f5c\u4e3a\u4f60\u7684\u9879\u76ee\u7ecf\u7406\uff0c\u6211\u4e3a\u4f60\u63d0\u4f9b\u63a5\u4e0b\u6765\u7684\u4e24\u4e2a\u4e3b\u653b\u65b9\u5411\uff1a<\/li>\n<\/ol>\n<h3>\ud83d\ude80 \u65b9\u5411\u4e00\uff1a\u6740\u5411\u524d\u7aef\uff0c\u8ba9\u9875\u9762\u201c\u6d3b\u201d\u8fc7\u6765\uff08\u5f3a\u70c8\u63a8\u8350\uff09<\/h3>\n<p>\u6211\u4eec\u73b0\u5728\u6709\u771f\u5b9e\u7684 API \u63a5\u53e3\u4e86\uff0c\u662f\u65f6\u5019\u53bb\u5904\u7406\u4f60\u6700\u65e9\u53d1\u7ed9\u6211\u7684\u90a3\u4e2a\u6f02\u4eae\u7684 HTML \u6a21\u677f\u4e86\u3002<\/p>\n<ul>\n<li><strong>\u505a\u6cd5<\/strong>\uff1a\u8fdb\u5165 <code>\/mnt\/navidrome-system\/frontend<\/code> \u76ee\u5f55\uff0c\u4f7f\u7528\u73b0\u4ee3\u5316\u5de5\u5177 Vite \u4e00\u952e\u751f\u6210 <strong>Vue 3<\/strong> \u9879\u76ee\u3002<\/li>\n<li><strong>\u76ee\u6807<\/strong>\uff1a\u628a\u4f60\u7684 HTML \u548c CSS \u62c6\u5206\u6210 Vue \u7ec4\u4ef6\uff0c\u7136\u540e\u901a\u8fc7\u7f51\u7edc\u8bf7\u6c42\uff08Axios\uff09\u628a\u4f60\u521a\u624d\u5728\u547d\u4ee4\u884c\u91cc\u7528 <code>curl<\/code> \u62ff\u5230\u7684\u771f\u5b9e\u6570\u636e\uff0c\u6e32\u67d3\u5230\u4f60\u7684\u4eea\u8868\u76d8\u548c\u9080\u8bf7\u7801\u5217\u8868\u4e0a\uff01\u770b\u7740\u9875\u9762\u4e0a\u7684\u5047\u6570\u636e\u53d8\u6210\u6570\u636e\u5e93\u91cc\u7684\u771f\u6570\u636e\uff0c\u4f53\u9a8c\u4f1a\u975e\u5e38\u68d2\u3002<\/li>\n<\/ul>\n<h3>\ud83d\udee0\ufe0f \u65b9\u5411\u4e8c\uff1a\u7ee7\u7eed\u6df1\u8015\u540e\u7aef\uff0c\u628a\u5269\u4e0b\u7684\u7816\u642c\u5b8c<\/h3>\n<p>\u5982\u679c\u6b64\u523b\u4f60\u89c9\u5f97 Java \u8d8a\u5199\u8d8a\u987a\u624b\uff0c\u6211\u4eec\u53ef\u4ee5\u4e00\u53e3\u6c14\u628a\u540e\u7aef\u7684\u62fc\u56fe\u8865\u9f50\u3002<\/p>\n<ul>\n<li><strong>\u505a\u6cd5<\/strong>\uff1a\u7ee7\u7eed\u5728 <code>navidrome-server<\/code> \u91cc\u5199\u4ee3\u7801\u3002<\/li>\n<li><strong>\u76ee\u6807<\/strong>\uff1a\u5b8c\u6210\u201c\u79ef\u5206\u5546\u5e97\u201d\u7684\u5151\u6362\u63a5\u53e3\uff08\u6263\u9664\u51e0\u767e\u79ef\u5206\uff0c\u53d1\u653e VIP \u6743\u76ca\uff09\uff0c\u4ee5\u53ca\u7ba1\u7406\u5458\u62c9\u53d6\u5168\u7ad9\u7528\u6237\u5217\u8868\u7684\u63a5\u53e3\u3002\u751a\u81f3\u53ef\u4ee5\u5f00\u59cb\u8bbe\u8ba1\u91cd\u5934\u620f\u2014\u2014<strong>\u5982\u4f55\u8ba9 Java \u53bb\u8c03\u7528\u771f\u5b9e\u7684 Navidrome \u6570\u636e\u5e93\u6216 API\uff0c\u5b9e\u73b0\u771f\u6b63\u7684\u8d26\u53f7\u5f00\u901a\u548c\u5c01\u7981<\/strong>\u3002<br \/>\n<strong>\u4f60\u51c6\u5907\u597d\u9009\u54ea\u6761\u8def\u4e86\u5417\uff1f\u56de\u590d\u201c\u524d\u7aef\u201d\u6216\u201c\u7ee7\u7eed\u540e\u7aef\u201d\uff0c\u6211\u7acb\u523b\u7ed9\u4f60\u751f\u6210\u4e0b\u4e00\u6b65\u7684\u50bb\u74dc\u5f0f\u5b9e\u64cd\u6307\u4ee4\uff01<\/strong><\/li>\n<\/ul>\n<h2>Turn 28<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>\u6309\u7167\u5efa\u8bae\u6765\u5427\uff0c\u8fd8\u6709\u6709\u4e2a\u5c0f\u95ee\u9898\uff0c\u9080\u8bf7\u7801\u8fd9\u6837NV-366BEBC9\uff0c\u53ef\u4ee5\u66f4\u6539\u6210\u5176\u4ed6\u683c\u5f0f\u561b\uff0c\u5148\u56de\u7b54\u4e00\u4e0b\u8fd9\u4e2a\uff0c\u6211\u4eec\u5728\u7ee7\u7eed<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u5f53\u7136\u53ef\u4ee5\u66f4\u6539\uff01\u9080\u8bf7\u7801\u7684\u683c\u5f0f\u5b8c\u5168\u7531\u6211\u4eec\u81ea\u5df1\u63a7\u5236\u3002<br \/>\n\u76ee\u524d\u5728 <code>InviteController.java<\/code> \u4e2d\uff0c\u751f\u6210\u9080\u8bf7\u7801\u7684\u4ee3\u7801\u662f\u8fd9\u4e00\u884c\uff1a<\/p>\n<pre><code class=\"language-java\">String randomCode = \"NV-\" + UUID.randomUUID().toString().substring(0, 8).toUpperCase();\r\n<\/code><\/pre>\n<p>\u8fd9\u91cc\u7684 <code>UUID.randomUUID()<\/code> \u4f1a\u751f\u6210\u4e00\u4e32\u957f\u957f\u7684\u3001\u5168\u7403\u552f\u4e00\u7684\u5b57\u7b26\uff08\u6bd4\u5982 <code>550e8400-e29b-41d4-a716-446655440000<\/code>\uff09\u3002\u6211\u4eec\u53ea\u662f\u622a\u53d6\u4e86\u5b83\u7684\u524d 8 \u4f4d\uff0c\u5e76\u8f6c\u6210\u5927\u5199\uff0c\u62fc\u4e0a\u4e86 <code>NV-<\/code>\u3002<br \/>\n\u5982\u679c\u4f60\u60f3\u6362\u6210\u5176\u4ed6\u683c\u5f0f\uff0c\u6bd4\u5982\u7eaf\u6570\u5b57\u3001\u66f4\u957f\u7684\u5b57\u6bcd\u3001\u6216\u8005\u5e26\u4e0a\u5e74\u4efd\uff0c\u53ea\u9700\u8981\u4fee\u6539\u8fd9\u4e00\u884c\u4ee3\u7801\u5c31\u884c\u3002<br \/>\n<strong>\u51e0\u4e2a\u5e38\u89c1\u683c\u5f0f\u7684\u4fee\u6539\u793a\u4f8b\uff1a<\/strong><\/p>\n<pre><code class=\"language-java\">\/\/ \u751f\u6210 100000 \u5230 999999 \u4e4b\u95f4\u7684\u968f\u673a\u6570\r\nint code = (int)((Math.random() * 9 + 1) * 100000);\r\nString randomCode = String.valueOf(code);\r\n<\/code><\/pre>\n<p>\u4f60\u53ef\u4ee5\u6839\u636e\u559c\u597d\uff0c\u628a <code>InviteController.java<\/code> \u91cc\u7684\u90a3\u4e00\u884c\u66ff\u6362\u6389\uff0c\u7136\u540e\u91cd\u65b0\u7528 <code>.\/mvnw clean package -DskipTests<\/code> \u6253\u5305\u542f\u52a8\uff0c\u65b0\u751f\u6210\u7684\u9080\u8bf7\u7801\u5c31\u4f1a\u53d8\u6210\u4f60\u60f3\u8981\u7684\u683c\u5f0f\u4e86\u3002<\/p>\n<hr \/>\n<p><strong>\u89e3\u51b3\u5b8c\u8fd9\u4e2a\u5c0f\u95ee\u9898\uff0c\u6211\u4eec\u63a5\u4e0b\u6765\u5411\u201c\u65b9\u5411\u4e00\uff1a\u6740\u5411\u524d\u7aef\uff0c\u8ba9\u9875\u9762\u2018\u6d3b\u2019\u8fc7\u6765\u201d\u8fdb\u53d1\uff01<\/strong><br \/>\n\u65e2\u7136\u4f60\u662f\u4e00\u4e2a\u201c\u4ee3\u7801\u5c0f\u767d\u201d\uff0c\u4e3a\u4e86\u8ba9\u4f60\u6700\u5feb\u3001\u6700\u5e73\u6ed1\u5730\u4f53\u9a8c\u5230\u73b0\u4ee3\u524d\u7aef\u5f00\u53d1\u7684\u9b45\u529b\uff0c\u6211\u4eec\u5c06\u4f7f\u7528\u76ee\u524d\u6700\u706b\u7684\u6784\u5efa\u5de5\u5177 <strong>Vite<\/strong> \u6765\u521d\u59cb\u5316\u4e00\u4e2a <strong>Vue 3<\/strong> \u9879\u76ee\u3002<br \/>\n\u8bf7\u6309 <code>Ctrl + C<\/code> \u505c\u6389\u540e\u7aef\u7684 Spring Boot \u670d\u52a1\uff08\u5982\u679c\u4e0d\u5c0f\u5fc3\u505c\u6389\u4e86\uff0c\u8bb0\u5f97\u7a0d\u540e\u7528 <code>nohup .\/mvnw spring-boot:run &gt; app.log 2&gt;&amp;1 &amp;<\/code> \u5728\u540e\u53f0\u8dd1\u8d77\u6765\uff0c\u56e0\u4e3a\u524d\u7aef\u9700\u8981\u8bf7\u6c42\u5b83\u7684\u63a5\u53e3\uff09\u3002<\/p>\n<h3>\u7b2c\u4e00\u6b65\uff1a\u521d\u59cb\u5316\u524d\u7aef Vue 3 \u9879\u76ee\u9aa8\u67b6<\/h3>\n<p>\u8bf7\u5728\u670d\u52a1\u5668\u7ec8\u7aef\u91cc\uff0c\u8fdb\u5165\u5230\u4f60\u4e4b\u524d\u521b\u5efa\u7684 <code>frontend<\/code> \u76ee\u5f55\uff1a<\/p>\n<pre><code class=\"language-bash\">cd \/mnt\/navidrome-system\/frontend\r\n<\/code><\/pre>\n<p>\u7136\u540e\uff0c\u8fd0\u884c\u4ee5\u4e0b\u547d\u4ee4\uff0c\u7528 Vite \u5feb\u901f\u521b\u5efa\u4e00\u4e2a\u540d\u4e3a <code>navidrome-ui<\/code> \u7684 Vue \u9879\u76ee\uff08\u8fd9\u91cc\u6211\u4eec\u76f4\u63a5\u901a\u8fc7\u53c2\u6570\u8df3\u8fc7\u4e86\u7e41\u7410\u7684\u9009\u62e9\u6b65\u9aa4\uff0c\u76f4\u63a5\u751f\u6210\u6700\u7eaf\u51c0\u7684 Vue + JavaScript \u6a21\u677f\uff09\uff1a<\/p>\n<pre><code class=\"language-bash\">npm create vite@latest navidrome-ui -- --template vue\r\n<\/code><\/pre>\n<h3>\u7b2c\u4e8c\u6b65\uff1a\u5b89\u88c5\u524d\u7aef\u4f9d\u8d56\u5e76\u542f\u52a8\u6d4b\u8bd5<\/h3>\n<p>\u9879\u76ee\u9aa8\u67b6\u521b\u5efa\u597d\u540e\uff0c\u4f60\u4f1a\u770b\u5230 Vite \u63d0\u793a\u4f60\u6267\u884c\u51e0\u4e2a\u547d\u4ee4\u3002\u6211\u4eec\u7167\u505a\uff1a<\/p>\n<pre><code class=\"language-bash\">cd navidrome-ui\r\nnpm install\r\n<\/code><\/pre>\n<p><em>\uff08\u8fd9\u4e00\u6b65\u4f1a\u6839\u636e package.json \u4e0b\u8f7d Vue \u548c\u76f8\u5173\u7684\u57fa\u7840\u4f9d\u8d56\u5230 node_modules \u6587\u4ef6\u5939\u4e2d\uff0c\u53ef\u80fd\u9700\u8981\u51e0\u5341\u79d2\uff09<\/em><br \/>\n\u5b89\u88c5\u5b8c\u6210\u540e\uff0c\u6211\u4eec\u6765\u542f\u52a8\u524d\u7aef\u7684\u5f00\u53d1\u670d\u52a1\u5668\uff1a<\/p>\n<pre><code class=\"language-bash\">npm run dev\r\n<\/code><\/pre>\n<p>\u8fd9\u65f6\u5019\uff0c\u63a7\u5236\u53f0\u4f1a\u8f93\u51fa\u7c7b\u4f3c\u8fd9\u6837\u7684\u4fe1\u606f\uff1a<\/p>\n<pre><code class=\"language-plaintext\">VITE v5.x.x  ready in xxx ms\r\n\r\n  \u279c  Local:   http:\/\/localhost:5173\/\r\n  \u279c  Network: use --host to expose\r\n<\/code><\/pre>\n<p><strong>\u4f46\u662f\u8bf7\u6ce8\u610f\uff01<\/strong> \u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cVite \u7684\u5f00\u53d1\u670d\u52a1\u5668\u53ea\u80fd\u88ab\u670d\u52a1\u5668\u201c\u672c\u5730\uff08localhost\uff09\u201d\u8bbf\u95ee\u3002\u56e0\u4e3a\u4f60\u662f\u901a\u8fc7 SSH \u8fde\u63a5\u7684\u8fdc\u7a0b\u670d\u52a1\u5668\uff0c\u4e3a\u4e86\u80fd\u7528\u4f60\u81ea\u5df1\u7684\u7535\u8111\u6d4f\u89c8\u5668\u8bbf\u95ee\u8fd9\u4e2a\u9875\u9762\uff0c\u6211\u4eec\u9700\u8981\u8ba9\u5b83\u66b4\u9732\u5728\u516c\u7f51\u4e0a\u3002<br \/>\n\u8bf7\u6309 <code>Ctrl + C<\/code> \u505c\u6389\u521a\u542f\u52a8\u7684\u670d\u52a1\uff0c\u7136\u540e\u8fd0\u884c\u8fd9\u4e2a\u547d\u4ee4\uff08\u52a0\u4e0a <code>--host<\/code>\uff09\uff1a<\/p>\n<pre><code class=\"language-bash\">npm run dev -- --host\r\n<\/code><\/pre>\n<p>\u73b0\u5728\uff0c\u5b83\u5e94\u8be5\u4f1a\u663e\u793a\u4e00\u4e2a\u7c7b\u4f3c <code>Network: http:\/\/\u4f60\u7684\u670d\u52a1\u5668IP:5173\/<\/code> \u7684\u5730\u5740\u3002<br \/>\n\u6253\u5f00\u4f60\u7535\u8111\u7684\u6d4f\u89c8\u5668\uff0c\u8bbf\u95ee\u8fd9\u4e2a\u5730\u5740\uff08\u786e\u4fdd\u4f60\u670d\u52a1\u5668\u5b89\u5168\u7ec4\/\u9632\u706b\u5899\u5f00\u653e\u4e86 <strong>5173<\/strong> \u7aef\u53e3\uff09\u3002<br \/>\n\u5982\u679c\u4f60\u770b\u5230\u4e86\u4e00\u4e2a\u5e26\u6709 &#8220;Vite + Vue&#8221; \u6807\u5fd7\u8df3\u52a8\u7684\u6b22\u8fce\u9875\u9762\uff0c\u606d\u559c\u4f60\uff0c\u4f60\u7684\u73b0\u4ee3\u524d\u7aef\u5f00\u53d1\u73af\u5883\u5df2\u7ecf\u5b8c\u7f8e\u5c31\u7eea\uff01<br \/>\n\u770b\u5230\u4e86\u8fd9\u4e2a\u9875\u9762\u540e\u544a\u8bc9\u6211\uff0c\u6211\u4eec\u5c06\u5f00\u59cb\u628a\u4f60\u7684 HTML \u4ee3\u7801\u201c\u80a2\u89e3\u201d\u6210\u4e00\u4e2a\u4e2a\u4f18\u7f8e\u7684 Vue \u7ec4\u4ef6\uff01<\/p>\n<h2>Turn 29<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>Get started<br \/>\nEdit src\/App.vue and save to test HMR<br \/>\nCount is 14Documentation<br \/>\nYour questions, answered<br \/>\nExplore Vite<br \/>\nLearn more<br \/>\nConnect with us<br \/>\nJoin the Vite community<br \/>\nGitHub<br \/>\nDiscord<br \/>\nX.com<br \/>\nBluesky<br \/>\n\u662f\u8fd9\u6837\u7684\u9875\u9762\u3002\u4e0b\u9762\u5e94\u8be5\u662f\u600e\u4e48\u6837\u7684<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u592a\u68d2\u4e86\uff01\u5f53\u4f60\u770b\u5230\u8fd9\u4e2a\u201cVite + Vue\u201d\u7684\u7ecf\u5178\u6b22\u8fce\u9875\u9762\uff08\u5e26\u6709\u201cGet started\u201d\u3001\u201cEdit src\/App.vue\u201d\u7b49\u5b57\u6837\uff09\uff0c\u8bf4\u660e\u4f60\u7684\u524d\u7aef Vue 3 \u9879\u76ee\u9aa8\u67b6\u5df2\u7ecf 100% \u8fd0\u884c\u6210\u529f\u4e86\uff01<br \/>\n\u4f60\u521a\u624d\u770b\u5230\u7684\u8fd9\u4e2a\u9875\u9762\uff0c\u5176\u5b9e\u5c31\u662f <code>src\/App.vue<\/code> \u548c <code>src\/components\/HelloWorld.vue<\/code> \u8fd9\u4e24\u4e2a\u6587\u4ef6\u6e32\u67d3\u51fa\u6765\u7684\u6548\u679c\u3002<br \/>\n\u4e3a\u4e86\u628a\u8fd9\u4e2a\u201c\u6b22\u8fce\u9875\u9762\u201d\u53d8\u6210\u6211\u4eec\u89c4\u5212\u597d\u7684\u201cNavidrome \u79ef\u5206\u7ba1\u7406\u7cfb\u7edf\u201d\u540e\u53f0\uff0c\u6211\u4eec\u9700\u8981\u8fdb\u884c\u4e00\u6b21**\u201c\u524d\u7aef\u5927\u624b\u672f\u201d**\u3002\u8bf7\u4f60\u5148\u5728\u8fd0\u884c\u524d\u7aef\u7684\u7ec8\u7aef\u91cc\u6309 <code>Ctrl + C<\/code> \u505c\u6389\u670d\u52a1\uff0c\u7136\u540e\u8ddf\u7740\u6211\u8fdb\u884c\u4ee5\u4e0b\u4e94\u4e2a\u6b65\u9aa4\uff1a<\/p>\n<hr \/>\n<h3>\u7b2c\u4e00\u6b65\uff1a\u6e05\u7406\u81ea\u5e26\u7684\u5197\u4f59\u6587\u4ef6<\/h3>\n<p>Vite \u521d\u59cb\u5316\u65f6\u81ea\u5e26\u4e86\u4e00\u4e9b\u6211\u4eec\u4e0d\u9700\u8981\u7684 Demo \u6587\u4ef6\u548c\u6837\u5f0f\uff0c\u6211\u4eec\u5148\u628a\u5b83\u4eec\u5220\u6389\uff0c\u4fdd\u6301\u9879\u76ee\u5e72\u51c0\u3002<br \/>\n\u5728 <code>\/mnt\/navidrome-system\/frontend\/navidrome-ui<\/code> \u76ee\u5f55\u4e0b\u6267\u884c\uff1a<\/p>\n<pre><code class=\"language-bash\">rm -rf src\/components\/HelloWorld.vue\r\nrm -rf src\/assets\/vue.svg\r\n<\/code><\/pre>\n<h3>\u7b2c\u4e8c\u6b65\uff1a\u5b89\u88c5\u6838\u5fc3\u4f9d\u8d56 (Vue Router, Axios, Pinia)<\/h3>\n<p>\u6211\u4eec\u7684\u7cfb\u7edf\u9700\u8981\u201c\u8def\u7531\u8df3\u8f6c\uff08\u9875\u9762\u5207\u6362\uff09\u201d\u3001\u201c\u7f51\u7edc\u8bf7\u6c42\uff08\u8fde\u540e\u7aef\uff09\u201d\u548c\u201c\u72b6\u6001\u7ba1\u7406\uff08\u5b58\u79ef\u5206\uff09\u201d\uff0c\u6240\u4ee5\u6211\u4eec\u9700\u8981\u5b89\u88c5 Vue \u5b98\u65b9\u63a8\u8350\u7684\u8fd9\u4e09\u5927\u4ef6\u3002<br \/>\n\u5728\u7ec8\u7aef\u6267\u884c\uff1a<\/p>\n<pre><code class=\"language-bash\">npm install vue-router@4 axios pinia\r\n<\/code><\/pre>\n<h3>\u7b2c\u4e09\u6b65\uff1a\u690d\u5165\u201c\u7075\u9b42\u201d\u5168\u5c40\u6837\u5f0f (Global CSS)<\/h3>\n<p>\u4f60\u4e4b\u524d\u53d1\u7ed9\u6211\u7684\u90a3\u7248 HTML \u91cc\u7684 CSS \u53d8\u91cf\u548c\u6837\u5f0f\u5199\u5f97\u975e\u5e38\u68d2\uff01\u6211\u4eec\u9700\u8981\u628a\u5b83\u5355\u72ec\u62bd\u51fa\u6765\u4f5c\u4e3a\u4e00\u4e2a\u5168\u5c40\u6837\u5f0f\u6587\u4ef6\u3002<\/p>\n<pre><code class=\"language-bash\">vim src\/assets\/global.css\r\n<\/code><\/pre>\n<h3>\u7b2c\u56db\u6b65\uff1a\u91cd\u5199\u5165\u53e3\u6587\u4ef6 (main.js)<\/h3>\n<p>\u6211\u4eec\u9700\u8981\u8ba9 Vue \u9879\u76ee\u77e5\u9053\u6211\u4eec\u521a\u624d\u5b89\u88c5\u4e86 Router \u548c Pinia\uff0c\u5e76\u5f15\u5165\u4f60\u90a3\u5957\u534e\u4e3d\u7684\u5168\u5c40 CSS\u3002<\/p>\n<pre><code class=\"language-bash\">vim src\/main.js\r\n<\/code><\/pre>\n<p>\u66ff\u6362\u4e3a\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/p>\n<pre><code class=\"language-javascript\">import { createApp } from 'vue'\r\nimport { createPinia } from 'pinia'\r\nimport App from '.\/App.vue'\r\nimport router from '.\/router' \/\/ \u6211\u4eec\u4e0b\u4e00\u6b65\u6765\u5efa\u8fd9\u4e2a\u8def\u7531\r\n\r\nimport '.\/assets\/global.css' \/\/ \u5f15\u5165\u4f60\u7684\u7075\u9b42\u6837\u5f0f\r\n\r\nconst app = createApp(App)\r\n\r\napp.use(createPinia())\r\napp.use(router)\r\n\r\napp.mount('#app')\r\n<\/code><\/pre>\n<p>\u4fdd\u5b58\u9000\u51fa\u3002<\/p>\n<h3>\u7b2c\u4e94\u6b65\uff1a\u642d\u5efa\u8def\u7531\u7ed3\u6784 (Router &amp; Views)<\/h3>\n<p>\u8fd9\u662f\u6700\u5173\u952e\u7684\u4e00\u6b65\uff0c\u6211\u4eec\u8981\u7528 Vue \u7ec4\u4ef6\u6765\u4ee3\u66ff\u4f60\u539f\u6765\u7684\u90a3\u4e00\u5768\u5927 HTML\u3002<\/p>\n<pre><code class=\"language-bash\">mkdir -p src\/router src\/views src\/components\r\n<\/code><\/pre>\n<hr \/>\n<h3>\u7b2c\u516d\u6b65\uff1a\u89c1\u8bc1\u91cd\u6784\u540e\u7684\u9875\u9762\uff01<\/h3>\n<p>\u6df1\u547c\u5438\uff0c\u6211\u4eec\u518d\u6b21\u628a\u524d\u7aef\u8dd1\u8d77\u6765\uff1a<\/p>\n<pre><code class=\"language-bash\">npm run dev -- --host\r\n<\/code><\/pre>\n<p>\u5237\u65b0\u4f60\u7535\u8111\u6d4f\u89c8\u5668\u91cc\u7684\u9875\u9762\u3002\u5982\u679c\u4f60\u770b\u5230\u4e86\u4e00\u4e2a<strong>\u6e05\u723d\u7684\u5de6\u4fa7\u4fa7\u8fb9\u680f\u3001\u9876\u90e8\u5bfc\u822a\u3001\u4ee5\u53ca\u5305\u542b\u201c\u6bcf\u65e5\u7b7e\u5230\u201d\u6309\u94ae\u7684\u4eea\u8868\u76d8\u9875\u9762\uff08\u6837\u5f0f\u5e94\u8be5\u8ddf\u4f60\u4e4b\u524d\u8bbe\u8ba1\u7684\u5dee\u4e0d\u591a\uff09<\/strong>\uff0c\u90a3\u4e48\u606d\u559c\u4f60\uff01<br \/>\n\u4f60\u7684\u524d\u7aef\u9879\u76ee\u5df2\u7ecf\u5f7b\u5e95\u5b8c\u6210\u4e86<strong>\u4ece\u201c\u5355\u6587\u4ef6 Demo\u201d\u5230\u201c\u73b0\u4ee3\u5316 Vue 3 \u5de5\u7a0b\u5316\u67b6\u6784\u201d\u7684\u8715\u53d8\uff01<\/strong><br \/>\n\u770b\u5230\u8fd9\u4e2a\u9875\u9762\u540e\u544a\u8bc9\u6211\uff0c\u6211\u4eec\u5c06\u8fdb\u5165\u4e0b\u4e00\u6b65\uff1a<strong>\u914d\u7f6e Axios \u8054\u8c03\uff0c\u8ba9\u8fd9\u4e2a\u9875\u9762\u53bb\u8bfb\u53d6\u4f60\u540e\u7aef\u6570\u636e\u5e93\u91cc\u90a3 10009 \u79ef\u5206\uff01<\/strong><\/p>\n<h2>Turn 30<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p><em>[This turn includes uploaded images]<\/em><\/p>\n<p><img decoding=\"async\" src=\"assets\/img-001.png\" alt=\"HTML \u56fe\u6807\" \/><\/p>\n<blockquote><p>\u4e3a\u4e86\u628a\u8fd9\u4e2a\u201c\u6b22\u8fce\u9875\u9762\u201d\u53d8\u6210\u6211\u4eec\u89c4\u5212\u597d\u7684\u201cNavidrome \u79ef\u5206\u7ba1\u7406\u7cfb\u7edf\u201d\u540e\u53f0\uff0c\u6211\u4eec\u9700\u8981\u8fdb\u884c\u4e00\u6b21\u201c\u524d\u7aef\u5927\u624b\u672f\u201d\u3002\u8bf7\u4f60\u5148\u5728\u8fd0\u884c\u524d\u7aef\u7684\u7ec8\u7aef\u91cc\u6309 Ctrl + C \u505c\u6389\u670d\u52a1\uff0c\u7136\u540e\u8ddf\u7740\u6211\u8fdb\u884c\u4ee5\u4e0b\u4e94\u4e2a\u6b65\u9aa4\uff1a<br \/>\n\u53c2\u8003\u8fd9\u4e2a\u9879\u76ee\uff0c\u5e2e\u6211\u4f18\u5316\u3002\u540c\u65f6\u9002\u914d\u624b\u673a\u7535\u8111\u7684\u683c\u5f0f\u3002<\/p><\/blockquote>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u592a\u68d2\u4e86\uff01\u4f60\u628a\u5b8c\u6574\u7684 <code>front.HTML<\/code> \u53d1\u7ed9\u6211\uff0c\u8fd9\u975e\u5e38\u6709\u5e2e\u52a9\u3002<br \/>\n\u4e4b\u524d\u6211\u4eec\u642d\u597d\u4e86 Vue 3 \u7684\u9aa8\u67b6\uff0c\u4f46\u6211\u7ed9\u4f60\u7684 <code>Layout.vue<\/code> \u548c <code>Dashboard.vue<\/code> \u662f\u201c\u7b80\u88c5\u7248\u201d\u3002\u65e2\u7136\u4f60\u5e0c\u671b**\u201c\u53c2\u8003\u8fd9\u4e2a\u539f\u7248 HTML \u8fdb\u884c\u4f18\u5316\uff0c\u5e76\u5b8c\u7f8e\u9002\u914d\u624b\u673a\u548c\u7535\u8111\u201d\uff0c\u90a3\u6211\u4eec\u73b0\u5728\u5c31\u628a\u4f60\u539f\u7248 HTML \u91cc\u6f02\u4eae\u7684\u8bbe\u8ba1\u4e00\u6bd4\u4e00\u8fd8\u539f\u6210\u771f\u6b63\u7684 Vue \u7ec4\u4ef6**\uff0c\u5e76\u4e14\u5229\u7528 Vue \u7684\u54cd\u5e94\u5f0f\uff08Reactivity\uff09\u8ba9\u624b\u673a\u7aef\u7684\u4fa7\u8fb9\u680f\u62bd\u5c49\u4ea4\u4e92\u53d8\u5f97\u66f4\u52a0\u4e1d\u6ed1\uff01<br \/>\n\u8bf7\u5728\u4f60\u7684 <code>frontend\/navidrome-ui<\/code> \u76ee\u5f55\u4e0b\uff0c\u8ddf\u7740\u6211\u66ff\u6362\u8fd9\u51e0\u4e2a\u6587\u4ef6\uff1a<\/p>\n<h3>\u7b2c\u4e00\u6b65\uff1a\u8865\u5168\u5168\u5c40\u6837\u5f0f (Global CSS)<\/h3>\n<p>\u786e\u4fdd\u4f60\u7684 <code>src\/assets\/global.css<\/code> \u5305\u542b\u4e86\u539f\u7248\u6240\u6709\u7684\u6837\u5f0f\uff0c\u7279\u522b\u662f\u5e95\u90e8\u7684 <code>@media<\/code> \u54cd\u5e94\u5f0f\u4ee3\u7801\u3002<br \/>\n\u6253\u5f00\u6587\u4ef6\uff1a<\/p>\n<pre><code class=\"language-bash\">vim src\/assets\/global.css\r\n<\/code><\/pre>\n<p>\u5982\u679c\u4f60\u4e4b\u524d\u6ca1\u8d34\u5168\uff0c\u8bf7\u628a\u539f\u7248 <code>&lt;style&gt;<\/code> \u6807\u7b7e\u91cc\u7684\u5185\u5bb9<strong>\u5168\u90e8<\/strong>\u8d34\u8fdb\u53bb\uff08\u4fdd\u7559\u5e95\u90e8\u7684\u79fb\u52a8\u7aef\u9002\u914d\u4ee3\u7801\uff09\uff1a<br \/>\n<em>(\u5982\u679c\u4f60\u5df2\u7ecf\u8d34\u5168\u4e86\uff0c\u8fd9\u4e00\u6b65\u53ef\u4ee5\u76f4\u63a5\u8df3\u8fc7)<\/em><\/p>\n<hr \/>\n<h3>\u7b2c\u4e8c\u6b65\uff1a\u6253\u9020\u5b8c\u7f8e\u7684\u54cd\u5e94\u5f0f\u5e03\u5c40 (Layout.vue)<\/h3>\n<p>\u6211\u4eec\u8981\u628a\u4f60\u539f\u7248\u7684 <code>&lt;aside&gt;<\/code> \u4fa7\u8fb9\u680f\u548c <code>&lt;header&gt;<\/code> \u9876\u680f\u63d0\u53d6\u51fa\u6765\u3002\u5e76\u4e14\uff0c\u6211\u4eec\u7528 Vue \u7684\u53d8\u91cf\u6765\u63a7\u5236\u201c\u7535\u8111\u7aef\u6298\u53e0\u201d\u548c\u201c\u624b\u673a\u7aef\u5f39\u51fa\u201d\u3002<br \/>\n\u6253\u5f00\u5e03\u5c40\u6587\u4ef6\uff1a<\/p>\n<pre><code class=\"language-bash\">vim src\/components\/Layout.vue\r\n<\/code><\/pre>\n<p><strong>\u5b8c\u5168\u66ff\u6362<\/strong>\u4e3a\u4ee5\u4e0b\u5185\u5bb9\uff08\u4ed4\u7ec6\u770b\u6211\u52a0\u7684 <code>@click<\/code> \u548c <code>:class<\/code>\uff0c\u8fd9\u662f\u9002\u914d\u624b\u673a\u7aef\u7684\u6838\u5fc3\uff09\uff1a<\/p>\n<pre><code class=\"language-\u4ee3\u7801\u6bb5\">&lt;template&gt;\r\n  &lt;div class=\"backdrop\" :class=\"{ show: isMobileMenuOpen }\" @click=\"isMobileMenuOpen = false\"&gt;&lt;\/div&gt;\r\n\r\n  &lt;div class=\"app\"&gt;\r\n    &lt;aside class=\"sider\" :class=\"{ collapsed: isCollapsed, open: isMobileMenuOpen }\"&gt;\r\n      &lt;div class=\"brand\"&gt;\r\n        &lt;div class=\"logo\"&gt;N&lt;\/div&gt;\r\n        &lt;div class=\"brand-text\"&gt;\r\n          &lt;div class=\"brand-name\"&gt;Navidrome&lt;\/div&gt;\r\n          &lt;div class=\"brand-sub\"&gt;\u79ef\u5206\u4e0e\u7528\u6237\u7ba1\u7406&lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n\r\n      &lt;nav class=\"nav\"&gt;\r\n        &lt;router-link to=\"\/\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n          &lt;div class=\"nav-icon\"&gt;\ud83c\udfe0&lt;\/div&gt;\r\n          &lt;div class=\"nav-text\"&gt;\r\n            &lt;div class=\"nav-title\"&gt;\u4eea\u8868\u76d8&lt;\/div&gt;\r\n            &lt;div class=\"nav-desc\"&gt;\u6838\u5fc3\u6307\u6807\u4e0e\u7b7e\u5230&lt;\/div&gt;\r\n          &lt;\/div&gt;\r\n        &lt;\/router-link&gt;\r\n        \r\n        &lt;router-link to=\"\/invites\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n          &lt;div class=\"nav-icon\"&gt;\ud83c\udf9f\ufe0f&lt;\/div&gt;\r\n          &lt;div class=\"nav-text\"&gt;\r\n            &lt;div class=\"nav-title\"&gt;\u9080\u8bf7\u7801&lt;\/div&gt;\r\n            &lt;div class=\"nav-desc\"&gt;\u751f\u6210\u4e0e\u5206\u914d\u9080\u8bf7\u7801&lt;\/div&gt;\r\n          &lt;\/div&gt;\r\n        &lt;\/router-link&gt;\r\n      &lt;\/nav&gt;\r\n\r\n      &lt;div class=\"sider-footer\"&gt;\r\n        &lt;button class=\"btn\" @click=\"toggleCollapse\"&gt;\r\n          {{ isCollapsed ? '\u27a1\ufe0f' : '\u2b05\ufe0f' }} &lt;span class=\"hide-on-collapse\" v-show=\"!isCollapsed\"&gt;\u6536\u8d77\u4fa7\u680f&lt;\/span&gt;\r\n        &lt;\/button&gt;\r\n        &lt;button class=\"btn btn-danger\" @click=\"handleLogout\"&gt;\r\n          \u23fb &lt;span class=\"hide-on-collapse\" v-show=\"!isCollapsed\"&gt;\u9000\u51fa\u767b\u5f55&lt;\/span&gt;\r\n        &lt;\/button&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/aside&gt;\r\n\r\n    &lt;main class=\"main\"&gt;\r\n      &lt;header class=\"header\"&gt;\r\n        &lt;div style=\"display: flex; align-items: center; gap: 10px;\"&gt;\r\n          &lt;button class=\"btn mobile-only\" @click=\"isMobileMenuOpen = true\"&gt;\u2630&lt;\/button&gt;\r\n          &lt;div&gt;\r\n            &lt;div style=\"font-weight:900;\"&gt;Navidrome Server&lt;\/div&gt;\r\n          &lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n        &lt;div style=\"display: flex; gap: 10px; align-items: center;\"&gt;\r\n          &lt;button class=\"btn\" @click=\"toggleTheme\"&gt;{{ isDark ? '\u2600\ufe0f' : '\ud83c\udf19' }}&lt;\/button&gt;\r\n          &lt;span class=\"badge info\"&gt;\u5728\u7ebf&lt;\/span&gt;\r\n          &lt;button class=\"btn\"&gt;\ud83e\uddd1\u200d\ud83d\udcbb \u4e2a\u4eba\u4e2d\u5fc3&lt;\/button&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/header&gt;\r\n\r\n      &lt;section class=\"content\"&gt;\r\n        &lt;router-view&gt;&lt;\/router-view&gt;\r\n      &lt;\/section&gt;\r\n    &lt;\/main&gt;\r\n  &lt;\/div&gt;\r\n&lt;\/template&gt;\r\n\r\n&lt;script setup&gt;\r\nimport { ref, onMounted } from 'vue'\r\n\r\n\/\/ \u54cd\u5e94\u5f0f\u72b6\u6001\r\nconst isCollapsed = ref(false)\r\nconst isMobileMenuOpen = ref(false)\r\nconst isDark = ref(false)\r\n\r\n\/\/ \u65b9\u6cd5\uff1a\u7535\u8111\u7aef\u6298\u53e0\u4fa7\u8fb9\u680f\r\nconst toggleCollapse = () =&gt; {\r\n  isCollapsed.value = !isCollapsed.value\r\n}\r\n\r\n\/\/ \u65b9\u6cd5\uff1a\u624b\u673a\u7aef\u70b9\u51fb\u83dc\u5355\u540e\u81ea\u52a8\u5173\u95ed\u4fa7\u8fb9\u680f\r\nconst closeMobileMenu = () =&gt; {\r\n  isMobileMenuOpen.value = false\r\n}\r\n\r\n\/\/ \u65b9\u6cd5\uff1a\u5207\u6362\u9ed1\u591c\/\u767d\u5929\u4e3b\u9898\r\nconst toggleTheme = () =&gt; {\r\n  isDark.value = !isDark.value\r\n  const newTheme = isDark.value ? 'dark' : 'light'\r\n  document.documentElement.setAttribute('data-theme', newTheme)\r\n  localStorage.setItem('theme', newTheme)\r\n}\r\n\r\nconst handleLogout = () =&gt; {\r\n  if (confirm(\"\u786e\u8ba4\u9000\u51fa\u7cfb\u7edf\uff1f\")) {\r\n    alert(\"\u9000\u51fa\u6210\u529f\")\r\n  }\r\n}\r\n\r\n\/\/ \u521d\u59cb\u5316\u4e3b\u9898\r\nonMounted(() =&gt; {\r\n  if (localStorage.getItem('theme') === 'dark') {\r\n    isDark.value = true\r\n    document.documentElement.setAttribute('data-theme', 'dark')\r\n  }\r\n})\r\n&lt;\/script&gt;\r\n<\/code><\/pre>\n<hr \/>\n<h3>\u7b2c\u4e09\u6b65\uff1a\u8fd8\u539f\u4eea\u8868\u76d8 (Dashboard.vue)<\/h3>\n<p>\u628a\u4f60\u539f\u7248 HTML \u91cc\u7684\u6570\u636e\u5361\u7247\u548c\u516c\u544a\u8fd8\u539f\u51fa\u6765\u3002<\/p>\n<pre><code class=\"language-bash\">vim src\/views\/Dashboard.vue\r\n<\/code><\/pre>\n<p><strong>\u66ff\u6362<\/strong>\u4e3a\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/p>\n<pre><code class=\"language-\u4ee3\u7801\u6bb5\">&lt;template&gt;\r\n  &lt;div&gt;\r\n    &lt;div class=\"page-header\"&gt;\r\n      &lt;div&gt;\r\n        &lt;div class=\"page-title\"&gt;\u4eea\u8868\u76d8&lt;\/div&gt;\r\n        &lt;div class=\"page-subtitle\"&gt;\u6982\u89c8\u60a8\u7684\u8d26\u6237\u4fe1\u606f\u4e0e\u7cfb\u7edf\u72b6\u6001&lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n      &lt;div id=\"headerActions\" style=\"display:flex; gap:10px;\"&gt;\r\n        &lt;button class=\"btn btn-success\" @click=\"doCheckin\" :disabled=\"hasCheckedIn\" :style=\"{ opacity: hasCheckedIn ? 0.6 : 1 }\"&gt;\r\n          {{ hasCheckedIn ? '\u2705 \u4eca\u65e5\u5df2\u7b7e\u5230' : '\ud83d\udcc5 \u6bcf\u65e5\u7b7e\u5230' }}\r\n        &lt;\/button&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n\r\n    &lt;div class=\"stat-grid\"&gt;\r\n      &lt;div class=\"stat-card\"&gt;\r\n        &lt;div class=\"stat-icon\" style=\"color:var(--warning-color)\"&gt;\ud83e\ude99&lt;\/div&gt;\r\n        &lt;div&gt;\r\n          &lt;div class=\"stat-label\"&gt;\u6211\u7684\u79ef\u5206&lt;\/div&gt;\r\n          &lt;div class=\"stat-value\"&gt;{{ myPoints }}&lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n      &lt;div class=\"stat-card\"&gt;\r\n        &lt;div class=\"stat-icon\"&gt;\ud83d\udcc5&lt;\/div&gt;\r\n        &lt;div&gt;\r\n          &lt;div class=\"stat-label\"&gt;\u8fde\u7eed\u7b7e\u5230\u5929\u6570&lt;\/div&gt;\r\n          &lt;div class=\"stat-value\"&gt;{{ myCheckins }}&lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n      &lt;div class=\"stat-card\"&gt;\r\n        &lt;div class=\"stat-icon\"&gt;\ud83d\udc65&lt;\/div&gt;\r\n        &lt;div&gt;\r\n          &lt;div class=\"stat-label\"&gt;\u5168\u7ad9\u603b\u7528\u6237&lt;\/div&gt;\r\n          &lt;div class=\"stat-value\"&gt;--&lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n      &lt;div class=\"stat-card\"&gt;\r\n        &lt;div class=\"stat-icon\"&gt;\ud83c\udfb5&lt;\/div&gt;\r\n        &lt;div&gt;\r\n          &lt;div class=\"stat-label\"&gt;\u7cfb\u7edf\u72b6\u6001&lt;\/div&gt;\r\n          &lt;div class=\"stat-value\" style=\"color: var(--success-color); font-size: 18px;\"&gt;\u8fd0\u884c\u826f\u597d&lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n\r\n    &lt;div class=\"modern-card\"&gt;\r\n      &lt;div class=\"card-head\"&gt;\r\n        &lt;div&gt;\r\n          &lt;div class=\"card-title\"&gt;\u7cfb\u7edf\u516c\u544a&lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n      &lt;div class=\"card-body\" style=\"line-height: 1.8; color: var(--text-color-secondary);\"&gt;\r\n        \u6b22\u8fce\u6765\u5230 Navidrome \u97f3\u4e50\u793e\u533a\uff01\u60a8\u53ef\u4ee5\u901a\u8fc7\u6bcf\u65e5\u7b7e\u5230\u83b7\u53d6\u79ef\u5206\uff0c\u5e76\u5728\u201c\u79ef\u5206\u5546\u5e97\u201d\u5151\u6362\u66f4\u957f\u7684 VIP \u65f6\u957f\u6216\u9080\u8bf7\u7801\u989d\u5ea6\u3002&lt;br\/&gt;\r\n        \u57fa\u4e8e Vue 3 \u7684\u524d\u7aef\u5df2\u7ecf\u91cd\u6784\u5b8c\u6bd5\uff0c\u63a5\u4e0b\u6765\u6211\u4eec\u5c06\u628a\u8fd9\u91cc\u7684\u9759\u6001\u6570\u636e\u66ff\u6362\u4e3a\u540e\u7aef\u7684\u771f\u5b9e API \u8bf7\u6c42\uff01\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n  &lt;\/div&gt;\r\n&lt;\/template&gt;\r\n\r\n&lt;script setup&gt;\r\nimport { ref } from 'vue'\r\n\r\n\/\/ \u6a21\u62df\u6570\u636e (\u7a0d\u540e\u6211\u4eec\u4f1a\u7528 axios \u66ff\u6362)\r\nconst myPoints = ref(120)\r\nconst myCheckins = ref(5)\r\nconst hasCheckedIn = ref(false)\r\n\r\nconst doCheckin = () =&gt; {\r\n  hasCheckedIn.value = true\r\n  myPoints.value += 10\r\n  myCheckins.value += 1\r\n  alert(\"\u7b7e\u5230\u6210\u529f\uff01\u83b7\u5f97 10 \u79ef\u5206\")\r\n}\r\n&lt;\/script&gt;\r\n<\/code><\/pre>\n<hr \/>\n<h3>\u7b2c\u56db\u6b65\uff1a\u65b0\u5efa\u9080\u8bf7\u7801\u9875\u9762 (Invites.vue)<\/h3>\n<p>\u4e3a\u4e86\u8ba9\u4f60\u611f\u53d7\u8def\u7531\u5207\u6362\u7684\u9b45\u529b\uff0c\u6211\u4eec\u628a\u4f60\u539f\u6765\u7684\u9080\u8bf7\u7801\u8868\u683c\u62bd\u79bb\u6210\u4e00\u4e2a\u5355\u72ec\u7684\u9875\u9762\u3002<\/p>\n<pre><code class=\"language-bash\">vim src\/views\/Invites.vue\r\n<\/code><\/pre>\n<p>\u5199\u5165\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/p>\n<pre><code class=\"language-\u4ee3\u7801\u6bb5\">&lt;template&gt;\r\n  &lt;div&gt;\r\n    &lt;div class=\"page-header\"&gt;\r\n      &lt;div&gt;\r\n        &lt;div class=\"page-title\"&gt;\u9080\u8bf7\u7801\u7ba1\u7406&lt;\/div&gt;\r\n        &lt;div class=\"page-subtitle\"&gt;\u751f\u6210\u6ce8\u518c\u9080\u8bf7\u94fe\u63a5\u7ed9\u60a8\u7684\u670b\u53cb&lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n\r\n    &lt;div class=\"modern-card\"&gt;\r\n      &lt;div class=\"card-head\"&gt;\r\n        &lt;div&gt;\r\n          &lt;div class=\"card-title\"&gt;\u6211\u7684\u9080\u8bf7\u7801&lt;\/div&gt;\r\n          &lt;div class=\"card-sub\"&gt;\u9080\u8bf7\u94fe\u63a5\u6709\u6548\u671f\u9ed8\u8ba4 48 \u5c0f\u65f6&lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n        &lt;button class=\"btn btn-gradient\" @click=\"generateInvite\"&gt;\u2795 \u751f\u6210\u9080\u8bf7\u7801 (\u6263\u9664 50 \u79ef\u5206)&lt;\/button&gt;\r\n      &lt;\/div&gt;\r\n      &lt;div class=\"card-body\"&gt;\r\n        &lt;div class=\"table-wrap\"&gt;\r\n          &lt;table&gt;\r\n            &lt;thead&gt;\r\n              &lt;tr&gt;\r\n                &lt;th&gt;\u9080\u8bf7\u7801 (Code)&lt;\/th&gt;\r\n                &lt;th&gt;\u72b6\u6001&lt;\/th&gt;\r\n                &lt;th&gt;\u8fc7\u671f\u65f6\u95f4&lt;\/th&gt;\r\n                &lt;th&gt;\u64cd\u4f5c&lt;\/th&gt;\r\n              &lt;\/tr&gt;\r\n            &lt;\/thead&gt;\r\n            &lt;tbody&gt;\r\n              &lt;tr v-for=\"inv in invites\" :key=\"inv.code\"&gt;\r\n                &lt;td class=\"td-mono\"&gt;{{ inv.code }}&lt;\/td&gt;\r\n                &lt;td&gt;\r\n                  &lt;span class=\"badge\" :class=\"inv.status === 'valid' ? 'success' : 'error'\"&gt;\r\n                    {{ inv.status === 'valid' ? '\u6709\u6548' : '\u5df2\u5931\u6548' }}\r\n                  &lt;\/span&gt;\r\n                &lt;\/td&gt;\r\n                &lt;td class=\"td-mono\"&gt;{{ inv.expireTime }}&lt;\/td&gt;\r\n                &lt;td&gt;\r\n                  &lt;button class=\"btn btn-xs\" @click=\"copyLink(inv.code)\"&gt;\ud83d\udd17 \u590d\u5236\u94fe\u63a5&lt;\/button&gt;\r\n                &lt;\/td&gt;\r\n              &lt;\/tr&gt;\r\n              &lt;tr v-if=\"invites.length === 0\"&gt;\r\n                &lt;td colspan=\"4\" style=\"text-align: center; color: var(--text-color-tertiary);\"&gt;\u6682\u65e0\u9080\u8bf7\u7801\u6570\u636e&lt;\/td&gt;\r\n              &lt;\/tr&gt;\r\n            &lt;\/tbody&gt;\r\n          &lt;\/table&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n  &lt;\/div&gt;\r\n&lt;\/template&gt;\r\n\r\n&lt;script setup&gt;\r\nimport { ref } from 'vue'\r\n\r\n\/\/ \u6a21\u62df\u6570\u636e\r\nconst invites = ref([\r\n  { code: 'NV-2023-A1B2', status: 'valid', expireTime: '2026-04-16 12:00:00' }\r\n])\r\n\r\nconst generateInvite = () =&gt; {\r\n  alert(\"\u8fd9\u91cc\u7a0d\u540e\u5c06\u8c03\u7528\u540e\u7aef\u7684 \/v1\/invite\/generate \u63a5\u53e3\uff01\")\r\n}\r\n\r\nconst copyLink = (code) =&gt; {\r\n  alert(`\u9080\u8bf7\u94fe\u63a5: https:\/\/info.crabpt.vip\/#\/register?code=${code}`)\r\n}\r\n&lt;\/script&gt;\r\n<\/code><\/pre>\n<hr \/>\n<h3>\u7b2c\u4e94\u6b65\uff1a\u5728\u8def\u7531\u4e2d\u6ce8\u518c\u65b0\u9875\u9762<\/h3>\n<p>\u6700\u540e\uff0c\u544a\u8bc9 Vue \u6211\u4eec\u591a\u4e86\u4e00\u4e2a\u201c\u9080\u8bf7\u7801\u201d\u9875\u9762\u3002<\/p>\n<pre><code class=\"language-bash\">vim src\/router\/index.js\r\n<\/code><\/pre>\n<p>\u66ff\u6362\u4e3a\uff1a<\/p>\n<pre><code class=\"language-javascript\">import { createRouter, createWebHistory } from 'vue-router'\r\nimport Layout from '..\/components\/Layout.vue'\r\nimport Dashboard from '..\/views\/Dashboard.vue'\r\nimport Invites from '..\/views\/Invites.vue' \/\/ \u5f15\u5165\u521a\u5199\u7684\u7ec4\u4ef6\r\n\r\nconst routes = [\r\n  {\r\n    path: '\/',\r\n    component: Layout,\r\n    children: [\r\n      { path: '', name: 'Dashboard', component: Dashboard },\r\n      { path: 'invites', name: 'Invites', component: Invites } \/\/ \u914d\u7f6e\u8def\u7531\r\n    ]\r\n  }\r\n]\r\n\r\nconst router = createRouter({\r\n  history: createWebHistory(),\r\n  routes\r\n})\r\n\r\nexport default router\r\n<\/code><\/pre>\n<hr \/>\n<h3>\u9a8c\u6536\u6548\u679c\uff01<\/h3>\n<p>\u56de\u5230\u4f60\u7684\u7535\u8111\u6d4f\u89c8\u5668\uff0c\u5237\u65b0\u9875\u9762\uff08\u5982\u679c\u662f <code>npm run dev -- --host<\/code> \u5e94\u8be5\u4f1a\u81ea\u52a8\u70ed\u66f4\u65b0\uff09\uff1a<\/p>\n<ol>\n<li><strong>\u6d4b\u8bd5\u7535\u8111\u7aef<\/strong>\uff1a\u70b9\u51fb\u5de6\u4fa7\u7684\u201c\u4eea\u8868\u76d8\u201d\u548c\u201c\u9080\u8bf7\u7801\u201d\uff0c\u4f60\u4f1a\u53d1\u73b0\u9875\u9762\u5728\u53f3\u4fa7<strong>\u65e0\u5237\u65b0\u77ac\u95f4\u5207\u6362<\/strong>\uff0c\u8fd9\u5c31\u662f\u5355\u9875\u5e94\u7528 (SPA) \u7684\u9b45\u529b\uff01<\/li>\n<li><strong>\u6d4b\u8bd5\u624b\u673a\u7aef<\/strong>\uff1a\u6309 <code>F12<\/code> \u6253\u5f00\u5f00\u53d1\u8005\u5de5\u5177\uff0c\u70b9\u51fb\u5de6\u4e0a\u89d2\u7684\u201c\u624b\u673a\/\u5e73\u677f\u8bbe\u5907\u5207\u6362\u201d\u56fe\u6807\uff08\u6216\u8005\u76f4\u63a5\u628a\u6d4f\u89c8\u5668\u7a97\u53e3\u62c9\u7a84\uff09\u3002<\/li>\n<\/ol>\n<ul>\n<li>\u4f60\u4f1a\u770b\u5230\u5de6\u4fa7\u83dc\u5355\u81ea\u52a8\u9690\u85cf\uff0c\u9876\u90e8\u51fa\u73b0\u4e86 <code>\u2630<\/code> \u6309\u94ae\u3002<\/li>\n<li>\u70b9\u51fb <code>\u2630<\/code>\uff0c\u5de6\u4fa7\u83dc\u5355\u4f1a\u5e26\u906e\u7f69\u5c42\u5e73\u6ed1\u6ed1\u51fa\uff01\u70b9\u51fb\u83dc\u5355\u9879\uff0c\u83dc\u5355\u4f1a\u81ea\u52a8\u6536\u8d77\u3002<br \/>\n\u8fd9\u5c31\u662f\u57fa\u4e8e Vue 3 \u7684\u7ec4\u4ef6\u5316\u5f00\u53d1\uff01\u4e0d\u4ec5\u628a\u5197\u957f\u7684 HTML \u62c6\u89e3\u5f97\u6e05\u6e05\u695a\u695a\uff0c\u800c\u4e14\u5b8c\u7f8e\u517c\u5bb9\u4e86\u591a\u7aef\u8bbe\u5907\u7684\u683c\u5f0f\u3002<br \/>\n\u5982\u679c\u4f60\u89c9\u5f97\u9875\u9762\u6548\u679c\u6ca1\u95ee\u9898\uff0c\u6211\u4eec\u4e0b\u4e00\u8282\u5c31\u53ef\u4ee5\u76f4\u63a5\u5f15\u5165 Axios\uff0c<strong>\u628a\u6211\u4eec\u5728\u4eea\u8868\u76d8\u548c\u9080\u8bf7\u7801\u9875\u9762\u5199\u7684 alert \u6362\u6210\u771f\u5b9e\u7684\u540e\u7aef\u63a5\u53e3\u8bf7\u6c42<\/strong>\uff01\u968f\u65f6\u544a\u8bc9\u6211\u8fdb\u5ea6\uff01<\/li>\n<\/ul>\n<h2>Turn 31<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>\u5f88\u597d\uff0c\u540e\u9762\u8fd9\u4e9b\u6211\u4eec\u5728\u8fdb\u884c\u6162\u6162\u8c03\u6574\uff0c\u73b0\u5728\uff0c\u8ba9\u6211\u4eec\u6765\u7ee7\u7eed\u3002<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u597d\u7684\uff01\u6211\u4eec\u521a\u521a\u7528 Vite + Vue 3 \u91cd\u6784\u4e86\u524d\u7aef\u9875\u9762\uff0c\u5e76\u4e14\u6210\u529f\u8fd0\u884c\u4e86\u8d77\u6765\u3002\u73b0\u5728\u7684\u9875\u9762\u867d\u7136\u6f02\u4eae\uff0c\u4f46\u4e0a\u9762\u7684\u79ef\u5206\u3001\u9080\u8bf7\u7801\u7b49\u6570\u636e\u90fd\u662f\u6211\u4eec\u5199\u6b7b\u5728\u524d\u7aef\u4ee3\u7801\u91cc\u7684\u201c\u5047\u6570\u636e\u201d\u3002<br \/>\n\u6211\u4eec\u7684\u4e0b\u4e00\u4e2a\u6838\u5fc3\u76ee\u6807\u662f\uff1a<strong>\u8ba9\u524d\u7aef\u9875\u9762\uff08Vue\uff09\u548c\u540e\u7aef\u670d\u52a1\uff08Spring Boot\uff09\u201c\u5408\u4f53\u201d<\/strong>\u3002<br \/>\n\u8fd9\u6d89\u53ca\u5230\u524d\u540e\u7aef\u5206\u79bb\u67b6\u6784\u4e2d\u6700\u91cd\u8981\u7684\u4e00\u6b65\uff1a<strong>\u89e3\u51b3\u8de8\u57df\u95ee\u9898\uff08CORS\uff09\u5e76\u6253\u901a API \u8bf7\u6c42<\/strong>\u3002<br \/>\n\u4e3a\u4e86\u8ba9\u4f60\u8fd9\u5957\u7cfb\u7edf\u4ee5\u540e\u90e8\u7f72\u8d77\u6765\u6700\u7a33\u3001\u6700\u65b9\u4fbf\uff0c\u6211\u4eec\u4e0d\u91c7\u7528\u5728\u524d\u7aef\u4ee3\u7801\u91cc\u5199\u6b7b\u540e\u7aef IP \u5730\u5740\u7684\u201c\u571f\u529e\u6cd5\u201d\uff0c\u800c\u662f\u91c7\u7528\u66f4\u4e13\u4e1a\u7684**\u201c\u4ee3\u7406\u8f6c\u53d1\uff08Proxy\uff09\u201d**\u6a21\u5f0f\u3002<br \/>\n\u8bf7\u8ddf\u7740\u6211\u8fdb\u884c\u4ee5\u4e0b\u4e24\u6b65\u5b9e\u64cd\uff1a<\/p>\n<hr \/>\n<h3>\u7b2c\u4e00\u6b65\uff1a\u914d\u7f6e Vite \u7684\u4ee3\u7406 (\u89e3\u51b3\u8de8\u57df\u548c\u5730\u5740\u95ee\u9898)<\/h3>\n<p>\u6211\u4eec\u8981\u544a\u8bc9\u524d\u7aef\u9879\u76ee\uff1a\u5f53\u4f60\u9047\u5230\u4ee5 <code>\/v1<\/code> \u5f00\u5934\u7684\u7f51\u7edc\u8bf7\u6c42\u65f6\uff0c\u4e0d\u8981\u5728\u524d\u7aef\u81ea\u5df1\u627e\uff0c\u800c\u662f\u6084\u6084\u5730\u628a\u8bf7\u6c42\u8f6c\u53d1\u7ed9\u540e\u7aef\u7684 <code>http:\/\/127.0.0.1:8001<\/code>\u3002<br \/>\n\u5728\u4f60\u7684\u524d\u7aef\u76ee\u5f55 <code>\/mnt\/navidrome-system\/frontend\/navidrome-ui<\/code> \u4e0b\uff1a<\/p>\n<pre><code class=\"language-bash\">vim vite.config.js\r\n<\/code><\/pre>\n<p>\u628a\u4f60\u770b\u5230\u7684\u9ed8\u8ba4\u914d\u7f6e<strong>\u5b8c\u5168\u66ff\u6362<\/strong>\u6210\u4e0b\u9762\u8fd9\u6837\uff1a<\/p>\n<pre><code class=\"language-javascript\">import { defineConfig } from 'vite'\r\nimport vue from '@vitejs\/plugin-vue'\r\n\r\nexport default defineConfig({\r\n  plugins: [vue()],\r\n  server: {\r\n    host: '0.0.0.0', \/\/ \u5141\u8bb8\u5916\u90e8\u8bbf\u95ee\r\n    port: 5173,      \/\/ \u524d\u7aef\u7aef\u53e3\r\n    proxy: {\r\n      \/\/ \u4ee3\u7406\u8f6c\u53d1\u914d\u7f6e\uff1a\u53ea\u8981\u524d\u7aef\u8bf7\u6c42 \/v1 \u6253\u5934\uff0c\u5c31\u8f6c\u53d1\u7ed9\u540e\u7aef 8001\r\n      '\/v1': {\r\n        target: 'http:\/\/127.0.0.1:8001',\r\n        changeOrigin: true\r\n      }\r\n    }\r\n  }\r\n})\r\n<\/code><\/pre>\n<p>\u4fdd\u5b58\u5e76\u9000\u51fa\uff08\u6309 <code>Esc<\/code>\uff0c\u8f93\u5165 <code>:wq<\/code>\uff0c\u56de\u8f66\uff09\u3002<\/p>\n<hr \/>\n<h3>\u7b2c\u4e8c\u6b65\uff1a\u5c01\u88c5\u5168\u5c40\u7684\u7f51\u7edc\u8bf7\u6c42\u5de5\u5177 (Axios)<\/h3>\n<p>\u4e3a\u4e86\u65b9\u4fbf\u4ee5\u540e\u52a0 Token \u6821\u9a8c\u548c\u7edf\u4e00\u5904\u7406\u62a5\u9519\uff0c\u6211\u4eec\u5728\u524d\u7aef\u5efa\u4e00\u4e2a\u7f51\u7edc\u8bf7\u6c42\u7684\u201c\u7ba1\u5bb6\u201d\u3002<\/p>\n<pre><code class=\"language-bash\">mkdir -p src\/utils\r\nvim src\/utils\/request.js\r\n<\/code><\/pre>\n<hr \/>\n<h3>\u7b2c\u4e09\u6b65\uff1a\u5728\u4eea\u8868\u76d8\u9875\u9762\u8c03\u7528\u771f\u5b9e\u540e\u7aef\u63a5\u53e3<\/h3>\n<p>\u73b0\u5728\u5de5\u5177\u90fd\u51c6\u5907\u597d\u4e86\uff0c\u6211\u4eec\u53bb\u628a <code>Dashboard.vue<\/code> \u91cc\u7684\u5047\u6570\u636e\u6362\u6210\u771f\u7684\uff01<\/p>\n<pre><code class=\"language-bash\">vim src\/views\/Dashboard.vue\r\n<\/code><\/pre>\n<p>\u627e\u5230\u5e95\u90e8\u7684 <code>&lt;script setup&gt;<\/code> \u6807\u7b7e\uff0c<strong>\u5b8c\u5168\u66ff\u6362<\/strong>\u4e3a\u4ee5\u4e0b\u5185\u5bb9\uff08\u8bf7\u6ce8\u610f\u91cc\u9762\u5f15\u5165\u4e86 <code>request<\/code>\uff0c\u5e76\u4e14\u5728\u9875\u9762\u52a0\u8f7d\u65f6\u8c03\u7528\u4e86\u6211\u4eec\u7684 <code>\/v1\/login<\/code> \u63a5\u53e3\u83b7\u53d6\u7528\u6237\u4fe1\u606f\uff09\uff1a<\/p>\n<pre><code class=\"language-\u4ee3\u7801\u6bb5\">&lt;script setup&gt;\r\nimport { ref, onMounted } from 'vue'\r\nimport request from '..\/utils\/request' \/\/ \u5f15\u5165\u6211\u4eec\u521a\u624d\u5c01\u88c5\u7684 Axios \u5de5\u5177\r\n\r\n\/\/ \u54cd\u5e94\u5f0f\u6570\u636e\r\nconst myPoints = ref('--')\r\nconst myCheckins = ref('--')\r\nconst hasCheckedIn = ref(false)\r\n\r\n\/\/ \u5f53\u524d\u767b\u5f55\u7528\u6237\u7684 ID (\u5148\u5199\u6b7b\u4e3a 1\uff0c\u65b9\u4fbf\u6d4b\u8bd5)\r\nconst currentUserId = 1\r\n\r\n\/\/ \u9875\u9762\u52a0\u8f7d\u65f6\u6267\u884c\r\nonMounted(async () =&gt; {\r\n  try {\r\n    \/\/ 1. \u8c03\u7528\u767b\u5f55\u63a5\u53e3\u83b7\u53d6\u7528\u6237\u4fe1\u606f\uff08\u6a21\u62df\u767b\u5f55\u62ff\u5230\u4fe1\u606f\uff09\r\n    const user = await request.post('\/v1\/login', {\r\n      email: 'admin@crabpt.vip',\r\n      password: '123456'\r\n    })\r\n    \r\n    \/\/ \u628a\u540e\u7aef\u8fd4\u56de\u7684\u771f\u5b9e\u79ef\u5206\u8d4b\u503c\u7ed9\u524d\u7aef\u53d8\u91cf\r\n    myPoints.value = user.points\r\n    myCheckins.value = 5 \/\/ \u8fde\u7eed\u7b7e\u5230\u76ee\u524d\u540e\u7aef\u8fd8\u6ca1\u5b8c\u5168\u5199\u6b7b\uff0c\u5148\u5199\u4e2a\u56fa\u5b9a\u503c\r\n    \r\n  } catch (error) {\r\n    console.error(\"\u83b7\u53d6\u7528\u6237\u4fe1\u606f\u5931\u8d25\", error)\r\n  }\r\n})\r\n\r\n\/\/ \u7b7e\u5230\u6309\u94ae\u70b9\u51fb\u4e8b\u4ef6\r\nconst doCheckin = async () =&gt; {\r\n  if (hasCheckedIn.value) return\r\n\r\n  try {\r\n    \/\/ \u8c03\u7528\u540e\u7aef\u7684\u771f\u5b9e\u7b7e\u5230\u63a5\u53e3\r\n    await request.post('\/v1\/user\/checkin', {\r\n      userId: currentUserId\r\n    })\r\n    \r\n    \/\/ \u5982\u679c\u6ca1\u62a5\u9519\uff0c\u8bf4\u660e\u7b7e\u5230\u6210\u529f\uff0c\u524d\u7aef\u72b6\u6001\u66f4\u65b0\r\n    hasCheckedIn.value = true\r\n    myPoints.value += 10\r\n    myCheckins.value += 1\r\n    alert(\"\u7b7e\u5230\u6210\u529f\uff01\u83b7\u5f97 10 \u79ef\u5206\")\r\n  } catch (error) {\r\n    \/\/ \u5982\u679c\u540e\u7aef\u629b\u51fa\u9519\u8bef\uff08\u6bd4\u5982\uff1a\u60a8\u4eca\u5929\u5df2\u7ecf\u7b7e\u5230\u8fc7\u4e86\uff09\uff0c\u8fd9\u91cc\u4f1a\u6355\u83b7\u5e76\u5df2\u7ecf\u5728 request.js \u91cc\u5f39\u7a97\u4e86\r\n    \/\/ \u5982\u679c\u662f\u56e0\u4e3a\u5df2\u7ecf\u7b7e\u5230\u62a5\u9519\uff0c\u6211\u4eec\u987a\u4fbf\u628a\u524d\u7aef\u6309\u94ae\u7f6e\u7070\r\n    if(error.message.includes(\"\u5df2\u7ecf\u7b7e\u5230\")) {\r\n       hasCheckedIn.value = true\r\n    }\r\n  }\r\n}\r\n&lt;\/script&gt;\r\n<\/code><\/pre>\n<p>\u4fdd\u5b58\u5e76\u9000\u51fa\u3002<\/p>\n<hr \/>\n<h3>\u9a8c\u6536\u5408\u4f53\u6548\u679c\uff01<\/h3>\n<pre><code class=\"language-bash\">npm run dev -- --host\r\n<\/code><\/pre>\n<p><strong>\u6fc0\u52a8\u7684\u65f6\u523b\u5230\u4e86\uff1a<\/strong><\/p>\n<ul>\n<li>\u770b\u770b\u201c\u6211\u7684\u79ef\u5206\u201d\u90a3\u4e2a\u683c\u5b50\uff0c\u662f\u4e0d\u662f\u4e0d\u518d\u663e\u793a\u5047\u6570\u636e 120\uff0c\u800c\u662f\u663e\u793a\u4e86\u4f60\u6570\u636e\u5e93\u91cc\u771f\u5b9e\u7684\u90a3\u4e2a\u79ef\u5206\u6570\u5b57\uff08\u6bd4\u5982 10009\uff09\uff1f<\/li>\n<li>\u5c1d\u8bd5\u70b9\u4e00\u4e0b\u201c\u6bcf\u65e5\u7b7e\u5230\u201d\u6309\u94ae\uff0c\u770b\u770b\u79ef\u5206\u4f1a\u4e0d\u4f1a\u589e\u52a0\uff0c\u5e76\u4e14\u6309\u94ae\u53d8\u6210\u7eff\u8272\u5df2\u7b7e\u5230\u72b6\u6001\uff1f<br \/>\n\u5982\u679c\u4e00\u5207\u5982\u9884\u671f\u90a3\u6837\u5de5\u4f5c\uff0c\u90a3\u5c31\u8bf4\u660e<strong>\u4f60\u7684\u524d\u7aef\u548c\u540e\u7aef\u6b63\u5f0f\u63a5\u8f68\u4e86\uff01<\/strong> \u6574\u4e2a\u4efb\u7763\u4e8c\u8109\u5f7b\u5e95\u6253\u901a\uff01\u6d4b\u8bd5\u5b8c\u540e\u544a\u8bc9\u6211\u6548\u679c\u5982\u4f55\uff01<\/li>\n<\/ul>\n<h2>Turn 32<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>\u4e0d\uff01\u6211\u7f51\u9875\u4e0a\u53ea\u6709\u8fd9\u70b9\u4fe1\u606f\u3002\u662f\u90a3\u91cc\u7f3a\u5c11\u4e86\u3002\u524d\u7aef\u9700\u8981\u7ba1\u7406\u9875\u9762\uff0c\u4e2a\u4eba\u9875\u9762\uff0c\u7b49\u7b49\u7ba1\u7406\u7684\u529f\u80fd\uff0c\u548c\u4e2a\u4eba\u7684\u529f\u80fd\u9700\u8981\u7ba1\u7406\u8d26\u6237\u624d\u80fd\u767b\u5f55\uff0c\u7b49\u7b49\u8fd9\u4e00\u4e9b\u9700\u8981\u5b8c\u5584\u4e0a\uff01\u3002<br \/>\n\u4eea\u8868\u76d8<br \/>\n\u8d2d\u4e70\u5957\u9910<br \/>\n\u6211\u7684\u8ba2\u9605<br \/>\n\u6211\u7684\u8ba2\u5355<br \/>\n\u9080\u8bf7\u8ba1\u5212<br \/>\n\u6211\u7684\u5de5\u5355<br \/>\n\u79ef\u5206<br \/>\n\u8d26\u53f7\u8bbe\u7f6e<br \/>\n\u7b49\u7b49\u7b49\uff0c\u4fe1\u606f\u8bf7\u5e2e\u6211\u66f4\u52a0\u5b8c\u5584\u3002<br \/>\n\u8fd8\u9700\u8981\u767b\u5f55\u9875\u9762\uff0c\u4fee\u6539\u5bc6\u7801\u7b49\u4e00\u4e9b\u529f\u80fd\uff0c\u9700\u8981\u66f4\u52a0\u7684\u5b8c\u5584\u3002\u6211\u5e0c\u671b\u80fd\u591f\u66f4\u8be6\u7ec6\u7684\u3002<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u660e\u767d\u4e86\uff01\u6211\u975e\u5e38\u7406\u89e3\u4f60\u7684\u611f\u53d7\u3002\u4f5c\u4e3a\u4e00\u4e2a\u5b8c\u6574\u7684\u7ba1\u7406\u7cfb\u7edf\uff0c\u4ec5\u4ec5\u6709\u201c\u4eea\u8868\u76d8\u201d\u548c\u201c\u9080\u8bf7\u7801\u201d\u786e\u5b9e\u592a\u5355\u8584\u4e86\u3002<br \/>\n\u8981\u8fbe\u5230\u4f60\u8981\u6c42\u7684\u201c\u8be6\u7ec6\u201d\u548c\u201c\u5b8c\u5584\u201d\uff0c\u6211\u4eec\u9700\u8981\u628a\u7cfb\u7edf\u62c6\u5206\u4e3a**\u201c\u6e38\u5ba2\u533a\uff08\u767b\u5f55\/\u6ce8\u518c\uff09\u201d\u548c\u201c\u6210\u5458\u533a\uff08\u7ba1\u7406\u540e\u53f0\uff09\u201d<strong>\u3002<br \/>\n\u4e3a\u4e86\u8ba9\u4f60\u7f51\u9875\u4e0a\u7684\u4fe1\u606f\u4e30\u5bcc\u8d77\u6765\uff0c\u6211\u4eec\u8981\u8fdb\u884c\u4e00\u6b21<\/strong>\u201c\u5168\u529f\u80fd\u7248\u524d\u7aef\u67b6\u6784\u5347\u7ea7\u201d**\u3002\u6211\u4eec\u4f1a\u589e\u52a0\u767b\u5f55\u9875\u3001\u4e2a\u4eba\u4e2d\u5fc3\u3001\u5de5\u5355\u3001\u8ba2\u5355\u7b49\u6240\u6709\u4f60\u63d0\u5230\u7684\u6a21\u5757\u3002<br \/>\n\u8bf7\u5728 <code>frontend\/navidrome-ui<\/code> \u76ee\u5f55\u4e0b\uff0c\u6309\u7167\u4ee5\u4e0b\u6b65\u9aa4\u64cd\u4f5c\uff1a<\/p>\n<hr \/>\n<h3>\u7b2c\u4e00\u6b65\uff1a\u5efa\u7acb\u7528\u6237\u72b6\u6001\u7ba1\u5bb6 (Pinia Store)<\/h3>\n<p>\u6211\u4eec\u9700\u8981\u4e00\u4e2a\u5730\u65b9\u6765\u8bb0\u4f4f\u7528\u6237\u662f\u5426\u767b\u5f55\u3001\u7528\u6237\u7684\u79ef\u5206\u662f\u591a\u5c11\u3002<\/p>\n<ol>\n<li><strong>\u521b\u5efa store \u76ee\u5f55\uff1a<\/strong><code>mkdir -p src\/store<\/code><\/li>\n<li><strong>\u7f16\u5199\u7528\u6237\u72b6\u6001\uff1a<\/strong><code>vim src\/store\/user.js<\/code><\/li>\n<li><strong>\u7c98\u8d34\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/strong><\/li>\n<\/ol>\n<pre><code class=\"language-javascript\">import { defineStore } from 'pinia'\r\n\r\nexport const useUserStore = defineStore('user', {\r\n  state: () =&gt; ({\r\n    token: localStorage.getItem('token') || '',\r\n    userInfo: JSON.parse(localStorage.getItem('userInfo') || '{}'),\r\n  }),\r\n  getters: {\r\n    isLoggedIn: (state) =&gt; !!state.token,\r\n    points: (state) =&gt; state.userInfo.points || 0\r\n  },\r\n  actions: {\r\n    setLogin(data) {\r\n      this.token = 'dummy-token-' + data.id \/\/ \u6682\u65f6\u6a21\u62df token\r\n      this.userInfo = data\r\n      localStorage.setItem('token', this.token)\r\n      localStorage.setItem('userInfo', JSON.stringify(data))\r\n    },\r\n    logout() {\r\n      this.token = ''\r\n      this.userInfo = {}\r\n      localStorage.removeItem('token')\r\n      localStorage.removeItem('userInfo')\r\n    }\r\n  }\r\n})\r\n<\/code><\/pre>\n<hr \/>\n<h3>\u7b2c\u4e8c\u6b65\uff1a\u7f16\u5199\u534e\u4e3d\u7684\u767b\u5f55\u9875\u9762 (Login.vue)<\/h3>\n<p>\u8fd9\u662f\u7528\u6237\u8fdb\u5165\u7cfb\u7edf\u7684\u7b2c\u4e00\u9053\u95e8\u3002<\/p>\n<ol>\n<li><strong>\u65b0\u5efa\u6587\u4ef6\uff1a<\/strong><code>vim src\/views\/Login.vue<\/code><\/li>\n<li><strong>\u7c98\u8d34\u5185\u5bb9\uff1a<\/strong><\/li>\n<\/ol>\n<pre><code class=\"language-\u4ee3\u7801\u6bb5\">&lt;template&gt;\r\n  &lt;div class=\"login-container\"&gt;\r\n    &lt;div class=\"modern-card login-card\"&gt;\r\n      &lt;div class=\"brand\" style=\"justify-content: center; border: none;\"&gt;\r\n        &lt;div class=\"logo\"&gt;N&lt;\/div&gt;\r\n        &lt;div class=\"brand-name\" style=\"font-size: 24px;\"&gt;Navidrome&lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n      &lt;h2 style=\"text-align: center; margin-bottom: 24px;\"&gt;\u6b22\u8fce\u56de\u6765&lt;\/h2&gt;\r\n      \r\n      &lt;div class=\"field\"&gt;\r\n        &lt;label class=\"label\"&gt;\u90ae\u7bb1\u5730\u5740&lt;\/label&gt;\r\n        &lt;input v-model=\"form.email\" class=\"input\" type=\"email\" placeholder=\"admin@crabpt.vip\"&gt;\r\n      &lt;\/div&gt;\r\n      \r\n      &lt;div class=\"field\"&gt;\r\n        &lt;label class=\"label\"&gt;\u767b\u5f55\u5bc6\u7801&lt;\/label&gt;\r\n        &lt;input v-model=\"form.password\" class=\"input\" type=\"password\" placeholder=\"\u8bf7\u8f93\u5165\u5bc6\u7801\"&gt;\r\n      &lt;\/div&gt;\r\n\r\n      &lt;button class=\"btn btn-gradient\" style=\"width: 100%; margin-top: 10px;\" @click=\"handleLogin\"&gt;\r\n        \u7acb\u5373\u8fdb\u5165\u7cfb\u7edf\r\n      &lt;\/button&gt;\r\n      \r\n      &lt;div style=\"margin-top: 20px; text-align: center; font-size: 13px; color: var(--text-color-tertiary);\"&gt;\r\n        \u8fd8\u6ca1\u6709\u8d26\u53f7\uff1f\u8054\u7cfb\u7ba1\u7406\u5458\u83b7\u53d6\u9080\u8bf7\u7801\u6ce8\u518c\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n  &lt;\/div&gt;\r\n&lt;\/template&gt;\r\n\r\n&lt;script setup&gt;\r\nimport { ref } from 'vue'\r\nimport { useRouter } from 'vue-router'\r\nimport { useUserStore } from '..\/store\/user'\r\nimport request from '..\/utils\/request'\r\n\r\nconst router = useRouter()\r\nconst userStore = useUserStore()\r\nconst form = ref({ email: '', password: '' })\r\n\r\nconst handleLogin = async () =&gt; {\r\n  try {\r\n    const data = await request.post('\/v1\/login', form.value)\r\n    userStore.setLogin(data)\r\n    router.push('\/')\r\n  } catch (error) {\r\n    \/\/ \u9519\u8bef\u5df2\u5728 request.js \u62e6\u622a\u5904\u7406\r\n  }\r\n}\r\n&lt;\/script&gt;\r\n\r\n&lt;style scoped&gt;\r\n.login-container {\r\n  height: 100vh;\r\n  display: flex;\r\n  align-items: center;\r\n  justify-content: center;\r\n  background: var(--bg-color-tertiary);\r\n}\r\n.login-card {\r\n  width: 400px;\r\n  padding: 40px;\r\n}\r\n&lt;\/style&gt;\r\n<\/code><\/pre>\n<hr \/>\n<h3>\u7b2c\u4e09\u6b65\uff1a\u5b8c\u5584\u5168\u529f\u80fd\u4fa7\u8fb9\u680f (Layout.vue)<\/h3>\n<p>\u6211\u4eec\u8981\u628a\u4f60\u8981\u6c42\u7684\u201c\u8d2d\u4e70\u5957\u9910\u3001\u6211\u7684\u8ba2\u5355\u3001\u5de5\u5355\u3001\u8bbe\u7f6e\u201d\u5168\u90e8\u52a0\u8fdb\u53bb\u3002<\/p>\n<ol>\n<li><strong>\u4fee\u6539\u6587\u4ef6\uff1a<\/strong><code>vim src\/components\/Layout.vue<\/code><\/li>\n<li><strong><strong>\u627e\u5230<br \/>\n<\/strong><\/strong><\/p>\n<nav class=\"nav\">\u90e8\u5206\uff0c\u66ff\u6362\u4e3a\uff1a<\/nav>\n<\/li>\n<\/ol>\n<pre><code class=\"language-\u4ee3\u7801\u6bb5\">&lt;nav class=\"nav\"&gt;\r\n          &lt;router-link to=\"\/\" class=\"nav-item\" exact-active-class=\"active\"&gt;\r\n            &lt;div class=\"nav-icon\"&gt;\ud83c\udfe0&lt;\/div&gt;\r\n            &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u4eea\u8868\u76d8&lt;\/div&gt;&lt;\/div&gt;\r\n          &lt;\/router-link&gt;\r\n\r\n          &lt;div class=\"label\" style=\"margin: 10px 12px 5px;\"&gt;\u8d22\u52a1\u4e0e\u670d\u52a1&lt;\/div&gt;\r\n          \r\n          &lt;router-link to=\"\/store\" class=\"nav-item\" exact-active-class=\"active\"&gt;\r\n            &lt;div class=\"nav-icon\"&gt;\ud83d\uded2&lt;\/div&gt;\r\n            &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u8d2d\u4e70\u5957\u9910&lt;\/div&gt;&lt;\/div&gt;\r\n          &lt;\/router-link&gt;\r\n\r\n          &lt;router-link to=\"\/subscriptions\" class=\"nav-item\" exact-active-class=\"active\"&gt;\r\n            &lt;div class=\"nav-icon\"&gt;\ud83d\udc8e&lt;\/div&gt;\r\n            &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u6211\u7684\u8ba2\u9605&lt;\/div&gt;&lt;\/div&gt;\r\n          &lt;\/router-link&gt;\r\n\r\n          &lt;router-link to=\"\/orders\" class=\"nav-item\" exact-active-class=\"active\"&gt;\r\n            &lt;div class=\"nav-icon\"&gt;\ud83d\udcc4&lt;\/div&gt;\r\n            &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u6211\u7684\u8ba2\u5355&lt;\/div&gt;&lt;\/div&gt;\r\n          &lt;\/router-link&gt;\r\n\r\n          &lt;div class=\"label\" style=\"margin: 10px 12px 5px;\"&gt;\u8d26\u6237\u4e2d\u5fc3&lt;\/div&gt;\r\n\r\n          &lt;router-link to=\"\/invites\" class=\"nav-item\" exact-active-class=\"active\"&gt;\r\n            &lt;div class=\"nav-icon\"&gt;\ud83c\udf9f\ufe0f&lt;\/div&gt;\r\n            &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u9080\u8bf7\u8ba1\u5212&lt;\/div&gt;&lt;\/div&gt;\r\n          &lt;\/router-link&gt;\r\n\r\n          &lt;router-link to=\"\/tickets\" class=\"nav-item\" exact-active-class=\"active\"&gt;\r\n            &lt;div class=\"nav-icon\"&gt;\ud83d\udee0\ufe0f&lt;\/div&gt;\r\n            &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u6211\u7684\u5de5\u5355&lt;\/div&gt;&lt;\/div&gt;\r\n          &lt;\/router-link&gt;\r\n\r\n          &lt;router-link to=\"\/settings\" class=\"nav-item\" exact-active-class=\"active\"&gt;\r\n            &lt;div class=\"nav-icon\"&gt;\u2699\ufe0f&lt;\/div&gt;\r\n            &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u8d26\u53f7\u8bbe\u7f6e&lt;\/div&gt;&lt;\/div&gt;\r\n          &lt;\/router-link&gt;\r\n        &lt;\/nav&gt;\r\n<\/code><\/pre>\n<p>\u540c\u65f6\uff0c\u8bb0\u5f97\u5728 <code>header<\/code> \u91cc\u628a\u201c\u5f53\u524d\u79ef\u5206\u201d\u6539\u6210\u4ece <code>userStore<\/code> \u52a8\u6001\u8bfb\u53d6\u3002<\/p>\n<hr \/>\n<h3>\u7b2c\u56db\u6b65\uff1a\u66f4\u65b0\u8def\u7531\u914d\u7f6e (router\/index.js)<\/h3>\n<p>\u6211\u4eec\u9700\u8981\u5728\u8fd9\u91cc\u52a0\u4e0a\u201c\u672a\u767b\u5f55\u62e6\u622a\u201d\u903b\u8f91\uff0c\u5982\u679c\u6ca1\u767b\u5f55\uff0c\u5f3a\u884c\u8df3\u5230\u767b\u5f55\u9875\u3002<\/p>\n<pre><code class=\"language-javascript\">import { createRouter, createWebHistory } from 'vue-router'\r\nimport { useUserStore } from '..\/store\/user'\r\nimport Layout from '..\/components\/Layout.vue'\r\n\r\nconst routes = [\r\n  {\r\n    path: '\/login',\r\n    name: 'Login',\r\n    component: () =&gt; import('..\/views\/Login.vue')\r\n  },\r\n  {\r\n    path: '\/',\r\n    component: Layout,\r\n    children: [\r\n      { path: '', component: () =&gt; import('..\/views\/Dashboard.vue') },\r\n      { path: 'invites', component: () =&gt; import('..\/views\/Invites.vue') },\r\n      \/\/ \u5176\u4ed6\u9875\u9762\u5148\u5360\u4e2a\u4f4d\uff0c\u4f60\u53ef\u4ee5\u4e00\u4e2a\u4e2a\u521b\u5efa\u5bf9\u5e94\u7684 .vue \u6587\u4ef6\r\n      { path: 'store', component: () =&gt; import('..\/views\/Dashboard.vue') }, \r\n      { path: 'subscriptions', component: () =&gt; import('..\/views\/Dashboard.vue') },\r\n      { path: 'orders', component: () =&gt; import('..\/views\/Dashboard.vue') },\r\n      { path: 'tickets', component: () =&gt; import('..\/views\/Dashboard.vue') },\r\n      { path: 'settings', component: () =&gt; import('..\/views\/Dashboard.vue') },\r\n    ]\r\n  }\r\n]\r\n\r\nconst router = createRouter({\r\n  history: createWebHistory(),\r\n  routes\r\n})\r\n\r\n\/\/ \u8def\u7531\u5b88\u536b\uff1a\u6ca1\u767b\u5f55\u7684\u62e6\u4f4f\r\nrouter.beforeEach((to, from, next) =&gt; {\r\n  const userStore = useUserStore()\r\n  if (to.name !== 'Login' &amp;&amp; !userStore.isLoggedIn) {\r\n    next({ name: 'Login' })\r\n  } else {\r\n    next()\r\n  }\r\n})\r\n\r\nexport default router\r\n<\/code><\/pre>\n<hr \/>\n<h3>\u7b2c\u4e94\u6b65\uff1a\u5b8c\u5584\u4e2a\u4eba\u4e2d\u5fc3\u4e0e\u4fee\u6539\u5bc6\u7801\u903b\u8f91<\/h3>\n<p>\u4f60\u53ef\u4ee5\u5728 <code>Settings.vue<\/code> \u9875\u9762\uff08\u5bf9\u5e94\u521a\u624d\u7684 <code>\/settings<\/code>\uff09\u653e\u4e24\u4e2a\u8868\u5355\uff1a<\/p>\n<ol>\n<li><strong>\u57fa\u672c\u8d44\u6599<\/strong>\uff1a\u663e\u793a\u90ae\u7bb1\u3001Navidrome \u8d26\u53f7\u540d\u3002<\/li>\n<li><strong>\u5b89\u5168\u8bbe\u7f6e<\/strong>\uff1a\u8f93\u5165\u539f\u5bc6\u7801\u3001\u65b0\u5bc6\u7801\uff0c\u8c03\u7528\u540e\u7aef\u63a5\u53e3\u4fee\u6539\u3002<\/li>\n<\/ol>\n<hr \/>\n<h3>\ud83d\udfe2 \u73b0\u5728\u7684\u9875\u9762\u4f1a\u53d8\u6210\u4ec0\u4e48\u6837\uff1f<\/h3>\n<p>\u5f53\u4f60\u6267\u884c <code>npm run dev -- --host<\/code> \u542f\u52a8\u540e\uff1a<\/p>\n<ol>\n<li><strong>\u7b2c\u4e00\u773c<\/strong>\uff1a\u4f60\u4f1a\u770b\u5230\u4e00\u4e2a\u975e\u5e38\u4e13\u4e1a\u7684<strong>\u767b\u5f55\u754c\u9762<\/strong>\u3002<\/li>\n<li><strong>\u767b\u5f55\u540e<\/strong>\uff1a<\/li>\n<\/ol>\n<ul>\n<li>\u5de6\u4fa7\u83dc\u5355\u4f1a\u53d8\u5f97\u975e\u5e38\u4e30\u5bcc\uff0c\u6709\u8d22\u52a1\u3001\u5de5\u5355\u3001\u8bbe\u7f6e\u7b49\u591a\u4e2a\u5206\u7ec4\u3002<\/li>\n<li>\u53f3\u4fa7\u9876\u680f\u4f1a\u5b9e\u65f6\u663e\u793a\u201c\u6b22\u8fce\u4f60\uff0cadmin@crabpt.vip | \u79ef\u5206\uff1a10009\u201d\u3002<\/li>\n<li>\u9875\u9762\u4f1a\u6709\u591a\u7ea7\u6807\u9898\u548c\u66f4\u7ec6\u81f4\u7684\u8bf4\u660e\u3002<br \/>\n<strong>\u4e0b\u4e00\u6b65\u5efa\u8bae\uff1a<\/strong><br \/>\n\u5982\u679c\u4f60\u89c9\u5f97\u4fa7\u8fb9\u680f\u83dc\u5355\u5df2\u7ecf\u591f\u4e30\u5bcc\u4e86\uff0c\u6211\u4eec\u53ef\u4ee5\u5f00\u59cb\u628a <strong>\u201c\u8d2d\u4e70\u5957\u9910\u201d (Store.vue)<\/strong> \u548c <strong>\u201c\u4fee\u6539\u5bc6\u7801\u201d (Settings.vue)<\/strong> \u7684\u5177\u4f53\u7cbe\u7f8e\u4ee3\u7801\u5199\u51fa\u6765\u3002<br \/>\n\u4f60\u5e0c\u671b\u6211\u5148\u4e3a\u4f60\u751f\u6210\u54ea\u4e00\u4e2a\u9875\u9762\u7684\u8be6\u7ec6\u4ee3\u7801\uff1f<\/li>\n<\/ul>\n<h2>Turn 33<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>\u95ee\u98981\uff1a\u63a8\u51fa\u767b\u5165\u540e\u4ecd\u5728\u5f53\u524d\u9875\u9762<br \/>\n\u95ee\u98982\uff1a\u7b2c\u4e94\u6b65\uff1a\u5b8c\u5584\u4e2a\u4eba\u4e2d\u5fc3\u4e0e\u4fee\u6539\u5bc6\u7801\u903b\u8f91<br \/>\n\u4f60\u53ef\u4ee5\u5728 Settings.vue \u9875\u9762\uff08\u5bf9\u5e94\u521a\u624d\u7684 \/settings\uff09\u653e\u4e24\u4e2a\u8868\u5355\uff1a<br \/>\n\u57fa\u672c\u8d44\u6599\uff1a\u663e\u793a\u90ae\u7bb1\u3001Navidrome \u8d26\u53f7\u540d\u3002<br \/>\n\u5b89\u5168\u8bbe\u7f6e\uff1a\u8f93\u5165\u539f\u5bc6\u7801\u3001\u65b0\u5bc6\u7801\uff0c\u8c03\u7528\u540e\u7aef\u63a5\u53e3\u4fee\u6539\u3002<br \/>\n\u95ee\u98983\uff1a\u7ba1\u7406\u8d26\u6237\u8bc6\u522b\u540e\uff0c\u4f1a\u9700\u8981\u6709\u4e2a\u7528\u6237\u7ba1\u7406\u7684\uff0c\u9080\u8bf7\u7801\u7ba1\u7406\u7b49\u7b49\u7684\u754c\u9762\u9700\u8981\u5b8c\u6574\u3002<br \/>\n\u8ba9\u6211\u4eec\u5148\u89e3\u51b3\u8fd9\u51e0\u4e2a\u95ee\u9898\u3002<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u6536\u5230\uff0c\u8fd9\u4e09\u4e2a\u95ee\u9898\u5207\u4e2d\u4e86\u7ba1\u7406\u7cfb\u7edf\u7684\u6838\u5fc3\u4ea4\u4e92\u903b\u8f91\u3002\u4e3a\u4e86\u8ba9\u7cfb\u7edf\u66f4\u52a0\u4e13\u4e1a\u548c\u95ed\u73af\uff0c\u6211\u4eec\u9700\u8981\u5bf9\u524d\u7aef\u8fdb\u884c\u4e00\u6b21\u9488\u5bf9\u6027\u7684\u903b\u8f91\u52a0\u56fa\u3002<br \/>\n\u4ee5\u4e0b\u662f\u9488\u5bf9\u8fd9\u4e09\u4e2a\u95ee\u9898\u7684\u8be6\u7ec6\u89e3\u51b3\u65b9\u6848\uff1a<\/p>\n<h3>\u95ee\u9898 1\uff1a\u89e3\u51b3\u767b\u51fa\u540e\u4ecd\u7559\u5728\u539f\u9875\u9762\u7684\u903b\u8f91<\/h3>\n<p>\u5728 <code>Layout.vue<\/code> \u4e2d\uff0c\u76ee\u524d\u7684\u767b\u51fa\u53ea\u662f\u7b80\u5355\u7684\u5f39\u7a97\u3002\u6211\u4eec\u9700\u8981\u8c03\u7528 <code>userStore<\/code> \u6e05\u9664\u72b6\u6001\uff0c\u5e76\u5229\u7528 <code>vue-router<\/code> \u5f3a\u5236\u8df3\u8f6c\u56de\u767b\u5f55\u9875\u3002<br \/>\n<strong>\u4fee\u6539 src\/components\/Layout.vue \u4e2d\u7684 handleLogout \u65b9\u6cd5\uff1a<\/strong><\/p>\n<pre><code class=\"language-javascript\">import { useRouter } from 'vue-router'\r\nimport { useUserStore } from '..\/store\/user'\r\n\r\nconst router = useRouter()\r\nconst userStore = useUserStore()\r\n\r\nconst handleLogout = () =&gt; {\r\n  if (confirm(\"\u786e\u8ba4\u9000\u51fa\u7cfb\u7edf\uff1f\")) {\r\n    userStore.logout() \/\/ \u6e05\u9664 Pinia \u548c LocalStorage \u91cc\u7684\u7528\u6237\u4fe1\u606f\r\n    router.push('\/login') \/\/ \u5f3a\u5236\u8df3\u8f6c\u5230\u767b\u5f55\u9875\r\n  }\r\n}\r\n<\/code><\/pre>\n<hr \/>\n<h3>\u95ee\u9898 2\uff1a\u5b8c\u5584\u4e2a\u4eba\u4e2d\u5fc3\u4e0e\u5b89\u5168\u8bbe\u7f6e (<code>Settings.vue<\/code>)<\/h3>\n<p>\u6211\u4eec\u9700\u8981\u521b\u5efa\u4e00\u4e2a\u529f\u80fd\u5b8c\u5907\u7684\u8bbe\u7f6e\u9875\u9762\uff0c\u5305\u542b\u57fa\u7840\u8d44\u6599\u5c55\u793a\u548c\u5bc6\u7801\u4fee\u6539\u8868\u5355\u3002<br \/>\n<strong>\u65b0\u5efa src\/views\/Settings.vue\uff1a<\/strong><\/p>\n<pre><code class=\"language-\u4ee3\u7801\u6bb5\">&lt;template&gt;\r\n  &lt;div&gt;\r\n    &lt;div class=\"page-header\"&gt;\r\n      &lt;div class=\"page-title\"&gt;\u8d26\u53f7\u8bbe\u7f6e&lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n\r\n    &lt;div class=\"stat-grid\"&gt;\r\n      &lt;div class=\"modern-card\" style=\"grid-column: span 6;\"&gt;\r\n        &lt;div class=\"card-head\"&gt;&lt;div class=\"card-title\"&gt;\u57fa\u672c\u8d44\u6599&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;div class=\"card-body\"&gt;\r\n          &lt;div class=\"field\"&gt;\r\n            &lt;label class=\"label\"&gt;\u7535\u5b50\u90ae\u7bb1&lt;\/label&gt;\r\n            &lt;input class=\"input\" :value=\"userStore.userInfo.email\" readonly style=\"background: var(--bg-color-tertiary);\"&gt;\r\n          &lt;\/div&gt;\r\n          &lt;div class=\"field\"&gt;\r\n            &lt;label class=\"label\"&gt;\u5173\u8054 Navidrome \u8d26\u53f7&lt;\/label&gt;\r\n            &lt;input class=\"input\" :value=\"userStore.userInfo.navidromeUsername || '\u672a\u5173\u8054'\" readonly&gt;\r\n          &lt;\/div&gt;\r\n          &lt;div class=\"field\"&gt;\r\n            &lt;label class=\"label\"&gt;\u5f53\u524d\u89d2\u8272&lt;\/label&gt;\r\n            &lt;div&gt;&lt;span class=\"badge info\"&gt;{{ userStore.userInfo.role === 'admin' ? '\u8d85\u7ea7\u7ba1\u7406\u5458' : '\u666e\u901a\u7528\u6237' }}&lt;\/span&gt;&lt;\/div&gt;\r\n          &lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n\r\n      &lt;div class=\"modern-card\" style=\"grid-column: span 6;\"&gt;\r\n        &lt;div class=\"card-head\"&gt;&lt;div class=\"card-title\"&gt;\u4fee\u6539\u5bc6\u7801&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;div class=\"card-body\"&gt;\r\n          &lt;div class=\"field\"&gt;\r\n            &lt;label class=\"label\"&gt;\u539f\u5bc6\u7801&lt;\/label&gt;\r\n            &lt;input v-model=\"pwdForm.oldPassword\" type=\"password\" class=\"input\" placeholder=\"\u8bf7\u8f93\u5165\u5f53\u524d\u5bc6\u7801\"&gt;\r\n          &lt;\/div&gt;\r\n          &lt;div class=\"field\"&gt;\r\n            &lt;label class=\"label\"&gt;\u65b0\u5bc6\u7801&lt;\/label&gt;\r\n            &lt;input v-model=\"pwdForm.newPassword\" type=\"password\" class=\"input\" placeholder=\"\u5efa\u8bae\u5305\u542b\u5b57\u6bcd\u4e0e\u6570\u5b57\"&gt;\r\n          &lt;\/div&gt;\r\n          &lt;div class=\"field\"&gt;\r\n            &lt;label class=\"label\"&gt;\u786e\u8ba4\u65b0\u5bc6\u7801&lt;\/label&gt;\r\n            &lt;input v-model=\"pwdForm.confirmPassword\" type=\"password\" class=\"input\" placeholder=\"\u8bf7\u518d\u6b21\u8f93\u5165\u65b0\u5bc6\u7801\"&gt;\r\n          &lt;\/div&gt;\r\n          &lt;button class=\"btn btn-gradient\" style=\"width: 100%; margin-top: 10px;\" @click=\"updatePassword\"&gt;\r\n            \u66f4\u65b0\u5bc6\u7801\r\n          &lt;\/button&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n  &lt;\/div&gt;\r\n&lt;\/template&gt;\r\n\r\n&lt;script setup&gt;\r\nimport { ref } from 'vue'\r\nimport { useUserStore } from '..\/store\/user'\r\nimport request from '..\/utils\/request'\r\n\r\nconst userStore = useUserStore()\r\nconst pwdForm = ref({ oldPassword: '', newPassword: '', confirmPassword: '' })\r\n\r\nconst updatePassword = async () =&gt; {\r\n  if (pwdForm.value.newPassword !== pwdForm.value.confirmPassword) {\r\n    return alert(\"\u4e24\u6b21\u8f93\u5165\u7684\u65b0\u5bc6\u7801\u4e0d\u4e00\u81f4\")\r\n  }\r\n  try {\r\n    \/\/ \u8fd9\u91cc\u8c03\u7528\u540e\u7aef\u672a\u6765\u7684\u4fee\u6539\u5bc6\u7801\u63a5\u53e3\r\n    await request.post('\/v1\/user\/update-pwd', {\r\n      userId: userStore.userInfo.id,\r\n      ...pwdForm.value\r\n    })\r\n    alert(\"\u5bc6\u7801\u4fee\u6539\u6210\u529f\uff0c\u8bf7\u91cd\u65b0\u767b\u5f55\")\r\n    userStore.logout()\r\n    window.location.href = '\/login'\r\n  } catch (error) {}\r\n}\r\n&lt;\/script&gt;\r\n<\/code><\/pre>\n<hr \/>\n<h3>\u95ee\u9898 3\uff1a\u7ba1\u7406\u8d26\u6237\u8bc6\u522b\u4e0e\u7ba1\u7406\u754c\u9762\u5b9e\u73b0<\/h3>\n<p>\u6211\u4eec\u9700\u8981\u5728\u4fa7\u8fb9\u680f\u8bc6\u522b\u7528\u6237\u89d2\u8272\uff08Admin\uff09\uff0c\u5e76\u63d0\u4f9b\u4e13\u95e8\u7684\u201c\u7528\u6237\u7ba1\u7406\u201d\u754c\u9762\u3002<\/p>\n<h4>1. \u4fee\u6539 <code>Layout.vue<\/code> \u4fa7\u8fb9\u680f\u903b\u8f91<\/h4>\n<p>\u5728\u4fa7\u8fb9\u680f\u4e2d\u589e\u52a0\u7ba1\u7406\u5458\u4e13\u533a\uff0c\u5e76\u4f7f\u7528 <code>v-if<\/code> \u8fdb\u884c\u6743\u9650\u63a7\u5236\u3002<br \/>\n<strong>\u4fee\u6539 src\/components\/Layout.vue \u4e2d\u7684\u83dc\u5355\u90e8\u5206\uff1a<\/strong><\/p>\n<pre><code class=\"language-\u4ee3\u7801\u6bb5\">&lt;div v-if=\"userStore.userInfo.role === 'admin'\"&gt;\r\n  &lt;div class=\"label\" style=\"margin: 10px 12px 5px; color: var(--error-color);\"&gt;\u7ba1\u7406\u5de5\u5177&lt;\/div&gt;\r\n  \r\n  &lt;router-link to=\"\/admin\/users\" class=\"nav-item\" exact-active-class=\"active\"&gt;\r\n    &lt;div class=\"nav-icon\"&gt;\ud83d\udc64&lt;\/div&gt;\r\n    &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u7528\u6237\u7ba1\u7406&lt;\/div&gt;&lt;\/div&gt;\r\n  &lt;\/router-link&gt;\r\n\r\n  &lt;router-link to=\"\/admin\/invites\" class=\"nav-item\" exact-active-class=\"active\"&gt;\r\n    &lt;div class=\"nav-icon\"&gt;\ud83c\udf9f\ufe0f&lt;\/div&gt;\r\n    &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u5168\u5c40\u9080\u8bf7\u7801&lt;\/div&gt;&lt;\/div&gt;\r\n  &lt;\/router-link&gt;\r\n&lt;\/div&gt;\r\n<\/code><\/pre>\n<h4>2. \u5b9e\u73b0\u7528\u6237\u7ba1\u7406\u9875\u9762 (<code>Users.vue<\/code>)<\/h4>\n<p>\u8fd9\u4e2a\u9875\u9762\u5141\u8bb8\u7ba1\u7406\u5458\u67e5\u770b\u6240\u6709\u7528\u6237\u5e76\u8fdb\u884c\u5c01\u7981\u7b49\u64cd\u4f5c\u3002<br \/>\n<strong>\u65b0\u5efa src\/views\/admin\/Users.vue\uff1a<\/strong><\/p>\n<pre><code class=\"language-\u4ee3\u7801\u6bb5\">&lt;template&gt;\r\n  &lt;div&gt;\r\n    &lt;div class=\"page-header\"&gt;\r\n      &lt;div class=\"page-title\"&gt;\u5168\u7ad9\u7528\u6237\u7ba1\u7406&lt;\/div&gt;\r\n      &lt;button class=\"btn\" @click=\"fetchUsers\"&gt;\ud83d\udd04 \u5237\u65b0\u6570\u636e&lt;\/button&gt;\r\n    &lt;\/div&gt;\r\n\r\n    &lt;div class=\"modern-card\"&gt;\r\n      &lt;div class=\"card-body\"&gt;\r\n        &lt;div class=\"table-wrap\"&gt;\r\n          &lt;table&gt;\r\n            &lt;thead&gt;\r\n              &lt;tr&gt;\r\n                &lt;th&gt;ID&lt;\/th&gt;\r\n                &lt;th&gt;\u90ae\u7bb1&lt;\/th&gt;\r\n                &lt;th&gt;\u79ef\u5206&lt;\/th&gt;\r\n                &lt;th&gt;\u89d2\u8272&lt;\/th&gt;\r\n                &lt;th&gt;\u72b6\u6001&lt;\/th&gt;\r\n                &lt;th&gt;\u64cd\u4f5c&lt;\/th&gt;\r\n              &lt;\/tr&gt;\r\n            &lt;\/thead&gt;\r\n            &lt;tbody&gt;\r\n              &lt;tr v-for=\"user in userList\" :key=\"user.id\"&gt;\r\n                &lt;td class=\"td-mono\"&gt;#{{ user.id }}&lt;\/td&gt;\r\n                &lt;td&gt;{{ user.email }}&lt;\/td&gt;\r\n                &lt;td style=\"color: var(--warning-color); font-weight: bold;\"&gt;{{ user.points }}&lt;\/td&gt;\r\n                &lt;td&gt;&lt;span class=\"badge\" :class=\"user.role === 'admin' ? 'info' : 'warning'\"&gt;{{ user.role }}&lt;\/span&gt;&lt;\/td&gt;\r\n                &lt;td&gt;\r\n                  &lt;span class=\"badge\" :class=\"user.status === 'active' ? 'success' : 'error'\"&gt;\r\n                    {{ user.status === 'active' ? '\u6b63\u5e38' : '\u5c01\u7981' }}\r\n                  &lt;\/span&gt;\r\n                &lt;\/td&gt;\r\n                &lt;td&gt;\r\n                  &lt;button class=\"btn btn-xs btn-danger\" @click=\"toggleUserStatus(user)\"&gt;\r\n                    {{ user.status === 'active' ? '\u5c01\u7981' : '\u89e3\u5c01' }}\r\n                  &lt;\/button&gt;\r\n                &lt;\/td&gt;\r\n              &lt;\/tr&gt;\r\n            &lt;\/tbody&gt;\r\n          &lt;\/table&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n  &lt;\/div&gt;\r\n&lt;\/template&gt;\r\n\r\n&lt;script setup&gt;\r\nimport { ref, onMounted } from 'vue'\r\nimport request from '..\/..\/utils\/request'\r\n\r\nconst userList = ref([])\r\n\r\nconst fetchUsers = async () =&gt; {\r\n  try {\r\n    \/\/ \u8c03\u7528\u540e\u7aef\u6211\u4eec\u4e4b\u524d\u751f\u6210\u7684\u63a5\u53e3\u903b\u8f91\r\n    const data = await request.get('\/v1\/admin\/users')\r\n    userList.value = data\r\n  } catch (error) {}\r\n}\r\n\r\nconst toggleUserStatus = async (user) =&gt; {\r\n  const action = user.status === 'active' ? '\u5c01\u7981' : '\u89e3\u5c01'\r\n  if (confirm(`\u786e\u5b9a\u8981${action}\u7528\u6237 ${user.email} \u5417\uff1f`)) {\r\n    \/\/ \u8c03\u7528\u5c01\u7981\u63a5\u53e3\r\n    alert(`${action}\u6210\u529f`)\r\n    fetchUsers()\r\n  }\r\n}\r\n\r\nonMounted(fetchUsers)\r\n&lt;\/script&gt;\r\n<\/code><\/pre>\n<hr \/>\n<h3>\u6700\u540e\u4e00\u6b65\uff1a\u66f4\u65b0\u8def\u7531\u8868 (<code>src\/router\/index.js<\/code>)<\/h3>\n<p>\u786e\u4fdd\u6240\u6709\u65b0\u9875\u9762\u90fd\u80fd\u88ab\u8bbf\u95ee\u5230\u3002<\/p>\n<pre><code class=\"language-javascript\">\/\/ ... \u5176\u4ed6\u5f15\u5165\r\nconst routes = [\r\n  { path: '\/login', name: 'Login', component: () =&gt; import('..\/views\/Login.vue') },\r\n  {\r\n    path: '\/',\r\n    component: Layout,\r\n    children: [\r\n      { path: '', component: () =&gt; import('..\/views\/Dashboard.vue') },\r\n      { path: 'invites', component: () =&gt; import('..\/views\/Invites.vue') },\r\n      { path: 'settings', component: () =&gt; import('..\/views\/Settings.vue') },\r\n      \/\/ \u7ba1\u7406\u5458\u8def\u5f84\r\n      { path: 'admin\/users', component: () =&gt; import('..\/views\/admin\/Users.vue') },\r\n      { path: 'admin\/invites', component: () =&gt; import('..\/views\/Invites.vue') }, \/\/ \u590d\u7528\u9080\u8bf7\u7801\u9875\u9762\r\n      \/\/ \u5360\u4f4d\u9875\u9762\r\n      { path: 'store', component: () =&gt; import('..\/views\/Dashboard.vue') },\r\n      { path: 'subscriptions', component: () =&gt; import('..\/views\/Dashboard.vue') },\r\n      { path: 'orders', component: () =&gt; import('..\/views\/Dashboard.vue') },\r\n      { path: 'tickets', component: () =&gt; import('..\/views\/Dashboard.vue') },\r\n    ]\r\n  }\r\n]\r\n<\/code><\/pre>\n<h3>\ud83d\udfe2 \u5b8c\u5584\u540e\u7684\u7cfb\u7edf\u4f53\u9a8c\uff1a<\/h3>\n<ol>\n<li><strong>\u5b89\u5168\u6027<\/strong>\uff1a\u672a\u767b\u5f55\u7528\u6237\u4f1a\u88ab\u8def\u7531\u5b88\u536b\u5f3a\u5236\u62e6\u622a\u5230\u767b\u5f55\u9875\uff1b\u767b\u51fa\u540e\u4f1a\u5f7b\u5e95\u6e05\u9664\u672c\u5730\u7f13\u5b58\u5e76\u8df3\u8f6c\u3002<\/li>\n<li><strong>\u89d2\u8272\u5dee\u5f02\u5316<\/strong>\uff1a\u5982\u679c\u4f60\u4ee5\u7ba1\u7406\u5458\u767b\u5f55\uff0c\u4fa7\u8fb9\u680f\u4f1a\u51fa\u73b0\u7ea2\u8272\u7684\u201c\u7ba1\u7406\u5de5\u5177\u201d\u533a\uff1b\u666e\u901a\u7528\u6237\u5219\u5b8c\u5168\u770b\u4e0d\u5230\u3002<\/li>\n<li><strong>\u529f\u80fd\u95ed\u73af<\/strong>\uff1a\u7528\u6237\u53ef\u4ee5\u5728\u8bbe\u7f6e\u91cc\u5b9e\u65f6\u770b\u5230\u81ea\u5df1\u7684 Navidrome \u72b6\u6001\u5e76\u4fee\u6539\u5bc6\u7801\u3002<br \/>\n\u63a5\u4e0b\u6765\uff0c\u4f60\u53ef\u4ee5\u5c1d\u8bd5\u5728 <code>src\/views\/<\/code> \u76ee\u5f55\u4e0b\u521b\u5efa\u5bf9\u5e94\u7684\u9875\u9762\u6587\u4ef6\u3002\u5982\u679c\u6709\u4efb\u4f55\u5177\u4f53\u7684\u9875\u9762\uff08\u6bd4\u5982\u201c\u8d2d\u4e70\u5957\u9910\u201d\uff09\u9700\u8981\u7279\u6b8a\u7684\u5e03\u5c40\u8bbe\u8ba1\uff0c\u968f\u65f6\u544a\u8bc9\u6211\uff01<\/li>\n<\/ol>\n<h2>Turn 34<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>\u279c press h + enter to show help<br \/>\n3:44:43 AM [vite] (client) Pre-transform error: [vue\/compiler-sfc] This experimental syntax requires enabling one of the following parser plugin(s): &#8220;jsx&#8221;, &#8220;flow&#8221;, &#8220;typescript&#8221;. (41:0)<br \/>\n\/mnt\/navidrome-system\/frontend\/navidrome-ui\/src\/components\/Layout.vue<br \/>\n124| }<br \/>\n125|<br \/>\n126| \/\/\u83dc\u5355\u90e8\u5206\u7ba1\u7406<br \/>\n| ^<br \/>\n127|<\/p>\n<div>\n<p>| ^<br \/>\n128|<\/p>\n<div class=\"label\" style=\"margin: 10px 12px 5px; color: var(--error-color);\">\u7ba1\u7406\u5de5\u5177<\/div>\n<p>Plugin: vite:vue<br \/>\nFile: \/mnt\/navidrome-system\/frontend\/navidrome-ui\/src\/components\/Layout.vue:41:0<br \/>\n21 |<br \/>\n22 |<br \/>\n23 |<\/p>\n<div class=\"nav-icon\">\ud83d\uded2<\/div>\n<p>| ^<br \/>\n24 |<\/p>\n<div class=\"nav-text\">\n<div class=\"nav-title\">\u8d2d\u4e70\u5957\u9910<\/div>\n<\/div>\n<p>25 |<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u8fd9\u662f\u4e00\u4e2a\u975e\u5e38\u5178\u578b\u7684 Vue \u5355\u6587\u4ef6\u7ec4\u4ef6 (SFC) \u89e3\u6790\u9519\u8bef\u3002\u62a5\u9519\u4fe1\u606f <code>[vue\/compiler-sfc] This experimental syntax requires enabling one of the following parser plugin(s): \"jsx\"...<\/code> \u770b\u4f3c\u662f\u8bf4\u8bed\u6cd5\u4e0d\u652f\u6301\uff0c\u4f46<strong>\u6839\u672c\u539f\u56e0\u5176\u5b9e\u662f\u4f60\u628a HTML \u6a21\u677f\u4ee3\u7801\uff08<\/strong><\/p>\n<div>\n<p>&#8230;\uff09\u5199\u5230\u4e86 \u6807\u7b7e\u7684\u91cc\u9762\uff01<br \/>\nVue \u7684\u7f16\u8bd1\u5668\u5728 <code>&lt;script&gt;<\/code> \u6807\u7b7e\u91cc\u9047\u5230\u4e86 HTML \u6807\u7b7e\uff08\u6bd4\u5982 <code>&lt;div&gt;<\/code>\uff09\uff0c\u5b83\u4ee5\u4e3a\u4f60\u60f3\u5199 JSX\uff08\u4e00\u79cd JavaScript \u7684\u8bed\u6cd5\u6269\u5c55\uff09\uff0c\u6240\u4ee5\u624d\u62a5\u4e86\u8fd9\u4e2a\u770b\u4f3c\u5947\u602a\u7684\u9519\u3002<\/p>\n<h3>\u95ee\u9898\u5b9a\u4f4d<\/h3>\n<p>\u8bf7\u6253\u5f00\u4f60\u7684 <code>\/mnt\/navidrome-system\/frontend\/navidrome-ui\/src\/components\/Layout.vue<\/code> \u6587\u4ef6\u3002<br \/>\n\u6839\u636e\u62a5\u9519\u4fe1\u606f\uff1a<\/p>\n<pre><code class=\"language-plaintext\">126|  \/\/\u83dc\u5355\u90e8\u5206\u7ba1\u7406\r\n127|  &lt;div v-if=\"userStore.userInfo.role === 'admin'\"&gt;\r\n<\/code><\/pre>\n<p>\u8fd9\u6bb5<strong>\u83dc\u5355\u7ba1\u7406<\/strong>\u7684\u4ee3\u7801\uff0c\u88ab\u4f60\u9519\u8bef\u5730\u653e\u5230\u4e86\u6587\u4ef6\u5e95\u90e8\u7684 <code>&lt;script setup&gt;<\/code> \u91cc\u9762\uff0c\u6216\u8005\u653e\u5230\u4e86 <code>&lt;template&gt;<\/code> \u4e4b\u5916\u3002<\/p>\n<h3>\u6b63\u786e\u7684\u4fee\u6539\u65b9\u6cd5<\/h3>\n<p>Vue \u5355\u6587\u4ef6\u7ec4\u4ef6\u5206\u4e3a\u4e09\u90e8\u5206\uff1a<\/p>\n<ol>\n<li><code>&lt;template&gt;<\/code>\uff1a\u653e\u6240\u6709\u7684 HTML \u7ed3\u6784\uff08\u5305\u62ec\u4fa7\u8fb9\u680f\u83dc\u5355\uff09\u3002<\/li>\n<li><code>&lt;script setup&gt;<\/code>\uff1a\u653e\u6240\u6709\u7684 JavaScript \u903b\u8f91\u3002<\/li>\n<li><code>&lt;style&gt;<\/code>\uff1a\u653e\u7ec4\u4ef6\u4e13\u5c5e\u6837\u5f0f\u3002<br \/>\n\u521a\u624d\u8ba9\u4f60\u589e\u52a0\u7684\u201c\u7ba1\u7406\u5458\u4e13\u533a\u201d\u83dc\u5355\uff0c<strong><strong>\u5fc5\u987b\u653e\u5728 \u6807\u7b7e\u5185\u90e8\u7684<\/strong><\/strong><\/p>\n<nav class=\"nav\">\u91cc\u9762<\/nav>\n<p>\u3002<br \/>\n\u8bf7\u91cd\u65b0\u6253\u5f00\u5e76\u68c0\u67e5\u4f60\u7684 <code>Layout.vue<\/code>\uff0c\u5b83\u5e94\u8be5\u957f\u4e0b\u9762\u8fd9\u4e2a\u6837\u5b50\uff08\u8bf7\u6ce8\u610f\u533a\u5206 <code>&lt;template&gt;<\/code> \u548c <code>&lt;script setup&gt;<\/code> \u7684\u8fb9\u754c\uff09\uff1a<\/li>\n<\/ol>\n<pre><code class=\"language-\u4ee3\u7801\u6bb5\">&lt;template&gt;\r\n  &lt;div class=\"backdrop\" :class=\"{ show: isMobileMenuOpen }\" @click=\"isMobileMenuOpen = false\"&gt;&lt;\/div&gt;\r\n\r\n  &lt;div class=\"app\"&gt;\r\n    &lt;aside class=\"sider\" :class=\"{ collapsed: isCollapsed, open: isMobileMenuOpen }\"&gt;\r\n      &lt;div class=\"brand\"&gt;\r\n        &lt;\/div&gt;\r\n\r\n      &lt;nav class=\"nav\"&gt;\r\n        &lt;router-link to=\"\/\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n          &lt;div class=\"nav-icon\"&gt;\ud83c\udfe0&lt;\/div&gt;\r\n          &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u4eea\u8868\u76d8&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;\/router-link&gt;\r\n\r\n        &lt;div class=\"label\" style=\"margin: 10px 12px 5px;\"&gt;\u8d22\u52a1\u4e0e\u670d\u52a1&lt;\/div&gt;\r\n        \r\n        &lt;router-link to=\"\/store\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n          &lt;div class=\"nav-icon\"&gt;\ud83d\uded2&lt;\/div&gt;\r\n          &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u8d2d\u4e70\u5957\u9910&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;\/router-link&gt;\r\n\r\n        &lt;router-link to=\"\/settings\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n          &lt;div class=\"nav-icon\"&gt;\u2699\ufe0f&lt;\/div&gt;\r\n          &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u8d26\u53f7\u8bbe\u7f6e&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;\/router-link&gt;\r\n\r\n        &lt;div v-if=\"userStore.userInfo.role === 'admin'\"&gt;\r\n          &lt;div class=\"label\" style=\"margin: 10px 12px 5px; color: var(--error-color);\"&gt;\u7ba1\u7406\u5de5\u5177&lt;\/div&gt;\r\n          \r\n          &lt;router-link to=\"\/admin\/users\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n            &lt;div class=\"nav-icon\"&gt;\ud83d\udc64&lt;\/div&gt;\r\n            &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u7528\u6237\u7ba1\u7406&lt;\/div&gt;&lt;\/div&gt;\r\n          &lt;\/router-link&gt;\r\n\r\n          &lt;router-link to=\"\/admin\/invites\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n            &lt;div class=\"nav-icon\"&gt;\ud83c\udf9f\ufe0f&lt;\/div&gt;\r\n            &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u5168\u5c40\u9080\u8bf7\u7801&lt;\/div&gt;&lt;\/div&gt;\r\n          &lt;\/router-link&gt;\r\n        &lt;\/div&gt;\r\n        &lt;\/nav&gt;\r\n      &lt;div class=\"sider-footer\"&gt;\r\n        &lt;\/div&gt;\r\n    &lt;\/aside&gt;\r\n\r\n    &lt;\/div&gt;\r\n&lt;\/template&gt;\r\n\r\n&lt;script setup&gt;\r\nimport { ref, onMounted } from 'vue'\r\nimport { useRouter } from 'vue-router'\r\nimport { useUserStore } from '..\/store\/user'\r\n\r\nconst router = useRouter()\r\nconst userStore = useUserStore()\r\n\r\n\/\/ ...\u4e0b\u9762\u662f\u4f60\u539f\u6765\u7684 JavaScript \u4ee3\u7801...\r\nconst isCollapsed = ref(false)\r\n\/\/ ...\r\n&lt;\/script&gt;\r\n<\/code><\/pre>\n<p>\u8bf7\u6309\u7167\u4e0a\u9762\u7ed3\u6784\uff0c\u628a\u90a3\u6bb5 <code>&lt;div v-if=\"userStore...<\/code> \u526a\u5207\u51fa\u6765\uff0c\u653e\u56de <code>&lt;template&gt;<\/code> \u91cc\u9762\u7684 <code>&lt;nav class=\"nav\"&gt;<\/code> \u7684\u6700\u5e95\u90e8\u3002\u4fdd\u5b58\u6587\u4ef6\u540e\uff0cVite \u7684\u70ed\u66f4\u65b0\uff08HMR\uff09\u4f1a\u81ea\u52a8\u91cd\u65b0\u7f16\u8bd1\uff0c\u90a3\u4e2a\u7ea2\u8272\u62a5\u9519\u5c31\u4f1a\u6d88\u5931\u4e86\uff01<\/p>\n<h2>Turn 35<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<blockquote><p>\u8bf7\u6309\u7167\u4e0a\u9762\u7ed3\u6784\uff0c\u628a\u90a3\u6bb5 &lt;div v-if=&#8221;userStore&#8230; \u526a\u5207\u51fa\u6765\uff0c\u653e\u56de \u91cc\u9762\u7684<\/p>\n<nav class=\"nav\">\u7684\u6700\u5e95\u90e8\u3002\u4fdd\u5b58\u6587\u4ef6\u540e\uff0cVite \u7684\u70ed\u66f4\u65b0\uff08HMR\uff09\u4f1a\u81ea\u52a8\u91cd\u65b0\u7f16\u8bd1\uff0c\u90a3\u4e2a\u7ea2\u8272\u62a5\u9519\u5c31\u4f1a\u6d88\u5931\u4e86\uff01<\/nav>\n<\/blockquote>\n<div class=\"backdrop\"><\/div>\n<div class=\"app\">\n<aside class=\"sider\">\n<div class=\"brand\">\n<div class=\"logo\">N<\/div>\n<div class=\"brand-text\">\n<div class=\"brand-name\">Navidrome<\/div>\n<div class=\"brand-sub\">\u79ef\u5206\u4e0e\u7528\u6237\u7ba1\u7406<\/div>\n<\/div>\n<\/div>\n<nav class=\"nav\">\n<div class=\"nav-icon\">\ud83c\udfe0<\/div>\n<div class=\"nav-text\">\n<div class=\"nav-title\">\u4eea\u8868\u76d8<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<div class=\"label\" style=\"margin: 10px 12px 5px;\">\u8d22\u52a1\u4e0e\u670d\u52a1<\/div>\n<p>&nbsp;<\/p>\n<div class=\"nav-icon\">\ud83d\uded2<\/div>\n<div class=\"nav-text\">\n<div class=\"nav-title\">\u8d2d\u4e70\u5957\u9910<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<div class=\"nav-icon\">\ud83d\udc8e<\/div>\n<div class=\"nav-text\">\n<div class=\"nav-title\">\u6211\u7684\u8ba2\u9605<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<div class=\"nav-icon\">\ud83d\udcc4<\/div>\n<div class=\"nav-text\">\n<div class=\"nav-title\">\u6211\u7684\u8ba2\u5355<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<div class=\"label\" style=\"margin: 10px 12px 5px;\">\u8d26\u6237\u4e2d\u5fc3<\/div>\n<p>&nbsp;<\/p>\n<div class=\"nav-icon\">\ud83c\udf9f\ufe0f<\/div>\n<div class=\"nav-text\">\n<div class=\"nav-title\">\u9080\u8bf7\u8ba1\u5212<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<div class=\"nav-icon\">\ud83d\udee0\ufe0f<\/div>\n<div class=\"nav-text\">\n<div class=\"nav-title\">\u6211\u7684\u5de5\u5355<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<div class=\"nav-icon\">\u2699\ufe0f<\/div>\n<div class=\"nav-text\">\n<div class=\"nav-title\">\u8d26\u53f7\u8bbe\u7f6e<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<\/nav>\n<div class=\"sider-footer\"><button class=\"btn\"><br \/>\n{{ isCollapsed ? &#8216;\u27a1\ufe0f&#8217; : &#8216;\u2b05\ufe0f&#8217; }} <span class=\"hide-on-collapse\">\u6536\u8d77\u4fa7\u680f<\/span><br \/>\n<\/button><br \/>\n<button class=\"btn btn-danger\"><br \/>\n\u23fb <span class=\"hide-on-collapse\">\u9000\u51fa\u767b\u5f55<\/span><br \/>\n<\/button><\/div>\n<\/aside>\n<p>&nbsp;<\/p>\n<header class=\"header\">\n<div style=\"display: flex; align-items: center; gap: 10px;\"><button class=\"btn mobile-only\">\u2630<\/button><\/p>\n<div>\n<div style=\"font-weight: 900;\">Navidrome Server<\/div>\n<\/div>\n<\/div>\n<div style=\"display: flex; gap: 10px; align-items: center;\"><button class=\"btn\">{{ isDark ? &#8216;\u2600\ufe0f&#8217; : &#8216;\ud83c\udf19&#8217; }}<\/button><br \/>\n<span class=\"badge info\">\u5728\u7ebf<\/span><br \/>\n<button class=\"btn\">\ud83e\uddd1\u200d\ud83d\udcbb \u4e2a\u4eba\u4e2d\u5fc3<\/button><\/div>\n<\/header>\n<section class=\"content\"><\/section>\n<p>&nbsp;<\/p>\n<\/div>\n<p>&nbsp;<\/p>\n<p>\u4ee3\u7801\u4f18\u5316<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u592a\u68d2\u4e86\uff0c\u4f60\u5df2\u7ecf\u627e\u51c6\u4e86\u6587\u4ef6\uff01\u6211\u5df2\u7ecf\u5e2e\u4f60\u628a\u4ee3\u7801\u8fdb\u884c\u4e86<strong>\u5b8c\u7f8e\u7684\u91cd\u7ec4\u548c\u4f18\u5316<\/strong>\u3002<br \/>\n\u4e3b\u8981\u4fee\u590d\u548c\u4f18\u5316\u4e86\u4ee5\u4e0b\u51e0\u70b9\uff1a<\/p>\n<ol>\n<li><strong>\u89e3\u51b3\u62a5\u9519<\/strong>\uff1a\u628a\u539f\u672c\u9519\u8bef\u653e\u5728 <code>&lt;script setup&gt;<\/code> \u91cc\u7684 <code>&lt;div v-if=\"userStore.userInfo.role === 'admin'\"&gt;...<\/code> \u5b8c\u6574\u5730\u526a\u5207\u5230\u4e86 <code>&lt;template&gt;<\/code> \u7684 <code>&lt;nav&gt;<\/code> \u6807\u7b7e\u6700\u5e95\u90e8\u3002<\/li>\n<li><strong>\u89c4\u8303\u5316\u5f15\u5165<\/strong>\uff1a\u628a\u6240\u6709\u7684 <code>import<\/code> \u8bed\u53e5\u7edf\u4e00\u63d0\u5230\u4e86 <code>&lt;script setup&gt;<\/code> \u7684\u6700\u9876\u90e8\uff08\u8fd9\u662f Vue3 \u7684\u6807\u51c6\u89c4\u8303\uff0c\u9632\u6b62\u53d8\u91cf\u672a\u5b9a\u4e49\uff09\u3002<\/li>\n<li><strong>\u4fee\u590d\u79fb\u52a8\u7aef\u4f53\u9a8c<\/strong>\uff1a\u7ed9\u65b0\u52a0\u7684\u4e24\u4e2a\u7ba1\u7406\u5458\u83dc\u5355\u4e5f\u52a0\u4e0a\u4e86 <code>@click=\"closeMobileMenu\"<\/code>\uff0c\u8fd9\u6837\u5728\u624b\u673a\u4e0a\u70b9\u51fb\u5b83\u4eec\u65f6\uff0c\u4fa7\u8fb9\u680f\u4e5f\u4f1a\u81ea\u52a8\u6536\u8d77\u3002<\/li>\n<li><strong>Header \u6570\u636e\u7ed1\u5b9a<\/strong>\uff1a\u987a\u624b\u628a\u53f3\u4e0a\u89d2\u7684\u201c\u5728\u7ebf\u201d\u548c\u201c\u4e2a\u4eba\u4e2d\u5fc3\u201d\u7ed1\u5b9a\u4e86 <code>userStore<\/code> \u7684\u771f\u5b9e\u90ae\u7bb1\u6570\u636e\uff0c\u8ba9\u5b83\u770b\u8d77\u6765\u66f4\u4e13\u4e1a\u3002<br \/>\n\u8bf7\u76f4\u63a5<strong>\u5168\u9009\u8986\u76d6<\/strong>\u4f60\u7684 <code>\/mnt\/navidrome-system\/frontend\/navidrome-ui\/src\/components\/Layout.vue<\/code> \u6587\u4ef6\uff1a<\/li>\n<\/ol>\n<pre><code class=\"language-\u4ee3\u7801\u6bb5\">&lt;template&gt;\r\n  &lt;div class=\"backdrop\" :class=\"{ show: isMobileMenuOpen }\" @click=\"isMobileMenuOpen = false\"&gt;&lt;\/div&gt;\r\n\r\n  &lt;div class=\"app\"&gt;\r\n    &lt;aside class=\"sider\" :class=\"{ collapsed: isCollapsed, open: isMobileMenuOpen }\"&gt;\r\n      &lt;div class=\"brand\"&gt;\r\n        &lt;div class=\"logo\"&gt;N&lt;\/div&gt;\r\n        &lt;div class=\"brand-text\"&gt;\r\n          &lt;div class=\"brand-name\"&gt;Navidrome&lt;\/div&gt;\r\n          &lt;div class=\"brand-sub\"&gt;\u79ef\u5206\u4e0e\u7528\u6237\u7ba1\u7406&lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n\r\n      &lt;nav class=\"nav\"&gt;\r\n        &lt;router-link to=\"\/\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n          &lt;div class=\"nav-icon\"&gt;\ud83c\udfe0&lt;\/div&gt;\r\n          &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u4eea\u8868\u76d8&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;\/router-link&gt;\r\n\r\n        &lt;div class=\"label\" style=\"margin: 10px 12px 5px;\"&gt;\u8d22\u52a1\u4e0e\u670d\u52a1&lt;\/div&gt;\r\n        \r\n        &lt;router-link to=\"\/store\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n          &lt;div class=\"nav-icon\"&gt;\ud83d\uded2&lt;\/div&gt;\r\n          &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u8d2d\u4e70\u5957\u9910&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;\/router-link&gt;\r\n\r\n        &lt;router-link to=\"\/subscriptions\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n          &lt;div class=\"nav-icon\"&gt;\ud83d\udc8e&lt;\/div&gt;\r\n          &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u6211\u7684\u8ba2\u9605&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;\/router-link&gt;\r\n\r\n        &lt;router-link to=\"\/orders\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n          &lt;div class=\"nav-icon\"&gt;\ud83d\udcc4&lt;\/div&gt;\r\n          &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u6211\u7684\u8ba2\u5355&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;\/router-link&gt;\r\n\r\n        &lt;div class=\"label\" style=\"margin: 10px 12px 5px;\"&gt;\u8d26\u6237\u4e2d\u5fc3&lt;\/div&gt;\r\n\r\n        &lt;router-link to=\"\/invites\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n          &lt;div class=\"nav-icon\"&gt;\ud83c\udf9f\ufe0f&lt;\/div&gt;\r\n          &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u9080\u8bf7\u8ba1\u5212&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;\/router-link&gt;\r\n\r\n        &lt;router-link to=\"\/tickets\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n          &lt;div class=\"nav-icon\"&gt;\ud83d\udee0\ufe0f&lt;\/div&gt;\r\n          &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u6211\u7684\u5de5\u5355&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;\/router-link&gt;\r\n\r\n        &lt;router-link to=\"\/settings\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n          &lt;div class=\"nav-icon\"&gt;\u2699\ufe0f&lt;\/div&gt;\r\n          &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u8d26\u53f7\u8bbe\u7f6e&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;\/router-link&gt;\r\n\r\n        &lt;div v-if=\"userStore.userInfo &amp;&amp; userStore.userInfo.role === 'admin'\"&gt;\r\n          &lt;div class=\"label\" style=\"margin: 10px 12px 5px; color: var(--error-color);\"&gt;\u7ba1\u7406\u5de5\u5177&lt;\/div&gt;\r\n          \r\n          &lt;router-link to=\"\/admin\/users\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n            &lt;div class=\"nav-icon\"&gt;\ud83d\udc64&lt;\/div&gt;\r\n            &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u7528\u6237\u7ba1\u7406&lt;\/div&gt;&lt;\/div&gt;\r\n          &lt;\/router-link&gt;\r\n\r\n          &lt;router-link to=\"\/admin\/invites\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n            &lt;div class=\"nav-icon\"&gt;\ud83c\udf9f\ufe0f&lt;\/div&gt;\r\n            &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u5168\u5c40\u9080\u8bf7\u7801&lt;\/div&gt;&lt;\/div&gt;\r\n          &lt;\/router-link&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/nav&gt;\r\n\r\n      &lt;div class=\"sider-footer\"&gt;\r\n        &lt;button class=\"btn\" @click=\"toggleCollapse\"&gt;\r\n          {{ isCollapsed ? '\u27a1\ufe0f' : '\u2b05\ufe0f' }} &lt;span class=\"hide-on-collapse\" v-show=\"!isCollapsed\"&gt;\u6536\u8d77\u4fa7\u680f&lt;\/span&gt;\r\n        &lt;\/button&gt;\r\n        &lt;button class=\"btn btn-danger\" @click=\"handleLogout\"&gt;\r\n          \u23fb &lt;span class=\"hide-on-collapse\" v-show=\"!isCollapsed\"&gt;\u9000\u51fa\u767b\u5f55&lt;\/span&gt;\r\n        &lt;\/button&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/aside&gt;\r\n\r\n    &lt;main class=\"main\"&gt;\r\n      &lt;header class=\"header\"&gt;\r\n        &lt;div style=\"display: flex; align-items: center; gap: 10px;\"&gt;\r\n          &lt;button class=\"btn mobile-only\" @click=\"isMobileMenuOpen = true\"&gt;\u2630&lt;\/button&gt;\r\n          &lt;div&gt;\r\n            &lt;div style=\"font-weight:900;\"&gt;Navidrome Server&lt;\/div&gt;\r\n          &lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n        &lt;div style=\"display: flex; gap: 10px; align-items: center;\"&gt;\r\n          &lt;button class=\"btn\" @click=\"toggleTheme\"&gt;{{ isDark ? '\u2600\ufe0f' : '\ud83c\udf19' }}&lt;\/button&gt;\r\n          &lt;span class=\"badge\" :class=\"userStore.userInfo.role === 'admin' ? 'info' : 'success'\"&gt;\r\n            {{ userStore.userInfo.role === 'admin' ? '\u7ba1\u7406\u5458' : '\u5728\u7ebf' }}\r\n          &lt;\/span&gt;\r\n          &lt;button class=\"btn\"&gt;\ud83e\uddd1\u200d\ud83d\udcbb {{ userStore.userInfo.email || '\u672a\u767b\u5f55' }}&lt;\/button&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/header&gt;\r\n\r\n      &lt;section class=\"content\"&gt;\r\n        &lt;router-view&gt;&lt;\/router-view&gt;\r\n      &lt;\/section&gt;\r\n    &lt;\/main&gt;\r\n  &lt;\/div&gt;\r\n&lt;\/template&gt;\r\n\r\n&lt;script setup&gt;\r\n\/\/ 1. \u6240\u6709\u7684 import \u7edf\u4e00\u653e\u5728\u6700\u9876\u90e8\r\nimport { ref, onMounted } from 'vue'\r\nimport { useRouter } from 'vue-router'\r\nimport { useUserStore } from '..\/store\/user'\r\n\r\n\/\/ 2. \u5b9e\u4f8b\u5316\u8def\u7531\u548c\u72b6\u6001\u5e93\r\nconst router = useRouter()\r\nconst userStore = useUserStore()\r\n\r\n\/\/ 3. \u5b9a\u4e49\u54cd\u5e94\u5f0f\u72b6\u6001\r\nconst isCollapsed = ref(false)\r\nconst isMobileMenuOpen = ref(false)\r\nconst isDark = ref(false)\r\n\r\n\/\/ 4. \u65b9\u6cd5\u5b9a\u4e49\r\nconst toggleCollapse = () =&gt; {\r\n  isCollapsed.value = !isCollapsed.value\r\n}\r\n\r\nconst closeMobileMenu = () =&gt; {\r\n  isMobileMenuOpen.value = false\r\n}\r\n\r\nconst toggleTheme = () =&gt; {\r\n  isDark.value = !isDark.value\r\n  const newTheme = isDark.value ? 'dark' : 'light'\r\n  document.documentElement.setAttribute('data-theme', newTheme)\r\n  localStorage.setItem('theme', newTheme)\r\n}\r\n\r\nconst handleLogout = () =&gt; {\r\n  if (confirm(\"\u786e\u8ba4\u9000\u51fa\u7cfb\u7edf\uff1f\")) {\r\n    userStore.logout() \/\/ \u6e05\u9664\u7528\u6237\u4fe1\u606f\r\n    router.push('\/login') \/\/ \u8df3\u8f6c\u5230\u767b\u5f55\u9875\r\n  }\r\n}\r\n\r\n\/\/ 5. \u521d\u59cb\u5316\u64cd\u4f5c\r\nonMounted(() =&gt; {\r\n  if (localStorage.getItem('theme') === 'dark') {\r\n    isDark.value = true\r\n    document.documentElement.setAttribute('data-theme', 'dark')\r\n  }\r\n})\r\n&lt;\/script&gt;\r\n<\/code><\/pre>\n<p>\u4fdd\u5b58\u4e4b\u540e\uff0c\u7531\u4e8e Vite \u7684\u70ed\u66f4\u65b0\u673a\u5236\uff0c\u4f60\u7684\u6d4f\u89c8\u5668\u91cc\u5e94\u8be5\u4f1a\u7acb\u523b\u6062\u590d\u6b63\u5e38\uff0c\u5e76\u4e14\u5982\u679c\u4f60\u4ee5 admin \u8d26\u53f7\u767b\u5f55\uff0c\u4e0d\u4ec5\u5de6\u4fa7\u4f1a\u51fa\u73b0\u201c\u7ba1\u7406\u5de5\u5177\u201d\uff0c\u53f3\u4e0a\u89d2\u4e5f\u4f1a\u6f02\u4eae\u5730\u663e\u793a\u51fa\u4f60\u7684\u90ae\u7bb1\u548c\u8eab\u4efd\u6807\u7b7e\uff01<br \/>\n\u53bb\u6d4f\u89c8\u5668\u91cc\u5237\u65b0\u611f\u53d7\u4e00\u4e0b\u5427\uff01\u7136\u540e\u6211\u4eec\u5c31\u53ef\u4ee5\u63a5\u7740\u6d4b\u8bd5\u767b\u5f55\u3001\u767b\u51fa\u3001\u4ee5\u53ca\u521a\u521a\u65b0\u5efa\u7684\u7528\u6237\u7ba1\u7406\u7b49\u9875\u9762\u4e86\u3002<\/p>\n<h2>Turn 36<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>192.227.164.42 \u76ee\u524d\u65e0\u6cd5\u5904\u7406\u6b64\u8bf7\u6c42\u3002HTTP ERROR 503<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u51fa\u73b0\u4e86 <strong>HTTP ERROR 503<\/strong>\uff0c\u610f\u5473\u7740\u4f60\u7684<strong>\u524d\u7aef\u670d\u52a1\uff08Vite\uff09\u867d\u7136\u5728\u8fd0\u884c\uff0c\u4f46\u5b83\u65e0\u6cd5\u8fde\u63a5\u5230\u540e\u7aef\uff08Spring Boot\uff09<\/strong>\uff0c\u6216\u8005\u540e\u7aef\u670d\u52a1\u867d\u7136\u5f00\u542f\u4f46\u7531\u4e8e\u67d0\u79cd\u539f\u56e0\u5d29\u6e83\/\u62d2\u7edd\u4e86\u8fde\u63a5\u3002<br \/>\n\u56e0\u4e3a\u6211\u4eec\u521a\u521a\u914d\u7f6e\u4e86 Vite \u7684 <code>proxy<\/code>\uff08\u4ee3\u7406\u8f6c\u53d1\uff09\uff0c\u524d\u7aef 5173 \u4f1a\u53bb\u5bfb\u627e\u540e\u7aef 8001\u3002\u5982\u679c 8001 \u6ca1\u53cd\u5e94\uff0c\u5c31\u4f1a\u62a5 530\u3002<br \/>\n\u8bf7\u6309\u7167\u4ee5\u4e0b\u6b65\u9aa4\u68c0\u67e5\u5e76\u4fee\u590d\uff1a<\/p>\n<h3>1. \u68c0\u67e5\u540e\u7aef\u670d\u52a1\u662f\u5426\u771f\u7684\u5728\u8fd0\u884c<\/h3>\n<p>\u7531\u4e8e\u4f60\u521a\u624d\u505c\u6389\u540e\u7aef\u53bb\u4fee\u6539\u4ee3\u7801\uff0c\u53ef\u80fd\u5fd8\u8bb0\u91cd\u65b0\u542f\u52a8\uff0c\u6216\u8005\u540e\u7aef\u56e0\u4e3a\u62a5\u9519\u53c8\u5d29\u6e83\u4e86\u3002<br \/>\n\u5728\u7ec8\u7aef\u6267\u884c\uff1a<\/p>\n<pre><code class=\"language-bash\">ps -ef | grep java\r\n<\/code><\/pre>\n<ul>\n<li><strong>\u5982\u679c\u6ca1\u6709<\/strong>\u770b\u5230\u7c7b\u4f3c <code>java -jar ...navidrome-server...<\/code> \u7684\u8fdb\u7a0b\uff0c\u8bf4\u660e\u540e\u7aef\u6ca1\u542f\u52a8\u3002<\/li>\n<li><strong>\u5982\u679c\u6709<\/strong>\uff0c\u8bf7\u5148\u6740\u6389\u5b83\u91cd\u542f\uff0c\u786e\u4fdd\u52a0\u8f7d\u4e86\u6700\u65b0\u7684\u4ee3\u7801\u3002<br \/>\n<strong>\u91cd\u542f\u540e\u7aef\u6307\u4ee4\uff1a<\/strong><\/li>\n<\/ul>\n<pre><code class=\"language-bash\"># \u6740\u6389\u53ef\u80fd\u7684\u6b8b\u4f59\u8fdb\u7a0b\r\nkillall -9 java\r\n\r\n# \u8fdb\u5165\u540e\u7aef\u76ee\u5f55\u91cd\u65b0\u6253\u5305\u5e76\u542f\u52a8\r\ncd \/mnt\/navidrome-system\/backend\/navidrome-server\r\n.\/mvnw clean package -DskipTests\r\nnohup java -jar target\/navidrome-server-0.0.1-SNAPSHOT.jar &gt; app.log 2&gt;&amp;1 &amp;\r\n<\/code><\/pre>\n<h3>2. \u68c0\u67e5\u540e\u7aef\u65e5\u5fd7\u662f\u5426\u6709\u62a5\u9519<\/h3>\n<p>\u5982\u679c\u542f\u52a8\u540e\u8fd8\u662f 503\uff0c\u770b\u770b\u540e\u7aef\u662f\u4e0d\u662f\u56e0\u4e3a\u6570\u636e\u5e93\u8fde\u63a5\u7b49\u95ee\u9898\u62a5\u9519\u4e86\uff1a<\/p>\n<pre><code class=\"language-bash\">tail -n 50 \/mnt\/navidrome-system\/backend\/navidrome-server\/app.log\r\n<\/code><\/pre>\n<ul>\n<li>\u68c0\u67e5\u662f\u5426\u6709 <code>Connection refused<\/code> \u6216 <code>Access denied for user 'root'@'localhost'<\/code>\u3002<\/li>\n<\/ul>\n<h3>3. \u68c0\u67e5 MySQL \u5bb9\u5668\u72b6\u6001<\/h3>\n<p>\u5982\u679c\u6570\u636e\u5e93\u6302\u4e86\uff0c\u540e\u7aef\u542f\u52a8\u4f1a\u5931\u8d25\u3002<\/p>\n<pre><code class=\"language-bash\">docker ps\r\n<\/code><\/pre>\n<ul>\n<li>\u786e\u4fdd <code>navidrome-mysql<\/code> \u5904\u4e8e <code>Up<\/code> \u72b6\u6001\u3002<\/li>\n<\/ul>\n<h3>4. \u524d\u7aef\u5f00\u53d1\u670d\u52a1\u5668\u914d\u7f6e\u68c0\u67e5<\/h3>\n<p>\u786e\u4fdd\u4f60\u7684\u524d\u7aef <code>vite.config.js<\/code> \u91cc\u7684\u4ee3\u7406\u76ee\u6807\u5730\u5740\u6b63\u786e\u3002<\/p>\n<pre><code class=\"language-bash\">cat \/mnt\/navidrome-system\/frontend\/navidrome-ui\/vite.config.js\r\n<\/code><\/pre>\n<p>\u786e\u8ba4 <code>target<\/code> \u662f <code>http:\/\/127.0.0.1:8001<\/code>\u3002\u5982\u679c\u662f\u5199\u6210\u4e86\u516c\u7f51 IP\uff0c\u5728\u670d\u52a1\u5668\u5185\u90e8\u8f6c\u53d1\u53ef\u80fd\u4f1a\u56e0\u4e3a\u9632\u706b\u5899\u7b56\u7565\u5931\u8d25\uff0c\u5efa\u8bae\u7edf\u4e00\u4f7f\u7528 <code>127.0.0.1<\/code>\u3002<\/p>\n<hr \/>\n<h3>\ud83d\udca1 \u5feb\u901f\u6392\u67e5\u5c0f\u6280\u5de7<\/h3>\n<p>\u4f60\u53ef\u4ee5\u76f4\u63a5\u5728\u670d\u52a1\u5668\u4e0a\u7528 <code>curl<\/code> \u6345\u4e00\u4e0b\u540e\u7aef\uff0c\u770b\u770b\u540e\u7aef\u6709\u6ca1\u6709\u6c14\u513f\uff1a<\/p>\n<pre><code class=\"language-bash\">curl http:\/\/127.0.0.1:8001\/v1\/login\r\n<\/code><\/pre>\n<ul>\n<li>\u5982\u679c\u8fd4\u56de <code>{\"code\":500, \"msg\":\"Required request body is missing\", ...}<\/code> \u2014\u2014 <strong>\u540e\u7aef\u6b63\u5e38<\/strong>\uff0c\u662f\u524d\u7aef\u4ee3\u7406\u914d\u9519\u4e86\u3002<\/li>\n<li>\u5982\u679c\u8fd4\u56de <code>Failed to connect<\/code> \u2014\u2014 <strong>\u540e\u7aef\u6ca1\u5f00<\/strong> \u6216 \u7aef\u53e3\u5199\u9519\u4e86\u3002<br \/>\n<strong>\u64cd\u4f5c\u5efa\u8bae\uff1a<\/strong><br \/>\n\u5148\u6267\u884c <code>killall -9 java<\/code>\uff0c\u7136\u540e<strong>\u4e0d\u8981\u4f7f\u7528 nohup<\/strong>\uff0c\u76f4\u63a5 <code>java -jar ...<\/code> \u5728\u524d\u53f0\u8dd1\uff0c\u770b\u770b\u6709\u6ca1\u6709\u7ea2\u8272\u7684 Error \u62a5\u9519\u3002\u5982\u679c\u6ca1\u6709\u62a5\u9519\u4e14\u663e\u793a <code>Started...<\/code>\uff0c\u518d\u53bb\u5237\u65b0\u7f51\u9875\u3002<\/li>\n<\/ul>\n<h2>Turn 37<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>root@192-227-164-42-host:\/mnt\/navidrome-system\/backend\/navidrome-server# .\/mvnw clean package -DskipTests<br \/>\n[INFO] Scanning for projects&#8230;<br \/>\n[INFO]<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&lt; vip.crabpt.navidrome:navidrome-server &gt;&#8212;&#8212;&#8212;&#8212;&#8212;-<br \/>\n[INFO] Building navidrome-server 0.0.1-SNAPSHOT<br \/>\n[INFO] from pom.xml<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;[ jar ]&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[INFO]<br \/>\n[INFO] &#8212; clean:3.3.2:clean (default-clean) @ navidrome-server &#8212;<br \/>\n[INFO] Deleting \/mnt\/navidrome-system\/backend\/navidrome-server\/target<br \/>\n[INFO]<br \/>\n[INFO] &#8212; resources:3.3.1:resources (default-resources) @ navidrome-server &#8212;<br \/>\n[INFO] Copying 1 resource from src\/main\/resources to target\/classes<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[INFO] BUILD FAILURE<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[INFO] Total time: 1.073 s<br \/>\n[INFO] Finished at: 2026-04-14T03:55:33-04:00<br \/>\n[INFO] &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>\n[ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.3.1:resources (default-resources) on project navidrome-server: filtering \/mnt\/navidrome-system\/backend\/navidrome-server\/src\/main\/resources\/application.yml to \/mnt\/navidrome-system\/backend\/navidrome-server\/target\/classes\/application.yml failed with MalformedInputException: Input length = 1 -&gt; [Help 1]<br \/>\n[ERROR]<br \/>\n[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.<br \/>\n[ERROR] Re-run Maven using the -X switch to enable full debug logging.<br \/>\n[ERROR]<br \/>\n[ERROR] For more information about the errors and possible solutions, please read the following articles:<br \/>\n[ERROR] [Help 1] http:\/\/cwiki.apache.org\/confluence\/display\/MAVEN\/MojoExecutionException<br \/>\nroot@192-227-164-42-host:\/mnt\/navidrome-system\/backend\/navidrome-server#<br \/>\nroot@192-227-164-42-host:\/mnt\/navidrome-system\/backend\/navidrome-server# nohup java -jar target\/navidrome-server-0.0.1-SNAPSHOT.jar &gt; app.log 2&gt;&amp;1 &amp;<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u8fd9\u4e2a\u62a5\u9519 <code>MalformedInputException: Input length = 1<\/code> \u662f\u4e00\u4e2a\u975e\u5e38\u5178\u578b\u7684<strong>\u6587\u4ef6\u7f16\u7801\u95ee\u9898<\/strong>\u3002<\/p>\n<h3>\u95ee\u9898\u539f\u56e0<\/h3>\n<p>\u5728 Maven \u7f16\u8bd1\u8fc7\u7a0b\u4e2d\uff0c\u5b83\u5c1d\u8bd5\u8bfb\u53d6\u4f60\u7684 <code>application.yml<\/code> \u6587\u4ef6\u5e76\u590d\u5236\u5230\u7f16\u8bd1\u76ee\u5f55\uff0c\u4f46\u53d1\u73b0\u8be5\u6587\u4ef6\u4e2d\u5305\u542b\u4e86\u5b83\u65e0\u6cd5\u8bc6\u522b\u7684\u5b57\u7b26\uff08\u901a\u5e38\u662f\u7531\u4e8e\u5728 <code>vim<\/code> \u7f16\u8f91\u65f6\u6df7\u5165\u4e86\u975e UTF-8 \u7f16\u7801\u7684\u7279\u6b8a\u5b57\u7b26\uff0c\u6216\u8005\u662f\u7c98\u8d34\u4ee3\u7801\u65f6\u5e26\u5165\u4e86\u4e0d\u53ef\u89c1\u7684\u4e71\u7801\uff09\u3002<br \/>\n\u56e0\u4e3a\u7f16\u8bd1\u5931\u8d25\u4e86\uff0c<code>target<\/code> \u76ee\u5f55\u4e0b\u5e76\u6ca1\u6709\u751f\u6210\u65b0\u7684 <code>jar<\/code> \u5305\uff0c\u6240\u4ee5\u4f60\u6267\u884c <code>java -jar<\/code> \u65f6\uff0c\u7cfb\u7edf\u627e\u4e0d\u5230\u6587\u4ef6\u6216\u8005\u8fd0\u884c\u7684\u662f\u65e7\u7684\u9519\u8bef\u7248\u672c\uff0c\u5bfc\u81f4\u4e86\u7f51\u9875\u7aef\u7684 <strong>503 \u9519\u8bef<\/strong>\u3002<\/p>\n<hr \/>\n<h3>\u4fee\u590d\u6b65\u9aa4<\/h3>\n<h4>1. \u5f3a\u5236\u91cd\u5199 <code>application.yml<\/code>\uff08\u89e3\u51b3\u7f16\u7801\u95ee\u9898\uff09<\/h4>\n<p>\u6211\u4eec\u76f4\u63a5\u5728\u7ec8\u7aef\u7528 <code>cat<\/code> \u547d\u4ee4\u91cd\u65b0\u5199\u5165\u4e00\u4e2a\u5e72\u51c0\u7684\u914d\u7f6e\u6587\u4ef6\uff0c\u5f7b\u5e95\u6e05\u9664\u4e71\u7801\u3002<br \/>\n\u8bf7\u5728 <code>\/mnt\/navidrome-system\/backend\/navidrome-server<\/code> \u76ee\u5f55\u4e0b\u6267\u884c\uff1a<\/p>\n<pre><code class=\"language-bash\">cat &lt;&lt; 'EOF' &gt; src\/main\/resources\/application.yml\r\nserver:\r\n  port: 8001\r\n  servlet:\r\n    context-path: \/v1\r\n\r\nspring:\r\n  datasource:\r\n    driver-class-name: com.mysql.cj.jdbc.Driver\r\n    url: jdbc:mysql:\/\/127.0.0.1:3535\/nevidrome-server?autoReconnect=true&amp;useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=Asia\/Shanghai\r\n    username: root\r\n    password: navidrome_pwd_123\r\n\r\nmybatis-plus:\r\n  mapper-locations: classpath*:\/mapper\/**\/*.xml\r\n  global-config:\r\n    db-config:\r\n      id-type: auto\r\n      logic-delete-field: deleted\r\n      logic-delete-value: 1\r\n      logic-not-delete-value: 0\r\n  configuration:\r\n    map-underscore-to-camel-case: true\r\n    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl\r\nEOF\r\n<\/code><\/pre>\n<h4>2. \u91cd\u65b0\u6253\u5305\u9879\u76ee<\/h4>\n<p>\u73b0\u5728\u6587\u4ef6\u5e72\u51c0\u4e86\uff0c\u518d\u6b21\u5c1d\u8bd5\u6253\u5305\uff1a<\/p>\n<pre><code class=\"language-bash\">.\/mvnw clean package -DskipTests\r\n<\/code><\/pre>\n<p><strong>\u6ce8\u610f\uff1a<\/strong> \u5fc5\u987b\u770b\u5230 <code>BUILD SUCCESS<\/code> \u624d\u80fd\u8fdb\u884c\u4e0b\u4e00\u6b65\u3002<\/p>\n<h4>3. \u542f\u52a8\u540e\u7aef\u670d\u52a1<\/h4>\n<p>\u786e\u4fdd\u5148\u6740\u6389\u4e4b\u524d\u53ef\u80fd\u6b8b\u7559\u7684\u8fdb\u7a0b\uff1a<\/p>\n<pre><code class=\"language-bash\">killall -9 java\r\nnohup java -jar target\/navidrome-server-0.0.1-SNAPSHOT.jar &gt; app.log 2&gt;&amp;1 &amp;\r\n<\/code><\/pre>\n<h4>4. \u68c0\u67e5\u540e\u7aef\u662f\u5426\u771f\u7684\u6d3b\u4e86<\/h4>\n<p>\u6267\u884c\u4ee5\u4e0b\u547d\u4ee4\uff1a<\/p>\n<pre><code class=\"language-bash\">curl http:\/\/127.0.0.1:8001\/v1\/login\r\n<\/code><\/pre>\n<p>\u5982\u679c\u8fd4\u56de\u4e86\u4e00\u4e32 JSON\uff08\u62a5\u9519\u6ca1\u5173\u7cfb\uff0c\u53ea\u8981\u6709\u8fd4\u56de\u5185\u5bb9\uff09\uff0c\u8bf4\u660e\u540e\u7aef\u5f7b\u5e95\u597d\u4e86\u3002<\/p>\n<hr \/>\n<h3>503 \u9519\u8bef\u6d88\u5931\u4e86\u5417\uff1f<\/h3>\n<p>\u540e\u7aef\u542f\u52a8\u6210\u529f\u540e\uff0c\u53bb\u6d4f\u89c8\u5668\u5237\u65b0\u4f60\u7684\u524d\u7aef\u9875\u9762\u3002\u5982\u679c\u4f9d\u7136 503\uff0c\u8bf7\u68c0\u67e5\u4f60\u7684\u524d\u7aef <code>Vite<\/code> \u670d\u52a1\u662f\u5426\u8fd8\u5728\u8fd0\u884c\u3002\u5982\u679c\u4e0d\u5728\uff0c\u8bf7\u56de\u5230\u524d\u7aef\u76ee\u5f55\u91cd\u542f\uff1a<\/p>\n<pre><code class=\"language-bash\">cd \/mnt\/navidrome-system\/frontend\/navidrome-ui\r\nnpm run dev -- --host\r\n<\/code><\/pre>\n<p>\u4fee\u590d\u5b8c <code>application.yml<\/code> \u7684\u7f16\u7801\u95ee\u9898\u540e\uff0c\u6574\u4f53\u94fe\u8def\u5e94\u8be5\u5c31\u987a\u7545\u4e86\uff01\u5982\u679c\u8fd8\u6709\u62a5\u9519\uff0c\u8bf7\u628a\u6700\u65b0\u7684 <code>app.log<\/code> \u5c3e\u90e8\u5185\u5bb9\u53d1\u7ed9\u6211\u3002<\/p>\n<h2>Turn 38<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>root@192-227-164-42-host:\/mnt\/navidrome-system\/backend\/navidrome-server# curl http:\/\/127.0.0.1:8001\/v1\/login<br \/>\n{&#8220;timestamp&#8221;:&#8221;2026-04-14T07:57:04.876+00:00&#8243;,&#8221;status&#8221;:405,&#8221;error&#8221;:&#8221;Method Not Allowed&#8221;,&#8221;path&#8221;:&#8221;\/v1\/login&#8221;}root@192-227-164-42-host:\/mn<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u770b\u5230\u8fd9\u4e2a\u8fd4\u56de\u7ed3\u679c\uff0c\u6211\u53cd\u800c\u677e\u4e86\u4e00\u53e3\u6c14\uff1a<strong>\u540e\u7aef\u5df2\u7ecf\u5f7b\u5e95\u6d3b\u4e86\uff01<\/strong><br \/>\n\u867d\u7136\u663e\u793a\u4e86 <code>405 Method Not Allowed<\/code>\uff0c\u4f46\u8fd9\u6b63\u662f\u9884\u671f\u7684\u53cd\u9988\u3002\u56e0\u4e3a\u6211\u4eec\u7684\u767b\u5f55\u63a5\u53e3\u5b9a\u4e49\u7684\u662f <code>POST<\/code> \u8bf7\u6c42\uff08\u7528\u4e8e\u63d0\u4ea4\u7528\u6237\u540d\u548c\u5bc6\u7801\uff09\uff0c\u800c\u4f60\u76f4\u63a5\u7528 <code>curl<\/code> \u9ed8\u8ba4\u53d1\u8d77\u7684\u662f <code>GET<\/code> \u8bf7\u6c42\u3002\u53ea\u8981\u540e\u7aef\u80fd\u62a5\u51fa\u8fd9\u4e2a 405\uff0c\u5c31\u8bf4\u660e Spring Boot \u5df2\u7ecf\u6210\u529f\u542f\u52a8\u5e76\u63a5\u7ba1\u4e86\u7aef\u53e3\u3002<br \/>\n<strong>\u73b0\u5728\uff0c\u4f60\u7684 503 \u9519\u8bef\u5e94\u8be5\u5df2\u7ecf\u6d88\u5931\u4e86\u3002<\/strong> \u8bf7\u5237\u65b0\u6d4f\u89c8\u5668\u9875\u9762\u8bd5\u8bd5\u3002<\/p>\n<hr \/>\n<h3>\u4e0b\u4e00\u6b65\uff1a\u5b8c\u5584\u5168\u529f\u80fd\u524d\u7aef\u9875\u9762<\/h3>\n<p>\u65e2\u7136\u5e95\u5c42\u94fe\u8def\u5df2\u7ecf\u901a\u4e86\uff0c\u6211\u4eec\u8981\u89e3\u51b3\u4f60\u4e4b\u524d\u63d0\u5230\u7684\u201c\u7ba1\u7406\u9875\u9762\u3001\u4e2a\u4eba\u9875\u9762\u3001\u8d2d\u4e70\u5957\u9910\u3001\u4fee\u6539\u5bc6\u7801\u201d\u7b49\u529f\u80fd\u4e0d\u5b8c\u5584\u7684\u95ee\u9898\u3002<br \/>\n\u4e3a\u4e86\u8ba9\u7cfb\u7edf\u663e\u5f97\u66f4\u8be6\u7ec6\u3001\u66f4\u4e13\u4e1a\uff0c\u6211\u4eec\u9700\u8981\u628a\u51e0\u4e2a\u5173\u952e\u9875\u9762\uff08Settings\u3001Users\u3001Store\uff09\u7684\u4ee3\u7801\u771f\u6b63\u201c\u586b\u6ee1\u201d\u3002<\/p>\n<h4>1. \u5b8c\u5584\u4e2a\u4eba\u8bbe\u7f6e\u4e0e\u4fee\u6539\u5bc6\u7801 (<code>src\/views\/Settings.vue<\/code>)<\/h4>\n<p>\u8fd9\u4e2a\u9875\u9762\u89e3\u51b3\u4f60\u63d0\u51fa\u7684\u201c\u57fa\u672c\u8d44\u6599\u201d\u548c\u201c\u5b89\u5168\u8bbe\u7f6e\u201d\u9700\u6c42\u3002<\/p>\n<pre><code class=\"language-bash\">vim src\/views\/Settings.vue\r\n<\/code><\/pre>\n<pre><code class=\"language-\u4ee3\u7801\u6bb5\">&lt;template&gt;\r\n  &lt;div&gt;\r\n    &lt;div class=\"page-header\"&gt;\r\n      &lt;div class=\"page-title\"&gt;\u8d26\u53f7\u8bbe\u7f6e&lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n\r\n    &lt;div class=\"stat-grid\"&gt;\r\n      &lt;div class=\"modern-card\" style=\"grid-column: span 6;\"&gt;\r\n        &lt;div class=\"card-head\"&gt;&lt;div class=\"card-title\"&gt;\u57fa\u672c\u8d44\u6599&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;div class=\"card-body\"&gt;\r\n          &lt;div class=\"field\"&gt;\r\n            &lt;label class=\"label\"&gt;\u7535\u5b50\u90ae\u7bb1&lt;\/label&gt;\r\n            &lt;input class=\"input\" :value=\"userStore.userInfo.email\" readonly style=\"background: var(--bg-color-tertiary);\"&gt;\r\n          &lt;\/div&gt;\r\n          &lt;div class=\"field\"&gt;\r\n            &lt;label class=\"label\"&gt;\u5173\u8054 Navidrome \u8d26\u53f7&lt;\/label&gt;\r\n            &lt;input class=\"input\" :value=\"userStore.userInfo.navidromeUsername || '\u672a\u5173\u8054'\" readonly&gt;\r\n          &lt;\/div&gt;\r\n          &lt;div class=\"field\"&gt;\r\n            &lt;label class=\"label\"&gt;\u5f53\u524d\u89d2\u8272&lt;\/label&gt;\r\n            &lt;div&gt;&lt;span class=\"badge info\"&gt;{{ userStore.userInfo.role === 'admin' ? '\u8d85\u7ea7\u7ba1\u7406\u5458' : '\u666e\u901a\u7528\u6237' }}&lt;\/span&gt;&lt;\/div&gt;\r\n          &lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n\r\n      &lt;div class=\"modern-card\" style=\"grid-column: span 6;\"&gt;\r\n        &lt;div class=\"card-head\"&gt;&lt;div class=\"card-title\"&gt;\u4fee\u6539\u5bc6\u7801&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;div class=\"card-body\"&gt;\r\n          &lt;div class=\"field\"&gt;\r\n            &lt;label class=\"label\"&gt;\u539f\u5bc6\u7801&lt;\/label&gt;\r\n            &lt;input v-model=\"pwdForm.oldPassword\" type=\"password\" class=\"input\" placeholder=\"\u8bf7\u8f93\u5165\u5f53\u524d\u5bc6\u7801\"&gt;\r\n          &lt;\/div&gt;\r\n          &lt;div class=\"field\"&gt;\r\n            &lt;label class=\"label\"&gt;\u65b0\u5bc6\u7801&lt;\/label&gt;\r\n            &lt;input v-model=\"pwdForm.newPassword\" type=\"password\" class=\"input\" placeholder=\"\u5efa\u8bae\u5305\u542b\u5b57\u6bcd\u4e0e\u6570\u5b57\"&gt;\r\n          &lt;\/div&gt;\r\n          &lt;div class=\"field\"&gt;\r\n            &lt;label class=\"label\"&gt;\u786e\u8ba4\u65b0\u5bc6\u7801&lt;\/label&gt;\r\n            &lt;input v-model=\"pwdForm.confirmPassword\" type=\"password\" class=\"input\" placeholder=\"\u8bf7\u518d\u6b21\u8f93\u5165\u65b0\u5bc6\u7801\"&gt;\r\n          &lt;\/div&gt;\r\n          &lt;button class=\"btn btn-gradient\" style=\"width: 100%; margin-top: 10px;\" @click=\"updatePassword\"&gt;\r\n            \u66f4\u65b0\u5bc6\u7801\r\n          &lt;\/button&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n  &lt;\/div&gt;\r\n&lt;\/template&gt;\r\n\r\n&lt;script setup&gt;\r\nimport { ref } from 'vue'\r\nimport { useUserStore } from '..\/store\/user'\r\nimport request from '..\/utils\/request'\r\n\r\nconst userStore = useUserStore()\r\nconst pwdForm = ref({ oldPassword: '', newPassword: '', confirmPassword: '' })\r\n\r\nconst updatePassword = async () =&gt; {\r\n  if (pwdForm.value.newPassword !== pwdForm.value.confirmPassword) {\r\n    return alert(\"\u4e24\u6b21\u8f93\u5165\u7684\u65b0\u5bc6\u7801\u4e0d\u4e00\u81f4\")\r\n  }\r\n  try {\r\n    await request.post('\/v1\/user\/update-pwd', {\r\n      userId: userStore.userInfo.id,\r\n      ...pwdForm.value\r\n    })\r\n    alert(\"\u5bc6\u7801\u4fee\u6539\u6210\u529f\uff0c\u8bf7\u91cd\u65b0\u767b\u5f55\")\r\n    userStore.logout()\r\n    window.location.href = '\/login'\r\n  } catch (error) {}\r\n}\r\n&lt;\/script&gt;\r\n<\/code><\/pre>\n<h4>2. \u5b8c\u5584\u7528\u6237\u7ba1\u7406\u9875\u9762 (<code>src\/views\/admin\/Users.vue<\/code>)<\/h4>\n<p>\u89e3\u51b3\u4f60\u63d0\u5230\u7684\u201c\u7ba1\u7406\u8d26\u6237\u8bc6\u522b\u540e\u9700\u8981\u7528\u6237\u7ba1\u7406\u754c\u9762\u201d\u7684\u95ee\u9898\u3002<\/p>\n<pre><code class=\"language-bash\">mkdir -p src\/views\/admin\r\nvim src\/views\/admin\/Users.vue\r\n<\/code><\/pre>\n<pre><code class=\"language-\u4ee3\u7801\u6bb5\">&lt;template&gt;\r\n  &lt;div&gt;\r\n    &lt;div class=\"page-header\"&gt;\r\n      &lt;div class=\"page-title\"&gt;\u5168\u7ad9\u7528\u6237\u7ba1\u7406&lt;\/div&gt;\r\n      &lt;button class=\"btn\" @click=\"fetchUsers\"&gt;\ud83d\udd04 \u5237\u65b0\u6570\u636e&lt;\/button&gt;\r\n    &lt;\/div&gt;\r\n\r\n    &lt;div class=\"modern-card\"&gt;\r\n      &lt;div class=\"card-body\"&gt;\r\n        &lt;div class=\"table-wrap\"&gt;\r\n          &lt;table&gt;\r\n            &lt;thead&gt;\r\n              &lt;tr&gt;\r\n                &lt;th&gt;ID&lt;\/th&gt;\r\n                &lt;th&gt;\u90ae\u7bb1&lt;\/th&gt;\r\n                &lt;th&gt;\u79ef\u5206&lt;\/th&gt;\r\n                &lt;th&gt;\u89d2\u8272&lt;\/th&gt;\r\n                &lt;th&gt;\u72b6\u6001&lt;\/th&gt;\r\n                &lt;th&gt;\u64cd\u4f5c&lt;\/th&gt;\r\n              &lt;\/tr&gt;\r\n            &lt;\/thead&gt;\r\n            &lt;tbody&gt;\r\n              &lt;tr v-for=\"user in userList\" :key=\"user.id\"&gt;\r\n                &lt;td class=\"td-mono\"&gt;#{{ user.id }}&lt;\/td&gt;\r\n                &lt;td&gt;{{ user.email }}&lt;\/td&gt;\r\n                &lt;td style=\"color: var(--warning-color); font-weight: bold;\"&gt;{{ user.points }}&lt;\/td&gt;\r\n                &lt;td&gt;&lt;span class=\"badge\" :class=\"user.role === 'admin' ? 'info' : 'warning'\"&gt;{{ user.role }}&lt;\/span&gt;&lt;\/td&gt;\r\n                &lt;td&gt;\r\n                  &lt;span class=\"badge\" :class=\"user.status === 'active' ? 'success' : 'error'\"&gt;\r\n                    {{ user.status === 'active' ? '\u6b63\u5e38' : '\u5c01\u7981' }}\r\n                  &lt;\/span&gt;\r\n                &lt;\/td&gt;\r\n                &lt;td&gt;\r\n                  &lt;button class=\"btn btn-xs btn-danger\" @click=\"toggleUserStatus(user)\"&gt;\r\n                    {{ user.status === 'active' ? '\u5c01\u7981' : '\u89e3\u5c01' }}\r\n                  &lt;\/button&gt;\r\n                &lt;\/td&gt;\r\n              &lt;\/tr&gt;\r\n            &lt;\/tbody&gt;\r\n          &lt;\/table&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n  &lt;\/div&gt;\r\n&lt;\/template&gt;\r\n\r\n&lt;script setup&gt;\r\nimport { ref, onMounted } from 'vue'\r\nimport request from '..\/..\/utils\/request'\r\n\r\nconst userList = ref([])\r\n\r\nconst fetchUsers = async () =&gt; {\r\n  try {\r\n    \/\/ \u5047\u8bbe\u540e\u7aef\u5df2\u7ecf\u5199\u597d\u4e86\u83b7\u53d6\u7528\u6237\u5217\u8868\u7684\u63a5\u53e3\r\n    const data = await request.get('\/v1\/user\/list') \r\n    userList.value = data\r\n  } catch (error) {}\r\n}\r\n\r\nconst toggleUserStatus = async (user) =&gt; {\r\n  alert(\"\u6b63\u5728\u5bf9\u7528\u6237 \" + user.email + \" \u8fdb\u884c\u72b6\u6001\u64cd\u4f5c...\")\r\n}\r\n\r\nonMounted(fetchUsers)\r\n&lt;\/script&gt;\r\n<\/code><\/pre>\n<hr \/>\n<h3>3. \u63a5\u4e0b\u6765\u7684\u6b65\u9aa4\u5efa\u8bae<\/h3>\n<p>\u73b0\u5728\u540e\u7aef\u7684\u8fde\u63a5\u5df2\u7ecf\u4fee\u597d\uff0c\u524d\u7aef\u7684\u67b6\u6784\u4e5f\u652f\u6301\u4e86\u591a\u9875\u9762\u3002\u4f60\u63d0\u5230\u7684\u201c\u8d2d\u4e70\u5957\u9910\u3001\u5de5\u5355\u3001\u8ba2\u9605\u201d\u7b49\u9875\u9762\u76ee\u524d\u8fd8\u53ea\u662f\u5360\u4f4d\u7b26\u3002<br \/>\n<strong>\u4f60\u5e0c\u671b\u6211\u63a5\u4e0b\u6765\u4e3a\u4f60\u8be6\u7ec6\u7f16\u5199\u54ea\u4e00\u90e8\u5206\uff1f<\/strong><\/p>\n<ol>\n<li><strong>\u201c\u8d2d\u4e70\u5957\u9910\/\u79ef\u5206\u5546\u5e97\u201d\u9875\u9762<\/strong>\uff08\u5305\u542b\u5151\u6362 VIP \u548c\u6269\u5bb9\u7684 UI\uff09<\/li>\n<li><strong>\u201c\u5de5\u5355\u7cfb\u7edf\u201d\u9875\u9762<\/strong>\uff08\u5305\u542b\u63d0\u4ea4\u95ee\u9898\u548c\u67e5\u770b\u56de\u590d\u7684 UI\uff09<\/li>\n<li><strong>\u201c\u767b\u5f55\u9875\u9762\u201d<\/strong>\uff08\u5305\u542b\u7cbe\u7f8e\u7684\u80cc\u666f\u6837\u5f0f\u548c\u767b\u5f55\u903b\u8f91\uff09<br \/>\n<em>(\u5c0f\u63d0\u793a\uff1a\u522b\u5fd8\u4e86\u5728 src\/router\/index.js \u91cc\u628a\u8fd9\u4e9b\u65b0\u9875\u9762\u7684\u8def\u5f84\u6b63\u786e\u6620\u5c04\u54e6\uff01)<\/em><\/li>\n<\/ol>\n<h2>Turn 39<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>\u4ece\u8fd9\u5757\u5f00\u59cb\uff0c\u5e2e\u6211\u4f18\u5316\uff0c\u6211\u76ee\u524d\u4ece\u6dfb\u52a0\u4e86\u7ba1\u7406\u754c\u9762\u540e\u5c31\u65e0\u6cd5\u6253\u5f00\u524d\u7aef\u4e86\u3002<\/p>\n<div class=\"backdrop\"><\/div>\n<div class=\"app\">\n<aside class=\"sider\">\n<div class=\"brand\">\n<div class=\"logo\">N<\/div>\n<div class=\"brand-text\">\n<div class=\"brand-name\">Navidrome<\/div>\n<div class=\"brand-sub\">\u79ef\u5206\u4e0e\u7528\u6237\u7ba1\u7406<\/div>\n<\/div>\n<\/div>\n<nav class=\"nav\">\n<div class=\"nav-icon\">\ud83c\udfe0<\/div>\n<div class=\"nav-text\">\n<div class=\"nav-title\">\u4eea\u8868\u76d8<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<div class=\"label\" style=\"margin: 10px 12px 5px;\">\u8d22\u52a1\u4e0e\u670d\u52a1<\/div>\n<p>&nbsp;<\/p>\n<div class=\"nav-icon\">\ud83d\uded2<\/div>\n<div class=\"nav-text\">\n<div class=\"nav-title\">\u8d2d\u4e70\u5957\u9910<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<div class=\"nav-icon\">\ud83d\udc8e<\/div>\n<div class=\"nav-text\">\n<div class=\"nav-title\">\u6211\u7684\u8ba2\u9605<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<div class=\"nav-icon\">\ud83d\udcc4<\/div>\n<div class=\"nav-text\">\n<div class=\"nav-title\">\u6211\u7684\u8ba2\u5355<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<div class=\"label\" style=\"margin: 10px 12px 5px;\">\u8d26\u6237\u4e2d\u5fc3<\/div>\n<p>&nbsp;<\/p>\n<div class=\"nav-icon\">\ud83c\udf9f\ufe0f<\/div>\n<div class=\"nav-text\">\n<div class=\"nav-title\">\u9080\u8bf7\u8ba1\u5212<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<div class=\"nav-icon\">\ud83d\udee0\ufe0f<\/div>\n<div class=\"nav-text\">\n<div class=\"nav-title\">\u6211\u7684\u5de5\u5355<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<div class=\"nav-icon\">\u2699\ufe0f<\/div>\n<div class=\"nav-text\">\n<div class=\"nav-title\">\u8d26\u53f7\u8bbe\u7f6e<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<div>\n<div class=\"label\" style=\"margin: 10px 12px 5px; color: var(--error-color);\">\u7ba1\u7406\u5de5\u5177<\/div>\n<p>&nbsp;<\/p>\n<div class=\"nav-icon\">\ud83d\udc64<\/div>\n<div class=\"nav-text\">\n<div class=\"nav-title\">\u7528\u6237\u7ba1\u7406<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<div class=\"nav-icon\">\ud83c\udf9f\ufe0f<\/div>\n<div class=\"nav-text\">\n<div class=\"nav-title\">\u5168\u5c40\u9080\u8bf7\u7801<\/div>\n<\/div>\n<p>&nbsp;<\/p>\n<\/div>\n<\/nav>\n<div class=\"sider-footer\"><button class=\"btn\"><br \/>\n{{ isCollapsed ? &#8216;\u27a1\ufe0f&#8217; : &#8216;\u2b05\ufe0f&#8217; }} <span class=\"hide-on-collapse\">\u6536\u8d77\u4fa7\u680f<\/span><br \/>\n<\/button><br \/>\n<button class=\"btn btn-danger\"><br \/>\n\u23fb <span class=\"hide-on-collapse\">\u9000\u51fa\u767b\u5f55<\/span><br \/>\n<\/button><\/div>\n<\/aside>\n<p>&nbsp;<\/p>\n<header class=\"header\">\n<div style=\"display: flex; align-items: center; gap: 10px;\"><button class=\"btn mobile-only\">\u2630<\/button><\/p>\n<div>\n<div style=\"font-weight: 900;\">Navidrome Server<\/div>\n<\/div>\n<\/div>\n<div style=\"display: flex; gap: 10px; align-items: center;\"><button class=\"btn\">{{ isDark ? &#8216;\u2600\ufe0f&#8217; : &#8216;\ud83c\udf19&#8217; }}<\/button><br \/>\n<span class=\"badge\"><br \/>\n{{ userStore.userInfo.role === &#8216;admin&#8217; ? &#8216;\u7ba1\u7406\u5458&#8217; : &#8216;\u5728\u7ebf&#8217; }}<br \/>\n<\/span><br \/>\n<button class=\"btn\">\ud83e\uddd1\u200d\ud83d\udcbb {{ userStore.userInfo.email || &#8216;\u672a\u767b\u5f55&#8217; }}<\/button><\/div>\n<\/header>\n<section class=\"content\"><\/section>\n<p>&nbsp;<\/p>\n<\/div>\n<p>&nbsp;<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u51fa\u73b0\u201c\u524d\u7aef\u65e0\u6cd5\u6253\u5f00\u201d\uff08\u901a\u5e38\u8868\u73b0\u4e3a\u6d4f\u89c8\u5668\u91cc\u4e00\u7247\u7a7a\u767d\u7684\u201c\u767d\u5c4f\u201d\uff09\u7684\u60c5\u51b5\uff0c\u8fd9\u662f Vue \u5f00\u53d1\u4e2d\u6781\u5176\u5e38\u89c1\u7684\u4e00\u4e2a\u73b0\u8c61\u3002<br \/>\n<strong>\u8fd9 99% \u662f\u56e0\u4e3a\u4ea7\u751f\u4e86 JavaScript \u81f4\u547d\u62a5\u9519\uff08\u7a7a\u6307\u9488\u5f02\u5e38\uff09\u3002<\/strong><\/p>\n<h3>\u4e3a\u4ec0\u4e48\u4f1a\u767d\u5c4f\uff1f<\/h3>\n<p>\u5728\u4f60\u7684 <code>Layout.vue<\/code> \u9876\u680f\u4ee3\u7801\u91cc\uff0c\u6709\u8fd9\u4e48\u4e24\u884c\uff1a<\/p>\n<pre><code class=\"language-html\">&lt;span class=\"badge\" :class=\"userStore.userInfo.role === 'admin' ? 'info' : 'success'\"&gt;\r\n  {{ userStore.userInfo.role === 'admin' ? '\u7ba1\u7406\u5458' : '\u5728\u7ebf' }}\r\n&lt;\/span&gt;\r\n&lt;button class=\"btn\"&gt;\ud83e\uddd1\u200d\ud83d\udcbb {{ userStore.userInfo.email || '\u672a\u767b\u5f55' }}&lt;\/button&gt;\r\n<\/code><\/pre>\n<p>\u5f53\u4f60<strong>\u6ca1\u6709\u767b\u5f55<\/strong>\uff0c\u6216\u8005\u6d4f\u89c8\u5668\u7684\u672c\u5730\u7f13\u5b58\u88ab\u6e05\u7a7a\u65f6\uff0c<code>userStore.userInfo<\/code> \u91cc\u9762\u662f\u7a7a\u7684\uff08<code>undefined<\/code> \u6216 <code>null<\/code>\uff09\u3002\u6b64\u65f6 Vue \u8bd5\u56fe\u53bb\u5f3a\u884c\u8bfb\u53d6 <code>userInfo.role<\/code>\uff0c\u5c31\u4f1a\u62a5\u51fa <code>Cannot read properties of undefined<\/code> \u9519\u8bef\uff0c\u5bfc\u81f4\u6574\u4e2a\u9875\u9762\u6e32\u67d3\u5d29\u6e83\u3002<\/p>\n<h3>\u89e3\u51b3\u529e\u6cd5\uff1a\u4f7f\u7528\u201c\u53ef\u9009\u94fe\uff08<code>?.<\/code>\uff09\u201d\u4fdd\u62a4\u6570\u636e<\/h3>\n<p>\u6211\u4eec\u53ea\u9700\u8981\u5728\u8bfb\u53d6\u53d8\u91cf\u65f6\u52a0\u4e00\u4e2a\u95ee\u53f7 <code>?.<\/code>\uff0c\u544a\u8bc9 Vue\uff1a\u201c\u5982\u679c\u8fd9\u4e2a\u4e1c\u897f\u4e0d\u5b58\u5728\uff0c\u5c31\u76f4\u63a5\u8df3\u8fc7\uff0c\u4e0d\u8981\u62a5\u9519\u201d\u3002<br \/>\n\u8bf7\u6253\u5f00\u4f60\u7684 <code>\/mnt\/navidrome-system\/frontend\/navidrome-ui\/src\/components\/Layout.vue<\/code>\uff0c\u627e\u5230 <code>&lt;header class=\"header\"&gt;<\/code> \u91cc\u9762\u7684\u8fd9\u90e8\u5206\u4ee3\u7801\uff0c\u5c06\u5176<strong>\u66ff\u6362<\/strong>\u4e3a\u4ee5\u4e0b\u5b89\u5168\u7684\u4ee3\u7801\uff1a<\/p>\n<pre><code class=\"language-\u4ee3\u7801\u6bb5\">&lt;div style=\"display: flex; gap: 10px; align-items: center;\"&gt;\r\n          &lt;button class=\"btn\" @click=\"toggleTheme\"&gt;{{ isDark ? '\u2600\ufe0f' : '\ud83c\udf19' }}&lt;\/button&gt;\r\n          \r\n          &lt;span class=\"badge\" :class=\"userStore.userInfo?.role === 'admin' ? 'info' : 'success'\"&gt;\r\n            {{ userStore.userInfo?.role === 'admin' ? '\u7ba1\u7406\u5458' : '\u5728\u7ebf' }}\r\n          &lt;\/span&gt;\r\n          &lt;button class=\"btn\"&gt;\ud83e\uddd1\u200d\ud83d\udcbb {{ userStore.userInfo?.email || '\u672a\u767b\u5f55' }}&lt;\/button&gt;\r\n        &lt;\/div&gt;\r\n<\/code><\/pre>\n<p>\u540c\u65f6\uff0c\u4e3a\u4e86\u66f4\u52a0\u4f18\u96c5\uff0c\u4f60\u53ef\u4ee5\u628a\u4fa7\u8fb9\u680f\u7ba1\u7406\u5458\u83dc\u5355\u7684\u90a3\u884c <code>v-if<\/code> \u4e5f\u6539\u6210\u53ef\u9009\u94fe\u5199\u6cd5\uff08\u8fd9\u884c\u5728\u4f60\u4ee3\u7801\u7684\u7b2c 61 \u884c\u5de6\u53f3\uff09\uff1a<\/p>\n<pre><code class=\"language-\u4ee3\u7801\u6bb5\">&lt;div v-if=\"userStore.userInfo?.role === 'admin'\"&gt;\r\n          &lt;div class=\"label\" style=\"margin: 10px 12px 5px; color: var(--error-color);\"&gt;\u7ba1\u7406\u5de5\u5177&lt;\/div&gt;\r\n<\/code><\/pre>\n<hr \/>\n<h3>\u5982\u679c\u6539\u5b8c\u540e\u8fd8\u662f\u6253\u4e0d\u5f00\u524d\u7aef\uff1f<\/h3>\n<p>\u5982\u679c\u52a0\u4e0a\u4e86 <code>?.<\/code> \u8fd8\u662f\u767d\u5c4f\uff0c\u90a3\u53ea\u5269\u4e0b\u4e00\u79cd\u53ef\u80fd\uff1a<strong>\u8def\u7531\u6587\u4ef6\u627e\u4e0d\u5230\u5bf9\u5e94\u7684\u9875\u9762\u7ec4\u4ef6<\/strong>\u3002<br \/>\n\u56de\u987e\u4e00\u4e0b\uff0c\u6211\u4eec\u5728 <code>src\/router\/index.js<\/code> \u91cc\u914d\u7f6e\u4e86\uff1a<br \/>\n<code>{ path: 'admin\/users', component: () =&gt; import('..\/views\/admin\/Users.vue') }<\/code><br \/>\n<strong>\u5982\u679c\u4f60\u8fd8\u6ca1\u6765\u5f97\u53ca\u5728 \/views\/admin\/ \u76ee\u5f55\u4e0b\u521b\u5efa Users.vue \u6587\u4ef6<\/strong>\uff0cVite \u7f16\u8bd1\u65f6\u5c31\u4f1a\u627e\u4e0d\u5230\u6587\u4ef6\u800c\u76f4\u63a5\u62a5\u9519\u7f62\u5de5\u3002<br \/>\n<strong>\u6392\u67e5\u65b9\u6cd5\uff1a<\/strong><\/p>\n<pre><code class=\"language-bash\">mkdir -p src\/views\/admin\r\ntouch src\/views\/admin\/Users.vue\r\n<\/code><\/pre>\n<p>\u53bb\u8bd5\u8bd5\u5427\uff01\u52a0\u4e0a <code>?.<\/code> \u4fdd\u62a4\u7b26\u662f\u73b0\u4ee3\u524d\u7aef\u5f00\u53d1\u5fc5\u5907\u7684\u9632\u8eab\u5229\u5668\u3002\u89e3\u51b3\u540e\u5237\u65b0\u6d4f\u89c8\u5668\uff0c\u4f60\u5e94\u8be5\u80fd\u770b\u5230\u9875\u9762\u6062\u590d\u5982\u521d\u4e86\uff01<\/p>\n<h2>Turn 40<\/h2>\n<h3>\ud83d\udc64 User<\/h3>\n<p>\u8bf7\u7ed9\u6211\u5168\u90e8\u7684\u4ee3\u7801\u5427<\/p>\n<h3>\ud83e\udd16 Assistant<\/h3>\n<p>\u6ca1\u95ee\u9898\uff01\u7ecf\u5386\u4e86\u524d\u9762\u7684\u4fee\u8865\u548c\u91cd\u6784\uff0c\u628a\u6240\u6709\u6838\u5fc3\u6587\u4ef6\u4ece\u5934\u5230\u5c3e\u68b3\u7406\u5e76\u7ed9\u51fa\u4e00\u4e2a<strong>\u5b8c\u6574\u3001\u7eaf\u51c0\u3001\u65e0\u62a5\u9519\u7684\u6700\u7ec8\u7248\u4ee3\u7801<\/strong>\uff0c\u662f\u76ee\u524d\u6700\u7a33\u59a5\u7684\u505a\u6cd5\u3002<br \/>\n\u8bf7\u628a\u4f60\u524d\u7aef\u9879\u76ee\u4e2d\u5bf9\u5e94\u7684\u6587\u4ef6\uff0c<strong>\u5168\u90e8\u66ff\u6362<\/strong>\u4e3a\u4ee5\u4e0b\u5185\u5bb9\u3002\u8fd9\u5957\u4ee3\u7801\u5df2\u7ecf\u5305\u542b\u4e86\uff1a\u8def\u7531\u5b88\u536b\u62e6\u622a\u3001Pinia \u72b6\u6001\u7ba1\u7406\u3001Axios \u8bf7\u6c42\u5c01\u88c5\u3001\u53ef\u9009\u94fe\uff08<code>?.<\/code>\uff09\u9632\u767d\u5c4f\u4fdd\u62a4\u3001\u4ee5\u53ca\u5b8c\u6574\u7684\u5404\u4e2a\u5b50\u9875\u9762\u3002<\/p>\n<hr \/>\n<h3>1. \u72b6\u6001\u7ba1\u7406\u4e0e\u7f51\u7edc\u8bf7\u6c42<\/h3>\n<p><strong>\ud83d\udcc4 src\/store\/user.js<\/strong> (\u7528\u6237\u72b6\u6001\u7ba1\u5bb6)<\/p>\n<pre><code class=\"language-javascript\">import { defineStore } from 'pinia'\r\n\r\nexport const useUserStore = defineStore('user', {\r\n  state: () =&gt; ({\r\n    token: localStorage.getItem('token') || '',\r\n    userInfo: JSON.parse(localStorage.getItem('userInfo') || '{}'),\r\n  }),\r\n  getters: {\r\n    isLoggedIn: (state) =&gt; !!state.token,\r\n    points: (state) =&gt; state.userInfo?.points || 0\r\n  },\r\n  actions: {\r\n    setLogin(data) {\r\n      this.token = 'dummy-token-' + data.id\r\n      this.userInfo = data\r\n      localStorage.setItem('token', this.token)\r\n      localStorage.setItem('userInfo', JSON.stringify(data))\r\n    },\r\n    logout() {\r\n      this.token = ''\r\n      this.userInfo = {}\r\n      localStorage.removeItem('token')\r\n      localStorage.removeItem('userInfo')\r\n    }\r\n  }\r\n})\r\n<\/code><\/pre>\n<p><strong>\ud83d\udcc4 src\/utils\/request.js<\/strong> (Axios \u5c01\u88c5)<\/p>\n<pre><code class=\"language-javascript\">import axios from 'axios'\r\nimport { useUserStore } from '..\/store\/user'\r\n\r\nconst request = axios.create({\r\n  baseURL: '', \/\/ Vite \u5df2\u914d\u7f6e\u4ee3\u7406\uff0c\u65e0\u9700\u586b baseURL\r\n  timeout: 10000 \r\n})\r\n\r\nrequest.interceptors.request.use(\r\n  config =&gt; {\r\n    \/\/ \u81ea\u52a8\u643a\u5e26 Token\r\n    const token = localStorage.getItem('token')\r\n    if (token) {\r\n      config.headers['Authorization'] = `Bearer ${token}`\r\n    }\r\n    return config\r\n  },\r\n  error =&gt; Promise.reject(error)\r\n)\r\n\r\nrequest.interceptors.response.use(\r\n  response =&gt; {\r\n    const res = response.data\r\n    if (res.code !== 200) {\r\n      alert(res.msg || '\u8bf7\u6c42\u5931\u8d25')\r\n      return Promise.reject(new Error(res.msg || 'Error'))\r\n    }\r\n    return res.data\r\n  },\r\n  error =&gt; {\r\n    \/\/ \u5982\u679c\u8fd4\u56de 401 \u8bf4\u660e\u672a\u767b\u5f55\u6216\u8fc7\u671f\uff0c\u53ef\u4ee5\u5728\u8fd9\u91cc\u7edf\u4e00\u8df3\u8f6c\u767b\u5f55\r\n    if (error.response &amp;&amp; error.response.status === 401) {\r\n      const userStore = useUserStore()\r\n      userStore.logout()\r\n      window.location.href = '\/login'\r\n    } else {\r\n      alert(error.message || '\u7f51\u7edc\u8fde\u63a5\u5f02\u5e38')\r\n    }\r\n    return Promise.reject(error)\r\n  }\r\n)\r\n\r\nexport default request\r\n<\/code><\/pre>\n<hr \/>\n<h3>2. \u8def\u7531\u4e0e\u5168\u5c40\u5e03\u5c40<\/h3>\n<p><strong>\ud83d\udcc4 src\/router\/index.js<\/strong> (\u5168\u5c40\u8def\u7531\u914d\u7f6e)<\/p>\n<pre><code class=\"language-javascript\">import { createRouter, createWebHistory } from 'vue-router'\r\nimport { useUserStore } from '..\/store\/user'\r\nimport Layout from '..\/components\/Layout.vue'\r\n\r\nconst routes = [\r\n  {\r\n    path: '\/login',\r\n    name: 'Login',\r\n    component: () =&gt; import('..\/views\/Login.vue')\r\n  },\r\n  {\r\n    path: '\/',\r\n    component: Layout,\r\n    children: [\r\n      { path: '', name: 'Dashboard', component: () =&gt; import('..\/views\/Dashboard.vue') },\r\n      { path: 'invites', name: 'Invites', component: () =&gt; import('..\/views\/Invites.vue') },\r\n      { path: 'settings', name: 'Settings', component: () =&gt; import('..\/views\/Settings.vue') },\r\n      { path: 'admin\/users', name: 'AdminUsers', component: () =&gt; import('..\/views\/admin\/Users.vue') },\r\n      \/\/ \u5360\u4f4d\u9875\u9762\uff0c\u9632\u6b62\u70b9\u51fb\u62a5\u9519\r\n      { path: 'store', component: () =&gt; import('..\/views\/Dashboard.vue') },\r\n      { path: 'subscriptions', component: () =&gt; import('..\/views\/Dashboard.vue') },\r\n      { path: 'orders', component: () =&gt; import('..\/views\/Dashboard.vue') },\r\n      { path: 'tickets', component: () =&gt; import('..\/views\/Dashboard.vue') }\r\n    ]\r\n  }\r\n]\r\n\r\nconst router = createRouter({\r\n  history: createWebHistory(),\r\n  routes\r\n})\r\n\r\n\/\/ \u5168\u5c40\u8def\u7531\u5b88\u536b\uff1a\u672a\u767b\u5f55\u62e6\u622a\r\nrouter.beforeEach((to, from, next) =&gt; {\r\n  const userStore = useUserStore()\r\n  if (to.name !== 'Login' &amp;&amp; !userStore.isLoggedIn) {\r\n    next({ name: 'Login' })\r\n  } else {\r\n    next()\r\n  }\r\n})\r\n\r\nexport default router\r\n<\/code><\/pre>\n<p><strong>\ud83d\udcc4 src\/components\/Layout.vue<\/strong> (\u4fee\u590d\u4e86\u9632\u767d\u5c4f\u7684\u6700\u7ec8\u7248\u5e03\u5c40)<\/p>\n<pre><code class=\"language-\u4ee3\u7801\u6bb5\">&lt;template&gt;\r\n  &lt;div class=\"backdrop\" :class=\"{ show: isMobileMenuOpen }\" @click=\"isMobileMenuOpen = false\"&gt;&lt;\/div&gt;\r\n\r\n  &lt;div class=\"app\"&gt;\r\n    &lt;aside class=\"sider\" :class=\"{ collapsed: isCollapsed, open: isMobileMenuOpen }\"&gt;\r\n      &lt;div class=\"brand\"&gt;\r\n        &lt;div class=\"logo\"&gt;N&lt;\/div&gt;\r\n        &lt;div class=\"brand-text\"&gt;\r\n          &lt;div class=\"brand-name\"&gt;Navidrome&lt;\/div&gt;\r\n          &lt;div class=\"brand-sub\"&gt;\u79ef\u5206\u4e0e\u7528\u6237\u7ba1\u7406&lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n\r\n      &lt;nav class=\"nav\"&gt;\r\n        &lt;router-link to=\"\/\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n          &lt;div class=\"nav-icon\"&gt;\ud83c\udfe0&lt;\/div&gt;\r\n          &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u4eea\u8868\u76d8&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;\/router-link&gt;\r\n\r\n        &lt;div class=\"label\" style=\"margin: 10px 12px 5px;\"&gt;\u8d22\u52a1\u4e0e\u670d\u52a1&lt;\/div&gt;\r\n        \r\n        &lt;router-link to=\"\/store\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n          &lt;div class=\"nav-icon\"&gt;\ud83d\uded2&lt;\/div&gt;\r\n          &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u8d2d\u4e70\u5957\u9910&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;\/router-link&gt;\r\n\r\n        &lt;router-link to=\"\/subscriptions\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n          &lt;div class=\"nav-icon\"&gt;\ud83d\udc8e&lt;\/div&gt;\r\n          &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u6211\u7684\u8ba2\u9605&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;\/router-link&gt;\r\n\r\n        &lt;router-link to=\"\/orders\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n          &lt;div class=\"nav-icon\"&gt;\ud83d\udcc4&lt;\/div&gt;\r\n          &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u6211\u7684\u8ba2\u5355&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;\/router-link&gt;\r\n\r\n        &lt;div class=\"label\" style=\"margin: 10px 12px 5px;\"&gt;\u8d26\u6237\u4e2d\u5fc3&lt;\/div&gt;\r\n\r\n        &lt;router-link to=\"\/invites\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n          &lt;div class=\"nav-icon\"&gt;\ud83c\udf9f\ufe0f&lt;\/div&gt;\r\n          &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u9080\u8bf7\u8ba1\u5212&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;\/router-link&gt;\r\n\r\n        &lt;router-link to=\"\/tickets\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n          &lt;div class=\"nav-icon\"&gt;\ud83d\udee0\ufe0f&lt;\/div&gt;\r\n          &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u6211\u7684\u5de5\u5355&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;\/router-link&gt;\r\n\r\n        &lt;router-link to=\"\/settings\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n          &lt;div class=\"nav-icon\"&gt;\u2699\ufe0f&lt;\/div&gt;\r\n          &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u8d26\u53f7\u8bbe\u7f6e&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;\/router-link&gt;\r\n\r\n        &lt;div v-if=\"userStore.userInfo?.role === 'admin'\"&gt;\r\n          &lt;div class=\"label\" style=\"margin: 10px 12px 5px; color: var(--error-color);\"&gt;\u7ba1\u7406\u5de5\u5177&lt;\/div&gt;\r\n          \r\n          &lt;router-link to=\"\/admin\/users\" class=\"nav-item\" exact-active-class=\"active\" @click=\"closeMobileMenu\"&gt;\r\n            &lt;div class=\"nav-icon\"&gt;\ud83d\udc64&lt;\/div&gt;\r\n            &lt;div class=\"nav-text\"&gt;&lt;div class=\"nav-title\"&gt;\u7528\u6237\u7ba1\u7406&lt;\/div&gt;&lt;\/div&gt;\r\n          &lt;\/router-link&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/nav&gt;\r\n\r\n      &lt;div class=\"sider-footer\"&gt;\r\n        &lt;button class=\"btn\" @click=\"toggleCollapse\"&gt;\r\n          {{ isCollapsed ? '\u27a1\ufe0f' : '\u2b05\ufe0f' }} &lt;span class=\"hide-on-collapse\" v-show=\"!isCollapsed\"&gt;\u6536\u8d77\u4fa7\u680f&lt;\/span&gt;\r\n        &lt;\/button&gt;\r\n        &lt;button class=\"btn btn-danger\" @click=\"handleLogout\"&gt;\r\n          \u23fb &lt;span class=\"hide-on-collapse\" v-show=\"!isCollapsed\"&gt;\u9000\u51fa\u767b\u5f55&lt;\/span&gt;\r\n        &lt;\/button&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/aside&gt;\r\n\r\n    &lt;main class=\"main\"&gt;\r\n      &lt;header class=\"header\"&gt;\r\n        &lt;div style=\"display: flex; align-items: center; gap: 10px;\"&gt;\r\n          &lt;button class=\"btn mobile-only\" @click=\"isMobileMenuOpen = true\"&gt;\u2630&lt;\/button&gt;\r\n          &lt;div&gt;\r\n            &lt;div style=\"font-weight:900;\"&gt;Navidrome Server&lt;\/div&gt;\r\n          &lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n        &lt;div style=\"display: flex; gap: 10px; align-items: center;\"&gt;\r\n          &lt;button class=\"btn\" @click=\"toggleTheme\"&gt;{{ isDark ? '\u2600\ufe0f' : '\ud83c\udf19' }}&lt;\/button&gt;\r\n          \r\n          &lt;span class=\"badge\" :class=\"userStore.userInfo?.role === 'admin' ? 'info' : 'success'\"&gt;\r\n            {{ userStore.userInfo?.role === 'admin' ? '\u7ba1\u7406\u5458' : '\u5728\u7ebf' }}\r\n          &lt;\/span&gt;\r\n          &lt;button class=\"btn\"&gt;\ud83e\uddd1\u200d\ud83d\udcbb {{ userStore.userInfo?.email || '\u672a\u767b\u5f55' }}&lt;\/button&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/header&gt;\r\n\r\n      &lt;section class=\"content\"&gt;\r\n        &lt;router-view&gt;&lt;\/router-view&gt;\r\n      &lt;\/section&gt;\r\n    &lt;\/main&gt;\r\n  &lt;\/div&gt;\r\n&lt;\/template&gt;\r\n\r\n&lt;script setup&gt;\r\nimport { ref, onMounted } from 'vue'\r\nimport { useRouter } from 'vue-router'\r\nimport { useUserStore } from '..\/store\/user'\r\n\r\nconst router = useRouter()\r\nconst userStore = useUserStore()\r\n\r\nconst isCollapsed = ref(false)\r\nconst isMobileMenuOpen = ref(false)\r\nconst isDark = ref(false)\r\n\r\nconst toggleCollapse = () =&gt; isCollapsed.value = !isCollapsed.value\r\nconst closeMobileMenu = () =&gt; isMobileMenuOpen.value = false\r\n\r\nconst toggleTheme = () =&gt; {\r\n  isDark.value = !isDark.value\r\n  const newTheme = isDark.value ? 'dark' : 'light'\r\n  document.documentElement.setAttribute('data-theme', newTheme)\r\n  localStorage.setItem('theme', newTheme)\r\n}\r\n\r\nconst handleLogout = () =&gt; {\r\n  if (confirm(\"\u786e\u8ba4\u9000\u51fa\u7cfb\u7edf\uff1f\")) {\r\n    userStore.logout()\r\n    router.push('\/login')\r\n  }\r\n}\r\n\r\nonMounted(() =&gt; {\r\n  if (localStorage.getItem('theme') === 'dark') {\r\n    isDark.value = true\r\n    document.documentElement.setAttribute('data-theme', 'dark')\r\n  }\r\n})\r\n&lt;\/script&gt;\r\n<\/code><\/pre>\n<hr \/>\n<h3>3. \u6838\u5fc3\u4e1a\u52a1\u9875\u9762<\/h3>\n<p><strong>\ud83d\udcc4 src\/views\/Login.vue<\/strong><\/p>\n<pre><code class=\"language-\u4ee3\u7801\u6bb5\">&lt;template&gt;\r\n  &lt;div class=\"login-container\"&gt;\r\n    &lt;div class=\"modern-card login-card\"&gt;\r\n      &lt;div class=\"brand\" style=\"justify-content: center; border: none;\"&gt;\r\n        &lt;div class=\"logo\"&gt;N&lt;\/div&gt;\r\n        &lt;div class=\"brand-name\" style=\"font-size: 24px;\"&gt;Navidrome&lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n      &lt;h2 style=\"text-align: center; margin-bottom: 24px;\"&gt;\u6b22\u8fce\u56de\u6765&lt;\/h2&gt;\r\n      \r\n      &lt;div class=\"field\"&gt;\r\n        &lt;label class=\"label\"&gt;\u90ae\u7bb1\u5730\u5740&lt;\/label&gt;\r\n        &lt;input v-model=\"form.email\" class=\"input\" type=\"email\" placeholder=\"\u8f93\u5165\u90ae\u7bb1\" @keyup.enter=\"handleLogin\"&gt;\r\n      &lt;\/div&gt;\r\n      \r\n      &lt;div class=\"field\"&gt;\r\n        &lt;label class=\"label\"&gt;\u767b\u5f55\u5bc6\u7801&lt;\/label&gt;\r\n        &lt;input v-model=\"form.password\" class=\"input\" type=\"password\" placeholder=\"\u8f93\u5165\u5bc6\u7801\" @keyup.enter=\"handleLogin\"&gt;\r\n      &lt;\/div&gt;\r\n\r\n      &lt;button class=\"btn btn-gradient\" style=\"width: 100%; margin-top: 10px;\" @click=\"handleLogin\"&gt;\r\n        \u7acb\u5373\u8fdb\u5165\u7cfb\u7edf\r\n      &lt;\/button&gt;\r\n    &lt;\/div&gt;\r\n  &lt;\/div&gt;\r\n&lt;\/template&gt;\r\n\r\n&lt;script setup&gt;\r\nimport { ref } from 'vue'\r\nimport { useRouter } from 'vue-router'\r\nimport { useUserStore } from '..\/store\/user'\r\nimport request from '..\/utils\/request'\r\n\r\nconst router = useRouter()\r\nconst userStore = useUserStore()\r\nconst form = ref({ email: '', password: '' })\r\n\r\nconst handleLogin = async () =&gt; {\r\n  if (!form.value.email || !form.value.password) return alert(\"\u8bf7\u8f93\u5165\u90ae\u7bb1\u548c\u5bc6\u7801\")\r\n  try {\r\n    const data = await request.post('\/v1\/login', form.value)\r\n    userStore.setLogin(data) \/\/ \u5b58\u50a8\u72b6\u6001\r\n    router.push('\/') \/\/ \u8df3\u8f6c\u5230\u4eea\u8868\u76d8\r\n  } catch (error) {}\r\n}\r\n&lt;\/script&gt;\r\n\r\n&lt;style scoped&gt;\r\n.login-container {\r\n  height: 100vh;\r\n  display: flex;\r\n  align-items: center;\r\n  justify-content: center;\r\n  background: var(--bg-color-tertiary);\r\n}\r\n.login-card { width: 400px; padding: 40px; }\r\n&lt;\/style&gt;\r\n<\/code><\/pre>\n<p><strong>\ud83d\udcc4 src\/views\/Dashboard.vue<\/strong><\/p>\n<pre><code class=\"language-\u4ee3\u7801\u6bb5\">&lt;template&gt;\r\n  &lt;div&gt;\r\n    &lt;div class=\"page-header\"&gt;\r\n      &lt;div&gt;\r\n        &lt;div class=\"page-title\"&gt;\u4eea\u8868\u76d8&lt;\/div&gt;\r\n        &lt;div class=\"page-subtitle\"&gt;\u6982\u89c8\u60a8\u7684\u8d26\u6237\u4fe1\u606f\u4e0e\u7cfb\u7edf\u72b6\u6001&lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n      &lt;div style=\"display:flex; gap:10px;\"&gt;\r\n        &lt;button class=\"btn btn-success\" @click=\"doCheckin\" :disabled=\"hasCheckedIn\" :style=\"{ opacity: hasCheckedIn ? 0.6 : 1 }\"&gt;\r\n          {{ hasCheckedIn ? '\u2705 \u4eca\u65e5\u5df2\u7b7e\u5230' : '\ud83d\udcc5 \u6bcf\u65e5\u7b7e\u5230' }}\r\n        &lt;\/button&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n\r\n    &lt;div class=\"stat-grid\"&gt;\r\n      &lt;div class=\"stat-card\"&gt;\r\n        &lt;div class=\"stat-icon\" style=\"color:var(--warning-color)\"&gt;\ud83e\ude99&lt;\/div&gt;\r\n        &lt;div&gt;\r\n          &lt;div class=\"stat-label\"&gt;\u6211\u7684\u79ef\u5206&lt;\/div&gt;\r\n          &lt;div class=\"stat-value\"&gt;{{ userStore.userInfo?.points || 0 }}&lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n      &lt;div class=\"stat-card\"&gt;\r\n        &lt;div class=\"stat-icon\"&gt;\ud83c\udfb5&lt;\/div&gt;\r\n        &lt;div&gt;\r\n          &lt;div class=\"stat-label\"&gt;\u7cfb\u7edf\u72b6\u6001&lt;\/div&gt;\r\n          &lt;div class=\"stat-value\" style=\"color: var(--success-color); font-size: 18px;\"&gt;\u8fd0\u884c\u826f\u597d&lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n  &lt;\/div&gt;\r\n&lt;\/template&gt;\r\n\r\n&lt;script setup&gt;\r\nimport { ref } from 'vue'\r\nimport { useUserStore } from '..\/store\/user'\r\nimport request from '..\/utils\/request'\r\n\r\nconst userStore = useUserStore()\r\nconst hasCheckedIn = ref(false)\r\n\r\nconst doCheckin = async () =&gt; {\r\n  if (hasCheckedIn.value) return\r\n  try {\r\n    await request.post('\/v1\/user\/checkin', { userId: userStore.userInfo.id })\r\n    hasCheckedIn.value = true\r\n    userStore.userInfo.points += 10 \/\/ \u66f4\u65b0\u672c\u5730\u79ef\u5206\u663e\u793a\r\n    alert(\"\u7b7e\u5230\u6210\u529f\uff01\u83b7\u5f97 10 \u79ef\u5206\")\r\n  } catch (error) {\r\n    if(error.message &amp;&amp; error.message.includes(\"\u5df2\u7ecf\u7b7e\u5230\")) hasCheckedIn.value = true\r\n  }\r\n}\r\n&lt;\/script&gt;\r\n<\/code><\/pre>\n<p><strong>\ud83d\udcc4 src\/views\/Settings.vue<\/strong><\/p>\n<pre><code class=\"language-\u4ee3\u7801\u6bb5\">&lt;template&gt;\r\n  &lt;div&gt;\r\n    &lt;div class=\"page-header\"&gt;\r\n      &lt;div class=\"page-title\"&gt;\u8d26\u53f7\u8bbe\u7f6e&lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n\r\n    &lt;div class=\"stat-grid\"&gt;\r\n      &lt;div class=\"modern-card\" style=\"grid-column: span 6;\"&gt;\r\n        &lt;div class=\"card-head\"&gt;&lt;div class=\"card-title\"&gt;\u57fa\u672c\u8d44\u6599&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;div class=\"card-body\"&gt;\r\n          &lt;div class=\"field\"&gt;\r\n            &lt;label class=\"label\"&gt;\u7535\u5b50\u90ae\u7bb1&lt;\/label&gt;\r\n            &lt;input class=\"input\" :value=\"userStore.userInfo?.email\" readonly style=\"background: var(--bg-color-tertiary);\"&gt;\r\n          &lt;\/div&gt;\r\n          &lt;div class=\"field\"&gt;\r\n            &lt;label class=\"label\"&gt;\u5f53\u524d\u89d2\u8272&lt;\/label&gt;\r\n            &lt;div&gt;&lt;span class=\"badge info\"&gt;{{ userStore.userInfo?.role === 'admin' ? '\u8d85\u7ea7\u7ba1\u7406\u5458' : '\u666e\u901a\u7528\u6237' }}&lt;\/span&gt;&lt;\/div&gt;\r\n          &lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n\r\n      &lt;div class=\"modern-card\" style=\"grid-column: span 6;\"&gt;\r\n        &lt;div class=\"card-head\"&gt;&lt;div class=\"card-title\"&gt;\u4fee\u6539\u5bc6\u7801&lt;\/div&gt;&lt;\/div&gt;\r\n        &lt;div class=\"card-body\"&gt;\r\n          &lt;div class=\"field\"&gt;\r\n            &lt;label class=\"label\"&gt;\u539f\u5bc6\u7801&lt;\/label&gt;\r\n            &lt;input v-model=\"pwdForm.oldPassword\" type=\"password\" class=\"input\" placeholder=\"\u8bf7\u8f93\u5165\u5f53\u524d\u5bc6\u7801\"&gt;\r\n          &lt;\/div&gt;\r\n          &lt;div class=\"field\"&gt;\r\n            &lt;label class=\"label\"&gt;\u65b0\u5bc6\u7801&lt;\/label&gt;\r\n            &lt;input v-model=\"pwdForm.newPassword\" type=\"password\" class=\"input\"&gt;\r\n          &lt;\/div&gt;\r\n          &lt;button class=\"btn btn-gradient\" style=\"width: 100%; margin-top: 10px;\" @click=\"updatePassword\"&gt;\u66f4\u65b0\u5bc6\u7801&lt;\/button&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n  &lt;\/div&gt;\r\n&lt;\/template&gt;\r\n\r\n&lt;script setup&gt;\r\nimport { ref } from 'vue'\r\nimport { useUserStore } from '..\/store\/user'\r\nimport request from '..\/utils\/request'\r\n\r\nconst userStore = useUserStore()\r\nconst pwdForm = ref({ oldPassword: '', newPassword: '' })\r\n\r\nconst updatePassword = async () =&gt; {\r\n  try {\r\n    await request.post('\/v1\/user\/update-pwd', { userId: userStore.userInfo.id, ...pwdForm.value })\r\n    alert(\"\u5bc6\u7801\u4fee\u6539\u6210\u529f\uff0c\u8bf7\u91cd\u65b0\u767b\u5f55\")\r\n    userStore.logout()\r\n    window.location.href = '\/login'\r\n  } catch (error) {}\r\n}\r\n&lt;\/script&gt;\r\n<\/code><\/pre>\n<p><strong>\ud83d\udcc4 src\/views\/admin\/Users.vue<\/strong><\/p>\n<pre><code class=\"language-\u4ee3\u7801\u6bb5\">&lt;template&gt;\r\n  &lt;div&gt;\r\n    &lt;div class=\"page-header\"&gt;\r\n      &lt;div class=\"page-title\"&gt;\u5168\u7ad9\u7528\u6237\u7ba1\u7406&lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n    &lt;div class=\"modern-card\"&gt;\r\n      &lt;div class=\"card-body\"&gt;\r\n        &lt;div class=\"table-wrap\"&gt;\r\n          &lt;table&gt;\r\n            &lt;thead&gt;\r\n              &lt;tr&gt;&lt;th&gt;ID&lt;\/th&gt;&lt;th&gt;\u90ae\u7bb1&lt;\/th&gt;&lt;th&gt;\u79ef\u5206&lt;\/th&gt;&lt;th&gt;\u89d2\u8272&lt;\/th&gt;&lt;th&gt;\u72b6\u6001&lt;\/th&gt;&lt;\/tr&gt;\r\n            &lt;\/thead&gt;\r\n            &lt;tbody&gt;\r\n              &lt;tr v-for=\"user in userList\" :key=\"user.id\"&gt;\r\n                &lt;td class=\"td-mono\"&gt;#{{ user.id }}&lt;\/td&gt;\r\n                &lt;td&gt;{{ user.email }}&lt;\/td&gt;\r\n                &lt;td style=\"color: var(--warning-color); font-weight: bold;\"&gt;{{ user.points }}&lt;\/td&gt;\r\n                &lt;td&gt;&lt;span class=\"badge\" :class=\"user.role === 'admin' ? 'info' : 'warning'\"&gt;{{ user.role }}&lt;\/span&gt;&lt;\/td&gt;\r\n                &lt;td&gt;\r\n                  &lt;span class=\"badge\" :class=\"user.status === 'active' ? 'success' : 'error'\"&gt;\r\n                    {{ user.status === 'active' ? '\u6b63\u5e38' : '\u5f02\u5e38' }}\r\n                  &lt;\/span&gt;\r\n                &lt;\/td&gt;\r\n              &lt;\/tr&gt;\r\n            &lt;\/tbody&gt;\r\n          &lt;\/table&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n  &lt;\/div&gt;\r\n&lt;\/template&gt;\r\n\r\n&lt;script setup&gt;\r\nimport { ref, onMounted } from 'vue'\r\nimport request from '..\/..\/utils\/request'\r\n\r\nconst userList = ref([])\r\n\r\nonMounted(async () =&gt; {\r\n  try {\r\n    \/\/ \u8c03\u7528\u540e\u7aef\u63a5\u53e3\u83b7\u53d6\u7528\u6237\u5217\u8868\r\n    const data = await request.get('\/v1\/admin\/users')\r\n    userList.value = data\r\n  } catch (error) {}\r\n})\r\n&lt;\/script&gt;\r\n<\/code><\/pre>\n<h3>\ud83d\ude80 \u542f\u52a8\u6307\u5f15<\/h3>\n<p>\u8fd9 8 \u4e2a\u6587\u4ef6\u8986\u76d6\u4e86\u76ee\u524d\u4f60\u6240\u9700\u8981\u7684\u6700\u6838\u5fc3\u7684\u4e1a\u52a1\u95ed\u73af\u3002\u66ff\u6362\u5b8c\u6bd5\u540e\uff0c\u786e\u4fdd\u540e\u7aef\u6b63\u5728\u8fd0\u884c\uff0c\u5728\u7ec8\u7aef\u6267\u884c\uff1a<\/p>\n<pre><code class=\"language-bash\">npm run dev -- --host\r\n<\/code><\/pre>\n<p>\u6b64\u65f6\u4f60\u8bbf\u95ee\u7f51\u9875\uff0c\u5982\u679c\u6ca1\u6709\u767b\u5f55\uff0c\u5b83\u4f1a<strong>\u7acb\u523b\u62e6\u622a\u5e76\u8df3\u8f6c\u5230\u534e\u4e3d\u7684\u767b\u5f55\u9875<\/strong>\u3002\u767b\u5f55\u540e\uff0c\u4f60\u5c06\u770b\u5230\u5b8c\u6574\u7684\u4e2a\u4eba\u4fe1\u606f\u3001\u72b6\u6001\u6570\u636e\u4ee5\u53ca\u52a8\u6001\u5de6\u4fa7\u83dc\u5355\uff01<\/p>\n<hr \/>\n<p><em>Exported from <a href=\"https:\/\/github.com\/Nagi-ovo\/gemini-voyager\">Voyager<\/a><\/em><br \/>\n<em>Generated on April 14, 2026 at 04:10 PM<\/em><\/p>\n<\/div>\n<p><script>\n    const previewElement = document.getElementById('preview')\n    Vditor.setContentTheme('light', 'https:\/\/unpkg.com\/vditor@3.11.2\/dist\/css\/content-theme');\n    Vditor.codeRender(previewElement);\n    Vditor.highlightRender({\"enable\":true,\"lineNumber\":false,\"defaultLang\":\"\",\"style\":\"github\"}, previewElement, 'https:\/\/unpkg.com\/vditor@3.11.2');\n    Vditor.mathRender(previewElement, {\n        cdn: 'https:\/\/unpkg.com\/vditor@3.11.2',\n        math: {\"engine\":\"KaTeX\",\"inlineDigit\":false,\"macros\":{}},\n    });\n    Vditor.mermaidRender(previewElement, 'https:\/\/unpkg.com\/vditor@3.11.2', 'classic');\n    Vditor.SMILESRender(previewElement, 'https:\/\/unpkg.com\/vditor@3.11.2', 'classic');\n    Vditor.markmapRender(previewElement, 'https:\/\/unpkg.com\/vditor@3.11.2');\n    Vditor.flowchartRender(previewElement, 'https:\/\/unpkg.com\/vditor@3.11.2');\n    Vditor.graphvizRender(previewElement, 'https:\/\/unpkg.com\/vditor@3.11.2');\n    Vditor.chartRender(previewElement, 'https:\/\/unpkg.com\/vditor@3.11.2', 'classic');\n    Vditor.mindmapRender(previewElement, 'https:\/\/unpkg.com\/vditor@3.11.2', 'classic');\n    Vditor.abcRender(previewElement, 'https:\/\/unpkg.com\/vditor@3.11.2');\n    Vditor.mediaRender(previewElement);\n    Vditor.speechRender(previewElement);\n<\/script><br \/>\n<script src=\"https:\/\/unpkg.com\/vditor@3.11.2\/dist\/js\/icons\/ant.js\"><\/script><\/p>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Turn 9 \ud83d\udc64 User \u73b0\u5728\u4f60\u662f\u4e00\u4e2a\u9879\u76ee\u642d\u5efa\u7ba1\u7406\uff0c\u4e0b\u9762\u6240\u6709\u7684\u5bf9\u8bdd\u9700\u8981\u5728\u8fd9\u4e2a\u9879\u76ee\u4e2d\u8fdb\u884c\uff0c\u6211\u5e0c\u671b\u662f\u505a\u524d\u540e\u7aef\u7684\u5206 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-685","post","type-post","status-publish","format-standard","hentry","category-shouye"],"_links":{"self":[{"href":"https:\/\/sharebits.link\/index.php?rest_route=\/wp\/v2\/posts\/685","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sharebits.link\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sharebits.link\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sharebits.link\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/sharebits.link\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=685"}],"version-history":[{"count":1,"href":"https:\/\/sharebits.link\/index.php?rest_route=\/wp\/v2\/posts\/685\/revisions"}],"predecessor-version":[{"id":686,"href":"https:\/\/sharebits.link\/index.php?rest_route=\/wp\/v2\/posts\/685\/revisions\/686"}],"wp:attachment":[{"href":"https:\/\/sharebits.link\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=685"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sharebits.link\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=685"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sharebits.link\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=685"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}