級失蹤人員信息管理系統(tǒng))
做企業(yè)級失蹤人員信息發(fā)布與管理系統(tǒng)源碼項目有一件事讓我印象很深很多人上手這類系統(tǒng)時最容易低估的是審核流轉(zhuǎn)和數(shù)據(jù)閉環(huán)這兩塊反而把大量時間花在了頁面上。實際上一套真正能用的管理系統(tǒng)核心在于把公告發(fā)布、人員登記、線索上報、審核流轉(zhuǎn)、狀態(tài)變更這條鏈路串起來并且每一步都有跡可循。我做的這套項目基于SpringBootVueMyBatisMySQL架構(gòu)前后端分離源碼完整既能直接部署演示也適合在此基礎(chǔ)上做二次開發(fā)。文章里我會從需求本質(zhì)、技術(shù)選型、數(shù)據(jù)庫設(shè)計、后端接口實現(xiàn)、前端交互細(xì)節(jié)講到部署安全與排坑復(fù)盤盡量把文檔里不會寫的東西也講透。如果你正在做政務(wù)信息化、公益互助平臺、尋人相關(guān)方向的項目或者準(zhǔn)備用這類題目做畢業(yè)設(shè)計、企業(yè)實習(xí)項目這篇文章可以直接作為復(fù)現(xiàn)和改造的參考。1. 這類系統(tǒng)的需求本質(zhì)不只是發(fā)一個公告那么簡單先說一句大實話失蹤人員信息管理系統(tǒng)從功能列表上看似乎就是發(fā)布尋人啟事 管理線索聽起來輕松但落到真實業(yè)務(wù)里痛點比想象中多得多。1.1 三類參與者三個層面的真實痛點系統(tǒng)的使用者大致分為三類普通訪客、審核人員、系統(tǒng)管理員。不同角色對系統(tǒng)的期望是完全不同的這直接決定了功能設(shè)計的優(yōu)先級。普通訪客的痛點是信息太散。家人走失之后親友通常第一反應(yīng)是發(fā)朋友圈、貼紙質(zhì)告示、求助本地社區(qū)但這些渠道各自為戰(zhàn)信息無法匯總。平臺要做的不是再增加一個孤島而是提供一個統(tǒng)一入口讓所有公告集中展示、集中檢索并且能在線提交線索。審核人員的痛點是核實難、狀態(tài)更新難。走失信息涉及大量個人隱私一旦發(fā)布出去就是全網(wǎng)可見如果沒有審核環(huán)節(jié)假消息和過期消息會迅速消耗公眾信任。審核員最需要的是待審隊列 狀態(tài)流轉(zhuǎn)讓每一條信息從提交、審核、發(fā)布、找到、歸檔都有明確狀態(tài)。管理員的痛點是數(shù)據(jù)無法沉淀。沒有系統(tǒng)支撐時歷史走失案件的線索往往散落在各個渠道后期想統(tǒng)計、比對、復(fù)盤非常困難。一個合格的系統(tǒng)應(yīng)該把人和案件的數(shù)據(jù)結(jié)構(gòu)化留存支持按時間、區(qū)域、年齡、狀態(tài)等多個維度做統(tǒng)計分析。這三類痛點的交叉點就是系統(tǒng)的核心需求統(tǒng)一信息源、嚴(yán)格審核流、狀態(tài)可閉環(huán)。1.2 功能模塊與角色權(quán)限怎么劃分基于上面的痛點模塊劃分我建議按人、事、線、審四條線來做功能模塊普通訪客審核人員系統(tǒng)管理員走失人員信息登記可提交查看待審管理全部信息審核與發(fā)布無權(quán)限審核/駁回復(fù)核/撤銷公告展示與檢索查看、搜索查看管理線索上報與處理提交線索處理線索查看統(tǒng)計數(shù)據(jù)統(tǒng)計報表無權(quán)限有限查看全部維度賬號與角色管理無權(quán)限無權(quán)限分配賬號這塊設(shè)計有一個很容易踩的坑很多人會把登記和發(fā)布合并成一個操作提交之后直接上架。結(jié)果就是系統(tǒng)無法過濾虛假信息一旦被惡意利用后果非常嚴(yán)重。正確的做法是提交是一個動作審核是一個動作發(fā)布又是一個動作三者分離并且每一步都記錄操作日志。1.3 企業(yè)級三個字的分量這套系統(tǒng)叫企業(yè)級不是業(yè)務(wù)量大而是工程規(guī)范上的要求更高Controller 不能堆業(yè)務(wù)邏輯服務(wù)層要做事務(wù)管理數(shù)據(jù)訪問不能靠 JDBC 拼 SQLMyBatis 的 XML 里要支撐動態(tài)查詢狀態(tài)字段不能以魔法數(shù)字散落在各處要有狀態(tài)枚舉統(tǒng)一管理所有寫操作要有審計追蹤誰在什么時間改了什么一查便知。這些要求決定了整個項目的代碼結(jié)構(gòu)不是隨意的后面我會詳細(xì)講落地方式。2. 技術(shù)選型與工程結(jié)構(gòu)SpringBootVueMyBatis這套組合為什么能打技術(shù)選型這塊我不打算做一堆框架對比直接說結(jié)論和理由因為這套組合在同類管理系統(tǒng)里幾乎是最成熟、資料最全的路線。2.1 后端為什么用 SpringBootSpringBoot 在這類系統(tǒng)里幾乎是標(biāo)準(zhǔn)答案級別。它有內(nèi)嵌的 Web 容器打包就是一個可執(zhí)行的 jar部署成本低起步依賴把常見的配置都約定好了開發(fā)時不需要花大量時間在環(huán)境整合上生態(tài)里和權(quán)限、緩存、持久層框架的整合方案都已經(jīng)非常成熟。需要注意版本匹配問題如果項目是基于 SpringBoot 2.7.x那 JDK 8 就夠了如果源碼升級到了 SpringBoot 3.x那必須用 JDK 17 及以上。很多人卡在springboot版本太高導(dǎo)致的啟動失敗多半就是 JDK 版本不匹配或者部分第三方依賴還沒適配 3.x。2.2 前端為什么選 Vue 而不是其他框架失蹤人員信息管理系統(tǒng)里有大量多狀態(tài)頁面 數(shù)據(jù)表格 表單流程的界面邏輯Vue 的組件化和響應(yīng)式數(shù)據(jù)模型非常契合。比起直接用模板引擎渲染頁面前后端分離之后接口可以同時復(fù)用給管理后臺和外部擴(kuò)展。組件生態(tài)也很關(guān)鍵?;?Vue 的 Element 組件庫Element UI 對應(yīng) Vue 2Element Plus 對應(yīng) Vue 3提供了現(xiàn)成的表格、表單、日期選擇器、分頁組件管理后臺開發(fā)效率能翻倍。如果是從零開始搭頁面不借助組件庫光是一個帶篩選和分頁的表格就能寫幾百行。2.3 MyBatis MySQL 的組合為什么默契MyBatis 最大的價值是SQL 在手心里不慌。這類系統(tǒng)的查詢條件非常動態(tài)按姓名模糊查、按年齡段篩選、按走失時間范圍查、按區(qū)域查、按狀態(tài)查組合起來可能有幾十種情況。用 MyBatis 的動態(tài) SQL通過 if 標(biāo)簽拼接條件比 ORM 自動生成的查詢可控得多也方便直接針對慢查詢做優(yōu)化。MySQL 則承擔(dān)了穩(wěn)定可靠的數(shù)據(jù)存儲。關(guān)于版本5.7 和 8.x 都可以跑這套系統(tǒng)但從驅(qū)動和服務(wù)端兩個角度我更推薦 8.x。如果必須用 5.7要注意 JDBC 驅(qū)動用com.mysql.cj.jdbc.Driver而不是已經(jīng)過時的com.mysql.jdbc.Driver否則會有告警甚至連接失敗。2.4 工程目錄怎么組織項目整體分為前端frontend和后端backend兩個目錄結(jié)構(gòu)如下backend ├── src/main/java/com/xxx/missing │ ├── controller # REST接口層 │ ├── service # 業(yè)務(wù)邏輯層 │ ├── mapper # MyBatis數(shù)據(jù)訪問接口 │ ├── entity # 實體類 │ ├── dto # 接收參數(shù)的傳輸對象 │ ├── vo # 返回給前端的數(shù)據(jù)對象 │ ├── config # 配置類跨域、攔截器、WebMvc │ ├── common # 統(tǒng)一返回結(jié)構(gòu)、異常處理、工具類 │ └── enums # 狀態(tài)枚舉 └── src/main/resources ├── mapper/*.xml # MyBatis SQL映射文件 ├── application.yml └── db/init.sql # 初始化腳本 frontend ├── src │ ├── api # 接口請求封裝 │ ├── router # 路由配置 │ ├── stores # 狀態(tài)管理 │ ├── views # 頁面組件 │ ├── components # 公共組件 │ └── utils # 請求工具、常量等 └── package.json這種結(jié)構(gòu)最大的好處是職責(zé)清晰前端頁面不直接拼接口地址統(tǒng)一走api模塊后端每個層只做自己該做的事。改 SQL 不用動 Java 代碼改頁面不用動接口分工明確。3. 數(shù)據(jù)庫建模把人、事、線、審四類數(shù)據(jù)組織起來數(shù)據(jù)庫設(shè)計決定了系統(tǒng)能走多遠(yuǎn)。這個項目里我用的表不多但每張表的關(guān)系和字段都有講究。3.1 核心表清單與設(shè)計思路先看核心表表名用途核心字段關(guān)系sys_user系統(tǒng)用戶id, username, password, role角色字段區(qū)分審核員/管理員sys_operation_log操作審計日志user_id, action, target_id, create_time所有重要操作寫日志missing_person走失人員檔案id, name, gender, age, id_card_no, photo_path, feature_desc一比多關(guān)聯(lián)案件missing_case走失案件記錄id, person_id, status, missing_time, missing_address, reporter_id狀態(tài)機(jī)核心表notice公告內(nèi)容id, case_id, title, content, publish_time, status案件與公告一對一clue線索上報id, case_id, reporter_name, reporter_phone, content, status案件一對多線索這里最核心的一條關(guān)系鏈?zhǔn)莔issing_person人對應(yīng)missing_case案件一個案件發(fā)布一條notice公告一個案件收集多條clue線索。人和案件分開是因為一個人可能多次走失但每次走失都是獨立案件不能把多次信息混在一起。3.2 關(guān)鍵表字段細(xì)節(jié)missing_person表里有幾個字段要特別設(shè)計id_card_no身份證號必須脫敏存儲前端展示時只顯示前六位和后四位中間用星號代替。真正要做精確比對時可以在后端用加密后的值進(jìn)行匹配。photo_path照片不要存二進(jìn)制到數(shù)據(jù)庫而是把圖片文件存到服務(wù)器目錄或?qū)ο蟠鎯?shù)據(jù)庫里只保留相對路徑。這樣數(shù)據(jù)庫體積可控加載也更快。feature_desc體貌特征、身著衣物描述建議用text類型但要注意檢索時不要直接LIKE全表掃最好配合全文索引或分詞處理。missing_case表的狀態(tài)字段是系統(tǒng)最關(guān)鍵的枚舉值DRAFT草稿- PENDING待審核- PUBLISHED已發(fā)布- FOUND已找到- ARCHIVED已歸檔駁回場景下PENDING可以回退到DRAFT并記錄駁回原因。這個狀態(tài)流轉(zhuǎn)我會在后端部分詳細(xì)講。建表時有一句很實用的提示所有業(yè)務(wù)表的邏輯刪除字段deleted和審計字段create_time、update_time一定要加上即使現(xiàn)在覺得用不上將來做數(shù)據(jù)回溯和權(quán)限審計時都會需要。示例建表 SQL 大致這樣CREATE TABLE missing_case ( id BIGINT PRIMARY KEY AUTO_INCREMENT, person_id BIGINT NOT NULL COMMENT 關(guān)聯(lián)人員檔案ID, status VARCHAR(20) NOT NULL DEFAULT PENDING COMMENT 案件狀態(tài), missing_time DATETIME NOT NULL COMMENT 走失時間, missing_address VARCHAR(255) NOT NULL COMMENT 走失地點, reporter_id BIGINT NOT NULL COMMENT 登記人用戶ID, deleted TINYINT NOT NULL DEFAULT 0, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_status_time (status, missing_time), KEY idx_person (person_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT走失案件表;注意KEY idx_status_time (status, missing_time)這個聯(lián)合索引它直接服務(wù)于后臺按狀態(tài) 按時間排序的列表查詢。3.3 索引與查詢性能別讓模糊搜索拖垮庫這類系統(tǒng)最常見的查詢是列表頁的多條件篩選 分頁。最容易忽略的問題是模糊搜索對索引的破壞LIKE 關(guān)鍵字%前綴匹配可以走索引LIKE %關(guān)鍵字%任意位置匹配無法走普通索引會導(dǎo)致全表掃描。如果必須做任意位置匹配方案有兩個數(shù)據(jù)量小時直接用LIKE配合覆蓋索引也還能接受數(shù)據(jù)量大時給相關(guān)字段建全文索引MySQL 5.7 以上的ngram全文解析器支持中文分詞適合搜姓名和描述。分頁也有講究。數(shù)據(jù)量小的時候用LIMIT offset, size沒問題數(shù)據(jù)量大了以后深分頁會越來越慢因為它要把前面的數(shù)據(jù)全部掃一遍。優(yōu)化方案是改為主鍵游標(biāo)分頁WHERE id 上一頁最后一條的id ORDER BY id LIMIT size。對于這套系統(tǒng)前期用普通LIMIT即可但要留出優(yōu)化的擴(kuò)展空間。3.4 歸檔與數(shù)據(jù)留存當(dāng)案件狀態(tài)變?yōu)镕OUND已找到時公告不能直接物理刪除否則線索和審計記錄就失去了關(guān)聯(lián)對象。正確做法是狀態(tài)改為ARCHIVED公告在列表頁不再展示但數(shù)據(jù)依然保留。我在實際項目中遇到過人找到了公告還掛在首頁的尷尬事故就是沒做好狀態(tài)切換導(dǎo)致的所以狀態(tài)機(jī)一定要在數(shù)據(jù)庫層和業(yè)務(wù)層同時做約束。4. 后端落地接口設(shè)計、動態(tài)查詢與狀態(tài)機(jī)后端是整個系統(tǒng)的中樞我挑幾個最核心的落地點來講。4.1 REST 接口怎么設(shè)計接口路徑按資源劃分建議如下方法路徑功能POST/api/cases登記走失案件GET/api/cases分頁查詢案件列表GET/api/cases/{id}案件詳情PUT/api/cases/{id}/status狀態(tài)流轉(zhuǎn)審核/發(fā)布/歸檔GET/api/notices公開公告列表無需登錄POST/api/clues提交線索PUT/api/clues/{id}/status處理線索GET/api/stats/summary數(shù)據(jù)統(tǒng)計摘要公開接口和受保護(hù)接口要嚴(yán)格區(qū)分。訪客可以看公告列表和提交線索但不能操作后臺接口。這塊用攔截器做統(tǒng)一校驗就行不用每個方法都重復(fù)寫權(quán)限判斷。4.2 動態(tài)查詢MyBatis XML 的核心寫法案件列表頁的篩選條件是典型的動態(tài) SQL 場景。Mapper 接口定義ListCaseVO selectCasePage(Param(query) CaseQueryDTO query, Param(offset) int offset, Param(size) int size);對應(yīng) XML 的寫法select idselectCasePage resultTypecom.xxx.missing.vo.CaseVO SELECT c.id, p.name, p.gender, c.status, c.missing_time, c.missing_address FROM missing_case c LEFT JOIN missing_person p ON c.person_id p.id where c.deleted 0 if testquery.name ! null and query.name ! AND p.name LIKE CONCAT(%, #{query.name}, %) /if if testquery.status ! null and query.status ! AND c.status #{query.status} /if if testquery.startTime ! null AND c.missing_time gt; #{query.startTime} /if if testquery.endTime ! null AND c.missing_time lt; #{query.endTime} /if /where ORDER BY c.missing_time DESC LIMIT #{offset}, #{size} /select這里有兩個細(xì)節(jié)值得說第一where標(biāo)簽會自動處理第一個條件前面的AND避免 SQL 拼接出錯第二時間范圍查詢用起始時間和結(jié)束時間兩個參數(shù)比單獨傳一個字符串更安全防止注入。和在 XML 里必須轉(zhuǎn)義成gt;和lt;這個很多人第一次寫都會踩坑。同時還得配一個selectCasePageCount查詢總數(shù)用于前端分頁組件。這里建議把條件抽成公共 SQL 片段用sql標(biāo)簽引用避免兩條 SQL 的篩選條件不一致導(dǎo)致列表數(shù)量和總數(shù)對不上的問題。4.3 狀態(tài)機(jī)的實現(xiàn)方式狀態(tài)機(jī)不能只靠 if-else。我在代碼里用枚舉統(tǒng)一管理public enum CaseStatus { DRAFT(草稿), PENDING(待審核), PUBLISHED(已發(fā)布), FOUND(已找到), ARCHIVED(已歸檔); private final String desc; }然后定義一張流轉(zhuǎn)表用 Map 或者 狀態(tài)流轉(zhuǎn)配置類來約束允許的路徑DRAFT - PENDING PENDING - PUBLISHED / PENDING - DRAFT駁回 PUBLISHED - FOUND FOUND - ARCHIVED寫入操作時先判斷當(dāng)前狀態(tài)是否允許流轉(zhuǎn)到目標(biāo)狀態(tài)不允許就直接拋業(yè)務(wù)異常。這個做法在真實項目里非常有用它能防止審核員跳過審核直接把草稿改成已發(fā)布這種邏輯漏洞。4.4 統(tǒng)一返回與全局異常接口返回值不要各寫各的統(tǒng)一用一個結(jié)構(gòu)public class ResultT { private int code; // 0 成功其他為錯誤碼 private String msg; private T data; }配合RestControllerAdvice做全局異常處理業(yè)務(wù)異常統(tǒng)一返回錯誤碼參數(shù)校驗失敗返回字段錯誤信息未捕獲異常返回系統(tǒng)繁忙之類的中性提示避免把異常堆棧直接暴露給前端。這個規(guī)范在聯(lián)調(diào)和排障時能省大量時間。5. 前端交互公告擴(kuò)散頁與管理后臺的實現(xiàn)細(xì)節(jié)前端這塊我按訪客看到的公開頁面和內(nèi)部人員使用的管理頁面兩條線來講因為它們的體驗?zāi)繕?biāo)和實現(xiàn)重點完全不同。5.1 路由與狀態(tài)管理前端的路由建議分成兩塊公開區(qū)首頁公告列表、公告詳情、線索提交、走失登記管理區(qū)案件審核、公告管理、線索處理、數(shù)據(jù)統(tǒng)計、用戶管理。管理區(qū)的路由統(tǒng)一掛在一個需要登錄的父路由下配合路由守衛(wèi)做登錄態(tài)校驗。狀態(tài)管理用 Vuex 或 Pinia 存用戶信息和權(quán)限標(biāo)記頁面里根據(jù)角色展示或隱藏對應(yīng)的操作按鈕。如果管理后臺的按鈕權(quán)限比較多不要自己一遍遍寫v-if判斷角色封裝一個v-permission指令會更省事指令內(nèi)部通過狀態(tài)管理里的權(quán)限列表判斷是否渲染元素。5.2 公告詳情頁照片、描述和線索提交公開公告詳情頁有幾個交互細(xì)節(jié)值得注意照片展示不要一張張平鋪用圖片畫廊組件支持縮放和左右切換。走失人員的照片清晰度通常不高畫廊模式比單圖體驗好很多。體貌特征、衣著描述的區(qū)域要突出甚至可以做成獨立的關(guān)鍵信息卡片放在頁面頂部而不是藏在長篇文本里。線索提交表單需要做防抖和防重復(fù)提交。用戶連續(xù)點擊提交按鈕接口會被請求多次處理方式是在提交后立刻把按鈕置為 loading 狀態(tài)同時接口側(cè)做冪等控制同一手機(jī)號對同一案件短時間內(nèi)只能提交一次。線索表單示例大致長這樣el-form refclueFormRef :modelclueForm :rulesclueRules el-form-item label姓名 propreporterName el-input v-modelclueForm.reporterName maxlength30 / /el-form-item el-form-item label聯(lián)系電話 propreporterPhone el-input v-modelclueForm.reporterPhone maxlength11 / /el-form-item el-form-item label線索內(nèi)容 propcontent el-input typetextarea :rows4 v-modelclueForm.content maxlength500 show-word-limit / /el-form-item el-button typeprimary :loadingsubmitting clicksubmitClue提交線索/el-button /el-form這里有個小的用戶體驗技巧提交成功之后不要讓用戶馬上看到提交成功就完事最好在頁面里展示一句我們將盡快核實并與你聯(lián)系并隱藏重復(fù)提交的入口這樣既保護(hù)線人信息也避免騷擾。5.3 管理后臺表格、批量操作與審核隊列管理后臺最核心的頁面就是案件審核列表。用 el-table 加多條件搜索表單狀態(tài)用 el-tag 展示顏色待審核是橙色已發(fā)布是綠色已找到是藍(lán)色已歸檔是灰色。批量操作要格外小心。批量發(fā)布和批量駁回看起來方便但一旦操作失誤影響的是大量案件。我的建議是批量操作只開放狀態(tài)批量駁回并通知登記人這種低風(fēng)險動作高風(fēng)險動作比如批量歸檔要加二次確認(rèn)彈窗并且記錄操作人信息。審核詳情頁建議用抽屜組件而不是新開頁面這樣審核員可以在列表和詳情之間快速切換。抽屜里要展示案件時間線登記時間、提交時間、審核時間、發(fā)布時間、線索處理時間全部串成一條縱向時間線審核員只看一眼就能判斷這單目前卡在哪一步。5.4 圖片加載與列表性能公告列表頁可能包含大量圖片直接一次性加載會非??ā蓚€處理手段很實用圖片懶加載和虛擬滾動。懶加載可以用現(xiàn)成的指令庫或者自己寫一個IntersectionObserver監(jiān)聽圖片進(jìn)入視口后再設(shè)置src。列表項里的圖片建議用統(tǒng)一的縮略圖版本原圖等點擊詳情時再加載。如果列表超過幾百條表格區(qū)域建議用支持虛擬滾動的組件只渲染可視區(qū)域的行不然 DOM 節(jié)點太多頁面會掉幀。5.5 聯(lián)調(diào)階段的跨域和 Token 處理前端開發(fā)環(huán)境和后端聯(lián)調(diào)時最大的問題是跨域。本地開發(fā)時不建議用瀏覽器插件解決而是通過前端的 devServer 代理// vite.config.ts 或 vue.config.js server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }這樣前端代碼里的請求路徑都寫/api/...開發(fā)環(huán)境由代理轉(zhuǎn)發(fā)生產(chǎn)環(huán)境由 Nginx 轉(zhuǎn)發(fā)前端代碼本身不用區(qū)分環(huán)境。Token 失效的處理也很關(guān)鍵。在 axios 攔截器里統(tǒng)一做請求發(fā)起前從狀態(tài)管理取 token 并加到請求頭響應(yīng)返回 401 時清理本地登錄態(tài)并跳轉(zhuǎn)到登錄頁。不要每個接口單獨判斷否則代碼會非常冗余。6. 部署、安全與排坑本機(jī)能跑只是第一步源碼項目在本地跑通很容易真正有價值的是能部署到生產(chǎn)環(huán)境并且經(jīng)得住一些基本的攻擊和管理場景。這一部分把環(huán)境準(zhǔn)備、生產(chǎn)部署、安全加固和實際踩過的坑一次說清。6.1 本地環(huán)境準(zhǔn)備要點后端JDK 版本要和 SpringBoot 版本匹配。SpringBoot 2.7.x 用 JDK 8 或 11SpringBoot 3.x 用 JDK 17。很多人啟動報錯先懷疑代碼實際先檢查 JDK 和 Maven 依賴版本。建庫執(zhí)行db/init.sql注意字符集設(shè)置建議utf8mb4因為它能完整支持中文和 emoji避免中文亂碼。數(shù)據(jù)庫驅(qū)動MySQL 8.x 用com.mysql.cj.jdbc.Driver。前端安裝依賴時注意 Node 版本老項目用 Node 16新項目用 Node 18/20。如果依賴裝不上優(yōu)先檢查鏡像源配置和 lock 文件。6.2 生產(chǎn)部署方案生產(chǎn)環(huán)境最樸素的方案是前端打包后由 Nginx 托管后端 jar 包交給 systemd 管理MySQL 定時備份。前端打包npm run build產(chǎn)物是dist目錄把它放到 Nginx 的靜態(tài)目錄下然后配置/api反向代理到后端服務(wù)server { listen 80; server_name your-domain.com; root /var/www/missing-system/dist; index index.html; location /api/ { proxy_pass http://127.0.0.1:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 前端路由支持 history 模式 location / { try_files $uri $uri/ /index.html; } }后端用 systemd 守護(hù)進(jìn)程簡單可靠崩潰自動重啟[Unit] DescriptionMissing System Backend Afternetwork.target [Service] Userdeploy ExecStart/usr/bin/java -jar /var/www/missing-system/backend.jar Restartalways [Install] WantedBymulti-user.target數(shù)據(jù)庫備份別忽略寫個定時任務(wù)每天凌晨備份0 3 * * * mysqldump -u backup_user -ppassword missing_db /backup/missing_$(date \%F).sql6.3 安全加固的常見坑第一坑接口越權(quán)。列表接口如果直接暴露主鍵 ID攻擊者把 ID 換成別人的就可以查看他人案件。解決辦法是查詢時永遠(yuǎn)附帶當(dāng)前用戶的權(quán)限范圍管理員看全部審核員只看分配給他的或者待審的。第二坑圖片上傳漏洞。上傳接口不要只校驗 Content-Type因為可以偽造。要校驗文件擴(kuò)展名、文件頭魔數(shù)并且把圖片存儲目錄設(shè)置為不可執(zhí)行腳本文件名用隨機(jī)生成的 UUID避免路徑穿越。第三坑XSS 攻擊。公告內(nèi)容如果支持富文本必須過濾script標(biāo)簽以及各類事件屬性否則別人提交的內(nèi)容可能在你管理臺執(zhí)行腳本。建議前端用白名單過濾后端再做一次校驗。6.4 典型的排坑復(fù)盤這里列出我在實際運行這套系統(tǒng)時遇到的四個高概率問題每個都有對應(yīng)的解決思路問題現(xiàn)象根因解決方式SpringBoot 啟動失敗提示數(shù)據(jù)源配置異常版本太高如升級到 3.x 后部分配置項名稱變化檢查spring.datasource配置或回退到源碼匹配的 SpringBoot 版本連接 MySQL 報時區(qū)錯誤MySQL 8.x 默認(rèn)時區(qū)與 JDBC 不一致在 JDBC URL 加serverTimezoneAsia/Shanghai和useSSLfalse查出的實體屬性全是 null下劃線列名沒有映射到駝峰屬性配置map-underscore-to-camel-case: true或顯式寫 resultMap分頁總數(shù)和列表數(shù)據(jù)不一致查詢列表和查詢總數(shù)的條件不一致用sql抽取公共篩選條件兩處統(tǒng)一引用最后一個問題尤其隱蔽因為它不會報錯只是數(shù)據(jù)量上去之后統(tǒng)計數(shù)字越來越奇怪。我建議在寫完列表接口后馬上對比一下兩個 SQL 在同樣參數(shù)下是否返回相同口徑的結(jié)果?;氐竭@套系統(tǒng)本身我在實際開發(fā)中還有一個深刻的體會源碼給你的是基礎(chǔ)但真正能體現(xiàn)水平的是你對它做的安全加固和業(yè)務(wù)適配。比如給案件增加區(qū)域維度、給線索增加核實狀態(tài)、給統(tǒng)計模塊增加時段對比這些擴(kuò)展都是在現(xiàn)有表結(jié)構(gòu)上做的加法不會傷筋動骨。數(shù)據(jù)庫字段設(shè)計得規(guī)范業(yè)務(wù)擴(kuò)展時就會很輕松。這套系統(tǒng)最大的價值就是給你一個結(jié)構(gòu)清晰的起點讓你能把精力花在真正應(yīng)該花的地方。