實戰(zhàn)解析)
養(yǎng)過大型犬的朋友應(yīng)該都有過這種體驗出差三天家里狗子快把沙發(fā)拆了。約朋友上門幫忙吧欠人情不說人家也不一定有時間找家政保潔吧人家根本不接遛狗這種活。我當(dāng)時接觸到的寵物社區(qū)里類似的求助信息幾乎每天都有但靠人肉匹配效率實在太低了——這大概就是同城上門喂遛寵物系統(tǒng)最樸素的出發(fā)點。后來我自己上手做了這么一套系統(tǒng)技術(shù)棧選的是Java SpringBoot Vue3 MyBatis數(shù)據(jù)庫用MySQL前后端徹底分離。整套源碼從需求拆解到上線部署都走了一遍過程中踩了不少坑這篇文章算是把我認為最有價值的落地經(jīng)驗整理出來包括業(yè)務(wù)模塊怎么劃分、表結(jié)構(gòu)怎么設(shè)計訂單狀態(tài)機、為什么下單一定得加事務(wù)和唯一索引、Vue3這邊怎么跟后端對齊狀態(tài)、以及上線前要注意的邊界問題。如果你正準備做同城服務(wù)、上門服務(wù)、預(yù)約類系統(tǒng)或者只是想看SpringBoot Vue3 MyBatis這套組合在真實項目中怎么配合這篇文章應(yīng)該能幫你省掉不少試錯時間。1. 為什么同城喂遛能成這個系統(tǒng)到底在解決什么匹配問題1.1 用戶兩端的需求拆解任何同城服務(wù)類系統(tǒng)本質(zhì)都是撮合交易。喂遛寵物看似簡單但它的特殊性在于供給端和需求端都極度分散且信任門檻很高。需求端是寵物主。這類人的典型場景是臨時出差、加班太晚回不了家、或者寵物需要按時喂藥、按時遛放。他們對服務(wù)的要求不是“把狗帶出去走一圈”這么簡單而是希望服務(wù)者能按寵物習(xí)慣操作——幾點喂飯、遛多久、遇人要不要避開、狗糧放在哪、貓咪膽小該不該強行互動。這些細節(jié)會直接影響寵物主對平臺的信任。供給端是服務(wù)者。他們可能是喜歡寵物的學(xué)生、自由職業(yè)者、退休阿姨也可能是在寵物店兼職的店員。他們的訴求也很明確接單方便、價格透明、時間自由、并且不會因為服務(wù)糾紛被平臺無理由扣錢。所以這個系統(tǒng)表面上做的是“下單-接單-完成-評價”實際上做的是把寵物主的知識經(jīng)驗喂養(yǎng)習(xí)慣和服務(wù)者的時間技能高效匹配起來。這個認知直接決定了后續(xù)的表設(shè)計寵物檔案必須是結(jié)構(gòu)化且可編輯的訂單必須冗余一份當(dāng)時的服務(wù)信息評價和投訴必須留痕。1.2 業(yè)務(wù)閉環(huán)從下單到評價的完整鏈路我梳理出的最小業(yè)務(wù)閉環(huán)是這樣的寵物主注冊登錄完善寵物檔案品種、年齡、喂食習(xí)慣、遛放偏好、注意事項瀏覽同城服務(wù)者列表按區(qū)域、評分、服務(wù)類型篩選選定服務(wù)者后選擇服務(wù)日期和時段提交訂單服務(wù)者端看到待接單訂單決定接單或拒絕接單后寵物主可以在線查看服務(wù)時間服務(wù)者按約定上門完成任務(wù)服務(wù)完成后雙方互相評價訂單進入歷史記錄你可能會問為什么不是“寵物主直接選時間下單平臺自動分配服務(wù)者”因為喂遛服務(wù)不是打車每個寵物對服務(wù)者的要求不同有些貓怕生人有些狗力氣大需要壯一點的人來牽。必須給人選擇的余地讓寵物主手動挑服務(wù)者成功率反而更高。1.3 系統(tǒng)邊界MVP先砍掉哪些功能做需求的時候最容易失控。我一開始也想把在線支付、實時定位、后臺管理系統(tǒng)、優(yōu)惠券、會員體系全做進去后來被現(xiàn)實勸退了。MVP階段的取舍是保留完整的訂單閉環(huán)和雙向評價砍掉支付線下交易、砍掉實時定位追蹤服務(wù)開始后手動確認、砍掉平臺客服工單、砍掉優(yōu)惠券。理由很簡單這些功能不影響核心流程跑通而且支付涉及商戶資質(zhì)定位涉及地圖服務(wù)key和服務(wù)器成本這些都可以在驗證需求之后再加。提示如果你也在做類似的同城服務(wù)類系統(tǒng)記住一個原則——先證明有人愿意用再考慮怎么賺錢。MVP的“砍”不是功能缺失而是戰(zhàn)略選擇。2. 技術(shù)選型復(fù)盤SpringBootVue3MyBatis這套組合為什么合適2.1 Java后端為什么選SpringBoot而不是其他這里完全沒有貶低其他語言的意思但Java在“同城服務(wù)”這個場景里確實有優(yōu)勢。首先是生態(tài)成熟Spring全家桶對事務(wù)、權(quán)限、定時任務(wù)、消息隊列都有現(xiàn)成方案其次是做這類項目的團隊里Java開發(fā)者的占比太高了招人或者后續(xù)接手都方便。具體到SpringBoot我比較看重的是自動配置和起步依賴。一個新項目從零到能跑起來寫一個Controller加一個Mapper大概十分鐘就夠了。它不像傳統(tǒng)SSM那樣需要我手動裝配一堆XML配置也不需要關(guān)心Tomcat怎么部署直接打成jar包就能跑。在這個系統(tǒng)里SpringBoot承擔(dān)的工作可以拆成幾塊Spring Web提供RESTful API接口Spring Validation處理請求參數(shù)校驗Spring AOP實現(xiàn)日志記錄和部分鑒權(quán)邏輯Spring Task處理定時任務(wù)比如超時未支付訂單自動關(guān)閉MyBatis Starter數(shù)據(jù)庫訪問層集成2.2 Vue3組合式API的取舍前端我選了Vue3而不是Vue2核心原因是組合式API對復(fù)雜交互的代碼組織更友好。這個系統(tǒng)里有寵物主端和服務(wù)者端兩種視角同一個組件的邏輯復(fù)用訴求很強比如訂單列表兩個角色都會用到但操作按鈕完全不同。用Vue2的Options API寫我需要把data、methods、computed、watch分開一個頁面反復(fù)切來切去用Vue3的setup語法糖我可以在一個函數(shù)里把狀態(tài)和邏輯挨著寫再通過自定義組合式函數(shù)把訂單相關(guān)邏輯抽出來復(fù)用。打個比方Options API像一個按菜譜步驟操作的廚房每個環(huán)節(jié)分得很清但東西散落各處組合式API更像一個開放式廚房我把切菜、炒菜、裝盤的全流程放在一個操作臺上想復(fù)用直接整段搬走。UI組件庫方面項目用了Element Plus。它對Vue3支持比較完善表格、表單、對話框、時間選擇器這些后臺管理常用的組件都覆蓋了開發(fā)效率比手寫樣式高很多。如果你的頁面偏C端展示、對視覺要求較高可以考慮TailwindCSS或者Naive UI但做管理后臺和簡易C端Element Plus足夠。2.3 MyBatis和MySQL復(fù)雜查詢與事務(wù)控制的平衡為什么用MyBatis而不是JPA我的理由其實寫在標題里了MyBatis對SQL有完全的控制權(quán)。這個系統(tǒng)的查詢條件非常靈活——訂單列表要按狀態(tài)、日期、服務(wù)類型、城市、關(guān)鍵字組合篩選寵物主和服務(wù)者的訂單列表甚至需要動態(tài)拼接不同的where條件。用MyBatis的XML文件寫動態(tài)SQL改起來直接、出問題時定位也快。MySQL則負責(zé)穩(wěn)定的數(shù)據(jù)落地。訂單狀態(tài)機、用戶余額、寵物檔案這些核心數(shù)據(jù)都需要強一致性和事務(wù)控制MySQL的InnoDB引擎在這個場景下成熟可靠。唯一需要留意的版本是8.0以上對JSON類型和窗口函數(shù)的支持更好我建議新項目直接用8.0。3. 表結(jié)構(gòu)與訂單狀態(tài)機喂遛業(yè)務(wù)最核心的“地基”3.1 核心表關(guān)系用戶-寵物-服務(wù)者-訂單整個系統(tǒng)我設(shè)計了六張核心表互相之間的關(guān)聯(lián)關(guān)系相對清晰表名用途關(guān)鍵字段user用戶表區(qū)分寵物主和服務(wù)者角色id, phone, password, role, nickname, avatar, area_codepet寵物檔案表id, user_id, name, species, breed, weight, age_scope, personality, feeding_note, walk_rulesservice_provider服務(wù)者信息表id, user_id, service_type, price_per_hour, available_time, rating, review_count, skill_tagsorder_info訂單主表id, order_no, user_id, pet_id, provider_id, service_date, service_slot, status, amount, address, notereview評價表id, order_id, from_user_id, to_provider_id, rating, contentfavorite收藏表可選id, user_id, provider_iduser表里我用role字段區(qū)分角色0是寵物主1是服務(wù)者。一個賬號是否可以同時是兩種角色理論上可以但為了簡化權(quán)限邏輯MVP里我規(guī)定一個賬號只能選一個角色后續(xù)如果有需要再支持角色切換。pet表是整個服務(wù)的“說明書”。我把喂養(yǎng)習(xí)慣、遛放規(guī)則、懼怕什么、需不需要戴嘴套這些都做了字段甚至文本類型的note字段寵物主填寫越詳細服務(wù)者的操作越有依據(jù)糾紛越少。真實項目里這塊甚至可以做成動態(tài)表單讓寵物主按模板填寫減少文字描述的隨意性。3.2 訂單狀態(tài)機從待支付到已完成的狀態(tài)流轉(zhuǎn)訂單表里最重要的是status字段它驅(qū)動了整個系統(tǒng)的所有交互。我定義的狀態(tài)如下0-待支付下單未付款 1-待接單已付款/確認下單等待服務(wù)者接單 2-已接單服務(wù)者確認接受 3-服務(wù)中服務(wù)者點擊開始服務(wù) 4-已完成服務(wù)者點擊完成 5-已取消寵物主或服務(wù)者取消 6-售后中已完成但雙方產(chǎn)生糾紛這里有一個關(guān)鍵設(shè)計決策待支付和待接單是分開的。為什么不能下單直接進入待接單因為線下交易場景下如果寵物主沒付款就占用了服務(wù)者一個時段服務(wù)者一打開看到三四個訂單但都是“掛了沒付錢”的體驗非常差。我要求下單后30分鐘內(nèi)必須支付否則定時任務(wù)自動把訂單狀態(tài)改為已取消并釋放時段。狀態(tài)流轉(zhuǎn)的合法性我做了狀態(tài)機的約束后端在更新訂單時會校驗當(dāng)前狀態(tài)是否允許目標狀態(tài)。比如服務(wù)者不能在待支付狀態(tài)下接單寵物主不能在服務(wù)中狀態(tài)隨意取消。這個校驗我放在Service層做了統(tǒng)一處理而不是在Controller里做一層防止繞過某個接口直接改狀態(tài)。3.3 表設(shè)計中的三個防坑細節(jié)第一個坑是訂單表必須冗余快照字段。寵物主可能在下單后修改了寵物檔案如果把喂養(yǎng)習(xí)慣直接關(guān)聯(lián)pet表服務(wù)者看到的信息就可能和下單時的信息不一致。我在訂單表里冗余了pet_name、pet_breed、feeding_note快照字段下單那一刻把寵物檔案復(fù)制一份歷史訂單永遠保留下單時的摘要。第二個坑是時間段必須原子化。同城喂遛服務(wù)存在明確的檔期沖突同一個服務(wù)者不能在同一時間接兩單。我在訂單表加了唯一索引provider_id, service_date, service_slotservice_slot是預(yù)先定義好的時段比如上午9:00-11:00、下午14:00-16:00。這樣從數(shù)據(jù)庫層就直接擋住了重復(fù)接單比在Service層用鎖更保險。第三個坑是邏輯刪除優(yōu)先。用戶注銷、寵物檔案刪除這類操作我建議都采用邏輯刪除deleted字段標記而不是物理刪除。原因很簡單訂單表關(guān)聯(lián)了歷史數(shù)據(jù)如果你物理刪除了寵物或用戶記錄歷史訂單的關(guān)聯(lián)就會斷裂后續(xù)對賬、糾紛仲裁都沒有依據(jù)。4. 后端實現(xiàn)的關(guān)鍵細節(jié)認證、事務(wù)鎖與動態(tài)SQL4.1 JWT登錄與接口鑒權(quán)認證選型上我用了JWTJSON Web Token方式主要因為它天然適合前后端分離。服務(wù)器不需要保存會話狀態(tài)用戶登錄成功后后端簽發(fā)一個token返回給前端前端在后續(xù)請求的Header里帶上Authorization字段后端通過攔截器解析token得到用戶信息。關(guān)鍵實現(xiàn)里需要注意兩點一是攔截器只負責(zé)解析和放行具體的權(quán)限判斷應(yīng)該在代碼里做。我寫了一個簡單的攔截器從請求頭里取出token解析出userId放入ThreadLocal方便后續(xù)Service層獲取。如果token缺失或過期直接返回401。角色判斷則在需要區(qū)分寵物主和服務(wù)者的接口里通過注解或代碼判斷。二是token要設(shè)置合理的過期時間。這個系統(tǒng)面對的是低頻使用的場景用戶可能一周才打開一次app/網(wǎng)頁如果token設(shè)置30分鐘過期會讓用戶體驗極其糟糕。我設(shè)置了7天有效每次請求時向前滑動續(xù)期。安全性上犧牲了一點但對這類非金融類業(yè)務(wù)來說體驗優(yōu)先是可接受的。public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (StringUtils.isBlank(token)) { throw new BusinessException(401, 未登錄); } // 解析token設(shè)置用戶上下文 Long userId JwtUtil.parseToken(token); UserContext.set(userId); return true; } }4.2 下單接口事務(wù)唯一索引解決并發(fā)重復(fù)接單下單是整個系統(tǒng)里最容易出并發(fā)問題的接口。想象一下這個場景服務(wù)者在同一時間段被兩個寵物主同時下單服務(wù)者自己也很糾結(jié)手一抖兩單都接了。如果服務(wù)端不做并發(fā)控制就會導(dǎo)致一個服務(wù)者同時接了兩個重疊的訂單。我采用的方案是數(shù)據(jù)庫唯一索引 事務(wù)組合。下單時先在一個事務(wù)里執(zhí)行校驗訂單參數(shù)寵物主信息、寵物檔案、服務(wù)者信息必須存在且有效校驗服務(wù)者該時段是否已被占用——這條不是簡單的select而是直接嘗試insert一條訂單記錄插入訂單時若唯一索引provider_id, service_date, service_slot沖突數(shù)據(jù)庫拋異常事務(wù)回滾這一段代碼看起來平平無奇但它把并發(fā)問題從“代碼層面解決”變成了“數(shù)據(jù)庫層面兜底”。即使兩個請求同時進來只有一個能插入成功另一個必然因唯一索引沖突而失敗事務(wù)直接回滾。Override Transactional(rollbackFor Exception.class) public Long createOrder(CreateOrderRequest request) { // 1. 參數(shù)校驗 validateOrderParams(request); // 2. 檢查服務(wù)者和寵物信息 Pet pet petMapper.selectById(request.getPetId()); ServiceProvider provider providerMapper.selectByUserId(request.getProviderId()); // 3. 構(gòu)建訂單并插入 OrderInfo order buildOrder(pet, provider, request); try { orderMapper.insert(order); } catch (DuplicateKeyException e) { throw new BusinessException(該服務(wù)者當(dāng)前時段已被占用請換個時段試試); } // 4. 返回訂單號 return order.getId(); }4.3 MyBatis動態(tài)SQL處理訂單篩選與分頁訂單列表是查詢最復(fù)雜的接口因為篩選條件完全是動態(tài)的。寵物主視角可能想看“待接單的訂單”“本月的歷史訂單”“某個服務(wù)者的訂單”服務(wù)者視角可能想看“某一天的訂單”“某個狀態(tài)的所有訂單”。如果用Java代碼拼SQL業(yè)務(wù)一復(fù)雜就是災(zāi)難。MyBatis的XML動態(tài)SQL幫了大忙。它的where標簽可以自動去除多余的和ANDif標簽根據(jù)條件拼接查詢片段。我寫了一個通用的訂單查詢Mapper把用戶類型、狀態(tài)、日期范圍、服務(wù)者ID、寵物主ID全都作為可選條件傳入一個Query對象返回分頁數(shù)據(jù)。分頁這里我用了MyBatis的PageHelper插件基本原理是自動攔截SQL并在末尾追加LIMIT。需要注意的一個坑是PageHelper分頁時如果有多表聯(lián)查或者自定義返回字段一定要在查詢之前設(shè)置PageHelper.startPage且只對緊接著的第一條查詢生效否則會有數(shù)據(jù)錯亂的問題。另外如果你做了嵌套結(jié)果映射resultMap的collectionPageHelper只對主查詢做物理分頁子查詢的結(jié)果集不受分頁影響這個要記住。4.4 時間字段的序列化處理這個坑值得單獨拿出來講前后端時間對接。后端Java用了LocalDateTimeMyBatis可以正常映射到MySQL的datetime類型但到了JSON序列化這里就出事了。默認的Jackson會把LocalDateTime序列化成一大串?dāng)?shù)組前端根本沒法直接用。我統(tǒng)一做了兩件事第一在application.yml里配置了全局時間格式spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8第二對前端提交的日期字符串在后端DTO里用DateTimeFormat注解明確指定格式避免因為毫秒級時間戳或者不同格式的字符串導(dǎo)致解析失敗。實際使用中前端從下拉框選擇的服務(wù)日期我統(tǒng)一用yyyy-MM-dd格式傳回后端后端用LocalDate接收這樣比用LocalDateTime更準——因為日期只需要精確到天時段是單獨一個字段。避免把“2025-06-20 00:00:00”這種時間當(dāng)業(yè)務(wù)時間存儲后續(xù)做分組統(tǒng)計會出一堆邊界問題。5. Vue3前端如何把訂單狀態(tài)“管”起來5.1 路由與菜單結(jié)構(gòu)前端項目的目錄規(guī)劃我遵循了“按模塊劃分”的思路。路由分三層結(jié)構(gòu)登錄注冊頁、寵物主端主框架、服務(wù)者端主框架。兩個角色的菜單不同所以我在布局組件里根據(jù)當(dāng)前用戶角色動態(tài)渲染側(cè)邊欄。路由示例const routes [ { path: /login, component: Login }, { path: /pet-owner, component: Layout, meta: { role: PET_OWNER }, children: [ { path: providers, component: ProviderList }, { path: pets, component: PetManage }, { path: orders, component: OrderList } ] }, { path: /provider, component: Layout, meta: { role: PROVIDER }, children: [ { path: orders, component: ProviderOrderList }, { path: schedule, component: ProviderSchedule } ] } ]這里用了路由元信息meta.role來標記每個路由允許的角色再配合全局前置守衛(wèi)做訪問控制用戶未登錄跳轉(zhuǎn)登錄頁登錄了但訪問了不屬于自己角色的頁面重定向到自己的首頁。這樣做比在每一個頁面里寫if判斷要省事得多也避免漏判。5.2 Pinia存儲用戶角色與tokenVue3的狀態(tài)管理我選了Pinia它是Vuex的官方替代方案API更簡潔沒有mutations這個概念直接改state就行。我建了一個user store保存token、用戶基本信息、當(dāng)前角色。Pinia在刷新頁面后狀態(tài)會丟失這是所有前端狀態(tài)管理的通病。解決辦法很簡單把token存在localStorage頁面刷新后讀取localStorage再重新構(gòu)建store狀態(tài)。但要注意不要把所有用戶信息都塞localStorage敏感信息盡量只存token用戶信息每次進入系統(tǒng)時通過接口重新獲取。// store/user.js export const useUserStore defineStore(user, { state: () ({ token: localStorage.getItem(token) || , userInfo: null, role: localStorage.getItem(role) || }), actions: { async login(payload) { const res await api.login(payload); this.token res.data.token; this.role res.data.role; localStorage.setItem(token, this.token); localStorage.setItem(role, this.role); }, logout() { this.token ; this.userInfo null; this.role ; localStorage.clear(); router.push(/login); } } });5.3 Axios攔截器與頁面狀態(tài)對齊Axios攔截器是前后端對接的“中間件”它統(tǒng)一處理三件事請求頭注入token、響應(yīng)中統(tǒng)一處理業(yè)務(wù)錯誤碼、401跳轉(zhuǎn)登錄。后端返回格式我統(tǒng)一用{ code: 200, message: success, data: {} }所以攔截器里判斷code而不是HTTP狀態(tài)碼這是很多前后端協(xié)作項目里最容易產(chǎn)生分歧的地方。我強烈建議HTTP狀態(tài)碼只表示傳輸層的成功失敗業(yè)務(wù)層的成功失敗一律用code字段。前后端都遵循這個約定之后聯(lián)調(diào)時“接口返回了500但頁面沒提示”這類問題就直接消失了。還有一個細節(jié)后端返回的訂單狀態(tài)是數(shù)字前端需要用枚舉表來映射成中文顯示。這個映射關(guān)系如果前后端各維護一份時間久了必然對不上。我建議后端在util包提供一個OrderStatusEnum類最終通過字典接口下發(fā)前端只需要渲染前查一次字典。5.4 接單交互中的輪詢與狀態(tài)刷新服務(wù)者端的訂單列表有個很實際的問題寵物主下單后服務(wù)者端需要實時看到新訂單。全上WebSocket對MVP來說成本偏高我采用了簡單的輪詢方案——服務(wù)者訂單列表頁每30秒調(diào)一次查詢接口把status非終態(tài)待支付、待接單、已接單、服務(wù)中的訂單拉取回來前端合并去重后刷新列表。這個方案不是最優(yōu)的實時方案但勝在簡單可靠。如果你希望做真正的實時推送可以后續(xù)在系統(tǒng)里集成WebSocket或者Server-Sent Events把“有新訂單”事件推送給服務(wù)者端。6. 從Demo到上線部署、索引與待擴展能力6.1 打包部署前端Nginx與后端jar前后端分離項目部署上我的習(xí)慣做法是前端打包成靜態(tài)文件交給Nginx后端SpringBoot打成jar包通過systemd守護運行。Nginx里兩個核心配置一是root指向前端dist目錄二是/api路徑代理到后端的SpringBoot服務(wù)。location /api/ { proxy_pass http://127.0.0.1:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }部署時有一個常見坑前端靜態(tài)頁面的路由模式。如果用history模式用戶在瀏覽器直接刷新某個子路由頁面Nginx就會返回404。需要加一行try_files配置讓所有非靜態(tài)文件請求都回退到index.html。location / { root /var/www/pet-park/dist; try_files $uri $uri/ /index.html; }另外環(huán)境配置一定要分dev和prod兩份數(shù)據(jù)庫連接、Redis地址、日志級別分開管理。我見過很多人把測試庫密碼打包進生產(chǎn)jar才慌慌張張四處交代這種事真的不能發(fā)生在正式項目里。6.2 上線前必加的索引與約束項目在開發(fā)環(huán)境跑得很歡數(shù)據(jù)量一上來就卡大部分問題出在索引缺失。我上線前梳理了一遍所有查詢場景的where條件和排序字段加了以下索引order_info表(provider_id, service_date, service_slot) 唯一索引防并發(fā)重復(fù)接單order_info表(user_id, status) 普通索引服務(wù)寵物主端訂單列表order_info表(order_no) 唯一索引按訂單號查詢pet表(user_id) 普通索引查詢用戶寵物列表service_provider表(area_code, service_type) 復(fù)合索引服務(wù)者列表篩選索引不是越多越好而是要跟查詢匹配。我在開發(fā)中發(fā)現(xiàn)有人不加區(qū)分地給所有字段加索引結(jié)果寫入性能明顯變慢這就是過度索引的教訓(xùn)。一般先看慢查詢?nèi)罩驹俳Y(jié)合業(yè)務(wù)場景補索引比拍腦袋建索引靠譜得多。6.3 地圖/支付/消息推送的擴展預(yù)留最后聊聊這個系統(tǒng)上線后可以怎么擴展也算是我踩過需求坑之后的反思地圖服務(wù)。同城服務(wù)天然需要距離篩選和定位。在服務(wù)者表預(yù)留area_code字段是最基礎(chǔ)的進階做法是接入地圖服務(wù)讓寵物主按距離排序服務(wù)者。實際開發(fā)中建議用WebService API來算兩點間的駕車/步行距離而不要直接在前端暴露key。在線支付。MVP階段我可以接受線下交易但一旦單量上來沒有支付流水就是一筆糊涂賬。我預(yù)留了payment表和order_info.payment_status字段后續(xù)接微信支付、支付寶只需要加一個支付渠道表和回調(diào)通知處理不需要動訂單主流程。消息推送。我前面說的30秒輪詢是個臨時方案規(guī)?;筮€是要上真正的推送。小程序場景用訂閱消息App場景用廠商推送通道Web場景用WebSocket或SSE。擴展的時候注意推送內(nèi)容要包含訂單號和服務(wù)地址接收者點擊消息后要能直接跳到對應(yīng)的訂單詳情頁。我個人實測下來這個同城喂遛寵物系統(tǒng)從設(shè)計到跑通大約花了兩周真正耗時的是反復(fù)打磨狀態(tài)機和并發(fā)控制。如果重來一次我會在需求階段就把狀態(tài)機畫得再細一些把哪些狀態(tài)允許取消、取消后押金怎么退、服務(wù)超時誰來標記等問題一次想清楚這些業(yè)務(wù)細節(jié)不提前鎖死寫代碼的時候就會一直返工。你如果也要做類似的預(yù)約類系統(tǒng)希望這篇筆記能幫你少走幾步彎路。