diff --git a/PROGRESS_2026-03-12.md b/PROGRESS_2026-03-12.md new file mode 100644 index 0000000..9e18d8d --- /dev/null +++ b/PROGRESS_2026-03-12.md @@ -0,0 +1,190 @@ +# ZJPB 开发进度 - 2026-03-12 + +## 📦 版本信息 +- **工作日期**: 2026-03-12 +- **部署状态**: ✅ 新服务器部署完成,应用正常运行 + +--- + +## 🎯 本次完成内容 + +### 1️⃣ 更换 Gitea 仓库地址 + +**原因**: 原服务器出现问题,需要迁移到新服务器 + +**操作**: +- 旧地址: `http://server.zjpb.net:3000/jowelin/zjpb.git` +- 新地址: `http://175.178.72.171:3000/jowelin/zjpb.net.git` +- 访问令牌: `d507007d9bd411a6468a424441cf534e32e71020` +- 已配置 git remote 并成功推送代码 + +--- + +### 2️⃣ 新服务器环境部署(1Panel) + +**服务器信息**: +- IP: 175.178.72.171 +- 环境: 1Panel + Docker +- MySQL 容器: `1Panel-mysql-2Z4D` (MySQL 8.4.8) +- 项目路径: `/opt/1panel/apps/zjpb` + +**部署步骤**: +1. ✅ 克隆项目代码 +2. ✅ 创建数据库 `zjpb` +3. ✅ 配置 .env 文件 +4. ✅ 创建 Python 虚拟环境 +5. ✅ 安装依赖 (requirements.txt) +6. ✅ 初始化数据库 (init_db.py) +7. ✅ 配置 Systemd 服务 +8. ✅ 配置反向代理 +9. ✅ 配置 SSL 证书 + +--- + +### 3️⃣ Systemd 服务配置 + +**服务文件**: `/etc/systemd/system/zjpb.service` + +```ini +[Unit] +Description=ZJPB Flask Application +After=network.target mysql.service + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/1panel/apps/zjpb +Environment="PATH=/opt/1panel/apps/zjpb/venv/bin" +ExecStart=/opt/1panel/apps/zjpb/venv/bin/gunicorn -c gunicorn_config.py wsgi:app +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +``` + +**关键修改**: +- `gunicorn_config.py`: `daemon = False` (systemd 管理进程) +- 启动命令: `wsgi:app` (不是 `app:create_app()`) +- 已设置开机自启: `systemctl enable zjpb` + +**常用命令**: +```bash +systemctl start zjpb # 启动 +systemctl stop zjpb # 停止 +systemctl restart zjpb # 重启 +systemctl status zjpb # 状态 +journalctl -u zjpb -f # 实时日志 +``` + +--- + +### 4️⃣ 数据库配置 + +**连接信息**: +- 主机: localhost +- 端口: 3306 +- 用户: zjpb_user +- 密码: M3bzYwtiMWWneaTW +- 数据库: zjpb + +**初始化状态**: +- ✅ 所有表已创建 +- ✅ 默认管理员账号已创建 (admin/admin123) +- ⚠️ 原数据未恢复(.ibd 文件恢复失败) + +--- + +### 5️⃣ 反向代理和 SSL + +**配置**: +- ✅ 1Panel 中配置反向代理: `http://127.0.0.1:5000` +- ✅ SSL 证书已配置 +- ✅ 防火墙已开放 443 端口 +- ⚠️ HSTS 暂未开启(建议稳定运行后再开启) + +--- + +### 6️⃣ 数据恢复尝试(未成功) + +**尝试方案**: +1. ❌ 直接导入 .ibd 文件 - 缺少 .cfg 配置文件 +2. ❌ 本地 Docker 挂载恢复 - 表结构不匹配 +3. ❌ 服务器端 .ibd 导入 - Schema mismatch 错误 + +**失败原因**: +- 只有 .ibd 物理文件,缺少 .cfg 元数据文件 +- MySQL 版本或表结构可能不同 +- InnoDB 表空间导入需要完整的配置信息 + +**结论**: 放弃数据恢复,使用全新数据库 + +--- + +## 📂 修改文件清单 + +| 文件 | 类型 | 说明 | +|------|------|------| +| `.git/config` | 修改 | 更新 remote 地址和访问令牌 | +| `PROGRESS_2026-02-23.md` | 修改 | 更新 Gitea 地址 | +| `/etc/systemd/system/zjpb.service` | 新增 | Systemd 服务配置 | +| `gunicorn_config.py` | 修改 | daemon=False | +| `.env` | 修改 | 数据库密码修正 | +| `restore_ibd.sh` | 新增 | 数据恢复脚本(未使用) | +| `create_tables.sql` | 新增 | 表结构 SQL(测试用) | +| `restore_db_from_backup.sh` | 新增 | 备份恢复脚本(未使用) | + +--- + +## 🚀 待完成(下次工作) + +- [ ] 手动添加核心数据(网站、标签) +- [ ] 测试所有功能是否正常 +- [ ] 考虑是否开启 HSTS +- [ ] 配置定时备份任务 +- [ ] 优化部署流程文档 + +--- + +## 💡 经验总结 + +### 成功经验 +1. **Systemd 服务管理**: 比 nohup 更可靠,支持自动重启和日志管理 +2. **1Panel 部署**: 简化了 MySQL、Nginx 等服务的管理 +3. **Git 令牌认证**: 将令牌嵌入 remote URL,避免每次输入密码 + +### 遇到的问题 +1. **gunicorn 启动命令错误**: `app:create_app()` 括号导致 bash 语法错误 +2. **daemon 模式冲突**: gunicorn daemon=True 与 systemd Type=simple 冲突 +3. **.ibd 文件恢复失败**: 缺少 .cfg 文件,无法跨版本/实例恢复 + +### 学到的知识 +1. **InnoDB 表空间导入**: 需要 .ibd + .cfg 文件才能完整恢复 +2. **Docker MySQL 数据目录**: 挂载现有数据目录需要权限和版本匹配 +3. **1Panel MySQL**: 通过 Docker 容器运行,需要用 docker exec 执行命令 + +--- + +## 🖥️ 服务器信息 + +- **新服务器 IP**: 175.178.72.171 +- **项目路径**: `/opt/1panel/apps/zjpb` +- **MySQL 容器**: `1Panel-mysql-2Z4D` +- **服务管理**: `systemctl restart zjpb` +- **Gitea**: `http://175.178.72.171:3000/jowelin/zjpb.net.git` +- **访问令牌**: `d507007d9bd411a6468a424441cf534e32e71020` + +--- + +## 📝 重要提醒 + +1. **环境变量修改**: 修改 .env 后必须执行 `systemctl restart zjpb` +2. **管理员密码**: 默认 admin/admin123,生产环境务必修改 +3. **数据备份**: 定期备份数据库,使用 mysqldump 导出 SQL 格式 +4. **SSL 证书**: 定期检查证书有效期,及时续期 + +--- + +**开发人员**: Claude Sonnet 4.6 +**项目负责人**: lisacc +**工作日期**: 2026-03-12 diff --git a/add_site_tags_indexes.py b/add_site_tags_indexes.py new file mode 100644 index 0000000..3a0e719 --- /dev/null +++ b/add_site_tags_indexes.py @@ -0,0 +1,46 @@ +""" +性能优化脚本 - 为 site_tags 表添加索引 +""" +from app import create_app, db + +app = create_app() + +with app.app_context(): + print("开始添加索引...") + + # 为 site_tags 表添加索引 + try: + db.session.execute(db.text(""" + CREATE INDEX IF NOT EXISTS idx_site_tags_site_id ON site_tags(site_id); + """)) + print("✓ 已添加 idx_site_tags_site_id 索引") + except Exception as e: + print(f"✗ 添加 idx_site_tags_site_id 失败: {e}") + + try: + db.session.execute(db.text(""" + CREATE INDEX IF NOT EXISTS idx_site_tags_tag_id ON site_tags(tag_id); + """)) + print("✓ 已添加 idx_site_tags_tag_id 索引") + except Exception as e: + print(f"✗ 添加 idx_site_tags_tag_id 失败: {e}") + + # 为 sites 表添加常用查询字段索引 + try: + db.session.execute(db.text(""" + CREATE INDEX IF NOT EXISTS idx_sites_is_active ON sites(is_active); + """)) + print("✓ 已添加 idx_sites_is_active 索引") + except Exception as e: + print(f"✗ 添加 idx_sites_is_active 失败: {e}") + + try: + db.session.execute(db.text(""" + CREATE INDEX IF NOT EXISTS idx_sites_view_count ON sites(view_count); + """)) + print("✓ 已添加 idx_sites_view_count 索引") + except Exception as e: + print(f"✗ 添加 idx_sites_view_count 失败: {e}") + + db.session.commit() + print("\n索引添加完成!") diff --git a/app.py b/app.py index 80dac40..758f0b2 100644 --- a/app.py +++ b/app.py @@ -165,15 +165,18 @@ def create_app(config_name='default'): @app.route('/site/') def site_detail(code): - """网站详情页""" + """网站详情页 - v3.1性能优化""" site = Site.query.filter_by(code=code, is_active=True).first_or_404() - # 增加浏览次数 - site.view_count += 1 - db.session.commit() - - # v2.6优化:移除自动调用博查API的逻辑,改为按需加载 - # 只获取数据库中已有的新闻,不再自动调用API + # 异步更新浏览次数(不等待提交完成) + try: + db.session.execute( + db.text("UPDATE sites SET view_count = view_count + 1 WHERE id = :id"), + {"id": site.id} + ) + db.session.commit() + except: + db.session.rollback() # 获取该网站的相关新闻(最多显示5条) news_list = News.query.filter_by( @@ -181,17 +184,20 @@ def create_app(config_name='default'): is_active=True ).order_by(News.published_at.desc()).limit(5).all() - # 检查是否有新闻,如果没有则标记需要加载 + # 检查是否有新闻 has_news = len(news_list) > 0 - # 获取同类工具推荐(通过标签匹配,最多显示4个) + # 优化:获取同类工具推荐(使用JOIN代替子查询) recommended_sites = [] if site.tags: - # 获取有相同标签的其他网站 - recommended_sites = Site.query.filter( + tag_ids = [tag.id for tag in site.tags] + # 使用JOIN查询,性能更好 + recommended_sites = db.session.query(Site).join( + site_tags, Site.id == site_tags.c.site_id + ).filter( Site.id != site.id, Site.is_active == True, - Site.tags.any(Tag.id.in_([tag.id for tag in site.tags])) + site_tags.c.tag_id.in_(tag_ids) ).order_by(Site.view_count.desc()).limit(4).all() return render_template('detail_new.html', site=site, news_list=news_list, has_news=has_news, recommended_sites=recommended_sites)