config(dev): 更新开发环境配置和文件上传功能
- 调整文件上传路径配置
- 修复OA环境文件链接前缀配置
- 在审批实例服务中添加报销相关数据删除逻辑
- 实现申请人姓名自动填充功能
- 添加文件下载预览路由的正则表达式支持
- 为企业新闻VO添加附件列表兼容别名
- 完善员工入职服务的异常处理
- 新增账户附件管理控制器
- 实现文件上传接口并返回完整文件信息
已添加2个文件
已修改8个文件
260 ■■■■ 文件已修改
src/main/java/com/ruoyi/account/controller/AccountFileController.java 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/controller/FileUploadController.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/vo/EnterpriseNewsVo.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/project/common/CommonController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/controller/StaffOnJobController.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/IStaffOnJobService.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/impl/StaffOnJobServiceImpl.java 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-dev-pro.yml 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-oa.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/account/controller/AccountFileController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,129 @@
package com.ruoyi.account.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.basic.dto.StorageBlobVO;
import com.ruoyi.basic.mapper.StorageAttachmentMapper;
import com.ruoyi.basic.mapper.StorageBlobMapper;
import com.ruoyi.basic.pojo.StorageAttachment;
import com.ruoyi.basic.pojo.StorageBlob;
import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.framework.web.domain.R;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@RestController
@RequiredArgsConstructor
@Tag(name = "账户附件管理")
@RequestMapping("/account/accountFile")
public class AccountFileController {
    private final StorageAttachmentMapper storageAttachmentMapper;
    private final StorageBlobMapper storageBlobMapper;
    private final FileUtil fileUtil;
    @GetMapping("/listPage")
    @Operation(summary = "分页查询附件列表")
    public R listPage(
            @RequestParam Long accountId,
            @RequestParam String accountType,
            @RequestParam(defaultValue = "1") Long current,
            @RequestParam(defaultValue = "10") Long size) {
        Page<StorageBlobVO> page = new Page<>(current, size);
        LambdaQueryWrapper<StorageAttachment> queryWrapper = new LambdaQueryWrapper<StorageAttachment>()
                .eq(StorageAttachment::getRecordType, accountType)
                .eq(StorageAttachment::getRecordId, accountId);
        Long total = storageAttachmentMapper.selectCount(queryWrapper);
        page.setTotal(total);
        if (total == 0) {
            page.setRecords(new ArrayList<>());
            return R.ok(page);
        }
        List<StorageAttachment> storageAttachments = storageAttachmentMapper.selectList(queryWrapper);
        Map<Long, Long> blobIdToAttachmentIdMap = storageAttachments.stream()
                .collect(Collectors.toMap(StorageAttachment::getStorageBlobId, StorageAttachment::getId));
        List<Long> storageBlobIds = storageAttachments.stream()
                .map(StorageAttachment::getStorageBlobId)
                .collect(Collectors.toList());
        List<StorageBlob> storageBlobs = storageBlobMapper.selectByIds(storageBlobIds);
        List<StorageBlobVO> storageBlobVOS = new ArrayList<>();
        for (StorageBlob storageBlob : storageBlobs) {
            StorageBlobVO storageBlobVO = new StorageBlobVO();
            BeanUtils.copyProperties(storageBlob, storageBlobVO);
            storageBlobVO.setPreviewURL(fileUtil.buildSignedPreviewUrl(storageBlobVO));
            storageBlobVO.setUrl(fileUtil.buildSignedPreviewUrl(storageBlobVO));
            storageBlobVO.setName(storageBlob.getOriginalFilename());
            storageBlobVO.setDownloadURL(fileUtil.buildSignedDownloadUrl(storageBlobVO));
            storageBlobVO.setStorageAttachmentId(blobIdToAttachmentIdMap.get(storageBlob.getId()));
            storageBlobVOS.add(storageBlobVO);
        }
        page.setRecords(storageBlobVOS);
        return R.ok(page);
    }
    @PostMapping("/add")
    @Operation(summary = "保存附件")
    public R add(@RequestBody Map<String, Object> params) {
        Long accountId = params.get("accountId") != null ? Long.valueOf(params.get("accountId").toString()) : null;
        String accountType = params.get("accountType") != null ? params.get("accountType").toString() : null;
        Long storageBlobId = null;
        if (params.get("id") != null) {
            storageBlobId = Long.valueOf(params.get("id").toString());
        } else if (params.get("url") != null) {
            String url = params.get("url").toString();
            int previewIdx = url.indexOf("/preview/");
            if (previewIdx >= 0) {
                String afterPreview = url.substring(previewIdx + "/preview/".length());
                int queryIdx = afterPreview.indexOf("?");
                String uidFilename = queryIdx >= 0 ? afterPreview.substring(0, queryIdx) : afterPreview;
                StorageBlob blob = storageBlobMapper.selectOne(new LambdaQueryWrapper<StorageBlob>()
                        .eq(StorageBlob::getUidFilename, uidFilename).last("limit 1"));
                if (blob != null) {
                    storageBlobId = blob.getId();
                }
            }
        }
        if (accountId == null || accountType == null || storageBlobId == null) {
            return R.fail("参数不完整");
        }
        StorageAttachment storageAttachment = new StorageAttachment();
        storageAttachment.setRecordType(accountType);
        storageAttachment.setRecordId(accountId);
        storageAttachment.setStorageBlobId(storageBlobId);
        storageAttachment.setDeleted(0L);
        storageAttachmentMapper.insert(storageAttachment);
        return R.ok();
    }
    @DeleteMapping("/del")
    @Operation(summary = "删除附件")
    public R del(@RequestBody List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return R.fail("参数不能为空");
        }
        fileUtil.deleteStorageAttachmentsByStorageAttachmentIds(ids);
        return R.ok();
    }
}
src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java
@@ -11,7 +11,9 @@
import com.ruoyi.approve.bean.vo.ApprovalInstanceVo;
import com.ruoyi.approve.mapper.ApprovalInstanceMapper;
import com.ruoyi.approve.mapper.ApprovalTemplateNodeApproverMapper;
import com.ruoyi.approve.mapper.FinReimbursementDetailMapper;
import com.ruoyi.approve.mapper.FinReimbursementMapper;
import com.ruoyi.approve.mapper.FinReimbursementTravelMapper;
import com.ruoyi.approve.pojo.*;
import com.ruoyi.approve.service.*;
import com.ruoyi.approve.utils.ApproveProcessConfigNodeUtils;
@@ -94,6 +96,8 @@
    private final EnterpriseNewsMapper enterpriseNewsMapper;
    private final EnterpriseNewsScopeDeptMapper enterpriseNewsScopeDeptMapper;
    private final ApprovalTemplateNodeApproverMapper approvalTemplateNodeApproverMapper;
    private final FinReimbursementDetailMapper finReimbursementDetailMapper;
    private final FinReimbursementTravelMapper finReimbursementTravelMapper;
    @Override
    public R listPage(Page<ApprovalInstanceVo> page, ApprovalInstanceDto approvalInstanceDto) {
@@ -144,6 +148,13 @@
        approvalInstanceDto.setInstanceNo(instanceNo);
        approvalInstanceDto.setStatus("PENDING");
        approvalInstanceDto.setCurrentLevel(1);
        // å¦‚果前端传了 applicantId ä½†æ²¡ä¼  applicantName,则从用户表查询姓名
        if (approvalInstanceDto.getApplicantId() != null && !StringUtils.hasText(approvalInstanceDto.getApplicantName())) {
            SysUser applicantUser = sysUserMapper.selectUserById(approvalInstanceDto.getApplicantId());
            if (applicantUser != null) {
                approvalInstanceDto.setApplicantName(applicantUser.getNickName());
            }
        }
        boolean saved = this.save(approvalInstanceDto);
        if (!saved) {
            return false;
@@ -184,6 +195,26 @@
        if (ids == null || ids.isEmpty()) {
            return false;
        }
        // åˆ é™¤å…³è”的业务数据(报销单等)
        List<Long> reimbursementIds = new ArrayList<>();
        List<ApprovalInstance> instances = approvalInstanceMapper.selectBatchIds(ids);
        for (ApprovalInstance instance : instances) {
            if (instance.getBusinessId() != null && instance.getBusinessType() != null) {
                Long businessType = instance.getBusinessType();
                if (TypeEnums.TRAVEL_REIMBURSEMENT_APPROVAL.getCode().equals(businessType)
                        || TypeEnums.EXPENSE_APPROVAL.getCode().equals(businessType)) {
                    reimbursementIds.add(instance.getBusinessId());
                }
            }
        }
        if (!reimbursementIds.isEmpty()) {
            fileUtil.deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum.FILE, RecordTypeEnum.FIN_REIMBURSEMENT, reimbursementIds);
            finReimbursementDetailMapper.delete(Wrappers.<FinReimbursementDetail>lambdaQuery().in(FinReimbursementDetail::getReimbursementId, reimbursementIds));
            finReimbursementTravelMapper.delete(Wrappers.<FinReimbursementTravel>lambdaQuery().in(FinReimbursementTravel::getReimbursementId, reimbursementIds));
            finReimbursementMapper.delete(Wrappers.<FinReimbursement>lambdaQuery().in(FinReimbursement::getId, reimbursementIds));
        }
        fileUtil.deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVAL_INSTANCE, ids);
        int instanceRows = approvalInstanceMapper.update(
src/main/java/com/ruoyi/basic/controller/FileUploadController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package com.ruoyi.basic.controller;
import com.ruoyi.basic.dto.StorageBlobVO;
import com.ruoyi.basic.service.StorageBlobService;
import com.ruoyi.framework.web.domain.R;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequiredArgsConstructor
@Tag(name = "文件上传")
@RequestMapping("/file")
public class FileUploadController {
    private final StorageBlobService storageBlobService;
    @PostMapping("/upload")
    @Operation(summary = "文件上传")
    public R upload(@RequestParam("file") List<MultipartFile> file) {
        List<StorageBlobVO> result = storageBlobService.upload(file, false);
        if (result == null || result.isEmpty()) {
            return R.fail("上传失败");
        }
        StorageBlobVO vo = result.get(0);
        Map<String, Object> data = new HashMap<>();
        data.put("id", vo.getId());
        data.put("originalName", vo.getOriginalFilename());
        data.put("tempPath", vo.getPreviewURL());
        data.put("uidFilename", vo.getUidFilename());
        data.put("contentType", vo.getContentType());
        data.put("byteSize", vo.getByteSize());
        return R.ok(data);
    }
}
src/main/java/com/ruoyi/collaborativeApproval/vo/EnterpriseNewsVo.java
@@ -12,4 +12,13 @@
    private String createUserName;
    private List<StorageBlobVO> storageBlobDTOs;
    /** å‰ç«¯å…¼å®¹åˆ«åï¼Œæ˜ å°„ storageBlobDTOs */
    public List<StorageBlobVO> getAttachmentList() {
        return storageBlobDTOs;
    }
    public void setAttachmentList(List<StorageBlobVO> attachmentList) {
        this.storageBlobDTOs = attachmentList;
    }
}
src/main/java/com/ruoyi/project/common/CommonController.java
@@ -52,7 +52,7 @@
        return R.ok(storageBlobService.upload(files, true));
    }
    @GetMapping("/download/{fileName}")
    @GetMapping("/download/{fileName:.+}")
    @Anonymous
    public void download(@PathVariable String fileName,
                         @RequestParam(value = "token", required = false) String token,
@@ -71,7 +71,7 @@
        Files.copy(file.toPath(), response.getOutputStream());
    }
    @GetMapping("/preview/{fileName}")
    @GetMapping("/preview/{fileName:.+}")
    @Anonymous
    public ResponseEntity<FileSystemResource> preview(@PathVariable String fileName,
                                                      @RequestParam(value = "token", required = false) String token,
src/main/java/com/ruoyi/staff/controller/StaffOnJobController.java
@@ -18,6 +18,7 @@
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List;
/**
@@ -118,7 +119,7 @@
     */
    @PostMapping("/import")
    @Log(title = "在职员工导入", businessType = BusinessType.IMPORT)
    public AjaxResult importData(@RequestPart("file") MultipartFile file) {
    public AjaxResult importData(@RequestPart("file") MultipartFile file) throws IOException {
        Boolean b = staffOnJobService.importData(file);
        if (b) {
            return AjaxResult.success("导入成功");
src/main/java/com/ruoyi/staff/service/IStaffOnJobService.java
@@ -9,6 +9,8 @@
import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
public interface IStaffOnJobService extends IService<StaffOnJob> {
@@ -30,7 +32,7 @@
    List<StaffOnJobDto> staffOnJobList(StaffOnJob staffOnJob);
    Boolean importData(MultipartFile file);
    Boolean importData(MultipartFile file) throws IOException;
    String exportCopy(HttpServletResponse response, StaffOnJob staffOnJob) throws Exception;
}
src/main/java/com/ruoyi/staff/service/impl/StaffOnJobServiceImpl.java
@@ -34,10 +34,7 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.LocalDate;
@@ -111,7 +108,8 @@
        // åˆ›å»ºåˆåŒè®°å½•
        StaffContract staffContract = new StaffContract();
        staffContract.setStaffOnJobId(staffOnJobPrams.getId());
        staffContract.setContractTerm(staffOnJobPrams.getContractTerm());
        String contractTerm = staffOnJobPrams.getContractTerm();
        staffContract.setContractTerm(StringUtils.isNotEmpty(contractTerm) ? contractTerm : null);
        staffContract.setContractStartTime(staffOnJobPrams.getContractStartTime());
        staffContract.setContractEndTime(staffOnJobPrams.getContractEndTime());
        return staffContractMapper.insert(staffContract);
@@ -321,8 +319,7 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean importData(MultipartFile file) {
        try {
    public Boolean importData(MultipartFile file) throws IOException {
            ExcelUtil<StaffOnJobExcelDto> util = new ExcelUtil<>(StaffOnJobExcelDto.class);
            List<StaffOnJobExcelDto> staffOnJobs = util.importExcel(file.getInputStream());
            if (CollectionUtils.isEmpty(staffOnJobs)){
@@ -336,7 +333,7 @@
                StaffOnJobDto staffOnJobDto = new StaffOnJobDto();
                BeanUtils.copyProperties(staffOnJob, staffOnJobDto);
                // é€šè¿‡åç§°èŽ·å–éƒ¨é—¨id
                staffOnJobDto.setSysDeptId(// ... existing code ...
            staffOnJobDto.setSysDeptId(
                        sysDepts.stream()
                            .filter(dept -> dept.getDeptName() != null && dept.getDeptName().equals(staffOnJob.getSysDeptName()))
                            .findFirst()
@@ -352,10 +349,6 @@
                add(staffOnJobDto);
            });
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
src/main/resources/application-dev-pro.yml
@@ -28,7 +28,7 @@
# å¼€å‘环境配置
server:
  # æœåŠ¡å™¨çš„HTTP端口,默认为8080
  port: 7003
  port: 8888
  servlet:
    # åº”用的访问路径
    context-path: /
@@ -71,9 +71,9 @@
    druid:
      # ä¸»åº“数据源
      master:
        url: jdbc:mysql://localhost:3306/product-inventory-management-new-pro?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        url: jdbc:mysql://110.167.133.1:9002/product-inventory-management-oa?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
        username: root
        password: 123456
        password: oa@123456..
      # ä»Žåº“数据源
      slave:
        # ä»Žæ•°æ®æºå¼€å…³/默认关闭
@@ -147,12 +147,12 @@
    # redis é…ç½®
    redis:
      # åœ°å€
      host: 127.0.0.1
      host: 47.114.74.44
      #    host: 172.17.0.1
      # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
      port: 6379
      port: 6399
      # æ•°æ®åº“索引
      database: 0
      database: 7
      # å¯†ç 
      #    password: root2022!
      password:
@@ -254,11 +254,11 @@
# æ–‡ä»¶ä¸Šä¼ é…ç½®
file:
  temp-dir: D:/ruoyi/temp/uploads   # ä¸´æ—¶ç›®å½• åŽæœŸåˆ é™¤
  upload-dir: D:/ruoyi/prod/uploads # æ­£å¼ç›®å½• åŽæœŸåˆ é™¤
  path: C:/Users/12631/Desktop/download/uploads # ä¸Šä¼ ç›®å½•
  temp-dir: E:/ruoyi/oa/file/temp/uploads   # ä¸´æ—¶ç›®å½•
  upload-dir: E:/ruoyi/oa/file/prod/uploads # æ­£å¼ç›®å½•
  path: E:/ruoyi/oa/file # ä¸Šä¼ ç›®å½•
  urlPrefix: /common # é“¾æŽ¥å‰ç¼€
  domain: http://127.0.0.1:7003 # åŸŸåå‰ç¼€
  domain: http://localhost:8888 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°
  compress: true # æ˜¯å¦åŽ‹ç¼©
src/main/resources/application-oa.yml
@@ -259,7 +259,7 @@
  temp-dir: /javaWork/product-inventory-management/file/temp/uploads   # ä¸´æ—¶ç›®å½•
  upload-dir: /javaWork/product-inventory-management/file/prod/uploads # æ­£å¼ç›®å½•
  path: /javaWork/product-inventory-management/file # ä¸Šä¼ ç›®å½•
  urlPrefix: /prod-api/common # é“¾æŽ¥å‰ç¼€
  urlPrefix: /common # é“¾æŽ¥å‰ç¼€
  domain: http://110.167.133.1:9001 # åŸŸåå‰ç¼€
  expired: 120 # è¿‡æœŸæ—¶é—´(单位:分钟)
  useLimit: 10 # ä½¿ç”¨æ¬¡æ•°