v0.8.6测试

This commit is contained in:
2026-04-16 10:55:34 +08:00
parent 112dc94f7c
commit 3a4e0357a3
3 changed files with 85 additions and 16 deletions

View File

@@ -435,4 +435,42 @@ async def delete_admin(request: Request, user_id: int):
) )
return success_response(message="管理员删除成功") return success_response(message="管理员删除成功")
else: else:
return error_response(message="删除失败或管理员不存在") return error_response(message="删除失败或管理员不存在")
@router.post("/reset-password/{user_id}")
async def reset_admin_password(request: Request, user_id: int):
"""重置管理员密码(班主任)"""
user = await get_current_user(request)
is_teacher = await PermissionChecker.check_is_teacher(user["user_id"])
if not is_teacher:
return error_response(message="仅班主任可重置密码", code=403)
from models.user import UserModel
from utils.security import SecurityUtils
# 获取管理员信息
target_user = await UserModel.get_by_user_id(user_id)
if not target_user:
return error_response(message="管理员不存在", code=404)
if target_user["user_type"] != "admin":
return error_response(message="只能重置管理员密码", code=400)
# 生成新密码
new_password = SecurityUtils.generate_random_password(8)
password_hash = SecurityUtils.sha1_md5_password(new_password)
# 更新密码
updated = await UserModel.update_password(user_id, password_hash)
if updated:
await LogService.write_operation_log(
operator_id=user["user_id"], operator_name=user["username"],
operator_role="班主任", operation_type="reset_password",
target_type="admin", target_id=user_id,
details=f"重置管理员密码: {target_user['real_name']}({target_user['username']})",
ip=request.client.host
)
return success_response(data={"password": new_password}, message="密码重置成功")
else:
return error_response(message="密码重置失败")

View File

@@ -135,6 +135,7 @@ async function loadAdmins() {
<td>${escapeHtml(admin.role_type)}</td> <td>${escapeHtml(admin.role_type)}</td>
<td> <td>
<button class="btn btn-sm btn-primary" onclick="showEditAdminModal(${admin.user_id}, '${escapeHtml(admin.username)}', '${escapeHtml(admin.real_name)}', '${escapeHtml(admin.role_type)}')">编辑</button> <button class="btn btn-sm btn-primary" onclick="showEditAdminModal(${admin.user_id}, '${escapeHtml(admin.username)}', '${escapeHtml(admin.real_name)}', '${escapeHtml(admin.role_type)}')">编辑</button>
<button class="btn btn-sm btn-warning" onclick="resetAdminPassword(${admin.user_id}, '${escapeHtml(admin.real_name)}')">重置密码</button>
<button class="btn btn-sm btn-danger" onclick="deleteAdmin(${admin.user_id}, '${escapeHtml(admin.real_name)}')">删除</button> <button class="btn btn-sm btn-danger" onclick="deleteAdmin(${admin.user_id}, '${escapeHtml(admin.real_name)}')">删除</button>
</td> </td>
</tr>`; </tr>`;
@@ -227,6 +228,19 @@ async function deleteAdmin(userId, realName) {
} }
} }
async function resetAdminPassword(userId, realName) {
if (!confirm(`确定要重置管理员 "${realName}" 的密码吗?`)) {
return;
}
const res = await apiPost(`/api/admin/reset-password/${userId}`, {});
if (res && res.success) {
alert(`密码重置成功!\n管理员${realName}\n新密码${res.data.password}\n请妥善保管并及时通知该管理员。`);
} else {
showToast(res?.message || '密码重置失败', 'error');
}
}
function closeModal(modalId) { function closeModal(modalId) {
const modal = document.getElementById(modalId); const modal = document.getElementById(modalId);
if (modal) modal.style.display = 'none'; if (modal) modal.style.display = 'none';

View File

@@ -114,23 +114,39 @@ async function exportMoralityRecords() {
return; return;
} }
// 获取每个学生的历史记录 // 获取所有历史记录(不分页,获取全部)
const historyRes = await apiGet('/api/admin/conduct/history', { page: 1, page_size: 10000 });
if (!historyRes || !historyRes.success) {
showToast('获取历史记录失败', 'error');
return;
}
const allRecords = historyRes.data.records || [];
// 按学生ID分组历史记录
const recordsByStudent = {};
allRecords.forEach(record => {
const sid = record.student_id;
if (!recordsByStudent[sid]) {
recordsByStudent[sid] = [];
}
recordsByStudent[sid].push(record);
});
// 构建学生记录
const studentRecords = []; const studentRecords = [];
for (const student of students) { for (const student of students) {
const historyRes = await apiGet(`/api/student/conduct/${student.student_id}`, { limit: 1000 }); const studentRecords_list = recordsByStudent[student.student_id] || [];
if (historyRes && historyRes.success) { const positiveRecords = studentRecords_list.filter(r => r.points_change > 0).map(r => `${r.reason}(+${r.points_change})`);
const records = historyRes.data.records || []; const negativeRecords = studentRecords_list.filter(r => r.points_change < 0).map(r => `${r.reason}(${r.points_change})`);
const positiveRecords = records.filter(r => r.points_change > 0).map(r => `${r.reason}(${r.points_change > 0 ? '+' : ''}${r.points_change})`);
const negativeRecords = records.filter(r => r.points_change < 0).map(r => `${r.reason}(${r.points_change})`); studentRecords.push({
student_no: student.student_no,
studentRecords.push({ name: student.name,
student_no: student.student_no, total_points: student.total_points || 0,
name: student.name, positive_history: positiveRecords.join(''),
total_points: historyRes.data.total_points || 0, negative_history: negativeRecords.join('')
positive_history: positiveRecords.join(', '), });
negative_history: negativeRecords.join(', ')
});
}
} }
// 构建CSV内容 // 构建CSV内容
@@ -154,6 +170,7 @@ async function exportMoralityRecords() {
showToast(`导出成功,共${studentRecords.length}名学生`); showToast(`导出成功,共${studentRecords.length}名学生`);
} catch (err) { } catch (err) {
showToast('导出失败:' + err.message, 'error'); showToast('导出失败:' + err.message, 'error');
console.error('导出失败:', err);
} }
} }