Version 0.1.1 rise to flask app factory

This commit is contained in:
CakeCN
2025-08-27 19:39:38 +08:00
parent 8dba8688a7
commit 5c0187bc4b
55 changed files with 1708 additions and 613 deletions

View File

@@ -0,0 +1,46 @@
# myapp/services/auth_service.py
import os, uuid, json
from flask import current_app, session
def create_user_json(username: str):
user_dir = current_app.config["USER_DATA_DIR"]
os.makedirs(user_dir, exist_ok=True)
path = os.path.join(user_dir, f"{username}.json")
if not os.path.exists(path):
with open(path, "w", encoding="utf-8") as f:
json.dump({"username": username}, f, ensure_ascii=False)
def register_user(username: str, password: str, *, teacher: bool=False):
users_list = current_app.extensions["users_list"]
# 你原逻辑似乎把 has_user 的判断反了:直觉上「不存在才允许新增」
exists = users_list.has_user(username)
if exists:
return False, "用户已存在,请更换用户名"
users_list.add_user(username, password, teacher=teacher)
create_user_json(username)
return True, "注册成功"
def login_user(username: str, password: str, *, require_teacher: bool=False):
users_list = current_app.extensions["users_list"]
pswd = users_list.get_user_pswd(username)
if pswd is None or pswd != password:
return False, "用户名或密码错误"
if require_teacher:
is_teacher = users_list.get_user_is_teacher(username)
if not is_teacher:
return False, "用户名不存在或非教师账号"
# 设置会话 + 关联映射
user_uuid = "user_" + str(uuid.uuid4())
session["user_id"] = user_uuid
username2uuid = current_app.extensions["username2uuid"]
uuid2username = current_app.extensions["uuid2username"]
username2uuid[username] = user_uuid
uuid2username[user_uuid] = username
return True, "登录成功"
def logout_user():
session.pop("user_id", None)

View File

@@ -0,0 +1,59 @@
# myapp/services/backboard_service.py
import os
from flask import current_app
from backboardManager import Backboard # 你已有的类
def _to_wsl_path_if_needed(path: str) -> str:
cfg = current_app.config["VSCODE_WEB_PATH"]
if cfg.get("is_wsl") and cfg.get("windows_path") and cfg.get("wsl_path"):
return path.replace("\\", "/").replace(cfg["windows_path"], cfg["wsl_path"])
return path
def realtime_response(config: dict, realtime_action: dict) -> None:
"""
业务:根据 VSCode 上报的动作,更新 Backboard黑板状态。
"""
# 依赖从 app.extensions 取,避免循环导入
username2uuid = current_app.extensions["username2uuid"]
backboard_manager = current_app.extensions["backboard_manager"]
user_id = config["user_id"]
folder_path = config["path"] # 如需 WSL 转换可在生成 config 时处理
useruuid = username2uuid[user_id]
bb = backboard_manager.get_backboard(useruuid)
assert isinstance(bb, Backboard)
# 统一记历史
bb.add_history(realtime_action)
rtype = realtime_action.get("type")
if rtype == "workspaceFolders":
bb.file_tree = realtime_action.get("fileTree")
elif rtype == "activeFile":
file_path = realtime_action.get("filePath")
assert isinstance(file_path, str)
with open(file_path, "r", encoding="utf-8") as f:
bb.active_file_content = f.read()
bb.active_file_path = file_path
elif rtype == "paste":
file_path = realtime_action.get("filePath")
assert isinstance(file_path, str)
bb.pasted_file_path = file_path
bb.pasted_content = realtime_action.get("content")
bb.active_file_path = file_path
with open(file_path, "r", encoding="utf-8") as f:
bb.active_file_content = f.read()
elif rtype == "fileEdit":
file_path = realtime_action.get("filePath")
assert isinstance(file_path, str)
bb.active_file_path = file_path
with open(file_path, "r", encoding="utf-8") as f:
bb.active_file_content = f.read()
# 如需调试输出,可在这里统一记录日志
current_app.logger.debug("vscode config: %s", config)
current_app.logger.debug("vscode action: %s", realtime_action)

View File

