feat(constool): 添加数据对比同步工具并重构动态表格构建器
- 新增 KitDiffSyncConstTool 工具类,支持集合数据的对比同步功能 - 添加数据同步回调接口和完整的增删改查批量操作支持 - 重命名 KitDynamicTableUtil 为 KitDynamicTableBuilder 并调整方法名称 - 修改 renderOrder 方法为 ofOrder,renderAll 方法为 ofAll - 更新前端 FormatDuration 函数,支持年、天、时、分、秒格式化显示 - 添加字节格式化方法 renderBytes 到 KitStringUtil 工具类 - 在首页视图中集成 Pipeline 组件用于展示任务流程 - 添加窗口打开工具函数 OpenWindowInCurrent 和 OpenWindowInNew - 创建 MybatisMapperBatchOpRender 批量操作生成器
This commit is contained in:
@@ -273,3 +273,11 @@ export function IsNotBlank(str) {
|
|||||||
export function IsBlank(str) {
|
export function IsBlank(str) {
|
||||||
return str == null || String(str).trim().length === 0
|
return str == null || String(str).trim().length === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function OpenWindowInCurrent(url) {
|
||||||
|
window.location.href = url
|
||||||
|
}
|
||||||
|
|
||||||
|
export function OpenWindowInNew(url) {
|
||||||
|
window.open(url, '_blank')
|
||||||
|
}
|
||||||
|
|||||||
@@ -390,22 +390,31 @@ export function FormatEnabled(value) {
|
|||||||
*/
|
*/
|
||||||
export function FormatDuration(value) {
|
export function FormatDuration(value) {
|
||||||
if (value === null || value === undefined || value === '') {
|
if (value === null || value === undefined || value === '') {
|
||||||
return '-'
|
return ''
|
||||||
}
|
}
|
||||||
const seconds = Number(value)
|
const seconds = Number(value / 1000)
|
||||||
if (isNaN(seconds)) {
|
if (isNaN(seconds)) {
|
||||||
return '-'
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const hours = Math.floor(seconds / 3600)
|
const years = Math.floor(seconds / (365 * 24 * 3600))
|
||||||
|
const days = Math.floor((seconds % (365 * 24 * 3600)) / (24 * 3600))
|
||||||
|
const hours = Math.floor((seconds % (24 * 3600)) / 3600)
|
||||||
const minutes = Math.floor((seconds % 3600) / 60)
|
const minutes = Math.floor((seconds % 3600) / 60)
|
||||||
const secs = seconds % 60
|
const secs = (seconds % 60).toFixed(0)
|
||||||
|
|
||||||
if (hours > 0) {
|
if (years > 0) {
|
||||||
return `${hours}小时${minutes}分${secs}秒`
|
return `${years}年${days}天${hours}时${minutes}分${secs}秒`
|
||||||
|
} else if (days > 0) {
|
||||||
|
return `${days}天${hours}时${minutes}分${secs}秒`
|
||||||
|
} else if (hours > 0) {
|
||||||
|
return `${hours}时${minutes}分${secs}秒`
|
||||||
} else if (minutes > 0) {
|
} else if (minutes > 0) {
|
||||||
return `${minutes}分${secs}秒`
|
return `${minutes}分${secs}秒`
|
||||||
} else {
|
} else {
|
||||||
|
if (secs === '0') {
|
||||||
|
return ``
|
||||||
|
}
|
||||||
return `${secs}秒`
|
return `${secs}秒`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,317 @@
|
|||||||
|
<template>
|
||||||
|
<div :style="taskWindowStyle">
|
||||||
|
<div :style="taskHeaderStyle" />
|
||||||
|
<div :style="taskBodyStyle">
|
||||||
|
<!-- 标题和图标 -->
|
||||||
|
<el-row :gutter="2">
|
||||||
|
<el-col :span="16">{{ name }}</el-col>
|
||||||
|
<el-col :span="8" style="text-align: right">
|
||||||
|
<i :class="getStatusIconClassByStatus(status)" :style="getStatusIconColorByStatus(status)" />
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<!-- 分割线 -->
|
||||||
|
<div style="padding-left: 10px; padding-right: 10px; padding-top: 2px; border-bottom: 1px solid #EBEEF5" />
|
||||||
|
<!-- 任务执行信息 -->
|
||||||
|
<div
|
||||||
|
v-if="click === false"
|
||||||
|
style="width: 100%; height: 40px; line-height: 30px; font-size: 12px; color: #606266;
|
||||||
|
text-align: center;padding: 9px 0;font-family: Arial, sans-serif"
|
||||||
|
>
|
||||||
|
{{ desc }}
|
||||||
|
</div>
|
||||||
|
<el-button
|
||||||
|
v-else
|
||||||
|
v-prevent-re-click
|
||||||
|
type="text"
|
||||||
|
style="width: 100%; height: 40px; line-height: 30px; font-size: 12px; color: #606266"
|
||||||
|
@click="onTaskDetailClick"
|
||||||
|
>
|
||||||
|
{{ desc }}
|
||||||
|
</el-button>
|
||||||
|
<el-row :gutter="2">
|
||||||
|
<!-- 持续时间 -->
|
||||||
|
<el-col :span="15" style="font-size: 12px;color: #606266;height: 32px;max-height: 32px;line-height: 32px">
|
||||||
|
{{ FormatDuration(status !== statusMap.running ? durationMillis : new Date().getTime() - startTimeMillis) }}
|
||||||
|
</el-col>
|
||||||
|
<!-- 任务失败,且支持重试 -->
|
||||||
|
<el-col v-if="status === statusMap.failed && retry === true" :span="9" style="font-size: 12px;color: #606266;height: 32px;max-height: 32px">
|
||||||
|
<el-button v-prevent-re-click type="text" @click="onRetryClick">重试</el-button>
|
||||||
|
</el-col>
|
||||||
|
<!-- 任务运行中,且包含按钮组 -->
|
||||||
|
<el-col v-if="buttonList && buttonList.length > 0" :span="9" style="font-size: 12px;color: #606266;height: 32px;max-height: 32px;padding-right: 5px">
|
||||||
|
<el-button
|
||||||
|
v-for="(item, index) in buttonList"
|
||||||
|
:key="index"
|
||||||
|
v-prevent-re-click
|
||||||
|
type="text"
|
||||||
|
:style="`width: ${item.text && item.text.length >= 4 ? '74px': '26px'
|
||||||
|
};color: ${item.color ? item.color : statusColorMap.running};display: ${item.condition ===
|
||||||
|
'all' ?
|
||||||
|
'inline-block':
|
||||||
|
(item.condition === status ? 'inline-block': 'none')};overflow: hidden;text-overflow: ellipsis;`"
|
||||||
|
@click="onButtonNodeClick(item)"
|
||||||
|
>{{ item.text }}</el-button>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
<!-- 任务节点明细 -->
|
||||||
|
<cute-preview-drawer ref="taskDetailDrawer" :title="name">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item
|
||||||
|
v-for="(taskDetail, index) in taskDetails"
|
||||||
|
:key="index"
|
||||||
|
:color="getColorHexByStatus(taskDetail.status)"
|
||||||
|
:timestamp="taskDetail.timestamp"
|
||||||
|
>
|
||||||
|
{{ taskDetail.content }}
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
</cute-preview-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import KitMessage from '@/utils/elementui/KitMessage'
|
||||||
|
import CutePreviewDrawer from '@/views/components/dev/CutePreviewDrawer.vue'
|
||||||
|
import { OpenWindowInCurrent, OpenWindowInNew } from '@/utils/KitUtil'
|
||||||
|
import request from '@/utils/request'
|
||||||
|
import { FormatDuration } from '@/utils/filters'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PipeTask',
|
||||||
|
components: { CutePreviewDrawer },
|
||||||
|
props: {
|
||||||
|
/**
|
||||||
|
* 任务名称
|
||||||
|
*/
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 任务编码
|
||||||
|
*/
|
||||||
|
code: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 开始时间戳。单位:毫秒
|
||||||
|
*/
|
||||||
|
startTimeMillis: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 持续时间。单位:秒
|
||||||
|
*/
|
||||||
|
durationMillis: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 状态
|
||||||
|
*/
|
||||||
|
status: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 状态(翻译)
|
||||||
|
*/
|
||||||
|
statusDesc: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 任务执行信息
|
||||||
|
*/
|
||||||
|
desc: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 是否可以点击
|
||||||
|
*/
|
||||||
|
click: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 是否可以重试
|
||||||
|
*/
|
||||||
|
retry: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 任务明细类型(gitlab、jenkins等)
|
||||||
|
*/
|
||||||
|
detailType: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 按钮组
|
||||||
|
* @property type: execute(请求系统api)、link(调用三方链接跳转)
|
||||||
|
* @property text: 按钮标题
|
||||||
|
* @property color: 按钮颜色
|
||||||
|
* @property condition: 符合条件才展示,当值为`all`时,非pending状态都展示
|
||||||
|
* @property method: 当type=execute时,配置请求方式:GET/POST
|
||||||
|
* @property requestUrl: 当type=execute时,配置请求路径和参数
|
||||||
|
* @property linkUrl: 当type=link时,配置跳转路径和参数
|
||||||
|
* @property isBlank: 当type=link时,判断是否在新页面中打开
|
||||||
|
*/
|
||||||
|
buttonList: {
|
||||||
|
type: Array,
|
||||||
|
required: false,
|
||||||
|
default: function() {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
statusMap: {
|
||||||
|
pending: 'pending',
|
||||||
|
running: 'running',
|
||||||
|
success: 'success',
|
||||||
|
failed: 'failed'
|
||||||
|
},
|
||||||
|
statusColorMap: {
|
||||||
|
pending: '#EBEEF5',
|
||||||
|
success: '#67C23A',
|
||||||
|
failed: '#F56C6C',
|
||||||
|
running: '#409EFF'
|
||||||
|
},
|
||||||
|
// 任务明细
|
||||||
|
taskDetails: [
|
||||||
|
// { timestamp: '2018-04-03 20:46', content: '开始初始化', status: 'success' },
|
||||||
|
// { timestamp: '2018-04-03 20:46', content: '开始初始化', status: 'success' },
|
||||||
|
// { timestamp: '2018-04-03 20:46', content: '开始初始化', status: 'failed' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
taskWindowStyle() {
|
||||||
|
const colorHex = this.getColorHexByStatus(this.status)
|
||||||
|
return {
|
||||||
|
width: '240px',
|
||||||
|
maxWidth: '240px',
|
||||||
|
height: '100px',
|
||||||
|
maxHeight: '100px',
|
||||||
|
border: `1px solid ${colorHex}`,
|
||||||
|
borderRadius: '6px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
display: 'inline-block'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
taskHeaderStyle() {
|
||||||
|
const colorHex = this.getColorHexByStatus(this.status)
|
||||||
|
return {
|
||||||
|
width: '100%',
|
||||||
|
height: '5px',
|
||||||
|
backgroundColor: colorHex
|
||||||
|
}
|
||||||
|
},
|
||||||
|
taskBodyStyle() {
|
||||||
|
return {
|
||||||
|
padding: '5px',
|
||||||
|
fontSize: '14px',
|
||||||
|
backgroundColor: '#FFFFFF',
|
||||||
|
color: '#606266',
|
||||||
|
height: '95px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {},
|
||||||
|
mounted() {
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
FormatDuration,
|
||||||
|
/**
|
||||||
|
* 根据任务状态获取颜色Hex值
|
||||||
|
*/
|
||||||
|
getColorHexByStatus(status) {
|
||||||
|
const colorHex = this.statusColorMap[status]
|
||||||
|
if (colorHex) {
|
||||||
|
return colorHex
|
||||||
|
}
|
||||||
|
return this.statusColorMap.pending
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 根据任务状态渲染状态图标样式
|
||||||
|
* @param status
|
||||||
|
*/
|
||||||
|
getStatusIconClassByStatus(status) {
|
||||||
|
if (this.statusMap.success === status) {
|
||||||
|
return 'el-icon-circle-check'
|
||||||
|
}
|
||||||
|
if (this.statusMap.failed === status) {
|
||||||
|
return 'el-icon-circle-close'
|
||||||
|
}
|
||||||
|
if (this.statusMap.running === status) {
|
||||||
|
return 'el-icon-loading'
|
||||||
|
}
|
||||||
|
return 'el-icon-time'
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 根据任务状态渲染状态图标颜色
|
||||||
|
* @param status
|
||||||
|
*/
|
||||||
|
getStatusIconColorByStatus(status) {
|
||||||
|
const styles = {
|
||||||
|
width: '14px',
|
||||||
|
height: '14px'
|
||||||
|
}
|
||||||
|
styles.color = this.getColorHexByStatus(status)
|
||||||
|
return styles
|
||||||
|
},
|
||||||
|
onTaskDetailClick() {
|
||||||
|
console.error('当任务卡片可点击时,查看明细记录')
|
||||||
|
if (this.statusMap.pending === this.status) {
|
||||||
|
KitMessage.Error('节点任务未开始')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.detailType === 'gitlab') {
|
||||||
|
// 获取gitlab任务明细
|
||||||
|
} else if (this.detailType === 'jenkins') {
|
||||||
|
// 获取jenkins任务明细
|
||||||
|
} else {
|
||||||
|
// 获取业务服务明细
|
||||||
|
}
|
||||||
|
this.$refs.taskDetailDrawer.show()
|
||||||
|
},
|
||||||
|
onRetryClick() {
|
||||||
|
console.error('当任务卡片可重试时,任务重试', this.code)
|
||||||
|
this.$emit('retry', this.code)
|
||||||
|
},
|
||||||
|
async onButtonNodeClick(buttonProps) {
|
||||||
|
console.error('当任务卡片包含按钮组时,某个按钮被单击', buttonProps)
|
||||||
|
if (buttonProps.type === 'execute') {
|
||||||
|
const { method, requestUrl } = buttonProps
|
||||||
|
if (method === 'get' || method === 'post') {
|
||||||
|
const response = await request({ url: requestUrl, method: method })
|
||||||
|
console.error('请求系统内置接口结果:', response)
|
||||||
|
} else {
|
||||||
|
KitMessage.Error(`'${buttonProps.text}' 按钮异常,不支持的请求方式`)
|
||||||
|
}
|
||||||
|
} else if (buttonProps.type === 'link') {
|
||||||
|
const { blank, linkUrl } = buttonProps
|
||||||
|
if (blank) {
|
||||||
|
OpenWindowInNew(linkUrl)
|
||||||
|
} else {
|
||||||
|
OpenWindowInCurrent(linkUrl)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
KitMessage.Error(`'${buttonProps.text}' 按钮异常,不支持的类型`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<template>
|
||||||
|
<cute-horizontal-scroll-bar :width="maxWidth">
|
||||||
|
<i v-for="(item, index) in taskNodes" :key="index">
|
||||||
|
<!-- 最后一个元素如何渲染 -->
|
||||||
|
<i v-if="index === taskNodes.length - 1">
|
||||||
|
<pipe-task v-bind="item" />
|
||||||
|
</i>
|
||||||
|
<!-- 其他元素如何渲染 -->
|
||||||
|
<i v-else class="pipeline-card">
|
||||||
|
<pipe-task v-bind="item" />
|
||||||
|
<div class="pipeline-link" />
|
||||||
|
</i>
|
||||||
|
</i>
|
||||||
|
</cute-horizontal-scroll-bar>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import PipeTask from '@/views/components/devops/pipeline/PipeTask.vue'
|
||||||
|
import CuteHorizontalScrollBar from '@/views/components/dev/CuteHorizontalScrollBar.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Pipeline',
|
||||||
|
components: { CuteHorizontalScrollBar, PipeTask },
|
||||||
|
props: {
|
||||||
|
/**
|
||||||
|
* 流水线全局唯一实例ID
|
||||||
|
*/
|
||||||
|
instanceId: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 流水线任务节点明细
|
||||||
|
*/
|
||||||
|
taskNodes: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
maxWidth() {
|
||||||
|
return `${document.body.clientWidth - 310}px`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {},
|
||||||
|
mounted() {
|
||||||
|
},
|
||||||
|
methods: {}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.pipeline-card {
|
||||||
|
width: 240px;
|
||||||
|
max-width: 240px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pipeline-link {
|
||||||
|
height: 3px;
|
||||||
|
width: 30px;
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #E4E7ED;
|
||||||
|
margin-bottom: 3em;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
@@ -3,11 +3,20 @@
|
|||||||
<github-corner class="github-corner" />
|
<github-corner class="github-corner" />
|
||||||
|
|
||||||
<el-row :gutter="40" style="background-color: white;margin-left: 2px;margin-right: 2px;padding: 6px">
|
<el-row :gutter="40" style="background-color: white;margin-left: 2px;margin-right: 2px;padding: 6px">
|
||||||
<el-col>系统API文档:<el-link type="primary" :href="swaggerApi" target="_blank">{{ swaggerApi }}</el-link></el-col>
|
<el-col>系统API文档:
|
||||||
|
<el-link type="primary" :href="swaggerApi" target="_blank">{{ swaggerApi }}</el-link>
|
||||||
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<panel-group @handleSetLineChartData="handleSetLineChartData" />
|
<panel-group @handleSetLineChartData="handleSetLineChartData" />
|
||||||
|
|
||||||
|
<div style="border: 1px solid #E4E7ED;margin-bottom: 10px">
|
||||||
|
<Pipeline instance-id="instanceId1" :task-nodes="task1Nodes" />
|
||||||
|
<Pipeline instance-id="instanceId2" :task-nodes="task2Nodes" />
|
||||||
|
<Pipeline instance-id="instanceId3" :task-nodes="task3Nodes" />
|
||||||
|
<Pipeline instance-id="instanceId4" :task-nodes="task4Nodes" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;">
|
<el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;">
|
||||||
<line-chart :chart-data="lineChartData" />
|
<line-chart :chart-data="lineChartData" />
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -39,6 +48,7 @@ import RadarChart from '@/components/Echarts/RadarChart'
|
|||||||
import PieChart from '@/components/Echarts/PieChart'
|
import PieChart from '@/components/Echarts/PieChart'
|
||||||
import BarChart from '@/components/Echarts/BarChart'
|
import BarChart from '@/components/Echarts/BarChart'
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
|
import Pipeline from '@/views/components/devops/pipeline/Pipeline.vue'
|
||||||
|
|
||||||
const lineChartData = {
|
const lineChartData = {
|
||||||
newVisitis: {
|
newVisitis: {
|
||||||
@@ -62,6 +72,7 @@ const lineChartData = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'Dashboard',
|
name: 'Dashboard',
|
||||||
components: {
|
components: {
|
||||||
|
Pipeline,
|
||||||
GithubCorner,
|
GithubCorner,
|
||||||
PanelGroup,
|
PanelGroup,
|
||||||
LineChart,
|
LineChart,
|
||||||
@@ -72,7 +83,391 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
swaggerApi: store.getters.swaggerApi,
|
swaggerApi: store.getters.swaggerApi,
|
||||||
lineChartData: lineChartData.newVisitis
|
lineChartData: lineChartData.newVisitis,
|
||||||
|
task1Nodes: [
|
||||||
|
{
|
||||||
|
click: false,
|
||||||
|
code: 'release',
|
||||||
|
desc: '执行成功',
|
||||||
|
durationMillis: 7446,
|
||||||
|
name: '环境准备',
|
||||||
|
startTimeMillis: 1652343103245,
|
||||||
|
status: 'success',
|
||||||
|
statusDesc: '成功'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
click: true,
|
||||||
|
retry: true,
|
||||||
|
code: 'merge',
|
||||||
|
desc: '执行成功',
|
||||||
|
detailType: 'gitlab',
|
||||||
|
durationMillis: 50331,
|
||||||
|
name: '合并代码',
|
||||||
|
startTimeMillis: 1652343110691,
|
||||||
|
status: 'success',
|
||||||
|
statusDesc: '成功'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
click: false,
|
||||||
|
code: 'stc_upload',
|
||||||
|
desc: '代码上传成功',
|
||||||
|
durationMillis: 16123,
|
||||||
|
name: 'STC扫描(代码上传)',
|
||||||
|
startTimeMillis: 1652343161022,
|
||||||
|
status: 'success',
|
||||||
|
statusDesc: '成功'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
click: true,
|
||||||
|
retry: true,
|
||||||
|
code: 'build',
|
||||||
|
desc: '执行成功',
|
||||||
|
detailType: 'gitlab',
|
||||||
|
durationMillis: 104692,
|
||||||
|
name: '构建',
|
||||||
|
startTimeMillis: 1652343177145,
|
||||||
|
status: 'success',
|
||||||
|
statusDesc: '成功'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
buttonList: [
|
||||||
|
{
|
||||||
|
condition: 'all',
|
||||||
|
blank: true,
|
||||||
|
linkUrl: 'https://devops.odboy.cn/depolyHistory/release?appName=kenaito-pilot&id=1',
|
||||||
|
text: '查看扫描结果',
|
||||||
|
type: 'link'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
click: false,
|
||||||
|
code: 'stc_result',
|
||||||
|
desc: 'STC扫描通过',
|
||||||
|
durationMillis: 18971,
|
||||||
|
name: 'STC扫描(扫描结果)',
|
||||||
|
startTimeMillis: 1652343281837,
|
||||||
|
status: 'success',
|
||||||
|
statusDesc: '成功'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
click: false,
|
||||||
|
code: 'doPreCheck',
|
||||||
|
desc: '【田俊】确认通过',
|
||||||
|
durationMillis: 69937,
|
||||||
|
name: '发布前确认',
|
||||||
|
startTimeMillis: 1652343300808,
|
||||||
|
status: 'success',
|
||||||
|
statusDesc: '成功'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
buttonList: [
|
||||||
|
{
|
||||||
|
condition: 'success',
|
||||||
|
blank: true,
|
||||||
|
linkUrl: 'https://devops.odboy.cn/depolyHistory/release?appName=kenaito-pilot&id=1',
|
||||||
|
text: '查看部署详情',
|
||||||
|
type: 'link'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
click: false,
|
||||||
|
code: 'deploy',
|
||||||
|
desc: '成功',
|
||||||
|
durationMillis: 161975,
|
||||||
|
name: '部署',
|
||||||
|
startTimeMillis: 1652343370745,
|
||||||
|
status: 'success',
|
||||||
|
statusDesc: '成功'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
buttonList: [
|
||||||
|
{
|
||||||
|
condition: 'running',
|
||||||
|
method: 'get',
|
||||||
|
requestUrl: 'api/devops/ci/doCheck?id=60012',
|
||||||
|
text: '确认合并',
|
||||||
|
type: 'execute'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
click: false,
|
||||||
|
code: 'mergeMaster',
|
||||||
|
detailType: 'gitlab',
|
||||||
|
durationMillis: 0,
|
||||||
|
name: '合并主干',
|
||||||
|
desc: '合并master确认',
|
||||||
|
startTimeMillis: 0,
|
||||||
|
status: 'running',
|
||||||
|
statusDesc: '执行中'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
task2Nodes: [
|
||||||
|
{
|
||||||
|
click: false,
|
||||||
|
code: 'release',
|
||||||
|
desc: '执行成功',
|
||||||
|
durationMillis: 7446,
|
||||||
|
name: '环境准备',
|
||||||
|
startTimeMillis: 1652343103245,
|
||||||
|
status: 'success',
|
||||||
|
statusDesc: '成功'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
click: true,
|
||||||
|
retry: true,
|
||||||
|
code: 'merge',
|
||||||
|
desc: '执行成功',
|
||||||
|
detailType: 'gitlab',
|
||||||
|
durationMillis: 50331,
|
||||||
|
name: '合并代码',
|
||||||
|
startTimeMillis: 1652343110691,
|
||||||
|
status: 'success',
|
||||||
|
statusDesc: '成功'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
click: true,
|
||||||
|
retry: true,
|
||||||
|
code: 'build',
|
||||||
|
desc: '执行成功',
|
||||||
|
detailType: 'gitlab',
|
||||||
|
durationMillis: 104692,
|
||||||
|
name: '构建',
|
||||||
|
startTimeMillis: 1652343177145,
|
||||||
|
status: 'success',
|
||||||
|
statusDesc: '成功'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
buttonList: [
|
||||||
|
{
|
||||||
|
condition: 'success',
|
||||||
|
blank: true,
|
||||||
|
linkUrl: 'https://devops.odboy.cn/depolyHistory/release?appName=kenaito-pilot&id=1',
|
||||||
|
text: '查看部署详情',
|
||||||
|
type: 'link'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
condition: 'running',
|
||||||
|
method: 'get',
|
||||||
|
requestUrl: 'api/devops/ci/apply/doAgree?id=60012',
|
||||||
|
text: '同意',
|
||||||
|
type: 'execute'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
condition: 'running',
|
||||||
|
method: 'get',
|
||||||
|
requestUrl: 'api/devops/ci/apply/doRefuse?id=60012',
|
||||||
|
text: '拒绝',
|
||||||
|
color: '#ff0000',
|
||||||
|
type: 'execute'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
click: false,
|
||||||
|
code: 'deploy',
|
||||||
|
desc: '等待部署审批',
|
||||||
|
durationMillis: 161975,
|
||||||
|
name: '部署',
|
||||||
|
startTimeMillis: 1652343370745,
|
||||||
|
status: 'running',
|
||||||
|
statusDesc: '执行中'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
buttonList: [
|
||||||
|
{
|
||||||
|
condition: 'running',
|
||||||
|
method: 'get',
|
||||||
|
requestUrl: 'api/devops/ci/doCheck?id=60012',
|
||||||
|
text: '确认合并',
|
||||||
|
type: 'execute'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
click: false,
|
||||||
|
code: 'mergeMaster',
|
||||||
|
detailType: 'gitlab',
|
||||||
|
durationMillis: 7446,
|
||||||
|
name: '合并主干',
|
||||||
|
desc: '合并master确认',
|
||||||
|
startTimeMillis: 1652343103245,
|
||||||
|
status: 'success',
|
||||||
|
statusDesc: '成功'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
task3Nodes: [
|
||||||
|
{
|
||||||
|
click: false,
|
||||||
|
code: 'release',
|
||||||
|
desc: '执行成功',
|
||||||
|
durationMillis: 7446,
|
||||||
|
name: '环境准备',
|
||||||
|
startTimeMillis: 1652343103245,
|
||||||
|
status: 'success',
|
||||||
|
statusDesc: '成功'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
click: true,
|
||||||
|
retry: true,
|
||||||
|
code: 'merge',
|
||||||
|
desc: '执行失败',
|
||||||
|
detailType: 'gitlab',
|
||||||
|
durationMillis: 50331,
|
||||||
|
name: '合并代码',
|
||||||
|
startTimeMillis: 1652343110691,
|
||||||
|
status: 'failed',
|
||||||
|
statusDesc: '失败'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
click: true,
|
||||||
|
retry: true,
|
||||||
|
code: 'build',
|
||||||
|
desc: '待执行',
|
||||||
|
detailType: 'gitlab',
|
||||||
|
durationMillis: 0,
|
||||||
|
name: '构建',
|
||||||
|
startTimeMillis: 0,
|
||||||
|
status: 'pending',
|
||||||
|
statusDesc: '待执行'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
buttonList: [
|
||||||
|
{
|
||||||
|
condition: 'success',
|
||||||
|
blank: true,
|
||||||
|
linkUrl: 'https://devops.odboy.cn/depolyHistory/release?appName=kenaito-pilot&id=1',
|
||||||
|
text: '查看部署详情',
|
||||||
|
type: 'link'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
condition: 'running',
|
||||||
|
method: 'get',
|
||||||
|
requestUrl: 'api/devops/ci/apply/doAgree?id=60012',
|
||||||
|
text: '同意',
|
||||||
|
type: 'execute'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
condition: 'running',
|
||||||
|
method: 'get',
|
||||||
|
requestUrl: 'api/devops/ci/apply/doRefuse?id=60012',
|
||||||
|
text: '拒绝',
|
||||||
|
color: '#ff0000',
|
||||||
|
type: 'execute'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
click: false,
|
||||||
|
code: 'deploy',
|
||||||
|
desc: '待执行',
|
||||||
|
durationMillis: 0,
|
||||||
|
name: '部署',
|
||||||
|
startTimeMillis: 0,
|
||||||
|
status: 'pending',
|
||||||
|
statusDesc: '待执行'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
buttonList: [
|
||||||
|
{
|
||||||
|
condition: 'running',
|
||||||
|
method: 'get',
|
||||||
|
requestUrl: 'api/devops/ci/doCheck?id=60012',
|
||||||
|
text: '确认合并',
|
||||||
|
type: 'execute'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
click: false,
|
||||||
|
code: 'mergeMaster',
|
||||||
|
detailType: 'gitlab',
|
||||||
|
durationMillis: 0,
|
||||||
|
name: '合并主干',
|
||||||
|
desc: '待执行',
|
||||||
|
startTimeMillis: 0,
|
||||||
|
status: 'pending',
|
||||||
|
statusDesc: '待执行'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
task4Nodes: [
|
||||||
|
{
|
||||||
|
click: false,
|
||||||
|
code: 'release',
|
||||||
|
desc: '执行成功',
|
||||||
|
durationMillis: 7446,
|
||||||
|
name: '环境准备',
|
||||||
|
startTimeMillis: 1652343103245,
|
||||||
|
status: 'success',
|
||||||
|
statusDesc: '成功'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
click: true,
|
||||||
|
retry: true,
|
||||||
|
code: 'merge',
|
||||||
|
desc: '执行成功',
|
||||||
|
detailType: 'gitlab',
|
||||||
|
durationMillis: 50331,
|
||||||
|
name: '合并代码',
|
||||||
|
startTimeMillis: 1652343110691,
|
||||||
|
status: 'success',
|
||||||
|
statusDesc: '成功'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
click: true,
|
||||||
|
retry: true,
|
||||||
|
code: 'build',
|
||||||
|
desc: '执行失败',
|
||||||
|
detailType: 'gitlab',
|
||||||
|
durationMillis: 50331,
|
||||||
|
name: '构建',
|
||||||
|
startTimeMillis: 1652343110691,
|
||||||
|
status: 'failed',
|
||||||
|
statusDesc: '失败'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
buttonList: [
|
||||||
|
{
|
||||||
|
condition: 'success',
|
||||||
|
blank: true,
|
||||||
|
linkUrl: 'https://devops.odboy.cn/depolyHistory/release?appName=kenaito-pilot&id=1',
|
||||||
|
text: '查看部署详情',
|
||||||
|
type: 'link'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
condition: 'running',
|
||||||
|
method: 'get',
|
||||||
|
requestUrl: 'api/devops/ci/apply/doAgree?id=60012',
|
||||||
|
text: '同意',
|
||||||
|
type: 'execute'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
condition: 'running',
|
||||||
|
method: 'get',
|
||||||
|
requestUrl: 'api/devops/ci/apply/doRefuse?id=60012',
|
||||||
|
text: '拒绝',
|
||||||
|
color: '#ff0000',
|
||||||
|
type: 'execute'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
click: false,
|
||||||
|
code: 'deploy',
|
||||||
|
desc: '待执行',
|
||||||
|
durationMillis: 0,
|
||||||
|
name: '部署',
|
||||||
|
startTimeMillis: 0,
|
||||||
|
status: 'pending',
|
||||||
|
statusDesc: '待执行'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
buttonList: [
|
||||||
|
{
|
||||||
|
condition: 'running',
|
||||||
|
method: 'get',
|
||||||
|
requestUrl: 'api/devops/ci/doCheck?id=60012',
|
||||||
|
text: '确认合并',
|
||||||
|
type: 'execute'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
click: false,
|
||||||
|
code: 'mergeMaster',
|
||||||
|
detailType: 'gitlab',
|
||||||
|
durationMillis: 0,
|
||||||
|
name: '合并主干',
|
||||||
|
desc: '待执行',
|
||||||
|
startTimeMillis: 0,
|
||||||
|
status: 'pending',
|
||||||
|
statusDesc: '待执行'
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|||||||
+168
@@ -0,0 +1,168 @@
|
|||||||
|
package cn.odboy.constool;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.odboy.base.KitBaseUserTimeTb;
|
||||||
|
import cn.odboy.framework.exception.BadRequestException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据对比同步工具
|
||||||
|
* <p>
|
||||||
|
* 适用场景:对比两个相同类型的对象集合,以对照组(compareRecords)为基准:
|
||||||
|
* <p>
|
||||||
|
* - 本地不存在的数据 → 新增
|
||||||
|
* <p>
|
||||||
|
* - 本地存在的数据 → 更新
|
||||||
|
* <p>
|
||||||
|
* - 对照组不存在的数据 → 删除
|
||||||
|
*
|
||||||
|
* @param <T> 数据类型
|
||||||
|
* @author odboy
|
||||||
|
*/
|
||||||
|
public class KitDiffSyncConstTool<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据同步回调接口
|
||||||
|
*
|
||||||
|
* @param <T> 数据类型
|
||||||
|
*/
|
||||||
|
public interface SyncCallback<T> {
|
||||||
|
/**
|
||||||
|
* 全量存储对照组数据
|
||||||
|
*
|
||||||
|
* @param records 需要保存的数据列表
|
||||||
|
*/
|
||||||
|
void doSaveBatch(List<T> records);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量更新数据
|
||||||
|
*
|
||||||
|
* @param localList 本地已存在的数据列表
|
||||||
|
* @param compareList 对照组对应的数据列表
|
||||||
|
*/
|
||||||
|
void doUpdateBatch(List<T> localList, List<T> compareList);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除数据
|
||||||
|
*
|
||||||
|
* @param records 需要删除的数据列表
|
||||||
|
*/
|
||||||
|
void doDeleteBatch(List<T> records);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本地数据源
|
||||||
|
*/
|
||||||
|
private final List<T> localRecords;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对照数据源(以对照数据源为准)
|
||||||
|
*/
|
||||||
|
private final List<T> compareRecords;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
*
|
||||||
|
* @param localRecords 本地数据源
|
||||||
|
* @param compareRecords 对照数据源(基准数据)
|
||||||
|
*/
|
||||||
|
public KitDiffSyncConstTool(List<T> localRecords, List<T> compareRecords) {
|
||||||
|
this.localRecords = localRecords;
|
||||||
|
this.compareRecords = compareRecords;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行数据对比同步
|
||||||
|
*
|
||||||
|
* @param keyMapper 唯一键映射函数,用于比较数据唯一性(如:Entity::getId)
|
||||||
|
* @param callback 同步回调接口,处理具体的增删改操作
|
||||||
|
* @param <K> 唯一键类型
|
||||||
|
*/
|
||||||
|
public <K> void sync(Function<? super T, ? extends K> keyMapper, SyncCallback<T> callback) {
|
||||||
|
if (keyMapper == null) {
|
||||||
|
throw new BadRequestException("参数 keyMapper 必填");
|
||||||
|
}
|
||||||
|
if (callback == null) {
|
||||||
|
throw new BadRequestException("参数 callback 必填");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果本地数据为空,直接全量保存对照数据
|
||||||
|
if (this.localRecords.isEmpty()) {
|
||||||
|
callback.doSaveBatch(this.compareRecords);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建本地数据的键值映射
|
||||||
|
Map<K, T> localMap = localRecords.stream().collect(Collectors.toMap(keyMapper, i -> i));
|
||||||
|
|
||||||
|
List<T> insertList = new ArrayList<>();
|
||||||
|
List<T> updateCompareList = new ArrayList<>();
|
||||||
|
List<T> updateLocalList = new ArrayList<>();
|
||||||
|
List<T> deleteList = new ArrayList<>();
|
||||||
|
|
||||||
|
// 遍历对照数据,识别新增和更新
|
||||||
|
for (T compareRecord : compareRecords) {
|
||||||
|
K key = keyMapper.apply(compareRecord);
|
||||||
|
T local = localMap.get(key);
|
||||||
|
|
||||||
|
if (local == null) {
|
||||||
|
// 本地不存在,需要新增
|
||||||
|
insertList.add(compareRecord);
|
||||||
|
} else {
|
||||||
|
// 本地已存在,需要更新
|
||||||
|
updateLocalList.add(local);
|
||||||
|
updateCompareList.add(compareRecord);
|
||||||
|
// 标记为已处理
|
||||||
|
localMap.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 剩余的就是本地存在但对照组中不存在的,需要删除
|
||||||
|
for (Map.Entry<K, T> entry : localMap.entrySet()) {
|
||||||
|
deleteList.add(entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行批量操作
|
||||||
|
if (CollUtil.isNotEmpty(insertList)) {
|
||||||
|
callback.doSaveBatch(insertList);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CollUtil.isNotEmpty(updateLocalList)) {
|
||||||
|
callback.doUpdateBatch(updateLocalList, updateCompareList);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CollUtil.isNotEmpty(deleteList)) {
|
||||||
|
callback.doDeleteBatch(deleteList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用示例
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
List<KitBaseUserTimeTb> localList = new ArrayList<>();
|
||||||
|
List<KitBaseUserTimeTb> onlineList = new ArrayList<>();
|
||||||
|
|
||||||
|
KitDiffSyncConstTool<KitBaseUserTimeTb> diffSyncTool = new KitDiffSyncConstTool<>(localList, onlineList);
|
||||||
|
diffSyncTool.sync(KitBaseUserTimeTb::getCreateBy, new SyncCallback<>() {
|
||||||
|
@Override
|
||||||
|
public void doSaveBatch(List<KitBaseUserTimeTb> records) {
|
||||||
|
// 全量保存
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doUpdateBatch(List<KitBaseUserTimeTb> localList, List<KitBaseUserTimeTb> compareList) {
|
||||||
|
// 将 compareList 中的数据,填充到对应的 localList 数据中,更新 localList 数据
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doDeleteBatch(List<KitBaseUserTimeTb> records) {
|
||||||
|
// 条件删除
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* 存放常用的业务工具
|
||||||
|
*/
|
||||||
|
package cn.odboy.constool;
|
||||||
+3
-3
@@ -27,7 +27,7 @@ import lombok.experimental.UtilityClass;
|
|||||||
* @author odboy
|
* @author odboy
|
||||||
*/
|
*/
|
||||||
@UtilityClass
|
@UtilityClass
|
||||||
public class KitDynamicTableUtil {
|
public class KitDynamicTableBuilder {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 渲染排序规则
|
* 渲染排序规则
|
||||||
@@ -37,7 +37,7 @@ public class KitDynamicTableUtil {
|
|||||||
* @param <T> 渲染对象
|
* @param <T> 渲染对象
|
||||||
* @return /
|
* @return /
|
||||||
*/
|
*/
|
||||||
public static <M, T> QueryWrapper<M> renderOrder(KitPageArgs<T> pageArgs) {
|
public static <M, T> QueryWrapper<M> ofOrder(KitPageArgs<T> pageArgs) {
|
||||||
QueryWrapper<M> wrapper = new QueryWrapper<>();
|
QueryWrapper<M> wrapper = new QueryWrapper<>();
|
||||||
KitPageArgs.OrderBy orderBy = pageArgs.getOrderBy();
|
KitPageArgs.OrderBy orderBy = pageArgs.getOrderBy();
|
||||||
if (orderBy != null) {
|
if (orderBy != null) {
|
||||||
@@ -54,7 +54,7 @@ public class KitDynamicTableUtil {
|
|||||||
* @param <T> 渲染对象
|
* @param <T> 渲染对象
|
||||||
* @return /
|
* @return /
|
||||||
*/
|
*/
|
||||||
public static <M, T> LambdaQueryWrapper<M> renderAll(KitPageArgs<T> pageArgs) {
|
public static <M, T> LambdaQueryWrapper<M> ofAll(KitPageArgs<T> pageArgs) {
|
||||||
QueryWrapper<M> wrapper = KitMpQUtil.build(pageArgs.getArgs());
|
QueryWrapper<M> wrapper = KitMpQUtil.build(pageArgs.getArgs());
|
||||||
KitPageArgs.OrderBy orderBy = pageArgs.getOrderBy();
|
KitPageArgs.OrderBy orderBy = pageArgs.getOrderBy();
|
||||||
if (orderBy != null) {
|
if (orderBy != null) {
|
||||||
+39
-3
@@ -30,7 +30,8 @@ public final class KitStringUtil {
|
|||||||
/**
|
/**
|
||||||
* 驼峰命名法工具
|
* 驼峰命名法工具
|
||||||
*
|
*
|
||||||
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld" toUnderScoreCase("helloWorld") = "hello_world"
|
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld"
|
||||||
|
* toUnderScoreCase("helloWorld") = "hello_world"
|
||||||
*/
|
*/
|
||||||
public static String toCamelCase(String s) {
|
public static String toCamelCase(String s) {
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
@@ -56,7 +57,8 @@ public final class KitStringUtil {
|
|||||||
/**
|
/**
|
||||||
* 驼峰命名法工具
|
* 驼峰命名法工具
|
||||||
*
|
*
|
||||||
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld" toUnderScoreCase("helloWorld") = "hello_world"
|
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld"
|
||||||
|
* toUnderScoreCase("helloWorld") = "hello_world"
|
||||||
*/
|
*/
|
||||||
public static String toCapitalizeCamelCase(String s) {
|
public static String toCapitalizeCamelCase(String s) {
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
@@ -69,7 +71,8 @@ public final class KitStringUtil {
|
|||||||
/**
|
/**
|
||||||
* 驼峰命名法工具
|
* 驼峰命名法工具
|
||||||
*
|
*
|
||||||
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld" toUnderScoreCase("helloWorld") = "hello_world"
|
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld"
|
||||||
|
* toUnderScoreCase("helloWorld") = "hello_world"
|
||||||
*/
|
*/
|
||||||
public static String toUnderScoreCase(String s) {
|
public static String toUnderScoreCase(String s) {
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
@@ -95,4 +98,37 @@ public final class KitStringUtil {
|
|||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化字节数
|
||||||
|
*
|
||||||
|
* @param storage 字节数
|
||||||
|
* @return /
|
||||||
|
*/
|
||||||
|
private String renderBytes(Long storage) {
|
||||||
|
if (storage == null || storage <= 0) {
|
||||||
|
return "0 Byte";
|
||||||
|
}
|
||||||
|
|
||||||
|
double value = storage.doubleValue();
|
||||||
|
final long KB = 1024L;
|
||||||
|
final long MB = KB * 1024L;
|
||||||
|
final long GB = MB * 1024L;
|
||||||
|
final long TB = GB * 1024L;
|
||||||
|
final long PB = TB * 1024L;
|
||||||
|
|
||||||
|
if (value >= PB) {
|
||||||
|
return String.format("%.2f PB", value / PB);
|
||||||
|
} else if (value >= TB) {
|
||||||
|
return String.format("%.2f TB", value / TB);
|
||||||
|
} else if (value >= GB) {
|
||||||
|
return String.format("%.2f GB", value / GB);
|
||||||
|
} else if (value >= MB) {
|
||||||
|
return String.format("%.2f MB", value / MB);
|
||||||
|
} else if (value >= KB) {
|
||||||
|
return String.format("%.2f KB", value / KB);
|
||||||
|
} else {
|
||||||
|
return String.format("%d Byte", storage);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -18,7 +18,7 @@ package cn.odboy.system.features;
|
|||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.odboy.base.KitPageArgs;
|
import cn.odboy.base.KitPageArgs;
|
||||||
import cn.odboy.feature.KitDynamicTableResponse;
|
import cn.odboy.feature.KitDynamicTableResponse;
|
||||||
import cn.odboy.feature.KitDynamicTableUtil;
|
import cn.odboy.feature.KitDynamicTableBuilder;
|
||||||
import cn.odboy.system.dal.dataobject.SystemMenuTb;
|
import cn.odboy.system.dal.dataobject.SystemMenuTb;
|
||||||
import cn.odboy.system.dal.model.request.SystemQueryMenuArgs;
|
import cn.odboy.system.dal.model.request.SystemQueryMenuArgs;
|
||||||
import cn.odboy.system.dal.mysql.SystemMenuMapper;
|
import cn.odboy.system.dal.mysql.SystemMenuMapper;
|
||||||
@@ -75,7 +75,7 @@ public class CuteDynamicTableDemoApi {
|
|||||||
@PreAuthorize("@el.check()")
|
@PreAuthorize("@el.check()")
|
||||||
public ResponseEntity<KitDynamicTableResponse<SystemMenuTb, SystemMenuDynamicTableModel>> searchMenu2(@Validated @RequestBody KitPageArgs<SystemQueryMenuArgs> pageArgs) {
|
public ResponseEntity<KitDynamicTableResponse<SystemMenuTb, SystemMenuDynamicTableModel>> searchMenu2(@Validated @RequestBody KitPageArgs<SystemQueryMenuArgs> pageArgs) {
|
||||||
// 渲染排序(非必须)
|
// 渲染排序(非必须)
|
||||||
QueryWrapper<SystemMenuTb> wrapper = KitDynamicTableUtil.renderOrder(pageArgs);
|
QueryWrapper<SystemMenuTb> wrapper = KitDynamicTableBuilder.ofOrder(pageArgs);
|
||||||
LambdaQueryWrapper<SystemMenuTb> lambda = wrapper.lambda();
|
LambdaQueryWrapper<SystemMenuTb> lambda = wrapper.lambda();
|
||||||
// 查询条件
|
// 查询条件
|
||||||
SystemQueryMenuArgs args = pageArgs.getArgs();
|
SystemQueryMenuArgs args = pageArgs.getArgs();
|
||||||
@@ -91,7 +91,7 @@ public class CuteDynamicTableDemoApi {
|
|||||||
@PreAuthorize("@el.check()")
|
@PreAuthorize("@el.check()")
|
||||||
public ResponseEntity<KitDynamicTableResponse<SystemMenuTb, SystemMenuDynamicTableModel>> searchMenu3(@Validated @RequestBody KitPageArgs<SystemQueryMenuArgs> pageArgs) {
|
public ResponseEntity<KitDynamicTableResponse<SystemMenuTb, SystemMenuDynamicTableModel>> searchMenu3(@Validated @RequestBody KitPageArgs<SystemQueryMenuArgs> pageArgs) {
|
||||||
// 渲染查询条件
|
// 渲染查询条件
|
||||||
LambdaQueryWrapper<SystemMenuTb> wrapper = KitDynamicTableUtil.renderAll(pageArgs);
|
LambdaQueryWrapper<SystemMenuTb> wrapper = KitDynamicTableBuilder.ofAll(pageArgs);
|
||||||
// 响应结果
|
// 响应结果
|
||||||
return ResponseEntity.ok(new KitDynamicTableResponse<>(SystemMenuDynamicTableModel.class, pageArgs, "id", systemMenuMapper, wrapper));
|
return ResponseEntity.ok(new KitDynamicTableResponse<>(SystemMenuDynamicTableModel.class, pageArgs, "id", systemMenuMapper, wrapper));
|
||||||
}
|
}
|
||||||
|
|||||||
+168
@@ -0,0 +1,168 @@
|
|||||||
|
package cn.odboy.constool;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.odboy.base.KitBaseUserTimeTb;
|
||||||
|
import cn.odboy.framework.exception.BadRequestException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据对比同步工具
|
||||||
|
* <p>
|
||||||
|
* 适用场景:对比两个相同类型的对象集合,以对照组(compareRecords)为基准:
|
||||||
|
* <p>
|
||||||
|
* - 本地不存在的数据 → 新增
|
||||||
|
* <p>
|
||||||
|
* - 本地存在的数据 → 更新
|
||||||
|
* <p>
|
||||||
|
* - 对照组不存在的数据 → 删除
|
||||||
|
*
|
||||||
|
* @param <T> 数据类型
|
||||||
|
* @author odboy
|
||||||
|
*/
|
||||||
|
public class KitDiffSyncConstTool<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据同步回调接口
|
||||||
|
*
|
||||||
|
* @param <T> 数据类型
|
||||||
|
*/
|
||||||
|
public interface SyncCallback<T> {
|
||||||
|
/**
|
||||||
|
* 全量存储对照组数据
|
||||||
|
*
|
||||||
|
* @param records 需要保存的数据列表
|
||||||
|
*/
|
||||||
|
void doSaveBatch(List<T> records);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量更新数据
|
||||||
|
*
|
||||||
|
* @param localList 本地已存在的数据列表
|
||||||
|
* @param compareList 对照组对应的数据列表
|
||||||
|
*/
|
||||||
|
void doUpdateBatch(List<T> localList, List<T> compareList);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除数据
|
||||||
|
*
|
||||||
|
* @param records 需要删除的数据列表
|
||||||
|
*/
|
||||||
|
void doDeleteBatch(List<T> records);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本地数据源
|
||||||
|
*/
|
||||||
|
private final List<T> localRecords;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对照数据源(以对照数据源为准)
|
||||||
|
*/
|
||||||
|
private final List<T> compareRecords;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
*
|
||||||
|
* @param localRecords 本地数据源
|
||||||
|
* @param compareRecords 对照数据源(基准数据)
|
||||||
|
*/
|
||||||
|
public KitDiffSyncConstTool(List<T> localRecords, List<T> compareRecords) {
|
||||||
|
this.localRecords = localRecords;
|
||||||
|
this.compareRecords = compareRecords;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行数据对比同步
|
||||||
|
*
|
||||||
|
* @param keyMapper 唯一键映射函数,用于比较数据唯一性(如:Entity::getId)
|
||||||
|
* @param callback 同步回调接口,处理具体的增删改操作
|
||||||
|
* @param <K> 唯一键类型
|
||||||
|
*/
|
||||||
|
public <K> void sync(Function<? super T, ? extends K> keyMapper, SyncCallback<T> callback) {
|
||||||
|
if (keyMapper == null) {
|
||||||
|
throw new BadRequestException("参数 keyMapper 必填");
|
||||||
|
}
|
||||||
|
if (callback == null) {
|
||||||
|
throw new BadRequestException("参数 callback 必填");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果本地数据为空,直接全量保存对照数据
|
||||||
|
if (this.localRecords.isEmpty()) {
|
||||||
|
callback.doSaveBatch(this.compareRecords);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建本地数据的键值映射
|
||||||
|
Map<K, T> localMap = localRecords.stream().collect(Collectors.toMap(keyMapper, i -> i));
|
||||||
|
|
||||||
|
List<T> insertList = new ArrayList<>();
|
||||||
|
List<T> updateCompareList = new ArrayList<>();
|
||||||
|
List<T> updateLocalList = new ArrayList<>();
|
||||||
|
List<T> deleteList = new ArrayList<>();
|
||||||
|
|
||||||
|
// 遍历对照数据,识别新增和更新
|
||||||
|
for (T compareRecord : compareRecords) {
|
||||||
|
K key = keyMapper.apply(compareRecord);
|
||||||
|
T local = localMap.get(key);
|
||||||
|
|
||||||
|
if (local == null) {
|
||||||
|
// 本地不存在,需要新增
|
||||||
|
insertList.add(compareRecord);
|
||||||
|
} else {
|
||||||
|
// 本地已存在,需要更新
|
||||||
|
updateLocalList.add(local);
|
||||||
|
updateCompareList.add(compareRecord);
|
||||||
|
// 标记为已处理
|
||||||
|
localMap.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 剩余的就是本地存在但对照组中不存在的,需要删除
|
||||||
|
for (Map.Entry<K, T> entry : localMap.entrySet()) {
|
||||||
|
deleteList.add(entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行批量操作
|
||||||
|
if (CollUtil.isNotEmpty(insertList)) {
|
||||||
|
callback.doSaveBatch(insertList);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CollUtil.isNotEmpty(updateLocalList)) {
|
||||||
|
callback.doUpdateBatch(updateLocalList, updateCompareList);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CollUtil.isNotEmpty(deleteList)) {
|
||||||
|
callback.doDeleteBatch(deleteList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用示例
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
List<KitBaseUserTimeTb> localList = new ArrayList<>();
|
||||||
|
List<KitBaseUserTimeTb> onlineList = new ArrayList<>();
|
||||||
|
|
||||||
|
KitDiffSyncConstTool<KitBaseUserTimeTb> diffSyncTool = new KitDiffSyncConstTool<>(localList, onlineList);
|
||||||
|
diffSyncTool.sync(KitBaseUserTimeTb::getCreateBy, new SyncCallback<>() {
|
||||||
|
@Override
|
||||||
|
public void doSaveBatch(List<KitBaseUserTimeTb> records) {
|
||||||
|
// 全量保存
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doUpdateBatch(List<KitBaseUserTimeTb> localList, List<KitBaseUserTimeTb> compareList) {
|
||||||
|
// 将 compareList 中的数据,填充到对应的 localList 数据中,更新 localList 数据
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doDeleteBatch(List<KitBaseUserTimeTb> records) {
|
||||||
|
// 条件删除
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* 存放常用的业务工具
|
||||||
|
*/
|
||||||
|
package cn.odboy.constool;
|
||||||
+3
-3
@@ -27,7 +27,7 @@ import lombok.experimental.UtilityClass;
|
|||||||
* @author odboy
|
* @author odboy
|
||||||
*/
|
*/
|
||||||
@UtilityClass
|
@UtilityClass
|
||||||
public class KitDynamicTableUtil {
|
public class KitDynamicTableBuilder {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 渲染排序规则
|
* 渲染排序规则
|
||||||
@@ -37,7 +37,7 @@ public class KitDynamicTableUtil {
|
|||||||
* @param <T> 渲染对象
|
* @param <T> 渲染对象
|
||||||
* @return /
|
* @return /
|
||||||
*/
|
*/
|
||||||
public static <M, T> QueryWrapper<M> renderOrder(KitPageArgs<T> pageArgs) {
|
public static <M, T> QueryWrapper<M> ofOrder(KitPageArgs<T> pageArgs) {
|
||||||
QueryWrapper<M> wrapper = new QueryWrapper<>();
|
QueryWrapper<M> wrapper = new QueryWrapper<>();
|
||||||
KitPageArgs.OrderBy orderBy = pageArgs.getOrderBy();
|
KitPageArgs.OrderBy orderBy = pageArgs.getOrderBy();
|
||||||
if (orderBy != null) {
|
if (orderBy != null) {
|
||||||
@@ -54,7 +54,7 @@ public class KitDynamicTableUtil {
|
|||||||
* @param <T> 渲染对象
|
* @param <T> 渲染对象
|
||||||
* @return /
|
* @return /
|
||||||
*/
|
*/
|
||||||
public static <M, T> LambdaQueryWrapper<M> renderAll(KitPageArgs<T> pageArgs) {
|
public static <M, T> LambdaQueryWrapper<M> ofAll(KitPageArgs<T> pageArgs) {
|
||||||
QueryWrapper<M> wrapper = KitMpQUtil.build(pageArgs.getArgs());
|
QueryWrapper<M> wrapper = KitMpQUtil.build(pageArgs.getArgs());
|
||||||
KitPageArgs.OrderBy orderBy = pageArgs.getOrderBy();
|
KitPageArgs.OrderBy orderBy = pageArgs.getOrderBy();
|
||||||
if (orderBy != null) {
|
if (orderBy != null) {
|
||||||
+39
-3
@@ -30,7 +30,8 @@ public final class KitStringUtil {
|
|||||||
/**
|
/**
|
||||||
* 驼峰命名法工具
|
* 驼峰命名法工具
|
||||||
*
|
*
|
||||||
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld" toUnderScoreCase("helloWorld") = "hello_world"
|
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld"
|
||||||
|
* toUnderScoreCase("helloWorld") = "hello_world"
|
||||||
*/
|
*/
|
||||||
public static String toCamelCase(String s) {
|
public static String toCamelCase(String s) {
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
@@ -56,7 +57,8 @@ public final class KitStringUtil {
|
|||||||
/**
|
/**
|
||||||
* 驼峰命名法工具
|
* 驼峰命名法工具
|
||||||
*
|
*
|
||||||
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld" toUnderScoreCase("helloWorld") = "hello_world"
|
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld"
|
||||||
|
* toUnderScoreCase("helloWorld") = "hello_world"
|
||||||
*/
|
*/
|
||||||
public static String toCapitalizeCamelCase(String s) {
|
public static String toCapitalizeCamelCase(String s) {
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
@@ -69,7 +71,8 @@ public final class KitStringUtil {
|
|||||||
/**
|
/**
|
||||||
* 驼峰命名法工具
|
* 驼峰命名法工具
|
||||||
*
|
*
|
||||||
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld" toUnderScoreCase("helloWorld") = "hello_world"
|
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld"
|
||||||
|
* toUnderScoreCase("helloWorld") = "hello_world"
|
||||||
*/
|
*/
|
||||||
public static String toUnderScoreCase(String s) {
|
public static String toUnderScoreCase(String s) {
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
@@ -95,4 +98,37 @@ public final class KitStringUtil {
|
|||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化字节数
|
||||||
|
*
|
||||||
|
* @param storage 字节数
|
||||||
|
* @return /
|
||||||
|
*/
|
||||||
|
private String renderBytes(Long storage) {
|
||||||
|
if (storage == null || storage <= 0) {
|
||||||
|
return "0 Byte";
|
||||||
|
}
|
||||||
|
|
||||||
|
double value = storage.doubleValue();
|
||||||
|
final long KB = 1024L;
|
||||||
|
final long MB = KB * 1024L;
|
||||||
|
final long GB = MB * 1024L;
|
||||||
|
final long TB = GB * 1024L;
|
||||||
|
final long PB = TB * 1024L;
|
||||||
|
|
||||||
|
if (value >= PB) {
|
||||||
|
return String.format("%.2f PB", value / PB);
|
||||||
|
} else if (value >= TB) {
|
||||||
|
return String.format("%.2f TB", value / TB);
|
||||||
|
} else if (value >= GB) {
|
||||||
|
return String.format("%.2f GB", value / GB);
|
||||||
|
} else if (value >= MB) {
|
||||||
|
return String.format("%.2f MB", value / MB);
|
||||||
|
} else if (value >= KB) {
|
||||||
|
return String.format("%.2f KB", value / KB);
|
||||||
|
} else {
|
||||||
|
return String.format("%d Byte", storage);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -18,7 +18,7 @@ package cn.odboy.system.features;
|
|||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.odboy.base.KitPageArgs;
|
import cn.odboy.base.KitPageArgs;
|
||||||
import cn.odboy.feature.KitDynamicTableResponse;
|
import cn.odboy.feature.KitDynamicTableResponse;
|
||||||
import cn.odboy.feature.KitDynamicTableUtil;
|
import cn.odboy.feature.KitDynamicTableBuilder;
|
||||||
import cn.odboy.system.dal.dataobject.SystemMenuTb;
|
import cn.odboy.system.dal.dataobject.SystemMenuTb;
|
||||||
import cn.odboy.system.dal.model.request.SystemQueryMenuArgs;
|
import cn.odboy.system.dal.model.request.SystemQueryMenuArgs;
|
||||||
import cn.odboy.system.dal.mysql.SystemMenuMapper;
|
import cn.odboy.system.dal.mysql.SystemMenuMapper;
|
||||||
@@ -75,7 +75,7 @@ public class CuteDynamicTableDemoApi {
|
|||||||
@PreAuthorize("@el.check()")
|
@PreAuthorize("@el.check()")
|
||||||
public ResponseEntity<KitDynamicTableResponse<SystemMenuTb, SystemMenuDynamicTableModel>> searchMenu2(@Validated @RequestBody KitPageArgs<SystemQueryMenuArgs> pageArgs) {
|
public ResponseEntity<KitDynamicTableResponse<SystemMenuTb, SystemMenuDynamicTableModel>> searchMenu2(@Validated @RequestBody KitPageArgs<SystemQueryMenuArgs> pageArgs) {
|
||||||
// 渲染排序(非必须)
|
// 渲染排序(非必须)
|
||||||
QueryWrapper<SystemMenuTb> wrapper = KitDynamicTableUtil.renderOrder(pageArgs);
|
QueryWrapper<SystemMenuTb> wrapper = KitDynamicTableBuilder.ofOrder(pageArgs);
|
||||||
LambdaQueryWrapper<SystemMenuTb> lambda = wrapper.lambda();
|
LambdaQueryWrapper<SystemMenuTb> lambda = wrapper.lambda();
|
||||||
// 查询条件
|
// 查询条件
|
||||||
SystemQueryMenuArgs args = pageArgs.getArgs();
|
SystemQueryMenuArgs args = pageArgs.getArgs();
|
||||||
@@ -91,7 +91,7 @@ public class CuteDynamicTableDemoApi {
|
|||||||
@PreAuthorize("@el.check()")
|
@PreAuthorize("@el.check()")
|
||||||
public ResponseEntity<KitDynamicTableResponse<SystemMenuTb, SystemMenuDynamicTableModel>> searchMenu3(@Validated @RequestBody KitPageArgs<SystemQueryMenuArgs> pageArgs) {
|
public ResponseEntity<KitDynamicTableResponse<SystemMenuTb, SystemMenuDynamicTableModel>> searchMenu3(@Validated @RequestBody KitPageArgs<SystemQueryMenuArgs> pageArgs) {
|
||||||
// 渲染查询条件
|
// 渲染查询条件
|
||||||
LambdaQueryWrapper<SystemMenuTb> wrapper = KitDynamicTableUtil.renderAll(pageArgs);
|
LambdaQueryWrapper<SystemMenuTb> wrapper = KitDynamicTableBuilder.ofAll(pageArgs);
|
||||||
// 响应结果
|
// 响应结果
|
||||||
return ResponseEntity.ok(new KitDynamicTableResponse<>(SystemMenuDynamicTableModel.class, pageArgs, "id", systemMenuMapper, wrapper));
|
return ResponseEntity.ok(new KitDynamicTableResponse<>(SystemMenuDynamicTableModel.class, pageArgs, "id", systemMenuMapper, wrapper));
|
||||||
}
|
}
|
||||||
|
|||||||
+168
@@ -0,0 +1,168 @@
|
|||||||
|
package cn.odboy.constool;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.odboy.base.KitBaseUserTimeTb;
|
||||||
|
import cn.odboy.framework.exception.BadRequestException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据对比同步工具
|
||||||
|
* <p>
|
||||||
|
* 适用场景:对比两个相同类型的对象集合,以对照组(compareRecords)为基准:
|
||||||
|
* <p>
|
||||||
|
* - 本地不存在的数据 → 新增
|
||||||
|
* <p>
|
||||||
|
* - 本地存在的数据 → 更新
|
||||||
|
* <p>
|
||||||
|
* - 对照组不存在的数据 → 删除
|
||||||
|
*
|
||||||
|
* @param <T> 数据类型
|
||||||
|
* @author odboy
|
||||||
|
*/
|
||||||
|
public class KitDiffSyncConstTool<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据同步回调接口
|
||||||
|
*
|
||||||
|
* @param <T> 数据类型
|
||||||
|
*/
|
||||||
|
public interface SyncCallback<T> {
|
||||||
|
/**
|
||||||
|
* 全量存储对照组数据
|
||||||
|
*
|
||||||
|
* @param records 需要保存的数据列表
|
||||||
|
*/
|
||||||
|
void doSaveBatch(List<T> records);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量更新数据
|
||||||
|
*
|
||||||
|
* @param localList 本地已存在的数据列表
|
||||||
|
* @param compareList 对照组对应的数据列表
|
||||||
|
*/
|
||||||
|
void doUpdateBatch(List<T> localList, List<T> compareList);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除数据
|
||||||
|
*
|
||||||
|
* @param records 需要删除的数据列表
|
||||||
|
*/
|
||||||
|
void doDeleteBatch(List<T> records);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本地数据源
|
||||||
|
*/
|
||||||
|
private final List<T> localRecords;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对照数据源(以对照数据源为准)
|
||||||
|
*/
|
||||||
|
private final List<T> compareRecords;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
*
|
||||||
|
* @param localRecords 本地数据源
|
||||||
|
* @param compareRecords 对照数据源(基准数据)
|
||||||
|
*/
|
||||||
|
public KitDiffSyncConstTool(List<T> localRecords, List<T> compareRecords) {
|
||||||
|
this.localRecords = localRecords;
|
||||||
|
this.compareRecords = compareRecords;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行数据对比同步
|
||||||
|
*
|
||||||
|
* @param keyMapper 唯一键映射函数,用于比较数据唯一性(如:Entity::getId)
|
||||||
|
* @param callback 同步回调接口,处理具体的增删改操作
|
||||||
|
* @param <K> 唯一键类型
|
||||||
|
*/
|
||||||
|
public <K> void sync(Function<? super T, ? extends K> keyMapper, SyncCallback<T> callback) {
|
||||||
|
if (keyMapper == null) {
|
||||||
|
throw new BadRequestException("参数 keyMapper 必填");
|
||||||
|
}
|
||||||
|
if (callback == null) {
|
||||||
|
throw new BadRequestException("参数 callback 必填");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果本地数据为空,直接全量保存对照数据
|
||||||
|
if (this.localRecords.isEmpty()) {
|
||||||
|
callback.doSaveBatch(this.compareRecords);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建本地数据的键值映射
|
||||||
|
Map<K, T> localMap = localRecords.stream().collect(Collectors.toMap(keyMapper, i -> i));
|
||||||
|
|
||||||
|
List<T> insertList = new ArrayList<>();
|
||||||
|
List<T> updateCompareList = new ArrayList<>();
|
||||||
|
List<T> updateLocalList = new ArrayList<>();
|
||||||
|
List<T> deleteList = new ArrayList<>();
|
||||||
|
|
||||||
|
// 遍历对照数据,识别新增和更新
|
||||||
|
for (T compareRecord : compareRecords) {
|
||||||
|
K key = keyMapper.apply(compareRecord);
|
||||||
|
T local = localMap.get(key);
|
||||||
|
|
||||||
|
if (local == null) {
|
||||||
|
// 本地不存在,需要新增
|
||||||
|
insertList.add(compareRecord);
|
||||||
|
} else {
|
||||||
|
// 本地已存在,需要更新
|
||||||
|
updateLocalList.add(local);
|
||||||
|
updateCompareList.add(compareRecord);
|
||||||
|
// 标记为已处理
|
||||||
|
localMap.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 剩余的就是本地存在但对照组中不存在的,需要删除
|
||||||
|
for (Map.Entry<K, T> entry : localMap.entrySet()) {
|
||||||
|
deleteList.add(entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行批量操作
|
||||||
|
if (CollUtil.isNotEmpty(insertList)) {
|
||||||
|
callback.doSaveBatch(insertList);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CollUtil.isNotEmpty(updateLocalList)) {
|
||||||
|
callback.doUpdateBatch(updateLocalList, updateCompareList);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CollUtil.isNotEmpty(deleteList)) {
|
||||||
|
callback.doDeleteBatch(deleteList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用示例
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
List<KitBaseUserTimeTb> localList = new ArrayList<>();
|
||||||
|
List<KitBaseUserTimeTb> onlineList = new ArrayList<>();
|
||||||
|
|
||||||
|
KitDiffSyncConstTool<KitBaseUserTimeTb> diffSyncTool = new KitDiffSyncConstTool<>(localList, onlineList);
|
||||||
|
diffSyncTool.sync(KitBaseUserTimeTb::getCreateBy, new SyncCallback<>() {
|
||||||
|
@Override
|
||||||
|
public void doSaveBatch(List<KitBaseUserTimeTb> records) {
|
||||||
|
// 全量保存
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doUpdateBatch(List<KitBaseUserTimeTb> localList, List<KitBaseUserTimeTb> compareList) {
|
||||||
|
// 将 compareList 中的数据,填充到对应的 localList 数据中,更新 localList 数据
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doDeleteBatch(List<KitBaseUserTimeTb> records) {
|
||||||
|
// 条件删除
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* 存放常用的业务工具
|
||||||
|
*/
|
||||||
|
package cn.odboy.constool;
|
||||||
+65
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021-2026 Odboy
|
||||||
|
*
|
||||||
|
* 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 cn.odboy.feature;
|
||||||
|
|
||||||
|
import cn.odboy.base.KitPageArgs;
|
||||||
|
import cn.odboy.framework.mybatisplus.core.KitMpQUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态表格辅助工具
|
||||||
|
*
|
||||||
|
* @author odboy
|
||||||
|
*/
|
||||||
|
@UtilityClass
|
||||||
|
public class KitDynamicTableBuilder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染排序规则
|
||||||
|
*
|
||||||
|
* @param pageArgs 分页参数
|
||||||
|
* @param <M> 数据表对象
|
||||||
|
* @param <T> 渲染对象
|
||||||
|
* @return /
|
||||||
|
*/
|
||||||
|
public static <M, T> QueryWrapper<M> ofOrder(KitPageArgs<T> pageArgs) {
|
||||||
|
QueryWrapper<M> wrapper = new QueryWrapper<>();
|
||||||
|
KitPageArgs.OrderBy orderBy = pageArgs.getOrderBy();
|
||||||
|
if (orderBy != null) {
|
||||||
|
wrapper.orderBy(true, orderBy.isAsc(), orderBy.getColumn());
|
||||||
|
}
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染所有规则(需要配合 @KitMpQuery 注解使用)
|
||||||
|
*
|
||||||
|
* @param pageArgs 分页参数
|
||||||
|
* @param <M> 数据表对象
|
||||||
|
* @param <T> 渲染对象
|
||||||
|
* @return /
|
||||||
|
*/
|
||||||
|
public static <M, T> LambdaQueryWrapper<M> ofAll(KitPageArgs<T> pageArgs) {
|
||||||
|
QueryWrapper<M> wrapper = KitMpQUtil.build(pageArgs.getArgs());
|
||||||
|
KitPageArgs.OrderBy orderBy = pageArgs.getOrderBy();
|
||||||
|
if (orderBy != null) {
|
||||||
|
wrapper.orderBy(true, orderBy.isAsc(), orderBy.getColumn());
|
||||||
|
}
|
||||||
|
return wrapper.lambda();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2021-2026 Odboy
|
|
||||||
*
|
|
||||||
* 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 cn.odboy.feature;
|
|
||||||
|
|
||||||
import cn.odboy.base.KitPageArgs;
|
|
||||||
import cn.odboy.framework.mybatisplus.core.KitMpQUtil;
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
||||||
import lombok.experimental.UtilityClass;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 动态表格辅助工具
|
|
||||||
*
|
|
||||||
* @author odboy
|
|
||||||
*/
|
|
||||||
@UtilityClass
|
|
||||||
public class KitDynamicTableUtil {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 渲染排序规则
|
|
||||||
*
|
|
||||||
* @param pageArgs 分页参数
|
|
||||||
* @param <M> 数据表对象
|
|
||||||
* @param <T> 渲染对象
|
|
||||||
* @return /
|
|
||||||
*/
|
|
||||||
public static <M, T> QueryWrapper<M> renderOrder(KitPageArgs<T> pageArgs) {
|
|
||||||
QueryWrapper<M> wrapper = new QueryWrapper<>();
|
|
||||||
KitPageArgs.OrderBy orderBy = pageArgs.getOrderBy();
|
|
||||||
if (orderBy != null) {
|
|
||||||
wrapper.orderBy(true, orderBy.isAsc(), orderBy.getColumn());
|
|
||||||
}
|
|
||||||
return wrapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 渲染所有规则(需要配合 @KitMpQuery 注解使用)
|
|
||||||
*
|
|
||||||
* @param pageArgs 分页参数
|
|
||||||
* @param <M> 数据表对象
|
|
||||||
* @param <T> 渲染对象
|
|
||||||
* @return /
|
|
||||||
*/
|
|
||||||
public static <M, T> LambdaQueryWrapper<M> renderAll(KitPageArgs<T> pageArgs) {
|
|
||||||
QueryWrapper<M> wrapper = KitMpQUtil.build(pageArgs.getArgs());
|
|
||||||
KitPageArgs.OrderBy orderBy = pageArgs.getOrderBy();
|
|
||||||
if (orderBy != null) {
|
|
||||||
wrapper.orderBy(true, orderBy.isAsc(), orderBy.getColumn());
|
|
||||||
}
|
|
||||||
return wrapper.lambda();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -30,7 +30,8 @@ public final class KitStringUtil {
|
|||||||
/**
|
/**
|
||||||
* 驼峰命名法工具
|
* 驼峰命名法工具
|
||||||
*
|
*
|
||||||
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld" toUnderScoreCase("helloWorld") = "hello_world"
|
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld"
|
||||||
|
* toUnderScoreCase("helloWorld") = "hello_world"
|
||||||
*/
|
*/
|
||||||
public static String toCamelCase(String s) {
|
public static String toCamelCase(String s) {
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
@@ -56,7 +57,8 @@ public final class KitStringUtil {
|
|||||||
/**
|
/**
|
||||||
* 驼峰命名法工具
|
* 驼峰命名法工具
|
||||||
*
|
*
|
||||||
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld" toUnderScoreCase("helloWorld") = "hello_world"
|
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld"
|
||||||
|
* toUnderScoreCase("helloWorld") = "hello_world"
|
||||||
*/
|
*/
|
||||||
public static String toCapitalizeCamelCase(String s) {
|
public static String toCapitalizeCamelCase(String s) {
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
@@ -69,7 +71,8 @@ public final class KitStringUtil {
|
|||||||
/**
|
/**
|
||||||
* 驼峰命名法工具
|
* 驼峰命名法工具
|
||||||
*
|
*
|
||||||
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld" toUnderScoreCase("helloWorld") = "hello_world"
|
* @return toCamelCase(" hello_world ") == "helloWorld" toCapitalizeCamelCase("hello_world") == "HelloWorld"
|
||||||
|
* toUnderScoreCase("helloWorld") = "hello_world"
|
||||||
*/
|
*/
|
||||||
public static String toUnderScoreCase(String s) {
|
public static String toUnderScoreCase(String s) {
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
@@ -95,4 +98,37 @@ public final class KitStringUtil {
|
|||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化字节数
|
||||||
|
*
|
||||||
|
* @param storage 字节数
|
||||||
|
* @return /
|
||||||
|
*/
|
||||||
|
private String renderBytes(Long storage) {
|
||||||
|
if (storage == null || storage <= 0) {
|
||||||
|
return "0 Byte";
|
||||||
|
}
|
||||||
|
|
||||||
|
double value = storage.doubleValue();
|
||||||
|
final long KB = 1024L;
|
||||||
|
final long MB = KB * 1024L;
|
||||||
|
final long GB = MB * 1024L;
|
||||||
|
final long TB = GB * 1024L;
|
||||||
|
final long PB = TB * 1024L;
|
||||||
|
|
||||||
|
if (value >= PB) {
|
||||||
|
return String.format("%.2f PB", value / PB);
|
||||||
|
} else if (value >= TB) {
|
||||||
|
return String.format("%.2f TB", value / TB);
|
||||||
|
} else if (value >= GB) {
|
||||||
|
return String.format("%.2f GB", value / GB);
|
||||||
|
} else if (value >= MB) {
|
||||||
|
return String.format("%.2f MB", value / MB);
|
||||||
|
} else if (value >= KB) {
|
||||||
|
return String.format("%.2f KB", value / KB);
|
||||||
|
} else {
|
||||||
|
return String.format("%d Byte", storage);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -18,7 +18,7 @@ package cn.odboy.system.features;
|
|||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.odboy.base.KitPageArgs;
|
import cn.odboy.base.KitPageArgs;
|
||||||
import cn.odboy.feature.KitDynamicTableResponse;
|
import cn.odboy.feature.KitDynamicTableResponse;
|
||||||
import cn.odboy.feature.KitDynamicTableUtil;
|
import cn.odboy.feature.KitDynamicTableBuilder;
|
||||||
import cn.odboy.system.dal.dataobject.SystemMenuTb;
|
import cn.odboy.system.dal.dataobject.SystemMenuTb;
|
||||||
import cn.odboy.system.dal.model.request.SystemQueryMenuArgs;
|
import cn.odboy.system.dal.model.request.SystemQueryMenuArgs;
|
||||||
import cn.odboy.system.dal.mysql.SystemMenuMapper;
|
import cn.odboy.system.dal.mysql.SystemMenuMapper;
|
||||||
@@ -81,7 +81,7 @@ public class CuteDynamicTableDemoApi {
|
|||||||
@PreAuthorize("@el.check()")
|
@PreAuthorize("@el.check()")
|
||||||
public ResponseEntity<KitDynamicTableResponse<SystemMenuTb, SystemMenuDynamicTableModel>> searchMenu2(@Validated @RequestBody KitPageArgs<SystemQueryMenuArgs> pageArgs) {
|
public ResponseEntity<KitDynamicTableResponse<SystemMenuTb, SystemMenuDynamicTableModel>> searchMenu2(@Validated @RequestBody KitPageArgs<SystemQueryMenuArgs> pageArgs) {
|
||||||
// 渲染排序(非必须)
|
// 渲染排序(非必须)
|
||||||
QueryWrapper<SystemMenuTb> wrapper = KitDynamicTableUtil.renderOrder(pageArgs);
|
QueryWrapper<SystemMenuTb> wrapper = KitDynamicTableBuilder.ofOrder(pageArgs);
|
||||||
LambdaQueryWrapper<SystemMenuTb> lambda = wrapper.lambda();
|
LambdaQueryWrapper<SystemMenuTb> lambda = wrapper.lambda();
|
||||||
// 查询条件
|
// 查询条件
|
||||||
SystemQueryMenuArgs args = pageArgs.getArgs();
|
SystemQueryMenuArgs args = pageArgs.getArgs();
|
||||||
@@ -98,7 +98,7 @@ public class CuteDynamicTableDemoApi {
|
|||||||
@PreAuthorize("@el.check()")
|
@PreAuthorize("@el.check()")
|
||||||
public ResponseEntity<KitDynamicTableResponse<SystemMenuTb, SystemMenuDynamicTableModel>> searchMenu3(@Validated @RequestBody KitPageArgs<SystemQueryMenuArgs> pageArgs) {
|
public ResponseEntity<KitDynamicTableResponse<SystemMenuTb, SystemMenuDynamicTableModel>> searchMenu3(@Validated @RequestBody KitPageArgs<SystemQueryMenuArgs> pageArgs) {
|
||||||
// 渲染查询条件
|
// 渲染查询条件
|
||||||
LambdaQueryWrapper<SystemMenuTb> wrapper = KitDynamicTableUtil.renderAll(pageArgs);
|
LambdaQueryWrapper<SystemMenuTb> wrapper = KitDynamicTableBuilder.ofAll(pageArgs);
|
||||||
// 响应结果
|
// 响应结果
|
||||||
return ResponseEntity.ok(new KitDynamicTableResponse<>(SystemMenuDynamicTableModel.class, pageArgs, "id", systemMenuMapper, wrapper));
|
return ResponseEntity.ok(new KitDynamicTableResponse<>(SystemMenuDynamicTableModel.class, pageArgs, "id", systemMenuMapper, wrapper));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package cn.odboy;
|
||||||
|
|
||||||
|
import cn.odboy.system.dal.dataobject.SystemDeptTb;
|
||||||
|
import cn.odboy.util.KitClassUtil;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mybatis批量操作生成器
|
||||||
|
*
|
||||||
|
* @author odboy
|
||||||
|
*/
|
||||||
|
public class MybatisMapperBatchOpRender {
|
||||||
|
/**
|
||||||
|
* 持久类名
|
||||||
|
*/
|
||||||
|
private String entityClassName;
|
||||||
|
/**
|
||||||
|
* 持久类主键类型名
|
||||||
|
*/
|
||||||
|
private String entityPkTypeName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染MapperXml
|
||||||
|
*
|
||||||
|
* @param entityClass 持久类
|
||||||
|
* @param keyMapper 哪个是主键
|
||||||
|
*/
|
||||||
|
public <T, K> void render(Class<?> entityClass, Function<? super T, ? extends K> keyMapper) {
|
||||||
|
String mapperBatchInsert = "void batchInsert(@Param(\"list\") List<$EntityClassName> list);";
|
||||||
|
System.err.println(mapperBatchInsert.replace("$EntityClassName", this.entityClassName));
|
||||||
|
String mapperBatchUpdate = "void batchUpdateById(@Param(\"list\") List<$EntityClassName> list);";
|
||||||
|
System.err.println(mapperBatchUpdate.replace("$EntityClassName", this.entityClassName));
|
||||||
|
String mapperBatchDelete = "void batchDeleteByIds(@Param(\"list\") List<$EntityPkTypeName> list);";
|
||||||
|
System.err.println(mapperBatchDelete.replace("$EntityPkTypeName", this.entityPkTypeName));
|
||||||
|
|
||||||
|
List<Field> fields = new ArrayList<>();
|
||||||
|
KitClassUtil.getAllFields(entityClass, fields);
|
||||||
|
|
||||||
|
fields = fields.stream().filter(f -> !"serialVersionUID".equals(f.getName())).collect(Collectors.toList());
|
||||||
|
|
||||||
|
System.err.println(fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
new MybatisMapperBatchOpRender().render(SystemDeptTb.class, SystemDeptTb::getId);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user