java模板导出excel和word,并且打压缩包发回给后台

发布于:2024-10-18 ⋅ 阅读:(11) ⋅ 点赞:(0)
package com.xhsoft.flange.controller;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.xhsoft.app.constant.ResponseConstant;
import com.xhsoft.base.entity.CheckerInfo;
import com.xhsoft.base.entity.OperatorInfo;
import com.xhsoft.base.entity.ProjectInfo;
import com.xhsoft.base.entity.ToolInfo;
import com.xhsoft.base.service.ICheckerInfoService;
import com.xhsoft.base.service.IOperatorInfoService;
import com.xhsoft.base.service.IProjectInfoService;
import com.xhsoft.base.service.IToolInfoService;
import com.xhsoft.common.cache.CacheNames;
import com.xhsoft.common.cache.DictBizCache;
import com.xhsoft.common.enums.*;
import com.xhsoft.common.props.TePathProperties;
import com.xhsoft.common.utils.CommonUtil;
import com.xhsoft.common.utils.ExportUtil;
import com.xhsoft.common.utils.ZipUtil;
import com.xhsoft.data.entity.FlangeSize;
import com.xhsoft.data.entity.FlangeTorque;
import com.xhsoft.data.service.IFlangeSizeService;
import com.xhsoft.data.service.IFlangeTorqueService;
import com.xhsoft.flange.entity.DrawingJoint;
import com.xhsoft.flange.entity.FlangeJoint;
import com.xhsoft.flange.entity.FlangeJointHistory;
import com.xhsoft.flange.entity.FlangeJointSourceHistoryEntity;
import com.xhsoft.flange.excel.FlangeSizeExcel;
import com.xhsoft.flange.excel.FlangeTorqueExcel;
import com.xhsoft.flange.excel.JointExcel;
import com.xhsoft.flange.service.IDrawingJointService;
import com.xhsoft.flange.service.IFlangeJointHistoryService;
import com.xhsoft.flange.service.IFlangeJointService;
import com.xhsoft.flange.service.IFlangeJointSourceHistoryService;
import com.xhsoft.flange.to.FlangeJointTO;
import com.xhsoft.flange.to.ImportJoints;
import com.xhsoft.flange.to.JointCheckInfo;
import com.xhsoft.flange.to.JointImportInfo;
import com.xhsoft.flange.tool.FlangeJointTool;
import com.xhsoft.flange.vo.FlangeJointVO;
import com.xhsoft.modules.system.entity.Dict;
import com.xhsoft.modules.system.entity.DictBiz;
import com.xhsoft.modules.system.mapper.DictBizMapper;
import com.xhsoft.sys.service.IReportSnoService;
import com.xhsoft.sys.tool.LogTool;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.boot.ctrl.BladeController;
import org.springblade.core.excel.util.ExcelUtil;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.constant.BladeConstant;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.IoUtil;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.annotations.ApiIgnore;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.servlet.ServletOutputStream;

import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.*;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 法兰节点表 控制器
 *
 * @author com.xhsoft
 * @since 2022-11-04
 */
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/flange/flangejoint")
@Api(value = "法兰节点表", tags = "法兰节点表接口")
public class FlangeJointController extends BladeController {

	private TePathProperties pathProperties;

	private IProjectInfoService projectInfoService;
	private IFlangeJointService flangeJointService;
	private IDrawingJointService drawingJointService;
	private IFlangeJointHistoryService flangeJointHistoryService;
	private IOperatorInfoService operatorInfoService;
	private ICheckerInfoService checkerInfoService;
	private IToolInfoService toolInfoService;
	private IReportSnoService reportSnoService;
	private final BladeRedis bladeRedis;
	private LogTool logTool;

	private final IFlangeTorqueService flangeTorqueService;

	private final IFlangeSizeService flangeSizeService;

	private final DictBizMapper dictBizMapper;

	private final IFlangeJointSourceHistoryService flangeJointSourceHistoryService;

	/**
	 * 详情
	 */
	@GetMapping("/detail")
	@ApiOperationSupport(order = 1)
	@ApiOperation(value = "详情", notes = "传入flangeJoint")
	public R<FlangeJointVO> detail(FlangeJoint flangeJoint) {
		FlangeJoint detail = flangeJointService.getOne(Condition.getQueryWrapper(flangeJoint));
		FlangeJointVO vo = null;
		if(detail != null){
			vo = JSON.parseObject(JSON.toJSONString(detail), FlangeJointVO.class);
			Long projectId = detail.getProjectId();
			ProjectInfo project = projectInfoService.getById(projectId);
			if(project != null){
				vo.setCustName(project.getCustName());
			}
		}
		return R.data(vo);
	}

	/**
	 * 分页 法兰节点表
	 */
	@GetMapping("/list")
	@ApiOperationSupport(order = 2)
	@ApiOperation(value = "分页", notes = "传入flangeJoint")
	public R<IPage<FlangeJoint>> list(FlangeJoint flangeJoint, Query query) {
		QueryWrapper<FlangeJoint> wrapper = Condition.getQueryWrapper(flangeJoint);
		wrapper.lambda()
//			.orderByDesc(FlangeJoint::getCreateTime)
			.orderByAsc(FlangeJoint::getPackageNo,FlangeJoint::getJointNo);
		IPage<FlangeJoint> pages = flangeJointService.page(Condition.getPage(query), wrapper);
		return R.data(pages);
	}

	/**
     * 全部 法兰节点表
     */
    @GetMapping("/all")
    @ApiOperationSupport(order = 3)
    @ApiOperation(value = "全部", notes = "传入flangeJoint")
    public R<List<FlangeJoint>> all(FlangeJointTO flangeJoint) {
        List<FlangeJoint> list = flangeJointService.list(Condition.getQueryWrapper(flangeJoint));
        return R.data(list);
    }



	/**
	 * 新增 法兰节点表
	 */
	@PostMapping("/save")
	@ApiOperationSupport(order = 4)
	@ApiOperation(value = "新增", notes = "传入flangeJoint")
	public R save(@Valid @RequestBody FlangeJoint flangeJoint) {
		return R.status(flangeJointService.save(flangeJoint));
	}

	/**
	 * 修改 法兰节点表
	 */
	@PostMapping("/update")
	@ApiOperationSupport(order = 5)
	@ApiOperation(value = "修改", notes = "传入flangeJoint")
	public R update(@Valid @RequestBody FlangeJoint flangeJoint) {
		return R.status(flangeJointService.updateById(flangeJoint));
	}

	/**
	 * 新增或修改 法兰节点表
	 */
	@PostMapping("/submit")
	@ApiOperationSupport(order = 6)
	@ApiOperation(value = "新增或修改", notes = "传入flangeJoint")
	public R submit(@Valid @RequestBody FlangeJoint flangeJoint) {
		boolean isNew = flangeJoint.getId() == null;

		FlangeJoint check = new FlangeJoint();
		check.setProjectId(flangeJoint.getProjectId());
		check.setJointNo(flangeJoint.getJointNo());
		check.setPackageNo(flangeJoint.getPackageNo());
//		check.setLabelNo(flangeJoint.getLabelNo());   //2024-10-17 客户要求修改成 节点id+包id+项目id
		List<FlangeJoint> has = flangeJointService.list(Condition.getQueryWrapper(check));
		if (isNew){
			if(has.size() > 0){
				log.error("节点{}已存在",flangeJoint.getLabelNo());
				return R.fail(StatusEnum.NODE_EXIST.getCode());
			}
		}else {
			if(has.size() > 1){
				log.error("节点{}已存在",flangeJoint.getLabelNo());
				return R.fail(StatusEnum.NODE_EXIST.getCode());
			}
		}

		Boolean b = flangeJointService.saveOrUpdate(flangeJoint);
		if(isNew){
			logTool.insert(flangeJoint.getProjectId(), SysEnum.FLANGE, ModuleEnum.JOINT,
				"添加:"+ JSON.toJSONString(flangeJoint));
		} else {
			Long id = flangeJoint.getId();
			FlangeJoint joint = flangeJointService.getById(id);

			logTool.insert(flangeJoint.getProjectId(), SysEnum.FLANGE, ModuleEnum.JOINT,
				"修改:原数据"+JSON.toJSONString(joint)+",新数据"+JSON.toJSONString(flangeJoint));
		}

		return R.status(b);
	}


