|
|
@@ -0,0 +1,501 @@
|
|
|
+package com.malk.service.workhours;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.google.common.util.concurrent.RateLimiter;
|
|
|
+import com.malk.server.aliwork.YDConf;
|
|
|
+import com.malk.server.aliwork.YDParam;
|
|
|
+import com.malk.server.common.McException;
|
|
|
+import com.malk.server.workhours.ApprovalWriteBackResult;
|
|
|
+import com.malk.server.workhours.WHConf;
|
|
|
+import com.malk.service.aliwork.YDClient;
|
|
|
+import com.malk.service.aliwork.YDService;
|
|
|
+import com.malk.utils.UtilMap;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import java.util.*;
|
|
|
+import java.util.concurrent.*;
|
|
|
+import java.util.concurrent.atomic.AtomicInteger;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 审批结果回写工时汇总表
|
|
|
+ * <p>
|
|
|
+ * 审批结束或撤销后调用,入参为流程实例ID + 审批结果(0=拒绝/撤销,1=同意)。
|
|
|
+ * 工时审批含两个明细子表(项目/非项目工时),其他工时审批含一个明细子表。
|
|
|
+ * 明细按「填报人 + 工时日期(天) + 项目编号/Activity」1:1 对应工时汇总表(人+天一条)里的子表行:
|
|
|
+ * - 拒绝 → 对应子表行「已提交工时」置 0
|
|
|
+ * - 同意 → 对应子表行「已审批工时」= 该明细工时
|
|
|
+ * 主表合计按子表行重算(幂等,撤销/重发不翻倍)。
|
|
|
+ * <p>
|
|
|
+ * 设计文档:后端/阿科德斯/审批结果回写工时汇总表.md
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+public class ApprovalWriteBackService {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private YDClient ydClient;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private YDService ydService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private WHConf whConf;
|
|
|
+
|
|
|
+ private static final int THREAD_POOL_SIZE = 10;
|
|
|
+ private static final int MAX_RETRY = 2;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 明细审批意见「同意」取值(单选,已确认)
|
|
|
+ */
|
|
|
+ private static final String OPINION_APPROVE = "同意";
|
|
|
+
|
|
|
+ // ===== 工时汇总表:记录定位键 + 主表合计字段 =====
|
|
|
+ private static final String SUMMARY_SUBMITTER_ID = "textField_mmbffvd9"; // 填报人ID(文本)
|
|
|
+ private static final String SUMMARY_DAY_TEXT = "textField_mmbffvdb"; // 工时日期(文本) yyyyMMdd
|
|
|
+
|
|
|
+ private static final String M_DAY_SUBMITTED = "numberField_mmd1smem"; // 已提交工时(日)
|
|
|
+ private static final String M_DAY_APPROVED = "numberField_mmeakgi4"; // 已审批工时(日)
|
|
|
+ private static final String M_BIL_SUBMITTED = "numberField_mmacxewa"; // Billable已提交
|
|
|
+ private static final String M_BIL_APPROVED = "numberField_mmacxewg"; // Billable已审批
|
|
|
+ private static final String M_NON_SUBMITTED = "numberField_mmad6jbz"; // Non Billable已提交
|
|
|
+ private static final String M_NON_APPROVED = "numberField_mmad6jc1"; // Non Billable已审批
|
|
|
+ private static final String M_OTH_SUBMITTED = "numberField_mmd1smeq"; // 已提交(其他)
|
|
|
+ private static final String M_OTH_APPROVED = "numberField_mmd1smes"; // 已审批(其他)
|
|
|
+
|
|
|
+ // ===== 工时汇总表:三个子表 =====
|
|
|
+ private static final String SUB_BIL_TABLE = "tableField_mmczo634";
|
|
|
+ private static final String SUB_BIL_KEY = "textField_mmacxew3"; // 项目编号
|
|
|
+ private static final String SUB_BIL_SUBMITTED = "numberField_mmczo636"; // 已提交工时
|
|
|
+ private static final String SUB_BIL_APPROVED = "numberField_mmczo637"; // 已审批工时
|
|
|
+
|
|
|
+ private static final String SUB_NON_TABLE = "tableField_mmczo63h";
|
|
|
+ private static final String SUB_NON_KEY = "textField_mmczo639"; // 项目编号
|
|
|
+ private static final String SUB_NON_SUBMITTED = "numberField_mmczo63c";
|
|
|
+ private static final String SUB_NON_APPROVED = "numberField_mmczo63d";
|
|
|
+
|
|
|
+ private static final String SUB_OTH_TABLE = "tableField_mmeakgid";
|
|
|
+ private static final String SUB_OTH_KEY = "selectField_mmeakgie"; // Activity
|
|
|
+ private static final String SUB_OTH_SUBMITTED = "numberField_mmeakgij";
|
|
|
+ private static final String SUB_OTH_APPROVED = "numberField_mmeakgii";
|
|
|
+
|
|
|
+ // ===== 审批单明细子表字段(按类别)=====
|
|
|
+ // 工时审批·项目工时明细(Billable)
|
|
|
+ private static final String AP_BIL_TABLE = "tableField_mmae8t99";
|
|
|
+ private static final String AP_BIL_USERID = "textField_mmd1ozk4";
|
|
|
+ private static final String AP_BIL_KEY = "textField_mmacxew3"; // 项目编号
|
|
|
+ private static final String AP_BIL_DAYTEXT = "textField_mmd8g654";
|
|
|
+ private static final String AP_BIL_HOURS = "numberField_mmacxew9";
|
|
|
+ private static final String AP_BIL_OPINION = "radioField_mpwhk294";
|
|
|
+ // 工时审批·非项目工时明细(Non Billable)
|
|
|
+ private static final String AP_NON_TABLE = "tableField_mmd1wu9h";
|
|
|
+ private static final String AP_NON_USERID = "textField_mmd1wu9f";
|
|
|
+ private static final String AP_NON_KEY = "textField_mmd1wu9d"; // 项目编号
|
|
|
+ private static final String AP_NON_DAYTEXT = "textField_mmd8g655";
|
|
|
+ private static final String AP_NON_HOURS = "numberField_mmd1wu9e";
|
|
|
+ private static final String AP_NON_OPINION = "radioField_mpwhk295";
|
|
|
+ // 其他工时审批·明细(与非项目同子表ID,但语义为 Activity)
|
|
|
+ private static final String AP_OTH_TABLE = "tableField_mmd1wu9h";
|
|
|
+ private static final String AP_OTH_USERID = "textField_mmd1wu9f";
|
|
|
+ private static final String AP_OTH_KEY = "selectField_mmeakgie"; // Activity
|
|
|
+ private static final String AP_OTH_DAYTEXT = "textField_mmd8g655";
|
|
|
+ private static final String AP_OTH_HOURS = "numberField_mmd1wu9e";
|
|
|
+ private static final String AP_OTH_OPINION = "radioField_mpwhk294";
|
|
|
+
|
|
|
+ private enum Cat {BILLABLE, NON_BILLABLE, OTHER}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 单条审批明细(归一化)
|
|
|
+ */
|
|
|
+ private static class DetailRow {
|
|
|
+ Cat cat;
|
|
|
+ String userId;
|
|
|
+ String dayText;
|
|
|
+ String key; // 项目编号 / Activity
|
|
|
+ double hours;
|
|
|
+ boolean approve; // 有效意见:true=同意, false=拒绝
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 审批结果回写工时汇总表
|
|
|
+ *
|
|
|
+ * @param formInstanceId 流程实例ID
|
|
|
+ * @param result 审批结果:0=拒绝/撤销,1=同意
|
|
|
+ * @return 回写结果统计
|
|
|
+ */
|
|
|
+ public ApprovalWriteBackResult writeBack(String formInstanceId, int result) {
|
|
|
+ McException.assertAccessException(StringUtils.isBlank(formInstanceId), "实例ID不能为空");
|
|
|
+
|
|
|
+ // 1. 按实例ID查审批单表单详情(表单接口 retrieve_id),含主表 + 内联子表(子表内联上限 50)
|
|
|
+ Map approvalFormData = ydClient.queryData(YDParam.builder()
|
|
|
+ .appType(whConf.getYidaAppType())
|
|
|
+ .systemToken(whConf.getYidaSystemToken())
|
|
|
+ .formInstanceId(formInstanceId)
|
|
|
+ .build(), YDConf.FORM_QUERY.retrieve_id).getFormData();
|
|
|
+ log.info("[审批回写] 审批单详情 formInstanceId={} keys={}", formInstanceId,
|
|
|
+ approvalFormData == null ? null : approvalFormData.keySet());
|
|
|
+ McException.assertAccessException(approvalFormData == null || approvalFormData.isEmpty(),
|
|
|
+ "未查询到审批单数据,实例ID=" + formInstanceId);
|
|
|
+
|
|
|
+ // 2. 类型判定:工时审批含「项目工时明细」子表 tableField_mmae8t99,其他工时审批没有
|
|
|
+ boolean isOther = !approvalFormData.containsKey(AP_BIL_TABLE);
|
|
|
+ boolean approveWhole = (result == 1);
|
|
|
+
|
|
|
+ // 3. 取审批单明细(内联子表 <50 直接用,==50 才 queryDetails 递归取全,避免无效请求)
|
|
|
+ List<DetailRow> rows = new ArrayList<>();
|
|
|
+ if (isOther) {
|
|
|
+ rows.addAll(loadDetailRows(formInstanceId, approvalFormData, Cat.OTHER, approveWhole));
|
|
|
+ } else {
|
|
|
+ rows.addAll(loadDetailRows(formInstanceId, approvalFormData, Cat.BILLABLE, approveWhole));
|
|
|
+ rows.addAll(loadDetailRows(formInstanceId, approvalFormData, Cat.NON_BILLABLE, approveWhole));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 按汇总表记录键(填报人 + 工时日期文本)分组
|
|
|
+ Map<String, List<DetailRow>> groups = new LinkedHashMap<>();
|
|
|
+ for (DetailRow r : rows) {
|
|
|
+ if (StringUtils.isBlank(r.userId) || StringUtils.isBlank(r.dayText)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ groups.computeIfAbsent(r.userId + "" + r.dayText, k -> new ArrayList<>()).add(r);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 并发逐「日记录」处理
|
|
|
+ AtomicInteger hitRecords = new AtomicInteger(0);
|
|
|
+ AtomicInteger updatedRows = new AtomicInteger(0);
|
|
|
+ AtomicInteger missRows = new AtomicInteger(0);
|
|
|
+ AtomicInteger failRecords = new AtomicInteger(0);
|
|
|
+
|
|
|
+ RateLimiter limiter = RateLimiter.create(20.0);
|
|
|
+ ExecutorService executor = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
|
|
|
+ try {
|
|
|
+ List<Future<?>> futures = new ArrayList<>();
|
|
|
+ for (Map.Entry<String, List<DetailRow>> e : groups.entrySet()) {
|
|
|
+ List<DetailRow> groupRows = e.getValue();
|
|
|
+ String userId = groupRows.get(0).userId;
|
|
|
+ String dayText = groupRows.get(0).dayText;
|
|
|
+ futures.add(executor.submit(() -> {
|
|
|
+ for (int retry = 0; retry <= MAX_RETRY; retry++) {
|
|
|
+ try {
|
|
|
+ int[] r = applyToRecord(userId, dayText, groupRows, limiter);
|
|
|
+ if (r == null) {
|
|
|
+ // 未命中汇总表记录
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ hitRecords.incrementAndGet();
|
|
|
+ updatedRows.addAndGet(r[0]);
|
|
|
+ missRows.addAndGet(r[1]);
|
|
|
+ return;
|
|
|
+ } catch (Exception ex) {
|
|
|
+ if (retry < MAX_RETRY) {
|
|
|
+ sleep(1000L * (retry + 1));
|
|
|
+ } else {
|
|
|
+ failRecords.incrementAndGet();
|
|
|
+ log.error("[审批回写] 记录处理失败 userId={} day={}", userId, dayText, ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }));
|
|
|
+ }
|
|
|
+ for (Future<?> f : futures) {
|
|
|
+ try {
|
|
|
+ f.get();
|
|
|
+ } catch (Exception ex) {
|
|
|
+ log.error("[审批回写] 线程执行异常", ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ executor.shutdown();
|
|
|
+ }
|
|
|
+
|
|
|
+ ApprovalWriteBackResult stats = ApprovalWriteBackResult.builder()
|
|
|
+ .formType(isOther ? "其他工时审批" : "工时审批")
|
|
|
+ .formInstanceId(formInstanceId)
|
|
|
+ .result(result)
|
|
|
+ .detailRows(rows.size())
|
|
|
+ .groups(groups.size())
|
|
|
+ .hitRecords(hitRecords.get())
|
|
|
+ .updatedRows(updatedRows.get())
|
|
|
+ .missRows(missRows.get())
|
|
|
+ .failRecords(failRecords.get())
|
|
|
+ .build();
|
|
|
+ log.info("[审批回写] 完成 {}", stats);
|
|
|
+ return stats;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理单条汇总表日记录
|
|
|
+ *
|
|
|
+ * @return {updatedRows, missRows};汇总表无此记录返回 null
|
|
|
+ */
|
|
|
+ private int[] applyToRecord(String userId, String dayText, List<DetailRow> groupRows, RateLimiter limiter) {
|
|
|
+ // a. 定位汇总表记录(填报人ID + 工时日期文本,代码内精确过滤)
|
|
|
+ limiter.acquire();
|
|
|
+ List<Map> found = (List<Map>) ydClient.queryData(YDParam.builder()
|
|
|
+ .appType(whConf.getYidaAppType())
|
|
|
+ .systemToken(whConf.getYidaSystemToken())
|
|
|
+ .formUuid(whConf.getFormUuidWorkHoursSummary())
|
|
|
+ .searchFieldJson(JSON.toJSONString(UtilMap.map(
|
|
|
+ SUMMARY_SUBMITTER_ID + ", " + SUMMARY_DAY_TEXT, userId, dayText)))
|
|
|
+ .build(), YDConf.FORM_QUERY.retrieve_search_form).getData();
|
|
|
+
|
|
|
+ String summaryInstId = null;
|
|
|
+ for (Map item : found) {
|
|
|
+ Map fd = (Map) item.get("formData");
|
|
|
+ if (fd == null) continue;
|
|
|
+ if (userId.equals(String.valueOf(fd.get(SUMMARY_SUBMITTER_ID)))
|
|
|
+ && dayText.equals(String.valueOf(fd.get(SUMMARY_DAY_TEXT)))) {
|
|
|
+ summaryInstId = String.valueOf(item.get("formInstanceId"));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (StringUtils.isBlank(summaryInstId)) {
|
|
|
+ log.warn("[审批回写] 未命中汇总表记录 userId={} day={}", userId, dayText);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // b. 取汇总表记录完整数据(含三子表)
|
|
|
+ limiter.acquire();
|
|
|
+ Map formData = ydClient.queryData(YDParam.builder()
|
|
|
+ .appType(whConf.getYidaAppType())
|
|
|
+ .systemToken(whConf.getYidaSystemToken())
|
|
|
+ .formInstanceId(summaryInstId)
|
|
|
+ .build(), YDConf.FORM_QUERY.retrieve_id).getFormData();
|
|
|
+
|
|
|
+ Map<String, Object> updateData = new HashMap<>();
|
|
|
+ int updated = 0;
|
|
|
+ int miss = 0;
|
|
|
+
|
|
|
+ // c. 按类别分组本记录的明细,逐类别重建子表 + 置值
|
|
|
+ Map<Cat, List<DetailRow>> byCat = new EnumMap<>(Cat.class);
|
|
|
+ for (DetailRow r : groupRows) {
|
|
|
+ byCat.computeIfAbsent(r.cat, k -> new ArrayList<>()).add(r);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 各类别已提交/已审批合计(受影响类别从重建行重算,未受影响沿用主表现值)
|
|
|
+ double bilSubmitted = num(formData, M_BIL_SUBMITTED), bilApproved = num(formData, M_BIL_APPROVED);
|
|
|
+ double nonSubmitted = num(formData, M_NON_SUBMITTED), nonApproved = num(formData, M_NON_APPROVED);
|
|
|
+ double othSubmitted = num(formData, M_OTH_SUBMITTED), othApproved = num(formData, M_OTH_APPROVED);
|
|
|
+
|
|
|
+ for (Map.Entry<Cat, List<DetailRow>> e : byCat.entrySet()) {
|
|
|
+ Cat cat = e.getKey();
|
|
|
+ String subTable = subTable(cat);
|
|
|
+ String keyField = subKey(cat);
|
|
|
+ String submittedField = subSubmitted(cat);
|
|
|
+ String approvedField = subApproved(cat);
|
|
|
+
|
|
|
+ // 子表取数控制:内联 <50 直接用,==50 才递归取全,避免全量覆盖时漏行被删
|
|
|
+ List<Map> rebuilt = rebuildRows(
|
|
|
+ resolveDetailRows(summaryInstId, subTable, (List<Map>) formData.get(subTable)));
|
|
|
+
|
|
|
+ for (DetailRow dr : e.getValue()) {
|
|
|
+ Map target = null;
|
|
|
+ for (Map row : rebuilt) {
|
|
|
+ if (dr.key != null && dr.key.equals(String.valueOf(row.get(keyField)))) {
|
|
|
+ target = row;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (target == null) {
|
|
|
+ miss++;
|
|
|
+ log.warn("[审批回写] 子表行未匹配 cat={} key={} userId={} day={}", cat, dr.key, userId, dayText);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (dr.approve) {
|
|
|
+ target.put(approvedField, dr.hours);
|
|
|
+ } else {
|
|
|
+ target.put(submittedField, 0);
|
|
|
+ }
|
|
|
+ updated++;
|
|
|
+ }
|
|
|
+
|
|
|
+ updateData.put(subTable, rebuilt);
|
|
|
+ double sumSubmitted = sumRows(rebuilt, submittedField);
|
|
|
+ double sumApproved = sumRows(rebuilt, approvedField);
|
|
|
+ switch (cat) {
|
|
|
+ case BILLABLE:
|
|
|
+ bilSubmitted = sumSubmitted;
|
|
|
+ bilApproved = sumApproved;
|
|
|
+ break;
|
|
|
+ case NON_BILLABLE:
|
|
|
+ nonSubmitted = sumSubmitted;
|
|
|
+ nonApproved = sumApproved;
|
|
|
+ break;
|
|
|
+ case OTHER:
|
|
|
+ othSubmitted = sumSubmitted;
|
|
|
+ othApproved = sumApproved;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // d. 主表合计重算
|
|
|
+ updateData.put(M_BIL_SUBMITTED, bilSubmitted);
|
|
|
+ updateData.put(M_BIL_APPROVED, bilApproved);
|
|
|
+ updateData.put(M_NON_SUBMITTED, nonSubmitted);
|
|
|
+ updateData.put(M_NON_APPROVED, nonApproved);
|
|
|
+ updateData.put(M_OTH_SUBMITTED, othSubmitted);
|
|
|
+ updateData.put(M_OTH_APPROVED, othApproved);
|
|
|
+ updateData.put(M_DAY_SUBMITTED, bilSubmitted + nonSubmitted + othSubmitted);
|
|
|
+ updateData.put(M_DAY_APPROVED, bilApproved + nonApproved + othApproved);
|
|
|
+
|
|
|
+ // e. 全量覆盖更新(子表整组替换 + 主表合计)
|
|
|
+ limiter.acquire();
|
|
|
+ ydClient.operateData(YDParam.builder()
|
|
|
+ .appType(whConf.getYidaAppType())
|
|
|
+ .systemToken(whConf.getYidaSystemToken())
|
|
|
+ .formInstanceId(summaryInstId)
|
|
|
+ .updateFormDataJson(JSON.toJSONString(updateData))
|
|
|
+ .ignoreEmpty(false)
|
|
|
+ .useLatestVersion(true)
|
|
|
+ .build(), YDConf.FORM_OPERATION.update);
|
|
|
+
|
|
|
+ return new int[]{updated, miss};
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 加载审批单某类别明细(内联子表优先,必要时递归取全)
|
|
|
+ */
|
|
|
+ private List<DetailRow> loadDetailRows(String formInstanceId, Map approvalFormData, Cat cat, boolean approveWhole) {
|
|
|
+ String tableId = apTable(cat);
|
|
|
+ List<Map> raw = resolveDetailRows(formInstanceId, tableId, (List<Map>) approvalFormData.get(tableId));
|
|
|
+
|
|
|
+ List<DetailRow> list = new ArrayList<>();
|
|
|
+ for (Map row : raw) {
|
|
|
+ DetailRow dr = new DetailRow();
|
|
|
+ dr.cat = cat;
|
|
|
+ dr.userId = str(row, apUserId(cat));
|
|
|
+ dr.dayText = str(row, apDayText(cat));
|
|
|
+ dr.key = str(row, apKey(cat));
|
|
|
+ dr.hours = num(row, apHours(cat));
|
|
|
+ String opinion = str(row, apOpinion(cat));
|
|
|
+ // 有效意见:整单拒绝/撤销 → 全拒绝;整单同意 → 看单条明细意见
|
|
|
+ dr.approve = approveWhole && OPINION_APPROVE.equals(opinion);
|
|
|
+ list.add(dr);
|
|
|
+ }
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 子表取数控制:主表详情已内联返回子表时,行数 <50 视为完整直接用;
|
|
|
+ * ==50(可能被宜搭截断)或内联缺失,才调 {@link YDService#queryDetails} 递归取全,避免无效请求。
|
|
|
+ * 复用 ydService 封装,不重复造分页轮子。
|
|
|
+ */
|
|
|
+ private List<Map> resolveDetailRows(String formInstanceId, String tableId, List<Map> inlineRows) {
|
|
|
+ if (inlineRows != null && inlineRows.size() < 50) {
|
|
|
+ return inlineRows;
|
|
|
+ }
|
|
|
+ return ydService.queryDetails(YDParam.builder()
|
|
|
+ .appType(whConf.getYidaAppType())
|
|
|
+ .systemToken(whConf.getYidaSystemToken())
|
|
|
+ .formInstanceId(formInstanceId)
|
|
|
+ .tableFieldId(tableId)
|
|
|
+ .pageNumber(1)
|
|
|
+ .build());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 重建子表行:保留全部字段,成员/关联字段用 _id 取真实ID(全量覆盖写回前置)
|
|
|
+ */
|
|
|
+ private List<Map> rebuildRows(List<Map> rows) {
|
|
|
+ List<Map> out = new ArrayList<>();
|
|
|
+ if (rows == null) {
|
|
|
+ return out;
|
|
|
+ }
|
|
|
+ for (Map row : rows) {
|
|
|
+ Map newRow = new HashMap();
|
|
|
+ for (Object keyObj : row.keySet()) {
|
|
|
+ String key = String.valueOf(keyObj);
|
|
|
+ if (key.endsWith("_value") || (key.startsWith("employeeField_") && !key.endsWith("_id"))) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ String k = key;
|
|
|
+ if (key.endsWith("_id")) {
|
|
|
+ if (key.startsWith("employeeField_") || key.startsWith("associationFormField_")) {
|
|
|
+ k = key.substring(0, key.length() - 3);
|
|
|
+ } else {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ newRow.put(k, YDConf.getDataByCompId(row, k));
|
|
|
+ }
|
|
|
+ out.add(newRow);
|
|
|
+ }
|
|
|
+ return out;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ===== 字段映射辅助 =====
|
|
|
+ private String apTable(Cat c) {
|
|
|
+ return c == Cat.BILLABLE ? AP_BIL_TABLE : (c == Cat.NON_BILLABLE ? AP_NON_TABLE : AP_OTH_TABLE);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String apUserId(Cat c) {
|
|
|
+ return c == Cat.BILLABLE ? AP_BIL_USERID : (c == Cat.NON_BILLABLE ? AP_NON_USERID : AP_OTH_USERID);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String apDayText(Cat c) {
|
|
|
+ return c == Cat.BILLABLE ? AP_BIL_DAYTEXT : (c == Cat.NON_BILLABLE ? AP_NON_DAYTEXT : AP_OTH_DAYTEXT);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String apKey(Cat c) {
|
|
|
+ return c == Cat.BILLABLE ? AP_BIL_KEY : (c == Cat.NON_BILLABLE ? AP_NON_KEY : AP_OTH_KEY);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String apHours(Cat c) {
|
|
|
+ return c == Cat.BILLABLE ? AP_BIL_HOURS : (c == Cat.NON_BILLABLE ? AP_NON_HOURS : AP_OTH_HOURS);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String apOpinion(Cat c) {
|
|
|
+ return c == Cat.BILLABLE ? AP_BIL_OPINION : (c == Cat.NON_BILLABLE ? AP_NON_OPINION : AP_OTH_OPINION);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String subTable(Cat c) {
|
|
|
+ return c == Cat.BILLABLE ? SUB_BIL_TABLE : (c == Cat.NON_BILLABLE ? SUB_NON_TABLE : SUB_OTH_TABLE);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String subKey(Cat c) {
|
|
|
+ return c == Cat.BILLABLE ? SUB_BIL_KEY : (c == Cat.NON_BILLABLE ? SUB_NON_KEY : SUB_OTH_KEY);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String subSubmitted(Cat c) {
|
|
|
+ return c == Cat.BILLABLE ? SUB_BIL_SUBMITTED : (c == Cat.NON_BILLABLE ? SUB_NON_SUBMITTED : SUB_OTH_SUBMITTED);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String subApproved(Cat c) {
|
|
|
+ return c == Cat.BILLABLE ? SUB_BIL_APPROVED : (c == Cat.NON_BILLABLE ? SUB_NON_APPROVED : SUB_OTH_APPROVED);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ===== 通用工具 =====
|
|
|
+ private double num(Map m, String k) {
|
|
|
+ Object v = m == null ? null : m.get(k);
|
|
|
+ if (v == null) {
|
|
|
+ return 0d;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ return Double.parseDouble(String.valueOf(v));
|
|
|
+ } catch (Exception e) {
|
|
|
+ return 0d;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private double sumRows(List<Map> rows, String field) {
|
|
|
+ double sum = 0d;
|
|
|
+ for (Map r : rows) {
|
|
|
+ sum += num(r, field);
|
|
|
+ }
|
|
|
+ return sum;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String str(Map m, String k) {
|
|
|
+ Object v = m == null ? null : m.get(k);
|
|
|
+ return v == null ? "" : String.valueOf(v);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void sleep(long ms) {
|
|
|
+ try {
|
|
|
+ Thread.sleep(ms);
|
|
|
+ } catch (InterruptedException ie) {
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|