lesson edit (add 阶段/步骤)
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
import re
|
||||
from typing import Dict, List
|
||||
from flask import current_app
|
||||
from urllib.parse import urlparse
|
||||
from datetime import datetime
|
||||
from bson import ObjectId
|
||||
|
||||
@@ -111,7 +112,93 @@ def delete_material_chapter_lesson(material_id, chapter_name, lesson_name, teach
|
||||
# 如果删除操作成功,res.modified_count 应为 1
|
||||
return res.modified_count == 1
|
||||
|
||||
def save_materials_markdown_cos(
|
||||
material_id: str,
|
||||
chapter_name: str,
|
||||
lesson_name: str,
|
||||
docs: Dict[str, Dict[str, str]],
|
||||
version: str | None = None, # 目前不做并发控制,保留参数
|
||||
actor=None # 可记录操作者到审计日志
|
||||
) -> Dict[str, str]:
|
||||
"""
|
||||
docs 结构:
|
||||
{
|
||||
"lesson": {"cdn_link": "...lesson.md", "content": "..."},
|
||||
"prompt": {"cdn_link": "...prompt.md", "content": "..."},
|
||||
"score": {"cdn_link": "...score_prompt.md", "content": "..."}
|
||||
}
|
||||
约定:cdn_link 与对象存储的 key 一一映射(同路径/同文件名),
|
||||
我们通过解析 URL 的 path 作为 key 进行覆盖写入(不改文件名)。
|
||||
"""
|
||||
|
||||
def _cdn_to_key(cdn_link: str) -> str:
|
||||
"""
|
||||
把 CDN URL 映射成对象存储的 key:
|
||||
- 若 CDN 与存储是“同 key 直出”,则直接取 path(去掉前导 /)
|
||||
- 若你有自定义映射规则,在此处改写
|
||||
"""
|
||||
p = urlparse(cdn_link)
|
||||
key = p.path.lstrip("/")
|
||||
if not key:
|
||||
raise ValueError(f"非法 cdn_link:{cdn_link}")
|
||||
return key
|
||||
|
||||
# 1) 覆盖写入三份文件
|
||||
results: Dict[str, Dict[str, str]] = {}
|
||||
for kind in ("lesson", "prompt", "score"):
|
||||
item = docs.get(kind)
|
||||
if not item or "cdn_link" not in item or "content" not in item:
|
||||
raise ValueError(f"缺少 {kind} 的 cdn_link 或 content")
|
||||
|
||||
key = _cdn_to_key(item["cdn_link"])
|
||||
content = item["content"]
|
||||
# 覆盖写入(注意:upload_file 内部需实现覆盖同 key)
|
||||
try:
|
||||
# 约定:upload_file 返回可访问的 URL(覆盖后可与原相同)
|
||||
url = upload_file(content.encode("utf-8"), key)
|
||||
except Exception as e:
|
||||
current_app.logger.exception("覆盖写入失败 kind=%s key=%s", kind, key)
|
||||
raise
|
||||
|
||||
# 简单返回:保留原 cdn_link(一般与 url 相同),以及 md5 作为伪版本
|
||||
import hashlib
|
||||
etag = hashlib.md5(content.encode("utf-8")).hexdigest()
|
||||
results[kind] = {"cdn_link": url or item["cdn_link"], "version": etag}
|
||||
|
||||
# 2) 写库:更新该 lesson 的更新时间(不改文件名/链接)
|
||||
_touch_material_lesson_updated_at(material_id, chapter_name, lesson_name, actor=actor)
|
||||
|
||||
results["updated_at"] = datetime.now().isoformat() + "Z"
|
||||
return results
|
||||
|
||||
|
||||
def _touch_material_lesson_updated_at(material_id: str, chapter_name: str, lesson_name: str, actor=None) -> None:
|
||||
"""仅刷新 materials 文档中该课时的更新时间戳(以及顶层 updated_at)"""
|
||||
mongo = current_app.extensions["mongo"]
|
||||
now = datetime.now()
|
||||
|
||||
# 更新顶层 updated_at
|
||||
mongo.db.materials.update_one(
|
||||
{"_id": ObjectId(material_id)},
|
||||
{"$set": {"updated_at": now}}
|
||||
)
|
||||
|
||||
# 更新嵌套 lesson 的更新时间(如果你在 lesson 内有该字段)
|
||||
# 需要使用 arrayFilters 精确定位到对应章节/课时
|
||||
mongo.db.materials.update_one(
|
||||
{"_id": ObjectId(material_id)},
|
||||
{
|
||||
"$set": {
|
||||
"chapters.$[c].lessons.$[l].updated_at": now,
|
||||
# 可选:记录最近编辑人
|
||||
"chapters.$[c].lessons.$[l].last_editor": getattr(actor, "id", None) if actor else None
|
||||
}
|
||||
},
|
||||
array_filters=[
|
||||
{"c.chapter_name": chapter_name},
|
||||
{"l.lesson_name": lesson_name}
|
||||
]
|
||||
)
|
||||
def add_material_chapter_lesson(material_id, chapter_name, lesson_name, teacher_id):
|
||||
"""
|
||||
1) 生成占位 Markdown(lesson_prompt_score_prompt)
|
||||
|
||||
Reference in New Issue
Block a user