本文目录导读:

我来详细介绍PHP项目实现积分商城功能的完整方案。
数据库设计
核心数据表
-- 用户表(扩展字段) ALTER TABLE `user` ADD COLUMN `points` INT(11) DEFAULT 0 COMMENT '用户积分'; -- 积分记录表 CREATE TABLE `points_log` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL COMMENT '用户ID', `type` tinyint(1) NOT NULL COMMENT '类型:1增加 -1减少', `points` int(11) NOT NULL COMMENT '积分数量', `source` varchar(50) NOT NULL COMMENT '来源:sign/order/exchange/admin', `remark` varchar(255) DEFAULT NULL COMMENT '备注', `created_at` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 商品表 CREATE TABLE `points_goods` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(100) NOT NULL COMMENT '商品名称', `image` varchar(255) DEFAULT NULL COMMENT '商品图片', `points` int(11) NOT NULL COMMENT '所需积分', `stock` int(11) DEFAULT 0 COMMENT '库存', `sold_count` int(11) DEFAULT 0 COMMENT '已兑换数量', `status` tinyint(1) DEFAULT 1 COMMENT '状态:1上架 0下架', `description` text COMMENT '商品描述', `type` tinyint(1) DEFAULT 1 COMMENT '类型:1实物 2虚拟(优惠券等)', `sort_order` int(11) DEFAULT 0 COMMENT '排序', `created_at` datetime DEFAULT CURRENT_TIMESTAMP, `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 兑换记录表 CREATE TABLE `points_exchange` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL, `goods_id` int(11) NOT NULL, `points` int(11) NOT NULL COMMENT '消耗积分', `status` tinyint(1) DEFAULT 0 COMMENT '状态:0待处理 1已完成 2已取消', `order_no` varchar(32) DEFAULT NULL COMMENT '兑换订单号', `user_name` varchar(50) DEFAULT NULL COMMENT '收货人', `phone` varchar(20) DEFAULT NULL COMMENT '联系电话', `address` varchar(255) DEFAULT NULL COMMENT '收货地址', `remark` varchar(255) DEFAULT NULL COMMENT '备注', `created_at` datetime DEFAULT CURRENT_TIMESTAMP, `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `user_id` (`user_id`), KEY `order_no` (`order_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
核心功能实现
积分管理类
<?php
// PointsManager.php
class PointsManager {
private $db;
public function __construct($db) {
$this->db = $db;
}
/**
* 增加积分
*/
public function addPoints($userId, $points, $source, $remark = '') {
if ($points <= 0) return false;
$this->db->beginTransaction();
try {
// 更新用户积分
$sql = "UPDATE user SET points = points + ? WHERE id = ?";
$this->db->execute($sql, [$points, $userId]);
// 记录日志
$sql = "INSERT INTO points_log (user_id, type, points, source, remark)
VALUES (?, 1, ?, ?, ?)";
$this->db->execute($sql, [$userId, $points, $source, $remark]);
$this->db->commit();
return true;
} catch (Exception $e) {
$this->db->rollback();
return false;
}
}
/**
* 扣除积分
*/
public function deductPoints($userId, $points, $source, $remark = '') {
if ($points <= 0) return false;
// 检查积分是否足够
if (!$this->hasEnoughPoints($userId, $points)) {
return false;
}
$this->db->beginTransaction();
try {
$sql = "UPDATE user SET points = points - ? WHERE id = ? AND points >= ?";
$affected = $this->db->execute($sql, [$points, $userId, $points]);
if ($affected == 0) {
$this->db->rollback();
return false;
}
// 记录日志
$sql = "INSERT INTO points_log (user_id, type, points, source, remark)
VALUES (?, -1, ?, ?, ?)";
$this->db->execute($sql, [$userId, $points, $source, $remark]);
$this->db->commit();
return true;
} catch (Exception $e) {
$this->db->rollback();
return false;
}
}
/**
* 检查积分是否足够
*/
public function hasEnoughPoints($userId, $points) {
$sql = "SELECT points FROM user WHERE id = ?";
$user = $this->db->fetchOne($sql, [$userId]);
return $user && $user['points'] >= $points;
}
/**
* 获取用户积分
*/
public function getUserPoints($userId) {
$sql = "SELECT points FROM user WHERE id = ?";
$user = $this->db->fetchOne($sql, [$userId]);
return $user ? $user['points'] : 0;
}
/**
* 获取积分明细
*/
public function getPointsLog($userId, $page = 1, $pageSize = 20) {
$offset = ($page - 1) * $pageSize;
$sql = "SELECT * FROM points_log WHERE user_id = ?
ORDER BY created_at DESC LIMIT ?, ?";
return $this->db->fetchAll($sql, [$userId, $offset, $pageSize]);
}
}
商品管理类
<?php
// GoodsManager.php
class GoodsManager {
private $db;
public function __construct($db) {
$this->db = $db;
}
/**
* 获取商品列表
*/
public function getGoodsList($page = 1, $pageSize = 20) {
$offset = ($page - 1) * $pageSize;
$sql = "SELECT * FROM points_goods WHERE status = 1
ORDER BY sort_order ASC, id DESC LIMIT ?, ?";
return $this->db->fetchAll($sql, [$offset, $pageSize]);
}
/**
* 获取商品详情
*/
public function getGoodsDetail($goodsId) {
$sql = "SELECT * FROM points_goods WHERE id = ? AND status = 1";
return $this->db->fetchOne($sql, [$goodsId]);
}
/**
* 检查库存
*/
public function checkStock($goodsId, $quantity = 1) {
$sql = "SELECT stock FROM points_goods WHERE id = ?";
$goods = $this->db->fetchOne($sql, [$goodsId]);
return $goods && $goods['stock'] >= $quantity;
}
/**
* 扣减库存
*/
public function deductStock($goodsId, $quantity = 1) {
$sql = "UPDATE points_goods SET stock = stock - ?, sold_count = sold_count + ?
WHERE id = ? AND stock >= ?";
return $this->db->execute($sql, [$quantity, $quantity, $goodsId, $quantity]);
}
}
兑换功能实现
<?php
// ExchangeController.php
class ExchangeController {
private $db;
private $pointsManager;
private $goodsManager;
public function __construct($db) {
$this->db = $db;
$this->pointsManager = new PointsManager($db);
$this->goodsManager = new GoodsManager($db);
}
/**
* 执行兑换
*/
public function exchange($userId, $goodsId, $quantity = 1, $userInfo = []) {
// 获取商品信息
$goods = $this->goodsManager->getGoodsDetail($goodsId);
if (!$goods) {
return ['code' => 0, 'msg' => '商品不存在或已下架'];
}
// 检查库存
if (!$this->goodsManager->checkStock($goodsId, $quantity)) {
return ['code' => 0, 'msg' => '库存不足'];
}
// 计算所需积分
$totalPoints = $goods['points'] * $quantity;
// 检查用户积分
if (!$this->pointsManager->hasEnoughPoints($userId, $totalPoints)) {
return ['code' => 0, 'msg' => '积分不足'];
}
$this->db->beginTransaction();
try {
// 生成订单号
$orderNo = $this->generateOrderNo();
// 扣减库存
$this->goodsManager->deductStock($goodsId, $quantity);
// 扣除积分
$this->pointsManager->deductPoints(
$userId,
$totalPoints,
'exchange',
"兑换商品:{$goods['name']} x{$quantity}"
);
// 创建兑换记录
$sql = "INSERT INTO points_exchange (user_id, goods_id, points, order_no,
user_name, phone, address, status)
VALUES (?, ?, ?, ?, ?, ?, ?, 0)";
$this->db->execute($sql, [
$userId,
$goodsId,
$totalPoints,
$orderNo,
$userInfo['name'] ?? '',
$userInfo['phone'] ?? '',
$userInfo['address'] ?? ''
]);
$this->db->commit();
return ['code' => 1, 'msg' => '兑换成功', 'order_no' => $orderNo];
} catch (Exception $e) {
$this->db->rollback();
return ['code' => 0, 'msg' => '兑换失败,请重试'];
}
}
/**
* 获取兑换记录
*/
public function getExchangeList($userId, $page = 1, $pageSize = 20) {
$offset = ($page - 1) * $pageSize;
$sql = "SELECT e.*, g.name as goods_name, g.image as goods_image
FROM points_exchange e
LEFT JOIN points_goods g ON e.goods_id = g.id
WHERE e.user_id = ?
ORDER BY e.created_at DESC LIMIT ?, ?";
return $this->db->fetchAll($sql, [$userId, $offset, $pageSize]);
}
/**
* 生成订单号
*/
private function generateOrderNo() {
return 'EX' . date('YmdHis') . rand(100000, 999999);
}
}
前端页面示例
<!-- goods_list.php -->
<!DOCTYPE html>
<html>
<head>积分商城</title>
<style>
.goods-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
padding: 20px;
}
.goods-item {
border: 1px solid #ddd;
padding: 15px;
text-align: center;
}
.goods-item img {
max-width: 200px;
height: 200px;
object-fit: cover;
}
.points {
color: #ff6600;
font-size: 18px;
font-weight: bold;
}
.exchange-btn {
background: #ff6600;
color: white;
border: none;
padding: 8px 20px;
cursor: pointer;
border-radius: 4px;
}
.exchange-btn:hover {
background: #e55d00;
}
.stock-info {
color: #999;
font-size: 12px;
}
</style>
</head>
<body>
<div class="header">
<h2>积分商城</h2>
<div class="user-points">
我的积分:<span id="points"><?php echo $userPoints; ?></span>
</div>
</div>
<div class="goods-grid" id="goodsList">
<!-- 商品列表由PHP动态生成 -->
<?php foreach ($goodsList as $goods): ?>
<div class="goods-item">
<img src="<?php echo $goods['image']; ?>" alt="<?php echo $goods['name']; ?>">
<h3><?php echo $goods['name']; ?></h3>
<p class="points"><?php echo $goods['points']; ?> 积分</p>
<p class="stock-info">库存:<?php echo $goods['stock']; ?></p>
<button class="exchange-btn" onclick="exchange(<?php echo $goods['id']; ?>)">
立即兑换
</button>
</div>
<?php endforeach; ?>
</div>
<script>
function exchange(goodsId) {
// 弹出收货信息对话框
showExchangeDialog(goodsId);
}
function showExchangeDialog(goodsId) {
// 实现弹窗让用户输入收货信息
// 然后调用ajax提交兑换
}
</script>
</body>
</html>
安全建议
- 防刷机制:限制兑换频率,添加验证码
- 事务处理:确保库存和积分操作的原子性
- 锁机制:高并发场景下使用Redis锁或数据库行锁
- 数据验证:前后端双重验证积分和库存
- 日志记录:记录所有敏感操作
这个实现可以满足大多数积分商城的基本需求,你可以根据具体业务进行调整和扩展。