本文目录导读:

我来详细介绍如何在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分析的核心功能,包括数据收集、计算、缓存、趋势分析和可视化,根据具体需求,可以进一步扩展功能,如用户分组分析、触发条件设置等。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播