如何用PHP项目实现NPS分析?

wen java案例 1

本文目录导读:

如何用PHP项目实现NPS分析?

  1. 数据库设计
  2. NPS核心类实现
  3. 控制器实现
  4. 前端实现(AJAX交互)
  5. 分析仪表盘模板
  6. API接口
  7. 使用示例

我来详细介绍如何在PHP项目中实现NPS(净推荐值)分析。

数据库设计

-- NPS调查表
CREATE TABLE nps_surveys (
    id INT PRIMARY KEY AUTO_INCREMENT,VARCHAR(200) NOT NULL,
    description TEXT,
    status ENUM('active', 'inactive', 'closed') DEFAULT 'active',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- NPS响应表
CREATE TABLE nps_responses (
    id INT PRIMARY KEY AUTO_INCREMENT,
    survey_id INT NOT NULL,
    user_id INT COMMENT '关联用户ID,匿名可为NULL',
    score INT NOT NULL CHECK (score BETWEEN 0 AND 10),
    reason TEXT COMMENT '推荐/不推荐原因',
    category VARCHAR(50) COMMENT '反馈分类',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (survey_id) REFERENCES nps_surveys(id)
);
-- NPS计算缓存表
CREATE TABLE nps_cache (
    id INT PRIMARY KEY AUTO_INCREMENT,
    survey_id INT NOT NULL,
    promoters INT DEFAULT 0,
    passives INT DEFAULT 0,
    detractors INT DEFAULT 0,
    total_responses INT DEFAULT 0,
    nps_score DECIMAL(5,2) DEFAULT 0,
    calculated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (survey_id) REFERENCES nps_surveys(id)
);

NPS核心类实现

<?php
// NPSAnalyzer.php
class NPSAnalyzer {
    private $db;
    public function __construct($dbConnection) {
        $this->db = $dbConnection;
    }
    /**
     * 提交NPS评分
     */
    public function submitResponse($surveyId, $userId, $score, $reason = '', $category = '') {
        if (!is_numeric($score) || $score < 0 || $score > 10) {
            throw new InvalidArgumentException('Score must be between 0 and 10');
        }
        $stmt = $this->db->prepare(
            "INSERT INTO nps_responses (survey_id, user_id, score, reason, category) 
             VALUES (?, ?, ?, ?, ?)"
        );
        return $stmt->execute([$surveyId, $userId, $score, $reason, $category]);
    }
    /**
     * 计算NPS分数
     */
    public function calculateNPS($surveyId) {
        $stats = $this->getScoreDistribution($surveyId);
        $total = $stats['promoters'] + $stats['passives'] + $stats['detractors'];
        if ($total === 0) {
            return [
                'nps_score' => 0,
                'total' => 0,
                'distribution' => $stats
            ];
        }
        // NPS = 推荐者百分比 - 贬损者百分比
        $promoterPercent = ($stats['promoters'] / $total) * 100;
        $detractorPercent = ($stats['detractors'] / $total) * 100;
        $npsScore = $promoterPercent - $detractorPercent;
        // 缓存计算结果
        $this->cacheNPS($surveyId, $stats, $npsScore, $total);
        return [
            'nps_score' => round($npsScore, 2),
            'total' => $total,
            'distribution' => $stats,
            'promoter_percent' => round($promoterPercent, 2),
            'detractor_percent' => round($detractorPercent, 2)
        ];
    }
    /**
     * 获取评分分布
     */
    public function getScoreDistribution($surveyId) {
        $stmt = $this->db->prepare(
            "SELECT 
                SUM(CASE WHEN score >= 9 THEN 1 ELSE 0 END) as promoters,
                SUM(CASE WHEN score BETWEEN 7 AND 8 THEN 1 ELSE 0 END) as passives,
                SUM(CASE WHEN score <= 6 THEN 1 ELSE 0 END) as detractors
             FROM nps_responses 
             WHERE survey_id = ?"
        );
        $stmt->execute([$surveyId]);
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }
    /**
     * 缓存NPS结果
     */
    private function cacheNPS($surveyId, $distribution, $npsScore, $total) {
        $stmt = $this->db->prepare(
            "INSERT INTO nps_cache (survey_id, promoters, passives, detractors, total_responses, nps_score)
             VALUES (?, ?, ?, ?, ?, ?)
             ON DUPLICATE KEY UPDATE 
             promoters = VALUES(promoters),
             passives = VALUES(passives),
             detractors = VALUES(detractors),
             total_responses = VALUES(total_responses),
             nps_score = VALUES(nps_score),
             calculated_at = CURRENT_TIMESTAMP"
        );
        return $stmt->execute([
            $surveyId,
            $distribution['promoters'],
            $distribution['passives'],
            $distribution['detractors'],
            $total,
            $npsScore
        ]);
    }
    /**
     * 获取NPS趋势数据
     */
    public function getNPSTrend($surveyId, $startDate, $endDate, $interval = 'day') {
        $intervals = [
            'day' => "DATE(created_at)",
            'week' => "WEEK(created_at, 1)",
            'month' => "DATE_FORMAT(created_at, '%Y-%m')"
        ];
        $groupBy = $intervals[$interval] ?? $intervals['day'];
        $stmt = $this->db->prepare(
            "SELECT 
                {$groupBy} as period,
                COUNT(*) as total_responses,
                SUM(CASE WHEN score >= 9 THEN 1 ELSE 0 END) as promoters,
                SUM(CASE WHEN score BETWEEN 7 AND 8 THEN 1 ELSE 0 END) as passives,
                SUM(CASE WHEN score <= 6 THEN 1 ELSE 0 END) as detractors
             FROM nps_responses
             WHERE survey_id = ? 
             AND created_at BETWEEN ? AND ?
             GROUP BY period
             ORDER BY period"
        );
        $stmt->execute([$surveyId, $startDate, $endDate]);
        $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
        // 计算每个周期的NPS
        foreach ($results as &$row) {
            $total = $row['total_responses'];
            if ($total > 0) {
                $row['nps_score'] = round(
                    (($row['promoters'] / $total) * 100) - 
                    (($row['detractors'] / $total) * 100), 
                    2
                );
            } else {
                $row['nps_score'] = 0;
            }
        }
        return $results;
    }
}

控制器实现

<?php
// NPSSurveyController.php
class NPSSurveyController {
    private $analyzer;
    public function __construct($db) {
        $this->analyzer = new NPSAnalyzer($db);
    }
    /**
     * 显示NPS调查页面
     */
    public function showSurvey($surveyId) {
        // 获取调查信息
        $survey = $this->getSurvey($surveyId);
        // 渲染调查模板
        return $this->render('nps/survey', [
            'survey' => $survey
        ]);
    }
    /**
     * 处理NPS评分提交
     */
    public function submitScore($request) {
        try {
            $result = $this->analyzer->submitResponse(
                $request['survey_id'],
                $_SESSION['user_id'] ?? null,
                (int)$request['score'],
                $request['reason'] ?? '',
                $request['category'] ?? ''
            );
            if ($result) {
                return [
                    'success' => true,
                    'message' => '感谢您的反馈!'
                ];
            }
        } catch (Exception $e) {
            return [
                'success' => false,
                'message' => $e->getMessage()
            ];
        }
    }
    /**
     * 显示NPS分析仪表盘
     */
    public function dashboard($surveyId) {
        // 当前NPS
        $currentNPS = $this->analyzer->calculateNPS($surveyId);
        // 趋势数据(最近30天)
        $trend = $this->analyzer->getNPSTrend(
            $surveyId,
            date('Y-m-d', strtotime('-30 days')),
            date('Y-m-d'),
            'day'
        );
        // 获取详细反馈
        $feedbacks = $this->getDetailedFeedbacks($surveyId);
        return $this->render('nps/dashboard', [
            'current_nps' => $currentNPS,
            'trend' => $trend,
            'feedbacks' => $feedbacks
        ]);
    }
    /**
     * 获取详细反馈
     */
    private function getDetailedFeedbacks($surveyId, $limit = 20) {
        $stmt = $this->db->prepare(
            "SELECT nr.*, u.name as user_name
             FROM nps_responses nr
             LEFT JOIN users u ON nr.user_id = u.id
             WHERE nr.survey_id = ?
             ORDER BY nr.created_at DESC
             LIMIT ?"
        );
        $stmt->execute([$surveyId, $limit]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
}

前端实现(AJAX交互)

<!-- nps-survey.html -->
<div class="nps-container">
    <h2>您向朋友推荐我们的可能性有多大?</h2>
    <p>0 = 完全不可能,10 = 非常可能</p>
    <form id="npsForm" onsubmit="return submitNPS(event)">
        <div class="nps-scale">
            <?php for ($i = 0; $i <= 10; $i++): ?>
            <label class="nps-option">
                <input type="radio" name="score" value="<?= $i ?>" required>
                <span class="score-value"><?= $i ?></span>
            </label>
            <?php endfor; ?>
        </div>
        <div class="nps-categories">
            <label>反馈类别(可选):</label>
            <select name="category">
                <option value="">选择类别</option>
                <option value="product">产品</option>
                <option value="service">服务</option>
                <option value="support">支持</option>
                <option value="price">价格</option>
            </select>
        </div>
        <div class="nps-feedback">
            <label>为什么给出这个评分?</label>
            <textarea name="reason" rows="3" placeholder="分享您的想法..."></textarea>
        </div>
        <input type="hidden" name="survey_id" value="<?= $surveyId ?>">
        <button type="submit">提交反馈</button>
    </form>
</div>
<script>
function submitNPS(event) {
    event.preventDefault();
    const formData = new FormData(event.target);
    fetch('/api/nps/submit', {
        method: 'POST',
        body: new URLSearchParams(formData)
    })
    .then(response => response.json())
    .then(data => {
        if (data.success) {
            showSuccessMessage(data.message);
            event.target.reset();
        } else {
            showErrorMessage(data.message);
        }
    })
    .catch(error => {
        showErrorMessage('提交失败,请稍后重试');
    });
    return false;
}
</script>

分析仪表盘模板

<!-- nps-dashboard.php -->
<div class="nps-dashboard">
    <!-- 当前NPS分数卡片 -->
    <div class="nps-card nps-main">
        <h3>当前NPS分数</h3>
        <div class="nps-score <?= getScoreClass($currentNPS['nps_score']) ?>">
            <?= $currentNPS['nps_score'] ?>
        </div>
        <div class="nps-details">
            <span>推荐者: <?= $currentNPS['distribution']['promoters'] ?></span>
            <span>被动者: <?= $currentNPS['distribution']['passives'] ?></span>
            <span>贬损者: <?= $currentNPS['distribution']['detractors'] ?></span>
        </div>
    </div>
    <!-- NPS趋势图 -->
    <div class="nps-card nps-trend">
        <h3>NPS趋势(最近30天)</h3>
        <canvas id="npsTrendChart"></canvas>
    </div>
    <!-- 反馈列表 -->
    <div class="nps-card nps-feedbacks">
        <h3>用户反馈</h3>
        <div class="feedback-list">
            <?php foreach ($feedbacks as $feedback): ?>
            <div class="feedback-item feedback-<?= getFeedbackClass($feedback['score']) ?>">
                <div class="feedback-score"><?= $feedback['score'] ?>/10</div>
                <div class="feedback-text"><?= htmlspecialchars($feedback['reason']) ?></div>
                <div class="feedback-meta">
                    <?= $feedback['user_name'] ?? '匿名' ?> - 
                    <?= date('Y-m-d H:i', strtotime($feedback['created_at'])) ?>
                </div>
            </div>
            <?php endforeach; ?>
        </div>
    </div>
</div>
<?php
function getScoreClass($score) {
    if ($score >= 50) return 'excellent';
    if ($score >= 0) return 'good';
    return 'needs-improvement';
}
function getFeedbackClass($score) {
    if ($score >= 9) return 'promoter';
    if ($score >= 7) return 'passive';
    return 'detractor';
}
?>
<!-- NPS趋势图表脚本 -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
// 渲染NPS趋势图
const ctx = document.getElementById('npsTrendChart').getContext('2d');
new Chart(ctx, {
    type: 'line',
    data: {
        labels: <?= json_encode(array_column($trend, 'period')) ?>,
        datasets: [{
            label: 'NPS分数',
            data: <?= json_encode(array_column($trend, 'nps_score')) ?>,
            borderColor: '#4CAF50',
            tension: 0.1
        }]
    },
    options: {
        responsive: true,
        scales: {
            y: {
                min: -100,
                max: 100
            }
        }
    }
});
</script>

API接口

<?php
// api/nps.php
header('Content-Type: application/json');
$db = new PDO('mysql:host=localhost;dbname=your_db', 'user', 'pass');
$nps = new NPSAnalyzer($db);
$action = $_GET['action'] ?? '';
switch ($action) {
    case 'submit':
        $result = $nps->submitResponse(
            $_POST['survey_id'],
            $_SESSION['user_id'] ?? null,
            (int)$_POST['score'],
            $_POST['reason'] ?? '',
            $_POST['category'] ?? ''
        );
        echo json_encode([
            'success' => $result,
            'message' => $result ? '提交成功' : '提交失败'
        ]);
        break;
    case 'calculate':
        $result = $nps->calculateNPS($_GET['survey_id']);
        echo json_encode($result);
        break;
    case 'trend':
        $result = $nps->getNPSTrend(
            $_GET['survey_id'],
            $_GET['start_date'] ?? date('Y-m-d', strtotime('-30 days')),
            $_GET['end_date'] ?? date('Y-m-d'),
            $_GET['interval'] ?? 'day'
        );
        echo json_encode($result);
        break;
}

使用示例

<?php
// 使用示例
$db = new PDO('mysql:host=localhost;dbname=your_db', 'user', 'pass');
$nps = new NPSAnalyzer($db);
// 1. 创建调查
$surveyId = 1;
// 2. 提交评分
$nps->submitResponse($surveyId, null, 9, '非常好用', 'product');  // 推荐者
$nps->submitResponse($surveyId, null, 6, '有待改进', 'service');  // 贬损者
// 3. 计算NPS
$result = $nps->calculateNPS($surveyId);
echo "NPS Score: " . $result['nps_score'];  // 输出: 50 = (100% - 50%)
// 4. 获取趋势
$trend = $nps->getNPSTrend(
    $surveyId,
    '2023-01-01',
    '2023-12-31',
    'month'
);

这个实现包含了NPS分析的核心功能,包括数据收集、计算、缓存、趋势分析和可视化,根据具体需求,可以进一步扩展功能,如用户分组分析、触发条件设置等。

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