feat: 移动端完善与操作日志审计功能
1. 移动端fund-mobile完善: - 新增项目列表页面 (project/List.vue) - 新增客户列表页面 (customer/List.vue) - 新增统一API文件 (api/index.ts) - 更新路由配置,新增项目和客户路由 - 首页增加项目和客户快捷入口 2. 操作日志审计功能: - OperationLog实体类: 操作日志数据模型 - OperationLogMapper: MyBatis-Plus Mapper - OperationLogService: 日志服务接口和实现 - OperationLogController: 日志查询API - OperationLogAspect: AOP切面自动记录操作日志 - 支持异步保存,只记录写操作(增删改) 3. 操作日志功能特性: - 自动拦截Controller层方法 - 记录用户ID、用户名、操作描述、请求参数 - 记录IP、UserAgent、操作时间、耗时 - 支持成功/失败状态记录 - 支持分页查询和历史日志清理
This commit is contained in:
parent
eafb783e2b
commit
47703e40c4
68
fund-mobile/src/api/index.ts
Normal file
68
fund-mobile/src/api/index.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import request from './request'
|
||||||
|
|
||||||
|
// 用户认证
|
||||||
|
export function login(data: { username: string; password: string }) {
|
||||||
|
return request.post('/sys/api/v1/auth/login', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUserInfo() {
|
||||||
|
return request.get('/sys/api/v1/auth/info')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function logout() {
|
||||||
|
return request.post('/sys/api/v1/auth/logout')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 项目管理
|
||||||
|
export function getProjectList(params?: { pageNum: number; pageSize: number; projectName?: string }) {
|
||||||
|
return request.get('/proj/api/v1/project/page', { params })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getProjectById(id: number) {
|
||||||
|
return request.get(`/proj/api/v1/project/${id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 客户管理
|
||||||
|
export function getCustomerList(params?: { pageNum: number; pageSize: number; customerName?: string }) {
|
||||||
|
return request.get('/cust/api/v1/customer/page', { params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支出管理
|
||||||
|
export function createExpense(data: any) {
|
||||||
|
return request.post('/exp/api/v1/exp/expense', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getExpenseList(params: { pageNum: number; pageSize: number }) {
|
||||||
|
return request.get('/exp/api/v1/exp/expense/page', { params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应收款管理
|
||||||
|
export function getReceivableList(params: { pageNum: number; pageSize: number; status?: string }) {
|
||||||
|
return request.get('/receipt/api/v1/receipt/receivable/page', { params })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUpcomingDueList(daysWithin: number = 7) {
|
||||||
|
return request.get(`/receipt/api/v1/receipt/receivable/upcoming-due?daysWithin=${daysWithin}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统计数据
|
||||||
|
export function getTodayIncome() {
|
||||||
|
return request.get('/receipt/api/v1/receipt/receivable/stats/today-income')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTodayExpense() {
|
||||||
|
return request.get('/exp/api/v1/exp/expense/stats/today-expense')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUnpaidAmount() {
|
||||||
|
return request.get('/receipt/api/v1/receipt/receivable/stats/unpaid-amount')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getOverdueCount() {
|
||||||
|
return request.get('/receipt/api/v1/receipt/receivable/stats/overdue-count')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支出类型
|
||||||
|
export function getExpenseTypeTree() {
|
||||||
|
return request.get('/exp/api/v1/exp/expense-type/tree')
|
||||||
|
}
|
||||||
@ -21,6 +21,18 @@ const router = createRouter({
|
|||||||
component: () => import('@/views/receivable/List.vue'),
|
component: () => import('@/views/receivable/List.vue'),
|
||||||
meta: { title: '应收款列表', requiresAuth: true }
|
meta: { title: '应收款列表', requiresAuth: true }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/project',
|
||||||
|
name: 'ProjectList',
|
||||||
|
component: () => import('@/views/project/List.vue'),
|
||||||
|
meta: { title: '项目列表', requiresAuth: true }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/customer',
|
||||||
|
name: 'CustomerList',
|
||||||
|
component: () => import('@/views/customer/List.vue'),
|
||||||
|
meta: { title: '客户列表', requiresAuth: true }
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/my',
|
path: '/my',
|
||||||
name: 'My',
|
name: 'My',
|
||||||
|
|||||||
@ -61,6 +61,18 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="quick-text">应收款</div>
|
<div class="quick-text">应收款</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="quick-item" @click="$router.push('/project')">
|
||||||
|
<div class="quick-icon">
|
||||||
|
<van-icon name="todo-list-o" />
|
||||||
|
</div>
|
||||||
|
<div class="quick-text">项目</div>
|
||||||
|
</div>
|
||||||
|
<div class="quick-item" @click="$router.push('/customer')">
|
||||||
|
<div class="quick-icon">
|
||||||
|
<van-icon name="friends-o" />
|
||||||
|
</div>
|
||||||
|
<div class="quick-text">客户</div>
|
||||||
|
</div>
|
||||||
<div class="quick-item" @click="$router.push('/my')">
|
<div class="quick-item" @click="$router.push('/my')">
|
||||||
<div class="quick-icon">
|
<div class="quick-icon">
|
||||||
<van-icon name="user-o" />
|
<van-icon name="user-o" />
|
||||||
|
|||||||
186
fund-mobile/src/views/customer/List.vue
Normal file
186
fund-mobile/src/views/customer/List.vue
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page customer-list">
|
||||||
|
<van-nav-bar title="客户列表" left-arrow @click-left="$router.back()" />
|
||||||
|
|
||||||
|
<!-- 搜索栏 -->
|
||||||
|
<div class="search-bar">
|
||||||
|
<van-search v-model="searchText" placeholder="搜索客户名称" @search="handleSearch" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 客户列表 -->
|
||||||
|
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
|
||||||
|
<van-list
|
||||||
|
v-model:loading="loading"
|
||||||
|
:finished="finished"
|
||||||
|
finished-text="没有更多了"
|
||||||
|
@load="onLoad"
|
||||||
|
>
|
||||||
|
<div class="customer-card" v-for="item in list" :key="item.customerId">
|
||||||
|
<div class="customer-header">
|
||||||
|
<div class="customer-avatar">{{ item.customerName?.charAt(0) || 'C' }}</div>
|
||||||
|
<div class="customer-info">
|
||||||
|
<div class="customer-name">{{ item.customerName }}</div>
|
||||||
|
<div class="customer-short">{{ item.customerShort || '-' }}</div>
|
||||||
|
</div>
|
||||||
|
<van-tag :type="getLevelType(item.level)">{{ getLevelText(item.level) }}</van-tag>
|
||||||
|
</div>
|
||||||
|
<div class="customer-detail">
|
||||||
|
<div class="detail-item" v-if="item.phone">
|
||||||
|
<van-icon name="phone-o" />
|
||||||
|
<span>{{ item.phone }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-item" v-if="item.industry">
|
||||||
|
<van-icon name="cluster-o" />
|
||||||
|
<span>{{ item.industry }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</van-list>
|
||||||
|
</van-pull-refresh>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { getCustomerList } from '@/api'
|
||||||
|
|
||||||
|
const searchText = ref('')
|
||||||
|
const loading = ref(false)
|
||||||
|
const finished = ref(false)
|
||||||
|
const refreshing = ref(false)
|
||||||
|
const list = ref<any[]>([])
|
||||||
|
const pageNum = ref(1)
|
||||||
|
const pageSize = 10
|
||||||
|
|
||||||
|
const getLevelType = (level: string): 'primary' | 'success' | 'warning' | 'danger' | 'default' => {
|
||||||
|
const map: Record<string, 'primary' | 'success' | 'warning' | 'danger' | 'default'> = {
|
||||||
|
'A': 'primary',
|
||||||
|
'B': 'success',
|
||||||
|
'C': 'warning',
|
||||||
|
'D': 'danger'
|
||||||
|
}
|
||||||
|
return map[level] || 'default'
|
||||||
|
}
|
||||||
|
|
||||||
|
const getLevelText = (level: string) => {
|
||||||
|
const map: Record<string, string> = {
|
||||||
|
'A': 'A类',
|
||||||
|
'B': 'B类',
|
||||||
|
'C': 'C类',
|
||||||
|
'D': 'D类'
|
||||||
|
}
|
||||||
|
return map[level] || '普通'
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadData = async () => {
|
||||||
|
try {
|
||||||
|
const res: any = await getCustomerList({
|
||||||
|
pageNum: pageNum.value,
|
||||||
|
pageSize,
|
||||||
|
customerName: searchText.value || undefined
|
||||||
|
})
|
||||||
|
const records = res.data?.records || []
|
||||||
|
if (pageNum.value === 1) {
|
||||||
|
list.value = records
|
||||||
|
} else {
|
||||||
|
list.value.push(...records)
|
||||||
|
}
|
||||||
|
finished.value = records.length < pageSize
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
finished.value = true
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
refreshing.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onLoad = () => {
|
||||||
|
pageNum.value++
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onRefresh = () => {
|
||||||
|
pageNum.value = 1
|
||||||
|
finished.value = false
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSearch = () => {
|
||||||
|
pageNum.value = 1
|
||||||
|
finished.value = false
|
||||||
|
list.value = []
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.customer-list {
|
||||||
|
background: #f5f5f5;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar {
|
||||||
|
background: #fff;
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-card {
|
||||||
|
background: #fff;
|
||||||
|
margin: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-avatar {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: linear-gradient(135deg, #007AFF, #5AC8FA);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-name {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-short {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-detail {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
margin-top: 12px;
|
||||||
|
padding-top: 12px;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
205
fund-mobile/src/views/project/List.vue
Normal file
205
fund-mobile/src/views/project/List.vue
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page project-list">
|
||||||
|
<van-nav-bar title="项目列表" left-arrow @click-left="$router.back()" />
|
||||||
|
|
||||||
|
<!-- 搜索栏 -->
|
||||||
|
<div class="search-bar">
|
||||||
|
<van-search v-model="searchText" placeholder="搜索项目名称" @search="handleSearch" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 项目列表 -->
|
||||||
|
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
|
||||||
|
<van-list
|
||||||
|
v-model:loading="loading"
|
||||||
|
:finished="finished"
|
||||||
|
finished-text="没有更多了"
|
||||||
|
@load="onLoad"
|
||||||
|
>
|
||||||
|
<div class="project-card" v-for="item in list" :key="item.projectId" @click="goDetail(item)">
|
||||||
|
<div class="project-header">
|
||||||
|
<span class="project-name">{{ item.projectName }}</span>
|
||||||
|
<van-tag :type="getStatusType(item.status)">{{ getStatusText(item.status) }}</van-tag>
|
||||||
|
</div>
|
||||||
|
<div class="project-info">
|
||||||
|
<div class="info-item">
|
||||||
|
<van-icon name="user-o" />
|
||||||
|
<span>{{ item.customerName || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<van-icon name="clock-o" />
|
||||||
|
<span>{{ item.startDate }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="project-amount">
|
||||||
|
<div class="amount-item">
|
||||||
|
<span class="label">合同金额</span>
|
||||||
|
<span class="value">{{ formatMoney(item.contractAmount) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="amount-item">
|
||||||
|
<span class="label">预算金额</span>
|
||||||
|
<span class="value">{{ formatMoney(item.budgetAmount) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</van-list>
|
||||||
|
</van-pull-refresh>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { getProjectList } from '@/api'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const searchText = ref('')
|
||||||
|
const loading = ref(false)
|
||||||
|
const finished = ref(false)
|
||||||
|
const refreshing = ref(false)
|
||||||
|
const list = ref<any[]>([])
|
||||||
|
const pageNum = ref(1)
|
||||||
|
const pageSize = 10
|
||||||
|
|
||||||
|
const getStatusType = (status: string): 'primary' | 'success' | 'warning' | 'danger' | 'default' => {
|
||||||
|
const map: Record<string, 'primary' | 'success' | 'warning' | 'danger' | 'default'> = {
|
||||||
|
'ongoing': 'primary',
|
||||||
|
'completed': 'success',
|
||||||
|
'preparing': 'warning',
|
||||||
|
'archived': 'default',
|
||||||
|
'cancelled': 'danger'
|
||||||
|
}
|
||||||
|
return map[status] || 'default'
|
||||||
|
}
|
||||||
|
|
||||||
|
const getStatusText = (status: string) => {
|
||||||
|
const map: Record<string, string> = {
|
||||||
|
'ongoing': '进行中',
|
||||||
|
'completed': '已完成',
|
||||||
|
'preparing': '筹备中',
|
||||||
|
'archived': '已归档',
|
||||||
|
'cancelled': '已取消'
|
||||||
|
}
|
||||||
|
return map[status] || status
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatMoney = (value: number) => {
|
||||||
|
if (!value) return '¥0'
|
||||||
|
return '¥' + value.toLocaleString()
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadData = async () => {
|
||||||
|
try {
|
||||||
|
const res: any = await getProjectList({
|
||||||
|
pageNum: pageNum.value,
|
||||||
|
pageSize,
|
||||||
|
projectName: searchText.value || undefined
|
||||||
|
})
|
||||||
|
const records = res.data?.records || []
|
||||||
|
if (pageNum.value === 1) {
|
||||||
|
list.value = records
|
||||||
|
} else {
|
||||||
|
list.value.push(...records)
|
||||||
|
}
|
||||||
|
finished.value = records.length < pageSize
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
finished.value = true
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
refreshing.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onLoad = () => {
|
||||||
|
pageNum.value++
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onRefresh = () => {
|
||||||
|
pageNum.value = 1
|
||||||
|
finished.value = false
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSearch = () => {
|
||||||
|
pageNum.value = 1
|
||||||
|
finished.value = false
|
||||||
|
list.value = []
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const goDetail = (item: any) => {
|
||||||
|
router.push(`/project/${item.projectId}`)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.project-list {
|
||||||
|
background: #f5f5f5;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar {
|
||||||
|
background: #fff;
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card {
|
||||||
|
background: #fff;
|
||||||
|
margin: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-name {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-info {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-amount {
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
padding-top: 12px;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-item .label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-item .value {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
package com.fundplatform.sys.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.fundplatform.common.core.Result;
|
||||||
|
import com.fundplatform.sys.data.entity.OperationLog;
|
||||||
|
import com.fundplatform.sys.service.OperationLogService;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作日志Controller
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/log")
|
||||||
|
public class OperationLogController {
|
||||||
|
|
||||||
|
private final OperationLogService operationLogService;
|
||||||
|
|
||||||
|
public OperationLogController(OperationLogService operationLogService) {
|
||||||
|
this.operationLogService = operationLogService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询操作日志
|
||||||
|
*/
|
||||||
|
@GetMapping("/page")
|
||||||
|
public Result<Page<OperationLog>> page(
|
||||||
|
@RequestParam(defaultValue = "1") int pageNum,
|
||||||
|
@RequestParam(defaultValue = "10") int pageSize,
|
||||||
|
@RequestParam(required = false) Long userId,
|
||||||
|
@RequestParam(required = false) String operation,
|
||||||
|
@RequestParam(required = false) String startTime,
|
||||||
|
@RequestParam(required = false) String endTime) {
|
||||||
|
return Result.success(operationLogService.pageLogs(pageNum, pageSize, userId, operation, startTime, endTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取日志详情
|
||||||
|
*/
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Result<OperationLog> getById(@PathVariable Long id) {
|
||||||
|
return Result.success(operationLogService.getById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理历史日志
|
||||||
|
*/
|
||||||
|
@DeleteMapping("/clean")
|
||||||
|
public Result<Integer> cleanLogs(@RequestParam(defaultValue = "90") int days) {
|
||||||
|
return Result.success(operationLogService.cleanLogs(days));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,135 @@
|
|||||||
|
package com.fundplatform.sys.data.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作日志实体
|
||||||
|
*/
|
||||||
|
@TableName("sys_operation_log")
|
||||||
|
public class OperationLog {
|
||||||
|
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long logId;
|
||||||
|
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
private String operation;
|
||||||
|
|
||||||
|
private String method;
|
||||||
|
|
||||||
|
private String params;
|
||||||
|
|
||||||
|
private String ip;
|
||||||
|
|
||||||
|
private String userAgent;
|
||||||
|
|
||||||
|
private LocalDateTime operationTime;
|
||||||
|
|
||||||
|
private Long costTime;
|
||||||
|
|
||||||
|
private String result;
|
||||||
|
|
||||||
|
private String errorMsg;
|
||||||
|
|
||||||
|
public Long getLogId() {
|
||||||
|
return logId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogId(Long logId) {
|
||||||
|
this.logId = logId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserId(Long userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOperation() {
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOperation(String operation) {
|
||||||
|
this.operation = operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMethod() {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMethod(String method) {
|
||||||
|
this.method = method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getParams() {
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParams(String params) {
|
||||||
|
this.params = params;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIp() {
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIp(String ip) {
|
||||||
|
this.ip = ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserAgent() {
|
||||||
|
return userAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserAgent(String userAgent) {
|
||||||
|
this.userAgent = userAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getOperationTime() {
|
||||||
|
return operationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOperationTime(LocalDateTime operationTime) {
|
||||||
|
this.operationTime = operationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getCostTime() {
|
||||||
|
return costTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCostTime(Long costTime) {
|
||||||
|
this.costTime = costTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResult() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResult(String result) {
|
||||||
|
this.result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getErrorMsg() {
|
||||||
|
return errorMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrorMsg(String errorMsg) {
|
||||||
|
this.errorMsg = errorMsg;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
package com.fundplatform.sys.data.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.fundplatform.sys.data.entity.OperationLog;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作日志Mapper
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface OperationLogMapper extends BaseMapper<OperationLog> {
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package com.fundplatform.sys.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.fundplatform.sys.data.entity.OperationLog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作日志服务接口
|
||||||
|
*/
|
||||||
|
public interface OperationLogService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录操作日志
|
||||||
|
*/
|
||||||
|
void saveLog(OperationLog log);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询操作日志
|
||||||
|
*/
|
||||||
|
Page<OperationLog> pageLogs(int pageNum, int pageSize, Long userId, String operation, String startTime, String endTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID查询日志详情
|
||||||
|
*/
|
||||||
|
OperationLog getById(Long logId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理指定天数前的日志
|
||||||
|
*/
|
||||||
|
int cleanLogs(int days);
|
||||||
|
}
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
package com.fundplatform.sys.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.fundplatform.sys.data.entity.OperationLog;
|
||||||
|
import com.fundplatform.sys.data.mapper.OperationLogMapper;
|
||||||
|
import com.fundplatform.sys.service.OperationLogService;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作日志服务实现
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class OperationLogServiceImpl implements OperationLogService {
|
||||||
|
|
||||||
|
private final OperationLogMapper operationLogMapper;
|
||||||
|
|
||||||
|
public OperationLogServiceImpl(OperationLogMapper operationLogMapper) {
|
||||||
|
this.operationLogMapper = operationLogMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Async
|
||||||
|
public void saveLog(OperationLog log) {
|
||||||
|
if (log.getOperationTime() == null) {
|
||||||
|
log.setOperationTime(LocalDateTime.now());
|
||||||
|
}
|
||||||
|
operationLogMapper.insert(log);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<OperationLog> pageLogs(int pageNum, int pageSize, Long userId, String operation, String startTime, String endTime) {
|
||||||
|
Page<OperationLog> page = new Page<>(pageNum, pageSize);
|
||||||
|
LambdaQueryWrapper<OperationLog> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
|
||||||
|
if (userId != null) {
|
||||||
|
wrapper.eq(OperationLog::getUserId, userId);
|
||||||
|
}
|
||||||
|
if (operation != null && !operation.isEmpty()) {
|
||||||
|
wrapper.like(OperationLog::getOperation, operation);
|
||||||
|
}
|
||||||
|
if (startTime != null && !startTime.isEmpty()) {
|
||||||
|
LocalDate startDate = LocalDate.parse(startTime);
|
||||||
|
wrapper.ge(OperationLog::getOperationTime, LocalDateTime.of(startDate, LocalTime.MIN));
|
||||||
|
}
|
||||||
|
if (endTime != null && !endTime.isEmpty()) {
|
||||||
|
LocalDate endDate = LocalDate.parse(endTime);
|
||||||
|
wrapper.le(OperationLog::getOperationTime, LocalDateTime.of(endDate, LocalTime.MAX));
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper.orderByDesc(OperationLog::getOperationTime);
|
||||||
|
return operationLogMapper.selectPage(page, wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OperationLog getById(Long logId) {
|
||||||
|
return operationLogMapper.selectById(logId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int cleanLogs(int days) {
|
||||||
|
LocalDateTime threshold = LocalDateTime.now().minusDays(days);
|
||||||
|
LambdaQueryWrapper<OperationLog> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.lt(OperationLog::getOperationTime, threshold);
|
||||||
|
return operationLogMapper.delete(wrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
0
fund-sys/src/main/resources/db/operation_log.sql
Normal file
0
fund-sys/src/main/resources/db/operation_log.sql
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
0
fund-sys/target/classes/db/operation_log.sql
Normal file
0
fund-sys/target/classes/db/operation_log.sql
Normal file
@ -30,6 +30,7 @@
|
|||||||
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/vo/UserVO.java
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/vo/UserVO.java
|
||||||
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/data/service/SysTenantDataService.java
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/data/service/SysTenantDataService.java
|
||||||
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/service/DeptService.java
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/service/DeptService.java
|
||||||
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/data/entity/OperationLog.java
|
||||||
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/dto/MenuDTO.java
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/dto/MenuDTO.java
|
||||||
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/data/service/SysConfigDataService.java
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/data/service/SysConfigDataService.java
|
||||||
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/controller/HealthController.java
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/controller/HealthController.java
|
||||||
@ -39,9 +40,12 @@
|
|||||||
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/controller/RoleController.java
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/controller/RoleController.java
|
||||||
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/controller/TenantController.java
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/controller/TenantController.java
|
||||||
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/service/impl/ConfigServiceImpl.java
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/service/impl/ConfigServiceImpl.java
|
||||||
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/controller/OperationLogController.java
|
||||||
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/data/mapper/OperationLogMapper.java
|
||||||
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/data/entity/SysMenu.java
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/data/entity/SysMenu.java
|
||||||
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/service/impl/DeptServiceImpl.java
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/service/impl/DeptServiceImpl.java
|
||||||
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/utils/JwtUtil.java
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/utils/JwtUtil.java
|
||||||
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/service/OperationLogService.java
|
||||||
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/aop/ApiLogAspect.java
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/aop/ApiLogAspect.java
|
||||||
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/service/AuthService.java
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/service/AuthService.java
|
||||||
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/dto/RoleDTO.java
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/dto/RoleDTO.java
|
||||||
@ -59,7 +63,9 @@
|
|||||||
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/controller/TestController.java
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/controller/TestController.java
|
||||||
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/vo/DeptVO.java
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/vo/DeptVO.java
|
||||||
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/data/mapper/SysRoleMapper.java
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/data/mapper/SysRoleMapper.java
|
||||||
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/aop/OperationLogAspect.java
|
||||||
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/service/impl/MenuServiceImpl.java
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/service/impl/MenuServiceImpl.java
|
||||||
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/SysApplication.java
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/SysApplication.java
|
||||||
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/dto/LoginRequestDTO.java
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/dto/LoginRequestDTO.java
|
||||||
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/service/impl/OperationLogServiceImpl.java
|
||||||
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/service/TenantService.java
|
/home/along/MyCode/wanjiabuluo/fundplatform/fund-sys/src/main/java/com/fundplatform/sys/service/TenantService.java
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user