本文介绍如何让“random story”按钮根据文本文件中真实的故事数量(而非预设最大值)动态生成1到实际总数之间的随机数,确保始终在有效范围内获取故事。
本文介绍如何让“random story”按钮根据文本文件中真实的故事数量(而非预设最大值)动态生成1到实际总数之间的随机数,确保始终在有效范围内获取故事。
在当前实现中,country.js 通过硬编码的 actualTotalStories(如 'usa': { ..., actualTotalStories: 1000 })控制随机范围,而 router.php 虽已具备动态统计能力($actualTotalStories = count($stories)),但前端仍依赖静态配置,导致「Random Story」按钮错误地尝试请求 1–1000 中的任意编号——即使 usa.txt 当前仅含 6 个故事。
要彻底解决该问题,关键在于:将实际故事总数的计算逻辑完全交由后端决定,并移除前端对 actualTotalStories 的硬编码依赖。这样既能保证灵活性(新增故事无需修改 JS),又能确保随机范围永远精准匹配文件现状。
✅ 正确做法:前后端协同优化
1. 前端简化逻辑(country.js)
删除 countryData 中冗余的 actualTotalStories 字段,仅保留文件路径映射;fetchStory() 中不再传入 '0' 触发前端随机,而是直接让后端全权处理:
// 修改 countryData:仅保留必要字段
var countryData = {
'argentina': { file: 'argentina.txt' },
'usa': { file: 'usa.txt' },
'sweden': { file: 'sweden.txt' }
};
// getRandomButton 点击时,不传 story 参数(或传空/省略)
if (getRandomButton) {
getRandomButton.addEventListener('click', function () {
fetchStory(country, ''); // 传空字符串,让 PHP 判定为随机
});
}
同时更新 fetchStory() 函数,当 storyNumber 为空或 '0' 时,仅发送基础请求:
function fetchStory(country, storyNumber) {
clearStoryContainer();
let url = 'PHP/router.php?country=' + encodeURIComponent(country);
if (storyNumber && storyNumber !== '0') {
url += '&story=' + encodeURIComponent(storyNumber);
}
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
// ... 后续 onload/onerror 保持不变
}
2. 后端智能响应(router.php)
利用 file_get_contents() 和 preg_split() 动态计算真实故事数,并在未指定 story 或其值为 '0' 时自动生成合法随机数:
<?php if (!isset($_GET['country'])) {
die('Invalid request.');
}
$country = $_GET['country'];
$fileMap = [
'argentina' => '../../Stories/argentina.txt',
'usa' => '../../Stories/usa.txt',
'sweden' => '../../Stories/sweden.txt'
];
if (!isset($fileMap[$country]) || !file_exists($fileMap[$country])) {
die('The story file does not exist.');
}
$stories = preg_split('/<!-- Unique delimiter separating stories -->/', file_get_contents($fileMap[$country]));
$actualTotalStories = count($stories);
// 若未提供 story 参数,或值为 '0',则生成随机故事编号
if (!isset($_GET['story']) || $_GET['story'] === '0' || $_GET['story'] === '') {
$storyNumber = rand(1, $actualTotalStories);
} else {
$storyNumber = (int)$_GET['story'];
}
// 验证范围
if ($storyNumber $actualTotalStories) {
die('The requested story does not exist.');
}
$requestedStory = trim($stories[$storyNumber - 1]);
$formattedStory = nl2br($requestedStory); // 自动处理换行(等效于 str_replace + <br>)
echo $formattedStory;
?>
? nl2br() 是 PHP 内置函数,可安全替代手动 str_replace("\r\n", "
"),兼容 Windows/Linux/macOS 换行符,代码更简洁健壮。
3. 注意事项与最佳实践
- 避免重复解析:router.php 每次请求都会重新读取并分割文件。若故事文件较大或并发量高,建议缓存 $actualTotalStories(例如用 apcu_store() 或文件缓存),但对百条以内文本影响极小,可暂不优化。
- 安全性增强:当前示例未过滤 $_GET['country'],生产环境应严格白名单校验(已体现于 $fileMap 数组),防止路径遍历攻击。
- 调试技巧:临时在 router.php 开头加入 error_log("Country: $country, Total: $actualTotalStories, Requested: $storyNumber");,便于快速定位数量偏差问题。
通过以上改造,系统将真正实现「按需随机」:无论 usa.txt 当前含 6 条还是 999 条故事,点击「Random Story」始终返回 1–N 之间的有效编号,且无需任何前端配置变更——彻底解耦内容规模与代码逻辑。











