本文目录导读:

在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。数据准确性和时效性是比价系统的核心价值。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播