Initial commit: 日记系统
完整的日记记录系统 - Django 后端 (diary app) - 前端页面 - 部署脚本 (本地 + 云端) - Nginx 配置 - 数据迁移和同步工具
This commit is contained in:
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.db
|
||||||
|
*.sqlite3
|
||||||
|
static/
|
||||||
|
media/
|
||||||
|
.env
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
107
DEPLOYMENT_REPORT.md
Normal file
107
DEPLOYMENT_REPORT.md
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
# 日记系统 - 部署报告
|
||||||
|
|
||||||
|
## ✅ 部署完成
|
||||||
|
|
||||||
|
**时间**: 2026-04-14
|
||||||
|
**部署目标**: 云服务器 (cssc.datalibstar.com)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 部署状态
|
||||||
|
|
||||||
|
| 组件 | 状态 | 端口 | 备注 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| Gunicorn | ✅ 运行中 | 8002 | 后端 API 服务 |
|
||||||
|
| Nginx | ✅ 运行中 | 8001 | 反向代理 + 静态文件 |
|
||||||
|
| SQLite | ✅ 已创建 | - | 本地数据库 |
|
||||||
|
| 日记数据 | ✅ 已同步 | - | 第一天日记已创建 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🌐 访问地址
|
||||||
|
|
||||||
|
### 云服务器
|
||||||
|
- **主页**: http://cssc.datalibstar.com:8001/
|
||||||
|
- **API**: http://cssc.datalibstar.com:8001/api/entries/
|
||||||
|
- **Admin**: http://cssc.datalibstar.com:8001/admin/
|
||||||
|
|
||||||
|
### 本地
|
||||||
|
- **主页**: http://127.0.0.1:8001/
|
||||||
|
- **API**: http://127.0.0.1:8001/api/entries/
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 注意事项
|
||||||
|
|
||||||
|
### 1. 安全组端口
|
||||||
|
如果无法从外部访问云服务器,请在**腾讯云控制台**的安全组中开放端口:
|
||||||
|
- **8001** (日记系统 Web 访问)
|
||||||
|
|
||||||
|
### 2. 城市手册冲突
|
||||||
|
- 城市手册使用 Docker 占用 80 端口
|
||||||
|
- 日记系统使用 8001 端口,无冲突
|
||||||
|
- 如需使用 80 端口,需调整城市手册 Docker 配置
|
||||||
|
|
||||||
|
### 3. 数据库
|
||||||
|
- 云服务器使用 **SQLite** (本地文件)
|
||||||
|
- 本地部署使用 **PostgreSQL** (内网数据库)
|
||||||
|
- 两地数据不互通
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 服务器文件位置
|
||||||
|
|
||||||
|
```
|
||||||
|
/home/ubuntu/diary-system/
|
||||||
|
├── backend/ # Django 后端
|
||||||
|
│ ├── diary/ # 日记应用
|
||||||
|
│ ├── db.sqlite3 # SQLite 数据库
|
||||||
|
│ └── manage.py
|
||||||
|
├── frontend/ # 前端页面
|
||||||
|
│ └── index.html
|
||||||
|
├── deploy_cloud.sh # 部署脚本
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 常用命令
|
||||||
|
|
||||||
|
### 查看服务状态
|
||||||
|
```bash
|
||||||
|
# Gunicorn 状态
|
||||||
|
sudo systemctl status diary-system
|
||||||
|
|
||||||
|
# Nginx 状态
|
||||||
|
sudo systemctl status nginx
|
||||||
|
|
||||||
|
# 查看日志
|
||||||
|
sudo journalctl -u diary-system -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### 重启服务
|
||||||
|
```bash
|
||||||
|
sudo systemctl restart diary-system
|
||||||
|
sudo systemctl restart nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
### 查看日记数据
|
||||||
|
```bash
|
||||||
|
cd /home/ubuntu/diary-system/backend
|
||||||
|
python3 manage.py shell
|
||||||
|
>>> from diary.models import DiaryEntry
|
||||||
|
>>> DiaryEntry.objects.all()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 下一步
|
||||||
|
|
||||||
|
1. **开放安全组端口** (如需外部访问)
|
||||||
|
2. **配置域名** (可选,使用子域名如 diary.cssc.datalibstar.com)
|
||||||
|
3. **设置 HTTPS** (可选,使用 Let's Encrypt)
|
||||||
|
4. **定期备份数据库** (`/home/ubuntu/diary-system/backend/db.sqlite3`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_部署完成!🎉_
|
||||||
67
README.md
Executable file
67
README.md
Executable file
@@ -0,0 +1,67 @@
|
|||||||
|
# 码神的日记系统
|
||||||
|
|
||||||
|
⚡ 记录每天的进步与成长
|
||||||
|
|
||||||
|
## 技术架构
|
||||||
|
|
||||||
|
- **后端**: Django + Django REST Framework
|
||||||
|
- **前端**: 原生 HTML/JS (轻量级)
|
||||||
|
- **数据库**: PostgreSQL (与城市手册共用)
|
||||||
|
- **部署**: Gunicorn + Nginx
|
||||||
|
|
||||||
|
## 快速启动
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /root/.openclaw/workspace/diary-system
|
||||||
|
chmod +x start.sh
|
||||||
|
./start.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## 同步日记
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 sync_diary.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## 访问地址
|
||||||
|
|
||||||
|
### 本地部署
|
||||||
|
- **主页**: http://127.0.0.1:8001/
|
||||||
|
- **API**: http://127.0.0.1:8001/api/entries/
|
||||||
|
- **Admin**: http://127.0.0.1:8001/admin/
|
||||||
|
|
||||||
|
### 云服务器部署
|
||||||
|
- **主页**: http://cssc.datalibstar.com:8001/
|
||||||
|
- **API**: http://cssc.datalibstar.com:8001/api/entries/
|
||||||
|
- **Admin**: http://cssc.datalibstar.com:8001/admin/
|
||||||
|
|
||||||
|
⚠️ **注意**:如果无法访问云服务器,请在腾讯云控制台安全组中开放端口 `8001`
|
||||||
|
|
||||||
|
## API 接口
|
||||||
|
|
||||||
|
### 日记
|
||||||
|
- `GET /api/entries/` - 获取所有日记
|
||||||
|
- `GET /api/entries/today/` - 获取今天的日记
|
||||||
|
- `GET /api/entries/recent/` - 获取最近 7 天的日记
|
||||||
|
- `GET /api/entries/stats/` - 获取统计信息
|
||||||
|
- `POST /api/entries/` - 创建日记
|
||||||
|
- `PUT /api/entries/{id}/` - 更新日记
|
||||||
|
|
||||||
|
### 经验总结
|
||||||
|
- `GET /api/experiences/` - 获取所有经验
|
||||||
|
- `GET /api/experiences/recent/` - 获取最近 10 条经验
|
||||||
|
- `GET /api/experiences/by_category/` - 按类别分组
|
||||||
|
- `POST /api/experiences/` - 创建经验总结
|
||||||
|
- `PUT /api/experiences/{id}/` - 更新经验
|
||||||
|
|
||||||
|
## 数据库
|
||||||
|
|
||||||
|
使用现有的 PostgreSQL 数据库 (`cssc`),自动创建以下表:
|
||||||
|
- `diary_diaryentry` - 日记条目
|
||||||
|
- `diary_dailyprogress` - 每日进度
|
||||||
|
|
||||||
|
## 与城市手册的区别
|
||||||
|
|
||||||
|
- 端口不同(日记系统用 8001/8002,城市手册用 80/8000)
|
||||||
|
- 更轻量级的前端
|
||||||
|
- 专注于个人日记和进步追踪
|
||||||
23
add_first_experience.py
Normal file
23
add_first_experience.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""添加第一条经验总结"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'diary_system.settings')
|
||||||
|
sys.path.insert(0, '/root/.openclaw/workspace/diary-system/backend')
|
||||||
|
|
||||||
|
import django
|
||||||
|
django.setup()
|
||||||
|
|
||||||
|
from diary.models import Experience
|
||||||
|
|
||||||
|
# 创建第一条经验总结
|
||||||
|
exp = Experience.objects.create(
|
||||||
|
title="云服务器 Gunicorn 路径问题",
|
||||||
|
category="deployment",
|
||||||
|
problem="Gunicorn systemd 服务启动失败,错误:Failed to locate executable /usr/bin/gunicorn",
|
||||||
|
solution="1. 使用 pip3 install gunicorn 安装\n2. 查找 gunicorn 实际路径:/home/ubuntu/.local/bin/gunicorn\n3. 修改 systemd 服务文件,添加 Environment=PATH 并更新 ExecStart 路径",
|
||||||
|
lesson_learned="在云服务器上使用 pip 安装的包可能在用户目录,需要确认实际路径并在 systemd 中正确配置"
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"✅ 经验总结已创建:{exp.title}")
|
||||||
|
print(f" 类别:{exp.get_category_display()}")
|
||||||
23
add_first_experience_cloud.py
Normal file
23
add_first_experience_cloud.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""添加第一条经验总结 - 云服务器版"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'diary_system.settings')
|
||||||
|
sys.path.insert(0, '/home/ubuntu/diary-system/backend')
|
||||||
|
|
||||||
|
import django
|
||||||
|
django.setup()
|
||||||
|
|
||||||
|
from diary.models import Experience
|
||||||
|
|
||||||
|
# 创建第一条经验总结
|
||||||
|
exp = Experience.objects.create(
|
||||||
|
title="云服务器 Gunicorn 路径问题",
|
||||||
|
category="deployment",
|
||||||
|
problem="Gunicorn systemd 服务启动失败,错误:Failed to locate executable /usr/bin/gunicorn",
|
||||||
|
solution="1. 使用 pip3 install gunicorn 安装\n2. 查找 gunicorn 实际路径:/home/ubuntu/.local/bin/gunicorn\n3. 修改 systemd 服务文件,添加 Environment=PATH 并更新 ExecStart 路径",
|
||||||
|
lesson_learned="在云服务器上使用 pip 安装的包可能在用户目录,需要确认实际路径并在 systemd 中正确配置"
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"✅ 经验总结已创建:{exp.title}")
|
||||||
|
print(f" 类别:{exp.get_category_display()}")
|
||||||
1
backend/diary/__init__.py
Executable file
1
backend/diary/__init__.py
Executable file
@@ -0,0 +1 @@
|
|||||||
|
# Diary app
|
||||||
52
backend/diary/migrations/0001_initial.py
Executable file
52
backend/diary/migrations/0001_initial.py
Executable file
@@ -0,0 +1,52 @@
|
|||||||
|
# Generated by Django 4.2.11 on 2026-04-14 08:19
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='DiaryEntry',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('date', models.DateField(unique=True, verbose_name='日期')),
|
||||||
|
('title', models.CharField(default='每日日记', max_length=200, verbose_name='标题')),
|
||||||
|
('completed_tasks', models.TextField(blank=True, default='', verbose_name='完成的任务')),
|
||||||
|
('learned', models.TextField(blank=True, default='', verbose_name='学到的东西')),
|
||||||
|
('problems', models.TextField(blank=True, default='', verbose_name='遇到的问题和解决方案')),
|
||||||
|
('reflections', models.TextField(blank=True, default='', verbose_name='想法和反思')),
|
||||||
|
('improvements', models.TextField(blank=True, default='', verbose_name='进步点')),
|
||||||
|
('plans', models.TextField(blank=True, default='', verbose_name='明日计划')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': '日记',
|
||||||
|
'verbose_name_plural': '日记',
|
||||||
|
'ordering': ['-date'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='DailyProgress',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('category', models.CharField(max_length=50, verbose_name='类别')),
|
||||||
|
('description', models.TextField(verbose_name='描述')),
|
||||||
|
('progress_percent', models.IntegerField(default=0, verbose_name='进度百分比')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||||
|
('entry', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='progresses', to='diary.diaryentry')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': '进度',
|
||||||
|
'verbose_name_plural': '进度',
|
||||||
|
'ordering': ['-created_at'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
31
backend/diary/migrations/0002_experience.py
Normal file
31
backend/diary/migrations/0002_experience.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Generated by Django 4.2.11 on 2026-04-14 08:48
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('diary', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Experience',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('title', models.CharField(max_length=200, verbose_name='标题')),
|
||||||
|
('category', models.CharField(choices=[('deployment', '📦 部署'), ('development', '💻 开发'), ('database', '🗄️ 数据库'), ('permission', '🔐 权限'), ('network', '🌐 网络'), ('other', '其他')], max_length=50, verbose_name='类别')),
|
||||||
|
('problem', models.TextField(verbose_name='问题描述')),
|
||||||
|
('solution', models.TextField(verbose_name='解决方案')),
|
||||||
|
('lesson_learned', models.TextField(blank=True, default='', verbose_name='经验教训')),
|
||||||
|
('date', models.DateField(auto_now_add=True, verbose_name='日期')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': '经验总结',
|
||||||
|
'verbose_name_plural': '经验总结',
|
||||||
|
'ordering': ['-date', '-created_at'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
0
backend/diary/migrations/__init__.py
Executable file
0
backend/diary/migrations/__init__.py
Executable file
66
backend/diary/models.py
Executable file
66
backend/diary/models.py
Executable file
@@ -0,0 +1,66 @@
|
|||||||
|
from django.db import models
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
class DiaryEntry(models.Model):
|
||||||
|
"""日记条目"""
|
||||||
|
date = models.DateField('日期', unique=True)
|
||||||
|
title = models.CharField('标题', max_length=200, default='每日日记')
|
||||||
|
completed_tasks = models.TextField('完成的任务', blank=True, default='')
|
||||||
|
learned = models.TextField('学到的东西', blank=True, default='')
|
||||||
|
problems = models.TextField('遇到的问题和解决方案', blank=True, default='')
|
||||||
|
reflections = models.TextField('想法和反思', blank=True, default='')
|
||||||
|
improvements = models.TextField('进步点', blank=True, default='')
|
||||||
|
plans = models.TextField('明日计划', blank=True, default='')
|
||||||
|
created_at = models.DateTimeField('创建时间', auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField('更新时间', auto_now=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['-date']
|
||||||
|
verbose_name = '日记'
|
||||||
|
verbose_name_plural = '日记'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.date} - {self.title}"
|
||||||
|
|
||||||
|
|
||||||
|
class Experience(models.Model):
|
||||||
|
"""经验总结 - 记录遇到的问题和解决方法"""
|
||||||
|
title = models.CharField('标题', max_length=200)
|
||||||
|
category = models.CharField('类别', max_length=50, choices=[
|
||||||
|
('deployment', '📦 部署'),
|
||||||
|
('development', '💻 开发'),
|
||||||
|
('database', '🗄️ 数据库'),
|
||||||
|
('permission', '🔐 权限'),
|
||||||
|
('network', '🌐 网络'),
|
||||||
|
('other', '其他'),
|
||||||
|
])
|
||||||
|
problem = models.TextField('问题描述')
|
||||||
|
solution = models.TextField('解决方案')
|
||||||
|
lesson_learned = models.TextField('经验教训', blank=True, default='')
|
||||||
|
date = models.DateField('日期', auto_now_add=True)
|
||||||
|
created_at = models.DateTimeField('创建时间', auto_now_add=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['-date', '-created_at']
|
||||||
|
verbose_name = '经验总结'
|
||||||
|
verbose_name_plural = '经验总结'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.category} - {self.title}"
|
||||||
|
|
||||||
|
|
||||||
|
class DailyProgress(models.Model):
|
||||||
|
"""每日进度追踪"""
|
||||||
|
entry = models.ForeignKey(DiaryEntry, on_delete=models.CASCADE, related_name='progresses')
|
||||||
|
category = models.CharField('类别', max_length=50)
|
||||||
|
description = models.TextField('描述')
|
||||||
|
progress_percent = models.IntegerField('进度百分比', default=0)
|
||||||
|
created_at = models.DateTimeField('创建时间', auto_now_add=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['-created_at']
|
||||||
|
verbose_name = '进度'
|
||||||
|
verbose_name_plural = '进度'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.entry.date} - {self.category}: {self.progress_percent}%"
|
||||||
19
backend/diary/serializers.py
Executable file
19
backend/diary/serializers.py
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
from rest_framework import serializers
|
||||||
|
from .models import DiaryEntry, DailyProgress, Experience
|
||||||
|
|
||||||
|
class ExperienceSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Experience
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
class DailyProgressSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = DailyProgress
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
class DiaryEntrySerializer(serializers.ModelSerializer):
|
||||||
|
progresses = DailyProgressSerializer(many=True, read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = DiaryEntry
|
||||||
|
fields = '__all__'
|
||||||
12
backend/diary/urls.py
Executable file
12
backend/diary/urls.py
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
from django.urls import path, include
|
||||||
|
from rest_framework.routers import DefaultRouter
|
||||||
|
from .views import DiaryEntryViewSet, DailyProgressViewSet, ExperienceViewSet
|
||||||
|
|
||||||
|
router = DefaultRouter()
|
||||||
|
router.register(r'entries', DiaryEntryViewSet)
|
||||||
|
router.register(r'progress', DailyProgressViewSet)
|
||||||
|
router.register(r'experiences', ExperienceViewSet)
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('', include(router.urls)),
|
||||||
|
]
|
||||||
62
backend/diary/views.py
Executable file
62
backend/diary/views.py
Executable file
@@ -0,0 +1,62 @@
|
|||||||
|
from rest_framework import viewsets
|
||||||
|
from rest_framework.decorators import action
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from django.utils import timezone
|
||||||
|
from .models import DiaryEntry, DailyProgress, Experience
|
||||||
|
from .serializers import DiaryEntrySerializer, DailyProgressSerializer, ExperienceSerializer
|
||||||
|
|
||||||
|
class DiaryEntryViewSet(viewsets.ModelViewSet):
|
||||||
|
queryset = DiaryEntry.objects.all()
|
||||||
|
serializer_class = DiaryEntrySerializer
|
||||||
|
|
||||||
|
@action(detail=False, methods=['get'])
|
||||||
|
def today(self, request):
|
||||||
|
"""获取今天的日记"""
|
||||||
|
today = timezone.now().date()
|
||||||
|
entry, created = DiaryEntry.objects.get_or_create(date=today)
|
||||||
|
serializer = self.get_serializer(entry)
|
||||||
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
@action(detail=False, methods=['get'])
|
||||||
|
def recent(self, request):
|
||||||
|
"""获取最近 7 天的日记"""
|
||||||
|
entries = DiaryEntry.objects.order_by('-date')[:7]
|
||||||
|
serializer = self.get_serializer(entries, many=True)
|
||||||
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
@action(detail=False, methods=['get'])
|
||||||
|
def stats(self, request):
|
||||||
|
"""获取统计信息"""
|
||||||
|
total_entries = DiaryEntry.objects.count()
|
||||||
|
return Response({
|
||||||
|
'total_entries': total_entries,
|
||||||
|
'first_entry': DiaryEntry.objects.order_by('date').first().date if total_entries > 0 else None,
|
||||||
|
'latest_entry': DiaryEntry.objects.order_by('-date').first().date if total_entries > 0 else None,
|
||||||
|
})
|
||||||
|
|
||||||
|
class DailyProgressViewSet(viewsets.ModelViewSet):
|
||||||
|
queryset = DailyProgress.objects.all()
|
||||||
|
serializer_class = DailyProgressSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class ExperienceViewSet(viewsets.ModelViewSet):
|
||||||
|
queryset = Experience.objects.all()
|
||||||
|
serializer_class = ExperienceSerializer
|
||||||
|
|
||||||
|
@action(detail=False, methods=['get'])
|
||||||
|
def by_category(self, request):
|
||||||
|
"""按类别分组获取经验"""
|
||||||
|
categories = {}
|
||||||
|
for exp in Experience.objects.all():
|
||||||
|
cat = exp.get_category_display()
|
||||||
|
if cat not in categories:
|
||||||
|
categories[cat] = []
|
||||||
|
categories[cat].append(ExperienceSerializer(exp).data)
|
||||||
|
return Response(categories)
|
||||||
|
|
||||||
|
@action(detail=False, methods=['get'])
|
||||||
|
def recent(self, request):
|
||||||
|
"""获取最近 10 条经验"""
|
||||||
|
experiences = Experience.objects.order_by('-date', '-created_at')[:10]
|
||||||
|
serializer = self.get_serializer(experiences, many=True)
|
||||||
|
return Response(serializer.data)
|
||||||
1
backend/diary_system/__init__.py
Executable file
1
backend/diary_system/__init__.py
Executable file
@@ -0,0 +1 @@
|
|||||||
|
# Diary System Project
|
||||||
103
backend/diary_system/settings.py
Executable file
103
backend/diary_system/settings.py
Executable file
@@ -0,0 +1,103 @@
|
|||||||
|
"""
|
||||||
|
Django settings for diary_system project.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
import os
|
||||||
|
|
||||||
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
|
SECRET_KEY = 'django-insecure-diary-system-secret-key-change-in-production'
|
||||||
|
|
||||||
|
DEBUG = os.environ.get('DEBUG', 'True').lower() == 'true'
|
||||||
|
|
||||||
|
ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '*').split(',')
|
||||||
|
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
'django.contrib.admin',
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.staticfiles',
|
||||||
|
'rest_framework',
|
||||||
|
'corsheaders',
|
||||||
|
'diary',
|
||||||
|
]
|
||||||
|
|
||||||
|
MIDDLEWARE = [
|
||||||
|
'django.middleware.security.SecurityMiddleware',
|
||||||
|
'corsheaders.middleware.CorsMiddleware',
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
|
]
|
||||||
|
|
||||||
|
ROOT_URLCONF = 'diary_system.urls'
|
||||||
|
|
||||||
|
TEMPLATES = [
|
||||||
|
{
|
||||||
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
|
'DIRS': [],
|
||||||
|
'APP_DIRS': True,
|
||||||
|
'OPTIONS': {
|
||||||
|
'context_processors': [
|
||||||
|
'django.template.context_processors.debug',
|
||||||
|
'django.template.context_processors.request',
|
||||||
|
'django.contrib.auth.context_processors.auth',
|
||||||
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
WSGI_APPLICATION = 'diary_system.wsgi.application'
|
||||||
|
|
||||||
|
# 数据库配置 - 云服务器使用 SQLite,本地使用 PostgreSQL
|
||||||
|
if os.path.exists('/home/ubuntu/diary-system'):
|
||||||
|
# 云服务器配置 - SQLite
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
|
'NAME': BASE_DIR / 'db.sqlite3',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
# 本地配置 - PostgreSQL
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.postgresql',
|
||||||
|
'NAME': os.environ.get('DB_NAME', 'cssc'),
|
||||||
|
'USER': os.environ.get('DB_USER', 'coder'),
|
||||||
|
'PASSWORD': os.environ.get('DB_PASSWORD', '825670wl'),
|
||||||
|
'HOST': os.environ.get('DB_HOST', '10.2.0.100'),
|
||||||
|
'PORT': os.environ.get('DB_PORT', '5432'),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
|
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
|
||||||
|
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'},
|
||||||
|
{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
|
||||||
|
{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
|
||||||
|
]
|
||||||
|
|
||||||
|
LANGUAGE_CODE = 'zh-hans'
|
||||||
|
TIME_ZONE = 'UTC'
|
||||||
|
USE_I18N = True
|
||||||
|
USE_TZ = True
|
||||||
|
|
||||||
|
STATIC_URL = 'static/'
|
||||||
|
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
|
||||||
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
|
|
||||||
|
CORS_ALLOW_ALL_ORIGINS = True
|
||||||
|
|
||||||
|
REST_FRAMEWORK = {
|
||||||
|
'DEFAULT_PERMISSION_CLASSES': [
|
||||||
|
'rest_framework.permissions.AllowAny',
|
||||||
|
]
|
||||||
|
}
|
||||||
10
backend/diary_system/urls.py
Executable file
10
backend/diary_system/urls.py
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
"""
|
||||||
|
URL configuration for diary_system project.
|
||||||
|
"""
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.urls import path, include
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('admin/', admin.site.urls),
|
||||||
|
path('api/', include('diary.urls')),
|
||||||
|
]
|
||||||
11
backend/diary_system/wsgi.py
Executable file
11
backend/diary_system/wsgi.py
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
"""
|
||||||
|
WSGI config for diary_system project.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'diary_system.settings')
|
||||||
|
|
||||||
|
application = get_wsgi_application()
|
||||||
20
backend/manage.py
Executable file
20
backend/manage.py
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""Django's command-line utility for administrative tasks."""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Run administrative tasks."""
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'diary_system.settings')
|
||||||
|
try:
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
except ImportError as exc:
|
||||||
|
raise ImportError(
|
||||||
|
"Couldn't import Django. Are you sure it's installed and "
|
||||||
|
"available on your PYTHONPATH environment variable? Did you "
|
||||||
|
"forget to activate a virtual environment?"
|
||||||
|
) from exc
|
||||||
|
execute_from_command_line(sys.argv)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
4
backend/requirements.txt
Executable file
4
backend/requirements.txt
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
Django>=4.2
|
||||||
|
djangorestframework>=3.14
|
||||||
|
django-cors-headers>=4.0
|
||||||
|
psycopg2-binary>=2.9
|
||||||
8
collect_static.py
Executable file
8
collect_static.py
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'diary_system.settings')
|
||||||
|
sys.path.insert(0, '/root/.openclaw/workspace/diary-system/backend')
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
sys.argv = ['manage.py', 'collectstatic', '--noinput']
|
||||||
|
execute_from_command_line(sys.argv)
|
||||||
31
create_first_entry.py
Normal file
31
create_first_entry.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'diary_system.settings')
|
||||||
|
sys.path.insert(0, '/home/ubuntu/diary-system/backend')
|
||||||
|
|
||||||
|
import django
|
||||||
|
django.setup()
|
||||||
|
|
||||||
|
from diary.models import DiaryEntry
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
|
entry, created = DiaryEntry.objects.get_or_create(date=date.today())
|
||||||
|
entry.title = "第一天 - 日记系统上线"
|
||||||
|
entry.completed_tasks = """- 创建日记系统框架
|
||||||
|
- 开发 Web 日记系统(Django + 轻量前端)
|
||||||
|
- 配置 Nginx 反向代理
|
||||||
|
- 数据库迁移和同步
|
||||||
|
- 部署到云服务器"""
|
||||||
|
entry.learned = """- 掌握了 Django 项目快速搭建
|
||||||
|
- 学习了 Nginx 配置和权限管理
|
||||||
|
- 解决了云服务器部署问题"""
|
||||||
|
entry.reflections = """- 日记系统应该保持简洁,避免过度设计
|
||||||
|
- 每天记录进步可以帮助持续改进"""
|
||||||
|
entry.improvements = """1. 系统建设:建立了日记系统的基础框架
|
||||||
|
2. Web 开发:独立完成全栈 Web 应用开发
|
||||||
|
3. 问题解决:快速解决权限和部署问题"""
|
||||||
|
entry.save()
|
||||||
|
|
||||||
|
print(f"✅ 日记已创建:{entry.date}")
|
||||||
|
print(f" 标题:{entry.title}")
|
||||||
86
deploy.sh
Executable file
86
deploy.sh
Executable file
@@ -0,0 +1,86 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# 日记系统 - 自动部署脚本
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "🚀 开始部署日记系统..."
|
||||||
|
|
||||||
|
# 1. 安装依赖
|
||||||
|
echo "📦 安装 Python 依赖..."
|
||||||
|
cd /root/.openclaw/workspace/diary-system/backend
|
||||||
|
pip3 install -r requirements.txt --break-system-packages -q
|
||||||
|
|
||||||
|
# 2. 数据库迁移
|
||||||
|
echo "🗄️ 执行数据库迁移..."
|
||||||
|
python3 manage.py makemigrations diary --noinput
|
||||||
|
python3 manage.py migrate --noinput
|
||||||
|
|
||||||
|
# 3. 收集静态文件
|
||||||
|
echo "📁 收集静态文件..."
|
||||||
|
mkdir -p /root/.openclaw/workspace/diary-system/backend/static
|
||||||
|
python3 manage.py collectstatic --noinput
|
||||||
|
|
||||||
|
# 4. 创建 Gunicorn 服务
|
||||||
|
echo "⚙️ 创建 Gunicorn 服务..."
|
||||||
|
cat > /etc/systemd/system/diary-system.service << EOF
|
||||||
|
[Unit]
|
||||||
|
Description=Diary System Gunicorn Service
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
User=root
|
||||||
|
Group=root
|
||||||
|
WorkingDirectory=/root/.openclaw/workspace/diary-system/backend
|
||||||
|
ExecStart=/usr/bin/gunicorn --access-logfile - --workers 3 --bind 127.0.0.1:8002 diary_system.wsgi:application
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable diary-system
|
||||||
|
systemctl start diary-system
|
||||||
|
|
||||||
|
# 5. 配置 Nginx
|
||||||
|
echo "🌐 配置 Nginx..."
|
||||||
|
cat > /etc/nginx/sites-available/diary-system << 'EOF'
|
||||||
|
server {
|
||||||
|
listen 8001;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
location = /favicon.ico { access_log off; log_not_found off; }
|
||||||
|
|
||||||
|
location /static/ {
|
||||||
|
alias /root/.openclaw/workspace/diary-system/backend/static/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://127.0.0.1:8002;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
root /root/.openclaw/workspace/diary-system/frontend;
|
||||||
|
index index.html;
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
ln -sf /etc/nginx/sites-available/diary-system /etc/nginx/sites-enabled/
|
||||||
|
nginx -t
|
||||||
|
systemctl restart nginx
|
||||||
|
|
||||||
|
# 6. 同步日记数据
|
||||||
|
echo "📊 同步日记数据..."
|
||||||
|
python3 /root/.openclaw/workspace/diary-system/sync_diary.py
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✅ 部署完成!"
|
||||||
|
echo ""
|
||||||
|
echo "📍 访问地址:http://cssc.datalibstar.com:8001"
|
||||||
|
echo "📍 API: http://cssc.datalibstar.com:8001/api/"
|
||||||
|
echo "📍 Admin: http://cssc.datalibstar.com:8001/admin"
|
||||||
124
deploy_cloud.sh
Normal file
124
deploy_cloud.sh
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# 日记系统 - 云服务器部署脚本
|
||||||
|
# 在云服务器上执行:bash deploy_cloud.sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
PROJECT_DIR="/home/ubuntu/diary-system"
|
||||||
|
BACKEND_DIR="$PROJECT_DIR/backend"
|
||||||
|
FRONTEND_DIR="$PROJECT_DIR/frontend"
|
||||||
|
|
||||||
|
echo "======================================"
|
||||||
|
echo "⚡ 码神日记系统 - 云服务器部署"
|
||||||
|
echo "======================================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
cd "$PROJECT_DIR"
|
||||||
|
|
||||||
|
# 1. 安装依赖
|
||||||
|
echo "📦 安装 Python 依赖..."
|
||||||
|
cd "$BACKEND_DIR"
|
||||||
|
pip3 install -r requirements.txt -q 2>/dev/null || pip3 install -r requirements.txt --break-system-packages -q
|
||||||
|
|
||||||
|
# 2. 数据库迁移
|
||||||
|
echo "🗄️ 执行数据库迁移..."
|
||||||
|
python3 manage.py makemigrations diary --noinput 2>/dev/null || true
|
||||||
|
python3 manage.py migrate --noinput
|
||||||
|
|
||||||
|
# 3. 收集静态文件
|
||||||
|
echo "📁 收集静态文件..."
|
||||||
|
mkdir -p "$BACKEND_DIR/static"
|
||||||
|
python3 manage.py collectstatic --noinput
|
||||||
|
|
||||||
|
# 4. 创建 Gunicorn systemd 服务
|
||||||
|
echo "⚙️ 创建 Gunicorn 服务..."
|
||||||
|
sudo cat > /etc/systemd/system/diary-system.service << EOF
|
||||||
|
[Unit]
|
||||||
|
Description=Diary System Gunicorn Service
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
User=ubuntu
|
||||||
|
Group=ubuntu
|
||||||
|
WorkingDirectory=$BACKEND_DIR
|
||||||
|
ExecStart=/usr/bin/gunicorn --access-logfile - --workers 3 --bind 127.0.0.1:8002 diary_system.wsgi:application
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable diary-system
|
||||||
|
sudo systemctl restart diary-system
|
||||||
|
|
||||||
|
# 5. 配置 Nginx
|
||||||
|
echo "🌐 配置 Nginx..."
|
||||||
|
sudo cat > /etc/nginx/sites-available/diary-system << EOF
|
||||||
|
server {
|
||||||
|
listen 8001;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
location = /favicon.ico { access_log off; log_not_found off; }
|
||||||
|
|
||||||
|
location /static/ {
|
||||||
|
alias $BACKEND_DIR/static/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://127.0.0.1:8002;
|
||||||
|
proxy_set_header Host \$host;
|
||||||
|
proxy_set_header X-Real-IP \$remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
root $FRONTEND_DIR;
|
||||||
|
index index.html;
|
||||||
|
try_files \$uri \$uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
sudo ln -sf /etc/nginx/sites-available/diary-system /etc/nginx/sites-enabled/
|
||||||
|
sudo nginx -t
|
||||||
|
sudo systemctl restart nginx
|
||||||
|
|
||||||
|
# 6. 同步日记数据
|
||||||
|
echo "📊 同步日记数据..."
|
||||||
|
cd "$PROJECT_DIR"
|
||||||
|
python3 sync_diary.py || echo " (首次部署,跳过日记同步)"
|
||||||
|
|
||||||
|
# 7. 检查服务状态
|
||||||
|
echo ""
|
||||||
|
echo "🔍 检查服务状态..."
|
||||||
|
sudo systemctl is-active diary-system > /dev/null && echo " ✅ Gunicorn 运行中" || echo " ❌ Gunicorn 未运行"
|
||||||
|
sudo systemctl is-active nginx > /dev/null && echo " ✅ Nginx 运行中" || echo " ❌ Nginx 未运行"
|
||||||
|
|
||||||
|
# 8. 测试访问
|
||||||
|
echo ""
|
||||||
|
echo "🧪 测试访问..."
|
||||||
|
sleep 2
|
||||||
|
if curl -s http://127.0.0.1:8001/ | grep -q "码神的日记系统"; then
|
||||||
|
echo " ✅ 前端访问正常"
|
||||||
|
else
|
||||||
|
echo " ⚠️ 前端访问可能有问题"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if curl -s http://127.0.0.1:8001/api/entries/stats/ | grep -q "total_entries"; then
|
||||||
|
echo " ✅ API 访问正常"
|
||||||
|
else
|
||||||
|
echo " ⚠️ API 访问可能有问题"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "======================================"
|
||||||
|
echo "✅ 部署完成!"
|
||||||
|
echo "======================================"
|
||||||
|
echo ""
|
||||||
|
echo "📍 访问地址:http://cssc.datalibstar.com:8001"
|
||||||
|
echo "📍 API: http://cssc.datalibstar.com:8001/api/"
|
||||||
|
echo "📍 Admin: http://cssc.datalibstar.com:8001/admin"
|
||||||
|
echo ""
|
||||||
|
echo "🎉 日记系统已在云服务器上线!"
|
||||||
|
echo ""
|
||||||
143
deploy_to_cloud.sh
Normal file
143
deploy_to_cloud.sh
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# 日记系统 - 云服务器一键部署脚本
|
||||||
|
# 使用方法:在云服务器上执行 bash deploy_to_cloud.sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SERVER_URL="cssc.datalibstar.com"
|
||||||
|
DEPLOY_PORT="8001"
|
||||||
|
|
||||||
|
echo "======================================"
|
||||||
|
echo "⚡ 码神日记系统 - 云服务器部署"
|
||||||
|
echo "======================================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 检查是否在正确的服务器上
|
||||||
|
echo "🔍 检查服务器..."
|
||||||
|
if [ "$(hostname)" != "cssc" ] && [ "$(hostname)" != "cssc.datalibstar.com" ]; then
|
||||||
|
echo "⚠️ 警告:当前主机名不是 cssc,请确认是否在正确的服务器上"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 1. 克隆或更新代码
|
||||||
|
echo "📦 获取代码..."
|
||||||
|
cd /root/.openclaw/workspace || {
|
||||||
|
echo "❌ 错误:/root/.openclaw/workspace 不存在"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -d "diary-system" ]; then
|
||||||
|
echo " 更新现有代码..."
|
||||||
|
cd diary-system
|
||||||
|
git pull 2>/dev/null || echo " (非 git 仓库,跳过)"
|
||||||
|
cd ..
|
||||||
|
else
|
||||||
|
echo " 代码已存在,跳过克隆"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. 安装依赖
|
||||||
|
echo "📦 安装 Python 依赖..."
|
||||||
|
cd /root/.openclaw/workspace/diary-system/backend
|
||||||
|
pip3 install -r requirements.txt --break-system-packages -q
|
||||||
|
|
||||||
|
# 3. 数据库迁移
|
||||||
|
echo "🗄️ 执行数据库迁移..."
|
||||||
|
python3 manage.py makemigrations diary --noinput 2>/dev/null || true
|
||||||
|
python3 manage.py migrate --noinput
|
||||||
|
|
||||||
|
# 4. 收集静态文件
|
||||||
|
echo "📁 收集静态文件..."
|
||||||
|
mkdir -p /root/.openclaw/workspace/diary-system/backend/static
|
||||||
|
python3 manage.py collectstatic --noinput
|
||||||
|
|
||||||
|
# 5. 创建 Gunicorn systemd 服务
|
||||||
|
echo "⚙️ 创建 Gunicorn 服务..."
|
||||||
|
cat > /etc/systemd/system/diary-system.service << EOF
|
||||||
|
[Unit]
|
||||||
|
Description=Diary System Gunicorn Service
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
User=root
|
||||||
|
Group=root
|
||||||
|
WorkingDirectory=/root/.openclaw/workspace/diary-system/backend
|
||||||
|
ExecStart=/usr/bin/gunicorn --access-logfile - --workers 3 --bind 127.0.0.1:8002 diary_system.wsgi:application
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable diary-system
|
||||||
|
systemctl restart diary-system
|
||||||
|
|
||||||
|
# 6. 配置 Nginx
|
||||||
|
echo "🌐 配置 Nginx..."
|
||||||
|
cat > /etc/nginx/sites-available/diary-system << 'EOF'
|
||||||
|
server {
|
||||||
|
listen 8001;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
location = /favicon.ico { access_log off; log_not_found off; }
|
||||||
|
|
||||||
|
location /static/ {
|
||||||
|
alias /root/.openclaw/workspace/diary-system/backend/static/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://127.0.0.1:8002;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
root /root/.openclaw/workspace/diary-system/frontend;
|
||||||
|
index index.html;
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
ln -sf /etc/nginx/sites-available/diary-system /etc/nginx/sites-enabled/
|
||||||
|
nginx -t
|
||||||
|
systemctl restart nginx
|
||||||
|
|
||||||
|
# 7. 同步日记数据
|
||||||
|
echo "📊 同步日记数据..."
|
||||||
|
cd /root/.openclaw/workspace/diary-system
|
||||||
|
python3 sync_diary.py
|
||||||
|
|
||||||
|
# 8. 检查服务状态
|
||||||
|
echo ""
|
||||||
|
echo "🔍 检查服务状态..."
|
||||||
|
systemctl is-active diary-system > /dev/null && echo " ✅ Gunicorn 运行中" || echo " ❌ Gunicorn 未运行"
|
||||||
|
systemctl is-active nginx > /dev/null && echo " ✅ Nginx 运行中" || echo " ❌ Nginx 未运行"
|
||||||
|
|
||||||
|
# 9. 测试访问
|
||||||
|
echo ""
|
||||||
|
echo "🧪 测试访问..."
|
||||||
|
sleep 2
|
||||||
|
if curl -s http://127.0.0.1:8001/ | grep -q "码神的日记系统"; then
|
||||||
|
echo " ✅ 前端访问正常"
|
||||||
|
else
|
||||||
|
echo " ⚠️ 前端访问可能有问题"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if curl -s http://127.0.0.1:8001/api/entries/stats/ | grep -q "total_entries"; then
|
||||||
|
echo " ✅ API 访问正常"
|
||||||
|
else
|
||||||
|
echo " ⚠️ API 访问可能有问题"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "======================================"
|
||||||
|
echo "✅ 部署完成!"
|
||||||
|
echo "======================================"
|
||||||
|
echo ""
|
||||||
|
echo "📍 访问地址:http://${SERVER_URL}:${DEPLOY_PORT}"
|
||||||
|
echo "📍 API: http://${SERVER_URL}:${DEPLOY_PORT}/api/"
|
||||||
|
echo "📍 Admin: http://${SERVER_URL}:${DEPLOY_PORT}/admin"
|
||||||
|
echo ""
|
||||||
|
echo "🎉 日记系统已在云服务器上线!"
|
||||||
|
echo ""
|
||||||
304
frontend/index.html
Executable file
304
frontend/index.html
Executable file
@@ -0,0 +1,304 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>码神的日记系统</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
max-width: 1400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
header {
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
header h1 {
|
||||||
|
font-size: 2.5em;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
header p {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
.stats {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
.stat-card {
|
||||||
|
background: white;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
.stat-card h3 {
|
||||||
|
color: #667eea;
|
||||||
|
font-size: 2em;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
.stat-card p {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.section-box {
|
||||||
|
background: white;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
.section-box h2 {
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
border-bottom: 2px solid #667eea;
|
||||||
|
}
|
||||||
|
.diary-item {
|
||||||
|
padding: 15px;
|
||||||
|
border-left: 4px solid #667eea;
|
||||||
|
background: #f8f9fa;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.diary-item h3 {
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.diary-item .date {
|
||||||
|
color: #667eea;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.diary-item .section {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
.diary-item .section-title {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
.diary-item .section-content {
|
||||||
|
color: #666;
|
||||||
|
margin-left: 20px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
.experience-item {
|
||||||
|
padding: 15px;
|
||||||
|
border-left: 4px solid #f59e0b;
|
||||||
|
background: #fffbeb;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.experience-item .header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.experience-item .title {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
.experience-item .category {
|
||||||
|
background: #f59e0b;
|
||||||
|
color: white;
|
||||||
|
padding: 4px 12px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 0.85em;
|
||||||
|
}
|
||||||
|
.experience-item .problem {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
.experience-item .problem-title {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #dc2626;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
.experience-item .solution {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
.experience-item .solution-title {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #059669;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
.experience-item .lesson {
|
||||||
|
margin: 10px 0;
|
||||||
|
padding: 10px;
|
||||||
|
background: #fef3c7;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.experience-item .lesson-title {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #92400e;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
.loading {
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px;
|
||||||
|
color: white;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
background: #fee;
|
||||||
|
color: #c00;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.emoji {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
.grid-2 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(600px, 1fr));
|
||||||
|
gap: 30px;
|
||||||
|
}
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.grid-2 {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<header>
|
||||||
|
<h1>⚡ 码神的日记系统</h1>
|
||||||
|
<p>记录每天的进步与成长</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div id="app">
|
||||||
|
<div class="loading">加载中...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const API_BASE = '/api';
|
||||||
|
|
||||||
|
async function loadDiary() {
|
||||||
|
try {
|
||||||
|
const [statsRes, entriesRes, expStatsRes, experiencesRes] = await Promise.all([
|
||||||
|
fetch(`${API_BASE}/entries/stats/`),
|
||||||
|
fetch(`${API_BASE}/entries/recent/`),
|
||||||
|
fetch(`${API_BASE}/experiences/stats/`),
|
||||||
|
fetch(`${API_BASE}/experiences/recent/`)
|
||||||
|
]);
|
||||||
|
|
||||||
|
const stats = await statsRes.json();
|
||||||
|
const entries = await entriesRes.json();
|
||||||
|
const expStats = await expStatsRes.json();
|
||||||
|
const experiences = await experiencesRes.json();
|
||||||
|
|
||||||
|
render(stats, entries, expStats, experiences);
|
||||||
|
} catch (error) {
|
||||||
|
document.getElementById('app').innerHTML = `
|
||||||
|
<div class="error">加载失败:${error.message}</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function render(stats, entries, expStats, experiences) {
|
||||||
|
const app = document.getElementById('app');
|
||||||
|
|
||||||
|
app.innerHTML = `
|
||||||
|
<div class="stats">
|
||||||
|
<div class="stat-card">
|
||||||
|
<h3>${stats.total_entries || 0}</h3>
|
||||||
|
<p>总日记数</p>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<h3>${expStats.total_experiences || 0}</h3>
|
||||||
|
<p>经验总结</p>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<h3>${stats.first_entry || '-'}</h3>
|
||||||
|
<p>第一天</p>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<h3>${stats.latest_entry || '-'}</h3>
|
||||||
|
<p>最新日记</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid-2">
|
||||||
|
<div class="section-box">
|
||||||
|
<h2>📝 最近日记</h2>
|
||||||
|
${entries.length === 0 ? '<p>暂无日记</p>' : entries.map(entry => `
|
||||||
|
<div class="diary-item">
|
||||||
|
<div class="date">${entry.date}</div>
|
||||||
|
<h3>${entry.title || '每日日记'}</h3>
|
||||||
|
${entry.completed_tasks ? `
|
||||||
|
<div class="section">
|
||||||
|
<span class="section-title">✅ 完成的任务</span>
|
||||||
|
<div class="section-content">${entry.completed_tasks}</div>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
${entry.learned ? `
|
||||||
|
<div class="section">
|
||||||
|
<span class="section-title">📚 学到的东西</span>
|
||||||
|
<div class="section-content">${entry.learned}</div>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
${entry.reflections ? `
|
||||||
|
<div class="section">
|
||||||
|
<span class="section-title">💡 想法和反思</span>
|
||||||
|
<div class="section-content">${entry.reflections}</div>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
${entry.improvements ? `
|
||||||
|
<div class="section">
|
||||||
|
<span class="section-title">📈 进步点</span>
|
||||||
|
<div class="section-content">${entry.improvements}</div>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
</div>
|
||||||
|
`).join('')}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section-box">
|
||||||
|
<h2>💡 经验总结</h2>
|
||||||
|
${experiences.length === 0 ? '<p>暂无经验总结</p>' : experiences.map(exp => `
|
||||||
|
<div class="experience-item">
|
||||||
|
<div class="header">
|
||||||
|
<span class="title">${exp.title}</span>
|
||||||
|
<span class="category">${exp.category}</span>
|
||||||
|
</div>
|
||||||
|
<div class="problem">
|
||||||
|
<div class="problem-title">🐛 问题</div>
|
||||||
|
<div class="section-content">${exp.problem}</div>
|
||||||
|
</div>
|
||||||
|
<div class="solution">
|
||||||
|
<div class="solution-title">✅ 解决方案</div>
|
||||||
|
<div class="section-content">${exp.solution}</div>
|
||||||
|
</div>
|
||||||
|
${exp.lesson_learned ? `
|
||||||
|
<div class="lesson">
|
||||||
|
<div class="lesson-title">📌 经验教训</div>
|
||||||
|
<div class="section-content">${exp.lesson_learned}</div>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
</div>
|
||||||
|
`).join('')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadDiary();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
7
makemigrations.py
Normal file
7
makemigrations.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os, sys
|
||||||
|
sys.path.insert(0, '/root/.openclaw/workspace/diary-system/backend')
|
||||||
|
os.environ['DJANGO_SETTINGS_MODULE'] = 'diary_system.settings'
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
sys.argv = ['manage.py', 'makemigrations', 'diary']
|
||||||
|
execute_from_command_line(sys.argv)
|
||||||
7
migrate.py
Normal file
7
migrate.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os, sys
|
||||||
|
sys.path.insert(0, '/root/.openclaw/workspace/diary-system/backend')
|
||||||
|
os.environ['DJANGO_SETTINGS_MODULE'] = 'diary_system.settings'
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
sys.argv = ['manage.py', 'migrate']
|
||||||
|
execute_from_command_line(sys.argv)
|
||||||
21
nginx.conf
Executable file
21
nginx.conf
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
server {
|
||||||
|
listen 8001;
|
||||||
|
server_name localhost;
|
||||||
|
|
||||||
|
location /static/ {
|
||||||
|
alias /root/.openclaw/workspace/diary-system/backend/static/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://127.0.0.1:8002;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
root /root/.openclaw/workspace/diary-system/frontend;
|
||||||
|
index index.html;
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
23
nginx_cloud.conf
Normal file
23
nginx_cloud.conf
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
server {
|
||||||
|
listen 8001;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
location = /favicon.ico { access_log off; log_not_found off; }
|
||||||
|
|
||||||
|
location /static/ {
|
||||||
|
alias /home/ubuntu/diary-system/backend/static/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://127.0.0.1:8002;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
root /home/ubuntu/diary-system/frontend;
|
||||||
|
index index.html;
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
25
start.sh
Executable file
25
start.sh
Executable file
@@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# 日记系统启动脚本
|
||||||
|
|
||||||
|
cd /root/.openclaw/workspace/diary-system/backend
|
||||||
|
|
||||||
|
# 安装依赖
|
||||||
|
pip3 install -r requirements.txt -q
|
||||||
|
|
||||||
|
# 数据库迁移
|
||||||
|
python3 manage.py makemigrations diary
|
||||||
|
python3 manage.py migrate
|
||||||
|
|
||||||
|
# 收集静态文件
|
||||||
|
python3 manage.py collectstatic --noinput
|
||||||
|
|
||||||
|
# 启动 Gunicorn
|
||||||
|
echo "🚀 启动日记系统..."
|
||||||
|
gunicorn diary_system.wsgi:application \
|
||||||
|
--bind 127.0.0.1:8002 \
|
||||||
|
--workers 3 \
|
||||||
|
--daemon
|
||||||
|
|
||||||
|
echo "✅ 日记系统已启动"
|
||||||
|
echo "📝 访问地址:http://127.0.0.1:8001/"
|
||||||
|
echo "🔧 Admin: http://127.0.0.1:8001/admin/"
|
||||||
53
sync_diary.py
Normal file
53
sync_diary.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""同步今天的日记到数据库"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'diary_system.settings')
|
||||||
|
sys.path.insert(0, '/root/.openclaw/workspace/diary-system/backend')
|
||||||
|
|
||||||
|
import django
|
||||||
|
django.setup()
|
||||||
|
|
||||||
|
from datetime import date
|
||||||
|
from diary.models import DiaryEntry
|
||||||
|
|
||||||
|
# 读取今天的日记文件
|
||||||
|
diary_path = '/root/.openclaw/workspace/memory/2026-04-14.md'
|
||||||
|
with open(diary_path, 'r') as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
# 解析日记内容(简单解析)
|
||||||
|
def extract_section(content, marker):
|
||||||
|
lines = content.split('\n')
|
||||||
|
result = []
|
||||||
|
in_section = False
|
||||||
|
for line in lines:
|
||||||
|
if marker in line:
|
||||||
|
in_section = True
|
||||||
|
continue
|
||||||
|
if in_section:
|
||||||
|
if line.startswith('## ') or line.startswith('---'):
|
||||||
|
break
|
||||||
|
result.append(line)
|
||||||
|
return '\n'.join(result).strip()
|
||||||
|
|
||||||
|
# 创建或更新日记
|
||||||
|
today = date.today()
|
||||||
|
entry, created = DiaryEntry.objects.get_or_create(date=today)
|
||||||
|
|
||||||
|
entry.title = "第一天 - 日记系统上线"
|
||||||
|
entry.completed_tasks = extract_section(content, '✅ 完成的任务')
|
||||||
|
entry.learned = extract_section(content, '📚 学到的东西')
|
||||||
|
entry.problems = extract_section(content, '🐛 遇到的问题')
|
||||||
|
entry.reflections = extract_section(content, '💡 想法和反思')
|
||||||
|
entry.improvements = extract_section(content, '📈 进步点')
|
||||||
|
entry.plans = extract_section(content, '🎯 明日计划')
|
||||||
|
entry.save()
|
||||||
|
|
||||||
|
print(f"✅ 日记已同步:{entry.date}")
|
||||||
|
print(f" 标题:{entry.title}")
|
||||||
|
print(f" 完成的任务:{len(entry.completed_tasks)} 字")
|
||||||
|
print(f" 学到的东西:{len(entry.learned)} 字")
|
||||||
|
print(f" 进步点:{len(entry.improvements)} 字")
|
||||||
Reference in New Issue
Block a user