產(chǎn)品商城畢業(yè)設(shè)計(jì)項(xiàng)目:從表結(jié)構(gòu)到訂單事務(wù)全解析)
馬上又到畢業(yè)設(shè)計(jì)選題的季節(jié)了每年這時(shí)候都有不少計(jì)算機(jī)專業(yè)的同學(xué)在為一件事發(fā)愁能不能找到一套既能跑得起來、又能講得明白、還能扛得住答辯追問的項(xiàng)目。如果你正在為電商類Web系統(tǒng)撓頭看到“PHP阿克蘇地區(qū)農(nóng)產(chǎn)品商城網(wǎng)站系統(tǒng)”這個(gè)源碼編號78372應(yīng)該會覺得思路開闊了不少。這是一套以PHP為核心技術(shù)棧開發(fā)的B2C商城系統(tǒng)產(chǎn)品場景鎖定阿克蘇特色農(nóng)產(chǎn)品覆蓋用戶前臺購物、后臺運(yùn)營管理、管理員系統(tǒng)配置三大板塊商品、分類、購物車、訂單、地址、公告、數(shù)據(jù)統(tǒng)計(jì)這些電商基本鏈路全部打通。它適合計(jì)算機(jī)相關(guān)專業(yè)的學(xué)生拿來做畢業(yè)設(shè)計(jì)也適合想快速搭一個(gè)區(qū)域農(nóng)產(chǎn)品電商練手站的PHP開發(fā)者參考。說實(shí)話這種項(xiàng)目選題的價(jià)值不完全在代碼量而在于你有沒有把業(yè)務(wù)需求、表結(jié)構(gòu)設(shè)計(jì)和代碼實(shí)現(xiàn)串成一條完整的線。這篇文章我就拿這套源碼的設(shè)計(jì)思路來說一說從需求怎么拆、表怎么建到每個(gè)核心模塊的代碼怎么寫、坑在哪里盡量一次性講透。1. 項(xiàng)目定位與核心需求拆解1.1 阿克蘇農(nóng)產(chǎn)品的電商痛點(diǎn)先聊一個(gè)很實(shí)際的問題為什么偏偏是阿克蘇地區(qū)而不是隨便做一個(gè)通用商城。阿克蘇的冰糖心蘋果、薄皮核桃、紅棗、香梨這些農(nóng)產(chǎn)品品質(zhì)是實(shí)打?qū)嵉牡L期以來主要靠線下批發(fā)和熟人介紹推廣銷售半徑非常有限。要把這些農(nóng)產(chǎn)品賣到外地平臺就得解決信任、溯源、規(guī)格標(biāo)準(zhǔn)化這些核心問題。用戶打開網(wǎng)站第一眼要能確認(rèn)這是阿克蘇原產(chǎn)地商品所以首頁要突出產(chǎn)地介紹、特色推薦、產(chǎn)品故事下單的時(shí)候系統(tǒng)要支持按箱、按盒、按斤等多單位展示對應(yīng)不同的價(jià)格和庫存。這些細(xì)節(jié)點(diǎn)看著不大卻直接影響整個(gè)系統(tǒng)功能設(shè)計(jì)。做畢業(yè)設(shè)計(jì)容易犯一個(gè)錯(cuò)把電商系統(tǒng)想成“商品列表購物車下單”三個(gè)頁面做完以為完事了結(jié)果答辯時(shí)老師一問訂單狀態(tài)怎么流轉(zhuǎn)、庫存負(fù)數(shù)了怎么辦、權(quán)限怎么控制當(dāng)場卡殼。所以這篇拆解我不會跳著講而是把每一個(gè)環(huán)節(jié)的來龍去脈都攤開從需求推導(dǎo)到表結(jié)構(gòu)再落到代碼層這樣你拿著源碼去答任何問題都有底氣。1.2 商城系統(tǒng)功能模塊拆解這套系統(tǒng)從使用角色上切可以分三個(gè)端來看。用戶端前臺商城主要包含注冊登錄、商品分類瀏覽、關(guān)鍵詞檢索、商品詳情展示、加入購物車、訂單結(jié)算、訂單列表查看、個(gè)人資料修改、收貨地址維護(hù)。這是用戶每天直接打交道的部分界面操作要順手邏輯鏈路要完整。后臺管理端分兩種角色商家運(yùn)營人員和管理員。商家負(fù)責(zé)商品分類管理、商品上架下架、庫存調(diào)整、訂單發(fā)貨、公告發(fā)布管理員在商家基礎(chǔ)上額外擁有用戶管理、商家賬號管理、訂單總覽、數(shù)據(jù)統(tǒng)計(jì)、系統(tǒng)配置等權(quán)限。有些同學(xué)看到“三個(gè)端”就慌了覺得工作量大到做不完。我的建議是不用真的拆三個(gè)獨(dú)立系統(tǒng)一套后臺代碼用角色字段控制菜單和操作權(quán)限就夠了。前臺一套、后臺一套后臺里面做菜單權(quán)限的差異化展示既滿足多角色的業(yè)務(wù)需求又不會把畢業(yè)設(shè)計(jì)周期拖到失控。把模塊邊界梳理清楚后面數(shù)據(jù)庫表怎么設(shè)計(jì)、路由怎么規(guī)劃、控制器怎么分工都能順理成章地定下來。2. 技術(shù)選型與系統(tǒng)架構(gòu)設(shè)計(jì)2.1 為什么選PHP而不是Java或Python先說我的結(jié)論P(yáng)HP做這類商城系統(tǒng)開發(fā)效率高、部署成本低、學(xué)習(xí)曲線平滑尤其適合畢業(yè)設(shè)計(jì)這種短周期項(xiàng)目。Java企業(yè)級框架要配一堆環(huán)境本地跑起來的內(nèi)存和耐心消耗都不小Python Django倒也可以但很多同學(xué)光配虛擬環(huán)境和依賴包就折騰一兩天了。PHP天生就是為動態(tài)網(wǎng)頁設(shè)計(jì)的語言一臺服務(wù)器裝上PHP解釋器和MySQL半小時(shí)就能把商城環(huán)境跑通這對接下來的開發(fā)和調(diào)試絕對是實(shí)打?qū)嵉氖⌒?。語言版本建議直接用PHP 7.4以上最好上PHP 8.x。PHP 8的性能提升非常明顯而且新語法寫起來更簡潔。但這里要提醒一句如果拿到的源碼是老格式的先做一次語法兼容檢查PHP 7.2以下有些寫法跟新版本不兼容貿(mào)然升級可能會報(bào)一大堆錯(cuò)誤。開發(fā)環(huán)境推薦PHPStudy或XAMPP這類集成環(huán)境一鍵啟動Apache/Nginx、MySQL、PHP不用手動改配置。部署階段可以換寶塔面板LinuxNginxPHPMySQL的組合操作界面友好很多同學(xué)在云服務(wù)器上也能獨(dú)立完成部署??蚣苓x型方面ThinkPHP是國內(nèi)PHP項(xiàng)目使用率很高的框架路由、ORM、模板引擎、驗(yàn)證器都內(nèi)置好了開發(fā)效率非常高。如果源碼里能看到application、public、thinkphp這類目錄基本就是ThinkPHP系列。答辯的時(shí)候框架本身就是一個(gè)可以展開講的亮點(diǎn)——為什么用框架、框架解決了什么問題、MVC分層的好處是什么這些問題準(zhǔn)備好比單純背代碼有價(jià)值得多。2.2 數(shù)據(jù)庫設(shè)計(jì)與核心表結(jié)構(gòu)商城系統(tǒng)的地基在數(shù)據(jù)庫表設(shè)計(jì)表設(shè)計(jì)好了后面的功能基本都是增刪改查。這個(gè)項(xiàng)目的核心表大概有這些user用戶表字段包括用戶ID、昵稱、手機(jī)號、登錄密碼哈希、頭像、注冊時(shí)間、狀態(tài)goods_category商品分類表字段包括分類ID、分類名、上級分類ID、排序值goods商品表字段包括商品ID、分類ID、商品名、主圖、輪播圖組、詳情描述、庫存、價(jià)格、原價(jià)、單位、產(chǎn)地、銷量、上架狀態(tài)、創(chuàng)建時(shí)間cart購物車表字段包括購物車ID、用戶ID、商品ID、數(shù)量、加購時(shí)間address收貨地址表字段包括地址ID、用戶ID、收貨人姓名、聯(lián)系電話、省市區(qū)、詳細(xì)地址、是否默認(rèn)order訂單表字段包括訂單ID、訂單編號、用戶ID、商品總金額、運(yùn)費(fèi)、實(shí)付金額、訂單狀態(tài)、收貨人信息、下單時(shí)間、支付時(shí)間、發(fā)貨時(shí)間order_detail訂單明細(xì)表字段包括明細(xì)ID、訂單ID、商品ID、商品快照、單價(jià)、數(shù)量、小計(jì)notice公告表字段包括公告ID、標(biāo)題、內(nèi)容、發(fā)布時(shí)間訂單表和訂單明細(xì)表為什么要分開設(shè)計(jì)這個(gè)細(xì)節(jié)很能體現(xiàn)你對業(yè)務(wù)的理解。訂單表記錄一次下單的匯總信息訂單明細(xì)表記錄該訂單包含的每個(gè)商品。同時(shí)明細(xì)表里要存商品快照也就是下單那一刻的商品名稱、單價(jià)、圖片等信息。因?yàn)樯唐繁淼臄?shù)據(jù)之后可能會改價(jià)格、改名稱如果訂單直接去關(guān)聯(lián)商品表歷史訂單顯示出來的數(shù)據(jù)就跟著變了這就不對了。用快照把下單瞬間的信息固定下來歷史訂單才不會混亂。答辯時(shí)能主動講出這個(gè)設(shè)計(jì)原因老師對你的印象分絕對不一樣。分類表用parent_id字段做成父子結(jié)構(gòu)也比較講究。遇到“阿克蘇蘋果 冰糖心蘋果”這類二級類目遞歸查詢就能處理不用為每一級單獨(dú)建表也不需要冗余分類層級字段。物理外鍵建議不要過度使用依靠程序邏輯保證數(shù)據(jù)完整性否則刪除數(shù)據(jù)時(shí)各種約束錯(cuò)誤會搞得人頭疼。2.3 目錄結(jié)構(gòu)與代碼組織規(guī)范如果這套源碼走的是ThinkPHP框架目錄組織一般長這樣application/index/前臺模塊包含controller、view等子目錄admin/后臺模塊common/公共函數(shù)、公共模型public/入口文件靜態(tài)資源、上傳文件thinkphp/框架核心代碼database/SQL初始化和建表語句這樣組織的好處是前臺和后臺共用一套框架但業(yè)務(wù)邏輯互不干擾??刂破髦回?fù)責(zé)接收參數(shù)、調(diào)用模型、返回視圖具體的SQL和業(yè)務(wù)規(guī)則放到模型層。很多同學(xué)喜歡把SQL直接寫在控制器里當(dāng)時(shí)覺得方便后面稍微改個(gè)需求就要大范圍找代碼。把前置校驗(yàn)、數(shù)據(jù)過濾、事務(wù)處理這些邏輯統(tǒng)一放進(jìn)模型控制器的代碼會清爽很多以后擴(kuò)展功能也有清晰的落點(diǎn)。3. 核心功能模塊的實(shí)操實(shí)現(xiàn)3.1 用戶注冊登錄與角色權(quán)限控制用戶模塊是整個(gè)商城的前置條件。注冊流程建議三步走第一步校驗(yàn)手機(jī)號或用戶名唯一性第二步用password_hash()加密存儲密碼第三步自動登錄并跳轉(zhuǎn)首頁。這里特別強(qiáng)調(diào)一點(diǎn)密碼千萬不要用MD5、不要用SHA1、更不要明文存儲。MD5撞庫太容易了用password_hash()生成帶鹽的密碼哈希再用password_verify()做校驗(yàn)這是PHP官方推薦的方案也是答辯時(shí)一個(gè)重要的安全加分點(diǎn)。登錄態(tài)保持要重點(diǎn)檢查session配置。常見的問題是用戶明明登錄了隔一會兒再訪問頁面卻提示未登錄這個(gè)坑通常有三個(gè)來源session.gc_maxlifetime設(shè)置太短服務(wù)端的session數(shù)據(jù)被提前回收session.cookie_lifetime跟前者不一致瀏覽器的cookie過期時(shí)間和服務(wù)端對不上cookie的domain參數(shù)設(shè)置有誤比如設(shè)置成“.example.com”本地用localhost訪問時(shí)cookie就匹配不上排查順序就是檢查php.ini里這兩項(xiàng)配置是否協(xié)調(diào)再確認(rèn)cookie的domain在實(shí)際環(huán)境里是否有效。后臺每個(gè)控制器的構(gòu)造函數(shù)里我都建議統(tǒng)一調(diào)用一次權(quán)限檢查方法這樣即使哪天某個(gè)頁面忘了做前端權(quán)限隱藏后端也不會放行。前臺用戶和后臺管理員雖然是登錄但絕對不能混用一套判斷邏輯。我的做法是設(shè)置session里不同的角色標(biāo)識鍵比如前臺存user_id后臺存admin_id后臺入口統(tǒng)一校驗(yàn)后者的存在。這樣兩個(gè)端即使在同一套框架下權(quán)限隔離也是清晰的。3.2 商品展示與多條件檢索商品列表頁是用戶進(jìn)入商城最先看到的核心界面。首頁和列表頁的重點(diǎn)是組合查詢分類篩選、關(guān)鍵詞搜索、排序按銷量、按價(jià)格、按上架時(shí)間同時(shí)生效。SQL用條件拼接的方式構(gòu)建// 基礎(chǔ)條件分類信息和上架狀態(tài)必須存在 $where []; if (!empty($categoryId)) { $where[category_id] $categoryId; } $where[status] 1; // 關(guān)鍵詞搜索 if (!empty($keyword)) { $where[goods_name] [like, % . $keyword . %]; } // 排序白名單映射禁止直接拼接外部參數(shù) $allowOrder [ default id desc, price_asc price asc, price_desc price desc, sales_desc sales desc ]; $order $allowOrder[$sortField] ?? $allowOrder[default];order by這一塊要特別注意外部傳進(jìn)來的字段名不能直接拼進(jìn)SQL必須走白名單映射。否則用戶改一下URL參數(shù)就可能把任意字段拖出來排序這在真實(shí)環(huán)境里是SQL注入的高發(fā)點(diǎn)。白名單里沒有的排序值一律用默認(rèn)排序兜底。商品詳情頁除了基礎(chǔ)信息還要展示原價(jià)和現(xiàn)價(jià)。阿克蘇的特產(chǎn)比如冰糖心蘋果通常按箱賣所以價(jià)格字段要支持小數(shù)庫存單位用“份”或“箱”不要只寫死一個(gè)“件”字。商品詳情如果走富文本編輯器輸出的HTML一定要做過濾把script標(biāo)簽和危險(xiǎn)事件屬性全部剝掉否則就是存儲型XSS漏洞用戶打開頁面就可能中招。3.3 購物車、訂單與結(jié)算流程購物車模塊看似簡單實(shí)際有幾個(gè)容易忽略的細(xì)節(jié)數(shù)量不能為負(fù)用戶點(diǎn)減少到0就移除該商品同一個(gè)用戶同一個(gè)商品重復(fù)加購應(yīng)該做數(shù)量累加而不是插一條新記錄購物車表里不存價(jià)格只存商品ID和數(shù)量金額在結(jié)算時(shí)查商品表實(shí)時(shí)計(jì)算這樣做的好處是價(jià)格永遠(yuǎn)以商品表為準(zhǔn)不會出現(xiàn)購物車?yán)镲@示的價(jià)格跟結(jié)算頁不一致的情況。結(jié)算下單是整個(gè)系統(tǒng)最核心的環(huán)節(jié)必須用數(shù)據(jù)庫事務(wù)?;玖鞒淌莟ry { // 開啟事務(wù) $pdo-beginTransaction(); // 1. 讀取購物車選中的商品列表 // 2. 逐項(xiàng)校驗(yàn)商品狀態(tài)和庫存是否充足 // 3. 計(jì)算訂單總金額 // 4. 插入訂單主表 // 5. 插入訂單明細(xì)表含商品快照 // 6. 扣除商品庫存 // 7. 清空對應(yīng)購物車記錄 // 8. 提交事務(wù) $pdo-commit(); } catch (Exception $e) { // 任何一步出錯(cuò)整體回滾 $pdo-rollBack(); }如果中間任何一步出錯(cuò)而沒回滾就會出現(xiàn)“訂單生成了但庫存沒扣”或“庫存扣了但訂單沒生成”這種一致性災(zāi)難。把這個(gè)事務(wù)流程講清楚絕對是答辯時(shí)的亮點(diǎn)回答。訂單狀態(tài)至少設(shè)計(jì)五個(gè)待支付、已支付待發(fā)貨、已發(fā)貨、已完成、已取消。畢業(yè)設(shè)計(jì)一般不會真的對接微信支付和支付寶更多是用模擬支付——點(diǎn)擊支付按鈕直接跳轉(zhuǎn)支付成功頁并把訂單狀態(tài)改為已支付。這個(gè)方案在畢業(yè)設(shè)計(jì)場景完全夠用但要在論文和答辯文檔里寫清楚如果接入真實(shí)支付需要替換成對應(yīng)支付平臺的SDK接口。3.4 后臺管理與數(shù)據(jù)統(tǒng)計(jì)后臺管理的核心要求是數(shù)據(jù)操作要有“痕跡”。商品管理要支持上架、下架、庫存調(diào)整、刪除關(guān)鍵操作記錄操作時(shí)間訂單管理要能按狀態(tài)檢索發(fā)貨時(shí)錄入物流單號用戶管理要能禁用異常賬號禁用后用戶端立即不能登錄。這些功能在PHP里實(shí)現(xiàn)難度不大難的是把權(quán)限和操作日志做對。管理員操作商品表之前先判斷當(dāng)前登錄的角色是不是管理員。前端隱藏按鈕只是體驗(yàn)優(yōu)化后端校驗(yàn)才是安全底線。我自己踩過一個(gè)教訓(xùn)覺得按鈕在界面上看不見就等于安全了結(jié)果有人直接模擬POST請求調(diào)接口把商品價(jià)格改成0.01元下了一單。從那次之后后端接口的所有修改類操作我都強(qiáng)制做角色權(quán)限校驗(yàn)和操作記錄一個(gè)都不能少。數(shù)據(jù)統(tǒng)計(jì)模塊常用的功能有銷量Top10商品、訂單總數(shù)、銷售額趨勢。實(shí)現(xiàn)方式就是分組聚合查詢// 按月統(tǒng)計(jì)銷售額 $this-orderModel -where(pay_time, between, [$start, $end]) -field(DATE_FORMAT(pay_time, %Y-%m) as month, SUM(pay_amount) as total) -group(month) -select();這里有一個(gè)坑統(tǒng)計(jì)時(shí)間字段要統(tǒng)一。不要有的地方存時(shí)間戳、有的地方存datetime混著用會導(dǎo)致圖表數(shù)據(jù)對不上。推薦數(shù)據(jù)庫統(tǒng)一用datetime類型程序?qū)影葱柁D(zhuǎn)換格式。4. 開發(fā)調(diào)試中的常見問題與避坑實(shí)錄4.1 登錄狀態(tài)丟失與中文亂碼問題這兩個(gè)問題在PHP項(xiàng)目里出現(xiàn)頻率極高。登錄狀態(tài)丟失核心就是session和cookie配置問題。另一件容易忽略的事是php.ini里session.cookie_lifetime和session.gc_maxlifetime兩者的配合。前一個(gè)管瀏覽器cookie存活時(shí)間后一個(gè)管服務(wù)端session數(shù)據(jù)保留時(shí)間兩個(gè)值不一致就會出現(xiàn)“瀏覽器cookie還在但服務(wù)端數(shù)據(jù)已經(jīng)被回收”的情況表現(xiàn)為登錄狀態(tài)時(shí)有時(shí)無。排查時(shí)直接改配置再重啟php-fpm或Apache基本都能解決。中文亂碼一般有三種來源數(shù)據(jù)庫或數(shù)據(jù)表沒有使用utf8mb4編碼PHP文件保存時(shí)帶了BOM頭或本身不是UTF-8編碼HTML頁面頭部沒有聲明charsetutf-8排查思路是按順序檢查數(shù)據(jù)庫連接成功后先執(zhí)行SET NAMES utf8mb4編輯器統(tǒng)一把文件轉(zhuǎn)成UTF-8無BOM格式頁面模板頭部補(bǔ)上meta聲明。三條都做到亂碼問題基本清零。4.2 SQL注入與XSS防護(hù)必備技巧雖然畢業(yè)設(shè)計(jì)不是生產(chǎn)級項(xiàng)目但安全這關(guān)答辯時(shí)經(jīng)常會被問到。SQL注入的防護(hù)核心就是使用PDO預(yù)處理所有涉及變量拼接的查詢都走prepareexecute// 錯(cuò)誤示范直接拼接字符串 // $sql SELECT * FROM user WHERE id . $_GET[id]; // 正確做法PDO預(yù)處理 $stmt $pdo-prepare(SELECT * FROM user WHERE id ?); $stmt-execute([$_GET[id]]); $user $stmt-fetch();XSS防護(hù)就是輸出側(cè)過濾用htmlspecialchars處理所有用戶可控的內(nèi)容富文本按白名單過濾標(biāo)簽。文件上傳同樣要小心只允許jpg/png等常見擴(kuò)展名、校驗(yàn)MIME類型、用隨機(jī)文件名存儲、上傳目錄禁止執(zhí)行PHP腳本。這幾點(diǎn)做到面試官或答辯老師問安全方案時(shí)你基本能答得很有底氣。4.3 圖片上傳失敗與預(yù)覽不顯示商城系統(tǒng)里商品圖上傳是高頻功能碰到最多的問題是圖片超過默認(rèn)限制被靜默丟棄。PHP默認(rèn)upload_max_filesize一般是2Mpost_max_size是8M手機(jī)拍的商品圖經(jīng)常超過這個(gè)大小上傳就會失敗。解決辦法是在php.ini里調(diào)整upload_max_filesize 20M post_max_size 40M max_execution_time 300改完php.ini一定要重啟PHP服務(wù)才能生效。也可以用前端壓縮方案先把圖片壓到合理尺寸再上傳對商城場景來說商品圖2M以內(nèi)足夠清晰還能提升加載速度。預(yù)覽不顯示最常見的是路徑問題——相對路徑和絕對路徑混著用。建議圖片數(shù)據(jù)庫存相對路徑頁面統(tǒng)一用框架的base_url或__ROOT__拼接完整地址這樣即使是本地開發(fā)和線上部署兩套環(huán)境也不會出現(xiàn)圖片全部破圖的尷尬。4.4 性能優(yōu)化與部署配置備忘本地開發(fā)能跑通不代表部署到服務(wù)器上就高枕無憂。幾個(gè)基礎(chǔ)優(yōu)化建議商品列表頁高頻查詢字段要建索引比如分類ID、上架狀態(tài)、銷量開啟OPcache緩存PHP opcode性能提升立竿見影圖片要做壓縮和懶加載列表頁不要讓用戶一次性下載幾十張大圖數(shù)據(jù)庫連接盡量復(fù)用避免每次請求重復(fù)握手連接MySQL部署時(shí)還有一件重要的事關(guān)閉調(diào)試模式。ThinkPHP項(xiàng)目就是把APP_DEBUG設(shè)置為false生產(chǎn)環(huán)境的錯(cuò)誤日志不要直接打印到頁面。如果開著調(diào)試模式演示項(xiàng)目某條SQL報(bào)錯(cuò)時(shí)頁面會直接把數(shù)據(jù)庫賬號密碼、連接參數(shù)全暴露出來這個(gè)局面非常被動而且答辯現(xiàn)場無法解釋。部署前把日志寫入文件頁面保持干凈這一步很多同學(xué)容易漏我在這里特別強(qiáng)調(diào)一下。最后再分享一個(gè)小經(jīng)驗(yàn)。項(xiàng)目交付的時(shí)候除了源碼務(wù)必附帶一份完整的數(shù)據(jù)庫初始化SQL文件和部署說明文檔把步驟寫到“瀏覽器輸入哪個(gè)地址能看到首頁”這種程度。我見過太多源碼明明沒問題、卻因?yàn)椴渴鹞臋n含糊不清被老師扣分的案例。這套阿克蘇農(nóng)產(chǎn)品商城系統(tǒng)的整體設(shè)計(jì)思路從數(shù)據(jù)庫表結(jié)構(gòu)到購物車數(shù)量累加的處理本質(zhì)上是一套可以復(fù)用的電商開發(fā)方法論。把這個(gè)邏輯吃透下一次無論換什么題材——水果電商、手工藝品店、二手書城都能快速套用。寫代碼是體力活把每個(gè)設(shè)計(jì)背后的原因想清楚才是畢業(yè)設(shè)計(jì)真正的收獲。