diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppArticleCategoryController.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppArticleCategoryController.java index ec17bd560..aa094ec52 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppArticleCategoryController.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppArticleCategoryController.java @@ -51,6 +51,7 @@ public class AppArticleCategoryController { LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); wrapper.like(StrUtil.isNotBlank(appArticleCategory.getName()), AppArticleCategoryEntity::getName, appArticleCategory.getName()); + wrapper.orderByAsc(AppArticleCategoryEntity::getSort, AppArticleCategoryEntity::getId); return R.ok(appArticleCategoryService.page(page, wrapper)); } @@ -74,7 +75,8 @@ public class AppArticleCategoryController { @Inner(value = false) @GetMapping("/list") public R list() { - return R.ok(appArticleCategoryService.list()); + return R.ok(appArticleCategoryService.list(Wrappers.lambdaQuery() + .orderByAsc(AppArticleCategoryEntity::getSort, AppArticleCategoryEntity::getId))); } /** @@ -103,6 +105,24 @@ public class AppArticleCategoryController { return R.ok(appArticleCategoryService.updateById(appArticleCategory)); } + /** + * 按传入 ID 顺序调整文章分类,保留未选中记录在排序序列中的位置。 + *

+ * 以当前租户内未删除记录的排序值、主键升序为基准,仅重排选中记录占据的位置, 再将全部记录的排序值从 1 起连续重编号。 + * @param ids 按目标顺序排列的文章分类 ID;不可为 null 或空,不可包含 null、重复或不可用的 ID + * @return 排序保存成功的响应;校验或更新失败时抛出异常,由统一异常处理器转换响应 + * @throws IllegalArgumentException ID 列表为空、包含 null 或重复值,或指定记录已删除、不属于当前租户 或不存在 + * @throws IllegalStateException 批量更新返回失败 + */ + @Operation(summary = "拖拽排序", description = "按当前列表顺序保存排序") + @SysLog("文章分类排序") + @PutMapping("/sort") + @HasPermission("app_appArticleCategory_edit") + public R sort(@RequestBody List ids) { + appArticleCategoryService.sort(ids); + return R.ok(); + } + /** * 通过id删除文章分类表 * @param ids id列表 diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppArticleController.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppArticleController.java index 5251bdbbf..b56638268 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppArticleController.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/controller/AppArticleController.java @@ -36,13 +36,12 @@ public class AppArticleController { private final AppArticleService appArticleService; /** - * 分页查询文章资讯。 - *

- * 返回文章列表时会关联分类名称,供 App 资讯列表和后台管理复用。 - * + * 分页查询文章资讯。 + *

+ * 返回文章列表时会关联分类名称,供 App 资讯列表和后台管理复用。 * @param page 分页对象 * @param appArticle 文章资讯 - * @return 文章分页结果 + * @return 文章分页结果 */ @Operation(summary = "分页查询", description = "分页查询") @Inner(value = false) @@ -52,13 +51,12 @@ public class AppArticleController { } /** - * 查询文章详情。 - *

- * 会递增访问次数;如果传入 userId,则额外返回该用户是否收藏当前文章。 - * - * @param id 文章 ID - * @param userId 用户 ID,可为空 - * @return 文章详情 + * 查询文章详情。 + *

+ * 会递增访问次数;如果传入 userId,则额外返回该用户是否收藏当前文章。 + * @param id 文章 ID + * @param userId 用户 ID,可为空 + * @return 文章详情 */ @Inner(value = false) @Operation(summary = "通过id查询", description = "通过id查询") @@ -93,6 +91,24 @@ public class AppArticleController { return R.ok(appArticleService.updateById(appArticle)); } + /** + * 按传入 ID 顺序调整文章,保留未选中记录在排序序列中的位置。 + *

+ * 以当前租户内未删除记录的排序值、主键升序为基准,仅重排选中记录占据的位置, 再将全部记录的排序值从 1 起连续重编号。 + * @param ids 按目标顺序排列的文章 ID;不可为 null 或空,不可包含 null、重复或不可用的 ID + * @return 排序保存成功的响应;校验或更新失败时抛出异常,由统一异常处理器转换响应 + * @throws IllegalArgumentException ID 列表为空、包含 null 或重复值,或指定记录已删除、不属于当前租户 或不存在 + * @throws IllegalStateException 批量更新返回失败 + */ + @Operation(summary = "拖拽排序", description = "按当前列表顺序保存排序") + @SysLog("文章资讯排序") + @PutMapping("/sort") + @HasPermission("app_appArticle_edit") + public R sort(@RequestBody List ids) { + appArticleService.sort(ids); + return R.ok(); + } + /** * 通过id删除文章资讯 * @param ids id列表 diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppArticleCategoryService.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppArticleCategoryService.java index b2e9c79e5..d140da78c 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppArticleCategoryService.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppArticleCategoryService.java @@ -3,9 +3,21 @@ package com.pig4cloud.pigx.app.service; import com.baomidou.mybatisplus.extension.service.IService; import com.pig4cloud.pigx.app.api.entity.AppArticleCategoryEntity; +import java.util.List; + /** * 文章分类基础服务。 */ public interface AppArticleCategoryService extends IService { + /** + * 按传入 ID 顺序调整文章分类,保留未选中记录在排序序列中的位置。 + *

+ * 以当前租户内未删除记录的排序值、主键升序为基准,仅重排选中记录占据的位置, 再将全部记录的排序值从 1 起连续重编号。 + * @param ids 按目标顺序排列的文章分类 ID;不可为 null 或空,不可包含 null、重复或不可用的 ID + * @throws IllegalArgumentException ID 列表为空、包含 null 或重复值,或指定记录已删除、不属于当前租户 或不存在 + * @throws IllegalStateException 批量更新返回失败 + */ + void sort(List ids); + } diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppArticleService.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppArticleService.java index 0786252b7..2cb21657e 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppArticleService.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/AppArticleService.java @@ -4,8 +4,23 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import com.pig4cloud.pigx.app.api.entity.AppArticleEntity; +import java.util.List; + +/** + * 文章查询与排序服务,供管理端和 App 文章接口调用。 + */ public interface AppArticleService extends IService { + /** + * 按传入 ID 顺序调整文章,保留未选中记录在排序序列中的位置。 + *

+ * 以当前租户内未删除记录的排序值、主键升序为基准,仅重排选中记录占据的位置, 再将全部记录的排序值从 1 起连续重编号。 + * @param ids 按目标顺序排列的文章 ID;不可为 null 或空,不可包含 null、重复或不可用的 ID + * @throws IllegalArgumentException ID 列表为空、包含 null 或重复值,或指定记录已删除、不属于当前租户 或不存在 + * @throws IllegalStateException 批量更新返回失败 + */ + void sort(List ids); + /** * 获取文章并使阅读数+1 * @param id id diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppArticleCategoryServiceImpl.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppArticleCategoryServiceImpl.java index e0ffea62f..e0f8665d2 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppArticleCategoryServiceImpl.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppArticleCategoryServiceImpl.java @@ -1,10 +1,18 @@ package com.pig4cloud.pigx.app.service.impl; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.pig4cloud.pigx.app.api.entity.AppArticleCategoryEntity; import com.pig4cloud.pigx.app.mapper.AppArticleCategoryMapper; import com.pig4cloud.pigx.app.service.AppArticleCategoryService; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; /** * 文章分类表 @@ -16,4 +24,37 @@ import org.springframework.stereotype.Service; public class AppArticleCategoryServiceImpl extends ServiceImpl implements AppArticleCategoryService { + /** + * 按传入 ID 顺序调整文章分类,保留未选中记录在排序序列中的位置。 + *

+ * 以当前租户内未删除记录的排序值、主键升序为基准,仅重排选中记录占据的位置, 再将全部记录的排序值从 1 起连续重编号。 + * @param ids 按目标顺序排列的文章分类 ID;不可为 null 或空,不可包含 null、重复或不可用的 ID + * @throws IllegalArgumentException ID 列表为空、包含 null 或重复值,或指定记录已删除、不属于当前租户 或不存在 + * @throws IllegalStateException 批量更新返回失败 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void sort(List ids) { + Assert.notEmpty(ids, "排序记录不能为空"); + Set selected = new HashSet<>(ids); + Assert.isTrue(!selected.contains(null) && selected.size() == ids.size(), "排序记录无效"); + // 锁定统一顺序,避免重复排序值和分页边界导致拖动后顺序丢失。 + List records = list(Wrappers.lambdaQuery() + .orderByAsc(AppArticleCategoryEntity::getSort, AppArticleCategoryEntity::getId) + .last("FOR UPDATE")); + long matched = records.stream().filter(row -> selected.contains(row.getId())).count(); + Assert.isTrue(matched == ids.size(), "记录已变更,请刷新后重试"); + List updates = new ArrayList<>(); + int next = 0; + for (int i = 0; i < records.size(); i++) { + AppArticleCategoryEntity current = records.get(i); + Long id = selected.contains(current.getId()) ? ids.get(next++) : current.getId(); + AppArticleCategoryEntity update = new AppArticleCategoryEntity(); + update.setId(id); + update.setSort(i + 1); + updates.add(update); + } + Assert.state(updateBatchById(updates), "排序保存失败"); + } + } diff --git a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppArticleServiceImpl.java b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppArticleServiceImpl.java index fb5fa67f5..396c72e78 100644 --- a/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppArticleServiceImpl.java +++ b/pigx-app-server/pigx-app-server-biz/src/main/java/com/pig4cloud/pigx/app/service/impl/AppArticleServiceImpl.java @@ -13,6 +13,13 @@ import com.pig4cloud.pigx.app.mapper.AppArticleMapper; import com.pig4cloud.pigx.app.service.AppArticleService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.Objects; @@ -32,19 +39,17 @@ public class AppArticleServiceImpl extends ServiceImpl - * 如果传入用户 ID,会额外查询该用户是否已收藏当前文章。 - * 阅读数更新使用当前 visit 值作为条件,降低并发覆盖风险。 - * - * @param id 文章 ID - * @param userId 当前用户 ID,可为空 - * @return 文章详情 + * 获取文章详情,并尝试将阅读数加 1。 + *

+ * 如果传入用户 ID,会额外查询该用户是否已收藏当前文章。 阅读数更新使用当前 visit 值作为条件,降低并发覆盖风险。 + * @param id 文章 ID + * @param userId 当前用户 ID,可为空 + * @return 文章详情 */ @Override public AppArticleEntity getArticleAndIncrById(Long id, Long userId) { AppArticleEntity appArticleEntity = baseMapper.selectById(id); - // 登录用户访问详情时,补充当前用户是否已收藏该文章。 + // 登录用户访问详情时,补充当前用户是否已收藏该文章。 if (Objects.nonNull(userId)) { boolean exists = collectMapper.exists(Wrappers.lambdaQuery()// .eq(AppArticleCollectEntity::getArticleId, appArticleEntity.getId())// @@ -54,7 +59,7 @@ public class AppArticleServiceImpl extends ServiceImpllambdaQuery() .eq(AppArticleEntity::getId, id) @@ -63,11 +68,10 @@ public class AppArticleServiceImpl extends ServiceImpl + * 以当前租户内未删除记录的排序值、主键升序为基准,仅重排选中记录占据的位置, 再将全部记录的排序值从 1 起连续重编号。 + * @param ids 按目标顺序排列的文章 ID;不可为 null 或空,不可包含 null、重复或不可用的 ID + * @throws IllegalArgumentException ID 列表为空、包含 null 或重复值,或指定记录已删除、不属于当前租户 或不存在 + * @throws IllegalStateException 批量更新返回失败 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void sort(List ids) { + Assert.notEmpty(ids, "排序记录不能为空"); + Set selected = new HashSet<>(ids); + Assert.isTrue(!selected.contains(null) && selected.size() == ids.size(), "排序记录无效"); + // 锁定统一顺序,避免重复排序值和分页边界导致拖动后顺序丢失。 + List records = list(Wrappers.lambdaQuery() + .orderByAsc(AppArticleEntity::getSort, AppArticleEntity::getId) + .last("FOR UPDATE")); + long matched = records.stream().filter(row -> selected.contains(row.getId())).count(); + Assert.isTrue(matched == ids.size(), "记录已变更,请刷新后重试"); + List updates = new ArrayList<>(); + int next = 0; + for (int i = 0; i < records.size(); i++) { + AppArticleEntity current = records.get(i); + Long id = selected.contains(current.getId()) ? ids.get(next++) : current.getId(); + AppArticleEntity update = new AppArticleEntity(); + update.setId(id); + update.setSort(i + 1); + updates.add(update); + } + Assert.state(updateBatchById(updates), "排序保存失败"); + } + } diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysPostController.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysPostController.java index 985e16b98..2f93683f8 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysPostController.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/controller/SysPostController.java @@ -61,7 +61,8 @@ public class SysPostController { */ @GetMapping("/list") public R> listPosts() { - return R.ok(sysPostService.list(Wrappers.emptyWrapper())); + return R.ok(sysPostService + .list(Wrappers.lambdaQuery().orderByAsc(SysPost::getPostSort, SysPost::getPostId))); } /** @@ -74,8 +75,10 @@ public class SysPostController { @GetMapping("/page") @HasPermission("sys_post_view") public R getSysPostPage(@ParameterObject Page page, @ParameterObject SysPost sysPost) { - return R.ok(sysPostService.page(page, Wrappers.lambdaQuery() - .like(StrUtil.isNotBlank(sysPost.getPostName()), SysPost::getPostName, sysPost.getPostName()))); + return R.ok(sysPostService.page(page, + Wrappers.lambdaQuery() + .like(StrUtil.isNotBlank(sysPost.getPostName()), SysPost::getPostName, sysPost.getPostName()) + .orderByAsc(SysPost::getPostSort, SysPost::getPostId))); } /** @@ -128,6 +131,24 @@ public class SysPostController { return R.ok(sysPostService.updateById(sysPost)); } + /** + * 按传入 ID 顺序调整岗位,保留未选中记录在排序序列中的位置。 + *

+ * 以当前租户内未删除记录的排序值、主键升序为基准,仅重排选中记录占据的位置, 再将全部记录的排序值从 1 起连续重编号。 + * @param ids 按目标顺序排列的岗位 ID;不可为 null 或空,不可包含 null、重复或不可用的 ID + * @return 排序保存成功的响应;校验或更新失败时抛出异常,由统一异常处理器转换响应 + * @throws IllegalArgumentException ID 列表为空、包含 null 或重复值,或指定记录已删除、不属于当前租户 或不存在 + * @throws IllegalStateException 批量更新返回失败 + */ + @Operation(summary = "岗位拖拽排序", description = "按当前列表顺序保存岗位排序") + @SysLog("岗位排序") + @PutMapping("/sort") + @HasPermission("sys_post_edit") + public R sort(@RequestBody List ids) { + sysPostService.sort(ids); + return R.ok(); + } + /** * 通过id删除岗位信息表 * @param ids id 列表 diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysPostService.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysPostService.java index 9aae9fb15..397d9bcf4 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysPostService.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/SysPostService.java @@ -33,6 +33,16 @@ import java.util.List; */ public interface SysPostService extends IService { + /** + * 按传入 ID 顺序调整岗位,保留未选中记录在排序序列中的位置。 + *

+ * 以当前租户内未删除记录的排序值、主键升序为基准,仅重排选中记录占据的位置, 再将全部记录的排序值从 1 起连续重编号。 + * @param ids 按目标顺序排列的岗位 ID;不可为 null 或空,不可包含 null、重复或不可用的 ID + * @throws IllegalArgumentException ID 列表为空、包含 null 或重复值,或指定记录已删除、不属于当前租户 或不存在 + * @throws IllegalStateException 批量更新返回失败 + */ + void sort(List ids); + /** * 导出excel 表格 * @return @@ -42,8 +52,8 @@ public interface SysPostService extends IService { /** * 导入岗位 * @param excelVOList 岗位列表 - * @param bindingResult 错误信息列表 - * @return ok fail + * @param bindingResult 通用校验结果,其 target 持有错误信息列表 + * @return 全部导入成功返回 ok;存在校验失败时返回携带错误信息列表的 failed */ R importPost(List excelVOList, BindingResult bindingResult); diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysPostServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysPostServiceImpl.java index 9038d487f..dec480dc1 100644 --- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysPostServiceImpl.java +++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysPostServiceImpl.java @@ -30,8 +30,11 @@ import com.pig4cloud.pigx.common.core.util.MsgUtils; import com.pig4cloud.pigx.common.core.util.R; import com.pig4cloud.pigx.common.excel.vo.ErrorMessage; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; import org.springframework.validation.BindingResult; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -45,72 +48,104 @@ import java.util.Set; @Service public class SysPostServiceImpl extends ServiceImpl implements SysPostService { - /** - * 导入岗位 - * - * @param excelVOList 岗位列表 - * @param bindingResult 错误信息列表 - * @return ok fail - */ - @Override - public R importPost(List excelVOList, BindingResult bindingResult) { - // 通用校验获取失败的数据 - List errorMessageList = (List) bindingResult.getTarget(); + /** + * 导入岗位 + * @param excelVOList 岗位列表 + * @param bindingResult 通用校验结果,其 target 持有错误信息列表 + * @return 全部导入成功返回 ok;存在校验失败时返回携带错误信息列表的 failed + */ + @Override + public R importPost(List excelVOList, BindingResult bindingResult) { + // 通用校验获取失败的数据 + List errorMessageList = (List) bindingResult.getTarget(); - // 个性化校验逻辑 - List postList = this.list(); + // 个性化校验逻辑 + List postList = this.list(); - // 执行数据插入操作 组装 PostDto - for (PostExcelVO excel : excelVOList) { - Set errorMsg = new HashSet<>(); - // 检验岗位名称或者岗位编码是否存在 - boolean existPost = postList.stream() - .anyMatch(post -> excel.getPostName().equals(post.getPostName()) - || excel.getPostCode().equals(post.getPostCode())); + // 执行数据插入操作 组装 PostDto + for (PostExcelVO excel : excelVOList) { + Set errorMsg = new HashSet<>(); + // 检验岗位名称或者岗位编码是否存在 + boolean existPost = postList.stream() + .anyMatch(post -> excel.getPostName().equals(post.getPostName()) + || excel.getPostCode().equals(post.getPostCode())); - if (existPost) { - errorMsg.add(MsgUtils.getMessage(UpmsErrorCodes.SYS_POST_NAMEORCODE_EXISTING, excel.getPostName(), - excel.getPostCode())); - } + if (existPost) { + errorMsg.add(MsgUtils.getMessage(UpmsErrorCodes.SYS_POST_NAMEORCODE_EXISTING, excel.getPostName(), + excel.getPostCode())); + } - // 数据合法情况 - if (CollUtil.isEmpty(errorMsg)) { - insertExcelPost(excel); - } else { - // 数据不合法 - errorMessageList.add(new ErrorMessage(excel.getLineNum(), errorMsg)); - } - } - if (CollUtil.isNotEmpty(errorMessageList)) { - return R.failed(errorMessageList); - } - return R.ok(); - } + // 数据合法情况 + if (CollUtil.isEmpty(errorMsg)) { + insertExcelPost(excel); + } + else { + // 数据不合法 + errorMessageList.add(new ErrorMessage(excel.getLineNum(), errorMsg)); + } + } + if (CollUtil.isNotEmpty(errorMessageList)) { + return R.failed(errorMessageList); + } + return R.ok(); + } - /** - * 导出excel 表格 - * - * @return - */ - @Override - public List listPost(SysPost query, Long[] ids) { - List postList = this - .list(Wrappers.lambdaQuery(query).in(ArrayUtil.isNotEmpty(ids), SysPost::getPostId, CollUtil.toList(ids))); - // 转换成execl 对象输出 - return postList.stream().map(post -> { - PostExcelVO postExcelVO = new PostExcelVO(); - BeanUtil.copyProperties(post, postExcelVO); - return postExcelVO; - }).toList(); - } + /** + * 导出excel 表格 + * @return + */ + @Override + public List listPost(SysPost query, Long[] ids) { + List postList = this + .list(Wrappers.lambdaQuery(query).in(ArrayUtil.isNotEmpty(ids), SysPost::getPostId, CollUtil.toList(ids))); + // 转换成execl 对象输出 + return postList.stream().map(post -> { + PostExcelVO postExcelVO = new PostExcelVO(); + BeanUtil.copyProperties(post, postExcelVO); + return postExcelVO; + }).toList(); + } - /** - * 插入excel Post - */ - private void insertExcelPost(PostExcelVO excel) { - SysPost sysPost = new SysPost(); - BeanUtil.copyProperties(excel, sysPost); - this.save(sysPost); - } + /** + * 插入excel Post + */ + private void insertExcelPost(PostExcelVO excel) { + SysPost sysPost = new SysPost(); + BeanUtil.copyProperties(excel, sysPost); + this.save(sysPost); + } + + /** + * 按传入 ID 顺序调整岗位,保留未选中记录在排序序列中的位置。 + *

+ * 以当前租户内未删除记录的排序值、主键升序为基准,仅重排选中记录占据的位置, 再将全部记录的排序值从 1 起连续重编号。 + * @param ids 按目标顺序排列的岗位 ID;不可为 null 或空,不可包含 null、重复或不可用的 ID + * @throws IllegalArgumentException ID 列表为空、包含 null 或重复值,或指定记录已删除、不属于当前租户 或不存在 + * @throws IllegalStateException 批量更新返回失败 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void sort(List ids) { + Assert.notEmpty(ids, "排序记录不能为空"); + Set selected = new HashSet<>(ids); + Assert.isTrue(!selected.contains(null) && selected.size() == ids.size(), "排序记录无效"); + // 锁定统一顺序,避免重复排序值和分页边界导致拖动后顺序丢失。 + List records = list(Wrappers.lambdaQuery() + .orderByAsc(SysPost::getPostSort, SysPost::getPostId) + .last("FOR UPDATE")); + long matched = records.stream().filter(row -> selected.contains(row.getPostId())).count(); + Assert.isTrue(matched == ids.size(), "记录已变更,请刷新后重试"); + List updates = new ArrayList<>(); + int next = 0; + for (int i = 0; i < records.size(); i++) { + SysPost current = records.get(i); + Long id = selected.contains(current.getPostId()) ? ids.get(next++) : current.getPostId(); + SysPost update = new SysPost(); + update.setPostId(id); + update.setPostSort(i + 1); + updates.add(update); + } + Assert.state(updateBatchById(updates), "排序保存失败"); + } }