ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Flask+XGBoost水果质量检测:从特征工程到Web部署

Flask+XGBoost水果质量检测:从特征工程到Web部署 在开始动工之前我先说一下为什么这个话题值得写。很多人一看到“水果质量检测”第一反应是“这不就是深度学习图像分类吗”确实用卷积神经网络做水果分级是当前学术界和大厂的主流方案。但如果你是在做一个课程设计、毕业设计或者给中小型果园做一个快速验证的 Demo你会发现直接上 CNN 的代价并不小数据集要够大、GPU 要够用、训练时间要够长、解释性还偏弱。而 Flask XGBoost 的组合走的是另一条务实的路线先通过 OpenCV 从水果图像中提取可量化的特征再把特征转成表格数据交给 XGBoost 训练。这个方案不需要 GPU不需要上万张图片训练速度快结果可解释而且部署成本极低。今天这篇文章我会从系统设计、特征工程、模型训练、Flask 接口开发到前端页面完整地带你实现一个可直接运行的水果质量检测系统。我会重点讲清楚三个容易被忽视的问题XGBoost 到底在这个系统里担任什么角色、特征工程为什么比选模型更重要、Flask 是怎么把模型“变成”一个可用服务的。如果你正需要一套能写进简历、能演示、能运行的完整项目这篇应该对你有帮助。1. 做水果质量检测为什么不一定非要深度学习水果质量检测听起来很“AI”但实际要解决的问题分两类第一类是拍一张照片直接判断这个水果是好是坏第二类是给出多个维度的数据比如颜色、大小、甜度、表面缺陷数量综合判断质量等级。前者的确更适合 CNN后者却是 XGBoost 的主场。这里要给新手讲明白一个关键判断XGBoost 不是图像识别算法它是梯度提升树模型核心处理的是结构化特征数据。它无法直接“看”图片但它能从你已经提取好的特征中学习规律而且在小样本、表格型数据上的表现往往比深度模型更稳定。所以在水果质量检测项目里最务实的架构不是“图片进、结果出”的端到端模型而是水果图像/数据 - 特征提取 - 特征数值表 - XGBoost 模型 - 质量等级这个过程里Flask 承担的是 Web 服务层负责接收请求、调用模型、返回结果XGBoost 承担的是决策层负责根据输入特征给出质量预测。把这两者组合在一起Flask 基于 XGBoost 的水果质量检测系统 才算真正落地。从工程角度看Flask 负责把模型包装成 HTTP API这样前端可以上传图片后端执行特征提取和模型预测最后把结果展示在页面上。整个链路清晰也方便后续替换模型或扩展功能。2. 核心概念与系统架构2.1 Flask 在系统中的角色Flask 是一个轻量级 Python Web 框架。它的核心优势是可以快速搭建 HTTP 服务非常适合做模型部署。在这个水果质量检测系统中Flask 需要做的事情有提供前端页面作为系统交互入口提供图片上传接口接收用户传来的水果图片调用特征提取模块把图片转成 XGBoost 能识别的数值特征加载训练好的 XGBoost 模型执行预测把结果返回给前端展示。2.2 XGBoost 在系统中的角色XGBoosteXtreme Gradient Boosting是一种基于梯度提升决策树GBDT的集成学习算法。它通过不断训练决策树、拟合上一轮预测的残差最终把多棵树的预测结果累加起来。这个算法在工业界广泛用于分类、回归和排序问题。在水果质量检测场景中XGBoost 负责的任务是分类。假设标准水果分为三个等级优质、合格、不合格。输入是色泽、大小、表面缺陷数量、果形指数等特征输出是这三个等级的概率分布取最高项作为最终预测结果。这里有一个新手容易混淆的概念XGBoost 和 GBDT 的区别。GBDT 是基础算法XGBoost 是它的优化版本。XGBoost 在损失函数中加入了正则化项支持二阶导数信息并做了列抽样和并行化处理。简单理解就是XGBoost 在防止过拟合和训练效率上比原始 GBDT 更好这也是它在竞赛和项目中更常被选中的原因。2.3 特征工程连接图片与模型的桥梁既然 XGBoost 不能直接处理图片那么特征工程就是整个项目最核心的一步。水果质量检测常用的特征包括特征类别具体特征说明颜色特征平均 R、G、B 值反映水果整体色泽成熟度过低会明显偏绿尺寸特征最大直径、周长、面积反映水果大小直接影响商品等级纹理特征灰度共生矩阵的对比度、能量反映表面光滑程度缺陷特征缺陷区域数量、缺陷面积占比反映表面是否存在明显破损或腐烂点特征提取完成后每个水果样本都会变成一行数值数据。把几十个这样的样本汇总成一张表格就是 XGBoost 的训练集。2.4 系统整体工作流程从用户操作视角看系统运行流程如下用户在浏览器打开系统首页上传一张水果图片Flask 接收图片并保存到指定目录调用特征提取函数计算图片的颜色、大小、纹理、缺陷等特征把特征组成一维数组传入 XGBoost 模型模型输出分类结果和置信度Flask 将结果渲染到页面返回给用户。这个流程同样可以扩展到实时摄像头检测场景摄像头采集帧图像程序定时取帧走相同的特征提取和预测链路。3. 环境准备与项目初始化在动手写代码之前先准备开发环境。为了让整套代码可复现建议使用虚拟环境。3.1 安装 Python 与创建虚拟环境本项目基于 Python 开发使用的库包括 Flask、XGBoost、OpenCV、Pandas 和 Scikit-learn。版本请以实际项目为准本文重点演示通用思路。# 创建虚拟环境Windows 使用 python -m venv venv python3 -m venv venv # 激活虚拟环境 # Linux/macOS source venv/bin/activate # Windows venv\Scripts\activate3.2 安装依赖库pip install flask xgboost opencv-python pandas numpy scikit-learn joblib如果你的网络环境下载较慢可以切换为国内镜像源安装pip install -i https://mirrors.aliyun.com/pypi/simple flask xgboost opencv-python pandas numpy scikit-learn joblib这些库的职责分配如下Flask构建 Web 服务XGBoost模型训练与预测OpenCV图像读取、裁剪和特征提取Pandas/Numpy数据处理Scikit-learn数据划分、标签编码、指标评估Joblib模型持久化保存和加载。3.3 项目目录结构一个清晰的项目目录结构能让代码维护轻松很多。推荐按下面的方式组织fruit_quality_system/ ├── app.py # Flask 入口文件 ├── model_train.py # 模型训练脚本 ├── feature_extract.py # 特征提取模块 ├── requirements.txt # 依赖清单 ├── models/ │ └── xgb_model.joblib # 训练好的模型文件 ├── data/ │ ├── raw/ # 原始图片数据 │ └── features.csv # 特征表格 ├── uploads/ # 用户上传图片目录 └── templates/ └── index.html # 前端页面4. 特征工程的完整实现特征工程是这个项目中最关键的环节。如果特征提取得不准XGBoost 再强也训练不出好模型。下面我们用 OpenCV 实现一个特征提取模块它能读入一张水果图片输出一个固定维度的特征向量。这里的思路是先读取图片缩放成统一尺寸然后分别计算颜色均值、灰度共生矩阵纹理特征和简单缺陷检测结果。为了演示方便我们把它封装成extract_features函数。# 文件路径feature_extract.py import cv2 import numpy as np def extract_features(image_path): 从水果图片中提取特征向量。 输入图片路径 输出numpy 数组维度固定为 [R, G, B, size, contrast, energy, defect_count, defect_area] img cv2.imread(image_path) if img is None: raise ValueError(f无法读取图片: {image_path}) # 统一缩放到 224x224减少图片尺寸对特征的干扰 img cv2.resize(img, (224, 224)) # 1. 颜色特征BGR 三通道平均值 b_mean, g_mean, r_mean img[:, :, 0].mean(), img[:, :, 1].mean(), img[:, :, 2].mean() # 2. 大小特征这里用前景占比近似表示水果大小 # 实际项目中可以结合背景分割这里用简单阈值示例 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) _, binary cv2.threshold(gray, 60, 255, cv2.THRESH_BINARY_INV) size_ratio np.sum(binary 0) / (224 * 224) # 3. 纹理特征用灰度共生矩阵简化版这里使用标准差作为粗糙度近似 texture_std np.std(gray) # 4. 缺陷特征通过连通区域检测统计黑色/异常区域数量和面积 num_labels, labels, stats, _ cv2.connectedComponentsWithStats(binary) defect_count max(num_labels - 1, 0) # 面积占比去掉最大的前景区域其余视为缺陷 if num_labels 1: areas stats[1:, cv2.CC_STAT_AREA] # 去除最大连通域后剩余面积占比作为缺陷面积 total_area 224 * 224 defect_area (np.sum(areas) - np.max(areas)) / total_area # 如果只有一个前景区域认为缺陷面积为0 if defect_area 0: defect_area 0.0 else: defect_area 0.0 return np.array([r_mean, g_mean, b_mean, size_ratio, texture_std, defect_count, defect_area])这段代码里有几个可以继续优化的地方背景分割可以改用 HSV 色彩空间降低环境光照影响缺陷检测可以结合颜色异常区域检测而不是简单依赖二值化纹理特征可以换成完整的灰度共生矩阵统计量。从工程实践看特征维度可以少于深度学习模型的自动特征数量但每个特征都必须有物理含义这样才能在分析模型错误时还原原因。5. 训练 XGBoost 质量分类模型特征提取实现后下一步是准备训练数据。假设你已经准备了一批图片并且每张图片都有标签如 0 代表不合格1 代表合格2 代表优质。我们可以批量提取特征并保存成 CSV。5.1 批量提取特征并生成数据集# 文件路径generate_features.py import os import csv import cv2 import numpy as np from feature_extract import extract_features def generate_dataset(image_folder, output_csv): 遍历图片文件夹提取特征并保存为 CSV。 文件命名规则label_index.jpg例如 0_001.jpg 表示不合格1_001.jpg 表示合格。 with open(output_csv, w, newline, encodingutf-8) as f: writer csv.writer(f) writer.writerow([r_mean, g_mean, b_mean, size_ratio, texture_std, defect_count, defect_area, label]) for file_name in os.listdir(image_folder): if not file_name.endswith(.jpg): continue # 从文件名解析标签例如 2_100.jpg - label2 label int(file_name.split(_)[0]) file_path os.path.join(image_folder, file_name) features extract_features(file_path) writer.writerow(list(features) [label]) print(f已处理: {file_name}) if __name__ __main__: generate_features(data/raw, data/features.csv)这里使用的文件命名规则是label_index.jpg。真实项目中标签可以来自人工标注也可以来自已有的检测记录但要注意保持标签分布均衡避免某一类样本过多导致模型偏向该类。5.2 训练 XGBoost 模型准备好features.csv后就可以训练模型了。# 文件路径model_train.py import pandas as pd import xgboost as xgb from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score, classification_report import joblib # 1. 读取数据 df pd.read_csv(data/features.csv) # 2. 切分特征与标签 X df.drop(columns[label]) y df[label] # 3. 划分训练集与测试集 # 这里使用常见做法实际项目中要保证同一来源数据不混入测试集 X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42, stratifyy) # 4. 训练 XGBoost 分类模型 model xgb.XGBClassifier( n_estimators200, max_depth4, learning_rate0.1, subsample0.8, colsample_bytree0.8, use_label_encoderFalse, eval_metricmlogloss, random_state42 ) model.fit(X_train, y_train) # 5. 评估模型 y_pred model.predict(X_test) accuracy accuracy_score(y_test, y_pred) print(f测试集准确率: {accuracy:.4f}) print(classification_report(y_test, y_pred)) # 6. 保存模型 joblib.dump(model, models/xgb_model.joblib) print(模型已保存到 models/xgb_model.joblib)训练完成后项目目录下会生成models/xgb_model.joblib文件这是后面 Flask 服务要加载的模型文件。这里要提醒一点在特征数量不多的情况下XGBoost 的调参重点不应放在盲目加深树深度而应关注防止过拟合。max_depth设置在 3 到 6 之间通常比较稳妥learning_rate调小后要配合增加n_estimators。如果你的训练集只有几十张图片最好使用交叉验证来评估模型稳定性。5.3 使用 K 折交叉验证的进阶做法在真实项目里一次随机划分不够稳健。推荐使用 5 折交叉验证评估模型。特征提取阶段产生的表格数据规模不大交叉验证的耗时完全可以接受。# 文件路径model_train_cv.py import pandas as pd import xgboost as xgb from sklearn.model_selection import StratifiedKFold, cross_val_score import joblib df pd.read_csv(data/features.csv) X df.drop(columns[label]) y df[label] # 使用 K 折交叉验证评估模型稳定性 cv StratifiedKFold(n_splits5, shuffleTrue, random_state42) model xgb.XGBClassifier( n_estimators200, max_depth4, learning_rate0.1, subsample0.8, colsample_bytree0.8, use_label_encoderFalse, eval_metricmlogloss, random_state42 ) scores cross_val_score(model, X, y, cvcv, scoringaccuracy) print(f5 折交叉验证准确率: {scores}) print(f平均准确率: {scores.mean():.4f} (±{scores.std():.4f})) # 使用全量数据重新训练最终模型 model.fit(X, y) joblib.dump(model, models/xgb_model.joblib) print(最终模型已保存到 models/xgb_model.joblib)6. Flask 服务与预测接口开发模型训练完成后重点工作是把模型包装成 Web 服务。我们创建一个app.py用 Flask 实现两个核心功能上传图片并显示结果通过 JSON 接口传入特征向量返回预测结果。6.1 Flask 应用基础代码# 文件路径app.py import os import numpy as np import joblib from flask import Flask, request, jsonify, render_template from feature_extract import extract_features app Flask(__name__) # 配置上传目录 UPLOAD_FOLDER uploads os.makedirs(UPLOAD_FOLDER, exist_okTrue) app.config[UPLOAD_FOLDER] UPLOAD_FOLDER app.config[MAX_CONTENT_LENGTH] 8 * 1024 * 1024 # 限制最大上传 8MB # 加载模型 model_path models/xgb_model.joblib if os.path.exists(model_path): model joblib.load(model_path) else: raise FileNotFoundError(未找到模型文件请先运行 model_train.py 训练模型) # 质量等级映射 LABEL_MAP { 0: 不合格, 1: 合格, 2: 优质 } app.route(/) def index(): return render_template(index.html) app.route(/predict, methods[POST]) def predict(): # 1. 接收上传的图片文件 file request.files.get(file) if file is None: return jsonify({error: 未检测到上传文件}), 400 # 2. 保存图片 file_path os.path.join(app.config[UPLOAD_FOLDER], file.filename) file.save(file_path) try: # 3. 提取特征 features extract_features(file_path) # 4. 调整形状模型要求二维数组 features features.reshape(1, -1) # 5. 预测 label model.predict(features)[0] proba model.predict_proba(features)[0] confidence float(np.max(proba) * 100) result { filename: file.filename, quality: LABEL_MAP.get(int(label), 未知), confidence: round(confidence, 2), features: features.tolist() } # 如果希望通过页面展示结果可以同时返回 JSON return jsonify(result) except Exception as e: return jsonify({error: f预测失败: {str(e)}}), 500 if __name__ __main__: app.run(host0.0.0.0, port5000, debugTrue)这段代码实现了从接收图片到返回质量等级的核心链路。有几个设计细节值得说明MAX_CONTENT_LENGTH限定了上传大小防止大文件占满磁盘每次预测都重新提取特征便于后续更换特征提取算法predict_proba可以输出置信度帮助判断预测结果的可信程度host0.0.0.0让服务可以被局域网内其他设备访问演示时非常有用。6.2 特征向量直接预测接口有时候客户端采集设备已经计算好特征不需要上传图片。所以增加一个predict_feature接口接收 JSON 格式的特征值。app.route(/predict_feature, methods[POST]) def predict_feature(): 接收 JSON 格式的特征向量直接预测。 请求体示例 { features: [120.5, 140.2, 130.1, 0.6, 28.5, 2, 0.03] } data request.get_json() if not data or features not in data: return jsonify({error: 请求格式错误缺少 features 字段}), 400 features np.array(data[features]).reshape(1, -1) label model.predict(features)[0] proba model.predict_proba(features)[0] confidence float(np.max(proba) * 100) return jsonify({ quality: LABEL_MAP.get(int(label), 未知), confidence: round(confidence, 2) })这种接口设计的好处是把图片处理和模型预测解耦。当业务方要接其他传感器数据时无需经过图片上传直接传输特征值即可。7. 前端页面的简单实现后端接口写好后需要一个能交互的前端页面。为了保证项目简单我们直接使用 Flask 的模板渲染。页面包含一个文件选择控件、一个上传按钮和一个结果展示区域。!-- 文件路径templates/index.html -- !DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title水果质量检测系统/title style body { font-family: Arial, sans-serif; max-width: 700px; margin: 40px auto; } .upload-box { border: 2px dashed #ccc; padding: 40px; text-align: center; } .result { margin-top: 20px; padding: 20px; border-radius: 8px; } .quality { font-size: 24px; font-weight: bold; } /style /head body h2基于 XGBoost 的水果质量检测/h2 div classupload-box form iduploadForm enctypemultipart/form-data input typefile namefile acceptimage/* required button typesubmit开始检测/button /form /div div classresult idresult/div script document.getElementById(uploadForm).addEventListener(submit, async function (event) { event.preventDefault(); const formData new FormData(this); const response await fetch(/predict, { method: POST, body: formData }); const data await response.json(); if (data.error) { document.getElementById(result).innerHTML p stylecolor:red${data.error}/p; } else { const qualityColor data.quality 优质 ? green : data.quality 合格 ? orange : red; document.getElementById(result).innerHTML div styleborder: 1px solid #ddd; background: ${qualityColor} p文件名${data.filename}/p p classquality质量等级${data.quality}/p p置信度${data.confidence}%/p /div ; } }); /script /body /html页面逻辑不复杂表单提交后使用fetch把图片以multipart/form-data格式发送到/predict接口收到 JSON 后渲染结果。如果你想做得更完整还可以在页面上展示上传图片的预览以及历史检测记录。8. 运行验证与效果预期代码完成后按下面的步骤运行系统8.1 启动 Flask 服务python app.py正常启动后终端会输出类似下面的信息* Serving Flask app app * Debug mode: on * Running on all addresses (0.0.0.0) * Running on http://127.0.0.1:50008.2 用浏览器测试打开浏览器访问http://127.0.0.1:5000选择一张水果图片点击“开始检测”。页面会显示预测得到的水果质量等级与置信度。8.3 用 curl 命令测试接口如果你在终端环境可以用 curl 直接模拟上传请求curl -X POST -F filedata/raw/2_001.jpg http://127.0.0.1:5000/predict预期返回结果类似{ filename: 2_001.jpg, quality: 优质, confidence: 96.82, features: [[135.2, 148.6, 120.4, 0.57, 32.1, 1, 0.02]] }8.4 特征向量接口测试curl -X POST -H Content-Type: application/json \ -d {features: [135.2, 148.6, 120.4, 0.57, 32.1, 1, 0.02]} \ http://127.0.0.1:5000/predict_feature预期返回{ quality: 优质, confidence: 96.82 }8.5 如何判断运行成功从两个层面看功能层面接口返回状态码为 200且quality字段符合预期模型层面置信度越高预测越有参考价值。如果多个类别置信度接近说明特征区分度不足需要优化特征提取。如果预测失败优先查看终端日志、检查上传目录权限、确认模型文件路径是否正确。9. 常见问题与排查思路实际开发中新手遇到的问题大多集中在依赖安装、图像读取、特征形状、模型文件和上传路径几个方面。问题现象可能原因排查方式解决方案安装 xgboost 失败Python 版本或 pip 版本过旧检查python --version与pip --version升级 pippip install --upgrade pip图片无法读取图片路径含中文名或图像损坏打印传入路径检查文件是否存在重命名文件或更换图片预测时报维度错误特征提取函数返回长度与训练时不一致输出features.shape对比训练时的维度统一特征提取逻辑模型文件找不到未执行训练脚本或路径不对检查models/xgb_model.joblib是否存在先运行python model_train.py上传文件过大MAX_CONTENT_LENGTH限制查看服务端日志是否提示 413 状态码调大限制或在前端压缩图片预测结果全部集中在某一类训练数据不平衡或特征无区分度打印分类报告查看样本分布增加样本、重新设计特征、设置类别权重Flask 发布到公网后无法访问未监听 0.0.0.0 或被防火墙拦截本机 curl 测试检查安全组修改 host 参数为 0.0.0.0并发请求时服务卡顿每次请求都加载模型或特征提取耗时太长检查 CPU 占用统计接口响应时间模型只加载一次特征提取代码优化或引入缓存这里比较容易踩的坑是特征维度不一致。训练时extract_features返回 7 个特征部署时如果改了代码导致返回 8 个特征模型就会直接报错。所以特征提取模块要尽量保持稳定任何改动后都要重新训练并保存模型。另一个容易被忽视的问题是上传文件名如果包含中文处理时要格外小心。Windows 和 Linux 的文件名编码不同建议将上传文件统一重命名为时间戳加随机数的格式例如20250321153012_82347.jpg避免路径问题。10. 工程落地的硬性提醒写完代码只是一个开始如果你想把这个项目变成一个能真正用起来的系统下面这些工程问题才是决定它是否能活过“演示阶段”的关键。说实话这些教训是我见过最多项目翻车的地方提前写在这里希望你能绕开。10.1 特征提取从第一步就要统一很多人在训练阶段用的图片是实验室统一光照下拍的到了测试阶段换了一部手机、换了一个光照环境效果立刻下降。原因很简单特征分布变了。最好从一开始就在代码里固定图像处理流程统一缩放尺寸、统一色彩空间转换方式、固定二值化阈值。不要今天用 224 尺寸明天用 300 尺寸这会让模型学到的规律失去意义。10.2 训练集和测试集不能来自同一批照片的近邻帧如果同一颗苹果连续拍了 10 张照片其中 8 张进了训练集、2 张进了测试集模型相当于已经见过答案了。准确率虚高但一到真实环境就露馅。正确做法是按水果个体分组划分数据或者确保图片之间差异足够大。10.3 模型保存和加载要保持环境一致XGBoost 的模型文件跨 Python 小版本一般问题不大但不同 XGBoost 版本之间偶发会有兼容提示。建议在requirements.txt中固定版本。一个比较稳妥的requirements.txt写法是flask3.0.0 xgboost2.0.3 opencv-python4.9.0.80 pandas2.1.4 numpy1.26.2 scikit-learn1.3.2 joblib1.3.2版本号仅作示例实际安装时请以你的环境为准。如果模型训练和部署不在同一台机器尽量在部署机上重新用同样的依赖安装一次。10.4 生产环境建议开启多线程Flask 自带的开发服务器只能用于演示。如果部署到生产环境建议使用 Gunicorn 等 WSGI 服务器。pip install gunicorn gunicorn -w 4 -b 0.0.0.0:5000 app:app-w 4表示启动 4 个 worker 进程能有效提升并发处理能力。10.5 数据采集要有规范工程化之后数据采集绝不能随手拍。你的标签是谁打的打了几个维度是用什么标准定义的“优质”这些都要提前定好。没有人做标注标准后面训练的模型就会学一堆噪声。11. 总结与下一步学习方向这篇文章带你走完了 Flask 基于 XGBoost 的水果质量检测系统的完整链路特征提取、模型训练、Flask 接口开发、前端交互、运行验证和问题排查。这个项目的核心价值在于它没有依赖复杂的深度学习框架而是用传统机器学习完整地解决了一个真实业务问题非常适合作为课程设计、毕业设计或初学者的第一个完整 AI Web 项目。如果你想继续深入可以从三个方向着手一是特征工程优化。把简单的颜色均值替换为 HSV 空间统计量纹理特征替换为完整的灰度共生矩阵缺陷检测引入更精细的区域分割算法模型效果会有明显提升。二是模型对比实验。用同样的特征数据尝试随机森林、LightGBM、支持向量机和 XGBoost 做横向对比。你会发现不同模型在同一个特征集上的表现差异这比单纯背算法概念更有价值。三是系统功能扩展。往前端加入历史记录查询往 Flask 加入数据库存储把系统升级成 MySQL 数据持久化架构再配合 Flask 定时任务实现模型自动重训练。最后一个提醒这个项目最容易被忽略的是“特征”而不是模型。别一上来就纠结调参调到最好先把特征工程做扎实用最简单的模型跑通完整链路再逐步优化这才是最稳妥的开发节奏。放到 CSDN 收藏夹里下次做类似项目时直接拿出来参照即可。
返回列表