本文目录导读:

我来帮你创建一个PHP项目周报自动生成系统,这需要一个完整的解决方案,包括数据模型、生成逻辑和输出功能。
项目结构
project-report/
├── config/
│ └── config.php
├── src/
│ ├── ReportGenerator.php
│ ├── DataCollector.php
│ ├── TemplateManager.php
│ └── ExportManager.php
├── templates/
│ ├── default.html
│ └── compact.html
├── data/
│ └── tasks.json
└── index.php
核心代码实现
配置文件 (config/config.php)
<?php
return [
'project_name' => 'XX项目',
'team_members' => [
'张三' => '后端开发',
'李四' => '前端开发',
'王五' => '测试工程师'
],
'templates' => [
'default' => 'templates/default.html',
'compact' => 'templates/compact.html'
],
'export' => [
'format' => ['markdown', 'html', 'pdf']
]
];
数据收集器 (src/DataCollector.php)
<?php
class DataCollector {
private $dataFile;
private $tasks = [];
public function __construct($dataFile) {
$this->dataFile = $dataFile;
$this->loadExistingData();
}
public function loadExistingData() {
if (file_exists($this->dataFile)) {
$json = file_get_contents($this->dataFile);
$this->tasks = json_decode($json, true) ?? [];
}
}
public function saveTask($task) {
$task['id'] = uniqid();
$task['created_at'] = date('Y-m-d H:i:s');
$task['week'] = date('W');
$task['year'] = date('Y');
$this->tasks[] = $task;
$this->saveToFile();
return $task;
}
public function getTasksByWeek($year, $week) {
$weekTasks = array_filter($this->tasks, function($task) use ($year, $week) {
return $task['year'] == $year && $task['week'] == $week;
});
return $weekTasks;
}
public function saveToFile() {
file_put_contents($this->dataFile, json_encode($this->tasks, JSON_PRETTY_PRINT));
}
public function getTaskStats($tasks) {
return [
'total' => count($tasks),
'completed' => count(array_filter($tasks, fn($t) => $t['status'] == 'completed')),
'in_progress' => count(array_filter($tasks, fn($t) => $t['status'] == 'in_progress')),
'pending' => count(array_filter($tasks, fn($t) => $t['status'] == 'pending')),
'hours' => array_sum(array_column($tasks, 'hours'))
];
}
}
报告生成器 (src/ReportGenerator.php)
<?php
class ReportGenerator {
private $collector;
private $templateManager;
private $config;
public function __construct(DataCollector $collector, TemplateManager $templateManager, $config) {
$this->collector = $collector;
$this->templateManager = $templateManager;
$this->config = $config;
}
public function generate($year, $week, $template = 'default') {
// 获取本周任务
$tasks = $this->collector->getTasksByWeek($year, $week);
// 按状态分组
$groupedTasks = [
'completed' => [],
'in_progress' => [],
'pending' => []
];
foreach ($tasks as $task) {
$groupedTasks[$task['status']][] = $task;
}
// 获取统计数据
$stats = $this->collector->getTaskStats($tasks);
// 准备报告数据
$reportData = [
'project_name' => $this->config['project_name'],
'year' => $year,
'week' => $week,
'date_range' => $this->getDateRange($year, $week),
'stats' => $stats,
'tasks' => $groupedTasks,
'team_members' => $this->config['team_members'],
'summary' => $this->generateSummary($stats),
'highlights' => $this->findHighlights($tasks),
'risks' => $this->findRisks($tasks)
];
return $this->templateManager->render($template, $reportData);
}
private function getDateRange($year, $week) {
$start = new DateTime();
$start->setISODate($year, $week);
$start->setTime(0, 0, 0);
$end = clone $start;
$end->add(new DateInterval('P6D'));
$end->setTime(23, 59, 59);
return [
'start' => $start->format('Y-m-d'),
'end' => $end->format('Y-m-d')
];
}
private function generateSummary($stats) {
if ($stats['total'] === 0) {
return '本周暂无任务记录。';
}
$completionRate = round(($stats['completed'] / $stats['total']) * 100, 2);
return sprintf(
'本周共完成 %d 项任务,%d 项正在进行,%d 项待处理,任务完成率为 %.2f%%,累计工时 %.1f 小时。',
$stats['completed'],
$stats['in_progress'],
$stats['pending'],
$completionRate,
$stats['hours']
);
}
private function findHighlights($tasks) {
$highlights = [];
foreach ($tasks as $task) {
if ($task['priority'] == 'high' && $task['status'] == 'completed') {
$highlights[] = $task;
}
}
return $highlights;
}
private function findRisks($tasks) {
$risks = [];
foreach ($tasks as $task) {
if ($task['status'] == 'pending' && strtotime($task['deadline']) < strtotime('+1 week')) {
$risks[] = $task;
}
}
return $risks;
}
}
模板管理器 (src/TemplateManager.php)
<?php
class TemplateManager {
private $templatePath;
public function __construct($templatePath) {
$this->templatePath = $templatePath;
}
public function render($template, $data) {
$templateFile = $this->templatePath . '/' . $template . '.html';
if (!file_exists($templateFile)) {
throw new Exception("Template not found: $template");
}
$content = file_get_contents($templateFile);
// 提取并替换占位符
foreach ($data as $key => $value) {
if (is_array($value)) {
$content = $this->renderArray($content, $key, $value);
} else {
$content = str_replace('{{' . $key . '}}', $value, $content);
}
}
return $content;
}
private function renderArray($content, $key, $data) {
// 处理任务列表
if ($key == 'tasks') {
$pattern = '/{{'.$key.'}}.*?{{\/'.$key.'}}/s';
preg_match_all($pattern, $content, $matches);
foreach ($matches[0] as $match) {
$rendered = '';
foreach ($data as $status => $tasks) {
foreach ($tasks as $task) {
$taskTemplate = $match;
$taskTemplate = str_replace('{{status}}', $status, $taskTemplate);
$taskTemplate = str_replace('{{title}}', $task['title'], $taskTemplate);
$taskTemplate = str_replace('{{assignee}}', $task['assignee'], $taskTemplate);
$taskTemplate = str_replace('{{hours}}', $task['hours'], $taskTemplate);
$taskTemplate = str_replace('{{priority}}', $task['priority'], $taskTemplate);
$taskTemplate = str_replace('{{deadline}}', $task['deadline'], $taskTemplate);
$taskTemplate = str_replace('{{description}}', $task['description'] ?? '', $taskTemplate);
$rendered .= $taskTemplate;
}
}
$content = str_replace($match, $rendered, $content);
}
}
return $content;
}
}
导出管理器 (src/ExportManager.php)
<?php
class ExportManager {
private $config;
public function __construct($config) {
$this->config = $config;
}
public function export($html, $format = 'html') {
switch ($format) {
case 'html':
return $this->exportHTML($html);
case 'markdown':
return $this->exportMarkdown($html);
case 'pdf':
return $this->exportPDF($html);
default:
throw new Exception("Unsupported format: $format");
}
}
private function exportHTML($content) {
header('Content-Type: text/html; charset=utf-8');
header('Content-Disposition: attachment; filename="weekly-report-' . date('Y-m-d') . '.html"');
echo $content;
}
private function exportMarkdown($html) {
// 简单的HTML转Markdown
$markdown = $html;
$markdown = preg_replace('/<h1[^>]*>/', '# ', $markdown);
$markdown = preg_replace('/<h2[^>]*>/', '## ', $markdown);
$markdown = preg_replace('/<h3[^>]*>/', '### ', $markdown);
$markdown = str_replace(['<li>', '</li>'], ['- ', ''], $markdown);
$markdown = preg_replace('/<[^>]+>/', '', $markdown);
header('Content-Type: text/markdown; charset=utf-8');
header('Content-Disposition: attachment; filename="weekly-report-' . date('Y-m-d') . '.md"');
echo $markdown;
}
private function exportPDF($html) {
// 需要安装wkhtmltopdf或使用其他PDF库
// 这里使用简单的文本导出做示例
header('Content-Type: text/plain; charset=utf-8');
header('Content-Disposition: attachment; filename="weekly-report-' . date('Y-m-d') . '.txt"');
echo strip_tags($html);
}
}
默认模板 (templates/default.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">周报 - {{project_name}} - 第{{week}}周</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
h1 { color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; }
h2 { color: #34495e; margin-top: 20px; }
.summary { background-color: #ecf0f1; padding: 15px; border-radius: 5px; margin: 20px 0; }
.stats { display: flex; justify-content: space-around; margin: 20px 0; }
.stat-item { background: #fff; padding: 10px 20px; border-radius: 5px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }
.task { border: 1px solid #ddd; padding: 10px; margin: 10px 0; border-radius: 5px; }
.completed { border-left: 4px solid #27ae60; }
.in_progress { border-left: 4px solid #f39c12; }
.pending { border-left: 4px solid #e74c3c; }
.highlights { background-color: #d4efdf; padding: 10px; border-radius: 5px; }
.risks { background-color: #fadbd8; padding: 10px; border-radius: 5px; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
.date-range { color: #7f8c8d; font-size: 14px; }
</style>
</head>
<body>
<h1>{{project_name}} - 第{{week}}周周报</h1>
<p class="date-range">时间范围:{{date_range.start}} 至 {{date_range.end}}</p>
<div class="summary">
<h2>📊 工作总结</h2>
<p>{{summary}}</p>
</div>
<div class="stats">
<div class="stat-item">总数: {{stats.total}}</div>
<div class="stat-item">✔ 完成: {{stats.completed}}</div>
<div class="stat-item">🔄 进行中: {{stats.in_progress}}</div>
<div class="stat-item">⏳ 待处理: {{stats.pending}}</div>
<div class="stat-item">⏱ 工时: {{stats.hours}}h</div>
</div>
<h2>📝 任务详情</h2>
{{tasks}}
<div class="task {{status}}">
<strong>{{title}}</strong>
<span style="color:#7f8c8d;"> - {{assignee}}</span>
<div>优先级: {{priority}} | 预计工时: {{hours}}h | 截止日期: {{deadline}}</div>
<div style="margin-top:5px;">{{description}}</div>
</div>
{{/tasks}}
<h2>🌟 亮点成果</h2>
<div class="highlights">
{{highlights}}
<div>- {{title}} ({{assignee}})</div>
{{/highlights}}
</div>
<h2>⚠️ 风险预警</h2>
<div class="risks">
{{risks}}
<div>- {{title}} - 截止: {{deadline}}</div>
{{/risks}}
</div>
<h2>👥 团队成员</h2>
<table>
<tr><th>姓名</th><th>角色</th></tr>
{{team_members}}
<tr><td>{{name}}</td><td>{{role}}</td></tr>
{{/team_members}}
</table>
</body>
</html>
主入口文件 (index.php)
<?php
require_once 'src/DataCollector.php';
require_once 'src/ReportGenerator.php';
require_once 'src/TemplateManager.php';
require_once 'src/ExportManager.php';
$config = require 'config/config.php';
// 初始化
$collector = new DataCollector('data/tasks.json');
$templateManager = new TemplateManager('templates');
$generator = new ReportGenerator($collector, $templateManager, $config);
$exporter = new ExportManager($config);
// 处理任务添加
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['add_task'])) {
$task = [
'title' => $_POST['title'],
'description' => $_POST['description'],
'assignee' => $_POST['assignee'],
'status' => $_POST['status'],
'priority' => $_POST['priority'],
'hours' => (float)$_POST['hours'],
'deadline' => $_POST['deadline']
];
$collector->saveTask($task);
}
// 获取页码参数
$year = isset($_GET['year']) ? (int)$_GET['year'] : date('Y');
$week = isset($_GET['week']) ? (int)$_GET['week'] : date('W');
// 生成报告
$report = $generator->generate($year, $week);
$exportFormat = isset($_GET['export']) ? $_GET['export'] : 'html';
if (isset($_GET['export'])) {
$exporter->export($report, $exportFormat);
exit;
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">项目周报系统</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.nav { display: flex; gap: 10px; margin-bottom: 20px; }
.btn { padding: 8px 16px; border: none; border-radius: 4px; cursor: pointer; }
.btn-primary { background: #3498db; color: white; }
.btn-success { background: #27ae60; color: white; }
.btn-warning { background: #f39c12; color: white; }
.btn-danger { background: #e74c3c; color: white; }
.task-form { border: 1px solid #ddd; padding: 20px; border-radius: 5px; margin: 20px 0; }
input, textarea, select { width: 100%; padding: 8px; margin: 5px 0; }
label { font-weight: bold; }
</style>
</head>
<body>
<h1>项目周报自动生成系统</h1>
<div class="nav">
<form method="get" style="display:inline;">
<input type="number" name="year" value="<?php echo $year; ?>" min="2024" max="2030">
第<input type="number" name="week" value="<?php echo $week; ?>" min="1" max="53">周
<button type="submit" class="btn btn-primary">生成报告</button>
</form>
<a href="?year=<?php echo $year; ?>&week=<?php echo $week; ?>&export=html" class="btn btn-success btn-primary">导出HTML</a>
<a href="?year=<?php echo $year; ?>&week=<?php echo $week; ?>&export=markdown" class="btn btn-primary">导出Markdown</a>
<a href="?year=<?php echo $year; ?>&week=<?php echo $week; ?>&export=pdf" class="btn btn-warning">导出PDF</a>
</div>
<div class="task-form">
<h2>添加新任务</h2>
<form method="post">
<label>任务标题:</label>
<input type="text" name="title" required>
<label>任务描述:</label>
<textarea name="description" rows="3"></textarea>
<div style="display:flex; gap:10px;">
<div style="flex:1;">
<label>负责人:</label>
<select name="assignee">
<?php foreach ($config['team_members'] as $member => $role): ?>
<option value="<?php echo $member; ?>"><?php echo $member; ?></option>
<?php endforeach; ?>
</select>
</div>
<div style="flex:1;">
<label>状态:</label>
<select name="status">
<option value="pending">待处理</option>
<option value="in_progress">进行中</option>
<option value="completed">已完成</option>
</select>
</div>
</div>
<div style="display:flex; gap:10px;">
<div style="flex:1;">
<label>优先级:</label>
<select name="priority">
<option value="low">低</option>
<option value="medium">中</option>
<option value="high">高</option>
</select>
</div>
<div style="flex:1;">
<label>预计工时:</label>
<input type="number" name="hours" step="0.5" value="4">
</div>
<div style="flex:1;">
<label>截止日期:</label>
<input type="date" name="deadline" required>
</div>
</div>
<button type="submit" name="add_task" class="btn btn-primary" style="margin-top:10px;">添加任务</button>
</form>
</div>
<div class="report-content">
<?php echo $report; ?>
</div>
</body>
</html>
使用说明
安装部署
# 创建项目目录结构
mkdir -p project-report/{config,src,templates,data}
# 将上述代码保存到对应文件中
cd project-report
# 确保有写入权限
chmod 755 data/
使用流程
- 添加任务:通过Web界面添加任务信息
- 自动生成:选择周数自动生成周报
- 导出格式:支持HTML、Markdown、PDF格式导出
- 数据统计:自动计算完成率、工时等统计信息
示例任务数据
[
{
"id": "6651e9a5c0f64",
"title": "用户登录功能开发",
"description": "实现OAuth2.0认证流程",
"assignee": "张三",
"status": "completed",
"priority": "high",
"hours": 16,
"deadline": "2024-05-25",
"created_at": "2024-05-20 10:30:00",
"week": 21,
"year": 2024
}
]
扩展功能建议
- 自动邮件发送:集成邮件服务自动发送周报
- 多项目支持:支持多个项目同时管理
- 数据库存储:用MySQL替代JSON文件存储
- 权限系统:不同角色(成员/管理者)不同权限
- 自动汇总:从Git、Jira等系统自动拉取任务状态
这个系统可以根据你的具体需求进行调整,比如添加更多统计维度、自定义模板样式、集成第三方工具等,需要我帮你调整某个具体功能吗?