From 8c996fe9afc4a5c8d34c01bd24213e29675665f3 Mon Sep 17 00:00:00 2001 From: wanghb <17803890193@163.com> Date: Tue, 5 Dec 2023 16:29:06 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AD=BE=E7=BA=A6=E6=8A=95=E8=B5=84=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E5=B0=BD=E8=B0=83=E6=8A=A5=E5=91=8A=EF=BC=8C=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E4=BC=B0=E5=80=BC=E6=8A=A5=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 11 + .../ContractInvestmentController.java | 195 ++++++ .../dto/InvestIntentionDetailedDto.java | 48 ++ .../entity/dto/ReportUploadDto.java | 26 + .../service/ContractInvestmentService.java | 20 + .../ContractInvestmentServiceImpl.java | 72 +++ .../sztzjy/fund_investment/util/SealUtil.java | 46 ++ .../fund_investment/util/seal/SealCircle.java | 12 + .../fund_investment/util/seal/SealFont.java | 37 ++ .../fund_investment/util/seal/SealUtil.java | 553 ++++++++++++++++++ .../FundInvestmentApplicationTests.java | 65 ++ 11 files changed, 1085 insertions(+) create mode 100644 src/main/java/com/sztzjy/fund_investment/controller/ContractInvestmentController.java create mode 100644 src/main/java/com/sztzjy/fund_investment/entity/dto/InvestIntentionDetailedDto.java create mode 100644 src/main/java/com/sztzjy/fund_investment/entity/dto/ReportUploadDto.java create mode 100644 src/main/java/com/sztzjy/fund_investment/service/ContractInvestmentService.java create mode 100644 src/main/java/com/sztzjy/fund_investment/service/serviceImpl/ContractInvestmentServiceImpl.java create mode 100644 src/main/java/com/sztzjy/fund_investment/util/SealUtil.java create mode 100644 src/main/java/com/sztzjy/fund_investment/util/seal/SealCircle.java create mode 100644 src/main/java/com/sztzjy/fund_investment/util/seal/SealFont.java create mode 100644 src/main/java/com/sztzjy/fund_investment/util/seal/SealUtil.java diff --git a/pom.xml b/pom.xml index d3559c6..8a84185 100644 --- a/pom.xml +++ b/pom.xml @@ -167,6 +167,17 @@ caffeine 2.9.3 + + org.springframework + spring-test + 5.3.14 + + + + com.alibaba + easyexcel + 3.2.1 + diff --git a/src/main/java/com/sztzjy/fund_investment/controller/ContractInvestmentController.java b/src/main/java/com/sztzjy/fund_investment/controller/ContractInvestmentController.java new file mode 100644 index 0000000..fd80247 --- /dev/null +++ b/src/main/java/com/sztzjy/fund_investment/controller/ContractInvestmentController.java @@ -0,0 +1,195 @@ +package com.sztzjy.fund_investment.controller; + +import cn.hutool.core.io.resource.ClassPathResource; +import com.nimbusds.jose.shaded.gson.Gson; +import com.sztzjy.fund_investment.annotation.AnonymousAccess; +import com.sztzjy.fund_investment.entity.*; +import com.sztzjy.fund_investment.entity.dto.InvestIntentionDetailedDto; +import com.sztzjy.fund_investment.entity.dto.ReportUploadDto; +import com.sztzjy.fund_investment.mapper.FoundProjectMapper; +import com.sztzjy.fund_investment.mapper.ProjectPoolMapper; +import com.sztzjy.fund_investment.service.ContractInvestmentService; +import com.sztzjy.fund_investment.service.ISysProjectDueDiligenceService; +import com.sztzjy.fund_investment.util.ResultEntity; +import com.sztzjy.fund_investment.util.SealUtil; +import com.sztzjy.fund_investment.util.file.IFileUtil; +import com.sztzjy.fund_investment.util.file.LocalFileUtil; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; + +/** + * @author 17803 + * @date 2023-12-05 8:40 + */ + +@RestController +@RequestMapping("/stu/contractInvestment") +@Api(tags = "学生端--签约投资") +public class ContractInvestmentController { + + + @Autowired + private ContractInvestmentService contractInvestmentService; + + @Autowired + private FoundProjectMapper foundProjectMapper; + + @Autowired + private ProjectPoolMapper projectPoolMapper; + + @Autowired + private SealUtil sealUtil; + + @Value("${file.path}") + private String filePath; + + @Autowired + ISysProjectDueDiligenceService projectDueDiligenceService; + + + @Resource + IFileUtil fileUtil; + + /** + * 上传尽调报告 + */ + + @AnonymousAccess + @PostMapping("/uploadDueDiligenceReport") + @ApiOperation("上传尽调报告和上传估值报告") + public ResultEntity uploadDueDiligenceReport(@RequestParam("file") @RequestPart(name = "file") MultipartFile file, + @ApiParam("json格式数据") @RequestParam String reportUploadDto) { + Gson gson = new Gson(); + ReportUploadDto reportNameType = gson.fromJson(reportUploadDto, ReportUploadDto.class); + return contractInvestmentService.uploadDueDiligenceReport(file, reportNameType); + } + + + @AnonymousAccess + @GetMapping("/totalInvestAmount") + @ApiOperation("发出投资意向书输入总投资额度") + public ResultEntity totalInvestAmount(@ApiParam("流程ID") @RequestParam String flowId, + @ApiParam("总投资额度") @RequestParam Double totalInvest) { + //金额不低于100万 + if (10000000 > totalInvest) { + return new ResultEntity(HttpStatus.BAD_REQUEST, "金额不能低于100万"); + } + //查询估值 + + FoundProjectExample foundProjectExample = new FoundProjectExample(); + foundProjectExample.createCriteria().andFlowIdEqualTo(flowId); + List foundProjects = foundProjectMapper.selectByExample(foundProjectExample); + if (foundProjects.size() > 0) { + //立项项目ID + String projectPoolId = foundProjects.get(0).getProjectPoolId(); + + ProjectPoolExample projectPoolExample = new ProjectPoolExample(); + projectPoolExample.createCriteria().andProjectPoolIdEqualTo(projectPoolId); + List projectPools = projectPoolMapper.selectByExample(projectPoolExample); + if (projectPools.size() > 0) { + //项目估值 + String latestValuation = projectPools.get(0).getLatestValuation(); + //转为Double类型 + double v = Double.parseDouble(latestValuation); + // 金额不低于100万,持股比例自动显示,为总投资额/估值,持股比例不低于5% + double proportion = totalInvest / v; + if (0.05 > proportion) { + return new ResultEntity(HttpStatus.BAD_REQUEST, "持股比例不低于5%"); + } + return new ResultEntity<>(HttpStatus.OK, proportion); + } + + + } else { + return new ResultEntity(HttpStatus.BAD_REQUEST, "查询不到立项项目"); + } + return null; + } + + + @AnonymousAccess + @GetMapping("/getInvestIntentionDetailedInfo") + @ApiOperation("投资意向书甲乙双方详细信息") + public ResultEntity getInvestIntentionDetailedInfo(@ApiParam("流程ID") @RequestParam String flowId) { + //获取乙方数据 + + List list = projectDueDiligenceService.getBusinessInfoShareholderListByFlowId(flowId); + + +// InvestIntentionDetailedDto intentionDetailedDto = InvestIntentionDetailedDto.builder(). +// contactPersonB(list.get(0).getLegalPerson()). +// addressB() +// .build(); +// +// return new ResultEntity<>(HttpStatus.OK, intentionDetailedDto); + return null; + } + + + + + + + + @AnonymousAccess + @GetMapping("/seal") + @ApiOperation("生成乙方公章") + public ResponseEntity getSealImage(@ApiParam("流程ID") @RequestParam String flowId) throws Exception { + String s = null; + //获取乙方数据 + FoundProjectExample foundProjectExample = new FoundProjectExample(); + foundProjectExample.createCriteria().andFlowIdEqualTo(flowId); + List foundProjects = foundProjectMapper.selectByExample(foundProjectExample); + if (foundProjects.size() > 0) { + //立项项目ID + String projectPoolId = foundProjects.get(0).getProjectPoolId(); + + ProjectPoolExample projectPoolExample = new ProjectPoolExample(); + projectPoolExample.createCriteria().andProjectPoolIdEqualTo(projectPoolId); + List projectPools = projectPoolMapper.selectByExample(projectPoolExample); + if (projectPools.size() > 0) { + //项目估值 + ProjectPool projectPool = projectPools.get(0); + //生成公章 + s = sealUtil.genertSeal(projectPool.getCompanyName()); +// FileInputStream fileInputStream = new FileInputStream(filePath+"/seal/"+ s +".png"); +// +// MultipartFile multipartFile = new MockMultipartFile( +// "example.png", // 文件名 +// s + ".png", // 原始文件名 +// "png", // 文件类型 +// fileInputStream +// ); +// String upload = fileUtil.upload(multipartFile); + + + } + } + // 模拟加载公章图片的数据(假设在硬盘上的某个路径下) + String imagePath = filePath + "/seal/" + s + ".png"; // 替换为你的实际路径 + byte[] sealBytes = Files.readAllBytes(Paths.get(imagePath)); + + // 构建HTTP响应,设置Content-Type为image/png + return ResponseEntity.ok() + .contentType(MediaType.IMAGE_PNG) + .body(sealBytes); + + } + +} diff --git a/src/main/java/com/sztzjy/fund_investment/entity/dto/InvestIntentionDetailedDto.java b/src/main/java/com/sztzjy/fund_investment/entity/dto/InvestIntentionDetailedDto.java new file mode 100644 index 0000000..5f01460 --- /dev/null +++ b/src/main/java/com/sztzjy/fund_investment/entity/dto/InvestIntentionDetailedDto.java @@ -0,0 +1,48 @@ +package com.sztzjy.fund_investment.entity.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Builder; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * @author 17803 + * @date 2023-12-05 14:31 + */ + +@Data +@Builder +public class InvestIntentionDetailedDto { + + @ApiModelProperty("签署时间") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8") + private Date signingTime; + + @ApiModelProperty("甲方联系人") + private String contactPersonA; + + @ApiModelProperty("甲方联系方式") + private String contactInformationA; + + @ApiModelProperty("甲方地址") + private String addressA; + + + @ApiModelProperty("乙方联系人") + private String contactPersonB; + @ApiModelProperty("乙方地址") + private String addressB; + + public InvestIntentionDetailedDto(Date signingTime, String contactPersonA, String contactInformationA, String addressA, String contactPersonB, String addressB) { + this.signingTime = new Date(); + this.contactPersonA = "天泽股权投资基金"; + this.contactInformationA = "0755-02103100"; + this.addressA = "广东省深圳市宝安区"; + this.contactPersonB = contactPersonB; + this.addressB = addressB; + } +} diff --git a/src/main/java/com/sztzjy/fund_investment/entity/dto/ReportUploadDto.java b/src/main/java/com/sztzjy/fund_investment/entity/dto/ReportUploadDto.java new file mode 100644 index 0000000..152a77b --- /dev/null +++ b/src/main/java/com/sztzjy/fund_investment/entity/dto/ReportUploadDto.java @@ -0,0 +1,26 @@ +package com.sztzjy.fund_investment.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.Data; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * @author 17803 + * @date 2023-12-05 9:30 + */ + +@Data +public class ReportUploadDto { + + @ApiModelProperty("文件名称") + String fileName; + @ApiModelProperty("流程id") + String flowId; + @ApiModelProperty("学校ID") + String schoolId; + @ApiModelProperty("报告类型(1.尽调报告,2.估值报告)") + String reportNameType; + +} diff --git a/src/main/java/com/sztzjy/fund_investment/service/ContractInvestmentService.java b/src/main/java/com/sztzjy/fund_investment/service/ContractInvestmentService.java new file mode 100644 index 0000000..6450f4f --- /dev/null +++ b/src/main/java/com/sztzjy/fund_investment/service/ContractInvestmentService.java @@ -0,0 +1,20 @@ +package com.sztzjy.fund_investment.service; + +import com.sztzjy.fund_investment.entity.dto.ReportUploadDto; +import com.sztzjy.fund_investment.util.ResultEntity; +import org.springframework.web.multipart.MultipartFile; + +/** + * @author 17803 + * @date 2023-12-05 9:01 + */ +public interface ContractInvestmentService { + + + /** + * 上传尽调报告 + */ + ResultEntity uploadDueDiligenceReport(MultipartFile file, ReportUploadDto reportNameType); + + +} diff --git a/src/main/java/com/sztzjy/fund_investment/service/serviceImpl/ContractInvestmentServiceImpl.java b/src/main/java/com/sztzjy/fund_investment/service/serviceImpl/ContractInvestmentServiceImpl.java new file mode 100644 index 0000000..e628867 --- /dev/null +++ b/src/main/java/com/sztzjy/fund_investment/service/serviceImpl/ContractInvestmentServiceImpl.java @@ -0,0 +1,72 @@ +package com.sztzjy.fund_investment.service.serviceImpl;/** + * @author 17803 + * @date 2023-12-05 9:02 + */ + +import cn.hutool.core.util.IdUtil; +import com.sztzjy.fund_investment.entity.TrainingReport; +import com.sztzjy.fund_investment.entity.TrainingReportExample; +import com.sztzjy.fund_investment.entity.dto.ReportUploadDto; +import com.sztzjy.fund_investment.mapper.TrainingReportMapper; +import com.sztzjy.fund_investment.service.ContractInvestmentService; +import com.sztzjy.fund_investment.util.ResultEntity; +import com.sztzjy.fund_investment.util.file.IFileUtil; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Date; +import java.util.List; +import java.util.UUID; + +@Service +public class ContractInvestmentServiceImpl implements ContractInvestmentService { + + @Autowired + private IFileUtil fileUtil; + @Autowired + TrainingReportMapper trainingReportMapper; + + /** 上传尽调报告 + * + */ + @Override + public ResultEntity uploadDueDiligenceReport(MultipartFile file, ReportUploadDto reportNameType) { + //返回文件上传路径 + String filePath = fileUtil.upload(file); + //获取文件名 + String originalFilename = file.getOriginalFilename(); + //截取文件后缀 + String fileExtension = originalFilename.substring(originalFilename.lastIndexOf(".")).toLowerCase(); + if (!fileExtension.equals(".pdf") && !fileExtension.equals(".doc") && !fileExtension.equals(".docx")) { + return new ResultEntity<>(HttpStatus.BAD_REQUEST, "文件必须为word或pdf!"); + } + //查看文件是否已经上传 + List trainingReports = getTrainingReports(reportNameType.getFlowId(),reportNameType.getReportNameType()); + if (trainingReports.size()>0) + { + return new ResultEntity<>(HttpStatus.BAD_REQUEST, "不允许重复提交!"); + } + else { //只允许提交一次 + TrainingReport trainingReport = new TrainingReport(); + trainingReport.setId(IdUtil.fastSimpleUUID()); + trainingReport.setFlowId(reportNameType.getFlowId()); + trainingReport.setSchoolId(reportNameType.getSchoolId()); + trainingReport.setUrl(filePath); + trainingReport.setReportName(reportNameType.getFileName()); + trainingReport.setUploadtime(new Date()); + trainingReport.setStep(reportNameType.getReportNameType()); + trainingReportMapper.insert(trainingReport); + return new ResultEntity<>(HttpStatus.OK, "报告上传成功!"); + } + } + + public List getTrainingReports(String flowId,String reportName) { + TrainingReportExample trainingReportExample = new TrainingReportExample(); + trainingReportExample.createCriteria().andFlowIdEqualTo(flowId).andStepEqualTo(reportName); + return trainingReportMapper.selectByExample(trainingReportExample); + } + +} diff --git a/src/main/java/com/sztzjy/fund_investment/util/SealUtil.java b/src/main/java/com/sztzjy/fund_investment/util/SealUtil.java new file mode 100644 index 0000000..42ce1bc --- /dev/null +++ b/src/main/java/com/sztzjy/fund_investment/util/SealUtil.java @@ -0,0 +1,46 @@ +package com.sztzjy.fund_investment.util; + +import cn.hutool.core.util.IdUtil; +import com.sztzjy.fund_investment.util.file.LocalFileUtil; +import com.sztzjy.fund_investment.util.seal.SealCircle; +import com.sztzjy.fund_investment.util.seal.SealFont; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.io.File; + +/** + * @author 17803 + * @date 2023-12-05 15:11 + */ + +@Component +public class SealUtil { + + @Resource + LocalFileUtil localFileUtil; + + @Value("${file.path}") + private String filePath; + + public String genertSeal(String name) throws Exception{ + String file = filePath+"/seal/"; + // 创建 File 对象 + File infoFile = new File(file); + if (!infoFile.exists()) { + infoFile.mkdir(); + } + String s = IdUtil.fastSimpleUUID(); + com.sztzjy.fund_investment.util.seal.SealUtil.builder() + .size(200) + .borderCircle(SealCircle.builder().line(4).width(95).height(95).build()) + .mainFont(SealFont.builder().text(name+"有限公司").size(22).space(22.0).margin(4).build()) + .centerFont(SealFont.builder().text("★").size(60).build()) + .titleFont(SealFont.builder().text("电子签章").size(16).space(8.0).margin(54).build()) + .build() + .draw(filePath+"/seal/"+ s +".png"); + System.out.println(name+"公章已生成"); + return s; + } +} diff --git a/src/main/java/com/sztzjy/fund_investment/util/seal/SealCircle.java b/src/main/java/com/sztzjy/fund_investment/util/seal/SealCircle.java new file mode 100644 index 0000000..e7c3a14 --- /dev/null +++ b/src/main/java/com/sztzjy/fund_investment/util/seal/SealCircle.java @@ -0,0 +1,12 @@ +package com.sztzjy.fund_investment.util.seal; + + +import lombok.Builder; +import lombok.Getter; +@Getter +@Builder +public class SealCircle { + private Integer line; + private Integer width; + private Integer height; +} diff --git a/src/main/java/com/sztzjy/fund_investment/util/seal/SealFont.java b/src/main/java/com/sztzjy/fund_investment/util/seal/SealFont.java new file mode 100644 index 0000000..8a37d0b --- /dev/null +++ b/src/main/java/com/sztzjy/fund_investment/util/seal/SealFont.java @@ -0,0 +1,37 @@ +package com.sztzjy.fund_investment.util.seal;/** + * @author 17803 + * @date 2023-12-05 15:01 + */ + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class SealFont { + + private String text; + + private String family; + + private Integer size; + + private Boolean bold; + + private Double space; + + private Integer margin; + + public String getFamily() { + return family == null ? "仿宋体" : family; + } + + public boolean getBold() { + return bold == null ? true : bold; + } + + public SealFont append(String text) { + this.text += text; + return this; + } +} diff --git a/src/main/java/com/sztzjy/fund_investment/util/seal/SealUtil.java b/src/main/java/com/sztzjy/fund_investment/util/seal/SealUtil.java new file mode 100644 index 0000000..5daf493 --- /dev/null +++ b/src/main/java/com/sztzjy/fund_investment/util/seal/SealUtil.java @@ -0,0 +1,553 @@ +package com.sztzjy.fund_investment.util.seal; + +import lombok.Builder; +import lombok.Getter; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.font.FontRenderContext; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.io.File; + +/** + * 二,绘画出不同的电子合同章 + */ + import lombok.Builder; + import lombok.Getter; +@Builder +@Getter +public class SealUtil { + + // 起始位置 + private static final int INIT_BEGIN = 5; + + // 尺寸 + private Integer size; + + // 颜色 + private Color color; + + // 主字 + private SealFont mainFont; + + // 副字 + private SealFont viceFont; + + // 抬头 + private SealFont titleFont; + + // 中心字 + private SealFont centerFont; + + // 边线圆 + private SealCircle borderCircle; + + // 内边线圆 + private SealCircle borderInnerCircle; + + // 内线圆 + private SealCircle innerCircle; + + // 边线框 + private Integer borderSquare; + + // 加字 + private String stamp; + + /** + * 画公章 + */ + public boolean draw(String pngPath) throws Exception { + if (borderSquare != null) { + return draw2(pngPath); // 画私章 + } + + //1.画布 + BufferedImage bi = new BufferedImage(size, size, BufferedImage.TYPE_4BYTE_ABGR); + + //2.画笔 + Graphics2D g2d = bi.createGraphics(); + + //2.1抗锯齿设置 + //文本不抗锯齿,否则圆中心的文字会被拉长 + RenderingHints hints = new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); + //其他图形抗锯齿 + hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setRenderingHints(hints); + + //2.2设置背景透明度 + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 0)); + + //2.3填充矩形 + g2d.fillRect(0, 0, size, size); + + //2.4重设透明度,开始画图 + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 1)); + + //2.5设置画笔颜色 + g2d.setPaint(color == null ? Color.RED : color); + + //3.画边线圆 + if (borderCircle != null) { + drawCircle(g2d, borderCircle, INIT_BEGIN, INIT_BEGIN); + } else { + throw new Exception("BorderCircle can not null!"); + } + + int borderCircleWidth = borderCircle.getWidth(); + int borderCircleHeight = borderCircle.getHeight(); + + //4.画内边线圆 + if (borderInnerCircle != null) { + int x = INIT_BEGIN + borderCircleWidth - borderInnerCircle.getWidth(); + int y = INIT_BEGIN + borderCircleHeight - borderInnerCircle.getHeight(); + drawCircle(g2d, borderInnerCircle, x, y); + } + + //5.画内环线圆 + if (innerCircle != null) { + int x = INIT_BEGIN + borderCircleWidth - innerCircle.getWidth(); + int y = INIT_BEGIN + borderCircleHeight - innerCircle.getHeight(); + drawCircle(g2d, innerCircle, x, y); + } + + //6.画弧形主文字 + if (borderCircleHeight != borderCircleWidth) { + drawArcFont4Oval(g2d, borderCircle, mainFont, true); + } else { + drawArcFont4Circle(g2d, borderCircleHeight, mainFont, true); + } + + //7.画弧形副文字 + if (borderCircleHeight != borderCircleWidth) { + drawArcFont4Oval(g2d, borderCircle, viceFont, false); + } else { + drawArcFont4Circle(g2d, borderCircleHeight, viceFont, false); + } + + //8.画中心字 + drawFont(g2d, (borderCircleWidth + INIT_BEGIN) * 2, (borderCircleHeight + INIT_BEGIN) * 2, centerFont); + + //9.画抬头文字 + drawFont(g2d, (borderCircleWidth + INIT_BEGIN) * 2, (borderCircleHeight + INIT_BEGIN) * 2, titleFont); + + g2d.dispose(); + + return ImageIO.write(bi, "PNG", new File(pngPath)); + } + + /** + * 绘制圆弧形文字 + */ + private static void drawArcFont4Circle(Graphics2D g2d, int circleRadius, SealFont font, boolean isTop) { + if (font == null) { + return; + } + + //1.字体长度 + int textLen = font.getText().length(); + + //2.字体大小,默认根据字体长度动态设定 + int size = font.getSize() == null ? (55 - textLen * 2) : font.getSize(); + + //3.字体样式 + int style = font.getBold() ? Font.BOLD : Font.PLAIN; + + //4.构造字体 + Font f = new Font(font.getFamily(), style, size); + + FontRenderContext context = g2d.getFontRenderContext(); + Rectangle2D rectangle = f.getStringBounds(font.getText(), context); + + //5.文字之间间距,默认动态调整 + double space; + if (font.getSpace() != null) { + space = font.getSpace(); + } else { + if (textLen == 1) { + space = 0; + } else { + space = rectangle.getWidth() / (textLen - 1) * 0.9; + } + } + + //6.距离外圈距离 + int margin = font.getMargin() == null ? INIT_BEGIN : font.getMargin(); + + //7.写字 + double newRadius = circleRadius + rectangle.getY() - margin; + double radianPerInterval = 2 * Math.asin(space / (2 * newRadius)); + + double fix = 0.04; + if (isTop) { + fix = 0.18; + } + double firstAngle; + if (!isTop) { + if (textLen % 2 == 1) { + firstAngle = Math.PI + Math.PI / 2 - (textLen - 1) * radianPerInterval / 2.0 - fix; + } else { + firstAngle = Math.PI + Math.PI / 2 - ((textLen / 2.0 - 0.5) * radianPerInterval) - fix; + } + } else { + if (textLen % 2 == 1) { + firstAngle = (textLen - 1) * radianPerInterval / 2.0 + Math.PI / 2 + fix; + } else { + firstAngle = (textLen / 2.0 - 0.5) * radianPerInterval + Math.PI / 2 + fix; + } + } + + for (int i = 0; i < textLen; i++) { + double theta; + double thetaX; + double thetaY; + + if (!isTop) { + theta = firstAngle + i * radianPerInterval; + thetaX = newRadius * Math.sin(Math.PI / 2 - theta); + thetaY = newRadius * Math.cos(theta - Math.PI / 2); + } else { + theta = firstAngle - i * radianPerInterval; + thetaX = newRadius * Math.sin(Math.PI / 2 - theta); + thetaY = newRadius * Math.cos(theta - Math.PI / 2); + } + + AffineTransform transform; + if (!isTop) { + transform = AffineTransform.getRotateInstance(Math.PI + Math.PI / 2 - theta); + } else { + transform = AffineTransform.getRotateInstance(Math.PI / 2 - theta + Math.toRadians(8)); + } + Font f2 = f.deriveFont(transform); + g2d.setFont(f2); + g2d.drawString(font.getText().substring(i, i + 1), (float) (circleRadius + thetaX + INIT_BEGIN), (float) (circleRadius - thetaY + INIT_BEGIN)); + } + } + + /** + * 绘制椭圆弧形文字 + */ + private static void drawArcFont4Oval(Graphics2D g2d, SealCircle sealCircle, SealFont font, boolean isTop) { + if (font == null) { + return; + } + float radiusX = sealCircle.getWidth(); + float radiusY = sealCircle.getHeight(); + float radiusWidth = radiusX + sealCircle.getLine(); + float radiusHeight = radiusY + sealCircle.getLine(); + + //1.字体长度 + int textLen = font.getText().length(); + + //2.字体大小,默认根据字体长度动态设定 + int size = font.getSize() == null ? 25 + (10 - textLen) / 2 : font.getSize(); + + //3.字体样式 + int style = font.getBold() ? Font.BOLD : Font.PLAIN; + + //4.构造字体 + Font f = new Font(font.getFamily(), style, size); + + //5.总的角跨度 + double totalArcAng = font.getSpace() * textLen; + + //6.从边线向中心的移动因子 + float minRat = 0.90f; + + double startAngle = isTop ? -90f - totalArcAng / 2f : 90f - totalArcAng / 2f; + double step = 0.5; + int alCount = (int) Math.ceil(totalArcAng / step) + 1; + double[] angleArr = new double[alCount]; + double[] arcLenArr = new double[alCount]; + int num = 0; + double accArcLen = 0.0; + angleArr[num] = startAngle; + arcLenArr[num] = accArcLen; + num++; + double angR = startAngle * Math.PI / 180.0; + double lastX = radiusX * Math.cos(angR) + radiusWidth; + double lastY = radiusY * Math.sin(angR) + radiusHeight; + for (double i = startAngle + step; num < alCount; i += step) { + angR = i * Math.PI / 180.0; + double x = radiusX * Math.cos(angR) + radiusWidth, y = radiusY * Math.sin(angR) + radiusHeight; + accArcLen += Math.sqrt((lastX - x) * (lastX - x) + (lastY - y) * (lastY - y)); + angleArr[num] = i; + arcLenArr[num] = accArcLen; + lastX = x; + lastY = y; + num++; + } + double arcPer = accArcLen / textLen; + for (int i = 0; i < textLen; i++) { + double arcL = i * arcPer + arcPer / 2.0; + double ang = 0.0; + for (int p = 0; p < arcLenArr.length - 1; p++) { + if (arcLenArr[p] <= arcL && arcL <= arcLenArr[p + 1]) { + ang = (arcL >= ((arcLenArr[p] + arcLenArr[p + 1]) / 2.0)) ? angleArr[p + 1] : angleArr[p]; + break; + } + } + angR = (ang * Math.PI / 180f); + Float x = radiusX * (float) Math.cos(angR) + radiusWidth; + Float y = radiusY * (float) Math.sin(angR) + radiusHeight; + double qxang = Math.atan2(radiusY * Math.cos(angR), -radiusX * Math.sin(angR)); + double fxang = qxang + Math.PI / 2.0; + + int subIndex = isTop ? i : textLen - 1 - i; + String c = font.getText().substring(subIndex, subIndex + 1); + + //获取文字高宽 + FontMetrics fm = sun.font.FontDesignMetrics.getMetrics(f); + int w = fm.stringWidth(c), h = fm.getHeight(); + + if (isTop) { + x += h * minRat * (float) Math.cos(fxang); + y += h * minRat * (float) Math.sin(fxang); + x += -w / 2f * (float) Math.cos(qxang); + y += -w / 2f * (float) Math.sin(qxang); + } else { + x += (h * minRat) * (float) Math.cos(fxang); + y += (h * minRat) * (float) Math.sin(fxang); + x += w / 2f * (float) Math.cos(qxang); + y += w / 2f * (float) Math.sin(qxang); + } + + // 旋转 + AffineTransform affineTransform = new AffineTransform(); + affineTransform.scale(0.8, 1); + if (isTop) + affineTransform.rotate(Math.toRadians((fxang * 180.0 / Math.PI - 90)), 0, 0); + else + affineTransform.rotate(Math.toRadians((fxang * 180.0 / Math.PI + 180 - 90)), 0, 0); + Font f2 = f.deriveFont(affineTransform); + g2d.setFont(f2); + g2d.drawString(c, x.intValue() + INIT_BEGIN, y.intValue() + INIT_BEGIN); + } + } + + /** + * 画文字 + */ + private static void drawFont(Graphics2D g2d, int circleWidth, int circleHeight, SealFont font) { + if (font == null) { + return; + } + + //1.字体长度 + int textLen = font.getText().length(); + + //2.字体大小,默认根据字体长度动态设定 + int size = font.getSize() == null ? (55 - textLen * 2) : font.getSize(); + + //3.字体样式 + int style = font.getBold() ? Font.BOLD : Font.PLAIN; + + //4.构造字体 + Font f = new Font(font.getFamily(), style, size); + g2d.setFont(f); + + FontRenderContext context = g2d.getFontRenderContext(); + String[] fontTexts = font.getText().split("\n"); + if (fontTexts.length > 1) { + int y = 0; + for (String fontText : fontTexts) { + y += Math.abs(f.getStringBounds(fontText, context).getHeight()); + } + //5.设置上边距 + float margin = INIT_BEGIN + (float) (circleHeight / 2 - y / 2); + for (String fontText : fontTexts) { + Rectangle2D rectangle2D = f.getStringBounds(fontText, context); + g2d.drawString(fontText, (float) (circleWidth / 2 - rectangle2D.getCenterX()), margin); + margin += Math.abs(rectangle2D.getHeight()); + } + } else { + Rectangle2D rectangle2D = f.getStringBounds(font.getText(), context); + //5.设置上边距,默认在中心 + float margin = font.getMargin() == null ? + (float) (circleHeight / 2 - rectangle2D.getCenterY()) : + (float) (circleHeight / 2 - rectangle2D.getCenterY()) + (float) font.getMargin(); + g2d.drawString(font.getText(), (float) (circleWidth / 2 - rectangle2D.getCenterX()), margin); + } + } + + /** + * 画圆 + */ + private static void drawCircle(Graphics2D g2d, SealCircle circle, int x, int y) { + if (circle == null) { + return; + } + + //1.圆线条粗细默认是圆直径的1/35 + int lineSize = circle.getLine() == null ? circle.getHeight() * 2 / (35) : circle.getLine(); + + //2.画圆 + g2d.setStroke(new BasicStroke(lineSize)); + g2d.drawOval(x, y, circle.getWidth() * 2, circle.getHeight() * 2); + } + + /** + * 画私章 + */ + public boolean draw2(String pngPath) throws Exception { + if (mainFont == null || mainFont.getText().length() < 2 || mainFont.getText().length() > 4) { + throw new IllegalArgumentException("请输入2-4个字"); + } + + int fixH = 18; + int fixW = 2; + + //1.画布 + BufferedImage bi = new BufferedImage(size, size / 2, BufferedImage.TYPE_4BYTE_ABGR); + + //2.画笔 + Graphics2D g2d = bi.createGraphics(); + + //2.1设置画笔颜色 + g2d.setPaint(Color.RED); + + //2.2抗锯齿设置 + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + //3.写签名 + int marginW = fixW + borderSquare; + float marginH; + FontRenderContext context = g2d.getFontRenderContext(); + Rectangle2D rectangle; + Font f; + + if (mainFont.getText().length() == 2) { + if (stamp != null && stamp.trim().length() > 0) { + bi = drawThreeFont(bi, g2d, mainFont.append(stamp), borderSquare, size, fixH, fixW, true); + } else { + f = new Font(mainFont.getFamily(), Font.BOLD, mainFont.getSize()); + g2d.setFont(f); + rectangle = f.getStringBounds(mainFont.getText().substring(0, 1), context); + marginH = (float) (Math.abs(rectangle.getCenterY()) * 2 + marginW) + fixH - 4; + g2d.drawString(mainFont.getText().substring(0, 1), marginW, marginH); + marginW += Math.abs(rectangle.getCenterX()) * 2 + (mainFont.getSpace() == null ? INIT_BEGIN : mainFont.getSpace()); + g2d.drawString(mainFont.getText().substring(1), marginW, marginH); + + //拉伸 + BufferedImage nbi = new BufferedImage(size, size, bi.getType()); + Graphics2D ng2d = nbi.createGraphics(); + ng2d.setPaint(Color.RED); + ng2d.drawImage(bi, 0, 0, size, size, null); + + //画正方形 + ng2d.setStroke(new BasicStroke(borderSquare)); + ng2d.drawRect(0, 0, size, size); + ng2d.dispose(); + bi = nbi; + } + } else if (mainFont.getText().length() == 3) { + if (stamp != null && stamp.trim().length() > 0) { + bi = drawFourFont(bi, mainFont.append(stamp), borderSquare, size, fixH, fixW); + } else { + bi = drawThreeFont(bi, g2d, mainFont, borderSquare, size, fixH, fixW, false); + } + } else { + bi = drawFourFont(bi, mainFont, borderSquare, size, fixH, fixW); + } + + g2d.dispose(); + + return ImageIO.write(bi, "PNG", new File(pngPath)); + } + + /** + * 画三字 + */ + private static BufferedImage drawThreeFont(BufferedImage bi, Graphics2D g2d, SealFont font, int lineSize, int imageSize, int fixH, int fixW, boolean isWithYin) { + fixH -= 9; + int marginW = fixW + lineSize; + //设置字体 + Font f = new Font(font.getFamily(), Font.BOLD, font.getSize()); + g2d.setFont(f); + FontRenderContext context = g2d.getFontRenderContext(); + Rectangle2D rectangle = f.getStringBounds(font.getText().substring(0, 1), context); + float marginH = (float) (Math.abs(rectangle.getCenterY()) * 2 + marginW) + fixH; + int oldW = marginW; + + if (isWithYin) { + g2d.drawString(font.getText().substring(2, 3), marginW, marginH); + marginW += rectangle.getCenterX() * 2 + (font.getSpace() == null ? INIT_BEGIN : font.getSpace()); + } else { + marginW += rectangle.getCenterX() * 2 + (font.getSpace() == null ? INIT_BEGIN : font.getSpace()); + g2d.drawString(font.getText().substring(0, 1), marginW, marginH); + } + + //拉伸 + BufferedImage nbi = new BufferedImage(imageSize, imageSize, bi.getType()); + Graphics2D ng2d = nbi.createGraphics(); + ng2d.setPaint(Color.RED); + ng2d.drawImage(bi, 0, 0, imageSize, imageSize, null); + + //画正方形 + ng2d.setStroke(new BasicStroke(lineSize)); + ng2d.drawRect(0, 0, imageSize, imageSize); + ng2d.dispose(); + bi = nbi; + + g2d = bi.createGraphics(); + g2d.setPaint(Color.RED); + g2d.setFont(f); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + if (isWithYin) { + g2d.drawString(font.getText().substring(0, 1), marginW, marginH += fixH); + rectangle = f.getStringBounds(font.getText(), context); + marginH += Math.abs(rectangle.getHeight()); + g2d.drawString(font.getText().substring(1), marginW, marginH); + } else { + g2d.drawString(font.getText().substring(1, 2), oldW, marginH += fixH); + rectangle = f.getStringBounds(font.getText(), context); + marginH += Math.abs(rectangle.getHeight()); + g2d.drawString(font.getText().substring(2, 3), oldW, marginH); + } + return bi; + } + + /** + * 画四字 + */ + private static BufferedImage drawFourFont(BufferedImage bi, SealFont font, int lineSize, int imageSize, int fixH, int fixW) { + int marginW = fixW + lineSize; + //拉伸 + BufferedImage nbi = new BufferedImage(imageSize, imageSize, bi.getType()); + Graphics2D ng2d = nbi.createGraphics(); + ng2d.setPaint(Color.RED); + ng2d.drawImage(bi, 0, 0, imageSize, imageSize, null); + + //画正方形 + ng2d.setStroke(new BasicStroke(lineSize)); + ng2d.drawRect(0, 0, imageSize, imageSize); + ng2d.dispose(); + bi = nbi; + + Graphics2D g2d = bi.createGraphics(); + g2d.setPaint(Color.RED); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + FontRenderContext context = g2d.getFontRenderContext(); + + Font f = new Font(font.getFamily(), Font.BOLD, font.getSize()); + g2d.setFont(f); + Rectangle2D rectangle = f.getStringBounds(font.getText().substring(0, 1), context); + float marginH = (float) (Math.abs(rectangle.getCenterY()) * 2 + marginW) + fixH; + + g2d.drawString(font.getText().substring(2, 3), marginW, marginH); + int oldW = marginW; + marginW += Math.abs(rectangle.getCenterX()) * 2 + (font.getSpace() == null ? INIT_BEGIN : font.getSpace()); + + g2d.drawString(font.getText().substring(0, 1), marginW, marginH); + marginH += Math.abs(rectangle.getHeight()); + + g2d.drawString(font.getText().substring(3, 4), oldW, marginH); + + g2d.drawString(font.getText().substring(1, 2), marginW, marginH); + + return bi; + } +} diff --git a/src/test/java/com/sztzjy/fund_investment/FundInvestmentApplicationTests.java b/src/test/java/com/sztzjy/fund_investment/FundInvestmentApplicationTests.java index 3fd4532..a00e11f 100644 --- a/src/test/java/com/sztzjy/fund_investment/FundInvestmentApplicationTests.java +++ b/src/test/java/com/sztzjy/fund_investment/FundInvestmentApplicationTests.java @@ -1,8 +1,14 @@ package com.sztzjy.fund_investment; +import com.sztzjy.fund_investment.util.seal.SealCircle; +import com.sztzjy.fund_investment.util.seal.SealFont; +import com.sztzjy.fund_investment.util.seal.SealUtil; +import org.aspectj.lang.annotation.Before; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import java.io.File; + @SpringBootTest class FundInvestmentApplicationTests { @@ -10,4 +16,63 @@ class FundInvestmentApplicationTests { void contextLoads() { } + + @Test + public void OfficialSeal_1() throws Exception { + SealUtil.builder() + .size(200) + .borderCircle(SealCircle.builder().line(4).width(95).height(95).build()) + .mainFont(SealFont.builder().text("天泽股权投资基金").size(22).space(22.0).margin(4).build()) + .centerFont(SealFont.builder().text("★").size(60).build()) + .titleFont(SealFont.builder().text("电子签章").size(16).space(8.0).margin(54).build()) + .build() + .draw("D:\\home\\公章1.png"); + System.out.println("已完成"); + } + + @Test + public void OfficialSeal_2() throws Exception { + SealUtil.builder() + .size(300) + .borderCircle(SealCircle.builder().line(5).width(140).height(140).build()) + .mainFont(SealFont.builder().text("xxx科技有限公司").size(35).space(35.0).margin(10).build()) + .centerFont(SealFont.builder().text("★").size(100).build()) + .titleFont(SealFont.builder().text("电子签章").size(22).space(10.0).margin(68).build()) + .build() + .draw("D:\\home\\公章2.png"); + } + + @Test + public void OfficialSeal_3() throws Exception { + SealUtil.builder() + .size(300) + .borderCircle(SealCircle.builder().line(3).width(144).height(100).build()) + .borderInnerCircle(SealCircle.builder().line(1).width(140).height(96).build()) + .mainFont(SealFont.builder().text("xxx科技有限公司").size(25).space(12.0).margin(10).build()) + .centerFont(SealFont.builder().text("NO.5201314").size(20).build()) + .titleFont(SealFont.builder().text("电子合同专用章").size(20).space(9.0).margin(64).build()) + .build() + .draw("D:\\home\\公章3.png"); + } + + @Test + public void PrivateSeal_1() throws Exception { + SealUtil.builder() + .size(300) + .borderSquare(16) + .mainFont(SealFont.builder().text("秦始皇").size(120).build()) + .build() + .draw("D:\\home\\私章1.png"); + } + + @Test + public void PrivateSeal_2() throws Exception { + SealUtil.builder() + .size(300) + .borderSquare(16) + .mainFont(SealFont.builder().text("刘皇叔印").size(120).build()) + .build() + .draw("D:\\home\\私章2.png"); + } + }