約稿平臺:需求拆解到部署上線全記錄)
最近在做一個面向動漫畫家的約稿平臺技術棧是 Python Vue開發(fā)環(huán)境主力用的是 PyCharm。簡單說這個平臺讓插畫師、漫畫家把自己的作品掛出來做個人畫廊讓有約稿需求的客戶直觀地看到畫風、了解接單規(guī)則再在線發(fā)起約稿訂單。后端接口用 Django 搭 REST API前端用 Vue 做頁面和交互兩邊用 JSON 交流整個項目跑起來非常順。這篇文章把項目從需求拆解到數(shù)據(jù)庫設計、后端實現(xiàn)、Vue 頁面、部署上線整套流程都記錄下來包括踩過的坑和排查思路。如果你正想做一個同類的約稿平臺或者只是打算用 Python Vue 練一個完整的全棧項目這份記錄應該能幫你省不少繞路的時間。1. 約稿平臺的核心需求與技術選型解析1.1 先拆需求約稿平臺到底要做什么我說句實在的約稿平臺和普通電商完全不一樣。畫畫這種東西不是標準品同一張插畫不可能量產(chǎn)每一個訂單都是定制的。所以在動工之前我先把業(yè)務邏輯拆了個遍最后落到四條核心主線上展示畫家把自己的作品集展示出來客戶瀏覽畫風、看標簽、看近期作品判斷這位畫師的風格是否是自己想要的。約稿客戶看到滿意的畫師后發(fā)起咨詢描述需求比如“要一張 Q 版頭像雙人互動需要簡單背景”并約定價格和工期。交易談妥后生成訂單客戶先支付定金畫師開始創(chuàng)作過程中上傳草稿或過程圖供確認最終交付源文件和成品。評價訂單完成后雙方互相評價形成畫師信譽沉淀也給后來的客戶提供參考。這個拆解直接決定了我后面所有數(shù)據(jù)表的設計思路。最核心的一點是約稿訂單不是“商品訂單”而是“服務訂單”它要記錄的不只是金額和狀態(tài)還有需求描述、參考圖、節(jié)點反饋、交付物列表這些跟創(chuàng)作過程強相關的東西。如果把約稿當成普通商品用一套 SPU/SKU 邏輯去套后期改起來會非常痛苦。1.2 框架選型Django 還是 Flask后端用 Django 還是 Flask相當于在選擇“自帶輪子”還是“自己造輪子”。我把兩者的差異簡單列個表對比維度DjangoFlask上手難度概念多一些簡單直接自帶功能ORM、Admin 后臺、認證、CSRF幾乎沒有全部自己接適合場景業(yè)務復雜、需要后臺管理的系統(tǒng)輕量 API、原型快速演示生態(tài)組件豐富自由組合度高這個項目我最后選了 Django核心原因有三個。第一約稿平臺有三類角色畫師、約稿客戶、管理員權限模型比較復雜Django 自帶的用戶體系和認證框架能省掉大量底層工作。第二訂單、作品、消息這些數(shù)據(jù)都需要一個后臺給運營人員做審核和查看Django 自帶的 Admin 后臺幾乎是白送的。第三數(shù)據(jù)庫遷移工具很成熟模型字段改來改去也不會亂。Flask 絕對不是一個差選擇它的靈活性和輕量程度在純 API 場景下讓人愛不釋手。但如果我把認證、ORM、后臺管理、審核流程全部自己拼一遍時間和踩坑成本都會成倍增加。既然這個項目的目標是上線運營不是做個課程作業(yè)那么選 Django 是更穩(wěn)的一條路。1.3 前端選型與開發(fā)環(huán)境準備前端我用的是 Vue 的方案。選擇 Vue 的理由很現(xiàn)實Vue 的雙向綁定在處理表單類交互時效率極高而約稿平臺恰恰就是一個“作品展示 復雜表單 狀態(tài)切換”的項目。生態(tài)方面周邊工具鏈和組件庫都非常成熟頁面開發(fā)速度明顯比另一套知名框架快。開發(fā)環(huán)境這塊我直接用 PyCharm 作為主力 IDE這個選擇估計讓不少人意外。其實 PyCharm 的專業(yè)版本本身支持 JavaScript 和 Vue 文件的編輯調(diào)試我把 Django 后端和 Vue 前端放在了同一個工作區(qū)。這樣改完一行 Python 代碼切到 Vue 文件完全不需要在兩個 IDE 之間來回切換斷點調(diào)試也能同時跟 Python 接口和前端請求。當然這個配置有一點要注意PyCharm 對大型 Vue 項目的索引會變慢所以我特意在前端依賴目錄上右鍵標記了“排除”讓 IDE 不索引 node_modules 里的海量文件。不設置的話你會發(fā)現(xiàn)打開項目后風扇狂轉寫代碼都有延遲感。2. 數(shù)據(jù)庫設計與后端核心模塊實現(xiàn)2.1 表結構設計一次把模型想清楚在設計數(shù)據(jù)庫時我反復提醒自己一句話約稿平臺的核心是一張訂單流水的舞臺而不是一張作品表的相冊。所以“作品集”和“訂單”是兩條獨立但互相關聯(lián)的線。最終核心表定了七張用戶表擴展了 Django 內(nèi)置用戶增加手機號、昵稱、頭像、用戶類型整數(shù)區(qū)分畫師與客戶角色、個人簡介。畫師資料表只給畫師角色使用存儲接單狀態(tài)是否開放約稿、起價、平均完成天數(shù)、畫風標簽。作品集表關聯(lián)畫師文字字段有標題、描述、標簽媒體字段有原圖地址、縮略圖地址、圖片寬高比。訂單表核心服務訂單關聯(lián)發(fā)起方、畫師、需求描述、金額、狀態(tài)、定金比例、截止日期、交付物列表。消息表訂單內(nèi)的溝通記錄關聯(lián)發(fā)送人、接收人、所屬訂單。評價表訂單完成后雙方互相評價包含評分和內(nèi)容。附件表保存訂單過程中產(chǎn)生的參考圖、過程圖、最終交付文件統(tǒng)一做版本管理。這里特別想提醒一點訂單狀態(tài)不要用字符串硬編碼在數(shù)據(jù)庫里我用的是整數(shù)狀態(tài)碼具體含義在代碼層的狀態(tài)機里管理。這樣訂單從“待付款”到“已完成”的每一次流轉都能被服務層校驗不會出現(xiàn)臟數(shù)據(jù)。2.2 Django REST Framework 接口設計與 JWT 認證后端我用 Django REST FrameworkDRF搭建 REST API接口風格完全走 REST。核心接口清單大概是這樣的POST /api/auth/register 和 POST /api/auth/token 處理注冊和登錄GET /api/artworks/ 獲取畫廊作品列表支持按畫師、標簽、排序篩選POST /api/orders 發(fā)起約稿訂單PUT /api/orders/{id}/status 畫師更新訂單狀態(tài)GET /api/orders/{id}/messages 獲取訂單內(nèi)消息POST /api/orders/{id}/messages 發(fā)送消息認證方案我選了 JWT用的是現(xiàn)成的 simplejwt 庫配置起來很省心。我把 Access Token 的過期時間設成了兩個小時Refresh Token 設成了七天。老實說這個配置在某些場景下會帶來麻煩比如客戶掛機一段時間回來之后 token 過期了但只要前端配合做靜默刷新體驗上幾乎無感。權限控制是用 DRF 的權限類來做角色校驗的。訂單創(chuàng)建接口要求登錄用戶畫師修改訂單狀態(tài)接口必須校驗收當前登錄者是不是該訂單所屬的畫師。這塊我特別強調(diào)權限校驗絕對不能只靠前端隱藏按鈕那是不可靠的后端在每個寫操作前都必須重新校驗身份。2.3 圖片上傳與對象存儲方案畫廊平臺有兩大物理問題繞不開圖片數(shù)量多交付文件大。我第一版簡單粗暴地把所有圖片存到自己的服務器上結果就是磁盤空間和帶寬很快告急。后來換成了對象存儲方案前端上傳文件時直接傳到存儲服務后端只保存返回的文件 URL。文件路徑我按目錄做了隔離格式是畫師ID/作品集/時間戳_隨機串.jpg。這樣既避免文件名沖突也方便之后做目錄級別的清理和遷移。圖片處理這塊我也做了一層優(yōu)化。原圖必須留一份因為畫師需要交付源文件給客戶但展示圖一定要有縮略圖否則前端瀑布流一下加載幾百張動輒幾 MB 的圖片頁面直接卡死。縮略圖生成放在后端用 Python 圖像處理庫把大圖壓縮到合適尺寸前端滾動時只加載縮略圖。實測下來一張 2MB 原圖和一掌 100KB 縮略圖的加載體驗差距完全是兩個世界的感受。2.4 訂單狀態(tài)機與支付對接訂單狀態(tài)是整個項目里最容易被改壞的部分所以我在動手寫接口之前先設計了一套狀態(tài)機。六個狀態(tài)流轉路徑清晰狀態(tài)碼含義可流轉到1待付款畫師已確認需求22已付款33繪制中44待驗收5 或回 35已完成66已完成且評價終態(tài)有了這套狀態(tài)機客戶取消訂單或者發(fā)生退款糾紛的時候系統(tǒng)只允許從特定狀態(tài)觸發(fā)取消邏輯而不是任何狀態(tài)都能躥到“已取消”。支付對接這里我踩過一個非常實在的坑。個人開發(fā)沒有企業(yè)資質(zhì)正規(guī)第三方支付通道很難直接接入。我最后的方案是先走“平臺訂單 線下支付確認”的模式客戶下單后看到支付信息完成支付后提交支付憑證截圖平臺人工或半自動確認到賬再把訂單狀態(tài)推到“已付款”。如果你也是個人開發(fā)者這個方案最務實。但務必把流程在界面上寫清楚避免客戶付完錢找不到確認入口白等半天。3. Vue 前端實戰(zhàn)畫家畫廊與約稿全流程3.1 前端路由規(guī)劃與頁面結構Vue 前端的路由我用 Vue Router 規(guī)劃了這樣一組頁面首頁熱門作品流、畫師推薦位畫師列表頁按畫風標簽篩選、搜索、排序畫師畫廊詳情頁作品大圖展示、個人簡介、接單規(guī)則、起價和排期信息創(chuàng)建約稿訂單頁選擇規(guī)格、填寫需求描述、上傳參考圖訂單中心區(qū)分“我發(fā)起的訂單”和“我接的訂單”兩個視角消息中心按訂單聚類的聊天界面路由守衛(wèi)我做得比較嚴格。涉及訂單、消息的頁面都必須登錄未登錄直接跳轉登錄頁。這里有一個細節(jié)很多人容易忽略路由守衛(wèi)里不能只看本地有沒有 token因為一張過期或偽造的 token 同樣能騙過前端判斷。穩(wěn)妥做法是在路由守衛(wèi)里不僅檢查 token 存在性還需要用它去請求一次用戶信息請求失敗就清掉本地狀態(tài)并跳登錄。3.2 axios 封裝與用戶登錄態(tài)管理我專門建了 request 模塊來封裝 axios所有與后端的數(shù)據(jù)請求都走這一個入口。封裝內(nèi)容包含三塊baseURL 根據(jù) Vite 的環(huán)境變量區(qū)分開發(fā)和生產(chǎn)環(huán)境請求攔截器統(tǒng)一把 Access Token 加到 Authorization 請求頭響應攔截器統(tǒng)一處理錯誤格式遇到 401 會自動嘗試用 Refresh Token 刷新刷新失敗再退出登錄這里面最容易出問題的是“并發(fā)刷新”。用戶打開多個需要鑒權的請求時如果第一個 401 觸發(fā)了刷新其他幾個接口還拿著舊 token 請求就會出現(xiàn)連環(huán) 401、連環(huán)刷新最后把 Refresh Token 都刷失效。我最后的解法是維護一個刷新 Promise 變量第一個 401 觸發(fā)刷新時把這個 Promise 存起來后續(xù)接口直接復用這個 Promise刷新完成后統(tǒng)一重新發(fā)起請求。這個技巧雖然不起眼但它實打實解決了線上用戶量稍大之后的一大類登錄異常。3.3 畫廊瀑布流與作品詳情畫廊是平臺的門面我用柵格布局做了第一版但畫作的寬高比差異實在太大等高的卡片會把很多圖裁剪得很難看。后來換成了瀑布流方案每次滾動加載一批作品每個卡片根據(jù)圖片自身的寬高比自適應高度。技術實現(xiàn)其實不復雜。后端返回作品列表時同時返回圖片的 width 和 height 字段前端先按已知寬高比渲染占位區(qū)域圖片真正加載完再用真實高度做一次糾正。這樣用戶滾動時不會出現(xiàn)圖片加載完成后突然跳位子的情況。作品詳情頁我特別重視“從看到買”的轉化路徑。大圖預覽、畫師信息、作品標簽這些是基礎項最重要的是要把“發(fā)起約稿”按鈕做短路徑。我直接在詳情頁展示起價和預計排期點擊按鈕后會自動攜帶該作品信息跳轉到訂單表單客戶不用重新填一遍風格參考。很多平臺在這一點上做得很糟糕客戶看中了作品還要在畫廊和訂單頁面之間來回復制粘貼這體驗太勸退了。3.4 約稿下單流程與前端狀態(tài)同步約稿下單是整個業(yè)務閉環(huán)的關鍵在表單設計上我下了不少功夫。需求描述支持多行文本同時提供一組可勾選的需求標簽比如 Q 版、半身像、全身像、場景、多人等客戶勾選完再補充文字。參考圖支持多圖上傳上傳前會在前端做一次壓縮避免大圖直接懟到服務器。風格選擇這部分我做了個貼心設計客戶可以從畫師作品集中直接點選參考風格圖系統(tǒng)會把風格信息和訂單綁定畫師打開訂單就能一眼看清客戶想要的畫風傾向。訂單創(chuàng)建之后前端需要讓客戶和畫師都能及時看到狀態(tài)變化。我這里用了“輪詢 手動刷新”的組合方案。關鍵節(jié)點比如畫師上傳了交付圖、客戶確認了驗收前端每 30 秒輪詢一次訂單詳情普通查看場景則完全交給用戶下拉手動刷新。沒有把整個消息體系做成 WebSocket 實時推送因為對中小型平臺來說成本高、收益低完全沒必要。4. 實操過程中的常見問題與排查記錄4.1 跨域、鑒權與反復出現(xiàn)的 401前后端分離項目幾乎都會碰到跨域配置。我的處理是在 Django 后端配置跨域白名單把開發(fā)環(huán)境的地址加進白名單同時允許攜帶憑證。但真正消耗我時間的是鑒權問題。前端明明帶著 Token后端卻一直返回 401排查了挺久找到兩個原因。第一個是前端部分請求漏掉了請求頭因為之前有寫死的邏輯某些接口在封裝之外單獨請求攔截器沒有覆蓋到。第二個是 Token 字符串膨脹Refresh Token 有效期設得長payload 里又塞了一些可有可無的信息Token 太長之后超過了后端默認的請求頭大小限制。解決辦法其實很簡單統(tǒng)一走 axios 攔截器添加請求頭Refresh Token 改放到 HttpOnly Cookie 里既能有效防止腳本讀取又不會擠占請求頭空間。4.2 大圖上傳慢、內(nèi)存溢出的處理第一版上傳功能很粗糙允許用戶直接選擇 20MB 的大圖然后上傳。結果就是前端頁面卡頓后端磁盤眼看著往下掉。后來做三輪優(yōu)化前端在文件選中之后用 Canvas 做一次縮放超出一定尺寸先壓到不大于 4000 像素寬后端對接收到的圖片做二次校驗超大文件直接拒絕所有圖片上傳走對象存儲服務器不落盤圖片處理這里有個隱蔽的坑用圖像處理庫處理超大圖片時內(nèi)存占用高得驚人。在高并發(fā)下一次處理就可能把服務器的內(nèi)存打滿。我最后的做法是把圖片處理任務放進后臺任務隊列主接口只返回“處理中”狀態(tài)客戶端輪詢獲取處理結果。這樣既保證用戶體驗又保護了服務器穩(wěn)定性。4.3 訂單狀態(tài)變更不同步訂單狀態(tài)變更的“實時可見性”問題我在前期吃了不少苦。畫師上傳完交付圖、把訂單狀態(tài)改成待驗收客戶那邊如果不刷新頁面就會一直停留在舊狀態(tài)誤以為畫師沒有動靜。這個問題本質(zhì)上不是狀態(tài)機錯了而是狀態(tài)變更沒有及時同步給另一方。我的解法是在后端給每個訂單維護一個狀態(tài)更新時間戳前端輪詢時先對比時間戳發(fā)現(xiàn)變化才重新拉取訂單詳情。同時所有狀態(tài)變更都寫入訂單動態(tài)記錄客戶和畫師都能看到類似“畫師已上傳交付圖”“客戶已驗收”這樣的進展。這一改動讓雙方溝通順暢了很多也省了兩個人反復發(fā)消息問進度的隱私。4.4 部署上線時的環(huán)境坑部署階段踩的最多的坑可以歸納成一句話“本機跑得好好的服務器上卡半天?!蔽遗挪橄聛碇饕ㄎ坏饺齻€點靜態(tài)文件收集命令沒跑Django 的后臺頁面樣式全丟對象存儲的域名沒有加進 Django 安全配置的白名單圖片加載被攔服務器上的 Python 依賴版本和本地不一致部分庫的行為悄然變化解決方案其實也很樸素把環(huán)境做成可復現(xiàn)的。用依賴鎖文件導出所有依賴再在服務器上用虛擬環(huán)境統(tǒng)一安裝對應版本保證和本地開發(fā)環(huán)境完全一致。這個習慣在多人協(xié)作或換新服務器時尤其重要。5. 部署上線與后續(xù)迭代建議5.1 前后端分離部署方案部署方案我選擇了一條輕量路線一臺云服務器Django 后端用 gunicorn 啟動Vue 前端打包成靜態(tài)文件交給 Nginx 托管Nginx 把以 /api/ 開頭的請求反向代理到后端端口。一條路徑就把前后端串起來沒有引入額外的容器化工具部署和排查都是直來直去對中小型項目來說很省心。如果后續(xù)并發(fā)量上來了可以先把對象存儲掛上內(nèi)容分發(fā)網(wǎng)絡再用負載均衡掛多臺后端。但現(xiàn)在剛上線階段過早優(yōu)化反而會拖慢迭代速度。5.2 服務器環(huán)境配置實例這里把我部署時的核心三步記錄下來裝好系統(tǒng)里的 Python 虛擬環(huán)境和 Node 環(huán)境把代碼拉下來。后端跑數(shù)據(jù)庫遷移收集靜態(tài)文件用 gunicorn 啟動。前端執(zhí)行構建命令把生成的 dist 目錄放到 Nginx 站點目錄寫一份反向代理配置。反向代理配置里最需要注意的是 location 匹配順序。/ 路徑交給前端靜態(tài)文件/api/ 路徑反代給后端其余靜態(tài)資源走 Nginx 本地緩存。配錯的話最常見的現(xiàn)象是前端頁面能打開但接口全部 404而且這種錯誤從日志上不容易一眼看出來。5.3 進入維護期后可以做的小優(yōu)化項目穩(wěn)定上線后我給自己排了三個優(yōu)先級最高的小迭代方向搜索和標簽系統(tǒng)完善很多客戶找畫師是帶著關鍵詞來的比如“賽璐璐”“厚涂”“Q 版”支持多標簽組合篩選對成單轉化非常有幫助。消息提醒外擴除了站內(nèi)輪詢增加郵件或公眾號模板消息通知畫師有新訂單時能第一時間響應減少流失。畫師數(shù)據(jù)看板在畫師主頁增加本月接單量、成交率、平均工時這些指標既方便畫師管理自己的排期也能讓客戶更信任畫師的職業(yè)能力。這三個功能對技術的要求都不高但每一件都能實實在在提升平臺的專業(yè)感和用戶粘性。這次做完整套項目我的體會是全棧項目想跑通不難真正花時間的是把細節(jié)縫好。你可能在數(shù)據(jù)表設計和狀態(tài)機上的時間比寫頁面還多但正是這些“看不到”的地方?jīng)Q定了平臺后續(xù)能不能扛住真實用戶。技術選型上Python Vue 目前依然是中小型項目里性價比很高的組合Django 和 Flask 各有各的位置關鍵是先想清楚自己需要多少現(xiàn)成的輪子。如果讓我重新開工我依然不會把功能排得太滿而是先把“瀏覽作品、發(fā)起約稿、支付確認、交付驗收、評價”這條主線跑通再回頭補邊角。這條路我已經(jīng)替你踩過一遍了順著走下去能少繞的彎我都給你標出來了。