租户套餐管理优化体验

dependabot/npm_and_yarn/jeecgboot-vue3/vite-6.0.9
JEECG 2025-02-07 16:38:02 +08:00
parent 7257dfe5ba
commit d6c6a1c01f
11 changed files with 149 additions and 59 deletions

View File

@ -23,11 +23,7 @@ import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig; import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
import org.jeecg.modules.base.service.BaseCommonService; import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.modules.system.entity.*; import org.jeecg.modules.system.entity.*;
import org.jeecg.modules.system.service.ISysTenantPackService; import org.jeecg.modules.system.service.*;
import org.jeecg.modules.system.service.ISysTenantService;
import org.jeecg.modules.system.service.ISysUserService;
import org.jeecg.modules.system.service.ISysUserTenantService;
import org.jeecg.modules.system.service.ISysDepartService;
import org.jeecg.modules.system.vo.SysUserTenantVo; import org.jeecg.modules.system.vo.SysUserTenantVo;
import org.jeecg.modules.system.vo.tenant.TenantDepartAuthInfo; import org.jeecg.modules.system.vo.tenant.TenantDepartAuthInfo;
import org.jeecg.modules.system.vo.tenant.TenantPackModel; import org.jeecg.modules.system.vo.tenant.TenantPackModel;
@ -148,6 +144,21 @@ public class SysTenantController {
return result; return result;
} }
/**
* [QQYUN-11032]jeecg
* @param tenantId
* @return
* @author chenrui
* @date 2025/2/6 18:24
*/
@RequiresPermissions("system:tenant:syncDefaultPack")
@PostMapping(value = "/syncDefaultPack")
public Result<?> syncDefaultPack(@RequestParam(name="tenantId",required=true) Integer tenantId) {
//同步默认产品包
sysTenantPackService.syncDefaultPack(tenantId);
return Result.OK("操作成功");
}
/** /**
* *
* @param * @param

View File

@ -1,7 +1,7 @@
package org.jeecg.modules.system.service; package org.jeecg.modules.system.service;
import org.jeecg.modules.system.entity.SysTenantPack;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.system.entity.SysTenantPack;
import org.jeecg.modules.system.entity.SysTenantPackUser; import org.jeecg.modules.system.entity.SysTenantPackUser;
import java.util.List; import java.util.List;
@ -78,4 +78,13 @@ public interface ISysTenantPackService extends IService<SysTenantPack> {
* @param id * @param id
*/ */
void addTenantDefaultPack(Integer id); void addTenantDefaultPack(Integer id);
/**
*
* for [QQYUN-11032]jeecg
* @param tenantId
* @author chenrui
* @date 2025/2/5 19:08
*/
void syncDefaultPack(Integer tenantId);
} }

View File

