From 3dfee8e360e4e791411ddff586ab1bf70fbf6620 Mon Sep 17 00:00:00 2001 From: caiyuchao Date: Mon, 16 Jun 2025 19:14:31 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=B8=8B=E8=BD=BD=E5=8F=91=E7=A5=A8pdf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/upgrade/1.0.17/upgrade.sql | 3 ++ .../org/wfc/system/api/domain/SysFile.java | 14 ++++++ .../file/controller/SysFileController.java | 1 + .../service/FastDfsSysFileServiceImpl.java | 9 ++-- .../file/service/LocalSysFileServiceImpl.java | 11 ++++- .../file/service/MinioSysFileServiceImpl.java | 11 ++--- .../wfc/user/controller/UBillController.java | 7 +++ .../main/java/org/wfc/user/domain/UBill.java | 3 ++ .../wfc/user/domain/vo/UInvoiceBillVo.java | 3 ++ .../org/wfc/user/domain/vo/UInvoiceGenVo.java | 2 + .../org/wfc/user/service/IUBillService.java | 4 ++ .../user/service/impl/UBillServiceImpl.java | 45 ++++++++++++++++--- .../user/service/impl/UOrderServiceImpl.java | 1 + .../resources/mapper/user/UBillMapper.xml | 1 + 14 files changed, 98 insertions(+), 17 deletions(-) diff --git a/sql/upgrade/1.0.17/upgrade.sql b/sql/upgrade/1.0.17/upgrade.sql index ddb0c0c..19d6e09 100644 --- a/sql/upgrade/1.0.17/upgrade.sql +++ b/sql/upgrade/1.0.17/upgrade.sql @@ -12,6 +12,9 @@ ALTER TABLE `wfc_user_db`.`u_account` ADD COLUMN `package_reminder` tinyint(4) NULL COMMENT '套餐提醒' AFTER `up_limit_enable`, ADD COLUMN `balance_reminder` tinyint(4) NULL COMMENT '余额提醒' AFTER `package_reminder` +ALTER TABLE `wfc_user_db`.`u_bill` +ADD COLUMN `invoice_file_path` varchar(500) NULL COMMENT '发票文件路径' AFTER `invoice_file` + INSERT IGNORE INTO `wfc_system_db`.`sys_job` (`job_id`, `job_name`, `job_group`, `invoke_target`, `cron_expression`, `misfire_policy`, `concurrent`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (3, 'Reminder Task', 'DEFAULT', 'reminderTask.reminderJob', '0/60 * * * * ?', '3', '1', '0', 'admin', '2025-06-16 11:26:10', '', NULL, ''); SET FOREIGN_KEY_CHECKS = 1; diff --git a/wfc-api/wfc-api-system/src/main/java/org/wfc/system/api/domain/SysFile.java b/wfc-api/wfc-api-system/src/main/java/org/wfc/system/api/domain/SysFile.java index ecea4f6..346edb9 100644 --- a/wfc-api/wfc-api-system/src/main/java/org/wfc/system/api/domain/SysFile.java +++ b/wfc-api/wfc-api-system/src/main/java/org/wfc/system/api/domain/SysFile.java @@ -25,6 +25,11 @@ public class SysFile */ private String localUrl; + /** + * 文件路径 + */ + private String filePath; + /** * 文件gateway地址 */ @@ -64,6 +69,14 @@ public class SysFile this.gatewayUrl = gatewayUrl; } + public String getFilePath() { + return filePath; + } + + public void setFilePath(String filePath) { + this.filePath = filePath; + } + @Override public String toString() { return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) @@ -71,6 +84,7 @@ public class SysFile .append("url", getUrl()) .append("localUrl", getLocalUrl()) .append("gatewayUrl", getGatewayUrl()) + .append("filePath", getFilePath()) .toString(); } } diff --git a/wfc-modules/wfc-file/src/main/java/org/wfc/file/controller/SysFileController.java b/wfc-modules/wfc-file/src/main/java/org/wfc/file/controller/SysFileController.java index 2cbc7b8..c6f35cd 100644 --- a/wfc-modules/wfc-file/src/main/java/org/wfc/file/controller/SysFileController.java +++ b/wfc-modules/wfc-file/src/main/java/org/wfc/file/controller/SysFileController.java @@ -38,6 +38,7 @@ public class SysFileController { sysFile.setName(FileUtils.getName(urlResult.getGatewayUrl())); sysFile.setUrl(urlResult.getUrl()); sysFile.setLocalUrl(urlResult.getLocalUrl()); + sysFile.setFilePath(urlResult.getFilePath()); sysFile.setGatewayUrl(urlResult.getGatewayUrl()); return R.ok(sysFile); } catch (Exception e) { diff --git a/wfc-modules/wfc-file/src/main/java/org/wfc/file/service/FastDfsSysFileServiceImpl.java b/wfc-modules/wfc-file/src/main/java/org/wfc/file/service/FastDfsSysFileServiceImpl.java index b52d33d..ce9b5ed 100644 --- a/wfc-modules/wfc-file/src/main/java/org/wfc/file/service/FastDfsSysFileServiceImpl.java +++ b/wfc-modules/wfc-file/src/main/java/org/wfc/file/service/FastDfsSysFileServiceImpl.java @@ -1,15 +1,16 @@ package org.wfc.file.service; -import java.io.InputStream; import com.alibaba.nacos.common.utils.IoUtils; +import com.github.tobato.fastdfs.domain.fdfs.StorePath; +import com.github.tobato.fastdfs.service.FastFileStorageClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; -import com.github.tobato.fastdfs.domain.fdfs.StorePath; -import com.github.tobato.fastdfs.service.FastFileStorageClient; import org.wfc.common.core.utils.file.FileTypeUtils; +import java.io.InputStream; + /** * FastDFS 文件存储 * @@ -42,6 +43,6 @@ public class FastDfsSysFileServiceImpl implements ISysFileService FileTypeUtils.getExtension(file), null); IoUtils.closeQuietly(inputStream); String fileUrl = domain + "/" + storePath.getFullPath(); - return new LocalSysFileServiceImpl.FileUploadResult(fileUrl, fileUrl, fileUrl); + return new LocalSysFileServiceImpl.FileUploadResult(fileUrl, fileUrl, fileUrl, fileUrl); } } diff --git a/wfc-modules/wfc-file/src/main/java/org/wfc/file/service/LocalSysFileServiceImpl.java b/wfc-modules/wfc-file/src/main/java/org/wfc/file/service/LocalSysFileServiceImpl.java index c31a737..636d043 100644 --- a/wfc-modules/wfc-file/src/main/java/org/wfc/file/service/LocalSysFileServiceImpl.java +++ b/wfc-modules/wfc-file/src/main/java/org/wfc/file/service/LocalSysFileServiceImpl.java @@ -50,20 +50,23 @@ public class LocalSysFileServiceImpl implements ISysFileService public FileUploadResult uploadFile(MultipartFile file) throws Exception { String name = FileUploadUtils.upload(localFilePath, file); String url = localFilePrefix + name; + String filePath = localFilePath + name; String localUrl = domain + localFilePrefix + name; String gatewayUrl = gateway + pathPrefix + localFilePrefix + name; - return new FileUploadResult(url, localUrl, gatewayUrl); + return new FileUploadResult(url, localUrl, gatewayUrl, filePath); } public static class FileUploadResult { private final String url; private final String localUrl; private final String gatewayUrl; + private final String filePath; - public FileUploadResult(String url, String localUrl, String gatewayUrl) { + public FileUploadResult(String url, String localUrl, String gatewayUrl, String filePath) { this.url = url; this.localUrl = localUrl; this.gatewayUrl = gatewayUrl; + this.filePath = filePath; } public String getUrl() { @@ -77,5 +80,9 @@ public class LocalSysFileServiceImpl implements ISysFileService public String getGatewayUrl() { return gatewayUrl; } + + public String getFilePath() { + return filePath; + } } } diff --git a/wfc-modules/wfc-file/src/main/java/org/wfc/file/service/MinioSysFileServiceImpl.java b/wfc-modules/wfc-file/src/main/java/org/wfc/file/service/MinioSysFileServiceImpl.java index 51f7581..fdd017e 100644 --- a/wfc-modules/wfc-file/src/main/java/org/wfc/file/service/MinioSysFileServiceImpl.java +++ b/wfc-modules/wfc-file/src/main/java/org/wfc/file/service/MinioSysFileServiceImpl.java @@ -1,14 +1,15 @@ package org.wfc.file.service; -import java.io.InputStream; +import com.alibaba.nacos.common.utils.IoUtils; +import io.minio.MinioClient; +import io.minio.PutObjectArgs; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; -import com.alibaba.nacos.common.utils.IoUtils; import org.wfc.file.config.MinioConfig; import org.wfc.file.utils.FileUploadUtils; -import io.minio.MinioClient; -import io.minio.PutObjectArgs; + +import java.io.InputStream; /** * Minio 文件存储 @@ -45,6 +46,6 @@ public class MinioSysFileServiceImpl implements ISysFileService client.putObject(args); IoUtils.closeQuietly(inputStream); String fileUrl = minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/" + fileName; - return new LocalSysFileServiceImpl.FileUploadResult(fileUrl, fileUrl, fileUrl); + return new LocalSysFileServiceImpl.FileUploadResult(fileUrl, fileUrl, fileUrl, fileUrl); } } diff --git a/wfc-modules/wfc-user/src/main/java/org/wfc/user/controller/UBillController.java b/wfc-modules/wfc-user/src/main/java/org/wfc/user/controller/UBillController.java index 2ad8a4f..4a2bede 100644 --- a/wfc-modules/wfc-user/src/main/java/org/wfc/user/controller/UBillController.java +++ b/wfc-modules/wfc-user/src/main/java/org/wfc/user/controller/UBillController.java @@ -2,6 +2,7 @@ package org.wfc.user.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.wfc.common.core.web.controller.BaseController; @@ -9,6 +10,7 @@ import org.wfc.common.core.web.page.TableDataInfo; import org.wfc.user.domain.vo.UInvoiceBillVo; import org.wfc.user.service.IUBillService; +import javax.servlet.http.HttpServletResponse; import java.util.List; /** @@ -33,4 +35,9 @@ public class UBillController extends BaseController { return getDataTable(list); } + @GetMapping("/download/{id}") + public void download(HttpServletResponse response, @PathVariable("id") Long id) { + uBillService.download(response, id); + } + } diff --git a/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/UBill.java b/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/UBill.java index d89e809..c906406 100644 --- a/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/UBill.java +++ b/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/UBill.java @@ -46,6 +46,9 @@ public class UBill extends BaseData { @Schema(description = "发票文件") private String invoiceFile; + @Schema(description = "发票文件路径") + private String invoiceFilePath; + @Schema(description = "发票文件") private Date invoiceTime; } diff --git a/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/vo/UInvoiceBillVo.java b/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/vo/UInvoiceBillVo.java index 74c4822..2e1a935 100644 --- a/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/vo/UInvoiceBillVo.java +++ b/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/vo/UInvoiceBillVo.java @@ -13,6 +13,9 @@ import java.util.Date; @Data public class UInvoiceBillVo { + @Schema(description = "id") + private Long id; + @Schema(description = "发票编号") private String invoiceNumber; diff --git a/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/vo/UInvoiceGenVo.java b/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/vo/UInvoiceGenVo.java index 5edda20..56a6601 100644 --- a/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/vo/UInvoiceGenVo.java +++ b/wfc-modules/wfc-user/src/main/java/org/wfc/user/domain/vo/UInvoiceGenVo.java @@ -16,5 +16,7 @@ public class UInvoiceGenVo { private String invoiceFile; + private String invoiceFilePath; + private Date invoiceTime; } diff --git a/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/IUBillService.java b/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/IUBillService.java index e64483a..dd6bf22 100644 --- a/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/IUBillService.java +++ b/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/IUBillService.java @@ -7,6 +7,7 @@ import org.wfc.user.domain.vo.UBillUserVo; import org.wfc.user.domain.vo.UInvoiceBillVo; import org.wfc.user.domain.vo.UInvoiceGenVo; +import javax.servlet.http.HttpServletResponse; import java.util.List; /** @@ -23,5 +24,8 @@ public interface IUBillService extends IService { List getInvoiceByUser(); + void download(HttpServletResponse response, Long id); + UInvoiceGenVo genInvoice(UOrder order); + } diff --git a/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UBillServiceImpl.java b/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UBillServiceImpl.java index f56bd91..f43cc50 100644 --- a/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UBillServiceImpl.java +++ b/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UBillServiceImpl.java @@ -2,6 +2,7 @@ package org.wfc.user.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; @@ -29,7 +30,6 @@ import org.wfc.common.security.utils.SecurityUtils; import org.wfc.system.api.RemoteFileService; import org.wfc.system.api.domain.SysFile; import org.wfc.user.api.domain.UUser; -import org.wfc.user.domain.InvoiceBean; import org.wfc.user.domain.UBill; import org.wfc.user.domain.UOrder; import org.wfc.user.domain.UPackage; @@ -37,6 +37,7 @@ import org.wfc.user.domain.URateLimit; import org.wfc.user.domain.bo.UInvoiceTemplateBo; import org.wfc.user.domain.constant.OrderTypeEnum; import org.wfc.user.domain.constant.PeriodTypeEnum; +import org.wfc.user.domain.properties.InvoiceProperties; import org.wfc.user.domain.vo.UBillUserVo; import org.wfc.user.domain.vo.UInvoiceBillVo; import org.wfc.user.domain.vo.UInvoiceGenVo; @@ -47,9 +48,11 @@ import org.wfc.user.mapper.UUserMapper; import org.wfc.user.service.IUBillService; import org.wfc.user.util.TrafficConverter; +import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.math.RoundingMode; +import java.net.URLEncoder; import java.nio.file.Files; import java.nio.file.Paths; import java.text.SimpleDateFormat; @@ -77,7 +80,7 @@ public class UBillServiceImpl extends ServiceImpl implements private final RedisService redisService; private final UPackageMapper packageMapper; private final URateLimitMapper urateLimitMapper; - private final InvoiceBean invoiceBean; + private final InvoiceProperties invoiceProperties; public static final String PRE_INVOICE_NUMBER = "INV-"; public static final String LAST_INVOICE_NUMBER = "-001"; @@ -97,9 +100,37 @@ public class UBillServiceImpl extends ServiceImpl implements return this.baseMapper.getInvoiceByUser(loginUser.getUserid()); } + @Override + public void download(HttpServletResponse response, Long id) { + UBill bill = this.getById(id); + String invoiceFilePath = bill.getInvoiceFilePath(); + if (StrUtil.isNotBlank(invoiceFilePath)) { + byte[] content = FileUtil.readBytes(invoiceFilePath); + if (content == null) { + throw new ServiceException("file does not exist"); + } + try { + writeAttachment(response, bill.getInvoiceNumber() + ".pdf", content); + } catch (IOException e) { + throw new ServiceException("download file error"); + } + } + + } + + public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException { + // 设置 header 和 contentType + response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8")); + response.setContentType("application/octet-stream; charset=UTF-8"); + response.setHeader("Content-Length", String.valueOf(content.length - 1)); + response.setHeader("Content-Range", String.valueOf(content.length - 1)); + response.setHeader("Accept-Ranges", "bytes"); + // 输出附件 + IoUtil.write(response.getOutputStream(), false, content); + } + @Override public UInvoiceGenVo genInvoice(UOrder order) { - String url; UInvoiceTemplateBo invoiceBo = new UInvoiceTemplateBo(); if (order.getUserId() != null) { UUser user = userMapper.selectUserById(order.getUserId()); @@ -175,7 +206,7 @@ public class UBillServiceImpl extends ServiceImpl implements Context context = getContext(invoiceBo); String htmlStr = templateEngine.process("invoice", context); - String prefix = invoiceBean.getPath(); + String prefix = invoiceProperties.getPath(); String basePath = prefix + File.separator + "invoice"; String fontsDir = prefix + File.separator + "fonts"; FileUtil.mkdir(basePath); @@ -195,13 +226,14 @@ public class UBillServiceImpl extends ServiceImpl implements MultipartFile multipartFile = MultipartFileUtil.getMultipartFile(uploadFile); R fileResult = remoteFileService.upload(multipartFile); - url = fileResult.getData().getUrl(); + String url = fileResult.getData().getUrl(); + String filePath = fileResult.getData().getFilePath(); log.info("invoice file addr: {}", url); try { String subject = mailProperties.getSubject(); if (StrUtil.isBlank(subject)) { - subject = "Your WANFI Verification Code"; + subject = "Your WANFI Billing"; } MailUtils.sendHtml(invoiceBo.getEmail(), subject, htmlStr, uploadFile); @@ -213,6 +245,7 @@ public class UBillServiceImpl extends ServiceImpl implements invoiceGenVo.setInvoiceNumber(invoiceBo.getInvoiceNumber()); invoiceGenVo.setInvoiceFile(url); invoiceGenVo.setInvoiceTime(date); + invoiceGenVo.setInvoiceFilePath(filePath); return invoiceGenVo; } diff --git a/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UOrderServiceImpl.java b/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UOrderServiceImpl.java index a7a9cf0..ae23639 100644 --- a/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UOrderServiceImpl.java +++ b/wfc-modules/wfc-user/src/main/java/org/wfc/user/service/impl/UOrderServiceImpl.java @@ -109,6 +109,7 @@ public class UOrderServiceImpl extends ServiceImpl impleme // 设置发票编号和发票文件地址 bill.setInvoiceNumber(invoiceGenVo.getInvoiceNumber()); bill.setInvoiceFile(invoiceGenVo.getInvoiceFile()); + bill.setInvoiceFilePath(invoiceGenVo.getInvoiceFilePath()); bill.setInvoiceTime(invoiceGenVo.getInvoiceTime()); } diff --git a/wfc-modules/wfc-user/src/main/resources/mapper/user/UBillMapper.xml b/wfc-modules/wfc-user/src/main/resources/mapper/user/UBillMapper.xml index 30d5249..b7c512b 100644 --- a/wfc-modules/wfc-user/src/main/resources/mapper/user/UBillMapper.xml +++ b/wfc-modules/wfc-user/src/main/resources/mapper/user/UBillMapper.xml @@ -21,6 +21,7 @@