架構(gòu)設(shè)計:一套底層支撐考試、刷題、競賽與活動)
簡介這是一套面向教育機構(gòu)、培訓(xùn)平臺及知識競賽組織者的在線考試答題系統(tǒng)源碼適用于考試測評、日常刷題、活動競答與題庫建設(shè)等多場景兼顧教師出題管理與考生作答體驗。資源包共2000個文件主體為10443個PHP后端邏輯文件含題庫、用戶、考試核心模塊輔以427個JS交互腳本、73個CSS樣式文件、194個HTML前端頁面及88個WXML/WXSS微信小程序適配文件體現(xiàn)B/S架構(gòu)與跨端兼容設(shè)計另含SQL數(shù)據(jù)庫腳本、配置文件與日志模板總大小37.93MB。已有153人學(xué)習(xí)下載開發(fā)者可基于完整目錄結(jié)構(gòu)快速掌握系統(tǒng)分層邏輯復(fù)用題庫管理、隨機組卷、自動評分與防作弊等核心功能模塊并通過源碼定制角色權(quán)限、擴展題型或?qū)右苿佣恕?一次偶然的需求碰撞讓我徹底改寫了這類產(chǎn)品的架構(gòu)認知。一開始我只是給一家駕校做科目一模擬考試系統(tǒng)上線后不到三個月同一套底層陸續(xù)被教育機構(gòu)、銀行工會、商場活動方看中問法幾乎一致“能不能加個刷題模式”“能不能做團隊知識競賽”“能不能搞一個答題抽獎活動”我當時的第一反應(yīng)是“要重寫”但真正動手拆解之后才發(fā)現(xiàn)所謂在線考試答題系統(tǒng)本質(zhì)就三件事題庫管理、答題引擎、結(jié)果統(tǒng)計外層所有五花八門的功能只是對不同場景的規(guī)則包裝。這篇文章把我這些年在這種“考試、答題、刷題、知識競賽、活動答題、題庫”多場景需求里沉淀下來的系統(tǒng)設(shè)計思路、核心表結(jié)構(gòu)、踩坑記錄和上線經(jīng)驗完整梳理一遍。適合兩類人看一類是準備自己開發(fā)答題類產(chǎn)品的技術(shù)同學(xué)另一類是需要做技術(shù)選型或評估外包方案的培訓(xùn)機構(gòu)、企業(yè)HR、活動運營負責(zé)人。文章不準備只停留在理論層面所有內(nèi)容都是可以被直接落地的。1. 為什么一套系統(tǒng)能同時扛住考試、刷題、競賽和活動答題1.1 先看清四種場景的本質(zhì)差異很多人一聽“多場景復(fù)用”就覺得是過度設(shè)計但實際上這類系統(tǒng)之所以能復(fù)用是因為所有場景都共享同一個答題閉環(huán)出題、作答、判分、出結(jié)果。區(qū)別只在于四個維度的規(guī)則不同。我做過一個對比表把四個核心場景拆開看維度考試刷題知識競賽活動答題賬號要求強制登錄實名關(guān)聯(lián)弱登錄游客可刷必須登錄可能組隊通常無感登錄或微信授權(quán)答題節(jié)奏嚴格計時到時自動交卷自由節(jié)奏隨時暫停搶答/限時/同步開始輕快單題限時短判分規(guī)則客觀題主觀題混合嚴謹客觀題為主即時判定按積分計時雙重排名正確給分錯誤立即提示結(jié)果訴求成績單、及格率、歸檔錯題本、進度、知識點掌握度榜單、晉級、獎品中獎概率、分享裂變單看這張表你會發(fā)現(xiàn)如果照著四個場景獨立開發(fā)生命周期題目資源會嚴重割裂運營人員要維護四套題庫、四套用戶體系、四套統(tǒng)計報表這是巨大的重復(fù)投入。而如果做一套底層只是在最外層提供不同的場景配置模板那所有題目、用戶、答題記錄、統(tǒng)計口徑都能打通。所以這套系統(tǒng)的架構(gòu)思路從一開始就是統(tǒng)一題庫、統(tǒng)一答題引擎、隔離場景配置。題庫和引擎是固定的能力底座場景層只是配置文件的不同組合。1.2 從需求反推出一套可復(fù)用的系統(tǒng)架構(gòu)我在項目的第二版本里把系統(tǒng)拆成了四個中心這個劃分后續(xù)被驗證非常可靠題庫中心負責(zé)題目的增刪改查、分類、標簽、難度、答案解析、知識點關(guān)聯(lián)以及多租戶數(shù)據(jù)隔離。答題引擎負責(zé)一場考試/一次刷題會話的狀態(tài)流轉(zhuǎn)包括開始、暫停、提交、超時判定、斷點續(xù)答、防重復(fù)提交。場景配置中心把考試限制、刷題策略、競賽規(guī)則、活動權(quán)益抽象成配置項一個場景就是一組配置的實例。數(shù)據(jù)統(tǒng)計中心統(tǒng)一采集答題行為、判分結(jié)果、時間消耗輸出成績單、排行榜、知識點薄弱項、題目質(zhì)量分析。這四大中心的劃分不是拍腦袋而是從一次真實事故反推出來的。早期我把一堆功能塞在一個“考試模塊”里結(jié)果駕校要加刷題模式時發(fā)現(xiàn)刷題記錄和考試記錄混在了一張表里統(tǒng)計報表怎么算都不對最后只能加班拆表。所以現(xiàn)在我對這類系統(tǒng)的建議是答題會話和答題結(jié)果必須拆開建模一場考試、一次刷題練習(xí)、一場競賽搶答本質(zhì)都是不同狀態(tài)下的答題會話統(tǒng)一掛靠在同一個引擎下業(yè)務(wù)層再根據(jù)場景類型做差異化處理。2. 題庫與組卷模塊所有場景的地基2.1 題型設(shè)計的邊界別把自己鎖死在單選題上題庫最容易犯的錯誤是一開始只設(shè)計單選和多選等客戶提出填空、判斷、簡答題的時候發(fā)現(xiàn)數(shù)據(jù)庫結(jié)構(gòu)完全不支持只能打補丁。我建議的題型模型至少要留出這些擴展位客觀題單選、多選、判斷、不定項選擇判分規(guī)則由選項組合決定。主觀題簡答、論述、案例分析系統(tǒng)只做人工評分輔助不硬核判分。半自動題填空題、匹配題、排序題這類題的判分可以采用寬松匹配規(guī)則比如關(guān)鍵詞命中給分。在數(shù)據(jù)庫設(shè)計上題目主表和子表是可以縱向擴展的。題目主表存放題干、題型、難度、知識點、解析、所屬題庫ID選擇題子表存放選項列表和正確選項填空題子表存放多個空位的答案。這樣每次新增題型只需要擴展子表不需要改動主表結(jié)構(gòu)。我在處理填空題的答案匹配時吃過一次大虧。題目答案是“TCP/IP”考生填的是“tcp/ip”按精確匹配就判錯但實際按語義兩者是同一個答案。后來我加了一個答案匹配規(guī)則配置項允許多個等價答案并支持忽略大小寫、忽略首尾空格、忽略全半角。這個看起來不起眼的小功能在真實考試中能減少大量的人工申訴。2.2 人工組卷與智能抽題的取舍題庫建設(shè)好之后組卷策略是另一個關(guān)鍵點。考試類場景通常需要人工組卷保證試卷難度穩(wěn)定而刷題和競賽場景尤其是活動答題更依賴智能抽題做到每次會話題目不重復(fù)、難度符合用戶水平。智能抽題的實現(xiàn)邏輯不算復(fù)雜核心是帶權(quán)重的隨機選取。我通常按三個維度做約束知識點占比、難度分布、題型分布。偽代碼邏輯大概是這樣的function generatePaper(rule): remaining rule.totalCount selected [] for dimension in rule.dimensions: // 知識點、難度、題型 quota dimension.ratio * rule.totalCount pool queryQuestions(dimension, rule.excludeIds) picked shuffle(pool).take(quota) selected.add(picked) rule.excludeIds.add(picked.id) remaining - picked.size // 剩余名額從全量池中隨機補齊 selected.add(shuffle(queryAll(rule)).take(remaining)) return shuffle(selected)這里有一個非常容易被忽略的細節(jié)抽題必須支持排除已經(jīng)出現(xiàn)在同一場會話中的題目。如果不做排除用戶刷題時連續(xù)遇到重復(fù)題目的概率會很高尤其是題庫量小的時候體驗極差。我一般會在Redis里維護一個“本場已出題ID集合”每次抽完題都寫入超時后自動過期。人工組卷則強調(diào)可控性和穩(wěn)定性我會給組卷人提供一個實時預(yù)覽面板實時顯示當前試卷的知識點分布比例和平均難度預(yù)估幫助組卷人在保存前就調(diào)整到目標曲線。這個功能開發(fā)成本不高但客戶滿意度提升非常明顯。2.3 題庫安全與權(quán)限隔離題庫是這類系統(tǒng)的核心資產(chǎn)尤其是培訓(xùn)機構(gòu)題庫泄露等于核心競爭力流失。我做過三層的安全設(shè)計數(shù)據(jù)層隔離每個租戶/機構(gòu)有獨立的題庫空間通過庫ID字段隔離查詢鏈路里強制帶上租戶ID防止越權(quán)訪問。接口層權(quán)限題目詳情接口和試卷查看接口做權(quán)限分級。考生只能看到當前答卷中的題目不能通過遍歷題號獲取全部題目只有出題人和管理員能看到答案與解析。展示層防復(fù)制針對高價值題目前端限制右鍵菜單和文本選擇部分客戶會要求對題目文本做切片渲染防止直接爬取整題。還有一點是關(guān)于圖片題目的處理。很多實操類考試的題目是圖片版比如汽車零部件識別、電路圖判斷。圖片題目最大的風(fēng)險是容易被批量下載建議默認走帶簽名的臨時URL訪問URL有效期設(shè)為10分鐘而不是直接在頁面里暴露永久資源地址。3. 答題引擎的設(shè)計一場考試到底要存什么狀態(tài)3.1 一場答題會話的生命周期管理答題引擎是整個系統(tǒng)的“心臟”它的核心職責(zé)是把一場考試的狀態(tài)管清楚。我常用的狀態(tài)設(shè)計是IDLE待開始試卷已分配考生未進入。IN_PROGRESS進行中考生已進入計時開始答案實時持久化。PAUSED暫停僅用于刷題場景考試類場景一般不允許暫停。SUBMITTED已提交考生主動交卷或系統(tǒng)自動交卷進入判分流程。SCORED已判分判分完成成績可查詢。ARCHIVED已歸檔結(jié)果鎖定不可修改。我在狀態(tài)設(shè)計上最主要的經(jīng)驗是狀態(tài)流轉(zhuǎn)一定要放在服務(wù)端做不能依賴前端判斷。之前有個版本把“是否顯示交卷按鈕”放在前端控制結(jié)果有用戶通過瀏覽器調(diào)試把交卷按鈕撈出來未答完就提前交卷了。后來改成所有交卷操作都走后端校驗后端判斷當前會話狀態(tài)、剩余時間、已答題數(shù)才允許交卷。3.2 時間控制倒計時的三個大坑時間控制是考試系統(tǒng)里最容易出錯的地方我踩過的坑基本可以歸納為三個第一個坑前端倒計時不可信。考生的系統(tǒng)時間可能不準瀏覽器切后臺后定時器會被節(jié)流。所以正確做法是前端只負責(zé)展示“剩余秒數(shù)”真正的計時器由后端維護后端在會話開始時記錄start_time每次交卷請求把當前時間和start_time做差值超過規(guī)定時長直接拒絕并標記超時提交。第二個坑斷網(wǎng)續(xù)答的時間補償。考生在考試中斷網(wǎng)重連重連時發(fā)現(xiàn)倒計時還在走肯定投訴。我的方案是前端在重連成功后上報斷網(wǎng)時間戳后端校驗斷線時長和上次心跳時間差值不超過閾值就把start_time向后順延。這個補償邏輯需要和“防作弊”——切后臺的時間判定——區(qū)分開否則就會出現(xiàn)考生故意斷網(wǎng)來暫停考試的漏洞。第三個坑自動交卷的邊界條件。倒計時歸零那一刻正在作答但未保存的答案怎么處理我的方案是前端在倒計時還剩5秒時強制保存當前答案并在超時后禁止繼續(xù)作答后端在收到自動交卷請求后以最后一次持久化成功的答案為最終答案。這個機制要把“保存成功”的口令做成冪等的避免同一份答案重復(fù)提交產(chǎn)生兩條答題記錄。3.3 防作弊從主流程就開始設(shè)計而不是上線后補救防作弊是所有考試場景的剛需但也不要一開始就上人臉識別這種重方案性價比不高的功能反而會把考生惹毛。我建議按風(fēng)險等級分層處理基礎(chǔ)層所有考試都開切換頁面離開考試頁面的次數(shù)記錄超出閾值觸發(fā)警告。進階層重要考試開啟禁止切屏、強制全屏、鼠標離開考試窗口提醒。嚴苛層高價值考試開啟人臉識別抽拍、AI監(jiān)考、第二攝像頭監(jiān)控可對接第三方審核服務(wù)。另外一個低成本但效果很好的措施是題目和選項亂序。給同一份試卷的每個考生隨機打亂題目順序和選項順序能極大降低鄰座偷瞄答案的概率。實現(xiàn)上只需要在會話生成時為每個考生生成一份亂序映射表判分時根據(jù)映射表還原正確選項。設(shè)備和IP維度也不能完全忽視。我在高價值考試中會采集設(shè)備指紋瀏覽器指紋、IP段、MAC地址等如果同一設(shè)備指紋在短時間內(nèi)關(guān)聯(lián)多個考生賬號自動標記異常記錄提交給人工審核。這個功能不阻斷流程但確實在真實場景里幫用戶抓出過代考行為。4. 多場景適配不同答題模式的差異化實現(xiàn)4.1 刷題模式先去掉考試那一堆限制刷題模式和考試模式最大的差異在于刷題不需要一次完整會話而是隨開隨練、隨練隨走。所以刷題模式我建議單獨實現(xiàn)一套輕量流程關(guān)鍵詞是“即存即走”。具體表現(xiàn)在三個方面做題即保存無需提交進入刷題模式后每答一題立即保存答案不需要“交卷”動作退出即完成。錯題本閉環(huán)答錯的題自動進錯題本用戶可以從錯題本重新刷刷對了可以移出錯題本或標記掌握。模式切換提供順序練習(xí)、隨機練習(xí)、背題模式直接顯示答案和解析不做判分、模擬測試四種入口。模擬測試走正式考試引擎其余走刷題引擎。我踩過的一個坑是刷題模式下用戶反復(fù)在同一道題上作答多次需要保留歷史記錄還是只保留最后一次如果全部保留數(shù)據(jù)暴增如果只保留最后一次錯題本容易丟數(shù)據(jù)。后來我采取折中記錄每一次作答的明細用于統(tǒng)計掌握度但展示層以最后一次為準。4.2 知識競賽從單機答題變成實時互動知識競賽是“在線考試答題系統(tǒng)”里最有意思的場景因為它從單機走向了實時互動技術(shù)難度會有一次躍遷。這里分為兩種常見競賽形式一種是同步賽所有選手同一時間開始、同一時間結(jié)束按正確率和耗時排名。這種形式依賴后端統(tǒng)一計時器開賽時通過WebSocket批量推送開賽信令結(jié)束后統(tǒng)一回收成績。同步賽最怕的是網(wǎng)絡(luò)延遲導(dǎo)致大家開始時間不一致我建議前端在收到開賽信令后回傳本地時間戳后端校準出一個分發(fā)補償值讓所有選手的倒計時終點對齊。另一種是搶答賽系統(tǒng)出一道題所有選手在規(guī)定時間內(nèi)搶答答對得分答錯扣分。這個場景的技術(shù)核心是“公平性”誰先提交誰優(yōu)先。搶答提交的判定必須在服務(wù)端完成不能依賴前端時間戳而且要對極端情況做保護同一毫秒內(nèi)有兩個人提交如何判先我常用的方案是按請求到達網(wǎng)關(guān)的時間排序網(wǎng)關(guān)層的NTP同步周期控制在50ms以內(nèi)。競賽還有一個和考試截然不同的設(shè)計點排行榜需要實時滾動。這個功能建議使用Redis的有序集合ZSET維護分數(shù)作為score時間戳作為附加值參與排序排行榜查詢走Redis不同步寫數(shù)據(jù)庫等競賽結(jié)束后再把最終排名落庫。這樣即使榜單接口被高頻刷新數(shù)據(jù)庫也不會被打爆。4.3 活動答題高并發(fā)下的抽題與風(fēng)控活動答題是很多企業(yè)運營拉新的標配玩法典型形態(tài)是“答對5道題參與抽獎”。這類場景的流量特征和考試完全不同瞬時并發(fā)可能非常高而且安全需求相反——不是防作弊而是防刷題。我把活動答題的技術(shù)方案拆成兩端性能端抽題盡量走緩存。活動答題的題庫通常不大幾千道題撐死了。建議啟動時把題目全量加載到Redis緩存抽題時直接內(nèi)存隨機而不是每次都查數(shù)據(jù)庫。活動答題的接口要單獨做限流比如單用戶每秒最多1次請求超出直接丟棄。風(fēng)控端判斷“人機”是關(guān)鍵。活動答題如果不做風(fēng)控很快會被腳本刷爆獎品被機器人批量薅走。我建議至少做三層登錄門檻回調(diào)至少在抽獎環(huán)節(jié)強制微信授權(quán)或手機號驗證。頻率限制同一設(shè)備/IP/賬號限制每日答題次數(shù)超出后提示明日再來。行為校驗單題作答耗時小于800毫秒的全部標記為異常不參與抽獎資格。還有一個運營向小細節(jié)活動答題通常需要限制“每人只能中獎一次”這個校驗必須放在發(fā)獎事務(wù)的最前面而且發(fā)獎和扣減庫存要在一個事務(wù)里完成否則并發(fā)下會出現(xiàn)庫存扣成負數(shù)、幾個人同時領(lǐng)到最后一個獎品的情況。5. 成績計算與數(shù)據(jù)統(tǒng)計判分只是開始5.1 判分邏輯的正確寫法很多剛?cè)胄械拈_發(fā)者會把判分邏輯寫得很簡單正確答案和考生答案做一次字符串比較。這在只有單選題的小項目里可行但一旦題型變多這種寫法就廢了。我推薦的判分邏輯是按題型分派到不同的判定器每種題型有獨立的判定規(guī)則單選題考生答案ID與正確選項ID一致判對。多選題全部選對得滿分選錯或漏選可以配置為0分或部分得分。判斷題布爾值相等即可。填空題字符串標準化去空格、轉(zhuǎn)小寫、全半角歸一化后再比較支持多個等價答案。主觀題系統(tǒng)不判分進入人工評分隊列評分后支持成績修訂和申訴。這里我強烈建議一點判分過程要可追溯。每一個得分點都要記錄判分依據(jù)比如多選部分得分時得分明細里要寫明“選對2個漏選1個得50%分”。我在真實項目里被用戶質(zhì)問過“為什么我這題不是滿分”如果沒有判分依據(jù)根本說不清有了記錄之后申訴處理速度能快好幾倍。部分得分規(guī)則對考試成績分布的影響很大。比如多選題漏選給一半分、選錯給零分整體通過率會明顯高于全錯全扣。具體選擇哪種規(guī)則一定要讓客戶在配置里自己決定千萬不要寫死在代碼里。5.2 數(shù)據(jù)統(tǒng)計別把數(shù)據(jù)分析做成一個平均數(shù)展示列表統(tǒng)計模塊的價值高低決定了這套系統(tǒng)是交差用的工具還是真正能幫客戶提升題庫質(zhì)量的服務(wù)。我建議至少包含三個層次的分析成績匯總層最高分、最低分、平均分、通過率、不及格率。這些是最基礎(chǔ)的指標只能用來做結(jié)果匯報。試卷質(zhì)量層難度系數(shù)和區(qū)分度。難度系數(shù)計算公式是P 平均分 / 滿分P值低于0.3說明題目偏難高于0.8說明偏易。區(qū)分度指標可以簡單用“高分組平均分 - 低分組平均分”來評估區(qū)分度低于0.2的題目說明沒有區(qū)分能力可以考慮淘汰或修改。知識點掌握層按知識點聚合答題正確率。這一層價值最大因為培訓(xùn)機構(gòu)可以直接看到“三角函數(shù)”章節(jié)學(xué)員普遍弱然后針對性調(diào)整教學(xué)計劃。我的實現(xiàn)方式是在答題記錄落庫時把題目關(guān)聯(lián)的知識點ID同樣寫入明細表統(tǒng)計時按知識點ID做聚合生成學(xué)員個人畫像和班級整體畫像。統(tǒng)計分析有一個容易忽略的性能問題答題明細表增長很快一次五百人的考試就能產(chǎn)生幾萬條明細記錄直接用明細表做聚合查詢會越查越慢。我建議每天凌晨跑定時任務(wù)把明細表按天聚合到結(jié)果表報表查詢只查聚合表明細表只做鉆取回溯。6. 從零搭建到上線我的踩坑記錄與實施建議6.1 三個印象最深的線上事故這些年做答題系統(tǒng)線上問題沒少出講三個最典型的每個都值一次加班教訓(xùn)。事故一空格導(dǎo)致大面積錯判。有次填空題答案是“CRM系統(tǒng)”考生填“CRM系統(tǒng)”帶了一個全角空格所有帶空格的填空題全部判錯學(xué)員群直接炸了。排查后發(fā)現(xiàn)是字符串標準化環(huán)節(jié)漏了全角轉(zhuǎn)半角的處理。修復(fù)方案就是把標準化函數(shù)抽成公共模塊填空題、簡答題的關(guān)鍵詞命中全部走同一個函數(shù)。事故二并發(fā)交卷產(chǎn)生重復(fù)記錄。活動答題上線當天大量用戶集中提交數(shù)據(jù)庫在極端并發(fā)下出現(xiàn)了一條答題記錄在同一玩家名下存了兩份的情況導(dǎo)致獎品發(fā)放邏輯認為他答題次數(shù)超額。根因是數(shù)據(jù)庫缺少非唯一索引約束修復(fù)方式是在(session_id, question_id)上加唯一索引并在插入時使用ON DUPLICATE KEY UPDATE。事故三倒計時突然跳變。有考生反饋考試倒計時有次從15分鐘直接跳到3分鐘排查后發(fā)現(xiàn)是后端有一個定時任務(wù)在刷新會話時把start_time錯誤地更新成了最新一次心跳時間。修復(fù)方案是定時刷新任務(wù)只允許刷新last_heartbeat_time禁止觸碰start_time字段。這個事故讓我深刻意識到關(guān)鍵字段的寫入權(quán)限必須收口不能到處都有UPDATE session SET start_time xxx的代碼。6.2 上線前的壓測清單答題類系統(tǒng)和其他系統(tǒng)比對實時性要求更高壓測時不能只測接口吞吐量還要重點關(guān)注時間敏感鏈路。我總結(jié)了一份壓測前的檢查清單并發(fā)交卷測試模擬500人同時交卷確認不丟單、不重復(fù)、不超時。倒計時精確性測試比對服務(wù)端計時和真實時間誤差在3秒內(nèi)算合格。排行榜刷新測試競賽場景每2秒刷一次排名確認Redis集群無雪崩。斷網(wǎng)重連測試模擬斷網(wǎng)2分鐘再連上確認答案恢復(fù)完整時間補償正確。抽題緩存命中率測試活動答題場景盯緊Redis命中率正常情況下應(yīng)高于99%。壓測工具我常用JMeter和Locust腳本提前按場景寫好上線前至少跑兩輪完整回歸。6.3 部署形態(tài)與成本控制建議最后給一個務(wù)實的技術(shù)選型建議。這套系統(tǒng)的主流部署形態(tài)是這樣的單機起步一個Spring Boot或Go服務(wù)加上MySQL和Redis足夠支撐幾百到幾千人的考試場景。上云擴展考試高峰時用云服務(wù)器臨時擴容答題接口無狀態(tài)化通過負載均衡分發(fā)Redis扛會話狀態(tài)數(shù)據(jù)庫做主從。這樣一套配置扛幾萬人并發(fā)答題是沒問題的。文件存儲題目中的圖片和音視頻素材建議走對象存儲加CDN不要打在業(yè)務(wù)服務(wù)器上不然一場考試下來帶寬費用就能讓人肉疼。前端移動端適配要重點說一句競賽和活動答題必須優(yōu)先做手機H5或小程序端答題界面的操作熱區(qū)要大倒計時和交卷按鈕要固定在觸手可及的位置。我做過的幾個項目里70%以上的流量來自手機端如果前端適配沒做好后面接再多的功能都要打折扣。數(shù)據(jù)庫表設(shè)計上我見過很多失敗案例都是因為把一道題的所有字段塞在一張表里導(dǎo)致后面擴展無力。至少要把題目主表、選項表、答案表、知識點表、試卷表、答題明細表、會話表拆分開來字段冗余寧可多幾列也不能把關(guān)系揉在一起。這套系統(tǒng)做完之后我最大的感受是答題類產(chǎn)品真正難的不是某一個功能而是把不同場景的差異抽象成可配置的規(guī)則。考試要求嚴謹刷題要求輕快競賽要求實時活動要求抗壓它們共享的底層越穩(wěn)定外層場景的功能就越安全。你越早用抽象思維把這些場景拆開后面的擴展就越省力。本文還有配套的精品資源點擊獲取