mirror of
https://gitee.com/xiaonuobase/snowy.git
synced 2026-03-22 02:37:16 +08:00
【升级】彻底优化登录逻辑、大数据量下的机构以及in拆分
This commit is contained in:
@@ -58,6 +58,7 @@
|
||||
<script setup name="bizOrgForm">
|
||||
import { required } from '@/utils/formRules'
|
||||
import bizOrgApi from '@/api/biz/bizOrgApi'
|
||||
import userCenterApi from '@/api/sys/userCenterApi'
|
||||
import tool from '@/utils/tool'
|
||||
|
||||
// 定义emit事件
|
||||
@@ -72,6 +73,30 @@
|
||||
const submitLoading = ref(false)
|
||||
const treeFieldNames = { children: 'children', title: 'name', key: 'id' }
|
||||
|
||||
// 在树中递归查找节点
|
||||
const findNodeInTree = (nodes, id) => {
|
||||
if (!nodes) return false
|
||||
for (const node of nodes) {
|
||||
if (node.id === id) return true
|
||||
if (node.children && findNodeInTree(node.children, id)) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
// 确保选中的机构节点在树中可回显名称
|
||||
const ensureOrgInTree = (orgId) => {
|
||||
if (!orgId || orgId === '0' || findNodeInTree(treeData.value, orgId)) return
|
||||
userCenterApi.userCenterGetOrgListByIdList({ idList: [orgId] }).then((data) => {
|
||||
if (data && data.length > 0 && treeData.value.length > 0) {
|
||||
const topNode = treeData.value[0]
|
||||
if (topNode.children) {
|
||||
topNode.children.push({ ...data[0], isLeaf: true })
|
||||
} else {
|
||||
topNode.children = [{ ...data[0], isLeaf: true }]
|
||||
}
|
||||
treeData.value = [...treeData.value]
|
||||
}
|
||||
})
|
||||
}
|
||||
// 打开抽屉
|
||||
const onOpen = (record, parentId) => {
|
||||
visible.value = true
|
||||
@@ -81,16 +106,8 @@
|
||||
if (parentId) {
|
||||
formData.value.parentId = parentId
|
||||
}
|
||||
if (record) {
|
||||
const param = {
|
||||
id: record.id
|
||||
}
|
||||
bizOrgApi.orgDetail(param).then((data) => {
|
||||
formData.value = Object.assign({}, data)
|
||||
})
|
||||
}
|
||||
// 获取机构树并加入顶级
|
||||
bizOrgApi.orgTreeLazySelector().then((res) => {
|
||||
const treePromise = bizOrgApi.orgTreeLazySelector().then((res) => {
|
||||
treeData.value = [
|
||||
{
|
||||
id: '0',
|
||||
@@ -106,6 +123,18 @@
|
||||
}
|
||||
]
|
||||
})
|
||||
if (record) {
|
||||
const detailPromise = bizOrgApi.orgDetail({ id: record.id }).then((data) => {
|
||||
formData.value = Object.assign({}, data)
|
||||
return data
|
||||
})
|
||||
// 等待树和详情都加载完成后,确保父节点可回显
|
||||
Promise.all([treePromise, detailPromise]).then(([_, detail]) => {
|
||||
if (detail.parentId) {
|
||||
ensureOrgInTree(detail.parentId)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
// 懒加载子节点
|
||||
const onLoadData = (treeNode) => {
|
||||
|
||||
@@ -1,18 +1,28 @@
|
||||
<template>
|
||||
<XnResizablePanel direction="row" :initial-size="300" :min-size="200" :max-size="500" :md="0">
|
||||
<template #left>
|
||||
<div ref="treeContainerRef" style="height: 100%">
|
||||
<xn-tree-skeleton v-if="treeLoading && treeData.length === 0" />
|
||||
<a-tree
|
||||
v-else-if="treeData.length > 0"
|
||||
v-model:expandedKeys="defaultExpandedKeys"
|
||||
:tree-data="treeData"
|
||||
:field-names="treeFieldNames"
|
||||
:load-data="onLoadData"
|
||||
:height="treeHeight"
|
||||
@select="treeSelect"
|
||||
<div ref="treeContainerRef" style="height: 100%; display: flex; flex-direction: column">
|
||||
<a-input-search
|
||||
v-model:value="treeSearchKey"
|
||||
placeholder="搜索机构"
|
||||
allow-clear
|
||||
size="small"
|
||||
style="margin-bottom: 8px; flex-shrink: 0"
|
||||
@search="onTreeSearch"
|
||||
/>
|
||||
<a-empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" />
|
||||
<div style="flex: 1; overflow: hidden">
|
||||
<xn-tree-skeleton v-if="treeLoading && treeData.length === 0" />
|
||||
<a-tree
|
||||
v-else-if="treeData.length > 0"
|
||||
v-model:expandedKeys="defaultExpandedKeys"
|
||||
:tree-data="treeData"
|
||||
:field-names="treeFieldNames"
|
||||
:load-data="searchMode ? undefined : onLoadData"
|
||||
:height="treeHeight"
|
||||
@select="treeSelect"
|
||||
/>
|
||||
<a-empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #right>
|
||||
@@ -179,7 +189,7 @@
|
||||
let resizeObserver = null
|
||||
const calcTreeHeight = () => {
|
||||
if (treeContainerRef.value) {
|
||||
treeHeight.value = treeContainerRef.value.clientHeight
|
||||
treeHeight.value = treeContainerRef.value.clientHeight - 40
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
@@ -208,6 +218,44 @@
|
||||
tableRef.value.refresh(true)
|
||||
}
|
||||
const treeLoading = ref(true)
|
||||
const treeSearchKey = ref('')
|
||||
const searchMode = ref(false)
|
||||
// 收集树所有节点key,用于搜索时全部展开
|
||||
const collectTreeKeys = (nodes) => {
|
||||
const keys = []
|
||||
const traverse = (list) => {
|
||||
if (!list) return
|
||||
list.forEach((node) => {
|
||||
keys.push(node.id)
|
||||
if (node.children) traverse(node.children)
|
||||
})
|
||||
}
|
||||
traverse(nodes)
|
||||
return keys
|
||||
}
|
||||
// 树搜索
|
||||
const onTreeSearch = (value) => {
|
||||
if (!value || !value.trim()) {
|
||||
searchMode.value = false
|
||||
loadTreeData()
|
||||
return
|
||||
}
|
||||
treeLoading.value = true
|
||||
searchMode.value = true
|
||||
bizOrgApi
|
||||
.orgTree({ searchKey: value.trim() })
|
||||
.then((res) => {
|
||||
if (res !== null) {
|
||||
treeData.value = res
|
||||
defaultExpandedKeys.value = collectTreeKeys(res)
|
||||
} else {
|
||||
treeData.value = []
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
treeLoading.value = false
|
||||
})
|
||||
}
|
||||
// 加载左侧的树
|
||||
const loadTreeData = () => {
|
||||
treeLoading.value = true
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
<script setup name="bizPositionForm">
|
||||
import { required } from '@/utils/formRules'
|
||||
import bizPositionApi from '@/api/biz/bizPositionApi'
|
||||
import userCenterApi from '@/api/sys/userCenterApi'
|
||||
import tool from '@/utils/tool'
|
||||
|
||||
// 定义emit事件
|
||||
@@ -63,6 +64,25 @@
|
||||
const treeData = ref([])
|
||||
const submitLoading = ref(false)
|
||||
|
||||
// 在树中递归查找节点
|
||||
const findNodeInTree = (nodes, id) => {
|
||||
if (!nodes) return false
|
||||
for (const node of nodes) {
|
||||
if (node.id === id) return true
|
||||
if (node.children && findNodeInTree(node.children, id)) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
// 确保选中的机构节点在树中可回显名称
|
||||
const ensureOrgInTree = (orgId) => {
|
||||
if (!orgId || findNodeInTree(treeData.value, orgId)) return
|
||||
userCenterApi.userCenterGetOrgListByIdList({ idList: [orgId] }).then((data) => {
|
||||
if (data && data.length > 0) {
|
||||
treeData.value.push({ ...data[0], isLeaf: true })
|
||||
treeData.value = [...treeData.value]
|
||||
}
|
||||
})
|
||||
}
|
||||
// 打开抽屉
|
||||
const onOpen = (record, orgId) => {
|
||||
visible.value = true
|
||||
@@ -83,6 +103,10 @@
|
||||
isLeaf: item.isLeaf === undefined ? false : item.isLeaf
|
||||
}
|
||||
})
|
||||
// 编辑时确保选中的机构可回显
|
||||
if (record && formData.value.orgId) {
|
||||
ensureOrgInTree(formData.value.orgId)
|
||||
}
|
||||
})
|
||||
}
|
||||
// 懒加载子节点
|
||||
|
||||
@@ -1,18 +1,28 @@
|
||||
<template>
|
||||
<XnResizablePanel direction="row" :initial-size="300" :min-size="200" :max-size="500" :md="0">
|
||||
<template #left>
|
||||
<div ref="treeContainerRef" style="height: 100%">
|
||||
<xn-tree-skeleton v-if="treeLoading && treeData.length === 0" />
|
||||
<a-tree
|
||||
v-else-if="treeData.length > 0"
|
||||
v-model:expandedKeys="defaultExpandedKeys"
|
||||
:tree-data="treeData"
|
||||
:field-names="treeFieldNames"
|
||||
:load-data="onLoadData"
|
||||
:height="treeHeight"
|
||||
@select="treeSelect"
|
||||
<div ref="treeContainerRef" style="height: 100%; display: flex; flex-direction: column">
|
||||
<a-input-search
|
||||
v-model:value="treeSearchKey"
|
||||
placeholder="搜索机构"
|
||||
allow-clear
|
||||
size="small"
|
||||
style="margin-bottom: 8px; flex-shrink: 0"
|
||||
@search="onTreeSearch"
|
||||
/>
|
||||
<a-empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" />
|
||||
<div style="flex: 1; overflow: hidden">
|
||||
<xn-tree-skeleton v-if="treeLoading && treeData.length === 0" />
|
||||
<a-tree
|
||||
v-else-if="treeData.length > 0"
|
||||
v-model:expandedKeys="defaultExpandedKeys"
|
||||
:tree-data="treeData"
|
||||
:field-names="treeFieldNames"
|
||||
:load-data="searchMode ? undefined : onLoadData"
|
||||
:height="treeHeight"
|
||||
@select="treeSelect"
|
||||
/>
|
||||
<a-empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #right>
|
||||
@@ -170,7 +180,7 @@
|
||||
let resizeObserver = null
|
||||
const calcTreeHeight = () => {
|
||||
if (treeContainerRef.value) {
|
||||
treeHeight.value = treeContainerRef.value.clientHeight
|
||||
treeHeight.value = treeContainerRef.value.clientHeight - 40
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
@@ -199,6 +209,42 @@
|
||||
tableRef.value.refresh(true)
|
||||
}
|
||||
const treeLoading = ref(true)
|
||||
const treeSearchKey = ref('')
|
||||
const searchMode = ref(false)
|
||||
const collectTreeKeys = (nodes) => {
|
||||
const keys = []
|
||||
const traverse = (list) => {
|
||||
if (!list) return
|
||||
list.forEach((node) => {
|
||||
keys.push(node.id)
|
||||
if (node.children) traverse(node.children)
|
||||
})
|
||||
}
|
||||
traverse(nodes)
|
||||
return keys
|
||||
}
|
||||
const onTreeSearch = (value) => {
|
||||
if (!value || !value.trim()) {
|
||||
searchMode.value = false
|
||||
loadTreeData()
|
||||
return
|
||||
}
|
||||
treeLoading.value = true
|
||||
searchMode.value = true
|
||||
bizOrgApi
|
||||
.orgTree({ searchKey: value.trim() })
|
||||
.then((res) => {
|
||||
if (res !== null) {
|
||||
treeData.value = res
|
||||
defaultExpandedKeys.value = collectTreeKeys(res)
|
||||
} else {
|
||||
treeData.value = []
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
treeLoading.value = false
|
||||
})
|
||||
}
|
||||
// 加载左侧的树
|
||||
const loadTreeData = () => {
|
||||
treeLoading.value = true
|
||||
|
||||
@@ -322,6 +322,30 @@
|
||||
const formData = ref({})
|
||||
const treeFieldNames = { children: 'children', title: 'dictLabel', key: 'id' }
|
||||
|
||||
// 在树中递归查找节点
|
||||
const findNodeInTree = (nodes, id) => {
|
||||
if (!nodes) return false
|
||||
for (const node of nodes) {
|
||||
if (node.id === id) return true
|
||||
if (node.children && findNodeInTree(node.children, id)) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
// 确保选中的机构节点在树中可回显名称
|
||||
const ensureOrgsInTree = (orgIds) => {
|
||||
const missingIds = orgIds.filter((id) => id && !findNodeInTree(treeData.value, id))
|
||||
if (missingIds.length === 0) return
|
||||
userCenterApi.userCenterGetOrgListByIdList({ idList: missingIds }).then((data) => {
|
||||
if (data && data.length > 0) {
|
||||
data.forEach((org) => {
|
||||
treeData.value.push({ ...org, isLeaf: true })
|
||||
})
|
||||
treeData.value = [...treeData.value]
|
||||
}
|
||||
})
|
||||
}
|
||||
// 树加载Promise,用于协调回显时序
|
||||
let treeLoadPromise = null
|
||||
// 打开抽屉
|
||||
const onOpen = (record, orgId) => {
|
||||
visible.value = true
|
||||
@@ -341,7 +365,7 @@
|
||||
}
|
||||
nextTick(() => {
|
||||
// 机构选择器数据
|
||||
bizUserApi.userOrgTreeLazySelector().then((res) => {
|
||||
treeLoadPromise = bizUserApi.userOrgTreeLazySelector().then((res) => {
|
||||
if (res !== null) {
|
||||
treeData.value = res.map((item) => {
|
||||
return {
|
||||
@@ -392,7 +416,7 @@
|
||||
id: record.id
|
||||
}
|
||||
// 查询详情
|
||||
bizUserApi.userDetail(param).then((data) => {
|
||||
const detailPromise = bizUserApi.userDetail(param).then((data) => {
|
||||
if (data.positionJson) {
|
||||
// 替换表单中的格式与后端查到的
|
||||
data.positionJson = JSON.parse(data.positionJson)
|
||||
@@ -407,6 +431,24 @@
|
||||
})
|
||||
}
|
||||
selePositionData(formData.value.orgId)
|
||||
return data
|
||||
})
|
||||
// 等待树和详情都加载完成后,确保机构节点可回显
|
||||
nextTick(() => {
|
||||
if (treeLoadPromise) {
|
||||
Promise.all([treeLoadPromise, detailPromise]).then(([_, detail]) => {
|
||||
const orgIds = [detail.orgId]
|
||||
if (detail.positionJson) {
|
||||
const positions = typeof detail.positionJson === 'string'
|
||||
? JSON.parse(detail.positionJson)
|
||||
: detail.positionJson
|
||||
positions.forEach((item) => {
|
||||
if (item.orgId) orgIds.push(item.orgId)
|
||||
})
|
||||
}
|
||||
ensureOrgsInTree(orgIds)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
// 默认要校验的
|
||||
|
||||
@@ -1,18 +1,28 @@
|
||||
<template>
|
||||
<XnResizablePanel direction="row" :initial-size="300" :min-size="200" :max-size="500" :md="0">
|
||||
<template #left>
|
||||
<div ref="treeContainerRef" style="height: 100%">
|
||||
<xn-tree-skeleton v-if="treeLoading && treeData.length === 0" />
|
||||
<a-tree
|
||||
v-else-if="treeData.length > 0"
|
||||
v-model:expandedKeys="defaultExpandedKeys"
|
||||
:tree-data="treeData"
|
||||
:field-names="treeFieldNames"
|
||||
:load-data="onLoadData"
|
||||
:height="treeHeight"
|
||||
@select="treeSelect"
|
||||
<div ref="treeContainerRef" style="height: 100%; display: flex; flex-direction: column">
|
||||
<a-input-search
|
||||
v-model:value="treeSearchKey"
|
||||
placeholder="搜索机构"
|
||||
allow-clear
|
||||
size="small"
|
||||
style="margin-bottom: 8px; flex-shrink: 0"
|
||||
@search="onTreeSearch"
|
||||
/>
|
||||
<a-empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" />
|
||||
<div style="flex: 1; overflow: hidden">
|
||||
<xn-tree-skeleton v-if="treeLoading && treeData.length === 0" />
|
||||
<a-tree
|
||||
v-else-if="treeData.length > 0"
|
||||
v-model:expandedKeys="defaultExpandedKeys"
|
||||
:tree-data="treeData"
|
||||
:field-names="treeFieldNames"
|
||||
:load-data="searchMode ? undefined : onLoadData"
|
||||
:height="treeHeight"
|
||||
@select="treeSelect"
|
||||
/>
|
||||
<a-empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #right>
|
||||
@@ -246,7 +256,7 @@
|
||||
let resizeObserver = null
|
||||
const calcTreeHeight = () => {
|
||||
if (treeContainerRef.value) {
|
||||
treeHeight.value = treeContainerRef.value.clientHeight
|
||||
treeHeight.value = treeContainerRef.value.clientHeight - 40
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
@@ -274,6 +284,42 @@
|
||||
tableRef.value.refresh(true)
|
||||
}
|
||||
const treeLoading = ref(true)
|
||||
const treeSearchKey = ref('')
|
||||
const searchMode = ref(false)
|
||||
const collectTreeKeys = (nodes) => {
|
||||
const keys = []
|
||||
const traverse = (list) => {
|
||||
if (!list) return
|
||||
list.forEach((node) => {
|
||||
keys.push(node.id)
|
||||
if (node.children) traverse(node.children)
|
||||
})
|
||||
}
|
||||
traverse(nodes)
|
||||
return keys
|
||||
}
|
||||
const onTreeSearch = (value) => {
|
||||
if (!value || !value.trim()) {
|
||||
searchMode.value = false
|
||||
loadTreeData()
|
||||
return
|
||||
}
|
||||
treeLoading.value = true
|
||||
searchMode.value = true
|
||||
bizOrgApi
|
||||
.orgTree({ searchKey: value.trim() })
|
||||
.then((res) => {
|
||||
if (res !== null) {
|
||||
treeData.value = res
|
||||
defaultExpandedKeys.value = collectTreeKeys(res)
|
||||
} else {
|
||||
treeData.value = []
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
treeLoading.value = false
|
||||
})
|
||||
}
|
||||
// 加载左侧树
|
||||
const loadTreeData = () => {
|
||||
treeLoading.value = true
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
<script setup name="orgForm">
|
||||
import { required } from '@/utils/formRules'
|
||||
import orgApi from '@/api/sys/orgApi'
|
||||
import userCenterApi from '@/api/sys/userCenterApi'
|
||||
import tool from '@/utils/tool'
|
||||
|
||||
// 定义emit事件
|
||||
@@ -71,6 +72,30 @@
|
||||
const treeData = ref([])
|
||||
const submitLoading = ref(false)
|
||||
|
||||
// 在树中递归查找节点
|
||||
const findNodeInTree = (nodes, id) => {
|
||||
if (!nodes) return false
|
||||
for (const node of nodes) {
|
||||
if (node.id === id) return true
|
||||
if (node.children && findNodeInTree(node.children, id)) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
// 确保选中的机构节点在树中可回显名称
|
||||
const ensureOrgInTree = (orgId) => {
|
||||
if (!orgId || orgId === '0' || findNodeInTree(treeData.value, orgId)) return
|
||||
userCenterApi.userCenterGetOrgListByIdList({ idList: [orgId] }).then((data) => {
|
||||
if (data && data.length > 0 && treeData.value.length > 0) {
|
||||
const topNode = treeData.value[0]
|
||||
if (topNode.children) {
|
||||
topNode.children.push({ ...data[0], isLeaf: true })
|
||||
} else {
|
||||
topNode.children = [{ ...data[0], isLeaf: true }]
|
||||
}
|
||||
treeData.value = [...treeData.value]
|
||||
}
|
||||
})
|
||||
}
|
||||
// 打开抽屉
|
||||
const onOpen = (record, parentId) => {
|
||||
visible.value = true
|
||||
@@ -80,16 +105,8 @@
|
||||
if (parentId) {
|
||||
formData.value.parentId = parentId
|
||||
}
|
||||
if (record) {
|
||||
const param = {
|
||||
id: record.id
|
||||
}
|
||||
orgApi.orgDetail(param).then((data) => {
|
||||
formData.value = Object.assign({}, data)
|
||||
})
|
||||
}
|
||||
// 获取机构树并加入顶级
|
||||
orgApi.orgOrgTreeLazySelector().then((res) => {
|
||||
const treePromise = orgApi.orgOrgTreeLazySelector().then((res) => {
|
||||
treeData.value = [
|
||||
{
|
||||
id: '0',
|
||||
@@ -105,6 +122,18 @@
|
||||
}
|
||||
]
|
||||
})
|
||||
if (record) {
|
||||
const detailPromise = orgApi.orgDetail({ id: record.id }).then((data) => {
|
||||
formData.value = Object.assign({}, data)
|
||||
return data
|
||||
})
|
||||
// 等待树和详情都加载完成后,确保父节点可回显
|
||||
Promise.all([treePromise, detailPromise]).then(([_, detail]) => {
|
||||
if (detail.parentId) {
|
||||
ensureOrgInTree(detail.parentId)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
// 懒加载子节点
|
||||
const onLoadData = (treeNode) => {
|
||||
|
||||
@@ -1,18 +1,28 @@
|
||||
<template>
|
||||
<XnResizablePanel direction="row" :initial-size="300" :min-size="200" :max-size="500" :md="0">
|
||||
<template #left>
|
||||
<div ref="treeContainerRef" style="height: 100%">
|
||||
<xn-tree-skeleton v-if="treeLoading && treeData.length === 0" />
|
||||
<a-tree
|
||||
v-else-if="treeData.length > 0"
|
||||
v-model:expandedKeys="defaultExpandedKeys"
|
||||
:tree-data="treeData"
|
||||
:field-names="treeFieldNames"
|
||||
:load-data="onLoadData"
|
||||
:height="treeHeight"
|
||||
@select="treeSelect"
|
||||
<div ref="treeContainerRef" style="height: 100%; display: flex; flex-direction: column">
|
||||
<a-input-search
|
||||
v-model:value="treeSearchKey"
|
||||
placeholder="搜索组织"
|
||||
allow-clear
|
||||
size="small"
|
||||
style="margin-bottom: 8px; flex-shrink: 0"
|
||||
@search="onTreeSearch"
|
||||
/>
|
||||
<a-empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" />
|
||||
<div style="flex: 1; overflow: hidden">
|
||||
<xn-tree-skeleton v-if="treeLoading && treeData.length === 0" />
|
||||
<a-tree
|
||||
v-else-if="treeData.length > 0"
|
||||
v-model:expandedKeys="defaultExpandedKeys"
|
||||
:tree-data="treeData"
|
||||
:field-names="treeFieldNames"
|
||||
:load-data="searchMode ? undefined : onLoadData"
|
||||
:height="treeHeight"
|
||||
@select="treeSelect"
|
||||
/>
|
||||
<a-empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #right>
|
||||
@@ -176,7 +186,8 @@
|
||||
let resizeObserver = null
|
||||
const calcTreeHeight = () => {
|
||||
if (treeContainerRef.value) {
|
||||
treeHeight.value = treeContainerRef.value.clientHeight
|
||||
// 减去搜索框高度和间距
|
||||
treeHeight.value = treeContainerRef.value.clientHeight - 40
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
@@ -205,6 +216,45 @@
|
||||
tableRef.value.refresh(true)
|
||||
}
|
||||
const treeLoading = ref(true)
|
||||
const treeSearchKey = ref('')
|
||||
const searchMode = ref(false)
|
||||
// 收集树所有节点key,用于搜索时全部展开
|
||||
const collectTreeKeys = (nodes) => {
|
||||
const keys = []
|
||||
const traverse = (list) => {
|
||||
if (!list) return
|
||||
list.forEach((node) => {
|
||||
keys.push(node.id)
|
||||
if (node.children) traverse(node.children)
|
||||
})
|
||||
}
|
||||
traverse(nodes)
|
||||
return keys
|
||||
}
|
||||
// 树搜索
|
||||
const onTreeSearch = (value) => {
|
||||
if (!value || !value.trim()) {
|
||||
// 清空搜索,恢复懒加载模式
|
||||
searchMode.value = false
|
||||
loadTreeData()
|
||||
return
|
||||
}
|
||||
treeLoading.value = true
|
||||
searchMode.value = true
|
||||
orgApi
|
||||
.orgTree({ searchKey: value.trim() })
|
||||
.then((res) => {
|
||||
if (res !== null) {
|
||||
treeData.value = res
|
||||
defaultExpandedKeys.value = collectTreeKeys(res)
|
||||
} else {
|
||||
treeData.value = []
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
treeLoading.value = false
|
||||
})
|
||||
}
|
||||
// 加载左侧的树
|
||||
const loadTreeData = () => {
|
||||
treeLoading.value = true
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
<script setup name="positionForm">
|
||||
import { required } from '@/utils/formRules'
|
||||
import positionApi from '@/api/sys/positionApi'
|
||||
import userCenterApi from '@/api/sys/userCenterApi'
|
||||
import tool from '@/utils/tool'
|
||||
|
||||
// 定义emit事件
|
||||
@@ -62,6 +63,25 @@
|
||||
const treeData = ref([])
|
||||
const submitLoading = ref(false)
|
||||
|
||||
// 在树中递归查找节点
|
||||
const findNodeInTree = (nodes, id) => {
|
||||
if (!nodes) return false
|
||||
for (const node of nodes) {
|
||||
if (node.id === id) return true
|
||||
if (node.children && findNodeInTree(node.children, id)) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
// 确保选中的机构节点在树中可回显名称
|
||||
const ensureOrgInTree = (orgId) => {
|
||||
if (!orgId || findNodeInTree(treeData.value, orgId)) return
|
||||
userCenterApi.userCenterGetOrgListByIdList({ idList: [orgId] }).then((data) => {
|
||||
if (data && data.length > 0) {
|
||||
treeData.value.push({ ...data[0], isLeaf: true })
|
||||
treeData.value = [...treeData.value]
|
||||
}
|
||||
})
|
||||
}
|
||||
// 打开抽屉
|
||||
const onOpen = (record, orgId) => {
|
||||
visible.value = true
|
||||
@@ -82,6 +102,10 @@
|
||||
isLeaf: item.isLeaf === undefined ? false : item.isLeaf
|
||||
}
|
||||
})
|
||||
// 编辑时确保选中的机构可回显
|
||||
if (record && formData.value.orgId) {
|
||||
ensureOrgInTree(formData.value.orgId)
|
||||
}
|
||||
})
|
||||
}
|
||||
// 懒加载子节点
|
||||
|
||||
@@ -1,18 +1,28 @@
|
||||
<template>
|
||||
<XnResizablePanel direction="row" :initial-size="300" :min-size="200" :max-size="500" :md="0">
|
||||
<template #left>
|
||||
<div ref="treeContainerRef" style="height: 100%">
|
||||
<xn-tree-skeleton v-if="treeLoading && treeData.length === 0" />
|
||||
<a-tree
|
||||
v-else-if="treeData.length > 0"
|
||||
v-model:expandedKeys="defaultExpandedKeys"
|
||||
:tree-data="treeData"
|
||||
:field-names="treeFieldNames"
|
||||
:load-data="onLoadData"
|
||||
:height="treeHeight"
|
||||
@select="treeSelect"
|
||||
<div ref="treeContainerRef" style="height: 100%; display: flex; flex-direction: column">
|
||||
<a-input-search
|
||||
v-model:value="treeSearchKey"
|
||||
placeholder="搜索组织"
|
||||
allow-clear
|
||||
size="small"
|
||||
style="margin-bottom: 8px; flex-shrink: 0"
|
||||
@search="onTreeSearch"
|
||||
/>
|
||||
<a-empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" />
|
||||
<div style="flex: 1; overflow: hidden">
|
||||
<xn-tree-skeleton v-if="treeLoading && treeData.length === 0" />
|
||||
<a-tree
|
||||
v-else-if="treeData.length > 0"
|
||||
v-model:expandedKeys="defaultExpandedKeys"
|
||||
:tree-data="treeData"
|
||||
:field-names="treeFieldNames"
|
||||
:load-data="searchMode ? undefined : onLoadData"
|
||||
:height="treeHeight"
|
||||
@select="treeSelect"
|
||||
/>
|
||||
<a-empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #right>
|
||||
@@ -167,7 +177,7 @@
|
||||
let resizeObserver = null
|
||||
const calcTreeHeight = () => {
|
||||
if (treeContainerRef.value) {
|
||||
treeHeight.value = treeContainerRef.value.clientHeight
|
||||
treeHeight.value = treeContainerRef.value.clientHeight - 40
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
@@ -196,28 +206,68 @@
|
||||
tableRef.value.refresh(true)
|
||||
}
|
||||
const treeLoading = ref(true)
|
||||
const treeSearchKey = ref('')
|
||||
const searchMode = ref(false)
|
||||
const collectTreeKeys = (nodes) => {
|
||||
const keys = []
|
||||
const traverse = (list) => {
|
||||
if (!list) return
|
||||
list.forEach((node) => {
|
||||
keys.push(node.id)
|
||||
if (node.children) traverse(node.children)
|
||||
})
|
||||
}
|
||||
traverse(nodes)
|
||||
return keys
|
||||
}
|
||||
const onTreeSearch = (value) => {
|
||||
if (!value || !value.trim()) {
|
||||
searchMode.value = false
|
||||
loadTreeData()
|
||||
return
|
||||
}
|
||||
treeLoading.value = true
|
||||
searchMode.value = true
|
||||
orgApi
|
||||
.orgTree({ searchKey: value.trim() })
|
||||
.then((res) => {
|
||||
if (res !== null) {
|
||||
treeData.value = res
|
||||
defaultExpandedKeys.value = collectTreeKeys(res)
|
||||
} else {
|
||||
treeData.value = []
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
treeLoading.value = false
|
||||
})
|
||||
}
|
||||
// 加载左侧的树
|
||||
orgApi
|
||||
.orgTreeLazy()
|
||||
.then((res) => {
|
||||
if (res !== null) {
|
||||
treeData.value = res.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
isLeaf: item.isLeaf === undefined ? false : item.isLeaf
|
||||
}
|
||||
})
|
||||
if (isEmpty(defaultExpandedKeys.value)) {
|
||||
// 默认展开第一级
|
||||
if (treeData.value.length > 0) {
|
||||
defaultExpandedKeys.value.push(treeData.value[0].id)
|
||||
const loadTreeData = () => {
|
||||
treeLoading.value = true
|
||||
orgApi
|
||||
.orgTreeLazy()
|
||||
.then((res) => {
|
||||
if (res !== null) {
|
||||
treeData.value = res.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
isLeaf: item.isLeaf === undefined ? false : item.isLeaf
|
||||
}
|
||||
})
|
||||
if (isEmpty(defaultExpandedKeys.value)) {
|
||||
// 默认展开第一级
|
||||
if (treeData.value.length > 0) {
|
||||
defaultExpandedKeys.value.push(treeData.value[0].id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
treeLoading.value = false
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
treeLoading.value = false
|
||||
})
|
||||
}
|
||||
loadTreeData()
|
||||
// 懒加载子节点
|
||||
const onLoadData = (treeNode) => {
|
||||
return new Promise((resolve) => {
|
||||
|
||||
@@ -1,18 +1,28 @@
|
||||
<template>
|
||||
<XnResizablePanel direction="row" :initial-size="300" :min-size="200" :max-size="500" :md="0">
|
||||
<template #left>
|
||||
<div ref="treeContainerRef" style="height: 100%">
|
||||
<xn-tree-skeleton v-if="treeLoading && treeData.length === 0" />
|
||||
<a-tree
|
||||
v-else-if="treeData.length > 0"
|
||||
v-model:expandedKeys="defaultExpandedKeys"
|
||||
:tree-data="treeData"
|
||||
:field-names="treeFieldNames"
|
||||
:load-data="onLoadData"
|
||||
:height="treeHeight"
|
||||
@select="treeSelect"
|
||||
<div ref="treeContainerRef" style="height: 100%; display: flex; flex-direction: column">
|
||||
<a-input-search
|
||||
v-model:value="treeSearchKey"
|
||||
placeholder="搜索组织"
|
||||
allow-clear
|
||||
size="small"
|
||||
style="margin-bottom: 8px; flex-shrink: 0"
|
||||
@search="onTreeSearch"
|
||||
/>
|
||||
<a-empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" />
|
||||
<div style="flex: 1; overflow: hidden">
|
||||
<xn-tree-skeleton v-if="treeLoading && treeData.length === 0" />
|
||||
<a-tree
|
||||
v-else-if="treeData.length > 0"
|
||||
v-model:expandedKeys="defaultExpandedKeys"
|
||||
:tree-data="treeData"
|
||||
:field-names="treeFieldNames"
|
||||
:load-data="searchMode ? undefined : onLoadData"
|
||||
:height="treeHeight"
|
||||
@select="treeSelect"
|
||||
/>
|
||||
<a-empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #right>
|
||||
@@ -213,7 +223,7 @@
|
||||
let resizeObserver = null
|
||||
const calcTreeHeight = () => {
|
||||
if (treeContainerRef.value) {
|
||||
treeHeight.value = treeContainerRef.value.clientHeight
|
||||
treeHeight.value = treeContainerRef.value.clientHeight - 40
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
@@ -246,6 +256,43 @@
|
||||
tableRef.value.refresh(true)
|
||||
}
|
||||
const treeLoading = ref(true)
|
||||
const treeSearchKey = ref('')
|
||||
const searchMode = ref(false)
|
||||
const collectTreeKeys = (nodes) => {
|
||||
const keys = []
|
||||
const traverse = (list) => {
|
||||
if (!list) return
|
||||
list.forEach((node) => {
|
||||
keys.push(node.id)
|
||||
if (node.children) traverse(node.children)
|
||||
})
|
||||
}
|
||||
traverse(nodes)
|
||||
return keys
|
||||
}
|
||||
const onTreeSearch = (value) => {
|
||||
if (!value || !value.trim()) {
|
||||
searchMode.value = false
|
||||
loadTreeData()
|
||||
return
|
||||
}
|
||||
treeLoading.value = true
|
||||
searchMode.value = true
|
||||
orgApi
|
||||
.orgTree({ searchKey: value.trim() })
|
||||
.then((res) => {
|
||||
if (res !== null) {
|
||||
// 搜索模式下也保留全局节点
|
||||
treeData.value = [{ id: 'GLOBAL', name: '全局', isLeaf: true }, ...res]
|
||||
defaultExpandedKeys.value = collectTreeKeys(res)
|
||||
} else {
|
||||
treeData.value = [{ id: 'GLOBAL', name: '全局', isLeaf: true }]
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
treeLoading.value = false
|
||||
})
|
||||
}
|
||||
// 加载左侧的树
|
||||
const loadTreeData = () => {
|
||||
treeLoading.value = true
|
||||
|
||||
@@ -322,6 +322,30 @@
|
||||
const formData = ref({})
|
||||
const treeFieldNames = { children: 'children', title: 'name', key: 'id' }
|
||||
|
||||
// 在树中递归查找节点
|
||||
const findNodeInTree = (nodes, id) => {
|
||||
if (!nodes) return false
|
||||
for (const node of nodes) {
|
||||
if (node.id === id) return true
|
||||
if (node.children && findNodeInTree(node.children, id)) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
// 确保选中的机构节点在树中可回显名称
|
||||
const ensureOrgsInTree = (orgIds) => {
|
||||
const missingIds = orgIds.filter((id) => id && !findNodeInTree(treeData.value, id))
|
||||
if (missingIds.length === 0) return
|
||||
userCenterApi.userCenterGetOrgListByIdList({ idList: missingIds }).then((data) => {
|
||||
if (data && data.length > 0) {
|
||||
data.forEach((org) => {
|
||||
treeData.value.push({ ...org, isLeaf: true })
|
||||
})
|
||||
treeData.value = [...treeData.value]
|
||||
}
|
||||
})
|
||||
}
|
||||
// 树加载Promise,用于协调回显时序
|
||||
let treeLoadPromise = null
|
||||
// 打开抽屉
|
||||
const onOpen = (record, orgId) => {
|
||||
visible.value = true
|
||||
@@ -341,7 +365,7 @@
|
||||
}
|
||||
nextTick(() => {
|
||||
// 机构选择器数据
|
||||
userApi.userOrgTreeLazySelector().then((res) => {
|
||||
treeLoadPromise = userApi.userOrgTreeLazySelector().then((res) => {
|
||||
if (res !== null) {
|
||||
treeData.value = res.map((item) => {
|
||||
return {
|
||||
@@ -392,7 +416,7 @@
|
||||
id: record.id
|
||||
}
|
||||
// 查询详情
|
||||
userApi.userDetail(param).then((data) => {
|
||||
const detailPromise = userApi.userDetail(param).then((data) => {
|
||||
if (data.positionJson) {
|
||||
// 替换表单中的格式与后端查到的
|
||||
data.positionJson = JSON.parse(data.positionJson)
|
||||
@@ -407,6 +431,24 @@
|
||||
})
|
||||
}
|
||||
selePositionData(formData.value.orgId)
|
||||
return data
|
||||
})
|
||||
// 等待树和详情都加载完成后,确保机构节点可回显
|
||||
nextTick(() => {
|
||||
if (treeLoadPromise) {
|
||||
Promise.all([treeLoadPromise, detailPromise]).then(([_, detail]) => {
|
||||
const orgIds = [detail.orgId]
|
||||
if (detail.positionJson) {
|
||||
const positions = typeof detail.positionJson === 'string'
|
||||
? JSON.parse(detail.positionJson)
|
||||
: detail.positionJson
|
||||
positions.forEach((item) => {
|
||||
if (item.orgId) orgIds.push(item.orgId)
|
||||
})
|
||||
}
|
||||
ensureOrgsInTree(orgIds)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,28 @@
|
||||
<template>
|
||||
<XnResizablePanel direction="row" :initial-size="300" :min-size="200" :max-size="500" :md="0">
|
||||
<template #left>
|
||||
<div ref="treeContainerRef" style="height: 100%">
|
||||
<xn-tree-skeleton v-if="treeLoading && treeData.length === 0" />
|
||||
<a-tree
|
||||
v-else-if="treeData.length > 0"
|
||||
v-model:expandedKeys="defaultExpandedKeys"
|
||||
:tree-data="treeData"
|
||||
:field-names="treeFieldNames"
|
||||
:load-data="onLoadData"
|
||||
:height="treeHeight"
|
||||
@select="treeSelect"
|
||||
<div ref="treeContainerRef" style="height: 100%; display: flex; flex-direction: column">
|
||||
<a-input-search
|
||||
v-model:value="treeSearchKey"
|
||||
placeholder="搜索组织"
|
||||
allow-clear
|
||||
size="small"
|
||||
style="margin-bottom: 8px; flex-shrink: 0"
|
||||
@search="onTreeSearch"
|
||||
/>
|
||||
<a-empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" />
|
||||
<div style="flex: 1; overflow: hidden">
|
||||
<xn-tree-skeleton v-if="treeLoading && treeData.length === 0" />
|
||||
<a-tree
|
||||
v-else-if="treeData.length > 0"
|
||||
v-model:expandedKeys="defaultExpandedKeys"
|
||||
:tree-data="treeData"
|
||||
:field-names="treeFieldNames"
|
||||
:load-data="searchMode ? undefined : onLoadData"
|
||||
:height="treeHeight"
|
||||
@select="treeSelect"
|
||||
/>
|
||||
<a-empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #right>
|
||||
@@ -267,7 +277,7 @@
|
||||
let resizeObserver = null
|
||||
const calcTreeHeight = () => {
|
||||
if (treeContainerRef.value) {
|
||||
treeHeight.value = treeContainerRef.value.clientHeight
|
||||
treeHeight.value = treeContainerRef.value.clientHeight - 40
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
@@ -290,28 +300,68 @@
|
||||
})
|
||||
}
|
||||
const treeLoading = ref(true)
|
||||
const treeSearchKey = ref('')
|
||||
const searchMode = ref(false)
|
||||
const collectTreeKeys = (nodes) => {
|
||||
const keys = []
|
||||
const traverse = (list) => {
|
||||
if (!list) return
|
||||
list.forEach((node) => {
|
||||
keys.push(node.id)
|
||||
if (node.children) traverse(node.children)
|
||||
})
|
||||
}
|
||||
traverse(nodes)
|
||||
return keys
|
||||
}
|
||||
const onTreeSearch = (value) => {
|
||||
if (!value || !value.trim()) {
|
||||
searchMode.value = false
|
||||
loadTreeData()
|
||||
return
|
||||
}
|
||||
treeLoading.value = true
|
||||
searchMode.value = true
|
||||
orgApi
|
||||
.orgTree({ searchKey: value.trim() })
|
||||
.then((res) => {
|
||||
if (res !== null) {
|
||||
treeData.value = res
|
||||
defaultExpandedKeys.value = collectTreeKeys(res)
|
||||
} else {
|
||||
treeData.value = []
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
treeLoading.value = false
|
||||
})
|
||||
}
|
||||
// 左侧树查询
|
||||
orgApi
|
||||
.orgTreeLazy()
|
||||
.then((res) => {
|
||||
if (res !== null) {
|
||||
treeData.value = res.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
isLeaf: item.isLeaf === undefined ? false : item.isLeaf
|
||||
}
|
||||
})
|
||||
if (isEmpty(defaultExpandedKeys.value)) {
|
||||
// 默认展开第一级
|
||||
if (treeData.value.length > 0) {
|
||||
defaultExpandedKeys.value.push(treeData.value[0].id)
|
||||
const loadTreeData = () => {
|
||||
treeLoading.value = true
|
||||
orgApi
|
||||
.orgTreeLazy()
|
||||
.then((res) => {
|
||||
if (res !== null) {
|
||||
treeData.value = res.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
isLeaf: item.isLeaf === undefined ? false : item.isLeaf
|
||||
}
|
||||
})
|
||||
if (isEmpty(defaultExpandedKeys.value)) {
|
||||
// 默认展开第一级
|
||||
if (treeData.value.length > 0) {
|
||||
defaultExpandedKeys.value.push(treeData.value[0].id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
treeLoading.value = false
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
treeLoading.value = false
|
||||
})
|
||||
}
|
||||
loadTreeData()
|
||||
// 懒加载子节点
|
||||
const onLoadData = (treeNode) => {
|
||||
return new Promise((resolve) => {
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
*
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
*
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
package vip.xiaonuo.common.util;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* SQL工具类,处理数据库兼容性问题
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2026/2/12 18:43
|
||||
**/
|
||||
public class CommonSqlUtil {
|
||||
|
||||
/** Oracle IN子句最大元素数量限制 */
|
||||
private static final int IN_CLAUSE_LIMIT = 999;
|
||||
|
||||
/**
|
||||
* 安全的IN查询,自动处理Oracle IN子句1000限制
|
||||
* 当集合大小超过999时,自动拆分为多个IN子句用OR连接:
|
||||
* (column IN (...999个) OR column IN (...999个) OR ...)
|
||||
*
|
||||
* @param wrapper MyBatis-Plus LambdaQueryWrapper
|
||||
* @param column 查询列
|
||||
* @param values IN子句的值集合
|
||||
* @param <T> 实体类型
|
||||
*/
|
||||
public static <T> void safeIn(LambdaQueryWrapper<T> wrapper, SFunction<T, ?> column, Collection<?> values) {
|
||||
if (ObjectUtil.isEmpty(values)) {
|
||||
return;
|
||||
}
|
||||
if (values.size() <= IN_CLAUSE_LIMIT) {
|
||||
wrapper.in(column, values);
|
||||
return;
|
||||
}
|
||||
List<?> valueList = values instanceof List ? (List<?>) values : new ArrayList<>(values);
|
||||
List<? extends List<?>> partitions = CollectionUtil.split(valueList, IN_CLAUSE_LIMIT);
|
||||
wrapper.and(w -> {
|
||||
for (int i = 0; i < partitions.size(); i++) {
|
||||
if (i == 0) {
|
||||
w.in(column, partitions.get(i));
|
||||
} else {
|
||||
w.or().in(column, partitions.get(i));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -267,5 +267,9 @@ public abstract class SaBaseLoginUser {
|
||||
/** 数据范围 */
|
||||
@Schema(description = "数据范围")
|
||||
private List<String> dataScope;
|
||||
|
||||
/** 是否全部数据范围(SCOPE_ALL),为true时dataScope为空,表示不需要过滤 */
|
||||
@Schema(description = "是否全部数据范围", hidden = true)
|
||||
private boolean scopeAll;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,17 +39,25 @@ public class StpLoginUserUtil {
|
||||
|
||||
/**
|
||||
* 获取当前B端登录用户的当前请求接口的数据范围
|
||||
* 返回null表示SCOPE_ALL(全部数据权限,无需过滤)
|
||||
* 返回空列表表示无数据权限(需限制)
|
||||
* 返回非空列表表示按机构ID过滤
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/7/8 10:41
|
||||
**/
|
||||
public static List<String> getLoginUserDataScope() {
|
||||
List<String> resultList = CollectionUtil.newArrayList();
|
||||
getLoginUser().getDataScopeList().forEach(dataScope -> {
|
||||
if(dataScope.getApiUrl().equals(CommonServletUtil.getRequest().getServletPath())) {
|
||||
String servletPath = CommonServletUtil.getRequest().getServletPath();
|
||||
for (SaBaseLoginUser.DataScope dataScope : getLoginUser().getDataScopeList()) {
|
||||
if (dataScope.getApiUrl().equals(servletPath)) {
|
||||
if (dataScope.isScopeAll()) {
|
||||
// SCOPE_ALL:返回null表示全部数据权限,无需过滤
|
||||
return null;
|
||||
}
|
||||
resultList.addAll(dataScope.getDataScope());
|
||||
}
|
||||
});
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import vip.xiaonuo.biz.modular.user.entity.BizUser;
|
||||
import vip.xiaonuo.biz.modular.user.enums.BizUserStatusEnum;
|
||||
import vip.xiaonuo.biz.modular.user.service.BizUserService;
|
||||
import vip.xiaonuo.common.enums.CommonSortOrderEnum;
|
||||
import vip.xiaonuo.common.util.CommonSqlUtil;
|
||||
import vip.xiaonuo.common.exception.CommonException;
|
||||
import vip.xiaonuo.common.listener.CommonDataChangeEventCenter;
|
||||
import vip.xiaonuo.common.page.CommonPageRequest;
|
||||
@@ -134,16 +135,22 @@ public class BizGroupServiceImpl extends ServiceImpl<BizGroupMapper, BizGroup> i
|
||||
public List<Tree<String>> orgTreeSelector() {
|
||||
// 获取所有机构
|
||||
List<BizOrg> allOrgList = bizOrgService.list();
|
||||
// 定义机构集合
|
||||
Set<BizOrg> bizOrgSet = CollectionUtil.newHashSet();
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
loginUserDataScope.forEach(orgId -> bizOrgSet.addAll(bizOrgService.getParentListById(allOrgList, orgId, true)));
|
||||
} else {
|
||||
// 确定用于构建树的机构集合
|
||||
List<BizOrg> resultOrgList;
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
return CollectionUtil.newArrayList();
|
||||
}
|
||||
List<TreeNode<String>> treeNodeList = bizOrgSet.stream().map(bizOrg ->
|
||||
if(loginUserDataScope == null) {
|
||||
// SCOPE_ALL:使用全部机构
|
||||
resultOrgList = allOrgList;
|
||||
} else {
|
||||
Set<BizOrg> bizOrgSet = CollectionUtil.newHashSet();
|
||||
loginUserDataScope.forEach(orgId -> bizOrgSet.addAll(bizOrgService.getParentListById(allOrgList, orgId, true)));
|
||||
resultOrgList = CollectionUtil.newArrayList(bizOrgSet);
|
||||
}
|
||||
List<TreeNode<String>> treeNodeList = resultOrgList.stream().map(bizOrg ->
|
||||
new TreeNode<>(bizOrg.getId(), bizOrg.getParentId(),
|
||||
bizOrg.getName(), bizOrg.getSortCode()).setExtra(JSONUtil.parseObj(bizOrg)))
|
||||
.collect(Collectors.toList());
|
||||
@@ -157,11 +164,12 @@ public class BizGroupServiceImpl extends ServiceImpl<BizGroupMapper, BizGroup> i
|
||||
queryWrapper.lambda().eq(BizUser::getUserStatus, BizUserStatusEnum.ENABLE.getValue());
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
queryWrapper.lambda().in(BizUser::getOrgId, loginUserDataScope);
|
||||
} else {
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
return new Page<>();
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
CommonSqlUtil.safeIn(queryWrapper.lambda(), BizUser::getOrgId, loginUserDataScope);
|
||||
}
|
||||
// 只查询部分字段
|
||||
queryWrapper.lambda().select(BizUser::getId, BizUser::getAvatar, BizUser::getOrgId, BizUser::getPositionId, BizUser::getAccount,
|
||||
BizUser::getName, BizUser::getSortCode, BizUser::getGender, BizUser::getEntryDate);
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import vip.xiaonuo.biz.modular.org.entity.BizOrg;
|
||||
import vip.xiaonuo.biz.modular.org.enums.BizOrgSourceFromTypeEnum;
|
||||
@@ -77,8 +78,8 @@ public class BizOrgController {
|
||||
@Operation(summary = "获取机构树")
|
||||
@SaCheckPermission("/biz/org/tree")
|
||||
@GetMapping("/biz/org/tree")
|
||||
public CommonResult<List<Tree<String>>> tree() {
|
||||
return CommonResult.data(bizOrgService.tree());
|
||||
public CommonResult<List<Tree<String>>> tree(@RequestParam(required = false) String searchKey) {
|
||||
return CommonResult.data(bizOrgService.tree(searchKey));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,6 +46,14 @@ public interface BizOrgService extends IService<BizOrg> {
|
||||
*/
|
||||
List<Tree<String>> tree();
|
||||
|
||||
/**
|
||||
* 获取机构树(带搜索关键字)
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/4/24 20:08
|
||||
*/
|
||||
List<Tree<String>> tree(String searchKey);
|
||||
|
||||
/**
|
||||
* 获取机构树(懒加载)
|
||||
*
|
||||
|
||||
@@ -44,6 +44,7 @@ import vip.xiaonuo.biz.modular.position.service.BizPositionService;
|
||||
import vip.xiaonuo.biz.modular.user.entity.BizUser;
|
||||
import vip.xiaonuo.biz.modular.user.service.BizUserService;
|
||||
import vip.xiaonuo.common.cache.CommonCacheOperator;
|
||||
import vip.xiaonuo.common.util.CommonSqlUtil;
|
||||
import vip.xiaonuo.common.enums.CommonSortOrderEnum;
|
||||
import vip.xiaonuo.common.exception.CommonException;
|
||||
import vip.xiaonuo.common.listener.CommonDataChangeEventCenter;
|
||||
@@ -109,30 +110,51 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
}
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
queryWrapper.lambda().in(BizOrg::getId, loginUserDataScope);
|
||||
} else {
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
return new Page<>();
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
CommonSqlUtil.safeIn(queryWrapper.lambda(), BizOrg::getId, loginUserDataScope);
|
||||
}
|
||||
return this.page(CommonPageRequest.defaultPage(), queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Tree<String>> tree() {
|
||||
return this.tree(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Tree<String>> tree(String searchKey) {
|
||||
// 获取所有机构
|
||||
List<BizOrg> allOrgList = this.getAllOrgList();
|
||||
// 定义机构集合
|
||||
Set<BizOrg> bizOrgSet = CollectionUtil.newHashSet();
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
loginUserDataScope.forEach(orgId -> bizOrgSet.addAll(this.getParentListById(allOrgList, orgId, true)));
|
||||
} else {
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
return CollectionUtil.newArrayList();
|
||||
}
|
||||
if(loginUserDataScope == null) {
|
||||
bizOrgSet.addAll(allOrgList);
|
||||
} else {
|
||||
loginUserDataScope.forEach(orgId -> bizOrgSet.addAll(this.getParentListById(allOrgList, orgId, true)));
|
||||
}
|
||||
|
||||
List<BizOrg> bizOrgArrayList = new ArrayList<>(bizOrgSet);
|
||||
|
||||
// 如果有搜索关键字,过滤匹配的机构及其所有父级
|
||||
if (ObjectUtil.isNotEmpty(searchKey)) {
|
||||
Set<BizOrg> filteredSet = CollectionUtil.newLinkedHashSet();
|
||||
bizOrgArrayList.stream()
|
||||
.filter(org -> StrUtil.containsIgnoreCase(org.getName(), searchKey))
|
||||
.forEach(org -> filteredSet.addAll(this.getParentListById(allOrgList, org.getId(), true)));
|
||||
// 取交集:既在数据范围内,又匹配搜索条件
|
||||
bizOrgArrayList = new ArrayList<>(filteredSet);
|
||||
bizOrgArrayList.retainAll(bizOrgSet);
|
||||
}
|
||||
|
||||
// 修复:使用稳定的排序方式,首先按排序码排序,然后按机构ID排序作为次级条件
|
||||
List<BizOrg> bizOrgArrayList = new ArrayList<>(bizOrgSet);
|
||||
bizOrgArrayList.sort(Comparator.comparingInt(BizOrg::getSortCode)
|
||||
.thenComparing(BizOrg::getId)); // 添加ID作为次级排序条件
|
||||
|
||||
@@ -150,10 +172,31 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
String parentId = ObjectUtil.isNotEmpty(bizOrgTreeLazyParam.getParentId()) ? bizOrgTreeLazyParam.getParentId() : "0";
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if (ObjectUtil.isEmpty(loginUserDataScope)) {
|
||||
if (loginUserDataScope != null && ObjectUtil.isEmpty(loginUserDataScope)) {
|
||||
return CollectionUtil.newArrayList();
|
||||
}
|
||||
|
||||
// loginUserDataScope == null 时为 SCOPE_ALL,不做数据范围过滤
|
||||
if (loginUserDataScope == null) {
|
||||
// SCOPE_ALL:直接查询当前父级下的所有子机构
|
||||
List<BizOrg> childList = this.list(new LambdaQueryWrapper<BizOrg>()
|
||||
.eq(BizOrg::getParentId, parentId)
|
||||
.orderByAsc(BizOrg::getSortCode));
|
||||
if (ObjectUtil.isEmpty(childList)) {
|
||||
return CollectionUtil.newArrayList();
|
||||
}
|
||||
List<String> childIds = childList.stream().map(BizOrg::getId).collect(Collectors.toList());
|
||||
Set<String> hasChildrenParentIds = this.list(new LambdaQueryWrapper<BizOrg>()
|
||||
.select(BizOrg::getParentId)
|
||||
.in(BizOrg::getParentId, childIds))
|
||||
.stream().map(BizOrg::getParentId).collect(Collectors.toSet());
|
||||
return childList.stream().map(bizOrg -> {
|
||||
JSONObject jsonObject = JSONUtil.parseObj(bizOrg);
|
||||
jsonObject.set("isLeaf", !hasChildrenParentIds.contains(bizOrg.getId()));
|
||||
return jsonObject;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
// 从版本化缓存获取可见机构ID集合(命中时无需加载全量数据)
|
||||
Set<String> visibleOrgIds = this.getVisibleOrgIds(loginUserDataScope);
|
||||
if (ObjectUtil.isEmpty(visibleOrgIds)) {
|
||||
@@ -161,10 +204,11 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
}
|
||||
|
||||
// SQL直查:获取当前父级下的可见子机构(替代内存过滤2万条记录)
|
||||
List<BizOrg> childList = this.list(new LambdaQueryWrapper<BizOrg>()
|
||||
.eq(BizOrg::getParentId, parentId)
|
||||
.in(BizOrg::getId, visibleOrgIds)
|
||||
.orderByAsc(BizOrg::getSortCode));
|
||||
LambdaQueryWrapper<BizOrg> childQueryWrapper = new LambdaQueryWrapper<BizOrg>()
|
||||
.eq(BizOrg::getParentId, parentId);
|
||||
CommonSqlUtil.safeIn(childQueryWrapper, BizOrg::getId, visibleOrgIds);
|
||||
childQueryWrapper.orderByAsc(BizOrg::getSortCode);
|
||||
List<BizOrg> childList = this.list(childQueryWrapper);
|
||||
|
||||
if (ObjectUtil.isEmpty(childList)) {
|
||||
return CollectionUtil.newArrayList();
|
||||
@@ -172,10 +216,11 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
|
||||
// SQL批量查询:判断哪些子机构还有可见的下级(单次SQL替代N次遍历)
|
||||
List<String> childIds = childList.stream().map(BizOrg::getId).collect(Collectors.toList());
|
||||
Set<String> hasChildrenParentIds = this.list(new LambdaQueryWrapper<BizOrg>()
|
||||
LambdaQueryWrapper<BizOrg> hasChildrenWrapper = new LambdaQueryWrapper<BizOrg>()
|
||||
.select(BizOrg::getParentId)
|
||||
.in(BizOrg::getParentId, childIds)
|
||||
.in(BizOrg::getId, visibleOrgIds))
|
||||
.in(BizOrg::getParentId, childIds);
|
||||
CommonSqlUtil.safeIn(hasChildrenWrapper, BizOrg::getId, visibleOrgIds);
|
||||
Set<String> hasChildrenParentIds = this.list(hasChildrenWrapper)
|
||||
.stream().map(BizOrg::getParentId).collect(Collectors.toSet());
|
||||
|
||||
return childList.stream().map(bizOrg -> {
|
||||
@@ -203,12 +248,13 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
BizOrgCategoryEnum.validate(bizOrgAddParam.getCategory());
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
throw new CommonException("您没有权限增加机构");
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
if(!loginUserDataScope.contains(bizOrgAddParam.getParentId())) {
|
||||
throw new CommonException("您没有权限在该机构下增加机构,机构id:{}", bizOrgAddParam.getParentId());
|
||||
}
|
||||
} else {
|
||||
throw new CommonException("您没有权限增加机构");
|
||||
}
|
||||
BizOrg bizOrg = BeanUtil.toBean(bizOrgAddParam, BizOrg.class);
|
||||
|
||||
@@ -236,6 +282,9 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
BizOrg bizOrg = this.queryEntity(bizOrgEditParam.getId());
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
throw new CommonException("您没有权限编辑该机构,机构id:{}", bizOrg.getId());
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
if(!loginUserDataScope.contains(bizOrg.getId())) {
|
||||
throw new CommonException("您没有权限编辑该机构,机构id:{}", bizOrg.getId());
|
||||
@@ -243,8 +292,6 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
if(!loginUserDataScope.contains(bizOrg.getParentId())) {
|
||||
throw new CommonException("您没有权限编辑该机构下的机构,机构id:{}", bizOrg.getParentId());
|
||||
}
|
||||
} else {
|
||||
throw new CommonException("您没有权限编辑该机构,机构id:{}", bizOrg.getId());
|
||||
}
|
||||
BeanUtil.copyProperties(bizOrgEditParam, bizOrg);
|
||||
boolean repeatName = this.count(new LambdaQueryWrapper<BizOrg>().eq(BizOrg::getParentId, bizOrg.getParentId())
|
||||
@@ -273,12 +320,13 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
if(ObjectUtil.isNotEmpty(orgIdList)) {
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
throw new CommonException("您没有权限删除这些机构,机构id:{}", orgIdList);
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
if(!new HashSet<>(loginUserDataScope).containsAll(orgIdList)) {
|
||||
throw new CommonException("您没有权限删除这些机构,机构id:{}", orgIdList);
|
||||
}
|
||||
} else {
|
||||
throw new CommonException("您没有权限删除这些机构,机构id:{}", orgIdList);
|
||||
}
|
||||
List<BizOrg> allOrgList = this.getAllOrgList();
|
||||
// 获取所有子机构
|
||||
@@ -466,14 +514,17 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
// 定义机构集合
|
||||
Set<BizOrg> bizOrgSet = CollectionUtil.newHashSet();
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
return CollectionUtil.newArrayList();
|
||||
}
|
||||
if(loginUserDataScope == null) {
|
||||
// SCOPE_ALL:不做过滤,查询全部机构
|
||||
} else {
|
||||
// 获取所有机构
|
||||
List<BizOrg> allOrgList = this.getAllOrgList();
|
||||
loginUserDataScope.forEach(orgId -> bizOrgSet.addAll(this.getParentListById(allOrgList, orgId, true)));
|
||||
List<String> loginUserDataScopeFullList = bizOrgSet.stream().map(BizOrg::getId).collect(Collectors.toList());
|
||||
lambdaQueryWrapper.in(BizOrg::getId, loginUserDataScopeFullList);
|
||||
} else {
|
||||
return CollectionUtil.newArrayList();
|
||||
CommonSqlUtil.safeIn(lambdaQueryWrapper, BizOrg::getId, loginUserDataScopeFullList);
|
||||
}
|
||||
lambdaQueryWrapper.orderByAsc(BizOrg::getSortCode);
|
||||
List<BizOrg> bizOrgList = this.list(lambdaQueryWrapper);
|
||||
@@ -488,11 +539,12 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
QueryWrapper<BizOrg> queryWrapper = new QueryWrapper<BizOrg>().checkSqlInjection();
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
queryWrapper.lambda().in(BizOrg::getId, loginUserDataScope);
|
||||
} else {
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
return new Page<>();
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
CommonSqlUtil.safeIn(queryWrapper.lambda(), BizOrg::getId, loginUserDataScope);
|
||||
}
|
||||
// 查询部分字段
|
||||
queryWrapper.lambda().select(BizOrg::getId, BizOrg::getParentId, BizOrg::getName,
|
||||
BizOrg::getCategory, BizOrg::getSortCode);
|
||||
@@ -511,11 +563,12 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
QueryWrapper<BizUser> queryWrapper = new QueryWrapper<BizUser>().checkSqlInjection();
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
queryWrapper.lambda().in(BizUser::getOrgId, loginUserDataScope);
|
||||
} else {
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
return new Page<>();
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
CommonSqlUtil.safeIn(queryWrapper.lambda(), BizUser::getOrgId, loginUserDataScope);
|
||||
}
|
||||
// 只查询部分字段
|
||||
queryWrapper.lambda().select(BizUser::getId, BizUser::getAvatar, BizUser::getOrgId, BizUser::getPositionId, BizUser::getAccount,
|
||||
BizUser::getName, BizUser::getSortCode, BizUser::getGender, BizUser::getEntryDate);
|
||||
@@ -546,6 +599,9 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
if(ObjectUtil.isNotEmpty(orgIdList)) {
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
throw new CommonException("您没有权限复制机构");
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
// 如果有数据范围限制,则校验目标父id是否有权限
|
||||
if(!loginUserDataScope.contains(targetParentId)) {
|
||||
@@ -555,8 +611,6 @@ public class BizOrgServiceImpl extends ServiceImpl<BizOrgMapper, BizOrg> impleme
|
||||
if(!new HashSet<>(loginUserDataScope).containsAll(orgIdList)) {
|
||||
throw new CommonException("您没有权限复制这些机构,机构id:{}", orgIdList);
|
||||
}
|
||||
} else {
|
||||
throw new CommonException("您没有权限复制机构");
|
||||
}
|
||||
|
||||
// 遍历复制
|
||||
|
||||
@@ -47,6 +47,7 @@ import vip.xiaonuo.common.enums.CommonSortOrderEnum;
|
||||
import vip.xiaonuo.common.exception.CommonException;
|
||||
import vip.xiaonuo.common.listener.CommonDataChangeEventCenter;
|
||||
import vip.xiaonuo.common.page.CommonPageRequest;
|
||||
import vip.xiaonuo.common.util.CommonSqlUtil;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -92,11 +93,12 @@ public class BizPositionServiceImpl extends ServiceImpl<BizPositionMapper, BizPo
|
||||
}
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
queryWrapper.lambda().in(BizPosition::getOrgId, loginUserDataScope);
|
||||
} else {
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
return new Page<>();
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
CommonSqlUtil.safeIn(queryWrapper.lambda(), BizPosition::getOrgId, loginUserDataScope);
|
||||
}
|
||||
return this.page(CommonPageRequest.defaultPage(), queryWrapper);
|
||||
}
|
||||
|
||||
@@ -106,12 +108,13 @@ public class BizPositionServiceImpl extends ServiceImpl<BizPositionMapper, BizPo
|
||||
BizPositionCategoryEnum.validate(bizPositionAddParam.getCategory());
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
throw new CommonException("您没有权限在该机构下增加岗位,机构id:{}", bizPositionAddParam.getOrgId());
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
if(!loginUserDataScope.contains(bizPositionAddParam.getOrgId())) {
|
||||
throw new CommonException("您没有权限在该机构下增加岗位,机构id:{}", bizPositionAddParam.getOrgId());
|
||||
}
|
||||
} else {
|
||||
throw new CommonException("您没有权限在该机构下增加岗位,机构id:{}", bizPositionAddParam.getOrgId());
|
||||
}
|
||||
BizPosition bizPosition = BeanUtil.toBean(bizPositionAddParam, BizPosition.class);
|
||||
boolean repeatName = this.count(new LambdaQueryWrapper<BizPosition>().eq(BizPosition::getOrgId, bizPosition.getOrgId())
|
||||
@@ -133,12 +136,12 @@ public class BizPositionServiceImpl extends ServiceImpl<BizPositionMapper, BizPo
|
||||
BizPosition bizPosition = this.queryEntity(bizPositionEditParam.getId());
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
if(!loginUserDataScope.contains(bizPositionEditParam.getOrgId())) {
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
if(!bizPositionEditParam.getId().equals(StpUtil.getLoginIdAsString())) {
|
||||
throw new CommonException("您没有权限编辑该机构下的岗位,机构id:{}", bizPositionEditParam.getOrgId());
|
||||
}
|
||||
} else {
|
||||
if(!bizPositionEditParam.getId().equals(StpUtil.getLoginIdAsString())) {
|
||||
} else if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
if(!loginUserDataScope.contains(bizPositionEditParam.getOrgId())) {
|
||||
throw new CommonException("您没有权限编辑该机构下的岗位,机构id:{}", bizPositionEditParam.getOrgId());
|
||||
}
|
||||
}
|
||||
@@ -163,12 +166,13 @@ public class BizPositionServiceImpl extends ServiceImpl<BizPositionMapper, BizPo
|
||||
Set<String> positionOrgIdList = this.listByIds(positionIdList).stream().map(BizPosition::getOrgId).collect(Collectors.toSet());
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
throw new CommonException("您没有权限删除这些机构下的岗位,机构id:{}", positionOrgIdList);
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
if(!new HashSet<>(loginUserDataScope).containsAll(positionOrgIdList)) {
|
||||
throw new CommonException("您没有权限删除这些机构下的岗位,机构id:{}", positionOrgIdList);
|
||||
}
|
||||
} else {
|
||||
throw new CommonException("您没有权限删除这些机构下的岗位,机构id:{}", positionOrgIdList);
|
||||
}
|
||||
// 岗位下有人不能删除(直属岗位)
|
||||
boolean hasOrgUser = bizUserService.count(new LambdaQueryWrapper<BizUser>().in(BizUser::getPositionId, positionIdList)) > 0;
|
||||
@@ -223,14 +227,15 @@ public class BizPositionServiceImpl extends ServiceImpl<BizPositionMapper, BizPo
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
// 定义机构集合
|
||||
Set<BizOrg> bizOrgSet = CollectionUtil.newHashSet();
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
return CollectionUtil.newArrayList();
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
// 获取所有机构
|
||||
List<BizOrg> allOrgList = bizOrgService.list();
|
||||
loginUserDataScope.forEach(orgId -> bizOrgSet.addAll(bizOrgService.getParentListById(allOrgList, orgId, true)));
|
||||
List<String> loginUserDataScopeFullList = bizOrgSet.stream().map(BizOrg::getId).collect(Collectors.toList());
|
||||
lambdaQueryWrapper.in(BizOrg::getId, loginUserDataScopeFullList);
|
||||
} else {
|
||||
return CollectionUtil.newArrayList();
|
||||
CommonSqlUtil.safeIn(lambdaQueryWrapper, BizOrg::getId, loginUserDataScopeFullList);
|
||||
}
|
||||
lambdaQueryWrapper.orderByAsc(BizOrg::getSortCode);
|
||||
List<BizOrg> bizOrgList = bizOrgService.list(lambdaQueryWrapper);
|
||||
@@ -250,11 +255,12 @@ public class BizPositionServiceImpl extends ServiceImpl<BizPositionMapper, BizPo
|
||||
QueryWrapper<BizPosition> queryWrapper = new QueryWrapper<BizPosition>();
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
queryWrapper.lambda().in(BizPosition::getOrgId, loginUserDataScope);
|
||||
} else {
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
return new Page<>();
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
CommonSqlUtil.safeIn(queryWrapper.lambda(), BizPosition::getOrgId, loginUserDataScope);
|
||||
}
|
||||
// 查询部分字段
|
||||
queryWrapper.lambda().select(BizPosition::getId, BizPosition::getOrgId, BizPosition::getName,
|
||||
BizPosition::getCategory, BizPosition::getSortCode);
|
||||
|
||||
@@ -142,10 +142,10 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
|
||||
queryWrapper.lambda().ne(BizUser::getAccount, BizBuildInEnum.BUILD_IN_USER_ACCOUNT.getValue());
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
queryWrapper.lambda().in(BizUser::getOrgId, loginUserDataScope);
|
||||
} else {
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
queryWrapper.lambda().eq(BizUser::getId, StpUtil.getLoginIdAsString());
|
||||
} else if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
CommonSqlUtil.safeIn(queryWrapper.lambda(), BizUser::getOrgId, loginUserDataScope);
|
||||
}
|
||||
return this.page(CommonPageRequest.defaultPage(), queryWrapper);
|
||||
}
|
||||
@@ -174,12 +174,13 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
|
||||
private void checkParam(BizUserAddParam bizUserAddParam) {
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
throw new CommonException("您没有权限在该机构下增加人员,机构id:{}", bizUserAddParam.getOrgId());
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
if(!loginUserDataScope.contains(bizUserAddParam.getOrgId())) {
|
||||
throw new CommonException("您没有权限在该机构下增加人员,机构id:{}", bizUserAddParam.getOrgId());
|
||||
}
|
||||
} else {
|
||||
throw new CommonException("您没有权限在该机构下增加人员,机构id:{}", bizUserAddParam.getOrgId());
|
||||
}
|
||||
if (this.count(new LambdaQueryWrapper<BizUser>()
|
||||
.eq(BizUser::getAccount, bizUserAddParam.getAccount())) > 0) {
|
||||
@@ -225,12 +226,12 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
|
||||
private void checkParam(BizUserEditParam bizUserEditParam) {
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
if(!loginUserDataScope.contains(bizUserEditParam.getOrgId())) {
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
if(!bizUserEditParam.getId().equals(StpUtil.getLoginIdAsString())) {
|
||||
throw new CommonException("您没有权限编辑该机构下的人员,机构id:{}", bizUserEditParam.getOrgId());
|
||||
}
|
||||
} else {
|
||||
if(!bizUserEditParam.getId().equals(StpUtil.getLoginIdAsString())) {
|
||||
} else if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
if(!loginUserDataScope.contains(bizUserEditParam.getOrgId())) {
|
||||
throw new CommonException("您没有权限编辑该机构下的人员,机构id:{}", bizUserEditParam.getOrgId());
|
||||
}
|
||||
}
|
||||
@@ -279,15 +280,15 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
|
||||
Set<String> userOrgIdList = this.listByIds(bizUserIdList).stream().map(BizUser::getOrgId).collect(Collectors.toSet());
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
if(bizUserIdList.size() != 1 || !bizUserIdList.get(0).equals(StpUtil.getLoginIdAsString())) {
|
||||
throw new CommonException("您没有权限删除这些机构下的人员,机构id:{}", userOrgIdList);
|
||||
}
|
||||
} else if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
if(!new HashSet<>(loginUserDataScope).containsAll(userOrgIdList)) {
|
||||
throw new CommonException("您没有权限删除这些机构下的人员,机构id:{}",
|
||||
CollectionUtil.subtract(userOrgIdList, loginUserDataScope));
|
||||
}
|
||||
} else {
|
||||
if(bizUserIdList.size() != 1 || !bizUserIdList.get(0).equals(StpUtil.getLoginIdAsString())) {
|
||||
throw new CommonException("您没有权限删除这些机构下的人员,机构id:{}", userOrgIdList);
|
||||
}
|
||||
}
|
||||
// 清除【将这些人员作为主管】的信息
|
||||
this.update(new LambdaUpdateWrapper<BizUser>().in(BizUser::getDirectorId, bizUserIdList).set(BizUser::getDirectorId, null));
|
||||
@@ -330,12 +331,12 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
|
||||
BizUser bizUser = this.detail(bizUserIdParam);
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
if(!loginUserDataScope.contains(bizUser.getOrgId())) {
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
if(!bizUser.getId().equals(StpUtil.getLoginIdAsString())) {
|
||||
throw new CommonException("您没有权限禁用该机构下的人员:{},机构id:{}", bizUser.getName(), bizUser.getOrgId());
|
||||
}
|
||||
} else {
|
||||
if(!bizUser.getId().equals(StpUtil.getLoginIdAsString())) {
|
||||
} else if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
if(!loginUserDataScope.contains(bizUser.getOrgId())) {
|
||||
throw new CommonException("您没有权限禁用该机构下的人员:{},机构id:{}", bizUser.getName(), bizUser.getOrgId());
|
||||
}
|
||||
}
|
||||
@@ -349,12 +350,12 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
|
||||
BizUser bizUser = this.detail(bizUserIdParam);
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
if(!loginUserDataScope.contains(bizUser.getOrgId())) {
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
if(!bizUser.getId().equals(StpUtil.getLoginIdAsString())) {
|
||||
throw new CommonException("您没有权限启用该机构下的人员:{},机构id:{}", bizUser.getName(), bizUser.getOrgId());
|
||||
}
|
||||
} else {
|
||||
if(!bizUser.getId().equals(StpUtil.getLoginIdAsString())) {
|
||||
} else if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
if(!loginUserDataScope.contains(bizUser.getOrgId())) {
|
||||
throw new CommonException("您没有权限启用该机构下的人员:{},机构id:{}", bizUser.getName(), bizUser.getOrgId());
|
||||
}
|
||||
}
|
||||
@@ -368,12 +369,12 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
|
||||
BizUser bizUser = this.detail(bizUserIdParam);
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
if(!loginUserDataScope.contains(bizUser.getOrgId())) {
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
if(!bizUser.getId().equals(StpUtil.getLoginIdAsString())) {
|
||||
throw new CommonException("您没有权限为该机构下的人员:{}重置密码,机构id:{}", bizUser.getName(), bizUser.getOrgId());
|
||||
}
|
||||
} else {
|
||||
if(!bizUser.getId().equals(StpUtil.getLoginIdAsString())) {
|
||||
} else if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
if(!loginUserDataScope.contains(bizUser.getOrgId())) {
|
||||
throw new CommonException("您没有权限为该机构下的人员:{}重置密码,机构id:{}", bizUser.getName(), bizUser.getOrgId());
|
||||
}
|
||||
}
|
||||
@@ -393,12 +394,12 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
|
||||
BizUser bizUser = this.queryEntity(bizUserGrantRoleParam.getId());
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
if(!loginUserDataScope.contains(bizUser.getOrgId())) {
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
if(!bizUser.getId().equals(StpUtil.getLoginIdAsString())) {
|
||||
throw new CommonException("您没有权限为该机构下的人员:{}授权角色,机构id:{}", bizUser.getName(), bizUser.getOrgId());
|
||||
}
|
||||
} else {
|
||||
if(!bizUser.getId().equals(StpUtil.getLoginIdAsString())) {
|
||||
} else if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
if(!loginUserDataScope.contains(bizUser.getOrgId())) {
|
||||
throw new CommonException("您没有权限为该机构下的人员:{}授权角色,机构id:{}", bizUser.getName(), bizUser.getOrgId());
|
||||
}
|
||||
}
|
||||
@@ -414,10 +415,10 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
|
||||
queryWrapper.lambda().ne(BizUser::getAccount, BizBuildInEnum.BUILD_IN_USER_ACCOUNT.getValue());
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
queryWrapper.lambda().in(BizUser::getOrgId, loginUserDataScope);
|
||||
} else {
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
queryWrapper.lambda().eq(BizUser::getId, StpUtil.getLoginIdAsString());
|
||||
} else if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
CommonSqlUtil.safeIn(queryWrapper.lambda(), BizUser::getOrgId, loginUserDataScope);
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(bizUserExportParam.getUserIds())) {
|
||||
queryWrapper.lambda().in(BizUser::getId, StrUtil.split(bizUserExportParam.getUserIds(), StrUtil.COMMA));
|
||||
@@ -624,14 +625,15 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
// 定义机构集合
|
||||
Set<BizOrg> bizOrgSet = CollectionUtil.newHashSet();
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
return CollectionUtil.newArrayList();
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
// 获取所有机构
|
||||
List<BizOrg> allOrgList = bizOrgService.list();
|
||||
loginUserDataScope.forEach(orgId -> bizOrgSet.addAll(bizOrgService.getParentListById(allOrgList, orgId, true)));
|
||||
List<String> loginUserDataScopeFullList = bizOrgSet.stream().map(BizOrg::getId).collect(Collectors.toList());
|
||||
lambdaQueryWrapper.in(BizOrg::getId, loginUserDataScopeFullList);
|
||||
} else {
|
||||
return CollectionUtil.newArrayList();
|
||||
CommonSqlUtil.safeIn(lambdaQueryWrapper, BizOrg::getId, loginUserDataScopeFullList);
|
||||
}
|
||||
lambdaQueryWrapper.orderByAsc(BizOrg::getSortCode);
|
||||
List<BizOrg> bizOrgList = bizOrgService.list(lambdaQueryWrapper);
|
||||
@@ -651,11 +653,12 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
|
||||
QueryWrapper<BizOrg> queryWrapper = new QueryWrapper<BizOrg>().checkSqlInjection();
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
queryWrapper.lambda().in(BizOrg::getId, loginUserDataScope);
|
||||
} else {
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
return new Page<>();
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
CommonSqlUtil.safeIn(queryWrapper.lambda(), BizOrg::getId, loginUserDataScope);
|
||||
}
|
||||
// 查询部分字段
|
||||
queryWrapper.lambda().select(BizOrg::getId, BizOrg::getParentId, BizOrg::getName,
|
||||
BizOrg::getCategory, BizOrg::getSortCode);
|
||||
@@ -674,11 +677,12 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
|
||||
QueryWrapper<BizPosition> queryWrapper = new QueryWrapper<BizPosition>().checkSqlInjection();
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
queryWrapper.lambda().in(BizPosition::getOrgId, loginUserDataScope);
|
||||
} else {
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
return new Page<>();
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
CommonSqlUtil.safeIn(queryWrapper.lambda(), BizPosition::getOrgId, loginUserDataScope);
|
||||
}
|
||||
// 查询部分字段
|
||||
queryWrapper.lambda().select(BizPosition::getId, BizPosition::getOrgId, BizPosition::getName,
|
||||
BizPosition::getCategory, BizPosition::getSortCode);
|
||||
@@ -697,6 +701,9 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
|
||||
public Page<BizUserRoleResult> roleSelector(BizUserSelectorRoleParam bizUserSelectorRoleParam) {
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
return new Page<>();
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
if(ObjectUtil.isNotEmpty(bizUserSelectorRoleParam.getOrgId())) {
|
||||
if(loginUserDataScope.contains(bizUserSelectorRoleParam.getOrgId())) {
|
||||
@@ -715,8 +722,14 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
|
||||
bizUserSelectorRoleParam.getSearchKey(), loginUserDataScope, true), Page.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
// loginUserDataScope == null 时表示SCOPE_ALL,查询全部角色,不限制数据范围
|
||||
if(ObjectUtil.isNotEmpty(bizUserSelectorRoleParam.getOrgId())) {
|
||||
return BeanUtil.toBean(sysRoleApi.roleSelector(bizUserSelectorRoleParam.getOrgId(), bizUserSelectorRoleParam.getCategory(),
|
||||
bizUserSelectorRoleParam.getSearchKey(), null, true), Page.class);
|
||||
} else {
|
||||
return new Page<>();
|
||||
return BeanUtil.toBean(sysRoleApi.roleSelector(null, bizUserSelectorRoleParam.getCategory(),
|
||||
bizUserSelectorRoleParam.getSearchKey(), null, true), Page.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -725,11 +738,12 @@ public class BizUserServiceImpl extends ServiceImpl<BizUserMapper, BizUser> impl
|
||||
QueryWrapper<BizUser> queryWrapper = new QueryWrapper<BizUser>().checkSqlInjection();
|
||||
// 校验数据范围
|
||||
List<String> loginUserDataScope = StpLoginUserUtil.getLoginUserDataScope();
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
queryWrapper.lambda().in(BizUser::getOrgId, loginUserDataScope);
|
||||
} else {
|
||||
if(loginUserDataScope != null && loginUserDataScope.isEmpty()) {
|
||||
return new Page<>();
|
||||
}
|
||||
if(ObjectUtil.isNotEmpty(loginUserDataScope)) {
|
||||
CommonSqlUtil.safeIn(queryWrapper.lambda(), BizUser::getOrgId, loginUserDataScope);
|
||||
}
|
||||
// 只查询部分字段
|
||||
queryWrapper.lambda().select(BizUser::getId, BizUser::getAvatar, BizUser::getOrgId, BizUser::getPositionId, BizUser::getAccount,
|
||||
BizUser::getName, BizUser::getSortCode, BizUser::getGender, BizUser::getEntryDate);
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import vip.xiaonuo.common.annotation.CommonLog;
|
||||
import vip.xiaonuo.common.pojo.CommonResult;
|
||||
@@ -74,8 +75,8 @@ public class SysOrgController {
|
||||
@ApiOperationSupport(order = 2)
|
||||
@Operation(summary = "获取组织树")
|
||||
@GetMapping("/sys/org/tree")
|
||||
public CommonResult<List<Tree<String>>> tree() {
|
||||
return CommonResult.data(sysOrgService.tree());
|
||||
public CommonResult<List<Tree<String>>> tree(@RequestParam(required = false) String searchKey) {
|
||||
return CommonResult.data(sysOrgService.tree(searchKey));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,6 +46,14 @@ public interface SysOrgService extends IService<SysOrg> {
|
||||
*/
|
||||
List<Tree<String>> tree();
|
||||
|
||||
/**
|
||||
* 获取组织树(带搜索关键字)
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2022/4/24 20:08
|
||||
*/
|
||||
List<Tree<String>> tree(String searchKey);
|
||||
|
||||
/**
|
||||
* 获取机构树(懒加载)
|
||||
*
|
||||
@@ -110,6 +118,14 @@ public interface SysOrgService extends IService<SysOrg> {
|
||||
**/
|
||||
List<SysOrg> getAllOrgList();
|
||||
|
||||
/**
|
||||
* 获取所有组织ID列表(从缓存获取,避免每次stream转换)
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2026/2/12
|
||||
**/
|
||||
List<String> getAllOrgIdList();
|
||||
|
||||
/**
|
||||
* 根据组织全名称获取组织id,有则返回,无则创建
|
||||
*
|
||||
|
||||
@@ -52,10 +52,7 @@ import vip.xiaonuo.sys.modular.user.entity.SysUser;
|
||||
import vip.xiaonuo.sys.modular.user.enums.SysUserStatusEnum;
|
||||
import vip.xiaonuo.sys.modular.user.service.SysUserService;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -69,6 +66,15 @@ public class SysOrgServiceImpl extends ServiceImpl<SysOrgMapper, SysOrg> impleme
|
||||
|
||||
private static final String ORG_ALL_LIST_CACHE_KEY = "sys-org:all-list";
|
||||
|
||||
/** 本地内存缓存,避免每次从Redis取出后JSON反序列化2万+条记录 */
|
||||
private volatile List<SysOrg> localOrgListCache;
|
||||
|
||||
/** 本地内存缓存:parentId -> 直接子机构列表,用于快速查找子机构 */
|
||||
private volatile Map<String, List<SysOrg>> localParentChildrenMap;
|
||||
|
||||
/** 本地内存缓存:所有机构ID列表,避免每次stream().map().toList() */
|
||||
private volatile List<String> localAllOrgIdList;
|
||||
|
||||
@Resource
|
||||
private CommonCacheOperator commonCacheOperator;
|
||||
|
||||
@@ -108,7 +114,23 @@ public class SysOrgServiceImpl extends ServiceImpl<SysOrgMapper, SysOrg> impleme
|
||||
|
||||
@Override
|
||||
public List<Tree<String>> tree() {
|
||||
List<SysOrg> sysOrgList = this.getAllOrgList();
|
||||
return this.tree(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Tree<String>> tree(String searchKey) {
|
||||
List<SysOrg> allOrgList = this.getAllOrgList();
|
||||
List<SysOrg> sysOrgList;
|
||||
// 如果有搜索关键字,过滤匹配的组织及其所有父级
|
||||
if (ObjectUtil.isNotEmpty(searchKey)) {
|
||||
Set<SysOrg> filteredSet = CollectionUtil.newLinkedHashSet();
|
||||
allOrgList.stream()
|
||||
.filter(org -> StrUtil.containsIgnoreCase(org.getName(), searchKey))
|
||||
.forEach(org -> filteredSet.addAll(this.getParentListById(allOrgList, org.getId(), true)));
|
||||
sysOrgList = new ArrayList<>(filteredSet);
|
||||
} else {
|
||||
sysOrgList = allOrgList;
|
||||
}
|
||||
// 使用稳定的排序方式,首先按排序码排序,然后按机构ID排序作为次级条件
|
||||
sysOrgList.sort(Comparator.comparingInt(SysOrg::getSortCode)
|
||||
.thenComparing(SysOrg::getId)); // 添加ID作为次级排序条件
|
||||
@@ -190,7 +212,7 @@ public class SysOrgServiceImpl extends ServiceImpl<SysOrgMapper, SysOrg> impleme
|
||||
// 发布增加事件
|
||||
CommonDataChangeEventCenter.doAddWithData(SysDataTypeEnum.ORG.getValue(), JSONUtil.createArray().put(sysOrg));
|
||||
// 清除缓存
|
||||
commonCacheOperator.remove(ORG_ALL_LIST_CACHE_KEY);
|
||||
clearOrgCache();
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@@ -215,7 +237,7 @@ public class SysOrgServiceImpl extends ServiceImpl<SysOrgMapper, SysOrg> impleme
|
||||
// 发布更新事件
|
||||
CommonDataChangeEventCenter.doUpdateWithData(SysDataTypeEnum.ORG.getValue(), JSONUtil.createArray().put(sysOrg));
|
||||
// 清除缓存
|
||||
commonCacheOperator.remove(ORG_ALL_LIST_CACHE_KEY);
|
||||
clearOrgCache();
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@@ -262,7 +284,7 @@ public class SysOrgServiceImpl extends ServiceImpl<SysOrgMapper, SysOrg> impleme
|
||||
// 发布删除事件
|
||||
CommonDataChangeEventCenter.doDeleteWithDataIdList(SysDataTypeEnum.ORG.getValue(), toDeleteOrgIdList);
|
||||
// 清除缓存
|
||||
commonCacheOperator.remove(ORG_ALL_LIST_CACHE_KEY);
|
||||
clearOrgCache();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,7 +334,7 @@ public class SysOrgServiceImpl extends ServiceImpl<SysOrgMapper, SysOrg> impleme
|
||||
}
|
||||
});
|
||||
// 清除缓存
|
||||
commonCacheOperator.remove(ORG_ALL_LIST_CACHE_KEY);
|
||||
clearOrgCache();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,15 +349,65 @@ public class SysOrgServiceImpl extends ServiceImpl<SysOrgMapper, SysOrg> impleme
|
||||
|
||||
@Override
|
||||
public List<SysOrg> getAllOrgList() {
|
||||
// 优先从本地内存缓存获取,避免每次JSON反序列化
|
||||
List<SysOrg> localCache = localOrgListCache;
|
||||
if (localCache != null) {
|
||||
return localCache;
|
||||
}
|
||||
// 其次从Redis缓存获取
|
||||
Object cached = commonCacheOperator.get(ORG_ALL_LIST_CACHE_KEY);
|
||||
if (cached != null) {
|
||||
return JSONUtil.toList(JSONUtil.parseArray(cached), SysOrg.class);
|
||||
List<SysOrg> list = JSONUtil.toList(JSONUtil.parseArray(cached), SysOrg.class);
|
||||
localOrgListCache = list;
|
||||
buildParentChildrenMap(list);
|
||||
return list;
|
||||
}
|
||||
// 最后从数据库查询
|
||||
List<SysOrg> list = this.list(new LambdaQueryWrapper<SysOrg>().orderByAsc(SysOrg::getSortCode));
|
||||
commonCacheOperator.put(ORG_ALL_LIST_CACHE_KEY, list);
|
||||
localOrgListCache = list;
|
||||
buildParentChildrenMap(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建parentId -> 子机构列表的索引Map,同时构建所有机构ID列表
|
||||
*/
|
||||
private void buildParentChildrenMap(List<SysOrg> orgList) {
|
||||
Map<String, List<SysOrg>> map = new HashMap<>();
|
||||
List<String> allIdList = new ArrayList<>(orgList.size());
|
||||
for (SysOrg org : orgList) {
|
||||
map.computeIfAbsent(org.getParentId(), k -> new ArrayList<>()).add(org);
|
||||
allIdList.add(org.getId());
|
||||
}
|
||||
localParentChildrenMap = map;
|
||||
localAllOrgIdList = allIdList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有机构ID列表(从缓存获取,避免每次stream转换)
|
||||
*/
|
||||
@Override
|
||||
public List<String> getAllOrgIdList() {
|
||||
List<String> cached = localAllOrgIdList;
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
// 触发缓存构建
|
||||
getAllOrgList();
|
||||
return localAllOrgIdList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除本地内存缓存和Redis缓存
|
||||
*/
|
||||
private void clearOrgCache() {
|
||||
localOrgListCache = null;
|
||||
localParentChildrenMap = null;
|
||||
localAllOrgIdList = null;
|
||||
commonCacheOperator.remove(ORG_ALL_LIST_CACHE_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOrgIdByOrgFullNameWithCreate(String orgFullName) {
|
||||
List<SysOrg> allOrgList = this.getAllOrgList();
|
||||
@@ -386,7 +458,7 @@ public class SysOrgServiceImpl extends ServiceImpl<SysOrgMapper, SysOrg> impleme
|
||||
// 发布增加事件
|
||||
CommonDataChangeEventCenter.doAddWithData(SysDataTypeEnum.ORG.getValue(), JSONUtil.createArray().put(sysOrg));
|
||||
// 清除缓存
|
||||
commonCacheOperator.remove(ORG_ALL_LIST_CACHE_KEY);
|
||||
clearOrgCache();
|
||||
return sysOrg.getId();
|
||||
}
|
||||
|
||||
@@ -479,7 +551,25 @@ public class SysOrgServiceImpl extends ServiceImpl<SysOrgMapper, SysOrg> impleme
|
||||
@Override
|
||||
public List<SysOrg> getChildListById(List<SysOrg> originDataList, String id, boolean includeSelf) {
|
||||
List<SysOrg> resultList = CollectionUtil.newArrayList();
|
||||
execRecursionFindChild(originDataList, id, resultList);
|
||||
// 优先使用预构建的parentId索引Map,O(n)复杂度;否则降级为原始递归
|
||||
Map<String, List<SysOrg>> parentMap = localParentChildrenMap;
|
||||
if (parentMap != null) {
|
||||
// BFS方式查找所有子机构
|
||||
Deque<String> queue = new ArrayDeque<>();
|
||||
queue.add(id);
|
||||
while (!queue.isEmpty()) {
|
||||
String parentId = queue.poll();
|
||||
List<SysOrg> children = parentMap.get(parentId);
|
||||
if (children != null) {
|
||||
for (SysOrg child : children) {
|
||||
resultList.add(child);
|
||||
queue.add(child.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
execRecursionFindChild(originDataList, id, resultList);
|
||||
}
|
||||
if(includeSelf) {
|
||||
SysOrg self = this.getById(originDataList, id);
|
||||
if(ObjectUtil.isNotEmpty(self)) {
|
||||
|
||||
@@ -1527,30 +1527,34 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
|
||||
public List<JSONObject> getScopeListByMap(Map<String, List<SysRelation>> groupMap, String orgId) {
|
||||
List<JSONObject> resultList = CollectionUtil.newArrayList();
|
||||
List<SysOrg> sysOrgList = sysOrgService.getAllOrgList();
|
||||
List<String> scopeAllList = sysOrgList.stream().map(SysOrg::getId).toList();
|
||||
List<String> scopeOrgList = CollectionUtil.newArrayList(orgId);
|
||||
List<String> scopeOrgChildList = sysOrgService.getChildListById(sysOrgList, orgId, true)
|
||||
.stream().map(SysOrg::getId).toList();
|
||||
// 懒加载子机构列表:局部缓存,避免重复计算,线程安全
|
||||
final List<String>[] orgChildListHolder = new List[]{null};
|
||||
groupMap.forEach((key, value) -> {
|
||||
JSONObject jsonObject = JSONUtil.createObj().set("apiUrl", key);
|
||||
boolean hasScopeAll = false;
|
||||
Set<String> scopeSet = CollectionUtil.newHashSet();
|
||||
value.forEach(sysRelation -> {
|
||||
for (SysRelation sysRelation : value) {
|
||||
JSONObject extJsonObject = JSONUtil.parseObj(sysRelation.getExtJson());
|
||||
String scopeCategory = extJsonObject.getStr("scopeCategory");
|
||||
if (!scopeCategory.equals(SysRoleDataScopeCategoryEnum.SCOPE_SELF.getValue())) {
|
||||
if (scopeCategory.equals(SysRoleDataScopeCategoryEnum.SCOPE_ALL.getValue())) {
|
||||
scopeSet.addAll(scopeAllList);
|
||||
} else if (scopeCategory.equals(SysRoleDataScopeCategoryEnum.SCOPE_ORG.getValue())) {
|
||||
scopeSet.addAll(scopeOrgList);
|
||||
} else if (scopeCategory.equals(SysRoleDataScopeCategoryEnum.SCOPE_ORG_CHILD.getValue())) {
|
||||
scopeSet.addAll(scopeOrgChildList);
|
||||
} else {
|
||||
scopeSet.addAll(extJsonObject.getBeanList("scopeDefineOrgIdList", String.class));
|
||||
if (scopeCategory.equals(SysRoleDataScopeCategoryEnum.SCOPE_ALL.getValue())) {
|
||||
hasScopeAll = true;
|
||||
break;
|
||||
} else if (scopeCategory.equals(SysRoleDataScopeCategoryEnum.SCOPE_ORG.getValue())) {
|
||||
scopeSet.addAll(scopeOrgList);
|
||||
} else if (scopeCategory.equals(SysRoleDataScopeCategoryEnum.SCOPE_ORG_CHILD.getValue())) {
|
||||
if (orgChildListHolder[0] == null) {
|
||||
orgChildListHolder[0] = sysOrgService.getChildListById(sysOrgService.getAllOrgList(), orgId, true)
|
||||
.stream().map(SysOrg::getId).toList();
|
||||
}
|
||||
scopeSet.addAll(orgChildListHolder[0]);
|
||||
} else if (!scopeCategory.equals(SysRoleDataScopeCategoryEnum.SCOPE_SELF.getValue())) {
|
||||
scopeSet.addAll(extJsonObject.getBeanList("scopeDefineOrgIdList", String.class));
|
||||
}
|
||||
});
|
||||
resultList.add(jsonObject.set("dataScope", CollectionUtil.newArrayList(scopeSet)));
|
||||
}
|
||||
jsonObject.set("scopeAll", hasScopeAll);
|
||||
jsonObject.set("dataScope", hasScopeAll ? CollectionUtil.newArrayList() : CollectionUtil.newArrayList(scopeSet));
|
||||
resultList.add(jsonObject);
|
||||
});
|
||||
return resultList;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user