PHP项目怎么实现用户签到积分?

wen PHP项目 32

本文目录导读:

PHP项目怎么实现用户签到积分?

  1. 数据库设计
  2. PHP核心代码实现
  3. 前端调用示例
  4. 积分规则建议
  5. 注意事项

我来详细介绍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分
  • 断签后重新计算连续天数
  • 特殊节日双倍积分

注意事项

  1. 防止刷分:限制IP/设备频繁签到
  2. 数据一致性:使用事务保证签到和积分更新原子性
  3. 缓存优化:使用Redis缓存今日签到状态
  4. 定时任务:每月重置或统计签到数据

这个实现包含了完整的签到积分功能,可以根据业务需求调整积分计算规则。

抱歉,评论功能暂时关闭!