設(shè)計與實(shí)現(xiàn):從數(shù)據(jù)庫建模到并發(fā)交易落地)
簡介一套面向計算機(jī)科學(xué)與技術(shù)等專業(yè)本科畢業(yè)設(shè)計場景的校園一卡通信息管理系統(tǒng)設(shè)計文檔內(nèi)容圍繞 ASP.NET 與 SQL Server 技術(shù)路線完整呈現(xiàn)從需求分析、E-R 圖規(guī)劃、數(shù)據(jù)庫實(shí)現(xiàn)到消費(fèi)跟蹤、實(shí)時監(jiān)管等關(guān)鍵模塊的設(shè)計思路適用于需要參考高校信息化管理系統(tǒng)論文結(jié)構(gòu)或準(zhǔn)備相關(guān)畢設(shè)的讀者。包體為 1 個 docx 文檔壓縮后約 1.39MB便于直接閱讀與二次修改。已有 670 人學(xué)習(xí)下載。文檔以 2021 年某高校畢業(yè)設(shè)計論文為底稿除中英文摘要、任務(wù)書、進(jìn)度計劃表外還重點(diǎn)說明用戶信息管理、消費(fèi)記錄、信息更新和實(shí)時監(jiān)督四項(xiàng)功能的落地方式并對安全性、可擴(kuò)展性與可維護(hù)性展開討論。讀者可借此快速了解一卡通系統(tǒng)的整體設(shè)計流程借鑒數(shù)據(jù)庫建模和 Web 開發(fā)技術(shù)在校園場景中的具體應(yīng)用。1. 校園一卡通管理系統(tǒng)這份畢設(shè)文檔到底能落地多少如果你正在找「本科 論文 校園一卡通 信息管理系統(tǒng) 設(shè)計」方向的參考那你大概率已經(jīng)看過一堆只講概念的開題報告或者代碼和論文對不上的資源包。這份設(shè)計文檔的定位很明確它是一份可以直接照著開發(fā)的管理系統(tǒng)設(shè)計方案覆蓋從數(shù)據(jù)庫建模到核心交易流程的完整鏈路而不是停留在功能列表層面的PPT式論文。校園一卡通本質(zhì)上是一個典型的信息管理系統(tǒng)但它比普通的圖書管理、學(xué)生管理系統(tǒng)多了一層硬約束交易數(shù)據(jù)必須準(zhǔn)確、流水必須可追溯、并發(fā)場景下不能扣錯錢。這意味著你從這份文檔里拿到的不僅是CRUD界面更是一套圍繞交易一致性、報表統(tǒng)計、權(quán)限控制展開的設(shè)計思路。適合誰用兩類人一是本科畢設(shè)需要完整系統(tǒng)設(shè)計和實(shí)現(xiàn)的學(xué)生二是剛?cè)胄邢刖毷止芾硐到y(tǒng)開發(fā)的開發(fā)者前者看論文結(jié)構(gòu)后者看數(shù)據(jù)庫和業(yè)務(wù)代碼的落地細(xì)節(jié)。2. 系統(tǒng)邊界與模塊拆解先把業(yè)務(wù)流畫清楚再動代碼2.1 從需求文檔到模塊劃分八個功能域怎么收斂成四張表設(shè)計文檔里最容易被忽略的是需求分析到模塊劃分的推導(dǎo)過程。校園一卡通系統(tǒng)的用戶角色至少有四類學(xué)生、商戶食堂/超市窗口、財務(wù)管理員、系統(tǒng)管理員。每個角色看到的界面和能執(zhí)行的操作完全不同但底層數(shù)據(jù)是同一套。模塊劃分建議按角色聚合而不是按功能聚合。很多畢設(shè)會把「充值管理」「消費(fèi)管理」「退款管理」拆成三個獨(dú)立模塊但在數(shù)據(jù)庫層面它們都操作同一張交易流水表只是交易類型字段不同。文檔里推薦的劃分方式是基礎(chǔ)信息管理學(xué)生信息、卡片信息、商戶信息的增刪改查卡片業(yè)務(wù)發(fā)卡、掛失、解掛、補(bǔ)卡、注銷交易管理充值、消費(fèi)、退款所有操作統(tǒng)一走流水表統(tǒng)計報表按日/按商戶/按個人的消費(fèi)匯總系統(tǒng)管理賬號、角色、權(quán)限、操作日志這套劃分的邏輯是把「交易」作為核心域其余模塊都圍繞交易的前置和后置動作展開。你在畫模塊圖的時候核心箭頭不應(yīng)該從「學(xué)生管理」指向「消費(fèi)管理」而應(yīng)該從「卡片狀態(tài)」指向「交易流程」——一次消費(fèi)必須校驗(yàn)卡片狀態(tài)正常/掛失/注銷這個校驗(yàn)邏輯是所有模塊的公共依賴。2.2 角色權(quán)限的落地方式不引入Shiro也能做細(xì)粒度控制本科畢設(shè)里引入Shiro或Spring Security是常見做法但如果文檔的目標(biāo)是快速成型用「攔截器 注解」就夠用。權(quán)限模型分三層接口層根據(jù)登錄用戶的角色id判斷能訪問哪些Controller按鈕層同一個列表頁面不同角色看到不同的操作按鈕數(shù)據(jù)層商戶登錄后只能查詢自己的交易流水不能看全量數(shù)據(jù)比例說學(xué)生只允許調(diào)用 /api/card/balance 查詢余額和 /api/card/transactions 查自己的流水商戶可以調(diào)用 /api/pos/consume 發(fā)起消費(fèi)扣款財務(wù)管理員可以調(diào)用 /api/report/summary 看匯總報表。數(shù)據(jù)層權(quán)限最容易翻車很多畢設(shè)的SQL寫成 SELECT * FROM transactions WHERE card_id ?這個 ? 如果是前端傳過來的參數(shù)學(xué)生傳別人的card_id就能看到別人的消費(fèi)記錄。正確做法是從Session里取當(dāng)前登錄用戶的card_id而不是信任前端參數(shù)。提示權(quán)限設(shè)計的核心原則是「后端不信任任何前端輸入」。所有涉及用戶身份的id只能從服務(wù)端會話獲取。2.3 交易狀態(tài)機(jī)的設(shè)計為什么用int而不是字符串交易流水表里最關(guān)鍵的是status字段。文檔推薦用int類型配合狀態(tài)常量類而不是直接存字符串。原因有兩個一是int存儲占用小索引效率更高二是避免字符串拼寫錯誤導(dǎo)致的狀態(tài)判斷Bug比如「SUCCESS」和「SUCCEED」這種低級錯誤在真實(shí)項(xiàng)目中經(jīng)常出現(xiàn)。狀態(tài)機(jī)設(shè)計如下0待支付發(fā)起交易但還沒完成扣款1支付成功扣款已完成余額已變動2已退款原交易被撤銷3交易失敗余額不足、卡狀態(tài)異常等每次狀態(tài)變更都必須記錄變更時間。這樣在排查問題時可以通過時間線還原一筆交易的完整生命周期。代碼層面的狀態(tài)流轉(zhuǎn)要寫成一個獨(dú)立服務(wù)類不允許在Controller里直接修改status字段確保所有路徑上的狀態(tài)變化都能被日志記錄。3. 數(shù)據(jù)庫設(shè)計四張核心表撐起整個業(yè)務(wù)字段類型選錯就返工3.1 學(xué)生表、卡片表、交易表、商戶表表關(guān)系與主鍵策略數(shù)據(jù)庫是這份設(shè)計文檔最有參考價值的部分。一卡通系統(tǒng)的核心表就四張但每張表的字段設(shè)計都有講究。student表的主鍵不要用自增id用學(xué)號字符串做主鍵。原因很實(shí)際業(yè)務(wù)上層操作學(xué)生信息時到處都要帶學(xué)號如果主鍵是自增id那查詢學(xué)生時還得先根據(jù)學(xué)號查id多一次關(guān)聯(lián)。學(xué)號本身具備唯一性直接做主鍵查詢少一層Join。card表的核心字段card_id主鍵可以是物理卡號或IC卡序列號student_id關(guān)聯(lián)student表balance余額用DECIMAL(10, 2)status卡片狀態(tài)0-正常 1-掛失 2-注銷issued_time發(fā)卡時間transaction表是所有業(yè)務(wù)的基石字段設(shè)計transaction_id主鍵建議用雪花ID或UUID不要用自增IDcard_id關(guān)聯(lián)card表merchant_id商戶ID消費(fèi)時有值充值時為空type交易類型1-充值 2-消費(fèi) 3-退款amount交易金額balance_after交易后余額這個字段必須有status0-待處理 1-成功 2-失敗 3-已退款create_time交易發(fā)起時間finish_time交易完成時間merchant表字段merchant_id主鍵name商戶名稱status營業(yè)狀態(tài)contact聯(lián)系人3.2 金額字段用DECIMAL不用FLOAT/DOUBLE這是我在審別人數(shù)據(jù)庫設(shè)計時第一個看的地方。金額用FLOAT或DOUBLE在累加運(yùn)算時會產(chǎn)生精度誤差比如0.10.2的結(jié)果不是0.3而是0.30000000000000004。一卡通系統(tǒng)涉及大量充值、消費(fèi)、退款計算金額字段必須用DECIMAL(10, 2)或更高精度。balance_after字段很多人會問「是不是冗余」——既然有amount那余額不是能用上次余額減本次金額算出來嗎理論上是但在實(shí)際系統(tǒng)中保留balance_after有極大好處排查糾紛時一筆交易的余額變化直接查這條記錄就能還原不用從幾千條流水里累加計算。這是典型的用存儲空間換查詢效率的做法。時間字段統(tǒng)一用DATETIME不要用TIMESTAMP。TIMESTAMP有2038年問題而且受時區(qū)影響。DATETIME范圍從1000年到9999年足夠用。業(yè)務(wù)表里create_time和update_time用默認(rèn)值CURRENT_TIMESTAMP可以節(jié)省一層代碼邏輯。3.3 索引設(shè)計查詢慢的坑提前踩掉transaction表是增長最快的表一個月可能幾十萬行。索引設(shè)計直接決定報表查詢能不能用。三個必建索引idx_card_id按卡查流水是最高頻操作idx_merchant_id商戶查對賬idx_create_time按時間范圍查報表這里有個容易踩的坑走了idx_create_time的查詢?nèi)绻侬B加card_id條件索引效率會下降。常見的優(yōu)化做法是建立聯(lián)合索引(card_id, create_time)單獨(dú)按時間查報表時再走create_time單列索引。復(fù)合索引的最左前綴原則在有聯(lián)合索引之前要先想清楚哪些查詢條件組合最高頻。提示交易表不要做物理刪除。用status字段標(biāo)記作廢狀態(tài)保留原始流水記錄。財務(wù)對賬時每一筆記錄都要有據(jù)可查。4. 核心編碼實(shí)現(xiàn)交易并發(fā)不丟單報表統(tǒng)計不卡頓4.1 充值操作的完整流程事務(wù)、樂觀鎖與冪等控制充值流程看起來簡單加余額而已但并發(fā)下不加控制會出大問題。兩個人的請求同時到達(dá)都讀到余額100一個充50一個充30后提交的會把先提交的覆蓋最終結(jié)果是130而不是180。文檔里的方案是樂觀鎖。Transactional public void recharge(String cardId, BigDecimal amount, String orderNo) { // 冪等校驗(yàn)同一個訂單號只能處理一次 if (transactionService.existsByOrderNo(orderNo)) { throw new BizException(訂單號已存在請勿重復(fù)提交); } // 樂觀鎖更新version字段防止并發(fā)覆蓋 int rows cardMapper.updateBalanceWithVersion( cardId, amount, new BigDecimal(0), // 充值不扣減傳0 System.currentTimeMillis() ); if (rows 0) { throw new BizException(余額更新失敗請重試); } // 寫入交易流水 transactionService.createTransaction( cardId, null, 1, amount, 0, orderNo ); }這段邏輯的核心在第二行先查訂單號是否已存在如果存在直接拒絕。這是冪等控制防止客戶端重試導(dǎo)致同一筆訂單被處理兩次。樂觀鎖的實(shí)現(xiàn)方式是UPDATE語句帶上version條件更新的記錄數(shù)是0說明version不匹配說明有其他請求改了這條記錄需要重試或返回錯誤。參數(shù)說明orderNo業(yè)務(wù)訂單號每次充值的唯一標(biāo)識可以由前端生成也可以由后端根據(jù)時間戳和隨機(jī)數(shù)生成versioncard表里的版本字段每次更新1cardMapper.updateBalanceWithVersionMyBatis或JPA里寫的自定義更新SQL通過Version注解實(shí)現(xiàn)沒有使用悲觀鎖SELECT FOR UPDATE的原因有兩個一是悲觀鎖會鎖住整行充值場景并發(fā)量雖然不高但鎖競爭會影響其他讀操作二是悲觀鎖需要事務(wù)結(jié)束后才釋放鎖如果事務(wù)邏輯復(fù)雜鎖持有時間會變長。4.2 消費(fèi)扣款余額校驗(yàn)加鎖更新的兩步走消費(fèi)扣款比充值多一步必須先判斷余額是否足夠再執(zhí)行扣減。這里不能先查余額再更新因?yàn)椴楹透轮g的時間窗口內(nèi)余額可能已經(jīng)變了。正確做法是把余額判斷直接放進(jìn)UPDATE語句的WHERE條件。Transactional public void consume(String cardId, BigDecimal amount, String merchantId, String orderNo) { // 冪等校驗(yàn) if (transactionService.existsByOrderNo(orderNo)) { throw new BizException(訂單號已存在請勿重復(fù)提交); } // 校驗(yàn)卡片狀態(tài)不能是掛失或注銷 Card card cardMapper.selectById(cardId); if (card.getStatus() ! 0) { throw new BizException(卡片狀態(tài)異常無法消費(fèi)); } // 原子扣減余額足夠才執(zhí)行更新 int rows cardMapper.deductBalance(cardId, amount); if (rows 0) { throw new BizException(余額不足或卡片異常); } // 查當(dāng)前余額用于流水記錄 BigDecimal newBalance cardMapper.selectBalance(cardId); transactionService.createTransaction( cardId, merchantId, 2, amount, 1, orderNo, newBalance ); }核心在deductBalance方法的SQL上。執(zhí)行語句是UPDATE card SET balance balance - #{amount}, version version 1 WHERE card_id #{cardId} AND balance #{amount} AND status 0。MySQL會鎖住這行記錄先校驗(yàn)余額條件不滿足返回0行滿足則扣減。這比「先SELECT再UPDATE」少了一個競態(tài)窗口。這里有個技術(shù)細(xì)節(jié)值得注意為什么扣款在寫流水之前要重新查一次余額因?yàn)橛囝~扣減成功后新的余額在內(nèi)存里還沒有如果直接用傳入的amount去算新余額因?yàn)椴l(fā)原因可能算錯。重新查一次SELECT雖然多了一次IO但保證流水表里的balance_after字段一定是真實(shí)的數(shù)據(jù)庫值。參數(shù)說明deductBalance的返回int受影響行數(shù)0表示條件不滿足amount和balance都使用DECIMAL在Java側(cè)用BigDecimal類型映射避免浮點(diǎn)誤差事務(wù)內(nèi)兩次數(shù)據(jù)庫操作如果流水寫入失敗扣款會回滾保證數(shù)據(jù)一致4.3 報表統(tǒng)計的優(yōu)化按日聚合表解決慢查詢一個月的交易流水幾十萬行如果每張報表都實(shí)時SUM數(shù)據(jù)庫扛不住。設(shè)計文檔里給出的方案是「匯總表 定時任務(wù)」。-- 按日商戶匯總表 CREATE TABLE daily_merchant_summary ( id INT AUTO_INCREMENT PRIMARY KEY, merchant_id VARCHAR(32) NOT NULL, stat_date DATE NOT NULL, total_amount DECIMAL(10, 2) NOT NULL DEFAULT 0, total_count INT NOT NULL DEFAULT 0, update_time DATETIME NOT NULL, UNIQUE KEY uk_merchant_date (merchant_id, stat_date) );每日凌晨跑定時任務(wù)把前一天的交易流水按商戶分組聚合寫入?yún)R總表。報表查詢只查匯總表不碰流水表。如果業(yè)務(wù)上需要查當(dāng)天實(shí)時數(shù)據(jù)走流水表的聯(lián)合索引(card_id, create_time)數(shù)據(jù)量可控。這個設(shè)計的取舍很明確實(shí)時性差一天但查詢速度是毫秒級。如果要看今日實(shí)時數(shù)據(jù)單獨(dú)查流水表離線數(shù)據(jù)看匯總表。大部分校園場景做月度對賬和學(xué)期匯總延遲一天完全能接受。注意定時任務(wù)要考慮重復(fù)執(zhí)行的情況。比如任務(wù)掛掉了重啟后今天是第二次跑會導(dǎo)致匯總數(shù)據(jù)翻倍。解決辦法是加上任務(wù)執(zhí)行記錄表每天的任務(wù)ID唯一跑了就標(biāo)記完成重復(fù)執(zhí)行直接跳過。5. 避坑記錄校園一卡通系統(tǒng)開發(fā)的五個常見翻車點(diǎn)5.1 并發(fā)測試放大了樂觀鎖的缺陷現(xiàn)象用JMeter模擬100個并發(fā)充值請求結(jié)果有30多個請求報錯「余額更新失敗」成功率遠(yuǎn)低于預(yù)期。原因模擬的是同一張卡并發(fā)充值所有請求都在競爭同一行數(shù)據(jù)的version字段后到的直接失敗。這不是代碼Bug而是測試場景設(shè)計不合理——真實(shí)情況下100個學(xué)生充100張卡不會競爭同一行。解決做并發(fā)測試時用「不同卡號 少量并發(fā)」模擬真實(shí)場景。同時把失敗重試機(jī)制加到業(yè)務(wù)層比如樂觀鎖更新失敗后循環(huán)重試三次每次重試前重新讀取最新余額。血淚經(jīng)驗(yàn)樂觀鎖適合低沖突業(yè)務(wù)一卡通充值并發(fā)量本來就不高直接失敗讓用戶重新點(diǎn)一次問題不大。但如果是秒殺之類的超高并發(fā)場景樂觀鎖會導(dǎo)致大量失敗請求要考慮改用Redis分布式鎖或隊(duì)列削峰。5.2 DATETIME精度導(dǎo)致報表跳數(shù)據(jù)現(xiàn)象按天查報表某天數(shù)據(jù)總是少幾條排查SQL查不到問題。原因transactions表的create_time是DATETIME(3)毫秒精度。但代碼里傳入的時間是秒級精度比如前端按天查「2025-06-01」轉(zhuǎn)成的時間范圍是2025-06-01 00:00:00到2025-06-01 23:59:59而實(shí)際上有些流水的create_time精確到2025-06-02 00:00:00.123。邊界時間被排除在查詢范圍外。解決查詢時間范圍用 「 開始時間 AND 結(jié)束時間 1天」。比如查6月1日的數(shù)據(jù)條件是 create_time 2025-06-01 00:00:00 AND create_time 2025-06-02 00:00:00。前閉后開的區(qū)間寫法徹底解決邊界問題。5.3 金額精度問題Double把你坑得體無完膚現(xiàn)象充值100元消費(fèi)三次29.9元余額顯示10.300000000000004。原因數(shù)據(jù)庫金額字段用對了DECIMAL但Java實(shí)體類用Double類型接收前端用JavaScript的Number處理JSON序列化時精度丟失。解決Java側(cè)金額用BigDecimal前端金額展示用字符串或toFixed(2)處理。數(shù)據(jù)庫查詢返回的DECIMAL在MyBatis里映射成BigDecimal不要為了省事改成Double。在代碼里任何金額加減都用BigDecimal的add和subtract方法不用和-操作符。5.4 掛失卡的舊有余額處理現(xiàn)象學(xué)生掛失后補(bǔ)新卡舊卡余額沒有自動轉(zhuǎn)移新卡余額是0學(xué)生一臉懵。原因補(bǔ)卡流程只做了「舊卡狀態(tài)改為注銷」和「發(fā)新卡默認(rèn)余額0」沒有把舊卡的余額轉(zhuǎn)入新卡。解決補(bǔ)卡時生成兩條流水——舊卡余額清零退款類型新卡增加等額余額充值類型。兩步操作放在同一個事務(wù)里任何一步失敗都回滾。5.5 權(quán)限繞過商戶接口傳入他人卡號查流水現(xiàn)象商戶登錄后可以查到自己店鋪的所有消費(fèi)記錄這是對的。但改成學(xué)生卡號也能查。原因接口只校驗(yàn)了用戶是否登錄沒有校驗(yàn)數(shù)據(jù)歸屬權(quán)。商戶調(diào)用查詢流水的接口時后端按傳入?yún)?shù)查詢沒有校驗(yàn)這個卡號是否在該商戶名下消費(fèi)過。解決查詢條件強(qiáng)制帶上merchant_id并且merchant_id從登錄Session取不信任前端傳參。SQL變成WHERE card_id ? AND merchant_id ?商戶傳別人的卡號第二個條件不滿足查不到數(shù)據(jù)。6. 驗(yàn)證與交付測試用例怎么設(shè)計代碼怎么跑通全流程6.1 功能測試要點(diǎn)從正常流到異常流全覆蓋測試用例的設(shè)計比寫代碼更能看出系統(tǒng)質(zhì)量。一卡通系統(tǒng)的核心測試場景分三組。正常流新學(xué)生注冊、發(fā)卡、充值100元、余額變100、消費(fèi)35元、余額變65、掛失、余額凍結(jié)、補(bǔ)卡、余額遷移到新卡。這條鏈路走通系統(tǒng)80%的功能就正常了。異常流充值金額為負(fù)數(shù)、消費(fèi)金額大于余額、掛失后消費(fèi)、已注銷卡消費(fèi)、重復(fù)提交同一個訂單號。這幾類場景必須返回明確錯誤信息不能拋500或死鎖。并發(fā)流同一個卡號同時發(fā)起兩筆消費(fèi)總共余額50一筆30一筆40只能成功一筆。測試時需要開車并發(fā)工具模擬驗(yàn)收標(biāo)準(zhǔn)是成功的交易不出現(xiàn)負(fù)余額失敗的交易有狀態(tài)記錄。6.2 數(shù)據(jù)一致性驗(yàn)證對賬腳本系統(tǒng)上線前的最后一道防線是寫一個對賬腳本用SQL把賬算平。-- 驗(yàn)證余額一致性各卡余額 充值總額 - 消費(fèi)總額 退款總額 SELECT c.card_id, c.balance, COALESCE(SUM(CASE WHEN t.type 1 THEN t.amount ELSE 0 END), 0) AS total_recharge, COALESCE(SUM(CASE WHEN t.type 2 THEN t.amount ELSE 0 END), 0) AS total_consume, COALESCE(SUM(CASE WHEN t.type 3 THEN t.amount ELSE 0 END), 0) AS total_refund FROM card c LEFT JOIN transaction t ON c.card_id t.card_id AND t.status 1 GROUP BY c.card_id HAVING c.balance ! total_recharge - total_consume total_refund;這個SQL跑出來的記錄都是賬不平的卡片。正常情況下應(yīng)該返回0行。如果返回多行說明代碼里某條更新路徑?jīng)]有一起更新流水表沿著日志找到那條事務(wù)看commit和rollback的邊界對不對。注意對賬腳本不會發(fā)現(xiàn)「兩筆同扣一筆」的問題因?yàn)榭偨痤~是對得上的。要發(fā)現(xiàn)這種問題需要加一個「流水唯一性校驗(yàn)」——同一個order_no不允許出現(xiàn)兩次成功記錄。6.3 性能預(yù)估和生產(chǎn)部署建議校園一卡通的并發(fā)量級單機(jī)Tomcat加MySQL完全扛得住。按一個萬人規(guī)模的學(xué)校計算高峰就餐時段每秒約50筆交易樂觀鎖不沖突因?yàn)椴煌瑢W(xué)生刷不同卡事務(wù)耗時在10毫秒內(nèi)數(shù)據(jù)庫輕松處理。生產(chǎn)部署時有一個容易被忽略的細(xì)節(jié)數(shù)據(jù)庫連接池配置。Tomcat連接池默認(rèn)最大線程數(shù)是100如果某個報表SQL寫得特別慢占滿連接池消費(fèi)線程會排隊(duì)等連接。應(yīng)對措施是給報表查詢單獨(dú)配置一個連接池或者把慢SQL優(yōu)化好。從那以后我每次交付這類管理系統(tǒng)都會強(qiáng)制走一遍「對賬SQL 并發(fā)模擬 權(quán)限自測」三步驗(yàn)收三個環(huán)節(jié)全綠才敢說功能完成。這份文檔的價值不在于代碼量而在于那些不會寫在教科書里的業(yè)務(wù)約束——你的系統(tǒng)處理的是真實(shí)交易每一分錢都要有著落。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取