package com.yingpaipay.business.controller; import com.yingpaipay.business.domain.Document; import com.yingpaipay.business.domain.dto.WpsGetVersionsDto; import com.yingpaipay.business.domain.dto.WpsR; import com.yingpaipay.business.domain.dto.WpsVersionControlDto; import com.yingpaipay.business.domain.dto.WpsVersionDto; import com.yingpaipay.business.service.impl.CommonDocumentService; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.domain.R; import org.dromara.common.core.exception.BusinessException; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.oss.core.OssClient; import org.dromara.common.oss.entity.UploadResult; import org.dromara.common.oss.factory.OssFactory; import org.dromara.common.web.core.BaseController; import org.dromara.system.domain.SysOssExt; import org.dromara.system.domain.SysUser; import org.dromara.system.domain.vo.SysOssVo; import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.service.ISysOssService; import org.dromara.system.service.ISysUserService; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** * @Author: Huanyi * @Description: 由于 WPS 服务对于版本管理非常的麻烦,版本回退一直没有很好的解决方案,因此直接由我们自己负责管理版本。 */ @Deprecated @RestController @RequestMapping("/wps/callback/v3/3rd") @RequiredArgsConstructor @Slf4j public class WpsController extends BaseController { /** * 这里为版本控制缓存 */ private static final Map DOCUMENT_MAP = new ConcurrentHashMap<>(); private final ISysOssService ossService; private final CommonDocumentService documentService; private final ISysUserService userService; @PutMapping(value = "/upload/file/{documentId}", consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE) public void upload(@PathVariable String documentId, HttpServletRequest request) { String[] ids = documentId.split("_"); WpsVersionControlDto dto = DOCUMENT_MAP.get(Long.valueOf(ids[0])); File tempFile = null; try { tempFile = File.createTempFile(dto.getFileName() + "_", dto.getSuffix()); try (InputStream in = request.getInputStream(); FileOutputStream out = new FileOutputStream(tempFile)) { byte[] buffer = new byte[8192]; int bytesRead; while ((bytesRead = in.read(buffer)) != -1) { out.write(buffer, 0, bytesRead); } } // TODO 防止自增ID过早炸掉,这里采用更新操作,不使用自带的图片上传 String originalfileName = tempFile.getName(); String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf(".")); OssClient storage = OssFactory.instance(); UploadResult uploadResult = storage.uploadSuffix(tempFile, suffix, originalfileName); dto.setCurrentVersion(Long.parseLong(ids[1])); List versions = dto.getVersions(); for (WpsVersionDto version : versions) { if (Objects.equals(version.getVersion(), Long.valueOf(ids[1]))) { // FIXME 由于原始版本不能被删除,暂时先不实现: 每新增一个版本,就将原始版本上传一个全新的版本 // // TODO 旧的版本可以不需要了,只需要新的版本 // storage.delete(version.getUrl()); version.setFileName(uploadResult.getFilename()); version.setUrl(uploadResult.getUrl()); version.setUpdateTime(new Date()); } } } catch (IOException e) { log.error("文件写入失败, documentId={}", documentId, e); } finally { if (tempFile != null && tempFile.exists()) { boolean deleted = tempFile.delete(); if (!deleted) { log.warn("临时文件删除失败: {}", tempFile.getAbsolutePath()); } } } } private record WpsFilesCompleteRequestDto( String file_id, Object request, String name, Integer size, Map digest, Boolean is_manual, Object response, Integer status_code ) {} private record WpsFilesCompleteResponseDto( String id, String name, Integer version, Integer size, Integer create_time, Integer modify_time, String creator_id, String modifier_id ) {} @PostMapping("/files/{documentId}/upload/complete") public WpsR complete(@PathVariable String documentId, @RequestBody WpsFilesCompleteRequestDto dto) { Map request = (LinkedHashMap) dto.request; return WpsR.ok( new WpsFilesCompleteResponseDto( String.valueOf(request.get("file_id")), String.valueOf(request.get("name")), 1, Integer.valueOf(String.valueOf(request.get("size"))), Math.toIntExact(System.currentTimeMillis() / 1000L), Math.toIntExact(System.currentTimeMillis() / 1000L), "1", "1" ) ); } private record WpsUplaodPrepareDto(String[] digest_types) {} @GetMapping("/files/{documentId}/upload/prepare") public WpsR uploadPrepare(@PathVariable String documentId) { String[] types = {"md5"}; return WpsR.ok(new WpsUplaodPrepareDto(types)); } private record WpsUploadAddressRequestDto( String file_id, String name, Integer size, Map digest, Boolean is_manual, Integer attachment_size, String content_type ) {} private record WpsUploadAddressResponseDto(String method, String url) {} @PostMapping("/files/{documentId}/upload/address") public WpsR uploadAddress(@PathVariable String documentId, @RequestBody WpsUploadAddressRequestDto dto) { return WpsR.ok(new WpsUploadAddressResponseDto("PUT", "http://yp1.yingpaipay.com:9029/wps/callback/v3/3rd/upload/file/" + documentId)); } private record WpsGetFileInfoDto( String id, String name, Integer version, Integer size, Integer create_time, Integer modify_time, String creator_id, String modifier_id ) {} @GetMapping("/files/{documentId}") public WpsR getFileInfo(@PathVariable String documentId) { String[] ids = documentId.split("_"); long id = Long.parseLong(ids[0]); long version = Long.parseLong(ids[1]); if (DOCUMENT_MAP.containsKey(id)) { WpsVersionControlDto dto = DOCUMENT_MAP.get(id); return WpsR.ok(new WpsGetFileInfoDto( documentId, dto.getFileName(), 1, dto.getSize(), Math.toIntExact(new Date().getTime() / 1000L), Math.toIntExact(new Date().getTime() / 1000L), dto.getCreator(), dto.getUpdator() )); } WpsVersionControlDto dto = initDocument(id); return WpsR.ok(new WpsGetFileInfoDto( documentId, dto.getFileName(), 1, dto.getSize(), Math.toIntExact(new Date().getTime() / 1000L), Math.toIntExact(new Date().getTime() / 1000L), dto.getCreator(), dto.getUpdator() )); } private record WpsFilesPermissionDto( String user_id, Integer read, Integer update, Integer download, Integer rename, Integer history, Integer copy, Integer print, Integer saves, Integer comment ) {} /** * 随便写的,没必要划分的这么细致 */ @GetMapping("/files/{documentId}/permission") public WpsR permission(@PathVariable String documentId) { return WpsR.ok( new WpsFilesPermissionDto( "1", 1, 1, 1, 1, 1, 1, 1, 1, 1 ) ); } private record WpsGetUrlDto(String url) {} @GetMapping("/files/{documentId}/download") public WpsR getUrl(@PathVariable String documentId) { String[] ids = documentId.split("_"); long id = Long.parseLong(ids[0]); long version = Long.parseLong(ids[1]); if (DOCUMENT_MAP.containsKey(id)) { WpsVersionControlDto dto = DOCUMENT_MAP.get(id); List versions = dto.getVersions(); for (WpsVersionDto versionDto : versions) { if (versionDto.getVersion() == version) { return WpsR.ok(new WpsGetUrlDto(versionDto.getUrl())); } } return WpsR.ok(new WpsGetUrlDto(dto.getVersions().get(0).getUrl())); } WpsVersionControlDto dto = initDocument(id); List versions = dto.getVersions(); for (WpsVersionDto versionDto : versions) { if (Objects.equals(versionDto.getVersion(), dto.getCurrentVersion())) { return WpsR.ok(new WpsGetUrlDto(versionDto.getUrl())); } } return WpsR.ok(new WpsGetUrlDto(dto.getVersions().get(0).getUrl())); } @GetMapping("/files/{documentId}/versions/{version}/download") public WpsR getUrlByVersion(@PathVariable String documentId, @PathVariable Integer version) { if (DOCUMENT_MAP.containsKey(Long.valueOf(documentId.split("_")[0]))) { WpsVersionControlDto dto = DOCUMENT_MAP.get(Long.valueOf(documentId.split("_")[0])); return WpsR.ok(new WpsGetUrlDto(dto.getVersions().get(Integer.parseInt(documentId.split("_")[1])).getUrl())); } WpsVersionControlDto dto = initDocument(Long.valueOf(documentId.split("_")[0])); return WpsR.ok(new WpsGetUrlDto(dto.getVersions().get(0).getUrl())); } private record WpsGetUsersDto(String id, String name, String avatar_url) {} @GetMapping("/users") public WpsR getUsers(@RequestParam("user_ids") Long[] userIds) { if (userIds == null || userIds.length == 0) { return WpsR.ok(); } List vos = userService.selectUserByIds(Arrays.stream(userIds).toList()); List ossIds = new ArrayList<>(); vos.forEach(e -> ossIds.add(e.getAvatar())); List ossVos = ossService.queryListByIds(ossIds); List dtos = new ArrayList<>(); for (SysUser vo : vos) { String url = ""; for (SysOssVo ossVo : ossVos) { if (ossVo.getOssId().equals(vo.getAvatar())) { url = ossVo.getUrl(); } } dtos.add(new WpsGetUsersDto(String.valueOf(vo.getUserId()), vo.getNickName(), url)); } return WpsR.ok(dtos); } @PostMapping(value = "/init/{documentId}") public R init(@PathVariable Long documentId) { if (DOCUMENT_MAP.containsKey(documentId)) { throw new BusinessException("已有其他人正在审核该文件"); } initDocument(documentId); return R.ok(DOCUMENT_MAP.get(documentId).getCurrentVersion()); } private WpsVersionControlDto initDocument(Long ossId) { SysOssVo ossVo = ossService.getById(ossId); Document document = documentService.getByOssId(ossId); if (DOCUMENT_MAP.containsKey(document.getId())) { throw new BusinessException("已有其他人正在审核该文件"); } long currentVersion = System.currentTimeMillis(); List list = new ArrayList<>(); list.add(new WpsVersionDto(ossVo.getFileName(), 0L, ossVo.getUrl(), new Date(), new Date(), Math.toIntExact(document.getCreateTime().getTime() / 1000L), Math.toIntExact(document.getUpdateTime().getTime() / 1000L))); list.add(new WpsVersionDto(ossVo.getFileName(), currentVersion, ossVo.getUrl(), new Date(), new Date(), Math.toIntExact(document.getCreateTime().getTime() / 1000L), Math.toIntExact(document.getUpdateTime().getTime() / 1000L))); WpsVersionControlDto dto = new WpsVersionControlDto(); dto.setCurrentVersion(currentVersion); dto.setVersions(list); dto.setSuffix(ossVo.getFileSuffix()); dto.setCreator(String.valueOf(document.getCreateBy())); dto.setUpdator(String.valueOf(document.getUpdateBy())); dto.setFileName(ossVo.getOriginalName()); SysOssExt ext = JsonUtils.parseObject(ossVo.getExt1(), SysOssExt.class); long size = ext.getFileSize(); dto.setSize((int) size); DOCUMENT_MAP.put(ossId, dto); return dto; } @PutMapping("/clean/{documentId}") public R clean(@PathVariable Long documentId) { WpsVersionControlDto dto = DOCUMENT_MAP.get(documentId); long currentVersion = System.currentTimeMillis(); dto.getVersions().add(new WpsVersionDto( dto.getVersions().get(0).getFileName(), currentVersion, dto.getVersions().get(0).getUrl(), new Date(), new Date(), Math.toIntExact(new Date().getTime() / 1000L), Math.toIntExact(new Date().getTime() / 1000L) )); dto.setCurrentVersion(currentVersion); log.info("自行进行刷新版本"); dto.getVersions().forEach(e -> log.info("{}", JsonUtils.toJsonString(e))); return R.ok(currentVersion); } /** * 已废弃 */ @GetMapping("/files/{documentId}/versions") public WpsR getVersions(@PathVariable Long documentId, @RequestParam(value = "offset", required = false) Integer offset, @RequestParam(value = "limit", required = false) Integer size) { WpsVersionControlDto dto = DOCUMENT_MAP.get(documentId); List versions = dto.getVersions(); List reversed = new ArrayList<>(versions); Collections.reverse(reversed); if (offset == null || size == null) { return WpsR.ok(reversed); } int fromIndex = Math.min(offset, reversed.size()); int toIndex = Math.min(offset + size, reversed.size()); List paginated = reversed.subList(fromIndex, toIndex); List list = new ArrayList<>(); paginated.forEach(e -> list.add(new WpsGetVersionsDto( String.valueOf(documentId), dto.getFileName(), Math.toIntExact(e.getVersion() / 1000L), dto.getSize(), e.getCreate_time(), e.getUpdate_time(), dto.getCreator(), dto.getUpdator() ))); WpsR> r = WpsR.ok(list); String str = JsonUtils.toJsonString(r); log.warn(str); return r; } /** * 已废弃 */ @GetMapping("/files/{documentId}/versions/{version}") public WpsR getFileByVersion(@PathVariable Long documentId, @PathVariable Integer version) { WpsVersionControlDto dto = DOCUMENT_MAP.get(documentId); WpsVersionDto current = dto.getVersions().get(version - 1); return WpsR.ok(new WpsGetVersionsDto( String.valueOf(documentId), dto.getFileName(), Math.toIntExact(current.getVersion() / 1000L), dto.getSize(), current.getCreate_time(), current.getUpdate_time(), dto.getCreator(), dto.getUpdator() )); } @GetMapping("/files/list") public R> getVersionList(@RequestParam("ossId") Long ossId) { List list = DOCUMENT_MAP.get(ossId).getVersions(); list.forEach(e -> log.info("{}", JsonUtils.toJsonString(e))); return R.ok(list.stream().filter(e -> e.getVersion() != 0L).sorted((e1, e2) -> Math.toIntExact(e2.getVersion() - e1.getVersion())).toList()); } @GetMapping("/getFinal") public R getFinal(@RequestParam("id") String id) { String[] ids = id.split("_"); long documentId = Long.parseLong(ids[0]); long version = Long.parseLong(ids[1]); log.info("文档编号为 {}, 版本号为 : {}", documentId, version); WpsVersionControlDto dto = DOCUMENT_MAP.get(documentId); List versions = dto.getVersions(); for (WpsVersionDto versionDto : versions) { log.info("当前轮询的版本号为 {}", versionDto.getVersion()); if (versionDto.getVersion() == version) { String url = versionDto.getUrl(); SysOssVo vo = ossService.insertByUrl(versionDto.getFileName(), dto.getFileName(), url); DOCUMENT_MAP.remove(documentId); return R.ok(vo); } } throw new BusinessException("审核内容保存失败"); } @DeleteMapping("/cancel/{documentId}") public R cancel(@PathVariable Long documentId) { DOCUMENT_MAP.remove(documentId); return R.ok(); } }