待我审核

master
Harry Yang 2022-12-21 10:41:57 +08:00
parent dc0159b4eb
commit 01c2d0488d
6 changed files with 112 additions and 398 deletions

View File

@ -51,7 +51,6 @@ import cn.palmte.work.utils.InterfaceUtil;
import lombok.Builder;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
/**
* @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
@ -189,7 +188,6 @@ public class ProcessController {
@PostMapping
@Transactional
public void post(@RequestBody @Valid SaleContractProcessForm form) {
System.out.println(form);
SaleContractProcess entity = new SaleContractProcess();
BeanUtils.copyProperties(form, entity, "sealTypes", "applyDate", "applyDept");
entity.setApplyDate(LocalDate.parse(form.getApplyDate(), formatter));
@ -223,7 +221,7 @@ public class ProcessController {
private String projectNo;
private String projectTitle;
private String projectCreator;
private String applyPersonName;
private ProcessType processType;
private ProcessStatus processStatus;

View File

@ -3,6 +3,7 @@ package cn.palmte.work.model.process;
import org.hibernate.annotations.GenericGenerator;
import java.time.LocalDate;
import java.time.LocalDateTime;
import javax.persistence.Convert;
import javax.persistence.Entity;
@ -77,6 +78,14 @@ public class SaleContractProcess {
@Enumerated(EnumType.STRING)
private ProcessStatus status;
// 当前审核人
private String currentAudit;
// 最后更新时间
private LocalDateTime lastUpdateAt;
private LocalDateTime createAt;
// 项目类型
// @Enumerated(EnumType.STRING)
// private ProjectType projectType;

View File

@ -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);
}
}
}

View File

@ -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());
}
/**
*
* <p>
* 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<String> 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"));
}*/
}

View File

@ -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 '最后更新时间';

View File

@ -31,6 +31,10 @@
.el-table__empty-block {
height: 60px !important;
}
.el-radio-button__inner, .el-radio-group {
line-height: unset;
}
</style>
<div class="admin-content" id="app">
@ -42,7 +46,7 @@
<div class="am-g">
<div class="am-u-sm-12 am-u-md-12">
<el-form :inline="true" ref="queryForm" :model="queryForm" label-position="right" label-width="100px">
<el-form :inline="true" ref="queryForm" :model="queryForm" label-position="right">
<div>
<el-form-item label="标题">
@ -64,8 +68,8 @@
</el-select>
</el-form-item>
<el-form-item label="项目创建者">
<el-input placeholder="请输入项目创建者" v-model="queryForm.projectCreator"></el-input>
<el-form-item label="申请人">
<el-input placeholder="请输入申请人" v-model="queryForm.applyPersonName"></el-input>
</el-form-item>
<el-button type="primary" @click="queryTable">查询</el-button>
@ -75,13 +79,13 @@
<el-table border :data="tableData">
<el-table-column type="index" :index="1" label="序号" fixed></el-table-column>
<el-table-column prop="name" label="项目编号" fixed></el-table-column>
<el-table-column prop="type" label="标题"></el-table-column>
<el-table-column prop="spec" label="流程类型"></el-table-column>
<el-table-column prop="param" label="项目创建者"></el-table-column>
<el-table-column prop="unit" label="审核状态"></el-table-column>
<el-table-column prop="projectNo" label="项目编号" fixed></el-table-column>
<el-table-column prop="projectTitle" label="标题"></el-table-column>
<el-table-column prop="processType" label="流程类型"></el-table-column>
<el-table-column prop="applyPersonName" label="申请人"></el-table-column>
<el-table-column prop="status" label="审核状态"></el-table-column>
<el-table-column prop="amount" label="当前审核人"></el-table-column>
<el-table-column prop="price" label="最后更新时间"></el-table-column>
<el-table-column prop="lastUpdateAt" label="最后更新时间"></el-table-column>
<el-table-column label="操作" fixed="right" width="180">
<template slot-scope="scope">
@ -92,21 +96,24 @@
</el-table>
<el-pagination style="text-align: center; margin-top: 10px"
@current-change="handlePageChange"
layout="prev, pager, next" :total="50"></el-pagination>
</div>
</div>
<el-dialog title="审核" :visible.sync="auditFormVisible">
<el-form :model="auditForm">
<el-form ref="auditForm" :model="auditForm" label-width="80px">
<el-form-item label="审核">
<el-form-item prop="processStatus" label="审核" :rules="[{ required: true, message: '还没审核'}]">
<el-radio-group v-model="auditForm.processStatus">
<el-radio label="audit_passed">审核通过</el-radio>
<el-radio label="audit_not_passed">审核不通过</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="审核意见">
<el-input type="textarea" :autosize="{ minRows: 3, maxRows: 10}"
<el-form-item prop="auditOpinion" label="审核意见" :rules="[{ required: true, message: '审核意见不能为空'}]">
<el-input type="textarea" :autosize="{ minRows: 4, maxRows: 10}"
maxlength="5000" v-model="auditForm.auditOpinion"
placeholder="请输入审核意见" cols="90"></el-input>
</el-form-item>
@ -166,14 +173,48 @@
},
auditProcess(row, scope) {
console.log(row)
console.log(scope)
this.auditForm = {
processId: row.id,
processStatus: null
}
this.auditFormVisible = true
},
submitAudit() {
this.$refs["auditForm"].validate((valid) => {
if (valid) {
const loading = this.$loading({
lock: true,
text: '正在审核',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
const form = this.auditForm
fetch("${base}/process/audit", {
method: 'POST', // or 'PUT'
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(form),
}).then(response => {
this.$message({
showClose: true,
message: '审核成功',
type: 'success'
})
// 关闭对话框
this.auditFormVisible = false
}).catch(err => {
this.$message.error("审核失败");
}).finally(() => loading.close())
}
else {
return false;
}
})
console.log("submitAudit");
},
render(obj) {
@ -200,6 +241,12 @@
})
},
handlePageChange(val) {
console.log(`当前页:` + val)
},
}
new Vue({