	/**
	 * 删除 法兰节点表
	 */
	@PostMapping("/remove")
	@ApiOperationSupport(order = 7)
	@ApiOperation(value = "逻辑删除", notes = "传入ids")
	public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) {
		List<Long> idList = Func.toLongList(ids);
		for(Long id : idList){
			FlangeJoint flangeJoint = flangeJointService.getById(id);
			if(flangeJoint != null){
				logTool.insert(flangeJoint.getProjectId(), SysEnum.FLANGE, ModuleEnum.JOINT,
					"删除:"+ JSON.toJSONString(flangeJoint));
			}
		}
		return R.status(flangeJointService.deleteLogic(idList));
	}


	/**
     * 自定义分页 法兰节点表
	 *
	 * 	用于法兰节点页面列表数据查询
	 *
     */
    @GetMapping("/page")
    @ApiOperationSupport(order = 9)
    @ApiOperation(value = "分页", notes = "传入flangeJoint")
    public R<IPage<FlangeJointVO>> page(FlangeJoint flangeJoint, Query query) {
		QueryWrapper<FlangeJoint> wrapper = Condition.getQueryWrapper(flangeJoint);
		if (flangeJoint.getProjectId() != null){
			wrapper.lambda().eq(FlangeJoint::getProjectId, flangeJoint.getProjectId());
		}
		if (flangeJoint.getJointNo() != null){
			wrapper.lambda().like(FlangeJoint::getJointNo, flangeJoint.getJointNo());
		}
		if (flangeJoint.getPackageNo() != null){
			wrapper.lambda().like(FlangeJoint::getPackageNo, flangeJoint.getPackageNo());
		}
		if (flangeJoint.getPid() != null){
			wrapper.lambda().like(FlangeJoint::getPid, flangeJoint.getPid());
		}
		if (flangeJoint.getIsometricNo() != null){
			wrapper.lambda().like(FlangeJoint::getIsometricNo, flangeJoint.getIsometricNo());
		}
		if (flangeJoint.getStatus() != null){
			wrapper.lambda().eq(FlangeJoint::getStatus, flangeJoint.getStatus());
		}
		wrapper.lambda().orderByAsc(FlangeJoint::getPackageNo,FlangeJoint::getJointNo);

		IPage<FlangeJoint> pages = flangeJointService.selectPageWithJointState(Condition.getPage(query), wrapper, flangeJoint); // 2024-10-15 增加 查询条件,联表查询是否关联图纸(仅关联,仅未关联)
//		IPage<FlangeJoint> pages = flangeJointService.page(Condition.getPage(query), wrapper);

		IPage<FlangeJointVO> vopages = pages.convert(j -> {
			FlangeJointVO vo = new FlangeJointVO();
			BeanUtils.copyProperties(j, vo);

			DrawingJoint condition = new DrawingJoint();
			condition.setJointId(j.getId());
			List<DrawingJoint> djs = drawingJointService.list(Condition.getQueryWrapper(condition));
			if(djs != null && djs.size()>0){
				DrawingJoint dj = djs.get(0);
				vo.setDrawingId(dj.getDrawingId());
			}
			return vo;
		});

        return R.data(vopages);
    }


	/**
	 * 变更法兰节点状态
	 */
	@PostMapping("/status")
	@ApiOperationSupport(order = 11)
	@ApiOperation(value = "新增", notes = "传入flangeJointHistory")
	public R savetatus(@Valid @RequestBody FlangeJointHistory flangeJointHistory) {
		Long id = flangeJointHistory.getJointId();
		FlangeJoint joint = flangeJointService.getById(id);
		if(joint == null){
//			return R.fail("法兰节点不存在!");
			return R.fail(StatusEnum.FLANGE_NODE_NOT_EXIST.getCode());
		}
		Integer currentStatus = joint.getStatus();
		Integer oldStatus = flangeJointHistory.getBef();
		if(currentStatus != oldStatus) {
//			return R.fail("节点状态已经变更,请检查后重试!");
			return R.fail(StatusEnum.NODE_STATUS_CHANGED.getCode());
		}
		joint.setStatus(flangeJointHistory.getAft());
		flangeJointService.updateById(joint);

		flangeJointHistory.setAccount(AuthUtil.getUserAccount());
		flangeJointHistory.setName(AuthUtil.getUserName());
		flangeJointHistory.setTime(LocalDateTime.now());
		flangeJointHistoryService.save(flangeJointHistory);

		logTool.insert(flangeJointHistory.getProjectId(), SysEnum.FLANGE, ModuleEnum.JOINT,
			"状态变更:"+ JSON.toJSONString(flangeJointHistory));

		return R.success(StatusEnum.NODE_STATUS_UPDATE_SUCCESS.getCode());
	}

	/**
	 * 查询法兰节点状态变更历史
	 */
	@GetMapping("/status")
	@ApiOperationSupport(order = 12)
	@ApiOperation(value = "全部", notes = "传入flangeJointHistory")
	public R<List<FlangeJointHistory>> selectStatus(FlangeJointHistory flangeJointHistory) {
		QueryWrapper<FlangeJointHistory> wrapper = Condition.getQueryWrapper(flangeJointHistory);
		wrapper.lambda().orderByDesc(FlangeJointHistory::getTime);
		List<FlangeJointHistory> list = flangeJointHistoryService.list(wrapper);
		return R.data(list);
	}
	/**
	 * 变更法兰节点状态
	 */
	@PostMapping("/source")
	@ApiOperationSupport(order = 11)
	@ApiOperation(value = "新增", notes = "传入flangeJointHistory")
	public R saveSources(@Valid @RequestBody FlangeJointSourceHistoryEntity flangeJointSourceHistory) {
		Long id = flangeJointSourceHistory.getJointId();
		FlangeJoint joint = flangeJointService.getById(id);
		if(joint == null){
//			return R.fail("法兰节点不存在!");
			return R.fail(StatusEnum.FLANGE_NODE_NOT_EXIST.getCode());
		}
		Integer currentSource = joint.getJointSource();
		Integer oldSource = flangeJointSourceHistory.getBef();
		if(currentSource != oldSource) {
//			return R.fail("节点状态已经变更,请检查后重试!");
			return R.fail(StatusEnum.NODE_SOURCE_STATUS_CHANGED.getCode());
		}
		joint.setJointSource(flangeJointSourceHistory.getAft());
		flangeJointService.updateById(joint);

		flangeJointSourceHistory.setAccount(AuthUtil.getUserAccount());
		flangeJointSourceHistory.setName(AuthUtil.getUserName());
		flangeJointSourceHistory.setTime(LocalDateTime.now());
		flangeJointSourceHistoryService.save(flangeJointSourceHistory);

		logTool.insert(flangeJointSourceHistory.getProjectId(), SysEnum.FLANGE, ModuleEnum.JOINT,
			"状态变更:"+ JSON.toJSONString(flangeJointSourceHistory));

		return R.success(StatusEnum.NODE_SOURCE_UPDATE_SUCCESS.getCode());
	}

	/**
	 * 查询法兰节点来源变更历史
	 */
	@GetMapping("/source")
	@ApiOperationSupport(order = 14)
	@ApiOperation(value = "全部", notes = "传入flangeJointHistory")
	public R<List<FlangeJointSourceHistoryEntity>> selectSources(FlangeJointSourceHistoryEntity flangeJointSourceHistory) {
		QueryWrapper<FlangeJointSourceHistoryEntity> wrapper = Condition.getQueryWrapper(flangeJointSourceHistory);
		wrapper.lambda().orderByDesc(FlangeJointSourceHistoryEntity::getTime);
		List<FlangeJointSourceHistoryEntity> list = flangeJointSourceHistoryService.list(wrapper);
		return R.data(list);
	}


	/**
	 * 上传节点excel,并检查内容
	 */
	@PostMapping("/upload/check")
	@ApiOperationSupport(order = 13)
	@ApiOperation(value = "校验节点excel", notes = "节点excel")
	public R<JointCheckInfo> putFile(@RequestParam MultipartFile file, @RequestParam Long projectId) {
		if (file.isEmpty()) {//判断文件是否为空
			return R.fail(StatusEnum.INVALID_DATA_UPLOAD.getCode());
		}
		String fileName = file.getOriginalFilename(); //获得文件名
		if(!fileName.endsWith("xlsx") && !fileName.endsWith("xls")){
			log.error(fileName + "不是excel文件");
			// TODO: 2023-06-26 拼接的一会再做
			return R.fail(fileName + "不是excel压缩文件");
		}
		String uuid = UUID.randomUUID().toString();

		String dir = CommonUtil.SDF_DATE.format(new Date());

		List<JointExcel> data = null;
		try {
			data = ExcelUtil.read(file,0,3, JointExcel.class);
		} catch (Exception e) {
			Integer rowIndex = ((ExcelDataConvertException)e.getCause()).getRowIndex()+1;
			Integer columnIndex = ((ExcelDataConvertException)e.getCause()).getColumnIndex();
			String cellData = ((ExcelDataConvertException)e.getCause()).getCellData().getStringValue();

			e.printStackTrace();
			String message = "数据格式错误:第【"+rowIndex+"】行,第【"+columnIndex+"】列的【"+cellData+"】无效";
			throw new RuntimeException(message);
		}

		if(data == null)	data = new ArrayList<>();

		long start = System.nanoTime();

		Map<String, String> statusMap = new HashMap<>();
		Map<String, String> flangeSizeMap = new HashMap<>();
		Map<String, String> flangeRatingMap = new HashMap<>();
		List<FlangeJoint> joints = null;
		try{
			joints = data.stream().map(jointExcel -> {
				if (jointExcel.getFrictionCoefficient()!=null){
					String cof = DictBizCache.getValue("cof", jointExcel.getFrictionCoefficient().setScale(3, BigDecimal.ROUND_HALF_UP).toString());
					if (cof==null){
						throw new RuntimeException("数据类型错误:【No.="+jointExcel.getNo()+"】行,节点【"+jointExcel.getJointNo()+"】中【Coefficient of Friction (µ)】的值不在预设范围内!");
					}
				}

				if(jointExcel.getJointNo() == null || jointExcel.getJointNo().length() > 20){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Joint ID no.】的值【"+jointExcel.getJointNo()+"】无效,超出长度限制");
				}
				if(jointExcel.getPackageNo() == null || jointExcel.getPackageNo().length() > 50){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Test Package no.】的值【"+jointExcel.getPackageNo()+"】无效,超出长度限制");
				}
				if(jointExcel.getIsometricNo() == null || jointExcel.getIsometricNo().length() > 50){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Isometric no.】的值【"+jointExcel.getIsometricNo()+"】无效,超出长度限制");
				}
				if(jointExcel.getJointType() == null || jointExcel.getJointType().length() > 10){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Joint type (F-Flanged/C-Clamp)】的值【"+jointExcel.getJointType()+"】无效,超出长度限制");
				}
				if(jointExcel.getFlangeSizeName() == null || jointExcel.getFlangeSizeName().length() > 20){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Flange/Clamp Size】的值【"+jointExcel.getFlangeSizeName()+"】无效,超出长度限制");
				}
				if(jointExcel.getFlangeRatingName() == null || jointExcel.getFlangeRatingName().length() > 20){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Flange/Clamp Rating】的值【"+jointExcel.getFlangeRatingName()+"】无效,超出长度限制");
				}
				if(jointExcel.getBoltSize() == null || jointExcel.getBoltSize().length() > 20){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Bolt size】的值【"+jointExcel.getBoltSize()+"】无效,超出长度限制");
				}
				if(jointExcel.getBoltQty() == null || jointExcel.getBoltQty().length() > 20){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Bolt Qty】的值【"+jointExcel.getBoltQty()+"】无效,超出长度限制");
				}
				if(jointExcel.getBoltMaterial() == null || jointExcel.getBoltMaterial().length() > 20){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Bolt Material】的值【"+jointExcel.getBoltMaterial()+"】无效,超出长度限制");
				}
				if(jointExcel.getNutMaterial() == null || jointExcel.getNutMaterial().length() > 20){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Nut Material】的值【"+jointExcel.getNutMaterial()+"】无效,超出长度限制");
				}
				if(jointExcel.getGasketType() == null || jointExcel.getGasketType().length() > 100){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Gasket type/Material】的值【"+jointExcel.getGasketType()+"】无效,超出长度限制");
				}

				if(jointExcel.getLabelNo()!=null&&jointExcel.getLabelNo().length()>100){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Joint Label no.】的值【"+jointExcel.getLabelNo()+"】无效,超出长度限制");
				}

				if(jointExcel.getPid()!=null&&jointExcel.getPid().length()>50){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【P&ID no.】的值【"+jointExcel.getPid()+"】无效,超出长度限制");
				}

				if(jointExcel.getLineNo()!=null&&jointExcel.getLineNo().length()>50){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Line no.】的值【"+jointExcel.getLineNo()+"】无效,超出长度限制");
				}

				if(jointExcel.getFlangeMaterial()!=null&&jointExcel.getFlangeMaterial().length()>20){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Flange Material】的值【"+jointExcel.getFlangeMaterial()+"】无效,超出长度限制");
				}

				if(jointExcel.getBoltCoating()!=null&&jointExcel.getBoltCoating().length()>20){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Bolt Coating】的值【"+jointExcel.getBoltCoating()+"】无效,超出长度限制");
				}

				if(jointExcel.getOffsetNorth()!=null&&jointExcel.getOffsetNorth().length()>20){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【OFFSET-North Bef/Aft】的值【"+jointExcel.getOffsetNorth()+"】无效,超出长度限制");
				}
				if(jointExcel.getOffsetSouth()!=null&&jointExcel.getOffsetSouth().length()>20){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【OFFSET-South Bef/Aft】的值【"+jointExcel.getOffsetSouth()+"】无效,超出长度限制");
				}
				if(jointExcel.getOffsetEast()!=null&&jointExcel.getOffsetEast().length()>20){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【OFFSET-East Bef/Aft】的值【"+jointExcel.getOffsetEast()+"】无效,超出长度限制");
				}
				if(jointExcel.getOffsetWest()!=null&&jointExcel.getOffsetWest().length()>20){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【OFFSET-West Bef/Aft】的值【"+jointExcel.getOffsetWest()+"】无效,超出长度限制");
				}

				if(jointExcel.getFaceNorth()!=null&&jointExcel.getFaceNorth().length()>20){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【FACE-North Bef/Aft】的值【"+jointExcel.getFaceNorth()+"】无效,超出长度限制");
				}
				if(jointExcel.getFaceSouth()!=null&&jointExcel.getFaceSouth().length()>20){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【FACE-South Bef/Aft】的值【"+jointExcel.getFaceSouth()+"】无效,超出长度限制");
				}
				if(jointExcel.getFaceEast()!=null&&jointExcel.getFaceEast().length()>20){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【FACE-East Bef/Aft】的值【"+jointExcel.getFaceEast()+"】无效,超出长度限制");
				}
				if(jointExcel.getFaceWest()!=null&&jointExcel.getFaceWest().length()>20){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【FACE-West Bef/Aft】的值【"+jointExcel.getFaceWest()+"】无效,超出长度限制");
				}

				if(jointExcel.getLubricantName()!=null&&jointExcel.getLubricantName().length()>20){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Lubricant Name】的值【"+jointExcel.getLubricantName()+"】无效,超出长度限制");
				}

				if(jointExcel.getTorqueTension()!=null&&jointExcel.getTorqueTension().length()>20){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Torquing or Tensioning (TOR or TEN)】的值【"+jointExcel.getTorqueTension()+"】无效,超出长度限制");
				}

				if(jointExcel.getUseMethod()!=null&&jointExcel.getUseMethod().length()>20){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Method used】的值【"+jointExcel.getUseMethod()+"】无效,超出长度限制");
				}

				if(jointExcel.getStudEndLength()!=null&&jointExcel.getStudEndLength().length()>20){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Stud End Length (After nut - 3 to 5 nos)】的值【"+jointExcel.getStudEndLength()+"】无效,超出长度限制");
				}

				if(jointExcel.getToolType()!=null&&jointExcel.getToolType().length()>50){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Tool type or Ref.】的值【"+jointExcel.getToolType()+"】无效,超出长度限制");
				}

				if(jointExcel.getToolSerialNo()!=null&&jointExcel.getToolSerialNo().length()>50){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Serial No.】的值【"+jointExcel.getToolSerialNo()+"】无效,超出长度限制");
				}

				if(jointExcel.getOperator()!=null&&jointExcel.getOperator().length()>50){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Tightened By】的值【"+jointExcel.getOperator()+"】无效,超出长度限制");
				}

				if(jointExcel.getTester()!=null&&jointExcel.getTester().length()>50){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Testing by】的值【"+jointExcel.getTester()+"】无效,超出长度限制");
				}

				if(jointExcel.getTagId()!=null&&jointExcel.getTagId().length()>20){
					throw new RuntimeException("数据长度错误:【No.="+jointExcel.getNo()+"】行,【Tag ID】的值【"+jointExcel.getTagId()+"】无效,超出长度限制");
				}
				// 工具类型 必须做校验 必须为三种类型之一
				if(jointExcel.getToolType()!=null){
					String toolType = DictBizCache.getValue("tool_type", jointExcel.getToolType());
					if (toolType==null) {
						throw new RuntimeException("数据类型错误:【No.=" + jointExcel.getNo() + "】行,节点【" + jointExcel.getJointNo() + "】中【Tool type or Ref.】的值不在预设范围内!");
					}
				}
				// 节点来源
				if(jointExcel.getJointSource()!=null){
					// 节点来源 只能是 0 或者 1
					if (jointExcel.getJointSource() != JointTypeEnum.TEMP_NODE.getCode() && jointExcel.getJointSource() != JointTypeEnum.FORMAL_NODE.getCode() ) {
						throw new RuntimeException("数据类型错误:【No.=" + jointExcel.getNo() + "】行,节点【" + jointExcel.getJointNo() + "】中【JointSource】的值不在预设范围内!");
					}
				}
				// 法兰类型
				if(jointExcel.getFlangeType()!=null){
					String flangeType = DictBizCache.getValue("flange_type", jointExcel.getFlangeType());
					if (flangeType==null) {
						throw new RuntimeException("数据类型错误:【No.=" + jointExcel.getNo() + "】行,节点【" + jointExcel.getJointNo() + "】中【Flange Type】的值不在预设范围内!");
					}
				}else {
					throw new RuntimeException("数据类型错误:【No.=" + jointExcel.getNo() + "】行,节点【" + jointExcel.getJointNo() + "】中【Flange Type】的值为空!");
				}



				FlangeJoint joint = Objects.requireNonNull(BeanUtil.copy(jointExcel, FlangeJoint.class));
				joint.setProjectId(projectId);
				if(StringUtil.isBlank(jointExcel.getStatusName())){
					joint.setStatus(0);
				}else if(statusMap.containsKey(jointExcel.getStatusName())){
					joint.setStatus(Func.toInt(statusMap.get(jointExcel.getStatusName())));
				}else {
					String statusKey = DictBizCache.getKey(DictBizEnum.FLANGE_JOINT_STATUS, jointExcel.getStatusName());
					joint.setStatus(Func.toInt(statusKey, 0));
					statusMap.put(jointExcel.getStatusName(), statusKey);
				}
				if(flangeSizeMap.containsKey(jointExcel.getFlangeSizeName())){
					joint.setFlangeSize(flangeSizeMap.get(jointExcel.getFlangeSizeName()));
				}else {
					String flangeSizekey = DictBizCache.getKey(DictBizEnum.FLANGE_JOINT_SIZE, jointExcel.getFlangeSizeName());
					if(flangeSizekey==null){
						throw new RuntimeException("数据类型错误:【No.="+jointExcel.getNo()+"】行,节点【"+jointExcel.getJointNo()+"】中【Flange/Clamp Size】的值不在预设范围内!");
					}
					joint.setFlangeSize(Func.toStr(flangeSizekey, ""));
					flangeSizeMap.put(jointExcel.getFlangeSizeName(), flangeSizekey);
				}
				if(flangeRatingMap.containsKey(jointExcel.getFlangeRatingName())){
					joint.setFlangeRating(Func.toStr(flangeRatingMap.get(jointExcel.getFlangeRatingName()), ""));
				}else {
					String flangeRatingKey = DictBizCache.getKey(DictBizEnum.FLANGE_JOINT_RATING, jointExcel.getFlangeRatingName());
					if(flangeRatingKey==null){
						throw new RuntimeException("数据类型错误:【No.="+jointExcel.getNo()+"】行,节点【"+jointExcel.getJointNo()+"】中【Flange/Clamp Rating】的值不在预设范围内!");
					}
					joint.setFlangeRating(Func.toStr(flangeRatingKey, ""));
					flangeRatingMap.put(jointExcel.getFlangeRatingName(), flangeRatingKey);
				}

				return joint;
			}).collect(Collectors.toList());
		}catch(Exception e){
			log.error(e.getMessage());
			return R.fail(e.getMessage());
		}
		System.out.println("遍历数据 : " + (System.nanoTime() - start) / 1000000);

		//查询数据库中的全部节点
		FlangeJoint fj = new FlangeJoint();
		fj.setProjectId(projectId);
		fj.setIsDeleted(0);
		List<FlangeJoint> fjs = flangeJointService.list(Condition.getQueryWrapper(fj));
//		List<String> exists = fjs.stream().map(d -> d.getJointNo()).collect(Collectors.toList());

		OperatorInfo oi = new OperatorInfo();
		oi.setProjectId(projectId);
		oi.setIsDeleted(0);
		List<OperatorInfo> operatorInfos = operatorInfoService.list(Condition.getQueryWrapper(oi));
		List<String> existsOperators = operatorInfos.stream().map(o -> o.getName()).collect(Collectors.toList());

		CheckerInfo ci = new CheckerInfo();
		ci.setProjectId(projectId);
		ci.setIsDeleted(0);
		List<CheckerInfo> checkerInfos = checkerInfoService.list(Condition.getQueryWrapper(ci));
		List<String> existsCheckers = checkerInfos.stream().map(c -> c.getName()).collect(Collectors.toList());

		ToolInfo ti = new ToolInfo();
		ti.setProjectId(projectId);
		ti.setIsDeleted(0);
		List<ToolInfo> toolInfos = toolInfoService.list(Condition.getQueryWrapper(ti));
		List<String> existsTools = toolInfos.stream().map(t -> t.getSerialNo()).collect(Collectors.toList());

		//统计导入情况,哪些新增,哪些已存在
		int total = joints.size();
		List<FlangeJoint> addList = new ArrayList<>();
		List<FlangeJoint> updateList = new ArrayList<>();
		List<FlangeJoint> sameList = new ArrayList<>();
		List<OperatorInfo> operators = new ArrayList<>();
		List<CheckerInfo> testers = new ArrayList<>();
		List<ToolInfo> tools = new ArrayList<>();
		List<String> operatorNames = new ArrayList<>();
		List<String> testerNames = new ArrayList<>();
		List<String> addToolSerialNos = new ArrayList<>();
		for(FlangeJoint j : joints){
			int index = fjs.indexOf(j);
			if(index == -1){
				j.setProjectId(projectId);
				addList.add(j);
			}else {
				FlangeJoint current = fjs.get(index);
				j.setId(current.getId());
				if(FlangeJointTool.hasDiff(current, j)){
					updateList.add(j);
				}else {
					sameList.add(j);
				}
			}
			String operatorName = j.getOperator();
			String testerName = j.getTester();
			String toolSerialNo = j.getToolSerialNo();//工具序列号可能是多个,用","号分隔开的
			String toolType = j.getToolType();
			String toolTerm = j.getToolTerm();

			if(StringUtil.isNotBlank(operatorName) && !existsOperators.contains(operatorName) && !operatorNames.contains(operatorName)){
				OperatorInfo operator = new OperatorInfo();
				operator.setProjectId(projectId);
				operator.setName(operatorName);
				operator.setKind(1);
				operators.add(operator);

				operatorNames.add(operatorName);
			}
			if(StringUtil.isNotBlank(testerName) && !existsCheckers.contains(testerName) && !testerNames.contains(testerName)){
				CheckerInfo tester = new CheckerInfo();
				tester.setProjectId(projectId);
				tester.setName(testerName);
				tester.setKind(1);
				testers.add(tester);

				testerNames.add(testerName);
			}

			if(StringUtil.isNotBlank(toolSerialNo)){
				ToolInfo tool = null;
				String[] serialNos = toolSerialNo.split(",");
				for(String serialNo : serialNos){
					if(!existsTools.contains(serialNo) && !addToolSerialNos.contains(serialNo)){
						tool = new ToolInfo();
						tool.setProjectId(projectId);
						tool.setSerialNo(serialNo);
						tool.setType(toolType);
						tool.setTerm(toolTerm);
						tool.setKind(1);
						tools.add(tool);
						addToolSerialNos.add(serialNo);
					}
				}
			}
		}

		ImportJoints ipt = new ImportJoints();
		ipt.setAdd(addList);
		ipt.setUpdate(updateList);
		ipt.setSame(sameList);
		ipt.setOperators(operators);
		ipt.setTesters(testers);
		ipt.setTools(tools);
		bladeRedis.setEx(CacheNames.IMPORT_JOINTS + uuid, ipt, Duration.ofHours(12));//保存12小时

		JointCheckInfo checkinfo = new JointCheckInfo();
		checkinfo.setId(uuid);
		checkinfo.setAdd(addList.size());
		checkinfo.setUpdate(updateList.size());
		checkinfo.setSame(sameList.size());
		checkinfo.setTotal(total);

		return R.data(checkinfo);
	}

	/**
	 * 确认导入节点
	 */
	@PostMapping("/import")
	@ApiOperationSupport(order = 14)
	@ApiOperation(value = "导入节点", notes = "节点流水号")
	public R importJoints(@RequestBody JointImportInfo importInfo) {
		String id = importInfo.getId();
		Long projectId = importInfo.getProjectId();

		ProjectInfo project = projectInfoService.getById(projectId);
		if(project == null){
			return R.fail(StatusEnum.SPECIFIED_PROJECT_NOT_EXIST.getCode());
		}

		ImportJoints info = bladeRedis.get(CacheNames.IMPORT_JOINTS + id);
		List<FlangeJoint> adds = info.getAdd();
		List<FlangeJoint> updates = info.getUpdate();
		List<OperatorInfo> operators = info.getOperators();
		List<CheckerInfo> testers = info.getTesters();
		List<ToolInfo> tools = info.getTools();

		if(adds != null && adds.size() > 0){
			flangeJointService.saveBatch(adds);
		}
		if(updates != null && updates.size() > 0){
			flangeJointService.updateBatchById(updates);
		}
		if(operators != null && operators.size() > 0){
			operatorInfoService.saveBatch(operators);
		}
		if(testers != null && testers.size() > 0){
			checkerInfoService.saveBatch(testers);
		}
		if(tools != null && tools.size() > 0){
			toolInfoService.saveBatch(tools);
		}

		logTool.insert(projectId, SysEnum.FLANGE, ModuleEnum.JOINT,
			"导入: 新增节点数量="+(!CollectionUtils.isEmpty(adds) ? adds.size() :0)+",更新节点数量="+(!CollectionUtils.isEmpty(updates) ? updates.size() :0)
				+",新增操作人员数量="+(!CollectionUtils.isEmpty(operators) ? operators.size() :0)+",新增检测人员数量="+(!CollectionUtils.isEmpty(testers) ? testers.size() :0)+",新增操作工具数量="+(!CollectionUtils.isEmpty(tools) ? tools.size() :0));

		return R.success(StatusEnum.IMPORT_SUCCESS.getCode());
	}

	/**
	 * 导出节点
	 */
	@GetMapping("/export")
	@ApiOperationSupport(order = 15)
	@ApiOperation(value = "导出节点", notes = "传入joint")
	public void exportJoints(@ApiIgnore @RequestParam Map<String, Object> map, HttpServletResponse response) {
		QueryWrapper<FlangeJoint> queryWrapper = Condition.getQueryWrapper(map, FlangeJoint.class);
		queryWrapper.lambda().eq(FlangeJoint::getIsDeleted, BladeConstant.DB_NOT_DELETED);
		List<JointExcel> joints = flangeJointService.exportJoints(queryWrapper);
		List<JointExcel> list = new ArrayList<>();
		Integer i=1;
		for (JointExcel joint : joints) {
			joint.setNo((i++).toString());

			joint.setStatusName(DictBizCache.getValue(DictBizEnum.FLANGE_JOINT_STATUS, joint.getStatus()));
			joint.setFlangeSizeName(DictBizCache.getValue(DictBizEnum.FLANGE_JOINT_SIZE, joint.getFlangeSize()));
			joint.setFlangeRatingName(DictBizCache.getValue(DictBizEnum.FLANGE_JOINT_RATING, joint.getFlangeRating()));
			list.add(joint);
		}
		ClassPathResource classPathResource = new ClassPathResource("templates/Joint-export.xlsx");
		try {
			response.setContentType("application/vnd.ms-excel");
			String fileName = URLEncoder.encode("法兰节点数据", "UTF-8");
			response.setHeader("Content-disposition", "attachment;filename="+fileName+".xlsx");
			InputStream is = classPathResource.getInputStream();
			ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(is).build();
			WriteSheet writeSheet = EasyExcel.writerSheet(0).head(JointExcel.class).build();
			excelWriter.write(list, writeSheet);
			excelWriter.finish();
		}catch (IOException e){
			e.printStackTrace();
		}
	}

	/**
	 * 导出模板
	 */
	@GetMapping("export/template")
	@ApiOperationSupport(order = 16)
	@ApiOperation(value = "导出模板")
	public void exportTemplate(HttpServletResponse response) {
		List<JointExcel> list = new ArrayList<>();
		ClassPathResource classPathResource = new ClassPathResource("templates/Joint-import.xlsx");
		try {
			response.setContentType("application/vnd.ms-excel");
			String fileName = URLEncoder.encode("法兰节点模板", "UTF-8");
			response.setHeader("Content-disposition", "attachment;filename="+fileName+".xlsx");
			InputStream is = classPathResource.getInputStream();
			ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(is).build();
			WriteSheet writeSheet = EasyExcel.writerSheet(0).head(JointExcel.class).build();
			excelWriter.write(list, writeSheet);
			excelWriter.finish();
		}catch (IOException e){
			e.printStackTrace();
		}
	}

	/**
	 * 导出报告
	 */
	@GetMapping("/report")
	@ApiOperationSupport(order = 17)
	@ApiOperation(value = "导出报告", notes = "传入ids")
	public void exportReport(@RequestParam String ids, HttpServletResponse response) {
		List<FlangeJoint> joints = flangeJointService.listByIds(Func.toLongList(ids));

		String uuid = UUID.randomUUID().toString();
		ClassPathResource classPathResource = new ClassPathResource("templates/report-tpl.xlsx");

		Path zipPath = Paths.get(pathProperties.getReport(), uuid+".zip");
		File zipFile = zipPath.toFile();
		if(zipFile.exists()){
			zipFile.delete();//如果文件存在则先删除旧的文件
		}

		List<File> files = null;
		try{
			if(!zipFile.getParentFile().exists()){
				Files.createDirectories(zipPath.getParent());
			}
//			if(!zipFile.exists()){
//				Files.createFile(zipPath);
//			}

			files = joints.stream()
				.filter(j -> j.getStatus() == 4)
				.map(joint -> {
					FlangeJointVO flangeJointVO = new FlangeJointVO();
					BeanUtil.copy(joint, flangeJointVO);
					String jointNo = joint.getJointNo();
					String packageNo = joint.getPackageNo();
					String fileName = packageNo + "-" + jointNo + ".xlsx";
					Path filePath = Paths.get(pathProperties.getReport(), uuid, fileName);
					File file = filePath.toFile();

					InputStream is = null;
					FileOutputStream fos = null;
					try{
						if(!file.getParentFile().exists()){
							Files.createDirectories(filePath.getParent());
						}
						if(!file.exists()){
							Files.createFile(filePath);
						}

						is = classPathResource.getInputStream();
						fos = new FileOutputStream(file);
						ExcelWriter excelWriter = EasyExcel.write(file).withTemplate(is).build();

						WriteSheet sheetE = EasyExcel.writerSheet(0).build();
						WriteSheet sheetC = EasyExcel.writerSheet(1).build();

						Long projectId = joint.getProjectId();
						ProjectInfo projectInfo = projectInfoService.getById(projectId);
						if (projectInfo!=null){
							flangeJointVO.setCustName(projectInfo.getCustName());
							flangeJointVO.setProjectName(projectInfo.getName());
							flangeJointVO.setProjectAddress(projectInfo.getAddress());
							flangeJointVO.setDate(LocalDate.now().toString());
						}
						String faceEast = flangeJointVO.getFaceEast();
						String faceNorth = flangeJointVO.getFaceNorth();
						String faceSouth = flangeJointVO.getFaceSouth();
						String faceWest = flangeJointVO.getFaceWest();
						String offsetEast = flangeJointVO.getOffsetEast();
						String offsetNorth = flangeJointVO.getOffsetNorth();
						String offsetSouth = flangeJointVO.getOffsetSouth();
						String offsetWest = flangeJointVO.getOffsetWest();
						if ("ACC".equals(faceEast) && "ACC".equals(faceNorth) && "ACC".equals(faceSouth) && "ACC".equals(faceWest)){
							flangeJointVO.setFlangeFace("Good");
							if ("ACC".equals(offsetEast) && "ACC".equals(offsetNorth) && "ACC".equals(offsetSouth) && "ACC".equals(offsetWest)){
								flangeJointVO.setFlangeAlighment("Good");
							}else {
								flangeJointVO.setFlangeAlighment("Fail");
							}
						}else {
							flangeJointVO.setFlangeFace("Fail");
							flangeJointVO.setFlangeAlighment("Fail");
						}
						int sno = reportSnoService.getSno(projectId);
						flangeJointVO.setReportNo(String.format("%05d", sno));
						Integer passPressure1 = flangeJointVO.getPassPressure1();
						Integer passPressure2 = flangeJointVO.getPassPressure2();
						flangeJointVO.setRightActualTorque(flangeJointVO.getActualTorque());
						if (passPressure1!=null || passPressure2!=null){
							flangeJointVO.setRightActualTorque(null);
							flangeJointVO.setBoltLoad(null);
						}else {
							flangeJointVO.setActualTorque(null);
							flangeJointVO.setPassPressure1(null);
							flangeJointVO.setPassPressure2(null);
							flangeJointVO.setBoltStress(null);
						}
						excelWriter.fill(flangeJointVO, sheetE);
						excelWriter.fill(flangeJointVO, sheetC);
						excelWriter.finish();
					}catch(Exception e1){
						e1.printStackTrace();
					}finally{
						if(fos != null) try{ fos.close(); }catch(Exception e2){e2.printStackTrace();}
						if(is != null) try{ is.close(); }catch(Exception e3){e3.printStackTrace();}
					}
					return file;
				}).collect(Collectors.toList());

			ZipUtil.zipFile(files, zipPath.toFile().getAbsolutePath());
		}catch(Exception e){
			e.printStackTrace();
		}finally {
			if(files != null){
				for(File f : files){
					if(f.exists()) f.delete();
				}
			}
			Path dirPath = Paths.get(pathProperties.getReport(), uuid);
			if(dirPath.toFile().exists()){
				dirPath.toFile().delete();
			}
		}

		//下载文件
		InputStream inStream = null;
		try {
			if(!zipFile.exists()){
				return ;
			}
			inStream = new FileInputStream(zipFile);
			response.reset();
			response.setContentType("application/zip");
			response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(zipFile.getName(), "UTF-8"));
			response.setCharacterEncoding("UTF-8");
			IoUtil.copy(inStream, response.getOutputStream());
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (inStream != null) {
				try { inStream.close(); } catch (IOException e) { e.printStackTrace(); }
			}
		}
	}

	// 导出报告 word文档 模板类型 正式节点才能导出报告
	@GetMapping("/exportReportWord")
	@ApiOperationSupport(order = 8)
	@ApiOperation(value = "导出报告 word文档", notes = "")
	public void exportReportWord(@RequestParam String ids, HttpServletResponse response) throws IOException {


		List<FlangeJoint> joints = flangeJointService.listByIds(Func.toLongList(ids));

		String uuid = UUID.randomUUID().toString();
		Path zipPath = Paths.get(pathProperties.getReport(), uuid+".zip");
		File zipFile = zipPath.toFile();
		if(zipFile.exists()){
			zipFile.delete();//如果文件存在则先删除旧的文件
		}

		List<File> files = null;

		try{
			if(!zipFile.getParentFile().exists()){
				Files.createDirectories(zipPath.getParent());
			}
//			if(!zipFile.exists()){
//				Files.createFile(zipPath);
//			}

			files = joints.stream()
				.filter(j -> j.getStatus() == 4)
				.map(joint -> {

					FlangeJointVO flangeJoint = new FlangeJointVO();
					BeanUtil.copy(joint, flangeJoint);
					String jointNo = joint.getJointNo();
					String packageNo = joint.getPackageNo();
					String fileName = packageNo + "-" + jointNo + ".docx";
					Path filePath = Paths.get(pathProperties.getReport(), uuid, fileName);
					File file = filePath.toFile();

					InputStream is = null;
					FileOutputStream fos = null;
					try{
						if(!file.getParentFile().exists()){
							Files.createDirectories(filePath.getParent());
						}
						if(!file.exists()){
							Files.createFile(filePath);
						}
						// 传入节点对象  返回word文档的模板类
						XWPFTemplate template = getXwpfTemplate(flangeJoint);
						fos = new FileOutputStream(file);  // word文档的文件输出流
						template.write(fos);

					}catch(Exception e1){
						e1.printStackTrace();
					}finally{
						if(is != null) try{ is.close(); }catch(Exception e3){e3.printStackTrace();}
					}
					return file;
				}).collect(Collectors.toList());

			ZipUtil.zipFile(files, zipPath.toFile().getAbsolutePath());
		}catch(Exception e){
			e.printStackTrace();
		}finally {
			if(files != null){
				for(File f : files){
					if(f.exists()) f.delete();
				}
			}
			Path dirPath = Paths.get(pathProperties.getReport(), uuid);
			if(dirPath.toFile().exists()){
				dirPath.toFile().delete();
			}
		}

		//下载文件
		InputStream inStream = null;
		try {
			if(!zipFile.exists()){
				return ;
			}
			inStream = new FileInputStream(zipFile);
			response.reset();
			response.setContentType("application/zip");
			response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(zipFile.getName(), "UTF-8"));
			response.setCharacterEncoding("UTF-8");
			IoUtil.copy(inStream, response.getOutputStream());
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (inStream != null) {
				try { inStream.close(); } catch (IOException e) { e.printStackTrace(); }
			}
		}

	}

	/**
	 * 传入 节点对象  返回生成的word文档
	 * @param flangeJoint
	 * @return
	 * @throws IOException
	 */
	private XWPFTemplate getXwpfTemplate(FlangeJointVO flangeJoint) throws IOException {
		Map<String, Object> map = new HashMap<>();
//		List<Map<String, Object>> table = this.setListData(flangeJoints); 无内循环表格
//		map.put("table", table);

		map.put("jointNo", flangeJoint.getJointNo());
		map.put("packageNo", flangeJoint.getPackageNo());
		//lineNo
		map.put("lineNo", flangeJoint.getLineNo());
		// pid
		map.put("pid", flangeJoint.getPid());
		//isometricNo
		map.put("isometricNo", flangeJoint.getIsometricNo());
		//flangeType
		map.put("flangeType", flangeJoint.getFlangeType());

		// gasketType
		map.put("gasketType", flangeJoint.getGasketType());
		// flangeSize
		map.put("flangeSize", flangeJoint.getFlangeSize());
		//boltQty
		map.put("boltQty", flangeJoint.getBoltQty());
		//flangeRating
		map.put("flangeRating", flangeJoint.getFlangeRating());
		//nutMaterial
		map.put("nutMaterial", flangeJoint.getNutMaterial());
		//flangeMaterial
		map.put("flangeMaterial", flangeJoint.getFlangeMaterial());
		//nutAf
		map.put("nutAf", flangeJoint.getNutAf().toString());
		//lubricantName
		map.put("lubricantName", flangeJoint.getLubricantName());
		//boltSize
		map.put("boltSize", flangeJoint.getBoltSize());
		//frictionCoefficient
		map.put("frictionCoefficient", flangeJoint.getFrictionCoefficient());
		//boltLoad
		map.put("boltLoad", flangeJoint.getBoltLoad());
		//boltStress
		map.put("boltStress", flangeJoint.getBoltStress());

		//toolType
		map.put("toolType", flangeJoint.getToolType());
		//toolSerialNo
		map.put("toolSerialNo", flangeJoint.getToolSerialNo());
		//当天日期
		map.put("localDate", LocalDate.now().toString());
		//projectName
		ProjectInfo projectInfo = projectInfoService.getById(flangeJoint.getProjectId());
		//operator 紧固人员
		map.put("operator", flangeJoint.getOperator());
		if (projectInfo != null) {
			map.put("projectName", projectInfo.getName());
			// projectLocation
			map.put("projectLocation", projectInfo.getAddress());
			//custName
			map.put("custName", projectInfo.getCustName());
		}



		// 工具类型 如果是 手动和液压共用右面的 表格    拉伸用左边的
		if (ToolTypeEnum.TYPE3.getCode().equals(flangeJoint.getToolType())) {
			map.put("toolTypeLeft", flangeJoint.getToolType() );
			// passPressure1
			map.put("passPressure1", flangeJoint.getPassPressure1());
			map.put("passPressure2", flangeJoint.getPassPressure2());
			map.put("passPressure3", flangeJoint.getPassPressure3());
			map.put("passPressure4", flangeJoint.getPassPressure4());

		}else if (ToolTypeEnum.TYPE1.getCode().equals(flangeJoint.getToolType()) || ToolTypeEnum.TYPE2.getCode().equals(flangeJoint.getToolType())){
			map.put("toolTypeRight", flangeJoint.getToolType() );
			//pass30
			map.put("pass30", flangeJoint.getPass30());
			//pass70
			map.put("pass70", flangeJoint.getPass70());
			//pass100
			map.put("pass100", flangeJoint.getPass100());
		}else {
			// 存在历史节点并没有用 后期修改的下拉三种工具类型 所以先不处理
		}
		// 导出
		PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();

		Resource resource = resolver.getResource("classpath:/templates/word/jointReport_en.docx");
//		Configure config = Configure.builder().bind("table", new LoopRowTableRenderPolicy()).build();
//		XWPFTemplate template = XWPFTemplate.compile(resource.getInputStream(), config).render(map);
		XWPFTemplate template = XWPFTemplate.compile(resource.getInputStream()).render(map);
		return template;
	}

	private List<Map<String, Object>> setListData(List<FlangeJoint> flangeJoints) {
		List<Map<String, Object>> resultMap = new ArrayList<>();
		for (int i = 0; i < flangeJoints.size(); i++) {
			FlangeJoint maintainWork = flangeJoints.get(i);
			Map<String, Object> dataMap = new HashMap<>();
			dataMap.put("No", i+1);
			dataMap.put("maintainState", "测试");
			resultMap.add(dataMap);
		}
		return resultMap;
	}


	/**
	 * 自定义分页 法兰节点表
	 */
	@GetMapping("/dict/{projectId}")
	@ApiOperationSupport(order = 9)
	@ApiOperation(value = "字典", notes = "")
	public R<List<Dict>> dict(@PathVariable Long projectId) {
		FlangeJoint condition = new FlangeJoint();
		condition.setProjectId(projectId);
		condition.setIsDeleted(0);
		List<FlangeJoint> list = flangeJointService.list(Condition.getQueryWrapper(condition));
		List<Dict> dicts = list.stream().map(item -> {
			Dict d = new Dict();
			d.setDictKey(String.valueOf(item.getId()));
			d.setDictValue(item.getJointNo());
			return d;
		}).collect(Collectors.toList());
		return R.data(dicts);


	}

	//读取excel录入法兰扭矩值  需要先整理表格,取消合并单元格以及删除第一行空表头  目前只是针对F表
	@PostMapping("/readExcel/t_flange_torque")
	@ApiOperationSupport(order = 6)
	@ApiOperation(value = "读取excel录入法兰扭矩值", notes = "读取excel录入法兰扭矩值")
	@Transactional
	public R readExcelTorque(@RequestParam("jointType") String jointType,@RequestParam("file") MultipartFile file ) throws IOException, IllegalAccessException {
		if (StringUtil.isBlank(jointType)|| !jointType.equals("F")&&!jointType.equals("C")){
			return R.fail("jointType参数为空,必须为F或C");
		}
		// 获取 文件名
		String fileName = file.getOriginalFilename();
		// 如果 文件名 包含 C 则说明是C表
		// 防止后面维护数据导入错误 防呆设计
		if (fileName.contains("C表")){
			// 如果传来的jointType不为C,则抛异常
			if (!jointType.equals("C")){
				return R.fail("jointType参数为C,但文件名中包含C表,请检查文件名");
			}
		}else {
			if (jointType.equals("F")){
				return R.success("文件名中不包含C表,jointType为F,可以继续操作");
			}
		}

		List<FlangeTorqueExcel> excelDataList = EasyExcel.read(file.getInputStream()).head(FlangeTorqueExcel.class).sheet().doReadSync();

		ArrayList<FlangeTorque> flangeTorqueArrayList = new ArrayList<>();

		// 循环数据,录入到t_flange_torque表中
		for (FlangeTorqueExcel flangeTorqueExcel : excelDataList) {
			// 循环一个 对象,录入 19 条数据  从 cof= 0.18 一直到 0.06
			Field[] fields = flangeTorqueExcel.getClass().getDeclaredFields();
			for (Field field : fields) {
				if (field.getName().startsWith("cof")) {
					field.setAccessible(true);
					Object value = field.get(flangeTorqueExcel);
//					System.out.println(field.getName() + ": " + value);

					// 创建法兰扭力值数据 存表
					FlangeTorque flangeTorque = new FlangeTorque();
					//设置法兰类型 后续可能会有 国际化问题
					if (StringUtil.isNotBlank(flangeTorqueExcel.getModel())){
						// 如果型号不为空,则将 类型加型号以下划线拼接 (与客户沟通处理C表的方式)
						flangeTorque.setType( flangeTorqueExcel.getType()+ "_" +   flangeTorqueExcel.getModel());
					}else {
						flangeTorque.setType(flangeTorqueExcel.getType());
					}
					if (StringUtil.isNotBlank(flangeTorqueExcel.getSize())) {
						flangeTorque.setSize(flangeTorqueExcel.getSize().split("\\(")[0]);// 法兰尺寸
					}else {
						flangeTorque.setSize("--"); // 因为c表 表一 没有size 字段 所以用 -- 表示无此数据
					}
					flangeTorque.setClazz(flangeTorqueExcel.getClazz().split("\\(")[0]);// 法兰磅级
					flangeTorque.setReaiualStress(flangeTorqueExcel.getResidualStress());// 拉伸扭力值
					flangeTorque.setBoltSize(flangeTorqueExcel.getBoltSize()); // 螺栓尺寸
					// 处理摩擦系数
					// 获取 此 字段的注解
					ExcelProperty cof = field.getAnnotation(ExcelProperty.class);
					String cofValue = cof.value()[0];
					//取得 注解中的数值 例如 Cof=0.18(Nm)  取 0.18
					String numberPart = cofValue.substring(cofValue.indexOf('=') + 1, cofValue.indexOf('('));
					try {
						BigDecimal bigDecimal = new BigDecimal(numberPart);
						flangeTorque.setCof(bigDecimal);
					}catch (Exception e){
						flangeTorque.setCof(new BigDecimal("-999.999")); // 说明是用户自定义摩擦系数  Cof=x(Nm) 存特殊值
					}
					if (value == null) { //如果为空说明 没有此摩擦系数的数据 数据,跳过
//						flangeTorque.setActualTorque(null);
						continue; //如果这个摩擦系数没有值 直接跳过即可 不必存表
					}else {
						flangeTorque.setActualTorque((int) value);
					}
					flangeTorque.setJointType(jointType);// 必须输入F或者C

						//输出 数据
//					log.info("flangeTorque:{}", flangeTorque);

					flangeTorqueArrayList.add(flangeTorque);

				}

			}

		}

		//批量新增
		boolean b = flangeTorqueService.saveBatch(flangeTorqueArrayList);


		return R.status(b);
	}

	// 更新字典配置 flange_size 法兰尺寸  flange_rating 法兰磅级
	@GetMapping("/update/dict")
	@ApiOperationSupport(order = 7)
	@ApiOperation(value = "更新字典配置", notes = "更新字典配置")
	@Transactional
	public R updateDict() throws Exception {

		//查询表中t_flange_torque所有数据
		List<FlangeTorque> flangeTorqueList = flangeTorqueService.list();

		List<String> sizeList = flangeTorqueList.stream().map(FlangeTorque::getSize).distinct().collect(Collectors.toList());
		List<String> clazzList = flangeTorqueList.stream().map(FlangeTorque::getClazz).distinct().collect(Collectors.toList());

		// 处理法兰尺寸  注意法兰尺寸是有空值的
		QueryWrapper<DictBiz> dictBizQueryWrapper = new QueryWrapper<>();
		dictBizQueryWrapper.lambda().eq(DictBiz::getCode, "flange_size");
		dictBizQueryWrapper.lambda().ne(DictBiz::getParentId, 0); // 不删除这个的父级节点
		dictBizMapper.delete(dictBizQueryWrapper);


		List<DictBiz> flangeSize = dictBizMapper.selectList(Wrappers.lambdaQuery(DictBiz.class).eq(DictBiz::getCode, "flange_size"));
		Long flangeSizeId = null;
		if (flangeSize.size() == 1){
			flangeSizeId = flangeSize.get(0).getId();
		}else {
			throw new Exception("flange_size 字典配置有误");
		}

		for (String size : sizeList) {
			DictBiz dictBiz = new DictBiz();
			dictBiz.setTenantId("000000");
			dictBiz.setParentId(flangeSizeId);
			dictBiz.setCode("flange_size");
			if (StringUtil.isBlank(size)){
				dictBiz.setDictKey("--");
				dictBiz.setDictValue("--");
			}else {
				dictBiz.setDictKey(size);
				dictBiz.setDictValue(size);
			}
			dictBiz.setSort(sizeList.indexOf(size));//用默认排序就行
			dictBiz.setRemark("批量导入");

			dictBizMapper.insert(dictBiz);
		}
		// 处理法兰磅级
		QueryWrapper<DictBiz> dictBizQueryWrapper2 = new QueryWrapper<>();
		dictBizQueryWrapper2.lambda().eq(DictBiz::getCode, "flange_rating");
		dictBizQueryWrapper2.lambda().ne(DictBiz::getParentId, 0);
		dictBizMapper.delete(dictBizQueryWrapper2);



		List<DictBiz> flangeRating = dictBizMapper.selectList(Wrappers.lambdaQuery(DictBiz.class).eq(DictBiz::getCode, "flange_rating"));
		Long flangeRatingId = null;
		if (flangeRating.size() == 1){
			flangeRatingId = flangeRating.get(0).getId();
		}else {
			throw new Exception("flange_rating 字典配置有误");
		}

		for (String clazz : clazzList) {
			DictBiz dictBiz = new DictBiz();
			dictBiz.setTenantId("000000");
			dictBiz.setParentId(flangeRatingId);
			dictBiz.setCode("flange_rating");
			dictBiz.setDictKey(clazz);
			dictBiz.setDictValue(clazz);
			dictBiz.setSort(clazzList.indexOf(clazz));//用默认排序就行
			dictBiz.setRemark("批量导入");

			dictBizMapper.insert(dictBiz);
		}

		return R.data("成功");

	}


	// 更新字典配置 flange_type 法兰类型  不过对于前端显示是中英文还是三语 待商榷
	@GetMapping("/update/dictForFlangeType")
	@ApiOperationSupport(order = 7)
	@ApiOperation(value = "更新字典配置", notes = "更新字典配置")
	@Transactional
	public R updateDictForFlangeType() throws Exception {

		//查询表中t_flange_torque所有数据
		List<FlangeTorque> flangeTorqueList = flangeTorqueService.list();

		List<String> typeList = flangeTorqueList.stream().map(FlangeTorque::getType).distinct().collect(Collectors.toList());

		// 处理法兰尺寸
		QueryWrapper<DictBiz> dictBizQueryWrapper = new QueryWrapper<>();
		dictBizQueryWrapper.lambda().eq(DictBiz::getCode, "flange_type");
		dictBizQueryWrapper.lambda().ne(DictBiz::getParentId, 0); // 不删除这个的父级节点
		dictBizMapper.delete(dictBizQueryWrapper);


		List<DictBiz> flangeType = dictBizMapper.selectList(Wrappers.lambdaQuery(DictBiz.class).eq(DictBiz::getCode, "flange_type"));
		Long flangeTypeId = null;
		if (flangeType.size() == 1){
			flangeTypeId = flangeType.get(0).getId();
		}else {
			throw new Exception("flange_type 字典配置有误");
		}

		for (String size : typeList) {
			DictBiz dictBiz = new DictBiz();
			dictBiz.setTenantId("000000");
			dictBiz.setParentId(flangeTypeId);
			dictBiz.setCode("flange_type");
			dictBiz.setDictKey(size);
			dictBiz.setDictValue(size);
			dictBiz.setSort(typeList.indexOf(size));//用默认排序就行
			dictBiz.setRemark("批量导入");

			dictBizMapper.insert(dictBiz);
		}


		return R.data("成功");

	}

	// 更新业务字典的摩擦系数
	@GetMapping("/update/dictForCof")
	@ApiOperationSupport(order = 7)
	@ApiOperation(value = "更新业务字典的摩擦系数", notes = "更新业务字典的摩擦系数")
	@Transactional
	public R updateDictForCof() throws Exception {

		//查询表中t_flange_torque所有数据
		List<FlangeTorque> flangeTorqueList = flangeTorqueService.list();

		List<BigDecimal> cofList = flangeTorqueList.stream().map(FlangeTorque::getCof).distinct().collect(Collectors.toList());

		// 处理法兰摩擦系数 删除旧数据
		QueryWrapper<DictBiz> dictBizQueryWrapper = new QueryWrapper<>();
		dictBizQueryWrapper.lambda().eq(DictBiz::getCode, "cof");
		dictBizQueryWrapper.lambda().ne(DictBiz::getParentId, 0);
		dictBizMapper.delete(dictBizQueryWrapper);

		List<DictBiz> flangeSize = dictBizMapper.selectList(Wrappers.lambdaQuery(DictBiz.class).eq(DictBiz::getCode, "cof"));
		Long flangeSizeId = null;
		if (flangeSize.size() == 1){
			flangeSizeId = flangeSize.get(0).getId();
		}else {
			throw new Exception("cof 字典配置有误");
		}

		for (BigDecimal cof : cofList) {
			DictBiz dictBiz = new DictBiz();
			dictBiz.setTenantId("000000");
			dictBiz.setParentId(flangeSizeId);
			dictBiz.setCode("cof");
			dictBiz.setRemark("批量导入");
			if (cof!=null) {
				dictBiz.setDictKey(cof.toString());
				dictBiz.setDictValue(cof.toString());
			}else {
				dictBiz.setDictValue("X");
				dictBiz.setDictKey("-999.999"); // 用来表示用户自定义值
			}
			dictBiz.setSort(cofList.indexOf(cof));//用默认排序就行

			dictBizMapper.insert(dictBiz);
		}


		return R.data("成功");
	}


	//读取excel录入法兰尺寸值
	@PostMapping("/readExcel/t_flange_size")
	@ApiOperationSupport(order = 6)
	@ApiOperation(value = "读取excel录入法兰尺寸值", notes = "读取excel录入尺寸")
	@Transactional
	public R readExcelForFlangeSize(@RequestParam("file") MultipartFile file ) throws IOException {


		List<FlangeSizeExcel> excelDataList1 = EasyExcel.read(file.getInputStream()).head(FlangeSizeExcel.class).sheet(0).doReadSync();
		List<FlangeSizeExcel> excelDataList2 = EasyExcel.read(file.getInputStream()).head(FlangeSizeExcel.class).sheet(1).doReadSync();


		ArrayList<FlangeSize> flangeSizes = new ArrayList<>();

		// 循环数据,录入到t_flange_torque表中
		for (FlangeSizeExcel flangeSizeExcel : excelDataList1) {
			FlangeSize flangeSize = new FlangeSize();
			BeanUtil.copy(flangeSizeExcel, flangeSize);
			 // `standard` int DEFAULT NULL COMMENT '图片标准(1-国标;2-英制)',
			flangeSize.setStandard(1);
			flangeSizes.add(flangeSize);
		}
		// 循环数据,录入到t_flange_torque表中
		for (FlangeSizeExcel flangeSizeExcel : excelDataList2) {
			FlangeSize flangeSize = new FlangeSize();
			BeanUtil.copy(flangeSizeExcel, flangeSize);
			// `standard` int DEFAULT NULL COMMENT '图片标准(1-国标;2-英制)',
			flangeSize.setStandard(2);
			flangeSizes.add(flangeSize);
		}
		//批量新增
		boolean b = flangeSizeService.saveBatch(flangeSizes);


		return R.status(b);
	}






}

切记要引入这几个包,poi必须版本一致否则会出问题

        <dependency>
            <groupId>com.deepoove</groupId>
            <artifactId>poi-tl</artifactId>
            <version>1.10.0-beta</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-scratchpad</artifactId>
            <version>4.1.2</version>
        </dependency>

模板采用docx,和 xlsx

注意word模板是{{}}  excel是{}