From c95f5402012e05d5ed00bfb68f4a2078c1333535 Mon Sep 17 00:00:00 2001
From: YunaiV <zhijiantianya@gmail.com>
Date: Mon, 18 Jan 2021 21:07:14 +0800
Subject: [PATCH] =?UTF-8?q?=E8=BF=81=E7=A7=BB=E7=99=BB=E9=99=86=E6=97=A5?=
 =?UTF-8?q?=E5=BF=97=E7=9A=84=E4=BB=A3=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../monitor/SysLogininforController.java      |  50 ---
 ruoyi-ui/src/api/monitor/logininfor.js        |  35 --
 ruoyi-ui/src/api/system/loginlog.js           |  20 +
 ruoyi-ui/src/utils/dict.js                    |   3 +-
 .../logininfor => system/loginlog}/index.vue  | 415 ++++++++----------
 .../logger/SysLoginLogController.java         |  40 ++
 .../vo/loginlog/SysLoginLogPageReqVO.java     |  36 ++
 .../logger/vo/loginlog/SysLoginLogRespVO.java |   2 +-
 .../convert/logger/SysLoginLogConvert.java    |   4 +
 .../mysql/dao/logger/SysLoginLogMapper.java   |  19 +
 .../enums/logger/SysLoginResultEnum.java      |   2 +-
 .../service/logger/SysLoginLogService.java    |  11 +
 .../logger/impl/SysLoginLogServiceImpl.java   |  17 +
 13 files changed, 336 insertions(+), 318 deletions(-)
 delete mode 100644 ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java
 delete mode 100644 ruoyi-ui/src/api/monitor/logininfor.js
 create mode 100644 ruoyi-ui/src/api/system/loginlog.js
 rename ruoyi-ui/src/views/{monitor/logininfor => system/loginlog}/index.vue (50%)
 create mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/vo/loginlog/SysLoginLogPageReqVO.java

diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java
deleted file mode 100644
index 6146f34ad..000000000
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.ruoyi.web.controller.monitor;
-
-import java.util.List;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-import com.ruoyi.common.annotation.Log;
-import com.ruoyi.common.core.controller.BaseController;
-import com.ruoyi.common.core.domain.AjaxResult;
-import com.ruoyi.common.core.page.TableDataInfo;
-import com.ruoyi.common.enums.BusinessType;
-import com.ruoyi.common.utils.poi.ExcelUtil;
-import com.ruoyi.system.domain.SysLogininfor;
-import com.ruoyi.system.service.ISysLogininforService;
-
-/**
- * 系统访问记录
- *
- * @author ruoyi
- */
-@RestController
-@RequestMapping("/monitor/logininfor")
-public class SysLogininforController extends BaseController {
-
-    @Autowired
-    private ISysLogininforService logininforService;
-
-    @PreAuthorize("@ss.hasPermi('monitor:logininfor:list')")
-    @GetMapping("/list")
-    public TableDataInfo list(SysLogininfor logininfor) {
-        startPage();
-        List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
-        return getDataTable(list);
-    }
-
-    @Log(title = "登录日志", businessType = BusinessType.EXPORT)
-    @PreAuthorize("@ss.hasPermi('monitor:logininfor:export')")
-    @GetMapping("/export")
-    public AjaxResult export(SysLogininfor logininfor) {
-        List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
-        ExcelUtil<SysLogininfor> util = new ExcelUtil<SysLogininfor>(SysLogininfor.class);
-        return util.exportExcel(list, "登录日志");
-    }
-
-}
diff --git a/ruoyi-ui/src/api/monitor/logininfor.js b/ruoyi-ui/src/api/monitor/logininfor.js
deleted file mode 100644
index 0b89cdc6e..000000000
--- a/ruoyi-ui/src/api/monitor/logininfor.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import request from '@/utils/request'
-
-// 查询登录日志列表
-export function list(query) {
-  return request({
-    url: '/monitor/logininfor/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 删除登录日志
-export function delLogininfor(infoId) {
-  return request({
-    url: '/monitor/logininfor/' + infoId,
-    method: 'delete'
-  })
-}
-
-// 清空登录日志
-export function cleanLogininfor() {
-  return request({
-    url: '/monitor/logininfor/clean',
-    method: 'delete'
-  })
-}
-
-// 导出登录日志
-export function exportLogininfor(query) {
-  return request({
-    url: '/monitor/logininfor/export',
-    method: 'get',
-    params: query
-  })
-}
\ No newline at end of file
diff --git a/ruoyi-ui/src/api/system/loginlog.js b/ruoyi-ui/src/api/system/loginlog.js
new file mode 100644
index 000000000..41aba1fe1
--- /dev/null
+++ b/ruoyi-ui/src/api/system/loginlog.js
@@ -0,0 +1,20 @@
+import request from '@/utils/request'
+
+// 查询登录日志列表
+export function list(query) {
+  return request({
+    url: '/system/login-log/page',
+    method: 'get',
+    params: query
+  })
+}
+
+// 导出登录日志
+export function exportLoginLog(query) {
+  return request({
+    url: '/system/login-log/export',
+    method: 'get',
+    params: query,
+    responseType: 'blob'
+  })
+}
diff --git a/ruoyi-ui/src/utils/dict.js b/ruoyi-ui/src/utils/dict.js
index 00f74aefe..af1bcd836 100644
--- a/ruoyi-ui/src/utils/dict.js
+++ b/ruoyi-ui/src/utils/dict.js
@@ -12,7 +12,8 @@ export const DICT_TYPE = {
   SYS_DATA_SCOPE: 'sys_data_scope',
   SYS_USER_SEX: 'sys_user_sex',
   SYS_NOTICE_TYPE: 'sys_notice_type',
-  SYS_OPERATE_TYPE: 'sys_operate_type'
+  SYS_OPERATE_TYPE: 'sys_operate_type',
+  SYS_LOGIN_RESULT: 'sys_login_result'
 }
 
 /**
diff --git a/ruoyi-ui/src/views/monitor/logininfor/index.vue b/ruoyi-ui/src/views/system/loginlog/index.vue
similarity index 50%
rename from ruoyi-ui/src/views/monitor/logininfor/index.vue
rename to ruoyi-ui/src/views/system/loginlog/index.vue
index 90d321f3e..601c9420f 100644
--- a/ruoyi-ui/src/views/monitor/logininfor/index.vue
+++ b/ruoyi-ui/src/views/system/loginlog/index.vue
@@ -1,230 +1,185 @@
-<template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
-      <el-form-item label="登录地址" prop="ipaddr">
-        <el-input
-          v-model="queryParams.ipaddr"
-          placeholder="请输入登录地址"
-          clearable
-          style="width: 240px;"
-          size="small"
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="用户名称" prop="userName">
-        <el-input
-          v-model="queryParams.userName"
-          placeholder="请输入用户名称"
-          clearable
-          style="width: 240px;"
-          size="small"
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="状态" prop="status">
-        <el-select
-          v-model="queryParams.status"
-          placeholder="登录状态"
-          clearable
-          size="small"
-          style="width: 240px"
-        >
-          <el-option
-            v-for="dict in statusOptions"
-            :key="dict.dictValue"
-            :label="dict.dictLabel"
-            :value="dict.dictValue"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="登录时间">
-        <el-date-picker
-          v-model="dateRange"
-          size="small"
-          style="width: 240px"
-          value-format="yyyy-MM-dd"
-          type="daterange"
-          range-separator="-"
-          start-placeholder="开始日期"
-          end-placeholder="结束日期"
-        ></el-date-picker>
-      </el-form-item>
-      <el-form-item>
-        <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
-        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
-      </el-form-item>
-    </el-form>
-
-    <el-row :gutter="10" class="mb8">
-      <el-col :span="1.5">
-        <el-button
-          type="danger"
-          icon="el-icon-delete"
-          size="mini"
-          :disabled="multiple"
-          @click="handleDelete"
-          v-hasPermi="['monitor:logininfor:remove']"
-        >删除</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="danger"
-          icon="el-icon-delete"
-          size="mini"
-          @click="handleClean"
-          v-hasPermi="['monitor:logininfor:remove']"
-        >清空</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="warning"
-          icon="el-icon-download"
-          size="mini"
-          @click="handleExport"
-          v-hasPermi="['system:logininfor:export']"
-        >导出</el-button>
-      </el-col>
-      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
-    </el-row>
-
-    <el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
-      <el-table-column type="selection" width="55" align="center" />
-      <el-table-column label="访问编号" align="center" prop="infoId" />
-      <el-table-column label="用户名称" align="center" prop="userName" />
-      <el-table-column label="登录地址" align="center" prop="ipaddr" width="130" :show-overflow-tooltip="true" />
-      <el-table-column label="登录地点" align="center" prop="loginLocation" :show-overflow-tooltip="true" />
-      <el-table-column label="浏览器" align="center" prop="browser" />
-      <el-table-column label="操作系统" align="center" prop="os" />
-      <el-table-column label="登录状态" align="center" prop="status" :formatter="statusFormat" />
-      <el-table-column label="操作信息" align="center" prop="msg" />
-      <el-table-column label="登录日期" align="center" prop="loginTime" width="180">
-        <template slot-scope="scope">
-          <span>{{ parseTime(scope.row.loginTime) }}</span>
-        </template>
-      </el-table-column>
-    </el-table>
-
-    <pagination
-      v-show="total>0"
-      :total="total"
-      :page.sync="queryParams.pageNum"
-      :limit.sync="queryParams.pageSize"
-      @pagination="getList"
-    />
-  </div>
-</template>
-
-<script>
-import { list, delLogininfor, cleanLogininfor, exportLogininfor } from "@/api/monitor/logininfor";
-
-export default {
-  name: "Logininfor",
-  data() {
-    return {
-      // 遮罩层
-      loading: true,
-      // 选中数组
-      ids: [],
-      // 非多个禁用
-      multiple: true,
-      // 显示搜索条件
-      showSearch: true,
-      // 总条数
-      total: 0,
-      // 表格数据
-      list: [],
-      // 状态数据字典
-      statusOptions: [],
-      // 日期范围
-      dateRange: [],
-      // 查询参数
-      queryParams: {
-        pageNum: 1,
-        pageSize: 10,
-        ipaddr: undefined,
-        userName: undefined,
-        status: undefined
-      }
-    };
-  },
-  created() {
-    this.getList();
-    this.getDicts("sys_common_status").then(response => {
-      this.statusOptions = response.data;
-    });
-  },
-  methods: {
-    /** 查询登录日志列表 */
-    getList() {
-      this.loading = true;
-      list(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
-          this.list = response.rows;
-          this.total = response.total;
-          this.loading = false;
-        }
-      );
-    },
-    // 登录状态字典翻译
-    statusFormat(row, column) {
-      return this.selectDictLabel(this.statusOptions, row.status);
-    },
-    /** 搜索按钮操作 */
-    handleQuery() {
-      this.queryParams.pageNum = 1;
-      this.getList();
-    },
-    /** 重置按钮操作 */
-    resetQuery() {
-      this.dateRange = [];
-      this.resetForm("queryForm");
-      this.handleQuery();
-    },
-    // 多选框选中数据
-    handleSelectionChange(selection) {
-      this.ids = selection.map(item => item.infoId)
-      this.multiple = !selection.length
-    },
-    /** 删除按钮操作 */
-    handleDelete(row) {
-      const infoIds = row.infoId || this.ids;
-      this.$confirm('是否确认删除访问编号为"' + infoIds + '"的数据项?', "警告", {
-          confirmButtonText: "确定",
-          cancelButtonText: "取消",
-          type: "warning"
-        }).then(function() {
-          return delLogininfor(infoIds);
-        }).then(() => {
-          this.getList();
-          this.msgSuccess("删除成功");
-        })
-    },
-    /** 清空按钮操作 */
-    handleClean() {
-        this.$confirm('是否确认清空所有登录日志数据项?', "警告", {
-          confirmButtonText: "确定",
-          cancelButtonText: "取消",
-          type: "warning"
-        }).then(function() {
-          return cleanLogininfor();
-        }).then(() => {
-          this.getList();
-          this.msgSuccess("清空成功");
-        })
-    },
-    /** 导出按钮操作 */
-    handleExport() {
-      const queryParams = this.queryParams;
-      this.$confirm('是否确认导出所有操作日志数据项?', "警告", {
-          confirmButtonText: "确定",
-          cancelButtonText: "取消",
-          type: "warning"
-        }).then(function() {
-          return exportLogininfor(queryParams);
-        }).then(response => {
-          this.download(response.msg);
-        })
-    }
-  }
-};
-</script>
-
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="登录地址" prop="userIp">
+        <el-input
+          v-model="queryParams.userIp"
+          placeholder="请输入登录地址"
+          clearable
+          style="width: 240px;"
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="用户名称" prop="username">
+        <el-input
+          v-model="queryParams.username"
+          placeholder="请输入用户名称"
+          clearable
+          style="width: 240px;"
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select
+          v-model="queryParams.status"
+          placeholder="结果"
+          clearable
+          size="small"
+          style="width: 240px"
+        >
+          <el-option
+              :key="true"
+              label="成功"
+              :value="true"
+          />
+          <el-option
+              :key="false"
+              label="失败"
+              :value="false"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="登录时间">
+        <el-date-picker
+          v-model="dateRange"
+          size="small"
+          style="width: 240px"
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        ></el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          icon="el-icon-download"
+          size="mini"
+          @click="handleExport"
+          v-hasPermi="['system:login-log:export']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="访问编号" align="center" prop="id" />
+      <el-table-column label="日志类型" align="center" prop="logType">
+        <template slot-scope="scope">
+          <span>{{ scope.row.logType === 1 ? '登录' : '退出' }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="用户名称" align="center" prop="username" />
+      <el-table-column label="登录地址" align="center" prop="userIp" width="130" :show-overflow-tooltip="true" />
+      <el-table-column label="userAgent" align="center" prop="userAgent" width="400" :show-overflow-tooltip="true" />
+      <el-table-column label="结果" align="center" prop="status">
+        <template slot-scope="scope">
+          <span v-if="scope.row.result === 0">成功</span>
+          <span v-if="scope.row.result > 0">失败:{{ getDictDataLabel(DICT_TYPE.SYS_LOGIN_RESULT, scope.row.result) }} </span>
+        </template>
+      </el-table-column>
+      <el-table-column label="登录日期" align="center" prop="loginTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNo"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </div>
+</template>
+
+<script>
+import { list, exportLoginLog } from "@/api/system/loginlog";
+
+export default {
+  name: "Logininfor",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 表格数据
+      list: [],
+      // 状态数据字典
+      statusOptions: [],
+      // 日期范围
+      dateRange: [],
+      // 查询参数
+      queryParams: {
+        pageNo: 1,
+        pageSize: 10,
+        userIp: undefined,
+        username: undefined,
+        status: undefined
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询登录日志列表 */
+    getList() {
+      this.loading = true;
+      list(this.addDateRange(this.queryParams, [
+        this.dateRange[0] ? this.dateRange[0] + ' 00:00:00' : undefined,
+        this.dateRange[1] ? this.dateRange[1] + ' 23:59:59' : undefined,
+      ])).then(response => {
+          this.list = response.data.list;
+          this.total = response.data.total;
+          this.loading = false;
+        }
+      );
+    },
+    // 登录状态字典翻译
+    statusFormat(row, column) {
+      return this.selectDictLabel(this.statusOptions, row.status);
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNo = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.dateRange = [];
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有操作日志数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return exportLoginLog(queryParams);
+        }).then(response => {
+          this.downloadExcel(response, '登陆日志.xls');
+        })
+    }
+  }
+};
+</script>
+
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/SysLoginLogController.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/SysLoginLogController.java
index 65fba768d..88638b64e 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/SysLoginLogController.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/SysLoginLogController.java
@@ -1,4 +1,44 @@
 package cn.iocoder.dashboard.modules.system.controller.logger;
 
