本文目录导读:

我来详细讲解PHP项目日历功能的后端数据生成方案。
基础日历数据结构
核心数据模型
class CalendarData {
// 获取某月的日历数据
public static function getMonthData($year, $month) {
$firstDay = mktime(0, 0, 0, $month, 1, $year);
$daysInMonth = date('t', $firstDay);
$firstDayOfWeek = date('w', $firstDay); // 0=周日, 1=周一...
$monthData = [];
// 上月填充
$prevMonth = $month == 1 ? 12 : $month - 1;
$prevYear = $month == 1 ? $year - 1 : $year;
$prevDays = date('t', mktime(0, 0, 0, $prevMonth, 1, $prevYear));
$dayCount = 0;
// 填充上月最后几天
for ($i = 0; $i < $firstDayOfWeek; $i++) {
$monthData[] = [
'day' => $prevDays - $firstDayOfWeek + $i + 1,
'month' => $prevMonth,
'year' => $prevYear,
'isCurrentMonth' => false,
'date' => sprintf('%d-%02d-%02d', $prevYear, $prevMonth, $prevDays - $firstDayOfWeek + $i + 1),
'timestamp' => mktime(0, 0, 0, $prevMonth, $prevDays - $firstDayOfWeek + $i + 1, $prevYear)
];
}
// 当月天数
for ($i = 1; $i <= $daysInMonth; $i++) {
$currentDate = sprintf('%d-%02d-%02d', $year, $month, $i);
$timestamp = mktime(0, 0, 0, $month, $i, $year);
$monthData[] = [
'day' => $i,
'month' => $month,
'year' => $year,
'isCurrentMonth' => true,
'isToday' => $currentDate == date('Y-m-d'),
'date' => $currentDate,
'timestamp' => $timestamp,
'dayOfWeek' => date('w', $timestamp)
];
}
// 下月填充(补齐6行)
$remainingCells = 42 - count($monthData); // 6行x7列=42
$nextMonth = $month == 12 ? 1 : $month + 1;
$nextYear = $month == 12 ? $year + 1 : $year;
for ($i = 1; $i <= $remainingCells; $i++) {
$monthData[] = [
'day' => $i,
'month' => $nextMonth,
'year' => $nextYear,
'isCurrentMonth' => false,
'date' => sprintf('%d-%02d-%02d', $nextYear, $nextMonth, $i),
'timestamp' => mktime(0, 0, 0, $nextMonth, $i, $nextYear)
];
}
return [
'year' => $year,
'month' => $month,
'monthName' => date('F', $firstDay),
'days' => $monthData,
'totalDays' => $daysInMonth,
'startDayOfWeek' => $firstDayOfWeek
];
}
}
事件和日程管理
数据库表结构
CREATE TABLE calendar_events (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,VARCHAR(200) NOT NULL,
description TEXT,
event_date DATE NOT NULL,
start_time TIME,
end_time TIME,
event_type ENUM('event', 'task', 'reminder', 'holiday') DEFAULT 'event',
status ENUM('pending', 'completed', 'cancelled') DEFAULT 'pending',
color VARCHAR(7) DEFAULT '#1890ff',
is_recurring BOOLEAN DEFAULT FALSE,
recurrence_rule TEXT, -- JSON格式的重复规则
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id),
INDEX idx_user_date (user_id, event_date)
);
事件查询类
class EventManager {
private $db;
public function __construct(PDO $db) {
$this->db = $db;
}
// 获取某月的事件
public function getMonthEvents($userId, $year, $month) {
$startDate = sprintf('%d-%02d-01', $year, $month);
$endDate = date('Y-m-t', strtotime($startDate));
$stmt = $this->db->prepare("
SELECT * FROM calendar_events
WHERE user_id = ?
AND event_date BETWEEN ? AND ?
ORDER BY event_date, start_time
");
$stmt->execute([$userId, $startDate, $endDate]);
$events = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 处理重复事件
$recurringEvents = $this->getRecurringEvents($userId, $startDate, $endDate);
return array_merge($events, $recurringEvents);
}
// 获取重复事件
private function getRecurringEvents($userId, $startDate, $endDate) {
$stmt = $this->db->prepare("
SELECT * FROM calendar_events
WHERE user_id = ?
AND is_recurring = TRUE
AND (event_date <= ? OR event_date IS NULL)
");
$stmt->execute([$userId, $endDate]);
$recurringEvents = $stmt->fetchAll(PDO::FETCH_ASSOC);
$expandedEvents = [];
foreach ($recurringEvents as $event) {
$rule = json_decode($event['recurrence_rule'], true);
if ($rule) {
$expanded = $this->expandRecurringEvent($event, $rule, $startDate, $endDate);
$expandedEvents = array_merge($expandedEvents, $expanded);
}
}
return $expandedEvents;
}
// 展开重复事件
private function expandRecurringEvent($event, $rule, $startDate, $endDate) {
$events = [];
$currentDate = $event['event_date'];
$start = new DateTime($startDate);
$end = new DateTime($endDate);
switch ($rule['type']) {
case 'daily':
$interval = new DateInterval("P1D");
break;
case 'weekly':
$interval = new DateInterval("P1W");
break;
case 'monthly':
$interval = new DateInterval("P1M");
break;
case 'yearly':
$interval = new DateInterval("P1Y");
break;
default:
return [];
}
if (isset($rule['end_date'])) {
$endDateLimit = min($end, new DateTime($rule['end_date']));
} else {
$endDateLimit = $end;
}
$dateIterator = new DateTime($event['event_date']);
while ($dateIterator <= $endDateLimit) {
if ($dateIterator >= $start) {
$newEvent = $event;
$newEvent['event_date'] = $dateIterator->format('Y-m-d');
$newEvent['is_recurring_instance'] = true;
$newEvent['original_event_id'] = $event['id'];
$events[] = $newEvent;
}
$dateIterator->add($interval);
// 防止无限循环
if (count($events) > 365) break;
}
return $events;
}
}
组合日历数据
完整日历响应类
class CalendarService {
private $eventManager;
private $holidayManager;
public function __construct(PDO $db) {
$this->eventManager = new EventManager($db);
$this->holidayManager = new HolidayManager();
}
// 获取完整日历数据
public function getFullCalendarData($userId, $year, $month, $options = []) {
// 基础日历数据
$calendarData = CalendarData::getMonthData($year, $month);
// 获取事件
$events = $this->eventManager->getMonthEvents($userId, $year, $month);
// 获取节假日
$holidays = [];
if ($options['include_holidays'] ?? true) {
$holidays = $this->holidayManager->getMonthHolidays($year, $month);
}
// 合并数据到每一天
$eventMap = $this->indexEventsByDate($events);
$holidayMap = $this->indexEventsByDate($holidays);
foreach ($calendarData['days'] as &$day) {
$date = $day['date'];
// 添加事件
$day['events'] = $eventMap[$date] ?? [];
// 添加节假日
$day['holidays'] = $holidayMap[$date] ?? [];
// 添加节日标记
$day['isHoliday'] = !empty($day['holidays']);
// 计算事件数量
$day['eventCount'] = count($day['events']);
// 判断是否有重要事件
$day['hasImportantEvent'] = $this->hasImportantEvent($day['events']);
}
// 添加周信息
$calendarData['weeks'] = $this->groupByWeeks($calendarData['days']);
// 添加月份统计
$calendarData['statistics'] = $this->getMonthStatistics($events, $year, $month);
return $calendarData;
}
// 按日期索引事件
private function indexEventsByDate($events) {
$indexed = [];
foreach ($events as $event) {
$date = $event['event_date'];
if (!isset($indexed[$date])) {
$indexed[$date] = [];
}
$indexed[$date][] = $event;
}
return $indexed;
}
// 分组为周
private function groupByWeeks($days) {
$weeks = [];
$currentWeek = [];
foreach ($days as $index => $day) {
$currentWeek[] = $day;
// 每7天为一组(从周日开始)
if (($index + 1) % 7 == 0) {
$weeks[] = $currentWeek;
$currentWeek = [];
}
}
// 处理剩余天数
if (!empty($currentWeek)) {
$weeks[] = $currentWeek;
}
return $weeks;
}
// 月度统计
private function getMonthStatistics($events, $year, $month) {
$totalEvents = count($events);
$completedEvents = 0;
$eventTypes = [];
foreach ($events as $event) {
if ($event['status'] == 'completed') {
$completedEvents++;
}
$type = $event['event_type'];
if (!isset($eventTypes[$type])) {
$eventTypes[$type] = 0;
}
$eventTypes[$type]++;
}
return [
'totalEvents' => $totalEvents,
'completedEvents' => $completedEvents,
'completionRate' => $totalEvents > 0 ? round($completedEvents / $totalEvents * 100, 2) : 0,
'eventTypes' => $eventTypes
];
}
private function hasImportantEvent($events) {
foreach ($events as $event) {
if ($event['event_type'] == 'reminder' || $event['priority'] == 'high') {
return true;
}
}
return false;
}
}
节假日管理
class HolidayManager {
// 中国节假日API(示例)
public function getMonthHolidays($year, $month) {
// 方式1:调用外部API
// $holidays = $this->fetchFromApi($year, $month);
// 方式2:本地节假日数据
$holidays = $this->getLocalHolidays($year, $month);
return $holidays;
}
private function getLocalHolidays($year, $month) {
$holidays = [];
// 固定假日
$fixedHolidays = [
'01-01' => '元旦',
'02-14' => '情人节',
'03-08' => '妇女节',
'04-01' => '愚人节',
'05-01' => '劳动节',
'06-01' => '儿童节',
'10-01' => '国庆节',
'12-25' => '圣诞节'
];
foreach ($fixedHolidays as $date => $name) {
list($holidayMonth, $holidayDay) = explode('-', $date);
if ($holidayMonth == sprintf('%02d', $month)) {
$dateStr = sprintf('%d-%s', $year, $date);
$holidays[] = [
'date' => $dateStr,
'name' => $name,
'type' => 'fixed',
'isWorkday' => false
];
}
}
// 计算农历节日(需要农历转换库)
if ($lunarHolidays = $this->getLunarHolidays($year, $month)) {
$holidays = array_merge($holidays, $lunarHolidays);
}
return $holidays;
}
private function getLunarHolidays($year, $month) {
// 需要使用农历转换库,如 lunar-calendar
// 这里仅作示例
return [];
}
}
API响应格式
class CalendarController {
public function getMonthData() {
$year = $_GET['year'] ?? date('Y');
$month = $_GET['month'] ?? date('m');
$userId = $this->getCurrentUserId();
$service = new CalendarService($this->db);
$data = $service->getFullCalendarData($userId, $year, $month);
return json_encode([
'success' => true,
'data' => $data,
'pagination' => [
'prevMonth' => $month == 1 ? 12 : $month - 1,
'prevYear' => $month == 1 ? $year - 1 : $year,
'nextMonth' => $month == 12 ? 1 : $month + 1,
'nextYear' => $month == 12 ? $year + 1 : $year
]
]);
}
}
前端数据结构示例
// 前端接收的日历数据结构
{
year: 2024,
month: 1,
monthName: "January",
days: [
{
day: 29,
month: 12,
year: 2023,
isCurrentMonth: false,
date: "2023-12-29",
events: [],
holidays: [],
isHoliday: false,
eventCount: 0,
hasImportantEvent: false
},
{
day: 1,
month: 1,
year: 2024,
isCurrentMonth: true,
isToday: true,
date: "2024-01-01",
events: [
{
id: 1,
title: "新年快乐",
start_time: "09:00:00",
end_time: "10:00:00",
event_type: "holiday",
color: "#ff4d4f"
}
],
holidays: [
{
date: "2024-01-01",
name: "元旦",
type: "fixed",
isWorkday: false
}
],
isHoliday: true,
eventCount: 1,
hasImportantEvent: true
}
],
weeks: [
// 每周7天分组
],
statistics: {
totalEvents: 5,
completedEvents: 2,
completionRate: 40,
eventTypes: {
event: 3,
task: 2
}
}
}
这个方案提供了完整的后端日历数据生成方案,包括基础日历结构、事件管理、节假日处理和完整的API响应格式,你可以根据项目需求进行调整和扩展。