Compare commits

...

2 Commits

Author SHA1 Message Date
En
b715c9a31c Merge remote-tracking branch 'origin/master' 2026-03-23 19:58:11 +08:00
En
8aaa8cdd94 feat: 操作日志模块同步方案
后端新增:
- 新增 LogModule 枚举类,统一管理操作日志模块
- 新增 LogOperationType 枚举类,统一管理操作类型
- 修改 @Log 注解,module 参数改为 LogModule 枚举类型
- 修改 LogAspect 切面,将枚举转换为字符串存储
- 新增 GET /api/v1/school/operation-logs/modules 接口

前端修改:
- 新增 logOperationType.ts 常量文件
- 修改 OperationLogView.vue,通过 API 动态获取模块列表
- 修改 school.ts,新增 getOperationLogModules API

数据库修改:
- OperationLog 实体新增 requestParams 字段,用于记录请求参数
- 新增 V48 迁移脚本,添加 request_params 字段

重构:
- 所有 Controller 中的 @Log 注解改为使用 LogModule 枚举

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 19:57:40 +08:00
53 changed files with 839 additions and 144 deletions

View File

@ -3,7 +3,8 @@
"allow": [
"Bash(mvn compile:*)",
"Bash(sed:*)",
"Bash(grep:*)"
"Bash(grep:*)",
"Bash(export:*)"
]
}
}

View File

