diff --git a/sql/sqlserver/20231010JS.sql b/sql/20231010JS.sql similarity index 100% rename from sql/sqlserver/20231010JS.sql rename to sql/20231010JS.sql diff --git a/sql/20231012JS.sql b/sql/20231012JS.sql new file mode 100644 index 00000000..349eafdd --- /dev/null +++ b/sql/20231012JS.sql @@ -0,0 +1 @@ +ALTER TABLE archives_bank_slip ADD COLUMN `file_url` varchar(64) NULL DEFAULT NULL COMMENT '回单文件地址'; diff --git a/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/controller/admin/bankslip/BankSlipController.java b/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/controller/admin/bankslip/BankSlipController.java index 37472a5d..4ff28904 100644 --- a/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/controller/admin/bankslip/BankSlipController.java +++ b/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/controller/admin/bankslip/BankSlipController.java @@ -1,13 +1,17 @@ package cn.iocoder.yudao.module.accounting.controller.admin.bankslip; +import cn.hutool.core.io.IoUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.accounting.controller.admin.bankslip.vo.*; import cn.iocoder.yudao.module.accounting.convert.bankslip.BankSlipConvert; +import cn.iocoder.yudao.module.accounting.dal.dataobject.bankslip.BankReceiptDO; import cn.iocoder.yudao.module.accounting.dal.dataobject.bankslip.BankSlipDO; import cn.iocoder.yudao.module.accounting.service.bankslip.BankSlipService; +import cn.iocoder.yudao.module.bs.utils.BaiduOcrHandler; +import cn.iocoder.yudao.module.infra.service.file.FileService; import cn.iocoder.yudao.module.setting.service.passwords.PasswordsService; import com.alibaba.fastjson.JSONObject; import io.swagger.v3.oas.annotations.Operation; @@ -16,13 +20,18 @@ import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; +import java.io.DataInput; import java.io.IOException; import java.util.Collection; import java.util.List; +import java.util.UUID; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; @@ -38,6 +47,7 @@ public class BankSlipController { @Resource private PasswordsService passwordsService; + @PostMapping("/create") @Operation(summary = "创建银行回单") @PreAuthorize("@ss.hasPermission('archives:bank-slip:create')") @@ -118,4 +128,33 @@ public class BankSlipController { ExcelUtils.write(response, "银行回单.xls", "数据", BankSlipExcelVO.class, datas); } + @PostMapping("/bankReceipt/identify") + @Operation(summary = "银行回单识别") + public CommonResult identify(@RequestParam("multipartFile") MultipartFile multipartFile) throws Exception { + String imageBase64Param = BaiduOcrHandler.getImageBase64Param(multipartFile); + String originalFileName = multipartFile.getOriginalFilename(); + int lastDotIndex = originalFileName.lastIndexOf("."); + String fileExtension = originalFileName.substring(lastDotIndex + 1); + JSONObject ocrResult = null; + //发票识别 + switch (fileExtension.toLowerCase()) { + case "pdf": + ocrResult = BaiduOcrHandler.bankReceiptOcr(imageBase64Param, "pdf"); + break; + default: + // 获取文件大小(以字节为单位) + long fileSize = multipartFile.getSize(); + // 判断文件大小是否小于6MB + if (fileSize < 6 * 1024 * 1024) { + ocrResult = BaiduOcrHandler.bankReceiptOcr(imageBase64Param, "image"); + } else { + return error("图像数据,base64编码后进行urlencode,要求base64编码和urlencode后大小不超过6M,最短边至少15px,最长边最大4096px,支持jpg/jpeg/png/bmp格式"); + } + + } + System.out.println(ocrResult); + BankReceiptDO wordsResult = JSONObject.toJavaObject( ocrResult, BankReceiptDO.class); + BankSlipDO bankSlipDO = bankSlipService.jsonToJavaObject(wordsResult, multipartFile); + return success(bankSlipDO); + } } diff --git a/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/controller/admin/bankslip/vo/BankSlipBaseVO.java b/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/controller/admin/bankslip/vo/BankSlipBaseVO.java index 4f5da050..d33394c4 100644 --- a/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/controller/admin/bankslip/vo/BankSlipBaseVO.java +++ b/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/controller/admin/bankslip/vo/BankSlipBaseVO.java @@ -145,5 +145,8 @@ public class BankSlipBaseVO { @Schema(description = "认款状态 0:未认款 1 已认款") private String subscriptionStatus; + @Schema(description = "银行回单地址") + private String fileUrl; + } diff --git a/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/dal/dataobject/bankslip/BankReceiptDO.java b/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/dal/dataobject/bankslip/BankReceiptDO.java new file mode 100644 index 00000000..80b828f0 --- /dev/null +++ b/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/dal/dataobject/bankslip/BankReceiptDO.java @@ -0,0 +1,109 @@ +package cn.iocoder.yudao.module.accounting.dal.dataobject.bankslip; + +import com.alibaba.fastjson.JSONObject; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.*; + +import java.io.DataInput; +import java.util.List; + +/** + * 银行回单实体 + */ +@Data +@ToString(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public class BankReceiptDO { + + private String log_id; + private int words_result_num; + private WordsResult words_result; + + @Data + @ToString(callSuper = true) + @NoArgsConstructor + @AllArgsConstructor + public static class WordsResult { + private List 付款人账号; + private List 交易日期; + private List 用途; + private List 流水号; + private List 摘要; + private List 付款人户名; + private List 小写金额; + private List 收款人账号; + private List 大写金额; + private List 收款人户名; + private List 标题; + private List 回单编号; + private List 付款人开户银行; + private List 收款人开户银行; + + // 添加相应的 getter 和 setter 方法 + } + +// @Data +// @ToString(callSuper = true) +// @NoArgsConstructor +// @AllArgsConstructor +// public static class WordsResultData { +// private PaymentAccount 付款人账号; +// private TransactionDate 交易日期; +// private String 用途; +// private String 流水号; +// private String 摘要; +// private String 付款人户名; +// private LowerAmount 小写金额; +// private PaymentAccount 收款人账号; +// private UpperAmount 大写金额; +// private String 收款人户名; +// private String 标题; +// private String 回单编号; +// private String 付款人开户银行; +// private String 收款人开户银行; +// +// // 添加相应的 getter 和 setter 方法 +// } + + + + @Data + @ToString(callSuper = true) + @NoArgsConstructor + @AllArgsConstructor + public static class PaymentAccount { + private String word; + + // 添加相应的 getter 和 setter 方法 + } + @Data + @ToString(callSuper = true) + @NoArgsConstructor + @AllArgsConstructor + + public static class TransactionDate { + private String word; + + // 添加相应的 getter 和 setter 方法 + } + + @Data + @ToString(callSuper = true) + @NoArgsConstructor + @AllArgsConstructor + public static class LowerAmount { + private String word; + + // 添加相应的 getter 和 setter 方法 + } + @Data + @ToString(callSuper = true) + @NoArgsConstructor + @AllArgsConstructor + public static class UpperAmount { + private String word; + } + + +} \ No newline at end of file diff --git a/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/dal/dataobject/bankslip/BankSlipDO.java b/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/dal/dataobject/bankslip/BankSlipDO.java index 460518a7..3581d43d 100644 --- a/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/dal/dataobject/bankslip/BankSlipDO.java +++ b/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/dal/dataobject/bankslip/BankSlipDO.java @@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; +import lombok.experimental.Accessors; import java.time.LocalDateTime; @@ -16,6 +17,7 @@ import java.time.LocalDateTime; @TableName("archives_bank_slip") @KeySequence("archives_bank_slip_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data +@Accessors(chain = true) @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) @Builder @@ -204,5 +206,10 @@ public class BankSlipDO extends BaseDO { */ private String subscriptionStatus; + /** + * 回单文件地址 + */ + private String fileUrl; + } diff --git a/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/service/bankslip/BankSlipService.java b/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/service/bankslip/BankSlipService.java index b0356834..eaf5e00d 100644 --- a/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/service/bankslip/BankSlipService.java +++ b/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/service/bankslip/BankSlipService.java @@ -5,9 +5,13 @@ import cn.iocoder.yudao.module.accounting.controller.admin.bankslip.vo.BankSlipC import cn.iocoder.yudao.module.accounting.controller.admin.bankslip.vo.BankSlipExportReqVO; import cn.iocoder.yudao.module.accounting.controller.admin.bankslip.vo.BankSlipPageReqVO; import cn.iocoder.yudao.module.accounting.controller.admin.bankslip.vo.BankSlipUpdateReqVO; +import cn.iocoder.yudao.module.accounting.dal.dataobject.bankslip.BankReceiptDO; import cn.iocoder.yudao.module.accounting.dal.dataobject.bankslip.BankSlipDO; +import com.alibaba.fastjson.JSONObject; +import org.springframework.web.multipart.MultipartFile; import javax.validation.Valid; +import java.io.IOException; import java.util.Collection; import java.util.List; @@ -79,4 +83,10 @@ public interface BankSlipService { * @return 发票分页 */ PageResult getBankSlipPageTime(Long months); + + /** + * 银行回单json转银行回单对象 + * @param ocrResult + */ + BankSlipDO jsonToJavaObject(BankReceiptDO ocrResult, MultipartFile multipartFile) throws IOException; } diff --git a/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/service/bankslip/BankSlipServiceImpl.java b/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/service/bankslip/BankSlipServiceImpl.java index f6eeb33c..2647e809 100644 --- a/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/service/bankslip/BankSlipServiceImpl.java +++ b/yudao-module-accounting/yudao-module-accounting-biz/src/main/java/cn/iocoder/yudao/module/accounting/service/bankslip/BankSlipServiceImpl.java @@ -1,18 +1,29 @@ package cn.iocoder.yudao.module.accounting.service.bankslip; +import cn.hutool.core.io.IoUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.accounting.controller.admin.bankslip.vo.BankSlipCreateReqVO; import cn.iocoder.yudao.module.accounting.controller.admin.bankslip.vo.BankSlipExportReqVO; import cn.iocoder.yudao.module.accounting.controller.admin.bankslip.vo.BankSlipPageReqVO; import cn.iocoder.yudao.module.accounting.controller.admin.bankslip.vo.BankSlipUpdateReqVO; import cn.iocoder.yudao.module.accounting.convert.bankslip.BankSlipConvert; +import cn.iocoder.yudao.module.accounting.dal.dataobject.bankslip.BankReceiptDO; import cn.iocoder.yudao.module.accounting.dal.dataobject.bankslip.BankSlipDO; import cn.iocoder.yudao.module.accounting.dal.mysql.bankslip.BankSlipMapper; +import cn.iocoder.yudao.module.infra.service.file.FileService; +import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import org.springframework.web.multipart.MultipartFile; + import javax.annotation.Resource; +import java.io.DataInput; +import java.io.IOException; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.Collection; import java.util.List; @@ -32,6 +43,10 @@ public class BankSlipServiceImpl implements BankSlipService { @Resource private BankSlipMapper bankSlipMapper; + + @Resource + private FileService fileService; + @Override public Long createBankSlip(BankSlipCreateReqVO createReqVO) { // 插入 @@ -101,4 +116,36 @@ public class BankSlipServiceImpl implements BankSlipService { pageResult.setTotal(size); return pageResult; } + + /** + * 银行回单json转银行回单对象 + * @param bankReceiptDO + */ + @Override + public BankSlipDO jsonToJavaObject(BankReceiptDO bankReceiptDO, MultipartFile multipartFile) throws IOException { + BankSlipDO bankSlipDO = new BankSlipDO(); + String invoiceDateStr = bankReceiptDO.getWords_result().get交易日期().get(0).getWord().replace("年", "-").replaceAll("月", "-").replaceAll("日", "").trim(); + LocalDate parse = LocalDate.parse(invoiceDateStr, DateTimeFormatter.ofPattern("yyyy-MM-dd")); + LocalDateTime date = parse.atStartOfDay();; + bankSlipDO.setTradTime(date); + // 将日期字符串解析为 LocalDate 对象 + //这里用中文是因为调用百度ocr银行回单的接口返回的数据json都是中文的 + bankSlipDO + .setBank(bankReceiptDO.getWords_result().get标题().get(0)) + .setMyCompany(bankReceiptDO.getWords_result().get付款人开户银行().get(0)) + .setMyNum(bankReceiptDO.getWords_result().get付款人账号().get(0).getWord()) + .setAdverseCompany(bankReceiptDO.getWords_result().get收款人开户银行().get(0)) + .setAdverseNum(bankReceiptDO.getWords_result().get收款人账号().get(0).getWord()) + .setSerialNum(bankReceiptDO.getWords_result().get流水号().get(0)) + .setTradMoney(bankReceiptDO.getWords_result().get大写金额().get(0).getWord()) + .setReceiptNum(bankReceiptDO.getWords_result().get回单编号().get(0)) + .setDigest(bankReceiptDO.getWords_result().get摘要().get(0)) + .setPurpose(bankReceiptDO.getWords_result().get用途().get(0)) + ; + bankSlipDO.setFileUrl( + fileService.createFile(null, null, IoUtil.readBytes(multipartFile.getInputStream()))); + + return bankSlipDO; } + +} diff --git a/yudao-module-bs/yudao-module-bs-biz/src/main/java/cn/iocoder/yudao/module/bs/utils/BaiduOcrConstant.java b/yudao-module-bs/yudao-module-bs-biz/src/main/java/cn/iocoder/yudao/module/bs/utils/BaiduOcrConstant.java index 97777bd4..9e957f84 100644 --- a/yudao-module-bs/yudao-module-bs-biz/src/main/java/cn/iocoder/yudao/module/bs/utils/BaiduOcrConstant.java +++ b/yudao-module-bs/yudao-module-bs-biz/src/main/java/cn/iocoder/yudao/module/bs/utils/BaiduOcrConstant.java @@ -55,4 +55,9 @@ public interface BaiduOcrConstant { * 增值税发票验真ofd、pdf */ String INVOICEURL_VERIFICATION = "https://aip.baidubce.com/rest/2.0/ocr/v1/vat_invoice_verification"; + + /** + * 银行回单识别 img,pdf + */ + String BANK_RECEIPT = "https://aip.baidubce.com/rest/2.0/ocr/v1/bank_receipt_new"; } \ No newline at end of file diff --git a/yudao-module-bs/yudao-module-bs-biz/src/main/java/cn/iocoder/yudao/module/bs/utils/BaiduOcrHandler.java b/yudao-module-bs/yudao-module-bs-biz/src/main/java/cn/iocoder/yudao/module/bs/utils/BaiduOcrHandler.java index 6b3e9c8d..7ac85dfe 100644 --- a/yudao-module-bs/yudao-module-bs-biz/src/main/java/cn/iocoder/yudao/module/bs/utils/BaiduOcrHandler.java +++ b/yudao-module-bs/yudao-module-bs-biz/src/main/java/cn/iocoder/yudao/module/bs/utils/BaiduOcrHandler.java @@ -333,6 +333,34 @@ public class BaiduOcrHandler { return null; } + /** + * 增值税发票识别 + * @param baseStr + * @param fileType + * @return + * @throws Exception + */ + public static JSONObject bankReceiptOcr(String baseStr, String fileType) throws Exception { + String accessToken = getAccessToken(); + if (StringUtils.isEmpty(accessToken)) { + throw new RuntimeException("获取token失败"); + } + String image = "image"; + if (fileType.equals("pdf")) { + image = "pdf_file"; + } + String param = image + "=" +baseStr; + String jsonStr = HttpUtil.post(BaiduOcrConstant.BANK_RECEIPT, accessToken, param); + JSONObject jsonObject = JSON.parseObject(jsonStr); + if (!jsonObject.containsKey("words_result")) { + log.error("银行回单识别失败"+jsonObject.toString()); + throw exception("银行回单识别失败"+jsonObject.toString()); + } + + return jsonObject; + } + + /** * 增值税发票识别 * @param baseStr diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index 8d582ac9..45d40ba9 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -45,13 +45,13 @@ spring: datasource: master: name: ruoyi-vue-pro - url: jdbc:mysql://127.0.0.1:3307/lyr-one?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 + url: jdbc:mysql://127.0.0.1:3306/lyr-one?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 # url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.slave.name} # PostgreSQL 连接的示例 # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 # url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=${spring.datasource.dynamic.datasource.master.name} # SQLServer 连接的示例 username: root - password: 123456 + password: root # username: sa # password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # slave: # 模拟从库,可根据自己需要修改