ApprovalWriteBackService.java 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. package com.malk.service.workhours;
  2. import com.alibaba.fastjson.JSON;
  3. import com.google.common.util.concurrent.RateLimiter;
  4. import com.malk.server.aliwork.YDConf;
  5. import com.malk.server.aliwork.YDParam;
  6. import com.malk.server.common.McException;
  7. import com.malk.server.workhours.ApprovalWriteBackResult;
  8. import com.malk.server.workhours.WHConf;
  9. import com.malk.service.aliwork.YDClient;
  10. import com.malk.service.aliwork.YDService;
  11. import com.malk.utils.UtilMap;
  12. import lombok.extern.slf4j.Slf4j;
  13. import org.apache.commons.lang3.StringUtils;
  14. import org.springframework.beans.factory.annotation.Autowired;
  15. import org.springframework.stereotype.Service;
  16. import java.util.*;
  17. import java.util.concurrent.*;
  18. import java.util.concurrent.atomic.AtomicInteger;
  19. /**
  20. * 审批结果回写工时汇总表
  21. * <p>
  22. * 审批结束或撤销后调用,入参为流程实例ID + 审批结果(0=拒绝/撤销,1=同意)。
  23. * 工时审批含两个明细子表(项目/非项目工时),其他工时审批含一个明细子表。
  24. * 明细按「填报人 + 工时日期(天) + 项目编号/Activity」1:1 对应工时汇总表(人+天一条)里的子表行:
  25. * - 拒绝 → 对应子表行「已提交工时」置 0
  26. * - 同意 → 对应子表行「已审批工时」= 该明细工时
  27. * 主表合计按子表行重算(幂等,撤销/重发不翻倍)。
  28. * <p>
  29. * 设计文档:后端/阿科德斯/审批结果回写工时汇总表.md
  30. */
  31. @Slf4j
  32. @Service
  33. public class ApprovalWriteBackService {
  34. @Autowired
  35. private YDClient ydClient;
  36. @Autowired
  37. private YDService ydService;
  38. @Autowired
  39. private WHConf whConf;
  40. private static final int THREAD_POOL_SIZE = 10;
  41. private static final int MAX_RETRY = 2;
  42. /**
  43. * 明细审批意见「同意」取值(单选,已确认)
  44. */
  45. private static final String OPINION_APPROVE = "同意";
  46. // ===== 工时汇总表:记录定位键 + 主表合计字段 =====
  47. private static final String SUMMARY_SUBMITTER_ID = "textField_mmbffvd9"; // 填报人ID(文本)
  48. private static final String SUMMARY_DAY_TEXT = "textField_mmbffvdb"; // 工时日期(文本) yyyyMMdd
  49. private static final String M_DAY_SUBMITTED = "numberField_mmd1smem"; // 已提交工时(日)
  50. private static final String M_DAY_APPROVED = "numberField_mmeakgi4"; // 已审批工时(日)
  51. private static final String M_BIL_SUBMITTED = "numberField_mmacxewa"; // Billable已提交
  52. private static final String M_BIL_APPROVED = "numberField_mmacxewg"; // Billable已审批
  53. private static final String M_NON_SUBMITTED = "numberField_mmad6jbz"; // Non Billable已提交
  54. private static final String M_NON_APPROVED = "numberField_mmad6jc1"; // Non Billable已审批
  55. private static final String M_OTH_SUBMITTED = "numberField_mmd1smeq"; // 已提交(其他)
  56. private static final String M_OTH_APPROVED = "numberField_mmd1smes"; // 已审批(其他)
  57. // ===== 工时汇总表:三个子表 =====
  58. private static final String SUB_BIL_TABLE = "tableField_mmczo634";
  59. private static final String SUB_BIL_KEY = "textField_mmacxew3"; // 项目编号
  60. private static final String SUB_BIL_SUBMITTED = "numberField_mmczo636"; // 已提交工时
  61. private static final String SUB_BIL_APPROVED = "numberField_mmczo637"; // 已审批工时
  62. private static final String SUB_NON_TABLE = "tableField_mmczo63h";
  63. private static final String SUB_NON_KEY = "textField_mmczo639"; // 项目编号
  64. private static final String SUB_NON_SUBMITTED = "numberField_mmczo63c";
  65. private static final String SUB_NON_APPROVED = "numberField_mmczo63d";
  66. private static final String SUB_OTH_TABLE = "tableField_mmeakgid";
  67. private static final String SUB_OTH_KEY = "selectField_mmeakgie"; // Activity
  68. private static final String SUB_OTH_SUBMITTED = "numberField_mmeakgij";
  69. private static final String SUB_OTH_APPROVED = "numberField_mmeakgii";
  70. // ===== 审批单明细子表字段(按类别)=====
  71. // 工时审批·项目工时明细(Billable)
  72. private static final String AP_BIL_TABLE = "tableField_mmae8t99";
  73. private static final String AP_BIL_USERID = "textField_mmd1ozk4";
  74. private static final String AP_BIL_KEY = "textField_mmacxew3"; // 项目编号
  75. private static final String AP_BIL_DAYTEXT = "textField_mmd8g654";
  76. private static final String AP_BIL_HOURS = "numberField_mmacxew9";
  77. private static final String AP_BIL_OPINION = "radioField_mpwhk294";
  78. // 工时审批·非项目工时明细(Non Billable)
  79. private static final String AP_NON_TABLE = "tableField_mmd1wu9h";
  80. private static final String AP_NON_USERID = "textField_mmd1wu9f";
  81. private static final String AP_NON_KEY = "textField_mmd1wu9d"; // 项目编号
  82. private static final String AP_NON_DAYTEXT = "textField_mmd8g655";
  83. private static final String AP_NON_HOURS = "numberField_mmd1wu9e";
  84. private static final String AP_NON_OPINION = "radioField_mpwhk295";
  85. // 其他工时审批·明细(与非项目同子表ID,但语义为 Activity)
  86. private static final String AP_OTH_TABLE = "tableField_mmd1wu9h";
  87. private static final String AP_OTH_USERID = "textField_mmd1wu9f";
  88. private static final String AP_OTH_KEY = "selectField_mmeakgie"; // Activity
  89. private static final String AP_OTH_DAYTEXT = "textField_mmd8g655";
  90. private static final String AP_OTH_HOURS = "numberField_mmd1wu9e";
  91. private static final String AP_OTH_OPINION = "radioField_mpwhk294";
  92. private enum Cat {BILLABLE, NON_BILLABLE, OTHER}
  93. /**
  94. * 单条审批明细(归一化)
  95. */
  96. private static class DetailRow {
  97. Cat cat;
  98. String userId;
  99. String dayText;
  100. String key; // 项目编号 / Activity
  101. double hours;
  102. boolean approve; // 有效意见:true=同意, false=拒绝
  103. }
  104. /**
  105. * 审批结果回写工时汇总表
  106. *
  107. * @param formInstanceId 流程实例ID
  108. * @param result 审批结果:0=拒绝/撤销,1=同意
  109. * @return 回写结果统计
  110. */
  111. public ApprovalWriteBackResult writeBack(String formInstanceId, int result) {
  112. McException.assertAccessException(StringUtils.isBlank(formInstanceId), "实例ID不能为空");
  113. // 1. 按实例ID查审批单表单详情(表单接口 retrieve_id),含主表 + 内联子表(子表内联上限 50)
  114. Map approvalFormData = ydClient.queryData(YDParam.builder()
  115. .appType(whConf.getYidaAppType())
  116. .systemToken(whConf.getYidaSystemToken())
  117. .formInstanceId(formInstanceId)
  118. .build(), YDConf.FORM_QUERY.retrieve_id).getFormData();
  119. log.info("[审批回写] 审批单详情 formInstanceId={} keys={}", formInstanceId,
  120. approvalFormData == null ? null : approvalFormData.keySet());
  121. McException.assertAccessException(approvalFormData == null || approvalFormData.isEmpty(),
  122. "未查询到审批单数据,实例ID=" + formInstanceId);
  123. // 2. 类型判定:工时审批含「项目工时明细」子表 tableField_mmae8t99,其他工时审批没有
  124. boolean isOther = !approvalFormData.containsKey(AP_BIL_TABLE);
  125. boolean approveWhole = (result == 1);
  126. // 3. 取审批单明细(内联子表 <50 直接用,==50 才 queryDetails 递归取全,避免无效请求)
  127. List<DetailRow> rows = new ArrayList<>();
  128. if (isOther) {
  129. rows.addAll(loadDetailRows(formInstanceId, approvalFormData, Cat.OTHER, approveWhole));
  130. } else {
  131. rows.addAll(loadDetailRows(formInstanceId, approvalFormData, Cat.BILLABLE, approveWhole));
  132. rows.addAll(loadDetailRows(formInstanceId, approvalFormData, Cat.NON_BILLABLE, approveWhole));
  133. }
  134. // 4. 按汇总表记录键(填报人 + 工时日期文本)分组
  135. Map<String, List<DetailRow>> groups = new LinkedHashMap<>();
  136. for (DetailRow r : rows) {
  137. if (StringUtils.isBlank(r.userId) || StringUtils.isBlank(r.dayText)) {
  138. continue;
  139. }
  140. groups.computeIfAbsent(r.userId + "" + r.dayText, k -> new ArrayList<>()).add(r);
  141. }
  142. // 5. 并发逐「日记录」处理
  143. AtomicInteger hitRecords = new AtomicInteger(0);
  144. AtomicInteger updatedRows = new AtomicInteger(0);
  145. AtomicInteger missRows = new AtomicInteger(0);
  146. AtomicInteger failRecords = new AtomicInteger(0);
  147. RateLimiter limiter = RateLimiter.create(20.0);
  148. ExecutorService executor = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
  149. try {
  150. List<Future<?>> futures = new ArrayList<>();
  151. for (Map.Entry<String, List<DetailRow>> e : groups.entrySet()) {
  152. List<DetailRow> groupRows = e.getValue();
  153. String userId = groupRows.get(0).userId;
  154. String dayText = groupRows.get(0).dayText;
  155. futures.add(executor.submit(() -> {
  156. for (int retry = 0; retry <= MAX_RETRY; retry++) {
  157. try {
  158. int[] r = applyToRecord(userId, dayText, groupRows, limiter);
  159. if (r == null) {
  160. // 未命中汇总表记录
  161. break;
  162. }
  163. hitRecords.incrementAndGet();
  164. updatedRows.addAndGet(r[0]);
  165. missRows.addAndGet(r[1]);
  166. return;
  167. } catch (Exception ex) {
  168. if (retry < MAX_RETRY) {
  169. sleep(1000L * (retry + 1));
  170. } else {
  171. failRecords.incrementAndGet();
  172. log.error("[审批回写] 记录处理失败 userId={} day={}", userId, dayText, ex);
  173. }
  174. }
  175. }
  176. }));
  177. }
  178. for (Future<?> f : futures) {
  179. try {
  180. f.get();
  181. } catch (Exception ex) {
  182. log.error("[审批回写] 线程执行异常", ex);
  183. }
  184. }
  185. } finally {
  186. executor.shutdown();
  187. }
  188. ApprovalWriteBackResult stats = ApprovalWriteBackResult.builder()
  189. .formType(isOther ? "其他工时审批" : "工时审批")
  190. .formInstanceId(formInstanceId)
  191. .result(result)
  192. .detailRows(rows.size())
  193. .groups(groups.size())
  194. .hitRecords(hitRecords.get())
  195. .updatedRows(updatedRows.get())
  196. .missRows(missRows.get())
  197. .failRecords(failRecords.get())
  198. .build();
  199. log.info("[审批回写] 完成 {}", stats);
  200. return stats;
  201. }
  202. /**
  203. * 处理单条汇总表日记录
  204. *
  205. * @return {updatedRows, missRows};汇总表无此记录返回 null
  206. */
  207. private int[] applyToRecord(String userId, String dayText, List<DetailRow> groupRows, RateLimiter limiter) {
  208. // a. 定位汇总表记录(填报人ID + 工时日期文本,代码内精确过滤)
  209. limiter.acquire();
  210. List<Map> found = (List<Map>) ydClient.queryData(YDParam.builder()
  211. .appType(whConf.getYidaAppType())
  212. .systemToken(whConf.getYidaSystemToken())
  213. .formUuid(whConf.getFormUuidWorkHoursSummary())
  214. .searchFieldJson(JSON.toJSONString(UtilMap.map(
  215. SUMMARY_SUBMITTER_ID + ", " + SUMMARY_DAY_TEXT, userId, dayText)))
  216. .build(), YDConf.FORM_QUERY.retrieve_search_form).getData();
  217. String summaryInstId = null;
  218. for (Map item : found) {
  219. Map fd = (Map) item.get("formData");
  220. if (fd == null) continue;
  221. if (userId.equals(String.valueOf(fd.get(SUMMARY_SUBMITTER_ID)))
  222. && dayText.equals(String.valueOf(fd.get(SUMMARY_DAY_TEXT)))) {
  223. summaryInstId = String.valueOf(item.get("formInstanceId"));
  224. break;
  225. }
  226. }
  227. if (StringUtils.isBlank(summaryInstId)) {
  228. log.warn("[审批回写] 未命中汇总表记录 userId={} day={}", userId, dayText);
  229. return null;
  230. }
  231. // b. 取汇总表记录完整数据(含三子表)
  232. limiter.acquire();
  233. Map formData = ydClient.queryData(YDParam.builder()
  234. .appType(whConf.getYidaAppType())
  235. .systemToken(whConf.getYidaSystemToken())
  236. .formInstanceId(summaryInstId)
  237. .build(), YDConf.FORM_QUERY.retrieve_id).getFormData();
  238. Map<String, Object> updateData = new HashMap<>();
  239. int updated = 0;
  240. int miss = 0;
  241. // c. 按类别分组本记录的明细,逐类别重建子表 + 置值
  242. Map<Cat, List<DetailRow>> byCat = new EnumMap<>(Cat.class);
  243. for (DetailRow r : groupRows) {
  244. byCat.computeIfAbsent(r.cat, k -> new ArrayList<>()).add(r);
  245. }
  246. // 各类别已提交/已审批合计(受影响类别从重建行重算,未受影响沿用主表现值)
  247. double bilSubmitted = num(formData, M_BIL_SUBMITTED), bilApproved = num(formData, M_BIL_APPROVED);
  248. double nonSubmitted = num(formData, M_NON_SUBMITTED), nonApproved = num(formData, M_NON_APPROVED);
  249. double othSubmitted = num(formData, M_OTH_SUBMITTED), othApproved = num(formData, M_OTH_APPROVED);
  250. for (Map.Entry<Cat, List<DetailRow>> e : byCat.entrySet()) {
  251. Cat cat = e.getKey();
  252. String subTable = subTable(cat);
  253. String keyField = subKey(cat);
  254. String submittedField = subSubmitted(cat);
  255. String approvedField = subApproved(cat);
  256. // 子表取数控制:内联 <50 直接用,==50 才递归取全,避免全量覆盖时漏行被删
  257. List<Map> rebuilt = rebuildRows(
  258. resolveDetailRows(summaryInstId, subTable, (List<Map>) formData.get(subTable)));
  259. for (DetailRow dr : e.getValue()) {
  260. Map target = null;
  261. for (Map row : rebuilt) {
  262. if (dr.key != null && dr.key.equals(String.valueOf(row.get(keyField)))) {
  263. target = row;
  264. break;
  265. }
  266. }
  267. if (target == null) {
  268. miss++;
  269. log.warn("[审批回写] 子表行未匹配 cat={} key={} userId={} day={}", cat, dr.key, userId, dayText);
  270. continue;
  271. }
  272. if (dr.approve) {
  273. target.put(approvedField, dr.hours);
  274. } else {
  275. target.put(submittedField, 0);
  276. }
  277. updated++;
  278. }
  279. updateData.put(subTable, rebuilt);
  280. double sumSubmitted = sumRows(rebuilt, submittedField);
  281. double sumApproved = sumRows(rebuilt, approvedField);
  282. switch (cat) {
  283. case BILLABLE:
  284. bilSubmitted = sumSubmitted;
  285. bilApproved = sumApproved;
  286. break;
  287. case NON_BILLABLE:
  288. nonSubmitted = sumSubmitted;
  289. nonApproved = sumApproved;
  290. break;
  291. case OTHER:
  292. othSubmitted = sumSubmitted;
  293. othApproved = sumApproved;
  294. break;
  295. }
  296. }
  297. // d. 主表合计重算
  298. updateData.put(M_BIL_SUBMITTED, bilSubmitted);
  299. updateData.put(M_BIL_APPROVED, bilApproved);
  300. updateData.put(M_NON_SUBMITTED, nonSubmitted);
  301. updateData.put(M_NON_APPROVED, nonApproved);
  302. updateData.put(M_OTH_SUBMITTED, othSubmitted);
  303. updateData.put(M_OTH_APPROVED, othApproved);
  304. updateData.put(M_DAY_SUBMITTED, bilSubmitted + nonSubmitted + othSubmitted);
  305. updateData.put(M_DAY_APPROVED, bilApproved + nonApproved + othApproved);
  306. // e. 全量覆盖更新(子表整组替换 + 主表合计)
  307. limiter.acquire();
  308. ydClient.operateData(YDParam.builder()
  309. .appType(whConf.getYidaAppType())
  310. .systemToken(whConf.getYidaSystemToken())
  311. .formInstanceId(summaryInstId)
  312. .updateFormDataJson(JSON.toJSONString(updateData))
  313. .ignoreEmpty(false)
  314. .useLatestVersion(true)
  315. .build(), YDConf.FORM_OPERATION.update);
  316. return new int[]{updated, miss};
  317. }
  318. /**
  319. * 加载审批单某类别明细(内联子表优先,必要时递归取全)
  320. */
  321. private List<DetailRow> loadDetailRows(String formInstanceId, Map approvalFormData, Cat cat, boolean approveWhole) {
  322. String tableId = apTable(cat);
  323. List<Map> raw = resolveDetailRows(formInstanceId, tableId, (List<Map>) approvalFormData.get(tableId));
  324. List<DetailRow> list = new ArrayList<>();
  325. for (Map row : raw) {
  326. DetailRow dr = new DetailRow();
  327. dr.cat = cat;
  328. dr.userId = str(row, apUserId(cat));
  329. dr.dayText = str(row, apDayText(cat));
  330. dr.key = str(row, apKey(cat));
  331. dr.hours = num(row, apHours(cat));
  332. String opinion = str(row, apOpinion(cat));
  333. // 有效意见:整单拒绝/撤销 → 全拒绝;整单同意 → 看单条明细意见
  334. dr.approve = approveWhole && OPINION_APPROVE.equals(opinion);
  335. list.add(dr);
  336. }
  337. return list;
  338. }
  339. /**
  340. * 子表取数控制:主表详情已内联返回子表时,行数 &lt;50 视为完整直接用;
  341. * ==50(可能被宜搭截断)或内联缺失,才调 {@link YDService#queryDetails} 递归取全,避免无效请求。
  342. * 复用 ydService 封装,不重复造分页轮子。
  343. */
  344. private List<Map> resolveDetailRows(String formInstanceId, String tableId, List<Map> inlineRows) {
  345. if (inlineRows != null && inlineRows.size() < 50) {
  346. return inlineRows;
  347. }
  348. return ydService.queryDetails(YDParam.builder()
  349. .appType(whConf.getYidaAppType())
  350. .systemToken(whConf.getYidaSystemToken())
  351. .formInstanceId(formInstanceId)
  352. .tableFieldId(tableId)
  353. .pageNumber(1)
  354. .build());
  355. }
  356. /**
  357. * 重建子表行:保留全部字段,成员/关联字段用 _id 取真实ID(全量覆盖写回前置)
  358. */
  359. private List<Map> rebuildRows(List<Map> rows) {
  360. List<Map> out = new ArrayList<>();
  361. if (rows == null) {
  362. return out;
  363. }
  364. for (Map row : rows) {
  365. Map newRow = new HashMap();
  366. for (Object keyObj : row.keySet()) {
  367. String key = String.valueOf(keyObj);
  368. if (key.endsWith("_value") || (key.startsWith("employeeField_") && !key.endsWith("_id"))) {
  369. continue;
  370. }
  371. String k = key;
  372. if (key.endsWith("_id")) {
  373. if (key.startsWith("employeeField_") || key.startsWith("associationFormField_")) {
  374. k = key.substring(0, key.length() - 3);
  375. } else {
  376. continue;
  377. }
  378. }
  379. newRow.put(k, YDConf.getDataByCompId(row, k));
  380. }
  381. out.add(newRow);
  382. }
  383. return out;
  384. }
  385. // ===== 字段映射辅助 =====
  386. private String apTable(Cat c) {
  387. return c == Cat.BILLABLE ? AP_BIL_TABLE : (c == Cat.NON_BILLABLE ? AP_NON_TABLE : AP_OTH_TABLE);
  388. }
  389. private String apUserId(Cat c) {
  390. return c == Cat.BILLABLE ? AP_BIL_USERID : (c == Cat.NON_BILLABLE ? AP_NON_USERID : AP_OTH_USERID);
  391. }
  392. private String apDayText(Cat c) {
  393. return c == Cat.BILLABLE ? AP_BIL_DAYTEXT : (c == Cat.NON_BILLABLE ? AP_NON_DAYTEXT : AP_OTH_DAYTEXT);
  394. }
  395. private String apKey(Cat c) {
  396. return c == Cat.BILLABLE ? AP_BIL_KEY : (c == Cat.NON_BILLABLE ? AP_NON_KEY : AP_OTH_KEY);
  397. }
  398. private String apHours(Cat c) {
  399. return c == Cat.BILLABLE ? AP_BIL_HOURS : (c == Cat.NON_BILLABLE ? AP_NON_HOURS : AP_OTH_HOURS);
  400. }
  401. private String apOpinion(Cat c) {
  402. return c == Cat.BILLABLE ? AP_BIL_OPINION : (c == Cat.NON_BILLABLE ? AP_NON_OPINION : AP_OTH_OPINION);
  403. }
  404. private String subTable(Cat c) {
  405. return c == Cat.BILLABLE ? SUB_BIL_TABLE : (c == Cat.NON_BILLABLE ? SUB_NON_TABLE : SUB_OTH_TABLE);
  406. }
  407. private String subKey(Cat c) {
  408. return c == Cat.BILLABLE ? SUB_BIL_KEY : (c == Cat.NON_BILLABLE ? SUB_NON_KEY : SUB_OTH_KEY);
  409. }
  410. private String subSubmitted(Cat c) {
  411. return c == Cat.BILLABLE ? SUB_BIL_SUBMITTED : (c == Cat.NON_BILLABLE ? SUB_NON_SUBMITTED : SUB_OTH_SUBMITTED);
  412. }
  413. private String subApproved(Cat c) {
  414. return c == Cat.BILLABLE ? SUB_BIL_APPROVED : (c == Cat.NON_BILLABLE ? SUB_NON_APPROVED : SUB_OTH_APPROVED);
  415. }
  416. // ===== 通用工具 =====
  417. private double num(Map m, String k) {
  418. Object v = m == null ? null : m.get(k);
  419. if (v == null) {
  420. return 0d;
  421. }
  422. try {
  423. return Double.parseDouble(String.valueOf(v));
  424. } catch (Exception e) {
  425. return 0d;
  426. }
  427. }
  428. private double sumRows(List<Map> rows, String field) {
  429. double sum = 0d;
  430. for (Map r : rows) {
  431. sum += num(r, field);
  432. }
  433. return sum;
  434. }
  435. private String str(Map m, String k) {
  436. Object v = m == null ? null : m.get(k);
  437. return v == null ? "" : String.valueOf(v);
  438. }
  439. private void sleep(long ms) {
  440. try {
  441. Thread.sleep(ms);
  442. } catch (InterruptedException ie) {
  443. Thread.currentThread().interrupt();
  444. }
  445. }
  446. }