現(xiàn)居家養(yǎng)老小程序后端:從需求到部署全指南)
最近連續(xù)接到好幾撥人問(wèn)同一個(gè)事網(wǎng)上那些“ThinkPHP和Laravel框架都支持居家養(yǎng)老院服務(wù)系統(tǒng)-小程序”的項(xiàng)目標(biāo)書(shū)、源碼包、培訓(xùn)課到底靠不靠譜自己能不能照著做一套出來(lái)。這類(lèi)項(xiàng)目最近確實(shí)扎堆出現(xiàn)幾乎是外包圈和創(chuàng)業(yè)圈公認(rèn)的下一波剛需方向。作為一個(gè)后端寫(xiě)了十幾年、接過(guò)多個(gè)小程序全流程外包的PHP開(kāi)發(fā)者我可以負(fù)責(zé)任地說(shuō)這個(gè)標(biāo)題本身沒(méi)有任何噱頭ThinkPHP和Laravel都能做小程序后端接口這一點(diǎn)千真萬(wàn)確。但“能做”和“做得穩(wěn)”之間隔著大量的需求分析、業(yè)務(wù)建模、接口設(shè)計(jì)和運(yùn)維部署細(xì)節(jié)這些恰恰是隨便一個(gè)標(biāo)題黨頁(yè)面不會(huì)告訴你的部分。這篇文章我不打算講大道理直接拿“居家養(yǎng)老院服務(wù)系統(tǒng)”這個(gè)項(xiàng)目當(dāng)例子從需求拆解到框架選型從后端API設(shè)計(jì)到小程序端聯(lián)調(diào)再到部署上線的坑位完整過(guò)一遍。適合三類(lèi)人看準(zhǔn)備接這類(lèi)外包的PHP工程師、打算自研養(yǎng)老信息系統(tǒng)的機(jī)構(gòu)技術(shù)負(fù)責(zé)人、以及想通過(guò)學(xué)習(xí)類(lèi)似項(xiàng)目練手的學(xué)生開(kāi)發(fā)者。1. 項(xiàng)目需求拆解居家養(yǎng)老小程序到底要管什么很多開(kāi)發(fā)者看到“養(yǎng)老院服務(wù)系統(tǒng)”這幾個(gè)字第一反應(yīng)就是做一個(gè)老人信息管理后臺(tái)加一個(gè)服務(wù)預(yù)約頁(yè)面完事。但實(shí)際上這類(lèi)項(xiàng)目最核心的難點(diǎn)從來(lái)不在功能數(shù)量上而在角色、流程和異常狀態(tài)的處理上。不把需求徹底拆干凈后面寫(xiě)代碼一定會(huì)反復(fù)返工。1.1 參與角色與典型業(yè)務(wù)場(chǎng)景居家養(yǎng)老和機(jī)構(gòu)養(yǎng)老最大的區(qū)別在于“人不在你眼皮底下”。養(yǎng)老院模式下老人在一個(gè)集中場(chǎng)所所有服務(wù)都在院內(nèi)發(fā)生管理系統(tǒng)更像一個(gè)內(nèi)部ERP而居家養(yǎng)老模式下服務(wù)要派到老人家里去系統(tǒng)需要同時(shí)服務(wù)四類(lèi)角色老人本人在小程序里發(fā)起服務(wù)需求、查看服務(wù)記錄、一鍵呼叫子女或平臺(tái)。家屬通常是子女遠(yuǎn)程下單、查看老人健康數(shù)據(jù)、接收服務(wù)狀態(tài)通知、在線評(píng)價(jià)。護(hù)工/服務(wù)人員接收派單、上門(mén)打卡、填寫(xiě)服務(wù)記錄、上傳照片或體征數(shù)據(jù)。機(jī)構(gòu)管理員運(yùn)營(yíng)/調(diào)度/財(cái)務(wù)審核訂單、派單、處理異常、核算工時(shí)工資、管理老人和護(hù)工檔案。典型流程是這樣一個(gè)閉環(huán)子女在小程序上給老人預(yù)約一次“上門(mén)助浴”服務(wù)系統(tǒng)生成待派單訂單后臺(tái)調(diào)度員把訂單派給附近有空的護(hù)工護(hù)工手機(jī)端收到通知后接單、上門(mén)、到達(dá)后定位打卡服務(wù)完成后填寫(xiě)服務(wù)記錄并拍照子女收到微信訂閱消息通知確認(rèn)無(wú)誤后在線評(píng)價(jià)機(jī)構(gòu)后臺(tái)再根據(jù)訂單核算護(hù)工績(jī)效。整個(gè)鏈路涉及下單、派單、接單、履約、確認(rèn)、評(píng)價(jià)、結(jié)算七個(gè)環(huán)節(jié)任何一環(huán)斷了都會(huì)引起真實(shí)世界里的麻煩。1.2 功能模塊矩陣一張圖理清邊界我習(xí)慣在動(dòng)手前把所有功能列成一張模塊清單避免開(kāi)發(fā)到一半發(fā)現(xiàn)漏了東西。養(yǎng)老小程序項(xiàng)目常見(jiàn)模塊大概有這么幾塊用戶(hù)中心微信登錄、多角色身份切換、老人檔案綁定、家屬關(guān)系綁定、緊急聯(lián)系人設(shè)置。服務(wù)大廳服務(wù)項(xiàng)目展示助餐、助浴、助潔、助醫(yī)、康復(fù)訓(xùn)練、陪同外出等、價(jià)格展示、預(yù)約下單、訂單支付。工單流程訂單狀態(tài)查詢(xún)、待派單列表、派單操作、護(hù)工接單、上門(mén)打卡、服務(wù)記錄填寫(xiě)、完成確認(rèn)。健康管理老人體征數(shù)據(jù)錄入血壓、血糖、心率、體重、歷史趨勢(shì)圖表、異常值提醒。消息通知服務(wù)狀態(tài)變更提醒、用藥提醒、健康異常提醒基于微信訂閱消息實(shí)現(xiàn)。機(jī)構(gòu)后臺(tái)老人檔案管理、護(hù)工管理排班、訂單審核與派單、服務(wù)評(píng)價(jià)管理、收入統(tǒng)計(jì)、護(hù)工工資結(jié)算。這個(gè)列表看著不復(fù)雜但每一塊展開(kāi)都有細(xì)節(jié)。比如“訂單支付”不只是微信支付下單還要處理服務(wù)取消后的退款規(guī)則、優(yōu)惠券核銷(xiāo)、多次服務(wù)套餐等“護(hù)工排班”要處理請(qǐng)假、換班、臨時(shí)調(diào)單“健康數(shù)據(jù)”要區(qū)分手工錄入和設(shè)備自動(dòng)上傳。如果前期不把這些邊界定義清楚后期加需求會(huì)改到懷疑人生。1.3 “兩個(gè)框架都支持”背后的真實(shí)含義回到標(biāo)題本身為什么強(qiáng)調(diào)ThinkPHP和Laravel都支持因?yàn)楹芏喾羌夹g(shù)背景的采購(gòu)方聽(tīng)到“小程序”就以為必須用Java或者Go重新做一套后端其實(shí)完全沒(méi)有必要。小程序的本質(zhì)只是一個(gè)前端客戶(hù)端它需要一個(gè)后端提供數(shù)據(jù)和業(yè)務(wù)邏輯而這個(gè)后端用什么語(yǔ)言、什么框架都不受微信限制。微信小程序通過(guò)HTTPS請(qǐng)求調(diào)用后端接口后端返回JSON數(shù)據(jù)僅此而已。ThinkPHP和Laravel都是PHP社區(qū)非常成熟的Web框架天然具備小程序后端需要的能力路由解析、控制器、數(shù)據(jù)庫(kù)ORM、中間件鑒權(quán)、緩存、隊(duì)列、定時(shí)任務(wù)。只要會(huì)Redis、MySQL、RESTful API設(shè)計(jì)用這兩個(gè)框架寫(xiě)小程序接口和寫(xiě)網(wǎng)頁(yè)接口沒(méi)什么本質(zhì)區(qū)別。所以“都支持”是句實(shí)話(huà)但真正的技術(shù)決策點(diǎn)在于你的團(tuán)隊(duì)更熟悉哪套生態(tài)你的業(yè)務(wù)復(fù)雜度需要多少框架能力支撐你的長(zhǎng)期維護(hù)計(jì)劃是什么。下面這部分我就把兩個(gè)框架放在養(yǎng)老項(xiàng)目這個(gè)具體場(chǎng)景里做個(gè)對(duì)比。2. 框架選型ThinkPHP和Laravel怎么選才不坑很多技術(shù)討論一上來(lái)就比性能、比語(yǔ)法、比社區(qū)說(shuō)實(shí)話(huà)對(duì)項(xiàng)目交付意義不大。養(yǎng)老系統(tǒng)這種業(yè)務(wù)型項(xiàng)目選框架看的是團(tuán)隊(duì)、業(yè)務(wù)、維護(hù)這三件事。不過(guò)既然標(biāo)題專(zhuān)門(mén)點(diǎn)了兩個(gè)框架的名字我還是把兩者的關(guān)鍵差異放在臺(tái)面上說(shuō)清楚。2.1 兩套框架在養(yǎng)老項(xiàng)目上的核心差異為了不云評(píng)測(cè)直接列表格對(duì)比大家在選型時(shí)可以對(duì)著看。對(duì)比維度ThinkPHPLaravel上手門(mén)檻低中文文檔齊全國(guó)內(nèi)教程多學(xué)起來(lái)快相對(duì)高需要理解Composer、Eloquent、服務(wù)容器等概念路由與控制器簡(jiǎn)潔直觀默認(rèn)自動(dòng)路由配置靈活路由定義清晰中間件體系成熟分組管理方便ORM與數(shù)據(jù)庫(kù)操作think-orm簡(jiǎn)單易用能快速寫(xiě)CRUDEloquent關(guān)聯(lián)模型強(qiáng)大適合復(fù)雜業(yè)務(wù)關(guān)系鑒權(quán)與會(huì)話(huà)自帶基礎(chǔ)認(rèn)證需自己擴(kuò)展api_token方案Laravel Sanctum/Passport專(zhuān)門(mén)解決API認(rèn)證和令牌管理隊(duì)列與定時(shí)任務(wù)支持隊(duì)列但生態(tài)相對(duì)薄需要自己搭隊(duì)列、任務(wù)調(diào)度開(kāi)箱即用Horizon等工具完善代碼規(guī)范靈活寫(xiě)起來(lái)自由大項(xiàng)目容易出現(xiàn)風(fēng)格不一約定大于配置目錄結(jié)構(gòu)統(tǒng)一團(tuán)隊(duì)協(xié)作更好部署環(huán)境輕巧虛擬主機(jī)、低配服務(wù)器都能跑要求稍高但現(xiàn)代云服務(wù)器都能輕松處理長(zhǎng)期維護(hù)國(guó)內(nèi)小團(tuán)隊(duì)用得極多招人容易但版本遷移成本高全球生態(tài)升級(jí)路徑清晰Composer包管理規(guī)范從表格能看出來(lái)Laravel在工程化能力上更強(qiáng)ThinkPHP在易上手和輕量部署上占優(yōu)勢(shì)。關(guān)鍵是養(yǎng)老系統(tǒng)這種項(xiàng)目需要哪些能力下面拆開(kāi)講。2.2 按業(yè)務(wù)復(fù)雜度判斷這套系統(tǒng)到底需要什么養(yǎng)老項(xiàng)目的業(yè)務(wù)復(fù)雜度中等偏上重點(diǎn)不在算法而在流程的嚴(yán)謹(jǐn)性。訂單狀態(tài)機(jī)、定時(shí)提醒、消息推送、多角色權(quán)限、數(shù)據(jù)統(tǒng)計(jì)這些功能對(duì)框架的能力要求各有不同。先說(shuō)說(shuō)定時(shí)任務(wù)。用藥提醒、服務(wù)到期通知、健康數(shù)據(jù)異常巡檢這些都需要定時(shí)任務(wù)支撐。Laravel的Task Scheduling可以用一行cron配置搞定所有定時(shí)任務(wù)代碼全部寫(xiě)在app/Console/Kernel里版本控制、測(cè)試都方便。ThinkPHP也有命令行和定時(shí)任務(wù)支持但需要自己設(shè)計(jì)啟動(dòng)腳本、管理任務(wù)列表項(xiàng)目一大就有點(diǎn)零散。再說(shuō)說(shuō)隊(duì)列。養(yǎng)老系統(tǒng)里有一個(gè)容易被忽視的場(chǎng)景給家屬發(fā)送訂閱消息。微信訂閱消息接口要求實(shí)時(shí)調(diào)用如果用戶(hù)量大了或者遇到網(wǎng)絡(luò)抖動(dòng)接口會(huì)超時(shí)。更合理的做法是把發(fā)送操作丟進(jìn)隊(duì)列異步處理。Laravel的隊(duì)列生態(tài)非常成熟Redis驅(qū)動(dòng)加上失敗重試機(jī)制基本開(kāi)箱即用。ThinkPHP也能做但需要自己封裝消費(fèi)者進(jìn)程和失敗處理邏輯維護(hù)成本會(huì)高一些。還有一點(diǎn)是權(quán)限設(shè)計(jì)。養(yǎng)老系統(tǒng)有四類(lèi)角色而且一個(gè)人可能是家屬又是護(hù)工還同時(shí)綁定多位老人的檔案這就需要在用戶(hù)認(rèn)證之外做精細(xì)的角色權(quán)限控制。Laravel有Gate、Policy、Sanctum組合起來(lái)很順手ThinkPHP通常需要自己寫(xiě)中間件或者擴(kuò)展包來(lái)做。不是說(shuō)TP不能做而是Laravel的標(biāo)準(zhǔn)方案更省事。2.3 結(jié)合團(tuán)隊(duì)情況和交付周期做最終決策最終拍板建議三條標(biāo)準(zhǔn)。第一如果團(tuán)隊(duì)主力PHP開(kāi)發(fā)者以前主要寫(xiě)ThinkPHP、對(duì)Composer和現(xiàn)代PHP生態(tài)接觸不多那就不要強(qiáng)行上Laravel。項(xiàng)目交付時(shí)間是硬約束團(tuán)隊(duì)需要快速進(jìn)入狀態(tài)。ThinkPHP足夠支撐養(yǎng)老系統(tǒng)的全部業(yè)務(wù)代碼結(jié)構(gòu)上多花點(diǎn)心思做好分層即可。第二如果這是一個(gè)要長(zhǎng)期運(yùn)營(yíng)、后續(xù)會(huì)不斷增加功能的產(chǎn)品我更推薦Laravel。它的Eloquent關(guān)系模型非常適合老人、家屬、地址、訂單、健康記錄這些天然存在關(guān)聯(lián)的業(yè)務(wù)數(shù)據(jù)遷移Migration機(jī)制讓數(shù)據(jù)庫(kù)結(jié)構(gòu)變更可控Seeder工具可以讓演示數(shù)據(jù)隨時(shí)重建這些對(duì)項(xiàng)目迭代非常重要。我做過(guò)好幾個(gè)TP項(xiàng)目到后期數(shù)據(jù)庫(kù)字段變更全靠手工導(dǎo)SQL時(shí)間一長(zhǎng)就沒(méi)人說(shuō)得清當(dāng)前線上庫(kù)到底是什么結(jié)構(gòu)。第三如果采購(gòu)方對(duì)技術(shù)棧沒(méi)有硬性要求而你又有一定的Laravel基礎(chǔ)那就直接上Laravel。坦率地講在當(dāng)前PHP生態(tài)里L(fēng)aravel的工程化程度和招聘市場(chǎng)的認(rèn)可度都明顯高于ThinkPHP。拿了項(xiàng)目以后找人接手、擴(kuò)團(tuán)隊(duì)都容易得多。3. 后端核心設(shè)計(jì)與API落地不管選哪個(gè)框架后端設(shè)計(jì)的思路是相通的。這一部分我用“數(shù)據(jù)庫(kù)設(shè)計(jì) 登錄鑒權(quán) 工單流程 數(shù)據(jù)通知”四條主線把核心講透代碼示例會(huì)同時(shí)兼顧兩個(gè)框架的寫(xiě)法差異方便對(duì)照。3.1 數(shù)據(jù)庫(kù)表設(shè)計(jì)先想清楚誰(shuí)的數(shù)據(jù)歸誰(shuí)養(yǎng)老系統(tǒng)的數(shù)據(jù)模型核心是“人”和“服務(wù)”但比普通系統(tǒng)多了一層血緣關(guān)系。建議至少設(shè)計(jì)這些表elder_users老人檔案表姓名、身份證號(hào)、緊急聯(lián)系人、家屬openid綁定、家庭住址、病史、過(guò)敏史、用藥清單、當(dāng)前護(hù)理等級(jí)。care_workers護(hù)工表姓名、手機(jī)、身份證、健康證信息、可服務(wù)區(qū)域、服務(wù)項(xiàng)目、排班狀態(tài)、當(dāng)前是否空閑。service_items服務(wù)項(xiàng)目表項(xiàng)目名稱(chēng)、分類(lèi)、計(jì)價(jià)單位、預(yù)約時(shí)長(zhǎng)、適用老人類(lèi)型、是否上門(mén)。service_orders服務(wù)訂單主表訂單號(hào)、關(guān)聯(lián)老人、關(guān)聯(lián)護(hù)工、關(guān)聯(lián)服務(wù)項(xiàng)目、預(yù)約時(shí)間、實(shí)際開(kāi)始結(jié)束時(shí)間、服務(wù)地址、狀態(tài)、金額、支付單號(hào)、評(píng)價(jià)狀態(tài)。health_records健康記錄表老人ID、記錄類(lèi)型血壓/血糖/心率/體重、數(shù)值、單位、測(cè)量時(shí)間、采集方式手動(dòng)/設(shè)備、備注。notifications消息發(fā)送記錄表接收人openid、消息類(lèi)型、模板ID、發(fā)送內(nèi)容、發(fā)送狀態(tài)、發(fā)送時(shí)間。有一個(gè)細(xì)節(jié)新手很容易忽略service_orders這種核心業(yè)務(wù)表最好在創(chuàng)建時(shí)就直接冗余老人姓名、護(hù)工姓名、服務(wù)項(xiàng)目名稱(chēng)這幾個(gè)字段而不是查詢(xún)時(shí)全部去join關(guān)聯(lián)表。運(yùn)營(yíng)后臺(tái)要按訂單列表搜索、導(dǎo)Excel報(bào)表如果每次都實(shí)時(shí)關(guān)聯(lián)查詢(xún)數(shù)據(jù)量大一點(diǎn)就會(huì)非常慢。冗余字段雖然違反教科書(shū)范式但在業(yè)務(wù)系統(tǒng)里是常見(jiàn)的實(shí)用工程做法。3.2 登錄與會(huì)話(huà)管理一個(gè)code換來(lái)一個(gè)openid小程序登錄的流程是固定套路小程序端調(diào)用wx.login拿到臨時(shí)code把code傳給后端后端調(diào)用微信接口用code換取openid和session_key再用openid與本地用戶(hù)表匹配生成自己的登錄態(tài)返回給小程序。關(guān)鍵點(diǎn)在于后端不能把微信身份C2S直接作為業(yè)務(wù)身份用必須生成一個(gè)自定義token。在兩個(gè)框架里的差異體現(xiàn)在ThinkPHP通常會(huì)把token存到數(shù)據(jù)庫(kù)或者Redis然后通過(guò)中間件攔截請(qǐng)求校驗(yàn)Laravel則通常用Sanctum來(lái)生成API token或者自己寫(xiě)一個(gè)JWT中間件。給大家看一段Laravel風(fēng)格的核心處理邏輯public function login(Request $request) { $code $request-input(code); $wxResult $this-wxService-code2Session($code); // wxResult[openid] 是用戶(hù)的唯一身份標(biāo)識(shí) $user User::firstOrCreate([openid $wxResult[openid]], [ nickname , avatar ]); // 為當(dāng)前用戶(hù)簽發(fā)一個(gè)API token $token $user-createToken(mini-program)-plainTextToken; return apiResponse([token $token, user_id $user-id]); }ThinkPHP的思路也完全一致只不過(guò)把createToken換成自定義生成一個(gè)隨機(jī)字符串存到tp_token表或Redis里過(guò)期時(shí)間按業(yè)務(wù)需求設(shè)置。需要注意一個(gè)細(xì)節(jié)同一個(gè)微信用戶(hù)可能同時(shí)是家屬又是護(hù)工所以角色最好不要直接放在用戶(hù)表而是單獨(dú)建一個(gè)roles字段或者多對(duì)多關(guān)聯(lián)表登錄成功后一次性返回用戶(hù)所有角色小程序端根據(jù)角色動(dòng)態(tài)展示不同菜單。3.3 服務(wù)訂單狀態(tài)機(jī)卡單是這類(lèi)系統(tǒng)最典型的事故養(yǎng)老訂單最怕的就是“卡單”——訂單停在某個(gè)狀態(tài)沒(méi)人處理。原因多半是狀態(tài)流轉(zhuǎn)代碼寫(xiě)得隨心所欲某個(gè)狀態(tài)沒(méi)有轉(zhuǎn)移出口。建議一開(kāi)始就定義好狀態(tài)常量并用代碼注釋標(biāo)出完整流轉(zhuǎn)路徑。訂單狀態(tài)可以這樣設(shè)計(jì)狀態(tài)碼狀態(tài)名稱(chēng)下一步動(dòng)作誰(shuí)觸發(fā)0待派單調(diào)度員手動(dòng)/自動(dòng)派單后臺(tái)管理員1已派單護(hù)工確認(rèn)接單護(hù)工2服務(wù)中護(hù)工確認(rèn)開(kāi)始服務(wù)護(hù)工3待確認(rèn)家屬確認(rèn)完成家屬4已完成進(jìn)入評(píng)價(jià)與結(jié)算系統(tǒng)5已取消訂單終止家屬/管理員6異常人工介入處理系統(tǒng)/管理員這里特別要處理兩個(gè)邊界一是超時(shí)未接單。護(hù)工在一定時(shí)間內(nèi)不接單系統(tǒng)要自動(dòng)回收訂單重新進(jìn)入派單池同時(shí)給調(diào)度員一個(gè)提醒二是服務(wù)過(guò)程中臨時(shí)換人。護(hù)工病假或者中途有事訂單不能斷需要支持“轉(zhuǎn)單”動(dòng)作——把已派單狀態(tài)的服務(wù)單轉(zhuǎn)移給另一個(gè)空閑護(hù)工并保留原護(hù)工的服務(wù)記錄痕跡。在代碼層面所有修改訂單狀態(tài)的操作都應(yīng)封裝成統(tǒng)一方法并且在狀態(tài)變更時(shí)記錄操作日志。我見(jiàn)過(guò)不少項(xiàng)目為了圖快直接在controller里update狀態(tài)字段最后線上出問(wèn)題時(shí)根本查不到是誰(shuí)、什么時(shí)候改的。3.4 健康數(shù)據(jù)與訂閱消息信息透明比花哨功能更重要健康數(shù)據(jù)的價(jià)值在于連續(xù)性。家屬最關(guān)心的是老人最近兩周血壓是不是穩(wěn)定、血糖有沒(méi)有異常波動(dòng)所以后端接口一定要按日期范圍查詢(xún)并按時(shí)間排序。健康記錄的寫(xiě)入要支持兩種來(lái)源老人/護(hù)工手動(dòng)錄入以及未來(lái)對(duì)接血壓計(jì)、血糖儀等藍(lán)牙設(shè)備自動(dòng)上報(bào)。數(shù)據(jù)結(jié)構(gòu)上預(yù)留設(shè)備標(biāo)識(shí)字段不會(huì)錯(cuò)。消息通知要重點(diǎn)控制推送頻率。微信小程序訂閱消息的設(shè)計(jì)比較特殊用戶(hù)必須主動(dòng)授權(quán)后才能收到一次性消息。實(shí)際項(xiàng)目里最有效的策略是只在關(guān)鍵時(shí)刻發(fā)送通知比如“服務(wù)已上門(mén)”“服務(wù)已完成”“健康數(shù)據(jù)異?!薄胺?wù)即將開(kāi)始”讓家屬感覺(jué)到信息有價(jià)值而不是被無(wú)意義的推送轟炸。每次推送后在后端記錄一條發(fā)送流水用于排查消息丟失和投訴反饋。4. 小程序端開(kāi)發(fā)與前后端聯(lián)調(diào)后端接口設(shè)計(jì)得再完善小程序端無(wú)法流暢對(duì)接也是白搭。這一部分講小程序端的技術(shù)選擇、接口封裝規(guī)范、以及養(yǎng)老場(chǎng)景下獨(dú)有的界面體驗(yàn)優(yōu)化。4.1 小程序端選型原生、uni-app還是Taro養(yǎng)老項(xiàng)目的小程序端有三種技術(shù)路線我的建議按項(xiàng)目條件來(lái)選微信原生小程序最直接IDE穩(wěn)定無(wú)需額外編譯層調(diào)試工具和文檔齊全。缺點(diǎn)是代碼只能在微信平臺(tái)使用如果以后要上支付寶小程序需要重寫(xiě)。uni-appVue語(yǔ)法一套代碼編譯到微信、支付寶、抖音等多個(gè)小程序平臺(tái)適合準(zhǔn)備做多端運(yùn)營(yíng)的團(tuán)隊(duì)。但遇到微信平臺(tái)特有API時(shí)需要寫(xiě)條件編譯增加一點(diǎn)復(fù)雜度和兼容性測(cè)試成本。TaroReact語(yǔ)法適合React技術(shù)棧的團(tuán)隊(duì)原理和uni-app類(lèi)似當(dāng)前社區(qū)成熟度也不錯(cuò)??紤]到養(yǎng)老系統(tǒng)的目標(biāo)用戶(hù)高度集中于微信生態(tài)且多數(shù)采購(gòu)方短期內(nèi)只要求微信小程序一般原生就是性?xún)r(jià)比最高的方案。除非機(jī)構(gòu)明確說(shuō)以后要同時(shí)上架支付寶小程序否則沒(méi)必要為了“可能要做多端”提前增加復(fù)雜度。4.2 前后端接口對(duì)接的幾個(gè)關(guān)鍵封裝細(xì)節(jié)小程序端無(wú)論用什么框架都建議在service層統(tǒng)一封裝請(qǐng)求函數(shù)。核心要做四件事統(tǒng)一攜帶token、統(tǒng)一處理HTTP狀態(tài)碼、自動(dòng)處理登錄失效、統(tǒng)一解析后端返回的數(shù)據(jù)結(jié)構(gòu)??梢詤⒖歼@個(gè)思路function request(url, method GET, data {}) { return new Promise((resolve, reject) { wx.request({ url: baseUrl url, method: method, data: data, header: { Authorization: Bearer wx.getStorageSync(token) }, success: (res) { if (res.data.code 0) { resolve(res.data.data); } else if (res.data.code 401) { // token過(guò)期清理本地登錄態(tài)并跳轉(zhuǎn)登錄頁(yè) wx.clearStorageSync(); wx.navigateTo({ url: /pages/login/login }); } else { wx.showToast({ title: res.data.msg || 請(qǐng)求失敗, icon: none }); reject(res.data); } }, fail: (err) reject(err) }); }); }這個(gè)封裝雖然簡(jiǎn)單但能避免大量重復(fù)代碼。需要特別注意兩個(gè)點(diǎn)一是所有接口路徑必須使用HTTPS且域名已在小程序后臺(tái)配置成request合法域名否則真機(jī)上請(qǐng)求會(huì)直接失敗二是后端的響應(yīng)結(jié)構(gòu)一定要統(tǒng)一建議固定為“code msg data”三段式前段封裝函數(shù)才能根據(jù)code做統(tǒng)一處理。今天改一個(gè)字段名、明天換個(gè)數(shù)據(jù)結(jié)構(gòu)是聯(lián)調(diào)階段最大的時(shí)間殺手。4.3 養(yǎng)老場(chǎng)景下的界面體驗(yàn)大字版和極簡(jiǎn)操作路徑養(yǎng)老系統(tǒng)的使用者包含大量中老年人但小程序的主要操作者其實(shí)是他們的子女。所以界面設(shè)計(jì)可以分兩條線子女端功能豐富、信任感強(qiáng)能看到完整信息和歷史記錄老人端則只保留高頻核心動(dòng)作比如“呼叫服務(wù)”“語(yǔ)音留言”“健康打卡”按鈕要足夠大間距足夠?qū)挶苊庹`觸。這個(gè)適配不是技術(shù)問(wèn)題但很多時(shí)候比技術(shù)問(wèn)題更能決定項(xiàng)目能否被采購(gòu)方接受。再說(shuō)技術(shù)層面的性能優(yōu)化。小程序主包大小限制是2MB超過(guò)必須用分包加載。養(yǎng)老項(xiàng)目里最容易超包的是圖片素材和引入的地圖組件庫(kù)建議把商品圖、服務(wù)介紹圖全部走CDN遠(yuǎn)程地址本地不放圖再把用戶(hù)中心、健康管理等低頻頁(yè)面放進(jìn)分包。首屏首頁(yè)的請(qǐng)求能少則少只在onLoad階段請(qǐng)求一次“服務(wù)項(xiàng)目列表 當(dāng)前進(jìn)行中的訂單”其他數(shù)據(jù)等用戶(hù)操作時(shí)再加載。實(shí)測(cè)這套方案能把首屏加載時(shí)間壓到1.5秒以?xún)?nèi)對(duì)老年用戶(hù)和弱網(wǎng)環(huán)境都比較友好。5. 調(diào)試、部署與線上維護(hù)實(shí)錄項(xiàng)目做完到上線中間還有很長(zhǎng)一段路要走。這部分我把真實(shí)項(xiàng)目中踩過(guò)的坑集中列出來(lái)很多問(wèn)題看起來(lái)不起眼但一旦踩到會(huì)卡住很久。5.1 聯(lián)調(diào)排查抓包工具和模擬器差異開(kāi)發(fā)階段最容易遇到的問(wèn)題是代碼在開(kāi)發(fā)者工具里一切正常上了真機(jī)就出問(wèn)題。最典型的差異有兩個(gè)一是開(kāi)發(fā)者工具默認(rèn)不校驗(yàn)HTTPS證書(shū)和域名真機(jī)卻會(huì)嚴(yán)格校驗(yàn)二是開(kāi)發(fā)者工具里localStorage、授權(quán)彈窗行為與真機(jī)不同定位接口在真機(jī)上還需要配置隱私協(xié)議彈窗。遇到這類(lèi)問(wèn)題別瞎猜直接抓包看請(qǐng)求。抓包工具比如常見(jiàn)的Charles、Reqable等可以從網(wǎng)上下載用它們截取小程序發(fā)出的網(wǎng)絡(luò)請(qǐng)求檢查域名、請(qǐng)求頭、參數(shù)和響應(yīng)體基本上問(wèn)題一眼就能定位。主要排查點(diǎn)包括請(qǐng)求是否發(fā)出、Header里的token是否正確、后端返回的JSON是否符合預(yù)期。這不是什么旁門(mén)左道而是常規(guī)聯(lián)調(diào)手段使用自己開(kāi)發(fā)的服務(wù)接口完全沒(méi)有任何問(wèn)題。另外要提醒大家微信開(kāi)發(fā)者工具右上角的“不校驗(yàn)合法域名”開(kāi)關(guān)在調(diào)試期可以打開(kāi)但上線前一定要記得關(guān)閉并老老實(shí)實(shí)在小程序公眾平臺(tái)配置request合法域名。5.2 部署上線PHP版本、偽靜態(tài)和域名配置兩個(gè)框架的部署差異不大但有幾個(gè)共用要點(diǎn)容易踩坑。ThinkPHP項(xiàng)目在Nginx下必須要配置偽靜態(tài)規(guī)則把所有請(qǐng)求重寫(xiě)到index.php入口文件直接使用默認(rèn)路由會(huì)導(dǎo)致首頁(yè)能開(kāi)、子頁(yè)面404。Laravel也需要類(lèi)似配置而且Laravel對(duì)目錄權(quán)限更敏感storage和bootstrap/cache目錄必須保證PHP進(jìn)程可寫(xiě)否則會(huì)直接白屏。PHP版本建議ThinkPHP 8使用PHP 8.0以上Laravel 11使用PHP 8.2以上低版本PHP會(huì)缺少語(yǔ)法特性導(dǎo)致框架無(wú)法運(yùn)行。HTTPS證書(shū)一定不要等上線了再申請(qǐng)。小程序要求所有接口域名必須是HTTPS而且這個(gè)證書(shū)不能是自簽證書(shū)必須是受信任機(jī)構(gòu)簽發(fā)的。用云服務(wù)器自帶的安全組、寶塔面板的一鍵申請(qǐng)功能就能拿到免費(fèi)證書(shū)流程很快但要注意證書(shū)到期時(shí)間設(shè)置好到期提醒。小程序后臺(tái)需要配置三個(gè)域名request合法域名、uploadFile合法域名、downloadFile合法域名。如果小程序里有上傳圖片功能只配request域名是不夠的還要把圖片存儲(chǔ)域名配到uploadFile合法域名里否則上傳請(qǐng)求會(huì)被拒絕。數(shù)據(jù)庫(kù)字符集統(tǒng)一用utf8mb4否則遇到老人檔案里的生僻字、特殊字符會(huì)保存失敗。5.3 線上運(yùn)維備份、監(jiān)控與訂單狀態(tài)巡檢系統(tǒng)上線不是結(jié)束而是運(yùn)維的開(kāi)始。養(yǎng)老項(xiàng)目的線上運(yùn)維重點(diǎn)是“數(shù)據(jù)不丟”和“流程可追”。數(shù)據(jù)安全方面我習(xí)慣每天凌晨自動(dòng)備份數(shù)據(jù)庫(kù)到異地存儲(chǔ)保留最近14天的備份文件并且每周抽一天實(shí)際做恢復(fù)演練。不要等到服務(wù)器被刪、數(shù)據(jù)庫(kù)出問(wèn)題了才想起備份到時(shí)候哭都來(lái)不及。業(yè)務(wù)監(jiān)控方面除了常規(guī)的服務(wù)器CPU、內(nèi)存、磁盤(pán)告警更關(guān)鍵的是業(yè)務(wù)層面的異常巡檢。舉個(gè)例子寫(xiě)一個(gè)定時(shí)任務(wù)每隔10分鐘掃描一次service_orders表找出狀態(tài)停留在“待派單”超過(guò)30分鐘或者“已派單”超過(guò)20分鐘未接單的訂單直接把訂單號(hào)推送管理員的企業(yè)微信或短信。這種巡檢比看服務(wù)器日志高效得多因?yàn)榭▎尾灰欢〞?huì)報(bào)錯(cuò)但一定影響用戶(hù)對(duì)平臺(tái)的信任度。日志方面建議把框架日志按天分割同時(shí)結(jié)合查詢(xún)?nèi)罩痉治雎齋QL。養(yǎng)老系統(tǒng)的報(bào)表統(tǒng)計(jì)功能比如月度服務(wù)量、護(hù)工績(jī)效如果SQL寫(xiě)得不好非常容易出現(xiàn)慢查詢(xún)拖垮接口。上線后第一天先查一次慢查詢(xún)?nèi)罩景奄Y源開(kāi)銷(xiāo)最大的幾個(gè)接口做一次索引優(yōu)化這個(gè)習(xí)慣能幫你躲開(kāi)后期大量的線上事故。想想我自己實(shí)際做這類(lèi)項(xiàng)目時(shí)最深的體會(huì)是養(yǎng)老系統(tǒng)不是一個(gè)靠炫技就能做好的項(xiàng)目它的核心價(jià)值是穩(wěn)定、可追責(zé)、讓家屬放心。無(wú)論你最終選了ThinkPHP還是Laravel真正決定項(xiàng)目成色的是訂單狀態(tài)有沒(méi)有嚴(yán)格閉環(huán)、數(shù)據(jù)會(huì)不會(huì)丟、消息推送能不能準(zhǔn)時(shí)到達(dá)。把這些基礎(chǔ)做扎實(shí)了再考慮加什么智能硬件、AI判斷之類(lèi)的新功能也不遲。最后分享一個(gè)小技巧任何狀態(tài)變更操作后端都帶著當(dāng)前操作人ID一起落庫(kù)日后再有糾紛你查三分鐘就能理清時(shí)間線。就憑這條你在客戶(hù)心里的專(zhuān)業(yè)度就已經(jīng)超過(guò)大多數(shù)外包團(tuán)隊(duì)了。