v2.5更新

This commit is contained in:
2026-05-29 17:35:29 +08:00
parent 6c0d8f0e94
commit fe58ee1d23
12 changed files with 258 additions and 24 deletions

View File

@@ -161,7 +161,8 @@ class ConductModel:
student_id: int = None,
include_revoked: bool = True,
related_type: str = None,
reason_prefix: str = None
reason_prefix: str = None,
is_revoked: int = None
) -> List[Dict[str, Any]]:
"""获取所有记录(班主任/班长专用)"""
# 空字符串转为None
@@ -206,6 +207,10 @@ class ConductModel:
sql += " AND cr.reason LIKE %s"
params.append(f"{reason_prefix}%")
if is_revoked is not None:
sql += " AND cr.is_revoked = %s"
params.append(1 if is_revoked else 0)
sql += " ORDER BY cr.created_at DESC LIMIT %s OFFSET %s"
params.extend([limit, offset])

View File

@@ -76,6 +76,12 @@ class SubjectModel:
@staticmethod
async def delete(subject_id: int) -> bool:
"""软删除科目(设置 is_active = 0如果已禁用也返回成功"""
subject = await SubjectModel.get_by_id(subject_id)
if not subject:
return False
if subject.get("is_active") == 0:
return True # 已禁用,视为成功
sql = "UPDATE subjects SET is_active = 0 WHERE subject_id = %s"
result = await execute_update(sql, (subject_id,))
return result > 0

View File

@@ -319,7 +319,8 @@ async def get_conduct_history(
end_date: Optional[str] = None,
grouped: bool = Query(False),
related_type: Optional[str] = None,
reason_prefix: Optional[str] = None
reason_prefix: Optional[str] = None,
is_revoked: Optional[int] = None
):
"""获取操行分历史记录"""
try:
@@ -335,7 +336,8 @@ async def get_conduct_history(
end_date=end_date,
grouped=grouped,
related_type=related_type,
reason_prefix=reason_prefix
reason_prefix=reason_prefix,
is_revoked=is_revoked
)
return success_response(data=result)
except Exception as e:
@@ -343,6 +345,86 @@ async def get_conduct_history(
return error_response(message=f"获取历史记录失败: {str(e)}")
@router.post("/conduct/batch-revoke")
async def batch_revoke_conduct_records(request: Request):
"""批量撤销操行分记录"""
try:
user = await get_current_user(request)
if user["user_type"] != "admin":
return error_response(message="无权进行此操作", code=403)
body = await request.json()
record_ids = body.get("record_ids", [])
if not record_ids or not isinstance(record_ids, list):
return error_response(message="请提供要撤销的记录ID列表", code=400)
if len(record_ids) > 100:
return error_response(message="单次最多撤销100条记录", code=400)
success_count = 0
fail_count = 0
errors = []
for record_id in record_ids:
result = await ConductService.revoke_record(
record_id=record_id,
revoker_id=user["user_id"]
)
if result["success"]:
success_count += 1
else:
fail_count += 1
errors.append({"record_id": record_id, "error": result["message"]})
return success_response(data={
"success_count": success_count,
"fail_count": fail_count,
"errors": errors
}, message=f"批量撤销完成: {success_count}条成功, {fail_count}条失败")
except Exception as e:
logger.error(f"批量撤销失败: {e}", exc_info=True)
return error_response(message=f"批量撤销失败: {str(e)}")
@router.post("/conduct/batch-restore")
async def batch_restore_conduct_records(request: Request):
"""批量反撤销操行分记录"""
try:
user = await get_current_user(request)
if user["user_type"] != "admin":
return error_response(message="无权进行此操作", code=403)
body = await request.json()
record_ids = body.get("record_ids", [])
if not record_ids or not isinstance(record_ids, list):
return error_response(message="请提供要反撤销的记录ID列表", code=400)
if len(record_ids) > 100:
return error_response(message="单次最多反撤销100条记录", code=400)
success_count = 0
fail_count = 0
errors = []
for record_id in record_ids:
result = await ConductService.restore_record(
record_id=record_id,
restorer_id=user["user_id"]
)
if result["success"]:
success_count += 1
else:
fail_count += 1
errors.append({"record_id": record_id, "error": result["message"]})
return success_response(data={
"success_count": success_count,
"fail_count": fail_count,
"errors": errors
}, message=f"批量反撤销完成: {success_count}条成功, {fail_count}条失败")
except Exception as e:
logger.error(f"批量反撤销失败: {e}", exc_info=True)
return error_response(message=f"批量反撤销失败: {str(e)}")
# ========== 考勤管理 ==========

View File

@@ -18,7 +18,6 @@ import re
logger = setup_logger()
router = APIRouter()
# 版本列表(按顺序)
# 版本列表(按顺序)
ALL_VERSIONS = {
'1.0': 'v1.0.sql',
@@ -35,7 +34,9 @@ ALL_VERSIONS = {
'2.1': 'v2.1.sql',
'2.2': 'v2.2.sql',
'2.3': 'v2.3.sql',
'2.3': 'v2.3.sql',
'2.4': 'v2.4.sql',
'2.5': 'v2.5.sql',
}
# 版本特征标记(按优先级从高到低)

View File

@@ -213,7 +213,8 @@ class ConductService:
end_date: Optional[str] = None,
grouped: bool = False,
related_type: Optional[str] = None,
reason_prefix: Optional[str] = None
reason_prefix: Optional[str] = None,
is_revoked: Optional[int] = None
) -> Dict[str, Any]:
"""获取历史记录"""
# 空字符串转为None
@@ -251,7 +252,8 @@ class ConductService:
end_date=end_date,
student_id=student_id,
related_type=related_type,
reason_prefix=reason_prefix
reason_prefix=reason_prefix,
is_revoked=is_revoked
)
# 获取总数
@@ -273,6 +275,9 @@ class ConductService:
if reason_prefix:
count_conditions.append("cr.reason LIKE %s")
count_params.append(f"{reason_prefix}%")
if is_revoked is not None:
count_conditions.append("cr.is_revoked = %s")
count_params.append(1 if is_revoked else 0)
count_where = " AND ".join(count_conditions)
count_sql = f"""
SELECT COUNT(*) as total FROM conduct_records cr