本文目录导读:

在PHP项目中实现询价比价功能,需要处理数据采集、价格比较、用户交互等环节,下面提供一个从简单到复杂的实现思路和代码示例。
核心功能模块设计
一个完整的询价比价系统通常包含三部分:
- 询价管理(用户发起询价请求)
- 报价管理(供应商/系统返回报价)
- 比价展示(对比显示最低/最优价格)
数据库表结构设计(MySQL)
-- 询价表
CREATE TABLE inquiries (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
product_name VARCHAR(255) NOT NULL,
product_description TEXT,
quantity INT DEFAULT 1,
status ENUM('pending', 'quoted', 'completed') DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 报价表
CREATE TABLE quotes (
id INT AUTO_INCREMENT PRIMARY KEY,
inquiry_id INT NOT NULL,
supplier_name VARCHAR(255) NOT NULL,
price DECIMAL(10,2) NOT NULL,
currency VARCHAR(10) DEFAULT 'CNY',
delivery_time VARCHAR(100),
remarks TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (inquiry_id) REFERENCES inquiries(id) ON DELETE CASCADE
);
基础代码实现
用户发起询价
<?php
// InquiryController.php
class InquiryController {
private $db;
public function __construct($db) {
$this->db = $db;
}
// 用户提交询价
public function createInquiry($userId, $productName, $description, $quantity) {
$stmt = $this->db->prepare("INSERT INTO inquiries (user_id, product_name, product_description, quantity) VALUES (?, ?, ?, ?)");
$stmt->execute([$userId, $productName, $description, $quantity]);
return $this->db->lastInsertId();
}
// 供应商提交报价
public function submitQuote($inquiryId, $supplierName, $price, $currency, $deliveryTime) {
$stmt = $this->db->prepare("INSERT INTO quotes (inquiry_id, supplier_name, price, currency, delivery_time) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$inquiryId, $supplierName, $price, $currency, $deliveryTime]);
// 更新询价状态
$this->updateInquiryStatus($inquiryId, 'quoted');
return true;
}
// 获取某次询价的所有报价(比价用)
public function getQuotesForComparison($inquiryId) {
$stmt = $this->db->prepare("
SELECT q.*, i.product_name
FROM quotes q
JOIN inquiries i ON q.inquiry_id = i.id
WHERE q.inquiry_id = ?
ORDER BY q.price ASC
");
$stmt->execute([$inquiryId]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
private function updateInquiryStatus($inquiryId, $status) {
$stmt = $this->db->prepare("UPDATE inquiries SET status = ? WHERE id = ?");
$stmt->execute([$status, $inquiryId]);
}
}
比价展示页面
<?php
// compare.php - 比价显示页面
require_once 'InquiryController.php';
$controller = new InquiryController($db);
$inquiryId = $_GET['id'] ?? 0;
$quotes = $controller->getQuotesForComparison($inquiryId);
// 获取最低价和最高价
$minPrice = min(array_column($quotes, 'price'));
$maxPrice = max(array_column($quotes, 'price'));
?>
<!DOCTYPE html>
<html>
<head>比价结果</title>
<style>
.comparison-table { width: 100%; border-collapse: collapse; }
.comparison-table th, .comparison-table td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }
.best-price { background-color: #d4edda; font-weight: bold; }
.worst-price { background-color: #f8d7da; }
</style>
</head>
<body>
<h2>产品: <?= htmlspecialchars($quotes[0]['product_name'] ?? 'N/A') ?></h2>
<table class="comparison-table">
<thead>
<tr>
<th>供应商</th>
<th>价格 (<?= htmlspecialchars($quotes[0]['currency'] ?? 'CNY') ?>)</th>
<th>交货时间</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<?php foreach ($quotes as $quote): ?>
<tr class="<?= $quote['price'] == $minPrice ? 'best-price' : ($quote['price'] == $maxPrice ? 'worst-price' : '') ?>">
<td><?= htmlspecialchars($quote['supplier_name']) ?></td>
<td>¥<?= number_format($quote['price'], 2) ?></td>
<td><?= htmlspecialchars($quote['delivery_time'] ?? 'N/A') ?></td>
<td><?= htmlspecialchars($quote['remarks'] ?? '') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</body>
</html>
进阶功能实现
自动化数据采集(爬虫比价)
如果需要从外部网站自动采集价格,可以使用 Guzzle 或 cURL:
<?php
// PriceCrawler.php
use GuzzleHttp\Client;
class PriceCrawler {
private $client;
public function __construct() {
$this->client = new Client([
'timeout' => 10.0,
'headers' => [
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
]
]);
}
// 爬取某电商平台价格(示例:京东)
public function scrapeJDPrice($productUrl) {
try {
$response = $this->client->get($productUrl);
$html = (string) $response->getBody();
// 使用正则或DomDocument提取价格
preg_match('/"price"[^"]*"?[^"]*"?\s*:\s*"?([\d.]+)"/', $html, $matches);
return $matches[1] ?? null;
} catch (Exception $e) {
error_log("Failed to scrape: " . $e->getMessage());
return null;
}
}
// 批量比价
public function comparePrices(array $urls) {
$results = [];
foreach ($urls as $source => $url) {
$price = $this->scrapeJDPrice($url);
if ($price) {
$results[] = [
'source' => $source,
'price' => $price,
'url' => $url
];
}
}
// 按价格排序
usort($results, function($a, $b) {
return $a['price'] <=> $b['price'];
});
return $results;
}
}
使用缓存提高性能
缓存爬取结果,避免频繁请求:
<?php
// PriceCache.php
class PriceCache {
private $cacheDir = '/tmp/price_cache/';
public function get($key) {
$file = $this->cacheDir . md5($key) . '.cache';
if (file_exists($file) && (time() - filemtime($file) < 3600)) { // 1小时有效
return unserialize(file_get_contents($file));
}
return null;
}
public function set($key, $value) {
if (!is_dir($this->cacheDir)) {
mkdir($this->cacheDir, 0777, true);
}
$file = $this->cacheDir . md5($key) . '.cache';
file_put_contents($file, serialize($value));
}
}
// 使用方式
$cache = new PriceCache();
$cacheKey = "price_jd_" . md5($productUrl);
$price = $cache->get($cacheKey);
if (!$price) {
$crawler = new PriceCrawler();
$price = $crawler->scrapeJDPrice($productUrl);
$cache->set($cacheKey, $price);
}
基于比较器的智能比价
<?php
// PriceComparator.php
class PriceComparator {
// 多条件比较器
public function compare($quotes, $criteria = ['price' => 'asc', 'delivery_time' => 'asc']) {
usort($quotes, function($a, $b) use ($criteria) {
foreach ($criteria as $field => $order) {
if (!isset($a[$field]) || !isset($b[$field])) continue;
$result = $a[$field] <=> $b[$field];
if ($result !== 0) {
return ($order === 'desc') ? -$result : $result;
}
}
return 0;
});
return $quotes;
}
// 综合评分(价格权重60%,交货时间40%)
public function score($quote, $maxPrice, $minDeliveryDays, $maxDeliveryDays) {
$priceScore = 100 - (($quote['price'] - $minPrice) / ($maxPrice - $minPrice)) * 100;
// 交货时间得分假设日期转换为天
$deliveryScore = 100;
if (isset($quote['delivery_days'])) {
$deliveryScore = 100 - (($quote['delivery_days'] - $minDeliveryDays) / ($maxDeliveryDays - $minDeliveryDays)) * 100;
}
return $priceScore * 0.6 + $deliveryScore * 0.4;
}
}
注意事项
法律与合规
- 自动化采集需遵守目标网站的
robots.txt和用户协议 - 某些平台(如淘宝)禁止爬虫,可考虑使用官方API(如1688开放平台)
安全过滤
- 对用户输入进行严格过滤,防止XSS
- 使用参数化查询防止SQL注入
性能优化
- 使用 Redis 缓存爬取结果
- 异步处理批量比价(使用队列 like Laravel Horizon)
- 考虑使用 Elasticsearch 做价格索引和搜索
备选方案:使用第三方API
如果不想自己爬虫,可以对接:
- 1688开放平台(批发市场数据)
- 京东联盟API(电商价格数据)
- 比价网站API(如慢慢买、什么值得买)
完整示例:小项目快速实现
如果项目较小,可以简化为纯手动录入模式:
// 简化版:管理员手动录入报价
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$productName = $_POST['product_name'];
$supplier = $_POST['supplier'];
$price = $_POST['price'];
// 存储到数据库或文件
$data = json_decode(file_get_contents('prices.json'), true) ?? [];
$data[] = [
'product' => $productName,
'supplier' => $supplier,
'price' => $price,
'date' => date('Y-m-d')
];
file_put_contents('prices.json', json_encode($data));
}
// 显示比价
$prices = json_decode(file_get_contents('prices.json'), true) ?? [];
usort($prices, function($a, $b) {
return $a['price'] <=> $b['price'];
});
echo "<table><tr><th>产品</th><th>供应商</th><th>价格</th></tr>";
foreach ($prices as $p) {
echo "<tr><td>{$p['product']}</td><td>{$p['supplier']}</td><td>¥{$p['price']}</td></tr>";
}
echo "</table>";
根据你的项目规模和需求,可以选择手动录入、数据库存储、爬虫自动采集或对接第三方API。数据准确性和时效性是比价系统的核心价值。