@@ -0,0 +1,20 @@
# myapp/services/course_service.py
from flask import current_app
# 假设你已有这两个 loader
from db.course import load_course_from_json
def load_course(course_id: str):
return load_course_from_json(
course_id, course_data_dir=current_app.config["COURSE_DATA_DIR"]
)
def user_selected_course_briefs(user_obj):
"""根据用户对象的选课列表,拼装课程简要信息数组"""
course_list = current_app.extensions["course_list"]
briefs = []
for cid in getattr(user_obj, "select_course", []):
course_obj = load_course(cid)
brief = course_list.get_course_brief_info(cid, course_obj)
briefs.append(brief)
return briefs

View File

@@ -0,0 +1,40 @@
import os, shutil, markdown
def convert_markdown_to_html(md_file_path: str) -> str:
"""读取 markdown 并转成 HTML 片段"""
with open(md_file_path, 'r', encoding='utf-8') as f:
md_content = f.read()
return markdown.markdown(md_content)
def wrap_with_styles(html_content: str) -> str:
"""加上 CSS 样式和外层 HTML"""
return f"""
<html>
<head>
<style>
img {{
max-width: 100%;
height: auto;
}}
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.2.0/github-markdown.min.css">
</head>
<body class="markdown-body">
{html_content}
</body>
</html>
"""
def save_html(html_str: str, output_path: str) -> None:
os.makedirs(os.path.dirname(output_path), exist_ok=True)
with open(output_path, "w", encoding="utf-8") as f:
f.write(html_str)
def copy_images(source_dir: str, target_dir: str) -> None:
if not os.path.exists(source_dir):
return
os.makedirs(target_dir, exist_ok=True)
for filename in os.listdir(source_dir):
src = os.path.join(source_dir, filename)
if os.path.isfile(src):
shutil.copy(src, target_dir)

View File

@@ -0,0 +1,26 @@
# myapp/services/memory_service.py
import os, json
from flask import current_app
from db.user import load_user_from_json # 按你项目实际替换
def save_chapter_memory_by_uuid(
user_uuid: str,
course_id: str,
lesson_id: str,
subchapter_title: str,
mem_list,
score,
is_rebuttal: bool,
):
uuid2username = current_app.extensions["uuid2username"]
user_map = current_app.extensions["user_id2UserClass"] # 可复用缓存
user_data_dir = current_app.config["USER_DATA_DIR"]
username = uuid2username[user_uuid]
# 若内存里已有用户对象就直接用;否则从 JSON 载入
user_obj = user_map.get(user_uuid) or load_user_from_json(username, user_data_dir=user_data_dir)
user_map[user_uuid] = user_obj
user_obj.save_chapter_memory(
course_id, lesson_id, subchapter_title, mem_list, score, is_rebuttal
)

View File

@@ -0,0 +1,10 @@
# myapp/services/my_function.py
from flask import current_app
from .memory_service import save_chapter_memory_by_uuid
class MyFunction:
def save_chapter_memory(self, user_uuid, course_id, lesson_id, subchapter_title, mem_list, score, is_rebuttal):
current_app.logger.debug("save_chapter_memory %s %s %s", user_uuid, course_id, lesson_id)
save_chapter_memory_by_uuid(
user_uuid, course_id, lesson_id, subchapter_title, mem_list, score, is_rebuttal
)

View File

@@ -0,0 +1,42 @@
# myapp/services/user_service.py
import os
from typing import Tuple
from flask import current_app, session
# 假设你已有这两个 loader
from db.user import load_user_from_json
def _username_from_session() -> str:
"""通过 session['user_id'] -> username"""
user_uuid = session.get("user_id")
if not user_uuid:
return None
uuid2username = current_app.extensions["uuid2username"]
return uuid2username.get(user_uuid)
def get_or_load_current_user():
"""
返回当前用户对象(如 user_id2UserClass 中没有则从 JSON 载入并缓存)
"""
user_uuid = session.get("user_id")
if not user_uuid:
return None
username = _username_from_session()
if not username:
return None
user_map = current_app.extensions["user_id2UserClass"]
if user_uuid not in user_map:
user_map[user_uuid] = load_user_from_json(
username, user_data_dir=current_app.config["USER_DATA_DIR"]
)
return user_map[user_uuid]
def add_course_for_current_user(course_id: str, course_data):
user_obj = get_or_load_current_user()
if user_obj is None:
return False
# 你的 UserClass 应该有 select_new_course 接口
user_obj.select_new_course(course_id, course_data)
return True