SynBentransByBalance2Plugin.java 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. package kd.cosmic.jkjt.tmc.api;
  2. import com.alibaba.fastjson.JSONArray;
  3. import com.alibaba.fastjson.JSONObject;
  4. import kd.bos.coderule.api.CodeRuleInfo;
  5. import kd.bos.dataentity.OperateOption;
  6. import kd.bos.dataentity.entity.DynamicObject;
  7. import kd.bos.dataentity.entity.DynamicObjectCollection;
  8. import kd.bos.entity.operate.result.OperationResult;
  9. import kd.bos.logging.Log;
  10. import kd.bos.logging.LogFactory;
  11. import kd.bos.openapi.common.custom.annotation.ApiController;
  12. import kd.bos.openapi.common.custom.annotation.ApiPostMapping;
  13. import kd.bos.openapi.common.result.CustomApiResult;
  14. import kd.bos.orm.query.QCP;
  15. import kd.bos.orm.query.QFilter;
  16. import kd.bos.servicehelper.BusinessDataServiceHelper;
  17. import kd.bos.servicehelper.QueryServiceHelper;
  18. import kd.bos.servicehelper.coderule.CodeRuleServiceHelper;
  19. import kd.bos.servicehelper.operation.OperationServiceHelper;
  20. import kd.bos.util.StringUtils;
  21. import kd.cosmic.jkjt.tmc.util.CBSToolUtil;
  22. import kd.cosmic.jkjt.tmc.util.ParamsUtil;
  23. import kd.cosmic.jkjt.tmc.util.XmlUtils;
  24. import org.apache.commons.lang.time.DateFormatUtils;
  25. import java.io.Serializable;
  26. import java.math.BigDecimal;
  27. import java.sql.Timestamp;
  28. import java.text.ParseException;
  29. import java.text.SimpleDateFormat;
  30. import java.util.*;
  31. /**
  32. * @author wanghaiwu_kd
  33. * @date 2023/05/08
  34. * @description API插件,未开通银企直连的账户,根据CBS系统的账户余额,与星瀚中的余额比较,如果有差额,则自动生成交易明细。
  35. */
  36. @ApiController(
  37. desc = "未开通银企直连的账户根据CBS账户余额生产交易明细",
  38. value = "jkjt_cbsapi"
  39. )
  40. public class SynBentransByBalance2Plugin implements Serializable {
  41. private static final long serialVersionUID = -5560815141256121757L;
  42. private static Log logger = LogFactory.getLog(SynBentransByBalance2Plugin.class);
  43. //出纳初始化
  44. private static final String KEY_ENTITY_CASHINIT = "cas_cashmgtinit";
  45. //银行账户
  46. private static final String KEY_ENTITY_ACCOUNT = "am_accountbank";
  47. //离线明细引入实体编码
  48. private static final String KEY_ENTITY_BETRANSDETAIL = "bei_betransdetail_imp";
  49. @ApiPostMapping("SynBetransByBalance")
  50. public CustomApiResult<JSONObject> doCustomerService(){
  51. //先更新cbs开通状态
  52. List<Long> orgIds = new ArrayList<>();
  53. CBSToolUtil.synAccountOpenStatus(orgIds);
  54. String selectProperties = "bankaccountnumber, openorg, currency, bank, company";
  55. QFilter qFilter = new QFilter("nckd_issetcbs", QCP.equals, false);
  56. qFilter.and(new QFilter("acctstatus", QCP.equals, "normal"));
  57. // qFilter.and(new QFilter("bankaccountnumber", QCP.equals, "791909297510506"));
  58. DynamicObject[] acctList = BusinessDataServiceHelper.load(KEY_ENTITY_ACCOUNT, selectProperties, new QFilter[]{qFilter});
  59. List<DynamicObject> listObj = new ArrayList<>();
  60. int count = 0;
  61. for(DynamicObject acct : acctList){
  62. JSONObject lastBal = queryLastBal(acct);
  63. // if("500".equals(lastBal.getString("code"))){
  64. // continue;
  65. // }
  66. String accountNumber = acct.getString("bankaccountnumber");
  67. DynamicObject openOrg = acct.getDynamicObject("openorg");
  68. if(openOrg == null){
  69. continue;
  70. }
  71. Long orgId = openOrg.getLong("id");
  72. String cbsUrl = ParamsUtil.getCBSURLByMaster(orgId);
  73. if(StringUtils.isEmpty(cbsUrl)){
  74. logger.info("同步交易明细失败:组织【" + openOrg.getString("name") + "】(" + orgId.toString() + ")未配置相应的CBS地址");
  75. continue;
  76. }
  77. //账户实时余额查询ERCURBAL报文内容
  78. String body = getDetailBodyString(accountNumber);
  79. String paramBody = CBSToolUtil.getBodyString(body);
  80. //保存CBS调用日志
  81. String uuid = ParamsUtil.saveCBSLogData(cbsUrl, "detail", paramBody);
  82. String sTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
  83. logger.info("调用CBS接口ERCURBAL开始时间:" + sTime);
  84. String sr = CBSToolUtil.sendPost(cbsUrl, paramBody);
  85. String eTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
  86. logger.info("调用CBS接口ERCURBAL结束时间:" + eTime);
  87. //更新CBS调用日志
  88. ParamsUtil.updateCBSLogData(uuid, sr);
  89. String cbssr = CBSToolUtil.parseXMLcbs(sr);
  90. DynamicObject result = getBalanceDetailInfo(cbssr, acct, lastBal.getBigDecimal("bal"));
  91. if(result != null){
  92. listObj.add(result);
  93. }
  94. count++;
  95. //批量保存
  96. if(listObj.size() > 0 && count == 1000) {
  97. count = 0;
  98. OperationResult resultSave = OperationServiceHelper.executeOperate("save", KEY_ENTITY_BETRANSDETAIL, listObj.toArray(new DynamicObject[]{}), OperateOption.create());
  99. StringBuilder err = new StringBuilder();
  100. if (resultSave.getSuccessPkIds().size() <= 0) {
  101. for (int index = 0; index < resultSave.getAllErrorOrValidateInfo().size(); index++) {
  102. String message = resultSave.getAllErrorOrValidateInfo().get(index).getMessage();
  103. err.append("/").append(message);
  104. }
  105. }
  106. listObj.clear();
  107. }
  108. }
  109. //批量保存
  110. if(listObj.size() > 0) {
  111. OperationResult resultSave = OperationServiceHelper.executeOperate("save", KEY_ENTITY_BETRANSDETAIL, listObj.toArray(new DynamicObject[]{}), OperateOption.create());
  112. StringBuilder err = new StringBuilder();
  113. if (resultSave.getSuccessPkIds().size() <= 0) {
  114. for (int index = 0; index < resultSave.getAllErrorOrValidateInfo().size(); index++) {
  115. String message = resultSave.getAllErrorOrValidateInfo().get(index).getMessage();
  116. err.append("/").append(message);
  117. }
  118. }
  119. }
  120. //自动生成余额
  121. // try {
  122. // ParamsUtil.createBalance();
  123. // } catch (ParseException e) {
  124. // throw new RuntimeException(e);
  125. // }
  126. return returnResult("200", "同步成功!");
  127. }
  128. /**
  129. * 查询账号在星瀚的当前余额
  130. * @param acct
  131. * @return
  132. */
  133. private JSONObject queryLastBal(DynamicObject acct){
  134. String code = "200";
  135. BigDecimal bal = BigDecimal.ZERO;
  136. //查询是否存在交易明细
  137. SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
  138. //交易时间
  139. Date time = new Date();
  140. //交易日期
  141. Date date = null;
  142. try {
  143. date = sdf1.parse(sdf1.format(time));
  144. } catch (ParseException e) {
  145. throw new RuntimeException(e);
  146. }
  147. QFilter qFilter = new QFilter("accountbank.id", QCP.equals, acct.getLong("id"));
  148. qFilter.and(new QFilter("bizdate", QCP.less_than, date));
  149. String selectProperties = "transbalance";
  150. String orderBy = "biztime desc";
  151. DynamicObject[] detailList = BusinessDataServiceHelper.load(KEY_ENTITY_BETRANSDETAIL, selectProperties, qFilter.toArray(), orderBy);
  152. //如果没有交易明细,则查找是否有做出纳初始化初始余额
  153. if(detailList.length == 0){
  154. qFilter = new QFilter("entrybank.bank_accountbank", QCP.equals, acct.getLong("id"));
  155. selectProperties = "entrybank.bank_journalbalance";
  156. orderBy = "startperiod.number desc";
  157. DynamicObjectCollection objCols = QueryServiceHelper.query(KEY_ENTITY_CASHINIT, selectProperties, qFilter.toArray(), orderBy);
  158. if(objCols.size() > 0){
  159. code = "200";
  160. bal = objCols.get(0).getBigDecimal("entrybank.bank_journalbalance");
  161. }
  162. } else {
  163. code = "200";
  164. bal = detailList[0].getBigDecimal("transbalance");
  165. }
  166. JSONObject reslutData = new JSONObject();
  167. reslutData.put("code", code);
  168. reslutData.put("bal", bal);
  169. return reslutData;
  170. }
  171. /**
  172. * 交易明细参数body
  173. * @return
  174. */
  175. private String getDetailBodyString(String accountNumber){
  176. StringBuffer body = new StringBuffer();
  177. body.append("<?xml version='1.0' encoding='GB2312'?>\r\n");
  178. body.append("<CBSERPPGK>\r\n");
  179. body.append("<INFO>\r\n");
  180. body.append("<FUNNAM>ERCURBAL</FUNNAM>\r\n");
  181. body.append("</INFO>\r\n");
  182. body.append("<ERCURBALX>\r\n");
  183. body.append("<ACTNBR>").append(accountNumber).append("</ACTNBR>\r\n");
  184. body.append("</ERCURBALX>\r\n");
  185. body.append("</CBSERPPGK>\r\n");
  186. return body.toString();
  187. }
  188. /**
  189. * 处理cbs返回结果
  190. * @param cbssr
  191. * @return
  192. */
  193. private DynamicObject getBalanceDetailInfo(String cbssr, DynamicObject acct, BigDecimal lastBal){
  194. JSONObject cbsObj = XmlUtils.documentToJSONObject(cbssr);
  195. if(cbsObj != null && cbsObj.get("ACACTINFY") != null){
  196. JSONArray array = (JSONArray)cbsObj.get("ACACTINFY");
  197. JSONObject account = array.getJSONObject(0);
  198. List<DynamicObject> listObj = new ArrayList<>();
  199. DynamicObject detailInfo = null;
  200. try {
  201. detailInfo = parseBetransDetail(account, acct, lastBal);
  202. } catch (ParseException e) {
  203. throw new RuntimeException(e);
  204. }
  205. return detailInfo;
  206. }
  207. return null;
  208. }
  209. /**
  210. * 创建离线明细引入对象
  211. * @param cbsObj
  212. * @return
  213. */
  214. private DynamicObject parseBetransDetail(JSONObject cbsObj, DynamicObject acct, BigDecimal lastBal) throws ParseException {
  215. //账户余额
  216. BigDecimal actBal = cbsObj.getBigDecimal("ACTBAL");
  217. if(actBal == null){
  218. actBal = BigDecimal.ZERO;
  219. }
  220. //处理日期类字段
  221. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  222. SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
  223. //交易时间
  224. Date time = new Date();
  225. //交易日期
  226. Date date = sdf1.parse(sdf1.format(time));
  227. //如果当天存在cbs交易明细,则不增加交易明细
  228. Boolean isExistDetail = ParamsUtil.isExistsDetail(acct, date, new String[]{"curday", "history"});
  229. if(isExistDetail){
  230. return null;
  231. }
  232. DynamicObject objectInfo = ParamsUtil.getDetailInfo(acct, date, "balancecompare");
  233. if(objectInfo == null){
  234. objectInfo = BusinessDataServiceHelper.newDynamicObject(KEY_ENTITY_BETRANSDETAIL);
  235. }
  236. objectInfo.set("nckd_generatemethod", "balancecompare");
  237. //借贷方向:1:借方,2:贷方
  238. String direct = "";
  239. BigDecimal dayActAmt = actBal.subtract(lastBal).abs();
  240. if(lastBal.compareTo(actBal) > 0){
  241. direct = "1";
  242. } else if(lastBal.compareTo(actBal) < 0){
  243. direct = "2";
  244. } else {
  245. return null;
  246. }
  247. String detailid = UUID.randomUUID().toString().replace("-", "");
  248. detailid = (detailid == null) ? ("uuid" + String.valueOf(System.currentTimeMillis())) : detailid;
  249. objectInfo.set("oppunit", null);//对方用户名
  250. objectInfo.set("oppbanknumber", null);//对方账号
  251. objectInfo.set("oppbank", null);//对方开户行
  252. objectInfo.set("detailid", detailid);//明细流水号
  253. objectInfo.set("bizrefno", detailid);//业务参考号
  254. objectInfo.set("uniqueseq", detailid + "01");//银行主键
  255. objectInfo.set("description", null);//摘要
  256. //判断此明细流水号是否已存在,已存在的记录不接收
  257. QFilter filterBizRefNo = new QFilter("detailid", QCP.equals, objectInfo.getString("detailid"));
  258. QFilter filterDataSource = new QFilter("datasource", QCP.equals, "import");
  259. QFilter[] filters = new QFilter[]{filterBizRefNo, filterDataSource};
  260. DynamicObject betransDetailInfo = BusinessDataServiceHelper.loadSingle(KEY_ENTITY_BETRANSDETAIL, "id, billno", filters);
  261. if(betransDetailInfo != null){
  262. return null;
  263. }
  264. //交易时间
  265. objectInfo.set("bizTime", new Timestamp(time.getTime()));
  266. //交易日期
  267. objectInfo.set("bizdate", new Timestamp(date.getTime()));
  268. //入账状态
  269. if("2".equals(direct)){
  270. objectInfo.set("receredtype", "0");
  271. }
  272. //发生额
  273. objectInfo.set("debitamount", BigDecimal.ZERO);
  274. objectInfo.set("creditamount", BigDecimal.ZERO);
  275. String amountField = ParamsUtil.getAmountFieldName(direct);
  276. objectInfo.set(amountField, dayActAmt);
  277. //手续费
  278. // objectInfo.set("transfercharge", BigDecimal.ZERO);
  279. //余额
  280. objectInfo.set("transbalance", actBal);
  281. //单据状态
  282. objectInfo.set("billstatus", "A");
  283. //数据来源
  284. objectInfo.set("datasource", "import");
  285. //业务类型
  286. objectInfo.set("biztype", "1");
  287. //币别
  288. DynamicObjectCollection collection = acct.getDynamicObjectCollection("currency");
  289. Long currencyId = 0L;
  290. for (DynamicObject item : collection) {
  291. //引用该基础资料的单据id
  292. long pkid = item.getLong("pkid");
  293. // 获取该基础资料的动态对象
  294. DynamicObject fbasedataid = item.getDynamicObject("fbasedataid");
  295. //获取该基础资料的id
  296. currencyId = item.getLong("fbasedataid_id");
  297. }
  298. DynamicObject currency = BusinessDataServiceHelper.loadSingleFromCache(currencyId, "bd_currency");
  299. objectInfo.set("currency", currency);
  300. //银行账号
  301. objectInfo.set("accountbank", acct);
  302. objectInfo.set("bank", acct.get("bank"));
  303. objectInfo.set("company", acct.get("company"));
  304. CodeRuleInfo codeRule = CodeRuleServiceHelper.getCodeRule(objectInfo.getDataEntityType().getName(), objectInfo, null);
  305. String billno = CodeRuleServiceHelper.getNumber(codeRule, objectInfo);
  306. objectInfo.set("billno", billno);
  307. return objectInfo;
  308. }
  309. /**
  310. * 自定义返回data对象
  311. * @param code
  312. * @param message
  313. * @return
  314. */
  315. public CustomApiResult<JSONObject> returnResult(String code, String message){
  316. JSONObject reslutData = new JSONObject();
  317. reslutData.put("code", code);
  318. reslutData.put("message", message);
  319. CustomApiResult<JSONObject> result = CustomApiResult.success(reslutData);
  320. return result;
  321. }
  322. }