Files
2026-09-09 16:43:57 +08:00

196 lines
9.6 KiB
Python

"""学生端分组用例和班级快照查询。"""
from __future__ import annotations
from typing import Any
class GroupOperationsMixin:
def class_snapshot(self, class_name: str) -> dict[str, Any]:
with self.lock:
groups = self.connection.execute(
"""SELECT g.id,g.name,g.leader_student_id,g.created_at,
s.student_no AS leader_student_no,s.name AS leader_name
FROM groups g LEFT JOIN students s ON s.id=g.leader_student_id
WHERE g.class_name=? ORDER BY g.id""",
(class_name,),
).fetchall()
group_ids = [row["id"] for row in groups]
members: dict[int, list[dict[str, Any]]] = {row["id"]: [] for row in groups}
if group_ids:
placeholders = ",".join("?" for _ in group_ids)
member_rows = self.connection.execute(
f"""SELECT m.group_id,st.id,st.student_no,st.name,st.gender,g.leader_student_id
FROM group_members m JOIN students st ON st.id=m.student_id
JOIN groups g ON g.id=m.group_id
WHERE m.group_id IN ({placeholders})
ORDER BY CASE WHEN st.id=g.leader_student_id THEN 0 ELSE 1 END,m.joined_at,st.student_no""",
group_ids,
).fetchall()
pending = [dict(row) for row in member_rows]
for member in pending:
member["is_leader"] = member["id"] == member["leader_student_id"]
member.pop("leader_student_id", None)
members[member["group_id"]].append(member)
ungrouped_rows = self.connection.execute(
"""SELECT id,student_no,name,gender FROM students
WHERE class_name=? AND id NOT IN (SELECT student_id FROM group_members)
ORDER BY student_no""",
(class_name,),
).fetchall()
total = self.connection.execute(
"SELECT COUNT(*) AS n FROM students WHERE class_name=?", (class_name,)
).fetchone()["n"]
grouped = self.connection.execute(
"""SELECT COUNT(*) AS n FROM group_members m
JOIN students s ON s.id=m.student_id WHERE s.class_name=?""",
(class_name,),
).fetchone()["n"]
group_data = []
for row in groups:
group_data.append(
{
**dict(row),
"locked": self.is_locked(class_name),
"member_count": len(members[row["id"]]),
"full": len(members[row["id"]]) >= self.group_limit(),
"members": members[row["id"]],
}
)
return {
"class_name": class_name,
"locked": self.is_locked(class_name),
"group_limit": self.group_limit(),
"groups": group_data,
"ungrouped": [dict(row) for row in ungrouped_rows],
"statistics": {"total_students": total, "grouped_students": grouped, "group_count": len(group_data)},
}
def create_group(self, student_id: int, group_name: str) -> tuple[bool, str, int | None]:
student = self.get_student(student_id)
if student is None:
return False, "学生不存在", None
if self.is_locked(student["class_name"]):
return False, "老师已停止分组操作,当前只能查看", None
current = self.student_membership(student_id)
if current is not None:
return False, "你已加入其他组,不能创建组;请先退出当前组", None
if student["must_change_password"]:
return False, "首次登录必须先修改密码", None
group_name = group_name.strip() or student["name"]
with self.lock, self.connection:
count = self.connection.execute(
"SELECT COUNT(*) AS n FROM groups WHERE class_name=?", (student["class_name"],)
).fetchone()["n"]
cursor = self.connection.execute(
"INSERT INTO groups(class_name,name,leader_student_id) VALUES(?,?,?)",
(student["class_name"], group_name, student_id),
)
group_id = int(cursor.lastrowid)
self.connection.execute(
"INSERT INTO group_members(group_id,student_id) VALUES(?,?)",
(group_id, student_id),
)
return True, f"“{group_name}”创建成功,你是组长", group_id
def join_group(self, student_id: int, group_id: int, confirmed: bool) -> tuple[bool, str]:
student = self.get_student(student_id)
if student is None:
return False, "学生不存在"
if student["must_change_password"]:
return False, "首次登录必须先修改密码"
if self.is_locked(student["class_name"]):
return False, "老师已停止分组操作,当前只能查看"
with self.lock:
group = self.connection.execute(
"SELECT * FROM groups WHERE id=?", (group_id,)
).fetchone()
current = self.connection.execute(
"""SELECT m.group_id,g.name,g.leader_student_id
FROM group_members m JOIN groups g ON g.id=m.group_id WHERE m.student_id=?""",
(student_id,),
).fetchone()
if group is None:
return False, "目标小组不存在"
if group["class_name"] != student["class_name"]:
return False, "不能加入其他班级的小组"
if current and current["group_id"] == group_id:
return False, "你已经在这个组里"
member_count = self.connection.execute(
"SELECT COUNT(*) AS n FROM group_members WHERE group_id=?", (group_id,)
).fetchone()["n"]
group_limit = self.group_limit()
if member_count >= group_limit:
return False, f"该组已有 {group_limit} 人,不能再加入新成员"
if current and not confirmed:
role = "组长,原组会被解散" if current["leader_student_id"] == student_id else "成员"
return False, f"你当前是“{current['name']}”的{role}。加入新组前必须退出原组;确认后才会执行。"
with self.lock, self.connection:
if current:
if current["leader_student_id"] == student_id:
self.connection.execute("DELETE FROM groups WHERE id=?", (current["group_id"],))
else:
self.connection.execute(
"DELETE FROM group_members WHERE student_id=?", (student_id,)
)
self.connection.execute(
"INSERT INTO group_members(group_id,student_id) VALUES(?,?)",
(group_id, student_id),
)
return True, "加入成功"
def leave_group(self, student_id: int, confirmed: bool) -> tuple[bool, str]:
if self.get_student(student_id) is None:
return False, "学生不存在"
current = self.student_membership(student_id)
if current is None:
return False, "你当前没有加入任何小组"
if self.is_locked(current["class_name"]):
return False, "老师已停止分组操作,当前只能查看"
is_leader = current["leader_student_id"] == student_id
if not confirmed:
if is_leader:
return False, "你是组长。退出时你创建的组将被解散,所有成员都会变为未分组;确认后才会执行。"
return False, "退出后可以再加入其他组;确认后才会执行。"
with self.lock, self.connection:
if is_leader:
self.connection.execute("DELETE FROM groups WHERE id=?", (current["group_id"],))
else:
self.connection.execute(
"DELETE FROM group_members WHERE group_id=? AND student_id=?",
(current["group_id"], student_id),
)
return True, "组长已解散小组" if is_leader else "已退出小组"
def leader_remove_member(self, leader_student_id: int, member_student_id: int) -> tuple[bool, str]:
with self.lock, self.connection:
leader_group = self.connection.execute(
"""SELECT m.group_id,g.name,g.class_name,g.leader_student_id
FROM group_members m JOIN groups g ON g.id=m.group_id
WHERE m.student_id=?""",
(leader_student_id,),
).fetchone()
if leader_group is None or leader_group["leader_student_id"] != leader_student_id:
return False, "只有组长可以移出组员"
if leader_student_id == member_student_id:
return False, "组长不能移出自己;如需退出请解散小组"
if self.is_locked(leader_group["class_name"]):
return False, "老师已停止分组操作,当前只能查看"
member = self.connection.execute(
"""SELECT st.name
FROM group_members m JOIN students st ON st.id=m.student_id
WHERE m.student_id=? AND m.group_id=?""",
(member_student_id, leader_group["group_id"]),
).fetchone()
if member is None:
return False, "该学生不在你的小组中"
self.connection.execute(
"DELETE FROM group_members WHERE student_id=? AND group_id=?",
(member_student_id, leader_group["group_id"]),
)
return True, f"已将 {member['name']} 移出小组"