+import cn.iocoder.dashboard.common.pojo.CommonResult;
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.modules.system.controller.logger.vo.loginlog.SysLoginLogPageReqVO;
+import cn.iocoder.dashboard.modules.system.controller.logger.vo.loginlog.SysLoginLogRespVO;
+import cn.iocoder.dashboard.modules.system.convert.logger.SysLoginLogConvert;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.logger.SysLoginLogDO;
+import cn.iocoder.dashboard.modules.system.service.logger.SysLoginLogService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+@Api(tags = "登陆日志 API")
+@RestController
+@RequestMapping("/system/login-log")
 public class SysLoginLogController {
+
+    @Resource
+    private SysLoginLogService loginLogService;
+
+    @ApiOperation("获得登陆日志分页列表")
+    @GetMapping("/page")
+//    @PreAuthorize("@ss.hasPermi('system:login-log:query')")
+    public CommonResult<PageResult<SysLoginLogRespVO>> getLoginLogPage(@Validated SysLoginLogPageReqVO reqVO) {
+        PageResult<SysLoginLogDO> page = loginLogService.getLoginLogPage(reqVO);
+        return CommonResult.success(SysLoginLogConvert.INSTANCE.convertPage(page));
+    }
+
+//    @GetMapping("/export")
+////    @Log(title = "登录日志", businessType = BusinessType.EXPORT)
+////    @PreAuthorize("@ss.hasPermi('monitor:logininfor:export')")
+//    public void exportLoginLog(SysLogininfor logininfor) {
+//        List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
+//        ExcelUtil<SysLogininfor> util = new ExcelUtil<SysLogininfor>(SysLogininfor.class);
+//        return util.exportExcel(list, "登录日志");
+//    }
+
 }
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/vo/loginlog/SysLoginLogPageReqVO.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/vo/loginlog/SysLoginLogPageReqVO.java
new file mode 100644
index 000000000..c90bfff0b
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/vo/loginlog/SysLoginLogPageReqVO.java
@@ -0,0 +1,36 @@
+package cn.iocoder.dashboard.modules.system.controller.logger.vo.loginlog;
+
+import cn.iocoder.dashboard.common.pojo.PageParam;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.util.Date;
+
+import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@ApiModel("登陆日志分页列表 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class SysLoginLogPageReqVO extends PageParam {
+
+    @ApiModelProperty(value = "用户 IP", example = "127.0.0.1", notes = "模拟匹配")
+    private String userIp;
+
+    @ApiModelProperty(value = "用户账号", example = "芋道", notes = "模拟匹配")
+    private String username;
+
+    @ApiModelProperty(value = "操作状态", example = "true")
+    private Boolean status;
+
+    @ApiModelProperty(value = "开始时间", example = "2020-10-24")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private Date beginTime;
+
+    @ApiModelProperty(value = "结束时间", example = "2020-10-24")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private Date endTime;
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/vo/loginlog/SysLoginLogRespVO.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/vo/loginlog/SysLoginLogRespVO.java
index e3e3f9de1..f9e2664f8 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/vo/loginlog/SysLoginLogRespVO.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/logger/vo/loginlog/SysLoginLogRespVO.java
@@ -8,7 +8,7 @@ import lombok.ToString;
 
 import java.util.Date;
 
-@ApiModel("操作日志 Response VO")
+@ApiModel("登陆日志 Response VO")
 @Data
 @EqualsAndHashCode(callSuper = true)
 @ToString(callSuper = true)
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/convert/logger/SysLoginLogConvert.java b/src/main/java/cn/iocoder/dashboard/modules/system/convert/logger/SysLoginLogConvert.java
index 301fff469..71a7e5093 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/convert/logger/SysLoginLogConvert.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/convert/logger/SysLoginLogConvert.java
@@ -1,6 +1,8 @@
 package cn.iocoder.dashboard.modules.system.convert.logger;
 
+import cn.iocoder.dashboard.common.pojo.PageResult;
 import cn.iocoder.dashboard.modules.system.controller.logger.vo.loginlog.SysLoginLogCreateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.logger.vo.loginlog.SysLoginLogRespVO;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.logger.SysLoginLogDO;
 import org.mapstruct.Mapper;
 import org.mapstruct.factory.Mappers;
@@ -12,4 +14,6 @@ public interface SysLoginLogConvert {
 
     SysLoginLogDO convert(SysLoginLogCreateReqVO bean);
 
+    PageResult<SysLoginLogRespVO> convertPage(PageResult<SysLoginLogDO> page);
+
 }
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/logger/SysLoginLogMapper.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/logger/SysLoginLogMapper.java
index 5f0b0904c..8d7b0a944 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/logger/SysLoginLogMapper.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/logger/SysLoginLogMapper.java
@@ -1,9 +1,28 @@
 package cn.iocoder.dashboard.modules.system.dal.mysql.dao.logger;
 
+import cn.iocoder.dashboard.common.pojo.PageResult;
 import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.dashboard.framework.mybatis.core.query.QueryWrapperX;
+import cn.iocoder.dashboard.modules.system.controller.logger.vo.loginlog.SysLoginLogPageReqVO;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.logger.SysLoginLogDO;
+import cn.iocoder.dashboard.modules.system.enums.logger.SysLoginResultEnum;
 import org.apache.ibatis.annotations.Mapper;
 
 @Mapper
 public interface SysLoginLogMapper extends BaseMapperX<SysLoginLogDO> {
+
+    default PageResult<SysLoginLogDO> selectPage(SysLoginLogPageReqVO reqVO) {
+        QueryWrapperX<SysLoginLogDO> query = new QueryWrapperX<SysLoginLogDO>()
+                .likeIfPresent("user_ip", reqVO.getUserIp())
+                .likeIfPresent("username", reqVO.getUsername())
+                .betweenIfPresent("create_time", reqVO.getBeginTime(), reqVO.getEndTime());
+        if (Boolean.TRUE.equals(reqVO.getStatus())) {
+            query.eq("result", SysLoginResultEnum.SUCCESS.getResult());
+        } else if (Boolean.FALSE.equals(reqVO.getStatus())) {
+            query.gt("result", SysLoginResultEnum.SUCCESS.getResult());
+        }
+        query.orderByDesc("id"); // 降序
+        return selectPage(reqVO, query);
+    }
+
 }
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/enums/logger/SysLoginResultEnum.java b/src/main/java/cn/iocoder/dashboard/modules/system/enums/logger/SysLoginResultEnum.java
index 4a26a7243..96887fb7e 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/enums/logger/SysLoginResultEnum.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/enums/logger/SysLoginResultEnum.java
@@ -12,7 +12,7 @@ public enum SysLoginResultEnum {
 
     SUCCESS(0), // 成功
     BAD_CREDENTIALS(10), // 账号或密码不正确
-    USER_DISABLED(20), // 账号或密码不正确
+    USER_DISABLED(20), // 用户被禁用
     CAPTCHA_NOT_FOUND(30), // 验证码不存在
     CAPTCHA_CODE_ERROR(31), // 验证码不正确
 
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/logger/SysLoginLogService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/logger/SysLoginLogService.java
index adda74456..d6c67569f 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/logger/SysLoginLogService.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/logger/SysLoginLogService.java
@@ -1,6 +1,9 @@
 package cn.iocoder.dashboard.modules.system.service.logger;
 
+import cn.iocoder.dashboard.common.pojo.PageResult;
 import cn.iocoder.dashboard.modules.system.controller.logger.vo.loginlog.SysLoginLogCreateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.logger.vo.loginlog.SysLoginLogPageReqVO;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.logger.SysLoginLogDO;
 
 /**
  * 登陆日志 Service 接口
@@ -14,4 +17,12 @@ public interface SysLoginLogService {
      */
     void createLoginLog(SysLoginLogCreateReqVO reqVO);
 
+    /**
+     * 获得登陆日志分页
+     *
+     * @param reqVO 分页条件
+     * @return 登陆日志分页
+     */
+    PageResult<SysLoginLogDO> getLoginLogPage(SysLoginLogPageReqVO reqVO);
+
 }
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/logger/impl/SysLoginLogServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/logger/impl/SysLoginLogServiceImpl.java
index 36c41e31a..9f55354d1 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/logger/impl/SysLoginLogServiceImpl.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/logger/impl/SysLoginLogServiceImpl.java
@@ -1,13 +1,22 @@
 package cn.iocoder.dashboard.modules.system.service.logger.impl;
 
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.dashboard.common.pojo.PageResult;
 import cn.iocoder.dashboard.modules.system.controller.logger.vo.loginlog.SysLoginLogCreateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.logger.vo.loginlog.SysLoginLogPageReqVO;
 import cn.iocoder.dashboard.modules.system.convert.logger.SysLoginLogConvert;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dao.logger.SysLoginLogMapper;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.logger.SysLoginLogDO;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
 import cn.iocoder.dashboard.modules.system.service.logger.SysLoginLogService;
+import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
+import java.util.Collection;
+
+import static cn.iocoder.dashboard.util.collection.CollectionUtils.convertSet;
 
 /**
  * 登陆日志 Service 实现
@@ -18,10 +27,18 @@ public class SysLoginLogServiceImpl implements SysLoginLogService {
     @Resource
     private SysLoginLogMapper loginLogMapper;
 
+    @Resource
+    private SysUserService userService;
+
     @Override
     public void createLoginLog(SysLoginLogCreateReqVO reqVO) {
         SysLoginLogDO loginLog = SysLoginLogConvert.INSTANCE.convert(reqVO);
         loginLogMapper.insert(loginLog);
     }
 
+    @Override
+    public PageResult<SysLoginLogDO> getLoginLogPage(SysLoginLogPageReqVO reqVO) {
+        return loginLogMapper.selectPage(reqVO);
+    }
+
 }