@ -24,7 +24,7 @@ export interface TenantUpdateRequest {
logoUrl?: string;
/** 状态 */
status?: string;
/** 课程套餐ID用于三层架构 */
/** 课程套餐 ID用于三层架构 */
collectionIds?: number[];
/** 教师配额 */
teacherQuota?: number;
@ -34,4 +34,6 @@ export interface TenantUpdateRequest {
startDate?: string;
/** 结束日期 */
expireDate?: string;
/** 是否强制移除套餐(当套餐下有排课计划时需要此参数) */
forceRemove?: boolean;
}

View File

@ -875,29 +875,49 @@ export const getOperationLogs = (params?: {
action?: string;
startDate?: string;
endDate?: string;
}) => http.get<{ records: OperationLog[]; total: number; pageNum: number; pageSize: number; pages: number }>(
}) => http.get<{ list: any; total: number; pageNum: number; pageSize: number; pages: number }>(
'/v1/school/operation-logs',
{ params }
).then(res => ({
list: res.records || [],
total: res.total || 0,
pageNum: res.pageNum || 1,
pageSize: res.pageSize || 10,
pages: res.pages || 0,
}));
).then(res => {
// 字段映射:后端 userRole -> 前端 userType后端 details -> 前端 description
return {
list: (res.list || []).map(log => ({
...log,
userType: (log as any).userRole, // 后端字段 userRole 映射为前端 userType
description: (log as any).details, // 后端字段 details 映射为前端 description
oldValue: null, // 后端暂未支持
newValue: null, // 后端暂未支持
})),
total: res.total || 0,
pageNum: res.pageNum || 1,
pageSize: res.pageSize || 10,
pages: res.pages || 0,
};
});
export const getOperationLogStats = (startDate?: string, endDate?: string) =>
http.get<{ totalLogs: number; byModule: Record<string, number>; byOperator: Record<string, number> }>('/v1/school/operation-logs/stats', {
params: { startDate, endDate },
}).then(res => ({
totalLogs: res.totalLogs || 0,
byModule: res.byModule || {},
byOperator: res.byOperator || {},
}));
}).then(res => {
// 字段映射:后端 byModule -> 前端 modules 数组格式
return {
total: res.totalLogs || 0,
modules: Object.entries(res.byModule || {}).map(([name, count]) => ({ name, count })),
byModule: res.byModule || {},
byOperator: res.byOperator || {},
};
});
export const getOperationLogById = (id: number) =>
http.get<OperationLog>(`/v1/school/operation-logs/${id}`);
/**
*
*
*/
export const getOperationLogModules = () =>
http.get<string[]>('/v1/school/operation-logs/modules');
// ==================== 任务模板 API ====================
export interface TaskTemplate {

View File

@ -0,0 +1,21 @@
/**
* code
* LogOperationType
*/
export const LOG_OPERATION_TYPE_LABELS: Record<string, string> = {
CREATE: '新增',
UPDATE: '修改',
DELETE: '删除',
QUERY: '查询',
EXPORT: '导出',
IMPORT: '导入',
OTHER: '其他',
};
/**
*
*/
export const LOG_OPERATION_TYPE_OPTIONS = Object.entries(LOG_OPERATION_TYPE_LABELS).map(([code, label]) => ({
code,
label,
}));

View File

@ -467,7 +467,9 @@ const router = createRouter({
// 路由守卫
router.beforeEach((to, from, next) => {
const token = localStorage.getItem('token');
const userRole = localStorage.getItem('role');
const userRoleRaw = localStorage.getItem('role');
// role 转为小写以匹配路由
const userRole = userRoleRaw ? userRoleRaw.toLowerCase() : null;
// 设置页面标题
if (to.meta.title) {

View File

@ -490,10 +490,10 @@ export function translateResourceType(type: string): string {
// 通用状态映射(用于 Student、Teacher、Parent、Class 等实体的 active/ACTIVE 状态)
export const GENERIC_STATUS_MAP: Record<string, string> = {
ACTIVE: "激活",
active: "激活",
INACTIVE: "未激活",
inactive: "未激活",
ACTIVE: "启用",
active: "启用",
INACTIVE: "停用",
inactive: "停用",
ARCHIVED: "归档",
archived: "归档",
};
@ -503,8 +503,8 @@ export const GENERIC_STATUS_COLORS: Record<
string,
{ bg: string; text: string }
> = {
: { bg: "#E8F5E9", text: "#43A047" },
: { bg: "#F5F5F5", text: "#9E9E9E" },
: { bg: "#E8F5E9", text: "#43A047" },
: { bg: "#F5F5F5", text: "#9E9E9E" },
: { bg: "#FFF8E1", text: "#F9A825" },
};

View File

@ -0,0 +1,75 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
with open('TenantListView.vue', 'r', encoding='utf-8') as f:
content = f.read()
# 找到 handleModalOk 函数中的 catch 块并修改
old_catch = ''' } catch (error: any) {
message.error(error.response?.data?.message || '操作失败');
} finally {
modalLoading.value = false;
}
};'''
new_catch = ''' } catch (error: any) {
// 处理错误码 3102 - 套餐下有排课计划
if (error.response?.data?.code === 3102) {
const warnings = error.response.data.data as Array<{
collectionId: number;
collectionName: string;
scheduleCount: number;
}>;
// 保存当前表单数据
pendingFormData.value = {
...formData,
startDate,
expireDate,
};
// 显示强制移除确认弹窗
forceRemoveWarnings.value = warnings;
forceRemoveModalVisible.value = true;
modalLoading.value = false;
return;
}
message.error(error.response?.data?.message || '操作失败');
} finally {
modalLoading.value = false;
}
};
// 强制移除确认弹窗 - 确认
const handleForceRemoveConfirm = async () => {
if (!pendingFormData.value || !editingId.value) {
return;
}
modalLoading.value = true;
try {
// 传递 forceRemove: true 重新调用更新接口
await updateTenant(editingId.value, { ...pendingFormData.value, forceRemove: true } as UpdateTenantDto);
message.success('更新成功');
modalVisible.value = false;
forceRemoveModalVisible.value = false;
pendingFormData.value = null;
loadData();
} catch (error: any) {
message.error(error.response?.data?.message || '操作失败');
} finally {
modalLoading.value = false;
}
};
// 强制移除确认弹窗 - 取消
const handleForceRemoveCancel = () => {
forceRemoveModalVisible.value = false;
pendingFormData.value = null;
forceRemoveWarnings.value = [];
};'''
content = content.replace(old_catch, new_catch)
with open('TenantListView.vue', 'w', encoding='utf-8') as f:
f.write(content)
print('修改成功')

View File

@ -25,8 +25,8 @@
style="width: 150px"
@change="loadLogs"
>
<a-select-option v-for="action in actions" :key="action" :value="action">
{{ action }}
<a-select-option v-for="option in actions" :key="option.code" :value="option.code">
{{ option.label }}
</a-select-option>
</a-select>
<a-range-picker
@ -135,7 +135,8 @@ import { message } from 'ant-design-vue';
import type { TableProps } from 'ant-design-vue';
import dayjs, { type Dayjs } from 'dayjs';
import { SearchOutlined } from '@ant-design/icons-vue';
import { getOperationLogs, getOperationLogStats } from '@/api/school';
import { getOperationLogs, getOperationLogStats, getOperationLogModules } from '@/api/school';
import { LOG_OPERATION_TYPE_OPTIONS } from '@/constants/logOperationType';
interface OperationLog {
id: number;
@ -155,8 +156,8 @@ interface OperationLog {
//
const loading = ref(false);
const logs = ref<OperationLog[]>([]);
const modules = ref<string[]>(['排课管理', '教师管理', '学生管理', '班级管理', '课程管理']);
const actions = ref<string[]>(['创建', '更新', '删除', '创建排课', '批量创建排课', '取消排课']);
const modules = ref<string[]>([]);
const actions = ref(LOG_OPERATION_TYPE_OPTIONS);
//
const filters = reactive({
@ -215,6 +216,16 @@ const loadLogs = async () => {
}
};
//
const loadModules = async () => {
try {
const res = await getOperationLogModules();
modules.value = res;
} catch (error) {
console.error('加载模块列表失败', error);
}
};
//
const loadStats = async () => {
try {
@ -278,6 +289,7 @@ const getUserTypeColor = (type: string) => {
};
onMounted(() => {
loadModules();
loadLogs();
loadStats();
});

View File

@ -1,5 +1,8 @@
package com.reading.platform.common.annotation;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import java.lang.annotation.*;
/**
@ -22,7 +25,7 @@ public @interface Log {
* 例如用户管理课程管理学校管理等
* </p>
*/
String module() default "";
LogModule module() default LogModule.OTHER;
/**
* 操作类型
@ -30,7 +33,7 @@ public @interface Log {
* 例如新增修改删除查询导出等
* </p>
*/
String type() default "";
LogOperationType type() default LogOperationType.OTHER;
/**
* 操作描述
@ -49,54 +52,4 @@ public @interface Log {
* 默认true
*/
boolean recordParams() default true;
/**
* 操作状态枚举
*/
enum OperationType {
/**
* 新增
*/
CREATE("新增"),
/**
* 修改
*/
UPDATE("修改"),
/**
* 删除
*/
DELETE("删除"),
/**
* 查询
*/
QUERY("查询"),
/**
* 导出
*/
EXPORT("导出"),
/**
* 导入
*/
IMPORT("导入"),
/**
* 其他
*/
OTHER("其他");
private final String description;
OperationType(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
}

View File

@ -19,7 +19,6 @@ import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
/**
@ -59,8 +58,9 @@ public class LogAspect {
// 构建操作日志对象
OperationLog operationLog = new OperationLog();
operationLog.setId(IdWorker.getId());
operationLog.setModule(logAnnotation.module());
operationLog.setAction(logAnnotation.description());
operationLog.setModule(logAnnotation.module().toString());
operationLog.setAction(logAnnotation.type().getCode());
operationLog.setDetails(logAnnotation.description());
operationLog.setCreatedAt(LocalDateTime.now());
// 记录操作人信息
@ -76,6 +76,20 @@ public class LogAspect {
log.debug("获取当前用户信息失败:{}", e.getMessage());
}
// 设置租户 ID超管端使用 0其他端使用当前租户 ID
try {
Long tenantId = SecurityUtils.getCurrentTenantId();
if (tenantId != null) {
operationLog.setTenantId(tenantId);
} else {
// 超管端操作使用 0 作为标记
operationLog.setTenantId(0L);
}
} catch (Exception e) {
// 获取租户 ID 失败可能是超管端使用 0 作为标记
operationLog.setTenantId(0L);
}
// 记录请求信息
operationLog.setIpAddress(getIpAddress(request));
operationLog.setUserAgent(request.getHeader("User-Agent"));
@ -83,8 +97,11 @@ public class LogAspect {
// 记录请求参数
if (logAnnotation.recordParams()) {
try {
String params = JSON.toJSONString(getRequestParams(joinPoint));
operationLog.setDetails(params);
Object[] params = getRequestParams(joinPoint);
// 对参数进行脱敏处理移除敏感信息
Object[] sanitizedParams = desensitizeParams(joinPoint, params);
String paramsJson = JSON.toJSONString(sanitizedParams);
operationLog.setRequestParams(paramsJson);
} catch (Exception e) {
log.warn("记录请求参数失败:{}", e.getMessage());
}
@ -192,4 +209,68 @@ public class LogAspect {
private Object[] getRequestParams(JoinPoint joinPoint) {
return joinPoint.getArgs();
}
/**
* 对请求参数进行脱敏处理密码字段
* <p>
* 注意DTO 类中的密码字段使用 @JsonIgnore 注解序列化时会自动忽略
* 本方法主要处理 String 类型的直接参数 oldPassword, newPassword
* </p>
*
* @param joinPoint 切面连接点
* @param params 原始参数
* @return 脱敏后的参数
*/
private Object[] desensitizeParams(JoinPoint joinPoint, Object[] params) {
if (params == null || params.length == 0) {
return params;
}
Object[] sanitized = new Object[params.length];
for (int i = 0; i < params.length; i++) {
if (params[i] instanceof String strParam) {
// 检查参数名是否包含敏感词通过方法参数名判断
String paramName = getParameterName(joinPoint, i);
if (isSensitiveParam(paramName)) {
sanitized[i] = "***";
} else {
sanitized[i] = strParam;
}
} else {
// DTO 对象依赖 @JsonIgnore 注解自动脱敏
sanitized[i] = params[i];
}
}
return sanitized;
}
/**
* 获取参数名
*/
private String getParameterName(JoinPoint joinPoint, int paramIndex) {
try {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String[] paramNames = signature.getParameterNames();
if (paramNames != null && paramIndex < paramNames.length) {
return paramNames[paramIndex];
}
} catch (Exception e) {
log.debug("获取参数名失败:{}", e.getMessage());
}
return "";
}
/**
* 判断是否为敏感参数名
*/
private boolean isSensitiveParam(String paramName) {
if (paramName == null || paramName.isEmpty()) {
return false;
}
String lowerName = paramName.toLowerCase();
return lowerName.contains("password") ||
lowerName.contains("passwd") ||
lowerName.contains("secret") ||
lowerName.contains("token");
}
}

View File

@ -8,42 +8,44 @@ import lombok.Getter;
@Getter
public enum ErrorCode {
SUCCESS(200, "success"),
BAD_REQUEST(400, "Bad Request"),
UNAUTHORIZED(401, "Unauthorized"),
FORBIDDEN(403, "Forbidden"),
NOT_FOUND(404, "Resource Not Found"),
METHOD_NOT_ALLOWED(405, "Method Not Allowed"),
INTERNAL_ERROR(500, "Internal Server Error"),
SUCCESS(200, "成功"),
BAD_REQUEST(400, "请求失败"),
UNAUTHORIZED(401, "未授权"),
FORBIDDEN(403, "禁止访问"),
NOT_FOUND(404, "资源不存在"),
METHOD_NOT_ALLOWED(405, "方法不允许"),
INTERNAL_ERROR(500, "服务器内部错误"),
// Business Errors (1000+)
LOGIN_FAILED(1001, "Login failed"),
ACCOUNT_DISABLED(1002, "Account is disabled"),
TOKEN_EXPIRED(1003, "Token expired"),
TOKEN_INVALID(1004, "Token invalid"),
PERMISSION_DENIED(1005, "Permission denied"),
LOGIN_FAILED(1001, "登录失败"),
ACCOUNT_NOT_FOUND(1002, "账号不存在"),
ACCOUNT_DISABLED(1003, "账户已停用"),
INCORRECT_PASSWORD(1004, "密码错误"),
TOKEN_EXPIRED(1005, "Token 已过期"),
TOKEN_INVALID(1006, "Token 无效"),
PERMISSION_DENIED(1007, "权限不足"),
// Data Errors (2000+)
DATA_NOT_FOUND(2001, "Data not found"),
DATA_ALREADY_EXISTS(2002, "Data already exists"),
DATA_IN_USE(2003, "Data is in use"),
INVALID_PARAMETER(2004, "Invalid parameter"),
DATA_NOT_FOUND(2001, "数据不存在"),
DATA_ALREADY_EXISTS(2002, "数据已存在"),
DATA_IN_USE(2003, "数据使用中"),
INVALID_PARAMETER(2004, "参数无效"),
// Tenant Errors (3000+)
TENANT_NOT_FOUND(3001, "Tenant not found"),
TENANT_EXPIRED(3002, "Tenant has expired"),
TENANT_DISABLED(3003, "Tenant is disabled"),
TENANT_NOT_FOUND(3001, "租户不存在"),
TENANT_EXPIRED(3002, "租户已过期"),
TENANT_DISABLED(3003, "租户已停用"),
TENANT_SUSPENDED(3004, "您的账户因租户服务暂停而无法登录,请联系学校管理员"),
// Package Errors (3100+)
PACKAGE_NOT_FOUND(3101, "Package not found"),
REMOVE_PACKAGE_HAS_SCHEDULES(3102, "该套餐下有排课计划"),
PACKAGE_NOT_FOUND(3101, "套餐不存在"),
REMOVE_PACKAGE_HAS_SCHEDULES(3102, "该套餐下有排课计划,请确认是否强制移除"),
// User Errors (4000+)
USER_NOT_FOUND(4001, "User not found"),
USER_ALREADY_EXISTS(4002, "User already exists"),
PASSWORD_MISMATCH(4003, "Password mismatch"),
OLD_PASSWORD_ERROR(4004, "Old password is incorrect");
USER_NOT_FOUND(4001, "用户不存在"),
USER_ALREADY_EXISTS(4002, "用户已存在"),
PASSWORD_MISMATCH(4003, "密码不匹配"),
OLD_PASSWORD_ERROR(4004, "原密码错误");
private final Integer code;
private final String message;

View File

@ -9,8 +9,8 @@ import lombok.Getter;
@Getter
public enum GenericStatus {
ACTIVE("ACTIVE", "激活"),
INACTIVE("INACTIVE", "未激活"),
ACTIVE("ACTIVE", "启用"),
INACTIVE("INACTIVE", "停用"),
ARCHIVED("ARCHIVED", "归档");
private final String code;

View File

@ -0,0 +1,177 @@
package com.reading.platform.common.enums;
import lombok.Getter;
/**
* 日志操作模块枚举
* <p>
* 用于 @Log 注解的 module 参数赋值统一管理操作日志的模块名称
* </p>
*
* @author reading-platform
* @since 2026-03-23
*/
@Getter
public enum LogModule {
/**
* 认证管理
*/
AUTH("认证管理"),
/**
* 文件管理
*/
FILE("文件管理"),
/**
* 班级管理
*/
CLASS("班级管理"),
/**
* 教师管理
*/
TEACHER("教师管理"),
/**
* 学生管理
*/
STUDENT("学生管理"),
/**
* 家长管理
*/
PARENT("家长管理"),
/**
* 排课管理
*/
SCHEDULE("排课管理"),
/**
* 任务管理
*/
TASK("任务管理"),
/**
* 任务模板
*/
TASK_TEMPLATE("任务模板"),
/**
* 任务评价
*/
TASK_FEEDBACK("任务评价"),
/**
* 套餐管理
*/
PACKAGE("套餐管理"),
/**
* 成长记录
*/
GROWTH("成长记录"),
/**
* 数据导出
*/
EXPORT("数据导出"),
/**
* 通知管理
*/
NOTIFICATION("通知管理"),
/**
* 课程套餐管理
*/
COURSE_COLLECTION("课程套餐管理"),
/**
* 课程包管理
*/
COURSE_PACKAGE("课程包管理"),
/**
* 课程环节管理
*/
COURSE_LESSON("课程环节管理"),
/**
* 资源库管理
*/
COURSE_RESOURCE("资源库管理"),
/**
* 主题管理
*/
THEME("主题管理"),
/**
* 租户管理
*/
TENANT("租户管理"),
/**
* 系统设置
*/
SYSTEM_SETTING("系统设置"),
/**
* 课时管理
*/
LESSON("课时管理"),
/**
* 课时反馈
*/
LESSON_FEEDBACK("课时反馈"),
/**
* 学生记录
*/
STUDENT_RECORD("学生记录"),
/**
* 阿里云 IMM 服务
*/
IMM("阿里云 IMM 服务"),
/**
* 其他未分类的模块
*/
OTHER("其他");
private final String description;
LogModule(String description) {
this.description = description;
}
/**
* 重写 toString 方法返回描述文字
* 这样可以直接使用枚举值作为字符串存储到数据库
*/
@Override
public String toString() {
return this.description;
}
/**
* 根据 description 获取枚举
*
* @param description 模块描述
* @return 日志操作模块枚举
*/
public static LogModule fromDescription(String description) {
for (LogModule module : values()) {
if (module.description.equals(description)) {
return module;
}
}
// 找不到返回 null表示未知的模块
return null;
}
}

View File

@ -0,0 +1,74 @@
package com.reading.platform.common.enums;
import lombok.Getter;
/**
* 日志操作类型枚举
* <p>
* 用于 @Log 注解的 type 参数赋值
* </p>
*
* @author reading-platform
* @since 2026-03-23
*/
@Getter
public enum LogOperationType {
/**
* 新增
*/
CREATE("CREATE", "新增"),
/**
* 修改
*/
UPDATE("UPDATE", "修改"),
/**
* 删除
*/
DELETE("DELETE", "删除"),
/**
* 查询
*/
QUERY("QUERY", "查询"),
/**
* 导出
*/
EXPORT("EXPORT", "导出"),
/**
* 导入
*/
IMPORT("IMPORT", "导入"),
/**
* 其他
*/
OTHER("OTHER", "其他");
private final String code;
private final String description;
LogOperationType(String code, String description) {
this.code = code;
this.description = description;
}
/**
* 根据 code 获取枚举
*
* @param code 操作类型码
* @return 日志操作类型枚举
*/
public static LogOperationType fromCode(String code) {
for (LogOperationType type : values()) {
if (type.code.equals(code)) {
return type;
}
}
return OTHER;
}
}

View File

@ -22,8 +22,11 @@ public enum UserRole {
}
public static UserRole fromCode(String code) {
if (code == null) {
throw new IllegalArgumentException("Role code cannot be null");
}
for (UserRole role : values()) {
if (role.getCode().equals(code)) {
if (role.getCode().equalsIgnoreCase(code)) {
return role;
}
}

View File

@ -95,6 +95,7 @@ public class Result<T> implements Serializable {
return result;
}
/**
* 错误响应默认 500
*/

View File

@ -1,5 +1,8 @@
package com.reading.platform.controller;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.mapper.AdminUserMapper;
import com.reading.platform.common.mapper.ParentMapper;
import com.reading.platform.common.mapper.TenantMapper;
@ -37,12 +40,14 @@ public class AuthController {
private final AdminUserMapper adminUserMapper;
@Operation(summary = "用户登录")
@Log(module = LogModule.AUTH, type = LogOperationType.OTHER, description = "用户登录")
@PostMapping("/login")
public Result<LoginResponse> login(@Valid @RequestBody LoginRequest request) {
return Result.success(authService.login(request));
}
@Operation(summary = "用户登出")
@Log(module = LogModule.AUTH, type = LogOperationType.OTHER, description = "用户登出")
@PostMapping("/logout")
public Result<Void> logout() {
authService.logout();
@ -64,6 +69,7 @@ public class AuthController {
}
@Operation(summary = "修改密码")
@Log(module = LogModule.AUTH, type = LogOperationType.UPDATE, description = "修改密码", recordParams = false)
@PostMapping("/change-password")
public Result<Void> changePassword(
@RequestParam String oldPassword,
@ -75,6 +81,7 @@ public class AuthController {
}
@Operation(summary = "修改个人信息")
@Log(module = LogModule.AUTH, type = LogOperationType.UPDATE, description = "修改个人信息")
@PutMapping("/profile")
public Result<UpdateProfileResponse> updateProfile(
@Valid @RequestBody UpdateProfileRequest request) {

View File

@ -1,5 +1,8 @@
package com.reading.platform.controller;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.response.Result;
import com.reading.platform.common.util.OssUtils;
import com.reading.platform.dto.response.OssTokenVo;
@ -47,6 +50,7 @@ public class FileUploadController {
@PostMapping("/upload")
@Operation(summary = "上传文件")
@Log(module = LogModule.FILE, type = LogOperationType.CREATE, description = "上传文件")
public Result<Map<String, Object>> uploadFile(
@RequestParam("file") MultipartFile file,
@RequestParam(value = "type", defaultValue = "other") String type) {
@ -79,6 +83,7 @@ public class FileUploadController {
@DeleteMapping("/delete")
@Operation(summary = "删除文件")
@Log(module = LogModule.FILE, type = LogOperationType.DELETE, description = "删除文件")
public Result<Map<String, Object>> deleteFile(@RequestBody Map<String, String> request) {
String filePath = request.get("filePath");

View File

@ -1,5 +1,7 @@
package com.reading.platform.controller;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.response.Result;
import com.reading.platform.common.util.ImmUtil;
import com.reading.platform.dto.response.ImmTokenVo;

View File

@ -2,6 +2,9 @@ package com.reading.platform.controller.admin;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.annotation.RequireRole;
import com.reading.platform.common.enums.CourseStatus;
import com.reading.platform.common.enums.UserRole;
@ -59,6 +62,7 @@ public class AdminCourseCollectionController {
@PostMapping
@Operation(summary = "创建课程套餐")
@Log(module = LogModule.COURSE_COLLECTION, type = LogOperationType.CREATE, description = "创建课程套餐")
@RequireRole(UserRole.ADMIN)
public Result<CourseCollectionResponse> create(@Valid @RequestBody CreateCollectionRequest request) {
CourseCollectionResponse response = collectionService.createCollection(
@ -74,6 +78,7 @@ public class AdminCourseCollectionController {
@PutMapping("/{id}")
@Operation(summary = "更新课程套餐")
@Log(module = LogModule.COURSE_COLLECTION, type = LogOperationType.UPDATE, description = "更新课程套餐")
@RequireRole(UserRole.ADMIN)
public Result<CourseCollectionResponse> update(
@PathVariable Long id,
@ -92,6 +97,7 @@ public class AdminCourseCollectionController {
@DeleteMapping("/{id}")
@Operation(summary = "删除课程套餐")
@Log(module = LogModule.COURSE_COLLECTION, type = LogOperationType.DELETE, description = "删除课程套餐")
@RequireRole(UserRole.ADMIN)
public Result<Void> delete(@PathVariable Long id) {
collectionService.deleteCollection(id);
@ -100,6 +106,7 @@ public class AdminCourseCollectionController {
@PutMapping("/{id}/packages")
@Operation(summary = "设置套餐课程包")
@Log(module = LogModule.COURSE_COLLECTION, type = LogOperationType.UPDATE, description = "设置套餐课程包")
@RequireRole(UserRole.ADMIN)
public Result<Void> setPackages(
@PathVariable Long id,
@ -110,6 +117,7 @@ public class AdminCourseCollectionController {
@PostMapping("/{id}/publish")
@Operation(summary = "发布套餐")
@Log(module = LogModule.COURSE_COLLECTION, type = LogOperationType.UPDATE, description = "发布套餐")
@RequireRole(UserRole.ADMIN)
public Result<Void> publish(@PathVariable Long id) {
collectionService.publishCollection(id);
@ -118,6 +126,7 @@ public class AdminCourseCollectionController {
@PostMapping("/{id}/archive")
@Operation(summary = "下架套餐")
@Log(module = LogModule.COURSE_COLLECTION, type = LogOperationType.UPDATE, description = "下架套餐")
@RequireRole(UserRole.ADMIN)
public Result<Void> archive(@PathVariable Long id) {
collectionService.archiveCollection(id);
@ -126,6 +135,7 @@ public class AdminCourseCollectionController {
@PostMapping("/{id}/republish")
@Operation(summary = "重新发布套餐")
@Log(module = LogModule.COURSE_COLLECTION, type = LogOperationType.UPDATE, description = "重新发布套餐")
@RequireRole(UserRole.ADMIN)
public Result<Void> republish(@PathVariable Long id) {
collectionService.republishCollection(id);
@ -134,6 +144,7 @@ public class AdminCourseCollectionController {
@PostMapping("/{id}/withdraw")
@Operation(summary = "撤销审核")
@Log(module = LogModule.COURSE_COLLECTION, type = LogOperationType.UPDATE, description = "撤销审核")
@RequireRole(UserRole.ADMIN)
public Result<Void> withdraw(@PathVariable Long id) {
collectionService.withdrawCollection(id);
@ -142,6 +153,7 @@ public class AdminCourseCollectionController {
@PostMapping("/{id}/submit")
@Operation(summary = "提交审核")
@Log(module = LogModule.COURSE_COLLECTION, type = LogOperationType.UPDATE, description = "提交审核")
@RequireRole(UserRole.ADMIN)
public Result<Void> submit(@PathVariable Long id) {
collectionService.submitCollection(id);
@ -150,6 +162,7 @@ public class AdminCourseCollectionController {
@PostMapping("/{id}/reject")
@Operation(summary = "审核驳回")
@Log(module = LogModule.COURSE_COLLECTION, type = LogOperationType.UPDATE, description = "审核驳回")
@RequireRole(UserRole.ADMIN)
public Result<Void> reject(@PathVariable Long id, @Valid @RequestBody CourseCollectionRejectRequest request) {
collectionService.rejectCollection(id, request.getComment());

View File

@ -2,6 +2,9 @@ package com.reading.platform.controller.admin;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.annotation.RequireRole;
import com.reading.platform.common.enums.CourseStatus;
import com.reading.platform.common.enums.UserRole;
@ -39,6 +42,7 @@ public class AdminCourseController {
private final CoursePackageService courseService;
@PostMapping
@Log(module = LogModule.COURSE_PACKAGE, type = LogOperationType.CREATE, description = "创建课程包")
@Operation(summary = "创建课程包")
public Result<CourseResponse> createCourse(@Valid @RequestBody CourseCreateRequest request) {
log.info("收到课程包创建请求name={}, themeId={}, gradeTags={}", request.getName(), request.getThemeId(), request.getGradeTags());
@ -53,6 +57,7 @@ public class AdminCourseController {
}
@PutMapping("/{id}")
@Log(module = LogModule.COURSE_PACKAGE, type = LogOperationType.UPDATE, description = "更新课程包")
@Operation(summary = "更新课程包")
public Result<CourseResponse> updateCourse(@PathVariable Long id, @RequestBody CourseUpdateRequest request) {
courseService.updateCourse(id, request);
@ -97,6 +102,7 @@ public class AdminCourseController {
}
@DeleteMapping("/{id}")
@Log(module = LogModule.COURSE_PACKAGE, type = LogOperationType.DELETE, description = "删除课程包")
@Operation(summary = "删除课程包")
public Result<Void> deleteCourse(@PathVariable Long id) {
courseService.deleteCourse(id);
@ -104,6 +110,7 @@ public class AdminCourseController {
}
@PostMapping("/{id}/publish")
@Log(module = LogModule.COURSE_PACKAGE, type = LogOperationType.UPDATE, description = "发布课程包")
@Operation(summary = "发布课程包")
public Result<Void> publishCourse(@PathVariable Long id) {
courseService.publishCourse(id);
@ -111,6 +118,7 @@ public class AdminCourseController {
}
@PostMapping("/{id}/archive")
@Log(module = LogModule.COURSE_PACKAGE, type = LogOperationType.UPDATE, description = "归档课程包")
@Operation(summary = "归档课程包")
public Result<Void> archiveCourse(@PathVariable Long id) {
courseService.archiveCourse(id);
@ -118,6 +126,7 @@ public class AdminCourseController {
}
@PostMapping("/{id}/submit")
@Log(module = LogModule.COURSE_PACKAGE, type = LogOperationType.UPDATE, description = "提交课程包审核")
@Operation(summary = "提交课程包审核")
public Result<Void> submitCourse(@PathVariable Long id) {
courseService.submitCourse(id);
@ -125,6 +134,7 @@ public class AdminCourseController {
}
@PostMapping("/{id}/reject")
@Log(module = LogModule.COURSE_PACKAGE, type = LogOperationType.UPDATE, description = "驳回课程包审核")
@Operation(summary = "驳回课程包审核")
public Result<Void> rejectCourse(@PathVariable Long id, @Valid @RequestBody CourseRejectRequest request) {
courseService.rejectCourse(id, request.getComment());

View File

@ -1,5 +1,8 @@
package com.reading.platform.controller.admin;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.annotation.RequireRole;
import com.reading.platform.common.enums.UserRole;
import com.reading.platform.common.response.Result;
@ -63,6 +66,7 @@ public class AdminCourseLessonController {
@PostMapping("/{courseId}/lessons")
@Operation(summary = "创建课程环节")
@Log(module = LogModule.COURSE_LESSON, type = LogOperationType.CREATE, description = "创建课程环节")
@RequireRole(UserRole.ADMIN)
public Result<CourseLessonResponse> create(
@PathVariable Long courseId,
@ -91,6 +95,7 @@ public class AdminCourseLessonController {
@PutMapping("/{courseId}/lessons/{id}")
@Operation(summary = "更新课程环节")
@Log(module = LogModule.COURSE_LESSON, type = LogOperationType.UPDATE, description = "更新课程环节")
@RequireRole(UserRole.ADMIN)
public Result<CourseLessonResponse> update(
@PathVariable Long courseId,
@ -119,6 +124,7 @@ public class AdminCourseLessonController {
@DeleteMapping("/{courseId}/lessons/{id}")
@Operation(summary = "删除课程环节")
@Log(module = LogModule.COURSE_LESSON, type = LogOperationType.DELETE, description = "删除课程环节")
@RequireRole(UserRole.ADMIN)
public Result<Void> delete(
@PathVariable Long courseId,
@ -129,6 +135,7 @@ public class AdminCourseLessonController {
@PutMapping("/{courseId}/lessons/reorder")
@Operation(summary = "重新排序课程环节")
@Log(module = LogModule.COURSE_LESSON, type = LogOperationType.UPDATE, description = "重新排序课程环节")
@RequireRole(UserRole.ADMIN)
public Result<Void> reorder(
@PathVariable Long courseId,
@ -153,6 +160,7 @@ public class AdminCourseLessonController {
@PostMapping("/{courseId}/lessons/{lessonId}/steps")
@Operation(summary = "创建教学环节")
@Log(module = LogModule.COURSE_LESSON, type = LogOperationType.CREATE, description = "创建教学环节")
@RequireRole(UserRole.ADMIN)
public Result<LessonStepResponse> createStep(
@PathVariable Long courseId,
@ -171,6 +179,7 @@ public class AdminCourseLessonController {
@PutMapping("/{courseId}/lessons/steps/{stepId}")
@Operation(summary = "更新教学环节")
@Log(module = LogModule.COURSE_LESSON, type = LogOperationType.UPDATE, description = "更新教学环节")
@RequireRole(UserRole.ADMIN)
public Result<LessonStepResponse> updateStep(
@PathVariable Long courseId,
@ -189,6 +198,7 @@ public class AdminCourseLessonController {
@DeleteMapping("/{courseId}/lessons/steps/{stepId}")
@Operation(summary = "删除教学环节")
@Log(module = LogModule.COURSE_LESSON, type = LogOperationType.DELETE, description = "删除教学环节")
@RequireRole(UserRole.ADMIN)
public Result<Void> removeStep(
@PathVariable Long courseId,
@ -199,6 +209,7 @@ public class AdminCourseLessonController {
@PutMapping("/{courseId}/lessons/{lessonId}/steps/reorder")
@Operation(summary = "重新排序教学环节")
@Log(module = LogModule.COURSE_LESSON, type = LogOperationType.UPDATE, description = "重新排序教学环节")
@RequireRole(UserRole.ADMIN)
public Result<Void> reorderSteps(
@PathVariable Long courseId,

View File

@ -1,6 +1,9 @@
package com.reading.platform.controller.admin;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.annotation.RequireRole;
import com.reading.platform.common.enums.UserRole;
import com.reading.platform.common.response.PageResult;
@ -65,6 +68,7 @@ public class AdminResourceController {
@PostMapping("/libraries")
@Operation(summary = "创建资源库")
@Log(module = LogModule.COURSE_RESOURCE, type = LogOperationType.CREATE, description = "创建资源库")
@RequireRole(UserRole.ADMIN)
public Result<ResourceLibraryResponse> createLibrary(@Valid @RequestBody ResourceLibraryCreateRequest request) {
ResourceLibrary library = resourceLibraryService.createLibrary(
@ -78,6 +82,7 @@ public class AdminResourceController {
@PutMapping("/libraries/{id}")
@Operation(summary = "更新资源库")
@Log(module = LogModule.COURSE_RESOURCE, type = LogOperationType.UPDATE, description = "更新资源库")
@RequireRole(UserRole.ADMIN)
public Result<ResourceLibraryResponse> updateLibrary(
@PathVariable String id,
@ -92,6 +97,7 @@ public class AdminResourceController {
@DeleteMapping("/libraries/{id}")
@Operation(summary = "删除资源库")
@Log(module = LogModule.COURSE_RESOURCE, type = LogOperationType.DELETE, description = "删除资源库")
@RequireRole(UserRole.ADMIN)
public Result<Void> deleteLibrary(@PathVariable String id) {
resourceLibraryService.deleteLibrary(id);
@ -126,6 +132,7 @@ public class AdminResourceController {
@PostMapping("/items")
@Operation(summary = "创建资源项目")
@Log(module = LogModule.COURSE_RESOURCE, type = LogOperationType.CREATE, description = "创建资源项目")
@RequireRole(UserRole.ADMIN)
public Result<ResourceItemResponse> createItem(@Valid @RequestBody ResourceItemCreateRequest request) throws JsonProcessingException {
String tagsStr = null;
@ -147,6 +154,7 @@ public class AdminResourceController {
@PutMapping("/items/{id}")
@Operation(summary = "更新资源项目")
@Log(module = LogModule.COURSE_RESOURCE, type = LogOperationType.UPDATE, description = "更新资源项目")
@RequireRole(UserRole.ADMIN)
public Result<ResourceItemResponse> updateItem(
@PathVariable String id,
@ -166,6 +174,7 @@ public class AdminResourceController {
@DeleteMapping("/items/{id}")
@Operation(summary = "删除资源项目")
@Log(module = LogModule.COURSE_RESOURCE, type = LogOperationType.DELETE, description = "删除资源项目")
@RequireRole(UserRole.ADMIN)
public Result<Void> deleteItem(@PathVariable String id) {
resourceLibraryService.deleteItem(id);
@ -174,6 +183,7 @@ public class AdminResourceController {
@PostMapping("/items/batch-delete")
@Operation(summary = "批量删除资源项目")
@Log(module = LogModule.COURSE_RESOURCE, type = LogOperationType.DELETE, description = "批量删除资源项目")
@RequireRole(UserRole.ADMIN)
public Result<Void> batchDeleteItems(@RequestBody List<String> ids) {
resourceLibraryService.batchDeleteItems(ids);

View File

@ -1,6 +1,9 @@
package com.reading.platform.controller.admin;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.annotation.RequireRole;
import com.reading.platform.common.enums.UserRole;
import com.reading.platform.common.response.PageResult;
@ -39,6 +42,7 @@ public class AdminTenantController {
private final CourseCollectionMapper collectionMapper;
@Operation(summary = "Create tenant")
@Log(module = LogModule.TENANT, type = LogOperationType.CREATE, description = "创建租户")
@PostMapping
public Result<TenantResponse> createTenant(@Valid @RequestBody TenantCreateRequest request) {
Tenant tenant = tenantService.createTenant(request);
@ -46,6 +50,7 @@ public class AdminTenantController {
}
@Operation(summary = "Update tenant")
@Log(module = LogModule.TENANT, type = LogOperationType.UPDATE, description = "修改租户")
@PutMapping("/{id}")
public Result<TenantResponse> updateTenant(@PathVariable Long id, @RequestBody TenantUpdateRequest request) {
Tenant tenant = tenantService.updateTenant(id, request);
@ -78,6 +83,7 @@ public class AdminTenantController {
}
@Operation(summary = "Delete tenant")
@Log(module = LogModule.TENANT, type = LogOperationType.DELETE, description = "删除租户")
@DeleteMapping("/{id}")
public Result<Void> deleteTenant(@PathVariable Long id) {
tenantService.deleteTenant(id);
@ -105,6 +111,7 @@ public class AdminTenantController {
}
@Operation(summary = "更新租户配额")
@Log(module = LogModule.TENANT, type = LogOperationType.UPDATE, description = "更新租户配额")
@PutMapping("/{id}/quota")
public Result<TenantResponse> updateTenantQuota(
@PathVariable Long id,
@ -118,6 +125,7 @@ public class AdminTenantController {
}
@Operation(summary = "更新租户状态")
@Log(module = LogModule.TENANT, type = LogOperationType.UPDATE, description = "更新租户状态")
@PutMapping("/{id}/status")
public Result<TenantResponse> updateTenantStatus(@PathVariable Long id, @RequestBody Map<String, Object> status) {
String newStatus = (String) status.get("status");
@ -127,6 +135,7 @@ public class AdminTenantController {
}
@Operation(summary = "重置租户密码")
@Log(module = LogModule.TENANT, type = LogOperationType.UPDATE, description = "重置租户密码")
@PostMapping("/{id}/reset-password")
public Result<Map<String, String>> resetTenantPassword(@PathVariable Long id) {
String tempPassword = tenantService.resetPasswordAndReturnTemp(id);

View File

@ -1,5 +1,8 @@
package com.reading.platform.controller.admin;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.annotation.RequireRole;
import com.reading.platform.common.enums.UserRole;
import com.reading.platform.common.response.Result;
@ -46,6 +49,7 @@ public class AdminThemeController {
@PostMapping
@Operation(summary = "创建主题")
@Log(module = LogModule.THEME, type = LogOperationType.CREATE, description = "创建主题")
@RequireRole(UserRole.ADMIN)
public Result<ThemeResponse> create(@Valid @RequestBody ThemeCreateRequest request) {
Theme theme = themeService.create(
@ -58,6 +62,7 @@ public class AdminThemeController {
@PutMapping("/{id}")
@Operation(summary = "更新主题")
@Log(module = LogModule.THEME, type = LogOperationType.UPDATE, description = "更新主题")
@RequireRole(UserRole.ADMIN)
public Result<ThemeResponse> update(
@PathVariable Long id,
@ -74,6 +79,7 @@ public class AdminThemeController {
@DeleteMapping("/{id}")
@Operation(summary = "删除主题")
@Log(module = LogModule.THEME, type = LogOperationType.DELETE, description = "删除主题")
@RequireRole(UserRole.ADMIN)
public Result<Void> delete(@PathVariable Long id) {
themeService.delete(id);
@ -82,6 +88,7 @@ public class AdminThemeController {
@PutMapping("/reorder")
@Operation(summary = "重新排序主题")
@Log(module = LogModule.THEME, type = LogOperationType.UPDATE, description = "重新排序主题")
@RequireRole(UserRole.ADMIN)
public Result<Void> reorder(@RequestBody List<Long> ids) {
themeService.reorder(ids);

View File

@ -6,6 +6,9 @@ import com.reading.platform.common.response.Result;
import com.reading.platform.common.security.SecurityUtils;
import com.reading.platform.dto.response.NotificationForParentResponse;
import com.reading.platform.entity.Notification;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.annotation.RequireRole;
import com.reading.platform.common.enums.UserRole;
import com.reading.platform.service.NotificationService;
@ -61,6 +64,7 @@ public class ParentNotificationController {
}
@Operation(summary = "Mark notification as read")
@Log(module = LogModule.NOTIFICATION, type = LogOperationType.UPDATE, description = "标记通知为已读")
@PostMapping("/{id}/read")
public Result<Void> markAsRead(@PathVariable Long id) {
notificationService.markAsRead(id);
@ -68,6 +72,7 @@ public class ParentNotificationController {
}
@Operation(summary = "Mark all notifications as read")
@Log(module = LogModule.NOTIFICATION, type = LogOperationType.UPDATE, description = "标记所有通知为已读")
@PostMapping("/read-all")
public Result<Void> markAllAsRead() {
Long userId = SecurityUtils.getCurrentUserId();

View File

@ -11,6 +11,9 @@ import com.reading.platform.dto.response.TaskFeedbackResponse;
import com.reading.platform.dto.response.TaskResponse;
import com.reading.platform.dto.response.TaskWithCompletionResponse;
import com.reading.platform.entity.Task;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.annotation.RequireRole;
import com.reading.platform.common.enums.ErrorCode;
import com.reading.platform.common.enums.UserRole;
@ -72,6 +75,7 @@ public class ParentTaskController {
// =============== 提交与评价 API ===============
@Operation(summary = "提交任务完成")
@Log(module = LogModule.TASK, type = LogOperationType.CREATE, description = "提交任务完成")
@PostMapping("/{taskId}/submit")
public Result<TaskCompletionDetailResponse> submitTask(
@PathVariable Long taskId,
@ -84,6 +88,7 @@ public class ParentTaskController {
}
@Operation(summary = "修改任务提交")
@Log(module = LogModule.TASK, type = LogOperationType.UPDATE, description = "修改任务提交")
@PutMapping("/{taskId}/submit")
public Result<TaskCompletionDetailResponse> updateSubmission(
@PathVariable Long taskId,
@ -117,6 +122,7 @@ public class ParentTaskController {
// =============== 兼容旧接口 ===============
@Operation(summary = "完成任务(旧接口,兼容使用,支持 JSON body")
@Log(module = LogModule.TASK, type = LogOperationType.UPDATE, description = "完成任务")
@PostMapping("/{id}/complete")
public Result<Void> completeTask(
@PathVariable Long id,

View File

@ -2,6 +2,9 @@ package com.reading.platform.controller.school;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.mapper.ClassMapper;
import com.reading.platform.common.response.PageResult;
import com.reading.platform.common.response.Result;
@ -46,6 +49,7 @@ public class SchoolClassController {
private final LessonMapper lessonMapper;
@Operation(summary = "Create class")
@Log(module = LogModule.CLASS, type = LogOperationType.CREATE, description = "创建新班级")
@PostMapping
public Result<ClassResponse> createClass(@Valid @RequestBody ClassCreateRequest request) {
Long tenantId = SecurityUtils.getCurrentTenantId();
@ -54,6 +58,7 @@ public class SchoolClassController {
}
@Operation(summary = "Update class")
@Log(module = LogModule.CLASS, type = LogOperationType.UPDATE, description = "修改班级信息")
@PutMapping("/{id}")
public Result<ClassResponse> updateClass(@PathVariable Long id, @RequestBody ClassUpdateRequest request) {
Long tenantId = SecurityUtils.getCurrentTenantId();
@ -111,6 +116,7 @@ public class SchoolClassController {
}
@Operation(summary = "Delete class")
@Log(module = LogModule.CLASS, type = LogOperationType.DELETE, description = "删除班级")
@DeleteMapping("/{id}")
public Result<Void> deleteClass(@PathVariable Long id) {
Long tenantId = SecurityUtils.getCurrentTenantId();
@ -119,6 +125,7 @@ public class SchoolClassController {
}
@Operation(summary = "Assign teachers to class")
@Log(module = LogModule.CLASS, type = LogOperationType.UPDATE, description = "分配教师到班级")
@PostMapping("/{id}/teachers")
public Result<Void> assignTeachers(@PathVariable Long id, @RequestBody List<Long> teacherIds) {
Long tenantId = SecurityUtils.getCurrentTenantId();
@ -127,6 +134,7 @@ public class SchoolClassController {
}
@Operation(summary = "Assign students to class")
@Log(module = LogModule.CLASS, type = LogOperationType.UPDATE, description = "分配学生到班级")
@PostMapping("/{id}/students")
public Result<Void> assignStudents(@PathVariable Long id, @RequestBody List<Long> studentIds) {
Long tenantId = SecurityUtils.getCurrentTenantId();
@ -172,6 +180,7 @@ public class SchoolClassController {
}
@Operation(summary = "Update class teacher role")
@Log(module = LogModule.CLASS, type = LogOperationType.UPDATE, description = "更新班级教师角色")
@PutMapping("/{id}/teachers/{teacherId}")
public Result<Void> updateClassTeacher(
@PathVariable Long id,
@ -185,6 +194,7 @@ public class SchoolClassController {
}
@Operation(summary = "Remove teacher from class")
@Log(module = LogModule.CLASS, type = LogOperationType.DELETE, description = "从班级移除教师")
@DeleteMapping("/{id}/teachers/{teacherId}")
public Result<Void> removeClassTeacher(
@PathVariable Long id,

View File

@ -1,5 +1,8 @@
package com.reading.platform.controller.school;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.annotation.RequireRole;
import com.reading.platform.common.enums.UserRole;
import com.reading.platform.common.security.SecurityUtils;
@ -36,6 +39,7 @@ public class SchoolExportController {
*/
@GetMapping("/lessons")
@Operation(summary = "导出授课记录")
@Log(module = LogModule.EXPORT, type = LogOperationType.EXPORT, description = "导出授课记录")
public void exportLessons(
@RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate,
@ -60,6 +64,7 @@ public class SchoolExportController {
*/
@GetMapping("/teacher-stats")
@Operation(summary = "导出教师绩效统计")
@Log(module = LogModule.EXPORT, type = LogOperationType.EXPORT, description = "导出教师绩效统计")
public void exportTeacherStats(
@RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate,
@ -83,6 +88,7 @@ public class SchoolExportController {
*/
@GetMapping("/student-stats")
@Operation(summary = "导出学生统计")
@Log(module = LogModule.EXPORT, type = LogOperationType.EXPORT, description = "导出学生统计")
public void exportStudentStats(
@RequestParam(required = false) Long classId,
HttpServletResponse response) throws IOException {

View File

@ -1,6 +1,9 @@
package com.reading.platform.controller.school;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.mapper.GrowthRecordMapper;
import com.reading.platform.common.response.PageResult;
import com.reading.platform.common.response.Result;
@ -28,6 +31,7 @@ public class SchoolGrowthController {
private final GrowthRecordMapper growthRecordMapper;
@Operation(summary = "Create growth record")
@Log(module = LogModule.GROWTH, type = LogOperationType.CREATE, description = "创建成长记录")
@PostMapping
public Result<GrowthRecordResponse> createGrowthRecord(@Valid @RequestBody GrowthRecordCreateRequest request) {
Long tenantId = SecurityUtils.getCurrentTenantId();
@ -38,6 +42,7 @@ public class SchoolGrowthController {
}
@Operation(summary = "Update growth record")
@Log(module = LogModule.GROWTH, type = LogOperationType.UPDATE, description = "修改成长记录")
@PutMapping("/{id}")
public Result<GrowthRecordResponse> updateGrowthRecord(@PathVariable Long id, @RequestBody GrowthRecordUpdateRequest request) {
Long tenantId = SecurityUtils.getCurrentTenantId();
@ -67,6 +72,7 @@ public class SchoolGrowthController {
}
@Operation(summary = "Delete growth record")
@Log(module = LogModule.GROWTH, type = LogOperationType.DELETE, description = "删除成长记录")
@DeleteMapping("/{id}")
public Result<Void> deleteGrowthRecord(@PathVariable Long id) {
Long tenantId = SecurityUtils.getCurrentTenantId();

View File

@ -2,6 +2,7 @@ package com.reading.platform.controller.school;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reading.platform.common.annotation.RequireRole;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.UserRole;
import com.reading.platform.common.response.PageResult;
import com.reading.platform.common.response.Result;
@ -14,6 +15,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@ -36,14 +38,14 @@ public class SchoolOperationLogController {
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
@RequestParam(required = false) String module,
@RequestParam(required = false) String operator) {
@RequestParam(required = false) String action) {
// 获取当前租户 ID
Long tenantId = SecurityUtils.getCurrentTenantId();
// 调用 Service 层查询
Page<OperationLog> page = operationLogService.getLogPage(
tenantId, pageNum, pageSize, module, operator
tenantId, pageNum, pageSize, module, action
);
// Entity 转为 Response
@ -80,6 +82,16 @@ public class SchoolOperationLogController {
return Result.success(convertToResponse(log));
}
@GetMapping("/modules")
@Operation(summary = "获取可用模块列表")
public Result<List<String>> getAvailableModules() {
// 从枚举获取所有模块描述
List<String> modules = Arrays.stream(LogModule.values())
.map(LogModule::toString)
.collect(Collectors.toList());
return Result.success(modules);
}
/**
* Entity Response
*/

View File

@ -1,5 +1,8 @@
package com.reading.platform.controller.school;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.annotation.RequireRole;
import com.reading.platform.common.enums.UserRole;
import com.reading.platform.common.response.Result;
@ -65,6 +68,7 @@ public class SchoolPackageController {
@PostMapping("/{collectionId}/renew")
@Operation(summary = "续费课程套餐")
@Log(module = LogModule.PACKAGE, type = LogOperationType.UPDATE, description = "续费课程套餐")
@RequireRole(UserRole.SCHOOL)
public Result<Void> renewCollection(
@PathVariable Long collectionId,

View File

@ -2,6 +2,9 @@ package com.reading.platform.controller.school;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.mapper.ParentMapper;
import com.reading.platform.mapper.ParentStudentMapper;
import com.reading.platform.common.response.PageResult;
@ -36,6 +39,7 @@ public class SchoolParentController {
private final ParentStudentMapper parentStudentMapper;
@Operation(summary = "Create parent")
@Log(module = LogModule.PARENT, type = LogOperationType.CREATE, description = "创建新家长")
@PostMapping
public Result<ParentResponse> createParent(@Valid @RequestBody ParentCreateRequest request) {
Long tenantId = SecurityUtils.getCurrentTenantId();
@ -44,6 +48,7 @@ public class SchoolParentController {
}
@Operation(summary = "Update parent")
@Log(module = LogModule.PARENT, type = LogOperationType.UPDATE, description = "修改家长信息")
@PutMapping("/{id}")
public Result<ParentResponse> updateParent(@PathVariable Long id, @RequestBody ParentUpdateRequest request) {
Long tenantId = SecurityUtils.getCurrentTenantId();
@ -79,6 +84,7 @@ public class SchoolParentController {
}
@Operation(summary = "Delete parent")
@Log(module = LogModule.PARENT, type = LogOperationType.DELETE, description = "删除家长")
@DeleteMapping("/{id}")
public Result<Void> deleteParent(@PathVariable Long id) {
Long tenantId = SecurityUtils.getCurrentTenantId();
@ -87,6 +93,7 @@ public class SchoolParentController {
}
@Operation(summary = "Reset parent password")
@Log(module = LogModule.PARENT, type = LogOperationType.UPDATE, description = "重置家长密码")
@PostMapping("/{id}/reset-password")
public Result<java.util.Map<String, String>> resetPassword(@PathVariable Long id) {
Long tenantId = SecurityUtils.getCurrentTenantId();
@ -95,6 +102,7 @@ public class SchoolParentController {
}
@Operation(summary = "Bind student to parent")
@Log(module = LogModule.PARENT, type = LogOperationType.CREATE, description = "绑定学生到家长")
@PostMapping("/{parentId}/students/{studentId}")
public Result<Void> bindStudent(
@PathVariable Long parentId,
@ -107,6 +115,7 @@ public class SchoolParentController {
}
@Operation(summary = "Add child to parent (alias for bind student)")
@Log(module = LogModule.PARENT, type = LogOperationType.CREATE, description = "添加孩子到家长")
@PostMapping("/{parentId}/children/{studentId}")
public Result<Void> addChild(
@PathVariable Long parentId,
@ -130,6 +139,7 @@ public class SchoolParentController {
}
@Operation(summary = "Unbind student from parent")
@Log(module = LogModule.PARENT, type = LogOperationType.DELETE, description = "从家长解绑学生")
@DeleteMapping("/{parentId}/students/{studentId}")
public Result<Void> unbindStudent(@PathVariable Long parentId, @PathVariable Long studentId) {
Long tenantId = SecurityUtils.getCurrentTenantId();
@ -138,6 +148,7 @@ public class SchoolParentController {
}
@Operation(summary = "Remove child from parent (alias for unbind student)")
@Log(module = LogModule.PARENT, type = LogOperationType.DELETE, description = "从家长移除孩子")
@DeleteMapping("/{parentId}/children/{studentId}")
public Result<Void> removeChild(@PathVariable Long parentId, @PathVariable Long studentId) {
Long tenantId = SecurityUtils.getCurrentTenantId();

View File

@ -1,6 +1,9 @@
package com.reading.platform.controller.school;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.annotation.RequireRole;
import com.reading.platform.common.enums.UserRole;
import com.reading.platform.common.response.PageResult;
@ -82,6 +85,7 @@ public class SchoolScheduleController {
@PostMapping
@Operation(summary = "创建排课")
@Log(module = LogModule.SCHEDULE, type = LogOperationType.CREATE, description = "创建排课计划")
public Result<List<SchedulePlanResponse>> createSchedule(@Valid @RequestBody SchedulePlanCreateRequest request) {
Long tenantId = SecurityUtils.getCurrentTenantId();
List<SchedulePlan> plans = schoolScheduleService.createSchedule(tenantId, request);
@ -93,6 +97,7 @@ public class SchoolScheduleController {
@PutMapping("/{id}")
@Operation(summary = "更新排课")
@Log(module = LogModule.SCHEDULE, type = LogOperationType.UPDATE, description = "更新排课计划")
public Result<SchedulePlanResponse> updateSchedule(
@PathVariable Long id,
@RequestBody SchedulePlanUpdateRequest request) {
@ -104,6 +109,7 @@ public class SchoolScheduleController {
@DeleteMapping("/{id}")
@Operation(summary = "取消排课")
@Log(module = LogModule.SCHEDULE, type = LogOperationType.DELETE, description = "取消排课计划")
public Result<Void> cancelSchedule(@PathVariable Long id) {
Long tenantId = SecurityUtils.getCurrentTenantId();
schoolScheduleService.cancelSchedule(id, tenantId);
@ -112,6 +118,7 @@ public class SchoolScheduleController {
@PostMapping("/batch")
@Operation(summary = "批量创建排课")
@Log(module = LogModule.SCHEDULE, type = LogOperationType.CREATE, description = "批量创建排课计划")
public Result<List<SchedulePlanResponse>> batchCreateSchedules(
@RequestBody List<@Valid SchedulePlanCreateRequest> requests) {
@ -147,6 +154,7 @@ public class SchoolScheduleController {
@PostMapping("/batch-by-classes")
@Operation(summary = "批量创建排课(按班级)")
@Log(module = LogModule.SCHEDULE, type = LogOperationType.CREATE, description = "按班级批量创建排课")
public Result<List<SchedulePlanResponse>> createSchedulesByClasses(
@Valid @RequestBody ScheduleCreateByClassesRequest request) {
Long tenantId = SecurityUtils.getCurrentTenantId();

View File

@ -1,5 +1,8 @@
package com.reading.platform.controller.school;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.annotation.RequireRole;
import com.reading.platform.common.enums.UserRole;
import com.reading.platform.common.response.Result;
@ -41,6 +44,7 @@ public class SchoolSettingsController {
@PutMapping
@Operation(summary = "更新系统设置")
@Log(module = LogModule.SYSTEM_SETTING, type = LogOperationType.UPDATE, description = "更新系统设置")
public Result<SchoolSettingsResponse> updateSettings(@RequestBody SchoolSettingsUpdateRequest request) {
Long tenantId = SecurityUtils.getCurrentTenantId();
// TODO: 实现系统设置更新根据 tenantId 更新
@ -57,6 +61,7 @@ public class SchoolSettingsController {
@PutMapping("/basic")
@Operation(summary = "更新基础设置")
@Log(module = LogModule.SYSTEM_SETTING, type = LogOperationType.UPDATE, description = "更新基础设置")
public Result<BasicSettingsResponse> updateBasicSettings(@RequestBody BasicSettingsUpdateRequest request) {
Long tenantId = SecurityUtils.getCurrentTenantId();
// TODO: 实现基础设置更新根据 tenantId 更新
@ -73,6 +78,7 @@ public class SchoolSettingsController {
@PutMapping("/notification")
@Operation(summary = "更新通知设置")
@Log(module = LogModule.SYSTEM_SETTING, type = LogOperationType.UPDATE, description = "更新通知设置")
public Result<NotificationSettingsResponse> updateNotificationSettings(@RequestBody NotificationSettingsUpdateRequest request) {
Long tenantId = SecurityUtils.getCurrentTenantId();
// TODO: 实现通知设置更新根据 tenantId 更新
@ -89,6 +95,7 @@ public class SchoolSettingsController {
@PutMapping("/security")
@Operation(summary = "更新安全设置")
@Log(module = LogModule.SYSTEM_SETTING, type = LogOperationType.UPDATE, description = "更新安全设置")
public Result<SecuritySettingsResponse> updateSecuritySettings(@RequestBody SecuritySettingsUpdateRequest request) {
Long tenantId = SecurityUtils.getCurrentTenantId();
// TODO: 实现安全设置更新根据 tenantId 更新

View File

@ -1,6 +1,9 @@
package com.reading.platform.controller.school;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.mapper.StudentMapper;
import com.reading.platform.common.response.PageResult;
import com.reading.platform.common.response.Result;
@ -33,6 +36,7 @@ public class SchoolStudentController {
private final StudentMapper studentMapper;
@Operation(summary = "Create student")
@Log(module = LogModule.STUDENT, type = LogOperationType.CREATE, description = "创建学生")
@PostMapping
public Result<StudentResponse> createStudent(@Valid @RequestBody StudentCreateRequest request) {
Long tenantId = SecurityUtils.getCurrentTenantId();
@ -43,6 +47,7 @@ public class SchoolStudentController {
}
@Operation(summary = "Update student")
@Log(module = LogModule.STUDENT, type = LogOperationType.UPDATE, description = "修改学生")
@PutMapping("/{id}")
public Result<StudentResponse> updateStudent(@PathVariable Long id, @RequestBody StudentUpdateRequest request) {
Long tenantId = SecurityUtils.getCurrentTenantId();
@ -92,6 +97,7 @@ public class SchoolStudentController {
}
@Operation(summary = "Delete student")
@Log(module = LogModule.STUDENT, type = LogOperationType.DELETE, description = "删除学生")
@DeleteMapping("/{id}")
public Result<Void> deleteStudent(@PathVariable Long id) {
Long tenantId = SecurityUtils.getCurrentTenantId();

View File

@ -1,6 +1,9 @@
package com.reading.platform.controller.school;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.annotation.RequireRole;
import com.reading.platform.common.enums.UserRole;
import com.reading.platform.common.mapper.TaskTemplateMapper;
@ -70,6 +73,7 @@ public class SchoolTaskTemplateController {
@PostMapping
@Operation(summary = "创建模板")
@Log(module = LogModule.TASK_TEMPLATE, type = LogOperationType.CREATE, description = "创建任务模板")
public Result<TaskTemplateResponse> createTemplate(@Valid @RequestBody TaskTemplateCreateRequest request) {
Long tenantId = SecurityUtils.getCurrentTenantId();
Long userId = SecurityUtils.getCurrentUserId();
@ -79,6 +83,7 @@ public class SchoolTaskTemplateController {
@PutMapping("/{id}")
@Operation(summary = "更新模板")
@Log(module = LogModule.TASK_TEMPLATE, type = LogOperationType.UPDATE, description = "更新任务模板")
public Result<TaskTemplateResponse> updateTemplate(
@PathVariable Long id,
@RequestBody TaskTemplateCreateRequest request) {
@ -94,6 +99,7 @@ public class SchoolTaskTemplateController {
@DeleteMapping("/{id}")
@Operation(summary = "删除模板")
@Log(module = LogModule.TASK_TEMPLATE, type = LogOperationType.DELETE, description = "删除任务模板")
public Result<Void> deleteTemplate(@PathVariable Long id) {
Long tenantId = SecurityUtils.getCurrentTenantId();
TaskTemplate template = taskTemplateService.getTemplateById(id);

View File

@ -1,6 +1,9 @@
package com.reading.platform.controller.school;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.response.PageResult;
import com.reading.platform.common.response.Result;
import com.reading.platform.common.security.SecurityUtils;
@ -26,6 +29,7 @@ public class SchoolTeacherController {
private final TeacherService teacherService;
@Operation(summary = "Create teacher")
@Log(module = LogModule.TEACHER, type = LogOperationType.CREATE, description = "创建新教师")
@PostMapping
public Result<TeacherResponse> createTeacher(@Valid @RequestBody TeacherCreateRequest request) {
Long tenantId = SecurityUtils.getCurrentTenantId();
@ -34,6 +38,7 @@ public class SchoolTeacherController {
}
@Operation(summary = "Update teacher")
@Log(module = LogModule.TEACHER, type = LogOperationType.UPDATE, description = "修改教师信息")
@PutMapping("/{id}")
public Result<TeacherResponse> updateTeacher(@PathVariable Long id, @RequestBody TeacherUpdateRequest request) {
Long tenantId = SecurityUtils.getCurrentTenantId();
@ -63,6 +68,7 @@ public class SchoolTeacherController {
}
@Operation(summary = "Delete teacher")
@Log(module = LogModule.TEACHER, type = LogOperationType.DELETE, description = "删除教师")
@DeleteMapping("/{id}")
public Result<Void> deleteTeacher(@PathVariable Long id) {
Long tenantId = SecurityUtils.getCurrentTenantId();
@ -71,6 +77,7 @@ public class SchoolTeacherController {
}
@Operation(summary = "Reset teacher password")
@Log(module = LogModule.TEACHER, type = LogOperationType.UPDATE, description = "重置教师密码")
@PostMapping("/{id}/reset-password")
public Result<java.util.Map<String, String>> resetPassword(@PathVariable Long id) {
Long tenantId = SecurityUtils.getCurrentTenantId();

View File

@ -3,6 +3,7 @@ package com.reading.platform.controller.teacher;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reading.platform.common.enums.CourseStatus;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.mapper.ClassMapper;
import com.reading.platform.common.mapper.CoursePackageMapper;
import com.reading.platform.common.mapper.StudentMapper;

View File

@ -2,6 +2,7 @@ package com.reading.platform.controller.teacher;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reading.platform.common.annotation.RequireRole;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.UserRole;
import com.reading.platform.common.mapper.LessonFeedbackMapper;
import com.reading.platform.common.response.PageResult;

View File

@ -1,6 +1,9 @@
package com.reading.platform.controller.teacher;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.mapper.GrowthRecordMapper;
import com.reading.platform.common.response.PageResult;
import com.reading.platform.common.response.Result;
@ -28,6 +31,7 @@ public class TeacherGrowthController {
private final GrowthRecordMapper growthRecordMapper;
@Operation(summary = "创建成长记录")
@Log(module = LogModule.GROWTH, type = LogOperationType.CREATE, description = "创建成长记录")
@PostMapping
public Result<GrowthRecordResponse> createGrowthRecord(@Valid @RequestBody GrowthRecordCreateRequest request) {
Long tenantId = SecurityUtils.getCurrentTenantId();
@ -37,6 +41,7 @@ public class TeacherGrowthController {
}
@Operation(summary = "更新成长记录")
@Log(module = LogModule.GROWTH, type = LogOperationType.UPDATE, description = "更新成长记录")
@PutMapping("/{id}")
public Result<GrowthRecordResponse> updateGrowthRecord(@PathVariable Long id, @RequestBody GrowthRecordUpdateRequest request) {
GrowthRecord record = growthRecordService.updateGrowthRecord(id, request);
@ -64,6 +69,7 @@ public class TeacherGrowthController {
}
@Operation(summary = "删除成长记录")
@Log(module = LogModule.GROWTH, type = LogOperationType.DELETE, description = "删除成长记录")
@DeleteMapping("/{id}")
public Result<Void> deleteGrowthRecord(@PathVariable Long id) {
growthRecordService.deleteGrowthRecord(id);

View File

@ -1,6 +1,9 @@
package com.reading.platform.controller.teacher;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.mapper.ClassMapper;
import com.reading.platform.common.mapper.LessonMapper;
import com.reading.platform.common.mapper.StudentRecordMapper;
@ -55,6 +58,7 @@ public class TeacherLessonController {
private final SchoolScheduleService schoolScheduleService;
@Operation(summary = "创建课时")
@Log(module = LogModule.LESSON, type = LogOperationType.CREATE, description = "创建课时")
@PostMapping
public Result<LessonResponse> createLesson(@Valid @RequestBody LessonCreateRequest request) {
Long tenantId = SecurityUtils.getCurrentTenantId();
@ -65,6 +69,7 @@ public class TeacherLessonController {
}
@Operation(summary = "更新课时")
@Log(module = LogModule.LESSON, type = LogOperationType.UPDATE, description = "更新课时")
@PutMapping("/{id}")
public Result<LessonResponse> updateLesson(@PathVariable Long id, @RequestBody LessonUpdateRequest request) {
Lesson lesson = lessonService.updateLesson(id, request);
@ -126,6 +131,7 @@ public class TeacherLessonController {
}
@Operation(summary = "开始上课")
@Log(module = LogModule.LESSON, type = LogOperationType.OTHER, description = "开始上课")
@PostMapping("/{id}/start")
public Result<Void> startLesson(@PathVariable Long id) {
lessonService.startLesson(id);
@ -133,6 +139,7 @@ public class TeacherLessonController {
}
@Operation(summary = "完成课时")
@Log(module = LogModule.LESSON, type = LogOperationType.OTHER, description = "完成课时")
@PostMapping("/{id}/complete")
public Result<Void> completeLesson(@PathVariable Long id) {
lessonService.completeLesson(id);
@ -140,6 +147,7 @@ public class TeacherLessonController {
}
@Operation(summary = "取消课时")
@Log(module = LogModule.LESSON, type = LogOperationType.OTHER, description = "取消课时")
@PostMapping("/{id}/cancel")
public Result<Void> cancelLesson(@PathVariable Long id) {
lessonService.cancelLesson(id);
@ -164,6 +172,7 @@ public class TeacherLessonController {
}
@Operation(summary = "保存学生记录")
@Log(module = LogModule.STUDENT_RECORD, type = LogOperationType.CREATE, description = "保存学生记录")
@PostMapping("/{id}/students/{studentId}/record")
public Result<StudentRecordResponse> saveStudentRecord(
@PathVariable Long id,
@ -174,6 +183,7 @@ public class TeacherLessonController {
}
@Operation(summary = "批量保存学生记录")
@Log(module = LogModule.STUDENT_RECORD, type = LogOperationType.CREATE, description = "批量保存学生记录")
@PostMapping("/{id}/students/batch-records")
public Result<List<StudentRecordResponse>> batchSaveStudentRecords(
@PathVariable Long id,
@ -192,6 +202,7 @@ public class TeacherLessonController {
}
@Operation(summary = "提交课时反馈")
@Log(module = LogModule.LESSON_FEEDBACK, type = LogOperationType.CREATE, description = "提交课时反馈")
@PostMapping("/{id}/feedback")
public Result<LessonFeedback> submitFeedback(
@PathVariable Long id,
@ -202,6 +213,7 @@ public class TeacherLessonController {
}
@Operation(summary = "保存课时进度")
@Log(module = LogModule.LESSON, type = LogOperationType.UPDATE, description = "保存课时进度")
@PutMapping("/{id}/progress")
public Result<Void> saveLessonProgress(
@PathVariable Long id,
@ -235,6 +247,7 @@ public class TeacherLessonController {
}
@Operation(summary = "从排课开始上课")
@Log(module = LogModule.LESSON, type = LogOperationType.OTHER, description = "从排课开始上课")
@PostMapping("/from-schedule/{schedulePlanId}/start")
public Result<LessonResponse> startLessonFromSchedule(@PathVariable Long schedulePlanId) {
Long teacherId = SecurityUtils.getCurrentUserId();
@ -249,6 +262,7 @@ public class TeacherLessonController {
}
@Operation(summary = "从排课创建课时")
@Log(module = LogModule.LESSON, type = LogOperationType.CREATE, description = "从排课创建课时")
@PostMapping("/from-schedule/{schedulePlanId}")
public Result<LessonResponse> createLessonFromSchedule(@PathVariable Long schedulePlanId) {
Long teacherId = SecurityUtils.getCurrentUserId();

View File

@ -1,5 +1,8 @@
package com.reading.platform.controller.teacher;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reading.platform.common.response.PageResult;
import com.reading.platform.common.response.Result;
@ -37,6 +40,7 @@ public class TeacherNotificationController {
}
@Operation(summary = "标记通知为已读")
@Log(module = LogModule.NOTIFICATION, type = LogOperationType.OTHER, description = "标记通知为已读")
@PostMapping("/{id}/read")
public Result<Void> markAsRead(@PathVariable Long id) {
notificationService.markAsRead(id);
@ -44,6 +48,7 @@ public class TeacherNotificationController {
}
@Operation(summary = "标记所有通知为已读")
@Log(module = LogModule.NOTIFICATION, type = LogOperationType.OTHER, description = "标记所有通知为已读")
@PostMapping("/read-all")
public Result<Void> markAllAsRead() {
Long userId = SecurityUtils.getCurrentUserId();

View File

@ -2,6 +2,9 @@ package com.reading.platform.controller.teacher;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reading.platform.common.annotation.RequireRole;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.enums.UserRole;
import com.reading.platform.common.response.PageResult;
import com.reading.platform.common.response.Result;
@ -81,6 +84,7 @@ public class TeacherScheduleController {
@PostMapping
@Operation(summary = "创建排课")
@Log(module = LogModule.SCHEDULE, type = LogOperationType.CREATE, description = "创建排课")
public Result<SchedulePlanResponse> createSchedule(@Valid @RequestBody SchedulePlanCreateRequest request) {
Long teacherId = SecurityUtils.getCurrentUserId();
Long tenantId = SecurityUtils.getCurrentTenantId();
@ -90,6 +94,7 @@ public class TeacherScheduleController {
@PutMapping("/{id}")
@Operation(summary = "更新排课")
@Log(module = LogModule.SCHEDULE, type = LogOperationType.UPDATE, description = "更新排课")
public Result<SchedulePlanResponse> updateSchedule(
@PathVariable Long id,
@RequestBody SchedulePlanUpdateRequest request) {
@ -99,6 +104,7 @@ public class TeacherScheduleController {
@DeleteMapping("/{id}")
@Operation(summary = "取消排课")
@Log(module = LogModule.SCHEDULE, type = LogOperationType.DELETE, description = "取消排课")
public Result<Void> cancelSchedule(@PathVariable Long id) {
teacherScheduleService.cancelSchedule(id);
return Result.success();

View File

@ -1,6 +1,9 @@
package com.reading.platform.controller.teacher;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.mapper.TaskMapper;
import com.reading.platform.common.response.PageResult;
import com.reading.platform.common.response.Result;
@ -33,6 +36,7 @@ public class TeacherTaskController {
private final TaskFeedbackService taskFeedbackService;
@Operation(summary = "创建任务")
@Log(module = LogModule.TASK, type = LogOperationType.CREATE, description = "创建任务")
@PostMapping
public Result<TaskResponse> createTask(@Valid @RequestBody TaskCreateRequest request) {
Long tenantId = SecurityUtils.getCurrentTenantId();
@ -42,6 +46,7 @@ public class TeacherTaskController {
}
@Operation(summary = "更新任务")
@Log(module = LogModule.TASK, type = LogOperationType.UPDATE, description = "更新任务")
@PutMapping("/{id}")
public Result<TaskResponse> updateTask(@PathVariable Long id, @RequestBody TaskUpdateRequest request) {
Task task = taskService.updateTask(id, request);
@ -77,6 +82,7 @@ public class TeacherTaskController {
}
@Operation(summary = "删除任务")
@Log(module = LogModule.TASK, type = LogOperationType.DELETE, description = "删除任务")
@DeleteMapping("/{id}")
public Result<Void> deleteTask(@PathVariable Long id) {
taskService.deleteTask(id);
@ -106,6 +112,7 @@ public class TeacherTaskController {
}
@Operation(summary = "提交评价")
@Log(module = LogModule.TASK_FEEDBACK, type = LogOperationType.CREATE, description = "提交任务评价")
@PostMapping("/completions/{completionId}/feedback")
public Result<TaskFeedbackResponse> submitFeedback(
@PathVariable Long completionId,
@ -116,6 +123,7 @@ public class TeacherTaskController {
}
@Operation(summary = "修改评价")
@Log(module = LogModule.TASK_FEEDBACK, type = LogOperationType.UPDATE, description = "修改任务评价")
@PutMapping("/completions/{completionId}/feedback")
public Result<TaskFeedbackResponse> updateFeedback(
@PathVariable Long completionId,

View File

@ -2,6 +2,9 @@ package com.reading.platform.controller.teacher;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reading.platform.common.annotation.RequireRole;
import com.reading.platform.common.annotation.Log;
import com.reading.platform.common.enums.LogModule;
import com.reading.platform.common.enums.LogOperationType;
import com.reading.platform.common.enums.UserRole;
import com.reading.platform.common.mapper.TaskTemplateMapper;
import com.reading.platform.common.response.PageResult;
@ -65,6 +68,7 @@ public class TeacherTaskTemplateController {
@PostMapping
@Operation(summary = "创建模板")
@Log(module = LogModule.TASK_TEMPLATE, type = LogOperationType.CREATE, description = "创建任务模板")
public Result<TaskTemplateResponse> createTemplate(@Valid @RequestBody TaskTemplateCreateRequest request) {
Long tenantId = SecurityUtils.getCurrentTenantId();
Long userId = SecurityUtils.getCurrentUserId();
@ -74,6 +78,7 @@ public class TeacherTaskTemplateController {
@PostMapping("/from-template")
@Operation(summary = "从模板创建任务")
@Log(module = LogModule.TASK_TEMPLATE, type = LogOperationType.CREATE, description = "从模板创建任务")
public Result<Task> createFromTemplate(@Valid @RequestBody CreateTaskFromTemplateRequest request) {
Long tenantId = SecurityUtils.getCurrentTenantId();
Long userId = SecurityUtils.getCurrentUserId();
@ -83,6 +88,7 @@ public class TeacherTaskTemplateController {
@PutMapping("/{id}")
@Operation(summary = "更新模板")
@Log(module = LogModule.TASK_TEMPLATE, type = LogOperationType.UPDATE, description = "更新任务模板")
public Result<TaskTemplateResponse> updateTemplate(
@PathVariable Long id,
@RequestBody TaskTemplateCreateRequest request) {
@ -92,6 +98,7 @@ public class TeacherTaskTemplateController {
@DeleteMapping("/{id}")
@Operation(summary = "删除模板")
@Log(module = LogModule.TASK_TEMPLATE, type = LogOperationType.DELETE, description = "删除任务模板")
public Result<Void> deleteTemplate(@PathVariable Long id) {
taskTemplateService.deleteTemplate(id);
return Result.success();

View File

@ -51,5 +51,4 @@ public class TenantUpdateRequest {
@Schema(description = "结束日期")
private LocalDate expireDate;
}

View File

@ -23,7 +23,7 @@ public class OperationLog extends BaseEntity {
@Schema(description = "用户角色")
private String userRole;
@Schema(description = "操作类型")
@Schema(description = "操作类型CREATE/UPDATE/DELETE/QUERY/EXPORT/IMPORT/OTHER)")
private String action;
@Schema(description = "操作模块")
@ -44,4 +44,7 @@ public class OperationLog extends BaseEntity {
@Schema(description = "用户代理")
private String userAgent;
@Schema(description = "请求参数JSON 格式,不返回给前端)")
private String requestParams;
}

View File

@ -15,7 +15,7 @@ public interface OperationLogService extends IService<OperationLog> {
* 分页查询日志列表按租户隔离
*/
Page<OperationLog> getLogPage(Long tenantId, Integer pageNum, Integer pageSize,
String module, String operator);
String module, String action);
/**
* 获取日志统计按租户隔离

View File

@ -64,7 +64,7 @@ public class AuthServiceImpl implements AuthService {
if (adminUser != null) {
if (!passwordEncoder.matches(password, adminUser.getPassword())) {
log.warn("登录失败:密码错误,用户名:{}", username);
throw new BusinessException(ErrorCode.LOGIN_FAILED);
throw new BusinessException(ErrorCode.INCORRECT_PASSWORD);
}
if (!GenericStatus.isActive(adminUser.getStatus())) {
log.warn("登录失败:账户已禁用,用户名:{}", username);
@ -104,7 +104,7 @@ public class AuthServiceImpl implements AuthService {
if (teacher != null) {
if (!passwordEncoder.matches(password, teacher.getPassword())) {
log.warn("登录失败:密码错误,用户名:{}", username);
throw new BusinessException(ErrorCode.LOGIN_FAILED);
throw new BusinessException(ErrorCode.INCORRECT_PASSWORD);
}
if (!GenericStatus.isActive(teacher.getStatus())) {
log.warn("登录失败:账户已禁用,用户名:{}", username);
@ -150,7 +150,7 @@ public class AuthServiceImpl implements AuthService {
if (parent != null) {
if (!passwordEncoder.matches(password, parent.getPassword())) {
log.warn("登录失败:密码错误,用户名:{}", username);
throw new BusinessException(ErrorCode.LOGIN_FAILED);
throw new BusinessException(ErrorCode.INCORRECT_PASSWORD);
}
if (!GenericStatus.isActive(parent.getStatus())) {
log.warn("登录失败:账户已禁用,用户名:{}", username);
@ -190,7 +190,7 @@ public class AuthServiceImpl implements AuthService {
if (tenant != null) {
if (!passwordEncoder.matches(password, tenant.getPassword())) {
log.warn("登录失败:密码错误,用户名:{}", username);
throw new BusinessException(ErrorCode.LOGIN_FAILED);
throw new BusinessException(ErrorCode.INCORRECT_PASSWORD);
}
if (!TenantStatus.ACTIVE.getCode().equalsIgnoreCase(tenant.getStatus())) {
log.warn("登录失败:账户已禁用,用户名:{}", username);
@ -220,8 +220,8 @@ public class AuthServiceImpl implements AuthService {
.build();
}
log.warn("登录失败:用户不存在,用户名:{}", username);
throw new BusinessException(ErrorCode.LOGIN_FAILED);
log.warn("登录失败:账号不存在,用户名:{}", username);
throw new BusinessException(ErrorCode.ACCOUNT_NOT_FOUND);
}
private LoginResponse loginWithRole(String username, String password, String role) {
@ -232,9 +232,13 @@ public class AuthServiceImpl implements AuthService {
AdminUser adminUser = adminUserMapper.selectOne(
new LambdaQueryWrapper<AdminUser>().eq(AdminUser::getUsername, username)
);
if (adminUser == null || !passwordEncoder.matches(password, adminUser.getPassword())) {
log.warn("登录失败:用户不存在或密码错误,用户名:{}", username);
throw new BusinessException(ErrorCode.LOGIN_FAILED);
if (adminUser == null) {
log.warn("登录失败:账号不存在,用户名:{}", username);
throw new BusinessException(ErrorCode.ACCOUNT_NOT_FOUND);
}
if (!passwordEncoder.matches(password, adminUser.getPassword())) {
log.warn("登录失败:密码错误,用户名:{}", username);
throw new BusinessException(ErrorCode.INCORRECT_PASSWORD);
}
if (!GenericStatus.isActive(adminUser.getStatus())) {
log.warn("登录失败:账户已禁用,用户名:{}", username);
@ -269,9 +273,13 @@ public class AuthServiceImpl implements AuthService {
Tenant tenant = tenantMapper.selectOne(
new LambdaQueryWrapper<Tenant>().eq(Tenant::getUsername, username)
);
if (tenant == null || !passwordEncoder.matches(password, tenant.getPassword())) {
log.warn("登录失败:用户不存在或密码错误,用户名:{}", username);
throw new BusinessException(ErrorCode.LOGIN_FAILED);
if (tenant == null) {
log.warn("登录失败:账号不存在,用户名:{}", username);
throw new BusinessException(ErrorCode.ACCOUNT_NOT_FOUND);
}
if (!passwordEncoder.matches(password, tenant.getPassword())) {
log.warn("登录失败:密码错误,用户名:{}", username);
throw new BusinessException(ErrorCode.INCORRECT_PASSWORD);
}
if (!GenericStatus.isActive(tenant.getStatus())) {
log.warn("登录失败:账户已禁用,用户名:{}", username);
@ -303,9 +311,13 @@ public class AuthServiceImpl implements AuthService {
Teacher teacher = teacherMapper.selectOne(
new LambdaQueryWrapper<Teacher>().eq(Teacher::getUsername, username)
);
if (teacher == null || !passwordEncoder.matches(password, teacher.getPassword())) {
log.warn("登录失败:用户不存在或密码错误,用户名:{}", username);
throw new BusinessException(ErrorCode.LOGIN_FAILED);
if (teacher == null) {
log.warn("登录失败:账号不存在,用户名:{}", username);
throw new BusinessException(ErrorCode.ACCOUNT_NOT_FOUND);
}
if (!passwordEncoder.matches(password, teacher.getPassword())) {
log.warn("登录失败:密码错误,用户名:{}", username);
throw new BusinessException(ErrorCode.INCORRECT_PASSWORD);
}
if (!GenericStatus.isActive(teacher.getStatus())) {
log.warn("登录失败:账户已禁用,用户名:{}", username);
@ -346,9 +358,13 @@ public class AuthServiceImpl implements AuthService {
Parent parent = parentMapper.selectOne(
new LambdaQueryWrapper<Parent>().eq(Parent::getUsername, username)
);
if (parent == null || !passwordEncoder.matches(password, parent.getPassword())) {
log.warn("登录失败:用户不存在或密码错误,用户名:{}", username);
throw new BusinessException(ErrorCode.LOGIN_FAILED);
if (parent == null) {
log.warn("登录失败:账号不存在,用户名:{}", username);
throw new BusinessException(ErrorCode.ACCOUNT_NOT_FOUND);
}
if (!passwordEncoder.matches(password, parent.getPassword())) {
log.warn("登录失败:密码错误,用户名:{}", username);
throw new BusinessException(ErrorCode.INCORRECT_PASSWORD);
}
if (!GenericStatus.isActive(parent.getStatus())) {
log.warn("登录失败:账户已禁用,用户名:{}", username);
@ -379,7 +395,7 @@ public class AuthServiceImpl implements AuthService {
.tenantId(parent.getTenantId())
.build();
}
default -> throw new BusinessException(ErrorCode.LOGIN_FAILED);
default -> throw new BusinessException(ErrorCode.ACCOUNT_NOT_FOUND);
}
}

View File

@ -15,7 +15,9 @@ import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 操作日志服务实现类
@ -28,7 +30,7 @@ public class OperationLogServiceImpl extends ServiceImpl<OperationLogMapper, Ope
@Override
public Page<OperationLog> getLogPage(Long tenantId, Integer pageNum, Integer pageSize,
String module, String operator) {
String module, String action) {
log.debug("分页查询日志列表,页码:{},每页数量:{},租户 ID{}", pageNum, pageSize, tenantId);
int current = pageNum != null && pageNum > 0 ? pageNum : 1;
@ -43,8 +45,8 @@ public class OperationLogServiceImpl extends ServiceImpl<OperationLogMapper, Ope
if (StringUtils.hasText(module)) {
wrapper.eq(OperationLog::getModule, module);
}
if (StringUtils.hasText(operator)) {
wrapper.like(OperationLog::getAction, operator);
if (StringUtils.hasText(action)) {
wrapper.like(OperationLog::getAction, action);
}
wrapper.orderByDesc(OperationLog::getCreatedAt);
@ -62,9 +64,29 @@ public class OperationLogServiceImpl extends ServiceImpl<OperationLogMapper, Ope
.eq(OperationLog::getTenantId, tenantId)
);
// 按模块分组统计
List<OperationLog> allLogs = this.list(
new LambdaQueryWrapper<OperationLog>()
.eq(OperationLog::getTenantId, tenantId)
.select(OperationLog::getModule)
);
Map<String, Long> byModuleMap = allLogs.stream()
.collect(Collectors.groupingBy(
OperationLog::getModule,
Collectors.counting()
));
// 转换为前端期望的格式Map<String, Integer>
Map<String, Integer> byModule = byModuleMap.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().intValue()
));
Map<String, Object> stats = new HashMap<>();
stats.put("totalLogs", totalLogs);
stats.put("byModule", new HashMap<>());
stats.put("byModule", byModule);
stats.put("byOperator", new HashMap<>());
return stats;

View File

@ -15,7 +15,3 @@ ALTER TABLE `schedule_plan`
ADD COLUMN `lesson_type` VARCHAR(50) COMMENT '课程类型: INTRODUCTION/COLLECTIVE/LANGUAGE/SOCIETY/SCIENCE/ART/HEALTH',
ADD INDEX `idx_lesson_type` (`lesson_type`);
-- 添加外键约束
ALTER TABLE `schedule_plan`
ADD CONSTRAINT `fk_schedule_plan_course_package`
FOREIGN KEY (`course_package_id`) REFERENCES `course_package`(`id`);

View File

@ -0,0 +1,19 @@
-- =====================================================
-- 操作日志表添加请求参数字段
-- 版本V48
-- 创建时间2026-03-23
-- 描述:在 operation_log 表中添加 request_params 字段,用于记录请求参数
-- =====================================================
-- -----------------------------------------------------
-- 1. 添加 request_params 字段
-- -----------------------------------------------------
ALTER TABLE `operation_log`
ADD COLUMN `request_params` TEXT COMMENT '请求参数JSON 格式)' AFTER `user_agent`;
-- -----------------------------------------------------
-- 说明:
-- - request_params 字段用于存储请求参数的 JSON 表示
-- - 该字段仅在 Service 层和数据库层面使用,不通过 API 返回给前端
-- - 参数记录由 LogAspect 自动完成,当 @Log 注解的 recordParams() = true 时生效
-- -----------------------------------------------------