@ -1,6 +1,7 @@
package org.jeecg.modules.system.service.impl; package org.jeecg.modules.system.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.SecurityUtils;
import org.jeecg.common.constant.SymbolConstant; import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.constant.TenantConstant; import org.jeecg.common.constant.TenantConstant;
@ -9,7 +10,6 @@ import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils; import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.aop.TenantLog; import org.jeecg.modules.aop.TenantLog;
import org.jeecg.modules.system.entity.SysPackPermission; import org.jeecg.modules.system.entity.SysPackPermission;
import org.jeecg.modules.system.entity.SysTenant;
import org.jeecg.modules.system.entity.SysTenantPack; import org.jeecg.modules.system.entity.SysTenantPack;
import org.jeecg.modules.system.entity.SysTenantPackUser; import org.jeecg.modules.system.entity.SysTenantPackUser;
import org.jeecg.modules.system.mapper.SysPackPermissionMapper; import org.jeecg.modules.system.mapper.SysPackPermissionMapper;
@ -20,13 +20,12 @@ import org.jeecg.modules.system.service.ISysTenantPackService;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import javax.sql.DataSource;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -237,19 +236,54 @@ public class SysTenantPackServiceImpl extends ServiceImpl<SysTenantPackMapper, S
query.eq(SysTenantPack::getPackType,"default"); query.eq(SysTenantPack::getPackType,"default");
List<SysTenantPack> sysTenantPacks = sysTenantPackMapper.selectList(query); List<SysTenantPack> sysTenantPacks = sysTenantPackMapper.selectList(query);
for (SysTenantPack sysTenantPack: sysTenantPacks) { for (SysTenantPack sysTenantPack: sysTenantPacks) {
SysTenantPack pack = new SysTenantPack(); syncDefaultPack2CurrentTenant(tenantId, sysTenantPack);
BeanUtils.copyProperties(sysTenantPack,pack); }
pack.setTenantId(tenantId); }
pack.setPackType("custom");
pack.setId(""); @Override
sysTenantPackMapper.insert(pack); public void syncDefaultPack(Integer tenantId) {
List<String> permissionsByPackId = sysPackPermissionMapper.getPermissionsByPackId(sysTenantPack.getId()); // 查询默认套餐包
for (String permission:permissionsByPackId) { LambdaQueryWrapper<SysTenantPack> query = new LambdaQueryWrapper<>();
SysPackPermission packPermission = new SysPackPermission(); query.eq(SysTenantPack::getPackType,"default");
packPermission.setPackId(pack.getId()); List<SysTenantPack> sysDefaultTenantPacks = sysTenantPackMapper.selectList(query);
packPermission.setPermissionId(permission); // 查询当前租户套餐包
sysPackPermissionMapper.insert(packPermission); query = new LambdaQueryWrapper<>();
} query.eq(SysTenantPack::getPackType,"custom");
query.eq(SysTenantPack::getTenantId, tenantId);
List<SysTenantPack> currentTenantPacks = sysTenantPackMapper.selectList(query);
Map<String, SysTenantPack> currentTenantPackMap = new HashMap<String, SysTenantPack>();
if (oConvertUtils.listIsNotEmpty(currentTenantPacks)) {
currentTenantPackMap = currentTenantPacks.stream().collect(Collectors.toMap(SysTenantPack::getPackName, o -> o, (existing, replacement) -> existing));
}
// 添加不存在的套餐包
for (SysTenantPack defaultPacks : sysDefaultTenantPacks) {
if(!currentTenantPackMap.containsKey(defaultPacks.getPackName())){
syncDefaultPack2CurrentTenant(tenantId, defaultPacks);
}
}
}
/**
*
* for [QQYUN-11032]jeecg
* @param tenantId
* @param defaultPacks
* @author chenrui
* @date 2025/2/5 19:41
*/
private void syncDefaultPack2CurrentTenant(Integer tenantId, SysTenantPack defaultPacks) {
SysTenantPack pack = new SysTenantPack();
BeanUtils.copyProperties(defaultPacks,pack);
pack.setTenantId(tenantId);
pack.setPackType("custom");
pack.setId("");
sysTenantPackMapper.insert(pack);
List<String> permissionsByPackId = sysPackPermissionMapper.getPermissionsByPackId(defaultPacks.getId());
for (String permission:permissionsByPackId) {
SysPackPermission packPermission = new SysPackPermission();
packPermission.setPackId(pack.getId());
packPermission.setPermissionId(permission);
sysPackPermissionMapper.insert(packPermission);
} }
} }

View File

@ -0,0 +1,5 @@
-- -author:chenrui---date:2025/1/16-----for:[QQYUN-10935]jeecg---
UPDATE `sys_permission` SET `parent_id` = 'd7d6e2e4e2934f2c9385a623fd98c6f3', `name` = '' WHERE `id` = '1668174661456171010';
-- -- author:chenrui---date:20250206--for: [QQYUN-11032]jeecg ---
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `rule_flag`, `status`, `internal_or_external`) VALUES ('1887447660072292354', '1280350452934307841', '', NULL, NULL, 0, NULL, NULL, 2, 'system:tenant:syncDefaultPack', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'jeecg', '2025-02-06 18:26:04', 'jeecg', '2025-02-06 18:26:53', 0, 0, '1', 0);

View File

