lesson edit (add 阶段/步骤)

This commit is contained in:
CakeCN
2025-08-31 17:16:28 +08:00
parent b18c981367
commit fd6e12528c
8 changed files with 704 additions and 95 deletions

View File

@@ -1,9 +1,10 @@
# views/lesson.py
from flask import Blueprint, jsonify, request, render_template, session, current_app
from werkzeug.exceptions import BadRequest
from bson import ObjectId
from datetime import datetime
from ..auth.decorators import require_role
from ..services.course_service import load_material
from ..services.course_service import load_material,save_materials_markdown_cos
bp = Blueprint('lesson', __name__)
@@ -18,7 +19,7 @@ def edit_lesson_detail(material_id, chapter_name, lesson_name):
if chapter.chapter_name == chapter_name:
for lesson in chapter.lessons:
if lesson.lesson_name == lesson_name:
return render_template('lesson.html', lesson=lesson.model_dump(), role="teacher", material=material.model_dump())
return render_template('lesson.html', lesson=lesson.model_dump(), role="teacher", material={**material.model_dump(), "material_id": material_id}, chapter=chapter.model_dump())
return jsonify({"message": "课时不存在"}), 404
# 返回某份 markdown 文件内容(你也可以直接返回 S3/OSS 的直链)
@@ -32,10 +33,64 @@ def get_markdown_file(material_id, chapter_name, lesson_name, filename):
}
return demo_map.get(filename, ""), 200
# 保存(更新)某一份 markdown前端会把编辑后的文本整体传回
@bp.route('/api/lesson_file/<material_id>/<chapter_name>/<lesson_name>/<filename>', methods=['PUT'])
def save_markdown_file(material_id, chapter_name, lesson_name, filename):
text = request.get_data(as_text=True)
# TODO: 写入存储,并做版本/审计
# 返回更新时间等
return jsonify({"saved": True, "updated_at": datetime.utcnow().isoformat()}), 200
@bp.route("/api/materials/save/<material_id>/<chapter_name>/<lesson_name>", methods=['POST'])
@require_role(roles='teacher')
def save_materials_markdown(material_id, chapter_name, lesson_name):
payload = request.get_json(silent=True, force=True)
if not payload:
raise BadRequest("缺少 JSON payload")
# 基础校验
required_keys = ("lesson", "prompt", "score")
for k in required_keys:
if k not in payload or not isinstance(payload[k], dict):
raise BadRequest(f"缺少或非法字段: {k}")
if "cdn_link" not in payload[k] or "content" not in payload[k]:
raise BadRequest(f"{k} 需要包含 cdn_link 与 content 字段")
if not isinstance(payload[k]["cdn_link"], str) or not isinstance(payload[k]["content"], str):
raise BadRequest(f"{k}.cdn_link 与 {k}.content 必须为字符串")
# 可选:版本/并发控制(若你前端传了)
version = payload.get("version")
docs = {
"lesson": {
"cdn_link": payload["lesson"]["cdn_link"].strip(),
"content" : payload["lesson"]["content"]
},
"prompt": {
"cdn_link": payload["prompt"]["cdn_link"].strip(),
"content" : payload["prompt"]["content"]
},
"score": {
"cdn_link": payload["score"]["cdn_link"].strip(),
"content" : payload["score"]["content"]
}
}
# 轻量防御:限制单次大小(自行按需调整)
max_size = 4 * 1024 * 1024 # 2MB/份
for name, doc in docs.items():
if len(doc["content"].encode("utf-8")) > max_size:
raise BadRequest(f"{name} 文档过大,超过 {max_size} 字节限制")
# 调用服务层保存:写源站、刷新/回写 CDN、更新索引等
# 建议服务层返回新版本/新链接等元信息
result = save_materials_markdown_cos(
material_id=material_id,
chapter_name=chapter_name,
lesson_name=lesson_name,
docs=docs,
version=version, # 可选,用于并发检测
actor=getattr(request, "user", None) # 若你的鉴权中注入了当前教师
)
# 统一响应
return jsonify({
"ok": True,
"material_id": material_id,
"chapter_name": chapter_name,
"lesson_name": lesson_name,
"result": result # e.g. {"lesson":{"cdn_link": "...", "version":"..."}, ...}
}), 200