diff --git a/README.md b/README.md index 2399d16..65d1a2e 100644 --- a/README.md +++ b/README.md @@ -164,8 +164,8 @@ piano-plan/ --- -> **版本**:v1.5.0 +> **版本**:v1.5.1 > **创建时间**:2026-04-17 -> **最后更新**:2026-04-27 +> **最后更新**:2026-04-28 > -> **重要更新**:v1.5.0 - 数据统计页面(问题/级别分布可视化);PDF水印配置;编辑页按钮吸底;侧边栏调整 +> **重要更新**:v1.5.1 - PDF水印配置保存修复;{student_goals}占位符修复 diff --git a/compare_templates.py b/compare_templates.py new file mode 100644 index 0000000..48d1922 --- /dev/null +++ b/compare_templates.py @@ -0,0 +1,56 @@ +import sqlite3 + +def get_templates(db_path): + conn = sqlite3.connect(db_path) + c = conn.cursor() + c.execute("SELECT id, name, type, sort_order FROM templates ORDER BY type, sort_order") + rows = c.fetchall() + conn.close() + return rows + +dev_path = r"D:\F\NewI\opencode\daily-workspace\projects\青年钢琴集体课\练习方案系统\data\piano_plans.db" +prod_path = r"D:\F\NewI\opencode\daily-workspace\projects\青年钢琴集体课\练习方案系统\temp_prod.db" + +dev_rows = get_templates(dev_path) +prod_rows = get_templates(prod_path) + +with open("temp_templates_compare.txt", "w", encoding="utf-8") as f: + f.write("=== DEV TEMPLATES ===\n") + for r in dev_rows: + f.write(f"ID={r[0]} | {r[1]} | type={r[2]} | sort={r[3]}\n") + + f.write("\n=== PROD TEMPLATES ===\n") + for r in prod_rows: + f.write(f"ID={r[0]} | {r[1]} | type={r[2]} | sort={r[3]}\n") + + f.write("\n=== COMPARISON ===\n") + dev_ids = {r[0] for r in dev_rows} + prod_ids = {r[0] for r in prod_rows} + only_dev = dev_ids - prod_ids + only_prod = prod_ids - dev_ids + same_ids = dev_ids & prod_ids + + if only_dev: + f.write(f"ONLY IN DEV: IDs {only_dev}\n") + for r in dev_rows: + if r[0] in only_dev: + f.write(f" ID={r[0]} | {r[1]} | type={r[2]} | sort={r[3]}\n") + if only_prod: + f.write(f"ONLY IN PROD: IDs {only_prod}\n") + for r in prod_rows: + if r[0] in only_prod: + f.write(f" ID={r[0]} | {r[1]} | type={r[2]} | sort={r[3]}\n") + if same_ids: + dev_map = {r[0]: r for r in dev_rows} + prod_map = {r[0]: r for r in prod_rows} + for i in sorted(same_ids): + d = dev_map[i] + p = prod_map[i] + if d != p: + f.write(f"DIFF ID={i}:\n") + f.write(f" DEV: name={d[1]} type={d[2]} sort={d[3]}\n") + f.write(f" PROD: name={p[1]} type={p[2]} sort={p[3]}\n") + else: + f.write(f"ID={i}: IDENTICAL\n") + +print("done") diff --git a/docs/DEPLOYMENT_SOP.md b/docs/DEPLOYMENT_SOP.md index 84683d6..cc9fd28 100644 --- a/docs/DEPLOYMENT_SOP.md +++ b/docs/DEPLOYMENT_SOP.md @@ -1,12 +1,12 @@ # 钢琴练习方案系统 - 部署 SOP -> 版本:v1.5.0 -> 日期:2026-04-27 +> 版本:v1.5.1 +> 日期:2026-04-28 > 核心原则:**不删除,只备份后新增/替换** --- -## 重要更新(v1.5.0) +## 重要更新(v1.5.1) ### ⚠️ 问题文件已迁移到数据库 @@ -408,17 +408,7 @@ A: 检查是否执行了 migrate_goals_v3.py 迁移脚本,该脚本创建 stud [ ] 学员列表"暂无方案/问题"样式正常 [ ] PDF 水印功能正常(配置后导出可见) [ ] 数据统计页面正常显示 -``` -[ ] 容器状态:running -[ ] 服务响应:HTTP 200/302 -[ ] 数据库表完整:users, students, classes, student_problems, practice_plans, templates, problems, goals, goal_relations, student_goals, student_goal_evaluations -[ ] practice_plans 表有新字段:created_by, updated_by, updated_at, template_id, is_typical -[ ] 目标管理功能正常:创建目标、分配目标、评估目标 -[ ] 时间线正常显示阶段评估和最终评估 -[ ] API 配置正确 -[ ] 功能验证:能生成练习方案 -[ ] 方案列表支持删除 -[ ] 学员列表"暂无方案/问题"样式正常 +[ ] 导出PDF时 {student_goals} 正常显示学员目标 ``` --- @@ -427,7 +417,8 @@ A: 检查是否执行了 migrate_goals_v3.py 迁移脚本,该脚本创建 stud | 版本 | 日期 | 变更 | |------|------|------| -| v1.5.0 | 2026-04-27 | 数据统计页面(问题/级别分布可视化);PDF水印配置(可自定义文本);编辑页按钮吸底;侧边栏顺序调整;MySQL字体问题修复(Linux) | +| v1.5.1 | 2026-04-28 | PDF水印配置保存修复(3处漏改);{student_goals}占位符修复;移除目标导出时的"内容:"标签 | +| v1.5.0 | 2026-04-27 | 数据统计页面(问题/级别分布可视化);PDF水印配置(可自定义文本);编辑页按钮吸底;侧边栏顺序调整;Linux中文字体路径修复 | | v1.4.0 | 2026-04-27 | 典型方案采纳;推荐方案列表;方案编辑/详情页导航优化(bfcache处理);审计字段完善(created_by/updated_by/updated_at);方案列表支持删除;学员列表"暂无方案/问题"样式统一 | | v1.3.6 | 2026-04-24 | 方案详情导航优化;典型方案开关移至方案详情;方案列表显示问题级别+严重程度 | | v1.3.5 | 2026-04-24 | 班级班主任字段;用户姓名name字段;班级/学员/方案增加"我的"筛选 | @@ -442,5 +433,5 @@ A: 检查是否执行了 migrate_goals_v3.py 迁移脚本,该脚本创建 stud --- -> **最后更新**:2026-04-27 -> **更新原因**:v1.5.0 发布;数据统计页面;PDF水印;编辑页吸底按钮;Linux中文字体路径 +> **最后更新**:2026-04-28 +> **更新原因**:v1.5.1 补丁;PDF水印保存漏改;{student_goals}占位符修复;清理重复检查清单 diff --git a/temp_compare.py b/temp_compare.py new file mode 100644 index 0000000..1c6e4b6 --- /dev/null +++ b/temp_compare.py @@ -0,0 +1,58 @@ +import sqlite3, sys + +def get_templates(db_path): + conn = sqlite3.connect(db_path) + c = conn.cursor() + c.execute("SELECT id, name, type, sort_order FROM templates ORDER BY type, sort_order") + rows = c.fetchall() + conn.close() + return rows + +dev_path = "D:/F/NewI/opencode/daily-workspace/projects/青年钢琴集体课/练习方案系统/data/piano_plans.db" +prod_path = "D:/F/NewI/opencode/daily-workspace/projects/青年钢琴集体课/练习方案系统/temp_prod_piano.db" + +import shutil +shutil.copy("\\\\?\\UNC\\47.115.32.206\\ipc$\\tmp\\prod_piano.db", prod_path) + +dev_rows = get_templates(dev_path) +prod_rows = get_templates(prod_path) + +print("=== DEV TEMPLATES ===") +for r in dev_rows: + print(f"ID={r[0]} | {r[1]} | type={r[2]} | sort={r[3]}") + +print() +print("=== PROD TEMPLATES ===") +for r in prod_rows: + print(f"ID={r[0]} | {r[1]} | type={r[2]} | sort={r[3]}") + +print() +dev_ids = {r[0] for r in dev_rows} +prod_ids = {r[0] for r in prod_rows} +only_dev = dev_ids - prod_ids +only_prod = prod_ids - dev_ids +same_ids = dev_ids & prod_ids + +if only_dev: + print(f"=== ONLY IN DEV: IDs {only_dev} ===") + for r in dev_rows: + if r[0] in only_dev: + print(f" ID={r[0]} | {r[1]} | type={r[2]} | sort={r[3]}") +if only_prod: + print(f"=== ONLY IN PROD: IDs {only_prod} ===") + for r in prod_rows: + if r[0] in only_prod: + print(f" ID={r[0]} | {r[1]} | type={r[2]} | sort={r[3]}") +if same_ids: + print(f"=== IN BOTH (comparing) ===") + dev_map = {r[0]: r for r in dev_rows} + prod_map = {r[0]: r for r in prod_rows} + for i in sorted(same_ids): + d = dev_map[i] + p = prod_map[i] + if d != p: + print(f" ID={i}:") + print(f" DEV: name={d[1]} type={d[2]} sort={d[3]}") + print(f" PROD: name={p[1]} type={p[2]} sort={p[3]}") + else: + print(f" ID={i}: IDENTICAL") diff --git a/temp_compare_templates.py b/temp_compare_templates.py new file mode 100644 index 0000000..2db6397 --- /dev/null +++ b/temp_compare_templates.py @@ -0,0 +1,55 @@ +import sqlite3 + +def get_templates(db_path): + conn = sqlite3.connect(db_path) + c = conn.cursor() + c.execute("SELECT id, name, type, sort_order FROM templates ORDER BY type, sort_order") + rows = c.fetchall() + conn.close() + return rows + +dev_path = r"D:\F\NewI\opencode\daily-workspace\projects\青年钢琴集体课\练习方案系统\data\piano_plans.db" +prod_path = r"D:\F\NewI\opencode\daily-workspace\projects\青年钢琴集体课\练习方案系统\temp_prod.db" + +dev_rows = get_templates(dev_path) +prod_rows = get_templates(prod_path) + +print("=== DEV TEMPLATES ===") +for r in dev_rows: + print(f"ID={r[0]} | {r[1]} | type={r[2]} | sort={r[3]}") + +print() +print("=== PROD TEMPLATES ===") +for r in prod_rows: + print(f"ID={r[0]} | {r[1]} | type={r[2]} | sort={r[3]}") + +print() +dev_ids = {r[0] for r in dev_rows} +prod_ids = {r[0] for r in prod_rows} +only_dev = dev_ids - prod_ids +only_prod = prod_ids - dev_ids +same_ids = dev_ids & prod_ids + +if only_dev: + print(f"=== ONLY IN DEV: IDs {only_dev} ===") + for r in dev_rows: + if r[0] in only_dev: + print(f" ID={r[0]} | {r[1]} | type={r[2]} | sort={r[3]}") +if only_prod: + print(f"=== ONLY IN PROD: IDs {only_prod} ===") + for r in prod_rows: + if r[0] in only_prod: + print(f" ID={r[0]} | {r[1]} | type={r[2]} | sort={r[3]}") +if same_ids: + print(f"=== IN BOTH (comparing) ===") + dev_map = {r[0]: r for r in dev_rows} + prod_map = {r[0]: r for r in prod_rows} + for i in sorted(same_ids): + d = dev_map[i] + p = prod_map[i] + if d != p: + print(f" ID={i}:") + print(f" DEV: name={d[1]} type={d[2]} sort={d[3]}") + print(f" PROD: name={p[1]} type={p[2]} sort={p[3]}") + else: + print(f" ID={i}: IDENTICAL") diff --git a/temp_prod.db b/temp_prod.db new file mode 100644 index 0000000..a4dbfcb Binary files /dev/null and b/temp_prod.db differ diff --git a/temp_templates_compare.txt b/temp_templates_compare.txt new file mode 100644 index 0000000..be7da49 --- /dev/null +++ b/temp_templates_compare.txt @@ -0,0 +1,23 @@ +=== DEV TEMPLATES === +ID=3 | 简单文字版 | type=ai_prompt | sort=0 +ID=1 | 正式报告版 | type=ai_prompt | sort=1 +ID=4 | 报告导出模板(简洁版) | type=report | sort=0 +ID=2 | 报告导出模板 | type=report | sort=1 + +=== PROD TEMPLATES === +ID=1 | 正式报告版 | type=ai_prompt | sort=0 +ID=3 | 简单文字版 | type=ai_prompt | sort=1 +ID=4 | 报告导出模板(简洁版) | type=report | sort=0 +ID=2 | 报告导出模板(备份,勿用) | type=report | sort=1 + +=== COMPARISON === +DIFF ID=1: + DEV: name=正式报告版 type=ai_prompt sort=1 + PROD: name=正式报告版 type=ai_prompt sort=0 +DIFF ID=2: + DEV: name=报告导出模板 type=report sort=1 + PROD: name=报告导出模板(备份,勿用) type=report sort=1 +DIFF ID=3: + DEV: name=简单文字版 type=ai_prompt sort=0 + PROD: name=简单文字版 type=ai_prompt sort=1 +ID=4: IDENTICAL