本文目录导读:

我来详细介绍PHP项目实现用户签到积分的完整方案。
数据库设计
签到记录表
CREATE TABLE `user_sign_in` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '用户ID',
`sign_date` date NOT NULL COMMENT '签到日期',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP COMMENT '签到时间',
`points` int(11) DEFAULT 0 COMMENT '获得积分',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_date` (`user_id`, `sign_date`),
KEY `idx_user_id` (`user_id`),
KEY `idx_sign_date` (`sign_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
用户积分表
CREATE TABLE `user_points` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`total_points` int(11) DEFAULT 0 COMMENT '总积分',
`continuous_days` int(11) DEFAULT 0 COMMENT '连续签到天数',
`last_sign_date` date DEFAULT NULL COMMENT '最后签到日期',
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
积分记录表(可选,用于记录明细)
CREATE TABLE `points_log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`type` tinyint(4) DEFAULT 0 COMMENT '类型:1签到,2消费,3兑换',
`points` int(11) NOT NULL COMMENT '变动积分',
`description` varchar(255) DEFAULT NULL COMMENT '描述',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
PHP核心代码实现
签到类设计
<?php
class SignInSystem {
private $db;
private $userId;
public function __construct($db, $userId) {
$this->db = $db;
$this->userId = $userId;
}
/**
* 用户签到
*/
public function signIn() {
try {
$this->db->beginTransaction();
$today = date('Y-m-d');
// 检查今天是否已签到
if ($this->isTodaySigned()) {
return ['success' => false, 'message' => '今天已签到'];
}
// 获取签到状态
$status = $this->getSignInStatus();
$continuousDays = $status['continuous_days'];
$lastSignDate = $status['last_sign_date'];
// 计算连续签到天数
$yesterday = date('Y-m-d', strtotime('-1 day'));
if ($lastSignDate == $yesterday) {
$continuousDays++;
} else {
$continuousDays = 1;
}
// 计算本次签到积分
$points = $this->calculatePoints($continuousDays);
// 记录签到
$this->recordSignIn($today, $points);
// 更新用户积分状态
$this->updateUserPoints($points, $continuousDays, $today);
// 记录积分变动
$this->logPointsChange($points, "第{$continuousDays}天签到");
$this->db->commit();
return [
'success' => true,
'points' => $points,
'continuous_days' => $continuousDays,
'message' => "签到成功,获得{$points}积分"
];
} catch (Exception $e) {
$this->db->rollBack();
return ['success' => false, 'message' => '签到失败'];
}
}
/**
* 检查今天是否已签到
*/
private function isTodaySigned() {
$today = date('Y-m-d');
$stmt = $this->db->prepare(
"SELECT id FROM user_sign_in
WHERE user_id = ? AND sign_date = ?"
);
$stmt->execute([$this->userId, $today]);
return $stmt->fetch() ? true : false;
}
/**
* 获取签到状态
*/
private function getSignInStatus() {
$stmt = $this->db->prepare(
"SELECT continuous_days, last_sign_date
FROM user_points WHERE user_id = ?"
);
$stmt->execute([$this->userId]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$result) {
return ['continuous_days' => 0, 'last_sign_date' => null];
}
return $result;
}
/**
* 计算签到积分
* 采用递增策略:连续签到天数越多,积分越多
*/
private function calculatePoints($continuousDays) {
// 基础积分
$basePoints = 1;
// 连续签到加成
$bonus = 0;
if ($continuousDays >= 7) {
$bonus = 7; // 连续7天额外7分
} elseif ($continuousDays >= 5) {
$bonus = 5;
} elseif ($continuousDays >= 3) {
$bonus = 3;
}
return $basePoints + $bonus;
}
/**
* 记录签到
*/
private function recordSignIn($date, $points) {
$stmt = $this->db->prepare(
"INSERT INTO user_sign_in (user_id, sign_date, points)
VALUES (?, ?, ?)"
);
$stmt->execute([$this->userId, $date, $points]);
}
/**
* 更新用户积分
*/
private function updateUserPoints($points, $continuousDays, $date) {
// 检查用户积分记录是否存在
$stmt = $this->db->prepare(
"SELECT id FROM user_points WHERE user_id = ?"
);
$stmt->execute([$this->userId]);
if ($stmt->fetch()) {
// 更新
$stmt = $this->db->prepare(
"UPDATE user_points
SET total_points = total_points + ?,
continuous_days = ?,
last_sign_date = ?,
updated_at = NOW()
WHERE user_id = ?"
);
$stmt->execute([$points, $continuousDays, $date, $this->userId]);
} else {
// 插入新记录
$stmt = $this->db->prepare(
"INSERT INTO user_points (user_id, total_points, continuous_days, last_sign_date)
VALUES (?, ?, ?, ?)"
);
$stmt->execute([$this->userId, $points, $continuousDays, $date]);
}
}
/**
* 记录积分变动
*/
private function logPointsChange($points, $description) {
$stmt = $this->db->prepare(
"INSERT INTO points_log (user_id, type, points, description, created_at)
VALUES (?, 1, ?, ?, NOW())"
);
$stmt->execute([$this->userId, $points, $description]);
}
/**
* 获取用户签到记录
*/
public function getSignInRecords($limit = 30) {
$stmt = $this->db->prepare(
"SELECT sign_date, points, created_at
FROM user_sign_in
WHERE user_id = ?
ORDER BY sign_date DESC
LIMIT ?"
);
$stmt->execute([$this->userId, $limit]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* 获取用户当前签到状态
*/
public function getCurrentStatus() {
$stmt = $this->db->prepare(
"SELECT total_points, continuous_days, last_sign_date
FROM user_points WHERE user_id = ?"
);
$stmt->execute([$this->userId]);
$status = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$status) {
return [
'total_points' => 0,
'continuous_days' => 0,
'last_sign_date' => null,
'can_sign_in' => true
];
}
// 是否可以签到
$today = date('Y-m-d');
$status['can_sign_in'] = ($status['last_sign_date'] != $today);
return $status;
}
}
前端调用示例
// sign_in.php
<?php
require_once 'config/database.php';
require_once 'class/SignInSystem.php';
// 获取用户ID(实际应从session获取)
$userId = $_SESSION['user_id'] ?? 0;
if ($_POST['action'] == 'sign_in') {
$signIn = new SignInSystem($db, $userId);
$result = $signIn->signIn();
echo json_encode($result);
exit;
}
// 获取签到状态
$signIn = new SignInSystem($db, $userId);
$status = $signIn->getCurrentStatus();
$records = $signIn->getSignInRecords();
?>
积分规则建议
基础规则
- 每日签到:1分
- 连续3天:额外3分
- 连续5天:额外5分
- 连续7天:额外7分
高级规则(可选)
- 每月全勤:额外奖励50分
- 断签后重新计算连续天数
- 特殊节日双倍积分
注意事项
- 防止刷分:限制IP/设备频繁签到
- 数据一致性:使用事务保证签到和积分更新原子性
- 缓存优化:使用Redis缓存今日签到状态
- 定时任务:每月重置或统计签到数据
这个实现包含了完整的签到积分功能,可以根据业务需求调整积分计算规则。