diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/leave/BusLeaveManagerReviewReq.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/leave/BusLeaveManagerReviewReq.java index fc2ba345..f52d348a 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/leave/BusLeaveManagerReviewReq.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/dto/leave/BusLeaveManagerReviewReq.java @@ -1,5 +1,6 @@ package org.dromara.project.domain.dto.leave; +import com.alibaba.excel.annotation.ExcelProperty; import jakarta.validation.constraints.NotNull; import lombok.Data; @@ -26,12 +27,14 @@ public class BusLeaveManagerReviewReq implements Serializable { * 管理员意见(1未读 2同意 3拒绝) */ @NotNull(message = "管理员意见不能为空") - private String managerOpinion; + private String gangerOpinion; /** - * 管理员说明 + * 班组长说明 */ - private String managerExplain; + @ExcelProperty(value = "班组长说明") + private String gangerExplain; + /** * 备注 diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/vo/leave/BusLeaveVo.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/vo/leave/BusLeaveVo.java index ce46b6b8..5883af45 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/vo/leave/BusLeaveVo.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/domain/vo/leave/BusLeaveVo.java @@ -6,8 +6,11 @@ import io.github.linpeilie.annotations.AutoMapper; import lombok.Data; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; import org.dromara.project.domain.BusLeave; import org.dromara.project.domain.vo.reissuecard.AuditUserVo; +import org.springframework.transaction.annotation.Transactional; import java.io.Serial; import java.io.Serializable; @@ -65,6 +68,9 @@ public class BusLeaveVo implements Serializable { @ExcelDictFormat(dictType = "user_leave_type") private String leaveType; + @Translation(type = TransConstant.DICT_TYPE_TO_LABEL, mapper = "leaveType",other = "user_leave_type") + private String leaveTypeName; + /** * 请假开始时间 */ diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusAttendanceServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusAttendanceServiceImpl.java index f741558c..ce001c29 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusAttendanceServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusAttendanceServiceImpl.java @@ -760,6 +760,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl @Override @Transactional(rollbackFor = Exception.class) public Boolean managerReview(BusLeaveManagerReviewReq req) { - Long id = req.getId(); - String managerOpinion = req.getManagerOpinion(); - // 判断该请假记录是否存在 - BusLeave oldLeave = this.getById(id); - if (oldLeave == null) { - throw new ServiceException("施工人员请假申请不存在", HttpStatus.NOT_FOUND); + + BusLeave busLeave = baseMapper.selectById(req.getId()); + if (busLeave == null) { + throw new ServiceException("未找到该申请"); } - // 如果已经审核过,则返回 - if (!BusOpinionStatusEnum.UNREAD.getValue().equals(oldLeave.getManagerOpinion())) { - throw new ServiceException("该请假已审核,请勿重复操作", HttpStatus.BAD_REQUEST); + String gangerOpinion = req.getGangerOpinion(); + busLeave.setGangerOpinion(gangerOpinion); + busLeave.setGangerExplain(req.getGangerExplain()); + if(busLeave.getGangerId() == null){ + LoginUser loginUser = LoginHelper.getLoginUser(); + busLeave.setGangerName(loginUser.getNickname()); + busLeave.setGangerId(loginUser.getUserId()); } - // 判断班组长是否审核通过 - String gangerOpinion = oldLeave.getGangerOpinion(); - if (!BusOpinionStatusEnum.PASS.getValue().equals(gangerOpinion)) { - throw new ServiceException("请等待班组长审核通过后再进行操作", HttpStatus.BAD_REQUEST); + + busLeave.setGangerTime(LocalDateTime.now()); + + int i = baseMapper.updateById(busLeave); + + if(gangerOpinion.equals("2")){ + if(busLeave.getTimeType().equals("1")){ + LocalDateTime startTime = busLeave.getStartTime(); + LocalDateTime endTime = busLeave.getEndTime(); + LocalDate startDate = startTime.toLocalDate(); + LocalDate endDate = endTime.toLocalDate(); + + List list = attendanceService.list(Wrappers.lambdaQuery(BusAttendance.class) + .eq(BusAttendance::getProjectId, busLeave.getProjectId()) + .le(BusAttendance::getClockDate, endDate) + .ge(BusAttendance::getClockDate, startDate) + .eq(BusAttendance::getUserId, busLeave.getUserId()) + ); + for (BusAttendance attendance : list) { + attendance.setClockStatus(BusAttendanceClockStatusEnum.LATE.getValue()); + } + if(!list.isEmpty()){ + attendanceService.updateBatchById(list); + } + }else if(busLeave.getTimeType().equals("2")){ + if(busLeave.getPeriodType().equals("1")){ + LocalDateTime startTime = busLeave.getStartTime(); + LocalDate localDate = startTime.toLocalDate(); + List list = attendanceService.list(Wrappers.lambdaQuery(BusAttendance.class) + .eq(BusAttendance::getProjectId, busLeave.getProjectId()) + .eq(BusAttendance::getClockDate, localDate) + .eq(BusAttendance::getClockType, "1") + .eq(BusAttendance::getUserId, busLeave.getUserId()) + ); + for (BusAttendance attendance : list) { + attendance.setClockStatus(BusAttendanceClockStatusEnum.LATE.getValue()); + } + if(!list.isEmpty()){ + attendanceService.updateBatchById(list); + } + + }else if(busLeave.getPeriodType().equals("2")){ + LocalDateTime endTime = busLeave.getEndTime(); + LocalDate localDate = endTime.toLocalDate(); + List list = attendanceService.list(Wrappers.lambdaQuery(BusAttendance.class) + .eq(BusAttendance::getProjectId, busLeave.getProjectId()) + .eq(BusAttendance::getClockDate, localDate) + .eq(BusAttendance::getClockType, "2") + .eq(BusAttendance::getUserId, busLeave.getUserId()) + ); + for (BusAttendance attendance : list) { + attendance.setClockStatus(BusAttendanceClockStatusEnum.LATE.getValue()); + } + if(!list.isEmpty()){ + attendanceService.updateBatchById(list); + } + } + } } - // todo 判断当前用户是否为项目管理员 - // 判断是否为分包公司管理员 - Long contractorId = oldLeave.getContractorId(); - SubConstructionUser constructionUser = constructionUserService.lambdaQuery() - .eq(SubConstructionUser::getSysUserId, LoginHelper.getUserId()) - .eq(SubConstructionUser::getContractorId, contractorId) - .eq(SubConstructionUser::getUserRole, SubConstructionUserRoleEnum.ADMIN.getValue()) - .one(); - if (constructionUser == null) { - throw new ServiceException("您无权审核该请假申请", HttpStatus.FORBIDDEN); - } - // 填充默认值,更新数据 - BusLeave leave = new BusLeave(); - leave.setId(id); - leave.setManagerId(constructionUser.getId()); - leave.setManagerOpinion(managerOpinion); - leave.setManagerExplain(req.getManagerExplain()); - leave.setManagerTime(LocalDateTime.now()); - leave.setRemark(req.getRemark()); - boolean result = this.updateById(leave); - if (!result) { - throw new ServiceException("更新管理员审核操作失败", HttpStatus.ERROR); - } - // 更新考勤表记录 - LocalDateTime startTime = oldLeave.getStartTime(); - LocalDateTime endTime = oldLeave.getEndTime(); - // 计算相差的时间 -// long diffInMillis = endTime.getTime() - startTime.getTime(); -// long day = TimeUnit.MILLISECONDS.toDays(diffInMillis) + 1; -// Long userId = oldLeave.getUserId(); -// String userName = oldLeave.getUserName(); -// Long projectId = oldLeave.getProjectId(); - // 遍历每一天 - List attendanceList = new ArrayList<>(); -// for (long i = 0; i < day; i++) { -// BusAttendance attendance = new BusAttendance(); -// attendance.setUserId(userId); -// attendance.setUserName(userName); -// attendance.setProjectId(projectId); -// Date date = DateUtils.addDays(startTime, (int) i); -// attendance.setLeaveId(id); -// attendance.setClockDate(date); -// attendance.setClockStatus(BusAttendanceClockStatusEnum.LEAVE.getValue()); -// attendance.setCommuter(BusAttendanceCommuterEnum.ALLDAY.getValue()); -// attendanceList.add(attendance); -// } - boolean saveBatch = attendanceService.saveBatch(attendanceList); - if (!saveBatch) { - throw new ServiceException("更新考勤记录失败", HttpStatus.ERROR); - } - return true; + return i>0; } /** @@ -375,7 +383,7 @@ public class BusLeaveServiceImpl extends ServiceImpl // 精确查询 lqw.eq(ObjectUtils.isNotEmpty(id), BusLeave::getId, id); lqw.eq(ObjectUtils.isNotEmpty(userId), BusLeave::getUserId, userId); - lqw.eq(ObjectUtils.isNotEmpty(gangerId), BusLeave::getGangerId, gangerId); + lqw.eq(ObjectUtils.isNotEmpty(gangerId) && !Objects.equals(gangerId, SUPER_ADMIN_ID), BusLeave::getGangerId, gangerId); lqw.eq(StringUtils.isNotBlank(gangerOpinion), BusLeave::getGangerOpinion, gangerOpinion); lqw.eq(StringUtils.isNotBlank(managerOpinion), BusLeave::getManagerOpinion, managerOpinion); lqw.eq(ObjectUtils.isNotEmpty(projectId), BusLeave::getProjectId, projectId); @@ -418,6 +426,28 @@ public class BusLeaveServiceImpl extends ServiceImpl // 添加审核状态 String status = BusReviewStatusEnum.getEnumByOpinionStatus(leave.getGangerOpinion(), leave.getManagerOpinion()); leaveVo.setAuditStatus(status); + + //添加审核人 + if(StrUtil.isBlank(leaveVo.getGangerName())){ + String userType = leaveVo.getUserType(); + List sysUsers = new ArrayList<>(); + if(leaveVo.getGangerId()==null){ + if("1".equals(userType)){ + sysUsers = userService.selectUserByRoleIdAndProjectId(6L, leaveVo.getProjectId()); + } else if ("2".equals(userType)) { + sysUsers = userService.selectUserByRoleIdAndProjectId(7L, leaveVo.getProjectId()); + } + }else { + SysUserVo sysUserVo = userService.selectUserById(leaveVo.getGangerId()); + if(sysUserVo != null){ + leaveVo.setGangerName(sysUserVo.getNickName()); + } + } + if(CollectionUtil.isNotEmpty(sysUsers)){ + String collect = sysUsers.stream().map(SysUser::getNickName).collect(Collectors.joining()); + leaveVo.setGangerName(collect); + } + } return leaveVo; }).toList(); leaveVoPage.setRecords(leaveVoList); @@ -462,6 +492,12 @@ public class BusLeaveServiceImpl extends ServiceImpl // 5. 保存新记录(需补充完整字段赋值) leave.setUserTime(LocalDateTime.now()); + //插入班组 + SubConstructionUser bySysUserId = constructionUserService.getBySysUserId(userId); + if (bySysUserId != null) { + leave.setTeamId(bySysUserId.getTeamId()); + leave.setContractorId(bySysUserId.getContractorId()); + } this.save(leave); return leave.getId(); } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusReissueCardServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusReissueCardServiceImpl.java index ba76e67e..74040f47 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusReissueCardServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusReissueCardServiceImpl.java @@ -359,6 +359,28 @@ public class BusReissueCardServiceImpl extends ServiceImpl sysUsers = new ArrayList<>(); + if(reissueCardVo.getGangerId()==null){ + if("1".equals(userType)){ + sysUsers = userService.selectUserByRoleIdAndProjectId(6L, reissueCardVo.getProjectId()); + } else if ("2".equals(userType)) { + sysUsers = userService.selectUserByRoleIdAndProjectId(7L, reissueCardVo.getProjectId()); + } + }else { + SysUserVo sysUserVo = userService.selectUserById(reissueCardVo.getGangerId()); + if(sysUserVo != null){ + reissueCardVo.setGangerName(sysUserVo.getNickName()); + } + } + if(CollectionUtil.isNotEmpty(sysUsers)){ + String collect = sysUsers.stream().map(SysUser::getNickName).collect(Collectors.joining()); + reissueCardVo.setGangerName(collect); + } + } return reissueCardVo; }).toList(); reissueCardVoPage.setRecords(reissueCardVoList); diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/controller/HseFileFolderController.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/controller/HseFileFolderController.java new file mode 100644 index 00000000..f1ca736c --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/controller/HseFileFolderController.java @@ -0,0 +1,140 @@ +package org.dromara.safety.controller; + +import java.util.List; +import java.util.stream.Collectors; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.dromara.safety.domain.HseFileFolder; +import org.dromara.safety.domain.dto.fileFolder.FileFolderCreateDTO; +import org.dromara.safety.domain.dto.fileFolder.FileFolderMoveDTO; +import org.dromara.safety.domain.dto.fileFolder.ListQueryDto; +import org.dromara.safety.domain.vo.fileFolder.FileFolderTreeVO; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.safety.domain.vo.HseFileFolderVo; +import org.dromara.safety.domain.bo.HseFileFolderBo; +import org.dromara.safety.service.IHseFileFolderService; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +/** + * 会议纪要 + * + * @author Lion Li + * @date 2025-10-14 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/safety/fileFolder") +public class HseFileFolderController extends BaseController { + + private final IHseFileFolderService hseFileFolderService; + + /** + * 查询会议纪要列表 + */ + @GetMapping("/list-all") + public R> listAll(ListQueryDto dto) { + return R.ok(hseFileFolderService.listAll(dto)); + } + + /** + * 查询指定目录的树形结构(一次性加载所有层级) + * @return 树形结构列表 + */ + @GetMapping("/tree-all") + public R> treeAll(ListQueryDto dto) { + + // 1. 查询所有子项(利用path前缀匹配,一次性加载所有层级) + List allItems = hseFileFolderService.list(new LambdaQueryWrapper() + .like(HseFileFolder::getPath, "," + dto.getParentId() + ",") // 包含父ID的所有子项 + .eq(dto.getType()!=null,HseFileFolder::getType, dto.getType()) + .orderByDesc(HseFileFolder::getId) + .orderByAsc(HseFileFolder::getSort) + ); + + // 2. 构建树形结构 + List treeVOS = buildTree(allItems, dto.getParentId()); + return R.ok(treeVOS); + } + + /** + * 递归构建树形结构 + */ + private List buildTree(List allItems, Long parentId) { + return allItems.stream() + .filter(item -> parentId.equals(item.getParentId())) + .map(item -> { + FileFolderTreeVO vo = new FileFolderTreeVO(); + // 复制基本属性(可使用BeanUtils.copyProperties) + vo.setId(item.getId()); + vo.setName(item.getName()); + vo.setParentId(item.getParentId()); + vo.setType(item.getType()); + vo.setLevel(item.getLevel()); + vo.setSort(item.getSort()); + vo.setFileSuffix(item.getFileSuffix()); + + // 递归查询子节点 + vo.setChildren(buildTree(allItems, item.getId())); + return vo; + }) + .collect(Collectors.toList()); + } + + /** + * 创建文件或文件夹 + */ + @PostMapping("/create") + public R create(@RequestBody FileFolderCreateDTO dto) { + return R.ok(hseFileFolderService.createFileOrFolder(dto)); + } + + /** + * 删除文件或文件夹(级联删除子项) + */ + @DeleteMapping("/{id}") + @Transactional + public R delete(@PathVariable Long id) { + return R.ok(hseFileFolderService.deleteFileOrFolder(id)); + } + + /** + * 移动文件或文件夹到指定目录 + */ + @PostMapping("/move") + @Transactional + public R move(@RequestBody FileFolderMoveDTO dto) { + return R.ok(hseFileFolderService.moveFileOrFolder(dto.getId(), dto.getTargetParentId())); + } + + + + + + + + + + + + + + + + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/HseFileFolder.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/HseFileFolder.java new file mode 100644 index 00000000..3e05ce9f --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/HseFileFolder.java @@ -0,0 +1,92 @@ +package org.dromara.safety.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 会议纪要对象 hse_file_folder + * + * @author Lion Li + * @date 2025-10-14 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("hse_file_folder") +public class HseFileFolder extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @TableId(value = "id") + private Long id; + + /** + * 项目id + */ + private Long projectId; + + /** + * 名称(文件名或文件夹名) + */ + private String name; + + /** + * 父级ID(0表示根目录) + */ + private Long parentId; + + /** + * 类型(1-文件夹,2-文件) + */ + private Integer type; + + /** + * 层级(根目录为1,子级+1) + */ + private Integer level; + + /** + * 同层级排序号 + */ + private Integer sort; + + /** + * 层级路径(如:1,2,3 表示ID为1→2→3的层级) + */ + private String path; + + /** + * 文件id + */ + private Long fileId; + + /** + * 文件后缀 + */ + private String fileSuffix; + + /** + * 存储路径 + */ + private String filePath; + + /** + * + */ + private String remark; + + /** + * 删除标志(0代表存在 1代表删除) + */ +// @TableLogic + private String delFlag; + + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/bo/HseFileFolderBo.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/bo/HseFileFolderBo.java new file mode 100644 index 00000000..f30eee92 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/bo/HseFileFolderBo.java @@ -0,0 +1,90 @@ +package org.dromara.safety.domain.bo; + +import org.dromara.safety.domain.HseFileFolder; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +/** + * 会议纪要业务对象 hse_file_folder + * + * @author Lion Li + * @date 2025-10-14 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = HseFileFolder.class, reverseConvertGenerate = false) +public class HseFileFolderBo extends BaseEntity { + + /** + * 主键ID + */ + @NotNull(message = "主键ID不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 项目id + */ + @NotNull(message = "项目id不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long projectId; + + /** + * 名称(文件名或文件夹名) + */ + @NotBlank(message = "名称(文件名或文件夹名)不能为空", groups = { AddGroup.class, EditGroup.class }) + private String name; + + /** + * 父级ID(0表示根目录) + */ + private Long parentId; + + /** + * 类型(1-文件夹,2-文件) + */ + @NotNull(message = "类型(1-文件夹,2-文件)不能为空", groups = { AddGroup.class, EditGroup.class }) + private Integer type; + + /** + * 层级(根目录为1,子级+1) + */ + @NotNull(message = "层级(根目录为1,子级+1)不能为空", groups = { AddGroup.class, EditGroup.class }) + private Integer level; + + /** + * 同层级排序号 + */ + private Integer sort; + + /** + * 层级路径(如:1,2,3 表示ID为1→2→3的层级) + */ + @NotBlank(message = "层级路径(如:1,2,3 表示ID为1→2→3的层级)不能为空", groups = { AddGroup.class, EditGroup.class }) + private String path; + + /** + * 文件id + */ + private Long fileId; + + /** + * 文件后缀 + */ + private String fileSuffix; + + /** + * 存储路径 + */ + private String filePath; + + /** + * + */ + private String remark; + + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/dto/fileFolder/FileFolderCreateDTO.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/dto/fileFolder/FileFolderCreateDTO.java new file mode 100644 index 00000000..ed883521 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/dto/fileFolder/FileFolderCreateDTO.java @@ -0,0 +1,51 @@ +package org.dromara.safety.domain.dto.fileFolder; + + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + + + +@Data +public class FileFolderCreateDTO { + + /** + * 名称 + */ + @NotBlank(message = "名称不能为空") + private String name; + + /** + * 父级ID(0表示根目录) + */ + private Long parentId = 0L; + + /** + * 文件类型 + */ + @NotNull(message = "类型不能为空") + private Integer type; + + /** + * 排序号 + */ + private Integer sort=0; + + /** + * 文件后缀 + */ + private String fileSuffix; + + /** + * 文件存储路径(仅文件有效) + */ + private String filePath; + + /** + * 备注 + */ + private String remark; + +} + diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/dto/fileFolder/FileFolderMoveDTO.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/dto/fileFolder/FileFolderMoveDTO.java new file mode 100644 index 00000000..2d50c157 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/dto/fileFolder/FileFolderMoveDTO.java @@ -0,0 +1,14 @@ +package org.dromara.safety.domain.dto.fileFolder; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Data +public class FileFolderMoveDTO { + + @NotNull(message = "文件/文件夹ID不能为空") + private Long id; + + @NotNull(message = "目标父目录ID不能为空") + private Long targetParentId; +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/dto/fileFolder/ListQueryDto.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/dto/fileFolder/ListQueryDto.java new file mode 100644 index 00000000..b0f9a631 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/dto/fileFolder/ListQueryDto.java @@ -0,0 +1,22 @@ +package org.dromara.safety.domain.dto.fileFolder; + + +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Data +public class ListQueryDto { + /** + * 父目录ID,默认0(根目录) + */ + @NotNull(message = "父目录ID不能为空") + private Long parentId; + + + /** + * 类型:1-文件夹,2-文件,null-全部 + */ + private Integer type; + + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/vo/HseFileFolderVo.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/vo/HseFileFolderVo.java new file mode 100644 index 00000000..b95f0eb5 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/vo/HseFileFolderVo.java @@ -0,0 +1,109 @@ +package org.dromara.safety.domain.vo; + +import org.dromara.safety.domain.HseFileFolder; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * 会议纪要视图对象 hse_file_folder + * + * @author Lion Li + * @date 2025-10-14 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = HseFileFolder.class) +public class HseFileFolderVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @ExcelProperty(value = "主键ID") + private Long id; + + /** + * 项目id + */ + @ExcelProperty(value = "项目id") + private Long projectId; + + /** + * 名称(文件名或文件夹名) + */ + @ExcelProperty(value = "名称", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "文=件名或文件夹名") + private String name; + + /** + * 父级ID(0表示根目录) + */ + @ExcelProperty(value = "父级ID", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "0=表示根目录") + private Long parentId; + + /** + * 类型(1-文件夹,2-文件) + */ + @ExcelProperty(value = "类型", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "1=-文件夹,2-文件") + private Integer type; + + /** + * 层级(根目录为1,子级+1) + */ + @ExcelProperty(value = "层级", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "根=目录为1,子级+1") + private Integer level; + + /** + * 同层级排序号 + */ + @ExcelProperty(value = "同层级排序号") + private Integer sort; + + /** + * 层级路径(如:1,2,3 表示ID为1→2→3的层级) + */ + @ExcelProperty(value = "层级路径", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "如=:1,2,3,表=示ID为1→2→3的层级") + private String path; + + /** + * 文件id + */ + @ExcelProperty(value = "文件id") + private Long fileId; + + /** + * 文件后缀 + */ + @ExcelProperty(value = "文件后缀") + private String fileSuffix; + + /** + * 存储路径 + */ + @ExcelProperty(value = "存储路径") + private String filePath; + + /** + * + */ + @ExcelProperty(value = "") + private String remark; + + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/vo/fileFolder/FileFolderTreeVO.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/vo/fileFolder/FileFolderTreeVO.java new file mode 100644 index 00000000..235798c0 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/domain/vo/fileFolder/FileFolderTreeVO.java @@ -0,0 +1,53 @@ +package org.dromara.safety.domain.vo.fileFolder; + +import lombok.Data; + +import java.util.List; + +@Data +public class FileFolderTreeVO { + + + private Long id; + + /** + * 名称(文件名或文件夹名) + */ + private String name; + + + /** + * 父级ID(0表示根目录) + */ + private Long parentId; + + /** + * 1-文件夹,2-文件 + */ + private Integer type; + + /** + * 层级(根目录为1,子级+1) + */ + private Integer level; + + /** + * 同层级排序号 + */ + private Integer sort; + + /** + * 文件id + */ + private Long fileId; + + /** + * 文件后缀 + */ + private String fileSuffix; + + /** + * 子级 + */ + private List children; +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/mapper/HseFileFolderMapper.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/mapper/HseFileFolderMapper.java new file mode 100644 index 00000000..110ddee5 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/mapper/HseFileFolderMapper.java @@ -0,0 +1,25 @@ +package org.dromara.safety.mapper; + + +import org.apache.ibatis.annotations.Param; +import org.dromara.safety.domain.HseFileFolder; +import org.dromara.safety.domain.vo.HseFileFolderVo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 会议纪要Mapper接口 + * + * @author Lion Li + * @date 2025-10-14 + */ +public interface HseFileFolderMapper extends BaseMapperPlus { + + /** + * 批量更新子项的路径和层级 + */ + void batchUpdateChildPaths( + @Param("oldPath") String oldPath, + @Param("newPath") String newPath, + @Param("levelDiff") int levelDiff, + @Param("parentId") Long parentId); +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/service/IHseFileFolderService.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/service/IHseFileFolderService.java new file mode 100644 index 00000000..178923bf --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/service/IHseFileFolderService.java @@ -0,0 +1,90 @@ +package org.dromara.safety.service; + +import org.dromara.safety.domain.dto.fileFolder.FileFolderCreateDTO; +import org.dromara.safety.domain.dto.fileFolder.ListQueryDto; +import org.dromara.safety.domain.vo.HseFileFolderVo; +import org.dromara.safety.domain.bo.HseFileFolderBo; +import org.dromara.safety.domain.HseFileFolder; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import com.baomidou.mybatisplus.extension.service.IService; +import java.util.Collection; +import java.util.List; + +/** + * 会议纪要Service接口 + * + * @author Lion Li + * @date 2025-10-14 + */ +public interface IHseFileFolderService extends IService{ + + /** + * 查询会议纪要 + * + * @param id 主键 + * @return 会议纪要 + */ + HseFileFolderVo queryById(Long id); + + /** + * 分页查询会议纪要列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 会议纪要分页列表 + */ + TableDataInfo queryPageList(HseFileFolderBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的会议纪要列表 + * + * @param bo 查询条件 + * @return 会议纪要列表 + */ + List queryList(HseFileFolderBo bo); + + /** + * 新增会议纪要 + * + * @param bo 会议纪要 + * @return 是否新增成功 + */ + Boolean insertByBo(HseFileFolderBo bo); + + /** + * 修改会议纪要 + * + * @param bo 会议纪要 + * @return 是否修改成功 + */ + Boolean updateByBo(HseFileFolderBo bo); + + /** + * 校验并批量删除会议纪要信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + + List listAll(ListQueryDto dto); + + /** + * 创建文件或文件夹 + */ + HseFileFolder createFileOrFolder(FileFolderCreateDTO dto); + + /** + * 删除文件或文件夹(级联删除子项) + */ + boolean deleteFileOrFolder(Long id); + + /** + * 移动文件或文件夹到指定目录 + */ + boolean moveFileOrFolder(Long id, Long targetParentId); +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/service/impl/HseFileFolderServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/service/impl/HseFileFolderServiceImpl.java new file mode 100644 index 00000000..5f3cb302 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/safety/service/impl/HseFileFolderServiceImpl.java @@ -0,0 +1,271 @@ +package org.dromara.safety.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import org.dromara.safety.domain.dto.fileFolder.FileFolderCreateDTO; +import org.dromara.safety.domain.dto.fileFolder.ListQueryDto; +import org.springframework.stereotype.Service; +import org.dromara.safety.domain.bo.HseFileFolderBo; +import org.dromara.safety.domain.vo.HseFileFolderVo; +import org.dromara.safety.domain.HseFileFolder; +import org.dromara.safety.mapper.HseFileFolderMapper; +import org.dromara.safety.service.IHseFileFolderService; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 会议纪要Service业务层处理 + * + * @author Lion Li + * @date 2025-10-14 + */ +@RequiredArgsConstructor +@Service +public class HseFileFolderServiceImpl extends ServiceImpl implements IHseFileFolderService { + + private final HseFileFolderMapper baseMapper; + + /** + * 查询会议纪要 + * + * @param id 主键 + * @return 会议纪要 + */ + @Override + public HseFileFolderVo queryById(Long id){ + return baseMapper.selectVoById(id); + } + + /** + * 分页查询会议纪要列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 会议纪要分页列表 + */ + @Override + public TableDataInfo queryPageList(HseFileFolderBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的会议纪要列表 + * + * @param bo 查询条件 + * @return 会议纪要列表 + */ + @Override + public List queryList(HseFileFolderBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(HseFileFolderBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HseFileFolder::getId); + lqw.orderByAsc(HseFileFolder::getSort); + lqw.eq(bo.getProjectId() != null, HseFileFolder::getProjectId, bo.getProjectId()); + lqw.like(StringUtils.isNotBlank(bo.getName()), HseFileFolder::getName, bo.getName()); + lqw.eq(bo.getParentId() != null, HseFileFolder::getParentId, bo.getParentId()); + lqw.eq(bo.getType() != null, HseFileFolder::getType, bo.getType()); + lqw.eq(bo.getLevel() != null, HseFileFolder::getLevel, bo.getLevel()); + lqw.eq(bo.getSort() != null, HseFileFolder::getSort, bo.getSort()); + lqw.eq(StringUtils.isNotBlank(bo.getPath()), HseFileFolder::getPath, bo.getPath()); + lqw.eq(bo.getFileId() != null, HseFileFolder::getFileId, bo.getFileId()); + lqw.eq(StringUtils.isNotBlank(bo.getFileSuffix()), HseFileFolder::getFileSuffix, bo.getFileSuffix()); + lqw.eq(StringUtils.isNotBlank(bo.getFilePath()), HseFileFolder::getFilePath, bo.getFilePath()); + return lqw; + } + + /** + * 新增会议纪要 + * + * @param bo 会议纪要 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(HseFileFolderBo bo) { + HseFileFolder add = MapstructUtils.convert(bo, HseFileFolder.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改会议纪要 + * + * @param bo 会议纪要 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(HseFileFolderBo bo) { + HseFileFolder update = MapstructUtils.convert(bo, HseFileFolder.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(HseFileFolder entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除会议纪要信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + @Override + public List listAll(ListQueryDto dto) { + + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.orderByDesc(HseFileFolder::getId); + lqw.orderByAsc(HseFileFolder::getSort); + + lqw.eq(HseFileFolder::getParentId, dto.getParentId()); + lqw.eq(dto.getType()!=null,HseFileFolder::getType, dto.getType()); + + return baseMapper.selectVoList(lqw); + } + + public HseFileFolder createFileOrFolder(FileFolderCreateDTO dto) { + // 1. 验证父目录是否存在 + if (dto.getParentId() != 0) { + HseFileFolder parent = getById(dto.getParentId()); + if (parent == null || parent.getType() != 1) { // 父级必须是文件夹 + throw new ServiceException("父目录不存在或不是文件夹"); + } + } + + // 2. 构建新文件/文件夹对象 + HseFileFolder entity = new HseFileFolder(); + entity.setName(dto.getName()); + entity.setParentId(dto.getParentId()); + entity.setType(dto.getType()); + entity.setSort(dto.getSort() != null ? dto.getSort() : 0); + entity.setRemark(dto.getRemark()); + + + // 3. 设置层级和路径 + if (dto.getParentId() == 0) { // 根目录 + entity.setLevel(1); + entity.setPath("," + entity.getId() + ","); // ID会在插入后自动生成,这里先占位 + } else { // 子目录/文件 + HseFileFolder parent = getById(dto.getParentId()); + entity.setLevel(parent.getLevel() + 1); + entity.setPath(parent.getPath() + entity.getId() + ","); // ID会在插入后更新 + } + + // 4. 处理文件特有属性 + if (dto.getType() == 2) { // 如果是文件 + entity.setFileSuffix(dto.getFileSuffix()); + entity.setFilePath(dto.getFilePath()); + } + + // 5. 保存并更新路径(因为ID是自增的,需要先保存再更新路径) + save(entity); + + // 6. 修正路径中的ID + if (dto.getParentId() == 0) { + entity.setPath("," + entity.getId() + ","); + } else { + HseFileFolder parent = getById(dto.getParentId()); + entity.setPath(parent.getPath() + entity.getId() + ","); + } + updateById(entity); + + return entity; + } + + @Override + @Transactional + public boolean deleteFileOrFolder(Long id) { + HseFileFolder entity = getById(id); + if (entity == null) { + throw new ServiceException("文件/文件夹不存在"); + } + + // 1. 删除自身及所有子项(通过path匹配) + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery() + .like(HseFileFolder::getPath, "," + id + ","); + + return remove(queryWrapper); + } + + @Override + @Transactional + public boolean moveFileOrFolder(Long id, Long targetParentId) { + // 1. 验证源文件和目标目录是否存在 + HseFileFolder source = getById(id); + if (source == null) { + throw new ServiceException("源文件/文件夹不存在"); + } + + HseFileFolder targetParent = getById(targetParentId); + if (targetParent == null || targetParent.getType() != 1) { + throw new ServiceException("目标目录不存在或不是文件夹"); + } + + // 2. 防止循环移动(不能移动到自身或子目录下) + if (source.getPath().contains("," + targetParentId + ",")) { + throw new ServiceException("不能将文件夹移动到其子目录下"); + } + + // 3. 获取原路径和新路径的前缀 + String oldPath = source.getPath(); + String oldParentPath = source.getParentId() == 0 ? ",0," : getById(source.getParentId()).getPath(); + String newParentPath = targetParent.getPath(); + + // 4. 计算新路径和新层级 + String newPath = newParentPath + id + ","; + int newLevel = targetParent.getLevel() + 1; + + // 5. 更新自身信息 + source.setParentId(targetParentId); + source.setLevel(newLevel); + source.setPath(newPath); + updateById(source); + + // 6. 更新所有子项的路径和层级 + if (source.getType() == 1) { // 如果是文件夹,需要更新其子项 + // 计算路径替换的前后缀 + String pathReplaceFrom = oldPath; + String pathReplaceTo = newPath; + int levelDiff = newLevel - source.getLevel(); // 层级变化量 + + // 批量更新子项 + baseMapper.batchUpdateChildPaths(pathReplaceFrom, pathReplaceTo, levelDiff, id); + } + + return true; + } +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/resources/mapper/safety/HseFileFolderMapper.xml b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/resources/mapper/safety/HseFileFolderMapper.xml new file mode 100644 index 00000000..2745bb26 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/resources/mapper/safety/HseFileFolderMapper.xml @@ -0,0 +1,18 @@ + + + + + + + UPDATE hse_file_folder + SET + path = REPLACE(path, #{oldPath}, #{newPath}), + level = level + #{levelDiff}, + update_time = NOW() + WHERE + path LIKE CONCAT(#{oldPath}, '%') + AND parent_id != #{parentId} + +