招标解析接口
This commit is contained in:
parent
155a5ab913
commit
f41c2c30dd
|
|
@ -6,7 +6,10 @@ import com.bonus.common.core.controller.BaseController;
|
||||||
//import com.bonus.web.service.analysis.AnalysisService;
|
//import com.bonus.web.service.analysis.AnalysisService;
|
||||||
import com.bonus.common.core.domain.AjaxResult;
|
import com.bonus.common.core.domain.AjaxResult;
|
||||||
import com.bonus.common.core.page.TableDataInfo;
|
import com.bonus.common.core.page.TableDataInfo;
|
||||||
|
import com.bonus.common.domain.analysis.dto.AnalysisBidDto;
|
||||||
import com.bonus.common.domain.analysis.dto.AnalysisDto;
|
import com.bonus.common.domain.analysis.dto.AnalysisDto;
|
||||||
|
import com.bonus.common.domain.analysis.dto.AnalysisProDto;
|
||||||
|
import com.bonus.common.domain.analysis.vo.AnalysisBidVo;
|
||||||
import com.bonus.common.domain.analysis.vo.AnalysisVo;
|
import com.bonus.common.domain.analysis.vo.AnalysisVo;
|
||||||
import com.bonus.common.enums.OperaType;
|
import com.bonus.common.enums.OperaType;
|
||||||
import com.bonus.web.service.analysis.AnalysisService;
|
import com.bonus.web.service.analysis.AnalysisService;
|
||||||
|
|
@ -53,4 +56,44 @@ public class AnalysisController extends BaseController {
|
||||||
public AjaxResult saveData(@RequestBody AnalysisDto.TemplateDto dto) {
|
public AjaxResult saveData(@RequestBody AnalysisDto.TemplateDto dto) {
|
||||||
return analysisService.saveData(dto);
|
return analysisService.saveData(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation(value = "招标解析", notes = "查看标段详情")
|
||||||
|
@GetMapping("getBidList")
|
||||||
|
@SysLog(title = "招标解析", module = "招标解析->查看标段详情", businessType = OperaType.QUERY, details = "查看标段详情", logType = 1)
|
||||||
|
@RequiresPermissions("analysis:analysis:query")
|
||||||
|
public TableDataInfo getBidList(AnalysisDto.TemplateDto dto) {
|
||||||
|
startPage();
|
||||||
|
List<AnalysisBidVo> list = analysisService.getBidList(dto);
|
||||||
|
return getDataTable(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation(value = "招标解析", notes = "查看项目详情")
|
||||||
|
@GetMapping("getProDetail")
|
||||||
|
@SysLog(title = "招标解析", module = "招标解析->查看项目详情", businessType = OperaType.QUERY, details = "查看项目详情", logType = 1)
|
||||||
|
@RequiresPermissions("analysis:analysis:query")
|
||||||
|
public AjaxResult getProDetail(AnalysisDto.TemplateDto dto) {
|
||||||
|
return analysisService.getProDetail(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation(value = "招标解析", notes = "更新项目数据")
|
||||||
|
@PostMapping("editProData")
|
||||||
|
@SysLog(title = "招标解析", module = "招标解析->更新项目数据", businessType = OperaType.UPDATE, details = "更新项目数据", logType = 1)
|
||||||
|
@RequiresPermissions("analysis:analysis:edit")
|
||||||
|
public AjaxResult editProData(@RequestBody AnalysisProDto dto) {
|
||||||
|
return analysisService.editProData(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation(value = "招标解析", notes = "更新标段数据")
|
||||||
|
@PostMapping("editBidData")
|
||||||
|
@SysLog(title = "招标解析", module = "招标解析->更新标段数据", businessType = OperaType.UPDATE, details = "更新标段数据", logType = 1)
|
||||||
|
@RequiresPermissions("analysis:analysis:edit")
|
||||||
|
public AjaxResult editBidData(@RequestBody AnalysisBidDto dto) {
|
||||||
|
return analysisService.editBidData(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation(value = "测试mq异步消息", notes = "测试mq异步消息")
|
||||||
|
@GetMapping("/testAsyncMq2")
|
||||||
|
public AjaxResult testAsyncMq2() {
|
||||||
|
return analysisService.testAsyncMq2();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,21 @@ package com.bonus.web.service.analysis;
|
||||||
|
|
||||||
import com.bonus.analysis.service.IASAnalysisService;
|
import com.bonus.analysis.service.IASAnalysisService;
|
||||||
import com.bonus.common.core.domain.AjaxResult;
|
import com.bonus.common.core.domain.AjaxResult;
|
||||||
|
import com.bonus.common.domain.analysis.dto.AnalysisBidDto;
|
||||||
import com.bonus.common.domain.analysis.dto.AnalysisDto;
|
import com.bonus.common.domain.analysis.dto.AnalysisDto;
|
||||||
|
import com.bonus.common.domain.analysis.dto.AnalysisProDto;
|
||||||
|
import com.bonus.common.domain.analysis.vo.AnalysisBidVo;
|
||||||
import com.bonus.common.domain.analysis.vo.AnalysisVo;
|
import com.bonus.common.domain.analysis.vo.AnalysisVo;
|
||||||
import com.bonus.common.domain.mainDatabase.dto.EnterpriseDto;
|
import com.bonus.common.domain.mainDatabase.dto.EnterpriseDto;
|
||||||
|
import com.bonus.common.domain.ocr.dto.OcrRequest;
|
||||||
|
import com.bonus.common.domain.ocr.vo.WordConvertPdfResponse;
|
||||||
import com.bonus.common.domain.rabbitmq.dto.RabbitMqMessage;
|
import com.bonus.common.domain.rabbitmq.dto.RabbitMqMessage;
|
||||||
|
import com.bonus.common.utils.Base64ToPdfConverter;
|
||||||
|
import com.bonus.common.utils.FileUtil;
|
||||||
import com.bonus.common.utils.ValidatorsUtils;
|
import com.bonus.common.utils.ValidatorsUtils;
|
||||||
import com.bonus.common.utils.uuid.UUID;
|
import com.bonus.common.utils.uuid.UUID;
|
||||||
|
import com.bonus.ocr.service.OcrService;
|
||||||
|
import com.bonus.ocr.service.WordConvertPdfService;
|
||||||
import com.bonus.web.rabbitmq.producer.RabbitMQProducerService;
|
import com.bonus.web.rabbitmq.producer.RabbitMQProducerService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
@ -17,6 +26,8 @@ import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.transaction.interceptor.TransactionAspectSupport;
|
import org.springframework.transaction.interceptor.TransactionAspectSupport;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
@ -49,6 +60,9 @@ public class AnalysisService {
|
||||||
@Resource(name = "RabbitMQProducerService")
|
@Resource(name = "RabbitMQProducerService")
|
||||||
private RabbitMQProducerService rabbitMQProducerService;
|
private RabbitMQProducerService rabbitMQProducerService;
|
||||||
|
|
||||||
|
@Resource(name = "WordConvertPdfService")
|
||||||
|
private WordConvertPdfService wordConvertPdfService;
|
||||||
|
|
||||||
|
|
||||||
public AjaxResult testAsyncMq() {
|
public AjaxResult testAsyncMq() {
|
||||||
/*taskExecutor.submit(() -> {
|
/*taskExecutor.submit(() -> {
|
||||||
|
|
@ -123,5 +137,85 @@ public class AnalysisService {
|
||||||
return AjaxResult.error();
|
return AjaxResult.error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 招标解析->查看详情
|
||||||
|
* @param dto
|
||||||
|
* @return AjaxResult
|
||||||
|
* @author cwchen
|
||||||
|
* @date 2025/11/25 16:08
|
||||||
|
*/
|
||||||
|
public List<AnalysisBidVo> getBidList(AnalysisDto.TemplateDto dto) {
|
||||||
|
return analysisService.getBidList(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AjaxResult testAsyncMq2() {
|
||||||
|
// 创建OCR识别请求参数
|
||||||
|
OcrRequest ocrRequest = new OcrRequest();
|
||||||
|
ocrRequest.setFile(new File("C:\\Users\\10488\\Desktop\\OCR环境配置.docx"));
|
||||||
|
try {
|
||||||
|
WordConvertPdfResponse wordConvertPdfResponse = wordConvertPdfService.convertWordToPdf(ocrRequest);
|
||||||
|
String pdfBase64 = wordConvertPdfResponse.getPdfBase64();
|
||||||
|
Base64ToPdfConverter.convertToFile(pdfBase64,"C:\\Users\\10488\\Desktop\\test12121212.pdf");
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error(e.toString(),e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 招标解析->查看项目详情
|
||||||
|
* @param dto
|
||||||
|
* @return AjaxResult
|
||||||
|
* @author cwchen
|
||||||
|
* @date 2025/11/26 9:14
|
||||||
|
*/
|
||||||
|
public AjaxResult getProDetail(AnalysisDto.TemplateDto dto) {
|
||||||
|
AnalysisVo analysisVo = analysisService.getProDetail(dto);
|
||||||
|
return AjaxResult.success(analysisVo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 招标解析->更新项目数据
|
||||||
|
* @param dto
|
||||||
|
* @return AjaxResult
|
||||||
|
* @author cwchen
|
||||||
|
* @date 2025/11/26 9:25
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public AjaxResult editProData(AnalysisProDto dto) {
|
||||||
|
try {
|
||||||
|
// 校验数据是否合法
|
||||||
|
String validResult = validatorsUtils.valid(dto, AnalysisProDto.UPDATE.class);
|
||||||
|
if (StringUtils.isNotBlank(validResult)) {
|
||||||
|
return AjaxResult.error(validResult);
|
||||||
|
}
|
||||||
|
// 更新项目数据
|
||||||
|
analysisService.editProData(dto);
|
||||||
|
return AjaxResult.success();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.toString(),e);
|
||||||
|
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
|
||||||
|
return AjaxResult.error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public AjaxResult editBidData(AnalysisBidDto dto) {
|
||||||
|
try {
|
||||||
|
// 校验数据是否合法
|
||||||
|
String validResult = validatorsUtils.valid(dto, AnalysisBidDto.UPDATE.class);
|
||||||
|
if (StringUtils.isNotBlank(validResult)) {
|
||||||
|
return AjaxResult.error(validResult);
|
||||||
|
}
|
||||||
|
// 更新标段数据
|
||||||
|
analysisService.editBidData(dto);
|
||||||
|
return AjaxResult.success();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.toString(),e);
|
||||||
|
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
|
||||||
|
return AjaxResult.error();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
package com.bonus.analysis.mapper;
|
package com.bonus.analysis.mapper;
|
||||||
|
|
||||||
|
import com.bonus.common.domain.analysis.dto.AnalysisBidDto;
|
||||||
import com.bonus.common.domain.analysis.dto.AnalysisDto;
|
import com.bonus.common.domain.analysis.dto.AnalysisDto;
|
||||||
|
import com.bonus.common.domain.analysis.dto.AnalysisProDto;
|
||||||
|
import com.bonus.common.domain.analysis.vo.AnalysisBidVo;
|
||||||
import com.bonus.common.domain.analysis.vo.AnalysisVo;
|
import com.bonus.common.domain.analysis.vo.AnalysisVo;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
|
@ -32,4 +35,40 @@ public interface IASAnalysisMapper {
|
||||||
* @date 2025/11/24 14:05
|
* @date 2025/11/24 14:05
|
||||||
*/
|
*/
|
||||||
void addProData(AnalysisDto.TemplateDto dto);
|
void addProData(AnalysisDto.TemplateDto dto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 招标解析->查看标段详情
|
||||||
|
* @param dto
|
||||||
|
* @return List<AnalysisBidVo>
|
||||||
|
* @author cwchen
|
||||||
|
* @date 2025/11/25 16:27
|
||||||
|
*/
|
||||||
|
List<AnalysisBidVo> getBidList(AnalysisDto.TemplateDto dto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 招标解析->查看项目详情
|
||||||
|
* @param dto
|
||||||
|
* @return AnalysisVo
|
||||||
|
* @author cwchen
|
||||||
|
* @date 2025/11/26 9:16
|
||||||
|
*/
|
||||||
|
AnalysisVo getProDetail(AnalysisDto.TemplateDto dto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新项目数据
|
||||||
|
* @param dto
|
||||||
|
* @return void
|
||||||
|
* @author cwchen
|
||||||
|
* @date 2025/11/26 9:27
|
||||||
|
*/
|
||||||
|
void editProData(AnalysisProDto dto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新标段数据
|
||||||
|
* @param dto
|
||||||
|
* @return void
|
||||||
|
* @author cwchen
|
||||||
|
* @date 2025/11/26 9:39
|
||||||
|
*/
|
||||||
|
void editBidData(AnalysisBidDto dto);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
package com.bonus.analysis.service;
|
package com.bonus.analysis.service;
|
||||||
|
|
||||||
|
import com.bonus.common.domain.analysis.dto.AnalysisBidDto;
|
||||||
import com.bonus.common.domain.analysis.dto.AnalysisDto;
|
import com.bonus.common.domain.analysis.dto.AnalysisDto;
|
||||||
|
import com.bonus.common.domain.analysis.dto.AnalysisProDto;
|
||||||
|
import com.bonus.common.domain.analysis.vo.AnalysisBidVo;
|
||||||
import com.bonus.common.domain.analysis.vo.AnalysisVo;
|
import com.bonus.common.domain.analysis.vo.AnalysisVo;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -31,4 +34,40 @@ public interface IASAnalysisService {
|
||||||
* @date 2025/11/24 14:04
|
* @date 2025/11/24 14:04
|
||||||
*/
|
*/
|
||||||
void addProData(AnalysisDto.TemplateDto dto);
|
void addProData(AnalysisDto.TemplateDto dto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 招标解析->查看详情
|
||||||
|
* @param dto
|
||||||
|
* @return List<AnalysisBidVo>
|
||||||
|
* @author cwchen
|
||||||
|
* @date 2025/11/25 16:27
|
||||||
|
*/
|
||||||
|
List<AnalysisBidVo> getBidList(AnalysisDto.TemplateDto dto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 招标解析->查看项目详情
|
||||||
|
* @param dto
|
||||||
|
* @return AnalysisVo
|
||||||
|
* @author cwchen
|
||||||
|
* @date 2025/11/26 9:15
|
||||||
|
*/
|
||||||
|
AnalysisVo getProDetail(AnalysisDto.TemplateDto dto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新项目数据
|
||||||
|
* @param dto
|
||||||
|
* @return void
|
||||||
|
* @author cwchen
|
||||||
|
* @date 2025/11/26 9:27
|
||||||
|
*/
|
||||||
|
void editProData(AnalysisProDto dto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新标段数据
|
||||||
|
* @param dto
|
||||||
|
* @return void
|
||||||
|
* @author cwchen
|
||||||
|
* @date 2025/11/26 9:38
|
||||||
|
*/
|
||||||
|
void editBidData(AnalysisBidDto dto);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,10 @@ package com.bonus.analysis.service.impl;
|
||||||
|
|
||||||
import com.bonus.analysis.mapper.IASAnalysisMapper;
|
import com.bonus.analysis.mapper.IASAnalysisMapper;
|
||||||
import com.bonus.analysis.service.IASAnalysisService;
|
import com.bonus.analysis.service.IASAnalysisService;
|
||||||
|
import com.bonus.common.domain.analysis.dto.AnalysisBidDto;
|
||||||
import com.bonus.common.domain.analysis.dto.AnalysisDto;
|
import com.bonus.common.domain.analysis.dto.AnalysisDto;
|
||||||
|
import com.bonus.common.domain.analysis.dto.AnalysisProDto;
|
||||||
|
import com.bonus.common.domain.analysis.vo.AnalysisBidVo;
|
||||||
import com.bonus.common.domain.analysis.vo.AnalysisVo;
|
import com.bonus.common.domain.analysis.vo.AnalysisVo;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
@ -10,6 +13,7 @@ import org.springframework.stereotype.Service;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @className:ASAnalysisServiceImpl
|
* @className:ASAnalysisServiceImpl
|
||||||
|
|
@ -34,4 +38,28 @@ public class ASAnalysisServiceImpl implements IASAnalysisService {
|
||||||
public void addProData(AnalysisDto.TemplateDto dto) {
|
public void addProData(AnalysisDto.TemplateDto dto) {
|
||||||
analysisMapper.addProData(dto);
|
analysisMapper.addProData(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AnalysisBidVo> getBidList(AnalysisDto.TemplateDto dto) {
|
||||||
|
return analysisMapper.getBidList(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnalysisVo getProDetail(AnalysisDto.TemplateDto dto) {
|
||||||
|
try {
|
||||||
|
return Optional.ofNullable(analysisMapper.getProDetail(dto)).orElse(new AnalysisVo());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return new AnalysisVo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void editProData(AnalysisProDto dto) {
|
||||||
|
analysisMapper.editProData(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void editBidData(AnalysisBidDto dto) {
|
||||||
|
analysisMapper.editBidData(dto);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,4 +39,52 @@
|
||||||
#{templateId},#{createUserId},#{createUserName},#{updateUserId},#{updateUserName},'0'
|
#{templateId},#{createUserId},#{createUserName},#{updateUserId},#{updateUserName},'0'
|
||||||
)
|
)
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
<!--招标解析->查看标段详情-->
|
||||||
|
<select id="getBidList" resultType="com.bonus.common.domain.analysis.vo.AnalysisBidVo">
|
||||||
|
SELECT bid_id AS bidId,
|
||||||
|
pro_id AS proId,
|
||||||
|
mark_name AS markName,
|
||||||
|
unit,
|
||||||
|
bid_number AS bidNumber,
|
||||||
|
bid_name AS bidName,
|
||||||
|
maximum_bid_limit AS maximumBidLimit,
|
||||||
|
safety_const_fee AS safetyConstFee,
|
||||||
|
bid_bond AS bidBond,
|
||||||
|
duration,
|
||||||
|
bidding_stage AS biddingStage,
|
||||||
|
parsing_state AS parsingState
|
||||||
|
FROM tb_pro_bid
|
||||||
|
WHERE pro_id = #{proId} AND del_flag = '0'
|
||||||
|
ORDER BY create_time DESC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!--招标解析->查看项目详情-->
|
||||||
|
<select id="getProDetail" resultType="com.bonus.common.domain.analysis.vo.AnalysisVo">
|
||||||
|
SELECT tp.pro_id AS proId,
|
||||||
|
tp.pro_name AS proName,
|
||||||
|
tp.pro_code AS proCode,
|
||||||
|
tp.tenderer AS tenderer,
|
||||||
|
tp.agency AS agency,
|
||||||
|
tp.bid_opening_time AS bidOpeningTime,
|
||||||
|
tp.bid_opening_method AS bidOpeningMethod,
|
||||||
|
tp.create_time AS createTime,
|
||||||
|
tp.analysis_status AS analysisStatus,
|
||||||
|
tp.pro_introduction AS proIntroduction
|
||||||
|
FROM tb_pro tp WHERE pro_id = #{proId} AND del_flag = '0'
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!--更新项目数据-->
|
||||||
|
<update id="editProData">
|
||||||
|
UPDATE tb_pro SET pro_name = #{proName},pro_introduction = #{proIntroduction},
|
||||||
|
pro_code = #{proCode},tenderer = #{tenderer},agency = #{agency},bid_opening_time = #{bidOpeningTime},
|
||||||
|
bid_opening_method = #{bidOpeningMethod} WHERE pro_id = #{proId}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<!--更新标段数据-->
|
||||||
|
<update id="editBidData">
|
||||||
|
UPDATE tb_pro_bid SET mark_name = #{markName},unit = #{unit},bid_number = #{bidNumber},
|
||||||
|
bid_name = #{bidName},maximum_bid_limit = #{maximumBidLimit},safety_const_fee = #{safetyConstFee},
|
||||||
|
bid_bond = #{bidBond},duration = #{duration},bidding_stage = #{biddingStage} WHERE bid_id = #{bidId}
|
||||||
|
</update>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ public class SysUser extends BaseEntity
|
||||||
|
|
||||||
public static boolean isAdmin(Long userId)
|
public static boolean isAdmin(Long userId)
|
||||||
{
|
{
|
||||||
return userId != null && 1L == userId;
|
return userId != null && (1L == userId || 6L == userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getDeptId()
|
public Long getDeptId()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
package com.bonus.common.domain.analysis.dto;
|
||||||
|
|
||||||
|
import com.bonus.common.core.domain.model.LoginUser;
|
||||||
|
import com.bonus.common.utils.SecurityUtils;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.hibernate.validator.constraints.Length;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @className:AnalysisDto
|
||||||
|
* @author:cwchen
|
||||||
|
* @date:2025-11-24-9:40
|
||||||
|
* @version:1.0
|
||||||
|
* @description:招标解析-标段dto
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class AnalysisBidDto {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标段id
|
||||||
|
*/
|
||||||
|
@NotNull(message = "标段id不能为空", groups = {UPDATE.class})
|
||||||
|
private Long bidId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标的名称
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "标的名称不能为空", groups = {UPDATE.class})
|
||||||
|
@Length(max = 64, message = "标的名称字符长度不能超过64", groups = {UPDATE.class})
|
||||||
|
private String markName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单位
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "单位名称不能为空", groups = {UPDATE.class})
|
||||||
|
@Length(max = 64, message = "单位名称字符长度不能超过64", groups = {UPDATE.class})
|
||||||
|
private String unit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标段标号
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "标段标号不能为空", groups = {UPDATE.class})
|
||||||
|
@Length(max = 32, message = "标段标号字符长度不能超过32", groups = {UPDATE.class})
|
||||||
|
private String bidNumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标段名称
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "标段名称不能为空", groups = {UPDATE.class})
|
||||||
|
@Length(max = 64, message = "标段名称字符长度不能超过64", groups = {UPDATE.class})
|
||||||
|
private String bidName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最高投标限价(万元)
|
||||||
|
*/
|
||||||
|
@NotNull(message = "最高投标限价不能为空", groups = {UPDATE.class})
|
||||||
|
@Length(max = 64, message = "最高投标限价字符长度不能超过64", groups = {UPDATE.class})
|
||||||
|
private String maximumBidLimit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全文明施工费(万元)
|
||||||
|
*/
|
||||||
|
@NotNull(message = "安全文明施工费不能为空", groups = {UPDATE.class})
|
||||||
|
@Length(max = 64, message = "安全文明施工费字符长度不能超过64", groups = {UPDATE.class})
|
||||||
|
private String safetyConstFee;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 投标保证金(万元)
|
||||||
|
*/
|
||||||
|
@NotNull(message = "投标保证金不能为空", groups = {UPDATE.class})
|
||||||
|
@Length(max = 64, message = "投标保证金字符长度不能超过64", groups = {UPDATE.class})
|
||||||
|
private String bidBond;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工期
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "工期不能为空", groups = {UPDATE.class})
|
||||||
|
@Length(max = 128, message = "工期字符长度不能超过128", groups = {UPDATE.class})
|
||||||
|
private String duration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 招标阶段
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "招标阶段不能为空", groups = {UPDATE.class})
|
||||||
|
@Length(max = 32, message = "招标阶段字符长度不能超过32", groups = {UPDATE.class})
|
||||||
|
private String biddingStage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析状态 0.解析中 1.解析成功 2.解析失败
|
||||||
|
*/
|
||||||
|
private String parsingState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改时间
|
||||||
|
*/
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人
|
||||||
|
*/
|
||||||
|
private Long createUserId = Optional.ofNullable(SecurityUtils.getLoginUser())
|
||||||
|
.map(LoginUser::getUserId)
|
||||||
|
.orElse(null);
|
||||||
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人姓名
|
||||||
|
*/
|
||||||
|
private String createUserName = Optional.ofNullable(SecurityUtils.getLoginUser())
|
||||||
|
.map(LoginUser::getUsername)
|
||||||
|
.orElse(null);
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改人
|
||||||
|
*/
|
||||||
|
private Long updateUserId = Optional.ofNullable(SecurityUtils.getLoginUser())
|
||||||
|
.map(LoginUser::getUserId)
|
||||||
|
.orElse(null);
|
||||||
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改人姓名
|
||||||
|
*/
|
||||||
|
private String updateUserName = Optional.ofNullable(SecurityUtils.getLoginUser())
|
||||||
|
.map(LoginUser::getUsername)
|
||||||
|
.orElse(null);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改条件限制
|
||||||
|
*/
|
||||||
|
public interface UPDATE {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
package com.bonus.common.domain.analysis.dto;
|
||||||
|
|
||||||
|
import com.bonus.common.core.domain.model.LoginUser;
|
||||||
|
import com.bonus.common.domain.file.po.ResourceFilePo;
|
||||||
|
import com.bonus.common.domain.mainDatabase.dto.EnterpriseDto;
|
||||||
|
import com.bonus.common.utils.SecurityUtils;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.hibernate.validator.constraints.Length;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @className:AnalysisDto
|
||||||
|
* @author:cwchen
|
||||||
|
* @date:2025-11-24-9:40
|
||||||
|
* @version:1.0
|
||||||
|
* @description:招标解析dto
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class AnalysisProDto {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目id
|
||||||
|
*/
|
||||||
|
@NotNull(message = "项目id不能为空", groups = {UPDATE.class})
|
||||||
|
private Long proId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目名称
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "项目名称不能为空", groups = {UPDATE.class})
|
||||||
|
@Length(max = 128, message = "项目名称字符长度不能超过128", groups = {UPDATE.class})
|
||||||
|
private String proName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目简介
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "项目简介不能为空", groups = {UPDATE.class})
|
||||||
|
private String proIntroduction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目编号
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "项目编号不能为空", groups = {UPDATE.class})
|
||||||
|
@Length(max = 32, message = "项目编号字符长度不能超过32", groups = {UPDATE.class})
|
||||||
|
private String proCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 招标人
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "招标人不能为空", groups = {UPDATE.class})
|
||||||
|
@Length(max = 32, message = "招标人字符长度不能超过32", groups = {UPDATE.class})
|
||||||
|
private String tenderer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代理机构
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "代理机构不能为空", groups = {UPDATE.class})
|
||||||
|
@Length(max = 64, message = "代理机构字符长度不能超过64", groups = {UPDATE.class})
|
||||||
|
private String agency;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开标时间
|
||||||
|
*/
|
||||||
|
@NotNull(message = "开标时间不能为空", groups = {UPDATE.class})
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||||
|
private Date bidOpeningTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开标方式
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "开标方式不能为空", groups = {UPDATE.class})
|
||||||
|
@Length(max = 32, message = "开标方式字符长度不能超过32", groups = {UPDATE.class})
|
||||||
|
private String bidOpeningMethod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人
|
||||||
|
*/
|
||||||
|
private Long createUserId = Optional.ofNullable(SecurityUtils.getLoginUser())
|
||||||
|
.map(LoginUser::getUserId)
|
||||||
|
.orElse(null);
|
||||||
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人姓名
|
||||||
|
*/
|
||||||
|
private String createUserName = Optional.ofNullable(SecurityUtils.getLoginUser())
|
||||||
|
.map(LoginUser::getUsername)
|
||||||
|
.orElse(null);
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改人
|
||||||
|
*/
|
||||||
|
private Long updateUserId = Optional.ofNullable(SecurityUtils.getLoginUser())
|
||||||
|
.map(LoginUser::getUserId)
|
||||||
|
.orElse(null);
|
||||||
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改人姓名
|
||||||
|
*/
|
||||||
|
private String updateUserName = Optional.ofNullable(SecurityUtils.getLoginUser())
|
||||||
|
.map(LoginUser::getUsername)
|
||||||
|
.orElse(null);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改条件限制
|
||||||
|
*/
|
||||||
|
public interface UPDATE {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
package com.bonus.common.domain.analysis.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @className:AnalysisVo
|
||||||
|
* @author:cwchen
|
||||||
|
* @date:2025-11-24-9:42
|
||||||
|
* @version:1.0
|
||||||
|
* @description:招标解析-标段vo
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class AnalysisBidVo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标段id
|
||||||
|
*/
|
||||||
|
private Long bidId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目id
|
||||||
|
*/
|
||||||
|
private Long proId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标的名称
|
||||||
|
*/
|
||||||
|
private String markName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单位
|
||||||
|
*/
|
||||||
|
private String unit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标段标号
|
||||||
|
*/
|
||||||
|
private String bidNumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标段名称
|
||||||
|
*/
|
||||||
|
private String bidName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最高投标限价(万元)
|
||||||
|
*/
|
||||||
|
private String maximumBidLimit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全文明施工费(万元)
|
||||||
|
*/
|
||||||
|
private String safetyConstFee;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 投标保证金(万元)
|
||||||
|
*/
|
||||||
|
private String bidBond;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工期
|
||||||
|
*/
|
||||||
|
private String duration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 招标阶段
|
||||||
|
*/
|
||||||
|
private String biddingStage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析状态 0.解析中 1.解析成功 2.解析失败
|
||||||
|
*/
|
||||||
|
private String parsingState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人
|
||||||
|
*/
|
||||||
|
private Long createUserId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人姓名
|
||||||
|
*/
|
||||||
|
private String createUserName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改时间
|
||||||
|
*/
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改人
|
||||||
|
*/
|
||||||
|
private Long updateUserId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改人姓名
|
||||||
|
*/
|
||||||
|
private String updateUserName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除状态 0.未删除 1.删除
|
||||||
|
*/
|
||||||
|
private String delFlag;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ import lombok.Data;
|
||||||
import org.springframework.format.annotation.DateTimeFormat;
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @className:AnalysisVo
|
* @className:AnalysisVo
|
||||||
|
|
@ -56,7 +57,7 @@ public class AnalysisVo {
|
||||||
* 开标时间
|
* 开标时间
|
||||||
*/
|
*/
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||||
private Date bidOpeningTime;
|
private Date bidOpeningTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -75,4 +76,14 @@ public class AnalysisVo {
|
||||||
* 解析状态 0.解析中 1.解析成功 2.解析失败
|
* 解析状态 0.解析中 1.解析成功 2.解析失败
|
||||||
*/
|
*/
|
||||||
private String analysisStatus;
|
private String analysisStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目简介
|
||||||
|
*/
|
||||||
|
private String proIntroduction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标段list
|
||||||
|
* */
|
||||||
|
private List<AnalysisBidVo> bidList;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.bonus.common.domain.ocr.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @className:WordConvertPdfResponse
|
||||||
|
* @author:cwchen
|
||||||
|
* @date:2025-11-25-17:53
|
||||||
|
* @version:1.0
|
||||||
|
* @description: word转pdf响应结果
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class WordConvertPdfResponse {
|
||||||
|
|
||||||
|
private String status; // "success" 或 "error"
|
||||||
|
private String filename; // 文件名
|
||||||
|
private String pdfBase64; // PDF base64数据
|
||||||
|
private String message; // 错误信息
|
||||||
|
|
||||||
|
// getter和setter方法
|
||||||
|
// 辅助方法
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return "success".equals(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,179 @@
|
||||||
|
package com.bonus.common.utils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64转PDF工具类
|
||||||
|
*/
|
||||||
|
public class Base64ToPdfConverter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Base64字符串转换为PDF文件
|
||||||
|
*
|
||||||
|
* @param base64Content Base64编码的PDF内容
|
||||||
|
* @param outputPath 输出PDF文件路径
|
||||||
|
* @return 转换是否成功
|
||||||
|
*/
|
||||||
|
public static boolean convertToFile(String base64Content, String outputPath) {
|
||||||
|
return convertToFile(base64Content, new File(outputPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Base64字符串转换为PDF文件
|
||||||
|
*
|
||||||
|
* @param base64Content Base64编码的PDF内容
|
||||||
|
* @param outputFile 输出PDF文件对象
|
||||||
|
* @return 转换是否成功
|
||||||
|
*/
|
||||||
|
public static boolean convertToFile(String base64Content, File outputFile) {
|
||||||
|
if (base64Content == null || base64Content.trim().isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Base64内容不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理可能的Base64前缀(如:data:application/pdf;base64,)
|
||||||
|
String cleanBase64 = cleanBase64Content(base64Content);
|
||||||
|
|
||||||
|
FileOutputStream fos = null;
|
||||||
|
try {
|
||||||
|
// 确保输出目录存在
|
||||||
|
File parentDir = outputFile.getParentFile();
|
||||||
|
if (parentDir != null && !parentDir.exists()) {
|
||||||
|
parentDir.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解码Base64
|
||||||
|
byte[] pdfBytes = Base64.getDecoder().decode(cleanBase64);
|
||||||
|
|
||||||
|
// 写入文件
|
||||||
|
fos = new FileOutputStream(outputFile);
|
||||||
|
fos.write(pdfBytes);
|
||||||
|
fos.flush();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Base64转PDF失败: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
if (fos != null) {
|
||||||
|
try {
|
||||||
|
fos.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err.println("关闭文件流失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Base64字符串转换为字节数组
|
||||||
|
*
|
||||||
|
* @param base64Content Base64编码的PDF内容
|
||||||
|
* @return PDF字节数组
|
||||||
|
*/
|
||||||
|
public static byte[] convertToBytes(String base64Content) {
|
||||||
|
if (base64Content == null || base64Content.trim().isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Base64内容不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String cleanBase64 = cleanBase64Content(base64Content);
|
||||||
|
return Base64.getDecoder().decode(cleanBase64);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new IllegalArgumentException("Base64内容格式不正确", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证Base64字符串是否为有效的PDF内容
|
||||||
|
*
|
||||||
|
* @param base64Content Base64编码的内容
|
||||||
|
* @return 是否为有效的PDF内容
|
||||||
|
*/
|
||||||
|
public static boolean isValidPdfContent(String base64Content) {
|
||||||
|
if (base64Content == null || base64Content.trim().isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String cleanBase64 = cleanBase64Content(base64Content);
|
||||||
|
byte[] pdfBytes = Base64.getDecoder().decode(cleanBase64);
|
||||||
|
|
||||||
|
// 检查PDF文件头(PDF文件通常以 "%PDF-" 开头)
|
||||||
|
if (pdfBytes.length >= 5) {
|
||||||
|
return pdfBytes[0] == '%' &&
|
||||||
|
pdfBytes[1] == 'P' &&
|
||||||
|
pdfBytes[2] == 'D' &&
|
||||||
|
pdfBytes[3] == 'F' &&
|
||||||
|
pdfBytes[4] == '-';
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理Base64内容,移除可能的数据URI前缀
|
||||||
|
*
|
||||||
|
* @param base64Content 原始Base64内容
|
||||||
|
* @return 清理后的Base64内容
|
||||||
|
*/
|
||||||
|
private static String cleanBase64Content(String base64Content) {
|
||||||
|
String content = base64Content.trim();
|
||||||
|
|
||||||
|
// 移除常见的数据URI前缀
|
||||||
|
String[] prefixes = {
|
||||||
|
"data:application/pdf;base64,",
|
||||||
|
"data:application/octet-stream;base64,",
|
||||||
|
"data:application/x-pdf;base64,",
|
||||||
|
"data:text/plain;base64,"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (String prefix : prefixes) {
|
||||||
|
if (content.startsWith(prefix)) {
|
||||||
|
return content.substring(prefix.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量转换多个Base64内容为PDF文件
|
||||||
|
*
|
||||||
|
* @param base64Files 包含文件名和Base64内容的映射
|
||||||
|
* @param outputDirectory 输出目录
|
||||||
|
* @return 成功转换的文件数量
|
||||||
|
*/
|
||||||
|
public static int batchConvert(java.util.Map<String, String> base64Files, String outputDirectory) {
|
||||||
|
if (base64Files == null || base64Files.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
File outputDir = new File(outputDirectory);
|
||||||
|
if (!outputDir.exists()) {
|
||||||
|
outputDir.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
int successCount = 0;
|
||||||
|
for (java.util.Map.Entry<String, String> entry : base64Files.entrySet()) {
|
||||||
|
String fileName = entry.getKey();
|
||||||
|
if (!fileName.toLowerCase().endsWith(".pdf")) {
|
||||||
|
fileName += ".pdf";
|
||||||
|
}
|
||||||
|
|
||||||
|
File outputFile = new File(outputDir, fileName);
|
||||||
|
if (convertToFile(entry.getValue(), outputFile)) {
|
||||||
|
successCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return successCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -122,7 +122,7 @@ public class SecurityUtils
|
||||||
*/
|
*/
|
||||||
public static boolean isAdmin(Long userId)
|
public static boolean isAdmin(Long userId)
|
||||||
{
|
{
|
||||||
return userId != null && 1L == userId;
|
return userId != null && (1L == userId || 6L == userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -59,12 +59,12 @@ public class ResourcesConfig implements WebMvcConfigurer
|
||||||
{
|
{
|
||||||
registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
|
registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
|
||||||
// 参数校验拦截器
|
// 参数校验拦截器
|
||||||
registry.addInterceptor(paramSecureHandler)
|
/*registry.addInterceptor(paramSecureHandler)
|
||||||
.addPathPatterns("/**")
|
.addPathPatterns("/**")
|
||||||
.excludePathPatterns(EXCLUDEURLS)
|
.excludePathPatterns(EXCLUDEURLS)
|
||||||
.order(-10);
|
.order(-10);*/
|
||||||
// 防重放拦截器
|
// 防重放拦截器
|
||||||
registry.addInterceptor(replayAttackInterceptor)
|
/*registry.addInterceptor(replayAttackInterceptor)
|
||||||
.addPathPatterns("/**")
|
.addPathPatterns("/**")
|
||||||
.excludePathPatterns("/smartBid/captchaImage")
|
.excludePathPatterns("/smartBid/captchaImage")
|
||||||
.excludePathPatterns("/smartBid/login")
|
.excludePathPatterns("/smartBid/login")
|
||||||
|
|
@ -74,7 +74,7 @@ public class ResourcesConfig implements WebMvcConfigurer
|
||||||
.excludePathPatterns("/smartBid/session/check")
|
.excludePathPatterns("/smartBid/session/check")
|
||||||
.excludePathPatterns("/smartBid/sys/config/getConfig")
|
.excludePathPatterns("/smartBid/sys/config/getConfig")
|
||||||
.excludePathPatterns(EXCLUDEURLS)
|
.excludePathPatterns(EXCLUDEURLS)
|
||||||
.order(-15);
|
.order(-15);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,259 @@
|
||||||
|
package com.bonus.ocr.service;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import com.bonus.common.domain.ocr.dto.OcrRequest;
|
||||||
|
import com.bonus.common.domain.ocr.vo.WordConvertPdfResponse;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.client.config.RequestConfig;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClients;
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.apache.http.entity.mime.MultipartEntityBuilder;
|
||||||
|
import org.apache.http.entity.mime.HttpMultipartMode;
|
||||||
|
import org.apache.http.entity.mime.content.FileBody;
|
||||||
|
import org.apache.http.entity.ContentType;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
/**
|
||||||
|
* @className: WordConvertPdfService
|
||||||
|
* @author: cwchen
|
||||||
|
* @date: 2025-11-25-17:46
|
||||||
|
* @version: 1.0
|
||||||
|
* @description: word转pdf服务
|
||||||
|
*/
|
||||||
|
@Service(value = "WordConvertPdfService")
|
||||||
|
@Slf4j
|
||||||
|
public class WordConvertPdfService {
|
||||||
|
|
||||||
|
private static final String UTF_8 = "UTF-8";
|
||||||
|
private static final String FILE_PART_NAME = "file";
|
||||||
|
private static final String TYPE_PART_NAME = "type";
|
||||||
|
|
||||||
|
@Value("${ocr.service.convertUrl}")
|
||||||
|
private String ocrServiceUrl;
|
||||||
|
|
||||||
|
@Value("${ocr.service.timeout:30000}")
|
||||||
|
private int timeout;
|
||||||
|
|
||||||
|
private final CloseableHttpClient httpClient;
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
public WordConvertPdfService() {
|
||||||
|
// 使用默认值30000毫秒作为fallback
|
||||||
|
int actualTimeout = timeout > 0 ? timeout : 30000;
|
||||||
|
|
||||||
|
RequestConfig requestConfig = RequestConfig.custom()
|
||||||
|
.setConnectTimeout(actualTimeout)
|
||||||
|
.setSocketTimeout(actualTimeout)
|
||||||
|
.setConnectionRequestTimeout(actualTimeout)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
this.httpClient = HttpClients.custom()
|
||||||
|
.setDefaultRequestConfig(requestConfig)
|
||||||
|
.build();
|
||||||
|
this.objectMapper = new ObjectMapper();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用文件转换服务进行Word转PDF
|
||||||
|
*
|
||||||
|
* @param ocrRequest OCR请求参数
|
||||||
|
* @return OCR响应结果,包含PDF base64数据
|
||||||
|
* @throws IOException 当文件转换服务调用失败时抛出
|
||||||
|
*/
|
||||||
|
public WordConvertPdfResponse convertWordToPdf(OcrRequest ocrRequest) throws IOException {
|
||||||
|
validateOcrRequest(ocrRequest);
|
||||||
|
|
||||||
|
HttpPost httpPost = null;
|
||||||
|
try {
|
||||||
|
httpPost = createHttpPost(ocrRequest);
|
||||||
|
return executeOcrRequest(httpPost);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("调用文件转换服务进行Word转PDF失败", e);
|
||||||
|
throw new IOException("Word转PDF服务调用失败: " + e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
cleanupResources(ocrRequest, httpPost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证OCR请求参数
|
||||||
|
*/
|
||||||
|
private void validateOcrRequest(OcrRequest ocrRequest) {
|
||||||
|
if (ocrRequest == null) {
|
||||||
|
throw new IllegalArgumentException("OCR请求参数不能为空");
|
||||||
|
}
|
||||||
|
if (ocrRequest.getFile() == null || !ocrRequest.getFile().exists()) {
|
||||||
|
throw new IllegalArgumentException("Word文件不能为空或文件不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 验证文件类型是否为Word文档
|
||||||
|
String fileName = ocrRequest.getFile().getName().toLowerCase();
|
||||||
|
if (!fileName.endsWith(".doc") && !fileName.endsWith(".docx")) {
|
||||||
|
log.warn("文件类型可能不是Word文档: {}", fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建HTTP POST请求
|
||||||
|
*/
|
||||||
|
private HttpPost createHttpPost(OcrRequest ocrRequest) {
|
||||||
|
HttpPost httpPost = new HttpPost(ocrServiceUrl);
|
||||||
|
|
||||||
|
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
|
||||||
|
builder.setCharset(StandardCharsets.UTF_8);
|
||||||
|
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
|
||||||
|
|
||||||
|
// 添加文件字段
|
||||||
|
builder.addPart(FILE_PART_NAME,
|
||||||
|
new FileBody(ocrRequest.getFile(),
|
||||||
|
ContentType.MULTIPART_FORM_DATA,
|
||||||
|
ocrRequest.getFile().getName()));
|
||||||
|
|
||||||
|
httpPost.setEntity(builder.build());
|
||||||
|
httpPost.setHeader("Accept", "application/json");
|
||||||
|
|
||||||
|
return httpPost;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行文件转换服务请求
|
||||||
|
*/
|
||||||
|
private WordConvertPdfResponse executeOcrRequest(HttpPost httpPost) throws IOException {
|
||||||
|
log.info("开始调用转换服务进行Word转PDF");
|
||||||
|
|
||||||
|
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
|
||||||
|
return processHttpResponse(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理HTTP响应
|
||||||
|
*/
|
||||||
|
private WordConvertPdfResponse processHttpResponse(CloseableHttpResponse response) throws IOException {
|
||||||
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
|
String responseBody = getResponseBody(response);
|
||||||
|
|
||||||
|
log.info("文件转换服务响应状态: {}", statusCode);
|
||||||
|
log.debug("文件转换服务响应内容: {}", responseBody);
|
||||||
|
|
||||||
|
// 检查HTTP状态码
|
||||||
|
if (statusCode != 200) {
|
||||||
|
log.error("文件转换服务HTTP请求失败,状态码: {}, 响应: {}", statusCode, responseBody);
|
||||||
|
throw new IOException("文件转换服务HTTP请求失败,状态码: " + statusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
WordConvertPdfResponse WordConvertPdfResponse = parseResponseBody(responseBody);
|
||||||
|
handleConvertResult(WordConvertPdfResponse);
|
||||||
|
|
||||||
|
return WordConvertPdfResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取响应体
|
||||||
|
*/
|
||||||
|
private String getResponseBody(CloseableHttpResponse response) throws IOException {
|
||||||
|
HttpEntity entity = response.getEntity();
|
||||||
|
return EntityUtils.toString(entity, UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析响应体
|
||||||
|
*/
|
||||||
|
private WordConvertPdfResponse parseResponseBody(String responseBody) throws IOException {
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue(responseBody, WordConvertPdfResponse.class);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("解析OCR响应失败,响应内容: {}", responseBody, e);
|
||||||
|
throw new IOException("解析OCR响应失败: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理Word转PDF结果
|
||||||
|
*/
|
||||||
|
private void handleConvertResult(WordConvertPdfResponse WordConvertPdfResponse) {
|
||||||
|
if (WordConvertPdfResponse.isSuccess()) {
|
||||||
|
log.info("Word转PDF成功,文件名: {}", WordConvertPdfResponse.getFilename());
|
||||||
|
logPdfResult(WordConvertPdfResponse);
|
||||||
|
} else {
|
||||||
|
log.warn("Word转PDF失败: {}", WordConvertPdfResponse.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录PDF转换结果
|
||||||
|
*/
|
||||||
|
private void logPdfResult(WordConvertPdfResponse WordConvertPdfResponse) {
|
||||||
|
if (WordConvertPdfResponse.getPdfBase64() != null) {
|
||||||
|
int pdfLength = WordConvertPdfResponse.getPdfBase64().length();
|
||||||
|
log.info("PDF Base64数据长度: {} 字符", pdfLength);
|
||||||
|
|
||||||
|
// 记录前50个字符用于调试
|
||||||
|
if (log.isDebugEnabled() && pdfLength > 50) {
|
||||||
|
log.debug("PDF Base64前缀: {}...", WordConvertPdfResponse.getPdfBase64().substring(0, 50));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.warn("PDF Base64数据为空");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理资源
|
||||||
|
*/
|
||||||
|
private void cleanupResources(OcrRequest ocrRequest, HttpPost httpPost) {
|
||||||
|
// 清理HTTP连接
|
||||||
|
if (httpPost != null) {
|
||||||
|
httpPost.releaseConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理临时文件
|
||||||
|
// cleanupTempFile(ocrRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理临时文件
|
||||||
|
*/
|
||||||
|
private void cleanupTempFile(OcrRequest ocrRequest) {
|
||||||
|
if (ocrRequest.getFile() != null && ocrRequest.getFile().exists()) {
|
||||||
|
try {
|
||||||
|
boolean deleted = ocrRequest.getFile().delete();
|
||||||
|
if (!deleted) {
|
||||||
|
log.warn("临时文件删除失败: {}", ocrRequest.getFile().getAbsolutePath());
|
||||||
|
} else {
|
||||||
|
log.debug("临时文件已删除: {}", ocrRequest.getFile().getAbsolutePath());
|
||||||
|
}
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
log.error("删除临时文件时发生安全异常: {}", ocrRequest.getFile().getAbsolutePath(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭HTTP客户端
|
||||||
|
*/
|
||||||
|
public void close() {
|
||||||
|
try {
|
||||||
|
if (httpClient != null) {
|
||||||
|
httpClient.close();
|
||||||
|
log.info("Word转PDF服务HTTP客户端已关闭");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("关闭HTTP客户端失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销毁方法,用于Spring容器关闭时调用
|
||||||
|
*/
|
||||||
|
public void destroy() {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue