新增pi推送邮件

This commit is contained in:
Mrking 2024-09-12 12:54:01 +08:00
parent 79e94961ae
commit 11c705f7e6
14 changed files with 223 additions and 49 deletions

View File

@ -124,7 +124,6 @@
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId> <!-- 文件客户端:文件类型的识别 -->
</dependency>
</dependencies>
</project>

View File

@ -64,6 +64,32 @@
<version>2.1.0-jdk8-snapshot</version>
<scope>compile</scope>
</dependency>
<!-- Thymeleaf 模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- HTML转PDF工具flying-saucer -->
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-core</artifactId>
<version>9.1.20</version>
</dependency>
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf-openpdf</artifactId>
<version>9.1.20</version>
</dependency>
<dependency>
<groupId>com.github.librepdf</groupId>
<artifactId>openpdf</artifactId>
<version>1.3.14</version>
</dependency>
</dependencies>
</project>

View File

@ -51,7 +51,11 @@ public class AppBrandController {
public CommonResult<List<BrandSimpleRespVO>> getSimpleBrandList() {
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
CustomerDO customer = customerService.getCustomerByUserId(loginUser.getId());
List<CustomerBrandDO> listByCustomerId = customerBrandService.getListByCustomerId(customer.getId());
Long customerId = null;
if(customer!=null){
customerId = customer.getId();
}
List<CustomerBrandDO> listByCustomerId = customerBrandService.getListByCustomerId(customerId);
List<Long> brandIds = new ArrayList<>();
for (CustomerBrandDO customerBrandDO : listByCustomerId) {
brandIds.add(customerBrandDO.getBrandId());

View File

@ -1,5 +1,7 @@
package cn.hangtag.module.oms.controller.admin.saleorder;
import com.alibaba.fastjson.JSONObject;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
@ -8,6 +10,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.annotation.security.PermitAll;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
@ -129,9 +132,30 @@ public class SaleOrderController {
return success(true);
}
@PostMapping("/rejectOrder")
@Operation(summary = "驳回")
@PreAuthorize("@ss.hasPermission('oms:sale-order:rejectOrder')")
public CommonResult<Boolean> rejectOrder(@RequestBody JSONObject jobs) {
Long[] ids = jobs.getObject("ids", new Long[0].getClass());
String reason = jobs.getString("reason");
return success(true);
}
/**
* 生成PDF文档并下载
* @param response HTTP响应
* @throws Exception 异常
*/
@GetMapping("/download")
@PermitAll
public void downloadPdf(HttpServletResponse response) throws Exception {
String htmlContent = saleOrderService.generateHtmlContent();
saleOrderService.generatePdf(response, htmlContent);
}
}

View File

@ -70,4 +70,8 @@ public class SaleOrderRespVO {
@ExcelProperty("备注")
private String remarks;
@Schema(description = "驳回原因")
@ExcelProperty("驳回原因")
private String rejectReason;
}

View File

@ -62,6 +62,9 @@ public class SaleOrderSaveReqVO {
@Schema(description = "订单状态", example = "2")
private Integer orderStatus;
@Schema(description = "驳回原因", example = "2")
private String rejectReason;
@Schema(description = "销售订单明细列表")
private List<SaleOrderEntryDO> saleOrderEntrys;

View File

@ -102,5 +102,9 @@ public class SaleOrderDO extends BaseDO {
* 发票备注
*/
private String invoiceRemarks;
/**
* 驳回原因
*/
private String rejectReason;
}

View File

@ -1,6 +1,8 @@
package cn.hangtag.module.oms.service.saleorder;
import java.io.IOException;
import java.util.*;
import javax.servlet.http.HttpServletResponse;
import javax.validation.*;
import cn.hangtag.module.oms.controller.admin.saleorder.vo.*;
import cn.hangtag.module.oms.dal.dataobject.saleorder.SaleOrderDO;
@ -68,4 +70,8 @@ public interface SaleOrderService {
void updateSaleOrderBillStatus(List<Long> ids, String status);
void generateProduceOrder(List<Long> ids);
String generateHtmlContent();
void generatePdf(HttpServletResponse response, String htmlContent) throws IOException;
}

View File

@ -12,12 +12,24 @@ import cn.hangtag.module.oms.dal.mysql.saleorderentry.SaleOrderEntryMapper;
import cn.hangtag.module.oms.enums.saleorder.SaleOrderStatusEnum;
import cn.hangtag.module.oms.service.produceorder.ProduceOrderService;
import com.google.common.collect.Maps;
import com.lowagie.text.pdf.PdfWriter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
@ -42,6 +54,9 @@ public class SaleOrderServiceImpl implements SaleOrderService {
@Resource
private ProduceOrderService produceOrderService;
@Resource
private TemplateEngine templateEngine;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createSaleOrder(SaleOrderSaveReqVO createReqVO) {
@ -148,6 +163,52 @@ public class SaleOrderServiceImpl implements SaleOrderService {
}
}
@Override
public String generateHtmlContent() {
Context context = new Context();
context.setVariable("title", "PDF文档标题");
context.setVariable("content",
"这是PDF文档的主要内容"
+ "“成功并不是终点,失败也不是终结,最重要的是继续前行的勇气。在人生的旅途中,我们会遇到许多挑战与挫折,这些都是成长的必经之路。每一次跌倒都是一次学习的机会,每一次失败都为成功铺设了基础。只要我们保持信念,不断努力,最终会到达梦想的彼岸。无论前方的路有多么坎坷,只要心怀希望,我们就有无限的可能性去改变自己的命运,实现心中的理想。”"
+ "由Thymeleaf模板引擎渲染。"
+ "");
return templateEngine.process("pdf_template", context);
}
@Override
public void generatePdf(HttpServletResponse response, String htmlContent) throws IOException {
// 设置响应类型
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename=generated.pdf");
// 创建ITextRenderer实例
ITextRenderer renderer = new ITextRenderer();
// 设置字体路径使用 classpath 加载字体
ITextFontResolver fontResolver = renderer.getFontResolver();
ClassPathResource fontResource = new ClassPathResource("fonts/SimSun.ttf");
System.out.println(fontResource.getFile().getAbsolutePath());
fontResolver.addFont(fontResource.getFile().getAbsolutePath(), "Identity-H", true);
renderer.setDocumentFromString(htmlContent);
renderer.layout();
// 输出PDF到响应输出流
try (OutputStream outputStream = response.getOutputStream()) {
renderer.createPDF(outputStream);
outputStream.flush();
}
/*
// 设置输出PDF文件
OutputStream os = new FileOutputStream("index.pdf");
renderer.createPDF(os);
os.flush();
os.close();*/
}
private void createSaleOrderEntryList(Long parentId, List<SaleOrderEntryDO> list) {
list.forEach(o -> o.setParentId(parentId));

View File

@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title th:text="${title}">PDF文档</title>
<style>
/* 内联CSS样式确保在PDF中正确渲染 */
body {
font-family: "SimSun", serif;
padding: 20px;
line-height: 1.6;
}
h1 {
text-align: center;
margin-bottom: 40px;
color: #333;
}
p {
font-size: 16px;
color: #555;
}
.footer {
text-align: center;
margin-top: 50px;
font-size: 12px;
color: #999;
}
</style>
</head>
<body>
<h1 th:text="${title}">PDF文档标题</h1>
<p th:text="${content}">这是PDF文档的内容部分由Thymeleaf模板引擎渲染。</p>
<div class="footer">
<p>© 2024 路条编程 - 保留所有权利。</p>
</div>
</body>
</html>

View File

@ -40,6 +40,12 @@ spring:
redis:
time-to-live: 1h # 设置过期时间为 1 小时
thymeleaf:
prefix: classpath:/templates/
suffix: .html
mode: HTML
encoding: UTF-8
cache: false
--- #################### 接口文档配置 ####################
springdoc:

View File

@ -81,4 +81,14 @@ export const SaleOrderApi = {
})
},
// 驳回
rejectOrder: async (data) => {
return await request.post({
url: `/oms/sale-order/rejectOrder`,
data: data
})
},
}

View File

@ -118,6 +118,7 @@
<el-button
type="danger"
plain
@click="handleReject(selectionList.map((item) => item.id))"
:disabled="selectionList.length === 0"
>驳回
</el-button>
@ -267,17 +268,17 @@
<!-- 驳回对话框 -->
<!-- <el-dialog :title="rejectTitle" :visible.sync="rejectOpen" width="650px" append-to-body>-->
<!-- <el-form ref="form" :model="rejectform" label-width="80px" >-->
<!-- <el-form-item label="驳回原因" prop="rejectReason" >-->
<!-- <el-input v-model="" placeholder="请输入单价" />-->
<!-- </el-form-item>-->
<!-- </el-form>-->
<!-- <div slot="footer" class="dialog-footer">-->
<!-- <el-button type="primary" @click="submitRejectForm"> </el-button>-->
<!-- <el-button @click="rejectCancel"> </el-button>-->
<!-- </div>-->
<!-- </el-dialog>-->
<el-dialog :title="rejectTitle" v-model="rejectOpen" width="750px" style="height: 210px;" append-to-body>
<el-form ref="form" :model="rejectform" label-width="80px" >
<el-form-item label="驳回原因" prop="rejectReason" >
<el-input v-model="rejectform.rejectReason" :rows="3" type="textarea" placeholder="请输入驳回原因" />
</el-form-item>
</el-form>
<div class="dialog-footer" style="text-align: right">
<el-button type="primary" @click="submitRejectForm"> </el-button>
<el-button @click="rejectCancel"> </el-button>
</div>
</el-dialog>
</template>
@ -340,7 +341,7 @@ const exportLoading = ref(false) // 导出的加载中
//
const rejectOpen = ref(false)
const rejectTitle = ref()
const rejectTitle = ref('')
const rejectform = reactive({
rejectReason: undefined
})
@ -400,29 +401,12 @@ const handleDelete = async (id: number) => {
}
/** 反审核按钮操作 */
// const handleReject = async () => {
// try {
// let ids = selectionList;
// /*let auditStatuss = row.ifaudit || this.auditStatuss;
//
// for(var vals of auditStatuss) {
// if(vals!='0'){
// message.error("");
// return;
// }
// }*/
//
// rejectform = {
// rejectReason: null
// };
// this.resetForm("rejectform");
// this.rejectTitle = "?";
// this.rejectOpen = true;
// } catch {}
// },
/** 驳回操作 */
const handleReject = async (ids: number[]) => {
rejectform.rejectReason = undefined
rejectTitle.value = "是否驳回选中的数据项?"
rejectOpen.value = true
}
/** 审批/反审批操作 */
const handleUpdateBillStatus = async (ids: number[], operateKey: string) => {
try {
@ -494,23 +478,29 @@ const handleSelectionChange = (rows: SaleOrderVO[]) => {
/** 驳回提交按钮 */
/*const submitRejectForm = async () => {
const ids = selectionList.map((item) => item.id
if (this.rejectform.rejectReason != null) {
const submitRejectForm = async () => {
const ids = selectionList.value.map((item) => item.id)
if(rejectform.rejectReason != null) {
const data = {
ids:ids,
reason:this.rejectform.rejectReason
reason:rejectform.rejectReason
}
/!* notAuditOrder(data).then(response => {
this.msgSuccess("驳回成功");
this.rejectOpen = false;
this.getList();
});*!/
await SaleOrderApi.rejectOrder(data).then(response => {
message.success("驳回成功");
rejectOpen.value = false;
getList();
});
}else {
message.error(`选择驳回原因!`)
message.error(`填写驳回原因!`)
}
}*/
}
/** 驳回取消按钮 */
const rejectCancel = () =>{
rejectOpen.value = false
rejectform.rejectReason = undefined
}
/** 激活时 */