feat(controller): 新增企业ID批量更新功能
- 在BatchImportSupport中添加CompanyIdRefreshStats统计类 - 实现基于企业名称匹配的companyId批量更新逻辑 - 添加normalizeCompanyName和addCompanyNameMapping辅助方法 - 在各个Credit控制器中注入CreditCompanyService依赖 - 为所有相关控制器添加/company-id/refresh接口端点 - 实现多租户环境下的安全匹配和更新机制 - 支持limit参数控制批量处理数量 - 提供详细的更新统计数据返回
This commit is contained in:
@@ -1,13 +1,17 @@
|
|||||||
package com.gxwebsoft.cms.controller;
|
package com.gxwebsoft.cms.controller;
|
||||||
|
|
||||||
|
import cn.afterturn.easypoi.excel.ExcelImportUtil;
|
||||||
|
import cn.afterturn.easypoi.excel.entity.ImportParams;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import com.gxwebsoft.cms.entity.CmsDesign;
|
import com.gxwebsoft.cms.entity.CmsDesign;
|
||||||
import com.gxwebsoft.cms.entity.CmsModel;
|
import com.gxwebsoft.cms.entity.CmsModel;
|
||||||
|
import com.gxwebsoft.cms.param.CmsNavigationImportParam;
|
||||||
import com.gxwebsoft.cms.service.CmsDesignService;
|
import com.gxwebsoft.cms.service.CmsDesignService;
|
||||||
import com.gxwebsoft.cms.service.CmsModelService;
|
import com.gxwebsoft.cms.service.CmsModelService;
|
||||||
|
import com.gxwebsoft.common.core.annotation.OperationLog;
|
||||||
import com.gxwebsoft.common.core.utils.CommonUtil;
|
import com.gxwebsoft.common.core.utils.CommonUtil;
|
||||||
import com.gxwebsoft.common.core.utils.RedisUtil;
|
import com.gxwebsoft.common.core.utils.RedisUtil;
|
||||||
import com.gxwebsoft.common.core.web.BaseController;
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
@@ -21,11 +25,18 @@ import com.gxwebsoft.common.system.entity.User;
|
|||||||
import com.gxwebsoft.common.system.service.UserService;
|
import com.gxwebsoft.common.system.service.UserService;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 网站导航记录表控制器
|
* 网站导航记录表控制器
|
||||||
@@ -79,6 +90,7 @@ public class CmsNavigationController extends BaseController {
|
|||||||
return success(cmsNavigationService.getByIdRelByCodeRel(code));
|
return success(cmsNavigationService.getByIdRelByCodeRel(code));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAuthority('cms:cmsNavigation:save')")
|
||||||
@Operation(summary = "添加网站导航记录表")
|
@Operation(summary = "添加网站导航记录表")
|
||||||
@PostMapping()
|
@PostMapping()
|
||||||
public ApiResult<?> save(@RequestBody CmsNavigation cmsNavigation) {
|
public ApiResult<?> save(@RequestBody CmsNavigation cmsNavigation) {
|
||||||
@@ -99,6 +111,7 @@ public class CmsNavigationController extends BaseController {
|
|||||||
return fail("添加失败");
|
return fail("添加失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAuthority('cms:cmsNavigation:update')")
|
||||||
@Operation(summary = "修改网站导航记录表")
|
@Operation(summary = "修改网站导航记录表")
|
||||||
@PutMapping()
|
@PutMapping()
|
||||||
public ApiResult<?> update(@RequestBody CmsNavigation cmsNavigation) {
|
public ApiResult<?> update(@RequestBody CmsNavigation cmsNavigation) {
|
||||||
@@ -111,6 +124,7 @@ public class CmsNavigationController extends BaseController {
|
|||||||
return fail("修改失败");
|
return fail("修改失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAuthority('cms:cmsNavigation:remove')")
|
||||||
@Operation(summary = "删除网站导航记录表")
|
@Operation(summary = "删除网站导航记录表")
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
public ApiResult<?> remove(@PathVariable("id") Integer id) {
|
public ApiResult<?> remove(@PathVariable("id") Integer id) {
|
||||||
@@ -121,6 +135,7 @@ public class CmsNavigationController extends BaseController {
|
|||||||
return fail("删除失败");
|
return fail("删除失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAuthority('cms:cmsNavigation:save')")
|
||||||
@Operation(summary = "批量添加网站导航记录表")
|
@Operation(summary = "批量添加网站导航记录表")
|
||||||
@PostMapping("/batch")
|
@PostMapping("/batch")
|
||||||
public ApiResult<?> saveBatch(@RequestBody List<CmsNavigation> list) {
|
public ApiResult<?> saveBatch(@RequestBody List<CmsNavigation> list) {
|
||||||
@@ -131,6 +146,7 @@ public class CmsNavigationController extends BaseController {
|
|||||||
return fail("添加失败");
|
return fail("添加失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAuthority('cms:cmsNavigation:update')")
|
||||||
@Operation(summary = "批量修改网站导航记录表")
|
@Operation(summary = "批量修改网站导航记录表")
|
||||||
@PutMapping("/batch")
|
@PutMapping("/batch")
|
||||||
public ApiResult<?> removeBatch(@RequestBody BatchParam<CmsNavigation> batchParam) {
|
public ApiResult<?> removeBatch(@RequestBody BatchParam<CmsNavigation> batchParam) {
|
||||||
@@ -141,6 +157,7 @@ public class CmsNavigationController extends BaseController {
|
|||||||
return fail("修改失败");
|
return fail("修改失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAuthority('cms:cmsNavigation:remove')")
|
||||||
@Operation(summary = "批量删除网站导航记录表")
|
@Operation(summary = "批量删除网站导航记录表")
|
||||||
@DeleteMapping("/batch")
|
@DeleteMapping("/batch")
|
||||||
public ApiResult<?> removeBatch(@RequestBody List<Integer> ids) {
|
public ApiResult<?> removeBatch(@RequestBody List<Integer> ids) {
|
||||||
@@ -151,6 +168,157 @@ public class CmsNavigationController extends BaseController {
|
|||||||
return fail("删除失败");
|
return fail("删除失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* excel批量导入网站导航
|
||||||
|
*/
|
||||||
|
@PreAuthorize("hasAuthority('cms:cmsNavigation:save')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "批量导入网站导航")
|
||||||
|
@Transactional(rollbackFor = {Exception.class})
|
||||||
|
@PostMapping("/import")
|
||||||
|
public ApiResult<?> importBatch(@RequestParam("file") MultipartFile file) {
|
||||||
|
ImportParams importParams = new ImportParams();
|
||||||
|
try {
|
||||||
|
User loginUser = getLoginUser();
|
||||||
|
if (loginUser == null) {
|
||||||
|
return fail("请先登录");
|
||||||
|
}
|
||||||
|
Integer currentUserId = loginUser.getUserId();
|
||||||
|
Integer currentTenantId = loginUser.getTenantId();
|
||||||
|
|
||||||
|
// 1) 清理当前租户的历史数据:先清理 deleted=1,再把 deleted=0 标记为 deleted=1
|
||||||
|
List<CmsNavigation> undeleted = cmsNavigationService.list(new LambdaQueryWrapper<CmsNavigation>()
|
||||||
|
.eq(CmsNavigation::getTenantId, currentTenantId)
|
||||||
|
.eq(CmsNavigation::getDeleted, 0));
|
||||||
|
cmsNavigationService.remove(new LambdaQueryWrapper<CmsNavigation>()
|
||||||
|
.eq(CmsNavigation::getTenantId, currentTenantId)
|
||||||
|
.eq(CmsNavigation::getDeleted, 1));
|
||||||
|
if (!CollectionUtils.isEmpty(undeleted)) {
|
||||||
|
LambdaUpdateWrapper<CmsNavigation> updateWrapper = new LambdaUpdateWrapper<>();
|
||||||
|
updateWrapper.eq(CmsNavigation::getTenantId, currentTenantId);
|
||||||
|
updateWrapper.eq(CmsNavigation::getDeleted, 0);
|
||||||
|
updateWrapper.set(CmsNavigation::getDeleted, 1);
|
||||||
|
cmsNavigationService.update(updateWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) 读取Excel
|
||||||
|
List<CmsNavigationImportParam> list = ExcelImportUtil.importExcel(
|
||||||
|
file.getInputStream(), CmsNavigationImportParam.class, importParams);
|
||||||
|
if (CollectionUtils.isEmpty(list)) {
|
||||||
|
return fail("未读取到数据,请确认模板表头与示例格式一致");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) 按 parentId 分组(使用导入文件中的“原始ID/parentId”,以便重建树结构)
|
||||||
|
Map<Integer, List<CmsNavigationImportParam>> navGroups = new HashMap<>();
|
||||||
|
Map<Integer, CmsNavigation> tempIdMapping = new HashMap<>();
|
||||||
|
for (CmsNavigationImportParam param : list) {
|
||||||
|
Integer parentId = param.getParentId() != null ? param.getParentId() : 0;
|
||||||
|
navGroups.computeIfAbsent(parentId, k -> new ArrayList<>()).add(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4) 先创建根导航(parentId=0)
|
||||||
|
List<CmsNavigationImportParam> root = navGroups.getOrDefault(0, new ArrayList<>());
|
||||||
|
for (CmsNavigationImportParam param : root) {
|
||||||
|
CmsNavigation nav = convertToNavigation(param, currentUserId, currentTenantId);
|
||||||
|
nav.setParentId(0);
|
||||||
|
cmsNavigationService.save(nav);
|
||||||
|
cmsNavigationService.saveAsync(nav);
|
||||||
|
if (param.getNavigationId() != null) {
|
||||||
|
tempIdMapping.put(param.getNavigationId(), nav);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5) 递归创建子导航:从根节点开始递归,避免重复创建
|
||||||
|
for (CmsNavigationImportParam param : root) {
|
||||||
|
if (param.getNavigationId() != null) {
|
||||||
|
createChildNavigations(navGroups, tempIdMapping, param.getNavigationId(), currentUserId, currentTenantId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
redisUtil.delete(SITE_INFO_KEY_PREFIX.concat(currentTenantId.toString()));
|
||||||
|
return success("成功导入" + list.size() + "条");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return fail("导入失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归创建子级导航
|
||||||
|
*/
|
||||||
|
private void createChildNavigations(Map<Integer, List<CmsNavigationImportParam>> navGroups,
|
||||||
|
Map<Integer, CmsNavigation> tempIdMapping,
|
||||||
|
Integer originalParentId,
|
||||||
|
Integer defaultUserId,
|
||||||
|
Integer defaultTenantId) {
|
||||||
|
if (originalParentId == null || originalParentId == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<CmsNavigationImportParam> children = navGroups.get(originalParentId);
|
||||||
|
if (CollectionUtils.isEmpty(children)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CmsNavigation parent = tempIdMapping.get(originalParentId);
|
||||||
|
if (parent == null || parent.getNavigationId() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Integer newParentId = parent.getNavigationId();
|
||||||
|
for (CmsNavigationImportParam param : children) {
|
||||||
|
CmsNavigation nav = convertToNavigation(param, defaultUserId, defaultTenantId);
|
||||||
|
nav.setParentId(newParentId);
|
||||||
|
cmsNavigationService.save(nav);
|
||||||
|
cmsNavigationService.saveAsync(nav);
|
||||||
|
if (param.getNavigationId() != null) {
|
||||||
|
tempIdMapping.put(param.getNavigationId(), nav);
|
||||||
|
}
|
||||||
|
createChildNavigations(navGroups, tempIdMapping, param.getNavigationId(), defaultUserId, defaultTenantId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private CmsNavigation convertToNavigation(CmsNavigationImportParam param, Integer defaultUserId, Integer defaultTenantId) {
|
||||||
|
CmsNavigation nav = new CmsNavigation();
|
||||||
|
nav.setType(param.getType());
|
||||||
|
nav.setTitle(StrUtil.trimStart(param.getTitle()));
|
||||||
|
nav.setParentId(param.getParentId() != null ? param.getParentId() : 0);
|
||||||
|
// saveAsync 依赖 model 生成路由/页面;缺省按 page 处理
|
||||||
|
nav.setModel(StrUtil.isBlank(param.getModel()) ? "page" : param.getModel());
|
||||||
|
nav.setCode(param.getCode());
|
||||||
|
nav.setPath(param.getPath());
|
||||||
|
nav.setComponent(param.getComponent());
|
||||||
|
nav.setTarget(param.getTarget());
|
||||||
|
nav.setIcon(param.getIcon());
|
||||||
|
nav.setColor(param.getColor());
|
||||||
|
nav.setHide(param.getHide());
|
||||||
|
nav.setPermission(param.getPermission());
|
||||||
|
nav.setPassword(param.getPassword());
|
||||||
|
nav.setPosition(param.getPosition());
|
||||||
|
nav.setTop(param.getTop());
|
||||||
|
nav.setBottom(param.getBottom());
|
||||||
|
nav.setActive(param.getActive());
|
||||||
|
nav.setMeta(param.getMeta());
|
||||||
|
nav.setStyle(param.getStyle());
|
||||||
|
nav.setModelName(param.getModelName());
|
||||||
|
nav.setPageId(param.getPageId());
|
||||||
|
nav.setItemId(param.getItemId());
|
||||||
|
nav.setIsMpWeixin(param.getIsMpWeixin());
|
||||||
|
nav.setGutter(param.getGutter());
|
||||||
|
nav.setSpan(param.getSpan());
|
||||||
|
nav.setReadNum(param.getReadNum());
|
||||||
|
nav.setMerchantId(param.getMerchantId());
|
||||||
|
nav.setLang(param.getLang());
|
||||||
|
nav.setHome(param.getHome());
|
||||||
|
nav.setRecommend(param.getRecommend());
|
||||||
|
nav.setSortNumber(param.getSortNumber() != null ? param.getSortNumber() : 0);
|
||||||
|
nav.setComments(param.getComments());
|
||||||
|
nav.setStatus(param.getStatus() != null ? param.getStatus() : 0);
|
||||||
|
|
||||||
|
// 默认用户/租户兜底(允许Excel里不填)
|
||||||
|
nav.setUserId(param.getUserId() != null ? param.getUserId() : defaultUserId);
|
||||||
|
nav.setTenantId(param.getTenantId() != null ? param.getTenantId() : defaultTenantId);
|
||||||
|
nav.setDeleted(0);
|
||||||
|
return nav;
|
||||||
|
}
|
||||||
|
|
||||||
@Operation(summary = "获取树形结构的网站导航数据")
|
@Operation(summary = "获取树形结构的网站导航数据")
|
||||||
@GetMapping("/tree")
|
@GetMapping("/tree")
|
||||||
public ApiResult<List<CmsNavigation>> tree(CmsNavigationParam param) {
|
public ApiResult<List<CmsNavigation>> tree(CmsNavigationParam param) {
|
||||||
|
|||||||
@@ -1,20 +1,29 @@
|
|||||||
package com.gxwebsoft.common.system.controller;
|
package com.gxwebsoft.common.system.controller;
|
||||||
|
|
||||||
|
import cn.afterturn.easypoi.excel.ExcelImportUtil;
|
||||||
|
import cn.afterturn.easypoi.excel.entity.ImportParams;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import com.gxwebsoft.common.core.annotation.OperationLog;
|
import com.gxwebsoft.common.core.annotation.OperationLog;
|
||||||
import com.gxwebsoft.common.core.web.*;
|
import com.gxwebsoft.common.core.web.*;
|
||||||
import com.gxwebsoft.common.system.entity.Menu;
|
import com.gxwebsoft.common.system.entity.*;
|
||||||
import com.gxwebsoft.common.system.entity.Plug;
|
import com.gxwebsoft.common.system.param.MenuImportParam;
|
||||||
import com.gxwebsoft.common.system.param.MenuParam;
|
import com.gxwebsoft.common.system.param.MenuParam;
|
||||||
import com.gxwebsoft.common.system.service.MenuService;
|
import com.gxwebsoft.common.system.param.VersionParam;
|
||||||
import com.gxwebsoft.common.system.service.PlugService;
|
import com.gxwebsoft.common.system.service.*;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.List;
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 菜单控制器
|
* 菜单控制器
|
||||||
@@ -22,21 +31,27 @@ import java.util.List;
|
|||||||
* @author WebSoft
|
* @author WebSoft
|
||||||
* @since 2018-12-24 16:10:23
|
* @since 2018-12-24 16:10:23
|
||||||
*/
|
*/
|
||||||
@Tag(name = "菜单管理")
|
@Tag(name = "菜单")
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/system/menu")
|
@RequestMapping("/api/system/menu")
|
||||||
public class MenuController extends BaseController {
|
public class MenuController extends BaseController {
|
||||||
@Resource
|
@Resource
|
||||||
private MenuService menuService;
|
private MenuService menuService;
|
||||||
@Resource
|
@Resource
|
||||||
private PlugService plugService;
|
private CompanyService companyService;
|
||||||
|
@Resource
|
||||||
|
private UserService userService;
|
||||||
|
@Resource
|
||||||
|
private RoleService roleService;
|
||||||
|
@Resource
|
||||||
|
private RoleMenuService roleMenuService;
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('sys:menu:list')")
|
@PreAuthorize("hasAuthority('sys:menu:list')")
|
||||||
@Operation(summary = "分页查询菜单")
|
@Operation(summary = "分页查询菜单")
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
public ApiResult<PageResult<Menu>> page(MenuParam param) {
|
public ApiResult<PageResult<Menu>> page(MenuParam param) {
|
||||||
PageParam<Menu, MenuParam> page = new PageParam<>(param);
|
PageParam<Menu, MenuParam> page = new PageParam<>(param);
|
||||||
page.setDefaultOrder("sort_number");
|
page.setDefaultOrder("sort_number asc, create_time desc");
|
||||||
return success(menuService.page(page, page.getWrapper()));
|
return success(menuService.page(page, page.getWrapper()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +60,7 @@ public class MenuController extends BaseController {
|
|||||||
@GetMapping()
|
@GetMapping()
|
||||||
public ApiResult<List<Menu>> list(MenuParam param) {
|
public ApiResult<List<Menu>> list(MenuParam param) {
|
||||||
PageParam<Menu, MenuParam> page = new PageParam<>(param);
|
PageParam<Menu, MenuParam> page = new PageParam<>(param);
|
||||||
page.setDefaultOrder("sort_number");
|
page.setDefaultOrder("sort_number asc, create_time desc");
|
||||||
return success(menuService.list(page.getOrderWrapper()));
|
return success(menuService.list(page.getOrderWrapper()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,12 +72,18 @@ public class MenuController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('sys:menu:save')")
|
@PreAuthorize("hasAuthority('sys:menu:save')")
|
||||||
|
@OperationLog
|
||||||
@Operation(summary = "添加菜单")
|
@Operation(summary = "添加菜单")
|
||||||
@PostMapping()
|
@PostMapping()
|
||||||
public ApiResult<?> add(@RequestBody Menu menu) {
|
public ApiResult<?> add(@RequestBody Menu menu) {
|
||||||
if (menu.getParentId() == null) {
|
if (menu.getParentId() == null) {
|
||||||
menu.setParentId(0);
|
menu.setParentId(0);
|
||||||
}
|
}
|
||||||
|
// 去除字符串前面的空格
|
||||||
|
menu.setTitle(StrUtil.trimStart(menu.getTitle()));
|
||||||
|
menu.setPath(StrUtil.trimStart(menu.getPath()));
|
||||||
|
menu.setComponent(StrUtil.trimStart(menu.getComponent()));
|
||||||
|
menu.setAuthority(StrUtil.trimStart(menu.getAuthority()));
|
||||||
if (menuService.save(menu)) {
|
if (menuService.save(menu)) {
|
||||||
return success("添加成功");
|
return success("添加成功");
|
||||||
}
|
}
|
||||||
@@ -70,9 +91,15 @@ public class MenuController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('sys:menu:update')")
|
@PreAuthorize("hasAuthority('sys:menu:update')")
|
||||||
|
@OperationLog
|
||||||
@Operation(summary = "修改菜单")
|
@Operation(summary = "修改菜单")
|
||||||
@PutMapping()
|
@PutMapping()
|
||||||
public ApiResult<?> update(@RequestBody Menu menu) {
|
public ApiResult<?> update(@RequestBody Menu menu) {
|
||||||
|
// 去除字符串前面的空格
|
||||||
|
menu.setTitle(StrUtil.trimStart(menu.getTitle()));
|
||||||
|
menu.setPath(StrUtil.trimStart(menu.getPath()));
|
||||||
|
menu.setComponent(StrUtil.trimStart(menu.getComponent()));
|
||||||
|
menu.setAuthority(StrUtil.trimStart(menu.getAuthority()));
|
||||||
if (menuService.updateById(menu)) {
|
if (menuService.updateById(menu)) {
|
||||||
return success("修改成功");
|
return success("修改成功");
|
||||||
}
|
}
|
||||||
@@ -80,6 +107,7 @@ public class MenuController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('sys:menu:remove')")
|
@PreAuthorize("hasAuthority('sys:menu:remove')")
|
||||||
|
@OperationLog
|
||||||
@Operation(summary = "删除菜单")
|
@Operation(summary = "删除菜单")
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
public ApiResult<?> remove(@PathVariable("id") Integer id) {
|
public ApiResult<?> remove(@PathVariable("id") Integer id) {
|
||||||
@@ -90,6 +118,7 @@ public class MenuController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('sys:menu:save')")
|
@PreAuthorize("hasAuthority('sys:menu:save')")
|
||||||
|
@OperationLog
|
||||||
@Operation(summary = "批量添加菜单")
|
@Operation(summary = "批量添加菜单")
|
||||||
@PostMapping("/batch")
|
@PostMapping("/batch")
|
||||||
public ApiResult<?> saveBatch(@RequestBody List<Menu> menus) {
|
public ApiResult<?> saveBatch(@RequestBody List<Menu> menus) {
|
||||||
@@ -100,6 +129,7 @@ public class MenuController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('sys:menu:update')")
|
@PreAuthorize("hasAuthority('sys:menu:update')")
|
||||||
|
@OperationLog
|
||||||
@Operation(summary = "批量修改菜单")
|
@Operation(summary = "批量修改菜单")
|
||||||
@PutMapping("/batch")
|
@PutMapping("/batch")
|
||||||
public ApiResult<?> updateBatch(@RequestBody BatchParam<Menu> batchParam) {
|
public ApiResult<?> updateBatch(@RequestBody BatchParam<Menu> batchParam) {
|
||||||
@@ -110,6 +140,7 @@ public class MenuController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('sys:menu:remove')")
|
@PreAuthorize("hasAuthority('sys:menu:remove')")
|
||||||
|
@OperationLog
|
||||||
@Operation(summary = "批量删除菜单")
|
@Operation(summary = "批量删除菜单")
|
||||||
@DeleteMapping("/batch")
|
@DeleteMapping("/batch")
|
||||||
public ApiResult<?> removeBatch(@RequestBody List<Integer> ids) {
|
public ApiResult<?> removeBatch(@RequestBody List<Integer> ids) {
|
||||||
@@ -119,14 +150,35 @@ public class MenuController extends BaseController {
|
|||||||
return fail("删除失败");
|
return fail("删除失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('sys:menu:update')")
|
@PreAuthorize("hasAuthority('sys:menu:remove')")
|
||||||
@Operation(summary = "菜单克隆")
|
@Operation(summary = "删除父级以下菜单")
|
||||||
@PostMapping("/clone")
|
@DeleteMapping("/deleteParentMenu/{id}")
|
||||||
public ApiResult<?> onClone(@RequestBody MenuParam param){
|
public ApiResult<?> deleteParentMenu(@PathVariable("id") Integer id) {
|
||||||
if(menuService.cloneMenu(param)){
|
final List<Menu> list = menuService.list(new LambdaQueryWrapper<Menu>().eq(Menu::getParentId, id));
|
||||||
return success("克隆成功,请刷新");
|
if (CollectionUtils.isEmpty(list)) {
|
||||||
|
menuService.removeById(id);
|
||||||
|
return success("删除成功");
|
||||||
}
|
}
|
||||||
return fail("克隆失败");
|
final Set<Integer> ids = list.stream().map(Menu::getMenuId).collect(Collectors.toSet());
|
||||||
|
final List<Menu> list2 = menuService.list(new LambdaUpdateWrapper<Menu>().in(Menu::getParentId, ids));
|
||||||
|
final Set<Integer> collect = list2.stream().map(Menu::getMenuId).collect(Collectors.toSet());
|
||||||
|
if (!CollectionUtils.isEmpty(list2)) {
|
||||||
|
ids.addAll(collect);
|
||||||
|
final List<Menu> list3 = menuService.list(new LambdaUpdateWrapper<Menu>().in(Menu::getParentId, ids));
|
||||||
|
final Set<Integer> collect1 = list3.stream().map(Menu::getMenuId).collect(Collectors.toSet());
|
||||||
|
if (!CollectionUtils.isEmpty(collect1)) {
|
||||||
|
ids.addAll(collect1);
|
||||||
|
}
|
||||||
|
ids.add(id);
|
||||||
|
if (menuService.removeByIds(ids)) {
|
||||||
|
return success("删除成功");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ids.add(id);
|
||||||
|
if (menuService.removeByIds(ids)) {
|
||||||
|
return success("删除成功");
|
||||||
|
}
|
||||||
|
return fail("删除失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('sys:menu:update')")
|
@PreAuthorize("hasAuthority('sys:menu:update')")
|
||||||
@@ -135,11 +187,271 @@ public class MenuController extends BaseController {
|
|||||||
public ApiResult<?> install(@PathVariable("id") Integer id){
|
public ApiResult<?> install(@PathVariable("id") Integer id){
|
||||||
if(menuService.install(id)){
|
if(menuService.install(id)){
|
||||||
// 更新安装次数
|
// 更新安装次数
|
||||||
final Plug plug = plugService.getOne(new LambdaQueryWrapper<Plug>().eq(Plug::getMenuId, id));
|
// final Plug plug = plugService.getOne(new LambdaQueryWrapper<Plug>().eq(Plug::getMenuId, id));
|
||||||
plug.setInstalls(plug.getInstalls() + 1);
|
// plug.setInstalls(plug.getInstalls() + 1);
|
||||||
plugService.updateById(plug);
|
// plugService.updateById(plug);
|
||||||
return success("安装成功");
|
return success("安装成功");
|
||||||
}
|
}
|
||||||
return fail("安装失败",id);
|
return fail("安装失败",id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* excel批量导入菜单
|
||||||
|
*/
|
||||||
|
@PreAuthorize("hasAuthority('sys:menu:save')")
|
||||||
|
@Operation(summary = "批量导入菜单")
|
||||||
|
@Transactional(rollbackFor = {Exception.class})
|
||||||
|
@PostMapping("/import")
|
||||||
|
public ApiResult<?> importBatch(MultipartFile file) {
|
||||||
|
ImportParams importParams = new ImportParams();
|
||||||
|
try {
|
||||||
|
System.out.println("=== 开始菜单导入流程 ===");
|
||||||
|
|
||||||
|
// 检查导入前的菜单数据
|
||||||
|
long beforeCount = menuService.count();
|
||||||
|
System.out.println("导入前菜单总数: " + beforeCount);
|
||||||
|
|
||||||
|
// 检查当前未删除的菜单
|
||||||
|
List<Menu> undeletedMenus = menuService.list(new LambdaQueryWrapper<Menu>().eq(Menu::getDeleted, 0));
|
||||||
|
System.out.println("当前未删除的菜单数: " + undeletedMenus.size());
|
||||||
|
if (!undeletedMenus.isEmpty()) {
|
||||||
|
System.out.println("未删除菜单列表:");
|
||||||
|
for (Menu menu : undeletedMenus) {
|
||||||
|
System.out.println(" ID: " + menu.getMenuId() + ", 名称: " + menu.getTitle() +
|
||||||
|
", 父级ID: " + menu.getParentId() + ", deleted: " + menu.getDeleted());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第一步:永久删除已标记为 deleted=1 的记录
|
||||||
|
boolean deleteResult = menuService.remove(new LambdaQueryWrapper<Menu>().eq(Menu::getDeleted, 1));
|
||||||
|
System.out.println("已永久删除标记为deleted=1的菜单记录,结果: " + deleteResult);
|
||||||
|
|
||||||
|
// 第二步:将现有未删除的记录(deleted=0)标记为 deleted=1
|
||||||
|
if (!undeletedMenus.isEmpty()) {
|
||||||
|
LambdaUpdateWrapper<Menu> updateWrapper = new LambdaUpdateWrapper<>();
|
||||||
|
updateWrapper.eq(Menu::getDeleted, 0);
|
||||||
|
updateWrapper.set(Menu::getDeleted, 1);
|
||||||
|
boolean updateResult = menuService.update(updateWrapper);
|
||||||
|
System.out.println("更新未删除菜单记录的结果: " + updateResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查更新后的菜单数据
|
||||||
|
long afterCleanupCount = menuService.count(new LambdaQueryWrapper<Menu>().eq(Menu::getDeleted, 0));
|
||||||
|
System.out.println("清理后未标记删除的菜单数: " + afterCleanupCount);
|
||||||
|
|
||||||
|
// 第三步:导入XLS文件的内容
|
||||||
|
List<MenuImportParam> list = ExcelImportUtil.importExcel(file.getInputStream(), MenuImportParam.class, importParams);
|
||||||
|
System.out.println("从Excel文件中读取到" + list.size() + "条菜单记录");
|
||||||
|
|
||||||
|
// 存储原始parentId到菜单列表的映射关系
|
||||||
|
Map<Integer, List<MenuImportParam>> menuGroups = new HashMap<>();
|
||||||
|
// 存储原始ID到新菜单对象的映射关系(用于后续设置正确的parentId)
|
||||||
|
Map<Integer, Menu> tempIdMapping = new HashMap<>();
|
||||||
|
|
||||||
|
// 按parentId分组(处理null值)
|
||||||
|
for (MenuImportParam param : list) {
|
||||||
|
Integer parentId = param.getParentId() != null ? param.getParentId() : 0;
|
||||||
|
menuGroups.computeIfAbsent(parentId, k -> new ArrayList<>()).add(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("菜单分组情况:");
|
||||||
|
for (Map.Entry<Integer, List<MenuImportParam>> entry : menuGroups.entrySet()) {
|
||||||
|
System.out.println(" parentId=" + entry.getKey() + " 的菜单数: " + entry.getValue().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先创建所有父级菜单(parentId为0的菜单)
|
||||||
|
List<MenuImportParam> rootMenus = menuGroups.getOrDefault(0, new ArrayList<>());
|
||||||
|
List<Menu> createdRootMenus = new ArrayList<>();
|
||||||
|
|
||||||
|
System.out.println("开始创建" + rootMenus.size() + "个根菜单");
|
||||||
|
for (MenuImportParam param : rootMenus) {
|
||||||
|
Menu menu = convertToMenu(param);
|
||||||
|
menu.setParentId(0); // 根菜单的parentId为0
|
||||||
|
menuService.save(menu);
|
||||||
|
createdRootMenus.add(menu);
|
||||||
|
System.out.println("创建根菜单: " + menu.getTitle() + ", ID: " + menu.getMenuId() + ", 原始ID: " + param.getMenuId());
|
||||||
|
|
||||||
|
// 记录原始ID到新菜单的映射关系
|
||||||
|
if (param.getMenuId() != null) {
|
||||||
|
tempIdMapping.put(param.getMenuId(), menu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归创建子级菜单(注意:这里不再处理parentId=0的菜单,因为已经在上面处理过了)
|
||||||
|
System.out.println("开始创建子级菜单(跳过根菜单)");
|
||||||
|
// 只处理非根菜单的子级菜单
|
||||||
|
for (Map.Entry<Integer, List<MenuImportParam>> entry : menuGroups.entrySet()) {
|
||||||
|
Integer parentId = entry.getKey();
|
||||||
|
if (parentId != 0) { // 跳过根菜单(parentId=0)
|
||||||
|
System.out.println("处理parentId=" + parentId + "的子菜单");
|
||||||
|
createChildMenus(menuGroups, tempIdMapping, parentId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有导入的菜单ID
|
||||||
|
List<Integer> allImportedMenuIds = new ArrayList<>();
|
||||||
|
for (Menu menu : tempIdMapping.values()) {
|
||||||
|
allImportedMenuIds.add(menu.getMenuId());
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("总共导入了" + allImportedMenuIds.size() + "个菜单");
|
||||||
|
|
||||||
|
// 显示导入的菜单详情
|
||||||
|
if (!allImportedMenuIds.isEmpty()) {
|
||||||
|
List<Menu> allImportedMenus = menuService.list(new LambdaQueryWrapper<Menu>()
|
||||||
|
.in(Menu::getMenuId, allImportedMenuIds)
|
||||||
|
.orderByAsc(Menu::getParentId, Menu::getSortNumber));
|
||||||
|
System.out.println("导入的菜单详情:");
|
||||||
|
for (Menu menu : allImportedMenus) {
|
||||||
|
System.out.println(" ID: " + menu.getMenuId() + ", 名称: " + menu.getTitle() +
|
||||||
|
", 父级ID: " + menu.getParentId() + ", 类型: " + menu.getMenuType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为超级管理员配置菜单权限
|
||||||
|
if (!allImportedMenuIds.isEmpty()) {
|
||||||
|
List<Menu> allImportedMenus = menuService.list(new LambdaQueryWrapper<Menu>()
|
||||||
|
.in(Menu::getMenuId, allImportedMenuIds));
|
||||||
|
System.out.println("为" + allImportedMenus.size() + "个菜单配置超级管理员权限");
|
||||||
|
configureSuperAdminPermissionsForImportedMenus(allImportedMenus);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最终检查
|
||||||
|
long finalCount = menuService.count();
|
||||||
|
System.out.println("导入后菜单总数: " + finalCount);
|
||||||
|
System.out.println("=== 菜单导入流程结束 ===");
|
||||||
|
|
||||||
|
return success("成功导入" + list.size() + "条");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return fail("导入失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归创建子级菜单
|
||||||
|
* @param menuGroups 菜单分组
|
||||||
|
* @param tempIdMapping 临时ID映射关系
|
||||||
|
* @param originalParentId 原始父级ID
|
||||||
|
*/
|
||||||
|
private void createChildMenus(Map<Integer, List<MenuImportParam>> menuGroups,
|
||||||
|
Map<Integer, Menu> tempIdMapping,
|
||||||
|
Integer originalParentId) {
|
||||||
|
System.out.println(">>> 进入createChildMenus方法,处理originalParentId=" + originalParentId);
|
||||||
|
|
||||||
|
// 特殊处理:originalParentId=0的情况已经在主方法中处理过了,这里不应该再处理
|
||||||
|
if (originalParentId == 0) {
|
||||||
|
System.out.println(" 跳过originalParentId=0的处理(已在主方法中处理)");
|
||||||
|
System.out.println("<<< 退出createChildMenus方法,处理originalParentId=" + originalParentId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<MenuImportParam> childMenus = menuGroups.get(originalParentId);
|
||||||
|
if (childMenus == null || childMenus.isEmpty()) {
|
||||||
|
System.out.println(" 没有找到originalParentId=" + originalParentId + "的子菜单");
|
||||||
|
System.out.println("<<< 退出createChildMenus方法,处理originalParentId=" + originalParentId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取新的父级菜单对象
|
||||||
|
Menu parentMenu = tempIdMapping.get(originalParentId);
|
||||||
|
if (parentMenu == null) {
|
||||||
|
System.out.println(" 未找到原始ID为" + originalParentId + "的父级菜单,跳过处理");
|
||||||
|
System.out.println("<<< 退出createChildMenus方法,处理originalParentId=" + originalParentId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer newParentId = parentMenu.getMenuId();
|
||||||
|
|
||||||
|
System.out.println(" 创建父级ID为" + originalParentId + "(新ID:" + newParentId + ")的子菜单,共" + childMenus.size() + "个");
|
||||||
|
|
||||||
|
// 创建所有子菜单
|
||||||
|
for (int i = 0; i < childMenus.size(); i++) {
|
||||||
|
MenuImportParam param = childMenus.get(i);
|
||||||
|
System.out.println(" 处理第" + (i+1) + "个子菜单,原始ID: " + param.getMenuId() + ", 原始ParentId: " + param.getParentId());
|
||||||
|
|
||||||
|
Menu menu = convertToMenu(param);
|
||||||
|
menu.setParentId(newParentId);
|
||||||
|
menuService.save(menu);
|
||||||
|
|
||||||
|
System.out.println(" 创建子菜单: " + menu.getTitle() + ", ID: " + menu.getMenuId() +
|
||||||
|
", 父级ID: " + menu.getParentId() + ", 原始ID: " + param.getMenuId());
|
||||||
|
|
||||||
|
// 记录原始ID到新菜单的映射关系
|
||||||
|
if (param.getMenuId() != null) {
|
||||||
|
tempIdMapping.put(param.getMenuId(), menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归创建当前菜单的子级菜单(使用原始ID作为下一个递归的父级ID)
|
||||||
|
System.out.println(" 递归调用createChildMenus,处理原始ID=" + param.getMenuId() + "的子菜单");
|
||||||
|
createChildMenus(menuGroups, tempIdMapping, param.getMenuId());
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("<<< 退出createChildMenus方法,处理originalParentId=" + originalParentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将MenuImportParam转换为Menu实体
|
||||||
|
* @param param MenuImportParam对象
|
||||||
|
* @return Menu实体
|
||||||
|
*/
|
||||||
|
private Menu convertToMenu(MenuImportParam param) {
|
||||||
|
Menu menu = new Menu();
|
||||||
|
menu.setParentId(param.getParentId());
|
||||||
|
menu.setTitle(param.getTitle());
|
||||||
|
menu.setPath(param.getPath());
|
||||||
|
menu.setComponent(param.getComponent());
|
||||||
|
menu.setModules(param.getModules());
|
||||||
|
menu.setModulesUrl(param.getModulesUrl());
|
||||||
|
menu.setMenuType(param.getMenuType());
|
||||||
|
menu.setSortNumber(param.getSortNumber() != null ? param.getSortNumber() : 0);
|
||||||
|
menu.setAuthority(param.getAuthority());
|
||||||
|
menu.setIcon(param.getIcon());
|
||||||
|
menu.setHide(param.getHide());
|
||||||
|
menu.setAppId(param.getAppId());
|
||||||
|
menu.setTenantId(param.getTenantId());
|
||||||
|
menu.setDeleted(0); // 新导入的数据deleted设为0
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为超级管理员配置导入菜单的权限
|
||||||
|
* @param importedMenus 导入的菜单列表
|
||||||
|
*/
|
||||||
|
private void configureSuperAdminPermissionsForImportedMenus(List<Menu> importedMenus) {
|
||||||
|
try {
|
||||||
|
// 1.查找当前租户的超管权限的roleId
|
||||||
|
final Role superAdmin = roleService.getOne(new LambdaQueryWrapper<Role>().eq(Role::getRoleCode, "superAdmin"));
|
||||||
|
if (superAdmin == null) {
|
||||||
|
System.out.println("未找到superAdmin角色");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Integer roleId = superAdmin.getRoleId();
|
||||||
|
final Integer tenantId = superAdmin.getTenantId();
|
||||||
|
|
||||||
|
// 为所有导入的菜单配置权限
|
||||||
|
for (Menu menu : importedMenus) {
|
||||||
|
RoleMenu roleMenu = new RoleMenu();
|
||||||
|
roleMenu.setRoleId(roleId);
|
||||||
|
roleMenu.setMenuId(menu.getMenuId());
|
||||||
|
roleMenu.setTenantId(tenantId);
|
||||||
|
roleMenuService.save(roleMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调整根菜单的排序(如果有根菜单的话)
|
||||||
|
for (Menu menu : importedMenus) {
|
||||||
|
if (menu.getParentId() == 0) {
|
||||||
|
menu.setSortNumber(0);
|
||||||
|
menuService.updateById(menu);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("为超级管理员配置菜单权限成功,共配置了" + importedMenus.size() + "个菜单");
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("为超级管理员配置菜单权限失败: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
package com.gxwebsoft.common.system.entity;
|
package com.gxwebsoft.common.system.entity;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.*;
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.util.Date;
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -19,6 +18,7 @@ import java.util.List;
|
|||||||
@Data
|
@Data
|
||||||
@Schema(description = "菜单")
|
@Schema(description = "菜单")
|
||||||
@TableName("sys_menu")
|
@TableName("sys_menu")
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class Menu implements GrantedAuthority {
|
public class Menu implements GrantedAuthority {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
public static final int TYPE_MENU = 0; // 菜单类型
|
public static final int TYPE_MENU = 0; // 菜单类型
|
||||||
@@ -40,9 +40,19 @@ public class Menu implements GrantedAuthority {
|
|||||||
@Schema(description = "菜单组件地址")
|
@Schema(description = "菜单组件地址")
|
||||||
private String component;
|
private String component;
|
||||||
|
|
||||||
|
@Schema(description = "模块ID")
|
||||||
|
private String modules;
|
||||||
|
|
||||||
|
@Schema(description = "模块API")
|
||||||
|
private String modulesUrl;
|
||||||
|
|
||||||
@Schema(description = "菜单类型, 0菜单, 1按钮")
|
@Schema(description = "菜单类型, 0菜单, 1按钮")
|
||||||
private Integer menuType;
|
private Integer menuType;
|
||||||
|
|
||||||
|
@Schema(description = "打开方式, 0当前页, 1新窗口")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Integer openType;
|
||||||
|
|
||||||
@Schema(description = "排序号")
|
@Schema(description = "排序号")
|
||||||
private Integer sortNumber;
|
private Integer sortNumber;
|
||||||
|
|
||||||
@@ -69,12 +79,10 @@ public class Menu implements GrantedAuthority {
|
|||||||
private Integer tenantId;
|
private Integer tenantId;
|
||||||
|
|
||||||
@Schema(description = "创建时间")
|
@Schema(description = "创建时间")
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
private Date createTime;
|
||||||
private LocalDateTime createTime;
|
|
||||||
|
|
||||||
@Schema(description = "修改时间")
|
@Schema(description = "修改时间")
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
private Date updateTime;
|
||||||
private LocalDateTime updateTime;
|
|
||||||
|
|
||||||
@Schema(description = "子菜单")
|
@Schema(description = "子菜单")
|
||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package com.gxwebsoft.common.system.param;
|
||||||
|
|
||||||
|
import cn.afterturn.easypoi.excel.annotation.Excel;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单导入参数
|
||||||
|
*
|
||||||
|
* @author WebSoft
|
||||||
|
* @since 2025-09-30
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class MenuImportParam implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Excel(name = "菜单ID")
|
||||||
|
private Integer menuId;
|
||||||
|
|
||||||
|
@Excel(name = "父级ID")
|
||||||
|
private Integer parentId;
|
||||||
|
|
||||||
|
@Excel(name = "菜单名称")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@Excel(name = "路由地址")
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
@Excel(name = "组件路径")
|
||||||
|
private String component;
|
||||||
|
|
||||||
|
@Excel(name = "模块ID")
|
||||||
|
private String modules;
|
||||||
|
|
||||||
|
@Excel(name = "模块API")
|
||||||
|
private String modulesUrl;
|
||||||
|
|
||||||
|
@Excel(name = "菜单类型")
|
||||||
|
private Integer menuType;
|
||||||
|
|
||||||
|
@Excel(name = "排序号")
|
||||||
|
private Integer sortNumber;
|
||||||
|
|
||||||
|
@Excel(name = "权限标识")
|
||||||
|
private String authority;
|
||||||
|
|
||||||
|
@Excel(name = "图标")
|
||||||
|
private String icon;
|
||||||
|
|
||||||
|
@Excel(name = "是否隐藏")
|
||||||
|
private Integer hide;
|
||||||
|
|
||||||
|
@Excel(name = "关联应用")
|
||||||
|
private Integer appId;
|
||||||
|
|
||||||
|
@Excel(name = "租户id")
|
||||||
|
private Integer tenantId;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
package com.gxwebsoft.common.system.param;
|
package com.gxwebsoft.common.system.param;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.gxwebsoft.common.core.annotation.QueryField;
|
import com.gxwebsoft.common.core.annotation.QueryField;
|
||||||
import com.gxwebsoft.common.core.annotation.QueryType;
|
import com.gxwebsoft.common.core.annotation.QueryType;
|
||||||
import com.gxwebsoft.common.core.web.BaseParam;
|
import com.gxwebsoft.common.core.web.BaseParam;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
@@ -40,10 +40,17 @@ public class MenuParam extends BaseParam {
|
|||||||
@Schema(description = "菜单组件地址")
|
@Schema(description = "菜单组件地址")
|
||||||
private String component;
|
private String component;
|
||||||
|
|
||||||
|
@Schema(description = "模块ID")
|
||||||
|
private String modules;
|
||||||
|
|
||||||
@Schema(description = "菜单类型, 0菜单, 1按钮")
|
@Schema(description = "菜单类型, 0菜单, 1按钮")
|
||||||
@QueryField(type = QueryType.EQ)
|
@QueryField(type = QueryType.EQ)
|
||||||
private Integer menuType;
|
private Integer menuType;
|
||||||
|
|
||||||
|
@Schema(description = "打开方式, 0当前页, 1新窗口")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Integer openType;
|
||||||
|
|
||||||
@Schema(description = "权限标识")
|
@Schema(description = "权限标识")
|
||||||
private String authority;
|
private String authority;
|
||||||
|
|
||||||
@@ -65,4 +72,12 @@ public class MenuParam extends BaseParam {
|
|||||||
@QueryField(type = QueryType.EQ)
|
@QueryField(type = QueryType.EQ)
|
||||||
private Integer tenantId;
|
private Integer tenantId;
|
||||||
|
|
||||||
|
@Schema(description = "企业ID")
|
||||||
|
@QueryField(type = QueryType.EQ)
|
||||||
|
private Integer companyId;
|
||||||
|
|
||||||
|
@Schema(description = "租户名称")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String tenantName;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package com.gxwebsoft.common.system.param;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.gxwebsoft.common.core.annotation.QueryField;
|
||||||
|
import com.gxwebsoft.common.core.annotation.QueryType;
|
||||||
|
import com.gxwebsoft.common.core.web.BaseParam;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 版本更新查询参数
|
||||||
|
*
|
||||||
|
* @author 科技小王子
|
||||||
|
* @since 2024-01-15 18:52:24
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
@Schema(name = "VersionParam对象", description = "版本更新查询参数")
|
||||||
|
public class VersionParam extends BaseParam {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "ID")
|
||||||
|
@QueryField(type = QueryType.EQ)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@Schema(description = "版本名")
|
||||||
|
private String versionName;
|
||||||
|
|
||||||
|
@Schema(description = "版本号")
|
||||||
|
@QueryField(type = QueryType.EQ)
|
||||||
|
private Integer versionCode;
|
||||||
|
|
||||||
|
@Schema(description = "下载链接")
|
||||||
|
private String androidDownloadUrl;
|
||||||
|
|
||||||
|
@Schema(description = "下载链接")
|
||||||
|
private String iosDownloadUrl;
|
||||||
|
|
||||||
|
@Schema(description = "更新日志")
|
||||||
|
private String updateInfo;
|
||||||
|
|
||||||
|
@Schema(description = "强制更新")
|
||||||
|
@QueryField(type = QueryType.EQ)
|
||||||
|
private Integer isHard;
|
||||||
|
|
||||||
|
@Schema(description = "热更")
|
||||||
|
@QueryField(type = QueryType.EQ)
|
||||||
|
private Integer isHot;
|
||||||
|
|
||||||
|
@Schema(description = "备注")
|
||||||
|
private String comments;
|
||||||
|
|
||||||
|
@Schema(description = "文章排序(数字越小越靠前)")
|
||||||
|
@QueryField(type = QueryType.EQ)
|
||||||
|
private Integer sortNumber;
|
||||||
|
|
||||||
|
@QueryField(type = QueryType.EQ)
|
||||||
|
private Integer userId;
|
||||||
|
|
||||||
|
@Schema(description = "状态, 0正常, 1冻结")
|
||||||
|
@QueryField(type = QueryType.EQ)
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
@Schema(description = "是否删除, 0否, 1是")
|
||||||
|
@QueryField(type = QueryType.EQ)
|
||||||
|
private Integer deleted;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -646,6 +646,45 @@ public class BatchImportSupport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量失败时降级逐行:允许调用方自定义“成功条数”的计算口径(例如:仅统计 insert 入库条数)。
|
||||||
|
*
|
||||||
|
* <p>batchPersistCount / rowPersistCount 返回的是“需要累计的条数增量”,并不等同于“是否成功”。</p>
|
||||||
|
*/
|
||||||
|
public <T> int persistChunkWithFallbackCount(List<T> items,
|
||||||
|
List<Integer> excelRowNumbers,
|
||||||
|
Supplier<Integer> batchPersistCount,
|
||||||
|
BiFunction<T, Integer, Integer> rowPersistCount,
|
||||||
|
List<String> errorMessages) {
|
||||||
|
if (CollectionUtils.isEmpty(items)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return runInNewTx(batchPersistCount);
|
||||||
|
} catch (Exception batchException) {
|
||||||
|
int count = 0;
|
||||||
|
for (int i = 0; i < items.size(); i++) {
|
||||||
|
T item = items.get(i);
|
||||||
|
int excelRowNumber = (excelRowNumbers != null && i < excelRowNumbers.size()) ? excelRowNumbers.get(i) : -1;
|
||||||
|
try {
|
||||||
|
Integer delta = runInNewTx(() -> rowPersistCount.apply(item, excelRowNumber));
|
||||||
|
if (delta != null && delta > 0) {
|
||||||
|
count += delta;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (errorMessages != null) {
|
||||||
|
if (excelRowNumber > 0) {
|
||||||
|
errorMessages.add("第" + excelRowNumber + "行:" + e.getMessage());
|
||||||
|
} else {
|
||||||
|
errorMessages.add(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static String normalize(String value) {
|
private static String normalize(String value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -26,8 +26,10 @@ import javax.annotation.Resource;
|
|||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 企业控制器
|
* 企业控制器
|
||||||
@@ -141,7 +143,7 @@ public class CreditCompanyController extends BaseController {
|
|||||||
public ApiResult<List<String>> importBatch(@RequestParam("file") MultipartFile file,
|
public ApiResult<List<String>> importBatch(@RequestParam("file") MultipartFile file,
|
||||||
@RequestParam(value = "companyId", required = false) Integer companyId) {
|
@RequestParam(value = "companyId", required = false) Integer companyId) {
|
||||||
List<String> errorMessages = new ArrayList<>();
|
List<String> errorMessages = new ArrayList<>();
|
||||||
int successCount = 0;
|
int insertedCount = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
List<CreditCompanyImportParam> list = null;
|
List<CreditCompanyImportParam> list = null;
|
||||||
@@ -217,10 +219,12 @@ public class CreditCompanyController extends BaseController {
|
|||||||
chunkItems.add(item);
|
chunkItems.add(item);
|
||||||
chunkRowNumbers.add(excelRowNumber);
|
chunkRowNumbers.add(excelRowNumber);
|
||||||
if (chunkItems.size() >= chunkSize) {
|
if (chunkItems.size() >= chunkSize) {
|
||||||
successCount += batchImportSupport.persistChunkWithFallback(
|
insertedCount += batchImportSupport.persistChunkWithFallbackCount(
|
||||||
chunkItems,
|
chunkItems,
|
||||||
chunkRowNumbers,
|
chunkRowNumbers,
|
||||||
() -> batchImportSupport.upsertBySingleKey(
|
() -> {
|
||||||
|
int delta = countInsertedByMatchName(chunkItems);
|
||||||
|
batchImportSupport.upsertBySingleKey(
|
||||||
creditCompanyService,
|
creditCompanyService,
|
||||||
chunkItems,
|
chunkItems,
|
||||||
CreditCompany::getId,
|
CreditCompany::getId,
|
||||||
@@ -229,22 +233,22 @@ public class CreditCompanyController extends BaseController {
|
|||||||
CreditCompany::getMatchName,
|
CreditCompany::getMatchName,
|
||||||
null,
|
null,
|
||||||
mpBatchSize
|
mpBatchSize
|
||||||
),
|
);
|
||||||
|
return delta;
|
||||||
|
},
|
||||||
(rowItem, rowNumber) -> {
|
(rowItem, rowNumber) -> {
|
||||||
boolean saved = creditCompanyService.save(rowItem);
|
boolean saved = creditCompanyService.save(rowItem);
|
||||||
if (!saved) {
|
if (saved) {
|
||||||
|
return 1; // insert 入库
|
||||||
|
}
|
||||||
CreditCompany existing = creditCompanyService.getByMatchName(rowItem.getMatchName());
|
CreditCompany existing = creditCompanyService.getByMatchName(rowItem.getMatchName());
|
||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
rowItem.setId(existing.getId());
|
rowItem.setId(existing.getId());
|
||||||
if (creditCompanyService.updateById(rowItem)) {
|
if (creditCompanyService.updateById(rowItem)) {
|
||||||
return true;
|
return 0; // update 不计入“入库”条数
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
throw new RuntimeException("保存失败");
|
||||||
return true;
|
|
||||||
}
|
|
||||||
errorMessages.add("第" + rowNumber + "行:保存失败");
|
|
||||||
return false;
|
|
||||||
},
|
},
|
||||||
errorMessages
|
errorMessages
|
||||||
);
|
);
|
||||||
@@ -259,10 +263,12 @@ public class CreditCompanyController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!chunkItems.isEmpty()) {
|
if (!chunkItems.isEmpty()) {
|
||||||
successCount += batchImportSupport.persistChunkWithFallback(
|
insertedCount += batchImportSupport.persistChunkWithFallbackCount(
|
||||||
chunkItems,
|
chunkItems,
|
||||||
chunkRowNumbers,
|
chunkRowNumbers,
|
||||||
() -> batchImportSupport.upsertBySingleKey(
|
() -> {
|
||||||
|
int delta = countInsertedByMatchName(chunkItems);
|
||||||
|
batchImportSupport.upsertBySingleKey(
|
||||||
creditCompanyService,
|
creditCompanyService,
|
||||||
chunkItems,
|
chunkItems,
|
||||||
CreditCompany::getId,
|
CreditCompany::getId,
|
||||||
@@ -271,31 +277,31 @@ public class CreditCompanyController extends BaseController {
|
|||||||
CreditCompany::getMatchName,
|
CreditCompany::getMatchName,
|
||||||
null,
|
null,
|
||||||
mpBatchSize
|
mpBatchSize
|
||||||
),
|
);
|
||||||
|
return delta;
|
||||||
|
},
|
||||||
(rowItem, rowNumber) -> {
|
(rowItem, rowNumber) -> {
|
||||||
boolean saved = creditCompanyService.save(rowItem);
|
boolean saved = creditCompanyService.save(rowItem);
|
||||||
if (!saved) {
|
if (saved) {
|
||||||
|
return 1; // insert 入库
|
||||||
|
}
|
||||||
CreditCompany existing = creditCompanyService.getByMatchName(rowItem.getMatchName());
|
CreditCompany existing = creditCompanyService.getByMatchName(rowItem.getMatchName());
|
||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
rowItem.setId(existing.getId());
|
rowItem.setId(existing.getId());
|
||||||
if (creditCompanyService.updateById(rowItem)) {
|
if (creditCompanyService.updateById(rowItem)) {
|
||||||
return true;
|
return 0; // update 不计入“入库”条数
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
throw new RuntimeException("保存失败");
|
||||||
return true;
|
|
||||||
}
|
|
||||||
errorMessages.add("第" + rowNumber + "行:保存失败");
|
|
||||||
return false;
|
|
||||||
},
|
},
|
||||||
errorMessages
|
errorMessages
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorMessages.isEmpty()) {
|
if (errorMessages.isEmpty()) {
|
||||||
return success("成功导入" + successCount + "条数据", null);
|
return success("成功入库" + insertedCount + "条数据", null);
|
||||||
} else {
|
} else {
|
||||||
return success("导入完成,成功" + successCount + "条,失败" + errorMessages.size() + "条", errorMessages);
|
return success("导入完成,入库" + insertedCount + "条,失败" + errorMessages.size() + "条", errorMessages);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -405,6 +411,53 @@ public class CreditCompanyController extends BaseController {
|
|||||||
return value == null || value.trim().isEmpty();
|
return value == null || value.trim().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 入库条数以 insert 为准:matchName 在数据库中不存在时计 1,否则计 0。
|
||||||
|
*
|
||||||
|
* <p>统计口径需与批量 upsert 的匹配字段保持一致(matchName)。</p>
|
||||||
|
*/
|
||||||
|
private int countInsertedByMatchName(List<CreditCompany> items) {
|
||||||
|
if (CollectionUtils.isEmpty(items)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
List<String> matchNames = new ArrayList<>(items.size());
|
||||||
|
for (CreditCompany item : items) {
|
||||||
|
if (item == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String key = item.getMatchName();
|
||||||
|
if (!isBlank(key)) {
|
||||||
|
matchNames.add(key.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matchNames.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> existing = new HashSet<>();
|
||||||
|
List<CreditCompany> dbRows = creditCompanyService.lambdaQuery()
|
||||||
|
.select(CreditCompany::getMatchName)
|
||||||
|
.in(CreditCompany::getMatchName, matchNames)
|
||||||
|
.list();
|
||||||
|
if (!CollectionUtils.isEmpty(dbRows)) {
|
||||||
|
for (CreditCompany row : dbRows) {
|
||||||
|
String v = row != null ? row.getMatchName() : null;
|
||||||
|
if (!isBlank(v)) {
|
||||||
|
existing.add(v.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
for (CreditCompany item : items) {
|
||||||
|
String key = item != null ? item.getMatchName() : null;
|
||||||
|
if (!isBlank(key) && !existing.contains(key.trim())) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将CreditCompanyImportParam转换为CreditCompany实体
|
* 将CreditCompanyImportParam转换为CreditCompany实体
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user