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");
+ }
+
}