@ -26,12 +26,12 @@
>邀请用户加入</a-button >邀请用户加入</a-button
> >
<a-button <a-button
preIcon="ant-design:plus-outlined" preIcon="ant-design:sliders-outlined"
type="primary" type="primary"
@click="handlePack" @click="handlePack"
style="margin-right: 5px" style="margin-right: 5px"
:disabled="selectedRowKeys.length === 0" :disabled="selectedRowKeys.length === 0"
>套餐</a-button >套餐管理</a-button
> >
<a-button type="primary" @click="recycleBinClick" preIcon="ant-design:hdd-outlined">回收站</a-button> <a-button type="primary" @click="recycleBinClick" preIcon="ant-design:hdd-outlined">回收站</a-button>
</template> </template>

View File

@ -16,7 +16,7 @@
<TableAction :actions="getActions(record)" /> <TableAction :actions="getActions(record)" />
</template> </template>
</BasicTable> </BasicTable>
<!-- 产品--> <!-- 套餐-->
<TenantPackMenuModal @register="registerPackMenuModal" @success="handleSuccess"/> <TenantPackMenuModal @register="registerPackMenuModal" @success="handleSuccess"/>
</div> </div>
</template> </template>
@ -66,7 +66,7 @@
{ {
label: '删除', label: '删除',
popConfirm: { popConfirm: {
title: '是否确认删除租户产品包', title: '是否确认删除租户套餐包',
confirm: handleDelete.bind(null, record.id), confirm: handleDelete.bind(null, record.id),
}, },
}, },
@ -74,7 +74,7 @@
} }
/** /**
* 编辑产品 * 编辑套餐
*/ */
function handleAdd() { function handleAdd() {
packModal(true, { packModal(true, {
@ -86,7 +86,7 @@
/** /**
* 删除默认产品 * 删除默认套餐
*/ */
async function handleDelete(id) { async function handleDelete(id) {
await deleteTenantPack({ ids: id }, handleSuccess); await deleteTenantPack({ ids: id }, handleSuccess);
@ -104,7 +104,7 @@
} }
/** /**
* 新增产品 * 新增套餐
*/ */
async function handlePack() { async function handlePack() {
if (unref(selectedRowKeys).length > 1) { if (unref(selectedRowKeys).length > 1) {
@ -124,12 +124,12 @@
} }
/** /**
* 批量删除产品 * 批量删除套餐
*/ */
async function handlePackBatch() { async function handlePackBatch() {
Modal.confirm({ Modal.confirm({
title: '删除租户产品包', title: '删除租户套餐包',
content: '是否删除租户产品包', content: '是否删除租户套餐包',
okText: '确认', okText: '确认',
cancelText: '取消', cancelText: '取消',
onOk: async () => { onOk: async () => {

View File

@ -11,6 +11,13 @@
style="margin-right: 5px" style="margin-right: 5px"
>批量删除 >批量删除
</a-button> </a-button>
<a-button
preIcon="ant-design:sync-outlined"
type="primary"
@click="handleSyncDefaultPack"
style="margin-right: 5px"
>初始化默认套餐
</a-button>
</template> </template>
<template #action="{ record }"> <template #action="{ record }">
<TableAction :actions="getActions(record)" :dropDownActions="getDropDownAction(record)" /> <TableAction :actions="getActions(record)" :dropDownActions="getDropDownAction(record)" />
@ -24,7 +31,7 @@
import { reactive, ref, unref } from 'vue'; import { reactive, ref, unref } from 'vue';
import { BasicModal, useModal, useModalInner } from '/@/components/Modal'; import { BasicModal, useModal, useModalInner } from '/@/components/Modal';
import { packColumns, userColumns, packFormSchema } from '../tenant.data'; import { packColumns, userColumns, packFormSchema } from '../tenant.data';
import { getTenantUserList, leaveTenant, packList, deleteTenantPack } from '../tenant.api'; import { getTenantUserList, leaveTenant, packList, deleteTenantPack, syncDefaultTenantPack } from '../tenant.api';
import { useListPage } from '/@/hooks/system/useListPage'; import { useListPage } from '/@/hooks/system/useListPage';
import { BasicTable, TableAction } from '/@/components/Table'; import { BasicTable, TableAction } from '/@/components/Table';
import TenantPackMenuModal from './TenantPackMenuModal.vue'; import TenantPackMenuModal from './TenantPackMenuModal.vue';
@ -64,7 +71,7 @@
const [registerTable, { reload }, { rowSelection, selectedRowKeys, selectedRows }] = tableContext; const [registerTable, { reload }, { rowSelection, selectedRowKeys, selectedRows }] = tableContext;
// Emits // Emits
const emit = defineEmits(['register', 'success']); const emit = defineEmits(['register', 'success']);
// //
const showPackAddAndEdit = ref<boolean>(false); const showPackAddAndEdit = ref<boolean>(false);
// //
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => { const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
@ -73,7 +80,7 @@
success(); success();
}); });
// //
const title = '租户产品包列表'; const title = '租户个性化套餐包';
// //
async function handleSubmit(v) { async function handleSubmit(v) {
@ -115,40 +122,40 @@
}); });
} }
//,() //,()
const packCode = reactive<any>(['superAdmin','accountAdmin','appAdmin']); const packCode = reactive<any>(['superAdmin','accountAdmin','appAdmin']);
const { createMessage } = useMessage(); const { createMessage } = useMessage();
/** /**
* 删除产品 * 删除套餐
* @param 删除 * @param 删除
*/ */
async function handleDelete(record) { async function handleDelete(record) {
//update-begin---author:wangshuai ---date:20230222 for------------ //update-begin---author:wangshuai ---date:20230222 for------------
if(packCode.indexOf(record.packCode) != -1){ if(packCode.indexOf(record.packCode) != -1){
createMessage.warning("默认系统产品包不允许删除"); createMessage.warning("默认系统套餐包不允许删除");
return; return;
} }
//update-end---author:wangshuai ---date:20230222 for------------ //update-end---author:wangshuai ---date:20230222 for------------
await deleteTenantPack({ ids: record.id }, success); await deleteTenantPack({ ids: record.id }, success);
} }
/** /**
* 批量删除产品 * 批量删除套餐
*/ */
async function handlePackBatch() { async function handlePackBatch() {
let value = selectedRows.value; let value = selectedRows.value;
if(value && value.length>0){ if(value && value.length>0){
for (let i = 0; i < value.length; i++) { for (let i = 0; i < value.length; i++) {
if(packCode.indexOf(value[i].packCode) != -1){ if(packCode.indexOf(value[i].packCode) != -1){
createMessage.warning("默认系统产品包不允许删除"); createMessage.warning("默认系统套餐包不允许删除");
return; return;
} }
} }
} }
Modal.confirm({ Modal.confirm({
title: '删除租户产品包', title: '删除租户套餐包',
content: '是否删除租户产品包', content: '是否删除租户套餐包',
okText: '确认', okText: '确认',
cancelText: '取消', cancelText: '取消',
onOk: async () => { onOk: async () => {
@ -157,6 +164,18 @@
}) })
} }
async function handleSyncDefaultPack() {
Modal.confirm({
title: '初始化默认套餐包',
content: '是否初始化默认套餐包',
okText: '确认',
cancelText: '取消',
onOk: async () => {
await syncDefaultTenantPack({tenantId: unref(tenantId)}, success);
},
});
}
/** /**
* *
* 新增表单 * 新增表单
@ -171,7 +190,7 @@
} }
/** /**
* 产品包下面的用户 * 套餐包下面的用户
* @param record * @param record
*/ */
function seeTenantPackUser(record) { function seeTenantPackUser(record) {
@ -193,7 +212,7 @@
{ {
label: '删除', label: '删除',
popConfirm: { popConfirm: {
title: '是否确认删除租户产品包', title: '是否确认删除租户套餐包',
confirm: handleDelete.bind(null, record), confirm: handleDelete.bind(null, record),
}, },
}, },

View File

@ -20,7 +20,7 @@
}); });
// //
const tenantId = ref<number>(); const tenantId = ref<number>();
// //
const packType = ref<number>(); const packType = ref<number>();
// //
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => { const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
@ -36,14 +36,14 @@
console.log(data.record) console.log(data.record)
await setFieldsValue({ ...data.record }); await setFieldsValue({ ...data.record });
} }
//update-begin---author:wangshuai ---date:20230705 forQQYUN-56852 ------------ //update-begin---author:wangshuai ---date:20230705 forQQYUN-56852 ------------
setModalProps({ confirmLoading: false, showCancelBtn:!!data?.showFooter, showOkBtn:!!data?.showFooter }); setModalProps({ confirmLoading: false, showCancelBtn:!!data?.showFooter, showOkBtn:!!data?.showFooter });
// //
setProps({ disabled: !data?.showFooter }) setProps({ disabled: !data?.showFooter })
//update-end---author:wangshuai ---date:20230705 forQQYUN-56852 ------------ //update-end---author:wangshuai ---date:20230705 forQQYUN-56852 ------------
}); });
// //
const title = computed(() => (unref(isUpdate) ? '编辑租户产品包' : '新增租户产品包')); const title = computed(() => (unref(isUpdate) ? '编辑租户套餐包' : '新增租户套餐包'));
// //
async function handleSubmit(v) { async function handleSubmit(v) {
const values = await validate(); const values = await validate();

View File

@ -43,7 +43,7 @@
return tenantPackData.tenantId; return tenantPackData.tenantId;
}) })
// //
const tenantPackData = reactive<any>({}); const tenantPackData = reactive<any>({});
// //
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => { const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {

View File

@ -20,6 +20,7 @@ enum Api {
recycleBinPageList = '/sys/tenant/recycleBinPageList', recycleBinPageList = '/sys/tenant/recycleBinPageList',
deleteLogicDeleted = '/sys/tenant/deleteLogicDeleted', deleteLogicDeleted = '/sys/tenant/deleteLogicDeleted',
revertTenantLogic = '/sys/tenant/revertTenantLogic', revertTenantLogic = '/sys/tenant/revertTenantLogic',
syncDefaultPack = '/sys/tenant/syncDefaultPack',
//api //api
queryTenantPackUserList = '/sys/tenant/queryTenantPackUserList', queryTenantPackUserList = '/sys/tenant/queryTenantPackUserList',
deleteTenantPackUser = '/sys/tenant/deleteTenantPackUser', deleteTenantPackUser = '/sys/tenant/deleteTenantPackUser',
@ -156,6 +157,17 @@ export const deleteTenantPack = (params, handleSuccess) => {
}); });
}; };
/**
* 初始化套餐包
* @param params
* @param handleSuccess
*/
export const syncDefaultTenantPack = (params, handleSuccess) => {
return defHttp.post({ url: Api.syncDefaultPack, data: params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
};
/** /**
* 获取租户回收站的列表 * 获取租户回收站的列表
* @param params * @param params

View File

@ -251,10 +251,10 @@ export const userSearchFormSchema: FormSchema[] = [
}, },
]; ];
// //
export const packColumns: BasicColumn[] = [ export const packColumns: BasicColumn[] = [
{ {
title: '产品包名称', title: '套餐包名称',
dataIndex: 'packName', dataIndex: 'packName',
width: 100, width: 100,
}, },
@ -277,21 +277,21 @@ export const packColumns: BasicColumn[] = [
}, },
]; ];
// //
export const packFormSchema: FormSchema[] = [ export const packFormSchema: FormSchema[] = [
{ {
field: 'packName', field: 'packName',
label: '产品包名称', label: '套餐包名称',
component: 'JInput', component: 'JInput',
colProps: { xxl: 8 }, colProps: { xxl: 8 },
}, },
]; ];
// //
export const packMenuFormSchema: FormSchema[] = [ export const packMenuFormSchema: FormSchema[] = [
{ {
field: 'packName', field: 'packName',
label: '产品包名称', label: '套餐包名称',
component: 'Input', component: 'Input',
}, },
{ {
@ -377,7 +377,7 @@ export const searchRecycleFormSchema : FormSchema[] = [
}, },
] ]
// //
export const tenantPackUserColumns: BasicColumn[] = [ export const tenantPackUserColumns: BasicColumn[] = [
{ {
title: '用户', title: '用户',