incomeDetails;
+
+}
diff --git a/src/main/java/cn/palmte/work/model/process/package-info.java b/src/main/java/cn/palmte/work/model/process/package-info.java
new file mode 100644
index 0000000..4c4a60c
--- /dev/null
+++ b/src/main/java/cn/palmte/work/model/process/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * 流程相关的 Model
+ *
+ * @author Harry Yang
+ * @since 2.0 2022/12/14 16:32
+ */
+package cn.palmte.work.model.process;
\ No newline at end of file
diff --git a/src/main/java/cn/palmte/work/service/UploadService.java b/src/main/java/cn/palmte/work/service/UploadService.java
deleted file mode 100644
index 92888e8..0000000
--- a/src/main/java/cn/palmte/work/service/UploadService.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package cn.palmte.work.service;
-
-import cn.palmte.work.config.UploadProperties;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import sun.misc.BASE64Decoder;
-import top.jfunc.common.datetime.DatetimeUtils;
-import top.jfunc.common.utils.CommonUtil;
-import top.jfunc.common.utils.FileUtil;
-import java.io.File;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.Date;
-
-/**
- * @author xiongshiyan at 2020/10/13 , contact me with email yanshixiong@126.com or phone 15208384257
- */
-@Service
-public class UploadService {
- private static final Logger logger = LoggerFactory.getLogger(UploadService.class);
- public static final String DATA_IMAGE_PNG_BASE64 = "data:image/png;base64,";
- public static final String DATA_IMAGE_JPEG_BASE64 = "data:image/jpeg;base64,";
- public static final String DATA_IMAGE_JPG_BASE64 = "data:image/jpg;base64,";
-
- @Autowired
- private UploadProperties uploadProperties;
-
- public String upload(String base64Str){
- String suffix = "png";
- if (base64Str.contains(DATA_IMAGE_PNG_BASE64)) {
- base64Str = base64Str.replaceAll(DATA_IMAGE_PNG_BASE64, "");
- suffix = "png";
- } else if (base64Str.contains(DATA_IMAGE_JPEG_BASE64)) {
- base64Str = base64Str.replaceAll(DATA_IMAGE_JPEG_BASE64, "");
- suffix = "jpeg";
- }else if (base64Str.contains(DATA_IMAGE_JPG_BASE64)) {
- base64Str = base64Str.replaceAll(DATA_IMAGE_JPG_BASE64, "");
- suffix = "jpg";
- }
- String uploadPath = uploadProperties.getPath();
- String uploadPrefix = uploadProperties.getPrefix();
- String yyyyMMdd = DatetimeUtils.toStr(new Date(), "yyyyMMdd");
- String saveDir = uploadPath + "/" + yyyyMMdd;
- FileUtil.makeSureExistDir(saveDir);
-
- String fileName = CommonUtil.randomString(32) + "." + suffix;
- String absolutePath = saveDir + "/" + fileName;
- File file = new File(absolutePath);
-
- decodeAndWrite(base64Str , file);
-
- return uploadPrefix + "/" + yyyyMMdd + "/" + fileName;
- }
-
-
- private void decodeAndWrite(String base64Str, File destFile) {
- try {
- // Base64解码
- byte[] bytes = new BASE64Decoder().decodeBuffer(base64Str);
- for (int i = 0; i < bytes.length; ++i) {
- if (bytes[i] < 0) {
- // 调整异常数据
- bytes[i] += 256;
- }
- }
- Files.write(Paths.get(destFile.getAbsolutePath()), bytes);
- } catch (Exception e) {
- logger.error(e.getMessage(), e);
- }
- }
-}
diff --git a/src/main/java/cn/palmte/work/utils/image/VerifyImageUtil.java b/src/main/java/cn/palmte/work/utils/image/VerifyImageUtil.java
deleted file mode 100644
index 9930bfc..0000000
--- a/src/main/java/cn/palmte/work/utils/image/VerifyImageUtil.java
+++ /dev/null
@@ -1,305 +0,0 @@
-package cn.palmte.work.utils.image;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import sun.misc.BASE64Decoder;
-import top.jfunc.common.utils.ArrayUtil;
-import top.jfunc.common.utils.RandomUtil;
-
-import javax.imageio.ImageIO;
-import java.awt.*;
-import java.awt.image.BufferedImage;
-import java.io.*;
-import java.security.SecureRandom;
-import java.util.ArrayList;
-import java.util.Base64;
-import java.util.List;
-import java.util.Random;
-
-/**
- * 滑动验证码相关工具方法
- */
-public class VerifyImageUtil {
- private static final Logger logger = LoggerFactory.getLogger(VerifyImageUtil.class);
- /**
- * 模板图宽度
- */
- private static int CUT_WIDTH = 50;
- /**
- * 模板图高度
- */
- private static int CUT_HEIGHT = 50;
- /**
- * 抠图凸起圆心
- */
- private static int CIRCLE_R = 5;
- /**
- * 抠图内部矩形填充大小
- */
- private static int RECTANGLE_PADDING = 8;
- /**
- * 抠图的边框宽度
- */
- private static int SLIDER_IMG_OUT_PADDING = 1;
-
- private static int CENTER_INDICATOR = 1;
- private static int BORDER_INDICATOR = 2;
- private static int OTHER_INDICATOR = 0;
-
- /**
- * 根据传入的file生成验证码图片
- */
- public static VerifyImage getVerifyImage(File file) throws IOException {
- BufferedImage srcImage = ImageIO.read(file);
-
- return getVerifyImage(srcImage);
- }
-
- /**
- * 根据转入的流生成验证码图片
- */
- public static VerifyImage getVerifyImage(InputStream inputStream) throws IOException {
- BufferedImage srcImage = ImageIO.read(inputStream);
-
- return getVerifyImage(srcImage);
- }
-
- /**
- * 以左上角为计数起点
- */
- public static VerifyImage getVerifyImage(BufferedImage srcImage) throws IOException {
- //1/4=75处,最右减1个方块儿,-5是保守的防止超界
- int locationX = RandomUtil.randomInt(srcImage.getWidth()/4, srcImage.getWidth()-CUT_WIDTH-5);
- //距离最下方一个方块,-5是保守的防止超界
- int locationY = RandomUtil.randomInt(5, srcImage.getHeight()-CUT_HEIGHT-5);
-
- BufferedImage markImage = new BufferedImage(CUT_WIDTH,CUT_HEIGHT,BufferedImage.TYPE_4BYTE_ABGR);
- cutImgByTemplate(srcImage, markImage, getBlockData(), locationX, locationY);
- return new VerifyImage(getImageBASE64(srcImage),
- getImageBASE64(markImage),
- locationX,
- locationY,
- srcImage.getWidth(),
- srcImage.getHeight());
- }
-
-
- /**
- * 生成随机滑块形状
- *
- * 0 透明像素
- * 1 滑块像素
- * 2 阴影像素
- * @return int[][]
- */
- private static int[][] getBlockData() {
- int[][] data = new int[CUT_WIDTH][CUT_HEIGHT];
- SecureRandom random = new SecureRandom();
- //Random random = new Random();
- //(x-a)²+(y-b)²=r²
- //x中心位置左右5像素随机
- double x1 = RECTANGLE_PADDING + (CUT_WIDTH - 2 * RECTANGLE_PADDING) / 2.0/* - 5 + random.nextInt(10)*/;
- //y 矩形上边界半径-1像素移动
- double y1_top = RECTANGLE_PADDING - random.nextInt(3);
- double y1_bottom = CUT_HEIGHT - RECTANGLE_PADDING + random.nextInt(3);
- double y1 = random.nextInt(2) == 1 ? y1_top : y1_bottom;
-
-
- double x2_right = CUT_WIDTH - RECTANGLE_PADDING - CIRCLE_R + random.nextInt(2 * CIRCLE_R - 4);
- double x2_left = RECTANGLE_PADDING + CIRCLE_R - 2 - random.nextInt(2 * CIRCLE_R - 4);
- double x2 = random.nextInt(2) == 1 ? x2_right : x2_left;
- double y2 = RECTANGLE_PADDING + (CUT_HEIGHT - 2 * RECTANGLE_PADDING) / 2.0 - 4 + random.nextInt(10);
-
- double po = Math.pow(CIRCLE_R, 2);
- for (int i = 0; i < CUT_WIDTH; i++) {
- for (int j = 0; j < CUT_HEIGHT; j++) {
- //矩形区域
- boolean fill;
- if ((i >= RECTANGLE_PADDING && i < CUT_WIDTH - RECTANGLE_PADDING)
- && (j >= RECTANGLE_PADDING && j < CUT_HEIGHT - RECTANGLE_PADDING)) {
- data[i][j] = CENTER_INDICATOR;
- fill = true;
- } else {
- data[i][j] = OTHER_INDICATOR;
- fill = false;
- }
- //凸出区域
- double d3 = Math.pow(i - x1, 2) + Math.pow(j - y1, 2);
- if (d3 < po) {
- data[i][j] = CENTER_INDICATOR;
- } else {
- if (!fill) {
- data[i][j] = OTHER_INDICATOR;
- }
- }
- //凹进区域
- double d4 = Math.pow(i - x2, 2) + Math.pow(j - y2, 2);
- if (d4 < po) {
- data[i][j] = OTHER_INDICATOR;
- }
- }
- }
- //边界阴影
- for (int i = 0; i < CUT_WIDTH; i++) {
- for (int j = 0; j < CUT_HEIGHT; j++) {
- //四个正方形边角处理
- for (int k = 1; k <= SLIDER_IMG_OUT_PADDING; k++) {
- //左上、右上
- if (i >= RECTANGLE_PADDING - k && i < RECTANGLE_PADDING
- && ((j >= RECTANGLE_PADDING - k && j < RECTANGLE_PADDING)
- || (j >= CUT_HEIGHT - RECTANGLE_PADDING - k && j < CUT_HEIGHT - RECTANGLE_PADDING +1))) {
- data[i][j] = BORDER_INDICATOR;
- }
-
- //左下、右下
- if (i >= CUT_WIDTH - RECTANGLE_PADDING + k - 1 && i < CUT_WIDTH - RECTANGLE_PADDING + 1) {
- for (int n = 1; n <= SLIDER_IMG_OUT_PADDING; n++) {
- if (((j >= RECTANGLE_PADDING - n && j < RECTANGLE_PADDING)
- || (j >= CUT_HEIGHT - RECTANGLE_PADDING - n && j <= CUT_HEIGHT - RECTANGLE_PADDING ))) {
- data[i][j] = BORDER_INDICATOR;
- }
- }
- }
- }
-
- if (data[i][j] == 1 && j - SLIDER_IMG_OUT_PADDING > 0 && data[i][j - SLIDER_IMG_OUT_PADDING] == 0) {
- data[i][j - SLIDER_IMG_OUT_PADDING] = BORDER_INDICATOR;
- }
- if (data[i][j] == 1 && j + SLIDER_IMG_OUT_PADDING > 0 && j + SLIDER_IMG_OUT_PADDING < CUT_HEIGHT && data[i][j + SLIDER_IMG_OUT_PADDING] == 0) {
- data[i][j + SLIDER_IMG_OUT_PADDING] = BORDER_INDICATOR;
- }
- if (data[i][j] == 1 && i - SLIDER_IMG_OUT_PADDING > 0 && data[i - SLIDER_IMG_OUT_PADDING][j] == 0) {
- data[i - SLIDER_IMG_OUT_PADDING][j] = BORDER_INDICATOR;
- }
- if (data[i][j] == 1 && i + SLIDER_IMG_OUT_PADDING > 0 && i + SLIDER_IMG_OUT_PADDING < CUT_WIDTH && data[i + SLIDER_IMG_OUT_PADDING][j] == 0) {
- data[i + SLIDER_IMG_OUT_PADDING][j] = BORDER_INDICATOR;
- }
- }
- }
-
-
- //最外边界
- /*for (int i = 0; i < CUT_WIDTH; i++) {
- data[0][i] = data[CUT_WIDTH-1][i] = 3;
- }
- for (int i = 0; i < CUT_HEIGHT; i++) {
- data[i][0] = data[i][CUT_HEIGHT-1] = 3;
- }*/
-
- return data;
- }
-
- /**
- * 裁剪区块
- * 根据生成的滑块形状,对原图和裁剪块进行变色处理
- * @param oriImage 原图
- * @param targetImage 裁剪图
- * @param blockImage 滑块
- * @param x 裁剪点x
- * @param y 裁剪点y
- */
- private static void cutImgByTemplate(BufferedImage oriImage, BufferedImage targetImage, int[][] blockImage, int x, int y) {
-
- print(blockImage);
-
- for (int i = 0; i < CUT_WIDTH; i++) {
- for (int j = 0; j < CUT_HEIGHT; j++) {
- int xx = x + i;
- int yy = y + j;
- int rgbFlg = blockImage[i][j];
- int rgb_ori = oriImage.getRGB(xx, yy);
-
- // 原图中对应位置变色处理
- if (rgbFlg == CENTER_INDICATOR) {
- //中心
- //抠图上复制对应颜色值
- targetImage.setRGB(i,j, rgb_ori);
- //原图对应位置颜色变化[透明50%]
- oriImage.setRGB(xx, yy, rgb_ori & 0x80ffffff);
- } else if (rgbFlg == BORDER_INDICATOR) {
- //边框
- targetImage.setRGB(i, j, Color.WHITE.getRGB());
- oriImage.setRGB(xx, yy, Color.GRAY.getRGB());
- }else if(rgbFlg == OTHER_INDICATOR){
- //int alpha = 0;
- targetImage.setRGB(i, j, rgb_ori & 0x00ffffff);
- }
- }
-
- }
- }
- private static void print(int[][] blockData){
- StringBuilder builder = new StringBuilder("\r\n");
- for (int i = 0; i < blockData.length; i++) {
- for (int j = 0; j < blockData[i].length; j++) {
- builder.append(blockData[i][j]).append(" ");
- }
- builder.append("\r\n");
- }
- logger.debug(builder.toString());
- }
-
-
- /**
- * 随机获取一张图片文件
- */
- public static File getRandomFile(File dir) throws IOException {
- File[] fileList = dir.listFiles();
- if(ArrayUtil.isEmpty(fileList)){
- return null;
- }
- List fileNameList = new ArrayList<>(fileList.length);
- for (File tempFile: fileList){
- if (tempFile.isDirectory()) {
- continue;
- }
- if (tempFile.getName().endsWith(".png") || tempFile.getName().endsWith(".jpg")){
- fileNameList.add(tempFile.getAbsolutePath().trim());
- }
- }
- int randomIndex = new SecureRandom().nextInt(fileNameList.size());
- //int randomIndex = new Random().nextInt(fileNameList.size());
- return new File(fileNameList.get(randomIndex));
- }
-
- /**
- * 将IMG输出为文件
- */
- public static void writeImg(BufferedImage image, File file) throws IOException {
- ByteArrayOutputStream bao=new ByteArrayOutputStream();
- ImageIO.write(image,"png",bao);
- try (FileOutputStream out = new FileOutputStream(file)){
- out.write(bao.toByteArray());
- }
- }
-
- /**
- * 将图片转换为BASE64
- */
- public static String getImageBASE64(BufferedImage image) throws IOException {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ImageIO.write(image,"png",out);
- //转成byte数组
- byte[] bytes = out.toByteArray();
-
- return Base64.getEncoder().encodeToString(bytes);
- }
-
- /**
- * 将BASE64字符串转换为图片
- */
- public static BufferedImage base64String2Image(String base64String) throws IOException {
- BASE64Decoder decoder=new BASE64Decoder();
- byte[] bytes1 = decoder.decodeBuffer(base64String);
- ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes1);
- return ImageIO.read(byteArrayInputStream);
- }
-
- /*public static void main(String[] args) throws IOException {
- VerifyImage verifyImage = VerifyImageUtil.getVerifyImage(new File("C:\\Users\\xiongshiyan\\Desktop\\ssss.png"));
- System.out.println(verifyImage);
- VerifyImageUtil.writeImg(VerifyImageUtil.base64String2Image(verifyImage.getSrcImage()), new File("C:\\Users\\xiongshiyan\\Desktop\\src.png"));
- VerifyImageUtil.writeImg(VerifyImageUtil.base64String2Image(verifyImage.getCutImage()), new File("C:\\Users\\xiongshiyan\\Desktop\\cut.png"));
- }*/
-
-}
\ No newline at end of file
diff --git a/src/main/resources/sql/2.0.sql b/src/main/resources/sql/2.0.sql
new file mode 100644
index 0000000..f452615
--- /dev/null
+++ b/src/main/resources/sql/2.0.sql
@@ -0,0 +1,38 @@
+# 创建 流程表
+create table sale_contract_process
+(
+ id int auto_increment primary key,
+ apply_date date null,
+ apply_dept varchar(255) null,
+ apply_dept_leader_name varchar(255) null,
+ apply_person_name varchar(255) null,
+ apply_person_phone varchar(255) null,
+ client_name varchar(255) null,
+ contract_name varchar(255) null,
+ contract_no varchar(255) null,
+ payment_terms varchar(255) null,
+ project_id int null,
+ project_no varchar(255) null,
+ project_title varchar(255) null comment '标题',
+ seal_types varchar(255) null comment '印章类型',
+ `status` varchar(255) null,
+ tax_rate varchar(255) null,
+
+ current_audit varchar(255) null comment '当前审核人',
+
+ create_at datetime default CURRENT_TIMESTAMP comment '创建时间',
+ last_update_at datetime on update CURRENT_TIMESTAMP comment '最后更新时间'
+
+);
+
+
+alter table sale_contract_process
+ add current_audit varchar(255) null comment '当前审核人';
+
+alter table sale_contract_process
+ add create_at datetime default CURRENT_TIMESTAMP comment '创建时间';
+alter table sale_contract_process
+ add last_update_at datetime on update CURRENT_TIMESTAMP comment '最后更新时间';
+
+
+
diff --git a/src/main/resources/static/assets/process/业务采购流程@2x.png b/src/main/resources/static/assets/process/业务采购流程@2x.png
new file mode 100644
index 0000000..32ec71c
Binary files /dev/null and b/src/main/resources/static/assets/process/业务采购流程@2x.png differ
diff --git a/src/main/resources/static/assets/process/业务采购流程@3x.png b/src/main/resources/static/assets/process/业务采购流程@3x.png
new file mode 100644
index 0000000..a17a35b
Binary files /dev/null and b/src/main/resources/static/assets/process/业务采购流程@3x.png differ
diff --git a/src/main/resources/static/assets/process/销售合同流程@2x.png b/src/main/resources/static/assets/process/销售合同流程@2x.png
new file mode 100644
index 0000000..1899213
Binary files /dev/null and b/src/main/resources/static/assets/process/销售合同流程@2x.png differ
diff --git a/src/main/resources/static/assets/process/销售合同流程@3x.png b/src/main/resources/static/assets/process/销售合同流程@3x.png
new file mode 100644
index 0000000..2ab1d02
Binary files /dev/null and b/src/main/resources/static/assets/process/销售合同流程@3x.png differ
diff --git a/src/main/resources/templates/admin/business/process-completed.ftl b/src/main/resources/templates/admin/business/process-completed.ftl
new file mode 100644
index 0000000..f82c5c9
--- /dev/null
+++ b/src/main/resources/templates/admin/business/process-completed.ftl
@@ -0,0 +1,44 @@
+<#assign base=request.contextPath />
+<#import "../../common/defaultLayout.ftl" as defaultLayout>
+<@defaultLayout.layout>
+
+
+ {{ message }}
+
+
+
+
+
+
+@defaultLayout.layout>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/templates/admin/business/process-new.ftl b/src/main/resources/templates/admin/business/process-new.ftl
new file mode 100644
index 0000000..a2d9463
--- /dev/null
+++ b/src/main/resources/templates/admin/business/process-new.ftl
@@ -0,0 +1,855 @@
+<#assign base=request.contextPath />
+<#import "../../common/defaultLayout.ftl" as defaultLayout>
+<@defaultLayout.layout>
+<#-- -->
+
+
+
+
+
+
+
业务应用 /
+ {{subTitle}}
+
+
+
+
+
+
+
+
+
+ <#-- 业务采购流程 -->
+
+
+ <#-- 新增销售合同流程 -->
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{projectTitle}}
+ 未选择项目
+
+
+
+
+ {{processForm.applyDate}}
+ 未选择项目
+
+
+
+ {{processForm.projectType}}
+ 未选择项目
+
+
+
+ {{processForm.cooperationType}}
+ 未选择项目
+
+
+
+
+
+
+
+
+
+
+
+ {{processForm.applyPersonName}}
+ 未选择项目
+
+
+
+ {{processForm.applyDeptLeaderName}}
+ 未选择部门
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{processForm.contractAmount}}元
+ 未选择项目
+
+
+
+
+
+
+
+
+
+
+ <#--TODO 最终用户名称-->
+ {{processForm.finalUserName}}
+ 未选择项目
+
+
+
+
+
+
+ <#list sealTypes as sealType>
+ ${sealType.description}
+ #list>
+
+
+
+
+
+
+
+
+ <#list taxRate as rate>
+
+ #list>
+
+
+
+
+ {{processForm.isPrepaid}}
+ 未选择项目
+
+
+
+ {{processForm.repaidAmount}}
+ 未选择项目
+
+
+
+ {{processForm.budgetGrossMargin}}
+ 未选择项目
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 点击上传
+ 只能上传PDF、excel、word、图片、压缩包,且不超过50MB
+
+
+
+
+
+
+ 详细清单
+
+
+
+
+
+ <#--
-->
+
+ 返回上一级
+ 保存草稿
+ 提交
+
+
+
+
+
+ <#-- 销售合同清单明细 -->
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 返回上一级
+ 保存并返回上一级
+
+
+
+
+ <#-- 新增业务采购合同流程 -->
+
+
+
+ <#-- 选择 业务采购清单明细 -->
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+@defaultLayout.layout>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/templates/admin/business/process-review.ftl b/src/main/resources/templates/admin/business/process-review.ftl
new file mode 100644
index 0000000..1390c37
--- /dev/null
+++ b/src/main/resources/templates/admin/business/process-review.ftl
@@ -0,0 +1,299 @@
+<#assign base=request.contextPath />
+<#import "../../common/defaultLayout.ftl" as defaultLayout>
+<@defaultLayout.layout>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <#list processTypes as processType>
+
+ #list>
+
+
+
+
+
+
+
+
+ 查询
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 查看详情
+ 审核
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 审核通过
+ 审核不通过
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+@defaultLayout.layout>
+