feat: wps
parent
0cda1b471b
commit
3aabb226e9
|
|
@ -1,122 +1,154 @@
|
|||
package org.qinan.safetyeval.adapter.web;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.jjb.saas.framework.auth.model.SSOUser;
|
||||
import com.jjb.saas.framework.utils.JwtTokenUtils;
|
||||
import com.alibaba.cola.dto.SingleResponse;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.x.file.storage.core.FileInfo;
|
||||
import org.dromara.x.file.storage.core.FileStorageService;
|
||||
import org.qinan.safetyeval.infrastructure.mapper.AccountMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.qinan.safetyeval.client.api.WpsApi;
|
||||
import org.qinan.safetyeval.client.co.WpsDocOpenCO;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 对外开放接口(免鉴权)。
|
||||
* <p>
|
||||
* 所有不需要登录验证的接口统一挂载在 /safetyEval/images 前缀下。
|
||||
* </p>
|
||||
* WPS WebOffice 服务器回调(免登录拦截,由 WPS 携带 token)。
|
||||
*
|
||||
* @author safety-eval
|
||||
*/
|
||||
@Api(tags = "对外开放接口")
|
||||
@Api(tags = "WPS回调接口")
|
||||
@RestController
|
||||
@RequestMapping("/safetyEval-h5/wsp")
|
||||
@Slf4j
|
||||
public class H5OpenController {
|
||||
|
||||
@Autowired
|
||||
private AccountMapper accountMapper;
|
||||
@Autowired
|
||||
private FileStorageService fileStorageService;
|
||||
@Resource
|
||||
private WpsApi wpsApi;
|
||||
@Resource
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@ApiOperation("wps回调预览")
|
||||
@ApiOperation("wps回调-获取文件信息")
|
||||
@GetMapping(value = "/v3/3rd/files/{file_id}", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public String getWpsInfoByFileId(@PathVariable("file_id") String file_id, HttpServletRequest request) {
|
||||
log.info("token, {}", request.getHeader("token"));
|
||||
log.info("Token, {}", request.getHeader("Token"));
|
||||
String header = request.getHeader("X-Weboffice-Token");
|
||||
log.info("X-Weboffice-Token, {}", request.getHeader("X-Weboffice-Token"));
|
||||
|
||||
String token = header;
|
||||
log.info("2.5token不为空:{}", token);
|
||||
token = token.replaceAll("jjb-saas-auth:oauth:", "").trim();
|
||||
String json = null;
|
||||
SSOUser ssoUser;
|
||||
try {
|
||||
json = JwtTokenUtils.parseToken(token);
|
||||
} catch (Exception var41) {
|
||||
}
|
||||
if (null != json) {
|
||||
ssoUser = (SSOUser) JSONUtil.toBean(json, SSOUser.class);
|
||||
} else {
|
||||
ssoUser = new SSOUser();
|
||||
}
|
||||
log.info("ssoUser:{}", ssoUser);
|
||||
log.info("wps回调预览:{}", file_id);
|
||||
return accountMapper.getWps(1);
|
||||
public String getWpsInfoByFileId(@PathVariable("file_id") String fileId) {
|
||||
log.info("wps回调预览:{}", fileId);
|
||||
return toJson(wpsApi.getFileInfo(fileId));
|
||||
}
|
||||
|
||||
@ApiOperation("wps回调下载")
|
||||
@ApiOperation("wps回调-获取下载地址")
|
||||
@GetMapping(value = "/v3/3rd/files/{file_id}/download", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public String getWpsDownloadByFileId(@PathVariable("file_id") String file_id) {
|
||||
log.info("wps回调下载:{}", file_id);
|
||||
return accountMapper.getWps(2);
|
||||
public String getWpsDownloadByFileId(@PathVariable("file_id") String fileId) {
|
||||
log.info("wps回调下载:{}", fileId);
|
||||
return toJson(wpsApi.getDownload(fileId));
|
||||
}
|
||||
|
||||
@ApiOperation("wps回调权限信息")
|
||||
@ApiOperation("wps回调-文档权限")
|
||||
@GetMapping(value = "/v3/3rd/files/{file_id}/permission", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public String getWpsPermissionByFileId(@PathVariable("file_id") String file_id) {
|
||||
log.info("wps回调权限信息:{}", file_id);
|
||||
return accountMapper.getWps(3);
|
||||
public String getWpsPermissionByFileId(@PathVariable("file_id") String fileId) {
|
||||
log.info("wps回调权限信息:{}", fileId);
|
||||
return toJson(wpsApi.getPermission(fileId));
|
||||
}
|
||||
|
||||
@ApiOperation("wps回调用户信息")
|
||||
@ApiOperation("wps回调-批量用户信息")
|
||||
@GetMapping(value = "/v3/3rd/users", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public String getWpsUsers(@RequestParam List<Long> user_ids) {
|
||||
log.info("wps回调用户信息:{}", user_ids);
|
||||
return accountMapper.getWps(4);
|
||||
public String getWpsUsers(@RequestParam("user_ids") List<String> userIds) {
|
||||
log.info("wps回调用户信息:{}", userIds);
|
||||
return toJson(wpsApi.getUsers(userIds));
|
||||
}
|
||||
|
||||
@ApiOperation("wps回调上传第一步")
|
||||
@ApiOperation("wps回调-上传准备")
|
||||
@GetMapping(value = "/v3/3rd/files/{file_id}/upload/prepare", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public String getWpsUpLoadOne(@PathVariable("file_id") String file_id) {
|
||||
log.info("wps回调上传第一步:{}", file_id);
|
||||
return accountMapper.getWps(5);
|
||||
public String getWpsUpLoadOne(@PathVariable("file_id") String fileId) {
|
||||
log.info("wps回调上传第一步:{}", fileId);
|
||||
return toJson(wpsApi.uploadPrepare(fileId));
|
||||
}
|
||||
|
||||
@ApiOperation("wps回调上传第二步")
|
||||
@PostMapping(value = "/v3/3rd/files/{file_id}/upload/address")
|
||||
public String getWpsUpLoadTwo(@PathVariable("file_id") String file_id, @RequestBody Map<String, Object> map) {
|
||||
log.info("wps回调上传第二步:{}, map: {}", file_id, JSON.toJSONString(map));
|
||||
return accountMapper.getWps(6);
|
||||
@ApiOperation("wps回调-获取上传地址")
|
||||
@PostMapping(value = "/v3/3rd/files/{file_id}/upload/address", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public String getWpsUpLoadTwo(@PathVariable("file_id") String fileId,
|
||||
@RequestBody Map<String, Object> body) {
|
||||
log.info("wps回调上传第二步:{}, body={}", fileId, body);
|
||||
return toJson(wpsApi.uploadAddress(fileId, body));
|
||||
}
|
||||
|
||||
@ApiOperation("wps回调上传第三步")
|
||||
@PostMapping(value = "/v3/3rd/files/{file_id}/upload/complete")
|
||||
public String getWpsUpLoadThree(@PathVariable("file_id") String file_id, @RequestBody Map<String, Object> map) {
|
||||
String msg = accountMapper.getWps(7);
|
||||
log.info("wps回调上传第三步:{}, map: {}", file_id, msg);
|
||||
return msg;
|
||||
@ApiOperation("wps回调-上传完成")
|
||||
@PostMapping(value = "/v3/3rd/files/{file_id}/upload/complete", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public String getWpsUpLoadThree(@PathVariable("file_id") String fileId,
|
||||
@RequestBody Map<String, Object> body) {
|
||||
log.info("wps回调上传第三步:{}, body={}", fileId, body);
|
||||
return toJson(wpsApi.uploadComplete(fileId, body));
|
||||
}
|
||||
|
||||
@PutMapping(value = "/upload/{file_id}", consumes = "application/octet-stream")
|
||||
public String uploadFile(@PathVariable("file_id") String file_id, HttpServletRequest request) throws IOException {
|
||||
// 获取文件的二进制流
|
||||
try (InputStream inputStream = request.getInputStream()) {
|
||||
FileInfo upload = fileStorageService.of(inputStream, file_id).upload();
|
||||
log.info("上传文件成功:{}", JSON.toJSONString(upload));
|
||||
return "ok";
|
||||
} catch (Exception e) {
|
||||
log.error("上传文件失败:{}", e.getMessage());
|
||||
return "上传文件失败";
|
||||
@ApiOperation("wps回调-PUT上传文件实体")
|
||||
@PutMapping(value = "/upload/{file_id}", consumes = "application/octet-stream", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public String uploadFile(@PathVariable("file_id") String fileId, HttpServletRequest request) throws IOException {
|
||||
log.info("wps PUT上传:{}", fileId);
|
||||
return toJson(wpsApi.uploadBinary(fileId, request.getInputStream()));
|
||||
}
|
||||
|
||||
@ApiOperation("wps回调-历史版本列表")
|
||||
@GetMapping(value = "/v3/3rd/files/{file_id}/versions", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public String getWpsVersions(@PathVariable("file_id") String fileId) {
|
||||
log.info("wps回调历史版本列表:{}", fileId);
|
||||
return toJson(wpsApi.listVersions(fileId));
|
||||
}
|
||||
|
||||
@ApiOperation("wps回调-指定历史版本")
|
||||
@GetMapping(value = "/v3/3rd/files/{file_id}/versions/{version}", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public String getWpsVersion(@PathVariable("file_id") String fileId,
|
||||
@PathVariable("version") Integer version) {
|
||||
log.info("wps回调历史版本详情:{}, version={}", fileId, version);
|
||||
return toJson(wpsApi.getVersion(fileId, version));
|
||||
}
|
||||
|
||||
@ApiOperation("wps回调-历史版本下载")
|
||||
@GetMapping(value = "/v3/3rd/files/{file_id}/versions/{version}/download", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public String getWpsVersionDownload(@PathVariable("file_id") String fileId,
|
||||
@PathVariable("version") Integer version) {
|
||||
log.info("wps回调历史版本下载:{}, version={}", fileId, version);
|
||||
return toJson(wpsApi.getVersionDownload(fileId, version));
|
||||
}
|
||||
|
||||
@ApiOperation("wps回调-文档重命名")
|
||||
@PutMapping(value = "/v3/3rd/files/{file_id}/name", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public String renameWpsFile(@PathVariable("file_id") String fileId,
|
||||
@RequestBody Map<String, Object> body) {
|
||||
log.info("wps回调重命名:{}, body={}", fileId, body);
|
||||
return toJson(wpsApi.rename(fileId, body));
|
||||
}
|
||||
|
||||
@ApiOperation("打开页:当前用户有文档返回最近一份,没有则创建空白docx")
|
||||
@GetMapping(value = "/doc/my-or-create", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public SingleResponse<WpsDocOpenCO> myOrCreate() {
|
||||
log.info("wps my-or-create");
|
||||
return SingleResponse.of(wpsApi.myOrCreate());
|
||||
}
|
||||
|
||||
@ApiOperation("始终新建一份空白docx")
|
||||
@PostMapping(value = "/doc/create", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public SingleResponse<WpsDocOpenCO> createBlank() {
|
||||
log.info("wps create blank");
|
||||
return SingleResponse.of(wpsApi.createBlank());
|
||||
}
|
||||
|
||||
private String toJson(Map<String, Object> result) {
|
||||
try {
|
||||
return objectMapper.writeValueAsString(result);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("wps response serialize failed", e);
|
||||
return "{\"code\":40001,\"message\":\"serialize error\"}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
package org.qinan.safetyeval.app.convertor;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.qinan.safetyeval.client.co.WpsFileCO;
|
||||
import org.qinan.safetyeval.client.co.WpsUserCO;
|
||||
import org.qinan.safetyeval.domain.entity.AccountEntity;
|
||||
import org.qinan.safetyeval.domain.entity.WpsDocEntity;
|
||||
import org.qinan.safetyeval.domain.entity.WpsDocVersionEntity;
|
||||
|
||||
/**
|
||||
* WPS 回调对象转换(App层)
|
||||
*
|
||||
* @author safety-eval
|
||||
*/
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface WpsCmdConvertor {
|
||||
|
||||
@Mapping(source = "fileId", target = "id")
|
||||
@Mapping(source = "docVersion", target = "version")
|
||||
@Mapping(source = "createTimeEpoch", target = "createTime")
|
||||
@Mapping(source = "modifyTimeEpoch", target = "modifyTime")
|
||||
WpsFileCO toFileCO(WpsDocEntity entity);
|
||||
|
||||
@Mapping(source = "fileId", target = "id")
|
||||
@Mapping(source = "docVersion", target = "version")
|
||||
@Mapping(source = "createTimeEpoch", target = "createTime")
|
||||
@Mapping(source = "createTimeEpoch", target = "modifyTime")
|
||||
@Mapping(target = "creatorId", ignore = true)
|
||||
WpsFileCO toFileCO(WpsDocVersionEntity entity);
|
||||
|
||||
@Mapping(target = "id", expression = "java(entity.getId() == null ? null : String.valueOf(entity.getId()))")
|
||||
@Mapping(source = "userName", target = "name")
|
||||
@Mapping(source = "profileUrl", target = "avatarUrl")
|
||||
WpsUserCO toUserCO(AccountEntity entity);
|
||||
}
|
||||
|
|
@ -0,0 +1,480 @@
|
|||
package org.qinan.safetyeval.app.executor;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.x.file.storage.core.FileInfo;
|
||||
import org.dromara.x.file.storage.core.FileStorageService;
|
||||
import org.qinan.safetyeval.app.convertor.WpsCmdConvertor;
|
||||
import org.qinan.safetyeval.client.api.WpsApi;
|
||||
import org.qinan.safetyeval.client.co.WpsDocOpenCO;
|
||||
import org.qinan.safetyeval.client.co.WpsFileCO;
|
||||
import org.qinan.safetyeval.client.co.WpsPermissionCO;
|
||||
import org.qinan.safetyeval.client.co.WpsUserCO;
|
||||
import org.qinan.safetyeval.domain.entity.AccountEntity;
|
||||
import org.qinan.safetyeval.domain.entity.FileStorage;
|
||||
import org.qinan.safetyeval.domain.entity.WpsDocEntity;
|
||||
import org.qinan.safetyeval.domain.entity.WpsDocVersionEntity;
|
||||
import org.qinan.safetyeval.domain.exception.BizException;
|
||||
import org.qinan.safetyeval.domain.exception.ErrorCode;
|
||||
import org.qinan.safetyeval.domain.gateway.AccountGateway;
|
||||
import org.qinan.safetyeval.domain.service.FileStorageDomainService;
|
||||
import org.qinan.safetyeval.domain.service.WpsDocDomainService;
|
||||
import org.qinan.safetyeval.infrastructure.adapter.ThreadLocalUserInfoAdapter;
|
||||
import org.qinan.safetyeval.infrastructure.adapter.auth.AuthUserContextAdapter;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* WPS WebOffice 回调执行器
|
||||
*
|
||||
* @author safety-eval
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class WpsExecutor implements WpsApi {
|
||||
|
||||
@Resource
|
||||
private WpsDocDomainService wpsDocDomainService;
|
||||
@Resource
|
||||
private AccountGateway accountGateway;
|
||||
@Resource
|
||||
private WpsCmdConvertor wpsCmdConvertor;
|
||||
@Resource
|
||||
private FileStorageService fileStorageService;
|
||||
@Resource
|
||||
private FileStorageDomainService fileStorageDomainService;
|
||||
|
||||
@Value("${wps.upload-base-url:}")
|
||||
private String uploadBaseUrl;
|
||||
|
||||
@Value("${wps.def-docx-url:}")
|
||||
private String defDocxUrl;
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getFileInfo(String fileId) {
|
||||
try {
|
||||
WpsDocEntity doc = wpsDocDomainService.getByFileId(fileId);
|
||||
return ok(wpsCmdConvertor.toFileCO(doc));
|
||||
} catch (BizException e) {
|
||||
return fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getDownload(String fileId) {
|
||||
try {
|
||||
WpsDocEntity doc = wpsDocDomainService.getByFileId(fileId);
|
||||
Map<String, Object> data = new LinkedHashMap<>();
|
||||
data.put("url", resolveDownloadUrl(doc.getDownloadUrl(), doc.getResourceId()));
|
||||
return ok(data);
|
||||
} catch (BizException e) {
|
||||
return fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getPermission(String fileId) {
|
||||
try {
|
||||
wpsDocDomainService.getByFileId(fileId);
|
||||
Long userId = AuthUserContextAdapter.getCurrentUserId();
|
||||
if (userId == null) {
|
||||
throw new BizException(ErrorCode.WPS_USER_NOT_LOGIN);
|
||||
}
|
||||
WpsPermissionCO permission = new WpsPermissionCO();
|
||||
permission.setUserId(String.valueOf(userId));
|
||||
permission.setRead(1);
|
||||
permission.setUpdate(1);
|
||||
permission.setDownload(1);
|
||||
permission.setPrint(1);
|
||||
permission.setCopy(1);
|
||||
permission.setComment(1);
|
||||
// 未实现重命名回调,保持 0
|
||||
permission.setRename(1);
|
||||
permission.setHistory(1);
|
||||
permission.setSaveas(1);
|
||||
return ok(permission);
|
||||
} catch (BizException e) {
|
||||
return fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getUsers(List<String> userIds) {
|
||||
try {
|
||||
if (userIds == null || userIds.isEmpty()) {
|
||||
return ok(Collections.emptyList());
|
||||
}
|
||||
List<Long> ids = userIds.stream()
|
||||
.filter(StringUtils::hasText)
|
||||
.map(Long::valueOf)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
List<AccountEntity> accounts = accountGateway.listByIds(ids);
|
||||
List<WpsUserCO> users = new ArrayList<>();
|
||||
for (AccountEntity account : accounts) {
|
||||
users.add(wpsCmdConvertor.toUserCO(account));
|
||||
}
|
||||
return ok(users);
|
||||
} catch (BizException e) {
|
||||
return fail(e);
|
||||
} catch (Exception e) {
|
||||
log.error("wps getUsers failed, userIds={}", userIds, e);
|
||||
return fail(ErrorCode.UNKNOWN_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> uploadPrepare(String fileId) {
|
||||
try {
|
||||
wpsDocDomainService.getByFileId(fileId);
|
||||
Map<String, Object> data = new LinkedHashMap<>();
|
||||
data.put("digest_types", Collections.singletonList("sha1"));
|
||||
return ok(data);
|
||||
} catch (BizException e) {
|
||||
return fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> uploadAddress(String fileId, Map<String, Object> body) {
|
||||
try {
|
||||
wpsDocDomainService.getByFileId(fileId);
|
||||
if (!StringUtils.hasText(uploadBaseUrl)) {
|
||||
log.error("wps.upload-base-url is empty");
|
||||
return fail(ErrorCode.UNKNOWN_ERROR);
|
||||
}
|
||||
String base = uploadBaseUrl.endsWith("/") ? uploadBaseUrl.substring(0, uploadBaseUrl.length() - 1) : uploadBaseUrl;
|
||||
Map<String, Object> data = new LinkedHashMap<>();
|
||||
data.put("url", base + "/upload/" + fileId);
|
||||
data.put("method", "PUT");
|
||||
return ok(data);
|
||||
} catch (BizException e) {
|
||||
return fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> uploadBinary(String fileId, InputStream inputStream) {
|
||||
try {
|
||||
wpsDocDomainService.getByFileId(fileId);
|
||||
FileInfo ossFileInfo = fileStorageService.of(inputStream, fileId).upload();
|
||||
FileStorage fileStorage = FileStorageHelper.buildResource(ossFileInfo, fileId, true);
|
||||
FileStorage saved = fileStorageDomainService.saveResource(fileStorage);
|
||||
wpsDocDomainService.savePendingUpload(fileId, saved.getId(), saved.getUrl(), saved.getSize());
|
||||
log.info("wps upload binary success, fileId={}, resourceId={}, url={}", fileId, saved.getId(), saved.getUrl());
|
||||
Map<String, Object> data = new LinkedHashMap<>();
|
||||
data.put("status", "ok");
|
||||
return ok(data);
|
||||
} catch (BizException e) {
|
||||
return fail(e);
|
||||
} catch (Exception e) {
|
||||
log.error("wps upload binary failed, fileId={}", fileId, e);
|
||||
return fail(ErrorCode.FILE_UPLOAD_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Map<String, Object> uploadComplete(String fileId, Map<String, Object> body) {
|
||||
try {
|
||||
WpsDocEntity doc = wpsDocDomainService.getByFileId(fileId);
|
||||
if (doc.getPendingResourceId() == null) {
|
||||
throw new BizException(ErrorCode.WPS_UPLOAD_PENDING_NOT_FOUND);
|
||||
}
|
||||
Integer pendingResourceId = doc.getPendingResourceId();
|
||||
String pendingUrl = doc.getPendingDownloadUrl();
|
||||
Long pendingSize = doc.getPendingSize();
|
||||
|
||||
Long userId = AuthUserContextAdapter.getCurrentUserId();
|
||||
String modifierId = userId == null ? doc.getModifierId() : String.valueOf(userId);
|
||||
long nowEpoch = System.currentTimeMillis() / 1000;
|
||||
|
||||
int nextVersion = (doc.getDocVersion() == null ? 1 : doc.getDocVersion()) + 1;
|
||||
String name = doc.getName();
|
||||
Boolean isManual = null;
|
||||
String digestSha1 = null;
|
||||
if (body != null && body.get("request") instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> request = (Map<String, Object>) body.get("request");
|
||||
if (request.get("name") != null) {
|
||||
name = String.valueOf(request.get("name"));
|
||||
}
|
||||
if (request.get("is_manual") != null) {
|
||||
isManual = Boolean.valueOf(String.valueOf(request.get("is_manual")));
|
||||
}
|
||||
Object digestObj = request.get("digest");
|
||||
if (digestObj instanceof Map) {
|
||||
Object sha1 = ((Map<?, ?>) digestObj).get("sha1");
|
||||
if (sha1 != null) {
|
||||
digestSha1 = String.valueOf(sha1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doc.setName(name);
|
||||
doc.setDocVersion(nextVersion);
|
||||
doc.setSize(pendingSize == null ? doc.getSize() : pendingSize);
|
||||
doc.setResourceId(pendingResourceId);
|
||||
doc.setDownloadUrl(pendingUrl);
|
||||
doc.setModifierId(modifierId);
|
||||
doc.setModifyTimeEpoch(nowEpoch);
|
||||
|
||||
WpsDocVersionEntity versionEntity = new WpsDocVersionEntity();
|
||||
versionEntity.setOrgId(doc.getOrgId());
|
||||
versionEntity.setFileId(fileId);
|
||||
versionEntity.setDocVersion(nextVersion);
|
||||
versionEntity.setName(name);
|
||||
versionEntity.setSize(doc.getSize());
|
||||
versionEntity.setResourceId(pendingResourceId);
|
||||
versionEntity.setDownloadUrl(pendingUrl);
|
||||
versionEntity.setDigestSha1(digestSha1);
|
||||
versionEntity.setModifierId(modifierId);
|
||||
versionEntity.setManualFlag(Boolean.TRUE.equals(isManual) ? 1 : 2);
|
||||
versionEntity.setCreateTimeEpoch(nowEpoch);
|
||||
versionEntity.setTenantId(doc.getTenantId());
|
||||
|
||||
WpsDocEntity updated = wpsDocDomainService.completeSave(doc, versionEntity);
|
||||
log.info("wps upload complete, body={}", JSON.toJSONString(body));
|
||||
return ok(wpsCmdConvertor.toFileCO(updated));
|
||||
} catch (BizException e) {
|
||||
return fail(e);
|
||||
} catch (Exception e) {
|
||||
log.error("wps upload complete failed, fileId={}", fileId, e);
|
||||
return fail(ErrorCode.FILE_UPLOAD_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> listVersions(String fileId) {
|
||||
try {
|
||||
WpsDocEntity doc = wpsDocDomainService.getByFileId(fileId);
|
||||
List<WpsDocVersionEntity> versions = wpsDocDomainService.listVersions(fileId);
|
||||
List<WpsFileCO> list = new ArrayList<>();
|
||||
for (WpsDocVersionEntity version : versions) {
|
||||
WpsFileCO co = wpsCmdConvertor.toFileCO(version);
|
||||
co.setCreatorId(doc.getCreatorId());
|
||||
co.setModifierId(version.getModifierId());
|
||||
list.add(co);
|
||||
}
|
||||
return ok(list);
|
||||
} catch (BizException e) {
|
||||
return fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getVersion(String fileId, Integer version) {
|
||||
try {
|
||||
WpsDocEntity doc = wpsDocDomainService.getByFileId(fileId);
|
||||
WpsDocVersionEntity versionEntity = wpsDocDomainService.getVersion(fileId, version);
|
||||
WpsFileCO co = wpsCmdConvertor.toFileCO(versionEntity);
|
||||
co.setCreatorId(doc.getCreatorId());
|
||||
co.setModifierId(versionEntity.getModifierId());
|
||||
return ok(co);
|
||||
} catch (BizException e) {
|
||||
return fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getVersionDownload(String fileId, Integer version) {
|
||||
try {
|
||||
WpsDocVersionEntity versionEntity = wpsDocDomainService.getVersion(fileId, version);
|
||||
Map<String, Object> data = new LinkedHashMap<>();
|
||||
data.put("url", resolveDownloadUrl(versionEntity.getDownloadUrl(), versionEntity.getResourceId()));
|
||||
return ok(data);
|
||||
} catch (BizException e) {
|
||||
return fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> rename(String fileId, Map<String, Object> body) {
|
||||
try {
|
||||
String name = body == null || body.get("name") == null ? null : String.valueOf(body.get("name"));
|
||||
wpsDocDomainService.rename(fileId, name);
|
||||
return ok(Collections.emptyMap());
|
||||
} catch (BizException e) {
|
||||
return fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WpsDocOpenCO myOrCreate() {
|
||||
try {
|
||||
Long userId = requireUserId();
|
||||
String creatorId = String.valueOf(userId);
|
||||
WpsDocEntity existing = wpsDocDomainService.findLatestByCreatorId(creatorId);
|
||||
if (existing != null) {
|
||||
return toOpenCO(existing, false);
|
||||
}
|
||||
WpsDocEntity created = doCreateBlankDoc(userId);
|
||||
return toOpenCO(created, true);
|
||||
} catch (Exception e) {
|
||||
log.error("wps myOrCreate failed", e);
|
||||
throw new BizException(ErrorCode.UNKNOWN_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WpsDocOpenCO createBlank() {
|
||||
try {
|
||||
Long userId = requireUserId();
|
||||
WpsDocEntity created = doCreateBlankDoc(userId);
|
||||
return toOpenCO(created, true);
|
||||
} catch (Exception e) {
|
||||
log.error("wps createBlank failed", e);
|
||||
throw new BizException(ErrorCode.UNKNOWN_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
protected WpsDocEntity doCreateBlankDoc(Long userId) {
|
||||
if (!StringUtils.hasText(defDocxUrl)) {
|
||||
throw new BizException(ErrorCode.WPS_BLANK_DOCX_URL_EMPTY);
|
||||
}
|
||||
Long orgId = requireOrgId();
|
||||
String creatorId = String.valueOf(userId);
|
||||
String fileId = generateFileId();
|
||||
String docName = "未命名文档.docx";
|
||||
long nowEpoch = System.currentTimeMillis() / 1000;
|
||||
|
||||
FileStorage saved;
|
||||
try (InputStream inputStream = new URL(defDocxUrl).openStream()) {
|
||||
FileInfo ossFileInfo = fileStorageService.of(inputStream, docName).upload();
|
||||
FileStorage fileStorage = FileStorageHelper.buildResource(ossFileInfo, docName, true);
|
||||
saved = fileStorageDomainService.saveResource(fileStorage);
|
||||
} catch (BizException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.error("download blank docx failed, url={}", defDocxUrl, e);
|
||||
throw new BizException(ErrorCode.WPS_BLANK_DOCX_DOWNLOAD_FAILED, e);
|
||||
}
|
||||
|
||||
WpsDocEntity doc = new WpsDocEntity();
|
||||
doc.setFileId(fileId);
|
||||
doc.setOrgId(orgId);
|
||||
doc.setName(docName);
|
||||
doc.setDocVersion(1);
|
||||
doc.setSize(saved.getSize() == null ? 0L : saved.getSize());
|
||||
doc.setResourceId(saved.getId());
|
||||
doc.setDownloadUrl(saved.getUrl());
|
||||
doc.setCreatorId(creatorId);
|
||||
doc.setModifierId(creatorId);
|
||||
doc.setCreateTimeEpoch(nowEpoch);
|
||||
doc.setModifyTimeEpoch(nowEpoch);
|
||||
doc.setTenantId(AuthUserContextAdapter.getCurrentTenantId());
|
||||
|
||||
WpsDocVersionEntity versionEntity = new WpsDocVersionEntity();
|
||||
versionEntity.setOrgId(orgId);
|
||||
versionEntity.setFileId(fileId);
|
||||
versionEntity.setDocVersion(1);
|
||||
versionEntity.setName(docName);
|
||||
versionEntity.setSize(doc.getSize());
|
||||
versionEntity.setResourceId(saved.getId());
|
||||
versionEntity.setDownloadUrl(saved.getUrl());
|
||||
versionEntity.setModifierId(creatorId);
|
||||
versionEntity.setManualFlag(1);
|
||||
versionEntity.setCreateTimeEpoch(nowEpoch);
|
||||
versionEntity.setTenantId(doc.getTenantId());
|
||||
|
||||
return wpsDocDomainService.createInitial(doc, versionEntity);
|
||||
}
|
||||
|
||||
private WpsDocOpenCO toOpenCO(WpsDocEntity doc, boolean created) {
|
||||
WpsDocOpenCO co = new WpsDocOpenCO();
|
||||
co.setFileId(doc.getFileId());
|
||||
co.setName(doc.getName());
|
||||
co.setCreated(created);
|
||||
return co;
|
||||
}
|
||||
|
||||
private String generateFileId() {
|
||||
// 字母数字,不超过 47:wps + 32位uuid = 35
|
||||
return "wps" + UUID.randomUUID().toString().replace("-", "");
|
||||
}
|
||||
|
||||
private Long requireUserId() {
|
||||
Long userId = AuthUserContextAdapter.getCurrentUserId();
|
||||
if (userId == null) {
|
||||
throw new BizException(ErrorCode.WPS_USER_NOT_LOGIN);
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
private Long requireOrgId() {
|
||||
Long orgId = AuthUserContextAdapter.getCurrentOrgId();
|
||||
if (orgId != null) {
|
||||
return orgId;
|
||||
}
|
||||
orgId = ThreadLocalUserInfoAdapter.get();
|
||||
if (orgId != null) {
|
||||
return orgId;
|
||||
}
|
||||
if (RequestContextHolder.getRequestAttributes() instanceof ServletRequestAttributes) {
|
||||
orgId = ThreadLocalUserInfoAdapter.getOrgId();
|
||||
}
|
||||
if (orgId == null) {
|
||||
throw new BizException(ErrorCode.ORG_INFO_NOT_FOUND);
|
||||
}
|
||||
return orgId;
|
||||
}
|
||||
|
||||
private String resolveDownloadUrl(String downloadUrl, Integer resourceId) {
|
||||
if (StringUtils.hasText(downloadUrl)) {
|
||||
return downloadUrl;
|
||||
}
|
||||
if (resourceId == null) {
|
||||
throw new BizException(ErrorCode.FILE_NOT_FOUND);
|
||||
}
|
||||
FileStorage resource = fileStorageDomainService.getResource(resourceId);
|
||||
return resource.getUrl();
|
||||
}
|
||||
|
||||
private Map<String, Object> ok(Object data) {
|
||||
Map<String, Object> resp = new LinkedHashMap<>();
|
||||
resp.put("code", 0);
|
||||
resp.put("data", data);
|
||||
return resp;
|
||||
}
|
||||
|
||||
private Map<String, Object> fail(BizException e) {
|
||||
Map<String, Object> resp = new LinkedHashMap<>();
|
||||
resp.put("code", toWpsCode(e.getCode()));
|
||||
resp.put("message", e.getMessage());
|
||||
return resp;
|
||||
}
|
||||
|
||||
private Map<String, Object> fail(ErrorCode errorCode) {
|
||||
Map<String, Object> resp = new LinkedHashMap<>();
|
||||
resp.put("code", toWpsCode(errorCode.getCode()));
|
||||
resp.put("message", errorCode.getMessage());
|
||||
return resp;
|
||||
}
|
||||
|
||||
private int toWpsCode(String errorCode) {
|
||||
if (ErrorCode.WPS_DOC_NOT_FOUND.getCode().equals(errorCode)
|
||||
|| ErrorCode.WPS_VERSION_NOT_FOUND.getCode().equals(errorCode)
|
||||
|| ErrorCode.FILE_NOT_FOUND.getCode().equals(errorCode)) {
|
||||
return 40004;
|
||||
}
|
||||
if (ErrorCode.WPS_USER_NOT_LOGIN.getCode().equals(errorCode)) {
|
||||
return 40005;
|
||||
}
|
||||
return 40001;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package org.qinan.safetyeval.client.api;
|
||||
|
||||
import org.qinan.safetyeval.client.co.WpsDocOpenCO;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* WPS WebOffice 回调接口(Client层)
|
||||
*
|
||||
* @author safety-eval
|
||||
*/
|
||||
public interface WpsApi {
|
||||
|
||||
Map<String, Object> getFileInfo(String fileId);
|
||||
|
||||
Map<String, Object> getDownload(String fileId);
|
||||
|
||||
Map<String, Object> getPermission(String fileId);
|
||||
|
||||
Map<String, Object> getUsers(List<String> userIds);
|
||||
|
||||
Map<String, Object> uploadPrepare(String fileId);
|
||||
|
||||
Map<String, Object> uploadAddress(String fileId, Map<String, Object> body);
|
||||
|
||||
Map<String, Object> uploadComplete(String fileId, Map<String, Object> body);
|
||||
|
||||
Map<String, Object> uploadBinary(String fileId, InputStream inputStream);
|
||||
|
||||
Map<String, Object> listVersions(String fileId);
|
||||
|
||||
Map<String, Object> getVersion(String fileId, Integer version);
|
||||
|
||||
Map<String, Object> getVersionDownload(String fileId, Integer version);
|
||||
|
||||
Map<String, Object> rename(String fileId, Map<String, Object> body);
|
||||
|
||||
/**
|
||||
* 打开页:当前用户有文档则返回最近一份,没有则创建空白 docx
|
||||
*/
|
||||
WpsDocOpenCO myOrCreate();
|
||||
|
||||
/**
|
||||
* 始终新建一份空白 docx(即使用户已有文档)
|
||||
*/
|
||||
WpsDocOpenCO createBlank();
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package org.qinan.safetyeval.client.co;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 打开/新建 WPS 文档返回
|
||||
*
|
||||
* @author safety-eval
|
||||
*/
|
||||
@Data
|
||||
public class WpsDocOpenCO {
|
||||
|
||||
/** WPS file_id,唯一 */
|
||||
@ApiModelProperty(value = "WPS file_id,唯一")
|
||||
private String fileId;
|
||||
/** 文档名称 */
|
||||
@ApiModelProperty(value = "文档名称")
|
||||
private String name;
|
||||
/** 是否本次新建 */
|
||||
@ApiModelProperty(value = "是否本次新建")
|
||||
private Boolean created;
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package org.qinan.safetyeval.client.co;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* WPS 文件信息 CO(官方字段 snake_case)
|
||||
*
|
||||
* @author safety-eval
|
||||
*/
|
||||
@Data
|
||||
public class WpsFileCO {
|
||||
|
||||
private String id;
|
||||
private String name;
|
||||
private Integer version;
|
||||
private Long size;
|
||||
|
||||
@JsonProperty("create_time")
|
||||
private Long createTime;
|
||||
|
||||
@JsonProperty("modify_time")
|
||||
private Long modifyTime;
|
||||
|
||||
@JsonProperty("creator_id")
|
||||
private String creatorId;
|
||||
|
||||
@JsonProperty("modifier_id")
|
||||
private String modifierId;
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package org.qinan.safetyeval.client.co;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* WPS 文档权限 CO(与官方 permission 回调字段一致)
|
||||
*
|
||||
* @author safety-eval
|
||||
*/
|
||||
@Data
|
||||
public class WpsPermissionCO {
|
||||
|
||||
@JsonProperty("user_id")
|
||||
private String userId;
|
||||
|
||||
private Integer read;
|
||||
private Integer update;
|
||||
private Integer download;
|
||||
private Integer rename;
|
||||
private Integer history;
|
||||
private Integer copy;
|
||||
private Integer print;
|
||||
private Integer saveas;
|
||||
private Integer comment;
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package org.qinan.safetyeval.client.co;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* WPS 用户信息 CO
|
||||
*
|
||||
* @author safety-eval
|
||||
*/
|
||||
@Data
|
||||
public class WpsUserCO {
|
||||
|
||||
private String id;
|
||||
private String name;
|
||||
|
||||
@JsonProperty("avatar_url")
|
||||
private String avatarUrl;
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package org.qinan.safetyeval.domain.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* WPS 在线文档领域实体
|
||||
*
|
||||
* @author safety-eval
|
||||
*/
|
||||
@Data
|
||||
public class WpsDocEntity {
|
||||
|
||||
private Long id;
|
||||
private String fileId;
|
||||
private Long orgId;
|
||||
private String name;
|
||||
private Integer docVersion;
|
||||
private Long size;
|
||||
private Integer resourceId;
|
||||
private String downloadUrl;
|
||||
private String creatorId;
|
||||
private String modifierId;
|
||||
private Long createTimeEpoch;
|
||||
private Long modifyTimeEpoch;
|
||||
private Long tenantId;
|
||||
|
||||
/** 三阶段上传待确认 filestorage.id */
|
||||
private Integer pendingResourceId;
|
||||
/** 三阶段上传待确认 URL */
|
||||
private String pendingDownloadUrl;
|
||||
/** 三阶段上传待确认大小 */
|
||||
private Long pendingSize;
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package org.qinan.safetyeval.domain.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* WPS 文档版本领域实体
|
||||
*
|
||||
* @author safety-eval
|
||||
*/
|
||||
@Data
|
||||
public class WpsDocVersionEntity {
|
||||
|
||||
private Long id;
|
||||
private Long orgId;
|
||||
private String fileId;
|
||||
private Integer docVersion;
|
||||
private String name;
|
||||
private Long size;
|
||||
private Integer resourceId;
|
||||
private String downloadUrl;
|
||||
private String digestSha1;
|
||||
private String modifierId;
|
||||
/** 1手动保存 2自动保存 */
|
||||
private Integer manualFlag;
|
||||
private Long createTimeEpoch;
|
||||
private Long tenantId;
|
||||
}
|
||||
|
|
@ -94,6 +94,15 @@ public enum ErrorCode {
|
|||
FILE_URL_GENERATE_FAILED("03-01-007", "文件地址生成失败"),
|
||||
FILE_SIZE_EXCEEDED("03-01-008", "文件大小超出限制"),
|
||||
|
||||
// ---- WPS ----
|
||||
WPS_DOC_NOT_FOUND("05-01-001", "WPS文档不存在"),
|
||||
WPS_USER_NOT_LOGIN("05-01-002", "WPS用户未登录"),
|
||||
WPS_UPLOAD_PENDING_NOT_FOUND("05-01-003", "WPS上传结果不存在"),
|
||||
WPS_VERSION_NOT_FOUND("05-01-004", "WPS文档版本不存在"),
|
||||
WPS_DOC_NAME_INVALID("05-01-005", "WPS文档名称不合法"),
|
||||
WPS_BLANK_DOCX_URL_EMPTY("05-01-006", "WPS空白模板地址未配置"),
|
||||
WPS_BLANK_DOCX_DOWNLOAD_FAILED("05-01-007", "WPS空白模板下载失败"),
|
||||
|
||||
|
||||
/**
|
||||
* 短信码不存在
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import org.qinan.safetyeval.domain.entity.AccountEntity;
|
|||
import org.qinan.safetyeval.domain.query.AccountQuery;
|
||||
import org.qinan.safetyeval.domain.query.PageResult;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户Gateway接口(Domain层定义)
|
||||
*
|
||||
|
|
@ -24,4 +26,6 @@ public interface AccountGateway {
|
|||
PageResult<AccountEntity> page(AccountQuery query);
|
||||
|
||||
AccountEntity foundByPhone(String phone);
|
||||
|
||||
List<AccountEntity> listByIds(List<Long> ids);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
package org.qinan.safetyeval.domain.gateway;
|
||||
|
||||
import org.qinan.safetyeval.domain.entity.WpsDocEntity;
|
||||
|
||||
/**
|
||||
* WPS 文档 Gateway
|
||||
*
|
||||
* @author safety-eval
|
||||
*/
|
||||
public interface WpsDocGateway {
|
||||
|
||||
WpsDocEntity getByFileId(String fileId);
|
||||
|
||||
/**
|
||||
* 当前用户最近一份文档(按 id 倒序)
|
||||
*/
|
||||
WpsDocEntity getLatestByCreatorId(String creatorId);
|
||||
|
||||
WpsDocEntity save(WpsDocEntity entity);
|
||||
|
||||
WpsDocEntity modify(WpsDocEntity entity);
|
||||
|
||||
/**
|
||||
* 清空三阶段上传 pending 字段(需显式写 null)
|
||||
*/
|
||||
void clearPending(Long id);
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package org.qinan.safetyeval.domain.gateway;
|
||||
|
||||
import org.qinan.safetyeval.domain.entity.WpsDocVersionEntity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* WPS 文档版本 Gateway
|
||||
*
|
||||
* @author safety-eval
|
||||
*/
|
||||
public interface WpsDocVersionGateway {
|
||||
|
||||
WpsDocVersionEntity save(WpsDocVersionEntity entity);
|
||||
|
||||
WpsDocVersionEntity getByFileIdAndVersion(String fileId, Integer docVersion);
|
||||
|
||||
List<WpsDocVersionEntity> listByFileIdDesc(String fileId);
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
package org.qinan.safetyeval.domain.service;
|
||||
|
||||
import org.qinan.safetyeval.domain.entity.WpsDocEntity;
|
||||
import org.qinan.safetyeval.domain.entity.WpsDocVersionEntity;
|
||||
import org.qinan.safetyeval.domain.exception.BizException;
|
||||
import org.qinan.safetyeval.domain.exception.ErrorCode;
|
||||
import org.qinan.safetyeval.domain.gateway.WpsDocGateway;
|
||||
import org.qinan.safetyeval.domain.gateway.WpsDocVersionGateway;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* WPS 文档领域服务
|
||||
*
|
||||
* @author safety-eval
|
||||
*/
|
||||
@Service
|
||||
public class WpsDocDomainService {
|
||||
|
||||
private static final Pattern INVALID_NAME_CHARS = Pattern.compile("[\\\\/|:\"*?<>]");
|
||||
|
||||
@Resource
|
||||
private WpsDocGateway wpsDocGateway;
|
||||
@Resource
|
||||
private WpsDocVersionGateway wpsDocVersionGateway;
|
||||
|
||||
public WpsDocEntity getByFileId(String fileId) {
|
||||
WpsDocEntity entity = wpsDocGateway.getByFileId(fileId);
|
||||
if (entity == null) {
|
||||
throw new BizException(ErrorCode.WPS_DOC_NOT_FOUND);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前用户最近一份文档,没有则返回 null
|
||||
*/
|
||||
public WpsDocEntity findLatestByCreatorId(String creatorId) {
|
||||
return wpsDocGateway.getLatestByCreatorId(creatorId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化文档主表 + 首个版本
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public WpsDocEntity createInitial(WpsDocEntity doc, WpsDocVersionEntity versionEntity) {
|
||||
WpsDocEntity saved = wpsDocGateway.save(doc);
|
||||
versionEntity.setFileId(saved.getFileId());
|
||||
versionEntity.setOrgId(saved.getOrgId());
|
||||
versionEntity.setTenantId(saved.getTenantId());
|
||||
wpsDocVersionGateway.save(versionEntity);
|
||||
return saved;
|
||||
}
|
||||
|
||||
public List<WpsDocVersionEntity> listVersions(String fileId) {
|
||||
getByFileId(fileId);
|
||||
return wpsDocVersionGateway.listByFileIdDesc(fileId);
|
||||
}
|
||||
|
||||
public WpsDocVersionEntity getVersion(String fileId, Integer docVersion) {
|
||||
getByFileId(fileId);
|
||||
WpsDocVersionEntity version = wpsDocVersionGateway.getByFileIdAndVersion(fileId, docVersion);
|
||||
if (version == null) {
|
||||
throw new BizException(ErrorCode.WPS_VERSION_NOT_FOUND);
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* 三阶段 PUT 上传成功后写入 pending
|
||||
*/
|
||||
public WpsDocEntity savePendingUpload(String fileId, Integer resourceId, String downloadUrl, Long size) {
|
||||
WpsDocEntity doc = getByFileId(fileId);
|
||||
doc.setPendingResourceId(resourceId);
|
||||
doc.setPendingDownloadUrl(downloadUrl);
|
||||
doc.setPendingSize(size);
|
||||
return wpsDocGateway.modify(doc);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存完成后更新主表、写入版本记录,并清空 pending
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public WpsDocEntity completeSave(WpsDocEntity latest, WpsDocVersionEntity versionEntity) {
|
||||
WpsDocEntity updated = wpsDocGateway.modify(latest);
|
||||
wpsDocVersionGateway.save(versionEntity);
|
||||
wpsDocGateway.clearPending(updated.getId());
|
||||
return wpsDocGateway.getByFileId(updated.getFileId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 重命名文档
|
||||
*/
|
||||
public WpsDocEntity rename(String fileId, String name) {
|
||||
if (!StringUtils.hasText(name) || name.length() > 240 || INVALID_NAME_CHARS.matcher(name).find()) {
|
||||
throw new BizException(ErrorCode.WPS_DOC_NAME_INVALID);
|
||||
}
|
||||
WpsDocEntity doc = getByFileId(fileId);
|
||||
doc.setName(name.trim());
|
||||
doc.setModifyTimeEpoch(System.currentTimeMillis() / 1000);
|
||||
return wpsDocGateway.modify(doc);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package org.qinan.safetyeval.infrastructure.convertor;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.qinan.safetyeval.domain.entity.WpsDocEntity;
|
||||
import org.qinan.safetyeval.infrastructure.dataobject.WpsDocDO;
|
||||
|
||||
/**
|
||||
* WPS 文档 Entity ↔ DO
|
||||
*
|
||||
* @author safety-eval
|
||||
*/
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface WpsDocDoConvertor {
|
||||
|
||||
WpsDocDO toDO(WpsDocEntity entity);
|
||||
|
||||
WpsDocEntity toEntity(WpsDocDO dataObject);
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package org.qinan.safetyeval.infrastructure.convertor;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.qinan.safetyeval.domain.entity.WpsDocVersionEntity;
|
||||
import org.qinan.safetyeval.infrastructure.dataobject.WpsDocVersionDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* WPS 文档版本 Entity ↔ DO
|
||||
*
|
||||
* @author safety-eval
|
||||
*/
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface WpsDocVersionDoConvertor {
|
||||
|
||||
WpsDocVersionDO toDO(WpsDocVersionEntity entity);
|
||||
|
||||
WpsDocVersionEntity toEntity(WpsDocVersionDO dataObject);
|
||||
|
||||
List<WpsDocVersionEntity> toEntityList(List<WpsDocVersionDO> dataObjects);
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package org.qinan.safetyeval.infrastructure.dataobject;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* WPS 文档 DO
|
||||
*
|
||||
* @author safety-eval
|
||||
*/
|
||||
@Data
|
||||
@TableName("wps_doc")
|
||||
public class WpsDocDO {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private String fileId;
|
||||
private Long orgId;
|
||||
private String name;
|
||||
private Integer docVersion;
|
||||
private Long size;
|
||||
private Integer resourceId;
|
||||
private String downloadUrl;
|
||||
private String creatorId;
|
||||
private String modifierId;
|
||||
private Long createTimeEpoch;
|
||||
private Long modifyTimeEpoch;
|
||||
private String deleteEnum;
|
||||
private String remarks;
|
||||
private String createName;
|
||||
private String updateName;
|
||||
private Long tenantId;
|
||||
private Integer version;
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
private Long createId;
|
||||
private Long updateId;
|
||||
private String env;
|
||||
|
||||
private Integer pendingResourceId;
|
||||
private String pendingDownloadUrl;
|
||||
private Long pendingSize;
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package org.qinan.safetyeval.infrastructure.dataobject;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* WPS 文档版本 DO
|
||||
*
|
||||
* @author safety-eval
|
||||
*/
|
||||
@Data
|
||||
@TableName("wps_doc_version")
|
||||
public class WpsDocVersionDO {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private Long orgId;
|
||||
private String fileId;
|
||||
private Integer docVersion;
|
||||
private String name;
|
||||
private Long size;
|
||||
private Integer resourceId;
|
||||
private String downloadUrl;
|
||||
private String digestSha1;
|
||||
private String modifierId;
|
||||
private Integer manualFlag;
|
||||
private Long createTimeEpoch;
|
||||
private String deleteEnum;
|
||||
private Long tenantId;
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
|
|
@ -109,6 +109,19 @@ public class AccountGatewayImpl implements AccountGateway {
|
|||
return toEntity(dataObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AccountEntity> listByIds(List<Long> ids) {
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
return java.util.Collections.emptyList();
|
||||
}
|
||||
LambdaQueryWrapper<AccountDO> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.in(AccountDO::getId, ids);
|
||||
wrapper.eq(AccountDO::getDeleteEnum, "false");
|
||||
return accountMapper.selectList(wrapper).stream()
|
||||
.map(this::toEntity)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private AccountDO toDO(AccountEntity entity) {
|
||||
AccountDO dataObject = new AccountDO();
|
||||
dataObject.setUserName(entity.getUserName());
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
package org.qinan.safetyeval.infrastructure.gatewayimpl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import org.qinan.safetyeval.domain.entity.WpsDocEntity;
|
||||
import org.qinan.safetyeval.domain.gateway.WpsDocGateway;
|
||||
import org.qinan.safetyeval.infrastructure.convertor.WpsDocDoConvertor;
|
||||
import org.qinan.safetyeval.infrastructure.dataobject.WpsDocDO;
|
||||
import org.qinan.safetyeval.infrastructure.mapper.WpsDocMapper;
|
||||
import org.qinan.safetyeval.infrastructure.support.InsertFieldDefaults;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* WPS 文档 Gateway 实现
|
||||
*
|
||||
* @author safety-eval
|
||||
*/
|
||||
@Component
|
||||
public class WpsDocGatewayImpl implements WpsDocGateway {
|
||||
|
||||
@Resource
|
||||
private WpsDocMapper wpsDocMapper;
|
||||
@Resource
|
||||
private WpsDocDoConvertor wpsDocDoConvertor;
|
||||
|
||||
@Override
|
||||
public WpsDocEntity getByFileId(String fileId) {
|
||||
LambdaQueryWrapper<WpsDocDO> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(WpsDocDO::getFileId, fileId);
|
||||
wrapper.eq(WpsDocDO::getDeleteEnum, "false");
|
||||
return wpsDocDoConvertor.toEntity(wpsDocMapper.selectOne(wrapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public WpsDocEntity getLatestByCreatorId(String creatorId) {
|
||||
LambdaQueryWrapper<WpsDocDO> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(WpsDocDO::getCreatorId, creatorId);
|
||||
wrapper.eq(WpsDocDO::getDeleteEnum, "false");
|
||||
wrapper.orderByDesc(WpsDocDO::getId);
|
||||
wrapper.last("limit 1");
|
||||
return wpsDocDoConvertor.toEntity(wpsDocMapper.selectOne(wrapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public WpsDocEntity save(WpsDocEntity entity) {
|
||||
WpsDocDO dataObject = wpsDocDoConvertor.toDO(entity);
|
||||
InsertFieldDefaults.apply(dataObject);
|
||||
wpsDocMapper.insert(dataObject);
|
||||
return wpsDocDoConvertor.toEntity(dataObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WpsDocEntity modify(WpsDocEntity entity) {
|
||||
WpsDocDO dataObject = wpsDocDoConvertor.toDO(entity);
|
||||
dataObject.setId(entity.getId());
|
||||
InsertFieldDefaults.applyForUpdate(dataObject);
|
||||
wpsDocMapper.updateById(dataObject);
|
||||
return getByFileId(entity.getFileId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearPending(Long id) {
|
||||
LambdaUpdateWrapper<WpsDocDO> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(WpsDocDO::getId, id)
|
||||
.set(WpsDocDO::getPendingResourceId, null)
|
||||
.set(WpsDocDO::getPendingDownloadUrl, null)
|
||||
.set(WpsDocDO::getPendingSize, null)
|
||||
.set(WpsDocDO::getUpdateTime, LocalDateTime.now());
|
||||
wpsDocMapper.update(null, wrapper);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package org.qinan.safetyeval.infrastructure.gatewayimpl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.qinan.safetyeval.domain.entity.WpsDocVersionEntity;
|
||||
import org.qinan.safetyeval.domain.gateway.WpsDocVersionGateway;
|
||||
import org.qinan.safetyeval.infrastructure.convertor.WpsDocVersionDoConvertor;
|
||||
import org.qinan.safetyeval.infrastructure.dataobject.WpsDocVersionDO;
|
||||
import org.qinan.safetyeval.infrastructure.mapper.WpsDocVersionMapper;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* WPS 文档版本 Gateway 实现
|
||||
*
|
||||
* @author safety-eval
|
||||
*/
|
||||
@Component
|
||||
public class WpsDocVersionGatewayImpl implements WpsDocVersionGateway {
|
||||
|
||||
@Resource
|
||||
private WpsDocVersionMapper wpsDocVersionMapper;
|
||||
@Resource
|
||||
private WpsDocVersionDoConvertor wpsDocVersionDoConvertor;
|
||||
|
||||
@Override
|
||||
public WpsDocVersionEntity save(WpsDocVersionEntity entity) {
|
||||
WpsDocVersionDO dataObject = wpsDocVersionDoConvertor.toDO(entity);
|
||||
if (dataObject.getDeleteEnum() == null) {
|
||||
dataObject.setDeleteEnum("false");
|
||||
}
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
if (dataObject.getCreateTime() == null) {
|
||||
dataObject.setCreateTime(now);
|
||||
}
|
||||
if (dataObject.getUpdateTime() == null) {
|
||||
dataObject.setUpdateTime(now);
|
||||
}
|
||||
wpsDocVersionMapper.insert(dataObject);
|
||||
return wpsDocVersionDoConvertor.toEntity(dataObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WpsDocVersionEntity getByFileIdAndVersion(String fileId, Integer docVersion) {
|
||||
LambdaQueryWrapper<WpsDocVersionDO> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(WpsDocVersionDO::getFileId, fileId);
|
||||
wrapper.eq(WpsDocVersionDO::getDocVersion, docVersion);
|
||||
wrapper.eq(WpsDocVersionDO::getDeleteEnum, "false");
|
||||
return wpsDocVersionDoConvertor.toEntity(wpsDocVersionMapper.selectOne(wrapper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<WpsDocVersionEntity> listByFileIdDesc(String fileId) {
|
||||
LambdaQueryWrapper<WpsDocVersionDO> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(WpsDocVersionDO::getFileId, fileId);
|
||||
wrapper.eq(WpsDocVersionDO::getDeleteEnum, "false");
|
||||
wrapper.orderByDesc(WpsDocVersionDO::getDocVersion);
|
||||
return wpsDocVersionDoConvertor.toEntityList(wpsDocVersionMapper.selectList(wrapper));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package org.qinan.safetyeval.infrastructure.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.qinan.safetyeval.infrastructure.dataobject.WpsDocDO;
|
||||
|
||||
/**
|
||||
* WPS 文档 Mapper
|
||||
*
|
||||
* @author safety-eval
|
||||
*/
|
||||
@Mapper
|
||||
public interface WpsDocMapper extends BaseMapper<WpsDocDO> {
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package org.qinan.safetyeval.infrastructure.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.qinan.safetyeval.infrastructure.dataobject.WpsDocVersionDO;
|
||||
|
||||
/**
|
||||
* WPS 文档版本 Mapper
|
||||
*
|
||||
* @author safety-eval
|
||||
*/
|
||||
@Mapper
|
||||
public interface WpsDocVersionMapper extends BaseMapper<WpsDocVersionDO> {
|
||||
}
|
||||
|
|
@ -171,3 +171,7 @@ def:
|
|||
password: a123456
|
||||
publicKey: 0402df2195296d4062ac85ad766994d73e871b887e18efb9a9a06b4cebc72372869b7da6c347c129dee2b46a0f279ff066b01c76208c2a052af75977c722a2ccee
|
||||
reviewEnable: false
|
||||
|
||||
wps:
|
||||
upload-base-url: ${WPS_UPLOAD_BASE_URL:https://gbs-gateway.qhdsafety.com/safetyEval-h5/wsp}
|
||||
def-docx-url: https://test-dragon-yf-pub.oss-cn-hangzhou.aliyuncs.com/jjb/6a59d83ee4b0b4541389511f.docx
|
||||
|
|
|
|||
Loading…
Reference in New Issue