diff --git a/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/mapper/LogMapper.java b/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/mapper/LogMapper.java index a928a151..67478ae9 100644 --- a/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/mapper/LogMapper.java +++ b/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/mapper/LogMapper.java @@ -22,6 +22,7 @@ import com.baomidou.mybatisplus.core.toolkit.Constants; import org.apache.ibatis.annotations.Param; import top.charles7c.continew.admin.system.model.entity.LogDO; import top.charles7c.continew.admin.system.model.resp.*; +import top.charles7c.continew.admin.system.model.resp.log.LogResp; import top.charles7c.continew.starter.data.mybatis.plus.base.BaseMapper; import java.util.List; @@ -45,6 +46,14 @@ public interface LogMapper extends BaseMapper { IPage selectLogPage(@Param("page") IPage page, @Param(Constants.WRAPPER) QueryWrapper queryWrapper); + /** + * 查询列表 + * + * @param queryWrapper 查询条件 + * @return 列表信息 + */ + List selectLogList(@Param(Constants.WRAPPER) QueryWrapper queryWrapper); + /** * 查询仪表盘总计信息 * diff --git a/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/model/resp/LogDetailResp.java b/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/model/resp/log/LogDetailResp.java similarity index 98% rename from continew-admin-system/src/main/java/top/charles7c/continew/admin/system/model/resp/LogDetailResp.java rename to continew-admin-system/src/main/java/top/charles7c/continew/admin/system/model/resp/log/LogDetailResp.java index f39dfa33..37410731 100644 --- a/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/model/resp/LogDetailResp.java +++ b/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/model/resp/log/LogDetailResp.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package top.charles7c.continew.admin.system.model.resp; +package top.charles7c.continew.admin.system.model.resp.log; import cn.crane4j.annotation.Assemble; import cn.crane4j.annotation.Mapping; diff --git a/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/model/resp/LogResp.java b/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/model/resp/log/LogResp.java similarity index 97% rename from continew-admin-system/src/main/java/top/charles7c/continew/admin/system/model/resp/LogResp.java rename to continew-admin-system/src/main/java/top/charles7c/continew/admin/system/model/resp/log/LogResp.java index 848c7384..a8150df1 100644 --- a/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/model/resp/LogResp.java +++ b/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/model/resp/log/LogResp.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package top.charles7c.continew.admin.system.model.resp; +package top.charles7c.continew.admin.system.model.resp.log; import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/model/resp/log/LoginLogExportResp.java b/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/model/resp/log/LoginLogExportResp.java new file mode 100644 index 00000000..929cebf9 --- /dev/null +++ b/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/model/resp/log/LoginLogExportResp.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.charles7c.continew.admin.system.model.resp.log; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.charles7c.continew.admin.system.enums.LogStatusEnum; +import top.charles7c.continew.starter.extension.crud.converter.ExcelBaseEnumConverter; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 登录日志导出信息 + * + * @author Charles7c + * @since 2023/1/14 18:27 + */ +@Data +@ExcelIgnoreUnannotated +@Schema(description = "登录日志导出信息") +public class LoginLogExportResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Schema(description = "ID", example = "1") + @ExcelProperty(value = "ID") + private Long id; + + /** + * 登录时间 + */ + @Schema(description = "登录时间", example = "2023-08-08 08:08:08", type = "string") + @ExcelProperty(value = "登录时间") + private LocalDateTime createTime; + + /** + * 用户昵称 + */ + @Schema(description = "用户昵称", example = "张三") + @ExcelProperty(value = "用户昵称") + private String createUserString; + + /** + * 登录行为 + */ + @Schema(description = "登录行为", example = "账号登录") + @ExcelProperty(value = "登录行为") + private String description; + + /** + * 状态 + */ + @Schema(description = "状态(1:成功;2:失败)", type = "Integer", allowableValues = {"1", "2"}, example = "1") + @ExcelProperty(value = "状态", converter = ExcelBaseEnumConverter.class) + private LogStatusEnum status; + + /** + * 登录 IP + */ + @Schema(description = "登录 IP", example = "192.168.0.1") + @ExcelProperty(value = "登录 IP") + private String ip; + + /** + * 登录地点 + */ + @Schema(description = "登录地点", example = "中国北京北京市") + @ExcelProperty(value = "登录地点") + private String address; + + /** + * 浏览器 + */ + @Schema(description = "浏览器", example = "Chrome 115.0.0.0") + @ExcelProperty(value = "浏览器") + private String browser; + + /** + * 终端系统 + */ + @Schema(description = "终端系统", example = "Windows 10") + @ExcelProperty(value = "终端系统") + private String os; +} diff --git a/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/model/resp/log/OperationLogExportResp.java b/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/model/resp/log/OperationLogExportResp.java new file mode 100644 index 00000000..488a1e73 --- /dev/null +++ b/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/model/resp/log/OperationLogExportResp.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.charles7c.continew.admin.system.model.resp.log; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import top.charles7c.continew.admin.system.enums.LogStatusEnum; +import top.charles7c.continew.starter.extension.crud.converter.ExcelBaseEnumConverter; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 操作日志导出信息 + * + * @author Charles7c + * @since 2023/1/14 18:27 + */ +@Data +@ExcelIgnoreUnannotated +@Schema(description = "操作日志导出信息") +public class OperationLogExportResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Schema(description = "ID", example = "1") + @ExcelProperty(value = "ID") + private Long id; + + /** + * 操作时间 + */ + @Schema(description = "操作时间", example = "2023-08-08 08:08:08", type = "string") + @ExcelProperty(value = "操作时间") + private LocalDateTime createTime; + + /** + * 操作人 + */ + @Schema(description = "操作人", example = "张三") + @ExcelProperty(value = "操作人") + private String createUserString; + + /** + * 操作内容 + */ + @Schema(description = "操作内容", example = "账号登录") + @ExcelProperty(value = "操作内容") + private String description; + + /** + * 所属模块 + */ + @Schema(description = "所属模块", example = "部门管理") + @ExcelProperty(value = "所属模块") + private String module; + + /** + * 状态 + */ + @Schema(description = "状态(1:成功;2:失败)", type = "Integer", allowableValues = {"1", "2"}, example = "1") + @ExcelProperty(value = "状态", converter = ExcelBaseEnumConverter.class) + private LogStatusEnum status; + + /** + * 操作 IP + */ + @Schema(description = "操作 IP", example = "192.168.0.1") + @ExcelProperty(value = "操作 IP") + private String ip; + + /** + * 操作地点 + */ + @Schema(description = "操作地点", example = "中国北京北京市") + @ExcelProperty(value = "操作地点") + private String address; + + /** + * 耗时(ms) + */ + @Schema(description = "耗时(ms)", example = "58") + @ExcelProperty(value = "耗时(ms)") + private Long timeTaken; + + /** + * 浏览器 + */ + @Schema(description = "浏览器", example = "Chrome 115.0.0.0") + @ExcelProperty(value = "浏览器") + private String browser; + + /** + * 终端系统 + */ + @Schema(description = "终端系统", example = "Windows 10") + @ExcelProperty(value = "终端系统") + private String os; +} diff --git a/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/service/LogService.java b/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/service/LogService.java index 6cd544fb..8f25b271 100644 --- a/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/service/LogService.java +++ b/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/service/LogService.java @@ -16,9 +16,13 @@ package top.charles7c.continew.admin.system.service; +import jakarta.servlet.http.HttpServletResponse; import top.charles7c.continew.admin.system.model.query.LogQuery; import top.charles7c.continew.admin.system.model.resp.*; +import top.charles7c.continew.admin.system.model.resp.log.LogDetailResp; +import top.charles7c.continew.admin.system.model.resp.log.LogResp; import top.charles7c.continew.starter.extension.crud.model.query.PageQuery; +import top.charles7c.continew.starter.extension.crud.model.query.SortQuery; import top.charles7c.continew.starter.extension.crud.model.resp.PageResp; import java.util.List; @@ -42,13 +46,31 @@ public interface LogService { PageResp page(LogQuery query, PageQuery pageQuery); /** - * 查看详情 + * 查询详情 * * @param id ID * @return 详情信息 */ LogDetailResp get(Long id); + /** + * 导出登录日志 + * + * @param query 查询条件 + * @param sortQuery 排序查询条件 + * @param response 响应对象 + */ + void exportLoginLog(LogQuery query, SortQuery sortQuery, HttpServletResponse response); + + /** + * 导出操作日志 + * + * @param query 查询条件 + * @param sortQuery 排序查询条件 + * @param response 响应对象 + */ + void exportOperationLog(LogQuery query, SortQuery sortQuery, HttpServletResponse response); + /** * 查询仪表盘总计信息 * diff --git a/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/service/impl/LogServiceImpl.java b/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/service/impl/LogServiceImpl.java index ea6d598e..e2df6206 100644 --- a/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/service/impl/LogServiceImpl.java +++ b/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/service/impl/LogServiceImpl.java @@ -19,21 +19,31 @@ package top.charles7c.continew.admin.system.service.impl; import cn.crane4j.annotation.AutoOperate; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Opt; +import cn.hutool.core.text.CharSequenceUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; +import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import top.charles7c.continew.admin.system.mapper.LogMapper; import top.charles7c.continew.admin.system.model.entity.LogDO; import top.charles7c.continew.admin.system.model.query.LogQuery; import top.charles7c.continew.admin.system.model.resp.*; +import top.charles7c.continew.admin.system.model.resp.log.LogDetailResp; +import top.charles7c.continew.admin.system.model.resp.log.LogResp; +import top.charles7c.continew.admin.system.model.resp.log.LoginLogExportResp; +import top.charles7c.continew.admin.system.model.resp.log.OperationLogExportResp; import top.charles7c.continew.admin.system.service.LogService; import top.charles7c.continew.starter.core.util.validate.CheckUtils; import top.charles7c.continew.starter.core.util.validate.ValidationUtils; import top.charles7c.continew.starter.extension.crud.model.query.PageQuery; +import top.charles7c.continew.starter.extension.crud.model.query.SortQuery; import top.charles7c.continew.starter.extension.crud.model.resp.PageResp; +import top.charles7c.continew.starter.file.excel.util.ExcelUtils; import java.util.Date; import java.util.List; @@ -50,10 +60,92 @@ import java.util.Map; @RequiredArgsConstructor public class LogServiceImpl implements LogService { - private final LogMapper logMapper; + private final LogMapper baseMapper; @Override public PageResp page(LogQuery query, PageQuery pageQuery) { + QueryWrapper queryWrapper = this.handleQueryWrapper(query); + IPage page = baseMapper.selectLogPage(pageQuery.toPage(), queryWrapper); + return PageResp.build(page); + } + + @Override + @AutoOperate(type = LogDetailResp.class) + public LogDetailResp get(Long id) { + LogDO logDO = baseMapper.selectById(id); + CheckUtils.throwIfNotExists(logDO, "LogDO", "ID", id); + return BeanUtil.copyProperties(logDO, LogDetailResp.class); + } + + @Override + public void exportLoginLog(LogQuery query, SortQuery sortQuery, HttpServletResponse response) { + List list = BeanUtil.copyToList(this.list(query, sortQuery), LoginLogExportResp.class); + ExcelUtils.export(list, "导出登录日志数据", LoginLogExportResp.class, response); + } + + @Override + public void exportOperationLog(LogQuery query, SortQuery sortQuery, HttpServletResponse response) { + List list = BeanUtil.copyToList(this + .list(query, sortQuery), OperationLogExportResp.class); + ExcelUtils.export(list, "导出操作日志数据", OperationLogExportResp.class, response); + } + + @Override + public DashboardTotalResp getDashboardTotal() { + return baseMapper.selectDashboardTotal(); + } + + @Override + public List listDashboardAccessTrend(Integer days) { + return baseMapper.selectListDashboardAccessTrend(days); + } + + @Override + public List listDashboardPopularModule() { + return baseMapper.selectListDashboardPopularModule(); + } + + @Override + public List> listDashboardGeoDistribution() { + return baseMapper.selectListDashboardGeoDistribution(); + } + + /** + * 查询列表 + * + * @param query 查询条件 + * @param sortQuery 排序查询条件 + * @return 列表信息 + */ + private List list(LogQuery query, SortQuery sortQuery) { + QueryWrapper queryWrapper = this.handleQueryWrapper(query); + this.sort(queryWrapper, sortQuery); + return baseMapper.selectLogList(queryWrapper); + } + + /** + * 设置排序 + * + * @param queryWrapper 查询条件封装对象 + * @param sortQuery 排序查询条件 + */ + private void sort(QueryWrapper queryWrapper, SortQuery sortQuery) { + Sort sort = Opt.ofNullable(sortQuery).orElseGet(SortQuery::new).getSort(); + for (Sort.Order order : sort) { + if (null != order) { + String property = order.getProperty(); + queryWrapper.orderBy(true, order.isAscending(), CharSequenceUtil.toUnderlineCase(property)); + } + } + } + + /** + * 处理查询条件 + * + * @param query 查询条件 + * @return QueryWrapper + */ + private QueryWrapper handleQueryWrapper(LogQuery query) { QueryWrapper queryWrapper = new QueryWrapper<>(); // 构建条件 String description = query.getDescription(); @@ -81,35 +173,6 @@ public class LogServiceImpl implements LogService { if (null != status) { queryWrapper.eq("t1.status", status); } - IPage page = logMapper.selectLogPage(pageQuery.toPage(), queryWrapper); - return PageResp.build(page); - } - - @Override - @AutoOperate(type = LogDetailResp.class) - public LogDetailResp get(Long id) { - LogDO logDO = logMapper.selectById(id); - CheckUtils.throwIfNotExists(logDO, "LogDO", "ID", id); - return BeanUtil.copyProperties(logDO, LogDetailResp.class); - } - - @Override - public DashboardTotalResp getDashboardTotal() { - return logMapper.selectDashboardTotal(); - } - - @Override - public List listDashboardAccessTrend(Integer days) { - return logMapper.selectListDashboardAccessTrend(days); - } - - @Override - public List listDashboardPopularModule() { - return logMapper.selectListDashboardPopularModule(); - } - - @Override - public List> listDashboardGeoDistribution() { - return logMapper.selectListDashboardGeoDistribution(); + return queryWrapper; } } diff --git a/continew-admin-system/src/main/resources/mapper/LogMapper.xml b/continew-admin-system/src/main/resources/mapper/LogMapper.xml index d7fb113a..bd8097ce 100644 --- a/continew-admin-system/src/main/resources/mapper/LogMapper.xml +++ b/continew-admin-system/src/main/resources/mapper/LogMapper.xml @@ -1,7 +1,27 @@ - + SELECT + t1.id, + t1.description, + t1.module, + t1.time_taken, + t1.ip, + t1.address, + t1.browser, + t1.os, + t1.status, + t1.error_msg, + t1.create_user, + t1.create_time, + t2.nickname AS createUserString + FROM sys_log AS t1 + LEFT JOIN sys_user AS t2 ON t2.id = t1.create_user + ${ew.customSqlSegment} + + + - \ No newline at end of file + diff --git a/continew-admin-webapi/src/main/java/top/charles7c/continew/admin/webapi/system/LogController.java b/continew-admin-webapi/src/main/java/top/charles7c/continew/admin/webapi/system/LogController.java index 277fda4d..a8ad5340 100644 --- a/continew-admin-webapi/src/main/java/top/charles7c/continew/admin/webapi/system/LogController.java +++ b/continew-admin-webapi/src/main/java/top/charles7c/continew/admin/webapi/system/LogController.java @@ -20,6 +20,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; @@ -27,37 +28,50 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import top.charles7c.continew.admin.system.model.query.LogQuery; -import top.charles7c.continew.admin.system.model.resp.LogDetailResp; -import top.charles7c.continew.admin.system.model.resp.LogResp; +import top.charles7c.continew.admin.system.model.resp.log.LogDetailResp; +import top.charles7c.continew.admin.system.model.resp.log.LogResp; import top.charles7c.continew.admin.system.service.LogService; import top.charles7c.continew.starter.extension.crud.model.query.PageQuery; +import top.charles7c.continew.starter.extension.crud.model.query.SortQuery; import top.charles7c.continew.starter.extension.crud.model.resp.PageResp; import top.charles7c.continew.starter.web.model.R; /** - * 日志管理 API + * 系统日志 API * * @author Charles7c * @since 2023/1/18 23:55 */ -@Tag(name = "日志管理 API") +@Tag(name = "系统日志 API") @RestController @RequiredArgsConstructor @RequestMapping("/system/log") public class LogController { - private final LogService logService; + private final LogService baseService; @Operation(summary = "分页查询列表", description = "分页查询列表") @GetMapping public R> page(LogQuery query, @Validated PageQuery pageQuery) { - return R.ok(logService.page(query, pageQuery)); + return R.ok(baseService.page(query, pageQuery)); } - @Operation(summary = "查看详情", description = "查看详情") + @Operation(summary = "查询详情", description = "查询详情") @Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH) @GetMapping("/{id}") public R get(@PathVariable Long id) { - return R.ok(logService.get(id)); + return R.ok(baseService.get(id)); + } + + @Operation(summary = "导出登录日志", description = "导出登录日志") + @GetMapping("/export/login") + public void exportLoginLog(LogQuery query, SortQuery sortQuery, HttpServletResponse response) { + baseService.exportLoginLog(query, sortQuery, response); + } + + @Operation(summary = "导出操作日志", description = "导出操作日志") + @GetMapping("/export/operation") + public void exportOperationLog(LogQuery query, SortQuery sortQuery, HttpServletResponse response) { + baseService.exportOperationLog(query, sortQuery, response); } }