InvoiceLibraryServiceImpl.java 275 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409
  1. package com.malk.qiwang.Service.impl;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.JSONArray;
  4. import com.alibaba.fastjson.JSONObject;
  5. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  6. import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
  7. import com.malk.qiwang.Service.QiWangService;
  8. import com.malk.qiwang.Service.TXYInvoice;
  9. import com.malk.qiwang.entity.CompanyTitle;
  10. import com.malk.qiwang.entity.InvoiceLibrary;
  11. import com.malk.qiwang.mapper.CompanyTitleMapper;
  12. import com.malk.qiwang.mapper.InvoiceLibraryMapper;
  13. import com.malk.qiwang.Service.IInvoiceLibraryService;
  14. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  15. import com.malk.qiwang.model.McInvoiceDto;
  16. import com.malk.server.common.FilePath;
  17. import com.malk.server.common.McException;
  18. import com.malk.server.common.McR;
  19. import com.malk.server.dingtalk.DDR_New;
  20. import com.malk.service.aliwork.YDClient;
  21. import com.malk.service.dingtalk.DDClient;
  22. import com.malk.service.dingtalk.DDClient_Contacts;
  23. import com.malk.service.dingtalk.DDClient_Workflow;
  24. import com.malk.utils.*;
  25. import com.spire.pdf.PdfCompressionLevel;
  26. import com.spire.pdf.PdfDocument;
  27. import com.spire.pdf.PdfPageBase;
  28. import com.spire.pdf.exporting.PdfImageInfo;
  29. import com.spire.pdf.graphics.PdfBitmap;
  30. import com.tencentcloudapi.common.exception.TencentCloudSDKException;
  31. import lombok.SneakyThrows;
  32. import lombok.extern.slf4j.Slf4j;
  33. import net.coobird.thumbnailator.Thumbnails;
  34. import org.apache.commons.lang3.StringUtils;
  35. import org.apache.poi.hpsf.Decimal;
  36. import org.apache.poi.util.IOUtils;
  37. import org.springframework.beans.factory.annotation.Autowired;
  38. import org.springframework.beans.factory.annotation.Value;
  39. import org.springframework.core.io.Resource;
  40. import org.springframework.core.io.UrlResource;
  41. import org.springframework.http.HttpEntity;
  42. import org.springframework.http.HttpHeaders;
  43. import org.springframework.http.MediaType;
  44. import org.springframework.http.ResponseEntity;
  45. import org.springframework.stereotype.Service;
  46. import org.springframework.web.bind.annotation.GetMapping;
  47. import org.springframework.web.bind.annotation.PathVariable;
  48. import org.springframework.web.bind.annotation.RequestParam;
  49. import org.springframework.web.client.RestTemplate;
  50. import java.io.*;
  51. import java.math.BigDecimal;
  52. import java.net.HttpURLConnection;
  53. import java.net.URL;
  54. import java.nio.file.Files;
  55. import java.nio.file.Path;
  56. import java.nio.file.Paths;
  57. import java.time.LocalDate;
  58. import java.time.LocalDateTime;
  59. import java.time.format.DateTimeFormatter;
  60. import java.util.*;
  61. import java.util.concurrent.CompletableFuture;
  62. import java.util.stream.Collectors;
  63. /**
  64. * <p>
  65. * 发票库表 服务实现类
  66. * </p>
  67. *
  68. * @author LQY
  69. * @since 2026-04-27
  70. */
  71. @Service
  72. @Slf4j
  73. public class InvoiceLibraryServiceImpl extends ServiceImpl<InvoiceLibraryMapper, InvoiceLibrary> implements IInvoiceLibraryService {
  74. @Autowired
  75. private QiWangService qiWangService;
  76. @Autowired
  77. private DDClient ddClient;
  78. @Autowired
  79. private YDClient ydClient;
  80. @Autowired
  81. private TXYInvoice txyInvoice;
  82. @Value("${dingtalk.operator}")
  83. private String operator;
  84. @Value("${dingtalk.downloadPath}")
  85. private String downloadPath;
  86. @Autowired
  87. private FilePath filePath;
  88. @Autowired
  89. private DDClient_Workflow ddClientWorkflow;
  90. @Autowired
  91. private InvoiceLibraryMapper baseMapper;
  92. @Autowired
  93. private CompanyTitleMapper companyTitleMapper;
  94. @Autowired
  95. private DDClient_Contacts ddClient_contacts;
  96. private static final String url = "http://47.103.203.2:9092/qiwang/";
  97. // private static final String url = "http://410a09c5.r39.cpolar.top/qiwang/";
  98. @Override
  99. public McR invoiceLibrary(Map map) {
  100. log.info("接收到的参数: {}", map);
  101. try {
  102. String processInstanceId = UtilMap.getString(map, "processInstanceId");
  103. if (StringUtils.isBlank(processInstanceId)) {
  104. log.error("processInstanceId为空");
  105. return McR.error("400", "审批实例ID不能为空");
  106. }
  107. // 获取审批实例信息
  108. String accessToken = ddClient.getAccessToken();
  109. Map processInstance = ddClientWorkflow.getProcessInstanceId(accessToken, processInstanceId);
  110. if (processInstance == null) {
  111. log.error("获取审批实例失败: {}", processInstanceId);
  112. return McR.error("500", "获取审批实例信息失败");
  113. }
  114. String userId = (String) processInstance.get("originatorUserId");
  115. String businessId = (String) processInstance.get("businessId");
  116. log.info("审批人ID: {}", userId);
  117. List<Map> formComponentValues = (List<Map>) processInstance.get("formComponentValues");
  118. if (formComponentValues == null || formComponentValues.isEmpty()) {
  119. log.warn("表单数据为空");
  120. return McR.error("400", "表单数据为空");
  121. }
  122. // 存储主表的字段值
  123. String bm = null;
  124. String bxlb = null;
  125. String fkzhxx = null;
  126. String fkzh = null;
  127. String yhqc = null;
  128. String skzh = null;
  129. BigDecimal mainJe = null;
  130. // 遍历收集主表字段值
  131. for (Map formComponentValue : formComponentValues) {
  132. String id = String.valueOf(formComponentValue.get("id"));
  133. Object value = formComponentValue.get("value");
  134. if ("MoneyField_3QZLY8BD3780".equals(id)) {
  135. if (value instanceof BigDecimal) {
  136. mainJe = (BigDecimal) value;
  137. } else if (value != null) {
  138. try {
  139. mainJe = new BigDecimal(String.valueOf(value));
  140. } catch (NumberFormatException e) {
  141. log.error("金额格式转换失败: {}", value);
  142. mainJe = BigDecimal.ZERO;
  143. }
  144. }
  145. }
  146. if ("RecipientAccountField_1FGFBO5HL1PC0".equals(id)) {
  147. skzh = value != null ? String.valueOf(value) : "";
  148. }
  149. if ("DDSelectField_1TP75OVCPAAO0".equals(id)) {
  150. bxlb = value != null ? String.valueOf(value) : "";
  151. }
  152. if ("DepartmentField_1RD2SROH579C0".equals(id)) {
  153. bm = value != null ? String.valueOf(value) : "";
  154. }
  155. if ("DDSelectField_11D6S45JKFHS0".equals(id)) {
  156. fkzhxx = value != null ? String.valueOf(value) : "";
  157. }
  158. if ("TextField_1S1VV9PIIQWW0".equals(id)) {
  159. fkzh = value != null ? String.valueOf(value) : "";
  160. }
  161. if ("TextField_ZUVAAH3FIGW0".equals(id)) {
  162. yhqc = value != null ? String.valueOf(value) : "";
  163. }
  164. }
  165. // 遍历子表数据
  166. for (Map formComponentValue : formComponentValues) {
  167. String id = String.valueOf(formComponentValue.get("id"));
  168. if ("TableField_1A1CDMEN8DDS0".equals(id)) {
  169. String tableFieldValue = String.valueOf(formComponentValue.get("value"));
  170. log.info("子表数据: {}", tableFieldValue);
  171. try {
  172. List<Map> tableRows = (List<Map>) JSONObject.parse(tableFieldValue);
  173. if (tableRows == null || tableRows.isEmpty()) {
  174. log.warn("子表数据为空");
  175. return McR.error("400", "发票明细为空");
  176. }
  177. // 用于存储无发票记录和发票记录
  178. List<InvoiceLibrary> noInvoiceList = new ArrayList<>();
  179. List<InvoiceLibrary> invoiceList = new ArrayList<>();
  180. List<Object> allResults = new ArrayList<>();
  181. // 存储有发票("是"和"被共享")的解析数据
  182. List<Map<String, Object>> parsedInvoiceDataList = new ArrayList<>();
  183. // 存储共享发票数据
  184. List<Map<String, Object>> sharedInvoiceDataList = new ArrayList<>();
  185. // 用于校验的集合
  186. List<String> invoiceNumberList = new ArrayList<>();
  187. Map<String, String> invoiceNumberToFileName = new HashMap<>();
  188. List<String> invalidBuyerTaxIds = new ArrayList<>();
  189. // 存储税率信息(从"被共享"发票中获取)
  190. BigDecimal sharedTaxRate = BigDecimal.ZERO;
  191. String sharedKind = "";
  192. boolean hasSharedInvoice = false; // 标记是否存在"被共享"发票
  193. // ========== 第一遍遍历:收集数据并查找"被共享"发票 ==========
  194. for (Map row : tableRows) {
  195. List<Map> rowValues = (List<Map>) row.get("rowValue");
  196. String hasInvoice = null;
  197. String cdbm = null;
  198. String lbmx = null;
  199. String bz = null;
  200. BigDecimal detailje = null;
  201. List<Map> attachmentList = null;
  202. for (Map rowItem : rowValues) {
  203. String key = String.valueOf(rowItem.get("key"));
  204. if ("DDSelectField_1ORUK0KIM5D6O".equals(key)) {
  205. Object value = rowItem.get("value");
  206. hasInvoice = value != null ? String.valueOf(value) : "";
  207. }
  208. if ("DDSelectField_12SQMP8DSUF40".equals(key)) {
  209. Object value = rowItem.get("value");
  210. lbmx = value != null ? String.valueOf(value) : "";
  211. }
  212. if ("TextareaField_1GRJNUC4BAE80".equals(key)) {
  213. Object value = rowItem.get("value");
  214. bz = value != null ? String.valueOf(value) : "";
  215. }
  216. if ("DepartmentField_ATSWRJBJ7K00".equals(key)) {
  217. Object value = rowItem.get("value");
  218. cdbm = value != null ? String.valueOf(value) : "";
  219. }
  220. if ("NumberField_1622XLFLYEWW0".equals(key)) {
  221. Object value = rowItem.get("value");
  222. if (value instanceof BigDecimal) {
  223. detailje = (BigDecimal) value;
  224. } else if (value != null) {
  225. try {
  226. detailje = new BigDecimal(String.valueOf(value));
  227. } catch (NumberFormatException e) {
  228. log.error("金额格式转换失败: {}", value);
  229. detailje = BigDecimal.ZERO;
  230. }
  231. } else {
  232. detailje = BigDecimal.ZERO;
  233. }
  234. }
  235. if ("DDAttachment_Z02OGR5QL8U8".equals(key)) {
  236. Object value = rowItem.get("value");
  237. if (value instanceof List) {
  238. attachmentList = (List<Map>) value;
  239. }
  240. }
  241. }
  242. // ========== 处理 hasInvoice = "否" 的情况(无发票) ==========
  243. if ("否".equals(hasInvoice)) {
  244. // 保存基础报销记录(无发票)
  245. InvoiceLibrary baseInvoice = new InvoiceLibrary();
  246. baseInvoice.setOaId(processInstanceId);
  247. baseInvoice.setSkzh(skzh);
  248. baseInvoice.setBusinessId(businessId);
  249. baseInvoice.setOaStatus("0");
  250. baseInvoice.setInvoiceStatus("0");
  251. baseInvoice.setDep(cdbm != null && StringUtils.isNotBlank(cdbm) ? cdbm : (bm != null ? bm : ""));
  252. baseInvoice.setAccountTitle(lbmx != null ? lbmx : "");
  253. baseInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  254. baseInvoice.setDetailAmount(detailje != null ? detailje : BigDecimal.ZERO);
  255. baseInvoice.setCreatedAt(LocalDateTime.now());
  256. baseInvoice.setUpdatedAt(LocalDateTime.now());
  257. baseInvoice.setInvoiceCode("");
  258. baseInvoice.setInvoiceNumber("");
  259. baseInvoice.setInvoiceType("");
  260. baseInvoice.setAmount(null);
  261. baseInvoice.setTaxAmount(null);
  262. baseInvoice.setTotalAmount(null);
  263. baseInvoice.setBuyerName("");
  264. baseInvoice.setUserId(userId);
  265. baseInvoice.setBuyerTaxId("");
  266. baseInvoice.setSellerName("");
  267. baseInvoice.setSellerTaxId("");
  268. baseInvoice.setFormName("员工费用报销");
  269. baseInvoice.setPaySubject(fkzhxx != null ? fkzhxx : "");
  270. baseInvoice.setPayAccount(fkzh != null ? fkzh : "");
  271. baseInvoice.setBankName(yhqc != null ? yhqc : "");
  272. baseInvoice.setHasInvoice("否");
  273. baseInvoice.setSharedTaxAmount(null);
  274. baseInvoice.setSharedAmount(null);
  275. baseInvoice.setSharedRate(null);
  276. baseInvoice.setNote(bz != null ? bz : "");
  277. noInvoiceList.add(baseInvoice);
  278. log.info("收集无发票记录: 成本部门={}, 费用明细={}", cdbm, lbmx);
  279. continue;
  280. }
  281. // ========== 处理 hasInvoice = "被共享"(被共享发票,提供税率) ==========
  282. if ("被共享".equals(hasInvoice) && attachmentList != null && !attachmentList.isEmpty()) {
  283. // 标记存在被共享发票
  284. hasSharedInvoice = true;
  285. for (Map attachment : attachmentList) {
  286. String fileId = UtilMap.getString(attachment, "fileId");
  287. String fileType = UtilMap.getString(attachment, "fileType");
  288. String fileName = UtilMap.getString(attachment, "fileName");
  289. try {
  290. String filePath = downloadPath + fileId + "." + fileType;
  291. downloadDdFile(processInstanceId, fileId, filePath);
  292. String hz = "qw/files/" + fileId + "." + fileType;
  293. Map fileMap = new HashMap();
  294. fileMap.put("url", url + hz);
  295. fileMap.put("size", new File(filePath).length() / 1024f / 1024f);
  296. fileMap.put("isPdf", "pdf".equalsIgnoreCase(fileType));
  297. McR<Object> result = processMixedInvoice(fileMap);
  298. Object responseData = result.getData();
  299. // 解析发票数据
  300. if (responseData != null) {
  301. String jsonStr = JSONObject.toJSONString(responseData);
  302. JSONObject jsonObject = JSONObject.parseObject(jsonStr);
  303. JSONArray resultArray = jsonObject.getJSONArray("result");
  304. if (resultArray != null && !resultArray.isEmpty()) {
  305. for (int invoiceIndex = 0; invoiceIndex < resultArray.size(); invoiceIndex++) {
  306. JSONObject invoice = resultArray.getJSONObject(invoiceIndex);
  307. String invoiceNumber = invoice.getString("serial") != null ? invoice.getString("serial") : "";
  308. String buyerTaxId = invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "";
  309. String buyerName = invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "";
  310. // ========== 获取税率(供共享发票使用) ==========
  311. Object taxRateObj = invoice.get("taxRate");
  312. Object kind = invoice.getString("kindName");
  313. if (taxRateObj != null) {
  314. String taxRateStr = String.valueOf(taxRateObj);
  315. String taxRateNum = taxRateStr.replace("%", "").trim();
  316. sharedKind = kind != null ? String.valueOf(kind) : "";
  317. sharedTaxRate = new BigDecimal(taxRateNum);
  318. sharedTaxRate = sharedTaxRate.divide(new BigDecimal("100"), 10, BigDecimal.ROUND_HALF_UP);
  319. log.info("从被共享发票获取税率: 原始={}, 转换后={}", taxRateStr, sharedTaxRate);
  320. }
  321. // 保存解析的数据供后续使用
  322. Map<String, Object> parsedData = new HashMap<>();
  323. parsedData.put("invoice", invoice);
  324. parsedData.put("fileId", fileId);
  325. parsedData.put("bz", bz);
  326. parsedData.put("lbmx", lbmx);
  327. parsedData.put("fileName", fileName);
  328. parsedData.put("fileType", fileType);
  329. parsedData.put("cdbm", cdbm);
  330. parsedData.put("hasInvoice", hasInvoice);
  331. parsedData.put("detailje", detailje);
  332. parsedData.put("responseData", responseData);
  333. parsedInvoiceDataList.add(parsedData);
  334. // ========== 校验1:抬头校验 ==========
  335. if (!isTitleValidationExempt(invoice.getString("name")) && StringUtils.isNotBlank(buyerName)) {
  336. CompanyTitle existingTitle = companyTitleMapper.selectByTaxId(buyerName);
  337. if (existingTitle == null) {
  338. String errorInfo = String.format("发票: %s, 购买方名称: %s, 税号: %s",
  339. fileName, buyerName, buyerTaxId);
  340. invalidBuyerTaxIds.add(errorInfo);
  341. log.warn("抬头校验失败: 税号={} 不存在于抬头库, 文件名={}", buyerTaxId, fileName);
  342. }
  343. } else if (!isTitleValidationExempt(invoice.getString("name"))) {
  344. String errorInfo = String.format("发票: %s, 购买方名称为空,无法校验公司抬头", fileName);
  345. invalidBuyerTaxIds.add(errorInfo);
  346. log.warn("抬头校验失败: 购买方名称为空, 文件名={}", fileName);
  347. }
  348. // ========== 校验2:发票号重复校验 ==========
  349. if (StringUtils.isNotBlank(invoiceNumber)) {
  350. InvoiceLibrary existingInvoice = baseMapper.selectByInvoiceNumber(invoiceNumber);
  351. if (existingInvoice != null) {
  352. String errorMsg = String.format("发票号重复: %s (当前文件: %s, 已存在于OA审批单: %s)",
  353. invoiceNumber, fileName, existingInvoice.getOaId());
  354. log.error(errorMsg);
  355. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  356. return McR.error("400", errorMsg);
  357. }
  358. if (invoiceNumberList.contains(invoiceNumber)) {
  359. String errorMsg = String.format("当前审批单内发票号重复: %s (当前文件: %s, 已存在文件: %s)",
  360. invoiceNumber, fileName, invoiceNumberToFileName.get(invoiceNumber));
  361. log.error(errorMsg);
  362. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  363. return McR.error("400", errorMsg);
  364. }
  365. invoiceNumberList.add(invoiceNumber);
  366. invoiceNumberToFileName.put(invoiceNumber, fileName);
  367. }
  368. }
  369. }
  370. }
  371. } catch (Exception e) {
  372. log.error("被共享发票解析异常: fileId={}, fileName={}", fileId, fileName, e);
  373. terminateWorkflow(accessToken, processInstanceId, userId, "被共享发票解析失败: " + e.getMessage());
  374. return McR.error("400", "被共享发票解析失败: " + e.getMessage());
  375. }
  376. }
  377. continue;
  378. }
  379. // ========== 处理 hasInvoice = "是"(有发票,但不共享) ==========
  380. if ("是".equals(hasInvoice) && attachmentList != null && !attachmentList.isEmpty()) {
  381. for (Map attachment : attachmentList) {
  382. String fileId = UtilMap.getString(attachment, "fileId");
  383. String fileType = UtilMap.getString(attachment, "fileType");
  384. String fileName = UtilMap.getString(attachment, "fileName");
  385. try {
  386. String filePath = downloadPath + fileId + "." + fileType;
  387. downloadDdFile(processInstanceId, fileId, filePath);
  388. String hz = "qw/files/" + fileId + "." + fileType;
  389. Map fileMap = new HashMap();
  390. fileMap.put("url", url + hz);
  391. fileMap.put("size", new File(filePath).length() / 1024f / 1024f);
  392. fileMap.put("isPdf", "pdf".equalsIgnoreCase(fileType));
  393. McR<Object> result = processMixedInvoice(fileMap);
  394. Object responseData = result.getData();
  395. if (responseData != null) {
  396. String jsonStr = JSONObject.toJSONString(responseData);
  397. JSONObject jsonObject = JSONObject.parseObject(jsonStr);
  398. JSONArray resultArray = jsonObject.getJSONArray("result");
  399. if (resultArray != null && !resultArray.isEmpty()) {
  400. for (int invoiceIndex = 0; invoiceIndex < resultArray.size(); invoiceIndex++) {
  401. JSONObject invoice = resultArray.getJSONObject(invoiceIndex);
  402. String invoiceNumber = invoice.getString("serial") != null ? invoice.getString("serial") : "";
  403. String buyerTaxId = invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "";
  404. String buyerName = invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "";
  405. Map<String, Object> parsedData = new HashMap<>();
  406. parsedData.put("invoice", invoice);
  407. parsedData.put("fileId", fileId);
  408. parsedData.put("bz", bz);
  409. parsedData.put("lbmx", lbmx);
  410. parsedData.put("fileName", fileName);
  411. parsedData.put("fileType", fileType);
  412. parsedData.put("cdbm", cdbm);
  413. parsedData.put("hasInvoice", hasInvoice);
  414. parsedData.put("detailje", detailje);
  415. parsedData.put("responseData", responseData);
  416. parsedInvoiceDataList.add(parsedData);
  417. // 抬头校验
  418. if (!isTitleValidationExempt(invoice.getString("name")) && StringUtils.isNotBlank(buyerName)) {
  419. CompanyTitle existingTitle = companyTitleMapper.selectByTaxId(buyerName);
  420. if (existingTitle == null) {
  421. String errorInfo = String.format("发票: %s, 购买方名称: %s, 税号: %s",
  422. fileName, buyerName, buyerTaxId);
  423. invalidBuyerTaxIds.add(errorInfo);
  424. log.warn("抬头校验失败: 税号={} 不存在于抬头库, 文件名={}", buyerTaxId, fileName);
  425. }
  426. } else if (!isTitleValidationExempt(invoice.getString("name"))) {
  427. String errorInfo = String.format("发票: %s, 购买方名称为空,无法校验公司抬头", fileName);
  428. invalidBuyerTaxIds.add(errorInfo);
  429. log.warn("抬头校验失败: 购买方名称为空, 文件名={}", fileName);
  430. }
  431. // 发票号重复校验
  432. if (StringUtils.isNotBlank(invoiceNumber)) {
  433. InvoiceLibrary existingInvoice = baseMapper.selectByInvoiceNumber(invoiceNumber);
  434. if (existingInvoice != null) {
  435. String errorMsg = String.format("发票号重复: %s (当前文件: %s, 已存在于OA审批单: %s)",
  436. invoiceNumber, fileName, existingInvoice.getOaId());
  437. log.error(errorMsg);
  438. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  439. return McR.error("400", errorMsg);
  440. }
  441. if (invoiceNumberList.contains(invoiceNumber)) {
  442. String errorMsg = String.format("当前审批单内发票号重复: %s (当前文件: %s, 已存在文件: %s)",
  443. invoiceNumber, fileName, invoiceNumberToFileName.get(invoiceNumber));
  444. log.error(errorMsg);
  445. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  446. return McR.error("400", errorMsg);
  447. }
  448. invoiceNumberList.add(invoiceNumber);
  449. invoiceNumberToFileName.put(invoiceNumber, fileName);
  450. }
  451. }
  452. }
  453. }
  454. } catch (Exception e) {
  455. log.error("发票解析异常: fileId={}, fileName={}", fileId, fileName, e);
  456. terminateWorkflow(accessToken, processInstanceId, userId, "发票解析失败: " + e.getMessage());
  457. return McR.error("400", "发票解析失败: " + e.getMessage());
  458. }
  459. }
  460. continue;
  461. }
  462. // ========== 处理 hasInvoice = "共享"(共享发票,需要从被共享发票获取税率) ==========
  463. if ("共享".equals(hasInvoice)) {
  464. if (detailje != null && detailje.compareTo(BigDecimal.ZERO) > 0) {
  465. Map<String, Object> sharedData = new HashMap<>();
  466. sharedData.put("detailje", detailje);
  467. sharedData.put("lbmx", lbmx);
  468. sharedData.put("bz", bz);
  469. sharedData.put("cdbm", cdbm);
  470. sharedData.put("hasInvoice", hasInvoice);
  471. sharedInvoiceDataList.add(sharedData);
  472. log.info("收集共享发票数据: 金额={}, 成本部门={}", detailje, cdbm);
  473. }
  474. }
  475. }
  476. // ========== 检查:如果存在共享发票但没有被共享发票 ==========
  477. if (!sharedInvoiceDataList.isEmpty() && !hasSharedInvoice) {
  478. String errorMsg = "存在'共享'发票,但未找到'被共享'发票,无法获取税率信息,请确保至少有一行选择'被共享'并上传发票附件";
  479. log.error(errorMsg);
  480. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  481. return McR.error("400", errorMsg);
  482. }
  483. // ========== 抬头校验失败处理 ==========
  484. if (!invalidBuyerTaxIds.isEmpty()) {
  485. String errorMsg = "发票抬头有误,以下发票的购买方税号不在公司抬头库中:\n" + String.join("\n", invalidBuyerTaxIds);
  486. log.error(errorMsg);
  487. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  488. return McR.error("400", errorMsg);
  489. }
  490. BigDecimal totalAmount = BigDecimal.ZERO;
  491. // ========== 第二遍遍历:保存发票数据 ==========
  492. // 1. 保存有发票数据("是"和"被共享")
  493. for (Map<String, Object> parsedData : parsedInvoiceDataList) {
  494. JSONObject invoice = (JSONObject) parsedData.get("invoice");
  495. Object responseData = parsedData.get("responseData");
  496. String cdbm = (String) parsedData.get("cdbm");
  497. String lbmx = (String) parsedData.get("lbmx");
  498. String bz = (String) parsedData.get("bz");
  499. String hasInvoice = (String) parsedData.get("hasInvoice");
  500. Object detailjeObj = parsedData.get("detailje");
  501. String detailje = detailjeObj != null ? String.valueOf(detailjeObj) : "";
  502. allResults.add(responseData);
  503. BigDecimal amount = invoice.getBigDecimal("amount");
  504. if (amount != null) {
  505. totalAmount = totalAmount.add(amount);
  506. }
  507. InvoiceLibrary invoiceLibrary = new InvoiceLibrary();
  508. invoiceLibrary.setOaId(processInstanceId);
  509. invoiceLibrary.setSkzh(skzh);
  510. invoiceLibrary.setBusinessId(businessId);
  511. invoiceLibrary.setInvoiceCode(invoice.getString("code") != null ? invoice.getString("code") : "");
  512. invoiceLibrary.setInvoiceNumber(invoice.getString("serial") != null ? invoice.getString("serial") : "");
  513. invoiceLibrary.setInvoiceType(invoice.getString("kindName") != null ? invoice.getString("kindName") : "");
  514. String date = invoice.getString("date");
  515. if (StringUtils.isNotBlank(date)) {
  516. try {
  517. invoiceLibrary.setInvoiceDate(LocalDate.parse(date.trim()));
  518. } catch (Exception e) {
  519. log.warn("日期解析失败: {}", date);
  520. }
  521. }
  522. invoiceLibrary.setAmount(invoice.getBigDecimal("excludingTax") != null ? invoice.getBigDecimal("excludingTax") : BigDecimal.ZERO);
  523. invoiceLibrary.setTaxAmount(invoice.getBigDecimal("tax") != null ? invoice.getBigDecimal("tax") : BigDecimal.ZERO);
  524. invoiceLibrary.setTotalAmount(invoice.getBigDecimal("amount") != null ? invoice.getBigDecimal("amount") : BigDecimal.ZERO);
  525. invoiceLibrary.setBuyerName(invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "");
  526. invoiceLibrary.setBuyerTaxId(invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "");
  527. invoiceLibrary.setSellerName(invoice.getString("sellerName") != null ? invoice.getString("sellerName") : "");
  528. invoiceLibrary.setSellerTaxId(invoice.getString("sellerTaxId") != null ? invoice.getString("sellerTaxId") : "");
  529. invoiceLibrary.setOaStatus("0");
  530. invoiceLibrary.setNote(bz != null ? bz : "");
  531. invoiceLibrary.setFormName("员工费用报销");
  532. invoiceLibrary.setUserId(userId);
  533. invoiceLibrary.setPaySubject(fkzhxx != null ? fkzhxx : "");
  534. invoiceLibrary.setPayAccount(fkzh != null ? fkzh : "");
  535. invoiceLibrary.setBankName(yhqc != null ? yhqc : "");
  536. invoiceLibrary.setInvoiceStatus("0");
  537. invoiceLibrary.setHasInvoice(hasInvoice);
  538. if (StringUtils.isNotBlank(detailje)) {
  539. try {
  540. invoiceLibrary.setDetailAmount(new BigDecimal(detailje));
  541. } catch (NumberFormatException e) {
  542. log.error("detailJe格式转换失败: {}", detailje);
  543. invoiceLibrary.setDetailAmount(null);
  544. }
  545. } else {
  546. invoiceLibrary.setDetailAmount(null);
  547. }
  548. invoiceLibrary.setSharedTaxAmount(null);
  549. invoiceLibrary.setSharedAmount(null);
  550. invoiceLibrary.setSharedRate(null);
  551. invoiceLibrary.setAccountTitle(lbmx != null ? lbmx : "");
  552. invoiceLibrary.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  553. invoiceLibrary.setCreatedAt(LocalDateTime.now());
  554. invoiceLibrary.setUpdatedAt(LocalDateTime.now());
  555. if (cdbm != null && StringUtils.isNotBlank(cdbm)) {
  556. invoiceLibrary.setDep(cdbm);
  557. } else {
  558. invoiceLibrary.setDep(bm != null ? bm : "");
  559. }
  560. invoiceList.add(invoiceLibrary);
  561. }
  562. // 2. 保存共享发票数据(使用从"被共享"发票获取的税率)
  563. if (!sharedInvoiceDataList.isEmpty()) {
  564. // 检查税率是否有效
  565. if (sharedTaxRate.compareTo(BigDecimal.ZERO) == 0) {
  566. log.warn("共享发票税率未获取到,使用默认税率0");
  567. }
  568. for (Map<String, Object> sharedData : sharedInvoiceDataList) {
  569. Object detailjeObj = sharedData.get("detailje");
  570. String cdbm = (String) sharedData.get("cdbm");
  571. String lbmx = (String) sharedData.get("lbmx");
  572. String bz = (String) sharedData.get("bz");
  573. String hasInvoice = (String) sharedData.get("hasInvoice");
  574. String detailje = detailjeObj != null ? String.valueOf(detailjeObj) : "";
  575. try {
  576. BigDecimal detailAmount = new BigDecimal(detailje);
  577. // 使用从"被共享"发票解析到的税率
  578. BigDecimal ocrTaxRate = sharedTaxRate;
  579. // 计算金额: 含税金额 / (1 + 税率) = 不含税金额
  580. BigDecimal divisor = BigDecimal.ONE.add(ocrTaxRate);
  581. BigDecimal sharedTaxAmount = detailAmount.divide(divisor, 2, BigDecimal.ROUND_HALF_UP);
  582. BigDecimal sharedAmount = detailAmount.subtract(sharedTaxAmount);
  583. BigDecimal sharedRate = ocrTaxRate;
  584. log.info("共享发票计算: 含税金额={}, 税率={}%, 不含税金额={}, 税额={}",
  585. detailAmount, ocrTaxRate.multiply(new BigDecimal("100")),
  586. sharedAmount, sharedTaxAmount);
  587. InvoiceLibrary sharedInvoice = new InvoiceLibrary();
  588. sharedInvoice.setOaId(processInstanceId);
  589. sharedInvoice.setSkzh(skzh);
  590. sharedInvoice.setBusinessId(businessId);
  591. sharedInvoice.setOaStatus("0");
  592. sharedInvoice.setInvoiceStatus("0");
  593. sharedInvoice.setDep(cdbm != null && StringUtils.isNotBlank(cdbm) ? cdbm : (bm != null ? bm : ""));
  594. sharedInvoice.setAccountTitle(lbmx != null ? lbmx : "");
  595. sharedInvoice.setNote(bz != null ? bz : "");
  596. sharedInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  597. sharedInvoice.setAmount(null);
  598. sharedInvoice.setTaxAmount(null);
  599. sharedInvoice.setTotalAmount(null);
  600. sharedInvoice.setUserId(userId);
  601. sharedInvoice.setFormName("员工费用报销");
  602. sharedInvoice.setPaySubject(fkzhxx != null ? fkzhxx : "");
  603. sharedInvoice.setPayAccount(fkzh != null ? fkzh : "");
  604. sharedInvoice.setBankName(yhqc != null ? yhqc : "");
  605. sharedInvoice.setHasInvoice(hasInvoice);
  606. sharedInvoice.setDetailAmount(detailAmount);
  607. sharedInvoice.setSharedTaxAmount(sharedAmount);
  608. sharedInvoice.setSharedAmount(sharedTaxAmount);
  609. sharedInvoice.setSharedRate(String.valueOf(sharedRate));
  610. sharedInvoice.setCreatedAt(LocalDateTime.now());
  611. sharedInvoice.setUpdatedAt(LocalDateTime.now());
  612. sharedInvoice.setInvoiceCode("");
  613. sharedInvoice.setInvoiceNumber("");
  614. sharedInvoice.setInvoiceType(sharedKind);
  615. sharedInvoice.setBuyerName("");
  616. sharedInvoice.setBuyerTaxId("");
  617. sharedInvoice.setSellerName("");
  618. sharedInvoice.setSellerTaxId("");
  619. invoiceList.add(sharedInvoice);
  620. log.info("成功保存共享发票记录,成本部门: {}", cdbm);
  621. } catch (NumberFormatException e) {
  622. log.error("共享发票金额格式转换失败: detailje={}", detailje, e);
  623. } catch (Exception e) {
  624. log.error("共享发票处理失败", e);
  625. }
  626. }
  627. }
  628. // 保存所有记录(无发票 + 有发票)
  629. if (!noInvoiceList.isEmpty()) {
  630. for (InvoiceLibrary invoice : noInvoiceList) {
  631. baseMapper.insert(invoice);
  632. }
  633. log.info("成功保存 {} 条无发票记录", noInvoiceList.size());
  634. addWorkflowComment(processInstanceId, userId, "报销总金额:" + mainJe);
  635. }
  636. if (!invoiceList.isEmpty()) {
  637. for (InvoiceLibrary invoice : invoiceList) {
  638. baseMapper.insert(invoice);
  639. }
  640. log.info("成功保存 {} 条发票记录(有发票: {}, 共享发票: {})",
  641. invoiceList.size(),
  642. parsedInvoiceDataList.stream().filter(d -> "是".equals(d.get("hasInvoice")) || "被共享".equals(d.get("hasInvoice"))).count(),
  643. sharedInvoiceDataList.size());
  644. addWorkflowComment1(processInstanceId, userId, totalAmount);
  645. }
  646. if (!allResults.isEmpty()) {
  647. return McR.success(allResults);
  648. } else {
  649. return McR.success();
  650. }
  651. } catch (Exception e) {
  652. log.error("解析子表数据失败", e);
  653. return McR.error("300", "解析数据失败: " + e.getMessage());
  654. }
  655. }
  656. }
  657. return McR.success();
  658. } catch (Exception e) {
  659. log.error("处理审批实例失败", e);
  660. return McR.error("500", "系统处理失败: " + e.getMessage());
  661. }
  662. }
  663. /**
  664. * 终止钉钉审批流程
  665. *
  666. * @param accessToken 钉钉accessToken
  667. * @param processInstanceId 审批实例ID
  668. * @param operatingUserId 操作人用户ID(通常是发起人ID)
  669. * @param remark 终止原因备注
  670. */
  671. private void terminateWorkflow(String accessToken, String processInstanceId, String operatingUserId, String remark) {
  672. // 异步延迟15秒后执行终止
  673. CompletableFuture.runAsync(() -> {
  674. try {
  675. log.info("延迟15秒后开始终止审批流程: processInstanceId={}, remark={}", processInstanceId, remark);
  676. Thread.sleep(15000);
  677. // 实际执行终止操作
  678. doTerminateWorkflow(accessToken, processInstanceId, operatingUserId, remark);
  679. } catch (InterruptedException e) {
  680. Thread.currentThread().interrupt();
  681. log.error("延迟终止流程被中断: processInstanceId={}", processInstanceId, e);
  682. } catch (Exception e) {
  683. log.error("异步执行终止流程异常: processInstanceId={}", processInstanceId, e);
  684. }
  685. });
  686. }
  687. /**
  688. * 实际执行钉钉审批流程终止
  689. */
  690. private void doTerminateWorkflow(String accessToken, String processInstanceId, String operatingUserId, String remark) {
  691. try {
  692. log.info("开始调用钉钉API终止审批流程: processInstanceId={}", processInstanceId);
  693. // 构建请求URL
  694. String url = "https://api.dingtalk.com/v1.0/workflow/processInstances/terminate";
  695. // 构建请求头
  696. HttpHeaders headers = new HttpHeaders();
  697. headers.setContentType(MediaType.APPLICATION_JSON);
  698. headers.set("x-acs-dingtalk-access-token", accessToken);
  699. // 构建请求体
  700. Map<String, Object> requestBody = new HashMap<>();
  701. requestBody.put("processInstanceId", processInstanceId);
  702. requestBody.put("isSystem", true); // 系统调用终止
  703. requestBody.put("remark", remark);
  704. requestBody.put("operatingUserId", operatingUserId);
  705. log.info("钉钉终止请求参数: {}", requestBody);
  706. // 发送POST请求
  707. HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);
  708. RestTemplate restTemplate = new RestTemplate();
  709. ResponseEntity<String> response = restTemplate.postForEntity(url, requestEntity, String.class);
  710. if (response.getStatusCode().is2xxSuccessful()) {
  711. log.info("审批流程终止成功: processInstanceId={}, response={}", processInstanceId, response.getBody());
  712. } else {
  713. log.error("审批流程终止失败: processInstanceId={}, statusCode={}, response={}",
  714. processInstanceId, response.getStatusCode(), response.getBody());
  715. }
  716. } catch (Exception e) {
  717. log.error("调用钉钉终止流程接口异常: processInstanceId={}, error={}", processInstanceId, e.getMessage(), e);
  718. }
  719. }
  720. @Override
  721. public void updateOaStatusByOaId(Map map) {
  722. try {
  723. String oaId = UtilMap.getString(map, "oaId");
  724. // 创建更新条件
  725. LambdaUpdateWrapper<InvoiceLibrary> updateWrapper = new LambdaUpdateWrapper<>();
  726. updateWrapper.eq(InvoiceLibrary::getOaId, oaId)
  727. .set(InvoiceLibrary::getOaStatus, "1")
  728. .set(InvoiceLibrary::getUpdatedAt, LocalDateTime.now());
  729. int updateCount = baseMapper.update(null, updateWrapper);
  730. if (updateCount > 0) {
  731. log.info("更新成功: 共更新{}条记录", updateCount);
  732. } else {
  733. log.warn("未找到符合条件的记录: oaId={}", oaId);
  734. }
  735. } catch (Exception e) {
  736. throw new RuntimeException("更新OA状态失败", e);
  737. }
  738. }
  739. @Override
  740. public McR invoiceLibrarys(Map map) {
  741. log.info("接收到的参数: {}", map);
  742. try {
  743. String processInstanceId = UtilMap.getString(map, "processInstanceId");
  744. if (StringUtils.isBlank(processInstanceId)) {
  745. log.error("processInstanceId为空");
  746. return McR.error("400", "审批实例ID不能为空");
  747. }
  748. // 获取审批实例信息
  749. String accessToken = ddClient.getAccessToken();
  750. Map processInstance = ddClientWorkflow.getProcessInstanceId(accessToken, processInstanceId);
  751. if (processInstance == null) {
  752. log.error("获取审批实例失败: {}", processInstanceId);
  753. return McR.error("500", "获取审批实例信息失败");
  754. }
  755. String userId = (String) processInstance.get("originatorUserId");
  756. log.info("审批人ID: {}", userId);
  757. List<Map> formComponentValues = (List<Map>) processInstance.get("formComponentValues");
  758. if (formComponentValues == null || formComponentValues.isEmpty()) {
  759. log.warn("表单数据为空");
  760. return McR.error("400", "表单数据为空");
  761. }
  762. // 存储主表的字段值
  763. String zt = null;
  764. BigDecimal mainJe = null;
  765. String bxlb = null;
  766. String bm = null;
  767. // 遍历收集主表字段值
  768. for (Map formComponentValue : formComponentValues) {
  769. String id = String.valueOf(formComponentValue.get("id"));
  770. Object value = formComponentValue.get("value");
  771. if ("DDSelectField_4SITXLYUEO80".equals(id)) {
  772. zt = value != null ? String.valueOf(value) : "";
  773. }
  774. if ("MoneyField_3QZLY8BD3780".equals(id)) {
  775. if (value instanceof BigDecimal) {
  776. mainJe = (BigDecimal) value;
  777. } else if (value != null) {
  778. try {
  779. mainJe = new BigDecimal(String.valueOf(value));
  780. } catch (NumberFormatException e) {
  781. log.error("金额格式转换失败: {}", value);
  782. mainJe = BigDecimal.ZERO;
  783. }
  784. }
  785. }
  786. if ("DDSelectField_1TP75OVCPAAO0".equals(id)) {
  787. bxlb = value != null ? String.valueOf(value) : "";
  788. }
  789. if ("DepartmentField_OKBSJN0MD6O0".equals(id)) {
  790. bm = value != null ? String.valueOf(value) : "";
  791. }
  792. }
  793. // 判断主表状态 - 无发票
  794. if ("否".equals(zt)) {
  795. Map<String, Object> result = new HashMap<>();
  796. result.put("je", mainJe);
  797. result.put("status", "否");
  798. result.put("message", "无发票报销");
  799. // 保存基础报销记录
  800. InvoiceLibrary baseInvoice = new InvoiceLibrary();
  801. baseInvoice.setOaId(processInstanceId);
  802. baseInvoice.setOaStatus("0");
  803. baseInvoice.setInvoiceStatus("0");
  804. baseInvoice.setDep(bm != null ? bm : "");
  805. baseInvoice.setAccountTitle(bxlb != null ? bxlb : "");
  806. baseInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  807. baseInvoice.setCreatedAt(LocalDateTime.now());
  808. baseInvoice.setUpdatedAt(LocalDateTime.now());
  809. baseInvoice.setInvoiceCode("");
  810. baseInvoice.setInvoiceNumber("");
  811. baseInvoice.setInvoiceType("");
  812. baseInvoice.setAmount(BigDecimal.ZERO);
  813. baseInvoice.setTaxAmount(BigDecimal.ZERO);
  814. baseInvoice.setTotalAmount(BigDecimal.ZERO);
  815. baseInvoice.setBuyerName("");
  816. baseInvoice.setBuyerTaxId("");
  817. baseInvoice.setSellerName("");
  818. baseInvoice.setSellerTaxId("");
  819. baseMapper.insert(baseInvoice);
  820. addWorkflowComment(processInstanceId, userId, result);
  821. return McR.success(result);
  822. }
  823. // 判断主表状态 - 有发票
  824. if ("是".equals(zt)) {
  825. for (Map formComponentValue : formComponentValues) {
  826. String id = String.valueOf(formComponentValue.get("id"));
  827. if ("TableField_1A1CDMEN8DDS0".equals(id)) {
  828. String tableFieldValue = String.valueOf(formComponentValue.get("value"));
  829. log.info("子表数据: {}", tableFieldValue);
  830. try {
  831. List<Map> tableRows = (List<Map>) JSONObject.parse(tableFieldValue);
  832. if (tableRows == null || tableRows.isEmpty()) {
  833. log.warn("子表数据为空");
  834. return McR.error("400", "发票明细为空");
  835. }
  836. List<Object> allResults = new ArrayList<>();
  837. List<InvoiceLibrary> invoiceList = new ArrayList<>();
  838. for (Map row : tableRows) {
  839. List<Map> rowValues = (List<Map>) row.get("rowValue");
  840. String hasInvoice = null;
  841. List<Map> attachmentList = null;
  842. for (Map rowItem : rowValues) {
  843. String key = String.valueOf(rowItem.get("key"));
  844. if ("DDSelectField_1ORUK0KIM5D6O".equals(key)) {
  845. Object value = rowItem.get("value");
  846. hasInvoice = value != null ? String.valueOf(value) : "";
  847. }
  848. if ("DDAttachment_Z02OGR5QL8U8".equals(key)) {
  849. Object value = rowItem.get("value");
  850. if (value instanceof List) {
  851. attachmentList = (List<Map>) value;
  852. }
  853. }
  854. }
  855. if ("是".equals(hasInvoice) && attachmentList != null && !attachmentList.isEmpty()) {
  856. for (Map attachment : attachmentList) {
  857. String fileId = UtilMap.getString(attachment, "fileId");
  858. String fileType = UtilMap.getString(attachment, "fileType");
  859. String fileName = UtilMap.getString(attachment, "fileName");
  860. log.info("处理发票: fileName={}, fileId={}", fileName, fileId);
  861. try {
  862. String filePath = downloadPath + fileId + "." + fileType;
  863. downloadDdFile(processInstanceId, fileId, filePath);
  864. String hz = "qw/files/" + fileId + "." + fileType;
  865. Map fileMap = new HashMap();
  866. fileMap.put("url", url + hz);
  867. fileMap.put("size", new File(filePath).length() / 1024f / 1024f);
  868. fileMap.put("isPdf", "pdf".equalsIgnoreCase(fileType));
  869. McR<Object> result = processMixedInvoice(fileMap);
  870. Object responseData = result.getData();
  871. allResults.add(responseData);
  872. // 解析发票数据 - responseData 是对象,不是数组
  873. if (responseData != null) {
  874. try {
  875. String jsonStr = JSONObject.toJSONString(responseData);
  876. JSONObject jsonObject = JSONObject.parseObject(jsonStr);
  877. // 获取 result 数组
  878. JSONArray resultArray = jsonObject.getJSONArray("result");
  879. if (resultArray != null && !resultArray.isEmpty()) {
  880. JSONObject invoice = resultArray.getJSONObject(0);
  881. InvoiceLibrary invoiceLibrary = new InvoiceLibrary();
  882. invoiceLibrary.setOaId(processInstanceId);
  883. invoiceLibrary.setInvoiceCode(invoice.getString("code") != null ? invoice.getString("code") : "");
  884. invoiceLibrary.setInvoiceNumber(invoice.getString("serial") != null ? invoice.getString("serial") : "");
  885. invoiceLibrary.setInvoiceType(invoice.getString("kindName") != null ? invoice.getString("kindName") : "");
  886. String date = invoice.getString("date");
  887. if (StringUtils.isNotBlank(date)) {
  888. try {
  889. invoiceLibrary.setInvoiceDate(LocalDate.parse(date.trim()));
  890. } catch (Exception e) {
  891. log.warn("日期解析失败: {}", date);
  892. }
  893. }
  894. invoiceLibrary.setAmount(invoice.getBigDecimal("excludingTax") != null ? invoice.getBigDecimal("excludingTax") : BigDecimal.ZERO);
  895. invoiceLibrary.setTaxAmount(invoice.getBigDecimal("tax") != null ? invoice.getBigDecimal("tax") : BigDecimal.ZERO);
  896. invoiceLibrary.setTotalAmount(invoice.getBigDecimal("amount") != null ? invoice.getBigDecimal("amount") : BigDecimal.ZERO);
  897. invoiceLibrary.setBuyerName(invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "");
  898. invoiceLibrary.setBuyerTaxId(invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "");
  899. invoiceLibrary.setSellerName(invoice.getString("sellerName") != null ? invoice.getString("sellerName") : "");
  900. invoiceLibrary.setSellerTaxId(invoice.getString("sellerTaxId") != null ? invoice.getString("sellerTaxId") : "");
  901. invoiceLibrary.setOaStatus("0");
  902. invoiceLibrary.setInvoiceStatus("0");
  903. invoiceLibrary.setDep(bm != null ? bm : "");
  904. invoiceLibrary.setAccountTitle(bxlb != null ? bxlb : "");
  905. invoiceLibrary.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  906. invoiceLibrary.setCreatedAt(LocalDateTime.now());
  907. invoiceLibrary.setUpdatedAt(LocalDateTime.now());
  908. invoiceList.add(invoiceLibrary);
  909. }
  910. } catch (Exception e) {
  911. log.error("解析发票数据失败", e);
  912. }
  913. }
  914. } catch (Exception e) {
  915. log.error("处理发票失败: fileId={}, fileName={}", fileId, fileName, e);
  916. }
  917. }
  918. }
  919. }
  920. if (!invoiceList.isEmpty()) {
  921. for (InvoiceLibrary invoice : invoiceList) {
  922. baseMapper.insert(invoice);
  923. }
  924. log.info("成功保存 {} 条发票记录", invoiceList.size());
  925. }
  926. addWorkflowComment(processInstanceId, userId, allResults);
  927. return McR.success(allResults);
  928. } catch (Exception e) {
  929. log.error("解析子表数据失败", e);
  930. return McR.error("300", "解析数据失败: " + e.getMessage());
  931. }
  932. }
  933. }
  934. }
  935. return McR.success();
  936. } catch (Exception e) {
  937. log.error("处理审批实例失败", e);
  938. return McR.error("500", "系统处理失败: " + e.getMessage());
  939. }
  940. }
  941. @Override
  942. public McR deleteAllByOaId(String oaId) {
  943. try {
  944. // 创建删除条件
  945. LambdaQueryWrapper<InvoiceLibrary> queryWrapper = new LambdaQueryWrapper<>();
  946. queryWrapper.eq(InvoiceLibrary::getOaId, oaId);
  947. // 执行删除
  948. int deleteCount = baseMapper.delete(queryWrapper);
  949. if (deleteCount > 0) {
  950. return McR.success("删除成功,共删除 " + deleteCount + " 条记录");
  951. } else {
  952. return McR.error("404", "未找到 oa_id 为 " + oaId + " 的记录");
  953. }
  954. } catch (Exception e) {
  955. log.error("删除失败", e);
  956. return McR.error("500", "删除失败: " + e.getMessage());
  957. }
  958. }
  959. @Override
  960. public McR invoiceLibrary1(Map map) {
  961. log.info("接收到的参数: {}", map);
  962. try {
  963. String processInstanceId = UtilMap.getString(map, "processInstanceId");
  964. if (StringUtils.isBlank(processInstanceId)) {
  965. log.error("processInstanceId为空");
  966. return McR.error("400", "审批实例ID不能为空");
  967. }
  968. // 获取审批实例信息
  969. String accessToken = ddClient.getAccessToken();
  970. Map processInstance = ddClientWorkflow.getProcessInstanceId(accessToken, processInstanceId);
  971. if (processInstance == null) {
  972. log.error("获取审批实例失败: {}", processInstanceId);
  973. return McR.error("500", "获取审批实例信息失败");
  974. }
  975. String userId = (String) processInstance.get("originatorUserId");
  976. String businessId = (String) processInstance.get("businessId");
  977. log.info("审批人ID: {}", userId);
  978. List<Map> formComponentValues = (List<Map>) processInstance.get("formComponentValues");
  979. if (formComponentValues == null || formComponentValues.isEmpty()) {
  980. log.warn("表单数据为空");
  981. return McR.error("400", "表单数据为空");
  982. }
  983. // 存储主表的字段值
  984. String bm = null;
  985. String bxlb = null;
  986. String fkzhxx = null;
  987. String fkzh = null;
  988. String yhqc = null;
  989. String skzh = null;
  990. BigDecimal mainJe = null;
  991. // 遍历收集主表字段值
  992. for (Map formComponentValue : formComponentValues) {
  993. String id = String.valueOf(formComponentValue.get("id"));
  994. Object value = formComponentValue.get("value");
  995. if ("MoneyField_1FVEBDQBSQ680".equals(id)) {
  996. if (value instanceof BigDecimal) {
  997. mainJe = (BigDecimal) value;
  998. } else if (value != null) {
  999. try {
  1000. mainJe = new BigDecimal(String.valueOf(value));
  1001. } catch (NumberFormatException e) {
  1002. log.error("金额格式转换失败: {}", value);
  1003. mainJe = BigDecimal.ZERO;
  1004. }
  1005. }
  1006. }
  1007. if ("RecipientAccountField_1U3QZJI8H6RK0".equals(id)) {
  1008. skzh = value != null ? String.valueOf(value) : "";
  1009. }
  1010. if ("DDSelectField_YLLB7AD1ATS0".equals(id)) {
  1011. bxlb = value != null ? String.valueOf(value) : "";
  1012. }
  1013. if ("DepartmentField_RF93A6VHA9C0".equals(id)) {
  1014. bm = value != null ? String.valueOf(value) : "";
  1015. }
  1016. if ("DDSelectField_23RVAEKUM5UO0".equals(id)) {
  1017. fkzhxx = value != null ? String.valueOf(value) : "";
  1018. }
  1019. if ("TextField_1TE3AOHRMD4W0".equals(id)) {
  1020. fkzh = value != null ? String.valueOf(value) : "";
  1021. }
  1022. if ("TextField_EN2MDZIMUTK0".equals(id)) {
  1023. yhqc = value != null ? String.valueOf(value) : "";
  1024. }
  1025. }
  1026. // 遍历子表数据
  1027. for (Map formComponentValue : formComponentValues) {
  1028. String id = String.valueOf(formComponentValue.get("id"));
  1029. if ("TableField_1VKP90DW7WKG0".equals(id)) {
  1030. String tableFieldValue = String.valueOf(formComponentValue.get("value"));
  1031. log.info("子表数据: {}", tableFieldValue);
  1032. try {
  1033. List<Map> tableRows = (List<Map>) JSONObject.parse(tableFieldValue);
  1034. if (tableRows == null || tableRows.isEmpty()) {
  1035. log.warn("子表数据为空");
  1036. return McR.error("400", "发票明细为空");
  1037. }
  1038. // 用于存储无发票记录和发票记录
  1039. List<InvoiceLibrary> noInvoiceList = new ArrayList<>();
  1040. List<InvoiceLibrary> invoiceList = new ArrayList<>();
  1041. List<Object> allResults = new ArrayList<>();
  1042. // 存储有发票("是"和"被共享")的解析数据
  1043. List<Map<String, Object>> parsedInvoiceDataList = new ArrayList<>();
  1044. // 存储共享发票数据
  1045. List<Map<String, Object>> sharedInvoiceDataList = new ArrayList<>();
  1046. // 用于校验的集合
  1047. List<String> invoiceNumberList = new ArrayList<>();
  1048. Map<String, String> invoiceNumberToFileName = new HashMap<>();
  1049. List<String> invalidBuyerTaxIds = new ArrayList<>();
  1050. // 存储税率信息(从"被共享"发票中获取)
  1051. BigDecimal sharedTaxRate = BigDecimal.ZERO;
  1052. String sharedKind = "";
  1053. boolean hasSharedInvoice = false; // 标记是否存在"被共享"发票
  1054. // ========== 第一遍遍历:收集数据并查找"被共享"发票 ==========
  1055. for (Map row : tableRows) {
  1056. List<Map> rowValues = (List<Map>) row.get("rowValue");
  1057. String hasInvoice = null;
  1058. String cdbm = null;
  1059. String lbmx = null;
  1060. String bz = null;
  1061. BigDecimal detailje = null;
  1062. List<Map> attachmentList = null;
  1063. for (Map rowItem : rowValues) {
  1064. String key = String.valueOf(rowItem.get("key"));
  1065. if ("DDSelectField_20ZGDOIRHB400".equals(key)) {
  1066. Object value = rowItem.get("value");
  1067. hasInvoice = value != null ? String.valueOf(value) : "";
  1068. }
  1069. if ("DDSelectField_BVQPDAKTMO74".equals(key)) {
  1070. Object value = rowItem.get("value");
  1071. lbmx = value != null ? String.valueOf(value) : "";
  1072. }
  1073. if ("TextareaField_MO34ZZVGJHC".equals(key)) {
  1074. Object value = rowItem.get("value");
  1075. bz = value != null ? String.valueOf(value) : "";
  1076. }
  1077. if ("DepartmentField_1VQ3M98PC7SW0".equals(key)) {
  1078. Object value = rowItem.get("value");
  1079. cdbm = value != null ? String.valueOf(value) : "";
  1080. }
  1081. if ("NumberField_BLX4BRW8GEW0".equals(key)) {
  1082. Object value = rowItem.get("value");
  1083. if (value instanceof BigDecimal) {
  1084. detailje = (BigDecimal) value;
  1085. } else if (value != null) {
  1086. try {
  1087. detailje = new BigDecimal(String.valueOf(value));
  1088. } catch (NumberFormatException e) {
  1089. log.error("金额格式转换失败: {}", value);
  1090. detailje = BigDecimal.ZERO;
  1091. }
  1092. } else {
  1093. detailje = BigDecimal.ZERO;
  1094. }
  1095. }
  1096. if ("DDAttachment_1YQ3DD7BH4BK0".equals(key)) {
  1097. Object value = rowItem.get("value");
  1098. if (value instanceof List) {
  1099. attachmentList = (List<Map>) value;
  1100. }
  1101. }
  1102. }
  1103. // ========== 处理 hasInvoice = "否" 的情况(无发票) ==========
  1104. if ("否".equals(hasInvoice)) {
  1105. // 保存基础报销记录(无发票)
  1106. InvoiceLibrary baseInvoice = new InvoiceLibrary();
  1107. baseInvoice.setOaId(processInstanceId);
  1108. baseInvoice.setSkzh(skzh);
  1109. baseInvoice.setBusinessId(businessId);
  1110. baseInvoice.setOaStatus("0");
  1111. baseInvoice.setInvoiceStatus("0");
  1112. baseInvoice.setDep(cdbm != null && StringUtils.isNotBlank(cdbm) ? cdbm : (bm != null ? bm : ""));
  1113. baseInvoice.setAccountTitle(lbmx != null ? lbmx : "");
  1114. baseInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  1115. baseInvoice.setDetailAmount(detailje != null ? detailje : BigDecimal.ZERO);
  1116. baseInvoice.setCreatedAt(LocalDateTime.now());
  1117. baseInvoice.setUpdatedAt(LocalDateTime.now());
  1118. baseInvoice.setInvoiceCode("");
  1119. baseInvoice.setInvoiceNumber("");
  1120. baseInvoice.setInvoiceType("");
  1121. baseInvoice.setAmount(null);
  1122. baseInvoice.setTaxAmount(null);
  1123. baseInvoice.setTotalAmount(null);
  1124. baseInvoice.setBuyerName("");
  1125. baseInvoice.setBuyerTaxId("");
  1126. baseInvoice.setSellerName("");
  1127. baseInvoice.setSellerTaxId("");
  1128. baseInvoice.setUserId(userId);
  1129. baseInvoice.setFormName("付款申请");
  1130. baseInvoice.setPaySubject(fkzhxx != null ? fkzhxx : "");
  1131. baseInvoice.setPayAccount(fkzh != null ? fkzh : "");
  1132. baseInvoice.setBankName(yhqc != null ? yhqc : "");
  1133. baseInvoice.setHasInvoice("否");
  1134. baseInvoice.setSharedTaxAmount(null);
  1135. baseInvoice.setSharedAmount(null);
  1136. baseInvoice.setSharedRate(null);
  1137. baseInvoice.setNote(bz != null ? bz : "");
  1138. noInvoiceList.add(baseInvoice);
  1139. log.info("收集无发票记录: 成本部门={}, 费用明细={}", cdbm, lbmx);
  1140. continue;
  1141. }
  1142. // ========== 处理 hasInvoice = "被共享"(被共享发票,提供税率) ==========
  1143. if ("被共享".equals(hasInvoice) && attachmentList != null && !attachmentList.isEmpty()) {
  1144. // 标记存在被共享发票
  1145. hasSharedInvoice = true;
  1146. for (Map attachment : attachmentList) {
  1147. String fileId = UtilMap.getString(attachment, "fileId");
  1148. String fileType = UtilMap.getString(attachment, "fileType");
  1149. String fileName = UtilMap.getString(attachment, "fileName");
  1150. try {
  1151. String filePath = downloadPath + fileId + "." + fileType;
  1152. downloadDdFile(processInstanceId, fileId, filePath);
  1153. String hz = "qw/files/" + fileId + "." + fileType;
  1154. Map fileMap = new HashMap();
  1155. fileMap.put("url", url + hz);
  1156. fileMap.put("size", new File(filePath).length() / 1024f / 1024f);
  1157. fileMap.put("isPdf", "pdf".equalsIgnoreCase(fileType));
  1158. McR<Object> result = processMixedInvoice(fileMap);
  1159. Object responseData = result.getData();
  1160. // 解析发票数据
  1161. if (responseData != null) {
  1162. String jsonStr = JSONObject.toJSONString(responseData);
  1163. JSONObject jsonObject = JSONObject.parseObject(jsonStr);
  1164. JSONArray resultArray = jsonObject.getJSONArray("result");
  1165. if (resultArray != null && !resultArray.isEmpty()) {
  1166. for (int invoiceIndex = 0; invoiceIndex < resultArray.size(); invoiceIndex++) {
  1167. JSONObject invoice = resultArray.getJSONObject(invoiceIndex);
  1168. String invoiceNumber = invoice.getString("serial") != null ? invoice.getString("serial") : "";
  1169. String buyerTaxId = invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "";
  1170. String buyerName = invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "";
  1171. // ========== 获取税率(供共享发票使用) ==========
  1172. Object taxRateObj = invoice.get("taxRate");
  1173. Object kind = invoice.getString("kindName");
  1174. if (taxRateObj != null) {
  1175. String taxRateStr = String.valueOf(taxRateObj);
  1176. String taxRateNum = taxRateStr.replace("%", "").trim();
  1177. sharedKind = kind != null ? String.valueOf(kind) : "";
  1178. sharedTaxRate = new BigDecimal(taxRateNum);
  1179. sharedTaxRate = sharedTaxRate.divide(new BigDecimal("100"), 10, BigDecimal.ROUND_HALF_UP);
  1180. log.info("从被共享发票获取税率: 原始={}, 转换后={}", taxRateStr, sharedTaxRate);
  1181. }
  1182. // 保存解析的数据供后续使用
  1183. Map<String, Object> parsedData = new HashMap<>();
  1184. parsedData.put("invoice", invoice);
  1185. parsedData.put("fileId", fileId);
  1186. parsedData.put("bz", bz);
  1187. parsedData.put("lbmx", lbmx);
  1188. parsedData.put("fileName", fileName);
  1189. parsedData.put("fileType", fileType);
  1190. parsedData.put("cdbm", cdbm);
  1191. parsedData.put("hasInvoice", hasInvoice);
  1192. parsedData.put("detailje", detailje);
  1193. parsedData.put("responseData", responseData);
  1194. parsedInvoiceDataList.add(parsedData);
  1195. // ========== 校验1:抬头校验 ==========
  1196. if (!isTitleValidationExempt(invoice.getString("name")) && StringUtils.isNotBlank(buyerName)) {
  1197. CompanyTitle existingTitle = companyTitleMapper.selectByTaxId(buyerName);
  1198. if (existingTitle == null) {
  1199. String errorInfo = String.format("发票: %s, 购买方名称: %s, 税号: %s",
  1200. fileName, buyerName, buyerTaxId);
  1201. invalidBuyerTaxIds.add(errorInfo);
  1202. log.warn("抬头校验失败: 税号={} 不存在于抬头库, 文件名={}", buyerTaxId, fileName);
  1203. }
  1204. } else if (!isTitleValidationExempt(invoice.getString("name"))) {
  1205. String errorInfo = String.format("发票: %s, 购买方名称为空,无法校验公司抬头", fileName);
  1206. invalidBuyerTaxIds.add(errorInfo);
  1207. log.warn("抬头校验失败: 购买方名称为空, 文件名={}", fileName);
  1208. }
  1209. // ========== 校验2:发票号重复校验 ==========
  1210. if (StringUtils.isNotBlank(invoiceNumber)) {
  1211. InvoiceLibrary existingInvoice = baseMapper.selectByInvoiceNumber(invoiceNumber);
  1212. if (existingInvoice != null) {
  1213. String errorMsg = String.format("发票号重复: %s (当前文件: %s, 已存在于OA审批单: %s)",
  1214. invoiceNumber, fileName, existingInvoice.getOaId());
  1215. log.error(errorMsg);
  1216. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  1217. return McR.error("400", errorMsg);
  1218. }
  1219. if (invoiceNumberList.contains(invoiceNumber)) {
  1220. String errorMsg = String.format("当前审批单内发票号重复: %s (当前文件: %s, 已存在文件: %s)",
  1221. invoiceNumber, fileName, invoiceNumberToFileName.get(invoiceNumber));
  1222. log.error(errorMsg);
  1223. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  1224. return McR.error("400", errorMsg);
  1225. }
  1226. invoiceNumberList.add(invoiceNumber);
  1227. invoiceNumberToFileName.put(invoiceNumber, fileName);
  1228. }
  1229. }
  1230. }
  1231. }
  1232. } catch (Exception e) {
  1233. log.error("被共享发票解析异常: fileId={}, fileName={}", fileId, fileName, e);
  1234. terminateWorkflow(accessToken, processInstanceId, userId, "被共享发票解析失败: " + e.getMessage());
  1235. return McR.error("400", "被共享发票解析失败: " + e.getMessage());
  1236. }
  1237. }
  1238. continue;
  1239. }
  1240. // ========== 处理 hasInvoice = "是"(有发票,但不共享) ==========
  1241. if ("是".equals(hasInvoice) && attachmentList != null && !attachmentList.isEmpty()) {
  1242. for (Map attachment : attachmentList) {
  1243. String fileId = UtilMap.getString(attachment, "fileId");
  1244. String fileType = UtilMap.getString(attachment, "fileType");
  1245. String fileName = UtilMap.getString(attachment, "fileName");
  1246. try {
  1247. String filePath = downloadPath + fileId + "." + fileType;
  1248. downloadDdFile(processInstanceId, fileId, filePath);
  1249. String hz = "qw/files/" + fileId + "." + fileType;
  1250. Map fileMap = new HashMap();
  1251. fileMap.put("url", url + hz);
  1252. fileMap.put("size", new File(filePath).length() / 1024f / 1024f);
  1253. fileMap.put("isPdf", "pdf".equalsIgnoreCase(fileType));
  1254. McR<Object> result = processMixedInvoice(fileMap);
  1255. Object responseData = result.getData();
  1256. if (responseData != null) {
  1257. String jsonStr = JSONObject.toJSONString(responseData);
  1258. JSONObject jsonObject = JSONObject.parseObject(jsonStr);
  1259. JSONArray resultArray = jsonObject.getJSONArray("result");
  1260. if (resultArray != null && !resultArray.isEmpty()) {
  1261. for (int invoiceIndex = 0; invoiceIndex < resultArray.size(); invoiceIndex++) {
  1262. JSONObject invoice = resultArray.getJSONObject(invoiceIndex);
  1263. String invoiceNumber = invoice.getString("serial") != null ? invoice.getString("serial") : "";
  1264. String buyerTaxId = invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "";
  1265. String buyerName = invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "";
  1266. Map<String, Object> parsedData = new HashMap<>();
  1267. parsedData.put("invoice", invoice);
  1268. parsedData.put("fileId", fileId);
  1269. parsedData.put("bz", bz);
  1270. parsedData.put("lbmx", lbmx);
  1271. parsedData.put("fileName", fileName);
  1272. parsedData.put("fileType", fileType);
  1273. parsedData.put("cdbm", cdbm);
  1274. parsedData.put("hasInvoice", hasInvoice);
  1275. parsedData.put("detailje", detailje);
  1276. parsedData.put("responseData", responseData);
  1277. parsedInvoiceDataList.add(parsedData);
  1278. // 抬头校验
  1279. if (!isTitleValidationExempt(invoice.getString("name")) && StringUtils.isNotBlank(buyerName)) {
  1280. CompanyTitle existingTitle = companyTitleMapper.selectByTaxId(buyerName);
  1281. if (existingTitle == null) {
  1282. String errorInfo = String.format("发票: %s, 购买方名称: %s, 税号: %s",
  1283. fileName, buyerName, buyerTaxId);
  1284. invalidBuyerTaxIds.add(errorInfo);
  1285. log.warn("抬头校验失败: 税号={} 不存在于抬头库, 文件名={}", buyerTaxId, fileName);
  1286. }
  1287. } else if (!isTitleValidationExempt(invoice.getString("name"))) {
  1288. String errorInfo = String.format("发票: %s, 购买方名称为空,无法校验公司抬头", fileName);
  1289. invalidBuyerTaxIds.add(errorInfo);
  1290. log.warn("抬头校验失败: 购买方名称为空, 文件名={}", fileName);
  1291. }
  1292. // 发票号重复校验
  1293. if (StringUtils.isNotBlank(invoiceNumber)) {
  1294. InvoiceLibrary existingInvoice = baseMapper.selectByInvoiceNumber(invoiceNumber);
  1295. if (existingInvoice != null) {
  1296. String errorMsg = String.format("发票号重复: %s (当前文件: %s, 已存在于OA审批单: %s)",
  1297. invoiceNumber, fileName, existingInvoice.getOaId());
  1298. log.error(errorMsg);
  1299. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  1300. return McR.error("400", errorMsg);
  1301. }
  1302. if (invoiceNumberList.contains(invoiceNumber)) {
  1303. String errorMsg = String.format("当前审批单内发票号重复: %s (当前文件: %s, 已存在文件: %s)",
  1304. invoiceNumber, fileName, invoiceNumberToFileName.get(invoiceNumber));
  1305. log.error(errorMsg);
  1306. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  1307. return McR.error("400", errorMsg);
  1308. }
  1309. invoiceNumberList.add(invoiceNumber);
  1310. invoiceNumberToFileName.put(invoiceNumber, fileName);
  1311. }
  1312. }
  1313. }
  1314. }
  1315. } catch (Exception e) {
  1316. log.error("发票解析异常: fileId={}, fileName={}", fileId, fileName, e);
  1317. terminateWorkflow(accessToken, processInstanceId, userId, "发票解析失败: " + e.getMessage());
  1318. return McR.error("400", "发票解析失败: " + e.getMessage());
  1319. }
  1320. }
  1321. continue;
  1322. }
  1323. // ========== 处理 hasInvoice = "共享"(共享发票,需要从被共享发票获取税率) ==========
  1324. if ("共享".equals(hasInvoice)) {
  1325. if (detailje != null && detailje.compareTo(BigDecimal.ZERO) > 0) {
  1326. Map<String, Object> sharedData = new HashMap<>();
  1327. sharedData.put("detailje", detailje);
  1328. sharedData.put("lbmx", lbmx);
  1329. sharedData.put("bz", bz);
  1330. sharedData.put("cdbm", cdbm);
  1331. sharedData.put("hasInvoice", hasInvoice);
  1332. sharedInvoiceDataList.add(sharedData);
  1333. log.info("收集共享发票数据: 金额={}, 成本部门={}", detailje, cdbm);
  1334. }
  1335. }
  1336. }
  1337. // ========== 检查:如果存在共享发票但没有被共享发票 ==========
  1338. if (!sharedInvoiceDataList.isEmpty() && !hasSharedInvoice) {
  1339. String errorMsg = "存在'共享'发票,但未找到'被共享'发票,无法获取税率信息,请确保至少有一行选择'被共享'并上传发票附件";
  1340. log.error(errorMsg);
  1341. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  1342. return McR.error("400", errorMsg);
  1343. }
  1344. // ========== 抬头校验失败处理 ==========
  1345. if (!invalidBuyerTaxIds.isEmpty()) {
  1346. String errorMsg = "发票抬头有误,以下发票的购买方税号不在公司抬头库中:\n" + String.join("\n", invalidBuyerTaxIds);
  1347. log.error(errorMsg);
  1348. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  1349. return McR.error("400", errorMsg);
  1350. }
  1351. BigDecimal totalAmount = BigDecimal.ZERO;
  1352. // ========== 第二遍遍历:保存发票数据 ==========
  1353. // 1. 保存有发票数据("是"和"被共享")
  1354. for (Map<String, Object> parsedData : parsedInvoiceDataList) {
  1355. JSONObject invoice = (JSONObject) parsedData.get("invoice");
  1356. Object responseData = parsedData.get("responseData");
  1357. String cdbm = (String) parsedData.get("cdbm");
  1358. String lbmx = (String) parsedData.get("lbmx");
  1359. String bz = (String) parsedData.get("bz");
  1360. String hasInvoice = (String) parsedData.get("hasInvoice");
  1361. Object detailjeObj = parsedData.get("detailje");
  1362. String detailje = detailjeObj != null ? String.valueOf(detailjeObj) : "";
  1363. allResults.add(responseData);
  1364. BigDecimal amount = invoice.getBigDecimal("amount");
  1365. if (amount != null) {
  1366. totalAmount = totalAmount.add(amount);
  1367. }
  1368. InvoiceLibrary invoiceLibrary = new InvoiceLibrary();
  1369. invoiceLibrary.setOaId(processInstanceId);
  1370. invoiceLibrary.setSkzh(skzh);
  1371. invoiceLibrary.setBusinessId(businessId);
  1372. invoiceLibrary.setInvoiceCode(invoice.getString("code") != null ? invoice.getString("code") : "");
  1373. invoiceLibrary.setInvoiceNumber(invoice.getString("serial") != null ? invoice.getString("serial") : "");
  1374. invoiceLibrary.setInvoiceType(invoice.getString("kindName") != null ? invoice.getString("kindName") : "");
  1375. String date = invoice.getString("date");
  1376. if (StringUtils.isNotBlank(date)) {
  1377. try {
  1378. invoiceLibrary.setInvoiceDate(LocalDate.parse(date.trim()));
  1379. } catch (Exception e) {
  1380. log.warn("日期解析失败: {}", date);
  1381. }
  1382. }
  1383. invoiceLibrary.setAmount(invoice.getBigDecimal("excludingTax") != null ? invoice.getBigDecimal("excludingTax") : BigDecimal.ZERO);
  1384. invoiceLibrary.setTaxAmount(invoice.getBigDecimal("tax") != null ? invoice.getBigDecimal("tax") : BigDecimal.ZERO);
  1385. invoiceLibrary.setTotalAmount(invoice.getBigDecimal("amount") != null ? invoice.getBigDecimal("amount") : BigDecimal.ZERO);
  1386. invoiceLibrary.setBuyerName(invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "");
  1387. invoiceLibrary.setBuyerTaxId(invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "");
  1388. invoiceLibrary.setSellerName(invoice.getString("sellerName") != null ? invoice.getString("sellerName") : "");
  1389. invoiceLibrary.setSellerTaxId(invoice.getString("sellerTaxId") != null ? invoice.getString("sellerTaxId") : "");
  1390. invoiceLibrary.setOaStatus("0");
  1391. invoiceLibrary.setUserId(userId);
  1392. invoiceLibrary.setNote(bz != null ? bz : "");
  1393. invoiceLibrary.setFormName("付款申请");
  1394. invoiceLibrary.setPaySubject(fkzhxx != null ? fkzhxx : "");
  1395. invoiceLibrary.setPayAccount(fkzh != null ? fkzh : "");
  1396. invoiceLibrary.setBankName(yhqc != null ? yhqc : "");
  1397. invoiceLibrary.setInvoiceStatus("0");
  1398. invoiceLibrary.setHasInvoice(hasInvoice);
  1399. if (StringUtils.isNotBlank(detailje)) {
  1400. try {
  1401. invoiceLibrary.setDetailAmount(new BigDecimal(detailje));
  1402. } catch (NumberFormatException e) {
  1403. log.error("detailJe格式转换失败: {}", detailje);
  1404. invoiceLibrary.setDetailAmount(null);
  1405. }
  1406. } else {
  1407. invoiceLibrary.setDetailAmount(null);
  1408. }
  1409. invoiceLibrary.setSharedTaxAmount(null);
  1410. invoiceLibrary.setSharedAmount(null);
  1411. invoiceLibrary.setSharedRate(null);
  1412. invoiceLibrary.setAccountTitle(lbmx != null ? lbmx : "");
  1413. invoiceLibrary.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  1414. invoiceLibrary.setCreatedAt(LocalDateTime.now());
  1415. invoiceLibrary.setUpdatedAt(LocalDateTime.now());
  1416. if (cdbm != null && StringUtils.isNotBlank(cdbm)) {
  1417. invoiceLibrary.setDep(cdbm);
  1418. } else {
  1419. invoiceLibrary.setDep(bm != null ? bm : "");
  1420. }
  1421. invoiceList.add(invoiceLibrary);
  1422. }
  1423. // 2. 保存共享发票数据(使用从"被共享"发票获取的税率)
  1424. if (!sharedInvoiceDataList.isEmpty()) {
  1425. // 检查税率是否有效
  1426. if (sharedTaxRate.compareTo(BigDecimal.ZERO) == 0) {
  1427. log.warn("共享发票税率未获取到,使用默认税率0");
  1428. }
  1429. for (Map<String, Object> sharedData : sharedInvoiceDataList) {
  1430. Object detailjeObj = sharedData.get("detailje");
  1431. String cdbm = (String) sharedData.get("cdbm");
  1432. String lbmx = (String) sharedData.get("lbmx");
  1433. String bz = (String) sharedData.get("bz");
  1434. String hasInvoice = (String) sharedData.get("hasInvoice");
  1435. String detailje = detailjeObj != null ? String.valueOf(detailjeObj) : "";
  1436. try {
  1437. BigDecimal detailAmount = new BigDecimal(detailje);
  1438. // 使用从"被共享"发票解析到的税率
  1439. BigDecimal ocrTaxRate = sharedTaxRate;
  1440. // 计算金额: 含税金额 / (1 + 税率) = 不含税金额
  1441. BigDecimal divisor = BigDecimal.ONE.add(ocrTaxRate);
  1442. BigDecimal sharedTaxAmount = detailAmount.divide(divisor, 2, BigDecimal.ROUND_HALF_UP);
  1443. BigDecimal sharedAmount = detailAmount.subtract(sharedTaxAmount);
  1444. BigDecimal sharedRate = ocrTaxRate;
  1445. log.info("共享发票计算: 含税金额={}, 税率={}%, 不含税金额={}, 税额={}",
  1446. detailAmount, ocrTaxRate.multiply(new BigDecimal("100")),
  1447. sharedAmount, sharedTaxAmount);
  1448. InvoiceLibrary sharedInvoice = new InvoiceLibrary();
  1449. sharedInvoice.setOaId(processInstanceId);
  1450. sharedInvoice.setSkzh(skzh);
  1451. sharedInvoice.setBusinessId(businessId);
  1452. sharedInvoice.setOaStatus("0");
  1453. sharedInvoice.setInvoiceStatus("0");
  1454. sharedInvoice.setDep(cdbm != null && StringUtils.isNotBlank(cdbm) ? cdbm : (bm != null ? bm : ""));
  1455. sharedInvoice.setAccountTitle(lbmx != null ? lbmx : "");
  1456. sharedInvoice.setNote(bz != null ? bz : "");
  1457. sharedInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  1458. sharedInvoice.setAmount(null);
  1459. sharedInvoice.setTaxAmount(null);
  1460. sharedInvoice.setTotalAmount(null);
  1461. sharedInvoice.setUserId(userId);
  1462. sharedInvoice.setFormName("付款申请");
  1463. sharedInvoice.setPaySubject(fkzhxx != null ? fkzhxx : "");
  1464. sharedInvoice.setPayAccount(fkzh != null ? fkzh : "");
  1465. sharedInvoice.setBankName(yhqc != null ? yhqc : "");
  1466. sharedInvoice.setHasInvoice(hasInvoice);
  1467. sharedInvoice.setDetailAmount(detailAmount);
  1468. sharedInvoice.setSharedTaxAmount(sharedAmount);
  1469. sharedInvoice.setSharedAmount(sharedTaxAmount);
  1470. sharedInvoice.setSharedRate(String.valueOf(sharedRate));
  1471. sharedInvoice.setCreatedAt(LocalDateTime.now());
  1472. sharedInvoice.setUpdatedAt(LocalDateTime.now());
  1473. sharedInvoice.setInvoiceCode("");
  1474. sharedInvoice.setInvoiceNumber("");
  1475. sharedInvoice.setInvoiceType(sharedKind);
  1476. sharedInvoice.setBuyerName("");
  1477. sharedInvoice.setBuyerTaxId("");
  1478. sharedInvoice.setSellerName("");
  1479. sharedInvoice.setSellerTaxId("");
  1480. invoiceList.add(sharedInvoice);
  1481. log.info("成功保存共享发票记录,成本部门: {}", cdbm);
  1482. } catch (NumberFormatException e) {
  1483. log.error("共享发票金额格式转换失败: detailje={}", detailje, e);
  1484. } catch (Exception e) {
  1485. log.error("共享发票处理失败", e);
  1486. }
  1487. }
  1488. }
  1489. // 保存所有记录(无发票 + 有发票)
  1490. if (!noInvoiceList.isEmpty()) {
  1491. for (InvoiceLibrary invoice : noInvoiceList) {
  1492. baseMapper.insert(invoice);
  1493. }
  1494. log.info("成功保存 {} 条无发票记录", noInvoiceList.size());
  1495. addWorkflowComment(processInstanceId, userId, "报销总金额:" + mainJe);
  1496. }
  1497. if (!invoiceList.isEmpty()) {
  1498. for (InvoiceLibrary invoice : invoiceList) {
  1499. baseMapper.insert(invoice);
  1500. }
  1501. log.info("成功保存 {} 条发票记录(有发票: {}, 共享发票: {})",
  1502. invoiceList.size(),
  1503. parsedInvoiceDataList.stream().filter(d -> "是".equals(d.get("hasInvoice")) || "被共享".equals(d.get("hasInvoice"))).count(),
  1504. sharedInvoiceDataList.size());
  1505. addWorkflowComment1(processInstanceId, userId, totalAmount);
  1506. }
  1507. if (!allResults.isEmpty()) {
  1508. return McR.success(allResults);
  1509. } else {
  1510. return McR.success();
  1511. }
  1512. } catch (Exception e) {
  1513. log.error("解析子表数据失败", e);
  1514. return McR.error("300", "解析数据失败: " + e.getMessage());
  1515. }
  1516. }
  1517. }
  1518. return McR.success();
  1519. } catch (Exception e) {
  1520. log.error("处理审批实例失败", e);
  1521. return McR.error("500", "系统处理失败: " + e.getMessage());
  1522. }
  1523. }
  1524. @Override
  1525. public McR invoiceLibrary2(Map map) {
  1526. log.info("接收到的参数: {}", map);
  1527. try {
  1528. String processInstanceId = UtilMap.getString(map, "processInstanceId");
  1529. if (StringUtils.isBlank(processInstanceId)) {
  1530. log.error("processInstanceId为空");
  1531. return McR.error("400", "审批实例ID不能为空");
  1532. }
  1533. // 获取审批实例信息
  1534. String accessToken = ddClient.getAccessToken();
  1535. Map processInstance = ddClientWorkflow.getProcessInstanceId(accessToken, processInstanceId);
  1536. if (processInstance == null) {
  1537. log.error("获取审批实例失败: {}", processInstanceId);
  1538. return McR.error("500", "获取审批实例信息失败");
  1539. }
  1540. String userId = (String) processInstance.get("originatorUserId");
  1541. String businessId = (String) processInstance.get("businessId");
  1542. log.info("审批人ID: {}", userId);
  1543. List<Map> formComponentValues = (List<Map>) processInstance.get("formComponentValues");
  1544. if (formComponentValues == null || formComponentValues.isEmpty()) {
  1545. log.warn("表单数据为空");
  1546. return McR.error("400", "表单数据为空");
  1547. }
  1548. // 存储主表的字段值
  1549. String bm = null;
  1550. String bxlb = null;
  1551. String fkzhxx = null;
  1552. String fkzh = null;
  1553. String yhqc = null;
  1554. String skzh = null;
  1555. BigDecimal mainJe = null;
  1556. // 遍历收集主表字段值
  1557. for (Map formComponentValue : formComponentValues) {
  1558. String id = String.valueOf(formComponentValue.get("id"));
  1559. Object value = formComponentValue.get("value");
  1560. if ("MoneyField_1V4A9P72K6TC0".equals(id)) {
  1561. if (value instanceof BigDecimal) {
  1562. mainJe = (BigDecimal) value;
  1563. } else if (value != null) {
  1564. try {
  1565. mainJe = new BigDecimal(String.valueOf(value));
  1566. } catch (NumberFormatException e) {
  1567. log.error("金额格式转换失败: {}", value);
  1568. mainJe = BigDecimal.ZERO;
  1569. }
  1570. }
  1571. }
  1572. if ("DDSelectField_YLLB7AD1ATS0".equals(id)) {
  1573. bxlb = value != null ? String.valueOf(value) : "";
  1574. }
  1575. if ("DepartmentField_ZI9KALEYOTC0".equals(id)) {
  1576. bm = value != null ? String.valueOf(value) : "";
  1577. }
  1578. if ("DDSelectField_1WEX2WWCJ3PC0".equals(id)) {
  1579. fkzhxx = value != null ? String.valueOf(value) : "";
  1580. }
  1581. if ("RecipientAccountField_13CHMZZOTK4G0".equals(id)) {
  1582. skzh = value != null ? String.valueOf(value) : "";
  1583. }
  1584. if ("TextField_2W39FPCZH1A0".equals(id)) {
  1585. fkzh = value != null ? String.valueOf(value) : "";
  1586. }
  1587. if ("TextField_QW7HJIR7T800".equals(id)) {
  1588. yhqc = value != null ? String.valueOf(value) : "";
  1589. }
  1590. }
  1591. // 遍历子表数据
  1592. for (Map formComponentValue : formComponentValues) {
  1593. String id = String.valueOf(formComponentValue.get("id"));
  1594. if ("TableField_182UNZEX02U80".equals(id)) {
  1595. String tableFieldValue = String.valueOf(formComponentValue.get("value"));
  1596. log.info("子表数据: {}", tableFieldValue);
  1597. try {
  1598. List<Map> tableRows = (List<Map>) JSONObject.parse(tableFieldValue);
  1599. if (tableRows == null || tableRows.isEmpty()) {
  1600. log.warn("子表数据为空");
  1601. return McR.error("400", "发票明细为空");
  1602. }
  1603. // 用于存储无发票记录和发票记录
  1604. List<InvoiceLibrary> noInvoiceList = new ArrayList<>();
  1605. List<InvoiceLibrary> invoiceList = new ArrayList<>();
  1606. List<Object> allResults = new ArrayList<>();
  1607. // 存储有发票("是"和"被共享")的解析数据
  1608. List<Map<String, Object>> parsedInvoiceDataList = new ArrayList<>();
  1609. // 存储共享发票数据
  1610. List<Map<String, Object>> sharedInvoiceDataList = new ArrayList<>();
  1611. // 用于校验的集合
  1612. List<String> invoiceNumberList = new ArrayList<>();
  1613. Map<String, String> invoiceNumberToFileName = new HashMap<>();
  1614. List<String> invalidBuyerTaxIds = new ArrayList<>();
  1615. // 存储税率信息(从"被共享"发票中获取)
  1616. BigDecimal sharedTaxRate = BigDecimal.ZERO;
  1617. String sharedKind = "";
  1618. boolean hasSharedInvoice = false; // 标记是否存在"被共享"发票
  1619. // ========== 第一遍遍历:收集数据并查找"被共享"发票 ==========
  1620. for (Map row : tableRows) {
  1621. List<Map> rowValues = (List<Map>) row.get("rowValue");
  1622. String hasInvoice = null;
  1623. String cdbm = null;
  1624. String lbmx = null;
  1625. String bz = null;
  1626. BigDecimal detailje = null;
  1627. List<Map> attachmentList = null;
  1628. for (Map rowItem : rowValues) {
  1629. String key = String.valueOf(rowItem.get("key"));
  1630. if ("DDSelectField_44I0SP6M1I00".equals(key)) {
  1631. Object value = rowItem.get("value");
  1632. hasInvoice = value != null ? String.valueOf(value) : "";
  1633. }
  1634. if ("DDSelectField_20HL7OBLLB4W0".equals(key)) {
  1635. Object value = rowItem.get("value");
  1636. lbmx = value != null ? String.valueOf(value) : "";
  1637. }
  1638. if ("TextField_1YUNETYYW9XC0".equals(key)) {
  1639. Object value = rowItem.get("value");
  1640. bz = value != null ? String.valueOf(value) : "";
  1641. }
  1642. if ("DepartmentField_RC2YZRJQAQ80".equals(key)) {
  1643. Object value = rowItem.get("value");
  1644. cdbm = value != null ? String.valueOf(value) : "";
  1645. }
  1646. if ("NumberField_1UC3HZ8OEIPS0".equals(key)) {
  1647. Object value = rowItem.get("value");
  1648. if (value instanceof BigDecimal) {
  1649. detailje = (BigDecimal) value;
  1650. } else if (value != null) {
  1651. try {
  1652. detailje = new BigDecimal(String.valueOf(value));
  1653. } catch (NumberFormatException e) {
  1654. log.error("金额格式转换失败: {}", value);
  1655. detailje = BigDecimal.ZERO;
  1656. }
  1657. } else {
  1658. detailje = BigDecimal.ZERO;
  1659. }
  1660. }
  1661. if ("DDAttachment_LEUWYIC68TC0".equals(key)) {
  1662. Object value = rowItem.get("value");
  1663. if (value instanceof List) {
  1664. attachmentList = (List<Map>) value;
  1665. }
  1666. }
  1667. }
  1668. // ========== 处理 hasInvoice = "否" 的情况(无发票) ==========
  1669. if ("否".equals(hasInvoice)) {
  1670. // 保存基础报销记录(无发票)
  1671. InvoiceLibrary baseInvoice = new InvoiceLibrary();
  1672. baseInvoice.setOaId(processInstanceId);
  1673. baseInvoice.setSkzh(skzh);
  1674. baseInvoice.setBusinessId(businessId);
  1675. baseInvoice.setOaStatus("0");
  1676. baseInvoice.setInvoiceStatus("0");
  1677. baseInvoice.setDep(cdbm != null && StringUtils.isNotBlank(cdbm) ? cdbm : (bm != null ? bm : ""));
  1678. baseInvoice.setAccountTitle(lbmx != null ? lbmx : "");
  1679. baseInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  1680. baseInvoice.setDetailAmount(detailje != null ? detailje : BigDecimal.ZERO);
  1681. baseInvoice.setCreatedAt(LocalDateTime.now());
  1682. baseInvoice.setUpdatedAt(LocalDateTime.now());
  1683. baseInvoice.setInvoiceCode("");
  1684. baseInvoice.setInvoiceNumber("");
  1685. baseInvoice.setInvoiceType("");
  1686. baseInvoice.setAmount(null);
  1687. baseInvoice.setTaxAmount(null);
  1688. baseInvoice.setTotalAmount(null);
  1689. baseInvoice.setBuyerName("");
  1690. baseInvoice.setUserId(userId);
  1691. baseInvoice.setBuyerTaxId("");
  1692. baseInvoice.setSellerName("");
  1693. baseInvoice.setSellerTaxId("");
  1694. baseInvoice.setFormName("合作商付款申请");
  1695. baseInvoice.setPaySubject(fkzhxx != null ? fkzhxx : "");
  1696. baseInvoice.setPayAccount(fkzh != null ? fkzh : "");
  1697. baseInvoice.setBankName(yhqc != null ? yhqc : "");
  1698. baseInvoice.setHasInvoice("否");
  1699. baseInvoice.setSharedTaxAmount(null);
  1700. baseInvoice.setSharedAmount(null);
  1701. baseInvoice.setSharedRate(null);
  1702. baseInvoice.setNote(bz != null ? bz : "");
  1703. noInvoiceList.add(baseInvoice);
  1704. log.info("收集无发票记录: 成本部门={}, 费用明细={}", cdbm, lbmx);
  1705. continue;
  1706. }
  1707. // ========== 处理 hasInvoice = "被共享"(被共享发票,提供税率) ==========
  1708. if ("被共享".equals(hasInvoice) && attachmentList != null && !attachmentList.isEmpty()) {
  1709. // 标记存在被共享发票
  1710. hasSharedInvoice = true;
  1711. for (Map attachment : attachmentList) {
  1712. String fileId = UtilMap.getString(attachment, "fileId");
  1713. String fileType = UtilMap.getString(attachment, "fileType");
  1714. String fileName = UtilMap.getString(attachment, "fileName");
  1715. try {
  1716. String filePath = downloadPath + fileId + "." + fileType;
  1717. downloadDdFile(processInstanceId, fileId, filePath);
  1718. String hz = "qw/files/" + fileId + "." + fileType;
  1719. Map fileMap = new HashMap();
  1720. fileMap.put("url", url + hz);
  1721. fileMap.put("size", new File(filePath).length() / 1024f / 1024f);
  1722. fileMap.put("isPdf", "pdf".equalsIgnoreCase(fileType));
  1723. McR<Object> result = processMixedInvoice(fileMap);
  1724. Object responseData = result.getData();
  1725. // 解析发票数据
  1726. if (responseData != null) {
  1727. String jsonStr = JSONObject.toJSONString(responseData);
  1728. JSONObject jsonObject = JSONObject.parseObject(jsonStr);
  1729. JSONArray resultArray = jsonObject.getJSONArray("result");
  1730. if (resultArray != null && !resultArray.isEmpty()) {
  1731. for (int invoiceIndex = 0; invoiceIndex < resultArray.size(); invoiceIndex++) {
  1732. JSONObject invoice = resultArray.getJSONObject(invoiceIndex);
  1733. String invoiceNumber = invoice.getString("serial") != null ? invoice.getString("serial") : "";
  1734. String buyerTaxId = invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "";
  1735. String buyerName = invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "";
  1736. // ========== 获取税率(供共享发票使用) ==========
  1737. Object taxRateObj = invoice.get("taxRate");
  1738. Object kind = invoice.getString("kindName");
  1739. if (taxRateObj != null) {
  1740. String taxRateStr = String.valueOf(taxRateObj);
  1741. String taxRateNum = taxRateStr.replace("%", "").trim();
  1742. sharedKind = kind != null ? String.valueOf(kind) : "";
  1743. sharedTaxRate = new BigDecimal(taxRateNum);
  1744. sharedTaxRate = sharedTaxRate.divide(new BigDecimal("100"), 10, BigDecimal.ROUND_HALF_UP);
  1745. log.info("从被共享发票获取税率: 原始={}, 转换后={}", taxRateStr, sharedTaxRate);
  1746. }
  1747. // 保存解析的数据供后续使用
  1748. Map<String, Object> parsedData = new HashMap<>();
  1749. parsedData.put("invoice", invoice);
  1750. parsedData.put("fileId", fileId);
  1751. parsedData.put("bz", bz);
  1752. parsedData.put("lbmx", lbmx);
  1753. parsedData.put("fileName", fileName);
  1754. parsedData.put("fileType", fileType);
  1755. parsedData.put("cdbm", cdbm);
  1756. parsedData.put("hasInvoice", hasInvoice);
  1757. parsedData.put("detailje", detailje);
  1758. parsedData.put("responseData", responseData);
  1759. parsedInvoiceDataList.add(parsedData);
  1760. // ========== 校验1:抬头校验 ==========
  1761. if (!isTitleValidationExempt(invoice.getString("name")) && StringUtils.isNotBlank(buyerName)) {
  1762. CompanyTitle existingTitle = companyTitleMapper.selectByTaxId(buyerName);
  1763. if (existingTitle == null) {
  1764. String errorInfo = String.format("发票: %s, 购买方名称: %s, 税号: %s",
  1765. fileName, buyerName, buyerTaxId);
  1766. invalidBuyerTaxIds.add(errorInfo);
  1767. log.warn("抬头校验失败: 税号={} 不存在于抬头库, 文件名={}", buyerTaxId, fileName);
  1768. }
  1769. } else if (!isTitleValidationExempt(invoice.getString("name"))) {
  1770. String errorInfo = String.format("发票: %s, 购买方名称为空,无法校验公司抬头", fileName);
  1771. invalidBuyerTaxIds.add(errorInfo);
  1772. log.warn("抬头校验失败: 购买方名称为空, 文件名={}", fileName);
  1773. }
  1774. // ========== 校验2:发票号重复校验 ==========
  1775. if (StringUtils.isNotBlank(invoiceNumber)) {
  1776. InvoiceLibrary existingInvoice = baseMapper.selectByInvoiceNumber(invoiceNumber);
  1777. if (existingInvoice != null) {
  1778. String errorMsg = String.format("发票号重复: %s (当前文件: %s, 已存在于OA审批单: %s)",
  1779. invoiceNumber, fileName, existingInvoice.getOaId());
  1780. log.error(errorMsg);
  1781. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  1782. return McR.error("400", errorMsg);
  1783. }
  1784. if (invoiceNumberList.contains(invoiceNumber)) {
  1785. String errorMsg = String.format("当前审批单内发票号重复: %s (当前文件: %s, 已存在文件: %s)",
  1786. invoiceNumber, fileName, invoiceNumberToFileName.get(invoiceNumber));
  1787. log.error(errorMsg);
  1788. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  1789. return McR.error("400", errorMsg);
  1790. }
  1791. invoiceNumberList.add(invoiceNumber);
  1792. invoiceNumberToFileName.put(invoiceNumber, fileName);
  1793. }
  1794. }
  1795. }
  1796. }
  1797. } catch (Exception e) {
  1798. log.error("被共享发票解析异常: fileId={}, fileName={}", fileId, fileName, e);
  1799. terminateWorkflow(accessToken, processInstanceId, userId, "被共享发票解析失败: " + e.getMessage());
  1800. return McR.error("400", "被共享发票解析失败: " + e.getMessage());
  1801. }
  1802. }
  1803. continue;
  1804. }
  1805. // ========== 处理 hasInvoice = "是"(有发票,但不共享) ==========
  1806. if ("是".equals(hasInvoice) && attachmentList != null && !attachmentList.isEmpty()) {
  1807. for (Map attachment : attachmentList) {
  1808. String fileId = UtilMap.getString(attachment, "fileId");
  1809. String fileType = UtilMap.getString(attachment, "fileType");
  1810. String fileName = UtilMap.getString(attachment, "fileName");
  1811. try {
  1812. String filePath = downloadPath + fileId + "." + fileType;
  1813. downloadDdFile(processInstanceId, fileId, filePath);
  1814. String hz = "qw/files/" + fileId + "." + fileType;
  1815. Map fileMap = new HashMap();
  1816. fileMap.put("url", url + hz);
  1817. fileMap.put("size", new File(filePath).length() / 1024f / 1024f);
  1818. fileMap.put("isPdf", "pdf".equalsIgnoreCase(fileType));
  1819. McR<Object> result = processMixedInvoice(fileMap);
  1820. Object responseData = result.getData();
  1821. if (responseData != null) {
  1822. String jsonStr = JSONObject.toJSONString(responseData);
  1823. JSONObject jsonObject = JSONObject.parseObject(jsonStr);
  1824. JSONArray resultArray = jsonObject.getJSONArray("result");
  1825. if (resultArray != null && !resultArray.isEmpty()) {
  1826. for (int invoiceIndex = 0; invoiceIndex < resultArray.size(); invoiceIndex++) {
  1827. JSONObject invoice = resultArray.getJSONObject(invoiceIndex);
  1828. String invoiceNumber = invoice.getString("serial") != null ? invoice.getString("serial") : "";
  1829. String buyerTaxId = invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "";
  1830. String buyerName = invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "";
  1831. Map<String, Object> parsedData = new HashMap<>();
  1832. parsedData.put("invoice", invoice);
  1833. parsedData.put("fileId", fileId);
  1834. parsedData.put("bz", bz);
  1835. parsedData.put("lbmx", lbmx);
  1836. parsedData.put("fileName", fileName);
  1837. parsedData.put("fileType", fileType);
  1838. parsedData.put("cdbm", cdbm);
  1839. parsedData.put("hasInvoice", hasInvoice);
  1840. parsedData.put("detailje", detailje);
  1841. parsedData.put("responseData", responseData);
  1842. parsedInvoiceDataList.add(parsedData);
  1843. // 抬头校验
  1844. if (!isTitleValidationExempt(invoice.getString("name")) && StringUtils.isNotBlank(buyerName)) {
  1845. CompanyTitle existingTitle = companyTitleMapper.selectByTaxId(buyerName);
  1846. if (existingTitle == null) {
  1847. String errorInfo = String.format("发票: %s, 购买方名称: %s, 税号: %s",
  1848. fileName, buyerName, buyerTaxId);
  1849. invalidBuyerTaxIds.add(errorInfo);
  1850. log.warn("抬头校验失败: 税号={} 不存在于抬头库, 文件名={}", buyerTaxId, fileName);
  1851. }
  1852. } else if (!isTitleValidationExempt(invoice.getString("name"))) {
  1853. String errorInfo = String.format("发票: %s, 购买方名称为空,无法校验公司抬头", fileName);
  1854. invalidBuyerTaxIds.add(errorInfo);
  1855. log.warn("抬头校验失败: 购买方名称为空, 文件名={}", fileName);
  1856. }
  1857. // 发票号重复校验
  1858. if (StringUtils.isNotBlank(invoiceNumber)) {
  1859. InvoiceLibrary existingInvoice = baseMapper.selectByInvoiceNumber(invoiceNumber);
  1860. if (existingInvoice != null) {
  1861. String errorMsg = String.format("发票号重复: %s (当前文件: %s, 已存在于OA审批单: %s)",
  1862. invoiceNumber, fileName, existingInvoice.getOaId());
  1863. log.error(errorMsg);
  1864. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  1865. return McR.error("400", errorMsg);
  1866. }
  1867. if (invoiceNumberList.contains(invoiceNumber)) {
  1868. String errorMsg = String.format("当前审批单内发票号重复: %s (当前文件: %s, 已存在文件: %s)",
  1869. invoiceNumber, fileName, invoiceNumberToFileName.get(invoiceNumber));
  1870. log.error(errorMsg);
  1871. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  1872. return McR.error("400", errorMsg);
  1873. }
  1874. invoiceNumberList.add(invoiceNumber);
  1875. invoiceNumberToFileName.put(invoiceNumber, fileName);
  1876. }
  1877. }
  1878. }
  1879. }
  1880. } catch (Exception e) {
  1881. log.error("发票解析异常: fileId={}, fileName={}", fileId, fileName, e);
  1882. terminateWorkflow(accessToken, processInstanceId, userId, "发票解析失败: " + e.getMessage());
  1883. return McR.error("400", "发票解析失败: " + e.getMessage());
  1884. }
  1885. }
  1886. continue;
  1887. }
  1888. // ========== 处理 hasInvoice = "共享"(共享发票,需要从被共享发票获取税率) ==========
  1889. if ("共享".equals(hasInvoice)) {
  1890. if (detailje != null && detailje.compareTo(BigDecimal.ZERO) > 0) {
  1891. Map<String, Object> sharedData = new HashMap<>();
  1892. sharedData.put("detailje", detailje);
  1893. sharedData.put("lbmx", lbmx);
  1894. sharedData.put("bz", bz);
  1895. sharedData.put("cdbm", cdbm);
  1896. sharedData.put("hasInvoice", hasInvoice);
  1897. sharedInvoiceDataList.add(sharedData);
  1898. log.info("收集共享发票数据: 金额={}, 成本部门={}", detailje, cdbm);
  1899. }
  1900. }
  1901. }
  1902. // ========== 检查:如果存在共享发票但没有被共享发票 ==========
  1903. if (!sharedInvoiceDataList.isEmpty() && !hasSharedInvoice) {
  1904. String errorMsg = "存在'共享'发票,但未找到'被共享'发票,无法获取税率信息,请确保至少有一行选择'被共享'并上传发票附件";
  1905. log.error(errorMsg);
  1906. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  1907. return McR.error("400", errorMsg);
  1908. }
  1909. // ========== 抬头校验失败处理 ==========
  1910. if (!invalidBuyerTaxIds.isEmpty()) {
  1911. String errorMsg = "发票抬头有误,以下发票的购买方税号不在公司抬头库中:\n" + String.join("\n", invalidBuyerTaxIds);
  1912. log.error(errorMsg);
  1913. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  1914. return McR.error("400", errorMsg);
  1915. }
  1916. BigDecimal totalAmount = BigDecimal.ZERO;
  1917. // ========== 第二遍遍历:保存发票数据 ==========
  1918. // 1. 保存有发票数据("是"和"被共享")
  1919. for (Map<String, Object> parsedData : parsedInvoiceDataList) {
  1920. JSONObject invoice = (JSONObject) parsedData.get("invoice");
  1921. Object responseData = parsedData.get("responseData");
  1922. String cdbm = (String) parsedData.get("cdbm");
  1923. String lbmx = (String) parsedData.get("lbmx");
  1924. String bz = (String) parsedData.get("bz");
  1925. String hasInvoice = (String) parsedData.get("hasInvoice");
  1926. Object detailjeObj = parsedData.get("detailje");
  1927. String detailje = detailjeObj != null ? String.valueOf(detailjeObj) : "";
  1928. allResults.add(responseData);
  1929. BigDecimal amount = invoice.getBigDecimal("amount");
  1930. if (amount != null) {
  1931. totalAmount = totalAmount.add(amount);
  1932. }
  1933. InvoiceLibrary invoiceLibrary = new InvoiceLibrary();
  1934. invoiceLibrary.setOaId(processInstanceId);
  1935. invoiceLibrary.setSkzh(skzh);
  1936. invoiceLibrary.setBusinessId(businessId);
  1937. invoiceLibrary.setInvoiceCode(invoice.getString("code") != null ? invoice.getString("code") : "");
  1938. invoiceLibrary.setInvoiceNumber(invoice.getString("serial") != null ? invoice.getString("serial") : "");
  1939. invoiceLibrary.setInvoiceType(invoice.getString("kindName") != null ? invoice.getString("kindName") : "");
  1940. String date = invoice.getString("date");
  1941. if (StringUtils.isNotBlank(date)) {
  1942. try {
  1943. invoiceLibrary.setInvoiceDate(LocalDate.parse(date.trim()));
  1944. } catch (Exception e) {
  1945. log.warn("日期解析失败: {}", date);
  1946. }
  1947. }
  1948. invoiceLibrary.setAmount(invoice.getBigDecimal("excludingTax") != null ? invoice.getBigDecimal("excludingTax") : BigDecimal.ZERO);
  1949. invoiceLibrary.setTaxAmount(invoice.getBigDecimal("tax") != null ? invoice.getBigDecimal("tax") : BigDecimal.ZERO);
  1950. invoiceLibrary.setTotalAmount(invoice.getBigDecimal("amount") != null ? invoice.getBigDecimal("amount") : BigDecimal.ZERO);
  1951. invoiceLibrary.setBuyerName(invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "");
  1952. invoiceLibrary.setBuyerTaxId(invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "");
  1953. invoiceLibrary.setSellerName(invoice.getString("sellerName") != null ? invoice.getString("sellerName") : "");
  1954. invoiceLibrary.setSellerTaxId(invoice.getString("sellerTaxId") != null ? invoice.getString("sellerTaxId") : "");
  1955. invoiceLibrary.setOaStatus("0");
  1956. invoiceLibrary.setUserId(userId);
  1957. invoiceLibrary.setNote(bz != null ? bz : "");
  1958. invoiceLibrary.setFormName("合作商付款申请");
  1959. invoiceLibrary.setPaySubject(fkzhxx != null ? fkzhxx : "");
  1960. invoiceLibrary.setPayAccount(fkzh != null ? fkzh : "");
  1961. invoiceLibrary.setBankName(yhqc != null ? yhqc : "");
  1962. invoiceLibrary.setInvoiceStatus("0");
  1963. invoiceLibrary.setHasInvoice(hasInvoice);
  1964. if (StringUtils.isNotBlank(detailje)) {
  1965. try {
  1966. invoiceLibrary.setDetailAmount(new BigDecimal(detailje));
  1967. } catch (NumberFormatException e) {
  1968. log.error("detailJe格式转换失败: {}", detailje);
  1969. invoiceLibrary.setDetailAmount(null);
  1970. }
  1971. } else {
  1972. invoiceLibrary.setDetailAmount(null);
  1973. }
  1974. invoiceLibrary.setSharedTaxAmount(null);
  1975. invoiceLibrary.setSharedAmount(null);
  1976. invoiceLibrary.setSharedRate(null);
  1977. invoiceLibrary.setAccountTitle(lbmx != null ? lbmx : "");
  1978. invoiceLibrary.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  1979. invoiceLibrary.setCreatedAt(LocalDateTime.now());
  1980. invoiceLibrary.setUpdatedAt(LocalDateTime.now());
  1981. if (cdbm != null && StringUtils.isNotBlank(cdbm)) {
  1982. invoiceLibrary.setDep(cdbm);
  1983. } else {
  1984. invoiceLibrary.setDep(bm != null ? bm : "");
  1985. }
  1986. invoiceList.add(invoiceLibrary);
  1987. }
  1988. // 2. 保存共享发票数据(使用从"被共享"发票获取的税率)
  1989. if (!sharedInvoiceDataList.isEmpty()) {
  1990. // 检查税率是否有效
  1991. if (sharedTaxRate.compareTo(BigDecimal.ZERO) == 0) {
  1992. log.warn("共享发票税率未获取到,使用默认税率0");
  1993. }
  1994. for (Map<String, Object> sharedData : sharedInvoiceDataList) {
  1995. Object detailjeObj = sharedData.get("detailje");
  1996. String cdbm = (String) sharedData.get("cdbm");
  1997. String lbmx = (String) sharedData.get("lbmx");
  1998. String bz = (String) sharedData.get("bz");
  1999. String hasInvoice = (String) sharedData.get("hasInvoice");
  2000. String detailje = detailjeObj != null ? String.valueOf(detailjeObj) : "";
  2001. try {
  2002. BigDecimal detailAmount = new BigDecimal(detailje);
  2003. // 使用从"被共享"发票解析到的税率
  2004. BigDecimal ocrTaxRate = sharedTaxRate;
  2005. // 计算金额: 含税金额 / (1 + 税率) = 不含税金额
  2006. BigDecimal divisor = BigDecimal.ONE.add(ocrTaxRate);
  2007. BigDecimal sharedTaxAmount = detailAmount.divide(divisor, 2, BigDecimal.ROUND_HALF_UP);
  2008. BigDecimal sharedAmount = detailAmount.subtract(sharedTaxAmount);
  2009. BigDecimal sharedRate = ocrTaxRate;
  2010. log.info("共享发票计算: 含税金额={}, 税率={}%, 不含税金额={}, 税额={}",
  2011. detailAmount, ocrTaxRate.multiply(new BigDecimal("100")),
  2012. sharedAmount, sharedTaxAmount);
  2013. InvoiceLibrary sharedInvoice = new InvoiceLibrary();
  2014. sharedInvoice.setOaId(processInstanceId);
  2015. sharedInvoice.setSkzh(skzh);
  2016. sharedInvoice.setBusinessId(businessId);
  2017. sharedInvoice.setOaStatus("0");
  2018. sharedInvoice.setInvoiceStatus("0");
  2019. sharedInvoice.setDep(cdbm != null && StringUtils.isNotBlank(cdbm) ? cdbm : (bm != null ? bm : ""));
  2020. sharedInvoice.setAccountTitle(lbmx != null ? lbmx : "");
  2021. sharedInvoice.setNote(bz != null ? bz : "");
  2022. sharedInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  2023. sharedInvoice.setAmount(null);
  2024. sharedInvoice.setTaxAmount(null);
  2025. sharedInvoice.setTotalAmount(null);
  2026. sharedInvoice.setUserId(userId);
  2027. sharedInvoice.setFormName("合作商付款申请");
  2028. sharedInvoice.setPaySubject(fkzhxx != null ? fkzhxx : "");
  2029. sharedInvoice.setPayAccount(fkzh != null ? fkzh : "");
  2030. sharedInvoice.setBankName(yhqc != null ? yhqc : "");
  2031. sharedInvoice.setHasInvoice(hasInvoice);
  2032. sharedInvoice.setDetailAmount(detailAmount);
  2033. sharedInvoice.setSharedTaxAmount(sharedAmount);
  2034. sharedInvoice.setSharedAmount(sharedTaxAmount);
  2035. sharedInvoice.setSharedRate(String.valueOf(sharedRate));
  2036. sharedInvoice.setCreatedAt(LocalDateTime.now());
  2037. sharedInvoice.setUpdatedAt(LocalDateTime.now());
  2038. sharedInvoice.setInvoiceCode("");
  2039. sharedInvoice.setInvoiceNumber("");
  2040. sharedInvoice.setInvoiceType(sharedKind);
  2041. sharedInvoice.setBuyerName("");
  2042. sharedInvoice.setBuyerTaxId("");
  2043. sharedInvoice.setSellerName("");
  2044. sharedInvoice.setSellerTaxId("");
  2045. invoiceList.add(sharedInvoice);
  2046. log.info("成功保存共享发票记录,成本部门: {}", cdbm);
  2047. } catch (NumberFormatException e) {
  2048. log.error("共享发票金额格式转换失败: detailje={}", detailje, e);
  2049. } catch (Exception e) {
  2050. log.error("共享发票处理失败", e);
  2051. }
  2052. }
  2053. }
  2054. // 保存所有记录(无发票 + 有发票)
  2055. if (!noInvoiceList.isEmpty()) {
  2056. for (InvoiceLibrary invoice : noInvoiceList) {
  2057. baseMapper.insert(invoice);
  2058. }
  2059. log.info("成功保存 {} 条无发票记录", noInvoiceList.size());
  2060. addWorkflowComment(processInstanceId, userId, "报销总金额:" + mainJe);
  2061. }
  2062. if (!invoiceList.isEmpty()) {
  2063. for (InvoiceLibrary invoice : invoiceList) {
  2064. baseMapper.insert(invoice);
  2065. }
  2066. log.info("成功保存 {} 条发票记录(有发票: {}, 共享发票: {})",
  2067. invoiceList.size(),
  2068. parsedInvoiceDataList.stream().filter(d -> "是".equals(d.get("hasInvoice")) || "被共享".equals(d.get("hasInvoice"))).count(),
  2069. sharedInvoiceDataList.size());
  2070. addWorkflowComment1(processInstanceId, userId, totalAmount);
  2071. }
  2072. if (!allResults.isEmpty()) {
  2073. return McR.success(allResults);
  2074. } else {
  2075. return McR.success();
  2076. }
  2077. } catch (Exception e) {
  2078. log.error("解析子表数据失败", e);
  2079. return McR.error("300", "解析数据失败: " + e.getMessage());
  2080. }
  2081. }
  2082. }
  2083. return McR.success();
  2084. } catch (Exception e) {
  2085. log.error("处理审批实例失败", e);
  2086. return McR.error("500", "系统处理失败: " + e.getMessage());
  2087. }
  2088. }
  2089. @Override
  2090. public McR invoiceLibrary3(Map map) {
  2091. log.info("接收到的参数: {}", map);
  2092. try {
  2093. String processInstanceId = UtilMap.getString(map, "processInstanceId");
  2094. if (StringUtils.isBlank(processInstanceId)) {
  2095. log.error("processInstanceId为空");
  2096. return McR.error("400", "审批实例ID不能为空");
  2097. }
  2098. // 获取审批实例信息
  2099. String accessToken = ddClient.getAccessToken();
  2100. Map processInstance = ddClientWorkflow.getProcessInstanceId(accessToken, processInstanceId);
  2101. if (processInstance == null) {
  2102. log.error("获取审批实例失败: {}", processInstanceId);
  2103. return McR.error("500", "获取审批实例信息失败");
  2104. }
  2105. String userId = (String) processInstance.get("originatorUserId");
  2106. String businessId = (String) processInstance.get("businessId");
  2107. log.info("审批人ID: {}", userId);
  2108. List<Map> formComponentValues = (List<Map>) processInstance.get("formComponentValues");
  2109. if (formComponentValues == null || formComponentValues.isEmpty()) {
  2110. log.warn("表单数据为空");
  2111. return McR.error("400", "表单数据为空");
  2112. }
  2113. // 存储主表的字段值
  2114. String bm = null;
  2115. String bxlb = null;
  2116. String fkzhxx = null;
  2117. String fkzh = null;
  2118. String yhqc = null;
  2119. String skzh = null;
  2120. BigDecimal mainJe = null;
  2121. // 遍历收集主表字段值
  2122. for (Map formComponentValue : formComponentValues) {
  2123. String id = String.valueOf(formComponentValue.get("id"));
  2124. Object value = formComponentValue.get("value");
  2125. if ("MoneyField_5U5SLLA3CS40".equals(id)) {
  2126. if (value instanceof BigDecimal) {
  2127. mainJe = (BigDecimal) value;
  2128. } else if (value != null) {
  2129. try {
  2130. mainJe = new BigDecimal(String.valueOf(value));
  2131. } catch (NumberFormatException e) {
  2132. log.error("金额格式转换失败: {}", value);
  2133. mainJe = BigDecimal.ZERO;
  2134. }
  2135. }
  2136. }
  2137. if ("RecipientAccountField_1J46MPCCTYZK0".equals(id)) {
  2138. skzh = value != null ? String.valueOf(value) : "";
  2139. }
  2140. if ("DDSelectField_BS1HCMO7YTK0".equals(id)) {
  2141. bxlb = value != null ? String.valueOf(value) : "";
  2142. }
  2143. if ("DepartmentField_S9DH58V8SNK0".equals(id)) {
  2144. bm = value != null ? String.valueOf(value) : "";
  2145. }
  2146. if ("DDSelectField_GLW8YHBHSLS0".equals(id)) {
  2147. fkzhxx = value != null ? String.valueOf(value) : "";
  2148. }
  2149. if ("TextField_1PR1HQ4H28SG0".equals(id)) {
  2150. fkzh = value != null ? String.valueOf(value) : "";
  2151. }
  2152. if ("TextField_1UPR1XZTI3VK".equals(id)) {
  2153. yhqc = value != null ? String.valueOf(value) : "";
  2154. }
  2155. }
  2156. // 遍历子表数据
  2157. for (Map formComponentValue : formComponentValues) {
  2158. String id = String.valueOf(formComponentValue.get("id"));
  2159. if ("TableField_9LJ65U3CQ980".equals(id)) {
  2160. String tableFieldValue = String.valueOf(formComponentValue.get("value"));
  2161. log.info("子表数据: {}", tableFieldValue);
  2162. try {
  2163. List<Map> tableRows = (List<Map>) JSONObject.parse(tableFieldValue);
  2164. if (tableRows == null || tableRows.isEmpty()) {
  2165. log.warn("子表数据为空");
  2166. return McR.error("400", "发票明细为空");
  2167. }
  2168. // 用于存储无发票记录和发票记录
  2169. List<InvoiceLibrary> noInvoiceList = new ArrayList<>();
  2170. List<InvoiceLibrary> invoiceList = new ArrayList<>();
  2171. List<Object> allResults = new ArrayList<>();
  2172. // 存储有发票("是"和"被共享")的解析数据
  2173. List<Map<String, Object>> parsedInvoiceDataList = new ArrayList<>();
  2174. // 存储共享发票数据
  2175. List<Map<String, Object>> sharedInvoiceDataList = new ArrayList<>();
  2176. // 用于校验的集合
  2177. List<String> invoiceNumberList = new ArrayList<>();
  2178. Map<String, String> invoiceNumberToFileName = new HashMap<>();
  2179. List<String> invalidBuyerTaxIds = new ArrayList<>();
  2180. // 存储税率信息(从"被共享"发票中获取)
  2181. BigDecimal sharedTaxRate = BigDecimal.ZERO;
  2182. String sharedKind = "";
  2183. boolean hasSharedInvoice = false; // 标记是否存在"被共享"发票
  2184. // ========== 第一遍遍历:收集数据并查找"被共享"发票 ==========
  2185. for (Map row : tableRows) {
  2186. List<Map> rowValues = (List<Map>) row.get("rowValue");
  2187. String hasInvoice = null;
  2188. String cdbm = null;
  2189. String lbmx = null;
  2190. String bz = null;
  2191. BigDecimal detailje = null;
  2192. List<Map> attachmentList = null;
  2193. for (Map rowItem : rowValues) {
  2194. String key = String.valueOf(rowItem.get("key"));
  2195. if ("DDSelectField_TTTOAI0FDXJ4".equals(key)) {
  2196. Object value = rowItem.get("value");
  2197. hasInvoice = value != null ? String.valueOf(value) : "";
  2198. }
  2199. if ("DDSelectField_17FH3ES8HWHS0".equals(key)) {
  2200. Object value = rowItem.get("value");
  2201. lbmx = value != null ? String.valueOf(value) : "";
  2202. }
  2203. if ("TextareaField_1XIFOSSQO5A80".equals(key)) {
  2204. Object value = rowItem.get("value");
  2205. bz = value != null ? String.valueOf(value) : "";
  2206. }
  2207. if ("DepartmentField_15QGD1PV69XC0".equals(key)) {
  2208. Object value = rowItem.get("value");
  2209. cdbm = value != null ? String.valueOf(value) : "";
  2210. }
  2211. if ("NumberField_FYHZK218TTDS".equals(key)) {
  2212. Object value = rowItem.get("value");
  2213. if (value instanceof BigDecimal) {
  2214. detailje = (BigDecimal) value;
  2215. } else if (value != null) {
  2216. try {
  2217. detailje = new BigDecimal(String.valueOf(value));
  2218. } catch (NumberFormatException e) {
  2219. log.error("金额格式转换失败: {}", value);
  2220. detailje = BigDecimal.ZERO;
  2221. }
  2222. } else {
  2223. detailje = BigDecimal.ZERO;
  2224. }
  2225. }
  2226. if ("DDAttachment_XVU9V89QOEM8".equals(key)) {
  2227. Object value = rowItem.get("value");
  2228. if (value instanceof List) {
  2229. attachmentList = (List<Map>) value;
  2230. }
  2231. }
  2232. }
  2233. // ========== 处理 hasInvoice = "否" 的情况(无发票) ==========
  2234. if ("否".equals(hasInvoice)) {
  2235. // 保存基础报销记录(无发票)
  2236. InvoiceLibrary baseInvoice = new InvoiceLibrary();
  2237. baseInvoice.setOaId(processInstanceId);
  2238. baseInvoice.setSkzh(skzh);
  2239. baseInvoice.setBusinessId(businessId);
  2240. baseInvoice.setOaStatus("0");
  2241. baseInvoice.setInvoiceStatus("0");
  2242. baseInvoice.setDep(cdbm != null && StringUtils.isNotBlank(cdbm) ? cdbm : (bm != null ? bm : ""));
  2243. baseInvoice.setAccountTitle(lbmx != null ? lbmx : "");
  2244. baseInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  2245. baseInvoice.setDetailAmount(detailje != null ? detailje : BigDecimal.ZERO);
  2246. baseInvoice.setCreatedAt(LocalDateTime.now());
  2247. baseInvoice.setUpdatedAt(LocalDateTime.now());
  2248. baseInvoice.setInvoiceCode("");
  2249. baseInvoice.setInvoiceNumber("");
  2250. baseInvoice.setInvoiceType("");
  2251. baseInvoice.setAmount(null);
  2252. baseInvoice.setUserId(userId);
  2253. baseInvoice.setTaxAmount(null);
  2254. baseInvoice.setTotalAmount(null);
  2255. baseInvoice.setBuyerName("");
  2256. baseInvoice.setBuyerTaxId("");
  2257. baseInvoice.setSellerName("");
  2258. baseInvoice.setSellerTaxId("");
  2259. baseInvoice.setFormName("合作商报销申请");
  2260. baseInvoice.setPaySubject(fkzhxx != null ? fkzhxx : "");
  2261. baseInvoice.setPayAccount(fkzh != null ? fkzh : "");
  2262. baseInvoice.setBankName(yhqc != null ? yhqc : "");
  2263. baseInvoice.setHasInvoice("否");
  2264. baseInvoice.setSharedTaxAmount(null);
  2265. baseInvoice.setSharedAmount(null);
  2266. baseInvoice.setSharedRate(null);
  2267. baseInvoice.setNote(bz != null ? bz : "");
  2268. noInvoiceList.add(baseInvoice);
  2269. log.info("收集无发票记录: 成本部门={}, 费用明细={}", cdbm, lbmx);
  2270. continue;
  2271. }
  2272. // ========== 处理 hasInvoice = "被共享"(被共享发票,提供税率) ==========
  2273. if ("被共享".equals(hasInvoice) && attachmentList != null && !attachmentList.isEmpty()) {
  2274. // 标记存在被共享发票
  2275. hasSharedInvoice = true;
  2276. for (Map attachment : attachmentList) {
  2277. String fileId = UtilMap.getString(attachment, "fileId");
  2278. String fileType = UtilMap.getString(attachment, "fileType");
  2279. String fileName = UtilMap.getString(attachment, "fileName");
  2280. try {
  2281. String filePath = downloadPath + fileId + "." + fileType;
  2282. downloadDdFile(processInstanceId, fileId, filePath);
  2283. String hz = "qw/files/" + fileId + "." + fileType;
  2284. Map fileMap = new HashMap();
  2285. fileMap.put("url", url + hz);
  2286. fileMap.put("size", new File(filePath).length() / 1024f / 1024f);
  2287. fileMap.put("isPdf", "pdf".equalsIgnoreCase(fileType));
  2288. McR<Object> result = processMixedInvoice(fileMap);
  2289. Object responseData = result.getData();
  2290. // 解析发票数据
  2291. if (responseData != null) {
  2292. String jsonStr = JSONObject.toJSONString(responseData);
  2293. JSONObject jsonObject = JSONObject.parseObject(jsonStr);
  2294. JSONArray resultArray = jsonObject.getJSONArray("result");
  2295. if (resultArray != null && !resultArray.isEmpty()) {
  2296. for (int invoiceIndex = 0; invoiceIndex < resultArray.size(); invoiceIndex++) {
  2297. JSONObject invoice = resultArray.getJSONObject(invoiceIndex);
  2298. String invoiceNumber = invoice.getString("serial") != null ? invoice.getString("serial") : "";
  2299. String buyerTaxId = invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "";
  2300. String buyerName = invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "";
  2301. // ========== 获取税率(供共享发票使用) ==========
  2302. Object taxRateObj = invoice.get("taxRate");
  2303. Object kind = invoice.getString("kindName");
  2304. if (taxRateObj != null) {
  2305. String taxRateStr = String.valueOf(taxRateObj);
  2306. String taxRateNum = taxRateStr.replace("%", "").trim();
  2307. sharedKind = kind != null ? String.valueOf(kind) : "";
  2308. sharedTaxRate = new BigDecimal(taxRateNum);
  2309. sharedTaxRate = sharedTaxRate.divide(new BigDecimal("100"), 10, BigDecimal.ROUND_HALF_UP);
  2310. log.info("从被共享发票获取税率: 原始={}, 转换后={}", taxRateStr, sharedTaxRate);
  2311. }
  2312. // 保存解析的数据供后续使用
  2313. Map<String, Object> parsedData = new HashMap<>();
  2314. parsedData.put("invoice", invoice);
  2315. parsedData.put("fileId", fileId);
  2316. parsedData.put("bz", bz);
  2317. parsedData.put("lbmx", lbmx);
  2318. parsedData.put("fileName", fileName);
  2319. parsedData.put("fileType", fileType);
  2320. parsedData.put("cdbm", cdbm);
  2321. parsedData.put("hasInvoice", hasInvoice);
  2322. parsedData.put("detailje", detailje);
  2323. parsedData.put("responseData", responseData);
  2324. parsedInvoiceDataList.add(parsedData);
  2325. // ========== 校验1:抬头校验 ==========
  2326. if (!isTitleValidationExempt(invoice.getString("name")) && StringUtils.isNotBlank(buyerName)) {
  2327. CompanyTitle existingTitle = companyTitleMapper.selectByTaxId(buyerName);
  2328. if (existingTitle == null) {
  2329. String errorInfo = String.format("发票: %s, 购买方名称: %s, 税号: %s",
  2330. fileName, buyerName, buyerTaxId);
  2331. invalidBuyerTaxIds.add(errorInfo);
  2332. log.warn("抬头校验失败: 税号={} 不存在于抬头库, 文件名={}", buyerTaxId, fileName);
  2333. }
  2334. } else if (!isTitleValidationExempt(invoice.getString("name"))) {
  2335. String errorInfo = String.format("发票: %s, 购买方名称为空,无法校验公司抬头", fileName);
  2336. invalidBuyerTaxIds.add(errorInfo);
  2337. log.warn("抬头校验失败: 购买方名称为空, 文件名={}", fileName);
  2338. }
  2339. // ========== 校验2:发票号重复校验 ==========
  2340. if (StringUtils.isNotBlank(invoiceNumber)) {
  2341. InvoiceLibrary existingInvoice = baseMapper.selectByInvoiceNumber(invoiceNumber);
  2342. if (existingInvoice != null) {
  2343. String errorMsg = String.format("发票号重复: %s (当前文件: %s, 已存在于OA审批单: %s)",
  2344. invoiceNumber, fileName, existingInvoice.getOaId());
  2345. log.error(errorMsg);
  2346. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  2347. return McR.error("400", errorMsg);
  2348. }
  2349. if (invoiceNumberList.contains(invoiceNumber)) {
  2350. String errorMsg = String.format("当前审批单内发票号重复: %s (当前文件: %s, 已存在文件: %s)",
  2351. invoiceNumber, fileName, invoiceNumberToFileName.get(invoiceNumber));
  2352. log.error(errorMsg);
  2353. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  2354. return McR.error("400", errorMsg);
  2355. }
  2356. invoiceNumberList.add(invoiceNumber);
  2357. invoiceNumberToFileName.put(invoiceNumber, fileName);
  2358. }
  2359. }
  2360. }
  2361. }
  2362. } catch (Exception e) {
  2363. log.error("被共享发票解析异常: fileId={}, fileName={}", fileId, fileName, e);
  2364. terminateWorkflow(accessToken, processInstanceId, userId, "被共享发票解析失败: " + e.getMessage());
  2365. return McR.error("400", "被共享发票解析失败: " + e.getMessage());
  2366. }
  2367. }
  2368. continue;
  2369. }
  2370. // ========== 处理 hasInvoice = "是"(有发票,但不共享) ==========
  2371. if ("是".equals(hasInvoice) && attachmentList != null && !attachmentList.isEmpty()) {
  2372. for (Map attachment : attachmentList) {
  2373. String fileId = UtilMap.getString(attachment, "fileId");
  2374. String fileType = UtilMap.getString(attachment, "fileType");
  2375. String fileName = UtilMap.getString(attachment, "fileName");
  2376. try {
  2377. String filePath = downloadPath + fileId + "." + fileType;
  2378. downloadDdFile(processInstanceId, fileId, filePath);
  2379. String hz = "qw/files/" + fileId + "." + fileType;
  2380. Map fileMap = new HashMap();
  2381. fileMap.put("url", url + hz);
  2382. fileMap.put("size", new File(filePath).length() / 1024f / 1024f);
  2383. fileMap.put("isPdf", "pdf".equalsIgnoreCase(fileType));
  2384. McR<Object> result = processMixedInvoice(fileMap);
  2385. Object responseData = result.getData();
  2386. if (responseData != null) {
  2387. String jsonStr = JSONObject.toJSONString(responseData);
  2388. JSONObject jsonObject = JSONObject.parseObject(jsonStr);
  2389. JSONArray resultArray = jsonObject.getJSONArray("result");
  2390. if (resultArray != null && !resultArray.isEmpty()) {
  2391. for (int invoiceIndex = 0; invoiceIndex < resultArray.size(); invoiceIndex++) {
  2392. JSONObject invoice = resultArray.getJSONObject(invoiceIndex);
  2393. String invoiceNumber = invoice.getString("serial") != null ? invoice.getString("serial") : "";
  2394. String buyerTaxId = invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "";
  2395. String buyerName = invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "";
  2396. Map<String, Object> parsedData = new HashMap<>();
  2397. parsedData.put("invoice", invoice);
  2398. parsedData.put("fileId", fileId);
  2399. parsedData.put("bz", bz);
  2400. parsedData.put("lbmx", lbmx);
  2401. parsedData.put("fileName", fileName);
  2402. parsedData.put("fileType", fileType);
  2403. parsedData.put("cdbm", cdbm);
  2404. parsedData.put("hasInvoice", hasInvoice);
  2405. parsedData.put("detailje", detailje);
  2406. parsedData.put("responseData", responseData);
  2407. parsedInvoiceDataList.add(parsedData);
  2408. // 抬头校验
  2409. if (!isTitleValidationExempt(invoice.getString("name")) && StringUtils.isNotBlank(buyerName)) {
  2410. CompanyTitle existingTitle = companyTitleMapper.selectByTaxId(buyerName);
  2411. if (existingTitle == null) {
  2412. String errorInfo = String.format("发票: %s, 购买方名称: %s, 税号: %s",
  2413. fileName, buyerName, buyerTaxId);
  2414. invalidBuyerTaxIds.add(errorInfo);
  2415. log.warn("抬头校验失败: 税号={} 不存在于抬头库, 文件名={}", buyerTaxId, fileName);
  2416. }
  2417. } else if (!isTitleValidationExempt(invoice.getString("name"))) {
  2418. String errorInfo = String.format("发票: %s, 购买方名称为空,无法校验公司抬头", fileName);
  2419. invalidBuyerTaxIds.add(errorInfo);
  2420. log.warn("抬头校验失败: 购买方名称为空, 文件名={}", fileName);
  2421. }
  2422. // 发票号重复校验
  2423. if (StringUtils.isNotBlank(invoiceNumber)) {
  2424. InvoiceLibrary existingInvoice = baseMapper.selectByInvoiceNumber(invoiceNumber);
  2425. if (existingInvoice != null) {
  2426. String errorMsg = String.format("发票号重复: %s (当前文件: %s, 已存在于OA审批单: %s)",
  2427. invoiceNumber, fileName, existingInvoice.getOaId());
  2428. log.error(errorMsg);
  2429. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  2430. return McR.error("400", errorMsg);
  2431. }
  2432. if (invoiceNumberList.contains(invoiceNumber)) {
  2433. String errorMsg = String.format("当前审批单内发票号重复: %s (当前文件: %s, 已存在文件: %s)",
  2434. invoiceNumber, fileName, invoiceNumberToFileName.get(invoiceNumber));
  2435. log.error(errorMsg);
  2436. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  2437. return McR.error("400", errorMsg);
  2438. }
  2439. invoiceNumberList.add(invoiceNumber);
  2440. invoiceNumberToFileName.put(invoiceNumber, fileName);
  2441. }
  2442. }
  2443. }
  2444. }
  2445. } catch (Exception e) {
  2446. log.error("发票解析异常: fileId={}, fileName={}", fileId, fileName, e);
  2447. terminateWorkflow(accessToken, processInstanceId, userId, "发票解析失败: " + e.getMessage());
  2448. return McR.error("400", "发票解析失败: " + e.getMessage());
  2449. }
  2450. }
  2451. continue;
  2452. }
  2453. // ========== 处理 hasInvoice = "共享"(共享发票,需要从被共享发票获取税率) ==========
  2454. if ("共享".equals(hasInvoice)) {
  2455. if (detailje != null && detailje.compareTo(BigDecimal.ZERO) > 0) {
  2456. Map<String, Object> sharedData = new HashMap<>();
  2457. sharedData.put("detailje", detailje);
  2458. sharedData.put("lbmx", lbmx);
  2459. sharedData.put("bz", bz);
  2460. sharedData.put("cdbm", cdbm);
  2461. sharedData.put("hasInvoice", hasInvoice);
  2462. sharedInvoiceDataList.add(sharedData);
  2463. log.info("收集共享发票数据: 金额={}, 成本部门={}", detailje, cdbm);
  2464. }
  2465. }
  2466. }
  2467. // ========== 检查:如果存在共享发票但没有被共享发票 ==========
  2468. if (!sharedInvoiceDataList.isEmpty() && !hasSharedInvoice) {
  2469. String errorMsg = "存在'共享'发票,但未找到'被共享'发票,无法获取税率信息,请确保至少有一行选择'被共享'并上传发票附件";
  2470. log.error(errorMsg);
  2471. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  2472. return McR.error("400", errorMsg);
  2473. }
  2474. // ========== 抬头校验失败处理 ==========
  2475. if (!invalidBuyerTaxIds.isEmpty()) {
  2476. String errorMsg = "发票抬头有误,以下发票的购买方税号不在公司抬头库中:\n" + String.join("\n", invalidBuyerTaxIds);
  2477. log.error(errorMsg);
  2478. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  2479. return McR.error("400", errorMsg);
  2480. }
  2481. BigDecimal totalAmount = BigDecimal.ZERO;
  2482. // ========== 第二遍遍历:保存发票数据 ==========
  2483. // 1. 保存有发票数据("是"和"被共享")
  2484. for (Map<String, Object> parsedData : parsedInvoiceDataList) {
  2485. JSONObject invoice = (JSONObject) parsedData.get("invoice");
  2486. Object responseData = parsedData.get("responseData");
  2487. String cdbm = (String) parsedData.get("cdbm");
  2488. String lbmx = (String) parsedData.get("lbmx");
  2489. String bz = (String) parsedData.get("bz");
  2490. String hasInvoice = (String) parsedData.get("hasInvoice");
  2491. Object detailjeObj = parsedData.get("detailje");
  2492. String detailje = detailjeObj != null ? String.valueOf(detailjeObj) : "";
  2493. allResults.add(responseData);
  2494. BigDecimal amount = invoice.getBigDecimal("amount");
  2495. if (amount != null) {
  2496. totalAmount = totalAmount.add(amount);
  2497. }
  2498. InvoiceLibrary invoiceLibrary = new InvoiceLibrary();
  2499. invoiceLibrary.setOaId(processInstanceId);
  2500. invoiceLibrary.setSkzh(skzh);
  2501. invoiceLibrary.setBusinessId(businessId);
  2502. invoiceLibrary.setInvoiceCode(invoice.getString("code") != null ? invoice.getString("code") : "");
  2503. invoiceLibrary.setInvoiceNumber(invoice.getString("serial") != null ? invoice.getString("serial") : "");
  2504. invoiceLibrary.setInvoiceType(invoice.getString("kindName") != null ? invoice.getString("kindName") : "");
  2505. String date = invoice.getString("date");
  2506. if (StringUtils.isNotBlank(date)) {
  2507. try {
  2508. invoiceLibrary.setInvoiceDate(LocalDate.parse(date.trim()));
  2509. } catch (Exception e) {
  2510. log.warn("日期解析失败: {}", date);
  2511. }
  2512. }
  2513. invoiceLibrary.setAmount(invoice.getBigDecimal("excludingTax") != null ? invoice.getBigDecimal("excludingTax") : BigDecimal.ZERO);
  2514. invoiceLibrary.setTaxAmount(invoice.getBigDecimal("tax") != null ? invoice.getBigDecimal("tax") : BigDecimal.ZERO);
  2515. invoiceLibrary.setTotalAmount(invoice.getBigDecimal("amount") != null ? invoice.getBigDecimal("amount") : BigDecimal.ZERO);
  2516. invoiceLibrary.setBuyerName(invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "");
  2517. invoiceLibrary.setBuyerTaxId(invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "");
  2518. invoiceLibrary.setSellerName(invoice.getString("sellerName") != null ? invoice.getString("sellerName") : "");
  2519. invoiceLibrary.setSellerTaxId(invoice.getString("sellerTaxId") != null ? invoice.getString("sellerTaxId") : "");
  2520. invoiceLibrary.setOaStatus("0");
  2521. invoiceLibrary.setNote(bz != null ? bz : "");
  2522. invoiceLibrary.setUserId(userId);
  2523. invoiceLibrary.setFormName("合作商报销申请");
  2524. invoiceLibrary.setPaySubject(fkzhxx != null ? fkzhxx : "");
  2525. invoiceLibrary.setPayAccount(fkzh != null ? fkzh : "");
  2526. invoiceLibrary.setBankName(yhqc != null ? yhqc : "");
  2527. invoiceLibrary.setInvoiceStatus("0");
  2528. invoiceLibrary.setHasInvoice(hasInvoice);
  2529. if (StringUtils.isNotBlank(detailje)) {
  2530. try {
  2531. invoiceLibrary.setDetailAmount(new BigDecimal(detailje));
  2532. } catch (NumberFormatException e) {
  2533. log.error("detailJe格式转换失败: {}", detailje);
  2534. invoiceLibrary.setDetailAmount(null);
  2535. }
  2536. } else {
  2537. invoiceLibrary.setDetailAmount(null);
  2538. }
  2539. invoiceLibrary.setSharedTaxAmount(null);
  2540. invoiceLibrary.setSharedAmount(null);
  2541. invoiceLibrary.setSharedRate(null);
  2542. invoiceLibrary.setAccountTitle(lbmx != null ? lbmx : "");
  2543. invoiceLibrary.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  2544. invoiceLibrary.setCreatedAt(LocalDateTime.now());
  2545. invoiceLibrary.setUpdatedAt(LocalDateTime.now());
  2546. if (cdbm != null && StringUtils.isNotBlank(cdbm)) {
  2547. invoiceLibrary.setDep(cdbm);
  2548. } else {
  2549. invoiceLibrary.setDep(bm != null ? bm : "");
  2550. }
  2551. invoiceList.add(invoiceLibrary);
  2552. }
  2553. // 2. 保存共享发票数据(使用从"被共享"发票获取的税率)
  2554. if (!sharedInvoiceDataList.isEmpty()) {
  2555. // 检查税率是否有效
  2556. if (sharedTaxRate.compareTo(BigDecimal.ZERO) == 0) {
  2557. log.warn("共享发票税率未获取到,使用默认税率0");
  2558. }
  2559. for (Map<String, Object> sharedData : sharedInvoiceDataList) {
  2560. Object detailjeObj = sharedData.get("detailje");
  2561. String cdbm = (String) sharedData.get("cdbm");
  2562. String lbmx = (String) sharedData.get("lbmx");
  2563. String bz = (String) sharedData.get("bz");
  2564. String hasInvoice = (String) sharedData.get("hasInvoice");
  2565. String detailje = detailjeObj != null ? String.valueOf(detailjeObj) : "";
  2566. try {
  2567. BigDecimal detailAmount = new BigDecimal(detailje);
  2568. // 使用从"被共享"发票解析到的税率
  2569. BigDecimal ocrTaxRate = sharedTaxRate;
  2570. // 计算金额: 含税金额 / (1 + 税率) = 不含税金额
  2571. BigDecimal divisor = BigDecimal.ONE.add(ocrTaxRate);
  2572. BigDecimal sharedTaxAmount = detailAmount.divide(divisor, 2, BigDecimal.ROUND_HALF_UP);
  2573. BigDecimal sharedAmount = detailAmount.subtract(sharedTaxAmount);
  2574. BigDecimal sharedRate = ocrTaxRate;
  2575. log.info("共享发票计算: 含税金额={}, 税率={}%, 不含税金额={}, 税额={}",
  2576. detailAmount, ocrTaxRate.multiply(new BigDecimal("100")),
  2577. sharedAmount, sharedTaxAmount);
  2578. InvoiceLibrary sharedInvoice = new InvoiceLibrary();
  2579. sharedInvoice.setOaId(processInstanceId);
  2580. sharedInvoice.setSkzh(skzh);
  2581. sharedInvoice.setBusinessId(businessId);
  2582. sharedInvoice.setOaStatus("0");
  2583. sharedInvoice.setInvoiceStatus("0");
  2584. sharedInvoice.setDep(cdbm != null && StringUtils.isNotBlank(cdbm) ? cdbm : (bm != null ? bm : ""));
  2585. sharedInvoice.setAccountTitle(lbmx != null ? lbmx : "");
  2586. sharedInvoice.setNote(bz != null ? bz : "");
  2587. sharedInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  2588. sharedInvoice.setAmount(null);
  2589. sharedInvoice.setTaxAmount(null);
  2590. sharedInvoice.setTotalAmount(null);
  2591. sharedInvoice.setUserId(userId);
  2592. sharedInvoice.setFormName("合作商报销申请");
  2593. sharedInvoice.setPaySubject(fkzhxx != null ? fkzhxx : "");
  2594. sharedInvoice.setPayAccount(fkzh != null ? fkzh : "");
  2595. sharedInvoice.setBankName(yhqc != null ? yhqc : "");
  2596. sharedInvoice.setHasInvoice(hasInvoice);
  2597. sharedInvoice.setDetailAmount(detailAmount);
  2598. sharedInvoice.setSharedTaxAmount(sharedAmount);
  2599. sharedInvoice.setSharedAmount(sharedTaxAmount);
  2600. sharedInvoice.setSharedRate(String.valueOf(sharedRate));
  2601. sharedInvoice.setCreatedAt(LocalDateTime.now());
  2602. sharedInvoice.setUpdatedAt(LocalDateTime.now());
  2603. sharedInvoice.setInvoiceCode("");
  2604. sharedInvoice.setInvoiceNumber("");
  2605. sharedInvoice.setInvoiceType(sharedKind);
  2606. sharedInvoice.setBuyerName("");
  2607. sharedInvoice.setBuyerTaxId("");
  2608. sharedInvoice.setSellerName("");
  2609. sharedInvoice.setSellerTaxId("");
  2610. invoiceList.add(sharedInvoice);
  2611. log.info("成功保存共享发票记录,成本部门: {}", cdbm);
  2612. } catch (NumberFormatException e) {
  2613. log.error("共享发票金额格式转换失败: detailje={}", detailje, e);
  2614. } catch (Exception e) {
  2615. log.error("共享发票处理失败", e);
  2616. }
  2617. }
  2618. }
  2619. // 保存所有记录(无发票 + 有发票)
  2620. if (!noInvoiceList.isEmpty()) {
  2621. for (InvoiceLibrary invoice : noInvoiceList) {
  2622. baseMapper.insert(invoice);
  2623. }
  2624. log.info("成功保存 {} 条无发票记录", noInvoiceList.size());
  2625. addWorkflowComment(processInstanceId, userId, "报销总金额:" + mainJe);
  2626. }
  2627. if (!invoiceList.isEmpty()) {
  2628. for (InvoiceLibrary invoice : invoiceList) {
  2629. baseMapper.insert(invoice);
  2630. }
  2631. log.info("成功保存 {} 条发票记录(有发票: {}, 共享发票: {})",
  2632. invoiceList.size(),
  2633. parsedInvoiceDataList.stream().filter(d -> "是".equals(d.get("hasInvoice")) || "被共享".equals(d.get("hasInvoice"))).count(),
  2634. sharedInvoiceDataList.size());
  2635. addWorkflowComment1(processInstanceId, userId, totalAmount);
  2636. }
  2637. if (!allResults.isEmpty()) {
  2638. return McR.success(allResults);
  2639. } else {
  2640. return McR.success();
  2641. }
  2642. } catch (Exception e) {
  2643. log.error("解析子表数据失败", e);
  2644. return McR.error("300", "解析数据失败: " + e.getMessage());
  2645. }
  2646. }
  2647. }
  2648. return McR.success();
  2649. } catch (Exception e) {
  2650. log.error("处理审批实例失败", e);
  2651. return McR.error("500", "系统处理失败: " + e.getMessage());
  2652. }
  2653. }
  2654. @Override
  2655. public McR invoiceLibrary5(Map map) {
  2656. log.info("接收到的参数: {}", map);
  2657. try {
  2658. String processInstanceId = UtilMap.getString(map, "processInstanceId");
  2659. if (StringUtils.isBlank(processInstanceId)) {
  2660. log.error("processInstanceId为空");
  2661. return McR.error("400", "审批实例ID不能为空");
  2662. }
  2663. // 获取审批实例信息
  2664. String accessToken = ddClient.getAccessToken();
  2665. Map processInstance = ddClientWorkflow.getProcessInstanceId(accessToken, processInstanceId);
  2666. if (processInstance == null) {
  2667. log.error("获取审批实例失败: {}", processInstanceId);
  2668. return McR.error("500", "获取审批实例信息失败");
  2669. }
  2670. String userId = (String) processInstance.get("originatorUserId");
  2671. String businessId = (String) processInstance.get("businessId");
  2672. log.info("审批人ID: {}", userId);
  2673. List<Map> formComponentValues = (List<Map>) processInstance.get("formComponentValues");
  2674. if (formComponentValues == null || formComponentValues.isEmpty()) {
  2675. log.warn("表单数据为空");
  2676. return McR.error("400", "表单数据为空");
  2677. }
  2678. // 存储主表的字段值
  2679. String bm = null;
  2680. String bxlb = null;
  2681. String fkzhxx = null;
  2682. String fkzh = null;
  2683. String yhqc = null;
  2684. String skzh = null;
  2685. BigDecimal mainJe = null;
  2686. // 遍历收集主表字段值
  2687. for (Map formComponentValue : formComponentValues) {
  2688. String id = String.valueOf(formComponentValue.get("id"));
  2689. Object value = formComponentValue.get("value");
  2690. if ("MoneyField_1435GRXGYPA80".equals(id)) {
  2691. if (value instanceof BigDecimal) {
  2692. mainJe = (BigDecimal) value;
  2693. } else if (value != null) {
  2694. try {
  2695. mainJe = new BigDecimal(String.valueOf(value));
  2696. } catch (NumberFormatException e) {
  2697. log.error("金额格式转换失败: {}", value);
  2698. mainJe = BigDecimal.ZERO;
  2699. }
  2700. }
  2701. }
  2702. if ("RecipientAccountField_1PNXMXLXIW000".equals(id)) {
  2703. skzh = value != null ? String.valueOf(value) : "";
  2704. }
  2705. if ("DDSelectField_1UX0JVU89Q000".equals(id)) {
  2706. bxlb = value != null ? String.valueOf(value) : "";
  2707. }
  2708. if ("DepartmentField_1BJT6UY9Y5B40".equals(id)) {
  2709. bm = value != null ? String.valueOf(value) : "";
  2710. }
  2711. if ("DDSelectField_1D77TV92WXEO0".equals(id)) {
  2712. fkzhxx = value != null ? String.valueOf(value) : "";
  2713. }
  2714. if ("TextField_3CQPLF9BVBO0".equals(id)) {
  2715. fkzh = value != null ? String.valueOf(value) : "";
  2716. }
  2717. if ("TextField_YSFYQQ305SG0".equals(id)) {
  2718. yhqc = value != null ? String.valueOf(value) : "";
  2719. }
  2720. }
  2721. // 遍历子表数据
  2722. for (Map formComponentValue : formComponentValues) {
  2723. String id = String.valueOf(formComponentValue.get("id"));
  2724. if ("TableField_169TONKJ3KW00".equals(id)) {
  2725. String tableFieldValue = String.valueOf(formComponentValue.get("value"));
  2726. log.info("子表数据: {}", tableFieldValue);
  2727. try {
  2728. List<Map> tableRows = (List<Map>) JSONObject.parse(tableFieldValue);
  2729. if (tableRows == null || tableRows.isEmpty()) {
  2730. log.warn("子表数据为空");
  2731. return McR.error("400", "发票明细为空");
  2732. }
  2733. // 用于存储无发票记录和发票记录
  2734. List<InvoiceLibrary> noInvoiceList = new ArrayList<>();
  2735. List<InvoiceLibrary> invoiceList = new ArrayList<>();
  2736. List<Object> allResults = new ArrayList<>();
  2737. // 存储有发票("是"和"被共享")的解析数据
  2738. List<Map<String, Object>> parsedInvoiceDataList = new ArrayList<>();
  2739. // 存储共享发票数据
  2740. List<Map<String, Object>> sharedInvoiceDataList = new ArrayList<>();
  2741. // 用于校验的集合
  2742. List<String> invoiceNumberList = new ArrayList<>();
  2743. Map<String, String> invoiceNumberToFileName = new HashMap<>();
  2744. List<String> invalidBuyerTaxIds = new ArrayList<>();
  2745. // 存储税率信息(从"被共享"发票中获取)
  2746. BigDecimal sharedTaxRate = BigDecimal.ZERO;
  2747. String sharedKind = "";
  2748. boolean hasSharedInvoice = false; // 标记是否存在"被共享"发票
  2749. // ========== 第一遍遍历:收集数据并查找"被共享"发票 ==========
  2750. for (Map row : tableRows) {
  2751. List<Map> rowValues = (List<Map>) row.get("rowValue");
  2752. String hasInvoice = null;
  2753. String cdbm = null;
  2754. String lbmx = null;
  2755. String bz = null;
  2756. BigDecimal detailje = null;
  2757. List<Map> attachmentList = null;
  2758. for (Map rowItem : rowValues) {
  2759. String key = String.valueOf(rowItem.get("key"));
  2760. if ("DDSelectField_1G6XMYA3FMCG0".equals(key)) {
  2761. Object value = rowItem.get("value");
  2762. hasInvoice = value != null ? String.valueOf(value) : "";
  2763. }
  2764. if ("DDSelectField_17K1Q5M73YKG0".equals(key)) {
  2765. Object value = rowItem.get("value");
  2766. lbmx = value != null ? String.valueOf(value) : "";
  2767. }
  2768. if ("TextareaField_207VNZ39WOCG0".equals(key)) {
  2769. Object value = rowItem.get("value");
  2770. bz = value != null ? String.valueOf(value) : "";
  2771. }
  2772. if ("DepartmentField_KJIWTTZLE800".equals(key)) {
  2773. Object value = rowItem.get("value");
  2774. cdbm = value != null ? String.valueOf(value) : "";
  2775. }
  2776. if ("NumberField_L0ALER5S6180".equals(key)) {
  2777. Object value = rowItem.get("value");
  2778. if (value instanceof BigDecimal) {
  2779. detailje = (BigDecimal) value;
  2780. } else if (value != null) {
  2781. try {
  2782. detailje = new BigDecimal(String.valueOf(value));
  2783. } catch (NumberFormatException e) {
  2784. log.error("金额格式转换失败: {}", value);
  2785. detailje = BigDecimal.ZERO;
  2786. }
  2787. } else {
  2788. detailje = BigDecimal.ZERO;
  2789. }
  2790. }
  2791. if ("DDAttachment_EPL7ILUTD0G0".equals(key)) {
  2792. Object value = rowItem.get("value");
  2793. if (value instanceof List) {
  2794. attachmentList = (List<Map>) value;
  2795. }
  2796. }
  2797. }
  2798. // ========== 处理 hasInvoice = "否" 的情况(无发票) ==========
  2799. if ("否".equals(hasInvoice)) {
  2800. // 保存基础报销记录(无发票)
  2801. InvoiceLibrary baseInvoice = new InvoiceLibrary();
  2802. baseInvoice.setOaId(processInstanceId);
  2803. baseInvoice.setSkzh(skzh);
  2804. baseInvoice.setBusinessId(businessId);
  2805. baseInvoice.setOaStatus("0");
  2806. baseInvoice.setInvoiceStatus("0");
  2807. baseInvoice.setDep(cdbm != null && StringUtils.isNotBlank(cdbm) ? cdbm : (bm != null ? bm : ""));
  2808. baseInvoice.setAccountTitle(lbmx != null ? lbmx : "");
  2809. baseInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  2810. baseInvoice.setDetailAmount(detailje != null ? detailje : BigDecimal.ZERO);
  2811. baseInvoice.setCreatedAt(LocalDateTime.now());
  2812. baseInvoice.setUpdatedAt(LocalDateTime.now());
  2813. baseInvoice.setInvoiceCode("");
  2814. baseInvoice.setInvoiceNumber("");
  2815. baseInvoice.setInvoiceType("");
  2816. baseInvoice.setAmount(null);
  2817. baseInvoice.setTaxAmount(null);
  2818. baseInvoice.setTotalAmount(null);
  2819. baseInvoice.setBuyerName("");
  2820. baseInvoice.setBuyerTaxId("");
  2821. baseInvoice.setSellerName("");
  2822. baseInvoice.setSellerTaxId("");
  2823. baseInvoice.setUserId(userId);
  2824. baseInvoice.setFormName("出差费用报销");
  2825. baseInvoice.setPaySubject(fkzhxx != null ? fkzhxx : "");
  2826. baseInvoice.setPayAccount(fkzh != null ? fkzh : "");
  2827. baseInvoice.setBankName(yhqc != null ? yhqc : "");
  2828. baseInvoice.setHasInvoice("否");
  2829. baseInvoice.setSharedTaxAmount(null);
  2830. baseInvoice.setSharedAmount(null);
  2831. baseInvoice.setSharedRate(null);
  2832. baseInvoice.setNote(bz != null ? bz : "");
  2833. noInvoiceList.add(baseInvoice);
  2834. log.info("收集无发票记录: 成本部门={}, 费用明细={}", cdbm, lbmx);
  2835. continue;
  2836. }
  2837. // ========== 处理 hasInvoice = "被共享"(被共享发票,提供税率) ==========
  2838. if ("被共享".equals(hasInvoice) && attachmentList != null && !attachmentList.isEmpty()) {
  2839. // 标记存在被共享发票
  2840. hasSharedInvoice = true;
  2841. for (Map attachment : attachmentList) {
  2842. String fileId = UtilMap.getString(attachment, "fileId");
  2843. String fileType = UtilMap.getString(attachment, "fileType");
  2844. String fileName = UtilMap.getString(attachment, "fileName");
  2845. try {
  2846. String filePath = downloadPath + fileId + "." + fileType;
  2847. downloadDdFile(processInstanceId, fileId, filePath);
  2848. String hz = "qw/files/" + fileId + "." + fileType;
  2849. Map fileMap = new HashMap();
  2850. fileMap.put("url", url + hz);
  2851. fileMap.put("size", new File(filePath).length() / 1024f / 1024f);
  2852. fileMap.put("isPdf", "pdf".equalsIgnoreCase(fileType));
  2853. McR<Object> result = processMixedInvoice(fileMap);
  2854. Object responseData = result.getData();
  2855. // 解析发票数据
  2856. if (responseData != null) {
  2857. String jsonStr = JSONObject.toJSONString(responseData);
  2858. JSONObject jsonObject = JSONObject.parseObject(jsonStr);
  2859. JSONArray resultArray = jsonObject.getJSONArray("result");
  2860. if (resultArray != null && !resultArray.isEmpty()) {
  2861. for (int invoiceIndex = 0; invoiceIndex < resultArray.size(); invoiceIndex++) {
  2862. JSONObject invoice = resultArray.getJSONObject(invoiceIndex);
  2863. String invoiceNumber = invoice.getString("serial") != null ? invoice.getString("serial") : "";
  2864. String buyerTaxId = invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "";
  2865. String buyerName = invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "";
  2866. // ========== 获取税率(供共享发票使用) ==========
  2867. Object taxRateObj = invoice.get("taxRate");
  2868. Object kind = invoice.getString("kindName");
  2869. if (taxRateObj != null) {
  2870. String taxRateStr = String.valueOf(taxRateObj);
  2871. String taxRateNum = taxRateStr.replace("%", "").trim();
  2872. sharedKind = kind != null ? String.valueOf(kind) : "";
  2873. sharedTaxRate = new BigDecimal(taxRateNum);
  2874. sharedTaxRate = sharedTaxRate.divide(new BigDecimal("100"), 10, BigDecimal.ROUND_HALF_UP);
  2875. log.info("从被共享发票获取税率: 原始={}, 转换后={}", taxRateStr, sharedTaxRate);
  2876. }
  2877. // 保存解析的数据供后续使用
  2878. Map<String, Object> parsedData = new HashMap<>();
  2879. parsedData.put("invoice", invoice);
  2880. parsedData.put("fileId", fileId);
  2881. parsedData.put("bz", bz);
  2882. parsedData.put("lbmx", lbmx);
  2883. parsedData.put("fileName", fileName);
  2884. parsedData.put("fileType", fileType);
  2885. parsedData.put("cdbm", cdbm);
  2886. parsedData.put("hasInvoice", hasInvoice);
  2887. parsedData.put("detailje", detailje);
  2888. parsedData.put("responseData", responseData);
  2889. parsedInvoiceDataList.add(parsedData);
  2890. // ========== 校验1:抬头校验 ==========
  2891. if (!isTitleValidationExempt(invoice.getString("name")) && StringUtils.isNotBlank(buyerName)) {
  2892. CompanyTitle existingTitle = companyTitleMapper.selectByTaxId(buyerName);
  2893. if (existingTitle == null) {
  2894. String errorInfo = String.format("发票: %s, 购买方名称: %s, 税号: %s",
  2895. fileName, buyerName, buyerTaxId);
  2896. invalidBuyerTaxIds.add(errorInfo);
  2897. log.warn("抬头校验失败: 税号={} 不存在于抬头库, 文件名={}", buyerTaxId, fileName);
  2898. }
  2899. } else if (!isTitleValidationExempt(invoice.getString("name"))) {
  2900. String errorInfo = String.format("发票: %s, 购买方名称为空,无法校验公司抬头", fileName);
  2901. invalidBuyerTaxIds.add(errorInfo);
  2902. log.warn("抬头校验失败: 购买方名称为空, 文件名={}", fileName);
  2903. }
  2904. // ========== 校验2:发票号重复校验 ==========
  2905. if (StringUtils.isNotBlank(invoiceNumber)) {
  2906. InvoiceLibrary existingInvoice = baseMapper.selectByInvoiceNumber(invoiceNumber);
  2907. if (existingInvoice != null) {
  2908. String errorMsg = String.format("发票号重复: %s (当前文件: %s, 已存在于OA审批单: %s)",
  2909. invoiceNumber, fileName, existingInvoice.getOaId());
  2910. log.error(errorMsg);
  2911. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  2912. return McR.error("400", errorMsg);
  2913. }
  2914. if (invoiceNumberList.contains(invoiceNumber)) {
  2915. String errorMsg = String.format("当前审批单内发票号重复: %s (当前文件: %s, 已存在文件: %s)",
  2916. invoiceNumber, fileName, invoiceNumberToFileName.get(invoiceNumber));
  2917. log.error(errorMsg);
  2918. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  2919. return McR.error("400", errorMsg);
  2920. }
  2921. invoiceNumberList.add(invoiceNumber);
  2922. invoiceNumberToFileName.put(invoiceNumber, fileName);
  2923. }
  2924. }
  2925. }
  2926. }
  2927. } catch (Exception e) {
  2928. log.error("被共享发票解析异常: fileId={}, fileName={}", fileId, fileName, e);
  2929. terminateWorkflow(accessToken, processInstanceId, userId, "被共享发票解析失败: " + e.getMessage());
  2930. return McR.error("400", "被共享发票解析失败: " + e.getMessage());
  2931. }
  2932. }
  2933. continue;
  2934. }
  2935. // ========== 处理 hasInvoice = "是"(有发票,但不共享) ==========
  2936. if ("是".equals(hasInvoice) && attachmentList != null && !attachmentList.isEmpty()) {
  2937. for (Map attachment : attachmentList) {
  2938. String fileId = UtilMap.getString(attachment, "fileId");
  2939. String fileType = UtilMap.getString(attachment, "fileType");
  2940. String fileName = UtilMap.getString(attachment, "fileName");
  2941. try {
  2942. String filePath = downloadPath + fileId + "." + fileType;
  2943. downloadDdFile(processInstanceId, fileId, filePath);
  2944. String hz = "qw/files/" + fileId + "." + fileType;
  2945. Map fileMap = new HashMap();
  2946. fileMap.put("url", url + hz);
  2947. fileMap.put("size", new File(filePath).length() / 1024f / 1024f);
  2948. fileMap.put("isPdf", "pdf".equalsIgnoreCase(fileType));
  2949. McR<Object> result = processMixedInvoice(fileMap);
  2950. Object responseData = result.getData();
  2951. if (responseData != null) {
  2952. String jsonStr = JSONObject.toJSONString(responseData);
  2953. JSONObject jsonObject = JSONObject.parseObject(jsonStr);
  2954. JSONArray resultArray = jsonObject.getJSONArray("result");
  2955. if (resultArray != null && !resultArray.isEmpty()) {
  2956. for (int invoiceIndex = 0; invoiceIndex < resultArray.size(); invoiceIndex++) {
  2957. JSONObject invoice = resultArray.getJSONObject(invoiceIndex);
  2958. String invoiceNumber = invoice.getString("serial") != null ? invoice.getString("serial") : "";
  2959. String buyerTaxId = invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "";
  2960. String buyerName = invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "";
  2961. Map<String, Object> parsedData = new HashMap<>();
  2962. parsedData.put("invoice", invoice);
  2963. parsedData.put("fileId", fileId);
  2964. parsedData.put("bz", bz);
  2965. parsedData.put("lbmx", lbmx);
  2966. parsedData.put("fileName", fileName);
  2967. parsedData.put("fileType", fileType);
  2968. parsedData.put("cdbm", cdbm);
  2969. parsedData.put("hasInvoice", hasInvoice);
  2970. parsedData.put("detailje", detailje);
  2971. parsedData.put("responseData", responseData);
  2972. parsedInvoiceDataList.add(parsedData);
  2973. // 抬头校验
  2974. if (!isTitleValidationExempt(invoice.getString("name")) && StringUtils.isNotBlank(buyerName)) {
  2975. CompanyTitle existingTitle = companyTitleMapper.selectByTaxId(buyerName);
  2976. if (existingTitle == null) {
  2977. String errorInfo = String.format("发票: %s, 购买方名称: %s, 税号: %s",
  2978. fileName, buyerName, buyerTaxId);
  2979. invalidBuyerTaxIds.add(errorInfo);
  2980. log.warn("抬头校验失败: 税号={} 不存在于抬头库, 文件名={}", buyerTaxId, fileName);
  2981. }
  2982. } else if (!isTitleValidationExempt(invoice.getString("name"))) {
  2983. String errorInfo = String.format("发票: %s, 购买方税号为空", fileName);
  2984. invalidBuyerTaxIds.add(errorInfo);
  2985. log.warn("抬头校验失败: 购买方税号为空, 文件名={}", fileName);
  2986. }
  2987. // 发票号重复校验
  2988. if (StringUtils.isNotBlank(invoiceNumber)) {
  2989. InvoiceLibrary existingInvoice = baseMapper.selectByInvoiceNumber(invoiceNumber);
  2990. if (existingInvoice != null) {
  2991. String errorMsg = String.format("发票号重复: %s (当前文件: %s, 已存在于OA审批单: %s)",
  2992. invoiceNumber, fileName, existingInvoice.getOaId());
  2993. log.error(errorMsg);
  2994. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  2995. return McR.error("400", errorMsg);
  2996. }
  2997. if (invoiceNumberList.contains(invoiceNumber)) {
  2998. String errorMsg = String.format("当前审批单内发票号重复: %s (当前文件: %s, 已存在文件: %s)",
  2999. invoiceNumber, fileName, invoiceNumberToFileName.get(invoiceNumber));
  3000. log.error(errorMsg);
  3001. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  3002. return McR.error("400", errorMsg);
  3003. }
  3004. invoiceNumberList.add(invoiceNumber);
  3005. invoiceNumberToFileName.put(invoiceNumber, fileName);
  3006. }
  3007. }
  3008. }
  3009. }
  3010. } catch (Exception e) {
  3011. log.error("发票解析异常: fileId={}, fileName={}", fileId, fileName, e);
  3012. terminateWorkflow(accessToken, processInstanceId, userId, "发票解析失败: " + e.getMessage());
  3013. return McR.error("400", "发票解析失败: " + e.getMessage());
  3014. }
  3015. }
  3016. continue;
  3017. }
  3018. // ========== 处理 hasInvoice = "共享"(共享发票,需要从被共享发票获取税率) ==========
  3019. if ("共享".equals(hasInvoice)) {
  3020. if (detailje != null && detailje.compareTo(BigDecimal.ZERO) > 0) {
  3021. Map<String, Object> sharedData = new HashMap<>();
  3022. sharedData.put("detailje", detailje);
  3023. sharedData.put("lbmx", lbmx);
  3024. sharedData.put("bz", bz);
  3025. sharedData.put("cdbm", cdbm);
  3026. sharedData.put("hasInvoice", hasInvoice);
  3027. sharedInvoiceDataList.add(sharedData);
  3028. log.info("收集共享发票数据: 金额={}, 成本部门={}", detailje, cdbm);
  3029. }
  3030. }
  3031. }
  3032. // ========== 检查:如果存在共享发票但没有被共享发票 ==========
  3033. if (!sharedInvoiceDataList.isEmpty() && !hasSharedInvoice) {
  3034. String errorMsg = "存在'共享'发票,但未找到'被共享'发票,无法获取税率信息,请确保至少有一行选择'被共享'并上传发票附件";
  3035. log.error(errorMsg);
  3036. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  3037. return McR.error("400", errorMsg);
  3038. }
  3039. // ========== 抬头校验失败处理 ==========
  3040. if (!invalidBuyerTaxIds.isEmpty()) {
  3041. String errorMsg = "发票抬头有误,以下发票的购买方税号不在公司抬头库中:\n" + String.join("\n", invalidBuyerTaxIds);
  3042. log.error(errorMsg);
  3043. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  3044. return McR.error("400", errorMsg);
  3045. }
  3046. BigDecimal totalAmount = BigDecimal.ZERO;
  3047. // ========== 第二遍遍历:保存发票数据 ==========
  3048. // 1. 保存有发票数据("是"和"被共享")
  3049. for (Map<String, Object> parsedData : parsedInvoiceDataList) {
  3050. JSONObject invoice = (JSONObject) parsedData.get("invoice");
  3051. Object responseData = parsedData.get("responseData");
  3052. String cdbm = (String) parsedData.get("cdbm");
  3053. String lbmx = (String) parsedData.get("lbmx");
  3054. String bz = (String) parsedData.get("bz");
  3055. String hasInvoice = (String) parsedData.get("hasInvoice");
  3056. Object detailjeObj = parsedData.get("detailje");
  3057. String detailje = detailjeObj != null ? String.valueOf(detailjeObj) : "";
  3058. allResults.add(responseData);
  3059. BigDecimal amount = invoice.getBigDecimal("amount");
  3060. if (amount != null) {
  3061. totalAmount = totalAmount.add(amount);
  3062. }
  3063. InvoiceLibrary invoiceLibrary = new InvoiceLibrary();
  3064. invoiceLibrary.setOaId(processInstanceId);
  3065. invoiceLibrary.setSkzh(skzh);
  3066. invoiceLibrary.setBusinessId(businessId);
  3067. invoiceLibrary.setUserId(userId);
  3068. invoiceLibrary.setInvoiceCode(invoice.getString("code") != null ? invoice.getString("code") : "");
  3069. invoiceLibrary.setInvoiceNumber(invoice.getString("serial") != null ? invoice.getString("serial") : "");
  3070. invoiceLibrary.setInvoiceType(invoice.getString("kindName") != null ? invoice.getString("kindName") : "");
  3071. String date = invoice.getString("date");
  3072. if (StringUtils.isNotBlank(date)) {
  3073. try {
  3074. invoiceLibrary.setInvoiceDate(LocalDate.parse(date.trim()));
  3075. } catch (Exception e) {
  3076. log.warn("日期解析失败: {}", date);
  3077. }
  3078. }
  3079. invoiceLibrary.setAmount(invoice.getBigDecimal("excludingTax") != null ? invoice.getBigDecimal("excludingTax") : BigDecimal.ZERO);
  3080. invoiceLibrary.setTaxAmount(invoice.getBigDecimal("tax") != null ? invoice.getBigDecimal("tax") : BigDecimal.ZERO);
  3081. invoiceLibrary.setTotalAmount(invoice.getBigDecimal("amount") != null ? invoice.getBigDecimal("amount") : BigDecimal.ZERO);
  3082. invoiceLibrary.setBuyerName(invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "");
  3083. invoiceLibrary.setBuyerTaxId(invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "");
  3084. invoiceLibrary.setSellerName(invoice.getString("sellerName") != null ? invoice.getString("sellerName") : "");
  3085. invoiceLibrary.setSellerTaxId(invoice.getString("sellerTaxId") != null ? invoice.getString("sellerTaxId") : "");
  3086. invoiceLibrary.setOaStatus("0");
  3087. invoiceLibrary.setNote(bz != null ? bz : "");
  3088. invoiceLibrary.setFormName("出差费用报销");
  3089. invoiceLibrary.setPaySubject(fkzhxx != null ? fkzhxx : "");
  3090. invoiceLibrary.setPayAccount(fkzh != null ? fkzh : "");
  3091. invoiceLibrary.setBankName(yhqc != null ? yhqc : "");
  3092. invoiceLibrary.setInvoiceStatus("0");
  3093. invoiceLibrary.setHasInvoice(hasInvoice);
  3094. if (StringUtils.isNotBlank(detailje)) {
  3095. try {
  3096. invoiceLibrary.setDetailAmount(new BigDecimal(detailje));
  3097. } catch (NumberFormatException e) {
  3098. log.error("detailJe格式转换失败: {}", detailje);
  3099. invoiceLibrary.setDetailAmount(null);
  3100. }
  3101. } else {
  3102. invoiceLibrary.setDetailAmount(null);
  3103. }
  3104. invoiceLibrary.setSharedTaxAmount(null);
  3105. invoiceLibrary.setSharedAmount(null);
  3106. invoiceLibrary.setSharedRate(null);
  3107. invoiceLibrary.setAccountTitle(lbmx != null ? lbmx : "");
  3108. invoiceLibrary.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  3109. invoiceLibrary.setCreatedAt(LocalDateTime.now());
  3110. invoiceLibrary.setUpdatedAt(LocalDateTime.now());
  3111. if (cdbm != null && StringUtils.isNotBlank(cdbm)) {
  3112. invoiceLibrary.setDep(cdbm);
  3113. } else {
  3114. invoiceLibrary.setDep(bm != null ? bm : "");
  3115. }
  3116. invoiceList.add(invoiceLibrary);
  3117. }
  3118. // 2. 保存共享发票数据(使用从"被共享"发票获取的税率)
  3119. if (!sharedInvoiceDataList.isEmpty()) {
  3120. // 检查税率是否有效
  3121. if (sharedTaxRate.compareTo(BigDecimal.ZERO) == 0) {
  3122. log.warn("共享发票税率未获取到,使用默认税率0");
  3123. }
  3124. for (Map<String, Object> sharedData : sharedInvoiceDataList) {
  3125. Object detailjeObj = sharedData.get("detailje");
  3126. String cdbm = (String) sharedData.get("cdbm");
  3127. String lbmx = (String) sharedData.get("lbmx");
  3128. String bz = (String) sharedData.get("bz");
  3129. String hasInvoice = (String) sharedData.get("hasInvoice");
  3130. String detailje = detailjeObj != null ? String.valueOf(detailjeObj) : "";
  3131. try {
  3132. BigDecimal detailAmount = new BigDecimal(detailje);
  3133. // 使用从"被共享"发票解析到的税率
  3134. BigDecimal ocrTaxRate = sharedTaxRate;
  3135. // 计算金额: 含税金额 / (1 + 税率) = 不含税金额
  3136. BigDecimal divisor = BigDecimal.ONE.add(ocrTaxRate);
  3137. BigDecimal sharedTaxAmount = detailAmount.divide(divisor, 2, BigDecimal.ROUND_HALF_UP);
  3138. BigDecimal sharedAmount = detailAmount.subtract(sharedTaxAmount);
  3139. BigDecimal sharedRate = ocrTaxRate;
  3140. log.info("共享发票计算: 含税金额={}, 税率={}%, 不含税金额={}, 税额={}",
  3141. detailAmount, ocrTaxRate.multiply(new BigDecimal("100")),
  3142. sharedAmount, sharedTaxAmount);
  3143. InvoiceLibrary sharedInvoice = new InvoiceLibrary();
  3144. sharedInvoice.setOaId(processInstanceId);
  3145. sharedInvoice.setSkzh(skzh);
  3146. sharedInvoice.setBusinessId(businessId);
  3147. sharedInvoice.setOaStatus("0");
  3148. sharedInvoice.setInvoiceStatus("0");
  3149. sharedInvoice.setDep(cdbm != null && StringUtils.isNotBlank(cdbm) ? cdbm : (bm != null ? bm : ""));
  3150. sharedInvoice.setAccountTitle(lbmx != null ? lbmx : "");
  3151. sharedInvoice.setNote(bz != null ? bz : "");
  3152. sharedInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  3153. sharedInvoice.setAmount(null);
  3154. sharedInvoice.setTaxAmount(null);
  3155. sharedInvoice.setTotalAmount(null);
  3156. sharedInvoice.setUserId(userId);
  3157. sharedInvoice.setFormName("出差费用报销");
  3158. sharedInvoice.setPaySubject(fkzhxx != null ? fkzhxx : "");
  3159. sharedInvoice.setPayAccount(fkzh != null ? fkzh : "");
  3160. sharedInvoice.setBankName(yhqc != null ? yhqc : "");
  3161. sharedInvoice.setHasInvoice(hasInvoice);
  3162. sharedInvoice.setDetailAmount(detailAmount);
  3163. sharedInvoice.setSharedTaxAmount(sharedAmount);
  3164. sharedInvoice.setSharedAmount(sharedTaxAmount);
  3165. sharedInvoice.setSharedRate(String.valueOf(sharedRate));
  3166. sharedInvoice.setCreatedAt(LocalDateTime.now());
  3167. sharedInvoice.setUpdatedAt(LocalDateTime.now());
  3168. sharedInvoice.setInvoiceCode("");
  3169. sharedInvoice.setInvoiceNumber("");
  3170. sharedInvoice.setInvoiceType(sharedKind);
  3171. sharedInvoice.setBuyerName("");
  3172. sharedInvoice.setBuyerTaxId("");
  3173. sharedInvoice.setSellerName("");
  3174. sharedInvoice.setSellerTaxId("");
  3175. invoiceList.add(sharedInvoice);
  3176. log.info("成功保存共享发票记录,成本部门: {}", cdbm);
  3177. } catch (NumberFormatException e) {
  3178. log.error("共享发票金额格式转换失败: detailje={}", detailje, e);
  3179. } catch (Exception e) {
  3180. log.error("共享发票处理失败", e);
  3181. }
  3182. }
  3183. }
  3184. // 保存所有记录(无发票 + 有发票)
  3185. if (!noInvoiceList.isEmpty()) {
  3186. for (InvoiceLibrary invoice : noInvoiceList) {
  3187. baseMapper.insert(invoice);
  3188. }
  3189. log.info("成功保存 {} 条无发票记录", noInvoiceList.size());
  3190. addWorkflowComment(processInstanceId, userId, "报销总金额:" + mainJe);
  3191. }
  3192. if (!invoiceList.isEmpty()) {
  3193. for (InvoiceLibrary invoice : invoiceList) {
  3194. baseMapper.insert(invoice);
  3195. }
  3196. log.info("成功保存 {} 条发票记录(有发票: {}, 共享发票: {})",
  3197. invoiceList.size(),
  3198. parsedInvoiceDataList.stream().filter(d -> "是".equals(d.get("hasInvoice")) || "被共享".equals(d.get("hasInvoice"))).count(),
  3199. sharedInvoiceDataList.size());
  3200. addWorkflowComment1(processInstanceId, userId, totalAmount);
  3201. }
  3202. if (!allResults.isEmpty()) {
  3203. return McR.success(allResults);
  3204. } else {
  3205. return McR.success();
  3206. }
  3207. } catch (Exception e) {
  3208. log.error("解析子表数据失败", e);
  3209. return McR.error("300", "解析数据失败: " + e.getMessage());
  3210. }
  3211. }
  3212. }
  3213. return McR.success();
  3214. } catch (Exception e) {
  3215. log.error("处理审批实例失败", e);
  3216. return McR.error("500", "系统处理失败: " + e.getMessage());
  3217. }
  3218. }
  3219. @Override
  3220. public McR invoiceLibrary6(Map map) {
  3221. log.info("接收到的参数: {}", map);
  3222. try {
  3223. String processInstanceId = UtilMap.getString(map, "processInstanceId");
  3224. if (StringUtils.isBlank(processInstanceId)) {
  3225. log.error("processInstanceId为空");
  3226. return McR.error("400", "审批实例ID不能为空");
  3227. }
  3228. // 获取审批实例信息
  3229. String accessToken = ddClient.getAccessToken();
  3230. Map processInstance = ddClientWorkflow.getProcessInstanceId(accessToken, processInstanceId);
  3231. if (processInstance == null) {
  3232. log.error("获取审批实例失败: {}", processInstanceId);
  3233. return McR.error("500", "获取审批实例信息失败");
  3234. }
  3235. String userId = (String) processInstance.get("originatorUserId");
  3236. log.info("审批人ID: {}", userId);
  3237. List<Map> formComponentValues = (List<Map>) processInstance.get("formComponentValues");
  3238. if (formComponentValues == null || formComponentValues.isEmpty()) {
  3239. log.warn("表单数据为空");
  3240. return McR.error("400", "表单数据为空");
  3241. }
  3242. // 存储主表的字段值
  3243. String zt = null;
  3244. BigDecimal mainJe = null;
  3245. String bxlb = null;
  3246. String bm = null;
  3247. String fkzhxx = null;
  3248. String fkzh = null;
  3249. String yhqc = null;
  3250. String sfct = null;
  3251. // 遍历收集主表字段值
  3252. for (Map formComponentValue : formComponentValues) {
  3253. String id = String.valueOf(formComponentValue.get("id"));
  3254. Object value = formComponentValue.get("value");
  3255. if ("DDSelectField_VWIFJ1JZK0W0".equals(id)) {
  3256. zt = value != null ? String.valueOf(value) : "";
  3257. }
  3258. if ("MoneyField_3QS10ALLABU0".equals(id)) {
  3259. if (value instanceof BigDecimal) {
  3260. mainJe = (BigDecimal) value;
  3261. } else if (value != null) {
  3262. try {
  3263. mainJe = new BigDecimal(String.valueOf(value));
  3264. } catch (NumberFormatException e) {
  3265. log.error("金额格式转换失败: {}", value, e);
  3266. mainJe = BigDecimal.ZERO;
  3267. }
  3268. }
  3269. }
  3270. // 如果需要这两个字段,请取消注释并填写正确的字段ID
  3271. if ("DDSelectField_YLLB7AD1ATS0".equals(id)) {
  3272. bxlb = value != null ? String.valueOf(value) : "";
  3273. }
  3274. if ("DepartmentField_RF93A6VHA9C0".equals(id)) {
  3275. bm = value != null ? String.valueOf(value) : "";
  3276. }
  3277. if ("DDSelectField_IQXAGL7Z8XC0".equals(id)) {
  3278. fkzhxx = value != null ? String.valueOf(value) : "";
  3279. }
  3280. if ("TextField_3W9CLQOZZ540".equals(id)) {
  3281. fkzh = value != null ? String.valueOf(value) : "";
  3282. }
  3283. if ("TextField_9PJ4JW8KTCS0".equals(id)) {
  3284. yhqc = value != null ? String.valueOf(value) : "";
  3285. }
  3286. if ("DDSelectField_21L6KT7DT70G0".equals(id)) {
  3287. sfct = value != null ? String.valueOf(value) : "否";
  3288. }
  3289. }
  3290. // 判断主表状态 - 无发票
  3291. if ("否".equals(zt)) {
  3292. // Map<String, Object> result = new HashMap<>();
  3293. // result.put("je", mainJe);
  3294. // result.put("status", "否");
  3295. // result.put("message", "无发票报销");
  3296. // 保存基础报销记录
  3297. InvoiceLibrary baseInvoice = new InvoiceLibrary();
  3298. baseInvoice.setOaId(processInstanceId);
  3299. baseInvoice.setOaStatus("0");
  3300. baseInvoice.setInvoiceStatus("0");
  3301. baseInvoice.setDep(bm != null ? bm : "");
  3302. baseInvoice.setAccountTitle(bxlb != null ? bxlb : "");
  3303. baseInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  3304. baseInvoice.setCreatedAt(LocalDateTime.now());
  3305. baseInvoice.setUpdatedAt(LocalDateTime.now());
  3306. baseInvoice.setInvoiceCode("");
  3307. baseInvoice.setInvoiceNumber("");
  3308. baseInvoice.setInvoiceType("");
  3309. baseInvoice.setAmount(BigDecimal.ZERO);
  3310. baseInvoice.setTaxAmount(BigDecimal.ZERO);
  3311. baseInvoice.setTotalAmount(BigDecimal.ZERO);
  3312. baseInvoice.setBuyerName("");
  3313. baseInvoice.setFormName("批量付款申请(一对多)");
  3314. baseInvoice.setPaySubject(fkzhxx != null ? fkzhxx : "");
  3315. baseInvoice.setPayAccount(fkzh != null ? fkzh : "");
  3316. baseInvoice.setBankName(yhqc != null ? yhqc : "");
  3317. baseInvoice.setIsLongTerm(sfct != null ? sfct : "");
  3318. baseInvoice.setBuyerTaxId("");
  3319. baseInvoice.setSellerName("");
  3320. baseInvoice.setSellerTaxId("");
  3321. baseMapper.insert(baseInvoice);
  3322. addWorkflowComment(processInstanceId, userId, "报销总金额:"+mainJe);
  3323. return McR.success();
  3324. }
  3325. // 判断主表状态 - 有发票
  3326. if ("是".equals(zt)) {
  3327. for (Map formComponentValue : formComponentValues) {
  3328. String id = String.valueOf(formComponentValue.get("id"));
  3329. if ("DDAttachment_1C1GRYIRFJOG0".equals(id)) {
  3330. // 获取附件value(它是一个JSON字符串)
  3331. Object attachmentValue = formComponentValue.get("value");
  3332. List<Map> attachmentList = null;
  3333. // 解析附件JSON字符串
  3334. if (attachmentValue != null) {
  3335. String attachmentJsonStr = String.valueOf(attachmentValue);
  3336. if (StringUtils.isNotBlank(attachmentJsonStr)) {
  3337. try {
  3338. attachmentList = JSONObject.parseArray(attachmentJsonStr, Map.class);
  3339. log.info("解析附件列表成功,共{}个附件", attachmentList != null ? attachmentList.size() : 0);
  3340. } catch (Exception e) {
  3341. log.error("解析附件JSON失败: {}", attachmentJsonStr, e);
  3342. return McR.error("400", "附件格式解析失败");
  3343. }
  3344. }
  3345. }
  3346. try {
  3347. List<Object> allResults = new ArrayList<>();
  3348. List<InvoiceLibrary> invoiceList = new ArrayList<>();
  3349. // 用于校验的集合
  3350. List<String> invoiceNumberList = new ArrayList<>();
  3351. Map<String, String> invoiceNumberToFileName = new HashMap<>();
  3352. List<String> invalidBuyerTaxIds = new ArrayList<>();
  3353. // 存储第一遍解析的发票数据,避免重复解析
  3354. List<Map<String, Object>> parsedInvoiceDataList = new ArrayList<>();
  3355. if (attachmentList != null && !attachmentList.isEmpty()) {
  3356. for (Map attachment : attachmentList) {
  3357. String fileId = UtilMap.getString(attachment, "fileId");
  3358. String fileType = UtilMap.getString(attachment, "fileType");
  3359. String fileName = UtilMap.getString(attachment, "fileName");
  3360. // 参数校验
  3361. if (StringUtils.isBlank(fileId) || StringUtils.isBlank(fileType)) {
  3362. log.warn("文件信息不完整: fileId={}, fileType={}, fileName={}", fileId, fileType, fileName);
  3363. continue;
  3364. }
  3365. try {
  3366. String filePath = downloadPath + fileId + "." + fileType;
  3367. downloadDdFile(processInstanceId, fileId, filePath);
  3368. String hz = "qw/files/" + fileId + "." + fileType;
  3369. Map fileMap = new HashMap();
  3370. fileMap.put("url", url + hz);
  3371. fileMap.put("size", new File(filePath).length() / 1024f / 1024f);
  3372. fileMap.put("isPdf", "pdf".equalsIgnoreCase(fileType));
  3373. McR<Object> result = processMixedInvoice(fileMap);
  3374. Object responseData = result.getData();
  3375. // 解析发票数据
  3376. if (responseData != null) {
  3377. String jsonStr = JSONObject.toJSONString(responseData);
  3378. JSONObject jsonObject = JSONObject.parseObject(jsonStr);
  3379. JSONArray resultArray = jsonObject.getJSONArray("result");
  3380. if (resultArray != null && !resultArray.isEmpty()) {
  3381. JSONObject invoice = resultArray.getJSONObject(0);
  3382. String invoiceNumber = invoice.getString("serial") != null ? invoice.getString("serial") : "";
  3383. String buyerTaxId = invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "";
  3384. String buyerName = invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "";
  3385. // 保存解析的数据供后续使用
  3386. Map<String, Object> parsedData = new HashMap<>();
  3387. parsedData.put("invoice", invoice);
  3388. parsedData.put("fileId", fileId);
  3389. parsedData.put("fileName", fileName);
  3390. parsedData.put("fileType", fileType);
  3391. parsedData.put("responseData", responseData);
  3392. parsedInvoiceDataList.add(parsedData);
  3393. // ========== 校验1:抬头校验 ==========
  3394. if (StringUtils.isNotBlank(buyerName)) {
  3395. CompanyTitle existingTitle = companyTitleMapper.selectByTaxId(buyerName);
  3396. if (existingTitle == null) {
  3397. String errorInfo = String.format("发票: %s, 购买方名称: %s, 税号: %s",
  3398. fileName, buyerName, buyerTaxId);
  3399. invalidBuyerTaxIds.add(errorInfo);
  3400. log.warn("抬头校验失败: 税号={} 不存在于抬头库, 文件名={}", buyerTaxId, fileName);
  3401. }
  3402. } else {
  3403. String errorInfo = String.format("发票: %s, 购买方税号为空", fileName);
  3404. invalidBuyerTaxIds.add(errorInfo);
  3405. log.warn("抬头校验失败: 购买方税号为空, 文件名={}", fileName);
  3406. }
  3407. // ========== 校验2:发票号重复校验(与数据库中的发票号比较) ==========
  3408. if (StringUtils.isNotBlank(invoiceNumber)) {
  3409. // 检查数据库中是否已存在相同的发票号
  3410. LambdaQueryWrapper<InvoiceLibrary> queryWrapper = new LambdaQueryWrapper<>();
  3411. queryWrapper.eq(InvoiceLibrary::getInvoiceNumber, invoiceNumber);
  3412. InvoiceLibrary existingInvoice = baseMapper.selectOne(queryWrapper);
  3413. if (existingInvoice != null) {
  3414. String errorMsg = String.format("发票号重复: %s (当前文件: %s, 已存在于OA审批单: %s)",
  3415. invoiceNumber, fileName, existingInvoice.getOaId());
  3416. log.error(errorMsg);
  3417. // 终止流程并回写原因
  3418. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  3419. return McR.error("400", errorMsg);
  3420. }
  3421. // 同时检查同一审批单内是否有重复(防止同一单据内上传重复发票)
  3422. if (invoiceNumberList.contains(invoiceNumber)) {
  3423. String errorMsg = String.format("当前审批单内发票号重复: %s (当前文件: %s, 已存在文件: %s)",
  3424. invoiceNumber, fileName, invoiceNumberToFileName.get(invoiceNumber));
  3425. log.error(errorMsg);
  3426. // 终止流程并回写原因
  3427. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  3428. return McR.error("400", errorMsg);
  3429. }
  3430. invoiceNumberList.add(invoiceNumber);
  3431. invoiceNumberToFileName.put(invoiceNumber, fileName);
  3432. }
  3433. } else {
  3434. log.warn("发票解析结果为空: fileName={}", fileName);
  3435. }
  3436. } else {
  3437. log.warn("发票OCR识别结果为空: fileName={}", fileName);
  3438. }
  3439. } catch (Exception e) {
  3440. log.error("发票解析异常: fileId={}, fileName={}", fileId, fileName, e);
  3441. String errorMsg = "发票解析失败: " + e.getMessage();
  3442. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  3443. return McR.error("400", errorMsg);
  3444. }
  3445. }
  3446. } else {
  3447. log.warn("附件列表为空");
  3448. return McR.error("400", "请上传发票附件");
  3449. }
  3450. // ========== 抬头校验失败处理 ==========
  3451. if (!invalidBuyerTaxIds.isEmpty()) {
  3452. String errorMsg = "发票抬头有误,以下发票的购买方税号不在公司抬头库中:\n" + String.join("\n", invalidBuyerTaxIds);
  3453. log.error(errorMsg);
  3454. // 终止流程
  3455. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  3456. return McR.error("400", errorMsg);
  3457. }
  3458. BigDecimal totalAmount = BigDecimal.ZERO;
  3459. // ========== 第二遍遍历:保存发票数据(校验通过后) ==========
  3460. for (Map<String, Object> parsedData : parsedInvoiceDataList) {
  3461. JSONObject invoice = (JSONObject) parsedData.get("invoice");
  3462. Object responseData = parsedData.get("responseData");
  3463. allResults.add(responseData);
  3464. BigDecimal amount = invoice.getBigDecimal("amount");
  3465. if (amount != null) {
  3466. totalAmount = totalAmount.add(amount);
  3467. }
  3468. // 构建InvoiceLibrary对象
  3469. InvoiceLibrary invoiceLibrary = new InvoiceLibrary();
  3470. invoiceLibrary.setOaId(processInstanceId);
  3471. invoiceLibrary.setInvoiceCode(invoice.getString("code") != null ? invoice.getString("code") : "");
  3472. invoiceLibrary.setInvoiceNumber(invoice.getString("serial") != null ? invoice.getString("serial") : "");
  3473. invoiceLibrary.setInvoiceType(invoice.getString("kindName") != null ? invoice.getString("kindName") : "");
  3474. String date = invoice.getString("date");
  3475. if (StringUtils.isNotBlank(date)) {
  3476. try {
  3477. invoiceLibrary.setInvoiceDate(LocalDate.parse(date.trim()));
  3478. } catch (Exception e) {
  3479. log.warn("日期解析失败: {}", date, e);
  3480. }
  3481. }
  3482. invoiceLibrary.setAmount(invoice.getBigDecimal("excludingTax") != null ? invoice.getBigDecimal("excludingTax") : BigDecimal.ZERO);
  3483. invoiceLibrary.setTaxAmount(invoice.getBigDecimal("tax") != null ? invoice.getBigDecimal("tax") : BigDecimal.ZERO);
  3484. invoiceLibrary.setTotalAmount(invoice.getBigDecimal("amount") != null ? invoice.getBigDecimal("amount") : BigDecimal.ZERO);
  3485. invoiceLibrary.setBuyerName(invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "");
  3486. invoiceLibrary.setBuyerTaxId(invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "");
  3487. invoiceLibrary.setSellerName(invoice.getString("sellerName") != null ? invoice.getString("sellerName") : "");
  3488. invoiceLibrary.setSellerTaxId(invoice.getString("sellerTaxId") != null ? invoice.getString("sellerTaxId") : "");
  3489. invoiceLibrary.setOaStatus("0");
  3490. invoiceLibrary.setInvoiceStatus("0");
  3491. invoiceLibrary.setDep(bm != null ? bm : "");
  3492. invoiceLibrary.setAccountTitle(bxlb != null ? bxlb : "");
  3493. invoiceLibrary.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  3494. invoiceLibrary.setCreatedAt(LocalDateTime.now());
  3495. invoiceLibrary.setUpdatedAt(LocalDateTime.now());
  3496. invoiceLibrary.setFormName("批量付款申请(一对多)");
  3497. invoiceLibrary.setPaySubject(fkzhxx != null ? fkzhxx : "");
  3498. invoiceLibrary.setPayAccount(fkzh != null ? fkzh : "");
  3499. invoiceLibrary.setBankName(yhqc != null ? yhqc : "");
  3500. invoiceLibrary.setIsLongTerm(sfct != null ? sfct : "");
  3501. invoiceList.add(invoiceLibrary);
  3502. }
  3503. // 保存发票记录
  3504. if (!invoiceList.isEmpty()) {
  3505. for (InvoiceLibrary invoice : invoiceList) {
  3506. baseMapper.insert(invoice);
  3507. }
  3508. log.info("成功保存 {} 条发票记录", invoiceList.size());
  3509. } else {
  3510. log.warn("没有有效的发票数据可保存");
  3511. }
  3512. addWorkflowComment1(processInstanceId, userId, totalAmount);
  3513. return McR.success(allResults);
  3514. } catch (Exception e) {
  3515. log.error("解析子表数据失败", e);
  3516. return McR.error("300", "解析数据失败: " + e.getMessage());
  3517. }
  3518. }
  3519. }
  3520. }
  3521. return McR.success();
  3522. } catch (Exception e) {
  3523. log.error("处理审批实例失败", e);
  3524. return McR.error("500", "系统处理失败: " + e.getMessage());
  3525. }
  3526. }
  3527. @Override
  3528. public void updateSfctByOaId(Map map) {
  3529. try {
  3530. String oaId = UtilMap.getString(map, "oaId");
  3531. // String sfct = UtilMap.getString(map, "sfct");
  3532. String fkzhxx = UtilMap.getString(map, "fkzhxx");
  3533. String fkzh = UtilMap.getString(map, "fkzh");
  3534. String yhqc = UtilMap.getString(map, "yhqc");
  3535. // sfct = (sfct == null || sfct.trim().isEmpty()) ? "否" : sfct;
  3536. // 创建更新条件
  3537. LambdaUpdateWrapper<InvoiceLibrary> updateWrapper = new LambdaUpdateWrapper<>();
  3538. updateWrapper.eq(InvoiceLibrary::getOaId, oaId)
  3539. // .set(InvoiceLibrary::getIsLongTerm, sfct)
  3540. .set(InvoiceLibrary::getPayAccount, fkzh)
  3541. .set(InvoiceLibrary::getBankName, yhqc)
  3542. .set(InvoiceLibrary::getPaySubject, fkzhxx)
  3543. .set(InvoiceLibrary::getUpdatedAt, LocalDateTime.now());
  3544. int updateCount = baseMapper.update(null, updateWrapper);
  3545. if (updateCount > 0) {
  3546. log.info("更新成功: 共更新{}条记录", updateCount);
  3547. } else {
  3548. log.warn("未找到符合条件的记录: oaId={}", oaId);
  3549. }
  3550. } catch (Exception e) {
  3551. throw new RuntimeException("更新OA状态失败", e);
  3552. }
  3553. }
  3554. /**
  3555. * 添加钉钉审批评论
  3556. */
  3557. private void addWorkflowComment(String processInstanceId, String userId, Object totalAmount) {
  3558. try {
  3559. Map<String, Object> body = new HashMap<>();
  3560. body.put("processInstanceId", processInstanceId);
  3561. body.put("text", totalAmount);
  3562. body.put("commentUserId", userId);
  3563. String result = UtilHttp.doPost(
  3564. "https://api.dingtalk.com/v1.0/workflow/processInstances/comments",
  3565. ddClient.initTokenHeader(),
  3566. null,
  3567. body
  3568. );
  3569. log.info("添加审批评论成功: {}", result);
  3570. } catch (Exception e) {
  3571. log.error("添加审批评论失败", e);
  3572. }
  3573. }
  3574. private void addWorkflowComment1(String processInstanceId, String userId, BigDecimal totalAmount) {
  3575. try {
  3576. Map<String, Object> body = new HashMap<>();
  3577. body.put("processInstanceId", processInstanceId);
  3578. body.put("text", "发票总金额:"+totalAmount);
  3579. body.put("commentUserId", userId);
  3580. String result = UtilHttp.doPost(
  3581. "https://api.dingtalk.com/v1.0/workflow/processInstances/comments",
  3582. ddClient.initTokenHeader(),
  3583. null,
  3584. body
  3585. );
  3586. log.info("添加审批评论成功: {}", result);
  3587. } catch (Exception e) {
  3588. log.error("添加审批评论失败", e);
  3589. }
  3590. }
  3591. /**
  3592. * 从 Map 中获取字符串值
  3593. */
  3594. private String getStringValue(Map<String, Object> map, String key) {
  3595. Object value = map.get(key);
  3596. return value != null ? String.valueOf(value) : "";
  3597. }
  3598. /**
  3599. * 从 Map 中获取 BigDecimal 值
  3600. */
  3601. private BigDecimal getBigDecimalValue(Map<String, Object> map, String key) {
  3602. Object value = map.get(key);
  3603. if (value == null) {
  3604. return BigDecimal.ZERO;
  3605. }
  3606. if (value instanceof BigDecimal) {
  3607. return (BigDecimal) value;
  3608. }
  3609. try {
  3610. return new BigDecimal(String.valueOf(value));
  3611. } catch (NumberFormatException e) {
  3612. return BigDecimal.ZERO;
  3613. }
  3614. }
  3615. /**
  3616. * 空字符串转换
  3617. */
  3618. private String nullToEmpty(String str) {
  3619. return str != null ? str : "";
  3620. }
  3621. private void downloadDdFile(String processInstanceId, String fileId, String downloadPath) {
  3622. try {
  3623. Map body = new HashMap();
  3624. body.put("processInstanceId", processInstanceId);
  3625. body.put("fileId", fileId);
  3626. DDR_New ddrNew = (DDR_New) UtilHttp.doPost("https://api.dingtalk.com/v1.0/workflow/processInstances/spaces/files/urls/download", ddClient.initTokenHeader(), null, body, DDR_New.class);
  3627. //2、执行下载
  3628. Map result = (Map) ddrNew.getResult();
  3629. String downloadUri = UtilMap.getString(result, "downloadUri");
  3630. downloadFile(downloadUri, downloadPath);
  3631. } catch (Exception e) {
  3632. throw new RuntimeException(e);
  3633. }
  3634. }
  3635. //文件下载到本地
  3636. private void downloadFile(String downloadUri, String downloadPath) {
  3637. try {
  3638. URL url = new URL(downloadUri);
  3639. HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
  3640. int responseCode = httpConn.getResponseCode();
  3641. // 检查HTTP响应代码是否为200
  3642. if (responseCode == HttpURLConnection.HTTP_OK) {
  3643. InputStream inputStream = httpConn.getInputStream();
  3644. FileOutputStream outputStream = new FileOutputStream(downloadPath);
  3645. byte[] buffer = new byte[4096];
  3646. int bytesRead = -1;
  3647. while ((bytesRead = inputStream.read(buffer)) != -1) {
  3648. outputStream.write(buffer, 0, bytesRead);
  3649. }
  3650. outputStream.close();
  3651. inputStream.close();
  3652. } else {
  3653. System.out.println("无法下载文件。HTTP响应代码: " + responseCode);
  3654. }
  3655. httpConn.disconnect();
  3656. } catch (Exception e) {
  3657. throw new RuntimeException(e);
  3658. }
  3659. }
  3660. /**
  3661. * 私有化方法:混票识别处理
  3662. *
  3663. * @param data 包含url、size、isPdf的Map参数
  3664. * @return 识别结果
  3665. */
  3666. private McR<Object> processMixedInvoice(Map<String, Object> data) throws TencentCloudSDKException {
  3667. McException.assertParamException_Null(data, "url");
  3668. String image = String.valueOf(data.get("url"));
  3669. log.info("混票识别, 免登地址, {}", image);
  3670. // 非PDF, 且内存大于3M, 压缩后上传
  3671. if (UtilMap.getFloat(data, "size") > 3.0f && !UtilMap.getBoolean(data, "isPdf")) {
  3672. image = imageUrlConvertBase64(image);
  3673. }
  3674. if (UtilMap.getFloat(data, "size") > 6.0f && UtilMap.getBoolean(data, "isPdf")) {
  3675. image = pdfUrlConvertBase64(image);
  3676. }
  3677. List<Map> invoices = (List<Map>) txyInvoice.doRecognizeGeneralInvoice(image).get("MixedInvoiceItems");
  3678. System.out.println("invoices=====" + invoices);
  3679. List<String> nos = new ArrayList<>();
  3680. List<McInvoiceDto> result = invoices.stream().map(item -> {
  3681. Map prop = UtilMap.getMap(UtilMap.getMap(item, "SingleInvoiceInfos"), UtilMap.getString(item, "SubType"));
  3682. String no = UtilMap.getString(prop, "Number");
  3683. // if ("OtherInvoice".equals(UtilMap.getString(item, "SubType")) && StringUtils.isBlank(no)) {
  3684. // log.info("忽略无发票号码的 OCR 非票据内容: subType={}, description={}",
  3685. // UtilMap.getString(item, "SubType"), UtilMap.getString(item, "SubTypeDescription"));
  3686. // return null;
  3687. // }
  3688. String taxRate = null;
  3689. // List<Map<String, Object>> vatInvoiceItemInfos = UtilMap.getList(prop, "VatInvoiceItemInfos");
  3690. // 优先使用 VatInvoiceItemInfos,如果为空则使用 VatElectronicItems
  3691. List<Map<String, Object>> vatInvoiceItemInfos =
  3692. UtilMap.getList(prop, "VatInvoiceItemInfos") != null && !UtilMap.getList(prop, "VatInvoiceItemInfos").isEmpty()
  3693. ? UtilMap.getList(prop, "VatInvoiceItemInfos")
  3694. : UtilMap.getList(prop, "VatElectronicItems");
  3695. // if (!vatInvoiceItemInfos.isEmpty()) {
  3696. // taxRate = UtilMap.getString(vatInvoiceItemInfos.get(0), "TaxRate");
  3697. // }
  3698. if (!vatInvoiceItemInfos.isEmpty()) {
  3699. String originalTaxRate = UtilMap.getString(vatInvoiceItemInfos.get(0), "TaxRate");
  3700. // 判断是否为有效数字(包括带%号的格式,如"9%")
  3701. if (StringUtils.isNotBlank(originalTaxRate)) {
  3702. // 移除百分号并尝试解析为数字
  3703. String cleanedTaxRate = originalTaxRate.replace("%", "").trim();
  3704. try {
  3705. // 尝试转换为数字
  3706. new BigDecimal(cleanedTaxRate);
  3707. taxRate = originalTaxRate; // 保持原值(可能带%)
  3708. } catch (NumberFormatException e) {
  3709. // 如果不是数字(如"不征税"、"免税"等),赋值为"0"
  3710. taxRate = "0";
  3711. log.debug("税率格式异常,原始值: {}, 已设置为0", originalTaxRate);
  3712. }
  3713. } else {
  3714. taxRate = "0";
  3715. }
  3716. } else {
  3717. taxRate = "0";
  3718. }
  3719. if (nos.contains(no)) {
  3720. return null;
  3721. }
  3722. nos.add(no);
  3723. String kind = UtilMap.getString(item, "TypeDescription");
  3724. String invoiceName = UtilMap.getString(item, "SubTypeDescription");
  3725. if (kind.equals("全电发票")) {
  3726. if (invoiceName.contains("铁路电子客票")) {
  3727. kind = "火车票";
  3728. } else if (invoiceName.contains("机票行程单")) {
  3729. kind = "机票行程单";
  3730. } else if (invoiceName.contains("专用发票")) {
  3731. kind = "全电专用发票";
  3732. } else {
  3733. kind = "全电普通发票";
  3734. }
  3735. }
  3736. if (kind.equals("增值税发票")) {
  3737. if (invoiceName.contains("区块链电子发票")) {
  3738. kind = "区块链电子发票";
  3739. } else if (invoiceName.contains("增值税专用发票")) {
  3740. kind = "增值税专用发票";
  3741. } else if (invoiceName.contains("增值税普通发票")) {
  3742. kind = "增值税普通发票";
  3743. } else if (invoiceName.contains("增值税电子专用发票")) {
  3744. kind = "增值税电子专用发票";
  3745. } else if (invoiceName.contains("增值税电子普通发票")) {
  3746. kind = "增值税电子普通发票";
  3747. } else {
  3748. kind = "增值税普通发票";
  3749. }
  3750. }
  3751. if (isTitleValidationExempt(invoiceName)) {
  3752. kind = "普通发票";
  3753. }
  3754. McInvoiceDto invoiceDto = McInvoiceDto.builder()
  3755. .name(UtilMap.getString(item, "SubTypeDescription"))
  3756. .kindName(kind)
  3757. .kind(UtilMap.getInt(item, "Type"))
  3758. .taxRate(taxRate)
  3759. .code(UtilMap.getString(prop, "Code"))
  3760. .serial(UtilMap.getString(prop, "Number"))
  3761. .date(UtilString.replaceDateZH_cn(UtilMap.getString(prop, "Date")))
  3762. .checkCode(UtilMap.getString(prop, "CheckCode"))
  3763. .amount(UtilNumber.setBigDecimal(UtilMap.getString_first(prop, "Total", "Fare")))
  3764. .tax(UtilNumber.setBigDecimal(UtilMap.getString_first(prop, "Tax")))
  3765. .excludingTax(UtilNumber.setBigDecimal(UtilMap.getString(prop, "PretaxAmount")))
  3766. .buyerName(StringUtils.defaultString(guyuanNameRepalce(UtilMap.getString(prop, "Buyer"))))
  3767. .buyerTaxId(StringUtils.isBlank(UtilMap.getString(prop, "BuyerTaxID"))
  3768. ? "" : UtilMap.getString(prop, "BuyerTaxID"))
  3769. .sellerName(guyuanNameRepalce(UtilMap.getString_first(prop, "Seller", "Issuer")))
  3770. .sellerTaxId(UtilMap.getString_first(prop, "SellerTaxID", "AgentCode"))
  3771. .passengerName(UtilMap.getString_first(prop, "Name", "UserName"))
  3772. .seatType(UtilMap.getString(prop, "Seat"))
  3773. .departureTime(UtilString.replaceDateZH_cn(UtilMap.getString(prop, "DateGetOn"))
  3774. + " " + UtilMap.getString(prop, "TimeGetOn"))
  3775. .departurePort(UtilMap.getString_first(prop, "StationGetOn", "Entrance", "Place"))
  3776. .arrivePort(UtilMap.getString_first(prop, "StationGetOff", "Exit"))
  3777. .trainNo(UtilMap.getString_first(prop, "TrainNumber", "LicensePlate"))
  3778. .insuranceCosts(UtilNumber.setBigDecimal(UtilMap.getString(prop, "Insurance")))
  3779. .fuelCosts(UtilNumber.setBigDecimal(UtilMap.getString(prop, "FuelSurcharge")))
  3780. .constructionCosts(UtilNumber.setBigDecimal(UtilMap.getString(prop, "AirDevelopmentFund")))
  3781. .build();
  3782. if ("机票行程单".equals(kind)) {
  3783. invoiceDto.setSellerName(UtilMap.getString_first(prop, "Issuer"));
  3784. invoiceDto.setSellerTaxId(UtilMap.getString_first(prop, "Seller"));
  3785. Map flight = (Map) UtilMap.getList(prop, "FlightItems").get(0);
  3786. invoiceDto.setDepartureTime(UtilString.replaceDateZH_cn(UtilMap.getString(item, "DateGetOn"))
  3787. + " " + UtilMap.getString(prop, "TimeGetOn"));
  3788. invoiceDto.setDeparturePort(UtilMap.getString(flight, "StationGetOn"));
  3789. invoiceDto.setArrivePort(UtilMap.getString(flight, "StationGetOff"));
  3790. invoiceDto.setSeatType(UtilMap.getString(flight, "Seat"));
  3791. }
  3792. if ("出租车发票".equals(item.get("TypeDescription"))) {
  3793. invoiceDto.setDepartureTime(UtilMap.getString(prop, "TimeGetOn")
  3794. + " ~ " + UtilMap.getString(prop, "TimeGetOff"));
  3795. }
  3796. return invoiceDto;
  3797. }).filter(Objects::nonNull).collect(Collectors.toList());
  3798. return McR.success(McInvoiceDto.formatResponse(result));
  3799. }
  3800. private boolean isTitleValidationExempt(String invoiceName) {
  3801. return Arrays.asList(
  3802. "出租车发票",
  3803. "汽车票",
  3804. "通用机打发票",
  3805. "定额发票",
  3806. "轮船票",
  3807. "医疗门诊收费票据(电子)",
  3808. "医疗住院收费票据(电子)",
  3809. "过路过桥费发票"
  3810. ).contains(invoiceName);
  3811. }
  3812. private static String guyuanNameRepalce(String name) {
  3813. name = name.replaceAll(" ", "");
  3814. if (name.contains("谷元")) {
  3815. return UtilString.replaceBracketIsWhole(name);
  3816. } else {
  3817. return UtilString.replaceBracketIsSemiangle(name);
  3818. }
  3819. }
  3820. /// url压缩转base64
  3821. @SneakyThrows
  3822. private String imageUrlConvertBase64(String imageUrl) {
  3823. ByteArrayOutputStream out = new ByteArrayOutputStream();
  3824. // scale(比例), outputQuality(质量)
  3825. Thumbnails.fromURLs(Arrays.asList(new URL(imageUrl))).scale(0.5f).outputQuality(0.25f).toOutputStream(out);
  3826. InputStream inputStream = new ByteArrayInputStream(out.toByteArray());
  3827. //转换为base64
  3828. byte[] bytes = IOUtils.toByteArray(inputStream);
  3829. return Base64.getEncoder().encodeToString(bytes);
  3830. }
  3831. @SneakyThrows
  3832. private String pdfUrlConvertBase64(String pdfUrl) {
  3833. String fileName = "tmp_" + new Date().getTime() + ".pdf";
  3834. // 下载文件
  3835. File file = UtilFile.mkdirIfNot(fileName, filePath.getPath().getTmp());
  3836. UtilHttp.doDownload(pdfUrl, file);
  3837. // PDF压缩
  3838. PdfDocument doc = new PdfDocument(); // 创建PdfDocument类的对象
  3839. doc.loadFromFile(file.getAbsolutePath()); // 加载PDF文档
  3840. doc.getFileInfo().setIncrementalUpdate(false); // 禁用增量更新
  3841. doc.setCompressionLevel(PdfCompressionLevel.Normal); // 将压缩级别设置为最佳
  3842. // 遍历文档页面
  3843. for (int i = 0; i < doc.getPages().getCount(); i++) {
  3844. PdfPageBase page = doc.getPages().get(i); // 获取指定页面
  3845. PdfImageInfo[] images = page.getImagesInfo(); // 获取每个页面的图像信息集合
  3846. // 遍历集合中的所有项目
  3847. if (images != null && images.length > 0)
  3848. for (int j = 0; j < images.length; j++) {
  3849. PdfImageInfo image = images[j]; // 获取指定图片
  3850. PdfBitmap bp = new PdfBitmap(image.getImage());
  3851. bp.setQuality(30); // 设置压缩质量
  3852. page.replaceImage(j, bp); // 将原始图像替换为压缩图像
  3853. }
  3854. }
  3855. // 将结果文档保存至另一个PDF文档中: 覆盖
  3856. doc.saveToFile(file.getAbsolutePath());
  3857. doc.close();
  3858. // PDF转base64, 无需透出本地文件地址
  3859. String base64 = UtilFile.fileToBase64(file.getAbsolutePath());
  3860. // 删除临时PDF文件
  3861. UtilFile.deleteFile(file.getAbsolutePath());
  3862. return base64;
  3863. }
  3864. @GetMapping("/files/{fileId}")
  3865. public ResponseEntity<Resource> getFileResource(
  3866. @PathVariable String fileId,
  3867. @RequestParam(defaultValue = "download") String option) throws IOException {
  3868. // 根据fileId获取实际文件路径,这里简化处理,实际可能需要从数据库查询
  3869. Path filePath = Paths.get(downloadPath).resolve(fileId).normalize();
  3870. // Path filePath = Paths.get("D://qiwang//files//").resolve(fileId).normalize();
  3871. // 检查文件是否存在
  3872. if (!Files.exists(filePath)) {
  3873. return ResponseEntity.notFound().build();
  3874. }
  3875. // 创建Resource对象
  3876. Resource resource = new UrlResource(filePath.toUri());
  3877. // 根据选项设置响应头
  3878. HttpHeaders headers = new HttpHeaders();
  3879. if ("preview".equalsIgnoreCase(option)) {
  3880. // 预览模式 - 尝试确定内容类型
  3881. String contentType = Files.probeContentType(filePath);
  3882. // 强制修正 PDF 的 Content-Type
  3883. if (filePath.toString().toLowerCase().endsWith(".pdf")) {
  3884. contentType = "application/pdf";
  3885. } else if (contentType == null) {
  3886. contentType = "application/octet-stream";
  3887. }
  3888. headers.setContentType(MediaType.parseMediaType(contentType));
  3889. headers.add("Content-Disposition", "inline; filename=\"" + resource.getFilename() + "\"");
  3890. headers.add("X-Content-Type-Options", "nosniff"); // 防止浏览器忽略 Content-Type
  3891. } else {
  3892. // 下载模式 - 默认处理
  3893. headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
  3894. headers.add("Content-Disposition", "attachment; filename=\"" + resource.getFilename() + "\"");
  3895. }
  3896. return ResponseEntity.ok()
  3897. .headers(headers)
  3898. .contentLength(resource.contentLength())
  3899. .body(resource);
  3900. }
  3901. }