package handler import ( "fmt" "net/http" "github.com/gin-gonic/gin" "github.com/user-management-system/internal/service" ) // LogHandler handles log requests type LogHandler struct { loginLogService *service.LoginLogService operationLogService *service.OperationLogService } // NewLogHandler creates a new LogHandler func NewLogHandler(loginLogService *service.LoginLogService, operationLogService *service.OperationLogService) *LogHandler { return &LogHandler{ loginLogService: loginLogService, operationLogService: operationLogService, } } // GetMyLoginLogs 获取我的登录日志 // @Summary 获取登录日志 // @Description 获取当前用户的登录日志 // @Tags 日志 // @Produce json // @Security BearerAuth // @Param page query int false "页码" // @Param page_size query int false "每页数量" // @Success 200 {object} Response{data=LoginLogListResponse} "登录日志列表" // @Failure 401 {object} Response "未认证" // @Router /api/v1/users/me/login-logs [get] func (h *LogHandler) GetMyLoginLogs(c *gin.Context) { userID, ok := getUserIDFromContext(c) if !ok { c.JSON(http.StatusUnauthorized, gin.H{"code": 401, "message": "unauthorized"}) return } page, pageSize := parsePageAndSize(c) logs, total, err := h.loginLogService.GetMyLoginLogs(c.Request.Context(), userID, page, pageSize) if err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "message": "success", "data": gin.H{ "list": logs, "total": total, "page": page, "page_size": pageSize, }, }) } // GetMyOperationLogs 获取我的操作日志 // @Summary 获取操作日志 // @Description 获取当前用户的操作日志 // @Tags 日志 // @Produce json // @Security BearerAuth // @Param page query int false "页码" // @Param page_size query int false "每页数量" // @Success 200 {object} Response{data=OperationLogListResponse} "操作日志列表" // @Failure 401 {object} Response "未认证" // @Router /api/v1/users/me/operation-logs [get] func (h *LogHandler) GetMyOperationLogs(c *gin.Context) { userID, ok := getUserIDFromContext(c) if !ok { c.JSON(http.StatusUnauthorized, gin.H{"code": 401, "message": "unauthorized"}) return } page, pageSize := parsePageAndSize(c) logs, total, err := h.operationLogService.GetMyOperationLogs(c.Request.Context(), userID, page, pageSize) if err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "message": "success", "data": gin.H{ "list": logs, "total": total, "page": page, "page_size": pageSize, }, }) } // GetLoginLogs 获取登录日志列表 // @Summary 获取登录日志列表 // @Description 获取所有登录日志(仅管理员),支持游标分页和偏移分页 // @Tags 日志 // @Produce json // @Security BearerAuth // @Param cursor query string false "游标分页游标" // @Param size query int false "每页数量(游标模式)" // @Param page query int false "页码" // @Param page_size query int false "每页数量" // @Success 200 {object} Response{data=LoginLogListResponse} "登录日志列表" // @Failure 403 {object} Response "无权限" // @Router /api/v1/admin/logs/login [get] func (h *LogHandler) GetLoginLogs(c *gin.Context) { var req service.ListLoginLogRequest if err := c.ShouldBindQuery(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()}) return } // Use cursor-based pagination when cursor is provided if req.Cursor != "" || req.Size > 0 { result, err := h.loginLogService.GetLoginLogsCursor(c.Request.Context(), &req) if err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "message": "success", "data": result, }) return } // Fallback to legacy offset-based pagination logs, total, err := h.loginLogService.GetLoginLogs(c.Request.Context(), &req) if err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "message": "success", "data": gin.H{ "list": logs, "total": total, "page": req.Page, "page_size": req.PageSize, }, }) } // GetOperationLogs 获取操作日志列表 // @Summary 获取操作日志列表 // @Description 获取所有操作日志(仅管理员),支持游标分页和偏移分页 // @Tags 日志 // @Produce json // @Security BearerAuth // @Param cursor query string false "游标分页游标" // @Param size query int false "每页数量(游标模式)" // @Param page query int false "页码" // @Param page_size query int false "每页数量" // @Success 200 {object} Response{data=OperationLogListResponse} "操作日志列表" // @Failure 403 {object} Response "无权限" // @Failure 500 {object} Response "服务器错误" // @Router /api/v1/admin/logs/operation [get] func (h *LogHandler) GetOperationLogs(c *gin.Context) { var req service.ListOperationLogRequest if err := c.ShouldBindQuery(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()}) return } // Use cursor-based pagination when cursor is provided if req.Cursor != "" || req.Size > 0 { result, err := h.operationLogService.GetOperationLogsCursor(c.Request.Context(), &req) if err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "message": "success", "data": result, }) return } // Fallback to legacy offset-based pagination logs, total, err := h.operationLogService.GetOperationLogs(c.Request.Context(), &req) if err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "message": "success", "data": gin.H{ "list": logs, "total": total, "page": req.Page, "page_size": req.PageSize, }, }) } // ExportLoginLogs 导出登录日志 // @Summary 导出登录日志 // @Description 导出登录日志为 CSV 文件 // @Tags 日志 // @Produce json // @Security BearerAuth // @Param start_time query string false "开始时间" // @Param end_time query string false "结束时间" // @Param user_id query int64 false "用户ID" // @Success 200 {file} file "CSV文件" // @Failure 403 {object} Response "无权限" // @Failure 500 {object} Response "服务器错误" // @Router /api/v1/admin/logs/login/export [get] func (h *LogHandler) ExportLoginLogs(c *gin.Context) { var req service.ExportLoginLogRequest if err := c.ShouldBindQuery(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()}) return } data, filename, contentType, err := h.loginLogService.ExportLoginLogs(c.Request.Context(), &req) if err != nil { handleError(c, err) return } c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) c.Data(http.StatusOK, contentType, data) }