Commit 5dbcb427 authored by uuo00_n's avatar uuo00_n

feat: 重构用户与实体模型,实现账户与人物分离

引入新的数据模型和绑定机制,将用户账户与业务实体(学生/教师)解耦:
1. 新增 persons/teachers/bindings 集合及相关API
2. 改造 dashboard 服务适配新模型
3. 更新初始化脚本支持数据迁移
4. 保留旧字段兼容现有功能

登录响应现在包含绑定的人物信息,仪表盘查询基于实体模型重构
parent 7d00ec63
## 问题概述
- 现状:`users` 同时承载学生/老师/班主任/干部等身份,且“学生↔用户”绑定分散,导致查询与权限判定割裂。
- 痛点:
- 身份与账号未解耦,复用困难、扩展性差
- 教师/班主任/学生的组织归属(班级、系部)未统一建模
- 仪表盘与考勤口径需跨实体检索,复杂度与耦合高
## 目标
- 账户与人物实体解耦:统一用“账号(Account)↔人物(Person)”绑定
- 学生/老师/干部使用专属子实体,清晰表达业务属性
- 路由鉴权同时基于账号的角色等级与实体身份
## 数据模型(MongoDB 集合)
1. accounts(账号)
- 字段:`_id, username, email, hashed_password, edition, status`
- 说明:仅承载登录与权限基础信息,不直接承载业务属性
2. persons(人物基础档案)
- 字段:`person_id, name, gender, phone, id_no, type: ['student','teacher','staff']`
- 索引:`person_id` 唯一
3. students(学生实体)
- 字段:`person_id, student_id, grade, major, class_id, guardians, status, department`
- 关系:`account_id?`(可空);索引:`student_id` 唯一、`class_id``account_id` 稀疏唯一
4. teachers(教师实体)
- 字段:`person_id, teacher_id, department, title, roles: ['teacher','homeroom','cadre']`
- 关系:`account_id?`;索引:`teacher_id` 唯一、`department``account_id` 稀疏唯一
5. bindings(统一绑定表)
- 字段:`account_id, person_id, primary: boolean, type`
- 说明:一账号可绑定一个主人物(primary)与若干备用绑定;索引:`account_id+type` 唯一(primary)
6. classes(班级)
- 扩展:`head_teacher_person_id` 替代原 `head_teacher_id`(指向教师实体)
7. schedules(课表)
- 扩展:`teacher_person_id` 替代原 `teacher_id``classes: [{class_id, location}]` 保持共享节次
8. leaves/attendance/conduct/directives
- 维持现有集合,但在查询上通过 `person_id`/`student_id`/`class_id` 衔接
## 鉴权与角色
- 保留 `role_level`(user=1, manager=2, leader=3, master=4, administrator=5)
- 登录载荷扩展:`person_id, person_type, bound_primary`(用于后端快速定位实体)
- 路由依赖:在 `require_role(min_level)` 基础上增加实体类型校验(例如学生端必须有绑定且为 `student`
## API 设计(新增/改造)
- 绑定管理(新):`POST /api/v1/bindings``DELETE /api/v1/bindings/{account_id}/{person_id}``GET /api/v1/bindings/me`
- 人物档案:`POST/GET/PUT /api/v1/persons`
- 学生:`POST/GET/PUT /api/v1/students`(含批量导入/绑定工具)
- 教师:`POST/GET/PUT /api/v1/teachers`(标记班主任/干部)
- 班级与课表:适配 `person_id` 字段,查询统一从实体出发
- 仪表盘路由:学生/班主任/中层/校级端以实体维度查询,兼容旧数据
## 迁移方案
1. 引入新集合与索引(不破坏现有)
2. 从现有 `users` 生成 `persons` 与对应 `students/teachers`(示例用户:`leader_edu→teacher(cadre)``manager_edu→teacher(homeroom)`
3. 写入 `bindings`(把当前登录账号与对应人物建立主绑定)
4. 更新 `classes.head_teacher_person_id``schedules.teacher_person_id`
5. 仪表盘查询优先走新模型,保留旧模型回退
## 分阶段实施
- M1(模型与绑定):创建集合与索引,提供绑定接口,完成示例数据迁移与验证
- M2(查询改造):学生/班主任/中层/校级端查询切换到实体模型,完成序列化与性能验证
- M3(管理接口):完善人员与班级、系部管理接口,支持批量导入/绑定
- M4(清理与文档):去除冗余字段、补充接口文档与示例
## 验证与性能
- MCP 校验集合与索引;事务性绑定校验唯一约束
- 查询走索引:`account_id/person_id/class_id/teacher_person_id`
- 压测:按班级/教师的日级查询与节次查询
## 兼容策略
- 保留现有 `users` 字段与接口短期可用
- 新接口逐步替换前端对旧字段的依赖
确认后,我将按此方案落地:先交付 M1(集合与绑定接口),在不影响现有功能的前提下逐步迁移与联调。
\ No newline at end of file
......@@ -72,6 +72,11 @@ async def login(form_data: OAuth2PasswordRequestForm = Depends()):
"role": user.get("role", "user"),
"role_level": user.get("role_level", get_role_level(user.get("role"))),
"edition": user.get("edition", "edu"),
**({
"person_id": str(b["person_id"]),
"person_type": b.get("type"),
"bound_primary": True,
} if (b := await db.db.bindings.find_one({"account_id": user["_id"], "primary": True})) else {})
},
expires_delta=access_token_expires
)
......@@ -82,4 +87,9 @@ async def login(form_data: OAuth2PasswordRequestForm = Depends()):
"role": user.get("role", "user"),
"role_level": user.get("role_level", get_role_level(user.get("role"))),
"edition": user.get("edition", "edu"),
**({
"person_id": str(b["person_id"]),
"person_type": b.get("type"),
"bound_primary": True,
} if b else {})
}
\ No newline at end of file
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from app.api.deps import require_edition_for_mode, get_current_active_user, require_role
from app.services.bindings import create_binding, delete_binding, get_binding_by_account
router = APIRouter(dependencies=[Depends(require_edition_for_mode())])
class BindingPayload(BaseModel):
person_id: str
type: str
primary: bool = True
@router.post("")
async def bind(payload: BindingPayload, current_user: dict = Depends(require_role(2))):
ok = await create_binding(str(current_user["_id"]), payload.person_id, payload.type, payload.primary)
if not ok:
raise HTTPException(status_code=400, detail="主绑定已存在或参数错误")
return {"success": True}
@router.delete("/{person_id}")
async def unbind(person_id: str, current_user: dict = Depends(require_role(2))):
ok = await delete_binding(str(current_user["_id"]), person_id)
if not ok:
raise HTTPException(status_code=404, detail="未找到绑定")
return {"success": True}
@router.get("/me")
async def me(current_user: dict = Depends(get_current_active_user)):
b = await get_binding_by_account(str(current_user["_id"]))
if not b:
raise HTTPException(status_code=404, detail="未绑定人物")
b["account_id"] = str(b["account_id"])
b["person_id"] = str(b["person_id"])
return b
\ No newline at end of file
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from bson import ObjectId
from app.api.deps import require_edition_for_mode, require_role
from app.db.mongodb import db
router = APIRouter(dependencies=[Depends(require_edition_for_mode())])
class HeadTeacherPayload(BaseModel):
head_teacher_person_id: str
@router.put("/{class_id}/head-teacher")
async def set_head_teacher(class_id: str, payload: HeadTeacherPayload, current_user: dict = Depends(require_role(3))):
res = await db.db.classes.update_one({"class_id": class_id}, {"$set": {"head_teacher_person_id": ObjectId(payload.head_teacher_person_id)}})
if res.matched_count == 0:
raise HTTPException(status_code=404, detail="班级不存在")
return {"success": True}
@router.get("")
async def list_classes(current_user: dict = Depends(require_role(2))):
res = []
cursor = db.db.classes.find({}).sort("class_id", 1)
async for d in cursor:
d["_id"] = str(d["_id"]) if d.get("_id") else None
if d.get("head_teacher_person_id"):
d["head_teacher_person_id"] = str(d["head_teacher_person_id"])
res.append(d)
return res
\ No newline at end of file
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel, Field
from typing import List
from bson import ObjectId
from app.api.deps import require_edition_for_mode, require_role
from app.db.mongodb import db
router = APIRouter(dependencies=[Depends(require_edition_for_mode())])
class PersonCreate(BaseModel):
person_id: str
name: str
type: str = Field(pattern="^(student|teacher|staff)$")
@router.post("/bulk")
async def bulk_create(persons: List[PersonCreate], current_user: dict = Depends(require_role(3))):
docs = [{"person_id": p.person_id, "name": p.name, "type": p.type} for p in persons]
if not docs:
raise HTTPException(status_code=400, detail="空数据")
await db.db.persons.insert_many(docs)
return {"inserted": len(docs)}
@router.get("")
async def list_persons(current_user: dict = Depends(require_role(3))):
res = []
cursor = db.db.persons.find({}).sort("person_id", 1)
async for d in cursor:
d["_id"] = str(d["_id"])
res.append(d)
return res
\ No newline at end of file
from fastapi import APIRouter
from app.api.v1 import auth, conversation, admin, dashboard, students
from app.api.v1 import auth, conversation, admin, dashboard, students, bindings, persons, teachers, classes, schedules
api_router = APIRouter()
......@@ -8,4 +8,9 @@ api_router.include_router(auth.router, prefix="/auth", tags=["认证"])
api_router.include_router(conversation.router, prefix="/conversations", tags=["对话"])
api_router.include_router(admin.router, prefix="/admin", tags=["管理员"])
api_router.include_router(dashboard.router, prefix="/dashboard", tags=["仪表盘"])
api_router.include_router(students.router, prefix="/students", tags=["学生"])
\ No newline at end of file
api_router.include_router(students.router, prefix="/students", tags=["学生"])
api_router.include_router(bindings.router, prefix="/bindings", tags=["绑定"])
api_router.include_router(persons.router, prefix="/persons", tags=["人员"])
api_router.include_router(teachers.router, prefix="/teachers", tags=["教师"])
api_router.include_router(classes.router, prefix="/classes", tags=["班级"])
api_router.include_router(schedules.router, prefix="/schedules", tags=["课表"])
\ No newline at end of file
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from bson import ObjectId
from app.api.deps import require_edition_for_mode, require_role
from app.db.mongodb import db
router = APIRouter(dependencies=[Depends(require_edition_for_mode())])
class AssignTeacherPayload(BaseModel):
lesson_id: str
teacher_person_id: str
@router.put("/assign-teacher")
async def assign_teacher(payload: AssignTeacherPayload, current_user: dict = Depends(require_role(3))):
res = await db.db.schedules.update_one({"lesson_id": payload.lesson_id}, {"$set": {"teacher_person_id": ObjectId(payload.teacher_person_id)}})
if res.matched_count == 0:
raise HTTPException(status_code=404, detail="节次不存在")
return {"success": True}
@router.get("")
async def list_schedules(current_user: dict = Depends(require_role(2))):
res = []
cursor = db.db.schedules.find({}).sort([("weekday", 1), ("period", 1)])
async for d in cursor:
if d.get("teacher_person_id"):
d["teacher_person_id"] = str(d["teacher_person_id"])
res.append({
"lesson_id": d.get("lesson_id"),
"weekday": d.get("weekday"),
"period": d.get("period"),
"course_name": d.get("course_name"),
"teacher_person_id": d.get("teacher_person_id"),
"classes": d.get("classes", []),
})
return res
\ No newline at end of file
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from typing import List, Optional
from bson import ObjectId
from app.api.deps import require_edition_for_mode, require_role
from app.db.mongodb import db
router = APIRouter(dependencies=[Depends(require_edition_for_mode())])
class TeacherCreate(BaseModel):
person_id: str
teacher_id: str
department: str
roles: List[str]
account_id: Optional[str] = None
@router.post("/bulk")
async def bulk_create(teachers: List[TeacherCreate], current_user: dict = Depends(require_role(3))):
docs = []
for t in teachers:
doc = {
"person_id": ObjectId(t.person_id),
"teacher_id": t.teacher_id,
"department": t.department,
"roles": t.roles,
}
if t.account_id:
doc["account_id"] = ObjectId(t.account_id)
docs.append(doc)
if not docs:
raise HTTPException(status_code=400, detail="空数据")
await db.db.teachers.insert_many(docs)
return {"inserted": len(docs)}
@router.get("")
async def list_teachers(current_user: dict = Depends(require_role(3))):
res = []
cursor = db.db.teachers.find({}).sort("teacher_id", 1)
async for d in cursor:
d["_id"] = str(d["_id"])
d["person_id"] = str(d["person_id"])
if d.get("account_id"):
d["account_id"] = str(d["account_id"])
res.append(d)
return res
\ No newline at end of file
from typing import Optional, Dict
from bson import ObjectId
from app.db.mongodb import db
async def create_binding(account_id: str, person_id: str, type_: str, primary: bool = True) -> bool:
aid = ObjectId(account_id)
pid = ObjectId(person_id)
if primary:
existing = await db.db.bindings.find_one({"account_id": aid, "type": type_, "primary": True})
if existing:
return False
res = await db.db.bindings.insert_one({"account_id": aid, "person_id": pid, "type": type_, "primary": primary})
return bool(res.inserted_id)
async def delete_binding(account_id: str, person_id: str) -> bool:
aid = ObjectId(account_id)
pid = ObjectId(person_id)
res = await db.db.bindings.delete_one({"account_id": aid, "person_id": pid})
return res.deleted_count == 1
async def get_binding_by_account(account_id: str) -> Optional[Dict]:
aid = ObjectId(account_id)
return await db.db.bindings.find_one({"account_id": aid, "primary": True})
\ No newline at end of file
......@@ -19,12 +19,30 @@ async def _current_period() -> int:
if h < 12:
return 3
return 4
async def _get_student_by_user(user_id: ObjectId) -> Dict[str, Any]:
s = await db.db.students.find_one({"user_id": user_id})
async def _get_primary_binding(account_id: ObjectId) -> Dict[str, Any]:
b = await db.db.bindings.find_one({"account_id": account_id, "primary": True})
if not b:
raise HTTPException(status_code=404, detail="未绑定人物")
return b
async def _get_student_entity(account_id: ObjectId, binding: Dict[str, Any]) -> Dict[str, Any]:
s = await db.db.students.find_one({"person_id": binding.get("person_id")})
if s:
return s
raise HTTPException(status_code=404, detail="当前用户未绑定学生")
raise HTTPException(status_code=404, detail="未找到学生实体")
async def _get_teacher_entity(account_id: ObjectId, binding: Dict[str, Any]) -> Dict[str, Any]:
t = await db.db.teachers.find_one({"person_id": binding.get("person_id")})
if t:
return t
raise HTTPException(status_code=404, detail="未找到教师实体")
async def _get_student_by_user(user_id: ObjectId) -> Dict[str, Any]:
b = await _get_primary_binding(user_id)
if b.get("type") != "student":
raise HTTPException(status_code=404, detail="当前绑定非学生")
return await _get_student_entity(user_id, b)
async def student_today_summary(current_user: Dict[str, Any]) -> Dict[str, Any]:
today = await _today_iso()
......@@ -86,7 +104,7 @@ async def homeroom_current_summary(current_user: Dict[str, Any]) -> Dict[str, An
current_lesson_id = f"W{weekday}-P{period}"
uid = ObjectId(current_user["_id"])
classes = []
cursor = db.db.classes.find({"head_teacher_id": uid})
cursor = db.db.classes.find({"$or": [{"head_teacher_person_id": uid}, {"head_teacher_id": uid}]})
async for c in cursor:
classes.append(c)
class_ids = [c.get("class_id") for c in classes]
......@@ -148,32 +166,82 @@ async def department_overview(current_user: Dict[str, Any]) -> Dict[str, Any]:
today = await _today_iso()
weekday = await _weekday()
total_students = await db.db.students.count_documents({})
present = await db.db.attendance.count_documents({"date": today, "status": "出勤"})
absent = await db.db.attendance.count_documents({"date": today, "status": {"$in": ["缺勤", "请假"]}})
ratio = (present / total_students) if total_students else 0
present_today = await db.db.attendance.count_documents({"date": today, "status": "出勤"})
absent_or_leave_today = await db.db.attendance.count_documents({"date": today, "status": {"$in": ["缺勤", "请假"]}})
anomalies: List[Dict[str, Any]] = []
cursor = db.db.classes.find({})
async for c in cursor:
cursor_classes = db.db.classes.find({})
async for c in cursor_classes:
cid = c.get("class_id")
total = c.get("students_count", 0)
a = await db.db.attendance.count_documents({"class_id": cid, "date": today, "status": {"$in": ["缺勤", "请假"]}})
r = (a / total) if total else 0
if r > 0.3:
anomalies.append({"class_id": cid, "anomaly_rate": r})
teachers: Dict[str, int] = {}
cursor = db.db.schedules.find({"weekday": weekday})
async for s in cursor:
abnormal = await db.db.attendance.count_documents({
"class_id": cid,
"date": today,
"status": {"$in": ["缺勤", "请假"]}
})
rate = (abnormal / total) if total else 0
if rate > 0.3:
anomalies.append({"class_id": cid, "anomaly_rate": rate})
teacher_stats: Dict[str, Dict[str, int]] = {}
cursor_sched = db.db.schedules.find({"weekday": weekday})
async for s in cursor_sched:
tid = s.get("teacher_id")
if tid:
key = str(tid)
teachers[key] = teachers.get(key, 0) + 1
if not tid:
continue
key = str(tid)
st = teacher_stats.get(key)
if not st:
st = {"present_slots": 0, "total_slots": 0}
teacher_stats[key] = st
st["total_slots"] += 1
ratios: List[float] = []
for cc in s.get("classes", []):
cid = cc.get("class_id")
cls = await db.db.classes.find_one({"class_id": cid})
total = cls.get("students_count", 0) if cls else 0
present = await db.db.attendance.count_documents({
"class_id": cid,
"date": today,
"lesson_id": s.get("lesson_id"),
"status": "出勤",
})
ratios.append((present / total) if total else 0)
avg_ratio = (sum(ratios) / len(ratios)) if ratios else 0
if avg_ratio >= 0.7:
st["present_slots"] += 1
teacher_attendance_rates: List[Dict[str, Any]] = []
for key, st in teacher_stats.items():
total_slots = st.get("total_slots", 0)
present_slots = st.get("present_slots", 0)
rate = (present_slots / total_slots) if total_slots else 0
teacher_attendance_rates.append({
"teacher_id": key,
"present_slots": present_slots,
"total_slots": total_slots,
"rate": rate,
})
directives: List[Dict[str, Any]] = []
cursor_dir = db.db.directives.find({"level": {"$in": ["department", "campus"]}}).sort("created_at", -1).limit(5)
async for d in cursor_dir:
directives.append({
"level": d.get("level"),
"content": d.get("content"),
"created_at": d.get("created_at"),
})
return {
"student_present_ratio": ratio,
"students_attendance": {
"total": total_students,
"present": present_today,
"absent_or_leave": absent_or_leave_today,
},
"teacher_attendance_rates": teacher_attendance_rates,
"anomalies": anomalies,
"teacher_timeslots": teachers,
"directives": directives,
}
async def campus_overview(current_user: Dict[str, Any]) -> Dict[str, Any]:
......
......@@ -357,10 +357,13 @@ async def seed_school_data(db, mode: str):
await db.students.create_index("student_id", unique=True)
await db.students.create_index("class_id")
await db.students.create_index("user_id", unique=True, sparse=True)
await db.students.create_index("person_id", unique=True, sparse=True)
await db.classes.create_index("class_id", unique=True)
await db.classes.create_index("head_teacher_id")
await db.classes.create_index("head_teacher_person_id")
await db.schedules.create_index([("classes.class_id", 1), ("weekday", 1), ("period", 1)])
await db.schedules.create_index("lesson_id", unique=True)
await db.schedules.create_index("teacher_person_id")
await db.attendance.create_index([("lesson_id", 1), ("student_id", 1)])
await db.attendance.create_index("date")
await db.conduct.create_index([("student_id", 1), ("date", 1)])
......@@ -414,9 +417,6 @@ async def seed_school_data(db, mode: str):
return docs
students_docs = gen_students("SW22-1", 12) + gen_students("SW22-2", 12)
sample_user = await db.users.find_one({"username": "user", "edition": mode})
if sample_user and students_docs:
students_docs[0]["user_id"] = sample_user["_id"]
await db.students.insert_many(students_docs)
# 更新班级人数
......@@ -447,6 +447,7 @@ async def seed_school_data(db, mode: str):
"period": period,
"course_name": course,
"teacher_id": teacher_id,
"teacher_person_id": None,
"start_time": f"{8 + period - 1}:00",
"end_time": f"{8 + period}:40",
"week_range": "1-18",
......@@ -539,5 +540,89 @@ async def seed_school_data(db, mode: str):
print("学校业务数据初始化完成:students/classes/schedules/attendance/conduct/leaves/directives")
await seed_identity_data(db, mode)
async def seed_identity_data(db, mode: str):
await db.persons.create_index("person_id", unique=True)
await db.teachers.create_index("teacher_id", unique=True)
await db.teachers.create_index("account_id", unique=True, sparse=True)
await db.bindings.create_index([("account_id", 1), ("type", 1)], unique=True)
user_doc = await db.users.find_one({"username": "user", "edition": mode})
manager_doc = await db.users.find_one({"username": "manager_edu", "edition": mode})
leader_doc = await db.users.find_one({"username": "leader_edu", "edition": mode})
master_doc = await db.users.find_one({"username": "master_edu", "edition": mode})
persons = []
student_persons = []
cursor_students = db.students.find({})
async for stu in cursor_students:
pid = f"P-STU-{stu.get('student_id')}"
person_doc = {"_id": ObjectId(), "person_id": pid, "name": stu.get("name"), "type": "student"}
persons.append(person_doc)
student_persons.append({"person": person_doc, "student_id": stu.get("student_id")})
if user_doc:
persons.append({"_id": ObjectId(), "person_id": "P-STU-USER", "name": "学生USER", "type": "student"})
if manager_doc:
persons.append({"_id": ObjectId(), "person_id": "P-TEA-001", "name": "班主任A", "type": "teacher"})
if leader_doc:
persons.append({"_id": ObjectId(), "person_id": "P-TEA-002", "name": "中层干部A", "type": "teacher"})
if master_doc:
persons.append({"_id": ObjectId(), "person_id": "P-TEA-003", "name": "校级干部A", "type": "teacher"})
if persons:
await db.persons.insert_many(persons)
teachers = []
def find_person(pid: str):
return next((p for p in persons if p["person_id"] == pid), None)
if manager_doc:
p = find_person("P-TEA-001")
teachers.append({"person_id": p["_id"], "teacher_id": "T-001", "department": "软件技术系", "roles": ["teacher", "homeroom"], "account_id": manager_doc["_id"]})
if leader_doc:
p = find_person("P-TEA-002")
teachers.append({"person_id": p["_id"], "teacher_id": "T-002", "department": "软件技术系", "roles": ["teacher", "cadre"], "account_id": leader_doc["_id"]})
if master_doc:
p = find_person("P-TEA-003")
teachers.append({"person_id": p["_id"], "teacher_id": "T-003", "department": "软件技术系", "roles": ["teacher", "cadre"], "account_id": master_doc["_id"]})
if teachers:
await db.teachers.insert_many(teachers)
for sp in student_persons:
await db.students.update_one({"student_id": sp["student_id"]}, {"$set": {"person_id": sp["person"]["_id"]}})
bindings = []
if user_doc:
p = find_person("P-STU-USER")
bindings.append({"account_id": user_doc["_id"], "person_id": p["_id"], "type": "student", "primary": True})
if manager_doc:
p = find_person("P-TEA-001")
bindings.append({"account_id": manager_doc["_id"], "person_id": p["_id"], "type": "teacher", "primary": True})
if leader_doc:
p = find_person("P-TEA-002")
bindings.append({"account_id": leader_doc["_id"], "person_id": p["_id"], "type": "teacher", "primary": True})
if master_doc:
p = find_person("P-TEA-003")
bindings.append({"account_id": master_doc["_id"], "person_id": p["_id"], "type": "teacher", "primary": True})
if bindings:
await db.bindings.insert_many(bindings)
teacher_map = {}
cursor_teachers = db.teachers.find({})
async for t in cursor_teachers:
if t.get("account_id"):
teacher_map[str(t.get("account_id"))] = t.get("person_id")
manager_person = teacher_map.get(str(manager_doc["_id"])) if manager_doc else None
if manager_person:
await db.classes.update_many({}, {"$set": {"head_teacher_person_id": manager_person}})
for acc_id, person_id in teacher_map.items():
await db.schedules.update_many({"teacher_id": ObjectId(acc_id)}, {"$set": {"teacher_person_id": person_id}})
# 移除重复旧字段,保留实体化字段
await db.classes.update_many({}, {"$unset": {"head_teacher_id": ""}})
await db.schedules.update_many({}, {"$unset": {"teacher_id": ""}})
await db.students.update_many({}, {"$unset": {"user_id": ""}})
if __name__ == "__main__":
asyncio.run(init_db())
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment