Jelajahi Sumber

固话导入

HULEI 3 minggu lalu
induk
melakukan
5907e59b75

+ 1 - 1
com.awspaas.user.apps.donenow_ctt/src/com/awspaas/user/apps/donenow_ctt/controller/contractApproveController.java

@@ -2605,7 +2605,7 @@ public class contractApproveController {
             Map<String, Object> result = new HashMap<>();
             result.put("isCreated", true);
             result.put("insertCount", detailIds.size());
-            String message = String.format("成功创建 %d 条季度汇总明细记录(已同步删除关联流程数据)", detailIds.size());
+            String message = String.format("成功创建 %d 条季度汇总明细记录", detailIds.size());
             result.put("message", message);
 
             ResponseObject responseObject = ResponseObject.newOkResponse();

+ 311 - 0
com.awspaas.user.apps.donenow_ctt/src/com/awspaas/user/apps/donenow_ctt/event/DataWindowImportAfterEvent.java

@@ -0,0 +1,311 @@
+import com.actionsoft.bpms.dw.exec.event.ideimport.DataWindowAfterImport;
+import com.actionsoft.bpms.dw.exec.imp.model.ImportModel;
+import com.actionsoft.bpms.server.UserContext;
+import com.actionsoft.bpms.commons.database.RowMap;
+import com.actionsoft.bpms.util.DBSql;
+import org.apache.commons.lang3.StringUtils;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.*;
+
+public class DataWindowImportAfterEvent extends DataWindowAfterImport {
+    // ====================== 全局静态常量(统一配置,方便维护) =======================
+    // 日期格式化常量:核心精确到天,兼容账期转换
+    private static final DateTimeFormatter FORMATTER_DATE = DateTimeFormatter.ofPattern("yyyy-MM-dd"); // 当前时间(精确到天)
+    private static final DateTimeFormatter FORMATTER_YEAR_MONTH_DASH = DateTimeFormatter.ofPattern("yyyy-MM"); // 账期格式1
+    private static final DateTimeFormatter FORMATTER_YEAR_MONTH_NO_DASH = DateTimeFormatter.ofPattern("yyyyMM"); // 账期格式2
+    private static final DateTimeFormatter FORMATTER_YEAR_MONTH_DAY = DateTimeFormatter.ofPattern("yyyy-MM-dd"); // 周期起止日期
+
+    // 数据库表名常量(对应你的业务表)
+    private static final String TABLE_INSTALLED_PRODUCT = "BO_EU_DNCRM_INSTALLED_PRODUCT"; // 已安装产品表(匹配合同ID)
+    private static final String TABLE_CONTRACT_PERIOD = "BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD"; // 合同周期表(最终更新表)
+    private static final String TABLE_PROXY_PAY = "BO_EU_DNCTT_PROXY_PAY"; // 代缴费表(核心数据来源,手机号/账期/金额)
+
+    // 数据库字段名常量(与表结构严格对应)
+    private static final String FIELD_CONTRACT_SERVICE_ID = "CONTRACT_SERVICE_ID"; // 合同服务ID
+    private static final String FIELD_PERIOD_PRICE = "PERIOD_PRICE"; // 周期金额(待更新)
+    private static final String FIELD_REFERENCE_NUMBER = "REFERENCE_NUMBER"; // 参考编号(提取手机号后四位)
+    private static final String FIELD_EXTENDED_PRICE = "EXTENDED_PRICE"; // 含税总金额(数据库金额来源)
+    private static final String FIELD_CREATE_DATE = "CREATE_DATE"; // 创建时间(查询条件)
+    private static final String FIELD_PHONE = "PHONE"; // 完整手机号
+    private static final String FIELD_FEE_DATE = "FEE_DATE"; // 账期
+
+    // 提示信息常量(统一日志输出)
+    private static final String MSG_NO_CURRENT_DATA = "未查询到当天(" + LocalDate.now().format(FORMATTER_DATE) + ")的有效代缴费数据";
+    private static final String MSG_NO_CONTRACT_ID = "未匹配到对应的合同服务ID";
+    private static final String MSG_FEE_DATE_FORMAT_ERROR = "账期格式错误(需为yyyy-MM或yyyyMM)";
+    private static final String MSG_NO_VALID_DATA = "无有效数据可进行后续处理";
+    private static final String MSG_PROCESS_COMPLETE = "处理完成:更新周期表%d条数据";
+
+    // 简单日志方法(无SDK依赖,控制台输出)
+    private static void log(String msg) {
+        System.out.println("[DataWindowImportAfterEvent] " + msg);
+    }
+
+    // ====================== 核心业务方法(重写平台导入事件,全数据库驱动) =======================
+    @Override
+    public void excute(UserContext userContext, String processDefId, String formDefId, List<ImportModel> importModels, String type, Map<String, Object> extendParams) {
+        // 1. 获取当前系统日期(精确到天),作为数据库查询核心条件
+        String currentDate = LocalDate.now().format(FORMATTER_DATE);
+        log("===== 开始处理,当前查询日期(精确到天):" + currentDate + " =====");
+
+        // 2. 从代缴费表查询当天所有有效数据(手机号、账期、金额)
+        List<RowMap> proxyPayList = getProxyPayDataByCurrentDate(currentDate);
+        if (proxyPayList == null || proxyPayList.isEmpty()) {
+            log(MSG_NO_CURRENT_DATA);
+            log("===== 处理结束,无有效数据 =====");
+            return;
+        }
+        log("查询到当天代缴费记录共:" + proxyPayList.size() + " 条");
+
+        // 3. 整理数据库返回数据(格式化、去重、过滤无效数据)
+        Map<String, BigDecimal> phoneAmountMap = new HashMap<>(); // 手机号后四位 → 含税金额
+        Map<String, String> phoneFeeDateMap = new HashMap<>();    // 手机号后四位 → 账期
+        Set<String> phoneLast4Set = new HashSet<>();               // 去重手机号后四位
+
+        for (RowMap row : proxyPayList) {
+            // 3.1 提取并校验完整手机号(截取后四位)
+            String fullPhone = Objects.toString(row.get(FIELD_PHONE), "").trim();
+            if (StringUtils.isBlank(fullPhone) || fullPhone.length() < 4) {
+                log("手机号无效(为空或长度不足4位),跳过当前记录:" + fullPhone);
+                continue;
+            }
+            String phoneLast4 = fullPhone.substring(fullPhone.length() - 4); // 截取后四位
+
+            // 3.2 提取并校验账期
+            String feeDate = Objects.toString(row.get(FIELD_FEE_DATE), "").trim();
+            if (StringUtils.isBlank(feeDate)) {
+                log("账期为空,跳过当前记录(手机号后四位:" + phoneLast4 + ")");
+                continue;
+            }
+
+            // 3.3 提取并格式化金额(兼容多类型,避免报错)
+            BigDecimal amount = formatAmountFromRow(row);
+            if (amount.compareTo(BigDecimal.ZERO) == 0) {
+                log("金额无效(为0或转换失败),跳过当前记录(手机号后四位:" + phoneLast4 + ")");
+                continue;
+            }
+
+            // 3.4 封装到容器(自动去重,Map重复key会覆盖,保留最新数据)
+            phoneLast4Set.add(phoneLast4);
+            phoneAmountMap.put(phoneLast4, amount);
+            phoneFeeDateMap.put(phoneLast4, feeDate);
+        }
+
+        // 4. 校验整理后的数据是否有效
+        if (phoneLast4Set.isEmpty() || phoneAmountMap.isEmpty()) {
+            log(MSG_NO_VALID_DATA);
+            log("===== 处理结束,无有效数据 =====");
+            return;
+        }
+        log("整理后有效数据共:" + phoneLast4Set.size() + " 条");
+
+        // 5. 手机号后四位匹配合同服务ID(建立映射关系)
+        Map<String, String> phoneContractMap = matchPhoneToContractId(new ArrayList<>(phoneLast4Set));
+        if (phoneContractMap.isEmpty()) {
+            log(MSG_NO_CONTRACT_ID);
+            log("===== 处理结束,无匹配合同 =====");
+            return;
+        }
+        log("匹配到有效合同服务ID共:" + phoneContractMap.size() + " 个");
+
+        // 6. 转换为合同ID-金额映射(提取基准账期,用于后续日期转换)
+        Map<String, BigDecimal> contractAmountMap = new HashMap<>();
+        String targetFeeDate = ""; // 基准账期(取第一个有效账期)
+
+        for (Map.Entry<String, String> entry : phoneContractMap.entrySet()) {
+            String phoneLast4 = entry.getKey();
+            String contractId = entry.getValue();
+            BigDecimal amount = phoneAmountMap.getOrDefault(phoneLast4, BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP);
+
+            // 过滤无效金额
+            if (amount.compareTo(BigDecimal.ZERO) > 0) {
+                contractAmountMap.put(contractId, amount);
+            }
+
+            // 提取基准账期(仅取第一个有效值)
+            if (StringUtils.isBlank(targetFeeDate)) {
+                targetFeeDate = phoneFeeDateMap.getOrDefault(phoneLast4, "");
+            }
+        }
+
+        // 7. 账期转换为当月起止日期(精准更新对应周期)
+        Map<String, String> periodDateMap = convertFeeDateToPeriod(targetFeeDate);
+        if (periodDateMap == null) {
+            log(MSG_FEE_DATE_FORMAT_ERROR);
+            log("===== 处理结束,账期转换失败 =====");
+            return;
+        }
+        String periodBeginDate = periodDateMap.get("begin");
+        String periodEndDate = periodDateMap.get("end");
+        log("账期转换完成,更新周期:" + periodBeginDate + " 至 " + periodEndDate);
+
+        // 8. 批量更新合同服务周期表(核心落地步骤)
+        int updatedCount = batchUpdateContractPeriod(contractAmountMap, periodBeginDate, periodEndDate);
+
+        // 9. 打印最终处理结果,流程闭环
+        log("===== 处理完成,共更新周期表 " + updatedCount + " 条数据 =====");
+    }
+
+    // ====================== 工具方法(各司其职,解耦主流程,无报错兼容) =======================
+    /**
+     * 核心:以当前日期(精确到天)为条件,查询代缴费表当天所有有效数据
+     * 忽略CREATE_DATE的时分秒,避免因时间戳差异导致数据遗漏
+     */
+    private List<RowMap> getProxyPayDataByCurrentDate(String currentDate) {
+        // SQL优化:使用DATE()函数提取日期部分,兼容DATETIME/DATE类型字段
+        String sql = "SELECT " + FIELD_PHONE + ", " + FIELD_FEE_DATE + ", " + FIELD_EXTENDED_PRICE + ", " + FIELD_CREATE_DATE +
+                " FROM " + TABLE_PROXY_PAY +
+                " WHERE DATE(" + FIELD_CREATE_DATE + ") = ?"; // 忽略时分秒,精准匹配当天所有记录
+
+        // 执行查询,返回当天所有代缴费记录(平台通用方法,无报错)
+        return DBSql.getMaps(sql, currentDate);
+    }
+
+    /**
+     * 格式化从RowMap中提取的金额(兼容BigDecimal/Number/String类型,避免转换报错)
+     */
+    private BigDecimal formatAmountFromRow(RowMap row) {
+        Object amountObj = row.get(FIELD_EXTENDED_PRICE);
+        BigDecimal amount = BigDecimal.ZERO;
+
+        if (amountObj != null) {
+            try {
+                if (amountObj instanceof BigDecimal) {
+                    amount = (BigDecimal) amountObj;
+                } else if (amountObj instanceof Number) {
+                    // 兼容Integer/Double/Long等数值类型
+                    amount = BigDecimal.valueOf(((Number) amountObj).doubleValue());
+                } else {
+                    // 兼容字符串类型金额,去除空格后转换
+                    amount = new BigDecimal(amountObj.toString().trim());
+                }
+            } catch (NumberFormatException e) {
+                log("金额转换失败,无效值:" + amountObj.toString());
+                amount = BigDecimal.ZERO;
+            }
+        }
+
+        // 格式化金额:保留2位小数,四舍五入(符合财务数据规范)
+        return amount.setScale(2, RoundingMode.HALF_UP);
+    }
+
+    /**
+     * 匹配手机号后四位与合同服务ID(从已安装产品表查询,批量匹配提高效率)
+     */
+    private Map<String, String> matchPhoneToContractId(List<String> phoneLast4List) {
+        Map<String, String> phoneContractMap = new HashMap<>();
+        if (phoneLast4List == null || phoneLast4List.isEmpty()) {
+            return phoneContractMap;
+        }
+
+        try {
+            // 动态拼接IN条件SQL,批量查询(避免循环单条查询,提高效率)
+            StringBuilder sql = new StringBuilder("SELECT RIGHT(" + FIELD_REFERENCE_NUMBER + ",4) AS REF_LAST4, " +
+                    FIELD_CONTRACT_SERVICE_ID + " FROM " + TABLE_INSTALLED_PRODUCT + " WHERE RIGHT(" +
+                    FIELD_REFERENCE_NUMBER + ",4) IN (");
+
+            for (int i = 0; i < phoneLast4List.size(); i++) {
+                sql.append(i == 0 ? "?" : ",?");
+            }
+            sql.append(")");
+
+            // 执行查询,封装映射关系
+            List<RowMap> contractList = DBSql.getMaps(sql.toString(), phoneLast4List.toArray());
+            for (RowMap row : contractList) {
+                String refLast4 = row.getString("REF_LAST4");
+                refLast4 = refLast4 == null ? "" : refLast4.trim();
+                String contractId = row.getString(FIELD_CONTRACT_SERVICE_ID);
+                contractId = contractId == null ? "" : contractId.trim();
+
+                // 过滤无效数据,封装有效映射
+                if (StringUtils.isNoneBlank(refLast4, contractId)) {
+                    phoneContractMap.put(refLast4, contractId);
+                }
+            }
+        } catch (Exception e) {
+            log("匹配手机号与合同ID异常:" + e.getMessage());
+            e.printStackTrace();
+        }
+
+        return phoneContractMap;
+    }
+
+    /**
+     * 账期转换为当月起止日期(支持yyyy-MM和yyyyMM两种格式,兼容业务场景)
+     */
+    private Map<String, String> convertFeeDateToPeriod(String feeDate) {
+        if (StringUtils.isBlank(feeDate)) {
+            return null;
+        }
+
+        try {
+            // 解析账期为YearMonth对象,兼容两种格式
+            YearMonth feeYearMonth = feeDate.contains("-") ?
+                    YearMonth.parse(feeDate, FORMATTER_YEAR_MONTH_DASH) :
+                    YearMonth.parse(feeDate, FORMATTER_YEAR_MONTH_NO_DASH);
+
+            // 构建当月起止日期(1号至月末最后一天)
+            Map<String, String> periodMap = new HashMap<>(2);
+            periodMap.put("begin", feeYearMonth.atDay(1).format(FORMATTER_YEAR_MONTH_DAY));
+            periodMap.put("end", feeYearMonth.atEndOfMonth().format(FORMATTER_YEAR_MONTH_DAY));
+
+            return periodMap;
+        } catch (DateTimeParseException e) {
+            log("账期格式转换异常,无效账期:" + feeDate + ",异常信息:" + e.getMessage());
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * 批量更新合同服务周期表(使用CASE WHEN语法,一条SQL完成所有更新,高效防注入)
+     */
+    private int batchUpdateContractPeriod(Map<String, BigDecimal> contractAmountMap, String periodBeginDate, String periodEndDate) {
+        if (contractAmountMap.isEmpty() || StringUtils.isAnyBlank(periodBeginDate, periodEndDate)) {
+            return 0;
+        }
+
+        try {
+            // 动态拼接批量更新SQL
+            StringBuilder sql = new StringBuilder("UPDATE " + TABLE_CONTRACT_PERIOD + " SET " +
+                    FIELD_PERIOD_PRICE + " = CASE " + FIELD_CONTRACT_SERVICE_ID + " ");
+            List<Object> batchParams = new ArrayList<>();
+            List<String> contractIdList = new ArrayList<>();
+
+            // 拼接CASE WHEN条件(合同ID → 对应金额)
+            for (Map.Entry<String, BigDecimal> entry : contractAmountMap.entrySet()) {
+                String contractId = entry.getKey();
+                BigDecimal amount = entry.getValue();
+                sql.append("WHEN ? THEN ? ");
+                batchParams.add(contractId);
+                batchParams.add(amount);
+                contractIdList.add(contractId);
+            }
+
+            // 拼接WHERE条件(限定合同ID和周期日期范围,避免误改数据)
+            sql.append("END WHERE ").append(FIELD_CONTRACT_SERVICE_ID).append(" IN (");
+            for (int i = 0; i < contractIdList.size(); i++) {
+                sql.append(i == 0 ? "?" : ",?");
+            }
+            sql.append(") AND PERIOD_BEGIN_DATE >= ? AND PERIOD_END_DATE <= ?");
+
+            // 追加WHERE条件参数
+            batchParams.addAll(contractIdList);
+            batchParams.add(periodBeginDate);
+            batchParams.add(periodEndDate);
+
+            // 执行批量更新,返回受影响行数
+            return DBSql.update(sql.toString(), batchParams.toArray());
+        } catch (Exception e) {
+            log("批量更新周期表异常:" + e.getMessage());
+            e.printStackTrace();
+            return 0;
+        }
+    }
+}

+ 2 - 1
com.awspaas.user.apps.donenow_ctt/src/com/awspaas/user/apps/donenow_ctt/service/contractService.java

@@ -1356,13 +1356,14 @@ public class contractService {
 
 
         RowMap lastPayPeriod = DBSql.getMap(conn, "select * from BO_EU_DNIVT_ORDER_PAYMENT_PLAN where CONTRACT_SERVICE_ID=? and PERIOD_BEGIN_DATE<=? order by PLAN_DATE desc", new Object[]{service.get("ID"), LocalDateYYYYMMDD(END_DATE)});
+
         if (lastPayPeriod != null) {
 
             PERIOD_TYPE = service.getString("PURCHASE_PERIOD_TYPE");//采购周期类型
             LocalDate lastPeriodBeginDate = getLocalDate(lastPayPeriod.get("PERIOD_BEGIN_DATE"));
             LocalDate lastPeriodEndDate = getLocalDate(lastPayPeriod.get("PERIOD_END_DATE"));
 
-            DBSql.update(conn, "DELETE FROM BO_EU_DNIVT_ORDER_PAYMENT_PLAN WHERE  CONTRACT_SERVICE_ID=? and PERIOD_BEGIN_DATE>? and (PLAN_AMOUNT=0 or PLAN_AMOUNT is NULL)", new Object[]{service.getString("ID"), LocalDateYYYYMMDD(END_DATE)});
+            DBSql.update(conn, "DELETE FROM BO_EU_DNIVT_ORDER_PAYMENT_PLAN WHERE  CONTRACT_SERVICE_ID=? and PERIOD_BEGIN_DATE>?", new Object[]{service.getString("ID"), LocalDateYYYYMMDD(END_DATE)});
 
             BigDecimal SERVICE_UNIT_COST = service.get("UNIT_COST", BigDecimal.class);//服务单元成本
             BigDecimal SERVICE_TOTAL_COST = multiply(SERVICE_UNIT_COST, service.get("QUANTITY"));//服务总成本