Sure! Pl
This commit is contained in:
commit
98e2950b2e
18
.gitignore
vendored
Normal file
18
.gitignore
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/node_modules
|
||||||
|
/public/build
|
||||||
|
/public/hot
|
||||||
|
/public/storage
|
||||||
|
/storage/*.key
|
||||||
|
/vendor
|
||||||
|
.env
|
||||||
|
.env.backup
|
||||||
|
.env.production
|
||||||
|
.phpunit.result.cache
|
||||||
|
Homestead.json
|
||||||
|
Homestead.yaml
|
||||||
|
auth.json
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
/.fleet
|
||||||
|
/.idea
|
||||||
|
/.vscode
|
||||||
202
README.md
Normal file
202
README.md
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
# 智能周报生成器
|
||||||
|
|
||||||
|
基于阿里云千问Max大模型的智能周报生成应用,使用 Laravel 框架开发。
|
||||||
|
|
||||||
|
## ✨ 功能特性
|
||||||
|
|
||||||
|
- 📝 **任务管理**:动态添加/删除任务列表
|
||||||
|
- 🖼️ **图片上传**:支持为每个任务上传截图(可选)
|
||||||
|
- 🤖 **AI生成**:调用千问Max模型智能生成专业周报
|
||||||
|
- 📄 **Markdown预览**:实时预览生成的周报内容
|
||||||
|
- ⬇️ **多格式下载**:支持 Markdown 和 PDF 格式下载
|
||||||
|
- 🎨 **精美界面**:现代化深色主题,响应式设计
|
||||||
|
|
||||||
|
## 🛠️ 技术栈
|
||||||
|
|
||||||
|
- **后端**:PHP 8.0+ / Laravel 8
|
||||||
|
- **前端**:原生 HTML/CSS/JavaScript
|
||||||
|
- **AI模型**:阿里云通义千问 Max (qwen-max)
|
||||||
|
- **PDF生成**:DomPDF
|
||||||
|
|
||||||
|
## 📋 环境要求
|
||||||
|
|
||||||
|
- PHP >= 8.0
|
||||||
|
- Composer
|
||||||
|
- 以下 PHP 扩展:
|
||||||
|
- BCMath
|
||||||
|
- Ctype
|
||||||
|
- cURL
|
||||||
|
- DOM
|
||||||
|
- Fileinfo
|
||||||
|
- JSON
|
||||||
|
- Mbstring
|
||||||
|
- OpenSSL
|
||||||
|
- PDO
|
||||||
|
- Tokenizer
|
||||||
|
- XML
|
||||||
|
|
||||||
|
## 🚀 快速开始
|
||||||
|
|
||||||
|
### Windows 用户
|
||||||
|
|
||||||
|
1. **双击运行安装脚本**
|
||||||
|
```
|
||||||
|
install.bat
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **启动开发服务器**
|
||||||
|
```
|
||||||
|
start.bat
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **访问应用**
|
||||||
|
|
||||||
|
打开浏览器访问:http://localhost:8000
|
||||||
|
|
||||||
|
### 手动安装
|
||||||
|
|
||||||
|
1. **克隆项目**
|
||||||
|
```bash
|
||||||
|
cd E:\Workspace\weekreport
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **安装依赖**
|
||||||
|
```bash
|
||||||
|
composer install
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **配置环境**
|
||||||
|
```bash
|
||||||
|
# 复制环境配置文件
|
||||||
|
copy env.txt .env
|
||||||
|
|
||||||
|
# 或在 Linux/Mac 上
|
||||||
|
cp env.txt .env
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **生成应用密钥**
|
||||||
|
```bash
|
||||||
|
php artisan key:generate
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **创建存储链接**
|
||||||
|
```bash
|
||||||
|
php artisan storage:link
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **启动服务器**
|
||||||
|
```bash
|
||||||
|
php artisan serve
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚙️ 配置说明
|
||||||
|
|
||||||
|
### API 配置
|
||||||
|
|
||||||
|
在 `.env` 文件中配置千问 API:
|
||||||
|
|
||||||
|
```env
|
||||||
|
# 千问Max API配置
|
||||||
|
QWEN_API_KEY=sk-7ea0fac260574e4ea22c9de728e7bff9
|
||||||
|
QWEN_API_URL=https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions
|
||||||
|
QWEN_MODEL=qwen-max
|
||||||
|
```
|
||||||
|
|
||||||
|
### 数据库配置(可选)
|
||||||
|
|
||||||
|
默认使用 SQLite,如需使用 MySQL:
|
||||||
|
|
||||||
|
```env
|
||||||
|
DB_CONNECTION=mysql
|
||||||
|
DB_HOST=127.0.0.1
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_DATABASE=weekreport
|
||||||
|
DB_USERNAME=your_username
|
||||||
|
DB_PASSWORD=your_password
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📖 使用指南
|
||||||
|
|
||||||
|
1. **设置周报信息**
|
||||||
|
- 选择周报的开始和结束日期
|
||||||
|
- 填写作者姓名(可选)
|
||||||
|
|
||||||
|
2. **添加任务**
|
||||||
|
- 点击"添加新任务"按钮
|
||||||
|
- 填写任务描述
|
||||||
|
- 可选:上传任务相关截图
|
||||||
|
|
||||||
|
3. **生成周报**
|
||||||
|
- 点击"生成周报"按钮
|
||||||
|
- 等待 AI 生成专业周报
|
||||||
|
|
||||||
|
4. **预览与下载**
|
||||||
|
- 在右侧预览区查看生成结果
|
||||||
|
- 点击下载按钮获取 Markdown 或 PDF 格式
|
||||||
|
|
||||||
|
## 📁 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
weekreport/
|
||||||
|
├── app/
|
||||||
|
│ ├── Http/
|
||||||
|
│ │ └── Controllers/
|
||||||
|
│ │ └── WeekReportController.php # 主控制器
|
||||||
|
│ └── Providers/
|
||||||
|
├── config/
|
||||||
|
│ ├── app.php
|
||||||
|
│ ├── services.php # API配置
|
||||||
|
│ └── ...
|
||||||
|
├── public/
|
||||||
|
│ └── index.php # 入口文件
|
||||||
|
├── resources/
|
||||||
|
│ └── views/
|
||||||
|
│ └── weekreport/
|
||||||
|
│ └── index.blade.php # 前端页面
|
||||||
|
├── routes/
|
||||||
|
│ └── web.php # 路由定义
|
||||||
|
├── storage/
|
||||||
|
├── .env # 环境配置
|
||||||
|
├── composer.json
|
||||||
|
├── install.bat # Windows安装脚本
|
||||||
|
├── start.bat # Windows启动脚本
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 API 接口
|
||||||
|
|
||||||
|
| 方法 | 路径 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| GET | `/` | 显示周报生成页面 |
|
||||||
|
| POST | `/upload` | 上传任务图片 |
|
||||||
|
| POST | `/generate` | 生成周报 |
|
||||||
|
| POST | `/download/markdown` | 下载 Markdown 格式 |
|
||||||
|
| POST | `/download/pdf` | 下载 PDF 格式 |
|
||||||
|
|
||||||
|
## 🐛 常见问题
|
||||||
|
|
||||||
|
### Q: 生成周报时报错 "API Key 未配置"
|
||||||
|
|
||||||
|
确保 `.env` 文件中正确配置了 `QWEN_API_KEY`。
|
||||||
|
|
||||||
|
### Q: 图片上传后不显示
|
||||||
|
|
||||||
|
运行以下命令创建存储链接:
|
||||||
|
```bash
|
||||||
|
php artisan storage:link
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: PDF 中文乱码
|
||||||
|
|
||||||
|
确保系统安装了中文字体,DomPDF 会自动使用系统字体。
|
||||||
|
|
||||||
|
## 📄 许可证
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
## 🙏 致谢
|
||||||
|
|
||||||
|
- [阿里云百炼](https://bailian.console.aliyun.com/) - 提供千问大模型 API
|
||||||
|
- [Laravel](https://laravel.com/) - PHP Web 框架
|
||||||
|
- [DomPDF](https://github.com/dompdf/dompdf) - PDF 生成库
|
||||||
|
- [Remix Icon](https://remixicon.com/) - 图标库
|
||||||
21
app/Console/Kernel.php
Normal file
21
app/Console/Kernel.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console;
|
||||||
|
|
||||||
|
use Illuminate\Console\Scheduling\Schedule;
|
||||||
|
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||||
|
|
||||||
|
class Kernel extends ConsoleKernel
|
||||||
|
{
|
||||||
|
protected function schedule(Schedule $schedule): void
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function commands(): void
|
||||||
|
{
|
||||||
|
$this->load(__DIR__.'/Commands');
|
||||||
|
|
||||||
|
require base_path('routes/console.php');
|
||||||
|
}
|
||||||
|
}
|
||||||
22
app/Exceptions/Handler.php
Normal file
22
app/Exceptions/Handler.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class Handler extends ExceptionHandler
|
||||||
|
{
|
||||||
|
protected $dontFlash = [
|
||||||
|
'current_password',
|
||||||
|
'password',
|
||||||
|
'password_confirmation',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function register(): void
|
||||||
|
{
|
||||||
|
$this->reportable(function (Throwable $e) {
|
||||||
|
//
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
12
app/Http/Controllers/Controller.php
Normal file
12
app/Http/Controllers/Controller.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
|
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||||
|
use Illuminate\Routing\Controller as BaseController;
|
||||||
|
|
||||||
|
class Controller extends BaseController
|
||||||
|
{
|
||||||
|
use AuthorizesRequests, ValidatesRequests;
|
||||||
|
}
|
||||||
429
app/Http/Controllers/WeekReportController.php
Normal file
429
app/Http/Controllers/WeekReportController.php
Normal file
@ -0,0 +1,429 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Barryvdh\DomPDF\Facade\Pdf;
|
||||||
|
use Parsedown;
|
||||||
|
|
||||||
|
class WeekReportController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 显示周报生成页面
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return view('weekreport.index');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传任务图片
|
||||||
|
*/
|
||||||
|
public function uploadImage(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'image' => 'required|image|mimes:jpeg,png,jpg,gif,webp|max:10240',
|
||||||
|
]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$file = $request->file('image');
|
||||||
|
$filename = time() . '_' . uniqid() . '.' . $file->getClientOriginalExtension();
|
||||||
|
|
||||||
|
// 存储到public目录
|
||||||
|
$path = $file->storeAs('uploads', $filename, 'public');
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'path' => $path,
|
||||||
|
'url' => asset('storage/' . $path),
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => '图片上传失败: ' . $e->getMessage(),
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成周报
|
||||||
|
*/
|
||||||
|
public function generate(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'tasks' => 'required|array|min:1',
|
||||||
|
'tasks.*.description' => 'required|string|max:2000',
|
||||||
|
'tasks.*.image' => 'nullable|string',
|
||||||
|
'week_start' => 'nullable|date',
|
||||||
|
'week_end' => 'nullable|date',
|
||||||
|
'author' => 'nullable|string|max:100',
|
||||||
|
'next_week_tasks' => 'nullable|array',
|
||||||
|
'next_week_tasks.*' => 'nullable|string|max:2000',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$tasks = $request->input('tasks');
|
||||||
|
$weekStart = $request->input('week_start') ?? date('Y-m-d', strtotime('monday this week'));
|
||||||
|
$weekEnd = $request->input('week_end') ?? date('Y-m-d', strtotime('sunday this week'));
|
||||||
|
$author = (string) ($request->input('author') ?? '');
|
||||||
|
$nextWeekTasks = $request->input('next_week_tasks', []);
|
||||||
|
|
||||||
|
// 构建提示词
|
||||||
|
$taskList = $this->buildTaskList($tasks);
|
||||||
|
$nextWeekList = $this->buildNextWeekTaskList($nextWeekTasks);
|
||||||
|
$prompt = $this->buildPrompt($taskList, $weekStart, $weekEnd, $author, $nextWeekList);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 调用千问API
|
||||||
|
$reportContent = $this->callQwenApi($prompt);
|
||||||
|
|
||||||
|
// 插入图片到报告中
|
||||||
|
$reportWithImages = $this->insertImagesToReport($reportContent, $tasks);
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'report' => $reportWithImages,
|
||||||
|
'raw_report' => $reportContent,
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => '生成周报失败: ' . $e->getMessage(),
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建任务列表文本
|
||||||
|
*/
|
||||||
|
private function buildTaskList(array $tasks): string
|
||||||
|
{
|
||||||
|
$list = [];
|
||||||
|
foreach ($tasks as $index => $task) {
|
||||||
|
$num = $index + 1;
|
||||||
|
$desc = trim($task['description']);
|
||||||
|
$hasImage = !empty($task['image']) ? '(有截图)' : '';
|
||||||
|
$list[] = "{$num}. {$desc}{$hasImage}";
|
||||||
|
}
|
||||||
|
return implode("\n", $list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建下周任务列表文本
|
||||||
|
*/
|
||||||
|
private function buildNextWeekTaskList(array $tasks): string
|
||||||
|
{
|
||||||
|
if (empty($tasks)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$list = [];
|
||||||
|
foreach ($tasks as $index => $desc) {
|
||||||
|
if (!empty(trim($desc))) {
|
||||||
|
$num = $index + 1;
|
||||||
|
$list[] = "{$num}. " . trim($desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return implode("\n", $list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建完整提示词
|
||||||
|
*/
|
||||||
|
private function buildPrompt(string $taskList, string $weekStart, string $weekEnd, string $author, string $nextWeekList = ''): string
|
||||||
|
{
|
||||||
|
$authorInfo = $author ? "作者:{$author}" : "";
|
||||||
|
|
||||||
|
// 根据是否有下周计划来调整要求
|
||||||
|
$nextWeekSection = '';
|
||||||
|
$nextWeekRequirement = '';
|
||||||
|
|
||||||
|
if (!empty(trim($nextWeekList))) {
|
||||||
|
$nextWeekSection = "\n\n下周计划任务(用户提供):\n{$nextWeekList}";
|
||||||
|
$nextWeekRequirement = "\n8. 添加\"下周工作计划\"部分,根据用户提供的下周任务列表进行专业化描述和扩展";
|
||||||
|
} else {
|
||||||
|
$nextWeekRequirement = "\n8. 不要添加下周工作计划部分(用户未提供)";
|
||||||
|
}
|
||||||
|
|
||||||
|
return <<<PROMPT
|
||||||
|
你是一位专业的周报撰写助手。请根据以下任务列表,生成一份专业、详细的Markdown格式工作周报。
|
||||||
|
|
||||||
|
周报时间范围:{$weekStart} 至 {$weekEnd}
|
||||||
|
{$authorInfo}
|
||||||
|
|
||||||
|
本周完成的任务:
|
||||||
|
{$taskList}{$nextWeekSection}
|
||||||
|
|
||||||
|
要求:
|
||||||
|
1. 使用Markdown格式输出
|
||||||
|
2. 包含周报标题(使用一级标题)
|
||||||
|
3. 包含时间范围和作者信息(如果提供)
|
||||||
|
4. 对每个任务进行合理的分类和扩展描述
|
||||||
|
5. 每个任务需要包含:
|
||||||
|
- 任务名称
|
||||||
|
- 任务详细描述(根据原始描述进行适当扩展和专业化表述)
|
||||||
|
- 完成情况
|
||||||
|
6. 添加一个"本周工作总结"部分
|
||||||
|
7. 【重要】关于图片占位符:只有任务描述中明确标注了"(有截图)"的任务,才在该任务描述末尾单独一行添加占位符"[图片占位符-任务X]"(X为任务序号)。没有标注"(有截图)"的任务,绝对不要添加任何图片占位符{$nextWeekRequirement}
|
||||||
|
9. 语言专业、简洁、条理清晰
|
||||||
|
|
||||||
|
请直接输出Markdown格式的周报内容,不要有任何额外说明。
|
||||||
|
PROMPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用千问API
|
||||||
|
*/
|
||||||
|
private function callQwenApi(string $prompt): string
|
||||||
|
{
|
||||||
|
$apiKey = config('services.qwen.api_key');
|
||||||
|
$apiUrl = config('services.qwen.api_url');
|
||||||
|
$model = config('services.qwen.model');
|
||||||
|
|
||||||
|
if (empty($apiKey)) {
|
||||||
|
throw new \Exception('API Key 未配置');
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = Http::timeout(120)
|
||||||
|
->withoutVerifying()
|
||||||
|
->withHeaders([
|
||||||
|
'Authorization' => "Bearer {$apiKey}",
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
])
|
||||||
|
->post($apiUrl, [
|
||||||
|
'model' => $model,
|
||||||
|
'messages' => [
|
||||||
|
[
|
||||||
|
'role' => 'system',
|
||||||
|
'content' => '你是一位专业的工作周报撰写助手,擅长将简单的任务描述转化为专业、详细的周报内容。'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'role' => 'user',
|
||||||
|
'content' => $prompt
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'temperature' => 0.7,
|
||||||
|
'max_tokens' => 4000,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!$response->successful()) {
|
||||||
|
$error = $response->json('error.message') ?? $response->body();
|
||||||
|
throw new \Exception("API调用失败: {$error}");
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $response->json();
|
||||||
|
|
||||||
|
if (!isset($data['choices'][0]['message']['content'])) {
|
||||||
|
throw new \Exception('API返回数据格式错误');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data['choices'][0]['message']['content'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在报告中插入图片
|
||||||
|
*/
|
||||||
|
private function insertImagesToReport(string $report, array $tasks): string
|
||||||
|
{
|
||||||
|
foreach ($tasks as $index => $task) {
|
||||||
|
$num = $index + 1;
|
||||||
|
|
||||||
|
// 支持多种占位符格式
|
||||||
|
$placeholders = [
|
||||||
|
"[图片占位符-任务{$num}]",
|
||||||
|
"[图片占位符-{$num}]",
|
||||||
|
"[图片占位符{$num}]",
|
||||||
|
"【图片占位符-任务{$num}】",
|
||||||
|
"【图片占位符-{$num}】",
|
||||||
|
"【图片占位符{$num}】",
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!empty($task['image'])) {
|
||||||
|
$imageUrl = asset('storage/' . $task['image']);
|
||||||
|
$imageMarkdown = "\n\n\n";
|
||||||
|
foreach ($placeholders as $placeholder) {
|
||||||
|
$report = str_replace($placeholder, $imageMarkdown, $report);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 没有图片时移除所有占位符
|
||||||
|
foreach ($placeholders as $placeholder) {
|
||||||
|
$report = str_replace($placeholder, '', $report);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理所有未使用的占位符(各种格式)
|
||||||
|
$report = preg_replace('/\[图片占位符[-]?任务?\d*\]/', '', $report);
|
||||||
|
$report = preg_replace('/【图片占位符[-]?任务?\d*】/', '', $report);
|
||||||
|
$report = preg_replace('/\[?图片占位符[-—]?\d+\]?/', '', $report);
|
||||||
|
|
||||||
|
// 清理可能产生的多余空行
|
||||||
|
$report = preg_replace('/\n{3,}/', "\n\n", $report);
|
||||||
|
|
||||||
|
return $report;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载Markdown格式
|
||||||
|
*/
|
||||||
|
public function downloadMarkdown(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'content' => 'required|string',
|
||||||
|
'filename' => 'nullable|string|max:100',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$content = $request->input('content');
|
||||||
|
$filename = $request->input('filename', '周报_' . date('Y-m-d')) . '.md';
|
||||||
|
|
||||||
|
return response($content)
|
||||||
|
->header('Content-Type', 'text/markdown; charset=utf-8')
|
||||||
|
->header('Content-Disposition', 'attachment; filename="' . $filename . '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载PDF格式
|
||||||
|
*/
|
||||||
|
public function downloadPdf(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'content' => 'required|string',
|
||||||
|
'filename' => 'nullable|string|max:100',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$markdownContent = $request->input('content');
|
||||||
|
$filename = $request->input('filename', '周报_' . date('Y-m-d')) . '.pdf';
|
||||||
|
|
||||||
|
// 将Markdown转换为HTML
|
||||||
|
$parsedown = new Parsedown();
|
||||||
|
$htmlContent = $parsedown->text($markdownContent);
|
||||||
|
|
||||||
|
// 添加样式
|
||||||
|
$styledHtml = $this->wrapHtmlWithStyle($htmlContent);
|
||||||
|
|
||||||
|
// 生成PDF - 启用远程字体和图片
|
||||||
|
$pdf = Pdf::loadHTML($styledHtml);
|
||||||
|
$pdf->setPaper('A4', 'portrait');
|
||||||
|
$pdf->setOption('isRemoteEnabled', true);
|
||||||
|
$pdf->setOption('defaultFont', 'sans-serif');
|
||||||
|
|
||||||
|
return $pdf->download($filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为HTML添加样式
|
||||||
|
*/
|
||||||
|
private function wrapHtmlWithStyle(string $html): string
|
||||||
|
{
|
||||||
|
return <<<HTML
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||||
|
<style>
|
||||||
|
@font-face {
|
||||||
|
font-family: 'NotoSansSC';
|
||||||
|
src: url('https://fonts.gstatic.com/s/notosanssc/v36/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYg.woff2') format('woff2');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-family: 'NotoSansSC', 'SimHei', 'Microsoft YaHei', sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.8;
|
||||||
|
color: #333;
|
||||||
|
padding: 40px;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 24px;
|
||||||
|
color: #1a1a1a;
|
||||||
|
border-bottom: 3px solid #4a90d9;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-top: 25px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
padding-left: 10px;
|
||||||
|
border-left: 4px solid #4a90d9;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #34495e;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
ul, ol {
|
||||||
|
margin-left: 25px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
margin: 15px 0;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-family: Consolas, monospace;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
overflow-x: auto;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
blockquote {
|
||||||
|
border-left: 4px solid #ddd;
|
||||||
|
padding-left: 15px;
|
||||||
|
color: #666;
|
||||||
|
margin: 15px 0;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 15px 0;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 10px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
hr {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{$html}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
HTML;
|
||||||
|
}
|
||||||
|
}
|
||||||
59
app/Http/Kernel.php
Normal file
59
app/Http/Kernel.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||||
|
|
||||||
|
class Kernel extends HttpKernel
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The application's global HTTP middleware stack.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $middleware = [
|
||||||
|
\App\Http\Middleware\TrustProxies::class,
|
||||||
|
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
||||||
|
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||||
|
\App\Http\Middleware\TrimStrings::class,
|
||||||
|
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The application's route middleware groups.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $middlewareGroups = [
|
||||||
|
'web' => [
|
||||||
|
\App\Http\Middleware\EncryptCookies::class,
|
||||||
|
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||||
|
\Illuminate\Session\Middleware\StartSession::class,
|
||||||
|
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||||
|
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||||
|
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
|
],
|
||||||
|
|
||||||
|
'api' => [
|
||||||
|
'throttle:api',
|
||||||
|
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The application's route middleware.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $routeMiddleware = [
|
||||||
|
'auth' => \App\Http\Middleware\Authenticate::class,
|
||||||
|
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||||
|
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||||
|
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||||
|
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||||
|
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
|
||||||
|
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
|
||||||
|
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||||
|
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
||||||
|
];
|
||||||
|
}
|
||||||
21
app/Http/Middleware/Authenticate.php
Normal file
21
app/Http/Middleware/Authenticate.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Auth\Middleware\Authenticate as Middleware;
|
||||||
|
|
||||||
|
class Authenticate extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the path the user should be redirected to when they are not authenticated.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
protected function redirectTo($request)
|
||||||
|
{
|
||||||
|
if (! $request->expectsJson()) {
|
||||||
|
return route('login');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
app/Http/Middleware/EncryptCookies.php
Normal file
17
app/Http/Middleware/EncryptCookies.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
|
||||||
|
|
||||||
|
class EncryptCookies extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The names of the cookies that should not be encrypted.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $except = [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
17
app/Http/Middleware/PreventRequestsDuringMaintenance.php
Normal file
17
app/Http/Middleware/PreventRequestsDuringMaintenance.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
|
||||||
|
|
||||||
|
class PreventRequestsDuringMaintenance extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The URIs that should be reachable while maintenance mode is enabled.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $except = [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
32
app/Http/Middleware/RedirectIfAuthenticated.php
Normal file
32
app/Http/Middleware/RedirectIfAuthenticated.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Providers\RouteServiceProvider;
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class RedirectIfAuthenticated
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \Closure $next
|
||||||
|
* @param string|null ...$guards
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle(Request $request, Closure $next, ...$guards)
|
||||||
|
{
|
||||||
|
$guards = empty($guards) ? [null] : $guards;
|
||||||
|
|
||||||
|
foreach ($guards as $guard) {
|
||||||
|
if (Auth::guard($guard)->check()) {
|
||||||
|
return redirect(RouteServiceProvider::HOME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
19
app/Http/Middleware/TrimStrings.php
Normal file
19
app/Http/Middleware/TrimStrings.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
|
||||||
|
|
||||||
|
class TrimStrings extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The names of the attributes that should not be trimmed.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $except = [
|
||||||
|
'current_password',
|
||||||
|
'password',
|
||||||
|
'password_confirmation',
|
||||||
|
];
|
||||||
|
}
|
||||||
28
app/Http/Middleware/TrustProxies.php
Normal file
28
app/Http/Middleware/TrustProxies.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Http\Middleware\TrustProxies as Middleware;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class TrustProxies extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The trusted proxies for this application.
|
||||||
|
*
|
||||||
|
* @var array|string|null
|
||||||
|
*/
|
||||||
|
protected $proxies;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The headers that should be used to detect proxies.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $headers =
|
||||||
|
Request::HEADER_X_FORWARDED_FOR |
|
||||||
|
Request::HEADER_X_FORWARDED_HOST |
|
||||||
|
Request::HEADER_X_FORWARDED_PORT |
|
||||||
|
Request::HEADER_X_FORWARDED_PROTO |
|
||||||
|
Request::HEADER_X_FORWARDED_AWS_ELB;
|
||||||
|
}
|
||||||
17
app/Http/Middleware/VerifyCsrfToken.php
Normal file
17
app/Http/Middleware/VerifyCsrfToken.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
|
||||||
|
|
||||||
|
class VerifyCsrfToken extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The URIs that should be excluded from CSRF verification.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $except = [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
18
app/Providers/AppServiceProvider.php
Normal file
18
app/Providers/AppServiceProvider.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
|
class AppServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
public function register(): void
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
public function boot(): void
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
35
app/Providers/RouteServiceProvider.php
Normal file
35
app/Providers/RouteServiceProvider.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use Illuminate\Cache\RateLimiting\Limit;
|
||||||
|
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\RateLimiter;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
|
class RouteServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
public const HOME = '/';
|
||||||
|
|
||||||
|
public function boot(): void
|
||||||
|
{
|
||||||
|
$this->configureRateLimiting();
|
||||||
|
|
||||||
|
$this->routes(function () {
|
||||||
|
Route::middleware('api')
|
||||||
|
->prefix('api')
|
||||||
|
->group(base_path('routes/api.php'));
|
||||||
|
|
||||||
|
Route::middleware('web')
|
||||||
|
->group(base_path('routes/web.php'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configureRateLimiting(): void
|
||||||
|
{
|
||||||
|
RateLimiter::for('api', function (Request $request) {
|
||||||
|
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
19
artisan
Normal file
19
artisan
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
define('LARAVEL_START', microtime(true));
|
||||||
|
|
||||||
|
require __DIR__.'/vendor/autoload.php';
|
||||||
|
|
||||||
|
$app = require_once __DIR__.'/bootstrap/app.php';
|
||||||
|
|
||||||
|
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
|
||||||
|
|
||||||
|
$status = $kernel->handle(
|
||||||
|
$input = new Symfony\Component\Console\Input\ArgvInput,
|
||||||
|
new Symfony\Component\Console\Output\ConsoleOutput
|
||||||
|
);
|
||||||
|
|
||||||
|
$kernel->terminate($input, $status);
|
||||||
|
|
||||||
|
exit($status);
|
||||||
24
bootstrap/app.php
Normal file
24
bootstrap/app.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Application;
|
||||||
|
|
||||||
|
$app = new Application(
|
||||||
|
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
|
||||||
|
);
|
||||||
|
|
||||||
|
$app->singleton(
|
||||||
|
Illuminate\Contracts\Http\Kernel::class,
|
||||||
|
App\Http\Kernel::class
|
||||||
|
);
|
||||||
|
|
||||||
|
$app->singleton(
|
||||||
|
Illuminate\Contracts\Console\Kernel::class,
|
||||||
|
App\Console\Kernel::class
|
||||||
|
);
|
||||||
|
|
||||||
|
$app->singleton(
|
||||||
|
Illuminate\Contracts\Debug\ExceptionHandler::class,
|
||||||
|
App\Exceptions\Handler::class
|
||||||
|
);
|
||||||
|
|
||||||
|
return $app;
|
||||||
2
bootstrap/cache/.gitignore
vendored
Normal file
2
bootstrap/cache/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
67
composer.json
Normal file
67
composer.json
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
{
|
||||||
|
"name": "weekreport/app",
|
||||||
|
"type": "project",
|
||||||
|
"description": "周报生成应用 - 基于千问Max模型",
|
||||||
|
"keywords": ["laravel", "weekreport", "qwen"],
|
||||||
|
"license": "MIT",
|
||||||
|
"require": {
|
||||||
|
"php": "^8.0",
|
||||||
|
"guzzlehttp/guzzle": "^7.5",
|
||||||
|
"laravel/framework": "^8.83",
|
||||||
|
"laravel/tinker": "^2.7",
|
||||||
|
"barryvdh/laravel-dompdf": "^2.0",
|
||||||
|
"erusev/parsedown": "^1.7"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"fakerphp/faker": "^1.21",
|
||||||
|
"mockery/mockery": "^1.5",
|
||||||
|
"nunomaduro/collision": "^5.0",
|
||||||
|
"phpunit/phpunit": "^9.5"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"App\\": "app/",
|
||||||
|
"Database\\Factories\\": "database/factories/",
|
||||||
|
"Database\\Seeders\\": "database/seeders/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Tests\\": "tests/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"post-autoload-dump": [
|
||||||
|
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
||||||
|
"@php artisan package:discover --ansi"
|
||||||
|
],
|
||||||
|
"post-update-cmd": [
|
||||||
|
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
|
||||||
|
],
|
||||||
|
"post-root-package-install": [
|
||||||
|
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
||||||
|
],
|
||||||
|
"post-create-project-cmd": [
|
||||||
|
"@php artisan key:generate --ansi"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"dont-discover": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"optimize-autoloader": true,
|
||||||
|
"preferred-install": "dist",
|
||||||
|
"sort-packages": true,
|
||||||
|
"allow-plugins": {
|
||||||
|
"pestphp/pest-plugin": true,
|
||||||
|
"php-http/discovery": true
|
||||||
|
},
|
||||||
|
"audit": {
|
||||||
|
"block-insecure": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"prefer-stable": true
|
||||||
|
}
|
||||||
7790
composer.lock
generated
Normal file
7790
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
84
config/app.php
Normal file
84
config/app.php
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'name' => env('APP_NAME', '周报生成器'),
|
||||||
|
'env' => env('APP_ENV', 'production'),
|
||||||
|
'debug' => (bool) env('APP_DEBUG', false),
|
||||||
|
'url' => env('APP_URL', 'http://localhost'),
|
||||||
|
'asset_url' => env('ASSET_URL'),
|
||||||
|
'timezone' => 'Asia/Shanghai',
|
||||||
|
'locale' => 'zh_CN',
|
||||||
|
'fallback_locale' => 'en',
|
||||||
|
'faker_locale' => 'zh_CN',
|
||||||
|
'key' => env('APP_KEY'),
|
||||||
|
'cipher' => 'AES-256-CBC',
|
||||||
|
'providers' => [
|
||||||
|
Illuminate\Auth\AuthServiceProvider::class,
|
||||||
|
Illuminate\Broadcasting\BroadcastServiceProvider::class,
|
||||||
|
Illuminate\Bus\BusServiceProvider::class,
|
||||||
|
Illuminate\Cache\CacheServiceProvider::class,
|
||||||
|
Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
|
||||||
|
Illuminate\Cookie\CookieServiceProvider::class,
|
||||||
|
Illuminate\Database\DatabaseServiceProvider::class,
|
||||||
|
Illuminate\Encryption\EncryptionServiceProvider::class,
|
||||||
|
Illuminate\Filesystem\FilesystemServiceProvider::class,
|
||||||
|
Illuminate\Foundation\Providers\FoundationServiceProvider::class,
|
||||||
|
Illuminate\Hashing\HashServiceProvider::class,
|
||||||
|
Illuminate\Mail\MailServiceProvider::class,
|
||||||
|
Illuminate\Notifications\NotificationServiceProvider::class,
|
||||||
|
Illuminate\Pagination\PaginationServiceProvider::class,
|
||||||
|
Illuminate\Pipeline\PipelineServiceProvider::class,
|
||||||
|
Illuminate\Queue\QueueServiceProvider::class,
|
||||||
|
Illuminate\Redis\RedisServiceProvider::class,
|
||||||
|
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
|
||||||
|
Illuminate\Session\SessionServiceProvider::class,
|
||||||
|
Illuminate\Translation\TranslationServiceProvider::class,
|
||||||
|
Illuminate\Validation\ValidationServiceProvider::class,
|
||||||
|
Illuminate\View\ViewServiceProvider::class,
|
||||||
|
|
||||||
|
// Application Service Providers
|
||||||
|
App\Providers\AppServiceProvider::class,
|
||||||
|
App\Providers\RouteServiceProvider::class,
|
||||||
|
],
|
||||||
|
'aliases' => [
|
||||||
|
'App' => Illuminate\Support\Facades\App::class,
|
||||||
|
'Arr' => Illuminate\Support\Arr::class,
|
||||||
|
'Artisan' => Illuminate\Support\Facades\Artisan::class,
|
||||||
|
'Auth' => Illuminate\Support\Facades\Auth::class,
|
||||||
|
'Blade' => Illuminate\Support\Facades\Blade::class,
|
||||||
|
'Broadcast' => Illuminate\Support\Facades\Broadcast::class,
|
||||||
|
'Bus' => Illuminate\Support\Facades\Bus::class,
|
||||||
|
'Cache' => Illuminate\Support\Facades\Cache::class,
|
||||||
|
'Config' => Illuminate\Support\Facades\Config::class,
|
||||||
|
'Cookie' => Illuminate\Support\Facades\Cookie::class,
|
||||||
|
'Crypt' => Illuminate\Support\Facades\Crypt::class,
|
||||||
|
'Date' => Illuminate\Support\Facades\Date::class,
|
||||||
|
'DB' => Illuminate\Support\Facades\DB::class,
|
||||||
|
'Eloquent' => Illuminate\Database\Eloquent\Model::class,
|
||||||
|
'Event' => Illuminate\Support\Facades\Event::class,
|
||||||
|
'File' => Illuminate\Support\Facades\File::class,
|
||||||
|
'Gate' => Illuminate\Support\Facades\Gate::class,
|
||||||
|
'Hash' => Illuminate\Support\Facades\Hash::class,
|
||||||
|
'Http' => Illuminate\Support\Facades\Http::class,
|
||||||
|
'Js' => Illuminate\Support\Js::class,
|
||||||
|
'Lang' => Illuminate\Support\Facades\Lang::class,
|
||||||
|
'Log' => Illuminate\Support\Facades\Log::class,
|
||||||
|
'Mail' => Illuminate\Support\Facades\Mail::class,
|
||||||
|
'Notification' => Illuminate\Support\Facades\Notification::class,
|
||||||
|
'Password' => Illuminate\Support\Facades\Password::class,
|
||||||
|
'Queue' => Illuminate\Support\Facades\Queue::class,
|
||||||
|
'RateLimiter' => Illuminate\Support\Facades\RateLimiter::class,
|
||||||
|
'Redirect' => Illuminate\Support\Facades\Redirect::class,
|
||||||
|
'Request' => Illuminate\Support\Facades\Request::class,
|
||||||
|
'Response' => Illuminate\Support\Facades\Response::class,
|
||||||
|
'Route' => Illuminate\Support\Facades\Route::class,
|
||||||
|
'Schema' => Illuminate\Support\Facades\Schema::class,
|
||||||
|
'Session' => Illuminate\Support\Facades\Session::class,
|
||||||
|
'Storage' => Illuminate\Support\Facades\Storage::class,
|
||||||
|
'Str' => Illuminate\Support\Str::class,
|
||||||
|
'URL' => Illuminate\Support\Facades\URL::class,
|
||||||
|
'Validator' => Illuminate\Support\Facades\Validator::class,
|
||||||
|
'View' => Illuminate\Support\Facades\View::class,
|
||||||
|
'PDF' => Barryvdh\DomPDF\Facade\Pdf::class,
|
||||||
|
],
|
||||||
|
];
|
||||||
19
config/cache.php
Normal file
19
config/cache.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'default' => env('CACHE_DRIVER', 'file'),
|
||||||
|
'stores' => [
|
||||||
|
'file' => [
|
||||||
|
'driver' => 'file',
|
||||||
|
'path' => storage_path('framework/cache/data'),
|
||||||
|
'lock_path' => storage_path('framework/cache/data'),
|
||||||
|
],
|
||||||
|
'array' => [
|
||||||
|
'driver' => 'array',
|
||||||
|
'serialize' => false,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'),
|
||||||
|
];
|
||||||
12
config/cors.php
Normal file
12
config/cors.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'paths' => ['api/*', 'sanctum/csrf-cookie'],
|
||||||
|
'allowed_methods' => ['*'],
|
||||||
|
'allowed_origins' => ['*'],
|
||||||
|
'allowed_origins_patterns' => [],
|
||||||
|
'allowed_headers' => ['*'],
|
||||||
|
'exposed_headers' => [],
|
||||||
|
'max_age' => 0,
|
||||||
|
'supports_credentials' => false,
|
||||||
|
];
|
||||||
17
config/database.php
Normal file
17
config/database.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'default' => env('DB_CONNECTION', 'sqlite'),
|
||||||
|
'connections' => [
|
||||||
|
'sqlite' => [
|
||||||
|
'driver' => 'sqlite',
|
||||||
|
'url' => env('DATABASE_URL'),
|
||||||
|
'database' => env('DB_DATABASE', database_path('database.sqlite')),
|
||||||
|
'prefix' => '',
|
||||||
|
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'migrations' => 'migrations',
|
||||||
|
];
|
||||||
284
config/dompdf.php
Normal file
284
config/dompdf.php
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return array(
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Settings
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Set some default values. It is possible to add all defines that can be set
|
||||||
|
| in dompdf_config.inc.php. You can also override the entire config file.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
'show_warnings' => false, // Throw an Exception on warnings from dompdf
|
||||||
|
|
||||||
|
'public_path' => null, // Override the public path if needed
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Dejavu Sans font is missing glyphs for converted entities, turn it off if you need to show € and £.
|
||||||
|
*/
|
||||||
|
'convert_entities' => true,
|
||||||
|
|
||||||
|
'options' => array(
|
||||||
|
/**
|
||||||
|
* The location of the DOMPDF font directory
|
||||||
|
*
|
||||||
|
* The location of the directory where DOMPDF will store fonts and font metrics
|
||||||
|
* Note: This directory must exist and be writable by the webserver process.
|
||||||
|
* *Please note the trailing slash.*
|
||||||
|
*
|
||||||
|
* Notes regarding fonts:
|
||||||
|
* Additional .afm font metrics can be added by executing load_font.php from command line.
|
||||||
|
*
|
||||||
|
* Only the original "Base 14 fonts" are present on all pdf viewers. Additional fonts must
|
||||||
|
* be embedded in the pdf file or the PDF may not display correctly. This can significantly
|
||||||
|
* increase file size unless font subsetting is enabled. Before embedding a font please
|
||||||
|
* review your rights under the font license.
|
||||||
|
*
|
||||||
|
* Any font specification in the source HTML is translated to the closest font available
|
||||||
|
* in the font directory.
|
||||||
|
*
|
||||||
|
* The pdf standard "Base 14 fonts" are:
|
||||||
|
* Courier, Courier-Bold, Courier-BoldOblique, Courier-Oblique,
|
||||||
|
* Helvetica, Helvetica-Bold, Helvetica-BoldOblique, Helvetica-Oblique,
|
||||||
|
* Times-Roman, Times-Bold, Times-BoldItalic, Times-Italic,
|
||||||
|
* Symbol, ZapfDingbats.
|
||||||
|
*/
|
||||||
|
"font_dir" => storage_path('fonts'), // advised by dompdf (https://github.com/dompdf/dompdf/pull/782)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The location of the DOMPDF font cache directory
|
||||||
|
*
|
||||||
|
* This directory contains the cached font metrics for the fonts used by DOMPDF.
|
||||||
|
* This directory can be the same as DOMPDF_FONT_DIR
|
||||||
|
*
|
||||||
|
* Note: This directory must exist and be writable by the webserver process.
|
||||||
|
*/
|
||||||
|
"font_cache" => storage_path('fonts'),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The location of a temporary directory.
|
||||||
|
*
|
||||||
|
* The directory specified must be writeable by the webserver process.
|
||||||
|
* The temporary directory is required to download remote images and when
|
||||||
|
* using the PDFLib back end.
|
||||||
|
*/
|
||||||
|
"temp_dir" => sys_get_temp_dir(),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ==== IMPORTANT ====
|
||||||
|
*
|
||||||
|
* dompdf's "chroot": Prevents dompdf from accessing system files or other
|
||||||
|
* files on the webserver. All local files opened by dompdf must be in a
|
||||||
|
* subdirectory of this directory. DO NOT set it to '/' since this could
|
||||||
|
* allow an attacker to use dompdf to read any files on the server. This
|
||||||
|
* should be an absolute path.
|
||||||
|
* This is only checked on command line call by dompdf.php, but not by
|
||||||
|
* direct class use like:
|
||||||
|
* $dompdf = new DOMPDF(); $dompdf->load_html($htmldata); $dompdf->render(); $pdfdata = $dompdf->output();
|
||||||
|
*/
|
||||||
|
"chroot" => realpath(base_path()),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protocol whitelist
|
||||||
|
*
|
||||||
|
* Protocols and PHP wrappers allowed in URIs, and the validation rules
|
||||||
|
* that determine if a resouce may be loaded. Full support is not guaranteed
|
||||||
|
* for the protocols/wrappers specified
|
||||||
|
* by this array.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
'allowed_protocols' => [
|
||||||
|
"file://" => ["rules" => []],
|
||||||
|
"http://" => ["rules" => []],
|
||||||
|
"https://" => ["rules" => []]
|
||||||
|
],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
'log_output_file' => null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to enable font subsetting or not.
|
||||||
|
*/
|
||||||
|
"enable_font_subsetting" => true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The PDF rendering backend to use
|
||||||
|
*
|
||||||
|
* Valid settings are 'PDFLib', 'CPDF' (the bundled R&OS PDF class), 'GD' and
|
||||||
|
* 'auto'. 'auto' will look for PDFLib and use it if found, or if not it will
|
||||||
|
* fall back on CPDF. 'GD' renders PDFs to graphic files. {@link
|
||||||
|
* Canvas_Factory} ultimately determines which rendering class to instantiate
|
||||||
|
* based on this setting.
|
||||||
|
*
|
||||||
|
* Both PDFLib & CPDF rendering backends provide sufficient rendering
|
||||||
|
* capabilities for dompdf, however additional features (e.g. object,
|
||||||
|
* image and font support, etc.) differ between backends. Please see
|
||||||
|
* {@link PDFLib_Adapter} for more information on the PDFLib backend
|
||||||
|
* and {@link CPDF_Adapter} and lib/class.pdf.php for more information
|
||||||
|
* on CPDF. Also see the documentation for each backend at the links
|
||||||
|
* below.
|
||||||
|
*
|
||||||
|
* The GD rendering backend is a little different than PDFLib and
|
||||||
|
* CPDF. Several features of CPDF and PDFLib are not supported or do
|
||||||
|
* not make any sense when creating image files. For example,
|
||||||
|
* multiple pages are not supported, nor are PDF 'objects'. Have a
|
||||||
|
* look at {@link GD_Adapter} for more information. GD support is
|
||||||
|
* experimental, so use it at your own risk.
|
||||||
|
*
|
||||||
|
* @link http://www.pdflib.com
|
||||||
|
* @link http://www.ros.co.nz/pdf
|
||||||
|
* @link http://www.php.net/image
|
||||||
|
*/
|
||||||
|
"pdf_backend" => "CPDF",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PDFlib license key
|
||||||
|
*
|
||||||
|
* If you are using a licensed, commercial version of PDFlib, specify
|
||||||
|
* your license key here. If you are using PDFlib-Lite or are evaluating
|
||||||
|
* the commercial version of PDFlib, comment out this setting.
|
||||||
|
*
|
||||||
|
* @link http://www.pdflib.com
|
||||||
|
*
|
||||||
|
* If pdflib present in web server and auto or selected explicitely above,
|
||||||
|
* a real license code must exist!
|
||||||
|
*/
|
||||||
|
//"DOMPDF_PDFLIB_LICENSE" => "your license key here",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* html target media view which should be rendered into pdf.
|
||||||
|
* List of types and parsing rules for future extensions:
|
||||||
|
* http://www.w3.org/TR/REC-html40/types.html
|
||||||
|
* screen, tty, tv, projection, handheld, print, braille, aural, all
|
||||||
|
* Note: aural is deprecated in CSS 2.1 because it is replaced by speech in CSS 3.
|
||||||
|
* Note, even though the generated pdf file is intended for print output,
|
||||||
|
* the desired content might be different (e.g. screen or projection view of html file).
|
||||||
|
* Therefore allow specification of content here.
|
||||||
|
*/
|
||||||
|
"default_media_type" => "screen",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default paper size.
|
||||||
|
*
|
||||||
|
* North America standard is "letter"; other countries generally "a4"
|
||||||
|
*
|
||||||
|
* @see CPDF_Adapter::PAPER_SIZES for valid sizes ('letter', 'legal', 'A4', etc.)
|
||||||
|
*/
|
||||||
|
"default_paper_size" => "a4",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default paper orientation.
|
||||||
|
*
|
||||||
|
* The orientation of the page (portrait or landscape).
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
'default_paper_orientation' => "portrait",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default font family
|
||||||
|
*
|
||||||
|
* Used if no suitable fonts can be found. This must exist in the font folder.
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
"default_font" => "serif",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image DPI setting
|
||||||
|
*
|
||||||
|
* This setting determines the default DPI setting for images and fonts. The
|
||||||
|
* DPI may be overridden for inline images by explictly setting the
|
||||||
|
* image's width & height style attributes (i.e. if the image's native
|
||||||
|
* width is 600 pixels and you specify the image's width as 72 points,
|
||||||
|
* the image will have a DPI of 600 in the rendered PDF. The DPI of
|
||||||
|
* background images can not be overridden and is controlled entirely
|
||||||
|
* via this parameter.
|
||||||
|
*
|
||||||
|
* For the purposes of DOMPDF, pixels per inch (PPI) = dots per inch (DPI).
|
||||||
|
* If a size in html is given as px (or without unit as image size),
|
||||||
|
* this tells the corresponding size in pt.
|
||||||
|
* This adjusts the relative sizes to be similar to the rendering of the
|
||||||
|
* html page in a reference browser.
|
||||||
|
*
|
||||||
|
* In pdf, always 1 pt = 1/72 inch
|
||||||
|
*
|
||||||
|
* Rendering resolution of various browsers in px per inch:
|
||||||
|
* Windows Firefox and Internet Explorer:
|
||||||
|
* SystemControl->Display properties->FontResolution: Default:96, largefonts:120, custom:?
|
||||||
|
* Linux Firefox:
|
||||||
|
* about:config *resolution: Default:96
|
||||||
|
* (xorg screen dimension in mm and Desktop font dpi settings are ignored)
|
||||||
|
*
|
||||||
|
* Take care about extra font/image zoom factor of browser.
|
||||||
|
*
|
||||||
|
* In images, <img> size in pixel attribute, img css style, are overriding
|
||||||
|
* the real image dimension in px for rendering.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
"dpi" => 96,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable inline PHP
|
||||||
|
*
|
||||||
|
* If this setting is set to true then DOMPDF will automatically evaluate
|
||||||
|
* inline PHP contained within <script type="text/php"> ... </script> tags.
|
||||||
|
*
|
||||||
|
* Enabling this for documents you do not trust (e.g. arbitrary remote html
|
||||||
|
* pages) is a security risk. Set this option to false if you wish to process
|
||||||
|
* untrusted documents.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
"enable_php" => false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable inline Javascript
|
||||||
|
*
|
||||||
|
* If this setting is set to true then DOMPDF will automatically insert
|
||||||
|
* JavaScript code contained within <script type="text/javascript"> ... </script> tags.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
"enable_javascript" => true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable remote file access
|
||||||
|
*
|
||||||
|
* If this setting is set to true, DOMPDF will access remote sites for
|
||||||
|
* images and CSS files as required.
|
||||||
|
* This is required for part of test case www/test/image_variants.html through www/examples.php
|
||||||
|
*
|
||||||
|
* Attention!
|
||||||
|
* This can be a security risk, in particular in combination with DOMPDF_ENABLE_PHP and
|
||||||
|
* allowing remote access to dompdf.php or on allowing remote html code to be passed to
|
||||||
|
* $dompdf = new DOMPDF(, $dompdf->load_html(...,
|
||||||
|
* This allows anonymous users to download legally doubtful internet content which on
|
||||||
|
* tracing back appears to being downloaded by your server, or allows malicious php code
|
||||||
|
* in remote html pages to be executed by your server with your account privileges.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
"enable_remote" => true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A ratio applied to the fonts height to be more like browsers' line height
|
||||||
|
*/
|
||||||
|
"font_height_ratio" => 1.1,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the HTML5 Lib parser
|
||||||
|
*
|
||||||
|
* @deprecated This feature is now always on in dompdf 2.x
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
"enable_html5_parser" => true,
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
);
|
||||||
22
config/filesystems.php
Normal file
22
config/filesystems.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'default' => env('FILESYSTEM_DISK', 'local'),
|
||||||
|
'disks' => [
|
||||||
|
'local' => [
|
||||||
|
'driver' => 'local',
|
||||||
|
'root' => storage_path('app'),
|
||||||
|
'throw' => false,
|
||||||
|
],
|
||||||
|
'public' => [
|
||||||
|
'driver' => 'local',
|
||||||
|
'root' => storage_path('app/public'),
|
||||||
|
'url' => env('APP_URL').'/storage',
|
||||||
|
'visibility' => 'public',
|
||||||
|
'throw' => false,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'links' => [
|
||||||
|
public_path('storage') => storage_path('app/public'),
|
||||||
|
],
|
||||||
|
];
|
||||||
35
config/logging.php
Normal file
35
config/logging.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Monolog\Handler\NullHandler;
|
||||||
|
use Monolog\Handler\StreamHandler;
|
||||||
|
use Monolog\Handler\SyslogUdpHandler;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'default' => env('LOG_CHANNEL', 'stack'),
|
||||||
|
'deprecations' => [
|
||||||
|
'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
|
||||||
|
'trace' => false,
|
||||||
|
],
|
||||||
|
'channels' => [
|
||||||
|
'stack' => [
|
||||||
|
'driver' => 'stack',
|
||||||
|
'channels' => ['single'],
|
||||||
|
'ignore_exceptions' => false,
|
||||||
|
],
|
||||||
|
'single' => [
|
||||||
|
'driver' => 'single',
|
||||||
|
'path' => storage_path('logs/laravel.log'),
|
||||||
|
'level' => env('LOG_LEVEL', 'debug'),
|
||||||
|
],
|
||||||
|
'daily' => [
|
||||||
|
'driver' => 'daily',
|
||||||
|
'path' => storage_path('logs/laravel.log'),
|
||||||
|
'level' => env('LOG_LEVEL', 'debug'),
|
||||||
|
'days' => 14,
|
||||||
|
],
|
||||||
|
'null' => [
|
||||||
|
'driver' => 'monolog',
|
||||||
|
'handler' => NullHandler::class,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
9
config/services.php
Normal file
9
config/services.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'qwen' => [
|
||||||
|
'api_key' => env('QWEN_API_KEY'),
|
||||||
|
'api_url' => env('QWEN_API_URL', 'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions'),
|
||||||
|
'model' => env('QWEN_MODEL', 'qwen-max'),
|
||||||
|
],
|
||||||
|
];
|
||||||
24
config/session.php
Normal file
24
config/session.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'driver' => env('SESSION_DRIVER', 'file'),
|
||||||
|
'lifetime' => env('SESSION_LIFETIME', 120),
|
||||||
|
'expire_on_close' => false,
|
||||||
|
'encrypt' => false,
|
||||||
|
'files' => storage_path('framework/sessions'),
|
||||||
|
'connection' => env('SESSION_CONNECTION'),
|
||||||
|
'table' => 'sessions',
|
||||||
|
'store' => env('SESSION_STORE'),
|
||||||
|
'lottery' => [2, 100],
|
||||||
|
'cookie' => env(
|
||||||
|
'SESSION_COOKIE',
|
||||||
|
Str::slug(env('APP_NAME', 'laravel'), '_').'_session'
|
||||||
|
),
|
||||||
|
'path' => '/',
|
||||||
|
'domain' => env('SESSION_DOMAIN'),
|
||||||
|
'secure' => env('SESSION_SECURE_COOKIE'),
|
||||||
|
'http_only' => true,
|
||||||
|
'same_site' => 'lax',
|
||||||
|
];
|
||||||
11
config/view.php
Normal file
11
config/view.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'paths' => [
|
||||||
|
resource_path('views'),
|
||||||
|
],
|
||||||
|
'compiled' => env(
|
||||||
|
'VIEW_COMPILED_PATH',
|
||||||
|
realpath(storage_path('framework/views'))
|
||||||
|
),
|
||||||
|
];
|
||||||
0
database/database.sqlite
Normal file
0
database/database.sqlite
Normal file
24
env.txt
Normal file
24
env.txt
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
APP_NAME="周报生成器"
|
||||||
|
APP_ENV=local
|
||||||
|
APP_KEY=base64:rNxPFnFb3oK5WrCqOuPEkgXAKAZFLKSvGdIcfPpPaYQ=
|
||||||
|
APP_DEBUG=true
|
||||||
|
APP_URL=http://localhost:8000
|
||||||
|
|
||||||
|
LOG_CHANNEL=stack
|
||||||
|
LOG_DEPRECATIONS_CHANNEL=null
|
||||||
|
LOG_LEVEL=debug
|
||||||
|
|
||||||
|
DB_CONNECTION=sqlite
|
||||||
|
DB_DATABASE=database/database.sqlite
|
||||||
|
|
||||||
|
BROADCAST_DRIVER=log
|
||||||
|
CACHE_DRIVER=file
|
||||||
|
FILESYSTEM_DISK=local
|
||||||
|
QUEUE_CONNECTION=sync
|
||||||
|
SESSION_DRIVER=file
|
||||||
|
SESSION_LIFETIME=120
|
||||||
|
|
||||||
|
# 千问Max API配置
|
||||||
|
QWEN_API_KEY=sk-7ea0fac260574e4ea22c9de728e7bff9
|
||||||
|
QWEN_API_URL=https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions
|
||||||
|
QWEN_MODEL=qwen-max
|
||||||
60
install.bat
Normal file
60
install.bat
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001 >nul
|
||||||
|
echo ========================================
|
||||||
|
echo 智能周报生成器 - 安装脚本
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM 检查 PHP
|
||||||
|
where php >nul 2>nul
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo [错误] 未找到 PHP,请先安装 PHP 8.1 或更高版本
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
REM 检查 Composer
|
||||||
|
where composer >nul 2>nul
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo [错误] 未找到 Composer,请先安装 Composer
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [1/4] 安装 PHP 依赖...
|
||||||
|
call composer install
|
||||||
|
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo [错误] Composer 安装失败
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo [2/4] 配置环境文件...
|
||||||
|
if not exist ".env" (
|
||||||
|
copy env.txt .env >nul
|
||||||
|
echo 已创建 .env 文件
|
||||||
|
) else (
|
||||||
|
echo .env 文件已存在,跳过
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo [3/4] 生成应用密钥...
|
||||||
|
php artisan key:generate --ansi
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo [4/4] 创建存储软链接...
|
||||||
|
php artisan storage:link
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ========================================
|
||||||
|
echo 安装完成!
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
echo 启动开发服务器:
|
||||||
|
echo php artisan serve
|
||||||
|
echo.
|
||||||
|
echo 然后访问:http://localhost:8000
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
21
public/.htaccess
Normal file
21
public/.htaccess
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
<IfModule mod_negotiation.c>
|
||||||
|
Options -MultiViews -Indexes
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
RewriteEngine On
|
||||||
|
|
||||||
|
# Handle Authorization Header
|
||||||
|
RewriteCond %{HTTP:Authorization} .
|
||||||
|
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
||||||
|
|
||||||
|
# Redirect Trailing Slashes If Not A Folder...
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
RewriteCond %{REQUEST_URI} (.+)/$
|
||||||
|
RewriteRule ^ %1 [L,R=301]
|
||||||
|
|
||||||
|
# Send Requests To Front Controller...
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
RewriteRule ^ index.php [L]
|
||||||
|
</IfModule>
|
||||||
25
public/index.php
Normal file
25
public/index.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Contracts\Http\Kernel;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
define('LARAVEL_START', microtime(true));
|
||||||
|
|
||||||
|
// 检查是否处于维护模式
|
||||||
|
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
|
||||||
|
require $maintenance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自动加载
|
||||||
|
require __DIR__.'/../vendor/autoload.php';
|
||||||
|
|
||||||
|
// 启动应用
|
||||||
|
$app = require_once __DIR__.'/../bootstrap/app.php';
|
||||||
|
|
||||||
|
$kernel = $app->make(Kernel::class);
|
||||||
|
|
||||||
|
$response = $kernel->handle(
|
||||||
|
$request = Request::capture()
|
||||||
|
)->send();
|
||||||
|
|
||||||
|
$kernel->terminate($request, $response);
|
||||||
2
public/robots.txt
Normal file
2
public/robots.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
User-agent: *
|
||||||
|
Disallow:
|
||||||
1439
resources/views/weekreport/index.blade.php
Normal file
1439
resources/views/weekreport/index.blade.php
Normal file
File diff suppressed because it is too large
Load Diff
5
routes/api.php
Normal file
5
routes/api.php
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
|
// API routes can be added here if needed
|
||||||
8
routes/console.php
Normal file
8
routes/console.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Inspiring;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
|
||||||
|
Artisan::command('inspire', function () {
|
||||||
|
$this->comment(Inspiring::quote());
|
||||||
|
})->purpose('Display an inspiring quote');
|
||||||
17
routes/web.php
Normal file
17
routes/web.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
use App\Http\Controllers\WeekReportController;
|
||||||
|
|
||||||
|
// 主页面
|
||||||
|
Route::get('/', [WeekReportController::class, 'index'])->name('home');
|
||||||
|
|
||||||
|
// 上传图片
|
||||||
|
Route::post('/upload', [WeekReportController::class, 'uploadImage'])->name('upload');
|
||||||
|
|
||||||
|
// 生成周报
|
||||||
|
Route::post('/generate', [WeekReportController::class, 'generate'])->name('generate');
|
||||||
|
|
||||||
|
// 下载周报
|
||||||
|
Route::post('/download/markdown', [WeekReportController::class, 'downloadMarkdown'])->name('download.markdown');
|
||||||
|
Route::post('/download/pdf', [WeekReportController::class, 'downloadPdf'])->name('download.pdf');
|
||||||
12
start.bat
Normal file
12
start.bat
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001 >nul
|
||||||
|
echo ========================================
|
||||||
|
echo 智能周报生成器 - 启动服务
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
echo 正在启动开发服务器...
|
||||||
|
echo 访问地址: http://localhost:8000
|
||||||
|
echo.
|
||||||
|
echo 按 Ctrl+C 停止服务器
|
||||||
|
echo.
|
||||||
|
php artisan serve --host=localhost --port=8000
|
||||||
3
storage/app/.gitignore
vendored
Normal file
3
storage/app/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
*
|
||||||
|
!public/
|
||||||
|
!.gitignore
|
||||||
2
storage/app/public/.gitignore
vendored
Normal file
2
storage/app/public/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
3
storage/framework/cache/.gitignore
vendored
Normal file
3
storage/framework/cache/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
*
|
||||||
|
!data/
|
||||||
|
!.gitignore
|
||||||
2
storage/framework/cache/data/.gitignore
vendored
Normal file
2
storage/framework/cache/data/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
2
storage/framework/sessions/.gitignore
vendored
Normal file
2
storage/framework/sessions/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
2
storage/framework/views/.gitignore
vendored
Normal file
2
storage/framework/views/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
2
storage/logs/.gitignore
vendored
Normal file
2
storage/logs/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
Loading…
Reference in New Issue
Block a user