IVController.java 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. package com.malk.pro.guyuan.controller;
  2. import com.alibaba.fastjson.JSON;
  3. import com.malk.pro.guyuan.server.model.McInvoiceDto;
  4. import com.malk.pro.guyuan.server.model.McInvoiceKind;
  5. import com.malk.pro.guyuan.server.tencent.TXYConf;
  6. import com.malk.pro.guyuan.service.tencent.IvYdService;
  7. import com.malk.pro.guyuan.service.tencent.TXYInvoice;
  8. import com.malk.server.aliwork.YDConf;
  9. import com.malk.server.aliwork.YDParam;
  10. import com.malk.server.common.FilePath;
  11. import com.malk.server.common.McException;
  12. import com.malk.server.common.McR;
  13. import com.malk.service.aliwork.YDClient;
  14. import com.malk.service.aliwork.YDService;
  15. import com.malk.utils.*;
  16. import com.spire.pdf.PdfCompressionLevel;
  17. import com.spire.pdf.PdfDocument;
  18. import com.spire.pdf.PdfPageBase;
  19. import com.spire.pdf.exporting.PdfImageInfo;
  20. import com.spire.pdf.graphics.PdfBitmap;
  21. import com.tencentcloudapi.common.exception.TencentCloudSDKException;
  22. import lombok.SneakyThrows;
  23. import lombok.extern.slf4j.Slf4j;
  24. import net.coobird.thumbnailator.Thumbnails;
  25. import org.apache.commons.lang3.StringUtils;
  26. import org.apache.poi.util.IOUtils;
  27. import org.springframework.beans.factory.annotation.Autowired;
  28. import org.springframework.web.bind.annotation.PostMapping;
  29. import org.springframework.web.bind.annotation.RequestBody;
  30. import org.springframework.web.bind.annotation.RequestMapping;
  31. import org.springframework.web.bind.annotation.RestController;
  32. import javax.servlet.http.HttpServletRequest;
  33. import java.io.ByteArrayInputStream;
  34. import java.io.ByteArrayOutputStream;
  35. import java.io.File;
  36. import java.io.InputStream;
  37. import java.net.URL;
  38. import java.util.*;
  39. import java.util.stream.Collectors;
  40. /**
  41. * 错误抛出与拦截详见CatchException
  42. */
  43. @Slf4j
  44. @RestController
  45. @RequestMapping("/guyuan/")
  46. public class IVController {
  47. @Autowired
  48. private TXYInvoice txyInvoice;
  49. @Autowired
  50. private YDClient ydClient;
  51. /// 优先获取字段, 新版本接口已支持字段返回
  52. private String findValue(List<Map<String, String>> infos, String... names) {
  53. for (String name : names) {
  54. Optional optional = infos.stream().filter(info -> info.get("Name").equals(name)).findAny();
  55. if (optional.isPresent()) {
  56. return String.valueOf(((Map) optional.get()).get("Value"));
  57. }
  58. }
  59. return "";
  60. }
  61. // 兼容历史配置, 格式 (谷元)
  62. private String guyuanNameRepalce(String name) {
  63. if (name.contains("谷元")) {
  64. return UtilString.replaceBracketIsWhole(name);
  65. } else {
  66. return UtilString.replaceBracketIsSemiangle(name);
  67. }
  68. }
  69. /// url压缩转base64
  70. @SneakyThrows
  71. private String imageUrlConvertBase64(String imageUrl) {
  72. ByteArrayOutputStream out = new ByteArrayOutputStream();
  73. // scale(比例), outputQuality(质量)
  74. Thumbnails.fromURLs(Arrays.asList(new URL(imageUrl))).scale(0.5f).outputQuality(0.25f).toOutputStream(out);
  75. InputStream inputStream = new ByteArrayInputStream(out.toByteArray());
  76. //转换为base64
  77. byte[] bytes = IOUtils.toByteArray(inputStream);
  78. return Base64.getEncoder().encodeToString(bytes);
  79. }
  80. @Autowired
  81. private FilePath filePath;
  82. /// PDF压缩转base64
  83. @SneakyThrows
  84. private String pdfUrlConvertBase64(String pdfUrl) {
  85. String fileName = "tmp_" + new Date().getTime() + ".pdf";
  86. // 下载文件
  87. File file = UtilFile.mkdirIfNot(fileName, filePath.getPath().getTmp());
  88. UtilHttp.doDownload(pdfUrl, file);
  89. // PDF压缩
  90. PdfDocument doc = new PdfDocument(); // 创建PdfDocument类的对象
  91. doc.loadFromFile(file.getAbsolutePath()); // 加载PDF文档
  92. doc.getFileInfo().setIncrementalUpdate(false); // 禁用增量更新
  93. doc.setCompressionLevel(PdfCompressionLevel.Normal); // 将压缩级别设置为最佳
  94. // 遍历文档页面
  95. for (int i = 0; i < doc.getPages().getCount(); i++) {
  96. PdfPageBase page = doc.getPages().get(i); // 获取指定页面
  97. PdfImageInfo[] images = page.getImagesInfo(); // 获取每个页面的图像信息集合
  98. // 遍历集合中的所有项目
  99. if (images != null && images.length > 0)
  100. for (int j = 0; j < images.length; j++) {
  101. PdfImageInfo image = images[j]; // 获取指定图片
  102. PdfBitmap bp = new PdfBitmap(image.getImage());
  103. bp.setQuality(30); // 设置压缩质量
  104. page.replaceImage(j, bp); // 将原始图像替换为压缩图像
  105. }
  106. }
  107. // 将结果文档保存至另一个PDF文档中: 覆盖
  108. doc.saveToFile(file.getAbsolutePath());
  109. doc.close();
  110. // PDF转base64, 无需透出本地文件地址
  111. String base64 = UtilFile.fileToBase64(file.getAbsolutePath());
  112. // 删除临时PDF文件
  113. UtilFile.deleteFile(file.getAbsolutePath());
  114. return base64;
  115. }
  116. // prd 校验发票抬头, 购买方范围
  117. private void validateBuyer(String BuyerName, String tips) {
  118. List<String> corpNames = Arrays.asList(
  119. "谷元(上海)文化科技有限责任公司",
  120. "上海爱鱼文化传媒有限公司",
  121. "上海渔米可禧文化传媒有限公司",
  122. "上海巧豆文化传媒有限公司",
  123. "钰鸿文化创意(上海)有限公司",
  124. "上海攸元文化科技有限公司",
  125. "上海盈田演出经纪有限公司",
  126. "上海小蝠文化科技有限公司",
  127. "沐田居海(厦门)文化传媒有限公司",
  128. "北京元环文化科技有限责任公司",
  129. "厦门攸元文化科技有限公司",
  130. "厦门亨有文化科技有限公司",
  131. "厦门银雀思汀文化传媒有限公司",
  132. "上海渝泽信息科技有限公司",
  133. "厦门神谷飞流影视传媒有限公司",
  134. "厦门谷钛数字科技有限公司",
  135. "上海观情科技有限公司",
  136. "渔米可禧文化传媒(香港)有限公司",
  137. "杭州渔米可禧文化传媒有限公司");
  138. McException.assertAccessException(!corpNames.contains(BuyerName), tips + ", 购买方名称不合法!");
  139. }
  140. /**
  141. * 混票识别 [新版本]
  142. */
  143. @PostMapping("invoice-iv2")
  144. McR invoice_iv2(@RequestBody Map<String, String> data) throws TencentCloudSDKException {
  145. McException.assertParamException_Null(data, "url");
  146. String image = ydClient.convertTemporaryUrl(data.get("url"));
  147. log.info("混票识别, 免登地址, {}", image);
  148. // 非PDF, 且内存大于3M, 压缩后上传
  149. if (UtilMap.getFloat(data, "size") > 3.0f && !UtilMap.getBoolean(data, "isPdf")) {
  150. image = imageUrlConvertBase64(image);
  151. }
  152. if (UtilMap.getFloat(data, "size") > 6.0f && UtilMap.getBoolean(data, "isPdf")) {
  153. image = pdfUrlConvertBase64(image);
  154. }
  155. List<Map> invoices = (List<Map>) txyInvoice.doRecognizeGeneralInvoice(image).get("MixedInvoiceItems");
  156. List<McInvoiceDto> result = invoices.stream().map(item -> {
  157. Map prop = UtilMap.getMap(UtilMap.getMap(item, "SingleInvoiceInfos"), UtilMap.getString(item, "SubType"));
  158. // ppExt: 通用字段定义
  159. McInvoiceDto invoiceDto = McInvoiceDto.builder()
  160. .name(UtilMap.getString(item, "SubTypeDescription"))
  161. .kindName(UtilMap.getString(item, "TypeDescription"))
  162. .kind(UtilMap.getInt(item, "Type"))
  163. .code(UtilMap.getString(prop, "Code"))
  164. .serial(UtilMap.getString(prop, "Number"))
  165. .date(UtilString.replaceDateZH_cn(UtilMap.getString(prop, "Date")))
  166. .checkCode(UtilMap.getString(prop, "CheckCode"))
  167. // ppExt: 多明细行时, 优先取值合计 [全电票返回了subTotal字段, 但值为空]
  168. .amount(UtilNumber.setBigDecimal(UtilMap.getString_first(prop, "SubTotal", "Total")))
  169. .tax(UtilNumber.setBigDecimal(UtilMap.getString_first(prop, "SubTax", "Tax")))
  170. .excludingTax(UtilNumber.setBigDecimal(UtilMap.getString(prop, "PretaxAmount")))
  171. .buyerName(guyuanNameRepalce(UtilMap.getString(prop, "Buyer")))
  172. // ppExt: 中央非税未返回税号官方说明: 非税发票理论是没有税号的,图片中属于信用代码
  173. .buyerTaxId(UtilMap.getString(prop, "BuyerTaxID"))
  174. .sellerName(guyuanNameRepalce(UtilMap.getString_first(prop, "Seller", "Issuer"))) // 行程单: 填开单位
  175. .sellerTaxId(UtilMap.getString_first(prop, "SellerTaxID", "AgentCode")) // 行程单: 销售单位代号
  176. .passengerName(UtilMap.getString_first(prop, "Name", "UserName")) // 火车票, 行程单
  177. // 交通出行
  178. .seatType(UtilMap.getString(prop, "Seat"))
  179. .departureTime(UtilString.replaceDateZH_cn(UtilMap.getString(prop, "DateGetOn")) + " " + UtilMap.getString(prop, "TimeGetOn"))
  180. .departurePort(UtilMap.getString_first(prop, "StationGetOn", "Entrance", "Place")) // 火车票: 出发车站, 过路过桥费: 入口, 出租车: 发票所在地
  181. .arrivePort(UtilMap.getString_first(prop, "StationGetOff", "Exit")) // 行程单, 火车票: 到达车站, 过路过桥费: 出口
  182. .trainNo(UtilMap.getString_first(prop, "TrainNumber", "LicensePlate")) // 火车票: 车次, 出租车: 车牌号
  183. .insuranceCosts(UtilNumber.setBigDecimal((UtilMap.getString(prop, "Insurance")))) // 行程单: 保险费
  184. .fuelCosts(UtilNumber.setBigDecimal((UtilMap.getString(prop, "FuelSurcharge")))) // 行程单: 燃油附加费
  185. .constructionCosts(UtilNumber.setBigDecimal((UtilMap.getString(prop, "AirDevelopmentFund")))) // 行程单: 民航发展基金
  186. .build();
  187. // ppExt: 机票行程单, 行程与座位信息在明细内
  188. if ("机票行程单".equals(item.get("TypeDescription"))) {
  189. Map flight = (Map) UtilMap.getList(prop, "FlightItems").get(0);
  190. invoiceDto.setDepartureTime(UtilString.replaceDateZH_cn(UtilMap.getString(item, "DateGetOn")) + " " + UtilMap.getString(prop, "TimeGetOn"));
  191. invoiceDto.setDeparturePort(UtilMap.getString(flight, "StationGetOn"));
  192. invoiceDto.setArrivePort(UtilMap.getString(flight, "StationGetOff"));
  193. invoiceDto.setSeatType(UtilMap.getString(flight, "Seat"));
  194. }
  195. if ("出租车发票".equals(item.get("TypeDescription"))) {
  196. // 上下车时间
  197. invoiceDto.setDepartureTime(UtilMap.getString(prop, "TimeGetOn") + " ~ " + UtilMap.getString(prop, "TimeGetOff"));
  198. }
  199. return invoiceDto;
  200. }).collect(Collectors.toList());
  201. return McR.success(McInvoiceDto.formatResponse(result));
  202. }
  203. /**
  204. * 混票识别 [旧版本, 已废弃]
  205. */
  206. @PostMapping("invoice-iv")
  207. McR invoice_iv(@RequestBody Map<String, String> data) throws TencentCloudSDKException {
  208. McException.assertParamException_Null(data, "url");
  209. String image = ydClient.convertTemporaryUrl(data.get("url"));
  210. log.info("混票识别, 免登地址, {}", image);
  211. // 非PDF, 且内存大于3M, 压缩后上传
  212. if (UtilMap.getFloat(data, "size") > 3.0f && !UtilMap.getBoolean(data, "isPdf")) {
  213. image = imageUrlConvertBase64(image);
  214. }
  215. if (UtilMap.getFloat(data, "size") > 6.0f && UtilMap.getBoolean(data, "isPdf")) {
  216. image = pdfUrlConvertBase64(image);
  217. }
  218. // ppExt: 通用字段定义
  219. List<Map> invoices = (List<Map>) txyInvoice.doMixedInvoiceOCR(image).get("MixedInvoiceItems");
  220. List<McInvoiceDto> result = invoices.stream().map(item -> {
  221. String kind = TXYConf.TYPE_INVOICE.get(item.get("Type").toString());
  222. List<Map<String, String>> infos = (List<Map<String, String>>) item.get("SingleInvoiceInfos");
  223. McInvoiceDto.assertSuccess(item, kind); // 响应断言
  224. String invoiceName = findValue(infos, "发票名称");
  225. if (kind.equals("全电发票")) {
  226. kind = invoiceName.contains("增值税专用发票") ? "全电专用发票" : "全电普通发票";
  227. }
  228. if (kind.equals("增值税发票")) {
  229. kind = invoiceName.contains("增值税专用发票") ? "增值税专用发票" : "增值税普通发票";
  230. if (invoiceName.contains("增值税电子")) {
  231. kind = invoiceName.contains("专用发票") ? "增值税电子专用发票" : "增值税电子普通发票";
  232. }
  233. }
  234. McInvoiceDto invoiceDto = McInvoiceDto.builder()
  235. .name(invoiceName)
  236. .kindName(kind)
  237. .kind(McInvoiceKind.getKindCode(kind))
  238. .code(findValue(infos, "发票代码", "票据代码")) // 发票, 非税发票
  239. // 储存唯一ID [发票, 火车票, 行程单]
  240. .serial(findValue(infos, "发票号码", "编号", "电子客票号码", "票据号码").replace("No", "")) // 发票, 非税发票
  241. .date(findValue(infos, "开票日期").replace("年", "-").replace("月", "-").replace("日", ""))
  242. .checkCode(findValue(infos, "校验码"))
  243. .amount(UtilNumber.replaceCurrencyCHYToDecimal(findValue(infos, "小写金额", "价税合计(小写)", "合计金额", "票价", "金额"))) // 发票, 全电票, 行程单, 火车票, 过路过桥费
  244. .excludingTax(UtilNumber.replaceCurrencyCHYToDecimal(findValue(infos, "合计金额", "金额", "票价", "小写金额"))) // [ppExt: 多明细行时, 优先取值合计] 行程单, 火车票, 定额发票
  245. .tax(UtilNumber.replaceCurrencyCHYToDecimal(findValue(infos, "合计税额"))) // 增值税发票
  246. .buyerName(guyuanNameRepalce(findValue(infos, "购买方名称", "交款人"))) // 发票, 非税发票
  247. .buyerTaxId(findValue(infos, "购买方识别号", "购买方统一社会信用代码/纳税人识别号", "交款人统一社会信用代码")) // 发票, 全电票, 非税发票
  248. .sellerName(guyuanNameRepalce(findValue(infos, "销售方名称", "填开单位"))) // 行程单
  249. .sellerTaxId(findValue(infos, "销售方识别号", "销售方统一社会信用代码/纳税人识别号", "销售单位代号")) // 发票, 全电票, 行程单
  250. .passengerName(findValue(infos, "旅客姓名", "姓名")) // 行程单, 火车票
  251. .seatType(findValue(infos, "座位等级", "席别")) // 行程单, 火车票
  252. .departurePort(findValue(infos, "始发地", "出发站", "入口")) // 行程单, 火车票, 过路过桥费
  253. .arrivePort(findValue(infos, "目的地", "到达站", "出口")) // 行程单, 火车票, 过路过桥费
  254. .trainNo(findValue(infos, "航班号", "车次", "车牌号")) // 行程单, 火车票, 出租车
  255. .insuranceCosts(UtilNumber.setBigDecimal((findValue(infos, "保险费")))) // 行程单
  256. .fuelCosts(UtilNumber.setBigDecimal((findValue(infos, "燃油附加费")))) // 行程单
  257. .constructionCosts(UtilNumber.setBigDecimal((findValue(infos, "民航发展基金")))) // 行程单
  258. .build();
  259. // 价格不一致情况下, 通过合计返回
  260. if (!UtilNumber.equalBigDecimal(invoiceDto.getAmount(), invoiceDto.getExcludingTax().add(invoiceDto.getTax()))) {
  261. invoiceDto.setAmount(invoiceDto.getExcludingTax().add(invoiceDto.getTax()));
  262. }
  263. // 机票行程单
  264. if (kind.equals(McInvoiceKind.JP.getDesc())) {
  265. String date = findValue(infos, "日期").replace("年", "-").replace("月", "-").replace("日", " ");
  266. invoiceDto.setDepartureTime(date + " " + findValue(infos, "时间"));
  267. }
  268. // 火车票
  269. if (kind.equals(McInvoiceKind.HC.getDesc())) {
  270. invoiceDto.setDepartureTime(findValue(infos, "出发时间").replace("年", "-").replace("月", "-").replace("日", " "));
  271. }
  272. // 出租车
  273. if (kind.equals(McInvoiceKind.CZC.getDesc())) {
  274. String date = findValue(infos, "日期").replace("年", "-").replace("月", "-").replace("日", " ");
  275. invoiceDto.setDepartureTime(date + " " + findValue(infos, "上车"));
  276. }
  277. return invoiceDto;
  278. }).collect(Collectors.toList());
  279. return McR.success(McInvoiceDto.formatResponse(result));
  280. }
  281. /**
  282. * 发票查重, 验真
  283. */
  284. @PostMapping("invoice-va")
  285. McR invoice_va(@RequestBody Map data) {
  286. McException.assertParamException_Null(data, "param");
  287. List<McInvoiceDto> invoices = JSON.parseArray(JSON.toJSONString(data.get("param")), McInvoiceDto.class);
  288. log.info("发票查重, 验真, {}", invoices);
  289. invoices.forEach(UtilMc.consumerWithIndex((item, index) -> {
  290. McInvoiceDto dto = (McInvoiceDto) item;
  291. String invoiceNo = dto.getSerial(); // 唯一标识, 发票号码
  292. String serial = "第【" + (index + 1) + "】张发票";
  293. validateBuyer(dto.getBuyerName(), serial + "有疑问");
  294. McException.assertAccessException(StringUtils.isBlank(invoiceNo), serial + ", 识别结果为空, 请检查!");
  295. YDParam ydParam = YDParam.builder()
  296. .formUuid("FORM-W2A66Z910O9B3LP9C6IYUDPRVWY62DO0YHIILY")
  297. .searchFieldJson(JSON.toJSONString(UtilMap.map("radioField_liihyrtb, textField_liihyrt8", "否", invoiceNo)))
  298. .build();
  299. List<String> idList = (List<String>) ydClient.queryData(ydParam, YDConf.FORM_QUERY.retrieve_search_form_id).getData();
  300. if (idList.size() > 0) {
  301. McException.exceptionAccess(serial + "已存在, 请勿重复提交!");
  302. }
  303. // prd 仅仅识别增值税普通发票
  304. if (dto.getName().contains("普通发票")) {
  305. String serialTips = serial + "有疑问";
  306. try {
  307. // ppExt: 识别与验真后抬头对比 [全电票, 新版本识别接口, 返回名称为: 电子发票(普通发票) 不包含全电标识, 发类型为: 全电发票. 注意取值]
  308. Map rsp = txyInvoice.doVatInvoiceVerifyNew(dto.getKindName(), dto.getCode(), invoiceNo, dto.getDate(), String.valueOf(dto.getAmount()), dto.getCheckCode(), String.valueOf(dto.getExcludingTax()), serialTips);
  309. Map invoice = (Map) rsp.get("Invoice");
  310. McException.assertAccessException(!dto.getBuyerName().equals(guyuanNameRepalce(invoice.get("BuyerName").toString())), serialTips + ", 购买方名称不匹配!");
  311. McException.assertAccessException(!dto.getBuyerTaxId().equals(invoice.get("BuyerTaxCode")), serialTips + ", 购买方税号不匹配!");
  312. McException.assertAccessException(!dto.getSellerName().equals(guyuanNameRepalce(invoice.get("SellerName").toString())), serialTips + ", 销售方名称不匹配!");
  313. McException.assertAccessException(!dto.getSellerTaxId().equals(invoice.get("SellerTaxCode")), serialTips + ", 销售方税号不匹配!");
  314. } catch (TencentCloudSDKException e) {
  315. log.error(e.getMessage(), e);
  316. // prd: 上传发票为假发票时,提示:该发票有疑问,请联系财务人员
  317. String message = e.getMessage();
  318. // ppExt: 已经是新版本接口, 过滤提示 [官方答复: 提示不会检测您是否使用的是新版,所有的用户都会提示, 忽略即可]
  319. if (message.contains("温馨提示")) {
  320. message = message.split("温馨提示")[0];
  321. }
  322. if (message.contains("发票不存在")) {
  323. message = "有疑问,请联系财务人员";
  324. }
  325. McException.exceptionAccess(serial + message);
  326. }
  327. }
  328. }));
  329. return McR.success();
  330. }
  331. @Autowired
  332. private YDService ydService;
  333. @Autowired
  334. private IvYdService ivYdService;
  335. /**
  336. * 发票状态更新: 服务注册
  337. */
  338. @PostMapping("invoice-up")
  339. McR invoice_va(HttpServletRequest request) {
  340. Map data = UtilServlet.getParamMap(request);
  341. log.info("发票状态更新: 服务注册, {}", data);
  342. String compId = UtilMap.getString(data, "compId");
  343. String status = UtilMap.getString(data, "status");
  344. // 读取关联表单
  345. List<String> associationForm = (List<String>) JSON.parse(UtilMap.getString(data, "multiAssociation"));
  346. List<String> formInstanceIds = new ArrayList<>();
  347. for (String record : associationForm) {
  348. // 解析关联表单 ['[{"formType":"receipt","formUuid":"FORM-W2A66Z910O9B3LP9C6IYUDPRVWY62DO0YHIILY", "subTitle": "21.16", "instanceId": "FINST-WJC66GC1E1KWXO0173ZVW56MUFHX2ULES5DCMZGA", "appType": "APP_FKRK7Y94DPI1S9DV1605","title":"电子发票(普通发票)"}]']
  349. List<Map> associationData = (List<Map>) JSON.parse(record);
  350. formInstanceIds.addAll(associationData.stream().map(form -> UtilMap.getString(form, "instanceId")).collect(Collectors.toList()));
  351. }
  352. // 宜搭批量更新
  353. Map update = UtilMap.map(compId, status);
  354. if (compId.equals("selectField_liihyrt6")) {
  355. update.put("radioField_liw7rb2q", "否"); // 提交后, 更新是否退回标识为否
  356. }
  357. // prd 9.10 更新报销单, 关联到发票:: ppExt 宜搭服务注册, 提交规则系统默认字段 [详见 YDService]
  358. ivYdService.operateData(data, update, YDParam.builder()
  359. .formUuid("FORM-W2A66Z910O9B3LP9C6IYUDPRVWY62DO0YHIILY")
  360. .formInstanceIdList(formInstanceIds)
  361. .updateFormDataJson(JSON.toJSONString(update))
  362. .appType("APP_FKRK7Y94DPI1S9DV1605")
  363. .systemToken("FN7666A1ZD0STZZ75W4CKD1GD07X3PUW2FBRKT")
  364. .build(), YDConf.FORM_OPERATION.multi_update);
  365. return McR.success();
  366. }
  367. /**
  368. * 发票状态更新: 退回提交
  369. */
  370. @PostMapping("invoice-zy")
  371. McR invoice_zy(@RequestBody Map data) {
  372. log.info("发票状态更新: 退回提交, {}", data);
  373. List<String> pre_ids = (List<String>) data.get("pre_ids"); // 释放修改前
  374. List<String> cur_ids = (List<String>) data.get("cur_ids"); // 占用修改后
  375. // [前端调用添加] 退回为监听宜搭dom事件, 先执行接口调用, 才会校验宜搭必填, 过滤无效调用
  376. if (cur_ids.size() == 0) {
  377. return McR.success();
  378. }
  379. Map pre_update = (Map) data.get("pre_update");
  380. Map cur_update = (Map) data.get("cur_update");
  381. // 宜搭批量更新
  382. ydClient.operateData(YDParam.builder()
  383. .formUuid("FORM-W2A66Z910O9B3LP9C6IYUDPRVWY62DO0YHIILY")
  384. .formInstanceIdList(pre_ids)
  385. .updateFormDataJson(JSON.toJSONString(pre_update))
  386. .build(), YDConf.FORM_OPERATION.multi_update);
  387. ydClient.operateData(YDParam.builder()
  388. .formUuid("FORM-W2A66Z910O9B3LP9C6IYUDPRVWY62DO0YHIILY")
  389. .formInstanceIdList(cur_ids)
  390. .updateFormDataJson(JSON.toJSONString(cur_update))
  391. .build(), YDConf.FORM_OPERATION.multi_update);
  392. return McR.success();
  393. }
  394. @PostMapping("test")
  395. McR test() {
  396. // List<Map> process = (List<Map>) ydClient.queryData(YDParam.builder()
  397. // .formUuid("W2A66Z910O9B3LP9C6IYUDPRVWY62DO0YHIILY")
  398. // .formInstId("FINST-NGA66WA1FV4EB7QJC3OATA3EV8MK35Z9COEMLFR22")
  399. // .build(), YDConf.FORM_QUERY.retrieve_id).getData();
  400. List<Map> process = (List<Map>) ydClient.queryData(YDParam.builder()
  401. .formUuid("FORM-0IA66C71F6NBAETREO8DE9SSN43D3YIZ0AYILC")
  402. .searchFieldJson(JSON.toJSONString(UtilMap.map("textField_lmewsobs", "Y16668919W4E4FHQ6123ADDHB8XK3S709YEMLXWF")))
  403. .build(), YDConf.FORM_QUERY.retrieve_search_form).getData();
  404. return McR.success();
  405. }
  406. }