InvoiceLibraryServiceImpl.java 216 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600
  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://24120b4f.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. log.info("审批人ID: {}", userId);
  116. List<Map> formComponentValues = (List<Map>) processInstance.get("formComponentValues");
  117. if (formComponentValues == null || formComponentValues.isEmpty()) {
  118. log.warn("表单数据为空");
  119. return McR.error("400", "表单数据为空");
  120. }
  121. // 存储主表的字段值
  122. String zt = null;
  123. BigDecimal mainJe = null;
  124. String bxlb = null;
  125. String bm = null;
  126. String fkzhxx = null;
  127. String fkzh = null;
  128. String yhqc = null;
  129. String sfct = null;
  130. // 遍历收集主表字段值
  131. for (Map formComponentValue : formComponentValues) {
  132. String id = String.valueOf(formComponentValue.get("id"));
  133. Object value = formComponentValue.get("value");
  134. if ("DDSelectField_4SITXLYUEO80".equals(id)) {
  135. zt = value != null ? String.valueOf(value) : "";
  136. }
  137. if ("MoneyField_3QZLY8BD3780".equals(id)) {
  138. if (value instanceof BigDecimal) {
  139. mainJe = (BigDecimal) value;
  140. } else if (value != null) {
  141. try {
  142. mainJe = new BigDecimal(String.valueOf(value));
  143. } catch (NumberFormatException e) {
  144. log.error("金额格式转换失败: {}", value);
  145. mainJe = BigDecimal.ZERO;
  146. }
  147. }
  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. if ("DDSelectField_DEKPLHARB6O0".equals(id)) {
  165. sfct = value != null ? String.valueOf(value) : "否";
  166. }
  167. }
  168. // 判断主表状态 - 无发票
  169. if ("否".equals(zt)) {
  170. // 保存基础报销记录
  171. InvoiceLibrary baseInvoice = new InvoiceLibrary();
  172. baseInvoice.setOaId(processInstanceId);
  173. baseInvoice.setOaStatus("0");
  174. baseInvoice.setInvoiceStatus("0");
  175. baseInvoice.setDep(bm != null ? bm : "");
  176. baseInvoice.setAccountTitle(bxlb != null ? bxlb : "");
  177. baseInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  178. baseInvoice.setCreatedAt(LocalDateTime.now());
  179. baseInvoice.setUpdatedAt(LocalDateTime.now());
  180. baseInvoice.setInvoiceCode("");
  181. baseInvoice.setInvoiceNumber("");
  182. baseInvoice.setInvoiceType("");
  183. baseInvoice.setAmount(BigDecimal.ZERO);
  184. baseInvoice.setTaxAmount(BigDecimal.ZERO);
  185. baseInvoice.setTotalAmount(BigDecimal.ZERO);
  186. baseInvoice.setBuyerName("");
  187. baseInvoice.setBuyerTaxId("");
  188. baseInvoice.setSellerName("");
  189. baseInvoice.setSellerTaxId("");
  190. baseInvoice.setFormName("员工费用报销");
  191. baseInvoice.setPaySubject(fkzhxx != null ? fkzhxx : "");
  192. baseInvoice.setPayAccount(fkzh != null ? fkzh : "");
  193. baseInvoice.setBankName(yhqc != null ? yhqc : "");
  194. baseInvoice.setIsLongTerm(sfct != null ? sfct : "");
  195. baseInvoice.setHasInvoice("否");
  196. baseInvoice.setDetailAmount(null);
  197. baseInvoice.setSharedTaxAmount(null);
  198. baseInvoice.setSharedAmount(null);
  199. baseInvoice.setSharedRate(null);
  200. baseMapper.insert(baseInvoice);
  201. addWorkflowComment(processInstanceId, userId, "报销总金额:" + mainJe);
  202. return McR.success();
  203. }
  204. // 判断主表状态 - 有发票
  205. if ("是".equals(zt)) {
  206. for (Map formComponentValue : formComponentValues) {
  207. String id = String.valueOf(formComponentValue.get("id"));
  208. if ("TableField_1A1CDMEN8DDS0".equals(id)) {
  209. String tableFieldValue = String.valueOf(formComponentValue.get("value"));
  210. log.info("子表数据: {}", tableFieldValue);
  211. try {
  212. List<Map> tableRows = (List<Map>) JSONObject.parse(tableFieldValue);
  213. if (tableRows == null || tableRows.isEmpty()) {
  214. log.warn("子表数据为空");
  215. return McR.error("400", "发票明细为空");
  216. }
  217. List<Object> allResults = new ArrayList<>();
  218. List<InvoiceLibrary> invoiceList = new ArrayList<>();
  219. // 存储第一遍解析的发票数据,避免重复解析
  220. List<Map<String, Object>> parsedInvoiceDataList = new ArrayList<>();
  221. // 存储共享发票数据
  222. List<Map<String, Object>> sharedInvoiceDataList = new ArrayList<>();
  223. // 用于校验的集合
  224. List<String> invoiceNumberList = new ArrayList<>();
  225. Map<String, String> invoiceNumberToFileName = new HashMap<>();
  226. List<String> invalidBuyerTaxIds = new ArrayList<>();
  227. // 存储税率值,供共享发票使用
  228. BigDecimal sharedTaxRate = BigDecimal.ZERO;
  229. String sharedKind = "";
  230. // ========== 第一遍遍历:收集并校验所有发票数据 ==========
  231. for (Map row : tableRows) {
  232. List<Map> rowValues = (List<Map>) row.get("rowValue");
  233. String hasInvoice = null;
  234. String cdbm = null;
  235. String detailje = null;
  236. List<Map> attachmentList = null;
  237. for (Map rowItem : rowValues) {
  238. String key = String.valueOf(rowItem.get("key"));
  239. if ("DDSelectField_1ORUK0KIM5D6O".equals(key)) {
  240. Object value = rowItem.get("value");
  241. hasInvoice = value != null ? String.valueOf(value) : "";
  242. }
  243. if ("DepartmentField_ATSWRJBJ7K00".equals(key)) {
  244. Object value = rowItem.get("value");
  245. cdbm = value != null ? String.valueOf(value) : "";
  246. }
  247. if ("NumberField_1622XLFLYEWW0".equals(key)) {
  248. Object value = rowItem.get("value");
  249. detailje = value != null ? String.valueOf(value) : "";
  250. }
  251. if ("DDAttachment_Z02OGR5QL8U8".equals(key)) {
  252. Object value = rowItem.get("value");
  253. if (value instanceof List) {
  254. attachmentList = (List<Map>) value;
  255. }
  256. }
  257. }
  258. if ("是".equals(hasInvoice) && attachmentList != null && !attachmentList.isEmpty()) {
  259. for (Map attachment : attachmentList) {
  260. String fileId = UtilMap.getString(attachment, "fileId");
  261. String fileType = UtilMap.getString(attachment, "fileType");
  262. String fileName = UtilMap.getString(attachment, "fileName");
  263. try {
  264. String filePath = downloadPath + fileId + "." + fileType;
  265. downloadDdFile(processInstanceId, fileId, filePath);
  266. String hz = "qw/files/" + fileId + "." + fileType;
  267. Map fileMap = new HashMap();
  268. fileMap.put("url", url + hz);
  269. fileMap.put("size", new File(filePath).length() / 1024f / 1024f);
  270. fileMap.put("isPdf", "pdf".equalsIgnoreCase(fileType));
  271. McR<Object> result = processMixedInvoice(fileMap);
  272. Object responseData = result.getData();
  273. // 解析发票数据
  274. if (responseData != null) {
  275. String jsonStr = JSONObject.toJSONString(responseData);
  276. JSONObject jsonObject = JSONObject.parseObject(jsonStr);
  277. JSONArray resultArray = jsonObject.getJSONArray("result");
  278. if (resultArray != null && !resultArray.isEmpty()) {
  279. JSONObject invoice = resultArray.getJSONObject(0);
  280. String invoiceNumber = invoice.getString("serial") != null ? invoice.getString("serial") : "";
  281. String buyerTaxId = invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "";
  282. String buyerName = invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "";
  283. // 获取税率,供共享发票使用
  284. Object taxRateObj = invoice.get("taxRate");
  285. Object kind = invoice.getString("kindName");
  286. if (taxRateObj != null) {
  287. String taxRateStr = String.valueOf(taxRateObj);
  288. String taxRateNum = taxRateStr.replace("%", "").trim();
  289. sharedKind = kind != null ? String.valueOf(kind) : "";
  290. sharedTaxRate = new BigDecimal(taxRateNum);
  291. sharedTaxRate = sharedTaxRate.divide(new BigDecimal("100"), 10, BigDecimal.ROUND_HALF_UP);
  292. log.info("从有发票中获取税率: 原始={}, 转换后={}", taxRateStr, sharedTaxRate);
  293. }
  294. // 保存解析的数据供后续使用
  295. Map<String, Object> parsedData = new HashMap<>();
  296. parsedData.put("invoice", invoice);
  297. parsedData.put("fileId", fileId);
  298. parsedData.put("fileName", fileName);
  299. parsedData.put("fileType", fileType);
  300. parsedData.put("cdbm", cdbm);
  301. parsedData.put("hasInvoice", hasInvoice);
  302. parsedData.put("detailje", detailje);
  303. parsedData.put("responseData", responseData);
  304. parsedInvoiceDataList.add(parsedData);
  305. // ========== 校验1:抬头校验 ==========
  306. if (StringUtils.isNotBlank(buyerName)) {
  307. CompanyTitle existingTitle = companyTitleMapper.selectByTaxId(buyerName);
  308. if (existingTitle == null) {
  309. String errorInfo = String.format("发票: %s, 购买方名称: %s, 税号: %s",
  310. fileName, buyerName, buyerTaxId);
  311. invalidBuyerTaxIds.add(errorInfo);
  312. log.warn("抬头校验失败: 税号={} 不存在于抬头库, 文件名={}", buyerTaxId, fileName);
  313. }
  314. } else {
  315. String errorInfo = String.format("发票: %s, 购买方税号为空", fileName);
  316. invalidBuyerTaxIds.add(errorInfo);
  317. log.warn("抬头校验失败: 购买方税号为空, 文件名={}", fileName);
  318. }
  319. // ========== 校验2:发票号重复校验 ==========
  320. if (StringUtils.isNotBlank(invoiceNumber)) {
  321. // 检查数据库中是否已存在相同的发票号
  322. InvoiceLibrary existingInvoice = baseMapper.selectByInvoiceNumber(invoiceNumber);
  323. if (existingInvoice != null) {
  324. String errorMsg = String.format("发票号重复: %s (当前文件: %s, 已存在于OA审批单: %s)",
  325. invoiceNumber, fileName, existingInvoice.getOaId());
  326. log.error(errorMsg);
  327. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  328. return McR.error("400", errorMsg);
  329. }
  330. // 同时检查同一审批单内是否有重复
  331. if (invoiceNumberList.contains(invoiceNumber)) {
  332. String errorMsg = String.format("当前审批单内发票号重复: %s (当前文件: %s, 已存在文件: %s)",
  333. invoiceNumber, fileName, invoiceNumberToFileName.get(invoiceNumber));
  334. log.error(errorMsg);
  335. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  336. return McR.error("400", errorMsg);
  337. }
  338. invoiceNumberList.add(invoiceNumber);
  339. invoiceNumberToFileName.put(invoiceNumber, fileName);
  340. }
  341. }
  342. }
  343. } catch (Exception e) {
  344. log.error("发票解析异常: fileId={}, fileName={}", fileId, fileName, e);
  345. terminateWorkflow(accessToken, processInstanceId, userId, "发票解析失败: " + e.getMessage());
  346. return McR.error("400", "发票解析失败: " + e.getMessage());
  347. }
  348. }
  349. } else if ("共享".equals(hasInvoice)) {
  350. // 收集共享发票数据,待第二遍遍历时保存
  351. if (detailje != null && StringUtils.isNotBlank(detailje)) {
  352. Map<String, Object> sharedData = new HashMap<>();
  353. sharedData.put("detailje", detailje);
  354. sharedData.put("cdbm", cdbm);
  355. sharedData.put("hasInvoice", hasInvoice);
  356. sharedInvoiceDataList.add(sharedData);
  357. log.info("收集共享发票数据: 金额={}, 成本部门={}", detailje, cdbm);
  358. }
  359. }
  360. }
  361. // ========== 抬头校验失败处理 ==========
  362. if (!invalidBuyerTaxIds.isEmpty()) {
  363. String errorMsg = "发票抬头有误,以下发票的购买方税号不在公司抬头库中:\n" + String.join("\n", invalidBuyerTaxIds);
  364. log.error(errorMsg);
  365. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  366. return McR.error("400", errorMsg);
  367. }
  368. BigDecimal totalAmount = BigDecimal.ZERO;
  369. // ========== 第二遍遍历:保存发票数据(校验通过后) ==========
  370. // 1. 保存普通发票数据
  371. for (Map<String, Object> parsedData : parsedInvoiceDataList) {
  372. JSONObject invoice = (JSONObject) parsedData.get("invoice");
  373. Object responseData = parsedData.get("responseData");
  374. String cdbm = (String) parsedData.get("cdbm");
  375. String hasInvoice = (String) parsedData.get("hasInvoice");
  376. String detailje = (String) parsedData.get("detailje"); // 修改为 String 类型
  377. allResults.add(responseData);
  378. // 汇总 amount
  379. BigDecimal amount = invoice.getBigDecimal("amount");
  380. if (amount != null) {
  381. totalAmount = totalAmount.add(amount);
  382. }
  383. // 构建InvoiceLibrary对象
  384. InvoiceLibrary invoiceLibrary = new InvoiceLibrary();
  385. invoiceLibrary.setOaId(processInstanceId);
  386. invoiceLibrary.setInvoiceCode(invoice.getString("code") != null ? invoice.getString("code") : "");
  387. invoiceLibrary.setInvoiceNumber(invoice.getString("serial") != null ? invoice.getString("serial") : "");
  388. invoiceLibrary.setInvoiceType(invoice.getString("kindName") != null ? invoice.getString("kindName") : "");
  389. String date = invoice.getString("date");
  390. if (StringUtils.isNotBlank(date)) {
  391. try {
  392. invoiceLibrary.setInvoiceDate(LocalDate.parse(date.trim()));
  393. } catch (Exception e) {
  394. log.warn("日期解析失败: {}", date);
  395. }
  396. }
  397. invoiceLibrary.setAmount(invoice.getBigDecimal("excludingTax") != null ? invoice.getBigDecimal("excludingTax") : BigDecimal.ZERO);
  398. invoiceLibrary.setTaxAmount(invoice.getBigDecimal("tax") != null ? invoice.getBigDecimal("tax") : BigDecimal.ZERO);
  399. invoiceLibrary.setTotalAmount(invoice.getBigDecimal("amount") != null ? invoice.getBigDecimal("amount") : BigDecimal.ZERO);
  400. invoiceLibrary.setBuyerName(invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "");
  401. invoiceLibrary.setBuyerTaxId(invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "");
  402. invoiceLibrary.setSellerName(invoice.getString("sellerName") != null ? invoice.getString("sellerName") : "");
  403. invoiceLibrary.setSellerTaxId(invoice.getString("sellerTaxId") != null ? invoice.getString("sellerTaxId") : "");
  404. invoiceLibrary.setOaStatus("0");
  405. invoiceLibrary.setFormName("员工费用报销");
  406. invoiceLibrary.setPaySubject(fkzhxx != null ? fkzhxx : "");
  407. invoiceLibrary.setPayAccount(fkzh != null ? fkzh : "");
  408. invoiceLibrary.setBankName(yhqc != null ? yhqc : "");
  409. invoiceLibrary.setIsLongTerm(sfct != null ? sfct : "");
  410. invoiceLibrary.setInvoiceStatus("0");
  411. invoiceLibrary.setHasInvoice(hasInvoice);
  412. // 处理 detailAmount,将 String 转换为 BigDecimal
  413. if (detailje != null && StringUtils.isNotBlank(detailje)) {
  414. try {
  415. invoiceLibrary.setDetailAmount(new BigDecimal(detailje));
  416. } catch (NumberFormatException e) {
  417. log.error("detailJe格式转换失败: {}", detailje);
  418. invoiceLibrary.setDetailAmount(null);
  419. }
  420. } else {
  421. invoiceLibrary.setDetailAmount(null);
  422. }
  423. // 普通发票不涉及共享字段,设置为 null
  424. invoiceLibrary.setSharedTaxAmount(null);
  425. invoiceLibrary.setSharedAmount(null);
  426. invoiceLibrary.setSharedRate(null);
  427. invoiceLibrary.setAccountTitle(bxlb != null ? bxlb : "");
  428. invoiceLibrary.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  429. invoiceLibrary.setCreatedAt(LocalDateTime.now());
  430. invoiceLibrary.setUpdatedAt(LocalDateTime.now());
  431. // 设置成本部门
  432. if (cdbm != null && StringUtils.isNotBlank(cdbm)) {
  433. invoiceLibrary.setDep(cdbm);
  434. } else {
  435. invoiceLibrary.setDep(bm != null ? bm : "");
  436. }
  437. invoiceList.add(invoiceLibrary);
  438. }
  439. for (Map<String, Object> sharedData : sharedInvoiceDataList) {
  440. String detailje = (String) sharedData.get("detailje");
  441. String cdbm = (String) sharedData.get("cdbm");
  442. String hasInvoice = (String) sharedData.get("hasInvoice");
  443. try {
  444. BigDecimal detailAmount = new BigDecimal(detailje);
  445. // 使用从普通发票中解析到的税率
  446. BigDecimal ocrTaxRate = sharedTaxRate;
  447. if (ocrTaxRate.compareTo(BigDecimal.ZERO) == 0) {
  448. log.warn("共享发票税率未获取到,使用默认税率0,金额={}", detailAmount);
  449. }
  450. // 计算金额: 含税金额 / (1 + 税率) = 税额
  451. BigDecimal divisor = BigDecimal.ONE.add(ocrTaxRate);
  452. BigDecimal sharedTaxAmount = detailAmount.divide(divisor, 2, BigDecimal.ROUND_HALF_UP);
  453. BigDecimal sharedAmount = detailAmount.subtract(sharedTaxAmount);
  454. BigDecimal sharedRate = ocrTaxRate; // 这是小数形式的税率,如 0.09
  455. log.info("共享发票计算: 含税金额={}, 税率={}%, 不含税金额={}, 税额={}",
  456. detailAmount, ocrTaxRate.multiply(new BigDecimal("100")),
  457. sharedAmount, sharedTaxAmount);
  458. // 保存共享发票数据
  459. InvoiceLibrary sharedInvoice = new InvoiceLibrary();
  460. sharedInvoice.setOaId(processInstanceId);
  461. sharedInvoice.setOaStatus("0");
  462. sharedInvoice.setInvoiceStatus("0");
  463. sharedInvoice.setDep(cdbm != null && StringUtils.isNotBlank(cdbm) ? cdbm : (bm != null ? bm : ""));
  464. sharedInvoice.setAccountTitle(bxlb != null ? bxlb : "");
  465. sharedInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  466. sharedInvoice.setAmount(null);
  467. sharedInvoice.setTaxAmount(null);
  468. sharedInvoice.setTotalAmount(null); // 共享发票不设置总金额
  469. sharedInvoice.setFormName("员工费用报销");
  470. sharedInvoice.setPaySubject(fkzhxx != null ? fkzhxx : "");
  471. sharedInvoice.setPayAccount(fkzh != null ? fkzh : "");
  472. sharedInvoice.setBankName(yhqc != null ? yhqc : "");
  473. sharedInvoice.setIsLongTerm(sfct != null ? sfct : "");
  474. sharedInvoice.setHasInvoice(hasInvoice);
  475. sharedInvoice.setDetailAmount(detailAmount);
  476. // 共享发票字段设置值 - 修正:存储数字而不是带百分号的字符串
  477. sharedInvoice.setSharedTaxAmount(sharedAmount);
  478. sharedInvoice.setSharedAmount(sharedTaxAmount);
  479. sharedInvoice.setSharedRate(String.valueOf(sharedRate)); // 直接存储小数,如 0.09
  480. sharedInvoice.setCreatedAt(LocalDateTime.now());
  481. sharedInvoice.setUpdatedAt(LocalDateTime.now());
  482. sharedInvoice.setInvoiceCode("");
  483. sharedInvoice.setInvoiceNumber("");
  484. sharedInvoice.setInvoiceType(sharedKind);
  485. sharedInvoice.setBuyerName("");
  486. sharedInvoice.setBuyerTaxId("");
  487. sharedInvoice.setSellerName("");
  488. sharedInvoice.setSellerTaxId("");
  489. invoiceList.add(sharedInvoice);
  490. log.info("成功保存共享发票记录,成本部门: {}", cdbm);
  491. } catch (NumberFormatException e) {
  492. log.error("共享发票金额格式转换失败: detailje={}", detailje, e);
  493. } catch (Exception e) {
  494. log.error("共享发票处理失败", e);
  495. }
  496. }
  497. // 保存发票记录
  498. if (!invoiceList.isEmpty()) {
  499. for (InvoiceLibrary invoice : invoiceList) {
  500. baseMapper.insert(invoice);
  501. }
  502. log.info("成功保存 {} 条发票记录(普通发票: {}, 共享发票: {})",
  503. invoiceList.size(), parsedInvoiceDataList.size(), sharedInvoiceDataList.size());
  504. }
  505. addWorkflowComment1(processInstanceId, userId, totalAmount);
  506. return McR.success(allResults);
  507. } catch (Exception e) {
  508. log.error("解析子表数据失败", e);
  509. return McR.error("300", "解析数据失败: " + e.getMessage());
  510. }
  511. }
  512. }
  513. }
  514. return McR.success();
  515. } catch (Exception e) {
  516. log.error("处理审批实例失败", e);
  517. return McR.error("500", "系统处理失败: " + e.getMessage());
  518. }
  519. }
  520. /**
  521. * 终止钉钉审批流程
  522. *
  523. * @param accessToken 钉钉accessToken
  524. * @param processInstanceId 审批实例ID
  525. * @param operatingUserId 操作人用户ID(通常是发起人ID)
  526. * @param remark 终止原因备注
  527. */
  528. private void terminateWorkflow(String accessToken, String processInstanceId, String operatingUserId, String remark) {
  529. // 异步延迟15秒后执行终止
  530. CompletableFuture.runAsync(() -> {
  531. try {
  532. log.info("延迟15秒后开始终止审批流程: processInstanceId={}, remark={}", processInstanceId, remark);
  533. Thread.sleep(15000);
  534. // 实际执行终止操作
  535. doTerminateWorkflow(accessToken, processInstanceId, operatingUserId, remark);
  536. } catch (InterruptedException e) {
  537. Thread.currentThread().interrupt();
  538. log.error("延迟终止流程被中断: processInstanceId={}", processInstanceId, e);
  539. } catch (Exception e) {
  540. log.error("异步执行终止流程异常: processInstanceId={}", processInstanceId, e);
  541. }
  542. });
  543. }
  544. /**
  545. * 实际执行钉钉审批流程终止
  546. */
  547. private void doTerminateWorkflow(String accessToken, String processInstanceId, String operatingUserId, String remark) {
  548. try {
  549. log.info("开始调用钉钉API终止审批流程: processInstanceId={}", processInstanceId);
  550. // 构建请求URL
  551. String url = "https://api.dingtalk.com/v1.0/workflow/processInstances/terminate";
  552. // 构建请求头
  553. HttpHeaders headers = new HttpHeaders();
  554. headers.setContentType(MediaType.APPLICATION_JSON);
  555. headers.set("x-acs-dingtalk-access-token", accessToken);
  556. // 构建请求体
  557. Map<String, Object> requestBody = new HashMap<>();
  558. requestBody.put("processInstanceId", processInstanceId);
  559. requestBody.put("isSystem", true); // 系统调用终止
  560. requestBody.put("remark", remark);
  561. requestBody.put("operatingUserId", operatingUserId);
  562. log.info("钉钉终止请求参数: {}", requestBody);
  563. // 发送POST请求
  564. HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);
  565. RestTemplate restTemplate = new RestTemplate();
  566. ResponseEntity<String> response = restTemplate.postForEntity(url, requestEntity, String.class);
  567. if (response.getStatusCode().is2xxSuccessful()) {
  568. log.info("审批流程终止成功: processInstanceId={}, response={}", processInstanceId, response.getBody());
  569. } else {
  570. log.error("审批流程终止失败: processInstanceId={}, statusCode={}, response={}",
  571. processInstanceId, response.getStatusCode(), response.getBody());
  572. }
  573. } catch (Exception e) {
  574. log.error("调用钉钉终止流程接口异常: processInstanceId={}, error={}", processInstanceId, e.getMessage(), e);
  575. }
  576. }
  577. @Override
  578. public void updateOaStatusByOaId(Map map) {
  579. try {
  580. String oaId = UtilMap.getString(map, "oaId");
  581. // 创建更新条件
  582. LambdaUpdateWrapper<InvoiceLibrary> updateWrapper = new LambdaUpdateWrapper<>();
  583. updateWrapper.eq(InvoiceLibrary::getOaId, oaId)
  584. .set(InvoiceLibrary::getOaStatus, "1")
  585. .set(InvoiceLibrary::getUpdatedAt, LocalDateTime.now());
  586. int updateCount = baseMapper.update(null, updateWrapper);
  587. if (updateCount > 0) {
  588. log.info("更新成功: 共更新{}条记录", updateCount);
  589. } else {
  590. log.warn("未找到符合条件的记录: oaId={}", oaId);
  591. }
  592. } catch (Exception e) {
  593. throw new RuntimeException("更新OA状态失败", e);
  594. }
  595. }
  596. @Override
  597. public McR invoiceLibrarys(Map map) {
  598. log.info("接收到的参数: {}", map);
  599. try {
  600. String processInstanceId = UtilMap.getString(map, "processInstanceId");
  601. if (StringUtils.isBlank(processInstanceId)) {
  602. log.error("processInstanceId为空");
  603. return McR.error("400", "审批实例ID不能为空");
  604. }
  605. // 获取审批实例信息
  606. String accessToken = ddClient.getAccessToken();
  607. Map processInstance = ddClientWorkflow.getProcessInstanceId(accessToken, processInstanceId);
  608. if (processInstance == null) {
  609. log.error("获取审批实例失败: {}", processInstanceId);
  610. return McR.error("500", "获取审批实例信息失败");
  611. }
  612. String userId = (String) processInstance.get("originatorUserId");
  613. log.info("审批人ID: {}", userId);
  614. List<Map> formComponentValues = (List<Map>) processInstance.get("formComponentValues");
  615. if (formComponentValues == null || formComponentValues.isEmpty()) {
  616. log.warn("表单数据为空");
  617. return McR.error("400", "表单数据为空");
  618. }
  619. // 存储主表的字段值
  620. String zt = null;
  621. BigDecimal mainJe = null;
  622. String bxlb = null;
  623. String bm = null;
  624. // 遍历收集主表字段值
  625. for (Map formComponentValue : formComponentValues) {
  626. String id = String.valueOf(formComponentValue.get("id"));
  627. Object value = formComponentValue.get("value");
  628. if ("DDSelectField_4SITXLYUEO80".equals(id)) {
  629. zt = value != null ? String.valueOf(value) : "";
  630. }
  631. if ("MoneyField_3QZLY8BD3780".equals(id)) {
  632. if (value instanceof BigDecimal) {
  633. mainJe = (BigDecimal) value;
  634. } else if (value != null) {
  635. try {
  636. mainJe = new BigDecimal(String.valueOf(value));
  637. } catch (NumberFormatException e) {
  638. log.error("金额格式转换失败: {}", value);
  639. mainJe = BigDecimal.ZERO;
  640. }
  641. }
  642. }
  643. if ("DDSelectField_1TP75OVCPAAO0".equals(id)) {
  644. bxlb = value != null ? String.valueOf(value) : "";
  645. }
  646. if ("DepartmentField_OKBSJN0MD6O0".equals(id)) {
  647. bm = value != null ? String.valueOf(value) : "";
  648. }
  649. }
  650. // 判断主表状态 - 无发票
  651. if ("否".equals(zt)) {
  652. Map<String, Object> result = new HashMap<>();
  653. result.put("je", mainJe);
  654. result.put("status", "否");
  655. result.put("message", "无发票报销");
  656. // 保存基础报销记录
  657. InvoiceLibrary baseInvoice = new InvoiceLibrary();
  658. baseInvoice.setOaId(processInstanceId);
  659. baseInvoice.setOaStatus("0");
  660. baseInvoice.setInvoiceStatus("0");
  661. baseInvoice.setDep(bm != null ? bm : "");
  662. baseInvoice.setAccountTitle(bxlb != null ? bxlb : "");
  663. baseInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  664. baseInvoice.setCreatedAt(LocalDateTime.now());
  665. baseInvoice.setUpdatedAt(LocalDateTime.now());
  666. baseInvoice.setInvoiceCode("");
  667. baseInvoice.setInvoiceNumber("");
  668. baseInvoice.setInvoiceType("");
  669. baseInvoice.setAmount(BigDecimal.ZERO);
  670. baseInvoice.setTaxAmount(BigDecimal.ZERO);
  671. baseInvoice.setTotalAmount(BigDecimal.ZERO);
  672. baseInvoice.setBuyerName("");
  673. baseInvoice.setBuyerTaxId("");
  674. baseInvoice.setSellerName("");
  675. baseInvoice.setSellerTaxId("");
  676. baseMapper.insert(baseInvoice);
  677. addWorkflowComment(processInstanceId, userId, result);
  678. return McR.success(result);
  679. }
  680. // 判断主表状态 - 有发票
  681. if ("是".equals(zt)) {
  682. for (Map formComponentValue : formComponentValues) {
  683. String id = String.valueOf(formComponentValue.get("id"));
  684. if ("TableField_1A1CDMEN8DDS0".equals(id)) {
  685. String tableFieldValue = String.valueOf(formComponentValue.get("value"));
  686. log.info("子表数据: {}", tableFieldValue);
  687. try {
  688. List<Map> tableRows = (List<Map>) JSONObject.parse(tableFieldValue);
  689. if (tableRows == null || tableRows.isEmpty()) {
  690. log.warn("子表数据为空");
  691. return McR.error("400", "发票明细为空");
  692. }
  693. List<Object> allResults = new ArrayList<>();
  694. List<InvoiceLibrary> invoiceList = new ArrayList<>();
  695. for (Map row : tableRows) {
  696. List<Map> rowValues = (List<Map>) row.get("rowValue");
  697. String hasInvoice = null;
  698. List<Map> attachmentList = null;
  699. for (Map rowItem : rowValues) {
  700. String key = String.valueOf(rowItem.get("key"));
  701. if ("DDSelectField_1ORUK0KIM5D6O".equals(key)) {
  702. Object value = rowItem.get("value");
  703. hasInvoice = value != null ? String.valueOf(value) : "";
  704. }
  705. if ("DDAttachment_Z02OGR5QL8U8".equals(key)) {
  706. Object value = rowItem.get("value");
  707. if (value instanceof List) {
  708. attachmentList = (List<Map>) value;
  709. }
  710. }
  711. }
  712. if ("是".equals(hasInvoice) && attachmentList != null && !attachmentList.isEmpty()) {
  713. for (Map attachment : attachmentList) {
  714. String fileId = UtilMap.getString(attachment, "fileId");
  715. String fileType = UtilMap.getString(attachment, "fileType");
  716. String fileName = UtilMap.getString(attachment, "fileName");
  717. log.info("处理发票: fileName={}, fileId={}", fileName, fileId);
  718. try {
  719. String filePath = downloadPath + fileId + "." + fileType;
  720. downloadDdFile(processInstanceId, fileId, filePath);
  721. String hz = "qw/files/" + fileId + "." + fileType;
  722. Map fileMap = new HashMap();
  723. fileMap.put("url", url + hz);
  724. fileMap.put("size", new File(filePath).length() / 1024f / 1024f);
  725. fileMap.put("isPdf", "pdf".equalsIgnoreCase(fileType));
  726. McR<Object> result = processMixedInvoice(fileMap);
  727. Object responseData = result.getData();
  728. allResults.add(responseData);
  729. // 解析发票数据 - responseData 是对象,不是数组
  730. if (responseData != null) {
  731. try {
  732. String jsonStr = JSONObject.toJSONString(responseData);
  733. JSONObject jsonObject = JSONObject.parseObject(jsonStr);
  734. // 获取 result 数组
  735. JSONArray resultArray = jsonObject.getJSONArray("result");
  736. if (resultArray != null && !resultArray.isEmpty()) {
  737. JSONObject invoice = resultArray.getJSONObject(0);
  738. InvoiceLibrary invoiceLibrary = new InvoiceLibrary();
  739. invoiceLibrary.setOaId(processInstanceId);
  740. invoiceLibrary.setInvoiceCode(invoice.getString("code") != null ? invoice.getString("code") : "");
  741. invoiceLibrary.setInvoiceNumber(invoice.getString("serial") != null ? invoice.getString("serial") : "");
  742. invoiceLibrary.setInvoiceType(invoice.getString("kindName") != null ? invoice.getString("kindName") : "");
  743. String date = invoice.getString("date");
  744. if (StringUtils.isNotBlank(date)) {
  745. try {
  746. invoiceLibrary.setInvoiceDate(LocalDate.parse(date.trim()));
  747. } catch (Exception e) {
  748. log.warn("日期解析失败: {}", date);
  749. }
  750. }
  751. invoiceLibrary.setAmount(invoice.getBigDecimal("excludingTax") != null ? invoice.getBigDecimal("excludingTax") : BigDecimal.ZERO);
  752. invoiceLibrary.setTaxAmount(invoice.getBigDecimal("tax") != null ? invoice.getBigDecimal("tax") : BigDecimal.ZERO);
  753. invoiceLibrary.setTotalAmount(invoice.getBigDecimal("amount") != null ? invoice.getBigDecimal("amount") : BigDecimal.ZERO);
  754. invoiceLibrary.setBuyerName(invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "");
  755. invoiceLibrary.setBuyerTaxId(invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "");
  756. invoiceLibrary.setSellerName(invoice.getString("sellerName") != null ? invoice.getString("sellerName") : "");
  757. invoiceLibrary.setSellerTaxId(invoice.getString("sellerTaxId") != null ? invoice.getString("sellerTaxId") : "");
  758. invoiceLibrary.setOaStatus("0");
  759. invoiceLibrary.setInvoiceStatus("0");
  760. invoiceLibrary.setDep(bm != null ? bm : "");
  761. invoiceLibrary.setAccountTitle(bxlb != null ? bxlb : "");
  762. invoiceLibrary.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  763. invoiceLibrary.setCreatedAt(LocalDateTime.now());
  764. invoiceLibrary.setUpdatedAt(LocalDateTime.now());
  765. invoiceList.add(invoiceLibrary);
  766. }
  767. } catch (Exception e) {
  768. log.error("解析发票数据失败", e);
  769. }
  770. }
  771. } catch (Exception e) {
  772. log.error("处理发票失败: fileId={}, fileName={}", fileId, fileName, e);
  773. }
  774. }
  775. }
  776. }
  777. if (!invoiceList.isEmpty()) {
  778. for (InvoiceLibrary invoice : invoiceList) {
  779. baseMapper.insert(invoice);
  780. }
  781. log.info("成功保存 {} 条发票记录", invoiceList.size());
  782. }
  783. addWorkflowComment(processInstanceId, userId, allResults);
  784. return McR.success(allResults);
  785. } catch (Exception e) {
  786. log.error("解析子表数据失败", e);
  787. return McR.error("300", "解析数据失败: " + e.getMessage());
  788. }
  789. }
  790. }
  791. }
  792. return McR.success();
  793. } catch (Exception e) {
  794. log.error("处理审批实例失败", e);
  795. return McR.error("500", "系统处理失败: " + e.getMessage());
  796. }
  797. }
  798. @Override
  799. public McR deleteAllByOaId(String oaId) {
  800. try {
  801. // 创建删除条件
  802. LambdaQueryWrapper<InvoiceLibrary> queryWrapper = new LambdaQueryWrapper<>();
  803. queryWrapper.eq(InvoiceLibrary::getOaId, oaId);
  804. // 执行删除
  805. int deleteCount = baseMapper.delete(queryWrapper);
  806. if (deleteCount > 0) {
  807. return McR.success("删除成功,共删除 " + deleteCount + " 条记录");
  808. } else {
  809. return McR.error("404", "未找到 oa_id 为 " + oaId + " 的记录");
  810. }
  811. } catch (Exception e) {
  812. log.error("删除失败", e);
  813. return McR.error("500", "删除失败: " + e.getMessage());
  814. }
  815. }
  816. @Override
  817. public McR invoiceLibrary1(Map map) {
  818. log.info("接收到的参数: {}", map);
  819. try {
  820. String processInstanceId = UtilMap.getString(map, "processInstanceId");
  821. if (StringUtils.isBlank(processInstanceId)) {
  822. log.error("processInstanceId为空");
  823. return McR.error("400", "审批实例ID不能为空");
  824. }
  825. // 获取审批实例信息
  826. String accessToken = ddClient.getAccessToken();
  827. Map processInstance = ddClientWorkflow.getProcessInstanceId(accessToken, processInstanceId);
  828. if (processInstance == null) {
  829. log.error("获取审批实例失败: {}", processInstanceId);
  830. return McR.error("500", "获取审批实例信息失败");
  831. }
  832. String userId = (String) processInstance.get("originatorUserId");
  833. log.info("审批人ID: {}", userId);
  834. List<Map> formComponentValues = (List<Map>) processInstance.get("formComponentValues");
  835. if (formComponentValues == null || formComponentValues.isEmpty()) {
  836. log.warn("表单数据为空");
  837. return McR.error("400", "表单数据为空");
  838. }
  839. // 存储主表的字段值
  840. String zt = null;
  841. BigDecimal mainJe = null;
  842. String bxlb = null;
  843. String bm = null;
  844. String fkzhxx = null;
  845. String fkzh = null;
  846. String yhqc = null;
  847. String sfct = null;
  848. // 遍历收集主表字段值
  849. for (Map formComponentValue : formComponentValues) {
  850. String id = String.valueOf(formComponentValue.get("id"));
  851. Object value = formComponentValue.get("value");
  852. if ("DDSelectField_4TXFWTKFF7K0".equals(id)) {
  853. zt = value != null ? String.valueOf(value) : "";
  854. }
  855. if ("MoneyField_1FVEBDQBSQ680".equals(id)) {
  856. if (value instanceof BigDecimal) {
  857. mainJe = (BigDecimal) value;
  858. } else if (value != null) {
  859. try {
  860. mainJe = new BigDecimal(String.valueOf(value));
  861. } catch (NumberFormatException e) {
  862. log.error("金额格式转换失败: {}", value);
  863. mainJe = BigDecimal.ZERO;
  864. }
  865. }
  866. }
  867. if ("DDSelectField_YLLB7AD1ATS0".equals(id)) {
  868. bxlb = value != null ? String.valueOf(value) : "";
  869. }
  870. if ("DepartmentField_RF93A6VHA9C0".equals(id)) {
  871. bm = value != null ? String.valueOf(value) : "";
  872. }
  873. if ("DDSelectField_23RVAEKUM5UO0".equals(id)) {
  874. fkzhxx = value != null ? String.valueOf(value) : "";
  875. }
  876. if ("TextField_1TE3AOHRMD4W0".equals(id)) {
  877. fkzh = value != null ? String.valueOf(value) : "";
  878. }
  879. if ("TextField_EN2MDZIMUTK0".equals(id)) {
  880. yhqc = value != null ? String.valueOf(value) : "";
  881. }
  882. if ("DDSelectField_P2EFYKN5ALC0".equals(id)) {
  883. sfct = value != null ? String.valueOf(value) : "否";
  884. }
  885. }
  886. // 判断主表状态 - 无发票
  887. if ("否".equals(zt)) {
  888. // 保存基础报销记录
  889. InvoiceLibrary baseInvoice = new InvoiceLibrary();
  890. baseInvoice.setOaId(processInstanceId);
  891. baseInvoice.setOaStatus("0");
  892. baseInvoice.setInvoiceStatus("0");
  893. baseInvoice.setDep(bm != null ? bm : "");
  894. baseInvoice.setAccountTitle(bxlb != null ? bxlb : "");
  895. baseInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  896. baseInvoice.setCreatedAt(LocalDateTime.now());
  897. baseInvoice.setUpdatedAt(LocalDateTime.now());
  898. baseInvoice.setInvoiceCode("");
  899. baseInvoice.setInvoiceNumber("");
  900. baseInvoice.setInvoiceType("");
  901. baseInvoice.setAmount(BigDecimal.ZERO);
  902. baseInvoice.setTaxAmount(BigDecimal.ZERO);
  903. baseInvoice.setTotalAmount(BigDecimal.ZERO);
  904. baseInvoice.setBuyerName("");
  905. baseInvoice.setBuyerTaxId("");
  906. baseInvoice.setSellerName("");
  907. baseInvoice.setSellerTaxId("");
  908. baseInvoice.setFormName("付款申请");
  909. baseInvoice.setPaySubject(fkzhxx != null ? fkzhxx : "");
  910. baseInvoice.setPayAccount(fkzh != null ? fkzh : "");
  911. baseInvoice.setBankName(yhqc != null ? yhqc : "");
  912. baseInvoice.setIsLongTerm(sfct != null ? sfct : "");
  913. baseInvoice.setHasInvoice("否");
  914. baseInvoice.setDetailAmount(null);
  915. baseInvoice.setSharedTaxAmount(null);
  916. baseInvoice.setSharedAmount(null);
  917. baseInvoice.setSharedRate(null);
  918. baseMapper.insert(baseInvoice);
  919. addWorkflowComment(processInstanceId, userId, "报销总金额:" + mainJe);
  920. return McR.success();
  921. }
  922. // 判断主表状态 - 有发票
  923. if ("是".equals(zt)) {
  924. for (Map formComponentValue : formComponentValues) {
  925. String id = String.valueOf(formComponentValue.get("id"));
  926. if ("TableField_1VKP90DW7WKG0".equals(id)) {
  927. String tableFieldValue = String.valueOf(formComponentValue.get("value"));
  928. log.info("子表数据: {}", tableFieldValue);
  929. try {
  930. List<Map> tableRows = (List<Map>) JSONObject.parse(tableFieldValue);
  931. if (tableRows == null || tableRows.isEmpty()) {
  932. log.warn("子表数据为空");
  933. return McR.error("400", "发票明细为空");
  934. }
  935. List<Object> allResults = new ArrayList<>();
  936. List<InvoiceLibrary> invoiceList = new ArrayList<>();
  937. // 存储第一遍解析的发票数据,避免重复解析
  938. List<Map<String, Object>> parsedInvoiceDataList = new ArrayList<>();
  939. // 存储共享发票数据
  940. List<Map<String, Object>> sharedInvoiceDataList = new ArrayList<>();
  941. // 用于校验的集合
  942. List<String> invoiceNumberList = new ArrayList<>();
  943. Map<String, String> invoiceNumberToFileName = new HashMap<>();
  944. List<String> invalidBuyerTaxIds = new ArrayList<>();
  945. // 存储税率值,供共享发票使用
  946. BigDecimal sharedTaxRate = BigDecimal.ZERO;
  947. String sharedKind = "";
  948. // ========== 第一遍遍历:收集并校验所有发票数据 ==========
  949. for (Map row : tableRows) {
  950. List<Map> rowValues = (List<Map>) row.get("rowValue");
  951. String hasInvoice = null;
  952. String cdbm = null;
  953. String detailje = null;
  954. List<Map> attachmentList = null;
  955. for (Map rowItem : rowValues) {
  956. String key = String.valueOf(rowItem.get("key"));
  957. if ("DDSelectField_20ZGDOIRHB400".equals(key)) {
  958. Object value = rowItem.get("value");
  959. hasInvoice = value != null ? String.valueOf(value) : "";
  960. }
  961. if ("DepartmentField_1VQ3M98PC7SW0".equals(key)) {
  962. Object value = rowItem.get("value");
  963. cdbm = value != null ? String.valueOf(value) : "";
  964. }
  965. if ("NumberField_BLX4BRW8GEW0".equals(key)) {
  966. Object value = rowItem.get("value");
  967. detailje = value != null ? String.valueOf(value) : "";
  968. }
  969. if ("DDAttachment_1YQ3DD7BH4BK0".equals(key)) {
  970. Object value = rowItem.get("value");
  971. if (value instanceof List) {
  972. attachmentList = (List<Map>) value;
  973. }
  974. }
  975. }
  976. if ("是".equals(hasInvoice) && attachmentList != null && !attachmentList.isEmpty()) {
  977. for (Map attachment : attachmentList) {
  978. String fileId = UtilMap.getString(attachment, "fileId");
  979. String fileType = UtilMap.getString(attachment, "fileType");
  980. String fileName = UtilMap.getString(attachment, "fileName");
  981. try {
  982. String filePath = downloadPath + fileId + "." + fileType;
  983. downloadDdFile(processInstanceId, fileId, filePath);
  984. String hz = "qw/files/" + fileId + "." + fileType;
  985. Map fileMap = new HashMap();
  986. fileMap.put("url", url + hz);
  987. fileMap.put("size", new File(filePath).length() / 1024f / 1024f);
  988. fileMap.put("isPdf", "pdf".equalsIgnoreCase(fileType));
  989. McR<Object> result = processMixedInvoice(fileMap);
  990. Object responseData = result.getData();
  991. // 解析发票数据
  992. if (responseData != null) {
  993. String jsonStr = JSONObject.toJSONString(responseData);
  994. JSONObject jsonObject = JSONObject.parseObject(jsonStr);
  995. JSONArray resultArray = jsonObject.getJSONArray("result");
  996. if (resultArray != null && !resultArray.isEmpty()) {
  997. JSONObject invoice = resultArray.getJSONObject(0);
  998. String invoiceNumber = invoice.getString("serial") != null ? invoice.getString("serial") : "";
  999. String buyerTaxId = invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "";
  1000. String buyerName = invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "";
  1001. // 获取税率,供共享发票使用
  1002. Object taxRateObj = invoice.get("taxRate");
  1003. Object kind = invoice.getString("kindName");
  1004. System.out.println("qqq"+taxRateObj);
  1005. System.out.println("qqq"+kind);
  1006. if (taxRateObj != null) {
  1007. String taxRateStr = String.valueOf(taxRateObj);
  1008. String taxRateNum = taxRateStr.replace("%", "").trim();
  1009. sharedKind = kind != null ? String.valueOf(kind) : "";
  1010. sharedTaxRate = new BigDecimal(taxRateNum);
  1011. sharedTaxRate = sharedTaxRate.divide(new BigDecimal("100"), 10, BigDecimal.ROUND_HALF_UP);
  1012. log.info("从有发票中获取税率: 原始={}, 转换后={}", taxRateStr, sharedTaxRate);
  1013. }
  1014. // 保存解析的数据供后续使用
  1015. Map<String, Object> parsedData = new HashMap<>();
  1016. parsedData.put("invoice", invoice);
  1017. parsedData.put("fileId", fileId);
  1018. parsedData.put("fileName", fileName);
  1019. parsedData.put("fileType", fileType);
  1020. parsedData.put("cdbm", cdbm);
  1021. parsedData.put("hasInvoice", hasInvoice);
  1022. parsedData.put("detailje", detailje);
  1023. parsedData.put("responseData", responseData);
  1024. parsedInvoiceDataList.add(parsedData);
  1025. // ========== 校验1:抬头校验 ==========
  1026. if (StringUtils.isNotBlank(buyerName)) {
  1027. CompanyTitle existingTitle = companyTitleMapper.selectByTaxId(buyerName);
  1028. if (existingTitle == null) {
  1029. String errorInfo = String.format("发票: %s, 购买方名称: %s, 税号: %s",
  1030. fileName, buyerName, buyerTaxId);
  1031. invalidBuyerTaxIds.add(errorInfo);
  1032. log.warn("抬头校验失败: 税号={} 不存在于抬头库, 文件名={}", buyerTaxId, fileName);
  1033. }
  1034. } else {
  1035. String errorInfo = String.format("发票: %s, 购买方税号为空", fileName);
  1036. invalidBuyerTaxIds.add(errorInfo);
  1037. log.warn("抬头校验失败: 购买方税号为空, 文件名={}", fileName);
  1038. }
  1039. // ========== 校验2:发票号重复校验 ==========
  1040. if (StringUtils.isNotBlank(invoiceNumber)) {
  1041. // 检查数据库中是否已存在相同的发票号
  1042. InvoiceLibrary existingInvoice = baseMapper.selectByInvoiceNumber(invoiceNumber);
  1043. if (existingInvoice != null) {
  1044. String errorMsg = String.format("发票号重复: %s (当前文件: %s, 已存在于OA审批单: %s)",
  1045. invoiceNumber, fileName, existingInvoice.getOaId());
  1046. log.error(errorMsg);
  1047. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  1048. return McR.error("400", errorMsg);
  1049. }
  1050. // 同时检查同一审批单内是否有重复
  1051. if (invoiceNumberList.contains(invoiceNumber)) {
  1052. String errorMsg = String.format("当前审批单内发票号重复: %s (当前文件: %s, 已存在文件: %s)",
  1053. invoiceNumber, fileName, invoiceNumberToFileName.get(invoiceNumber));
  1054. log.error(errorMsg);
  1055. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  1056. return McR.error("400", errorMsg);
  1057. }
  1058. invoiceNumberList.add(invoiceNumber);
  1059. invoiceNumberToFileName.put(invoiceNumber, fileName);
  1060. }
  1061. }
  1062. }
  1063. } catch (Exception e) {
  1064. log.error("发票解析异常: fileId={}, fileName={}", fileId, fileName, e);
  1065. terminateWorkflow(accessToken, processInstanceId, userId, "发票解析失败: " + e.getMessage());
  1066. return McR.error("400", "发票解析失败: " + e.getMessage());
  1067. }
  1068. }
  1069. } else if ("共享".equals(hasInvoice)) {
  1070. // 收集共享发票数据,待第二遍遍历时保存
  1071. if (detailje != null && StringUtils.isNotBlank(detailje)) {
  1072. Map<String, Object> sharedData = new HashMap<>();
  1073. sharedData.put("detailje", detailje);
  1074. sharedData.put("cdbm", cdbm);
  1075. sharedData.put("hasInvoice", hasInvoice);
  1076. sharedInvoiceDataList.add(sharedData);
  1077. log.info("收集共享发票数据: 金额={}, 成本部门={}", detailje, cdbm);
  1078. }
  1079. }
  1080. }
  1081. // ========== 抬头校验失败处理 ==========
  1082. if (!invalidBuyerTaxIds.isEmpty()) {
  1083. String errorMsg = "发票抬头有误,以下发票的购买方税号不在公司抬头库中:\n" + String.join("\n", invalidBuyerTaxIds);
  1084. log.error(errorMsg);
  1085. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  1086. return McR.error("400", errorMsg);
  1087. }
  1088. BigDecimal totalAmount = BigDecimal.ZERO;
  1089. // ========== 第二遍遍历:保存发票数据(校验通过后) ==========
  1090. // 1. 保存普通发票数据
  1091. for (Map<String, Object> parsedData : parsedInvoiceDataList) {
  1092. JSONObject invoice = (JSONObject) parsedData.get("invoice");
  1093. Object responseData = parsedData.get("responseData");
  1094. String cdbm = (String) parsedData.get("cdbm");
  1095. String hasInvoice = (String) parsedData.get("hasInvoice");
  1096. String detailje = (String) parsedData.get("detailje"); // 修改为 String 类型
  1097. allResults.add(responseData);
  1098. // 汇总 amount
  1099. BigDecimal amount = invoice.getBigDecimal("amount");
  1100. if (amount != null) {
  1101. totalAmount = totalAmount.add(amount);
  1102. }
  1103. // 构建InvoiceLibrary对象
  1104. InvoiceLibrary invoiceLibrary = new InvoiceLibrary();
  1105. invoiceLibrary.setOaId(processInstanceId);
  1106. invoiceLibrary.setInvoiceCode(invoice.getString("code") != null ? invoice.getString("code") : "");
  1107. invoiceLibrary.setInvoiceNumber(invoice.getString("serial") != null ? invoice.getString("serial") : "");
  1108. invoiceLibrary.setInvoiceType(invoice.getString("kindName") != null ? invoice.getString("kindName") : "");
  1109. String date = invoice.getString("date");
  1110. if (StringUtils.isNotBlank(date)) {
  1111. try {
  1112. invoiceLibrary.setInvoiceDate(LocalDate.parse(date.trim()));
  1113. } catch (Exception e) {
  1114. log.warn("日期解析失败: {}", date);
  1115. }
  1116. }
  1117. invoiceLibrary.setAmount(invoice.getBigDecimal("excludingTax") != null ? invoice.getBigDecimal("excludingTax") : BigDecimal.ZERO);
  1118. invoiceLibrary.setTaxAmount(invoice.getBigDecimal("tax") != null ? invoice.getBigDecimal("tax") : BigDecimal.ZERO);
  1119. invoiceLibrary.setTotalAmount(invoice.getBigDecimal("amount") != null ? invoice.getBigDecimal("amount") : BigDecimal.ZERO);
  1120. invoiceLibrary.setBuyerName(invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "");
  1121. invoiceLibrary.setBuyerTaxId(invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "");
  1122. invoiceLibrary.setSellerName(invoice.getString("sellerName") != null ? invoice.getString("sellerName") : "");
  1123. invoiceLibrary.setSellerTaxId(invoice.getString("sellerTaxId") != null ? invoice.getString("sellerTaxId") : "");
  1124. invoiceLibrary.setOaStatus("0");
  1125. invoiceLibrary.setFormName("付款申请");
  1126. invoiceLibrary.setPaySubject(fkzhxx != null ? fkzhxx : "");
  1127. invoiceLibrary.setPayAccount(fkzh != null ? fkzh : "");
  1128. invoiceLibrary.setBankName(yhqc != null ? yhqc : "");
  1129. invoiceLibrary.setIsLongTerm(sfct != null ? sfct : "");
  1130. invoiceLibrary.setInvoiceStatus("0");
  1131. invoiceLibrary.setHasInvoice(hasInvoice);
  1132. // 处理 detailAmount,将 String 转换为 BigDecimal
  1133. if (detailje != null && StringUtils.isNotBlank(detailje)) {
  1134. try {
  1135. invoiceLibrary.setDetailAmount(new BigDecimal(detailje));
  1136. } catch (NumberFormatException e) {
  1137. log.error("detailJe格式转换失败: {}", detailje);
  1138. invoiceLibrary.setDetailAmount(null);
  1139. }
  1140. } else {
  1141. invoiceLibrary.setDetailAmount(null);
  1142. }
  1143. // 普通发票不涉及共享字段,设置为 null
  1144. invoiceLibrary.setSharedTaxAmount(null);
  1145. invoiceLibrary.setSharedAmount(null);
  1146. invoiceLibrary.setSharedRate(null);
  1147. invoiceLibrary.setAccountTitle(bxlb != null ? bxlb : "");
  1148. invoiceLibrary.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  1149. invoiceLibrary.setCreatedAt(LocalDateTime.now());
  1150. invoiceLibrary.setUpdatedAt(LocalDateTime.now());
  1151. // 设置成本部门
  1152. if (cdbm != null && StringUtils.isNotBlank(cdbm)) {
  1153. invoiceLibrary.setDep(cdbm);
  1154. } else {
  1155. invoiceLibrary.setDep(bm != null ? bm : "");
  1156. }
  1157. invoiceList.add(invoiceLibrary);
  1158. }
  1159. for (Map<String, Object> sharedData : sharedInvoiceDataList) {
  1160. String detailje = (String) sharedData.get("detailje");
  1161. String cdbm = (String) sharedData.get("cdbm");
  1162. String hasInvoice = (String) sharedData.get("hasInvoice");
  1163. try {
  1164. BigDecimal detailAmount = new BigDecimal(detailje);
  1165. // 使用从普通发票中解析到的税率
  1166. BigDecimal ocrTaxRate = sharedTaxRate;
  1167. if (ocrTaxRate.compareTo(BigDecimal.ZERO) == 0) {
  1168. log.warn("共享发票税率未获取到,使用默认税率0,金额={}", detailAmount);
  1169. }
  1170. // 计算金额: 含税金额 / (1 + 税率) = 税额
  1171. BigDecimal divisor = BigDecimal.ONE.add(ocrTaxRate);
  1172. BigDecimal sharedTaxAmount = detailAmount.divide(divisor, 2, BigDecimal.ROUND_HALF_UP);
  1173. BigDecimal sharedAmount = detailAmount.subtract(sharedTaxAmount);
  1174. BigDecimal sharedRate = ocrTaxRate; // 这是小数形式的税率,如 0.09
  1175. log.info("共享发票计算: 含税金额={}, 税率={}%, 不含税金额={}, 税额={}",
  1176. detailAmount, ocrTaxRate.multiply(new BigDecimal("100")),
  1177. sharedAmount, sharedTaxAmount);
  1178. // 保存共享发票数据
  1179. InvoiceLibrary sharedInvoice = new InvoiceLibrary();
  1180. sharedInvoice.setOaId(processInstanceId);
  1181. sharedInvoice.setOaStatus("0");
  1182. sharedInvoice.setInvoiceStatus("0");
  1183. sharedInvoice.setDep(cdbm != null && StringUtils.isNotBlank(cdbm) ? cdbm : (bm != null ? bm : ""));
  1184. sharedInvoice.setAccountTitle(bxlb != null ? bxlb : "");
  1185. sharedInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  1186. sharedInvoice.setAmount(null);
  1187. sharedInvoice.setTaxAmount(null);
  1188. sharedInvoice.setTotalAmount(null); // 共享发票不设置总金额
  1189. sharedInvoice.setFormName("付款申请");
  1190. sharedInvoice.setPaySubject(fkzhxx != null ? fkzhxx : "");
  1191. sharedInvoice.setPayAccount(fkzh != null ? fkzh : "");
  1192. sharedInvoice.setBankName(yhqc != null ? yhqc : "");
  1193. sharedInvoice.setIsLongTerm(sfct != null ? sfct : "");
  1194. sharedInvoice.setHasInvoice(hasInvoice);
  1195. sharedInvoice.setDetailAmount(detailAmount);
  1196. // 共享发票字段设置值 - 修正:存储数字而不是带百分号的字符串
  1197. sharedInvoice.setSharedTaxAmount(sharedAmount);
  1198. sharedInvoice.setSharedAmount(sharedTaxAmount);
  1199. sharedInvoice.setSharedRate(String.valueOf(sharedRate)); // 直接存储小数,如 0.09
  1200. sharedInvoice.setCreatedAt(LocalDateTime.now());
  1201. sharedInvoice.setUpdatedAt(LocalDateTime.now());
  1202. sharedInvoice.setInvoiceCode("");
  1203. sharedInvoice.setInvoiceNumber("");
  1204. sharedInvoice.setInvoiceType(sharedKind);
  1205. sharedInvoice.setBuyerName("");
  1206. sharedInvoice.setBuyerTaxId("");
  1207. sharedInvoice.setSellerName("");
  1208. sharedInvoice.setSellerTaxId("");
  1209. invoiceList.add(sharedInvoice);
  1210. log.info("成功保存共享发票记录,成本部门: {}", cdbm);
  1211. } catch (NumberFormatException e) {
  1212. log.error("共享发票金额格式转换失败: detailje={}", detailje, e);
  1213. } catch (Exception e) {
  1214. log.error("共享发票处理失败", e);
  1215. }
  1216. }
  1217. // 保存发票记录
  1218. if (!invoiceList.isEmpty()) {
  1219. for (InvoiceLibrary invoice : invoiceList) {
  1220. baseMapper.insert(invoice);
  1221. }
  1222. log.info("成功保存 {} 条发票记录(普通发票: {}, 共享发票: {})",
  1223. invoiceList.size(), parsedInvoiceDataList.size(), sharedInvoiceDataList.size());
  1224. }
  1225. addWorkflowComment1(processInstanceId, userId, totalAmount);
  1226. return McR.success(allResults);
  1227. } catch (Exception e) {
  1228. log.error("解析子表数据失败", e);
  1229. return McR.error("300", "解析数据失败: " + e.getMessage());
  1230. }
  1231. }
  1232. }
  1233. }
  1234. return McR.success();
  1235. } catch (Exception e) {
  1236. log.error("处理审批实例失败", e);
  1237. return McR.error("500", "系统处理失败: " + e.getMessage());
  1238. }
  1239. }
  1240. @Override
  1241. public McR invoiceLibrary2(Map map) {
  1242. log.info("接收到的参数: {}", map);
  1243. try {
  1244. String processInstanceId = UtilMap.getString(map, "processInstanceId");
  1245. if (StringUtils.isBlank(processInstanceId)) {
  1246. log.error("processInstanceId为空");
  1247. return McR.error("400", "审批实例ID不能为空");
  1248. }
  1249. // 获取审批实例信息
  1250. String accessToken = ddClient.getAccessToken();
  1251. Map processInstance = ddClientWorkflow.getProcessInstanceId(accessToken, processInstanceId);
  1252. if (processInstance == null) {
  1253. log.error("获取审批实例失败: {}", processInstanceId);
  1254. return McR.error("500", "获取审批实例信息失败");
  1255. }
  1256. String userId = (String) processInstance.get("originatorUserId");
  1257. log.info("审批人ID: {}", userId);
  1258. List<Map> formComponentValues = (List<Map>) processInstance.get("formComponentValues");
  1259. if (formComponentValues == null || formComponentValues.isEmpty()) {
  1260. log.warn("表单数据为空");
  1261. return McR.error("400", "表单数据为空");
  1262. }
  1263. // 存储主表的字段值
  1264. String zt = null;
  1265. BigDecimal mainJe = null;
  1266. String bxlb = null;
  1267. String bm = null;
  1268. String fkzhxx = null;
  1269. String fkzh = null;
  1270. String yhqc = null;
  1271. String sfct = null;
  1272. // 遍历收集主表字段值
  1273. for (Map formComponentValue : formComponentValues) {
  1274. String id = String.valueOf(formComponentValue.get("id"));
  1275. Object value = formComponentValue.get("value");
  1276. if ("DDSelectField_15ZO6PMU1ZC00".equals(id)) {
  1277. zt = value != null ? String.valueOf(value) : "";
  1278. }
  1279. if ("MoneyField_1V4A9P72K6TC0".equals(id)) {
  1280. if (value instanceof BigDecimal) {
  1281. mainJe = (BigDecimal) value;
  1282. } else if (value != null) {
  1283. try {
  1284. mainJe = new BigDecimal(String.valueOf(value));
  1285. } catch (NumberFormatException e) {
  1286. log.error("金额格式转换失败: {}", value);
  1287. mainJe = BigDecimal.ZERO;
  1288. }
  1289. }
  1290. }
  1291. if ("DDSelectField_YLLB7AD1ATS0".equals(id)) {
  1292. bxlb = value != null ? String.valueOf(value) : "";
  1293. }
  1294. if ("DepartmentField_ZI9KALEYOTC0".equals(id)) {
  1295. bm = value != null ? String.valueOf(value) : "";
  1296. }
  1297. if ("DDSelectField_1WEX2WWCJ3PC0".equals(id)) {
  1298. fkzhxx = value != null ? String.valueOf(value) : "";
  1299. }
  1300. if ("TextField_2W39FPCZH1A0".equals(id)) {
  1301. fkzh = value != null ? String.valueOf(value) : "";
  1302. }
  1303. if ("TextField_QW7HJIR7T800".equals(id)) {
  1304. yhqc = value != null ? String.valueOf(value) : "";
  1305. }
  1306. if ("DDSelectField_12JNC3P1K5V40".equals(id)) {
  1307. sfct = value != null ? String.valueOf(value) : "否";
  1308. }
  1309. }
  1310. // 判断主表状态 - 无发票
  1311. if ("否".equals(zt)) {
  1312. // 保存基础报销记录
  1313. InvoiceLibrary baseInvoice = new InvoiceLibrary();
  1314. baseInvoice.setOaId(processInstanceId);
  1315. baseInvoice.setOaStatus("0");
  1316. baseInvoice.setInvoiceStatus("0");
  1317. baseInvoice.setDep(bm != null ? bm : "");
  1318. baseInvoice.setAccountTitle(bxlb != null ? bxlb : "");
  1319. baseInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  1320. baseInvoice.setCreatedAt(LocalDateTime.now());
  1321. baseInvoice.setUpdatedAt(LocalDateTime.now());
  1322. baseInvoice.setInvoiceCode("");
  1323. baseInvoice.setInvoiceNumber("");
  1324. baseInvoice.setInvoiceType("");
  1325. baseInvoice.setAmount(BigDecimal.ZERO);
  1326. baseInvoice.setTaxAmount(BigDecimal.ZERO);
  1327. baseInvoice.setTotalAmount(BigDecimal.ZERO);
  1328. baseInvoice.setBuyerName("");
  1329. baseInvoice.setBuyerTaxId("");
  1330. baseInvoice.setSellerName("");
  1331. baseInvoice.setSellerTaxId("");
  1332. baseInvoice.setFormName("合作商付款申请");
  1333. baseInvoice.setPaySubject(fkzhxx != null ? fkzhxx : "");
  1334. baseInvoice.setPayAccount(fkzh != null ? fkzh : "");
  1335. baseInvoice.setBankName(yhqc != null ? yhqc : "");
  1336. baseInvoice.setIsLongTerm(sfct != null ? sfct : "");
  1337. baseInvoice.setHasInvoice("否");
  1338. baseInvoice.setDetailAmount(null);
  1339. baseInvoice.setSharedTaxAmount(null);
  1340. baseInvoice.setSharedAmount(null);
  1341. baseInvoice.setSharedRate(null);
  1342. baseMapper.insert(baseInvoice);
  1343. addWorkflowComment(processInstanceId, userId, "报销总金额:" + mainJe);
  1344. return McR.success();
  1345. }
  1346. // 判断主表状态 - 有发票
  1347. if ("是".equals(zt)) {
  1348. for (Map formComponentValue : formComponentValues) {
  1349. String id = String.valueOf(formComponentValue.get("id"));
  1350. if ("TableField_182UNZEX02U80".equals(id)) {
  1351. String tableFieldValue = String.valueOf(formComponentValue.get("value"));
  1352. log.info("子表数据: {}", tableFieldValue);
  1353. try {
  1354. List<Map> tableRows = (List<Map>) JSONObject.parse(tableFieldValue);
  1355. if (tableRows == null || tableRows.isEmpty()) {
  1356. log.warn("子表数据为空");
  1357. return McR.error("400", "发票明细为空");
  1358. }
  1359. List<Object> allResults = new ArrayList<>();
  1360. List<InvoiceLibrary> invoiceList = new ArrayList<>();
  1361. // 存储第一遍解析的发票数据,避免重复解析
  1362. List<Map<String, Object>> parsedInvoiceDataList = new ArrayList<>();
  1363. // 存储共享发票数据
  1364. List<Map<String, Object>> sharedInvoiceDataList = new ArrayList<>();
  1365. // 用于校验的集合
  1366. List<String> invoiceNumberList = new ArrayList<>();
  1367. Map<String, String> invoiceNumberToFileName = new HashMap<>();
  1368. List<String> invalidBuyerTaxIds = new ArrayList<>();
  1369. // 存储税率值,供共享发票使用
  1370. BigDecimal sharedTaxRate = BigDecimal.ZERO;
  1371. String sharedKind = "";
  1372. // ========== 第一遍遍历:收集并校验所有发票数据 ==========
  1373. for (Map row : tableRows) {
  1374. List<Map> rowValues = (List<Map>) row.get("rowValue");
  1375. String hasInvoice = null;
  1376. String cdbm = null;
  1377. String detailje = null;
  1378. List<Map> attachmentList = null;
  1379. for (Map rowItem : rowValues) {
  1380. String key = String.valueOf(rowItem.get("key"));
  1381. if ("DDSelectField_44I0SP6M1I00".equals(key)) {
  1382. Object value = rowItem.get("value");
  1383. hasInvoice = value != null ? String.valueOf(value) : "";
  1384. }
  1385. if ("DepartmentField_RC2YZRJQAQ80".equals(key)) {
  1386. Object value = rowItem.get("value");
  1387. cdbm = value != null ? String.valueOf(value) : "";
  1388. }
  1389. if ("NumberField_1UC3HZ8OEIPS0".equals(key)) {
  1390. Object value = rowItem.get("value");
  1391. detailje = value != null ? String.valueOf(value) : "";
  1392. }
  1393. if ("DDAttachment_LEUWYIC68TC0".equals(key)) {
  1394. Object value = rowItem.get("value");
  1395. if (value instanceof List) {
  1396. attachmentList = (List<Map>) value;
  1397. }
  1398. }
  1399. }
  1400. if ("是".equals(hasInvoice) && attachmentList != null && !attachmentList.isEmpty()) {
  1401. for (Map attachment : attachmentList) {
  1402. String fileId = UtilMap.getString(attachment, "fileId");
  1403. String fileType = UtilMap.getString(attachment, "fileType");
  1404. String fileName = UtilMap.getString(attachment, "fileName");
  1405. try {
  1406. String filePath = downloadPath + fileId + "." + fileType;
  1407. downloadDdFile(processInstanceId, fileId, filePath);
  1408. String hz = "qw/files/" + fileId + "." + fileType;
  1409. Map fileMap = new HashMap();
  1410. fileMap.put("url", url + hz);
  1411. fileMap.put("size", new File(filePath).length() / 1024f / 1024f);
  1412. fileMap.put("isPdf", "pdf".equalsIgnoreCase(fileType));
  1413. McR<Object> result = processMixedInvoice(fileMap);
  1414. Object responseData = result.getData();
  1415. // 解析发票数据
  1416. if (responseData != null) {
  1417. String jsonStr = JSONObject.toJSONString(responseData);
  1418. JSONObject jsonObject = JSONObject.parseObject(jsonStr);
  1419. JSONArray resultArray = jsonObject.getJSONArray("result");
  1420. if (resultArray != null && !resultArray.isEmpty()) {
  1421. JSONObject invoice = resultArray.getJSONObject(0);
  1422. String invoiceNumber = invoice.getString("serial") != null ? invoice.getString("serial") : "";
  1423. String buyerTaxId = invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "";
  1424. String buyerName = invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "";
  1425. // 获取税率,供共享发票使用
  1426. Object taxRateObj = invoice.get("taxRate");
  1427. Object kind = invoice.getString("kindName");
  1428. if (taxRateObj != null) {
  1429. String taxRateStr = String.valueOf(taxRateObj);
  1430. String taxRateNum = taxRateStr.replace("%", "").trim();
  1431. sharedKind = kind != null ? String.valueOf(kind) : "";
  1432. sharedTaxRate = new BigDecimal(taxRateNum);
  1433. sharedTaxRate = sharedTaxRate.divide(new BigDecimal("100"), 10, BigDecimal.ROUND_HALF_UP);
  1434. log.info("从有发票中获取税率: 原始={}, 转换后={}", taxRateStr, sharedTaxRate);
  1435. }
  1436. // 保存解析的数据供后续使用
  1437. Map<String, Object> parsedData = new HashMap<>();
  1438. parsedData.put("invoice", invoice);
  1439. parsedData.put("fileId", fileId);
  1440. parsedData.put("fileName", fileName);
  1441. parsedData.put("fileType", fileType);
  1442. parsedData.put("cdbm", cdbm);
  1443. parsedData.put("hasInvoice", hasInvoice);
  1444. parsedData.put("detailje", detailje);
  1445. parsedData.put("responseData", responseData);
  1446. parsedInvoiceDataList.add(parsedData);
  1447. // ========== 校验1:抬头校验 ==========
  1448. if (StringUtils.isNotBlank(buyerName)) {
  1449. CompanyTitle existingTitle = companyTitleMapper.selectByTaxId(buyerName);
  1450. if (existingTitle == null) {
  1451. String errorInfo = String.format("发票: %s, 购买方名称: %s, 税号: %s",
  1452. fileName, buyerName, buyerTaxId);
  1453. invalidBuyerTaxIds.add(errorInfo);
  1454. log.warn("抬头校验失败: 税号={} 不存在于抬头库, 文件名={}", buyerTaxId, fileName);
  1455. }
  1456. } else {
  1457. String errorInfo = String.format("发票: %s, 购买方税号为空", fileName);
  1458. invalidBuyerTaxIds.add(errorInfo);
  1459. log.warn("抬头校验失败: 购买方税号为空, 文件名={}", fileName);
  1460. }
  1461. // ========== 校验2:发票号重复校验 ==========
  1462. if (StringUtils.isNotBlank(invoiceNumber)) {
  1463. // 检查数据库中是否已存在相同的发票号
  1464. InvoiceLibrary existingInvoice = baseMapper.selectByInvoiceNumber(invoiceNumber);
  1465. if (existingInvoice != null) {
  1466. String errorMsg = String.format("发票号重复: %s (当前文件: %s, 已存在于OA审批单: %s)",
  1467. invoiceNumber, fileName, existingInvoice.getOaId());
  1468. log.error(errorMsg);
  1469. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  1470. return McR.error("400", errorMsg);
  1471. }
  1472. // 同时检查同一审批单内是否有重复
  1473. if (invoiceNumberList.contains(invoiceNumber)) {
  1474. String errorMsg = String.format("当前审批单内发票号重复: %s (当前文件: %s, 已存在文件: %s)",
  1475. invoiceNumber, fileName, invoiceNumberToFileName.get(invoiceNumber));
  1476. log.error(errorMsg);
  1477. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  1478. return McR.error("400", errorMsg);
  1479. }
  1480. invoiceNumberList.add(invoiceNumber);
  1481. invoiceNumberToFileName.put(invoiceNumber, fileName);
  1482. }
  1483. }
  1484. }
  1485. } catch (Exception e) {
  1486. log.error("发票解析异常: fileId={}, fileName={}", fileId, fileName, e);
  1487. terminateWorkflow(accessToken, processInstanceId, userId, "发票解析失败: " + e.getMessage());
  1488. return McR.error("400", "发票解析失败: " + e.getMessage());
  1489. }
  1490. }
  1491. } else if ("共享".equals(hasInvoice)) {
  1492. // 收集共享发票数据,待第二遍遍历时保存
  1493. if (detailje != null && StringUtils.isNotBlank(detailje)) {
  1494. Map<String, Object> sharedData = new HashMap<>();
  1495. sharedData.put("detailje", detailje);
  1496. sharedData.put("cdbm", cdbm);
  1497. sharedData.put("hasInvoice", hasInvoice);
  1498. sharedInvoiceDataList.add(sharedData);
  1499. log.info("收集共享发票数据: 金额={}, 成本部门={}", detailje, cdbm);
  1500. }
  1501. }
  1502. }
  1503. // ========== 抬头校验失败处理 ==========
  1504. if (!invalidBuyerTaxIds.isEmpty()) {
  1505. String errorMsg = "发票抬头有误,以下发票的购买方税号不在公司抬头库中:\n" + String.join("\n", invalidBuyerTaxIds);
  1506. log.error(errorMsg);
  1507. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  1508. return McR.error("400", errorMsg);
  1509. }
  1510. BigDecimal totalAmount = BigDecimal.ZERO;
  1511. // ========== 第二遍遍历:保存发票数据(校验通过后) ==========
  1512. // 1. 保存普通发票数据
  1513. for (Map<String, Object> parsedData : parsedInvoiceDataList) {
  1514. JSONObject invoice = (JSONObject) parsedData.get("invoice");
  1515. Object responseData = parsedData.get("responseData");
  1516. String cdbm = (String) parsedData.get("cdbm");
  1517. String hasInvoice = (String) parsedData.get("hasInvoice");
  1518. String detailje = (String) parsedData.get("detailje"); // 修改为 String 类型
  1519. allResults.add(responseData);
  1520. // 汇总 amount
  1521. BigDecimal amount = invoice.getBigDecimal("amount");
  1522. if (amount != null) {
  1523. totalAmount = totalAmount.add(amount);
  1524. }
  1525. // 构建InvoiceLibrary对象
  1526. InvoiceLibrary invoiceLibrary = new InvoiceLibrary();
  1527. invoiceLibrary.setOaId(processInstanceId);
  1528. invoiceLibrary.setInvoiceCode(invoice.getString("code") != null ? invoice.getString("code") : "");
  1529. invoiceLibrary.setInvoiceNumber(invoice.getString("serial") != null ? invoice.getString("serial") : "");
  1530. invoiceLibrary.setInvoiceType(invoice.getString("kindName") != null ? invoice.getString("kindName") : "");
  1531. String date = invoice.getString("date");
  1532. if (StringUtils.isNotBlank(date)) {
  1533. try {
  1534. invoiceLibrary.setInvoiceDate(LocalDate.parse(date.trim()));
  1535. } catch (Exception e) {
  1536. log.warn("日期解析失败: {}", date);
  1537. }
  1538. }
  1539. invoiceLibrary.setAmount(invoice.getBigDecimal("excludingTax") != null ? invoice.getBigDecimal("excludingTax") : BigDecimal.ZERO);
  1540. invoiceLibrary.setTaxAmount(invoice.getBigDecimal("tax") != null ? invoice.getBigDecimal("tax") : BigDecimal.ZERO);
  1541. invoiceLibrary.setTotalAmount(invoice.getBigDecimal("amount") != null ? invoice.getBigDecimal("amount") : BigDecimal.ZERO);
  1542. invoiceLibrary.setBuyerName(invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "");
  1543. invoiceLibrary.setBuyerTaxId(invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "");
  1544. invoiceLibrary.setSellerName(invoice.getString("sellerName") != null ? invoice.getString("sellerName") : "");
  1545. invoiceLibrary.setSellerTaxId(invoice.getString("sellerTaxId") != null ? invoice.getString("sellerTaxId") : "");
  1546. invoiceLibrary.setOaStatus("0");
  1547. invoiceLibrary.setFormName("合作商付款申请");
  1548. invoiceLibrary.setPaySubject(fkzhxx != null ? fkzhxx : "");
  1549. invoiceLibrary.setPayAccount(fkzh != null ? fkzh : "");
  1550. invoiceLibrary.setBankName(yhqc != null ? yhqc : "");
  1551. invoiceLibrary.setIsLongTerm(sfct != null ? sfct : "");
  1552. invoiceLibrary.setInvoiceStatus("0");
  1553. invoiceLibrary.setHasInvoice(hasInvoice);
  1554. // 处理 detailAmount,将 String 转换为 BigDecimal
  1555. if (detailje != null && StringUtils.isNotBlank(detailje)) {
  1556. try {
  1557. invoiceLibrary.setDetailAmount(new BigDecimal(detailje));
  1558. } catch (NumberFormatException e) {
  1559. log.error("detailJe格式转换失败: {}", detailje);
  1560. invoiceLibrary.setDetailAmount(null);
  1561. }
  1562. } else {
  1563. invoiceLibrary.setDetailAmount(null);
  1564. }
  1565. // 普通发票不涉及共享字段,设置为 null
  1566. invoiceLibrary.setSharedTaxAmount(null);
  1567. invoiceLibrary.setSharedAmount(null);
  1568. invoiceLibrary.setSharedRate(null);
  1569. invoiceLibrary.setAccountTitle(bxlb != null ? bxlb : "");
  1570. invoiceLibrary.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  1571. invoiceLibrary.setCreatedAt(LocalDateTime.now());
  1572. invoiceLibrary.setUpdatedAt(LocalDateTime.now());
  1573. // 设置成本部门
  1574. if (cdbm != null && StringUtils.isNotBlank(cdbm)) {
  1575. invoiceLibrary.setDep(cdbm);
  1576. } else {
  1577. invoiceLibrary.setDep(bm != null ? bm : "");
  1578. }
  1579. invoiceList.add(invoiceLibrary);
  1580. }
  1581. for (Map<String, Object> sharedData : sharedInvoiceDataList) {
  1582. String detailje = (String) sharedData.get("detailje");
  1583. String cdbm = (String) sharedData.get("cdbm");
  1584. String hasInvoice = (String) sharedData.get("hasInvoice");
  1585. try {
  1586. BigDecimal detailAmount = new BigDecimal(detailje);
  1587. // 使用从普通发票中解析到的税率
  1588. BigDecimal ocrTaxRate = sharedTaxRate;
  1589. if (ocrTaxRate.compareTo(BigDecimal.ZERO) == 0) {
  1590. log.warn("共享发票税率未获取到,使用默认税率0,金额={}", detailAmount);
  1591. }
  1592. // 计算金额: 含税金额 / (1 + 税率) = 税额
  1593. BigDecimal divisor = BigDecimal.ONE.add(ocrTaxRate);
  1594. BigDecimal sharedTaxAmount = detailAmount.divide(divisor, 2, BigDecimal.ROUND_HALF_UP);
  1595. BigDecimal sharedAmount = detailAmount.subtract(sharedTaxAmount);
  1596. BigDecimal sharedRate = ocrTaxRate; // 这是小数形式的税率,如 0.09
  1597. log.info("共享发票计算: 含税金额={}, 税率={}%, 不含税金额={}, 税额={}",
  1598. detailAmount, ocrTaxRate.multiply(new BigDecimal("100")),
  1599. sharedAmount, sharedTaxAmount);
  1600. // 保存共享发票数据
  1601. InvoiceLibrary sharedInvoice = new InvoiceLibrary();
  1602. sharedInvoice.setOaId(processInstanceId);
  1603. sharedInvoice.setOaStatus("0");
  1604. sharedInvoice.setInvoiceStatus("0");
  1605. sharedInvoice.setDep(cdbm != null && StringUtils.isNotBlank(cdbm) ? cdbm : (bm != null ? bm : ""));
  1606. sharedInvoice.setAccountTitle(bxlb != null ? bxlb : "");
  1607. sharedInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  1608. sharedInvoice.setAmount(null);
  1609. sharedInvoice.setTaxAmount(null);
  1610. sharedInvoice.setTotalAmount(null); // 共享发票不设置总金额
  1611. sharedInvoice.setFormName("合作商付款申请");
  1612. sharedInvoice.setPaySubject(fkzhxx != null ? fkzhxx : "");
  1613. sharedInvoice.setPayAccount(fkzh != null ? fkzh : "");
  1614. sharedInvoice.setBankName(yhqc != null ? yhqc : "");
  1615. sharedInvoice.setIsLongTerm(sfct != null ? sfct : "");
  1616. sharedInvoice.setHasInvoice(hasInvoice);
  1617. sharedInvoice.setDetailAmount(detailAmount);
  1618. // 共享发票字段设置值 - 修正:存储数字而不是带百分号的字符串
  1619. sharedInvoice.setSharedTaxAmount(sharedAmount);
  1620. sharedInvoice.setSharedAmount(sharedTaxAmount);
  1621. sharedInvoice.setSharedRate(String.valueOf(sharedRate)); // 直接存储小数,如 0.09
  1622. sharedInvoice.setCreatedAt(LocalDateTime.now());
  1623. sharedInvoice.setUpdatedAt(LocalDateTime.now());
  1624. sharedInvoice.setInvoiceCode("");
  1625. sharedInvoice.setInvoiceNumber("");
  1626. sharedInvoice.setInvoiceType(sharedKind);
  1627. sharedInvoice.setBuyerName("");
  1628. sharedInvoice.setBuyerTaxId("");
  1629. sharedInvoice.setSellerName("");
  1630. sharedInvoice.setSellerTaxId("");
  1631. invoiceList.add(sharedInvoice);
  1632. log.info("成功保存共享发票记录,成本部门: {}", cdbm);
  1633. } catch (NumberFormatException e) {
  1634. log.error("共享发票金额格式转换失败: detailje={}", detailje, e);
  1635. } catch (Exception e) {
  1636. log.error("共享发票处理失败", e);
  1637. }
  1638. }
  1639. // 保存发票记录
  1640. if (!invoiceList.isEmpty()) {
  1641. for (InvoiceLibrary invoice : invoiceList) {
  1642. baseMapper.insert(invoice);
  1643. }
  1644. log.info("成功保存 {} 条发票记录(普通发票: {}, 共享发票: {})",
  1645. invoiceList.size(), parsedInvoiceDataList.size(), sharedInvoiceDataList.size());
  1646. }
  1647. addWorkflowComment1(processInstanceId, userId, totalAmount);
  1648. return McR.success(allResults);
  1649. } catch (Exception e) {
  1650. log.error("解析子表数据失败", e);
  1651. return McR.error("300", "解析数据失败: " + e.getMessage());
  1652. }
  1653. }
  1654. }
  1655. }
  1656. return McR.success();
  1657. } catch (Exception e) {
  1658. log.error("处理审批实例失败", e);
  1659. return McR.error("500", "系统处理失败: " + e.getMessage());
  1660. }
  1661. }
  1662. @Override
  1663. public McR invoiceLibrary3(Map map) {
  1664. log.info("接收到的参数: {}", map);
  1665. try {
  1666. String processInstanceId = UtilMap.getString(map, "processInstanceId");
  1667. if (StringUtils.isBlank(processInstanceId)) {
  1668. log.error("processInstanceId为空");
  1669. return McR.error("400", "审批实例ID不能为空");
  1670. }
  1671. // 获取审批实例信息
  1672. String accessToken = ddClient.getAccessToken();
  1673. Map processInstance = ddClientWorkflow.getProcessInstanceId(accessToken, processInstanceId);
  1674. if (processInstance == null) {
  1675. log.error("获取审批实例失败: {}", processInstanceId);
  1676. return McR.error("500", "获取审批实例信息失败");
  1677. }
  1678. String userId = (String) processInstance.get("originatorUserId");
  1679. log.info("审批人ID: {}", userId);
  1680. List<Map> formComponentValues = (List<Map>) processInstance.get("formComponentValues");
  1681. if (formComponentValues == null || formComponentValues.isEmpty()) {
  1682. log.warn("表单数据为空");
  1683. return McR.error("400", "表单数据为空");
  1684. }
  1685. // 存储主表的字段值
  1686. String zt = null;
  1687. BigDecimal mainJe = null;
  1688. String bxlb = null;
  1689. String bm = null;
  1690. String fkzhxx = null;
  1691. String fkzh = null;
  1692. String yhqc = null;
  1693. String sfct = null;
  1694. // 遍历收集主表字段值
  1695. for (Map formComponentValue : formComponentValues) {
  1696. String id = String.valueOf(formComponentValue.get("id"));
  1697. Object value = formComponentValue.get("value");
  1698. if ("DDSelectField_19B5ZAI2SE000".equals(id)) {
  1699. zt = value != null ? String.valueOf(value) : "";//ocr识别
  1700. }
  1701. if ("MoneyField_5U5SLLA3CS40".equals(id)) {
  1702. if (value instanceof BigDecimal) {
  1703. mainJe = (BigDecimal) value;
  1704. } else if (value != null) {
  1705. try {
  1706. mainJe = new BigDecimal(String.valueOf(value));
  1707. } catch (NumberFormatException e) {
  1708. log.error("金额格式转换失败: {}", value);
  1709. mainJe = BigDecimal.ZERO;
  1710. }
  1711. }
  1712. }
  1713. if ("DDSelectField_BS1HCMO7YTK0".equals(id)) {
  1714. bxlb = value != null ? String.valueOf(value) : "";
  1715. }
  1716. // if ("DepartmentField_ZI9KALEYOTC0".equals(id)) {
  1717. // bm = value != null ? String.valueOf(value) : "";
  1718. // }
  1719. if ("DDSelectField_GLW8YHBHSLS0".equals(id)) {
  1720. fkzhxx = value != null ? String.valueOf(value) : "";
  1721. }
  1722. if ("TextField_1PR1HQ4H28SG0".equals(id)) {
  1723. fkzh = value != null ? String.valueOf(value) : "";
  1724. }
  1725. if ("TextField_1UPR1XZTI3VK".equals(id)) {
  1726. yhqc = value != null ? String.valueOf(value) : "";
  1727. }
  1728. if ("DDSelectField_1TMBRS8T7DPC0".equals(id)) {
  1729. sfct = value != null ? String.valueOf(value) : "否";
  1730. }
  1731. }
  1732. // 判断主表状态 - 无发票
  1733. if ("否".equals(zt)) {
  1734. // 保存基础报销记录
  1735. InvoiceLibrary baseInvoice = new InvoiceLibrary();
  1736. baseInvoice.setOaId(processInstanceId);
  1737. baseInvoice.setOaStatus("0");
  1738. baseInvoice.setInvoiceStatus("0");
  1739. baseInvoice.setDep(bm != null ? bm : "");
  1740. baseInvoice.setAccountTitle(bxlb != null ? bxlb : "");
  1741. baseInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  1742. baseInvoice.setCreatedAt(LocalDateTime.now());
  1743. baseInvoice.setUpdatedAt(LocalDateTime.now());
  1744. baseInvoice.setInvoiceCode("");
  1745. baseInvoice.setInvoiceNumber("");
  1746. baseInvoice.setInvoiceType("");
  1747. baseInvoice.setAmount(BigDecimal.ZERO);
  1748. baseInvoice.setTaxAmount(BigDecimal.ZERO);
  1749. baseInvoice.setTotalAmount(BigDecimal.ZERO);
  1750. baseInvoice.setBuyerName("");
  1751. baseInvoice.setBuyerTaxId("");
  1752. baseInvoice.setSellerName("");
  1753. baseInvoice.setSellerTaxId("");
  1754. baseInvoice.setFormName("合作商报销申请");
  1755. baseInvoice.setPaySubject(fkzhxx != null ? fkzhxx : "");
  1756. baseInvoice.setPayAccount(fkzh != null ? fkzh : "");
  1757. baseInvoice.setBankName(yhqc != null ? yhqc : "");
  1758. baseInvoice.setIsLongTerm(sfct != null ? sfct : "");
  1759. baseInvoice.setHasInvoice("否");
  1760. baseInvoice.setDetailAmount(null);
  1761. baseInvoice.setSharedTaxAmount(null);
  1762. baseInvoice.setSharedAmount(null);
  1763. baseInvoice.setSharedRate(null);
  1764. baseMapper.insert(baseInvoice);
  1765. addWorkflowComment(processInstanceId, userId, "报销总金额:" + mainJe);
  1766. return McR.success();
  1767. }
  1768. // 判断主表状态 - 有发票
  1769. if ("是".equals(zt)) {
  1770. for (Map formComponentValue : formComponentValues) {
  1771. String id = String.valueOf(formComponentValue.get("id"));
  1772. if ("TableField_9LJ65U3CQ980".equals(id)) {
  1773. String tableFieldValue = String.valueOf(formComponentValue.get("value"));
  1774. log.info("子表数据: {}", tableFieldValue);
  1775. try {
  1776. List<Map> tableRows = (List<Map>) JSONObject.parse(tableFieldValue);
  1777. if (tableRows == null || tableRows.isEmpty()) {
  1778. log.warn("子表数据为空");
  1779. return McR.error("400", "发票明细为空");
  1780. }
  1781. List<Object> allResults = new ArrayList<>();
  1782. List<InvoiceLibrary> invoiceList = new ArrayList<>();
  1783. // 存储第一遍解析的发票数据,避免重复解析
  1784. List<Map<String, Object>> parsedInvoiceDataList = new ArrayList<>();
  1785. // 存储共享发票数据
  1786. List<Map<String, Object>> sharedInvoiceDataList = new ArrayList<>();
  1787. // 用于校验的集合
  1788. List<String> invoiceNumberList = new ArrayList<>();
  1789. Map<String, String> invoiceNumberToFileName = new HashMap<>();
  1790. List<String> invalidBuyerTaxIds = new ArrayList<>();
  1791. // 存储税率值,供共享发票使用
  1792. BigDecimal sharedTaxRate = BigDecimal.ZERO;
  1793. String sharedKind = "";
  1794. // ========== 第一遍遍历:收集并校验所有发票数据 ==========
  1795. for (Map row : tableRows) {
  1796. List<Map> rowValues = (List<Map>) row.get("rowValue");
  1797. String hasInvoice = null;
  1798. String cdbm = null;
  1799. String detailje = null;
  1800. List<Map> attachmentList = null;
  1801. for (Map rowItem : rowValues) {
  1802. String key = String.valueOf(rowItem.get("key"));
  1803. if ("DDSelectField_TTTOAI0FDXJ4".equals(key)) {
  1804. Object value = rowItem.get("value");
  1805. hasInvoice = value != null ? String.valueOf(value) : "";
  1806. }
  1807. if ("DepartmentField_15QGD1PV69XC0".equals(key)) {
  1808. Object value = rowItem.get("value");
  1809. cdbm = value != null ? String.valueOf(value) : "";
  1810. }
  1811. if ("NumberField_FYHZK218TTDS".equals(key)) {
  1812. Object value = rowItem.get("value");
  1813. detailje = value != null ? String.valueOf(value) : "";
  1814. }
  1815. if ("DDAttachment_XVU9V89QOEM8".equals(key)) {
  1816. Object value = rowItem.get("value");
  1817. if (value instanceof List) {
  1818. attachmentList = (List<Map>) value;
  1819. }
  1820. }
  1821. }
  1822. if ("是".equals(hasInvoice) && attachmentList != null && !attachmentList.isEmpty()) {
  1823. for (Map attachment : attachmentList) {
  1824. String fileId = UtilMap.getString(attachment, "fileId");
  1825. String fileType = UtilMap.getString(attachment, "fileType");
  1826. String fileName = UtilMap.getString(attachment, "fileName");
  1827. try {
  1828. String filePath = downloadPath + fileId + "." + fileType;
  1829. downloadDdFile(processInstanceId, fileId, filePath);
  1830. String hz = "qw/files/" + fileId + "." + fileType;
  1831. Map fileMap = new HashMap();
  1832. fileMap.put("url", url + hz);
  1833. fileMap.put("size", new File(filePath).length() / 1024f / 1024f);
  1834. fileMap.put("isPdf", "pdf".equalsIgnoreCase(fileType));
  1835. McR<Object> result = processMixedInvoice(fileMap);
  1836. Object responseData = result.getData();
  1837. // 解析发票数据
  1838. if (responseData != null) {
  1839. String jsonStr = JSONObject.toJSONString(responseData);
  1840. JSONObject jsonObject = JSONObject.parseObject(jsonStr);
  1841. JSONArray resultArray = jsonObject.getJSONArray("result");
  1842. if (resultArray != null && !resultArray.isEmpty()) {
  1843. JSONObject invoice = resultArray.getJSONObject(0);
  1844. String invoiceNumber = invoice.getString("serial") != null ? invoice.getString("serial") : "";
  1845. String buyerTaxId = invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "";
  1846. String buyerName = invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "";
  1847. // 获取税率,供共享发票使用
  1848. Object taxRateObj = invoice.get("taxRate");
  1849. Object kind = invoice.getString("kindName");
  1850. if (taxRateObj != null) {
  1851. String taxRateStr = String.valueOf(taxRateObj);
  1852. String taxRateNum = taxRateStr.replace("%", "").trim();
  1853. sharedKind = kind != null ? String.valueOf(kind) : "";
  1854. sharedTaxRate = new BigDecimal(taxRateNum);
  1855. sharedTaxRate = sharedTaxRate.divide(new BigDecimal("100"), 10, BigDecimal.ROUND_HALF_UP);
  1856. log.info("从有发票中获取税率: 原始={}, 转换后={}", taxRateStr, sharedTaxRate);
  1857. }
  1858. // 保存解析的数据供后续使用
  1859. Map<String, Object> parsedData = new HashMap<>();
  1860. parsedData.put("invoice", invoice);
  1861. parsedData.put("fileId", fileId);
  1862. parsedData.put("fileName", fileName);
  1863. parsedData.put("fileType", fileType);
  1864. parsedData.put("cdbm", cdbm);
  1865. parsedData.put("hasInvoice", hasInvoice);
  1866. parsedData.put("detailje", detailje);
  1867. parsedData.put("responseData", responseData);
  1868. parsedInvoiceDataList.add(parsedData);
  1869. // ========== 校验1:抬头校验 ==========
  1870. if (StringUtils.isNotBlank(buyerName)) {
  1871. CompanyTitle existingTitle = companyTitleMapper.selectByTaxId(buyerName);
  1872. if (existingTitle == null) {
  1873. String errorInfo = String.format("发票: %s, 购买方名称: %s, 税号: %s",
  1874. fileName, buyerName, buyerTaxId);
  1875. invalidBuyerTaxIds.add(errorInfo);
  1876. log.warn("抬头校验失败: 税号={} 不存在于抬头库, 文件名={}", buyerTaxId, fileName);
  1877. }
  1878. } else {
  1879. String errorInfo = String.format("发票: %s, 购买方税号为空", fileName);
  1880. invalidBuyerTaxIds.add(errorInfo);
  1881. log.warn("抬头校验失败: 购买方税号为空, 文件名={}", fileName);
  1882. }
  1883. // ========== 校验2:发票号重复校验 ==========
  1884. if (StringUtils.isNotBlank(invoiceNumber)) {
  1885. // 检查数据库中是否已存在相同的发票号
  1886. InvoiceLibrary existingInvoice = baseMapper.selectByInvoiceNumber(invoiceNumber);
  1887. if (existingInvoice != null) {
  1888. String errorMsg = String.format("发票号重复: %s (当前文件: %s, 已存在于OA审批单: %s)",
  1889. invoiceNumber, fileName, existingInvoice.getOaId());
  1890. log.error(errorMsg);
  1891. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  1892. return McR.error("400", errorMsg);
  1893. }
  1894. // 同时检查同一审批单内是否有重复
  1895. if (invoiceNumberList.contains(invoiceNumber)) {
  1896. String errorMsg = String.format("当前审批单内发票号重复: %s (当前文件: %s, 已存在文件: %s)",
  1897. invoiceNumber, fileName, invoiceNumberToFileName.get(invoiceNumber));
  1898. log.error(errorMsg);
  1899. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  1900. return McR.error("400", errorMsg);
  1901. }
  1902. invoiceNumberList.add(invoiceNumber);
  1903. invoiceNumberToFileName.put(invoiceNumber, fileName);
  1904. }
  1905. }
  1906. }
  1907. } catch (Exception e) {
  1908. log.error("发票解析异常: fileId={}, fileName={}", fileId, fileName, e);
  1909. terminateWorkflow(accessToken, processInstanceId, userId, "发票解析失败: " + e.getMessage());
  1910. return McR.error("400", "发票解析失败: " + e.getMessage());
  1911. }
  1912. }
  1913. } else if ("共享".equals(hasInvoice)) {
  1914. // 收集共享发票数据,待第二遍遍历时保存
  1915. if (detailje != null && StringUtils.isNotBlank(detailje)) {
  1916. Map<String, Object> sharedData = new HashMap<>();
  1917. sharedData.put("detailje", detailje);
  1918. sharedData.put("cdbm", cdbm);
  1919. sharedData.put("hasInvoice", hasInvoice);
  1920. sharedInvoiceDataList.add(sharedData);
  1921. log.info("收集共享发票数据: 金额={}, 成本部门={}", detailje, cdbm);
  1922. }
  1923. }
  1924. }
  1925. // ========== 抬头校验失败处理 ==========
  1926. if (!invalidBuyerTaxIds.isEmpty()) {
  1927. String errorMsg = "发票抬头有误,以下发票的购买方税号不在公司抬头库中:\n" + String.join("\n", invalidBuyerTaxIds);
  1928. log.error(errorMsg);
  1929. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  1930. return McR.error("400", errorMsg);
  1931. }
  1932. BigDecimal totalAmount = BigDecimal.ZERO;
  1933. // ========== 第二遍遍历:保存发票数据(校验通过后) ==========
  1934. // 1. 保存普通发票数据
  1935. for (Map<String, Object> parsedData : parsedInvoiceDataList) {
  1936. JSONObject invoice = (JSONObject) parsedData.get("invoice");
  1937. Object responseData = parsedData.get("responseData");
  1938. String cdbm = (String) parsedData.get("cdbm");
  1939. String hasInvoice = (String) parsedData.get("hasInvoice");
  1940. String detailje = (String) parsedData.get("detailje"); // 修改为 String 类型
  1941. allResults.add(responseData);
  1942. // 汇总 amount
  1943. BigDecimal amount = invoice.getBigDecimal("amount");
  1944. if (amount != null) {
  1945. totalAmount = totalAmount.add(amount);
  1946. }
  1947. // 构建InvoiceLibrary对象
  1948. InvoiceLibrary invoiceLibrary = new InvoiceLibrary();
  1949. invoiceLibrary.setOaId(processInstanceId);
  1950. invoiceLibrary.setInvoiceCode(invoice.getString("code") != null ? invoice.getString("code") : "");
  1951. invoiceLibrary.setInvoiceNumber(invoice.getString("serial") != null ? invoice.getString("serial") : "");
  1952. invoiceLibrary.setInvoiceType(invoice.getString("kindName") != null ? invoice.getString("kindName") : "");
  1953. String date = invoice.getString("date");
  1954. if (StringUtils.isNotBlank(date)) {
  1955. try {
  1956. invoiceLibrary.setInvoiceDate(LocalDate.parse(date.trim()));
  1957. } catch (Exception e) {
  1958. log.warn("日期解析失败: {}", date);
  1959. }
  1960. }
  1961. invoiceLibrary.setAmount(invoice.getBigDecimal("excludingTax") != null ? invoice.getBigDecimal("excludingTax") : BigDecimal.ZERO);
  1962. invoiceLibrary.setTaxAmount(invoice.getBigDecimal("tax") != null ? invoice.getBigDecimal("tax") : BigDecimal.ZERO);
  1963. invoiceLibrary.setTotalAmount(invoice.getBigDecimal("amount") != null ? invoice.getBigDecimal("amount") : BigDecimal.ZERO);
  1964. invoiceLibrary.setBuyerName(invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "");
  1965. invoiceLibrary.setBuyerTaxId(invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "");
  1966. invoiceLibrary.setSellerName(invoice.getString("sellerName") != null ? invoice.getString("sellerName") : "");
  1967. invoiceLibrary.setSellerTaxId(invoice.getString("sellerTaxId") != null ? invoice.getString("sellerTaxId") : "");
  1968. invoiceLibrary.setOaStatus("0");
  1969. invoiceLibrary.setFormName("合作商报销申请");
  1970. invoiceLibrary.setPaySubject(fkzhxx != null ? fkzhxx : "");
  1971. invoiceLibrary.setPayAccount(fkzh != null ? fkzh : "");
  1972. invoiceLibrary.setBankName(yhqc != null ? yhqc : "");
  1973. invoiceLibrary.setIsLongTerm(sfct != null ? sfct : "");
  1974. invoiceLibrary.setInvoiceStatus("0");
  1975. invoiceLibrary.setHasInvoice(hasInvoice);
  1976. // 处理 detailAmount,将 String 转换为 BigDecimal
  1977. if (detailje != null && StringUtils.isNotBlank(detailje)) {
  1978. try {
  1979. invoiceLibrary.setDetailAmount(new BigDecimal(detailje));
  1980. } catch (NumberFormatException e) {
  1981. log.error("detailJe格式转换失败: {}", detailje);
  1982. invoiceLibrary.setDetailAmount(null);
  1983. }
  1984. } else {
  1985. invoiceLibrary.setDetailAmount(null);
  1986. }
  1987. // 普通发票不涉及共享字段,设置为 null
  1988. invoiceLibrary.setSharedTaxAmount(null);
  1989. invoiceLibrary.setSharedAmount(null);
  1990. invoiceLibrary.setSharedRate(null);
  1991. invoiceLibrary.setAccountTitle(bxlb != null ? bxlb : "");
  1992. invoiceLibrary.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  1993. invoiceLibrary.setCreatedAt(LocalDateTime.now());
  1994. invoiceLibrary.setUpdatedAt(LocalDateTime.now());
  1995. // 设置成本部门
  1996. if (cdbm != null && StringUtils.isNotBlank(cdbm)) {
  1997. invoiceLibrary.setDep(cdbm);
  1998. } else {
  1999. invoiceLibrary.setDep(bm != null ? bm : "");
  2000. }
  2001. invoiceList.add(invoiceLibrary);
  2002. }
  2003. for (Map<String, Object> sharedData : sharedInvoiceDataList) {
  2004. String detailje = (String) sharedData.get("detailje");
  2005. String cdbm = (String) sharedData.get("cdbm");
  2006. String hasInvoice = (String) sharedData.get("hasInvoice");
  2007. try {
  2008. BigDecimal detailAmount = new BigDecimal(detailje);
  2009. // 使用从普通发票中解析到的税率
  2010. BigDecimal ocrTaxRate = sharedTaxRate;
  2011. if (ocrTaxRate.compareTo(BigDecimal.ZERO) == 0) {
  2012. log.warn("共享发票税率未获取到,使用默认税率0,金额={}", detailAmount);
  2013. }
  2014. // 计算金额: 含税金额 / (1 + 税率) = 税额
  2015. BigDecimal divisor = BigDecimal.ONE.add(ocrTaxRate);
  2016. BigDecimal sharedTaxAmount = detailAmount.divide(divisor, 2, BigDecimal.ROUND_HALF_UP);
  2017. BigDecimal sharedAmount = detailAmount.subtract(sharedTaxAmount);
  2018. BigDecimal sharedRate = ocrTaxRate; // 这是小数形式的税率,如 0.09
  2019. log.info("共享发票计算: 含税金额={}, 税率={}%, 不含税金额={}, 税额={}",
  2020. detailAmount, ocrTaxRate.multiply(new BigDecimal("100")),
  2021. sharedAmount, sharedTaxAmount);
  2022. // 保存共享发票数据
  2023. InvoiceLibrary sharedInvoice = new InvoiceLibrary();
  2024. sharedInvoice.setOaId(processInstanceId);
  2025. sharedInvoice.setOaStatus("0");
  2026. sharedInvoice.setInvoiceStatus("0");
  2027. sharedInvoice.setDep(cdbm != null && StringUtils.isNotBlank(cdbm) ? cdbm : (bm != null ? bm : ""));
  2028. sharedInvoice.setAccountTitle(bxlb != null ? bxlb : "");
  2029. sharedInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  2030. sharedInvoice.setAmount(null);
  2031. sharedInvoice.setTaxAmount(null);
  2032. sharedInvoice.setTotalAmount(null); // 共享发票不设置总金额
  2033. sharedInvoice.setFormName("合作商报销申请");
  2034. sharedInvoice.setPaySubject(fkzhxx != null ? fkzhxx : "");
  2035. sharedInvoice.setPayAccount(fkzh != null ? fkzh : "");
  2036. sharedInvoice.setBankName(yhqc != null ? yhqc : "");
  2037. sharedInvoice.setIsLongTerm(sfct != null ? sfct : "");
  2038. sharedInvoice.setHasInvoice(hasInvoice);
  2039. sharedInvoice.setDetailAmount(detailAmount);
  2040. // 共享发票字段设置值 - 修正:存储数字而不是带百分号的字符串
  2041. sharedInvoice.setSharedTaxAmount(sharedAmount);
  2042. sharedInvoice.setSharedAmount(sharedTaxAmount);
  2043. sharedInvoice.setSharedRate(String.valueOf(sharedRate)); // 直接存储小数,如 0.09
  2044. sharedInvoice.setCreatedAt(LocalDateTime.now());
  2045. sharedInvoice.setUpdatedAt(LocalDateTime.now());
  2046. sharedInvoice.setInvoiceCode("");
  2047. sharedInvoice.setInvoiceNumber("");
  2048. sharedInvoice.setInvoiceType(sharedKind);
  2049. sharedInvoice.setBuyerName("");
  2050. sharedInvoice.setBuyerTaxId("");
  2051. sharedInvoice.setSellerName("");
  2052. sharedInvoice.setSellerTaxId("");
  2053. invoiceList.add(sharedInvoice);
  2054. log.info("成功保存共享发票记录,成本部门: {}", cdbm);
  2055. } catch (NumberFormatException e) {
  2056. log.error("共享发票金额格式转换失败: detailje={}", detailje, e);
  2057. } catch (Exception e) {
  2058. log.error("共享发票处理失败", e);
  2059. }
  2060. }
  2061. // 保存发票记录
  2062. if (!invoiceList.isEmpty()) {
  2063. for (InvoiceLibrary invoice : invoiceList) {
  2064. baseMapper.insert(invoice);
  2065. }
  2066. log.info("成功保存 {} 条发票记录(普通发票: {}, 共享发票: {})",
  2067. invoiceList.size(), parsedInvoiceDataList.size(), sharedInvoiceDataList.size());
  2068. }
  2069. addWorkflowComment1(processInstanceId, userId, totalAmount);
  2070. return McR.success(allResults);
  2071. } catch (Exception e) {
  2072. log.error("解析子表数据失败", e);
  2073. return McR.error("300", "解析数据失败: " + e.getMessage());
  2074. }
  2075. }
  2076. }
  2077. }
  2078. return McR.success();
  2079. } catch (Exception e) {
  2080. log.error("处理审批实例失败", e);
  2081. return McR.error("500", "系统处理失败: " + e.getMessage());
  2082. }
  2083. }
  2084. @Override
  2085. public McR invoiceLibrary5(Map map) {
  2086. log.info("接收到的参数: {}", map);
  2087. try {
  2088. String processInstanceId = UtilMap.getString(map, "processInstanceId");
  2089. if (StringUtils.isBlank(processInstanceId)) {
  2090. log.error("processInstanceId为空");
  2091. return McR.error("400", "审批实例ID不能为空");
  2092. }
  2093. // 获取审批实例信息
  2094. String accessToken = ddClient.getAccessToken();
  2095. Map processInstance = ddClientWorkflow.getProcessInstanceId(accessToken, processInstanceId);
  2096. if (processInstance == null) {
  2097. log.error("获取审批实例失败: {}", processInstanceId);
  2098. return McR.error("500", "获取审批实例信息失败");
  2099. }
  2100. String userId = (String) processInstance.get("originatorUserId");
  2101. log.info("审批人ID: {}", userId);
  2102. List<Map> formComponentValues = (List<Map>) processInstance.get("formComponentValues");
  2103. if (formComponentValues == null || formComponentValues.isEmpty()) {
  2104. log.warn("表单数据为空");
  2105. return McR.error("400", "表单数据为空");
  2106. }
  2107. // 存储主表的字段值
  2108. String zt = null;
  2109. BigDecimal mainJe = null;
  2110. String bxlb = null;
  2111. String bm = null;
  2112. String fkzhxx = null;
  2113. String fkzh = null;
  2114. String yhqc = null;
  2115. String sfct = null;
  2116. // 遍历收集主表字段值
  2117. for (Map formComponentValue : formComponentValues) {
  2118. String id = String.valueOf(formComponentValue.get("id"));
  2119. Object value = formComponentValue.get("value");
  2120. if ("DDSelectField_7F1V8IJJ9QG".equals(id)) {
  2121. zt = value != null ? String.valueOf(value) : "";//ocr识别
  2122. }
  2123. if ("MoneyField_1435GRXGYPA80".equals(id)) {
  2124. if (value instanceof BigDecimal) {
  2125. mainJe = (BigDecimal) value;
  2126. } else if (value != null) {
  2127. try {
  2128. mainJe = new BigDecimal(String.valueOf(value));
  2129. } catch (NumberFormatException e) {
  2130. log.error("金额格式转换失败: {}", value);
  2131. mainJe = BigDecimal.ZERO;
  2132. }
  2133. }
  2134. }
  2135. if ("DDSelectField_1UX0JVU89Q000".equals(id)) {
  2136. bxlb = value != null ? String.valueOf(value) : "";
  2137. }
  2138. if ("DepartmentField_1BJT6UY9Y5B40".equals(id)) {
  2139. bm = value != null ? String.valueOf(value) : "";
  2140. }
  2141. if ("DDSelectField_1D77TV92WXEO0".equals(id)) {
  2142. fkzhxx = value != null ? String.valueOf(value) : "";
  2143. }
  2144. if ("TextField_3CQPLF9BVBO0".equals(id)) {
  2145. fkzh = value != null ? String.valueOf(value) : "";
  2146. }
  2147. if ("TextField_YSFYQQ305SG0".equals(id)) {
  2148. yhqc = value != null ? String.valueOf(value) : "";
  2149. }
  2150. if ("DDSelectField_1AYKIPZ3435S0".equals(id)) {
  2151. sfct = value != null ? String.valueOf(value) : "否";
  2152. }
  2153. }
  2154. // 判断主表状态 - 无发票
  2155. if ("否".equals(zt)) {
  2156. // 保存基础报销记录
  2157. InvoiceLibrary baseInvoice = new InvoiceLibrary();
  2158. baseInvoice.setOaId(processInstanceId);
  2159. baseInvoice.setOaStatus("0");
  2160. baseInvoice.setInvoiceStatus("0");
  2161. baseInvoice.setDep(bm != null ? bm : "");
  2162. baseInvoice.setAccountTitle(bxlb != null ? bxlb : "");
  2163. baseInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  2164. baseInvoice.setCreatedAt(LocalDateTime.now());
  2165. baseInvoice.setUpdatedAt(LocalDateTime.now());
  2166. baseInvoice.setInvoiceCode("");
  2167. baseInvoice.setInvoiceNumber("");
  2168. baseInvoice.setInvoiceType("");
  2169. baseInvoice.setAmount(BigDecimal.ZERO);
  2170. baseInvoice.setTaxAmount(BigDecimal.ZERO);
  2171. baseInvoice.setTotalAmount(BigDecimal.ZERO);
  2172. baseInvoice.setBuyerName("");
  2173. baseInvoice.setBuyerTaxId("");
  2174. baseInvoice.setSellerName("");
  2175. baseInvoice.setSellerTaxId("");
  2176. baseInvoice.setFormName("出差费用报销");
  2177. baseInvoice.setPaySubject(fkzhxx != null ? fkzhxx : "");
  2178. baseInvoice.setPayAccount(fkzh != null ? fkzh : "");
  2179. baseInvoice.setBankName(yhqc != null ? yhqc : "");
  2180. baseInvoice.setIsLongTerm(sfct != null ? sfct : "");
  2181. baseInvoice.setHasInvoice("否");
  2182. baseInvoice.setDetailAmount(null);
  2183. baseInvoice.setSharedTaxAmount(null);
  2184. baseInvoice.setSharedAmount(null);
  2185. baseInvoice.setSharedRate(null);
  2186. baseMapper.insert(baseInvoice);
  2187. addWorkflowComment(processInstanceId, userId, "报销总金额:" + mainJe);
  2188. return McR.success();
  2189. }
  2190. // 判断主表状态 - 有发票
  2191. if ("是".equals(zt)) {
  2192. for (Map formComponentValue : formComponentValues) {
  2193. String id = String.valueOf(formComponentValue.get("id"));
  2194. if ("TableField_169TONKJ3KW00".equals(id)) {
  2195. String tableFieldValue = String.valueOf(formComponentValue.get("value"));
  2196. log.info("子表数据: {}", tableFieldValue);
  2197. try {
  2198. List<Map> tableRows = (List<Map>) JSONObject.parse(tableFieldValue);
  2199. if (tableRows == null || tableRows.isEmpty()) {
  2200. log.warn("子表数据为空");
  2201. return McR.error("400", "发票明细为空");
  2202. }
  2203. List<Object> allResults = new ArrayList<>();
  2204. List<InvoiceLibrary> invoiceList = new ArrayList<>();
  2205. // 存储第一遍解析的发票数据,避免重复解析
  2206. List<Map<String, Object>> parsedInvoiceDataList = new ArrayList<>();
  2207. // 存储共享发票数据
  2208. List<Map<String, Object>> sharedInvoiceDataList = new ArrayList<>();
  2209. // 用于校验的集合
  2210. List<String> invoiceNumberList = new ArrayList<>();
  2211. Map<String, String> invoiceNumberToFileName = new HashMap<>();
  2212. List<String> invalidBuyerTaxIds = new ArrayList<>();
  2213. // 存储税率值,供共享发票使用
  2214. BigDecimal sharedTaxRate = BigDecimal.ZERO;
  2215. String sharedKind = "";
  2216. // ========== 第一遍遍历:收集并校验所有发票数据 ==========
  2217. for (Map row : tableRows) {
  2218. List<Map> rowValues = (List<Map>) row.get("rowValue");
  2219. String hasInvoice = null;
  2220. String cdbm = null;
  2221. String detailje = null;
  2222. List<Map> attachmentList = null;
  2223. for (Map rowItem : rowValues) {
  2224. String key = String.valueOf(rowItem.get("key"));
  2225. if ("DDSelectField_1G6XMYA3FMCG0".equals(key)) {
  2226. Object value = rowItem.get("value");
  2227. hasInvoice = value != null ? String.valueOf(value) : "";
  2228. }
  2229. if ("DepartmentField_KJIWTTZLE800".equals(key)) {
  2230. Object value = rowItem.get("value");
  2231. cdbm = value != null ? String.valueOf(value) : "";
  2232. }
  2233. if ("NumberField_L0ALER5S6180".equals(key)) {
  2234. Object value = rowItem.get("value");
  2235. detailje = value != null ? String.valueOf(value) : "";
  2236. }
  2237. if ("DDAttachment_EPL7ILUTD0G0".equals(key)) {
  2238. Object value = rowItem.get("value");
  2239. if (value instanceof List) {
  2240. attachmentList = (List<Map>) value;
  2241. }
  2242. }
  2243. }
  2244. if ("是".equals(hasInvoice) && attachmentList != null && !attachmentList.isEmpty()) {
  2245. for (Map attachment : attachmentList) {
  2246. String fileId = UtilMap.getString(attachment, "fileId");
  2247. String fileType = UtilMap.getString(attachment, "fileType");
  2248. String fileName = UtilMap.getString(attachment, "fileName");
  2249. try {
  2250. String filePath = downloadPath + fileId + "." + fileType;
  2251. downloadDdFile(processInstanceId, fileId, filePath);
  2252. String hz = "qw/files/" + fileId + "." + fileType;
  2253. Map fileMap = new HashMap();
  2254. fileMap.put("url", url + hz);
  2255. fileMap.put("size", new File(filePath).length() / 1024f / 1024f);
  2256. fileMap.put("isPdf", "pdf".equalsIgnoreCase(fileType));
  2257. McR<Object> result = processMixedInvoice(fileMap);
  2258. Object responseData = result.getData();
  2259. // 解析发票数据
  2260. if (responseData != null) {
  2261. String jsonStr = JSONObject.toJSONString(responseData);
  2262. JSONObject jsonObject = JSONObject.parseObject(jsonStr);
  2263. JSONArray resultArray = jsonObject.getJSONArray("result");
  2264. if (resultArray != null && !resultArray.isEmpty()) {
  2265. JSONObject invoice = resultArray.getJSONObject(0);
  2266. String invoiceNumber = invoice.getString("serial") != null ? invoice.getString("serial") : "";
  2267. String buyerTaxId = invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "";
  2268. String buyerName = invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "";
  2269. // 获取税率,供共享发票使用
  2270. Object taxRateObj = invoice.get("taxRate");
  2271. Object kind = invoice.getString("kindName");
  2272. if (taxRateObj != null) {
  2273. String taxRateStr = String.valueOf(taxRateObj);
  2274. String taxRateNum = taxRateStr.replace("%", "").trim();
  2275. sharedKind = kind != null ? String.valueOf(kind) : "";
  2276. sharedTaxRate = new BigDecimal(taxRateNum);
  2277. sharedTaxRate = sharedTaxRate.divide(new BigDecimal("100"), 10, BigDecimal.ROUND_HALF_UP);
  2278. log.info("从有发票中获取税率: 原始={}, 转换后={}", taxRateStr, sharedTaxRate);
  2279. }
  2280. // 保存解析的数据供后续使用
  2281. Map<String, Object> parsedData = new HashMap<>();
  2282. parsedData.put("invoice", invoice);
  2283. parsedData.put("fileId", fileId);
  2284. parsedData.put("fileName", fileName);
  2285. parsedData.put("fileType", fileType);
  2286. parsedData.put("cdbm", cdbm);
  2287. parsedData.put("hasInvoice", hasInvoice);
  2288. parsedData.put("detailje", detailje);
  2289. parsedData.put("responseData", responseData);
  2290. parsedInvoiceDataList.add(parsedData);
  2291. // ========== 校验1:抬头校验 ==========
  2292. if (StringUtils.isNotBlank(buyerName)) {
  2293. CompanyTitle existingTitle = companyTitleMapper.selectByTaxId(buyerName);
  2294. if (existingTitle == null) {
  2295. String errorInfo = String.format("发票: %s, 购买方名称: %s, 税号: %s",
  2296. fileName, buyerName, buyerTaxId);
  2297. invalidBuyerTaxIds.add(errorInfo);
  2298. log.warn("抬头校验失败: 税号={} 不存在于抬头库, 文件名={}", buyerTaxId, fileName);
  2299. }
  2300. } else {
  2301. String errorInfo = String.format("发票: %s, 购买方税号为空", fileName);
  2302. invalidBuyerTaxIds.add(errorInfo);
  2303. log.warn("抬头校验失败: 购买方税号为空, 文件名={}", fileName);
  2304. }
  2305. // ========== 校验2:发票号重复校验 ==========
  2306. if (StringUtils.isNotBlank(invoiceNumber)) {
  2307. // 检查数据库中是否已存在相同的发票号
  2308. InvoiceLibrary existingInvoice = baseMapper.selectByInvoiceNumber(invoiceNumber);
  2309. if (existingInvoice != null) {
  2310. String errorMsg = String.format("发票号重复: %s (当前文件: %s, 已存在于OA审批单: %s)",
  2311. invoiceNumber, fileName, existingInvoice.getOaId());
  2312. log.error(errorMsg);
  2313. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  2314. return McR.error("400", errorMsg);
  2315. }
  2316. // 同时检查同一审批单内是否有重复
  2317. if (invoiceNumberList.contains(invoiceNumber)) {
  2318. String errorMsg = String.format("当前审批单内发票号重复: %s (当前文件: %s, 已存在文件: %s)",
  2319. invoiceNumber, fileName, invoiceNumberToFileName.get(invoiceNumber));
  2320. log.error(errorMsg);
  2321. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  2322. return McR.error("400", errorMsg);
  2323. }
  2324. invoiceNumberList.add(invoiceNumber);
  2325. invoiceNumberToFileName.put(invoiceNumber, fileName);
  2326. }
  2327. }
  2328. }
  2329. } catch (Exception e) {
  2330. log.error("发票解析异常: fileId={}, fileName={}", fileId, fileName, e);
  2331. terminateWorkflow(accessToken, processInstanceId, userId, "发票解析失败: " + e.getMessage());
  2332. return McR.error("400", "发票解析失败: " + e.getMessage());
  2333. }
  2334. }
  2335. } else if ("共享".equals(hasInvoice)) {
  2336. // 收集共享发票数据,待第二遍遍历时保存
  2337. if (detailje != null && StringUtils.isNotBlank(detailje)) {
  2338. Map<String, Object> sharedData = new HashMap<>();
  2339. sharedData.put("detailje", detailje);
  2340. sharedData.put("cdbm", cdbm);
  2341. sharedData.put("hasInvoice", hasInvoice);
  2342. sharedInvoiceDataList.add(sharedData);
  2343. log.info("收集共享发票数据: 金额={}, 成本部门={}", detailje, cdbm);
  2344. }
  2345. }
  2346. }
  2347. // ========== 抬头校验失败处理 ==========
  2348. if (!invalidBuyerTaxIds.isEmpty()) {
  2349. String errorMsg = "发票抬头有误,以下发票的购买方税号不在公司抬头库中:\n" + String.join("\n", invalidBuyerTaxIds);
  2350. log.error(errorMsg);
  2351. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  2352. return McR.error("400", errorMsg);
  2353. }
  2354. BigDecimal totalAmount = BigDecimal.ZERO;
  2355. // ========== 第二遍遍历:保存发票数据(校验通过后) ==========
  2356. // 1. 保存普通发票数据
  2357. for (Map<String, Object> parsedData : parsedInvoiceDataList) {
  2358. JSONObject invoice = (JSONObject) parsedData.get("invoice");
  2359. Object responseData = parsedData.get("responseData");
  2360. String cdbm = (String) parsedData.get("cdbm");
  2361. String hasInvoice = (String) parsedData.get("hasInvoice");
  2362. String detailje = (String) parsedData.get("detailje"); // 修改为 String 类型
  2363. allResults.add(responseData);
  2364. // 汇总 amount
  2365. BigDecimal amount = invoice.getBigDecimal("amount");
  2366. if (amount != null) {
  2367. totalAmount = totalAmount.add(amount);
  2368. }
  2369. // 构建InvoiceLibrary对象
  2370. InvoiceLibrary invoiceLibrary = new InvoiceLibrary();
  2371. invoiceLibrary.setOaId(processInstanceId);
  2372. invoiceLibrary.setInvoiceCode(invoice.getString("code") != null ? invoice.getString("code") : "");
  2373. invoiceLibrary.setInvoiceNumber(invoice.getString("serial") != null ? invoice.getString("serial") : "");
  2374. invoiceLibrary.setInvoiceType(invoice.getString("kindName") != null ? invoice.getString("kindName") : "");
  2375. String date = invoice.getString("date");
  2376. if (StringUtils.isNotBlank(date)) {
  2377. try {
  2378. invoiceLibrary.setInvoiceDate(LocalDate.parse(date.trim()));
  2379. } catch (Exception e) {
  2380. log.warn("日期解析失败: {}", date);
  2381. }
  2382. }
  2383. invoiceLibrary.setAmount(invoice.getBigDecimal("excludingTax") != null ? invoice.getBigDecimal("excludingTax") : BigDecimal.ZERO);
  2384. invoiceLibrary.setTaxAmount(invoice.getBigDecimal("tax") != null ? invoice.getBigDecimal("tax") : BigDecimal.ZERO);
  2385. invoiceLibrary.setTotalAmount(invoice.getBigDecimal("amount") != null ? invoice.getBigDecimal("amount") : BigDecimal.ZERO);
  2386. invoiceLibrary.setBuyerName(invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "");
  2387. invoiceLibrary.setBuyerTaxId(invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "");
  2388. invoiceLibrary.setSellerName(invoice.getString("sellerName") != null ? invoice.getString("sellerName") : "");
  2389. invoiceLibrary.setSellerTaxId(invoice.getString("sellerTaxId") != null ? invoice.getString("sellerTaxId") : "");
  2390. invoiceLibrary.setOaStatus("0");
  2391. invoiceLibrary.setFormName("出差费用报销");
  2392. invoiceLibrary.setPaySubject(fkzhxx != null ? fkzhxx : "");
  2393. invoiceLibrary.setPayAccount(fkzh != null ? fkzh : "");
  2394. invoiceLibrary.setBankName(yhqc != null ? yhqc : "");
  2395. invoiceLibrary.setIsLongTerm(sfct != null ? sfct : "");
  2396. invoiceLibrary.setInvoiceStatus("0");
  2397. invoiceLibrary.setHasInvoice(hasInvoice);
  2398. // 处理 detailAmount,将 String 转换为 BigDecimal
  2399. if (detailje != null && StringUtils.isNotBlank(detailje)) {
  2400. try {
  2401. invoiceLibrary.setDetailAmount(new BigDecimal(detailje));
  2402. } catch (NumberFormatException e) {
  2403. log.error("detailJe格式转换失败: {}", detailje);
  2404. invoiceLibrary.setDetailAmount(null);
  2405. }
  2406. } else {
  2407. invoiceLibrary.setDetailAmount(null);
  2408. }
  2409. // 普通发票不涉及共享字段,设置为 null
  2410. invoiceLibrary.setSharedTaxAmount(null);
  2411. invoiceLibrary.setSharedAmount(null);
  2412. invoiceLibrary.setSharedRate(null);
  2413. invoiceLibrary.setAccountTitle(bxlb != null ? bxlb : "");
  2414. invoiceLibrary.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  2415. invoiceLibrary.setCreatedAt(LocalDateTime.now());
  2416. invoiceLibrary.setUpdatedAt(LocalDateTime.now());
  2417. // 设置成本部门
  2418. if (cdbm != null && StringUtils.isNotBlank(cdbm)) {
  2419. invoiceLibrary.setDep(cdbm);
  2420. } else {
  2421. invoiceLibrary.setDep(bm != null ? bm : "");
  2422. }
  2423. invoiceList.add(invoiceLibrary);
  2424. }
  2425. for (Map<String, Object> sharedData : sharedInvoiceDataList) {
  2426. String detailje = (String) sharedData.get("detailje");
  2427. String cdbm = (String) sharedData.get("cdbm");
  2428. String hasInvoice = (String) sharedData.get("hasInvoice");
  2429. try {
  2430. BigDecimal detailAmount = new BigDecimal(detailje);
  2431. // 使用从普通发票中解析到的税率
  2432. BigDecimal ocrTaxRate = sharedTaxRate;
  2433. if (ocrTaxRate.compareTo(BigDecimal.ZERO) == 0) {
  2434. log.warn("共享发票税率未获取到,使用默认税率0,金额={}", detailAmount);
  2435. }
  2436. // 计算金额: 含税金额 / (1 + 税率) = 税额
  2437. BigDecimal divisor = BigDecimal.ONE.add(ocrTaxRate);
  2438. BigDecimal sharedTaxAmount = detailAmount.divide(divisor, 2, BigDecimal.ROUND_HALF_UP);
  2439. BigDecimal sharedAmount = detailAmount.subtract(sharedTaxAmount);
  2440. BigDecimal sharedRate = ocrTaxRate; // 这是小数形式的税率,如 0.09
  2441. log.info("共享发票计算: 含税金额={}, 税率={}%, 不含税金额={}, 税额={}",
  2442. detailAmount, ocrTaxRate.multiply(new BigDecimal("100")),
  2443. sharedAmount, sharedTaxAmount);
  2444. // 保存共享发票数据
  2445. InvoiceLibrary sharedInvoice = new InvoiceLibrary();
  2446. sharedInvoice.setOaId(processInstanceId);
  2447. sharedInvoice.setOaStatus("0");
  2448. sharedInvoice.setInvoiceStatus("0");
  2449. sharedInvoice.setDep(cdbm != null && StringUtils.isNotBlank(cdbm) ? cdbm : (bm != null ? bm : ""));
  2450. sharedInvoice.setAccountTitle(bxlb != null ? bxlb : "");
  2451. sharedInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  2452. sharedInvoice.setAmount(null);
  2453. sharedInvoice.setTaxAmount(null);
  2454. sharedInvoice.setTotalAmount(null); // 共享发票不设置总金额
  2455. sharedInvoice.setFormName("出差费用报销");
  2456. sharedInvoice.setPaySubject(fkzhxx != null ? fkzhxx : "");
  2457. sharedInvoice.setPayAccount(fkzh != null ? fkzh : "");
  2458. sharedInvoice.setBankName(yhqc != null ? yhqc : "");
  2459. sharedInvoice.setIsLongTerm(sfct != null ? sfct : "");
  2460. sharedInvoice.setHasInvoice(hasInvoice);
  2461. sharedInvoice.setDetailAmount(detailAmount);
  2462. // 共享发票字段设置值 - 修正:存储数字而不是带百分号的字符串
  2463. sharedInvoice.setSharedTaxAmount(sharedAmount);
  2464. sharedInvoice.setSharedAmount(sharedTaxAmount);
  2465. sharedInvoice.setSharedRate(String.valueOf(sharedRate)); // 直接存储小数,如 0.09
  2466. sharedInvoice.setCreatedAt(LocalDateTime.now());
  2467. sharedInvoice.setUpdatedAt(LocalDateTime.now());
  2468. sharedInvoice.setInvoiceCode("");
  2469. sharedInvoice.setInvoiceNumber("");
  2470. sharedInvoice.setInvoiceType(sharedKind);
  2471. sharedInvoice.setBuyerName("");
  2472. sharedInvoice.setBuyerTaxId("");
  2473. sharedInvoice.setSellerName("");
  2474. sharedInvoice.setSellerTaxId("");
  2475. invoiceList.add(sharedInvoice);
  2476. log.info("成功保存共享发票记录,成本部门: {}", cdbm);
  2477. } catch (NumberFormatException e) {
  2478. log.error("共享发票金额格式转换失败: detailje={}", detailje, e);
  2479. } catch (Exception e) {
  2480. log.error("共享发票处理失败", e);
  2481. }
  2482. }
  2483. // 保存发票记录
  2484. if (!invoiceList.isEmpty()) {
  2485. for (InvoiceLibrary invoice : invoiceList) {
  2486. baseMapper.insert(invoice);
  2487. }
  2488. log.info("成功保存 {} 条发票记录(普通发票: {}, 共享发票: {})",
  2489. invoiceList.size(), parsedInvoiceDataList.size(), sharedInvoiceDataList.size());
  2490. }
  2491. addWorkflowComment1(processInstanceId, userId, totalAmount);
  2492. return McR.success(allResults);
  2493. } catch (Exception e) {
  2494. log.error("解析子表数据失败", e);
  2495. return McR.error("300", "解析数据失败: " + e.getMessage());
  2496. }
  2497. }
  2498. }
  2499. }
  2500. return McR.success();
  2501. } catch (Exception e) {
  2502. log.error("处理审批实例失败", e);
  2503. return McR.error("500", "系统处理失败: " + e.getMessage());
  2504. }
  2505. }
  2506. @Override
  2507. public McR invoiceLibrary6(Map map) {
  2508. log.info("接收到的参数: {}", map);
  2509. try {
  2510. String processInstanceId = UtilMap.getString(map, "processInstanceId");
  2511. if (StringUtils.isBlank(processInstanceId)) {
  2512. log.error("processInstanceId为空");
  2513. return McR.error("400", "审批实例ID不能为空");
  2514. }
  2515. // 获取审批实例信息
  2516. String accessToken = ddClient.getAccessToken();
  2517. Map processInstance = ddClientWorkflow.getProcessInstanceId(accessToken, processInstanceId);
  2518. if (processInstance == null) {
  2519. log.error("获取审批实例失败: {}", processInstanceId);
  2520. return McR.error("500", "获取审批实例信息失败");
  2521. }
  2522. String userId = (String) processInstance.get("originatorUserId");
  2523. log.info("审批人ID: {}", userId);
  2524. List<Map> formComponentValues = (List<Map>) processInstance.get("formComponentValues");
  2525. if (formComponentValues == null || formComponentValues.isEmpty()) {
  2526. log.warn("表单数据为空");
  2527. return McR.error("400", "表单数据为空");
  2528. }
  2529. // 存储主表的字段值
  2530. String zt = null;
  2531. BigDecimal mainJe = null;
  2532. String bxlb = null;
  2533. String bm = null;
  2534. String fkzhxx = null;
  2535. String fkzh = null;
  2536. String yhqc = null;
  2537. String sfct = null;
  2538. // 遍历收集主表字段值
  2539. for (Map formComponentValue : formComponentValues) {
  2540. String id = String.valueOf(formComponentValue.get("id"));
  2541. Object value = formComponentValue.get("value");
  2542. if ("DDSelectField_VWIFJ1JZK0W0".equals(id)) {
  2543. zt = value != null ? String.valueOf(value) : "";
  2544. }
  2545. if ("MoneyField_3QS10ALLABU0".equals(id)) {
  2546. if (value instanceof BigDecimal) {
  2547. mainJe = (BigDecimal) value;
  2548. } else if (value != null) {
  2549. try {
  2550. mainJe = new BigDecimal(String.valueOf(value));
  2551. } catch (NumberFormatException e) {
  2552. log.error("金额格式转换失败: {}", value, e);
  2553. mainJe = BigDecimal.ZERO;
  2554. }
  2555. }
  2556. }
  2557. // 如果需要这两个字段,请取消注释并填写正确的字段ID
  2558. if ("DDSelectField_YLLB7AD1ATS0".equals(id)) {
  2559. bxlb = value != null ? String.valueOf(value) : "";
  2560. }
  2561. if ("DepartmentField_RF93A6VHA9C0".equals(id)) {
  2562. bm = value != null ? String.valueOf(value) : "";
  2563. }
  2564. if ("DDSelectField_IQXAGL7Z8XC0".equals(id)) {
  2565. fkzhxx = value != null ? String.valueOf(value) : "";
  2566. }
  2567. if ("TextField_3W9CLQOZZ540".equals(id)) {
  2568. fkzh = value != null ? String.valueOf(value) : "";
  2569. }
  2570. if ("TextField_9PJ4JW8KTCS0".equals(id)) {
  2571. yhqc = value != null ? String.valueOf(value) : "";
  2572. }
  2573. if ("DDSelectField_21L6KT7DT70G0".equals(id)) {
  2574. sfct = value != null ? String.valueOf(value) : "否";
  2575. }
  2576. }
  2577. // 判断主表状态 - 无发票
  2578. if ("否".equals(zt)) {
  2579. // Map<String, Object> result = new HashMap<>();
  2580. // result.put("je", mainJe);
  2581. // result.put("status", "否");
  2582. // result.put("message", "无发票报销");
  2583. // 保存基础报销记录
  2584. InvoiceLibrary baseInvoice = new InvoiceLibrary();
  2585. baseInvoice.setOaId(processInstanceId);
  2586. baseInvoice.setOaStatus("0");
  2587. baseInvoice.setInvoiceStatus("0");
  2588. baseInvoice.setDep(bm != null ? bm : "");
  2589. baseInvoice.setAccountTitle(bxlb != null ? bxlb : "");
  2590. baseInvoice.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  2591. baseInvoice.setCreatedAt(LocalDateTime.now());
  2592. baseInvoice.setUpdatedAt(LocalDateTime.now());
  2593. baseInvoice.setInvoiceCode("");
  2594. baseInvoice.setInvoiceNumber("");
  2595. baseInvoice.setInvoiceType("");
  2596. baseInvoice.setAmount(BigDecimal.ZERO);
  2597. baseInvoice.setTaxAmount(BigDecimal.ZERO);
  2598. baseInvoice.setTotalAmount(BigDecimal.ZERO);
  2599. baseInvoice.setBuyerName("");
  2600. baseInvoice.setFormName("批量付款申请(一对多)");
  2601. baseInvoice.setPaySubject(fkzhxx != null ? fkzhxx : "");
  2602. baseInvoice.setPayAccount(fkzh != null ? fkzh : "");
  2603. baseInvoice.setBankName(yhqc != null ? yhqc : "");
  2604. baseInvoice.setIsLongTerm(sfct != null ? sfct : "");
  2605. baseInvoice.setBuyerTaxId("");
  2606. baseInvoice.setSellerName("");
  2607. baseInvoice.setSellerTaxId("");
  2608. baseMapper.insert(baseInvoice);
  2609. addWorkflowComment(processInstanceId, userId, "报销总金额:"+mainJe);
  2610. return McR.success();
  2611. }
  2612. // 判断主表状态 - 有发票
  2613. if ("是".equals(zt)) {
  2614. for (Map formComponentValue : formComponentValues) {
  2615. String id = String.valueOf(formComponentValue.get("id"));
  2616. if ("DDAttachment_1C1GRYIRFJOG0".equals(id)) {
  2617. // 获取附件value(它是一个JSON字符串)
  2618. Object attachmentValue = formComponentValue.get("value");
  2619. List<Map> attachmentList = null;
  2620. // 解析附件JSON字符串
  2621. if (attachmentValue != null) {
  2622. String attachmentJsonStr = String.valueOf(attachmentValue);
  2623. if (StringUtils.isNotBlank(attachmentJsonStr)) {
  2624. try {
  2625. attachmentList = JSONObject.parseArray(attachmentJsonStr, Map.class);
  2626. log.info("解析附件列表成功,共{}个附件", attachmentList != null ? attachmentList.size() : 0);
  2627. } catch (Exception e) {
  2628. log.error("解析附件JSON失败: {}", attachmentJsonStr, e);
  2629. return McR.error("400", "附件格式解析失败");
  2630. }
  2631. }
  2632. }
  2633. try {
  2634. List<Object> allResults = new ArrayList<>();
  2635. List<InvoiceLibrary> invoiceList = new ArrayList<>();
  2636. // 用于校验的集合
  2637. List<String> invoiceNumberList = new ArrayList<>();
  2638. Map<String, String> invoiceNumberToFileName = new HashMap<>();
  2639. List<String> invalidBuyerTaxIds = new ArrayList<>();
  2640. // 存储第一遍解析的发票数据,避免重复解析
  2641. List<Map<String, Object>> parsedInvoiceDataList = new ArrayList<>();
  2642. if (attachmentList != null && !attachmentList.isEmpty()) {
  2643. for (Map attachment : attachmentList) {
  2644. String fileId = UtilMap.getString(attachment, "fileId");
  2645. String fileType = UtilMap.getString(attachment, "fileType");
  2646. String fileName = UtilMap.getString(attachment, "fileName");
  2647. // 参数校验
  2648. if (StringUtils.isBlank(fileId) || StringUtils.isBlank(fileType)) {
  2649. log.warn("文件信息不完整: fileId={}, fileType={}, fileName={}", fileId, fileType, fileName);
  2650. continue;
  2651. }
  2652. try {
  2653. String filePath = downloadPath + fileId + "." + fileType;
  2654. downloadDdFile(processInstanceId, fileId, filePath);
  2655. String hz = "qw/files/" + fileId + "." + fileType;
  2656. Map fileMap = new HashMap();
  2657. fileMap.put("url", url + hz);
  2658. fileMap.put("size", new File(filePath).length() / 1024f / 1024f);
  2659. fileMap.put("isPdf", "pdf".equalsIgnoreCase(fileType));
  2660. McR<Object> result = processMixedInvoice(fileMap);
  2661. Object responseData = result.getData();
  2662. // 解析发票数据
  2663. if (responseData != null) {
  2664. String jsonStr = JSONObject.toJSONString(responseData);
  2665. JSONObject jsonObject = JSONObject.parseObject(jsonStr);
  2666. JSONArray resultArray = jsonObject.getJSONArray("result");
  2667. if (resultArray != null && !resultArray.isEmpty()) {
  2668. JSONObject invoice = resultArray.getJSONObject(0);
  2669. String invoiceNumber = invoice.getString("serial") != null ? invoice.getString("serial") : "";
  2670. String buyerTaxId = invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "";
  2671. String buyerName = invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "";
  2672. // 保存解析的数据供后续使用
  2673. Map<String, Object> parsedData = new HashMap<>();
  2674. parsedData.put("invoice", invoice);
  2675. parsedData.put("fileId", fileId);
  2676. parsedData.put("fileName", fileName);
  2677. parsedData.put("fileType", fileType);
  2678. parsedData.put("responseData", responseData);
  2679. parsedInvoiceDataList.add(parsedData);
  2680. // ========== 校验1:抬头校验 ==========
  2681. if (StringUtils.isNotBlank(buyerName)) {
  2682. CompanyTitle existingTitle = companyTitleMapper.selectByTaxId(buyerName);
  2683. if (existingTitle == null) {
  2684. String errorInfo = String.format("发票: %s, 购买方名称: %s, 税号: %s",
  2685. fileName, buyerName, buyerTaxId);
  2686. invalidBuyerTaxIds.add(errorInfo);
  2687. log.warn("抬头校验失败: 税号={} 不存在于抬头库, 文件名={}", buyerTaxId, fileName);
  2688. }
  2689. } else {
  2690. String errorInfo = String.format("发票: %s, 购买方税号为空", fileName);
  2691. invalidBuyerTaxIds.add(errorInfo);
  2692. log.warn("抬头校验失败: 购买方税号为空, 文件名={}", fileName);
  2693. }
  2694. // ========== 校验2:发票号重复校验(与数据库中的发票号比较) ==========
  2695. if (StringUtils.isNotBlank(invoiceNumber)) {
  2696. // 检查数据库中是否已存在相同的发票号
  2697. LambdaQueryWrapper<InvoiceLibrary> queryWrapper = new LambdaQueryWrapper<>();
  2698. queryWrapper.eq(InvoiceLibrary::getInvoiceNumber, invoiceNumber);
  2699. InvoiceLibrary existingInvoice = baseMapper.selectOne(queryWrapper);
  2700. if (existingInvoice != null) {
  2701. String errorMsg = String.format("发票号重复: %s (当前文件: %s, 已存在于OA审批单: %s)",
  2702. invoiceNumber, fileName, existingInvoice.getOaId());
  2703. log.error(errorMsg);
  2704. // 终止流程并回写原因
  2705. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  2706. return McR.error("400", errorMsg);
  2707. }
  2708. // 同时检查同一审批单内是否有重复(防止同一单据内上传重复发票)
  2709. if (invoiceNumberList.contains(invoiceNumber)) {
  2710. String errorMsg = String.format("当前审批单内发票号重复: %s (当前文件: %s, 已存在文件: %s)",
  2711. invoiceNumber, fileName, invoiceNumberToFileName.get(invoiceNumber));
  2712. log.error(errorMsg);
  2713. // 终止流程并回写原因
  2714. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  2715. return McR.error("400", errorMsg);
  2716. }
  2717. invoiceNumberList.add(invoiceNumber);
  2718. invoiceNumberToFileName.put(invoiceNumber, fileName);
  2719. }
  2720. } else {
  2721. log.warn("发票解析结果为空: fileName={}", fileName);
  2722. }
  2723. } else {
  2724. log.warn("发票OCR识别结果为空: fileName={}", fileName);
  2725. }
  2726. } catch (Exception e) {
  2727. log.error("发票解析异常: fileId={}, fileName={}", fileId, fileName, e);
  2728. String errorMsg = "发票解析失败: " + e.getMessage();
  2729. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  2730. return McR.error("400", errorMsg);
  2731. }
  2732. }
  2733. } else {
  2734. log.warn("附件列表为空");
  2735. return McR.error("400", "请上传发票附件");
  2736. }
  2737. // ========== 抬头校验失败处理 ==========
  2738. if (!invalidBuyerTaxIds.isEmpty()) {
  2739. String errorMsg = "发票抬头有误,以下发票的购买方税号不在公司抬头库中:\n" + String.join("\n", invalidBuyerTaxIds);
  2740. log.error(errorMsg);
  2741. // 终止流程
  2742. terminateWorkflow(accessToken, processInstanceId, userId, errorMsg);
  2743. return McR.error("400", errorMsg);
  2744. }
  2745. BigDecimal totalAmount = BigDecimal.ZERO;
  2746. // ========== 第二遍遍历:保存发票数据(校验通过后) ==========
  2747. for (Map<String, Object> parsedData : parsedInvoiceDataList) {
  2748. JSONObject invoice = (JSONObject) parsedData.get("invoice");
  2749. Object responseData = parsedData.get("responseData");
  2750. allResults.add(responseData);
  2751. BigDecimal amount = invoice.getBigDecimal("amount");
  2752. if (amount != null) {
  2753. totalAmount = totalAmount.add(amount);
  2754. }
  2755. // 构建InvoiceLibrary对象
  2756. InvoiceLibrary invoiceLibrary = new InvoiceLibrary();
  2757. invoiceLibrary.setOaId(processInstanceId);
  2758. invoiceLibrary.setInvoiceCode(invoice.getString("code") != null ? invoice.getString("code") : "");
  2759. invoiceLibrary.setInvoiceNumber(invoice.getString("serial") != null ? invoice.getString("serial") : "");
  2760. invoiceLibrary.setInvoiceType(invoice.getString("kindName") != null ? invoice.getString("kindName") : "");
  2761. String date = invoice.getString("date");
  2762. if (StringUtils.isNotBlank(date)) {
  2763. try {
  2764. invoiceLibrary.setInvoiceDate(LocalDate.parse(date.trim()));
  2765. } catch (Exception e) {
  2766. log.warn("日期解析失败: {}", date, e);
  2767. }
  2768. }
  2769. invoiceLibrary.setAmount(invoice.getBigDecimal("excludingTax") != null ? invoice.getBigDecimal("excludingTax") : BigDecimal.ZERO);
  2770. invoiceLibrary.setTaxAmount(invoice.getBigDecimal("tax") != null ? invoice.getBigDecimal("tax") : BigDecimal.ZERO);
  2771. invoiceLibrary.setTotalAmount(invoice.getBigDecimal("amount") != null ? invoice.getBigDecimal("amount") : BigDecimal.ZERO);
  2772. invoiceLibrary.setBuyerName(invoice.getString("buyerName") != null ? invoice.getString("buyerName") : "");
  2773. invoiceLibrary.setBuyerTaxId(invoice.getString("buyerTaxId") != null ? invoice.getString("buyerTaxId") : "");
  2774. invoiceLibrary.setSellerName(invoice.getString("sellerName") != null ? invoice.getString("sellerName") : "");
  2775. invoiceLibrary.setSellerTaxId(invoice.getString("sellerTaxId") != null ? invoice.getString("sellerTaxId") : "");
  2776. invoiceLibrary.setOaStatus("0");
  2777. invoiceLibrary.setInvoiceStatus("0");
  2778. invoiceLibrary.setDep(bm != null ? bm : "");
  2779. invoiceLibrary.setAccountTitle(bxlb != null ? bxlb : "");
  2780. invoiceLibrary.setPayAmount(mainJe != null ? mainJe : BigDecimal.ZERO);
  2781. invoiceLibrary.setCreatedAt(LocalDateTime.now());
  2782. invoiceLibrary.setUpdatedAt(LocalDateTime.now());
  2783. invoiceLibrary.setFormName("批量付款申请(一对多)");
  2784. invoiceLibrary.setPaySubject(fkzhxx != null ? fkzhxx : "");
  2785. invoiceLibrary.setPayAccount(fkzh != null ? fkzh : "");
  2786. invoiceLibrary.setBankName(yhqc != null ? yhqc : "");
  2787. invoiceLibrary.setIsLongTerm(sfct != null ? sfct : "");
  2788. invoiceList.add(invoiceLibrary);
  2789. }
  2790. // 保存发票记录
  2791. if (!invoiceList.isEmpty()) {
  2792. for (InvoiceLibrary invoice : invoiceList) {
  2793. baseMapper.insert(invoice);
  2794. }
  2795. log.info("成功保存 {} 条发票记录", invoiceList.size());
  2796. } else {
  2797. log.warn("没有有效的发票数据可保存");
  2798. }
  2799. addWorkflowComment1(processInstanceId, userId, totalAmount);
  2800. return McR.success(allResults);
  2801. } catch (Exception e) {
  2802. log.error("解析子表数据失败", e);
  2803. return McR.error("300", "解析数据失败: " + e.getMessage());
  2804. }
  2805. }
  2806. }
  2807. }
  2808. return McR.success();
  2809. } catch (Exception e) {
  2810. log.error("处理审批实例失败", e);
  2811. return McR.error("500", "系统处理失败: " + e.getMessage());
  2812. }
  2813. }
  2814. @Override
  2815. public void updateSfctByOaId(Map map) {
  2816. try {
  2817. String oaId = UtilMap.getString(map, "oaId");
  2818. String sfct = UtilMap.getString(map, "sfct");
  2819. String fkzhxx = UtilMap.getString(map, "fkzhxx");
  2820. String fkzh = UtilMap.getString(map, "fkzh");
  2821. String yhqc = UtilMap.getString(map, "yhqc");
  2822. sfct = (sfct == null || sfct.trim().isEmpty()) ? "否" : sfct;
  2823. // 创建更新条件
  2824. LambdaUpdateWrapper<InvoiceLibrary> updateWrapper = new LambdaUpdateWrapper<>();
  2825. updateWrapper.eq(InvoiceLibrary::getOaId, oaId)
  2826. .set(InvoiceLibrary::getIsLongTerm, sfct)
  2827. .set(InvoiceLibrary::getPayAccount, fkzh)
  2828. .set(InvoiceLibrary::getBankName, yhqc)
  2829. .set(InvoiceLibrary::getPaySubject, fkzhxx)
  2830. .set(InvoiceLibrary::getUpdatedAt, LocalDateTime.now());
  2831. int updateCount = baseMapper.update(null, updateWrapper);
  2832. if (updateCount > 0) {
  2833. log.info("更新成功: 共更新{}条记录", updateCount);
  2834. } else {
  2835. log.warn("未找到符合条件的记录: oaId={}", oaId);
  2836. }
  2837. } catch (Exception e) {
  2838. throw new RuntimeException("更新OA状态失败", e);
  2839. }
  2840. }
  2841. /**
  2842. * 添加钉钉审批评论
  2843. */
  2844. private void addWorkflowComment(String processInstanceId, String userId, Object totalAmount) {
  2845. try {
  2846. Map<String, Object> body = new HashMap<>();
  2847. body.put("processInstanceId", processInstanceId);
  2848. body.put("text", totalAmount);
  2849. body.put("commentUserId", userId);
  2850. String result = UtilHttp.doPost(
  2851. "https://api.dingtalk.com/v1.0/workflow/processInstances/comments",
  2852. ddClient.initTokenHeader(),
  2853. null,
  2854. body
  2855. );
  2856. log.info("添加审批评论成功: {}", result);
  2857. } catch (Exception e) {
  2858. log.error("添加审批评论失败", e);
  2859. }
  2860. }
  2861. private void addWorkflowComment1(String processInstanceId, String userId, BigDecimal totalAmount) {
  2862. try {
  2863. Map<String, Object> body = new HashMap<>();
  2864. body.put("processInstanceId", processInstanceId);
  2865. body.put("text", "发票总金额:"+totalAmount);
  2866. body.put("commentUserId", userId);
  2867. String result = UtilHttp.doPost(
  2868. "https://api.dingtalk.com/v1.0/workflow/processInstances/comments",
  2869. ddClient.initTokenHeader(),
  2870. null,
  2871. body
  2872. );
  2873. log.info("添加审批评论成功: {}", result);
  2874. } catch (Exception e) {
  2875. log.error("添加审批评论失败", e);
  2876. }
  2877. }
  2878. /**
  2879. * 从 Map 中获取字符串值
  2880. */
  2881. private String getStringValue(Map<String, Object> map, String key) {
  2882. Object value = map.get(key);
  2883. return value != null ? String.valueOf(value) : "";
  2884. }
  2885. /**
  2886. * 从 Map 中获取 BigDecimal 值
  2887. */
  2888. private BigDecimal getBigDecimalValue(Map<String, Object> map, String key) {
  2889. Object value = map.get(key);
  2890. if (value == null) {
  2891. return BigDecimal.ZERO;
  2892. }
  2893. if (value instanceof BigDecimal) {
  2894. return (BigDecimal) value;
  2895. }
  2896. try {
  2897. return new BigDecimal(String.valueOf(value));
  2898. } catch (NumberFormatException e) {
  2899. return BigDecimal.ZERO;
  2900. }
  2901. }
  2902. /**
  2903. * 空字符串转换
  2904. */
  2905. private String nullToEmpty(String str) {
  2906. return str != null ? str : "";
  2907. }
  2908. private void downloadDdFile(String processInstanceId, String fileId, String downloadPath) {
  2909. try {
  2910. Map body = new HashMap();
  2911. body.put("processInstanceId", processInstanceId);
  2912. body.put("fileId", fileId);
  2913. 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);
  2914. //2、执行下载
  2915. Map result = (Map) ddrNew.getResult();
  2916. String downloadUri = UtilMap.getString(result, "downloadUri");
  2917. downloadFile(downloadUri, downloadPath);
  2918. } catch (Exception e) {
  2919. throw new RuntimeException(e);
  2920. }
  2921. }
  2922. //文件下载到本地
  2923. private void downloadFile(String downloadUri, String downloadPath) {
  2924. try {
  2925. URL url = new URL(downloadUri);
  2926. HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
  2927. int responseCode = httpConn.getResponseCode();
  2928. // 检查HTTP响应代码是否为200
  2929. if (responseCode == HttpURLConnection.HTTP_OK) {
  2930. InputStream inputStream = httpConn.getInputStream();
  2931. FileOutputStream outputStream = new FileOutputStream(downloadPath);
  2932. byte[] buffer = new byte[4096];
  2933. int bytesRead = -1;
  2934. while ((bytesRead = inputStream.read(buffer)) != -1) {
  2935. outputStream.write(buffer, 0, bytesRead);
  2936. }
  2937. outputStream.close();
  2938. inputStream.close();
  2939. } else {
  2940. System.out.println("无法下载文件。HTTP响应代码: " + responseCode);
  2941. }
  2942. httpConn.disconnect();
  2943. } catch (Exception e) {
  2944. throw new RuntimeException(e);
  2945. }
  2946. }
  2947. /**
  2948. * 私有化方法:混票识别处理
  2949. *
  2950. * @param data 包含url、size、isPdf的Map参数
  2951. * @return 识别结果
  2952. */
  2953. private McR<Object> processMixedInvoice(Map<String, Object> data) throws TencentCloudSDKException {
  2954. McException.assertParamException_Null(data, "url");
  2955. String image = String.valueOf(data.get("url"));
  2956. log.info("混票识别, 免登地址, {}", image);
  2957. // 非PDF, 且内存大于3M, 压缩后上传
  2958. if (UtilMap.getFloat(data, "size") > 3.0f && !UtilMap.getBoolean(data, "isPdf")) {
  2959. image = imageUrlConvertBase64(image);
  2960. }
  2961. if (UtilMap.getFloat(data, "size") > 6.0f && UtilMap.getBoolean(data, "isPdf")) {
  2962. image = pdfUrlConvertBase64(image);
  2963. }
  2964. List<Map> invoices = (List<Map>) txyInvoice.doRecognizeGeneralInvoice(image).get("MixedInvoiceItems");
  2965. System.out.println("invoices=====" + invoices);
  2966. List<String> nos = new ArrayList<>();
  2967. List<McInvoiceDto> result = invoices.stream().map(item -> {
  2968. Map prop = UtilMap.getMap(UtilMap.getMap(item, "SingleInvoiceInfos"), UtilMap.getString(item, "SubType"));
  2969. String taxRate = null;
  2970. // List<Map<String, Object>> vatInvoiceItemInfos = UtilMap.getList(prop, "VatInvoiceItemInfos");
  2971. // 优先使用 VatInvoiceItemInfos,如果为空则使用 VatElectronicItems
  2972. List<Map<String, Object>> vatInvoiceItemInfos =
  2973. UtilMap.getList(prop, "VatInvoiceItemInfos") != null && !UtilMap.getList(prop, "VatInvoiceItemInfos").isEmpty()
  2974. ? UtilMap.getList(prop, "VatInvoiceItemInfos")
  2975. : UtilMap.getList(prop, "VatElectronicItems");
  2976. // if (!vatInvoiceItemInfos.isEmpty()) {
  2977. // taxRate = UtilMap.getString(vatInvoiceItemInfos.get(0), "TaxRate");
  2978. // }
  2979. if (!vatInvoiceItemInfos.isEmpty()) {
  2980. String originalTaxRate = UtilMap.getString(vatInvoiceItemInfos.get(0), "TaxRate");
  2981. // 判断是否为有效数字(包括带%号的格式,如"9%")
  2982. if (StringUtils.isNotBlank(originalTaxRate)) {
  2983. // 移除百分号并尝试解析为数字
  2984. String cleanedTaxRate = originalTaxRate.replace("%", "").trim();
  2985. try {
  2986. // 尝试转换为数字
  2987. new BigDecimal(cleanedTaxRate);
  2988. taxRate = originalTaxRate; // 保持原值(可能带%)
  2989. } catch (NumberFormatException e) {
  2990. // 如果不是数字(如"不征税"、"免税"等),赋值为"0"
  2991. taxRate = "0";
  2992. log.debug("税率格式异常,原始值: {}, 已设置为0", originalTaxRate);
  2993. }
  2994. } else {
  2995. taxRate = "0";
  2996. }
  2997. } else {
  2998. taxRate = "0";
  2999. }
  3000. String no = UtilMap.getString(prop, "Number");
  3001. if (nos.contains(no)) {
  3002. return null;
  3003. }
  3004. nos.add(no);
  3005. String kind = UtilMap.getString(item, "TypeDescription");
  3006. String invoiceName = UtilMap.getString(item, "SubTypeDescription");
  3007. if (kind.equals("全电发票")) {
  3008. if (invoiceName.contains("铁路电子客票")) {
  3009. kind = "火车票";
  3010. } else if (invoiceName.contains("机票行程单")) {
  3011. kind = "机票行程单";
  3012. } else if (invoiceName.contains("专用发票")) {
  3013. kind = "全电专用发票";
  3014. } else {
  3015. kind = "全电普通发票";
  3016. }
  3017. }
  3018. if (kind.equals("增值税发票")) {
  3019. if (invoiceName.contains("区块链电子发票")) {
  3020. kind = "区块链电子发票";
  3021. } else if (invoiceName.contains("增值税专用发票")) {
  3022. kind = "增值税专用发票";
  3023. } else if (invoiceName.contains("增值税普通发票")) {
  3024. kind = "增值税普通发票";
  3025. } else if (invoiceName.contains("增值税电子专用发票")) {
  3026. kind = "增值税电子专用发票";
  3027. } else if (invoiceName.contains("增值税电子普通发票")) {
  3028. kind = "增值税电子普通发票";
  3029. } else {
  3030. kind = "增值税普通发票";
  3031. }
  3032. }
  3033. McInvoiceDto invoiceDto = McInvoiceDto.builder()
  3034. .name(UtilMap.getString(item, "SubTypeDescription"))
  3035. .kindName(kind)
  3036. .kind(UtilMap.getInt(item, "Type"))
  3037. .taxRate(taxRate)
  3038. .code(UtilMap.getString(prop, "Code"))
  3039. .serial(UtilMap.getString(prop, "Number"))
  3040. .date(UtilString.replaceDateZH_cn(UtilMap.getString(prop, "Date")))
  3041. .checkCode(UtilMap.getString(prop, "CheckCode"))
  3042. .amount(UtilNumber.setBigDecimal(UtilMap.getString_first(prop, "Total", "Fare")))
  3043. .tax(UtilNumber.setBigDecimal(UtilMap.getString_first(prop, "Tax")))
  3044. .excludingTax(UtilNumber.setBigDecimal(UtilMap.getString(prop, "PretaxAmount")))
  3045. .buyerName(StringUtils.defaultString(guyuanNameRepalce(UtilMap.getString(prop, "Buyer"))))
  3046. .buyerTaxId(StringUtils.isBlank(UtilMap.getString(prop, "BuyerTaxID"))
  3047. ? "" : UtilMap.getString(prop, "BuyerTaxID"))
  3048. .sellerName(guyuanNameRepalce(UtilMap.getString_first(prop, "Seller", "Issuer")))
  3049. .sellerTaxId(UtilMap.getString_first(prop, "SellerTaxID", "AgentCode"))
  3050. .passengerName(UtilMap.getString_first(prop, "Name", "UserName"))
  3051. .seatType(UtilMap.getString(prop, "Seat"))
  3052. .departureTime(UtilString.replaceDateZH_cn(UtilMap.getString(prop, "DateGetOn"))
  3053. + " " + UtilMap.getString(prop, "TimeGetOn"))
  3054. .departurePort(UtilMap.getString_first(prop, "StationGetOn", "Entrance", "Place"))
  3055. .arrivePort(UtilMap.getString_first(prop, "StationGetOff", "Exit"))
  3056. .trainNo(UtilMap.getString_first(prop, "TrainNumber", "LicensePlate"))
  3057. .insuranceCosts(UtilNumber.setBigDecimal(UtilMap.getString(prop, "Insurance")))
  3058. .fuelCosts(UtilNumber.setBigDecimal(UtilMap.getString(prop, "FuelSurcharge")))
  3059. .constructionCosts(UtilNumber.setBigDecimal(UtilMap.getString(prop, "AirDevelopmentFund")))
  3060. .build();
  3061. if ("机票行程单".equals(kind)) {
  3062. invoiceDto.setSellerName(UtilMap.getString_first(prop, "Issuer"));
  3063. invoiceDto.setSellerTaxId(UtilMap.getString_first(prop, "Seller"));
  3064. Map flight = (Map) UtilMap.getList(prop, "FlightItems").get(0);
  3065. invoiceDto.setDepartureTime(UtilString.replaceDateZH_cn(UtilMap.getString(item, "DateGetOn"))
  3066. + " " + UtilMap.getString(prop, "TimeGetOn"));
  3067. invoiceDto.setDeparturePort(UtilMap.getString(flight, "StationGetOn"));
  3068. invoiceDto.setArrivePort(UtilMap.getString(flight, "StationGetOff"));
  3069. invoiceDto.setSeatType(UtilMap.getString(flight, "Seat"));
  3070. }
  3071. if ("出租车发票".equals(item.get("TypeDescription"))) {
  3072. invoiceDto.setDepartureTime(UtilMap.getString(prop, "TimeGetOn")
  3073. + " ~ " + UtilMap.getString(prop, "TimeGetOff"));
  3074. }
  3075. return invoiceDto;
  3076. }).filter(Objects::nonNull).collect(Collectors.toList());
  3077. return McR.success(McInvoiceDto.formatResponse(result));
  3078. }
  3079. private static String guyuanNameRepalce(String name) {
  3080. name = name.replaceAll(" ", "");
  3081. if (name.contains("谷元")) {
  3082. return UtilString.replaceBracketIsWhole(name);
  3083. } else {
  3084. return UtilString.replaceBracketIsSemiangle(name);
  3085. }
  3086. }
  3087. /// url压缩转base64
  3088. @SneakyThrows
  3089. private String imageUrlConvertBase64(String imageUrl) {
  3090. ByteArrayOutputStream out = new ByteArrayOutputStream();
  3091. // scale(比例), outputQuality(质量)
  3092. Thumbnails.fromURLs(Arrays.asList(new URL(imageUrl))).scale(0.5f).outputQuality(0.25f).toOutputStream(out);
  3093. InputStream inputStream = new ByteArrayInputStream(out.toByteArray());
  3094. //转换为base64
  3095. byte[] bytes = IOUtils.toByteArray(inputStream);
  3096. return Base64.getEncoder().encodeToString(bytes);
  3097. }
  3098. @SneakyThrows
  3099. private String pdfUrlConvertBase64(String pdfUrl) {
  3100. String fileName = "tmp_" + new Date().getTime() + ".pdf";
  3101. // 下载文件
  3102. File file = UtilFile.mkdirIfNot(fileName, filePath.getPath().getTmp());
  3103. UtilHttp.doDownload(pdfUrl, file);
  3104. // PDF压缩
  3105. PdfDocument doc = new PdfDocument(); // 创建PdfDocument类的对象
  3106. doc.loadFromFile(file.getAbsolutePath()); // 加载PDF文档
  3107. doc.getFileInfo().setIncrementalUpdate(false); // 禁用增量更新
  3108. doc.setCompressionLevel(PdfCompressionLevel.Normal); // 将压缩级别设置为最佳
  3109. // 遍历文档页面
  3110. for (int i = 0; i < doc.getPages().getCount(); i++) {
  3111. PdfPageBase page = doc.getPages().get(i); // 获取指定页面
  3112. PdfImageInfo[] images = page.getImagesInfo(); // 获取每个页面的图像信息集合
  3113. // 遍历集合中的所有项目
  3114. if (images != null && images.length > 0)
  3115. for (int j = 0; j < images.length; j++) {
  3116. PdfImageInfo image = images[j]; // 获取指定图片
  3117. PdfBitmap bp = new PdfBitmap(image.getImage());
  3118. bp.setQuality(30); // 设置压缩质量
  3119. page.replaceImage(j, bp); // 将原始图像替换为压缩图像
  3120. }
  3121. }
  3122. // 将结果文档保存至另一个PDF文档中: 覆盖
  3123. doc.saveToFile(file.getAbsolutePath());
  3124. doc.close();
  3125. // PDF转base64, 无需透出本地文件地址
  3126. String base64 = UtilFile.fileToBase64(file.getAbsolutePath());
  3127. // 删除临时PDF文件
  3128. UtilFile.deleteFile(file.getAbsolutePath());
  3129. return base64;
  3130. }
  3131. @GetMapping("/files/{fileId}")
  3132. public ResponseEntity<Resource> getFileResource(
  3133. @PathVariable String fileId,
  3134. @RequestParam(defaultValue = "download") String option) throws IOException {
  3135. // 根据fileId获取实际文件路径,这里简化处理,实际可能需要从数据库查询
  3136. Path filePath = Paths.get("D://qiwang//files//").resolve(fileId).normalize();
  3137. // 检查文件是否存在
  3138. if (!Files.exists(filePath)) {
  3139. return ResponseEntity.notFound().build();
  3140. }
  3141. // 创建Resource对象
  3142. Resource resource = new UrlResource(filePath.toUri());
  3143. // 根据选项设置响应头
  3144. HttpHeaders headers = new HttpHeaders();
  3145. if ("preview".equalsIgnoreCase(option)) {
  3146. // 预览模式 - 尝试确定内容类型
  3147. String contentType = Files.probeContentType(filePath);
  3148. // 强制修正 PDF 的 Content-Type
  3149. if (filePath.toString().toLowerCase().endsWith(".pdf")) {
  3150. contentType = "application/pdf";
  3151. } else if (contentType == null) {
  3152. contentType = "application/octet-stream";
  3153. }
  3154. headers.setContentType(MediaType.parseMediaType(contentType));
  3155. headers.add("Content-Disposition", "inline; filename=\"" + resource.getFilename() + "\"");
  3156. headers.add("X-Content-Type-Options", "nosniff"); // 防止浏览器忽略 Content-Type
  3157. } else {
  3158. // 下载模式 - 默认处理
  3159. headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
  3160. headers.add("Content-Disposition", "attachment; filename=\"" + resource.getFilename() + "\"");
  3161. }
  3162. return ResponseEntity.ok()
  3163. .headers(headers)
  3164. .contentLength(resource.contentLength())
  3165. .body(resource);
  3166. }
  3167. }