package com.fdkankan.fusion.service.impl;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.thread.ThreadUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fdkankan.fusion.common.PageInfo;
import com.fdkankan.fusion.common.util.*;
import com.fdkankan.fusion.common.FilePath;
import com.fdkankan.fusion.common.ResultCode;
import com.fdkankan.fusion.entity.*;
import com.fdkankan.fusion.exception.BusinessException;
import com.fdkankan.fusion.mapper.IModelMapper;
import com.fdkankan.fusion.request.ModelPram;
import com.fdkankan.fusion.response.SceneVo;
import com.fdkankan.fusion.service.ICaseNumService;
import com.fdkankan.fusion.service.ICaseService;
import com.fdkankan.fusion.service.IFusionNumService;
import com.fdkankan.fusion.service.IModelService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fdkankan.redis.util.RedisUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
/**
*
* 服务实现类
*
*
* @author
* @since 2022-08-03
*/
@Service
@Slf4j
public class ModelServiceImpl extends ServiceImpl implements IModelService {
@Autowired
UploadToOssUtil uploadToOssUtil;
@Autowired
UploadService uploadService;
@Autowired
ICaseNumService caseNumService;
@Autowired
ICaseService caseService;
@Autowired
IFusionNumService fusionNumService;
@Autowired
RedisUtil redisUtil;
@Value("${upload.query-path}")
private String queryPath;
@Value("${spring.profiles.active}")
private String environment;
@Override
public Model uploadObj(MultipartFile file, String username) throws Exception {
if(file.isEmpty()){
throw new BusinessException(ResultCode.UPLOAD_FILE_NO_EXIST);
}
if(file.getSize()>10 * 1024 * 1024 * 100){
throw new BusinessException(ResultCode.UPLOAD_FILE_TO_LONG);
}
//获取文件名
String fileName = file.getOriginalFilename();
if(StringUtils.isEmpty(fileName)){
throw new BusinessException(ResultCode.UPLOAD_FILE_NO_EXIST);
}
if(com.fdkankan.fusion.common.util.StringUtils.isChinese(fileName)){
throw new BusinessException(ResultCode.UPLOAD_FILE_CHINA_NAME);
}
if(fileName.length() >50){
throw new BusinessException(ResultCode.UPLOAD_FILE_NAME_TO_LONG);
}
if(!fileName.toLowerCase().endsWith(".zip")){
throw new BusinessException(ResultCode.UPLOAD_FILE_TYPE_ERROR);
}
//获取文件后缀名
String modelName = fileName.substring(0,fileName.lastIndexOf("."));
Model model = new Model();
model.setModelTitle(modelName);
model.setModelSize(FileWriterUtil.setFileSize(file.getSize()));
model.setUserName(username);
this.save(model);
redisUtil.set(RedisKeyUtil.modelUpload+model.getModelId(),"0");
runThread(file,model,this);
return model;
}
private void setCreateStatus(Model model,Integer status){
String redisKey = RedisKeyUtil.modelCancelUpload+model.getModelId();
if(redisUtil.hasKey(redisKey)){
if(redisUtil.get(redisKey).equals("-2")){
return;
}
}
model.setCreateStatus(status);
this.saveOrUpdate(model);
if(status != 1){
redisUtil.set(RedisKeyUtil.modelUpload+model.getModelId(),status.toString());
}
}
public void runThread(MultipartFile file,Model model,IModelService modelService){
ExecutorService executor = ThreadUtil.newSingleExecutor();
try {
CompletableFuture.runAsync(() -> {
File newObjFile = null;
File objPathFile = null;
File mntFile = null;
try {
String fileName = UUID.randomUUID().toString().replace("-","") +".zip";
String objPath = String.format(FilePath.OBJ_LOCAL_PATH,environment , "modelId_"+model.getModelId()) ;
log.info("uploadObj--ThreadStart-fileName:{},modeId:{}",fileName,model.getModelId());
newObjFile = new File(objPath +"/" + fileName);
if(!newObjFile.getParentFile().exists()){
newObjFile.mkdirs();
}
file.transferTo(newObjFile);
if(fileName.toLowerCase().endsWith(".zip")){
ShellUtil.unZip(newObjFile.getPath(),objPath);
}
objPathFile = new File(objPath );
if(!objPathFile.isDirectory()){
throw new BusinessException(-1,"解压错误");
}
List fileList = new ArrayList<>();
FileWriterUtil.getCanRunList(fileList,objPathFile);
if(fileList.size() <=0){
throw new BusinessException(-1,"可上传文件不存在");
}
File file1 = fileList.get(0);
if(file1 == null){
throw new BusinessException(-1,"可上传文件不存在");
}
if(com.fdkankan.fusion.common.util.StringUtils.isChinese(file1.getName())){
throw new BusinessException(-1,"压缩包中文");
}
String b3dmJsonPath = null;
if(file1.getName().endsWith(".b3dm") ){
b3dmJsonPath = FileWriterUtil.checkB3dmTileset(objPathFile);
if(b3dmJsonPath == null){
throw new BusinessException(-1,"缺少tileset.json文件");
}
}
redisUtil.set(RedisKeyUtil.modelUpload+model.getModelId(),"20");
String glbOssPath = String.format(FilePath.GLB_OSS_PATH,environment, model.getModelId());
String name = file1.getName();
if(name.contains("obj") || name.contains("OBJ")){
glbOssPath = glbOssPath.replace("mesh.glb",file1.getName().replace(".obj",".glb"));
model.setModelDateType("obj");
model.setModelType("glb");
OBJToGLBUtil.objToGlb2(file1.getPath(), file1.getPath().replace(".obj",".glb"));
redisUtil.set(RedisKeyUtil.modelUpload+model.getModelId(),"65");
uploadToOssUtil.uploadOss(file1.getPath().replace(".obj",".glb"),glbOssPath);
if(!uploadToOssUtil.existKey(glbOssPath)){
throw new BusinessException(-1,"缺少.glb文件");
}
}
if(name.contains(".ply")){
model.setModelDateType("ply");
model.setModelType("ply");
}
if(name.contains(".las")){
model.setModelDateType("las");
model.setModelType("las");
}
if("las".equals(model.getModelType()) || "ply".equals(model.getModelType()) ){
mntFile = OBJToGLBUtil.lasOrPlyToBin(file1);
glbOssPath = mntFile.getPath().replace("/mnt/","")+"/webcloud";
redisUtil.set(RedisKeyUtil.modelUpload+model.getModelId(),"65");
uploadToOssUtil.uploadFileOss(mntFile );
if(!uploadToOssUtil.existKey(glbOssPath+"/cloud.js")){
throw new BusinessException(-1,"缺少cloud.js文件");
}
}
model.setModelGlbUrl(JSONArray.toJSONString(Arrays.asList(queryPath + glbOssPath)));
String b3dmPath = objPathFile.getPath().replace(FilePath.LOCAL_BASE_PATH,"fusion/");
if(name.contains(".osgb")){
model.setModelDateType("osgb");
model.setModelType("b3dm");
redisUtil.set(RedisKeyUtil.modelUpload+model.getModelId(),"60");
String localPath = OBJToGLBUtil.OsgbToB3dm(objPathFile);
String jsonPath = null;
jsonPath = FileWriterUtil.checkB3dmTileset(objPathFile);
if(jsonPath == null){
throw new BusinessException(-1,"缺少tileset.json文件");
}
redisUtil.set(RedisKeyUtil.modelUpload+model.getModelId(),"80");
ShellUtil.yunUpload(localPath,b3dmPath);
model.setModelGlbUrl((JSONArray.toJSONString(Arrays.asList(queryPath + jsonPath.replace(FilePath.LOCAL_BASE_PATH,"fusion/")))));
//FileUtil.del(localPath);
}
if(name.contains(".b3dm") && b3dmJsonPath != null){
model.setModelDateType("b3dm");
model.setModelType("b3dm");
redisUtil.set(RedisKeyUtil.modelUpload+model.getModelId(),"60");
ShellUtil.yunUpload(objPathFile.getPath(),b3dmPath);
model.setModelGlbUrl((JSONArray.toJSONString(Arrays.asList(queryPath + b3dmJsonPath.replace(FilePath.LOCAL_BASE_PATH,"fusion/")))));
}
setCreateStatus(model,1);
modelService.saveOrUpdate(model);
redisUtil.set(RedisKeyUtil.modelUpload+model.getModelId(),"100");
}catch (Exception e){
setCreateStatus(model,-1);
log.error("uploadObj--ThreadError-modeId:{},error:{}",model.getModelId(),e);
}finally {
if(newObjFile!=null){
FileUtil.del(newObjFile);
}
if(objPathFile!=null){
FileUtil.del(objPathFile);
}
if(mntFile!=null){
FileUtil.del(mntFile.getParentFile());
}
}
}, executor).whenComplete((reslut, e) -> {
log.info("uploadObj--ThreadEnd-modeId:{}",model.getModelId());
});
}catch (Exception e){
setCreateStatus(model,-1);
log.error("uploadObj--ThreadError-modeId:{},error:{}",model.getModelId(),e);
}
}
@Override
public PageInfo pageList(ModelPram param, String token) {
String username = JwtUtil.getUsername(token);
LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Model::getUserName,username);
wrapper.eq(Model::getType,3);
wrapper.notIn(Model::getCreateStatus,-2);
if(param.getStatus()!=null){ //参数2为成功,数据库中成功为1
wrapper.eq(Model::getCreateStatus,param.getStatus() == 2 ? 1 :param.getStatus());
}
if(StringUtils.isNotBlank(param.getModelTitle())){
wrapper.like(Model::getModelTitle,param.getModelTitle());
}
wrapper.orderByDesc(Model::getCreateTime);
Page page = this.page(new Page<>(param.getPageNum(),param.getPageSize()),wrapper);
for (Model model : page.getRecords()) {
if(model.getType() == 3 && StringUtils.isEmpty(model.getNum())) {
model.setNum(model.getModelId().toString());
}
}
return PageInfo.PageInfo(page);
}
@Override
public void delete(Integer modelId) {
List caseNumEntityList = caseNumService.getByNum(modelId.toString());
List fusionNumList = fusionNumService.getByNum(modelId.toString());
if(caseNumEntityList.size() >0 || fusionNumList.size() >0){
StringBuilder title = new StringBuilder();
List caseIdIds = caseNumEntityList.parallelStream().map(CaseNumEntity::getCaseId).collect(Collectors.toList());
if(caseIdIds.size() >0){
List list = caseService.getByIds(caseIdIds);
List collect = list.parallelStream().map(CaseEntity::getCaseTitle).collect(Collectors.toList());
for (String str : collect) {
title.append(str).append(",");
}
if(title.length()>0){
title.delete(title.length()-1,title.length());
}
}
throw new BusinessException(ResultCode.CASE_USE.code, String.format(ResultCode.CASE_USE.msg,title));
}
Model model = this.getById(modelId);
if(model == null ){
throw new BusinessException(ResultCode.MODEL_NOT_EXIST);
}
this.removeById(modelId);
fusionNumService.deleteByModelId(modelId);
if(StringUtils.isNotBlank(model.getModelGlbUrl())){
uploadService.deleteOssUrl(model.getModelGlbUrl());
}
}
@Override
public List getListByNum(List numList) {
LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
wrapper.in(Model::getNum,numList);
return this.list(wrapper);
}
@Override
public List getListByModelIds(List modelIds) {
LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
wrapper.in(Model::getModelId,modelIds);
return this.list(wrapper);
}
@Override
public List getListByModelIdStrs(List numList) {
LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
wrapper.in(Model::getModelId,numList);
wrapper.orderByDesc(Model::getCreateTime);
return this.list(wrapper);
}
@Override
public void deleteByNum(List numList) {
LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
wrapper.in(Model::getNum,numList);
this.remove(wrapper);
}
@Override
public List getByUserName(String username) {
LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Model::getUserName,username)
.eq(Model::getType,3);
return this.list(wrapper);
}
@Override
public Model getIsNullNewByNum(String num,Integer type) {
LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Model::getNum,num);
wrapper.eq(Model::getType,type);
Model model = this.getOne(wrapper);
if(model == null){
model = new Model();
}
return model;
}
@Override
public Object getInfo(Integer modelId) {
Model model = this.getById(modelId);
if(model == null){
throw new BusinessException(ResultCode.SCENE_NOT_EXIST);
}
SceneVo sceneVo = new SceneVo();
BeanUtils.copyProperties(model,sceneVo);
return sceneVo;
}
@Override
public String uploadObjProgress(Integer modelId) {
String redisKey = RedisKeyUtil.modelUpload+modelId;
String cancel = RedisKeyUtil.modelCancelUpload+modelId;
if(redisUtil.hasKey(cancel)){
return redisUtil.get(cancel);
}
if(redisUtil.hasKey(redisKey)){
return redisUtil.get(redisKey);
}
return "0";
}
@Override
public void cancelUpload(Integer modelId) {
String redisKey = RedisKeyUtil.modelCancelUpload+modelId;
if(redisUtil.hasKey(redisKey)){
return;
}
Model model = this.getById(modelId);
if(model == null){
return;
}
model.setCreateStatus(-2);
this.updateById(model);
redisUtil.set(redisKey,"-2");
}
@Override
public HashMap getMapByNum(List numList) {
HashMap map = new HashMap<>();
List modelList = this.getListByNum(numList);
modelList.forEach(entity-> map.put(entity.getNum() + entity.getType(),entity));
return map;
}
}