生證補(bǔ)辦管理系統(tǒng)開發(fā)實(shí)戰(zhàn):Vue+Spring Boot+Node.js)
學(xué)生證丟了在不少高校至今還是一件跑斷腿的事。我認(rèn)識的一位輔導(dǎo)員朋友每周都要在紙質(zhì)申請表堆里翻來找去哪個(gè)學(xué)生卡在哪個(gè)環(huán)節(jié)得打電話一個(gè)個(gè)問。學(xué)生那邊更窩火上午跑教務(wù)處交表下午被告知缺照片補(bǔ)好再交又趕上周末。后來我們幫某高校的理學(xué)院做了一套學(xué)生證丟失補(bǔ)辦管理系統(tǒng)前端用Vue開發(fā)、整個(gè)工程跑在Node.js環(huán)境里后端接口統(tǒng)一由Spring Boot提供。系統(tǒng)上線后補(bǔ)辦周期從平均一周壓到了兩天最讓我滿意的是整個(gè)流程這是終于能看得見、查得著了。這篇文章我會把從需求梳理到開發(fā)落地的完整過程寫出來包括技術(shù)選型的原因、數(shù)據(jù)庫怎么設(shè)計(jì)、狀態(tài)機(jī)怎么定義、以及我踩過的幾個(gè)典型坑。不管你是要給學(xué)生處做類似管理平臺還是想?yún)⒖记昂蠖朔蛛x的方案這些內(nèi)容應(yīng)該都能派上用場。1. 學(xué)生證補(bǔ)辦業(yè)務(wù)里的真實(shí)痛點(diǎn)與流程重構(gòu)先別急著打開IDE寫代碼做這類管理系統(tǒng)最忌諱的就是一上來就建表。補(bǔ)辦學(xué)生證只是表面需求真正要解決的是流程混亂、信息不透明、責(zé)任不清晰這三件麻煩事。1.1 傳統(tǒng)手工補(bǔ)辦流程到底差在哪原來這套高校的流程是學(xué)生丟了證先到所在學(xué)院輔導(dǎo)員那里領(lǐng)一張紙質(zhì)申請表手填個(gè)人信息再貼照片經(jīng)常照片尺寸不對然后找輔導(dǎo)員簽字再送到教務(wù)處窗口審核后交工本費(fèi)最后過一兩周去領(lǐng)證。這過程至少有四個(gè)坑申請表是紙質(zhì)的容易丟、容易填錯(cuò)而且一旦進(jìn)入審批鏈誰也說不清它現(xiàn)在在哪個(gè)抽屜里。照片沒有統(tǒng)一規(guī)范有人交一寸藍(lán)底有人交兩寸紅底制證員拿到手里還得手動(dòng)裁剪。繳費(fèi)和制證是兩條線財(cái)務(wù)處記錄和學(xué)生證制作進(jìn)度對不上月底對賬靠人工。沒有防重復(fù)機(jī)制個(gè)別學(xué)生一學(xué)期丟三次也能補(bǔ)三次學(xué)校完全掌握不了丟失頻率。這些坑不是靠Excel能解決的需要一個(gè)有狀態(tài)流轉(zhuǎn)、角色權(quán)限和留痕機(jī)制的系統(tǒng)來托底。1.2 重新定義角色與流程分支開發(fā)前我們和教務(wù)科的老師開了三次會把流程固化成了這樣學(xué)生在線發(fā)起補(bǔ)辦申請?zhí)顚懟拘畔⑸蟼髯C件照查看進(jìn)度。輔導(dǎo)員收到申請后核實(shí)身份和材料是否齊全確認(rèn)是否為本人、是否在在讀狀態(tài)。教務(wù)管理員復(fù)核繳費(fèi)狀態(tài)和照片規(guī)格分配制證任務(wù)。制證人員按批次制作完成后點(diǎn)可領(lǐng)取。學(xué)生處主管查看全局?jǐn)?shù)據(jù)導(dǎo)出統(tǒng)計(jì)報(bào)表。同時(shí)流程并不是一條直線有時(shí)需要駁回。所以狀態(tài)機(jī)必須是可回退的而不是只管往下推。我們最終設(shè)計(jì)了如下狀態(tài)草稿 - 待輔導(dǎo)員審核 - 待教務(wù)審核 - 待繳費(fèi) - 已完成繳費(fèi) - 制證中 - 可領(lǐng)取 - 已領(lǐng)取 \-- 審核駁回 - 已駁回可重新編輯提交駁回的設(shè)計(jì)很關(guān)鍵。很多初做管理系統(tǒng)的人只做了審批通過和拒絕但拒絕后學(xué)生怎么辦我們是讓學(xué)生能修改申請材料后重新提交而不是徹底鎖死否則學(xué)生只能線下再跑一趟系統(tǒng)反而成了負(fù)擔(dān)。1.3 權(quán)限與操作留痕權(quán)限模型我們用了基于角色的訪問控制后端用Spring Security實(shí)現(xiàn)前端用Vue Router的路由守衛(wèi)控制頁面跳轉(zhuǎn)。學(xué)生端只能看到我的申請輔導(dǎo)員端只能看到本班級的待辦列表教務(wù)處能看到所有流程數(shù)據(jù)。所有狀態(tài)變更都寫進(jìn)一張審計(jì)日志表記錄操作人、操作時(shí)間、從哪個(gè)狀態(tài)變到哪個(gè)狀態(tài)。千萬別小看這個(gè)設(shè)計(jì)上線第三周就出現(xiàn)了輔導(dǎo)員誤點(diǎn)通過的情況就是因?yàn)橛腥罩竞蜖顟B(tài)回退才在幾分鐘內(nèi)恢復(fù)原樣。2. 技術(shù)選型為什么偏偏是Vue Node.js環(huán)境 Spring Boot標(biāo)題寫的是nodejsvue基于springboot可能有人會覺得矛盾——Node.js和Spring Boot不是兩套后端嗎其實(shí)這里的意思很明確前端工程化基于Node.js用Vue框架開發(fā)而后端服務(wù)用Spring Boot構(gòu)建。2.1 Node.js在整個(gè)系統(tǒng)中真正干的事Node.js在前后端分離架構(gòu)里承擔(dān)的是前端基礎(chǔ)設(shè)施的角色。我們用Vite作為構(gòu)建工具NPM管理依賴開發(fā)時(shí)啟動(dòng)一個(gè)本地開發(fā)服務(wù)器請求通過代理轉(zhuǎn)發(fā)到后端。前端源碼是.vue單文件組件編譯產(chǎn)物是純靜態(tài)資源交給Nginx托管。有人會問那能不能不裝Node.js直接改代碼不行。Vue的源碼不能直接在瀏覽器里跑需要經(jīng)過Babel轉(zhuǎn)譯、Webpack/Vite打包、CSS預(yù)編譯等步驟這些工具鏈全部是Node.js寫的。所以標(biāo)題里的nodejsvue其實(shí)是在描述前端那一套標(biāo)準(zhǔn)工程實(shí)踐。我們可以用下面的表格簡單理清兩者分工技術(shù)棧負(fù)責(zé)范圍關(guān)鍵工具Node.js前端構(gòu)建、依賴管理、開發(fā)代理NPM、Vite、BabelVue 3頁面交互、組件化、路由控制Vue Router、Pinia、Element PlusSpring Boot后端API、鑒權(quán)、業(yè)務(wù)邏輯、持久化Spring MVC、Spring Security、MyBatis PlusMySQL數(shù)據(jù)存儲InnoDB 引擎2.2 Spring Boot后端為什么合適Spring Boot最吸引我的地方不是它重而是它的生態(tài)整齊。做這個(gè)系統(tǒng)我們需要用戶認(rèn)證、接口鑒權(quán)、參數(shù)校驗(yàn)、事務(wù)管理、日志這些Spring Boot都有成熟的起步依賴寫上幾個(gè)注解就能用省去了造輪子的時(shí)間。例如文件上傳Spring Boot的MultipartFile接口直接支持請求頭解析和文件保存權(quán)限控制用PreAuthorize(hasRole(ADMIN))就可以在接口層做攔截?cái)?shù)據(jù)庫操作我們用MyBatis Plus單表增刪改查幾乎不用寫SQL。對于一個(gè)以業(yè)務(wù)流轉(zhuǎn)為主的管理系統(tǒng)穩(wěn)定性和可維護(hù)性比花哨的框架更重要。Spring Boot的自動(dòng)配置機(jī)制能減少大量XML配置新同學(xué)接手也容易上手。2.3 對比下來其他方案輸在哪里之前我們衡量過要不要用Node.js的Express或NestJS直接寫后端前后端都用JS。但后來否了原因有兩個(gè)學(xué)校信息中心現(xiàn)有服務(wù)器上大多是Java技術(shù)棧運(yùn)維同學(xué)對JVM監(jiān)控、Tomcat容器更熟引入一套Node后端會增加部署復(fù)雜度。這個(gè)系統(tǒng)的核心是審批流和權(quán)限控制Java生態(tài)在這塊的方案更成熟比如Spring Security的過濾器鏈就比Node里手動(dòng)寫JWT中間件要更體系化。當(dāng)然如果你們的團(tuán)隊(duì)是純前端出身可以完全用Node做后端。我們這里只是出于運(yùn)維習(xí)慣選擇了Spring Boot并沒有好壞之分。3. 數(shù)據(jù)庫建模從學(xué)生檔案到補(bǔ)辦記錄一條鏈數(shù)據(jù)模型決定了業(yè)務(wù)能走多遠(yuǎn)。這套系統(tǒng)的核心表其實(shí)不多但表之間關(guān)系要理清楚尤其是一人多證的情況要提前防住。3.1 核心數(shù)據(jù)表設(shè)計(jì)思路一共7張業(yè)務(wù)表各司其職表名用途關(guān)鍵字段sys_user用戶統(tǒng)一表學(xué)號/工號、密碼、角色student_info學(xué)生檔案姓名、學(xué)院、班級、證件照、在讀狀態(tài)lost_application補(bǔ)辦申請主表申請人、學(xué)號、申請?jiān)?、?dāng)前狀態(tài)review_record審核記錄表申請ID、審核人、審核動(dòng)作、備注payment_record繳費(fèi)記錄表申請ID、金額、支付單號、繳費(fèi)時(shí)間card_production制證記錄表申請ID、制證人、完成時(shí)間audit_log操作日志表操作人、操作類型、詳情有一個(gè)細(xì)節(jié)需要注意student_info表里的證件照是個(gè)URL地址不存二進(jìn)制。數(shù)據(jù)庫里保存路徑文件本身放到服務(wù)器磁盤通過靜態(tài)資源映射訪問。這樣數(shù)據(jù)庫體積小備份也快。3.2 防重復(fù)補(bǔ)辦的“學(xué)號身份證狀態(tài)”聯(lián)合校驗(yàn)很多系統(tǒng)做出來才發(fā)現(xiàn)學(xué)生會重復(fù)提交。比如上午提交了一塊下午又提交一塊前端沒控制后端也不查結(jié)果生成兩條申請。我們在lost_application表里加了唯一索引ALTER TABLE lost_application ADD UNIQUE KEY uk_student_valid (student_id, status_valid_flag);但嚴(yán)格來說狀態(tài)字段會變化沒法直接做唯一索引。所以我在應(yīng)用層用了加鎖的寫法在Spring Boot服務(wù)里通過數(shù)據(jù)庫的SELECT ... FOR UPDATE鎖住該學(xué)生的檔案記錄再判斷是否已有未完結(jié)的申請。Transactional public ApplicationVO createApplication(Long studentId) { StudentInfo student studentInfoMapper.selectByIdForUpdate(studentId); Long pendingCount lostApplicationMapper.countPending(studentId); if (pendingCount 0) { throw new BusinessException(你還有待處理的補(bǔ)辦申請不能重復(fù)提交); } // 新建申請... }這個(gè)selectByIdForUpdate在MySQL的InnoDB引擎下會鎖定該行直到事務(wù)結(jié)束避免兩個(gè)請求同時(shí)進(jìn)入判斷邏輯。上線后我用壓測模擬并發(fā)提交兩百個(gè)請求同時(shí)進(jìn)來最終只生成了兩條記錄符合預(yù)期。3.3 還應(yīng)該加的字段申請?jiān)蚝途o急程度如果這個(gè)系統(tǒng)做成通用產(chǎn)品我不會漏掉申請?jiān)蚝途o急程度。學(xué)生證丟失的原因分很多種被盜、自然遺失、損壞、換防偽卡。不同原因需要不同的審核關(guān)注度。比如被盜的要上傳派出所報(bào)案證明損壞的要交回舊證。我們這次的系統(tǒng)只做了原因枚舉沒有強(qiáng)制上傳證明文件但下一次迭代應(yīng)該把材料清單做成可配置的。這樣如果政策變化管理員可以在后臺編輯需要上傳的材料類型不用改代碼。4. 前端實(shí)現(xiàn)狀態(tài)機(jī)可視化與文件上傳前端主要面向三類使用者學(xué)生、審核人、管理員。每個(gè)角色的訴求不一樣。學(xué)生要的是我提交了現(xiàn)在到哪一步了審核人要的是積壓了多少待辦點(diǎn)開就能看信息管理員要的是全流程數(shù)據(jù)能統(tǒng)計(jì)、能導(dǎo)出。4.1 路由設(shè)計(jì)與權(quán)限控制前端我們用Vue 3 Vue Router Pinia。路由分四塊/login登錄頁/student學(xué)生端包括申請頁、申請列表、進(jìn)度詳情/approval審核端包括輔導(dǎo)員待辦、教務(wù)待辦/admin管理端包括補(bǔ)辦統(tǒng)計(jì)、黑名單、參數(shù)配置登錄成功后后端返回JWT令牌和一個(gè)角色標(biāo)識。前端將用戶信息存到Pinia并在路由守衛(wèi)里判斷router.beforeEach((to, from, next) { const user useUserStore(); if (to.meta.roles !to.meta.roles.includes(user.role)) { next(/403); return; } next(); });這里要注意前端路由守衛(wèi)只是提升體驗(yàn)真正安全還是要依靠后端每個(gè)接口的權(quán)限校驗(yàn)。否則有人手動(dòng)改了前端緩存繞過路由直接調(diào)用接口也能訪問敏感數(shù)據(jù)。4.2 進(jìn)度時(shí)間軸組件學(xué)生最關(guān)心的就是進(jìn)展。我們沒有用復(fù)雜的WebSocket而是采用輪詢加緩存的方式。申請?jiān)斍轫撁扛?0秒調(diào)一次/application/progress/{id}拿到狀態(tài)后更新界面。前端狀態(tài)展示用了一個(gè)自定義時(shí)間軸組件根據(jù)狀態(tài)字段渲染不同顏色的節(jié)點(diǎn)template el-steps :activeactiveStep align-center el-step title提交申請 description填寫信息并上傳照片 / el-step title輔導(dǎo)員審核 description核實(shí)學(xué)籍與材料 / el-step title教務(wù)審核 description復(fù)核信息與照片 / el-step title繳費(fèi) description繳納工本費(fèi) / el-step title制證 description等待證件制作 / el-step title領(lǐng)取 description前往指定地點(diǎn)領(lǐng)取 / /el-steps /template輪詢實(shí)現(xiàn)簡單不會給后端造成壓力。每隔30秒全校補(bǔ)辦用戶同時(shí)在線也就幾百個(gè)請求完全扛得住。如果以后規(guī)模大了再換成WebSocket或者SSE不遲。4.3 證件照上傳前端壓縮后端校驗(yàn)這個(gè)環(huán)節(jié)最坑。學(xué)生用手機(jī)拍的照片動(dòng)不動(dòng)3~5MB原始尺寸幾千萬像素如果不處理直接傳不僅慢還容易把服務(wù)器磁盤塞滿。我們的做法是前端用Canvas做圖片壓縮統(tǒng)一調(diào)整為300×400像素的JPG大小控制在100KB以內(nèi)。async function compressImage(file) { const img await createImageBitmap(file); const canvas document.createElement(canvas); canvas.width 300; canvas.height 400; const ctx canvas.getContext(2d); ctx.drawImage(img, 0, 0, 300, 400); const compressedBlob await new Promise(resolve { canvas.toBlob(resolve, image/jpeg, 0.85); }); return new File([compressedBlob], file.name, { type: image/jpeg }); }后端也不能完全信任前端必須在Controller層再次校驗(yàn)文件大小和類型if (file.getSize() 200 * 1024) { throw new BusinessException(圖片大小不能超過200KB); } if (!image/jpeg.equals(file.getContentType())) { throw new BusinessException(僅支持JPG格式); }為什么堅(jiān)持JPG因?yàn)樾@卡和印刷端的兼容性最好PNG有時(shí)候色彩空間不對打印出來顏色發(fā)灰??刂普掌袷侥軜O大降低制證部的返工率。5. 踩坑實(shí)錄這幾個(gè)坑代碼評審都攔不住再周密的計(jì)劃落地時(shí)總會遇到意外。這一部分記錄的是我們上線前后遇到的最典型的四個(gè)問題每個(gè)都是真實(shí)調(diào)試過的。5.1 Node版本不一致導(dǎo)致本地能跑、服務(wù)器構(gòu)建失敗某個(gè)團(tuán)隊(duì)成員本機(jī)是Node 14另一個(gè)是Node 18。有一個(gè)esbuild相關(guān)的依賴在Node 14上能安裝但運(yùn)行時(shí)會有告警在Node 18上直接編譯報(bào)錯(cuò)。排查了半天后來統(tǒng)一在項(xiàng)目根目錄放了.nvmrc文件寫死v18.16.0并用package.json里的engines字段約束{ engines: { node: 18.0.0, npm: 9.0.0 } }這里我的建議是一個(gè)團(tuán)隊(duì)務(wù)必統(tǒng)一Node主版本。不同大版本的二進(jìn)制模塊差異很大node_modules里很多原生模塊都依賴Node ABI版本換版本后必須刪除node_modules重裝否則會出現(xiàn)各種莫名其妙的運(yùn)行時(shí)錯(cuò)誤。5.2 跨域?qū)е碌卿浗涌谀貌坏紺ookie開發(fā)環(huán)境下前端跑在localhost:5173后端跑在localhost:8080跨域問題必然出現(xiàn)。我們用代理解決在Vite的vite.config.js里配置server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }但生產(chǎn)環(huán)境上線后學(xué)生通過https://tsgl.school.edu.cn訪問后端接口部署在https://api.school.edu.cn依然存在跨域。我們直接在Spring Boot的CORS配置里顯式允許了前端域名同時(shí)設(shè)置了allowCredentials(true)。注意如果要允許攜帶CookieallowedOrigins不能使用通配符*必須寫具體的域名比如https://tsgl.school.edu.cn。如果實(shí)在需要多個(gè)域名可以寫一個(gè)動(dòng)態(tài)CORS配置從數(shù)據(jù)庫讀取白名單。這個(gè)坑讓我們的登錄態(tài)調(diào)試花了整整一下午。5.3 并發(fā)提交導(dǎo)致重復(fù)補(bǔ)辦記錄前面已經(jīng)提到過selectByIdForUpdate的方案。這里補(bǔ)一個(gè)真實(shí)事故描述上線第一天有個(gè)班的學(xué)生集中補(bǔ)辦有兩個(gè)人幾乎同時(shí)點(diǎn)擊提交按鈕。如果沒有數(shù)據(jù)庫鎖兩個(gè)請求都會查到無待處理申請從而各創(chuàng)建一條記錄。幸運(yùn)的是我們提前做了鎖擋住了。但后來發(fā)現(xiàn)還有一個(gè)漏洞學(xué)生可以繞過前端按鈕連點(diǎn)通過Postman直接調(diào)用接口重復(fù)注冊不同的學(xué)號。最終我們除了鎖還在申請接口上加了防重令牌機(jī)制。這個(gè)防重令牌就是我們自己做的一個(gè)簡單TokenBucket前端在申請時(shí)先向后端要一個(gè)one-time-token后端緩存到Redis提交時(shí)校驗(yàn)并刪除。如果重復(fù)提交同一個(gè)令牌則直接拒絕。5.4 照片無法訪問靜態(tài)資源路徑配置問題照片上傳后保存在服務(wù)器/data/student-card/images/目錄我們想在瀏覽器里通過https://api.school.edu.cn/files/images/xxx.jpg直接訪問。但默認(rèn)Spring Boot的static目錄是classpath:/static/我們放在了外部磁盤目錄需要通過WebMvcConfigurer去映射Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/files/**) .addResourceLocations(file:/data/student-card/); }這里還有一個(gè)安全細(xì)節(jié)上傳的文件名不能使用原始文件名否則可能包含../等路徑穿越字符。我們用的是UUID.randomUUID().toString()生成新文件名擴(kuò)展名強(qiáng)行改為.jpg既避免路徑穿越也統(tǒng)一格式。千萬別用Math.random()拼文件名一定要用UUID或時(shí)間戳加隨機(jī)數(shù)。6. 從能用到好用我們踩過之后總結(jié)出的優(yōu)化方向系統(tǒng)上線兩個(gè)多月基本信息可以說非常平穩(wěn)。但說實(shí)話如果現(xiàn)在讓我重新做一遍我會在開始前就規(guī)劃好以下三點(diǎn)。6.1 消息通知不能靠學(xué)生主動(dòng)查現(xiàn)在的進(jìn)度查詢是學(xué)生自己登錄系統(tǒng)看。很多學(xué)生忙起來根本不會天天登系統(tǒng)結(jié)果審核通過了也不知道一直卡著不去繳費(fèi)。下一次迭代一定要加上微信或郵件通知。最經(jīng)濟(jì)的方式是接入郵件服務(wù)在審核狀態(tài)變化時(shí)通過Spring Boot的事件機(jī)制發(fā)送郵件給申請人。具體做法是在業(yè)務(wù)完成后發(fā)布一個(gè)事件applicationEventPublisher.publishEvent(new ReviewPassedEvent(applicationId));然后寫一個(gè)監(jiān)聽器去負(fù)責(zé)推送。不要把發(fā)郵件的代碼寫在業(yè)務(wù)方法里否則郵件服務(wù)故障會直接拖垮主流程。異步事件解耦是這里最值得學(xué)習(xí)的設(shè)計(jì)思路。6.2 統(tǒng)計(jì)報(bào)表讓管理者看到卡點(diǎn)在哪管理端的統(tǒng)計(jì)功能我是后來才補(bǔ)的。最初只做了列表但教務(wù)處長過來問這個(gè)月丟了證最多的是哪個(gè)學(xué)院平均補(bǔ)辦時(shí)長多少天我一下子答不上來。后來我加了幾張視圖按學(xué)院維度統(tǒng)計(jì)申請次數(shù)按狀態(tài)維度統(tǒng)計(jì)滯留時(shí)間。真正有價(jià)值的指標(biāo)是當(dāng)前平均每個(gè)狀態(tài)停留多少小時(shí)。這能直接暴露流程痛點(diǎn)。比如數(shù)據(jù)告訴我們輔導(dǎo)員審核平均要1.5天而制證環(huán)節(jié)只要4小時(shí)那我們就知道優(yōu)化方向在審批端而不是制作端。用SQL直接查詢SELECT status, AVG(TIMESTAMPDIFF(HOUR, created_at, updated_at)) AS avg_hours FROM lost_application WHERE created_at DATE_SUB(NOW(), INTERVAL 30 DAY) GROUP BY status;6.3 環(huán)境配置與數(shù)據(jù)庫腳本的統(tǒng)一管理最開始每個(gè)人的數(shù)據(jù)庫建表語句都在本地執(zhí)行結(jié)果測試環(huán)境少了一個(gè)索引生產(chǎn)環(huán)境字段名不一致團(tuán)隊(duì)聯(lián)調(diào)時(shí)浪費(fèi)了大量時(shí)間。后來引入了Flyway做數(shù)據(jù)庫版本管理所有建表語句作為遷移腳本放到項(xiàng)目倉庫啟動(dòng)時(shí)自動(dòng)按順序執(zhí)行。Flyway的好處是讓環(huán)境遷移變成代碼評審的一部分。我還建議把不同環(huán)境的配置寫在不同application-{profile}.yml里比如application-dev.yml、application-prod.yml避免改一處影響全局。團(tuán)隊(duì)項(xiàng)目里這幾乎是必須的要求可以少吵很多架。最后的實(shí)操筆記如果你正在接手類似的補(bǔ)辦系統(tǒng)我最后想分享兩點(diǎn)心得體會第一一定要把駁回后可修改再提交這個(gè)交互想清楚很多審批系統(tǒng)做成了不可逆的直線結(jié)果學(xué)生在系統(tǒng)里申請失敗后還是得跑線下這就違背了做線上化的初衷。第二上線前務(wù)必要做一次并發(fā)模擬測試哪怕只是簡單的幾十個(gè)線程同時(shí)提交也能暴露掉很多事務(wù)和鎖的問題。就這個(gè)學(xué)生證補(bǔ)辦系統(tǒng)本身而言我們目前還在優(yōu)化電子學(xué)生證的對接接口。后續(xù)如果能把電子二維碼和紙質(zhì)學(xué)生證關(guān)聯(lián)起來補(bǔ)辦頻率應(yīng)該還會進(jìn)一步下降。感謝在開發(fā)過程中一起加班改流程的某學(xué)院的林老師和負(fù)責(zé)制證的王師傅他們給出的業(yè)務(wù)反饋遠(yuǎn)比任何技術(shù)文檔都珍貴。